内容输出来源:拉勾教育大前端高薪训练营
一、Vue.js源码组织方式
1.源码采用TypeScript重写
提高了代码的可维护性。大型项目的开发都推荐使用类型化的语言,在编码的过程中检查类型的问题。
2.使用Monorepo管理项目结构
使用一个项目管理多个包,把不同功能的代码放到不同的package中管理,每个功能模块都可以单独发布,单独测试,单独使用。
packages目录结构:
└── packages/
├── compiler-core # 和平台无关的编译器
├── compiler-dom # 浏览器平台下的编译器,依赖于compiler-core
├── compiler-sfc # 单文件组件编译器,依赖于compiler-core和compiler-dom
├── compiler-ssr # 服务端渲染的编译器,依赖于compiler-dom
├── reactivity # 数据响应式系统
├── runtime-core # 和平台无关的运行时
├── runtime-dom # 针对浏览器的运行时,处理原生dom的api、事件等
├── runtime-test # 专门为测试而编写的轻量级运行时,渲染出来的dom树其实是一个js对象,可以运行在所有支持js的环境里
├── server-renderer # 用于服务端渲染
├── shared # 内部使用的公共的api
├── size-check # 私有不会发布到npm,作用是在tree shaking之后检查包的大小
├── template-explorer # 在浏览器运行的实时编译组件会输出render函数
└── vue # 构建完整版的vue依赖于compiler和runtime
└── global.d.ts
3.不同构建版本
Vue3中不再构建UMD模块化的方式,因为UMD会让代码有更多冗余,它要支持多种模块化的方式。Vue3中将CJS、ESModule和自执行函数的方式分别打包到了不同的文件中。在packages/vue/dist中有Vue3的不同构建版本。
- cjs(两个版本都是完整版本,包含编译器)
- vue.cjs.js
- vue.cjs.prod.js(生产版本,代码进行了压缩)
- global(这四个版本都可以在浏览器中直接通过scripts标签导入,导入之后会增加一个全局的Vue对象)
- vue.global.js(完整版,包含编译器和运行时)
- vue.global.prod.js(完整版,包含编译器和运行时,生产版本,代码进行了压缩)
- vue.runtime.global.js(只包含运行时)
- vue.runtime.global.prod.js(只包含运行时,生产版本,代码进行了压缩)
- browser(四个版本都包含esm,浏览器的原生模块化方式,可以直接通过
<script type="module" />的方式导入模块)- vue.esm-browser.js
- vue.esm-browser.prod.js
- vue.runtime.esm-browser.js
- vue.runtime.esm-browser.prod.js
- bundler(这两个版本没有打包所有代码,只会打包使用的代码,需要配合打包工具来使用,会让Vue体积更小)
- vue.esm-bundle.js
- vue.runtime.esm-bundler.js
二、Composition API
1.学习Composition API的资源:
- RFC(Request For Comments)
- Composition API RFC
2.设计动机
- Options API
- 包含一个描述组件选项(data、methods、props等)的对象
- Options API 开发复杂组件,同一个功能逻辑的代码被拆分到不同选项
- Composition API
- Vue.js 3.0新增的一组API
- 一组基于函数的API
- 可以更灵活的组织组件的逻辑
3.Options API vs Compostion API
- Options ApI 一个功能需要在不同Vue配置项中定义属性和方法,比价分散,不利于维护
- Composition API 代码是根据逻辑功能来组织的,一个功能所定义的所有api会放在一起(更加的高内聚、低耦合),提高可读性和可维护性。而且基于函数组合的API可以更好的重用逻辑代码,而Vue 2.X Options API中需要通过Mixins重用逻辑代码,更容易发生命名冲突且关系不清。
三、性能提升
1. 响应式系统升级
- Vue.js 2.x中响应式系统的核心是defineProperty
在初始化的时候会遍历data中的所有成员,通过defineProperty把成员的所有属性转换成getter、setter,如果data中的属性又是对象,需要递归处理每一个子对象的属性。在初始化时进行,即使没有使用这个属性也会进行响应式处理; - Vue.js 3.0中使用Proxy对象重写响应式系统
使用ES6新增的Proxy对象,Proxy对象的性能本来就比defineProperty要好,另外代理对象可以拦截对象的访问、赋值、删除等操作,不需要初始化的时候遍历所有的属性,另外如果有多层属性的嵌套的话,只有在访问某个属性的时候才会递归处理下一级的属性,使用Proxy对象默认就可以监听到动态添加的属性,而Vue.js 2.x中想要动态添加响应式的属性,需要调用Vue.set方法,而且Vue.js 2.x中还监听不到属性的删除,对数组的索引和length属性的修改也监听不到,Vue.js 3.x使用代理对象可以监听到属性的删除以及数组的索引和length属性的修改操作,所以提升了响应式系统的性能和功能 - 可以监听动态新增的属性
- 可以监听删除的属性
- 可以监听数组的索引和length属性
2. 编译优化
通过优化编译的过程和重写虚拟dom让首次渲染和更新的性能有了大幅度的提升
- Vue.js 2.x中通过标记静态根节点,优化diff过程
- Vue.js 3.0中标记和提升所有的根节点,diff的时候只需要对比动态节点内容
- 引入Fragments(模板中不需要再创建一个根节点,模板里面可以直接放文本内容或者同级的标签,vscode需要升级vetur插件,否则模板中没有唯一的根节点,vscode依然会提示错误。)
- 静态节点提升
- Patch flag
- 缓存事件处理函数,减少不必要的更新操作
2.0的时候模板首先要编译成render函数,这个过程是在构建的时候进行的,编译时会编译静态根节点和静态节点,静态根节点在编译的时候要求节点必须有一个静态子节点,当组件发生变化的时候回通知watcher触发watcher的update,最终执行虚拟dom的patch操作,遍历所有的虚拟节点,找到差异,然后更新到真实的dom上,diff的过程中会比较整个虚拟dom,先比较新旧根节点以及它的属性,然后再对比内部的子节点,2.0中渲染的最小的单位是组件,2.0diff的过程会跳过根节点,因为静态根节点的内容不会发生变化,也就是2.0通过标记静态根节点优化了diff的过程,但是2.0的静态节点还需要进行diff这个过程没有被优化,3.0为了提高性能,在编译的过程会标记和提升所有的静态节点,diff的时候只对比动态节点的内容,3.0新引入Fragments,模板中不需要再创建一个根节点,模板里面可以直接放文本内容或者同级的标签,vscode需要升级vetur插件,否则模板中没有唯一的根节点,vscode依然会提示错误。
3. 源码体积优化
通过优化源码的体积和更好的TreeShaking的支持,减少打包的体积
- Vue.js 3.0中移除了一些不常用的API
- 例如:inline-template、filter等,移除的filter可以通过methods或者计算属性来实现。
- Tree-shaking(依赖ES Module,通过编译阶段的静态分析,找到没有引用的模块,在打包的时候中直接过滤掉以缩小打包体积) 3.0在设计之初考虑到了Tree shaking,内置的组件transtion、keep-alive和内置指令例如:v-model,都是按需引入的,3.0中新增的api如果没有使用的话是不会打包的,但是vue的核心模块都会被打包,在vue.runtime.esm-bundler.js只会打包核心的运行时和响应式系统
四、Vite
Vue.js 3.0的构建工具,vite在法语中是“快”的意思,意味着vite比基于webpack的vue-cli更快。
ES Module
- 现代浏览器都支持ES Module(IE不支持)
- 通过下面的方式加载模块
<script type="module" src="..."></script>
- 支持模块的script默认延迟加载
- 类似于script标签设置defer
- 在文档解析完成后,触发DOMContentLoaded事件前执行
1.Vite vs Vue-cli
- Vite在开发模式下不需要打包可以直接运行
Vite的工作原理: 因为在开发模式下vite使用浏览器原生支持的ES Module通过import来加载模块,在开发环境下不会打包项目,把所有模块的请求交给服务器来出来处理,服务器处理浏览器不能识别的模块,如果是.vue单文件组件,会调用vue中的模块:compiler-sfc编译组件,并把编译的结果返回浏览器
- Vue-CLI开发模式下必须对项目打包才可以运行
- Vite在生产环境下使用Rollup打包
- 基于ES Module的方式打包
- 不需要再使用babel把import转化成require以及一些相应的辅助函数,因此打包的体积比webpack打包的体积更小
- Vue-CLI使用webpack打包
2.Vite的特点
- 快速冷启动
- 按需编译
- 模块热更新
3.Vite创建项目
- 基于Vue.js 3.0创建项目
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev
- 基于模板创建项目
npm init vite-app --template react
npm init vite-app --template preact