R 基础 2

1 高阶组件


1 高阶组件的使用方式

  const A = () => {}   // 定义组件
  export default Test(A)   // 修饰组件

  @test
  @user
  @demo
  class A extends React.Component{}   // 定义组件

  class A extends React.Component{}
  export default test(user(demo(A)))  //等价于上面的修饰器
  


2 注意事项: 

  1 修饰符语法只能用于类组件

  2 当一个组件要同时被多个高阶组件修饰时, 用修饰器语法就很便捷, 或者用常规的方式嵌套

  3 当一个UI组件同时被多个高阶组件修饰时, 发现 props 有丢失的情况, 如下解决方案

	高阶组件内UI组件实例时 +{...this.props}: <A1 {...this.props} />

2 HOOKS

     
1 简单描述: 

  1 概念: React 16.8 的新增特性, 可以让函数式组件使用 state 以及其他的 React 特性   //高性能+状态

  2 本质: React 内置了一组 API
  
  3 常用 API: 
	1 useState   // 状态
	2 useEffect   // 副作用 [use 饿饭可特]
	3 useContext   // 深层状态传递



3 useState()   // 函数式组件中使用自己的 state 数据

  	import React from 'react'
	import {useState} from 'react'

	// 操作
	export default props => {
	
	  // 定义变量
	  const [msg1, setMsg1] = useState(1)   // 变量 msg1, 值 1, 更新变量的函数 setMsg1
	  const [msg2, setMsg2] = useState([])  // 定义一个空数组

	  // 修改变量
	  function add() {setMsg1(msg1+1)}      // 定义方式 1
	  const sub = () => {setMsg1(msg-1)}   // 定义方式 2
	}

	注意事项: 
	  1 定义变量一定要有初始值
	  2 定义变量的同时, 定义一个 setMsg1方法, 专门用于修改声明式变量 msg1, 并且会触发 diff运算



4 useEffect()   // 函数式组件中使用副作用

  1 简单描述
	1 副作用: 调接口, 定时器, DOM操作, 长连接等 逻辑都叫做副作用 (会影响组件性能)
	2 通常这样的副作用, 写在生命周期函数里, 但是函数式组件中没有, this和生命周期函数
	3 那么 useEffect API 就可以帮我们模拟出生命周期的特性, 然后就可以在函数式组件中执行副作用了


  2 代码范式: 
  
  import React from 'react'
  import {useEffect} from 'react'

  export default props => {
    useEffect(()=> {
      // do something (调接口等)
      return ()=> {
        // do something (关定时器等)
      }
    },[])
  }


  3 注意事项: 
    1 默认要写 [], 不写会有问题
    2 若要在某个变量改变的时候, 重新触发 useEffect, 这么写 [msg]
      useEffect(()=>{return ()=>{}}, [msg1])  // msg1每次改变, 就会再运行一次模拟生命周期
    3 useEffect(()=>{return undefined}, [])  // 什么都不做也要返回一个 undefined
	4 ES6: useEffect(()=>{return ()=>{}}, [])
	5 ES5: useEffect(function() {return function() {}}, [])
	6 把相关问题放在一个副作用里, 因为一个组件可以创建多个副作用

3 React 路由


### 有时间搞, 还不是很完善, 暂时不需要 --> 基于 UmiJS 的路由规则

     
1 概念
----------------------------------------------------------------------------
  1 React 路由特点都是组件化的
  2 react-router 是基于上下文实现的
  3 路由配置的流程
    1 路由模式的选择
    2 修改 url的操作接口 (可以后写)
    3 配置路由映射关系 (就是路由和对应组件的匹配规则)
----------------------------------------------------------------------------
  
2 安装
----------------------------------------------------------------------------
  1 React路由有3个库 -- react-router / react-router-dom / react-router-native
  2 npm install react-router -S
  3 npm install react-router-dom -S  ( 建议安装这个, 其实安装上面的也可以)
----------------------------------------------------------------------------
  
3 路由模式的选择 (俩种)
----------------------------------------------------------------------------
  1 let { HashRouter, BrowserRouter} from 'react-router-dom'
  2 哈希模式: <HashRouter> <div>呵呵</div> </HashRouter>  
  3 history 模式: <BrowserRouter> <div>呵呵</div> </BrowserRouter> 
  4 用引入的组件实例 HashRouter把, 把组件视图包起来, 该组件就有了路由的功能
----------------------------------------------------------------------------

4 改变url的方式 (俩种) + 基础路由匹配规则
----------------------------------------------------------------------------
1 声明式路由跳转 -- App.js
  1 let { HashRouter, NavLink, Link, Route, Redirect} from 'react-router-dom' 
  2 <HashRouter> 视图结构 </HashRouter>  // 1 选择路由模式
  3 视图结构中(定义 3个改变 url的标签)  // 1 改变 url的操作接口
    1 <NavLink to='/jsx'> jsx学习 </NavLink>  
    2 <NavLink to='/props'> props学习 </NavLink>
    3 <Link to='/hh'> hh</Link>  // NavLink自带高亮样式的, Link没有
  4 视图结构中(定义视图标签 Route)
	1 import Jsx from '@/pages/Jsx'
	2 import Props from '@/pages/Props'
	3 <Route path='/jsx' component={Jsx}/>  // 对应上面的 url路径
	4 <Route path='/props' component={Props}/>  // 对应路径显示对应的视图容器
	5 类似 Vue中的视图标签, 但是这么写没有路由配置文件, 代码乱而多, 而且不方便管理
	6 所以我们通常把, 相关数据定义在一个文件中, 然后通过遍历渲染的方式, 在 App.js中定义视图标签

2 编程式路由跳转
  1 凡是被 <Route>元素直接包裹的组件, 它的 props都能拿到路由相关 API 
  2 由于一部分公共组件, 没有在 App.vue中被<Route>渲染和包裹, 所以没有路由相关 API
    1 通过一个内置的高阶组件来扩展
      1 import { withRouter} from 'react-router-dom'
      2 A1组件: export default withRouter(A1) //该组件props就可以使用路由相关API了
    2 通过内置 API来实现 (推介) 
      // 常用 API: useHistory / useLocation / useParams / useRouteMatch
      1 import { useHistory} from 'react-router-dom'
      2 const A1 = ()=>{const h = useHistory()	//即可通过 hh来访问路由相关的 API 了}
  3 公共组件只用路由相关 API, 应该是二级路由的使用需求 
----------------------------------------------------------------------------

5 标准的路由规则映射定义
----------------------------------------------------------------------------
1 创建路由配置文件 src/router/index.js (定义路由映射关系)

2 引入要配置路由的组件
  1 import Jsx from '@/pages/Jsx'
  2 import Props from '@/pages/Props'

3 设置路由配置文件
  export default [
	{id: 1, path: '/jsx', text: '学习Jsx', component: Jsx},
	{id: 2, path: '/props', text: '学习Props', component: Props}]

4 App.js 中使用
  1 import routes from '@/router/index.js'
  
  2 定义渲染 NavLink的方法() 
    1 感觉导航栏自己按需添加比较科学, 没必要在此处渲染
    2 类似 Vue中的 router-link, 通过改变 url, 来达到显示路由对应组件的效果
    renderNavLinks() {return(
      routes.map(v =>(<NavLink key={v.id} to={v.path}>{v.text}</NavLink>)) )}
      
  3 定义渲染视图容器的方法
    1 给所有配置了路由的组件视图, 定义一个对应的视图容器 <Route>
    2 如果自己直接写也是可以的, ( 1级路由文件少的情况下)
    3 因为没有 Vue中的默认视图, 所以配置路由的 1级组件都要, 有自己对应的视图容器才可以显示
	renderRoutes() {return(
	  routes.map(v=>(<Route key={v.id} path={v.path} component={v.component}/>)}
	  
  4 使用路由
	return (<div>
	  <nav>{this.renderNavLinks()}</nav>  //渲染我们所配路由的声明式导航, (感觉自己写科学)
	  <div class="main">  
	    {this.renderRoutes()}
	  </div>
	</div>)

  5 如何实现重定向 Redirect
	1 import {Redirect, Switch} from 'react-router-dom'
	2 基于上面的代码
	  <div class="main">  //上面渲染视图容器的地方
	    <Switch>  // 设置重定向需要, 用这个标签把, 视图容器标签包裹起来
	      {this.renderRoutes()}
	      <Redirect from='/*' to='/jsx' />  //表示所有找不到的, 定向到 '/jsx'
	    </Switch> 
	  </div>
----------------------------------------------------------------------------

6 动态路由传参
----------------------------------------------------------------------------
1 路由映射表配置 -->  path: 'jsx/:id'  // 目标页面路由配置
2 路由传参 --> <span onClick={()=>props.history.push('jsx'+id)}></span>  // 起始页面
3 目标页面接收路由参数
  1 props.match.params.id   // 直接接收
  2 import { useParams} from 'react-router-dom'  // 内置 API 接收
    const id = useParams().id 
----------------------------------------------------------------------------

7 代码分割 (路由懒加载)
----------------------------------------------------------------------------
1 理解
  1 把所有的 js文件打包在一个 js文件中, 如果这个项目很大这么干是不可取的, 就要对代码进行分割
  2 Vue 中叫做路由懒加载, 这里叫做代码分割, 也是按需加载

2 步骤
  1 安装
    1 npm i @babel/plugin-syntax-dynamic-import -D   // 用于支持动态导入语法
    2 npm i @babel/preset-react -D   // 
    3 npm i @loadable/component -S   // 用于动态导入组件
  2 使用
    1 import loadable from '@loadable/component'
    2 const Jsx = loadable(()=>import('@/pages/Jsx'))  // 懒加载的写法
    3 const routes = [{id: 1, path: '/jsx', text: 'jsx语法', component: 'Jsx'}]
    4 export default routes
    5 简写: export default []
----------------------------------------------------------------------------

4 Axios 调接口


### 有时间搞, 暂时不需要 (有 Dva 的 model)

     
1 安装: npm i axios -S
2 axios 的二次封装, 跟 Vue中是一样的
3 基于二次封装再封装一个 API 接口文件
4 再需要数据的页面, 引入对应接口 API,useEffect() 中请求数据 
5 基本上除了, 调接口的位置, 和数据的处理方式,Vue是差不太多的 (一般使用 Redux)

5 Mobx

1 简单描述: 
----------------------------------------------------------------------------
  1 概念: 也是一个全局状态管理工具  ///类似 Vue 中的 Vuex
  2 作用: 可以让全局组件之间实现数据共享   //很方便做缓存
  3 理解: 虽然框架一般都有组件通信的功能, 但是全局状态管理工具, 可以更人性的操作数据
  4 使用流程: 
    1 安装相关的包, 做一个环境
    2 主要是基于俩个文件: 全局状态管理的配置文件(涉及到分包) + 单文件组件中如何去使用
----------------------------------------------------------------------------

2 环境配置
--------------------------------------------------------------------------------------
1 相关安装: 
    1 npm i mobx -S  // 只是用于定义状态管理容器的
    2 npm i mobx-react -S  //用于把 mobx 配置文件 和 react 项目联系起来
    
1 环境配置: App.js
  import { Provider} from 'mobx-react'  //连接工具
  import store from '@/store/index.js'  //mobx 配置文件
  <Provider store1={store}></Provider>   
  //用如上标签, 把 App.js 的视图结构包裹起来; 且被路由包裹(习惯); 自定义属性 store1
-------------------------------------------------------------------------------------

3 开始使用: 
-------------------------------------------------------------------------------------
1 创建状态管理文件: src/store/index.js  //用于集中管理 mobx 单个分出去的数据模块的
  import X1 from './modules/x1.js'
  class Store{
    constructor() {
      this.x1 = new X1()  //在这里对子 store 进行实例化
    }
  } 
  export default new Store()  //导出这个全局对象

2 创建状态模块: src/store/modules/x1.js  //分模块(也叫子store), 便于管理

  import {  //导入 mobx 的一些常用 API
    observable,   // 用于定义可共享的数据,相当于Vuex中的 state
    action,       // 相当于是Vuex中的 mutations + actions
 	computed,     // 相当于是Vuex中的 getters
	makeObservable,   //自定义指定属性和方法的分类 -> 方式 1
    makeAutoObservable   //默认分类属性和方法 -> 方式 2
  } from 'mobx'
  
  class X1{
    constructor() {
      makeAutoObservable(this)  //把所有定义的方法放在 action 上, 属性放在 observable上
      makeObservable(this, {
        msg: observable,  //把 msg 变为一个个共享的数据
      })
    }
    msg = "不再期待爱情"
    hh() {console.log(this.msg)}
    get hh() {}   //书写的时候, 计算属性前面要加一个 get 作为标识
  } 
  export default X1

3 单文件组件中怎么使用 mobx呢: 
  import React from 'react'  //单文件组件必须的
  import { observer, inject} from 'mobx-react'  // 引入俩个高阶组件
  
  const A1 = props => {
    const msg = props.store1.msg  //取值方式 1 --> 应该可能是, store1, 而不是 store
    const {msg} = props.store1  //取值方式 2
    return (<div> {msg} </div>) 
  }
  
  export default inject(store1)(observer(A1))   // 这个修饰了之后, props 中就有了 store 数据
  // inject()() 双括号调用 --> inject() 返回的是一个高阶函数
  // inject(store)(UI组件)  --> 表示注入
  // observer(UI组件)   --> 把当前组件变成观察者, 解决全局数据变化不能动态更新数据的问题
-------------------------------------------------------------------------------------

Redux

1 简单描述: 
---------------------------------------------------------------------------
1 概念: 也是一个全局状态管理工具  //目前是比 mobx 更主流的, 状态管理工具
2 作用: 可以让全局组件之间实现数据共享
3 使用流程: 
    1 安装相关的包, 做一个环境
    2 主要是基于俩个文件: 全局状态管理的配置文件(涉及到分包) + 单文件组件中如何去使用

4 相关知识: 
  1 redux 中也是单向数据流的  //store 是只读的, 是通过 action 中的方法来修改的
  2 redux 中只支持 dispatch() 派发同步的 action, 而不能派发异步的 //例如不能调用 action 中的调接口
  3 dispatch() 的参数只能是一个对象
  4 react 的默认规范, 所有外部数据, 都因该是从 props 中进来的

5 关于解决 dispatch() 不能派发异步任务的处理方式: thunk 工具的作用  //根据下面的代码去理解
  1 dispatch(fn)  //thunk 会根据判断, fn=普通对象直接派发, fn不是对象 thunk会阻止派发
  2 dispatch(fn) 正常情况 fn不是对象就会报错, 有了 redux-thunk就不会报错了
  3 这样设置了之后, 如果 fn 是函数, 就会先调用这个方法, 然后再判断
  4 由于要在这个位置调接口, 为了便于管理和维护, 我们会把这些 fn 方法集合在一个文件中去使用
  5 新建目录 src/

  描述吧: 
    1 dispatch(fetch) 触发调接口时, 此时 fetch 不是普通对象, 处理插件 -- thunk
---------------------------------------------------------------------------

2 环境配置: 
---------------------------------------------------------------------------
1 需要安装的包: 
  npm i redux -S  //提供API
  npm i react-redux -S  //用于把 redux 配置文件 和 react 项目联系起来
  npm i redux-thunk -S  //用于解决 redux 只支持 dispatch() 配发同步 action, 的插件工具
  npm i axios -S  //用于请求数据

2 App.js 中  //mobx 也是这个配置方式
    import {Provider} from 'react-redux' 
    import store from './store/index.js'     
    <Provider store1={store}></Provider>   
    //用如上标签, 把 App.js 的视图结构包裹起来; 且被路由包裹(习惯); 自定义属性 store1

3 插件工具 redux-thunk 的环境配置: src/store/index.js 
  import thunk from 'redux-thunk'
  const store = createStore(reducer, applyMiddleware(thunk))  // + applyMiddleware(thunk) 参数
---------------------------------------------------------------------------

3 基础使用方式: 
---------------------------------------------------------------------------
1 创建状态管理文件: src/store/index.js 🔷

  import { //三个重要的 API
    createStore,  //用于创建, 什么仓库
    applyMiddleware,  //合并子store
    combineReducers   //使用中间键
  } from 'redux'
  import home from './modules/x1'  //子模块
  import thunk from 'redux-thunk'  //注意不要忘记安装

  const initState = {  // 1 定义数据
    msg1: 'hello redux'  //要共享的数据
  }  
  
  function reducer(state=initState, action) {  // 2 定义处理数据的方法 + 相关业务操作
    //action -> 业务操作 -> 格式: action = {type, payload}
    //type: 要执行怎么业务操作, 的一个标识
    //payload: 业务操作需要的参数
    let newState = JSON.parse(JSON.stringify(state))  //深复制, 解决渲染问题 (这种深复制的方法效率不太高)
    
    switch (action.type) {  // 未来会有很多的业务操作代码
      case 'add':
        // 如果 type == add, 我们就执行 add相关的业务操作
        break;
      case 'del':
        // 如果 type == del, 我们就执行 del相关的业务操作
        break;
      default: 
    }
    return newState  //返回新的 state
  }
  
  const store = createStore(reducer, applyMiddleware(thunk)) 
  export default store


2 单文件组件中怎么使用 Redux 全局状态呢 🔷
  import React from 'react'
  import {connect} from 'react-redux'  // connect(fn1, fn2)(UI) -> 高阶组件

  cnost A = props => {
    console.log(props.msg1)
    console.log(props.hh)
  }

  function fn1(store) {  //定义第一个方法
    //作用: 把当前组件要用到的全局状态数据, 映射进来, 让 props 可访问
    //参数 store: 是高阶组件 connect() 给的 -> store = 全局状态管理文件中定义数据的那个对象
    return { msg1: store.msg1}  //这样就可以通过 props 访问到数据 msg1 了
  }

  function fn2(dispatch) {  //定义第二个方法
    //作用: 把当前组件要用到的全局状态方法, 映射进来, 让 props 可调用  (可用于修改数据)
    //形参 dispatch: 是高阶组件 connect() 给的 -> 它是一个方法
    //做了哪些事: 
    //  1 设置 dispatch 的参数: 说明你要调用那个方法 + 传递的参数
    //  2 再把这个方法赋值给 hh属性, 让 props 来触发这个调用, 来执行设置在全局中的业务操作
    return { hh: ()=>dispatch({type: 'add', payload: 'hello 2021'})}
  }
  
  export default connect(fn1, fn2)(A)  //抛出组件, 注意使用 connect() 高阶组件


3 单文件函数式组件中, 还有一种使用方式(推介), 基于 hooks 的API 🔷

  import { 
    useDispatch,  //用于创建那个, 配发任务的函数的
    useSelector  //用于映射, 全局状态管理中的state
  } from 'react-redux'

  const A = props => {
    const msg = useSelector(store=>store.msg1)  //得到全局状态的 msg
    const dispatch = useDispatch()  //只支持派发, 同步的 action 任务 (比如修改数据啥的)
  }

  const ck1 = () => {  //定义一个触发事件, 去传参 + 调用, 全局状态中的 action 中的方法
    dispatch({type: 'add', payload: 'hello 2021'}) // 用于配发任务和数据给 action
  }

  export default A
---------------------------------------------------------------------------

4 React 中数据请求 API 的位置: src/    //注意提前安装和配置 redux-thunk 
---------------------------------------------------------------------------
1 说白了就是在, 单文件组件中编写请求数据的请求代码, 再把结果数据派发到, Redux 中去执行业务操作
2 而且会把, 这些请求的代码, 放在一个文件中集中管理
3 而且调接口调用并没有发生在组件里面, 二十发生在 thunk 插件代码里面

function h1(xx) {  // xx == 请求参数
  return function (dispatch) {
    //开始调接口的地方
    api1(xx).then(res=>{ //请求到数据, 然后基于数据, 去派发同步任务
      dispatch({
        type: 'add',
        payload: res.hh
      })
    })
  }
}

dispatch(h1)  //redux 在使用的时候, 直接调用函数名即可

暂时这么理解
---------------------------------------------------------------------------



4 标准的一个开发流程: 
---------------------------------------------------------------------------
2 创建状态子模块: src/store/moedules/x1.js
---------------------------------------------------------------------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值