初级面试Vue篇

初级面试Vue篇

1.Computed 和 Watch 的区别

Computed:

  • 它支持缓存,只有依赖的数据发生了变化,才会重新计算
  • 不支持异步,当Computed 中有异步时,无法监听数据的变化
  • computed 的值会默认走缓存,计算属性是基于他们的响应式依赖进行缓存的,也就是基于data声明过,或者父组件传递过来的props中的数据进行计算的
  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,一般会使用computed
  • 如果computed属性的属性值是函数,那么默认使用get方法,函数的返回值就是属性的属性值;在computed中,属性有一个get方法和一个set方法,当数据发生变化时,会调用set方法

Watch:

  • 它不支持缓存,数据变化时,他就会出发相应的操作
  • 支持异步监听
  • 监听的函数接受两个参数,第一个参数是最新的值,第二个是变化之前的值
  • 当一个属性发生变化时,就需要执行相应的操作
  • 监听数据必须是data中声明的或者父组件传递过来的props中的数据,当发生变化时,会触发其他操作,函数有两个的参数:
    • immediate:组件加载立即触发回调函数
    • deep:深度侦听,发现数据内部的变化,在复杂数据类型中,例如数组中的对象发生变化。需要注意的是,deep无法侦听到数组和对象内部的变化

当想要执行异步或者昂贵的操作以响应不断变化时,就需要使用watch

总结:

  • computed 计算属性:依赖其他属性值,并且computed的值有缓存,只有未来的属性发生改变,下一次获取computed 的值时才会重新计算 computed 的值
  • watch侦听器:更多的是观察的作用,无缓存性,类似于某种数据的监听回调,每当监听的数据发生变化时都会执行回调函数进行后续的操作

运用场景:

  • 当需要进行数值计算,并且依赖于其他数据时,应该使用computed,英文欸可以利用computed的缓存特性,避免每车次取值时都要重新计算。
  • 当需要在数据变化时执行异步或者开销较大的操作时,应该使用watch,使用watch选项允许执行异步操作(访问一个API),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的

2. 如何保存页面的当前的状态

既然要保持页面的状态(其实也就是组件的状态),那么会出现以下两种情况:

  • 前组件会被卸载
  • 前组件不会被卸载

那么可以按照这两种情况分别得到以下方法:

组件会被卸载:

(1)将状态存储在LocalStorage / SessionStorage

优点:

  • 兼容性好,不需要额外库和工具
  • 简单快捷,基本可以满足大部分需求

缺点:

  • 状态通过 JOSN 方法存储(相当于深拷贝),如果状态中有特殊情况(比如 Date特殊 对象等)的时候会得到字符串而不是原来的值。
  • 如果 B 组件后退或者下一页跳转并不是组件,那么 flag 判断会失效,导致从其他页面进入 A 组件页面时 A 组件会重新读取Storage,会造成很奇怪的现象

(2)路由传值

优点:

  • 简单快捷,不会污染 LocalStorage / SessionStorage
  • 可以传递 Date 等特殊对象(不担心 JOSN.stringify / pase 的不足)

缺点:

  • 如果 A 组件可以跳转至多个组件,那么在每一个跳转组件内都要写相同的逻辑

组件不会被卸载:

(1)单页面渲染

要切换的组件作为子组件全屏渲染,父组件中正常存储页面状态

优点:

  • 代码量少
  • 不需要考虑状态传递过程中的错误

缺点:

  • 增加 A 组件维护成本
  • 组要传入额外的 prop 到 B 组件
  • 无法利用路由定位页面

除此之外,在 Vue 中,还可以时用 keep-alive 来缓存页面,当组件在 keep-alive 内被切换组件的 activated、deactivated 这两个生命周期钩子函数会执行

被包裹在keep-alive中的组件的状态将会被保留:

<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</kepp-alive>

router.js

{
  path: '/',
  name: 'xxx',
  component: ()=>import('../src/views/xxx.vue'),
  meta:{
    keepAlive: true // 需要被缓存
  }
}

3. v-if和v-show的区别

  • **手段:**v-if 时动态的向 DOM 树内添加或者删除 DOM 元素;v-show 时通过设置 DOM 元素的 display 样式属性控制显隐
  • **编译过程:**v-if 切换有一个局部函数编译 / 卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show 只是简单的基于 CSS 切换
  • **编译条件:**v-if 时惰性的,如果初始条件为假,则什么都不做;只有在条件第一次变为真时才开始局部编译;v-show 是任何条件下,无论首次条件是否为真,都被编译,然后被缓存,而且 DOM 元素保留
  • **性能消耗:**v-if 有更高的切换消耗;v-show 有更高的初始渲染消耗
  • **使用场景:**v-if 适合运行条件不大可能改变;v-show适合频繁切换

4. data为什么是一个函数而不是对象

JavaScript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。

而在Vue中,更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。

所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义,这样当每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。

5. Vue的优点

  • 轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十 kb
  • 简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
  • 双向数据绑定:保留了 angular 的特点,在数据操作方面更为简单;
  • 组件化:保留了 react 的优点,实现了 html 的封装和重用,在构建单页面应用方面有着独特的优势;
  • 视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
  • 虚拟DOM:dom 操作是非常耗费性能的,不再使用原生的 dom 操作节点,极大解放 dom 操作,但具体操作的还是 dom 不过是换了另一种方式;
  • 运行速度更快:相比较于 react 而言,同样是操作虚拟 dom,就性能而言, vue 存在很大的优势。

6. Vue的性能优化有哪些

(1)编码阶段

  • 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
  • v-if和v-for不能连用
  • 如果需要使用v-for给每项元素绑定事件时使用事件代理
  • SPA 页面采用keep-alive缓存组件
  • 在更多的情况下,使用v-if替代v-show
  • key保证唯一
  • 使用路由懒加载、异步组件
  • 防抖、节流
  • 第三方模块按需导入
  • 长列表滚动到可视区域动态加载
  • 图片懒加载

(2)SEO 优化

  • 预渲染
  • 服务端渲染SSR

(3)打包优化

  • 压缩代码
  • Tree Shaking/Scope Hoisting
  • 使用cdn加载第三方模块
  • 多线程打包happypack
  • splitChunks抽离公共文件
  • sourceMap优化

(4)用户体验

  • 骨架屏
  • PWA
  • 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

7. 说一下Vue的生命周期

Vue 实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。

  1. beforeCreate(创建前):数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据。
  2. created (创建后) :实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 $el 属性。
  3. beforeMount(挂载前):在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。此时还没有挂载html到页面上。
  4. mounted(挂载后):在el被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html 页面中。此过程中进行ajax交互。
  5. beforeUpdate(更新前):响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。
  6. updated(更新后) :在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
  7. beforeDestroy(销毁前):实例销毁之前调用。这一步,实例仍然完全可用,this 仍能获取到实例。
  8. destroyed(销毁后):实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。

另外还有 keep-alive 独有的生命周期,分别为 activateddeactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 activated 钩子函数。

8. created和mounted的区别

  • created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
  • mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作

9. 一般在哪个生命周期请求异步数据

我们可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。

推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

  • 能更快获取到服务端数据,减少页面加载时间,用户体验更好;
  • SSR不支持 beforeMount 、mounted 钩子函数,放在 created 中有助于一致性。

10. props / $emit

父组件通过props向子组件传递数据,子组件通过$emit和父组件通信

1. 父组件向子组件传值
  • props只能是父组件向子组件进行传值,props使得父子组件之间形成了一个单向下行绑定。子组件的数据会随着父组件不断更新。
  • props 可以显示定义一个或一个以上的数据,对于接收的数据,可以是各种数据类型,同样也可以传递一个函数。
  • props属性名规则:若在props中使用驼峰形式,模板中需要使用短横线的形式
// 父组件
<template>
    <div id="father">
        <son :msg="msgData" :fn="myFunction"></son>
    </div>
</template>

<script>
import son from "./son.vue";
export default {
    name: father,
    data() {
        msgData: "父组件数据";
    },
    methods: {
        myFunction() {
            console.log("vue");
        }
    },
    components: {
        son
    }
};
</script>
// 子组件
<template>
    <div id="son">
        <p>{{msg}}</p>
        <button @click="fn">按钮</button>
    </div>
</template>
<script>
export default {
    name: "son",
    props: ["msg", "fn"]
};
</script>
2. 子组件向父组件传值
  • $emit绑定一个自定义事件,当这个事件被执行的时就会将参数传递给父组件,而父组件通过v-on监听并接收参数。
// 父组件
<template>
  <div class="section">
    <com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
    <p>{{currentIndex}}</p>
  </div>
</template>

<script>
import comArticle from './test/article.vue'
export default {
  name: 'comArticle',
  components: { comArticle },
  data() {
    return {
      currentIndex: -1,
      articleList: ['红楼梦', '西游记', '三国演义']
    }
  },
  methods: {
    onEmitIndex(idx) {
      this.currentIndex = idx
    }
  }
}
</script>
// 父组件
<template>
  <div class="section">
    <com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
    <p>{{currentIndex}}</p>
  </div>
</template>

<script>
import comArticle from './test/article.vue'
export default {
  name: 'comArticle',
  components: { comArticle },
  data() {
    return {
      currentIndex: -1,
      articleList: ['红楼梦', '西游记', '三国演义']
    }
  },
  methods: {
    onEmitIndex(idx) {
      this.currentIndex = idx
    }
  }
}
</script>
//子组件
<template>
  <div>
    <div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div>
  </div>
</template>

<script>
export default {
  props: ['articles'],
  methods: {
    emitIndex(index) {
      this.$emit('onEmitIndex', index) // 触发父组件的方法,并传递参数index
    }
  }
}
</script>

11. ref / $refs

这种方式也是实现父子组件之间的通信。

ref: 这个属性用在子组件上,它的引用就指向了子组件的实例。可以通过实例来访问组件的数据和方法。

在子组件中:

export default {
  data () {
    return {
      name: 'JavaScript'
    }
  },
  methods: {
    sayHello () {
      console.log('hello')
    }
  }
}

在父组件中:

<template>
  <child ref="child"></component-a>
</template>
<script>
  import child from './child.vue'
  export default {
    components: { child },
    mounted () {
      console.log(this.$refs.child.name);  // JavaScript
      this.$refs.child.sayHello();  // hello
    }
  }
</script>

12. r o u t e 和 route 和 routerouter 的区别

  • $route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数
  • $router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。

13. params和query的区别

用法:query要用path来引入,params要用name来引入,接收参数都是类似的,分别是 this.$route.query.namethis.$route.params.name

url地址显示:query更加类似于ajax中get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示

注意:query刷新不会丢失query里面的数据 params刷新会丢失 params里面的数据。

14. Vuex 的原理

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  • 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样可以方便地跟踪每一个状态的变化。

b025e120ca3d0bd2ded3d038d58cacf4.jpg

Vuex为Vue Components建立起了一个完整的生态圈,包括开发中的API调用一环。

(1)核心流程中的主要功能:

  • Vue Components 是 vue 组件,组件会触发(dispatch)一些事件或动作,也就是图中的 Actions;
  • 在组件中发出的动作,肯定是想获取或者改变数据的,但是在 vuex 中,数据是集中管理的,不能直接去更改数据,所以会把这个动作提交(Commit)到 Mutations 中;
  • 然后 Mutations 就去改变(Mutate)State 中的数据;
  • 当 State 中的数据被改变之后,就会重新渲染(Render)到 Vue Components 中去,组件展示更新后的数据,完成一个流程。

(2)各模块在核心流程中的主要功能:

  • Vue Components∶ Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
  • dispatch∶操作行为触发方法,是唯一能执行action的方法。
  • actions∶ 操作行为处理模块。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
  • commit∶状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
  • mutations∶状态改变操作方法。是Vuex修改state的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
  • state∶ 页面状态管理容器对象。集中存储Vuecomponents中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
  • getters∶ state对象读取方法。Vue Components通过该方法读取全局state对象。

15. Vuex中action和mutation的区别

mutation中的操作是一系列的同步函数,用于修改state中的变量的的状态。当使用vuex时需要通过commit来提交需要操作的内容。mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      state.count++      // 变更状态
    }
  }
})

当触发一个类型为 increment 的 mutation 时,需要调用此函数:

store.commit('increment')

而Action类似于mutation,不同点在于:

  • Action 可以包含任意异步操作。
  • Action 提交的是 mutation,而不是直接变更状态。
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。

所以,两者的不同点如下:

  • Mutation专注于修改State,理论上是修改State的唯一途径;Action业务代码、异步请求。
  • Mutation:必须同步执行;Action:可以异步,但不能直接操作State。
  • 在视图更新时,先触发actions,actions再触发mutation
  • mutation的参数是state,它包含store中的数据;store的参数是context,它是 state 的父级,包含 state、getters

16. Vuex 和 localStorage 的区别

(1)最重要的区别

  • vuex存储在内存中
  • localstorage 则以文件的方式存储在本地,只能存储字符串类型的数据,存储对象需要 JSON的stringify和parse方法进行处理。 读取内存比读取硬盘速度要快

(2)应用场景

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vuex用于组件之间的传值。
  • localstorage是本地存储,是将数据存储到浏览器的方法,一般是在跨页面传递数据时使用 。
  • Vuex能做到数据的响应式,localstorage不能

(3)永久性

刷新页面时vuex存储的值会丢失,localstorage不会。

**注意:**对于不变的数据确实可以用localstorage可以代替vuex,但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage无法做到

vue 中 key 值的作用可以分为两种情况来考虑:

  • 第一种情况是 v-if 中使用 key。由于 Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。因此当使用 v-if 来实现元素切换的时候,如果切换前后含有相同类型的元素,那么这个元素就会被复用。如果是相同的 input 元素,那么切换前后用户的输入不会被清除掉,这样是不符合需求的。因此可以通过使用 key 来唯一的标识一个元素,这个情况下,使用 key 的元素不会被复用。这个时候 key 的作用是用来标识一个独立的元素。
  • 第二种情况是 v-for 中使用 key。用 v-for 更新已渲染过的元素列表时,它默认使用“就地复用”的策略。如果数据项的顺序发生了改变,Vue 不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处的每个元素。因此通过为每个列表项提供一个 key 值,来以便 Vue 跟踪元素的身份,从而高效的实现复用。这个时候 key 的作用是为了高效的更新渲染虚拟 DOM。

key 是为 Vue 中 vnode 的唯一标记,通过这个 key,diff 操作可以更准确、更快速

  • 更准确:因为带 key 就不是就地复用了,在 sameNode 函数a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。
  • 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快

18. 为什么不建议用index作为key?

使用index 作为 key和没写基本上没区别,因为不管数组的顺序怎么颠倒,index 都是 0, 1, 2…这样排列,导致 Vue 会复用错误的旧子节点,做很多额外的工作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值