React 是一个 JavaScript 库,使用了一些ES6语法。如:箭头函数, 类, 模板字符串, let, 和 const 声明。
React.JS的特点
- 声明式设计 −React.JS采用声明范式,可以轻松描述应用。
- 高效 −React.JS通过对DOM的模拟,最大限度地减少与DOM的交互。
- 灵活 −React.JS可以与已知的库或框架很好地配合。
- JSX − JSX 是 JavaScript 语法的扩展。React.JS开发不一定使用 JSX ,但建议使用它。
- 组件 − 通过 React.JS构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
- 单向响应的数据流 − React.JS实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单
1 React.js 的安装
React.js 的导入使用有两种方式,分别是使用 CDN 导入和使用 npm 的 create-react-app
导入。
- 使用 CDN 导入,导入地址Demo页面如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<!-- react.min.js - React 的核心库 -->
<script src="https://cdn.bootcss.com/react/15.4.2/react.min.js"></script>
<!-- react-dom.min.js - 提供与 DOM 相关的功能 -->
<script src="https://cdn.bootcss.com/react/15.4.2/react-dom.min.js"></script>
<!-- babel.min.js - Babel 可以将 ES6 代码转为 ES5 代码 -->
<script src="https://cdn.bootcss.com/babel-standalone/6.22.1/babel.min.js"></script>
</head>
<body>
<div id="example"></div>
<!-- 要注意如果不添加 type="text/babel", 会报错 -->
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
</script>
</body>
</html>
- 使用
create-react-app
快速构建 React 开发环境
create-react-app
自动创建的项目是基于 Webpack + ES6 。
首先,全局安装 create-react-app
:
npm install -g create-react-app
然后在要创建项目的文件目录路径下,输入如以下语句用以初始化项目 “脚手架”
create-react-app [name]
最后启动以及生产最终项目文件的语法与 Vue 类似,分别是:
npm start
及
npm build
2 JSX
const element = <h1>Hello, world!</h1>;
查看上述这段代码,这种看起来既不是字符串变量也不是 HTML的东西,它被称为 JSX, 一种 JavaScript 的语法扩展。
JSX 用来声明 React 当中的 元素,推荐在 React 中使用 JSX 来描述用户界面。
JSX 乍看起来可能比较像是 模版语言,但事实上它完全是在 JavaScript 内部实现的。
JSX 本身其实也是一种表达式,在编译之后呢,JSX 其实会被转化为普通的 JavaScript 对象。
Babel 转译器会把 JSX 转换成一个名为 React.createElement()
的方法调用,即下面两种代码的作用是完全相同的:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
2.1 JSX 中使用 Javascript 表达式
书写 JSX 时,可以使用 花括号 导入任意的 Javascript 表达式,同时在 JSX 代码的外面扩上一个小括号,这样可以防止 分号自动插入。如:
const element = (<p>Hello,{getName(user)}</p>);
2.2 JSX 属性
使用引号来定义以字符串为值的属性:
const element = <div tabIndex="0"></div>;
同样可以引入 Javascript 表达式,如:
const element = <div tabIndex={getIndex()}></div>;
JSX 的特性更接近 JavaScript 而不是 HTML , 所以 React DOM 使用 camelCase 小驼峰命名 来定义属性的名称,而不是使用 HTML 的属性名称。如,
class
变成了className
,而tabindex
则对应着tabIndex
。
2.3 JSX 防注入攻击
你可以放心地在 JSX 当中使用用户输入,React DOM 在渲染之前默认会 过滤 所有传入的值。它可以确保你的应用不会被注入攻击。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(跨站脚本) 攻击。
3 元素渲染
元素是构成 React 应用的最小单位,React 元素可以是用户自定义的组件
与浏览器的 DOM 元素不同,React 当中的元素事实上是普通的对象,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致。
如同 Vue.js 类似,首先要在 HTML 页面中寻找一个根DOM节点,而要将 React 元素渲染到根DOM节点中,需要通过把它们都传递给 ReactDOM.render()
的方法,来达到将其渲染到页面上的目的。如:
var element = (<p>Hello</p>);
ReactDOM.render(
element,
document.getElementById('example')
);
3.1 更新元素渲染
与 Vue 不同,React 元素并不支持数据的双向绑定,当元素被创建之后,你是无法改变其内容或属性的。因此更新界面的办法是创建一个新的元素,然后将它传入 ReactDOM.render()
方法(或通过修改局部状态实现),如:
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
3.2 事件处理
React 元素的事件处理和 DOM元素的很相似,但是有一点语法上的不同:
- React 元素事件绑定的事件名称是小驼峰命名
- 采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM元素的写法)
如 DOM 元素绑定写法是: onclick="clickTest()"
,而 React 元素事件绑定的写法是 onClick={clickTest}
.
- 在阻止默认行为的时候,比如点击链接后阻止它跳转,此时不能像 DOM 元素那样直接返回一个
false
值,需要显示地调用e.preventDefault()
3.2.1 this
的绑定
同时还要注意的是 类的方法默认是不绑定 this
的,如果在没有绑定 this
的情况下,触发的事件方法使用了 this
的相关信息,返回的 this
是一个 undefined
而 React this
的绑定方式有几种:
class App extends React.Component {
constructor(props) {
super(props);
this.checkThis3.bind(this);
}
checkThis1 = (e) => {
console.log(e);
console.log(this);
}
checkThis2(e) {
console.log(e);
console.log(this);
}
checkThis3(e) {
console.log(e);
console.log(this);
}
render() {
return (
<div>
<div onClick={this.checkThis1}>声明事件方法时使用了箭头函数,绑定事件时没有显式绑定this,此时正常输出this与e</div>
<div onClick={this.checkThis2}>正常普通形式地声明事件方法,绑定事件方法时没有显式绑定this,此时输出this为undefind,正常输出e</div>
<div onClick={this.checkThis2.bind(this)}>正常普通形式地声明事件方法,绑定事件方法时显式绑定了this,此时正常输出this与e</div>
<div onClick={(e) => {this.checkThis2(e)}}>正常普通形式地声明事件方法,绑定事件方法时使用箭头函数绑定,正常输出this,想要事件event对象,需要在箭头函数中显示传入e</div>
<div onClick={this.checkThis3}>正常普通形式地声明事件方法,在类的构造函数中显式绑定了this,正常输出this与e</div>
</div>
);
}
}
4 组件
React 的组件就等同于 Vue 的组件,从概念上像是函数,它可以接收任意的输入值(称之为 props
),并返回一个需要在页面上展示的 React 元素。
4.1 定义组件
React 定义组件有两个方式,分别为 函数定义 以及 类定义。
- 函数定义,如下例子,它接收一个单一的
props
对象并返回了一个 React 元素:
function Welcome(prop) {
return <p>Hello, {prop.name}</p>
}
- 类定义,可以使用 ES6 的
class
来定义一个组件,而使用 类定义 的组件比 函数定义 的组件会多一些额外的属性
class Welcome extends React.Component {
render() {
return <p>Hello, {this.prop.name}</p>
}
}
值得注意的是与 Vue.js 一样,React 中组件的定义同样是只得一个根 DOM 包裹所有内容返回,另外
return
语句后最少要紧跟一个(
括号,不能直接换行
4.2 类定义组件的额外特性
使用类就允许我们使用其它特性,例如 局部状态、生命周期钩子
4.2.1 局部状态 state
上述讲到,对比 Vue,React 无法通过修改 props
的数值从而直接对页面进行一个数据同步化处理,需要再次使用 ReactDOM.render
方法渲染组件。此外还可以通过修改局部状态来实现,而局部状态就是在类定义的组件的一个额外特性。
一些输入 DOM 如 input
标签的数据同步更新也是依赖这个局部状态,局部状态 state
看起来与 props
类似,类定义组件时,需要在 类构造函数 中进行一个初始化声明,如:
构造函数是唯一能够初始化
this.state
的地方
class Welcome extends React.Component {
constructor(props) {
this.props = props;
this.state = {
name: "Seiei"
};
}
render() {
return <h1>{this.state.name}</h1>
}
}
想要更改 state
,需要通过显式调用 this.setState
形如:
this.setState(
{time: new Date()}
);
状态 state
和 props
都是异步更新的,如下例子:
var Text = React.createClass({
getInitialState: function() {
return {name: "react"};
},
keyUp: function(e){
this.setState({name: e.target.value});
console.log(this.state.name);
},
render: function() {
return (
<div className="a">
大家好,我是用{this.state.name}渲染出来的!
<input onKeyUp={this.keyUp} />
</div>
);
}
});
在上述例子中,每次修改 input
标签的内容时,视图都会相应的同步更新,但在控制台中输出的内容却并不一定是对应的数值,这说明的是 state
状态的更新是异步,在控制台中输出的数值是异步还没执行完的数值。
要解决这个方法可以调用 this.setState
的重载方法,在第二个参数里传入同步处理的函数。
如修改为:
this.setState({name: e.target.value}, () => {console.log(this.state.name);});
当对原先状态进行一些类似自加减的行为时,可以修改为:
this.setState((prevState) => ({isToggleOn: !prevState.isToggleOn}));
注意:那个类似箭头函数,箭头后跟小括号而不是花括号
4.2.2 生命周期钩子
类似于 Vue 中的 created
,beforeMount
,mounted
这样的生命周期钩子,React 的声明周期钩子函数如上图。
每当组件第一次加载到 DOM 中,此时称为 挂载,每当组件生成的 DOM 被移除的时候,此时称为 卸载。
同时由图可以看出,局部状态每次更新,需要更新页面的时候都会触发
render
函数。
4.3 条件渲染
跟 Vue 的条件渲染不同,React 的条件渲染没有关键字语法糖可以使用,React 的条件渲染跟 Javascript 条件判断一致,使用 Javascript 的 if
操作符或一些类如三元表达式来表示当前状态的元素,然后让 React 去渲染它们。
例如:
- 在定义函数组件时,通过
if
操作符分别返回不同的 React 组件 - 在
render
方法中,声明一个元素变量,根据if
操作符该变量的赋值也不同,最后将该变量进行一个返回 - 直接使用三元表达式
想要进行一个隐藏、显示组件的切换。在 if
操作符中返回 null
而不是它的渲染结果即可实现。
4.4 列表渲染
同样的相对Vue.js,React 的列表渲染同样没有关键字、语法糖可以使用。使用的是 类似 Javascript 中的 map()
函数,例如:
function UserComponent(props) {
return (
<ul>
{props.users.map((item, index) =>
(<li key={index}>{item.name}</li>)
)}
</ul>
);
}
class App extends React.Component {
userList = [{name: "Seiei"}, {name: "Taka"}]
render: function() {
return (
<div>
<UserComponent users={this.userList}/>
</div>
);
}
}
但如同 Vue.js 一样,当循环创造元素时,需要该这个元素赋予一个特殊的 Key
属性,Key
可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识,可以使用序列号索引作为 key
值。
当要注意的是:元素的key只有放在其环绕数组的上下文中才有意义,也就是是说如果 key
值是定义在循环元素之下的元素时,此时的 key
值是没有意义的,最好的方法就是 key
值定义在 map
方法中。
注意:React 的
map()
方法箭头后跟的无需花括号
4.5 受控组件
受控组件就是 React 中的用户输入元素,实质就是使用 state
局部状态达到数据双向同步更新的效果,如:
class NameInput extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<div>
<input value={this.state.value} onChange={this.handleChange} />
</div>
)
}
}
4.6 状态提升
如同 Vue.js 一样,有时候组件之间需要共用相同的数据源,这时候除了引入使用一些状态管理插件。最原始的方法就是将这些状态数据提升到他们最近的 父组件 当中进行管理。
此时,父组件可以将修改 state
状态的方法通过 props
属性传递给子组件,子组件便可以通过该方法从而能修改父组件中的 state
状态值。