前端面试题 Vue 向(综合)

前言

本篇内容基本都是根据我实际面试遇到的问题进行整理的,针对的是初级岗位,所以大部分内容都比较基础。

JavaScript

对象去重

要对API返回的对象进行去重,可以使用JavaScript中的Set数据结构。Set是一种集合,它只存储唯一的值,可以帮助我们快速去除重复项。

假设API返回的对象数组为data,我们可以按照以下步骤进行去重:

  1. 创建一个空的Set对象,命名为uniqueSet:const uniqueSet = new Set();

  2. 遍历data数组,对每个对象执行以下操作:

    • 将对象转换为字符串,使用JSON.stringify()方法:const objString = JSON.stringify(obj);

    • 将转换后的字符串添加到uniqueSet中:uniqueSet.add(objString);

  3. 将uniqueSet转换回数组形式,使用Array.from()方法:const uniqueArray = Array.from(uniqueSet);

  4. 将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);

数组去重

数组去重的方法有多种,以下是几种常见的方法:

  1. 使用Set:利用Set数据结构的特性,可以快速去除数组中的重复元素。

const arr = [1, 2, 3, 3, 4, 5, 5];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4, 5]
  1. 使用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]
  1. 使用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]
  1. 使用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]

字符去重

字符去重的方法与数组去重类似,以下是几种常见的方法:

  1. 使用Set:利用Set数据结构的特性,可以快速去除字符串中的重复字符。

const str = "hello";
const uniqueStr = [...new Set(str)].join("");
console.log(uniqueStr); // "helo"
  1. 使用filter:通过filter方法遍历字符串,返回只包含唯一字符的新字符串。

const str = "hello";
const uniqueStr = str
  .split("")
  .filter((item, index, arr) => arr.indexOf(item) === index)
  .join("");
console.log(uniqueStr); // "helo"
  1. 使用reduce:通过reduce方法遍历字符串,将不重复的字符添加到新字符串中。

const str = "hello";
const uniqueStr = str.split("").reduce((prev, curr) => {
  if (!prev.includes(curr)) {
    prev += curr;
  }
  return prev;
}, "");
console.log(uniqueStr); // "helo"
  1. 使用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中,可以使用多种方法来统计字符在字符串中出现的次数。以下是几种常见的方法:

  1. 使用循环遍历字符串:

    • 使用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
  1. 使用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
  1. 使用正则表达式:

    • 使用正则表达式来匹配目标字符在字符串中出现的次数。

    • 使用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中,可以使用多种方法来对数组进行排序。以下是几种常见的排序方法:

  1. 使用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]
  1. 使用自定义比较函数:

    • 可以编写自定义的比较函数来实现特定的排序逻辑。

    • 比较函数接受两个参数,通常被称为ab,表示要比较的两个元素。

    • 如果a应该排在b之前,返回一个负数;如果a应该排在b之后,返回一个正数;如果ab相等,返回0。

let arr = [5, 2, 8, 1, 4];
arr.sort((a, b) => b - a); // 按照数字大小逆序排序
console.log(arr); // 输出:[8, 5, 4, 2, 1]
  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中,可以使用一些内置的方法来进行数据类型的转换。以下是一些常见的数据类型转换方法:

  1. 字符串转换为数字:

    • 使用parseInt()函数将字符串转换为整数。

    • 使用parseFloat()函数将字符串转换为浮点数。

    • 使用Number()函数将字符串转换为数字。

  2. 数字转换为字符串:

    • 使用toString()方法将数字转换为字符串。

    • 使用String()函数将数字转换为字符串。

  3. 字符串转换为布尔值:

    • 使用Boolean()函数将非空字符串转换为true,空字符串转换为false

  4. 布尔值转换为字符串:

    • 使用toString()方法将布尔值转换为字符串。

    • 使用String()函数将布尔值转换为字符串。

  5. 数字转换为布尔值:

    • 使用Boolean()函数将非零数字转换为true,零转换为false

  6. 布尔值转换为数字:

    • 使用Number()函数将true转换为1,false转换为0。

  7. 其他类型转换为数组:

    • 使用Array.from()方法将类数组对象或可迭代对象转换为数组。

    • 使用split()方法将字符串按指定分隔符转换为数组。

  8. 其他类型转换为对象:

    • 使用Object()函数将基本类型转换为包装对象。

需要注意的是,在进行数据类型转换时,要注意数据的有效性和边界情况,以避免出现意外的结果或错误。

== 和 === 的区别

在JavaScript中,=====都是用于比较两个值的运算符,它们之间有以下区别:

  1. ==(相等运算符):

    • ==会进行类型转换后再比较两个值是否相等。

    • 如果比较的两个值类型不同,==会尝试将它们转换为相同的类型,然后再进行比较。

    • 在进行类型转换时,会遵循一些隐式转换的规则,例如将字符串转换为数字,将布尔值转换为数字等。

    • ==会进行类型转换,可能会导致一些意外的结果,因此在使用时需要小心。

  2. ===(严格相等运算符):

    • ===不会进行类型转换,它会直接比较两个值的类型和值是否完全相等。

    • 只有当两个值的类型和值都相等时,===才会返回true,否则返回false

    • ===更加严格,可以避免一些类型转换带来的意外结果。

    • 在进行比较时,建议优先使用===,以避免类型转换带来的问题。

Vue

Vue2和Vue3的区别

Vue2和Vue3是Vue.js框架的两个版本。Vue3是在Vue2的基础上进行了重写和改进,主要有以下几个区别:

  1. 性能优化:Vue3在编译和运行时进行了优化,提高了性能和运行效率。

  2. Composition API:Vue3引入了Composition API,使得组件逻辑更加灵活和可复用。

  3. 更好的TypeScript支持:Vue3对TypeScript的支持更加友好,提供了更好的类型推导和类型检查。

  4. 更小的体积:Vue3的体积比Vue2更小,加载速度更快。

  5. 更好的响应式系统:Vue3使用Proxy代替了Vue2中的Object.defineProperty,使得响应式系统更加强大和灵活。

Vue2中的响应式系统的工作流程

在Vue2中,响应式系统是通过Object.defineProperty来实现的。当一个对象被传入Vue实例的data选项中时,Vue会遍历这个对象的所有属性,并使用Object.defineProperty将它们转换为getter和setter。这样一来,当属性被读取或修改时,Vue能够捕捉到这些操作,并触发相应的更新。

Vue3中响应式系统的改进和优化

在Vue3中,响应式系统进行了重写,并引入了Proxy来替代Object.defineProperty。相比于Vue2,Vue3的响应式系统有以下改进和优化:

  1. 更好的性能:Vue3使用Proxy代理对象,可以直接监听整个对象的变化,而不需要遍历对象的属性。这样可以提高性能,尤其是在大型应用中。

  2. 更灵活的响应式追踪:Vue3的响应式系统可以追踪到动态添加的属性和删除的属性,而Vue2只能追踪已经存在的属性。

  3. 更好的类型推导和类型检查:Vue3的响应式系统对TypeScript的支持更友好,可以更准确地推导和检查属性的类型。

  4. 更小的体积:由于使用了Proxy,Vue3的体积比Vue2更小,加载速度更快。

Vue3中的Composition API

Composition API是Vue3引入的一种新的组合式API风格。它主要有以下优势和特点:

  1. 更灵活的组件逻辑复用:Composition API使得组件的逻辑可以更加灵活地组织和复用,可以将相关的逻辑放在一起,提高代码的可读性和维护性。

  2. 更好的代码组织:Composition API可以将一个组件的逻辑拆分成多个函数,每个函数负责不同的功能,使得代码更加清晰和易于维护。

  3. 更好的类型推导和类型检查:Composition API对TypeScript的支持更好,可以更准确地推导和检查函数的参数和返回值的类型。

  4. 更好的代码重用:Composition API可以将逻辑提取为自定义的Hook函数,可以在多个组件中进行复用。

  5. 更好的IDE支持:由于Composition API使用了函数式的写法,IDE可以提供更好的代码提示和自动补全。

Composition API适用于需要更灵活组织和复用组件逻辑的场景,特别是对于大型应用或复杂组件来说,可以提高开发效率和代码质量。

请你介绍一下Vue的生命周期是什么,以及它的几个阶段。

Vue的生命周期是指Vue实例从创建到销毁的整个过程,可以分为以下几个阶段:

  1. 创建阶段:在这个阶段,Vue实例会进行初始化,包括数据观测、编译模板、挂载实例等。

  2. 更新阶段:当数据发生变化时,Vue会进行重新渲染,并更新DOM。

  3. 销毁阶段:当Vue实例不再需要时,会进行销毁操作,清理相关的事件监听和定时器等。

在每个阶段,Vue提供了一些钩子函数,可以在特定的时机执行一些操作,例如created、mounted、updated和destroyed等。

钩子函数以及生命周期顺序

在Vue中,常用的钩子函数按照生命周期顺序可以分为以下几个阶段:

  1. 创建阶段:

    • beforeCreate:在实例初始化之后,数据观测和事件配置之前调用。可以在这个钩子函数中进行一些初始化的操作,例如配置全局变量、引入插件等。

    • created:在实例创建完成后调用,此时实例已经完成了数据观测和事件配置,但尚未挂载到DOM上。可以在这个钩子函数中进行一些异步操作,例如发送网络请求、获取数据等。

  2. 挂载阶段:

    • beforeMount:在挂载开始之前被调用,此时模板编译已完成,但尚未将模板渲染到DOM中。可以在这个钩子函数中进行一些DOM操作,例如修改DOM元素的属性、样式等。

    • mounted:在挂载完成后被调用,此时实例已经被挂载到DOM上。可以在这个钩子函数中进行一些需要DOM元素的操作,例如初始化第三方库、绑定事件等。

  3. 更新阶段:

    • beforeUpdate:在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前。可以在这个钩子函数中进行一些数据处理的操作,例如格式化数据、计算属性等。

    • updated:在数据更新完成后被调用,此时虚拟DOM已经重新渲染和打补丁完成。可以在这个钩子函数中进行一些DOM操作,例如更新DOM元素的内容、样式等。

  4. 销毁阶段:

    • beforeDestroy:在实例销毁之前调用,此时实例仍然完全可用。可以在这个钩子函数中进行一些清理的操作,例如取消订阅、解绑事件等。

    • destroyed:在实例销毁之后调用,此时实例已经被销毁,所有的事件监听器和子实例都被移除。可以在这个钩子函数中进行一些最终的清理操作,例如释放资源、销毁实例等。

页面第一次加载会触发 4 个钩子,分别是:beforeCreate、created、beforeMount、mounted

v-if和v-show的区别

v-if和v-show都是Vue中的条件渲染指令,用于根据条件来显示或隐藏元素。它们的区别主要有以下几点:

  1. 编译时机:v-if是在每次渲染时进行条件判断,如果条件为false,则不会渲染对应的元素及其子组件。而v-show是在渲染时始终会渲染对应的元素,只是通过CSS的display属性来控制元素的显示和隐藏。

  2. 切换开销:由于v-if是在每次渲染时进行条件判断,如果条件为false,则会销毁对应的元素及其子组件,再次条件为true时重新创建和渲染。而v-show只是通过CSS的display属性来切换元素的显示和隐藏,切换开销较小。

  3. 初始渲染开销:由于v-if是在渲染时进行条件判断,如果条件为false,则不会渲染对应的元素及其子组件,初始渲染开销较小。而v-show在初始渲染时会渲染对应的元素,初始渲染开销较大。

综上所述,如果需要频繁切换元素的显示和隐藏,可以使用v-show,如果条件不经常改变,或者初始渲染开销较大,可以使用v-if。

组件之间的通信方式

Vue中组件之间的通信方式有以下几种:

  1. 父子组件通信:父组件可以通过props向子组件传递数据,子组件通过$emit触发事件,将数据传递给父组件。

  2. 子父组件通信:子组件可以通过$emit触发事件,将数据传递给父组件,父组件通过监听子组件的事件来获取数据。

  3. 兄弟组件通信:可以通过一个共同的父组件作为中介,通过props和$emit来进行兄弟组件之间的通信。

  4. 跨级组件通信:可以使用provide和inject来进行跨级组件之间的通信,父级组件通过provide提供数据,子孙组件通过inject来注入数据。

  5. 使用Vuex进行状态管理:Vuex是Vue的官方状态管理库,可以在不同组件之间共享和管理状态。

v-bind和v-model的区别

  1. v-bind指令:

    • 作用:v-bind用于将Vue实例中的数据绑定到HTML元素的属性上,实现数据的动态绑定。

    • 使用场景:常用于将数据绑定到HTML元素的属性上,例如class、style、src等。适用于单向数据绑定,即数据的变化会反映到视图上,但视图的变化不会影响数据。

  2. v-model指令:

    • 作用:v-model用于在表单元素和Vue实例的数据之间建立双向绑定关系,实现数据的双向同步。

    • 使用场景:常用于表单元素,例如input、textarea、select等。适用于需要实现表单元素和数据之间的双向绑定,即数据的变化会反映到视图上,同时视图的变化也会影响数据。

双向绑定的原理

双向绑定是Vue中的一个重要特性,它可以实现数据的双向同步。双向绑定的原理主要是通过数据劫持和发布-订阅模式来实现的。

在Vue中,当数据发生变化时,Vue会通过数据劫持的方式,即通过Object.defineProperty来劫持数据的getter和setter方法。当数据被修改时,Vue会通知相关的订阅者,订阅者会自动更新对应的视图。而当视图中的输入框等表单元素发生变化时,Vue会通过事件监听的方式,将变化的数据同步到数据模型中。

通过数据劫持和发布-订阅模式的结合,Vue实现了数据和视图之间的双向绑定,使得数据的变化能够自动反映到视图中,同时视图中的变化也能够自动更新到数据模型中。

实现一个基本的双向数据绑定

实现一个基本的双向数据绑定可以通过以下步骤来实现:

  1. 创建一个数据模型对象,包含需要绑定的数据属性。

  2. 在视图中使用表单元素(如输入框)来展示和修改数据。

  3. 在数据模型对象中,使用Object.defineProperty来定义数据属性的getter和setter方法。

  4. 在getter方法中,返回数据属性的值。

  5. 在setter方法中,将新的值赋给数据属性,并触发一个自定义的事件。

  6. 在视图中,通过监听表单元素的变化事件,将变化的值同步到数据模型中。

  7. 在数据模型对象中,监听自定义的事件,当事件触发时,更新视图中的数据。

通过以上步骤,就可以实现一个基本的双向数据绑定,使得数据的变化能够自动反映到视图中,同时视图中的变化也能够自动更新到数据模型中。

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协议中常用的两种请求方法,它们有以下区别:

  1. 数据传输方式:

    • GET:通过URL参数将数据附加在URL后面,以查询字符串的形式发送给服务器。数据会暴露在URL中,可以被缓存、书签等获取。

    • POST:将数据放在请求的消息体中发送给服务器。数据不会暴露在URL中,对于敏感数据更安全。

  2. 数据长度限制:

    • GET:由于数据附加在URL中,URL的长度有限制,不同浏览器和服务器对URL长度的限制不同,一般在2KB到8KB之间。

    • POST:由于数据放在消息体中,没有明确的长度限制,但服务器和网络设备可能会对消息体的大小进行限制。

  3. 数据安全性:

    • GET:由于数据暴露在URL中,容易被拦截、缓存、记录日志等,不适合传输敏感数据。

    • POST:数据放在消息体中,相对于GET请求更安全,适合传输敏感数据。

  4. 缓存:

    • GET:GET请求可以被缓存,可以通过浏览器的后退、刷新等操作重新使用缓存的数据。

    • POST:POST请求不会被缓存,每次请求都会向服务器发送请求。

  5. 幂等性:

    • GET:GET请求是幂等的,即多次请求相同的URL和参数,服务器的响应是一致的,不会对服务器产生副作用。

    • POST:POST请求不是幂等的,多次请求相同的URL和参数,服务器的响应可能不同,可能对服务器产生副作用。

根据具体的需求和场景,选择合适的请求方法可以更好地满足数据传输的需求。一般来说,GET适合获取数据,POST适合提交数据。

本地缓存

  1. Cookie 适用于浏览器与服务器的数据传递:

    • Cookie 是一种在浏览器和服务器之间传递的小型文本文件,用于存储少量数据。

    • Cookie 可以设置过期时间,可以在浏览器关闭后仍然保留。

    • Cookie 的大小有限制,一般为4KB左右。

    • Cookie 在每次请求中都会被发送到服务器,会增加网络流量。

  2. localStorage 适用于长期存储数据:

    • localStorage 是 HTML5 提供的一种持久化存储数据的机制。

    • localStorage 存储的数据没有过期时间,除非手动清除或代码删除。

    • localStorage 的大小限制一般为5MB左右。

    • localStorage 只能通过 JavaScript 进行访问,不会被发送到服务器。

  3. sessionStorage 适用于会话期间保持数据:

    • sessionStorage 也是 HTML5 提供的一种本地存储机制,但与 localStorage 不同,sessionStorage 的数据在会话结束后会被清除。

    • sessionStorage 的大小限制一般为5MB左右。

    • sessionStorage 只能通过 JavaScript 进行访问,不会被发送到服务器。

HTML

前端页面的构成

前端页面通常由以下三层构成:

  1. 结构层(HTML):结构层是前端页面的基础,使用HTML(超文本标记语言)来描述页面的结构和内容。HTML标签和元素用于定义页面的各个部分,如标题、段落、列表、表格等。结构层负责提供页面的基本骨架和内容组织。

  2. 表现层(CSS):表现层使用CSS(层叠样式表)来定义页面的样式和布局。CSS通过选择器和属性来选择页面中的元素,并为其指定样式,如颜色、字体、大小、边距、布局等。表现层负责控制页面的外观和样式,使页面具有吸引力和可读性。

  3. 行为层(JavaScript):行为层使用JavaScript来实现页面的交互和动态效果。JavaScript是一种脚本语言,可以通过操作DOM(文档对象模型)来实现对页面元素的增删改查,以及事件处理、表单验证、动画效果等。行为层负责为页面添加交互性和动态性,使用户能够与页面进行互动。

什么是HTML语义化

HTML语义化是指在编写HTML代码时,根据标签的本身含义和作用来选择合适的标签,以便于开发者和浏览器理解和解析网页内容。它强调使用具有语义的标签来描述网页的结构和内容,使得网页的结构更加清晰、易于理解和维护。

具体来说,HTML语义化包括以下几个方面:

  1. 使用合适的标签:根据内容的含义和作用,选择合适的HTML标签来包裹和描述内容。例如,使用<h1><h6>标签来表示标题的级别,使用<p>标签来表示段落,使用<ul><li>标签来表示无序列表等。

  2. 避免滥用无语义的标签:尽量避免使用无语义的<div><span>标签来包裹内容,而是使用具有语义的标签来更准确地描述内容。例如,使用<nav>标签来表示导航栏,使用<header><footer>标签来表示网页的头部和底部。

  3. 使用语义化的属性:除了选择合适的标签,还应该使用具有语义的属性来描述标签的具体含义。例如,使用alt属性来为<img>标签提供替代文本,使用<a>标签的href属性来指定链接地址。

  4. 构建良好的文档结构:通过合理地嵌套和组织标签,构建良好的文档结构。这样可以使得网页的层次结构更加清晰,方便开发者和浏览器解析和理解网页内容。

通过遵循HTML语义化的原则,可以提高网页的可访问性、SEO优化、代码可读性和可维护性,降低样式和脚本的耦合性,提升用户体验。因此,在开发Web前端时,我们应该尽可能地使用语义化的HTML标签来构建网页结构。

src 和 href 的区别

  1. src属性:

    • 用于指定外部资源(如图片、脚本、音频、视频等)的地址。

    • src属性是必需的,浏览器会根据该属性加载并显示资源。

    • 当浏览器解析到src属性时,会暂停页面的渲染,直到资源加载完成。

  2. 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设置来优化网页:

  1. 页面标题(Title):使用<title>标签来定义页面的标题,确保标题准确、简明地描述页面内容,并包含关键词。搜索引擎会将标题作为重要的参考因素之一。

<head>
  <title>网页标题</title>
</head>
  1. 页面描述(Meta Description):使用<meta>标签的name="description"属性来提供页面的描述信息,描述应该准确、吸引人,并包含关键词。搜索引擎会将描述作为搜索结果中的一部分展示给用户。

<head>
  <meta name="description" content="页面描述">
</head>
  1. 关键词(Meta Keywords):使用<meta>标签的name="keywords"属性来提供页面的关键词,列举与页面内容相关的关键词。然而,搜索引擎对关键词的权重已经降低,不再是主要的优化因素。

<head>
  <meta name="keywords" content="关键词1, 关键词2, 关键词3">
</head>
  1. 页面URL(URL Structure):使用有意义的URL结构,包含关键词,以便搜索引擎和用户更好地理解页面内容。短、简洁的URL通常更易于记忆和分享。

<!-- 示例:使用关键词作为URL -->
<a href="/关键词">链接文本</a>
  1. 标题标签(Heading Tags):使用适当的标题标签(<h1><h6>)来标记页面

CSS

CSS选择器

  1. 元素选择器(Element Selector):通过HTML元素的标签名选择元素,例如 p 选择所有 <p> 元素。

  2. 类选择器(Class Selector):通过元素的 class 属性选择元素,以 . 开头,例如 .my-class 选择所有 class 属性为 my-class 的元素。

  3. ID选择器(ID Selector):通过元素的 id 属性选择元素,以 # 开头,例如 #my-id 选择 id 属性为 my-id 的元素。请注意,一个页面中应该只有唯一的 id

  4. 属性选择器(Attribute Selector):通过元素的属性选择元素,例如 [type="text"] 选择所有 type 属性值为 text 的元素。

  5. 后代选择器(Descendant Selector):通过元素的后代关系选择元素,例如 div p 选择所有 <div> 元素内的 <p> 元素。

  6. 子元素选择器(Child Selector):通过元素的直接子元素关系选择元素,例如 ul > li 选择所有 <ul> 元素下的直接子元素 <li>

  7. 相邻兄弟选择器(Adjacent Sibling Selector):通过元素的相邻兄弟关系选择元素,例如 h2 + p 选择紧接在 <h2> 元素后的第一个 <p> 元素。

  8. 伪类选择器(Pseudo-class Selector):通过元素的特殊状态选择元素,例如 :hover 选择鼠标悬停在元素上的状态。

  9. 伪元素选择器(Pseudo-element Selector):通过元素的特殊部分选择元素,例如 ::before 选择元素的前面插入的内容。

CSS选择器的优先级

CSS选择器的优先级是用于确定当多个选择器同时应用于同一个元素时,哪个选择器的样式规则将被应用。CSS选择器的优先级由以下几个因素决定,按照优先级从高到低的顺序排列:

  1. 内联样式(Inline Styles):使用 style 属性直接在HTML元素上定义的样式具有最高的优先级。例如:<div style="color: red;">Hello</div>

  2. ID选择器(ID Selectors):通过 id 属性选择元素的样式具有较高的优先级。例如:#my-id { color: blue; }

  3. 类选择器、属性选择器和伪类选择器(Class Selectors, Attribute Selectors, and Pseudo-class Selectors):通过类选择器、属性选择器或伪类选择器选择元素的样式具有较低的优先级。例如:.my-class { color: green; }[type="text"] { font-size: 16px; }:hover { background-color: yellow; }

  4. 元素选择器和伪元素选择器(Element Selectors and Pseudo-element Selectors):通过元素选择器或伪元素选择器选择元素的样式具有最低的优先级。例如:p { font-weight: bold; }::before { content: "Before"; }

总结:选择器的优先级又为: !important > 内联(style) > ID > 属性选择器 > 类名选择器 > 标签

如果多个选择器具有相同的优先级,那么后面定义的样式规则将覆盖先前定义的样式规则。如果多个选择器具有不同的优先级,具有较高优先级的样式规则将覆盖较低优先级的样式规则。

需要注意的是,内联样式具有最高的优先级,但是过度使用内联样式会导致代码冗余和维护困难,一般建议使用外部样式表或内部样式表来管理样式。合理使用选择器的优先级可以确保样式规则按照预期生效。

常用的伪类选择器

在CSS中,有许多伪类选择器可以用来选择元素的不同状态或位置。以下是一些常用的伪类选择器:

  1. :hover:选择鼠标悬停在元素上的状态。

  2. :active:选择元素被激活(鼠标按下)的状态。

  3. :focus:选择元素获得焦点的状态(通常用于表单元素)。

  4. :visited:选择已访问过的链接的状态。

  5. :first-child:选择父元素下的第一个子元素。

  6. :last-child:选择父元素下的最后一个子元素。

  7. :nth-child(n):选择父元素下的第n个子元素。

  8. :nth-last-child(n):选择父元素下的倒数第n个子元素。

  9. :nth-of-type(n):选择父元素下的第n个指定类型的子元素。

  10. :nth-last-of-type(n):选择父元素下的倒数第n个指定类型的子元素。

  11. :not(selector):选择不匹配给定选择器的元素。

  12. :empty:选择没有子元素的元素。

  13. :checked:选择被选中的表单元素(如复选框或单选按钮)。

  14. :disabled:选择被禁用的表单元素。

  15. :enabled:选择可用的表单元素。

CSS中的尺寸单位

CSS中有多种尺寸单位,每种单位都有其特定的用途和差异。以下是一些常见的CSS尺寸单位及其差异:

  1. 像素(Pixel,px):像素是相对于显示设备的最小物理点的单位。它是最常用的尺寸单位,可以精确控制元素的大小和位置。像素单位在不同设备上具有固定的物理大小,但在高分辨率屏幕上可能会显得较小。

  2. 百分比(Percentage,%):百分比单位是相对于父元素的尺寸进行计算的。例如,如果一个元素的宽度设置为50%,那么它的宽度将是父元素宽度的一半。百分比单位可以用于实现响应式布局和相对尺寸的调整。

  3. 视窗单位(Viewport Units):视窗单位是相对于浏览器视口的尺寸进行计算的。常见的视窗单位有vw(视口宽度的百分比)、vh(视口高度的百分比)、vmin(视口宽度和高度中较小值的百分比)和vmax(视口宽度和高度中较大值的百分比)。视窗单位可以用于创建基于视口尺寸的自适应布局。

  4. em(相对于父元素的字体尺寸):em单位是相对于元素的字体尺寸进行计算的。例如,如果一个元素的字体大小设置为2em,那么它的字体大小将是父元素字体大小的两倍。em单位可以用于创建相对于字体大小的尺寸。

  5. rem(相对于根元素的字体尺寸):rem单位是相对于根元素(通常是 <html> 元素)的字体尺寸进行计算的。与em单位不同,rem单位不会受到父元素字体大小的影响,更容易控制和调整。

  6. ch(字符宽度):ch单位是相对于当前字体中字符 "0" 的宽度进行计算的。它可以用于创建基于字符宽度的布局。

  7. vw、vh、vmin、vmax、em、rem、ch等单位都是相对单位,可以根据不同的需求选择合适的单位来实现所需的效果。

需要根据具体的需求和设计来选择合适的尺寸单位,以确保元素在不同设备和屏幕尺寸下都能正确地呈现和适应布局。

CSS边距顺序

在CSS中,margin属性用于设置元素的外边距,它可以接受多个值来定义不同方向上的边距。边距的顺序可以按照以下方式指定:

  1. 如果只提供一个值,则应用于所有四个边距(上、右、下、左)。

    margin: 10px;
  2. 如果提供两个值,则第一个值应用于上下边距(上、下),第二个值应用于左右边距(左、右)。

    margin: 10px 20px;
  3. 如果提供三个值,则第一个值应用于上边距,第二个值应用于左右边距,第三个值应用于下边距。

    margin: 10px 20px 30px;
  4. 如果提供四个值,则按照顺序应用于上、右、下、左边距。

    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中,有多种方法可以实现元素的居中。以下是一些常用的居中方法:

  1. 水平居中:

    • 使用text-align: center;将文本内容水平居中,适用于块级元素和行内元素的文本内容。

    • 使用margin: 0 auto;将块级元素的左右外边距设置为自动,适用于具有固定宽度的块级元素。

  2. 垂直居中:

    • 使用display: flex;align-items: center;将容器元素的子元素垂直居中,适用于块级元素。

    • 使用position: absolute;top: 50%;以及transform: translateY(-50%);将元素相对于父元素垂直居中,适用于绝对定位的元素。

  3. 水平垂直居中:

    • 使用display: flex;justify-content: center; align-items: center;将容器元素的子元素水平垂直居中,适用于块级元素。

    • 使用position: absolute;top: 50%; left: 50%;以及transform: translate(-50%, -50%);将元素相对于父元素水平垂直居中,适用于绝对定位的元素。

  4. 表格布局:

    • 使用display: table;margin: 0 auto;将元素水平居中,适用于块级元素。

元素隐藏

  1. opacity: 0

    • 优点:元素仍然占据空间,只是不可见,可以保持布局的完整性。

    • 缺点:元素仍然可以被点击和交互,不适用于需要完全隐藏元素的情况。

    • 适用场景:适用于需要元素占据空间但不可见的情况,比如实现渐变效果或动画过渡。

  2. visibility: hidden

    • 优点:元素不可见,但仍然占据空间,可以保持布局的完整性。

    • 缺点:元素仍然可以被点击和交互,不适用于需要完全隐藏元素的情况。

    • 适用场景:适用于需要元素占据空间但不可见的情况,比如实现菜单的展开和收起。

  3. display: none

    • 优点:元素完全隐藏,不占据空间,不可点击和交互。

    • 缺点:元素隐藏后会导致布局的改变。

    • 适用场景:适用于需要完全隐藏元素的情况,比如实现弹出框或模态框。

  4. position: absolute; left: -9999px;

    • 优点:元素不可见,但仍然占据空间,可以保持布局的完整性。

    • 缺点:元素仍然可以被点击和交互,不适用于需要完全隐藏元素的情况。

    • 适用场景:适用于需要隐藏元素但仍然占据空间的情况,比如实现无障碍功能。

  5. clip: rect(0 0 0 0);

    • 优点:元素被裁剪为不可见的矩形,不占据空间。

    • 缺点:不支持动画效果,不适用于需要保持布局完整性的情况。

    • 适用场景:适用于需要隐藏元素且不占据空间的情况,比如实现特定形状的元素隐藏。

  6. transform: scale(0)

    • 优点:元素被缩放为0,不可见且不占据空间。

    • 缺点:不支持动画效果,不适用于需要保持布局完整性的情况。

    • 适用场景:适用于需要隐藏元素且不占据空间的情况,比如实现动画效果。

  7. 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命令及其在项目中的具体使用场景:

  1. git init:在项目目录中初始化一个新的Git仓库。

    • 使用场景:在开始一个新项目时,初始化Git仓库。

  2. git clone <repository>:克隆一个远程Git仓库到本地。

    • 使用场景:获取远程仓库的代码到本地进行开发。

  3. git add <file>:将文件添加到暂存区。

    • 使用场景:将修改的文件添加到Git的暂存区,准备提交。

  4. git commit -m "<message>":提交暂存区的文件到本地仓库。

    • 使用场景:将暂存区的文件提交到本地仓库,记录代码的变更。

  5. git push:将本地仓库的代码推送到远程仓库。

    • 使用场景:将本地的代码推送到远程仓库,与团队成员共享代码。

  6. git pull:从远程仓库拉取最新的代码到本地。

    • 使用场景:获取远程仓库的最新代码,与团队成员的修改保持同步。

  7. git branch:查看本地分支列表。

    • 使用场景:查看当前仓库的所有分支。

  8. git checkout <branch>:切换到指定分支。

    • 使用场景:切换到指定的分支进行开发或查看代码。

  9. git merge <branch>:将指定分支的代码合并到当前分支。

    • 使用场景:将其他分支的代码合并到当前分支,保持代码的同步。

  10. git stash:将当前的修改暂存起来。

    • 使用场景:在切换分支或处理

阅读剩余
THE END