vue2
v-show 与 v-if 的区别
- v-show指令是通过修改元素的display的CSS属性让其显示或者隐藏;
- v-if指令是直接 销毁 和 重建 DOM达到让元素显示和隐藏的效果;
- 使用 v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。
keep-alive 的作用是什么
官网解释:包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
作用:实现组件缓存,保持这些组件的状态,以避免反复渲染导致的性能问题。需要缓存组件 频繁切换,不需要重复渲染。
场景:tabs标签页、后台导航、vue性能优化。
原理:Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。
nextTick的实现
- nextTick是Vue提供的一个全局API,是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用$nextTick,则可以在回调中获取更新后的DOM。
- Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启1个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中1次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。nextTick方法会在队列中加入一个回调函数,确保该函数在前面的dom操作完成后才调用
- 比如,我在干什么的时候就会使用nextTick,传一个回调函数进去,在里面执行dom操作即可。简单了解nextTick的实现,它会在callbacks里面加入我们传入的函数,然后用timerFunc异步方式调用它们,首选的异步方式会是Promise。这让我明白了为什么可以在nextTick中看到dom操作结果。
- 实现原理:在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后立即使用 nextTick 来获取更新后的 DOM。nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用Promise、MutationObserver、setImmediate,如果以上都不行则采用setTimeout定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。
Vue 组件之间的通信方式
- 父组件向子组件传值:props
- 子组件向父组件传值:emit事件
- $ref获取子组件的实例
- provide、inject:实现多层级注入数据
- event-bus事件总线
Vuex的理解及使用场景
Vuex 是一个专为 Vue 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。
Vuex 的状态存储是响应式的;当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新 2. 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation,这样使得我们可以方便地跟踪每一个状态的变化。
Vuex主要包括以下几个核心模块:
- State:定义了应用的状态数据
- Getter:在 store 中定义”getter”(可以认为是 store 的计算属性),就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
- Mutation:是唯一更改 store 中状态的方法,且必须是同步函数
- Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作
- Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中
vue 的生命周期
- beforeCreate:在实例创建之间执行,数据是未加载状态。创建一个Vue实例,此时实例上只有一些生命周期函数和默认的事件。此时data computed watch methods上的方法和数据均不能访问。
- created:在实例创建、数据加载后,能初始化数据,DOM渲染之前执行。可以对data数据进行操作,可进行一些请求,请求不易过多,避免白屏时间太长。
- beforeMount:虚拟DOM已创建完成,在数据渲染前最后一次更改数据。el未挂载。判断el的挂载方式。判断是否有template设置。将template进行渲染保存到内存当中,还未挂载在页面上。
- mounted:页面、数据渲染完成。el挂载完毕。可以访问DOM节点。将内存中的模版挂载到页面上。此时可以操作页面上的DOM节点。此时组件从创建阶段进入运行阶段
- beforeUpdate:重新渲染之前触发。不会造成重渲染。页面显示的数据是旧的,此时data里面的数据是最新,页面数据和data数据暂未同步。
- updated:数据已经更新完成,DOM也重新render完成,更改数据会陷入死循环。根据data里的最新数据渲染出最新的DOM树,然后将最新的DOM挂载到页面。此时data和页面数据一致,都是最新的。
- beforeDestroy:实例销毁前执行,实例仍然完全可用。此时组件从运行阶段进入到销毁阶段。组件上的data和methods以及过滤器等都处于可用状态,销毁还未执行。
- destroyed:实例销毁后执行,这时候只剩下DOM空壳。组件已经被完全销毁,组件中所有的数据、方法、指令、过滤器等,都已不可用。
简述Vue每个周期具体适合哪些场景
- beforeCreate:在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。在beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
- created:data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
- beforeMount:执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
- mounted:执行到这个钩子的时候,就表示Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
- beforeUpdate:当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的,页面还没有和最新的数据保持同步
- updated:页面显示的数据和data中的数据已经保持同步了,都是最新的
- beforeDestory:Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods,指令,过滤器 ……都是处于可用状态。还没有真正被销毁
- destroyed:这个时候上所有的 data 和 methods,指令,过滤器 ……都是处于不可用状态。组件已经被销毁了。
简述MVVM 和MVC的原理以及区别
MVVM视图模型双向绑定,是Model-View-ViewModel的缩写。MVVM的优点:
- 低耦合。视图(View)可以独立于Model变化和修改,一个Model可以绑定到不同的View上,当View变化的时候Model可以不变化,当Model变化的时候View也可以不变。
- 可重用性。你可以把一些视图逻辑放在一个Model里面,让很多View重用这段视图逻辑。
- 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
- 可测试。
MVC是应用最广泛的软件架构之一,一般MVC分为:Model(模型),View(视图),Controller(控制器)。这主要是基于分层的目的,让彼此的职责分开.View一般用过Controller来和Model进行联系。Controller是Model和View的协调者,View和Model不直接联系。基本都是单向联系。M和V指的意思和MVVM中的M和V意思一样。C即Controller指的是页面业务逻辑。MVC是单向通信。也就是View跟Model,必须通过Controller来承上启下。
MVC与MVVM的区别?
- MVC和MVVM的区别并不是VM完全取代了C,ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。也就是说MVVM实现的是业务逻辑组件的重用。
- MVC中Controller演变成MVVM中的ViewModel
- MVVM通过数据来显示视图层而不是节点操作
- MVVM主要解决了MVC中大量的dom操作使页面渲染性能降低,加载速度变慢,影响用户体验
vue常见指令
- v-model 多用于表单元素实现双向数据绑定
- v-bind:简写为冒号:”:“,动态绑定一些元素的属性,类型可以是:字符串、对象或数组。
- v-on:click 给标签绑定函数,可以缩写为:”@“,例如绑定一个点击函数 函数必须写在methods里面
- v-for 格式:v-for=“字段名 in(of) 数组json” 循环数组或json,记得加上key
- v-show 显示内容
- v-if 指令:取值为true/false,控制元素是否需要被渲染
- v-text 解析文本
- v-html 解析html标签
vue中的data为什么是一个函数?起到什么作用?
- 在Vue组件中,data选项必须是一个函数,而不能直接是一个对象。这是因为Vue组件可以同时存在多个实例,如果直接使用对象形式的data选项,那么所有的实例将会共享同一个data对象,这样就会造成数据互相干扰的问题。
- 因此,将data选项设置为函数可以让每个实例都拥有自己独立的data对象。当组件被创建多次时,每个实例都会调用该函数并返回一个新的data对象,从而保证了数据的隔离性。
- 另外,data选项作为一个函数还具有一个重要的特性,就是它可以接收一个参数,这个参数是组件实例本身。这个特性在一些场景下非常有用,例如在定义组件时需要使用组件实例的一些属性或方法来计算初始数据。
- 因此,为了避免数据共享和保证数据隔离性,以及方便使用组件实例的属性和方法,Vue组件中的data选项必须是一个函数。
vue中ref的作用
- 获取DOM元素的引用
- 获取子组件的引用
- 利用 v-for 和 ref 获取一组数组或者dom 节点
vue中hash和history的区别
Vue-router的路由分为hash和history模式
hash模式:
- 当#后面的url地址发生变化时,浏览器不会向服务器发送请求,故不会刷新页面
- 当#后面的url地址发生变化时,会触发hashChange(hash模式得核心实现原理)事件,从而,我们可以通过监听hashChange事件来知道路由发生变化,从而进一步去更新我们的页面
- 只可修改hash部分
- 当浏览器刷新时,浏览器只会向服务器去请求# 前面的域名服务器下根目录下的index.html文件
history模式
- history模式得路由和域名之间直接通过/连接,无#符分隔,就是普通url形式
- history模式当发生路由跳转时,通过HTML5的history.pushState()方法或者history.replaceState() 方法改变地址栏地址,并将地址的改变记录到浏览器访问栈中。(这里有一点需要注意,它只改变了浏览器地址栏中的地址,但并不会像服务器去发送请求)
- 当浏览器前进,后台,或者调用back(),forward(), go()等方法时,会触发popstate事件。故,我们可以通过监听popstate事件来获取最新的路由地址,从而更新页面
- 通过pushstate() 修改的url可以是与当前url同源的任意url。
- 需要和服务器配合使用,否则容易出现页面404的情况
Vue3
Vue2.0和Vue3.0的区别
vue经历从2.0到3.0更新之后,简⽽⾔之就是变得更轻,更快,使⽤起来更加⽅便,每⼀次的版本迭代都是对上⼀个版本的升级优化,不管 是对于我们开发者还是对于⽤户体验都是不断地在越来越⽅便,接下来我会着重于开发者来说⼀下两个不同版本的区别。
- 工程化有差别:打包工具Vite替代了webpack;更适合typescript。
- 在开发过程中两个版本的使⽤⽅法虽然在表⾯上没有太⼤的⼀个区别,但是在他的底层⽅⾯去看的话区别还是很⼤的,其中就包括渲染⽅式,数据监听,双向绑定,⽣命周期,vue3更精准变更通知,这⾥着重说⼀下关于双向绑定的更新。 - vue2 的双向数据绑定是利⽤ES5的⼀个 API,Object.definePropert()对数据进⾏劫持 结合发布订阅模式的⽅式来实现的。 - vue3 中使⽤了 ES6 的 ProxyAPI 对数据代理,通过 reactive() 函数给每⼀个对象都包⼀层 Proxy,通过 Proxy 监听属性的变化,从⽽实现对数据的监控。
Vue3带来了什么改变
- 性能的提升:打包大小减少41%,初次渲染快55%, 更新渲染快133%,内存减少54%。
- 源码的升级:使用Proxy代替defineProperty实现响应式,重写虚拟DOM的实现和Tree-Shaking。
- 拥抱TypeScript:Vue3可以更好的支持TypeScript。
- 新特性:Composition API(组合API),setup、ref与reactive、watch与watchEffect、provide/inject
- 新的内置组件:Fragment、Teleport、Suspense
- 生命周期重命名:beforeDestroy改名为 beforeUnmount,destroyed改名为 unmounted
Vue3.0中的响应式原理是什么?vue2的响应式原理是什么?
响应式原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,通过拦截对数据的操作,在数据变动时发 布消息给订阅者,触发相应的监听回调。
核心过程是通过 ES5 的 Object.defindeProperty (vue3是Es6的Proxy)中的访问器属性中的 get 和 set 方法,把数据(data)设置为getter和setter的访问形式,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Watcher,观察者 Watcher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM树上。
vue2.x的响应式
实现原理:
- 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
- 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
存在问题:
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
Vue3.0的响应式
实现原理:
- 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
- 通过Reflect(反射): 对源对象的属性进行操作。
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
proxy.name = 'tom'vue3响应式数据的判断:
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
- isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
- isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
Proxy的优势:
- 可以对整个对象进行监听,而无需遍历属性。
- 添加和删除对象的属性时,也支持响应式。
vue3的常用 Composition API有哪些?
setup:Vue3.0中一个新的配置项,值为一个函数。
- 执行时机在beforeCreate之前,this是undefined。
- setup的参数,props、context、attrs、slots、emit
- 返回值:返回值可以是一个对象(即模版中可以使用的数据),也可以是一个渲染函数(则可以自定义渲染内容)。
ref:定义一个响应式的数据
- 语法:
const xxx = ref(initValue) - 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
- JS中操作数据:xxx.value
- 模板中读取数据: 不需要.value,直接:
<div>{ {xxx}}</div>
diff算法
参考:https://blog.csdn.net/qq_36384657/article/details/137630990
vue3 diff算法做了哪些优化:
- 静态树提升,Vue3使用静态树提升技术,将静态内容从动态内容中分离出来,并在渲染时只更新动态内容,从而减少不必要的更新操作,提高性能。
- 优化的算法逻辑:Vue3对diff算法的逻辑进行了优化,进行前缀后缀处理,并引入最长递增子序列,减少元素移动次数。使得在更新过程中能够更快地定位变化并进行更新,减少不必要的操作。
diff算法比较流程
- 比较是否是相同节点;相同节点比较属性,并复用老节点;
- 如果是相同节点,考虑老节点和新节点的儿子节点情况: - 老的没有儿子,新的有儿子,将新的儿子节点挂载给老节点; - 老的有儿子,新的没儿子。删除页面节点; - 老的有儿子,新的有儿子,但都是文本节点,直接更新文本节点; - 老的儿子是一个列表,新的儿子也是一个列表。核心diff,双端比较。
vue的patch
当组件中数据更新时,会通过调用 render 方法为组件生成一个新的 VNode,将新的 VNode 与 旧的 VNode 通过 diff 算法进行比较,查找本次需要更新的内容,接着执行 DOM 操作去更新对应节点.
updateComponent = () => {
// 执行 _update 进入更新阶段,首先会执行 _render,将组件变成 VNode
vm._update(vm._render(), hydrating)
}首先会执行 vm._render() 得到组件的 VNode,接着将 VNode 传递给 vm._update 方法,就正式开始 patch 阶段.
Vue 中的 patch 都做了些什么?
Vue 的 patch 负责组件的 首次渲染、后续更新、销毁组件
- 如果老的 VNode 是真实元素,则表示首次渲染,创建整棵 DOM 树,并插入 body,然后移除老的模版节点
- 如果老的 VNode 不是真实元素,并且新的 VNode 也存在,则表示更新阶段,执行 patchVnode
- 首先是全量更新所有的属性
- 如果新老 VNode 都有孩子,则递归执行 updateChildren,进行 diff 过程
- 老的 VNode 没孩子,如果新的 VNode 有孩子,则新增这些新孩子节点
- 如果老的 VNode 有孩子,新的 VNode 没孩子,则删除这些老孩子节点
- 如果不符合上面的几种,说明属于更新文本节点 - 如果新的 VNode 不存在,老的 VNode 存在,则调用 destroy 销毁老节点
vue-router
响应式原理
Vue Router与Vue的响应式系统深度集成。当路由变化时,Vue Router会更新一个响应式的currentRoute对象,任何依赖这个对象的组件都会自动重新渲染:
// 简化的响应式实现
class Router {
constructor(Vue) {
// 创建响应式的当前路由对象
this.currentRoute = Vue.observable({
path: '/',
params: {},
query: {}
});
}
updateRoute(route) {
// 更新响应式对象,触发视图更新
Object.assign(this.currentRoute, route);
}
}Vue Router的核心实现原理包括:
- 利用浏览器的Hash API或History API实现前端路由
- 通过路径匹配算法将URL映射到组件
- 使用中间件模式实现路由守卫
- 结合Webpack实现路由懒加载
- 与Vue的响应式系统集成实现视图更新
vuex
- 状态管理
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它使用集中式的存储管理所有组件的状态,并以一种可预测的方式来确保状态以一种可追踪的方式发生变化。 - 核心概念
● State:应用的状态,存储在 Vuex 中的单一状态树。 ● Getters:用于从状态中派生出状态的计算属性。 ● Mutations:唯一能够直接修改状态的函数,必须是同步的。 ● Actions:可以包含异步操作的函数,用于触发 mutations。 ● Modules:支持将状态、getter、mutation 和 action 划分到模块中,以管理大型应用。 - 数据流
Vuex 的数据流遵循单向数据流的原则: - 组件通过 mapState 获取 state。
- 组件通过 dispatch 触发 action。
- action 可以调用 mutations,通过 commit 提交变更。
- 变更会直接影响到 state,更新后,依赖于 state 的组件会自动更新。
- 实现细节
● Vuex 使用 Vue 的响应式系统,确保状态变化时,所有依赖状态的组件都会重新渲染。 ● 通过 Vue 的 set 方法确保在 Vuex 中添加新属性时仍然是响应式的。