某天,我的 sass-loader 突然不认 /deep/ 语法了

15,241 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

TL;DR

如果是新项目开发中发现 sass-loader 不认 /deep/语法,参考一下建议:

  • 使用 ::v-deep 语法替代(不同 sass 的实现都支持)
  • 尽量使用 dart-sass,改用 ::v-deep语法
  • 最好为 sass-loader 指定 implementation 参数

如果是历史项目一直使用的 node-sass + /deep/ 进行开发,但是 sass-loader 突然不认 /deep/语法了,检查一下 sass-loader 实际加载的是不是 sass(dart-sass)

方法:

在项目的根目录运行启动 node,输入 require.resolve('sass'),如果模块解析成功返回了 sass 包的路径,则说明 sass-loader 错误地加载到了 sass而不是 node-sass

问题描述

在笔者工作维护的一个旧项目中,技术栈是 Vue,采用 sass-loader + node-sass进行 scss语法的解析。

问题出现在一个阳光明媚、居家隔离的下午,笔者像日常一样 yarn start 启动项目后准备开始怼业务需求,却发现页面打不开,报错内容都是:

SassError: expected selector. /deep/ ...

看到错误的第一眼我马上去检查了我项目 node_modules 里的 sass-loader node-sass安装的版本是不是发生了变化,结果看到安装的版本都是之前 yarn.lock 里所指定的版本。

尽管如此我还是把 node_modules 全都移除了之后重新安装了一次项目依赖,结果问题依旧存在。

一脸疑惑的我甚至让同事给我 air drop 了一整个 node_modules 来排除环境的影响,结果一样的依赖、一样的代码在他的电脑可以正常运行,在我的电脑就报错了。

难道是全局的 node_modules 里安装了 sass 导致 sass-loader 错误加载了?

我把全局的 node_modules 文件夹都清空后发现问题依然存在。

查看文档

十分不解的我自认为已经排除了代码跟全局环境的影响了,开始怀疑是 sass-loader 包自身的问题,打开了 github.com/webpack-con… 开始翻 issue,但是并没有什么收获。但是在 README 中看到了:

于是我尝试向 sass-loader 指定 scss 语法的实现为 node-sass

module.exports = {
  module: {
    rules: [
      {
        test: /.s[ac]ss$/i,
        use: [
          "style-loader",
          "css-loader",
          {
            loader: "sass-loader",
            options: {
              implementation: require("node-sass"),
            },
          },
        ],
      },
    ],
  },
};

重启项目后果然生效了!

可是 sass-loader 在之前一直都是认的 implementationnode-sass,为什么突然就“翻脸不认 node-sass 了”呢?

继续看到文档中写的:

于是我检查了一下项目的 package.json

如果 sass-loader 是检查的 package.json 那应该是能正常拿到我使用的是 node-sass 才对。好奇的我把 sass-loader clone 了下来,准备一探究竟!

Soga(原来如此)

github.com/webpack-con…

sass-loader 的入口 index.js/loader 方法里调用了 getSassImplementation方法

github.com/webpack-con…

getSassImplementation里如果判断 loader options 中没有指定 implementation 则会调用 getDefaultSassImplementation

github.com/webpack-con…

原来 sass-loader并不是解析项目的 package.json 而是直接在运行时里依次尝试加载 sass(dart-sass)、node-sasssass-embedded(sass 同个团队维护拥有相同 API 的版本,运行更快,但是需要 dart 的运行环境)。

这么说是在我的项目里可以加载到 sass 才导致的 sass-loader 不使用 node-sass

于是我马上进入到我的项目根目录启动 node 后运行 require('sass')sass 果然被成功加载了!

那么罪魁祸首是?

这时候我彻底焕然大悟。同期我正在开发一个新的项目,有天为了模拟公司的 jenkins 中的构建环境,将公司的构建镜像拉到了本地 docker 的容器内运行。

在用 docker cp 命令将本地项目拷贝到 docker 容器中的时候,因为环境不同 node_modules 最好重新安装。但是为了避免重新在本地安装依赖,我把 node_modules 暂时移动到了上一级目录中(即上图中的 /Users/yongjia.lin/projects),打算 docker cp 后再移动回来。

但是过了没多久我就忘了这回事了,而旧项目也无辜地收到了波及...

总结

但其实旧项目也没有那么无辜,我们最好在 sass-loader 中指定 implementation 参数,避免受到外部薛定谔的 sass 的影响。另新项目不要再用 node-sass 啦~