前端常见面试题(三)@郝晨光


1. keep-alive组件的作用

  1. 为什么使用keep-alive?
    在Vue中,我们使用component内置组件或者vue-router切换视图的时候,由于Vue会主动卸载不使用的组件,所以我们不能保存组件之前的状态,而我们经常能遇到需要保存之前状态的需求,例如搜索页(保存搜索记录),列表页(保存之前的浏览记录)等等

  2. keep-alive的作用?
    keep-alive是一个Vue的内置组件,它能将不活动的组件保存下来,而不是直接销毁,当我们再次访问这个组件的时候,会先从keep-alive中存储的组件中寻找,如果有缓存的话,直接渲染之前缓存的,如果没有的话,再加载对应的组件。
    作为抽象组件,keep-alive是不会直接渲染在DOM中的

  3. keep-alive的属性?
    keep-alive提供了三个可选属性

    1. include - 字符串或数组或正则表达式。只有名称匹配的组件会被缓存。

       // **字符串**只缓存a组件
       <keep-alive include="a">
             <component :is="componentName"></component>
       </keep-alive>
       // **字符串**只缓存a组件和b组件
       <keep-alive include="a,b">
             <component :is="componentName"></component>
       </keep-alive>
       // **正则表达式,需要使用v-bind **只缓存a组件和b组件
       <keep-alive :include="/a|b/">
             <component :is="componentName"></component>
       </keep-alive>
       // **数组,需要使用v-bind **只缓存a组件和b组件
       <keep-alive :include="[a,b]">
             <component :is="componentName"></component>
       </keep-alive>
      
    2. exclude - 字符串或数组或正则表达式。名称匹配的组件不会被缓存。

       // **字符串**不缓存a组件
       <keep-alive exclude="a">
             <component :is="componentName"></component>
       </keep-alive>
       // **字符串**不缓存a组件和b组件
       <keep-alive exclude="a,b">
             <component :is="componentName"></component>
       </keep-alive>
       // **正则表达式,需要使用v-bind **不缓存a组件和b组件
       <keep-alive :exclude="/a|b/">
             <component :is="componentName"></component>
       </keep-alive>
       // **数组,需要使用v-bind **不缓存a组件和b组件
       <keep-alive :exclude="[a,b]">
             <component :is="componentName"></component>
       </keep-alive>
      
    3. max - 数字类型。表示最多可以缓存多少组件实例。

      <keep-alive :max="10">   <!--表示最多缓存十个组件实例-->
        <component :is="view"></component>
      </keep-alive>
      
  4. 对于keep-alive需要知道的事情?
    keep-alive提供了两个生命钩子,分别是activated与deactivated。
    因为keep-alive会将组件保存在内存中,并不会销毁以及重新创建,所以不会重新调用组件的created等方法,需要用activated与deactivated这两个生命钩子来得知当前组件是否处于活动状态。

      <script>
          export default {
                name: 'componentA',
                activated() {
                      console.log('组件激活了')
                }
                deactivated() {
                      console.log('组件被缓存了')
                }
          }
      </script>
    


2. 单页面应用和多页面应用区别及优缺点

  1. 单页面应用(SPA),其实就是指只有一个主页面的应用,类似于前端现在的三大框架,React、Vue、Angular浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。
    单页应用的原理是:利用js感知到url的变化,通过这一点,可以用js动态的将当前的页面内容清除掉,然后将下一个页面的内容挂载到当前的页面上。页面每次切换跳转时,并不需要做html文件的请求,这样就节约了很多http发送时延,我们在切换页面的时候速度很快。

  2. 单页面应用的优缺点:

    1. 优点
      • 用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小
      • 前后端分离
      • 页面效果会比较炫酷(比如切换页面内容时的专场动画)
    2. 缺点
      • 不利于seo (搜索引擎优化)
      • 导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)
      • 初次加载时耗时多
      • 页面复杂度提高很多
  3. 多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新,每次都请求一个新的页面,

    1. 优点
      • 首屏时间快,seo效果好
    2. 缺点


3. 什么是计算属性?什么情况使用?

  1. 首先看一下什么是计算属性?
    computed是Vue的计算属性

  2. 为什么会有计算属性?
    我们都知道的是,在Vue的模板指令中我们可以书写一些简单的计算,但是,如果我们在模板指令中书写太多的逻辑运算的话,会使得代码难以维护,例如

    <div>{{ count * price* discount + deliver }}</div>
    

    我们不需要关注这个运算的结果是什么,想象一下,当你的项目中在模板指令存在这样的运算的时候,你的代码将变得难以解读

  3. 什么情况下使用计算属性?
    当我们需要一个值或者一个数据,而这个数据需要通过一些逻辑运算才能得到的时候,我们更希望将它放在计算属性内,这样的话我们可以将整个项目对于数据的复杂运算逻辑全部集中管理在计算属性内

  4. 计算属性如何使用?
    在一个计算属性里可以完成各种复杂的逻辑,包括运算、函数调用等,只要最终返回一个结果就可以。计算属性可以依赖多个Vue 实例的数据,还可以依赖其他计算属性; 而且计算属性不仅可以依赖当前Vue 实例的数据,还可以依赖其他实例的数据
    只要其中任一数据变化,计算属性就会重新执行,视图也会更新。

     // template
    <div>{{totalPrice}}</div>
     // script
      computed: {
          totalPrice() {
                return this.count * this.price* this.discount + this.deliver;
          }
      }
    

    这样我们就实现了一个最基础的计算属性
    而计算属性还有一些稍微高级的用法

     // template
    <div>{{totalPrice}}</div>
     // script
      computed: {
          totalPrice: {
               get() {
                   return this.count * this.price* this.discount + this.deliver;
                }
                set(newValue) {
                      this.count = (newValue - this.deliver) / this.discount / this.price
                }
          }
      }
    

    每一个计算属性都包含一个getter 和一个setter ,我们上面的示例中,利用getter函数返回了计算属性的值,并且提供一个setter 函数, 当手动修改计算属性的值就像修改一个普通数据那样时,就会触发setter 函数,执行一些自定义的操作.

    可以看到的是,计算属性可以是一个函数,而我们在Vue中,methods也是一个函数,那我们同样可以通过methods中的方法来实现计算属性,但是它们之间肯定是有区别的,接着看一下它们之间的区别


4. computed、methods的区别

  1. computed计算属性与methods方法的区别
    • computed计算属性是基于内部的响应式依赖来进行计算并缓存的,所谓的响应式依赖就是被我们的Vue实例所监听的数据
    • computed计算属性是拥有缓存的,我们每次访问同一个计算属性,只要内部依赖没有发生改变,它都不会重新计算
    • methods方法是调用函数,我们多次使用就等于多次调用了这个函数,函数是没有缓存的,所以每次都重新计算了,当我们的内部依赖发生改变的时候,都会重新render页面,此时页面上所有调用了这个函数的地方都会再次重新调用这个函数
    • 这也是我们为什么要使用计算属性,而不是使用methods来计算一个数据。


5. 什么是自定义指令,有哪些钩子函数及自定义指令的使用场景

  1. 自定义指令是什么?以及自定义指令的使用场景
    在Vue中,有很多内置指令,但是这些指令只能满足我们最基础的使用,当我们在实际项目中遇到了必须要对DOM进行操作的逻辑时,我们就用到了自定义指令
  2. 自定义指令的钩子函数 (这里我们可以直接看官网的介绍)
    bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
    inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
    update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
    componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
    unbind:只调用一次,指令与元素解绑时调用。
  3. 钩子函数的参数
    el - 绑定指令的DOM元素,可以用来直接操作DOM元素
    binding - 绑定指令时得到的值、修饰符、参数等
    VNode - Vue生成的虚拟DOM节点
    oldVNode - 修改之前的VNode,只能在update函数和componentUpdated中调用
  4. 自定义指令的使用方法
    Vue.directive('drag', {
        bind(el,binding,VNode) {
            console.log('指令绑定成功')
        }
        inserted(el,binding,VNode) {
            console.log('当前DOM节点已经插入到父节点')
        }
        upate(el,binding,VNode,oldVNode) {
            console.log('当前VNode发生更新')
        }
        componentUpated(el,binding,VNode,oldVNode) {
            console.log('当前VNode更新成功')
        }
        unbind(el,binding,VNode) {
            console.log('指令与当前元素解绑')
        }
    })
    
    <div v-drag>绑定指令的div</div>
    

*** ## 6. 父组件获取异步动态数据传递给子组件,报错如何解决?

这个问题一般出现在对象,或者我们直接调用数组指定下标的内容时会出现,因为数组的话,一般我们都是执行列表渲染,数组内没有数据是不会执行遍历的,而对象不一样,如果没有对应的属性,还是会调用,只是返回undefined,而我们再调用undefined上的属性时,就会出现报错

在父组件给子组件传值的时候,给子组件加一个判断,如果数据没有请求到就不渲染当前组件

<div v-if="list.length>0">
    <child-compnent></child-component>
</div>

data() {
    return {
        list: []
    }
}
//  执行异步请求数据的方法和生命周期钩子等等
methods: {
    getData() {
        // ·····
     }
}


7. vue-router参数传递方法详述及区别

  1. 首先在Vue中router路由跳转分为两种
    router.push(路由信息) - 编程式导航
    <router-link to="路由信息"></router-link> - 声明式导航
  2. 这两种跳转方式传递参数是没有太大的区别的
  3. vue-router传参有两种方式queryparams
  4. query传参
    • <router-link to="/?tab=all">query传参</router-link>
      this.$router.push('/?tab=all')
      
    •  <router-link :to="{ path: '/', query: {tab: 'all' }}">query传参</router-link>
       this.$router.push({ path: '/', query: {tab: 'all' }})
      
  5. params传参
    • <router-link to="/detail/1">query传参</router-link>
      this.$router.push('/detail/1')
      
    •  <router-link :to="{ path: '/', params: { id: 1 } }">query传参</router-link>
       this.$router.push({ path: '/', params: { id: 1 } })
      
  6. query和params传参的区别
    • params是路由的一部分,必须要在路由后面添加参数名。query是拼接在url后面的参数,没有也没关系。
    • params一旦设置在路由,params就是路由的一部分,如果这个路由有params传参,但是在跳转的时候没有传这个参数,会导致跳转失败或者页面会没有内容。
    • query会直接显示在 ? 后边 url?tab=all 而params是显示在路由的子路由上 url/name
    • 获取方式不同,query使用this.$route.query获取,params使用this.$route.params获取
    • 使用场景不同,query一般用来做搜索或者列表页,而params一般用来做详情页


结言
感谢您的查阅,代码冗余或者有错误的地方望不吝赐教;菜鸟一枚,请多关照

猜你喜欢

转载自blog.csdn.net/qq_44492790/article/details/95237810