Vue2和Vue3

171 阅读5分钟

Vue基础

1. 对Vue的理解

   Vue是一个用于构建用户界面的开源JavaScript框架,它采用了组件化的思想和响应式的数据绑定机制,使得开发者可以更轻松的构建交互性的web应用程序,Vue的主要特点包括:

  1. 轻量级(渐进式),Vue只关注是视图层,大小只有几十Kb,适合快速开发轻量级应用。
  2. 简单易学:Vue由国人开发,中文文档丰富,学习门槛低
  3. 虚拟DOM:Vue使用虚拟DOM来优化DOM操作,提高了应用性能。
  4. 组建化:Vue的组件系统使得HTML,CSS和JavaScript可以封装成可复用的组件,提高了开发效率和代码复用率。
  5. 响应式:Vue通过Object.defineProperty和Proxy实现数据的响应式,当数据变化时,视图会自动更新

2. Vue3和Vue2的区别

  1.    Vue3更注重模块上的拆分,在Vue2中无法单独使用部分模块,需要引入完整的Vuejs(例如只想使用响应式部分,但是需要引入完整的Vuejs),Vue3中的模块之间耦合度低,模块可以独立使用。拆分模块
  2.    Vue2中很多方法挂载到了实例中导致没有使用也会被打包(还有很多组件也是一样)。通过构建工具Tree-shaking机制实现按需引入,减少用户打包后体积。重写API
  3.    Vue3允许自定义渲染器,扩展能力强。不会发生以前的事情,改写Vue源码改造渲染方式。扩展更方便
  4.    在Vue2的时候使用defineProperty来进行数据的劫持,需要对属性进行重写添加getter及setter性能差
  5.    当新增属性和删除属性时无法监控变化。需要通过$set,$delete来实现
  6.    数组不采用defineProperty来进行持戒(浪费性能,对所有索引进行劫持会造成性能浪费)需要对数组单独处理
  7.    Diff算法也进行了重写
  8.    Vue3模版编译优化,采用PatchFlags优化动态节点,采用BlockTree进行靶向更新等。
  9.    相比Vue2来说Vue3新增了很多新的特性(CompostionAPI ,Teleport,Fragments)

3. Vue3为什么使用proxy

  1. proxy可以代理整个对象,defineproperty只代理对象上的某个属性
  2. proxy对代理对象的监听更加丰富
  3. proxy代理对象会生成新的对象,不会修改被代理对象本身
  4. proxy不兼容ie浏览器

4. Vue3中CompositionAPI的优势是?

  1. Vue2中采用的是OptionsAPI,用户提供的data,props,methods,computed,watch 等属性(用户编写复杂业务逻辑会出现反复横跳问题)
  2. Vue2中所有的属性都是通过this访问,this存在指向明确问题
  3. Vue2中很多未使用方法或属性依旧会被打包,并且所有全局API都在Vue对象上公开。CompositonAPI对 tree-shaking更加友好,代码也更容易压缩。
  4. 组件逻辑共享问题,Vue2采用mixins实现组件之间的逻辑共享;但是会有数据来源不明确,命名冲突等问题。Vue3采用CompostionAPI提供公共逻辑非常方便
  5. 简单的组件仍然可以采用OptionsAPI进行编写,CompositonAPI在复杂的逻辑中有着明显的优势

4. new Vue() 这个过程中究竟做了些什么?

  • 在new Vue() 的时候内部会进行初始化操作。
  • 内部会初始化组件绑定的事件,初始化组件的父子关系 $parent $children $root
  • 初始化响应式数据data、computed、props、watch、method。同时也初始化了provide 和inject 方法。内部会对数据进行劫持 对象采用defineProperty数组采用方法重写。
  • 在看一下用户是否传入了el属性和template 或者 render。render的优先级更高,如果用户写的是template,会做模版编译(三部曲)。最终就拿到了render函数
  • 内部挂载的时候会产生一个watcher,会调用render函数会触发依赖收集。内部还会给所有的响应式数据增加dep属性,让属性记录当前的watcher(用户后续修改的时候可以触发watcher 重新渲染)
  • Vue更新的时候采用虚拟DOM的方式进行diff算法更新

5. Vue.extend 方法的作用?

5.1. Vue.extend概念

   使用Vue构造器,创建一个“子类”。参数是一个包含组件选项的对象。

   data选项是特列,需要注意 在 Vue.extend() 中它必须时函数

5.2.分析

   所有的组件创建时都会调用Vue.extend 方法进行创建

   有了此方法我们可以手动挂载组件

   后端存储的字符串模版我们可以通过Vue.extend方法将其进行渲染,但是需要引入编译时。

6. Vue组件data 为什么必须是个函数?

   根实例对象data可以是对象也可以是函数“单例”,不会产生数据污染情况

   组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。所以需要通过工厂函数返回全新data作为组件的数据源

7. 组件中些name选项有哪些好处及作用?

  1.  增加name 选项会在components属性中增加组件本身,实现组件的递归调用。
  2. 可以标识组件的具体名称方便调试和查找对应的组件。
  3.  children.filter(item=>item.children.filter(item => item.options.name  === 'xxx')
  4.  Sub.options.components[name] = Sub // 子组件会通过那么属性,将自己也注册到组件中

10. Vue.use 是干什么的?

1.use 概念

   安装Vue.js 插件。如果插件是一个对象,必须提供install 方法。如果插件是一个函数,它会被作为install安装。install 方法调用时,会将Vue作为参数传入,这样插件中就不再需要依赖Vue了。

2.插件的功能

   添加全局指令、全局过滤器、全局组件    通过全局混入来添加一些组件选项。    添加vue 实例方法,通过把它们添加到vue.prototype 上实现。

11. Vue.mixin 的使用场景和原理

11.1.Vue.mixin 概念

mixin可以用来扩展组件,将公共逻辑进行抽离。在需要该逻辑时进行“混入”,采用策略模式针对不同的属性进行合并。如果混入的数据和本身组件中的数据冲突,会采用“就近原则”以组件的数据为准。

mixin 有很多缺陷 “命名冲突问题”、“数据来源问题”,Vue3 采用 CompositionAPI 提取公共逻辑非常方便。

11.2.混入方式

   在vue中我们可以局部混入跟全局混入。一般情况下全局混入用于编写插件。局部混入用于复用逻辑。

12.3.mixin 合并策略

  •    核心就是:对象的合并处理。
  •    props、methods、inject、computed 同名时会被替换
  •    data 会被合并
  •    生命周期和watch会被合并成队列
  •    components 、directives 、filters 会在原型链上叠加
  •    组件的扩展除了 mixin 之外还有一个属性叫 extends,但是不怎么常用

13. Vue中slot是如何实现的?什么时候使用它?

13.1.什么是插槽?

   插槽设计来源于Web Components规范草案,利用slot 进行占位,在使用组件时,组件标签内部内容会分发到对应的slot中。

13.2.什么时候使用它

   通过插槽可以让用户更好的对组件进行扩展和定制化。可以通过具名插槽指定渲染的位置。常用的组件例如:弹框组件、布局组件、表格组件、树组件。。。

13.3.插槽的分类

   默认插槽、具名插槽、作用域插槽

14. computed 和 watch 区别

computed

   计算属性仅当用户取值时才会执行对应的方法。

   computed属性具备缓存的,依赖的值不发生变化,对其取值时计算属性方法不会重新执行。

   计算属性可以简化模版中复杂表达式。

   计算属性中不支持异步逻辑。

   computed属性是可以再模版中使用的。

watch

   watch则是监控值的变化,当值发生变化时调用对应的回调函数。经常用于监控某个值的变化,进行一些操作

computed、methods、watch 之间区别

1.computed vs methods

computed 是有缓存的

methods 没有缓存

2.computed vs watch 区别

Watch 是监听,数据或者路由发生了改变才可以响应(执行)

Computed 计算某一个属性的改变,如果某一个值改变了,计算属性会监听到进行返回

Watch 是当前监听到数据了,才会执行内部代码

watch 和watchEffect的区别?

  • watchEffect 立即运行一个函数,然后被动地追踪它的依赖,当这些依赖改变时重新执行该函数。
  • watch监听一个或多个响应式数据源并在数据源变化时调用一个回调函数。

Vue状态管理

1. 既然Vue通过数据劫持可以精准探测数据变化,为什么还需要虚拟DOM进行diff监测差异

   Vue内部设计原因导致,vue设计的是每个组件一个watcher(渲染watcher),没有采用一个属性对应一个watcher。这样会导致大量watcher的产生而且浪费内存,如果颗粒度低也无法精准监测变化。所以采用diff算法 + 组件级 watcher。

2. 响应式数据的理解

2.1. 如何实现响应式数据

数组和对象类型当值变化时如何劫持到。对象内部通过defineReactive 方法,使用Object.defineproperty将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。多层对象是通过递归来实现劫持。Vue3则采用proxy

2.2Vue2处理缺陷

  1. 在vue2的时候使用defineproperty来进行数据的劫持,需要对属性进行重新添加getter及setter性能差。
  2. 当新增属性和删除属性时无法监控变化。需要set,set,delete 实现。
  3. 对于Es6中新产生的Map、Set这些数据结构不支持

3. Vue中如何监测数组变化?

实现数组劫持

  1. 数组考虑性能原因没有用defineproperty 对数组的每一项进行拦截,而是选择重写数组(push,shift,pop,splice,unshift,sort,reverse)方法。
  2. 数组中如果是对象数据也会进行递归劫持

数组的缺点:

数组的索引和长度变化是无法监控到到的

4. ref 和 reactive 区别

ref和reactive 是Vue3 数据响应式中非常重要的两个概念。

reactive用于处理对象类型的属性响应式。底层采用的是 new Proxy()

ref 通常用于处理单值的响应式,ref主要解决原始值的响应式问题。底层采用的是Object.defineproperty

props和data 优先级谁高

props = > methods => data = > computed => watch

5. VueX的个人理解

5.1.概念

   Vuex 是一个专为Vue.js 应用程序开发的状态管理模式。采用集中式存储管理应用的所有组件的状态。核心就是解决数据的共享。

   以相应的规则保证状态以一种可预测的方式发生变化

5.2.状态修改

   组件中commit() -> mutation -> 

   组件中dispatch() ->action(为了解决接口复用的问题,封装公共的逻辑) - commit() -> mutation 修改状态

5.3.缺点

   Vuex中store只有一份,复杂的数据需要依赖于模块,Vuex状态是一个树状结构,最终会将模块的状态挂载到根模块上

6. 如何监听Vuex中数据的变化

   通过watch 监控Vuex中状态变化

   通过store.subscribe监控状态变化

7. 页面刷新后Vuex的数据丢失怎么解决?

   每次获取数据前Vuex数据是否存在,不存在则发送请求重新获取数据,存储到Vuex中。    采用Vuex持久化插件,将数据存储到localStorage 或者 sessiStoreage中 Vuex持久化存储

8. mutation 和action 的区别

   在action中可以处理异步逻辑,可以获取数据后将结果提交给 mutation ,mutation 中则是修改state

   在action中可以多次进行commit操作,包括action中也可以调用action

   在非mutation中修改数据,在严格模式下会发生异常

   dispatch时会将action包装成promise,而mutation则没有进行包装

9. Vuex的module 什么情况下会使用

使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得复杂时,store对象就有可能变得相当臃肿。

Vuex允许我们将store分割成模块(module)。每个模块拥有自己的state,mutation,action,getter,甚至是嵌套子模块

生命周期

1. Vue2生命周期

主要的生命周期有:创建前后,挂载前后,更新前后,销毁前后

常用的生命周期钩子:

  1. mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等[初始化操作]
  2. beforeDestroy:请求定时器、解绑自定义事件、取消订阅消息等【收尾工作】

beforeCreate 初始化父子关系及事件,数据监测(data observer)之前被调用。用此方法一般编写插件的时候会用到

created 实例已经创建完成之后被调用。在这一步,实例已完成以下配置:数据观,测(data observer),属性和方法等,但是这里没有$el.一般也不咋用。

beforeMount在挂载开始之前被调用:相关的render函数首次被调用。

mounted被新创建的vm.$el 替换,并挂载到实例上去之后调用该钩子。可用于获取DOM元素

beforeUpdate 数据更新时调用,发生在虚拟DOM 重新渲染和打补丁之前。此时修改数据不会再次触发更新方法。

updated 由于数据更改导致的虚拟DOM 重新渲染和打补丁,在这之后调用该钩子。

beforeDestroy 实例销毁之前调用。在这一步,实例仍然完全可用。

destroyed Vue实例销毁后调用。调用后,Vue 实例指示的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不会被调用。

Keep-alive(active 和 deactivated)

Vue3生命周期 

beforeDestroy 改为 beforeUnmount

destroyed 改为 Unmounted

2. nextTick的理解

在下一次DOM更新结束后执行其指定的回调。 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数执行。

  1. Vue中视图更新是异步的,使用nextTick 方法可以保证用户定义的逻辑在更新之后执行。
  2. 可以用于获取更新后的DOM,多次调用nextTick会被合并

3. keep-alive 在哪里使用

1.概念

keep-alive 是Vue中的内置组件,能在组件切换过程会缓存组件的实例,而不是销毁它们。在组件再次更新激活时可以通过缓存的实例拿到之前渲染的DOM进行渲染,无需重新生成节点。

4 发送请求在created还是mounted?

这个问题具体要看项目和业务的情况了,因为组件的加载顺序是,父组件引入了子组件,那么先执行父的前3个周期,再执行子的前4个生命周期,那么如果的业务是父组件引入了子组件,并且优先加载子组件的数据,那么在父组件中的请求要放在mouted中,如果当前组件没有依赖关系那么放在哪个生命周期中请求都可以。

5. 为什么发送请求不在beforeCreate里,beforeCreate和created有什么区别?

因为:如果请求是在methods封装好了,在beforeCreate调用的时候,beforeCreate阶段是拿不到methods里面的方法(beforeCreate阶段获取不到data、methods数据,会报错了)

beforeCreate和created 区别 beforeCreate阶段没有data,methods created中有data,methods

6. 在created中如何获取dom

beforeCreate和created 中拿不到dom 的,

只要写异步代码,获取dom是在异步中获取的就可以了.例如:setTimeout,promise

  1. 只要执行异步代码,获取dom是在异步中获取的,就可以了。例如;setTimout,请求,promise ,async await等等
  2. 使用vue系统内置的this,$nextTick
setTimeout(() =>{
    console.log(document.getElementById('home'))
})

axios({
    url
}).then(res =>{
    console.log(res,document.getElementById('home'))
})

this.$nextTick({
    console.log(res,document.getElementById('home'))
})

7.一旦进入组件会执行哪些生命周期?

beforeCreate、created、beforeMount、mounted

8.第二次或者第N次进去会执行哪些生命周期

如果当前组件加入了keep-alive,只会执行一个生命周期 activeated

如果没有加入keep-alive 会执行 beforeCreate、created、beforeMount、mounted

9.父组件引入子组件,那么生命周期执行的顺序是?

先执行 父:beforeCreate、created、beforeMount 再执行 子:beforeCreate、created、beforeMount、mounted 再执行 父:mounted

10.加入keep-alive会执行哪些生命周期

如果使用了keep-alive组件会新增(activated,deactivated)

如果当前组件加入了keep-alive第一次进入这个组件会执行5个生命周期(beforeCreate、created、beforeMount、mounted)

11.你在什么情况下用过哪些生命周期?说一说生命周期使用场景

created 单组件请求 mounted 同步可以获取dom,如果先子组件请求后父组件请求 actived 判断id是否相等,如果不相等发起请求 destroyed 关闭页面记录视频播放的时间,初始化的时候从上一次的历史开始播放

Vue指令

1. v-show和 v-if 怎么理解

  1. v-if 在编译时会编译成三元表达式,条件不成立不会渲染当前指令所在节点的dom元素
  2. v-show原理是修改元素的css属性display:none来决定是显示还是隐藏
  • v-if 可以阻断内部代码是否执行,如果条件不成立不会执行内部逻辑
  • 如果页面逻辑在第一次加载的时候已经被确认后续不会频繁更改则采用v-if

v-if比v-show优先级更高

2. v-if 和 v-for 哪个优先级更高

v-if 和 v-for 避免在同一个标签中使用。如果遇到需要同时使用时可以考虑写成计算属性的方式

在Vue2中解析时,先解析v-for再解析v-if。会导致先循环后对每一项进行判断,浪费性能

在Vue3中 v-if的优先级高于v-for

3. v-once使用场景有哪些

v-once是Vue中内置指令,只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

4. v-model 双向绑定的理解,及其实现原理

4.1.双向绑定的概念

   vue中双向绑定靠的是指令v-model,可以绑定一个动态值到视图上,同时修改视图能改变数据对应的值(能修改的视图就是表单组件)经常会听到一句话:v-model 是 value + input 的语法糖

4.2.表单元素中v-model

   内部会根据标签的不同解析出不同的语法。并且这里有“额外”的处理逻辑

   例如 文本框会被解析成 value + input 事件

   例如 复选框会被解析成 checked + change 事件

4.3.组件中的v-model

   组件上的v-model默认会利用名为 value 的prop 和 名为 input 的事件。对于组件而言v-model 就是value + input 的语法糖。可用于组件中数据的双向绑定。

5. watch 和 watchEffect的区别

watch :既要指明监视的属性,也要指明监视的回调。

watchEffect:不用指明监视哪个属性,回调中用到了哪个属性,就监视哪个属性。

watchEffect有点像computed:但computed注重计算的值,要返回值。watchEffect不用返回值

6. Vue-Router有几种钩子函数,具体时什么及执行流程是怎样的

导航被触发。

在失活的组件里调用beforeRouteleave 守卫

调用全局的beforeEach守卫

在重用的组件里调用beforeRouteUpdate 守卫

在路由配置里调用beforeEnter

解析异步路由组件

在被激活的组件里调用beforeRouteEnter

调用全局的beforeResolve

导航被确认

调用全局的afterEach钩子

触发DOM更新

调用beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入

routerrouter和route router 不仅包含当前路由还包含整个路由的属性和方法 route包含当前路由对象

7.Vue 的动态路由

要在路由配置里设置meata属性,扩展权限相关的字段,在路由导航守卫里通过判断这个权限标识,实现路由的动态增加和跳转。

根据用户登陆的账号,返回用户角色

前端再根据角色,跟路由表的meta.role进行匹配

把匹配搭配的路由可形成可访问的路由

8. Vue项目本地开发完成后部署到服务器后报404是什么原因呢?

   history模式刷新时会像服务端发起请求,服务端无法响应到对应的资源,所以会出现404问题。

9. Vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?

常见的权限控制
  1.    登陆权限:用户登陆后返回Token,前端将Token保存到本地,作为用户登陆的凭证,每次发送请求时会携带Token,后端会对Token进行验证。当页面刷新时我们可以使用Token来获得用户权限。
  2.    访问权限:根据用户是否登陆判断能否访问某个页面,通过路由守卫判断用户是否有此权限。
  3.    页面权限:前端配置的路由分为两部分“通用路由配置”和“需要权限的路由配置”。在权限路由中增加访问权限meta(备注)。用户登陆后可得到对应的权限列表,通过权限列表筛查出对应符合的路由信息,最后通过addRoutes方法,动态添加路由。
  4.    按钮权限:按钮权限一般采用自定义指令实现,当用户登陆时后端会返回对应的按钮权限,在按钮上使用此指令,指令内部会判断用户是否有此按钮权限,如果没有则会移除按钮。

elementUI是怎么做表单验证的

  1. 在表单中加入rules属性,然后再data里写校验规则
  2. 内部添加规则
  3. 自定义函数校验