六、Vue
vue-cli、@vue/cli
-
对应的 vue 版本不同
vue-cli
为 2.x 版本时 vue 的脚手架@vue/cli
为 3.x 版本的 vue 脚手架
-
创建和初始化方式不同
- vue 2 创建项目
vue init webpack
。CLI 2 可以选择根据模板初始化项目 - vue 3 创建项目
vue create
。 CLI 3 并未重新开发使用模板,同时 CLI 3 的 name 也不能使用驼峰命名
- vue 2 创建项目
-
启动项目命令不同
- vue 2 启动:
npm run dev
- vue 3 启动:
npm run serve
- vue 2 启动:
-
目录结构不同
-
vue 3 项目结构更简单,文件变少,结构更清晰 。
-
vue 3 移除了配置文件目录 config 和 build 文件夹。
-
vue 3 移除了 static 静态资源文件夹,新增 public 文件夹,静态资源转移到 public 目录中,并且 index.html 移动到 public 中。
-
vue 3 在 src 文件夹中新增了 views 文件夹,用于分类 视图组件 和 公共组件。
-
CLI 3 也隐藏了 webpack 配置文件。如果需要自定义配置,需要自己新建 vue.config.js 文件。
-
vue 3 安装项目时会自动下载 node-model 文件夹
-
-
配置不同
- Vue-cli 所生成的项目中,是把 webpack 的配置也一起放到了项目的配置文件中了。
- @vue/cli 所生成的项目中,它会把关于项目的 webpack 配置隐藏起来,并且它会抛出一个配置文件让开发者去做定制化。@vue/cli 的好处在于,如果当脚手架中的一些配置官方进行更新,那么开发者更新起来就不会这么麻烦,并且官方也希望开发者,如有需要可以通过配置文件的方式去定制他们各自的需求,使用了 webpack merge 进行了合并。
Vue2、Vue3
- 双向数据绑定原理不同
- Vue2 的双向数据绑定利用了 ES5 的 API
Object.definePropert()
对数据进行劫持,结合发布订阅模式的方式来实现的 - Vue3 使用 ES6 的 Proxy API 对数据代理
- Vue2 的双向数据绑定利用了 ES5 的 API
- 是否支持碎片
- Vue2 不支持碎片
- Vue3 支持碎片(即,支持多个根节点)
- API 类型不同
- Vue2 使用选项类型 API(Options API)。在代码中分割不同属性:data,computed,methods 等
- Vue3 使用组合类型 API(Composition API)。使用方法进行分隔,显得更加简便整洁
- 建立数据方式不同
- Vue2 在 data 中定义变量,在 methods 中创建方法
- Vue3 使用 setup() 方法
- Teleport 瞬移组件
- Vue2 没有
- Vue3 有 teleport 组件
- 指令和插槽的使用不同
- Vue2 不建议将 v-for 和 v-if 写在一起使用。允许直接使用 slot
- Vue3 将 v-if 当作 v-for 大的一个判断语句,不会相互冲突。不允许直接使用 slot,正确格式为 v-slot
- 父子传参方式不同
- Vue2 子组件通过 prop 接收。子组件中通过 $emit 向父组件触发一个监听方法,向父组件传递参数
- Vue3 只需要在 setup() 接收第二个参数中使用分解对象法取出 emit 就可以在 setup() 方法中随意使用了
- 生命周期钩子函数不同
Vue3 对 Vue2 有什么优势
- 性能更好(编译优化、使用
proxy
等) - 体积更小
- 更好的
TS
支持 - 更好的代码组织
- 更好的逻辑抽离
- 更多新功能
组合式API、选项式API
- 选项式 API
- 具有相同功能的放在一起,可以用包含多个选项的对象来描述组件的逻辑
- 例如 data、methods、mounted 等配置项
- 选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例
- 优点:简单易上手,小项目中使用归类清晰明了
- 缺点:一个功能往往需要在不同的配置项中定义属性和方法,比较分散,代码组织性差
- 组合式 API
- 一个功能逻辑的代码组织在一起,包括数据、函数等
- 它是一系列 API 的集合,使我们可以用函数的方式书写,它涵盖的 API 有:
- 响应式 API:例如 ref() 和 reactive(),使我们可以直接创建响应式状态、计算属性和侦听器
- 生命周期钩子:例如 onMounted() 和 onUnmounted(),使我们可以在组件各个生命周期阶段添加逻辑
- 依赖注入:例如 provide() 和 inject(),使我们可以在使用响应式 API 时,利用 Vue 的依赖注入系统
- 优点:把一个功能相关的东西放在一个地方,实现更加简洁高效的逻辑复用,后期维护方便
- 缺点:更抽象,不易上手
Vue2 深入响应式原理
- 自由理解
- 对象内部通过
defineReactive
方法,使用Object.defineProperty
将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的dep
属性,存放他所依赖的watcher
(依赖收集),当属性变化后会通知自己对应的watcher
去更新(派发更新)。
- 对象内部通过
- 官方理解
- Vue2 的双向数据绑定利用了 ES5 的 API
Object.definePropert()
对数据进行劫持,结合发布订阅模式的方式来实现的 - 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用
Object.defineProperty
把这些 property 全部转为getter/setter
- 这些
getter/setter
对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更 - 每个组件实例都对应一个
watcher
实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。 - 之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染
- Vue2 的双向数据绑定利用了 ES5 的 API
Vue2 响应式缺陷
- 深度监听,需要递归到底,一次计算量大
- 无法监听新增属性、删除属性(使用
Vue.set
、Vue.delete
可以) - 无法监听原生数组,需要重写数组原型
Vue3 使用 Proxy API 数据代理的优势
- 深度监听,性能更好(获取到哪一层才触发响应式
get
,不是一次性递归) - 可监听
新增/删除
属性 - 可监听数组变化
Vue2 生命周期钩子
- 什么是生命周期
- vue 每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期
- 生命周期及执行顺序
- beforeCreate()
- 表示实例在完全创建出来之前会执行它,在执行它时,data 和 methods 中的数据都还未初始化
- created()
- 此时 data 和 methods 已经初始化完成,created 完成之后,vue 开始编译模板,最终在内存中生成一个编译好的最终模板字符串,然后把模板字符串渲染为内存中的 dom
- beforeMount()
- 表示模板在内存中已经编译好了,但是并没有渲染到页面中。页面显示的还仅仅是模板字符串
- mounted()
- 此时内存中的模板已经真实的挂载到了页面中,用户可以看到渲染好的页面了
- beforeUpdate()
- 运行中的事件,执行它时,data 中的数据已经被更新了,但是页面中的 data 还未被替换过来
- updated()
- 运行中的事件,执行它时,页面和 data 中的数据已经同步了
- beforeDestroy()
- 实例销毁之前调用
- destroyed()
- 当我们离开这个页面的时候,便会调用这个函数
- beforeCreate()
- 内置的方法属性的执行顺序
- props 组件传值
- methods 方法
- data 数据
- computed 计算属性
- watch 侦听属性
Vue3 生命周期钩子
- onBeforeMount()
- 在组件被挂载之前被调用
- 当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程
- onMounted()
- 在组件挂载完成后执行
- onBeforeUpdate()
- 在组件即将因为响应式状态变更而更新其 DOM 树之前调用
- onUpdated()
- 在组件因为响应式状态变更而更新其 DOM 树之后调用
- onBeforeUnmount()
- 在组件实例被卸载之前调用
- 当这个钩子被调用时,组件实例依然还保有全部的功能
- onUnmounted()
- 在组件实例被卸载之后调用
- 可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接
- onErrorCaptured()
- 在捕获了后代组件传递的错误时调用
- 这个钩子可以通过返回 false 来阻止错误继续向上传递
- onActivated()
- 若组件实例是 KeepAlive 缓存树的一部分,当组件被插入到 DOM 中时调用
- onDeactivated()
- 若组件实例是 KeepAlive 缓存树的一部分,当组件从 DOM 中被移除时调用
Vue3 中 setup() 函数的特性
- setup 钩子是在组件中使用组合式 API 的入口
- setup 执行时机在 beforeCreate 和 created 之间,故在 setup 函数中,无法使用 data 和 methods
- setup 函数里面是没有 this 对象的,使用它的话会报 undefined
- 在 setup 函数中定义的变量和方法需要 return 出去,不然无法在模板中使用
- 在 setup 函数中返回的对象会暴露给模板和组件实例
- setup 函数只能够同步,不能够异步操作
- setup 有两个参数:
- props:接收数据(响应式)。可引入 toRefs 来结构 props
- context:是一个普通的 Javascript 对象,渲染到上下文。可在模板使用。它有三个属性:
- attrs:它是绑定到组件中的 非 props 数据,并且是非响应式的
- slots:是组件的插槽,同样也不是响应式的
- emit:是一个方法,相当于 vue2 中的 this.$emit 方法
Vue2 中 data 为什么是个函数
- 理解
- 在 Vue 中,组件实例是可复用的,如果 data 是一个普通对象,那么所有实例将共享同一个 data 对象,这样会导致数据混乱和不可预测的行为。
- 将 data 设为一个函数,每次创建一个新的组件实例时,该函数都会被调用,返回一个新的数据对象,确保每个组件实例都有自己独立的数据。这样可以避免不同组件实例之间因为数据共享而导致的数据污染问题,确保组件的正确性和可维护性
- 总结
- 确保独立性。每个组件实例都有自己的数据副本,不会共享数据。
- 防止数据冲突。避免多个组件实例之间因数据共享而导致的状态混乱。
- 提供私有的数据空间。每个组件实例都有自己的私有数据空间,维护各自的数据。
this.$set() 的作用
- 用于在响应式对象上设置新的属性或修改已有的属性,并确保这些属性也是响应式的。
- Vue 响应式系统的一个限制,即无法检测到通过索引直接设置数组元素或通过
Object.defineProperty
添加的属性的变化。 - 通过
this.$set()
方法,可以显式地告诉 Vue,某个属性的值已经发生了变化,从而触发视图的更新。
vue 给 data 中对象添加新属性会发生什么?如何解决?
-
发生什么
- 在 Vue 中,给 data 中的对象直接添加新属性通常不会触发视图更新,
- 因为 Vue 的响应式系统在初始化实例时已经为现有属性建立了 watcher。
- 如果你直接添加新属性,Vue 不会自动转换它成响应式的。
-
解决方法
-
使用
Vue.set
或this.$set
方法来添加新属性。(推荐使用)// 假设有一个Vue实例,其data如下: data() { return { myObject: {} }; } // 使用Vue.set添加新属性 Vue.set(this.myObject, 'newProp', 'value'); // 或者使用this.$set this.$set(this.myObject, 'newProp', 'value');
-
使用
Object.assign
或Spread operator
来创建一个新对象,包含原有属性和新属性。// 使用Object.assign this.myObject = Object.assign({}, this.myObject, { newProp: 'value' }); // 使用Spread operator this.myObject = { ...this.myObject, newProp: 'value' };
-
nextTick() 的原理和作用
- 原理
nextTick()
方法是 Vue.js 提供的一个异步更新 DOM 的工具。nextTick()
的原理是利用 JavaScript 的事件循环机制,在当前代码执行完成后,将回调函数推入微任务队列中,等待下一个微任务执行时执行这些回调函数。这样可以保证回调函数在 DOM 更新之后执行,从而避免出现获取到未更新的 DOM 元素的情况。- 本质是为了利用 JavaScript 的异步回调任务队列实现 Vue 框架中自己的异步回调队列。
- 作用
nextTick()
的主要作用是在 Vue 数据更新后,等待当前的 DOM 更新完成之后执行回调函数,以确保能够操作到最新的 DOM 元素。- 满足实际应用中对 DOM 更新数据时机的后续逻辑处理。
列表组件中写 key 的作用
-
说明
- vue 和 react 都是采用 diff 算法来对比新旧虚拟节点,从而更新节点
- 在 vue 的 diff 函数的交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的 key 去对比旧节点数组中的 key,从而找到相应旧节点
-
作用
-
vue 的虚拟 DOM 的 diff 算法依赖于 key 来判断节点的变化情况
-
利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快
-
避免就地复用的情况,更高效准确
- 当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染
- 只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出
-
-
用 index 作为 key 的缺点
- 渲染结果不准确。当列表发生重新排序、插入、删除操作时,vue无法正确识别和更新
- 不利于性能优化。可能产生没有必要的真实DOM更新,效率低
Vue 为什么需要虚拟 Dom
- Vue 的核心是允许用户声明式地构建用户界面,这意味着我们不需要直接操作DOM。
- 然而,Vue 仍然需要一种方法来跟踪和高效地更新 DOM。
- 虚拟 DOM(Virtual DOM)是实现这一目标的一种方法。
- 虚拟 DOM 允许 Vue 在内存中构建和操作 DOM 结构。
- 当数据改变时,Vue 会对比新旧虚拟 DOM 之间的差异,并只将变更应用到实际的 DOM 上,而不是重新渲染整个视图。
- 这样做可以极大地提高性能,并减少了某些类型的错误。
Vue2、Vue3 的 diff 算法
- Vue2 的 diff 算法:
- 使用深度优先算法遍历虚拟DOM树。
- 比较相同标签名的节点:
- 如果节点和新节点都存在,则比较属性,重复使用旧节点,更新需要变更的属性。
- 如果节点和新节点不同,则需要替换整个节点。
- 比较不同标签名的节点,直接替换整个节点。
- Vue3 的 diff 算法:
- 新增了
static
标记,用于标记静态节点,减少比较层级。 - 使用了基于栈的算法来处理动态节点,提高 diff 效率。
- 对于 Composition API 函数返回的相同节点,会复用旧的节点,只比较变化的部分。
- 新增了
- 区别:
- Vue2 采用递归 diff,Vue3 采用了基于栈的 diff。
- Vue3 中采用了针对动态节点的优化,如
Teleport
和Suspense
,减少了 diff 层级。 - Vue3 中的 Composition API 和 setup 功能,可以让代码更加内聚,减少不必要的 diff。
Vue2 组件传值的几种方式
-
props 传值 (父传子)
- 子组件必须注册好要传的数据
-
事件 传值 (子传父)
- 子组件使用 this.$emit() 方法传值给父组件,父组件接收数据并调用自定义事件
-
事件总线(Event Bus)
-
创建一个专用于事件传递的 Vue 实例作为中央事件总线,不直接关联组件,实现任何组件间通信
-
// EventBus.js import Vue from 'vue'; export const EventBus = new Vue(); // 发送组件:使用 EventBus.$emit 触发一个事件 EventBus.$emit('eventName', data); // 接收组件:使用 EventBus.$on 监听一个事件 EventBus.$on('eventName', (data) => { // 处理接收到的数据 });
-
-
状态管理工具 Vuex
-
provide/ inject
-
路由跳转传参
-
本地缓存 storage
Vuex 状态管理模式
- 什么是 vuex
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
- 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
- 五个核心
- State
- 单一状态树,存储数据
- 用一个对象就包含了全部的应用层级状态。作为一个唯一数据源而存在。这也意味着,每个应用将仅仅包含一个 store 实例
- 单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照
- Getters
- getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
- Mutations
- 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
- Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
- Actions
- Action 提交的是 mutation,而不是直接变更状态
- Action 可以包含任意异步操作
- Modules
- 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿
- 为了解决以上问题,Vuex 允许我们将 store 分割成模块
- 每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
- State
- 优点
- js 原生的数据对象写法,比起 localStorage 不需要做转换,使用方便
- 属于 vue 生态一环,能够触发响应式的渲染页面更新 (localStorage 就不会)
- 限定了一种可预测的方式改变数据,避免大项目中数据不小心的污染
- 减少了ajax请求次数,有些情景可以直接从内存中的state获取
- 缺点
- 刷新浏览器,vuex中的state会重新变为初始状态,vuex 存储的值会丢失。可使用使用插件 vuex-along 或 vuex-persistedstate 解决
vuex、pinia 的区别
-
兼容性
- Vuex 是 Vue 2 的官方状态管理库。
- Pinia 是为 Vue 3 设计的状态管理库。
-
架构设计
- Vuex 采用全局单例模式,通过一个 store 对象来管理所有的状态,组件通过 store 对象的 mutations 和 actions 来修改状态。
- Pinia 采用分离模式,每个组件都拥有自己的 store 实例,通过在组件中创建store实例来管理状态。
-
TypeScript支持
- Vuex 从Vue 2.x 版本开始引入了对 TypeScript 的支持,但需要使用额外的插件来实现类型检查。
- Pinia 在设计之初就对 TypeScript 提供了原生的支持,提供了更好的类型推导和类型检查的支持。
-
插件生态系统
- Vuex 拥有广泛的插件生态系统,可以通过插件来扩展和增强 Vuex 的功能。
- Pinia 相对较新,插件生态系统相对较小。
-
性能和体积
- Vuex 体积相对较大,可能会增加应用程序的初始加载时间。
- Pinia 在性能和体积方面具有优势,它只会在实际使用时才会进行模块的注册和加载。
Vuex 和本地存储的区别
- 主要区别
- Vuex 用于组件之间的状态管理,而本地存储用于长期保存数据。
- Vuex 的状态是响应式的,而本地存储不是。
- Vuex 的状态在页面刷新时会丢失,而本地存储的数据则不会丢失。
- Vuex 的状态更改需要通过特定的 Vuex 动作,而本地存储可以直接通过设置值。
- 应用场景
- 如果需要一个长期的、可响应的数据存储,可以将数据存储在本地存储中,并通过 Vuex 管理状态。
- 如果需要在多个组件之间共享状态,并且希望状态的变化是响应式的,那么可以使用 Vuex 来管理状态。
- 可以结合使用两者,将不经常变化的数据存储在 Vuex,而将经常变化或需要持久存储的数据存储在本地存储
computed、watch
- 计算属性 computed
- 变量不在 data 中定义,而是定义在 computed 中,有返回值。函数名直接在页面模板中渲染,不加小括号
- 根据传入的变量的变化,进行结果的更新
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当 computed 内有异步操作时无效,无法监听数据的变化
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用 computed
- 侦听属性 watch
- 监听数据必须是 data 中声明过或者父组件传递过来的 props 中的数据,当数据变化时,触发其他操作
- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值
- 不支持缓存,数据变,直接会触发相应的操作
- 支持异步
- 当一个属性发生变化时,需要执行对应的操作;一对多
- 当有一些数据需要随着其它数据变动而变动时,或者当需要在数据变化时执行异步或开销较大的操作时,用 watch
v-if、v-show
- 手段
- v-if 是动态的向 DOM 树内添加或者删除 DOM 元素
- v-show 是通过设置 DOM 元素的 display 样式属性控制显隐
- 编译过程
- v-if 切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件
- v-show 只是简单的基于 css 切换
- 编译条件
- v-if 是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译
- v-show 是在任何条件下都被编译,然后被缓存,而且 DOM 元素保留
- 性能消耗
- v-if 有更高的切换消耗
- v-show 有更高的初始渲染消耗
- 使用场景
- v-if 适合运营条件不大可能改变
- v-show 适合频繁切换
Vue 常用修饰符
- 事件修饰符
.stop
:阻止事件冒泡.prevent
:提交事件不再重新加载页面,可以用来阻止表单提交的默认行为.once
:点击事件只会触发一次.native
:使用时将被当成原生HTML标签看待
- 按键修饰符
@keyup
:键盘抬起@keydown
:键盘按下- 按键码:在键盘修饰符后面添加.xxx(.enter,.tab,.delete,.esc,.up,.down,.space 等),用于监听按了哪个键
- 表单修饰符
.lazy
:在表单输入时不会马上显示在页面,而是等输入完成失去焦点时才会显示.trim
:过滤表单输入时两边的空格.number
:限制输入数字或将输入的数据转为数字
Vue 中修饰符.sync与v-model的区别
sync
的作用.sync
修饰符可以实现父子组件之间的双向绑定,并且可以实现子组件同步修改父组件的值,相比较与v-model
来说,sync
修饰符就简单很多了- 一个组件上可以有多个
.sync
修饰符
- 相同点
- 都是语法糖,都可以实现父子组件中的数据的双向通信
- 区别
- 格式不同:
v-model="num"
,:num.sync="num"
v-model
:@input + value
:num.sync
:@update:num
v-model
只能用一次;.sync
可以有多个
- 格式不同:
history 和 hash 两种路由
-
history
- 利用了
HTML5 History Interface
中新增的pushState()
和replaceState()
方法- 这两个方法应用于浏览器的历史记录站,在当前已有的
back
、forward
、go
的基础之上,它们提供了对历史记录进行修改的功能。这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前URL
改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础 - 特点:虽然美观,但是刷新会出现
404
需要后端进行配置
- 这两个方法应用于浏览器的历史记录站,在当前已有的
- 利用了
-
hash
-
location.hash
的值实际就是URL
中#
后面的东西 它的特点在于:hash
虽然出现URL
中,但不会被包含在HTTP
请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面 -
可以为 hash 的改变添加监听事件
window.addEventListener("hashchange", funcRef, false);
-
每一次改变
hash
(window.location.hash
),都会在浏览器的访问历史中增加一个记录利用hash
的以上特点,就可以来实现前端路由“更新视图但不重新请求页面”的功能了。 -
特点:兼容性好但是不美观
-
Vue 路由守卫
- 全局路由守卫
beforeEach
(to, from, next) 全局前置守卫,路由跳转前触发beforeResolve
(to, from, next) 全局解析守卫 在所有组件内守卫和异步路由组件被解析之后触发afterEach
(to, from) 全局后置守卫,路由跳转完成后触发
- 路由独享守卫
beforeEnter
(to,from,next) 路由对象单个路由配置 ,单个路由进入前触发
- 组件路由守卫
beforeRouteEnter
(to,from,next) 在组件生命周期beforeCreate阶段触发beforeRouteUpdadte
(to,from,next) 当前路由改变时触发beforeRouteLeave
(to,from,next) 导航离开该组件的对应路由时触发
- 参数
- to: 即将要进入的目标路由对象
- from: 即将要离开的路由对象
- next(Function):是否可以进入某个具体路由,或者是某个具体路由的路径
Vue 指令
v-if
:根据其表达式的布尔值来判断是否应该渲染该元素。如果表达式的值为真,则渲染元素;否则不渲染。v-show
:当其表达式的值为假时,会添加 display: none 样式以隐藏元素,且不会重新渲染元素。v-else
:用于条件分支,分别对应 else 和 else if 语句的功能。v-for
:用于循环遍历数组或其他可迭代的数据结构。v-bind
:用于将HTML特性的值绑定到DOM元素上。可以使用 v-bind:key等方式定义唯一的键值对,以便进行数据绑定。v-on
:用于监听指定元素的DOM事件。可以通过 v-on:click=“handler” 的方式定义事件的处理器函数。v-once
:用于一次性插入文本,之后即使重新渲染也不会被再次计算。这使得它适合用于只插入一次的内容。v-html
:可以将数据转换为目标HTML代码,通常用于从API获取的HTML字符串。
Vue 和 React 的区别,以及应用场景
- 语法和特点:
- Vue 是一款渐进式 JavaScript 框架,采用了类似 HTML 的模板语法,易于学习和上手。Vue 提供了一套响应式的数据绑定机制,可以实现轻量级的组件化开发。
- React 是由 Facebook 开发的 JavaScript 库,采用了 JSX 语法,将 HTML 和 JavaScript 代码结合在一起。React 采用虚拟 DOM 的机制,可以高效地更新 DOM,提升性能。
- 生态系统和工具支持:
- Vue 有完善的生态系统,包括 Vue Router、Vuex 等官方插件,以及丰富的第三方库和工具支持。Vue 的文档清晰易懂,社区活跃,有大量的教程和资源。
- React 也有庞大的生态系统,包括 React Router、Redux 等官方插件,以及许多第三方库和工具。React 拥有强大的社区支持和活跃的开发者社群。
- 应用场景:
- Vue 适合快速开发小型到中型的单页面应用(SPA),特别适合初学者或者团队中有前端开发经验较少的成员。Vue的语法简洁,易于理解和维护,适合快速迭代和开发。
- React 适合开发大型、复杂的前端应用,特别适合需要高度定制化和复杂交互逻辑的项目。React 的虚拟 DOM 机制和组件化开发思想可以有效提升性能和代码复用性。
混入(Mixin)
- 什么是 Mixin
- Mixin 是一种在 Vue 中用来复用组件选项的方式,可以将一组组件选项对象合并到另一个组件的选项对象中,从而实现对组件功能的复用和扩展。
- 在实际项目中,可以通过 mixin 避免重复编写相同的代码,提高代码的复用性和可维护性。
- 缺陷
- 命名冲突、依赖问题、数据来源问题
- 注意事项
- Mixin 中的选项会被合并到组件本身的选项中,如果组件和 mixin 具有相同的选项,组件自身的选项会优先。
- Mixin 可以引入 data、methods、created 等组件选项。
- 避免滥用 mixin,过多的 mixin 可能会导致代码难以理解和维护。
- Mixins 在 Vue 3 支持主要是为了向后兼容,因为生态中有许多库使用到。在新的应用中应尽量避免使用 mixin,特别是全局 mixin。若要进行逻辑复用,推荐用组合函数来替代。
Vue 中 keep-alive
- 在 Vue 中,
<keep-alive>
是一个抽象组件,用于缓存动态组件。 - 它能够缓存不活动的组件实例,而不是销毁它们。
- 这可以提高应用性能,特别是在频繁切换组件时。
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
Vue3 中的 Teleport 是什么?它的作用是什么?
- Vue3中的Teleport 是控制渲染位置的新指令。
- 它的作用是在DOM中移动一个组件的内容而不改变组件的父级。
使用 Vue 过程中遇到过哪些坑
- 内存泄露
- 全局变量、全局事件、全局定时器没有销毁
- 自定义事件没有销毁
- Vue2 响应式的缺陷(vue3不在有)
data
后续新增属性用Vue.set
data
删除属性用Vue.delete
Vue2
并不支持数组下标的响应式。也就是说Vue2
检测不到通过下标更改数组的值arr[index] = value
实现双向数据绑定MVVM
<input id="input"/>
<script>
const data = {};
const input = document.getElementById('input');
// 劫持数据
Object.defineProperty(data, 'text', {
get() {
return this.value;
},
set(value) {
input.value = value;
this.value = value;
}
});
// 监听
input.addEventListener('change', function(e) {
data.text = e.target.value;
});
</script>
Vue实例挂载的过程中发生了什么
- 挂载过程指的是
app.mount()
过程,这个过程中整体上做了两件事:初始化和建立更新机制 - 初始化会创建组件实例、初始化组件状态,创建各种响应式数据
- 建立更新机制这一步会立即执行一次组件更新函数,这会首次执行组件渲染函数并执行
patch
将前面获得vnode
转换为dom
;同时首次执行渲染函数会创建它内部响应式数据之间和组件更新函数之间的依赖关系,这使得以后数据变化时会执行对应的更新函数
Vue 的父子组件生命周期钩子函数执行顺序
- 渲染顺序:先父后子,完成顺序:先子后父
- 子组件先挂载,然后到父组件
- 父
beforeCreate
->父created
->父beforeMount
->子beforeCreate
->子created
->子beforeMount
->子mounted
->父mounted
- 更新顺序:父更新导致子更新,子更新完成后父
- 父
beforeUpdate
->子beforeUpdate
->子updated
->父updated
- 父
- 销毁顺序:先父后子,完成顺序:先子后父
- 父
beforeDestroy
->子beforeDestroy
->子destroyed
->父destroyed
- 父
Vue 组件化的理解
- 组件化开发能大幅提高开发效率、测试性、复用性等
- 常用的组件化技术:属性、自定义事件、插槽
- 降低更新频率,只重新渲染变化的组件
- 组件的特点:高内聚、低耦合、单向数据流
插槽、作用域插槽
- 插槽
- 创建组件虚拟节点时,会将组件儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿子进行分类
{a:[vnode],b[vnode]}
- 渲染组件时会拿对应的
slot
属性的节点进行替换操作。(插槽的作用域为父组件)
- 创建组件虚拟节点时,会将组件儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿子进行分类
- 作用域插槽
- 作用域插槽在解析的时候不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。(插槽的作用域为子组件)
- 普通插槽渲染的作用域是父组件,作用域插槽的渲染作用域是当前子组件。
Vue 自定义指令
- 理解和使用
Vue
有一组默认指令,比如v-model
或v-for
,同时Vue
也允许用户注册自定义指令来扩展 Vue 能力- 自定义指令主要完成一些可复用低层级
DOM
操作 - 使用自定义指令分为定义、注册和使用三步:
- 定义自定义指令有两种方式:对象和函数形式,前者类似组件定义,有各种生命周期;后者只会在
mounte
d和updated
时执行 - 注册自定义指令类似组件,可以使用
app.directive()
全局注册,使用{directives:{xxx}}
局部注册 - 使用时在注册名称前加上
v-
即可,比如v-focus
- 定义自定义指令有两种方式:对象和函数形式,前者类似组件定义,有各种生命周期;后者只会在
- 常用的自定义指令
- 复制粘贴
v-copy
- 长按
v-longpress
- 防抖
v-debounce
- 图片懒加载
v-lazy
- 按钮权限
v-premission
- 页面水印
v-waterMarker
- 拖拽指令
v-draggable
- 复制粘贴
- 原理
- 指令本质上是装饰器,是
vue
对HTML
元素的扩展,给HTML
元素增加自定义功能。vue
编译DOM
时,会找到指令对象,执行指令的相关方法。 - 自定义指令有五个生命周期(也叫钩子函数),分别是
bind
、inserted
、update
、componentUpdated
、unbind
- 指令本质上是装饰器,是