准备知识点
1) [].slice.call(lis): 将伪数组转换为真数组
2) node.nodeType: 得到节点类型
节点类型
nodeType nodeName nodeValue
1、元素节点 1 大写标签名 null
2、属性节点 2 属性名 属性值
3、文本节点 3 #text 文本内容
4、注释节点 8 #comment 注释内容
3) Object.defineProperty(obj, propName, {}): 给对象添加/修改属性(指定描述符)
configurable: true/false 是否可以重新define
enumerable: true/false 是否可以枚举(for..in / keys())
value: 指定初始值
writable: true/false value是否可以修改
get: 回调函数, 用来得到当前属性值
set: 回调函数, 用来监视当前属性值的变化
4) Object.keys(obj): 得到对象自身可枚举的属性名的数组
for js当中给的最基本语法 它是专门用来遍历数组的 这个效率一般 但是可以使用break和continue
for...in js当中给的遍历对象的语法 它的效率是最低的,因为不但要遍历自身的属性还要去遍历原型
forEach 它是数组的一个方法,效率是最高的,专门用来遍历数组,但是没办法使用break和continue
for...of 它是遍历可迭代对象的,具有迭代器方法的数据才能使用,它效率比for好,也可以使用break和continue
最好以后禁用for...in 遍历对象
遍历对象全部改为
Object.keys(obj).forEach(key => {
console.log(key,obj[key])
})
5) obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
一般都会配合for...in
for..in遍历的时候遍历自己的属性和原型里面的,我们想要知道遍历的这个属性是不是自身的
就要采用obj.hasOwnProperty(prop)
6) documentfragment
是一个容器,这个容器本身进入不了页面,它内部的所有东西会进去页面,在内存中干活,批量更新
1、可以把原来的元素的子节点,转移到documentfragment 节点当中,原来的里面就没有了
2、去处理documentfragment 中的子元素节点数据
3、最后把documentfragment 节点 追加到原来的元素当中
使用Object.defineProperty在vm身上添加和data当中同名的属性
访问vm身上的属性会调用getter,返回的其实是data中的同名属性值
修改vm身上的属性会调用setter,修改的其实是data中的同名属性数据
做了两件事:
1、根据data中的属性,对应都创建一个dep对象
2、会把data当中的数据变为响应式数据,原理还是添加getter和setter
数据劫持(数据监视)
通过Object.defineProperty对data所有的属性修改响应式(添加getter和setter)
如果用户访问vm身上的属性,返回data的同名属性值,同时就会访问data的同名属性,
data的同名属性是有getter的,返回data的同名属性值
如果用户修改vm身上的属性,修改的是data的同名属性,同时修改data的同名属性
data同名属性也是有setter的,把数给data的同名属性(同时修改页面上的数据)
setter内部其实做了三个事
1、修改自己的数据
2、把修改的新数据也递归调用变为响应式
3、通知页面去修改数据
单独看插值
第一步:从模板当中找到表达式 {{msg}}
第二步:根据从模板中找到的表达式msg,从vm的_data当中获取和msg同名的属性值
第三步:找到text对应的更新器
第四步:将找到的同名属性值把模板当中的{{msg}} 替换
第五步:为当前这个表达式创建watcher,并且和同名属性的dep进行关联绑定
单独看一般指令: 一般指令v-text v-html v-class
第一步:从模板当中找到指令的表达式以及指令名 msg 和 text
第二步:根据从模板中找到的表达式msg,从vm的_data当中获取和msg同名的属性值
第三步:根据指令名找到对应的更新器
第四步:将找到的同名属性值添加到使用指令的元素内容当中,并删除元素对应的指令属性
第五步:为当前这个表达式创建watcher,并且和同名属性的dep进行关联绑定
//无论是插值语法解析还是一般指令解析
//解析完成以后,对于每个表达式都会创建一个watcher对象
//但是事件指令解析完成没有创建watcher,因为事件不能改变
watcher:{
cp:cp //回调函数
vm:vm //vm对象
exp:exp //‘msg’
depIds:{0:dep},
value://当前表达式的值
}
单独看事件指令:(它没有替换之说,触发事件直接调用回调函数就完了)
第一步:从模板当中找到指令的表达式以及指令名 test 和 on:click
第二步:根据on:click判断是不是事件指令
第三步:以表达式test 从vm当中的methods里面找到对应的回调函数
以指令名冒号后面的东西找到事件类型
第四步:将使用这个指令的元素绑定事件,同时把事件回调函数的this永久指向vm对象,最终删除指令属性
数据单向绑定(插值语法和普通指令才会存在单向数据绑定,事件指令没有)
初始化数据展示,说的就是模板解析
模板解析解析完成,页面上的数据才会从文档碎片还给dom
在模板解析的过程当中,除了初始化数据解析展示,还做了模板表达式和data的属性之间的绑定
(watcher和dep两种小弟的关系)
前面我们在数据劫持的时候为每个属性都创建了一个dep对象
{
id:0,
subs:[watcher] //后期存储模板表达式的watcher
}
修改数据之后,我们的data里面的属性数据会改变
接着会调用这个属性的setter,setter里面会通知当前这个属性的dep当中所有的watcher
每个watcher都有一个回调函数,会调用这个回调函数去更新页面对应的数据
双向数据绑定依赖于单向数据绑定
双向数据绑定其实就是多了一个事件 input事件,触发事件修改data的值