稿定 Web 端业务中的平面编辑器已经有五年以上的历史。作为一个历经多人主导维护的前端项目,它有这么一些复杂度:
成都创新互联公司是专业的文登网站建设公司,文登接单;提供网站制作、成都网站建设,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行文登网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!
编辑器在 2016 年的第一次提交,基于 Vue 0.8 和 AMD 语法
我们不敢说这就是所谓的「大型企业级」项目,但这至少肯定不是个玩具项目。然而超乎预期的是,「Vite 的迁移成本甚至比升级 Webpack 和 Babel 大版本还要低」。只花了一个下午的时间,基于 Vite 的编辑器最小可用 MVP 就跑起来了。下面分几点介绍相关的实践经验:
我们知道,以 Webpack 为代表的主流前端 bundler 之所以慢,根源在于它们冷启动时必须递归打包出整个项目的依赖树,并受限于 JavaScript 的天性(解释执行与单线程模型)而存在吞吐量上的瓶颈。为了解决这两个痛点,Vite 另起炉灶切换了路线:
Vite 的这个设计与 webpack-dev-server 之间的区别,在其文档中也已经展示得很清楚,一图胜千言:
Webpack 式的经典 bundler 示意图
Vite 式的 No-bundler 示意图
基于这个差异我们就可以知道,要让 Vite 支持原有的 Webpack 项目,需要保证的无非两件事:
当然这只是最简单的思维模型。实际的前端项目中往往还会引入一些奇怪的东西,比如 CSS、JSON、Worker、WASM、HTML 模板……虽然 Vite 对这些需求已经内建了良好的支持,但确实谁也不敢保证能一键开箱即用——这并不是 Vite 或 Webpack 的问题,而是移植代码构建环境时的共通难点。对这类任务,「最难的地方总在于从零到一的「点亮」」。因此这里对此的建议是这样的:「充分熟悉从项目入口到各组件渲染完成之间所经历的代码(子)树,确保这一个最小的子集能够在新环境下正常运作」。其他代码都可以大刀阔斧地暂时移除掉。
对于架构设计合理的软件项目,一般都可以容易地实现模块的精简和扩展。例如在这个编辑器中,我们就支持了可配置并按需加载的元素类型。对于现有的 20 余种业务元素,它们对应的模块都已经支持了按需加载,只会在遇到相应数据时 import() 导入。因此在迁移时,只需保留若干基础元素模块实现用于测试即可。类似地,在业务项目中也可以通过精简路由配置等方式,定制出一个用于走通主流程的最小可用版本。
上述的代码精简过程,其实不外乎是建立一个干净的 example 页面来导入项目,注释掉部分代码然后反复执行 vite 命令测试,这里不再赘述。对于 Vite 迁移,很多同学最担忧的可能还是 Webpack 插件兼容性方面的问题。我们恰好也遇到了类似的问题,这里简单分享一下。
在前面 2016 年的编辑器上古版本代码截图中有一个细节,那就是其中引入了 editor.html 作为组件的 HTML 模板。这个行为历经多年一直保留到了现在——也就是说这里没有使用 SFC 单文件组件,而是对 text-element.js 等组件配套放一个 text-element.html 作为其模板,像这样:
// 导入 HTML 源码 --code秘密花园
import TextElementTpl from './text-element.html'
// Vue 2.0 的经典配置 --code秘密花园
export default {
template: TextElementTpl,
methods: {
// ...
},
created() {
// ...
}
}
在 Webpack 配置中,我们一般会用 HTML loader 来支持它,那么 Vite 呢?这类需求似乎并没有内置,而现在社区的 vite-plugin-html 是为 EJS 模板设计的,star 数量好像也不多……但真的就要等社区做现成的给你吗?
其实,Vite 的插件系统是直接依赖 rollup 的。对于这个需求,只要这样在 vite.config.js 里写个几行的插件就够了:
// 使用 rollup 附带的 plugin utils --ConardLi
const { createFilter, dataToEsm } = require('@rollup/pluginutils');
function createMyHTMLPlugin() {
// 建立一个用于筛选模块的 filter
const filter = createFilter(['**/*.html']);
return {
name: 'vite-plugin-my-html', // 起个名字 --ConardLi
// 根据 id 来筛选模块,并在遇到匹配的模块时变换其 source
transform(source, id) {
if (!filter(id)) return;
// 这样 HTML 字符串就能被 export default 给其他 JS 模块了
return dataToEsm(source);
},
};
}
// 这样就可以按照 Vite 的标准 API 来使用插件了
module.exports = {
plugins: [createMyHTMLPlugin()],
}
这个 createMyHTMLPlugin 不就是个非常简单的函数而已吗?但它却切实地解决了一个实际问题。个人认为对用户友好的构建系统应该做到在大多数时候能开箱即用,并能通过简单的逻辑自行扩展。在这一点上,可以说 Vite 还是做得相当出色的。另外 Vite 相比 Snowpack 的一个主要区别,就是它的插件系统与 Rollup 有更深的集成,由此实现了在 dev 和 build 两种模式下通用的插件 API。因此在业务中,也有机会自行「套壳」一些成熟的 Rollup 插件来实现需求。
在这次实践中用到的 Vite 配置相当少,值得一提的主要是这么几条:
基于上面介绍的这些实践,应当已经足够解决 Vite 对各类业务模块的加载问题了。但最后还有一个比较头疼的地方:如果 node_modules 中的依赖不能被 esbuild 正确打包,又该怎么办呢?
在这次迁移中,这样的问题我们有遇到两处,各自的原因有所不同:
对于这两个问题,其实都有一种通用的 workaround 手法:「建立一个 third_party 目录,把存在问题的上游模块拷贝一份进去,在这里修复问题并调整模块依赖即可」。如 Pica 库内 require('./a.js') 的代码,就可以复制到 third_party 目录后,将模块导入路径改为 require('pica/src/a.js'),这样并不需全量复制整个上游依赖。而对于这里遇到的两个 CommonJS 问题,具体的修复也都很容易,例如把对 arguments 的读取放到 export default 的函数体内,并直接移除在浏览器环境下用不到的 Node 文件读取逻辑等。这样的 third_party 模式实际上倒也不算什么 hack,在很多语言的工程中有很广泛的使用,但也有些地方值得注意:
以上就是全部值得列出的问题了,最后放一张基于 Vite 启动本地环境成功时的截图:
上图的日志有个问题,即加载了两个不同的 Vue 版本。这是因为 SFC 部分和依赖 HTML 模板的代码误用了不同的 Vue 依赖。这个问题后来通过 alias 配置将 vue 全部重写到 vue/dist/vue 而解决了。
由于编辑器 SDK 原本就使用 Babel 独立发版,因此原有的 NPM 发布过程不受影响,Vite 整体的侵入性也并不高。至于最终效果上也没有什么别的,就是油门踩到底加速了一下:
这样一来,这个历史项目就重新获得了即时反馈级别的开发体验,同时也让更高效的 CI 集成成为了可能。这里的想象空间还很大,我们很期待让 Vite 在未来发挥出更大的作用。
实际上作为本文的作者,之前个人还尝试过一些类似的代码移植。这类工作就像是一个破解密室逃脱游戏的过程,非常有趣。个人感觉像这次的 Vite 迁移,在实践手段上其实和之前的经历都是相当共通的:
所以最后,非常鼓励大家多做兴趣驱动的技术尝试。没准未来的哪天,折腾它们的经验就能帮助你找到抓手,赋能业务,形成闭环,打出一套组合拳呢
文章名称:前端历史项目的Vite迁移实践总结
文章起源:http://www.shufengxianlan.com/qtweb/news4/13304.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联