[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