0%

自动化构建(Grunt、Gulp)

自动化构建

源代码 -> 生成代码

前端样式自动化构建

输入 scss scss/main.scss css/style.css 即可构建出 css

  • 缺点:每次都需要重复输入这个复杂的命令

    npm script 就是用来解决这个问题

    1
    2
    3
    4
    5
    {
    "scripts": {
    "build": "sass scss/main.scss css/style.css --watch",
    },
    }

接下来安装 browser-sync 用于启动测试服务器

1
yarn add browser-sync --dev

运行测试服务器 npm run serve

  • --files 监听文件变化
1
2
3
4
5
{
"scripts": {
"serve": "browser-sync . --files \"css/*.css\"
},
}

不过这里可能有个问题,如果在运行测试服务器之前,没有生成 css,浏览器就没办法展示样式

  • 这里就可以用到 npm script 的钩子机制 preserve 在启动任务之前,让 build 先去工作
1
2
3
4
5
{
"scripts": {
"preserve": "yarn build"
},
}

如果给 build 增加 --watch 参数,scss 会阻塞命令行等待文件变化,就会导致 browser-sync 没办法直接去工作

  • 这里就需要用 npm-run-all 同时执行多个命令

    1
    yarn add npm-run-all --dev
1
2
3
4
5
{
"scripts": {
"start": "run-p build serve"
},
}

常用工具

  • 严格来说 Webpack 是模块打包工具
  • Grunt、Gulp、FIS 是自动化构建工具

常用自动化构建工具

  • Grunt 老牌构建工具,插件生态完善,但是它的工作过程是基于临时文件实现的,所以构建速度较慢(Scss 编译后就会将结果写入到临时文件,下一个插件读取这个临时文件进行操作,环节越多读取文件次数越多)
  • Gulp 基于内存实现的,对文件的处理都是在内存中实现,速度快一些,默认同时支持多个任务,目前比较流行
  • FIS 百度团队研发,把项目中最典型需求都集成到内部,大而全

Grunt

基本使用

1
2
yarn init –yes
yarn add grunt

新建 gruntfile.js

  • Grunt 的入口文件,用于定义一些需要 Grunt 自动执行的任务
  • 需要导出一个函数,该函数接收一个 grunt 对象,内部提供一些创建任务可以用到的 API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = grunt => {
grunt.registerTask('foo', '任务描述', () => {
console.log('foo')
})
grunt.registerTask('bar', () => {
console.log('bar')
})
// 第二个参数可以指定此任务的映射任务
// 这样执行 default 就相当于执行对应的任务
// 这里映射的任务会按顺序依次执行,不会同步执行
grunt.registerTask('default', ['foo', 'bar'])
// 由于函数体中需要使用 this,所以这里不能使用箭头函数
grunt.registerTask('async-task', function () {
const done = this.async()
setTimeout(() => {
console.log('async task working~')
done()
}, 1000)
})
}

标记任务失败

  • 任务函数执行过程中如果返回 false,则意味着此任务执行失败

  • 如果一个任务列表中的某个任务执行失败,则后续任务默认不会运行

    除非 grunt 运行时指定 --force 参数强制执行

  • 异步函数中标记当前任务执行失败的方式是为回调函数指定一个 false 的实参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = grunt => {
grunt.registerTask('bad', () => {
console.log('bad working~')
return false
})
grunt.registerTask('foo', () => {
console.log('foo working~')
})
grunt.registerTask('bar', () => {
console.log('bar working~')
})
grunt.registerTask('default', ['foo', 'bad', 'bar'])
grunt.registerTask('bad-async', function () {
const done = this.async()
setTimeout(() => {
console.log('async task working~')
done(false)
}, 1000)
})
}

配置选项方法

init 配置,用 grunt.config 获取属性值

1
2
3
4
5
6
7
8
9
10
module.exports = grunt => {
grunt.initConfig({
foo: {
bar: 123,
},
})
grunt.registerTask('foo', () => {
console.log(grunt.config('foo.bar'))
})
}

多目标任务

init 配置,通过 this 拿到执行的名称和数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = grunt => {
// 多目标模式,可以让任务根据配置形成多个子任务
grunt.initConfig({
build: {
options: {
msg: 'task options',
},
foo: {
options: {
msg: 'foo target options',
},
},
bar: '456',
},
})
grunt.registerMultiTask('build', function () {
console.log(this.options())
console.log(`target:${this.target},data:${this.data}`)
})
}

Grunt 插件

安装 grunt-contrib-clean

1
yarn add grunt-contrib-clean --dev

将 temp 目录下所有文件都清除

1
2
3
4
5
6
7
8
9
module.exports = grunt => {
grunt.initConfig({
clean: {
temp: 'temp/**',
},
})

grunt.loadNpmTasks('grunt-contrib-clean')
}

安装 grunt-sasssass

1
yarn add grunt-sass sass --dev

安装 grunt-babalbabel

1
yarn add grunt-babel @babel/core @babel/preset-env --dev

随着 gruntfile.js 越来越复杂,loadNpmTasks 操作也会越来越多,可以使用 load-grunt-tasks

1
yarn add load-grunt-tasks --dev

当文件修改完,需要自动去编译,这时需要另外一个插件 grunt-contrib-watch

1
yarn add grunt-contrib-watch --dev

Grunt 使用插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')

module.exports = grunt => {
grunt.initConfig({
sass: {
options: {
sourceMap: true,
implementation: sass,
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss',
},
},
},
babel: {
options: {
sourceMap: true,
presets: ['@babel/preset-env'],
},
main: {
files: {
'dist/js/app.js': 'src/js/app.js',
},
},
},
watch: {
js: {
files: ['src/js/*.js'],
tasks: ['babel'],
},
css: {
files: ['src/scss/*.scss'],
tasks: ['sass'],
},
},
})

loadGruntTasks(grunt) // 自动加载所有 grunt 插件中的任务
grunt.registerTask('default', ['sass', 'babel', 'watch'])
}

Gulp

基本使用

Gulp 核心特点:高效易用

  • 安装 Gulp 同时会自动安装一个叫 gulpcli 模块
1
2
yarn init --yes
yarn add gulp --dev

创建 gulpfile.js 入口,导出一个 foo 函数

1
2
3
exports.foo = () => {
console.log('foo task working~')
}

执行 yarn gulp foo,发现有报错

  • gulp 取消了同步代码模式,约定每一个任务都是异步的,当任务执行完需要通过回调函数标记任务执行完毕
1
2
3
[11:04:54] The following tasks did not complete: foo
[11:04:54] Did you forget to signal async completion?
error Command failed with exit code 1.

gulp 4.0 以后的保留了 gulp.task API

  • gulp 4.0 以后推荐使用导出函数成员方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
exports.foo = done => {
console.log('foo task working~')
done() // 标识任务执行完成
}

// default 是默认任务
exports.default = done => {
console.log('default task working~')
done()
}

// v4.0 之前需要通过 gulp.task() 方法注册任务
const gulp = require('gulp')
gulp.task('bar', done => {
console.log('bar task working~')
done()
})

组合任务

通过 seriesparallel 组合多个任务

  • 编译 CSS、JS 任务互不干扰可以并行处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const { series, parallel } = require('gulp')

const task1 = done => {
setTimeout(() => {
console.log('task1 working~')
done()
}, 1000)
}

const task2 = done => {
setTimeout(() => {
console.log('task2 working~')
done()
}, 1000)
}

const task3 = done => {
setTimeout(() => {
console.log('task3 working~')
done()
}, 1000)
}

// 让多个任务按照顺序依次执行
exports.foo = series(task1, task2, task3)
// 让多个任务同时执行
exports.bar = parallel(task1, task2, task3)

异步任务

  1. 回调函数
  2. Promsie、async 和 await(Node 8 以上的版本)
  3. Stram 方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const fs = require('fs')

exports.callback = done => {
console.log('callback task')
done()
}
exports.callback_error = done => {
console.log('callback task')
done(new Error('task failed'))
}

exports.promise = () => {
console.log('promise task')
return Promise.resolve()
}
exports.promise_error = () => {
console.log('promise task')
return Promise.reject(new Error('task failed'))
}

const timeout = time => {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
exports.async = async () => {
await timeout(1000)
console.log('async task')
}

exports.stream = () => {
const readStream = fs.createReadStream('package.json')
const writeStream = fs.createWriteStream('temp.txt')
readStream.pipe(writeStream) // pipe导入写入流中
// gulp 只是注册了 end 事件去结束
/* readStream.on('end', () => {
done()
}) */
return readStream
}

核心工作原理

  • The streaming build system

stream 的 Transform 类型可以创建文件转换流对象,里面需要指定 transform 属性(转化流核心转换过程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const fs = require('fs')
const { Transform } = require('stream')

exports.default = () => {
// 文件读取流
const readStream = fs.createReadStream('normalize.css')
// 文件写入流
const writeStream = fs.createWriteStream('normalize.min.css')
// 文件转换流
const transformStream = new Transform({
// 核心转换过程
transform: (chunk, encoding, callback) => {
const input = chunk.toString()
const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')
callback(null, output)
},
})

return readStream
.pipe(transformStream) // 转换
.pipe(writeStream) // 写入
}

Gulp核心工作原理

文件操作 API

1
2
yarn add gulp-clean-css --dev
yarn add gulp-rename --dev

通过 src 去 pipe 到插件转换流,再去 pipe 到写入流的过程就是使用 gulp 常规过程

1
2
3
4
5
6
7
8
9
10
const { src, dest } = require('gulp')
const cleanCSS = require('gulp-clean-css')
const rename = require('gulp-rename')

exports.default = () => {
return src('src/*.css')
.pipe(cleanCSS())
.pipe(rename({ extname: '.min.css' }))
.pipe(dest('dist'))
}

Gulp 案例

样式编译

下载代码

1
git clone https://github.com/zec/zec-gulp-demo.git

安装 gulpgulp-sass

  • 安装 gulp-sass 会安装 node-sass(是 C++ 模块),内部会有对 C++ 程序集的依赖,二进制包需要国外站点去下载,有时会下载不下来,可以配置淘宝镜像源下载
1
2
yarn add gulp --dev
yarn add gulp-sass --dev
1
2
3
4
5
6
7
8
9
10
11
12
const { src, dest } = require('gulp')
const sass = require('gulp-sass')

const style = () => {
return src(`src/assets/styles/*.scss`, { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}

module.exports = {
style,
}

脚本编译

安装 gulp-babel@babel/core@babel/preset-env

1
2
yarn add gulp-babel --dev
yarn add @babel/core @babel/preset-env --dev
1
2
3
4
5
6
7
8
9
10
11
12
const { src, dest } = require('gulp')
const babel = require('gulp-babel')

const script = () => {
return src(`src/assets/scripts/*.js`, { base: 'src' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))
}

module.exports = {
script,
}

页面模板编译

安装 gulp-swig

1
yarn add gulp-swig --dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const { src, dest, parallel } = require('gulp')
const swig = require('gulp-swig')

const data = {
menus: [
{
name: 'Home',
icon: 'aperture',
link: 'index.html',
},
{
name: 'Features',
link: 'features.html',
},
{
name: 'About',
link: 'about.html',
},
{
name: 'Contact',
link: '#',
children: [
{
name: 'Twitter',
link: 'https://twitter.com/w_zce',
},
{
name: 'About',
link: 'https://weibo.com/zceme',
},
{
name: 'divider',
},
{
name: 'About',
link: 'https://github.com/zce',
},
],
},
],
pkg: require('./package.json'),
date: new Date(),
}

const page = () => {
return src(`src/**/*.html`, { base: 'src' }).pipe(swig({ data })).pipe(dest('dist'))
}

const compile = parallel(style, script, page)

module.exports = {
compile
}

图片和字体文件的转换

安装 gulp-imagemin

  • 内部依赖的模块也是通过 C++ 模块,需要下载二进制程序集(大部分 GitHub 下载),但是这个没办法配置镜像
1
yarn add gulp-imagemin --dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const { src, dest, parallel } = require('gulp')
const imagemin = require('gulp-imagemin')

const image = () => {
return src(`src/assets/images/**`, { base: 'src' }).pipe(imagemin()).pipe(dest('dist'))
}

const font = () => {
return src(`src/assets/fonts/**`, { base: 'src' }).pipe(imagemin()).pipe(dest('dist'))
}

const compile = parallel(style, script, page, image, font)

module.exports = {
compile
}

其它文件及文件清除

安装 del

1
yarn add del --dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const { src, dest, parallel, series } = require('gulp')
const del = require('del')

const clean = () => {
return del(['dist'])
}

const compile = parallel(style, script, page, image, font)

const build = series(clean, parallel(compile, extra))

module.exports = {
build,
}

自动加载插件

安装 gulp-load-plugins

1
yarn add gulp-load-plugins --dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const { src, dest, parallel, series } = require('gulp')
const del = require('del')
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()

const data = {...}

const clean = () => {
return del(['dist'])
}

const style = () => {
return src(`src/assets/styles/*.scss`, { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}

const script = () => {
return src(`src/assets/scripts/*.js`, { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))
}

const page = () => {
return src(`src/**/*.html`, { base: 'src' }).pipe(plugins.swig({ data })).pipe(dest('dist'))
}

const image = () => {
return src(`src/assets/images/**`, { base: 'src' }).pipe(plugins.imagemin()).pipe(dest('dist'))
}

const font = () => {
return src(`src/assets/fonts/**`, { base: 'src' }).pipe(plugins.imagemin()).pipe(dest('dist'))
}

const extra = () => {
return src(`public/**`, { base: 'public' }).pipe(dest('dist'))
}

const compile = parallel(style, script, page, image, font)

const build = series(clean, parallel(compile, extra))

module.exports = {
build,
}

热更新开发服务器

安装 browser-sync

1
yarn add browser-sync --dev
  • 编译过程中并没有处理 node_modules 模块下面的拷贝

    可以给 browser-sync 增加一个单独的路由(routes),这个路由优先级比 baseDir

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const browserSync = require('browser-sync')

const bs = browserSync.create()

const serve = () => {
bs.init({
notify: false,
port: 2080,
// open: false,
files: 'dist/**',
server: {
baseDir: 'dist',
routes: {
'/node_modules': 'node_modules',
},
},
})
}

module.exports = {
serve,
}

监听变化及构建优化

  • stylescriptpage 文件改变可以 .pipe(bs.reload({ stream: true }))

    这样 files: 'dist/**' 就不需要了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const { src, dest, parallel, series, watch } = require('gulp')

const style = () => {
return src(`src/assets/styles/*.scss`, { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
.pipe(bs.reload({ stream: true }))
}

const script = () => {
return src(`src/assets/scripts/*.js`, { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))
.pipe(bs.reload({ stream: true }))
}

const page = () => {
return src(`src/**/*.html`, { base: 'src' })
.pipe(plugins.swig({ data }))
.pipe(dest('dist'))
.pipe(bs.reload({ stream: true }))
}

const serve = () => {
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
// watch('src/assets/images/**', image)
// watch('src/assets/fonts/**', font)
// watch('public/**', extra)
watch(['src/assets/images/**', 'src/assets/fonts/**', 'public/**'], bs.reload)

bs.init({
notify: false,
port: 2080,
// open: false,
// files: 'dist/**',
server: {
baseDir: ['dist', 'src', 'public'],
routes: {
'/node_modules': 'node_modules',
},
},
})
}

const compile = parallel(style, script, page)
const build = series(clean, parallel(compile, image, font, extra))
const develop = series(compile, serve)

useref 文件引用处理

有些文件依赖的是 node_modules 下的文件,这里并没有将其拷贝到 dist 目录,如果上线肯定找不到,本地能找到是因为做了路由映射

1
yarn add gulp-useref --dev

构建注释

1
2
3
4
5
6
<!-- build:css assets/styles/vendor.css -->
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
<!-- endbuild -->
<!-- build:css assets/styles/main.css -->
<link rel="stylesheet" href="assets/styles/main.css">
<!-- endbuild -->
1
2
3
4
5
const useref = () => {
return src('dist/*.html', { base: 'dist' })
.pipe(plugins.useref({ searchPath: ['dist', '.'] }))
.pipe(dest('dist'))
}

分别压缩 HTML、CSS、JS

  • 另外还需要判断对不同文件的操作,这个时候就需要一个额外的插件去判断
1
2
yarn add gulp-htmlmin gulp-uglify gulp-clean-css --dev
yarn add gulp-if --dev

操作时会发现 main.css 没有内容,这时因为文件读写冲突,一边读一边写

  • 可以把最终转换的结果放在其他目录中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const useref = () => {
return src('dist/*.html', { base: 'dist' })
.pipe(plugins.useref({ searchPath: ['dist', '.'] }))
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(
plugins.if(
/\.html$/,
plugins.htmlmin({
collapseWhitespace: true,
minifyCSS: true,
minifyJS: true,
})
)
)
.pipe(dest('release'))
}

重新规划构建过程

useref 打破了我们的目录结构

  • 打包上线是 dist 目录,而 useref 把目录改成了 release

增加临时目录 temp

  1. scriptstylepage 放到 temp 目录
  2. imagefontextra 不需要改,这三个只需要 build 时候去做,只有被 useref 影响才需要
  3. serve 里的 baseDir 需要改为 temp
  4. useref src 从 temp 里取,把结果放到 dist 目录里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
const { src, dest, parallel, series, watch } = require('gulp')
const del = require('del')
const browserSync = require('browser-sync')
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const bs = browserSync.create()

const clean = () => {
return del(['dist', 'temp'])
}

const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('temp'))
.pipe(bs.reload({ stream: true }))
}

const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('temp'))
.pipe(bs.reload({ stream: true }))
}

const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(plugins.swig({ data, defaults: { cache: false } })) // 防止模板缓存导致页面不能及时更新
.pipe(dest('temp'))
.pipe(bs.reload({ stream: true }))
}

const image = () => {
return src('src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}

const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}

const extra = () => {
return src('public/**', { base: 'public' })
.pipe(dest('dist'))
}

const serve = () => {
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
// watch('src/assets/images/**', image)
// watch('src/assets/fonts/**', font)
// watch('public/**', extra)
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload)

bs.init({
notify: false,
port: 2080,
// open: false,
// files: 'dist/**',
server: {
baseDir: ['temp', 'src', 'public'],
routes: {
'/node_modules': 'node_modules'
}
}
})
}

const useref = () => {
return src('temp/*.html', { base: 'temp' })
.pipe(plugins.useref({ searchPath: ['temp', '.'] }))
// html js css
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
minifyCSS: true,
minifyJS: true
})))
.pipe(dest('dist'))
}

const compile = parallel(style, script, page)
const build = series(
clean,
parallel(
series(compile, useref),
image,
font,
extra
)
)
const develop = series(compile, serve)

module.exports = {
clean,
build,
develop
}

构建完这套系统,如果你不想写文档说明,导出几个常用即可,cleanbuilddevelop

  • 并在 package.json 中增加 script 命令
1
2
3
4
5
"scripts": {
"clean": "gulp clean",
"build": "gulp build",
"develop": "gulp develop"
},

封装工作流

提取多个项目共同的自动化构建过程(Don’t repeat yourself)

gulp自动化工作流构建

提取 gulpfile

zce-gulp-demo 这里叫 A,zce-pages 这里叫 B

  • 在命令行输入 code . -a 在同一窗口打开这个项目

把 A 项目工作流提取到 B 项目中,操作步骤如下:

  1. 把 A 的 gulpfile.js 内容复制到 B 的 lib/index.js
  2. 把 A 的 package.json 中的 devDependencies 复制到 B 中 dependencies
  3. 把 A 的 package.json 中的 devDependencies 全部删除,并把 node_modules 全部删除
  4. 把 B link 到全局,切换到 A 输入命令 yarn link zce-pages,B 项目会通过软链接形式链过来
  5. 在 B 中新建 gulpfile.js 新增 module.exports = require('zce-pages') 内容
  6. 执行 yarn build

执行 yarn build 我这边报错了,是因为 gulp-sass 没有下载下来

1
2
3
4
5
Error in plugin "gulp-sass"
Message:

gulp-sass no longer has a default Sass compiler; please set one yourself.
Both the "sass" and "node-sass" packages are permitted.

解决:安装 sass

1
yarn add sass

修改 lib/index.js

1
2
3
4
5
6
7
8
const sass = require('gulp-sass')(require('sass'))

const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('temp'))
.pipe(bs.reload({ stream: true }))
}

抽象路径配置

对于代码里写死的路径,如 sass 的路径,这样不是很灵活,我们可以抽象一下形成配置

  • cwd 返回命令行工作目录,然后拿到工作文件 pages.config.js

    require 不存在的目录会报错,所以这里用 try...catch... 包裹一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const cwd = process.cwd()
let config = {
build: {
src: 'src',
dist: 'dist',
temp: 'temp',
public: 'public',
paths: {
styles: 'assets/styles/*.scss',
scripts: 'assets/scripts/*.js',
pages: '*.html',
images: 'assets/images/**',
fonts: 'assets/fonts/**'
}
}
}

try {
const loadConfig = require(`${cwd}/pages.config.js`)
config = Object.assign({}, config, loadConfig)
} catch (e) {}

const style = () => {
return src(config.build.paths.styles, { base: config.build.src, cwd: config.build.src })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest(config.build.temp))
.pipe(bs.reload({ stream: true }))
}

包装 Gulp Cli

gulpfile.js 里只是写了个 module.exports = require('zce-pages'),其实有点冗余,我们希望没有 gulpfile.js 也能正常工作

  • 这里需要制定一下工作目录为当前
1
yarn gulp build --gulpfile ./node_modules/zce-pages/lib/index.js --cwd .

不过这么多参数就很复杂了,可以集成 cli

  1. 创建 bin/zce-pages.js 作为 cli 执行入口

  2. package.json 中增加 bin 字段

    1
    "bin": "bin/zce-pages.js",

修改 zce-pages.js

  • process.argv 是一个数组
  • require 是载入这个模块,require.resolve 是找到这个模块所在的路径
1
2
3
4
5
6
7
8
#!/usr/bin/env node

process.argv.push('--cwd')
process.argv.push(process.cwd())
process.argv.push('--gulpfile')
process.argv.push(require.resolve('..'))

require('gulp/bin/gulp')

代码提交完,就可以使用 publish 提交 git

1
yarn publish --registry https://registry.yarnpkg.com

同步时可能会有问题,因为你发布的是 npm 源,但是你是从淘宝源下载,可能会存在同步问题,可以进入淘宝镜像搜一下你 publish 的包,然后更新

总结

  1. 新建项目如果想要使用工作流,直接运行 package.json 中的命令即可

  2. 创建的 cli 是 node_modules/bin/zce-pages.cmd,这个文件中是通过 node 去执行 zce-pages/bin/zce-pages.js

  3. node_modules 找到 zce-pages/bin/zce-pages.js,里面就是告诉 gulp 工作时的工作目录和 gulpfile 的工作目录

    之后执行 gulp/bin/gulp 就可以执行 gulpifle 了,完成整个工作流过程

FIS

高度集成,只需要简单配置

1
yarn global add fis3

relase 解决的资源定位

1
fis3 realse -d output

新建 fis-conf.js

  • 编译与压缩

    安装 yarn add fis-parser-node-sassyarn add fis-parser-babel-6.x

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fis.match('*.{js,scss,png}', {
release: '/assets/$0',
})

fis.match('**/*.scss', {
rExt: '.css',
parser: fis.plugin('node-sass'),
optimizer: fis.plugin('clean-css'),
})

fis.match('**/*.js', {
parser: fis.plugin('babel-6.x'),
optimizer: fis.plugin('uglify-js'),
})

查看转换过程中转换的配置

1
fis3 inspect