精选前端面试题(持续更新中ing)
1、js中set和map的作用和区别?
在 JavaScript 中,Set 和 Map 是两种非常重要的集合类型
1、Set 是一种集合数据结构,用于存储唯一值。它类似于数组,但成员的值都是唯一的,没有重复的值。
Set 中的值只能是唯一的,任何重复的值都会被自动忽略。
Set 中的值可以是任何数据类型(原始值或对象引用)。
Set 提供了操作集合的方法,比如添加、删除、检查成员等。
add(value): 添加一个值到 Set 中,如果该值已存在,则不会改变 Set。
delete(value): 从 Set 中删除一个值,如果删除成功则返回 true,否则返回 false。
has(value): 检查 Set 中是否包含一个值,如果包含则返回 true,否则返回 false。
clear(): 清除 Set 中的所有值。
size: 获取 Set 中元素的数量
let mySet = new Set();
mySet.add(1);
mySet.add(1);
console.log(mySet); // 输出: Set {1}
2、Map 是一种键值对集合,类似于对象,但“键”的范围不限于字符串和符号,可以是任何数据类型(原始值或对象引用)。
Map 中的键和值都是唯一的,键和值可以是任意类型。
Map 保持插入顺序,即迭代 Map 对象时,元素的顺序与它们被插入的顺序一致。
Map 提供了操作键值对的方法,比如设置、获取、删除、检查键等。
set(key, value): 设置 Map 对象中指定元素的值。
get(key): 返回指定键的值,如果不存在则返回 undefined。
delete(key): 移除 Map 对象中指定的元素,如果删除成功则返回 true,否则返回 false。
has(key): 判断 Map 对象中是否存在指定的键,如果存在则返回 true,否则返回 false。
clear(): 移除 Map 对象中的所有键/值对。
size: 获取 Map 中键值对的数量。
let myMap = new Map();
myMap.set('name', 'Alice');
myMap.set(1, 'first');
myMap.set(1, 'second'); // 后面的值会覆盖前面的值,但键还是1
console.log(myMap); // 输出: Map(2) { 'name' => 'Alice', 1 => 'second' }
3、区别
存储内容:
Set 只存储唯一值,没有键和值的区分。
Map 存储键值对,键和值都可以是任意类型,且键是唯一的。
键的类型:
Set 中的元素作为值,没有键。
Map 中的元素作为键,每个键都映射到一个值。
顺序:
Set 和 Map 都保持插入顺序。
方法:
Set 提供了与集合操作相关的方法,如 add、delete、has 等。
Map 提供了与键值对操作相关的方法,如 set、get、delete、has 等。
2、flex 1是哪几个的缩写?
flex: 1 是 flex-grow、flex-shrink 和 flex-basis 的简写形式。具体来说:
flex-grow: 1:表示弹性盒子在主轴方向上的增长比例为 1,即它可以占用剩余空间。
flex-shrink: 1:表示弹性盒子在主轴方向上的收缩比例为 1,即在空间不足时可以缩小。
flex-basis: 0%(或简写为 0,在某些情况下等同于 auto 的初始效果,但在此上下文中通常理解为 0 以确保空间分配):表示弹性盒子的初始大小为 0,这样可以确保所有设置了 flex: 1 的弹性盒子平分剩余空间。
3、flex布局有哪几种属性?
display: flex;
flex-direction;//决定主轴的方向(即项目的排列方向)
flex-wrap;//定义如果一条轴线排不下项目,如何换行。
justify-content;//定义项目在主轴上的对齐方式 space-between,space-around,space-evenly
align-items;//定义项目在交叉轴上如何对齐
align-content;//定义了多根轴线(多行)在交叉轴上的对齐方式
4、vuex里变各属性介绍?
Vuex 包含五个核心概念,分别是 state、getters、mutations、actions 和 modules。
1、state
定义:state 是 Vuex 中的基本数据,用于存储变量,相当于Vue 组件中的 data。
特性:state 中的数据是响应式的,当数据发生变化时,依赖该数据的组件会自动更新。
使用:在组件中可以通过 this.$store.state.xxx 或使用辅助函数 mapState 将数据映射到组件的 computed 计算属性中来访问 state 中的数据。
2、getters
定义:getters 是从 state 中派生的数据,相当于 state 的计算属性。
特性:getters 的返回值会根据其依赖的 state 值的变化而重新计算,并且具有缓存功能,只有当依赖值发生变 化时才会重新计算。
使用:在组件中可以通过 this.$store.getters.xxx 或使用辅助函数 mapGetters 将 getters 映射到组 件的 computed 计算属性中来访问 getters 的返回值。
3、mutations
定义:mutations 是 Vuex 中唯一允许更新 state 的方法,它必须是同步的。
特性:每个 mutation 都有一个字符串的事件类型(type)和一个回调函数(handler),回调函数用于执行状 态更新操作,并且会接受 state 作为第一个参数。
使用:在组件中通过 this.$store.commit('xxx', payload) 来触发 mutation,其中 'xxx' 是 mutation 的事件类型,payload 是传递给 mutation 的额外参数。
4、actions
定义:actions 类似于 mutations,但不同的是 actions 提交的是 mutations,而不是直接变更状态,并且 actions 可以包含任意异步操作。
特性:actions 的回调函数接收一个上下文对象(context),该对象包含了 commit 方法用于触发mutation, 以及 state 和 getters 等属性用于访问状态和 getters。
使用:在组件中通过 this.$store.dispatch('xxx', payload) 来触发 action,其中 'xxx' 是 action 的事件类型,payload 是传递给 action 的额外参数。或使用辅助函数 mapActions
5、modules
定义:modules 是 Vuex 的模块化机制,允许将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getter,甚至是嵌套子模块。
特性:模块化使得 Vuex 的结构更加清晰,方便管理。每个模块都是独立的,可以单独进行状态管理,同时也可以通 过 namespaced: true 来启用命名空间,避免不同模块之间的命名冲突。
使用:在创建 Vuex store 时,可以通过 modules 选项来定义模块。在组件中访问模块的状态和方法时,需要 使用模块的命名空间(如果启用了命名空间)来区分不同模块的状态和方法。
//一:定义store
//引入Vue和Vuex库
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);// 使用Vuex插件
//定义一个名为cartModule的模块,用于管理购物车状态
const cartModule = {
// state对象包含模块的状态
state: {
items: [],// 购物车中的商品列表
totalQuantity: 0,// 购物车中商品的总数量
totalPrice: 0.0,// 购物车中商品的总价格
checkoutStatus: false,// 结账状态,默认为未结账
lastAddedItemId: 0// 最后添加商品的ID,用于唯一标识商品
},
// getters对象包含基于state的派生状态(计算属性)
getters: {
getItems: state => state.items,// 获取购物车中的商品列表
getTotalQuantity: state => state.totalQuantity,// 获取购物车中商品的总数量
getTotalPrice: state => state.totalPrice,// 获取购物车中商品的总价格
getCheckoutStatus: state => state.checkoutStatus,// 获取结账状态
getLastAddedItemId: state => state.lastAddedItemId// 获取最后添加商品的ID
},
// mutations对象包含改变状态的同步方法
mutations: {
ADD_ITEM(state, item) {//向购物车中添加商品,并更新总数量和总价格
state.items.push({ ...item, id: ++state.lastAddedItemId });
state.totalQuantity += item.quantity;//总数量
state.totalPrice += item.price * item.quantity;//价格*总数量
},
REMOVE_ITEM(state, itemId) {// 从购物车中移除指定ID的商品,并更新总数量和总价格
const item = state.items.find(i => i.id === itemId);
if (item) {
state.totalQuantity -= item.quantity;
state.totalPrice -= item.price * item.quantity;
state.items = state.items.filter(i => i.id !== itemId);
}
},
SET_CHECKOUT_STATUS(state, status) {// 设置结账状态
state.checkoutStatus = status;
}
},
// actions对象包含改变状态的异步方法或批量更新
actions: {
addItem({ commit }, item) {// 调用mutation方法向购物车中添加商品
commit('ADD_ITEM', item);
},
removeItem({ commit }, itemId) {// 调用mutation方法从购物车中移除商品
commit('REMOVE_ITEM', itemId);
},
checkout({ commit }) {// 调用mutation方法设置结账状态为已结账
commit('SET_CHECKOUT_STATUS', true);
}
}
};
// 创建Vuex store实例,并将cartModule作为模块传入
export default new Vuex.Store({
modules: {
cart: cartModule//namespaced: true 启用了命名空间,可以定义多个不同的store
}
});
<template>
<div>
<h1>购物车</h1>
<!-- 使用v-for指令遍历购物车中的商品列表 -->
<ul>
<li v-for="item in items" :key="item.id">
<!-- 显示商品名称、数量和价格 -->
{{ item.name }} - {{ item.quantity }} x ${{ item.price }}
<!-- 提供一个按钮用于移除当前商品 -->
<button @click="removeItem(item.id)">移除</button>
</li>
</ul>
<!-- 显示购物车中商品的总数量和总价格 -->
<p>总数量: {{ totalQuantity }}</p>
<p>总价: ${{ totalPrice }}</p>
<!-- 提供一个按钮用于添加新商品 -->
<button @click="addItem">添加商品</button>
<!-- 提供一个按钮用于结账 -->
<button @click="checkout">结账</button>
<!-- 如果结账状态为已结账,则显示已结账提示 -->
<p v-if="checkoutStatus">已结账</p>
</div>
</template>
<script>
export default {
// 使用computed属性从Vuex store中获取购物车状态
computed: {
items() {
// 使用getters获取购物车中的商品列表
return this.$store.getters['cart/getItems'];
},
totalQuantity() {
// 使用getters获取购物车中商品的总数量
return this.$store.getters['cart/getTotalQuantity'];
},
totalPrice() {
// 使用getters获取购物车中商品的总价格
return this.$store.getters['cart/getTotalPrice'];
},
checkoutStatus() {
// 使用getters获取结账状态
return this.$store.getters['cart/getCheckoutStatus'];
}
},
// 定义方法用于与Vuex store交互
methods: {
addItem() {
// 创建一个新商品对象,并通过dispatch调用action方法将其添加到购物车中
const newItem = { name: '商品', quantity: 1, price: 10.0 };
this.$store.dispatch('cart/addItem', newItem);
},
removeItem(itemId) {
// 通过dispatch调用action方法从购物车中移除指定ID的商品
this.$store.dispatch('cart/removeItem', itemId);
},
checkout() {
// 通过dispatch调用action方法设置结账状态为已结账
this.$store.dispatch('cart/checkout');
}
}
};
</script>
5、ref()和 reactive() 的作用以及区别
ref() 和 reactive() 都是 Vue.js 3 中用于创建响应式数据的方法
一、ref() 的作用及特点
1、ref() 主要用于包装 JavaScript 的基本类型数据(如字符串、数字、布尔值等),使其具有响应性。
2、它返回一个响应式引用对象,该对象有一个 .value 属性来存储传入的值
3、在html模板中,Vue 会自动解包 ref,使得可以直接使用 ref 变量而无需 .value 属性。
4、在JavaScript代码中访问或修改 ref 包装的数据时,需要通过 .value 属性来获取或设置其实际值
二、reactive() 的作用及特点
1、reactive() 主要用于包装 JavaScript 对象和数组等复杂类型的数据,使其具有响应性
2、它返回一个响应式代理对象,该对象可以拦截对原始对象的各种操作(如属性读取、赋值等),并在数据变化时触 发更新。
3、可以直接访问和修改对象或数组的属性或元素,而无需使用 .value 属性。
4、内部使用 Proxy 对象来实现数据代理和响应式机制。
三、使用场景:
当需要处理简单的、单一的响应式数据时,优先选择 ref()。
当需要处理复杂对象或数组时,考虑使用 reactive()。
四、实现原理:
ref() 通过 Object.defineProperty() 的 get 和 set 方法实现数据代理。
reactive() 使用 ES6 的 Proxy 对象来实现数据代理,可以更细粒度地控制对象数据的访问和修改。
import { reactive,ref } from 'vue';
const count = ref(0); // 使用 ref() 包装一个数字
function increment() {
count.value++; // 通过 .value 属性修改值
}
const user = reactive({
name: 'John Doe',
age: 30
}); // 使用 reactive() 包装一个对象
function updateUser() {
user.name = 'Jane Doe'; // 直接修改对象属性
user.age = 28;
}
6、箭头函数和普通函数的区别?
1、箭头函数:使用箭头(=>)来定义,语法更加简洁;
普通函数:使用function关键字来定义,语法相对传统;
2、箭头函数:如果只有一个参数,可以省略参数周围的括号;如果函数体只有一行代码且不需要返回值(即隐式返回),可以省略大括号和return关键字。
普通函数:参数和函数体都需要使用括号明确包围;
3、箭头函数:没有自己的this,它会捕获其所在上下文的this值作为自己的this值。这意味着在箭头函数内部,this始终指向定义该函数时的上下文。
普通函数:this的指向是可变的,它通常指向调用它的对象。在严格模式下('use strict'),如果未指定上下文(即非方法调用),this将默认为undefined;在非严格模式下,将默认为全局对象(在浏览器中通常是window)
4、箭头函数:不绑定arguments对象。如果需要访问传递给函数的参数列表,可以使用剩余参数(...args)语法。
普通函数:每次调用都会创建一个新的arguments对象,该对象包含传递给函数的所有参数。
5、箭头函数:不能用作构造函数,因此没有prototype属性。
普通函数:可以用作构造函数来创建对象实例
6、箭头函数:不能使用super关键字
普通函数:在类的方法中,可以使用super关键字来调用父类的方法或访问父类的属性。
7、css中定位有哪些,有什么区别,哪些会影响性能
一、静态定位(Static)
特点:这是所有元素的默认定位方式。元素按照正常的文档流进行排列,不会受到top、bottom、left、right属性的影响。
二、相对定位(Relative)
特点:元素相对于其正常位置进行定位。即使移动了元素,它原本在文档流中的空间仍然会被保留。
三、绝对定位(Absolute)
特点:元素脱离正常的文档流,相对于最近的已定位(即position属性不为static)的祖先元素进行定位。如果没 有已定位的祖先元素,则相对于文档的初始包含块(通常是<html>元素或浏览器窗口)进行定位。(父相子绝)
四、固定定位(Fixed)
特点:元素相对于浏览器窗口进行定位,无论页面如何滚动,元素始终保持在指定的位置。元素脱离文档流,不占据 原来的空间。
五、粘性定位(Sticky)
特点:元素在滚动到特定位置之前表现为相对定位,滚动到特定位置后表现为固定定位。这允许元素在滚动过程中固 定在某个位置,直到滚动超出其父容器的边界。
六、对性能的影响
*静态定位和相对定位:由于它们不改变元素在文档流中的位置或只进行微小的偏移,因此对性能的影响较小。
*绝对定位和固定定位:元素脱离文档流,可能导致其他元素重新排列以填补空白。这可能会增加浏览器的重排和重绘工作,从而在某些情况下影响性能。然而,对于少量元素或简单布局,这种影响通常是微不足道的。
*粘性定位:粘性定位的实现可能涉及复杂的计算和状态切换,因此在某些情况下可能对性能产生一定影响。然而,现代浏览器已经对粘性定位进行了优化,以提供流畅的用户体验。
8、重排和重绘的区别?
1、重排(Reflow):
*当页面元素的尺寸、结构或某些属性(如边距、内边距、边框宽度、字体大小等)发生改变时,会触发重排。
*添加、删除DOM元素,或者改变DOM元素的位置也会触发重排。
*浏览器窗口大小的变化(如调整窗口大小或旋转设备)同样会导致重排。
2、重绘(Repaint):
*当页面元素的样式发生改变,但这些改变不影响元素在文档流中的位置和大小时,会触发重绘。
*常见的触发重绘的样式变化包括颜色、背景、文本颜色、边框颜色等的改变。
3、性能开销
重排:
*重排是一种比较昂贵的操作,因为它需要浏览器重新计算元素的几何属性并重新布局页面。
*这会消耗较多的计算资源和时间,尤其是在页面包含大量元素或复杂布局的情况下。
重绘:
*相比重排,重绘是一种比较轻量级的操作。
*它只需要浏览器重新渲染元素的外观,而不需要重新计算元素的位置和大小。
4、优化建议
*合并样式改变:尽量将多次样式改变合并成一次,以减少重排和重绘的次数。
*使用CSS动画:利用CSS动画代替JavaScript操作来更新样式,因为CSS动画通常在浏览器内部进行了优化,可以减少性能开销。
*避免频繁操作DOM:减少不必要的DOM操作,尤其是在循环或频繁触发的事件处理程序中。
*使用绝对定位或固定定位:对于不需要参与文档流布局的元素,可以使用绝对定位或固定定位来减少对其他元素的影响。
*利用文档片段:在大量添加或删除DOM元素时,可以使用文档片段(DocumentFragment)来减少重排次数。文档片段是一个轻量级的文档对象,可以在其中构建DOM结构,然后一次性将其添加到页面中。
9、vue中nexttick的作用和用法,以及场景
1、$nextTick的主要作用是确保在DOM更新完成后执行一些操作。在Vue中,数据的更新是异步的,即Vue会异步执行更新队列,而不是立即操作DOM。因此,如果需要在数据更新后立即获取更新后的DOM元素或执行某些依赖于最新DOM状态的操作,就需要使用$nextTick。
// 使用回调函数
this.$nextTick(function() {
// 在DOM更新后执行的代码
console.log('DOM已更新');
});
// 使用Promise对象
this.$nextTick().then(function() {
// 在DOM更新后执行的代码
console.log('DOM已更新');
});
2、常见使用场景
(1)在数据变化后立即获取更新后的DOM:
当数据发生变化后,如果需要立即获取更新后的DOM元素的状态(如尺寸、位置、属性等),可以使用$nextTick。
(2)确保在DOM更新后执行某些操作:
有时需要在DOM更新后执行一些操作,比如添加或删除元素、更新元素的样式、触发动画效果等。使用$nextTick可以确保这些操作在DOM更新后进行,避免操作无效或报错。
(3)在组件的生命周期钩子中使用:
在Vue组件的mounted和updated生命周期钩子中,可以使用$nextTick来确保在DOM更新后执行某些逻辑。这特别适用于需要在组件挂载或更新后立即操作DOM的场景。
(4)在动态渲染组件时使用:
当动态渲染组件时,可以使用$nextTick来确保组件已经渲染完成。这对于需要在组件渲染后立即执行某些操作的场景非常有用。
(5)在集成第三方库时使用:
在Vue中集成第三方库时,有时需要确保第三方库在正确的DOM状态下初始化。使用$nextTick可以确保在DOM更新完成后初始化第三方库。
10、computed和watch的区别?
computed和watch是两个用于响应数据变化的特性。
一、computed(计算属性)
计算属性是基于其他数据计算得出的属性,它的值会根据依赖的数据自动更新。
计算属性会被缓存,只有当依赖的数据发生变化时,才会重新计算
计算属性更适合处理数据的计算和衍生,它提供了一种简洁和高效的方式来处理数据的计算和格式化。
计算属性返回一个新的计算后的值,通常用于模板中。
当需要根据其他数据进行计算或格式化时,例如根据输入框的值计算出其他相关数据、根据列表数据计算出统计信息等。
computed在依赖未变化的情况下避免了多次计算,适用于频繁读取的数据。
二、watch(侦听器)
watch用于监视数据的变化,并在数据变化时执行相应的操作。
watch可以监听单个数据、对象的属性或数组的变化,并且可以进行深度监听。
watch适用于需要在数据变化时执行异步或开销较大的操作,例如发送网络请求、处理复杂的计算逻辑等。
watch提供了更灵活的方式来响应数据的变化,并且可以处理更复杂的业务逻辑。
watch的回调函数没有返回值,它的重点在于执行的副作用,例如更新DOM、调用方法等。
当需要监听某个数据的变化并执行异步操作时,例如API请求或复杂的逻辑处理。
当需要在数据变化后触发某些副作用,例如重置表单、清空数据等操作时。
watch更多地用于处理复杂的逻辑和异步操作,可能在性能方面考虑较少。
11、v-if和v-for在vue2和vue3中的区别?
1、在Vue2中
当v-if和v-for同时出现在一个元素上时,v-for的优先级高于v-if。
这意味着v-for会先遍历数据,然后再根据v-if的条件决定是否渲染每个元素。
2、在Vue3中
在Vue3中,v-if的优先级高于v-for。
这意味着Vue会先根据v-if的条件过滤数据,然后再对过滤后的数据进行v-for遍历。
12、vue中操作dom的方法有哪些
1、使用 ref 引用 DOM 元素
<template>
<div>
<input ref="myInput" type="text" />//引用信息将会注册到父组件的 $refs 对象上。
<button @click="focusInput">Focus Input</button>
</div>
</template>
<script>
export default {
methods: {
focusInput() {
this.$refs.myInput.focus();//引用信息将会注册到父组件的 $refs 对象上。
}
}
}
</script>
2、使用生命周期钩子
//在 mounted 钩子中,组件的 DOM 已经被渲染和插入到文档中。
<template>
<div ref="myDiv">
Hello, Vue!
</div>
</template>
<script>
export default {
mounted() {
this.$refs.myDiv.style.color = 'red';
}
}
</script>
3、使用第三方库(如 jQuery)
<template>
<div ref="myDiv">
Hello, Vue with jQuery!
</div>
</template>
<script>
import $ from 'jquery';
export default {
mounted() {
$(this.$refs.myDiv).css('color', 'blue');
}
}
</script>
13、vue中父子组件间传参的方式有哪些?
一、使用Props传递参数
父组件通过属性向子组件传递数据。子组件通过props选项接收父组件传递的数据,并在模板中使用这些数据。
*数据传递是单向的,即父组件向子组件传递
二、使用事件传递参数
子组件通过触发事件向父组件传递数据
子组件使用$emit方法触发事件,并传递数据作为参数,父组件通过监听子组件触发的事件来接收数据。
三、使用Provide和Inject(依赖注入)
父组件通过provide提供数据,子组件通过inject注入这些数据
可以实现跨多个层级的组件通信,适用于祖孙组件或更深层次的组件间通信
四、使用parent和children
父组件通过$children属性访问子组件实例,子组件通过$parent属性访问父组件实例。
五、使用Vuex或事件总线(Bus)
//一、使用Props传递参数
//父组件
<template>
<div>
<ChildComponent :message="parentMessage"></ChildComponent>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
parentMessage: 'Hello from Parent!'
};
}
}
</script>
//子组件
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
props: {
message: {
type: String,
required: true
}
}
}
</script>
//二、使用事件传递参数
//父组件
<template>
<div>
//和子组件$emit的第一个参数保持一直
<ChildComponent @message-from-child="handleMessageFromChild"></ChildComponent>
<p>{{ childMessage }}</p>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
childMessage: ''
};
},
methods: {
handleMessageFromChild(message) {//监听的事件
this.childMessage = message;
}
}
}
</script>
//子组件
<template>
<button @click="sendMessageToParent">Send Message to Parent</button>
</template>
<script>
export default {
methods: {
sendMessageToParent() {
//第一个参数要个父组件@方法名保持一致,第二个参数是要传递的内容
this.$emit('message-from-child', 'Hello from Child!');
}
}
}
</script>
14、vue3中路由守卫?
Vue Router中的路由守卫主要分为全局守卫、路由独享守卫和组件内守卫三种类型。
一、全局守卫
beforeEach:在路由即将改变前调用,参数包括即将进入的目标路由对象to、即将离开的当前路由对象from以及一 个用于阻止导航的next函数。
beforeResolve:在路由解析之后但在导航确认之前调用,通常用于数据预取。
afterEach:在路由改变之后调用,不接收next函数,因此不能改变导航结果。
二、路由独享守卫
路由独享守卫是在单个路由配置对象中定义的,它们只会在该路由匹配时生效。常见的路由独享守卫有:
beforeEnter:在路由即将进入前调用,参数包括即将进入的目标路由对象to、即将离开的当前路由对象from以及一个用于阻止导航的next函数。
三、组件内守卫
组件内守卫是在Vue组件内部定义的,它们会在组件的生命周期钩子中调用。
beforeRouteEnter:在路由进入组件之前调用,此时组件实例还未创建,不能访问this。参数包括即将进入的目标路由对象to、即将离开的当前路由对象from以及一个用于继续导航的next函数(在next中传递的参数会作为组件的初始props)。
beforeRouteUpdate:在路由更新组件时调用(例如,从一个/user/1路由跳转到/user/2)。此时组件实例已经存在,可以访问this。参数同样包括to、from和next。
beforeRouteLeave:在导航离开组件时调用。参数包括即将离开的当前路由对象from、即将进入的目标路由对象to以及一个用于阻止导航的next函数。
// 全局守卫
router.beforeEach((to, from, next) => {
// 权限验证逻辑
if (to.meta.requiresAuth && !isAuthenticated) {
// 未登录则重定向到登录页面
next({ path: '/login' });
} else {
// 已登录或不需要验证则继续导航
next();
}
});
// 路由独享守卫
const route = {
path: '/profile',
component: Profile,
beforeEnter: (to, from, next) => {
// 特定路由的验证逻辑
if (to.meta.requiresProfile) {
// 验证逻辑...
next();
} else {
next({ path: '/' });
}
}
};
// 组件内守卫
export default {
name: 'UserProfile',
beforeRouteEnter(to, from, next) {
// 组件即将进入前的逻辑
next(vm => {
// 可以通过vm访问组件实例
});
},
beforeRouteUpdate(to, from, next) {
// 路由更新时的逻辑
next();
},
beforeRouteLeave(to, from, next) {
// 离开组件前的逻辑
next();
}
};
15、js中原型和原型链?
一、原型(Prototype)
*在JavaScript中,对象有一个特殊的隐藏属性[[Prototype]],它要么为null,要么就是对另一个对象的引用,该对象被称为“原型”
*通过原型,可以定义对象的共享属性和方法。这意味着所有对象实例都可以访问和修改这些属性和方法,这在创建大量具有相同属性和方法的对象时非常有用,因为它可以避免在每个对象实例中重复定义这些属性和方法。
*原型还可以用于动态修改和扩展对象,允许我们在不修改原始构造函数的情况下,为所有对象实例添加新的属性和方法。
二、原型链(Prototype Chain)
原型链是JavaScript中实现对象继承的主要机制。当一个对象试图访问一个属性时,如果它自身没有这个属性,JavaScript会在它的原型链上查找这个属性,直到找到这个属性或者到达链的尽头(null)。
原型是 JavaScript 中每个对象上都有的一个特殊属性,它指向另一个对象,这个对象即原型对象。原型对象也可以有自己的原型,这样就形成了一条链,即原型链。
原型链是由一系列原型对象连接而成的链,每个对象都有一个 __proto__ 属性指向它的原型对象,直到 Object.prototype,这个原型对象是最终的原型对象,也就是说它没有自己的原型。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log("Hello, my name is " + this.name);
};
const alice = new Person("Alice", 30);
const bob = new Person("Bob", 25);
alice.sayHello(); // 输出: Hello, my name is Alice
bob.sayHello(); // 输出: Hello, my name is Bob
16、js中对数组和对象的操作方法有哪些?
一、数组的操作方法
1、添加/删除元素
push():向数组的末尾添加一个或多个元素,并返回新的长度。
pop():删除并返回数组的最后一个元素。
shift():删除并返回数组的第一个元素。
unshift():向数组的开头添加一个或多个元素,并返回新的长度。
splice():通过删除或替换现有元素或者添加新元素来修改数组,并以数组的形式返回被修改的内容。此方法会改变原数组。
2、截取/复制数组
slice():返回一个新的数组对象,这个对象是一个由原数组的指定开始到结束(不包括结束)的浅拷贝。原始数组不会被改变。
concat():用于连接两个或更多的数组。该方法不会改变现有的数组,而是返回一个新数组。
3、排序/反转数组
sort():对数组的元素进行排序,并返回数组。默认情况下,sort()方法将元素转换为字符串,然后比较它们的UTF-16代码单元值序列,以进行排序。如果需要对数字进行排序,需要提供一个比较函数。
reverse():反转数组中元素的顺序。
4、遍历/映射数组
forEach():对数组的每个元素执行一次给定的函数。
map():创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
for循环:通过索引来遍历数组元素。
for...of循环:直接遍历数组(或任何可迭代对象)的值,无需关心索引。
5、搜索/查找数组
find():返回数组中满足提供的测试函数的第一个元素的值。否则返回undefined。
findIndex():返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。
includes():判断一个数组是否包含一个指定的值,根据情况可从左往右或从右往左遍历。
indexOf():在数组中从左到右搜索一个值,并返回其索引(从0开始)。
lastIndexOf():在数组中从右到左搜索一个值,并返回其索引(从0开始)。
6、归约/累加数组
reduce():对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。
reduceRight():与reduce()类似,但是从右到左。
7、其他方法
fill():使用给定的值填充数组的从起始索引到结束索引的所有元素。不会改变原数组的大小。
copyWithin():在当前数组中,将指定位置的元素复制到其他位置,并返回该数组。不会改变数组的长度。
flat()和flatMap():flat()方法会按照一个可指定的深度递归遍历数组,并将所有元素合并为一个新数组。 flatMap()方法首先使用映射函数映射每个元素,然后将结果展平成一个新数组。
toString():把数组转换为字符串,并返回结果。数组中的元素之间用逗号分隔。
values():返回一个新的数组迭代器对象,该对象包含数组中的每个索引的键/值对。
entries():返回一个新的数组迭代器对象,该对象包含数组中的每个索引的键/值对(索引作为键)。
keys():返回一个新的数组迭代器对象,该对象包含数组中的每个索引的键。
二、对象的操作方法
1、创建对象
Object.create():创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
字面量语法:使用花括号{}直接创建一个对象。
2、添加/删除属性
点(.)或方括号([])语法:向对象添加或访问属性。
delete操作符:删除对象的属性。
3、属性描述符
Object.defineProperty():在对象上定义一个新属性,或修改一个对象的现有属性,并返回该对象。
Object.defineProperties():在对象上定义多个新属性或修改现有属性,并返回该对象。
4、获取对象信息
Object.keys():返回一个给定对象自身可枚举属性组成的数组,数组中属性名的排列顺序和使用for...in循环遍历该对象时返回的顺序一致(两者的主要区别是for-in循环还会枚举其原型链上的属性)。
Object.values():返回一个给定对象自身的所有可枚举属性值的数组,其排列与Object.keys()返回的数组中的属性名排列相同。
Object.entries():返回一个给定对象自身可枚举属性的键值对数组,其排列与通过手动遍历该对象属性返回的顺序一致。
Object.getOwnPropertyNames():返回一个数组,该数组对对象的所有自身属性(不包括不可枚举属性,但包括符号属性)的键进行排序。
Object.getOwnPropertySymbols():返回一个给定对象自身的所有符号属性的数组。
5、合并对象
Object.assign():将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
6、冻结/密封对象
Object.freeze():可以冻结一个对象。冻结对象后,不能向对象添加新的属性,不能删除现有属性,不能修改现有属性的值,不能修改现有属性的可枚举性、可配置性,也不能修改现有属性的[[Writable]]特性。
Object.seal():可以封闭一个对象,阻止添加新的属性并将所有现有属性标记为不可配置。当前属性的值只要可写,依然可以被修改。
7、其他方法
Object.is():确定两个值是否相同,与严格相等运算符(===)的行为存在一些细微差别。
Object.hasOwnProperty():返回一个布尔值,表示对象自身属性中是否存在指定的属性(不包括继承的属性)。
Object.prototype.toString.call():可以用来获取对象的内部[[Class]]属性,返回表示该对象的字符串。
17、get请求和post请求的区别?
1、数据传输方式
GET请求:将数据附加在URL的查询字符串中;
POST请求:将数据放在HTTP请求体中。这意味着,请求的参数不会直接暴露在URL上,而是包含在请求的正文部分。
2、缓存处理
GET请求:通常会被浏览器缓存;
POST请求:通常不会被浏览器缓存;
3、安全性:
GET请求:参数直接暴露在URL上,容易泄露敏感信息。因此,GET请求不适合传输敏感数据。
POST请求:参数不会暴露在URL上,相对更安全。
4、幂等性:
GET请求:是幂等的。这意味着,多次执行同一个GET请求不会产生副作用。例如,多次请求同一个资源,返回的结果 应该是相同的。
POST请求:不是幂等的。多次执行同一个POST请求可能会改变服务器的状态。例如,多次提交表单数据,可能会导 致数据库中插入多条记录。
5、数据长度限制
GET请求:参数长度受URL长度限制。
POST请求:没有长度限制。
6、书签保存与历史记录
GET请求:URL可以保存为书签,并且参数会保留在浏览器历史记录中。这使得用户可以通过书签或历史记录方便地重新访问之前请求过的资源。
POST请求:URL不能直接保存为书签,并且参数不会保留在浏览器历史记录中
7、数据类型限制
GET请求:通常只接受ASCII字符
POST请求:没有数据类型限制
import axios from 'axios';
// 封装一个GET请求函数
export const getRequest = (url, params) => {
return axios.get(url, {
params: params, // 将参数作为查询字符串附加到URL上
})
.then(response => {
// 如果请求成功,返回响应数据
return response.data;
})
.catch(error => {
// 如果请求失败,抛出错误
console.error('GET request error:', error);
throw error;
});
};
// 封装一个POST请求函数
export const postRequest = (url, data) => {
return axios.post(url, data)
.then(response => {
// 如果请求成功,返回响应数据
return response.data;
})
.catch(error => {
// 如果请求失败,抛出错误
console.error('POST request error:', error);
throw error;
});
};
//调用
getRequest('https://api.example.com/data', { id: 123 })
postRequest('https://api.example.com/data', { name: 'John Doe', age: 30 })
18、js中,请求拦截器和响应拦截器都能分别做什么,给出代码案例
一、请求拦截器
请求拦截器可以在请求被发送到服务器之前对其进行修改或添加一些额外的处理逻辑。例如,你可以在每个请求中添加认证令牌、修改请求头或处理错误。
const axios = require('axios');
// 添加请求拦截器
axios.interceptors.request.use(config => {
// 在发送请求之前做些什么
// 例如,添加认证令牌到请求头
const token = 'your-auth-token';
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
}, error => {
// 对请求错误做些什么
return Promise.reject(error);
});
// 发送请求
axios.get('/some/endpoint')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('Error:', error);
});
二、响应拦截器
响应拦截器可以在服务器响应到达客户端之前对其进行处理。例如,你可以统一处理错误响应、转换响应数据格式或根据响应状态码执行不同的逻辑。
const axios = require('axios');
// 添加响应拦截器
axios.interceptors.response.use(response => {
// 对响应数据做点什么
// 例如,检查响应状态码并处理错误
if (response.status === 200) {
// 请求成功,返回响应数据
return response.data;
} else {
// 请求失败,抛出错误
return Promise.reject(new Error(`Error ${response.status}: ${response.statusText}`));
}
}, error => {
// 对响应错误做点什么
// 例如,统一处理401未授权错误并重定向到登录页面
if (error.response && error.response.status === 401) {
// 执行重定向或其他逻辑
window.location.href = '/login';
}
return Promise.reject(error);
});
// 发送请求
axios.get('/some/endpoint')
.then(data => {
console.log('Data:', data);
})
.catch(error => {
console.error('Error:', error.message);
});
19、vue3中缓存组件怎么处理,里边用到了那个钩子函数?
在Vue 3中,缓存组件通常使用<keep-alive>组件来实现。<keep-alive>是Vue内置的一个组件,它能够缓存不活动的组件实例,而不是销毁它们,从而保留组件的状态并避免重复渲染,进而提升性能和用户体验。
<keep-alive>还支持include和exclude属性,允许你精确控制哪些组件需要被缓存,哪些需要被排除。你还可以使用max属性来限制缓存组件的最大数量。
在Vue 3中,当缓存的组件被激活或停用时,会触发特定的生命周期钩子函数
(1)onActivated:当组件被插入DOM时触发。这通常发生在用户导航回到该组件所在的页面或视图时。在这个钩子函数中,你可以执行一些需要在组件激活时进行的操作,比如更新数据或重新获取焦点等。
(2)onDeactivated:当组件从DOM中移除时触发。这通常发生在用户导航离开该组件所在的页面或视图时。在这个钩子函数中,你可以执行一些清理工作,比如取消订阅事件或停止某些后台操作等。
20、vue2和vue3的区别?
一、响应式数据绑定原理
Vue 2:使用ES5的Object.defineProperty方法,通过发布/订阅模式实现双向数据绑定。这种方式存在一些局限性,例如它只能监听某个属性,不能对全对象进行监听,且需要额外的操作来监听数组的变化。
Vue 3:引入ES6的Proxy对象,对数据进行代理,从而实现了更强大和灵活的响应式系统。Proxy可以监听对象和数组的变化,无需进行额外的操作,并且可以直接绑定整个对象,提高了效率和易用性。
二、API设计
Vue 2:使用选项式API(Options API),组件的逻辑被分散在data、methods、computed等选项中。这种方式在组件较复杂时,可能会导致代码组织不够清晰。
Vue 3:引入了组合式API(Composition API),通过setup函数来组织组件的逻辑。这种方式使得代码更加模块化和可复用,逻辑更加清晰,易于理解和维护。
三、生命周期钩子函数
Vue 2:提供了如beforeCreate、created、beforeMount、mounted等生命周期钩子函数。
Vue 3:同样提供了生命周期钩子函数,但命名和触发时机有所调整。例如,setup函数在beforeCreate和created之前执行,而onBeforeMount和onMounted分别对应Vue 2中的beforeMount和mounted。此外,Vue 3还增加了onRenderTracked和onRenderTriggered两个调试钩子。
四、TypeScript支持
Vue 2:虽然可以使用TypeScript,但支持不够完善,类型推断和类型检查较弱。
Vue 3:从设计之初就考虑了TypeScript的支持,提供了更好的类型推断,允许更安全的开发体验。这使得在Vue 3项目中使用TypeScript变得更加自然和高效。
五、组件结构
Vue 2:在<template>中必须只有一个根标签。
Vue 3:支持碎片化(Fragments),允许组件有多个根标签。Vue 3会默认把这些标签包裹在一个虚拟标签中,以减少内存占用。
六、创建Vue实例
Vue 2:通过new Vue()构造函数来创建Vue实例,通常是在main.js文件中直接创建应用实例,并将路由和状态管理作为选项传入。
Vue 3:使用createApp函数来创建应用实例,这使得创建过程更加清晰。路由和状态管理通过use方法进行插件注册。
七、性能优化
Vue 2:性能较好,但在大型应用中,当数据变化频繁时可能出现性能瓶颈。
Vue 3:引入了虚拟DOM的优化,减少了不必要的渲染;使用编译时优化,生成更小的代码,提高了运行时性能。此外,Vue 3的响应式系统也更加高效,进一步提升了性能。
21、css中像素有那些?
一、、CSS像素(px)
定义:CSS像素是Web编程中的概念,指的是CSS样式代码中使用的逻辑像素。它是浏览器内一切长度的基本单位。
特性:
*CSS像素是一个相对单位,它相对于设备像素(device pixel)而言。在同样一个设备上,每1个CSS像素所 代表的物理像素是可以变化的。
*在不同的设备之间,每1个CSS像素所代表的物理像素也是可以变化的。
*CSS像素的值是固定的,不会随屏幕尺寸或分辨率变化。
二、、物理像素(pt)
定义:物理像素是显示屏上能控制显示的最小物理单位,即显示器上一个个的点。从屏幕生产出来的那天起,它上面的物理像素点就固定不变了。
单位换算:1pt = 1/72英寸,而1英寸等于2.54厘米。因此,pt是一个真正的绝对单位。
三、设备像素比(DPR)与设备独立像素(DIP
设备像素比(DPR):设备像素比描述的是未缩放状态下,物理像素和CSS像素的初始比例关系。它可以通过设备的物理像素除以CSS像素来计算。例如,在Retina屏的iPhone上,DPR的值为2,意味着1个CSS像素相当于2个物理像素。
(物理像素/CSS像素)=2
【【假设有一个元素的CSS宽度为100px。在DPR = 2的设备上,这个元素实际上会占据200x设备物理像素的宽度(因为每个CSS像素由2x2个物理像素组成,所以100px * 2 = 200个物理像素宽度)。】】
设备独立像素(DIP):设备独立像素也称为逻辑像素,它可以是系统中的一个点,这个点代表一个可以由程序使用的虚拟像素。在移动端浏览器中以及某些桌面浏览器中,window对象有一个devicePixelRatio属性,它的定义为设备物理像素和设备独立像素的比例。CSS像素就可以看做是设备的独立像素。
四、视窗相关像素单位(vh、vw等)
vh:视窗高度(viewpoint height),1vh等于视窗高度的1%。
vw:视窗宽度(viewpoint width),类似地,1vw等于视窗宽度的1%。
五、其他相对单位(em、rem)
em:相对单位,基准点为父节点字体的大小。如果自身定义了font-size,则按自身来计算。整个页面内1em的值不是固定的。
rem:相对单位,可理解为“root em”,即相对根节点html的字体大小来计算。这是CSS3新加的属性,被Chrome、Firefox、IE9+等浏览器支持。
六、百分比像素单位(%)
定义:百分比是一个相对单位,它表示某个值相对于另一个值(通常是父元素的某个属性)的百分比。在CSS中,百分比单位常用于定义元素的宽度、高度、边距、内边距等属性。
为了在不同的DPR屏幕下让图片看起来都不失真,开发者需要为不同DPR的屏幕提供不同大小的图片资源。例如:
对于DPR = 1的设备,提供标准大小的图片。
对于DPR = 2的设备,提供两倍大小的图片(即图片宽度和高度都是原来的两倍)。
对于DPR = 3的设备,提供三倍大小的图片。
<img src="image-small.png"
srcset="image-small.png 1x, image-medium.png 2x, image-large.png 3x">
22、promise有哪些状态和方法?
一、Promise的三种状态
*Pending(进行中):
这是Promise的初始状态,表示异步操作尚未完成,处于等待状态。
*Fulfilled(已完成):
表示异步操作已成功完成,并返回了结果。此时,Promise的状态从Pending变为Fulfilled。
*Rejected(已拒绝):
表示异步操作失败,并返回了错误原因。此时,Promise的状态从Pending变为Rejected。
二、Promise的实例方法
then(onFulfilled, onRejected):
catch(onRejected)
finally(onFinally)
三、Promise的静态方法
Promise.resolve(value)
返回一个状态为fulfilled的Promise对象,并将传入的值作为该Promise对象的结果。
Promise.reject(reason)
返回一个状态为rejected的Promise对象,并将传入的错误作为该Promise对象的结果
Promise.all(iterable)
接收一个包含多个Promise对象的可迭代对象(如Promise数组),并返回一个新的Promise
Promise.allSettled(iterable)
只有当所有传入的Promise对象都变为settled(即fulfilled或rejected)时,返回的Promise才变为fulfilled。
Promise.race(iterable)
返回的Promise的状态和结果由第一个变为settled(fulfilled或rejected)的Promise决定
Promise.any(iterable)
只要存在一个Promise变为fulfilled,返回的Promise就变为fulfilled,其结果为第一个fulfilled的Promise的结果。
四、
23、
24、
25、
26、
27、
28、
29、
30、
31、
32、
33、
34、
35、
36、
37、
38、
39、
40、
41、
42、
43、
44、
45、
46、
47、
48、
49、
50、
51、
52、
53、
54、
55、
56、
57、
58、
59、
60、
61、
62、
63、
64、
65、
66、
67、
68、
69、
70、
71、
72、
73、
74、
75、
76、
77、
78、
79、
80、
81、
82、
83、
84、
85、
86、
87、
88、
89、
90、
91、
92、
93、
94、
95、
96、
97、
98、
99、