翻譯它主要是用於學習Webpack,原地址為github.com/ruanyf/webp…
安裝使用指南
首先,全域性安裝Webpack和webpack-dev-server
$ npm i -g webpack webpack-dev-server
複製程式碼
然後克隆clone阮一峰的倉庫
$ git clone https://github.com/ruanyf/webpack-demos.git
複製程式碼
安裝依賴
$ cd webpack-demos
$ npm install
複製程式碼
現在開始進入demo*目錄並且執行它們
$ cd demo01
$ npm run dev
複製程式碼
上面的程式碼不會自動的開啟你的瀏覽器,需要手動訪問http://127.0.0.1:8080
前言:Webpack是什麼
Webpack用於構建Javascript模組指令碼來給瀏覽器使用的前端工具。
它和Browserify很像,但是能做更多的事~
$ browserify main.js > bundle.js
# 上下程式碼作用相同
$ webpack main.js bundle.js
複製程式碼
Webpack需要一個名為webpack.config.js
的配置檔案,這個檔案就是一個CommonJS的模組(module)
// webpack.config.js的內容
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
複製程式碼
當建好webpack.config.js
後就能直接執行Webpack,而不加引數
$ webpack
複製程式碼
有一些必須知道的引數選項如下
webpack
——開發時的構建命令webpack -p
——釋出產品時的構建命令webpack --watch
——用於增量開發的構建webpack -d
——包括source mapswebpack --colors
——讓構建輸出更好看
可以在定製webpack.config.js
中的scripts
選項,如下所示
// package.json
{
// ...
"scripts": {
"dev": "webpack-dev-server --devtool eval --progress --colors",
"deploy": "NODE_ENV=production webpack -p"
},
// ...
}
複製程式碼
Demo01:入口檔案
入口檔案用來Webpack讀取它然後構建bundle.js
例如下面,main.js
就是一個入口檔案
// main.js
document.write('<h1>Hello World</h1>');
複製程式碼
index.html
<html>
<body>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
複製程式碼
Webpack依據webpack.config.js
來構建bundle.js
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
複製程式碼
執行下面的命令然後訪問http://127.0.0.1:8080
$ cd demo01
$ npm run dev
複製程式碼
Demo02:多個入口檔案
Webpack
允許多個入口檔案存在,在多頁面的app中很有用,每個頁面有不同的入口檔案。
// main1.js
document.write('<h1>Hello World</h1>');
// main2.js
document.write('<h2>Hello Webpack</h2>');
複製程式碼
index.html
<html>
<body>
<script src="bundle1.js"></script>
<script src="bundle2.js"></script>
</body>
</html>
複製程式碼
webpack.config.js
module.exports = {
entry: {
bundle1: './main1.js',
bundle2: './main2.js'
},
output: {
filename: '[name].js'
}
};
複製程式碼
Demo03:Babel-loader
載入器(Loaders)是一些前處理器,用於在Webpack的構建過程前,將你app裡的一些資原始檔進行轉換。
例如,Babel-loader能夠將JSX/ES6檔案轉為普通的JS檔案,之後Webpack能夠開始構建它們。Webpack官方文件有一個載入器的列表地址
main.jsx
是一個JSX檔案
// main.jsx
const React = require('react');
const ReactDOM = require('react-dom');
ReactDOM.render(
<h1>Hello, world!</h1>,
document.querySelector('#wrapper')
);
複製程式碼
index.html
<html>
<body>
<div id="wrapper"></div>
<script src="bundle.js"></script>
</body>
</html>
複製程式碼
webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
}
]
}
};
複製程式碼
在上面的程式碼裡需要babel-loader的兩個外掛babel-preset-es2015和babel-preset-react來轉換ES6和React(翻譯:現在為廢棄了,詳情)
Demo04:CSS-loader
Webpack允許在JS檔案中包含CSS,需要CSS-loader對這些CSS進行處理
main.js
require('./app.css');
複製程式碼
app.css
body {
background-color: blue;
}
複製程式碼
index.html
<html>
<head>
<script type="text/javascript" src="bundle.js"></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
複製程式碼
webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
]
}
};
複製程式碼
注意!必須使用兩個載入器來轉換CSS檔案。CSS-loader用來讀取CSS檔案來轉換,另一個Style-loader用來往HTML中插入<style>
標籤。
然後開啟server
$ cd demo04
$ npm run dev
複製程式碼
事實上,Webpack將CSS檔案的內容直接插入到index.html
<head>
<script type="text/javascript" src="bundle.js"></script>
<style type="text/css">
body {
background-color: blue;
}
</style>
</head>
複製程式碼
Demo5: Image loader
Webpack能夠將圖片包含進JS檔案中
main.js
var img1 = document.createElement("img");
img1.src = require("./small.png");
document.body.appendChild(img1);
var img2 = document.createElement("img");
img2.src = require("./big.png");
document.body.appendChild(img2);
複製程式碼
index.html
<html>
<body>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
複製程式碼
webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.(png|jpg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
};
複製程式碼
url-loader
將image檔案轉為<img>
標籤,如果圖片大小小魚8192位元組,它將轉換為Data url(翻譯:圖片變為Base64編碼,減少請求次數),否則,他將轉為普通檔案URL。
<img src="data:image/png;base64,iVBOR...uQmCC">
<img src="4853ca667a2b8b8844eb2693ac1b2578.png">
複製程式碼
Demo06:CSS Module
css-loader?modules
(請求引數為modules)能夠使用CSS Module,CSS Module帶給你的JS檔案模組中的CSS一個區域性作用域,也可以使用:global(selector)
讓CSS變為全域性作用。
index.html
<html>
<body>
<h1 class="h1">Hello World</h1>
<h2 class="h2">Hello Webpack</h2>
<div id="example"></div>
<script src="./bundle.js"></script>
</body>
</html>
複製程式碼
app.css
/* local scope */
.h1 {
color:red;
}
/* global scope */
:global(.h2) {
color: blue;
}
複製程式碼
main.jsx
var React = require('react');
var ReactDOM = require('react-dom');
var style = require('./app.css');
ReactDOM.render(
<div>
<h1 className={style.h1}>Hello World</h1>
<h2 className="h2">Hello Webpack</h2>
</div>,
document.getElementById('example')
);
複製程式碼
webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}
};
複製程式碼
訪問 http://127.0.0.1:8080會看到只有h1
是紅色的,因為他的CSS是區域性作用域,然後所以h2
都是藍色的,因為它是全域性作用域。
Demo07:UglifyJs Plugin
Webpack用外掛系統來擴充套件他的功能。例如,UglifyJs Plugin,它用來壓縮JS程式碼,使得JS檔案體積變小。
main.js
var longVariableName = 'Hello';
longVariableName += ' World';
document.write('<h1>' + longVariableName + '</h1>');
複製程式碼
index.html
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>
複製程式碼
webpack.config.js
var webpack = require('webpack');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new UglifyJsPlugin()
]
};
複製程式碼
在訪問伺服器後,可以看到main.js
最小化為如下程式碼:
var o="Hello";o+=" World",document.write("<h1>"+o+"</h1>")
複製程式碼
(翻譯:是在bundle.js
的最後幾個程式碼那,是這些程式碼)
Demo08:HTML Webpack Plugin和Open Browser Webpack Plugin
這個demo用來展示如何載入第三方外掛
html-webpack-plugin
能為你建立index.html
,open-browser-webpack-plugin
能夠在Webpack載入時開啟一個新的瀏覽器標籤(tab)
main.js
document.write('<h1>Hello World</h1>');
複製程式碼
webpack.config.js
var HtmlwebpackPlugin = require('html-webpack-plugin');
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new HtmlwebpackPlugin({
title: 'Webpack-demos',
filename: 'index.html'
}),
new OpenBrowserPlugin({
url: 'http://localhost:8080'
})
]
};
複製程式碼
現在你不用手動建立index.html
也不用手動開啟瀏覽器了。
Demo09:環境變數(Environment flags)
使用環境變數讓一些程式碼只能在開發環境時使用。
main.js
document.write('<h1>Hello World</h1>');
if (__DEV__) {
document.write(new Date());
}
複製程式碼
index.html
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>
複製程式碼
webpack.config.js
var webpack = require('webpack');
var devFlagPlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [devFlagPlugin]
};
複製程式碼
現在傳遞環境變數給Webpack。開啟demo09/package.json
,找到如下scripts
選項
// package.json
{
// ...
"scripts": {
"dev": "cross-env DEBUG=true webpack-dev-server --open",
},
// ...
}
複製程式碼
Demo10:程式碼分離
在大型web應用中,將所有程式碼放入一個檔案是十分低效的。Webpack允許你將大型JS檔案分成多塊。特別的,如果一些程式碼塊只是在某些情況下需要,那麼這些程式碼塊會按需載入。
Webpack使用require.ensure
來定義一個分割點
// main.js
require.ensure(['./a'], function (require) {
var content = require('./a');
document.open();
document.write('<h1>' + content + '</h1>');
document.close();
});
複製程式碼
require.ensure
告訴Webpack ./a,js
需要從bundle.js
中分離出來作為一個單獨的塊檔案
// a.js
module.exports = 'Hello World';
複製程式碼
現在Webpack關心依賴、輸出檔案、執行時的東西。你不必將多餘的東西放到index.html
和webpack.config.js
中
index.html
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>
複製程式碼
webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
複製程式碼
訪問伺服器後,你感覺不到任何不同。實際上,Webpack將構建main.js
和a.js
到不同的塊中(bundle.js
和0.bundle.js
),然後從bundle.js
中按需載入0.bundle.js
Demo11:bundle-loader下的程式碼分離
另一個程式碼分割的方式是bundle-loader
// main.js
// Now a.js is requested, it will be bundled into another file
var load = require('bundle-loader!./a.js');
// To wait until a.js is available (and get the exports)
// you need to async wait for it.
load(function(file) {
document.open();
document.write('<h1>' + file + '</h1>');
document.close();
});
複製程式碼
require('bundle-loader!./a.js')
告訴Webpack從其他塊載入a.js
現在main.js
構建為bundle.js
,a.js
構建為0.bundle.js
Demo12:通用塊
在多個JS指令碼中有通用塊,通過CommonsChunkPlugin
你能提取不同檔案中的通用部分,對於瀏覽器快取來節省頻寬是非常有用的。
// main1.jsx
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h1>Hello World</h1>,
document.getElementById('a')
);
// main2.jsx
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h2>Hello Webpack</h2>,
document.getElementById('b')
);
複製程式碼
index.html
<html>
<body>
<div id="a"></div>
<div id="b"></div>
<script src="commons.js"></script>
<script src="bundle1.js"></script>
<script src="bundle2.js"></script>
</body>
</html>
複製程式碼
上面的commons.js
是main1.jsx
和main2.jsx
的通用部分。正如你想的,commons.js
包括react
和react-dom
Webpack.config.js
var webpack = require('webpack');
module.exports = {
entry: {
bundle1: './main1.jsx',
bundle2: './main2.jsx'
},
output: {
filename: '[name].js'
},
module: {
rules:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
},
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "commons",
// (the commons chunk name)
filename: "commons.js",
// (the filename of the commons chunk)
})
]
}
複製程式碼
Demo13:Vendor chunk
通過CommonsChunkPlugin
,你能從JS指令碼提取官方庫到單獨的檔案中
main.js
var $ = require('jquery');
$('h1').text('Hello World');
複製程式碼
index.html
<html>
<body>
<h1></h1>
<script src="vendor.js"></script>
<script src="bundle.js"></script>
</body>
</html>
複製程式碼
webpack.config.js
var webpack = require('webpack');
module.exports = {
entry: {
app: './main.js',
vendor: ['jquery'],
},
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.js'
})
]
};
複製程式碼
在上面的程式碼中,entry.vendor:['jquery']
告訴Webpack,jquery
應該被包括到通用塊vendor.js
中。
如果你想要一個模組作為全域性變數來在不同的模組中使用,比如不用在每個檔案中require('jquery')
,而是讓$
或者jQuery
作為全域性變數,需要使用ProvidePlugin
,它能夠自動載入模組而不需要到處import
或者是require
// main.js
$('h1').text('Hello World');
// webpack.config.js
var webpack = require('webpack');
module.exports = {
entry: {
app: './main.js'
},
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
]
};
複製程式碼
當然,在Demo13中,你需要自己全域性載入jquery.js
Demo14:公開全域性變數
如果你想用一些全域性變數,不需要在Webpack包中包含他們,你可以使用webpack.config.js裡的externals
欄位
例如,我們有一個data.js
// data.js
var data = 'Hello World';
複製程式碼
index.html
<html>
<body>
<script src="data.js"></script>
<script src="bundle.js"></script>
</body>
</html>
複製程式碼
注意,Webpack只會構建bundle.js
, 而不會構建data.js
我們可以把data
作為全域性變數
// webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
},
]
},
externals: {
// require('data') is external and available
// on the global var data
'data': 'data'
}
};
複製程式碼
此時,你可以require('data')
作為模組變數,實際上它是一個全域性變數
// main.jsx
var data = require('data');
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h1>{data}</h1>,
document.body
);
複製程式碼
同樣可以將react
和react-dom
放入externals
,這樣顯著的降低構建bundle.js
的時間和檔案大小
Demo15:React router
這個Demo使用Webpack來構建React router的官方樣例
先想象一個有控制板、收件箱、日曆的小應用
+---------------------------------------------------------+
| +---------+ +-------+ +--------+ |
| |Dashboard| | Inbox | |Calendar| Logged in as Jane |
| +---------+ +-------+ +--------+ |
+---------------------------------------------------------+
| |
| Dashboard |
| |
| |
| +---------------------+ +----------------------+ |
| | | | | |
| | + + | +---------> | |
| | | | | | | |
| | | + | | +-------------> | |
| | | | + | | | | |
| | | | | | | | | |
| +-+---+----+-----+----+ +----------------------+ |
| |
+---------------------------------------------------------+
複製程式碼
webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
},
]
}
};
複製程式碼
index.js
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter, Switch, Route, Link } from 'react-router-dom';
import './app.css';
class App extends React.Component {
render() {
return (
<div>
<header>
<ul>
<li><Link to="/app">Dashboard</Link></li>
<li><Link to="/inbox">Inbox</Link></li>
<li><Link to="/calendar">Calendar</Link></li>
</ul>
Logged in as Jane
</header>
<main>
<Switch>
<Route exact path="/" component={Dashboard}/>
<Route path="/app" component={Dashboard}/>
<Route path="/inbox" component={Inbox}/>
<Route path="/calendar" component={Calendar}/>
<Route path="*" component={Dashboard}/>
</Switch>
</main>
</div>
);
}
};
class Dashboard extends React.Component {
render() {
return (
<div>
<p>Dashboard</p>
</div>
);
}
};
class Inbox extends React.Component {
render() {
return (
<div>
<p>Inbox</p>
</div>
);
}
};
class Calendar extends React.Component {
render() {
return (
<div>
<p>Calendar</p>
</div>
);
}
};
render((
<BrowserRouter>
<Route path="/" component={App} />
</BrowserRouter>
), document.querySelector('#app'));
複製程式碼
index.html
<html>
<body>
<div id="app"></div>
<script src="/bundle.js"></script>
</body>
</htmL>
複製程式碼