Vite是一款比较火的构建工具,使用原生 ESM 文件编译,运行效率高,目前我们公司都在向这方面转移,
项目构建打包之后会有2种运行情况:
1.构建出来的安装包只能在android/ios本地嵌入,未在服务器上面运行,所以使用<script type="module">
会报如下错误,这是因为他们本地是以file://的方式加载。
2.构建出来的包会部署到线上,但是线上引入的资源会在oss上面,不会和html在同域名下,这就会报跨域的错误。
针对这2种情况官方提供### @vitejs/plugin-legacy插件用于支持在低版本浏览器上面无法用<script type="module">加载脚本。
plugin-legacy会使用<script nomodule>脚本用于判断如果浏览器不支持type=module就会执行里面的代码,动态添加script标签,vite-legacy-polyfill和vite-legacy-entry。
事实上我的问题是运行的所有环境都不支持<script type="module">加载,所以这里的代码似乎多余了,我琢磨了plugin-legacy相关的配置,没发现如何编译成只含有legacy相关的代码,于是我手动修改成
当然这样直接双击html文件就能正常运行,但是如果您是一个多页应用的项目,这种做法无疑是很难受的,于是我是在package.json脚本里面加了如下代码
在执行完build之后执行BuildDone.js文件,安装了jsdom依赖
const path = require('path');
const fs = require('fs');
const JSDOM = require('jsdom').JSDOM;
const distRootPath = path.join(__dirname,'../','dist');
/**
* android 模式下,修改生成之后的模板
* @param html
* @return {*}
*/
const parseDom = (html)=>{
const Dom = new JSDOM(html);
const scripts = Dom.window.document.querySelectorAll('script[type=module]');
deleteFile(scripts[0].src)
for(let script of scripts){
script.parentNode.removeChild(script)
}
// const link = Dom.window.document.querySelector('link[rel=modulepreload]');
// deleteFile(link.href)
// link.parentNode.removeChild(link)
const nomoduleScripts = Dom.window.document.querySelectorAll('script[nomodule]');
nomoduleScripts[0].parentNode.removeChild(nomoduleScripts[0])
nomoduleScripts[1].removeAttribute('nomodule');
nomoduleScripts[1].removeAttribute('crossorigin');
nomoduleScripts[1].removeAttribute('id');
const scriptNode = Dom.window.document.createElement('script');
scriptNode.src = nomoduleScripts[2].dataset.src;
Dom.window.document.body.appendChild(scriptNode)
nomoduleScripts[2].parentNode.removeChild(nomoduleScripts[2])
return Dom.serialize();
}
/**
* 删除文件
* @param filePath
*/
const deleteFile = (filePath)=>{
const realPath = path.join(distRootPath,filePath)
fs.existsSync(realPath) &&fs.unlinkSync(realPath);
}
/**
* 生成文件
*/
const indexPath = path.join(distRootPath,'index.html');
const rawHtml = fs.readFileSync(indexPath,{encoding:'utf-8'});
let html = parseDom(rawHtml)
fs.writeFileSync(path.join(distRootPath,`index.html`),html,{encoding:'utf-8'});
由此一来就能自动生成只包含legacy项目的脚本了。
当然这种方式实现是非常丑陋的,我可能需要再仔细阅读下相关文档看是否有解决方案,如果您有比较好的方案,请通知我,非常感谢🙏。