240116.2352

一次 Remix + Vite + Stylus 初体验,以及踩坑


前一段时间看中了 React 之后,然后就在着手把之前的 Nuxt+Vue 的 sworkshop-home 迁移到基于 React 的框架上。选来选去之后最终选了 Remix。

可能是因为自己的习惯实在奇怪,所以就也引出了一系列的问题。解决这些问题也花了不短的时间,觉得可以记录一下。

至于为什么选中了 Remix,是这样子的:安妮有 SSR 的需求,所以显然那些 SPA 或者不怎么能 SSR 的比较 low-level 的脚手架(like vanilla vite)显然就不太行。而在此之上安妮又有使用 stylus 的想法,React 的框架大部分都对于不是 sass/scss 的比较新的这种 css preprocessor 的支持蛮不好的。所以考虑了很久最终还是选用了 Remix。

Remix + Vite

安装 Remix + Vite 本来应该特别简单的,可惜的是安妮最开始用了标准的 Template。后续还是跟随着 Migrate Guide 弄上了 Vite。

这个 Guide 没什么好说的。从 init 工具安装到 migrate 到 Vite 的过程相当简单,跟着一步一步走就行了。因为安妮没有用到 express 的自定义后端或者 Tailwind CSS 或者 MDX 什么的,也没开始写自己的代码,所以只把需要的基础工具链的东西一步一步走完就可以了,也完美的一下子就跑了起来。

虽然据说还是 Experimental Feature,不过从目前的使用体验来看,其实蛮能用的。

+ Stylus

Stylus 如果在 Remix 的非 Vite 版本将会是一个比较难的地方。不过因为安妮换用了 Vite,而 Vite 是具有对 CSS Module + Sass/Scss/Less/Stylus 具有开箱可用的支持的,所以只需要 yarn add -D stylus 就可以了。

Stylus @impot 路径别名支持

不过,想要给 stylus 找插件就有点麻烦了。css preprocessor 领域 Sass/Scss 一家独大,less 紧随其后,而 Stylus 几乎就处于了一种快要无人问津的程度了。而且好像在 Vue 有 SFC,React 有 CSS Module 的情况下,似乎 css preprocessor 本身都快要无人问津了。

也可能是由于 Stylus 的语言设计或者 JS 接口的问题,Vite CSS Preprocessor 上面有这样一句话:@import alias and url rebasing are not supported for Stylus due to its API constraints.

这当然对安妮是很不满意的。于是在经过了一番互联网搜索后,安妮找到了这样一个插件:vite-plugin-stylus-alias – npm (npmjs.com) 。虽然最后更新已经是 2 年前了,安妮还是抱着试一试的态度装了一下——不出意外的话显然是不行的。结果是装了之后连最基础的 yarn dev 都跑不起来了。后来经过一番检查发现这个插件最后一次更新大概 Vite 还是 2.x 版本,而 Vite 现在都已经到 5.x 了。好在 Vite 的 API 还是足够稳定的。把插件 pull 下来之后自己再次经过一大番一整天的 debug 工作,成功的让它在安妮的环境下跑了起来。

后续给这个插件交了一个 pr,不过这个作者看起来都快有两三年没上 GitHub 了,所以安妮还是先本地打包一个用了。打算等一两个星期,如果原作者还没有看到 pr 的话,安妮就自己在 npm 发一个 next 版本了。这里也贴上安妮后续更新了的仓库

如果你想要在你自己的 Remix + Vite + Stylus 项目中使用的话,这里有几个需要注意的点:首先是这个插件需要先 build 再使用的。你可以通过下面的方式在本地 build 出来:

$ git clone https://github.com/Eyre-S/vite-plugin-stylus-alias.git
$ cd vite-plugin-stylus-alias
$ git checkout nightly
$ yarn
$ yarn build
$ yarn pack

在经过这几个步骤之后,你可以在项目根目录看到一个名称大概率是 vite-plugin-stylus-alias-next-v1.2.0.tgz 的文件。这就是插件在本地打包后的文件了,你可以把它拖到你需要使用的项目里,然后像这样子 "vite-plugin-stylus-alias-next": "file:./libs/vite-plugin-stylus-alias-next-v1.2.0"devDependency 里导入就可以了。

然后是如何配置这个插件。一般来说这个插件是不需要配置的,不过因为 Remix 使用了 vite-tsconfig-path 插件,而它是读取 tsconfig.json 来配置路径别名,而 vite-plugin-stylus-alias 插件只会读取 Vite 的 resolve.alias 配置,所以,你还需要再在 Vite 里面配置一遍路径别名。最终写出来的 vite.config.ts 大概像是下面一样:

export default defineConfig({
	plugins: [
		remix(),
		vitePluginTsconfigPaths(),
		vitePluginStylusAlias(),
	],
	resolve: {
		alias: {
			"~": path.resolve(__dirname, "./app"),
		}
	}
})

你或许已经注意到了刚才在配置的时候使用了 nightly 分支——这是因为,这个插件的原作者不知道为什么设置了忽略 ~ 符号,而刚刚好 Remix 就默认使用了 ~ 符号作为默认的路径别名。所以安妮为了暂时不和原作者意图冲突,就只在 nightly 分支去除了 ~ 符号的特殊判断,以让它能够使用 Remix 默认行为的 ~ 符号。去除这个特殊判断看起来也没有影响什么工作情况,所以应该是可以正常使用的。

为 Stylus + CSS Module 设置 VSCode 插件

Stylus 几乎就处于了一种快要无人问津的程度了。在 VSCode 上,安妮最终也只找到了由 sysoev 编写的 stylus language support,其最后更新时间也是两年前——也只能是还能用了。

因为安妮使用了 TypeScript,所以当然想要让 CSS Module 有类名提示了。不过,能够支持 Stylus 的 CSS Module 插件确实很少。最终弄了很久之后才发现—— VSCode 市场上排名第一个的 CSS Modules 插件就支持 Stylus,只是在它的 Readme 上完全没有写任何它支持 CSS Preprocessor 的迹象,安妮最后折腾无果想去随便看看 issue 有没有人提到 stylus,然后才发现,这个插件原来是支持所有的 CSS Preprocessor 的(每一个 CSS Preprocessor 都有 issue 而且都是已完成状态)。

另外,另一个基于 npm 模块 typed-css-module 的插件 CSS Module Typed 插件有可能也是能用的——只不过安妮在折腾的时候遇到了后面的一个坑,这就是后话了。

一些坑:

水合(SSR Hydration)错误

在折腾好所有的框架跑起来了之后,安妮刚准备写代码,一看后台报了大量错误:

于是安妮开始回滚,但回滚到了刚 init 好 Remix + Vite 项目的阶段,甚至是重新用官方的 vite template 重建了一个新项目还是遇到了这个问题。

后来经过在各种浏览器里搜索排查才发现了问题:hydration error when there is a chrome extension that modifies DOM · Issue #4822 · remix-run/remix (github.com)

根据这篇 issue 所说,这是 React 18.x 后导致的水合问题,升级到 18.3-canary 版本或者降级到 17.x 版本都可以解决——安妮升级到了 18.3-canary 版本,确实是可以解决问题的。至于 18.3-canary 还没有对应的 @types 包,看起来倒是 18.2 的 @types 包也可以正常用就不管了。

/app/routes/_index.module.styl 导致 Remix 无法编译

其实是一个挺蠢的错误的,仔细阅读 Remix 的文档就会发现文档里有这样一句话:Remix introduces a key convention to help streamline the routing process: the app/routes folder. When a developer introduces a file within this folder, Remix inherently understands it as a route. 所以,这是因为在 routes 文件夹当中的 .styl 文件也被 Remix 当作路由 endpoint 来解析了导致的—— Stylus 当然没法是路由 endpoint,于是就出错了。

解决方法也很简单,在 Remix 配置里面加上对 .styl/.stylus 的忽略就好了:

export default defineConfig({
	plugins: [
		remix({
			ignoredRouteFiles: [
				"**/*.styl",
				"**/*.stylus"
			],
		}),
		vitePluginTsconfigPaths(),
		vitePluginStylusAlias(),
	],
...

End

至此,Remix + Vite + Stylus 的配置就大体完成,可以开心的写代码了。

还有一些配置像是写一个 Stylus 的全局导入,或者给 CSS Modules 加上 kebab-case to camelCase 的自动转换什么的就都很简单了,自己摸索一下应该都能写得出来,也不详细赘述了。



發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *