跳到主要内容位置

前端: 30 篇

查看所有标签(分类)

解决 Vite 项目启动时端口重复问题的总结


背景#

在前端开发的江湖上,Vite 就像一个轻盈迅捷的剑客,以“快”和“爽”闻名。默认栖身于本地 3000 端口,简直是开发者的“专属包间”。但江湖人多,难免遇到这种情况:你刚起剑练招,发现隔壁的同门师兄已经占了你的包间。于是,你只能抱着代码,哼哼唧唧换个地方继续修炼——这种“被端口抢占”的故事,简直就是开发者的日常笑话!

例如:

  1. 多人协作:团队中的多个开发者同时运行了 Vite 项目,使用了相同的端口。
  2. 多项目运行:本地同时启动了多个 Vite 项目或其他服务(如 Webpack、Node.js 服务器),导致端口冲突。
  3. 默认配置不足:Vite 默认没有处理端口占用的逻辑,遇到冲突会直接报错,开发中断。

为了解决这个问题,我研究了几种方法,最终采用了 detect-port 插件,实现了动态检测和自动调整端口的功能,极大地提升了开发效率。


解决方案#

1. 常见解决方法#

1.1 *手动更改端口**#

vite.config.ts 文件中修改 server.port 的值。例如:

export default defineConfig({
server: {
port: 3001 // 手动指定一个新的端口
}
})

缺点:需要手动调整端口,效率较低,且不方便多人协作。

1.2 尝试端口范围#

一些开发者会通过运行脚本动态尝试多个端口,但编写脚本可能较繁琐且不直观。

1.3 使用第三方工具#

利用工具如 detect-portportfinder,自动检测端口是否被占用并返回可用端口。

2. Detect-port 的解决方案(本人的解决方法)#

我最终采用了 detect-port 插件,因为它简单易用,能够自动检测当前端口是否被占用,并返回一个可用的端口。

Step 1: 安装 detect-port#

在项目根目录下运行以下命令:

pnpm install detect-port --save-dev

或者使用 npm/yarn 安装:

npm install detect-port --save-dev
yarn add detect-port --dev

Step 2: 配置 Vite 项目#

修改 vite.config.ts 文件,引入 detect-port,实现动态端口检测:

import { defineConfig } from 'vite'
import detectPort from 'detect-port'
export default defineConfig(async () => {
const DEFAULT_PORT = 3000 // 默认端口
// 使用 detect-port 检测端口是否被占用
const port = await detectPort(DEFAULT_PORT)
console.log(`Selected port: ${port}`) // 输出实际选用的端口
return {
server: {
host: '0.0.0.0', // 允许局域网访问
port, // 动态端口
open: true, // 自动打开浏览器
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
}
})

Step 3: 效果展示#

  • 如果默认端口(如 3000)已被占用,detect-port 会自动检测下一个可用端口,例如 3001 或 3002。
  • 启动时会输出实际使用的端口号:
Selected port: 3001

这样,我们无需手动修改端口设置,提升了开发效率。lalala~

3. 优化点#

在实践中,我还添加了一些优化:

3.1 添加日志输出#

使用 console.log 记录端口信息,便于调试:

console.log(`Using port ${port}. To change, edit vite.config.ts`)

3.2 范围检测(可选)#

通过扩展 detect-port,可尝试在特定范围内寻找端口:

const port = await detectPort(3000)
if (port !== 3000) {
console.log(`Port 3000 is in use. Using port ${port} instead.`)
}

小结#

问题总结#

Vite 默认端口冲突的问题在多人协作和多项目运行的场景中非常常见。传统的手动解决方案需要频繁调整配置,显得麻烦且低效。

我的解决方案#

利用 detect-port 插件,可以轻松实现动态端口检测和分配:

  • 自动检测端口冲突。
  • 动态分配可用端口,提升开发效率。
  • 配置简单、易于扩展。

未来优化#

  • 扩展端口范围检测:可以尝试自定义检测端口范围(如 3000-3100)。
  • 集成其他工具:例如结合 vite-plugin-inspect,提供更多启动信息。
  • 多环境支持:为不同环境(开发、测试、生产)定制端口策略。

在团队开发中,这种自动化的方式能够显著减少端口冲突问题,提升协作效率,非常值得推广。

Vue3中watch中props监听加箭头函数与不加箭头函数的区别

1 前言#

今天写项目时,遇到一个问题,我需要打开点击一个按钮打开一个弹框页面,然后通过watch去监听传进来的值,但是呢,写了watch我点击按钮只有首次打开能够监听到,尽管加上了deep: true页面也不能监听到变化,点击效果如下图:

image-20240903172257604

2 代码实例#

直接上我的关键代码,这个是我弹框页面的代码:

<template>
<div class="content_container">1111</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, watch, onActivated } from 'vue'
import { ElMessageBox } from 'element-plus'
const props = defineProps(['currentRow'])
console.log('props', props)
watch(
props.currentRow,
(newVal, oldVal) => {
console.log('newVal', newVal)
},
{
immediate: true,
deep: true
}
)
</script>

解决后的代码:

<template>
<div class="content_container">1111</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, watch, onActivated } from 'vue'
import { ElMessageBox } from 'element-plus'
const props = defineProps(['currentRow'])
console.log('props', props)
watch(
() => props.currentRow,
(newVal, oldVal) => {
console.log('newVal', newVal)
},
{
immediate: true,
deep: true
}
)
</script>

其实,只是修改了一句话,多加了一个箭头函数~ 就可以了!他就可以每次进来就都监听了,为啥呢?请看下面的代码精读环节

3 代码精读#

3.1 直接监听 props 属性#

javascriptwatch(
props.currentRow,
(newVal, oldVal) => {
console.log('newVal', newVal);
},
{
immediate: true,
deep: true
}
);

当你直接监听 props.currentRow 时,watch 函数会尝试将 props.currentRow 当作一个响应式引用进行监听。然而,props 本身并不是一个响应式引用,而是一个对象。因此,这种方式可能会导致以下问题:

  • 非响应式引用: 如果 props.currentRow 是一个简单的值(如字符串或数字),那么 watch 可能无法正确地监听到它的变化。
  • 对象或数组内部属性变化: 即使设置了 deep: truewatch 仍然可能无法正确地监听到对象或数组内部属性的变化。

3.2 使用箭头函数监听 props 属性#

javascriptwatch(
() => props.currentRow,
(newVal, oldVal) => {
console.log('newVal', newVal);
},
{
immediate: true,
deep: true
}
);

使用箭头函数 () => props.currentRow 可以确保每次 props.currentRow 发生变化时,都会重新计算并触发 watch 回调。这种方式更可靠,因为它明确地告诉 watch 如何获取最新的 props.currentRow 值。

4 总结#

在正常的开发中,我们应该使用箭头函数组合watch来进行props值的变化,来确保每次 props 变化时都能重新计算。

pnpm依赖安装解决pnpm项目从一个文件夹复制到另一个文件夹运行失败问题解决-以vbenAdmin项目为例

1 前言#

最近在使用 vben admin 项目迁移时,从一个文件夹复制到另一个文件夹运行不起来,排查了一天,终于把问题排查出来了,特地记录一下,便于自己看也便于大家看。

使用项目安装包版本:

v2.11.5 https://github.com/vbenjs/vue-vben-admin/tags

2 解决方案#

2.1 软链接问题解决#

问题: pnpm 使用硬链接或符号链接来管理 node_modules 中的依赖项。如果链接路径在新的文件夹或文件系统中失效,可能会导致依赖解析失败。

解决方法: 尝试删除并重新安装所有依赖,确保链接正确创建。

rm -rf node_modules pnpm-lock.yaml

node_modules 文件夹一定要全部删除,pnpm-lock.yaml文件也要删除,上述是用命令删除的,也可以自己手动删(所有的 node_modules 都要删)

image-20240830135249168

2.2 文件路径过长问题#

问题: 在 Windows 系统上,文件路径过长(超过 260 个字符)可能导致文件系统问题,导致文件无法被正确访问。

解决方法: 尝试将项目目录移动到一个路径较短的位置,比如 C:\Projects\my-project,然后重新安装依赖。

node_modules 文件夹里面的内容路径会很长,所以整个项目文件夹所在路径不宜过长,否则路径可能会超过 260 个字符。

image-20240830135718984

2.3 pnpm 缓存问题#

问题: pnpm 的缓存可能导致一些包没有被正确更新或安装。

解决方法: 清除 pnpm 的缓存:

依次执行下面命令

  • 清理未使用的缓存包
pnpm store prune
  • 清空整个 pnpm 的缓存存储库
pnpm store clear

2.4 pnpm 缓存深度清理#

执行下面这句话:

pnpm store path

然后我们看到这个路径

image-20240830140127029

进入这个目录,将这 3 个文件夹都手动删除

image-20240830140240181

2.5 pnpm install#

删完之后,执行pnpm install

pnpm install

image-20240830141017683

2.6 pnpm dev#

等待依赖安装完毕后,我们执行pnpm dev

pnpm dev

程序运行成功

image-20240830142025994

3 pnpm 安装依然失败解决方案#

当我们试了各种方法还是不行,诶?这个时候我们返璞归真,是不是我们的 pnpm 的源下载不了的原因呢?国内的 pnpm 下载出现网络相关的问题还是挺多的,那么让我们来替换成国内专属的 pnpm 源试试

3.1 国内可用源#

3.1.1 淘宝 pnpm 源

https://registry.npmmirror.com

3.1.2 腾讯云 pnpm 源

https://mirrors.cloud.tencent.com

3.1.3 cnpm 源

https://r.cnpmjs.org/

3.2 设置国内可用源命令#

pnpm config set registry https://registry.npmmirror.com

3.3 查看设置的源#

pnpm config get registry

返回信息内容是你刚刚设置地址,即设置成功。

image-20240929154343078

3.4 再次 pnpm install#

ok,pnpm 相关依赖在徐徐下载,我的终于又又装成功了~

4 遗留的瑕疵#

项目虽然可以成功运行了,也可以正常写代码的了,但是终端还有几个 warning ,如果有大佬知道如何解决也可以指点一下,感谢~

image-20240830142224622

为什么 2!=false 和 2!=true 返回的都是true

前言#

今天突然想起一个奇怪的问题,记录一下,我在控制台执行内容如下:

image-20240821171734282

由上图可见,2 != false2 != true 返回的值竟然都是true,那么为什么呢,请看下文:

1 != 操作符的作用#

  • != 是“不等于”操作符。它会在比较前执行类型转换,然后再比较两个值是否不相等。

    在 JavaScript 中,2 != false2 != true 返回 true 的原因涉及到 JavaScript 中的类型转换和比较规则。

2 类型转换#

当使用 != 进行比较时,JavaScript 会尝试将比较的两个值转换为相同的类型,然后再进行比较。以下是 2 != false2 != true 的过程:

2 != false#

  1. false 会被转换为数字类型。根据 JavaScript 的转换规则,false 被转换为 0
  2. 现在表达式变成了 2 != 0
  3. 20 不相等,因此返回 true

2 != true#

  1. true 会被转换为数字类型。根据 JavaScript 的转换规则,true 被转换为 1
  2. 现在表达式变成了 2 != 1
  3. 21 不相等,因此返回 true

总结#

  • 2 != false 返回 true 是因为 20 不相等。
  • 2 != true 返回 true 是因为 21 不相等。

这就是为什么 2 != false2 != true 都会返回 true

Vue3使用Element Plus单个Tag标签文字过长自动换行代码实现

1 需求引入#

直接上图,原先程序显示效果是这样:

image-20240807172107258

由上图可见,element plus 的 tag 标签组件里面内容过长时,把页面都撑开了,不能自动换行,我的 tag 标签代码原先是这样写的

<div style="width: 100%; margin-bottom: 5px">
<el-tag v-if="uploadMessage.successMessage.length > 0" :type="'success'" effect="plain">
{{ uploadMessage.successMessage }}
</el-tag>
</div>

2 解决需求,代码实现#

先看解决效果图:

image-20240807172500463

如何解决的呢,只要我们再单独写一下 css 的样式就 ok 了,看下面的 css 样式代码:

.el-tag {
white-space: normal;
height: auto;
padding: 2px;
display: inline-block;
}

好了,直接加上上面这段 css 代码就 ok 了。

3 小结#

我当时用这个标签是显示错误信息的,后来发现 element 的 Alert 组件用于现实错误信息才比较合适,因为那个有个关闭的按钮,既然已经写了,就记录一下吧~ 希望对看文章的你也有些用吧 ~ >_< ~

async、await的这个小细节你知道?async、await多次实践使用后的一点小结(async返回异步问题)

前言#

我们都知道async、await 是用来将“同步函数变成异步函数,可以同步获取到里面异步函数的返回值”的,比如我们在请求一个接口的时候,这个接口的返回值是一个异步的,那我们就可以用 await 将这个异步接口返回变成同步,使我们可以同步的获取到接口的返回值,然后在紧接着的下文中就可以直接用这个接口的返回值。

一 实例 1#

循序渐进,先上一个简单的代码

const asyncFun = (value: number) => {
return new Promise((resolve, reject) => {
// 模拟一个异步请求
setTimeout(() => {
resolve(value + 1)
}, 2000)
})
}
const getValue = async () => {
let result = await asyncFun(1)
console.log('result:', result)
}
getValue() // result:2

如上图代码,输出的结果是

result: 2

我们来浅分析一下这段代码,这段代码其实我们是模拟了一个接口请求的操作,asyncFun(value: number)返回一个 Promise,Promise 就代表这个结果是一个异步的,当我们调用这个函数时得到的结果就会等所有同步代码执行完以后才会得到这个 Promise 的结果。

如果我们调用asyncFun(value: number)这个函数想要同步获取到里面的结果,获取完这个结果后,紧接着同步执行下面的代码,那我们就要使用asyncawait 来解决,这是ES6 推出的新语法,好用的很啊,但是也有一些细节需要注意(下文会提到)。

所以我们在使用getValue() 这个函数调用asyncFun(value: number)时,首先在getValue()函数头部加了 async,声明咱这个函数是一个异步函数,这样在这个函数里面我们就可以用await将异步的返回值转为同步获取到了,所以await asyncFun(1); 的意思是等这里完全执行完(2 秒之后),获取到value+1这个具体数值,然后才会执行console.log('result:', result); 这句话。

所以上面这段代码最后输出的就是

result: 2

二 实例 2(踩坑记录)#

同样的,咱们也是直接上代码块,大家看一下这段代码输出的是个啥:

const asyncFun = (value: number) => {
return new Promise((resolve, reject) => {
// 模拟一个异步请求
setTimeout(() => {
resolve(value + 1)
}, 2000)
})
}
const getValue = async () => {
let result = await asyncFun(1)
return result
}
const add666Value = () => {
let result: any = getValue()
result = result + '666'
console.log('result:', result)
}
add666Value()

上面这段代码就是将getValue()方法又封装了一遍,得到这个结果,然后再去输出这个结果,那么大家觉得这个是个啥值呢?可能有的人会认为输出的是result: 2666,其实不然,真正输出的结果是:

result: [object Promise]666

嘿嘿,这是为啥呢?这就是前面说的那个小细节了,使用 async 声明的函数其返回值是一个 Promise。通常我们写一个普通函数,想 return 啥它就能直接 return 啥,但是只要在这个函数头上加了 async ,那它就会返回 Promise 类型,这个函数的返回值就是一个异步的了,等到所有同步函数执行完以后才会等到里面的确切值。

比如这个小例子 1:

const littleExample1 = () => {
const a = 1
return a
}

这个littleExample1()的返回值就是一个 number 类型的 1;

而看下面这个小例子 2:

const littleExample2 = async () => {
const a = 1
return a
}

嘿,这个小例子 2 返回的就是个 Promise 了,你就说细节不细节,之前都没关注过,但这个场景确实在实际使用中有用到的,然后我就说为啥返回值不是预期的值呢,结果一排查就是这个 async 的原因。

三 实例 2 解决方案#

话不多说,我们来看看 实例2 中的代码块如何处理才能得到一个我们预期的值,也就是: result: 2666 ,直接上代码:

const asyncFun = (value: number) => {
return new Promise((resolve, reject) => {
// 模拟一个异步请求
setTimeout(() => {
resolve(value + 1)
}, 2000)
})
}
const getValue = async () => {
let result = await asyncFun(1)
return result
}
const add666Value = async () => {
let result: any = await getValue()
result = result + '666'
console.log('result:', result)
}
add666Value()

ok,这样子就是我们预期的输出结果了,也就是我们将add666Value()前面加了个 async ,将其声明为异步函数getValue()前面再加个await ,这样getValue()返回的结果就不是 Promise 了,result 就可以得到一个 number 类型的 2,number 类型+string 类型时前面的 number 类型会变成 string 类型,相当于'2'+'666',所以最终输出的结果就是:

result: 2666

至此,async返回异步问题已解决。

四 加强记忆#

知识点参考:

  • AsyncFunction

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction

vscode代码片段自动转图标禁用(el-tag代码自动转成了图标问题排查)

前言#

今天在写代码时,一时觉得代码里面有一些图标,光标移到那里代码又显示处理了,这样会使那行代码忽长忽短,看起来很变扭,着实难受。早就看到了,但是不知道这个是啥原因导致的,然后就百度了一度,本以为是插件的原因,结果发现是 vscode 配置导致,下面情况问题及解决方案:

1 有图标的代码显示#

img

2 改完之后无图标的代码显示#

img

这样看起来比之前舒服多了....

3 详细解决流程#

设置->搜索 iconify,然后把下面这个 √ 去掉就 OK 了~

img

改为这个就 ok 啦~ 代码就会自动变为代码了~

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

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

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

一文学会ajax基础使用与使用nodejs搭建一个后端服务

写在前面#

简介#

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。

AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。

XMLHttpRequest 只是实现 Ajax 的一种方式。

上面是来自菜鸟教程的简介,用白话文概括呢,ajax 是 javascript 里面内置的一种异步方法实现方式,用 XMLHttpRequest 对象可以来实现这种异步方式。目前我们主流的前端接口请求方式fetch、axios 都是基于 ajax 封装的,所以了解 ajax 的使用对于解读 fetch 与 axios 的源码也是必不可少的。

In short,学习 ajax 的使用是很重要的。

0 准备工作#

首先,执行个小目标,我们在本地先快速起一个后端服务,使前端可以调到这个接口,很简单,请看下面的操作:

打开我们的 vscode(如果你使用的是 webstorm 也是一样的操作,不过很推荐 vscode 呀,干净简洁很喜欢~),执行npm init初始化一下 npm 配置(在这之前要确保电脑上已经安装 node.js,可以在终端输入 node -v 查看,有版本号说明已经安装):

0.1 npm 初始化

  • node -v 有版本号显示说明已经安装 node.js

image.png

  • 执行 npm init,一路回车就完事了,然后我们可以看到所在文件夹里面会有个 package.json 文件,说明 npm init 初始化成功。之后安装的 npm 包版本都可以在 package.json 里面查看到。

image-20230113150934180

0.2 安装 express

在终端执行npm install express,安装 express,这是一个库可以让我们用 node.js 启动一个后端服务器,当这个后端服务启动的时候,我们就可以通过前端代码请求它暴露出来的接口访问到相应的后端服务。

0.2.1 如果安装成功的小伙伴不用看这个小点,我在安装的时候遇到了这个报错,分析一下是因为 npm 的源是国外的原因,所以我将其设置成了淘宝镜像

image-20230113152632762

设置淘宝镜像命令,在终端执行(下面图片里面有):

npm config set registry https://registry.npm.taobao.org

0.2.1 安装成功

image-20230113152715760

顺便再装个库,npm install body-parser 后面会有用,针对 post 请求的

image-20230113215801561

0.2.2 启动 node 后端服务

我们创建一个文件夹 src,在 src 下面建个 server.js 的文件,然后再粘贴下方的代码到 server.js 文件里面

//1. 引入express
const express = require('express');
//2. 创建应用对象
const app = express();
//3. 创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
app.get('/server', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   //设置响应体 这里是返回给前端的内容
   response.send('哈哈哈,接口请求成功,这一串文字是接口返回的数据~~');
});
//4. 监听端口启动服务
app.listen(8000, () => {
   console.log("服务已经启动, 8000 端口监听中....");
});

image-20230113153544950

终端进入 src 这个文件夹,然后执行node server.js,启动后端服务

image-20230113154009446

如上图,我们已经在本地打开了 8000 端口,同时写了个/server get 类型的接口地址,我们可以直接通过浏览器来访问http://127.0.0.1:8000/server测试一下接口,如果我们看到下图这样,说明后端服务开启成功~

image-20230113154625311

ok,后端服务启动完毕!~ 还是很厉害的嘛,我们进入下个流程~~ 前端写 ajax 请求。

1 写个简单的 ajax 请求#

1.1 get 请求 与 post 请求#

1.1.1 前端代码#

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>ajax使用</title>
   <style>
       .mainContainer {
           font-size: 20px;
      }
   </style>
</head>
<body>
   <div class="mainContainer">
       <div>右键打开控制台查看ajax网络请求</div>
       <button>点击发送GET请求</button>
       <button>02点击发送带参数的GET请求</button>
       <button>03点击发送带参数的POST请求</button>
   </div>
   <script>
       //获取button元素
       const btn = document.getElementsByTagName('button')[0]
       //绑定事件
       btn.onclick = function () {
           //1. 创建对象
           const xhr = new XMLHttpRequest()
           //2. 初始化 设置请求方法和 url
           xhr.open('GET', 'http://127.0.0.1:8000/server')
           //3. 发送
           xhr.send()
           //4. 事件绑定 处理服务端返回的结果
           // on when 当....时候
           // readystate 是 xhr 对象中的属性, 表示状态 0 1 2 3 4
           // change 改变
           xhr.onreadystatechange = function () {
               //判断 (服务端返回了所有的结果)
               if (xhr.readyState === 4) {
                   //判断响应状态码 200 404 403 401 500
                   // 2xx 成功
                   if (xhr.status >= 200 && xhr.status < 300) {
                       //处理结果 行 头 空行 体
                       //响应
                       // console.log(xhr.status);//状态码
                       // console.log(xhr.statusText);//状态字符串
                       // console.log(xhr.getAllResponseHeaders());//所有响应头
                       // console.log(xhr.response);//响应体
                       //设置 result 的文本
                       console.log(`${btn.textContent}返回回来的数据:`, xhr.response)
                  } else {
                       console.log('接口请求失败', btn.textContent)
                  }
              }
          }
      }
       // 02点击发送带参数的GET请求
       const btn2 = document.getElementsByTagName('button')[1];
       //绑定事件
       btn2.onclick = function () {
           //1. 创建对象
           const xhr = new XMLHttpRequest()
           //2. 初始化 设置请求方法和 url
           xhr.open('GET', 'http://127.0.0.1:8000/server/getAndValue?a=100&b=200&c=300');
           //3. 发送
           xhr.send()
           //4. 事件绑定 处理服务端返回的结果
           xhr.onreadystatechange = function () {
               if (xhr.readyState === 4) {
                   if (xhr.status >= 200 && xhr.status < 300) {
                       console.log(`${btn2.textContent}返回回来的数据:`, xhr.response)
                  } else {
                       console.log('接口请求失败', btn2.textContent)
                  }
              }
          }
      }
       // 03点击发送带参数的post请求
       const btn3 = document.getElementsByTagName('button')[2];
       //绑定事件
       btn3.onclick = function () {
           //1. 创建对象
           const xhr = new XMLHttpRequest()
           //2. 初始化 设置请求方法和 url
           xhr.open('POST', 'http://127.0.0.1:8000/server/postAndValue');
           //设置请求头 post请求要设置请求头,以form表单的形式传参
           xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
           //3. 发送
           xhr.send('a=100&b=200&c=30000000000111')
           //4. 事件绑定 处理服务端返回的结果
           xhr.onreadystatechange = function () {
               if (xhr.readyState === 4) {
                   if (xhr.status >= 200 && xhr.status < 300) {
                       console.log(`${btn3.textContent}返回回来的数据:`, xhr.response)
                  } else {
                       console.log('接口请求失败', btn3.textContent)
                  }
              }
          }
      }
   </script>
</body>
</html>

1.1.2 后端代码#

//1. 引入express
const express = require('express');
const bodyParser = require('body-parser'); // 使用express中间件,以解决post请求后端获取不到值问题
//2. 创建应用对象
const app = express();
// 下面这两句话是针对post请求加的, req.body是解析json的结果,一定加上这么2句,否则post请求获取不到req.body的  
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
//3. 创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
app.get('/server', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   //设置响应体 这里是返回给前端的内容
   response.send('哈哈哈,接口请求成功,这一串文字是接口返回的数据~~');
});
// 3.2 get带参数的接口
app.get('/server/getAndValue', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   // response.setHeader('Access-Control-Allow-Headers', '*');
   //设置响应体 这里是返回给前端的内容
   let result = request.query;
   if (result.a !== undefined) {
       result.a = result.a + 10000
  }
   response.send(result);
});
// 3.2 get带参数的接口
app.get('/server/getAndValue', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   // response.setHeader('Access-Control-Allow-Headers', '*');
   //设置响应体 这里是返回给前端的内容
   let result = request.query;
   if (result.a !== undefined) {
       result.a = result.a + 10000
  }
   response.send(result);
});
// 3.2 post带参数的接口
app.all('/server/postAndValue', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   response.setHeader('Access-Control-Allow-Headers', '*');
   // response.header('Access-Control-Allow-Headers', 'Content-Type')
   //设置响应体 这里是返回给前端的内容
   let result = request.body;
   console.log('postAndValue--->', result)
   response.send(request.body);
});
//4. 监听端口启动服务
app.listen(8000, () => {
   console.log("服务已经启动, 8000 端口监听中....");
});

1.2.3 运行结果#

image-20230113220628033

2 相关参考手册#

2.1 XMLHttpRequest 对象的方法#

方法描述
abort()取消当前请求
getAllResponseHeaders()返回头部信息
getResponseHeader()返回特定的头部信息
open(method, url, async, user, psw)规定请求 method:请求类型 GET 或 POSTurl:文件位置 async:true(异步)或 false(同步)user:可选的用户名称 psw:可选的密码
send()将请求发送到服务器,用于 GET 请求
send(string)将请求发送到服务器,用于 POST 请求
setRequestHeader()设置请求头,向要发送的报头添加标签/值对

2.2 XMLHttpRequest 对象的属性#

属性描述
onreadystatechange定义当 readyState 属性发生变化时被调用的函数
readyState保存 XMLHttpRequest 的状态。0:请求未初始化 1:服务器连接已建立 2:请求已收到 3:正在处理请求 4:请求已完成且响应已就绪
responseText以字符串返回响应数据
responseXML以 XML 数据返回响应数据
response以原格式返回响应数据,可以理解为后端返回的数据格式是啥那就是啥
status返回请求的状态号 200: "OK"403: "Forbidden"404: "Not Found"
statusText返回状态文本(比如 "OK" 或 "Not Found")

2.3 http 状态码#

1xx: 信息#

消息:描述:
100 Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。
101 Switching Protocols服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。

2xx: 成功#

消息:描述:
200 OK请求成功(其后是对 GET 和 POST 请求的应答文档。)
201 Created请求被创建完成,同时新的资源被创建。
202 Accepted供处理的请求已被接受,但是处理未完成。
203 Non-authoritative Information文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝。
204 No Content没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而 Servlet 可以确定用户文档足够新,这个状态代码是很有用的。
205 Reset Content没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。
206 Partial Content客户发送了一个带有 Range 头的 GET 请求,服务器完成了它。

3xx: 重定向#

消息:描述:
300 Multiple Choices多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。
301 Moved Permanently所请求的页面已经转移至新的 url。
302 Found所请求的页面已经临时转移至新的 url。
303 See Other所请求的页面可在别的 url 下被找到。
304 Not Modified未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(一般是提供 If-Modified-Since 头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
305 Use Proxy客户请求的文档应该通过 Location 头所指明的代理服务器提取。
306 Unused此代码被用于前一版本。目前已不再使用,但是代码依然被保留。
307 Temporary Redirect被请求的页面已经临时移至新的 url。

4xx: 客户端错误#

消息:描述:
400 Bad Request服务器未能理解请求。
401 Unauthorized被请求的页面需要用户名和密码。
402 Payment Required此代码尚无法使用。
403 Forbidden对被请求页面的访问被禁止。
404 Not Found服务器无法找到被请求的页面。
405 Method Not Allowed请求中指定的方法不被允许。
406 Not Acceptable服务器生成的响应无法被客户端所接受。
407 Proxy Authentication Required用户必须首先使用代理服务器进行验证,这样请求才会被处理。
408 Request Timeout请求超出了服务器的等待时间。
409 Conflict由于冲突,请求无法被完成。
410 Gone被请求的页面不可用。
411 Length Required"Content-Length" 未被定义。如果无此内容,服务器不会接受请求。
412 Precondition Failed请求中的前提条件被服务器评估为失败。
413 Request Entity Too Large由于所请求的实体的太大,服务器不会接受请求。
414 Request-url Too Long由于 url 太长,服务器不会接受请求。当 post 请求被转换为带有很长的查询信息的 get 请求时,就会发生这种情况。
415 Unsupported Media Type由于媒介类型不被支持,服务器不会接受请求。
416服务器不能满足客户在请求中指定的 Range 头。
417 Expectation Failed

5xx: 服务器错误#

消息:描述:
500 Internal Server Error请求未完成。服务器遇到不可预知的情况。
501 Not Implemented请求未完成。服务器不支持所请求的功能。
502 Bad Gateway请求未完成。服务器从上游服务器收到一个无效的响应。
503 Service Unavailable请求未完成。服务器临时过载或当机。
504 Gateway Timeout网关超时。
505 HTTP Version Not Supported服务器不支持请求中指明的 HTTP 协议版本。

js map函数的用法

  • map()方法只能应用于数组遍历。如果想要遍历对象,可将对象转化为数组对象再其进行遍历。
var arr = [1,2,3,4];
//item,index,arr 分别为:当前元素的值(必填),当前元素的索引值,当前元素属于的数组对象
//最终在不改变原有数组值的情况下将原来数组循环一遍,处理好数据,返回一个新数组。
var arr1 = arr.map((item,index,arr) => {
console.log("--");
console.log(item);//当前元素值
console.log(index);//当前索引值
console.log(arr);//对象
return item*10 //新数组为10,20,30,40
})
console.log("arr1:",arr1);
  • 输出结果:在这里插入图片描述

vscode+react环境部署,运行第一个react框架

运行结果图:

在这里插入图片描述

一、安装vscode#

见链接:https://www.cnblogs.com/csji/p/13558221.html

二、安装node、npm、yarn(采用淘宝镜像)#

1.安装node#

见链接:https://blog.csdn.net/bbj12345678/article/details/106741758

  • 检测是否已经安装 键盘同时按下win+r,然后输入cmd,在黑窗口内输入:node -v 在这里插入图片描述

会出现相应的版本号,说明已经安装好了。

2.安装yarn#

见链接:https://blog.csdn.net/qq_45011985/article/details/105566648

  • 检测是否已经安装 键盘同时按下win+r,然后输入cmd,在黑窗口内输入:yarn -v 在这里插入图片描述

三、拉取React脚手架,运行第一个项目#

  • 官网教程: 在这里插入图片描述
  • 在vscode终端里面运行下面的语句:
npx create-react-app my-app
cd my-app
npm start

等待加载完毕,我们再输入 yarn run start 运行即可。

总结#

待更新。