Vue.js面试题(三)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Vue.js面试题(三)

Vue.js面试题(二)https://developer.aliyun.com/article/1399412?spm=a2c6h.13148508.setting.14.59904f0eEJICdO


41.★★★ Vue深层次嵌套传值方法

利用attrs和attrs 和 listeners


42.★★★ Vue组件如何引入使用

  1. 定义组件并抛出
  2. import引入,并在component里面定义
  3. 使用组件(注意首字母大写)


43.★★★★ Vue路由实现的底层原理

在Vue中利用数据劫持defineProperty在原型prototype上初始化了一些getter,

分别是router代表当前Router的实例 、 router代表当前Router的实例、router代表当前Router的实例、route 代表当前Router的信息。在install中也全局注册了router-view,router-link,其中的Vue.util.defineReactive, 这是Vue里面观察者劫持数据的方法,

劫持_route,当_route触发setter方法的时候,则会通知到依赖的组件。

接下来在init中,会挂载判断是路由的模式,是history或者是hash,点击行为按钮,

调用hashchange或者popstate的同时更_route,_route的更新会触发route-view的重新渲染。


44.★★★★ 如何封装一个通用组件

通用组件的封装就是对可复用组件的解耦和样式复用,为了解耦一般数据都是通过父组件传递过来,

在子组件中进行数据处理,对于一些较为复杂的数据可能还需要做数据验证,

为了避免高耦合,逻辑最好放在父组件中,通过自定义事件将数据回传,子组件只是一个承载体,

这样既降低耦合,保证子组件中数据和逻辑不会混乱。

如果同一组件需要适应不同需求时,我们需要配合slot来使用,

可以通过具名插槽灵活地解决了不同场景同一组件不同配置的问题。


45.★★ Vue 生命周期通常使用哪些

常用的生命周期有,beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed


46.★★ Vue 深层次的组件怎么和父组件通讯

//使用$attrs和$listeners
Vue.component('C', {
  template: `
    <div>
      <p>我是C组件</p>
      <input 
        type='text' 
        v-model='$attrs.msgc' 
        @input='$emit("getC", $attrs.msgc)' 
      />
    </div>
    `
})
Vue.component('B', {
  /**
    给C组件绑定$attrs属性和$listeners事件,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的)
  */
  template: `
    <div>
      <p>我是B组件</p>
      <input 
        type='text' 
        v-model='mymsg1' 
        @input="$emit('getChild', mymsg1)"
      />
      <C v-bind='$attrs' v-on='$listeners'/>
    </div>
  `,
  props: ['msg1'],
  data () {
    return {
      mymsg1: this.msg1
    }
  }
})
Vue.component('A', {
  template: `
    <div id='app'>
      <p>我是A组件</p>
      <B 
        :msg1='msg1' 
        :msgc='msgc' 
        @getChild='getChild'
        @getC='getC'
      />
    </div>
  `,
  data () {
    return {
      msg1: 'A',
      msgc: 'hello c!'
    }
  },
  methods: {
    getChild (val) {
      console.log( val )
    },
    getC (val) {
      console.log( val )
    }
  }
})
const app = new Vue({
  el: '#app',
  template: `
      <A />  
  `
})


47.★★★★★ Vue 响应式原理

1.观察者observer:首先通过观察者对data中的属性使用object.defineproperty劫持数据的getter和setter,通知订阅者,触发他的update方法,对视图进行更新


2.Compile:用来解析模板指令,并替换模板数据,初始化视图,初始化相应的订阅器


3.订阅者Watcher:订阅者接到通知后,调用update方法更新对应的视图


4.订阅器Dep:订阅者可能有多个,因此需要订阅器Dep来专门接收这些订阅者,并统一管理


但在vue3中抛弃了object.defineproperty方法,因为


  • Object.defineproperty无法监测对象属性的添加和删除、数组索引和长度的变更,因此vue重写了数组的push/pop/shift/unshift/splice/sort/reverse方法
  • Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,这样很消耗性能


vue3中实现数据双向绑定的原理是数据代理,使用proxy实现。Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。


48.★★★★ Vue proxy的原理

主要通过Proxy对对象进行绑定监听处理,通过new Map对对象的属性操作进行处理,将要执行的函数匹配到存到对应的prop上面,通过每次的访问触发get方法,进行存方法的操作,通过修改触发set的方法,此时执行回调监听的函数,这样达到修改数据和视图的


49. ★★★ Vue $forceUpdate的原理

作用

迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。


内部原理


Vue.prototype.$forceUpdate = function () {
    const vm: Component = this
    if (vm._watcher) {
        vm._watcher.update()
    }
}


50.★★★ v-for key

  • key是为Vue中的vnode标记的唯一id,通过这个key,我们的diff操作可以更准确、更快速
  • diff算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的key与旧节点进行比对,然后超出差异.


diff程可以概括为:oldCh和newCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和newCh至少有一个已经遍历完了,就会结束比较,这四种比较方式就是首、尾、旧尾新头、旧头新尾.

准确: 如果不加key,那么vue会选择复用节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug. 快速: key的唯一性可以被Map数据结构充分利用,相比于遍历查找的时间复杂度O(n),Map的时间复杂度仅仅为O(1)


51.★★★★ defineProperty在数据劫持后是如何通知数据的更新和视图的更新的

vue的双向绑定是由数据劫持结合发布者-订阅者模式实现的,那么什么是数据劫持?vue是如何进行数据劫持的?说白了就是通过Object.defineProperty()来劫持对象属性的setter和getter操作,在数据变动时做你想要做的事情


我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发生变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图


因此接下去我们执行以下3个步骤,实现数据的双向绑定:

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

2.实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在v-model,v-on等指令,则解析器Compile初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)。


52.★★★★ axios谁封装的,怎么封装的

// 使用axios用于对数据的请求
import axios from 'axios'
// 创建axios实例
const instance = axios.create({
  baseURL: baseURL + version,
  timeout: 5000
})
// 创建请求的拦截器
instance.interceptors.request.use(config => {
  config.headers['Authorization'] = localStorage.getItem('token')
  return config
}, error => {
  return Promise.reject(error)
})
// 创建响应的拦截器
instance.interceptors.response.use(response => {
  let res = null
  // 对相应的数据进行过滤
  if (response.status === 200) {
    if (response.data && response.data.err === 0) {
      res = response.data.data
    } else if (response.data.err === -1) {
      return alert('token无效')
    }
  } else {
    return alert('请求失败')
  }
  return res
}, error => {
  return Promise.reject(error)
})
export default instance


53.★★★ 为什么要设置key值,可以用index吗?为什么不能?

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


54.★★★ 怎么修改Vuex中的状态?Vuex中有哪些方法?

  • 通过this.$store.state.属性 的方法来访问状态
  • 通过this.$store.commit(‘mutation中的方法’) 来修改状态


55.★★★ vue-router路由传参的方式

query
// 方法一
<template>
    <router-link
          :to="{
                  path: 'blogDetail',
                  query: { id: item.id, views: item.views }
                }"
          tag="h2"
        >
    </router-link>
</template>
// 方法二
this.$router.push({  
  path: 'blogDetail', 
  query: { 
    id: item.id,
    views: item.views
  }
})


params
<template>
    <router-link
          :to="{
                  name: 'blogDetail',
                  params: { id: item.id, views: item.views }
                }"
          tag="h2"
        >
    </router-link>
</template>
this.$router.push({  
  name: 'blogDetail', 
  params: { 
    id: item.id,
    views: item.views
  }
})


56.★★★ hash history区别

b71e020123d011e143231a6005a9adb.png


57.★★★ 用过beforeEach吗?

每次通过vue-router进行页面跳转,都会触发beforeEach这个钩子函数,这个回调函数共有三个参数,to,from,next这三个参数,to表示我要跳转的目标路由对应的参数,from表示来自那个路由,就是操作路由跳转之前的,即将离开的路由对应的参数,next是一个回调函数,一定要调用next方法来resolve这个钩子函数;


58.★★★ Vue中的单项数据流

  • 单向数据流指只能从一个方向来修改状态。
  • 数据从父级组件传递给子组件,只能单向绑定。
  • 子组件内部不能直接修改从父级传递过来的数据。


59.★★★ Vue组件中的Data为什么是函数,根组件却是对象呢?

如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。

所以说vue组件的data必须是函数。这都是因为js的特性带来的,跟vue本身设计无关。


60.★★★ 你做过哪些Vue的性能优化?

1、首屏加载优化


2、路由懒加载


{      
  path: '/',      
  name: 'home',      
  component: () => import('./views/home/index.vue'),      
  meta: { isShowHead: true }
}


3、开启服务器 Gzip

开启 Gzip 就是一种压缩技术,需要前端提供压缩包,然后在服务器开启压缩,文件在服务器压缩后传给浏览器,浏览器解压后进行再进行解析。首先安装 webpack 提供的compression-webpack-plugin进行压缩,然后在 vue.config.js:


const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['js', 'css']......plugins: [      
  new CompressionWebpackPlugin(
    {        
      algorithm: 'gzip',        
      test:     new RegExp('\\.(' + productionGzipExtensions.join('|') +                 ')$'),            
      threshold: 10240,        
      minRatio: 0.8      
       }
)]....


4、启动 CDN 加速

我们继续采用 cdn 的方式来引入一些第三方资源,就可以缓解我们服务器的压力,原理是将我们的压力分给其他服务器点。


5、代码层面优化


  • computed 和 watch 区分使用场景


computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;

watch:类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。


  • v-if 和 v-show 区分使用场景 v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。这里要说的优化点在于减少页面中 dom 总数,我比较倾向于使用 v-if,因为减少了 dom 数量。
  • v-for 遍历必须为 item 添加 key,且避免同时使用 v-if v-for 遍历必须为 item 添加 key,循环调用子组件时添加 key,key 可以唯一标识一个循环个体,可以使用例如 item.id 作为 key 避免同时使用 v-if,v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度。

6、Webpack 对图片进行压缩


7、避免内存泄漏


8、减少 ES6 转为 ES5 的冗余代码


Vue.js面试题(四)https://developer.aliyun.com/article/1399447

相关文章
|
2月前
|
JSON JavaScript 前端开发
Javascript基础 86个面试题汇总 (附答案)
该文章汇总了JavaScript的基础面试题及其答案,涵盖了JavaScript的核心概念、特性以及常见的面试问题。
51 3
|
2月前
|
前端开发 JavaScript
JavaScript 面试系列:如何理解 ES6 中 Generator ?常用使用场景有哪些?
JavaScript 面试系列:如何理解 ES6 中 Generator ?常用使用场景有哪些?
|
3月前
|
JavaScript 前端开发 应用服务中间件
【Vue面试题三十】、vue项目本地开发完成后部署到服务器后报404是什么原因呢?
这篇文章分析了Vue项目在服务器部署后出现404错误的原因,主要是由于history路由模式下服务器缺少对单页应用的支持,并提供了通过修改nginx配置使用`try_files`指令重定向所有请求到`index.html`的解决方案。
【Vue面试题三十】、vue项目本地开发完成后部署到服务器后报404是什么原因呢?
|
3月前
|
JavaScript 前端开发
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
这篇文章主要讨论了axios的使用、原理以及源码分析。 文章中首先回顾了axios的基本用法,包括发送请求、请求拦截器和响应拦截器的使用,以及如何取消请求。接着,作者实现了一个简易版的axios,包括构造函数、请求方法、拦截器的实现等。最后,文章对axios的源码进行了分析,包括目录结构、核心文件axios.js的内容,以及axios实例化过程中的配置合并、拦截器的使用等。
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
|
3月前
|
JavaScript 前端开发 数据处理
【Vue面试题二十八】、vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?
这篇文章讨论了Vue中实现权限管理的策略,包括接口权限、路由权限、菜单权限和按钮权限的控制方法,并提供了不同的实现方案及代码示例,以确保用户只能访问被授权的资源。
【Vue面试题二十八】、vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?
|
15天前
|
JSON JavaScript 前端开发
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
本文介绍了JSONP的工作原理及其在解决跨域请求中的应用。首先解释了同源策略的概念,然后通过多个示例详细阐述了JSONP如何通过动态解释服务端返回的JavaScript脚本来实现跨域数据交互。文章还探讨了使用jQuery的`$.ajax`方法封装JSONP请求的方式,并提供了具体的代码示例。最后,通过一个更复杂的示例展示了如何处理JSON格式的响应数据。
27 2
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
|
3月前
|
JavaScript 前端开发
【Vue面试题二十七】、你了解axios的原理吗?有看过它的源码吗?
文章讨论了Vue项目目录结构的设计原则和实践,强调了项目结构清晰的重要性,提出了包括语义一致性、单一入口/出口、就近原则、公共文件的绝对路径引用等原则,并展示了单页面和多页面Vue项目的目录结构示例。
|
2月前
|
缓存 JavaScript 前端开发
vue面试题
vue面试题
166 64
|
1月前
|
JavaScript 前端开发
vue尚品汇商城项目-day01【8.路由跳转与传参相关面试题】
vue尚品汇商城项目-day01【8.路由跳转与传参相关面试题】
40 0
vue尚品汇商城项目-day01【8.路由跳转与传参相关面试题】
|
1月前
|
Web App开发 JavaScript 前端开发
前端Node.js面试题
前端Node.js面试题