上一篇文章 《webpack 快速构建 React 学习环境(1)》中介绍了构建一个最简单开发环境,这里接着完善这个开发环境,让它用起来更加的趁手。
看着篇文章前请先看 《webpack 快速构建 React 学习环境(1)》
本小结内容对应构建的项目源码:github.com/wewin11235/…,仓库的 stage2
分支
文章同步发布在个人博客站点
上一篇中构建的开发环境存在的问题
上一节搭建的开发环境不能热加载,每次文件改动后都需要重新编译,手动刷新页面。虽然使用 webpack --watch
命令在文件变化后能重新编译,但是仍需要手动刷新页面。webpack --watch
的方式还有个缺点,每次都是重新编译生成新的文件到 build 目录下, 文件多的时候这个编译过程就会慢。
webpack-dev-server
配合 HRM
可以构建一个完美的开发环境,改动保存后自动编译,无需手动刷新页面。
使用 webpack-dev-server
webpack-dev-server 主要是启动了一个使用 express 的 Http 服务器。它的作用主要是用来伺服资源文件。此外这个Http服务器和client使用了websocket通讯协议,原始文件作出改动后,webpack-dev-server 会实时的编译. webpack-dev-server 的时时编译并不会输出到 webpack.config.js 中指定的出口,编译生成的文件在内存中,也有效的提高了编译效率。
安装与配置:
npm in webpack-dev-server -D
复制代码
安装完成后: ./node_modules/.bin/webpack-dev-server
便可启动 server
ℹ 「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] (https://webpack.docschina.org/guides/hot-module-replacement)。 react 框架下还需要用到 react-hot-loader
开启 HotModuleReplace
改动有以下几处:
添加 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 框架下热更新支持(react-hot-loader)
安装 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-demo
这个重构和开发环境搭建无关,只是在引入了 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 分支
原文发布时间为:2018年06月29日
作者:蚂蚁哈哈哈
本文来源:掘金 如需转载请联系原作者