跳到主要内容

工具链

vue-router hash 模式和历史模式区别

路由守卫

路由守卫是 Vue Router 提供的一种机制,用于在路由导航过程中对路由进行拦截和控制。通过使用路由守卫,我们可以在路由导航前、导航后、导航中断等不同的阶段执行相应的逻辑。

Vue Router 提供了三种类型的路由守卫:

  1. 全局前置守卫(Global Before Guards):在路由切换之前被调用,可以用于进行全局的权限校验或者路由跳转拦截等操作。

  2. 路由独享守卫(Per-Route Guards):在特定的路由配置中定义的守卫。这些守卫只会在当前路由匹配成功时被调用。

  3. 组件内的守卫(In-Component Guards):在组件实例内部定义的守卫。这些守卫可以在组件内部对路由的变化进行相应的处理。

  • 全局前置守卫
router.beforeEach((to, from, next) => {
// to: 即将进入的目标
// from:当前导航正要离开的路由
return false // 返回false用于取消导航
// return { name: 'Login' } // 返回到对应name的页面
// next({ name: 'Login' }) // 进入到对应的页面
// next() // 放行
})
  • 全局解析守卫:类似beforeEach
router.beforeResolve(to => {
if (to.meta.canCopy) {
return false // 也可取消导航
}
})
  • 全局后置钩子
router.afterEach((to, from) => {
logInfo(to.fullPath)
})
  • 导航错误钩子,导航发生错误调用
router.onError(error => {
logError(error)
})
  • 路由独享守卫,beforeEnter可以传入单个函数,也可传入多个函数。
function dealParams (to) {
// ...
}
function dealPermission (to) {
// ...
}

const routes = [
{
path: '/home',
component: Home,
beforeEnter: (to, from) => {
return false // 取消导航
}
// beforeEnter: [dealParams, dealPermission]
}
]

组件内的守卫

const Home = {
template: '...',
beforeRouteEnter (to, from) {
// 此时组件实例还未被创建,不能获取this
},
beforeRouteUpdate (to, from) {
// 当前路由改变,但是组件被复用的时候调用,此时组件已挂载好
},
beforeRouteLeave (to, from) {
// 导航离开渲染组件的对应路由时调用
}
}

vuex 原理

vuex 和 Pinia 有什么区别

Vuex 和 Pinia 都是用于 Vue 应用程序的状态管理库,它们有一些相似之处,但也存在一些差异。以下是它们的对比:

一、相似之处

  1. 集中式状态管理
  • 两者都提供了一种集中式的方式来管理应用程序的状态。这使得状态可以在不同的组件之间共享,并且可以更容易地跟踪和调试状态的变化。
  • 例如,在一个电商应用中,用户的购物车状态可以存储在状态管理库中,以便在不同的页面和组件中访问和更新。
  1. 响应式状态
  • Vuex 和 Pinia 都与 Vue 的响应式系统集成,使得状态的变化可以自动触发相关组件的重新渲染。
  • 当购物车中的商品数量发生变化时,相关的组件可以自动更新以反映这个变化。

二、不同之处

  1. 语法和 API
  • Pinia

  • Pinia 提供了一种更加简洁和直观的 API。它使用类似于 Vue 组件的语法来定义状态和操作,使得代码更加易读和易于维护。

  • 例如,定义一个 store 可以像这样:

import { defineStore } from 'pinia'

export const useCartStore = defineStore('cart', {
state: () => ({
items: []
}),
actions: {
addItem (item) {
this.items.push(item)
}
}
})
  • Vuex

  • Vuex 的语法相对较为复杂,需要定义 mutations、actions 和 getters 等不同的概念来管理状态。

  • 例如,定义一个 store 可能如下所示:

import Vuex from 'vuex'

const store = new Vuex.Store({
state: {
items: []
},
mutations: {
ADD_ITEM (state, item) {
state.items.push(item)
}
},
actions: {
addItem ({ commit }, item) {
commit('ADD_ITEM', item)
}
},
getters: {
cartItems: (state) => state.items
}
})
  1. 模块系统
  • Pinia

  • Pinia 的模块系统更加灵活和易于使用。可以轻松地将 store 拆分为多个模块,并且可以在不同的模块之间共享状态和操作。

  • 例如,可以创建一个名为user的模块和一个名为cart的模块,并在它们之间共享一些状态和操作:

import { defineStore } from 'pinia'

const useUserStore = defineStore('user', {
// ...
})

const useCartStore = defineStore('cart', {
state: () => ({
// ...
}),
actions: {
addItem (item) {
// 可以访问 userStore 的状态
if (useUserStore().isLoggedIn) {
// ...
}
}
}
})
  • Vuex
  • Vuex 的模块系统也很强大,但相对来说更加复杂。需要使用命名空间来区分不同模块的 actions、mutations 和 getters,并且在模块之间共享状态和操作需要一些额外的配置。
  1. 类型支持
  • Pinia

  • Pinia 对 TypeScript 的支持非常好,可以轻松地为 store 定义类型,并且在开发过程中可以获得更好的类型提示和错误检查。

  • 例如,可以使用 TypeScript 来定义一个 store 的类型:

import { defineStore } from 'pinia'

interface CartItem {
id: number;
name: string;
price: number;
}

export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as CartItem[]
})
// ...
})
  • Vuex
  • Vuex 也支持 TypeScript,但相对来说需要一些额外的配置和类型定义文件来获得更好的类型支持。
  1. 开发体验
  • Pinia
  • Pinia 提供了一些开发工具,如 Pinia Devtools,可以方便地调试和检查 store 的状态和操作。它还与 Vue Devtools 集成,使得在开发过程中可以更好地跟踪状态的变化。
  • Pinia 的 API 更加简洁,使得开发过程更加高效和愉快。
  • Vuex
  • Vuex 也有一些开发工具,如 Vuex Devtools,但相对来说功能可能没有 Pinia Devtools 那么强大。
  • Vuex 的语法相对较为复杂,可能需要一些时间来适应和掌握。

总的来说,Pinia 和 Vuex 都是强大的状态管理库,选择哪一个取决于你的具体需求和个人偏好。如果你喜欢简洁和直观的 API,并且对 TypeScript 有较好的支持需求,那么 Pinia 可能是一个更好的选择。如果你已经熟悉 Vuex 并且对其功能和模块系统有特定的需求,那么 Vuex 也是一个可靠的选择。

Vuex redux

Redux和Vuex都是用于在前端应用中管理状态的JavaScript库。它们的设计思想都基于Flux架构,强调单向数据流的概念,以避免数据的混乱和不可预测的状态变化。

Redux的设计思想可以总结为三个原则:

  1. 单一数据源:Redux中所有的状态数据都保存在单一的store对象中,便于管理和维护。

  2. 状态只读:Redux的状态数据是只读的,唯一的改变方式是通过dispatch一个action来触发reducer函数对状态进行更新。

  3. 纯函数更新状态:Redux的reducer函数必须是纯函数,即接收一个旧的状态和一个action对象,返回一个新的状态。通过这种方式,Redux保证了状态的可控和可预测性。

Vuex的设计思想类似于Redux,但又有所不同:

  1. 单一数据源:Vuex也采用了单一数据源的思想,将所有状态保存在store对象中。

  2. 显示状态修改:和Redux不同的是,Vuex允许组件直接修改状态,但这必须是通过commit一个mutation来实现的,mutation也必须是同步的。

  3. 模块化:Vuex提供了模块化机制,可以将store对象分解成多个模块,以提高可维护性和代码复用性。

Redux和Vuex都是通过一些基本概念来实现状态管理:

  1. Store:保存状态的对象,整个应用只有一个Store。

  2. Action:描述状态变化的对象,由View层发起。

  3. Reducer:一个纯函数,接收旧的状态和一个Action对象,返回新的状态。

  4. Dispatch:一个函数,用来触发Action。

  5. Mutation:类似于Redux的Reducer,但必须是同步的。用来更新状态。

总之,Redux和Vuex都是优秀的状态管理库,通过它们可以有效地管理前端应用的状态,实现数据的单向流动和可预测性。同时,Redux和Vuex都遵循了Flux架构的设计思想,使得状态管理更加规范化和可控。

一、静态提升(Static Hoisting)

  1. 原理与作用:
  • 在 Vue 3 的编译阶段,会分析模板中的静态内容,将静态的节点提升到渲染函数之外。这意味着对于静态的元素和文本,不会在每次渲染时都重新创建虚拟 DOM 节点,而是在组件首次渲染时创建一次,然后在后续的渲染中直接复用。
  • 例如,如果一个组件的模板中有一个静态的标题文本,在 Vue 2 中,每次渲染都会为这个文本创建一个新的虚拟 DOM 节点。而在 Vue 3 中,这个静态文本会被提升,只在组件首次渲染时创建虚拟 DOM 节点,后续渲染直接使用已创建的节点。
  1. 性能提升:
  • 减少了虚拟 DOM 的创建和比较开销,特别是在组件频繁渲染时,效果尤为明显。因为静态内容通常不会改变,避免了不必要的重复操作,提高了渲染性能。

二、补丁算法优化

  1. 更高效的比较策略:
  • Vue 3 改进了虚拟 DOM 的补丁算法,能够更快速地找到新旧虚拟 DOM 树之间的差异。新的算法在比较节点时更加智能,能够准确判断节点的类型和属性变化,只对真正发生变化的节点进行更新操作。
  • 例如,当一个列表中的某个元素的文本内容发生变化时,Vue 3 能够快速定位到这个变化的节点,而不会像 Vue 2 那样对整个列表进行逐一比较。
  1. 减少不必要的操作:
  • 通过更精确的比较,Vue 3 避免了一些不必要的 DOM 操作。例如,如果一个元素的样式属性没有发生变化,Vue 3 不会触发样式的重新设置,从而减少了对浏览器渲染引擎的压力。

三、事件处理优化

  1. 静态事件提升:
  • 对于静态的事件监听器,Vue 3 也会在编译阶段进行提升。如果一个组件的模板中有一个静态的点击事件监听器,在 Vue 3 中,这个事件监听器会被提取出来,在组件首次渲染时进行绑定,后续渲染中不再重复绑定。
  • 例如,一个按钮的点击事件在组件的整个生命周期中都不会改变,那么在 Vue 3 中,这个事件监听器只会在首次渲染时绑定一次,提高了性能。
  1. 事件缓存:
  • Vue 3 还对事件进行了缓存处理。当一个组件被销毁时,它的事件监听器不会立即被移除,而是被缓存起来。如果这个组件在后续的渲染中再次出现,Vue 3 可以直接复用缓存的事件监听器,而不需要重新绑定,进一步提高了性能。

四、编译器优化

  1. 更好的代码生成:
  • Vue 3 的编译器能够生成更高效的渲染函数代码。通过对模板的分析和优化,编译器可以生成更加简洁、高效的虚拟 DOM 创建和更新代码。
  • 例如,编译器可以根据模板中的条件判断和循环结构,生成更加优化的虚拟 DOM 操作代码,减少不必要的计算和操作。
  1. 类型推断:
  • Vue 3 的编译器支持类型推断,这使得在开发过程中可以获得更好的类型提示和错误检查。同时,类型推断也可以帮助编译器生成更加高效的代码,因为编译器可以根据类型信息进行一些优化操作。
  • 例如,如果一个组件的 props 定义了明确的类型,编译器可以在生成虚拟 DOM 代码时,针对这些类型进行优化,提高代码的执行效率。

Vue 3 仍然使用虚拟 DOM(Virtual DOM)。

一、虚拟 DOM 在 Vue 3 中的重要性

  1. 高效的 DOM 操作:虚拟 DOM 是一种在内存中表示真实 DOM 结构的树形数据结构。在 Vue 3 中,当数据发生变化时,首先会在虚拟 DOM 上进行比较和计算,确定最小化的 DOM 操作集合,然后再将这些操作应用到真实 DOM 上。这样可以避免直接频繁地操作真实 DOM,从而提高性能。
  • 例如,当一个组件中的数据发生变化时,Vue 3 会先更新虚拟 DOM 树,然后通过对比新旧虚拟 DOM 树的差异,找出需要更新的真实 DOM 节点,最后只对这些节点进行实际的 DOM 操作。
  1. 跨平台开发支持:虚拟 DOM 使得 Vue 3 不仅可以在浏览器中运行,还可以通过一些工具和技术进行跨平台开发。例如,使用 Vue Native 可以将 Vue 3 应用编译为原生移动应用,在移动平台上运行。这是因为虚拟 DOM 可以在不同的平台上进行渲染,而不需要依赖特定平台的 DOM 操作。
  • 比如,在开发一个同时支持 Web 和移动平台的应用时,可以使用 Vue 3 的虚拟 DOM 来实现一套代码在多个平台上的运行,提高开发效率和代码复用性。

二、Vue 3 对虚拟 DOM 的优化

  1. 静态提升(Static Hoisting):Vue 3 在编译阶段会分析组件的模板,将静态的节点提升到渲染函数之外。这样在每次渲染时,不需要为静态节点创建新的虚拟 DOM 节点,从而减少了虚拟 DOM 的创建和比较开销。
  • 例如,如果一个组件的模板中有一些静态的文本节点或元素,Vue 3 会在编译时将这些静态节点提取出来,在渲染时直接复用,而不是每次都重新创建虚拟 DOM 节点。
  1. 补丁算法优化:Vue 3 对虚拟 DOM 的补丁算法进行了优化,使得在更新 DOM 时更加高效。新的补丁算法可以更快地找到需要更新的节点,减少不必要的比较和操作。
  • 比如,在对比新旧虚拟 DOM 树时,Vue 3 可以更准确地判断节点的类型和属性变化,只对真正发生变化的节点进行更新,提高了渲染性能。
  1. 事件处理优化:在 Vue 3 中,事件处理也进行了优化。对于静态的事件监听器,同样会在编译阶段进行提升,减少了每次渲染时的创建和绑定开销。
  • 例如,如果一个组件中有一个静态的点击事件监听器,Vue 3 会在编译时将这个事件监听器提取出来,在渲染时直接复用,而不是每次都重新绑定。

scope 是怎么做的样式隔离的

Vue 中的样式隔离是通过 Vue 单文件组件(Single File Components,简称 SFC)的 <style> 标签中的 scoped 属性实现的。当你在一个 Vue 组件的 <style> 标签上添加 scoped 属性时,Vue 会自动将该样式限定在当前组件的范围内,从而防止样式冲突和不必要的样式泄漏。

实现原理

Vue 在编译带有 scoped 属性的 <style> 标签时,会按照以下步骤处理样式隔离:

  1. 生成唯一的作用域 ID:Vue 为每个带有 scoped 属性的组件生成一个唯一的作用域 ID(如 data-v-f3f3eg9)。这个 ID 是随机的,确保每个组件的作用域 ID 是独一无二的。

  2. 添加作用域 ID 到模板元素:Vue 会在编译组件模板的过程中,将这个作用域 ID 作为自定义属性添加到组件模板的所有元素上。例如,如果作用域 ID 是 data-v-f3f3eg9,那么在该组件模板的所有元素上都会添加一个属性 data-v-f3f3eg9

  3. 修改 CSS 选择器:对于组件内部的每个 CSS 规则,Vue 会自动转换其选择器,使其仅匹配带有对应作用域 ID 的元素。这是通过在 CSS 选择器的末尾添加相应的作用域 ID 属性选择器来实现的。例如,如果 CSS 规则是 .button { color: red; },并且作用域 ID 是 data-v-f3f3eg9,那么该规则会被转换成 .button[data-v-f3f3eg9] { color: red; }

示例

假设有如下 Vue 单文件组件:

<template>
<button class="btn">Click Me</button>
</template>

<style scoped>
.btn {
background-color: red;
}
</style>

编译后,CSS 规则会变成类似于这样(注意:实际的作用域 ID 是随机生成的):

.btn[data-v-f3f3eg9] {
background-color: red;
}

并且模板里的 <button> 元素会被编译为类似这样:

<button class="btn" data-v-f3f3eg9>Click Me</button>

这样,.btn 样式规则只会应用到当前组件中的 <button> 元素上,而不会影响到其他组件中的同类元素,实现了样式隔离。

注意事项

  • 由于样式隔离是通过属性选择器和自定义属性实现的,因此这种方法的性能可能会略低于全局样式规则。
  • scoped 样式不能影响子组件,仅限于当前的组件。如果需要影响子组件,则需要使用深度选择器(>>>/deep/)。
  • 其他 Web 组件技术如 Shadow DOM 也可以提供样式隔离的功能,但 Vue 选择了这种不需要 polyfill、兼容性更好的实现方式。

在 Vue 中,.vue 单文件组件的 <style> 标签可以添加一个 scoped 属性来实现样式的隔离。通过这个 scoped 属性,Vue 会确保样式只应用到当前组件的模板中,而不会泄漏到外部的其他组件中。

这个效果是通过 PostCSS 在构建过程中对 CSS 进行转换来实现的。基本原理如下:

Scoped Styles 的工作原理

  1. 当你为 <style> 标签添加 scoped 属性时,Vue 的加载器(比如 vue-loader)会处理你的组件文件。

  2. vue-loader 使用 PostCSS 来处理 scoped 的 CSS。它为组件模板内的每个元素添加一个独特的属性(如 data-v-f3f3eg9)。这个属性是随机生成的,确保唯一性(是在 Vue 项目构建过程中的 hash 值)。

  3. 同时,所有的 CSS 规则都会被更新,以仅匹配带有相应属性选择器的元素。例如:如果你有一个 .button 类的样式规则,它会被转换成类似 .button[data-v-f3f3eg9] 的形式。这确保了样式只会被应用到拥有对应属性的 DOM 元素上。

示例

假设你在组件 MyComponent.vue 内写了如下代码:

<template>
<button class="btn">Click Me</button>
</template>

<style scoped>
.btn {
background-color: blue;
}
</style>

vue-loader 将处理上述代码,模板中的 <button> 可能会渲染成类似下面的 HTML:

<button class="btn" data-v-f3f3eg9>Click Me</button>

CSS 则会被转换成:

.btn[data-v-f3f3eg9] {
background-color: blue;
}

因此,.btn 类的样式仅会应用于拥有 data-v-f3f3eg9 属性的 <button> 元素上。

注意

  • Scoped styles 提供了样式封装,但不是绝对的隔离。子组件的根节点仍然会受到父组件的 scoped CSS 的影响。在子组件中使用 scoped 可以避免这种情况。
  • Scoped CSS 不防止全局样式影响组件。如果其他地方定义了全局样式,它们仍然会应用到组件中。
  • 当使用外部库的类名时,scoped 可能会导致样式不被应用,因为它会期望所有匹配规则的元素都带有特定的属性。

总的来说,Scoped Styles 是 Vue 单文件组件提供的一种方便且有效的样式封装方式,通过 PostCSS 转换和属性选择器来实现组件之间的样式隔离。

异常处理机制有哪些

Vue的错误处理机制主要包括以下几个方面:

  1. Error Capturing(错误捕获):Vue提供了全局错误处理的钩子函数errorCaptured,可以在组件层级中捕获子组件产生的错误。通过在父组件中使用errorCaptured钩子函数,可以捕获子组件中的错误,并对其进行处理或展示错误信息。

  2. Error Boundary(错误边界):Vue 2.x中没有内置的错误边界机制,但你可以通过自定义组件来实现。错误边界是一种特殊的组件,它可以捕获并处理其子组件中的错误。错误边界组件使用errorCaptured钩子函数来捕获子组件中的错误,并使用v-ifv-show等指令来显示错误信息或替代内容。

3.异常处理:在Vue组件的生命周期钩子函数中,可以使用try-catch语句捕获并处理可能出现的异常。例如,在mounted钩子函数中进行接口请求,可以使用try-catch来捕获请求过程中的异常,并进行相应的处理。

  1. 错误提示和日志记录:在开发环境中,Vue会在浏览器的控制台中输出错误信息,以方便开发者进行调试。在生产环境中,可以使用日志记录工具(如Sentry)来记录错误信息,以便及时发现和解决问题。

代码举例

以下是使用代码举例说明以上四种Vue错误处理方式的示例:

  1. Error Capturing(错误捕获):
// ParentComponent.vue
<template>
<div>
<ChildComponent />
<div v-if="error">{{ error }}</div>
</div>
</template>

<script>
export default {
data() {
return {
error: null
};
},
errorCaptured(err, vm, info) {
this.error = err.toString(); // 将错误信息存储在父组件的data中
return false; // 阻止错误继续向上传播
}
};
</script>
  1. Error Boundary(错误边界):
// ErrorBoundary.vue
<template>
<div v-if="hasError">
Oops, something went wrong.
<button @click="resetError">Retry</button>
</div>
<div v-else>
<slot></slot>
</div>
</template>

<script>
export default {
data() {
return {
hasError: false
};
},
errorCaptured() {
this.hasError = true;
return false;
},
methods: {
resetError() {
this.hasError = false;
}
}
};
</script>

// ParentComponent.vue
<template>
<div>
<ErrorBoundary>
<ChildComponent />
</ErrorBoundary>
</div>
</template>
  1. 异常处理:
// ChildComponent.vue
<template>
<div>{{ data }}</div>
</template>

<script>
export default {
data() {
return {
data: null
};
},
mounted() {
try {
// 模拟接口请求
const response = await fetch('/api/data');
this.data = await response.json();
} catch (error) {
console.error(error); // 处理异常,输出错误信息
}
}
};
</script>
  1. 错误提示和日志记录:
// main.js
import Vue from 'vue'
import Sentry from '@sentry/browser'

Vue.config.errorHandler = (err) => {
console.error(err) // 错误提示
Sentry.captureException(err) // 错误日志记录
}

new Vue({
// ...
}).$mount('#app')

上述代码中,Error Capturing通过在父组件中的errorCaptured钩子函数中捕获子组件的错误,并展示在父组件中。Error Boundary通过自定义错误边界组件,在子组件发生错误时展示错误信息或替代内容。异常处理通过在子组件的生命周期钩子函数中使用try-catch语句来捕获异常并进行处理。错误提示和日志记录通过在Vue.config.errorHandler中定义全局的错误处理函数,将错误信息输出到控制台,并使用Sentry等工具记录错误日志。

这些示例展示了不同的错误处理方式,可以根据实际需求选择合适的方式来处理Vue应用中的错误。

3.x 中 app.config 有哪些应用配置?

确实,在 Vue 3 中,app.config 提供了一系列的应用级别的配置选项,用于自定义或调整 Vue 应用的行为。你提到的这些配置项都是 app.config 的一部分,下面是关于它们的详细介绍:

app.config.errorHandler

  • 作用:为未捕获的异常定义一个全局的处理函数。这在集中处理组件渲染或观察者(watchers)中的异常时非常有用。
  • 示例
// app.config.errorHandler = (err, instance, info) => {
// // 处理错误
// console.log('nop')
// }

app.config.warnHandler

  • 作用:为 Vue 运行时警告定义一个全局的处理函数,允许你在开发过程中自定义处理警告的方式。
  • 示例
app.config.warnHandler = (msg, instance, trace) => {
// 处理警告
}

app.config.performance

  • 作用:开启性能追踪。在开发模式下启用,能够测量和追踪组件的初始化、编译时间等性能指标。
  • 示例
app.config.performance = true

app.config.compilerOptions

  • 作用:允许自定义编译器选项,如模板中的自定义指令等。这对于更细致地控制模板的编译过程很有帮助。
  • 示例
app.config.compilerOptions = {
// 编译器配置
}

app.config.globalProperties

  • 作用:定义全局可用的属性。这在 Vue 2 中通过 Vue.prototype 实现,Vue 3 中通过 app.config.globalProperties 实现。
  • 示例
app.config.globalProperties.$utils = {
// 一些全局方法或属性
}

app.config.optionMergeStrategies

  • 作用:自定义选项的合并策略。允许你为自定义选项指定如何合并父子选项。
  • 示例
app.config.optionMergeStrategies.myOption = (parent, child) => {
// 合并策略
}

app.config.idPrefix

  • 作用:配置此应用中通过 useId() 生成的所有 ID 的前缀。由 3.5+ 版本引入。
  • 示例
app.config.idPrefix = 'custom-'

// 在组件中:
const id1 = useId() // 'my-app:0'
const id2 = useId() // 'my-app:1'

app.config.throwUnhandledErrorInProduction

  • 作用:强制在生产模式下抛出未处理的错误。 由 3.5+ 版本引入。

默认情况下,在 Vue 应用中抛出但未显式处理的错误在开发和生产模式下有不同的行为:

在开发模式下,错误会被抛出并可能导致应用崩溃。这是为了使错误更加突出,以便在开发过程中被注意到并修复。

在生产模式下,错误只会被记录到控制台以尽量减少对最终用户的影响。然而,这可能会导致只在生产中发生的错误无法被错误监控服务捕获。

通过将 app.config.throwUnhandledErrorInProduction 设置为 true,即使在生产模式下也会抛出未处理的错误。

这些应用级配置选项提供了对 Vue 应用的高度控制,允许开发者根据实际需要调整 Vue 的默认行为。在使用时,建议根据项目实际情况和需求进行选择性地配置。

实现一个简单的 i18n (国际化 (Internationalization) 的缩写) 插件

实现下面的这样的一个插件 <h1>{{ $translate('greetings.hello') }}</h1>

以下是一个简单的 Vue 3 的国际化插件实现:

  1. 创建一个名为i18nPlugin.js的文件:
const i18nPlugin = {
install (app, options) {
const translations = options.translations
app.config.globalProperties.$translate = (key) => {
const parts = key.split('.')
let value = translations[parts[0]]
for (let i = 1; i < parts.length && value; i++) {
value = value[parts[i]]
}
return value || key
}
}
}

export default i18nPlugin
  1. 在你的 Vue 3 项目中使用这个插件:

假设你有以下的语言翻译对象:

// en.js
const enTranslations = {
greetings: {
hello: 'Hello!'
}
}

// export default enTranslations

// zh.js
const zhTranslations = {
greetings: {
hello: '你好!'
}
}

// export default zhTranslations

在项目的入口文件(通常是main.jsmain.ts)中:

import { createApp } from 'vue'
import App from './App.vue'
import enTranslations from './locales/en'
import i18nPlugin from './i18nPlugin'

const app = createApp(App)

app.use(i18nPlugin, { translations: enTranslations })

app.mount('#app')

这样,在你的组件中就可以使用{{ $translate('greetings.hello') }}来获取翻译后的文本,并且可以通过修改传入插件的翻译对象来切换不同的语言。

插件是什么概念,有什么作用,该怎么用

在 Vue 中,插件是一种用于增强 Vue 功能的工具。

一、概念

Vue 插件是一个包含install方法的对象,或者是一个函数,这个函数接收 Vue 的构造函数作为参数。插件可以为 Vue 添加全局的功能,如全局组件、全局指令、全局过滤器、全局混入(mixin)等,也可以扩展 Vue 的实例方法或原型属性。

二、作用

  1. 添加全局功能
  • 全局组件:可以通过插件注册全局组件,使得在整个项目的任何组件中都可以直接使用这个组件,无需在每个组件中单独引入。例如,一个弹窗组件可以作为插件注册为全局组件,方便在项目中的各个地方弹出统一风格的弹窗。
  • 全局指令:插件可以添加全局指令,用于在模板中对元素进行特定的操作。比如,一个v-focus指令可以在元素挂载时自动聚焦该元素,提高用户体验。
  • 全局过滤器:用于对数据进行格式化处理。例如,一个全局过滤器可以将日期格式化为特定的字符串格式,方便在模板中显示日期数据。
  • 全局混入:可以在多个组件之间共享一些通用的选项或方法。例如,一个全局混入可以为多个组件添加相同的生命周期钩子函数或方法,减少重复代码。
  1. 扩展 Vue 实例
  • 插件可以向 Vue 实例添加新的方法或属性,使得在项目中的任何地方都可以通过this访问这些方法或属性。例如,一个插件可以添加一个$http方法,用于发送 HTTP 请求,方便在组件中进行数据获取。
  1. 集成第三方库
  • 可以将第三方库包装成 Vue 插件,使其与 Vue 更好地集成。例如,将 Vue Router(路由库)和 Vuex(状态管理库)作为插件使用,方便在 Vue 项目中进行路由管理和状态管理。

三、使用方法

  1. 创建插件
  • 插件可以是一个对象,包含install方法:
const myPlugin = {
install (Vue) {
// 在这里添加全局功能或扩展 Vue 实例
Vue.prototype.$myMethod = function () {
console.log('This is a custom method added by the plugin.')
}
}
}
  • 也可以是一个函数,接收 Vue 构造函数作为参数:
function myPlugin (Vue) {
Vue.prototype.$myMethod = function () {
console.log('This is a custom method added by the plugin.')
}
}
  1. 使用插件
  • 在 Vue 项目中,可以通过Vue.use()方法来使用插件。通常在项目的入口文件(如main.js)中进行插件的安装。
import Vue from 'vue'
import App from './App.vue'
// 引入插件
import myPlugin from './myPlugin'

Vue.use(myPlugin)

new Vue({
render: (h) => h(App)
}).$mount('#app')
  1. 在组件中使用插件提供的功能
  • 在组件的方法、生命周期钩子函数或模板中,可以通过this.$myMethod()来调用插件添加的方法。
<template>
<div @click="callPluginMethod">Click me</div>
</template>
<script>
export default {
methods: {
callPluginMethod() {
this.$myMethod();
},
},
};
</script>

通过使用插件,可以将一些通用的功能封装起来,提高代码的可维护性和可复用性,同时也方便在项目中进行功能的扩展和集成第三方库。

vue3 相比较于 vue2 在编译阶段有哪些改进

Vue 3 在编译阶段相对于 Vue 2 进行了一些重要的改进,主要包括以下几个方面:

  1. 静态模板提升(Static Template Hoisting):Vue 3 引入了静态模板提升技术,通过对模板进行分析和优化,将模板编译为更简洁、更高效的渲染函数。这种优化可以减少不必要的运行时开销,并提高组件的渲染性能。

  2. Fragments 片段支持:Vue 3 支持使用 Fragments 片段来包裹多个根级元素,而不需要额外的父元素。这样可以避免在编译阶段为每个组件生成额外的包裹元素,减少了虚拟 DOM 树的层级,提高了渲染性能。

  3. 静态属性提升(Static Props Hoisting):Vue 3 在编译阶段对静态属性进行了优化,将静态属性从渲染函数中提取出来,只在组件初始化时计算一次,并在后续的渲染中重用。这样可以减少不必要的属性计算和传递,提高了组件的渲染性能。

  4. 事件处理函数的内联化:Vue 3 在编译阶段对事件处理函数进行了内联化,将事件处理函数直接写入模板中,而不是在运行时动态生成。这样可以减少运行时的事件绑定和查找开销,提高了事件处理的性能。

  5. 静态节点提升(Static Node Hoisting):Vue 3 通过静态节点提升技术,将静态的节点在编译阶段进行处理,避免了在每次渲染时对静态节点的比对和更新操作,提高了渲染性能。

  6. 缓存事件处理器(Cached Event Handlers):Vue 3 在编译阶段对事件处理器进行了缓存,避免了重复创建事件处理函数的开销。对于相同的事件处理器,只会创建一次,并在组件的生命周期中重复使用,减少了内存占用和运行时开销。

  7. 更细粒度的组件分割(Fine-Grained Component Splitting):Vue 3 支持更细粒度的组件分割,可以将组件的模板、脚本和样式进行独立的编译和加载,以实现更好的代码拆分和按需加载,提高了应用的加载速度和性能。

这些改进使得 Vue 3 在编译阶段能够生成更优化的代码,减少了不必要的运行时开销,并提高了组件的渲染性能和整体的运行效率。

你做过哪些性能优化

1、v-ifv-show

  • 频繁切换时使用v-show,利用其缓存特性
  • 首屏渲染时使用v-if,如果为false则不进行渲染

2、v-forkey

  • 列表变化时,循环时使用唯一不变的key,借助其本地复用策略
  • 列表只进行一次渲染时,key可以采用循环的index

3、侦听器和计算属性

  • 侦听器watch用于数据变化时引起其他行为
  • 多使用compouter计算属性顾名思义就是新计算而来的属性,如果依赖的数据未发生变化,不会触发重新计算

4、合理使用生命周期

  • destroyed阶段进行绑定事件或者定时器的销毁
  • 使用动态组件的时候通过keep-alive包裹进行缓存处理,相关的操作可以在actived阶段激活

5、数据响应式处理

  • 不需要响应式处理的数据可以通过Object.freeze处理,或者直接通过this.xxx = xxx的方式进行定义
  • 需要响应式处理的属性可以通过this.$set的方式处理,而不是JSON.parse(JSON.stringify(XXX))的方式

6、路由加载方式

  • 页面组件可以采用异步加载的方式

7、插件引入

  • 第三方插件可以采用按需加载的方式,比如element-ui

8、减少代码量

  • 采用mixin的方式抽离公共方法
  • 抽离公共组件
  • 定义公共方法至公共js
  • 抽离公共css

9、编译方式

  • 如果线上需要template的编译,可以采用完成版vue.esm.js
  • 如果线上无需template的编译,可采用运行时版本vue.runtime.esm.js,相比完整版体积要小大约30%

10、渲染方式

  • 服务端渲染,如果是需要SEO的网站可以采用服务端渲染的方式
  • 前端渲染,一些企业内部使用的后端管理系统可以采用前端渲染的方式

11、字体图标的使用

  • 有些图片图标尽可能使用字体图标
22%