前端面试题三(vue篇)

638 阅读6分钟

1.说说Vue组件间通信方式

通信种类

  1. 父组件向子组件通信
  2. 子组件向父组件通信 blog.csdn.net/qq_37570945…
  3. 隔代组件间通信
  4. 兄弟组件间通信

实现通信方式

(一). props

  1. 通过一般 标签 属性实现父向子通信
  2. 通过 *** 函数 *** 属性实现子向父通信
  3. 缺点: 隔代组件和兄弟组件通信比较麻烦
  • 隔代组件得逐层传递
  • 兄弟组件得借助父组件

(二). vue自定义事件

  1. vue内置实现,可以代替函数类型的props
  • 1). 绑定监听:<MyComp @eventName="callback"
  • 2). 触发(分发)事件: this.$emit('eventName',data)
在父组件写入子组件标签时,给它绑定自定义监听,给它指定回调函数,在子组件中分发事件,vue提供了$emit('指定时间名',数据),这时候数据就会从子组件传递给父组件
  1. 缺点: 只适合子向父通信

(三). 消息订阅与发布

  1. 需要引入消息订阅与发布的实现库,如:pubsub-js
  • 1). 订阅消息: PubSub.subscribe('msg',(msg,data)=>{})
  • 2). 发布消息:PubSub.publish('msg',data)
  1. 优点: 此方式可用于任意关系组件间的通信

(四). vuex

  1. 是什么: vuex是vue官方提供的集中式管理vue多组件共享状态数据的vue插件
  2. 优点: 对组件间关系没有限制,且相比于pubsub库管理更集中,更方便

(五). slot

  1. 是什么:专门用来实现父向子传递带数据的标签
  • 1). 子组件
  • 2). 父组件
  1. 注意:通信的标签模板是在父组件中解析好再传递给子组件的

2.关于vue,vue设计原则的理解

  1. vue是由饿了么ued团队开发并维护的一个渐进式js框架
  2. vue是一个mvvm的框架
  3. vue的定义和特点:
  • 渐进式JavaScript框架
  • 易用、灵活和高效

渐进式JavaScript框架:

与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

易用性

vue提供数据响应式、声明式模板语法和基于配置的组件系统等核心特性。这些使我们只需要关注应用的核心业务即可,只要会写js、html和css就能轻松编写vue应用。

灵活性

渐进式框架的最大优点就是灵活性,如果应用足够小,我们可能仅需要vue核心特性即可完成功能;随着应用规模不断扩大,我们才可能逐渐引入路由、状态管理、vue-cli等库和工具,不管是应用体积还是学习难度都是一个逐渐增加的平和曲线。

高效性

超快的虚拟 DOM 和 diff 算法使我们的应用拥有最佳的性能表现。

追求高效的过程还在继续,vue3中引入Proxy对数据响应式改进以及编译器中对于静态内容编译的改进都会让vue更加高效。

3.如何使用vue去构建项目

  1. 使用vue-cli脚手架工具构建项目
  2. 也可以直接引入vue.js进行项目的构建

4.Vue生命周期函数

vue生命周期分为四个阶段:

  1. 组件创建时(creating)
  2. 模板渲染时(mounting)
  3. 数据更新时(updating)
  4. 组件卸载时(destroying)

vue生命周期完整过程:

5.Vuex 管理状态的机制

  1. 对Vuex基本理解
  • 1). 是什么:Vuex是一个专为Vue.js应用程序开发的状态管理的vue插件
  • 2). 作用:集中式管理vue多个共享的状态和后台获取数据,主要是为了解决组件间状态共享的问题,强调的是集中式管理,主要便于维护,便于解耦,所以不是所有的项目都适合使用vuex。
  1. Vuex的工作原理

6.说出4个vue当中的指令和它的用法

  1. v-if 条件渲染指令,代表存在销毁
  2. v-bind 绑定指令,用来绑定属性(简写方式是:)
  3. v-on 监听事情指令(简写是@)
  4. v-for 循环指令

7.导航钩子有哪些?他们有哪些参数?

  1. 导航钩子其实就是路由的生命周期(vue-router)
  2. 主要分为 全局局部
  3. 全局钩子函数
  • 1). beforeEach: 路由切换开始调用
  • 2). afterEach: 在路由切换离开时调用
  1. 局部到单个路由
  • 1). beforeEnter
  1. 组件的钩子函数
  • 1). beforeRouterEnter
  • 2). beforeRouterUpdate
  • 3). beforeRouterLeave
  1. 参数
  • 1). to:即将进入的目标对象
  • 2). from:当前导航要离开对象
  • 3). next: 是一个函数 调用 resolve 执行一下

8.v-model是什么?Vue中标签怎么绑定事件?

  1. vue中利用v-model来进行表单数据的双向绑定
  2. v-bind 绑定一个value的属性
  3. 利用v-on 把当前的元素绑定到一个事件上
<div id="demo">
    <input v-model="inputValue" />
    <p>{{inputValue}}</p>
    
    <input v-bind:value="inputValue2" v-on:input="inputValue2 = $event.target.value" />
</div>
<script>
    var vm = new Vue({
        el:"#demo",
        data:{
            inputValue:'',
            inputValue2:''
        }
    })
</script>

9.路由懒加载

理解:路由懒加载 也叫延迟加载,即在你需要的时候加载

如何使用:使用到了vue中的异步组件,配合webpack的代码分离,只需要自己返回一个permise函数,resolve中只包含你想要用的那个xxx.vue文件就可以了。

//在router文件的index.js中进行懒加载
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Detail from '@/components/Detail'

function resolveView(view){
    return ()=> import(/*webpack chunk name*/`@/components/${view}.vue`)
}

export default new Router({
    routes:[
        {
            path:'/',
            name:'HelloWorld',
            component: resolveView('HelloWorld')
        },
        {
            path:'/detail',
            name:'Detail',
            component: resolveView('Detail')
        },
    ]
})

10.Vue-loader 解释一下

(一).什么是vue-loader?

  • vue-loader 就是一个加载器,能把.vue组件转化成Javascript模块

(二).为什么我们要转译这个vue组件

  • 可以动态的渲染一些数据
  • 对三个标签都做了优化,script中可以直接使用es6 style 也默认可以使用sass,并且 还提供了作用域的选择
  • 另外开发阶段,还提供了热加载

11.用过插槽码?用的具名插槽还是匿名插槽呢?

含义:

  1. vue中的插槽 是一个非常好用的东西,slot 说白了就是一个占位
  2. 在vue当中 插槽包含三种 一种是默认插槽(匿名插槽),一种是具名插槽,一种是作用域插槽
  • 1). 匿名插槽 :就是没有名字的,只要默认的 都填到这里
  • 2). 具名插槽 :指的是具有名字的
  • 3). 作用域插槽 : 指只作用域当前的slot

举例:

//子组件about.vue
<template>
    <div>
        <h2>关于插槽</h2>
        <slot name="header"></slot>                 /*具名插槽*/
        <slot></slot>                               /*匿名插槽*/
        <slot name="footer" say="hello"></slot>     /*say='hello'传递的参数*/
    </div>
</template>
<script>
    export default {
        name:'about'
    }
</script>

//父组件:helloWorld.vue
<template>
    <div>
        <About>
            <h2>这是HelloWorld组件的内容</h2>
            <div slot="footer" slot-scope="aaa">
                {{aaa}}这是底部
            </div>
            <div></div>
            <div slot="header" slot-scope="aaa">
                {{aaa}}这是头部
            </div>
        </About>
    </div>
</template>
<script>
import About from '@/componts/About';
export defalut {
    name:'HelloWorld',
    components:{
        About
    }
}
</script>

  • 打印结果:

12.说说你对vue虚拟DOM的理解

(一).什么是虚拟dom

  • 说白了 就是以js对象的形式 去添加dom元素
  • 本质上是优化了diff算法
  • 采用了 新旧dom的对比 获取你差异的dom,一次性更新到 你真实的dom上
  • 虚拟dom本身也有自己的缺陷,他更适合批量修改dom
  • 尽量不要跨层级修改dom
  • 设置key 可以最大的利用节点

13.如何理解Vue的MVVM模式

(一). mvvm 和 mvc 之间的区别

  1. mvc 指的是model view controller
  2. mvvm指的是model view viewModel
  3. 而vue专注于view 和 viewModel 的框架

(二). mvc流程

  • 用户输入数据(view) =>给到控制器(controller)=>控制器判断可以把数去传递给model => model传递完数据会再给到view
  • 有时候用户把数据(view)传递给控制期(controller),controller会判断 这个数据不需要经过数据库请求,就可以把数据直接返回给view
  • view 不需要业务处理,只需要数据请求,view 也可以直接像model请求

(三). mvvm流程

14.Vue中keep-alive的作用

(一).什么是keep-alive

  • 说白了 它能让不活动的组件 "活着"
  • 它提供了 includeexclude 两个属性 允许组件有条件的缓存

(二).实现的原理

  • 其实就是在created的时候将需要的缓存的vnode节点 放到 cache 中,在render的时候 根据name进行取出

(三).怎么使用它

  • 在路由的meta中添加 keepAlive 属性
  • 跟 router-view 配合使用的
// router/index.js
export default new Router({
    routes:[
        {
            path:'/',
            name:'HelloWorld',
            component:resolveView('HelloWorld'),
            meta:{
                keepAlive:true
            }
        }
    ]
})
// App.vue
<template>
    <div>
        <keep-alive>
            <router-view v-if="$route.meta.keepAlive" />
        </keep-alive>
    </div>
</template>
  • $route.meta.keepAlive 根据页面的不同情况来缓存

15.v-if和v-for哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?

  • 源码中找答案compiler/codegen/index.js
  • 结论
  1. 显然v-for优先于v-if被解析
  2. 如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费了性能
  3. 要避免出现这种情况,则在外层嵌套template,在这一层进行v-if判断,然后在内部进行v-for循环
  4. 如果条件出现在循环内部,可通过计算属性提前过滤掉那些不需要显示的项

16.Vue组件data为什么必须是个函数而Vue的根实例则没有此限制?

  • 源码中找答案:src\core\instance\state.js - initData()
  • 结论
  1. Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的;
  2. 采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,这样大家都是独立的,虽然对象的数据是一样的,但是我指向不同的引用,相互不会干扰,有效规避多实例之间状态污染问题。
  3. 而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况。
  4. (之前我在看源码的时候,我曾经看过数据初始化那部分的代码,我发现它会检测data的形式,从而去执行它的具体执行方式,另外的话,由于根实例在创建的时候,可能在合并选项的时候,它会有实例拿到,只有根实例才会有,它可以有效的躲过关于data选项的校验,而如果是个普通的组件的话,由于它当时不存在实例,所以它没有办法躲过那个校验的if逻辑,它会直接被检测是否是data类型,所以它就没有办法跳过这个校验)

17.vue中key的作用和工作原理,说下理解?

  1. key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。 (其原理是什么呢?我可以通过源码的方式给你解释,因为我曾经看过源码,在patch的过程中,会执行patchVnode, patchVnode的过程中会执行 updateChildren()这个方法,它会去更新所有的两个新旧元素,在这个过程中,通过key我可以精准的判断这两个节点是不是一个节点,如果没有加key的话永远认为是一个相同的节点,所以能做到的就是强硬的去更新,那么我们就没有办法去避免频繁的更新操作,所以会额外多很多dom操作,不加key的性能就会很差,当然 如果我加上了key,就会通过一系列优化的算法,比如我会猜测首尾结构的相似性,由于大部分情况下,元素都不会发生大量的位置变化,所以我会很高效的结束这个循环,减少大量的操作过程)
  2. 另外,若不设置key还可能在列表更新时引发一些隐蔽的bug(比如某一行你认为不应该更新的,结果更新了,你认为应该变的可能它就没有变,就是因为没有合理的设置key)
  3. vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。

18.你怎么理解vue中的diff算法?

  1. diff算法是虚拟DOM技术的必然产物:通过新旧虚拟DOM作对比(即diff),将变化的地方更新在真实DOM上;另外,也需要diff高效的执行对比过程,从而降低时间复杂度为O(n)。
  2. vue 2.x中为了降低Watcher粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精确找到发生变化的地方。
  3. vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上一次渲染结果oldVnode和新的渲染结果newVnode,此过程称为patch。(打补丁)
  4. diff过程整体遵循深度优先、同层比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做4次比对尝试,如果没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点,因此整个patch过程非常高效。

19. 谈一谈对vue组件化的理解?

  1. 组件是独立和可复用的代码组织单元。组件系统是 Vue 核心特性之一,它使开发者使用小型、独立和通常可复用的组件构建大型应用;
  2. 组件化开发能大幅提高应用开发效率、测试性、复用性等;
  3. 组件使用按分类有:页面组件、业务组件、通用组件; (页面组件:用路由来导航一些页面组件,特点 复用性不强,但是它是组织我们不同内容来回切换的必备组件; 业务组件:类似于登录的组件,购物车的bar组件,有很强的业务性,通用性;通用组件:类似于表单,按钮)
  4. vue的组件很特别是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent类,扩展于Vue,(在扩展过程中,我们会继成vue中已经有的这些选项;)
  5. vue中常见组件化技术有:属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等;
  6. 合理的划分组件,有助于提升应用性能;
  7. 组件应该是高内聚、低耦合的;
  8. 遵循单向数据流的原则。

20. vue性能优化的方法?

  • 答题思路:根据题目描述,这里主要探讨Vue代码层面的优化

(一). 路由懒加载

const router = new VueRouter({
 routes: [
    { path: '/foo', component: () => import('./Foo.vue') }
  ]
})

(二).kep-alive缓存页面

<template>
 <div id="app">
 <keep-alive>
 <router-view/>
 </keep-alive>
 </div>
</template>

(三). 使用v-show复用DOM

<template>
 <div class="cell">
 <!--这种情况用v-show复用DOM,比v-if效果好-->
 <div v-show="value" class="on">
 <Heavy :n="10000"/>
 </div>
 <section v-show="!value" class="off">
 <Heavy :n="10000"/>
 </section>
 </div>
</template>

(四). v-for 遍历避免同时使用 v-if

(五). 长列表性能优化

  • 如果列表是纯粹的数据展示,不会有任何改变,就不需要做响应化
  • 如果是大数据长列表,可采用虚拟滚动,只渲染少部分区域的内容

(六). Vue 组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。

created() {
  this.timer = setInterval(this.refresh, 2000)
},
beforeDestroy() {
  clearInterval(this.timer)
}

(七). 图片懒加载

对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。

<img v-lazy="/static/img/1.png">

(八). 第三方插件按需引入

像element-ui这样的第三方组件库可以按需引入避免体积太大。

import Vue from 'vue';
import { Button, Select } from 'element-ui';

 Vue.use(Button)
 Vue.use(Select)

(九). 无状态的组件标记为函数式组件

<template functional>
 <div class="cell">
 <div v-if="props.value" class="on"></div>
 <section v-else class="off"></section>
 </div>
</template>

<script>
export default {
 props: ['value']
}
</script>

(十). 子组件分割

<template>
 <div>
 <ChildComp/>
 </div>
</template>

<script>
export default {
 components: {
 ChildComp: {
 methods: {
 heavy () { /* 耗时任务 */ }
      },
 render (h) {
 return h('div', this.heavy())
      }
    }
  }
}
</script>

(十一). 变量本地化

<template>
 <div :style="{ opacity: start / 300 }">
 {{ result }}
 </div>
</template>

<script>
import { heavy } from '@/utils'

export default {
 props: ['start'],
 computed: {
 base () { return 42 },
 result () {
 const base = this.base // 不要频繁引用this.base
 let result = this.start
 for (let i = 0; i < 1000; i++) {
 result += heavy(base)
      }
 return result
    }
  }
}
</script>