【开发笔记】那些 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);
});
});
}