最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue:常见面试题 - 掘金

    正文概述 掘金(_清欢l)   2021-11-10   1287

    整理vue常见基础面试题

    1. 谈谈你对MVVM开发模式的理解

    MVVM分为Model、View、ViewModel三者。

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

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

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

    2.Vue常用指令

    • v-text:元素的InnerText属性,用于数据绑定
    • v-html:元素的InnerHTML属性,用于绑定一段html
    • v-bind:给元素的属性赋值,v-bind后面是  :属性名=[变量名],简写为:属性名
    • v-for:列表的循环渲染。语法v-for="item in data",data 可以是数组或者对象
    • v-show、v-if:控制页面元素的显示和隐藏
    • v-if、v-else-if、v-else:v-if的条件渲染和JavaScript条件判断语句中if、else、else if非常类似。
    • v-on:给元素绑定事件,用法v-on:事件名="方法"
    • v-model:实现数据的双向绑定
    • v-pre:跳过所在元素和它的子元素的编译过程,也就是把这个节点及其子节点当作一个静态节点来处理
    • v-once:模板只会在第一次更新时显示数据,此后再次更新该DOM里面引用的数据时,内容不会自动更新

    3.v-if和v-show

    v-show 仅仅控制元素的显示方式,将 display 属性在 block 和 none 来回切换;

    v-if会控制这个 DOM 节点的存在与否。

    • 主要区别是是否渲染,当我们需要经常切换某个元素的显示/隐藏时,使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。
    • 当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中。所以,不推荐v-if和v-for同时使用。
    • 如果v-if和v-for一起用的话,vue中的的会自动提示v-if应该放到外层去。

    4.V-model原理

    v-model是一个十分强大的指令,它可以自动让原生表单组件的值自动和你选择的值绑定,我们经常用它做双向绑定。view层输入值影响data的属性值,data属性值发生改变会更新view层的数值变化。

    • 依赖收集:在组件初始化时,比如render function里引用到了某个响应式对象,就会触发这个对象属性的getter,这时候就会把这个render function放到这个对象的订阅者列表里(track)
    • 响应下次去设置这个响应式对象的属性时就会触发对象属性的getter,继而调用对应的订阅者列表(trigger)

    1、实现原理浅层理解:触发input事件来修改value值

    v-model其实是个语法糖,它实际上是做了两步动作:

    • v-bind:绑定响应式数据
    • 触发 input 事件 并传递数据 (核心和重点) input>标签有一个oninput事件,该事件类似于onchange事件,不同之处在于 oninput 事件在元素值发生变化是立即触发, onchange 在元素失去焦点时触发。

    2、 深层理解:利用Object.defineProperty()数据劫持来实现。

    现目前使用的很多框架,最大的优点就是可以实现数据绑定,再也不需要手动进行DOM操作了,它们实现的原理之一就是数据劫持。

    3、Object.defineProperty()实现数据劫持

    Object.defineProperty(obj,prop,descriptor)的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。但不能监听数组的变化。

    Vue:常见面试题 - 掘金 在Vue中其实就是通过Object.defineProperty来劫持对象属性的setter和getter操作,并“种下”一个监听器,当数据发生变化的时候发出通知。我们完全可以控制对象属性的设置和读取。getter 函数用于在数据读取时进行依赖收集,在对应的 dep 中存储所有的 watcher;setter 则是数据更新后通知所有的 watcher 进行更新。

    Object.defineProperty(a, 'b', {
      enumerable: false,
      configurable: false,
      get: function(){
        console.log('b' + '被访问');
      },
      set: function(newVal){
        console.log('b' + '被修改,新' + 'b' + '=' + newVal);
      }
    });
     
    a.b = 2;  // b被修改,新b=2
    a.b;    // b被访问
    

    get: 一旦目标属性被访问就会调回此方法,并将此方法的运算结果返回用户。

    set:一旦目标属性被赋值,就会调回此方法。

    注意:当使用了getter或setter方法,不允许使用writable和value这两个属性

    5.如何优化SPA应用的首屏加载速度慢的问题?

    • 将公用的JS库通过script标签外部引入,减小 app.bundel 的大小,让浏览器并行下载资源文件,提高下载速度;
    • 在配置 路由时,页面和组件使用懒加载的方式引入,进一步缩小 app.bundel 的体积,在调用某个组件时再加载对应的js文件;
    • 加一个首屏loading图,提升用户体验;

    6.vue组件间传值

    组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用,一般来说,组件可以有以下几种关系:

    Vue:常见面试题 - 掘金

    如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。

    • ref$parent / $children 适用 父子组件通信
      ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
      parent/parent / parent/children:访问父 / 子实例
    • 父子组件传递数据props/$emit
    • EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通信」这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件
    • $emit/$on 通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级
    • attrs/listeners 适用于 隔代组件通信
      attrs:包含了父作用域中不被prop所识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v−bind="attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
      listeners:包含了父作用域中的(不含.native修饰器的)v−on事件监听器。它可以通过v−on="listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="listeners:包含了父作用域中的(不含.native修饰器的)v−on事件监听器。它可以通过v−on="listeners" 传入内部组件
    • provide / inject 适用于 隔代组件通信
      祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。
      provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
    • vuex适用于 父子、隔代、兄弟组件通信
      Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
      改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

    7.vuex

    Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当然,若应用比较简单,共享状态也比较少,可以用 Vue.observe 去替代 Vuex,省去安装一个库也挺好。

    • 当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。
    • 当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走Action,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。

    属性: State、 Getter、Mutation 、Action、 Module

    • state => 基本数据(数据源存放地),定义并初始化全局状态。
    • getters => 从基本数据派生出来的数据,依赖 State 中的状态,进行二次包装,不会影响 State 源数据。
    • mutations => 更改 State 状态的函数,必须是同步。
    • actions => 用于提交 Mutation,可包含任意异步操作。
    • modules => 若应用复杂,Store 会集中一个比较大的对象而显得臃肿,Module允许我们将 Store模块化管理。

    Vue:常见面试题 - 掘金

    8.computed和watch

    • computed:
      • 计算属性,依赖其他属性值,且值具备缓存的特性。只有它依赖的属性值发生改变,下一次获取的值才会重新计算。
      • 当一个属性受多个属性影响的时候就需要用到computed
      • 适用于数值计算,并且依赖于其他属性时。因为可以利用缓存特性,避免每次获取值,都需要重新计算。购物车商品结算的时候
    • watch:
      • 当一条数据影响多条数据的时候就需要用watch
      • 观察属性,监听属性值变动。每当属性值发生变化,都会执行相应的回调。
      • 适用于数据变化时执行异步或开销比较大的操作。搜索数据

    9.vue组件中data为什么必须是一个函数,key的作用

    组建中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。

    • 因为JavaScript的特性所导致,在component中,data必须以函数的形式存在,不可以是对象。
    • 需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。作用主要是为了高效的更新虚拟DOM。

    9.$nextTick的使用

    当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,
    你需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功。

    10.单页面应用(SPA)和多页面应用(MPA)区别及优缺点

    • SPA:通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
    • MPA:就是指一个应用中有多个页面,页面跳转时是整页刷新

    单页面的优点:用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小;前后端分离;页面效果会比较炫酷(比如切换页面内容时的专场动画)。

    单页面缺点:不利于seo;导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理);初次加载时耗时多;页面复杂度提高很多。

    11.vue常用修饰符

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

    12.vue两个核心点

    数据驱动:ViewModel,保证数据和视图的一致性。
    组件系统:应用类UI可以看作全部是由组件树构成的。

    13.vue生命周期

    • beforeCreate:在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。在beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
    • create:data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
    • beforeMount:执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
    • mounted:执行到这个钩子的时候,就表示Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
    • beforeUpdate: 当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步
    • updated:页面显示的数据和data中的数据已经保持同步了,都是最新的
    • beforeDestory:Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
    • destroyed: 这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。
    1. 第一次加载接口触发:beforeCreate, created, beforeMount, mounted
    • created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
    • mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
    1. 获取数据:一般 created/beforeMount/mounted 皆可.比如如果你要操作 DOM , 那肯定 mounted 时候才能操作.

    14.vue-router

    1. vue用来写路由一个插件。组件:router-link、router-view

    2. vue-router模块的router-link组件。children数组来定义子路由

    3. 定义动态路由:在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id。

    4. 导航钩子:

      - 全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。
      - 组件内的钩子
      - 单独路由独享组件
    

    5. 导航守卫

    • 全局前置守卫:在路由跳转前触发,可在执行 next 方法前做一些身份登录验证的逻辑。
    const router = new VueRouter({})
    
    router.beforeEach((to, from, next) => {
      ...
      // 必须执行 next 方法来触发路由跳转 
      next() 
    })
    
    • 全局解析守卫:与 beforeEach 类似,也是路由跳转前触发,区别是还需在所有组件内守卫和异步路由组件被解析之后,也就是在组件内 beforeRouteEnter 之后被调用。
    const router = new VueRouter({})
    
    router.beforeResolve((to, from, next) => {
      ...
      // 必须执行 next 方法来触发路由跳转 
      next() 
    })
    
    • 全局后置钩子:和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身。
    router.afterEach((to, from) => {
      // ...
    })
    
    • 路由独享守卫:可在路由配置上直接定义 beforeEnter
    const router = new VueRouter({
      routes: [
        {
          path: '/home',
          component: Home,
          beforeEnter: (to, from, next) => {
          
          }
        }
      ]
    })
    
    • 组件内的守卫:组件内可直接定义如下路由导航守卫
    const Foo = {
      template: `...`,
      beforeRouteEnter(to, from, next) {
        // 不能获取组件实例 this
        // 当守卫执行前,组件实例还没被创建
      },
      beforeRouteUpdate(to, from, next) {
        // 当前路由改变,但是组件被复用时调用
        // 可访问实例 this
      },
      beforeRouteLeave(to, from, next) {
        // 导航离开组件时被调用
      }
    }
    

    6. 路由传参

    • 路由配置
    // 路由配置
    {
      path: '/detail/:id',
      name: 'Detail',
      component: () => import('./Detail.vue')
    }
    // 路由跳转
    let id = 1
    this.$router.push({ path: '/detail/${id}'})
    // 获取参数
    this.$route.params.id
    
    • URL 虽然不显示我们的传参,但是是可以在子组件获取参数的。当然也有问题:会存在刷新丢失参数。若想不丢失,需和方案一路由配置一样。原因是第二种方式传参是上一个页面 push 函数中携带的,刷新没有 push 的动作。
    // 路由配置
    {
      path: '/detail',
      name: 'Detail',
      component: () => import('./Detail.vue')
    }
    // 路由跳转
    let id = 1
    this.$router.push({ name: 'Detail', params: { id: id } })
    // 获取参数
    this.$route.params.id
    
    • 路由配置
    // 路由配置
    {
      path: '/detail',
      name: 'Detail',
      component: () => import('./Detail.vue')
    }
    // 路由跳转
    let id = 1
    this.$router.push({ name: 'Detail', query: { id: id } })
    // 获取参数
    this.$route.query.id
    

    15. hashhistoryabstract

    1. hash:监听的hashchange事件,即地址栏 URL 中的 # 符号;hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。兼容性好,支持低版本浏览器和 IE 浏览器。

    2. history:history模式监听pushstate和popstate,(需要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。

    3. abstract:支持所有 JS 运行模式,Vue-Router 自身会对环境做校验,如果发现没有浏览器 API,路由会自动强制进入 abstract 模式。在移动端原生环境也是使用 abstract 模式。

    使用场景:

    • 一般场景下,hash 和 history 都可以,除非你更在意颜值,# 符号夹杂在 URL 里看起来确实有些不太美丽。

    history优势:

    • pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL;
    • pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
    • pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;
    • pushState() 可额外设置 title 属性供后续使用。

    history劣势:

    • 跳转后刷新或者回跳,会报一个404的错误,找不到指定的路由,最后后端去指向正确的路由 加了/hd/xxx 去匹配是否有这个/hd/{:path} 才得以解决

    15.Vue nextTick实现原理

    nextTick:在下次 DOM 更新循环结束之后执行延迟回调。常用于修改数据后获取更新后的DOM。
    源码位置:vue/src/core/util/next-tick.js
    按照浏览器是否支持promise->mutation-observe->setImmediate->setTimeout的顺序来实现,表现出来的效果跟setTimeout大致一样的

    1. 运用异步锁的概念,保证同一时刻任务队列中只有一个 flushCallbacks。当 pengding 为 false 的时候,表示浏览器任务队列中没有 flushCallbacks 函数;当 pengding 为 true 的时候,表示浏览器任务队列中已经放入 flushCallbacks;待执行 flushCallback 函数时,pengding 会被再次置为 false,表示下一个 flushCallbacks 可进入任务队列。
    2. 环境能力检测,选择可选中效率最高的(宏任务/微任务)进行包装执行,保证是在同步代码都执行完成后再去执行修改 DOM 等操作。
    3. flushCallbacks 先拷贝再清空,为了防止nextTick嵌套nextTick导致循环不结束。

    16.vue源码核心

    • 模板编译compiler
    • 虚拟dom及diff
    • 虚拟dom转真实dom
    • 基于依赖收集的响应式原理

    17.vue的Diff算法

    核心源码:vue/src/core/vdom/patch.js
    为什么要diff、同层对比、深度优先、两端对比

    • 首先需要diff的原因是操作dom的代价是昂贵的,我们要减少操作dom所以需要对比出新老虚拟dom的最小差异点,尽可能复用原有dom

    • 因为我们很少跨层级的去移动dom,所以diff算法只会对比同一父节点下的子节点,使算法的时间复杂度是O(n)

    • 深度优先,比较新vnode找到对应的老vnode,这时会先执行patchChildren进入子节点的比较,所以是深度优先

    • 首尾两端对比法,依次把旧vnode数组的首尾分别和新vnode数组的首尾进行对比,能匹配上就进入他们子节点的对比,都匹配不上就通过key索引找新vnode数组第一个节点对应的在旧vnode数组里的index,还是找不到的话就新建一个vnode。当旧vnode数组里还有节点,而新vnode数组已经遍历完了时就remove掉对应的多出来的真实dom

    18.slot插槽

    slot 插槽,可以理解为slot在组件模板中提前占据了位置。当复用组件时,使用相关的slot标签时,标签里的内容就会自动替换组件模板中对应slot标签的位置,作为承载分发内容的出口。
    主要作用是复用和扩展组件,做一些定制化组件的处理。

    • 默认插槽
    // 子组件
    <template>
      <slot>
        <div>默认插槽备选内容</div>
      </slot>
    </template>
    
    // 父组件
    <template>
      <Child>
        <div>替换默认插槽内容</div>
      </Child>
    </template>
    
    • 具名插槽:slot 标签没有name属性,则为默认插槽。具备name属性,则为具名插槽
    // 子组件
    <template>
      <slot>默认插槽的位置</slot>
      <slot name="content">插槽content内容</slot>
    </template>
    
    // 父组件
    <template>
       <Child>
         <template v-slot:default>
           默认...
         </template>
         <template v-slot:content>
           内容...
         </template>
       </Child>
    </template>
    
    • 作用域插槽:子组件在作用域上绑定的属性来将组件的信息传给父组件使用,这些属性会被挂在父组件接受的对象上
    // 子组件
    <template>
      <slot name="footer" childProps="子组件">
        作用域插槽内容
      </slot>
    </template>
    
    // 父组件
    <template>
      <Child v-slot="slotProps">
        {{ slotProps.childProps }}
      </Child>
    </template>
    

    19. Vue.directive

    Vue.directive 可以注册全局指令和局部指令。

    指令定义函数提供如下钩子函数

    1. bind:指令第一次绑定到元素时调用(只调用一次)
    2. inserted: 被绑定元素插入父节点时使用(父节点存在即可调用)
    3. update:被绑定元素所在模板更新时调用,不论绑定值是否变化。通过比较更新前后的绑定值。
    4. componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
    5. unbind: 只调用一次,指令与元素解绑时调用。

    项目中有涉及 一键copy、权限控制 都可以用指令的方式控制,目的就是简化我们的工作量。

    20.Vue 过滤器

    Vue 过滤器可用在两个地方:双花括号插值和 v-bind 表达式。
    Vue3 中已经废弃这个特点。

    • 局部过滤器
      <div>{{ message | formatMessage }}</div>
    </template>
    <script>
    export default {
      filters: {
        formatMessage: function(value) {
          // 可基于源值做一些处理
          return value
        }
      }
    }
    </script>
    
    • 全局过滤器
      // 可基于源值做一些处理
      return value
    })
    

    过滤器可串联,执行顺序从左到右,第二个过滤器输入值是第一个过滤器的输出值。

    <div>{{ message | formatMessage1 | formatMessage2 }}</div>
    

    21.proxy与Object.defineProperty 优劣对比

    「Proxy 的优势如下:」

    • Proxy 可以直接监听对象而非属性;
    • Proxy 可以直接监听数组的变化;
    • Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是
    • Object.defineProperty 不具备的;
    • Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
    • Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;

    「Object.defineProperty 的优势如下:」

    • 兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue
      的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。

    22.自定义组件

    • 在components目录新建你的组件文件(smithButton.vue),script一定要export default {
    • 在需要用的页面(组件)中导入:import smithButton from ‘../components/smithButton.vue’
    • 注入到vue的子组件的components属性上面,components:{smithButton}
    • template视图view中使用,<smith-button>  </smith-button>
    • smithButton命名,使用的时候则smith-button

    23.Vue.js的template编译的理解

    先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)

    • 首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式),compilecreateCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。

    • 然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)

    24.vue项目优化

    1. 代码层面优化
    • v-if 和 v-show 区分使用场景
    • computed 和 watch 区分使用场景
    • v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
    • 长列表性能优化
    • 事件的销毁
    • 图片资源懒加载
    • 路由懒加载
    • 第三方插件的按需引入
    • 优化无限列表性能
    • 服务端渲染 SSR or 预渲染
    1. webpack层面优化
    • Webpack 对图片进行压缩
    • 减少 ES6 转为 ES5 的冗余代码
    • 提取公共代码
    • 模板预编译
    • 提取组件的 CSS
    • 优化 SourceMap
    • 构建结果输出分析
    • Vue 项目的编译优化

    3.基础web技术优化

    • 开启 gzip 压缩
    • 浏览器缓存
    • CDN 的使用
    • 使用 Chrome Performance 查找性能瓶颈

    25.vue3.0新特性

    1. 检测机制的改变
      3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:
      • 只能监测属性,不能监测对象
      • 检测属性的添加和删除;
      • 检测数组索引和长度的变更;
      • 支持 Map、Set、WeakMap 和 WeakSet。

    新的 observer 还提供了以下特性:

    • 用于创建 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。
      • 默认采用惰性观察。在 2.x 中,不管反应式数据有多大,都会在启动时被观察到。如果你的数据集很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分的数据。
      • 更精确的变更通知。在 2.x 中,通过 Vue.set 强制添加新属性将导致依赖于该对象的 watcher 收到变更通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。
      • 不可变的 observable:我们可以创建值的“不可变”版本(即使是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树以外的变化。
      • 更好的调试功能:我们可以使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在什么时候以及为什么重新渲染
    1. 模板
      模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。
    2. 对象式的组件声明方式
      vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。

    4.其它方面的更改
    vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其他的更改:

    • 支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
    • 支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。
    • 基于 treeshaking 优化,提供了更多的内置功能。

    26.源码结构

    -- config                           // 项目开发环境配置
    |   |-- index.js                     // 项目打包部署配置
    |-- src                              // 源码目录
    |   |-- components                   // 公共组件
    |       |-- header.vue               // 页面头部公共组件
    |       |-- footer.vue               // 页尾头部公共组件
    |       |-- index.js                 // 加载各种公共组件
    |   |-- config                       // 路由配置和程序的基本信息配置
    |       |-- routes.js                // 配置页面路由
    |   |-- css                          // 各种css文件
    |       |-- common.css               // 全局通用css文件
    |   |-- iconfont                     // 各种字体图标
    |   |-- images                       // 公共图片
    |   |-- less                         // 各种less文件
    |       |-- common.less              // 全局通用less文件
    |   |-- pages                        // 页面组件
    |       |-- home                     // 个人中心
    |       |-- index                    // 网站首页
    |       |-- login                    // 登录
    |       |-- signout                  // 退出
    |   |-- store                        // vuex的状态管理
    |       |-- index.js                 // 加载各种store模块
    |       |-- user.js                  // 用户store
    |   |-- template                     // 各种html文件
    |       |-- index.html               // 程序入口html文件
    |   |-- util                         // 公共的js方法,vue的mixin混合
    |   |-- app.vue                      // 页面入口文件
    |   |-- main.js                      // 程序入口文件,加载各种公共组件
    |-- .babelrc                         // ES6语法编译配置
    |-- gulpfile.js                      // 启动,打包,部署,自动化构建
    |-- webpack.config.js                // 程序打包配置
    |-- server.js                        // 代理服务器配置
    |-- README.md                        // 项目说明
    |-- package.json                     // 配置项目相关信息,通过执行 npm init 命令创建


    起源地下载网 » Vue:常见面试题 - 掘金

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元