webpack 使用總結

什麼是 webpack

Webpack 是德國開發者 Tobias Koppers 開發的一個強力的模塊打包器。 所謂包(bundle)就是一個 JavaScript 文件,它把一堆資源(assets)合併在一塊兒,以便它們能夠在同一個文件請求中發回給客戶端。 包中能夠包含 JavaScript、CSS 樣式、HTML 以及不少其它類型的文件。javascript

爲何要使用 webpack

  • 與 react 一類模塊化開發的框架搭配着用比較好。
  • 屬於配置型的構建工具,容易上手,160 行代碼可大體實現 gulp 400 行才能實現的功能。
  • webpack 使用內存來對構建內容進行緩存,構建過程會比較快。

webpack 的特色

可以實現多種不一樣的前端模塊系統
前端頁面愈來愈複雜、代碼量變大,咱們須要使用一些模塊系統來組織代碼、把代碼分割成不一樣的模塊來使用。
目前前端模塊系統主要分爲如下幾個標準:css

  • Script標籤形式
  • CommonJS
  • AMD和一些變種實現
  • CMD
  • ES6模塊

以上方式有各自的優缺點,這裏不作贅述。
webpack 做爲一個智能解析器,能夠處理幾乎任何第三方的庫,不管他們的模塊形式是 commonjs,amd 仍是普通的 js 文件,甚至加載依賴的時候能夠動態表達式:html

import React, { PropTypes } from 'react'
require( 'es6-promise' ).polyfill()
require( "./templates/" + name + ".jade" )
var $ = require( 'jQuery' )

分塊打包
資源請求是個老生常談的優化問題,有兩個極端的方向來處理資源:前端

  • 將全部文件都打包在一個請求裏
  • 每一個模塊都發起一個請求

webpack 打包前端資源(模塊)時可以實現代碼分割,按需加載,如在單頁面應用裏,將每一個 page 的資源單獨打包,按頁面加載。這種方式被稱爲 code splitting(具體實現方式查看:https://webpack.github.io/docs/code-splitting.html。java

處理全部資源
目前模塊系統只能處理 javascript,而咱們還有不少其餘靜態資源須要處理,好比:node

  • 預編譯的 js 文件(coffeescript 或 jsx 或 es6 -> javascript)
  • css 文件 (less -> css -> autoprefixer)
  • 圖片 (壓縮、轉 base64)
  • 模板 (jade、各類template)
  • 等等

配置 webpack 後,這些都很容易處理。react

webpack 與 、requirejs 等工具的區別

gulp、grunt 是自動化任務構建工具。
webpack 是模塊化解決方案。jquery

gulp 是經過一系列插件將本來複雜繁瑣的任務(Task)自動化,並不能將你的 css 等非 js 資源模塊化,它與 webpack 之間有必定的功能重合,好比打包、壓縮混淆、圖片轉 base64 等等。但它們的目的跟要解決的問題是不同的。webpack

webpack 本質上是相似 browserify、seajs 、requirejs 的JS模塊化的方案。其中 seajs / require 是一種類型,browserify / webpack 是另外一種類型。git

seajs / require : 是一種在線」編譯」 模塊的方案,至關於在頁面上加載一個 CMD/AMD 解釋器。這樣瀏覽器就認識了 define、exports、module 這些東西。也就實現了模塊化。

browserify / webpack : 是一種預編譯模塊的方案,相比於上面 ,這個方案更加智能。這裏以webpack爲例。首先,它是預編譯的,不須要在瀏覽器中加載解釋器。另外,你在本地直接寫JS,無論是 AMD / CMD / ES6 風格的模塊化,它都能認識,而且編譯成瀏覽器認識的JS。

webpack 的配置

首先看一個簡單的 webpack 配置圖。
webpack.config.js:

Webpack的配置主要爲了這幾個項目:
devtool
devtool 屬性能夠配置調試代碼的方式,有多種調試方式。devtool 通常只在開發時使用,生產環境下應將值設爲 false。經常使用的值爲如下兩個:

  • eval
    能夠設斷點調試,不顯示列信息,每一個js模塊代碼用eval()執行,而且在生成的每一個模塊代碼尾部加上註釋,不會生成.map文件。
  • source-map
    能夠設斷點調試,不顯示列信息,生成相應的.Map文件,並在合併後的代碼尾部加上註釋//# sourceMappingURL=**.js.map ,能夠看到模塊代碼並無被eval()包裹,此種模式並無將調試信息放入D打包後的代碼中,保持了打包後代碼的簡潔性.

其餘還有eval-source-map、cheap-source-map、cheap-module-source-map、cheap-module-eval-source-map、hidden-source-map,也能夠本身組合,如cheap-module-eval-source-map。

// webpack.config.js
module.exports = {
   devtool: 'cheap-module-eval-source-map'
};

聽說cheap-module-eval-source-map絕大多數狀況下都會是最好的選擇,這也是下版本 webpack 的默認選項。

具體使用參考官網 devtool 部分

entry 和 output
entry 用來定義入口文件,能夠是個字符串或數組或者對象。
當 entry 是個字符串的時候:

module.exports = {
   entry: './main.js'
};

當 entry 是個數組的時候,裏面一樣包含入口 js 文件,另一個參數能夠是用來配置 webpack 提供的一個靜態資源服務器,webpack-dev-server。webpack-dev-server 會監控項目中每個文件的變化,實時的進行構建,而且自動刷新頁面:

module.exports = {
   entry: [
     'webpack/hot/only-dev-server' ,
     './main.js'
   ]
};

當 entry 是個對象的時候,咱們能夠將不一樣的文件構建成不一樣的文件,按需使用:

module.exports = {
   entry: {
     a: './a.js' ,
     b: './b.js'
   }
};

output 用於定義構建後的文件的輸出,是個對象。其中包含path和filename:

module.exports = {
   output: {
     path: './build' ,
     filename: 'bundle.js'
   }
};

若是有多個入口文件分開打包,能夠經過[name]來命名打包的輸出文件。

module.exports = {
   entry: {
     a: "./a" ,
     b: "./b" ,
     c: [ "./c" , "./d" ]
   },
   output: {
     path: path.join(__dirname, "dist" ),
     filename: "[name].entry.js"
   }
};

具體使用參考官網
entry 以及 output 部分

module
module 主要是用來配置加載器(Loaders),包括loaders、preLoaders、postLoaders、noParse。
webpack 自己只能處理 JavaScript 模塊,若是要處理其餘類型的文件,就須要使用 loader 進行轉換,本文第六章將會介紹一些經常使用的 Loaders。

loaders、preLoaders、postLoaders的配置選項包括如下幾方面:

  • test: 一個匹配loaders所處理的文件的拓展名的正則表達式(必須)
  • loader: loader的名稱(必須)
  • include/exclude: 手動添加必須處理的文件/文件夾,或屏蔽不須要處理的文件/文件夾(可選)
  • query: 爲loaders提供額外的設置選項(可選)
module.exports = {
   module: {
     preLoaders: [
       {test: /\.js$/, loader: "eslint-loader" , exclude: /node_modules/} //打包前使用eslint檢測js
     ],
     loaders: [
       {test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, //處理css文件,把less編譯成css
       {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } // 處理圖片,大小低於8k的文件編譯爲base64
     ]
   }
};

noParse是 webpack 的一個頗有用的配置項,若是你肯定一個模塊中沒有其它新的依賴就能夠配置這項,webpack 將再也不掃描這個文件中的依賴。

module: {
     noParse: [/moment- with -locales/]
}

具體使用參考官網 module 部分
官網也收集了經常使用的 Loaders 列表:list-of-loaders

resolve
resolve 用來配置文件路徑的指向。能夠定義文件跟模塊的默認路徑及後綴等,節省 webpack 搜索文件的時間、優化引用模塊時的體驗。經常使用的包括alias、extensions、root、modulesDirectories屬性:

  • alias:是個對象,把資源路徑重定向到另外一個路徑,好比require(‘React’)默認引用的是 /node_modules/react.js,咱們能夠定義用react.min.js替代
    //webpack.config.js
    ...
    resolve: {
       alias: {
         'someutil' : path.join(__dirname, './src/util/someutil' ),
         'react-dom' : path.join(nodeModulesPath, 'react-dom/dist/react-dom.min' )
       }
    }
    ...
    //app.js
    //配置resolve.alias前
    import react-dom form 'react-dom'  //此時默認載入'/node_modules/react-dom/dist/react-dom.js'
    import someutil form '../../src/util/someutil' //須要定位該文件相對位置
      
    //配置resolve.alias後
    import react-dom form 'react-dom'  //此時載入壓縮後的'react-dom.min.js'
    import someutil form 'someutil' //不須要再寫相對位置,由於已在resolve.alias中定義
  • extensions:是個數組,定義資源的默認後綴,好比定義後引用a.js、b.json、c.css等資源能夠不用寫後綴名直接寫a、b、c

     

    //webpack.config.js
    ...
    resolve: {
       extensions: [ '' , '.js' , '.jsx' , '.json' , '.css' ]
    }
    ...
    //app.js
    //配置resolve.extensions前
    import a form 'a.js' 
    import b form 'b.json'
    import c form 'c.css'
      
    //配置resolve.extensions後
    import a form 'a' 
    import b form 'b'
    import c form 'c'
  • root:是個數組,經過絕對路徑的方式來定義查找模塊的文件夾。能夠是一個數組,主要是用來增長模塊的搜尋位置使用的。root 的值必須是絕對路徑,使用path.resolve設置。
    //webpack.config.js
    var path = require( 'path' );
    resolve: {
       root: [
         path.resolve( './app/modules' ),
         path.resolve( './vendor/modules' )
       ]
    }
    //這樣設置後,會增長搜索app/modules和vendor/modules下全部node_modules裏面的模塊。
  • modulesDirectories:是個數組,是用來設置搜索的目錄名的,默認值:[「web_modules」, 「node_modules」]。若是把值設置成[「mydir」], webpack會查詢「./mydir」, 「../mydir」, 「../../mydir」等。
    //webpack.config.js
    ...
    resolve: {
       modulesDirectories: [ 'node_modules' , './src/component' ]
    }
    ...
    //app.js
    //配置resolve.modulesDirectories前
    import a form '../../src/component/a' 
    import b form '../../src/component/b'
      
    //配置resolve.modulesDirectories後
    import a form 'a' 
    import b form 'b'

具體使用參考官網 resolve 部分

plugins
插件,比 loader 更強大,能使用更多 webpack 的 api。webpack 自己內置了一些經常使用的插件,還能夠經過 npm 安裝第三方插件。本文第七章將會介紹一些經常使用的插件。

var HtmlWebpackPlugin = require( 'html-webpack-plugin' );
module.exports = {
   plugins: {
     new webpack.optimize.OccurenceOrderPlugin(), //爲組件分配ID
     new webpack.HotModuleReplacementPlugin(), //熱加載插件
     new webpack.optimize.UglifyJsPlugin(), //壓縮混淆js
     new webpack.NoErrorsPlugin(), //跳過編譯時出錯的代碼並記錄
     new ExtractTextPlugin( '[name]-[hash:5].min.css' ) //將css單獨打包並重命名
   }
}

具體使用參考官網內置 plugins 列表 list-of-plugins 部分,這裏有全部內置插件的詳細使用說明。

externals
當咱們想在項目中require一些其餘的類庫或者API,而又不想讓這些類庫的源碼被構建到運行時文件中。例如 React、jQuery 等文件咱們想使用 CDN 的引用,不想把他們打包進輸出文件。就能夠經過配置 externals 參數來配置:

module.exports = {
   externals: {
     jQuery: true
   }
};

而後在頁面裏引入< script src="//cdn/jquery.min.js">
這樣 jQuery 就不用打包了,直接指向 windows.jQuery 就好
externals 部分

其餘

除以上 6 個經常使用配置屬性外,webpack 的配置屬性還有contextcache、 profile等等,我的以爲並不經常使用,各位能夠前往官網 configuration 詳細查看。

經常使用 Loaders 介紹

babel-loader
babel-loader 用來編譯下一代 JavaScript,好比 ES六、React 等,編譯 ES六、React 的話還須要安裝 babel-preset-es201五、babel-preset-react。

eslint-loader
配合 eslint 用來規範糾錯 js,你可能同時還會須要 babel-eslinteslint-plugin-react

style-loader、css-loader、less-loader、sass-loader
處理 css、less、sass 文件

postcss-loader
用 postcss 來處理 CSS,最被經常使用的是其 autoprefixer 插件

resolve-url-loader
用來解析 css 裏的 url() 聲明裏的文件的相對路徑,使用 css-loader 時通常也必需要同時使用 resolve-url-loader

file-loader
用來處理文件名及路徑,好比給文件添加 hash,返回文件相對路徑等。

url-loader
與上面的 file-loader 相似,可是當文件小於設定的 limit 時能夠處理成 Data Url(base 64)。

raw-loader
把文件內容做爲字符串返回。例如var fileContent = require(‘raw!./file.txt’),這裏把./file.txt的內容做爲字符串返回。

imports-loader
用於向一個模塊的做用域內注入變量。
舉個栗子,好比咱們須要使用bootstrap.js,這個文件雖然依賴jQuery但其內部沒有require(‘jquery’),這致使文件內的jQuery對象不可識別,因此模塊化調用bootstrap.js時就會報錯jQuery is not defined`。使用 imports-loader 的話:

require( 'imports?jQuery=jquery!bootstrap/dist/js/bootstrap' );

imports-loader 會在 bootstrap 的源碼前面,注入以下代碼:

/* IMPORTS FROM imports-loader */
var jQuery = require( "jquery" );

exports-loader
用於向一個模塊中提供導出模塊功能。功能與imports-loader相似,但他是在文件的最後添加一行。
舉個栗子,好比file.js中沒有調用export導出模塊,或者沒有define定義模塊,所以沒法模塊化調用它。可使用 exports-loader:

require( "exports?file,parse=helpers.parse!./file.js" );

他會在./file.js文件的最後添加以下代碼:

/* EXPORTS FROM exports-loader */
exports[ "file" ] = (file);
exports[ "parse" ] = (helpers.parse);

expose-loader
這個 loader 是將某個對象暴露成一個全局變量。
舉個栗子,好比把jQuery對象暴露成全局變量。這樣,那些bootstrap.js之類的文件就都能訪問這個變量了。

module: {
   loaders: [
     { test: require.resolve( "jquery" ), loader: "expose?$!expose?jQuery" },
   ]
}

react-hot-loader
React 的網頁熱加載刷新 loader。同時須要配合使用 webpack-dev-server 。

經常使用插件介紹

具體的使用方法請在官網list-of-plugins查詢,本文只簡單介紹幾個經常使用插件。

CommonsChunkPlugin

插件用法比較多,經常使用的是把一些不常常更改的公共組件合併壓縮成一個 common 文件。有些類庫如utils, bootstrap之類的可能被多個頁面共享,最好是能夠合併成一個js,而非每一個js單獨去引用。這樣可以節省一些空間。

彷佛只有在多入口文件時才能把公共文件提取出來,但通常模塊化都建議單入口文件,因此我的以爲這個插件對我並無什麼卵用,有多入口需求的同窗可使用。

extract-text-webpack-plugin

第三方插件,將 CSS 打包成獨立文件,而非內聯。

HotModuleReplacementPlugin

代碼熱替換。

HtmlWebpackPlugin

生成 HTML 文件,配合 ExtractTextPlugin 能夠加入打包後的 js 和 css。

NoErrorsPlugin

報錯但不退出 webpack 進程。

OccurenceOrderPlugin

經過計算模塊出現次數來分配模塊。經過這個插件webpack能夠分析和優先考慮使用最多的模塊,併爲它們分配最小的ID。這個常常被使用能夠較快地得到模塊。這使得模塊能夠預讀,建議這樣能夠減小總文件大小。

ProvidePlugin

定義一個共用的插件入口。

new webpack.ProvidePlugin({
   $: "jquery" ,
   jQuery: "jquery"
})

這樣就可直接在文件中使用$,無需再require(‘jQuery’)。

UglifyJsPlugin

js 代碼壓縮混淆 。

DedupePlugin

檢測徹底相同(以及幾乎徹底相同)的文件,並把它們從輸出中移除。

DefinePlugin

主要用來定義全局的環境變量,以便咱們在本身的程序中引用它。

webpack-visualizer-plugin

一個第三方插件,能夠生成一個文件查看項目引用的全部模塊的佔比。

經常使用優化手段及技巧

區分開發及生產環境
前端開發環境一般分爲兩種:

  • 開發環境: 須要日誌輸出,sourcemap ,錯誤報告等等
  • 生產環境:須要作代碼壓縮,對文件名進行 hash 處理等等

爲了區分咱們能夠建立兩個文件分別進行不一樣環境下的配置:

  • webpack.config.dev.js // 開發環境
  • webpack.config.prod.js // 生產環境

同時 webpack 還提供了 DefinePlugin 插件來設置全局環境變量,後面會根據設置的不一樣環境變量決定是否打包壓縮,仍是啓動dev server 或者是 prod server

plugins: [
     new webpack.DefinePlugin({
         'process.env.NODE_ENV' : JSON.stringify( 'production' ) // or development
     })
]

判斷當前環境是不是生產環境

var isProduction = function () {
   return process.env.NODE_ENV === 'production' ;
}

使用代碼熱替換
使用代碼熱替換在開發的時候無需刷新頁面便可看到更新,只在開發環境的配置文件使用,具體配置以下。

npm install --save-dev webpack-dev-server webpack-dev-middleware express

把webpack/hot/dev-server加入到 webpack 配置文件的 entry 項:

<!-- webpack.config.dev.js -->
entry: [
     'webpack-dev-server/client?http://localhost:3000' ,
     './src/app.js'
],

把new webpack.HotModuleReplacementPlugin()加入到 webpack 配置文件的 plugins 項:

<!-- server.js -->
var webpack = require( 'webpack' );
var WebpackDevServer = require( 'webpack-dev-server' );
var config = require( './webpack.config' );
  
var compiler = webpack(config);
var server = new WebpackDevServer(compiler, {
   publicPath: config.output.publicPath,
   hot: true ,
   historyApiFallback: true ,
   stats: {
     colors: true ,
     hash: false ,
     timings: true ,
     chunks: false ,
     chunkModules: false ,
     modules: false
   }
});
  
server.listen(3000, 'localhost' , function (err, result) {
   if (err) {
     return console.log(err);
   }
   console.log( 'Listening at http://localhost:3000/' );
});

在 package.json 中定義啓動監聽熱加載:

<!-- package.json -->
"scripts" : {
   "start" : "node server.js"
}

如今你能夠經過運行npm start啓動服務器。

編譯 ES六、React
這裏主要是藉助 Babel 進行編譯。
安裝依賴:

npm install --save-dev babel-loader babel-core babel-preset-stage-0 babel-preset-es2015 babel-preset-react babel-preset-react-hmre

在項目根目錄新建個.babelrc文件:

<!-- .babelrc -->
{
   "presets" : [ "es2015" , "stage-0" , "react" ]
}

配置 webpack 配置文件的 module 項:

<!-- webpack.config.js -->
module: {
     loaders: [{
         test: /\.js$/,
         loaders: [ 'react-hot' , 'babel' ], //若生產環境下去掉'react-hot'
         include: path.join(__dirname, 'src' )
     }}]
}

更多請訪問 babel-loaderbabel-preset-react-hmre

使用 ESLint 檢查規範 js 代碼
ESLint 是個代碼錯誤與風格檢測工具,能夠輔助編碼規範執行,有效控制代碼質量。
ESLint 主要有如下特色:

  • 默認規則包含全部 JSLint、JSHint 中存在的規則,易遷移;
  • 規則可配置性高:可設置「警告」、「錯誤」兩個 error 等級,或者直接禁用;
  • 包含代碼風格檢測的規則(能夠丟掉 JSCS 了);
  • 支持插件擴展、自定義規則。

安裝依賴:

npm install --save-dev eslint eslint-loader eslint-plugin-react

項目根目錄新建.eslintrc文件,用來配置 ESLint 規則,如下爲常見規則:

<!-- .eslintrc -->
{
   "parserOptions" : { //EsLint經過parserOptions,容許指定校驗的ecma的版本,及ecma的一些特性
     "ecmaVersion" : 6, //指定ECMAScript支持的版本,6爲ES6
     "sourceType" : "module" , //指定來源的類型,有兩種」script」或」module」
     "ecmaFeatures" : { // ecmaFeatures指定你想使用哪些額外的語言特性
         "jsx" : true //啓動JSX
     }
   },
   "parser" : "babel-eslint" , // EsLint默認使用esprima作腳本解析,也能夠切換成babel-eslint解析
   "env" : { // Environment能夠預設好的其餘環境的全局變量,如brower、node環境變量、es6環境變量、mocha環境變量等
     "browser" : true ,
     "node" : true ,
     "es6" : true ,
     "mocha" : true
   },
   "plugins" : [ // EsLint容許使用第三方插件
     "react"
   ],
   extends: [ // Extends是EsLint默認推薦的驗證你可使用配置選擇哪些校驗是你所須要的
     "eslint:recommended"
   ],
   rules: [ // 自定義規則
     no-empty: [ "error" , { "allowEmptyCatch" : true }],
     "strict" : [2, "never" ],
     "react/jsx-uses-react" : "error" ,
     "react/jsx-uses-vars" : "error" ,
     "no-console" : [ "error" , { allow: [ "warn" , "error" , "log" ] }]
   ],
   "globals" : { // 即插件在執行過程當中用到的其它全局變量
   }
}

在項目根目錄新建.eslintignore 文件告訴 ESLint 去忽略特定的文件和目錄:

<!-- .eslintignore -->
dist
node_modules
dependent
coverage
webpack.*.js
*Server.js

在Sublime中安裝插件:
SublimeLinter
SublimeLinter-contrib-eslint

更多請訪問 eslintbabel-eslinteslint-plugin-reaceslint-loaderESLint 使用入門Lint Like It’s 2015

壓縮代碼
使用 webpack 內置的 UglifyJsPlugin 便可:

<!-- webpack.config.js -->
plugins: [
     new webpack.optimize.UglifyJsPlugin({
         compress: {
             warnings: false
         }
     })
]

Code Spliiting,分割代碼,按需加載
對於大型的web 應用而言,把全部的代碼放到一個文件的作法效率不好,特別是在加載了一些只有在特定環境下才會使用到的阻塞的代碼的時候。Webpack有個功能會把你的代碼分離成Chunk,後者能夠按需加載。這個功能就是 Code Spliiting
Code Spliting的具體作法就是一個分離點,在分離點中依賴的模塊會被打包到一塊兒,能夠異步加載。一個分離點會產生一個打包文件。

本文這裏主要是基於 React 與 React-Router 的 Code Spliiting。開篇說了 webpack 目前已經更新到 2.x 版本, 1.x 與 2.x 的 Code Spliiting 方法略有不一樣,這裏分開來說。

webpack 1.x 的例子:

<router history= "{history}" >
   <route path= "/" getcomponent= "{(location," callback)= "" > {
       require.ensure([], function (require) {
         callback( null , require( './HomePage.jsx' ));
       });
     }}
   />
   <route path= "/about" getcomponent= "{(location," callback)= "" > {
       require.ensure([], function (require) {
         callback( null , require( './AboutPage.jsx' ));
       });
     }}
   />
</route></route></router>

webpack 2.x 的例子:

class Page extends React.Component {
   render() {
     <route path= "{this.props.path}" getcomponent= "{(location," callback)= "" > {
         System.import( this .props.component)
           .then((component) => {
             callback( null , component);
           });
       }
   }
}
</route>
<router history= "{history}" >
   <page path= "/about" component= "./AboutPage.jsx" >
</page></router>

使用 DllPlugin 和 DllReferencePlugin 分割代碼
經過 DllPlugin 和 DllReferencePlugin,webpack 引入了另一種代碼分割的方案。咱們能夠將經常使用的庫文件打包到 dll 包中,而後在 webpack 配置中引用。業務代碼的能夠像往常同樣使用 require 引入依賴模塊,好比 require(‘react’), webpack 打包業務代碼時會首先查找該模塊是否已經包含在 dll 中了,只有 dll 中沒有該模塊時,webpack 纔將其打包到業務 chunk 中。

首先咱們使用 DllPlugin 將經常使用的庫打包在一塊兒:

var webpack = require( 'webpack' );
module.exports = {
   entry: {
     vendor: [ 'lodash' , 'react' ],
   },
   output: {
     filename: '[name].[chunkhash].js' ,
     path: 'build/' ,
   },
   plugins: [ new webpack.DllPlugin({
     name: '[name]_lib' ,
     path: './[name]-manifest.json' ,
   })]
};

該配置會產生兩個文件,模塊庫文件:vender.[chunkhash].js 和模塊映射文件:vender-menifest.json。其中 vender-menifest.json 標明瞭模塊路徑和模塊 ID(由 webpack 產生)的映射關係,其文件內容以下:

{
   "name" : "vendor_lib" ,
   "content" : {
     "./node_modules/.npminstall/lodash/4.17.2/lodash/lodash.js" : 1,
     "./node_modules/.npminstall/webpack/1.13.3/webpack/buildin/module.js" : 2,
     "./node_modules/.npminstall/react/15.3.2/react/react.js" : 3,
     ...
     }
}  

而後在業務代碼的 webpack 配置文件中使用 DllReferencePlugin 插件引用:

var webpack = require( 'webpack' );
module.exports = {
   entry: {
     app: [ './app' ],
   },
   output: {
     filename: '[name].[chunkhash].js' ,
     path: 'build/' ,
   },
   plugins: [ new webpack.DllReferencePlugin({
     context: '.' ,
     manifest: require( './vendor-manifest.json' ),
   })]
};

須要注意的是:dll包的代碼是不會執行的,須要在業務代碼中經過require顯示引入。
編譯 less/sass、自動添加瀏覽器前綴、將 css 單獨打包
我用的是 less,因此這裏以 less 爲例。
安裝編譯 less 的依賴:

$ npm install css-loader style-loader less less-loader --save-dev

安裝處理 css 內 url() 聲明路徑的依賴:

$ npm install resolve-url-loader --save-dev

安裝 autoprefixer 依賴:

$ npm install postcss-loader autoprefixer  --save-dev

配置 webpack 配置文件的 module、postcss項:

<!-- webpack.config.js -->
module: {
     loaders: [{
         test: /\.less$/,
         loader: 'style' , 'css?modules&importLoaders=1&localIdentName=[name]-[local]-[hash:base64:5]!resolve-url!postcss!less'
         include: path.join(__dirname, 'src' )
     }}]
},
postcss: [
     require( 'autoprefixer' )
]

生產環境下咱們可能會須要把 css 單獨打包出來,這時須要用到 ExtractTextPlugin 插件 :

npm install extract-text-webpack-plugin --save-dev
var ExtractTextPlugin = require( 'extract-text-webpack-plugin' );
...
plugins: [
     new ExtractTextPlugin( '[name]-[hash:5].min.css' )
],
module: {
     loaders: [{
         test: /\.less$/,
         loader: ExtractTextPlugin.extract( 'style' , 'css?modules&importLoaders=1&localIdentName=[name]-[local]-[hash:base64:5]!resolve-url!postcss!less' )
         include: path.join(__dirname, 'src' )
     }}]
},
postcss: [
     require( 'autoprefixer' )
]
...

壓縮圖片、將圖片轉爲 base64
圖片處理常見的loader有如下三種:

  • file-loader: 默認狀況下會根據圖片生成對應的 MD5 hash 的文件格式
  • url-loader: url-loader相似於file-loader,可是url-loader能夠根據自定義文件大小或者轉化爲 base64 格式的 dataUrl,或者單獨做爲文件,也能夠自定義對應的 hash 文件名
  • image-webpack-loader: 提供壓縮圖片的功能

安裝依賴:

npm url-loader image-webpack-loader --save-dev
module: {
   loaders: [
     {
       test: /\.(jpe?g|png|gif|svg)$/i,
       loaders: [
         'url?limit=10000&name=img/[hash:8].[name].[ext]' , // 圖片小於8k就轉化爲 base64, 或者單獨做爲文件
         'image-webpack' // 圖片壓縮
       ]
     }
   ]
}

給文件添加 hash 緩存、自動生成頁面
在 output 項給生成文件添加 hash:

output: {
     ...
     filename: '[chunkhash:8].bundle.js' // chunkhash 默認是16位,可自定義配置
     ...
}

文件名帶上 hash 值後,這個值在每次編譯的時候都會發生變化,都須要在 html 文件裏手動修改引用的文件名,這種重複工做很瑣碎且容易出錯,這裏咱們可使用 html-webpack-plugin 來幫咱們自動處理這件事情:

// 在 app 目錄下建一個 index.tpl.html 做爲鉤子
 
   
     <meta charset= "UTF-8" >
   
   
     <div id= "root" ></div>
  
// 在 webpack.config.dev.js 和 webpack.config.prod.js 添加配置代碼,便可生成相對應的 index.html
plugins: [
   new HtmlWebpackPlugin({
     template: 'app/index.tpl.html' ,
     minify: {
         removeComments: true ,
         collapseWhitespace: true ,
         removeRedundantAttributes: true ,
         useShortDoctype: true ,
         removeEmptyAttributes: true ,
         removeStyleLinkTypeAttributes: true ,
         keepClosingSlash: true ,
         minifyJS: true ,
         minifyCSS: true ,
         minifyURLs: true ,
     },
     inject: 'body' ,
     filename: 'index.html'
   })
]

使用 Fetch
我我的的項目裏 Fetch 已經徹底替代 Ajax 了,使用 Fetch 爲了兼容舊瀏覽器須要使用一些膩子腳本,咱們能夠將膩子腳本暴露到全局。這裏主要使用前文中提到的import-loader、exports-loader。
安裝依賴:

npm install imports-loader exports-loader --save-dev 
npm install es6-promise whatwg-fetch --save 

配置 webpack 配置文件的 plugins 項:

plugins: [
     new webpack.ProvidePlugin({
         'Promise' : 'es6-promise' ,
         'fetch' : 'imports?this=>global!exports?global.fetch!whatwg-fetch'
     }),
  ],

這樣就能夠直接在開發文件中使用 Fetch。

總結

再次說明下本文的 配套 DEMO 請訪問 https://github.com/tumars/boilerplate-webpack-react-es6-cssModule 查看。
本文還會繼續更新修改,歡迎各位交流指教

相關文章
相關標籤/搜索