用 gulp-sass + gulp.watch 能实现基础自动编译,但未加错误处理(如 gulp-plumber)会导致报错中断监听;未做增量编译(如 gulp-cached + 依赖追踪)则每次全量编译低效;多入口需分任务或重命名控制输出路径。

直接说结论:用 gulp-sass + gulp.watch 能跑通基础自动编译,但不加错误拦截和增量处理,gulp watch 一报错就中断,改个括号就得手动重启。
为什么 gulp.watch 会突然不工作?
这不是 watch 本身的问题,而是 gulp-sass 默认出错时抛异常、终止 pipe 流程,导致整个监听链断掉。你改完 _variables.scss,保存后终端打出 Error: Invalid CSS after "...": expected "{", was "}",接着就静音了——后续任何修改都不再触发编译。
- 必须用
gulp-plumber包裹sass(),捕获错误并继续监听 - 不能只写
.on('error', sass.logError):它只打印错误,不阻止中断 - 常见写法陷阱:
.pipe(sass().on('error', sass.logError))看似合理,实则无效
怎样让 Sass 编译只处理改动的文件?
全量编译在中大型项目里越来越慢,尤其当 @import 关系复杂时(比如 theme.scss 导入 20 个 partial),每次改一个变量,所有都重编。真正的增量依赖追踪需要两层配合:
-
gulp-cached记录哪些文件内容变了(基于内存缓存) -
gulp-sass的includePaths配置要正确,否则无法识别被@import的依赖文件是否变动 - 仅靠
gulp-cached不够:它不感知@import 'mixins'这类依赖变化,得配合gulp-dependents或改用dart-sass的watch原生能力(推荐)
简版增量示例(仅内容变更):
const cached = require('gulp-cached');
const sass = require('gulp-sass')(require('sass'));
gulp.task('sass:incremental', () => {
return gulp.src('src/scss/**/*.scss')
.pipe(cached('sass'))
.pipe(sass({ includePaths: ['src/scss'] }).on('error', plumber.stop))
.pipe(gulp.dest('dist/css'));
});
多个入口、多个输出目录怎么配?
比如你有 admin.scss 输出到 admin.css,frontend.scss 输出到 css/ 下,不能只写一个 gulp.src('./scss/**/*.scss') ——那样会把所有文件都混进同一个 dest,路径全乱。
- 每个入口单独定义 task,或用
gulp-if+ 文件名判断分流 - 更稳妥的是用
gulp-sass-glob支持@import 'components/**/*',但注意它不解决路径映射问题 - 输出路径控制关键在
gulp.dest()和gulp-rename:例如.pipe(rename({ dirname: '' }))可抹平源目录层级
典型多入口配置片段:
gulp.task('sass:admin', () => {
return gulp.src('src/scss/admin.scss')
.pipe(plumber())
.pipe(sass().on('error', plumber.stop))
.pipe(gulp.dest('dist/admin/css'));
});
gulp.task('sass:frontend', () => {
return gulp.src('src/scss/frontend.scss')
.pipe(plumber())
.pipe(sass().on('error', plumber.stop))
.pipe(gulp.dest('dist/css'));
});
真正难的不是写通流程,而是当 _mixins.scss 被 12 个文件 import,你改它一行,怎么确保这 12 个都重新编译——gulp-cached 不管这个,得靠构建时扫描依赖图,或者换用 sass --watch 原生命令嵌入 Gulp。










