Vue.js 的核心包括一套“响应式系统”。
“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。
对于官网上关于响应式数据的描述,并不能让人短时间内明白其原理。下面我将按照我的理解分析一下Vue2.0响应式核心代码实现。
Vue中响应式数据分为:对象类型{}
和数组类型[]
对象类型 {}
我们若想要实现响应式,需要以下类和方法:
- 数据
data
- 数据监听器
defineReactive
- 订阅者(更新视图)
watcher
- 维护订阅者
dep
- 状态
active
实现原理: 对象内部通过defineReactive
方法,使用Object.defineProperty
将属性进行劫持(只会劫持已经存在的属性)。
假设页面上有容器app
,data
存放响应式变量,当data
中的值改变时,容器内的数值也会发生变化。
<div id="app"></div>
<script>
let data = {
count:0
}
app.innerHTML = data.count
</script>
1.添加视图更新watcher
<div id="app"></div>
<script>
let data = {
count:0
}
// 定义watcher函数,传入参数为函数,且立即执行
let watcher = (fn)=>{
fn();
}
watcher(()=>{
// 更换app内容
app.innerHTML = data.count;
})
</script>
watcher
所执行的操作 将页面上的内容更新=>视图更新
2.实现数据监听器
将data中的属性依次增加get()
和set()
方法,这样当用户取值的时候,当作模版收集起来。待数据变化通知模版数据更新。
<script>
let data = {
count:0
}
// 数据监听器
function defineReactive(obj) {
//每一个属性都重新定义get、set
for(let key in obj){
let value = obj[key]
Object.defineProperty(obj,key,{
// 当data中的值“出现”的时候,执行get()
get(){
//将获取的原始值返回
return value;
},
// 当data中的值“改变”的时候,执行get()
set(newValue){
value = newValue
}
})
}
}
//劫持data中的数据
defineReactive(data)
//此时的a没有被数据监听器监测到,属于“后来者”不受劫持
data.a = 10;
// 定义watcher函数,传入参数为函数,且立即执行
let watcher = (fn)=>{
fn();
}
watcher(()=>{
// 取值
app.innerHTML = data.count;
})
</script>
3. 当数据改变时更新视图
<div id="app"></div>
<script>
let data = {
count:0
}
//需要执行的视图内容
let active;
// 数据监听器
function defineReactive(obj) {
for(let key in obj){
let value = obj[key];
//存放当前属性相关的所有方法
let dep = [];
Object.defineProperty(obj,key,{
// 当data中的值“出现”的时候,执行get()
get(){
//(3)
if(active){
dep.push(active)
}
return value
},
// 当data中的值“改变”的时候,执行get()
set(newValue){
value = newValue
dep.forEach(active=>active())
}
})
}
}
//劫持data中的数据
defineReactive(data)
// 定义watcher函数,传入参数为函数,且立即执行
let watcher = (fn)=>{
active = fn;
//(1)
fn();
//(4)
active = null;
}
watcher(()=>{
// 更换app内容
//(2)
app.innerHTML = data.count;
})
</script>
当定义watcher
时,会依次执行(1)=>(2)=>(3)=>(4)。
每个属性都拥有自己的dep
属性,存放它所存放的watcher
,当属性变化后会同志自己对应的watcher
去更新。
Vue2.0响应式用的是Object.defineProperty
Vue3.0响应式用的是proxy
当data
中的数据存在多层嵌套的时候,如果用Object.defineProperty
,内部会进行递归,影响性能。proxy
提升性能,但是不兼容ie11。
数组类型 []
数组考虑性能原因没有用defineProperty对数组的每一项进行拦截,而是选择对数组原型上的方法进行重写(push,pop,shift,unshift,splice,sort,reverse
)只有这7种方法会重写数组
<div id="app"></div>
<script>
let data = [1,2,3];
// 获取数组所有方法-原型链
let originArray = Array.prototype;
// 浅拷贝
let arrayMethods = Object.create(originArray);
// 数据监听器
function defineReactive(obj) {
// 函数劫持,重写方法。可以添加自己想要执行的内容
arrayMethods.push = function (...args) {
// 更改this指向
originArray.push.call(this,...args);
render();
}
// 挂载到原型上
obj.__proto__ = arrayMethods
}
defineReactive(data)
// 视图渲染
function render() {
app.innerHTML = data;
}
render();
</script>
在Vue中修改数组的索引和长度是无法监控到的。需要通过以上7种变异方法修改数组才会触发数组对应的watcher
进行更新。数组中如果是对象类型也会进行递归劫持。
如果想要更改索引,可以通过Vue.$set
来进行处理,内部核心代码是splice
方法
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!