近期在梳理有关gulp工作流的过程中,写了一套简单的移动端静态页面解决方案
该方案具体解决了以下几个问题:
1、更改html、css、js等任意文件,实现浏览器重载,同步调试
2、实现sass实时编译与注入
3、实现移动端自适应与代码自动优化
4、支持css、js选择性合并压缩
5、支持ES6转换
6、解决前端静态资源版本更新与缓存
查看demo详见:github地址
安装
安装gulp插件
npm install --save-dev gulp
npm install --save-dev brower-sync
npm install --save-dev node-sass
npm install --save-dev gulp-sass
npm install --save-dev gulp-postcss
npm install --save-dev postcss-adaptive
npm install --save-dev autoprefixer
npm install --save-dev gulp-uglify
npm install --save-dev gulp-concat
npm install --save-dev gulp-clean-css
npm install --save-dev gulp-babel
npm install --save-dev babel-core
npm install --save-dev babel-preset-es2015
npm install --save-dev del
npm install --save-dev gulp-rev
npm install --save-dev gulp-rev-collector
npm install --save-dev gulp-html-replace
npm install --save-dev merge-stream
npm install --save-dev gulp-sync
实现方法
任务一:浏览器实时调试重载
gulp.task('browser-sync', function () {
browserSync.init({
files: ['**'],
server: {
baseDir: './',
index: 'index.html'
},
port: 8080
})
})
启动8080端口进行调试,files指定变动重载文件目录,这边我默认所有文件变动重载
任务二:sass实时编译注入(含优化)
gulp.task('sass', function () {
var plugins = [
adaptive({ remUnit: 75 }),
autoprefixer({ browsers: ['last 1 version'] })
];
return gulp.src('./scss/*.scss')
.pipe(sass({
sourcemaps: true
}).on('error', sass.logError))
.pipe(postcss(plugins))
.pipe(gulp.dest('./css'));
});
这一步的目的是把sass文件转化成css文件。其中用到了两个辅助插件,一个是adaptive对应的postcss-adaptive,是配合淘宝团队开发的移动端适配flexible.min.js一起使用的。另一个是autoprefixer对应的autoprefixer,以实现自动补充浏览器前缀的功能,个人觉得挺实用的。
gulp.task('sass:watch', function () {
gulp.watch('./scss/*.scss', ['sass']);
});
通过gulp watch实时监听sass文件的变化,改动同步注入到对应的css文件
任务一和任务二实现了文件在编译过程中的调试功能,打开命令行工具,运行gulp会执行下述代码
gulp.task('default', gulpsync.sync(['sass', 'sass:watch', 'browser-sync']));
任务三:文件打包
当文件编译完毕之后进入到打包阶段。我们先梳理一下打包的几个流程:
1.打包前先清除目标文件夹里的js和css(因为我们做了版本迭代,不重名的文件在多次打包后不会覆盖,所以需要把前一版本的文件删除)
2.把需要打包的css,js,images,html等文件经过特殊处理后放到目标文件夹
3.替换html中引入的文件版本号
步骤一:清除目标文件夹内容
gulp.task('clean:package', function () {
del([
'./build/css/*',
'!./build/css/*.json',
'./build/js/*',
'!./build/js/*.json',
]);
});
编译后的目标文件夹我指定的是build,其中用到了del来指定删除和保留的文件。json文件是css和js生成的版本号对应的文件,这里未做删除。
步骤二:打包文件
gulp.task('pack', function () {
// 打包css
var packcss = gulp.src('./css/*.css')
.pipe(concat('main.css'))
.pipe(cssmin({
advanced: false,//类型:Boolean 默认:true [是否开启高级优化(合并选择器等)]
compatibility: 'ie7',//保留ie7及以下兼容写法 类型:String 默认:''or'*' [启用兼容模式; 'ie7':IE7兼容模式,'ie8':IE8兼容模式,'*':IE9+兼容模式]
keepBreaks: true,//类型:Boolean 默认:false [是否保留换行]
keepSpecialComments: '*'
//保留所有特殊前缀 当你用autoprefixer生成的浏览器前缀,如果不加这个参数,有可能将会删除你的部分前缀
}))
.pipe(rev())
.pipe(gulp.dest('./build/css'))
.pipe(rev.manifest())
.pipe(gulp.dest('./build/css'));
// 打包js
var packjs = gulp.src(['./js/*.js', '!./js/*.min.js'])
.pipe(babel({
presets: ['es2015']
}))
.pipe(uglify())
.pipe(rev()) //加名字MD5
.pipe(gulp.dest('./build/js')) //保存
.pipe(rev.manifest())
.pipe(gulp.dest("./build/js"));
// 打包minjs
var packminjs = gulp.src('./js/*.min.js')
.pipe(gulp.dest('./build/js'));
// 打包html
var packhtml = gulp.src(['*.html'])
.pipe(gulp.dest('./build'));
// 打包images
var packimage = gulp.src('./images/**/*')
.pipe(gulp.dest('./build/images'));
return merge(packcss, packimage, packhtml, packjs, packminjs);
});
由于打包的文件的种类和方式比较多,因此我用了merge方法对应的merge-stream来合并任务,这样看上去会比较清晰。
css文件的打包方式是把所有的css文件合并压缩成一个大文件并生成带有版本号的json文件,其中合并用了gulp-concat,压缩用到了gulp-clean-css,生成版本号用到了gulp-rev,具体用法可参考npm文档。
js文件支持es6的语法,在打包过程中需先进行es6的转换,用到gulp-babel,再对每个文件分别进行压缩,这里用到了gulp-uglify,同样gulp-rev为其生成版本号文件。这里我没有按照css的打包流程来对所有js文件进行合并压缩,保留了使用的js插件库文件,翻译压缩对象仅仅为自己写的js文件,个人觉得这样更方便维护和更新。
剩余的html,image和min.js文件则原封不动的拷贝到打包目标文件夹,等待进行后续操作。其中我并没有把image文件进行压缩,因为每次build执行压缩操作比较耗时,建议image文件统一在tinypng这个网页上一次性压缩。
在执行完pack任务之后,在指定的目录会分别生成两个rev-manifest.json文件,分别携带了css和js文件的版本号,打开文件
{
"main.js": "main-e184645161.js"
}
但这并不是我们想要的效果,我们想要生成的文件名如下
{
"main.js": "main.js?v=e184645161"
}
因此我们需要修改几个文件来达到效果:
打开node_modules\gulp-rev\index.js
第135行 manifest[originalFile] = revisionedFile;
更新为: manifest[originalFile] = originalFile + '?v=' + file.revHash;
打开node_modules\rev-path\index.js
9行 return modifyFilename(pth, (filename, ext) => `${filename}-${hash}${ext}`);
更新为: return modifyFilename(pth, (filename, ext) => `${filename}${ext}`);
打开node_modules\gulp-rev-collector\index.js
40行的 var cleanReplacement = path.basename(json[key]).replace(new RegExp( opts.revSuffix ), '' );
更新为: var cleanReplacement = path.basename(json[key]).split('?')[0];
搜索prefixDelim,把 regexp: new RegExp( prefixDelim + pattern, 'g' ),
更新为: regexp: new RegExp( '([\/\\\\\'"])' + pattern+'(\\?v=\\w{10})?', 'g' ),
修改完毕之后运行gulp就能达到我们想要的效果了
步骤三:替换html中引用文件的版本号
gulp.task('rev:js', function () {
return gulp.src(['./build/js/*.json', './build/*.html'])
.pipe(revCollector({
replaceReved: true,
}))
.pipe(gulp.dest('./build'));
})
gulp-rev-collector这个插件的作用就是根据json文件,找出html中一一对应的js文件,并修改版本号。
gulp.task('replace:css', function () {
var text = fs.readFileSync('./build/css/rev-manifest.json', 'utf8'),
obj = JSON.parse(text),
str = '';
for (i in obj) {
str = obj[i];
}
gulp.src('./build/*.html')
.pipe(htmlreplace({
'css': './css/' + str,
}, { keepBlockTags: true }))
.pipe(gulp.dest('./build'));
})
在处理css版本替换的时候就面临了一个问题,因为css的所有文件都会被合并生成一个大文件,所以生成的json文件里只有一个合并后文件的版本号,因此gulp-rev-collector就无法一一对应替换,所以这边我用到的方法是,先获取到json文件里css文件的版本号,再找到所有html里需要替换的css文件(一个html文件里可能引用了多个css文件,有些需要替换,有些引用的外链无需替换)统一替换成一个合并后的css文件。这里用到了一个替换插件gulp-html-replace来实现对应操作,只需将需要编译的css文件用注释包起来就可以替换成想要的文件
编译前:
<!-- build:css -->
<link rel="stylesheet" href="./css/normalize.css">
<link rel="stylesheet" href="./css/main.css">
<!-- endbuild -->
编译后:
<!-- build:css -->
<link rel="stylesheet" href="./css/main.css?v=305ba0386a">
<!-- endbuild -->
任务三实现了编译结束后的打包工作,打开命令行工具,运行gulp build就会执行
gulp.task('build', gulpsync.sync(['clean:package', 'pack', 'rev:js', 'replace:css']))
以上就实现了用gulp实时编译和按需打包的功能啦~想试试的童鞋点击这里来构建自己的gulp工作流吧!
网友评论