16個demo,webpack+react搭配使用
首先教大家2個新技能
- 1.按照正常github地址情況下,你的github本身不能訪問目錄。
- 例如要訪問vue-demo下的vueCpu資料夾:github.com/holidaying/… (master是分支名)就可以訪問。
- 2.github目錄的製作
- 明確一個問題。一個標題就是一個目錄名稱
- 寫法[xx](#題目名稱)#不能少
- 題目名稱的寫法規則:abc demo->abc-demo,Abc-Demo->abc-demo。會忽略:和()即就是題目中所有可見字元的空格均用-連線,中、英文空格要分開,中文空格對應中文-。並且全為小寫
步驟
首先,install Webpack 和 webpack-dev-server.
$ npm i -g webpack webpack-dev-server複製程式碼
# Linux & Mac
$ git clone git@github.com:holidaying/webpack-demo.git
# Windows
$ git clone https://github.com/holidaying/webpack-demo.git
:
$ cd webpack-demo
$ npm install複製程式碼
接下來就可以進行demo演示了.
$ cd demo01
$ webpack-dev-server複製程式碼
用瀏覽器訪問 http://127.0.0.1:8080.
什麼是webpack?
Webpack 是前端的打包工具類類似於 Grunt and Gulp.但是有區別,因為它是模組化構建機制,Webpack可以看做是模組打包機:它做的事情是,分析你的專案結構,找到JavaScript模組以及其它的一些瀏覽器不能直接執行的擴充語言(Scss,TypeScript等),並將其打包為合適的格式以供瀏覽器使用。
WebPack和Grunt以及Gulp相比有什麼特性
其實Webpack和另外兩個並沒有太多的可比性,Gulp/Grunt是一種能夠優化前端的開發流程的工具,而WebPack是一種模組化的解決方案,不過Webpack的優點使得Webpack可以替代Gulp/Grunt類的工具。
Grunt和Gulp的工作方式是:在一個配置檔案中,指明對某些檔案進行類似編譯,組合,壓縮等任務的具體步驟,這個工具之後可以自動替你完成這些任務。
更多資訊.
$ webpack main.js bundle.js複製程式碼
它的配置檔案是 webpack.config.js
.
// 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 maps方便除錯webpack --colors
– 讓打包介面更好看
去構建你的專案, 你可以把啟動項寫進package.json
// package.json
{
// ...
"scripts": {
"dev": "webpack-dev-server --devtool eval --progress --colors",
"deploy": "NODE_ENV=production webpack -p"
},
// ...
}複製程式碼
目錄
- 單檔案入口
- 多檔案入口
- Babel-loader
- CSS-loader
- Image loader
- CSS Module
- UglifyJs Plugin外掛
- HTML Webpack Plugin and Open Browser Webpack Plugin
- Environment flags環境變數
- Code splitting程式碼分割
- Code splitting with bundle-loader
- Common chunk提取公共檔案
- Vendor chunk提取公共的第三方程式碼
- externals全域性變數
- 熱模組替代/熱更新
- React router
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 follows webpack.config.js
to build bundle.js
.
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};複製程式碼
啟動服務, 訪問 http://127.0.0.1:8080 .
$ webpack-dev-server複製程式碼
Demo02: 多檔案入口(原始碼)
多個入口檔案,實用於多個頁面的應用
// 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 (原始碼)
通過使用不同的loader,webpack通過呼叫外部的指令碼或工具可以對各種各樣的格式的檔案進行處理(更多資訊). 例如, Babel-loader Babel其實是一個編譯JavaScript的平臺可以將 JSX/ES6 檔案轉換成瀏覽器可以識別的js檔案. 官方文件loaders.
main.jsx
is a 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: {
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader?presets[]=es2015&presets[]=react'
},
]
}
};複製程式碼
在 webpack.config.js
, module.loaders
區域是用來分配loader的. 像上面的程式碼片段使用了 babel-loader
需要安裝外掛 babel-preset-es2015 和 babel-preset-react to 編譯成 ES6 and React. 可以用query配置引數
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'react']
}
}
]
}複製程式碼
Demo04: CSS-loader (原始碼)
Webpack 允許你在js檔案中require 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: {
loaders:[
{ test: /\.css$/, loader: 'style-loader!css-loader' },
]
}
};複製程式碼
但是,你需要使用2中loaders來轉換CSS 檔案. 第一個是 CSS-loader 來讀取CSS檔案, 另外一個是Style-loader 是將style樣式插入到html中。 中間用!連線
啟動服務後, index.html
有內部樣式.
<head>
<script type="text/javascript" src="bundle.js"></script>
<style type="text/css">
body {
background-color: blue;
}
</style>
</head>複製程式碼
Demo05: Image loader (原始碼)
Webpack 允許你在js檔案中require圖片 , 通過 url-loader和file-loader來預處理圖片檔案.
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: {
loaders:[
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' }
]
}
};複製程式碼
url-loader 轉換圖片檔案. 如果圖片的大小小於 8192 bytes,它將會轉成base64位的地址; 相反, 它就是普通地址.
引數前是用?連線的
啟動服務後, small.png
and big.png
將會有一下的地址.
<img src="data:image/png;base64,iVBOR...uQmCC">
<img src="4853ca667a2b8b8844eb2693ac1b2578.png">複製程式碼
Demo06: CSS Module (原始碼)
css-loader?modules
(the query parameter modules) enables the CSS Modules spec.
CSS Module可以開啟全域性變數和區域性變數,:global(...)表示全域性變數,可以在全域性中使用樣式(更多資訊)
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
.h1 {
color:red;
}
: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: {
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
},
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules'
}
]
}
};複製程式碼
啟動服務.
$ webpack-dev-server複製程式碼
訪問 http://127.0.0.1:8080 , 你將看到只有第二個 h1
是紅的,因為它是區域性, 同時 h2
是藍色的, 因為是h2
全域性的.
Demo07: UglifyJs Plugin (原始碼)
Webpack 可以去掉本身附加的東西,優化程式碼 UglifyJs Plugin will minify output(bundle.js
) JS codes.
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 = webpack.optimize.UglifyJsPlugin;
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new uglifyJsPlugin({
compress: {
warnings: false
}
})
]
};複製程式碼
啟動服務後, main.js
將會壓縮如下.
var o="Hello";o+=" World",document.write("<h1>"+o+"</h1>")複製程式碼
Demo08: HTML Webpack Plugin and Open Browser Webpack Plugin (原始碼)
這個例子需要載入三個外掛
html-webpack-plugin 建立 index.html
,open-browser-webpack-plugin 開啟瀏覽器
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'
})
]
};複製程式碼
啟動 webpack-dev-server
.啟動這個需要node7版本以上
$ webpack-dev-server複製程式碼
不用手寫index.html
也不用手動開啟瀏覽器 Webpack 可以為你做這些事.
Demo09: 設定環境變數 (原始碼)
你可以利用環境變數來控制特定程式碼的輸出
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]
};複製程式碼
# Linux & Mac
$ env DEBUG=true webpack-dev-server
# Windows
$ set DEBUG=true
$ webpack-dev-server複製程式碼
Demo10: Code splitting (原始碼)
對於大型專案,把所有程式碼編譯到一個檔案並不是有效的, Webpack 允許你把程式碼分成好多塊. 特別是某種情況下,只需要個別程式碼這些塊可以按需載入。
在commonjs中有一個Modules/Async/A規範,裡面定義了require.ensure語法。webpack實現了它,作用是可以在打包的時候進行程式碼分片,並非同步載入分片後的程式碼。用法如下:
require.ensure([], function(require){
var list = require('./list');
list.show();
});複製程式碼
此時list.js會被打包成一個單獨的chunk檔案,大概長這樣:
1.fb874860b35831bc96a8.js
可讀性比較差。我在上一篇結尾也提到了,給它命名的方式,那就是給require.ensure傳遞第三個引數,如:
require.ensure([], function(require){
var list = require('./list');
list.show();
}, 'list');複製程式碼
這樣就能得到你想要的檔名稱:
首先,你需要用 require.ensure
to 來定義一個分割的點. (官方文件)
// 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';複製程式碼
Now Webpack takes care of the dependencies, output files and runtime stuff. You don't have to put any redundancy into your index.html
and webpack.config.js
.
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>複製程式碼
webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};複製程式碼
啟動服務.
$ webpack-dev-server複製程式碼
在介面上, 你感覺不到任何不一樣的地方. 但是, Webpack 已經把 main.js
和 a.js
編譯成(bundle.js
和 1.bundle.js
)的塊。
Demo11: 通過bundle-loader進行程式碼分裂 (原始碼)
dem10是一種,另一種是利用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')
tells Webpack to load a.js
from another chunk.
Now Webpack will build main.js
into bundle.js
, and a.js
into 1.bundle.js
.
Demo12: Common chunk (原始碼)
利用webpack.optimize.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="init.js"></script>
<script src="bundle1.js"></script>
<script src="bundle2.js"></script>
</body>
</html>複製程式碼
webpack.config.js
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
entry: {
bundle1: './main1.jsx',
bundle2: './main2.jsx'
},
output: {
filename: '[name].js'
},
module: {
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
},
]
},
plugins: [
new CommonsChunkPlugin('init.js')
]
}複製程式碼
Demo13: Vendor chunk (原始碼)
利用webpack.optimize.CommonsChunkPlugin,你可以把第三方庫抽離出來
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(/* chunkName= */'vendor', /* filename= */'vendor.js')
]
};複製程式碼
If you want a module available as variable in every module, such as making $ and jQuery available in every module without writing require("jquery")
. You should use ProvidePlugin
(官方文件).
// 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",
"window.jQuery": "jquery"
})
]
};複製程式碼
外掛會執行兩次這個方法,第一次將公共的第三方程式碼抽離移到vendor的塊中,這個過程之前也講過會將執行時runtime也轉移到vendor塊中,第二次執行則是將執行時runtime抽離出來轉移到manifest塊中。這步操作解決了快取問題。
這樣處理,最後會生成3個打包檔案chunk,app.js是業務程式碼,vendor則是公共的第三方程式碼,manifest.js則是執行時。
Demo14: Exposing global variables (原始碼)
webpack可以不處理應用的某些依賴庫,使用externals配置後,依舊可以在程式碼中通過CMD、AMD或者window/global全域性的方式訪問。如果你想引入一些全域性變數, 但是不想被載入處理, 你可以在 webpack.config.js
使用 externals
模組 (官方文件).
有時我們希望我們通過script引入的庫,如用CDN的方式引入的jquery,我們在使用時,依舊用require的方式來使用,但是卻不希望webpack將它又編譯進檔案中。
例子, data.js
.
var data = 'Hello World';複製程式碼
We can expose data
as a global variable.
// webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
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
);複製程式碼
Demo15: 熱模組替換/熱更新 (原始碼)
Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running without a page reload.
通過webpack-dev-server.你可以使用2中方式 來進行熱模組替換
(1) Specify --hot
and --inline
on the command line
$ webpack-dev-server --hot --inline複製程式碼
引數的意思:
--hot
: adds the HotModuleReplacementPlugin and switch the server to hot mode.--inline
: embed the webpack-dev-server runtime into the bundle.--hot --inline
: also adds the webpack/hot/dev-server entry.
(2) 修改 webpack.config.js
.
- 新增
new webpack.HotModuleReplacementPlugin()
to theplugins
模組 - 新增
webpack/hot/dev-server
和webpack-dev-server/client?http://localhost:8080
to theentry
模組
webpack.config.js
如下所示.
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: [
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8080',
'./index.js'
],
output: {
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
},
include: path.join(__dirname, '.')
}]
}
};複製程式碼
啟動服務
$ webpack-dev-server複製程式碼
訪問 http://localhost:8080, 你可以在瀏覽器上看到 'Hello World' .
不要關閉服務.開啟終端找到 App.js
, 同時修改 'Hello World' 為 'Hello Webpack'. 儲存後,你就可以在瀏覽器上看到資料更新了
App.js
import React, { Component } from 'react';
export default class App extends Component {
render() {
return (
<h1>Hello World</h1>
);
}
}複製程式碼
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));複製程式碼
index.html
<html>
<body>
<div id='root'></div>
<script src="/static/bundle.js"></script>
</body>
</html>複製程式碼
Demo16: React router例子 (原始碼)
利用webpack做的例子 React-router's 官方例子.
Let's imagine a little app with a dashboard, inbox, and calendar.
+---------------------------------------------------------+
| +---------+ +-------+ +--------+ |
| |Dashboard| | Inbox | |Calendar| Logged in as Jane |
| +---------+ +-------+ +--------+ |
+---------------------------------------------------------+
| |
| Dashboard |
| |
| |
| +---------------------+ +----------------------+ |
| | | | | |
| | + + | +---------> | |
| | | | | | | |
| | | + | | +-------------> | |
| | | | + | | | | |
| | | | | | | | | |
| +-+---+----+-----+----+ +----------------------+ |
| |
+---------------------------------------------------------+複製程式碼
$ webpack-dev-server --history-api-fallback複製程式碼
參照文件
- Webpack docs
- webpack-howto, by Pete Hunt
- Diving into Webpack, by Web Design Weekly
- Webpack and React is awesome, by Christian Alfoni
- Browserify vs Webpack, by Cory House
- React Webpack cookbook, by Christian Alfoni
全文參考阮老師的教程