一、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 的不同构建版本。
一共分为四类:
- 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
- 可以更加灵活的组织组件的逻辑。
- 同一个功能的代码不需要拆分,有利于对代码的提取和重用
- 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,让首次渲染和更新的性能有了大幅提升)
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的快就是使用浏览器支持的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脚本