>[warning]指令v-model實現
****
1. 思路: 解析DOM屬性, 判斷是否有v-model屬性, 然后執行對應代碼
(比較復雜, 仔細看)
代碼如下:
~~~
<script>
Compile.prototype = {
nodeToFragment(el) {
let fragment = document.createDocumentFragment();
let child = el.firstChild;
while (child) {
fragment.appendChild(child);
child = el.firstChild;
}
return fragment;
},
compileElement(el) {
let childNodes = el.childNodes;
let self = this;
[].slice.call(childNodes).forEach(function (node) {
let reg = /\{\{(.*)\}\}/;
let text = node.textContent;
// 1. 判斷當前DOM是否是標簽
if (self.isElementNode(node)) {
self.compile(node);
} else if (self.isTextNode(node) && reg.test(text)) {
self.compileText(node, reg.exec(text)[1]);
}
if (node.childNodes && node.childNodes.length) {
self.compileElement(node);
}
})
},
compileText(node, exp) {
let self = this;
let initText = this.vm[exp];
self.updateText(node, initText);
new Watcher(this.vm, function (val) {
self.updateText(node, val);
}, exp);
},
updateText(node, value) {
node.textContent = typeof value === 'undefined' ? '' : value;
},
isTextNode: function (node) {
return node.nodeType == 3;
},
// 2. 判斷是否是標簽節點
isElementNode: function (node) {
return node.nodeType == 1;
},
// 3. 解析虛擬DOM節點屬性值
compile(node) {
// 拿到這個標簽所有屬性
let nodeAttrs = node.attributes;
let self = this;
// 遍歷標簽屬性集合
Array.prototype.forEach.call(nodeAttrs, function (attr) {
// 拿出每個屬性的名字
let attrName = attr.name;
// 4. 判斷屬性是否是v-開頭的
if (self.isDirective(attrName)) {
// 拿到屬性對應的值
let exp = attr.value;
// 6. 截取掉v-開頭2個字符
let dir = attrName.substring(2);
// 7. 看看是否剩下model字符串
if (self.isModelDirective(dir)) {
// 9. 注冊watcher監聽器
self.compileModel(node, self.vm, exp);
}
// 15. 刪除v-model屬性
node.removeAttribute(attrName);
}
});
},
// 5. 判斷是否以v-開頭字符串
isDirective: function (attr) {
return attr.indexOf('v-') == 0;
},
// 8. 判斷是否是model開頭的字符串 (v-model)
isModelDirective: function (dir) {
return dir.indexOf('model') === 0;
},
// 10. v-model指令解析
// 標簽, Vue實例, key值
compileModel: function (node, vm, exp) {
let self = this;
let val = this.vm[exp]; // 監聽的value是多少
// 11. 初始化表單的值
this.modelUpdater(node, val);
// 13. 注冊監聽器
new Watcher(this.vm, function (value) {
// 監聽器觸發, 則更新表單的值
self.modelUpdater(node, value);
}, exp);
// 14. 給表單標簽綁定input事件(這也是為什么不是所有標簽都可以使用v-model的原因)
node.addEventListener('input', function(e) {
// 獲取表單最新的值
let newValue = e.target.value;
if (val === newValue) {
return;
}
// 同步model里的值
self.vm[exp] = newValue;
val = newValue;
});
},
// 12. model更新表單的值
modelUpdater: function(node, value) {
node.value = typeof value == 'undefined' ? '' : value;
},
};
function Vue(options, exp) {
this.data = options.data();
let self = this;
Object.keys(this.data).forEach(function (key) {
self.proxyKeys(key);
});
observe(this.data);
// 8. 操作DOM部分交給Compile處理
new Compile(options.el, this);
// 9. 給Vue掛載mounted方法
options.mounted.call(this);
}
Vue.prototype = {
proxyKeys: function (key) {
let self = this;
Object.defineProperty(this, key, {
get: function () {
return self.data[key];
},
set: function (newVal) {
self.data[key] = newVal
}
})
}
}
</script>
<div id="app">
<h1>{{userName}}</h1>
<input type="text" v-model="userName">
</div>
<script>
new Vue({
el: "#app",
data() {
return {
"userName": "hello VueJS"
}
},
mounted() {
setTimeout(() => {
this.userName = "漂亮";
}, 2000);
}
});
</script>
~~~
[當前頁源代碼](https://github.com/lidongxuwork126com/ldx_vue/tree/master/%E4%BB%BFVue%E6%BA%90%E7%A0%81)
接下來我們再講講事件綁定, 怎么回事吧, 看下個文章
- web前端
- CSS問題
- 布局
- 雙飛翼布局_flex方式
- 雙飛翼布局_margin方式
- 圣杯布局_flex方式
- 圣杯布局_margin方式
- 子元素居中問題
- 彈性布局
- 概念_
- 標準模式與混雜模式
- 各種FC
- line-height
- vertical-align
- CSS3新特性
- 效果
- div添加箭頭
- CSS繪制三角形
- JavaScript
- 兼容
- 事件相關
- 原理
- Ajax原理
- 繼承原理
- 原型鏈繼承
- 組合繼承
- 寄生組合繼承
- 數據綁定
- 1單向數據綁定m到c到v
- 2偽雙向數據綁定
- 3雙向數據綁定
- socket.io
- 運行時
- this指向
- 0.1+0.2問題
- 對象/數組-深拷貝&淺拷貝
- 事件循環
- typeof
- instanceof
- 概念
- 閉包
- 回調函數
- Promise
- 原生對象
- Attribute和property區別
- 防抖函數
- 節流函數
- 語言類型
- Vue
- Vue優缺點
- 仿Vue源碼
- 1數據綁定_Observe
- 2數據綁定_訂閱者&觀察者定義
- 3數據綁定_Vue類實現
- 4數據綁定_Vue訪問data更改
- 5DOM編譯_Compile_雙大括號模板講解
- 6DOM編譯_v-model講解
- 7DOM編譯_v-on:事件綁定講解
- 項目總結
- 使用Svg圖標
- vueCli環境_真機測試
- vueCli集成環信SDK
- 父子組件雙向綁定
- React
- React優缺點
- 我的組件庫
- Vue的組件庫
- 環信_聊天組件
- 面試題
- HTML_分類
- CSS_分類
- JavaScript_分類
- VueJS_分類
- ReactJS_分類
- AngularJS_分類
- 瀏覽器端
- 筆試題
- CSS
- 特殊布局
- JavaScript_
- 經典_宏任務_微任務
- 瀏覽器問題
- CORS
- web服務器
- Apache
- 開啟跨域
- Nginx
- 常用命令
- 正向代理
- 反向代理
- 負載均衡
- mac安裝Nginx
- 配置80端口
- 算法
- 冒泡排序
- 選擇排序
- 合并對象_排序
- 楊輝三角
- 紅黑樹
- 計算機基礎
- 網絡相關
- OSI七層模型
- http協議
- http工作原理
- https協議
- GET和POST區別
- hosts文件
- php相關
- session機制
- Linux
- 阿里云服務器
- linux使用Mysql
- 安裝mysql
- 導入.sql文件
- 遠程連接mysql
- linux使用xampp
- 安裝Xampp
- 配置web訪問
- 域名綁定服務器
- linux搭建git服務器_apache下
- 代碼管理
- 什么是git
- 命令行_使用git
- .gitignore文件講解
- 軟件
- VSCode的安裝
- 理財
- 基金
- 攝影