vue面试相关问题

目录

1.深入浅出Vue响应式原理

2.v-if、v-show区别
3.vue-router 有哪些模式? 区别是什么? 具体是怎么实现的

  1. vue-router导航守卫

  2. vue组件通信

  3. diff算法

  4. Vue3 proxy

  5. vue.$set

  6. Vue是怎么重写数组的

  7. Vue.nextTick()

  8. Key的作用

  9. slot插槽

  10. 为什么vue data属性必须是一个函数

14.diff算法

  1. vue-router编程式导航

  2. vue中输入框事件的使用——@input、@keyup.enter、@change、@blur

  3. 自定义指令(v-check、 v-focus)的方法有哪些?它有哪些钩子函数?

  4. 自定义组件的v-model

  5. vue-router router-link组件的active-class属性

  6. vue- router嵌套路由

  7. Vue中computed和method之间有什么不同点

  8. 要发送一个异步请求,应该放在哪个生命周期

  9. vue和原生js的优点分析

  10. 如果要新增一个属性怎么实现响应式

  11. 虚拟 DOM 有什么优点和缺、点

  12. vue项目实现路由按需加载

  13. 为什么 vue 不建议在 action 中修改 state 而是在 mutation 中修改

  14. .vue是怎么渲染到页面上的(源码分析)

  15. vue项目中实现图片懒加载

  16. vue3的新特性有了解过吗

  17. vue项目优化

1.深入浅出Vue响应式原理

2.v-if、v-show区别

3.vue-router 有哪些模式? 区别是什么? 具体是怎么实现的

浅谈vue-router原理
https://www.cnblogs.com/goloving/p/9147551.html

https://blog.csdn.net/wang1006008051/article/details/81805932

history和hash的区别
  1. hash路由在地址栏URL上有#,用 window.location.hash 读取。而history路由没有会好看一点
  2. 我们进行回车刷新操作,hash路由会加载到地址栏对应的页面,而history路由一般就404报错了(刷新是网络请求,没有后端准备时会报错)。
  3. hash路由支持低版本的浏览器,而history路由是HTML5新增的API。
    4.hash的特点在于它虽然出现在了URL中,但是不包括在http请求中,所以对于后端是没有一点影响的,所以改变hash不会重新加载页面,所以这也是单页面应用的必备。
    5.history运用了浏览器的历史记录栈,之前有back,forward,go方法,之后在HTML5中新增了pushState()和replaceState()方法,它们提供了对历史记录进行修改的功能,不过在进行修改时,虽然改变了当前的URL,但是浏览器不会马上向后端发送请求。
    6.history的这种模式需要后台配置支持。比如:当我们进行项目的主页的时候,一切正常,可以访问,但是当我们刷新页面或者直接访问路径的时候就会返回404,那是因为在history模式下,只是动态的通过js操作window.history来改变浏览器地址栏里的路径,并没有发起http请求,但是当我直接在浏览器里输入这个地址的时候,就一定要对服务器发起http请求,但是这个目标在服务器上又不存在,所以会返回404
总结

当然啦,history 也不是样样都好。SPA 虽然在浏览器里游刃有余,但真要通过 URL 向后端发起 HTTP 请求时,两者的差异就来了。尤其在用户手动输入 URL 后回车,或者刷新(重启)浏览器的时候。

  1. hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.abc.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
  2. history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
  3. 结合自身例子,对于一般的 Vue + Vue-Router + Webpack + XXX 形式的 Web 开发场景,用 history 模式即可,只需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。

4. vue-router导航守卫

https://zhuanlan.zhihu.com/p/54112006
应用场景
https://www.cnblogs.com/baifangzi/p/14283463.html

5. vue组件通信

6. diff算法

7. Vue3 proxy

8. vue.$set

原理
https://www.jb51.net/article/146580.htm
源码

export function set (target: Array | Object, key: any, val: any): any {
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }  // 判断目标值是否为数组,并且key值是否为有效的数组索引
  if (Array.isArray(target) && isValidArrayIndex(key)) {  // 对比数组的key值和数组长度,取较大值设置为数组的长度
    target.length = Math.max(target.length, key)  // 替换目标值
    target.splice(key, 1, val)
    return val
  }  // 如果目标值是对象,并且key值是目标值存在的有效key值,并且不是原型上的key值
  if (key in target && !(key in Object.prototype)) {  // 直接更改目标值
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__ // 判断目标值是否为响应式的
  if (target._isVue || (ob && ob.vmCount)) { // 如果是vue根实例,就警告
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  if (!ob) { // 如果目标值不是响应式的,那么值需要给对应的key赋值
    target[key] = val
    return val
  }  // 其他情况,目标值是响应式的,就通过Object.defineProperty进行数据监听
  defineReactive(ob.value, key, val)  // 通知更新dom操作
  ob.dep.notify()
  return val
}

大概流程

  1. 判断目标值是否为有效值,不是有效值直接停止

  2. 判断是否为数组,并且key值是否为有效的key值
    如果是数组,就选择数组的长度和key值取较大值作为数组的新的length值,并且替换目标值splice方法,重写了,所以执行splice,会双向数据绑定

  3. 判断目标值是否为响应式的ob
    如果是vue实例,直接不行
    如果不是响应式的数据,就是普通的修改对象操作
    如果是响应式数据,那就通过Object.defineProperty进行数据劫持

  4. 通知dom更新

9. Vue是怎么重写数组的

// 缓存数组原型
const arrayProto = Array.prototype;
// 实现 arrayMethods.__proto__ === Array.prototype
export const arrayMethods = Object.create(arrayProto);
// 需要进行功能拓展的方法
const methodsToPatch = [
  "push",
  "pop",
  "shift",
  "unshift",
  "splice",
  "sort",
  "reverse"
];

/**
 * Intercept mutating methods and emit events
 */
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) {
    // push、unshift会新增索引,所以要手动observer
      case "push":
      case "unshift":
        inserted = args;
        break;
      // splice方法,如果传入了第三个参数,也会有索引加入,也要手动observer。
      case "splice":
        inserted = args.slice(2);
        break;
    }
    // 
    if (inserted) ob.observeArray(inserted);// 获取插入的值,并设置响应式监听
    // notify change
    ob.dep.notify();// 通知依赖更新
    // 返回原生数组方法的执行结果
    return result;
  });
});

};

10. Vue.nextTick()

实现原理

/* @flow */
/* globals MutationObserver */

import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'

export let isUsingMicroTask = false

const callbacks = []
let pending = false

/**
 * 对所有callback进行遍历,然后指向响应的回调函数
 * 使用 callbacks保证了可以在同一个tick内执行多次 nextTick,不会开启多个异步任务,而把这些异步任务都压成一个同步任务,在下一个 tick 执行完毕。
*/

function flushCallbacks () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]( "i")
  }
}


let timerFunc
/*
* timerFunc 实现的就是根据当前环境判断使用哪种方式实现
* 就是按照 Promise.then和 MutationObserver以及setImmediate的优先级来判断,支持哪个就用哪个,如果执行环境不支持,会采用setTimeout(fn, 0)代替;
*/

// 判断是否支持原生 Promise
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
  // 不支持 Promise的话,再判断是否原生支持 MutationObserver
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  // 新建一个 textNode的DOM对象,使用 MutationObserver 绑定该DOM并传入回调函数,在DOM发生变化的时候会触发回调,该回调会进入主线程(比任务队列优先执行)
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    // 此时便会触发回调
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
  // 不支持的 MutationObserver 的话,再去判断是否原生支持 setImmediate
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  // Promise,MutationObserver, setImmediate 都不支持的话,最后使用 setTimeout(fun, 0)
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

// 该函数的作用就是延迟 cb 到当前调用栈执行完成之后执行
export function nextTick (cb?: Function, ctx?: Object) {
  // 传入的回调函数会在callbacks中存起来
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  // pending是一个状态标记,保证timerFunc在下一个tick之前只执行一次
  if (!pending) {
    pending = true
    /**
    * timerFunc 实现的就是根据当前环境判断使用哪种方式实现
    * 就是按照 Promise.then和 MutationObserver以及setImmediate的优先级来判断,支持哪个就用哪个,如果执行环境不支持,会采用setTimeout(fn, 0)代替;
    */
    timerFunc()
  }
  // 当nextTick不传参数的时候,提供一个Promise化的调用
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}
  1. nextTick接受一个回调函数时(当不传参数的时候,提供一个Promise化的调用),传入的回调函数会在callbacks中存起来,根据一个状态标记 pending 来判断当前是否要执行 timerFunc();
  2. timerFunc() 是根据当前环境判断使用哪种方式实现,按照 Promise.then和 MutationObserver以及setImmediate的优先级来判断,支持哪个就用哪个,如果执行环境不支持,会采用setTimeout(fn, 0)代替;
  3. timerFunc()函数中会执行 flushCallbacks函数,flushCallbacks函数的作用就是对所有callback进行遍历,然后指向响应的回调函数

总体来说:就是先判断是否支持promise,如果支持promise。就通过Promise.resolve的方法,异步执行方法,如果不支持promise,就判断是否支持MutationObserver。如果支持,就通过MutationObserver(微异步)来异步执行方法,如果MutationObserver还不支持,就通过setTimeout来异步执行方法。

MutaionObserver通过创建新的节点,调用timerFunc方法,改变MutationObserver监听的节点变化,从而触发异步方法执行。

11. Key的作用

12. slot插槽

13. 为什么vue data属性必须是一个函数

14.diff算法

视频

15. vue-router编程式导航

官方文档

16. vue中输入框事件的使用——@input、@keyup.enter、@change、@blur

17. 自定义指令(v-check、 v-focus)的方法有哪些?它有哪些钩子函数?

vue2.0
vue3.0
2.0与3.0主要区别为3.0的钩子函数更全
2.0

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

  • unbind:只调用一次,指令与元素解绑时调用。

3.0

  • created:在绑定元素的 attribute 或事件监听器被应用之前调用。在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时,这很有用。

  • beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用。

  • mounted:在绑定元素的父组件被挂载后调用。

  • beforeUpdate:在更新包含组件的 VNode 之前调用。

  • updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用。

  • beforeUnmount:在卸载绑定元素的父组件之前调用

  • unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次。

18. 自定义组件的v-model

19. vue-router router-link组件的active-class属性

20. vue- router嵌套路由

21. Vue中computed和method之间有什么不同点

VUE中methods watch和compute的区别和联系

22. 要发送一个异步请求,应该放在哪个生命周期

23. vue和原生js的优点分析

24. 如果要新增一个属性怎么实现响应式

this.$set(this.data,”key”,value’)

Vue.set(vm.items,2,"ling") : 表示 把vm.items 这个数组的下标为2 的元素,改为"ling"

Vue.set(vm.person,"age","26")

Vue.set()向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如 this.myObject.newProperty = 'hi')

区别在于Vue.set()是将set函数绑定在Vue构造函数上,this.$set()是将set函数绑定在Vue原型上。

Vue.set数组实现的原理:其实Vue.set()对于数组的处理其实就是调用了splice方法

25. 虚拟 DOM 有什么优点和缺、点

26. vue项目实现路由按需加载

27. 为什么 vue 不建议在 action 中修改 state 而是在 mutation 中修改

28. .vue是怎么渲染到页面上的(源码分析)

29. vue项目中实现图片懒加载

30. vue3的新特性有了解过吗

31. vue项目优化

32. 为什么Vue3.0使用Proxy实现数据监听

33.

你可能感兴趣的:(vue面试相关问题)