前端面试题 Vue 向(综合)
前言
本篇内容基本都是根据我实际面试遇到的问题进行整理的,针对的是初级岗位,所以大部分内容都比较基础。
JavaScript
对象去重
要对API返回的对象进行去重,可以使用JavaScript中的Set数据结构。Set是一种集合,它只存储唯一的值,可以帮助我们快速去除重复项。
假设API返回的对象数组为data,我们可以按照以下步骤进行去重:
-
创建一个空的Set对象,命名为uniqueSet:
const uniqueSet = new Set();
-
遍历data数组,对每个对象执行以下操作:
-
将对象转换为字符串,使用JSON.stringify()方法:
const objString = JSON.stringify(obj);
-
将转换后的字符串添加到uniqueSet中:
uniqueSet.add(objString);
-
-
将uniqueSet转换回数组形式,使用Array.from()方法:
const uniqueArray = Array.from(uniqueSet);
-
将uniqueArray中的字符串转换回对象形式,使用JSON.parse()方法:
const uniqueObjects = uniqueArray.map(objString => JSON.parse(objString));
现在,uniqueObjects数组中就是去重后的对象数组了。
以下是完整的代码示例:
const data = [/* API返回的对象数组 */];
const uniqueSet = new Set();
data.forEach(obj => {
const objString = JSON.stringify(obj);
uniqueSet.add(objString);
});
const uniqueArray = Array.from(uniqueSet);
const uniqueObjects = uniqueArray.map(objString => JSON.parse(objString));
console.log(uniqueObjects);
数组去重
数组去重的方法有多种,以下是几种常见的方法:
-
使用Set:利用Set数据结构的特性,可以快速去除数组中的重复元素。
const arr = [1, 2, 3, 3, 4, 5, 5];
const uniqueArr = [new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4, 5]
-
使用filter:通过filter方法遍历数组,返回只包含唯一元素的新数组。
const arr = [1, 2, 3, 3, 4, 5, 5];
const uniqueArr = arr.filter((item, index) => arr.indexOf(item) === index);
console.log(uniqueArr); // [1, 2, 3, 4, 5]
-
使用reduce:通过reduce方法遍历数组,将不重复的元素添加到新数组中。
const arr = [1, 2, 3, 3, 4, 5, 5];
const uniqueArr = arr.reduce((prev, curr) => {
if (!prev.includes(curr)) {
prev.push(curr);
}
return prev;
}, []);
console.log(uniqueArr); // [1, 2, 3, 4, 5]
-
使用indexOf:遍历数组,利用indexOf方法判断元素是否已经存在于新数组中。
const arr = [1, 2, 3, 3, 4, 5, 5];
const uniqueArr = [];
for (let i = 0; i < arr.length; i++) {
if (uniqueArr.indexOf(arr[i]) === -1) {
uniqueArr.push(arr[i]);
}
}
console.log(uniqueArr); // [1, 2, 3, 4, 5]
字符去重
字符去重的方法与数组去重类似,以下是几种常见的方法:
-
使用Set:利用Set数据结构的特性,可以快速去除字符串中的重复字符。
const str = "hello";
const uniqueStr = [new Set(str)].join("");
console.log(uniqueStr); // "helo"
-
使用filter:通过filter方法遍历字符串,返回只包含唯一字符的新字符串。
const str = "hello";
const uniqueStr = str
.split("")
.filter((item, index, arr) => arr.indexOf(item) === index)
.join("");
console.log(uniqueStr); // "helo"
-
使用reduce:通过reduce方法遍历字符串,将不重复的字符添加到新字符串中。
const str = "hello";
const uniqueStr = str.split("").reduce((prev, curr) => {
if (!prev.includes(curr)) {
prev += curr;
}
return prev;
}, "");
console.log(uniqueStr); // "helo"
-
使用indexOf:遍历字符串,利用indexOf方法判断字符是否已经存在于新字符串中。
const str = "hello";
let uniqueStr = "";
for (let i = 0; i < str.length; i++) {
if (uniqueStr.indexOf(str[i]) === -1) {
uniqueStr += str[i];
}
}
console.log(uniqueStr); // "helo"
字符出现的次数
在JavaScript中,可以使用多种方法来统计字符在字符串中出现的次数。以下是几种常见的方法:
-
使用循环遍历字符串:
-
使用
for
循环或while
循环遍历字符串的每个字符。 -
使用一个计数器变量,每当遍历到目标字符时,计数器加1。
-
最后得到的计数器的值就是目标字符在字符串中出现的次数。
-
function countOccurrences(str, targetChar) {
let count = 0;
for (let i = 0; i < str.length; i++) {
if (str[i] === targetChar) {
count++;
}
}
return count;
}
let str = "Hello, World!";
let targetChar = "o";
console.log(countOccurrences(str, targetChar)); // 输出:2
-
使用
split()
和filter()
方法:-
使用
split()
方法将字符串拆分为字符数组。 -
使用
filter()
方法筛选出与目标字符相等的字符。 -
最后得到的数组的长度就是目标字符在字符串中出现的次数。
-
function countOccurrences(str, targetChar) {
let charArray = str.split("");
let occurrences = charArray.filter((char) => char === targetChar);
return occurrences.length;
}
let str = "Hello, World!";
let targetChar = "o";
console.log(countOccurrences(str, targetChar)); // 输出:2
-
使用正则表达式:
-
使用正则表达式来匹配目标字符在字符串中出现的次数。
-
使用
match()
方法返回匹配结果的数组。 -
最后得到的数组的长度就是目标字符在字符串中出现的次数。
-
function countOccurrences(str, targetChar) {
let regex = new RegExp(targetChar, "g");
let matches = str.match(regex);
return matches ? matches.length : 0;
}
let str = "Hello, World!";
let targetChar = "o";
console.log(countOccurrences(str, targetChar)); // 输出:2
数组排序
在JavaScript中,可以使用多种方法来对数组进行排序。以下是几种常见的排序方法:
-
使用
sort()
方法:-
sort()
方法是数组的原生方法,可以对数组进行原地排序(即修改原数组)。 -
默认情况下,
sort()
方法将数组元素转换为字符串,并按照Unicode编码进行排序。 -
如果要按照其他方式进行排序,可以传入一个比较函数作为参数。
-
let arr = [5, 2, 8, 1, 4];
arr.sort(); // 默认按照Unicode编码排序
console.log(arr); // 输出:[1, 2, 4, 5, 8]
// 按照数字大小进行排序
arr.sort((a, b) => a - b);
console.log(arr); // 输出:[1, 2, 4, 5, 8]
-
使用自定义比较函数:
-
可以编写自定义的比较函数来实现特定的排序逻辑。
-
比较函数接受两个参数,通常被称为
a
和b
,表示要比较的两个元素。 -
如果
a
应该排在b
之前,返回一个负数;如果a
应该排在b
之后,返回一个正数;如果a
和b
相等,返回0。
-
let arr = [5, 2, 8, 1, 4];
arr.sort((a, b) => b - a); // 按照数字大小逆序排序
console.log(arr); // 输出:[8, 5, 4, 2, 1]
-
使用
localeCompare()
方法:-
对于字符串数组,可以使用
localeCompare()
方法进行排序。 -
localeCompare()
方法比较两个字符串的排序顺序,并返回一个负数、零或正数,表示它们的相对顺序。
-
let arr = ["apple", "banana", "cherry"];
arr.sort((a, b) => a.localeCompare(b)); // 按照字母顺序排序
console.log(arr); // 输出:["apple", "banana", "cherry"]
需要注意的是,sort()
方法会直接修改原数组,而不是返回一个新的排序后的数组。如果需要保留原数组,可以先使用slice()
方法创建一个副本,然后对副本进行排序。
数据类型转换
在JavaScript中,可以使用一些内置的方法来进行数据类型的转换。以下是一些常见的数据类型转换方法:
-
字符串转换为数字:
-
使用
parseInt()
函数将字符串转换为整数。 -
使用
parseFloat()
函数将字符串转换为浮点数。 -
使用
Number()
函数将字符串转换为数字。
-
-
数字转换为字符串:
-
使用
toString()
方法将数字转换为字符串。 -
使用
String()
函数将数字转换为字符串。
-
-
字符串转换为布尔值:
-
使用
Boolean()
函数将非空字符串转换为true
,空字符串转换为false
。
-
-
布尔值转换为字符串:
-
使用
toString()
方法将布尔值转换为字符串。 -
使用
String()
函数将布尔值转换为字符串。
-
-
数字转换为布尔值:
-
使用
Boolean()
函数将非零数字转换为true
,零转换为false
。
-
-
布尔值转换为数字:
-
使用
Number()
函数将true
转换为1,false
转换为0。
-
-
其他类型转换为数组:
-
使用
Array.from()
方法将类数组对象或可迭代对象转换为数组。 -
使用
split()
方法将字符串按指定分隔符转换为数组。
-
-
其他类型转换为对象:
-
使用
Object()
函数将基本类型转换为包装对象。
-
需要注意的是,在进行数据类型转换时,要注意数据的有效性和边界情况,以避免出现意外的结果或错误。
== 和 === 的区别
在JavaScript中,==
和===
都是用于比较两个值的运算符,它们之间有以下区别:
-
==
(相等运算符):-
==
会进行类型转换后再比较两个值是否相等。 -
如果比较的两个值类型不同,
==
会尝试将它们转换为相同的类型,然后再进行比较。 -
在进行类型转换时,会遵循一些隐式转换的规则,例如将字符串转换为数字,将布尔值转换为数字等。
-
==
会进行类型转换,可能会导致一些意外的结果,因此在使用时需要小心。
-
-
===
(严格相等运算符):-
===
不会进行类型转换,它会直接比较两个值的类型和值是否完全相等。 -
只有当两个值的类型和值都相等时,
===
才会返回true
,否则返回false
。 -
===
更加严格,可以避免一些类型转换带来的意外结果。 -
在进行比较时,建议优先使用
===
,以避免类型转换带来的问题。
-
Vue
Vue2和Vue3的区别
Vue2和Vue3是Vue.js框架的两个版本。Vue3是在Vue2的基础上进行了重写和改进,主要有以下几个区别:
-
性能优化:Vue3在编译和运行时进行了优化,提高了性能和运行效率。
-
Composition API:Vue3引入了Composition API,使得组件逻辑更加灵活和可复用。
-
更好的TypeScript支持:Vue3对TypeScript的支持更加友好,提供了更好的类型推导和类型检查。
-
更小的体积:Vue3的体积比Vue2更小,加载速度更快。
-
更好的响应式系统:Vue3使用Proxy代替了Vue2中的Object.defineProperty,使得响应式系统更加强大和灵活。
Vue2中的响应式系统的工作流程
在Vue2中,响应式系统是通过Object.defineProperty来实现的。当一个对象被传入Vue实例的data选项中时,Vue会遍历这个对象的所有属性,并使用Object.defineProperty将它们转换为getter和setter。这样一来,当属性被读取或修改时,Vue能够捕捉到这些操作,并触发相应的更新。
Vue3中响应式系统的改进和优化
在Vue3中,响应式系统进行了重写,并引入了Proxy来替代Object.defineProperty。相比于Vue2,Vue3的响应式系统有以下改进和优化:
-
更好的性能:Vue3使用Proxy代理对象,可以直接监听整个对象的变化,而不需要遍历对象的属性。这样可以提高性能,尤其是在大型应用中。
-
更灵活的响应式追踪:Vue3的响应式系统可以追踪到动态添加的属性和删除的属性,而Vue2只能追踪已经存在的属性。
-
更好的类型推导和类型检查:Vue3的响应式系统对TypeScript的支持更友好,可以更准确地推导和检查属性的类型。
-
更小的体积:由于使用了Proxy,Vue3的体积比Vue2更小,加载速度更快。
Vue3中的Composition API
Composition API是Vue3引入的一种新的组合式API风格。它主要有以下优势和特点:
-
更灵活的组件逻辑复用:Composition API使得组件的逻辑可以更加灵活地组织和复用,可以将相关的逻辑放在一起,提高代码的可读性和维护性。
-
更好的代码组织:Composition API可以将一个组件的逻辑拆分成多个函数,每个函数负责不同的功能,使得代码更加清晰和易于维护。
-
更好的类型推导和类型检查:Composition API对TypeScript的支持更好,可以更准确地推导和检查函数的参数和返回值的类型。
-
更好的代码重用:Composition API可以将逻辑提取为自定义的Hook函数,可以在多个组件中进行复用。
-
更好的IDE支持:由于Composition API使用了函数式的写法,IDE可以提供更好的代码提示和自动补全。
Composition API适用于需要更灵活组织和复用组件逻辑的场景,特别是对于大型应用或复杂组件来说,可以提高开发效率和代码质量。
请你介绍一下Vue的生命周期是什么,以及它的几个阶段。
Vue的生命周期是指Vue实例从创建到销毁的整个过程,可以分为以下几个阶段:
-
创建阶段:在这个阶段,Vue实例会进行初始化,包括数据观测、编译模板、挂载实例等。
-
更新阶段:当数据发生变化时,Vue会进行重新渲染,并更新DOM。
-
销毁阶段:当Vue实例不再需要时,会进行销毁操作,清理相关的事件监听和定时器等。
在每个阶段,Vue提供了一些钩子函数,可以在特定的时机执行一些操作,例如created、mounted、updated和destroyed等。
钩子函数以及生命周期顺序
在Vue中,常用的钩子函数按照生命周期顺序可以分为以下几个阶段:
-
创建阶段:
-
beforeCreate:在实例初始化之后,数据观测和事件配置之前调用。可以在这个钩子函数中进行一些初始化的操作,例如配置全局变量、引入插件等。
-
created:在实例创建完成后调用,此时实例已经完成了数据观测和事件配置,但尚未挂载到DOM上。可以在这个钩子函数中进行一些异步操作,例如发送网络请求、获取数据等。
-
-
挂载阶段:
-
beforeMount:在挂载开始之前被调用,此时模板编译已完成,但尚未将模板渲染到DOM中。可以在这个钩子函数中进行一些DOM操作,例如修改DOM元素的属性、样式等。
-
mounted:在挂载完成后被调用,此时实例已经被挂载到DOM上。可以在这个钩子函数中进行一些需要DOM元素的操作,例如初始化第三方库、绑定事件等。
-
-
更新阶段:
-
beforeUpdate:在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前。可以在这个钩子函数中进行一些数据处理的操作,例如格式化数据、计算属性等。
-
updated:在数据更新完成后被调用,此时虚拟DOM已经重新渲染和打补丁完成。可以在这个钩子函数中进行一些DOM操作,例如更新DOM元素的内容、样式等。
-
-
销毁阶段:
-
beforeDestroy:在实例销毁之前调用,此时实例仍然完全可用。可以在这个钩子函数中进行一些清理的操作,例如取消订阅、解绑事件等。
-
destroyed:在实例销毁之后调用,此时实例已经被销毁,所有的事件监听器和子实例都被移除。可以在这个钩子函数中进行一些最终的清理操作,例如释放资源、销毁实例等。
-
页面第一次加载会触发 4 个钩子,分别是:beforeCreate、created、beforeMount、mounted
v-if和v-show的区别
v-if和v-show都是Vue中的条件渲染指令,用于根据条件来显示或隐藏元素。它们的区别主要有以下几点:
-
编译时机:v-if是在每次渲染时进行条件判断,如果条件为false,则不会渲染对应的元素及其子组件。而v-show是在渲染时始终会渲染对应的元素,只是通过CSS的display属性来控制元素的显示和隐藏。
-
切换开销:由于v-if是在每次渲染时进行条件判断,如果条件为false,则会销毁对应的元素及其子组件,再次条件为true时重新创建和渲染。而v-show只是通过CSS的display属性来切换元素的显示和隐藏,切换开销较小。
-
初始渲染开销:由于v-if是在渲染时进行条件判断,如果条件为false,则不会渲染对应的元素及其子组件,初始渲染开销较小。而v-show在初始渲染时会渲染对应的元素,初始渲染开销较大。
综上所述,如果需要频繁切换元素的显示和隐藏,可以使用v-show,如果条件不经常改变,或者初始渲染开销较大,可以使用v-if。
组件之间的通信方式
Vue中组件之间的通信方式有以下几种:
-
父子组件通信:父组件可以通过props向子组件传递数据,子组件通过$emit触发事件,将数据传递给父组件。
-
子父组件通信:子组件可以通过$emit触发事件,将数据传递给父组件,父组件通过监听子组件的事件来获取数据。
-
兄弟组件通信:可以通过一个共同的父组件作为中介,通过props和$emit来进行兄弟组件之间的通信。
-
跨级组件通信:可以使用provide和inject来进行跨级组件之间的通信,父级组件通过provide提供数据,子孙组件通过inject来注入数据。
-
使用Vuex进行状态管理:Vuex是Vue的官方状态管理库,可以在不同组件之间共享和管理状态。
v-bind和v-model的区别
-
v-bind指令:
-
作用:v-bind用于将Vue实例中的数据绑定到HTML元素的属性上,实现数据的动态绑定。
-
使用场景:常用于将数据绑定到HTML元素的属性上,例如class、style、src等。适用于单向数据绑定,即数据的变化会反映到视图上,但视图的变化不会影响数据。
-
-
v-model指令:
-
作用:v-model用于在表单元素和Vue实例的数据之间建立双向绑定关系,实现数据的双向同步。
-
使用场景:常用于表单元素,例如input、textarea、select等。适用于需要实现表单元素和数据之间的双向绑定,即数据的变化会反映到视图上,同时视图的变化也会影响数据。
-
双向绑定的原理
双向绑定是Vue中的一个重要特性,它可以实现数据的双向同步。双向绑定的原理主要是通过数据劫持和发布-订阅模式来实现的。
在Vue中,当数据发生变化时,Vue会通过数据劫持的方式,即通过Object.defineProperty来劫持数据的getter和setter方法。当数据被修改时,Vue会通知相关的订阅者,订阅者会自动更新对应的视图。而当视图中的输入框等表单元素发生变化时,Vue会通过事件监听的方式,将变化的数据同步到数据模型中。
通过数据劫持和发布-订阅模式的结合,Vue实现了数据和视图之间的双向绑定,使得数据的变化能够自动反映到视图中,同时视图中的变化也能够自动更新到数据模型中。
实现一个基本的双向数据绑定
实现一个基本的双向数据绑定可以通过以下步骤来实现:
-
创建一个数据模型对象,包含需要绑定的数据属性。
-
在视图中使用表单元素(如输入框)来展示和修改数据。
-
在数据模型对象中,使用Object.defineProperty来定义数据属性的getter和setter方法。
-
在getter方法中,返回数据属性的值。
-
在setter方法中,将新的值赋给数据属性,并触发一个自定义的事件。
-
在视图中,通过监听表单元素的变化事件,将变化的值同步到数据模型中。
-
在数据模型对象中,监听自定义的事件,当事件触发时,更新视图中的数据。
通过以上步骤,就可以实现一个基本的双向数据绑定,使得数据的变化能够自动反映到视图中,同时视图中的变化也能够自动更新到数据模型中。
Vue3中的Teleport和Suspense是什么
在Vue3中,Teleport和Suspense是两个新的特性。
Teleport是一个新的组件,它允许我们将组件的内容渲染到DOM中的任意位置,而不受组件的层级限制。通过使用Teleport,我们可以将组件的内容渲染到DOM中的指定位置,例如在body元素下的某个容器中,而不受组件层级的限制。这在处理一些特殊的布局需求或模态框等场景下非常有用。
Suspense是一个新的组件,它可以在异步组件加载时显示一个占位符,直到异步组件加载完成后再显示真正的内容。通过使用Suspense,我们可以更好地处理异步组件的加载状态,提供更好的用户体验。在Suspense中,我们可以定义一个fallback元素,当异步组件加载时,会显示fallback元素,直到异步组件加载完成后再显示真正的内容。
Teleport和Suspense这两个新特性在Vue3中提供了更好的灵活性和用户体验,使得开发者可以更好地处理一些特殊的布局需求和异步加载的场景。
Vue2中的Options API和Vue3中的Composition API的区别
Vue2中的Options API是一种对象式的API风格,通过在Vue组件中定义一些选项来描述组件的行为。每个选项对应一个生命周期钩子函数、数据、计算属性、方法等。这种风格的优点是简单易懂,适合小型应用和简单组件的开发。但是,当组件变得复杂时,选项之间的关联关系不明显,难以维护和复用。
Vue3中的Composition API是一种基于函数的API风格,通过使用函数来组织和复用组件的逻辑。它将相关的逻辑放在一起,而不是按照选项的方式分散在不同的地方。这种风格的优点是更灵活、可组合和可复用,适合大型应用和复杂组件的开发。通过使用Composition API,我们可以更好地组织和管理组件的逻辑,提高代码的可读性和维护性。
另外,Composition API还引入了一些新的函数,如reactive、ref、watch等,用于处理响应式数据、副作用和侦听器等。这些函数使得开发者可以更方便地处理组件的状态和副作用。
总结来说,Options API适合简单应用和组件,而Composition API适合大型应用和复杂组件,提供了更灵活、可组合和可复用的方式来组织和管理组件的逻辑。
写一个简单的Vue2 Options API示例和Vue3 Composition API示例
Vue2 Options API示例:
<template>
<div>
<p>{{ message }}</p>
<button @click="changeMessage">Change Message</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, this is Kuiwaiwai!'
};
},
methods: {
changeMessage() {
this.message = 'Hello, Kuiwaiwai!';
}
}
};
</script>
Vue3 Composition API示例:
<template>
<div>
<p>{{ message }}</p>
<button @click="changeMessage">Change Message</button>
</div>
</template>
<script>
import { reactive } from 'vue';
export default {
setup() {
const data = reactive({
message: 'Hello, this is Kuiwaiwai!'
});
const changeMessage = () => {
data.message = 'Hello, Kuiwaiwai!';
};
return {
message: data.message,
changeMessage
};
}
};
</script>
在Vue2的示例中,使用了Options API来定义组件的data选项和methods选项。通过this关键字可以访问到data中的message属性,并在methods中定义了changeMessage方法来改变message的值。
在Vue3的示例中,使用了Composition API的setup函数来定义组件的逻辑。通过reactive函数创建了一个响应式的data对象,并在setup函数中定义了changeMessage函数来改变message的值。最后,通过return语句将需要在模板中使用的数据和方法暴露出去。
数据
get 和 post 的区别
"get"和"post"是HTTP协议中常用的两种请求方法,它们有以下区别:
-
数据传输方式:
-
GET:通过URL参数将数据附加在URL后面,以查询字符串的形式发送给服务器。数据会暴露在URL中,可以被缓存、书签等获取。
-
POST:将数据放在请求的消息体中发送给服务器。数据不会暴露在URL中,对于敏感数据更安全。
-
-
数据长度限制:
-
GET:由于数据附加在URL中,URL的长度有限制,不同浏览器和服务器对URL长度的限制不同,一般在2KB到8KB之间。
-
POST:由于数据放在消息体中,没有明确的长度限制,但服务器和网络设备可能会对消息体的大小进行限制。
-
-
数据安全性:
-
GET:由于数据暴露在URL中,容易被拦截、缓存、记录日志等,不适合传输敏感数据。
-
POST:数据放在消息体中,相对于GET请求更安全,适合传输敏感数据。
-
-
缓存:
-
GET:GET请求可以被缓存,可以通过浏览器的后退、刷新等操作重新使用缓存的数据。
-
POST:POST请求不会被缓存,每次请求都会向服务器发送请求。
-
-
幂等性:
-
GET:GET请求是幂等的,即多次请求相同的URL和参数,服务器的响应是一致的,不会对服务器产生副作用。
-
POST:POST请求不是幂等的,多次请求相同的URL和参数,服务器的响应可能不同,可能对服务器产生副作用。
-
根据具体的需求和场景,选择合适的请求方法可以更好地满足数据传输的需求。一般来说,GET适合获取数据,POST适合提交数据。
本地缓存
-
Cookie 适用于浏览器与服务器的数据传递:
-
Cookie 是一种在浏览器和服务器之间传递的小型文本文件,用于存储少量数据。
-
Cookie 可以设置过期时间,可以在浏览器关闭后仍然保留。
-
Cookie 的大小有限制,一般为4KB左右。
-
Cookie 在每次请求中都会被发送到服务器,会增加网络流量。
-
-
localStorage 适用于长期存储数据:
-
localStorage 是 HTML5 提供的一种持久化存储数据的机制。
-
localStorage 存储的数据没有过期时间,除非手动清除或代码删除。
-
localStorage 的大小限制一般为5MB左右。
-
localStorage 只能通过 JavaScript 进行访问,不会被发送到服务器。
-
-
sessionStorage 适用于会话期间保持数据:
-
sessionStorage 也是 HTML5 提供的一种本地存储机制,但与 localStorage 不同,sessionStorage 的数据在会话结束后会被清除。
-
sessionStorage 的大小限制一般为5MB左右。
-
sessionStorage 只能通过 JavaScript 进行访问,不会被发送到服务器。
-
HTML
前端页面的构成
前端页面通常由以下三层构成:
-
结构层(HTML):结构层是前端页面的基础,使用HTML(超文本标记语言)来描述页面的结构和内容。HTML标签和元素用于定义页面的各个部分,如标题、段落、列表、表格等。结构层负责提供页面的基本骨架和内容组织。
-
表现层(CSS):表现层使用CSS(层叠样式表)来定义页面的样式和布局。CSS通过选择器和属性来选择页面中的元素,并为其指定样式,如颜色、字体、大小、边距、布局等。表现层负责控制页面的外观和样式,使页面具有吸引力和可读性。
-
行为层(JavaScript):行为层使用JavaScript来实现页面的交互和动态效果。JavaScript是一种脚本语言,可以通过操作DOM(文档对象模型)来实现对页面元素的增删改查,以及事件处理、表单验证、动画效果等。行为层负责为页面添加交互性和动态性,使用户能够与页面进行互动。
什么是HTML语义化
HTML语义化是指在编写HTML代码时,根据标签的本身含义和作用来选择合适的标签,以便于开发者和浏览器理解和解析网页内容。它强调使用具有语义的标签来描述网页的结构和内容,使得网页的结构更加清晰、易于理解和维护。
具体来说,HTML语义化包括以下几个方面:
-
使用合适的标签:根据内容的含义和作用,选择合适的HTML标签来包裹和描述内容。例如,使用
<h1>
到<h6>
标签来表示标题的级别,使用<p>
标签来表示段落,使用<ul>
和<li>
标签来表示无序列表等。 -
避免滥用无语义的标签:尽量避免使用无语义的
<div>
和<span>
标签来包裹内容,而是使用具有语义的标签来更准确地描述内容。例如,使用<nav>
标签来表示导航栏,使用<header>
和<footer>
标签来表示网页的头部和底部。 -
使用语义化的属性:除了选择合适的标签,还应该使用具有语义的属性来描述标签的具体含义。例如,使用
alt
属性来为<img>
标签提供替代文本,使用<a>
标签的href
属性来指定链接地址。 -
构建良好的文档结构:通过合理地嵌套和组织标签,构建良好的文档结构。这样可以使得网页的层次结构更加清晰,方便开发者和浏览器解析和理解网页内容。
通过遵循HTML语义化的原则,可以提高网页的可访问性、SEO优化、代码可读性和可维护性,降低样式和脚本的耦合性,提升用户体验。因此,在开发Web前端时,我们应该尽可能地使用语义化的HTML标签来构建网页结构。
src 和 href 的区别
-
src
属性:-
用于指定外部资源(如图片、脚本、音频、视频等)的地址。
-
src
属性是必需的,浏览器会根据该属性加载并显示资源。 -
当浏览器解析到
src
属性时,会暂停页面的渲染,直到资源加载完成。
-
-
href
属性:-
用于指定链接的目标地址,常用于链接外部样式表、网页、文档等。
-
href
属性是可选的,如果省略了href
属性,链接将不会起作用。 -
当浏览器解析到
href
属性时,不会暂停页面的渲染,而是会异步加载资源,不会阻塞页面的加载。
-
总结:
-
src
用于指定外部资源的地址,是必需的,会阻塞页面的加载。 -
href
用于指定链接的目标地址,是可选的,不会阻塞页面的加载。
行内元素与块级元素
在HTML中,元素可以分为两种类型:行内元素(inline element)和块级元素(block-level element)。
行内元素(inline element):
-
示例:
<span>
,<a>
,<strong>
,<em>
,<img>
,<input>
,<button>
等。 -
特点:
-
默认情况下,行内元素不会独占一行,它们会在同一行内水平排列。
-
行内元素的宽度和高度由其内容决定,无法设置固定的宽度和高度。
-
行内元素可以设置水平方向的内边距(padding)和外边距(margin),但不会影响到垂直方向的布局。
-
行内元素不能包含块级元素,只能包含其他行内元素或者文本。
-
块级元素(block-level element):
-
示例:
<div>
,<p>
,<h1>
-<h6>
,<ul>
,<li>
,<table>
,<form>
等。 -
特点:
-
块级元素会独占一行,每个块级元素都会从新的一行开始。
-
块级元素的宽度默认为其父元素的100%,可以通过设置宽度和高度来改变其尺寸。
-
块级元素可以设置水平和垂直方向的内边距(padding)和外边距(margin),可以通过设置CSS属性来控制布局。
-
块级元素可以包含其他块级元素和行内元素。
-
行内元素和块级元素的转换:
-
行内元素可以通过设置
display: block;
来转换为块级元素。 -
块级元素可以通过设置
display: inline;
来转换为行内元素。 -
可以通过设置
display: inline-block;
来将元素同时具备行内元素和块级元素的特性。
需要注意的是,元素的默认类型是由HTML规范决定的,但可以通过CSS来改变元素的显示类型。这种转换可以通过display
属性来实现,从而改变元素的布局和行为。
基本的SEO优化
在HTML中,可以通过以下基本的SEO设置来优化网页:
-
页面标题(Title):使用
<title>
标签来定义页面的标题,确保标题准确、简明地描述页面内容,并包含关键词。搜索引擎会将标题作为重要的参考因素之一。
<head>
<title>网页标题</title>
</head>
-
页面描述(Meta Description):使用
<meta>
标签的name="description"
属性来提供页面的描述信息,描述应该准确、吸引人,并包含关键词。搜索引擎会将描述作为搜索结果中的一部分展示给用户。
<head>
<meta name="description" content="页面描述">
</head>
-
关键词(Meta Keywords):使用
<meta>
标签的name="keywords"
属性来提供页面的关键词,列举与页面内容相关的关键词。然而,搜索引擎对关键词的权重已经降低,不再是主要的优化因素。
<head>
<meta name="keywords" content="关键词1, 关键词2, 关键词3">
</head>
-
页面URL(URL Structure):使用有意义的URL结构,包含关键词,以便搜索引擎和用户更好地理解页面内容。短、简洁的URL通常更易于记忆和分享。
<!-- 示例:使用关键词作为URL -->
<a href="/关键词">链接文本</a>
-
标题标签(Heading Tags):使用适当的标题标签(
<h1>
到<h6>
)来标记页面
CSS
CSS选择器
-
元素选择器(Element Selector):通过HTML元素的标签名选择元素,例如
p
选择所有<p>
元素。 -
类选择器(Class Selector):通过元素的
class
属性选择元素,以.
开头,例如.my-class
选择所有class
属性为my-class
的元素。 -
ID选择器(ID Selector):通过元素的
id
属性选择元素,以#
开头,例如#my-id
选择id
属性为my-id
的元素。请注意,一个页面中应该只有唯一的id
。 -
属性选择器(Attribute Selector):通过元素的属性选择元素,例如
[type="text"]
选择所有type
属性值为text
的元素。 -
后代选择器(Descendant Selector):通过元素的后代关系选择元素,例如
div p
选择所有<div>
元素内的<p>
元素。 -
子元素选择器(Child Selector):通过元素的直接子元素关系选择元素,例如
ul > li
选择所有<ul>
元素下的直接子元素<li>
。 -
相邻兄弟选择器(Adjacent Sibling Selector):通过元素的相邻兄弟关系选择元素,例如
h2 + p
选择紧接在<h2>
元素后的第一个<p>
元素。 -
伪类选择器(Pseudo-class Selector):通过元素的特殊状态选择元素,例如
:hover
选择鼠标悬停在元素上的状态。 -
伪元素选择器(Pseudo-element Selector):通过元素的特殊部分选择元素,例如
::before
选择元素的前面插入的内容。
CSS选择器的优先级
CSS选择器的优先级是用于确定当多个选择器同时应用于同一个元素时,哪个选择器的样式规则将被应用。CSS选择器的优先级由以下几个因素决定,按照优先级从高到低的顺序排列:
-
内联样式(Inline Styles):使用
style
属性直接在HTML元素上定义的样式具有最高的优先级。例如:<div style="color: red;">Hello</div>
-
ID选择器(ID Selectors):通过
id
属性选择元素的样式具有较高的优先级。例如:#my-id { color: blue; }
-
类选择器、属性选择器和伪类选择器(Class Selectors, Attribute Selectors, and Pseudo-class Selectors):通过类选择器、属性选择器或伪类选择器选择元素的样式具有较低的优先级。例如:
.my-class { color: green; }
、[type="text"] { font-size: 16px; }
、:hover { background-color: yellow; }
-
元素选择器和伪元素选择器(Element Selectors and Pseudo-element Selectors):通过元素选择器或伪元素选择器选择元素的样式具有最低的优先级。例如:
p { font-weight: bold; }
、::before { content: "Before"; }
总结:选择器的优先级又为: !important > 内联(style) > ID > 属性选择器 > 类名选择器 > 标签
如果多个选择器具有相同的优先级,那么后面定义的样式规则将覆盖先前定义的样式规则。如果多个选择器具有不同的优先级,具有较高优先级的样式规则将覆盖较低优先级的样式规则。
需要注意的是,内联样式具有最高的优先级,但是过度使用内联样式会导致代码冗余和维护困难,一般建议使用外部样式表或内部样式表来管理样式。合理使用选择器的优先级可以确保样式规则按照预期生效。
常用的伪类选择器
在CSS中,有许多伪类选择器可以用来选择元素的不同状态或位置。以下是一些常用的伪类选择器:
-
:hover
:选择鼠标悬停在元素上的状态。 -
:active
:选择元素被激活(鼠标按下)的状态。 -
:focus
:选择元素获得焦点的状态(通常用于表单元素)。 -
:visited
:选择已访问过的链接的状态。 -
:first-child
:选择父元素下的第一个子元素。 -
:last-child
:选择父元素下的最后一个子元素。 -
:nth-child(n)
:选择父元素下的第n个子元素。 -
:nth-last-child(n)
:选择父元素下的倒数第n个子元素。 -
:nth-of-type(n)
:选择父元素下的第n个指定类型的子元素。 -
:nth-last-of-type(n)
:选择父元素下的倒数第n个指定类型的子元素。 -
:not(selector)
:选择不匹配给定选择器的元素。 -
:empty
:选择没有子元素的元素。 -
:checked
:选择被选中的表单元素(如复选框或单选按钮)。 -
:disabled
:选择被禁用的表单元素。 -
:enabled
:选择可用的表单元素。
CSS中的尺寸单位
CSS中有多种尺寸单位,每种单位都有其特定的用途和差异。以下是一些常见的CSS尺寸单位及其差异:
-
像素(Pixel,px):像素是相对于显示设备的最小物理点的单位。它是最常用的尺寸单位,可以精确控制元素的大小和位置。像素单位在不同设备上具有固定的物理大小,但在高分辨率屏幕上可能会显得较小。
-
百分比(Percentage,%):百分比单位是相对于父元素的尺寸进行计算的。例如,如果一个元素的宽度设置为50%,那么它的宽度将是父元素宽度的一半。百分比单位可以用于实现响应式布局和相对尺寸的调整。
-
视窗单位(Viewport Units):视窗单位是相对于浏览器视口的尺寸进行计算的。常见的视窗单位有vw(视口宽度的百分比)、vh(视口高度的百分比)、vmin(视口宽度和高度中较小值的百分比)和vmax(视口宽度和高度中较大值的百分比)。视窗单位可以用于创建基于视口尺寸的自适应布局。
-
em(相对于父元素的字体尺寸):em单位是相对于元素的字体尺寸进行计算的。例如,如果一个元素的字体大小设置为2em,那么它的字体大小将是父元素字体大小的两倍。em单位可以用于创建相对于字体大小的尺寸。
-
rem(相对于根元素的字体尺寸):rem单位是相对于根元素(通常是
<html>
元素)的字体尺寸进行计算的。与em单位不同,rem单位不会受到父元素字体大小的影响,更容易控制和调整。 -
ch(字符宽度):ch单位是相对于当前字体中字符 "0" 的宽度进行计算的。它可以用于创建基于字符宽度的布局。
-
vw、vh、vmin、vmax、em、rem、ch等单位都是相对单位,可以根据不同的需求选择合适的单位来实现所需的效果。
需要根据具体的需求和设计来选择合适的尺寸单位,以确保元素在不同设备和屏幕尺寸下都能正确地呈现和适应布局。
CSS边距顺序
在CSS中,margin
属性用于设置元素的外边距,它可以接受多个值来定义不同方向上的边距。边距的顺序可以按照以下方式指定:
-
如果只提供一个值,则应用于所有四个边距(上、右、下、左)。
margin: 10px;
-
如果提供两个值,则第一个值应用于上下边距(上、下),第二个值应用于左右边距(左、右)。
margin: 10px 20px;
-
如果提供三个值,则第一个值应用于上边距,第二个值应用于左右边距,第三个值应用于下边距。
margin: 10px 20px 30px;
-
如果提供四个值,则按照顺序应用于上、右、下、左边距。
margin: 10px 20px 30px 40px;
需要注意的是,边距的顺序是顺时针方向的,即上、右、下、左。可以根据具体的需求选择合适的方式来指定边距的顺序。
以上顺序在外边距padding
中同理。
布局
当谈到常见的布局方式时,Flexbox(弹性盒子布局)和Grid(网格布局)是两种非常流行的选择。
Flexbox是一种一维布局模型,适用于构建灵活的、响应式的布局。它通过将容器内的元素放置在一个主轴上,并根据需要进行伸缩和对齐来实现布局。Flexbox非常适合于构建导航菜单、列表、卡片布局等。
以下是使用Flexbox布局的一些常见属性:
-
display: flex;
:将容器设置为Flex容器。 -
flex-direction: row/column;
:指定Flex容器的主轴方向。 -
justify-content: flex-start/center/flex-end/space-between/space-around;
:定义Flex容器内元素在主轴上的对齐方式。 -
align-items: flex-start/center/flex-end/stretch;
:定义Flex容器内元素在交叉轴上的对齐方式。 -
flex-grow: 1;
:定义Flex容器内元素的伸缩比例。
Grid是一种二维布局模型,适用于构建复杂的、多列多行的布局。它通过将容器划分为行和列的网格,然后将元素放置在网格中来实现布局。Grid非常适合于构建网格布局、响应式布局和复杂的页面结构。
以下是使用Grid布局的一些常见属性:
-
display: grid;
:将容器设置为Grid容器。 -
grid-template-columns: 100px 100px 100px;
:定义Grid容器的列宽。 -
grid-template-rows: 100px 100px 100px;
:定义Grid容器的行高。 -
grid-gap: 10px;
:定义Grid容器内网格之间的间隔。 -
grid-column-start/end: 1/3;
:定义元素跨越的列数。 -
grid-row-start/end: 1/3;
:定义元素跨越的行数。
Flexbox和Grid可以单独使用,也可以结合使用,根据不同的布局需求选择合适的方式。它们都提供了强大的布局能力,使得前端开发更加灵活和高效。
元素居中
在CSS中,有多种方法可以实现元素的居中。以下是一些常用的居中方法:
-
水平居中:
-
使用
text-align: center;
将文本内容水平居中,适用于块级元素和行内元素的文本内容。 -
使用
margin: 0 auto;
将块级元素的左右外边距设置为自动,适用于具有固定宽度的块级元素。
-
-
垂直居中:
-
使用
display: flex;
和align-items: center;
将容器元素的子元素垂直居中,适用于块级元素。 -
使用
position: absolute;
和top: 50%;
以及transform: translateY(-50%);
将元素相对于父元素垂直居中,适用于绝对定位的元素。
-
-
水平垂直居中:
-
使用
display: flex;
和justify-content: center; align-items: center;
将容器元素的子元素水平垂直居中,适用于块级元素。 -
使用
position: absolute;
和top: 50%; left: 50%;
以及transform: translate(-50%, -50%);
将元素相对于父元素水平垂直居中,适用于绝对定位的元素。
-
-
表格布局:
-
使用
display: table;
和margin: 0 auto;
将元素水平居中,适用于块级元素。
-
元素隐藏
-
opacity: 0
-
优点:元素仍然占据空间,只是不可见,可以保持布局的完整性。
-
缺点:元素仍然可以被点击和交互,不适用于需要完全隐藏元素的情况。
-
适用场景:适用于需要元素占据空间但不可见的情况,比如实现渐变效果或动画过渡。
-
-
visibility: hidden
-
优点:元素不可见,但仍然占据空间,可以保持布局的完整性。
-
缺点:元素仍然可以被点击和交互,不适用于需要完全隐藏元素的情况。
-
适用场景:适用于需要元素占据空间但不可见的情况,比如实现菜单的展开和收起。
-
-
display: none
-
优点:元素完全隐藏,不占据空间,不可点击和交互。
-
缺点:元素隐藏后会导致布局的改变。
-
适用场景:适用于需要完全隐藏元素的情况,比如实现弹出框或模态框。
-
-
position: absolute; left: -9999px;
-
优点:元素不可见,但仍然占据空间,可以保持布局的完整性。
-
缺点:元素仍然可以被点击和交互,不适用于需要完全隐藏元素的情况。
-
适用场景:适用于需要隐藏元素但仍然占据空间的情况,比如实现无障碍功能。
-
-
clip: rect(0 0 0 0);
-
优点:元素被裁剪为不可见的矩形,不占据空间。
-
缺点:不支持动画效果,不适用于需要保持布局完整性的情况。
-
适用场景:适用于需要隐藏元素且不占据空间的情况,比如实现特定形状的元素隐藏。
-
-
transform: scale(0)
-
优点:元素被缩放为0,不可见且不占据空间。
-
缺点:不支持动画效果,不适用于需要保持布局完整性的情况。
-
适用场景:适用于需要隐藏元素且不占据空间的情况,比如实现动画效果。
-
-
visibility: collapse;(仅适用于表格元素)
-
优点:表格的行或列被折叠,不可见且不占据空间。
-
缺点:仅适用于表格元素,不适用于其他类型的元素。
-
适用场景:适用于需要隐藏表格的行或列的情况。
-
具体问题
当一个标签中放置两个class时,样式会如何生效
当一个元素有多个class时,样式的生效顺序是由CSS规则中的顺序决定的。如果两个class中存在相同的样式规则,后面定义的class中的样式规则将覆盖先前定义的class中的样式规则。
需要注意的是,如果两个class中存在相同的样式规则,并且这些规则具有相同的优先级,那么后面定义的class中的样式规则将覆盖先前定义的class中的样式规则。这是由CSS的层叠性质决定的。
当同一个标签中出现两个class时,哪个class会生效
如果写成 <div class="class1" class="class2"></div>
这样的形式,会被解析为两个独立的class属性,而不是一个包含多个class的属性。在这种情况下,只有最后一个class属性会生效,即 class="class2"
表格奇数行有背景偶数行无背景
要实现让表格的奇数行有背景色,偶数行无背景色的效果,可以使用CSS中的伪类选择器来选择奇偶行,并为其设置不同的背景样式。
例如,假设你的表格的HTML结构如下:
<table>
<tr>
<td>Row 1</td>
</tr>
<tr>
<td>Row 2</td>
</tr>
<tr>
<td>Row 3</td>
</tr>
<tr>
<td>Row 4</td>
</tr>
<!-- 更多行... -->
</table>
你可以使用CSS中的:nth-child()
伪类选择器来选择奇偶行,并为其设置不同的背景样式。具体的CSS代码如下:
tr:nth-child(odd) {
background-color: #f2f2f2; /* 奇数行背景色 */
}
tr:nth-child(even) {
background-color: transparent; /* 偶数行无背景色 */
}
在上述代码中,:nth-child(odd)
选择器选择奇数行,:nth-child(even)
选择器选择偶数行。你可以根据需要调整背景颜色或其他样式。
Git
常用的Git命令
在项目中,常用的Git命令有很多,以下是一些常见的Git命令及其在项目中的具体使用场景:
-
git init
:在项目目录中初始化一个新的Git仓库。-
使用场景:在开始一个新项目时,初始化Git仓库。
-
-
git clone <repository>
:克隆一个远程Git仓库到本地。-
使用场景:获取远程仓库的代码到本地进行开发。
-
-
git add <file>
:将文件添加到暂存区。-
使用场景:将修改的文件添加到Git的暂存区,准备提交。
-
-
git commit -m "<message>"
:提交暂存区的文件到本地仓库。-
使用场景:将暂存区的文件提交到本地仓库,记录代码的变更。
-
-
git push
:将本地仓库的代码推送到远程仓库。-
使用场景:将本地的代码推送到远程仓库,与团队成员共享代码。
-
-
git pull
:从远程仓库拉取最新的代码到本地。-
使用场景:获取远程仓库的最新代码,与团队成员的修改保持同步。
-
-
git branch
:查看本地分支列表。-
使用场景:查看当前仓库的所有分支。
-
-
git checkout <branch>
:切换到指定分支。-
使用场景:切换到指定的分支进行开发或查看代码。
-
-
git merge <branch>
:将指定分支的代码合并到当前分支。-
使用场景:将其他分支的代码合并到当前分支,保持代码的同步。
-
-
git stash
:将当前的修改暂存起来。-
使用场景:在切换分支或处理
-