• 进阶vue面试题总结


    过滤器的作用,如何实现一个过滤器

    根据过滤器的名称,过滤器是用来过滤数据的,在Vue中使用filters来过滤数据,filters不会修改数据,而是过滤数据,改变用户看到的输出(计算属性 computed ,方法 methods 都是通过修改数据来处理数据格式的输出显示)。

    使用场景:

    • 需要格式化数据的情况,比如需要处理时间、价格等数据格式的输出 / 显示。
    • 比如后端返回一个 年月日的日期字符串,前端需要展示为 多少天前 的数据格式,此时就可以用fliters过滤器来处理数据。

    过滤器是一个函数,它会把表达式中的值始终当作函数的第一个参数。过滤器用在插值表达式 { { }}v-bind 表达式 中,然后放在操作符“ | ”后面进行指示。

    例如,在显示金额,给商品价格添加单位:

    <li>商品价格:{
       {
       item.price | filterPrice}}</li>
    
     filters: {
       
        filterPrice (price) {
       
          return price ? ('¥' + price) : '--'
        }
      }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    路由的hash和history模式的区别

    Vue-Router有两种模式:hash模式history模式。默认的路由模式是hash模式。

    1. hash模式

    简介: hash模式是开发中默认的模式,它的URL带着一个#

    特点:hash值会出现在URL里面,但是不会出现在HTTP请求中,对后端完全没有影响。所以改变hash值,不会重新加载页面。这种模式的浏览器支持度很好,低版本的IE浏览器也支持这种模式。hash路由被称为是前端路由,已经成为SPA(单页面应用)的标配。

    原理: hash模式的主要原理就是onhashchange()事件

    window.onhashchange = function(event){
       
        console.log(event.oldURL, event.newURL);
        let hash = location.hash.slice(1);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用onhashchange()事件的好处就是,在页面的hash值发生变化时,无需向后端发起请求,window就可以监听事件的改变,并按规则加载相应的代码。除此之外,hash值变化对应的URL都会被浏览器记录下来,这样浏览器就能实现页面的前进和后退。虽然是没有请求后端服务器,但是页面的hash值和对应的URL关联起来了。

    2. history模式

    简介: history模式的URL中没有#,它使用的是传统的路由分发模式,即用户在输入一个URL时,服务器会接收这个请求,并解析这个URL,然后做出相应的逻辑处理。 特点: 相比hash模式更加好看。但是,history模式需要后台配置支持。如果后台没有正确配置,访问时会返回404。 API: history api可以分为两大部分,切换历史状态和修改历史状态:

    • 修改历史状态:包括了 HTML5 History Interface 中新增的 pushState()replaceState() 方法,这两个方法应用于浏览器的历史记录栈,提供了对历史记录进行修改的功能。只是当他们进行修改时,虽然修改了url,但浏览器不会立即向后端发送请求。如果要做到改变url但又不刷新页面的效果,就需要前端用上这两个API。
    • 切换历史状态: 包括forward()back()go()三个方法,对应浏览器的前进,后退,跳转操作。

    虽然history模式丢弃了丑陋的#。但是,它也有自己的缺点,就是在刷新页面的时候,如果没有相应的路由或资源,就会刷出404来。

    如果想要切换到history模式,就要进行以下配置(后端也要进行配置):

    const router = new VueRouter({
       
      mode: 'history',
      routes: [...]
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    3. 两种模式对比

    调用 history.pushState() 相比于直接修改 hash,存在以下优势:

    • pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL;
    • pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
    • pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;
    • pushState() 可额外设置 title 属性供后续使用。
    • hash模式下,仅hash符号之前的url会被包含在请求中,后端如果没有做到对路由的全覆盖,也不会返回404错误;history模式下,前端的url必须和实际向后端发起请求的url一致,如果没有对用的路由处理,将返回404错误。

    hash模式和history模式都有各自的优势和缺陷,还是要根据实际情况选择性的使用。

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

    我们可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。

    推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

    • 能更快获取到服务端数据,减少页面加载时间,用户体验更好;
    • SSR不支持 beforeMount 、mounted 钩子函数,放在 created 中有助于一致性。

    vue 中使用了哪些设计模式

    1.工厂模式 - 传入参数即可创建实例

    虚拟 DOM 根据参数的不同返回基础标签的 Vnode 和组件 Vnode

    2.单例模式 - 整个程序有且仅有一个实例

    vuex 和 vue-router 的插件注册方法 install 判断如果系统存在实例就直接返回掉

    3.发布-订阅模式 (vue 事件机制)

    4.观察者模式 (响应式数据原理)

    5.装饰模式: (@装饰器的用法)

    6.策略模式 策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案-比如选项的合并策略

    …其他模式欢迎补充

    常见的事件修饰符及其作用

    • .stop:等同于 JavaScript 中的 event.stopPropagation() ,防止事件冒泡;
    • .prevent :等同于 JavaScript 中的 event.preventDefault() ,防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播);
    • .capture :与事件冒泡的方向相反,事件捕获由外到内;
    • .self :只会触发自己范围内的事件,不包含子元素;
    • .once :只会触发一次。

    Computed 和 Methods 的区别

    可以将同一函数定义为一个 method 或者一个计算属性。对于最终的结果,两种方式是相同的

    不同点:

    • computed: 计算属性是基于它们的依赖进行缓存的,只有在它的相关依赖发生改变时才会重新求值;
    • method 调用总会执行该函数。

    Vue 模板编译原理

    Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步

    第一步是将 模板字符串 转换成 element ASTs(解析器)
    第二步是对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)
    第三步是 使用 element ASTs 生成 render 函数代码字符串(代码生成器)
    
    • 1
    • 2
    • 3

    描述下Vue自定义指令

    在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
    一般需要对DOM元素进行底层操作时使用,尽量只用来操作 DOM展示,不修改内部的值。当使用自定义指令直接修改 value 值时绑定v-model的值也不会同步更新;如必须修改可以在自定义指令中使用keydown事件,在vue组件中使用 change事件,回调中修改vue数据;

    (1)自定义指令基本内容

    • 全局定义:Vue.directive("focus",{})

    • 局部定义:directives:{focus:{}}

    • 钩子函数:指令定义对象提供钩子函数

      o bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

      o inSerted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)。

      o update:所在组件的VNode更新时调用,但是可能发生在其子VNode更新之前调用。指令的值可能发生了改变,也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。

      o ComponentUpdate:指令所在组件的 VNode及其子VNode全部更新后调用。

      o unbind:只调用一次,指令与元素解绑时调用。

    • 钩子函数参数
      o el:绑定元素

      o bing: 指令核心对象,描述指令全部信息属性

      o name

      o value

      o oldValue

      o expression

      o arg

      o modifers

      o vnode 虚拟节点

      o oldVnode:上一个虚拟节点(更新钩子函数中才有用)

    (2)使用场景

    • 普通DOM元素进行底层操作的时候,可以使用自定义指令

    • 自定义指令是用来操作DOM的。尽管Vue推崇数据驱动视图的理念,但并非所有情况都适合数据驱动。自定义指令就是一种有效的补充和扩展,不仅可用于定义任何的DOM操作,并且是可复用的。

    (3)使用案例

    初级应用:

    • 鼠标聚焦
    • 下拉菜单
    • 相对时间转换
    • 滚动动画

    高级应用:

    • 自定义指令实现图片懒加载
    • 自定义指令集成第三方插件

    如何从真实DOM到虚拟DOM

    涉及到Vue中的模板编译原理,主要过程:

    1. 将模板转换成 ast 树, ast 用对象来描述真实的JS语法(将真实DOM转换成虚拟DOM)
    2. 优化树
    3. ast 树生成代码

    参考:前端vue面试题详细解答

    Computed 和 Watch 的区别

    对于Computed:

    • 它支持缓存,只有依赖的数据发生了变化,才会重新计算
    • 不支持异步,当Computed中有异步操作时,无法监听数据的变化
    • computed的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data声明过,或者父组件传递过来的props中的数据进行计算的。
    • 如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,一般会使用computed
    • 如果computed属性的属性值是函数,那么默认使用get方法,函数的返回值就是属性的属性值;在computed中,属性有一个get方法和一个set方法,当数据发生变化时,会调用set方法。

    对于Watch:

    • 它不支持缓存,数据变化时,它就会触发相应的操作
    • 支持异步监听
    • 监听的函数接收两个参数,第一个参数是最新的值,第二个是变化之前的值
    • 当一个属性发生变化时,就需要执行相应的操作
    • 监听数据必须是data中声明的或者父组件传递过来的props中的数据,当发生变化时,会触发其他操作,函数有两个的参数:
      • immediate:组件加载立即触发回调函数
      • deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,deep无法监听到数组和对象内部的变化。

    当想要执行异步或者昂贵的操作以响应不断的变化时,就需要使用watch。

    总结:

    • computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
    • watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。

    运用场景:

    • 当需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时都要重新计算。
    • 当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许执行异步操作 ( 访问一个 API ),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

    子组件可以直接改变父组件的数据吗?

    子组件不可以直接改变父组件的数据。这样做主要是为了维护父子组件的单向数据流。每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。如果这样做了,Vue 会在浏览器的控制台中发出警告。

    Vue提倡单向数据流,即父级 props 的更新会流向子组件,但是反过来则不行。这是为了防止意外的改变父组件状态,使得应用的数据流变得难以理解,导致数据流混乱。如果破坏了单向数据流,当应用复杂时,debug 的成本会非常高。

    只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。

    对keep-alive的理解,它是如何实现的,具体缓存的是什么?

    如果需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 组件包裹需要保存的组件。

    (1)keep-alive

    keep-alive有以下三个属性:

    • include 字符串或正则表达式,只有名称匹配的组件会被匹配;
    • exclude 字符串或正则表达式,任何名称匹配的组件都不会被缓存;
    • max 数字,最多可以缓存多少组件实例。

    注意:keep-alive 包裹动态组件时,会缓存不活动的组件实例。

    主要流程

    1. 判断组件 name ,不在 include 或者在 exclude 中,直接返回 vnode,说明该组件不被缓存。
    2. 获取组件实例 key ,如果有获取实例的 key,否则重新生成。
    3. key生成规则,cid +“∶∶”+ tag ,仅靠cid是不够的,因为相同的构造函数可以注册为不同的本地组件。
    4. 如果缓存对象内存在,则直接从缓存对象中获取组件实例给 vnode ,不存在则添加到缓存对象中。 5.最大缓存数量,当缓存组件数量超过 max 值时,清除 keys 数组内第一个组件。

    (2)keep-alive 的实现

    const patternTypes: Array<Function> = [String, RegExp, Array] // 接收:字符串,正则,数组
    
    export default {
       
      name: 'keep-alive',
      abstract: true, // 抽象组件,是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
    
      props: {
       
        include: patternTypes, // 匹配的组件,缓存
        exclude: patternTypes, // 不去匹配的组件,不缓存
        max: [String, Number], // 缓存组件的最大实例数量, 由于缓存的是组件实例(vnode),数量过多的时候,会占用过多的内存,可以用max指定上限
      },
    
      created() {
       
        // 用于初始化缓存虚拟DOM数组和vnode的key
        this.cache = Object.create(null)
        this.keys = []
      },
    
      destroyed() {
       
        // 销毁缓存cache的组件实例
        for (const key in this.cache) {
       
          pruneCacheEntry(this.cache, key, this.keys)
        }
      },
    
      mounted() {
       
        // prune 削减精简[v.]
        // 去监控include和exclude的改变,根据最新的include和exclude的内容,来实时削减缓存的组件的内容
        this.$watch('include', (val) => {
       
          pruneCache(this, (name) => matches(val, name))
        })
        this.$watch('exclude', (val) => {
       
          pruneCache(this, (name) => !matches(val, name))
        })
      },
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    render函数:

    1. 会在 keep-alive 组件内部去写自己的内容,所以可以去获取默认 slot 的内容,然后根据这个去获取组件
    2. keep-alive 只对第一个组件有效,所以获取第一个子组件。
    3. 和 keep-alive 搭配使用的一般有:动态组件 和router-view
    render () {
       
      //
      function getFirstComponentChild (children: ?Array<VNode>): 
    • 1
    • 2
    • 3
  • 相关阅读:
    微信小程序分包和预加载分包
    国际通用回收标准-GRS、RCS的答疑
    猿创征文 |【MySQL数据库】(五)多表查询
    论文学习 --- RL Regret-based Defense in Adversarial Reinforcement Learning
    MySQL事务管理
    如何解决Web前端安全问题?
    Vue异步更新机制、$nextTick实现同步更新
    高压互锁(HVIL)
    人工智能技术概述_3.机器学习
    阿里云CDN实践
  • 原文地址:https://blog.csdn.net/bb_xiaxia1998/article/details/127401926