课程来源:慕课1245,笔记
1.vue3简介
1.1 课程简介
Compiler原理介绍,了解Vue3带来的性能提升,开发架构上的变化和打包编译原理。在课程实战环节中,介绍了最新的API用法、遗弃的API和升级指南。Vite作为Vue3开发环境,可以实现动态加载,冷启动 + 编译,方便开发的同时,使用rollup大幅降低了配置流程,结合Vue3的composition API,更加方便了开发。介绍了这么多,快来试试吧!
1.2 vue3的优势:

1.3 vue3.0带来的变化:
- 按需加载,VDom/reactive 算法,e.g. v-model/Transition
- 组合API
- TS 支持,
- 新增 Fragment,不受根节点限制,渲染函数可以接受array
- 新增Teleport,类似portal,随用随取,e.g. dialog、action
- 新增Suspense,嵌套的异步依赖,async setup
- 性能提升 1.3~2.X
1.4 vue-next-template-explorer 尝鲜:
示例:

可以看到模板中有四个div,也就是四个根节点,在vue2中,一个模板里面只能有一个根节点。编译器使用 createVNode 创建了虚拟节点,并启用 hoistStatic 能力将静态节点和动态节点区分,对静态内容,不再做更新处理。

上面这个示例,不启用 cacheHandlers 之前是将事件绑定在当前上下文,启用了 cacheHandlers 的能力,会将事件进行全局注册。在定义组件的场景下,启用 cacheHandlers 就不会对重复创建的组件上的事件多次实例化。

ssr 模式下,抽离了静态节点,转化为string字符串。
根据上面几个实例,可以看到 compiler 优化的几点:
- 静态节点不在做更新处理(hoistStatic -> SSR)
- 静态绑定的class id等属性不做更新处理
- 结合打包hint,进行更新分析(动态绑定)
- 事件监听器 Cache 缓存处理(cacheHandlers)
- hoistStatic自动针对多静态节点进行优化,输出字符串
- 按需加载,当模板为空时,输出为null
总结起来就是:
- virtual dom机制调整
- 内存优化
- 按需加载,更灵活的组件化
2.vite
vite是一个http服务器,特殊的地方:
- 可以在单文件中写es6的语法
- 支持热更新,请求的内容才会被打包更新
- rollup打包,按需编译
快速启动(冷启动,vite的文件放在内存里,加载更快(需要webpack去打包文件,然后去浏览器侧请求)。
vite尝试步骤:
- 在命令行: nrm use npm
- 在命令行:npm install -g vite
- 在命令行:mkdir vite-demo
- 在命令行:cd vite-demo
- 在命令行: npm init -y
- 新建 APP.vue main.js
- 执行:vite 执行
最近找到一篇文章:vite对浏览器的请求做了什么(https://juejin.cn/post/7033713960784248868)
作者用很简单的方式讲解了vite编译的原理,自创了一个简易版的demo,很好理解,讲解了怎么实现vite的本地server、怎么编译sfc、怎么解决文件导入路径问题、怎么处理type module,当然前提是要了解vue 以及 vue compiler。
文章里 client 的 demo 实现如下:
const Koa = require('koa')
const app = new Koa()
const fs = require('fs')
const path = require('path')
const compilerSfc = require('@vue/compiler-sfc')
const compilerDom = require('@vue/compiler-dom')
// 把能读出来的文件地址变成相对地址
// 正则替换 重写导入 变成相对地址
// import { createApp } from 'vue' => import { createApp } from '/@modules/vue'
function rewriteImport (content) {
return content.replace(/ from ['|"](.*)['|"]/g, function (s0, s1) {
// s0匹配字符串,s1分组内容
// 是否是相对路径
if (s1.startsWith('./') || s1.startsWith('/') || s1.startsWith('../')) {
// 直接返回
return s0
} else {
return ` from '/@modules/${s1}'`
}
})
}
app.use(async (ctx) => {
const { url, query } = ctx.request;
// 处理请求资源代码都写这
if (url === '/') {
const p = path.join(__dirname, './index.html') // 绝对路径
// 首页
ctx.type = 'text/html'
ctx.body = fs.readFileSync(p, 'utf8')
} else if (url.endsWith('.js')) {
// 响应js请求
const p = path.join(__dirname, url)
ctx.type = 'text/javascript'
ctx.body = rewriteImport(fs.readFileSync(p, 'utf8')) // 处理依赖函数
} else if (url.indexOf('.vue') > -1) {
// 处理vue文件 App.vue?vue&type=style&index=0&lang.css
// 读取vue内容
const p = path.join(__dirname, url.split('?')[0])
// compilerSfc解析sfc 获得ast
const ret = compilerSfc.parse(fs.readFileSync(p, 'utf8'))
// App.vue?type=template
// 如果请求没有query.type 说明是sfc
if (!query.type) {
// 处理内部的script
const scriptContent = ret.descriptor.script.content
// 将默认导出配置对象转为常量
const script = scriptContent.replace(
'export default ',
'const __script = ',
)
ctx.type = 'text/javascript'
ctx.body = `
${rewriteImport(script)}
// template解析转换为单独请求一个资源
import {render as __render} from '${url}?type=template'
__script.render = __render
export default __script`
} else if (query.type === 'template') {
const tpl = ret.descriptor.template.content
// 编译包含render模块
const render = compilerDom.compile(tpl, { mode: 'module' }).code
ctx.type = 'text/javascript'
ctx.body = rewriteImport(render)
} else if (url.endsWith('.png')) {
ctx.body = fs.readFileSync('src' + url)
}
}
})
app.listen(3001, () => {
console.log('dyVite start!!')
})

浙公网安备 33010602011771号