一、Ajax与axios的区别?
1.1Ajax
Ajax,即 “Asynchronous Javascript And XML(异步 JavaScript 和 XML)”。通俗地理解,Ajax 是在网页中利用 XMLHttpRequest 对象与服务器进行数据交互的方式。它是对原生 XHR 的封装,为了实现跨域目的,增添了对 JSONP 的支持。
Ajax的工作原理:
Ajax 的工作原理分为五个步骤:
-
创建 XMLHttpRequest 异步对象;
-
注册回调函数;
-
使用 open 方法与服务器建立连接;
-
向服务器发送数据;
-
创建回调函数。
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。