Webpack 是當下最熱門的前端資源模塊化管理和打包工具。css
Webpack 是當下最熱門的前端資源模塊化管理和打包工具。它能夠將許多鬆散的模塊按照依賴和規則打包成符合生產環境部署的前端資源。還能夠將按需加載的模塊進行代碼分隔,等到實際須要的時候再異步加載。經過 loader 的轉換,任何形式的資源均可以視做模塊,好比 CommonJs 模塊、 AMD 模塊、 ES6 模塊、CSS、圖片、 JSON、Coffeescript、 LESS 等。html
Webpack 和其餘模塊化工具備什麼區別呢?前端
首先要安裝 Node.js, Node.js 自帶了軟件包管理器 npm,Webpack能夠經過npm去安裝。node
用 npm 安裝 Webpack:react
$ npm install webpack -g
此時 Webpack 已經安裝到了全局環境下,能夠經過命令行 webpack -h 試試。jquery
一般咱們會將 Webpack 安裝到項目的依賴中,這樣就可使用項目本地版本的 Webpack。webpack
# 進入項目目錄 # 肯定已經有 package.json,沒有就經過 npm init 建立 # 安裝 webpack 依賴 $ npm install webpack --save-dev
查看webpackgit
# 查看 webpack 版本信息 $ npm info webpack
安裝指定版本的 webpackes6
$ npm install webpack@1.12.x --save-dev
若是須要使用 Webpack 開發工具,要單獨安裝:github
$ npm install webpack-dev-server --save-dev
首先建立一個靜態頁面 index.html 和一個 JS 入口文件 entry.js:
<!-- index.html --> <html> <head> <meta charset="utf-8"> </head> <body> <script src="bundle.js"></script> </body> </html>
// entry.js document.write('It works.')
而後編譯 entry.js 並打包到 bundle.js:
$ webpack entry.js bundle.js
打包過程會顯示日誌:
Hash: e964f90ec65eb2c29bb9 Version: webpack 1.12.2 Time: 54ms Asset Size Chunks Chunk Names bundle.js 1.42 kB 0 [emitted] main [0] ./entry.js 27 bytes {0} [built]
用瀏覽器打開 index.html 將會看到 It works. 。 接下來添加一個模塊 module.js 並修改入口 entry.js:
// module.js module.exports = 'It works from module.js.'
// entry.js document.write('It works.') document.write(require('./module.js')) // 添加模塊
從新打包 webpack entry.js bundle.js 後刷新頁面看到變化
It works.It works from module.js. Hash: 279c7601d5d08396e751 Version: webpack 1.12.2 Time: 63ms Asset Size Chunks Chunk Names bundle.js 1.57 kB 0 [emitted] main [0] ./entry.js 66 bytes {0} [built] [1] ./module.js 43 bytes {0} [built]
Webpack 會分析入口文件,解析包含依賴關係的各個文件。這些文件(模塊)都打包到 bundle.js 。Webpack 會給每一個模塊分配一個惟一的 id 並經過這個 id 索引和訪問模塊。在頁面啓動時,會先執行 entry.js 中的代碼,其它模塊會在運行 require 的時候再執行。
Webpack 自己只能處理 JavaScript 模塊,若是要處理其餘類型的文件,就須要使用 loader 進行轉換。
Loader 能夠理解爲是模塊和資源的轉換器,它自己是一個函數,接受源文件做爲參數,返回轉換的結果。這樣,咱們就能夠經過 require 來加載任何類型的模塊或文件,好比 CoffeeScript、 JSX、 LESS 或圖片。
先來看看 loader 有哪些特性?
loader 通常以 xxx-loader
的方式命名,xxx 表明了這個 loader 要作的轉換功能,好比 json-loader
。
Loader 能夠在 require() 引用模塊的時候添加,也能夠在 webpack 全局配置中進行綁定,還能夠經過命令行的方式使用。
接上一節的例子,咱們要在頁面中引入一個 CSS 文件 style.css,首頁將 style.css 也當作是一個模塊,而後用 css-loader 來讀取它,再用 style-loader 把它插入到頁面中。
/* style.css */ body { background: yellow; }
修改 entry.js:
require("!style-loader!css-loader!./style.css") // 載入 style.css document.write('It works.') document.write(require('./module.js'))
安裝 loader:
npm install css-loader style-loader
從新編譯打包,刷新頁面,就能夠看到黃色的頁面背景了。
若是每次 require CSS 文件的時候都要寫 loader 前綴,是一件很繁瑣的事情。咱們能夠根據模塊類型(擴展名)來自動綁定須要的 loader。
將 entry.js 中的 require("!style-loader!css-loader!./style.css")
修改成 require("./style.css")
,而後執行:
$ webpack entry.js bundle.js --module-bind 'css=style!css'
# 有些環境下可能須要使用雙引號 $ webpack entry.js bundle.js --module-bind "css=style!css"
顯然,這兩種使用 loader 的方式,效果是同樣的。
Webpack 在執行的時候,除了在命令行傳入參數,還能夠經過指定的配置文件來執行。默認狀況下,會搜索當前目錄的 webpack.config.js 文件,這個文件是一個 node.js 模塊,返回一個 json 格式的配置信息對象,或者經過 --config 選項來指定配置文件。
建立一個配置文件 webpack.config.js:
var webpack = require('webpack') module.exports = { entry: './entry.js', output: { path: __dirname, filename: 'bundle.js' }, module: { loaders: [ {test: /\.css$/,loader: 'style-loader!css-loader'}] } }
webpack.config.js文件一般放在項目的根目錄中,它自己也是一個標準的Commonjs規範的模塊。在導出的配置對象中有幾個關鍵的參數:
entry參數定義了打包後的入口文件,有三種寫法,每一個入口稱爲一個chunk:
類型 | 示例 | 解釋 |
---|---|---|
字符串 | entry: "./index/index.js" | 配置模塊會被解析爲模塊,並在啓動時加載。 chunk名爲默認爲main, 具體打包文件名視output配置而定。 |
數組 | entry: ['./src/mod1.js', [...,] './src/index.js'] | 全部的模塊會在啓動時 按照配置順序 加載,合併到最後一個模塊會被導出。chunk名默認爲main |
對象 | entry:{index: '...', login : [...]} | 若是傳入Object ,則會生成多個入口打包文件key 是chunk 名,value 能夠是字符串,也但是數組。 |
{ entry: { page1: "./page1", //支持數組形式,將加載數組中的全部模塊,但以最後一個模塊做爲輸出 page2: ["./entry1", "./entry2"] }, output: { path: "dist/js/page", publicPath: "/output/", filename: "[name].bundle.js" } }
該段代碼最終會生成一個 page1.bundle.js 和 page2.bundle.js,並存放到 ./dist/js/page 文件夾下
output參數是個對象,定義了輸出文件的位置及名字:
output: { path: "dist/js/page", publicPath: "/output/", filename: "[name].bundle.js" }
當咱們在entry中定義構建多個文件時,filename能夠對應的更改成[name].js用於定義不一樣文件構建後的名字。
在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,圖片等靜態文件都是模塊,不一樣模塊的加載是經過模塊加載器(webpack-loader)來統一管理的。loaders之間是能夠串聯的,一個加載器的輸出能夠做爲下一個加載器的輸入,最終返回到JavaScript上: loader使用須要先安裝再加入到配置下中。
Loaders須要單獨安裝而且須要在webpack.config.js下的modules關鍵字下進行配置,Loaders的配置選項包括如下幾方面:
module: { //加載器配置 loaders: [ //.css 文件使用 style-loader 和 css-loader 來處理 { test: /\.css$/, loader: 'style-loader!css-loader' }, //.less 文件使用 less-loader、cssloader來編譯 { test:/\.less$/, loader: 'style!less'} //.scss 文件使用 style-loader、css-loader 和 sass-loader 來編譯處理 { test: /\.scss$/, loader: 'style!css!sass?sourceMap'}, ] }
babel-loader babel官網
// npm一次性安裝多個依賴模塊,模塊之間用空格隔開 npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
module: { loaders: [ { test:/\.jsx?$/, exclude:/node_modules/, loader:'babel', query:{presets:['react','es2015']} } ] }
npm install babel-loader --save-dev npm install babel-preset-es2015 --save-dev
{ "presets": ["es2015"] }
npm install --save-dev url-loadr
module: { loaders: [ { test: /\.(png|jpg|gif)$/, loader: 'url-loader?limit=10000&name=build/images/[name].[ext]' } ] }
對於上面的配置,若是圖片資源小於10kb就會轉化成 base64 格式的 dataUrl,其餘的圖片會存放在build/images文件夾下。
npm install --save-dev file-loader
module: { loaders: [ { test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/, loader: 'file' }, ] }
module: { //加載器配置 loaders: [ //.css 文件使用 style-loader 和 css-loader 來處理 { test: /\.css$/, loader: 'style-loader!css-loader' }, //.scss 文件使用 style-loader、css-loader 和 sass-loader 來編譯處理 { test: /\.scss$/, loader: 'style!css!sass?sourceMap'}, //圖片文件使用 url-loader 來處理,小於8kb的直接轉爲base64 { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} ] }
插件(Plugins)是用來拓展Webpack功能的,它們會在整個構建過程當中生效,執行相關的任務。
Webpack有不少內置插件,同時也有不少第三方插件,可讓咱們完成更加豐富的功能。
要使用某個插件,咱們須要經過npm安裝它,而後要作的就是在webpack配置中的plugins關鍵字部分添加該插件的一個實例(plugins是一個數組)繼續看例子,咱們添加了一個實現版權聲明的插件。
module.exports = { plugins: [ new webpack.BannerPlugin("Copyright Nico inc.") //在這個數組中new一個就能夠了 ] }
這個插件的做用是依據一個簡單的模板,幫你生成最終的HTML5文件,這個文件中自動引用了你打包後的JS文件。每次編譯都在文件名中插入一個不一樣的哈希值。
安裝
npm install --save-dev html-webpack-plugin
這個插件自動完成了咱們以前手動作的一些事情,在正式使用以前須要對一直以來的項目結構作一些改變:
在app目錄下,建立一個Html文件模板,這個模板包含title等其它你須要的元素,在編譯過程當中,本插件會依據此模板生成最終的html頁面,會自動添加所依賴的 css, js,favicon等文件,在本例中咱們命名模板文件名稱爲index.tmpl.html,模板源代碼以下
index.tmpl.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Webpack</title> </head> <body> <div id='box'> </div> </body> </html>
webpack.config.js
var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins: [ new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html"//new 一個這個插件的實例,並傳入相關的參數 }) ] }
分離CSS和JS文件
安裝
npm install --save-dev extract-text-webpack-plugin
webpack.config.js
var webpack = require('webpack'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); ... plugins: [ new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html" }), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin(), new ExtractTextPlugin("style.css") ] }
項目中,對於一些經常使用的組件,站點公用模塊常常須要與其餘邏輯分開,而後合併到同一個文件,以便於長時間的緩存。要實現這一功能,配置參照:
var webpack = require('webpack'); var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; ··· entry: { a: './index/a.js', b: './idnex/b.js', c: './index/c.js', d: './index/d.js' }, ··· plugins: [ new CommonsChunkPlugin('part.js', ['a', 'b']), new CommonsChunkPlugin('common.js', ['part1', 'c']) ] ···
webpack 自帶了一個壓縮插件 UglifyJsPlugin,只須要在配置文件中引入便可。
{ plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] }
加入了這個插件以後,編譯的速度會明顯變慢,因此通常只在生產環境啓用。
緩存無處不在,使用緩存的最好方法是保證你的文件名和文件內容是匹配的(內容改變,名稱相應改變) webpack能夠把一個哈希值添加到打包的文件名中,使用方法以下,添加特殊的字符串混合體([name], [id] and [hash])到輸出文件名前
var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { entry: __dirname + "/app/main.js", output: { path: __dirname + "/build", filename: "[name]-[hash].js" }, module: {}, plugins: [ new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html" }), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin(), new ExtractTextPlugin("[name]-[hash].css") ] }
當咱們常用React、jQuery等外部第三方庫的時候,一般在每一個業務邏輯JS中都會遇到這些庫。 如咱們須要在各個文件中都是有jQuery的$對象,所以咱們須要在每一個用到jQuery的JS文件的頭部經過require('jquery')來依賴jQuery。 這樣作很是繁瑣且重複,所以webpack提供了咱們一種比較高效的方法,咱們能夠經過在配置文件中配置使用到的變量名,那麼webpack會自動分析,而且在編譯時幫咱們完成這些依賴的引入。
webpack.config.js中
var webpack = require('webpack'); ... plugins: [ new webpack.ProvidePlugin({ 'Moment': 'moment', "$": "jquery", "jQuery": "jquery", "window.jQuery": "jquery", "React": "react" }) ] ...
要告訴webpack咱們但願當前是什麼環境,只須要在命令中寫入 BUILD_DEV=1 webpck 那麼webpack經過配置,就會將全部咱們引用到的__DEV__變量設置爲true。
咱們能夠在package.json中事先定義好命令:
"scripts": { "dev": "BUILD_DEV=1 webpack-dev-server --progress --colors", "build": "BUILD_PRERELEASE=1 webpack -p" }
那麼就能夠避免輸入冗長的命令了:
開發時輸入:
npm run dev
發佈時輸入:
npm run build
webpack的使用和browserify有些相似,下面列舉幾個經常使用命令:
webpack 最基本的啓動webpack命令 webpack -w 提供watch方法,實時進行打包更新 webpack -p 對打包後的文件進行壓縮 webpack -d 提供SourceMaps,方便調試 webpack --colors 輸出結果帶彩色,好比:會用紅色顯示耗時較長的步驟 webpack --profile 輸出性能數據,能夠看到每一步的耗時 webpack --display-modules 默認狀況下 node_modules 下的模塊會被隱藏,加上這個參數能夠顯示這些被隱藏的模塊
/ ├── shell/ 腳本 │ ├── conf/ 工程配置 │ ├── src/ 開發目錄 │ ├── components/ 組件 │ ├── pages/ 頁面 │ ├── ├── dist/ 自動生成 │ ├── test/ 測試 │ ├── node_modules/ 自動生成,包含.Node 依賴以及開發依賴 │ ├── static/ 庫文件等,不會被webpack的loader處理,手動管理 │ └── etc
完整的目錄結構:
projectTemplate/ ├── shell/ node腳本 │ ├── │ ├── dev-server.js 本地開發服務器 │ ├── build.js 打包腳本 │ ├── utils.js 工具函數 │ ├── ├── conf/ 工程配置 │ ├── │ ├── index.js 基礎配置文件,在此可簡單的修改webpack相關配置 │ ├── webpack.base.js webpack的基礎配置,主要是loader、resolve的配置 │ ├── webpack.dev.js webpack開發配置,主要是eslint、livereload、hot module replacement及相關的插件 │ ├── webpack.prod.js webpack生產配置,主要是代碼的壓縮混淆,圖片壓縮,加hash │ ├── karma.conf.js 測試配置 │ ├── ├── src/ 開發目錄 │ ├── components/ 組件 │ ├── pages/ 頁面(頁面下的項目目錄須要遵循必定的規範以便建立webpack的入口文件,不過這些規範是能夠調整的;如下只是推薦) │ ├── index/ 首頁 │ ├── images/ 圖片資源 │ ├── page.css 樣式文件,文件名稱能夠按照本身意願命名 │ ├── page.js 腳本文件及webpack的入口文件,文件名稱能夠在/conf/index.js配置 │ ├── template.html 模板文件及要撰寫的html文件,文件名稱能夠在/conf/index.js配置 │ ├── │ ├── dist/ 自動生成 │ ├── test/ 測試(目錄能夠意願來建立,可是測試文件名稱必須遵循*_test.js的命名規範,可在/conf/karma.conf.js修改配置) │ ├── node_modules/ 自動生成,包含node依賴以及開發依賴 │ ├── static/ 庫文件等,不會被webpack的loader處理,手動管理 │ └── etc
想不想讓你的瀏覽器監測你都代碼的修改,並自動刷新修改後的結果,其實Webpack提供一個可選的本地開發服務器,這個本地服務器基於node.js構建,能夠實現你想要的這些功能,不過它是一個單獨的組件,在webpack中進行配置以前須要單獨安裝它做爲項目依賴
npm install --save-dev webpack-dev-server
devserver做爲webpack配置選項中的一項,具備如下配置選項
devserver配置選項 | 功能描述 |
---|---|
contentBase | 默認webpack-dev-server會爲根文件夾提供本地服務器, 若是想爲另一個目錄下的文件提供本地服務器, 應該在這裏設置其所在目錄(本例設置到「public"目錄) |
port | 設置默認監聽端口,若是省略,默認爲」8080「 |
inline | 設置爲true,當源文件改變時會自動刷新頁面 |
colors | 設置爲true,使終端輸出的文件爲彩色的 |
historyApiFallback | 在開發單頁應用時很是有用,它依賴於HTML5 history API, 若是設置爲true,全部的跳轉將指向index.html |
以下配置:
module.exports = { ... devServer: { contentBase: "./public",//本地服務器所加載的頁面所在的目錄 colors: true,//終端中輸出結果爲彩色 historyApiFallback: true,//不跳轉 inline: true//實時刷新 } }