工作8年总被面试官问过的高频问题(VUE篇二)

一、Ajax与axios的区别?

1.1Ajax

Ajax,即 “Asynchronous Javascript And XML(异步 JavaScript 和 XML)”。通俗地理解,Ajax 是在网页中利用 XMLHttpRequest 对象与服务器进行数据交互的方式。它是对原生 XHR 的封装,为了实现跨域目的,增添了对 JSONP 的支持。

Ajax的工作原理:

Ajax 的工作原理分为五个步骤:

  1. 创建 XMLHttpRequest 异步对象;

  2. 注册回调函数;

  3. 使用 open 方法与服务器建立连接;

  4. 向服务器发送数据;

  5. 创建回调函数。

1.2Axios

axios 是通过 Promise 对 Ajax 技术的一种封装。axios 是一个基于 Promise 的 HTTP 库,可在浏览器和 node.js 中使用。axios 本质上是对原生 XHR 的封装,且是 Promise 的实现版本。可以说 axios 属于 Ajax,但 Ajax 并不只有 axios。

1.3语法

jQuery 将 XHR 进行了封装变成了 Ajax,而又通过 promise 把 Ajax 封装成了 axios。

注:传统 Ajax 指的是 XMLHttpRequest(XHR),axios 和 jQuery Ajax 都是对 Ajax 的封装。

语法上来看:

  • Ajax:

$.ajax({
    type:"POST",
    url:"",
    contentType: "application/json", 
    dataType:"json",
    data:{
        "请求参数"
    },
    success:function (data) {
        //返回的 data 数据
    }
});
  • axios:
    axios.post('/xxx/xxx', {
        name: '张三',
        age: 18
    })
    .then(function (response) {
        //处理返回数据
        console.log(response);
    })
    .catch(function (error) {
        //处理异常
        console.log(error);
    });

    早期使用 jquery 开发偏多,配合使用的 Ajax 比较多,现如今随着使用 Vue 开发的项目越来越多,一般都是配套使用 axios。

二、如何判断简单数据类型和复杂数据类型?

2.1数据类型有哪些

基本类型(简单类型):Number、String、Boolean、undefined、null、Symbol(ES6 新增)

引用类型(复杂类型):Object、Array、Function

2.2简单数据类型的判断

2.2.1 Typeof

typeof 5          // "number"
typeof true       // "boolean"
typeof 'text'     // "string"
typeof Symbol(1)  // "symbol"
typeof undefined  // "undefined"
typeof null       // "object"

2.2.2Instanceof

不能直接判断数据类型,instanceof是用来判断数据是否是某个对象的实例,返回一个布尔值。

5 instanceof Number // false
new Number(5) instanceof Number  // true

// 因为原型链继承的关系,instanceof 会把数组都识别为 Object 对象,所有引用类型的祖先都是 Object 对象
let arr = [1, 2];
console.log(Object.prototype.toString.call(arr) === '[object Array]') // true
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true

let fn = function(){};
console.log(fn instanceof Object)  // true

2.3复杂数据类型的判断

2.3.1 Object.prototype.toString

在判断数据类型时,我们称Object.prototype.toString 为“万能方法”“终极方法”,工作中也是比较常用而且准确。

var arr = [1,2,3];
Object.prototype.toString.call(arr)  //'[object Array]'

var obj = {};
Object.prototype.toString.call(obj)  //'[object Object]'

2.3.2 constructor

constructor 是 JavaScript 中所有对象的一个重要属性,通常指向创建该对象的构造函数。也就是说,通过某个构造函数创建的对象,其 constructor 属性指向这个构造函数本身。

使用 constructor 可以查看目标构造函数,也可以进行数据类型判断。但是不能判断 null 和 undefined,因为这两个特殊类型没有其对应的包装对象。constructor 和 instanceof 类似,constructor 返回结果的是自己的构造函数,而 instructor 则是自己与构造函数比较返回布尔值。

console.log('123:', '123'.constructor); // String
console.log('345:', (345).constructor); // Number
console.log('a:', [1, 2, 3].constructor); // Array
console.log('o:', { name: 'jack', age: 18 }.constructor); // Object
console.log('undefined:', undefined.constructor); // 报错
console.log('null:', null.constructor); // 报错

三、Vuex与Pinia的区别?

3.1、设计理念与架构

  • Vuex:采用全局单例模式,通过一个 store 对象来管理所有的状态。强调集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

  • Pinia:采用分离模式,每个组件都拥有自己的 store 实例。强调简单、轻量级的状态管理,使得代码更加扁平化和易于维护。

3.2、模块化设计

  • Vuex:提供了 States、Mutations、Getters、Actions、Modules 五个模块。Modules 模块使得可以将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getters,甚至是嵌套子模块。

  • Pinia:只有 States、Getters、Actions 三个模块设计。没有 modules 配置,每一个独立的仓库都是通过 defineStore 生成的。

3.3、语法使用与兼容性

  • Vuex:当前最新版是 4.x,Vuex4 用于 Vue3,Vuex3 用于 Vue2。提供了丰富的功能和插件,如严格模式等。

  • Pinia:当前最新版是 2.x,既支持 Vue2 也支持 Vue3。提供了更好的 TypeScript 支持,使得代码更加简洁和易于维护。

3.4、体积与性能

  • Vuex:体积相对较大,因为提供了更多的功能和插件。性能稳定,但可能不如 Pinia 在某些方面高效。

  • Pinia:体积较小,大约只有 1KB。使用了新的 ES6 语法和数据处理方式,因此具有更好的性能。

四、vue的for循环为什么要加key?

4.1 确保元素唯一性

在 Vue 中,key 属性用于唯一标识一个元素或组件。这个唯一性能够帮助 Vue 高效地追踪每个节点,从而在更新时减少不必要的操作。具体原因如下:

  • 唯一标识:每个元素都有一个唯一的 key,这使得 Vue 能够准确识别哪些元素发生了变化,哪些没有。

  • 防止错误更新:如果没有唯一的 key,Vue 在更新时可能会误判节点,导致错误的更新操作。

  • 避免重复:key 确保了列表中的每个元素都是唯一的,避免了重复元素的出现。

实例说明:

<template>
  <ul>
    <li v-for="item in items" :key="item.id">{{ item.text }}</li>
  </ul>
</template>

在这个例子中,item.id作为key属性,确保了每个li元素都是唯一的,从而避免了错误的更新。

4.2提升渲染性能

  • 高效 Diff 算法:Vue 的 Diff 算法依赖于 key 来快速比较新旧虚拟 DOM 树中的节点,从而减少不必要的 DOM 操作。

  • 减少重绘:通过唯一的 key,Vue 能够确定哪些节点需要更新,哪些可以复用,从而减少了重绘和重排。

  • 优化性能:在列表渲染中,key 属性能够显著提升渲染性能,特别是在大数据量的情况下。

4.3维护组件状态

在使用 Vue 开发复杂应用时,组件的状态管理是一个重要的方面。key 属性在这方面也发挥了重要作用:

  • 状态保持:通过唯一的 key,Vue 能够在组件重新渲染时保持其内部状态。

  • 防止状态混乱:在列表渲染中,key 属性能够确保每个组件实例的状态独立,避免状态混乱。

  • 复用组件:key 属性还可以帮助 Vue 更好地复用组件,减少不必要的实例化操作。

实例说明:

<template>

  <div v-for="item in items" :key="item.id">

    <my-component :data="item"></my-component>

  </div>

</template>

4.4常见误区

  • 不要使用索引作为 key:使用索引作为 key 可能会导致更新错误,特别是在列表项的顺序发生变化时。应尽量使用唯一标识符。

  • 避免重复 key:确保每个 key 在同一父元素下是唯一的,以免引发渲染错误。

  • 合理选择 key 值:选择一个能够唯一标识元素的属性作为 key,如 ID 或其他唯一标识符。

五、watch和watchEffect的异同

5.1相同点

  • 依赖响应式:watch 和 watchEffect 都依赖于 Vue 的响应式系统,能够监测响应式数据的变化。

  • 执行副作用:两者都能响应式地执行有副作用的回调,即当依赖的响应式数据变化时,会触发相应的回调函数。

5.2不同点

初始化执行时机

  • watch 是惰性执行的,即只有在被监听的数据发生变化时才会执行回调函数。如果设置了 immediate: true 选项,则 watch 会在页面首次加载时立即执行一次。

  • watchEffect 在组件初始化时会立即执行一次回调函数,用来收集依赖。之后,当依赖的响应式数据变化时,它也会执行回调函数。

参数不同

  • watchEffect 只需要传递一个回调函数,不需要传递侦听的数据,它会在页面加载时主动执行一次,来收集依赖;而 watch 至少要有两个参数(第三个参数是配置项),第一个参数是侦听的数据,第二个参数是回调函数。

返回值不同

  • watchEffect 获取不到更改前的值;而 watch 可以同时获取更改前和更改后的值。

5.3实例

<template>
  <div>
    <h1>姓名:{{ obj.name }}</h1>
    <h1>年龄:{{ obj.age }}</h1>
    <div>
      <button @click="obj.name += '-'">修改姓名</button>
      <button @click="obj.age++">修改年龄</button>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, watchEffect } from 'vue';
const obj = ref({
  name: 'jack',
  age: 18,
});
const age1 = ref();
watch(
  () => obj.value.name,
  (newVal, oldVal) => {
    console.log(`count 变化,新值为 ${newVal},旧值为 ${oldVal}`);
  }
);
watchEffect(() => {
  age1.value = obj.value.age * 2;
  console.log(age1.value);
});
</script>

六、Vue中解决跨域问题

6.1什么是跨域

跨域(Cross-Origin)指的是浏览器阻止前端网页从一个域名(Origin)向另一个域名的服务器发送请求。具体来说,一个页面的协议、域名、端口三者任意一个与请求的目标地址不同,就被视为跨域请求。

6.2同源策略

同源的意思就是两个页面具有相同的协议,域名和端口号,同源策略是一种约定,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。同源策略会阻止一个域的 javascript 脚本和另外一个域的内容进行交互。

6.3方法1:配置代理服务器

在 Vue 项目的根目录下找到 vue.config.js 文件。如果没有这个文件,可以手动创建。

在 vue.config.js 文件中添加代理配置:

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://example.com', // 目标服务器的地址
        changeOrigin: true, // 是否改变请求源
        pathRewrite: {
          '^/api': '' // 将/api 替换为空字符串
        }
      }
    }
  }
}

在代码中使用/api 作为前缀来发送请求,如:

axios.get('/api/data').then(response => {
  console.log(response.data);
});

这样,所有以/api开头的请求都会被代理到http://example.com服务器上,从而避免跨域问题。

6.4方法2:使用CORS

CORS(跨域资源共享)是一种允许服务器指示浏览器允许来自其他域的请求的机制。具体步骤如下:

// Node.js (Express) 示例
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.get('/data', (req, res) => {
  res.json({ message: 'This is CORS-enabled for all origins!' });
});
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

前端直接发送请求:

axios.get('http://localhost:3000/data').then(response => {
  console.log(response.data);
});

6.5方法3:使用JSONP

JSONP(JSON with Padding)是一种传统的跨域解决方案,通过动态创建<script>标签来实现跨域请求,因为<script>标签不受同源策略的限制。

JSONP 通过在 URL 中传递回调函数的名称,后端返回对应的 JavaScript 代码,前端执行该代码,从而实现数据的获取。

原生的实现方式:

let script = document.createElement('script');
script.src = 'http://www.nealyang.cn/login?username=Nealyang&callback=callback';
document.body.appendChild(script);
function callback(res) {
  console.log(res);
}

当然,jquery 也支持 jsonp 的实现方式:

$.ajax({
    url:'http://www.nealyang.cn/login',
    type:'GET',
    dataType:'jsonp',//请求方式为 jsonp
    jsonpCallback:'callback',
    data:{
        "username":"Nealyang"
    }
})

虽然这种方式非常好用,但是一个最大的缺陷是,只能够实现 get 请求。

6.6方法4:Nginx解决跨域

location / {  
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_headerAccess-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
 
    if ($request_method = 'OPTIONS') {
        return 204;
}
proxy_pass http://192.168.12.1:8081;
}
  • Access-Control-Allow-Origin:服务器默认是不被允许跨域的。给 Nginx 服务器配置 Access-Control-Allow-Origin *后,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。

  • Access-Control-Allow-Headers:是为了防止出现以下错误:Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.这个错误表示当前请求 Content-Type 的值不被支持。其实是我们发起了"application/json"的类型请求导致的。这里涉及一个概念:预检请求(preflight request),请看下面"预检请求"的介绍。

  • Access-Control-Allow-Methods:是为了防止出现以下错误:Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

  • 给 OPTIONS 添加 204 的返回:是为了处理在发送 POST 请求时 Nginx 依然拒绝访问的错误,发送"预检请求"时,需要用到方法 OPTIONS,所以服务器需要允许该方法。

七、mixins 和 hooks 的区别

7.1什么是混入(mixins)

混入(Mixins)是Vue.js(包括Vue2和Vue3)中的一个特性,它允许开发者将一组组件选项合并到目标组件中。通过混入,可以在多个组件之间共享重复的逻辑、方法和数据,从而提高代码的可复用性和可维护性。混入是一个对象,它可以包含任何组件选项,如data、methods、computed等。当组件使用混入对象时,所有混入对象的选项将被“混入”到该组件本身的选项中,这意味着组件可以接收混入对象的方法、生命周期钩子、计算属性等。

实例:

首先,创建一个名为 mixins.js 的文件,用于存放混入对象:

// mixins.js
export default {  
  data() {  
    return {  
      mixinData: 'This is data from mixin'  
    };  
  },  
  created() {  
    console.log('Mixin created hook called');  
  },  
  methods: {  
    mixinMethod() {  
      console.log('This is a method from mixin');  
    }  
  }  
};

在这个例子中,创建了一个名为 myMixin 的混入,它有一个响应式数据 mixinData、一个生命周期钩子 created 和一个方法 mixinMethod。

在组件中引入并使用混入:

<template>  
  <div>  
    <p>{{ mixinData }}</p>  
    <button @click="mixinMethod">Call Mixin Method</button>  
  </div>  

</template>  
  
<script>  
import myMixin from './myMixin.js'; 
  
export default {  
  mixins: [myMixin],  
  data() {  
    return {  
      componentData: 'This is data from the component'  
    };  
  },  
  mounted() {  
    console.log('Component mounted hook called');  
  }  
};  

</script>

然后,我们在一个 Vue 3 组件中通过 mixins 选项引入了 myMixin。

7.2 什么是hooks

在计算机编程领域,特别是在前端框架中,hooks 通常指的是一种特定的函数或回调机制。

Vue3 中的 Hooks:Vue3 引入了 Composition API,其中包括了一组基于函数的 API,这些 API 允许开发者以更加灵活和模块化的方式来组织和复用组件逻辑。这些基于函数的 API 就被称为 hooks。常用的 hooks 包括 setup、ref、reactive、computed 等,以及开发者自定义的 hooks。通过 hooks,可以将组件的逻辑封装在函数中,按需调用,提高了代码的可读性和可维护性。

实例:
首先,创建一个名为 mixins.js 的文件,用于存放 hooks 对象:

// mixins.js
import { ref } from 'vue';
export const useCounter = () => {
  const count = ref(0);
  const increment = () => {
    count.value++;
  };

  return {
    count,
    increment,
  };
};

在这个例子中,定义了一个名为 useCounter 的hooks。这里,useCounter 函数提供了一个简单的计数器逻辑,包括一个响应式的 count 状态和一个 increment 方法来增加 count 的值。

在组件中引入并使用混入:

<template> 
  <div>  
    <p>Count: {{ count }}</p>  
    <button @click="increment">Increment</button>  
  </div>  
</template>  


<script>  
import { useCounter } from './mixins.js'; // 注意文件路径可能需要调整  

export default {  
  setup() {  
    const { count, increment } = useCounter();  
    return { count, increment };  
  },  
};  

</script>

7.3 mixins和hooks的区别

在 Vue 3 中,mixins 和 hooks(或称为 Composition API 中的 hooks)都是用于复用组件逻辑的方式,但它们之间存在一些关键的区别。

Mixins

  • Mixins 是一种将多个组件中共享的选项合并到一起的技术。在 Vue 2 中,mixins 被广泛使用,而在 Vue 3 中虽然仍然支持,但官方推荐使用 Composition API 来替代 mixins,因为 mixins 有一些缺点。
    • Mixins 可能导致命名冲突,因为它们共享的是同一个对象。
    • Mixins 使得组件实例难以理解,因为一个组件可能从多个 mixins 继承属性和方法,这使得组件的逻辑变得分散和难以追踪。
    • Mixins 不支持 TypeScript 的类继承,这限制了其在 TypeScript 项目中的应用。

Hooks (Composition API)

  • Vue 3 引入了 Composition API,这是一种新的逻辑复用机制,它通过 hooks(如 useState, useEffect, useContext 等)来实现。Hooks 允许用户更加灵活地组织和复用逻辑,而不是通过继承 mixins。
    • Hooks 提供了一种更清晰的方式来组织和复用逻辑,每个 hook 都是一个独立的函数,可以单独使用或组合使用。
    • Hooks 解决了 mixins 中的命名冲突问题,因为每个 hook 都有自己的作用域。
    • Hooks 与 TypeScript 兼容,可以更好地支持类型检查和自动补全。
    • Hooks 使得逻辑更加模块化,易于理解和维护。

对比总结

  • 组织方式:Mixins 通过对象合并共享逻辑,而 Hooks 通过函数调用来组合逻辑。
  • 可维护性:Hooks 通常更容易理解和维护,因为逻辑是模块化的。
  • 灵活性:Hooks 提供了更多的灵活性,允许开发者根据需要组合不同的逻辑片段。
  • TypeScript 支持:Hooks 与 TypeScript 的兼容性更好。

尽管 Vue 3 仍然支持 mixins,但官方推荐使用 Composition API(hooks)来组织和复用组件逻辑,因为它们提供了更好的开发体验和更强的类型安全性。

八、toref和torefs的区别

toRef 和 toRefs 可以用来复制 reactive 里面的属性然后转成 ref,而且它既保留了响应式,也保留了引用(解释:也就是修改了源数据,响应式数据本身会修改;修改了响应式,源数据也会同步修改,且视图都会更新)。toRefs 底层其实是循环的 toRef。

  • toRef:复制 reactive 里的单个属性并转成 ref。
  • toRefs:复制 reactive 里的所有属性并转成 ref。

实例:
toRef 代码如下

<template>
  <div>
    <p>姓名:{{ nameRef.value }}</p>
    <p>年龄:{{ person.age }}</p>
    <button @click="changeName">修改姓名</button>
  </div>
</template>

<script setup>
import { reactive, toRef } from 'vue';
const person = reactive({ name: '张三', age: 18 });
const nameRef = toRef(person, 'name');

const changeName = () => {
  nameRef.value = '李四';
};
</script>

简单来说就是toRef适合创建单个属性的响应式引用,这个引用是响应式的,当源对象的该属性发生变化时,通过toRef创建的引用也会同步更新。

toRefs 代码如下

<template>
  <div>
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ age }}</h2>
    <button @click="changeName">增加~</button>
    <button @click="changeAge">加一</button>
  </div>
</template>
<script setup>
import { toRefs, reactive } from 'vue'
let person = reactive({
  name: '张三',
  age: 18
})
let { name, age } = toRefs(person)
const changeAge = () => {
  age.value += 1
  console.log(age);
}
const changeName = () => {
  name.value += '!'
  console.log(name);
}
</script>

我们在解构赋值的时候给 person 加上了 toRefs,就是让解构出来的值变成 ref 所定义的响应式数据。

整体来讲:

  • toRef:复制 reactive 里的单个属性并转成 ref。
  • toRefs:复制 reactive 里的所有属性并转成 ref。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值