• 前端必会vue面试题


    Vue中如何进行依赖收集?

    • 每个属性都有自己的dep属性,存放他所依赖的watcher,当属性变化之后会通知自己对应的watcher去更新
    • 默认会在初始化时调用render函数,此时会触发属性依赖收集 dep.depend
    • 当属性发生修改时会触发watcher更新dep.notify()

    依赖收集简版

    let obj = {
        name: 'poetry', age: 20 };
    
    class Dep {
       
        constructor() {
       
          this.subs = [] // subs [watcher]
        }
        depend() {
       
          this.subs.push(Dep.target)
        }
        notify() {
       
          this.subs.forEach(watcher => watcher.update())
        }
    }
    Dep.target = null;
    observer(obj); // 响应式属性劫持
    
    // 依赖收集  所有属性都会增加一个dep属性,
    // 当渲染的时候取值了 ,这个dep属性 就会将渲染的watcher收集起来
    // 数据更新 会让watcher重新执行
    
    // 观察者模式
    
    // 渲染组件时 会创建watcher
    class Watcher {
       
        constructor(render) {
       
          this.get();
        }
        get() {
       
          Dep.target = this;
          render(); // 执行render
          Dep.target = null;
        }
        update() {
       
          this.get();
        }
    }
    const render = () => {
       
        console.log(obj.name); // obj.name => get方法
    }
    
    // 组件是watcher、计算属性是watcher
    new Watcher(render);
    
    function observer(value) {
        // proxy reflect
        if (typeof value === 'object' && typeof value !== null)
        for (let key in value) {
       
            defineReactive(value, key, value[key]);
        }
    }
    function defineReactive(obj, key, value) {
       
        // 创建一个dep
        let dep = new Dep();
    
        // 递归观察子属性
        observer(value);
    
        Object.defineProperty(obj, key, {
       
            get() {
        // 收集对应的key 在哪个方法(组件)中被使用
                if (Dep.target) {
        // watcher
                    dep.depend(); // 这里会建立 dep 和watcher的关系
                }
                return value;
            },
            set(newValue) {
       
                if (newValue !== value) {
       
                    observer(newValue);
                    value = newValue; // 让key对应的方法(组件重新渲染)重新执行
                    dep.notify()
                }
            }
        })
    }
    
    // 模拟数据获取,触发getter
    obj.name = 'poetries'
    
    // 一个属性一个dep,一个属性可以对应多个watcher(一个属性可以在任何组件中使用、在多个组件中使用)
    // 一个dep 对应多个watcher 
    // 一个watcher 对应多个dep (一个视图对应多个属性)
    // dep 和 watcher是多对多的关系
    
    • 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
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    前端vue面试题详细解答

    异步组件是什么?使用场景有哪些?

    分析

    因为异步路由的存在,我们使用异步组件的次数比较少,因此还是有必要两者的不同。

    体验

    大型应用中,我们需要分割应用为更小的块,并且在需要组件时再加载它们

    import {
        defineAsyncComponent } from 'vue'
    // defineAsyncComponent定义异步组件,返回一个包装组件。包装组件根据加载器的状态决定渲染什么内容
    const AsyncComp = defineAsyncComponent(() => {
       
      // 加载函数返回Promise
      return new Promise((resolve, reject) => {
       
        // ...可以从服务器加载组件
        resolve(/* loaded component */)
      })
    })
    // 借助打包工具实现ES模块动态导入
    const AsyncComp = defineAsyncComponent(() =>
      import('./components/MyComponent.vue')
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    回答范例

    1. 在大型应用中,我们需要分割应用为更小的块,并且在需要组件时再加载它们。
    2. 我们不仅可以在路由切换时懒加载组件,还可以在页面组件中继续使用异步组件,从而实现更细的分割粒度。
    3. 使用异步组件最简单的方式是直接给defineAsyncComponent指定一个loader函数,结合ES模块动态导入函数import可以快速实现。我们甚至可以指定loadingComponenterrorComponent选项从而给用户一个很好的加载反馈。另外Vue3中还可以结合Suspense组件使用异步组件。
    4. 异步组件容易和路由懒加载混淆,实际上不是一个东西。异步组件不能被用于定义懒加载路由上,处理它的是vue框架,处理路由组件加载的是vue-router。但是可以在懒加载的路由组件中使用异步组件

    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适合频繁切换。

    什么是递归组件?举个例子说明下?

    分析

    递归组件我们用的比较少,但是在TreeMenu这类组件中会被用到。

    体验

    组件通过组件名称引用它自己,这种情况就是递归组件

    <template>
      <li>
        <div> {
      { model.name }}div>
        <ul v-show="isOpen" v-if="isFolder">
          
          <TreeItem
            class="item"
            v-for="model in model.children"
            :model="model">
          TreeItem>
        ul>
      li>
    <script>
    export default {
         
      name: 'TreeItem',
      // ...
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    回答范例

    1. 如果某个组件通过组件名称引用它自己,这种情况就是递归组件。
    2. 实际开发中类似TreeMenu这类组件,它们的节点往往包含子节点,子节点结构和父节点往往是相同的。这类组件的数据往往也是树形结构,这种都是使用递归组件的典型场景。
    3. 使用递归组件时,由于我们并未也不能在组件内部导入它自己,所以设置组件name属性,用来查找组件定义,如果使用SFC,则可以通过SFC文件名推断。组件内部通常也要有递归结束条件,比如model.children这样的判断。
    4. 查看生成渲染函数可知,递归组件查找时会传递一个布尔值给resolveComponent,这样实际获取的组件就是当前组件本身

    原理

    递归组件编译结果中,获取组件时会传递一个标识符 _resolveComponent("Comp", true)

    const _component_Comp = _resolveComponent("Comp", true)
    
    • 1

    就是在传递maybeSelfReference

    export function resolveComponent(
      name: string,
      maybeSelfReference?: boolean
    ): ConcreteComponent | string {
       
      return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    resolveAsset中最终返回的是组件自身:

    if (!res && maybeSelfReference) {
       
        // fallback to implicit self-reference
        return Component
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    父组件可以监听到子组件的生命周期吗

    比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:

    // Parent.vue
    <Child @mounted="doSomething"/
    • 1
  • 相关阅读:
    通过stream对list集合中对象的多个字段进行去重
    eyb:Redis学习(3)
    【C++】map和set
    微信小程序个人账号申请和配置详细教程
    Redis - 数据类型映射底层结构
    Zookeeper系统模型_Watcher监听机制
    为什么tomcat要自定义线程池实现?
    web前端设计与开发期末作品_期末大作业【使用HTML制作汽车首页】
    Latice CPLD jed转VME文件简介
    Ubuntu 23.10 Beta 镜像开放下载
  • 原文地址:https://blog.csdn.net/helloworld1024fd/article/details/126900681