Skip to content

Vue.js 3.0做了什么 #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jappp opened this issue Mar 10, 2021 · 0 comments
Open

Vue.js 3.0做了什么 #11

jappp opened this issue Mar 10, 2021 · 0 comments

Comments

@jappp
Copy link
Owner

jappp commented Mar 10, 2021

源码优化

1. 新的代码管理方式 Monorepo

什么是 Monorepo 呢?简单来说,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。

Vue2.x的源码托管于src目录里面,根据功能划分出compiler(模板编译),core(与平台无关的通用代码),platforms(平台专有代码)、server(服务端渲染代码)、sfc(vue单文件解析代码)、shared(共享工具代码)等。

到了 Vue.js 3.0 ,整个源码是通过 monorepo 的方式维护的,根据功能将不同的模块拆分到 packages 目录下面不同的子目录中
image

2. 采用了 TypeScript 开发

对于复杂的框架项目开发,使用类型语言非常有利于代码的维护,因为它可以在编码期间帮你做类型检查,避免一些因类型问题导致的错误。
Vue.js 2.x 使用的类型语言是 Flow,但是 Flow 对于一些复杂场景类型的检查,支持得并不好。所以 Vue.js 3.0 使用 TypeScript 重构了整个项目。

性能优化

1. 源码体积优化
Vue.js 3.0 在源码体积的减少方面做了一些工作:

  • 移除了一些冷门的 feature(比如 filter、inline-template 等)
  • 引入 tree-shaking 的技术,减少打包体积

tree-shaking 就是打包的时候自动移除未引用的模块,去除冗余代码。
利用 tree-shaking 技术,如果你在项目中没有引入 Transition、KeepAlive 等组件,那么它们对应的代码就不会打包,这样也就间接达到了减少项目引入的 Vue.js 包体积的目的。

2. 数据劫持优化

Vue.js 区别于 React 的一大特色是它的数据是响应式的,DOM 是数据的一种映射,数据发生变化后可以自动更新 DOM。
要实现这个功能,必须得劫持数据的访问和更新,Vue.js 1.x 和 Vue.js 2.x都是通过 Object.defineProperty 这个API去劫持数据的getter和setter的

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

它的缺陷主要是:

  1. Object.defineProperty 的缺陷在于必须事先知道要劫持的属性是什么,所以不能动态检测到对象属性添加与删除。虽然Vue提供了 $set和 $delete 实例方法,但是增加了心智负担。
  2. 无法监听数组的变化,Vue内部实现是使用了一些hack,把无法监听数组的情况通过重写数组的部分方法来实现响应式,只是拦截了unshift shift push pop splice sort reverse 这几个方法,直接通过数组下标修改数组是无法监听到的。
  3. 另外 Object.defineProperty 无法判断运行时具体访问哪个属性,所以对于嵌套层级较深的对象,需要递归遍历把该对象的每一层数据都变成响应式的,这无疑会有相当大的性能压力。

为了解决这些缺陷,Vue.js 3.0 使用了ES6新特性 Proxy做数据劫持,它的内部是这样的

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

Proxy 可以劫持整个对象,并返回一个新对象。但要注意的是,Proxy API并不能监听到内部深层次对象的变化,Vue.js 3.0 的处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部对象才会变成响应式,提高了性能。

语法 API 优化:Composition API

Vue.js 3.0 在语法方面进行了优化,主要是提供了 Composition API。

1. 优化逻辑组织
Vue以前的版本中,数据和逻辑被分散在了各个option中,当组件小的时候,这种分类方式一目了然;但是在大型组件中,一个组件可能有多个逻辑关注点,当使用 Options API 的时候,每一个关注点都有自己的 Options,如果需要修改一个逻辑点关注点,就需要在单个文件中不断上下切换和寻找。

Vue.js 3.0 提供了一种新的 API:Composition API,它有一个很好的机制去解决这样的问题,就是将某个逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去
image

  1. 优化逻辑复用
    Vue2.x js中,我们经常会用mixins去复用逻辑,例如鼠标位置监听的逻辑,编写一个mixin
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基本是没有问题的,但是如果一个复杂组件内引用了多个mixin时,经常会造成命名冲突,并且数据源来源不清晰,类似于React的HOC高阶组件的Props。但是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>

可以看到,整个数据来源清晰了,即使去编写更多的 hook 函数,也不会出现命名冲突的问题。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant