React进阶之路(四)-- React-router-v6、Mobx

news/2024/3/4 19:52:26

文章目录

  • 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-dom@6

我们以一个小案例为例:

需求: 准备俩个按钮,点击不同按钮切换不同组件内容的显示

在这里插入图片描述

实现步骤:

  1. 导入必要的路由router内置组件
  2. 准备俩个React组件
  3. 按照路由的规则进行路由配置
// 引入必要的内置组件
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'// 准备俩个路由组件const Home = () => <div>this is home</div>
const About = () => <div>this is about</div>function App() {return (<div className="App">{/* 按照规则配置路由,是一个非hash模式的路由 */}<BrowserRouter>{/* 指定跳转的组件,to用来配置路由地址 */}<Link to="/">首页</Link><Link to="/about">关于</Link><Routes><Route path="/" element={<Home />}></Route><Route 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编程的方式进行路由页面跳转,比如说从首页跳转到关于页

实现步骤:

  1. 导入一个 useNavigate 钩子函数
  2. 执行 useNavigate 函数 得到 跳转函数
  3. 在事件中执行跳转函数完成路由跳转
// 导入useNavigate函数
import { useNavigate } from 'react-router-dom'
const Home = () => {// 执行函数const navigate = useNavigate()return (<div>Home<button onClick={ ()=> navigate('/about') }> 跳转关于页 </button></div>)
}export default Home

注: 如果在跳转时不想添加历史记录,可以添加额外参数replace 为true

navigate('/about', { replace: true } )

路由传参

场景:跳转路由的同时,有时候要需要传递参数

searchParams传参

路由传参
在这里插入图片描述
路由取参
在这里插入图片描述

params传参

路由传参

在这里插入图片描述

在这里插入图片描述
在指定路由的时候,要先占个位!

路由取参
在这里插入图片描述

嵌套路由

场景:在我们做的很多的管理后台系统中,通常我们都会设计一个Layout组件,在它内部实现嵌套路由

在这里插入图片描述
实现步骤:
在这里插入图片描述

  1. App.js中定义嵌套路由声明
<Routes><Route path="/"  element={<Layout/>}><Route path="board" element={ <Board/> } /><Route path="article" element={ <Article/> } /></Route>{ /* 省略部分  */ }
</Routes>

在这里插入图片描述

  1. Layout组件内部通过 <Outlet/> 指定二级路由出口
import { Outlet } from 'react-router-dom'const Layout = () => {return (<div>layout{ /* 二级路由的path等于 一级path + 二级path  */ }<Link to="/board">board</Link><Link to="/article">article</Link>{ /* 二级路由出口 */ }<Outlet/></div>)
}
export default Layout

默认二级路由

场景: 应用首次渲染完毕就需要显示的二级路由

实现步骤:

  1. 给默认二级路由标记index属性
  2. 把原本的路径path属性去掉

代码实现:

<Routes><Route path="/"  element={<Layout/>}><Route index element={ <Board/> } /><Route path="article" element={ <Article/> } /></Route>
</Routes>
import { Outlet } from 'react-router-dom'const Layout = () => {return (<div>layout{ /* 默认二级不再具有自己的路径  */ }<Link to="/">board</Link><Link to="/article">article</Link>{ /* 二级路由出口 */ }<Outlet/></div>)
}

404路由配置

场景:当url的路径在整个路由配置中都找不到对应的path,使用404兜底组件进行渲染

首先我们准备一个NotFound组件

const NotFound = () => {return <div>this is NotFound</div>
}export default NotFound

然后将这个组件添加到声明当中的,作为兜底方法

<BrowserRouter><Routes><Route path="/" element={<Layout />}><Route index element={<Board />} /><Route path="article" element={<Article />} /></Route><Route path="*" element={<NotFound />}></Route></Routes>
</BrowserRouter>

集中式路由配置

场景: 当我们需要路由权限控制点时候, 对路由数组做一些权限的筛选过滤,所谓的集中式路由配置就是用一个数组统一把所有的路由对应关系写好,替换本来的Routes组件

import { BrowserRouter, Routes, Route, useRoutes } from 'react-router-dom'import 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 className="App"><BrowserRouter>{/* 3. 替换之前的Routes组件 */}<WrapperRoutes /></BrowserRouter></div>)
}export default App

Mobx

什么是Mobx

一个可以和React良好配合的集中状态管理工具,和Redux解决的问题相似,都可以独立组件进行集中状态管理

mobx和react的关系,相当于vuex和vue

同类工具还有:

  • redux
  • dva
  • recoil

在这里插入图片描述
优势:

  1. 简单:编写无模板的极简代码精准描述你的意图
  2. 轻松实现最优渲染:依赖自动追踪,实现最小渲染优化
  3. 架构自由:可移植, 可测试 无特殊心智负担

环境配置

Mobx是一个独立的响应式的库,可以独立于任何UI框架存在,但是通常大家习惯把它和React进行绑定使用,用Mobx来做响应式数据建模,React作为UI视图框架渲染内容,我们环境的配置需要三个部分

  1. 一个create-react-app创建好的React项目环境
  2. mobx框架本身
  3. 一个用来链接mobx和React的中间件
# 安装mobx和中间件工具 mobx-react-lite  只能函数组件中使用
$ yarn add  mobx  mobx-react-lite

基础使用

需求: 使用mobx实现一个计数器的案例

在这里插入图片描述

首先我们初始化mobx:

一般我们mobx的代码会写在store文件夹中:

在这里插入图片描述

初始化步骤

  1. 定义数据状态state
  2. 在构造器中实现数据响应式处理 makeAutoObservble
  3. 定义修改数据的函数action
  4. 实例化store并导出
import { makeAutoObservable } from 'mobx'class CounterStore {count = 0 // 定义数据constructor() {makeAutoObservable(this)  // 响应式处理}// 定义修改数据的方法addCount = () => {this.count++}
}const counter = new CounterStore()
export default counter

然后React使用store:

实现步骤

  1. 在组件中导入counterStore实例对象
  2. 在组件中使用storeStore实例对象中的数据
  3. 通过事件调用修改数据的方法修改store中的数据
  4. 让组件响应数据变化
// 导入counterStore
import counterStore from './store'
// 导入中间件连接mobx、react 完成响应式变化
import { observer } from 'mobx-react-lite'
function App() {return (<div className="App"><button onClick={() => counterStore.addCount()}>{counterStore.count}</button></div>)
}
// 包裹组件让视图响应数据变化
export default observer(App)

在原来我们的数据是react来管理的,所以数据的变化会引起模板的重新渲染,而现在我们将状态交给mobx来管理,并且改变状态的方法也是mobx中提供的,所以我们需要observer方法来包裹App跟组件,让视图响应数据变化。

observer函数*

observer函数是一个高阶组件(Higher-Order Component,HOC),它可以接收一个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的优化机制。

计算属性(衍生状态)

概念: 有一些状态根据现有的状态计算(衍生)得到,我们把这种状态叫做计算属性, 看下面的例子

在这里插入图片描述
实现步骤

  1. 声明一个存在的数据
  2. 通过get关键词 定义计算属性
  3. 在 makeAutoObservable 方法中标记计算属性(其实标记不标记都可以,只是为了可读性)
import { computed, makeAutoObservable } from 'mobx'class 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 counter

get使用来表明getter方法的关键字

// 导入counterStore
import counterStore from './store'
// 导入observer方法
import { observer } from 'mobx-react-lite'
function App() {return (<div className="App">{/* 原数组 */}{JSON.stringify(counterStore.list)}{/* 计算属性 */}{JSON.stringify(counterStore.filterList)}<button onClick={() => counterStore.changeList()}>change list</button></div>)
}
// 包裹组件让视图响应数据变化
export default observer(App)

异步数据处理

实现步骤:

  1. 在mobx中编写异步请求方法 获取数据 存入state中
  2. 组件中通过 useEffect + 空依赖 触发action函数的执行
// 异步的获取import { makeAutoObservable } from 'mobx'
import axios from 'axios'class 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 channlStore
import { 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)

模块化

场景: 一个项目有很多的业务模块,我们不能把所有的代码都写到一起,这样不好维护,提了提供可维护性,需要引入模块化机制

在这里插入图片描述
实现步骤

  1. 拆分模块js文件,每个模块中定义自己独立的state/action
  2. 在store/index.js中导入拆分之后的模块,进行模块组合
  3. 利用React的context的机制导出统一的useStore方法,给业务组件使用。(当然也可以直接导出,使用context导出的好处就是调试+依赖注入)

store/taskStore.js:

import { makeAutoObservable } from 'mobx'class TaskStore {taskList = []constructor() {makeAutoObservable(this)}addTask () {this.taskList.push('vue', 'react')}
}const task = new TaskStore()export default task

store/counterStore.js:

import { makeAutoObservable } from 'mobx'class 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 'react'import counter from './counterStore'
import task from './taskStore'class 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 className="App"><button 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 className="App"><Bar /><button onClick={() => taskStore.setTaskList('angular')}>修改taskStore</button></div>)
}
export default App

Mobx和React职责划分

Mobx和React的职责划分就是,Mobx负责管理应用的状态,React负责渲染应用的界面。你应该把那些需要跨组件共享或者响应变化的数据交给Mobx维护,比如用户的信息、购物车的内容、主题的设置等。你应该把那些只和组件自身相关或者不需要响应变化的数据交给React维护,比如表单的输入、组件的展开状态、动画的进度等。

在这里插入图片描述

  • 这里的业务状态数据就类似于从后端获得的数据,其余的我们都可以当作UI的临时状态。

http://www.ppmy.cn/news/1210495.html

相关文章

Postman的环境变量和全局变量

近期在复习Postman的基础知识&#xff0c;在小破站上跟着百里老师系统复习了一遍&#xff0c;也做了一些笔记&#xff0c;希望可以给大家一点点启发。 多种环境&#xff1a;开发环境、测试环境、预发布环境、生产环境&#xff0c;可以用环境变量来解决。 今天的分享就到这里&a…

LeetCode【701】二叉搜索数的插入操作

题目&#xff1a; 思路&#xff1a; https://zhuanlan.zhihu.com/p/272781596 代码&#xff1a; class Solution {public TreeNode insertIntoBST(TreeNode root, int val) {return process(root,val); }//递归public TreeNode process(TreeNode root,int val){//base ca…

基于Mahony互补滤波的IMU数据优化_学习笔记整理

这周自己被安排进行优化软件 IMU 姿态解算项目&#xff0c;之前自己只简单了解四元数&#xff0c;对IMU数据处理从未接触&#xff0c;通过这一周的学习感觉收获颇丰&#xff0c;在今天光棍节之际&#xff0c;&#xff0c;&#xff0c;用大半天的时间对这一周的收获进行整理&…

Mybatis教程

Mybatis教程 参考&#xff1a; MyBatis教程看这一篇就够啦&#xff0c;简单又全面&#xff08;IDEA版&#xff09; MyBatis详解 MyBatis(一文学会&#xff01;) MyBatis教程&#xff08;看这一篇就够了&#xff09; mybatis查询结果对象值为null的情况 mybatis查询结果对…

C++函数模板与内存管理详解

情景引入&#xff1a; 现在假设我们要写一个swap函数交换两个值的大小&#xff0c;我们知道在之前讲过的函数重载是支持不同类型的参数重载的&#xff0c;因此我们只要写出int ,double ,char ,float ,bool的类型函数重载就行了&#xff0c;但是这个函数的功能差距不大&#xf…

[autojs]逍遥模拟器和vscode对接

第一步&#xff1a;启动autojs服务 第二步&#xff1a;去cmd查看ip地址&#xff0c;输入ipconfig 第三步&#xff1a;打开逍遥模拟器中的sutojs-左上角- 连接电脑&#xff0c;然后输入WLAN或者其他ip也行&#xff0c;根据自己电脑实际情况确认 此时vscode显示连接成功。我们写…

Hls学习(一)

1&#xff1a;CPU、DSP、GPU都算软件可编程的硬件 2&#xff1a;dsp在递归方面有所减弱&#xff0c;在递归方面有所增强&#xff0c;比如递归啊等&#xff0c;GPU可以同时处理多个进程&#xff0c;对于大块数据&#xff0c;流处理比较适用 3&#xff1a;为了提高运算量处理更多…

C++ 编译与链接过程

案例讲解 有 main.cpp 和 add.cpp 2个文件&#xff0c;add.cpp中实现add_func函数&#xff0c;main.cpp文件中需要使用add_func函数。 demo&#xff1a; // main.cpp文件 #include <iostream>int add_func(int a, int b);int main() {int a 10;int b 10;int ret ad…

可视化 | 3D文字球状标签云

文章目录 &#x1f4da;改编点&#x1f4da;final 改编自echarts 3d词云&#xff08;指向滑动、拖动、缩放、点击、自转 &#xff09; &#x1f4da;改编点 背景透明&#xff1a;background:rgb(0,0,0,0);不用链接&#xff0c;用span&#xff0c;重点span标class"star&q…

如何选择一个可靠的爬虫代理服务商?技术人员都需要知道

我身边从事大数据相关行业的朋友最近告诉我&#xff0c;自己新招的小伙伴工作效率很低&#xff0c;很多最基础的工具都不会选择&#xff0c;经常因为代理IP不可靠导致工作出错。 听完这些我才意识到&#xff0c;在这个大数据时代&#xff0c;还是有很多新手在进行网络爬取任务…

如何将BMP图片批量转为PNG透明图片,并去掉BMP黑色背景

将BMP图片批量转为PNG透明图片&#xff0c;并去掉BMP黑色背景&#xff0c;这里推荐一款软件bmp2png&#xff0c;关键是免费的。截图如下&#xff1a; 这个小软件不仅可以将bmp图片批量转为png图片&#xff0c;而且还增加了压缩功能&#xff0c;导出png图片时压缩导出图片&#…

SwiftUI 如何保证 Text 中字符数量相等的字符串显示宽度一定相同?

0. 问题现象 在 SwiftUI 中我们往往需要将内容相似的字符串展列出来给用户比较,这些字符串内容各有不同但字符数量始终是相等的,我们希望它们的显示宽度始终保持一致: 如上图所示:即使是等宽字符组成的字符串在字符数量相等时它们的显示宽度仍然可能不一致。但演示中最底部…

14 # 手写 debounce 防抖方法

什么是防抖 防抖: n 秒后再去执行该事件&#xff0c;若在 n 秒内被重复触发&#xff0c;则重新计时&#xff0c;这个效果跟英雄联盟里的回城技能差不多。 本质上是优化高频率执行代码的一种手段&#xff0c;目的就是降低回调执行频率、节省计算资源。 应用场景&#xff1a; …

使用 pubsub-js 进行消息发布订阅

npm 包地址 github 包地址 pubsub-js 是一个轻量级的 JavaScript 基于主题的消息订阅发布库 &#xff0c;压缩后小于1b。它具有使用简单、性能高效、支持多平台等优点&#xff0c;可以很好地满足各种需求。 功能特点&#xff1a; 无依赖同步解耦ES3 兼容。pubsub-js 能够在…

王道数据结构第五章二叉树的遍历第18题

目录 题目描述 解题思路 宏定义 二叉树定义 实现函数 测试代码 测试结果

聊聊性能测试的左移右移

前面的文章中&#xff0c;分享过我对于测试左移右移的一些思考和实践方法。有同学在后台留言问我&#xff1a;常规的性能测试一般都是在测试阶段集成测试时候才开始介入&#xff0c;很容易测试时间不够&#xff0c;可不可以借鉴测试左移右移的思路&#xff0c;更早的介入和发现…

c语言实现哈夫曼编码

要实现哈夫曼编码&#xff0c;需要以下步骤&#xff1a; 统计字符出现的频率构建哈夫曼树遍历哈夫曼树&#xff0c;给不同的字符赋予不同的编码将编码后的字符写入文件中 下面是一个简单的 C 语言实现&#xff1a; #include <stdio.h> #include <stdlib.h> #inc…

【蓝桥杯 第十三届省赛Java B组】真题训练(A - F)

目录 A、星期计算 - BigInteger B、山 - 暴力判断 字符串 C、字符统计 - 简单哈希 D、最少刷题数 - 排序 思维 二分 分情况讨论 &#xff08;1&#xff09;&#xff08;错误&#xff09;自写哈希表 &#xff08;2&#xff09;正解 E、求阶乘 - 数学思维 二分 F、…

2023最新软件测试面试300问

一、Linux系统应用和环境配置 1、Linux系统的操作命令给我说10个&#xff0c;一般用什么工具远程连接Linux服务器&#xff1f; 2、Linux中的日志存储在哪里&#xff1f;怎么查看日志内容&#xff1f; 3、Linux中top和ps命令的区别&#xff1f; 4、Linux命令运行的结果如何写…

.one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复

导言&#xff1a; 随着科技的不断发展&#xff0c;网络犯罪也在不断进化&#xff0c;.one4-V-XXXXXXXX勒索病毒作为其中的一种威胁&#xff0c;对个人和企业的数据安全构成了巨大的风险。本文将深入介绍.one4-V-XXXXXXXX勒索病毒的特点、详细的数据恢复方法&#xff0c;以及全…
最新文章