日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

vue實現簡易的雙向數據綁定

瀏覽:102日期:2022-10-15 09:24:36

主要是通過數據劫持和發布訂閱一起實現的

雙向數據綁定 數據更新時,可以更新視圖 視圖的數據更新是,可以反向更新模型 組成說明 Observe監聽器 劫持數據, 感知數據變化, 發出通知給訂閱者, 在get中將訂閱者添加到訂閱器中 Dep消息訂閱器 存儲訂閱者, 通知訂閱者調用更新函數 訂閱者Wather取出模型值,更新視圖 解析器Compile 解析指令, 更新模板數據, 初始化視圖, 實例化一個訂閱者, 將更新函數綁定到訂閱者上, 可以在接收通知二次更新視圖, 對于v-model還需要監聽input事件,實現視圖到模型的數據流動 基本結構HTML模板

<div id='app'> <form> <input type='text' v-model='username'> </form> <p v-bind='username'></p> </div> 一個根節點#app 表單元素,里面包含input, 使用v-model指令綁定數據username p元素上使用v-bind綁定數username MyVue類

簡單的模擬Vue類

將實例化時的選項options, 數據options.data進行保存 此外,通過options.el獲取dom元素,存儲到$el上

class MyVue { constructor(options) {this.$options = optionsthis.$el = document.querySelector(this.$options.el)this.$data = options.data } }實例化MyVue

實例化一個MyVue,傳遞選項進去,選項中指定綁定的元素el和數據對象data

const myVm = new MyVue({ el: ’#app’, data: {username: ’LastStarDust’ } })Observe監聽器實現

劫持數據是為了修改數據的時候可以感知, 發出通知, 執行更新視圖操作

class MyVue { constructor(options) {// ...// 監視數據的屬性this.observable(this.$data) } // 遞歸遍歷數據對象的所有屬性, 進行數據屬性的劫持 { username: ’LastStarDust’ } observable(obj) {// obj為空或者不是對象, 不做任何操作const isEmpty = !obj || typeof obj !== ’object’if(isEmpty) { return}// [’username’]const keys = Object.keys(obj)keys.forEach(key => { // 如果屬性值是對象,遞歸調用 let val = obj[key] if(typeof val === ’object’) { this.observable(val) } // this.defineReactive(this.$data, ’username’, ’LastStarDust’) this.defineReactive(obj, key, val)})return obj } // 數據劫持,修改屬性的get和set方法 defineReactive(obj, key, val) {Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { console.log(`取出${key}屬性值: 值為${val}`) return val }, set(newVal) { // 沒有發生變化, 不做更新 if(newVal === val) { return } console.log(`更新屬性${key}的值為: ${newVal}`) val = newVal }}) } }Dep消息訂閱器

存儲訂閱者, 收到通知時,取出訂閱者,調用訂閱者的update方法

// 定義消息訂閱器 class Dep { // 靜態屬性 Dep.target,這是一個全局唯一 的Watcher,因為在同一時間只能有一個全局的 Watcher static target = null constructor() {// 存儲訂閱者this.subs = [] } // 添加訂閱者 add(sub) {this.subs.push(sub) } // 通知 notify() {this.subs.forEach(sub => { // 調用訂閱者的update方法 sub.update()}) } }將消息訂閱器添加到數據劫持過程中

為每一個屬性添加訂閱者

defineReactive(obj, key, val) {const dep = new Dep()Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { // 會在初始化時, 觸發屬性get()方法,來到這里Dep.target有值,將其作為訂閱者存儲起來,在觸發屬性的set()方法時,調用notify方法 if(Dep.target) { dep.add(Dep.target) } console.log(`取出${key}屬性值: 值為${val}`) return val }, set(newVal) { // 沒有發生變化, 不做更新 if(newVal === val) { return } console.log(`更新屬性${key}的值為: ${newVal}`) val = newVal dep.notify() }}) }訂閱者Wather

從模型中取出數據并更新視圖

// 定義訂閱者類 class Wather { constructor(vm, exp, cb) {this.vm = vm // vm實例this.exp = exp // 指令對應的字符串值, 如v-model='username', exp相當于'username'this.cb = cb // 回到函數 更新視圖時調用this.value = this.get() // 將自己添加到消息訂閱器Dep中 } get() {// 將當前訂閱者作為全局唯一的Wather,添加到Dep.target上Dep.target = this// 獲取數據,觸發屬性的getter方法const value = this.vm.$data[this.exp]// 在執行添加到消息訂閱Dep后, 重置Dep.targetDep.target = nullreturn value } // 執行更新 update() {this.run() } run() {// 從Model模型中取出屬性值const newVal = this.vm.$data[this.exp]const oldVal = this.valueif(newVal === oldVal) { return false}// 執行回調函數, 將vm實例,新值,舊值傳遞過去this.cb.call(this.vm, newVal, oldVal) } }解析器Compile 解析模板指令,并替換模板數據,初始化視圖; 將模板指令對應的節點綁定對應的更新函數,初始化相應的訂閱器; 初始化編譯器, 存儲el對應的dom元素, 存儲vm實例, 調用初始化方法 在初始化方法中, 從根節點開始, 取出根節點的所有子節點, 逐個對節點進行解析 解析節點過程中 解析指令存在, 取出綁定值, 替換模板數據, 完成首次視圖的初始化 給指令對應的節點綁定更新函數, 并實例化一個訂閱器Wather 對于v-model指令, 監聽’input’事件,實現視圖更新是,去更新模型的數據

// 定義解析器 // 解析指令,替換模板數據,初始視圖 // 模板的指令綁定更新函數, 數據更新時, 更新視圖 class Compile { constructor(el, vm) {this.el = elthis.vm = vmthis.init(this.el) } init(el) {this.compileEle(el) } compileEle(ele) {const nodes = ele.children// 遍歷節點進行解析for(const node of nodes) { // 如果有子節點,遞歸調用 if(node.children && node.children.length !== 0) { this.compileEle(node) } // 指令時v-model并且是標簽是輸入標簽 const hasVmodel = node.hasAttribute(’v-model’) const isInputTag = [’INPUT’, ’TEXTAREA’].indexOf(node.tagName) !== -1 if(hasVmodel && isInputTag) { const exp = node.getAttribute(’v-model’) const val = this.vm.$data[exp] const attr = ’value’ // 初次模型值推到視圖層,初始化視圖 this.modelToView(node, val, attr) // 實例化一個訂閱者, 將更新函數綁定到訂閱者上, 未來數據更新,可以更新視圖 new Wather(this.vm, exp, (newVal)=> { this.modelToView(node, newVal, attr) }) // 監聽視圖的改變 node.addEventListener(’input’, (e) => { this.viewToModel(exp, e.target.value) }) } // 指令時v-bind if(node.hasAttribute(’v-bind’)) { const exp = node.getAttribute(’v-bind’) const val = this.vm.$data[exp] const attr = ’innerHTML’ // 初次模型值推到視圖層,初始化視圖 this.modelToView(node, val, attr) // 實例化一個訂閱者, 將更新函數綁定到訂閱者上, 未來數據更新,可以更新視圖 new Wather(this.vm, exp, (newVal)=> { this.modelToView(node, newVal, attr) }) }} } // 將模型值更新到視圖 modelToView(node, val, attr) {node[attr] = val } // 將視圖值更新到模型上 viewToModel(exp, val) {this.vm.$data[exp] = val } }完整代碼

<!DOCTYPE html><html lang='en'><head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <title>Document</title></head><body> <div id='app'> <form> <input type='text' v-model='username'> </form> <div> <span v-bind='username'></span> </div> <p v-bind='username'></p> </div> <script> class MyVue { constructor(options) {this.$options = optionsthis.$el = document.querySelector(this.$options.el)this.$data = options.data// 監視數據的屬性this.observable(this.$data)// 編譯節點new Compile(this.$el, this) } // 遞歸遍歷數據對象的所有屬性, 進行數據屬性的劫持 { username: ’LastStarDust’ } observable(obj) {// obj為空或者不是對象, 不做任何操作const isEmpty = !obj || typeof obj !== ’object’if(isEmpty) { return}// [’username’]const keys = Object.keys(obj)keys.forEach(key => { // 如果屬性值是對象,遞歸調用 let val = obj[key] if(typeof val === ’object’) { this.observable(val) } // this.defineReactive(this.$data, ’username’, ’LastStarDust’) this.defineReactive(obj, key, val)})return obj } // 數據劫持,修改屬性的get和set方法 defineReactive(obj, key, val) {const dep = new Dep()Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { // 會在初始化時, 觸發屬性get()方法,來到這里Dep.target有值,將其作為訂閱者存儲起來,在觸發屬性的set()方法時,調用notify方法 if(Dep.target) { dep.add(Dep.target) } console.log(`取出${key}屬性值: 值為${val}`) return val }, set(newVal) { // 沒有發生變化, 不做更新 if(newVal === val) { return } console.log(`更新屬性${key}的值為: ${newVal}`) val = newVal dep.notify() }}) } } // 定義消息訂閱器 class Dep { // 靜態屬性 Dep.target,這是一個全局唯一 的Watcher,因為在同一時間只能有一個全局的 Watcher static target = null constructor() {// 存儲訂閱者this.subs = [] } // 添加訂閱者 add(sub) {this.subs.push(sub) } // 通知 notify() {this.subs.forEach(sub => { // 調用訂閱者的update方法 sub.update()}) } } // 定義訂閱者類 class Wather { constructor(vm, exp, cb) {this.vm = vm // vm實例this.exp = exp // 指令對應的字符串值, 如v-model='username', exp相當于'username'this.cb = cb // 回到函數 更新視圖時調用this.value = this.get() // 將自己添加到消息訂閱器Dep中 } get() {// 將當前訂閱者作為全局唯一的Wather,添加到Dep.target上Dep.target = this// 獲取數據,觸發屬性的getter方法const value = this.vm.$data[this.exp]// 在執行添加到消息訂閱Dep后, 重置Dep.targetDep.target = nullreturn value } // 執行更新 update() {this.run() } run() {// 從Model模型中取出屬性值const newVal = this.vm.$data[this.exp]const oldVal = this.valueif(newVal === oldVal) { return false}// 執行回調函數, 將vm實例,新值,舊值傳遞過去this.cb.call(this.vm, newVal, oldVal) } } // 定義解析器 // 解析指令,替換模板數據,初始視圖 // 模板的指令綁定更新函數, 數據更新時, 更新視圖 class Compile { constructor(el, vm) {this.el = elthis.vm = vmthis.init(this.el) } init(el) {this.compileEle(el) } compileEle(ele) {const nodes = ele.childrenfor(const node of nodes) { if(node.children && node.children.length !== 0) { // 遞歸調用, 編譯子節點 this.compileEle(node) } // 指令時v-model并且是標簽是輸入標簽 const hasVmodel = node.hasAttribute(’v-model’) const isInputTag = [’INPUT’, ’TEXTAREA’].indexOf(node.tagName) !== -1 if(hasVmodel && isInputTag) { const exp = node.getAttribute(’v-model’) const val = this.vm.$data[exp] const attr = ’value’ // 初次模型值推到視圖層,初始化視圖 this.modelToView(node, val, attr) // 實例化一個訂閱者, 將更新函數綁定到訂閱者上, 未來數據更新,可以更新視圖 new Wather(this.vm, exp, (newVal)=> { this.modelToView(node, newVal, attr) }) // 監聽視圖的改變 node.addEventListener(’input’, (e) => { this.viewToModel(exp, e.target.value) }) } if(node.hasAttribute(’v-bind’)) { const exp = node.getAttribute(’v-bind’) const val = this.vm.$data[exp] const attr = ’innerHTML’ // 初次模型值推到視圖層,初始化視圖 this.modelToView(node, val, attr) // 實例化一個訂閱者, 將更新函數綁定到訂閱者上, 未來數據更新,可以更新視圖 new Wather(this.vm, exp, (newVal)=> { this.modelToView(node, newVal, attr) }) }} } // 將模型值更新到視圖 modelToView(node, val, attr) {node[attr] = val } // 將視圖值更新到模型上 viewToModel(exp, val) {this.vm.$data[exp] = val } } const myVm = new MyVue({ el: ’#app’, data: {username: ’LastStarDust’ } }) // console.log(Dep.target) </script></body></html>

以上就是vue實現簡易的雙向數據綁定的詳細內容,更多關于vue 實現雙向數據綁定的資料請關注好吧啦網其它相關文章!

標簽: Vue
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
四虎精品永久免费| 国产专区一区| 日韩精品三级| 91综合久久爱com| 国产精品视频一区二区三区综合| 日本va欧美va瓶| 国产亚洲久久| 免费看一区二区三区| 精品一区二区三区中文字幕| 国产精品第一| 精品久久一区| 欧洲一区二区三区精品| 成人精品天堂一区二区三区| 欧美日韩国产传媒| 蜜桃av一区二区| 日韩高清不卡一区| 精品国产一区二区三区噜噜噜| 黄色精品视频| 99久精品视频在线观看视频| 免费毛片在线不卡| 一区二区亚洲视频| 日韩av网站免费在线| 国产精品视频首页| 国产一区二区三区不卡av| 国产成人黄色| 久久国产电影| 亚洲另类av| 蜜桃久久久久| 精品中文字幕一区二区三区av| 免费精品视频在线| 麻豆久久久久久| 亚洲网站视频| 欧美日韩一区二区三区在线电影| 国产一区二区三区四区二区| 一区二区小说| 亚洲欧洲日韩精品在线| 精品入口麻豆88视频| 亚洲福利久久| 91成人精品观看| 久久中文精品| 五月天久久网站| 91成人在线网站| 欧美成人基地 | 日韩激情一二三区| 免费一级欧美片在线观看网站| 久久人人97超碰国产公开结果| 美女精品网站| 精品视频高潮| 国产美女一区| 久久精品99久久久| 999久久久91| 欧美日韩一区二区国产| 久久久成人网| 日韩国产在线观看一区| 成午夜精品一区二区三区软件| japanese国产精品| 久久国产精品色av免费看| 黑人精品一区| 日本成人中文字幕在线视频| 国产超碰精品| 日本不卡一区二区三区| 久久久男人天堂| 免费视频最近日韩| 中文在线а√在线8| 免费不卡在线视频| 涩涩av在线| 91成人福利| 99pao成人国产永久免费视频| 国产精品亚洲综合色区韩国| 香蕉精品视频在线观看| 美女视频一区在线观看| 日韩中文字幕亚洲一区二区va在线 | 麻豆精品视频在线观看免费| 激情久久久久久久| 久久99国产精品视频| 羞羞答答国产精品www一本| 精品国产一区二区三区性色av| 免费精品视频最新在线| 国产99精品一区| 久久99性xxx老妇胖精品| 日韩中文字幕1| 欧美日韩精品一区二区视频| 欧美国产另类| 亚欧成人精品| 亚洲激情欧美| 91精品国产调教在线观看| 国产精品xvideos88| 一级成人国产| 自由日本语亚洲人高潮| 日韩欧美精品| 91福利精品在线观看| 免费黄网站欧美| 国产高清一区| 国产欧美一区二区三区精品酒店| 国产亚洲久久| 亚洲aa在线| 亚洲综合丁香| 99精品一区| 日韩精品诱惑一区?区三区| 麻豆高清免费国产一区| 日韩欧美中文字幕一区二区三区| 99视频精品免费观看| 在线日韩视频| 久久三级视频| 999视频精品| 伊人久久在线| 国产欧洲在线| 亚洲综合电影| av日韩中文| 美女福利一区二区三区| 国产一区二区三区国产精品| 麻豆免费精品视频| 久久精品伊人| 精品在线网站观看| 久久精品伊人| 精品在线网站观看| 国产中文在线播放| 亚洲精品一级二级| 91精品国产福利在线观看麻豆| 久久久夜精品| 久久婷婷久久| 伊人久久大香线蕉av不卡| 色老板在线视频一区二区| 日韩国产一区二区| 播放一区二区| 午夜日本精品| 日韩一区欧美二区| 亚洲精品护士| 91成人福利| 久久香蕉网站| 日韩综合在线| 日韩在线免费| 激情久久久久久| 中文在线一区| 首页亚洲欧美制服丝腿| 免费观看在线综合色| 亚洲青青久久| 国产精品一区二区精品视频观看 | 国产精品手机在线播放| 精品日韩一区| 日韩精品欧美| 亚洲少妇诱惑| 中文字幕亚洲影视| 久久国内精品自在自线400部| 国产精品天堂蜜av在线播放| 精品免费在线| 99国产精品免费视频观看| 国产亚洲精品v| 日本免费新一区视频| 美女视频黄久久| 日韩欧美另类一区二区| 国产亚洲网站| 国产精品15p| 欧美精选视频一区二区| 亚洲欧美日韩综合国产aⅴ| 日本成人一区二区| 97人人精品| aa国产精品| 日本视频中文字幕一区二区三区| 国产精品va视频| 国产精品亚洲一区二区三区在线观看| re久久精品视频| 欧美久久精品| 日韩在线观看一区| 视频在线观看一区| 国产精品2区| 蜜桃国内精品久久久久软件9| 亚洲综合色婷婷在线观看| 欧美精品aa| 欧美成人精品| 91伊人久久| 色婷婷狠狠五月综合天色拍| 亚洲欧美日本视频在线观看| 国产午夜久久av| 欧美aa国产视频| 国产精品乱战久久久| 久久国产亚洲精品| 欧美在线观看天堂一区二区三区| 亚洲最新无码中文字幕久久 | 日韩高清中文字幕一区二区| 三级一区在线视频先锋| 丰满少妇一区| 免费成人在线视频观看| 精品久久电影| 男人天堂欧美日韩| 精品国产精品久久一区免费式 | 欧美精品1区| 亚洲深爱激情| 岛国av在线网站| 日韩欧美久久| 香蕉久久99| 久久久国产精品网站| 中文不卡在线| 亚洲精品在线影院| 国产乱子精品一区二区在线观看 | 亚洲啊v在线免费视频| 亚洲啊v在线| 国产剧情在线观看一区| 亚洲一区二区三区高清| 成人片免费看|