耀极客论坛

 找回密码
 立即注册
查看: 3146|回复: 0

Vue data中随意改一个属性,视图都会更新吗?

[复制链接]

193

主题

176

帖子

276

积分

中级会员

Rank: 3Rank: 3

积分
276
发表于 2022-5-9 02:17:34 | 显示全部楼层 |阅读模式
  这篇文章主要讨论Vue data中随意改一个属性,视图都会更新吗?下面来自面试官的问题然后做i出的一个问题总结,具有一定的参考价值,需要的小伙伴可以参考一下
       
  • 面试官:看过 Vue 的源码没?   
  • 候选者:看过。   
  • 面试官:那你说下 Vue data 中随意更改一个属性,视图都会被更新吗?   
  • 候选者:不会。   
  • 面试官:why?   
  • 候选者:如果该属性没有被用到 template 中,就没有必要去更新视图,频繁这样性能不好。   
  • 面试官:那 Vue 中是如何去实现该方案的?   
  • 候选者:在实例初始化过程中,利用Object.defineProperty对 data 中的属性进行数据监听,如果在 template 中被使用到的属性,就被 Dep 类收集起来,等到属性被更改时会调用notify更新视图。   
  • 面试官:那你怎么知道那些属性是在 template 被用到的呢?   
  • 候选者:WTF。。。这个倒不是很清楚,您能解释下吗?   
  • 面试官:OK,那我就简单解释下:
  先写个简单的 demo,其中 data 中有 4 个属性a,b,c,d,在模板中被利用到的属性只有a,b。看看是不是只有a,b才会调用Dep收集起来呢?
  1. new Vue({
  2.   el: '#app',
  3.   data() {
  4.     return {
  5.       a: 1,
  6.       b: 2,
  7.       c: 3,
  8.       d: 4,
  9.     };
  10.   },
  11.   created() {
  12.     console.log(this.b);
  13.     this.b = 'aaa';
  14.   },
  15.   template: '‹div>Hello World{{a}}{{b}}‹/div>',
  16. });
复制代码
  在Vueinstance/state.js里面,会利用proxy把每个属性都 代理一遍
  1. const keys = Object.keys(data)
  2.   const props = vm.$options.props
  3.   const methods = vm.$options.methods
  4.   let i = keys.length
  5.   while (i--) {
  6.     const key = keys[i]
  7.     if (props && hasOwn(props, key)) {
  8.       process.env.NODE_ENV !== 'production' && warn(
  9.         `The data property "${key}" is already declared as a prop. ` +
  10.         `Use prop default value instead.`,
  11.         vm
  12.       )
  13.     } else if (!isReserved(key)) {
  14.       // 代理对象的属性
  15.       proxy(vm, `_data`, key)
  16.     }
  17.   }
  18.   // observe data
  19.   observe(data, true /* asRootData */)
复制代码
  利用defineReactive对data中的每个属性进行劫持
  1. observe(data, true /* asRootData */);
  2. // observe
  3. const keys = Object.keys(obj);
  4. for (let i = 0; i ‹ keys.length; i++) {
  5.   defineReactive(obj, keys[i]);
  6. }
  7. // defineReactive
  8. Object.defineProperty(obj, key, {
  9.   enumerable: true,
  10.   configurable: true,
  11.   get: function reactiveGetter() {
  12.     const value = getter ? getter.call(obj) : val;
  13.     // 重点在这里,后续如果在模板中使用到的属性,都会被执行reactiveGetter函数
  14.     // 被Dep类 收集起来
  15.     if (Dep.target) {
  16.       console.log(`${key} 属性 被Dep类收集了`)
  17.       dep.depend();
  18.       if (childOb) {
  19.         childOb.dep.depend();
  20.         if (Array.isArray(value)) {
  21.           dependArray(value);
  22.         }
  23.       }
  24.     }
  25.     return value;
  26.   },
  27.   set: function reactiveSetter(newVal) {
  28.     const value = getter ? getter.call(obj) : val;
  29.     /* eslint-disable no-self-compare */
  30.     if (newVal === value || (newVal !== newVal && value !== value)) {
  31.       return;
  32.     }
  33.     if (setter) {
  34.       // 这里是处理computed set 函数
  35.       setter.call(obj, newVal);
  36.     } else {
  37.       val = newVal;
  38.     }
  39.     childOb = !shallow && observe(newVal);
  40.     // 如果我们在更改属性时,就会调用notify 异步更新视图
  41.     dep.notify();
  42.   },
  43. });
复制代码
  执行$mount进行视图挂载
  1. if (vm.$options.el) {
  2.   vm.$mount(vm.$options.el);
  3. }
复制代码
  $mount 是调用 Vue 原型上的方法, 重点是最后一句 mount.call(this, el, hydrating)
  1. Vue.prototype.$mount = function (
  2.   el?: string | Element,
  3.   hydrating?: boolean
  4. ): Component {
  5.   el = el && query(el);
  6.   const options = this.$options;
  7.   // resolve template/el and convert to render function
  8.   /**
  9.    * 查看render 函数是否存在?如果不存在就解析template模板
  10.    * Vue渲染页面时,有两个方式 1. template,2. render,最终所有的模板类的都需要使用render去渲染
  11.    */
  12.   if (!options.render) {
  13.     let template = options.template;
  14.     if (template) {
  15.       if (typeof template === 'string') {
  16.         if (template.charAt(0) === '#') {
  17.           template = idToTemplate(template);
  18.           /* istanbul ignore if */
  19.           if (process.env.NODE_ENV !== 'production' && !template) {
  20.             warn(
  21.               `Template element not found or is empty: ${options.template}`,
  22.               this
  23.             );
  24.           }
  25.         }
  26.       } else if (template.nodeType) {
  27.         template = template.innerHTML;
  28.       } else {
  29.         if (process.env.NODE_ENV !== 'production') {
  30.           warn('invalid template option:' + template, this);
  31.         }
  32.         return this;
  33.       }
  34.     } else if (el) {
  35.       // 如果模板不存在,就创建一个默认的html模板
  36.       template = getOuterHTML(el);
  37.     }
  38.   }
  39.   // 重写了Vue.prototype.$mount ,最终调用缓存的mount方法完成对$mount的挂载
  40.   return mount.call(this, el, hydrating);
  41. };
复制代码
  这里mount调用了 mountComponent(this, el, hydrating) 方法,而 mountComponent是执行了 _render函数,最终_render是调用render 生成一个vnode。
  1. const { render, _parentVnode } = vm.$options;
  2. vnode = render.call(vm._renderProxy, vm.$createElement);
复制代码

  最后一张图可以看到是render函数在渲染我们demo里面的template模板,最终只有a, b两个属性才会被Dep类收集起来。

  如果文中有错误的地方,麻烦各位指出,我会持续改进的。谢谢, 需要调试源码的,这里点击这里,按照 readme操作即可。希望star下
  到此这篇关于Vue data中随意改一个属性,视图都会更新?的文章就介绍到这了,更多相关Vue data 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|耀极客论坛 ( 粤ICP备2022052845号-2 )|网站地图

GMT+8, 2022-12-10 02:51 , Processed in 0.064943 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表