技術分享!大前端備戰(zhàn)2021年,使用vite構建React !
寫在開頭
由于
vite
這個構建工具被用在了vue3上門,而且它的構建思路我覺得優(yōu)于webpack
,底層也是使用了esbuild
,性能上更優(yōu)那么為了照顧一些小伙伴之前沒有學習過
vite
的,我們先來看看什么是vite
什么是vite
Vite,一個基于瀏覽器原生 ES imports 的開發(fā)服務器。利用瀏覽器去解析 imports,在服務器端按需編譯返回,完全跳過了打包這個概念,服務器隨起隨用,支持熱更新,而且熱更新的速度不會隨著模塊增多而變慢。針對生產(chǎn)環(huán)境則可以把同一份代碼用 rollup 打包
vite
的天然優(yōu)勢:快速冷啟動服務器
即時熱模塊更換(HMR)
真正的按需編譯
vite工作原理
當聲明一個 script 標簽類型為 module 時

瀏覽器就會像服務器發(fā)起一個GET?
http://localhost:3000/src/main.js請求main.js文件:

瀏覽器請求到了main.js文件,檢測到內(nèi)部含有import引入的包,又會對其內(nèi)部的 import 引用發(fā)起 HTTP 請求獲取模塊的內(nèi)容文件
如:
GET http://localhost:3000/@modules/vue.js
如:
GET http://localhost:3000/src/App.vue
其Vite 的主要功能就是通過劫持瀏覽器的這些請求,并在后端進行相應的處理將項目中使用的文件通過簡單的分解與整合,然后再返回給瀏覽器渲染頁面,vite整個過程中沒有對文件進行打包編譯,所以其運行速度比原始的webpack開發(fā)編譯速度快出許多
簡單實現(xiàn)vite
由于代碼量有一些大,我就不自己去寫了,直接拿了別人的代碼過來,原文地址是:

首先是koa啟動監(jiān)聽端口,用于訪問熱更新服務

這里
readBody
其實就是一個讀取文件流的方法,封裝過而已,看成普通的讀取流方法即可
koa中間件處理
首先處理重寫路徑,因為瀏覽器只有絕對路徑和相對路徑

重寫完了路徑后,需要攔截.vue文件和帶@module(重寫路徑之前就是node_modules里面的文件)
// 2. 攔截含有/@modules/vue的請求, 去node_modules引入對應的模塊并返回
({ app, root }) => {
const reg = /^\/@modules\//
app.use(async (ctx, next) => {
// 如果沒有匹配到 /@modules/vue 就往下執(zhí)行即可
if (!reg.test(ctx.path)) {
return next();
}
const id = ctx.path.replace(reg, '');
let mapping = {
vue: path.resolve(root, 'node_modules', '@vue/runtime-dom/dist/runtime-dom.esm-browser.js'),
}
const content = await fs.readFile(mapping[id], 'utf8');
ctx.type = 'js'; // 返回的文件是js
ctx.body = content;
})
},
當解析處理完路徑后,我們需要解析vue的模板文件,(如果是react的jsx代碼,同理)
// 3. 解析.vue文件
({ app, root }) => {
app.use(async (ctx, next) => {
if (!ctx.path.endsWith('.vue')) {
return next();
}
const filePath = path.join(root, ctx.path);
const content = await fs.readFile(filePath, 'utf8');
// 引入.vue文件解析模板
const { compileTemplate, parse } = require(path.resolve(root, 'node_modules', '@vue/compiler-sfc/dist/compiler-sfc.cjs'))
let { descriptor } = parse(content);
if (!ctx.query.type) {
//App.vue
let code = ''
if (descriptor.script) {
let content = descriptor.script.content;
code += content.replace(/((?:^|\n|;)\s*)export default/, '$1const __script=');
}
if (descriptor.template) {
const requestPath = ctx.path + `?type=template`;
code += `\nimport { render as __render } from "${requestPath}"`;
code += `\n__script.render = __render`
}
code += `\nexport default __script`
ctx.type = 'js';
ctx.body = code
}
if (ctx.query.type == 'template') {
ctx.type = 'js';
let content = descriptor.template.content
const { code } = compileTemplate({ source: content }); // 將app.vue中的模板 轉換成render函數(shù)
ctx.body = code;
}
})
},
// 4. 靜態(tài)服務插件 實現(xiàn)可以返回文件的功能
({ app, root }) => {
app.use(static(root))
app.use(static(path.resolve(root, 'public')))
}
]
function createServer() {
let app = new Koa()
const context = {? ? ?// 直接創(chuàng)建一個上下文 來給不同的插件共享功能
app,
root: process.cwd() // C:\Users\...\my-vite-vue3
}
// 運行中間件
resolvePlugin.forEach(plugin => plugin(context))
return app
}
下面是兩個工具函數(shù):一個是流的讀取,一個是重寫路徑的函數(shù)
//讀取body方法
async function readBody(stream) {
if (stream instanceof Readable) {
return new Promise((resolve) => {
let res = ''
stream.on('data', function (chunk) {
res += chunk
});
stream.on('end', function () {
resolve(res)
})
})
} else {
return stream;
}
}
重寫路徑中間件
const resolvePlugin = [
// 1. 重寫引入模塊路徑前面加上/@modules/vue, 重寫后瀏覽器會再次發(fā)送請求
({ app, root }) => {
function rewriteImports(source) {
let imports = parse(source)[0];
let ms = new MagicString(source);
if (imports.length > 0) {
for (let i = 0; i < imports.length; i++) {
let { s, e } = imports[i];
let id = source.slice(s, e); // 應用的標識 vue? ./App.vue
// 不是./ 或者 /
if (/^[^\/\.]/.test(id)) {
id = `/@modules/${id}`;
ms.overwrite(s, e, id)
}
}
}
return ms.toString();
}
這樣一個簡單的vite就完成了
開始在react中使用
vite算是一個新的技術,而且在國內(nèi)目前不夠流行,為了避免踩坑,我們直接采用官方推薦的模板生成
npm init vite-app --template react
生成模板完成后,執(zhí)行命令啟動項目
yarn
yarn dev
這樣一個react的項目就搭建好了,默認使用的是17.0.0版本的react,這樣createElement方法再也不用從react里面導出了,我想這樣jsx風格代碼也會更容易被遷移到其他框架項目中
? "dependencies": {
? ? "react": "^17.0.0",
? ? "react-dom": "^17.0.0"
? },
? "devDependencies": {
? ? "vite": "^1.0.0-rc.13",
? ? "vite-plugin-react": "^4.0.0"
? }
這個模板生成的是自帶熱更新的,相對比較簡單,如果是有特殊需求,可以使用更多的plugin,在vite.config.js中設置
默認的配置
// @ts-check
import reactPlugin from 'vite-plugin-react'
/**
?* @type { import('vite').UserConfig }
?*/
const config = {
? jsx: 'react',
? plugins: [reactPlugin]
}
export default config
寫在最后
本文更多是在講vite的實現(xiàn)原理,目前我還沒有把它使用在生產(chǎn)環(huán)境中
在我看來,vite如果生態(tài)能發(fā)展起來,可能我們就用不到wepback6這個版本了(當然未來不可猜測)
了解更多,請點擊:https://www.bilibili.com/video/BV13b411g78D
作者:Peter譚老師
鏈接:https://juejin.cn/post/6910962438376521742
來源:掘金
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。