vue.js 3.0的优化

vue.js 3.0主要做了哪些优化

  Vue.js1.x2.0版本,最大的升级就是引入了虚拟DOM的概念,它为后续做服务端渲染以及跨端框架Weex提供了基础。
  Vue.js 2.0的不足之处:
    1. 源码自身的维护性
      数据量大后带来的渲染和更新的性能问题
    2. 兼容性
      为了兼容一直保留的鸡肋API

1. 源码优化

  对于Vue.js框架本身开发的优化,它的目的事让代码更易于开发和维护。
  源码的优化主要体现在使用monorepoTypeScript管理和开发源码,这样做的目的是提升自身代码可维护性

1. 更好的代码管理方式:monorepo

  • 相对于Vue.js 2.x的源码组织方式,monorepo把这些模块拆分到不同的package中
    每个package有各自的API、类型定义和测试
    这样使得模块拆分更细化,职责划分更明确,模块之间的依赖关系也更加明确
    开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性
  • package(比如reactivity响应式库)是可以独立于Vue.js使用的
    这样用户如果只想使用Vue.js 3.0的响应式能力,可单独依赖这个响应式库而不用去依赖整个Vue.js,减小了引用包的体积大小

2. 有类型的Javscript:TypeScript

  • 编码期间做类型检查
      避免一些应为类型问题导致的错误
  • 有利于它去定义接口的类型,利于IDE对变量类型的推导
Flow
  • Flow是Facebook出品的JavaScript静态类型检查工具,它可以以非常小的成本对已有的JavaScript代码迁入,非常灵活,这也是Vue.js 2.0当初选型它是一方面的考量
  • Flow对于一些复杂场景类型的检查,支持并不好

2. 性能优化

1. 源码体积的优化

  • 移除一些冷门的feature
      Eg:filter、inline-template
  • 引入tree-shaking的技术,减少打包体积
tree-shaking

  依赖ES2015模块语法的静态结构(即importexport),通过编译阶段的静态分析,找到没有引入的模块并打上标记
Eg:
math.js

export function square(x) {
  return x * x
}
export function cube(x) {
  return x * x * x
}

我们在这个模块外面只引入了cube方法:

import { cube } from './math.js'

最终math模块会被webpack打包生成如下代码:

/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
  'use strict';
  /* unused harmony export square */
  /* harmony export (immutable) */ __webpack_exports__['a'] = cube;
  function square(x) {
    return x * x;
  }
  function cube(x) {
    return x * x * x;
  }
}); 

  可以看到,未被引入的square模块被标记了, 然后压缩阶段会利用例如uglify-jsterser 等压缩工具真正地删除这些没有用到的代码。
  也就是说,利用tree-shaking 技术,如果你在项目中没有引入TransitionKeepAlive 等组件,那么它们对应的代码就不会打包,这样也就间接达到了减少项目引入的Vue.js包体积的目的。

2. 数据劫持优化

  • 实现DOM功能,必须劫持数据的访问和更新
       当数据改变后,为了自动更新DOM,就必须劫持数据的更新,也就是说,当数据发生改变后能自动执行一些代码去更新DOM
  • Vue.js怎么知道更新哪一片DOM呢?
      因为在渲染DOM的时候访问了数据,我们可以对它进行访问劫持,这样就在内部建立了依赖关系,也就知道数据对应的DOM是什么了

  Vue.js 1.xVue.js 2.x内部都是通过 Object.defineProperty这个API去劫持数据的gettersetter,具体是这样的:

Object.defineProperty(data, 'a',{
  get(){
    // track
  },
  set(){
    // trigger
  }
})

  但这个API有一些缺陷,它必须预先知道要拦截的key 是什么,所以它并不能检测对象属性的添加和删除。尽管Vue.js 为了解决这个问题提供了$set$delete 实例方法,但是对于用户来说,还是增加了一定的心智负担。
  另外Object.defineProperty 的方式还有一个问题,举个例子,比如这个嵌套层级比较深的对象:

export default {
  data: {
    A: {
      B: {
        C: {
          D: 1
        }
      }
    }
  }
}

  由于Vue.js 无法判断你在运行时到底会访问到哪个属性,所以对于这样一个嵌套层级较深的对象,如果要劫持它内部深层次的对象变化,就需要递归遍历这个对象,执行Object.defineProperty 把每一层对象数据都变成响应式的。毫无疑问,如果定义的响应式数据过于复杂,这就会有相当大的性能负担。
  为了解决上述 2 个问题,Vue.js 3.0使用了Proxy API做数据劫持,它的内部是这样的:

observed = new Proxy(data, {
  get() {
    // track
  },
  set() {
    // trigger
  }
})

由于它劫持的是整个对象,那么自然对于对象的属性的增加和删除都能检测到。
  但要注意的是,Proxy API 并不能监听到内部深层次的对象变化,因此 Vue.js 3.0 的处理方式是在getter中去递归响应式,这样的好处是真正访问到的内部对象才会变成响应式,而不是无脑递归,这样无疑也在很大程度上提升了性能

3.编译优化

  除了数据劫持部分的优化,我们可以在耗时相对较多的patch阶段想办法,通过在编译阶段优化编译的结果,实现运行时patch过程的优化。Vue.js 2.x的数据更新并触发重新渲染的粒度是组件级的;

Block tree
  • Block tree是一个将模板基于动态节点指令切割的嵌套区块,每个区块内部的节点结构是固定的,每个区块只需要一个Array来追踪自身的动态节点
  • 借助Block treeVue.jsvnode更新性能由与模块整体大小相关提升为与动态内容的数量相关

  除此之外,Vue.js 3.0 在编译阶段还包含了对Slot 的编译优化、事件侦听函数的缓存优化,并且在运行时重写了diff算法

4.语法API优化:Composition API

1. 优化逻辑组织

  在Vue.js 1.x2.x版本中,编些组件本质就是在编写一个‘包含了描述组件选项的对象’,称为Options API

Option API
  • Option API的设计是按照methods,computed,data,props这些不同的选项分类
  • 当组件小的时候,这些分类方式一目了然;但是在大型组件中,一个组件可能有多个逻辑关注点,当使用Options API的时候,每个关注点都有自己的Options,如果要修改一个逻辑关注点,就需要在单个文件中不断上下切换和寻找

  在Vue.js 3.0中,提供了一中新的API:Composition API,就是将某个关注点相关的代码全都放在一个函数里面,这样当需要修改一个功能时,就不再需要在文件中跳来跳去

2.优化逻辑复用

  当开发项目变得复杂的时候,免不了需要抽象出一些复用的逻辑。
  在Vue.js 2.x中,我们通常会用 mixins 去复用逻辑,举一个鼠标位置侦听的例子,我们会编写如下函数 mousePositionMixin:

const mousePositionMixin = {
  data() {
    return {
      x: 0,
      y: 0
    }
  },
  mounted() {
    window.addEventListener('mousemove', this.update)
  },
  destroyed() {
    window.removeEventListener('mousemove', this.update)
  },
  methods: {
    update(e) {
      this.x = e.pageX
      this.y = e.pageY
    }
  }
}
export default mousePositionMixin

然后在组件中使用:

<template>
  <div>
    Mouse position: x {{ x }} / y {{ y }}
  </div>
</template>
<script>
import mousePositionMixin from './mouse'
export default {
  mixins: [mousePositionMixin]
}
</script>

  • 首先,每个mixin都可以定义自己的propsdata,它们之间是无感的,所以很容易定义相同的变量,导致命名冲突
  • 对组件而言,如果模板中使用不在当前组件中的变量,那么就不会太容易知道这些变量在哪里定义的,这就是数据来源不清晰

  但是Vue.js 3.0设计的Composition API,就很好地帮助我们解决了mixins的这两个问题。

import { ref, onMounted, onUnmounted } from 'vue'
export default function useMousePosition() {
  const x = ref(0)
  const y = ref(0)
  const update = e => {
    x.value = e.pageX
    y.value = e.pageY
  }
  onMounted(() => {
    window.addEventListener('mousemove', update)
  })
  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })
  return { x, y }
}

这里我们约定useMousePosition这个函数为hook 函数,然后在组件中使用:

<template>
  <div>
    Mouse position: x {{ x }} / y {{ y }}
  </div>
</template>
<script>
  import useMousePosition from './mouse'
  export default {
    setup() {
      const { x, y } = useMousePosition()
      return { x, y }
    }
  }
</script>

  • 除了在逻辑方面有优势,也会有更好的类型支持
  • 因为它们都是一些函数,在调用函数时,自然有的类型就被推导出来了,不像Options API所有的东西都是使用this
  • Composition API对tree-shaking友好,代码也更容易压缩

Composition API属于API的增强,它并不是Vue.js 3.0 组件开发的范式,如果组件足够简单,还是可以使用Options API

5. 引入RFC:使每个版本改动可控

  RFC(Request For Comments),旨在为新功能进入框架提供一个一致且受控的路径

Vue.js 3.0中大规模启用RFC(Request For Comments),了解每一个feature采用或被废弃的原因

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值