web前端面试题

234 阅读36分钟

学习地址:juejin.cn/post/684490…

juejin.cn/post/684490…

1.如何解决 a 标点击后 hover 事件失效的问题?

a:link:未访问的样式

a:visited:已经访问的样式

a:hover:鼠标移上去时的样式

a:active:鼠标按下的样式

因此要记住书写顺序:link→visited→hover→active

2.数组去重#

Array.from()和new Set()的用法 Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组。

let arrayLike = { 0: 'tom', 1: '65', 2: '男', 3: ['jane','john','Mary'], 'length': 4 }
let arr = Array.from(arrayLike)
console.log(arr) // ['tom','65','男',['jane','john','Mary']]

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

数组去重:Array.from(new Set(arr))

let arr = [12,12,45,97,9797,564,134,45642]
let set = new Set(arr) {12,45,97,9797,564,134,4562} 
//去重 
console.log(Array.from(set)) // [ 12, 45, 97, 9797, 564, 134, 45642 ]

3.js的递归

var findFinalValue = function(nums, original) {
    for(var i=0;i<nums.length;i++){
        if(nums[i]==original){
            original = 2 * original
            return findFinalValue(nums,original)
        }
    }
    return original

};

 return findFinalValue(nums,original) 这一步骤包含了递归算法,注意如果findFinalValue为传参方法那么return里也需要传参

4.null 和undefined的区别

console.log(null==undefined)//true 
console.log(null===undefined)//false
  • null表示一个"无"的对象,也就是该处不应该有值;

undefined表示未定义。

  • 在转换为数字时结果不同,Number(null)为0,而undefined为NaN。

  • 使用场景上:

null:

  • 作为函数的参数,表示该函数的参数不是对象

  • 作为对象原型链的终点

undefined:

  • 变量被声明了,但没有赋值时,就等于undefined

  • 调用函数时,应该提供的参数没有提供,该参数等于undefined

  • 对象没有赋值属性,该属性的值为undefined

  • 函数没有返回值时,默认返回undefined,而不是null

5.点击一个input依次触发的事件:

'onmouseenter' :鼠标指针移动到元素上时触发

onmousedown' :鼠标按键被按下时发生

'onfocus' :onfocus 属性在元素获得焦点时触发

'onclick':点击事件完成

6.Vue之单文件组件(已考):

单文件组件是一种扩展名为 .vue 的文件,一个单文件组件可以是一个单独的页面组件,如 home.vue ,也可以是页面中的一个基础组件,如 nav.vue , my-botton.vue

单文件组件的结构:

单文件组件由三部分组成:模板template;脚本script;样式style。

template 里面放置 html 标签代码;

script 里面放 js/ts 代码;

style 放 css/less/sass 代码

7.Vue父子组件的通信(已考):

父组件通过props向子组件传递数据(在子组件中用props接收)

Image.png

子组件通过$emit()向父组件传递数据或事件,如在子组件中自定义事件changeMessage,携带信息, 然后在父组件中通过v-on监听这个事件

Image.png

过程总结: 使用 Props(属性)传递数据: 在父组件中定义一个或多个属性,并将需要传递给子组件的值绑定到这些属性上。 在子组件中使用 props 选项声明接收这些属性,并在模板中使用它们。 通过传递属性的值来向子组件传递数据。

步骤 1.父组件注册子组件

import ChildComponent from './ChildComponent.vue';
export default {
  components: {
    ChildComponent
  },
  // ...
}

2.在父组件的模板中使用子组件

//使用V-bind
<template>
  <div>
    <!-- 使用子组件 -->
    <child-component :data="parentData"></child-component>
  </div>
</template>

//使用V-model
<ChildComponent v-model="childData"></ChildComponent>
import ChildComponent from './ChildComponent.vue';
 export default { 
components: { ChildComponent, 
},
}
  1. 子组件ChildComponent的写法
<template>
  <div>
    <!-- 子组件中使用父组件传递的prop -->
    <p>{{data}}</p>
    <input type="text" :value="data" @input="updateValue($event.target.value)" >
  </div>
</template>


<script>
export default {
  props: ['data']
  //子组件中通过监听input事件,将新的值通过$emit方法发送给父组件
  methods: {
    updateValue(newValue) {
     this.$emit('input', newValue);
    },
}
</script>

8.typeof返回的结果有几种(已考):

typeof一共可以返回6种值,分别是number、boolean、string、function、object、undefined

9. eval()函数(已考):

将传入的字符串当做 JavaScript 代码进行执行:

例子:字符串的相加

console.log(eval('2 + 2'));
// Expected output: 4

10. HTML块级元素及行内元素(已考):

块级元素(div,p,h1):块级元素(block element)在浏览器中占据整行,并排斥其它元素与其位于同一行。也就是说,块级元素的宽度是100%,块级元素可以通过style改变其宽和高

行内元素(span,a,strong,em在浏览器中可以与其它行内元素共占一行,只有当多个元素的总宽度大于浏览器的宽度时,才会换行显示。不可以设置宽(width)和高(height),行内元素的尺寸由其内容决定

注意:img是行内元素还是块级元素?

img 标签没有独占一行,所以是行内元素

既然是行内元素为什么能够设置宽高呢?

img标签属于替换元素,具有内置的宽高属性,所以可以设置

11.CSS垂直方向布局中,子元素溢出父元素

使用overflow属性设置父元素如何处理溢出的子元素

可选值:

             visible 默认值 子元素会从父元素中溢出,在父元素外部的位置显示

             hidden 溢出的内容将会被裁剪不会显示

             scroll 生成两个滚动条,通过滚动条来查看完整的内容

             auto 根据需要生成滚动条

12.子盒子设置margin,溢出父盒子,怎么解决(已考)

margin为外边距,padding为内边距

Image.png

方法:父级元素添加 box-sizing: border-box;

box-sizing的属性对应有三个值

box-sizing: content-box|border-box|inherit;

1.content-box 这应该就是属于默认的,宽度和高度分别应用到元素的内容框,在宽度和高度之外绘制元素的内边距和边框。

2.border-box 为元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制,通过从已设定的宽度和高度分别减去边框和内边距才能得到内容的宽度和高度。

3.inherit 从父元素继承 box-sizing 属性的值

13.display:none和visibility:hidden两者的区别

display与元素的隐藏 如果给一个元素设置了display: none,那么该元素以及它的所有后代元素都会隐藏,隐藏后的元素无法点击,无法使用屏幕阅读器等辅助设备访问,占据的空间消失

visibility: hidden也可以隐藏这个元素,但是隐藏元素仍需占用与未隐藏时一样的空间,也就是说虽然元素不可见了,但是仍然会影响页面布局。

Image.png

14. ==和===的区别

== 代表相同, ===代表严格相同

当进行双等号比较时候: 先检查两个操作数数据类型,如果相同, 则进行===比较, 如果不同, 则愿意进行一次类型转换, 转换成相同类型后再进行比较

===比较时, 如果类型不同,直接就是false.

15.Vue的特点

首先Vue最核心的两个特点,响应式和组件化。

响应式:这也就是vue.js最大的优点,通过MVVM思想实现数据的双向绑定,通过虚拟DOM让我们可以用数据来操作DOM,而不必去操作真实的DOM,提升了性能。且让开发者有更多的时间去思考业务逻辑。

组件化:把一个单页应用中的各个模块拆分到一个个组件当中,或者把一些公共的部分抽离出来做成一个可复用的组件。所以组件化带来的好处就是,提高了开发效率,方便重复使用,使项目的可维护性更强。

虚拟DOM,当然,这个不是vue中独有的

16.浅拷贝和深拷贝

赋值:var b=a (这是赋值,只有变量到变量,不涉及对象,这个不属于浅拷贝也不属于深拷贝)

  • 浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存。
function shallowClone(obj) {
    const newObj = {};
    for(let prop in obj) {
        if(obj.hasOwnProperty(prop)){
            newObj[prop] = obj[prop];
        }
    }
    return newObj;
    }

数组和对象的赋值都是浅拷贝

let arr=[1,2,3] 
let newarr=arr
let newarr=[...arr] //解构赋值,但也不是深拷贝,只是一维数组上成立
newarr.push(4)
//arr和newarr均为[1,2,3,4]
  • 深拷贝(deep copy):复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变

针对对象的深拷贝

Json.parse( Json.stringify())

利用 JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象

手写深拷贝(递归)

var deepClone=function(source){
    const targetObj=source.constructor===Array?[]:{};//外层循环
    for(let key in source){
        if(source.hasOwnProperty(key)){
            targetObj[key]=source[key].constructor===Array?[]:{};
            targetObj[key]=deepClone(source[key])
            }else{
                targetObj[key]=souce[key]
            }
        }
        return targetObj
    }

首先判断源对象的类型,如果是数组,则创建一个空数组作为目标对象,否则创建一个空对象。然后,遍历源对象的属性,如果某个属性是自身的属性(而不是继承来的),则判断其类型,如果是数组,则在目标对象中创建一个空数组,如果是对象,则在目标对象中创建一个空对象,并通过递归调用深度克隆函数将嵌套的对象进行复制。最后,如果属性的值不是对象或数组类型,则直接将其赋值给目标对象

知识点

1.for key in source语法:

for key in 这个方法中key指的是键值对,即 for(var i=0;i<source.length;i++)中i的意思

2.source.constructor === Array和**Array.isArray(source)**均为检验是否为数组的方法

代码核心执行:targetObj[key]=souce[key]

17.js常见的几种排序算法

1.冒泡排序: 从前往后依次比较相邻的两个值,如果前一个大于后一个值就交换位置,一趟之后最大的值就排在最后一位了

function bubbleSort (list) {
  for (let i = 0; i < list.length; i++) {
    for (let j = 0; j < list.length-i; j++) {
      if (list[j] > list[j + 1]) {
        let temp = list[j];
        list[j] = list[j + 1];
        list[j + 1] = temp;
      }
    }
  }
  return list;
}

2.选择排序: 选择排序的基本思路,将数组分为左侧的有序序列如:[a0,…ai],和右侧的无序待排序列[a[i+1],…an],每次将无序序列中选出一个最小值放入到有序序列的最后一个

function selectSort (list) {
  for (let i = 0; i < list.length - 1; i++) {
// 外层循环,遍历列表中的每个元素(除了最后一个元素)
    for (let j = i + 1; j < list.length; j++) {
// 内层循环,从当前元素的下一个位置开始,遍历列表中剩余的元素
      if (list[j] < list[i]) {
// 如果内层循环中的元素比外层循环中的元素小
        let temp = list[i];
        list[i] = list[j];
        list[j] = temp;
      }
    }
  }
  return list;
}

3.sort()方法和reverse()方法

var ar1=[2,4,6,8,1,3]
var ar2=[2,16,36,8,56]
ar1.sort()
ar2.sort()//这个方法值只能排序第一位数  也可以字符串进行排序
var ar1=[2,4,6,8,1,3]
ar1.reverse()//此方法为倒序,也就是反过来。并不会进行大小排序
console.log(ar1)//[3, 1, 8, 6, 4, 2]

18.v-if和v-show的区别

当条件不成立时,v-if不会渲染DOM元素,v-show操作的是样式(display),切换当前DOM的显示和隐藏。

(从Vue源码的角度上来看,v-if的判断应该是发生在template编译成render function的过程中)

19.margin和padding的不同

作用对象不同:padding作用于自身,而margin作用于其外部对象

20.vw和百分比的区别

和父亲元素有关:百分比有继承关系,是基于父亲对象的大小而设置

vw只与屏幕的尺寸有关

21.三类IP地址的范围

A 类 IP 地址:

* 范围:1.0.0.0 到 126.0.0.0
* 分配规则:A 类地址主要用于大型组织或国家级网络,可容纳最多 1677 7214 208 个主机。

B 类 IP 地址:

* 范围:128.0.0.0 到 191.255.0.0
* 分配规则:B 类地址常用于中型组织或大型企业,可容纳最多 65534 个主机。

C 类 IP 地址:

* 范围:192.0.0.0 到 223.255.255.0
* 分配规则:C 类地址通常分配给小型组织或私人网络,可容纳最多 254 个主机。

22.Float

CSS 的 Float(浮动),会使元素向左或向右移动,其周围的元素也会重新排列。

一个浮动元素会尽量向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止

题目描述:html结构如下

<div class="box">
        <div class="box1"></div>
        <div class="box2"></div>
        <div class="box3"></div>
    </div>
<style>
    .box{
        float: left;
        height: 500px;
        width: 500px;
        border:1px dashed #000;
    }
    .box1{
        float: right;
        height: 100px;
        width: 100px;
        border:1px dashed #000
    }
    .box2{
        height: 100px;
        width: 100px;
        border:1px dashed rgb(219, 27, 27)
    }
    .box3{
        height: 100px;
        width: 200px;
        border:1px dashed rgb(26, 220, 59)
    }
</style>

这会使得box2和box3在y轴方向上排列,显示在box左侧,box1单独显示在box的右侧

解析:块级元素在浏览器中占据整行,并排斥其它元素与其位于同一行,使用float使得其尽量向左或向右移动,但依然不会在同一行显示

除非使用diplay:flex才可以将 .box 容器设置为 Flexbox 布局。这样,.box1 和 .box2 会自动水平排列在一行

23.浏览器输入URL后回车会发生什么

www.baidu.com

https:传输协议,他和http的区别在于会加密,在http和TCP直接加了一个安全层

www:万维网

baidu.com:域名

注意:URL只是真实IP地址的一个映射

www.baidu.com 映射的是192.168.10.xx这个IP地址

URL的解析流程

  1. 协议(Scheme)解析:解析 URL 中的方案部分,即 "https"。该方案指定了要使用的协议或应用程序,例如 HTTP、HTTPS、FTP 等。

  2. 主机(Host)解析:解析 URL 中的主机部分,主机是 "baidu.com"。

  3. 端口(Port)解析:如果 URL 中指定了端口,例如在 "www.baidu.com:8080" 中的 ":8080",则会解析出该端口号。如果未指定端口,那么将使用默认的端口,如 HTTP 默认端口为 80,HTTPS 默认端口为 443

  4. 路径(Path)解析:解析 URL 中的路径部分,即 "/"。路径指定了要访问的资源在服务器上的位置。在这个例子中,路径为根路径

具体流程

  1. DNS 解析:应用层http到IP地址的转换,这个过程就是DNS解析
  2. TCP 连接
  3. 发送 HTTP 请求
  4. 服务器处理请求并返回 HTTP 报文
  5. 浏览器解析渲染页面
  6. 连接结束

Image.png

24.原型和原型链

在JavaScript中,每个对象都有一个原型(prototype),它定义了该对象的属性和方法。原型是一个对象,其他对象可以通过原型继承(prototype inheritance)来共享它的属性和方法

原型(Prototype):每个函数都有一个隐藏的内部属性 [[Prototype]],它指向另一个对象,我们称其为原型对象。可以通过 Object.getPrototypeOf(object) 或 proto 访问对象的原型。原型可以放一些属性和方法,共享给实例对象使用

例子:Array具有原型方法 Array.prototype.splice,则new的对象arr也具有该原型

proto:每个对象都有一个__proto__属性,指向了原型prototype

arr.proto===Array.prototype 这个过程就叫做原型链

25.防抖和节流

1.防抖:触发事件后n秒内函数只执行一次,若在n秒内再次触发则重新计算(延迟一段时间执行该事件处理函数)

2.节流:连续发生的事件在n秒内只执行一次函数

区别:防抖是在当事件触发后,延迟一定时间(例如300毫秒)执行;

节流是当事件触发后,立即执行该事件的操作,并在一定时间内禁止再次触发该事件

使用场景举例:

  • 防抖(Debounce):当用户在搜索框中不断输入关键字时,应用防抖来降低请求频率,待用户停止输入一段时间后再发送搜索请求。
  • 节流(Throttle):当用户快速滚动页面时,使用节流来限制触发频率,以保证页面滚动的流畅性。

26.promise(重点)

Promise 是一个对象它代表了一个异步操作的最终完成或者失败。由于它的then方法和catch、finally方法会返回一个新的Promise所以可以允许我们链式调用,解决了传统的回调函数冗余问题。

有resolve和reject两种状态,resolve 和 reject 是 Promise 的执行函数,而 onFulfilled 和 onRejected 是 then() 方法中的回调函数参数。

问题:promise中catch和reject有什么区别

catch:catch 是 Promise 对象上的方法,用于捕获 Promise 链中的错误,并执行相应的错误处理逻辑。它通常用于 Promise 链的最后部分,用于处理前面任何一个 Promise 的 rejected(失败)状态

reject 是Promise 的执行函数之一,用于将 Promise 标记为 rejected(失败)状态,并传递一个错误原因给下一个catch方法的回调函数

问题:页面中有很多异步任务,会影响我们浏览网页,怎么解决和处理

答:防抖、节流、Promise、async/await 等,来管理异步任务的执行顺序

问题:promsie和async/await的区别

相同点:Promise 和 async/await 均是 JavaScript 中处理异步操作的方法。

区别:Promise 是一种用于处理异步操作的对象,它表示一个异步操作的最终完成(或失败)及其结果值。调用 then 方法来处理异步操作成功的情况,并使用 catch 方法来处理错误情况;

async/await是一种是基于 Promise 的语法糖

async 关键字声明一个函数为异步函数,这样函数内部就可以使用** await 关键字来等待异步操作的结果

总结:Async/Await 基于 Promise,使用起来更简洁、直观,提供一种更高效的方式来处理异步操作

重点:promise.all

问题:有两个异步请求,要他们两个异步请求的结果得到后再执行第三个请求

解决方法: 将两个异步请求用 await 进行等待并获取它们的结果。然后,使用Promise.all同时等待这两个结果的完成。最后,在 Promise.all 返回的 Promise 上使用 await 等待并获取它们的结果,并将结果传递给第三个请求。

async function executeRequests() {
    const result1 = await asyncRequest1();
    const result2 = await asyncRequest2();
    // 同时等待两个异步请求的结果完成
    
    await Promise.all([result1, result2]);
    
    // 执行第三个请求并传入参数
    const result3 = await asyncRequest3(param);
    return result3;
  }

Promise为什么能链式调用

由于它的then方法和catch、finally方法会返回一个新的Promise对象所以可以允许我们链式调用

27.CSS的动画有哪些,怎么实现

css实现动画主要有3种方式,第一种是:transition实现渐变动画,第二种是:transform转变动画,第三种是:animation实现自定义动画

1.transition实现渐变动画:指定元素的宽度在500毫秒内从100像素变为200像素

.element {
       width: 100px;
    transition: width 0.5s;
  }

  .element:hover {
   //鼠标移动上去触发
    width: 200px;
  }

2.transform转变动画(一般和transition一起使用)

.element {
    width: 200px;
    height: 200px;
    background-color: red;
    transition: transform 0.5s;
  }
  .element:hover {
    transform: rotate(45deg) scale(1.2);
  }

transition: transform 0.5s :在0.5秒内进行transform操作

transform: rotate(45deg) scale(1.2) :旋转45度和缩放到原来大小的1.2倍

28.JS的作用域

JavaScript中的作用域是指变量、函数和对象的可访问性或可见性。作用域规定了在代码中的哪些部分可以访问或引用特定的变量、函数和对象。

在JavaScript中,有以下几种作用域类型:

  1. 全局作用域(Global Scope):全局作用域是在整个代码中都可访问的作用域。在全局作用域声明的变量和函数可以在代码的任何地方使用。

  2. 函数作用域(Function Scope):函数作用域是在函数内部可访问的作用域。在函数作用域声明的变量和函数只能在函数内部使用,外部无法访问。

  3. 块级作用域(Block Scope):块级作用域是在代码块(通常是由花括号{}包围的代码)内部可访问的作用域。在ES6之前,JavaScript只有全局作用域和函数作用域,并没有块级作用域。但是,在ES6中引入了let和const关键字,它们引入了块级作用域的概念

重点:\color{red}重点:

外部不能访问内部的元素,内部可以访问外部的变量

查找时候优先查找内部的,内部的找完再去找外部的

apply,call,bind的共同点和区别\color{red}apply,call,bind的共同点和区别

共同点:都是函数对象的方法,可以用来改变函数的执行上下文(即函数内部的 this 值)以及传递参数 显式地设置函数的执行上下文

区别:

  1. apply: 该方法允许你在指定的作用域内调用函数,并传递一个数组或类似数组的对象作为参数。具体语法如下: function.apply(thisArg, [argsArray])
  • thisArg:函数执行时的上下文对象,即函数内部的this的值将被设置为该对象
  • argsArray:一个类数组对象或数组,包含要传递给函数的参数
function greet() {
    console.log(`Hello, ${this.name}!`);
  }
  const person = {
    name: 'John',
  };
  // 使用 apply 方法来将 greet 函数与 person 对象绑定
  greet.apply(person); // 输出:Hello, John!
  1. call: call 方法参数需要以逗号分隔的形式传递而不是数组。

具体语法如下function.call(thisArg, arg1, arg2, ...)

*thisArg:函数执行时的上下文对象,即函数内部的 this 的值将被设置为该对象。

*arg1, arg2, ...:要传递给函数的参数列表,逗号分隔。

3.bind: bind 方法返回一个新函数,该函数与原始函数具有相同的主体和作用域,但可以绑定到指定的对象上。调用绑定函数时,将在指定对象上调用原始函数。具体语法如下: function.bind(thisArg, arg1, arg2, ...)

  • thisArg:函数执行时的上下文对象,即函数内部的 this 的值将被设置为该对象。

  • arg1, arg2, ...:要预先传递给原始函数的参数列表。

29.CSS中position的属性

  1. static(默认值):元素遵循正常的文档流布局,并忽略 top、bottom、left、right、z-index 属性。

  2. relative:相对定位元素。元素相对于其正常位置进行定位,可以通过 top、bottom、left、right 属性调整元素的位置。元素先放置在未添加定位时的位置,再在不改变页面布局的前提下调整元素位置。相对定位仍遵循文档流布局,即元素仍占据原来的空间

  3. absolute:绝对定位元素;元素会被移出正常文档流,并不为元素预留空间相对于其最近的非默认定位的祖先元素进行定位如果没有非默认定位的祖先元素,则相对于文档进行定位。通过 top、bottom、left、right 属性,可以将元素放置在指定位置。

注意:window > document > html > body

  1. fixed:元素相对于视口进行定位,即无论页面滚动与否,元素都会固定在指定位置。也可以使用 top、bottom、left、right 属性调整元素的位置。

  2. sticky:元素在跨越特定阈值前为相对定位,之后为固定定位。通过设置 top、bottom、left、right 等属性,在元素跨越阈值前后进行切换。

30.JS里面怎么判断数据类型

typeof可以判断基础数据类型,typeof不能判断数组对象,typeof 运算符将数组视为对象

const arr = [1, 2, 3]
console.log(typeof arr); // 输出 "object"

instanceof 可以判断引用数据类型数组和对象

const arr = [1, 2, 3]; 
console.log(arr instanceof Array); // 输出 true

obj.constructor===Array

Array.isArray(obj)

31.JS中绑定事件的几种方式,addEventListener有几个参数,第三个参数是什么(冒泡和捕获)

  1. HTML 属性绑定:可以在 HTML 元素上直接使用属性来绑定事件,例如:

<div><button onclick="testClick()">点击</butt0n></div>

  1. dom绑定:可以通过获取dom节点来绑定事件,例如:
const button=document.querySelector("button")
button.onclick=fun
  1. addEventListener
const button = document.querySelector('button'); 
button.addEventListener('click', myFunction);

addEventListener方法有两个必需参数和一个可选参数。第一个参数是要监听的事件类型(比如click、mouseenter等),第二个参数是事件发生时要执行的处理函数。而第三个参数则是一个配置对象,是可选的。

第三个参数涉及到冒泡和捕获,是true时为捕获,是false则为冒泡。

childId.addEventListener('click',function(){ alert('子级1'); },false);当我们点击子级1文本时,会先触发childId点击事件,弹出“子级”,向上冒泡执行parent事件

冒泡和捕获应用的场景\color{red}冒泡和捕获应用的场景

事件委托:通过将事件绑定在父元素上而不是每个子元素上,在点击子级时候,通过冒泡法,去获取target,利用事件冒泡的机制来处理子元素的事件。这样可以减少事件监听器的数量,提高性能。特别适用于多个子元素需要相同行为响应的场景,例如列表、表格等。

JS事件流(已考) 事件流,描述的是页面中接受事件的顺序。

window=>dom=>html=>body=>div的一个过程,父亲节点到子节点使用的是捕获方法,子节点到父亲节点使用的是冒泡方法

vue阻止事件冒泡(stop\color{red}vue阻止事件冒泡(stop)

<div @click="parentClick">
    <button @click.stop="childClick">点击我</button>
  </div>

@click.stop 修饰符会阻止事件继续冒泡到父元素。这意味着只会触发子元素的点击事件,并且父元素的点击事件不会被触发。

当点击子元素按钮时,控制台将仅输出 '子元素被点击',而不会输出 '父元素被点击'。

32.Vue生命周期

Vue生命周期即生命周期钩子函数,这些钩子函数允许你在不同的阶段执行自定义的代码,最常见的为

mounted:【页面加载完毕的时候就是此时】默认情况下,在组件的生命周期中只会触发一次

created——mounted——updated——destroyed

  1. beforeCreate:在 Vue 实例被创建之前调用。此时实例的数据观测和事件还未初始化。

  2. created:在 Vue 实例创建完成后立即调用。在这个阶段,实例已经完成了数据观测、属性和方法的运算,但尚未挂载到 DOM 中。

  3. beforeMount:在 Vue 实例挂载到 DOM 元素之前调用。在这个阶段,Vue 将编译模板为渲染函数,并将其应用到实例的 render 上。

  4. mounted:在 Vue 实例挂载到 DOM 元素后调用。在这个阶段,实例已经完成了模板的渲染,并将数据和方法都挂载到了 DOM 中,可以进行 DOM 相关的操作。

  5. beforeUpdate:在数据更新之前调用,发生在虚拟 DOM 重新渲染和打补丁之前。

  6. updated:在数据更新之后调用,发生在虚拟 DOM 重新渲染和打补丁之后。需要注意的是避免在此钩子函数中修改数据,以防止无限循环的更新。

  7. beforeDestroy:在实例销毁之前调用。在这个阶段,实例仍然完全可用。

  8. destroyed:在实例销毁之后调用。在这个阶段,实例已经被完全销毁,所有的事件监听器和子实例也都被移除。

Image.png 销毁阶段,可用于一些定时器或订阅的取消

created() {
    // 创建定时器
    this.timer = setInterval(() => {
        this.currentTime = new Date().toLocaleTimeString();
      // 定时器回调逻辑
    }, 1000);

  },
  beforeDestroy() {
    // 在销毁前清除定时器
    clearInterval(this.timer);
  }

33.nextTick()的原理

this.$nextTick() 是 Vue.js 框架提供的一个方法,用于在 DOM 更新完成后执行回调函数。

只有dom更新完毕后才会加载nextTick里面的内容

nextTick()用于将回调函数推迟到下一个事件循环中执行

34. 算法二分查找及其时间复杂度,只用说思路

二分查找的函数接受一个有序数组和一个待查找的值,返回对应值在数组中的索引

算法二分查找的核心为while(left<right)循环,其时间复杂度为O(logn)

function binarySearch(arr, val) {
    let left = 0;
    let right = arr.length - 1;
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        if (arr[mid] === val) {
            return mid;
        }
        if (arr[mid] < val) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}

35.CSS选择器的优先级是怎么计算的

内联样式>id样式>类样式>行内样式/伪类

1697545381394.png

36.echarts的使用

<div id="waterChart" style="height: 520px; width:100%"></div>
const chart = echarts.init(document.getElementById('waterChart'));
const option = {
        xAxis: {
          name: '日期',
          type: 'category',
          data: xData,
        },
        yAxis: {
          name: '水位(单位:m)',
          type: 'value',
        },
        series: [{
          type: 'line',
          data: yData,
        }],
      };
chart.setOption(option);

37.水平垂直居中

display: flex;
justify-content: center;//水平
align-items: center;//

38.快速排序算法的原理是什么

分治思想

基本思想是选择一个基准数,将数组分成两个区域,左边区域所有数都小于等于基准数,右边区域所有数都大于等于基准数,然后对左右区域分别递归地进行快速排序,最终将整个数组排序。

function quickSort(arr) {
    if (arr.length <= 1) {
        return arr;
    }

    const pivotIndex = Math.floor(arr.length / 2);
    const pivot = arr[pivotIndex];
    const left = [];
    const right = [];

    for (let i = 0; i < arr.length; i++) {
        if (i === pivotIndex) {
            continue;
        }

        if (arr[i] < pivot) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }

    return [...quickSort(left), pivot, ...quickSort(right)];
}

39.vue中computed和watch的区别

computed适合基于已有响应式数据进行计算,而watch适合监听特定数据变化并执行相应操作

因此watch一般使用方法对监听的数据进行计算之外的操作

(1)computed

1、 支持缓存,只有依赖数据发生改变,才会重新进行计算

2、 不支持异步,当computed内有异步操作时无效,无法监听数据的变化;

3、 computed是计算属性,也就是依赖某个值或者props通过计算得来的数据;

4、 computed的值是在getter执行之后进行缓存的,只有在它依赖的数据发生变化(依赖的数据可以是单个,也可以是多个)时,会重新调用getter来计算;

(2)watch

1、不支持缓存,数据变,直接会触发相应的操作

2、支持异步操作

3、watch是监听器,可以监听某一个数据,然后执行相应的操作;

4、监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值

40.闭包(已考)

里层函数和外层函数一起构成了闭包,内层函数可以使用外层函数的变量

闭包不一定要有return,return后可以让外部使用函数内定义的值

闭包的作用返回这个函数的引用,从而可以访问函数内部的变量和参数

function outer1(){
    var a=1;
    return function fn(){
        console.log(a+1)
    }
}
const fn=outer1();
a=80
fn()//即使变量修改后,仍然只输出2,通过调用fn()访问函数内部的变量和参数

总结:闭包可以使得外部使用函数内部的变量,但无法修改它,实现了数据的私有

注意:闭包会引起内存泄漏

41.vue渐进式框架

从开始的vue到vuex element-ui router越来越大 也就是框架从小到大的意思

42.keep-alive

keep-alive是vue自带的一个组件,用于进行页面的缓存,从而提升性能

首页进入详情页,每次点击都是相同的,就没有必要再请求了

问题:keep-alive 缓存过的页面,跳转到另外一个页面后再用后退键返回还会重新刷新页面吗?

后退功能会让浏览器重新请求之前访问过的页面,而这个页面可能已经被从缓存中移除了。因此,即使该页面本来是被缓存过的,也会重新加载和渲染页面。

问题:如果不用 keep-alive,页面切换后还想让原来表单的一些填写好的数据不消失,能想到什么办法吗

通过 localStorage 或 sessionStorage 对页面状态进行保存和恢复

保存页面状态:

在合适的时机(例如页面卸载前)将需要保存的状态数据存储到 localStorage 或 sessionStorage 中。可以使用 setItem() 方法将数据存储为键值对的形式。

localStorage.setItem('pageState', JSON.stringify(stateData));

恢复页面状态:

在页面加载时(例如 created() 钩子函数中)从 localStorage 或 sessionStorage 中获取之前保存的状态数据,并将其应用到页面上。

const savedState = localStorage.getItem('pageState');
if (savedState) {
  this.pageState = JSON.parse(savedState);
}

43.ref是什么

ref是通过标签获取dom的

42.scope原理

让css样式只在本组件内生效,而不影响其他组件

43.display有那些值

none:此元素不显示

block:转为块元素

inline:默认,将其元素转换为内联元素

flex:弹性布局

44.清除浮动的方式

1.触发BFC可以变相清除浮动

BFC(块级格式化上下文)是 Web 页面中一个重要的布局概念,用于控制盒模型的排列和定位。

overflow 属性为除了 visible 以外的值(overflow: auto、hidden 或 scroll)当元素的 overflow 属性设置为除了 visible 以外的值时,会创建一个新的 BFC。

BFC:BFC是一个完全独立的空间(布局环境),让空间里的子元素不会影响到外面的布局

2.element添加伪元素标签(after方式)

::after 是 CSS 伪元素中的一种,它用于在元素的内容之后插入一个生成的内容(末尾添加)

.clearfix::after {
    content: '';
    display: block;
    clear: both;
  }

45.盒模型的相互转换

1.标准盒模型(W3C 盒模型)到 IE 盒模型的转换:

.element { box-sizing: border-box; }

  1. IE 盒模型到标准盒模型(W3C 盒模型)的转换:

.element { box-sizing: content-box; }

两者的区别在于content的不同,IE盒模型的content包括border、padding

标准盒子模型

盒子总宽度/高度=width/height+padding+border+margin

width/height只是内容高度,不包含padding和border的值

IE盒子模型

盒子总宽度/总高度=width/height+margin=(内容宽度/高度+padding+border)+margin

宽度和高度包含了padding和border的值

46.同步任务和异步任务(已考)

同步任务"就是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务

事件轮询是一种异步编程模型,它用于管理JS中的任务队列,以确保每个任务按顺序执行

"异步任务"是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了

主要的异步任务有

Events:javascript各种事件的执行都是异步任务

setTimeout、setInterval 定时器

XMLHttpRequest(也就是 Ajax)

Promise

async function

任务队列的执行顺序总结如下:

  • 执行同步任务,按照代码顺序依次执行。
  • 遇到异步任务时,将其放入任务队列中,并继续执行后续的同步任务。
  • 当前同步任务执行完毕后,检查微任务队列,并依次执行所有微任务。
  • 微任务执行完毕后,进行 UI 渲染和重排重绘等操作。
  • 进入下一个宏任务,重复上述过程。

注意:异步任务会在后台执行,不会阻塞同步任务的执行,在同步任务执行的稍后进行

47.dom的渲染流程(已考)

解析HTML=>构建dom树=>解析 CSS=>构建渲染树=>确定每个节点在屏幕中的确切位置和大小,也称为回流=>绘制

浏览器是一个边解析边渲染的过程。

DOM节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为relow;当盒模型的位置,大小以及其他属性,如颜色,字体,等确定下来之后,浏览器便开始绘制内容,这个过程称为repain。页面在首次加载时必然会经历reflow和repain。reflow和repain过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少reflow和repain。

48.diff算法

虚拟dom:将dom数据化 可以使用 diff算法来比较两个树结构(例如 DOM 树)之间的差异,并找出最小的更新集合,以减少不必要的操作和提高性能

49.MVVM 和MVC区别

MVVM(Model-View-ViewModel)和MVC(Model-View-Controller)是两种常见的软件架构模式,用于组织和分离应用程序的不同部分。

MVC 架构中,Model(模型)用于处理数据和业务逻辑,View(视图)用于展示数据给用户,Controller(控制器)负责协调模型和视图之间的交互

MVVM 架构中,Model(模型)处理数据和业务逻辑,View(视图)负责展示,ViewModel(视图模型)则是连接模型和视图之间的桥梁,负责将模型数据转化为视图所需的格式

MVVM 架构中,视图和视图模型之间通过双向的数据绑定关系建立联系

总结:MVVM 是一种在前端开发中比较常用的架构模式,而 MVC 则在后端开发和传统的桌面应用程序中更为常见(springboot)

50.vue的双向绑定原理

1.通过object.defineProperty劫持数据发生的改变

劫持的定义:data中的属性值vue对象的属性值保持双向

2.如果数据发生改变了(在set中赋值了),通过update方法进行dom节点的更新(diff算法实现)

51.数据结构(重点,必考)

1.数组(Array)

数组是一种有序的、可变长度的数据集合,可以存储多个不同类型的元素。数组使用索引来访问和修改其中的元素


var arr=[1,2,'小明']

2. 对象(Object)

对象是键值对的集合,每个键对应一个值。键是字符串类型,值可以是任意类型的数据。对象常用于表示实体或复杂的数据结构。

let person = {
    name: 'John',
    age: 30,
    city: 'New York'
  };

3. 集合(Set)

集合是一种无序、唯一元素的数据结构,不允许重复值。可以用来存储和处理一组互不相同的数据。

let set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(3); // 重复元素将被忽略
console.log(set.size); // 输出:3
consloe.log(set)   //输出Set(3) { 1, 2, 3 }

4.映射/字典(Map)

映射是一种键值对的集合,类似于对象,但映射允许键的类型更加灵活,并且保留元素插入的顺序。

let map = new Map();
map.set('name', 'Alice');设置键值对,返回该 Map 对象。
map.set('age', 25);
console.log(map);//输出map(2){'name'=>'Alice';'age'=>25}
console.log(map.get('name')); // 输出:Alice 返回键对应的值,如果不存在,则返回 undefined。
console.log(map.has('age')); // 输出:true

对象方式创建字典

const dictionary = {}; 
// 添加键值对 
dictionary["key1"] = "value1"; 
dictionary["key2"] = "value2"; 

5.栈(Stack)

栈是一种具有后进先出(LIFO)特性的数据结构。只能在栈的顶部添加或删除元素。

简单理解:进就是添加元素,出就是删除元素

入栈的方法:push() 出栈的方法:pop()

栈通过 push 方法在栈顶添加元素,通过 pop 方法从栈顶移除元素。

栈的特点是后进先出(LIFO),意味着最后添加的元素会最先被移除(意思就是后面进来的先出)

pop() 删除数组的最后一个元素,并返回这个被删除的元素

let stack = [];
stack.push('apple');
stack.push('banana');
stack.push('orange');
console.log(stack); // 输出:['apple', 'banana', 'orange']
let topElement = stack.pop();
console.log(topElement); // 输出:orange
console.log(stack); // 输出:['apple', 'banana']

注意: arr转字符串:[].join('')

字符串转数组:str.split('')

6. 队列(Queue)

队列是一种具有先进先出(FIFO)特性的数据结构。只能在队列的末尾添加元素,从队列的开头移除元素。

队列通过push方法在队列末尾添加元素,通过shift方法从队列开头移除元素

队列的特点是先进先出(FIFO),最先添加的元素会最先被移除

let queue = [];
queue.push('apple');
queue.push('banana');
queue.push('orange');
console.log(queue); // 输出:['apple', 'banana', 'orange']
let firstElement = queue.shift();
console.log(firstElement); // 输出:apple
console.log(queue); // 输出:['banana', 'orange']
shift() 删除数组的第一个元素,并返回这个被删除的元素

重点:js任务队列:

js语言是单线程的

同步、异步(计时器,事件(点不点事件不确定),请求)

1.首先执行同步任务,形容对应的堆和栈

2.当有异步任务时,提交给对应的异步进程(axio请求,DOM事件,setTimeout方法)处理

3.异步任务完毕后推入到任务队列(即异步队列)宏任务+微任务,其中微任务(promise.then())的先执行

4.主线程执行完毕后查询任务队列,取出一个任务,推入主线程

5.重复执行(事件循环)

7. 链表(Linked List)

链表是一种由节点组成的数据结构,每个节点包含数据和指向下一个节点的引用。链表可以是单向链表或双向链表。(即多个元素存储的列表)

链表中的元素在内存中不是顺序存储的,是通过next指针联系在一起

node1={name:'小明'}
node2={name:'小红'}
node3={name:'小刚'}
node1.next=node2;
node2.next = node3;
console.log(node1); // 输出{ name: '小明', next: { name: '小红', next: { name: '小刚' } } }

JS中的原型链就是链表结构 node1.__proto__=node2; node2.__proto__ = node3; node3.__proto__ = null;

链表的分类:

单向链表:最后的next指向null

双向链表:链表中的元素不光有next 还有prev

链表的修改 c.next=n

链表的删除 a.next=c(这个方法删除了b节点)

单向链表的创建、遍历、插入、删除

//定义单向链表的节点类
class Node{
    constructor(data){
        this.data = data    //节点的数据域(数据成员)
        this.next = null    //节点的指针域(指针成员)   
    }
}
//定义单向链表类
class SingleLinked{  
    constructor(){
        this.size = 0  //单链表的长度,用来记录链表中的节点个数,为一个空链表
        this.head = new Node('head')  //是链表的头指针:记录链表的起始地址
        this.currentNode = ''  //用来记录当前节点
    }
    //获取链表的长度
    getLength(){
        return this.size
    }
    //判断链表是否为空
    isEmpty(){
        return this.size === 0   //如果this.size为0则说明链表为空,即返回true
    }
    //遍历链表:不重复的访问链表中的每一个节点
    displayList(){
        var list = ''
        var currentNode = this.head  //指向链表的头指针
        while(currentNode){  //若当前节点不为空,则执行循环
            list+=currentNode.data    //连接节点的数据域
            currentNode = currentNode.next  //让当前指针指向当前节点的下一个节点
            if(currentNode){   //如果currentNode不为空则加上连接符
                list += '->'  //链表节点的连接符
            }
        }
        console.log(list)
    }
    //获取链表的最后一个节点
    findLast(){
        var currNode = this.head
        while(currNode.next){   //若当前节点的next域为空,则他是链表的最后一个节点,跳出循环
            currNode = currNode.next  //若当前节点的next域不为空则让指针指向当前节点的下一个节点
        }
        return currNode
    }
    //采用尾插法给链表插入元素
    appendNode(element){
        var currNode = this.findLast()  //找到链表的最后一个节点
        var newNode = new Node(element)  //创建一个新的节点
        currNode.next = newNode
        newNode.next = null
        this.size++   //链表的长度加1
    }
    //删除链表中的一个节点
    delete(element){
        //this.displayList()
        var currentNode = this.head
        try{
            while((currentNode.next!=null)&&(currentNode.next.element!=element)){  //判断,如果节点靠后则节点的next的next为空,不为空时进行删除
            if(currentNode.next.data === element){
                currentNode.next = currentNode.next.next    
                this.size--
            }else{
                currentNode = currentNode.next
                }
            }
        }
        catch(e){   //测试函数,判断函数的运行错误
            console.log(e)
        }
    }
}

8. 树(Tree)

树是一种分层的数据结构,由节点和边组成。树具有一个根节点,每个节点可以有多个子节点。

重点:手写树

class TreeNode {
  value:number
  children:TreeNode[]//树其实就是一种自指

  constructor(value:number) {
    this.value = value;
    this.children = [];
  }
  addChild(node:TreeNode) {
    this.children.push(node);//树其实就是一种自指
  }
}
// 创建根节点
const root = new TreeNode(1);
// 创建子节点
const child1 = new TreeNode(2);
const child2 = new TreeNode(3);
// 将子节点添加到根节点
root.addChild(child1);
root.addChild(child2);
console.log(root)
//输出TreeNode {
  value: 1,
  children: [
    TreeNode { value: 2, children: [] },
    TreeNode { value: 3, children: [] }
  ]
}

9. 哈希表

hash其实就是一大堆不能理解的编码,用于加密

字典与哈希表的区别:

1.如果找key对应的value需要遍历key,用哈希表可以省去遍历过程

2.字典是根据添加的顺序进行排列,hash表不是添加的顺序进行排列

重点:手写hash表

class HashTable{
    table:string[]

    constructor(){
        this.table=[];
    }
    hashCode(key:string):number{
        let hashKey:number =0
        for(var i=0;i<key.length;i++){
            hashKey+=key.charCodeAt(i)//这是一个编码过程,通过code码得到hashKey
        }
        return hashKey
    }//编码哈希,加密

    set(key:string,value:string):void{
        let hashKey:number=this.hashCode(key);
        this.table[hashKey]=value
    }
    get(key:string):string{
        let hashKey:number=this.hashCode(key);
        return this.table[hashKey];
    }
}
let hashTable=new HashTable();
hashTable.set('name','戴立龙')
hashTable.set('age','24')
console.log(hashTable)
//HashTable {
  table: [ <301 empty items>, '24', <115 empty items>, '戴立龙' ]
}
console.log(hashTable.get('age'))

哈希表的hash code出现重叠的解决办法

当哈希表的哈希码(hash code)出现重叠时,可能会导致哈希冲突,即多个键被映射到同一个桶中。这会影响哈希表的性能和效率

最常用的方法:链地址法(Chaining): 在每个桶中维护一个链表或者其他数据结构,用于存储所有哈希冲突的键值对。当发生冲突时,新的键值对会添加到对应桶的链表中。这种方法不会造成聚集性冲突,但需要额外的存储空间来存储链表的节点。

10. 堆(Heap)

堆是一种特殊的树状数据结构,其中每个节点的值都大于或小于其子节点的值。堆常用于实现优先队列等应用。

11.hashMap和hashTable的区别(已考)

①键的类型

HashMap允许键的类型可以是任意的,包括基本数据类型和对象。而HashTable只能使用字符串作为键

②性能

HashMap的性能通常比HashTable更好。因为HashMap不需要在计算哈希值之前将键转换为字符串,而HashTable则必须对所有键进行字符串化处理

③null键与null值

null键和null值:HashMap允许使用null作为键,并且可以存储null值。 HashTable不允许使用null键或null值,否则会抛出NullPointerException

51.vue怎么使用v-for和key渲染组件

<template>
    <div>
      <my-component v-for="item in items" :key="item.id" :data="item" />
    </div>
  </template>
  <script>
  import MyComponent from './MyComponent.vue';
  export default {
    components: {
      MyComponent
    },
    data() {
      return {
        items: [
          { id: 1, name: 'Item 1' },
          { id: 2, name: 'Item 2' },
          { id: 3, name: 'Item 3' }
        ]
      };
    }
  };
  </script>

在v-for指令中,我们使用items数组进行遍历,并将每个数组元素命名为item。通过 :key属性,我们使用item.id作为唯一的键来帮助Vue跟踪和优化渲染

注意:使用循环项的唯一标识作为key,确保每个子组件都有一个唯一的标识,并且在更新渲染时可以正确地跟踪和识别它们,避免所有的dom均被渲染

问题:v-for和v-if可以用在一个标签上吗

可以

在 Vue.js 中,v-for 用于循环渲染列表数据,而 v-if 用于条件性地显示或隐藏元素。当 v-for 和 v-if 同时使用时,它们的执行顺序以及对应的结果可能会影响组件的性能。

默认情况下,Vue.js 会优先处理带有 v-for 的循环,然后再根据条件进行渲染。这意味着 v-if 中的条件语句会在每次循环迭代时都进行计算。如果列表数据较大且条件判断复杂,则可能会导致性能问题。

问题:for...in 和 for...of的区别

for...in 用于遍历对象属性,for...of 用于遍历可迭代对象的元素

由于 for-in 循环会遍历对象的原型链,因此可能会迭代到不是自身属性的属性。为了避免这种情况,我们可以使用 hasOwnProperty 方法来检查属性是否为对象自身的属性。

const obj = {a: 1, b: 2, c: 3};
for (let prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    console.log(prop); // 'a', 'b', 'c'
  }
}

for-of 循环是一种迭代可迭代对象的方法。它可以用于遍历数组、字符串、Map、Set、TypedArray 等可迭代对象。通常,我们使用 for-of 循环来遍历数组或字符串的值。

for-of 循环不适用于迭代对象属性{},会抛出 TypeError 异常

52.前后端解决跨域问题的方法

之所以会出现跨域现象,是因为受到了同源策略的限制,同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。

Image.png

CORS为跨域资源共享。CORS是一种基于HTTP头部协议的跨域解决方案,在服务器端设置相关的响应头信息,允许客户端进行跨域请求。 在服务器端,需要设置Access-Control-Allow-Origin和Access-Control-Allow-Methods等相关的响应头信息

Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Allow-Methods: GET, POST

后端解决跨域

@CrossOrigin

@CrossOrigin(origins = "*", maxAge = 3600)

前端代理解决跨域

前端 CORS(跨域资源共享):CORS 是一种跨域解决方案,它是 W3C 标准,通过在服务器端设置响应头中的Access-Control-Allow-Origin 字段来告诉浏览器是否允许跨域访问。CORS 支持各种类型的 HTTP请求方法,比 JSONP 更加灵活。

vue框架会有 vue.config.js / vite.config.js 文件,文件中会有proxy字段

实现展示:想将 Axios 中的 /api/model 代理到 http://localhost:8081/model

1.vue.config.js里面设置

  devServer:{
    port:9091,
    proxy: {
      '/api': {
        target: 'http://localhost:8081',
        changeOrigin: true,
        pathRewrite: { '^/api': '' }
      }
    }
    }

2.axios请求写法

axios.get('/api/model').then((res) => {
       ...
        } else {
          // 失败的提示
          this.$message.error(res.data.msg);
        }

pathRewrite 表示将所有以 /api 开头的请求路径重写为空字符串,即将 /api/model 重写为 /model。这样,当你在 Axios 中调用 /api/model 时,实际上会发出一个请求到 http://localhost:8081/model

注意:在使用 Vue CLI 的开发服务器时,如果你修改了配置文件(如 vue.config.js),需要重启开发服务器才能使修改生效