1-列表渲染
const songs = [
{ id: 1, name: "111" },
{ id: 2, name: "222" },
{ id: 3, name: "333" },
];
function App() {
return (
<div className="App">
<ul>
{songs.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
export default App;
2-条件渲染
const flag = true;
function App() {
return <div className="App">{flag ? <span>app</span> : null}</div>;
}
export default App;
3-精简条件渲染
const getHtag = (type) => {
if (type === 1) {
return <h1>this is h1</h1>;
}
if (type === 2) {
return <h2>this is h2</h2>;
}
if (type === 3) {
return <h3>this is h3</h3>;
}
};
function App() {
return <div className="App">{getHtag(2)}</div>;
}
export default App;
4-样式控制
与vue的class不同,react为className
文件内样式以自定义对象,用"style={自定义对象}"的形式写在标签内
import "./app.css";
const style = {
color: "red",
fontSize: "30px",
};
function App() {
return (
<div className="App">
<span style={style}>span</span>
<span className="active">span</span>
</div>
);
}
export default App;
5-动态类名
与vue相似,采用三元表达式完成
import "./app.css";
const style = {
color: "red",
fontSize: "30px",
};
let astive = true;
function App() {
return (
<div className="App">
<span style={style}>span</span>
<span className={astive ? "active" : ""}>span</span>
</div>
);
}
export default App;
6-事件绑定
需要添加事件的元素标签内以"onClick={自定义事件}"的形式实现事件绑定
import React from "react";
// 函数组件的创建和渲染
// 创建
function Hello() {
const clickHandler = () => {
console.log("Hello点击事件");
};
return <div onClick={clickHandler}>Hello</div>;
}
// 类组件的创建和渲染
// 创建
class HelloComponent extends React.Component {
// 事件回调函数(标准写法)
clickHandler = () => {
console.log("class Hello React.Component点击事件");
};
render() {
return <div onClick={this.clickHandler}>class Hello React.Component</div>;
}
}
function App() {
return (
<div className="App">
{/* 渲染Hello组件 */}
<Hello></Hello>
<HelloComponent></HelloComponent>
</div>
);
}
export default App;
7-事件传递参数
与vue相同在函数添加形参,调用函数时传入实参,事件e可打印事件对象
import React from "react";
// 函数组件的创建和渲染
// 创建
function Hello() {
const clickHandler = (e, msg) => {
console.log("Hello点击事件", msg, e);
};
return <div onClick={(e) => clickHandler(e, "Hello")}>Hello</div>;
}
// 类组件的创建和渲染
// 创建
class HelloComponent extends React.Component {
// 事件回调函数(标准写法)
clickHandler = (e, msg) => {
console.log("class Hello React.Component点击事件", e, msg);
};
render() {
return (
<div onClick={(e) => this.clickHandler(e, "666")}>
class Hello React.Component
</div>
);
}
}
function App() {
return (
<div className="App">
{/* 渲染Hello组件 */}
<Hello></Hello>
<HelloComponent></HelloComponent>
</div>
);
}
export default App;
8-状态定义修改
react中的状态类似与vue2中的静态数据,vue2中数据放在data函数中return出来
react中的状态需要方在state={}对象内,修改时需要用固定的方法setState方法修改状态值
// 组件状态 类组件作为演示
import React from "react";
class TestComponent extends React.Component {
// 1.定义组件状态
state = {
// 定义各种属性,全都是当前组件状态
name: "组件状态",
};
// 事件回调
changeName = () => {
// 3.修改state中的状态name
// 注:不可以直接做赋值修改 必须通过一个方法
this.setState({
name: "2222",
});
};
render() {
// 2.使用状态
return (
<>
<div>666 当前name为:{this.state.name}</div>
<button onClick={this.changeName}>修改</button>
</>
);
}
}
function App() {
return (
<div className="App">
<TestComponent></TestComponent>
</div>
);
}
export default App;
9-this指向问题bind修正写法
react中的事件函数一般写为箭头函数,若写为普通函数时在事件调用时打印this就为undefined
遇到这种情况需要使用bind方法修正this指向问题
此种写法在实际项目中一般不使用
// this 有问题写法
import React from "react";
// this有问题的写法
class Test extends React.Component {
constructor() {
super();
// 使用bind强行修正我们的this指向
this.handler = this.handler.bind(this);
}
handler() {
console.log(this);
}
render() {
return <button onClick={this.handler}>点击</button>;
}
}
// 根组件
function App() {
return (
<div className="App">
<Test></Test>
</div>
);
}
export default App;
10-this指向问题的另一种修改
与上述this问题相同,此修正方式是将事件调用中的函数写法改为箭头函数方式
// this问题
import React from "react";
// this有问题的写法
class Test extends React.Component {
handler() {
console.log(this);
}
render() {
// reander 函数中的this已经被react内部做了修正
// 这里的this就是指向当前的组件实例对象
// 那我们箭头函数中的this 直接沿用 所以也是指向组件的实例对象
console.log("父函数中的this指向为:", this);
return (
// 如果不通过construcor做修正 直接可以在事件绑定的位置
// 通过箭头函数的写法 直接沿用父函数中的this指向也可以
<button onClick={() => this.handler()}>点击</button>
);
}
}
// 根组件
function App() {
return (
<div className="App">
<Test></Test>
</div>
);
}
export default App;
11-react的状态不可变
修改state中的对象时需采用下面的方式
// react的状态不可变
import React from "react";
class Counter extends React.Component {
// 定义组件状态
state = {
count: 0,
list: [1, 2, 3],
person: {
name: "jack",
age: 18,
},
};
// react不能直接修改状态 setState
// 数组修改
arrayHandler = () => {
// this.setState({
// list: [...this.state.list, 4, 5],
// });
// 删除数组 -filter
this.setState({
list: this.state.list.filter((item) => item !== 2),
});
};
// 对象修改
objectHandler = () => {
this.setState({
person: {
...this.state.person,
name: "tome",
},
});
};
render() {
return (
<>
<ul>
{this.state.list.map((item) => {
return <li key={item}>{item}</li>;
})}
</ul>
<button onClick={this.arrayHandler}>数组修改</button>
<div>{this.state.person.name}</div>
<button onClick={this.objectHandler}>对象修改</button>
</>
);
}
}
// 根组件
function App() {
return (
<div className="App">
<Counter></Counter>
</div>
);
}
export default App;
12-表单组件-受控组件(推荐)
react表单处理中分为受控组件与非受控组件
例如input框中,受控表示iuput框的状态被react组件状态控制(可以理解为state中的状态
)
使用步骤
- 在组件的state中声明一个组件的状态数据
- 将状态数据设置为input标签元素的value属性的值
- 为input添加change事件,在事件处理程序中,通过事件对象e获取到当前文本框的值(
即用户当前输入的值
) - 调用setState方法,将文本框的值作为state状态的最新值
类似于vue中的双向绑定(v-model)
// 表单处理
// 2.非受控组件
import React from "react";
class Input extends React.Component {
state = {
message: "this is message",
};
inputChange = (e) => {
console.log("change", e);
this.setState({
message: e.target.value,
});
};
// 产出ui模板结构
render() {
return (
<>
<input
type="text"
onChange={this.inputChange}
value={this.state.message}
/>
</>
);
}
}
// 根组件
function App() {
return (
<div className="App">
<Input></Input>
</div>
);
}
export default App;
13-表单组件-非受控组件
非受控组件就是通过手动操作dom的方式获取文本框的值,文本框的状态不受react组件的state中的状态控制,直接通过原生dom获取输入框的值
使用步骤
- 导入
createRef
函数 - 调用createRef函数,创建一个ref对象,存储到名为
msgRef
的实例属性中 - 为input添加ref属性,值为
msgRef
- 在按钮的事件处理程序中,通过
msgRef.current
即可拿到input对应的dom元素,而其中msgRef.current.value
拿到的就是文本框的值
// 表单处理
// 2.非受控组件
import React, { createRef } from "react";
class Input extends React.Component {
// 这个实例属性是可以自定义的 语义化
msgRef = createRef();
getValue = () => {
// 通过msgRef获取input value值
console.log(this.msgRef.current.value);
};
// 产出ui模板结构
render() {
return (
<>
<input type="text" ref={this.msgRef} />
<button onClick={this.getValue}>点击获取输入框的值</button>
</>
);
}
}
// 根组件
function App() {
return (
<div className="App">
<Input></Input>
</div>
);
}
export default App;
14-组件通信
组件是独立且封闭的单元,默认情况下组件只能使用自己的数据(state)
组件化开发的过程中,完整的功能会拆分多个组件,在这个过程中不可避免的需要互相传递一些数据
为了能让各组件之间可以进行互相沟通,数据传递,这个过程就是组件通信
- 父子关系 - 最重要的
- 兄弟关系 - 自定义事件模式产生技术方法 eventBus / 通过共同的父组件通信
- 其它关系 - mobx / redux / zustand
父传子
实现步骤
-
父组件提供要传递的数据 -
state
-
给子组件标签
添加属性
值为 state中的数据 -
子组件中通过
props
接收父组件中传过来的数据 -
- 类组件使用this.props获取props对象
- 函数式组件直接通过参数获取props对象
-
props说明
-
1. props是只读对象(readonly)
-
根据单项数据流的要求,子组件只能读取props中的数据,不能进行修改
-
2. props可以传递任意数据
-
数字、字符串、布尔值、数组、对象、
函数、JSX
// 组件通信
// 父传子
// 父组件
import React from "react";
// 函数式的son
// function SonF(props) {
function SonF({ msg, list, userIfor, getMsg, child }) {
// props是一个对象 里面存着通过父组件传入的所有数据
// 可以解构赋值
// const { msg, list, userIfor, getMsg, child } = props;
return (
<>
<div>函数子组件,{msg}</div>
<div>
函数子组件数组,
{list.map((item) => (
<p key={item}>{item}</p>
))}
</div>
<div>{userIfor.name}</div>
<button onClick={getMsg}>触发父组件传入的函数</button>
<div>{child}</div>
</>
);
// props也是一个普通的js对象,所以原生支持的写法 这里都是可以的
}
// 类组件的Son
class SonC extends React.Component {
// 产出ui模板结构
render() {
return (
// 类组件必须通过this关键词 去获取props
<>
<div>类子组件,{this.props.msg}</div>
</>
);
}
}
class App extends React.Component {
// 准备数据
state = {
message: "message",
list: [1, 2, 3],
userIfor: {
name: "name",
age: 12,
},
};
// 传递函数
getMsg = (i) => {
console.log("父组件函数", i);
};
render() {
return (
<div>
{/* 子组件身上绑定属性 属性名可以自定义 */}
<SonF
userIfor={this.state.userIfor}
list={this.state.list}
msg={this.state.message}
getMsg={() => this.getMsg(1)}
child={<button>this is child</button>}
></SonF>
<SonC msg={this.state.message}></SonC>
</div>
);
}
}
export default App;
子传父
父组件给子组件传递回调函数,子组件调用
实现步骤
- 父组件提供一个回调函数 - 用于接收数据
- 将函数作为属性的值,传给子组件
- 子组件通过props调用 回调函数
- 将子组件中的数据作为参数传递给回调函数
// 组件通信
// 子传父
import React from "react";
// 函数式的son
// function SonF(props) {
// const { getSonMsg } = props;
// return (
// <>
// <div>函数子组件</div>
// <button
// onClick={() => {
// getSonMsg("子组件数据");
// }}
// >
// 子传父
// </button>
// </>
// );
// }
// 另一种写法
function SonF(props) {
const { getSonMsg } = props;
function sonClick() {
getSonMsg("子组件数据");
}
return (
<>
<div>函数子组件</div>
<button onClick={sonClick}>子传父</button>
</>
);
}
class App extends React.Component {
// 准备数据
state = {
list: [1, 2, 3],
};
// 准备一个函数 传给子组件
getSonMsg = (i) => {
console.log(i);
};
render() {
return (
<div>
{/* 子组件身上绑定属性 属性名可以自定义 */}
<SonF getSonMsg={this.getSonMsg}></SonF>
</div>
);
}
}
export default App;
兄弟传值
通过状态提升机制,利用共同的父组件实现兄弟通信
// 组件通信
// 兄弟传值
// 实现方式
// 1.先将B中的数据通过子传父 传给App
// 2.再把App接收到的Son中的数据 通过父传子 传给A
import React from "react";
// sonA
function SonA(props) {
return (
<>
<div>函数子组件A{props.sendAMsg}</div>
</>
);
}
// sonB
function SonB(props) {
const bMsg = "这里来自于b组件中的数据";
return (
<>
<div>
函数子组件B
<button onClick={() => props.getBMsg(bMsg)}>B子组件</button>
</div>
</>
);
}
class App extends React.Component {
state = {
sendAMsg: "测试一下父传子",
};
// 声明一个传给B组件的方法
getBMsg = (msg) => {
console.log(msg);
// 把msg交给子组件A
this.setState({
sendAMsg: msg,
});
};
render() {
return (
<div>
{/* 子组件身上绑定属性 属性名可以自定义 */}
<SonA sendAMsg={this.state.sendAMsg}></SonA>
<SonB getBMsg={this.getBMsg}></SonB>
</div>
);
}
}
export default App;
跨组件传值
实现步骤
1- 创建Context对象 导出 Provider 和 Consumer对象
2- 使用Provider包裹上层组件提供数据
3- 需要用到数据的组件使用Consumer包裹获取数据
import React, { createContext } from "react";
// 组件通信
// 跨组件传值
// app ->A->c
// app数据 ->c
// 注:
// 1.上层组件和下层组件关系是相对的只要存在就可以使用 通常我们都会通过app作为数据提供方
// 2.这里涉及到的语法都是固定的
// 1.导入createContext方法并执行,结构提供者和消费者
const { Provider, Consumer } = createContext();
function ComA(props) {
return (
<>
<div>ComA</div>
<ComC></ComC>
</>
);
}
// function ComC(props) {
// return (
// <>
// <div>
// ComC
// {/* 3.通过Consumer使用数据 */}
// <Consumer>{(value) => <span> {value} </span>}</Consumer>
// </div>
// </>
// );
// }
// 另一种方式
function ComC(props) {
return (
<>
<Consumer>
{(value) => (
<div>
ComC
{/* 3.通过Consumer使用数据 */}
<span> {value} </span>
</div>
)}
</Consumer>
</>
);
}
class App extends React.Component {
state = {
msg: "msg",
};
render() {
return (
//2. 使用provider包裹根组件
<>
<Provider value={this.state.msg}>
<div>
{/* 子组件身上绑定属性 属性名可以自定义 */}
<ComA></ComA>
</div>
</Provider>
</>
);
}
}
export default App;
15-children属性
表示该组件的子节点,只要组件内部有子节点,props中就有该属性
children类型 普通文本、普通标签元素、函数/对象、jsx
/* eslint-disable array-callback-return */
import React from "react";
function ListItem({ children }) {
// children();
return (
<>
<div>
ListItem
{/* <span>{children}</span> */}
{children}
</div>
</>
);
}
class App extends React.Component {
render() {
return (
<>
<ListItem>
{/* <div>哈哈哈哈</div>
123 */}
{/* {() => console.log(123)} */}
{
<div>
<p>{"this is p"}</p>
</div>
}
</ListItem>
</>
);
}
}
export default App;
16-props类型校验基础使用
实现步骤
- 安装属性校验包:
yarn add prop-types
- 导入
prop-types
包 - 使用
组件名.propTypes = {}
给组件添加校验规则
/* eslint-disable array-callback-return */
import React from "react";
import PropTypes from "prop-types";
function ListItem({ list }) {
return (
<>
<div>
{list.map((item) => (
<p key={item}>{item}</p>
))}
</div>
</>
);
}
// eslint-disable-next-line react/no-typos
ListItem.propTypes = {
// 定义各种规则
list: PropTypes.array,
};
class App extends React.Component {
render() {
return (
<>
<ListItem list={[1, 2]}></ListItem>
</>
);
}
}
export default App;
17-props默认值
两种方式
函数式组件可在外部写好默认值,也可以在props中直接定义
类组件需要在组件内通过 static defaultProps={}
定义默认值
import React from "react";
import PropTypes from "prop-types";
// function ListItem(props) {
// return (
// <>
// <div>
// ListItem
// {props.pageSize}
// </div>
// </>
// );
// }
// 第二种方式
// function ListItem({ pageSize = 10 }) {
// return (
// <>
// <div>
// ListItem
// {pageSize}
// </div>
// </>
// );
// }
// 类组件写法
class ListItem extends React.Component {
static defaultProps = {
pageSize: 10,
};
render() {
return (
<>
<ListItem list={[1, 2]} pageSize={20}></ListItem>
</>
);
}
}
ListItem.propTypes = {
// 定义各种规则
list: PropTypes.array.isRequired,
};
// 设置默认值
ListItem.defaultProps = {
pageSize: 10,
};
class App extends React.Component {
render() {
return (
<>
<ListItem
list={[1, 2]}
// pageSize={20}
></ListItem>
</>
);
}
}
export default App;
18-生命周期
钩子 函数 | 触发时机 | 作用 |
---|---|---|
constructor | 创建组件时,最先执行,初始化的时候只执行一次 | 1. 初始化state 2. 创建 Ref 3. 使用 bind 解决 this 指向问题等 |
render | 每次组件渲染都会触发 | 渲染UI(注意: 不能在里面调用setState() ) |
componentDidMount | 组件挂载(完成DOM渲染)后执行,初始化的时候执行一次 | 1. 发送网络请求 2.DOM操作 |
componentWillUnmount | 组件卸载(从页面中消失) | 执行清理工作(比如:清理定时器等) |
19-hooks
hooks的本质:一套能够使函数更强大、更灵活的'钩子'
hooks是为了让函数有自己的状态
useState 每次定义都包含一个数据和修改数据的方法
// useState
// 1.导入useState函数 react
// 2.执行这个函数并且传入初始值
// 3.数据,修改数据的方法
// 4.使用数据 修改数据
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<>
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
</>
);
}
export default App;
状态读取修改
1、useSrate传过来的参数 作为count的初始值
2、[count,setCount] 这里的写法是一个解构赋值 useState返回值是一个数组
名字可以自定义 顺序不可以更换
3、setcount函数 作用用来修改count依旧保持不能直接修改原值还是生成一个新值替换原值
4、count, setCount 是一对 setCount只能用来修改对应的count值
hooks的副作用
useEffect函数
副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)
常见的副作用
- 数据请求 ajax发送
- 手动修改dom
- localstorage操作
useEffect函数的作用就是为react函数组件提供副作用处理的!
基础使用
import { useEffect, useState } from 'react'
function App() {
const [count, setCount] = useState(0)
useEffect(()=>{
// dom操作
document.title = `当前已点击了${count}次`
})
return (
<button onClick={() => { setCount(count + 1) }}>{count}</button>
)
}
export default App
依赖项控制执行时机
1- 不添加依赖项
组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行
- 组件初始渲染
- 组件更新 (不管是哪个状态引起的更新)
useEffect(()=>{
console.log('副作用执行了')
})
2- 添加空数组
组件只在首次渲染时执行一次
useEffect(()=>{
console.log('副作用执行了')
},[])
3- 添加特定依赖项
副作用函数在首次渲染时执行,在依赖项发生变化时重新执行
function App() {
const [count, setCount] = useState(0)
const [name, setName] = useState('zs')
useEffect(() => {
console.log('副作用执行了')
}, [count])
return (
<>
<button onClick={() => { setCount(count + 1) }}>{count}</button>
<button onClick={() => { setName('cp') }}>{name}</button>
</>
)
}
4- 清理副作用
如果想要清理副作用 可以在副作用函数中的末尾return一个新的函数,在新的函数中编写清理副作用的逻辑
注意执行时机为:
- 组件卸载时自动执行
- 组件更新时,下一个useEffect副作用函数执行之前自动执行
useEffect - 发送网络请求
语法要求
不可以直接在useEffect的回调函数外层直接包裹 await ,因为异步会导致清理函数无法立即返回
错误写法
useEffect(async ()=>{
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
console.log(res)
},[])
正确写法
在内部单独定义一个函数,然后把这个函数包装成同步
useEffect(()=>{
async function fetchData(){
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
console.log(res)
}
},[])
20-useRef
在函数组件中获取真实的dom元素对象或者是组件对象
使用步骤
- 导入
useRef
函数 - 执行
useRef
函数并传入null,返回值为一个对象 内部有一个current属性存放拿到的dom对象(组件实例) - 通过ref 绑定 要获取的元素或者组件
import React, { useEffect, useRef } from "react";
// 组件实例
// dom对象
class TestC extends React.Component {
state = {
name: "nihao",
};
getName = () => {
return "this is child test";
};
render() {
return (
<>
<div>我是类组件</div>;
</>
);
}
}
function App() {
const testRef = useRef(null);
const h1Ref = useRef(null);
useEffect(() => {
console.log(testRef.current);
console.log(h1Ref.current);
// useEffect 在dom渲染之后执行
}, []);
return (
<>
<div>
<TestC ref={testRef}></TestC>
<h1 ref={h1Ref}>this is h1</h1>
</div>
</>
);
}
export default App;
35-useContext使用
useContext类似于vue3中的 previde
与 inject
数据的提供与注入
实现步骤
- 使用
createContext
创建Context对象 - 在顶层组件通过
Provider
提供数据 需要用<Context.Provider></Context.Provider>
标签包裹被提供数据的组件 - 在底层组件通过
useContext
函数获取数据
import React, { createContext, useContext, useState } from "react";
const Context = createContext();
function ComA() {
const count = useContext(Context);
return (
<>
<div>
this is ComA
<br />
app传过来的数据为:{count}
<ComC></ComC>
</div>
</>
);
}
function ComC() {
const count = useContext(Context);
return (
<>
<div>
this is ComC
<br />
app传过来的数据为:{count}
</div>
</>
);
}
function App() {
const [count, setCount] = useState(20);
return (
<>
<Context.Provider value={count}>
<div>
<ComA></ComA>
<button
onClick={() => {
setCount(count + 1);
}}
>
add+
</button>
</div>
</Context.Provider>
</>
);
}
export default App;
mobx
mobx与react的和关系相当于vue和vueX (同类工具还有 redux、dva、recoil)
计数器案例
// 安装
npm i mobx mobx-react-lite
store 模块化
// src/store/counter.Store.js
// 编写mobox store 小案例
import { makeAutoObservable } from "mobx";
class CounterStore {
// 1、定义数据状态
count = 0;
// 1--定义一个原始数据 list
list = [1, 2, 3, 4, 5, 6];
constructor() {
// 2、数据响应式处理
makeAutoObservable(this);
}
// 2---定义计算属性 computed
get filterList() {
return this.list.filter((item) => item > 2);
}
// 3---方法修改list
addList = () => {
this.list.push(7, 8, 9);
};
// 3、定义action函数(修改数据)
addCount = () => {
this.count++;
};
}
// 4、实例化并导出实例
// const couterStore = new CounterStore();
export { CounterStore };
store index
// 组合子模块
// 封装统一导出的供业务使用的方法
import { ListStore } from "./list.Store";
import React from "react";
// 1.声明一个rootStore
class RootStore {
constructor() {
// 对子模块进行实例化操作
// 实例化根store的时候
// 根store有两个属性 分别是counterStore和listStore
// 各自对应的值 就是我们导入的子模块实例对象
this.couterStore = new CounterStore();
}
}
// 实例化操作
const rootStore = new RootStore();
// 使用react conntext机制 完成统一方法封装
// Provider value={传递的数据}
// 查找机制:useContext 优先从Provider value找 如果找不到 就会找createContext方法传递过来的默认参数
const context = React.createContext(rootStore);
const useStore = () => React.useContext(context);
export { useStore };
连接react
// src/App.js
// 1、导入countStore
import { useStore } from "./store/index";
// 2、导入中间件连接mobox react 完成响应式变化
import { observer } from "mobx-react-lite";
function App() {
const rootStore = useStore();
// console.log(rootStore);
return (
<div className="App">
App
{/* 把store中的count渲染一下 */}
{rootStore.couterStore.count}
{/* 点击事件触发action函数修改count值 */}
<button onClick={rootStore.couterStore.addCount}>++</button>
<br />
{rootStore.couterStore.filterList}
<button onClick={rootStore.couterStore.addList}>修改数组</button>
{/* */}
</div>
);
}
// 3.包裹组件让视图响应数据变化
export default observer(App);