面试官: 连 Vue 视图更新都不会写?(Vue视图更新原理【一】)

1,292 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

写在前面

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 采用的是对称加密还是非对称加密?具体说说其加密过程

面试官:说说 Cookie 和 Token 的区别?

面试官:网络安全了解多少,简单说说?(一)

面试官:网络安全了解多少,简单说说?(二)

面试官:网络安全了解多少,简单说说?(三)

面试官:网络安全了解多少,简单说说?(四)

面试官:网络安全了解多少,简单说说?(五)

面试官:网络安全了解多少,简单说说?(六)

面试官:网络安全了解多少,简单说说?(七)

面试官:网络安全了解多少,简单说说?(八)

浅尝 | 从 0 到 1 Vue 组件库封装

面试官:这么简单的正则表达式都不会?

Webpack 打包类库踩坑

面试官:你就只会 npm run build 吗?(Webpack 配置 Vue+Ts)

面试官:连VuePress都没搭过还说开发过组件库?(VuePress 搭建)

面试官: 连 Vue 视图更新都不会写?(Vue视图更新原理【一】)

面试官: 能不能手写 Vue 响应式?(Vue2 响应式原理【完整版】)

面试官:能不能手写 Vue3 响应式(Vue3 原理解析之响应系统的实现)

JS 优雅之道(JS 代码优化小 Tip)

面试官:你真的会用 SVG 吗? (SVG 应用实战)

面试官:说一下这个Loading动画实现思路 (CSS3 实现 Loading 动画)

JS 扫盲题 ( 面试题梳理系列 (一))

面试官:你确定你说的防抖不是节流吗?( 面试题梳理系列 (二))

面试官:除了 HTTP,你还用过什么通信协议?(Websocket 在数字孪生中的应用)

面试官:你真的理解 Event Loop 吗?( JS 事件循环 )

面试官:v-for 中 key 为什么不能用 index,从原理层面聊聊?

面试官:vue-router 的 hash 与 history 哪个模式会刷新页面?

面试官:说说你平时用过的自适应方案(数字孪生可视化自适应方案)

面试官:说一下如何优化过渡动画(数字孪生可视化过渡动画)

写在最后

博主接下来将持续更新好文,欢迎关注博主哟!!
如果文章对您有帮助麻烦亲点赞、收藏 + 关注和博主一起成长哟!!❤️❤️❤️