• 记一场vue面试


    Vue 修饰符有哪些

    事件修饰符

    • .stop 阻止事件继续传播
    • .prevent 阻止标签默认行为
    • .capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
    • .self 只当在 event.target 是当前元素自身时触发处理函数
    • .once 事件将只会触发一次
    • .passive 告诉浏览器你不想阻止事件的默认行为

    v-model 的修饰符

    • .lazy 通过这个修饰符,转变为在 change 事件再同步
    • .number 自动将用户的输入值转化为数值类型
    • .trim 自动过滤用户输入的首尾空格

    键盘事件的修饰符

    • .enter
    • .tab
    • .delete (捕获“删除”和“退格”键)
    • .esc
    • .space
    • .up
    • .down
    • .left
    • .right

    系统修饰键

    • .ctrl
    • .alt
    • .shift
    • .meta

    鼠标按钮修饰符

    • .left
    • .right
    • .middle

    Vue模版编译原理

    vue中的模板template无法被浏览器解析并渲染,因为这不属于浏览器的标准,不是正确的HTML语法,所有需要将template转化成一个JavaScript函数,这样浏览器就可以执行这一个函数并渲染出对应的HTML元素,就可以让视图跑起来了,这一个转化的过程,就成为模板编译。模板编译又分三个阶段,解析parse,优化optimize,生成generate,最终生成可执行函数render。

    • 解析阶段:使用大量的正则表达式对template字符串进行解析,将标签、指令、属性等转化为抽象语法树AST。
    • 优化阶段:遍历AST,找到其中的一些静态节点并进行标记,方便在页面重渲染的时候进行diff比较时,直接跳过这一些静态节点,优化runtime的性能。
    • 生成阶段:将最终的AST转化为render函数字符串。

    简述 mixin、extends 的覆盖逻辑

    (1)mixin 和 extends mixin 和 extends均是用于合并、拓展组件的,两者均通过 mergeOptions 方法实现合并。

    • mixins 接收一个混入对象的数组,其中混入对象可以像正常的实例对象一样包含实例选项,这些选项会被合并到最终的选项中。Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。
    • extends 主要是为了便于扩展单文件组件,接收一个对象或构造函数。

    (2)mergeOptions 的执行过程

    • 规范化选项(normalizeProps、normalizelnject、normalizeDirectives)
    • 对未合并的选项,进行判断
    if (!child._base) {
      if (child.extends) {
        parent = mergeOptions(parent, child.extends, vm);
      }
      if (child.mixins) {
        for (let i = 0, l = child.mixins.length; i < l; i++) {
          parent = mergeOptions(parent, child.mixins[i], vm);
        }
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 合并处理。根据一个通用 Vue 实例所包含的选项进行分类逐一判断合并,如 props、data、 methods、watch、computed、生命周期等,将合并结果存储在新定义的 options 对象里。
    • 返回合并结果 options。

    路由的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

    使用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
    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模式都有各自的优势和缺陷,还是要根据实际情况选择性的使用。

    v-model 是如何实现的,语法糖实际是什么?

    (1)作用在表单元素上 动态绑定了 input 的 value 指向了 messgae 变量,并且在触发 input 事件的时候去动态把 message设置为目标值:

    <input v-model="sth" />
    //  等同于
    <input     v-bind:value="message"     v-on:input="message=$event.target.value"
    >
    //$event 指代当前触发的事件对象;//$event.target 指代当前触发的事件对象的dom;//$event.target.value 就是当前dom的value值;//在@input方法中,value => sth;//在:value中,sth => value;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2)作用在组件上 在自定义组件中,v-model 默认会利用名为 value 的 prop和名为 input 的事件

    本质是一个父子组件通信的语法糖,通过prop和$.emit实现。 因此父组件 v-model 语法糖本质上可以修改为:

    <child :value="message"  @input="function(e){message = e}"></child>
    
    
    • 1
    • 2

    在组件的实现中,可以通过 v-model属性来配置子组件接收的prop名称,以及派发的事件名称。
    例子:

    // 父组件
    <aa-input v-model="aa"></aa-input>
    // 等价于
    <aa-input v-bind:value="aa" v-on:input="aa=$event.target.value"></aa-input>
    
    // 子组件:
    <input v-bind:value="aa" v-on:input="onmessage"></aa-input>
    
    props:{value:aa,}
    methods:{
        onmessage(e){
            $emit('input',e.target.value)
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    默认情况下,一个组件上的v-model 会把 value 用作 prop且把 input 用作 event。但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。js 监听input 输入框输入数据改变,用oninput,数据改变以后就会立刻出发这个事件。通过input事件把数据 e m i t 出 去 , 在 父 组 件 接 受 。 父 组 件 设 置 v − m o d e l 的 值 为 i n p u t ‘ emit 出去,在父组件接受。父组件设置v-model的值为input ` emitvmodelinputemit`过来的值。

    $nextTick 原理及作用

    Vue 的 nextTick 其本质是对 JavaScript 执行原理 EventLoop 的一种应用。

    nextTick 的核心是利用了如 Promise 、MutationObserver、setImmediate、setTimeout的原生 JavaScript 方法来模拟对应的微/宏任务的实现,本质是为了利用 JavaScript 的这些异步回调任务队列来实现 Vue 框架中自己的异步回调队列。

    nextTick 不仅是 Vue 内部的异步队列的调用方法,同时也允许开发者在实际项目中使用这个方法来满足实际应用中对 DOM 更新数据时机的后续逻辑处理

    nextTick 是典型的将底层 JavaScript 执行原理应用到具体案例中的示例,引入异步更新队列机制的原因∶

    • 如果是同步更新,则多次对一个或多个属性赋值,会频繁触发 UI/DOM 的渲染,可以减少一些无用渲染
    • 同时由于 VirtualDOM 的引入,每一次状态发生变化后,状态变化的信号会发送给组件,组件内部使用 VirtualDOM 进行计算得出需要更新的具体的 DOM 节点,然后对 DOM 进行更新操作,每次更新状态后的渲染过程需要更多的计算,而这种无用功也将浪费更多的性能,所以异步渲染变得更加至关重要

    Vue采用了数据驱动视图的思想,但是在一些情况下,仍然需要操作DOM。有时候,可能遇到这样的情况,DOM1的数据发生了变化,而DOM2需要从DOM1中获取数据,那这时就会发现DOM2的视图并没有更新,这时就需要用到了nextTick了。

    由于Vue的DOM操作是异步的,所以,在上面的情况中,就要将DOM2获取数据的操作写在$nextTick中。

    this.$nextTick(() => {    // 获取数据的操作...})
    
    
    • 1
    • 2

    所以,在以下情况下,会用到nextTick:

    • 在数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的DOM结构的时候,这个操作就需要方法在nextTick()的回调函数中。
    • 在vue生命周期中,如果在created()钩子进行DOM操作,也一定要放在nextTick()的回调函数中。

    因为在created()钩子函数中,页面的DOM还未渲染,这时候也没办法操作DOM,所以,此时如果想要操作DOM,必须将操作的代码放在nextTick()的回调函数中。

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

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

    描述下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)使用案例

    初级应用:

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

    高级应用:

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

    Vue 单页应用与多页应用的区别

    概念:

    • SPA单页面应用(SinglePage Web Application),指只有一个主页面的应用,一开始只需要加载一次js、css等相关资源。所有内容都包含在主页面,对每一个功能模块组件化。单页应用跳转,就是切换相关组件,仅仅刷新局部资源。
    • MPA多页面应用 (MultiPage Application),指有多个独立页面的应用,每个页面必须重复加载js、css等相关资源。多页应用跳转,需要整页资源刷新。

    Computed 和 Methods 的区别

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

    不同点:

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

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

    不会立即同步执行重新渲染。Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化, Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

    如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环tick中,Vue 刷新队列并执行实际(已去重的)工作。

    Vue 模板编译原理

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

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

    Vue为什么没有类似于React中shouldComponentUpdate的生命周期?

    考点: Vue的变化侦测原理

    前置知识: 依赖收集、虚拟DOM、响应式系统

    根本原因是Vue与React的变化侦测方式有所不同

    React是pull的方式侦测变化,当React知道发生变化后,会使用Virtual Dom Diff进行差异检测,但是很多组件实际上是肯定不会发生变化的,这个时候需要用shouldComponentUpdate进行手动操作来减少diff,从而提高程序整体的性能.

    Vue是pull+push的方式侦测变化的,在一开始就知道那个组件发生了变化,因此在push的阶段并不需要手动控制diff,而组件内部采用的diff方式实际上是可以引入类似于shouldComponentUpdate相关生命周期的,但是通常合理大小的组件不会有过量的diff,手动优化的价值有限,因此目前Vue并没有考虑引入shouldComponentUpdate这种手动优化的生命周期.

    MVVM、MVC、MVP的区别

    MVC、MVP 和 MVVM 是三种常见的软件架构设计模式,主要通过分离关注点的方式来组织代码结构,优化开发效率。

    在开发单页面应用时,往往一个路由页面对应了一个脚本文件,所有的页面逻辑都在一个脚本文件里。页面的渲染、数据的获取,对用户事件的响应所有的应用逻辑都混合在一起,这样在开发简单项目时,可能看不出什么问题,如果项目变得复杂,那么整个文件就会变得冗长、混乱,这样对项目开发和后期的项目维护是非常不利的。

    (1)MVC

    MVC 通过分离 Model、View 和 Controller 的方式来组织代码结构。其中 View 负责页面的显示逻辑,Model 负责存储页面的业务数据,以及对相应数据的操作。并且 View 和 Model 应用了观察者模式,当 Model 层发生改变的时候它会通知有关 View 层更新页面。Controller 层是 View 层和 Model 层的纽带,它主要负责用户与应用的响应操作,当用户与页面产生交互的时候,Controller 中的事件触发器就开始工作了,通过调用 Model 层,来完成对 Model 的修改,然后 Model 层再去通知 View 层更新。

    (2)MVVM

    MVVM 分为 Model、View、ViewModel:

    • Model代表数据模型,数据和业务逻辑都在Model层中定义;
    • View代表UI视图,负责数据的展示;
    • ViewModel负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;

    Model和View并无直接关联,而是通过ViewModel来进行联系的,Model和ViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。

    这种模式实现了 Model和View的数据自动同步,因此开发者只需要专注于数据的维护操作即可,而不需要自己操作DOM。

    (3)MVP

    MVP 模式与 MVC 唯一不同的在于 Presenter 和 Controller。在 MVC 模式中使用观察者模式,来实现当 Model 层数据发生变化的时候,通知 View 层的更新。这样 View 层和 Model 层耦合在一起,当项目逻辑变得复杂的时候,可能会造成代码的混乱,并且可能会对代码的复用性造成一些问题。MVP 的模式通过使用 Presenter 来实现对 View 层和 Model 层的解耦。MVC 中的Controller 只知道 Model 的接口,因此它没有办法控制 View 层的更新,MVP 模式中,View 层的接口暴露给了 Presenter 因此可以在 Presenter 中将 Model 的变化和 View 的变化绑定在一起,以此来实现 View 和 Model 的同步更新。这样就实现了对 View 和 Model 的解耦,Presenter 还包含了其他的响应逻辑。

    Vue 中的 key 到底有什么用?

    key 是给每一个 vnode 的唯一 id,依靠 key,我们的 diff 操作可以更准确、更快速 (对于简单列表页渲染来说 diff 节点也更快,但会产生一些隐藏的副作用,比如可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位。)

    diff 算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的 key 与旧节点进行比对,从而找到相应旧节点.

    更准确 : 因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确,如果不加 key,会导致之前节点的状态被保留下来,会产生一系列的 bug。

    更快速 : key 的唯一性可以被 Map 数据结构充分利用,相比于遍历查找的时间复杂度 O(n),Map 的时间复杂度仅仅为 O(1),源码如下:

    function createKeyToOldIdx(children, beginIdx, endIdx) {
      let i, key;
      const map = {};
      for (i = beginIdx; i <= endIdx; ++i) {
        key = children[i].key;
        if (isDef(key)) map[key] = i;
      }
      return map;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Vue的优点

    • 轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十 kb
    • 简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
    • 双向数据绑定:保留了 angular 的特点,在数据操作方面更为简单;
    • 组件化:保留了 react 的优点,实现了 html 的封装和重用,在构建单页面应用方面有着独特的优势;
    • 视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
    • 虚拟DOM:dom 操作是非常耗费性能的,不再使用原生的 dom 操作节点,极大解放 dom 操作,但具体操作的还是 dom 不过是换了另一种方式;
    • 运行速度更快:相比较于 react 而言,同样是操作虚拟 dom,就性能而言, vue 存在很大的优势。

    什么是作用域插槽

    插槽

    • 创建组件虚拟节点时,会将组件儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿子进行分类{a:[vnode],b[vnode]}
    • 渲染组件时会拿对应的 slot 属性的节点进行替换操作。(插槽的作用域为父组件)
    <app>
        <div slot="a">xxxxdiv>
        <div slot="b">xxxxdiv>
    app> 
    
    slot name="a" 
    slot name="b"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    作用域插槽

    • 作用域插槽在解析的时候不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。(插槽的作用域为子组件)
    • 普通插槽渲染的作用域是父组件,作用域插槽的渲染作用域是当前子组件。

    // 插槽
    
    const VueTemplateCompiler = require('vue-template-compiler'); 
    let ele = VueTemplateCompiler.compile(` 
         
            
    node
    react
    vue
    `
    ) // with(this) { // return _c('my-component', [_c('div', { // attrs: { "slot": "header" }, // slot: "header" // }, [_v("node")] // _文本及诶点 ) // , _v(" "), // _c('div', [_v("react")]), _v(" "), _c('div', { // attrs: { "slot": "footer" }, // slot: "footer" }, [_v("vue")])]) // } const VueTemplateCompiler = require('vue-template-compiler'); let ele = VueTemplateCompiler.compile(`
    `
    ); with(this) { return _c('div', [_v("node"), _v(" "), _t(_v("vue")])]), _v(" "), _t("default")], 2) } // _t定义在 core/instance/render-helpers/index.js
    • 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
    // 作用域插槽:
    let ele = VueTemplateCompiler.compile(` 
            
    {{msg.a}}
    `
    ); // with(this) { // return _c('app', { scopedSlots: _u([{ // // 作用域插槽的内容会被渲染成一个函数 // key: "footer", // fn: function (msg) { // return _c('div', {}, [_v(_s(msg.a))]) } }]) // }) // } // } const VueTemplateCompiler = require('vue-template-compiler'); VueTemplateCompiler.compile(`
    `
    ); // with(this) { return _c('div', [_t("footer", null, { "a": "1", "b": "2" })], 2) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Watch中的deep:true是如何实现的

    当用户指定了 watch 中的deep属性为 true 时,如果当前监控的值是数组类型。会对对象中的每一项进行求值,此时会将当前 watcher存入到对应属性的依赖中,这样数组中对象发生变化时也会通知数据更新

    源码相关

    get () { 
        pushTarget(this) // 先将当前依赖放到 Dep.target上 
        let value 
        const vm = this.vm 
        try { 
            value = this.getter.call(vm, vm) 
        } catch (e) { 
            if (this.user) { 
                handleError(e, vm, `getter for watcher "${this.expression}"`) 
            } else { 
                throw e 
            } 
        } finally { 
            if (this.deep) { // 如果需要深度监控 
            traverse(value) // 会对对象中的每一项取值,取值时会执行对应的get方法 
        }popTarget() 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Vue组件为什么只能有一个根元素

    vue3中没有问题

    Vue.createApp({
      components: {
        comp: {
          template: `
            
    root1
    root2
    `
    } } }).mount('#app')
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. vue2中组件确实只能有一个根,但vue3中组件已经可以多根节点了。
    2. 之所以需要这样是因为vdom是一颗单根树形结构,patch方法在遍历的时候从根节点开始遍历,它要求只有一个根节点。组件也会转换为一个vdom
    3. vue3中之所以可以写多个根节点,是因为引入了Fragment的概念,这是一个抽象的节点,如果发现组件是多根的,就创建一个Fragment节点,把多个根节点作为它的children。将来patch的时候,如果发现是一个Fragment节点,则直接遍历children创建或更新

    v-if、v-show、v-html 的原理

    • v-if会调用addIfCondition方法,生成vnode的时候会忽略对应节点,render的时候就不会渲染;
    • v-show会生成vnode,render的时候也会渲染成真实节点,只是在render过程中会在节点的属性中修改show属性值,也就是常说的display;
    • v-html会先移除节点下的所有节点,调用html方法,通过addProp添加innerHTML属性,归根结底还是设置innerHTML为v-html的值。
  • 相关阅读:
    SPSS列联表分析
    【案例】超声波测距系统设计
    【计算机网络】习题(三)—— 数据链路层
    Java mybatis spring习题
    微信支付Native(一)准备和相关知识
    线上牛奶配送
    SQL常见问题之如何分析周活跃率?(每周的活跃率)——步骤具体且明确
    C++:二叉搜索树(非平衡化)
    LVS,Nginx,Haproxy三种负载均衡产品的对比
    【leetcode】最多可以参加的会议数目
  • 原文地址:https://blog.csdn.net/bb_xiaxia1998/article/details/127915654