react学习笔记

最近才学习的知识

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

  1. 类中的构造器省略,和不省略有什么区别呢
  2. 类中的构造器到底有什么作用呢
    如果不初始化 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} />
  }
}

事件处理

  1. 通过onXxx属性指定事件处理函数注意大小写
  • 为了更好的兼容性
  • react的委托事件,委托给最外层的元素
  1. 通过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值的作用

  1. 如果结构中包含输入类的DOM,
    对dom进行逆序添加删除,破坏顺序的操作
    会产生没有必要的真实dom更新,效率低
    并且有可能引发bug
  2. 如果仅用于列表展示,没有顺序改变
    index是可以用的
  1. 拆分组件 拆分界面 抽取组件
  2. 实现静态组件
  3. 实现动态组件

功能界面的组件化todolist案例

  1. 拆分组件实现静态组件
  2. 如何确定组件放在那个state中
  • 某些组件放在共同的父组件state中
  • 某个组件放在自身state中
  1. 父子传值
  • 父传子 通过props
  • 子传父 传递函数,子调用传入参数,父级改值
  <SiderPage collapsed={this.state.collapsed}></SiderPage>
  collapsed={this.props.collapsed}
  1. 状态放在那里,操作状态就放在哪里
  2. 注意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': '' }
  })
  )
}

消息订阅-发布机制

  1. cnpm install pubsub-js --save

react路由

印记中午查文档
https://docschina.org/

路由的基本使用

  1. 明确好界面中导航区、展示区
  2. 导航区的a标签改为Link标签
 <Link to={`${match.url}/rendering`}>Rendering with React</Link>
  1. 展示去写Route标签进行路由与组件匹配
 <Route exact path="/" component={Home} />
  1. 的最外层包裹了一个BrowserRouter和HashRouter

路由组件与一般组件

  1. 写法不同
  • 一般组件:
  • 路由组件:
  1. 存放位置不同
  • 一般组件:component
  • 路由组件:pages
  1. 接受到的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

  1. 放在路由注册的最下方,所有路由都匹配不上,才去重定向
  2. 渲染 将使导航到一个新的地址。
  3. 这个新的地址会覆盖 history 栈中的当前地址,
  4. 类似服务器端(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

  1. push是压栈 留痕迹
  2. 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

  1. 您可以通过 withRouter 高阶组件访问 history
  2. 对象的属性和最近的 的 match 。
  3. 当路由渲染时, withRouter 会将已经更新的 match ,
  4. location 和 history 属性传递给被包裹的组件
  5. withRouter让一般组件使用路由组件的API
  6. 也可以使用传参的方式把路由对象传入一般组件
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)

路由的模糊匹配和严格匹配

  1. 默认模糊匹配,输入路径包含匹配的路径
  2. 严格匹不能随便开启,需要再开,有时候会导致二级路由无法匹配
如果为 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

  1. 英文文档 https://redux.js.org/
  2. 中文文档 https://redux.js.org.cn/
  3. github文档 https://github.com/reactjs/redux
  4. 某个组件的状态需要让其他组件可以随时拿到(共享)
  5. 一个组件需要改另一个组件的状态(通信)
  6. 总体原则,尽量不用

redux三个核心概念

action

  1. 动作对象
  2. 包含2个属性
  • type:标识属性,字符串,唯一,必要属性
  • data:数据属性,任意类型,可选属性
  1. 例子
{type:"ADD_STUDENT",data:{name:"gpc",age:18}}
export const addCount = num => ({ type: "ADD", num })

reducer

(reducer本质就是一个处理state的函数)

  1. 用与初始化状态、加工状态
  2. 加工时,根据旧的state和action,产生新的state的纯函数

store

  1. 将state action reducer联系在一起的对象
  2. 如何得到此对象
  3. 对象的功能

react-redux作用

cnpm install --save react-redux redux actions

  1. 容器组件和ui组件整合一个文件
  2. Provider可以给所有组件store
  3. connect生成容器组件可以自己检测redux中状态改变
  4. mapDispatchToProps也可以简写成一个对象

具体流程

  1. 定义UI组件不爆露
  2. 引入connect生成容器组件,并且暴露
connect(
  state=>({key:value})映射状态
  {key:xxxAction} 映射操作状态的方法
)(UI组件)
  1. 在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

纯函数

  1. 同样的输入,必定得到同样的输出
  2. 必须遵守一些约束
  • 不得改写参数数据
  • 不会产生任何副作用,例如网络请求,输入和输出设备
  • 不能调用Date.now()或者Math.random()等不纯的方法
  1. 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

  1. stateHook:React.useState()
  2. EffectHook:React.useEffect()
  3. 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

  1. useEffect可以执行副作用操作(模拟生命周期钩子)
  2. react常用的副作用操作
  • ajax请求
  • 设置订阅/定时器
  • 手动更改真实dom
  1. 代码案例
useEffect(
  () => {
    //在此可执行任何副作用操作
    return () => { //返回的这个函数相当于componentWillUnmount 
    //用于清除副作用 清除定时器等副作用
      subscription.unsubscribe();
    };
  },
  [stateValue],
  // stateValue 每次数据改变后执行
  //如果是空数组,回调函数只会在第一次render()后执行
  //如果不写[],代表检测所有人
);
  1. 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>

优化

  1. 父组件渲染触发子组件重新渲染(子组件没有使用父组件的state)
  2. 父组件改变state,但是没有真的变化,也触发重新渲染

解决

  1. 重写shouldComponentUpdate()方法,比较新旧state和props
  2. PureComponent重新了shouldComponentUpdate方法,采用这个
class Child extends PureComponent{}

render props

  1. vue中的slot
  2. react中:
  3. children props 通过组件标签体传入结构
  4. 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

react17引入less

vue3在线文档笔记

https://24kcs.github.io/vue3_study

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值