实现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>
- 实现首次渲染数据
未完,不想写了。有心情再写✍🏻