上一篇文章 《webpack 快速構建 React 學習環境(1)》中介紹了構建一個最簡單開發環境,這裏接着完善這個開發環境,讓它用起來更加的趁手。html
看着篇文章前請先看 《webpack 快速構建 React 學習環境(1)》node
本小結內容對應構建的項目源碼:github.com/wewin11235/…,倉庫的 stage2
分支react
文章同步發佈在我的博客站點webpack
上一節搭建的開發環境不能熱加載,每次文件改動後都須要從新編譯,手動刷新頁面。雖然使用 webpack --watch
命令在文件變化後能從新編譯,可是仍須要手動刷新頁面。webpack --watch
的方式還有個缺點,每次都是從新編譯生成新的文件到 build 目錄下, 文件多的時候這個編譯過程就會慢。git
webpack-dev-server
配合 HRM
能夠構建一個完美的開發環境,改動保存後自動編譯,無需手動刷新頁面。github
webpack-dev-server 主要是啓動了一個使用 express 的 Http 服務器。它的做用主要是用來伺服資源文件。此外這個Http服務器和client使用了websocket通信協議,原始文件做出改動後,webpack-dev-server 會實時的編譯. webpack-dev-server 的時時編譯並不會輸出到 webpack.config.js 中指定的出口,編譯生成的文件在內存中,也有效的提升了編譯效率。web
安裝與配置:express
npm i webpack-dev-server -D
複製代碼
安裝完成後: ./node_modules/.bin/webpack-dev-server
即可啓動 servernpm
ℹ 「wds」: Project is running at http://localhost:8081/
ℹ 「wds」: webpack output is served from /./
⚠ 「wdm」: Hash: b2af4145bdae26f19266
複製代碼
dev server 被啓動在了 8081 端口, server 會默認找項目主目錄下的 index.html 文件做爲服務的根頁面。 因爲咱們編譯後的服務入口文件 index.html 放在 build 目錄下,因此要對 webpack-dev-server 作一下配置:瀏覽器
webpack.config.js 增長以下配置:
devServer: {
contentBase: path.join(__dirname, 'build')
}
複製代碼
此時 webpack.config.js :
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[hash:8].js',
publicPath: '/',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader'
},
},
],
},
plugins: [new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.html'
})],
devServer: {
contentBase: path.join(__dirname, 'build'),
}
};
複製代碼
這樣就能夠訪問 http://localhost:8081
查看啓動的服務。
此時 dev server 雖然啓動,但嘗試修改 index.js 內容能夠發現頁面並不能自動刷新,webpack dever server 的自動刷新有兩種模式 Iframe 和 inline 兩種模式,這裏用 inline 模式,修改配置以下:
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[hash:8].js',
publicPath: '/',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader'
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.html'
})
],
devServer: {
contentBase: path.join(__dirname, 'build'),
port: 9000, // 指定服務啓動在 9000 端口
inline: true, // inline 模式啓動
open: true // 執行webpack-dev-server 後自動打開瀏覽器
}
};
複製代碼
上面的配置,當文件有更新,你會發現瀏覽器刷新了,配置 HMR 能夠實現局部熱替換,不用整個瀏覽器刷新。HMR 的配置請參考 [hot-module-replacement] (webpack.docschina.org/guides/hot-…)。 react 框架下還須要用到 react-hot-loader
改動有如下幾處: 添加 src/print.js
文件
export default function printMe() {
console.log("Updating print.js...");
}
複製代碼
修改 webpack.config.js 文件,添加 hot 配置:
const path = require('path');
const webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[hash:8].js',
publicPath: '/',
},
mode: 'development',
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader'
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.html'
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: path.join(__dirname, 'build'),
port: 9000,
open: true, // HMR 支持
hot: true
}
};
複製代碼
src/index.js
文件以下:
import React from 'react';
import ReactDOM from 'react-dom';
import { hot } from 'react-hot-loader'
class HelloReact extends React.Component{
constructor(props) {
super(props);
}
render(){
return( <div>Hello React</div>);
}
}
export default hot(module)(HelloReact);
ReactDOM.render(<HelloReact />, document.getElementById('root'));
if (module.hot) {
module.hot.accept('./print.js', function(){
console.log("Accepting the updated printMe module!");
printMe();
})
}
複製代碼
安裝 react-hot-loader
npm i react-hot-loader -D
複製代碼
修改 .babelrc 以下
{
"presets": [
["@babel/preset-env", {
"targets": {
"node": "current"
}
}],
"@babel/preset-react"
],
"plugins": ["react-hot-loader/babel"] //添加 ‘react-hot-loader/babel’ 這個插件支持
}
複製代碼
在使用的時候改變下 React 組件的 export 的方式,如咱們的 src/index.js 文件修改成以下:
import React from 'react';
import ReactDOM from 'react-dom';
import { hot } from 'react-hot-loader'; // 引入 hot 模塊
class HelloReact extends React.Component{
constructor(props) {
super(props);
}
render(){
return( <div>Hello React</div>);
}
}
export default hot(module)(HelloReact); //修改 export 時候的方式
ReactDOM.render(<HelloReact />, document.getElementById('root'));
if (module.hot) {
module.hot.accept('./print.js', function(){
console.log("Accepting the updated printMe module!");
printMe();
})
}
複製代碼
此時運行 ./node_modules/.bin/webpack-dev-server --mode=development
,再嘗試修改 index.js 內容你會發現頁面更新了,可是瀏覽器並無刷新。
這樣咱們就構建了一個自動編譯,自動更新的 React 的開發環境。
這個重構和開發環境搭建無關,只是在引入了 react-hot-loader
後,會發現如今的 src/index.js
此時顯得很不優雅,這給由於 ReactHello
做爲一個組件本就應該抽取爲一個獨立的文件:
新建文件:src/ReactHello.js
:
import React from 'react';
import { hot } from 'react-hot-loader'
class HelloReact extends React.Component{
constructor(props) {
super(props);
}
render(){
return( <div>Hello React</div>);
}
}
export default hot(module)(HelloReact);
複製代碼
調整 src/index.js
:
import React from 'react';
import ReactDOM from 'react-dom';
import HelloReact from './HelloReact.js';
ReactDOM.render(<HelloReact />, document.getElementById('root'));
if (module.hot) {
module.hot.accept('./print.js', function(){
console.log("Accepting the updated printMe module!");
printMe();
})
}
複製代碼
這樣看着就舒服多了。
文章配合使用的 Demo 地址:github.com/wewin11235/… 倉庫的 stage2 分支