<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                >[success] # 手寫一個簡單vue數據響應 ~~~ 1.實現一個簡單的vue 響應的模型,剛才已經分析過了整體思路,現在實現一個簡單的 vue 版本,分析一下Vue 在使用時候一些規則。 1.1.首先接受的是一個對象,其中data可以是對象也可以是function 1.2.data 屬性可以直接通過vue實例調用 2.對下面幾個類做個說明: 2.1.Vue 類一個配置入口,其中'_proxyData'方法將data中的數據掛載到vue實例上 2.2.Observe 類負責對data 中每個屬性都進行getter 和setter,并且對數據變化進行監聽調用 2.3.解析指令和差值表達式 ~~~ * vue 官網的圖解 ~~~ 1.發布者Dep 在數據getter時候,將需要的觀察者 收集到發布者中,在setter進行發送通知 1.watcher 作為一個觀察者,等待setter 時候觸發整體調用機制 ~~~ ![](https://img.kancloud.cn/81/e7/81e765156986df52c7374621fefdddf6_725x465.png) >[danger] ##### 最后整體效果 * 使用效果 ~~~ <div id="app"> <div> <p>{{name}}</p> <p>{{name}}</p> <p v-text="sex"></p> </div> </div> <!--下面的js 被放到一個test.js 文件中--> <script src="./test.js"></script> <script> const vm = new Vue({ el: '#app', data: { name: 'w', sex: 'female', } }) console.log(vm) </script> ~~~ ~~~ function getData(data, vm) { try { return data.call(vm, vm) } catch (e) { handleError(e, vm, "data()"); return {} } } class Vue { constructor(options) { // 1.將options 進行保存 this.$opitions = options || {} const data = options.data this.$data = typeof data === 'function' ? getData(data, vm) : data || {} this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el // 2.將data 中數據轉換成getter setter 存在vue 中, // 這次只是將 data 中的數據 以getter setter 形式放到Vue 對象中 this._proxyData(this.$data) // 3.調用observe對象,observe 對象負責對data 中每個屬性都進行getter 和setter // 并且對數據變化進行監聽調用 new Observe(this.$data) // 4. 調用compiler對象,解析指令和差值表達式 new Compiler(this) } _proxyData(data) { // 遍歷data中的所有key為了將數據形成getter 和setter 的形式 Object.keys(data).forEach(key => { Object.defineProperty(this, key, { enumerable: true, configurable: true, get() { return data[key] }, set(newValue) { // 如果新老值沒有變化就不用進行set 重新賦值 if (newValue !== data[key]) { data[key] = newValue } } }) }) } } class Observe { constructor(data) { this.walk(data) } // 循環遍歷data對象的所有屬性,作為遞歸的一個判斷條件 walk(data) { if (typeof data !== 'object' || !data) return Object.keys(data).forEach(key => { this.defineReactive(data, key, data[key]) }) } defineReactive(data, key, val) { let that = this // 負責收集依賴,并發送通知 let dep = new Dep() console.log(dep, 111) this.walk(val) Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { // 將需要的觀察者 收集到發布者中 Dep.target && dep.addSub(Dep.target) return val }, set(newValue) { if (newValue === val) return val = newValue // 如果賦值的 是對象需要從新進行getter 和setter 綁定 that.walk(newValue) // 發送通知 dep.notify() } }) } } // 處理判el 對象綁定是 function query(el) { if (typeof el === 'string') { var selected = document.querySelector(el); if (!selected) { warn( 'Cannot find element: ' + el ); return document.createElement('div') } return selected } else { return el } } class Compiler { constructor(vm) { this.el = query(vm.$el) this.vm = vm // 調用處理模板 this.compile(this.el) } // 編譯模板 // 處理文本節點和元素節點 compile(el) { // 獲取節點中的內容 const childNodes = el.childNodes // 需要對不同節點做不同的處理 // 1.1.文本節點一般是通過{{msg}},雙大括號這種進行值替換 // 1.2.dom 節點一般會自定義一些指令 v-if 或者 v-text 一類 Array.from(childNodes).forEach(node => { if (this.isTextNode(node)) { this.compileText(node) } else if (this.isElementNode(node)) { this.compileElement(node) } // 如果當前節點還有子節點 這時候就需要遞歸繼續compile操作 if (node.childNodes && node.childNodes.length) { this.compile(node) } }) } // 判斷是不是 文本節點,這是dom 自己的屬性3 表示文本 isTextNode(node) { return node.nodeType === 3 } // 判斷是不是屬性節點,這是dom 自己的屬性1 表示dom 元素 isElementNode(node) { return node.nodeType === 1 } // 判斷是不是指令 也就是v- 開頭 isDirective(attrName) { return attrName.startsWith('v-') } // 編譯文本節點 compileText(node) { // 1.文本節點一般是通過{{msg}},雙大括號這種進行值替換 // 現在的思路就是利用正則取出 雙大括號中的key 并且在vm找到key 對應的value渲染上 const reg = /\{\{(.+)\}\}/ // 獲取文本節點中的內容例如<p>我是{{ name }}</p> // 獲取的內容就為 -- 我是{{ name }} const value = node.textContent if (reg.test(value)) { // 獲取雙大括號中的key const key = RegExp.$1.trim() // 重新賦值 文本節點中的內容 // 之前已經將data 中的數據通過_proxyData方法放到vue 實例上了 node.textContent = value.replace(reg, this.vm[key]) // 創建watcher對象,當數據改變更新視圖 new Watcher(this.vm, key, (newValue) => { node.textContent = newValue }) } } // 編譯屬性節點 compileElement(node) { // 遍歷dom 節點所有元素中的屬性,找到指令 Array.from(node.attributes).forEach(attr => { // 獲取元素中的屬性 let attrName = attr.name if (this.isDirective(attrName)) { // attrName 的形式 v-text v-model // 截取屬性的名稱,獲取 text model attrName = attrName.substr(2) // 獲取屬性的名稱,屬性的名稱就是我們數據對象的屬性 v-text="name",獲取的是name const key = attr.value // 處理不同的指令 this.update(node, key, attrName) } }) } // 負責更新 DOM // 創建 Watcher update(node, key, dir) { // node 節點,key 數據的屬性名稱,dir 指令的后半部分 const updateFn = this[dir + 'Updater'] updateFn && updateFn.call(this, node, this.vm[key], key) } // v-text 指令的更新方法 textUpdater(node, value, key) { node.textContent = value new Watcher(this.vm, key, (newValue) => { node.textContent = newValue }) } // v-model 指令的更新方法 modelUpdater(node, value, key) { node.value = value new Watcher(this.vm, key, (newValue) => { node.value = newValue }) // 雙向綁定 node.addEventListener('input', () => { this.vm[key] = node.value }) } } // -------利用發布訂閱 模式來進行 數據響應通知---------- // 發布者 -- 發布者要收集所有觀察者才能 給每個觀察者發送要接受的內容 class Dep { constructor() { this.subs = [] } addSub(sub) { if (sub && sub.update) { this.subs.push(sub) } } notify() { this.subs.forEach(sub => sub.update()) } } // 觀察者 等待發布者發送指令 class Watcher { constructor(vm, key, cb) { this.vm = vm // data 中的key this.key = key // 執行的回調 this.cb = cb // 把watcher對象記錄到Dep類的靜態屬性target Dep.target = this // 觸發get方法,在get方法中會調用addSub this.oldValue = vm[key] Dep.target = null } // 當數據發生變化的時候更新視圖 update() { let newValue = this.vm[this.key] if (this.oldValue === newValue) { return } this.cb(newValue) } } ~~~ >[danger] ##### 參考文章 [mvvm](https://github.com/DMQ/mvvm)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看