面试总结三大模块:Vue双向绑定及原理、生命周期、组件通信、Vue官方API

目录:1.Vue双向绑定及原理

    1.1你对MVVM是怎么理解的?

    1.2你对Vue响应式原理是怎么理解的?是否可以实现一个简版的?Vue2中是如何监听数组的变化的?Vue3使用Proxy重写,相比Vue2的Object.defineProperty,有哪些优势?

      1.2.1响应式原理

      1.2.2 Vue2 Object.defineProperty简版实现代码

      1.2.3 针对弊端解决 无法监听数组问题:Vue2中是如何监听数组的变化的?

      1.2.4 针对弊端解决 无法劫持对象的新增属性:this.$set能够解决对象新增属性问题。延伸问题:$set为啥能监测数组变动?

      1.2.5 Vue3 proxy简版实现代码

    1.3你对Vue的v-model双向绑定是怎么理解的?是否可以实现一个简版的

      1.3.1 v-model双向绑定理解

      1.3.2 v-model双向绑定实现原理代码简版

      1.3.3 双向绑定原理

    2.生命周期

    2.1Vue有哪些生命周期?它们有哪些使用场景?你的接口请求一般都放在哪个生命周期方法中,为什么?你的获取dom的方法一般都放在哪个生命周期方法中,为什么?

        2.1.1vue3中的选项式生命周期钩子及使用场景

        2.1.2 vue3中的组合式生命周期钩子(使用场景对比选项式生命周期钩子
        2.1.3异步请求一般在哪个生命周期,为什么?
        2.1.4获取DOM的方法放在哪个生命周期方法中?为什么?

   3.组件通信

    3.1Vue的子组件如何调用父组件的方法?
        3.1.1直接在子组件中通过this.$parent.event来调用父组件的方法
        3.1.2在子组件里用$emit向父组件触发一个事件,父组件监听这个事件就行了
        3.1.3父组件把方法传入子组件中,在子组件里直接调用这个方法
    3.2Vue组件通信有哪些方式
   4.vue官方API
    4.1Vue的data为啥是函数
    4.2Vue列表中加的key是干什么的
    4.3Vuex是什么,它的使用场景是什么
    4.4Vue-router的history模式和hash模式
      4.4.1hash模式
      4.4.2hash模式特点
      4.4.3history模式
      4.4.4history模式特点
      4.4.5history模式和hash模式的区别
      4.4.6传统路由和前端路由的区别
    4.5有没有写过Vue的指令,它的实现原理是什么?有没有写过Vue的插件,它的实现原理是什么?
      4.5.1指令api:生命周期和钩子函数参数
      4.5.2指令和插件本质
      4.5.3 实现一键复制文本内容,用于鼠标右键粘贴的指令(配合插件执行全局注册)
          4.5.3.1定义指令对象代码(执行的代码不重要,重要在于编写格式)
          4.5.3.2定义一个能够注册指令的插件(不使用插件注册的方法是直接在main调用directive注册就可以)
          4.5.3.3在main中引入插件来进行指令注册

          4.5.3.4使用指令方法
    4.6nextTick是干什么的?
      4.6.1作用
      4.6.2使用场景
      4.6.1实现原理
    4.7vue性能优化
      4.7.1v-if和v-show区分使用场景
      4.7.2computed、watch、methods区分使用场景
      4.7.3提前处理好数据解决v-if和v-for必须同级的问题
      4.7.4v-for 遍历必须为 item 添加 key
      4.7.5图片大小优化和懒加载
      4.7.6路由懒加载
    4.8vue的虚拟dom你是怎么理解的?是否可以实现一个简版的?
      4.8.1虚拟dom理解
      4.7.2简版实现
      4.7.3实现原理
 
一、Vue双向绑定及原理

1.你对MVVM是怎么理解的?

  MVVM是Model-View-ViewModel缩写。Model层代表数据模型,View代表视图层,ViewModel是MVVM的核心,它是连接View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据,实现数据的双向绑定。可以不用再去低效又麻烦地通过操纵 DOM 去更新视图,专心处理和维护 ViewModel层。

2.你对Vue响应式原理是怎么理解的?是否可以实现一个简版的?Vue2中是如何监听数组的变化的?Vue3使用Proxy重写,相比Vue2的Object.defineProperty,有哪些优势?

    2.1响应式原理

  • Vue2的响应式是基于Object.defineProperty实现的
  • Vue3的响应式是基于ES6的Proxy来实现的

   2.2 Vue2 Object.defineProperty简版实现代码

// 响应式函数
function reactive(obj, key, value) {
Object.defineProperty(data, key, {
get() {
console.log(`访问了${key}属性`)
return value
},
set(val) {
console.log(`将${key}由->${value}->设置成->${val}`)
if (value !== val) {
value = val
}
}
})
} const data = {
name: '林三心',
age: 22
}
Object.keys(data).forEach(key => reactive(data, key, data[key]))
console.log(data.name)
// 访问了name属性
// 林三心
data.name = 'sunshine_lin' // 将name由->林三心->设置成->sunshine_lin
console.log(data.name)
// 访问了name属性
// sunshine_lin

  弊端:1.data新增了hobby属性,进行访问和设值,但是都不会触发get和set,所以弊端就是:Object.defineProperty只对初始对象里的属性有监听作用,而对新增的属性无效。这也是为什么Vue2中对象新增属性的修改需要使用Vue.$set来设值的原因;2.不能对数组数据进行劫持,无法监测到数组长度变化,需重写数组方法。

// 接着上面代码

data.hobby = '打篮球'
console.log(data.hobby) // 打篮球
data.hobby = '打游戏'
console.log(data.hobby) // 打游戏

   2.3 针对弊端解决 无法监听数组问题:Vue2中是如何监听数组的变化的?

import { def } from '../util/index'

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto) const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
] methodsToPatch.forEach(function (method) {
// 缓存原来的方法
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})

  看来Vue能对数组进行监听的原因是,把数组的方法重写了。总结起来就是这几步:

  01先获取原生 Array 的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化。

  02对 Array 的原型方法使用 Object.defineProperty 做一些拦截操作。

  03把需要被拦截的 Array 类型的数据原型指向改造后原型

   2.4 针对弊端解决 无法劫持对象的新增属性:this.$set能够解决对象新增属性问题。延伸问题:$set为啥能监测数组变动?

function set (target, key, val) {
//...
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val);
return val
}
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val
}
//...
defineReactive$$1(ob.value, key, val);
ob.dep.notify();
return val
}
  • 如果target是一个数组且索引有效,就设置length的属性。
  • 通过splice方法把value设置到target数组指定位置。
  • 设置的时候,vue会拦截到target发生变化,然后把新增的value也变成响应式
  • 最后返回value

  这就是vue重写数组方法的原因,利用数组这些方法触发使得每一项的value都是响应式的。

   2.5 Vue3 proxy简版实现代码(优势是解决了弊端)

  target:源对象   prop:要进行操作的属性   value:set操作时的最新值

  Reflect作用是反射对象,Reflect.get等同于对读取对象的属性obj.a;Reflect.set等同添加或修改对象属性的值obj.b=c;Reflect.deleProperty等同删除对象属性delete obj.d

  js是单线程遇到错误会直接挂机,Reflect兼容性更好,否则需要使用try catch捕获错误

     

3.你对Vue的v-model双向绑定是怎么理解的?是否可以实现一个简版的?

  3.1 v-model双向绑定理解

      v-model可以实现表单元素和数据之间的双向绑定,即修改表单的值,data中对应变量的值也会被修改。在data对应变量中修改值,被绑定的表单值也会被修改;所以称之为双向绑定。

  3.2 v-model双向绑定实现原理代码简版

  使用v-bind将文本框的value绑定给数据变量,实现变量改变,文本框值也随之改变的单向绑定;然后使用v-on绑定文本框的值改变事件,最后将事件对象中的文本框值传递给变量,即实现了双向绑定。

  不使用方法实现:

<div id="ab">
<!-- <input type="text" v-model="msg"> -->
<input type="text" name="" id="" v-bind:value="msg" v-on:input="msg = $event.target.value">
<h4>{{msg}}</h4>
</div>
<script>
const app = new Vue({
el:'#ab',
data:{
msg:'起飞'
}
})
</script>

  使用方法实现:

   <div id="ab">
<!-- <input type="text" v-model="msg"> -->
<input type="text" name="" id="" v-bind:value="msg" v-on:input="Value">
<h4>{{msg}}</h4>
</div>
<script>
const app = new Vue({
el:'#ab',
data:{
msg:'起飞'
},
methods:{
Value(event){
this.msg = event.target.value;
}
}
})
</script>

   3.3双向绑定原理

  Vue 主要通过以下 4 个步骤来实现数据双向绑定的:

  实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。

  实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。

  实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。

  实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。

二、生命周期

1.Vue有哪些生命周期?它们有哪些使用场景?你的接口请求一般都放在哪个生命周期方法中,为什么?你的获取dom的方法一般都放在哪个生命周期方法中,为什么?

 1.1 vue3中的选项式生命周期钩子及使用场景

  • beforeCreate: 在实例初始化之后、进行数据侦听和事件/侦听器的配置之前同步调用
  • created:在实例创建完成后被立即同步调用
  • beforeMount:在挂载开始之前被调用
  • mounted:在实例挂载完成后被调用
  • beforeUpdate:在数据发生改变后,DOM 被更新之前被调用
  • updated:在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用
  • beforeUnmount(在Vue2中是:beforeDestroy):在卸载组件实例之前调用
  • unmounted (在Vue2中是: destroyed):卸载组件实例后调用

 1.2 vue3中的组合式生命周期钩子(使用场景对比选项式生命周期钩子) 

  

    setup() 内部(组合式api)调用的生命周期钩子里是没有beforeCreateCreate函数的。

  官方解释: 因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。
 1.3异步请求一般在哪个生命周期,为什么?
  可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。
  • 对于作为子组件被调用的组件里,异步请求应当在mounted里调用,因为这个时候子组件可能需要涉及到对dom的操作;
  • 对于页面级组件,当我们需要使用ssr(服务端渲染)的时候,只有created是可用的,所以这个时候请求数据只能用它;能更快获取到服务端数据,减少页面 loading 时间;
  • 对于页面级组件, 当我们做异步操作时,涉及到要访问dom的操作,我们仍旧只能使用mounted;
  • 对于一般情况,createdmounted都是可以的;

 1.4获取DOM的方法放在哪个生命周期方法中?为什么?

  1. beforeCreated:生成$options选项,并给实例添加生命周期相关属性。在实例初始化之后,在 数据观测(data observer) 和event/watcher 事件配置之前被调用,也就是说,data,watcher,methods都不存在                   这个阶段。但是有一个对象存在,那就是$route,因此此阶段就可以根据路由信息进行重定向等操作。

  2. created:初始化与依赖注入相关的操作,会遍历传入methods的选项,初始化选项数据,从$options获取数据选项(vm.$options.data),给数据添加‘观察器’对象并创建观察器,定义getter、setter存储器属                性。在实例创建之后被调用,该阶段可以访问data,使用watcher、events、methods,也就是说 数据观测(data observer) 和event/watcher 事件配置 已完成。但是此时dom还没有被挂载。该阶段允许执行              http请求操作。

  3. beforeMount:将HTML解析生成AST节点,再根据AST节点动态生成渲染函数。相关render函数首次被调用(划重点)。

  4. mounted:在挂载完成之后被调用,执行render函数生成虚拟dom,创建真实dom替换虚拟dom,并挂载到实例。可以操作dom,比如事件监听

  5. beforeUpdate:$vm.data更新之后,虚拟dom重新渲染之前被调用。在这个钩子可以修改$vm.data,并不会触发附加的冲渲染过程。

  6. updated:虚拟dom重新渲染后调用,若再次修改$vm.data,会再次触发beforeUpdate、updated,进入死循环。

  7. beforeDestroy:实例被销毁前调用,也就是说在这个阶段还是可以调用实例的。

  8. destroyed:实例被销毁后调用,所有的事件监听器已被移除,子实例被销毁。

  总结来说,虚拟dom开始渲染是在beforeMount时,dom实例挂载完成在mounted阶段显示。

  在选项式生命周期中只有mounted时期挂载完成后才能够获取DOM,mounted时期前DOM未生成,mounted时期后调用进入死循环,同理在组合式生命周期只有在onMounted时期,setup时期等都不可以获取DOM。(或者可以在created时期使用$nextTick函数)

三、组件通信

1.Vue的子组件如何调用父组件的方法?

 1.1直接在子组件中通过this.$parent.event来调用父组件的方法

// 父组件
<template>
<div>
<child></child>
</div>
</template>
<script>
import child from '~/components/dam/child';
export default {
components: {
child
},
methods: {
fatherMethod() {
console.log('测试');
}
}
};
</script>
// 子组件
<template>
<div>
<button @click="childMethod()">点击</button>
</div>
</template>
<script>
export default {
methods: {
childMethod() {
this.$parent.fatherMethod();
}
}
};
</script>

  1.2在子组件里用$emit向父组件触发一个事件,父组件监听这个事件就行了

// 父组件
<template>
<div>
<child @fatherMethod="fatherMethod"></child>
</div>
</template>
<script>
import child from '~/components/dam/child';
export default {
components: {
child
},
methods: {
fatherMethod() {
console.log('测试');
}
}
};
</script>
// 子组件
<template>
<div>
<button @click="childMethod()">点击</button>
</div>
</template>
<script>
export default {
methods: {
childMethod() {
this.$emit('fatherMethod');
}
}
};
</script>

  1.3父组件把方法传入子组件中,在子组件里直接调用这个方法

// 父组件
<template>
<div>
<child :fatherMethod="fatherMethod"></child>
</div>
</template>
<script>
import child from '~/components/dam/child';
export default {
components: {
child
},
methods: {
fatherMethod() {
console.log('测试');
}
}
};
</script>
// 子组件
<template>
<div>
<button @click="childMethod()">点击</button>
</div>
</template>
<script>
export default {
props: {
fatherMethod: {
type: Function,
default: null
}
},
methods: {
childMethod() {
if (this.fatherMethod) {
this.fatherMethod();
}
}
}
};
</script>

2.Vue组件通信有哪些方式

  • 父子通信: 父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners
  • 兄弟通信: Bus;Vuex
  • 跨级通信: Bus;Vuex;provide / inject API、$attrs/$listeners
  • https://juejin.cn/post/6877101934600273934

四、vue官方api
1.Vue的data为啥是函数?(单页面应用不受限制)
  在vue中一个组件可能会被其他的组件引用(组件复用),为了防止多个组件实例对象之间共用一个data,产生数据污染。将data定义成一个函数,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响initData时会将其作为工厂函数都会返回全新data对象。
 

2. Vue列表中加的key是干什么的?

  vue中列表循环需加:key="唯一标识" 唯一标识可以是item里面id 等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM。

  key在diff算法中的用法演示:https://juejin.cn/post/7156507746555133965#heading-5

  延申问题:不推荐index作为key值    

  当以数组为下标的index作为key值时,其中一个元素(例如增删改查)发生了变化就有可能导致所有的元素的key值发生改变 diff算法时比较同级之间的不同,以key来进行关联,当对数组进行下标的变换时,比如删除第一条数据,那么以后所有的index都会发生改变,那么key自然也跟着全部发生改变,所以index作为key值是不稳定的,而这种不稳定性有可能导致性能的浪费,导致diff无法关联起上一次一样的数据。因此,能不使用index作为key就不使用index。
 
3.Vuex是什么,它的使用场景是什么
  vuex是一个专为vue.js开发的状态管理模式,实际上在真是开发中我们可能会在下面这种情况使用它:
  1. 登录的状态、以及用户的信息
  2. 购物车的信息,收藏的信息等
  3. 用户的地理位置
4.Vue-router的history模式和hash模式?
  4.1hash模式
    在浏览器中符号“#”,#以及#后面的字符称之为 hash, 用 window.location.hash 读取。特点:hash 虽然在 URL 中,但不被包括在 HTTP 请求中;用来指导浏览器动作,对服务端安全无用,hash 不会重加载页面。路由的哈希模式其实是利用了window.onhashchange事件,也就是说你的url中的哈希值(#后面的值)如果有变化,就会自动调用hashchange的监听事件,在hashchange的监听事件内可以得到改变后的url,这样能够找到对应页面进行加载。
  4.2hash模式特点
  • URL 中的 hash 值只是客户端的一种状态,向服务端发送请求的时候,hash 部分不会被发送。
  • hash 值得改变会在浏览器的历史记增加访问记录,所以可以通过浏览器的回退、前进控制 hash 值的改变。
  • 可以通过 a 标签设置 href 值或者通过 js 给location.hash 赋值来改变 hash 值。
  • 可以通过hashchang 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。
  4.3history模式
    history 采用 HTML5 的新特性;且提供了两个新方法: pushState(), replaceState()可以对浏览器历史记录栈进行修改,以 及 popState 事件的监听到状态变更pushState方法、replaceState方法,只能导致history对象发生变化,从而改变当前地址栏的 URL,但浏览器不会向后端发送请求,也不会触发popstate事件的执行。

    popstate事件的执行是在点击浏览器的前进后退按钮的时候,才会被触发(popstate控制浏览器历史记录的api)

    使用history模式需要更改router.js的mode为history,设置vue.config.js的publicPath:'/‘

  4.4history模式特点
  • 通过 pushState 和 replaceState 两个API 来操作实现 URL 的变化。
  • 可以通过 popstate 事假来监听 URL 的变化,从而对页面进行跳转(渲染)。
  • history.pushState() 或 history.replaceState() 不会触发 popstate 事件,需要手动触发页面跳转
  4.5history模式和hash模式的区别
    1.形式上:hash模式url里面永远带着#号,开发当中默认使用这个模式。如果用户考虑url的规范那么就需要使用history模式,因为history模式没有#号,是个正常的url,适合推广宣传;

    2.功能上:比如我们在开发app的时候有分享页面,那么这个分享出去的页面就是用vue或是react做的,咱们把这个页面分享到第三方的app里,有的app里面url是不允许带有#号的,所以要将#号去除那么就要使用history模式,但是使用history模式还有一个问题就是,在访问二级页面的时候,做刷新操作,会出现404错误,那么就需要和后端人配合,让他配置一下apache或是nginx的url重定向,重定向到你的首页路由上就ok了
  4.6延申:传统路由和前端路由的区别

    传统的路由指的是:当用户访问一个url时,对应的服务器会接收这个请求,然后解析url中的路径,从而执行对应的处理逻辑。这样就完成了一次路由分发

    前端路由是:不涉及服务器的,是前端利用hash或者HTML5的history API来实现的,一般用于不同内容的展示和切换

5.有没有写过Vue的指令,它的实现原理是什么?有没有写过Vue的插件,它的实现原理是什么?

  5.1指令api:生命周期和钩子函数参数

  • bind:指令第一次绑定到元素时调用,此钩子只会调用一次。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。
  • el:指令所绑定的元素,可以用来直接操作 DOM
  • binding:一个对象,包含以下属性:
  • name:指令名,不包括 v- 前缀。
  • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
  • oldValue:指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
  • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
  • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
  • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnodeVue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。

  5.2 指令和插件本质

    Vue 插件机制的原理:本质上插件就是一个对象,在对象里面调用install 方法 

    Vue指令的原理:本质上插件就是一个对象

  5.3 实现一键复制文本内容,用于鼠标右键粘贴的指令(配合插件执行全局注册)

     5.3.1定义指令对象代码(执行的代码不重要,重要在于编写格式)
import { Message } from 'ant-design-vue';

const vCopy = { // 名字爱取啥取啥
/*
bind 钩子函数,第一次绑定时调用,可以在这里做初始化设置
el: 作用的 dom 对象
value: 传给指令的值,也就是我们要 copy 的值
*/
bind(el, { value }) {
el.$value = value; // 用一个全局属性来存传进来的值,因为这个值在别的钩子函数里还会用到
el.handler = () => {
if (!el.$value) {
// 值为空的时候,给出提示,我这里的提示是用的 ant-design-vue 的提示,你们随意
Message.warning('无复制内容');
return;
}
// 动态创建 textarea 标签
const textarea = document.createElement('textarea');
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
textarea.readOnly = 'readonly';
textarea.style.position = 'absolute';
textarea.style.left = '-9999px';
// 将要 copy 的值赋给 textarea 标签的 value 属性
textarea.value = el.$value;
// 将 textarea 插入到 body 中
document.body.appendChild(textarea);
// 选中值并复制
textarea.select();
// textarea.setSelectionRange(0, textarea.value.length);
const result = document.execCommand('Copy');
if (result) {
Message.success('复制成功');
}
document.body.removeChild(textarea);
};
// 绑定点击事件,就是所谓的一键 copy 啦
el.addEventListener('click', el.handler);
},
// 当传进来的值更新的时候触发
componentUpdated(el, { value }) {
el.$value = value;
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('click', el.handler);
},
}; export default vCopy;

    5.3.2定义一个能够注册指令的插件(不使用插件注册的方法是直接在main调用directive注册就可以)

import copy from './v-copy';
// 自定义指令
const directives = {
copy,
};
// 这种写法可以批量注册指令
export default {
install(Vue) { //install函数是插件的语法
Object.keys(directives).forEach((key) => {
Vue.directive(key, directives[key]); //注册指令对象
});
},
};

    5.3.3在main中引入插件来进行指令注册

import Vue from 'vue';
import Directives from './directives'; Vue.use(Directives); //插件使用方法use

    5.3.4使用指令方法

<template>
<button v-copy="copyText">copy</button>
</template> <script>
export default {
data() {
return {
copyText: '要 Copy 的内容',
};
},
};
</script>

6.nxetTick是干什么用的

   6.1作用
    nextTick 接收一个回调函数作为参数,并将这个回调函数延迟到DOM更新后才执行;
  6.2使用场景
    想要操作 基于最新数据生成的DOM 时,就将这个操作放在 nextTick 的回调中
  6.3实现原理

    将传入的回调函数包装成异步任务,异步任务又分微任务和宏任务,为了尽快执行所以优先选择微任务,把微任务放在DOM 更新后执行;

    Vue2根据环境是否支持,选择的微任务优先级为:Promise---> MutationObserver---> setImmediate---> setTimeout

    Vue3直接选用Promise,添加几个维护队列queueJob -> queueFlush -> flushJobs -> nextTick参数的 fn,详情看 https://juejin.cn/post/7021688091513454622#heading-9

7.vue性能优化
  7.1v-if和v-show区分使用场景
  7.2computed、watch、methods区分使用场景
  7.3提前处理好数据解决v-if和v-for必须同级的问题
    避免v-if和v-for同时使用,v-forv-if具有更高的优先级,意味着v-if 将分别重复运行于每个v-for循环中。
    解决途径:可以在computed中提前把要v-for的数据中v-if的数据项给过滤处理了。
  7.4v-for 遍历必须为 item 添加 key
    在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。
  7.5图片大小优化和懒加载
    关于图片大小的优化,可以用image-webpack-loader进行压缩图片。
    关于图片懒加载,可以用vue-lazyload插件实现。
  7.6路由懒加载

     Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。

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

8.vue的虚拟dom你是怎么理解的?是否可以实现一个简版的?

  8.1虚拟dom理解
    Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实的DOM上
  8.2简版实现
var element = {
tagName: 'ul', // 节点标签名
props: { // DOM的属性,用一个对象存储键值对
id: 'list'
},
children: [ // 该节点的子节点
{tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
{tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
{tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
]
}

  8.3实现原理

  • 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
  • diff 算法 — 比较两棵虚拟 DOM 树的差异;
  • pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。

前端面试-经典的Vue面试题的更多相关文章

  1. JQuery选择器大全 前端面试送命题:面试题篇 对IOC和DI的通俗理解 c#中关于协变性和逆变性(又叫抗变)帮助理解

    JQuery选择器大全   jQuery 的选择器可谓之强大无比,这里简单地总结一下常用的元素查找方法 $("#myELement")    选择id值等于myElement的元素 ...

  2. 阿里巴巴前端面试parseInt()函数的面试题

    JavaScript 是弱类型语言,为了保证数值的有效性,在处理数值的时候,我们可以对数值字符串进行强行转换.如 parseInt 取整和 parseFloat 取浮点数.Java 也有 Intege ...

  3. 前端面试经典题目合集(HTML+CSS)一

    1.说说你对HTML语义化的理解? (1)什么是HTML语义化? 根据内容的结构化(内容语义化),选择合适的标签(代码语义化)便于开发者阅读和写出更优雅的代码的同时让浏览器的爬虫和机器很好地解析. ( ...

  4. 前端面试经典题目(HTML+CSS)二

    1.浏览器页面由哪三层构成,分别是什么,作用是什么? 构成:结构层.表示层.行为层 分别是:HTML.CSS.JavaScript 作用:HTML实现页面结构,CSS完成页面的表现与风格,JavaSc ...

  5. 前端面试经典题之ES6新特性

    ES6 主要是为了解决 ES5 的先天不足,在原先ES5的基础上新增了许多内容,本篇文章将列举出ES6中新增的10大特性. 一. let 和 const 与var不同,let和const都是用于命名局 ...

  6. 前端面试经典题之apply与call的比较

    在讲apply和call之前,我们需要先清楚在js中,this指向的是什么. 大家可以参考一下阮一峰老师写的关于JavaScript中this的原理讲解文章:http://www.ruanyifeng ...

  7. 2019前端面试系列——Vue面试题

    Vue 双向绑定原理        mvvm 双向绑定,采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter.getter,在数 ...

  8. 【前端面试】Vue面试题总结(持续更新中)

    Vue面试题总结(持续更新中) 题目参考链接 https://blog.csdn.net/weixin_45257157/article/details/106215158 由于已经有很多前辈深造VU ...

  9. web前端面试试题总结---css篇

    CSS 介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的? (1)有两种, IE 盒子模型.W3C 盒子模型: (2)盒模型: 内容(content).填充(padding).边界(m ...

  10. 2019前端面试系列——JS面试题

    判断 js 类型的方式 1. typeof 可以判断出'string','number','boolean','undefined','symbol' 但判断 typeof(null) 时值为 'ob ...

随机推荐

  1. ES6的Set详解

    数组去重 let arr = [ 1,2,3,4,5,3,2 ] // 数组去重 // 方法一 let newArr = [new Set(arr)] console.log(newArr); // ...

  2. 微信小程序wxs封装使用以及公共js组件封装

    wxs封装 wxs可以直接写在wxml页面中,并且在对应的位置调用,比如在{{ xxx.xxx() }}调用wxs的函数 <view> <view>第{{m1.getMax(1 ...

  3. JZOJ 5343. 【NOIP2017模拟9.3A组】健美猫

    题面 其中 \(1 \leq n \leq 2 \times 10^6\) 分析 考虑每次移动,发现负数对答案贡献少 \(1\),非负数多 \(1\) 每次移动都加了 \(1\) 负数变非负数关键点在 ...

  4. 血药谷浓度能否区分经TNF拮抗剂诱导获得缓解和低活动度的RA患者

    血药谷浓度能否区分经TNF拮抗剂诱导获得缓解和低活动度的RA患者? Sanmarti R, et al. EULAR 2015. Present ID: FRI0133. 原文 译文 FRI0133 ...

  5. gin 01

    1.gin介绍: 2.gin的安装 go get -u github.com/gin-gonic/gin 3.gin的第一个helloWord package main import ( " ...

  6. 3.基于Label studio的训练数据标注指南:文本分类任务

    文本分类任务Label Studio使用指南 1.基于Label studio的训练数据标注指南:信息抽取(实体关系抽取).文本分类等 2.基于Label studio的训练数据标注指南:(智能文档) ...

  7. 浅析容器运行时奥秘——OCI标准

    背景 2013年Docker开源了容器镜像格式和运行时以后,为我们提供了一种更为轻量.灵活的"计算.网络.存储"资源虚拟化和管理的解决方案,在业界迅速火了起来. 2014年更是容器 ...

  8. Qt中的多窗体编程

    在某些应用中,会用到多窗体功能,这里就来讨论一下Qt下多窗体功能的实现.本例仍以qt4.8.7版本为例,并基于QtCreator4.6.2环境进行开发.在本例中,以一个能显示实时时钟的第二窗体为例进行 ...

  9. USACO2023Feb游记

    由于学校要求,过来打 USACO. 由于上次已经打到白金了,所以继续. 然后还是 AK 了. 感觉题意很迷惑,所以都翻译一下. Hungry Cow Bessie 很饿,每天晚饭如果有干草就会吃 \( ...

  10. EveryCircuit_v2.15汉化破解版apk下载

    安卓手机扫码下载  大小 6.44M EveryCircuit(电子电路模拟器)是一个专为电子信息技术专业的人士所打造的软件,它能够让你轻松的了解到电子电路究竟是如何进行工作的. 下载地址:https ...