每次更改完样式,需要手工刷新页面才行,如果用过Bracket等开发工具的会知道,有一个功能是动态展示,只要保存,就会自动更新页面显示,Gulp通过Browsersync
这个插件也可以实现该功能。
安装Browsersync和修改Gulp任务
首先还是安装这个包,有点大,可能需要下载一会:
npm install browser-sync --save-def
之后需要修改gulpfile.js,来使用这个包(这里遇到了.start is not a function
错误,由于还没有深入gulp,只好先改成3.9.1版。),然后使用了例子的文件:
var gulp = require('gulp'), watch = require('gulp-watch'), postcss = require('gulp-postcss'), autoprefixer = require('autoprefixer'), cssvars = require('postcss-simple-vars'), nested = require('postcss-nested'), cssImport = require('postcss-import'), browserSync = require('browser-sync').create(); gulp.task('default', function() { console.log("Hooray - you created a Gulp task."); }); gulp.task('html', function() { console.log("Imagine something useful being done to your HTML here."); }); gulp.task('styles', function() { return gulp.src('./app/assets/styles/styles.css') .pipe(postcss([cssImport, cssvars, nested, autoprefixer])) .pipe(gulp.dest('./app/temp/styles')); }); gulp.task('watch', function() { browserSync.init({ server: { baseDir: "app" } }); watch('./app/index.html', function() { browserSync.reload(); }); watch('./app/assets/styles/**/*.css', function() { gulp.start('cssInject'); }); }); gulp.task('cssInject', ['styles'], function() { return gulp.src('./app/temp/styles/styles.css') .pipe(browserSync.stream()); });
这里有几个地方需要学习一下:
- 导入的时候是导入
require('browser-sync').create()
,不是直接导包。Node.js的导入包的设置与具体的包有关。 - 如果要更新HTML页面,在监听HTML文件的任务里需要使用
browserSync.reload();
- 监听CSS文件,这里使用到了任务以来,任务的链条是这样的
- 监听到CSS源文件发生变动
- 应该执行
cssInject
任务–》这个任务依赖于'styles'
(作为第二个参数,任务数组的一个元素传入),所以会先执行'styles'
任务 - 执行
cssInject
任务,将生成的style.css文件通过管道传递给browserSync的流,即去更新页面。
这里的新知识就是关于任务依赖的参数。被当做依赖传入的任务,会先得到执行,执行完毕之后,再执行当前任务。
browserSync
需要先初始化,其中有个server
参数是指的项目根目录,当前路径是运行gulpfile.js
的路径,所以设置为相对路径'app'
。
然后启动watch任务,实际上browserSync
是一个小的浏览器,采用推送方式自动更新,可以看到命令行中的提示:
>gulp watch [18:20:40] Using gulpfile D:\Coding\frontendworkflow\gulpfile.js [18:20:40] Starting 'watch'... [18:20:40] Finished 'watch' after 31 ms [Browsersync] Access URLs: --------------------------------------- Local: http://localhost:3000 External: http://192.168.100.85:3000 --------------------------------------- UI: http://localhost:3001 UI External: http://localhost:3001 --------------------------------------- [Browsersync] Serving files from: app
此时无论修改index.html还是所有的css文件,保存后都会立刻反应在页面上。
仔细观察的话,会发现每次页面刷新,右上角都会提示连接到browsersync,想关掉这个提示需要加一行配置:
browserSync.init({
notify: false,
server: {
baseDir: "app"
}
});
browsersync的功能很强大,比如如果另外开启一个浏览器,在地址栏输入http://localhost:3000/
,滚动其中一个浏览器窗口,另外一个也会滚动。样式也会同步更新,便于看浏览器兼容性。
http://localhost:3001/
则是browsersync的管理面板,有兴趣可以继续研究。
WebStorm中配置项目
这是我自己琢磨出来的,在WebStorm中配置普通的前端工程化项目按照如下步骤:
- 创建一个Node.js项目,或者普通的Web项目也可以。然后到
File | Settings | Languages & Frameworks | Node.js and NPM
中设置本机Node.js和NPM的地址,之后可以在左侧的External Libraries
中看到Node.js Core
,说明配置成功。 - 遇到代码检测错误,可以在拼写错误的地方按Alt+Enter,在提示中选择启用Node.js拼写支持,即可写Node.js的特有代码。
- 右键在左侧项目目录中点击
gulpfile.js
,可以看到IDE认出了这是一个gulp任务文件,菜单中最下边有Show Gulp Tasks
的选项,点击后可以打开Gulp面板,列出所有任务,可以启动任意任务,比从命令行启动或者terminal窗口启动占用一个窗口要方便。 - 从Git新下来项目的时候,IDE会提示检测到NPM,可以直接让其运行NPM来安装依赖。
结构化Gulp任务文件
现在的gulpfile.js
文件已经有点多了,导入文件,监听html和监听css的任务都在一起。这个文件也是可以通过分成小文件来有效的实现结构化管理。
在项目目录下新建gulp
目录,然后在其中创建tasks
目录。
要将任务分离出去,创建一个与任务同名的js文件,比如styles.js
,将在gulpfile.js
中的以下部分剪切过去,另外还需要导入对应的包。styles.js
的内容像这样:
let gulp = require('gulp'), postcss = require('gulp-postcss'), autoprefixer = require('autoprefixer'), cssvars = require('postcss-simple-vars'), nested = require('postcss-nested'), cssImport = require('postcss-import'); gulp.task('styles', function() { return gulp.src('./app/assets/styles/styles.css') .pipe(postcss([cssImport, cssvars, nested, autoprefixer])) .pipe(gulp.dest('./app/temp/styles')); });
再创建watch.js
把watch
任务分离出去:
var gulp = require('gulp'), browserSync = require('browser-sync').create(), watch = require('gulp-watch'); gulp.task('watch', function () { browserSync.init({ notify: false, server: { baseDir: "app" } }); watch('./app/index.html', function () { browserSync.reload(); }); watch('./app/assets/styles/**/*.css', function () { gulp.start('cssInject'); }); }); gulp.task('cssInject', ['styles'], function () { return gulp.src('./app/temp/styles/styles.css') .pipe(browserSync.stream()); });
原来的文件里两个dummy 任务都可以删除,导入也没有什么用了,所以可以全部删除。很显然不能是一个空文件,其实像CSS文件一样,剩下要做的就是导入几个任务文件:
require('./gulp/tasks/styles'); require('./gulp/tasks/watch');
这样就有效分离了gulp的各个任务文件。而运行的命令不变,依然是gulp watch
。而且WebStorm依然可以解析该Gulp文件并启动任务。
处理Gulp异常
在学习的过程中,肯定会发现,如果CSS保存了错误的指令,postcss在编译的时候会报错,然后watch任务也停了下来,必须手工启动,browsersync也会失去连接,这显然比较麻烦。
CSS语法错误是很常见的错误,如果不希望工作流被打断,很显然需要处理编译中的异常,以让程序不会停止。由于编译过程在styles
任务中执行,所以来修改一下styles.js
加入处理异常的功能:
gulp.task('styles', function () {
return gulp.src('./app/assets/styles/styles.css')
.pipe(postcss([cssImport, cssvars, nested, autoprefixer]))
.on('error', function (error) {
console.log(error);
this.emit('end');
})
.pipe(gulp.dest('./app/temp/styles'));
});
在完成编译工作的过程里,加上了一个on
函数,第一个参数是类型,用字符串'error'
表示出现异常,第二个参数是一个处理函数,参数可以传入错误对象,然后打印出来。
函数不需要返回值,但是需要通过this.emit('end')
抛出一个类似事件的东西,告诉流没有异常,而是正常结束。
这样我们在编译CSS文件的过程里,遇到错误不会中止,同时又可以告诉我们哪里出了错误。比如我们故意使用一个不存在的CSS变量:
[20:37:24] Starting 'styles'... { CssSyntaxError: D:\Coding\frontendworkflow\app\assets\styles\modules\_large-hero.css:5:5: Undefined variable $color ...... fileName: 'D:\\Coding\\frontendworkflow\\app\\assets\\styles\\modules\\_large-hero.css', lineNumber: 5 } [20:37:24] Finished 'styles' after 111 ms [20:37:24] Starting 'cssInject'... [Browsersync] 1 file changed (styles.css) [20:37:24] Finished 'cssInject' after 1.45 ms
会发现打印出了编译出错的地方,然而任务并没有结束,browsersync也一样更新正常。现在不会中断的,反应式的前端工具流就搭建完成了。