webpack中提供了热模块更新的功能,在不刷新整个页面的情况下来替换某些更变的组件,而这样做的最大的好处就在于状态的保存。比如我们前面在输入框中输入的内容,就不会在我们热模块替换以后被刷新掉,让我们要重新再输入一次了。
而react的构建现在一般还是基于webpack的,webpack也提供了对应的插件。react-hot-loader
https://github.com/gaearon/react-hot-loader
跟着github上面的例子我们就可以很快进行搭建了。
这里要注意几点。
我们创建项目的时候是类似creat-react-app的方式创建项目的。
creat-react-app的创建方式是有两个js文件,一个是index.js,一个是App.js
App.js是根组件,而index.js才是来执行初始化以及渲染这些工作的。而我们有的时候是把这两个合二为一了。这样就跟官网的对不上会有点。
下面给出一个demo来进行配置
目录结构
App.jsx
import React from 'react'; import { hot } from 'react-hot-loader'; import { Hello } from './components/hello.jsx'; require('./font.js'); class App extends React.Component { constructor(){ super(); console.log("App"); } render() { return ( <div> <h1> <Hello /> Hello<br /> <input type="text" name="" id="" /> </h1> <svg className="icon-svg" aria-hidden="true"> <use xlinkHref="#d-icon-gou-check"></use> </svg> </div> ) } } export default hot(module)(App)
这里的svg可以进行删除
index.jsx
import React from 'react' import { render } from 'react-dom' import App from './App.jsx' import './scss/index.scss'; const root = document.createElement('div') document.body.appendChild(root) render(<App />, root)
这里我们可以发现,在index.jsx中是自行创建了一个div所以我们在模版文件中其实可以不用哪个我们经常使用的
<div id=root></div>
了
子组件
hello.jsx
import React from 'react'; import { hot } from 'react-hot-loader'; export class Hello extends React.Component{ render(){ return ( <div>hello</div> ) } }
可以发现,我们的子组件并没有使用hot这个东西,代表只要我们给根组件设置hot就好了。
官网上给出的例子是使用--hot进行启动,如果我们只想在webpack中进行配置而不是使用npm script
我们可以这样配置webpack的config文件。
webpack.config.dev.js
console.log("dev"); const path = require('path'); const webpack = require('webpack'); const htmlWebpackPlugin = require('html-webpack-plugin'); const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin'); const HotModuleReplacePlgun = require('webpack/lib/HotModuleReplacementPlugin'); module.exports = { entry: ['react-hot-loader/patch', './src/index.jsx'], output: { path: path.resolve(__dirname, 'dist'), publicPath: '/dist/', filename: 'js/[name].js' }, module: { rules: [ { test: /\.jsx$/, exclude: /(node_modules)/, include: path.resolve(__dirname, 'src'), use: { loader: "babel-loader", options: { presets: ['env', 'react'], plugins: ['react-hot-loader/babel'], } } }, { test: /\.css$/, include: path.resolve(__dirname, 'src'), use:[ "style-loader", "css-loader" ] }, { test: /\.scss$/, include: path.resolve(__dirname, 'src'), use:[ "style-loader", "css-loader", "sass-loader" ] }, { test: /\.(gif|png|jpg)$/, include: path.resolve(__dirname, 'src'), use: [{ loader: "url-loader", options: { limit: 8192, name: "assets/img/[name].[ext]" } }], }, { test: /\.(eot|svg|ttf|woff|woff2)$/, include: path.resolve(__dirname, 'src'), use: [{ loader: "url-loader", options: { limit: 8192, name: "assets/[name].[ext]" } }], }, ] }, plugins: [ new htmlWebpackPlugin({ template: "./src/index.html" }), // new ExtractTextWebpackPlugin("css/[name].css"), // new ExtractTextWebpackPlugin("scss/[name].scss"),使用这个就无法开启热模块替换了 // 提取公共模块 new webpack.optimize.CommonsChunkPlugin({ name: 'common', filename: "js/base.js", }), // 设置热更新 new HotModuleReplacePlgun(), new webpack.NamedModulesPlugin(), ], devServer: { port: 8888, // 使用热模块更新,必须安装插件 hot:true, // 自动在启动后打开浏览器 open : true, openPage:'dist/', }, resolve: { // 配置寻找第三方库的时候的位置 modules: [path.resolve(__dirname, 'node_modules')], // extensions:['jsx','js','json'] }, watchOptions: { // 不监听这些文件 ignored: /node_modules/ }, devtool: 'source-map' };
这里最关键的是
jsx的配置以及入口文件的配置
然后设置hot为true
这里要注意的一点是,我们没有使用单独提取文件的功能把css进行单独抽出
这个原因是因为他不支持热模块更新的原型,一旦单独提取了css文件,那么我们修改了scss跟css都不会立即生效。
所以我们需要两个webpack.config.js文件来实现开发跟生产的区分。
最后是npm的执行文件
问题得以解决。
因为技术的更新其实很快。可能当你看到这篇博文的时候,里面的方法已经跟最新版的对不上了,这个时候我还是建议你去github上面把这个项目clone下来。然后执行一下里面的例子看看,这个样子应该会好一点。