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

 更新时间:2021年12月15日 11:42:43   作者:Rudy24  
这篇文章主要讨论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收集起来呢?

new Vue({
  el: '#app',
  data() {
    return {
      a: 1,
      b: 2,
      c: 3,
      d: 4,
    };
  },
  created() {
    console.log(this.b);
    this.b = 'aaa';
  },
  template: '<div>Hello World{{a}}{{b}}</div>',
});


在Vueinstance/state.js里面,会利用proxy把每个属性都 代理一遍

const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {
      // 代理对象的属性
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  observe(data, true /* asRootData */)


利用defineReactive对data中的每个属性进行劫持

observe(data, true /* asRootData */);

// observe
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
  defineReactive(obj, keys[i]);
}

// defineReactive
Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function reactiveGetter() {
    const value = getter ? getter.call(obj) : val;
    // 重点在这里,后续如果在模板中使用到的属性,都会被执行reactiveGetter函数
    // 被Dep类 收集起来
    if (Dep.target) {
      console.log(`${key} 属性 被Dep类收集了`)
      dep.depend();
      if (childOb) {
        childOb.dep.depend();
        if (Array.isArray(value)) {
          dependArray(value);
        }
      }
    }
    return value;
  },
  set: function reactiveSetter(newVal) {
    const value = getter ? getter.call(obj) : val;
    /* eslint-disable no-self-compare */
    if (newVal === value || (newVal !== newVal && value !== value)) {
      return;
    }
    if (setter) {
      // 这里是处理computed set 函数
      setter.call(obj, newVal);
    } else {
      val = newVal;
    }
    childOb = !shallow && observe(newVal);
    // 如果我们在更改属性时,就会调用notify 异步更新视图
    dep.notify();
  },
});

执行$mount进行视图挂载

if (vm.$options.el) {
  vm.$mount(vm.$options.el);
}


$mount 是调用 Vue 原型上的方法, 重点是最后一句 mount.call(this, el, hydrating)

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el);

  const options = this.$options;
  // resolve template/el and convert to render function
  /**
   * 查看render 函数是否存在?如果不存在就解析template模板
   * Vue渲染页面时,有两个方式 1. template,2. render,最终所有的模板类的都需要使用render去渲染
   */
  if (!options.render) {
    let template = options.template;
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template);
          /* istanbul ignore if */
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            );
          }
        }
      } else if (template.nodeType) {
        template = template.innerHTML;
      } else {
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this);
        }
        return this;
      }
    } else if (el) {
      // 如果模板不存在,就创建一个默认的html模板
      template = getOuterHTML(el);
    }
  }
  // 重写了Vue.prototype.$mount ,最终调用缓存的mount方法完成对$mount的挂载
  return mount.call(this, el, hydrating);
};

这里mount调用了 mountComponent(this, el, hydrating) 方法,而 mountComponent是执行了 _render函数,最终_render是调用render 生成一个vnode。

const { render, _parentVnode } = vm.$options;
vnode = render.call(vm._renderProxy, vm.$createElement);


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

如果文中有错误的地方,麻烦各位指出,我会持续改进的。谢谢, 需要调试源码的,这里点击这里,按照 readme操作即可。希望star下

到此这篇关于Vue data中随意改一个属性,视图都会更新?的文章就介绍到这了,更多相关Vue data 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 五步教你用Nginx部署Vue项目及解决动态路由刷新404问题

    五步教你用Nginx部署Vue项目及解决动态路由刷新404问题

    nginx 是一个代理的服务器,下面这篇文章主要给大家介绍了关于如何通过五步教你用Nginx部署Vue项目及解决动态路由刷新404问题的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • 教你用Vue基础语法来写一个五子棋小游戏

    教你用Vue基础语法来写一个五子棋小游戏

    在布局上,五子棋相比那些目标是随机运动的游戏,实现起来相对简单许多,思路也很清晰,下面这篇文章主要给大家介绍了关于用Vue基础语法来写一个五子棋小游戏的相关资料,需要的朋友可以参考下
    2022-06-06
  • Vue3实现pdf预览功能

    Vue3实现pdf预览功能

    在开发中,PDF预览和交互功能是一个常见的需求,无论是管理系统、在线教育平台,还是企业内部的知识库,能够高效地展示和操作PDF文件都能极大地提升用户体验,本文将详细介绍如何在Vue项目中实现pdf预览功能,需要的朋友可以参考下
    2025-03-03
  • Vue.js基础之监听子组件事件v-on及绑定数据v-model学习

    Vue.js基础之监听子组件事件v-on及绑定数据v-model学习

    这篇文章主要为大家介绍了Vue.js基础之监听子组件事件v-on及绑定数据v-model学习,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Vue项目中如何引入本地第三方JS库

    Vue项目中如何引入本地第三方JS库

    vue中常遇到第三方js,这篇文章主要给大家介绍了关于Vue项目中如何引入本地第三方JS库的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • 快速处理vue渲染前的显示问题

    快速处理vue渲染前的显示问题

    下面小编就为大家分享一篇快速处理vue渲染前的显示问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • Vuex的插件vuex-persistedstate数据持久化存储操作

    Vuex的插件vuex-persistedstate数据持久化存储操作

    这篇文章主要介绍了Vuex的插件vuex-persistedstate数据持久化存储操作,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-12-12
  • Vue学习笔记进阶篇之vue-cli安装及介绍

    Vue学习笔记进阶篇之vue-cli安装及介绍

    这篇文章主要介绍了Vue学习笔记进阶篇之vue-cli安装及介绍,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • Vue3通过ref操作Dom元素及hooks的使用方法

    Vue3通过ref操作Dom元素及hooks的使用方法

    这篇文章主要介绍了Vue3通过ref操作Dom元素及hooks的使用方法,需要的朋友可以参考下
    2023-01-01
  • 解决VantUI popup 弹窗不弹出或无蒙层的问题

    解决VantUI popup 弹窗不弹出或无蒙层的问题

    这篇文章主要介绍了解决VantUI popup 弹窗不弹出或无蒙层的问题。具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11

最新评论