上海建网站服务器,做网站的服务器带宽一般多少,网站建设源码,做网站和seo哪个好文章目录 ReactRouter前置基本使用核心内置组件说明编程式导航路由传参嵌套路由默认二级路由404路由配置集中式路由配置 Mobx什么是Mobx环境配置基础使用observer函数*计算属性#xff08;衍生状态#xff09;异步数据处理模块化多组件数据共享Mobx和React职责划分 ReactRout… 文章目录 ReactRouter前置基本使用核心内置组件说明编程式导航路由传参嵌套路由默认二级路由404路由配置集中式路由配置 Mobx什么是Mobx环境配置基础使用observer函数*计算属性衍生状态异步数据处理模块化多组件数据共享Mobx和React职责划分 ReactRouter
前置
在一开始前端开发都是单页应用也就是只有一个html文件。后来主流的开发模式变成了通过路由进行页面切换。这样做的优势就是避免整体页面刷新 用户体验变好。缺点就是前端负责事情变多了 开发的难度变大。
路由的本质是什么
路由的概念来源于后端 : 一个路径表示匹配一个服务器资源例如
/a.html - a对应的文件资源/b.html - b对应的文件资源
共同的思想: 一对一的关系
前端的路由: 一个路径path对应唯一的一个组件comonent 当我们访问一个path 自动把path对应的组件进行渲染
const routes [{path:/home,component: Home},{path:/about,component: About},{path:/article,component: Article}
]基本使用
首先安装依赖
yarn add react-router-dom6我们以一个小案例为例
需求: 准备俩个按钮点击不同按钮切换不同组件内容的显示 实现步骤
导入必要的路由router内置组件准备俩个React组件按照路由的规则进行路由配置
// 引入必要的内置组件
import { BrowserRouter, Routes, Route, Link } from react-router-dom// 准备俩个路由组件const Home () divthis is home/div
const About () divthis is about/divfunction App() {return (div classNameApp{/* 按照规则配置路由是一个非hash模式的路由 */}BrowserRouter{/* 指定跳转的组件to用来配置路由地址 */}Link to/首页/LinkLink to/about关于/LinkRoutesRoute path/ element{Home /}/RouteRoute path/about element{About /}/Route/Routes/BrowserRouter/div)
}export default App核心内置组件说明
BrowerRouter组件
作用: 包裹整个应用一个React应用只需要使用一次
Hash路由和history路由是两种前端路由的实现方式它们的区别主要有以下几点
Hash路由是一种把前端路由的路径用井号 # 拼接在真实 URL 后面的模式。当井号 # 后面的路径发生变化时浏览器并不会向服务器发送请求而是根据 hash 值的变化来更新页面内容。History路由是一种利用 HTML5 的 history API 来实现的路由模式。它可以通过 pushState 和 replaceState 方法来修改浏览器的历史记录从而改变 URL 的显示同时不会触发页面的刷新。Hash路由相比于 history 路由有以下几个缺点 Hash路由的 URL 较丑有一个多余的 # 符号。Hash路由原本是用来做页面定位的如果用来做路由的话原来的锚点功能就不能用了。Hash路由的传参是基于 URL 的如果要传递复杂的数据会有体积的限制而 history 路由不仅可以在 URL 里放参数还可以将数据存放在一个特定的对象中。Hash路由设置的新值必须与原来不一样才会触发记录添加到栈中而 history 路由可以设置与当前 URL 一模一样的新 URL这样也会把记录添加到栈中。
Link组件
作用: 用于指定导航链接完成声明式的路由跳转 类似于 router-link/ 这里to属性用于指定路由地址表示要跳转到哪里去Link组件最终会被渲染为原生的a链接
Routes组件
作用: 提供一个路由出口组件内部会存在多个内置的Route组件满足条件的路由会被渲染到组件内部 什么是路由出口 路由出口是一个用于在页面中显示路由组件的标签它可以让你在不同的位置展示不同的内容根据路由的变化而变化。路由出口有以下几个特点 你可以在一个页面中使用多个路由出口只要给它们不同的名字就可以实现复杂的布局效果。你可以在路由出口中嵌套其他的路由出口以实现多级的路由导航。你可以在路由出口中使用路由守卫以实现对路由的控制和拦截。 Route组件
作用: 用于定义路由路径和渲染组件的对应关系 [element因为react体系内把组件叫做react element] 其中path属性用来指定匹配的路径地址element属性指定要渲染的组件图中配置的意思为: 当url上访问的地址为 /about 时当前路由发生匹配对应的About组件渲染
编程式导航
声明式 【 Link to】 vs 编程式 【调用路由方法进行路由跳转】
概念: 通过js编程的方式进行路由页面跳转比如说从首页跳转到关于页
实现步骤
导入一个 useNavigate 钩子函数执行 useNavigate 函数 得到 跳转函数在事件中执行跳转函数完成路由跳转
// 导入useNavigate函数
import { useNavigate } from react-router-dom
const Home () {// 执行函数const navigate useNavigate()return (divHomebutton onClick{ () navigate(/about) } 跳转关于页 /button/div)
}export default Home注: 如果在跳转时不想添加历史记录可以添加额外参数replace 为true
navigate(/about, { replace: true } )路由传参
场景跳转路由的同时有时候要需要传递参数
searchParams传参
路由传参 路由取参
params传参
路由传参 在指定路由的时候要先占个位
路由取参
嵌套路由
场景在我们做的很多的管理后台系统中通常我们都会设计一个Layout组件在它内部实现嵌套路由 实现步骤
App.js中定义嵌套路由声明
RoutesRoute path/ element{Layout/}Route pathboard element{ Board/ } /Route patharticle element{ Article/ } //Route{ /* 省略部分 */ }
/RoutesLayout组件内部通过 Outlet/ 指定二级路由出口
import { Outlet } from react-router-domconst Layout () {return (divlayout{ /* 二级路由的path等于 一级path 二级path */ }Link to/boardboard/LinkLink to/articlearticle/Link{ /* 二级路由出口 */ }Outlet//div)
}
export default Layout默认二级路由
场景: 应用首次渲染完毕就需要显示的二级路由
实现步骤:
给默认二级路由标记index属性把原本的路径path属性去掉
代码实现
RoutesRoute path/ element{Layout/}Route index element{ Board/ } /Route patharticle element{ Article/ } //Route
/Routesimport { Outlet } from react-router-domconst Layout () {return (divlayout{ /* 默认二级不再具有自己的路径 */ }Link to/board/LinkLink to/articlearticle/Link{ /* 二级路由出口 */ }Outlet//div)
}404路由配置
场景当url的路径在整个路由配置中都找不到对应的path使用404兜底组件进行渲染
首先我们准备一个NotFound组件
const NotFound () {return divthis is NotFound/div
}export default NotFound然后将这个组件添加到声明当中的作为兜底方法
BrowserRouterRoutesRoute path/ element{Layout /}Route index element{Board /} /Route patharticle element{Article /} //RouteRoute path* element{NotFound /}/Route/Routes
/BrowserRouter集中式路由配置 场景: 当我们需要路由权限控制点时候, 对路由数组做一些权限的筛选过滤所谓的集中式路由配置就是用一个数组统一把所有的路由对应关系写好替换本来的Routes组件 import { BrowserRouter, Routes, Route, useRoutes } from react-router-domimport Layout from ./pages/Layout
import Board from ./pages/Board
import Article from ./pages/Article
import NotFound from ./pages/NotFound// 1. 准备一个路由数组 数组中定义所有的路由对应关系
const routesList [{path: /,element: Layout /,children: [{element: Board /,index: true, // index设置为true 变成默认的二级路由},{path: article,element: Article /,},],},// 增加n个路由对应关系{path: *,element: NotFound /,},
]// 2. 使用useRoutes方法传入routesList生成Routes组件
function WrapperRoutes() {let element useRoutes(routesList)return element
}function App() {return (div classNameAppBrowserRouter{/* 3. 替换之前的Routes组件 */}WrapperRoutes //BrowserRouter/div)
}export default AppMobx
什么是Mobx
一个可以和React良好配合的集中状态管理工具和Redux解决的问题相似都可以独立组件进行集中状态管理
mobx和react的关系相当于vuex和vue
同类工具还有
reduxdvarecoil 优势
简单编写无模板的极简代码精准描述你的意图轻松实现最优渲染依赖自动追踪实现最小渲染优化架构自由可移植, 可测试 无特殊心智负担
环境配置
Mobx是一个独立的响应式的库可以独立于任何UI框架存在但是通常大家习惯把它和React进行绑定使用用Mobx来做响应式数据建模React作为UI视图框架渲染内容我们环境的配置需要三个部分
一个create-react-app创建好的React项目环境mobx框架本身一个用来链接mobx和React的中间件
# 安装mobx和中间件工具 mobx-react-lite 只能函数组件中使用
$ yarn add mobx mobx-react-lite基础使用
需求: 使用mobx实现一个计数器的案例 首先我们初始化mobx
一般我们mobx的代码会写在store文件夹中 初始化步骤
定义数据状态state在构造器中实现数据响应式处理 makeAutoObservble定义修改数据的函数action实例化store并导出
import { makeAutoObservable } from mobxclass CounterStore {count 0 // 定义数据constructor() {makeAutoObservable(this) // 响应式处理}// 定义修改数据的方法addCount () {this.count}
}const counter new CounterStore()
export default counter然后React使用store
实现步骤
在组件中导入counterStore实例对象在组件中使用storeStore实例对象中的数据通过事件调用修改数据的方法修改store中的数据让组件响应数据变化
// 导入counterStore
import counterStore from ./store
// 导入中间件连接mobx、react 完成响应式变化
import { observer } from mobx-react-lite
function App() {return (div classNameAppbutton onClick{() counterStore.addCount()}{counterStore.count}/button/div)
}
// 包裹组件让视图响应数据变化
export default observer(App)在原来我们的数据是react来管理的所以数据的变化会引起模板的重新渲染而现在我们将状态交给mobx来管理并且改变状态的方法也是mobx中提供的所以我们需要observer方法来包裹App跟组件让视图响应数据变化。 observer函数*
observer函数是一个高阶组件Higher-Order ComponentHOC它可以接收一个React组件作为参数并返回一个新的React组件这个新的组件会自动订阅Mobx中的可观察数据并在数据变化时重新渲染。
observer函数应该包裹那些需要响应Mobx中的数据变化的组件通常是那些展示数据或者处理用户交互的组件。
我们可能会有一种猜想我们直接使用observer函数包裹最外层的组件App是不是就可以了这样当App组件重新渲染的时候也会把子组件连带着一起重新渲染了
这种想法大错特错
App组件重新渲染了子组件也不一定会重新渲染这取决于子组件是否接收了来自App组件的props以及这些props是否发生了变化。子组件只有在它的props或者state发生变化时才会重新渲染。如果子组件只是依赖于Mobx中的数据而不是App组件传递的props那么它就不会因为App组件的重新渲染而重新渲染除非它也使用了observer函数来订阅Mobx中的数据。子组件包含在App组件中只是表示它是App组件的子节点它并不会自动继承App组件的props或者响应App组件的更新。你可以把这个过程想象成一个树形结构每个组件都是一个节点每个节点都有自己的数据和渲染逻辑只有当节点的数据发生变化时它才会重新渲染自己和它的子节点。
如果你想让子组件跟随App组件的更新而更新你可以有以下几种方法
在App组件中使用observer函数并且把Mobx中的数据通过props传递给子组件这样子组件就会根据props的变化而重新渲染。在子组件中也使用observer函数并且直接从Mobx中获取数据这样子组件就会根据Mobx中的数据变化而重新渲染。在App组件中使用forceUpdate方法强制App组件和它的子组件重新渲染但这种方法不推荐使用因为它会破坏React的优化机制。
计算属性衍生状态
概念: 有一些状态根据现有的状态计算衍生得到我们把这种状态叫做计算属性, 看下面的例子 实现步骤
声明一个存在的数据通过get关键词 定义计算属性在 makeAutoObservable 方法中标记计算属性其实标记不标记都可以只是为了可读性
import { computed, makeAutoObservable } from mobxclass CounterStore {list [1, 2, 3, 4, 5, 6]constructor() {makeAutoObservable(this, {filterList: computed})}// 修改原数组changeList () {this.list.push(7, 8, 9)}// 定义计算属性get filterList () {return this.list.filter(item item 4)}
}const counter new CounterStore()export default counterget使用来表明getter方法的关键字 // 导入counterStore
import counterStore from ./store
// 导入observer方法
import { observer } from mobx-react-lite
function App() {return (div classNameApp{/* 原数组 */}{JSON.stringify(counterStore.list)}{/* 计算属性 */}{JSON.stringify(counterStore.filterList)}button onClick{() counterStore.changeList()}change list/button/div)
}
// 包裹组件让视图响应数据变化
export default observer(App)异步数据处理
实现步骤:
在mobx中编写异步请求方法 获取数据 存入state中组件中通过 useEffect 空依赖 触发action函数的执行
// 异步的获取import { makeAutoObservable } from mobx
import axios from axiosclass ChannelStore {channelList []constructor() {makeAutoObservable(this)}// 只要调用这个方法 就可以从后端拿到数据并且存入channelListsetChannelList async () {const res await axios.get(http://geek.itheima.net/v1_0/channels)this.channelList res.data.data.channels}
}
const channlStore new ChannelStore()
export default channlStoreimport { useEffect } from react
import { useStore } from ./store
import { observer } from mobx-react-lite
function App() {const { channlStore } useStore()// 1. 使用数据渲染组件// 2. 触发action函数发送异步请求useEffect(() {channlStore.setChannelList()}, [])return (ul{channlStore.channelList.map((item) (li key{item.id}{item.name}/li))}/ul)
}
// 让组件可以响应数据的变化[也就是数据一变组件重新渲染]
export default observer(App)模块化
场景: 一个项目有很多的业务模块我们不能把所有的代码都写到一起这样不好维护提了提供可维护性需要引入模块化机制 实现步骤
拆分模块js文件每个模块中定义自己独立的state/action在store/index.js中导入拆分之后的模块进行模块组合利用React的context的机制导出统一的useStore方法给业务组件使用。当然也可以直接导出使用context导出的好处就是调试依赖注入
store/taskStore.js
import { makeAutoObservable } from mobxclass TaskStore {taskList []constructor() {makeAutoObservable(this)}addTask () {this.taskList.push(vue, react)}
}const task new TaskStore()export default taskstore/counterStore.js
import { makeAutoObservable } from mobxclass CounterStore {count 0list [1, 2, 3, 4, 5, 6]constructor() {makeAutoObservable(this)}addCount () {this.count}changeList () {this.list.push(7, 8, 9)}get filterList () {return this.list.filter(item item 4)}
}const counter new CounterStore()export default counter组合模块导出统一方法
index.js
import React from reactimport counter from ./counterStore
import task from ./taskStoreclass RootStore {constructor() {this.counterStore counterthis.taskStore task}
}const rootStore new RootStore()// context机制的数据查找链 Provider如果找不到 就找createContext方法执行时传入的参数
const context React.createContext(rootStore)const useStore () React.useContext(context)
// useStore() rootStore { counterStore, taskStore }export { useStore }这个地方直接导出rootstore也是可以的。
接下来我们就来使用
import { observer } from mobx-react-lite
// 导入方法
import { useStore } from ./store
function App() {// 得到storeconst store useStore()//这个地方我们可以直接解构赋值想用哪一个就解构哪一个例如//const {counterStore} useStore()return (div classNameAppbutton onClick{() store.counterStore.addCount()}{store.counterStore.count}/button/div)
}
// 包裹组件让视图响应数据变化
export default observer(App)多组件数据共享
目标当数据发生变化所有用到数据的组件都会得到同步的组件的更新
实现步骤在Foo组件和Bar组件中分别使用store中的数据然后在app组件中进行数据修改查看Foo组件和Bar组件是否得到更新 Bar.js
// 用taskStore中的taskList数据
import { useStore } from ./store
import { observer } from mobx-react-lite
const Bar () {const { taskStore } useStore()return (ul{taskStore.taskList.map((item) (li{item}/li))}/ul)
}export default observer(Son)Foo.js
// 用taskStore中的taskList数据
import { useStore } from ./store
import { observer } from mobx-react-lite
const Bar () {const { taskStore } useStore()return (ul{taskStore.taskList.map((item) (li{item}/li))}/ul)
}export default observer(Son)App.js
import Bar from ./Bar
import Foo from ./Foo
import { useStore } from ./store
function App() {const { taskStore } useStore()return (div classNameAppBar /button onClick{() taskStore.setTaskList(angular)}修改taskStore/button/div)
}
export default AppMobx和React职责划分
Mobx和React的职责划分就是Mobx负责管理应用的状态React负责渲染应用的界面。你应该把那些需要跨组件共享或者响应变化的数据交给Mobx维护比如用户的信息、购物车的内容、主题的设置等。你应该把那些只和组件自身相关或者不需要响应变化的数据交给React维护比如表单的输入、组件的展开状态、动画的进度等。 这里的业务状态数据就类似于从后端获得的数据其余的我们都可以当作UI的临时状态。