文章目录
前言
前端小菜鸟的研究第一天记录:昨天刚刚将使用脚手架初始化的一个空项目扔上去了,想着总这么空着不好,于是决定开始慢慢填充内容,主要是填充一些自己平时的学习内容,有个地方记录并呈现效果。
1. 前期工作
(1)目录整理:目前的初始化的src文件目录比较杂乱,所以先按照开发习惯将src分了三个模块,分别是业务代码、静态资源和公共组件,目录层级看起来清爽了些。
(2)less- loader安装:因为看less比较习惯一点,所以将初始化时的css后缀全都改掉了,改完之后发现样式们都不生效了,研究了会发现是脚手架初始化出来的react项目默认不支持less,需要手动配置。
由于webpack配置上默认隐藏的,所以首先通过npm run eject
命令将其暴露如下图。
然后通过npm i less less-loader
安装less和less-loader。
最后就是配置到webpack.config.js了。
# 1. 可以参照sass的格式并在其附近添加下面这两句,用于匹配less文件的处理
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
# 2. 在getStyleLoaders函数中新增如下配置:
{
loader: require.resolve('less-loader')
}
# 3. 在module.rules中新增如下配置,依旧可以参照sass:
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'less-loader'
),
sideEffects: true,
},
{
test: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
},
'less-loader'
),
},
这么一通搞下来就差不多了。
2. 主角来了
React Router库包含三个不同的npm包,以下每个包都有不同的用途。
- react-router是核心库,用作另两个程序包的依赖
- react-router-dom是React应用中用于路由的软件包
- react-router-native用于开发React Native应用的绑定
2.1 路由创建
入口页面的路由创建如下,主要是从react-router-dom中导入了Routes、Route和HashRouter三个包。
import { Routes, Route, HashRouter as Router } from 'react-router-dom';
ReactDOM.render(
<Router>
<Routes>
<Route exact path='/' element={<HomePage />} />
</Routes>
</Router>,
document.getElementById('root')
);
- Routes:相对于V5 版本的一个变化,之前是使用
<switch />
组件来做的,它包括相对路由和链接、自动路由排名、嵌套路由和布局等功能。 - Route: 负责渲染React组件的UI,当匹配到path属性对应的url路径时渲染element对应的组件。与之前的
<Route />
相比,属性变化了,参数形式也变了。 - HashRouter:实现全局路由的切换。
# 既然说到这里,顺便接一下HashRouter和browserRouter路由两者的区别:
1.browserHistory 使用的是H5的History API,浏览器提供相应的接口来修改浏览器的历史记录;
而 hashHistory 是通过改变地址后面的 hash 来改变浏览器的历史记录;
2. History API 提供了 pushState() 和 replaceState() 方法来增加或替换历史记录。
hash 没有相应的方法,所以并没有替换历史记录的功能。(在hash模式下 ,history.push 底层是调用了window.location.href来改变路由。history.replace底层是掉用 window.location.replace改变路由。)
3. hash 部分不会被浏览器发送到服务端,也就是说不管是请求 http://domain.com/index.html#a 还是 http://domain.com/index.html#b ,服务只知道请求了 index.html 并不知道 hash 部分的细节。
而 History API 需要服务端支持,这样服务端能获取请求细节。
2.2 导航菜单的添加
React程序中直接使用a标签进行页面跳转,将导致在每次渲染新视图或页面本身时刷新web页面。为了避免刷新网页,react-router-dom 库提供了 Link 组件大概的用法如下
<Router>
<Link to="/" style={{ padding: 5 }}>
Home
</Link>
<Routes>
<Route exact path='/' element={<HomePage />} />
</Routes>
</Router>
该方法也会将该标签处理为<a>
,这样处理下点击对应的tab就会直接跳转到对应的组件,当然需要在上面的Routes中配置对应的路由信息。
function HomePage() {
const tabs = [{
id: 'homePage',
text: <Link to='/'>首页</Link>
}, {
id: 'cssDemo',
text: <Link to='/cssDemo'>CSS练手</Link>
}];
const [activeTab, setActiveTab] = useState('homePage');
return (
<div className="home-page">
<header className="page-header">
贝卡贝卡贝卡
</header>
<div className='home-body'>
我还没想好放啥,将就看吧。。
</div>
<Tab tabs={tabs} activeTab={activeTab} setActiveTab={setActiveTab} />
</div>
);
}
2.3 嵌套路由处理
当路由被嵌套时,一般认为网页的某一部分保持不变,只有网页的子部分发生变化。
在React Router v5中,必须明确定义嵌套路由,React Router v6并非如此。它从React Router库中挑选了一个名为 Outlet 的最佳元素,为特定路由呈现任何匹配的子元素。
在我的Css练手页面中确实比较需要这样的场景,所以根据大佬的文章自己在这边尝试配置一下。
# 1. 在该页面中引入outlet,我理解这里类似于一个占位,等待匹配对应的路由
import React from 'react';
import { Outlet } from 'react-router-dom';
function CssDemo() {
return (
<div className="css-demo">
<header className="demo-header">
Css Demo
</header>
{/* 渲染任何匹配的子级 */}
<Outlet />
</div>
);
}
export default CssDemo;
# 2. 列表组件如下:
function DemoList() {
const demoPosts = {
'1': {
title: 'Demo 1',
description: '练手学习的第一个css.'
},
'2': {
title: 'Demo 2',
description: '练手学习的第一个css'
}
};
return (
<div className="css-demo">
{
Object.entries(demoPosts).map(([slug, { title }]) => {
return <div key={slug}>
<span>{title}</span>
</div>
})
}
</div>
);
}
export default DemoList;
# 3.入口处添加如下配置
<Route path='cssDemo' element={<CssDemo />} >
<Route path="" element={<DemoList />} />
</Route>
效果展示如下图:
2.4 访问路由的URL参数和动态参数
列表的展示,已经初步实现了,接下来要做的便是点击对应的列表项能够进入对应的页面。将每个文章的标题包裹在 DemoLists 组件中的 Link 组件内。然后,使用每个文章的 slug 定义每个文章的路径,前缀为 /cssDemo/ 的文章在浏览器中的路径是一致的。
{
Object.entries(demoPosts).map(([slug, { title }]) => {
return <div key={slug}>
<Link to={`/cssDemo/${slug}`}>{title}</Link>
</div>
})
}
创建文章页面
import React from 'react';
import { useParams } from 'react-router-dom';
function Article() {
const articles = {
"1": {
title: "第一篇博客文章",
description: "第一篇哈哈哈哈"
},
"2": {
title: "第二篇博客文章",
description: "第二篇红红火火"
}
};
const { slug } = useParams();
const post = articles[slug];
const { title, description } = post;
return (
<div className='article'>
<h3>{title}</h3>
<p>{description}</p>
</div>
);
}
export default Article;
以上的路由使用操作,是根据这篇文章中的介绍来的,感觉很详细。
至此效果已基本达成,但还缺了一个重要的部分——返回
2.5 返回上一页
v5版本中的withRouter在v6中已被取消,取而代之的是更加方便的useNavigator
钩子,具体的用法如下。
import React from 'react';
import { Outlet, useNavigate } from 'react-router-dom';
function CssDemo() {
const navigate = useNavigate()
return (
<div className="css-demo">
<header className="demo-header" onClick={() => navigate(-1)}>
Css Demo
</header>
<Outlet />
</div>
);
}
# 常见的几种用法如下
navigate('/homePage')
navigate(-1) // 返回上一级
navigate(0, {replace: true})// 强制刷新当前页面并不加入路由历史