最近才学习的知识
2012年2月28日结束
style
<p style={{ color: "red", fontSize: 30 }}></p>
state状态
constructor(){
this.state={}
}
react状态不可以直接更改
this.setState({修改})
props
直接放在类的里面
props是只读的
基础用法
传值
<Person title={this.state.title}></Person>
取值
<p title={this.props.title}></p>
传多个值
<Person {...person}></Person>
取值
<p title={this.props.title}></p>
理解
...person错误语法
{...person}正确
但是react中babel+react就允许...person了
props默认值
对标签属性的类型和必要性限制
static类的静态方法和属性
static defaultProps = {
gpc: "gpc"
}
prop类型验证
cnpm i prop-types -S 需要安装
对Person组件传值的类型和必要性限制
static propTypes = {
//要验证的属性名 期望的类型
title: PropTypes.string
title2: PropTypes.func
name: PropTypes.string.isRequired
}
constructor
- 类中的构造器省略,和不省略有什么区别呢
- 类中的构造器到底有什么作用呢
如果不初始化 state 或不进行方法绑定,
则不需要为 React 组件实现构造函数。
- 通过给 this.state 赋值对象来初始化内部 state。
- 为事件处理函数绑定实例
import React, { Component } from 'react'
export default class Person extends Component {
// 构造器是否接受props,是否传递给super
// 取决于是否在构造器中通过this访问props
// 如果不需要就可以省略
// constructor(props) {
// super(props);
//console.log(props)
// // 不要在这里调用 this.setState()
// }
//可以不写构造器
state = { name: 1 }
say = () => { }
render() { render调用的次数 1+n次
1是第一次渲染调用,n是以后每次更新状态,组件也得更新
return <div className="person">工具模块</div>
}
}
函数组件
function Person(props) {
函数组件只有props
函数里面没有static属性
const { name, age } = props;
return (
<ul>
<li>{name}</li>
<li>{age}</li>
</ul>
)
}
Person.prototype = {
name: PropType.string
}
ref即将废弃
this.refs.input1.value
< input type = "text" ref = "input1" />
ref推荐的使用
<input type="text" ref={dom => this.input = dom} />
this.input.value获取值
creatRef
React.creatRef调用后返回一个容器,里面是ref标识的节点
class {
myRef=react.createRef();
// this.myRef.current.value
render(){
return <input type="text" ref={this.myRef} />
}
}
事件处理
- 通过onXxx属性指定事件处理函数注意大小写
- 为了更好的兼容性
- react的委托事件,委托给最外层的元素
- 通过event.target得到发生事件的DOM元素的对象
理解
非受控组件
import React, { Component } from 'react'
export default class Person extends Component {
页面中所有输入类的dom,现用现取,非受控组件
handleSubmit = (event) => {
event.preventDefault();//阻止表单提交
const { username, password } = this;
console.log(username, password);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" ref={this.username = c} />
<input type="text" ref={this.password = c} />
</form>
)
}
}
受控组件
受控组件没有ref好一点
import React, { Component } from 'react'
export default class Person extends Component {
state = {
username: "",
password: ""
}
demo = event => {
this.setState({
username: event.target.value
})
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" onChange={this.demo} />
</form>
)
}
}
函数柯里化
接受的参数是函数 或者 函数返回的值是函数
import React, { Component } from 'react'
export default class Person extends Component {
state = {
username: "",
password: ""
}
saveFromData = (formtype) => { //函数里面返回函数
return event => {
this.setState({
[formtype]: event.target.value
})
}
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" name="username" onChange={this.saveFromData('username')} />
</form>
)
}
}
获取表单值
import React, { Component } from 'react'
export default class Person extends Component {
state = {
username: "",
password: ""
}
saveFromData = (formtype, value) => { //函数里面返回函数
this.setState({
[formtype]: value
})
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" name="username"
onChange={event => this.saveFromData('username', evnet.target.value)} />
</form>
)
}
}
旧的生命周期
1.初始化阶段:由ReactDOM.render()触发---初次渲染
(一辈子只执行一次)
1. constructor()
2. componentWillMount()
3. render() 正在渲染
4. componentDidMount() 组件挂载完成
2.更新阶段:组件内部this.setState()或父组件render触发
(根据props属性或state状态的改变,改变)
1. componentWillReceiveProps 组件将要接受新的props属性
2. shouldComponentUpdate 是否要更新组件
3. componentWillUpdate 页面是旧的,数据是新的
4. render
5. componentDidUpdate
3. 组件销毁阶段:由ReactDOM.unmountComponentAtNode()触发
(一辈子只执行一次)
componentWillUnmount
新版本生命周期
添加2个,废弃3个
挂载
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
下述生命周期方法即将过时,在新代码中应该避免使用它们:
UNSAFE_componentWillMount()
更新
- 当组件的 props 或 state 发生变化时会触发更新
- 组件更新的生命周期调用顺序如下:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
注意:
下述方法即将过时,在新代码中应该避免使用它们:
UNSAFE_componentWillUpdate()
UNSAFE_componentWillReceiveProps()
卸载
当组件从 DOM 中移除时会调用如下方法:
componentWillUnmount()
虚拟DOM key值的作用
- 如果结构中包含输入类的DOM,
对dom进行逆序添加删除,破坏顺序的操作
会产生没有必要的真实dom更新,效率低
并且有可能引发bug - 如果仅用于列表展示,没有顺序改变
index是可以用的
- 拆分组件 拆分界面 抽取组件
- 实现静态组件
- 实现动态组件
功能界面的组件化todolist案例
- 拆分组件实现静态组件
- 如何确定组件放在那个state中
- 某些组件放在共同的父组件state中
- 某个组件放在自身state中
- 父子传值
- 父传子 通过props
- 子传父 传递函数,子调用传入参数,父级改值
<SiderPage collapsed={this.state.collapsed}></SiderPage>
collapsed={this.props.collapsed}
- 状态放在那里,操作状态就放在哪里
- 注意defaultChecked和checked去区别
defaultValue和value的区别
父取子的属性和方法
通过ref获取子的实例对象 来调用
axios
cnpm i axios -S
复习基础
let obj = {a:{b:{c:1}}
连续结构赋值
let {a:{b:{c}}} = obj
连续结构赋值加重命名
let {a:{b:data} = obj
<input ref={c=>this.keyWordElement=c}/>
连续结构赋值加重命名
const {keyWordElement:{value:keyWord}} = this
axios代理配置
第一种 简单配置
packge.json
"proxy":"http://localhost:5000"
第二种 配置
src下新建setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = function (app) {
// /v1/restserver / ting
// proxy第一个参数为要代理的路由
// 第二参数中target为代理后的请求网址,
// hangeOrigin是否改变请求头,其他参数请看官网
app.use(
createProxyMiddleware('/v1', {
target: `http://tingapi.ting.baidu.com/
v1/restserver/ting?xml&calback
=&from=webapp_music&method=baidu.ting
.billboard.billList&type=2&size=100&
offset=0&callback=jsonp_1578470847634_65767`,
changeOrigin: true,
// pathRewrite: { '^/api2': '' }
}),
createProxyMiddleware('/api2', {
target: `http://tingapi.ting.baidu.com`,
changeOrigin: true,
pathRewrite: { '^/api2': '' }
})
)
}
消息订阅-发布机制
- cnpm install pubsub-js --save
react路由
印记中午查文档
https://docschina.org/
路由的基本使用
- 明确好界面中导航区、展示区
- 导航区的a标签改为Link标签
<Link to={`${match.url}/rendering`}>Rendering with React</Link>
- 展示去写Route标签进行路由与组件匹配
<Route exact path="/" component={Home} />
- 的最外层包裹了一个BrowserRouter和HashRouter
路由组件与一般组件
- 写法不同
- 一般组件:
- 路由组件:
- 存放位置不同
- 一般组件:component
- 路由组件:pages
- 接受到的props不同
路由组件的三个固定属性
history:
go(n)
goBack()
goForward()
push()
replace()
location:
pathname:"/about"
search:""
state:undefined
match:
params
path
url
NavLink解决高亮
加样式属性
import { NavLink } from 'react-router-dom'
<NavLink to="/about">About</NavLink>
将与 className 属性一起使用。
<NavLink
to="/faq"
activeClassName="selected"
>FAQs</NavLink>
标签体的内容也可以获取到
this.props.children
标签体的内容也是一个属性
<NavLink to="/about" children='about' />
switch
渲染与该地址匹配的第一个子节点 或者 。
import { Switch, Route } from 'react-router'
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
</Switch>
redirect
- 放在路由注册的最下方,所有路由都匹配不上,才去重定向
- 渲染 将使导航到一个新的地址。
- 这个新的地址会覆盖 history 栈中的当前地址,
- 类似服务器端(HTTP 3xx)的重定向。
写在最后
<Redirect to="/somewhere/else" />
<Switch>
<Redirect from="/old-path" to="/new-path" />
<Route path="/new-path" component={Place} />
</Switch>
默认页面
import { IndexRoute } from 'react-router'
<Router>
<Route path="/" component={App}>
{/* 当 url 为/时渲染 Dashboard */}
<IndexRoute component={Dashboard} />
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="messages/:id" component={Message} />
</Route>
</Route>
</Router>
路由传参
params传惨
路由链接,携带参数
<Link to="/user/gpc">gpc</Link>
注册路由,声明接受
<Route path="/user/:username" component={User}/>
接受参数
this.props.match.params.username
const User = ({ match }) => {
return <h1>Hello {match.params.username}!</h1>
}
search查询字符串传参
import qs from 'querystring'
吧对象转换为urlencoded格式
qs.stringify(obj)
qs.parse(str)
this.props.location.search
<Link to='/courses?sort=name'/>
state传参
// Hash刷新 state会丢失
<Link to={{
pathname: '/courses',
search: '?sort=name',
hash: '#the-hash',
state: { fromDashboard: true }
}}/>
this.props.location.state
push和replace
- push是压栈 留痕迹
- replace是替换 不留痕迹
编程式路由导航
replace+prarms携带id参数
this.props.history.replace('/home/id')
push+search携带id参数
this.props.history.push('/home?id=1')
push+state
this.props.history.push('/home',{id:1})
编程式前进后退
this.props.history.go(number)
this.props.history.goBack()
this.props.history.goForward()
withRouter
- 您可以通过 withRouter 高阶组件访问 history
- 对象的属性和最近的 的 match 。
- 当路由渲染时, withRouter 会将已经更新的 match ,
- location 和 history 属性传递给被包裹的组件
- withRouter让一般组件使用路由组件的API
- 也可以使用传参的方式把路由对象传入一般组件
import React from 'react'
import PropTypes from 'prop-types'
import { withRouter } from 'react-router'
// 显示当前位置的路径名的简单组件
class ShowTheLocation extends React.Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
}
render() {
const { match, location, history } = this.props
return (
<div>You are now at {location.pathname}</div>
)
}
}
// 创建一个“connected”的新组件(借用Redux 术语)到路由器。
const ShowTheLocationWithRouter = withRouter(ShowTheLocation)
路由的模糊匹配和严格匹配
- 默认模糊匹配,输入路径包含匹配的路径
- 严格匹不能随便开启,需要再开,有时候会导致二级路由无法匹配
如果为 true,则只有在路径完全匹配 location.pathname 时才匹配。
<Route exact path="/one" component={About}/>
路由权限解决
render() {
//获取用户登陆信息,如果没有登陆跳转登陆页面
//如果是事件函数 this.props.history.push()
// this.props.history.replace()不需要再退回登陆页
if (!false) {
return <Redirect to="/login"></Redirect>
}
return (
<div>
</div>
)
}
解决多级路径刷新页面样式丢失的问题
localhost3000就代表脚手架
public文件夹就相当于localhost3000根路径
引入样式用/根路径 或者 %PUBLIC_URL% HashROuter解决
antd的按需加载
redux
- 英文文档 https://redux.js.org/
- 中文文档 https://redux.js.org.cn/
- github文档 https://github.com/reactjs/redux
- 某个组件的状态需要让其他组件可以随时拿到(共享)
- 一个组件需要改另一个组件的状态(通信)
- 总体原则,尽量不用
redux三个核心概念
action
- 动作对象
- 包含2个属性
- type:标识属性,字符串,唯一,必要属性
- data:数据属性,任意类型,可选属性
- 例子
{type:"ADD_STUDENT",data:{name:"gpc",age:18}}
export const addCount = num => ({ type: "ADD", num })
reducer
(reducer本质就是一个处理state的函数)
- 用与初始化状态、加工状态
- 加工时,根据旧的state和action,产生新的state的纯函数
store
- 将state action reducer联系在一起的对象
- 如何得到此对象
- 对象的功能
react-redux作用
cnpm install --save react-redux redux actions
- 容器组件和ui组件整合一个文件
- Provider可以给所有组件store
- connect生成容器组件可以自己检测redux中状态改变
- mapDispatchToProps也可以简写成一个对象
具体流程
- 定义UI组件不爆露
- 引入connect生成容器组件,并且暴露
connect(
state=>({key:value})映射状态
{key:xxxAction} 映射操作状态的方法
)(UI组件)
- 在UI组件中通过this.props.xxx读取和操状态
redux精简案例
src/redux/reducers/index
import { combineReducers } from 'redux'
import * as count from './count'
import * as person from './person'
export default combineReducers({
...person,
...count
})
src/redux/reducers/count.js
export function count(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + action.num
case 'DECREMENT':
return state - action.num
default:
return state
}
}
src/redux/reducers/person.js
export function person(persons = [], action) {
switch (action.type) {
case 'ADD_TODO':
// person.push(action.text);
// return pserson
//这样写就不是纯函数了,所以不可以这样写
return persons.concat([action.text])
default:
return persons
}
}
src/redux/actions/person.js
export const addPerson = text => ({ type: "ADD_TODO", text });
src/redux/actions/count.js
export const addCount = num => ({ type: "INCREMENT", num });
export const delCount = num => ({ type: "DECREMENT", num });
src/index.js 引入redux创建store对象
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './store/reducer/index.js'
// <React.StrictMode>
// </React.StrictMode>,
// 将app组件渲染到index页面上
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
组件中使用
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addCount, delCount } from '../../redux/actions/count'
//ui组件
class Count extends Component {
add = () => {
console.log(this.props.count)
this.props.addCount(3)
}
del = () => {
this.props.delCount(3)
}
render() {
return (
<div>
<h2>当前求和状态{this.props.count}</h2>
<button onClick={this.add}>加加</button>
<button onClick={this.del}>渐渐</button>
</div>
)
}
}
//容器组件
export default connect(
//简写 挂载reducers里定义的值
//state就是reducers/index.js导出的合并后的对象
//count自定义属性,state.count从总对象里取值
state => ({ count: state.count }),
//挂载action操作reducers的方法
{ addCount, delCount }
// 映射状态
// 映射操作状态的方法
)(Count)
@connect语法糖+redux-action插件简化版本
语法变化
https://blog.csdn.net/qq_36262295/article/details/113691975
实战案例
https://blog.csdn.net/qq_36262295/article/details/113708401
纯函数
- 同样的输入,必定得到同样的输出
- 必须遵守一些约束
- 不得改写参数数据
- 不会产生任何副作用,例如网络请求,输入和输出设备
- 不能调用Date.now()或者Math.random()等不纯的方法
- redux的reducer函数必须是一个纯函数
lazy加载路由
import React,{Component,lazy,Suspense} from 'react'
const Home = lazy(()=>import('./Home'));
用这个标签包裹注册路由
fallback或者放一个loading组件
<Suspense fallback={<h1>Loading....</h1>}>
<Route path="/about" component={Home}>
</Suspense>
redux异步action了解
// redux-thunk 支持异步redux的插件
在首页Index.js导入
import { applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
let store = createStore(count_reducer, applyMiddleware(thunk))
// 在action.js里面
// 异步action 指action值为函数,
export const jiaCount = (num, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(addCount(num))
}, time)
}
}
Hooks
- 可以在函数组件中使用state以及其他react特性
- 只在最顶层使用 Hook
三个常用的Hook
- stateHook:React.useState()
- EffectHook:React.useEffect()
- RefHook:React.useRef()
stateHook
import React, { useState } from 'react';
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
等价的 class 示例
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState
({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
useEffect
- useEffect可以执行副作用操作(模拟生命周期钩子)
- react常用的副作用操作
- ajax请求
- 设置订阅/定时器
- 手动更改真实dom
- 代码案例
useEffect(
() => {
//在此可执行任何副作用操作
return () => { //返回的这个函数相当于componentWillUnmount
//用于清除副作用 清除定时器等副作用
subscription.unsubscribe();
};
},
[stateValue],
// stateValue 每次数据改变后执行
//如果是空数组,回调函数只会在第一次render()后执行
//如果不写[],代表检测所有人
);
- useEffect Hook 看做 这三个函数的组合。
- componentDidMount
- componentDidUpdate
- componentWillUnmount
useRef
- useRef 返回一个可变的 ref 对象,
- 其 .current 属性被初始化为传入的参数(initialValue)。
- 返回的 ref 对象在组件的整个生命周期内保持不变。
- 一个常见的用例便是命令式地访问子组件:
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
context
- Context 主要应用场景在于很多不同层级的
- 组件需要访问同样一些的数据。
- 请谨慎使用,因为这会使得组件的复用性变差。
// 有局限只使用类组件
//创建context对象
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
使用前需要定义接受
static contextType = MyContext
this.context
Consumer
可以在函数组件使用
<MyContext.Consumer>
{value => <span>{value}</span>
/* 基于 context 值进行渲染*/}
</MyContext.Consumer>
优化
- 父组件渲染触发子组件重新渲染(子组件没有使用父组件的state)
- 父组件改变state,但是没有真的变化,也触发重新渲染
解决
- 重写shouldComponentUpdate()方法,比较新旧state和props
- PureComponent重新了shouldComponentUpdate方法,采用这个
class Child extends PureComponent{}
render props
- vue中的slot
- react中:
- children props 通过组件标签体传入结构
- render props 通过组件标签传入结构,一般用render函数
children props
<A>
<B>XXXX</B>
</A>
this.props.children获取B组件的dom内容
问题:B组件使用A组件的数据,做不到
render props
F组件渲染A组件的同时,传入render属性返回C组件
<A render={data=><C data={data}></C>}></A>
A组件执行就渲染出来了C组件 {this.props.render(传入的state数据)}
C组件 读取A组件的数据显示{this.props.data}
错误边界Error boundary
用来捕捉后代组件错误,渲染出备用页面
年少不知富婆好,误把少女当成宝
打包加服务器运行
cnpm i serve -g
vue3在线文档笔记
https://24kcs.github.io/vue3_study