React是纯View层,但在最新的React V16版本中,按传统的纯js脚本开发已经不能适应项目的需要,配合Webpack打包,可以够更便捷地管理项目和更新插件;再者,使用ES6开发,将大大地提高团队地开发效率。本篇主要是介绍如何部署React+Webpack组合搭建,那么我们一步步来吧。
创建一个web项目ReactWebpack,目录结构如图所示:
app目录:存放核心代码。
components目录:存放React组件。
css目录:存放样式文件。
build目录:存放打包编译后的js文件。
index.html:展示页。
main.js:入口js文件。
假设你本机已经安装nodejs,可以使用命令行npm,我用的版本是node-v6.0.0-x64.msi,百度即可下载。
为什么用cnpm?因为cnpm是淘宝npm镜像,下载速度会更流畅,当然也可以使用npm。
安装命令行:
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
package.json是一个标准的npm说明文件,里面蕴含了丰富的信息,包括当前项目的依赖模块,自定义的脚本任务等,通过这个文件即可知道您的项目跑起来需要哪些模块库,项目作者、版本等。
在dos环境下,检索至项目根目录,执行命令行:
$ npm init
输入项目名称:react_demo,其它直接enter即可,成功后如图所示:
此时,会在项目的根目录下多了一个package.json文件,文件内容如下:
webpack主要用于对项目的打包和管理,安装成功后,会在项目根目录上多了一个node_modules文件夹,里面是存放所有npm下载的库模块。安装命令行:
$ cnpm install --save-dev webpack
安装成功后如图所示:
webpack.config.js是打包配置文件,文件内容如下:
var webpack = require('webpack');
module.exports = {
//根目录
context: __dirname + "",
//文件入口
entry: {
index: './app/main.js'
},
//文件出口
output: {
path: __dirname + "/build", // 设置输出目录
filename: '[name].bundle.js', // 输出文件名
},
module: {
//loaders加载器
loaders: [{
test: /\.(js|jsx)$/, //【一个匹配loaders所处理的文件的拓展名的正则表达式,这里用来匹配js和jsx文件(必须)】
exclude: /node_modules/, //【屏蔽不需要处理的文件(文件夹)(可选)】
loader: 'babel-loader' //【loader的名称(必须)】此处为可以转换ES6和JSX语法的加载器
},
{
test: /\.css$/,
loader: "style!css" //.css文件使用 style-loader 和 css-loader 来处理
},
{
test: /\.(jpeg|png|gif|svg)$/i,
loader: 'url-loader?limit=8192&name=[name].[ext]' //图片文件使用 url-loader来处理,小于8kb的直接转为base64
}
]
},
// 插件
plugins: [
// 默认会把所有入口节点的公共代码提取出来,生成一个common.js
//new webpack.optimize.CommonsChunkPlugin('common'),
// 压缩代码抬头注释
new webpack.BannerPlugin('此处添加打包抬头注释!'),
// 代码压缩
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
],
//由于压缩后的代码不易于定位错误,配置该项后发生错误时即可采用source-map的形式直接显示你出错代码的位置
devtool: 'eval-source-map',
//其它解决方案配置
resolve: {
// 配置简写,配置过后,书写该文件路径的时候可以省略文件后缀。如require("common")
extensions: ['.js', '.jsx', '.coffee']
}
}
详细的格式我就不解释了,里面都有注释。
执行命令行:
$ cnpm install --save-dev react react-dom
执行命令行:
cnpm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
在项目根目录下新建.babelrc文件,就是只有后缀名的文件,如图所示:
//.babelrc
{
"presets": [
"react",
"es2015"
]
}
Babel其实可以完全在webpack.config.js中进行配置。但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。
新建如下图目录所示文件(其中index.bundle.js为打包时才会有的文件,可以不用新建):
Component.jsx脚本如下:
import React from 'react';
class Component extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
handleClick(e) {
}
render() {
return (
Hello {this.props.name}!
)
}
}
//导出组件
export default Component;
color.css脚本如下:
.red{
color: red;
}
main.js脚本如下:
//ES6
import React from 'react';
import ReactDom from 'react-dom';
import Component from './components/Component.jsx';
ReactDom.render(
,
document.getElementById('content')
);
(注)SayHello.jsx为ES5写法,后面会介绍到。
执行命令行:
$ webpack -d
(注)监察打包 webpack --watch 其中copy+c结束监察,监察是指只要代码有变动即会自动打包。
打包成功后会在build目录下自动生成一个index.bundle.js文件,打开index.html文件,写入脚本:
至此,我们已经成功部署好了React+Webpack组合开发环境。当然,毕竟并不是所有初学者都学过ES6语法,那么我们可否用ES5组合开发呢?答案是可以的。我们把SayHello.jsx写入脚本如下:
var React = require('react');
var createReactClass = require('create-react-class');
var SayHello = createReactClass({
render:function(){
var style = {color:"red"};
return (Hello ES5(2)
);
}
});
exports = module.exports = SayHello;
然后更改main.js脚本为:
//ES6
//import React from 'react';
//import ReactDom from 'react-dom';
//import Component from './components/Component.jsx';
//
//ReactDom.render(
// ,
// document.getElementById('content')
//);
//ES5 写法1
var React = require('react');
var ReactDOM = require('react-dom');
var createReactClass = require('create-react-class');
var SayHello = createReactClass({
render:function(){
return (Hello ES5(1)
);
}
});
ReactDOM.render( ,document.getElementById("content"));
//ES5 写法2
//var React = require('react');
//var ReactDOM = require('react-dom');
//var createReactClass = require('create-react-class');
//var SayHello = require('./components/SayHello');
//ReactDOM.render( ,document.getElementById("content"));
打包后执行结果(上例中写法2为引入外部组件方式,效果与写法1一致):
有反馈说根据上面代码敲了一遍,发现打包不了。这是因为我再写这个demo的时候用的是webpack3.x版本,现在最新版本已经是4.23.0,可以通过命令webpack -v查看自己的版本。此处打包建议需要版本一致,所以可修改package.json文件,修改如下:
{
"name": "react_demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack"
},
"author": "huangzp",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"create-react-class": "^15.6.2",
"react": "^16.1.1",
"react-dom": "^16.1.1",
"webpack": "^4.23.0"
}
}
然后通过命令先把原有node_modules清理:
npm install rimraf -g
rimraf node_modules
再重新初始化:
npm install
关于使用webpack4.x还有一个地方需要修改,就是该版本不再支持UglifyJs代码压缩方法,所以webpack.config.js文件修改如下:
var webpack = require('webpack');
module.exports = {
//根目录
context: __dirname + "",
//文件入口
entry: {
index: './app/main.js'
},
//文件出口
output: {
path: __dirname + "/build", // 设置输出目录
filename: '[name].bundle.js', // 输出文件名
// filename:'./build/[name].bundle.js'
},
module: {
//rules加载器
rules: [{
test: /\.(js|jsx)$/, //【一个匹配rules所处理的文件的拓展名的正则表达式,这里用来匹配js和jsx文件(必须)】
exclude: /node_modules/, //【屏蔽不需要处理的文件(文件夹)(可选)】
loader: 'babel-loader' //【loader的名称(必须)】此处为可以转换ES6和JSX语法的加载器
},
{
test: /\.css$/,
loader: "style-loader!css-loader" //.css文件使用 style-loader 和 css-loader 来处理
},
{
test: /\.(jpeg|png|gif|svg)$/i,
loader: 'url-loader?limit=8192&name=[name].[ext]' //图片文件使用 url-loader来处理,小于8kb的直接转为base64
}
]
},
// 插件
plugins: [
// 默认会把所有入口节点的公共代码提取出来,生成一个common.js
//new webpack.optimize.CommonsChunkPlugin('common'),
// 压缩代码抬头注释
new webpack.BannerPlugin('此处添加打包抬头注释!'),
// 代码压缩
// new webpack.optimize.UglifyJsPlugin({
// compress: {
// warnings: false
// }
// })
],
// 代码压缩
optimization: {
minimize: false // 当发布环境打包时需设为true
},
//由于压缩后的代码不易于定位错误,配置该项后发生错误时即可采用source-map的形式直接显示你出错代码的位置
devtool: 'eval-source-map',
//其它解决方案配置
resolve: {
// 配置简写,配置过后,书写该文件路径的时候可以省略文件后缀。如require("common")
extensions: ['.js', '.jsx', '.coffee']
}
}
这样,webpack -d即可打包成功。
// 综合应用ES5
var React = require('react');
var ReactDOM = require('react-dom');
var createReactClass = require('create-react-class');
var $ = require('jquery');
// 内联样式
var stateCSS = {
cursor: "pointer"
};
// 混合函数对象
var mixinCommon = {
componentWillMount: function() {
console.log("mix componentWillMount!");
}
}
// 子组件数据传递
var ChildComp = createReactClass({
componentDidMount: function() {
this.setState({
dataCallBackFun: this.props.callBackFun
});
},
getInitialState: function() {
return {
dataFromParant: "",
dataCallBackFun: null,
respData: ""
}
},
// 静态方法
statics: {
doMethod: function() {
console.log("静态方法被调用!");
}
},
setInputData: function(value) {
this.setState({
dataFromParant: value
});
},
childDataChangeHandle: function(e) {
this.setState({
respData: e.target.value
});
},
clickHandle: function(e) {
this.state.dataCallBackFun(this.state.respData);
},
staticHandle: function(e) {
ChildComp.doMethod();
},
render: function() {
return(
);
}
});
// 列表组件展示
var ListComp = createReactClass({
render: function() {
return(
{
this.props.listData.map(function(item,index){
return - ID:{item.id},值:{item.data}
})
}
);
}
});
// 父组件
var MyComp = createReactClass({
// 混合函数mixin
mixins: [mixinCommon],
// 默认参数
getDefaultProps: function() {
return {
msg: "helloui.prop"
}
},
// 状态变量
getInitialState: function() {
return {
param: "helloui.state",
inValue: "",
callBackValue: "",
list: []
}
},
// 操作事件
clickHandle: function(e) {
this.setState({
param: "hello.clicked"
});
},
// 父子组件参数传递
inputDataChangeHandle: function(e) {
this.setState({
inValue: e.target.value
});
},
// 父子组件参数传递
putDataToChildHandle: function(e) {
this.refs.childcomp.setInputData(this.state.inValue);
},
// 父子组件参数传递
childCallBackFunHandle: function(resp) {
this.setState({
callBackValue: resp
});
},
// 生命钩子函数
componentDidMount: function() {
console.log('Mount挂载完成');
this.setState({
list: [{
id: "id1",
data: "data1"
},
{
id: "id2",
data: "data2"
},
{
id: "id3",
data: "data3"
}
]
});
// ajax请求
$.ajax({
url: "https://api.github.com/users/octocat/gists",
data: "",
type: 'GET',
timeout: 30000,
error: function(response, state) {
console.log(response);
},
success: function(response, state) {
console.log(response)
}
});
},
// 渲染
render: function() {
return(
默认参数:{this.props.msg}
状态变量:{this.state.param}
);
}
});
ReactDOM.render( , document.getElementById("content"));
// Element + Factory 用法
var EleComp = createReactClass({
render: function() {
return(
{this.props.text}
);
}
});
var EleFactory = React.createFactory(EleComp);
var MyElements = React.createElement('div', null, EleFactory({
text: 'element1'
}), EleFactory({
text: 'element2'
}));
ReactDOM.render(MyElements, document.getElementById("elements"));