前端構建之gulp

前端構建之gulp

爲何須要前端構建
不解釋
本文大體分爲如下幾個內容:
  • 規範校驗js代碼(jslint)
  • js解釋器(babel)
  • 合併js代碼(concat)
  • 壓縮js代碼(uglify)
  • sourcemap
  • 規範校驗css代碼(csslint)
  • css解釋器
  • 合併css代碼
  • 壓縮css代碼

規範校驗js代碼

1. 用gulp檢測js代碼規範

js檢測工具用來css

  • 檢測本身寫的js是否有語法錯誤
  • 是否根據咱們設定的規則寫的

經常使用的js檢測工具備jshint、jslint、eslint等, 推薦用eslint。
結合gulp的話,用gulp-eslint這個插件。使用eslint時配置一個.eslintrc文件,用來編寫自定義js規則。
若是用到了es模塊,須要在.eslintrc配置中指定parserOptions的sourceType爲modulehtml

{
  "root": true,
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  "parserOptions": {
    "sourceType": "module"
  },
  "extends": "eslint:recommended",  //使用推薦的eslint語法
  "rules": {
    "indent": ["error", 4], //自定義indent爲4個空格,級別爲error
    "semi": ["off", "always"],
    "no-console": "off",
    "no-unused-vars": "off"
  }
}
2. 使用prettier強制修改js代碼

使用prettier, 根據prettier自定義來強制修改js代碼。
能夠在使用prettier時添加options,或者創建一個config文件來配置options也能夠。
參考這裏前端

{
            singleQuote: true,
            trailingComma: "all",
            bracketSpacing : true,
            semi: true,
            tabWidth: 4,  //定義indent爲4個空格
            printWidth: 120
        }
3. 當gulp一個task依賴異步任務時

若是gulp一個task依賴一個或者多個異步任務時, 須要判斷這個異步是否完成,才能進行下一個任務,
這樣才能保證gulp的任務讓順序執行。
異步任務的處理能夠參考這裏node

4. del
經過del模塊能夠刪除指定file
5. gulp中實現目錄copy
很簡單: gulp.src() 獲取目錄下全部文件
       gulp.dest()輸出到指定目錄
6. run-sequence

run-sequence這個模塊用來在gulp中控制多個任務讓順序執行git

7. 更新代碼
1. 是否是常常出現indent不齊
2. 當看到別人用雙引號引字符串時想給他改爲單引號
3. 有的語句結尾有分號結束,有的語句結尾沒有分號結束, 強迫症的你有沒有想它的衝動

這些能夠經過下面這個思路解決。es6

  1. gulp.src()獲取想要處理的js文件
  2. 經過prettier 根據自定義規則強制修改
  3. eslint檢測經過prettier強制修改的js文件
  4. gulp.dest()輸出到一個臨時文件夾(好比tempjs)
  5. copy這個臨時文件夾(好比tempjs)的內容到原來的js文件夾下
  6. 刪掉臨時文件夾(tempjs)
// js eslint set
gulp.task('js_lint', function () {
    return gulp.src('./src/js/**/*.js')  //步驟1
        .pipe($.prettier({               //步驟2
            singleQuote: true,           //自定義選項
            trailingComma: "all",
            bracketSpacing : true,
            semi: true,
            tabWidth: 4,
            printWidth: 120
        }))
        .pipe($.eslint())                //步驟3
        .pipe($.eslint.format())         //輸出到console
        .pipe($.eslint.failAfterError()) //出現錯誤時,指定這個任務不成功
        .pipe(gulp.dest('./src/tempjs')) //步驟4
});

// js file copy
gulp.task('copy_js', function () {       //步驟5
    return gulp.src('./src/tempjs/**/*.js')
        .pipe(gulp.dest('./src/js'))
});

// js file clean
gulp.task('clean', function (cb) {       //步驟6
    return del('./src/tempjs', cb);
});

// js forma
gulp.task('format_js', function (cb) {
    runSequence('js_lint','copy_js','clean',cb);  //指定執行順序
});
8. precommit

這個插件目的是在commit以前,執行相關操做
"precommit": "gulp format_js"github

9. husky

這個插件目的是創建和git之間的hooks(鉤子), 一般和precommit結合用,
好比使用git commit這個命令時, 會經過hooks調用precommit語句。
這樣咱們就不須要專門去執行npm run precommit這個命令了,
當咱們git commit的時候,husky會自動調用npm run precommitnpm

10. lint-staged

可是還有一個問題, 上面執行完git commit 後,經過prettier修改了全部的js文件,
有些便不是我本身修改的文件,也會被強制修改,
因而能夠經過lint-staged這個node模塊來指定stage中的文件。
思想很簡單,只對git stage中的文件處理。
能夠參考這裏gulp

一般使用它的思想:segmentfault

  1. 執行git commit時,經過husky調用precommit
  2. precommit 執行lint-staged,即只對git stage中的文件進行處理
  3. lint-staged中配置prettier修改js文件(添加一個config文件來配置)
  4. lint-staged中配置eslintjs檢測文件(添加一個eslintrc文件來配置)
  5. 若是正常經過, 則把經過prettier修改和eslint檢測的js文件,經過git add 添加
"scripts": {  
    "precommit": "lint-staged"
  },
  "lint-staged": {
    "*.js": [                 // 要處理的js路徑
      "prettier --write",  //要處理的文件上修改
      "eslint  --fix",       // 參數fix的意思是:根據eslint配置文件自動修復js文件
      "git add"
    ]
  },

prettier 能強制修改js,eslint --fix也能強制修改js, 區別是什麼呢???
個人理解prettier更強一點,eslint --fix 強制修改有限。

這裏有一個注意點,prettier的配置參數, 經過添加一個config文件來配置。

能夠在使用prettier時添加options,或者創建一個config文件來配置options也能夠。

"lint-staged"的配置的意思是,
對git stage中的文件的操做,如eslint *.js, git add *.js, 不能用寫gulp *.js
參考這裏

我添加的是 prettier.config.js文件,

module.exports = {
    printWidth: 120,
    parser: "flow",
    singleQuote: true,
    tabWidth: 4,
    bracketSpacing : true,
    semi: true
};
eslintrc前面已經寫過了。

經過lint-staged就不須要第7步中生成一個臨時文件(tempjs)來過渡了, 
由於prettier就直接處理stage終端文件了。

第7步的思想能夠簡化爲:

  1. 經過lint-staged獲取想要處理的js文件
  2. 經過prettier 根據自定義規則強制修改要處理的js文件
  3. eslint檢測經過prettier強制修改的js文件
  4. eslint檢測經過的話, 將處理的js文件添加到git stage中

這樣減小了臨時文件夾的輸出,複製,刪除。
這樣也不須要寫gulp task了,直接經過兩個prettier,eslint配置文件就搞定了。


使用es6寫js時,轉化爲各個瀏覽器能識別的es5

11. babel 編譯
babel用來將es6語法轉化爲es5, 好比es6中的let轉化爲var, 變量字符串拼接轉化爲+,等等。
結合gulp用,就是gulp-babel, 它依賴於babel-core,因此須要安裝gulp-babel和babel-core。

本身的理解: babel, babel-core提供了babel的運行環境(理解有錯誤的話,以後改正)。
只有這兩個(babel, babel-core)還不能實現轉譯,須要transform插件(如babel-preset-env)
而後須要安裝babel-preset-env來將本身寫的es6(源碼),根據當前執行環境(瀏覽器),
轉化爲es5(當前環境-瀏覽器能識別的語法)

何時用這個babel呢, 獲取源碼後,合併壓縮以前。
gulp.src('*.js')
    .pipe($.babel({ presets:['env'] }))
    .pipe(js合併)
    .pipe(js壓縮)

若是使用uglify來壓縮js文件, 須要配置babel的options,
添加forceAllTransforms: true 便可以。參考這裏

合併js

爲何須要合併js呢,經過合併js文件,能夠減小http請求。

12. concat
經過gulp-concat能夠將多個js文件合併成一個js文件。
用法相對簡單,
gulp.src('src/js/**/*.js')            //想要合併的js文件
  .pipe($.concat('all.js'))           //concat的參數指定合併後的文件名
  .pipe(gulp.dest('dist/js/'))        //合併後的目錄

如今有一個新的需求task1, 須要單獨寫幾個js文件,
可是不想和其餘js合併到一塊兒,也就是說想單獨合併這幾個js文件,而後單獨輸出,
這個時候, 就要從新寫一個gulp task來合併。

gulp.src('src/js/task1/**/*.js')        //task1下,想要合併的js文件
  .pipe($.concat('task1.js'))           //task1下全部js文件合併輸出爲task1
  .pipe(gulp.dest('dist/js/'))          //合併後的目錄

當這種新的需求不少的時候,就須要不斷添加新的gulp task,在大項目多人一塊兒寫不一樣的新需求時很麻煩。

因而能夠創建一箇中間文件(middle),
在這個中間文件添加想要合併的js文件,及想要輸出的js文件。

gulp task只是處理這個中間文件。
這樣有新的需求時,修改中間文件就能夠了, 就不須要修改gulp的配置文件了。

中間文件,我這裏選擇yaml

concat 
  -
    src:
      - src/js/task1/top.js
      - src/js/task1/background.js
    out: task1.js
  -
    src:
      - src/js/task2/top.js
      - src/js/task2/index.js
    out: task2.js

這樣就實現了輸出到不一樣js中了。

那麼, gulp中task怎麼寫呢?
由於要處理yaml文件,須要安裝js-yaml 這個插件
關於yaml能夠參考阮教授的文章

gulp.task('concat', function() {
    var concatData = yaml.safeLoad(             //經過yaml.safeLoad()轉化爲js語言,能夠用console.log(concatData)打出來出來看看
      fs.readFileSync('./example.yml', 'utf8')   //讀取fs模塊讀取文件內容,能夠用console.log()打出來出來看看
    );
    for (var i = 0; i < doc.concat.length; i++) {
        gulp.src(concatData.concat[i].src)
            .pipe($.concat({path: concatData.concat[i].out}))
            .pipe(gulp.dest('../dist/js/build/'))
    }
  })

若是task是上面這樣寫的話, 還不能達到要求
由於這個任務是比較耗時的,致使咱們不能準確判斷這個任務何時結束,這時須要進行異步處理,此次用promise處理。
安裝bluebird使用promise。

var Promise = require( 'bluebird');
  gulp.task('concat', function() {
    var concatData = yaml.safeLoad(             //經過yaml.safeLoad()轉化爲js語言,能夠用console.log(concatData)打出來出來看看
      fs.readFileSync('./example.yml', 'utf8')   //讀取fs模塊讀取文件內容,能夠用console.log()打出來出來看看
    );
    return new Promise(function (resolve, reject) {
        setTimeout(function() {
         for (var i = 0; i < doc.concat.length; i++) {
            gulp.src(concatData.concat[i].src)
                .pipe($.concat({path: concatData.concat[i].out}))
                .pipe(gulp.dest('../dist/js/vendor/'))
          }
          resolve();
        },500)
    })
  })

壓縮js

13. gulp-uglify

gulp-uglify 用來壓縮js文件
壓縮很簡單, 獲取js原文件,合併,而後壓縮

gulp.task('uglify_js', function () {
    return gulp
        .src('src/js/**/*.js')
        .pipe(
            $.babel({
                presets: [
                    ['env', { forceAllTransforms: true } ]  // forceAllTransforms用來解決uglify不支持es6
                ]
            })
        )
        .pipe($.concat('all.js'))
        .pipe($.uglify())
        .pipe(gulp.dest('./dist'));
})

sourcemap

14. gulp-sourcemaps
sourcemap 的目的:還原源碼,
 什麼意思呢? 當咱們將源碼通過合併,壓縮處理後,成爲了一個文件,不方便調試。
 特別是壓縮後更是面目全非,這樣咱們在開發階段不能實現debug,好比打斷點功能就不可能實現了。
 sorucemap就是解決的這個問題。
 使用sourcemap, 
 * sourcemap.init() 初始化
 * sourcemap.write('sourcemap') //指定目錄,存放一一對應的map文件
 
 通過sourcemap處理後,在目標目錄(dest指定的)下,生成一個新文件(與轉譯前對應的map文件),
 目錄結構與轉譯前徹底對應。這樣咱們就很容易找到某個文件,而後實現打斷點。
gulp.task('concat_js', function() {
    return gulp
        .src('src/js/**/*.js')
        .pipe($.sourcemaps.init())
        .pipe($.concat('all.js'))
        .pipe($.sourcemaps.write('sourcemap'))
        .pipe(gulp.dest('./dist'));
});

在dist目錄下,就能發現sorucemap文件, 裏面的目錄結構和src/js下的目錄結構如出一轍。這樣和方便查找和操做。

規範校驗css代碼

規範校驗js寫法的工具備jslint,jshint,最近的eslint等等,
規範校驗css寫法的工具備csslint,stylelint等等。
15. stylelint

stylelint用來檢測css是否根據配置文件來寫的,是基於postcss的一個檢測工具。
stylefmt是一個根據stylelint的配置文件來強制修改css文件的插件。有點prettier的意思。

可是能夠不用stylefmt, 在stylelint的選項中添加 --fix 也能夠修改css文件。

npm install gulp-postcss gulp-stylelint stylelint stylelint-config-standard -save-dev

使用stylelint時,有個標準配置插件, stylelint-config-standard。

如下爲 stylelint的配置文件(.stylelintrc)

{
    "extends": "stylelint-config-standard",    // 使用stylelint的默認標準配置
    "rules": {
        "declaration-block-trailing-semicolon": null,   // 各類規則,根據本身須要能夠靈活配置
        "indentation": 4,
        "block-no-empty": null,
        "max-empty-lines": 1,
        "selector-list-comma-newline-after": never-multi-line,
        "at-rule-no-unknown": null,
        "declaration-colon-space-after": null,
        "no-duplicate-selectors" : null,
        "no-descending-specificity": null,
        "selector-pseudo-element-colon-notation": null,
        "no-empty-source": null
    }
}

結合gulp用。

gulp.task('lint_css', function () {
   return gulp.src('src/css/**/*.css')
       .pipe($.stylelint({
           reporters: [
               {formatter: 'string', console: true}   //將錯誤輸出到console
           ],
           fix: true
       }))
       .pipe(gulp.dest('./dist/css'));
});
16. postcss
  1. 當咱們用sass的寫法來書寫css
  2. 想在css中用變量,for循環等語法
  3. 想利用 (cssnext)[http://cssnext.io/] 書寫css時
  4. 根據不一樣瀏覽器自動添加前綴 (autoprefixer)

    當咱們想在css中實現上述等功能時,能夠用postcss。
    postcss就是一個平臺,能夠經過添加各類插件來實現上述等功能。

npm install postcss-cssnext postcss-simple-vars postcss-nested --save-dev

能夠寫一個postcss的配置文件來專門規範postcss相關的配置,也能夠直接寫在在gulp配置文件中。
var cssnext = require('postcss-cssnext');
var simpleVars = require('postcss-simple-vars');
var nested = require('postcss-nested')

gulp.task('concat_css', function () {
    var plugins = [
        cssnext(),      // 處理cssnext
        simpleVars(),  // 處理變量
        nested()       // 處理向sass那樣的內嵌寫法
    ];
    return gulp.src('src/css/**/*.css')
        .pipe($.sourcemaps.init())
        .pipe($.postcss(plugins))
        .pipe($.sourcemaps.write('sourceMap/css'))
        .pipe(gulp.dest('./dist/css'));
})

css合併

gulp經過正則匹配很方便的實現多入口多輸出的功能。

入口文件(多入口):
gulp.src('src/css/**/*.css')

出口文件(多出口):
gulp.dest('./dist/css')
dist/css目錄下的結構和src/css的結構同樣。

壓縮css代碼

17. gulp-clean-css
css 的壓縮能夠用gulp-clean-css。

`npm install gulp-clean-css --save-dev`
gulp.task('uglify_css', function () {
    var plugins = [
        cssnext(),      // 處理cssnext
        simpleVars(),  // 處理變量
        nested()       // 處理向sass那樣的內嵌寫法
    ];
    return gulp.src('src/css/**/*.css')
        .pipe($.sourcemaps.init())
        .pipe($.postcss(plugins))
        .pipe($.sourcemaps.write('sourceMap/css'))
        .pipe($.cleanCss())
        .pipe(gulp.dest('./dist/css'));
})

因而當咱們想要lint css的時候,執行gulp lint_css,
想要format css的時候執行gulp concat_css,
想要壓縮css的時候,執行uglify_css

可是這樣很麻煩, 能夠在添加一個task,執行全部想要執行的task。

gulp.task('format_js', function (cb) {
    runSequence('js_lint','copy_js','clean',cb);
});

最後附上完整的(github源代碼)[https://github.com/zhangchch/...]。

以上都是本身的理解,若有錯誤的地方,真心求指教。
相關文章
相關標籤/搜索