react 基础笔记

105 阅读8分钟

react

原生组件

  1. 原生js操作dom繁琐,效率低(dom-api)
  2. 原生js操作dom,造成浏览器会进行大量的重绘重排
  3. 没有组件化编码,代码复用率低

特点

  1. 采用组件化模式,声明式编码,提高开发效率
  2. 在react native中可以使用react进行移动端开发
  3. 使用虚拟DOM+优秀的diff算法,尽量减少真实DOM的操作

虚拟DOM

  1. 本质是Object对象,(一般对象)
  2. vdom比较轻,dom比较重,vdom是react再用,无需真实dom上那么多属性。
  3. vdom最终会被转化为真实dom,呈现在页面

JSX语法

  1. vdom不能加“”

  2. 标签中混入js表达式要用 {}

  3. 样式类名用className

  4. 内联样式要用style={{key:value}} //style={{ fontSize: 13 }}

  5. vdom必须有一个跟标签,且标签必须闭合

  6. 标签首字母:

    • 小写:转化为html,无对应,报错
    • 大写:渲染组件

函数式组件

注:不能使用this, undefined因为bable编译后开启了严格模式

ReactDom.render():

  1. react解析组件标签,找到标签
  2. 将vdom转化为真实的dom呈现在页面

类式组件

class MyComponnet extends ReactDom.compont{
​
    render(){ //render 在原型上,供实例使用
​
       return () //this 指实例对象
​
            }
​
}

ReactDom.render():

  1. react解析组件标签,找到标签
  2. 把类定义,new出实例对象,通过实例调用原型上的render
  3. 将render返回vdom转化为真实的dom呈现在页面

State(状态)

简单组件无 state

复杂组件有state

class HelloReact extends React.Component {
  constructor(props) {
    super(props);
    this.state = {name:'hello'};
  }
 
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>现在是 {this.state.name}.</h2>
      </div>
    );
  }
}
 
ReactDOM.render(
  <HelloReact />,
  document.getElementById('example')
);

组件实例的三大核心

state:{}

不可直接修改,必须使用 this.setState({key:value})

//合并,不会覆盖其他属性

class HelloReact extends React.Component {
  constructor(props) {
    super(props);
    this.state = {name: 'hello'};
  }

 
  clieckBtn=()=>{
    this.setState({
      date: new Date()
    });
  }
 
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>现在是 {this.state.date}.</h2>
        <button onClick={this.clieckBtn}> </button>
      </div>
    );
  }
}
 
ReactDOM.render(
  <HelloReact />,
  document.getElementById('example')
);

注:

  1. 组件中的render方法中this指组件实例对象

  2. 组件自定义方法中this为undefined

    • 强制绑定this,通过函数对象bind()
    • 箭头函数
  3. 状态数据不能直接修改或更改

construction构造器

  1. 通过this.state赋值对象初始化内部state
  2. 为事件处理函数绑定实例

注:

  1. 不要调用this.setState方法
  2. 构造器是否接受props,是否传递给super ,取决于是否在构造器中通过this使用

props:只读

子组件获取数据,并且不可修改

  1. 默认 Props

    class HelloReact extends React.Component {
      render() {
        return (
          <h1>Hello, {this.props.name}</h1>
        );
      }
    }
     
    HelloReact.defaultProps = {
      name: 'xixi'
    };
     
    const element = <HelloReact/>;
     
    ReactDOM.render(
      element,
      document.getElementById('example')
    );
    

2.Props 验证

var title = "嘻嘻哈哈";
// var title = 123;
class HelloReact extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.title}</h1>
    );
  }
}
 
HelloReact.propTypes = {
  title: PropTypes.string
};
ReactDOM.render(
    <HelloReact title={title} />,
    document.getElementById('example')
);

ref

React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。

1.字符型refs (存在效率问题,已弃用)

<input ref="myInput" />
​
var input = this.refs.myInput;
var inputValue = input.value;

2.回调形式

<input ref={(a)=>{this.input1=a}}  .../>  // a:input元素

注:

若ref回调函数以内联方式定义的,在更新过程中会被执行两次,第一次传参为null,第二次传参为Dom。因为每次渲染时会创建一个新的函数实例,react清空的ref并设置新的。

解决,定义成class类函数绑定(一般不用管,直接写成内联)

inputFunc=(a)=>{
   this.input1=a
}
​
​
​
<input ref={this.inputFunc()}  .../>  

3.creactRef 函数

this.myRef = React.createRef().current.innerHTML
​
​
​
<div ref={this.myRef}>hello</div>
​

事件处理

React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:

  • React 事件绑定属性的命名采用驼峰式写法,而不是小写。

    使用自定义合成(合成)事件,而不是使用原生DOM事件(兼容性)

    事件可以通过事件委托(最外层) 高效

  • 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)

  • 通过event.target得到发生事件的Dom元素对象(不要过度使用ref)

function ActionLink() {
  function handleClick(event) {
    event.preventDefault();
  }
 
  return (
    <a href="#" onClick={handleClick}>
      点我
    </a>
  );
}
​
//event 为合成事件

受控组件和不受控组件

区别:

  1. 受控组件依赖于状态,而非受控组件不受状态的控制;
  2. 受控组件只有继承“React.Component”才会有状态,而非受控组件则不是只有继承才有状态;
  3. 受控组件一般适用于需要动态设置初始值时,非受控组件一般用于无任何动态初始值信息时。

组件的生命周期

组件的生命周期可分成三个状态:

  • Mounting(挂载):已插入真实 DOM
  • Updating(更新):正在被重新渲染
  • Unmounting(卸载):已移出真实 DOM

挂载时:

constructor -> componentWillMount -> render -> componentDidMount

更新时:

  1. 父组件更新 (render)

    componentWillReceiveProps() -> shouldComponentUpdate() -> componentWillUpdate() -> render() -> componentDidUpdate()

    注:

    componentWillReceiveProps()

    第一次不调用

  2. 数据更新

    setState() -> shouldComponentUpdate() -> componentWillUpdate() -> render() -> componentDidUpdate()

    注:

    shouldComponentUpdate()

    可以不写,写了必须返回 true/false

    true:执行下一步

    false:不执行

  3. 强制更新

    foreUpdata() -> shouldComponentUpdate() -> componentWillUpdate() -> render() -> componentDidUpdate()

新:

componentWillMount () → UNSAFE_componentWillMount ()

componentWillReceiveProps() → UNSAFE_componentWillReceiveProps(nextProps)

componentWillUpdate() → UNSAFE_componentWillUpdate(nextProps, nextState)

新增:

getDerivedStateFromProps

为static,所以无法调用this.props。

1.父组件通知子组件pros发生变更时

2.子组件每次render前

3.首次构造时执行

getSnapshotBeforeUpdate

1.首次构造时不执行

2.子组件每次render后

消息订阅与发布机制(兄弟组件传参)

npm install pubsub-js --save  // pubsub-js库 

(1)先利用npm install pubsub-js --save下载pubsub-js库。

(2)再利用import PubSub from 'pubsub-js'引入pubsub-js库。

(3)订阅:利用componentDidMount钩子将PubSub.subscribe函数挂载,从而在需要订阅的组件进行消息的订阅

    //订阅
    componentDidMount(){
        this.token = PubSub.subscribe('usersData',(_,data) => {
            console.log('订阅:',data)
        
        })
    }
​

(4)发布:利用PubSub.publish在进行发布的组件中发布消息。

    PubSub.publish('usersData',{isFirst:false,isLoading:true})

(5)取消:在刚才订阅的组件中,利用componentWillUnmount钩子将PubSub.unsubscribe挂载将此组件中的订阅进行取消。

  componentWillUnmount(){
    PubSub.unsubscribe(this.token)
  }
​

路由

路由分类

1.后端路由: 1)理解: value是function, 用来处理客户端提交的请求。 2)注册路由: router.get(path, function(req, res)) 3)工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据 2.前端路由: 1)浏览器端路由,value是component,用于展示页面内容。 2)注册路由: 3)工作过程:当浏览器的path变为/home时, 当前路由组件就会变为Home组件

路由组件与一般组件

1.写法不同: 一般组件: 路由组件: 2.存放位置不同: 一般组件:components 路由组件:pages 3.接收到的props不同: 一般组件:写组件标签时传递了什么,就能收到什么 路由组件:接收到三个固定的属性(常用) history: go: ƒ go(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"

路由组件

<BrowerRouter />   //常用
​
<HashRouter />
​
<Route  path=''  exact  />   //exact 精准匹配(有二级路由会出问题)
//route 的主要参数 : path exact component(5+) element   element={<item.component />}
                   render  
​
<Redirect  to='' />
​
<Link to='' />
​
<NavLink to=''  />
​
<Switch> //只匹配一个

多级路径刷新页面样式丢失的问题

1.public/index.html中引入样式时 不写./ 写/ (常用)

2.public/index.html中引入样式时不写 ./ 写%PUBLIC_URL% (常用)

3.使用HashRouter

路由组件传递参数

1.params参数
    路由:<Link to='/demo/lili/18'}>详情</Link>
    注册路由:<Route path="/demo/:name/:age" component={xx}/>
    接收参数:this.props.match.params
2.search参数
    路由:<Link to='/demo/test?name=tom&age=18'}>详情</Link>
    注册路由:<Route path="/demo/test" component={xx}/>
    接收参数:this.props.location.search
    注:获取到的search是urlencoded编码字符串,需要借助querystring解析
3.state参数
    路由:<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
    注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={xx}/>
    接收参数:this.props.location.state
    备注:刷新也可以保留住参数
​

编程式路由导航

利用this.prosp.history对象
    this.prosp.history.push()
    this.prosp.history.replace()
    this.prosp.history.goBack()
    this.prosp.history.goForward()
    this.prosp.history.go()
​

withRouter 的使用

 如果在你想在一般组件使用 路由组件所特有的API 时, 就要借助 withRouter 
withRouter可以加工一般组件, 让一般组件具备路由组件所特有的API
withRouter的返回值是一个新组件
​
​
import { withRouter} from "react-router-dom";
​
export default withRouter(Layout)
​

注: withRouter 外必须包裹BrowserRouter或者HashRouter

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参数的丢失!!!
​
注:HashRouter可以用于解决一些路径错误相关的问题。
​

路由注意

//路由元素
const routes = [
  {
    path: '/home',
    name: 'home',
    component: () => import('@/pages/home/home.jsx'),
    meta: {
      title: 'Home',
      id: "Home",
​
    }
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('@/pages/login/login.jsx'),
    meta: {
      title: 'login',
      id: "login",
​
    }
  },
 
 
]
​
export default routes;
//生成路由
import React,{Component,lazy,Suspense} from "react"
//lazy Suspense 一般一起用,路由懒加载import { Route ,Switch} from "react-router-dom"import routes from "./routerItem"
​
​
export default class CusRouters extends Component{
    render (){
        return (
            <Suspense fallback={<div style={{ textAlign: 'center', fontSize: '20px', padding: '100px 0', color: '#0d7bff' }}>Loading...</div>}>
            <Switch>
                {routes.map(item=>{
                    return  <Route key={item.path} path={item.path} component={lazy(item.component)}/>
                })}
​
            </Switch>
            </Suspense>
        )
    }
}

注:

lazy(() => import(xx)) // 等价于 import Home from 'xx';

redux

专门做状态管理的js库,集中管理react应用的多组件共享状态(能不用就不用了)

redux三大核心

1 action 触发reducer方法动作 2 reducer 3 store

Action 是把数据从应用(译者注:这里之所以不叫 view 是因为这些数据有可能是服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。

Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。

Store 有以下职责: