跳到主要内容位置

前端: 22 篇

查看所有标签(分类)

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 运行即可。

总结#

待更新。