阅读视图

发现新文章,点击刷新页面。

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-compatsass-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 插件,通过 chainWebpacksass-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 描述你遇到的“历史写法”,我会优先考虑把高频场景纳入兼容范围。

❌