一、Vue3.0 源码组织方式

  • 源码组织方式的变化
    源码全部用TS重写。使用monorepo的方式来组织项目的结构,把独立的功能模块都提取到不同的包中
  • Composition API
    虽然代码重写,但是90%以上的代码兼容2.x。根据社区的反馈增加了组合API,用来解决vue2.x在开发大型项目时遇到超大组件使用options API不好拆分和重用的问题
  • 性能提升
    用proxy也就是代理对象重写了响应式的代码,并且对编译器做了优化,重写了虚拟DOM,从而让渲染和update的性能都有了大幅度的提升,另外官方介绍 服务端渲染的性能也提升了2-3倍
  • Vite
    随着 Vue3.0 的发布,官方也发布了一个开发工具:Vite,一个基于ES import的开发服务器。使用Vite在开发阶段测试项目的时候,不需要打包可以直接运行项目,服务随启随用,提高了开发的效率。

1、源码组织方式

  • 源码采用TypeScript重写
    着重类型检查、代码维护
  • 使用Monorepo管理项目结构
    使用一个项目来管理多个包,把不同功能代码放到不同package中管理。每个功能模块可以单独发布、单独测试、单独使用
    2、packages目录结构
    packages 目录下的包都是可独立发行、可独立使用的包。
  • compiler 开头的包都是和编译相关的代码。
  • runtime 开头的包都是运行时相关的代码。
    └── packages ············································ packages 目录
  • ├─ compiler-core ····································· 和平台无关的编译器
    ├─ compiler-dom ······································ 浏览器平台下的编译器,依赖于compiler-core
    ├─ compiler-sfc ······································ sfc是单文件组件的意思,用于编译单文件组件,依赖于compiler-core、compiler-dom
    ├─ compiler-ssr ······································ 服务端渲染的编译器,依赖于compiler-dom
    ├─ reactivity ········································ 数据响应式系统,它可以独立使用
    ├─ runtime-core ······································ 和平台无关的运行时
    ├─ runtime-dom ······································· 针对浏览器的运行时,它处理原生dom api、以及事件等等
    ├─ runtime-test ······································ 一个专门为测试而编写的轻量级运行时,由于这个运行时渲染出来的dom树其实是一个js对象,所以这个运行时可以运行在所有的js环境里。可以用来测试渲染是否正确,它还可以用于序列化dom、触发dom事件,以及记录某次更新中的dom操作
    ├─ server-renderer ··································· 用于服务端渲染
    ├─ shared ············································ vue内部使用的一些公共api
    ├─ size-check ········································ 是一个私有的包,不会发布到npm,作用是,tree shaking之后检查包的大小
    ├─ template-explorer ································· 在浏览器运行的时时编译组件,它会输出render函数,这个包readme中提供了线上访问地址
    ├─ vue ··············································· 用来构建完整版本的vue,依赖于compiler和runtime

二、不同的构建版本

Vue3.0 中不在构建 UMD 模块化的方式,因为 UMD 会让代码有更多的冗余,它要支持多种模块化的方式。Vue3.0构建版本中把 CJS、ESModule 和自执行函数分别打包到不同的文件中。

在 packages/vue 的dist目录中存放了 Vue3.0 的不同构建版本。

vite vue3架构图 vue3.0 vite_Vue

一共分为四类:

  • cjs(这里两个文件都是完整版本的vue,包含了运行时和编译器)
  • vue.cjs.js(开发版本,代码没有被压缩)
  • Vue.cjs.prod.js(生产版本,代码被压缩了)
  • global(这四个文件都可以在浏览器中通过 script 标签中导入,导入后会添加一个全局的 vue 对象)
  • vue.global.js(完整版的 vue,包含编译器和运行时,是开发版本,代码没有压缩)
  • vue.global.prod.js(完整版的 vue,包含编译器和运行时,是生产版本,代码被压缩了)
  • vue.runtime.global.js(只包含了运行时的构建版本,是开发版本,代码没有压缩)
  • vue.runtime.global.prod.js(只包含了运行时的构建版本,是生产版本,代码被压缩了)
  • browser(这四个文件都使用 ESModule 的方式,浏览器的原生模块化方式,在浏览器中可以直接通过三、Composition API设计动机
  • RFC (Request For Comments)
  • https://github.com/vuejs/rfcs。 这是vue官方的rfc仓库
  • Composition API RFC
  • 文档地址:https://composition-api.vuejs.org/zh/
    学习Composition API最好方式,就是查阅官方的RFC,RFC的全称是Request For Comments,Vue2 升级到 Vue3.0 大的变动都是通过 RFC 进行确认的。这个 RFC 就是官网结合 vue2.x 中的问题以及其周围生态的一些问题(例如:vue-router、vuex 等等),给出一些提案,然后收集社区的反馈并且讨论最终确认

设计动机

  • Options API
  • Vue2.x 使用的是 Options API。Options API 包含了一个描述组件选项(data、methods、props 等)的对象。
  • Options API 开发复杂组件,同一个功能逻辑的代码被拆分到不同选项
  • 当组件的功能比较复杂的时候,我们可能需要不停的拖动滚动条来找到我们需要的代码,而且不方便提取重用的代码。
  • Composition API
  • 它是 vue3.0 中新增的一组 API
  • 一组基于函数的 API
  • 可以更加灵活的组织组件的逻辑。
  • 同一个功能的代码不需要拆分,有利于对代码的提取和重用

vite vue3架构图 vue3.0 vite_响应式_02

  • Composition API 对比与 Options API 的好处
    在开发一些复杂组件的时候,里面有很多非常复杂的逻辑或者有很多交互等等之类的,看代码的时候,就要滚动条拖来拖去,有时候业务可能会很跳跃,这时候就会有点混乱,而且复用率很低。虽然可以使用 mixins 可以实现复用的问题,但是使用 mixins 也会有一些个问题,第一个:命名的问题,可能会与当前组件命名重复。第二个:数据来源不明确。

如果用 Composition API 的话,就可以把公共的函数封装在一起,在需要用的地方导出,然后在组件中 setup 进去。

需要注意的是:在 vue3 中,以上两种 API 都可以使用。

四、性能提升
vue3 中的性能提升可以从以下的几个方面来说明
1、响应式系统的升级(proxy对象)

  • Vue2.x 中的响应式核心使用的是 defineProperty,
  • 在组件 初始化 的时候,需要遍历data中所有的成员,通过defineProperty把对象的属性转换成 getter 和 setter,如果属性是 Object 类型,需要递归处理每一个子对象的属性。
  • 而 Vue3 中使用 es6 中的 Proxy 对象重写响应式系统。提升了响应式系统的性能和功能
  • Proxy 的性能会 defineProperty 快很多,另外代理对象可以拦截属性的访问、赋值、删除等操作,不需要初始化的时候遍历所有的属性,另外当某个属性是 Object 类型的时候,只有访问到某个属性的时候,才会递归处理下一级的属性。
  • 使用 Proxy 的好处:
  • 可监听动态新增的属性。vue2中想要动态添加一个响应式的属性需要调用vue.set的方法处理。
  • 可以监听删除的属性。vue2中监听不到属性的删除
  • 可以监听数组的索引和 length 属性。vue2中监听不到数组的索引和 length 属性的修改操作

2、编译优化(通过优化编译的过程和重写虚拟dom,让首次渲染和更新的性能有了大幅提升)

vite vue3架构图 vue3.0 vite_vite vue3架构图_03


vue2中模板首先需要编译成render函数,这个过程一般是在构建的过程中完成的。

在编译的时候会编译静态根节点和静态节点。静态根节点要求节点中必须有一个静态子节点。

当组件的状态发生变化后,会通知watcher,触发watcher的update,最终去执行虚拟dom的patch操作,

遍历所有的虚拟节点,找到差异,然后更新到真实dom上

diff 的过程中会去比较整个虚拟dom,先对比新旧的div以及它的属性,然后再对比它内部的子节点。

vue2中渲染的最小单位是组件

编译优化:

  • Vue.js 2.x 中通过标记静态根节点,优化 diff 的过程
  • vue2中diff的过程会跳过静态根节点,因为静态根节点的内容不会发生变化
  • 但是vue2中,静态节点还需要再进行diff,这个过程没有被优化。
  • Vue.js 3.0 中为了提高性能,在编译的时候会标记和提升所有的静态节点,通过patch flag将来在diff 的时候会跳过静态根节点只需要对比更新动态节点内容
  • Fragments (升级 vetur 插件):
    片段特性,组件模板中不在需要创建一个唯一的根节点,这一点和 react 很像啊。模板里面可以直接放文本内容,或者多个同级标签。注意:vscode中需要升级 vetur 插件,不然它还是会报红的。
  • 静态提升(hoistStatic)静态节点(标签里内容是纯文本的内容)提升。这些静态节点只有在初始化时被调用一次
  • Patch flag
  • 缓存事件处理函数(cacheHandlers)减少了不必要的更新操作

虚拟dom中diff的过程是最耗时的,在vue2中重新渲染的时候需要重新创建新旧vnode,diff的时候会跳过静态根节点,对比剩下的每一个新旧vnode,哪怕这个节点什么时候都没做。
vue3中通过标记和提升静态节点,以及patch flag标记动态节点来大大提升了diff的性能

3、源码体积优化(通过优化源码的体积和更好的 TreeShaking 的支持,减少大打包的体积)

  • Vue.js 3.0 中移除了一些不常用的 API:inline-template、filter(可以通过method或者计算属性来实现) 等
  • Tree-shaking
    例如:Vue3 中的没用到的模块不会被打包,但是核心模块会打包。内置的组件Keep-Alive、transition和一些内置的指令v-model 等都是按需引入的。

五、vite 法语 快 的意思

vite vue3架构图 vue3.0 vite_vite vue3架构图_04


Vite的快就是使用浏览器支持的ESModule的方式,避免开发环境下打包,从而提升开发速度

Vite,一个基于ES import的开发服务器。使用Vite在开发阶段测试项目的时候,不需要打包可以直接运行项目,服务随启随用,提高了开发的效率
1、Vite as Vue-CLI

  • Vite 在开发模式下不需要打包可以直接运行
  • Vue-CLI开发模式下必须对项目打包才可以运行
    2、Vite 的特点
  • 快速冷启动
  • 按需编译
  • 模块热更新
    Vite 在生产环境下使用 Rollup 打包
  • 基于 ES Module 的方式打包 不需要使用babel把import转换成require以及一些相应的辅助函数。因此打包体积更小。
    Vue-Cli 使用 Webpack 打包
    3、Vite 创建vue3项目
    npm init vite-app
    cd
    npm install
    npm run dev

基于模板创建项目
npm init vite-app --template react
npm init vite-app --template preact

vite开启的web服务器,它会劫持.vue的请求,首先会把.vue文件解析成js文件,并且把响应头中的content-type设置成application-javascript,目的告诉浏览器我现在给你发送的是一个js脚本