CD's blog CD's blog
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

CD_wOw

内卷的行情,到不了的梦
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Vue2基础及原理

  • Vue3基础及原理

    • Vue3 题解QA
      • Vue3 题解 QA
        • Vue3为何弃用defineProperty
        • 除了使用Proxy之外,Vue3的快还体现在什么地方?
        • Composition Api和Options Api
        • Proxy相对于Object.defineProperty的优点
        • Vue 3.0 在编译方面有哪些优化?
        • vue3 中如何进行像 2.X 那样的函数式调用组件
        • render 函数 2 与 3 的不同
        • Vue3 与 Vue2 的不同
    • 实现一个mini-vue3
    • Vue3实现原理
    • Vue3的响应式源码浅析
    • Vue3的patch原理
  • 周边工具

  • Vue笔记
  • Vue3基础及原理
CD
2021-03-18
目录

Vue3 题解QA

# Vue3 题解 QA

# Vue3为何弃用defineProperty

  1. Object.defineProperty 无法低耗费的监听到数组下标的变化,导致通过数组下标添加元素,不能实时响应;举例来说:因为数组的位置不固定,数量多变,正常对象 key 对应 value 一般不会变,但是如果数组删除了某个元素,比如第一个元素被删除或者头部增加一个元素,那么将导致后面所有的 key 对应 value 错位,如果 6 个元素,也就会触发 5 次 set。Vue 将数组的 7 个变异方法进行了重写,也就是更改了 Array 原型上的方法达到劫持变化。
  2. Object.defineProperty 只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历。如果属性值是对象,还需要深度遍历。 Proxy 可以劫持整个对象, 并返回一个新的对象。
  3. Proxy 不仅可以代理对象,还可以代理数组。
  4. 可以代理动态增加的属性。
  5. 可以监听删除的属性。
  6. 监听数组的索引和 length 属性

Tips: Proxy 虽然是劫持的整个对象,但也是浅层劫持,属性值是对象时同样也需要深度遍历,不然该属性对象的变化也无法监测到。

# 除了使用Proxy之外,Vue3的快还体现在什么地方?

# 源码体积的优化

  1. 重写了虚拟 dom

# 不主动数据劫持

  1. Vue2里面的数据被劫持,均是defineProperty主动为其添加getter\setter方法的。Vue3里面的数据劫持均为被动,对数据有操作,其才会被更改为响应式数据。
  2. 体现在整个系统上,则起码在初始阶段,系统不会那么繁忙。

# 代码编译优化

  1. 使用了 组合 API来代替 vue2 中的 Options API
  2. 组件内不需要根节点了,使用 fragment(代码片段)代替了,fragment(代码片段)不会在页面显示
  3. vue3 中标记和提升所有的静态根节点,diff 的时候只需要对比动态节点内容

# Composition Api和Options Api

  1. 代码更利于维护和封装;
  2. Vue2 中,我们会在一个 vue文件的 data,methods,computed,watch 中定义属性和方法,共同处理页面逻辑 ,一个功能的实现,代码过于分散;
  3. vue3 中,代码是根据逻辑功能来组织的,一个功能的所有 api 会放在一起(高内聚,低耦合),提高可读性和可维护性,基于函数组合的 API 更好的重用逻辑代码;
  4. Vue3 中用 setup 函数代替了 Vue2 中的 befareCreate 和 created;
  5. Vue3 中用 onUnmounted 代替了 Vue2 中的 beforeDestory;
  6. Vue3 中用 unmounted 代替了 Vue2 中的 destroyed;

# Proxy相对于Object.defineProperty的优点

  1. 代码的执行效果更快;
  2. Proxy 可以直接监听对象而非属性;
  3. Proxy 可以直接监听数组的变化;
  4. Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
  5. Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
  6. Proxy 不需要初始化的时候遍历所有属性,另外有多层属性嵌套的话,只有访问某个属性的时候,才会递归处理下一级的属性;

# Vue 3.0 在编译方面有哪些优化?

  • vue3.x 中标记和提升所有的静态节点,diff 的时候只需要对比动态节点内容
  • Fragments(升级 vetur 插件): template 中不需要唯一根节点,可以直接放文本或者同级标签
  • 静态提升(hoistStatic),当使用 hoistStatic 时,所有静态的节点都被提升到 render 方法之外.只会在应用启动的时候被创建一次,之后使用只需要应用提取的静态节点,随着每次的渲染被不停的复用
  • patch flag, 在动态标签末尾加上相应的标记,只有带 patchFlag 的节点才被认为是动态的元素,会被追踪属性的修改,能快速的找到动态节点,而不用逐个逐层遍历,提高了虚拟 dom diff 的性能
  • 缓存事件处理函数 cacheHandler,避免每次触发都要重新生成全新的 function 去更新之前的函数
  • tree shaking 通过摇树优化核心库体积,减少不必要的代码量

# vue3 中如何进行像 2.X 那样的函数式调用组件

这里依旧用 vue2.x 那里的 message 组件类似的写法,重点在于 2 与 3 之间使用的渲染方法的区别。而 vue3 将其核心拆离出来,使用者可以自定义 render 函数生成在不同环境下的代码,如:androd、ios 等。

Vue3.0 的渲染分为

  • Render Phase 渲染阶段 在渲染阶段,将会调用 render 函数,它返回一个虚拟节点
  • Mount Phase 挂载阶段 在挂载阶段,会使用虚拟节点,并调用 Dom Api 挂载到 web 上
  • patch Phase 补丁阶段 在补丁阶段,渲染器会将旧的虚拟节点和新的虚拟节点(当 reactivity 监听到变化时,会再次调用 render 函数生成新的虚拟节点)进行比较并只更新网页变化的部分
import { createVNode, render, isVNode, nextTick } from "vue";
import ImageConstructor from "./image.vue";

let vm;
let container;
/**
 * 传参:
 * @param urlList  {若为dom元素,则查找dom元素内所有的image图片,若为数组,则展示数组内的所有图片}
 * @param zIndex {层级}
 * @param emptyError {未找到img控制台提醒}
 * @param activeIndex {当前需要展示的图片路径,没有则默认展示数组第一张, number|string}
 */
const Image = function (
  opts: any = {
    urlList: [],
    emptyError: false,
    activeIndex: 0,
  }
) {
  if (opts.urlList instanceof HTMLDocument) {
    opts.urlList = Array.from(opts.urlList.getElementsByTagName("img")).map(
      (img) => img.src || ""
    );
  }

  if (!(opts.urlList instanceof Array) || !opts.urlList.length) {
    if (opts.emptyError) {
      console.error("所选模块|传入图片数组为空!");
    }
    return;
  }
  if (typeof opts.activeIndex === "string") {
    opts.activeIndex = opts.urlList.findIndex(opts.activeIndex) || 0;
  }
  container = document.createElement("div");
  opts.close = close;

  vm = createVNode(ImageConstructor, opts);
  console.log(vm);
  render(vm, container);
  document.body.appendChild(container);
};

function close() {
  render(null, vm);
  nextTick(() => {
    document.body.removeChild(container);
  });
}

export default Image;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

在 2x 中其调用方法是挂载在其原型对象上,3x 则给我们提供了指定的挂载路径

// 以下app为Vue为实例
//2x
app.prototype.$message = xxx;

// 3x
app.config.globalProperties.$message = xxx;
1
2
3
4
5
6

# render 函数 2 与 3 的不同

//2 api
render(h) {
  return h('div', {
    attrs: {
      id: 'foo'
    },
    on: {
      click: this.xxx
    }
  }, 'hello')
}

// 3 api
import {h} from 'vue';
render() {
  return h('div', {
    id: 'foo',
    onClick:this.xxx,
  }, 'hello')
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

籍此,我们可以看到两者的不同,一是传入的 options 参数完全扁平化开,若组件内有需要的参数将会作为 props 传入,否则将传入 attrs 中。特别的一提:$listener 也在 3.x 版本并入了 attrs 中。第二点是 createVNode 方法不再针对某个组件渲染,而是在 vue 中全局引入{h}。在 2 中,倘若想将一个大型的组件分割成很多个小的组件的时候,你可能必须要将这个 h 函数一路传递给这些分割函数,从而造成了一些麻烦,而全局的引入降低了心智负担。

# Vue3 与 Vue2 的不同

vue3.0 版本将所有的功能以包的形式分割开来,其中有一个@vue/reactivity 包,里面有 reactive、effect、computed、ref,通过 reactive({haha : 'asdasas'}) 进行了 proxy 代理,通过 effect 为状态,像 react。

  1. Vue3.0 源码有哪几个核心模块组成? reactivity 文件中,分别有 effect、reactive、computed、ref、公共常量及依赖收集的模块

  2. Vue3.0 中响应式原理的作用是什么? 为什么比 Vue2.0 性能高?

  • 3。0 中改用了 proxy 进行依赖收集,对对象进行递归劫持,对处理值的变化进行判断。
  • 2。0 的时候是通过 Dep 和 Watcher 进行依赖的收集和分发,3.0 使用了 effect 将视图渲染和数据响应式分离开来。且采用模块化的引入的形式,在不使用某一些功能的时候即不需要引入功能模块,可以减少打包编译的大小和时间。vue3。0 是在使用到当前对象时才初始化代理,而 vue2.0 的响应式是在一开始便用递归重写被代理对象的 Object.defineProperty。
  1. proxy 和 defineProperty 有什么区别嘛? 一个是劫持操作,一个是劫持属性。 proxy 只需要代理了一个对象的 set、get 操作,那么相当于对这个对象的所有子属性做了劫持。 而 defineProperty 则需要对每一个属性进行遍历然后劫持。因此在 2.x 里,vue 需要对 data 数据进行递归遍历,对 data 数据做深度的 Observer 处理,因此如果传入的 data 是比较深的层级,或者有很多没有用到的数据,都会影响到 vue 的初始化的速度。而 3.x 只需要在对象上做一层代理,而这个对象上就算有很多无需做响应式处理的数据也没关西,因为子对象是否需要被递归代理是在用到的时候才决定的。可以说相比 2.x 基本上没有了 initData 这部分的时间。也无须要通过$set 来添加新属性的监听了。

  2. Vue3 中计算属性为什么要优先于 effect 执行 因为如果通过 effect 去获取计算属性中的值的时候,计算属性通过 dirty 来进行缓存避免多次计算值的调用。如果计算属性内所需要被监听的值发生了改变,但是 computed.value 并没有被及时的修改,就会导致 effect 出来的值不是最新的值,所以需要优先执行并且将 dirty 设为 true。

  3. Vue3 中计算属性中 dirty 的含义 dirty 是为了对计算属性的值是否发生变更所做的一个锁,函数内通过识别 dirty 的值来判断是否需要重新运行计算属性中传入的函数/对象 setter。已避免计算属性在 getter 的过程中多次触发执行 effect。

编辑 (opens new window)
#Vue3#面试
上次更新: 2023/03/22, 15:28:00
Vue2 题解QA
实现一个mini-vue3

← Vue2 题解QA 实现一个mini-vue3→

最近更新
01
gsap动画库学习笔记 - 持续~
06-05
02
远程组件加载方案笔记
05-03
03
小程序使用笔记
03-29
更多文章>
Theme by Vdoing | Copyright © 2020-2023 CD | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式