0%

基于ES6利用Gulp编译BootStrap-Sass源码

BootStrap

BootStrap 是一个前端CSS框架,它提供了一些便捷的组件方便我们快速构建前端页面,目前已经到了版本4,版本4是用 Sass 编写的,版本3是由 Less 编写的,后来增加了 Sass 版本。这说明了什么?BootStrap 已经向 Sass靠近了,个人感觉 Sass 比 Less 更为强大,具有更丰富的语法功能。 所以,Sass 将会成为比 Less 更为主流的语言。 目前常用的 BootStrap 版本是3,在官网也提供了相关 Sass 版本的下载。 在此提供官网下载链接和 Sass 项目 GitHub 地址。 BootStrap BootStrap-Sass 在 BootStrap 的下载版本中,可以看到有三个。一个是编译好的 JS,CSS 文件,可以直接拿来用,方便快捷就可以下载这个来用。第二个是 Less 源码版本,你可以自己定义 Less 文件,在项目基础上继续用 Less 开发,编译成需要的 CSS 文件。第三个是后来新增的 Sass 版本,本节就以它为例来说明利用 Gulp 编译 BootStrap-Sass 的过程,目的一在于熟悉 Gulp 自动化编译 Sass 的流程,目的二在于了解前端自动化的工作原理。

Gulp

说完 BootStrap,我们再说下 Gulp,基于 Node.js。它干嘛的呢?就是一个前端自动化工具,什么用处?比如它可以编译 Less,Sass 生成到指定目录文件为 CSS,生成对应 map 文件,可以生成 JavaScript 的 map 文件,自动更新 html 中的 JS,CSS 引用路径,合并多个 JS,CSS 文件为统一整体,最小化压缩 JS,CSS 文件等等,最终目的呢?自动化替代重复劳动,提高效率。 说到 Gulp,就不得不提到它的竞争对手 Grunt,它具有和 Gulp 几乎一样的功能,然而 Grunt 有几个缺点,比如插件职责不明确,产生大量临时文件,语法繁琐等等。相比之下,Gulp插件职责明确,基于流式,不会产生临时文件,语法简单。冲着这几点,果断选择 Gulp。 利用 Gulp,我们就可以在项目中定义一个 gulpfile.babel.js 里面写入需要执行的任务,命令行执行 gulp 命令就可以完成自动化,一些重复的无聊的工作就不要你来做了。 Gulp中文网

ES6

说完 Gulp,然后就属 ES6 了,它是 ECMAScript 6 的简称,是 JavaScript 的一个新的版本类型,由于是 2015年发布的,所以也可以叫它 ES2015。我们之前编的 JavaScript 大多数是基于ES5或之前的版本,在 ES6 的基础上增加了许多新的语法特性,比如 Class,let,const 等等。 在 ES5 中,Gulp 的执行文件叫做 gulpfile.js,到了 ES6中,它就叫做 gulpfile.babel.js 了,多了一个 badel,那 babel 又是什么? 关于 ES6 的新特性预览可以看 ES6

Babel

Babel 其实是一个 JavaScript 编译器,支持 ES6,你可以用新型的 ES6 语法来编写你的 JavaScript,Babel 会为你生成对应的 ES5 的 JavaScript。乍看之下并没有什么关系,所以在这里你可以把 babel 看作 ES6 的代名词,在 Gulp 中,新型的 ES6 语法的 JavaScript 的 gulpfile 名字命名为 gulpfile.babel.js。 Babel

NPM

有一点 Node.js 基础的想必都知道这一个东西吧,Node Package Manager,Node.js 包管理器,利用它你可以安装 Node.js 的相关包,其中包括 Gulp。可以全局安装,加个 -g 参数,可以局部安装,需要路径下有个 package.json。 NPM怎样安装?安装了 Node.js 就好了。 Node.js 如果觉得速度慢,可以安装 CNPM,镜像源来源非国外,是淘宝的一个镜像源,速度快。 CMPM

Bower

在这里还需要用到一个工具 bower,类似 NPM,算是前端的一些组件管理工具,一些前端库比如 jquery,bootstrap 等等都可以用 bower 这个工具来下载,需要在根目录下建立一个 bower.json 和 .bowerrc 文件。利用 bower 我们就可以方便地管理前端的工具包了,不用我们去手动下载复制粘贴之类的。

WebStorm

在这再安利一个 IDE 吧,WebStorm,JetBrains公司出的一款强大又良心的编写前端的 IDE,支持各种插件,具有强大的语法提示,支持 JsHint 等代码检查,集成了终端,Git 等等强大的工具,Web 开发不二选择,推荐最新版本。 WebStorm

准备工作

扯完以上东西(其实还有好多没有扯完),让我们进入正题吧,正题是什么?哦没错,那就是

基于 ES6 语法使用 Gulp 编写 gulpfile.babel.js 来编译 BootStrap-Sass 源码。

下面是一些准备工作,没有做好的小伙伴请按照步骤一一完成。

安装 Node.js 和 NPM

从 Node 的官网下载 Node 并安装,安装流程不详细说明,安装完成之后 NPM 随之就会安装成功。 命令行下输入 npm 检查一下是否可以正常运行。

安装 Gulp

1
npm install -g gulp

加入 -g 参数是全局安装,安装完成之后你可以在任意位置使用命令。

安装 Bower

1
npm install -g bower

依然是全局安装 bower。

下载 BootStrap-Sass

可直接进入 BootStrap 页面点击第三个下载 Sass 源码。 也可以用 Git 将 BootStrap-Sass 的项目 clone 下来。

安装 WebStorm

推荐使用 WebStorm,可以开启 JsHint 等检测工具,具有强大的代码提示功能,不过不使用也没关系。 在你的 IDE 打开下载的项目,

新建 gulpfile.babel.js

gulpfile.babel.js 是基于 ES6 的 Gulp 处理文件,新建它,稍后所有的工作都在这里完成。

新建 .babelrc

新建 .babelrc 文件,内容

1
2
3
4
5
{
"presets": [
"es2015"
]
}

这是指定 gulp 使用最新标准的 JavaScript 进行编译。

新建 .bowerrc

新建 .bowerrc 文件,这是 bower 的配置文件,可以指定路径等相关配置,内容为

1
2
3
{
"directory": "bower_components"
}

这是指定 bower 工具下载前端组件时会默认下载到这个文件夹中。

修改 bower.json

可以精简 bower.json 文件,比如修改名称,删去 main,ignore 配置等。 比如精简成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "bootstrap-sass-demo",
"authors": [
"Germey"
],
"description": "bootstrap-sass is a Sass-powered version of Bootstrap, ready to drop right into your Sass powered applications.",
"moduleType": "globals",
"keywords": [
"twbs",
"bootstrap",
"sass"
],
"license": "MIT",
"dependencies": {
"jquery": ">= 1.9.0"
}
}

修改 package.json 在进行 Gulp 配置文件编写之前,首先需要引入一些 Node.js 开发包,比如 babel,gulp,wiredep等等。 修改 devDependencies 为

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
"devDependencies": {
"babel-core": "^6.4.0",
"babel-preset-es2015": "^6.3.13",
"babel-register": "^6.9.0",
"browser-sync": "^2.2.1",
"del": "^1.1.1",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.0.1",
"gulp-babel": "^6.1.1",
"gulp-cache": "^0.2.8",
"gulp-cssnano": "^2.0.0",
"gulp-eslint": "^0.13.2",
"gulp-htmlmin": "^1.3.0",
"gulp-if": "^1.2.5",
"gulp-imagemin": "^2.2.1",
"gulp-load-plugins": "^0.10.0",
"gulp-plumber": "^1.0.1",
"gulp-sass": "^2.0.0",
"gulp-size": "^1.2.1",
"gulp-sourcemaps": "^1.5.0",
"gulp-uglify": "^1.1.0",
"gulp-useref": "^3.0.0",
"main-bower-files": "^2.5.0",
"wiredep": "^2.2.2"
}

执行

1
npm install

安装所需要的库。 如此一来,所有的准备工作就差不多了。

实战

引入类库

首先引入一些必须的类库

1
2
3
4
5
import gulp from 'gulp';
import gulpLoadPlugins from 'gulp-load-plugins';
import browserSync from 'browser-sync'
import del from 'del';
import {stream as wiredep} from 'wiredep';

gulp 自不必多说,是 gulp 必须的核心类库。 gulp-load-plugins 是加载 gulp 插件的类库,我们知道 gulp 插件非常丰富,如果要一个个引入的话,需要写很多很多条 import 语句,引入了这个插件之后,调用时只需要加 点(.) + 插件名称 那就可以使用了。 browser-sync 是浏览器同步工具,如果有代码更新,浏览器会自动刷新更新资源。 del 是删除资源的工具包。 wiredep 是从 bower 同步到 html 中资源引用的插件,bower 中定义了依赖包,有了它,这些包的引用比如 js,css 就可以直接自动生成到 html 文件中。 接着初始化一些变量。

1
2
const $ = gulpLoadPlugins();
const reload = browserSync.reload;

将加载插件的插件初始化为 $ 符号,然后初始化 reload 等变量。

Sass 编译

下载好 Sass 源码之后,打开 assets/stylesheets 目录,可以看到 BootStrap 的 Sass 源代码。不过发现文件名都是 _ 开头的,这种类型的文件是不能被编译生成的,所以新建一个 bootstrap.sass 文件,内容为

1
@import "_bootstrap";

最后生成的目录结构如下

1
2
3
4
5
|_____bootstrap-compass.scss
|_____bootstrap-mincer.scss
|_____bootstrap-sprockets.scss
|_____bootstrap.scss
|____bootstrap

接下来我们只需要编译 bootstrap.scss 即可。 定义一个路径配置

1
2
3
4
const styles = {
'in': 'assets/stylesheets/**/*.scss',
'tmp': '.tmp/css',
};

包含 in 和 tmp 目录,in 代表 Sass 源文件地址,tmp 代表生成的编译后的 CSS 目录。 接下来最重要的,指定一个 Gulp Task

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gulp.task('styles', () => {
return gulp.src(styles.in)
.pipe($.plumber())
.pipe($.sourcemaps.init())
.pipe($.sass.sync({
outputStyle: 'expanded',
precision: 10,
includePaths: ['.']
}).on('error', $.sass.logError))
.pipe($.autoprefixer({browsers: ['> 1%', 'last 2 versions', 'Firefox ESR']}))
.pipe($.sourcemaps.write())
.pipe(gulp.dest(styles.tmp))
.pipe(reload({stream: true}));
});

task 是 gulp 的一个核心方法,定义了 styles 这个 task 之后,就可以执行

1
gulp styles

就可以完成以上定义的任务。 首先利用 gulp.src 引入了需要编译的 Sass 文件,然后利用一系列 pipe 流式管道来指定一系列处理任务。 plumber 是一个错误处理插件,当出现错误时,不会立即卡主,而是进入 plumber,防止程序运行终止。 sourcemaps 是用来生成映射文件的一个插件,map 文件记录了从 Sass 编译成 CSS 的过程中,每一行的 Sass 代码对应哪一行的 CSS 代码。 sass 是核心的编译 Sass 的插件,指定了输出格式 expanded,precision 指定了当输出十进制数字时,使用多少位的精度,然后指定了路径和错误日志。 autoprefixer 是一个以友好方式处理浏览器前缀的插件,比如一些 CSS 的定义会出现 -webkit- 等等,此插件是用来处理浏览器前缀的。

Autoprefixer默认将支持主流浏览器最近2个版本,这点类似Google。不过你可以在自己的项目中通过名称或者模式进行选择: 主流浏览器最近2个版本用“last 2 versions”; 全球统计有超过1%的使用率使用“>1%”; 仅新版本用“ff>20”或”ff>=20”. 然后Autoprefixer计算哪些前缀是需要的,哪些是已经过期的。

dest 是输出编译后的文件,指定输出路径。 reload 是同步浏览器资源的方法。 定义好如上内容之后,命令行输入

1
gulp styles

就会发现出现了 .tmp 目录,里面有 css/bootstrap.css

JavaScript 处理

同理,定义一个 task,用来处理 JavaScript

1
2
3
4
5
6
7
8
9
gulp.task('scripts', () => {
return gulp.src(scripts.in)
.pipe($.plumber())
.pipe($.sourcemaps.init())
.pipe($.babel())
.pipe($.sourcemaps.write('.'))
.pipe(gulp.dest(scripts.tmp))
.pipe(reload({stream: true}));
});

相比之下,此处多了一个 babel 插件。 babel 是基于 ES6 标准的一个 JavaScript 插件,它可以对 ES6 版本的代码进行转换,转换成 ES5 标准,避免出现出现 ES6 不兼容问题。 在此处还需要 scripts 的路径定义

1
2
3
4
5
const scripts = {
'in': 'assets/javascripts/**/*.js',
'tmp': '.tmp/js',
'out': 'dist/js'
};

定义完成之后,执行

1
gulp scripts

就可以完成 JavaScript 的转换。 另外还有一个专门负责代码风格转换的 task,使用了 eslint 这个插件

1
2
3
4
5
6
7
8
9
const lint = {
'in': 'assets/javascripts/**/*.js'
};
gulp.task('lint', () => {
return gulp.src(lint.in)
.pipe(reload({stream: true, once: true}))
.pipe($.eslint.format())
.pipe($.if(!browserSync.active, $.eslint.failAfterError()));
});

执行

1
gulp lint

之后,就可以进行代码风格的标准化。

HTML处理

我们可以发现,在前面的输出路径都是 .tmp 临时目录,后面还会有一个目录是 dist 目录,试想一下,如果我们编译了 BootStrap 而在 HTML 中没有引用,那编译来还有必要吗? 所以说,.tmp 作为临时目录,它可以存放被编译后的文件,但是不一定会被引用。被真正引用的文件才是真正有用的文件,我们将它放到 dist 目录。 所以接下来的 HTML 处理就是检查一下有哪些 CSS 和 JS 被引用了,可以将它们合并,然后将新的文件放到 dist 并更新它的引用路径。

1
2
3
4
5
6
7
8
9
10
11
12
const html = {
'in': 'assets/*.html',
'out': 'dist'
};
gulp.task('html', ['styles', 'scripts'], () => {
return gulp.src(html.in)
.pipe($.useref({searchPath: ['.tmp', 'assets', '.']}))
.pipe($.if('*.js', $.uglify()))
.pipe($.if('*.css', $.cssnano()))
.pipe($.if('*.html', $.htmlmin({collapseWhitespace: true})))
.pipe(gulp.dest(html.out));
});

在这里定义了一个 task 叫做 html,第二个参数是 styles 和 scripts 组成的数组,意思是在执行这个 task 之前,首先要执行这两个任务。 在处理时用到了 useref 这个插件,它可以检测 HTML 中引用的 CSS 和 JS,可以执行合并和压缩,然后更新新的路径。 这个插件的作用如上所述。 比如 HTML 当前内容是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome</title>
<!-- bower:css -->
<!-- endbower -->
<!-- build:css css/combined.css -->
<link href="../.tmp/css/bootstrap.css" rel="stylesheet">
<!-- endbuild -->
</head>
<body>
<h4>Hello This is a Gulp Sass Demo Configured by Germey.</h4>
</body>
<!-- bower:js -->
<!-- endbower -->
<!-- build:js js/combined.js -->
<script src="javascripts/bootstrap.js"></script>
<script src="javascripts/bootstrap-sprockets.js"></script>
<!-- endbuild -->
</html>

可以看到

1
2
3
<!-- build:css css/combined.css -->
<link href="../.tmp/css/bootstrap.css" rel="stylesheet">
<!-- endbuild -->

这里引用了 .tmp 目录下的 bootstrap.css,然后在外面用注释的形式定义了构建的路径和文件名。 那么执行这个任务之后,它便会将当前引用的 .tmp 目录下的 bootstrap.css 处理并输出为 combined.css,然后新生成的 HTML 文件的引用路径也相应改为 combined.css JS 也是同理

1
2
3
4
<!-- build:js js/combined.js -->
<script src="javascripts/bootstrap.js"></script>
<script src="javascripts/bootstrap-sprockets.js"></script>
<!-- endbuild -->

在此处是将两个文件处理合并为 combined.js 执行

1
gulp html

后,会新生成一个 HTML 文件到 dist 目录,内容为

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome</title><!-- bower:css --><!-- endbower -->
<link rel="stylesheet" href="css/combined.css">
</head>
<body><h4>Hello This is a Gulp Sass Demo Configured by Germey.</h4></body><!-- bower:js --><!-- endbower -->
<script src="js/combined.js"></script>
</html>

dist 的目录结构为

1
2
3
4
5
|____css
| |____combined.css
|____index.html
|____js
| |____combined.js

以上为利用 useref 插件进行 HTML 处理的过程。

图片压缩处理

接下来是对图片字体及其他格式文件的处理。 图片的主要处理是进行压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
const images = {
'in': 'assets/images/**/*',
'out': 'dist/images'
};
gulp.task('images', () => {
return gulp.src(images.in)
.pipe($.imagemin({
progressive: true,
interlaced: true,
svgoPlugins: [{cleanupIDs: false}]
}))
.pipe(gulp.dest(images.out));
});

定义好了 images 的输入和输出路径之后,定义 images 这个 task,在这里使用了 imagemin 这个插件 imagemin 插件是用来压缩图片的插件,处理后图片的占用空间会变小。 执行

1
gulp images

即可完成对图片的压缩

字体处理

字体的处理,筛选出某些特定格式的字体,输出到指定目录

1
2
3
4
5
6
7
8
9
10
11
12
const fonts = {
'in': ['assets/fonts/bootstrap/*'],
'tmp': '.tmp/fonts',
'out': 'dist/fonts'
};
gulp.task('fonts', () => {
return gulp.src(require('main-bower-files')('**/*.{eot,svg,ttf,woff,woff2}', function(err) {
})
.concat(fonts.in))
.pipe(gulp.dest(fonts.tmp))
.pipe(gulp.dest(fonts.out));
});

执行

1
gulp fonts

即可完成字体的处理

额外文件处理

在项目中还存在非 HTML 的文件,比如 视频,音频,PHP等。这些做一下统一判断然后归档即可。

1
2
3
4
5
6
7
8
9
10
11
12
const extras = {
'in': [
'assets/*.*',
'!assets/*.html'
],
'out': 'dist'
};
gulp.task('extras', () => {
return gulp.src(extras.in, {
dot: true
}).pipe(gulp.dest(extras.out));
});

其中 in 指定了在 asset 目录中除 html 后缀的文件,此处进行读入筛选,然后输出到指定路径即可。 执行

1
gulp extras

即可完成额外文件的处理

文件依赖处理

设想一个情景,一个项目需要很多很多依赖库,我们在 bower.json 中定义好了所有的依赖,使用 bower 将他们下载了下来,如果我们需要在 HTML 中引用他们,如果我们还是手动地添加一个个引用那是不是太麻烦了? 没错,这个操作同样可以自动化操作,借助 wiredep 插件即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const wire = {
'in': 'assets/*.html',
'out': 'dist'
};
gulp.task('wiredep', () => {
gulp.src(wire.in)
.pipe(wiredep({
ignorePath: /^(\.\.\/)*\.\./
}))
.pipe($.useref({searchPath: ['.tmp', 'assets', '.']}))
.pipe($.if('*.js', $.uglify()))
.pipe($.if('*.css', $.cssnano()))
.pipe(gulp.dest(wire.out));
});

在这里使用了 wiredep 插件。 在 HTML中定义如下内容

1
2
<!-- bower:js -->
<!-- endbower -->

执行

1
gulp wiredep

之后,便会自动更新 bower.json 中所有依赖库的引用,在这里以 JS 为例子。 当前在 bower.json 中定义了

1
2
3
"dependencies": {
"jquery": ">= 1.9.0"
}

执行完毕之后,HTML中便有了

1
2
3
<!-- bower:js -->
<script src="/bower_components/jquery/dist/jquery.js"></script>
<!-- endbower -->

路径会随之更新。

服务器

最后是一个 serve 的 task 在本地搭建一个服务器来测试,同时监听文件的变动随时更新资源文件。

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
const serve = {
'baseDir': ['.tmp', 'assets'],
'baseDirDist': ['dist'],
'routes': {
'/bower_components': 'bower_components'
},
'port': 9000
};
gulp.task('serve', ['styles', 'scripts', 'fonts', 'wiredep'], () => {
browserSync({
notify: false,
port: serve.port,
server: {
baseDir: serve.baseDir,
routes: serve.routes
}
});
gulp.watch([
html.out, scripts.tmp, scripts.out, images.out, fonts.tmp, fonts.out
]).on('change', reload);
gulp.watch(styles.in, ['styles']);
gulp.watch(scripts.in, ['scripts']);
gulp.watch(fonts.in, ['fonts']);
gulp.watch('bower.json', ['wiredep', 'fonts']);
});
gulp.task('serve:dist', () => {
browserSync({
notify: false,
port: serve.port,
server: {
baseDir: serve.baseDirDist
}
});
});

上述 serve 首先要执行 styles, scripts, fonts, wiredep 的操作,然后在 9000 端口上运行。 同时利用 watch 方法监听文件的变动,随时更新。

删除和一键构建

最后还有清理构建文件和一键构建的功能。 清理 task 叫做 clean。

1
gulp.task('clean', del.bind(null, ['.tmp', 'dist']));

即将 .tmp 和 dist 目录进行清理。 一键构建就是执行其他所有操作,将所有操作汇总。

1
2
3
4
5
6
7
8
9
const build = {
'in': 'dist/**/*'
};
gulp.task('build', ['lint', 'html', 'images', 'fonts', 'extras'], () => {
return gulp.src(build.in).pipe($.size({title: 'build', gzip: true}));
});
gulp.task('default', ['clean'], () => {
gulp.start('build');
});

最后执行了一个总的压缩汇总,

代码

以上便是利用 Gulp 编译 Bootstrap-Sass 的全部过程。 整个项目的代码如下 GulpBootstrapSass 如果有问题,欢迎留言交流,希望对大家有帮助!