MV*&组件化开发react专题
react介绍与react代码规范
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OG8yz88q-1571282357731)(en-resource://database/975:1)]
配置了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UfULnTlj-1571282357732)(en-resource://database/977:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v6Fv8V4c-1571282357732)(en-resource://database/979:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xW9a6q2T-1571282357733)(en-resource://database/981:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aIw6DDkS-1571282357733)(en-resource://database/985:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LzkxfxX2-1571282357734)(en-resource://database/987:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hLLpSIM3-1571282357734)(en-resource://database/989:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5pLsdRPL-1571282357735)(en-resource://database/991:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lTMVu6vK-1571282357735)(en-resource://database/993:1)]
虚拟DOM和jsx
react组件和变量有本质区别
组件是有生命周期的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vVtBQARb-1571282357736)(en-resource://database/995:1)]
第一个和第二个是同一个类的两个实例化对象,是两个不同的组件
下面两个list是同一个变量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KidI21YB-1571282357737)(en-resource://database/997:1)]
是es6组件的简写方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RyruJiVv-1571282357737)(en-resource://database/999:1)]
react虚拟dom不能渲染对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-viddoNnO-1571282357738)(en-resource://database/1001:1)]
原理:
- 把jsx编译成js,把过去构建DOM树的浏览器的工作交给js
- 把结构数据传入react render方法,将js的数据结构渲染出来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-594mrMVZ-1571282357738)(en-resource://database/1007:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DyadW7aL-1571282357739)(en-resource://database/1009:1)]
header组件会被卸载content组件会被挂载
class List extends React.Component {
constructor (props) {
super(props)
this.state = {
data: ['react', 'vue', 'angular']//=>['vue','angular'] 当组件没有key属性时,渲染会一一对应,react和vue会update,angular会被删掉
}
}
render () {
return (
<div>
{this.state.data.map(item => <Item value={item} key={item}/>)}
</div>
)
}
componentDidMount () {
window.list = this
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sAboEhih-1571282357739)(en-resource://database/1011:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTH0OvZb-1571282357739)(en-resource://database/1013:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RXftc8Hv-1571282357740)(en-resource://database/1015:1)]
生命周期与数据操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4lY5qDI1-1571282357740)(en-resource://database/1034:1)]
Component这个类实现了生命周期的一系列方法
react只有数据和生命周期两个概念
class Component {
setState(){}
readonly props
componentDidUpdate(prevProps,prevState){
}
componentDidMount(){
}
...
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrH6vGCl-1571282357741)(en-resource://database/1036:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HboBqo0v-1571282357741)(en-resource://database/1038:1)]
一个父组件只有在他的子组件全都挂载完成以后才会执行自己的componentDidMount
生命周期函数。
父组件会首先渲染,执行render函数,然后依次渲染他下面的子组件
import React,{Component} from 'react';
class App extends Component{
constructor(props){
super(props)
this.state = {
name: 'react'
}
}
render() {
console.log('App rerender')
return (
<div>
{this.state.name}
{this.state.name && <Son1 name={this.state.name + '-son'}/>}
</div>
);
}
componentDidMount() {
window.app = this
console.log('App mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('App will update')
}
componentDidUpdate(prevProps, prevState) {
console.log('App did update')
}
}
class Son1 extends Component{
render() {
console.log('son1 rerender')
return (
<div>
{this.props.name}
<GrandSon1 name={this.props.name + '-grand'}/>
</div>
);
}
componentDidMount() {
console.log('Son1 mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('son1 will update')
}
componentDidUpdate(prevProps, prevState) {
console.log('son1 did update')
}
componentWillUnmount() {
console.log('Son1 unmout')
}
}
class GrandSon1 extends Component{
render() {
console.log('GrandSon1 rerender')
return (
<div>
{this.props.name}
</div>
);
}
componentDidMount() {
console.log('GrandSon1 mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('GrandSon1 will update')
}
componentDidUpdate(prevProps, prevState) {
console.log('GrandSon1 did update')
}
componentWillUnmount() {
console.log('GrandSOn1 unmout')
}
}
export default App
当父组件改变数据时,父组件先update和render,他的子组件依次更新,完成更新是最后代的组件先完成更新
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cF03bWJE-1571282357742)(en-resource://database/1040:1)]
销毁组件的时候是从上往下销毁,先销毁子组件,后销毁孙组件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4oWrIJHl-1571282357742)(en-resource://database/1042:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NNv8bKS3-1571282357743)(en-resource://database/1044:1)]
react上下文 组件在嵌套层次比较深的时候,数据从最外层组件传到最内层组件
import React from 'react';
import PropTypes from 'prop-types'
class App extends React.Component {
getChildContext() {
return {color: "purple"};
}
constructor(props){
super(props)
this.state = {
name: 'son'
}
}
render() {
console.log('App rerender')
return (
<div>
{this.state.name && <Son1 name={this.state.name}/>}
</div>
);
}
componentDidUpdate(prevProps, prevState) {
console.log('App did update')
}
componentWillUpdate(prevProps, prevState) {
console.log('App will update')
}
componentDidMount() {
console.log('app mount')
window.app = this
}
}
App.childContextTypes = {
color: PropTypes.string
}
class Son1 extends React.Component{
render() {
console.log('Son1 rerender')
return (
<div>
{this.props.name && <GrandSon1 name={this.props.name + '-grand'}/>}
</div>
);
}
componentWillUpdate(prevProps, prevState) {
console.log('son will update')
}
componentDidUpdate(prevProps, prevState) {
console.log('son did update')
}
componentDidMount() {
console.log('son mount')
}
componentWillUnmount() {
console.log('son unmount')
}
}
class GrandSon1 extends React.Component{
render() {
console.log('GrandSon1 rerender')
return (
<div>
{this.props.name} - {this.context.color}
</div>
);
}
componentWillUpdate(prevProps, prevState) {
console.log('son will update')
}
componentDidUpdate(prevProps, prevState) {
console.log('GrandSon1 did update')
}
componentDidMount() {
console.log('grandson mount')
}
componentWillUnmount() {
console.log('GrandSon1 unmount')
}
}
GrandSon1.contextTypes = {
color: PropTypes.string
};
export default App;
组件通信
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LIFtqAg5-1571282357744)(en-resource://database/1046:1)]
父组件到子组件的通信是父组件拿到子组件的实例,然后调用子组件的方法,该操作不是react的风格
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PppK45Bf-1571282357744)(en-resource://database/1048:1)]
import React,{Component} from 'react';
import './App.css';
class List extends Component{
constructor(props) {
super(props);
this.state = {
data: [
{name: 'a',id:0},
{name: 'b',id:1},
{name: 'c',id:2}
],
}
}
render() {
return (
<div>
{this.state.data.map(item => <p key={item.id}>{item.name}</p>)}
</div>
);
}
clear() {
this.setState({
data: []
})
}
}
class App extends React.Component{
render() {
return (
<div>
<List ref='list'/>
</div>
);
}
componentDidMount() {
window.app = this
//父组件向子组件的通信 慎用
this.refs.list拿到list组件的实例对象
// this.refs.list.clear()
}
}
export default App;
观察者模式 不推荐
import React,{Component} from 'react';
import './App.css';
class EventComponent extends Component{
cb = {}
on(name,cb) {
this.cb[name] = cb
}
off(name) {
delete this.cb[name]
}
trigger(name,arg) {
this.cb[name](arg)
}
}
class List extends EventComponent{
constructor(props) {
super(props);
this.state = {
data: [
{name: 'a',id:0},
{name: 'b',id:1},
{name: 'c',id:2}
],
}
}
render() {
return (
<div>
{this.state.data.map(item => <p key={item.id}>{item.name}</p>)}
</div>
);
}
clear() {
this.setState({
data: []
})
}
add(name) {
let {
data
} = this.state
data.push({
name,
id:name
})
this.setState({data})
}
}
class Action extends EventComponent{
constructor(props) {
super(props);
this.state = {
value: ''
}
}
render() {
return (
<div>
<input value={this.state.value} onChange={(e) => this.setState({value: e.target.value })}/>
<button onClick={(e) =>{
this.trigger('add', this.state.value)
}}>add</button>
</div>
);
}
}
class App extends EventComponent{
render() {
return (
<div>
<List ref='list' />
<Action ref='action'/>
</div>
);
}
componentDidMount() {
let listInstance = this.refs.list
let actionInstance = this.refs.action
actionInstance.on('add',(name) => {
listInstance.add(name)
})
window.app = this
// this.refs.list.clear()
}
}
export default App;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7PxZVgSl-1571282357745)(en-resource://database/1050:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z9hjhykL-1571282357745)(en-resource://database/1052:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LiZfmI5S-1571282357745)(en-resource://database/1054:1)]
ReactDom与表单
react与dom操作结合
import ReactDOM from "react-dom";
import React from "react";
import './index.css'
class Loading extends React.Component{
render() {
return (
<div className='loading'>
<div className='loading__mask'></div>
<div className='loading__content'>
loading
</div>
</div>
);
}
}
let node = null
const loading = {
show() {
node = document.createElement('div')
document.body.appendChild(node)
//挂载Loading组件
ReactDOM.render(<Loading />, node)
},
hide() {
if(node) {
//卸载Loading组件
ReactDOM.unmountComponentAtNode(node)
document.body.removeChild(node)
}
}
}
export default loading
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d1uI9Z0W-1571282357746)(en-resource://database/1056:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-orOWrRGE-1571282357746)(en-resource://database/1058:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HJfl6kKg-1571282357747)(en-resource://database/1060:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ajtJ7tiq-1571282357747)(en-resource://database/1062:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aC9RQfjC-1571282357747)(en-resource://database/1064:1)]
react与es6
- 变量的赋值与解构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ef0CWb6-1571282357748)(en-resource://database/1066:1)]
数组的解构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ael5WybB-1571282357748)(en-resource://database/1068:1)]
对象的解构和扩展
import React from 'react';
import logo from './logo.svg';
import './App.css';
class App extends React.Component{
constructor(props){
super(props)
this.state = {
name: 'react & es6',
lesson: '6',
teacher:{
teacherName:'ryan',
age:30
},
skills:['vue','react','nodejs','webpack'],
friend:{
name:'aa'
}
}
}
render() {
let {
name,
lesson,
teacher:{
teacherName,
age
},
skills:[vue1, react1,nodejs1,webpack1],
friend:{
name: friendName
}
} = this.state
return (
<div>
<p>{name} - {lesson}</p>
<p>
{teacherName} - {age}
</p>
<p>
{vue1} - {react1} - {nodejs1} - {webpack1}
</p>
<p>{friendName}</p>
App
</div>
);
}
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom'
import inp from './input'
class User extends React.Component{
render() {
const {
name,
age,
lesson,
...rest
} = this.props
return (
<div {...rest}>
{name} - {age} - {lesson}
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name:'ryan',
age:30,
lesson:'react'
}
}
render() {
let {
name,
age,
lesson
} = this.state
return (
<div>
<User
name={name}
age={age}
lesson={lesson}
style={{color:'red'}}
onClick={() =>{
alert('hello')
}}
onMouseOver={() => {
}}
/>
</div>
)
}
}
export default App;
- 类的继承
import React from 'react';
import ReactDOM from 'react-dom'
import inp from './input'
class LogComponent extends React.Component{
componentWillUnmount() {
console.log('app','unmount')
}
}
class App extends LogComponent{
render() {
return (
<div>
app
</div>
);
}
}
export default App;
实现loading继承
import React from 'react';
import ReactDOM from 'react-dom'
import inp from './input'
class LoadingComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
loading: false
}
}
render() {
const {
loading
} = this.state
return (
<div>
{loading ? 'loading....' : ''}
</div>
);
}
showLoading() {
this.setState({
loading: true
})
}
hideLoading(){
this.setState({
loading: false
})
}
}
class App extends LoadingComponent{
render() {
return (
<div>
//super表示被继承的基类
{super.render()}
app
</div>
);
}
componentDidMount() {
this.showLoading()
setTimeout(() => {
this.hideLoading()
},3000)
}
}
let obj = new Proxy({
a:10,
b:20
}, {
get: function (target,key,) {
console.log('get ',key)
return target[key] * 10
},
set: function (target,key,value) {
return Reflect.set(target,key,value)
}
})
window.obj = obj
export default App;
- Proxy 拦截器
//两个参数 1目标对象 2处理器有get和set
let obj = new Proxy({
a:10,
b:20
}, {
get: function (target,key,) {//target即为第一个参数
console.log('get ',key)
return target[key] * 10
},
set: function (target,key,value) {
//Object.assign()
//Object.keys()
//Reflect相当于Object,未来给对象新添的方法只部署在Reflect上
return Reflect.set(target,key,value)
}
})
- Decorator装饰器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cCSM7w0S-1571282357749)(en-resource://database/1070:1)]
class Math {
@log
add(a, b) {
return a + b;
}
}
function log(target, name, descriptor) {
//参数1是所要修饰的目标对象,即类的实例(这不同于类的修饰,那种情况时target参数指的是类本身);参数2是所要修饰的属性名,参数3是该属性的描述对象
var oldValue = descriptor.value
descriptor.value = function () {
console.log(`Calling ${name} with`, arguments)
return oldValue.apply(this, arguments)
}
return descriptor;
}
const math = new Math();
math.add(1, 2)
import React from 'react';
import ReactDOM from 'react-dom'
let loading = (Com) => {
class LoadingComponent extends Com {
constructor(props) {
super(props);
this.state = {
loading: false
}
}
render() {
const {
loading
} = this.state
return (
<div>
{super.render()}
{loading ? 'loading....' : ''}
</div>
);
}
showLoading() {
this.setState({
loading: true
})
}
hideLoading() {
this.setState({
loading: false
})
}
}
return LoadingComponent
}
//@loading=>loading(App)返回一个新的组件继承自App
@loading
class App extends React.Component {
render() {
return (
<div>
app
</div>
);
}
componentDidMount() {
this.showLoading()
}
}
export default App;
async await 与promise
import React from 'react';
import ReactDOM from 'react-dom'
import axios from 'axios'
class App extends React.Component {
render() {
return (
<div>
app
</div>
);
}
getValue() {
return new Promise(resolve => {
setTimeout(() => {
resolve('ryan')
}, 3000)
})
}
async componentDidMount() {
let {
data: {
data
}
} = await axios
.get('http://localhost:7777/api')
console.log(data)
let value = await this.getValue()
console.log(value)
}//这段代码的意思是先请求接口,然后执行getValue()
//babel编译成下面这个函数
componentDidMount2() {
axios
.get('http://localhost:7777/api').then(({
data: {
data
}
}) => {
console.log(data)
this.getValue().then(value => {
console.log(value)
})
})
}
}
export default App
自定义组件
import React,{Component} from 'react'
//更好拼接类名的插件
import classNames from 'classnames'
import PropTypes from 'prop-types'
import Icon from '../Icon'
import './index.scss'
class Input extends Component{
constructor(props){
super(props)
this.state = {
focus: false,
innerValue: ''
}
}
static propTypes = {
value: PropTypes.string,
onChange: PropTypes.func,
size:PropTypes.string,
}
static defaultProps = {
size: 'middle',
onChange: () => {}
}
get isControl(){
return 'value' in this.props
}
get value() {
if(this.isControl){
return this.props.value
} else {
return this.state.innerValue
}
}
render() {
const {
focus
} = this.state
const {
icon,
children,
size,
prefix,
suffix,
onChange,
rule= new RegExp(),
message,
...rest
} = this.props
let cls = classNames({
input: true,
focus,
[`size-${size}`]: true,
'react-ui__input': true
})
return (
<div>
<div className={cls}>
{prefix && <Icon name={prefix}/>}
<input
value={this.value}
onFocus={e => {
this.setState({focus: true
})
}}
onBlur={e => {
this.setState({focus: false
})
}}
onChange={(e) => {
if(!this.isControl){
this.setState({
innerValue: e.target.value
})
}
this.props.onChange(e)
}}
/>
{suffix && <Icon name={suffix}/>}
</div>
<p>
{!rule.test(this.value) && message}
</p>
</div>
)
}
componentDidMount() {
this.setState({
innerValue: this.props.defaultValue
})
}
}
export default Input
受控 特点就是有value,必须有value和onChange才能改变表单的值
非受控 defaultValue,输入框里的值可以改变,不需要onChange
//非受控组件可以直接给某个变量赋值,不需要setState
<Input
rule={/\d/}
message="只允许输入数字"
size='small' defalutValue={this.value} onChange={(e) => {
this.value = e.target.value
}}/>
高阶组件
模拟redux的connect方法
import React from "react";
import PropTypes from 'prop-types'
class Provider extends React.Component {
getChildContext() {
return this.props.store
}
static childContextTypes = {
name: PropTypes.string,
age: PropTypes.number
}
constructor(props) {
super(props)
this.state = {
name: 'provider-user'
}
}
render() {
return this.props.children
}
}
class BaseUser extends React.Component {
render() {
return (
<div>
{this.props.name}
</div>
);
}
}
class BasePost extends React.Component {
render() {
return (
<div>
{this.props.age}
</div>
);
}
}
const connect = (Com) => {
class ConnectComponent extends React.Component {
static contextTypes = Provider.childContextTypes
//将ConnectComponent组件的名字改为传进来Com组件的名字
displayName = Com.displayName
render() {
return (
<Com {...this.context} />
);
}
}
return ConnectComponent
}
const User = connect(BaseUser)
const Post = connect(BasePost)
const store = {
name: 'ryan',
age: 10
}
class App extends React.Component {
render() {
return (
<Provider store={store}>
<div>
<User />
<Post />
</div>
</Provider>
);
}
}
export default App
模拟@connect方法
import React,{Component} from "react";
import PropTypes from 'prop-types'
// 用于预先 将业务组件,进行数据的封装,便于我们 方便获取数据
// 反向继承
const connect = key => Com => {
//属性代理,继承原生的component,一定要用render方法
class connectComponent extends Component {
constructor(props){
super(props)
this.state = {
[key]: store[key]
}
}
render() {
return <Com {...this.state}/>
}
componentDidMount() {
let that = this
window.store = new Proxy(store, {
get: function (target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
that.setState({
[key]:value
})
return Reflect.set(target, key, value, receiver);
}
})
}
}
return connectComponent
}
let store = {
name: 'ryan',
age: 10
}
@connect('age')
class User extends Component{
render() {
return <div>{this.props.age}</div>
}
}
class App extends Component{
render() {
return <User/>
}
}
export default App
import React, { Component } from "react";
import PropTypes from 'prop-types'
// 用于预先 将业务组件,进行数据的封装,便于我们 方便获取数据
// 反向继承 交互的封装,
const loading = Com => {
//反向继承,继承传入的类,不建议写render方法
class LoadingComponent extends Com {
//继承了Com的componentDidMount方法
showLoading() {
console.log('loading')
}
hideLoading() {
console.log('hide')
}
}
return LoadingComponent
}
@loading
class User extends Component {
render() {
return <div>user</div>
}
componentDidMount() {
this.showLoading()
//http
this.hideLoading()
}
}
class App extends Component {
render() {
return <User />
}
}
export default App
React hooks
组件的组成
数据:props state
组件生命周期
函数式组件
定义和修改组件状态 useState
import React, { useState, useEffect } from 'react'
//函数式组件
const Action = (props) => {
//定义状态 修改状态 传入初始状态
const [value, setValue] = useState('js')
const [sex, setSex] = useState('man')
//函数式组件不能条件式的定义状态,即const [sex,setSex] = useState('man')不能放在if(){这里},否则该组件会找不到其中的某项状态
return (
<div>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<button onClick={(e) => props.onAdd(value)}>add</button>
</div>
)
}
const List = (props) => {
return (
<div>
{props.data.map(item => (
<div key={item.id}>
<span>{item.name}</span><button>del</button>
</div>
))}
</div>
)
}
const App = (props) => {
const [data, setData] = useState([
{ name: 'react', id: 1 },
{ name: 'vue', id: 2 }])
return (
<div>
<Action onAdd={(name) => {
data.push({
name: name,
id: Math.random()
})
setData([...data])
}} />
<List data={data} />
</div>
)
}
export default App
组件生命周期
useEffect函数将componentDidMount和componentDidUpdate合并成一个方法
useEffect(()=>{
console.log('hello')
//当第二个参数传入[]空数组时,componentDidMount被触发
},[])
import React,{useState,useEffect,useRef} from 'react'
const useTitleHook = (title) => {
useEffect(() => {
document.title = title
//当title的状态被改变时该函数才会被调用
},[title])
}
const App = (props) => {
let [title,setTitle] = useState('hello')
useTitleHook(title)
return (
<div >
sss
<input value={title} onChange={(e) => setTitle(e.target.value)}/>
</div>
)
}
export default App
componentWillUnMount组件被销毁时,函数式组件的写法
useEffect(() => {
console.log('hello')
return ()=>{
//组件被销毁时触发
console.log('status clear')
},[title])
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IWooatab-1571282357749)(en-resource://database/1074:1)]
自定义hook
import React, { useState, useEffect, useRef } from 'react'
const useTitleHook = (title) => {
useEffect(() => {
document.title = title
return () => {
document.title = 'title'
}
})
}
const App = (props) => {
let [title, setTitle] = useState('hello')
useTitleHook(title)
return (
<div >
sss
<input value={title} onChange={(e) => setTitle(e.target.value)} />
</div>
)
}
export default App
React与Typescript
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nHepYuEE-1571282357750)(en-resource://database/1076:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lpq7W6Wn-1571282357750)(en-resource://database/1078:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rl1qVSiJ-1571282357751)(en-resource://database/1080:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cRl02MMr-1571282357751)(en-resource://database/1082:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4SsZeR2o-1571282357751)(en-resource://database/1084:1)]
class不能创建一个抽象类的实例
抽象类里的方法只能通过继承该抽象类和继承类的实例调用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-utOLEvI9-1571282357751)(en-resource://database/1086:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QJhJi830-1571282357752)(en-resource://database/1088:1)]
通过以下指令创建ts版本的react项目
create-react-app my-app --scripts-version=react-scripts-ts && cd my-app
import * as React from 'react';
import {FriendItem} from "../types";
interface onDel {
(index: number): void
}
interface IProps {
list: Array<FriendItem>,
onDel: onDel
}
class List extends React.Component<IProps>{
render() {
const {
list,
onDel
} = this.props
return (
<div>
{list.map((item: FriendItem,index: number) => <div>
{item.name} <button onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
onDel(index)
}}>-</button>
</div>)}
</div>
);
}
}
export default List
数据管理-Redux
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ln27dfeP-1571282357752)(en-resource://database/1090:1)]
//reducer表示store仓库里面的房间
//reducer这个入口里面有将各个不同房间整合起来的功能:combineReducers
import { combineReducers } from 'redux'
import title from './title'
import friend from './friend'
export default combineReducers({
title,
friend
// friend,
// post
})
//action表示发号施令,各个房间接受这个指令,对store里面的的数据做各种各样的修改,返回最新的数据给页面使用
//store是数据管理的总仓库,仓库里有dispatch这个属性
import { store } from '../store';
export const UPDATE_TITLE = 'UPDATE_TITLE'
export function updateTitle(data) {
const dispatch = store.dispatch
dispatch({ type: UPDATE_TITLE, data })
}
js数据解构时当里面的数据发生变化时,这时候它是对象的话,则它的内存地址没有发生变化,即对象里面的某个属性值发生变化后该对象的内存地址没有改变则js捕捉不到该对象的变化
js只会认为当前对象解构的数据的内存地址发生变化时这个数据才发生了变化
import { ADD_FRIEND, DEL_FRIEND } from '../actions/friend'
const initialState = {
list: [
{
name: 'ryan', id: 1
},
{
name: 'xiao', id: 2
}
]
}
export default (state = initialState, action = {}) => {
const { type, data } = action
let list = state.list
switch (type) {
case ADD_FRIEND:
list = state.list
list.push(data)
//list要保证是个新的,发生变化了页面才会重新渲染
return Object.assign({}, state, { list: [...list] })
case DEL_FRIEND:
list = list.filter(item => item.id !== data)
return Object.assign({}, state, { list: [...list] })
default:
return state
}
}
redux hook
没有定义全局的store,没有统一管理的地方,这里的store有redux-hook帮我们统一管理了
import React, { useReducer } from 'react'
import { render } from 'react-dom'
import Provider from 'react-redux/es/components/Provider'
import { store } from './store'
import App from './App'
const initialState = { count: 1 }
function reducer (state, action) {
switch (action.type) {
case 'reset':
return initialState
case 'increment':
return { count: state.count + 1 }
case 'decrement':
return { count: state.count - 1 }
default:
// A reducer must always return a valid state.
// Alternatively you can throw an error if an invalid action is dispatched.
return state
}
}
function Counter ({ initialCount }) {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'reset' })}>
Reset
</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
)
}
render(
<Counter />,
document.getElementById('root')
)
数据管理 Mobx
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u2StkqnO-1571282357753)(en-resource://database/1092:1)]
简单应用
import React,{Component} from 'react';
import {
observable
} from 'mobx'
import {
observer
} from 'mobx-react';
//装饰器,将App变成响应式组件
@observer
class App extends Component{
//响应式变量
@observable value = 'a'
constructor(props){
super(props)
this.state = {
time: 0
}
}
render() {
const {
time
} = this.state
return (
<div>
app
<input value={this.value} onChange={e => this.value = e.target.value}/>
{this.value}
</div>
);
}
componentDidMount() {
}
}
export default App;
复杂应用
- 入口文件
import React from 'react';
import ReactDOM from 'react-dom';
import {
Provider
} from 'mobx-react'
import './index.css';
import App from './App3';
import * as serviceWorker from './serviceWorker';
import store from './store'
window.store = store
ReactDOM.render(
<Provider {...store}>
<App />
</Provider>
, document.getElementById('root'));
window.ReactDOM = ReactDOM
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
// a 删除
// b,c 更新
// d 创
2.store里面的数据
import friend from './friend'
import post from './post'
export default {
friend,
post
}
import {
observable,computed,
autorun
} from 'mobx'
import post from './post'
class Friend {
@observable list = [
{
name:'ryan',
id:1
},
{
name:'xiao a',
id:2
},
{
name: 'xiao c',
id: 3
}
]
@observable activeId = 1
@computed//计算属性,只要return 里面的值发生了变化之后,被装饰的friendPost函数会自动执行
get friendPost(){
return post.list.filter(item => item.friendId === this.activeId)
}
}
const friend = new Friend()
export default friend
import {
observable,
action
} from 'mobx'
const addForm = {
title : '',
content : '',
friendId : '',
}
class Post {
@observable list = [
{
title:'hello ryan',
content: 'hello ryan post 1',
id:1,
friendId:1
},
{
title:'hello ryan 1',
content: 'hello ryan post 2',
id:2,
friendId:1
},
{
title:'hello ryan 2',
content: 'hello ryan post 3',
id:3,
friendId:1
}
]
}
const post = new Post()
export default post
3渲染组件
import React,{Component} from 'react';
import {
observable,
} from 'mobx'
import Friend from './views/friend'
import Post from './views/post'
import Action from './views/action'
import {
observer,
inject
} from 'mobx-react'
@inject('friend','post')//注入数据
@observer
class App extends Component{
render() {
console.log(this.props,'this.props')
return (
<div>
<h2>好友列表</h2>
<Friend/>
<h2>全部说说</h2>
<Post/>
<Action/>
</div>
);
}
componentDidMount() {
}
}
export default App;
import React,{Component}from 'react'
import {inject, observer} from "mobx-react";
@inject('friend')
@observer
class Friend extends Component{
render() {
const {
friend
} = this.props
return (
<div>
|{friend.list.map(item => <span onClick={() => {
friend.activeId = item.id
}} key={item.id}>{item.name} | </span>)}
</div>
);
}
}
export default Friend
import React,{Component}from 'react'
import {inject, observer} from "mobx-react";
@inject('friend')
@observer
class Post extends Component{
render() {
const {
friend
} = this.props
console.log(friend.friendPost.length,friend.activeId)
return (
<div>
{friend.friendPost.map(item => <div>
<h4>{item.title}</h4>
<p>{item.content}</p>
</div>)}
</div>
);
}
}
export default Post
规范化
// store/post.js
import {
observable,
action
} from 'mobx'
const addForm = {
title : '',
content : '',
friendId : '',
}
class Post {
@observable addForm = {
...addForm
}
@observable list = [
{
title:'hello ryan',
content: 'hello ryan post 1',
id:1,
friendId:1
},
{
title:'hello ryan 1',
content: 'hello ryan post 2',
id:2,
friendId:1
},
{
title:'hello ryan 2',
content: 'hello ryan post 3',
id:3,
friendId:1
}
]
@action
handleAdd(){
this.list.push(...this.addForm)
}
@action
clear(){
this.addForm = {
...addForm
}
}
}
const post = new Post()
export default post
//view/action/index.js
import React,{Component}from 'react'
import {inject, observer} from "mobx-react";
import {
observable
} from 'mobx'
@inject('post')
@observer
class Action extends Component{
render() {
const {
post
} = this.props
return (
<div>
<div>
<input placeholder='title' value={post.addForm.title} onChange={(e) => post.addForm.title = e.target.value }/>
</div>
<div>
<input placeholder='content' value={post.addForm.content} onChange={(e) => post.addForm.content = e.target.value }/>
</div>
<div>
<input type='number' placeholder='friendId' value={post.addForm.friendId} onChange={(e) => post.addForm.friendId = parseInt(e.target.value) }/>
</div>
<button onClick={(e) => {
// post.list.push()
post.handleAdd()
}}>add</button>
</div>
);
}
componentWillUnMount(){
post.clear()
}
}
export default Action
常用组件库ant-design
- 页面初始化,先显示loading组件,显示的时候请求初始化数据,将数据存到全局对象中,然后卸载掉root,再将App组件组件挂载到root上
import React, { useReducer } from 'react'
import { render, unmountComponentAtNode } from 'react-dom'
import Provider from 'react-redux/es/components/Provider'
import { store } from './store'
import App from './App'
import 'antd/dist/antd.css'
import {
getInitData
} from './api/auth'
let root = document.getElementById('root')
class Init extends React.Component {
render () {
return (
<div>
loading...
</div>
)
}
renderApp (initData) {
unmountComponentAtNode(root)
Object.assign(global, {
initData
})
render(
<Provider store={store}>
<App />
</Provider>, root
)
}
componentDidMount () {
getInitData().then(initData => {
this.renderApp(initData)
})
}
}
render(<Init />, root)
2.封装网络请求
import request from 'axios'
request.interceptors.request.use(req => {
console.group(req.method, ':', req.url)
return req
})
request.interceptors.response.use(res => {
if (res.data.status === '401') {
location.href = '/login'
return
}
return res
})
const getInitialData = params => request.get('/getInitialData', { params })
<></>相当于占位符,Header组件不用最外层的标签进行包裹,该组件内部的子元素直接套在该组件下
import React, { Component } from 'react'
import {
Popover,
Button
} from 'antd'
import {history} from '../../config'
const content = (
<div>
<p to='/logout' onClick={e => {
history.push('/logout')
}}>退出</p>
</div>
);
class Header extends Component {
render () {
return (
<>
<div className='app__header__logo'>React学习</div>
<div className='app__header__ad'>学而时习之</div>
<div className='app__header__action'>
<Popover content={content} title="Title" trigger="click">
<span>ryan</span>
</Popover>
</div>
</>
)
}
}
export default Header
封装路由历史,首先下载history包
import axios from 'axios'
import { createHashHistory } from 'history'
export const request = axios
request.defaults.baseURL = 'http://127.0.0.1:7777'
// request.defaults.withCredentials = true
request.interceptors.request.use((req) => {
if (PRODUCTION) {
console.log(req.method, ':', req.url)
}
return req
})
request.interceptors.response.use((res) => {
console.log(res)
console.group(res.config.method,res.config.url,res.status)
console.log(res.data)
console.groupEnd()
return res.data
})
export const history = createHashHistory()
然后导入到路由组件中
import {Router, Route, Link } from "react-router";
import React from 'react'
import {history} from './config'
import Header from './views/header'
import Sider from './views/sider'
import './index.css'
function App() {
return (
<Router history={history}>
<div className='app-content'>
<div className='app-header'>
<Header/>
</div>
<div className='app-body'>
<div className='app-body__sider'>
<Sider/>
</div>
<div className='app-body__content'>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</div>
</div>
</Router>
)
}
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Topics() {
return <h2>Topics</h2>;
}
function Topic({ match }) {
return <h3>Requested Param: {match.params.id}</h3>;
}
export default App
点击退出跳转到上一个页面
import React, { Component } from 'react'
import {
Popover,
Button
} from 'antd'
import {history} from '../../config'
const content = (
<div>
<p to='/logout' onClick={e => {
history.push('/logout')
}}>退出</p>
</div>
);
class Header extends Component {
render () {
return (
<>
<div className='app__header__logo'>React学习</div>
<div className='app__header__ad'>学而时习之</div>
<div className='app__header__action'>
<Popover content={content} title="Title" trigger="click">
<span>ryan</span>
</Popover>
</div>
</>
)
}
}
export default Header
React-Router原理 --React生态
class是一个类,虚拟dom通过给类名加一个标签<类名/>将这个类实例化了
Router和Route组件源码解析
import React from "react";
import PropTypes from 'prop-types'
class Router extends React.Component {
constructor(props) {
super(props);
this.state = {
hash: window.location.hash
}
}
static childContextTypes = {
hash: PropTypes.string,
}
getHash() {
let url = window.location.hash.replace('#', '')
return url
}
getChildContext() {
return {
hash: this.getHash()
}
}
componentDidMount() {
window.onhashchange = () => {//页面hash变化了通过window.location.hash可以捕捉到最新的hash
//当hash值变化之后会引发重新渲染
this.setState({
hash: this.getHash()
})
}
}
render() {
return (
<>
{this.props.children}
</>
);
}
}
class Route extends React.Component {
static contextTypes = {
hash: PropTypes.string
}
render() {
const {
path,
component
} = this.props
let instance = null
const {
hash
} = this.context
if (path === hash) {//实例化这个类 参数2:该组件的属性,如{name:a,age:3},参数3:子组件
instance = React.createElement(component, null, null)
}
return (
<>
{instance}
</>
);
}
}
class AA extends React.Component {
render() {
return (
<div>
aa
</div>
);
}
}
class BB extends React.Component {
render() {
return (
<div>
bb
</div>
);
}
}
function App() {
return (
<div className="App">
<Router >
<header className="App-header">
<div>header</div>
<Route path='/aa' component={AA} />
<Route path='/bb' component={BB} />
</header>
</Router>
</div>
);
}
export default App