记Vue3.0

2,231 阅读5分钟

前言

vue3.0正式版本终于在九月低发布了,本人在实习的过程中主要进行的项目也是基于Vue3.0的Beta版本composition-api开发的,就抱着热情学习了下Vue3.0,并在此做如下总结。

Vue3.0优化

源码优化

源码的优化主要体现在使用 monorepo (更好的代码管理方式)和 TypeScript (有类型的JavaScript语言)管理和开发源码,这样做的目标是提升自身代码可维护性。

性能优化

性能优化主要体现在:源码的体积优化、数据劫持优化、编译优化

  • 源码的体积优化 静态资源的体积减少,网络对包的传输时间也会相应的减少,JavaScript解析包的时间也会减少. Vue3.0主要通过移除一些冷门的feature以及tree-shakiing(通过编译阶段的静态分析,找到没有被引用的代码并做上标记)

举个例子:

src/math.js

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

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

src/index.js

import { cube } from ./math.js

dist/bundle.js

/* 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;
}

build后我们发现没有被import的square被做上了标记,然后的压缩阶段会利用压缩工具删除这些没有被使用的代码.

  • 数据劫持优化

Vue区分与React的一点是它的数据是响应式的,DOM是数据的一种映射,数据发生修改会自动的响应式的影响DOM,把我们的目光集中在数据而不是视图.这就需要通过数据的劫持,在数据发生更新的时候去自动的更新DOM.这就需要在渲染DOM的时候对其进行依赖收集(源码地址)通过watcher进行依赖的管理,参考下图:

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

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

这个API的缺陷是我们要知道需要劫持的数据的key,因此我们不能检测对象属性添加或者删除,vue通过setset和delete实例方法解决了如下问题,但是他还存在这另一个问题,当我们嵌套的层级较深的时候,如果我们要劫持深层次的属性,就需要遍历这个对象,显然这有着很大的性能负担.

Vue.js 3.0 使用了 Proxy API 做数据劫持,它的内部是这样的:

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

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

  • 编译优化

这是vue从new Vue 开始到生成DOM的过程,Vue3.0主要进行的优化在patch阶段,举个例子:

假设我们的代码是这样的:

<template>
  <div id="content">
    <p class="text">static text</p>
    <p class="text">static text</p>
    <p class="text">{{message}}</p>
    <p class="text">static text</p>
    <p class="text">static text</p>
  </div>
</template>

我们可以看到这里面只有一个动态节点,但在Vue2.x的版本进行diff的时候我们仍然遍历了所有节点,这就导致了vNode的性能和我们模版的大小正相关的关系,和我们动态节点的数量无关,当一些组件的整个模版内只有少量动态节点时,这些遍历都是性能的浪费。

Vue.js 3.0 做到了,它通过编译阶段对静态模板的分析,编译生成了 Block tree。借助 Block tree,Vue.js 将 vnode 更新性能由与模版整体大小相关提升为与动态内容的数量相关

语法 API 优化:Composition API

使用composition-api达到了:优化逻辑组织、优化逻辑复用

  • 优化逻辑组织

在vue之前的版本中我们使用Options API去编写组件:主要包括data、computed、methods、props这些选项去分类,当组件很小的时候,可以说是十分的清晰的,但是当我们的组件的代码量十分庞大的时候,我们想要搞清楚一些逻辑就会产生一些“反复横跳”的操作,Vue.js 3.0 提供了一种新的 API:Composition API,它有一个很好的机制去解决这样的问题,就是将某个逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去。

  • 优化逻辑复用

在我们开发的比较复杂的时候,尤其是中后台系统,少不了一些复用的逻辑,在之前的vue版本中我们通常用minxins去复用逻辑:

定义一个混入对象


export const myMinxin = {
    data() {
    		return {
        		name: 'qimukakax'
        	}
    },
    methods: {
    	getToken() {
        	const params = 'qimukakax'
        	getToken(params)
        }
    }
}

将对象混入当前的模版

<template>
	<div>components</div>
</template>

<script>
import { myMinxin } from './minxin.js'

export defaut { 
	minxins: [myMinxin],
    created() {
    	console.log(this.name)//qimukakax
    }
}
</script>

使用单个 mixin 似乎问题不大,但是当我们一个组件混入大量不同的 mixins 的时候,会存在两个非常明显的问题:命名冲突和数据来源不清晰。在composition-api中:

定义一个混入对象

 import {ref} from 'vue'
 export const useMyMinxin = {
  const name = ref('qimukakax')
  const getToken = () => {
      const params = 'qimukakax'
      getToken()
  }
}

将对象混入当前的模版

<template>
	<div>components</div>
</template>

<script>
import useMyMinxin  from './minxin.js'
import { onMounted } from 'vue'

export defaut {
  setup() {
      const { name, getToken } = useMyMinxin()
      onMounted(() => console.log(name)) 
  }
}
</script>

可以看到这样我们的条理变得清晰的一点,Composition API 除了在逻辑复用方面有优势,也会有更好的类型支持,因为它们都是一些函数,在调用函数时,自然所有的类型就被推导出来了,不像 Options API 所有的东西使用 this。另外,Composition API 对 tree-shaking 友好,代码也更容易压缩。

写在后面

(个人学习笔记,待完善)

参考资料

Vue.js 3.0 核心源码解析 黄轶

其中还引用了一些大佬的帖子,都留下链接🔗啦! 大家加油👊!