前端面试题 --- Vue部分

2022-10-31 15:33:09 浏览数 (2)

转载链接:https://blog.csdn.net/qq_54753561/article/details/122149197

核心原理部分

mvc mvvm和mvp的区别

MVVM 就是 Model-View-ViewModel 的缩写,MVVM 将视图和业务逻辑分开。 View:视图层,Model 数据模型,而 ViewModel 是把两者建立通信的桥梁。 在 MVVM 框架下,View 和 Model 之间没有直接的联系,而是通过 ViewModel 进行交互。View 和 ViewModel 之间以及 Model 和 ViewModel 之间的交互都是双向的,因此 view 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反映到 View 上。可以说它们两者是实时更新的,互相影响。 ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,因此开发者只需要关注业务逻辑,不需要手动操作 DOM,也不需要关注数据状态的同步问题,这些都由 MVVM 统一管理, 整体看来,MVVM比MVC精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作DOM元素。因为在MVVM中,View不知道Model的存在,Model和ViewModel也观察不到View,这种低耦合模式提高代码的可重用性。 【优点】 数据源和视图实现了双向绑定,很好的做到了数据的一致性 相比于mvp各层的耦合度更低,一个viewmodel层可以给多个view层共用。 【缺点】 因为使用了dataBinding,增加了大量的内存开销,增加了程序的编译时间,项目越大内存开销越大。数据绑定使得 Bug 很难被调试。你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题 MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范 - Model(模型):是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据 - View(视图):是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的 - Controller(控制器):是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据,也可以将Model的数据用View显示出来 【优点】 耦合性低,方便维护,可以利于分工协作 重用性高 【缺点】 使得项目架构变得复杂,对开发人员要求高 MVP MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示,在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的

Vue底层实现原理

vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter和getter,在数据变动时发布消息给订阅者,触发相应的监听回调 Vue是一个典型的MVVM框架,模型(Model)只是普通的javascript对象,修改它则-视图(View)会自动更新。这种设计让状态管理变得非常简单而直观 Observer(数据监听器) : Observer的核心是通过Object.defineProprtty()来监听数据的变动,这个函数内部可以定义setter和getter,每当数据发生变化,就会触发setter。这时候Observer就要通知订阅者,订阅者就是Watcher Compile(指令解析器) : Compile主要做的事情是解析模板指令,将模板中变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加鉴定数据的订阅者,一旦数据有变动,收到通知,更新-视图 Watcher(订阅者) : Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情是:

  1. 在自身实例化时往属性订阅器(dep)里面添加自己
  2. 自身必须有一个update()方法
  3. 待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调

Vue模版编译原理。

vue中的模板template无法被浏览器解析并渲染,因为这不属于浏览器的标准,不是正确的HTML语法,所有需要将template转化成一个JavaScript函数,这样浏览器就可以执行这一个函数并渲染出对应的HTML元素,就可以让视图跑起来了,这一个转化的过程,就成为模板编译。 Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步 第一步是将 模板字符串 转换成 element ASTs(解析器) 第二步是对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器) 第三步是 使用 element ASTs 生成 render 函数代码字符串(代码生成器)

vue虚拟dom,diff算法

虚拟 DOM,其实就是用对象的方式取代真实的 DOM 操作,把真实的 DOM 操作放在内存当中,在内存中的对象里做模拟操作。当页面打开时浏览器会解析 HTML 元素,构建一颗 DOM 树,将状态全部保存起来,在内存当中模拟我们真实的 DOM 操作,操作完后又会生成一颗 dom 树,两颗 DOM 树进行比较,根据 diff 算法比较两颗 DOM 树不同的地方,只渲染一次不同的地方。 (个人理解)虚拟dom他不并不是真实的 dom ,是根据模板生成一个js对象(使用createElement,方法),根据这个js对象再去生成真实的dom,对复杂的文档DOM结构,提供一种方便的工具,进行最小化的DOM操作 ,是可以快速的渲染和高效的更新元素,提高浏览器的性能, 例如,一个 ul 标签下很多个 li 标签,其中只有一个 li 有变化,这种情况下如果使用新的 ul 去替代旧的 ul,因为这些不必要的 DOM 操作而造成了性能上的浪费,但是如果直接使用虚拟节点覆盖旧节点的话,减少了很多的不必要的 DOM 操作。 我们在渲染页面的时候 会对新的虚拟dom和旧的虚拟dom进行对比 只渲染不同的地方,而不再是像之前只要发生变化,全部的真实dom都要重新渲染,所以提高了渲染的效率。 缺点:首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢

diff算法

diff 算法是一种通过同层的树节点进行比较的高效算法,比较方式:diff整体策略为:深度优先,同层比较 diff算法 当data发生改变 会根据新的数据生成一个新的虚拟dom ,新的虚拟dom和旧的虚拟dom进行对比,这个对比的过程就是diff算法,会找到不同地方,只去渲染不同的地方,总的来说就是减少DOM,重绘和回流。

为什么要用虚拟DOM来描述真实的DOM呢?

创建真实DOM成本比较高,如果用 js对象来描述一个dom节点,成本比较低,另外我们在频繁操作dom是一种比较大的开销。所以建议用虚拟dom来描述真实dom。

响应式原理

代码语言:javascript复制
  1. vue的响应式原理?
  2. 什么是响应式,“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。
  3. Vue 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。
  4. Object.defineProperty怎么用, 三个参数?,有什么作用啊?
  5. Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
  6. Object.defineProperty(obj, prop, {})
  7. obj:需要定义属性的对象
  8. prop:需要定义的属性
  9. {}:要定义或修改的属性描述符。
  10. value: "18", // 设置默认值得
  11. enumerable: true, //这一句控制属性可以枚举 enumerable 改为true 就可以参与遍历了 默认值false
  12. writable: true, // 控制属性可以被修改 默认值false
  13. configurable: true, // 控制属性可以被删除 默认值false
  14. get // 当有人读取 prop 的时候 get函数就会调用,并且返回就是 sss 的值
  15. set // 当有人修改 prop 的时候 set函数就会调用, 有个参数这个参数就是修改后的值
  16. Object.defineProperty 能定义symbol类型吗?
  17. 在ES6中,由于 Symbol类型的特殊性,用Symbol类型的值来做对象的key与常规的定义或修改不同,而Object.defineProperty 是定 义key为Symbol的属性的方法之一。
  18. vue2和vue3的响应式原理都有什么区别呢?
  19. vue2 用的是 Object.defindProperty 但是vue3用的是Proxy
  20. Object.defindProperty虽然能够实现双向绑定了,但是还是有缺点,只能对对象的属性进行数据劫持,所以会深度遍历整个对象,不管层级有多深,只要数组中嵌套有对象,就能监听到对象的数据变化无法监听到数组的变化,Proxy就没有这个问题,可以监听整个对象的数据变化,所以用vue3.0会用Proxy代替definedProperty。
  21. 上面就是一个典型的例子,当我们点击按钮想要根据数组 arr 的下标改变其元素的时候,你会发现 data 中的数据改变了,但是页面中的数据并没有改变。
  22. 我会用 this.$set( target, key, value ) 来解决
  23. 参数:
  24. {Object | Array} target
  25. {string | number} propertyName/index
  26. {any} value
  27. 第一参数时指定要修改的数据 (target)
  28. 第二个参数就是你要设置数据的下标或者是属性名
  29. 第三个参数就是现在要修改的数据 (重新赋的值)
  30. 改变/添加 对象属性的时候:this.$set(data 实例,"属性名(添加的属性名)","属性值(添加的属性值)")
  31. 改变/添加 数组属性的时候:this.$set(data 实例,数组下标,"改变后的元素(添加的元素)")
  32. 原因 :vue在创建实例的时候把data深度遍历所有属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。让 Vue 追踪依赖,在属性被访问和修改时通知变化。所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。
  33. 为什么要用 this.set 呢?this.set是干什么的?
  34. 当你发现你给对象加了一个属性,在控制台能打印出来,但是却没有更新到视图上时,也许这个时候就需要用到this.set()这个方法了,简单来说this.set的功能就是解决这个问题的啦。官方解释:向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如 this.myObject.newProperty = 'hi'),你会发现vue官网是vue.set,vue.set的用法
  35. 那 Vue.set 和 this.$set 有什么区别 ?
  36. Vue.set( ) 是将 set 函数绑定在 Vue 构造函数上,this.$set() 是将 set 函数绑定在 Vue原型上。

vue双向数据绑定原理?

是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调从而达到数据和视图同步。

主要分为四部分

1、 observer 主要是负责对Vue数据进行递归遍历,使其数据拥有get和set方法,当有数据给某个对象值赋值,就触发 setter 就监听到数据的变化了。( 如有变动可拿到最新值并通知订阅者 ) 2、compile 指令解析器负责绑定数据和指令解析。将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数。一旦数据有变动,收到通知,更新视图 3、 订阅者 watcher :Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是 负责数据监听,当数据发生改变,能调用自身的update()方法,并触发Compile中绑定的更新函数 4、实现一个订阅器 dep:采用发布者订阅者模式,用来收集订阅者的 watcher,对监听器 observer 和订阅者 watcher 进行统一管理

vue3的Proxy 相比于 vue2的defineProperty 的优势

在vue3 中 Vue3是通过Object.define.proxy 对对象进行代理,从而实现数据劫持。使用Proxy 的好处是它可以完美的监听到任何方式的数据改变,唯一的缺点是兼容性的问题,因为 Proxy 是 ES6 的语法 Vue3.0 摒弃了 Object.defineProperty,改为基于 Proxy 的观察者机制探索。 首先说一下 Object.defineProperty 的缺点: ① Object.defineProperty 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实施响应。 this.$set()解决Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue2.X 里,是通过递归 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象才是更好的选择。 而要取代它的 Proxy 有以下两个优点 可以劫持整个对象,并返回一个新对象。有多种劫持操作(13 种) 补充: Proxy 用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。mdn Proxy 是 ES6 新增的一个属性,翻译过来的意思就是代理,用在这里表示由它来“代理”某些操作。Proxy 让我们能够以简洁易懂的方式控制外部对象的访问,其功能非常类似于设计模式中的代理模式。 1、vue 中数组中的某个对象的属性发生变化,视图不更新如何解决? Object.defineProperty 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实施响应。this.$set()解决 问题原因:因为 vue 的检查机制在进行视图更新时无法监测 数组中的对象的某个属性值的变化。解决方案如下 方案一:利用 this.set(this.obj,key,val) 例:this.set(this.obj,‘k1’,‘v1’) 方案二:就利用 Object.assign({},this.obj)创建新对象 如果是数组就 Object.assign([],this.obj) 如果是对象就 Object.assign({},this.obj)。

vue.js的两个核心是什么

灵活的组件应用,高效的数据绑定

渐进式框架的理解,vue数据驱动的理解

渐进式代表的含义是:主张最少——它是一个轻量级框架,只做了自己该做的事,没有做不该做的事 每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主张,主张有强有弱,它的强势程度会影响在业务开发中的使用方式。 这里的vue数据驱动的是视图,也就是DOM元素,指的是让DOM的内容随着数据的改变而改变框架的理解

Vue的SSR是什么?有什么好处?

SSR全称Server Side Render

  • 有利于SEO:由于是在服务端,将数据填充进HTML之后再推送到浏览器,所以有利于SEO的爬取
  • 首屏渲染快

SSR的缺点:

  • 开发条件会受到限制,服务器端渲染只支持beforeCreate和created两个钩子;
  • 当需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境;
  • 更多的服务端负载。

vue3.0 与 vue2.0 的区别

1.性能提升 更小巧,更快速;支持摇树优化。支持 Fragments (支持多个根节点)和跨组件渲染;支持自定义渲染器。 2.API 变动 Vue2使用 选项类型API(Options API) 对比Vue3 合成型API(Composition API) optionsApi 使用传统api中,新增一个需求,要在data,methods,computed中修改 compositionApi 我们可以更加优雅的组织我们的代码,函数,让我们的代码更加有序的组合在一起 3.重写虚拟 DOM (Virtual DOM Rewrite) 随着虚拟 DOM 重写,减少 运行时(runtime)开销。重写将包括更有效的代码来创建虚拟节点。 vue3 没有了过滤器 双向数据绑定 从 Object.defineProperty() 变成了 proxy,通过下标修改数组变化了视图数据没发生变化 this.$set() vue3不需要

  1. 双向数据绑定原理发生了改变,使用proxy替换Object.defineProerty,使用Proxy的优势:
  • 可直接监听数组类型的数据变
  • 监听的目标为对象本身,不需要像Object.defineProperty一样遍历每个属性,有一定的性能提升
  • 可直接实现对象属性的新增/删除

setup 函数 3.0新加入了TypeScript以及PWA支持 默认使用懒加载 可以不用加上key vue3 的watch监听可以进行终止监听

生命周期有了一定的区别 Vue2--------------vue3

代码语言:javascript复制
  1. beforeCreate -> setup() 开始创建组件之前,创建的是data和method
  2. created -> setup()
  3. beforeMount -> onBeforeMount 组件挂载到节点上之前执行的函数。
  4. mounted -> onMounted 组件挂载完成后执行的函数
  5. beforeUpdate -> onBeforeUpdate 组件更新之前执行的函数。
  6. updated -> onUpdated 组件更新完成之后执行的函数。
  7. beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。
  8. destroyed -> onUnmounted 组件卸载之前执行的函数。
  9. activated -> onActivated 组件卸载完成后执行的函数
  10. deactivated -> onDeactivated

vue 与 react的区别

相同点 :

  • 都是使用了虚拟dom
  • 组件化开发
  • 父子之间通信单项数据流
  • 都支持服务端渲染

不同点:

  • reacct 的jsx vue的是 template
  • 数据变化,react 手动 setState vue自动响应式处理 proxy object.DefineProperty
  • react 单向数据流 ,vue双向数据流
  • react 的 redux mobx vue 的vuex 。pinia

vue生命周期(11个进行扩展延伸)

声明周期那几个?每一个生命周期的特点,可以做什么。

代码语言:javascript复制
  1. beforeCreate() 创建前,这个时候data中的数据,还未定义,所以不能使用
  2. created()创建后 最早开始使用 data和methods中数据的钩子函数
  3. beforeMount()挂载前 指令已经解析完毕内存中已经生成dom树,但是尚未挂载到页面中去,此时页面还是旧的。
  4. mounted()挂载后 dom已经渲染完毕,此时页面和内存中都是最新的数据,最早可以操作DOM元素钩子函数
  5. beforeUpdate()更新前 当视图层的数据发生改变会执行这个钩子 内存更新,但是DOM节点还未更新,数据没有与页面同步
  6. updated()更新后 数据更新完成以后触发的方法,DOM节点已经更新
  7. beforeDestroy()即将销毁 data和methods中的数据此时还是可以使用的,可以做一些释放内存的操作
  8. destroyed()销毁完毕 组件已经全部销毁,Vue实例已经被销毁,Vue中的任何数据都不可用
  9. 其他三个:
  10. activated 被 keep-alive 缓存的组件激活时调用。
  11. deactivated 被 keep-alive 缓存的组件停用时调用。
  12. errorCaptured 2.5.0 新增当捕获一个来自子孙组件的错误时被调用
  13. Vue3.0中的生命周期做了一些改动:
  14. beforeCreate -> setup() 开始创建组件之前,创建的是data和method
  15. created -> setup()
  16. beforeMount -> onBeforeMount 组件挂载到节点上之前执行的函数。
  17. mounted -> onMounted 组件挂载完成后执行的函数
  18. beforeUpdate -> onBeforeUpdate 组件更新之前执行的函数。
  19. Update - > onUpdated组件更新完成之后执行的函数。
  20. beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。
  21. destroyed -> onUnmounted 组件卸载之前执行的函数。
  22. - vue的实例加载完成是在哪个声明周期完成呢
  23. beforeCreate
  24. - vue的dom挂载完成是在哪个声命周期里呢
  25. mounted
  26. 、created mounted 的区别?
  27. created 模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
  28. mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
  29. 、怎么在created里面操作dom?
  30. this.$nextTick()将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
  31. 可以根据打印的顺序看到,在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作并无作用,而在created()里使用this.nextTick()可以等待dom生成以后再来获取dom对象,而通过this.nextTick()获取到的值为dom更新之后的值
  32. setTimeout(() => {
  33. console.log(this.$refs.button);
  34. });
  35. 、那 setTimeout this.$nextTick 什么区别呢?
  36. setTimeout 将同步转换为异步 this.$nextTick
  37. this.$nextTick 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,
  38. 、this.$nextTick()是宏任务还是微任务啊?
  39. 优先是Promise.then方法,是个微任务,这样可以避免多一次队列,进而少一次UI渲染,节省性能
  40. 、a页面跳转到b页面周期执行
  41. 页面a----beforeCreate undefined
  42. 页面a----created
  43. 页面a----beforeMount
  44. 页面a----mounted
  45. 页面b----beforeCreate undefined
  46. 页面b----created
  47. 页面b----beforeMount
  48. 页面a----beforeDestroy
  49. 页面a----destroyed
  50. 页面b----mounted
  51. 、组件 和 页面周期 的执行顺序
  52. - 页面beforeCreate undefined
  53. - 页面created
  54. - 页面beforeMount
  55. - 组件beforeCreate undefined
  56. - 组件created
  57. - 组件beforeMount
  58. - 组件mounted
  59. - 页面mounted
  60. 、父子组件生命周期执行顺序
  61. 加载渲染过程
  62. 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
  63. 代码更新过程
  64. 父beforeUpdate->子beforeUpdate->子updated->父updated
  65. 代码销毁过程
  66. 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
  67. 代码常用钩子简易版
  68. 父create->子created->子mounted->父mounted
  69. 、补充单一组件钩子执行顺序
  70. activated, deactivated 是组件keep-alive时独有的钩子
  71. beforeCreate
  72. created
  73. beforeMount
  74. mounted
  75. beforeUpdate
  76. updated
  77. activated
  78. deactivated
  79. beforeDestroy
  80. destroyed
  81. errorCaptured
  82. watch
  83. 仅仅是数据发生改变的时候会侦听到;
  84. 只是会检测到你写在watch里的那些属性,没写的就不会触发。
  85. updated
  86. 执行到它的时候时候是数据发生变化且界面更新完毕;
  87. 不能监听到路由数据(例如网址中的参数);
  88. 所有的数据发生变化都会调用(消耗性能);
  89. 每次触发的代码都是同一个
  90. computed
  91. 、监控自己定义的变量,不用再data里面声明,函数名就是变量名
  92. 、适合多个变量或对象进行处理后返回一个值(结果)。若这多个变量发生只要有一个发生变化,结果都会变化。
  93. 、计算的结果具有缓存,依赖响应式属性变化,响应式属性没有变化,直接从缓存中读取结果。
  94. 、在内部函数调用的时候不用加()。
  95. 、必须用return返回
  96. 、不要在computed 中对data中的数据进行赋值操作,这会形成一个死循环。
  97. methods不会被缓存:方法每次都会去重新计算结果。methods 方法表示一个具体的操作,主要书写业务逻辑;
  98. 使用 methods 方法编写的逻辑运算,在调用时 add() 一定要加“()”,methods 里面写的多位方法,调用方法一定要有()。methods方法页面刚加载时调用一次,以后只有被调用的时候才会被调用。我们在长度框和宽度框的值输入完以后,点击“ ” methods 方法调用一次。这里很明显我们采用 methods 会更节省资源。
  99. 使用场景?
  100. watch:
  101. 、watch 函数是不需要调用的。
  102. 、重点在于监控,监控数据发生变化的时候,执行回调函数操作。
  103. 、当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch
  104. 、函数名就是你要监听的数据名字
  105. 、监控一些input框值的特殊处理,适合一个数据影响多个数据。
  106. 、数据变化时,执行一些异步操作,或开销比较大的操作
  107. computed:
  108. 在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式
  109. 一个需要的结果受多个数据影响的时候,比如购物车结算金额(受到很多处的价格结算)。
  110. 操作某个属性,执行一些复杂的逻辑,并在多处使用这个结果。
  111. 内部函数中多处要使用到这个结果的。
  112. 、监控自己定义的变量,不用再data里面声明,函数名就是变量名
  113. 、适合多个变量或对象进行处理后返回一个值(结果)。若这多个变量发生只要有一个发生变化,结果都会变化。
  114. 、计算的结果具有缓存,依赖响应式属性变化,响应式属性没有变化,直接从缓存中读取结果。
  115. 、在内部函数调用的时候不用加()。
  116. 、必须用return返回
  117. 、不要在computed 中对data中的数据进行赋值操作,这会形成一个死循环。

Vue与Angular以及React的区别

▍Angular

框架比较成熟完整,过于庞大,上手难; 指令以ng-xxx开头;由谷歌开发和维护; 版本1比较适合PC端开发,版本2在往移动端靠; 不支持低版本浏览器;内置指令和自定义指令; 内置过滤器和自定义过滤器;支持双向数据绑定;

▍Vue

它是一个轻量级框架,其核心库只关注视图层,简单小巧、易学易上手; 指令以v-xxx开头;个人维护项目;适合于移动端开发;不支持低版本浏览器; 内置指令和自定义指令;内置过滤器和自定义过滤器;支持双向数据绑定; 使用DOM模板。中心思想:一切都是组件,组件实例之间可以嵌套;核心库不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载;基于依赖追踪的观察系统,并且异步队列更新。

▍React

依赖虚拟DOM;采用特殊的JSX语法;中心思想:一切都是组件,组件实例之间可以嵌套;核心库不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载。

请描述一下你对webpack的理解?

Webpack Webpack 是一个项目打包工具 可以压缩代码和图片,把浏览器识别不了的代码转化为能识别的,可以启动一个热加载服务器 配置跨域、路径别名、打包分析、cdn映入、去掉console.log、单独打包第三方模块、ie兼容、eslint规范、图片压缩

vue2对比vue3

最大的区别就是:Vue2使用 选项类型API(Options API) 对比Vue3 合成型API(Composition API)

  1. 双向数据绑定原理发生了改变,使用proxy替换Object.defineProerty,使用Proxy的优势:
  • 可直接监听数组类型的数据变
  • 监听的目标为对象本身,不需要像Object.defineProperty一样遍历每个属性,有一定的性能提升
  • 可直接实现对象属性的新增/删除
  1. 默认使用懒加载

在2.x版本里。不管数据多大,都会在一开始就为其创建观察者,在数据很大时,就会造成性能的问题。在3.x中,只会对渲染出来的数据创建观察者,而且3.x的观察者更高效。 3.0新加入了TypeScript以及PWA支持 生命周期有了一定的区别 Vue2--------------vue3 beforeCreate -> setup() 开始创建组件之前,创建的是data和method created -> setup() beforeMount -> onBeforeMount 组件挂载到节点上之前执行的函数。 mounted -> onMounted 组件挂载完成后执行的函数 beforeUpdate -> onBeforeUpdate 组件更新之前执行的函数。 updated -> onUpdated 组件更新完成之后执行的函数。 beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。 destroyed -> onUnmounted 组件卸载之前执行的函数。 activated -> onActivated 组件卸载完成后执行的函数 deactivated -> onDeactivated

一般在哪个生命周期请求异步数据

可以啊钩子函数中的 createdbeforeMountmounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。 在created中最好 能更快获取到服务端数据,减少页面加载时间,用户体验更好; SSR不支持 beforeMount 、mounted 钩子函数,放在 created 中有助于一致性。 mounted 在请求完数据之后需要对 dom 进行操作的时候可以用到

vue中 methods,computed, watch 的区别

computed 是vue中的计算属性,具有缓存性,当他的依赖于值,发生改变的时候才会重新调用 methods 是没有缓存的,只要调用,就会执行,一般结合事件来使用 watch 没有缓存性 监听data中的属性 属性值只要发生变化就会执行 可以利用他的特性做一些异步的操作

created和mounted区别?

created:dom渲染前调用,即通常初始化某些属性值 mounted:在dom渲染后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作

生命周期钩子是如何实现的

Vue 的生命周期钩子核心实现是利用发布订阅模式先把用户传入的的生命周期钩子订阅好(内部采用数组的方式存储)然后在创建组件实例的过程中会一次执行对应的钩子方法(发布)

vuex常问的考点

Vuex严格模式

代码语言:javascript复制
  1. 开启严格模式,仅需在创建 store 的时候传入 strict: true:
  2. const store = new Vuex.Store({
  3. // ...
  4. strict: true
  5. })
  6. 在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。

开发环境与发布环境

不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。

类似于插件,我们可以让构建工具来处理这种情况:

代码语言:javascript复制
  1. const store = createStore({
  2. // ...
  3. strict: process.env.NODE_ENV !== 'production'
  4. })

vuex是什么, state,getters,mutations,actions,modules的用途和用法

vuex是一个状态管理工具,所谓状态的是就是数据,采用集中式存储管所有组件的状态,是为了结局中大型项目一个数据共享的问题。vuex 他可以将数据保存到本地,数据是响应式的,能够保持数据页面的共享,提高开发效率。

好处:

能够在 vuex 中集中管理共享的数据,易于开发和后期维护 可以做状态管理、采用localstorage保存信息、数据一直存储在用户的客户端中 存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步,能够高效地实现组件之间的数据共享,提高开发 效率

vuex核心:

  1. state:vuex的基本数据,数据源存放地,用于定义共享的数据。
  2. geeter:从基本数据派生的数据,相当于state的计算属性
  3. mutation:提交更新数据的方法,唯一 一个可以操作state 中数据的方法,必须是同步的,第一个参数是state,第二个参数是cmmi传过来的数据
  4. action:action是用来做异步操作的,一般用来发请求,在 action 中写入函数,然后在页面中用dispatch调用,然后在 action 中通过commit 去调用 mutation 通过 mutation 去操作state。
  5. modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理

详述Vuex运行机制

运行机制:Vuex提供数据(state),来驱动视图(这里指的是Vue组件),视图通过Dispatch派发Action,在Action中可以进一步做一些异步的操作(例如通过ajax请求后端的接口数据),然后通过Commit提交给Mutations,由Mutations去最终更改state。那么为什么要经过Mutations呢?这是因为我们要在Vue调试工具(Devtools)中记录数据的变化,这样可以通过插件去进行进一步的调试。所以说Mutations中只能是纯同步的操作,如果是有异步操作,那么就需要在Actions中进行处理。如果说没有异步操作,那么可以直接由组件进行Commit操作Mutations。

高级用法辅助函数(语法糖)

mapState,mapActions,mapMutations,mapGetters

  1. 辅助函数可以把vuex中的数据和方法映射到vue组件中。达到简化操作的目的
  2. 如何使用:

Import { mapActions, mapGetters, mapMutations, mapState } from 'vuex' computed(){ ...mapState(['数据名字'])}

Vuex 页面刷新数据丢失怎么解决

需要做 vuex 数据持久化 一般使用本地存储的方案来保存数据 可以自己设计存储方案 也可以使用第三方插件 推荐使用 vuex-persist 插件,它就是为 Vuex 持久化存储而生的一个插件。不需要你手动存取 storage ,而是直接将状态保存至 cookie 或者 localStorage 中

Vuex 为什么要分模块并且加命名空间

模块:由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。方便管理

vue的路由

vue-router(路由原理?路由守卫?)

由于Vue在开发时对路由支持的不足,于是官方补充了vue-router插件。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,实际上就是组件的切换。路由就是SPA(单页应用)的路径管理器。再通俗的说,vue-router就是我们WebApp的链接路径管理系统。 原理 一般源码中,都会用到 window.history 和 location.hash 原理通过改变浏览器地址URL,在不重新请求页面的情况下,更新页面视图,通过BOM中的location对象,其中对象中的location.hash储存的是路由的地址、可以赋值改变其URL的地址。而这会触发hashchange事件,而通过window.addEventListener监听hash值然后去匹配对应的路由、从而渲染页面的组件 1.一种是# hash,在地址中加入#以欺骗浏览器,地址的改变是由于正在进行页内导航 2.一种是h5的history,使用URL的Hash来模拟一个完整的URL

路由有两种模式 hash和history模式 默认是hash vue-router的实现原理(核心):更新视图但不重新请求页面。 1、hash ——即地址栏 URL 中的#符号,它的特点在 于:hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影 响,因此改变 hash 不会重新加载页面。 2、history ——利用了 HTML5 History api 在浏览器中没有# 有浏览器兼容问题 3、history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,否则返回 404 错误。

•一:全局的守卫

无论访问哪一个路径,都会触发全局的钩子函数,位置是调用router的方法 router/index.js

router.beforeEach 全局前置守卫 进入路由之前 router.beforeResolve 全局解析守卫,在beforeRouteEnter调用之后调用 同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用 router.afterEach 全局后置钩子 进入路由之后 你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

二:组件级路由守卫 放在要守卫的组件里,跟data和methods同级

  • beforeRouteEnter 进入路由前,此时实例还没创建,无法获取到zhis
  • beforeRouteUpdate (2.2) 路由复用同一个组件时
  • beforeRouteLeave 离开当前路由,此时可以用来保存数据,或数据初始化,或关闭定时器等等
代码语言:javascript复制
  1. //在组件内部进行配置,这里的函数用法也是和beforeEach一毛一样
  2. const Foo = {
  3. template: `...`,
  4. beforeRouteEnter (to, from, next) {
  5. // 在渲染该组件的对应路由被 confirm 前调用
  6. // 不!能!获取组件实例 `this`
  7. // 因为当守卫执行前,组件实例还没被创建
  8. },
  9. beforeRouteUpdate (to, from, next) {
  10. // 在当前路由改变,但是该组件被复用时调用
  11. // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
  12. // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
  13. // 可以访问组件实例 `this`
  14. },
  15. beforeRouteLeave (to, from, next) {
  16. // 导航离开该组件的对应路由时调用
  17. // 可以访问组件实例 `this`
  18. }
  19. }

三:单个路由规则独享的守卫 写在路由配置中,只有访问到这个路径,才能触发钩子函数

beforeEnter:(to,from,next)=>{ alert("欢迎来到孙志豪的界面") next() }

参数

  • to: Route: 即将要进入的目标 路由对象
  • from: Route: 当前导航正要离开的路由对象
  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

重定向用哪个属性? redirect:”/路径”

vue路由的跳转方式有几种

1、<router-link to="需要跳转到页面的路径"> 2、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面 3、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面 4、this.$touter.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数

router.push、router.replace、router.go、router.back的区别?

router.push:跳转,并向history栈中加一个记录,可以后退到上一个页面 router.replace:跳转,不会向history栈中加一个记录,不可以后退到上一个页面 router.go:传正数向前跳转,传负数向后跳转 router.back 返回到上一级页面

vue 路由传参数如何实现、query 和 params

主要通过 query 和 params 来实现 (1) query可以使用name和path而params只能使用name (2) 使用params传参刷新后不会保存,而query传参刷新后可以保存 (3) Params在地址栏中不会显示,query会显示 (4) Params可以和动态路由一起使用,query不可以 (5)to=”/goods?id=1001”this.然后在接收的页面通过 $route.query.id 来接收

路由对象route和router的区别

route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。 router 是“路由实例对象”,包括了路由的跳转方法(push、go),钩子函数等。

vue-router 路由钩子函数是什么 执行顺序是什么执行顺序

代码语言:javascript复制
  1. 一、打开页面的任意一个页面,没有发生导航切换。
  2. 全局前置守卫beforeEach (路由器实例内的前置守卫)
  3. 路由独享守卫beforeEnter(激活的路由)
  4. 组件内守卫beforeRouteEnter(渲染的组件)
  5. 全局解析守卫beforeResolve(路由器实例内的解析守卫)
  6. 全局后置钩子afterEach(路由器实例内的后置钩子)
  7. 二、如果是有导航切换的(从一个组件切换到另外一个组件)
  8. 组件内守卫beforeRouteLeave(即将离开的组件)
  9. 全局前置守卫beforeEach (路由器实例内的前置守卫)
  10. 组件内守卫beforeRouteEnter(渲染的组件)
  11. 全局解析守卫beforeResolve(路由器实例内的解析守卫)
  12. 全局后置钩子afterEach(路由器实例内的后置钩子)
  13. 完整的导航解析流程
  14. 导航被触发。
  15. 在失活的组件里调用 beforeRouteLeave 守卫。
  16. 调用全局的 beforeEach 守卫。
  17. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2 )。
  18. 在路由配置里调用 beforeEnter。
  19. 解析异步路由组件。
  20. 在被激活的组件里调用 beforeRouteEnter。
  21. 调用全局的 beforeResolve 守卫 (2.5 )。
  22. 导航被确认。
  23. 调用全局的 afterEach 钩子。
  24. 触发 DOM 更新。
  25. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

动态路由:

动态路由是指路由器能够自动的建立自己的路由表,能够根据实际情况的变化实时地进行调整。用开头,后面跟的值是不确定的。这个值是我们要传递的参数 动态路由匹配本质上就是通过url进行传参

代码语言:javascript复制
  1. 比如在写一个商品详情页面的时候,我们的页面结构都一样,只是渲染的数据不同而已,这时候就可以根据商品的不同id去设置动态路由,只需要写一个组件,就可以把每个商品的商品详情映射到同一个组件上去。
  2. {
  3. path: '/Jqxq/:id', // 路由配置拼接
  4. name: 'Jqxq',
  5. component: Jqxq
  6. }
  7. 跳转 this.$router.push('/Jqxq/' item.id)
  8. 接收 :this.$route.params.id

嵌套路由:

vue项目中,界面通常由多个嵌套的组件构成, 必须先清楚这样一件事,一个<router-view/>对应展示的就是一个组件 因此实现嵌套路由有两个要点: 路由对象中定义子路由 用children实现嵌套路由 组件内<router-view/>的使用.

路由配置:

代码语言:javascript复制
  1. export default new Router({
  2. mode: 'history', //路由模式,取值为history与hash
  3. base: '/', //打包路径,默认为/,可以修改
  4. routes: [
  5. {
  6. path: string, //路径
  7. ccomponent: Component; //页面组件
  8. name: string; // 命名路由-路由名称
  9. components: { [name: string]: Component }; // 命名视图组件
  10. redirect: string | Location | Function; // 重定向
  11. props: boolean | string | Function; // 路由组件传递参数
  12. alias: string | Array<string>; // 路由别名
  13. children: Array<RouteConfig>; // 嵌套子路由
  14. // 路由单独钩子
  15. beforeEnter?: (to: Route, from: Route, next: Function) => void;
  16. meta: any; // 自定义标签属性,比如:是否需要登录
  17. icon: any; // 图标
  18. // 2.6.0
  19. caseSensitive: boolean; // 匹配规则是否大小写敏感?(默认值:false)
  20. pathToRegexpOptions: Object; // 编译正则的选项
  21. }
  22. ]
  23. })

怎么定义 vue-router 的动态路由? 怎么获取传过来的值

在 router 目录下的 index.js 文件中,对 path 属性加上 /:id,使用 route 对象的 params.id 获取

Vue-router共有几种模式?默认是那种?

有两种模式 hash和history模式 默认是hash 1、hash ——即地址栏 URL 中的#符号,它的特点在 于:hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影 响,因此改变 hash 不会重新加载页面。 2、history ——利用了 HTML5 History api 在浏览器中没有# 有浏览器兼容问题 history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 地址后加上/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。

路由懒加载

使用原因:在单页应用中,如果没有应用懒加载,运用 webpack 打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时 原理:vue 异步组件技术:异步加载,vue-router 配置路由 , 使用 vue 的异步组件技术 , 实现按需加载。 { path: '/home', component: () => import('@/views/home/home.vue') } // 懒加载

能说下 vue-router 中常用的路由模式实现原理吗

hash 模式 location.hash 的值实际就是 URL 中#后面的东西 它的特点在于:hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。 可以为 hash 的改变添加监听事件 window.addEventListener("hashchange", funcRef, false); 每一次改变 hash(window.location.hash),都会在浏览器的访问历史中增加一个记录利用 hash 的以上特点,就可以来实现前端路由“更新视图但不重新请求页面”的功能了 特点:兼容性好但是不美观 history 模式 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。 这两个方法应用于浏览器的历史记录站,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前 URL 改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。

指令部分进行扩展

vue常用修饰以及常见指令

代码语言:javascript复制
  1. 修饰符
  2. .stop 阻止事件冒泡
  3. .cpture 设置事件捕获
  4. .self 只有当事件作用在元素本身才会触发
  5. .prevent 阻止默认事件,比如超链接跳转
  6. .once 事件只能触发一次
  7. .native 触发js原生的事件
  8. .number 把文本框的内容转换为数字
  9. .trim 去除文本框左右空格
  • 常见指令
代码语言:javascript复制
  1. ⑴v-bind:给元素绑定属性
  2. ⑵v-on:给元素绑定事件
  3. ⑶v-html:给元素绑定数据,且该指令可以解析 html 标签
  4. ⑷v-text:给元素绑定数据,不解析标签
  5. ⑸v-model:数据双向绑定
  6. ⑹v-for:遍历数组
  7. ⑺v-if:条件渲染指令,动态在 DOM 内添加或删除 DOM 元素
  8. ⑻v-else:条件渲染指令,必须跟 v-if 成对使用
  9. ⑼v-else-if:判断多层条件,必须跟 v-if 成对使用
  10. ⑽v-cloak:解决插值闪烁问题
  11. ⑾v-once:只渲染元素或组件一次
  12. ⑿v-pre:跳过这个元素以及子元素的编译过程,以此来加快整个项目的编译速度
  13. ⒀v-show:条件渲染指令,将不符合条件的数据隐藏(display:none)

v-for 与 v-if 的优先级

v-for 比 v-if 优先,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候。

vue中key 的作用

“key 值:用于管理可复用的元素。因为 Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做使 Vue 变得非常快,但是这样也不总是符合实际需求。2.2.0 的版本里,当在组件中使用 v-for 时,key 是必须的。” key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点,更高效的对比虚拟DOM中每个节点是否是相同节点,相同就复用,不相同就删除旧的创建新的

key 是使用 index 还是 id 啊

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key 的作用主要是为了高效的更新虚拟 DOM。 举例子:加入写一个带有复选框的列表 选中第一个节点的复选框,点击删除,vue中是这样操作的,删除后新的数据这时会进行比较,第一个节点的标签一样,值不一样,就会复用原来位置的标签,不会做删除和创建,在第一个节点中是将复选框选中的,当我们看见好像是把第一个删除了,但是点击后去看复选框的时候还是选中在第一个,如果是直接将第一个节点删除了那么复选框就不会选中。

vue 初始化页面闪动问题。

能够解决插值表达式闪烁问题,需要在style中设置样式[v-clock]{display:none}

v-if和v-show的区别及使用场景?

v-if 动态的创建或者销毁元素,为true的时候为显示,为false的时候不显示,要使用v-else必须和v-if紧挨着 v-show 是控制元素的显示或者隐藏,在我们的标签上会看到有display:block,none v-if 有更高的切换消耗,而 v-show 有更高的初始化渲染消耗,一般推荐频繁切换的时候使用 v-show 更好,当我们的判断分支比较多的时候,和首次渲染的时候 使用v-if

自定义指令,自定义过滤器

出了vue自带的指定以外,我们如果需要对dom进行底层操作的时候这里就用到了自定义指令,分为一下 全局:vue.directive:{"",{}} 局部:directives:{指令名:{钩子函数}}

  1. bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  2. inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  3. update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  4. componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  5. unbind:只调用一次,指令与元素解绑时调用。

参数:

el:指令所绑定的元素 binding:一个对象包含一下, name:指令名,不包括 v- 前缀。 value:指令的绑定值

代码语言:javascript复制
  1. 补充 :vue3的自定义指令钩子函数?
  2. created - 自定义指令所在组件, 创建后
  3. beforeMount - 就是Vue2.x中的 bind, 自定义指令绑定到 DOM 后调用. 只调用一次, 注意: 只是加入进了DOM, 但是渲染没有完成
  4. mounted - 就是Vue2.x中的 inserted, 自定义指令所在DOM, 插入到父 DOM 后调用, 渲染已完成(最最重要)
  5. beforeUpdate - 自定义指令所在 DOM, 更新之前调用
  6. updated - 就是Vue2.x中的 componentUpdated
  7. beforeUnmount - 销毁前
  8. unmounted - 销毁后

自定义指令原理

1.在生成 ast 语法树时,遇到指令会给当前元素添加 directives 属性 2.通过 genDirectives 生成指令代码 3.在 patch 前将指令的钩子提取到 cbs 中,在 patch 过程中调用对应的钩子 4.当执行指令对应钩子函数时,调用对应指令定义的方法

选项对象和常用api

什么是过滤器?怎么定义全局和局部过滤器

过滤器是对 即将显示的数据做进一步的筛选处理,然后显示,过滤器并没有改变原来的数据,只是在原数据的基础上产生新的数据 全局: Vue.filter(‘过滤器名’,funciton(val){}) 局部过滤器,定义在组件内部 filters 属性上.它只能在此组件内部使用. filters:{过滤器名:funciton(参数){//逻辑代码}} 使用:过滤时间,过滤金钱

什么是 mixin ?

Mixin 使我们能够为 Vue 组件编写可插拔和可重用的功能。mixin 项目变得复杂的时候,多个组件间有重复的逻辑就会用到mixin 如果希望在多个组件之间重用一组组件选项,例如生命周期 hook、 方法等,则可以将其编写为 mixin,并在组件中简单的引用它。 然后将 mixin 的内容合并到组件中。如果你要在 mixin 中定义生命周期 hook,那么它在执行时将优化于组件自已的 hook。

在vue.js中mixin和页面执行顺序问题

mixin中的代码先执行,单文件中的后执行。 mixin的beforeCreate > 父beforeCreate > mixin的created > 父created > mixin的beforeMount > 父beforeMount > 子beforeCreate > 子created > 子beforeMount > 子mounted > mixin的mounted >父mounted

nextTick 使用场景和原理

在下次DOM更新循环结束后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。使用场景是:可以在created钩子函数中拿到dom节点 nextTick 中的回调是在下次 DOM 更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。主要思路就是采用微任务优先的方式调用异步方法去执行 nextTick 包装的方法

vue的删除数组和原生删除数组的区别delete

删除数组

  1. delete只是把数组元素的值变成empty/undefined,元素的键不变,数组长度不变。
  2. Vue.delete直接删除数组,改变数组的键值和长度。

删除对象 两者相同,都会把键名(属性/字段)和键值删除。

Vue.extend 作用和原理

官方解释:Vue.extend 使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。 其实就是一个子类构造器 是 Vue 组件的核心 api 实现思路就是使用原型继承的方法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并 基础用法

代码语言:javascript复制
  1. <div id="mount-point"></div>
  2. // 创建构造器
  3. /* Vue.extend( options )
  4. 参数:{Object} options
  5. 用法:使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象;
  6. data 选项是特例,需要注意:在 Vue.extend() 中它必须是函数;*/
  7. var Profile = Vue.extend({
  8. template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  9. data: function () {
  10. return {
  11. firstName: 'Walter',
  12. lastName: 'White',
  13. alias: 'Heisenberg'
  14. }
  15. }
  16. })
  17. // 创建 Profile 实例,并挂载到一个元素上。
  18. new Profile().$mount('#mount-point')
  19. // 结果如下:
  20. <p>Walter White aka Heisenberg</p>
  21. /*
  22. 可以看到,extend 创建的是 Vue 构造器,而不是我们平时常写的组件实例,所以不可以通过 new Vue({ components: testExtend }) 来直接使用,需要通过 new Profile().$mount(’#mount-point’) 来挂载到指定的元素上。
  23. */

组件模块部分(插槽,单页面,通信)

vue 组件父子,子父,兄弟通信

父传递子如何传递 (1)在父组件的子组件标签上绑定一个属性,挂载要传输的变量 (2)在子组件中通过props来接受数据,props可以是数组也可以是对象,接受的数据可以直接使用 props: ["属性名"] props:{属性名:数据类型} 子传递父如何传递 (1)在父组件的子组件标签上自定义一个事件,然后调用需要的方法 (2)在子组件的方法中通过 this.$emit("事件")来触发在父组件中定义的事件,数据是以参数的形式进行传递的 兄弟组件如何通信 (1)找到min.js文件,给他vue挂载一个 公共的 bus Vue.prototype.bus = new Vue() (2)传送数据的一方 用this.bus.emit('事件名','传送的数据') (3)在 接收数据的一方用通过 Bus.

代码语言:javascript复制
12种组件通信 vue12种通信方式

prop 验证,和默认值

props:会接收不同的数据类型,常用的数据类型的设置默认值的写法,Number, String, Boolean, Array, Function, Object 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级prop 的更新流动到子组件中,但是反过来则不行。这样防止子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。若果在子组件中直接修改prop传递的值,Vue会发出警告,

方法二:通过 vuex 实现 (要了解)

具体实现:vuex 是一个状态管理工具,主要解决大中型复杂项目的数据共享问题,主要包括 state,actions,mutations,getters 和 modules 5 个要素,主要流程:组件通过 dispatch 到 actions,actions 是异步操作,再 actions中通过 commit 到 mutations,mutations 再通过逻辑操作改变 state,从而同步到组件,更新其数据状态

组件中写 name 选项有什么作用

① 项目使用 keep-alive 时,可搭配组件的 name 进行缓存过滤。② DOM 做递归组件时需要调用自身 name ③ vue-devtools 调试工具里显示的组件名称是由 vue 中组件 name 决定的

自定义组件

在vue 中的 component 中新建组件,定义好<template>视图层,<script>逻辑层,<style>css样式层。,然后在页面引入,在components 中注册组件 ,在页面中作为标签来使用。 在vue中开发,都是用的组件化的思想开发的,组件封装的方式可以使我们的开发效率提高,把单页面的每个模块拆分为一个组件件, 组件封装的方式解决了我们传统项目,开发效率低,难以维护,复用性低等问题。 使用:比如说封装一个 swiper 首先我们要定义一个props要接受传递的数据,写入响应的逻辑,在通过import引入到页面注册作为标签使用即可。

keep-alive 的作用

keep-alive是Vue提供给我们一个内置组件,会缓存不活动的组件实例,而不是销毁它们, 作为标签使用 包裹在需要缓存的组件外 在组件切换过程中 把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性 作用: 比如列表页面进入详情,我们想保存列表滚动的位置,我们就可以使用keep-alive保存列表页面的滚动位置。 组件使用keep-alive以后会新增两个生命周期 actived() deactived() activated(组件激活时使用) 与 deactivated(组价离开时调用) 有两个参数: 允许组件有条件的进行缓存。 include - 包裹的组件名会被缓存 exclude 包裹的组件名都不会被缓存

keep-alive 缓存beforDestroy 还会执行吗。

首先,答案是不会的,准确的说是不会直接调用。 默认情况下,也就是没有设置keep-alive,当离开当前路由时,会直接调用beforeDestroy和destroyed来销毁。当组件设置keep-alive后,不会直接调用这个销毁周期函数,而是会在生命周期函数新增两个,activated和deactivated; 当退出的时候会执行deactivated 函数

VUE组件中的data为什么是一个函数

Object是引用数据类型,如果不用function返回,每个组件的data都是内存的同一个地址,一个数据改变了其他也改变了,这就造成了数据污染。如果data是一个函数,每个实例的data都在闭包中,就不会各自影响了

组件特性及好处、组件的基本组成

(1) 特性:重用性、可指定性、互操作性、高内聚性、低耦合度 (2) 好处:组件可以扩展HTML元素、封装可重用代码 template 结构(html代码) script行为 style样式

什么是slot?什么是命名slot?slot怎么使用?

插槽就是父组件往子组件中插入一些内容。 有三种方式,默认插槽,具名插槽,作用域插槽

  1. 默认插槽就是把父组件中的数据,显示在子组件中,子组件通过一个slot插槽标签显示父组件中的数据
  2. 具名插槽是在父组件中通过slot属性,给插槽命名,在子组件中通过slot标签,根据定义好的名字填充到对应的位置。这样就可以指定多个可区分的slot,在使用组件时灵活地进行插值。
  3. 作用域插槽是带数据的插槽,子组件提供给父组件的参数,父组件根据子组件传过来的插槽数据来进行不同的展现和填充内容。在标签中通过v-slot=""要穿过来的数据“来接受数据。

scoped 原理及穿透方法

vue 中的 scoped 通过在 DOM 结构以及 css 样式上加唯一不重复的标记:data-v-hash 的方式,以保证唯一(通过 PostCSS 转译),达到样式私有模块化的目的。 scoped 的 3 条渲染规则:① 给 HTML 的 DOM 节点加一个不重复的 data 属性,来表示它的唯一性;② 在每句 css 选择器末尾(编译后的生成的 css 语句)加一个当前组件的 data 属性选择器来私有化样式;③ 如果组件内部包含有其他组件,只会给其他组件的最外层标签加上 ddan 当前组件的 data 属性。补充:

代码语言:javascript复制
  1. 在做项目中,会遇到这么一个问题,即:引用了第三方组件,需要在组件中局部修改第三方组件的样式,而又不想去除scoped属性造成组件之间的样式污染。那么有哪些解决办法呢?
  2. ①不使用scopeds省略(不推荐);
  3. ② 在模板中使用两次style标签。
  4. ③scoped穿透:/deep/ >>>

函数式组件使用场景和原理

代码语言:javascript复制
  1. 函数式组件与普通组件的区别
  2. 1.函数式组件需要在声明组件是指定 functional:true
  3. 2.不需要实例化,所以没有this,this通过render函数的第二个参数context来代替
  4. 3.没有生命周期钩子函数,不能使用计算属性,watch
  5. 4.不能通过$emit 对外暴露事件,调用事件只能通过context.listeners.click的方式调用外部传入的事件
  6. 5.因为函数式组件是没有实例化的,所以在外部通过ref去引用组件时,实际引用的是HTMLElement
  7. 6.函数式组件的props可以不用显示声明,所以没有在props里面声明的属性都会被自动隐式解析为prop,而普通组件所有未声明的属性都解析到$attrs里面,并自动挂载到组件根元素上面(可以通过inheritAttrs属性禁止)
  8. 优点 1.由于函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件 2.函数式组件结构比较简单,代码结构更清晰
  9. 使用场景:
  10. 一个简单的展示组件,作为容器组件使用 比如 router-view 就是一个函数式组件
  11. “高阶组件”——用于接收一个组件作为参数,返回一个被包装过的组件

vue的项目中问题

单页面应用和多页面应用区别以及优缺点,

单页面:只有一个html页面,跳转方式是组件之间的切换 优点:跳转流畅、组件化开发、组件可复用、开发便捷 缺点:首屏加载过慢 多页面:有多个页面,跳转方式是页面之间的跳转 优点:首屏加载块 缺点:跳转速度慢

为什么要使用脚手架

快速开始一个vue项目,不用手动配置,直接开发

口喷axios封装

首先要安装axios,一般我会在项目的src目录中,新建一个network文件夹,作为我们的网络请求模块,然后在里面新建一个http.js和一个api.js文件和一个reques.js。 http.js文件用来封装我们的axios basUrl Tiemout, api.js用来统一管理我们的接口url, request.js中添加请求拦截和响应拦截。在请求拦截中,会给请求头添加token字段,还有loading动画的开启。在响应拦截中,可以做一些loading动画的关闭,还有可以根据后端返回的状态码,做一些检验token是否有效或者过期的操作。 接着就是做一些axios进行的api接口的封装,这里我用到了async,await封装请求接口函数,这样可以将异步操作同步化操作,代码更加友好,避免回调地域的出现。

vue中如何解决跨域问题?

在vue开发中实现跨域:在vue项目根目录下找到vue.config.js文件(如果没有该文件则自己创建),在proxy中设置跨域

代码语言:javascript复制
  1. devServer: {
  2. proxy: { //配置跨域
  3. '/api': {
  4. target: 'http://121.121.67.254:8185/', //这里后台的地址模拟的;应该填写你们真实的后台接口
  5. changOrigin: true, //允许跨域
  6. pathRewrite: {
  7. /* 重写路径,当我们在浏览器中看到请求的地址为:http://localhost:8080/api/core/getData/userInfo 时
  8. 实际上访问的地址是:http://121.121.67.254:8185/core/getData/userInfo,因为重写了 /api
  9. */
  10. '^/api': ''
  11. }
  12. },
  13. }
  14. },

assets和static的区别?

assets中的文件会经过webpack打包,重新编译,推荐在assets存放js等需要打包编译的文件。 static中的文件,不会打包编译。static中的文件只是复制一遍。static中建议放一些外部第三方文件,自己的放assets里,别人的放static中。(图片推荐放在static里)

Vue data 中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗?

不会立即同步执行重新渲染。Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化, Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。 如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环tick中,Vue 刷新队列并执行实际(已去重的)工作。

多环境变量

首先是通过在根目录下创建.env.*(配置文件)文件,development 本地开发环境配置、staging 测试环境配置、production 正式环境配置(生产环境)。因为我在创建的文件中并没有定义很多变量,只定义了基础的env,所以需要在src目录下创建一个config文件夹,创建对应的环境变量文件,用来管理不同的环境。在config中创建对应的文件是为了后期修改起来方便,不需要重启项目,符合开发习惯。之后就是根据需要的环境,在封装的axios中通过解构赋值的方式导入,放在baseURL中就可以使用。

element-ui和vant-ui按需引入

首先安装按需引入的插件,在babel.config.js中添加按需引入的配置,创建一个plugin文件夹,定义一个js文件用来存放按需引入的代码,之后在建好的js文件中首先导入vue,再导入需要的vant-ui插件,通过vue.use()全局注入。修改样式可以用样式穿透 /deep/

Vue 解决了什么问题

① 虚拟 dom:dom 操作时非常耗性能的,不再使用原生的 dom 操作节点,极大的解放 dom 操作,但具体操作的还是 dom,不过是换了一种方式。提供了很多指令当然需要 对 dom 进行底层操作的时候就用到自定义指令 ② 视图、数据、结构分离:使数据的更改更为简单,只需要操作数据就能完成相关操作。 ③ 组件化:把一个单页应用中的各种模块拆分到一个一个单独的组件中,便于开发,以及后期的维护

Vue.js 的特点

简洁:页面由 HTML 模板 Json 数据 Vue 实例组成 数据驱动:自动计算属性和追踪依赖的模板表达式 组件化:用可复用、解耦的组件来构造页面 轻量:代码量小,不依赖其他库 快速:精确有效批量 DOM 更新 模板友好:可通过 npm,bower 等多种方式安装,很容易融入 Vue 的核心库只关注视图层,并且非常容易学习

请说出 vue.cli 项目中 src 目录每个文件夹和文件的用法

assets 文件夹是放静态资源; components 是放组件; router 是定义路由相关的配置; view 视图; app.vue 是一个应用主组件; main.js 是入口文件

描述下 vue 从初始化页面–>修改数据–>刷新页面 UI 过程?

当 Vue 进入初始化阶段时,一方面 Vue 会遍历 data 中的属性,并用 Object.defineProperty 将它转化成 getter/setterd 的形式,实现数据劫持; 另一方面,Vue 的指令编译器 Compiler 对元素节点的各个指令进行解析,初始化视图,并订阅 Watcher 来更新视图,此时 Watcher 会将自己添加到消息订阅器 Dep 中,此时初始化完毕。 当数据发生变化时,触发 Observer 中 setter 方法,立即调用 Dep.notify( ),Dep 这个数组开始遍历所有的订阅者,并调用其 update 方法,Vue 内部再通过 diff 算法,patch 相应的更新完成对订阅者视图的改变。

Vue 怎么兼容 IE

使用 babel-polyfill 插件,和前缀名 hack

Vue 怎么重置 data

使用 Object.assign(),vm.data 可 以 获 取 当 前 状 态 下 的 data , Object.assign(this.data, this.options.data())

vue-router 登陆权限的判断

vue-router的登陆权限判断主要是在全局钩子函数中进行的,我们在router.js文件中的定义路由里,将需要登陆权限的页面加上meta属性,值是对象的形式,然后在该对象中自定义一个属性,属性值就是一个Boolean值,这时候在main.js文件的全局钩子函数中进行判断,如果需要跳转的页面的自定义属性值为true,那么将进行判断其是否登录,如果没有登录,则告诉用户登录,如果有登录,那么进行页面跳转。

vue-cli 替我们做了哪些工作

vue-cli 是基于 Vue.js 进行快速开发的完整系统,也可以理解成是很多 npm 包的集合。 vue-cli 完成的功能: .vue 文件 --> .js 文件 ES6 语法 --> ES5 语法 Sass,Less,Stylus --> CSS 对 jpg,png,font 等静态资源的处理 热更新 定义环境变量,区分 dev 和 production 模式 如果开发者需要补充或修改默认设置,需要在 package.json 同级下新建一个 vue.config.js 文件

Vue 如何检测数组变化

数组考虑性能原因没有用 defineProperty 对数组的每一项进行拦截,而是选择对 7 种数组(push,shift,pop,splice,unshift,sort,reverse)方法进行重写(AOP 切片思想) 所以在 Vue 中修改,数组的索引和长度是无法监控到的。需要通过以上 7 种变异方法修改数组才会触发数组对应的 watcher 进行更新

vue 中使用了哪些设计模式

1.工厂模式 - 传入参数即可创建实例 虚拟 DOM 根据参数的不同返回基础标签的 Vnode 和组件 Vnode 2.单例模式 - 整个程序有且仅有一个实例 vuex 和 vue-router 的插件注册方法 install 判断如果系统存在实例就直接返回掉 3.发布-订阅模式 (vue 事件机制) 4.观察者模式 (响应式数据原理) 5.策略模式 策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案-比如选项的合并策略

如何解决vue首屏加载过慢?

① 把不常改变的库放到 index.html 中,通过 cdn 引入

然后找到 build/webpack.base.conf.js 文件,在 module.exports = { } 中添加以下代码:

代码语言:javascript复制
  1. externals: {
  2. 'vue': 'Vue',
  3. 'vue-router': 'VueRouter',
  4. 'element-ui': 'ELEMENT',
  5. }

②vue 路由懒加载,图片懒加载,使用异步组件,按需加载 ③ 不生成 map 文件,找到 config/index.js 文件,修改为 productionSourcceMap:false ④vue 组件尽量不要全局引入 ⑤ 使用更轻量级的工具库 ⑥ 开启 gzip 压缩:这个优化是两方面的,前端将文件打包成.gz 文件,然后通过 nginx 的配置,让浏览器直接解析.gz 文件。 ⑦ 首页单独做服务端渲染:如果首页真的有瓶颈,可以考虑用 node 单独做服务端渲染,而下面的子页面仍用 spa 单页的方式交互。这里不推荐直接用 nuxt.js 服务端渲染方案,因为这样一来增加了学习成本,二来服务端的维护成本也会上升,有时在本机测试没问题,在服务端跑就有问题,为了省心,还是最大限度的使用静态页面较好。

Vue和JQuery的区别在哪?为什么放弃JQuery用Vue?

jQuery是直接操作DOM,Vue不直接操作DOM,Vue的数据与视图是分开的,Vue只需要操作数据就行它是个框架 jQuery的操作DOM行为是频繁的,而Vue利用虚拟DOM的技术,大大提高了更新DOM时的性能它是个库 Vue中不倡导直接操作DOM,开发者只需要把大部分精力放在数据层面上 Vue集成了一些库,大大提高开发效率,例如Route、Vuex等等

你都做过哪些Vue的性能优化?

尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher v-if和v-for不能连用 v-if 和 v-show 区分使用场景 v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if 如果需要使用v-for给每项元素绑定事件时使用事件代理 SPA 页面采用keep-alive缓存组件 在更多的情况下,使用v-if替代v-show 使用路由懒加载、异步组件 防抖、节流 第三方模块按需导入 长列表滚动到可视区域动态加载,不需要响应式的数据不要放到 data 中(可以Object.freeze() 冻结数据) 图片懒加载 SEO优化 预渲染 服务端渲染SSR 打包优化, 压缩代码 Tree Shaking/Scope Hoisting 使用cdn加载第三方模块 多线程打包happypack splitChunks抽离公共文件 sourceMap优化 骨架屏 PWA 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。防止内部泄漏,组件销毁后把全局变量和事件销毁

上期:前端面试题 --- JS部分

下期预告:前端面试题 --- JS高阶和其他

以上就是本期包内容了!!!

0 人点赞