vue 双向数据绑定的实现学习(一)
前言:本系列學(xué)習(xí)筆記從以下幾個點展開
- 什么是雙向數(shù)據(jù)綁定
- 雙向數(shù)據(jù)綁定的好處
- 怎么實現(xiàn)雙向數(shù)據(jù)綁定
- 實現(xiàn)雙向數(shù)據(jù)數(shù)據(jù)綁定需要哪些知識點
- 數(shù)據(jù)劫持
- 發(fā)布訂閱模式
- 實現(xiàn)雙向數(shù)據(jù)數(shù)據(jù)綁定需要哪些知識點
?先看看我們要實現(xiàn)的目標(biāo)是什么,如下動圖:
0、什么是雙向數(shù)據(jù)綁定
單向數(shù)據(jù)綁定:把Model?綁定到View上,當(dāng)我們用js修改模型?Model 時候,視圖View上對應(yīng)的內(nèi)容也會改動,這就是 數(shù)據(jù)動,頁面動 。
雙向數(shù)據(jù)綁定:簡言之?數(shù)據(jù)動?頁面動,頁面動,數(shù)據(jù)動,?典型的應(yīng)用就是在做表單時候,輸入框的內(nèi)容改動后,跟該輸入框的value?的值改動。
看vue?官網(wǎng)上的這個V-model? 的演示案例:
?
1、雙向數(shù)據(jù)綁定的好處
要說出這個好處的時候,也只有在實際場景中才能對應(yīng)的顯示出來。比如我們需要實時顯示數(shù)據(jù),我們一邊說話,一邊實時顯示我們說的話的文字內(nèi)容,等等。這讓我想起了去年參加云棲大會,臺上的大佬一邊說話,下面的字幕實時更新。(當(dāng)然實現(xiàn)這個技術(shù)有很多技術(shù)點,我們不討論這個內(nèi)容,小編也才疏學(xué)淺,搞不懂)
以上的都是廢話,我們直接看看怎么實現(xiàn)這個雙向數(shù)據(jù)綁定。
一、實現(xiàn)原理
Vue實現(xiàn)雙向數(shù)據(jù)綁定的原理:數(shù)據(jù)劫持 +?發(fā)布訂閱模式(有的也稱為觀察者模式)
數(shù)據(jù)劫持的核心技術(shù):?Object.defineProperty()
**vue 3.0?已經(jīng)用的不是這個技術(shù)了,采用是?原生的?Proxy,據(jù)說速度能夠提升100%,截張尤大的ppt,** 2018-11-21?修改本篇筆記
(香菇,剛研究會一點,就立馬變了,這就是前端世界),Proxy?的方式將會在本系列筆記結(jié)束后,再記錄這個技術(shù)點的使用
二、數(shù)據(jù)劫持的方法?Object.defineProperty()
先上一個參照代碼,它長這個樣子:
var book = {_year: 2004,edition: 1 }; Object.defineProperty(book, "year", {get: function(){console.log('訪問year了,返回_year')return this._year;},set: function(newValue){if (newValue > 2004) {this._year = newValue;
console.log('重新設(shè)置_year了,并返回edition')this.edition += newValue - 2004;}} }); book.year = 2005; alert(book.edition); //2
?---摘自 JavaScript高級程序設(shè)計
Object.defineProperty()?的具體介紹,我們本文不做具體展開,查看我這里的一篇文章,Object.defineProperty。?我們這里先要知道這么一個事情。這個方法要傳入三個參數(shù),傳入的數(shù)據(jù)對象data,屬性key,描述符對象。其中,描述符(descriptor)對象的屬 性必須是:configurable、enumerable、writable 和 value。設(shè)置其中的一或多個值,可以修改 對應(yīng)的特性值。我們需要用的是這訪問器屬性。當(dāng)我們在讀取訪問器屬性時,會調(diào)用 getter 函數(shù),這個函數(shù)負(fù)責(zé)返回有效的值;在寫入訪問器屬性時,會調(diào)用 setter 函數(shù)并傳入新值,這個函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù)。上文代碼上的 get?方法,在讀取屬性時調(diào)用的函數(shù),set方法,在寫入屬性時調(diào)用的函數(shù)。
三、發(fā)布訂閱者模式
我畫了一個圖,來理解這個模式,如下圖:
?
代碼解釋:
//下面封裝一個單例模式,內(nèi)容是發(fā)布訂閱模式 let event = {eventList: [],listener: function (key, fn) {if (!this.eventList[key]) { //沒有訂閱過此類消息,創(chuàng)建一個緩存列表this.eventList[key] = [];}this.eventList[key].push(fn)},trigger: function () {let key = Array.prototype.shift.call(arguments); // marrylet fns = this.eventList[key];if (!fns || fns.length == 0) { //沒有訂閱 則返回return false;}for (let i = 0, fn; fn = fns[i++];) {fn.apply(this, arguments)// 調(diào)用 event.listen 里面的 fn 方法,通過apply將當(dāng)前執(zhí)行的對象指向當(dāng)前的this,arguments 傳進 fn 函數(shù) }},remove: function (key, fn) { // 取消訂閱let fns = this.eventList[key];if (!fns) {return false;}if(!fn) {fns && (fns.length = 0)} else {for (let l = fns.length-1; l>=0; l--) {let _fn = fns[l];if( _fn === fn) {fns.splice(l, 1)}}}},install: function (obj) {for (let i in this) {if (i === 'install') {return false}obj[i] = this[i];}} }let testMsg = {} event.install(testMsg)// 上面方法 就會將event的方法 淺拷貝給 testMsg, 這樣testMsg就有 event的方法和屬性 testMsg.listener('rich', fn1 = (name) => {console.log(`${name}知道你有錢了`) }) testMsg.listener('borrowMoney', fn2 = (name) => {console.log(`${name}想問你借錢`) })// listen方法將事件 放進隊列 // testMsg.remove('rich', fn1) // 取消訂閱// trigger方法,處理事件隊列的方法,調(diào)用listen的函數(shù)的里面的回調(diào)函數(shù) fn testMsg.trigger('rich', '張三') testMsg.trigger('rich', '張三2') testMsg.trigger('borrowMoney', '李四') testMsg.trigger('borrowMoney', '李四2')// 代碼總結(jié): // 訂閱的事件具有對應(yīng)的key// 通過listener方法,將具體的事件隊列保存到 eventList ,可以理解為緩存列表也可以是事件隊列;// 執(zhí)行trigger 方法,將事件隊列拿出來執(zhí)行調(diào)用// remove 方法根據(jù)對應(yīng)的key值,刪除對應(yīng)的訂閱事件 // 模式總結(jié):封裝一個單例event, 執(zhí)行installEvent方法,將想要event對象拷貝到某個對象中去, // 發(fā)布者trigger方法,監(jiān)聽者listen方法 ,trigger 發(fā)布一個東西,listen立馬知道你要發(fā)布的東西 View Code?
?
四、實現(xiàn)分解
- 主函數(shù)入口
- function Myvue (options) {this.$options = optionsthis.$el = document.querySelector(options.el);this.$data = options.data;Object.keys(this.$data).forEach(key => {this.$prop = key;})this.init()
}
Myvue.prototype.init = function () {// 監(jiān)聽數(shù)據(jù)變化observer(this.$data);// 獲得值// let value = this.$data[this.$prop];// 不經(jīng)過模板編譯直接 通知訂閱者更新dom// new Watcher(this,this.$prop,value => {// console.log(`watcher ${this.$prop}的改動,要有動靜了`)// this.$el.textContent = value// }) //通知模板編譯來執(zhí)行頁面上模板變量替換new Compile(this)
}
?
- function Myvue (options) {this.$options = optionsthis.$el = document.querySelector(options.el);this.$data = options.data;Object.keys(this.$data).forEach(key => {this.$prop = key;})this.init()
}
Myvue.prototype.init = function () {// 監(jiān)聽數(shù)據(jù)變化observer(this.$data);// 獲得值// let value = this.$data[this.$prop];// 不經(jīng)過模板編譯直接 通知訂閱者更新dom// new Watcher(this,this.$prop,value => {// console.log(`watcher ${this.$prop}的改動,要有動靜了`)// this.$el.textContent = value// }) //通知模板編譯來執(zhí)行頁面上模板變量替換new Compile(this)
}
- 主函數(shù)調(diào)用
- <script>const vm = new Myvue({el: "#app",data: {name: "vue 雙向數(shù)據(jù)綁定test1"}});
</script>
?
- <script>const vm = new Myvue({el: "#app",data: {name: "vue 雙向數(shù)據(jù)綁定test1"}});
</script>
- 監(jiān)聽器
- 訂閱者
- 模板編譯器
?未完待續(xù),錯誤之處,敬請指出,共同進步!
下一篇?vue 雙向數(shù)據(jù)綁定的實現(xiàn)學(xué)習(xí)(二)-監(jiān)聽器的實現(xiàn)
文章參考:
https://www.cnblogs.com/beevesnoodles/p/9844854.html
https://github.com/youngwind/blog/issues/87
https://juejin.im/post/5a9108b6f265da4e7527b1a4
后記:代碼只做基本實現(xiàn),不做代碼健壯性處理,一些錯誤處理已經(jīng)忽略
轉(zhuǎn)載于:https://www.cnblogs.com/adouwt/p/9928278.html
總結(jié)
以上是生活随笔為你收集整理的vue 双向数据绑定的实现学习(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 练习3.21
- 下一篇: Java输入输出入门 A+B