>[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的安装
- 理财
- 基金
- 摄影