jiangwei小站
7572 字
38 分钟
面试题

浏览器#

TCP 三次握手#

HTTP(Hypertext Transfer Protocol)是一种基于请求-响应模式的应用层协议,用于在客户端和服务器之间传输超文本数据。

“三次握手”是指在建立 TCP(Transmission Control Protocol)连接时,客户端和服务器之间进行的一系列确认步骤。HTTP 使用 TCP 作为传输层协议,因此在建立 HTTP 连接之前,需要进行 TCP 的三次握手。

下面是三次握手的过程:

  1. 第一次握手(SYN):客户端向服务器发送一个带有 SYN(同步)标志的 TCP 报文段,表示客户端请求建立连接。客户端选择一个初始的序列号,并进入 SYN_SENT 状态。

  2. 第二次握手(SYN + ACK):服务器收到客户端的请求后,向客户端发送一个带有 SYN 和 ACK(确认)标志的 TCP 报文段,表示同意建立连接。服务器也选择一个初始的序列号,并确认客户端的序列号。服务器进入 SYN_RECEIVED 状态。

  3. 第三次握手(ACK):客户端收到服务器的确认后,向服务器发送一个带有 ACK 标志的 TCP 报文段,表示客户端接受连接。同时,客户端也确认了服务器的序列号。服务器和客户端都进入 ESTABLISHED 状态,建立了可靠的连接。

通过三次握手,客户端和服务器就建立了双向的 TCP 连接,可以进行数据的可靠传输。在建立连接之后,客户端可以向服务器发送 HTTP 请求,服务器接收请求并返回相应的 HTTP 响应。

需要注意的是,三次握手是建立 TCP 连接的过程,并不是 HTTP 协议的一部分。HTTP 协议本身是无状态的,每个请求和响应都是独立的,不保留之前的状态信息。TCP 连接的建立和断开是为了在传输层上提供可靠的数据传输。

浏览器渲染流程#

  1. 解析 HTML:浏览器首先将接收到的 HTML 文档进行解析,创建 DOM(文档对象模型)树。DOM 树表示了 HTML 文档的结构和层次关系。

  2. 解析 CSS:浏览器解析 CSS 样式表,创建 CSSOM(CSS 对象模型)树。CSSOM 树表示了 CSS 样式的层次结构和样式规则。

  3. 合并 DOM 和 CSSOM:浏览器将 DOM 树和 CSSOM 树合并为一个渲染树(Render Tree)。渲染树只包含需要显示的元素和它们的样式信息。

  4. 布局(Layout):浏览器根据渲染树的信息,计算各个元素在页面中的几何位置和大小,这个过程也称为布局或回流(reflow)。

  5. 绘制(Paint):浏览器使用计算好的布局信息,将页面上的元素绘制到屏幕上。

  6. 合成(Composite):如果页面有多个层叠的元素,浏览器会利用硬件加速来合成图层,提高渲染性能。

  7. 重复步骤:如果页面中的内容发生变化(例如用户交互、JavaScript 操作等),浏览器会重新执行上述步骤,更新渲染结果。

安全问题#

  1. 跨站脚本攻击(XSS):XSS 是一种常见的攻击方式,利用恶意脚本注入网页中,从而获取用户敏感信息或执行恶意操作。开发人员应遵循安全编码实践,对用户输入进行正确的验证和过滤,使用适当的转义和编码方式来防止 XSS 攻击。

  2. 跨站请求伪造(CSRF):CSRF 攻击利用用户已经通过认证的会话发送恶意请求,从而执行未经授权的操作。开发人员应该在关键操作中使用适当的防护措施,如添加随机生成的令牌(CSRF Token)来验证请求的合法性。

  3. 本地存储安全:HTML5 提供了本地存储功能,如 Web Storage(localStorage 和 sessionStorage)和 IndexedDB。开发人员在使用这些功能时应该注意数据安全,避免存储敏感信息,并对存储的数据进行适当的加密和验证。

  4. 跨源资源共享(CORS):CORS 允许网页在浏览器端与其他域进行跨域通信。开发人员应了解如何正确配置和使用 CORS,以防止恶意网站利用 CORS 漏洞进行信息泄露或攻击。

  5. 播放器安全:HTML5 标准支持音频和视频播放。在使用 HTML5 播放器时,开发人员应该注意安全性,避免加载来自不受信任的来源的媒体文件,以防止恶意代码执行或信息泄露。

  6. 隐私和权限:HTML5 引入了一些新的 API,如地理位置、摄像头和麦克风访问等。开发人员应该遵循用户隐私和权限原则,只在必要的情况下请求和使用这些敏感权限,并提供明确的用户授权和选择。

安全更新和版本控制:HTML5 技术和相关库、框架经常会有更新和修复安全漏洞的版本发布。开发人员应及时跟踪和应用这些更新,以确保使用的是最新和安全的版本。

js#

问:0.1 + 0.2 === 0.3 嘛?为什么?#

在两数相加时,会先转换成二进制,0.1 和 0.2 转换成二进制的时候尾数会发生无限循环,然后进行对阶运算,JS 引擎对二进制进行截断,所以造成精度丢失。
所以总结:精度丢失可能出现在进制转换和对阶运算中

问:JS 数据类型#

基本类型:Number、Boolean、String、null、undefined、symbol(ES6 新增的),BigInt(ES2020) 引用类型:Object,对象子类型(Array,Function)

js 深浅拷贝#

浅拷贝:扩展符,for in 遍历,Object.assign() 深拷贝:JSON.parse(JSON.stringify(obj)),递归,lodash.cloneDeep(), structuredClone(value)

Number() 的存储空间是多大?如果后台发送了一个超过最大自己的数字怎么办#

Math.pow(2, 53) ,53 为有效数字,会发生截断,等于 JS 能支持的最大数字。 可以使用 bigInt, 或者将数据转为 string.

new 一个函数发生了什么#

构造调用: 创造一个全新的对象 这个对象会被执行 [[Prototype]] 连接,将这个新对象的 [[Prototype]] 链接到这个构造函数.prototype 所指向的对象 这个新对象会绑定到函数调用的 this 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象

symbol 有什么用处#

可以用来表示一个独一无二的变量防止命名冲突。但是面试官问还有吗?我没想出其他的用处就直接答我不知道了,还可以利用 symbol 不会被常规的方法(除了 Object.getOwnPropertySymbols 外)遍历到,所以可以用来模拟私有变量。

主要用来提供遍历接口,布置了 symbol.iterator 的对象才可以使用 for···of 循环,可以统一处理数据结构。调用之后回返回一个遍历器对象,包含有一个 next 方法,使用 next 方法后有两个返回值 value 和 done 分别表示函数当前执行位置的值和是否遍历完毕。

Symbol.for() 可以在全局访问 symbol

问:了解 this 嘛,bind,call,apply 具体指什么#

call: Array.prototype.call(this, args1, args2) apply: Array.prototype.apply(this, [args1, args2]) ES6 之前用来展开数组调用, foo.apply(null, []),ES6 之后使用 … 操作符

bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用。

事件流#

事件流是网页元素接收事件的顺序,“DOM2 级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段 事件按 DOM 事件流的顺序执行事件处理程序: 父级捕获 子级捕获 子级冒泡 父级冒泡

addEventListener 第三个参数:true 为捕获阶段 false 为冒泡阶段

闭包#

保护变量:
通过使用闭包,可以在函数外部创建变量,并通过内部函数的闭包引用来访问和修改这些变量。这样可以避免变量被外部访问和篡改,实现了变量的私有化。

模块化开发:
闭包提供了一种实现模块化开发的方式。通过在函数内部定义变量和方法,并将其返回或暴露给外部,可以隐藏内部实现细节,只暴露对外部有用的接口或方法。

延续变量生命周期:
闭包使得函数内部的变量可以在函数执行完毕后继续存在,因为闭包引用了这些变量。这对于需要在函数执行完毕后继续访问或操作这些变量的情况非常有用。

function outerFunction() {
  var outerVariable = "I'm from outer";

  function innerFunction() {
    console.log(outerVariable);
  }

  return innerFunction;
}

var closure = outerFunction();
closure(); // 输出: "I'm from outer"

需要注意的是,由于闭包会持有对外部变量的引用,如果闭包的生命周期比外部函数长,这些外部变量就无法被垃圾回收,可能导致内存泄漏。因此,在使用闭包时需要注意管理和释放资源,避免造成不必要的内存占用。

原型链#

原型链(Prototype Chain)是 JavaScript 中用于实现对象之间继承的机制。每个 JavaScript 对象都有一个原型(Prototype)属性,它指向另一个对象,该对象被称为原型对象(Prototype Object)。当我们访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到对应的属性或方法或者到达原型链的末尾(即 null)。

原型链的工作方式如下:

每个对象都有一个内部属性 [[Prototype]],用于指向其原型对象。可以通过 Object.getPrototypeOf(obj) 或 proto 属性来访问对象的原型。

当我们访问一个对象的属性或方法时,如果对象本身没有这个属性或方法,JavaScript 引擎会沿着原型链向上查找。

如果在当前对象的原型上找到了属性或方法,则返回该属性或方法的值。

如果没有找到,则继续在原型对象的原型上进行查找,直到找到对应的属性或方法或者到达原型链的末尾(即 null)。

如果整个原型链上都没有找到对应的属性或方法,则返回 undefined。

通过原型链,JavaScript 实现了对象之间的继承。当我们访问一个对象的属性或方法时,可以首先在对象自身查找,然后根据原型链自动向上查找,直到找到或到达顶层原型对象。

下面是一个原型链的示例:

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function () {
  console.log("Hello, my name is " + this.name);
};

function Student(name, major) {
  Person.call(this, name);
  this.major = major;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.study = function () {
  console.log(this.name + " is studying " + this.major);
};

var john = new Student("John", "Computer Science");
john.greet(); // 输出: "Hello, my name is John"
john.study(); // 输出: "John is studying Computer Science"

在上述代码中,我们定义了一个 Person 构造函数和一个 Student 构造函数。Person 对象拥有 name 属性和 greet 方法,而 Student 对象继承了 Person 对象,并拥有 major 属性和 study 方法。

通过设置 Student.prototype = Object.create(Person.prototype),我们将 Student 的原型对象指向了 Person 的原型对象,建立了原型链。这样,Student 对象可以访问 Person 对象的属性和方法。

通过原型链,john 实例可以调用 greet 方法和 study 方法,它们分别是从 Person.prototype 和 Student.prototype 继承而来。

原型链是 JavaScript 中实现继承的一种机制,它使得对象可以共享属性和方法,并提供了一种灵活和高效的对象模型。

原型链的顶层是 Object.prototype

Event Loop#

JavaScript 的事件循环(Event Loop)是一种处理异步操作的机制,它控制着 JavaScript 代码的执行顺序和处理事件的方式。了解事件循环对于理解 JavaScript 异步编程非常重要。

下面是 JavaScript 事件循环的基本原理:

  1. 执行同步代码:JavaScript 代码在执行时,会按照顺序执行同步代码,从上到下逐行执行。

  2. 处理微任务(Microtasks):在执行同步代码之后,JavaScript 引擎会检查并处理微任务队列中的任务。微任务通常包括 Promise 的回调函数、MutationObserver 回调等。微任务会在当前任务执行完毕之后立即执行。

  3. 处理宏任务(Macrotasks):在处理完微任务之后,JavaScript 引擎会检查并处理宏任务队列中的任务。宏任务包括定时器(setTimeout、setInterval)、事件回调(例如点击事件、网络请求等)等。宏任务会在下一次事件循环开始时行。

  4. 更新渲染(如果需要):如果当前任务是处理宏任务队列中的任务,并且需要进行页面渲染,那么会执行渲染操作。

  5. 重复循环:事件循环会不断重复以上步骤,不断处理微任务和宏任务,直到所有任务都被处理完毕。

设计模式#

单例模式(Singleton Pattern):
单例模式用于确保一个类只有一个实例,并提供一个全局访问点来获取该实例。它常用于管理全局状态或资源共享。在 JavaScript 中,可以使用对象字面量、模块模式或构造函数等方式实现单例模式。

工厂模式(Factory Pattern):
工厂模式用于创建对象的接口,隐藏了对象的具体实现细节。它通过定义一个共同的接口来创建对象,而不是直接使用 new 操作符实例化。工厂模式可以根据不同的条件返回不同的对象实例。

观察者模式(Observer Pattern):
观察者模式定义了一种一对多的依赖关系,当一个对象状态发生改变时,它的所有依赖对象都会收到通知并自动更新。在 JavaScript 中,可以使用事件机制、回调函数或使用第三方库(如 RxJS)来实现观察者模式。

发布-订阅模式(Publish-Subscribe Pattern):
发布-订阅模式是一种更松散的耦合方式,它通过定义一种消息系统,允许发送者(发布者)发布消息,而订阅者(订阅者)接收并处理这些消息。这种模式可以实现模块间的解耦和灵活的通信。

装饰者模式(Decorator Pattern):
装饰者模式用于动态地给对象添加额外的功能,而不需要修改其原始类。它通过创建装饰器对象,将其包装在原始对象周围,并在运行时添加新的行为或修改现有行为。

策略模式(Strategy Pattern):
策略模式定义了一组可以互相替换的算法,并使其能够独立于使用它的客户端进行变化。它允许在运行时动态地选择算法,并将其封装在可重用的策略对象中。

vue#

数据绑定原理#

基于 Object.defineProperty 的实现(Vue.js 2.x):

Vue.js 使用 Object.defineProperty 来劫持组件的数据对象,将其转化为响应式对象。通过这个方法,可以在访问属性时触发 getter,以及在修改属性时触发 setter。
在组件实例化过程中,Vue.js 遍历数据对象的所有属性,为每个属性创建一个 Dep(依赖)对象,用于收集依赖该属性的 Watcher(观察者)对象。
当访问响应式对象的属性时,会触发属性的 getter 方法,并在其中收集依赖,将当前的 Watcher 对象添加到 Dep 中。
当修改响应式对象的属性时,会触发属性的 setter 方法,并在其中通知 Dep 中的所有 Watcher 对象进行更新操作,更新相关的 DOM 元素。

基于 Proxy 的实现(Vue.js 3.x):

Vue.js 3.x 使用基于 Proxy 的响应式系统,它提供了更好的性能和更丰富的功能。
在组件实例化过程中,Vue.js 使用 Proxy 对象代理组件的数据对象。通过 Proxy,可以捕捉到对数据的访问和修改作。
当访问代理对象的属性时,会触发 Proxy 的 get 方法,并在其中收集依赖,将当前的 Watcher 对象添加到依赖中。
当修改代理对象的属性时,会触发 Proxy 的 set 方法,并在其中通知依赖中的所有 Watcher 对象进行更新操作,更新相关的 DOM 元素。

computed 原理#

  1. 定义 computed 属性:在 Vue 组件中,通过定义一个名为 computed 的对象,将需要计算的属性和对应的计算函数放在其中。
computed: {
  // 计算属性的定义
  result() {
    // 计算函数
    return this.a + this.b;
  }
}
  1. 计算函数的执行:当访问 computed 属性(例如 this.result)时,Vue.js 会触发计算函数的执行。

  2. 依赖追踪:在计算函数执行过程中,Vue.js 的响应式系统会自动追踪所依赖的属性,即将计算函数内部对其他响应式数据的访问建立起依赖关系。

  3. 建立关联:Vue.js 将依赖关系记录在当前计算属性的 Watcher 对象中,以便在依赖发生变化时触发计算属性的重新计算。

  4. 缓存计算结果:Vue.js 会缓存计算属性的计算结果,只有当依赖发生变化时才会重新计算。这样可以避免不必要的重复计算,提高性能。

响应式更新:当计算属性所依赖的属性发生变化时,计算属性会被标记为“脏”,并在下一次访问时重新计算。这个过程是自动的,不需要手动触发。

watch 原理#

定义 watch 属性:在 Vue 组件中,通过定义一个名为 watch 的对象,将需要观察的数据和对应的回调函数放在其中。

watch: {
  // 监听的数据
  value(newValue, oldValue) {
    // 回调函数
    // 在数据变化时执行相应的操作
  }
}
  1. 监听数据变化:当监听的数据(例如 value)发生变化时,Vue.js 会触发相应的变化通知。

  2. 建立关联:Vue.js 的响应式系统会将回调函数与监听的数据建立关联,在数据变化时调用相应的回调函数。

  3. 依赖追踪:在回调函数执行过程中,Vue.js 的响应式系统会自动追踪所依赖的其他响应式数据,即将回调函数内部对其他响应式数据的访问建立起依赖关系。

  4. 缓存旧值:在数据变化时,Vue.js 会将旧值(oldValue)缓存起来,以便在回调函数中使用。

  5. 执行回调函数:当监听的数据发生变化时,Vue.js 会调用相应的回调函数,并将新值(newValue)和旧值(oldValue)作为参数传递给回调函数。

通过 watch,我们可以在数据变化时执行一些自定义的操作,例如发送网络请求、更新其他数据、触发其他方法等。watch 提供了一种更灵活的数据监听方式,使得我们可以针对特定数据的变化进行相应的处理。

需要注意的是,watch 监听的数据可以是响应式的数据(如组件的 data、computed、props 等),也可以是非响应式的数据(如普通的变量)。对于非响应式的数据,Vue.js 使用了一种深度遍历的方法来监听其变化。

Vue.observable#

import Vue from 'vue';

const state = Vue.observable({
  count: 0
});

export default state;

--------------------------

<template>
  <div>
    <p>Count: {{ state.count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
import state from './state.js';

export default {
  methods: {
    increment() {
      state.count++;
    }
  }
};
</script>

vue eventBus#

// event-bus.js

import Vue from 'vue';

const bus = new Vue();

export default bus;

// ComponentA.vue

import EventBus from './event-bus.js';

export default {
  methods: {
    sendData() {
      EventBus.$emit('event-name', data);
    }
  }
}

// ComponentB.vue

import EventBus from './event-bus.js';

export default {
  created() {
    EventBus.$on('event-name', this.handleEvent);
  },
  methods: {
    handleEvent(data) {
      // 处理接收到的数据
    }
  },
  beforeDestroy() {
    EventBus.$off('event-name', this.handleEvent);
  }
}

生命周期#

在 Vue.js 中,组件具有生命周期钩子函数,这些钩子函数允许你在不同的阶段执行代码。以下是 Vue 组件的生命周期钩子函数:

beforeCreate:在实例被创建之前调用,此时组件的数据观测(data)和事件回调(methods)都尚未初始化。

created:在实例创建完成后调用,此时组件的数据观测(data)已经初始化,但 DOM 元素尚未生成,无法访问 $el。

beforeMount:在组件挂载到 DOM 之前调用,此时模板编译已完成,但尚未将组件挂载到 DOM 上。

mounted:在组件挂载到 DOM 后调用,此时组件已经被挂载到 DOM 上,可以访问到 $el。

beforeUpdate:在组件更新之前调用,当组件的数据发生变化时,会触发重新渲染,但此时尚未更新 DOM。

updated:在组件更新完成后调用,此时组件的数据已经更新,DOM 也已重新渲染。

beforeDestroy:在组件销毁之前调用,此时组件实例仍然完全可用。

destroyed:在组件销毁后调用,此时组件实例已被销毁,清理工作应该在这里进行。

activated:在使用组件时,被缓存的组件激活时调用。

deactivated:在使用组件时,被缓存的组件停用时调用。

这些生命周期钩子函数允许你在不同的阶段执行特定的操作,例如初始化数据、调用 API、监听事件、操作 DOM 等。你可以在组件中定义这些钩子函数作为方法,Vue.js 会在相应的阶段自动调用这些方法。

需要注意的是,Vue 3 中引入了 Composition API,其中使用了 setup() 函数作为组件的入口点,并且使用了一些新的生命周期函数(如 onMounted、onUpdated、onUnmounted 等)。这些新的生命周期函数与 Vue 2.x 中的生命周期钩子函数有所不同,但它们的作用和用法是类似的。

还新增了两个生命周期onRenderTrackedonRenderTriggered

onRenderTracked 是一个跟踪渲染过程的钩子函数。它接受两个参数:target 和 key。target 是被跟踪的响应式对象,key 是被访问的属性或索引。当组件渲染时,如果访问了响应式对象的属性或索引,onRenderTracked 将被触发。

以下是 onRenderTracked 的示例用法:

import { onRenderTracked } from "vue";

onRenderTracked((event) => {
  console.log(`Tracked: ${event.target} - ${event.key}`);
});

onRenderTriggered 是一个跟踪渲染触发的钩子函数。它接受两个参数:target 和 key。当组件渲染时,如果触发了响应式对象的属性或索引的更新,onRenderTriggered 将被触发。

以下是 onRenderTriggered 的示例用法:

import { onRenderTriggered } from "vue";

onRenderTriggered((event) => {
  console.log(`Triggered: ${event.target} - ${event.key}`);
});

这两个钩子函数在开发过程中可以用于性能分析和调试,帮助你了解组件渲染过程中的依赖关系和触发情况,以便进行优化和排查问题。请注意,这些钩子函数在生产环境中会被自动禁用,因此只能在开发环境中使用

父子组件的生命周期顺序:#

父组件:
beforeCreate
created
beforeMount
子组件的 beforeCreate
子组件的 created
子组件的 beforeMount
子组件的 mounted
父组件的 mounted

子组件:
beforeCreate
created
beforeMount
mounted

router#

原理:
Vue.js 提供了两种路由模式:hash 模式和 history 模式。在 hash 模式下,URL 中的路由路径会被格式化为 /#/your-path 的形式,而在 history 模式下,URL 中的路由路径会被直接展示为正常的路径形式,如 /your-path。

在 hash 模式下,Vue Router 的原理如下:#

初始化:当 Vue Router 注册并使用时,它会监听浏览器的 URL 变化事件。初始时,Vue Router 会解析当前 URL 中的 hash 部分,提取出路由路径,并根据配置的路由规则找到对应的组件。

路由匹配:当用户在页面中点击链接或手动修改 URL 中的 hash 时,浏览器会触发 hashchange 事件。Vue Router 会根据新的 hash 值解析出新的路由路径,并进行路由匹配,找到对应的组件。

组件渲染:一旦路由匹配成功,Vue Router 会将匹配到的组件渲染到指定的位置,通常是通过 <router-view> 组件来实现。这样就完成了对应路由路径的组件渲染。

路由切换:在 hash 模式下,当用户点击链接或手动修改 URL 中的 hash 时,浏览器会触发 hashchange 事件,Vue Router 会根据新的 hash 值进行路由切换。它会重新解析新的 hash 值,进行路由匹配,并将对应的组件渲染到指定位置。

URL 更新:在 hash 模式下,Vue Router 不会修改浏览器的 URL,而是通过改变 URL 中的 hash 值来实现路由切换。这样做是为了避免与服务端的路由冲突,因为 hash 部分不会被发送到服务端。

总结来说,在 hash 模式下,Vue Router 通过监听浏览器的 hashchange 事件,解析 URL 中的 hash 值,进行路由匹配和组件渲染,实现了前端的路由功能。用户通过点击链接或手动修改 URL 中的 hash 来触发路由切换,而不会与服务端的路由冲突。

在 Vue Router 中使用 history 模式时,URL 中的路由路径会以常规的路径形式出现,例如 /your-path。这种模式使用浏览器的 History API 来管理页面的路由状态。下面是 history 模式的原理:#

初始化:当 Vue Router 注册并使用时,它会监听浏览器的导航事件。初始时,Vue Router 会解析当前 URL 中的路径部分,提取出路由路径,并根据配置的路由规则找到对应的组件。

路由匹配:当用户在页面中点击链接或手动修改 URL 中的路径时,浏览器会触发 popstate 事件。Vue Router 会根据新的路径值解析出新的路由路径,并进行路由匹配,找到对应的组件。

组件渲染:一旦路由匹配成功,Vue Router 会将匹配到的组件渲染到指定的位置,通常是通过 <router-view> 组件来实现。这样就完成了对应路由路径的组件渲染。

路由切换:在 history 模式下,当用户点击链接或手动修改 URL 中的路径时,浏览器会触发 popstate 事件,Vue Router 会根据新的路径值进行路由切换。它会重新解析新的路径值,进行路由匹配,并将对应的组件渲染到指定位置。

URL 更新:在 history 模式下,Vue Router 使用 History API 来修改浏览器的 URL,而不是通过 hash 值的变化。它使用 pushState()replaceState() 方法来添加或替换历史记录条目,以更新 URL 中的路径。

需要注意的是,在使用 history 模式时,服务器需要进行相应的配置,以确保在刷新页面或直接访问带有路由路径的 URL 时,能正确地返回 Vue.js 应用程序的入口页面。这是为了避免服务器返回 404 错误。

总结来说,在 history 模式下,Vue Router 使用浏览器的 History API 来管理页面的路由状态。它通过监听 popstate 事件,解析 URL 中的路径值,进行路由匹配和组件渲染。用户通过点击链接或手动修改 URL 中的路径来触发路由切换,并使用 History API 修改浏览器的 URL。

用法:#

router.push()、router.replace()、router.go() 和 router.beforeEach() 等。

获取参数:
route.paramsroute.params 和route.query

动态路由#

{
  path: '/category/:categoryName/product/:productId',
  component: Product
}

* electron 不支持 history 模式#

原因:在目录里找不到 host/page_a/index.html 文件

动态组件:#

在 Vue 中,你可以使用元素来创建一个动态组件。元素的 is 特性可以绑定一个组件的名称或动态表达式,用于指定要渲染的组件。

示例:

<template>
  <div>
    <component :is="currentComponent"></component>
    <button @click="toggleComponent">Toggle Component</button>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  data() {
    return {
      currentComponent: 'ComponentA'
    };
  },
  methods: {
    toggleComponent() {
      this.currentComponent = this.currentComponent === 'ComponentA' ? 'ComponentB' : 'ComponentA';
    }
  },
  components: {
    ComponentA,
    ComponentB
  }
};
</script>

比 v-if 优点:

  1. 动态组件适用于在多个组件之间切换
  2. 动态组件可以提前渲染和缓存,在切换时缓存已经渲染的组件,避免了组件的销毁和重新创建,从而提高了性能

缺点:

  1. 动态组件需要提前导入并注册所有可能切换的组件,因此会在初始渲染时有一些额外的开销
  2. 动态组件在切换时会保留之前渲染的组件实例,因此会在内存中保留之前渲染的组件的状态和数据,可能会占用更多的内存

vuex#

State(状态):用于存储应用程序的状态数据。在 Vuex 中,我们将状态存储在一个单一的状态树中,即一个 JavaScript 对象。这个状态树可以被组件中的计算属性或方法访问。

Getters(获取器):用于从状态中派生出一些衍生数据,类似于组件中的计算属性。Getters 可以对状态进行处理和筛选,并且可以在组件中以属性的方式使用。

Mutations(变更):用于修改状态的唯一途径。Mutations 是同步操作,每个 Mutation 都有一个字符串类型的事件类型(type)和一个回调函数(handler)。通过提交一个 Mutation 来修改状态,确保状态的变更可被追踪。

Actions(动作):用于处理异步操作和复杂的业务逻辑。Actions 可以包含任意异步操作,例如发送网络请求、执行定时器等。Actions 提交 Mutations 来修改状态,可以通过分发一个 Action 来触发一系列的 Mutations。

Modules(模块):用于将大型的 Vuex 应用程序分割成多个模块,每个模块拥有自己的状态、获取器、变更和动作。模块之间可以相互通信和共享状态。