React 简单入门
目标:
- 类比vue入门 react
- 简单入门,着重react使用方法
- 教程暂时不议论 redux
安装
1 | npx create-react-app my-app |
注意:
第一行的npx
不是拼写错误 —— 它是 npm 5.2+ 附带的 package 运行工具。
安装结束之后,的 app.js , 一起见识一下 JSX
1 | import logo from './logo.svg'; |
认识 JSX
1 | const element = <h1>Hello, world!</h1>; |
1 | const element = ( |
按照管官方的话说:既不是字符串也不是 HTML
- React 认为渲染逻辑本质上与其他 UI 逻辑内在耦合
- 在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用
- eact DOM 在渲染所有输入内容之前,默认会进行转义, 可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击,防止注入攻击
1. 在 JSX 中, 可以在大括号内放置任何有效的 JavaScript 表达式
1
2
3
4
5
6
const name = 'lxl';
const element = (
<h1>
Hello, {name}!
</h1>
);
2. JSX 特定属性
使用引号,来将属性值指定为字符串字面量
1
const element = <div tabIndex="0"></div>;
使用大括号,来在属性值中插入一个 JavaScript 表达式
1
const element = <img src={user.avatarUrl}></img>;
注意:
- 在属性中嵌入 JavaScript 表达式时,不要在大括号外面加上引号。你应该仅使用引号(对于字符串值)或大括号(对于表达式)中的一个,对于同一属性不能同时使用这两种符号
- 因为 JSX 语法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用 camelCase(小驼峰命名)来定义属性的名称,而不使用 HTML 属性名称的命名约定,例如:data-id 写为 dataId
- JSX 里的 class 是个关键字,所以使用 className 代替 class。
JSX 原理
简单来说就是对象,和vue的虚拟DOM一样,你写的想模板的jsx代码会转换成类似下边的结构
1 | // 注意:这是简化过的结构 |
Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用
等效的写法
1 | const element = ( |
1 | const element = React.createElement( |
函数组件与 class 组件
函数组件
最先安装好 react 后的 app.js,就是一个简单的函数组件,本质上就是一个函数,同样也能接收参数
1 | function Welcome(props) { |
class 组件
上文说的 jsx 特性里 ‘JSX 里的 class 是个关键字,所以使用 className 代替 class’,是有点误解,事实上 class 是 ES6 的关键词,声明一个 类:
1 | class Welcome extends React.Component { |
State
和vue一样,react也需要存储很多状态,通常是一些页面需要渲染的数据、响应页面元素的变量等,react的状态需要 __state__管理(高阶可以结合redux):
1 | class UserInfo extends React.Component { |
与vue不同,state并没有被组件或者对象双向绑定,无法响应式的直接‘this.state.name = ‘www’’,设置name,这样是错误的,需要调用 setState:
1 | this.setState({ |
this.setState({
date: new Date()
});
数据自顶向下流动
引用一下菜鸟的文字:
- 父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。这就是为什么状态通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问。
- 任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或 UI 只能影响树中下方的组件
生命周期
React v16.3之前组件的生命周期可分成三个状态:
- Mounting:已插入真实 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实 DOM
| 生命周期 | 描述 |
| ------------------------- | ------------------------------------------------------------ |
| componentWillMount | 只会在装载之前调用一次,在 `render` 之前调用,你可以在这个方法里面调用 `setState` 改变状态,并且不会导致额外调用一次 `render` |
| componentDidMount | 只会在装载完成之后调用一次,在 `render` 之后调用,从这里开始可以通过 `ReactDOM.findDOMNode(this)` 获取到组件的 DOM 节点。以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。 |
| componentWillReceiveProps | 更新组件触发,在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。 |
| shouldComponentUpdate | 更新组件触发,返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。<br/>可以在你确认不需要更新组件时使用。 |
| componentWillUpdate | 更新组件触发,在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。 |
| componentDidUpdate | 更新组件触发在组件完成更新后立即调用。在初始化时不会被调用。 |
| componentWillUnmount | 卸载组件触发,在组件从 DOM 中移除之前立刻被调用。 |
React v16.3 之后,引入了两个新的生命周期函数:
- getDerivedStateFromProps
- getSnapshotBeforeUpdate
同时deprecate了一组生命周期API,包括:
- componentWillReceiveProps
- componentWillMount
- componentWillUpdate
看例子,userInfo。
事件处理
事件处理上和 DOM 元素的很相似,不同的是:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。onBlur、onInput …
1
2
3<button onClick={activateLasers}>
Activate Lasers
</button> - 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
1 | function ActionLink() { |
来自官方忠告:
你必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
这并不是 React 特有的行为;这其实与 JavaScript 函数工作原理有关。通常情况下,如果你没有在方法后面添加 (),例如 onClick={this.handleClick},你应该为这个方法绑定 this。
如果觉得使用 bind 很麻烦,这里有两种方式可以解决。
- 如果你正在使用实验性的 public class fields 语法,你可以使用 class fields 正确的绑定回调函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}- 如果你没有使用 class fields 语法,你可以在回调中使用箭头函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
注意:
此语法问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题
条件渲染
可以直接三目运算符:
1
2
3
4
5
6
7
8render() {
const showUser = false;
return (
<div>
{showUser ? <UserInfo userData={userData}/> : <span>啥也没有</span>}
</div>
)
}可以if else:
1 |
|
- 运算符也是可以的
1
2
3
4
5
6
7
8render() {
const showUser = false;
return (
<div>
{showUser && <UserInfo userData={userData}/>}
</div>
)
}
没有做不到,只有想不到
列表渲染
1 | render () { |
状态提升
在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state,这就是所谓的“状态提升”。
查看温度换算例子
包含关系
有些组件无法提前知晓它们子组件的具体内容。在 Sidebar(侧边栏)和 Dialog(对话框)等展现通用容器(box)的组件中特别容易遇到这种情况
查看组合组件弹窗示例
路由
http://react-guide.github.io/react-router-cn/docs/guides/basics/RouteConfiguration.html