深入了解Vue响应式数据,剖析diff算法 并手写简单diff算法达到Dom响应式编程
Vue响应数据模式
Vue设计理念
vue是渐进式的JavaScript;
如下代码1
// a.html
<div id="app">
<h3>我是标题</h3>
</div>
如果我要改变Dom我们需要这样做:代码2
//
const title = "我是改变后的标题"
const h3 = document.createElement('h3');
h3.textContent = title;
const dom = document.getElementById('app');
setTimeout(res=>{ //延时效果
dom.innerHTML = '';
dom.appendChild(h3);
},2000)
结果是修改一次dom需要10行代码 如果量大 整个html会非常的冗杂 ,如果我改变的只有数据 而dom中可以依据数据来改变 那我们的页面会省略对于dom的操作 如下(使用vue);代码3
// b.html
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<body>
<div id="app">
<h3>{{title}}</h3>
</div>
</body>
<script type="text/javascript">
// const title = "我是改变后的标题"
// const h3 = document.createElement('h3');
// h3.textContent = title;
// const dom = document.getElementById('app');
// setTimeout(res=>{ //延时效果
// dom.innerHTML = '';
// dom.appendChild(h3);
// },2000)
var em = new Vue({
data(){
return {
title: '我是标题'
}
},
mounted() {
setTimeout(res => {
this.title = "我是改变后的标题"
}, 2000)
}
}).$mount('#app')
</script>
当2秒后 我们可以看到 titile数据改变 dom中的title随着改变,这就是响应式数据;我们使用响应式数据模式后,我们只用去思考对于逻辑和数据的书写 ,不用在过多的时间考虑dom的更改,提高编写效率,(为数据驱动!),
接下来我们开始尝试实现一下;
模拟Vue响应式数据 实现
我们可以看到 在2秒后 属于app中的h3标签下的title产生了改变,万变不离根本,(再深入的框架 ,也是由最基础的原生js来编写)当data中的title改变的时候,他的最终操作dom的方式也是如代码2所示,建立dom 获取dom 覆盖dom;这个流程不可少;
所以我们要思考 改变他的原因是什么;改变如下 代码4
// c.html
var em = new Vue({
data(){
return {
title: '我是标题'
}
},
mounted() {
setTimeout(res => {
this.title = "我是改变后的标题"
}, 2000)
}
}).$mount('#app');
我们看到 如果data中的数据改变 html必须是要改变 所以整个data都必须是响应式:代码5
//构造函数Vue
function Vue(option) {
this.$options = option;
this.$data = this.$options.data();
this.isMounted = false;
observe(this.$data);
this.$methods = this.$options.mounted;
// console.log(this)
return this;
}
Vue.prototype.$mount = (el, n) => { // 获取dom结构 追加元素
var that = this[n];//this指向window,这里改成em实例
const parent = document.querySelector(el);
//创建更新函数
console.log(that);
that.update = function() {
console.log('update');
// 首次挂载,次更新
if (!that.isMounted) {
that.isMounted = true;
if (that.$options.mounted) {
that.$options.mounted.call(that.$data);
}
} else {}
}
that.update();
}
//拿到传入的data 就可以做拦截;
// 拦截对象
function observe(obj) {
//遍历obj 每一个key 定义拦截
Object.keys(obj).forEach(key => defineReacitv(obj, key, obj[key]));
}
// 定义响应式函数
function defineReacitv(obj, key, val){ //val形成闭包
Object.defineProperty(obj,key,{
get(){//获取数据
console.log('get',key);
return val;
},
set(newVal){//更新数据
console.log('set',key);
if(newVal !== val){
val = newVal;
}
},
})
}
(闭包:一个函数作用域内的一个局部变量,通过内部函数,给外界去暴露,形成闭包,在内存中保存;)
通过控制台我们可以看到
经过两秒后 更新了title已经显示
当每次更新完 则生成一个dom结构去替换原有的结构 就需要编译器
// dom编辑器 render
//dom编译器
function render(html, val){
html = document.createElement(html);
html.textContent = val;
return html;
}
//prototype 更改如下
Vue.prototype.$mount = (el, n) => { // 获取dom结构 追加元素
var that = this[n]; //this指向window,这里改成em实例
const parent = document.querySelector(el);
//创建更新函数
// console.log(that);
that.update = function(key,val) {
console.log('update');
// const child = that.$options.render.call(this);
const child = render('h3',that.$data[key]);
console.log(render('h3',key));
// 首次挂载,次更新
if (!that.isMounted) {
that.isMounted = true;
parent.appendChild(child);
if (that.$options.mounted) {
that.$options.mounted.call(that.$data);
}
} else {
parent.innerHTML = '';
parent.appendChild(child);
}
}
that.update('title');
}
//更改响应函数defineReacitv
// 定义响应式函数
function defineReacitv(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log('get', key);
return val;
},
set(newVal) {
console.log('set', key);
if (newVal !== val) {
val = newVal;
//update
em.update(key);
}
},
})
}
得到如图
目前只要title更改 页面就可以随title更改。。。响应式完成;
虚拟dom,diff算法
当dom开始随数据变化而变化 无需每次手写dom的更新,友好的模式已经实现,但我们思考一个问题,如上代码都是基于#app节点去更新,也就是说每次更新都是把整个#app去做替换,这样不必要的消耗是很大的,所以Vue和React 便研究出diff算法 把改变了数据的dom进行准确的替换
实现
发现太长了 ····分页发布