React的引入
CDN引入
// react 引入,用来生成伪dom
<script src="https://cdn.bootcss.com/react/16.10.2/umd/react.development.js"></script>
// react-dom引入,用来渲染界面
<script src="https://cdn.bootcss.com/react-dom/16.10.2/umd/react-dom.development.js"></script>
cjs与umd的区别
- cjs全称commonJS,是Node.js支持的模块规范
- umd是统一模块定义,兼容各种模块规范(含浏览器)
- 理论上有限使用umd,同时支持Node.js和浏览器
- 最新的模块规范是使用import和export关键字
通过webpack引入React
- import ... from ...
// 注意大些写
yarn add react react-dom
import React from 'react'
import ReactDOM from 'react-dom'
用create-react-app
create-react-app 项目名字
JSX使用
创建项目
yarn global add create-react-app // 全局安装
create-react-app 项目名字 // 初始化目录
cd 项目名字 // 进入目录
yarn start // 开始开发
使用JSX的注意事项
- 注意className
<div className="red">n</div>
被转义为
React.createElement('div',{className:'red'},"n")
- 插入变量
- 标签里面的所有JS代码都要用{}包起来
- 如果需要变量n,那么就用{}把n包起来
- 如果需要对象,那么就要用{}把对象包起来,比如{{name:'frank'}}
JSX的条件判断
- 在React里
const Component = () => {
return n%2 === 0 ? <div>n是偶数</div> : <span>n是奇数</span>
}
// 如果需要外面的div, 可以写成
const Component = () -> {
return {
<div>
{ n%2 ==== 0 ? <div>n是偶数</div> : <span>n是奇数</span>}
</div>
}
}
- 还可以写成
const Component - () => {
const content = (
<div>
{ { n%2 ==== 0 ? <div>n是偶数</div> : <span>n是奇数</span>} }
</div>
)
return content
}
React里面编写循环
const Component = (props) => {
return props.numbers.map((n,index) => {
retuen <div>下标{index}的值为{n}</div>
})
}
// 如果你需要外面的div,可以写成
const Component - (props) => {
return (
<div>
{ props.numbers.map((n,index) => {
return <div>下标{index}的值为{n}</div>
})}
</div>
)
}
// 还可以这样写
const Component = (props) => {
const array = []
for(let i=0; i<props.numbers.length;i++){
array.push(<div>下标{index}的值为{props.numbers[i]}</div>)
}
return <div>{ array }</div>
}
组件
Element VS Component
- 元素与组件
// 这是一个react元素(d小写)
const div = React.createElement('div', ...)
// 这是一个React组件(D大写)
const Div = () => React.createElement('div',...)
React两种组件
- 函数组件
function Welcome(props){
return <h1>Hello, {props.name} </h1>;
}
// 使用方法: <Welcome name="frank"/>
- 类组件
class Welcome extend React.Component {
render(){
return <h1>Hello, {props.name} </h1>;
}
}
// 使用方法: <Welcome name="frank"/>
Welcome解析
- 会被翻译成什么
// >>> 表示翻译成
<div> >>> React.createElement('div')
<Welcome> >>> React.createElement(Welcome)
- React.createElement的逻辑
- 传入一个字符串'div', 会创建一个div
- 传入一个函数,会调用函数,获取其返回值
- 传入一个类,在类前面加上new,这回执行constructor,获取一个组件对象,然后调用对象的render方法,获取其返回值
props和state
- props(外部数据)
类组件直接读取属性 this.props.xxx 函数组件直接读取 参数.props.xxx
import React from "react";
import ReactDOM from "react-dom";
function App() {
return (
<div className="App">
爸爸
<Son messageForSon="儿子你好" />
</div>
);
}
class Son extends React.Component {
render() {
return (
<div className="Son">
我是儿子,我爸对我说「{this.props.messageForSon}」
<Grandson messageForGrandson="孙贼你好" />
</div>
);
}
}
const Grandson = props => {
return (
<div className="Grandson">
我是孙子,我爸对我说「{props.messageForGrandson}」
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

- state(内部数据)
import React from 'react'
import ReactDOM from 'react-dom'
function App(){
return (
<div className="App">
爸爸
<Son />
</div>
);
}
class Son extend React.Component {
constructor(){
super()
this.state = {
n : 0
};
}
add(){
this.setState({ n: this.state.n+1 });
}
render(){
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={() => this.add()}>+1</button>
<Grandson />
</div>
)
}
}
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

类组件注意事项
-
this.state.n +=1 无效?
- 其实n已经改变了
- 调用setState才会触发UI更新(异步更新)
- 因为React没有像Vue监听data一样监听state
-
setState会异步更新UI
- setState之后,state不会马上改变
- 更推荐的方式是setState(函数)
-
this.setState(this.state) 不推荐?
- React希望我们不要修改旧state(不可变数据)
- 常用代码: setState({ n: state.n+1})
函数组件的注意事项
- 也要通过setX(新值)来更新UI
- 没有this,一律使用参数和变量
复杂state
- 类组件有n/m
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
爸爸
<Son />
</div>
);
}
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0
};
}
addN() {
this.setState({ n: this.state.n + 1 });
// m 会被覆盖为 undefined 吗?
}
addM() {
this.setState({ m: this.state.m + 1 });
// n 会被覆盖为 undefined 吗?
}
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={() => this.addN()}>n+1</button>
m: {this.state.m}
<button onClick={() => this.addM()}>m+1</button>
<Grandson />
</div>
);
}
}
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
- 函数组件有n/m
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
爸爸
<Son />
</div>
);
}
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0
};
}
addN() {
this.setState({ n: this.state.n + 1 });
// m 会被覆盖为 undefined 吗?
}
addM() {
this.setState({ m: this.state.m + 1 });
// n 会被覆盖为 undefined 吗?
}
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={() => this.addN()}>n+1</button>
m: {this.state.m}
<button onClick={() => this.addM()}>m+1</button>
<Grandson />
</div>
);
}
}
const Grandson = () => {
const [n, setN] = React.useState(0);
const [m, setM] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>n+1</button>
m:{m}
<button onClick={() => setM(m + 1)}>m+1</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
- 类组件setState会自动合并第一层属性,但是第二层不会,用...解决
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
爸爸
<Son />
</div>
);
}
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0,
user: {
name: "frank",
age: 18
}
};
}
addN() {
this.setState({ n: this.state.n + 1 });
// m 会被覆盖为 undefined 吗?
}
addM() {
this.setState({ m: this.state.m + 1 });
// n 会被覆盖为 undefined 吗?
}
changeUser() {
this.setState({
// m 和 n 不会被置空
user: {
...this.state.user, // 复制之前的所有属性
name: "jack"
// age 被置空
}
});
}
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={() => this.addN()}>n+1</button>
m: {this.state.m}
<button onClick={() => this.addM()}>m+1</button>
<hr />
<div>user.name: {this.state.user.name}</div>
<div>user.age: {this.state.user.age}</div>
<button onClick={() => this.changeUser()}>change user</button>
<Grandson />
</div>
);
}
}
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
事件绑定
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
爸爸
<Son />
</div>
);
}
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0
};
}
addN = () => {
this.setState({ n: this.state.n + 1 });
};
addM() {
// 这样写 this 可能变成 window
this.setState({ m: this.state.m + 1 });
}
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={this.addN}>n+1</button>
m: {this.state.m}
<button onClick={this.addM}>m+1</button>
<Grandson />
</div>
);
}
}
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
class组件详解
类组件创建
import React from 'react'
class B extends React.Component {
constructor(props){
super(props)
}
render(){
return (
<div> hello </div>
)
}
}
export default B;
Props外部数据
- 传入props给B组件
class Parent extends React.Component {
constructor(props){
super(props)
this.state = {name:'frank'}
}
onClick = () => {}
render(){
return <B name = {this.state.name} onClick = this.onClick> hi </B>
}
}
// 外部数据被封装成一个对象,传入B组件
- 读取props外部数据
class B extend React.Component {
constructor(props){
super(props)
}
render(){
return <div onClick = {this.props.onClick}>
{this.props.name}
<div>
{this.props.children}
</div>
</div>
}
}
- 不准写props数据
state内部数据
class B extends React.Component {
constructor(props){
super(props)
this.state - {
user:{name:'frank', age:18}
}
}
render(){....}
}
声明周期
必须会的声明周期
- constructor() 在这里初始化state
- shouldComponentUpdate() return false阻止更新
- render() 创建虚拟dom
- componentDidMount() 组件已经出现在页面
- componentDidUpdate() 组件已经更新
- componentWillUnmount() 组件将死
constructor
-
用途
- 初始化props
- 初始化state,但此时不能调用setState
- 用来写bind this
constructor(){
// 其他代码
this.onClick =this.onClick.bind(this)
}
// 可以用新语法替代
onClick =() => {}
constructor(){ // 其他代码 }
shouldComponentUpdate
- 用途
- 返回true表示不足时UI更新
- 返回false表示阻止UI更新
- 代码
class App extends React.Component {
constructor(props){
super(props)
this.state = {
n: 1
}
}
onClick = () =>{
this.setState(state => ({ n: state.n+1}))
this.setState(state => ({ n: state.n-1}))
}
shouldComponentUpdate(newProps, newState){
if(newState.n === this.state.n){
return false
}else{
return true
}
}
render(){
...
}
}
render
- 用途
- 展示视图
return (<div>...</div>)
- 只能有一个根元素
- 如果有两个根元素,就要React.Fragment包起来
componentDidMount
- 用途
- 在元素插入页面后执行代码,这些代码以来DOM
- 比如你像获取div的高度,最好写进这里
- 此处可以发起加载数据的AJAX请求
- 首次渲染会执行此钩子
componentDidUpdate
- 用途
- 在视图更新后执行代码
- 此处也可以发起AJAX请求,用于更新文档
- 首次渲染不会执行此钩子
- 在此处setState可能会引起无限循环,除非放在if里面
- 若shouldComponentUpdate返回false,则不会出发此钩子
componentWillUnmount
- 用途
- 组将将要被移除页面然后被销毁时执行代码
- unmount过的组件不会再次mount
- 举例
- 如果在componentDidMount里面监听了window scroll,那么就要在componentWillUnmount里面取消监听
函数组件
创建方式
const Hello = () => {
return <div> {props.message}</div>
}
const Hello = props => <div>{props.message}</div>
function Hello(props){
return <div>{props.message}</div>
}
useEffect
- 模拟componentDidMount
useEffect(() => { console.log('第一次渲染')},[])
- 模拟componentDidUpdate
useEffect(() => { console.log('任意属性变更')})
useEffect(() => { console.log('n变更了')},[n])
- 模拟componentWillUnmount
useEffect(
console.log('第一次渲染')
return () => {
console.log('组件要卸载了')
}
)
useState原理
用法
function App(){
const [n, setN] = React.useState(0);
return (
<div className="App">
<p>[n]]</p>
<p>
<button onClick = {() => setN(n+1)}> +1 </button>
</p>
</div>
)
}
ReactDOM.render(<App/>, rootElement)