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

news2025/1/16 11:06:50

文章目录

  • ReactRouter
    • 前置
    • 基本使用
    • 核心内置组件说明
    • 编程式导航
    • 路由传参
    • 嵌套路由
    • 默认二级路由
    • 404路由配置
    • 集中式路由配置
  • Mobx
    • 什么是Mobx
    • 环境配置
    • 基础使用
    • 计算属性(衍生状态)
    • 异步数据处理
    • 模块化
    • 多组件数据共享

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跟组件,让视图响应数据变化。

计算属性(衍生状态)

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

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

  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)
  }
  // 只要调用这个方法 就可以从后端拿到数据并且存入channelList
  setChannelList = 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 = 0
  list = [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 = counter
    this.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() {
  // 得到store
  const 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1192021.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HBuilderX 运行Android App项目至雷电模拟器

一、下载安装HBuilderX HBuildeX官网 安装最新的正式版&#xff0c;或者点击历史版本查看更多版本&#xff1b;【ps&#xff1a;Alpha版本为开发版&#xff0c;功能更多&#xff0c;但是也不稳定&#xff0c;属于测试版本】 直接将压缩包解压&#xff0c;运行HBuildeX即可。 二…

凯美瑞 vs 太空船:Web3 游戏生长的两条路径

撰文&#xff1a;Teng Yan&#xff08;0xPrismatic&#xff09;&#xff0c;Delphi Digital 研究员 编译&#xff1a;TinTinLand 来源&#xff1a;https://0xprismatic.substack.com/p/my-short-web3-gaming-thesis 经常有人问我关于 Web3 游戏的看法&#xff0c;所以我想以这…

文本生成高精准3D模型,北京智源AI研究院等出品—3D-GPT

北京智源AI研究院、牛津大学、澳大利亚国立大学联合发布了一项研究—3D-GPT&#xff0c;通过文本问答方式就能创建高精准3D模型。 据悉&#xff0c;3D-GPT使用了大语言模型的多任务推理能力,通过任务调度代理、概念化代理和建模代理三大模块&#xff0c;简化了3D建模的开发流程…

vmware16.2内部win7联网

1、主机配置 前置条件&#xff1a;DHCP和NAT服务已启动 设置无线IP与虚拟机IP为自动获取 二者都是&#xff1a;右键-属性 选择IPv4 自动获取 2、虚拟机配置 设置虚拟机上网方式为NAT 菜单栏-虚拟机-设置 NMnet8改为NAT模式 菜单栏-编辑-虚拟网络编辑器 win7系统内部网…

Facebook广告被暂停是什么原因?广告账号被封怎么办?

许多做海外广告投放的小伙伴经常遇到一个难题&#xff0c;那就是投放的Facebook广告被拒或广告帐户被关闭赞停的经历&#xff0c;随之而来的更可能是广告账户被封&#xff0c;导致资金的损失。本文将从我自身经验&#xff0c;为大家分享&#xff0c;FB广告被暂停的原因有哪些&a…

Java多线程interrupt、interrupted、isInterrupted详解

一、概念 1.1 interrupt方法应用场景 用来打断正在阻塞的线程&#xff1a;sleep/wait/join打断正常的线程 1.2 interrupt() 方法 Thread类的实例方法&#xff0c;其作用是中断此线程&#xff08;此线程不一定是当前线程&#xff0c;而是指调用该方法的Thread实例所代表的线程…

【手写模拟Spring底层原理】

文章目录 模拟Spring底层详解1、结合配置类&#xff0c;扫描类资源1.1、创建需要扫描的配置类AppConfig&#xff0c;如下&#xff1a;1.2、创建Spring容器对象LyfApplicationContext&#xff0c;如下1.3、Spring容器对象LyfApplicationContext扫描资源 2、结合上一步的扫描&…

oracle11G在linux环境下的卸载操作

1.使用SQL*PLUS停止数据库 [oracleOracleTest oracle]$ sqlplus / as sysdba SQL> shutdown [immediate] SQL> exit2.停止Listener [oracleOracleTest oracle]$ lsnrctl stop3.停止HTTP服务 [rootOracleTest /root]# service httpd stop4.用su或者重新登录到root(如想…

bootstrap-fileinput拦截文件上传处理失败,根据后台返回数据处理

bootstrap-fileinput如何拦截后台数据&#xff0c;自定义处理业务逻辑 需要后台返回error字段&#xff0c;失败示例&#xff0c;注意&#xff1a;error必须有内容&#xff0c;不然默认也是成功&#xff0c; bootstrap-fileinput失败验证只需要 error 字段&#xff0c;其他附加…

【MySQL习题】各个视频的平均完播率【全网最详细教学】

目录 数据表描述 问题描述 输出示例 解题思路【重点】 正解代码 数据表描述 有以下两张表&#xff1a; 表1&#xff1a;用户-视频互动表tb_user_video_log 数据举例&#xff1a; 说明&#xff1a; uid-用户ID,video_id-视频ID start_time-开始观看时间end_time-结束观…

微信小程序报request:fail url not in domain list的解决方法

情况1&#xff1a;未设置合法域名 解决方法:请在微信公众平台登录小程序后台 > 开发管理 > 开发设置 > 服务器域名 情况2&#xff1a;设置了合法域名&#xff0c;开发工具仍然报错 解决方法: 在右上角点击详情&#xff0c;之后刷新一下项目配置&#xff0c;看看有…

一篇博客读懂单链表——Single-List

目录 一、初识单链表 单链表是如何构造的&#xff1a; 单链表如何解决顺序表中的问题&#xff1a; 二、单链表的初始定义 三、尾插和头插 3.1 新建结点CreateNode 3.2 打印SLTPrint 3.3 尾插SLTPushBack 3.4 头插SLTPushFront 四、尾删和头删 4.1 尾删SLTPopBack…

容器数据卷+MYSQL实战

什么是容器数据卷&#xff1f; 让我们回忆一下docker理念&#xff1a; 就是将应用和环境打包成一个镜像 数据&#xff1f; 如果数据都在容器中&#xff0c;那么我们删除容器&#xff0c;数据就会丢失 &#xff01;需求&#xff1a;数据持久化就完美了 对于MYSQL&#xff0…

玄子Share-Git 入门手册

玄子Share-Git 入门手册 简单介绍 Git Git 是一个自由和开源的分布式版本控制系统&#xff0c;旨在快速和高效地处理从小型到大型的所有项目 Git 简单易学&#xff0c;占用空间小&#xff0c;性能快如闪电。它比Subversion、CVS、Perforce和ClearCase等SCM工具更有优势&…

python默认的输入类型是字符串,怎样转换为其他的类型

在Python中&#xff0c;默认的输入类型是字符串&#xff08;str类型&#xff09;。无论你输入的是数字、字符还是其他类型的内容&#xff0c;input函数都会将其作为字符串处理并返回。 如果需要将字符串转换为其他类型&#xff08;如整数、浮点数等&#xff09;&#xff0c;可…

kafka和rocketMq的区别

kafka topic 中每一个分区会有 Leader 与 Follow。Kafka 的内部机制可以保证 topic 某一个分区的 Leader 与 Follow 不在同一台机器上 Leader 节点承担一个分区的读写&#xff0c;Follow 节点只负责数据备份 如果 Leader 分区所在的 Broker 节点宕机&#xff0c;会触发主从节…

Linux 的热插拔机制通过 Udev(用户空间设备)实现、守护进程

一、Udev作用概述 udev机制简介udev工作流程图 二、Linux的热拔插UDEV机制 三、守护进程 守护进程概念守护进程在后台运行基本特点 四、守护进程和后台进程的区别 一、Udev作用概述 udev机制简介 Udev&#xff08;用户空间设备&#xff09;是一个 Linux 系统中用于动态管…

玄子Share-HTML5知识手册

玄子Share-HTML5知识手册 前言&#xff1a; 这一版 HTML 笔记&#xff0c;算是我写的第四版了&#xff0c;第三版对照课本编写&#xff0c;第四版则是对照 MDN 官方文档编写&#xff0c;不论是术语亦或专业性&#xff0c;都更上一层 文章依托 MDN 文档&#xff0c;拓展了大量课…

【2023-11-09】git使用随记——gitignore文件配置某些文件忽略

git使用随记——gitignore文件配置某些文件忽略 通过git进行版本控制在项目中是非常常见的&#xff0c;一些项目构建上的文件通常是不需要进行版本控制的&#xff0c;也就无需推送到git仓库中&#xff0c;比如前端项目中的node_module目录。提供配置.gitignore文件 但是某些情…

腾讯云88,阿里云99,现在都这么卷了吗?!

你是否曾经想过&#xff0c;云服务器的价格竟然可以如此亲民&#xff1f;现在&#xff0c;腾讯云和阿里云竟然都推出了超低价位的云服务器&#xff0c;只要88元和99元&#xff01;这让我们这些自媒体人、创业者、开发者等都感到非常惊喜。可以看一下配置和价格&#xff1a; 可…