现代的浏览器都支持es6的import了,执行import XX from './xxx'
(script标签type=”module”)会发起一个网络请求,vite拦截这个请求去做vue去编译、解析。实现按需加载,不用打包,启动时间和更新大大缩短
与vue-cli的区别:
vue-cli基于webpack;vite基于rollup
toy-vite
加载html
第一步首先开启一个代理服务器,使用express和koa都行,这里选择使用koa
npm install koa --save
然后开启监听,加载html
const fs = require('fs')
const path = require('path')
const Koa = require('koa')
const app = new Koa()
app.use(ctx => {
const { request: { url, query } } = ctx
if (url === '/') { // 根目录加载html
let content = fs.readFileSync('./index.html', 'utf-8')
ctx.type = 'text/html'
ctx.body = content
} else if (url.endsWith('.js')) { // 拦截.js请求
const p = path.resolve(__dirname, url.slice(1))
ctx.type = 'application/javascript'
const content = fs.readFileSync(p, 'utf-8')
ctx.body = content
}
})
app.listen(9000, () => {
console.log('listened to 9000');
})
使用nodemon来热部署这个server(需要全局安装nodemon npm install nodemon -g
)
这时访问localhost:9000会出现一个报错,
因为没有./
、../
,引入的包来自node_modules,要将这些值标记为npm包
支持node_modules的import
这时正统vite打包之后的main.js中引入的vue,那这时我们应该就有了些许想法:把import XX from 'xx'替换为import XX from '@modules/xx'
最简单暴力的办法——使用正则替换,定义一个
function rewriteImport(content) {
return content.replace(/ from ['|"]([^'"]+)['|"]/g, function (s0, s1) {
if (s1[0] !== '.' && s1[1] !== '/') {
return ` from "/@modules/${s1}"`
} else {
return s0
}
})
}
将原来的content用rewriteImport方法包裹一下,这时候已经替换成功了,然后再将这些请求从node_modules中查找
再追加一段代码,让其能够在node_modules中进行查找
else if (url.startsWith('/@modules/')) {
const prefix = path.resolve(__dirname, 'node_modules', url.replace('/@modules/', ''))
const module = require(prefix + '/package.json').module
const p = path.resolve(prefix, module)
console.log(p);
}
已经可以成功的拿到了地址
支持.vue文件的import
npm包已经拿到了,现在就要再来处理.vue单文件组件,template模板=>render函数
使用官方库@vue/compiler
进行单文件解析
else if (url.indexOf('.vue') > -1) {
// 解析单文件
const p = path.resolve(__dirname, url.split('?')[0].slice(1))
// 使用官方库解析单文件
const { descriptor } = compilerSfc.parse(fs.readFileSync(p, 'utf-8'))
console.log(descriptor);
}
可以在控制台看到已经获取了template对象
再来看一眼vite解析之后的Vue
再按照这个格式去正则替换
if (!query.type) {
ctx.type = 'application/javascript'
ctx.body = `
${rewriteImport(descriptor.script.content.replace('export default', 'const __script = '))}
import { render as __render } from ${url}?type=template
__script.render = __render
export default __script
`
}
然后再将template变成render函数
else if (query.type == "template") {
const template = descriptor.template
const render = compilerDom.compile(template.content, { mode: "module" }).code
ctx.type = 'application/javascript'
ctx.body = rewriteImport(render)
}
到这里就差不多完成了
效果展示
toy-vite
vite
代码
已上传码云