本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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
在之前一直都是认的 implementation
是 node-sass
,为什么突然就“翻脸不认 node-sass
了”呢?
继续看到文档中写的:
于是我检查了一下项目的 package.json
:
如果 sass-loader
是检查的 package.json
那应该是能正常拿到我使用的是 node-sass
才对。好奇的我把 sass-loader
clone 了下来,准备一探究竟!
Soga(原来如此)
在 sass-loader
的入口 index.js/loader 方法里调用了 getSassImplementation
方法
getSassImplementation
里如果判断 loader options 中没有指定 implementation 则会调用 getDefaultSassImplementation
原来 sass-loader
并不是解析项目的 package.json
而是直接在运行时里依次尝试加载 sass
(dart-sass)、node-sass
、sass-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
啦~