前段时间我们搞了个研究和测试性质的cesium项目,基本都是前端的东西。也就是说,主要就是一些js。于是想到用gulp将项目进行发布。据说当今最热门的发布工具是webpack,其次是gulp。但webpack适合SPA,单页应用,而gulp适合传统的多页系统。我们项目非SPA,因此选用gulp。
gulp,就是一个项目发布工具,作用我看无非主要就是压缩js\css\html,然后还可以给文件加上版本号。压缩可以起到减少文件尺寸,节约带宽,加快加载,同时有一定保密作用;而加上版本号,则可以使浏览器缓存失效,避免程序已更新,但客户还是看到老效果这种弊端。这个对CDN就非常有用。但是这个功能估计要跟发布时的监控机制(?)结合起来才有意义,如果没有更新的代码,版本号也都跟之前不一样,迫使客户端全部重新加载一遍,反而造成浪费。我还估计,这些发布工具都跟自动部署这些概念有关,但我没有试过,可见多落后。这也是我这次花了一点时间来用一下gulp的原因。
闲话休提,以下是一些笔记:
一、准备环境
0、安装npm
1、安装gulp
npm install --save-dev gulp
也可以缩写成
npm i -D gulp
i就是install,-D 就是 --save-dev。至于–save-dev有什么作用,我搞不大清楚
2、安装各种插件
这些插件在发布时需要用到。
npm i -D gulp-rev gulp-rev-replace gulp-useref gulp-filter gulp-uglify-es gulp-csso gulp-minify-html gulp-clean
3、如果受到蛊惑,使用了淘宝的镜像
,可能会出现类似“ERR!Unexpected end of JSON input while parsing near”这样的错误
则应:
1)删掉package.lock.json
2)清除cache
npm cache clean --force
3)不再使用淘宝镜像
npm set registry https://registry.npmjs.org/
二、编写发布脚本
这个脚本一般叫gulpfile.js。看资料,说可以有多个这样的文件,分别完成不同的任务。我因为不大熟悉,就全部写在一个文件里了。内容其实也是js。用js来发布js。
我这份发布脚本,流程主要是
1)将发布目录清空
2)拷贝一些无须特别处理的文件,如图片,json;还有一些第三方js,它们本身就已经处理过了,直接拷贝就行了。
3)处理css,css文件加上版本号
4)处理js
js我没有加版本号。因为有许多js文件是动态加载的,加了版本号会404。也有一些插件,如gulp-rev-suffix,好像还是一个中国人写的,可以将版本号弄成
<script src="script/script-version.js?rev=17a5da6c8a2d875cf48aefb722eefa07"></script>
这种形式。但前提是html里要预留特殊标记:
<script src="script/script-version.js?rev=@@version"></script>
我觉得这很扯。
5)处理html
以下是 gulpfile.js 的完整示例:
/*
gulpfile.js
*/
const { series,parallel,src,dest } = require('gulp');
const rev = require('gulp-rev'),//添加版本号
revReplace = require('gulp-rev-replace'),//更新index.html下的引用
useref = require('gulp-useref'),//合并css或js文件
filter = require('gulp-filter'),//筛选和恢复
uglify = require('gulp-uglify-es').default,//压缩JS文件
csso = require('gulp-csso'),//压缩CSS文件
minifyHtml = require('gulp-minify-html'),//压缩html
clean = require('gulp-clean');//清理文件或文件夹
//public函数,那么我们发布的时候的命令就是 gulp build
exports.build = series(
empty,
copy1,copy2,
parallel(css,js),
html
);
const dist = "./dist";
const MANIFEST = dist + "/rev-manifest.json";
function empty(){//打包前先清掉目标发布文件夹
return src('dist/',{read: false,allowEmpty:true})
.pipe(clean());
}
function copy1(){//copy 文件夹libs to dist
return src(['code/libs/**'], {restore: true})
.pipe(dest(dist + '/libs'));
}
function copy2(){//直接复制无须处理的文件,如图片,json等等
return src(['code/**/*','!code/**/*.js','!code/**/*.css','!code/**/*.html','!code/libs/**','!code/.svn/**'], {restore: true})
.pipe(dest(dist));
}
function html(){
let manifest = src("./dist/rev-manifest.json");
//let indexHtmlFilter = filter(['**/*.html', '!**/widget.html'], {restore: true});
return src(['code/**/*.html','!code/libs/**','!code/.svn/**'], { sourcemaps: true })
.pipe(minifyHtml({
empty:true,spare:true,quotes:true
}))
//.pipe(indexHtmlFilter)/*筛选html文件*/
//.pipe(rev())/*生成哈希版本号*/
//.pipe(indexHtmlFilter.restore)/*放回流里*/
.pipe(revReplace({manifest: manifest}))/*更新index引用*/
.pipe(dest(dist));/*文件流放到dist目录下*/
}
function css(){
return src(['code/**/*.css','!code/libs/**','!code/.svn/**'], { sourcemaps: true })
.pipe(useref())/*处理注释压缩*/
.pipe(csso())/*压缩css文件*/
.pipe(rev())/*生成哈希版本号*/
.pipe(dest(dist))
.pipe(rev.manifest(MANIFEST,{base:dist,merge: true}))
.pipe(dest(dist));/*文件流放到dist目录下*/
}
function js(){
//var jsFilter = filter(['**/*.js','!code/js/lan3d.js'], {restore: true});
return src(['code/**/*.js','!code/libs/**','!code/.svn/**'], { sourcemaps: true })
.pipe(useref())/*处理注释压缩*/
.pipe(uglify())/*压缩js文件*/
//.pipe(jsFilter)/*筛选js文件*/
//.pipe(rev())/*生成哈希版本号*/
//.pipe(dest('dist'))
//.pipe(rev.manifest(MANIFEST,{merge: true}))
//.pipe(jsFilter.restore)/*放回流里*/
.pipe(dest(dist));/*文件流放到dist目录下*/
}
运行结果
三、要点
1、exports
一般网上的资料说的是gulp.task(),但看gulp官方网站,已经不推荐这种方式了。而是采用 exports.*** 的方式
2、gulp对es6或以上的支持
比如 const,Promise,async await之类的语法,关键是要:
用gulp-uglify-es这个插件来进行JS压缩,而不是一般资料里说的 gulp-uglify。
千万不要照网上说的什么采用babel或@babel,永远都是报错。害死人!!!
否则的话,你就在无尽的 Caused by: SyntaxError: Unexpected token: name ?_regeneratorRuntime?, expected:punc ?;? 之类的报错中死循环。
**3、一些用法**
```js
function html(){
let manifest = src("./dist/rev-manifest.json");
let indexHtmlFilter = filter(['**/*.html', '!**/widget.html'], {restore: true});
return src(['code/**/*.html','!code/libs/**','!code/.svn/**'], { sourcemaps: true })
.pipe(minifyHtml({
empty:true,spare:true,quotes:true
}))
.pipe(indexHtmlFilter)/*筛选html文件*/ //<------------------------
.pipe(rev())/*生成哈希版本号*///
.pipe(indexHtmlFilter.restore)/*放回流里*///<------------------------
.pipe(revReplace({manifest: manifest}))/*更新index引用*/
.pipe(dest(dist));/*文件流放到dist目录下*/
}
筛选,处理,然后放回流里。本来src这里已经指定了待处理的文件类型(html),然后处理过程中,加入这个筛选,是更精细的范围。处理完之后,放回流里,那么这个筛选结束了。筛选,在这里相当于大范围中的小范围,不同对象作不同的处理。
4、发布之后,一些不那么规范的代码可能会报错。这也算是一种质量控制机制吧。
附录