vue数据双向绑定的原理
一 复习闭包
1 闭包含义:
当函数嵌套时,内部函数使用了外部函数的变量,就会产生闭包
当函数可以记住并访问自己的作用域时,就会产生闭包
2 闭包注意点
① 队列里的代码执行完毕,同步的已经执行完了
② 作用域时js代码解析时创建,关浏览器时销毁
③ 作用域是来维护变量的查找规则的
④ 一个函数作用域一般对应一个执行上下文,除非函数被递归调用
⑤ 执行上下文是函数调用时创建,调用完销毁
⑥ 执行上下文是来存函数执行时需要的数据的
3 鸡肋闭包
// 5.在全局作用域对应的处于活动状态下的执行上下文中寻找 如果找到就使用 如果找不到 报错!
function wrap(){
// 3.在wrap作用域对应的处于活动状态下的执行上下文中寻找 如果找到就使用
// 4.如果找不到查看闭包 如果闭包中也没有如果找不到上上一层作用域
var test = "wrap_test";
function inner(){
//1. 首先在inner作用域对应的处于活动状态下的执行上下文中寻找 如果找到就使用
//2. 如果找不到查看闭包 如果闭包中也没有上上一层作用域
console.log(test)
}
inner()
}
wrap()
作用域:全局作用域,wrap函数作用域 , inner作用域
执行上下文:全局执行上下文 wrap执行上下文 inner执行上下文
4 经典闭包
function wrap(){
var test = "wrap_test";
return function(){
console.log(test)
}
}
var inner = wrap();
inner()
5 异步代码闭包
当函数可以记住并访问自己的作用域链时,就会产生闭包
function wrap(){
var test = "wrap_test";
setTimeout(function (){
console.log(test)
},0)
}
wrap()
6 异步面试题
闭包和内部函数是一一对应的 先同步执行,异步执行进了队列等待同步的执行完毕
// 第一次定时器执行时 定时器的回调拿到了一个闭包 闭包的值为{i:0}
// 第二次定时器执行时 定时器的回调拿到了一个闭包 闭包的值为{i:1}
// 第三次定时器执行时 定时器的回调拿到了一个闭包 闭包的值为{i:2}
// 第四次定时器执行时 定时器的回调拿到了一个闭包 闭包的值为{i:3}
// 第五次定时器执行时 定时器的回调拿到了一个闭包 闭包的值为{i:4}
for(var i=0;i<5;i++){
(function(i){
// i: 当前function自己的i
setTimeout(function(){
console.log(i);
},1000)
})(i)
// i :全局的i
}
//一秒钟之后同时输出01234
for(var i=0;i<5;i++){
(function(i){
setTimeout(function(){
console.log(i);
},1000*i)
})(i)
}
/*如果同步代码执行的时间比较短,那么应该是每隔一秒钟 输入 0 1 2 3 4*/
如果同步代码执行的时间比较长,那么就会一起在在队列中等待,同步代码执行完毕,会同时输出
二 文档碎片
1 querySelector
querySelector拿到的是静态列表,而getElement拿到的则是动态列表
window.onload=function (){
//querySelector系列拿到是静态列表!!!!
var liNodes = document.querySelectorAll("ul li");
var listNode = document.querySelector("#list");
//getxxx系列拿到的是动态列表
// var liNodes = listNode.getElementsByTagName("li");
//dom操作
listNode.innerHTML+="<li>5</li>"
liNodes = document.querySelectorAll("ul li");
//点灯
for(var i=0;i<liNodes.length;i++){
liNodes[i].style.background="pink";
}
}
2 template
template.html
<body>
<template>
<img :src="url" >
</template>
</body>
<script src="./js/text.js"></script>
text.js
var temp = document.querySelector("template");
//temp.content:文档碎片对象
var fragment = temp.content;
console.dir(fragment);
//start
var imgNode = fragment.querySelector("img");
imgNode.src = "./img/vuex.png"
//在start和end之间进行大量的dom操作 且不会触发dom树更新 不会引起浏览器的重新渲染
//end
//给body元素加一个子节点
//将文档碎片作为append的参数时 加入到dom树中的不是该文档碎片 而是文档碎片所有子节点
document.body.append(fragment)
temp.remove()
}
案例
window.onload=function () {
var ulNode = document.querySelector("ul");
//创建一个文档碎片
var fragment = document.createDocumentFragment();
var child = null;
//firstChild 会考虑文本节点的
while (ulNode.firstChild){
// appendChild的参数如果是存在于dom树内部的节点 那appendChild是剪切操作
// appendChild的参数如果是一个新创建的节点 那appendChild是添加操作
fragment.appendChild(ulNode.firstChild)
}
//dom树中将不会再有节点 而文档碎片中会塞入所有的ul的子节点
//进行大量的dom操作 且界面不会更新 提供了dom操作的性能
Array.from(fragment.children).forEach((item)=>{
item.innerHTML = "尚硅谷-" + item.innerHTML;
})
//appendChild 让 fragment作为参数 最终追加到文档中的是fragment的子节点列表 而不是fragment本身
ulNode.appendChild(fragment)
三 数据双向绑定
1 js属性
var obj = {a:"a"}
obj.b="b"
obj.c="c"
Object.defineProperty(obj,"c",{
// value:"c", //c的属性值
// writable:true, //当前属性是否可以被修改
enumerable:true,//当前属性是否可枚举(当前属性是否可以出现在对象的for in循环中)
configurable:true,//当前属性是否可以重新配置(是否可以被delete关键字删除 是否可以通过defineProperty重新定义)
//get set 和 writable value 只能出现一组
get(){return "ccc"},
set(newval){}
})
console.log(obj);
2 断点
resume播放按钮:跳到下一个断点处 给回调函数使用(异步代码)
step over:直接跳到下一行代码 如果当前行是个函数,则函数会被立马执行完
step into :跳到函数内部执行代码
step out:跳出该函数
3 mvvm原理
mvvm是 Model-View-ViewModel 的缩写,即 模型-视图-视图模型。
① Model:数据模型,后端传递的数据。(data,props,computed等部分)
② View:代表 UI 组件,它负责将数据模型转化成 UI 展现出来。(template部分)
③ ViewModel:是一个同步View 和 Model的对象。MVVM模式的核心,它连接Model和View的桥梁。
vue的核心,双向绑定、监听(watch)、操作(methods)等部分
数据代理:将data对象中直接属性一个个拿出来给vm实例对象新增同名的属性,此处的代理是浅代理
数据劫持:将data对象中所有属性一个个拿出来重新定义,让他们具备get set方法,此处劫持是深度的
模板解析
dep watcher
① dep(data中的每一个数据):
data中的每一个属性都分配了一个dep,在对data中的数据进行读取或修改时这个dep闭包都会被加载
② watcher(模板上的每一个指令):
在每一个指令被解析的最后一步,都会去构建一个watcher对象,这个对象拥有更新器闭包
模板写法:
<div id = "app">
<div v-html="obj.wife.name"></div>
<span>{{obj.wife.name}}</span>
</div>
一个watcher可能要对用多个dep
一个dep可能对应多个watcher
为了响应式更新,vue需要建立dep和watcher之间的多对多关系