前端面试遇到的问题记录(实习)

一、vue2和vue3的区别

Vue 2 和 Vue 3 之间存在一些重要的区别,这些差异主要体现在架构、API、性能优化和开发体验等方面。以下是 Vue 2 和 Vue 3 的主要区别:

  1. 数据绑定原理:

    • Vue 2 使用 ES5 的 Object.defineProperty 方法来劫持数据变化。
    • Vue 3 则使用了 ES6 的 Proxy API 来代理数据,这样可以更高效地监听整个对象和数组的变化。
  2. 支持碎片 (Fragments):

    • Vue 2 不支持组件具有多个根节点。
    • Vue 3 支持组件拥有多个根节点(即支持碎片),这在某些情况下可以带来更灵活的组件结构。
  3. API 类型:

    • Vue 2 使用基于选项的对象 API (Options API),例如 datamethodscomputedwatch 等。
    • Vue 3 引入了 Composition API,这是一种基于函数的 API,允许开发者使用 setup 函数来组织和重用组件内的逻辑。
  4. 生命周期钩子:

    • Vue 3 对生命周期钩子进行了调整,例如添加了 setup 钩子,
    • 下面我将通过具体的代码示例来展示 Vue 2 和 Vue 3 在编写组件时的主要区别,特别是围绕 Options API 和 Composition API 的使用。

      Vue 2 示例

      在 Vue 2 中,我们通常使用 Options API 来定义组件。这里是一个简单的 Vue 2 组件示例:

      1// Vue 2 示例
      2new Vue({
      3  el: '#app',
      4  data: {
      5    message: 'Hello Vue 2!'
      6  },
      7  methods: {
      8    reverseMessage() {
      9      this.message = this.message.split('').reverse().join('');
      10    }
      11  },
      12  computed: {
      13    reversedMessage() {
      14      return this.message.split('').reverse().join('');
      15    }
      16  }
      17});
      Vue 3 示例

      在 Vue 3 中,我们既可以继续使用 Options API,也可以采用 Composition API。下面是使用这两种 API 的 Vue 3 组件示例。

      Options API (Vue 3)
      1// Vue 3 示例 (Options API)
      2import { createApp } from 'vue';
      3
      4createApp({
      5  data() {
      6    return {
      7      message: 'Hello Vue 3!'
      8    };
      9  },
      10  methods: {
      11    reverseMessage() {
      12      this.message = this.message.split('').reverse().join('');
      13    }
      14  },
      15  computed: {
      16    reversedMessage() {
      17      return this.message.split('').reverse().join('');
      18    }
      19  }
      20}).mount('#app');
      Composition API (Vue 3)
      1// Vue 3 示例 (Composition API)
      2import { createApp, ref, computed } from 'vue';
      3
      4createApp({
      5  setup() {
      6    const message = ref('Hello Vue 3!');
      7    const reversedMessage = computed(() => message.value.split('').reverse().join(''));
      8
      9    function reverseMessage() {
      10      message.value = message.value.split('').reverse().join('');
      11    }
      12
      13    return {
      14      message,
      15      reversedMessage,
      16      reverseMessage
      17    };
      18  }
      19}).mount('#app');

      分析

    • Options API:

      • Vue 2 和 Vue 3 的 Options API 在语法上非常相似。
      • Vue 3 的 Options API 提供了一些新的生命周期钩子,如 beforeCreate 变为了 beforeSetup(虽然实际使用的名称为 beforeCreate,但 setup 是 Composition API 的一部分)。
    • Composition API:

      • Vue 3 的 Composition API 使用 setup 函数来组织组件逻辑。
      • ref 和 computed 是 Composition API 中的核心功能,用于创建响应式引用和计算属性。
      • Composition API 更加灵活,可以更容易地复用逻辑。
    • 总结

    • Vue 2: 使用 Options API,数据、方法和计算属性直接定义在组件对象中。
    • Vue 3: 既可以使用 Options API 也可以使用 Composition API。Composition API 提供了更多的灵活性和更好的可复用性

二、js数据 类型

JavaScript 中的数据类型分为两大类:原始类型(primitive types)和引用类型(reference types)。下面是对这两种类型的详细介绍:

原始类型 (Primitive Types)

原始类型是值类型,意味着变量直接存储值。原始类型的值是不可变的,即一旦创建就不能改变其内容,只能替换整个值。JavaScript 中的原始类型包括:

  1. number:

    • 用于表示数值,包括整数和浮点数。
    • 注意 JavaScript 中没有单独的整数类型,所有数字都是 number 类型。
  2. string:

    • 用于表示文本字符串。
    • 字符串可以用单引号 ('') 或双引号 ("") 包围。
  3. boolean:

    • 用于表示布尔值,只有两个可能的值:true 和 false
  4. null:

    • 用于表示空值或未定义的对象引用。
    • null 是一个特殊的值,表示“没有任何东西”。
  5. undefined:

    • 用于表示尚未赋值的变量。
    • undefined 表示一个变量已经声明,但还没有被赋予任何值。
  6. symbol:

    • 一种新的原始类型,引入于 ECMAScript 6 (ES6)。
    • symbol 类型的值是唯一的,主要用于作为对象的键名来创建唯一的属性标识符。
  7. bigint:

    • 一种新的原始类型,引入于 ECMAScript 10 (ES10)。
    • bigint 用于表示任意精度的大整数,适用于需要处理非常大的整数的情况。
    • bigint 类型的值以 _n 结尾来区分普通数字。

引用类型 (Reference Types)

引用类型是对象类型,意味着变量存储的是指向内存中对象的引用,而不是对象本身。引用类型的值是可以改变的,可以通过引用进行访问和修改。JavaScript 中的引用类型包括:

  1. object:

    • 所有的对象都是引用类型。
    • 对象是一组无序的属性集合,每个属性都有一个键值对的形式。
    • 常见的内置对象类型包括 ArrayFunctionDateRegExpMapSetWeakMapWeakSet 等。
  2. function:

    • 函数在 JavaScript 中也是一种对象类型,可以被赋值给变量、存储在数据结构中或作为参数传递给其他函数。
    • 函数可以被调用,并可以返回值。
  3. array:

    • 数组是一种特殊的对象类型,用于存储一系列有序的值。
    • 数组可以包含任意类型的值,包括原始类型和其他对象。
  4. date:

    • 用于表示日期和时间的内置对象类型。
  5. regexp:

    • 用于表示正则表达式的内置对象类型。

示例

下面是一些 JavaScript 数据类型的示例:

1// 原始类型
2let num = 42; // number
3let str = "Hello"; // string
4let bool = true; // boolean
5let n = null; // null
6let u = undefined; // undefined
7let sym = Symbol(); // symbol
8let bigInt = 1234567890123456789012345678901234567890n; // bigint
9
10// 引用类型
11let obj = {}; // object
12let arr = [1, 2, 3]; // array
13let date = new Date(); // date
14let regExp = /hello/i; // regexp
15let func = function() { return 'world'; }; // function
16
17console.log(typeof num); // "number"
18console.log(typeof str); // "string"
19console.log(typeof bool); // "boolean"
20console.log(typeof n); // "object" (注意:null 的 typeof 结果为 "object")
21console.log(typeof u); // "undefined"
22console.log(typeof sym); // "symbol"
23console.log(typeof bigInt); // "bigint"
24
25console.log(typeof obj); // "object"
26console.log(Array.isArray(arr)); // true
27console.log(date instanceof Date); // true
28console.log(regExp instanceof RegExp); // true
29console.log(typeof func); // "function"

总结

  • 原始类型:

    • 存储值,不可变。
    • 包括 numberstringbooleannullundefinedsymbolbigint
  • 引用类型:

    • 存储对象的引用,可变。
    • 包括 objectfunctionarraydateregexp 等。

了解 JavaScript 中的数据类型对于编写正确的代码至关重要。

三、判断类型的方法

在 JavaScript 中,判断变量或值的类型是非常常见的需求,尤其是当你需要根据数据类型采取不同的操作时。JavaScript 提供了几种内置的方法来检测一个值的类型。以下是常用的几种方法:

1. typeof 运算符

typeof 是一种简单快速的类型检测方法,它可以返回一个字符串表示变量的类型。对于基本类型,typeof 非常有用,但对于某些复杂类型(如 null 和数组)可能会产生误导。

例子
1console.log(typeof 42);          // "number"
2console.log(typeof "hello");     // "string"
3console.log(typeof true);        // "boolean"
4console.log(typeof undefined);   // "undefined"
5console.log(typeof null);        // "object" (注意: 这是一个特殊情况)
6console.log(typeof {});          // "object"
7console.log(typeof []);          // "object"
8console.log(typeof function() {});// "function"

2. instanceof 运算符

instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。这对于检测对象类型非常有用。

例子
1console.log([1, 2, 3] instanceof Array); // true
2console.log({} instanceof Object);        // true
3console.log(new Date() instanceof Date);  // true

3. Object.prototype.toString.call()

Object.prototype.toString.call() 方法可以用来获取一个对象的内部 [[Class]] 属性。这种方法可以更准确地检测数组和 null 类型。

例子
1console.log(Object.prototype.toString.call(42));          // "[object Number]"
2console.log(Object.prototype.toString.call("hello"));     // "[object String]"
3console.log(Object.prototype.toString.call(true));        // "[object Boolean]"
4console.log(Object.prototype.toString.call(undefined));   // "[object Undefined]"
5console.log(Object.prototype.toString.call(null));        // "[object Null]"
6console.log(Object.prototype.toString.call([]));          // "[object Array]"
7console.log(Object.prototype.toString.call({}));          // "[object Object]"
8console.log(Object.prototype.toString.call(function() {}));// "[object Function]"

4. Array.isArray()

Array.isArray() 方法专门用于检测一个值是否为数组。

例子
1console.log(Array.isArray([1, 2, 3])); // true
2console.log(Array.isArray({}));        // false
3console.log(Array.isArray(null));      // false

5. 自定义函数

有时,为了特定的需求,你可能需要编写自定义的函数来检测类型。例如,你可能会创建一个函数来检测一个值是否为正则表达式。

例子
1function isRegExp(value) {
2  return Object.prototype.toString.call(value) === '[object RegExp]';
3}
4
5console.log(isRegExp(/test/)); // true
6console.log(isRegExp("test")); // false

总结

  • typeof:

    • 适用于检测基本类型。
    • 对于 null 和数组可能会产生误导。
  • instanceof:

    • 适用于检测对象类型。
    • 可能受到原型链污染的影响。
  • Object.prototype.toString.call():

    • 更精确地检测类型。
    • 可以区分 null 和数组。
  • Array.isArray():

    • 专门用于检测数组。

选择合适的方法取决于你的具体需求。在大多数情况下,结合使用 typeofinstanceofObject.prototype.toString.call() 可以覆盖大部分场景。如果需要更细致的类型检查,可以考虑编写自定义函数。

四、想把块剧中,可以用什么方法

看起来您可能是想询问如何将一个块级元素(block-level element)居中。在 CSS 中,有几种方法可以实现这一目标。下面是一些常用的方法:

1. 使用 margin: auto

如果你想要水平居中一个块级元素,可以使用 margin: auto。这种方法适用于已知宽度的元素。

示例代码
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<title>Block Level Element Centering</title>
6<style>
7  .center-block {
8    width: 50%; /* 设置元素的宽度 */
9    margin: auto; /* 水平居中 */
10    border: 1px solid #000; /* 可选:添加边框以便可视化 */
11    padding: 20px; /* 可选:添加内边距 */
12  }
13</style>
14</head>
15<body>
16
17<div class="center-block">
18  这是一个居中的块级元素。
19</div>
20
21</body>
22</html>

2. 使用 Flexbox

Flexbox 提供了一种更加灵活的方式来对齐元素。要使用 Flexbox 居中一个块级元素,你可以将父元素设置为 flex 容器,并使用 justify-content: centeralign-items: center 来居中子元素。

示例代码
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<title>Block Level Element Centering with Flexbox</title>
6<style>
7  .container {
8    display: flex; /* 使父元素成为 flex 容器 */
9    justify-content: center; /* 水平居中 */
10    align-items: center; /* 垂直居中 */
11    height: 100vh; /* 可选:设置容器的高度为视口高度 */
12  }
13  .center-block {
14    border: 1px solid #000; /* 可选:添加边框以便可视化 */
15    padding: 20px; /* 可选:添加内边距 */
16  }
17</style>
18</head>
19<body>
20
21<div class="container">
22  <div class="center-block">
23    这是一个居中的块级元素。
24  </div>
25</div>
26
27</body>
28</html>

3. 使用 Grid

CSS Grid 也是另一种现代的方法来居中块级元素。你可以将父元素设置为 grid 容器,并使用 place-items: center 来居中子元素。

示例代码
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<title>Block Level Element Centering with Grid</title>
6<style>
7  .container {
8    display: grid; /* 使父元素成为 grid 容器 */
9    place-items: center; /* 水平和垂直居中 */
10    height: 100vh; /* 可选:设置容器的高度为视口高度 */
11  }
12  .center-block {
13    border: 1px solid #000; /* 可选:添加边框以便可视化 */
14    padding: 20px; /* 可选:添加内边距 */
15  }
16</style>
17</head>
18<body>
19
20<div class="container">
21  <div class="center-block">
22    这是一个居中的块级元素。
23  </div>
24</div>
25
26</body>
27</html>

总结

  • margin: auto:
    • 适用于已知宽度的元素,仅实现水平居中。
  • Flexbox:
    • 提供了水平和垂直居中的能力。
    • 适用于需要灵活布局的场景。
  • Grid:
    • 也提供了水平和垂直居中的能力。
    • 适用于更复杂的网格布局。

五、跨域产生原因

跨域问题是指在浏览器中尝试从一个域名(或协议、端口)加载资源时,试图访问另一个不同域名(或协议、端口)上的资源。这是由于浏览器实施的安全策略,称为“同源策略”(Same-Origin Policy),该策略限制了来自不同源的脚本之间的交互。

同源策略

同源策略规定,来自相同源的脚本可以自由地访问彼此的文档和数据,而来自不同源的脚本则不能。这里的“源”指的是协议(http 或 https)、域名(如 example.com)和端口(如 80 或 443)的组合。只有这三个组成部分完全相同,才被认为是同一个源。

跨域产生的原因

  1. 不同域名:

    • 当一个网页尝试从一个域名加载资源,而该资源位于另一个域名时,就会产生跨域问题。
    • 例如,从 example.com 加载资源 data.example.org
  2. 不同协议:

    • 如果一个网页尝试从 http 协议加载资源,而该资源位于 https 协议下,也会产生跨域问题。
    • 例如,从 http://example.com 加载资源 https://data.example.org
  3. 不同端口:

    • 即使是同一个域名,如果端口不同,也会被视为不同的源。
    • 例如,从 example.com:80 加载资源 example.com:8080

跨域问题的影响

  1. 安全限制:

    • 同源策略是为了保护用户的隐私和数据安全。
    • 例如,它防止恶意脚本从一个网站窃取另一个网站的敏感数据。
  2. 资源加载限制:

    • 浏览器会阻止来自不同源的脚本加载或访问其他源的资源,除非服务器明确允许跨域访问。

解决方案

  1. CORS (Cross-Origin Resource Sharing):

    • CORS 是一种机制,允许服务器显式地允许来自不同源的请求。
    • 服务器可以通过 HTTP 响应头 Access-Control-Allow-Origin 来指定允许哪些源进行跨域访问。
  2. JSONP (JSON with Padding):

    • JSONP 是一种跨域数据传输的技术,适用于 GET 请求。
    • 它依赖于 <script> 标签不受同源策略限制的特点,但只能用于传输 JSON 数据。
  3. 使用代理服务器:

    • 在后端服务器上设置一个代理,将跨域请求转发到目标服务器,并将响应返回给前端。
    • 这样可以绕过浏览器的同源策略限制。
  4. Server-Side Proxy:

    • 在服务器端设置一个代理,用于转发请求并处理响应。
    • 这种方法通常用于开发环境,以绕过跨域限制。
  5. 使用 WebSocket:

    • WebSocket 协议不受同源策略的限制,可以在不同源之间建立双向通信。
示例

假设你有一个前端应用部署在 example.com 上,需要从 api.example.org 获取数据。在这种情况下,如果不进行额外的配置,前端应用将无法直接通过 AJAX 请求获取 api.example.org 上的数据,因为这两个域名不同。

为了允许跨域访问,api.example.org 的服务器需要在响应头中添加 Access-Control-Allow-Origin 头,以指定允许哪个或哪些源进行跨域访问。

总结

  • 同源策略:
    • 限制了来自不同源的脚本之间的交互。
  • 跨域问题产生原因:
    • 不同的域名、协议或端口。
  • 解决方案:
    • CORS、JSONP、使用代理服务器等。

六、v-if v-so

在 Vue.js 中,v-ifv-show 是两个用于条件渲染的指令,它们都用于根据表达式的真假值来显示或隐藏 DOM 元素。尽管它们的目的相似,但它们的工作原理和使用场景有所不同。

v-if

v-if 是一个真正的条件渲染指令,它会根据表达式的值来判断是否应该将元素渲染到 DOM 中。当表达式的值为 false 时,元素不会被渲染到 DOM 中,也就是说,元素会被完全移除。

特点
  1. 条件渲染:

    • 当 v-if 的表达式值为 false 时,对应的 DOM 元素将不会被渲染到页面上。
    • 当表达式的值变为 true 时,元素将被重新渲染并插入到 DOM 中。
  2. 惰性:

    • v-if 是惰性的,只有当表达式的值首次变为 true 时,才会开始渲染相关的 DOM 元素。
    • 当表达式值变为 false 时,相应的 DOM 元素会被销毁。
  3. 动态性:

    • 当表达式的值频繁改变时,v-if 会导致元素被频繁地创建和销毁,这可能会影响性能。
示例
1<div v-if="isVisible">
2  这段文本只有当 isVisible 为 true 时才会显示。
3</div>

v-show

v-show 则是通过改变元素的 display CSS 属性来切换元素的可见性。无论表达式的值如何,元素始终存在于 DOM 中,只是根据表达式的值决定是否显示。

特点
  1. 始终存在:

    • 使用 v-show 的元素始终存在于 DOM 中,只是根据表达式的值通过 CSS 的 display 属性来控制其可见性。
    • 当表达式为 false 时,元素的 display 属性会被设置为 none,使其在视觉上不可见。
  2. 非惰性:

    • v-show 不是惰性的,即使表达式的值一开始为 false,元素也会被渲染到 DOM 中。
    • 当表达式的值变为 true 时,元素会立即变得可见。
  3. 性能:

    • 因为元素始终存在于 DOM 中,v-show 通常比 v-if 更快,尤其是在表达式的值频繁改变的情况下。
示例
1<div v-show="isVisible">
2  这段文本始终存在于 DOM 中,只有当 isVisible 为 true 时才会显示。
3</div>

总结

  • v-if:

    • 真正的条件渲染,元素根据表达式的值被创建或销毁。
    • 适合条件很少改变的情况。
  • v-show:

    • 通过 CSS 的 display 属性来控制元素的可见性。
    • 适合条件频繁改变的情况。

选择指导

  • 如果条件很少改变,使用 v-if
  • 如果条件频繁改变,使用 v-show
  • 如果需要根据条件执行一次性的 DOM 操作,如条件性地绑定事件监听器,使用 v-if

七、v-modle原理

八、父子组件传参方法

在 Vue.js 中,子组件向父组件传递数据通常通过事件(events)来实现。这种模式遵循了 Vue 的单向数据流原则,即数据从父组件流向子组件,而事件则用于从子组件向父组件传递信息。下面是详细的步骤和示例:

步骤

  1. 定义事件:

    • 在子组件中,使用 $emit 方法定义一个事件,并传递所需的参数。
  2. 监听事件:

    • 在父组件中,使用 v-on(或简写形式 @)监听子组件触发的事件,并定义一个处理函数来处理传递过来的数据。
示例
子组件 (ChildComponent.vue)
1<template>
2  <button @click="sendDataToParent">Send Data to Parent</button>
3</template>
4
5<script>
6export default {
7  methods: {
8    sendDataToParent() {
9      // 触发名为 'send-data' 的事件,并传递一个参数
10      this.$emit('send-data', 'Hello from child component!');
11    }
12  }
13};
14</script>
父组件 (ParentComponent.vue)
1<template>
2  <div>
3    <child-component @send-data="handleDataFromChild"></child-component>
4    <p>{{ receivedData }}</p>
5  </div>
6</template>
7
8<script>
9import ChildComponent from './ChildComponent.vue';
10
11export default {
12  components: {
13    ChildComponent
14  },
15  data() {
16    return {
17      receivedData: ''
18    };
19  },
20  methods: {
21    handleDataFromChild(data) {
22      // 当子组件触发 'send-data' 事件时,这个方法会被调用
23      this.receivedData = data;
24    }
25  }
26};
27</script>

解释

  1. 子组件:

    • 在子组件中,当用户点击按钮时,会触发 sendDataToParent 方法。
    • sendDataToParent 方法使用 $emit 触发一个名为 send-data 的事件,并传递一个字符串参数 "Hello from child component!"
  2. 父组件:

    • 在父组件中,使用 v-on 或 @ 监听子组件触发的 send-data 事件。
    • 当事件被触发时,父组件中的 handleDataFromChild 方法会被调用,并接收子组件传递过来的数据。
    • 接收到的数据被保存在 receivedData 数据属性中,并显示在页面上。

总结

  • 事件触发:

    • 子组件使用 $emit 触发事件,并传递数据。
  • 事件监听:

    • 父组件使用 v-on 或 @ 监听事件,并定义处理函数来处理数据。

九、前端存储数据方法

在前端开发中,有多种方法可以在客户端存储数据。这些方法各有特点,适用于不同的场景。以下是常用的几种前端存储技术:

1. Cookie

  • 用途:

    • 存储小量数据(通常不超过 4KB)。
    • 常用于会话管理、用户偏好设置等。
  • 特点:

    • 每次 HTTP 请求都会携带 Cookie 发送到服务器端。
    • 支持设置过期时间、路径、域名等属性。
    • 不支持跨域访问。
  • 示例:

    1document.cookie = "username=John Doe; expires=Fri, 31 Dec 2023 23:59:59 GMT";

2. Local Storage

  • 用途:

    • 存储大量数据(通常浏览器限制为 5MB 至 10MB)。
    • 数据持久化存储,除非用户手动清除或者程序删除。
  • 特点:

    • 数据永远不会过期,除非被显式删除。
    • 支持字符串形式的数据存储。
    • 不支持跨域访问。
  • 示例:

    1localStorage.setItem('username', 'John Doe');
    2const username = localStorage.getItem('username');

3. Session Storage

  • 用途:

    • 存储会话级别的数据。
    • 数据在浏览器关闭时自动删除。
  • 特点:

    • 数据仅在当前会话期间有效。
    • 支持字符串形式的数据存储。
    • 不支持跨域访问。
  • 示例:

    1sessionStorage.setItem('sessionUsername', 'John Doe');
    2const sessionUsername = sessionStorage.getItem('sessionUsername');

4. IndexedDB

  • 用途:

    • 存储大量数据(理论上无限大,但受制于浏览器限制)。
    • 支持结构化数据存储,例如对象和数组。
  • 特点:

    • 支持事务处理。
    • 支持索引,可以进行高效的查询。
    • 不支持跨域访问。
  • 示例:

     
    1const request = window.indexedDB.open('myDatabase', 1);
    2request.onsuccess = function(event) {
    3  db = event.target.result;
    4};
    5
    6request.onupgradeneeded = function(event) {
    7  const db = event.target.result;
    8  const objectStore = db.createObjectStore('users', { keyPath: 'id' });
    9};
    10
    11// 添加数据
    12const transaction = db.transaction(['users'], 'readwrite');
    13const objectStore = transaction.objectStore('users');
    14objectStore.add({ id: 1, name: 'John Doe' });
    15
    16// 查询数据
    17const request = objectStore.get(1);
    18request.onsuccess = function(event) {
    19  console.log(event.target.result);
    20};

5. Web SQL (已废弃)

  • 用途:

    • 存储数据,类似于 SQLite 数据库。
    • 支持事务处理和结构化数据存储。
  • 特点:

    • 不再被推荐使用。
    • 主要由 Safari 支持,其他浏览器已停止支持。
  • 示例:

    1const db = openDatabase('myDatabase', '1.0', 'My Database', 2 * 1024 * 1024);
    2
    3db.transaction(function(tx) {
    4  tx.executeSql('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)');
    5});
    6
    7db.transaction(function(tx) {
    8  tx.executeSql('INSERT INTO users (name) VALUES (?)', ['John Doe']);
    9});
    10
    11db.transaction(function(tx) {
    12  tx.executeSql('SELECT * FROM users', [], function(tx, results) {
    13    console.log(results.rows.item(0).name);
    14  });
    15});

6. HTML5 localStorage 和 sessionStorage 的区别

  • localStorage:

    • 数据永久存储,除非用户手动清除或者程序删除。
    • 数据在浏览器重启后仍然存在。
  • sessionStorage:

    • 数据仅在当前会话期间有效。
    • 数据在浏览器关闭后被清除。

选择合适的存储方式

  • Cookie:

    • 适用于会话管理和用户认证。
    • 数据量较小。
  • Local Storage:

    • 适用于持久化存储大量数据。
    • 数据不会随浏览器关闭而丢失。
  • Session Storage:

    • 适用于临时存储数据。
    • 数据在浏览器关闭后自动删除。
  • IndexedDB:

    • 适用于需要高性能存储和查询的场景。
    • 支持事务处理和结构化数据。
  • Web SQL:

    • 已废弃,不建议使用。

选择哪种存储方式取决于具体的应用需求。例如,如果你需要存储大量数据并且要求数据持久化,那么 localStorageIndexedDB 可能是更好的选择。如果你需要存储临时数据,那么 sessionStorage 更合适。而对于需要频繁交互的数据,IndexedDB 提供了更多的灵活性和性能优势。

十、v-modle原理

v-model 是 Vue.js 中用于实现表单输入和其他控件的双向数据绑定的一种简化的语法糖。在 Vue 2 中,v-model 的实现原理基于 v-bindv-on,并在内部处理了一些细节,使得开发者可以更方便地处理表单数据。下面是 v-model 的原理及其实现细节:

基本原理

  1. 数据绑定:

    • v-model 通过 v-bind 将数据属性绑定到表单元素的 value 属性上。
    • 当数据属性的值发生变化时,表单元素的值也会随之更新。
  2. 事件监听:

    • v-model 通过 v-on 为表单元素添加事件监听器,通常是 input 事件。
    • 当用户交互导致表单元素的值发生变化时,事件监听器会更新数据属性的值。

不同类型的表单元素

v-model 在不同类型的表单元素上的表现有所不同:

  1. 对于 <input><textarea>:

    • 默认情况下,v-model 绑定 value 属性并监听 input 事件。
    • 当用户输入时,input 事件触发,从而更新 Vue 实例中的数据属性。
  2. 对于 <select>:

    • 默认情况下,v-model 绑定 value 属性并监听 change 事件。
    • 当用户选择选项时,change 事件触发,从而更新 Vue 实例中的数据属性。
  3. 对于 <checkbox><radio>:

    • 对于单个 <checkbox>v-model 绑定 value 属性并监听 change 事件。
    • 对于多个 <checkbox>v-model 绑定到一个数组,并监听 change 事件。
    • 对于 <radio>v-model 绑定 value 属性并监听 change 事件。

实现细节

  1. 内部实现:

    • Vue 会为每个 v-model 绑定的表单元素创建一个内部的 Watcher
    • Watcher 负责收集依赖关系并监听数据变化。
    • 当数据变化时,Watcher 会更新表单元素的值。
    • 当表单元素的值发生变化时,Watcher 会更新数据属性的值。
  2. 修饰符:

    • v-model 支持一些修饰符来改变其默认行为,例如 .lazy.number 和 .trim
    • .lazy 会监听 change 事件而非 input 事件。
    • .number 会对输入的值进行类型转换。
    • .trim 会自动修剪输入的值两端的空白字符。
示例
1<!-- 使用 v-model 实现双向数据绑定 -->
2<template>
3  <div>
4    <input type="text" v-model="message" placeholder="Type something...">
5    <p>{{ message }}</p>
6  </div>
7</template>
8
9<script>
10export default {
11  data() {
12    return {
13      message: ''
14    };
15  }
16};
17</script>

总结

  • v-model:
    • 是一种简化的语法糖,用于实现双向数据绑定。
    • 基于 v-bind 和 v-on 实现。
    • 根据不同的表单元素有不同的默认行为。
    • 支持修饰符来改变默认的行为。

十一、数组去重的方法

在 JavaScript 中,去除数组中的重复元素是一项常见的操作。这里有一些常用的方法来实现数组去重:

1. 使用 Set

Set 是 ES6 中新增的数据结构,它类似于数组,但成员的值都是唯一的,没有重复的值。因此,可以利用 Set 的这一特性来轻松地去除数组中的重复项。

示例
1function uniqueWithSet(arr) {
2  return [...new Set(arr)];
3}
4
5const numbers = [1, 2, 2, 3, 4, 4, 5];
6const uniqueNumbers = uniqueWithSet(numbers);
7console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

2. 使用 Filter

filter() 方法可以创建一个新数组,其中包含通过测试的所有元素。可以通过检查当前元素是否首次出现来实现去重。

示例
1function uniqueWithFilter(arr) {
2  return arr.filter((item, index, self) => self.indexOf(item) === index);
3}
4
5const numbers = [1, 2, 2, 3, 4, 4, 5];
6const uniqueNumbers = uniqueWithFilter(numbers);
7console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

3. 使用 Reduce

reduce() 方法可以将数组转换成单个值,通过累加器来累积结果。在这个过程中,可以构建一个新数组,只包含首次出现的元素。

示例
1function uniqueWithReduce(arr) {
2  return arr.reduce((acc, item) => {
3    if (!acc.includes(item)) {
4      acc.push(item);
5    }
6    return acc;
7  }, []);
8}
9
10const numbers = [1, 2, 2, 3, 4, 4, 5];
11const uniqueNumbers = uniqueWithReduce(numbers);
12console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

4. 使用 Map

Map 是另一种 ES6 数据结构,它可以存储键值对。利用 Map 的键唯一性,可以去除数组中的重复元素。

示例
1function uniqueWithMap(arr) {
2  const map = new Map();
3  arr.forEach(item => map.set(item, 1));
4  return [...map.keys()];
5}
6
7const numbers = [1, 2, 2, 3, 4, 4, 5];
8const uniqueNumbers = uniqueWithMap(numbers);
9console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

5. 使用 Object

可以利用 JavaScript 对象的键唯一性来去除重复项。这种方法适用于元素为原始类型的数组。

示例
1function uniqueWithObject(arr) {
2  const obj = {};
3  arr.forEach(item => {
4    obj[item] = true;
5  });
6  return Object.keys(obj).map(Number);
7}
8
9const numbers = [1, 2, 2, 3, 4, 4, 5];
10const uniqueNumbers = uniqueWithObject(numbers);
11console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

6. 使用 Array.from 和 Set

如果想要返回一个新的数组,可以使用 Array.from() 方法结合 Set

示例
1function uniqueWithArrayFrom(arr) {
2  return Array.from(new Set(arr));
3}
4
5const numbers = [1, 2, 2, 3, 4, 4, 5];
6const uniqueNumbers = uniqueWithArrayFrom(numbers);
7console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

7. 使用 lodash 库

如果你的项目中已经使用了 lodash 库,可以使用 _.uniq() 方法来去重。

示例
1const _ = require('lodash');
2
3const numbers = [1, 2, 2, 3, 4, 4, 5];
4const uniqueNumbers = _.uniq(numbers);
5console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

总结

  • Set:

    • 最简单有效的方法。
    • 适用于所有类型的元素。
  • filter():

    • 适用于原始类型的元素。
    • 可能不适合深度相等的对象或数组。
  • reduce():

    • 适用于原始类型的元素。
    • 可以通过自定义比较函数来支持复杂类型。
  • Map:

    • 适用于所有类型的元素。
    • 适合大型数组。
  • Object:

    • 适用于原始类型的元素。
    • 简单快速,但不适用于复杂类型。
  • Array.fromSet:

    • 结合了 Set 的优点和 Array.from 的灵活性。
    • 适用于所有类型的元素。
  • lodash:

    • 如果已经在使用 lodash,可以直接使用。
    • 提供了额外的功能和优化。

十二、在 JavaScript 中,判断变量或值的类型有几种常用的方法 

在 JavaScript 中,判断变量或值的类型是非常常见的需求,尤其是当你需要根据数据类型采取不同的操作时。JavaScript 提供了几种内置的方法来检测一个值的类型。以下是常用的几种方法:

1. typeof 运算符

typeof 是一种简单快速的类型检测方法,它可以返回一个字符串表示变量的类型。对于基本类型,typeof 非常有用,但对于某些复杂类型(如 null 和数组)可能会产生误导。

例子
1console.log(typeof 42);          // "number"
2console.log(typeof "hello");     // "string"
3console.log(typeof true);        // "boolean"
4console.log(typeof undefined);   // "undefined"
5console.log(typeof null);        // "object" (注意: 这是一个特殊情况)
6console.log(typeof {});          // "object"
7console.log(typeof []);          // "object"
8console.log(typeof function() {});// "function"

2. instanceof 运算符

instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。这对于检测对象类型非常有用。

例子
1console.log([1, 2, 3] instanceof Array); // true
2console.log({} instanceof Object);        // true
3console.log(new Date() instanceof Date);  // true

3. Object.prototype.toString.call()

Object.prototype.toString.call() 方法可以用来获取一个对象的内部 [[Class]] 属性。这种方法可以更准确地检测数组和 null 类型。

例子
1console.log(Object.prototype.toString.call(42));          // "[object Number]"
2console.log(Object.prototype.toString.call("hello"));     // "[object String]"
3console.log(Object.prototype.toString.call(true));        // "[object Boolean]"
4console.log(Object.prototype.toString.call(undefined));   // "[object Undefined]"
5console.log(Object.prototype.toString.call(null));        // "[object Null]"
6console.log(Object.prototype.toString.call([]));          // "[object Array]"
7console.log(Object.prototype.toString.call({}));          // "[object Object]"
8console.log(Object.prototype.toString.call(function() {}));// "[object Function]"

4. Array.isArray()

Array.isArray() 方法专门用于检测一个值是否为数组。

例子
1console.log(Array.isArray([1, 2, 3])); // true
2console.log(Array.isArray({}));        // false
3console.log(Array.isArray(null));      // false

5. 自定义函数

有时,为了特定的需求,你可能需要编写自定义的函数来检测类型。例如,你可能会创建一个函数来检测一个值是否为正则表达式。

例子
1function isRegExp(value) {
2  return Object.prototype.toString.call(value) === '[object RegExp]';
3}
4
5console.log(isRegExp(/test/)); // true
6console.log(isRegExp("test")); // false

总结

  • typeof:

    • 适用于检测基本类型。
    • 对于 null 和数组可能会产生误导。
  • instanceof:

    • 适用于检测对象类型。
    • 可能受到原型链污染的影响。
  • Object.prototype.toString.call():

    • 更精确地检测类型。
    • 可以区分 null 和数组。
  • Array.isArray():

    • 专门用于检测数组。

选择合适的方法取决于你的具体需求。在大多数情况下,结合使用 typeofinstanceofObject.prototype.toString.call() 可以覆盖大部分场景。如果需要更细致的类型检查,可以考虑编写自定义函数。

十三、vue2生命周期

Vue.js 2 的生命周期是指 Vue 实例从创建到销毁的整个过程。理解 Vue 的生命周期对于调试和优化 Vue 应用程序非常重要。Vue 2 的生命周期可以分为几个主要阶段:

  1. 创建阶段:

    • beforeCreate:

      • 在 Vue 实例被创建之后,但在数据观测、事件配置、$el 编译等初始化操作之前被调用。
      • 在这个阶段,this 指向 Vue 实例,但是还不能访问到 datamethodscomputed 等属性,因为它们还未被初始化。
    • created:

      • 在 Vue 实例创建完成后被调用。
      • 在这个阶段,datamethodscomputed 等属性已经被初始化,但是模板编译和挂载尚未完成,$el 还不可用。
  2. 挂载阶段:

    • beforeMount:

      • 在模板编译完成并且即将挂载到 DOM 之前被调用。
      • 在这个阶段,$el 已经可用,但是尚未添加到 DOM 中。
    • mounted:

      • 在 Vue 实例被挂载到 DOM 并完成渲染之后被调用。
      • 在这个阶段,你可以访问并操作 DOM 元素。
  3. 更新阶段:

    • beforeUpdate:

      • 在数据更新导致的虚拟 DOM 重新渲染和打补丁之前被调用。
      • 在这个阶段,你可以访问旧的 DOM。
    • updated:

      • 在数据更新导致的虚拟 DOM 重新渲染和打补丁之后被调用。
      • 在这个阶段,你可以访问更新后的 DOM。
  4. 销毁阶段:

    • beforeDestroy:

      • 在 Vue 实例销毁之前被调用。
      • 在这个阶段,你可以进行一些必要的清理工作,比如取消事件监听器、定时器等。
    • destroyed:

      • 在 Vue 实例被销毁之后被调用。
      • 在这个阶段,Vue 实例已经完全被销毁,不再有任何关联的 DOM 元素,所有事件监听器已经被移除,所有的子实例也被销毁。

生命周期钩子函数

  • 钩子函数:
    • Vue 提供了一系列的钩子函数,它们会在不同的生命周期阶段被自动调用。
    • 这些钩子函数可以用来执行特定的操作,例如初始化数据、设置事件监听器、清理资源等。
示例
1new Vue({
2  el: '#app',
3  data: {
4    message: 'Hello Vue!'
5  },
6  beforeCreate() {
7    console.log('beforeCreate');
8  },
9  created() {
10    console.log('created');
11  },
12  beforeMount() {
13    console.log('beforeMount');
14  },
15  mounted() {
16    console.log('mounted');
17  },
18  beforeUpdate() {
19    console.log('beforeUpdate');
20  },
21  updated() {
22    console.log('updated');
23  },
24  beforeDestroy() {
25    console.log('beforeDestroy');
26  },
27  destroyed() {
28    console.log('destroyed');
29  },
30  methods: {
31    changeMessage() {
32      this.message = 'Message changed!';
33    }
34  }
35});

总结

  • 创建阶段:
    • beforeCreate 和 created
  • 挂载阶段:
    • beforeMount 和 mounted
  • 更新阶段:
    • beforeUpdate 和 updated
  • 销毁阶段:
    • beforeDestroy 和 destroyed

理解这些生命周期阶段可以帮助你更好地组织代码,特别是在处理复杂的 UI 更新逻辑、性能优化以及资源管理方面。

十四、为什么 Vue 2 中 data 是一个对象而不是一个函数。

在 Vue.js 2 中,data 属性是一个对象,而不是一个函数,这是因为 Vue 2 的设计决策和响应式系统的实现方式。下面我将解释为什么 Vue 2 中 data 是一个对象而不是一个函数。

Vue 2 的 data 设计

  1. 对象的可观察性:

    • Vue 2 使用了数据劫持的方式实现响应式系统,通过 Object.defineProperty() 方法将数据属性转换为 getter 和 setter。
    • 这种方式要求数据属性必须在 Vue 实例创建时就已经存在,以便能够对其进行劫持并实现响应式。
  2. 初始状态的共享:

    • 如果 data 是一个函数,每次创建新的 Vue 实例时,data 函数会被调用多次,这会导致每个实例的 data 属性都指向同一个对象实例。
    • 为了避免这种情况,Vue 2 中 data 必须是一个对象,以确保每个 Vue 实例都有自己的独立数据副本。
  3. 易于理解和使用:

    • 使用对象作为 data 属性更加直观,因为它直接反映了数据的状态。
    • 开发者可以更容易地查看和理解数据的结构和初始值。

Vue 3 的 data 设计

相比之下,在 Vue 3 中,data 既可以是对象也可以是函数,这是因为 Vue 3 使用了 Proxy API 来实现响应式系统,这使得 Vue 3 能够更灵活地处理数据。

Vue 2 的 data 实现细节

  1. 响应式机制:

    • Vue 2 使用了数据劫持的方式,即通过 Object.defineProperty() 方法来使数据具有响应性。
    • 当数据被访问或修改时,Vue 会收集依赖关系并更新视图。
  2. 数据初始化:

    • 在 Vue 2 中,当创建一个 Vue 实例时,data 对象会被转换成响应式的。
    • Vue 会遍历 data 对象中的所有属性,并使用 Object.defineProperty() 方法将其变为可观察的。
  3. 避免数据共享:

    • 如果 data 是一个函数,每次创建 Vue 实例时都会调用这个函数,这样就会产生多个指向同一个对象的引用。
    • 为了保证每个实例的数据是独立的,Vue 2 中的 data 必须是一个对象。
示例

下面是一个简单的 Vue 2 实例,展示了 data 作为一个对象的使用:

1new Vue({
2  el: '#app',
3  data: {
4    message: 'Hello Vue!'
5  },
6  created() {
7    console.log(this.message); // 输出 "Hello Vue!"
8  }
9});

在这个例子中,data 是一个对象,包含了 message 属性。当 Vue 实例创建时,message 的值被设置为 'Hello Vue!',并在 created 钩子中输出。

总结

  • Vue 2 的 data:
    • 是一个对象,用于初始化 Vue 实例的数据。
    • 保证了每个 Vue 实例都有独立的数据副本。
    • 使用数据劫持的方式实现响应式。

使用对象作为 data 属性是 Vue 2 中的一项设计决策,它确保了数据的响应式特性和每个实例的独立性。如果需要为每个实例生成不同的初始数据,可以在创建 Vue 实例时通过工厂函数来创建 data 对象。例如:

1new Vue({
2  el: '#app',
3  data: function () {
4    return {
5      message: 'Hello Vue!'
6    };
7  }
8});

十五 、关于对象的拷贝

在 JavaScript 中,对象的拷贝通常指的是创建一个对象的副本。对象拷贝可以分为浅拷贝和深拷贝两种类型。下面详细解释这两种拷贝的区别以及如何实现它们:

浅拷贝 (Shallow Copy)

浅拷贝只会复制对象的第一层属性,对于对象内部的引用类型(如数组、对象等),它只会复制引用而不是实际的内容。

实现方法
  1. 使用 Object.assign():

    1const shallowCopy = Object.assign({}, originalObject);
  2. 使用扩展运算符 (...):

    1const shallowCopy = { ...originalObject };
  3. 使用 Array.prototype.slice.call() (适用于数组对象):

    1const shallowCopy = [].slice.call(originalArray);

深拷贝 (Deep Copy)

深拷贝会递归地复制对象的所有层级,包括内部的引用类型。这意味着原始对象和拷贝对象将完全独立,更改其中一个不会影响另一个。

实现方法
  1. 使用 JSON.parse()JSON.stringify():

    1const deepCopy = JSON.parse(JSON.stringify(originalObject));

    注意事项:

    • 这种方法不适用于包含循环引用的对象。
    • 它也不能正确处理函数和 Symbol 类型的属性。
  2. 使用第三方库:

    • Lodash:

      1const _ = require('lodash');
      2const deepCopy = _.cloneDeep(originalObject);
    • Ramda:

      1const R = require('ramda');
      2const deepCopy = R.clone(originalObject);
  3. 手动实现:

    1function deepClone(obj) {
    2  if (obj === null || typeof obj !== 'object') {
    3    return obj;
    4  }
    5
    6  let clone = Array.isArray(obj) ? [] : {};
    7
    8  for (let key in obj) {
    9    if (obj.hasOwnProperty(key)) {
    10      clone[key] = deepClone(obj[key]);
    11    }
    12  }
    13
    14  return clone;
    15}
    16
    17const deepCopy = deepClone(originalObject);
示例

假设我们有一个包含嵌套对象和数组的原始对象:

1const originalObject = {
2  a: 1,
3  b: { c: 2 },
4  d: [3, 4, 5],
5  e: function () { return 'test'; },
6  f: Symbol('symbol')
7};
8
9const shallowCopy = Object.assign({}, originalObject);
10const deepCopy = JSON.parse(JSON.stringify(originalObject));

测试

现在我们可以测试浅拷贝和深拷贝的效果:

1originalObject.b.c = 3;
2originalObject.d.push(6);
3
4console.log(shallowCopy); // { a: 1, b: { c: 3 }, d: [3, 4, 5, 6], e: [Function], f: Symbol(symbol) }
5console.log(deepCopy);    // { a: 1, b: { c: 2 }, d: [3, 4, 5], e: [Function], f: Symbol(symbol) }

总结

  • 浅拷贝:

    • 只复制对象的第一层属性。
    • 引用类型属性的拷贝实际上复制的是引用。
    • 适用于不需要递归复制的对象。
  • 深拷贝:

    • 递归复制对象的所有层级。
    • 创建完全独立的副本。
    • 适用于需要完整复制的对象。

十六、Map可以用对象去重吗

Map 本身并不直接用于对象去重,因为它主要用于存储键值对。然而,你可以利用 Map 的特性来帮助实现对象数组的去重。下面是如何使用 Map 来去重对象数组的示例:

使用 Map 去重对象数组

  1. 创建一个 Map:

    • 使用 Map 来存储对象的引用或对象的唯一标识符作为键。
  2. 生成唯一键:

    • 对于每个对象,你需要生成一个唯一的键,通常这可以通过序列化对象来实现。
    • 例如,可以使用 JSON.stringify 将对象转换为字符串,然后将其作为 Map 的键。
  3. 检查重复:

    • 在将对象添加到最终结果数组之前,先检查 Map 中是否已经有了相同的键。
    • 如果没有,将对象添加到结果数组,并将键添加到 Map 中。

示例代码

1const objects = [
2  { id: 1, name: 'Alice' },
3  { id: 2, name: 'Bob' },
4  { id: 1, name: 'Alice' }, // 重复项
5  { id: 3, name: 'Charlie' },
6  { id: 2, name: 'Bob' }    // 重复项
7];
8
9// 创建一个 Map 用于存储对象的唯一标识符
10const seenObjects = new Map();
11
12// 创建一个空数组用于存储唯一对象
13const uniqueObjects = [];
14
15// 遍历对象数组
16objects.forEach(obj => {
17  // 生成一个唯一的键
18  const key = JSON.stringify(obj);
19
20  // 如果 `seenObjects` 中没有相同的键,则将对象添加到 `uniqueObjects`
21  if (!seenObjects.has(key)) {
22    seenObjects.set(key, true);
23    uniqueObjects.push(obj);
24  }
25});
26
27console.log(uniqueObjects);
28// 输出: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' }]

解释

  1. 生成唯一键:

    • 使用 JSON.stringify 将每个对象转换为字符串。
    • 这个字符串可以作为 Map 的键,用于检查是否有重复的对象。
  2. 检查重复:

    • 如果 Map 中还没有这个键,说明这个对象是唯一的。
    • 将对象添加到结果数组 uniqueObjects 中,并将键添加到 Map 中。
  3. 结果:

    • 最终得到的 uniqueObjects 数组只包含唯一对象。

注意事项

  • 序列化问题:

    • 使用 JSON.stringify 有一些局限性,例如它不能处理循环引用的对象,也无法区分对象中的原型链。
    • 如果对象结构复杂或含有循环引用,可能需要使用更复杂的算法来生成唯一键。
  • 性能考虑:

    • 对于大型数组或复杂对象,使用 JSON.stringify 可能会导致性能问题。
    • 在这种情况下,可以考虑使用更高效的键生成方法,例如自定义哈希函数。

十七、vue里key的作用,循环

在 Vue.js 中,key 属性在列表渲染(通常使用 v-for 指令)中扮演着非常重要的角色。正确使用 key 可以提高应用的性能,并且有助于保持数据的准确性。以下是 key 的主要作用:

1. 唯一标识符

  • 唯一性key 应该为每一个被渲染的元素提供一个唯一的标识符。
  • 性能优化:Vue 使用虚拟 DOM 来比较和更新 DOM 结构。当列表数据发生变化时,Vue 会使用 key 来识别哪些元素已经被添加、删除或者移动。这使得 Vue 可以最小化 DOM 更新,提高性能。

2. 组件的重用

  • 组件更新:当使用 v-for 渲染组件时,如果数据项的 key 发生变化,Vue 会重新创建组件而不是复用已有的组件实例。这有助于保持组件内部状态的准确性。

3. 避免警告

  • 避免警告:如果没有为列表中的每一项提供 key,Vue 会在控制台中发出警告。这是因为 Vue 不知道如何有效地更新列表中的元素,特别是在列表中有状态的组件时。

4. 列表更新的准确性

  • 列表更新:在动态列表中,当数据项的顺序发生变化时,Vue 会根据 key 来判断元素是否被移动、添加或删除。这有助于保持视图与数据模型的一致性。

如何使用 key

当你使用 v-for 渲染列表时,你应该尽可能地为每一项提供一个唯一的 key。一个常见的做法是使用数据项的 ID 作为 key 的值,例如:

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

在这个例子中,假设 items 数组中的每个对象都有一个 id 属性,那么我们就可以使用这个 id 作为 key 的值。

注意事项

  • 尽量避免使用索引作为 key

    • 当数据发生变化时,如排序或过滤等操作,索引会发生变化,这可能会导致不必要的组件重新渲染。
    • 如果列表数据不发生变化,使用索引作为 key 是可以接受的。
  • 不要使用对象或数组作为 key

    • 对象和数组的引用改变不会触发重新渲染,因此不适合用作 key
  • 确保 key 的唯一性

    • 即使是在一个较小的范围内,key 也应该是唯一的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值