codepen 上的代码请 fork 后再修改。
环境基础
- Chrome、FireFox等主流浏览器陆续支持 ES6+ 语法。
- QQ、360、搜狗等浏览器已支持 ES6+ 语法。
- 微软全面转移到 Edge。
- 淘宝不再支持 IE 8 。
- 项目已使用 webpack、babel 等来提供转义支持。
简介
A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES
一个用于搭建用户界面的js库
核心思路:假定按照人机交互活动的不同状态来设计UI,根据状态的迁移来自动渲染页面。同时,react 通过组件化来分治状态。
Hello World
// 引入 js 类库
import React from 'react'
import ReactDOM from 'react-dom'
// 定义 HelloWorld 组件
class HelloWorld extends React.Component {
render() {
// return 中是 JSX 语法
return (
Hello, world!
);
}
}
// 找到 HTML 中的 id="root" 的标签,将 Hello 作为子元素插入
ReactDOM.render( , document.getElementById('root'));
codepen
ReactDOM.render()
在客户端将 react 组件渲染成 HTML 的方法,在一般的 web app 中只在入口 js 文件中写一处。
JSX
一种在 js 中书写 HTML 的简单方式,可以在其中通过 {}
来使用 js 表达式、变量、函数等。需要注意的是,由于 class 是 js 的关键字/保留字,所以 HTML 中的 class
需要写成 className
。简单语法如下。
const generateJSX = (arg) => {
return {arg}
};
class HelloWorld extends React.Component {
constructor(props) {
super(props)
this.state = {
val: {key: 'JSX中使用对象的值'},
ifelse: '与其写那些匿名函数箭头函数,不如提出来写更好维护。'
};
}
render() {
return (
JSX
{this.state.val.key}
{true?'JSX中不能直接使用if-else,可以使用三元表达式':null}
{(function() {
if (true) {
return 在JSX中使用立即执行的匿名函数来写if-else
}
})()}
{true && 单if的时候可以用表达式&&jsx的方式来写
}
{(() => {
if (true)
return 在JSX中使用立即执行的箭头函数来写if-else
})()}
{generateJSX(this.state.ifelse)}
);
}
}
ReactDOM.render( , document.getElementById('root'));
codepen
需要注意的是,顶层只能有一对标签。
// bad
return
// good
return
为什么要使用 JSX
使用 JSX 时。
Click Me
不使用 JSX 时。
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)
就一般的 web 开发来说,显然第一种比较直观;对于不一般的 web 开发者来说,也还是第一种比较直观。
React 组件(Component)
React 支持自定义组件,而组件化,是工程化的基础之一。React 支持自定义组件和传参(props)。将自定义组件类比 HTML 标签,参数就类似标签属性,能在 HTML 中怎么使用标签,就能在 JSX 中怎么使用组件。
定义组件
一般由两种方式:函数和 es 6 class 。
函数方式:
function genComA(props={}) {
return hello
}
const genComB = (props={}) => {
return hello
};
class:
class Welcome extends React.Component {
render() {
return Hello, {this.props.name}
;
}
}
传参
前面已经有过很多示例。
在 React 中,可以传递给组件的参数类型和 js 一致,包括 number、string、boolean、object、function 等。在组件中通过 this.props
获取接收到的参数。
class HiName extends React.Component {
render() {
return Hi, {this.props.name}
}
}
class HelloWorld extends React.Component {
render() {
return (
);
}
}
// 输出 hi, sweetie
以函数形式创建的组件,则没有 this
,也就没有 this.props
,是通过封装成对象的形式传递,直接用 js 函数传参的方式获取即可。注:必须首字母大写,否则失效。
function Bye(props) {
return {props.name}, goodbye.
}
class HelloWorld extends React.Component {
render() {
return (
);
}
}
this.props.children
React 中有个保留的参数,叫 this.props.children
,主要用于封装和动态加载子组件。常见的用法有单页面 web app 中 header 和 footer 固定,内部内容动态变化等。
class App extends Component {
render() {
return (
{this.props.children}
)
}
}
参数校验
React 支持参数校验,包括 js 的数据类型、自定义类型、非空、枚举等。
class Greeting extends React.Component {
render() {
return (
Hello, {this.props.name}
);
}
}
Greeting.propTypes = {
name: React.PropTypes.string
};
具体参考官网。
状态(state)
React 组件基于自身的 state 变化来触发 render ,并进行相应的计算,达到渲染的效果。所以 state 的使用,是 React 中最重要的部分。
- React 组件的生命周期节点为
mount -> (update) -> unmount
。 - 其中
mount
和unmount
在生命周期中只执行一次,update
执行 0 到多次。 -
mount
和update
都会触发render
。 - 对于
mount
和update
都有will
和did
两种处理函数,对于unmount
只有componentWillUnmount
。 - 提供
shouldComponentUpdate
来处理比较复杂的情况下组件 state 变化是否渲染,以提升性能。紧急逃生,慎用。 - 提供
componentWillReceiveProps
用于组件mount
之后接收参数再次更新 state 。
class PropsCount extends React.Component {
componentWillMount() {
console.log('PropsCount will mount')
}
componentDidMount() {
console.log('PropsCount did mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('PropsCount will update')
}
componentDidUpdate(prevProps, provState) {
console.log('PropsCount did update')
}
render() {
console.log('PropsCount render')
return count update by props: {this.props.count}
}
}
class StateCount extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.count};
}
componentWillMount() {
console.log('StateCount will mount')
}
componentDidMount() {
console.log('StateCount did mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('StateCount will update')
}
componentDidUpdate(prevProps, provState) {
console.log('StateCount did update')
}
componentWillReceiveProps(nextProps) {
this.setState({count: nextProps.count});
}
render() {
console.log('StateCount render')
return count update by state: {this.state.count}
}
}
class Init extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.count}
}
componentWillMount() {
console.log('Init will mount')
}
componentDidMount() {
console.log('Init did mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('Init will update')
}
componentDidUpdate(prevProps, provState) {
console.log('Init did update')
}
render() {
console.log('Init render')
return init: {this.state.count}
}
}
class HelloWorld extends React.Component {
addOne = () => {
this.setState({count: this.state.count + 1});
};
constructor(props) {
super(props);
this.state = {count: 0};
}
componentWillMount() {
console.log('HelloWorld will mount')
}
componentDidMount() {
console.log('HelloWorld did mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('HelloWorld will update')
}
componentDidUpdate(prevProps, provState) {
console.log('HelloWorld did update')
}
render() {
console.log('HelloWorld render')
return (
);
}
}
ReactDOM.render( , document.getElementById('root'));
codepen
注意:在 componentWillMount componentWillUpdate
中不要使用 setState
。初始化的异步请求最好放在 componentDidMount
里,其他初始化的同步操作放在 constructor
里。
处理事件和获取值
官网参考。
class A extends React.Component {
constructor (props) {
super(props)
this.state={name:null}
}
onChange = (e) => {
this.setState({name: e.target.value})
};
render () {
return
{this.state.name && }
;
}
}
codepen