电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

vue實現(xiàn)簡易的雙向數(shù)據(jù)綁定

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

主要是通過數(shù)據(jù)劫持和發(fā)布訂閱一起實現(xiàn)的

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

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

簡單的模擬Vue類

將實例化時的選項options, 數(shù)據(jù)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和數(shù)據(jù)對象data

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

劫持數(shù)據(jù)是為了修改數(shù)據(jù)的時候可以感知, 發(fā)出通知, 執(zhí)行更新視圖操作

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

存儲訂閱者, 收到通知時,取出訂閱者,調(diào)用訂閱者的update方法

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

為每一個屬性添加訂閱者

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

從模型中取出數(shù)據(jù)并更新視圖

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

// 定義解析器 // 解析指令,替換模板數(shù)據(jù),初始視圖 // 模板的指令綁定更新函數(shù), 數(shù)據(jù)更新時, 更新視圖 class Compile { constructor(el, vm) {this.el = elthis.vm = vmthis.init(this.el) } init(el) {this.compileEle(el) } compileEle(ele) {const nodes = ele.children// 遍歷節(jié)點進行解析for(const node of nodes) { // 如果有子節(jié)點,遞歸調(diào)用 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) // 實例化一個訂閱者, 將更新函數(shù)綁定到訂閱者上, 未來數(shù)據(jù)更新,可以更新視圖 new Wather(this.vm, exp, (newVal)=> { this.modelToView(node, newVal, attr) }) // 監(jiān)聽視圖的改變 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) // 實例化一個訂閱者, 將更新函數(shù)綁定到訂閱者上, 未來數(shù)據(jù)更新,可以更新視圖 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// 監(jiān)視數(shù)據(jù)的屬性this.observable(this.$data)// 編譯節(jié)點new Compile(this.$el, this) } // 遞歸遍歷數(shù)據(jù)對象的所有屬性, 進行數(shù)據(jù)屬性的劫持 { username: ’LastStarDust’ } observable(obj) {// obj為空或者不是對象, 不做任何操作const isEmpty = !obj || typeof obj !== ’object’if(isEmpty) { return}// [’username’]const keys = Object.keys(obj)keys.forEach(key => { // 如果屬性值是對象,遞歸調(diào)用 let val = obj[key] if(typeof val === ’object’) { this.observable(val) } // this.defineReactive(this.$data, ’username’, ’LastStarDust’) this.defineReactive(obj, key, val)})return obj } // 數(shù)據(jù)劫持,修改屬性的get和set方法 defineReactive(obj, key, val) {const dep = new Dep()Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { // 會在初始化時, 觸發(fā)屬性get()方法,來到這里Dep.target有值,將其作為訂閱者存儲起來,在觸發(fā)屬性的set()方法時,調(diào)用notify方法 if(Dep.target) { dep.add(Dep.target) } console.log(`取出${key}屬性值: 值為${val}`) return val }, set(newVal) { // 沒有發(fā)生變化, 不做更新 if(newVal === val) { return } console.log(`更新屬性${key}的值為: ${newVal}`) val = newVal dep.notify() }}) } } // 定義消息訂閱器 class Dep { // 靜態(tài)屬性 Dep.target,這是一個全局唯一 的Watcher,因為在同一時間只能有一個全局的 Watcher static target = null constructor() {// 存儲訂閱者this.subs = [] } // 添加訂閱者 add(sub) {this.subs.push(sub) } // 通知 notify() {this.subs.forEach(sub => { // 調(diào)用訂閱者的update方法 sub.update()}) } } // 定義訂閱者類 class Wather { constructor(vm, exp, cb) {this.vm = vm // vm實例this.exp = exp // 指令對應(yīng)的字符串值, 如v-model='username', exp相當于'username'this.cb = cb // 回到函數(shù) 更新視圖時調(diào)用this.value = this.get() // 將自己添加到消息訂閱器Dep中 } get() {// 將當前訂閱者作為全局唯一的Wather,添加到Dep.target上Dep.target = this// 獲取數(shù)據(jù),觸發(fā)屬性的getter方法const value = this.vm.$data[this.exp]// 在執(zhí)行添加到消息訂閱Dep后, 重置Dep.targetDep.target = nullreturn value } // 執(zhí)行更新 update() {this.run() } run() {// 從Model模型中取出屬性值const newVal = this.vm.$data[this.exp]const oldVal = this.valueif(newVal === oldVal) { return false}// 執(zhí)行回調(diào)函數(shù), 將vm實例,新值,舊值傳遞過去this.cb.call(this.vm, newVal, oldVal) } } // 定義解析器 // 解析指令,替換模板數(shù)據(jù),初始視圖 // 模板的指令綁定更新函數(shù), 數(shù)據(jù)更新時, 更新視圖 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) { // 遞歸調(diào)用, 編譯子節(jié)點 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) // 實例化一個訂閱者, 將更新函數(shù)綁定到訂閱者上, 未來數(shù)據(jù)更新,可以更新視圖 new Wather(this.vm, exp, (newVal)=> { this.modelToView(node, newVal, attr) }) // 監(jiān)聽視圖的改變 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) // 實例化一個訂閱者, 將更新函數(shù)綁定到訂閱者上, 未來數(shù)據(jù)更新,可以更新視圖 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實現(xiàn)簡易的雙向數(shù)據(jù)綁定的詳細內(nèi)容,更多關(guān)于vue 實現(xiàn)雙向數(shù)據(jù)綁定的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標簽: Vue
相關(guān)文章:
主站蜘蛛池模板: 懂研帝_专业SCI论文润色机构_SCI投稿发表服务公司 | 小型高低温循环试验箱-可程式高低温湿热交变试验箱-东莞市拓德环境测试设备有限公司 | 顺景erp系统_erp软件_erp软件系统_企业erp管理系统-广东顺景软件科技有限公司 | 抓斗式清污机|螺杆式|卷扬式启闭机|底轴驱动钢坝|污水处理闸门-方源水利机械 | 液压油缸-液压缸厂家价格,液压站系统-山东国立液压制造有限公司 液压油缸生产厂家-山东液压站-济南捷兴液压机电设备有限公司 | 泵阀展|阀门展|水泵展|流体机械展 -2025上海国际泵管阀展览会flowtech china | 乐考网-银行从业_基金从业资格考试_初级/中级会计报名时间_中级经济师 | 苏州伊诺尔拆除公司_专业酒店厂房拆除_商场学校拆除_办公楼房屋拆除_家工装拆除拆旧 | 时代北利离心机,实验室离心机,医用离心机,低速离心机DT5-2,美国SKC采样泵-上海京工实业有限公司 工业电炉,台车式电炉_厂家-淄博申华工业电炉有限公司 | 拉曼光谱仪_便携式|激光|显微共焦拉曼光谱仪-北京卓立汉光仪器有限公司 | 税筹星_灵活用工平台_企业财务顾问_财税法薪综合服务平台 | 细石混凝土泵_厂家_价格-烟台九达机械有限公司 | 长城人品牌官网 | T恤衫定做,企业文化衫制作订做,广告T恤POLO衫定制厂家[源头工厂]-【汉诚T恤定制网】 | 全自动包装机_灌装机生产厂家-迈驰包装设备有限公司 | 噪声治理公司-噪音治理专业隔音降噪公司 | 减速机电机一体机_带电机减速器一套_德国BOSERL电动机与减速箱生产厂家 | 广州物流公司_广州货运公司_广州回程车运输 - 万信物流 | TPE_TPE热塑性弹性体_TPE原料价格_TPE材料厂家-惠州市中塑王塑胶制品公司- 中塑王塑胶制品有限公司 | 数显水浴恒温振荡器-分液漏斗萃取振荡器-常州市凯航仪器有限公司 | 流程管理|流程管理软件|企业流程管理|微宏科技-AlphaFlow_流程管理系统软件服务商 | 长沙网站建设制作「网站优化推广」-网页设计公司-速马科技官网 | 铁艺,仿竹,竹节,护栏,围栏,篱笆,栅栏,栏杆,护栏网,网围栏,厂家 - 河北稳重金属丝网制品有限公司 山东太阳能路灯厂家-庭院灯生产厂家-济南晟启灯饰有限公司 | 跨境物流_美国卡派_中大件运输_尾程派送_海外仓一件代发 - 广州环至美供应链平台 | 3dmax渲染-效果图渲染-影视动画渲染-北京快渲科技有限公司 | 手术室净化厂家_成都实验室装修公司_无尘车间施工单位_洁净室工程建设团队-四川华锐16年行业经验 | 东莞螺杆空压机_永磁变频空压机_节能空压机_空压机工厂批发_深圳螺杆空压机_广州螺杆空压机_东莞空压机_空压机批发_东莞空压机工厂批发_东莞市文颖设备科技有限公司 | 合肥角钢_合肥槽钢_安徽镀锌管厂家-昆瑟商贸有限公司 | 动库网动库商城-体育用品专卖店:羽毛球,乒乓球拍,网球,户外装备,运动鞋,运动包,运动服饰专卖店-正品运动品网上商城动库商城网 - 动库商城 | RO反渗透设备_厂家_价格_河南郑州江宇环保科技有限公司 | 盘扣式脚手架-附着式升降脚手架-移动脚手架,专ye承包服务商 - 苏州安踏脚手架工程有限公司 | 阳光模拟试验箱_高低温试验箱_高低温冲击试验箱_快速温变试验箱|东莞市赛思检测设备有限公司 | 老房子翻新装修,旧房墙面翻新,房屋防水补漏,厨房卫生间改造,室内装潢装修公司 - 一修房屋快修官网 | 大型果蔬切片机-水果冬瓜削皮机-洗菜机切菜机-肇庆市凤翔餐饮设备有限公司 | 高铝轻质保温砖_刚玉莫来石砖厂家_轻质耐火砖价格 | 明渠式紫外线杀菌器-紫外线消毒器厂家-定州市优威环保 | 高效复合碳源-多核碳源生产厂家-污水处理反硝化菌种一长隆科技库巴鲁 | 全自动烧卖机厂家_饺子机_烧麦机价格_小笼汤包机_宁波江北阜欣食品机械有限公司 | 5nd音乐网|最新流行歌曲|MP3歌曲免费下载|好听的歌|音乐下载 免费听mp3音乐 | 冻干机(冷冻干燥机)_小型|实验型|食品真空冷冻干燥机-松源 | 高压互感器,电流互感器,电压互感器-上海鄂互电气科技有限公司 |