听闻 Vite 速度很快,最近开始考虑把项目里面的 webpack 打包换成 Vite
虽然折腾了一波之后最后放弃了,还是记录一下过程,算是一篇踩坑记录
由于是 Rails 项目,我就直接使用封装好的 vite_ruby 开始搞了
看了下官方文档,感觉很清晰,再根据 这个步骤,常规操作搞一波,该 install 的 install 完,再替换下 javascript_packs_tag 什么的,试了一下启动,果然飞快,感觉要大功告成了:
- 再一看发现样式都没有出来,css 文件没有加载,得用 <%= vite_stylesheet_tag 'application.scss' %>
加载一下,另外如果是 scss 格式的话必须显示指定一下
- 接下来会发现大量这样的错误:
原因很简单,我们项目里用了 React 自然有大量的 JSX 语法,但是我们很多文件后缀是 .js 命名的,而在 JS 文件里的 JSX 语法是被 Vite 认为不支持的
Vite assumes that js files contain only js valid syntax. But jsx has another AST and therefore is only supported in jsx files
尤大大也说了 (https://twitter.com/youyuxi/status/1362050255009816577),本来 JS 文件大多数情况下是不需要完全的 AST transform 的,如果要支持 JS 文件里的 JSX 语法的话,相当于所有文件都需要被 full-AST-processed. 这样其实不是一个好的做法
那怎么解决呢?找了三个方案:
1. 替换所有 .js 文件为 .jsx 或者 .tsx
2. 使用 plugin (https://github.com/dravenww/vite-plugin-react-js-support) 增加对 JS 文件中的语法支持
3. 使用 esbuilder 配置 loader 来解析文件,原理和方案2一样
最后我选了方案1,虽然要替换几百个文件看起来改动很大,但这个应该才是正确的做法,本来就不该命名为 .js
跑了个 shell 批量给替换了:find app/javascript/components -name "*.js" -exec sh -c 'mv "$0" "${0%.js}.tsx"' {} \;
- JSX 的问题搞定了,发现出现了另一个问题,大量的文件在 import 的时候找不到,之前用相对路径写的全都不能用了
例如 import XX from 'components/form/xxxx'
都得替换成 import XX from '~/components/form/xxxx'
这个好解决,在 vite.config.ts 里配置一下 resolve path 就好了:
import { defineConfig } from 'vite'
import { resolve } from 'path'
import RubyPlugin, { projectRoot } from 'vite-plugin-ruby'
export default defineConfig({
plugins: [
RubyPlugin()
],
resolve: {
alias: [
{ find: 'components', replacement: resolve(__dirname, 'app/javascript/components') },
{ find: 'shared', replacement: resolve(__dirname, 'app/javascript/shared') },
{ find: 'actions', replacement: resolve(__dirname, 'app/javascript/actions') },
{ find: 'stores', replacement: resolve(__dirname, 'app/javascript/stores') }
]
}
})
- 下一个问题,Webpack 里的 require.context 在 Vite 里是不能用的,需要用 glob-import 来做
比如 const componentRequireContext = require.context('components', true)
需要改成 const componentRequireContext = import.meta.globEager('../components/**')
另外其他用到 require 的地方也得改成 import
否则就会出现 require is not defined 的报错
因为 Vite 是完全依靠 ESM 原生能力的,也就是说它只认 import, 而 require 是 CJS 里的方法。我们的代码最终被送到浏览器里执行,浏览器本身就没定义这个方法,所以就报错了。
这里和 Webpack 不一样,Webpack 事先会编译打包好,到浏览器的时候已经把 require 转成浏览器能识别的方式了。
所以这里还是有点难受的,好在我这项目里 require 的地方不多,勉强把这个坑踩过去了。
- 最后终于把所有报错解决了,也跑起来了,发现有点不对,页面显示基本都不完整,很多东西都没显示出来。
这里要说的是,我们项目里很多地方不是 React 全局接管路由的,也就是说不是纯前端渲染,而是用了 react_rails 的方式来渲染的前端组件,问题就出在这儿,这种方式渲染的内容都没有显示出来。
猜测是这种方式引用的前端组件没有被 Vite 识别到。
于是翻到了社区的讨论:
vite_ruby 这边作者表示自己没有用过 react_rails, 可能不太兼容, 建议 react_rails 作者去支持组件注册 https://github.com/ElMassimo/vite_ruby/discussions/86
而看到 react_rails 这边对于 Vite 的支持也没有明确进展 https://github.com/reactjs/react-rails/issues/1134
玩到这里,我就基本放弃了,溜了溜了。
如果是对于新项目,确实可以尝试 Vite,速度会有提升;如果对于依赖太多的老项目,迁移起来可能会比较麻烦。
QRコードをスキャンしてWeChatを追加します