2025最新面试题来啦!

306 阅读31分钟

2025最新面试题来啦!

前言:

讲讲苦逼前端仔为啥又要面试的背景吧哈哈哈,就当自己的碎碎念。呆了两年的公司,被迫转型当项目经理,并不是我们理解的那种可以带项目开发的那种,是整不完的各种材料,跪舔甲方,以及干了活还不被领导认可产出的苦逼牛马。所以说还是该换换环境吧,最近大环境不咋滴,投了一周的简历也才一两个面试。整理一下自己碰到的面试题吧,希望能对大家也有所帮助。对了,现在的我也是中级前端牛马了毕竟干了两三年不能再面初级岗了,大家一起加油!有内推的哥们欢迎私信!!!

面试题:

1. 说说做过哪些项目、项目之中碰到的难点、以及如何解决的?

    • 基建项目:Vue2到Vue3迁移

想要重构vue2项目,那就要从里到外都要升级重构一遍。

选择合适的构建工具:Vue3 推荐使用 Vite 作为构建工具,其热更新速度更快,开发效率更高。

安装 Vite:使用 npm 或 yarn 安装 Vite。

创建 Vue3 项目:使用 Vite 创建一个新的 Vue3 项目。

迁移代码:将 Vue2 项目的 src 目录下的内容复制粘贴到新创建的 Vue3 项目中。

修改配置文件:根据项目需求修改 Vite 配置文件。

测试和优化:测试项目功能,并对代码进行优化。

渐进式迁移

  • 使用@vue/composition-api逐步替换Options API
  • 构建双版本并行运行环境

创建两个版本环境逐步将依赖配置添加,建立好相应路由然后直接迁移代码,最后根据对两个环境的页面进行功能测试,修改相应vite配置进行优化。

首先讲讲为什么会有vue2项目重构到vue3的背景
对于网页端项目,访问速度越快、用户操作越流畅就越牛逼,代码的简洁程度也能影响一个网站的seo优化。
1. 速度更快:
Vue3 通过重写虚拟 DOM 实现和编译模板优化,实现了性能上的显著提升。以下是一些具体的数据:

  **组件初始化**:Vue3 的组件初始化性能比 Vue2 提高了 50%。  

  **更新性能**:Vue3 的更新性能比 Vue2 提高了 1.3-2 倍。  

  **SSR**:Vue3 的服务器端渲染速度比 Vue2 提高了 2-3 倍。
  
2. 体积更小:
Vue3 通过 Webpack 的 tree-shaking 功能,将无用模块剪辑,仅打包需要的代码,从而减小了包体积。以下是 Vue3 包体积的对比数据:

 **Vue2**:约 20-30 KB(压缩后)  

 **Vue3**:约 10-15 KB(压缩后)

3. 更易维护

Vue3 引入了 Composition API,简化了组件逻辑的组织,提高了代码的可读性和可维护性。以下是一些具体的变化:

 **Composition API**:允许开发者将组件逻辑分解为更小的函数,方便复用和维护。  
 **Options API**:Vue3 支持与 Options API 一起使用,方便开发者逐步迁移。  


4. 更好的 TypeScript 支持

Vue3 基于 TypeScript 编写,提供了自动的类型定义提示和编译器优化,使得开发者能够更方便地使用 TypeScript 进行开发。
  

2.平时项目之中有遇到过跨域问题吗,什么场景下遇到的呢,又是如何解决的呢。

回答:

一、跨域问题产生原因

  1. 同源策略限制
    浏览器安全机制要求协议、域名、端口三要素完全一致才能直接交互,否则会触发跨域拦截。

  2. 典型场景

    • 前后端分离开发时本地调试(localhost:8080 → api.domain.com)
    • 调用第三方API服务(your-site.com → weather-api.com)
    • 不同协议或子域访问(http → https 或 app.domain.com → api.domain.com)
1. CORS(推荐方案)
javascript
// 服务端设置响应头
Access-Control-Allow-Origin: *  // 允许所有域
Access-Control-Allow-Methods: GET,POST  // 允许的请求方法
Access-Control-Allow-Headers: Content-Type  // 允许的请求头

*特点:需要后端配合,支持所有请求类型,安全可控

2. JSONP(传统方案)
javascript
// 前端
<script>
function callback(data) {
  console.log('Received:', data)
}
</script>
<script src="https://api.com/data?callback=callback"></script>

// 服务端返回
callback({ "result": "data" })

*特点:仅支持GET请求,存在XSS风险

3. 代理服务器
javascript
// Vue开发环境配置(vue.config.js) 
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://target-domain.com',
        changeOrigin: true  // 修改请求头host
      }
    }
  }
}

*特点:开发环境常用,生产环境需配合Nginx

4. Nginx反向代理
location /api/ {
  proxy_pass http://backend-server:8080/;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
}

*特点:生产环境推荐方案,性能更好

5. 浏览器临时方案(仅开发用)
# Chrome启动参数(Windows)
chrome.exe --disable-web-security --user-data-dir=C:\temp

*特点:完全禁用安全策略,仅限本地调试

3.前端网页的渲染机制。

一、关键渲染流程

  1. HTML解析与DOM构建

    • 字节数据 → 字符串 → Token → DOM节点 → DOM树
    • 遇到<script>标签会暂停解析,等待JS加载执行完成
    • 预解析线程提前下载CSS/JS资源
  2. CSS解析与CSSOM构建

    • 解析CSS规则生成样式规则树
    • 与DOM树并行构建但会阻塞渲染
  3. 渲染树合成

    • 合并DOM树与CSSOM树,过滤不可见节点(如display:none
    • 形成包含样式和布局信息的渲染树(Render Tree)
  4. 布局阶段(回流)

    • 计算元素精确位置和尺寸(盒模型计算)
    • 首次布局称为"布局",后续布局调整称为"回流"
  5. 绘制与合成

    • 分层绘制(Layer)→ 光栅化(Raster)→ 合成(Composite)
    • GPU加速的动画会启用独立合成层

二、关键优化机制

  1. 阻塞特性

    • CSSOM构建会阻塞渲染(建议将CSS放在头部)
    • JS执行会阻塞DOM解析(建议使用async/defer)
  2. 重绘与回流

    • 回流必定触发重绘(几何属性变化触发回流)
    • 避免强制同步布局(如频繁读取offsetHeight)
  3. 现代浏览器优化

    • 增量式布局(Incremental Layout)
    • 合成线程独立于主线程(CSS动画建议使用transform/opacity)

4.后端反了10万条数据,存在一个数组之中,数组中有a,b,c三个字母如何最快找出他们的下标。

方法一:单次遍历法(最优解)


const targets = new Set(['a', 'b', 'c']);
const result = [];
for(let i=0; i<arr.length; i++) {
//使用ES6的Set数据结构存储目标字符,利用其O(1)时间复杂度的`has()`方法实现快速存在性检测
  if(targets.has(arr[i])){
  //当检测到当前元素属于目标集合时,立即将当前索引i存入结果数组。
  result.push(i);
  } 
}

5.vue2和vue3的区别。

1.首先谈谈生命周期。 vue3的生命周期相较于vue2并没有太大区别,vue2的生命周期前加上'on' .有一点需要特别注意一下,Vue3 在组合式API(Composition API)中使用生命周期钩子时需要先引入,而 Vue2 在选项API(Options API)中可以直接调用生命周期钩子。

Tips: setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地去定义。

2、组合式api Vue2是选项API(Options API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期钩子等),导致代码的可读性变差。当需要修改某个逻辑时,需要上下来回跳转文件位置。

Vue3组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。所有逻辑在setup函数中,使用 ref、watch 等函数组织代码。

3、setup函数 setup函数是组合式API的入口函数,默认导出配置选项,setup函数声明,返回模板需要数据与函数。

setup 函数是 Vue3 特有的选项,作为组合式API的起点 从组件生命周期看,它在 beforeCreate 之前执行 函数中 this 不是组件实例,是 undefined 如果数据或者函数在模板中使用,需要在 setup 返回 今后在vue3的项目中几乎用不到 this , 所有的东西通过函数获取。

4.多根节点

// Vue2只能存在一个根节点,需要用一个<div>来包裹着
    `<template>
  <div>
    <header></header>
    <main></main>
    <footer></footer>
  </div>
</template>
 
// Vue3支持多个根节点
<template>
  <header></header>
  <main></main>
  <footer></footer>
`</template>``

5、 响应式原理 Vue2 响应式原理基础是 Object.defineProperty;Vue3 响应式原理基础是 Proxy。

Object.defineProperty 基本用法:直接在一个对象上定义新的属性或修改现有的属性,并返回对象。 Proxy提供了一种更强大和灵活的拦截器机制,允许开发者定义代理对象并拦截对该对象的各种操作(如属性读取、属性设置、函数调用等)。 以下分别是Vue2和Vue3的响应式原理的简单示例。

// 定义一个对象  
const data = {  
  name: 'Vue2',  
  count: 0  
};  
  
// 使用Object.defineProperty为每个属性添加getter和setter  
Object.defineProperty(data, 'name', {  
  get() {  
    console.log('name getter called');  
    return this._name;  
  },  
  set(newValue) {  
    console.log('name setter called with value:', newValue);  
    this._name = newValue;  
    // 这里可以触发视图更新等操作  
  },  
  configurable: true,  
  enumerable: true  
});  
  
// 初始化_name属性  
data._name = data.name;  
  
// 访问和修改属性  
console.log(data.name); // 输出: name getter called,Vue2  
data.name = 'New Name'; // 输出: name setter called with value: New Name

vue3的示例

import { reactive } from 'vue';  
  
// 创建一个响应式对象  
const state = reactive({  
  name: 'Vue3',  
  count: 0  
});  
  
// 访问和修改属性  
console.log(state.name); // 访问属性,触发getter  
state.name = 'New Name'; // 修改属性,触发setter  
  
// 在Vue3中,你不需要显式地定义getter和setter,因为Proxy会为你处理这些  
// 但是,你可以通过调试或查看Vue3的内部实现来了解它是如何工作的
// 打印出来的是一个proxy对象

6.你平时用过ES6语法吗说说有哪些。

1.let与const 好处:

let用来声明变量,类似于var,但是所声明的变量,只在let命令所在的代码块内有效
特点:
1)存在块级作用域
2)不存在变量提升(考虑暂时性死区)
3)不允许重复声明(包括普通变量和函数参数)

const用于声名一个只读的常量,常量的值不支持改变

2.箭头函数 箭头函数和普通匿名函数有哪些不同?

  1. 函数体内的this对象,指向所在的对象。
  2. 不可以当作构造函数
  3. 不可以使用arguments对象,该对象在函数体内不存在,如果要用,可以使用rest参数代替
  4. 不可以使用yield命令

ES6 允许使用“箭头”(=>)定义函数。

场景:用于替换匿名函数

基本用法

//匿名函数
    div.onclick=function(){
        console.log("你好")
    }
    //箭头函数
    div.onclick=()=>{
        console.log("你好")
    }

3. 模板字符串 语法

let a=`我的名字是:${name}`

字符串和变量拼接

let s3 =" a " + s1 + " b " + s2;
let s4 = ` a ${s1} b ${s2}`;  

字符串换行

var box =`<div>
            <p>
              <span>123</span>
            </p>
            <p>${a1}</p>
         </div>`;

4. 解构赋值 解构赋值允许从数组或对象中提取数据,并将其赋值给变量。
解构赋值可以方便地交换变量的值,同时还支持默认值

对象结构赋值

var obj ={ name:"abc",age:18 };
    //用解构赋值的方式获取name、age

    let { name } = obj; //创建了一个变量name,值=obj.name
    console.log(name);  //"abc"

    let { age } =obj;
    console.log(age);  //18

函数参数结构赋值

function f1(obj){
        console.log(obj.age);
        console.log(obj.height)
    }
    //等价于
    function f1({ age,height }){
        console.log(age);
        console.log(height)
    }

    f1({age:5,height:180})

5. 展开运算 展开运算符...可以将可迭代对象宅开为多个元素

// 案例1
let a={age:19,hobby:'抽烟'}
let user={name:'green',age:21,...a}
console.log(user) //{ name: 'lqz', age: 19, hobby: '抽烟' }

// 案例2
let l=[1,2,3]
let l1=[44,55,66,...l]
console.log(l1) // [ 44, 55, 66, 1, 2, 3 ]

// 案例3
# 案例3
function demo01(a,...b){
    console.log(a)
    console.log(b)
}
demo01(1,2,34,4)
let l=[44,5,66,7]
demo01(...l)

6.Promise构造函数

Promise自身有我们常用的all、race、resoleve、reject等方法,原型中还有catch、then等方法,所以我们创建Promise实例时,将then会作为callback使用,避免回调地狱的出现。

Promise.all的提供了并行的操作能力,并且是在所有的一步操作执行完成后才执行回调。 all接收一个数组参数,它会把所有异步操作的结果放进一个数组中传给then。

// all的使用
function async1 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('async1');
      resolve('async1完成')
    }, 1000);
  })
}

function async2 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('async2');
      resolve('async2完成')
    }, 2000);
  })
}
 
function async3 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
    console.log('async3');
      resolve('async3完成')
    }, 3000);
  })
}
Promise.all([async1(), async2(), async3()]).then((result) => {
  console.log(result);
  // do something...
}).catch((err) => {
  console.log(err);
});
 
// result:
async1
async2
async3
[ 'async1完成', 'async2完成', 'async3完成' ]

一、Promise.resolve() 和 Promise.reject()

  1. Promise.resolve()
    创建一个 立即成功 的 Promise 对象,常用于将非 Promise 值转为 Promise[5][6][9]:

    javascript
    // 返回一个已成功的 Promise,值为 'Hello'
    Promise.resolve('Hello').then(res => console.log(res)); // 输出 'Hello' [5][9]
    
  2. Promise.reject()
    创建一个 立即失败 的 Promise 对象,常用于抛出错误[5][6]:

    javascript
    // 返回一个已失败的 Promise,错误为 'Error'
    Promise.reject('Error').catch(err => console.log(err)); // 输出 'Error' [5][6]
    

二、Promise.all()

作用:接收一个 Promise 数组,当所有 Promise 都成功时返回结果数组;若有一个失败,则立即失败[1][4][9]。

javascript
const p1 = Promise.resolve('Task1');
const p2 = new Promise(resolve => setTimeout(resolve, 1000, 'Task2'));

Promise.all([p1, p2])
  .then(results => console.log(results)) // 1秒后输出 ['Task1', 'Task2']
  .catch(err => console.log(err));       // 任一失败时触发 [4][9]

三、Promise.race()

作用:接收一个 Promise 数组,返回 第一个完成 的 Promise 的结果(无论成功或失败)[1][4][9]:

javascript
const p1 = new Promise(resolve => setTimeout(resolve, 500, 'Fast'));
const p2 = new Promise(resolve => setTimeout(resolve, 1000, 'Slow'));

Promise.race([p1, p2])
  .then(result => console.log(result)) // 0.5秒后输出 'Fast'
  .catch(err => console.log(err));     // 第一个失败的 Promise 触发 [4][9]

四、构造函数中的 resolve 和 reject

在创建 Promise 实例时,通过 resolve 和 reject 控制状态[3][6][9]:

javascript
const promise = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve('操作成功'); // 触发 then 回调 [5][6]
  } else {
    reject('操作失败');  // 触发 catch 回调 [5][6]
  }
});

promise
  .then(res => console.log(res))
  .catch(err => console.log(err));

总结

方法作用示例场景来源
Promise.resolve创建立即成功的 Promise同步返回已知结果
Promise.reject创建立即失败的 Promise快速抛出错误
Promise.all并行执行多个 Promise,全部成功时返回多接口数据聚合
Promise.race取第一个完成的 Promise 结果请求超时控制、竞速场景

promise的三种状态: pending、fulfilled、rejected(未决定,履行,拒绝),同一时间只能存在一种状态,且状态一旦改变就不能再变。promise是一个构造函数,promise对象代表一项有两种可能结果(成功或失败)的任务,它还持有多个回调,出现不同结果时分别发出相应回调。

7.解决异步

我们都知道js是单线程执行代码,导致js的很多操作都是异步执行(ajax)的,以下是解决异步的几种方式:

​ 1.回调函数(定时器)。

​ 2.事件监听。

​ 3.发布/订阅。

​ 4.Promise对象。(将执行代码和处理结果分开)

​ 5.Generator。

​ 6.ES7的async/await

8.聊聊你平时对数组的操作用法,尽可能全一点。

一、改变原数组的方法

  1. push()
    向数组末尾添加元素,返回新长度 [1][3][5]

    javascript
    let arr = [1,2]; arr.push(3); // arr变为[1,2,3]
    
  2. pop()
    删除数组最后一个元素,返回被删除的值 [1][3][5]

    javascript
    let last = arr.pop(); // last=3,arr变为[1,2]
    
  3. shift()
    删除数组第一个元素,返回被删除的值 [1][3][5]

    javascript
    let first = arr.shift(); // first=1,arr变为[2]
    
  4. unshift()
    向数组开头添加元素,返回新长度 [1][3][5]

    javascript
    arr.unshift(0); // arr变为[0,2]
    
  5. splice()
    添加/删除元素,返回被删除元素的数组 [1][3][4]

    javascript
    arr.splice(1,1, 'new'); // 从索引1删除1个元素并插入'new'
    
  6. reverse()
    反转数组顺序 [1][3][4]

    javascript
    arr.reverse(); // [2,0][0,2]
    
  7. sort()
    排序(默认按Unicode),可传比较函数 [1][4][7]

    javascript
    [3,1].sort((a,b) => a-b); // [1,3]
    
  8. copyWithin()
    复制数组部分内容到其他位置 [1]

    javascript
    [1,2,3,4].copyWithin(0,2); // [3,4,3,4]
    
  9. fill()
    填充数组为指定值 [1][2]

    javascript
    new Array(3).fill(0); // [0,0,0]
    

二、返回元素信息或索引的方法

  1. indexOf()
    返回元素首次出现的索引,无则-1 [1][3][7]

    javascript
    ['a','b'].indexOf('b'); // 1
    
  2. lastIndexOf()
    返回元素最后一次出现的索引 [1][7][8]

    javascript
    [2,5,2].lastIndexOf(2); // 2
    
  3. find()
    返回第一个符合条件的元素 [1][7]

    javascript
    [1,2,3].find(x => x>1); // 2
    
  4. findIndex()
    返回第一个符合条件的元素索引 [1][7]

    javascript
    [1,2,3].findIndex(x => x>1); // 1
    
  5. includes()
    判断是否包含某值,返回布尔值 [1][3][7]

    javascript
    [1,2].includes(2); // true
    

三、生成新数组或值的方法

  1. concat()
    合并数组,返回新数组 [1][3][4]

    javascript
    [1,2].concat([3]); // [1,2,3]
    
  2. slice()
    截取数组片段,返回新数组 [1][3][4]

    javascript
    [1,2,3].slice(1,3); // [2,3]
    
  3. map()
    遍历处理元素,返回新数组 [1][4][7]

    javascript
    [1,2].map(x => x*2); // [2,4]
    
  4. filter()
    过滤元素,返回符合条件的新数组 [1][7][8]

    javascript
    [1,2,3].filter(x => x>1); // [2,3]
    
  5. flat()
    数组扁平化(默认展开1层) [1][2]

    javascript
    [1,[2]].flat(); // [1,2]
    
  6. flatMap()
    先映射再扁平化 [1]

    javascript
    [1,2].flatMap(x => [x, x*2]); // [1,2,2,4]
    
  7. join()
    将数组转为字符串,默认逗号分隔 [1][3][7]

    javascript
    [1,2].join('-'); // "1-2"
    
  8. reduce() / reduceRight()
    累计计算,返回最终值 [1][7][8]

    javascript
    [1,2].reduce((sum, x) => sum + x, 0); // 3
    

四、遍历与判断方法

  1. forEach()
    遍历数组,无返回值 [1][3][7]

    javascript
    [1,2].forEach(x => console.log(x));
    
  2. every()
    所有元素满足条件则返回 true [1][7][8]

    javascript
    [2,4].every(x => x%2===0); // true
    
  3. some()
    任一元素满足条件则返回 true [1][7][8]

    javascript
    [1,2].some(x => x>1); // true
    

五、其他实用方法

  1. Array.from()
    将类数组转为数组 [1][6]

    javascript
    Array.from('abc'); // ['a','b','c']
    
  2. Array.of()
    创建包含任意类型元素的数组 [1][6]

    javascript
    Array.of(1, 'a'); // [1, 'a']
    
  3. Array.isArray()
    判断是否为数组 [3][6]

    javascript
    Array.isArray([1]); // true
    
  4. keys() / values()
    返回数组索引或值的迭代器

    javascript
    [...['a','b'].keys()]; // [0,1]
    

六、ES6+新增方法

  1. findLast() / findLastIndex()
    从后向前查找元素或索引(ES2023)
  2. toReversed() / toSorted()
    返回反转/排序后的新数组(ES2023)

9.讲一下foreach、map的区别

forEach()方法返回undefined ,而map()返回一个包含已转换元素的新数组。

const numbers = [12345];
 
// 使用 forEach()
const squareUsingForEach = [];
numbers.forEach(x => squareUsingForEach.push(x*x));
 
// 使用 map()
const squareUsingMap = numbers.map(x => x*x);
 
console.log(squareUsingForEach); // [1, 4, 9, 16, 25]
console.log(squareUsingMap);     // [1, 4, 9, 16, 25]

由于forEach()返回undefined,所以我们需要传递一个空数组来创建一个新的转换后的数组。map()方法不存在这样的问题,它直接返回新的转换后的数组。在这种情况下,建议使用map()方法。

10.async await

由于 JavaScript 是单线程执行模型,因此必须支持异步编程才能提高运行效率。异步编程的语法目标是让异步过程写起来像同步过程。

为了解决 Promise 的问题,async、await 在 ES7 中被提了出来,是目前为止最好的解决方案

const fs = require('fs')
async function readFile() {
  try {    
    var f1 = await readFileWithPromise('/etc/passwd')
    console.log(f1.toString())
    var f2 = await readFileWithPromise('/etc/profile')
    console.log(f2.toString())
  } catch (err) {
    console.log(err)
  }
}

async、await 函数写起来跟同步函数一样,条件是需要接收 Promise 或原始类型的值。异步编程的最终目标是转换成人类最容易理解的形式。

async函数返回一个 Promise 对象,可以使用then方法添加回调函数

11.vuex用过吗,讲讲其中的一些方法和作用

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。简单来说就是:应用遇到多个组件共享状态时,使用vuex。

1.怎么使用?

vue框架中状态管理。在main.js引入store,注入。
新建了一个目录store.js,…… export 。

vuex的流程

    页面通过mapAction异步提交事件到action。action通过commit把对应参数同步提交到mutation,mutation会修改state中对应的值。最后通过getter把对应值跑出去,在页面的计算属性中,通过,mapGetter来动态获取state中的值

image.png

二.vuex有哪几种属性?

vuex的store有几个属性值?分别讲讲它们的作用是什么? 有五种,分别是State , Getter , Mutation , Action , Module (就是mapAction)

state => 基本数据(数据源存放地) getters => 从基本数据派生出来的数据 mutations => 提交更改数据的方法,同步! actions => 像一个装饰器,包裹mutations,使之可以异步。 modules => 模块化Vuex

  1. state:vuex的基本数据,用来存储变量

  2. geeter:从基本数据(state)派生的数据,相当于state的计算属性

  3. mutation:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。

  4. action:和mutation的功能大致相同,不同之处在于 ==》1. Action 提交的是 mutation,而不是直接变更状态。 2. Action 可以包含任意异步操作。

  5. modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。

三、vuex的state、getter、mutation、action、module特性分别是什么?

state:存放公共数据的地方   getter:获取根据业务场景处理返回的数据   mutations:唯一修改state的方法,修改过程是同步的   action:异步处理,通过分发操作触发mutation   module:将store模块分割,减少代码臃肿

四、vuex怎样赋值?vuex存储数据的方法有哪些?

使用下面这两种方法存储数据:

  dispatch:异步操作,写法: this.$store.dispatch('actions方法名',值)

  commit:同步操作,写法:this.$store.commit('mutations方法名',值)

12.vue中的watch和computed区别

1、computed是计算属性;watch是监听,监听data中的数据变化。

2、computed支持缓存,当其依赖的属性的值发生变化时,计算属性会重新计算,反之,则使用缓存中的属性值; watch不支持缓存,当对应属性发生变化的时候,响应执行。

3、computed不支持异步,有异步操作时无法监听数据变化;watch支持异步操作。

4、computed第一次加载时就监听;watch默认第一次加载时不监听。

immediate 组件创建时刻执行与否
immediate: true,第一次加载时监听(默认为false)
deep 深度监听 不推荐使用(非常的消耗性能)
监听的属性是对象的话 不开启deep 对象子属性变化不会触发watch
开启了deep 对象内部所有子属性变化 都会触发watch

5、computed中的函数必须调用return;watch不是。

6、使用场景: computed:一个属性受到多个属性影响,如:购物车商品结算。 watch:一个数据影响多条数据,如:搜索数据。 数据变化响应,执行异步操作,或高性能消耗的操作,watch为最佳选择。

13.v-show和v-if的区别以及适用场景

v-if为动态添加/移除 DOM 元素,v-show为通过 CSS display 属性控制显隐。

适用场景

  1. 使用 v-if 的场景

    • 条件很少变化(如权限控制、首次加载判断)
    • 需要减少初始渲染开销(如首屏隐藏大组件)
    • 需要销毁组件状态(如切换后需重置表单)
  2. 使用 v-show 的场景

    • 频繁切换显示状态(如选项卡、折叠面板)
    • 需要保持组件状态(如表单输入内容保留)

14.qiankun项目搭建,以及关键配置,分别讲讲vue2和vue3的。

一、主应用配置(Vue3与Vue2对比)

1. Vue3主应用
javascript
// main.js
import { createApp } from 'vue'
import { registerMicroApps, start } from 'qiankun'
const app = createApp(App)

// 注册微应用(支持Vue2/Vue3子应用)
registerMicroApps([
  {
    name: 'vue2-sub', 
    entry: '//localhost:8080',
    container: '#sub-container',
    activeRule: '/vue2-app',  // 激活路由规则
    props: { token: '主应用传递的数据' } // 通信数据[1][2][7]
  },
  {
    name: 'vue3-sub',
    entry: '//localhost:8888',
    activeRule: '/vue3-app'
  }
])

start() // 启动qiankun[1][2][7]
2. Vue2主应用
javascript
// main.js
import Vue from 'vue'
import { registerMicroApps, start } from 'qiankun'
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

// 注册逻辑与Vue3一致[5][9]
registerMicroApps([...])
start()

二、子应用配置(Vue3与Vue2差异)

1. Vue3子应用
javascript
// main.js
import { createApp } from 'vue'
let instance = null
function render(props = {}) {
  instance = createApp(App).use(router).mount('#app')
}

// 暴露qiankun生命周期
export async function bootstrap() {}
export async function mount(props) { render(props) }
export async function unmount() { instance?.unmount() }
2. Vue2子应用
javascript
// main.js
let router = null
let instance = null
function render(props = {}) {
  instance = new Vue({ router, render: h => h(App) }).$mount('#app')
}

// 暴露生命周期(与Vue3格式相同)
export async function bootstrap() { /*...*/ }
export async function mount(props) { render(props) }
export async function unmount() { instance.$destroy() }

三、关键配置项

  1. 打包配置(公共)

    javascript
    // vue.config.js(Vue2/Vue3通用)
    module.exports = {
      devServer: {
        headers: { 'Access-Control-Allow-Origin': '*' } // 允许跨域
      },
      configureWebpack: {
        output: {
          library: `${name}-[name]`, // 微应用名称
          libraryTarget: 'umd' // 模块格式
        }
      }
    }
    
  2. 路由配置差异

    • Vue3子应用:使用createWebHistory时需设置base: activeRule路径
    • Vue2子应用:建议使用hash路由避免路径冲突

四、注意事项

  1. 通信机制

    • 主应用通过props传递数据,子应用通过window.__POWERED_BY_QIANKUN__判断运行环境
    • Vue3推荐使用provide/inject,Vue2可用Vue.observable
  2. 样式隔离

    • 添加scoped或CSS Modules避免样式污染
  3. 部署差异

    • 生产环境需将子应用入口entry改为线上地址(如CDN)

总结

  • 相同点:注册逻辑、生命周期暴露方式一致
  • 差异点:Vue3使用createApp+组合式API,Vue2使用new Vue()+选项式API
  • 高频考点:微应用注册配置、通信机制、路由隔离

15.以下是Vue2与Vue3组件传参方式对比及核心差异总结

一、相同点

  1. 父传子
    均通过props接收数据,父组件使用v-bind绑定属性
<Child :message="parentMsg" />


<script>
export default {
  props: ['message']
}
</script>

2. 子传父

     ```
       //Vue3:需显式使用`provide/inject`
         <button @click="$emit(‘update’, data)">提交</button>
         <Child @update=“handleUpdate” />
     ```

3. Vue3 的 provide 和 inject

provide和inject的基本用法 让我们通过一个简单的例子来了解如何在Vue 3中使用provide和inject进行依赖注入。

父组件 - 使用provide 首先,我们创建一个父组件ParentComponent。在这个组件中,我们使用provide方法来提供数据:

什么是依赖注入? 依赖注入(Dependency Injection, DI)是一种设计模式,它允许一个类或组件从外部获得它依赖的对象或资源,而不是在内部自己创建这些对象。这种模式可以提高代码的可测试性和可扩展性,使代码结构更加清晰。

provide和inject方法就是Vue 3实现这种依赖注入的工具。父组件通过provide提供数据,后代组件通过inject获取数据。这种模式特别适用于需要跨组件传递状态或配置的情况。

<template>
  <div>
    <h1>Parent Component</h1>
    <child-component></child-component>
  </div>
</template>

<script>
export default {
  name: 'ParentComponent',
  setup() {
    const message = 'Hello from Parent Component';
    
    // 使用provide提供数据
    provide('message', message);

    return {};
  },
};
</script>

在这个例子中,我们在setup函数中调用了provide方法,并提供了一个键值对,键是message,值是我们要传递的数据Hello from Parent Component。

子组件 - 使用inject

接下来,我们创建一个子组件ChildComponent。在这个组件中,我们使用inject方法来获取父组件提供的数据:

<template>
  <div>
    <h2>Child Component</h2>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  setup() {
    // 使用inject获取父组件提供的数据
    const message = inject('message');

    return {
      message,
    };
  },
};
</script>

我们可以通过provideinject共享一个对象,而不是单个值。、 如果我们想提供响应式数据,可以使用refreactive

<template>
  <div>
    <h1>Parent Component</h1>
    <child-component></child-component>
  </div>
</template>

<script>
import { ref, provide } from 'vue';

export default {
  name: 'ParentComponent',
  setup() {
    const count = ref(0);

    provide('count', count);

    return {};
  },
};
</script>

16.Vue3中ref与reactive的核心区别及使用场景总结


一、核心差异对比

特性refreactive
适用数据类型基本类型(String/Number等) 或需要.value访问的引用类型[1][2][3][6]对象/数组等引用类型[1][3][5][6]
访问方式JS中需通过.value访问 模板中自动解包[2][6][8]直接访问属性(无需.value)[1][3][6]
深度响应式若存储对象,底层转为reactive实现深度响应[5][8]原生深度响应式[1][3][5]
解构响应性保持响应性需配合toRefs保持响应性[8]

二、使用场景推荐

  1. ref 适用场景

    • 基本类型数据(计数器、开关状态等)

      js
      const count = ref(0) 
      
    • 需要显式追踪引用的DOM元素或组件实例

      <input ref="myInput"> 
      
    • 需要灵活处理值类型变化的场景

  2. reactive 适用场景

    • 复杂对象结构(表单对象、配置项等)

      js
      const form = reactive({ name: '', age: 18 })  
      
    • 需要深度嵌套响应的数据结构

      js
      const tree = reactive({ 
        nodes: [{ children: [...] }] 
      })  // [1][5]
      
    • 需要直接操作属性的场景(避免.value繁琐操作


三、最佳实践建议

  1. 优先使用ref处理基本类型,reactive处理对象/数组

  2. 组合式API中推荐配合toRefs解构reactive对象

    js
    const state = reactive({ x: 1, y: 2 })
    const { x, y } = toRefs(state)  // [8]
    
  3. 避免混用导致代码可读性下降(如:用ref包装对象)

17.讲讲Vue3中的proxy对象

一、Proxy的核心概念

  1. 定义
    Proxy是ES6引入的元编程特性,用于创建对象的代理,通过拦截器(traps)实现对对象基本操作(属性访问、赋值、枚举等)的自定义行为[1][2][5][6][9]。

  2. 基本语法

    javascript
    const proxy = new Proxy(target, handler)
    
    • target:被代理的目标对象(支持对象/数组/函数等)[6][7][9]
    • handler:包含拦截方法的对象(如get/set/deleteProperty等)[4][6][7]

二、在Vue3中的应用

  1. 响应式系统核心
    Vue3使用Proxy替代Vue2的Object.defineProperty,通过reactive()函数将普通对象转换为响应式对象[1][3][5][8][9]。

  2. 核心拦截器

    javascript
    const handler = {
      get(target, key) { // 拦截属性读取
        track(target, key)  // 依赖收集[8][9]
        return Reflect.get(...arguments)
      },
      set(target, key, value) { // 拦截属性修改
        trigger(target, key)  // 触发更新[8][9]
        return Reflect.set(...arguments)
      }
    }
    
  3. 优势对比

    特性Proxy (Vue3)Object.defineProperty (Vue2)
    数组响应性支持索引/长度变化[4][8]需重写数组方法
    动态属性自动追踪新增属性[5][8]需$set强制添加
    嵌套对象按需深度代理[3][8]初始化递归代理
    性能内存占用减少30%[8]全量递归转换

三、典型使用示例

javascript
// 创建响应式对象(Vue3底层实现原理简化版)
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      console.log(`读取 ${key}`);
      return Reflect.get(target, key);
    },
    set(target, key, value) {
      console.log(`设置 ${key}${value}`);
      return Reflect.set(target, key, value);
    }
  });
}

const state = reactive({ count: 0 });
state.count++;  // 输出:读取 count → 设置 count 为 1

四、注意事项

  1. 原始对象保护
    Proxy代理会修改原始对象,建议仅对副本进行代理[6][7]
  2. 浏览器兼容性
    需注意IE11等旧浏览器不支持Proxy(Vue3默认不兼容IE)[8]
  3. 性能优化
    Vue3通过WeakMap缓存代理对象避免重复代理[8][9]

18.浅拷贝和深拷贝的区别,讲一下你平时是怎么使用深拷贝的

一、核心区别

  1. 浅拷贝

    • 定义:仅复制对象的顶层属性,若属性是引用类型(如数组、对象),则复制的是内存地址而非实际数据,新旧对象共享同一块内存[1][4][6]
    • 修改影响:修改拷贝对象的引用类型数据时,原对象也会同步变化[1][4][5]
    javascript
    // 示例:JS中浅拷贝
    const obj = { a: 1, b: { c: 2 } };
    const shallowCopy = Object.assign({}, obj);
    shallowCopy.b.c = 99;  // 原对象obj.b.c也会变为99 [4][6]
    
  2. 深拷贝

    • 定义:递归复制对象的所有层级,包括引用类型的数据,生成完全独立的新对象[3][6][8]
    • 修改影响:新旧对象完全隔离,修改互不影响[3][4][8]
    javascript
    // 示例:JS中深拷贝
    const deepCopy = JSON.parse(JSON.stringify(obj));
    deepCopy.b.c = 100;  // 原对象obj.b.c仍为99 [6][8]
    

二、项目中的深拷贝应用

  1. 典型场景

    • 状态管理:在Redux/Vuex中修改状态时,需深拷贝旧状态生成新对象,避免直接修改原状态[6][8]
    • 数据隔离:处理表单数据、配置对象时,深拷贝可防止原始数据被意外污染[6][8]
    • 缓存数据:保存数据快照用于撤销/重做功能时,需深拷贝保证历史记录独立性[8][9]
  2. 实现方式

    • JSON方法JSON.parse(JSON.stringify(obj))(不支持函数/循环引用)[6][8]
    • 递归复制:手动实现递归遍历对象属性进行复制[6][8]
    • 工具库:使用lodash.cloneDeepimmer等库处理复杂对象[6][8]
    javascript
    // 使用lodash实现深拷贝
    import { cloneDeep } from 'lodash';
    const safeCopy = cloneDeep(originalData);
    

使用该方法存在的问题为:

函数丢失

  • 对象中的方法会被完全忽略,拷贝后属性消失
const obj = { date: new Date(), regex: /abc/ }; 

const copy = JSON.parse(JSON.stringify(obj)); // copy.date变为字符串,copy.regex变为{} [9]

简单场景:仍可使用该方法(仅处理基础类型和普通对象/数组时)

复杂场景:使用lodash.cloneDeep或实现递归拷贝(处理函数/循环引用/特殊对象)

// 使用lodash实现深拷贝 import { cloneDeep } from 'lodash';
const safeCopy = cloneDeep(originalData)

三、面试回答技巧

回答模板
浅拷贝只复制顶层数据,引用类型共享内存,修改会影响原对象;深拷贝递归复制所有层级,新旧对象完全独立。 项目中在需要数据隔离的场景(如状态管理、表单处理)使用深拷贝,常用JSON方法或工具库实现,但需注意JSON方法的局限性。

手写代码:

 let arr = [5, 4, 9, 8]
        let obj1 = {
            name: 'xxx',
            sex: '男',
            like: ['红色', '蓝色'],
            book: {
                title: 'js程序',
                price: '88'
            }
        }
        function deepClone(obj) {
            //查看要拷贝的对象是数组还是对象,如果数组创建空数组,是对象创建空对象
            let newObj = obj instanceof Array ? [] : {}
            for (let k in obj) {
                //K属性名  obj[k]值
                //判断当前每个元素是否是对象或者数组 
                //如果还是对象,继续递归拷贝
                //是值,直接添加到新创建的对象或者数组里
                if (typeof obj[k] === 'object') {
                    //递归拷贝完 放入到新对象的属性
                    newObj[k] = deepClone(obj[k])
                } else {
                    //否则是值, 直接添加到新建的 newObj中
                    newObj[k] = obj[k]
                }
            }
            //返回新对象
            return newObj
        }
 
        var obj2 = deepClone(obj1)
        obj2.like.push('紫色')
        obj2.book.price = 999
        console.log(obj1);
        console.log(obj2);

第二种方法简单一点:

  // JSON.stringify 简单粗暴
        var origin_data = { a: 1, b: 2, c: [1, 2, 3] }
        var copy_data = JSON.parse(JSON.stringify(origin_data))
        origin_data.a = 3;
        origin_data.b = 4;
        origin_data.c[0] = '呵呵哒';
        console.log(origin_data)
        console.log(copy_data)

19.TS相关面试题

一、基础核心概念

  1. 基本类型

    • 7种原始类型:number/string/boolean/null/undefined/symbol/bigint
    • 扩展类型:array/tuple/enum/any/void/never/object
    let age: number = 25;  // 数字类型[4][8][9]
    let list: number[] = [1,2,3];  // 数组类型[8]
    
  2. 接口(Interface) vs 类型别名(Type)

    特性接口类型别名
    扩展方式通过继承(extends)通过交叉类型(&)
    适用场景描述对象结构/类契约[1][8]任意类型别名[2][3][8]
    合并声明支持同名合并不允许重复声明
    interface User { name: string }  // 接口定义[1]
    type ID = string | number;       // 类型别名[2]
    

二、高频面试题解析

  1. TS与JS的核心区别

    • 静态类型检查 vs 动态类型[5][8]
    • 支持接口/泛型/装饰器等OOP特性[5][8]
    • 编译时错误提示,提升代码健壮性[2][3]
  2. 类型断言(Type Assertion)
    两种语法实现类型强制声明:

    let str = <string>someValue;  // 尖括号语法[4]
    let len = (someValue as string).length;  // as语法[4]
    
  3. 泛型应用场景
    实现组件复用同时保持类型安全:

    function identity<T>(arg: T): T { return arg; }  // 泛型函数[1][8]
    interface GenericArray<T> {  // 泛型接口
      data: T[];
    }
    
  4. 联合类型 vs 交叉类型

    • 联合类型:string | number(多选一)[1][8]
    • 交叉类型:Person & Serializable(合并属性)[1][7]

三、高级特性

  1. 工具类型

    type PartialUser = Partial<User>;      // 所有属性可选[7]
    type ReadonlyUser = Readonly<User>;    // 只读属性[7]
    type UserNames = Pick<User, 'name'>;   // 属性筛选[7]
    
  2. 装饰器(Decorators)
    通过@语法实现元编程:

    @sealed  // 类装饰器
    class Person {
      @log  // 方法装饰器
      greet() { /*...*/ }
    }
    

四、实战技巧

  1. 错误处理

    • 使用strict编译选项开启严格模式[2][3]
    • 通过@ts-ignore临时忽略类型错误(慎用)
  2. 项目迁移策略

    • 逐步添加tsconfig.json配置文件
    • .js文件重命名为.ts并修复类型错误
    • 使用declare声明第三方库类型[2][3]

20.讲一下事件循环和如何阻止事件冒泡

  1. JavaScript 事件循环 JavaScript 是单线程执行的,这意味着在任何给定的时间点,只能执行一个任务。然而,在现代 web 应用程序中,我们经常需要处理异步操作,如网络请求、定时器等。为了管理这些异步操作,JavaScript 引入了事件循环(Event Loop)的概念。

1.1 基本概念 事件循环负责协调 JavaScript 引擎与 Web API 之间的通信。它遵循以下简单的流程:

执行栈(Call Stack):这是一个数据结构,用来存储当前正在执行的函数。每当调用一个函数时,它会被压入栈顶;当函数执行完毕后,它会从栈顶弹出。 消息队列(Message Queue):这是一个保存待处理任务的数据结构。当异步任务完成时,它们会被放入消息队列。 事件循环:检查执行栈是否为空。如果为空且消息队列中有任务,则将队列中的第一个任务推送到执行栈中执行。


考虑一个简单的例子,演示事件循环的工作原理:

Javascript

深色版本

1console.log('Start');
2
3setTimeout(() => {
4  console.log('Timeout');
5}, 0);
6
7console.log('End');
输出结果将是:

深色版本

1Start
2End
3Timeout
这里,setTimeout 函数并不会立即执行,而是被放入消息队列等待执行栈空闲时再执行。

一、事件冒泡机制详解

定义:当DOM元素触发事件时(如点击),事件会从触发元素逐级向上传播至根节点,依次触发父元素的同类事件处理函数。例如点击子元素时,父元素、祖父元素等会依次触发点击事件[1][2][4][5]。

传播过程

  1. 捕获阶段(可选):从根节点向下传递至目标元素(需通过addEventListenercapture:true开启)[3]
  2. 目标阶段:触发目标元素的事件处理函数
  3. 冒泡阶段:事件从目标元素向上逐层传递至根节点(默认阶段)[1][4]

二、阻止事件冒泡的5种方法

  1. event.stopPropagation()

    • 作用:阻止事件继续向上冒泡,不影响当前元素的其他事件监听器[1][3][5]

    • 示例

      javascript
      button.addEventListener('click', (e) => {
        e.stopPropagation();  // 阻止父元素接收点击事件[1][3]
      });
      
  2. event.stopImmediatePropagation()

    • 增强版:不仅阻止冒泡,还会阻止当前元素的其他同类型事件监听器执行[1][3]
  3. return false

    • 特性:在jQuery中同时阻止冒泡和默认行为;原生JS中需配合event.preventDefault()[2][5][6][8]
    • 注意:仅适用于通过onclick属性或jQuery绑定的事件[5][7]
  4. 事件代理优化

    • 策略:在父元素监听事件,通过event.target判断来源,避免子元素冒泡影响[4]

    • 示例

      javascript
      document.getElementById('parent').addEventListener('click', (e) => {
        if (e.target.tagName === 'BUTTON') {
          // 仅处理按钮点击,避免其他子元素冒泡干扰[4]
        }
      });
      
  5. 兼容IE的写法

    • 方案:统一处理event.cancelBubble属性(IE8及以下)[5][8][9]

    • 封装函数

      javascript
      function stopBubble(e) {
        e = e || window.event;
        if (e.stopPropagation) e.stopPropagation();
        else e.cancelBubble = true;  // IE兼容[5][8]
      }
      

三、阻止冒泡与默认行为的区别

方法阻止冒泡阻止默认行为适用场景
event.stopPropagation()✔️仅需阻止事件传播时[1][6]
event.preventDefault()✔️阻止表单提交/链接跳转[6]
return false✔️✔️jQuery场景下的快捷操作[6][8]

四、应用建议

  • 优先使用stopPropagation:明确需要阻断事件传播时使用,不影响默认行为[1][3]
  • 慎用return false:在原生JS中可能无法完全阻止冒泡,且语义不明确[5][7]
  • 复杂场景用事件代理:动态元素或批量处理时更高效[4]

通过合理使用上述方法,可精准控制事件传播流程[1][3][5]。

21.函数闭包这个概念知道嘛,哪些时候会碰到闭包这个概念。

一、闭包的核心定义

闭包是能够访问其他函数作用域中变量的函数,即使这些函数已经执行完毕[1][4][5][7]。其本质是函数与其词法作用域的结合体,通过作用域链保留对外部变量的引用[3][6][9]。


二、闭包的形成原理

  1. 作用域链保留:内部函数被外部函数返回或传递时,会携带外部函数的作用域链[3][6]
  2. 变量生命周期延长:即使外部函数执行完毕,其变量仍被内部函数引用,不会被垃圾回收[2][5][8]
  3. 词法作用域特性:函数定义时确定作用域链,而非运行时[3][6]

示例

javascript
function outer() {
  let count = 0;
  return function inner() { 
    return ++count; // 形成闭包,持续访问outer的count变量[1][4]
  };
}
const counter = outer();
console.log(counter()); // 1
console.log(counter()); // 2

三、5种常见闭包应用场景(面试高频考点)

  1. 函数返回函数

    • 场景:工厂模式、计数器实现[1][4][8]
    • 特点:内部函数长期持有外部变量
  2. 事件处理与异步回调

    • 场景:setTimeout/addEventListener中访问外部变量[8]
    javascript
    function initButton() {
      const btn = document.getElementById('btn');
      btn.addEventListener('click', function() {
        console.log('点击次数统计'); // 闭包保留btn引用[8]
      });
    }
    
  3. 模块化与私有变量

    • 场景:实现数据封装,防止全局污染[5][8]
    javascript
    const module = (function() {
      let privateVar = 0;
      return {
        get: () => privateVar,
        add: () => privateVar++
      };
    })();
    
  4. 循环中的闭包陷阱

    • 经典面试题:循环中使用var导致变量共享问题[4][6]
    javascript
    for (var i = 0; i < 3; i++) {
      setTimeout(function() {
        console.log(i); // 输出3次3,需用闭包解决
      }, 100);
    }
    // 闭包修正
    for (var i = 0; i < 3; i++) {
      (function(j) {
        setTimeout(() => console.log(j), 100); // 输出0,1,2[4]
      })(i);
    }
    
  5. 函数柯里化

    • 场景:参数复用、延迟计算[3][6]
    javascript
    function curry(fn) {
      return function(a) {
        return function(b) {
          return fn(a, b); // 闭包保留a的值
        };
      };
    }
    

四、闭包的关键理解(面试回答要点)

  1. 不是特殊语法:所有JS函数都是闭包(根据词法作用域)[1][6][9]
  2. 内存管理注意点:滥用会导致内存泄漏,需及时解除引用[2][8]
  3. 设计价值:实现模块化、数据隐私、高阶函数等核心编程模式[5][8]

五、快速记忆口诀

“内访外,外已死,作用链,变量活”
(内部函数访问外部变量 → 外部函数已执行完毕 → 作用域链保留 → 变量持续存活)

21.讲一下构造函数的原型和对象的原型是一样的嘛,也说一下原型链。

一、构造函数原型与对象原型的关系

结论:构造函数的原型(prototype)与实例对象的原型(__proto__指向同一个原型对象,但两者是不同维度的概念[1][4][5][7]。

对比维度构造函数的prototype实例对象的__proto__
归属对象构造函数独有属性所有对象共有属性
作用存储共享方法/属性实现原型链继承的桥梁
关系验证实例.__proto__ === 构造函数.prototype[1][4]自动指向创建该实例的构造函数的原型对象

示例

javascript
function Person() {}
const p = new Person();

// 验证关系
console.log(p.__proto__ === Person.prototype); // true [1][4]

二、原型链的核心概念

定义:通过对象的__proto__属性形成的链式结构,用于实现继承和属性查找机制。

核心规则

  1. 查找路径:访问对象属性时,若自身不存在,则通过__proto__逐级向上查找原型链
  2. 终点验证Object.prototype.__proto__ === null,原型链终止于null
  3. 动态继承:原型对象修改会影响所有已创建的实例

原型链示意图

javascript
实例对象(p)
  ↓ __proto__
Person.prototype
  ↓ __proto__
Object.prototype
  ↓ __proto__
null

三、原型链的工作机制(面试高频考点)

  1. 属性屏蔽原则

    • 若实例与原型链上层有同名属性,优先使用实例自身属性[2][5][7]
    javascript
    Person.prototype.name = "原型";
    const p = new Person();
    p.name = "实例";
    console.log(p.name); // "实例"(优先访问自身属性)
    
  2. 构造函数关系验证

    • Person.prototype.constructor === Person(原型对象通过constructor指向构造函数)[2][7]
    • p.constructor === Person(通过原型链继承得到)[7]
  3. 跨级原型链访问

    javascript
    console.log(p.toString()); // 来自Object.prototype [5][8]
    

四、关键结论(面试回答要点)

  1. 原型一致性:构造函数的prototype与实例的__proto__指向同一对象[1][4][7]
  2. 原型链本质:基于__proto__形成的继承链,实现属性和方法的共享[2][5][8]
  3. 设计价值:解决构造函数模式的内存浪费问题,支持继承和多态[6][7][9]

五、快速记忆口诀

“构造函数造实例,prototype存共享,对象proto连成链,层层查找终到null”  [2][5][7]

22.js中有哪些方法会改变原数组而哪些不会改变。

一、改变原数组的核心方法

  1. 增删类操作

    • push():末尾添加元素(返回新长度)[1][2][3][5][6][7]
    • pop():删除末尾元素(返回被删元素)[1][2][3][5][6][7]
    • shift():删除首元素(返回被删元素)[1][2][3][5][6][7]
    • unshift():开头添加元素(返回新长度)[1][2][3][5][6][7]
    • splice():增删改元素(返回被删元素数组)[1][2][3][4][5][9]
  2. 顺序调整类

    • sort():元素排序(返回排序后数组)[1][2][3][4][5][7]
    • reverse():反转数组顺序(返回反转后数组)[1][2][3][4][5][7]
  3. 填充替换类

    • fill():填充指定值[1][2][3][5][7]
    • copyWithin():内部元素复制替换[1]

二、典型场景示例

javascript
// splice示例(改变原数组)
const arr = [1,2,3];
arr.splice(1, 1); // 删除索引1的元素
console.log(arr); // [1,3] [来源1][9]

// sort示例(原地排序)
const nums = [3,1,2];
nums.sort(); 
console.log(nums); // [1,2,3] [来源4][7]

三、易混淆方法对比

改变原数组的方法不改变原数组的方法
push/popconcat
spliceslice
fillmap
reversefilter
sortreduce

23.qiankun框架如何集成子应用如何配置。

一、主应用配置(3大核心步骤)
  1. 安装依赖

    yarn add qiankun  # 或 npm install qiankun -S [6]
    
  2. 注册子应用(关键参数)

    javascript
    registerMicroApps([
      {
        name: 'sub-app',        // 【唯一标识】必须与子应用package.json的name一致 [7]
        entry: '//子应用地址',  // 【入口地址】开发用localhost,生产用域名 [5][6]
        container: '#container',// 【挂载节点】主应用HTML中必须存在该DOM元素 [9]
        activeRule: '/sub-path' // 【路由规则】匹配子应用激活路径 [2][5]
      }
    ]);
    start();  // 启动qiankun [5]
    
  3. 容器配置

    html
    
    <div id="container"></div>  
    

二、子应用配置(3个改造重点)
  1. 导出生命周期钩子

    javascript
    // 必须导出bootstrap/mount/unmount三个函数 [5][7]
    export async function mount(props) {
      // 挂载逻辑:将子应用渲染到props.container指定的DOM节点 [9]
      ReactDOM.render(<App/>, props.container.querySelector('#root'));
    }
    
  2. 动态设置publicPath

    javascript
    // 入口文件顶部添加
    if (window.__POWERED_BY_QIANKUN__) {
      __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; [7]
    }
    
  3. Webpack跨域配置

    javascript
    // vue.config.js / webpack.config.js
    devServer: {
      headers: { 'Access-Control-Allow-Origin': '*' } // 允许主应用跨域访问 [6][7]
    }
    

24.对于鉴权,子模块和父模块之间是如何通信的,父模块的登录的token如何保存并与子应用共享

一、通信机制实现(2种核心方式)
  1. 全局状态管理
    主应用通过initGlobalState创建共享状态池,子应用通过onGlobalStateChange监听变化[5][9]

    javascript
    // 主应用设置token
    import { initGlobalState } from 'qiankun';
    const actions = initGlobalState({ auth: { token: 'xxx' } });
    
    // 子应用监听变化
    export function mount(props) {
      props.onGlobalStateChange((state) => {
        console.log('收到token:', state.auth.token);
      }, true);
    }
    
  2. Props透传机制
    注册子应用时通过props直接注入token[5][9]

    javascript
    // 主应用配置
    registerMicroApps([{
      name: 'subapp',
      entry: '//sub.domain.com',
      props: { 
        token: localStorage.getItem('token') // 直接传递存储的token
      }
    }]);
    

二、Token存储与共享方案
  1. 安全存储方式

    • 主应用登录后采用HttpOnly Cookie存储(防XSS攻击)[9]
    • 敏感系统可结合AES加密后存sessionStorage(会话级隔离)[9]
  2. 跨应用同步策略

    javascript
    // 主应用登录成功后
    document.cookie = `token=xxx; Path=/; HttpOnly; Secure`;
    
    // 子应用获取方式
    const token = document.cookie.split('; ').find(row => row.startsWith('token='))?.split('=')[1];
    

三、架构设计建议
  1. 中小型项目:优先使用props透传+Cookie存储
  2. 大型系统:采用全局状态管理+服务端鉴权
  3. 安全要求高:必须使用HttpOnly Cookie,配合网关统一鉴权

25.多个tab页面如何共享一个websoket对象(多个tab页面做登录操作就会互相挤掉)。

解决方案(基于Web技术标准):

  1. 主从模式
    指定一个主Tab维护WebSocket连接,其他Tab通过BroadcastChannellocalStorage事件与主Tab通信[9]

  2. 共享存储
    使用localStorage同步状态,所有Tab监听storage事件:

    javascript
    // 主Tab
    const ws = new WebSocket(url);
    localStorage.setItem('wsMaster', 'active');
    
    // 其他Tab
    window.addEventListener('storage', (e) => {
      if(e.key === 'wsMessage') {
        // 处理来自主Tab的消息
      }
    });
    
  3. 服务端优化
    支持同一用户多设备同时在线(需服务端改造)

典型场景:金融交易系统/实时协作工具常用方案[9]

26.react和vue对象改变了,但是数据没有渲染(可能有哪些原因造成的)。

在使用Vue.js时,数据有时不会渲染有几个关键原因:

1、数据未被Vue实例监测到

**在Vue实例创建之后添加新的属性**:Vue无法检测到在实例创建之后添加的新属性,因为这些属性不是响应式的。

let vm = new Vue({
  data: {
    a: 1
  }
});
vm.b = 2; // 这不会触发视图更新

**使用`Object.assign`或直接修改对象属性**:这会直接替换对象,Vue无法检测到这种变化。

vm.someObject = Object.assign({}, vm.someObject, { newProperty: 'new value' }); // 不会触发视图更新

**解决方案**:
使用`Vue.set`方法添加新属性:
Vue.set(vm.someObject, 'newProperty', 'new value');
this.$set(this.someObject, 'newProperty', 'new value');

2、数据未绑定到模板中 数据必须在模板中进行绑定,否则Vue不会知道需要更新视图。这种情况下,即使数据发生变化,也不会影响视图。

<template>
  <div></div> <!-- 未使用数据 -->
  <!--  标签中的数据未定义未定义或拼写错误也不会渲染 -->
</template>

3、数据更新未触发视图更新 Vue.js依赖于数据响应式系统,但有时数据更新可能不会触发视图更新,尤其是在处理数组和对象时。

vm.items[indexOfItem] = newValue; // 不会触发视图更新

直接修改数组索引:直接修改数组的某个索引不会触发视图更新。

  • 使用Array.prototype.pushpop等方法:这些方法会触发视图更新,但其他数组方法如splicesort等可能不会。

使用响应式方法,如Vue.setthis.$set

4、数据依赖问题

computed: {
  correctComputed() {
    return this.someData + this.otherData; // 声明所有依赖项已声明
  }
}

5、渲染函数或生命周期钩子错误 自定义渲染函数或生命周期钩子中的错误可能导致数据未正确渲染。例如,beforeUpdateupdated钩子中的逻辑错误。

6、异步数据未处理好。这些问题可能导致 Vue 无法正确渲染数据。下面将详细解释这些原因并提供解决方案。

在处理异步数据时,如果数据尚未加载完成就试图渲染,可能导致视图未能正确更新。例如,在created钩子中发起异步请求,但未处理返回数据。

created() {
  axios.get('/api/data').then(response => {
    this.$set(this, 'someData', response.data); // 确保视图更新
  });
}

27.TS里面有哪些基础类型

TypeScript 基础类型详解(截至2025年最新实践)
结合高频面试考点和实际开发场景,TS 基础类型可分为以下两类:


一、JavaScript 原有基础类型

  1. boolean
    布尔值,仅接受 true/false

    let isDone: boolean = false; // [1][2][6]
    
  2. number
    支持十进制、二进制、十六进制等:

    let hex: number = 0xf00d; // [1][2]
    
  3. string
    支持模板字符串:

    let name: string = `Gene`;
    let sentence: string = `Hello, ${name}`; // [2][6]
    
  4. Array
    两种声明方式:

    let list1: number[] = [1,2,3]; 
    let list2: Array<number> = [1,2,3]; // [2][6]
    
  5. null/undefined
    所有类型的子类型(需注意严格模式):

    let u: undefined = undefined; // [3][6]
    

二、TypeScript 新增核心类型

  1. Tuple(元组)
    固定长度和类型的数组:

    let x: [string, number] = ["hello", 10]; 
    x[0].substring(1); // 正确
    x[1].toFixed(2);   // 正确
    x[2] = "world";    // 错误!
    
  2. enum(枚举)
    默认从0开始自增,支持反向映射:

    enum Direction { Up, Down=2, Left }
    console.log(Direction.Up);    // 0
    console.log(Direction[2]);    // "Down"
    
  3. any
    绕过类型检查(慎用):

    let notSure: any = 4;
    notSure = "maybe a string"; 
    
  4. unknown
    类型安全的any(需类型断言):

    let value: unknown = "hello";
    let str: string = value as string; 
    
  5. void
    函数无返回值:

    function warn(): void { console.log("Warning!"); }
    
  6. never
    表示永不存在的值(常用于错误处理):

    function error(message: string): never {
      throw new Error(message); 
    }
    

三、高频面试扩展

  • 联合类型string | number
  • 字面量类型type Status = "success" | "fail"
  • 类型断言<string>value 或 value as string 
  • 类型别名type Point = { x: number; y: number }

实际应用场景

  • 枚举替代魔法字符串(如状态码)
  • 元组处理CSV文件等固定结构数据
  • unknown替代any提升类型安全性

28.v-for和v-if为什么不能一起用呢为什么会报错一起用

v-for与v-if不能同时使用的原因及解决方案(截至2025年Vue最佳实践)


一、核心原因

  1. 优先级冲突
    Vue2中v-for优先级高于v-if,导致每次渲染时:

    • 先执行完整列表循环 → 再对每个元素执行条件判断 → 即使大部分元素无需渲染也会被遍历
  2. 性能损耗
    当列表数据量大时,无用的循环计算会导致严重性能浪费(如1000条数据中仅需渲染10条)

  3. 语法规范限制
    Vue官方规范明确禁止该用法,启用ESLint等工具时会直接报错

    The 'undefined' variable inside 'v-for'...(错误示例见[2][3]

二、解决方案

  1. 外层包裹<template>
    将条件判断提升到循环外部:

    html
    <template v-if="isShowList">
      <div v-for="item in items" :key="item.id"></div>
    </template> 
    

    (通过隔离作用域避免性能损耗 [5][7])

  2. 计算属性过滤
    预处理需要渲染的数据:

    javascript
    computed: {
      filteredItems() {
        return this.items.filter(item => item.isActive)
      }
    }
    
    html
    <div v-for="item in filteredItems" :key="item.id"></div>
    

    (推荐方案,符合响应式特性 [1][6][9])

  3. Vue3优化方案
    Vue3中v-if优先级高于v-for,但仍建议保持分离写法:

    html
    <div v-if="shouldRender">
      <div v-for="item in items"></div>
    </div>
    

    (版本差异需注意 [4][7])


三、高频面试扩展

  • Diff算法影响:无效的DOM节点创建/销毁会增加虚拟DOM比对复杂度 [3][8]
  • 代码可维护性:分离逻辑使代码更易理解(条件判断与数据展示解耦 [9])
  • SSR优化:服务端渲染时无效循环会增加首屏渲染时间 [8]

典型错误示例

html

<ul>
  <li>{{ user.name }}</li>
</ul>

(正确改造方案见[6][7])


2025年最新建议:使用Composition API的computed + <template>组合实现条件循环,兼顾性能与可维护性。

29.vue中的代理和反代理(讲一下其中的原理)


一、Vue开发环境代理(正向代理)

核心作用:解决前端开发中的跨域问题,将API请求代理到目标服务器[1][2]
实现原理

  1. 通过vue.config.js配置开发服务器代理规则:

    javascript
    // [1]示例配置
    devServer: {
      proxy: {
        '/api': {
          target: 'http://api.yourservice.com', // 目标服务器地址
          changeOrigin: true, // 伪造Host头(绕过服务端校验)
          pathRewrite: {'^/api': ''} // 路径重写规则
        }
      }
    }
    
  2. 浏览器请求/api/user → Vue开发服务器拦截 → 转发到http://api.yourservice.com/user[1][2]

  3. 本质是正向代理:代理客户端请求,服务端无法感知真实客户端(开发者的浏览器)[3][6][9]


二、反向代理原理

与Vue开发代理的区别

正向代理反向代理
代理对象客户端服务端
典型场景开发环境跨域、VPN生产环境负载均衡、Nginx
可见性服务端看不到真实客户端客户端看不到真实服务端

反向代理核心功能

  1. 负载均衡:将请求分发到多个服务器(如Nginx轮询策略)[3][6][9]
  2. 安全防护:隐藏真实服务器IP,防止直接攻击[3][9]
  3. 静态资源缓存:提升高频访问内容的响应速度[3][6]
  4. SSL终端:统一处理HTTPS加密/解密[9]

三、高频面试扩展

  1. 为什么需要路径重写(pathRewrite)
    用于去除代理标识(如示例中的/api),使目标服务器收到规范路径[1][2]

  2. changeOrigin参数作用
    修改请求头中的Host字段为目标服务器域名,避免服务端拒绝跨域请求[1][2]

  3. 生产环境如何实现反向代理
    使用Nginx配置:

    location /api {
      proxy_pass http://backend-server;
      proxy_set_header Host $host;
    }
    

    (通过反向代理实现服务集群管理[6][9])

  4. 与Vue响应式系统的关联
    代理配置仅影响网络请求,与Vue的Object.defineProperty/Proxy响应式原理无关[7]


2025最新实践建议:开发环境使用Vue CLI代理解决跨域,生产环境通过Nginx反向代理实现服务治理,两者配合保障全链路安全与性能

30.webpack和vite的区别(vite为什么快一些呢)

一、核心区别对比

维度WebpackVite
启动机制先打包所有模块再启动服务器[1][2][4]直接启动服务器,按需编译[1][2][7]
构建语言基于Node.js(毫秒级)[2][4][7]预构建用Esbuild(Go语言,纳秒级)[1][2][4]
热更新需重新构建相关依赖链[3][6][7]仅更新修改文件,无需全量编译[1][3][6]
生产构建默认打包工具使用Rollup(可切换Webpack)[4][7][9]
生态成熟度插件丰富,生态完善[3][4]生态逐步完善,但部分场景仍需Webpack[3][9]

二、Vite更快的原因

  1. ESM原生支持
    直接利用浏览器ES Module特性,无需打包即可运行源码,启动速度提升10倍以上[1][2][4][7]。

    javascript
    // 浏览器直接请求ES模块
    import { component } from '/src/App.vue?t=1623345' // Vite动态编译
    
  2. 按需编译(On-demand Compilation)
    仅编译当前请求的模块,避免全量打包:

    • Webpack:1000个模块需全量分析 → 启动慢[1][2][6]
    • Vite:首屏仅编译10个模块 → 毫秒级响应[2][4][7]
  3. Esbuild预构建
    Go语言编写的Esbuild比Node.js快10-100倍,完成依赖预构建仅需秒级[1][2][4][7]。

  4. 内存热更新优化
    模块编译结果直接存于内存,避免磁盘IO开销,HMR更新速度提升50%+[6][7]。

  5. 动态依赖解析
    通过浏览器发起模块请求,天然支持代码分割,对比Webpack的静态分析更高效[6][8]。


三、适用场景建议

  • Vite首选场景

    • 新项目开发,尤其是Vue/React SPA
    • 需要快速启动和热更新的高频迭代项目[3][4][7]
  • Webpack仍适用场景

    • 遗留项目维护
    • 需要复杂自定义构建配置(如微前端)[3][9]

2025趋势:Vite已逐步成为新项目开发标准工具,但Webpack凭借成熟生态仍在大型企业级项目中广泛使用,Rspack等Rust工具正在崛起[9]。

31.js中的异步操作是怎么样的(举个例子)。

一、什么是异步操作?

就像餐厅点餐:

  • 同步:排队点餐 → 等厨师做完 → 再点下一单(卡住后续操作)[4][8]
  • 异步:点餐后拿号码牌 → 继续服务其他客人 → 餐好了叫号通知(不阻塞后续代码)[2][4]

核心特点:代码执行不阻塞,耗时操作完成后通过回调通知[2][4][8]


二、常见异步场景(附代码示例)

  1. 定时器setTimeout/setInterval

    javascript
    console.log("开始点餐"); // 同步
    setTimeout(() => {
      console.log("您的汉堡做好了🍔"); // 异步回调
    }, 2000);
    console.log("继续打扫卫生"); // 不会等待2秒
    // 输出顺序:开始 → 打扫 → 汉堡
    
  2. 网络请求(AJAX/Fetch)

    javascript
    fetch('https://api.example.com/data') // 异步请求
      .then(response => response.json())  // Promise链式调用[1][6]
      .then(data => console.log("收到数据:", data))
      .catch(err => console.error("出错:", err));
    
  3. 事件监听(DOM事件)

    javascript
    button.addEventListener('click', () => { // 异步响应点击
      console.log("按钮被点击了!");
    });
    

三、异步解决方案演进

  1. 回调函数(Callback Hell)

    javascript
    getData(data => {        // 第1层回调
      process(data, result => {  // 第2层回调
        save(result, () => {     // 第3层回调 → 嵌套地狱[1][7]
          console.log("完成!");
        });
      });
    });
    
  2. Promise(链式调用)

    javascript
    getData()
      .then(process)  // 清晰的任务链[1][6]
      .then(save)
      .then(() => console.log("完成!"))
      .catch(handleError);
    
  3. async/await(同步写法)

    javascript
    async function orderFood() {
      try {
        const data = await getData();     // 等待异步完成[3][8]
        const result = await process(data);
        await save(result);
        console.log("完成!");
      } catch (err) {
        handleError(err);
      }
    }
    

四、为什么需要异步?

  • 单线程限制:JavaScript主线程像单车道,异步让耗时操作(如IO)靠边停车,避免堵住后续车辆[3][8]
  • 性能优化:浏览器UI渲染、接口请求等操作并行处理,提升用户体验[2][4]

32.讲一下箭头函数吧(有什么特点然后什么时候用到的比较多),其中的this指向又是什么样子的呢

一、核心特点

  1. 简洁语法

    • 省略function关键字,单参数可省括号,单行代码可省return[1][2]
    javascript
    // 传统函数 → 箭头函数简化
    const sum = function(a, b) { return a + b; }  
    const sum = (a, b) => a + b;  // [1][2]
    
  2. 无独立this

    • 继承外层this:箭头函数的this由定义时的外层作用域决定,而非调用时[1][2][8]
    javascript
    const obj = {
      name: 'Vue',
      getName: () => console.log(this.name) // this指向window而非obj[8]
    }
    
  3. 其他限制

    • arguments对象(可用...rest替代)[1]
    • 不能作为构造函数(无法用new调用)[8]
    • prototype属性[8]

二、典型使用场景

  1. 回调函数(避免this丢失)

    javascript
    // 传统函数this指向问题
    button.addEventListener('click', function() {
      console.log(this); // 指向button元素
    });
    
    // 箭头函数保持外层this(如Vue组件中常用)
    button.addEventListener('click', () => {
      console.log(this); // 指向外层组件实例[1][8]
    });
    
  2. 简化短函数

    javascript
    // 数组方法回调
    const nums = [1, 2, 3].map(x => x * 2); // [2][8]
    

三、this指向对比

场景传统函数箭头函数
对象方法this指向调用对象this指向外层(如window)[8]
事件回调this指向触发元素this继承外层定义时的值[1]
setTimeout内部this指向windowthis与外部一致[8]

 黄金规则:箭头函数的this定义时确定,普通函数的this调用时确定[

33.网页中的性能是比较差的,如果现在有一个超长数据列表,需要在页面里将其渲染出来,成千上万条数据渲染时页面肯定会卡顿,如果卡顿的话如何优化呢(vue这边利用一个虚拟dom渲染,这种时候是不是也可以用到呢)。

一、卡顿原因分析

  1. DOM节点爆炸:成千上万条数据直接渲染 → DOM操作耗时(重排/重绘)[2]
  2. 内存占用过高:每个列表项的响应式数据追踪消耗资源[8]
  3. 主线程阻塞:同步渲染大量数据导致JS执行时间过长[8]

二、Vue专属优化方案

  1. 虚拟滚动(核心方案)

    • 原理:仅渲染可视区域内的元素(如只保留20条),滚动时动态替换内容[8]
    • 实现:使用vue-virtual-scroller等库
    <RecycleScroller :items="list" :item-size="50" key-field="id">
      <template #default="{ item }">
        <div>{{ item.content }}</div>
      </template>
    </RecycleScroller>
    
  2. 减少响应式数据

    • 冻结数据:对静态数据使用Object.freeze关闭响应式追踪[8]
    javascript
    this.list = Object.freeze(rawData); // 冻结后Vue不再添加getter/setter
    
  3. 组件级优化

    • 拆分子组件:避免父组件频繁重新渲染
    • 使用v-memo:Vue 3特性缓存静态节点[8]
    <div v-for="item in list" :key="item.id" v-memo="[item.id]">
      {{ item.content }}
    </div>
    

三、通用优化手段

  1. 分页/懒加载

    • 监听滚动事件动态加载数据(适合非一次性展示场景)[2]
  2. 时间分片

    • 使用requestAnimationFrame分批渲染(避免阻塞主线程)[8]
    javascript
    const renderChunk = (data) => {
      requestAnimationFrame(() => {
        renderNext100Items(data);
      });
    };
    
  3. Web Worker处理数据

    • 将数据排序/过滤等耗时操作放到Worker线程[8]

四、虚拟DOM的作用与局限

  • 优势:通过diff算法减少不必要的DOM操作[8]
  • 局限:虚拟DOM的diff计算本身也有开销,超长列表仍需结合虚拟滚动等方案

优化效果对比

方案DOM节点数内存占用适用场景
直接渲染10000+极小数据量
虚拟滚动20~50超长列表
分页加载每页100可分块加载场景

34.讲一下vue这边的组件和插件的区别吧,平时用到的插件有哪些呢

一、核心区别对比

维度组件插件来源
功能定位构建UI的独立单元(如按钮、表单)扩展Vue全局功能(路由、状态管理)[1][4]
注册方式局部注册(components)或全局注册必须通过Vue.use()全局安装[2][6]
作用范围局部作用(仅在注册范围内生效)全局生效(影响整个Vue应用)[1][3]
典型内容包含模板、逻辑、样式三要素可含全局方法/指令/混入/实例方法等[3][7]

二、常用Vue插件及作用

  1. Vue Router

    • 作用:实现SPA路由管理,提供<router-view>等组件[6][8]
    • 安装:Vue.use(VueRouter)
  2. Vuex

    • 作用:集中式状态管理,提供store全局访问[6][8]
    • 核心:state/mutations/actions/getters
  3. Element UI / Vuetify

    • 作用:提供全局UI组件库(如表格、弹窗)[3][6]
    • 特性:通过插件一次性注册所有组件
  4. Vue-i18n javascript Vue.use(VueI18n); // 注册后可在任意组件使用翻译功能


三、快速记忆口诀

“组件造零件,插件改引擎”

  • 组件:UI拼图(局部作用)
  • 插件:功能外挂(全局增强)

35.vue的组件之间的通信

1.组件通信常用方式有以下8种:

props

emit/emit/on(vue3废弃)

children(vue3废弃)/children(vue3废弃)/parent

attrs/attrs/listeners(vue3废弃)

ref

$root

eventbus

vuex

1.父子

props/emit/emit/parent/ref/$attrs

2.兄弟组件

parent/parent/root/eventbus/vuex

3.跨层级关系

eventbus/vuex/provide+inject

36.