首先要安裝 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.js
:html
<!-- 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.js
:react
// 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
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參數定義了打包後的入口文件,能夠是個字符串或數組或者是對象;若是是數組,數組中的全部文件會打包生成一個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: { 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>
。
在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配置項。
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
第一個是空字符串! 對應不須要後綴的狀況.
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') ]
當咱們想在項目中require一些其餘的類庫或者API,而又不想讓這些類庫的源碼被構建到運行時文件中,這在實際開發中頗有必要。此時咱們就能夠經過配置externals參數來解決這個問題:
externals: { "jquery": "jQuery" }
這樣咱們就能夠放心的在項目中使用這些API了:var jQuery = require(「jquery」)
;
當咱們在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
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 是一個基於 Node.js Express 框架的開發服務器,它是一個靜態資源 Web 服務器,對於簡單靜態頁面或者僅依賴於獨立服務的前端頁面,均可以直接使用這個開發服務器進行開發。在開發過程當中,開發服務器會監聽每個文件的變化,進行實時打包,而且能夠推送通知前端頁面代碼發生了變化,從而能夠實現頁面的自動刷新。
簡單來講,webpack-dev-server就是一個小型的靜態文件服務器。使用它,能夠爲webpack打包生成的資源文件提供Web服務。
webpack-dev-server有兩種模式支持自動刷新——iframe模式和inline模式。
在iframe模式下:頁面是嵌套在一個iframe下的,在代碼發生改動的時候,這個iframe會從新加載。使用iframe模式無需額外的配置,只需在瀏覽器輸入:
http://localhost:8080/webpack-dev-server/index.html
在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方式須要作三個配置:
把 webpack/hot/dev-server
加入到webpack配置文件的 entry
項;
把 new webpack.HotModuleReplacementPlugin()
加入到webpack配置文件的plugins
項;
把 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(二)