#####概述
一个常规的React项目通常用ES6开发,然后用babel从ES6编译到ES5,最后再用webpack整合各个文件和其对应的依赖项,打包成单个js文件使用。
但是这样做也存在问题,如果当前端代码与后台数据和逻辑紧密关联,调试的时候会比较麻烦。而且目前没有浏览器支持ES6的语法,所有ES6的项目需要编译成ES5才能运行。
本文的目的是建立一种方法,达到以下目的:
ES6
和JSX
语法)的编译React.creatClass
方法创建React组件最后的目录结构
react-cesium/
node_modules/ #开发时依赖模块
package.json #项目配置文件,用于开发时的包管理等
css/ #样式文件,无需编译,直接用css语法编写
html/ #html文件,无需编译,添加对应js文件即可
js/ #编译好的ES5文件,多个文件相互独立,无压缩,也可以直接修改(但是不建议)
src/ #源码文件,内有JSX和ES6,需编译,编译好的文件存放在js/文件夹下
cesium/
CesiumGlobe.js
...
App.js
...
下面我们来讲讲ES6和传统JavaScript的差别,同时把React ES6的项目调整成传统的JavaScript项目。
#####依赖项
ES6将每个依赖项作为一个模块加载到JavaScript文件中,用import
导入模块,用export
导出模块。比如,导入React依赖:
//导入外部模块
import React, {Component} from 'react';
//导入自己编写的模块
import CesiumGlobe from "./cesium/CesimGlobe";
传统的JavaScript将所有的依赖项通过<script>
标签加载进来,每一个依赖项成为一个全局变量。比如,加载Cesium库和React:
//导入外部模块
<script src="./node_modules/cesium/Build/Cesium/Cesium.js" charset="utf-8"></script>
<script src="./node_modules/react/dist/react.min.js" charset="utf-8"></script>
//导入自己编写的模块
<script type="text/javascript" src="./lib/cesium/CesiumGlobe.js"></script>
两者都有优缺点,ES6的语法可以实现局部模块的调用,不会让模块和模块内部的变量成为全局变量,避免全局污染,但是import
和export
的语法需要通过babel和webpack编译打包才能用在浏览器中。传统JavaScript的文件不需要编译直接使用,比如导入了Cesium,就直接使用Cesium.方法名
调用方法,Cesium
本身是一个全局变量。
#####生成组件
React项目中以组件的形式封装功能和视图。ES6中,实现用class 组件名 extends React.Component{}
的方法继承React的组件类,然后编写相关的函数和渲染功能:
class InputControlES6 extends React.Component {
constructor(props) {
super(props);
// 设置初始状态值,在constructor构造函数中
this.state = {
text: props.initialValue || 'placeholder'
};
// 组件内部方法需要手动bind到this对象中
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
text: event.target.value
});
}
render() {
return (
...
);
}
}
ES5中通过React.createClass()
方法返回一个组件类,赋值给一个变量,这个变量就作为这个组件类存在。
var InputControlES5 = React.createClass({
// 设置初始状态值,用getInitialState方法
getInitialState: function() {
return {
text: this.props.initialValue || 'placeholder'
};
},
//方法无需bind到this上
handleChange: function(event) {
this.setState({
text: event.target.value
});
},
render: function() {
return (
...
);
}
});
#####JSX
JSX是React的新发明,将HTML和JavaScript结合使用,全都写在JS代码中。个人认为,一开始你可能会有些不适应这样的写法,但是很快你就会依赖上JSX,因为他实在是太方便了。但是浏览器同样不支持JSX,所以还是需要用Babel编译成传统的JavaScript(这不是ES6和ES5的不同,是JSX这种语法对浏览器的不兼容)。 相同的一段代码,用JSX写是这样的:
<div>
Type something:
<input onChange={this.handleChange}
value={this.state.text} />
</div>
用传统的JavaScript结合React.createElement
方法写是这样的
React.createElement(
'div',
null,
'Type something:',
React.createElement('input', { onChange: this.handleChange,
value: this.state.text })
);
#####如何将ES6和JSX编译成ES5 Babel这个工具是目前被广泛使用的前端编译工具,能将ES6,JSX,TypeScript,CoffeeScript等语法的JS脚本编程成传统的JavaScript。 要使用Babel,你需要在电脑上安装node和npm,先用npm下载babel的依赖:
# Babel命令行工具
npm install --save-dev babel-cli
# ES2015转码规则
$ npm install --save-dev babel-preset-es2015
# react转码规则
$ npm install --save-dev babel-preset-react
# ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3
Babel的配置文件是.babelrc,存放在项目的根目录下。使用Babel的第一步,就是配置这个文件。 该文件用来设置转码规则和插件,基本格式如下。
{
"presets": [
"es2015",
"react",
"stage-2"
],
"plugins": []
}
安装配置完成后,使用命令行编译单个文件或者文件夹:
./node_modules/.bin/babel src -d lib
上面的命令将src
文件夹下的代码都编译到lib
文件夹中。
使用babel会自动识别代码中不是传统JavaScript的部分,编译成传统JavaScript,最后生成的代码没有打包成一个文件,还是原来的目录结构。只要用<script>
标签将每个文件引入html文件,就可以使用了。
当然,你如果觉得ES6和JSX的语法不方便,可以直接使用ES5编写代码,Babel会忽略ES5的语法,不编译也不改变,没有什么影响。
#####为什么要保留ES6和JSX,而不是直接使用ES5开发
我们来看几个ES6和ES5比较的例子:
######解构赋值
//ES6
const {homeView={},polylines={},isMobile=false,sceneMode=2} = this.state;
//ES5
var _state = undefined.state,
_state$homeView = _state.homeView,
homeView = _state$homeView === undefined ? {} : _state$homeView,
_state$polylines = _state.polylines,
polylines = _state$polylines === undefined ? {} : _state$polylines,
_state$isMobile = _state.isMobile,
isMobile = _state$isMobile === undefined ? false : _state$isMobile,
_state$sceneMode = _state.sceneMode,
sceneMode = _state$sceneMode === undefined ? 2 : _state$sceneMode;
上面的代码,将this.state
对象中的homeView
,polylines
,isMobile
,sceneMode
这四个成员变量赋值给同名的独立变量,如果this.state
中没有值,就给一个默认值。比如如果this.state.sceneMode
不存在,就默认为2。
######箭头函数
//ES6
const newArr = arr.map(item=>item+1);
//ES5
var newArr = arr.map(function (item) {
return item + 1;
});
上面代码遍历arr
数组,返回一个新数组newArr
,ES6支持箭头函数,可以省去function
和return
等关键字。
######数组或对象展开
//ES6
const newArr3 = [...newArr2,1,3,4,5];
const newObj = {...obj,key:value};
//ES5
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var newArr3 = [].concat(_toConsumableArray(newArr2), [1, 3, 4, 5]);
var newObj = _extends({}, obj, { key: value });
上面代码是ES6中非常好用的功能,用...
将数组或者对象展开,在后面添加多个新的数组元素或者对象的键值对。常用于React中setState
方法。展开对象的时候,如果后面添加的key与原对象中的key相同,就会冲掉原来的值,换上新的值。
######JSX的魅力
个人认为JSX的魅力是数据驱动视图变化,不需要添加太多人工的逻辑。举个例子,比如一个Cesium的指南针,要实现转盘旋转时,地球跟着旋转。看似简单的逻辑,需要用到鼠标按下、移动和抬起事件,要用CSS中的tranform特性旋转图标。下面的代码是React的render
中的逻辑,每次this.state
有值更新,就会重新调用render:
const rotationMarkerStyle = {
transform: 'rotate(-' + this.state.orbitCursorAngle + 'rad)',
WebkitTransform: 'rotate(-' + this.state.orbitCursorAngle + 'rad)',
opacity: this.state.orbitCursorOpacity
};
const outerCircleStyle = {
transform: 'rotate(-' + this.state.heading + 'rad)',
WebkitTransform: 'rotate(-' + this.state.heading + 'rad)',
opacity: ''
};
<div className={Styles.compass} title ={description} onMouseDown ={this.handleMouseDown} onDoubleClick ={this.handleDoubleClick} onMouseUp ={this.resetRotater}>
<div className={Styles.outerRing} style={outerCircleStyle}></div>
<div className={Styles.innerRing} title='点击拖拽旋转指南针'></div>
<div className={Styles.rotationMarker} style={rotationMarkerStyle}></div>
</div>
上面的JSX中,this.state.heading
和this.state.orbitcursorAngle
是随着鼠标点击变化的,每一次变化,style
的值也跟着变化,实现旋转指南针指针的功能。
#####总结
为了实现使用传统web项目的目录结构,不打包js文件,我们需要牺牲一些ES6的新特性(比如import
和export
方便的导入导出模块),不过大部分方便的功能是可以保留的。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。