vue作者尤雨溪在开发 vue3.0 的时候开发的一个基于浏览器原生 ES imports 的开发服务器(开发构建工具)。那么我们先来了解一下vite

Vite

Vite,一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。同时不仅有 Vue 文件支持,还搞定了热更新,而且热更新的速度不会随着模块增多而变慢。针对生产环境则可以把同一份代码用 rollup 打。虽然现在还比较粗糙,但这个方向我觉得是有潜力的,做得好可以彻底解决改一行代码等半天热更新的问题。它做到了本地快速开发启动, 用 vite 文档上的介绍,它具有以下特点:

  • 快速的冷启动,不需要等待打包操作;
  • 即时的热模块更新,替换性能和模块数量的解耦让更新飞起;
  • 真正的按需编译,不再等待整个应用编译完成;

使用 npm:

# npm 7+,需要加上额外的双短横线
$ npm init vite@latest <project-name> -- --template vue

$ cd <project-name>
$ npm install
$ npm run dev

或者 yarn:

$ yarn create vite <project-name> --template vue
$ cd <project-name>
$ yarn
$ yarn dev

概览

 

  • 速度更快
  • 体积减少
  • 更易维护
  • 更接近原生
  • 更易使用
  1. 重写了虚拟Dom实现
    diff算法优化
<div>
  <span/>
  <span>{{ msg }}</span>
</div>

被编译成:

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

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("span", null, "static"),
    _createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
  ]))
}

首先静态节点进行提升,会提升到 render 函数外面,这样一来,这个静态节点永远只被创建一次,之后直接在 render 函数中使用就行了。
Vue在运行时会生成number(大于0)值的PatchFlag,用作标记,仅带有PatchFlag标记的节点会被真正追踪,无论层级嵌套多深,它的动态节点都直接与Block根节点绑定,无需再去遍历静态节点,所以处理的数据量减少,性能得到很大的提升。

 

  1. 事件监听缓存:cacheHandlers

<div>
  <span @click="onClick">
    {{msg}}
  </span>
</div>

优化前:

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

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("span", { onClick: _ctx.onClick }, _toDisplayString(_ctx.msg), 9 /* TEXT, PROPS */, ["onClick"])
  ]))
}

onClick会被视为PROPS动态绑定,后续替换点击事件时需要进行更新。
优化后:

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

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("span", {
      onClick: _cache[1] || (_cache[1] = $event => (_ctx.onClick($event)))
    }, _toDisplayString(_ctx.msg), 1 /* TEXT */)
  ]))
}

会自动生成一个内联函数,这个内联函数里面再去引用当前组件最新的onclick,然后把这个内联函数cache起来,第一次渲染的时候会创建内联函数并且缓存,后续的更新就直接从缓存里面读同一个函数,既然是同一个函数就没有再更新的必要,就变成了一个静态节点
3. SSR速度提高
当有大量静态的内容时,这些内容会被当做纯字符串推进一个buffer里面,即使存在动态的绑定,会通过模板 插值嵌入进去,这样会比通过虚拟dom来渲染的快很多。vue3.0 当静态文件大到一定量的时候,会用_ceratStaticVNode方法在客户端去生成一个static node, 这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染

  1. tree-shaking​​​​​​​

 

tree-shakinng 原理
主要依赖es6的模块化的语法,es6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,
分析程序流,判断哪些变量未被使用、引用,进而删除对应代码
前提是所有的东西都必须用ES6 module的import来写 

按照作者的原话解释,Tree-shaking其实就是:把无用的模块进行“剪枝”,很多没有用到的API就不会打包到最后的包里
在Vue2中,全局 API 如 Vue.nextTick() 是不支持 tree-shake 的,不管它们实际是否被使用,都会被包含在最终的打包产物中。
而Vue3源码引入tree shaking特性,将全局 API 进行分块。如果你不使用其某些功能,它们将不会包含在你的基础包中
5. compositon Api​​​​​​​

 

没有Composition API之前vue相关业务的代码需要配置到option的特定的区域,中小型项目是没有问题的,但是在大型项目中会导致后期的维护性比较复杂,同时代码可复用性不高
compositon api提供了以下几个函数:

vue2使用全局api 如 Vue.component, Vue.mixin, Vue.use等,缺点是会导致所创建的根实例将共享相同的全局配置(从相同的 Vue 构造函数创建的每个根实例都共享同一套全局环境。这样就导致一个问题,只要某一个根实例对 全局 API 和 全局配置做了变动,就会影响由相同 Vue 构造函数创建的其他根实例。)
vue3 新增了createApp,调用createApp返回一个应用实例,拥有全局API的一个子集,任何全局改变 Vue 行为的 API 现在都会移动到应用实例上​​​​​​​

 

2. 组件挂载

  • setup (入口函数,接收两个参数(props,context))

  • ref (将一个原始数据类型转换成一个带有响应式特性)

  • reactive (reactive 用来定义响应式的对象)

  • watchEffect

  • watch

  • computed

  • toRefs (解构响应式对象数据)

  • 生命周期的hooks

    如果用ref处理对象或数组,内部会自动将对象/数组转换为reactive的代理对象
    ref内部:通过给value属性添加getter/setter来实现对数据的劫持
    reactive内部:通过使用proxy来实现对对象内部所有数据的劫持,并通过Reflect反射操作对象内部数据
    ref的数据操作:在js中使用ref对象.value获取数据,在模板中可直接使用

    import { useRouter } from 'vue-router'
    import { reactive, onMounted, toRefs } from 'vue'
    
    // setup在beforeCreate 钩子之前被调用
    // setup() 内部,this是undefined,因为 setup() 是在解析其它组件选项之前被调用的,所以 setup() 内部的 this 的行为与其它选项中的 this 完全不同。这在和其它选项式 API 一起使用 setup() 时可能会导致混淆
    // props 是响应式的,当传入新的 prop 时,它将被更新(因为props是响应式的,所以不能使用 ES6 解构,因为它会消除 prop 的响应性。)
    
    // props参数:包含组件props配置声明且传入了的所有props的对象
    // attrs参数:包含没有在props配置中声明的属性对象,相当于this.$attrs
    // slots参数:包含所有传入的插槽内容的对象,相当于this.$slots
    // emit参数:可以用来分发一个自定义事件,相当于this.$emit
    setup (props, {attrs, slots, emit}) {
      const state = reactive({
        userInfo: {}
      })
    
      const getUserInfo = async () => {
        state.userInfo = await GET_USER_INFO(props.id)
      }
    
      onMounted(getUserInfo) // 在 `mounted` 时调用 `getUserInfo`
    
    // setup的返回值
    
    // 一般都是返回一个对象,为模板提供数据,就是模板中可以直接使用此对象中所有属性/方法
    // 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
    // 返回对象中的方法会与methods中的方法合并成组件对象的方法
    // 若有重名,setup优先
      return {
        ...toRefs(state),
        getUserInfo
      }
    }

    灵活的逻辑组合与复用
    可与现有的Options API一起使用
    与选项API最大的区别的是逻辑的关注点
    选项API这种碎片化使得理解和维护复杂组件变得困难,在处理单个逻辑关注点时,我们必须不断地上下翻找相关代码的选项块。
    compositon API将同一个逻辑关注点相关代码收集在一起
    6. Fragment(碎片)

     

    <template>
      <header>...</header>
      <main v-bind="$attrs">...</main>
      <footer>...</footer>
    </template>

    Vue 3不再限于模板中的单个根节点,它正式支持了多根节点的组件,可纯文字,多节点,v-for等
    render 函数也可以返回数组
    7. Teleport(传送门)​​​​​​​

     

    这个组件的作用主要用来将模板内的 DOM 元素移动到其他位置。
    允许我们控制在 DOM 中哪个父节点下渲染了 HTML

    <teleport to="body">
      <div v-if="modalOpen" class="modal">
        <div>
          I'm a teleported modal!
          (My parent is "body")
          <button @click="modalOpen = false">
            Close
          </button>
        </div>
      </div>
    </teleport>

  • 更好的Typescript支持
    vue3是基于typescipt编写的,可以享受到自动的类型定义提示

  • 自定义渲染 API​​​​​​​

     

    vue官方实现的 createApp 会给我们的 template 映射生成 html 代码,但是要是你不想渲染生成到 html ,而是要渲染生成到 canvas 之类的不是html的代码的时候,那就需要用到 Custom Renderer API 来定义自己的 render 渲染生成函数了。
    意味着以后可以通过 vue, Dom 编程的方式来进行canvas、webgl 编程
    默认的目标渲染平台​​​​​​​

     

    自定义目标渲染平台​​​​​​​

     

  • 响应原理的变化
    vue2对象响应化:遍历每个key,通过 Object.defineProperty API定义getter,setter 进而触发一些视图更新
    数组响应化:覆盖数组的原型方法,增加通知变更的逻辑
    vue2响应式痛点
    递归,消耗大
    新增/删除属性,需要额外实现单独的API
    数组,需要额外实现
    Map Set Class等数据类型,无法响应式
    修改语法有限制
    vue3响应式方案: 使用ES6的Proxy进行数据响应化,解决上述vue2所有痛点,Proxy可以在目标对象上加一层拦截/代理,外界对目标对象的操作,都会经过这层拦截。Proxy可以在目标对象上加一层拦截/代理,外界对目标对象的操作,都会经过这层拦截,相比 Object.defineProperty ,Proxy支持的对象操作十分全面

Logo

前往低代码交流专区

更多推荐