跳到主要内容位置
Hello! 我是潜心专研的小张同学

在这里,有技术博客、以及UP主的想法和生活点滴。授人以鱼不如授人以渔,分享个人所学所见,在你学习前端开发的路上助你一臂之力!

QQ 1 群:706947563

去B站关注

最新博客 

router.go()妙用

1 背景#

最近在做动态路由相关的功能实现,涉及到一些页面跳转的功能,我们都知道返回上一页用的是 router.go(-1),它的意思就是说返回上一个路由地址。

但是,现在有这么一个场景(技术栈:vue3+"vue-router": "^4.1.6"),比如原来我是在路由地址 A,然后我点击了路由 B,我会在前置路由钩子里面先检测当前访问的路由地址 B,如果 B 没有权限访问那么就会默认跳转地址 C(这个是 404 页面),在地址 C 的 404 页面里面有个返回上一页按钮。

我给返回上一页按钮写的事件如下:

<script setup lang="ts">
import { useRouter } from 'vue-router'
import { House } from '@element-plus/icons-vue'
const router = useRouter()
const goIndex = () => {
router.go(-1)
}
</script>
<template>
<div class="container">
<img src="../assets/picture/common/404.png" alt="" class="img" />
<div class="text">当前页面不存在</div>
<el-link type="primary" :icon="House" @click="goIndex">回到上一页</el-link>
</div>
</template>

可是呀,事实情况总是事与愿违,但我执行router.go(-1) 这句话的时候并不能直接返回我原来的路由地址(A 地址),而是返回了那个没有权限访问的 B 地址(尽管页面没有真正跳转到 B 地址,但是路由地址里面会显示),而且再点击一下才会真正返回到想要的上一页路由地址 A 页面。

显然,这不是我想要的,那咋办呢,请看我下面的实现:

2 突发奇想的实现#

我想既然跳转一次不行,那我跳转 2 次呢,实现如下:

const goIndex = () => {
router.go(-1)
router.go(-1)
}

哈哈,我运行了上面这段代码,显然不行,于是乎,我又改成了下面这样,

const goIndex = () => {
router.go(-2)
}

果然,将-1 改成了-2,就可以直接跳转到前两页的位置,竟然就是这么简单!

3 小结#

果然还是得实践。

vue3+element puls upload组件回显图片base64的实现

1 背景#

最近遇到个需求,需要基于 vue3+element plus 的 upload 组件回显图片,通常我们是通过后端直接返回的 url 来回显就行了,而且在 element plus 也给出了示例:

image-20240123171340359

不过,o_O,我们下面将要以 base64 的形式来填充,其实也很简单,自己构造一个这样的对象就行了,url 里面放我们 base64 字符串,然后其他的造成即可,下面请看我的实现样例:

2 实现#

image-20240126173506038

  • 构造一个用 base64 字符串填充的文件列表
let fileList = ref([
{
// 这是文件名字
name: '文件名1',
// 这里是我自己定义的自定义属性,可有可无
fileId: '1',
// 这里是base64字符串,咳咳我们后端返回的格式有点特殊,所以我又给转换了一下,成为真正的base64字符串
url: 'data:image/png;base64,' + new BaseTool().arrayBufferToBase64(temp?.arrayBuffer)
}
])
  • 将图片列表渲染到组件里
<el-upload action="#" list-type="picture-card" v-model:file-list="fileList" :auto-upload="false">
<el-icon><Plus /></el-icon>
<template #file="{ file }">
<div>
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<el-icon><zoom-in /></el-icon>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file, item.dataValue)"
>
<el-icon><Delete /></el-icon>
</span>
</span>
</div>
</template>
</el-upload>

重点就是替换上面 fileList 就可。

3 小结#

ok 啦。

vue3+element plus实现查询条件展开和收起功能

1 需求来源#

image-20231129134425954

如图所示,这样一个查询页面,上面的条件太多,使得下面的列表展示的空间就变得很小了。所以,需要有一个东西控制,当条件太多时,就展示一个展开/收起按钮,可以控制查询条件的展开和收起。

2 实现效果图#

我们先直接来看下最终实现的效果图

screenshots

如果这就是你想要的,可以继续看下面的实现关键代码

3 具体实现关键代码#

<script setup lang="ts">
const conditionFold = ref(true)
const conditionInitShowLength = 6
const areConditionFold = () => {
conditionFold.value = !conditionFold.value
}
</script>
<template>
<div class="header customDiv">
<el-form ref="formRef" :inline="true" :model="formDataConfig" class="demo-form-inline">
<el-row>
<el-col :span="6">
<el-form-item label="查询逻辑">
<el-select v-model="filtersLogic" placeholder="默认同时符合">
<el-option label="同时符合" :value="0" />
<el-option label="部分符合" :value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col
:span="6"
v-for="(item, index) in formDataConfig.slice(
0,
conditionFold ? conditionInitShowLength : formDataConfig.length
)"
:key="item.key"
>
<el-form-item :label="item.label" :prop="`[${index}]value`">
<el-date-picker
v-if="item.type === 'date'"
v-model="item.value"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm:ss"
date-format="YYYY/MM/DD ddd"
time-format="A hh:mm:ss"
/>
<el-input
v-else-if="item.type === 'input' || !item.type"
v-model="item.value"
placeholder="请输入"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item>
<el-button type="primary" @click="onSubmit(formRef)">查询</el-button>
<el-button type="primary" @click="resetForm(formRef)">重置</el-button>
<el-button
v-if="formDataConfig.length > conditionInitShowLength"
type="primary"
link
@click="areConditionFold"
>
{{ conditionFold ? '展开' : '收起' }}
<el-icon v-if="conditionFold"><ArrowDown /></el-icon>
<el-icon v-else><ArrowUp /></el-icon>
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>

ok,代码就是上面的代码,可能少了一些变量,但是思路还是挺清晰的,主要就是通过conditionFold来控制按钮是否展示,conditionInitShowLength来控制收起时显示的默认长度。

4 技术小结#

当我们使用“展开/收起”按钮时,需要搭配row、col,列数需要是固定的,不然我们不知道“展开/收起”按钮展示及切换的时机。

vue3+element plus图片预览直接点击按钮就显示图片的预览形式

1 需求#

直接上需求:

我想要直接点击下面这个“预览”按钮,然后呈现出预览图片的形式

image-20231120090930791

也就是点击完“预览”按钮,会像下面这样:

image-20231120091054028

ok,需求知道了,下面让我们来看看如何实现吧 ~

2 实现#

template 部分

<el-button type="primary" size="small" @click="handlePreview(scope.$index, scope.row)">预览</el-button>
<!-- 图片预览 -->
<el-image-viewer v-if="showImagePreview" :zoom-rate="1.2" @close="closePreview" :url-list="imgPreviewList" />

script 部分

const imgPreviewList = ref < any > []
const showImagePreview = ref(false)
const currentBase64FileData = reactive({
base64: '',
name: ''
})
const handlePreview = async (index: number, row: any) => {
let res = await handleDownload(index, row, true)
currentBase64FileData.base64 = 'data:image/png;base64,' + res?.base64
currentBase64FileData.name = res?.name
showImagePreview.value = true
// 下面数组里可以放一个url,如'https://raw.githubusercontent.com/JACK-ZHANG-coming/map-depot/master/2023image-20231120091054028.png',我这里放的是一个base64数据,也可以用来显示图片
imgPreviewList.value = [currentBase64FileData.base64]
}
const closePreview = () => {
imgPreviewList.value = []
showImagePreview.value = false
}

ok,经过上面简单几句代码,就实现了“点击按钮直接显示图片的预览形式”啦 ~

3 技术小结#

技术栈: vue3+ element plus,其中 vue3 采用的是 script setup 组合式语法的形式。

这部分功能其实在 element plus 官方文档中有写,

https://element-plus.org/zh-CN/component/image.html#image-viewer-api

image-20231120100147616

不同的是,这里 element plus 并没有给出实际样例,只是用文字描述了下,咱就是说,家人们,这坑不坑,我还是看了别人的博客才知道这块的用处>_<

Vue3+Vue Router跳转相同路由监听页面刷新并执行某个操作

1 起源#

最近遇到了个这样的需求,大概就是:点击某个按钮,进入某个页面,然后再在这个页面执行某个操作(比如请求某个接口、赋初始值啥的)。

image-20231116163902611

这个需求看似简单,其实也不难。但是,我遇到了个问题,就是当在那个页面点击这个按钮的时候,因为跳转路由路径是一样的原因,页面是不会刷新的,那我怎么判断我是否我是否点击了那个按钮并且跳到了这个页面呢?

于是,我想到了路由传参,通过路由传参的方式,判断这个参数是否变化了,变化了就代表这个路由再次进入了。

2 解决方案#

用 query 的方式传参,参数附上时间戳,这样每进来一次都是不同的参数

点击按钮如下操作:

const router = useRouter()
const goDocumentNotification = () => {
router.push({
path: `/documentNotification`,
query: {
t: Date.now()
}
})
}

在进入的那个页面增加如下代码:

// 使用 watch 监听 route 的变化
watch(
() => route.query.t,
(newPath, oldPath) => {
// 路由变化,执行相应操作
query()
}
)

ok,经过上面的操作便可以在跳转相同路由下,监听页面刷新并执行某个操作啦。

3 知识扩展-关于 Vue Router 路由传参的几种常用方式#

说到这里,vue-router 传参的几种方式也顺便总结一下吧

3.1 params 传参(显示参数)#

浏览器里路由地址显示为这样:

http://127.0.0.1:5190/drs/index.html#/documentNotification/0

声明式:

// 子路由配置 { path: '/documentNotification/:id?', // ?代表这个参数为可传可不传 name: 'documentNotification', component: () => import('@/views/documentNotification/index.vue'), meta: { title: '发放通知', } } // 父路由组件
<router-link :to="/documentNotification/123">进入documentNotification路由</router-link>

编程式:

// 子路由配置
{
path: '/documentNotification/:id?', // ?代表这个参数为可传可不传
name: 'documentNotification',
component: () => import('@/views/documentNotification/index.vue'),
meta: {
title: '发放通知',
}
}
// 父路由编程式传参(一般通过事件触发)
router.push({
path:'/documentNotification/${yourParam}',
})

关于参数的获取:

route.params.id

3.2 params 传参(不显示参数)#

由于从 Vue Router 的 2022-8-22 这次更新后,便不能再用这种方式来写,关于不显示参数的传参,可以参考下面这篇博客:

https://blog.csdn.net/m0_57033755/article/details/129927829

3.3 query 传参#

浏览器里路由地址显示为这样:

http://localhost:3000/#/documentNotification?t=1700140985974

声明式:

//子路由配置
{
path: '/documentNotification',
name: 'documentNotification',
component: () => import('@/views/documentNotification/index.vue'),
meta: {
title: '发放通知'
}
}
//父路由组件
<router-link :to="{name:'documentNotification',query:{t:123}}">进入documentNotification路由</router-link>

编程式:

//子路由配置
{
path: '/documentNotification',
name: 'documentNotification',
component: () => import('@/views/documentNotification/index.vue'),
meta: {
title: '发放通知'
}
}
router.push({
path: `/documentNotification`,
query: {
t: Date.now()
}
})

关于参数的获取:

route.query.t

4 结语#

ok ,就到这里啦,对此你有何看法或想法呢,欢迎提出讨论呀~

浏览器自带api语音播报speechSynthesis.speak()无法自动播报问题分析及非完美解决方案

在这里插入图片描述

1 需求描述#

最近遇到一个需求,就是前端页面要实时监听后端传过来的数据,同时当后端传过来这条数据时前端界面要语音播报这条数据。

2 分析与解决方案#

这里主要说说语音播报的部分。本来觉得用浏览器自带的 API 来实现直接写一句话就 ok 了,但是没想到居然有一个 bug。那就是这条语音有时候能播报,有时候就没有声音了?为什么呢,查了半天,是浏览器的安全限制的问题,需要用户交互才能播放声音,这个问题困扰了一两天,找到了如下两条解决方案:

  • 第一个就是用按钮点击,当我们播报声音时,第一次播报必须得是用户用交互动作操作才行,显然我这里不能让用户点击,因为它是后台自动播报的,嘿嘿,这可难不到我,写了个模拟按钮点击事件,就 ok 啦~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>语音播报</title>
</head>
<body>
<h3>点击下方按钮可以进行语音播报</h3>
<hr />
<button onclick="areSpeak('哈哈哈哈哈,这是声音~')">播放声音</button>&nbsp;&nbsp;<button onclick="beQuiet()">停止播放</button>
</body>
<script>
//#region 语音播报封装
const areSpeak = newMsg => {
// 初次播报使用模拟按钮触发
virtualClick(SpeakVoice)
speakWithDelay(newMsg)
}
/**
* 语音播报
* @param msg 播报的信息
*/
const SpeakVoice = (msg = '') => {
const speech = new SpeechSynthesisUtterance(msg)
// 设置兼容中文
const voices = window.speechSynthesis.getVoices()
speech.voice = voices.filter(function (voice) {
return voice.localService == true && voice.lang == 'zh-CN'
})[0]
window.speechSynthesis.speak(speech)
}
/**
* 语音播报 带延迟 异步
* 搭配async await
* @param msg 播报的信息
*/
const speakWithDelay = (utterance, delay = 1000) => {
return new Promise(resolve => {
const speech = new SpeechSynthesisUtterance(utterance)
// 设置兼容中文
let voices = window.speechSynthesis.getVoices()
speech.voice = voices.filter(function (voice) {
return voice.localService == true && voice.lang == 'zh-CN'
})[0]
speech.onend = () => {
setTimeout(resolve, delay)
}
window.speechSynthesis.speak(speech)
})
}
/**
* 模拟按钮点击
* @param callback
*/
const virtualClick = callback => {
let button = document.createElement('button')
button.textContent = '点击我'
// 添加点击事件处理程序
button.addEventListener('click', function () {
console.log('按钮被点击了')
callback && callback()
})
// 模拟用户点击事件
let event = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
})
button.dispatchEvent(event)
}
const beQuiet = () => {
console.log('停止')
window.speechSynthesis.cancel()
SpeakVoice('')
}
//#endregion
</script>
</html>
  • 第二种解决方案就是用其他第三方的库,不过基于浏览器的安全限制,可能也会存在无法在后台自动播放的情况,所以这里还是没有采用其他第三方的库。

3 参考链接#

官方文档:

https://developer.mozilla.org/zh-CN/docs/Web/API/SpeechSynthesis

其他小伙伴的博客:

https://blog.csdn.net/pridas/article/details/119097189

https://blog.csdn.net/qq_47247479/article/details/126933326

4 源码地址#

https://github.com/JACK-ZHANG-coming/frontEnd-all-knowledge/tree/master/examples/funnyDemo/a05%E8%AF%AD%E9%9F%B3%E6%92%AD%E6%8A%A5%E5%8A%9F%E8%83%BD

如果屏幕前的小伙伴有什么好的解决方案也可以分享一下哦

vue3中子父组件传值通信的9种方法

vue3 中子父组件传值通信的 9 种方法#

1 props 传参#

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import Child1 from './components/Child1.vue'
import Child2 from './components/Child2.vue'
import Child3 from './components/Child3.vue'
const data = reactive({
lifebar: 100,
child1_lifebar: 0
})
const child1ref: {
value: {
/**生命值 */
lifebar: number
/**加血 */
addLifebar: () => void
}
} = ref<T>()
const child2ref = ref()
/**自身加血 */
const addLifebar = () => {
data.lifebar++
}
/**给队友加血 */
const addTeammateLifebar = () => {
console.log(child1ref.value)
console.log(child1ref.value.lifebar)
child1ref.value?.addLifebar()
}
onMounted(() => {
console.log('父组件加载完毕')
console.log(child1ref.value.lifebar)
data.child1_lifebar = child1ref.value.lifebar
})
</script>
<template>
<div class="main">
<div style="height: 60px; display: flex; align-items: center; justify-content: center">
我是父组件蔡文姬
<el-space style="margin-left: 5px">
<el-button @click="addLifebar">给自身增加1点血量</el-button>
<el-button @click="addTeammateLifebar">1-给子组件1马可波罗加1点血(父组件调用子组件函数)</el-button>
</el-space>
</div>
<div style="height: 100px; display: flex; flex-direction: column; align-items: center; justify-content: start">
<div><b>父组件蔡文姬信息</b></div>
<div>
当前血量:<b>{{ data.lifebar }}</b>
</div>
<div>
<!-- 这里没有自动刷新,暂时没有找到原因 -->
2-探测到的子组件1马可波罗当前气值(父组件调用子组件参数):<b>{{ data.child1_lifebar }}</b>
</div>
</div>
<div class="box-content">
<div class="left-box">
<Child1 ref="child1ref" :lifebar="data.lifebar"></Child1>
<Child3></Child3>
</div>
<div class="right-box"><Child2></Child2></div>
</div>
</div>
</template>
<style scoped lang="scss">
.main {
width: 80%;
height: 800px;
margin: auto;
border: 2px solid yellowgreen;
.box-content {
width: 100%;
height: calc(100% - 200px);
display: flex;
justify-content: space-around;
align-items: center;
.left-box {
width: 45%;
height: 100%;
border: 2px solid hotpink;
position: relative;
}
.right-box {
width: 45%;
height: 100%;
border: 2px solid hotpink;
}
}
}
</style>

后续待更新中。。#

源码地址#

https://github.com/JACK-ZHANG-coming/frontEnd-all-knowledge/tree/master/examples/vue/a06vue3%E4%B8%AD%E5%AD%90%E7%88%B6%E7%BB%84%E4%BB%B6%E4%BC%A0%E5%80%BC%E9%80%9A%E4%BF%A1%E7%9A%849%E7%A7%8D%E6%96%B9%E6%B3%95

这14种UML图,你知道几个?

这14种UML图,你知道几个?#

1 什么是UML#

UML-Unified Modeling Language 统一建模语言,又称标准建模语言。是用来对软件密集系统进行可视化建模的一种语言。UML的定义包括UML语义和UML表示法两个元素。

2 为什么要用UML#

UML的目标是以面向对象图的方式来描述任何类型的系统,具有很宽的应用领域。

在一个软件系统开始写代码之前,使用UML建模,可以使我们对这个系统的架构有更为清晰的了解,降低系统开发的风险,好处大大的。

在软件系统开发期间,亦可以使用UML。

3 UML图具体有哪些#

UML分为结构图与行为图两大类。

结构图包括:类图、对象图、包图、组合结构图、构件图、部署图、制品图;

行为图包括:用例图、顺序图、通信图、定时图、状态图、活动图、交互概念图;

3.1 ★类图#

类图是面向对象方法的核心建模工具。

3.1.1 类的表示方法#

如上所示,Employee 为类名,nameageaddress 为属性,work()为方法。我们还看到前面有-+ 这个的意思是:

+:表示public
-:表示private
:表示protected

属性的完整表示方式是: 可见性 名称 :类型 [ = 缺省值]

方法的完整表示方式是: 可见性 名称(参数列表) [ : 返回类型]

注意:

1,中括号中的内容表示是可选的

2,也有将类型放在变量名前面,返回值类型放在方法名前面

举个例子

上图Demo类定义了三个方法:

method()方法:修饰符为public,没有参数,没有返回值。

method1()方法:修饰符为private,没有参数,返回值类型为String。

method2()方法:修饰符为protected,接收两个参数,第一个参数类型为int,第二个参数类型为String,返回值类型是int。

3.1.2 常见的类之间的关系#

3.1.2.1 依赖关系#

一个事物发生变化影响另一个事物。

耦合度最弱的一种关系,在代码中为某个类的方法通过局部变量、方法临时调用一下别的类(依赖类)的方法,来完成一些功能。

下图所示,Driver司机类调用Car类的move()方法来开车,也就是临时用一下车。

3.1.2.2 泛化关系#

特殊/一般关系。是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。

子类继承了父类的所有属性及方法。

如下图所示,Student 类和 Teacher 类都是 Person 类的子类。

3.1.2.3 关联关系#

描述了一组链,链是对象之间的连接。是一种拥有关系,它使得一个类知道另一个类的属性和方法。

  • 单向关联

下图表示每个顾客都有一个地址,这通过让Customer类持有一个类型为Address的成员变量类实现。

  • 双向关联

从下图中我们很容易看出,所谓的双向关联就是双方各自持有对方类型的成员变量。

  • 自关联

3.1.2.4 聚合关系#

整体与部分生命周期不同。最弱的耦合关系,比如A聚合B,A和B分开后,B的生命周期依然在。

如下图所示,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在。

3.1.2.5 组合关系#

组合关系比聚合耦合要强,A与B组合,A没了,那对应的B也没了。

如下图所示,头和嘴的关系,没有了头,嘴也就不存在了。

3.1.2.6 实现关系#

接口与类之间的关系。

在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。

如下图所示,汽车和船是交通工具的泛化,汽车和船实现了交通工具。

3.2 ★对象图#

对象图是类图的一个实例,是系统在某个时间点的详细状态的快照,描述一组对象及它们之间的关系。

也就是说将类图整合在了一起使用。

下面是个例子:

3.3 包图#

3.4 组合结构图#

待更新。

3.5 构件图(组件图)#

待更新。

3.6 部署图#

待更新。

3.7 制品图#

待更新。


3.8 ★用例图(序列图)#

待更新。

3.9 顺序图#

待更新。

3.10 通信图#

待更新。

3.11 定时图#

待更新。

3.12 状态图(状态机图)#

待更新。

3.13 活动图#

待更新。

3.14 交互概念图#

待更新。

4 参考#

5 小结#

类图、对象图、用例图重要些,多练多应用实际。

ts绕开属性检查的3种方法

ts 绕开属性检查的 3 种方法#

引言#

不知道大家有没有遇到这种情况,当我们预先定义了 ts 的一些类型后,在我们真正用到时却又和原先约定的类型定义不一样,哎?那有时候我们有不想或者因为因为一些情况不好去改原来已经定义过的类型定义,这又该怎么办呢? 不要着急,ts 为我们提供了 3 中解决方案,

请看下面:

示例代码:

interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string, area: number } {
return {
color: config.color,
area: config.width
}
}
let mySquare = createSquare({ color: 'red', width: 100 })
console.log('mySquare--->', mySquare)

如上所示,声明一个 createSquare 函数,形参类型是 SquareConfig 接口,传入的形参是{ color: "red", width: 100 },好,这个时候是符合我们原先的 ts 类型定义的,但是当我们把入参改为{ color: "red", width12: 100 },这个时候 ts 就会判断出入参传入有误,嘿,还智能的提示一下写 width12 是不是想传 width 这个变量

image-20230301172449195

可是,如果我们真的是需要第二参数不同,那该如何嘞,请看下面的 3 种解决方式:

1 类型断言#

最简便的方法,用 as 告诉 ts 这就是我想要的,这个类型是对的,好的,那么 ts 就不会报错

interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
return {
color: config.color,
area: config.width,
}
}
let mySquare = createSquare({ color: "red", opacity: 0.5 } as SquareConfig); // 这里声明了{ color: "red", opacity: 0.5 } 就是SquareConfig类型
console.log('mySquare--->', mySquare)

2 添加一个字符串索引签名#

最佳方式

interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
return {
color: config.color,
area: config.width,
}
}
let mySquare = createSquare({ color: "red", opacity: 0.5 }); //这里依然不会报错
console.log('mySquare--->', mySquare)

3 对象赋值转接一手#

interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
return {
color: config.color,
area: config.width,
}
}
let squareOptions = { color: "red", opacity: 0.5 } // 用squareOptions变量来转接一下
let mySquare = createSquare(squareOptions); //这里依然不会报错
console.log('mySquare--->', mySquare)

参考:

https://typescript.bootcss.com/interfaces.html

如何使用react-router实现一个路由权限判断

前言

这是一篇关于react-router的通篇全解的文章,本文旨在阅读完本文可以对react-router有一个系统的了解——emmm原来的思路是打算这么写的,但是近日阅读了一个大佬的文章,发现最好还是学以致用,并且融入自己的思想,所以决定前面介绍react-router的一些常规知识(1-3大点),后面第4大点会写一个实例,用react-router写一个路由鉴权。第4大点最为重要,如果对react-router有些许了解的也可以直接进入第4大点。

首先,有一说一,最详细的教程还是:官网 。下面的介绍将是融入个人理解的白话文:

1 相关理解#

1.1 SPA的理解#

什么是spa?英文全拼single page web application,中文单页面Web应用。

通俗的说,点击页面中的链接不会刷新页面(浏览器左上角的那个小圆环不会转),只会做页面的局部更新,那这就是个单页面web应用。比如我们用的<a></a>标签,里面加个href='https://www.zhangqiang.hk.cn/' 属性,当我们点击那个a标签,此时页面跳转了网页,左上角那个小圆圈呼溜溜的转了,那这就不是个单页面web应用。

而ajax异步请求、react-router都是可以实现spa的、

1.2 路由的理解#

1.2.1 什么是路由?#

  • 一个路由就是一个映射关系(key:value)
  • key为路径, value可能是function或component

1.2.2 路由分类#

后端路由:

理解: value是function, 用来处理客户端提交的请求。 注册路由: router.get(path, function(req, res)) 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

前端路由:

浏览器端路由,value是component,用于展示页面内容。 注册路由: <Route path="/test" component={Test}> 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

react-router-dom的理解:

  • react的一个插件库。
  • 专门用来实现一个SPA应用。
  • 基于react的项目基本都会用到此库。

2 react-router-v5#

2.1 react-router-dom相关API#

2.1.1 内置组件#

<BrowserRouter>
<HashRouter>
<Route>
<Redirect>
<Link>
<NavLink>
<Switch>

2.1.2 其它#

  • history对象

  • match对象

  • withRouter函数

2.2 基本路由使用#

2.2.1 安装react-router-dom#

npm install --save react-router-dom

2.2.2 嵌套路由使用#

2.2.3 向路由组件传递参数数据#

2.2.4 多种路由跳转方式#

3 react-router-v6#

4 实例-react-router实现前端路由鉴权#

相关参考:

​ 使用React-Router实现前端路由鉴权:https://juejin.cn/post/6854573217445740557#comment