本文已参与「新人创作礼」活动,一起开启掘金创作之路
写在前面
Vue、React 可以说是这几年改变前端格局的大杀器,这部分更加高级的框架的出现,狠狠地推进了前端工程化的进度,也使前端能够更加快速,更加规范地完成业务的开发。秉承着底层架构者一贯遵循的执念——-把复杂留给自己,无论是Vue、还是 React ,框架开发者都将前端中最繁琐的部分进行了大量的封装和优化,把简单留给他人。在日复一日 coding 中,我们逐渐习惯了通过修改 data 中的某个值来完成 Dom 的更新,直到某个实习生问你,“为什么我修改了 data 中的值,Dom 就更新了呢?”,你用坚定的眼神,配上深沉的语气对她说,“嗯,对就是这样子的呀,记住就行了!”
说到这里,还不快来给我一起了解下 Vue 的视图更新原理,想想你身后的实习生还在逮着机会问你呢,要是深沉不来,咱要不就偷偷卷一下?
Tip: 本篇为低配版,完整版已更新 --> Vue 响应式原理【完整版】
Vue 视图更新原理
实现响应式的核心API:Object.defineProperty
有兴趣可以了解下介绍:MDN-Object.defineProperty,下面是基础用法介绍
// 模拟 Vue 中的 data
const data = {}
// 对外不可见的内部变量
let _myName = 'Yimwu'
// 响应式监听 data 中的 name
Object.defineProperty(data, "name", {
// 使用 data.name 时 get 方法被调用,返回内部存储变量值
get: () => {
console.log('get')
return _myName
},
// 使用 data.name = xxx 修改变量时,set 方法被调用,设置内部存储变量值
set: (newVal) => {
console.log('set')
_myName = newVal
}
})
console.log(data.name) // 输出 Yimwu get
data.name = 'Mr.Wu' // 输出 set (监听成功)
手写视图更新
这里跟上面 Object.defineProperty 的基础使用相同,只是进行了稍微的拓展,对对象的每个属性都进行了监听,并且利用闭包存储变化的值
// 验证更新是否触发
function updateView(){
console.log('视图更新')
}
// 重新定义属性,监听起来
function defineReactive(target, key, value){
Object.defineProperty(target, key, {
get(){
return value
},
set(newVal){
// value 一直在闭包中,此处设置完成后,下次get能够获取最新设置的值
// 这里有个小优化,若相同则不触发更新
if(newVal !== value){
value = newVal
// 触发更新
updateView()
}
}
})
}
// 监听对象属性
function observe(target){
if(typeof target !== 'object' || target === null) {
// 不是数组或对象不适合监听
return target
}
// 将对象的属性用 defineProperty 重新定义
for(let key in target) {
defineReactive(target, key, target[key])
}
}
// 准备数据
const data = {
name: 'Yimwu',
id: 001,
information: {
tel: '135xxxxx354',
email: '15xxxxx@xx.com'
}
}
// 监听数据
observe(data)
// 测试
data.name = 'YI' // (监听成功)输出 --> 数据更新
data.age = { num: 21 } (监听成功)输出 --> 数据更新
data.information.tel = '13456xxx234' // (监听失败)
data.age.num = 110 // (监听失败)
优化深度监听
// 重新定义属性,监听起来
function defineReactive(target, key, value){
// 再次用value嵌套调用 observe 深,若为对象,则进行进一步监听,若非value非对象则直接返回
observe(value)
Object.defineProperty(target, key, {
get(){
return value
},
set(newVal){
// 对于新增加的值进行深度监听,如 data.id = { num: 101 }, 新增加的 num 也将能够被监听到
observe(newVal)
// value 一直在闭包中,此处设置完成后,下次get能够获取最新设置的值
if(newVal !== value){
value = newVal
// 触发更新
updateView()
}
}
})
}
// 准备数据
const data = {
name: 'Yimwu',
id: 001,
information: {
tel: '135xxxxx354',
email: '15xxxxx@xx.com'
}
}
// 监听数据
observe(data)
// 测试
data.name = 'YI' // (监听成功)输出 --> 数据更新
data.id = { num: 010 } // (监听成功)输出 --> 数据更新
data.id.num = 110 // (监听成功)输出 --> 数据更新
data.information.tel = '00000000000' (监听成功)输出 --> 数据更新
总结
至此,一个简单的 data 监听基本实现,基本上能够做到对对象的各个属性进行深度的监听,但是这并不是最终版,剩下的优化和性能分析留到下一篇进行阐述!欢迎持续关注博主更新哦!
往期好文推荐
面试官:说说从输入 URL 到页面显示到底经历了什么,体现一下你的知识广度
面试官:HTTPS 采用的是对称加密还是非对称加密?具体说说其加密过程
面试官:你就只会 npm run build 吗?(Webpack 配置 Vue+Ts)
面试官:连VuePress都没搭过还说开发过组件库?(VuePress 搭建)
面试官: 连 Vue 视图更新都不会写?(Vue视图更新原理【一】)
面试官: 能不能手写 Vue 响应式?(Vue2 响应式原理【完整版】)
面试官:能不能手写 Vue3 响应式(Vue3 原理解析之响应系统的实现)
面试官:说一下这个Loading动画实现思路 (CSS3 实现 Loading 动画)
面试官:你确定你说的防抖不是节流吗?( 面试题梳理系列 (二))
面试官:除了 HTTP,你还用过什么通信协议?(Websocket 在数字孪生中的应用)
面试官:你真的理解 Event Loop 吗?( JS 事件循环 )
面试官:v-for 中 key 为什么不能用 index,从原理层面聊聊?
面试官:vue-router 的 hash 与 history 哪个模式会刷新页面?
面试官:说说你平时用过的自适应方案(数字孪生可视化自适应方案)
写在最后
博主接下来将持续更新好文,欢迎关注博主哟!!
如果文章对您有帮助麻烦亲点赞、收藏 + 关注和博主一起成长哟!!❤️❤️❤️