使用 grunt-sass 实现 dart-sass 与 node-sass 一键切换
解读
面试官真正想考察的是“在不改任务名、不改文件结构、只改配置”的前提下,让团队能在 dart-sass(官方推荐、无 C++ 编译依赖) 与 node-sass(老项目遗留,速度更快但 Node 版本锁死) 之间自由切换。
国内真实场景里,node-sass 常因 node-gyp、Python、VS Build Tools 下载失败 导致 CI 挂掉;而 dart-sass 又可能因 @use / @forward 新语法 把老代码打爆。因此“一键切换”必须满足:
- 零任务脚本改动;
- 依赖按需安装,不硬编码;
- 切换后 sourceMap、输出压缩、includePaths 完全一致;
- 能在 package.json scripts 里用
grunt sass:dart/grunt sass:node显式指定,也能通过 环境变量 默认回退。
知识点
- grunt-sass 只是壳,真正干活的是
implementation参数; - node-sass 与 sass(dart-sass) 的 API 差异:
renderSync选项、importer 签名、sourceMap 格式; - 动态 require 与 try/catch:防止未安装对应包时进程崩溃;
- grunt.config.merge 或 模板字符串 实现多 target;
- cross-env 或 npm config 在 Windows / macOS / Linux 下统一注入环境变量;
- ci 镜像源:国内淘宝源、华为源对 node-sass 二进制文件加速;
- lockfile 策略:yarn resolutions / npm overrides 锁定 sass 版本,避免同事误升级。
答案
- 安装依赖(按需)
# 方案 A:dart-sass
npm i -D grunt-sass sass
# 方案 B:node-sass
npm i -D grunt-sass node-sass
- Gruntfile.js 关键片段
module.exports = function(grunt) {
const engine = process.env.SASS_ENGINE || 'dart'; // 默认 dart
let impl;
try {
impl = engine === 'node'
? require('node-sass')
: require('sass');
} catch (e) {
grunt.fail.fatal(`请先安装 ${engine === 'node' ? 'node-sass' : 'sass'} 包`);
}
grunt.initConfig({
sass: {
options: {
implementation: impl,
sourceMap: true,
outputStyle: 'compressed',
includePaths: ['node_modules']
},
dist: {
files: [{
expand: true,
cwd: 'src/scss',
src: '**/*.scss',
dest: 'dist/css',
ext: '.css'
}]
}
}
});
grunt.loadNpmTasks('grunt-sass');
grunt.registerTask('default', ['sass']);
};
- package.json 脚本
"scripts": {
"sass:dart": "cross-env SASS_ENGINE=dart grunt sass",
"sass:node": "cross-env SASS_ENGINE=node grunt sass"
}
- 一键验证
npm run sass:node # 使用 node-sass
npm run sass:dart # 使用 dart-sass
拓展思考
- 性能对比:dart-sass 同步编译 1 万行代码比 node-sass 慢 30%,可通过 grunt-concurrent 把 sass 任务拆到子进程,或启用 sass 的 async render(需改写 grunt-sass 源码);
- 语法兼容:老项目用
/deep/或>>>时,dart-sass 会报错,可写 自定义 importer 把旧语法实时替换为::ng-deep; - 二进制缓存:CI 环境把
~/.npm/node-sass目录缓存到 阿里云 OSS / 腾讯云 COS,减少 90% 下载时间; - 未来迁移:官方已停止维护 node-sass,团队可定 里程碑版本号,在 package.json 中声明
"node-sass": "npm:sass@^1.77.0"实现 无感重定向,最终彻底移除 node-sass。