[TOC]
# 简介
使用 gulp 或者 npm 的方式都可以让我们执行一些自动化的前端构建任务。
当然使用`npm run`的方式会更灵活,我们可以自己编写脚本来满足需求。
使用gulp 插件的方式则省事不少,而且插件丰富,本片目的就是为了实现**基于gulp的多页应用打包**。
本篇文章介绍的比较简单,主要是对于 website 的前端开发构建流程。对于移动web APP的构建可以查看 yeoman 。或者以后在进行总结。
# Gulp 流
[探究Gulp的Stream](https://segmentfault.com/a/1190000003770541)
虽然Gulp使用的是`Stream`,但却不是普通的`Node Stream`,实际上,Gulp(以及Gulp插件)用的应该叫做`Vinyl File Object Stream`。
这里的Vinyl,是一种虚拟文件格式。Vinyl主要用两个属性来描述文件,它们分别是路径(path)及内容(contents)。具体来说,Vinyl并不神秘,它仍然是JavaScript Object。Vinyl官方给了这样的示例:
```js
var File = require('vinyl');
var coffeeFile = new File({
cwd: "/",
base: "/test/",
path: "/test/file.coffee",
contents: new Buffer("test = 123")
});
```
从这段代码可以看出,Vinyl是Object,path和contents也正是这个Object的属性。
# 构建流程详解:
1. 在 **开发环境(development环境)** 下:需要默认将 SRC 下的文件复制到 WWW 目录下,并且实时监听 SRC 文件的改动映射到 www(因为 ionic serve 默认会读取 www 目录下的文件启动本地服务);开发模式主要的 gulp 任务是监听 html, css, JS 的改动,sass 的编译,实时刷新,stylelint,JSLint,文件替换和 API 路径配置;
2. 在 **预生产环境(staging环境)** 下:首先需要清除掉 WWW 目录下的所有文件,然后重新从 SRC 编译代码到WWW 目录下,这个过程主要的 gulp 任务是:清除文件,HTML 模板合并缓存,图片压缩,CSS 合并压缩,JS 合并压缩;
3. 这一步最好,还是进行重启服务器进行最后一步的测试。
4. 在 **生产环境(production环境)** 下: 生产模式和预生产模式除了 API PATH 配置不一样,其它的gulp任务都是一样;
# 模块划分
模块的定义需要根据项目而定,切分的粒度也需要视情况而定,例如**我们可以将 login切换为一个模块,所有有关login模块的html,css,js,img都放在login模块目录下**:
推荐阅读: [前端工程——基础篇](https://github.com/fouber/blog/issues/10)
# 目录结构
目录树:
~~~
/
|
|- src/
|
| |
| |- index/
| | |- home/
| | | |- home.controller.js
| | | |- home.html
| | | |- home.scss
| | | |
| | |- login/
| | | |- login.controller.js
| | | |- login.html
| | | |- login.scss
| | | |
| | |- config/
| | | |- config.default.json
| | | |- config.development.json
| | | |- config.production.json
| | | |- config.js
| | |
| | |- ...
| | |
| |- styles/
| | |- scss/
| | | |- ionic.app.scss (index file)
| | |- ionic.app.css (all css will be compiled into this file)
| | |
| |- images/
| | |
| |- lib/
| | |- angular/
| | |- angular-resource
| | |- ionic
| |- shared/ (common components cross projects)
| | |- canvasClock/
| | |- calendar/
| | |- constants/
| |- index.html
|
|- app
~~~
初始化项目目录
```shell
$ mkdir ES6-with-gulp-babe && cd ES6-with-gulp-babe && git init && npm init
```
然后自己进行项目的说明配置。
确定项目目录:
```js
var paths = {
src : './src', //开发目录
dest : './dist' // 生产的目录 (该目录视情况而定,比如ionic项目就必须是名为www目录)
};
```
然后在`gulp.js`中,使用就行。
在项目部署时,只会针对`dest`目录,js和css、图片文件会被全部放置到`./dist/assets`目录下。
# 环境的区分
通过传入env的参数区分:
~~~
$ gulp build --env development/staging/production
~~~
开发环境,预发布环境,还是部署环境。
# 项目版权/注释
https://github.com/tracker1/gulp-header
```js
var header = require('gulp-header'),
//...
var pkg = require('./package.json');
var banner = [
'/*!\n' +
' * <%= pkg.name %>\n' +
' * <%= pkg.title %>\n' +
' * <%= pkg.url %>\n' +
' * @author <%= _.capitalize(pkg.author) %>\n' +
' * @version <%= pkg.version %>\n' +
' * Copyright ' + new Date().getFullYear() + '. <%= pkg.license %> licensed.\n' +
' */',
'\n'
].join('');
```
# 前端静态服务器
## 要求
* **跨域转发**
受到浏览器内部机制的限制,本域内,也就是自身服务器下的服务的js是不能操作其他域下的页面对象,或者去请求域下的数据,这个时候就会产生跨域问题。
为什么cdn或者图片加载的时候,不是本域的东西,却可以请求的来?跨域是指js不能操作和请求其他域的,浏览器本身请求和标签src内部的请求是不受限制的。
* **实时刷新的静态服务器**
[browser-sync](https://browsersync.io/) 启动 http静态服务器环境。当然还有其他的很多工具都可以使用。
* **热更新**
[拥有实时重载(live-reloading)和 CSS 注入的服务器](http://www.gulpjs.com.cn/docs/recipes/server-with-livereload-and-css-injection/)
* **模拟后端API的数据-前端的mock server**(//TODO)
Web开发是一个前后端合作的工作,但在开发的前期,双方约定好接口和数据后,就进入了各自的开发,这时候前端就需要有自己的开发环境,能够与最后和后端联调时后端提供的服务器能力接近,并且能够简化前端的工作流程,自动化完成相关任务。
对于项目中所有的ajax请求进行拦截,返回配置的数据,虚拟一个服务器,模拟后台返回json数据给前端,**这样可以一定程度上实现前后端分离,约定好接口之后,前后端即可同时开发,从而提高效率。**
## TODO
模拟后端API的数据-前端的mock server(//TODO)
https://nodemon.io/
https://github.com/JacksonGariety/gulp-nodemon
https://github.com/colynb/gulp-data
## 方案
某个项目我们用到了json的方式传递数据,因api接口的域名和实际本地开发的域名不相同,所以有跨域的情况出现
项目本地开发域名: `dms.dev-adtime.com`
项目服务器接口api的域名: `api.dev.adt100.net`
要将 `api.dev.adt100.net/dmc` 都转成 `dms.dev-adtime.com/dmc`
~~原来的做法:nginx 设置反向代理~~
现在的做法:
1. `browser-sync` 启动 http静态服务器环境
2. `http-proxy-middleware` 设置反向代理
安装:
```shell
$ $ npm i -D browser-sync http-proxy-middleware
```
配置
```js
var gulp = require('gulp'); // 主要
var browserSync = require('browser-sync');
var proxyMiddleware = require('http-proxy-middleware');
// http环境,接口反向代理
gulp.task('browser-sync', function() {
// 多个地址的反向代理
var proxy163 = proxyMiddleware('/f2e', {
target: 'http://img1.cache.netease.com',
headers: {
host:'img1.cache.netease.com' // 这个挺关键
}
});
var proxyAdtime = proxyMiddleware('/apps', {
target: 'http://cdn.adt100.com',
headers: {
host:'cdn.adt100.com'
}
});
browserSync({
server: {
baseDir: "./",
port: 80,
middleware: [proxy163, proxyAdtime]
}
});
});
```
## 参考
[GulpJs入门与实践](https://libraries.io/github/hellosun/gulpDemo)
[为browser-sync设置代理](http://yangblink.com/2016/09/17/为browser-sync设置代理/)
# css 操作
我要进行一个工程的样式文件进行发布,那我要先对scss进行编译,然后对里面的图片合并为雪碧图,最后在对处理好的css进行合并压缩,生成map。
## 要求
1. style-lint
stylelint 由 PostCSS 提供技术支持,所以它也可以理解 PostCSS 解析的语法,比如 SCSS。
[使用stylelint对CSS/Sass做代码审查](http://www.w3cplus.com/workflow/How-to-lint-your-css-with-stylelint.html)
2. css压缩
以下几种任选一种:
[gulp-csso](https://github.com/ben-eb/gulp-csso)
[gulp-cssnano](https://www.npmjs.com/package/gulp-cssnano) //主要使用postcss生态
[shorthand](https://github.com/vol7/shorthand)
3. [使用gulp工具让px自主转化为rem](http://www.yaya12.com/archives/710)。
使用插件:注意下列代码的75代表了1rem对应的px值,这个值根据设计师提供的设计图的总宽度/10决定。如果设计图的总宽度是以750px为标准,则填写75;如果是375px,则填写37.5;以此类推……
```js
var gulp = require('gulp');
var postcss = require('gulp-postcss');
var px2rem = require('postcss-px2rem');
gulp.task('default', function() {
var processors = [px2rem({
remUnit: 75
})];
return gulp.src('./src/*.css')
.pipe(postcss(processors))
.pipe(gulp.dest('./dest'));
});
```
4. css spirit
任选一种:
[gulp.spritesmith](https://www.npmjs.com/package/gulp.spritesmith)
[gulp-tmtsprite](https://github.com/weixin/gulp-tmtsprite)
5. 可以考虑配合添加版本号更新css中的image
```js
gulp.task('replaceImgInCss', function () {
//css,主要是针对css 中的img替换
gulp.src(['rev/**/rev-img-manifest.json', buildBasePath + 'css/*.css'])
.pipe(plumber())
.pipe(revCollector({
replaceReved: true
}))
.pipe(gulp.dest(config.dest + config.cssDir));
});
```
## 方案
可以考虑使用使用Sass(和Compass)编写CSS。就是Compass好久没更新了!
使用`gulp-autoprefixer`根据设置浏览器版本自动处理浏览器前缀。
```js
var autoPrefixBrowserList = ['last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'];
autoprefixer({
browsers: autoPrefixBrowserList,
cascade: true
})
```
`gulp-autoprefixer`的browsers参数详解 (传送门):
● `last 2 versions`: 主流浏览器的最新两个版本
● `last 1 Chrome versions`: 谷歌浏览器的最新版本
● `last 2 Explorer versions`: IE的最新两个版本
● `last 3 Safari versions`: 苹果浏览器最新三个版本
● `Firefox >= 20`: 火狐浏览器的版本大于或等于20
● `iOS 7`: IOS7版本
● `Firefox ESR`: 最新ESR版本的火狐
● `> 5%`: 全球统计有超过5%的使用率
# js 操作
## 要求
1. [ESLint](http://zhenhua-lee.github.io/tools/linter.html)
2. 压缩-ugly/packer,合并。
压缩的顺序不能出错
3. source-map
4. require.js
需要`r.js`进行打包,可选[gulp-requirejs-optimize](https://www.npmjs.com/package/gulp-requirejs-optimize)。
5. 前端资源缓存问题-md5
[gulp-rev](https://www.npmjs.com/package/gulp-rev) 用来为文件加上增加散列值
[gulp-rev-collector](https://github.com/shonny-ua/gulp-rev-collector) 配合gulp-rev用来替换html文件中的文件名
[gulp-rev和gulp-rev-collector的使用](https://www.manster.me/?p=488)
[前端静态资源版本更新与缓存——通过gulp 在原html文件上自动化添加js、css、img版本号](http://www.jianshu.com/p/164344f8e2ac)
静态服务器要配置静态资源的过期时间为永不过期。
这样做的目的是:
静态资源在客户端只会请求一次(首次),永久缓存,之后访问不会再发送请求协商304
更改、更新会指定到对应的文件
不删除旧版本(cdn等),以便回滚代码,回滚的时候更新html,同时也不会增加http请求。
6. 如果项目使用了typescript
[gulp-typescript](https://github.com/ivogabe/gulp-typescript) - TypeScript in JavaScript.
7. combo 线上
## 方案
如果web前端模块化了requireJS,就需要`r.js`进行打包,可选[gulp-requirejs-optimize](https://www.npmjs.com/package/gulp-requirejs-optimize)。
```js
// 操作r.js打包任务
var rjs = function (){
return gulp.src(config.rbuildjs) //只打包指定目录下requirejs模块化用到的js文件(这样就不容易打包出错了)
.pipe(rjsOptimize({
name: 'main',// 模块入口
out: config.src+"./main-built.js", // 输出压缩后的文件位置
mainConfigFile: config.rbuildjsPath,
// exclude: [
// 'jquery'
// ]
}))
.pipe(gulp.dest(config.dest)); //最后的目录在`config.dest+out`下面..哈哈 */
}
```
# images 操作
## 要求
1. 图片压缩
[gulp-imagemin](http://www.ydcss.com/archives/26)
2.
## 使用工具
## 代码
## 方案
## 参考
# html 操作
## 要求
1. html模块化,打包时,使用插件include 各个html的模块部分
类似这样的结构:
~~~
src
|--- ccomponent
|--- tabbox
|--- main.js
|--- main.html
|--- main.css
|--- img //图片目录
|--- header
|--- header.js
|--- header.html
|--- header.css
|--- img //图片目录
~~~
google:html gulp include模块
1. 使用posthtml
2. [gulp-content-includer](https://github.com/hellopao/gulp_plugin/tree/master/gulp-content-includer)
2. html页面压缩
[gulp-htmlmin](https://github.com/jonschlinkert/gulp-htmlmin)
```js
.pipe(htmlmin({
collapseWhitespace: true,
removeComments: true
}))
```
同时页面中的内联的css和js也要压缩
3. html中引用的 css 和 js 自动合并
[gulp-merge-link](https://github.com/aDaiCode/gulp-merge-link)-合并html页面中的js或css
## 方案
使用`gulp-inject`
```shell
npm install --save-dev gulp-inject
```
# 资源导出目录
资源导出目录dist为文件资源导出目录,需要配合启动服务器进行处理分为两个文件夹,images和pages;images仅存放通用的图片资源。pages目录结构和资源目录一致
# 代码的编写路径问题
根据代码结构,建议在编写代码的过程中页面独有资源采用相对路径,获取公用资源建议采用绝对路径,绝对路径根目录为dist目录
# Gulp插件
可以使用common.js的方式将gulp.js进行模块分割。
需要**同步执行任务**。([run-sequence](https://github.com/OverZealous/run-sequence),gulp 4.0自带了同步执行函数)
[1. gulp-load-plugins](https://github.com/jackfranklin/gulp-load-plugins) 不用在一个一个写插件了。
2. gulp-sass 用于将 Sass 文件编译为 CSS 文件
3. gulp-autoprefixer 根据浏览器版本自动处理添加浏览器前缀
4. browser-sync 能让浏览器实时、快速响应文件更改(html、js、css、sass、less等)并自动刷新页面
5. gulp-notify 用于任务提醒
6. [gulp-plumber](https://github.com/floatdrop/gulp-plumber) 正确的错误处理应该是能遇到错误时输出错误,并且能够保证整个gulp进程不挂,而且功能还能正常用。https://kejyuntw.gitbooks.io/gulp-learning-notes/plguins/Tool/Plugins-Tool-gulp-plumber.html
**注意**:pipe(gulpPlumber())一定要在编译Scss及最小化JavaScript之前就优先加入(加入顺序有差异),否则会无法起到作用
7. [gulp-sourcemaps](https://github.com/gulp-sourcemaps/gulp-sourcemaps) 生成source map,方便跟踪问题代码
8. [gulp-util](https://github.com/gulpjs/gulp-util) gulp工具集
9. [gulp-cssnano](https://github.com/ben-eb/gulp-cssnano) 压缩css文件
10. browserify 让你使用类似于 node 的 require() 的方式来组织浏览器端的 Javascript 代码
11. [vinyl-source-stream](https://github.com/hughsk/vinyl-source-stream) 将Browserify的bundle()的输出转换为Gulp可用的vinyl(一种虚拟文件格式)流。
12. [gulp-if](https://github.com/robrich/gulp-if),支持条件判断(支持函数条件)来控制相关流程。
13. [gulp-filter](https://github.com/sindresorhus/gulp-filter) 支持glob模式、函数过滤,以及过滤后恢复原来的输入
14. gulp-notify 当gulp 处理完档案后,可以使用gulp-notify 告知我们处理状况。
# 当中可能需要会用到的文件
## [Modernizr.js](https://modernizr.com/)
Modernizr是基于渐进增强理论来开发的,所以它支持并鼓励开发者一层一层的创建他们的网站。一切从一个应用了Javascript的空闲地基开始,一个接一个的添加增强的应用层。因为使用了Modernizr,所以你容易知道浏览器都支持什么。
它会检测当前浏览器是否支持CSS3的特性,比如`@font-face`、`border-radius`、 `border-image`、`box-shadow`、`rgba()` 等,同时也会检测是否支持HTML5的特性——比如`audio`、`video`、本地储存、和新的 `<input>`标签的类型和属性等。
内部可以自定义的包含需要检测的html5特性和[html5shiv.js](https://github.com/aFarkas/html5shiv)
## [Selectivizr.js](http://selectivizr.com/)
Selectivizr是一个JS文件,你只需要在使用前先加载下列框架中的任何一个:JQuery、dojo、prototype、Yahoo YUI、DOMAssistant、mootools、NVMatcher,然后再调用Selectivizr,就可以让IE6/7/8支持CSS3选择器。
## [Respond.js](https://github.com/scottjehl/Respond)
### 插件原理
1. 接下来,需要理解`respond.js`的实现思路:
2. 第一步,将`head`中所有外部引入的`CSS`文件路径取出来存储到一个数组当中;
3. 第二步,遍历数组,并一个个发送`AJAX`请求;
4. 第三步,`AJAX`回调后,分析`response`中的`media query`的`min-width`和`max-width`语法(注意,仅仅支持`min-width`和`max-width`),分析出`viewport`变化区间对应相应的`css`块;
5. 第四步,页面初始化时和`window.resize`时,根据当前`viewport`使用相应的`css`块。
> [HTML5 respond.js 解决IE6~8的响应式布局问题](http://blog.163.com/hongshaoguoguo@126/blog/static/18046981201410745621487/)
# 代码风格
```html
<script type="text/javascript" src="//use.typekit.net/mkq4pib.js"></script>
```
`//`表示同协议,一般现在用在`https`跨域名地址情况下。比如**第三方统计代码的引入**,用`//`就不用很麻烦地区分`https`还是`http`,也不用担心`https`下降到http出问题
# 参考
[Web Starter Kit ](https://github.com/google/web-starter-kit/)
[【使用gulp解决RequireJS项目前端缓存问题(一)】的更多相关文章](https://www.bbsmax.com/R/Ae5R1oAJQ9/)
[H-ui 前端框架](http://www.h-ui.net/index.shtml)
https://github.com/wungqiang/requirejs-skeleton
[frontend-boilerplate](https://github.com/leonardofaria/frontend-boilerplate)
[runkit](https://npm.runkit.com/generator-ionic-gulp-compass)
[ES6-with-gulp-build](https://github.com/chenbin92/ES6-with-gulp-build/issues/6)
[gulp-sass-bourbon-neat-browsersync-boilerplate](http://meredithunderell.com/gulp-sass-bourbon-neat-browsersync-boilerplate/)
- 前言
- 中文字体
- 移动Web适配方案
- !移动Web基础!
- 详解适配相关概念
- 移动开发之设计稿
- 移动适配方案(一)
- 移动适配方案(二)
- vw+rem 实现移动端布局
- 移动端适配之雪碧图(sprite)背景图片定位
- 适配 iPhoneX
- 前端开发实战
- 打造自己的前端开发流程(Gulp)
- flexible.js案例讲解
- viewport 与 flexible.js解读
- 图片与字体
- 踩过的坑
- 浏览器默认样式
- 300ms点击延迟和点击穿透
- ios css
- CSS 常见问题
- Ionic v1混合开发
- Native App、Web App 、Hybrid App?
- ionic项目结构
- 混淆加密
- 解决问题
- cordova
- 环境配置
- 打包发布
- 问题
- 移动前端开发优化
- Web开发之抓包
- ===web移动开发资源===
- H5组件框架
- 调试集合
- 简单h5调试
- whistle
- devtools-pro