node-sass 迁移 sass(dart-sass) 后样式报错?用 loader 先把构建救回来
Vue CLI 老项目迁移 dart-sass:用一个插件兼容 /deep/、>>> 和 calc(100%-16px)
仓库:
vue-cli-plugin-sass-compat
GitHub:github.com/myltx/vue-c…
老的 Vue CLI 项目升级 Node/依赖后,经常会被迫从 node-sass(libsass) 迁移到 sass(dart-sass)。真正卡人的往往不是“装上 sass 就完事”,而是项目里存在大量历史写法,导致迁移后构建直接报错或样式编译不符合预期。
这篇笔记记录一个“过渡期方案”:通过 vue-cli-plugin-sass-compat 在 sass-loader 后插入一个轻量 loader,对源码做字符串级兼容替换,让你先把构建跑起来,再逐步治理样式。
你可能遇到的两类典型坑
1) 深度选择器旧写法:/deep/、>>>
在一些链路/组合下,旧写法可能触发解析问题(例如 dart-sass 报 Expected selector),或者在升级过程中需要统一成 Vue 推荐的写法。
目标:将这些旧写法尽量自动转换为 ::v-deep。
2) calc() 运算符空格:calc(100%-16px)
sass(dart-sass) 对 calc() 表达式更严格,常见历史写法例如:
.a { width: calc(100%-16px); }
可能需要改成:
.a { width: calc(100% - 16px); }
目标:在迁移过渡期,自动补上二元运算符(+/-)两侧空格,避免全仓库手工替换造成巨大 diff。
方案:vue-cli-plugin-sass-compat
这个插件做的事情很克制:
- 作为 Vue CLI Service 插件,通过
chainWebpack在sass-loader后插入一个 loader - 只处理你项目内的
.scss/.sass文件(默认跳过node_modules) - 以“迁移过渡”为目标做最小替换:
-
/deep/、>>>→::v-deep -
calc(...)中的二元+/-自动补空格(尽量避开一元运算等场景)
-
使用前置:先完成依赖迁移(必做)
本插件不负责替你替换依赖。使用前请先把项目从 node-sass 迁移到 sass(dart-sass):
npm rm node-sass
npm i -D sass
然后正常跑一遍安装:
rm -rf node_modules
npm i
安装与使用
方式 A:已发布到 npm(推荐)
npm i -D vue-cli-plugin-sass-compat
可选:迁移检查命令(doctor)
插件在 serve/build 首次执行时会做一次轻量检查:如果检测到仍存在 node-sass 或尚未安装 sass,会打印提示。
也可以手动运行:
vue-cli-service sass-compat:doctor
可选配置(vue.config.js)
默认两项修复都开启(true)。需要精细控制时可以这样写:
module.exports = {
pluginOptions: {
sassCompat: {
fixDeep: true,
fixCalc: true
}
}
}
-
fixDeep:是否将/deep/、>>>等旧写法转换为::v-deep -
fixCalc:是否修复calc(100%-16px)等calc()内+/-运算符空格
转换示例
深度选择器
输入:
.a /deep/ .b {}
.a >>> .b {}
输出(示意):
.a ::v-deep .b {}
.a ::v-deep .b {}
calc() 空格
输入:
.a { width: calc(100%-16px); }
输出:
.a { width: calc(100% - 16px); }
工作原理(简述)
-
index.js:通过api.chainWebpack,在sass/scss规则里找到sass-loader,并在其后插入sass-compat-loader -
sass-compat-loader.js:拿到每个样式文件的源码做字符串替换- 跳过
node_modules -
/deep/直接替换为::v-deep -
>>>替换为::v-deep,并尽量补齐必要空格 - 对
calc(...)做一次括号配对扫描,只在calc()内尝试给二元+/-补空格
- 跳过
边界与建议
- 这是“迁移过渡”工具:建议你在构建恢复稳定后,逐步把业务代码里真正的历史写法治理掉,最终可以移除该插件
-
calc()修复目前只处理二元+/-,不会尝试覆盖*、/等更复杂场景 - 如果你项目里对
::v-deep的使用有更严格的团队规范,建议在过渡期结束后统一做一次规范化替换
最后
如果你也在做 Vue CLI 老项目的 node-sass -> sass(dart-sass) 迁移,欢迎直接试用这个插件;也欢迎提 issue 描述你遇到的“历史写法”,我会优先考虑把高频场景纳入兼容范围。