React+Webpack+ES5、ES6 基本组合(纯View层面)的架构搭建

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zeping891103/article/details/78646602

为什么想起写这篇博客呢?首先要知道,React是纯View层,但在最新的React V16版本中,按传统的纯js脚本开发已经不能适应项目的需要(部份重要React函数已不支持);另一个重要原因是,配合Webpack打包,可以够更便捷地管理项目和更新插件;再者,使用ES6开发,将大大地提高团队地开发效率。作为一名合格的前端架构师,自然要选择更专业的项目开发管理方式。本篇主要是介绍如何部署React+Webpack+ES6组合开发项目,那么我们一步步来吧。

一.创建目录结构

创建一个web项目ReactWebpack,目录结构如图所示:

app目录:存放核心代码。

components目录:存放React组件。

css目录:存放样式文件。

build目录:存放打包编译后的js文件。

index.html:展示页。

main.js:入口js文件。

 

二.安装cnpm镜像

假设你本机已经安装nodejs,可以使用命令行npm,我用的版本是node-v6.0.0-x64.msi,百度即可下载。

为什么用cnpm?因为cnpm是淘宝npm镜像,下载速度会更流畅,当然也可以使用npm。

安装命令行:

$ npm install -g cnpm --registry=https://registry.npm.taobao.org

三.创建package.json文件

package.json是一个标准的npm说明文件,里面蕴含了丰富的信息,包括当前项目的依赖模块,自定义的脚本任务等,通过这个文件即可知道您的项目跑起来需要哪些模块库,项目作者、版本等。

在dos环境下,检索至项目根目录,执行命令行:

$ npm init

输入项目名称:react_demo,其它直接enter即可,成功后如图所示:

此时,会在项目的根目录下多了一个package.json文件,文件内容如下:

 

四.安装webpack

webpack主要用于对项目的打包和管理,安装成功后,会在项目根目录上多了一个node_modules文件夹,里面是存放所有npm下载的库模块。安装命令行:

$ cnpm install --save-dev webpack

安装成功后如图所示:

 

五.创建webpack.config.js文件

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']
	}
}

详细的格式我就不解释了,里面都有注释。

六.安装React

执行命令行:

$ cnpm install --save-dev react react-dom

七.安装babel

执行命令行:

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 (
            <div>Hello {this.props.name}!</div>
        )
    }
}

//导出组件
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(
    <Component name="ES6"/>,
    document.getElementById('content')
);

(注)SayHello.jsx为ES5写法,后面会介绍到。

九.打包

执行命令行:

$ webpack -d

(注)监察打包 webpack --watch 其中copy+c结束监察,监察是指只要代码有变动即会自动打包。
打包成功后会在build目录下自动生成一个index.bundle.js文件,打开index.html文件,写入脚本:

<!DOCTYPE html>
<html>

	<head>
		<meta charset="utf-8" />
		<title></title>
		<link type="text/css" rel="styleSheet" href="app/css/color.css" />
	</head>

	<body>
		<!--要插入React组件的位置-->
		<div id="content"></div>
		<!--引入bundle.js-->
		<script src="build/index.bundle.js"></script>
	</body>

</html>

打开浏览器运行,运行结果:

至此,我们已经成功部署好了React+Webpack+ES6组合开发环境。当然,毕竟并不是所有前端都学过ES6语法,那么我们可否用React+Webpack+ES5组合开发呢?答案是可以的。我们把SayHello.jsx写入脚本如下:

var React = require('react');
var createReactClass = require('create-react-class');
var SayHello = createReactClass({
	render:function(){
		var style = {color:"red"};
		return (<h1 style={style}>Hello ES5(2)</h1>);
	}
});
exports = module.exports = SayHello;

然后更改main.js脚本为:

//ES6
//import React from 'react';
//import ReactDom from 'react-dom';
//import Component from './components/Component.jsx';
//
//ReactDom.render(
//  <Component name="ES6"/>,
//  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 (<h1 className="red">Hello ES5(1)</h1>);
	}
});
ReactDOM.render(<SayHello />,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(<SayHello />,document.getElementById("content"));

打包后执行结果(上例中写法2为引入外部组件方式,效果与写法1一致):

项目源码下载地址:http://download.csdn.net/download/zeping891103/10134852

(新补充)关于webpack -d命令打包失败问题

有反馈说根据上面代码敲了一遍,发现打包不了。这是因为我再写这个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!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
		//			}
		//		})
	],
	// 代码压缩
	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(<div>
				<input type="text" placeholder="双向绑定" readOnly="readOnly" disabled value={this.props.name}/>
				<input type="text" placeholder="由入参事件触发" readOnly="readOnly" disabled value={this.state.dataFromParant}/>
				<input type="text" placeholder="回调值" onChange={this.childDataChangeHandle} value={this.state.respData}/>
				<button onClick={this.clickHandle}>子组件回调</button>
				<button onClick={this.staticHandle}>静态函数调用</button>
		</div>);
	}
});

// 列表组件展示
var ListComp = createReactClass({
	render: function() {
		return(
			<div>
				<ul>
					{
			            this.props.listData.map(function(item,index){
			            	return <li key={item.id}>ID:{item.id},值:{item.data}</li>
			            })
			        }
				</ul>
			</div>
		);
	}
});

// 父组件
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(<div>
			<div>默认参数:{this.props.msg}</div>
			<div id="stateID" style={stateCSS} onClick={this.clickHandle}>状态变量:{this.state.param}</div>
			<div>
				<input type="text" placeholder="入参值" onChange={this.inputDataChangeHandle} value={this.state.inValue}/>
				<button onClick={this.putDataToChildHandle}>父组件入参</button>
				<ChildComp ref="childcomp" name={this.state.inValue} callBackFun={this.childCallBackFunHandle}></ChildComp>
				<input type="text" placeholder="由子组件回调触发" readOnly="readOnly" disabled value={this.state.callBackValue}/>
			</div>
			<div>
				<ListComp listData={this.state.list}></ListComp>
			</div>
			<div id="elements"></div>
		</div>);
	}
});
ReactDOM.render(<MyComp />, document.getElementById("content"));

// Element + Factory 用法
var EleComp = createReactClass({
	render: function() {
		return(
			<div>
				{this.props.text}
			</div>
		);
	}
});
var EleFactory = React.createFactory(EleComp);
var MyElements = React.createElement('div', null, EleFactory({
	text: 'element1'
}), EleFactory({
	text: 'element2'
}));
ReactDOM.render(MyElements, document.getElementById("elements"));

猜你喜欢

转载自blog.csdn.net/zeping891103/article/details/78646602
今日推荐