react原理
react->数据与模板相结合,生成虚拟dom,利用虚拟dom结构,生成真的dom。
如果数据改变,通过diff算法,比较原始的虚拟dom和新的虚拟dom,找出之间的区别,将差异的地方重新渲染。
减少对真实dom的创建和比对,提高性能
react虚拟Dom
const Dome = (
<div class='container'>
<span>直接写入html标签</span>
</div>
)
关于虚拟dom
1.本质是object类型的对象
2.虚拟dom较轻,虚拟dom没有那么多属性
3.虚拟dom将被react转为真实dom,呈现在页面上
jsx语法规则
1.类名使用className
2.标签中插入js需要使用{}
1.标签首字母:大写->react按照组件去渲染,若组件未定义,将报错;小写->转为html同名标签渲染,若html无此标签报错。
组件定义
function MyComponent(){
return <div>函数组件</div>
}
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {};
}
render(){ return <div>类组件</div> }
}
ReactDom.render(<App/>, document.getElementById('container'));
组件状态states
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {
age:25
};
}
render(){ return <div>类组件</div> }
}
组件属性props
1.组件的props,父级传给组件,不可以修改
2.组件的props限制
class MyComponent extends React.Component{
static propTypes = {
name:PropTypes.string.isRequired,
age:PropTypes.number,
eat:PropTypes.func
}
static defaultProps = {
sex:'男'
}
constructor(props){
super(props);
}
render(){ return <div>类组件</div> }
}
MyComponent.propTypes = {}
MyComponent.defaultProps = {}
组件的ref
1.字符串 不推荐使用
2.回调函数 this指向的函数,比箭头回调函数少调用一次
3.creatRef 访问值需要在current中,this.myRef.current.value;
class Demo extends React.Component{
myRef = React.createRef();
showValue(){
const { inputStr } = this.ref;
console.log(inputStr.value);
const { inputFunc } = this;
console.log(inputFunc.value);
console.log(this.myRef.current.value);
}
saveInput(ref){
this.thisFuncRef = ref;
}
render(){
return(
<div>
<input ref='inputStr' name='stringRef' /><br/>
<input ref={ref=> this.inputFunc = ref} name='funcRef' /><br/>
<input ref={this.saveInput} name='thisFuncRef' /><br/>
<input ref={this.myRef} name='creat_Ref' />
<button onClick={this.showValue} />
</div>
)
}
}
组件中的事件处理
1.处理onXxx属性指定事件处理函数
例如onClick,onMousemove,onMouseover... 等等事件首字母需大写
a.React使用的是自定义事件,非原生Dom事件---具有更好的兼容性
b.React的事件均为事件委托方式处理(委托给组件最外层元素)---高效
收集表单数据
1.非受控组件->数据直接从ref中获取并提交
2.受控组件->数据存与state中,通过state获取数据并提交
组件的生命周期
生命周期图形
class MyDemo extends React.Component{
constructor(props){
super(props);
}
componentWillMount(){
console.log('即将挂载,执行单次 顺序2')
}
componentDidMount(){
console.log('挂载完成,执行单次 顺序3')
}
componentWillUnmount(){
console.log('即将卸载,执行单次')
}
componentWillReceiveProps(){
console.log('即将收到父组件的props')
}
shouldComponentUpdate(){
console.log('state更新,是否更新组件')
return false;
}
componentWillUpdate(){
console.log('组件将更新')
}
componentDidUpdate(){
console.log('组件已更新')
}
render(){
return(<div>生命周期</div>)
}
}
Dom的diffing算法
1.虚拟Dom中key的作用
当状态更新时,react会根据【新数据】生成【新的虚拟Dom】,React进行
【新虚拟dom】与【旧虚拟dom】的diff比较:
若旧虚拟dom找到与新虚拟dom相同的key:
a.若未发生改变,则直接使用原来的dom
b.若已发生改变,则生成新的真实dom,并替换于页面之中
若未找到与新虚拟dom相同的key
创建新的dom,渲染到页面
2.使用index作为key可能引发的问题
a.若对数据进行逆序操作,会产生没有必要的更新,效率低下
b.若结构包含输入类的dom,会产生错误的dom更新->界面出现问题
组件的一些事
1.两个子组件共同使用父级的state数据,通过props传递:状态提升
2.子组件->父组件,通过回调函数;父组件->子组件,props
3.defaultValue与value的区别,defaultValue为默认值
4.消息订阅与发布机制(组件通信)
import PubSub from 'pubsub-js'
this.token = PubSub.subscribe('xzp-go',function(title, data){
});
PubSub.unsubscribe(this.token);
PubSub.publish('xzp-go',{...data})
react路由
react靠路由连接实现组件切换
路由组件接收到Props不同:
history:
go: ƒ go(n) (页面跳转至n)
goBack: ƒ goBack() (返回上一级菜单)
goForward: ƒ goForward() (往前)
push: ƒ push(path, state)
replace: ƒ replace(path, state)
location:
pathname: "/about"
search: ""
state: undefined
match:
params: {} (参数传递)
path: "/about"
url: "/about"
路由的严格匹配与模糊匹配
1.默认模糊匹配,【输入路径】必须包含【匹配的路径】,且顺序一致
2.开启严格模式:<Route exact={true} path='/xxx' component={xxx} />
默认页面配置Redirect
1.于所有路由最下方注册,当所有路由都匹配失败时,则跳转到Redirect指定的路由
嵌套路由
1.注册子路由需要写上父级路由的path值
2.路由的匹配是按照注册路由的顺序进行的
import { BrowserRouter } from 'react-router-dom';
ReactDom.render(<BrowserRouter><App/></BrowserRouter>, document.getElementById('root'));
import { Link, Route } from 'react-router-dom';
<div className='link-list'>
<Link className='link-item' to='/home'>Home</Link>
<NavLink activeClassName='xzp-link' className='link-item' to='/xxx'></NavLink>
</div>
<div className='content-div'>
<Switch>
<Route path='/home' component={Home} />
<Route path='/xxx' component={Home} />
<Redirect to='/home' />
</Switch>
</div>
<NavLink to='/home/myCard'>myCard</NavLink>
<Switch>
<Route path='/home/myCard' component={MyCard} />
</Switch>
<NavLink to={'/home/myCard/19970920/xzp'}>myCard</NavLink>
<Route path='/home/myCard/:id/:name' component={MyCard} />
const { params = {} } = this.props.match || {};
<NavLink to={'/home/myCard/?id=19970920&name=xzp'}>myCard</NavLink>
import qs from 'querystring'
const { search } = this.props.location || {};
const { id , name } = qs.parse(search.slice(1));
<Link to={{pathname:'/home/myCard',state:{id:19970920,name:'xzp'}}}>myCard</Link>
const { state={} } = this.props.location || {};
BrowserRouter与HashRouter的区别
1.底层原理不一致:
browserRouter使用的是H5的history API,不兼容IE9及以下版本。
hashRouter使用的是URL的哈希值
2.path表现形式不一样
browserRouter -> localHost:3000/demo/test
hashRouter -> localHost:3000/#/demo/test
3.刷新后对路由state参数的影响
1.browserRouter无任何影响,state保存在history中
2.hashRouter刷新后导致路由state参数丢失
4.hashRouter解决一些路径错误的相关错误
react redux react-redux
创建一个redux核心对象store
import { createStore } from 'redux';
import reducer from './xzp_reducer';
export default createStore(reducer);
reducer:本质为函数,传入两个参数:
preState:上一个state状态
action:dispatch发送过来的东西{type:xxx,data:xxx}
const initState = 0;
export default function xzpReducer(preState = initState, action){
const { type, data } = action;
switch(type){
case 'xx':
case 'zz':
default: return preState;
}
}
组件中调用:引入store,用于获取redux保存的状态
import store from './store';
store.dispatch({type:'add', data: {} });
新建文件:生成action对象
export const createAdd = data=>({type:'add',data});
export const createSub = data=>({type:'sub',data});
组件中调用:引入store,用于获取redux保存的状态
import store from './store';
import { createAdd } from './xzp_actions';
store.dispatch(createAdd(100));
异步action
1.需要使用中间件redux-thunk
2.创建action函数需要返回函数,该函数中写异步任务
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './xzp_reducer';
export default createStore(reducer, applyMiddleWare(thunk));
export const createAsync = (data)=>{
return (dispatch)=>{
dispatch({type:'xxx', data})
}
}
react-redux的使用,使用容器组件>UI组件,将state作为props传给UI组件
容器组件:将state与dispatch操作作为props传递给UI组件
import { connect } from ' react-redux';
function mapStateToProps(state){
return {keyName: state};
}
function mapDispatchToProps(dispatch){
return {
addValue:(val, name)=>{
dispatch({type:'xxx',data:value});
},
asyncValue:(val)=>{
dispatch(function(dp){
dp({type:'add',value:val})
});
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(XzpUI);
const { keyName } = this.props;
this.props.addValue(100);
react-redux优化给根组件包裹组件Provider
import store from './src/store';
import {Provider} from 'react-redux';
ReactDom.render(<Provider store={store}><App/></Provider>, document.getElementById('root'));
redux数据共享
需要使用combineReducers对单个组件的reducer进行汇总
import {createStore, applyMiddleware, combineReducers} from 'redux';
const allReducer = combineReducers({
xzp:xzpReducer,
yyy:yyyReducer
});
export default createStore(allReducer,applyMiddleware(thunk));
export default connect(state=>({
name:state.xzp.name,
love:state.yyy.name
}),dispatch=>({
changeAge:(age)=>({type:'addAge',data:age})
}))(XzpUI);
redux redux-react总结
redux原理->发布式的监听器,创建存储空间store存储state,并提供发布功能来修改数据以及监听发布消息触发回调函数。
redux-react作用->监听store数据的变化,将数据传递给UI组件
Provider->通过context api将store对象注入react组件
Connect->高阶组件,监听store数据的更新,通过setState方法触发组件更新,并将state作为props传递给UI组件。
react扩展
setState
this.setState({name:'xzp'});
this.setState({name:'yyy'},function(){
});
this.setState((state, props)=>{
return { name:'hh'};
}, function(){
});
lazyLoad
import { lazy, Suspense } from 'react';
import Loading from './component/Loading';
const Home = lazy(()=> import('./component/Home'));
const Xzp = lazy(()=> import('./component/Xzp'));
<Suspense fallback={<Loading />}>
<Route path='/home' component={Home} />
<Route path='/xzp' component={Xzp} />
</Suspense>
Hooks
1.Hooks为react的新特性/新语法
2.可以在函数组件中使用state以及其他的react特性
useState 使用state
useEffect 生命周期钩子
useReducer -> dispatch
useMemo 缓存数据
useCallBack 缓存函数
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
function Demo(){
const [value, setValue] = React.useState(0);
const [name, setName] = React.useState('xzp');
const myRef = React.useRef();
React.useEffect(()=>{
return ()=>{}
}, [name]);
function changeAdd(){
setValue(value +1);
setValue((value)=>{
return value +1;
});
let inputValue = myRef.current.value;
}
const callback = useCallback(()=>{
},[])
const sum = useMemo(()=>{
let sum = 0;
for (let i = 0; i < value * 100; i++) {
sum += i;
}
return sum;
},[])
return (
<div>
<input type="text" ref={myRef} />
<h2>state的值:{value}</h2>
<button onClink={changeAdd}>+1</button>
</div>
)
}
const myReducer =(state, action)=>{
switch(action.type){
case 'xxx':
return {}
default:
return state;
}
}
function App(){
const [state, dispatch ] = useReducer(myReducer, {value:0})
return (
<div onClick={()=>{dispatch({type:'xxx',data:{}})}}>{state.value}</div>
)
}
Fragment 与 空标签
区别:Fragment可以传key,空标签不可以新增任何属性
Context组件间通信方式
const MyContext = React.createContext();
const {Provider, Consumer} = MyContext;
let data = {name:'xzp'};
<Provider value={{data}}>
<Root />
</Provider>
static context = MyContext;
const {data = {}} = this.context;
function Child(){
return <div>
<Consumer>
{
value=>{ return <span>{value.data.name}<span> }
}
</Consumer>
</div>
}