Vue3.0学习和理解,面试可以装起来

Vue3.0的几大亮点

  1. 性能比vue2.x 快1.2~2倍
  2. 按需编译,体积比vue2.x更小
  3. composition Api (组和api类似React Hooks)
  4. 本身就是ts从写的更好的支持ts
  5. custom Renderer api 暴露了自定义渲染的api
  6. Teleport(Protal),Suspense 更先进的组件

Vue.3.0是如何变快的

1.优化了diff算法

  • Vue2.x中的虚拟dom是进行全量的对比
  • Vue3.x新增了动态标记位(PatchFlag),在与上次虚拟节点进行比较的时候,只对比带有patchflag的节点,并且可以通过flag的信息得知当前节点要对比的具体内容

image.png

Vue2.x中首先 div会和div比较h1和h1比较,p和p比较,这就是全量比较,其中只有p标签改变了值,比较的时候是每个节点对比,其中h1的值是写死的根本不会变,按道理就不应该去比较,所以vue3.0做了优化

image.png

Vue3.0的比较只比较和追踪带有标记位的

vue-next-template-explorer.netlify.app/

代码示例:

<div>
     <h1>这是一个测试</h1>
     <h1>这是一个测试</h1>
     <h1>这是一个测试</h1>
     <p>{{msg}}</p>
</div>
复制代码
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _createElementVNode("h1", null, "这是一个测试"),
    _createElementVNode("h1", null, "这是一个测试"),
    _createElementVNode("h1", null, "这是一个测试"),
    _createElementVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
  ]))
}

// Check the console for the AST
复制代码

image.png

在创建虚拟Dom节点的时候,只有p标签插入了一个静态标记位1

下列为标记值所代表的含义:

export const enum PatchFlags {

  TEXT = 1, *//动态文字内容*

  CLASS = 1 << 1,  *// 2  动态 class*

  STYLE = 1 << 2,  *//4 动态样式*

  PROPS = 1 << 3, *//8 动态属性 props (不包含类名和样式)*

  FULL_PROPS = 1 << 4,  *//16 有动态的key属性,当key改变时需要进行完整的diff比较*

  HYDRATE_EVENTS = 1 << 5,   *//32 带有监听事件的节点*

  STABLE_FRAGMENT = 1 << 6,   *//64 一个不会改变子节点顺序的 fragment*

  KEYED_FRAGMENT = 1 << 7,  *// 128 带有key的节点的fragment或者部分子节点带有key*

  UNKEYED_FRAGMENT = 1 << 8,  *//256 子节点没有key的fragment*

  NEED_PATCH = 1 << 9,   *//512 一个节点只会进行非props比较,比如`ref`*

  *// 动态的插槽*

  DYNAMIC_SLOTS = 1 << 10,

   *// SPECIAL FLAGS -------------------------------------------------------------*

   *// 以下是特殊的flag,不会在优化中被用到,是内置的特殊flag*
 
   *// 表示他是静态节点,他的内容永远不会改变,对于hydrate的过程中,不会需要再对其子节点进行diff*

  HOISTED = -1,

   *// 用来表示一个节点的diff应该结束*

  BAIL = -2,

}
复制代码

2. hoistStatic (静态提升)

这个是没有添加静态提升的代码,无论是更新还是不需要更新的每次都要从新生成节点 列如:3个p标签 开启静态提升之前:

image.png 点击右侧的options选中 image.png 开启静态提升以后:

import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

const _hoisted_1 = /*#__PURE__*/_createElementVNode("h1", null, "这是一个测试", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createElementVNode("h1", null, "这是一个测试", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createElementVNode("h1", null, "这是一个测试", -1 /* HOISTED */)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _hoisted_1,
    _hoisted_2,
    _hoisted_3,
    _createElementVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
  ]))
}

// Check the console for the AST
复制代码

image.png

选中静态提升的时候,h1的虚拟节点被从render方法中提升到全局变量中,这样就只创建一次,每次render的时候直接复用

3.事件侦听缓存

随便搞个栗子,开启事件侦听缓存之前:

<div>
     <button @click='clickEvent'></button>
</div>
复制代码
 import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

const _hoisted_1 = ["onClick"]

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _createElementVNode("button", { onClick: _ctx.clickEvent }, null, 8 /* PROPS */, _hoisted_1)
  ]))
}

// Check the console for the AST
复制代码

image.png

可以看到这个标记位的值是8,被当成了动态属性,这样就会被对比,这里每次都是同一个方法没必要去进行对比

开启事件侦听缓存之后:

image.png

image.png

标记位没有了,没有被追踪就没有产生对比了

Vue3.0的diff算法中只有有静态标记位的才会对比才会追踪。

Vue中ref和reactive的对比:

reactive

  •  reactive 是vue3.0提供的响应式数据的方法
  •  reactive 是通过es6中的proxy来实现的,接收的参数必须是对象(json/arr)传入其他的值不会被监听
  • 如果给reactive传递其他对象,默认情况下修改对象,视图不会更新,想要更新可以通过从新赋值的方式.如传入的是时间对象,值其实更新了,视图并灭有改变,想要改变就从新赋值

image.png

image.png

这样视图是没有更新的

**从新赋值以后视图更新

 setup(){
    const state=reactive({time:new Date()})
    const change=()=>{
      //时间从新赋值
      let newTime=new Date(state.time.getTime());

      newTime.setDate(state.time.getDate()+1);
      state.time=newTime;
       console.log(state.time)
    }
    return {
       ...toRefs(state),
       change

    }
  }
复制代码

image.png

Ref

  • 1.Ref底层的本质还是 reactive 系统会自动根据我们给ref传入的值将它转化为
  • Ref(xx)------->reactive({value:xxx})
  • 2. 在模板中获取ref 的值不用通过value,在js中需要,那为什么reactive就不用.value去修改值呢,它是怎么判断的呢

image.png

  • 3.之所会有ref是因为reactive只监听对象或者数组,想监听某个变量比较繁琐,所以就有了ref可以单独监听某个变量

好了编不下去了,累------未完待续

猜你喜欢

转载自juejin.im/post/7033702806892576775