轉:Webpack 指南(整理 草稿)

基礎

安裝

首先要安裝 Node.js, Node.js 自帶了軟件包管理器 npm。用 npm 全局安裝 Webpack:javascript

npm install webpack -g

一般咱們會將 Webpack 安裝到項目的依賴中,這樣就可使用項目本地版本的 Webpack。php

# 進入項目目錄,初始化,建立 package.json。 # 若存在 package.json 文件,則不運行。 $ npm init # 肯定已經有 package.json # 安裝 webpack 依賴 $ npm install webpack --save-dev

若是須要使用 Webpack 開發工具,要單獨安裝:css

$ npm install webpack-dev-server --save-dev

使用

首先建立一個靜態頁面 index.html 和一個 JS 入口文件 entry.jshtml

<!-- 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

用瀏覽器打開 index.html 將會看到java

It works.

最終目錄結構以下:node

.
├── entry.js
├── index.html ├── package.json ├── node_modules

接下來添加一個模塊 module.js 並修改入口 entry.jsreact

// 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 後刷新頁面看到變化jquery

It works.It works from module.js.

最終目錄結構以下:webpack

.
├── bundle.js
├── entry.js
├── index.html
├── module.js ├── package.json ├── node_modules

進階

使用 Loader

Webpack 自己只能處理 JavaScript 模塊,若是要處理其餘類型的文件,就須要使用 loader 進行轉換。Loader 能夠理解爲是模塊和資源的轉換器,它自己是一個函數,接受源文件做爲參數,返回轉換的結果。這樣,咱們就能夠經過 require 來加載任何類型的模塊或文件,好比 CoffeeScript、 JSX、 LESS 或圖片。

先來看看 loader 有哪些特性?

  • Loader 能夠經過管道方式鏈式調用,每一個 loader 能夠把資源轉換成任意格式並傳遞給下一個 loader ,可是最後一個 loader 必須返回 JavaScript。

  • Loader 能夠同步或異步執行。

  • Loader 運行在 node.js 環境中,因此能夠作任何可能的事情。

  • Loader 能夠接受參數,以此來傳遞配置項給 loader。

  • Loader 能夠經過文件擴展名(或正則表達式)綁定給不一樣類型的文件。

  • Loader 能夠經過 npm 發佈和安裝。

  • 除了經過 package.json 的 main 指定,一般的模塊也能夠導出一個 loader 來使用。

  • Loader 能夠訪問配置。

  • 插件可讓 loader 擁有更多特性。

  • Loader 能夠分發出附加的任意文件。

Loader 自己也是運行在 node.js 環境中的 JavaScript 模塊,它一般會返回一個函數。大多數狀況下,咱們經過 npm 來管理 loader,可是你也能夠在項目中本身寫 loader 模塊。

按照慣例,而非必須,loader 通常以 xxx-loader 的方式命名,xxx 表明了這個 loader 要作的轉換功能,好比 json-loader

除了npm安裝模塊的時候之外,在任何場景下,loader名字都是能夠簡寫的。例如:安裝時必須用全名,即:npm install json-loader,而在引用 loader 的時候可使用全名 json-loader,也可使用短名 json。這個命名規則和搜索優先級順序在 webpack 的 resolveLoader.moduleTemplates api 中定義。

Default: ["*-webpack-loader", "*-web-loader", "*-loader", "*"]

Loader 能夠在 require() 引用模塊的時候添加,也能夠在 webpack 全局配置中進行綁定,還能夠經過命令行的方式使用。

loader是能夠串聯使用的,也就是說,一個文件能夠先通過A-loader再通過B-loader最後再通過C-loader處理。而在通過全部的loader處理以前,webpack會先取到文件內容交給第一個loader。

接上一節的例子,咱們要在頁面中引入一個 CSS 文件 style.css,首先將 style.css 也當作是一個模塊,而後用 css-loader來讀取處理(路徑處理、import處理等),而後通過 style-loader 處理(包裝成JS文件,運行的時候直接將樣式插入DOM中)。

/* style.css */ body { background: yellow; }

修改 entry.js

// entry.js require("!style!css!./style.css") // 載入 style.css document.write('It works.') document.write(require('./module.js'))

安裝 loader:

# css-loader:讀取 css 文件 # style-loader:將 css 文件插入頁面 $ npm install css-loader style-loader --save-dev

從新編譯打包,刷新頁面,就能夠看到黃色的頁面背景了。

若是每次 require CSS 文件的時候都要寫 loader 前綴,是一件很繁瑣的事情。咱們能夠根據模塊類型(擴展名)來自動綁定須要的 loader。

將 entry.js 中的 require("!style!css!./style.css") 修改成 require("./style.css") ,而後執行:

$ webpack entry.js bundle.js --module-bind 'css=style!css'

顯然,這兩種使用 loader 的方式,效果是同樣的。最終的目錄結構以下:

.
├── bundle.js
├── entry.js
├── index.html
├── module.js ├── node_modules ├── package.json ├── style.css

loader還能夠接受參數,不一樣的參數可讓loader有不一樣的行爲(前提是loader確實支持不一樣的行爲),具體每一個loader支持什麼樣的參數能夠參考loader的文檔。loader的使用有三種方法,分別是:

  • 在require中顯式指定,如:

  • 在命令行中指定,如:$ webpack entry.js output.js --module-bind 'css=style!css'

  • 在配置項(webpack.config.js)中指定,如:

第一種顯式指定,即在 JS 文件中指定:

require('style!css!./style.css');`

第二種在命令行中指定參數的用法用得較少,能夠這樣寫:

$ webpack --module-bind jade --module-bind 'css=style!css'

使用 --module-bind 指定loader,若是後綴和loader同樣,直接寫就行了,好比jade表示.jade文件用jade-loader處理,若是不同,則須要顯示指定,如 css=style!css 表示分別使用 css-loader 和 style-loader 處理 .css 文件。

第三種在配置項中指定是最靈活的方式,它的指定方式是這樣:

module: { // loaders是一個數組,每一個元素都用來指定loader loaders: [{ test: /\.jade$/, //test值爲正則表達式,當文件路徑匹配時啓用 loader: 'jade', //指定使用什麼loader,能夠用字符串,也能夠用數組 exclude: /regexp/, //可使用exclude來排除一部分文件 //可使用query來指定參數,也能夠在loader中用和require同樣的用法指定參數,如`jade?p1=1` query: { p1:'1' } }, { test: /\.css$/, loader: 'style!css' //loader能夠和require用法同樣串聯 }, { test: /\.css$/, loaders: ['style', 'css'] //也能夠用數組指定loader }] }

注意: 用數組指定串聯loader時,配置文件中要寫 loaders,而非 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!css' } ] } }

同時簡化 entry.js 中的 style.css 加載方式:

require('./style.css')

最後運行 webpack,能夠看到 webpack 經過配置文件執行的結果和上一節經過命令行 webpack entry.js bundle.js --module-bind 'css=style!css' 執行的結果是同樣的。

插件

插件能夠完成更多 loader 不能完成的功能。
插件的使用通常是在 webpack 的配置信息 plugins 選項中指定。
Webpack 自己內置了一些經常使用的插件,還能夠經過 npm 安裝第三方插件。
接下來,咱們利用一個最簡單的 BannerPlugin 內置插件來實踐插件的配置和運行,這個插件的做用是給輸出的文件頭部添加註釋信息。

修改 webpack.config.js,添加 plugins:

var webpack = require('webpack') module.exports = { entry: './entry.js', output: { path: __dirname, filename: 'bundle.js' }, module: { loaders: [ {test: /\.css$/, loader: 'style!css'} ] }, plugins: [ new webpack.BannerPlugin('This file is created by zhaoda') ] }

而後運行 webpack,打開 bundle.js,能夠看到文件頭部出現了咱們指定的註釋信息:

/*! This file is created by zhaoda */ /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; // 後面代碼省略

參數詳解

entry

entry參數定義了打包後的入口文件,能夠是個字符串或數組或者是對象;若是是數組,數組中的全部文件會打包生成一個filename文件;若是是對象,能夠將不一樣的文件構建成不一樣的文件:

{ entry: { page1: "./page1", //支持數組形式,將加載數組中的全部模塊,但以最後一個模塊做爲輸出 page2: ["./entry1", "./entry2"], // 若是想保持目錄結構,則直接按照目錄結構命名 'subapp1/page': './app/subapp1/page.js', 'subapp2/page': './app/subapp2/page.js', }, output: { path: "dist/js/page", publicPath: "/output/", filename: "[name].bundle.js" } }

該段代碼最終會在 ./dist/js/page 文件夾下生成以下結構:

│  page1.bundle.js 
│  page2.bundle.js
│
├─subapp1
│      page.bundle.js
│      
└─ssubapp2
       page.bundle.js

保持目錄結構命名的方式,在構架大型應用中很是有用。

output

output參數是個對象,定義了輸出文件的位置及名字:

output: { path: path.resolve(__dirname, 'dist'), publicPath: 'http://localhost:3000/static/', filename: "js/[name].bundle.js" }
  • path: 打包文件存放的絕對路徑

  • publicPath: 網站運行時的訪問路徑

  • filename:打包後的文件名

當咱們在entry中定義構建多個文件時,filename能夠對應的更改成[name].js用於定義不一樣文件構建後的名字。
以下 'http://localhost:3000/static/' 通常咱們作調試時的路徑,若是咱們要在網頁中引用 js 文件,html 文件中的路徑寫爲:http://localhost:3000/static/js/<name>.bundle.js
即 <publicPath>+<filename>

module

在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,圖片等靜態文件都是模塊,不一樣模塊的加載是經過模塊加載器(webpack-loader)來統一管理的。loaders之間是能夠串聯的,一個加載器的輸出能夠做爲下一個加載器的輸入,最終返回到JavaScript上:

module: { //加載器配置 loaders: [ //.css 文件使用 style-loader 和 css-loader 來處理 { test: /\.css$/, loader: 'style-loader!css-loader' }, //.js 文件使用 jsx-loader 來編譯處理 { test: /\.js$/, loader: 'jsx-loader?harmony' }, //.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'} ] }
字段 說明
test 表示匹配的資源類型
loader 或 loaders 表示用來加載這種類型的資源的loader
定義loader的串聯關係,多個loader之間用「!」鏈接起來

此外,還能夠添加用來定義png、jpg這樣的圖片資源在小於10k時自動處理爲base64圖片的加載器:

test: /\.(png|jpg)$/,loader: 'url-loader?limit=10000'}

給css和less還有圖片添加了loader以後,咱們不只能夠像在node中那樣 require() js文件了,咱們還能夠 require() css、less甚至圖片文件:

require('./bootstrap.css'); require('./myapp.less'); var img = document.createElement('img'); img.src = require('./glyph.png');

注意,require() 還支持在資源path前面指定loader,即 require(![loaders list]![source path])形式:

require("!style!css!less!bootstrap/less/bootstrap.less"); // 「bootstrap.less」這個資源會先被"less-loader"處理, // 其結果又會被"css-loader"處理,接着是"style-loader" // 可類比pipe操做

require() 時指定的loader會覆蓋配置文件裏對應的loader配置項。

resolve

webpack在構建包的時候會按目錄的進行文件的查找,resolve 屬性中的 extensions 數組中用於配置程序能夠自行補全哪些文件後綴:

resolve: {
        //查找module的話從這裏開始查找 root: '/pomy/github/flux-example/src', //絕對路徑 //自動擴展文件後綴名,意味着咱們require模塊能夠省略不寫後綴名 extensions: ['', '.js', '.json', '.scss'], //模塊別名定義,方便後續直接引用別名,無須多寫長長的地址 alias: { AppStore : 'js/stores/AppStores.js',//後續直接 require('AppStore') 便可 ActionType : 'js/actions/ActionType.js', AppAction : 'js/actions/AppAction.js' } }

而後咱們想要加載一個js文件時,只要 require('common') 就能夠加載 common.js 文件了。
注意一下, extensions 第一個是空字符串! 對應不須要後綴的狀況.

plugin

webpack提供了[豐富的組件]用來知足不一樣的需求,固然咱們也能夠自行實現一個組件來知足本身的需求:

plugins: [ //your plugins list ]

在webpack中編寫js文件時,能夠經過require的方式引入其餘的靜態資源,可經過loader對文件自動解析並打包文件。一般會將js文件打包合併,css文件會在頁面的header中嵌入style的方式載入頁面。但開發過程當中咱們並不想將樣式打在腳本中,最好能夠獨立生成css文件,之外鏈的形式加載。這時 extract-text-webpack-plugin 插件能夠幫咱們達到想要的效果。須要使用npm的方式加載插件,而後參見下面的配置,就能夠將js中的css文件提取,並以指定的文件名來進行加載。

npm install extract-text-webpack-plugin –-save-dev
plugins: [ new ExtractTextPlugin('styles.css') ]

externals

當咱們想在項目中require一些其餘的類庫或者API,而又不想讓這些類庫的源碼被構建到運行時文件中,這在實際開發中頗有必要。此時咱們就能夠經過配置externals參數來解決這個問題:

externals: { "jquery": "jQuery" }

這樣咱們就能夠放心的在項目中使用這些API了:var jQuery = require(「jquery」);

context

當咱們在require一個模塊的時候,若是在require中包含變量,像這樣:

require("./mods/" + name + ".js");

那麼在編譯的時候咱們是不能知道具體的模塊的。但這個時候,webpack也會爲咱們作些分析工做:

1.分析目錄:’./mods’; 2.提取正則表達式:’/^.*.js$/’;

因而這個時候爲了更好地配合wenpack進行編譯,咱們能夠給它指明路徑,像在cake-webpack-config中所作的那樣(咱們在這裏先忽略abcoption的做用):

var currentBase = process.cwd(); var context = abcOptions.options.context ? abcOptions.options.context : path.isAbsolute(entryDir) ? entryDir : path.join(currentBase, entryDir);

關於 webpack.config.js 更詳盡的配置能夠參考 Webpack Configuration

開發環境

當項目逐漸變大,webpack 的編譯時間會變長,能夠經過參數讓編譯的輸出內容帶有進度和顏色。

$ webpack --progress --colors

若是不想每次修改模塊後都從新編譯,那麼能夠啓動監聽模式。開啓監聽模式後,沒有變化的模塊會在編譯後緩存到內存中,而不會每次都被從新編譯,因此監聽模式的總體速度是很快的。

$ webpack --progress --colors --watch

固然,使用 webpack-dev-server 開發服務是一個更好的選擇。它將在 localhost:8080 啓動一個 express 靜態資源 web 服務器,而且會以監聽模式自動運行 webpack,在瀏覽器打開 http://localhost:8080/ 或 http://localhost:8080/webpack... 能夠瀏覽項目中的頁面和編譯後的資源輸出,而且經過一個 socket.io 服務實時監聽它們的變化並自動刷新頁面。

# 安裝
$ npm install webpack-dev-server -g # 運行 $ webpack-dev-server --progress --colors

React 開發環境的配置

Webpack相關:

$ npm install webpack -g $ npm install webpack-dev-server -g # 安裝必要的 loader: ## 編譯 JSX $ npm install --save-dev babel-loader ## CSS 文件處理 $ npm install --save-dev css-loader style-loader ## React $ npm install --save-dev react-hot-loader

Babel 相關:

$ npm install --save-dev babel-core # 添加 ES6 支持 $ npm install --save-dev babel-preset-es2015 $ npm install --save-dev babel-react
var webpack = require('webpack'); module.exports = { entry: [ 'webpack/hot/only-dev-server', "./js/app.js" ], output: { path: './build', filename: "bundle.js" }, module: { loaders: [ { test: /\.js?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ }, { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}, { test: /\.css$/, loader: "style!css" } ] }, resolve:{ extensions:['','.js','.json'] }, plugins: [ new webpack.NoErrorsPlugin() ] };

參考資料:

webpack-dev-server

webpack-dev-server 是一個基於 Node.js Express 框架的開發服務器,它是一個靜態資源 Web 服務器,對於簡單靜態頁面或者僅依賴於獨立服務的前端頁面,均可以直接使用這個開發服務器進行開發。在開發過程當中,開發服務器會監聽每個文件的變化,進行實時打包,而且能夠推送通知前端頁面代碼發生了變化,從而能夠實現頁面的自動刷新。

簡單來講,webpack-dev-server就是一個小型的靜態文件服務器。使用它,能夠爲webpack打包生成的資源文件提供Web服務。

webpack-dev-server有兩種模式支持自動刷新——iframe模式和inline模式。

iframe模式

在iframe模式下:頁面是嵌套在一個iframe下的,在代碼發生改動的時候,這個iframe會從新加載。使用iframe模式無需額外的配置,只需在瀏覽器輸入:

http://localhost:8080/webpack-dev-server/index.html 

inline模式

在inline模式下:一個小型的webpack-dev-server客戶端會做爲入口文件打包,這個客戶端會在後端代碼改變的時候刷新頁面。使用inline模式有兩種方式:命令行方式和Node.js API。

命令行方式比較簡單,只需加入--line選項便可。例如:

webpack-dev-server --inline

使用--inline選項會自動把webpack-dev-server客戶端加到webpack的入口文件配置中。
注意:默認配置文件名稱爲:webpack.config.js,若要更改須要在命令行中指明。例如,

webpack-dev-server --inline --config webpack.config.dev.js。

若用Node.js API方式,由於webpack-dev-server沒有inline:true這個配置項,因此須要手動把

webpack-dev-server/client?http://localhost:8080

加到配置文件的入口文件配置處。

模塊熱替換

webpac-dev-server 支持 Hot Module Replacement,即模塊熱替換,在前端代碼變更的時候無需整個刷新頁面,只把變化的部分替換掉。使用HMR功能也有兩種方式:命令行方式和Node.js API。

命令行方式一樣比較簡單,只需加入--line --hot選項。--hot這個選項幹了一件事情,它把webpack/hot/dev-server入口點加入到了webpack配置文件中。這時訪問瀏覽器,你會看見控制檯的log信息:

[HMR] Waiting for update signal from WDS... [WDS] Hot Module Replacement enabled.

HMR前綴的信息由webpack/hot/dev-server模塊產生,WDS前綴的信息由webpack-dev-server客戶端產生。

Node.js API方式須要作三個配置:

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

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

  3. 把 hot:true 加入到 Webpack 配置文件的 webpack-dev-server 的配置項裏面。

devServer:{ hot:true }

注意:要使HMR功能生效,還須要作一件事情,就是要在應用熱替換的模塊或者根模塊裏面加入容許熱替換的代碼。不然,熱替換不會生效,仍是會重刷整個頁面。

if(module.hot) module.hot.accept();

也可使用一些插件去完成這個工做,例如webpack-module-hot-accept插件。不過,webpack-dev-server HMR結合react-hot-loader使用的時候,react-hot-loader會去作這個工做。綜合上述,使用wepack-dev-server輔助開發,使得開發者在開發前端代碼的過程當中無需頻繁手動刷新頁面,使用HMR甚至不用等待頁面刷新,確實能夠給開發者帶來很好的體驗。

可是,問題又來了。我要進行先後端聯調的時候怎麼辦呢?畢竟webpack-dev-server只是一個靜態文件服務器,不具有動態處理的能力。這個時候就須要將後端服務器與webpack-dev-server結合使用了。webpack-dev-server只用來爲webpack打包生成的資源文件提供服務,好比js文件、圖片文件、css文件等;後端服務器除提供API接口外,還提供入口HTML。

要將webpack-dev-server與後端服務器結合使用,須要作三件事情。

第一 首頁HTML文件是從後端服務器發出的,這時頁面的根地址變成了後端服務器地址,怎麼使得webpack產生的資源文件在請求資源的時候是向web-dev-server請求而不是後端服務器請求?只需在webpack配置文件中的 output.publicPath 配置項寫上絕對URL地址,例如output.publicPath = "http://localhost:8080/assets/"。這時,webpack打包產生的資源文件裏面的url地址都會是絕對地址,而不是相對地址。
第二 後端服務器產生的入口HTML文件要向webpack-dev-server請求資源文件,這個簡單,只需在HTML文件中加入資源文件的絕對地址,例如:<script src="http://localhost:8080/assets/bundle.js">
第三 要使webpack-dev-server和它的運行時程序鏈接起來。這個簡單,只須要使用iline模式便可。

提取公共代碼與壓縮

var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js'); module.exports = { ... ... // plugins 項配置中增長 plugins: [ ... ... // 提取公共代碼 commonsPlugin, //壓縮 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] }

參見:
WEBPACK DEV SERVER
webpack-dev-server 官方文檔
前端模塊加載工具——webpack(二)

相關文章
相關標籤/搜索