【開發筆記】那些 Webpack 過渡到 Vite 的坑
用 JavaScript 開發 Web 應用的朋友,一定多少聽過 Webpack。雖然不完美,但是長年下來,至少在 Vue 2 框架以前 Webpack 一直是封裝 Web 應用的主流選擇之一。直到近年 Vite 發表且開始逐漸受到重視,Webpack 過去令人詬病的體積龐大,編譯緩慢等等問題才慢慢獲得解決。但是...
Vite 是以 Native ES Modules 方式載入程式碼
不同於 Webpack 將需要用到的資源以打包的方式整個轉譯和封裝起來,Vite 在熱更新時,僅在程式碼的部分以 Native ES Modules 方式載入,避開 bundle 和 translation 耗費時間的過程,讓整個開發修改迭代的過程更加敏捷,更有效率。
新的機制帶來新的坑
不過新的機制雖然改善了就有的問題,但是也帶來新的毛病。例如在筆者近期的專案上,就出現了 AWS SDK v2 在 Vue 3 + Vite 項目配置上出現了生產環境編譯成功,但是無法執行的問題。直到 AWS SDK 更新到 v3 改寫了 S3 相關的程式碼才獲得解決。過程雖然波折,但是成果是值得的。整個 Web 應用在最後編譯輸出的大小節約方面,獲得了巨大的改善。
不過新的 Upload() 在處理上傳進度的部分,因為最小單位是 5mb 起跳,因此小檔案上傳時,prgress 更新不如先前 v2 使用 putObject 來得積極,算是個小缺點。
import { Buffer } from "buffer";
import { XhrHttpHandler } from "@aws-sdk/xhr-http-handler";
import { S3 } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
(file, Key) => {
return new Promise(async (resolve, reject) => {
const reader = new FileReader()
const blob = await fetch(file.objectURL).then((r) => r.blob()); //blob:url
reader.readAsDataURL(blob);
reader.onerror = function (error) {
reject(error)
}
reader.onload = async function (loadEvent) {
const base64 = reader.result.replace(`data:${file.type};base64,`, '')
const params = {
ACL: 'public-read',
Key: encodeURI(Key),
Body: Buffer.from(base64, 'base64'),
ContentType: file.type,
Bucket: bucket
}
try {
const upload = new Upload({
client: s3,
params: params,
});
upload.on("httpUploadProgress", (progressEvent) => {
const value = parseInt(Math.round((progressEvent.loaded * 100) / progressEvent.total));
window.dispatchEvent(new CustomEvent(import.meta.env.VITE_DEFAULT_NOTIFICATION_CUSTOM_HTTPUPLOADPROGRESS, {
detail: {
progress: value,
loaded: progressEvent.loaded,
total: progressEvent.total
}
}));
});
upload.done().then((response) => {
resolve(response);
});
} catch (error) {
console.log(error);
reject(error);
}
}
})
}
child_process 在 Vite 內也無法直接封裝到項目中引用
另外,就是 child_process 的部分。雖然網路上有些朋友分享了編輯 vite.config.js 讓 child_process 可以直接在項目中引用的方式,但是筆者一直沒有嘗試成功。後來是將 child_process 分離到後端中,以接口調用的方式來解決。
表面上似乎麻煩了一點,但是在整個項目架構上,因為 child_process 是用在 shell command 與 JavaScript 不同的異質性執行,或許分離到後端中是更加合理以及彈性,容易維護的選擇。
以下是 nodejs 後端參考的代碼例子。
const iconv = require("iconv-lite");
iconv.skipDecodeWarning = true;
const child_process = require("child_process");
function handleShellCommand(cmd) {
let result = {};
return new Promise((resolve, reject) => {
child_process.exec(cmd, { encoding: "binary" }, (err, stdout, stderr) => {
let data = iconv.decode(stdout, "utf-8");
if (err) {
result.errCode = 500;
result.data = {
data: data,
code: result.errCode,
message: `錯誤:指令’${str.split(" ")["0"]}’無法執行`
};
reject(result);
return;
}
result.errCode = 200;
result.data = {
data: data,
code: result.errCode,
message: "成功"
};
resolve(result);
});
});
}