Vue3学习记录(一)

最近都在忙着搬砖了,没意思没意思。这几天抽空继续学点新东西。Vue3出了快两个月了,现在才补哈哈哈。

Vue3亮点

  • Performance:性能比Vue 2.x快1.2~2倍
  • Tree shaking support:按需编译,体积比Vue2.x更小
  • Composition API: 组合API(类似React Hooks)
  • Better TypeScript support:更好的 Ts 支持
  • Custom Renderer API:暴露了自定义渲染API
  • Fragment, Teleport(Protal), Suspense:更先进的组件

Vue3性能提升的原因

diff算法优化

vue2的diff算法

之前学过,在vue中根据_update将虚拟DOM转为真实DOM并更新视图,其中主要调用了__patch__方法,它会将新老VNode进行比较,然后根据比较结果最小单位的修改视图。__patch的核心在于diff算法。

diff通过同层的树节点进行比较,所以时间复杂度只有O(n):

在这里插入图片描述

这里不讨论diff的具体实现,只需要知道vue2diff的比较是同层树节点进行比较。

假设有这么一段代码:

<template>
  <div>
    <p>我是固定的</p>
    <p>{{message}}</p>
  </div>
</template>

那么每次message更新,就会去逐层比较,但是其实最外层的div和第一个子元素p是固定的,是不会改变的。vue3改良了diff的算法比较。

vue3的diff算法

vue3新增了静态标记(PatchFlag),每次比较的时候只会比较带有patch flag的节点,并且通过pacth flag的类型得知要比较的内容。

// pacth flag 值与类型的对应关系
export const enum PatchFlags {
  TEXT = 1,// 动态文本节点
  CLASS = 1 << 1, // 2  // 动态 class
  STYLE = 1 << 2, // 4 // 动态 style
  PROPS = 1 << 3, // 8 // 动态属性,但不包含类名和样式
  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 比较
  DYNAMIC_SLOTS = 1 << 10, // 1024 // 动态 slot
  HOISTED = -1, // 静态节点
  // 指示在 diff 过程应该要退出优化模式
  BAIL = -2
}

将上述列子,复制到 在线源码转换上看,转为如下源码:

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("template", null, [
    _createVNode("div", null, [
      _createVNode("p", null, "我是固定的"),
      _createVNode("p", null, _toDisplayString(_ctx.message), 1 /* TEXT */)
    ])
  ]))
}

其中只有<p>{{message}}</p>是带有pacth flag标记的(1代表动态文本节点),这样每次更新的时候,只会比较message是否发生改变,而不会比较其他节点,提升了性能。

hoistStatic 静态提升

Vue2中无论元素是否参与更新, 每次都会重新创建, 然后再渲染,而Vue3中对于不参与更新的元素, 会做静态提升, 只会被创建一次, 在渲染时直接复用即可。还是引用上个例子,比较打开hoistStatic可以看到我是固定的这个p节点在不同情况下是否被提升。

在这里插入图片描述

cacheHandlers 事件侦听器缓存

<template>
  <div>
    <button @click="handleChange">改变message</button>
  </div>
</template>

在不使用cacheHandlers下,@click会被视为动态绑定, 因为绑定的函数可能会被改变,所以每次都会去追踪它的变化:

_createVNode("div", null, [
      _createVNode("button", { onClick: _ctx.handleChange }, "改变message", 8 /* PROPS */, ["onClick"])
])

开启cacheHandlers,在第一次渲染时会自动生成一个内联的函数,在内联函数里面引用当前的fn,然后把内联函数cache起来,后续的更新会从缓存中读同一个函数,因为是同一个函数,也就没有追踪变化的必要

_createVNode("div", null, [
      _createVNode("button", {
        onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.handleChange(...args)))
      }, "改变message")
])

ssr渲染

<template>
  <div>
    <p>我是固定的</p>
    <p>我是固定的</p>
    <p>我是固定的</p>
    <p>我是固定的</p>
    <p>我是固定的</p>
    <p>我是固定的</p>
    <p>我是固定的</p>
    <p>我是固定的</p>
    <p>我是固定的</p>
  </div>
</template>
// 无开启
import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue"

const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, [
  /*#__PURE__*/_createVNode("p", null, "我是固定的"),
  /*#__PURE__*/_createVNode("p", null, "我是固定的"),
  /*#__PURE__*/_createVNode("p", null, "我是固定的"),
  /*#__PURE__*/_createVNode("p", null, "我是固定的"),
  /*#__PURE__*/_createVNode("p", null, "我是固定的"),
  /*#__PURE__*/_createVNode("p", null, "我是固定的"),
  /*#__PURE__*/_createVNode("p", null, "我是固定的"),
  /*#__PURE__*/_createVNode("p", null, "我是固定的"),
  /*#__PURE__*/_createVNode("p", null, "我是固定的")
], -1 /* HOISTED */)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("template", null, [
    _hoisted_1
  ]))
}
// 开启
import { mergeProps as _mergeProps } from "vue"
import { ssrRenderAttrs as _ssrRenderAttrs } from "@vue/server-renderer"

export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {
  const _cssVars = { style: { color: _ctx.color }}
  _push(`<template${_ssrRenderAttrs(_mergeProps(_attrs, _cssVars))}><div><p>我是固定的</p><p>我是固定的</p><p>我是固定的</p><p>我是固定的</p><p>我是固定的</p><p>我是固定的</p><p>我是固定的</p><p>我是固定的</p><p>我是固定的</p></div></template>`)
}

开启SSR之后发现,当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面,即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dmo来渲染的快上很多很多。当静态内容大到一定量级时候,会用_createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。

Vue3上手

创建Vue3的3种方式

  • Vue-CLI
  • Webpack
  • Vite

ESM

script module最大的特点就是在浏览器端使用 exportimport 的方式导入和导出模块,目前主流的浏览器都已经支持。使用的时候我们只要在srcipt标签写上type="module"浏览器会把这段内联 script 或者外链 script 认为是 ECMAScript 模块,浏览器将对其内部的 import 引用发起 http 请求获取模块内容。

vite

它是一个由原生ESM驱动的Web开发构建工具,在开发环境下基于浏览器原生ES imports开发,在生产环境下基于Rollup打包。

冷启动快

之前vue使用的是webpackwebpack之类的打包工具是为了在浏览器里加载各模块,会组装各模块;所以冷启动比较慢;而vite利用原生浏览器支持模块化导入的特性,省略了对模块的组装,即不需要生成bundle,所以冷启动比较快。

按需编译

webpack会将各个模块打包进bundle里,但是整个过程是静态的,即不管某个模块的代码是否执行到,都会被打包进bundel中,所以项目越大,打包后bundle越大。而ESM就是按需加载模块的,所以实现了真正的按需编译。

即时更新

vite碰见import会发生http请求去加载文件,vite会拦截这些请求,然后做一些预编译,省去了webpack冗长的打包事件,所以热替换功能很快。

总结vite会拦截浏览器对模块的请求然后返回处理后的结果,import一下需要的包实现了真正的按需加载,由于不会将所有的包进行打包处理所以冷启动比较快,且每次更新也因为省去了这些打包流程所以更新的很快。
不过也是有缺点的: 必须用import,不能用require引npm包,且包必须支持import/umd(如path、mockjs会报错缺default)。

使用vite开发

npm install -g create-vite-app
create-vite-app projectName
cd projectName
npm install
npm run dev
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值