react知识汇总

参考:React面试全解 - 知乎

一、为什么用react

1、开发团队是fackbook团队,实力强大,开源
2、社区完善,很多问题都能找到
3、很多国内外大型互联网公司都在使用
4、优秀的diff算法、高效,使用虚拟dom,不直接操作页面

二、虚拟dom

DOM:浏览器本质,以js对象的方式表示页面元素,并提供api
按需更新就是用到了虚拟dom,通过比较新旧两颗DOM结构,按需更新
虚拟DOM:用JS对象模拟页面上的DOM和DOM嵌套关系,使DOM高效更新
var div = {
   tagname:'div'
   attr:{
    id:'test'
    }
}

三、diff算法

diff:新旧两颗DOM树对比时,需要逐层比较,这个过程就是diff算法;

数据改变的时候,react不会直接到去修改dom,而是生成新的虚拟dom,通过对比旧的虚拟dom,有改变的标签或节点就会生成对应部分的真实dom,没改变的部分则直接使用旧的真实dom

为什么不建议使用index作为key值?
key作为虚拟dom的唯一标识,如果有逆向增加、逆向删除等操作,key值就会变乱,导致dom全部重新生成,页面渲染变慢甚至出现数据混乱

webpack搭建项目并启动:

npm init -y 初始化
npm i webpack webpack-cli -D //安装webpack
npm i webpack-dev-server -D  //安装自动运行,将打包后的main.js放在根目录
npm i html-webpack-plugin -D  //将index.html放到根目录内存
 

pack.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack serve --open chrome.exe"
  },

webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')//导入自动自动生成index.html页面到根目录

const htmlPlugin = new HtmlWebpackPlugin({
    template:path.join(__dirname,'./src/index.html'),//源文件路劲,__dirname是当前目录
    filename:'index.html' //内存页生成的文件名称
})

module.exports = {
    mode:'production',
    plugins:[
        htmlPlugin
    ]
}

webpack结合react使用jsx过程和所需安装的包

babel升级,踩坑!Error: Cannot find module '@babel/core'_兔子零-CSDN博客

JSX语法(创建虚拟dom的最简单最直接方式)

const a = 1000
const jsxDom = <p>你好</p>
const title = 'testval'

ReactDom.render(<div 
    title={title}>
    {a}{jsxDom}
    {{arr.map(item=><h3 key={item}>{{item}}</h3>)}} //循环需要加key
</div>,
document.getElementById('app')
)

脚手架安装

npm i -g create-react-app  //只需执行一次,二次创建则不用再执行这命令
create-react-app  xxx  //项目名不用大写

VSCODE安装以下插件:
ES7+ React/Redux/React-Native snippets
用快捷键:rcc 快速生成react模板

创建组件和传值(父传子):

const dog= {
    name:'小狗'
    age:3
}

function Hello (props) { //方法名开头一定要大写
    return <div>Hello组件,传值{props.name},{props.age}</div>
}

ReactDom.render(<div>
    <Hello name={dog.name} age={dog.age}></Hello>
    //或使用扩展
    <Hello {...dog}></Hello>
</div>)

法二、
新建Hello.jsx文件:
import React from 'react'  //组件文件要引入,大写R开头
export default function Hello (props) { //方法名开头一定要大写
    return <div>Hello组件,传值{props.name},{props.age}</div>
}
引入:
import Hello from 'xxx/xxx/Hello.jsx'

法三、使用class创建
class ClassName extends React.Component{
    render(){
        return <div>{this.props.name}</div> //直接使用this.props拿到传值
        
    }
}


子传父

状态在哪,就在哪操作
父把操作的方法传给子,子通过prop调用这个方法,把新数据通过参数的形式返回给父来操作。
父:
const getChildToChange = ()=>{//todo}
<child getChildToChange={this.getChildToChange}>

子:
const { getChildToChange } = this.prop
getChildToChange(childdata)

祖孙则再传一层

消息的发布和订阅(兄弟组件传值)

?/安装
npm i pubsub-js --save
//引入
import PubSub from 'pubsub-js'
//A组件发布
PubSub.public('ecentName',dataArr)
B组件接收
this.sub = PubSub.subscribe('ecentName',(msg,data)=>{ data//就是传过来的值 })

卸载发布
PubSub.unsubscribe(this.sub)

prop的类型限制

//npm i props-types
import PropTypes from 'props-types'
class Child extends React.Component{
    static PropTypes = {
        value1:propsTypes.array.isRequired,//限制传数组
        valueName2:propsTypes.func.isRequired //限制传函数
    }
}

class创建的组件可以有私有变量属性,可读可改


class ClassName extends React.Component{
    constructor(){//构造函数里面的this是指向ClassName实例
        super() //子类自定义构造器,必须要调用super

        this.state = {  //相当于vue中data定义值
            test:'这是值'
        }
        this.test = this.test.bind(this)//将原型上的test方法改变this指向后,生成test挂在实                
                                            例上,使test方法里的this指向ClassName实例
    }
    render(){//react会默认new生成实例调用render方法
        return <div>
            {this.props.name}--{this.state.test}
            <div onClick = {this.test}>点击</div>
            //<div onClick = {()=>this.test()}>点击</div> //上面不写bind时用

        </div> //直接使用this.props拿到传值
        
    }
    test(){//类中的方法都是挂在原型对象上,实例调用时里面的this才是指向ClassName实例否undefind
        this.setState({ //修改state定义的值,此方法是异步,需要在回掉中拿到最新值
            test:'newval'
        },function(){
            console.log(this.state.test)
        })
    }
    //此test()是原始的定义方式,this是指向谁调用,如果用在onClick,则this指向window
    //此定义的正确使用方式是 onClick="{this.test.bind(this)}"
}
ClassName.propTypes = {
    name:PropTypes.string.isRequired,//限制为字符串和必须传
    age:PropTypes.number,
    tools:PropTypes.func,//传的是方法,类型则是func
}
ClassName.defaultProps = {//设置默认值
    age:18,
}

class创建的组件简化写法:

在类里面直接通过key=value会吧key挂在实例上,而不是原型上
原理:剪头函数里的this是指向定义时的this和谁调用无关

class ClassName extends React.Component{
    state = {//初始化状态
       name:'xxz',
       age:18
    }

    render(){//取值渲染
        const { age } = this.state
        return <div onClick={this.test}  >年龄:{age}</div>
    }

    test = ()=>{//自定义方法
        this.setState({age:20})
    }
}

JSX使用style和class

style:
render(<div style={{fontSize:'12px'}}></div>)

class:
新建styleObj.scss文件:
.title{
    colot:red
}
使用的js文件引入:
import styleObj from '../../styleObj.scss' //模块化
render(<div className={styleObj.title}></div>)

或
import  '../../styleObj.scss' //模块化
render(<div className='title'></div>)

ref

方式一、字符串式(不建议用,影响效率)
<div ref='mydef'></div>
实例:this.refs.mydef

方式二、回调函式(内联式在数据更新时执行两次)
<div ref={(c)=>this.myref=c}></div> //直接创一个变量myref挂载在实例上,c就是当前节点dom
使用:const { myref } =  this

方式三、使用React.createRef(推荐)
<div ref={this.myref}></div>
class里面:
myref = React.createRef() //多个ref则名称要不一样
console.log(this.myref.current) //拿到dom

可控和不可控组件

不可控:通过ref取值的不可控
可控:通过函数,将值挂载到state

高阶函数和函数柯里化

高阶函数:A函数接收到参数是一个函数或A函数返回值是一个函数,那A函数就是高阶函数
如:Promise、setTimeout、arr.map(()=>{})等等

函数柯里化:函数会不停的返回函数,并且通过一个函数接收一个参数来处理

原生事件想传额外的值

//高阶函数实现
<input type='checkbox' onChange={this.checkChange(id)}>
const checkChange = (id)=>{
    return (e)=>{
        console.log(id,e.target.checked)
    }
}

//普通函数实现
<input type='checkbox' onChange={()=>{this.checkChange(id)}}>
const checkChange = (id)=>{
    console.log(id)
}

生命周期

一、初始化阶段ReactDOM.render()
constructor()
componentWillMount()  //后期要加UNFATE_componentWillMount() 
render()
componentDidMount()

二、更新阶段
componentWillReceiveProps() //props改变  /后期要加UNFATE_
shouldComponentUpdate()
componentWillUpdate() /后期要加UNFATE_
render()
componentDidUpdate()

三、卸载ReactDOM.unmontComponentAtNode()
componentWillUnmount()

新生命周期

//渲染阶段
constructor()
getDerivedStateFromProps() //需要返回state对象或null
render()
componentDidMount()

//更新阶段
getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate(preProp,preState) //返回任意值除了undefined,componentDidUpdate接收 
componentDidUpdate(preProp,preState,snapshot)

//卸载
componentWillUnmount()

样式模块化,防止冲突,相当于vue中的scoped

创建时css文件名:xxx.module.css
使用:
import democss from './xxx.module.css'
className = {democss.xxx}

内联样式

<div style={{color:'red',fontSize:this.state.sixe+'px'}}>

react的 vscode插件 

快捷创建组件插件:ES7 React/Redux/GraphQL/React-Native snippets
输入rcc或rfc

配置代理

代理:相当于本地开一个服务器,前端向本地服务器发起请求,本地服务器再向远程服务器转发,
      本地服务器和远程服务器不存在跨域
法一:在pack.json文件里
"proxy":"http://xxxx:5000"

法二:配置多个代理
1、在src下新建setupProxy.js
2、
const proxy = require('http-proxy-middleware')//react默认已安装
module.exports = function(app){
    app.use(
        proxy('/api',{//带有api的都或触发此代理
            target:'http://localhost:5000',
            changeOrigin:true,//将请求头的host改成和远程服务器的一样
            pathRewrite:{'^/api':''}
        }
            
        )
    )
}

连续性结构赋值

如:
const this= {
    item:{
        name:'test'
    }
}

const {item:{name}} = this
console.log(name)//test
//改字段名
const {item:{name:newname}} = this
console.log(newname)//test

路由:

//安装:npm i react-router-dom

//ipmport {Router,Switch,Route,NavLink} from 'react-router-dom'

//点击跳转到对应路由
<NavLink to="/home"></NavLink>

//注册路由
<Switch> //switch 后面的覆盖前面的
    <Route path="/home" component={Home}></Route>
    <Redirect to="/home">
</Switch>


app.js下使用:
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import Home from '../page/Home.js'
import User from '../page/User.js'
return(
    <Router>
        <link to='/home'>去首页</link> //菜单
        <link to='/user'>去首页</link>
        <Routes> //路由注册
            <Route path="/" exact element={<Home />}></Route>//exact精确匹配路由,默认显示/home
            <Route path="/home" element={<Home />}></Route>
        </Routers>
      
    </Router>

)

路由传参数和编程式跳转

params方式:
<link to=`{/home/${id}}`>
<Route path="/home/:id" component={Home}></Route>
//子组建抛出 export default withRouter(home) 
this.props.match.params.id

serch方式(query):
import qs from 'querystring'
<link to=`{/home/?id=01&age=18}`>
<Route path="/home" component={Home}></Route>
const serch =  this.props.location //?id=01&age=18 (urlencoded字符串)
const {id} = qs.parse(serch.slice(1))

state方式(url上不会出现参数,刷新也不会丢失)
<link to={pathname:"/home",state:{id:1}}>
<Route path="/home" component={Home}></Route>
this.props.location.state

编程式跳转:
this.props.history.push('/home') //replace,goForward,go,goBack

一般组件的路由跳转

//让一般都组件也带有路由组件的props
import {widthrouter} from 'react-router-dom'
class className extends Component{ xx }
export default widthrouter(className)

BrowserRouter和HashRouter区别

BrowserRouter:使用h5的history原理,ie不兼容,路径上没#,刷新有保留props的state
HashRouter:使用锚点原理,全兼容、刷新不保留props的state

react-redux全局状态管理

步奏一:创建store.js

根目录创建store.js:
//安装 : npm i --save redux react-redux

//引入模块
import { legacy_createStore as createStore } from "redux";
//创建状态值,相当于vuex中的state
const initState = {
    num:1
}
//创建reducer,是一个函数,固定接收两个参数,参数1是state,参数2是action
//action的值固定格式是{type:xxx,value_xxx:xxx}
const reducer= (state=initState,action)=>{
    switch(action.type){
        case 'addNum':
            return {
                ...sate,
                num:action.num
            }

        default
            return state
    }
}

export const store = createStore(reducer);

步奏二:把redux嵌入到react项目中

//在index.js目录中
import { store } from "./store/index";
import {Provider} from 'react-redux'

root.render(
  <React.StrictMode>
    <Provider store = {store}>
        <App />
    </Provider>
    
  </React.StrictMode>
);

步奏三:组件中使用store和修改store的值

//在需要用到store的组件中使用rcredux快捷键创建组件

import React, { Component } from 'react'
import { connect } from 'react-redux'

export class myUseCom extends Component {
  render() {
    const {num,addNum} = this.props //从props中获取到store中的num,和改变方法
    return (
      <div>
        <button onClick={()=>addNum(num+1)}> {num} </button>
      </div>
    )
  }
}

//相当于vuex中的state
const mapStateToProps = (state) => ({
  num:state.num  //会把num创建到prop中,组件则可以从this.prop获取到该值
})

//相当于vuex中的mutation
const mapDispatchToProps = dispatch => {
  addNum:(num)=>dispatch({type:'addNum',num:num})
}

export default connect(mapStateToProps, mapDispatchToProps)(myUseCom)

hook

//hook主张用函数式创建组件,但函数式的this是undefind,state和生命周期如何调用呢
//函数组件中使用useSate和useEffect,就可以模仿state和生命周期的特性

import React,{useSate,useEffect} from 'react'
function demo (){
    //useSate hook 模仿state和setState
    const [number,setNumber] = useSate(0) //定义初始值0,和修改值得方法

    function show(){
        setNumber(100)//重置值
    }

    //useEffect hook 模仿生命周期
    useEffect(()=>{
      console.log('写这里,组件挂载完毕执行')
      return ()=>{
        console.log('写这里,组件卸载前执行。。')
      }
    },[number]) //是[]时候,相当于componentDidMount,[]里的number代表限定number改变才触发
    //参数二里的值表示此值改变会触发生命周期,没写的值在变化时不触发生命周期


    //useRef hook
    const myRef = React.useRef()
    

    return (
        <div>
            <input ref={myRef}>
            <button onclick={show}></button>
        </div>
    )
}

context(祖孙)

const {provide,mudom} = createContext()
父:
<provide value={this.state.count}>

子
<mudom>
    {(value)=>this.context.value}
</mudom>

pureComponent

替换Component,只有state和prop数据变化后才调用对应组件的render

插槽

方式一:
<parent>
    <child></child>
</parent>

class parent extends Components{
    render(){
        {this.props.childen}
    }
}

方式二:可传值
<parent render={(value)=><child myValue = value>}>
    
</parent>

class parent extends Components{
    render(){
        {this.props.render(this.state.value)}
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值