一步步搭建React项目(二):使用webpack配置开发环境
varHarrie opened this issue · 17 comments
上一个教程中,我们已经把一个react项目跑起来了,但是怎么看都觉得太过于敷衍,在这次教程中我们将使用webpack配置一个称心的开发环境
本教程webpack版本号为2+
接下来的教程,将在上一个教程项目的基础上完成
主要完成以下改进:
引入webpack,实现模块管理
实现监听文件改动,自动编译并刷新浏览器
实现热替换(HMR)
1. 初步引入webpack:实现模块管理
在根目录下创建webpack.dev.js
文件:
const path = require('path')
const root = __dirname
module.exports = {
// 入口文件
entry: path.resolve(root, 'src/main.js'),
// 出口文件
output: {
filename: 'bundle.js',
path: path.resolve(root, 'dist')
},
// loaders
module: {
rules: [
{test: /\.jsx?$/, use: ['babel-loader'], exclude: /node_modules/}
]
}
}
这里我们通过webpack
去执行babel
进行编译,所以将babel
的配置抽出到一个文件,根目录下创建.babelrc
:
{
"presets": [
["es2015", {"modules": false}], // webpack 2 本身已支持es6 module
"react"
]
}
将缺少的包都安装上:
$ npm install --save react react-dom
$ npm install --save-dev webpack babel-cli babel-loader babel-preset-es2015 babel-preset-react
当前的package.json
模块如下:
"dependencies": {
"react": "^15.4.2",
"react-dom": "^15.4.2"
},
"devDependencies": {
"babel-cli": "^6.23.0",
"babel-loader": "^6.3.2",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.23.0",
"webpack": "^2.2.1"
}
最后修改一下现有的文件:
- 改一下
index.html
的script引入位置:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>React Demo</title>
</head>
<body>
<div id="app"></div>
<script src="dist/bundle.js"></script>
</body>
</html>
main.js
中使用import
引入模块
import React from 'react'
import ReactDOM from 'react-dom'
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('app')
)
- 修改
package.json
的scripts
"scripts": {
"dev": "webpack --config webpack.dev.js"
}
测试看看,编译之后打开浏览器
$ npm run dev
2. 完善webpack配置:实现监听文件改动,自动编译并刷新浏览器
实现监听文件改动然后自动编译新的bundle.js
,我们需要用到webpack-dev-server
去创建一个本地服务器,同时,可以结合html-webpack-plugin去生成index.html
,先安装:
$ npm install webpack-dev-server html-webpack-plugin --save-dev
先说说html-webpack-plugin
的使用
将我们根目录下的index.html
改名为template.html
,顾名思义,现在作为一个模板,通过插件会在dist
中生成一个对应的index.html
文件,template.html
中去掉多余的东西:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>React Demo</title>
</head>
<body>
<div id="app"></div>
<!-- js文件会自动插入到这里,无需自己填写 -->
</body>
</html>
在webpack.dev.js
中加入html-webpack-plugin
的配置:
// 引入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
// 其他配置保持不变
// ...
plugins: [
new HtmlWebpackPlugin({
title: 'React Demo',
template: path.resolve(root, 'template.html')
})
]
}
现在通过npm run dev
,就能看到生成的dist/index.html
接下来引入webpack-dev.server
,webpack.dev.js
配置修改如下:
module.exports = {
entry: [
'webpack-dev-server/client',
path.resolve(root, 'src/main.js')
],
output: {
filename: 'bundle.js',
path: path.resolve(root, 'dist'),
publicPath: '/'
},
// ...
// 其他配置保持不变
// ...
devServer: {
contentBase: path.resolve(root, 'dist'),
publicPath: '/',
port: 8080,
historyApiFallback: true
}
}
package.json
中的scripts
修改如下:
"scripts": {
"dev": "webpack-dev-server --config webpack.dev.js"
}
通过npm run dev
就可以启动一个本地服务器了,只要文件有改动,就会自动刷新浏览器
3. 完善webpack配置:实现热替换(HMR)
自动刷新依然不尽兴,有时候仅仅改动了某个组件的细微地方(改动文案、样式等等),然后导致整个页面刷新了,有些调试步骤又得重新来一次
下面将讲解如何实现react的热替换
实现热替换需要用到react-hot-loader
,使用npm安装:
(该教程发布时,需要添加@next
才能安装3.x.x
版本)
$ npm install --save-dev react-hot-loader@next
更改webpack.dev.js
的配置:
// ...
const webpack = require('webpack')
module.exports = {
entry: [
'react-hot-loader/patch', // 激活HMR
'webpack-dev-server/client',
'webpack/hot/only-dev-server',
path.resolve(root, 'src/main.js')
],
// ...
// 其他配置保持不变
// ...
devServer: {
hot: true, // 激活服务器的HMR
contentBase: path.resolve(root, 'dist'),
publicPath: '/',
port: 8080,
historyApiFallback: true
},
plugins: [
new HtmlWebpackPlugin({
title: 'React Demo',
template: path.resolve(root, 'template.html')
}),
new webpack.HotModuleReplacementPlugin(), // 热替换插件
new webpack.NamedModulesPlugin() // 执行热替换时打印模块名字
]
}
.babelrc
也有相应的改动:
{
"presets": [
["es2015", {"modules": false}],
"react"
],
"plugins": [
"react-hot-loader/babel" // 添加HMR支持
]
}
为了测试热替换是否生效,在src
目录添加一个App.js
文件,作为根组件:
import React from 'react'
const App = () => (
<h1>Hello, world!</h1>
)
export default App
main.js
中引入并渲染App
,同时又一些为支持HMR的改动:
import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import App from './App'
const render = (App) => {
ReactDOM.render(
<AppContainer>
<App />
</AppContainer>,
document.getElementById('app')
)
}
render(App)
if (module.hot) {
module.hot.accept('./App', () => render(App))
}
重新运行npm run dev
,在对App
中的Hello, world!
进行改动时,页面并不是整个刷新的,至此完成热替换的配置
App.js 中应该是
const App = () => ( return ( <h1>Hello, world!</h1> ) )
@varHarrie 是我本地写错了,你的demo 没问题 👍
Module not found: Error: Can't resolve 'run' in 'D:\work\test-demo\react-demo'
@ multi react-hot-loader/patch webpack-dev-server/client webpack/hot/only-dev-server ./src/main.js run dev
这个报错是什么原因?求大神解救~
@varHarrie 这是main.js的内容,就是按照上面的代码写的~
import React from 'react'
import ReactDOM from 'react-dom'
import { AppContanier } from 'react-hot-loader'
import App from './App'
const render = (App) => {
ReactDOM.render(
,
document.getElementById('app')
)
}
render(App)
if (module.hot) {
module.hot.accept( './App', () => render(App) )
}
@varHarrie 解决了是一个单次写错了T_T
有一个问题:我按照题主的demo配置好之后,没有问题,然后开始做一些改动练习,但是发现对于this的获取出了问题,看了很多这方面的但是不太懂
import React from 'react'
const MyComponent = React.createClass({
getInitialState: () => {
return {liked: false};
},
handleClick: (event) => {
this.setState({liked: !this.state.liked});
},
render: () => {
console.log(this); // 这里this一直指向 function App()
const text = this.state.liked ? 'liked' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>You {text} this. Click to toggle.</p>
);
}
});
const App = () => (
<MyComponent />
);
export default App
其余代码和题主的一样
@ZJH9Rondo
对于使用React.createClass
创建的组件,成员函数使用普通函数
而不是箭头函数
,即:
render: () => {...}
改为render () {...}
,其他函数也都改过来试试。
项目和总结都写的很棒,请问一下每次生成的bundle.js/index.js都在哪里,看了下dist文件夹下并没有生成新的文件..
请问如何配置样式规则,比如less这样的
《深入浅出 Webpack》是国内第一本系统全面讲解 Webpack 的图书,涵盖了 Webpack 的入门、配置、实战、优化、原理。
前面的都可以跑起来,但是到了热替换这里,就报错了
ERROR in ./src/index.js
Module parse failed: Unexpected token (13:4)
You may need an appropriate loader to handle this file type.
const render = (App) => {
ReactDOM.render(
<AppContainer>
<App />
</AppContainer>
,
@ multi (webpack)-dev-server/client?http://localhost:3333 webpack/hot/dev-server babel-polyfill react-hot-loader/patch webpack-dev-server/client webpack/hot/only-dev-server ./src/index.js
没有分号看着很闹心。
2 里 有这个报错
[webpack-cli] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
- options has an unknown property 'publicPath'. These properties are valid:
object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?,eware?, onListening?, open?, port?, proxy?, setupExitSignals?, static
我把webpack.dev.js
里的 devServer 整个注掉之后 好了,求问为什么
3 也有问题
注掉 webpack.dev.js 里的 devServer 里的
contentBase: path.resolve(root, 'dist'),
publicPath: '/',
这两行就好了
怀疑写法和版本有兼容性
@ass315 版本差异导致的,写这篇文章时是webpack@2.x,现在已经是5.x了,很多配置参数都改了,具体要查阅webpack的官方文档了