04 Vue

60 阅读7分钟

vue 的路由传参方式

在 Vue.js 中,路由传参主要有两种方式:通过路径参数和通过查询参数。

路径参数: 路径参数是 URL 的一部分,通常用于标识特定的资源。例如:

// 定义路由 const routes = [ { path: '/user/:id', component: User } ] //
在组件中获取参数 export default { created() { console.log(this.$route.params.id)
} }

查询参数: 查询参数是 URL 中 ? 后面的键值对,通常用于传递非必要的参数。例如:

// 定义路由 const routes = [ { path: '/search', component: Search } ] //
在组件中获取参数 export default { created() {
console.log(this.$route.query.keyword) } }

编程式导航传参: 可以使用 this.$router.pushthis.$router.replace 方法进行编程式导航并传递参数。例如:

// 传递路径参数 this.$router.push({ name: 'user', params: { id: 123 } }) //
传递查询参数 this.$router.push({ path: '/search', query: { keyword: 'vue' } })

通过以上方式,可以在 Vue.js 中实现路由传参,并在组件中获取相应的参数。

vue 响应式原理以及双向绑定实现代码

Vue.js 的响应式原理是通过数据劫持和发布-订阅模式来实现的。核心是 Object.defineProperty 方法,通过它来劫持对象的属性,并在属性被访问或修改时进行相应的操作。

响应式原理:

  1. 数据劫持:通过 Object.defineProperty 劫持对象的属性,拦截属性的读取和设置操作。
  2. 发布-订阅模式:当数据变化时,通知所有依赖该数据的订阅者进行更新。

通过以上代码,可以实现一个简单的响应式系统,当 state.count 变化时,会自动触发依赖该数据的副作用函数进行更新。

双向绑定是通过 v-model 指令实现的,它本质上是语法糖,封装了 valueinput 事件的绑定。例如:

v-model 会自动绑定 input 元素的 value 属性和 input 事件,实现数据的双向绑定。

vue3 响应式原理,有什么不同

Vue 3 的响应式系统相比 Vue 2 有了显著的改进,主要体现在以下几个方面:

  1. Proxy 代替 Object.defineProperty: Vue 3 使用 Proxy 对象代替了 Vue 2 中的 Object.definePropertyProxy 可以直接监听对象和数组的变化,并且可以监听动态添加的属性,这解决了 Vue 2 无法检测数组和对象新增属性的问题。

  2. 更好的性能: 由于 Proxy 的使用,Vue 3 的响应式系统在性能上有了显著提升。Proxy 可以直接监听整个对象,而不需要递归地遍历每个属性,这使得响应式系统的初始化速度更快。

  3. 更灵活的 API: Vue 3 提供了 reactiveref 两种 API 来创建响应式数据。reactive 用于创建深层次的响应式对象,而 ref 用于创建基本类型的响应式数据。这使得开发者可以根据具体需求选择合适的 API。

  4. 更好的 TypeScript 支持: Vue 3 的响应式系统对 TypeScript 有更好的支持,类型推断更加准确,使得在 TypeScript 项目中使用 Vue 3 更加方便。

通过以上改进,Vue 3 的响应式系统在性能、灵活性和开发体验上都有了显著提升。

Vue2如何做数组的监听

在 Vue 2 中,由于 Object.defineProperty 的限制,无法直接监听数组的变化。为了实现对数组的监听,Vue 2 对数组的方法进行了重写,拦截了数组的变更操作。以下是 Vue 2 中监听数组变化的几种方式:

  1. 重写数组方法: Vue 2 重写了数组的变更方法,如 pushpopshiftunshiftsplicesortreverse,在这些方法被调用时,会触发视图更新。
const vm = new Vue({
  data: {
   items: [1, 2, 3]
  }
})

// 触发视图更新
vm.items.push(4)
  1. 使用 Vue.set 方法: 当需要向数组中添加新元素时,可以使用 Vue.set 方法,这样可以确保新元素是响应式的。
Vue.set(vm.items, index, newValue)
  1. 使用 splice 方法: 通过 splice 方法可以在数组中插入或删除元素,并且会触发视图更新。
vm.items.splice(index, 0, newValue) // 插入新元素
vm.items.splice(index, 1) // 删除元素

通过以上方式,可以在 Vue 2 中实现对数组的监听,并确保数组的变更能够触发视图更新。

Vue3实现数组的监听的原理

在 Vue 3 中,数组的监听是通过 Proxy 对象来实现的。Proxy 可以直接监听数组的变化,并且可以监听动态添加的属性,这解决了 Vue 2 无法检测数组和对象新增属性的问题。

实现原理:

  1. Proxy 对象: Vue 3 使用 Proxy 对象来代理数组,通过 Proxy 可以拦截数组的各种操作,如读取、设置、删除等。

  2. 捕获器(Traps)Proxy 提供了多种捕获器(traps),如 getsetdeleteProperty 等,这些捕获器可以在数组操作时执行特定的逻辑。

  3. 响应式系统: 当数组发生变化时,Vue 3 的响应式系统会通知所有依赖该数组的订阅者进行更新,从而实现视图的自动更新。

示例代码

import { reactive } from 'vue'

const state = reactive({
  items: [1, 2, 3]
})

// 触发视图更新
state.items.push(4)

通过以上方式,Vue 3 可以高效地监听数组的变化,并确保数组的变更能够触发视图更新。

vue 的 props,data,computed,watch,生命周期钩子函数生效的先后顺序

在 Vue.js 中,组件的各个属性和生命周期钩子函数的生效顺序如下:

  1. props:首先初始化 props,从父组件传递的属性。
  2. data:然后初始化 data,组件自身的状态数据。
  3. computed:接着初始化 computed 属性,基于 dataprops 计算得出的属性。
  4. watch:初始化 watch 选项,监听 dataprops 的变化。
  5. 生命周期钩子函数
  • beforeCreate:实例初始化之后,数据观测和事件配置之前调用。
  • created:实例创建完成后调用,此时可以访问数据和事件,但组件还未挂载。
  • beforeMount:在挂载开始之前调用,相关的 render 函数首次被调用。
  • mounted:实例被挂载后调用,此时 el 被新创建的 vm.$el 替换。
  • beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
  • updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。
  • beforeDestroy:实例销毁之前调用,此时实例仍然完全可用。
  • destroyed:实例销毁后调用,调用后,Vue 实例指示的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

通过以上顺序,可以更好地理解 Vue 组件的初始化和更新过程。

vue3 对于 vue2 在性能上的优化(从 compile 和 runtime 两方面)

Vue 3 在性能上相较于 Vue 2 有了显著的优化,主要体现在编译(compile)和运行时(runtime)两个方面:

编译方面的优化:

  1. 模板编译优化
  • Vue 3 的编译器在编译模板时会进行更多的静态分析,将静态节点和动态节点分开处理,减少运行时的开销。
  • 编译器会标记静态节点,使得这些节点在后续的渲染过程中不需要重新计算和更新,从而提升渲染性能。
  1. Tree-shaking 支持
  • Vue 3 的编译器支持 Tree-shaking,可以在打包时移除未使用的代码,减小打包体积,提高加载速度。

运行时的优化:

  1. Proxy 代替 Object.defineProperty
  • Vue 3 使用 Proxy 对象代替了 Vue 2 中的 Object.definePropertyProxy 可以直接监听对象和数组的变化,并且可以监听动态添加的属性,这解决了 Vue 2 无法检测数组和对象新增属性的问题。
  • Proxy 的使用使得响应式系统的性能有了显著提升,初始化速度更快,内存占用更低。
  1. 更高效的虚拟 DOM
  • Vue 3 对虚拟 DOM 进行了重写,采用了基于 Block Tree 的优化策略,可以更高效地进行节点的更新和对比。
  • 通过静态提升和缓存事件处理函数等手段,减少了不必要的重新渲染和计算,提高了运行时的性能。
  1. 更小的打包体积
  • Vue 3 的核心库经过优化,移除了不常用的功能,使得打包体积更小,加载速度更快。
  • 通过模块化设计,可以按需引入需要的功能,进一步减小打包体积。
  1. Composition API
  • Vue 3 引入了 Composition API,使得逻辑复用更加灵活,减少了代码冗余,提高了代码的可维护性和性能。

通过以上优化,Vue 3 在编译和运行时的性能上都有了显著提升,使得开发者可以构建更高效、更快速的应用。

vue 的 diff 算法思路,怎么对比节点

Vue 的 diff 算法是基于虚拟 DOM 的,它通过对比新旧虚拟 DOM 树,找出需要更新的部分,从而高效地更新真实 DOM。其核心思路如下:

  1. 同层比较:Vue 的 diff 算法只比较同一层级的节点,不会跨层级比较。这样可以减少比较次数,提高性能。

  2. 双端比较:Vue 采用双端比较算法,同时从新旧虚拟 DOM 的两端开始比较,直到两端相遇。具体步骤如下:

  • 比较新旧虚拟 DOM 的头部节点,如果相同,则继续比较下一个节点。
  • 比较新旧虚拟 DOM 的尾部节点,如果相同,则继续比较前一个节点。
  • 如果头部和尾部节点都不相同,则将旧虚拟 DOM 中的节点移动到新虚拟 DOM 中对应的位置。
  1. 节点复用:如果新旧虚拟 DOM 中的节点相同,则复用旧节点,避免重新创建节点,减少性能开销。

  2. 标记静态节点:在编译阶段,Vue 会标记静态节点,静态节点不会在后续的更新中重新渲染,从而提高性能。

  3. 最小化更新范围:Vue 的 diff 算法会尽量减少需要更新的节点范围,只更新发生变化的部分,避免不必要的 DOM 操作。

通过以上思路,Vue 的 diff 算法可以高效地对比新旧虚拟 DOM,找出需要更新的部分,从而实现高性能的 DOM 更新。

vue 的 compile 实现

Vue 的编译过程主要分为三个阶段:解析、优化和代码生成。

  1. 解析(Parse)
  • 将模板字符串解析成抽象语法树(AST),这个过程会进行词法分析和语法分析。
  • 词法分析将模板字符串拆分成一个个的 Token,语法分析则根据这些 Token 构建 AST。
  1. 优化(Optimize)
  • 对 AST 进行静态节点标记,找出模板中的静态部分。
  • 静态节点在后续的渲染过程中不会改变,因此可以跳过这些节点的对比,提升渲染性能。
  1. 代码生成(Codegen)
  • 将优化后的 AST 转换成渲染函数的字符串表示。
  • 渲染函数在运行时会被转换成虚拟 DOM 节点,从而更新真实 DOM。

通过以上三个阶段,Vue 将模板字符串编译成高效的渲染函数,从而实现高性能的视图更新。

// 示例代码
const template = `<div>{{ message }}</div>`
const { render } = Vue.compile(template)
new Vue({
  data: {
    message: 'Hello, Vue!',
  },
  render,
}).$mount('#app')

以上代码展示了 Vue 的编译过程,将模板字符串编译成渲染函数,并在 Vue 实例中使用该渲染函数进行视图更新。

vue 如何自定义指令

在 Vue.js 中,可以通过 directive 选项来自定义指令。自定义指令可以在元素绑定到 DOM 时执行特定的行为。以下是自定义指令的基本步骤:

  1. 定义指令: 使用 Vue.directive 全局定义指令,或者在组件的 directives 选项中局部定义指令。

  2. 钩子函数: 自定义指令可以包含多个钩子函数,例如 bindinsertedupdatecomponentUpdatedunbind,这些钩子函数会在指令生命周期的不同阶段被调用。

示例代码

<template>
  <div v-focus>这个元素会在插入到 DOM 时自动获取焦点</div>
</template>

<script>
export default {
  directives: {
    focus: {
      // 指令绑定到元素时调用
      bind(el) {
        el.focus()
      },
      // 元素插入到 DOM 时调用
      inserted(el) {
        el.focus()
      },
    },
  },
}
</script>

在上面的示例中,自定义了一个 v-focus 指令,当元素插入到 DOM 时,该元素会自动获取焦点。

通过自定义指令,可以在 Vue.js 中实现更灵活的 DOM 操作和行为控制。

用过 TypeScript 吗? 有什么优点,为什么用? 具体的场景,使用 TypeScript 进行类型定义。

答:vue 项目多, ts 用的少,也用过,写前端监控 sdk 时,对接口进行类型校验,它像一个文档,每一个接口都有定义,后面再翻看瞬间理解意思。(强类型好处还有很多,但是个人觉得写起来超级麻烦,当然有人很喜欢很爽。)

vue 的 keep-alive 使用场景,以及实现原理

keep-alive 是 Vue 提供的一个内置组件,用于对动态组件进行缓存。它可以在组件切换过程中将状态保留在内存中,避免重复渲染,提高性能。

使用场景:

  • 需要在组件切换时保留组件的状态或避免重新渲染的场景。
  • 例如:在多标签页切换、表单填写过程中切换页面等场景中使用 keep-alive 可以提升用户体验。

实现原理:

  • keep-alive 通过缓存已经创建的组件实例,并在组件切换时复用这些实例。
  • 当组件被包裹在 keep-alive 中时,第一次渲染时会创建组件实例并缓存起来,后续切换到该组件时会直接从缓存中获取实例,而不是重新创建。
  • keep-alive 还提供了 includeexclude 属性,可以根据条件决定哪些组件需要被缓存,哪些不需要。
<template>
  <div>
    <keep-alive>
      <component :is="currentView"></component>
    </keep-alive>
  </div>
</template>

<script>
export default {
  data() {
    return {
      currentView: 'MyComponent',
    }
  },
  components: {
    MyComponent: () => import('./MyComponent.vue'),
  },
}
</script>