實現vue的雙向數據綁定
前言
在日常生活中,很多求職者都會遇到面試問題就是vue雙向數據綁定的原理。很多同學的回答大體一致:
Object.defineProperty和觀察者模式
,再往下問就說不清楚了。接下來我會詳細解析vue雙向數據綁定原理,用最簡單的方式給大家呈現,所以實現的過程我們會以思路為主,不會考慮更多的性能問題。
雙向數據含義
- 關於雙向數據綁定,主要分為兩個方向:
- model(數據層) --> view(視圖層) 數據層改變視圖層自動更新
- view(視圖層) --> model(數據層) 視圖層改變(比如input和textarea等)數據層數據也會變化
- 對於早期框架以backbone為例來說,實現的是單向數據綁定,也就是數據層變化視圖層更新,視圖層變化只能通過controller(行為層,事件層)來手動修改數據。比如說用戶修改了input的值,我們可以在controller層監聽用戶的輸入事件,手動修改model層數據。目前的react個人認為應該是屬於單向數據流。
- 最早的雙向數據綁定提出據我使用應該是angularJs,此處實現會再以下詳細講述,此處不做介紹。目前最火的框架vue使用的就是雙向數據流。
單向數據流
- 我們實現vue雙向數據流會從單向數據流開始,實現的思路
頁面首次渲染綁定數據
-->數據變化視圖更新
- 實現首次渲染數據
- 此處使用的是面向對象的封裝模式,定義構造函數Vue,要求傳入參數是一個對象,對象包含el和data兩個key,el對應是根元素選擇器,data對應頁面渲染的數據。
- 封裝庫文件vue.js
class Vue { constructor (opts){ // 獲取根元素 this.el = document.querySelector(opts.el) // 獲取數據源 this.data = opts.data // 解析html this.compile(this.el) } compile(el){ /* 獲取html里面所有的插值表達式{{}} 獲取根元素下面所有的子節點,判斷子節點的類型 1. 如果子節點也是一個元素節點 - 繼續執行編譯函數,找到他們下面的子節點 此處使用了遞歸語法 2. 如果子節點就是一個文本節點 - 判斷文本節點里面是否包含插值表達式 此處使用正則表達式判斷 + 包含插值表達式,找到正則表達式里面的變量名 */ // 獲取所有的子節點 let childNodes = el.childNodes let reg = /\{\{(.*)\}\}/ childNodes.forEach(node => { // 區分節點類型 let text = node.textContent if(node.nodeType === 1){ // 元素節點 this.compile(node) }else if(node.nodeType === 3 && reg.test(text)){ // 文本節點 let exp = reg.exec(text)[1] console.warn('獲取的變量名', exp) // 渲染數據到頁面 this.bindData(node, exp) } }) } bindData(node, exp){ node.textContent = this.data[exp] } }
- html使用測試
<!-- 引入庫文件 --> <script src="vue.js"></script> <!-- html結構 --> <div id="app"> {{name}} <ul> <li>{{title}}</li> </ul> <p>{{msg}}</p> </div> <!-- 創建實例 --> <script> new Vue({ el: "#app", data: { name: '張三', title: '標題', msg: '消息' } }) </script>
- 實現首次渲染數據
未完,不想寫了。有心情再寫✍🏻