玩转vue面试题系列5 结合源码分析面试题

上文: 玩转vue面试题系列4 结合源码分析面试题

22. v-ifv-for那个优先级高

先说结论:v-for的优先级比v-if高。

在编译的时候,会将v-for渲染成_l函数,v-if会变成三元表达式。

注意:实际开发时,在一个标签上,不要同时使用v-for和v-if,二者不要连着用。

image-20220501164905895

image-20220501164949473

v-if和v-show的区别:

  • v-show:是控制样式,通过display:none控制元素的显示和隐藏。
  • v-if:控制是否渲染dom

为什么v-show控制样式,是通过display来进行控制元素的显示和隐藏的?而不是选择通过透明度opacity或者visibility?

v-if编译的时候会变成三元表达式,但是v-show会编译为一个指令

之所以不采用visibility:hidden,因为这样做了该元素仍然会占位,只是不可见(也不会响应事件了);而透明度opacity在为0的情况下,也不可见,但是也仍然占位,且会响应事件

image-20220501170915501

23. v-if,v-model,v-for的实现原理是什么

v-if已经说没了。

v-for:还是一样,会被渲染为 _l函数

image-20220501172516729

v-model

v-model实现双向数据绑定。放在表单元素上可以实现双向数据绑定,放在组件上不一样。

  • v-model放在不同的元素上,会编译出不同的结果:针对文本来说就会处理文本(会被编译成value+input+指令处理)
  • value和input实现双向数据绑定,阻止中文的触发,指令作用就是处理中文输入完毕后,手动触发更新。

v-model绑定到组件上,这里会编译成一个model对象,组件在创建虚拟节点的时候,会利用这个对象,会看一下里面是否有自定义的prop和event,如果没有则会被解析为value+input的语法糖

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

  • 普通(默认)插槽:
    • 普通插槽渲染作用域是在父组件中完成的
    • 在解析组件的时候会将组件的children放到componentOptions的children上,作为虚拟节点的属性
    • 将children取出放到组件的 vm.$options._renderChildren中
    • 做出一个映射表,放到vm. s l o t s 上 , 将 结 果 放 到 v m . slots上,将结果放到vm. slotsvm.scopedSlots上
    • vm.$scopedSlots: {a:fn,b:fn,default:fn}4
    • 渲染组件的时候,会调用 _t方法,此时会去vm.$scopedSlots找到对应的函数来渲染内容
  • 具名插槽:
    • 多增加了一个name,也就是名字
  • 作用域插槽:
    • 作用域是在子组件中。作用域插槽渲染的时候不会作为children,将作用域插槽做成了一个属性scopedSlots
    • 制作一个映射关系 $scopedSlots = {default “”: fn:function(){}}
    • 稍后在渲染组件的模板的时候,会通过name找到对应的函数,将数据传入到函数中,此时才渲染虚拟节点,用这个虚拟节点替换 _t函数

组件的孩子j叫插槽,元素的孩子还是孩子。

  • 创建组件的真实节点 this.$slots = {default:[儿子vnode]}

25. Vue.use是干什么的?原理是什么?

用来实现插件的。参数一般是一个函数,函数接收Vue的构造函数和额外的选项。

function plugin(Vue,options){}

Vue.use(plugin)

image-20220502182659504

使用Vue的use函数,是专门用来注册插件的。可以防止Vue的版本不统一。也不会让Vue-router,vuex等插件直接依赖于vue,我们只需要在使用这些插件的时候注册插件就可以了。

使用vue函数的目的,就是将vue构造函数传递给插件,让所有的插件依赖的vue是同一个版本。该发方法的源码本身并不难。

例如vue-router和vuex等插件,都没有依赖vue,在项目的package-json文件里面也没依赖vue,是通过参数形式传入的。

插件并不会进行重复安装的。

26. Vue事件修饰符有哪些?及其实现原理是什么?

实现原理只要是靠的是模板编译原理。.stop .prevent等修饰符是直接编译到事件内部了。

对于.passive, capture, .once在编译的时候增加标识 ! ~ &

键盘事件,也是通过模板编译来实现的。

27. Vue中的 .sync 修饰符的作用,用法及其实现原理

  • 和v-model一样,这个api是为了实现状态同步的。这个东西在vue3中移除了。

没什么意思,有需要的查文档。

28. 如何理解自定义指令

自定义指令,就是用户定义好对应的钩子,当元素在不同的状态时,会调用对应的钩子。所有的钩子会被合并到cbs对应的方法上,到时候会被依次调用

image-20220503132159508

29. keep-alive平时在哪里使用?原理是什么

  1. 在路由中使用
  2. component:is中使用

keep-alive的原理是默认缓存加载过的组件对应的实例,内部采用了LRU算法。下次组件切换加载的时候,此时会找到对应缓存的虚拟节点来进行初始化(走了初始化,但是没有进行真正的初始化,init -> prepatch),但是会采用上次缓存的实例上的$el来触发。

更新和销毁会触发actived和deactived的钩子

export default {
  name: 'keep-alive',
  // 抽象组件 不会被记录到 $children 和 $parent上
  abstract: true,

  props: {// 属性
    // 可以缓存那些组件
    include: patternTypes,
    // 可以排除那些组件
    exclude: patternTypes,
    // 最大缓存个数
    max: [String, Number]
  },

  methods: {
    cacheVNode() {
      const { cache, keys, vnodeToCache, keyToCache } = this
      if (vnodeToCache) {
        const { tag, componentInstance, componentOptions } = vnodeToCache
        cache[keyToCache] = {
          // 缓存中 放置需要缓存的组件 存放组件的实例
          name: getComponentName(componentOptions),
          tag,
          componentInstance, // 组件实例渲染后 会有 $el属性 下次直接复用 $el
        }
        keys.push(keyToCache) // 放入key
        // prune oldest entry
        // 超过最大长度 会移除最长时间未使用的缓存
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
        this.vnodeToCache = null
      }
    }
  },

  created () {
    // 创建一个缓存区 {}
    this.cache = Object.create(null)
    // 缓存组件的名字有哪些 []
    this.keys = []
  },

  destroyed () {
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },

  mounted () {
    // 渲染完成后 缓存vnode
    this.cacheVNode()
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  },

  updated () {
    this.cacheVNode()
  },

  render () {
    // 取出默认插槽
    const slot = this.$slots.default
    // 获取插槽中的第一个vnode
    const vnode: VNode = getFirstComponentChild(slot)
    // 拿到第一个插槽上的组件的额外选项 {Ctor, propsData, listeners, tag, children }
    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern 获取组件的名字 看组件是否加载过
      const name: ?string = getComponentName(componentOptions)
      // 校验是否需要缓存
      const { include, exclude } = this
      if ( // 这些情况不需要复用
        // not included 不需要缓存
        (include && (!name || !matches(include, name))) ||
        // excluded 排除的
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }
      // 缓存对象 {} []
      const { cache, keys } = this
      // 生成一个唯一的 key
      const key: ?string = vnode.key == null
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      if (cache[key]) { // key缓存过
        // 获取缓存的实例
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        remove(keys, key) // 把当前的key作为最新的
        keys.push(key)
      } else {
        // delay setting the cache until update
        // 以前没有缓存过 将当前的vnode进行缓存 缓存key
        this.vnodeToCache = vnode
        this.keyToCache = key
      }
      // 给虚拟节点增加标识 data: keepAlive:true
      vnode.data.keepAlive = true
    }
    // vnode上有 data.keepAlive 和 componentInstance 说明vnode缓存过
    return vnode || (slot && slot[0]) // 返回vnode
  }
}

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

在vue中有name属性的组件可以被递归调用。(这里可以被类比我们的匿名函数具名化)

在声明组件的时候,Sub.options[name] = 组件

image-20220502184432294

  • 我们用来标识组件,通过name可以找到该组件。也可以自己封装跨级通信
  • name属性还能用作devtool调试工具,来标明具体的组件。

主要用作组件递归和起到标识作用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尤雨东

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值