【Vue】面试题总结 - 基础知识总结 - 复习专用

1,130 阅读7分钟

本来是想总结一些面试题,这一总结起来,直接又重新学了一遍Vue 就当作复习Vue用的,总结了一些基础知识也有一些面试题 持续更新...........

1. 基础知识

1.1 指令

  • v-text : 更新元素的 textContent
  • v-html : 更新元素的 innerHTML
  • v-if : 如果为true, 当前标签才会输出到页面
  • v-else: 如果为false, 当前标签才会输出到页面
  • v-show : 通过控制display样式来控制显示/隐藏
  • v-for : 遍历数组/对象
  • v-on : 绑定事件监听, 一般简写为@
  • v-bind : 强制绑定解析表达式, 可以省略v-bind
  • v-model : 双向数据绑定
  • ref : 为某个元素注册一个唯一标识, vue对象通过$refs属性访问这个元素对象
  • v-cloak : 使用它防止闪现表达式, 与css配合: [v-cloak] { display: none }

1.2 v-if与v-show

v-if是控制元素是否加载到页面上(有性能开销) v-show是控制元素的显示与隐藏 (初始创建时加载一次)

频繁切换使用v-show

1.3 v-for

数组

(item, index) in array

对象

(value, key, index) in object

1.4 v-if与v-for不能同时用

v-for优先级高一点,会循环之后再判断条件,开销比较大 可以把v-if放在父标签或者子标签中

1.5 data、计算属性、方法

data对象:可以使用它来存储字符串、数组和对象等数据; 方法:可以使用它来存储函数并在模板中调用; 计算属性:可以使用它将函数存储下来,然后像访问data对象中的属性一样调用 在这里插入图片描述

1.6 计算属性与方法

  1. 计算属性会被缓存(只有当计算属性的依赖发生变化时,代码才会被再次执行)

  2. 计算属性可以设置setter和getter

1.7 方法中的this

在方法中,this指向该方法所属的组件。可以使用this访问data对象的属性和其他方法

1.8 事件对象

默认传入 $event 有参数时,显式在最后传入 $event

<p>{{number}}</p>
<button @click="incrementBy1">点击+1</button>
<button @click="incrementBy10(10, $event)">点击+10</button>
data(){
	return {
		number: 0
	}
}
methods: {
	incrementBy1(e){
		this.number++
		console.log(e.__proto__.constructor) // f MouseEvent()
		console.log(e.target) // <button>点击+1</button>
		console.log(e.currentTarget) // <button>点击+1</button>
	},
	incrementBy10(step, e){
		this.number+=step
		console.log(e.__proto__.constructor) // f MouseEvent()
	}
}

1.9 事件修饰符

.prevent 阻止执行事件默认行为 .stop 阻止事件继续传播 .once 只在第一次触发事件的时候触发事件侦听器 .capture 使用捕获模式 .self 只监听元素自身而不是它的子元素上触发的事件

1.10 侦听器 watch

侦听器可以监听data对象属性或者计算属性的变化 当监听的属性发生变化时,侦听器会被传入两个参数:所监听属性的当前值和原来的旧值 监听整个对象被称作深度监听,通过将deep选项设置为true来开启这一特性

data() {
	return {
		name: 'YK菌',
		info: {
			hobby: '写博客'
		}
	}
},
watch: {
	name(newValue, oldValue){
		console.log(newValue, oldValue);
	},
	info: {
		handle: function(newValue, oldValue){
			console.log(newValue, oldValue);
		},
		deep: true
}

当数据改变时,会调用watch里对应的函数 引用类型值改变后,新旧值都改变了,所以拿不到oldValue

1.11 动态绑定class、style

1. 类名class

对象

:class="{ 'active': isActive, 'error': isError }"
data(){
	return {
		isActive: true,
		isError: false
	}
}

数组

:class="[activeCls, errorCls]"
data(){
	return {
		activeCls: 'active',
		errorCls: 'error'
	}
}

对象数组之间可以互相嵌套

2. 内联样式style

对象

:style="{ 'color': color, 'fontSize': fontSize + 'px' }"
data(){
	return {
		color: 'red',
		fontSize : 14
	}
}

数组

:style="[ styleA, styleB ]"

1.12 过滤器

使用 只可以在插值和v-bind指令中使用过滤器

{{ origin | format }}

组件中定义

filters: {
	format(value){
		return value + 2;
	}
}

全局定义

Vue.filter('format', function(value){
	return value + 2;
})

过滤器是组件中唯一不能使用this来访问数据或者方法的地方 因为过滤器应该是纯函数,也就是说对于同样的输入每次都返回同样的输出,而不涉及任何外部数据。如果想在过滤器中访问其他数据,可以将它作为参数传入

1.13 v-clock

解决初始化慢导致页面闪动 不需要表达式,和CSS的display:none配合使用

<div v-clock>{{ data }}</div>
[v-clock] {
	display: none;
}

1.14 生命周期钩子

生命周期钩子是一系列会在组件生命周期——从组件被创建并添加到DOM,到组件被销毁的整个过程——的各个阶段被调用的函数

一个有八个 四组

beforeCreate在实例初始化前被触发。 created会在实例初始化之后、被添加到DOM之前触发。 beforeMount会在元素已经准备好被添加到DOM,但还没有添加的时候触发。 mounted会在元素创建后触发(但并不一定已经添加到了DOM,可以用nextTick来保证这一点)。 beforeUpdate会在由于数据更新将要对DOM做一些更改时触发。 updated会在DOM的更改已经完成后触发。 beforeDestroy会在组件即将被销毁并且从DOM上移除时触发。 destroyed会在组件被销毁后触发

在这里插入图片描述

1.15 什么是nextTick

Vue.nextTick( [callback, context] )
vm.$nextTick( [callback, context] )

在下次 DOM 更新循环结束(异步)之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

2. 组件相关

2.1 data为什么是函数

一个组件可以在同一个页面上被多次引用,不希望它们共享一个data对象 因为同一个组件的每个实例的data属性是同一个对象的引用,当该组件的某个实例修改了自身的data属性,相当于所有实例的data属性都被修改了 所以组件的data属性应该是一个函数,在组件初始化时Vue会调用这个函数来生成data对象

2.2 组件间通信

2.2.1 props 父 —> 子

父组件通过标签传递数据 list

<MyList :list="list" />

子组件通过props接收数据list

props: {
	list: Array
}

父组件通过props向下传递数据给子组件 注:组件中的数据共有三种形式:datapropscomputed

2.2.2 $emit 子 —> 父 + 自定义事件

子组件 向外触发一个事件,携带需要传出去的数据this.title

this.$emit('add', this.title)

父组件监听add事件

<MyInput @add="add">

父组件中定义add方法,获取传入的数据,进行操作

methods: {
	add(title){
		// 得到数据可以进行操作了
		this.list.push({
			id: Math.random(),
			title
		})
	}
}

2.2.3 EventBus事件总线 .$emit .$on

定义一个event-bus.js

import Vue from 'vue'
const eventBus = new Vue()
export default eventBus

在需要用到的组件中导入

import eventBus from './event-bus'

在事件中派发到eventBus中并携带数据

eventBus.$emit('addItem', this.title)

在需要用到数据的组件中接收数据

mounted() {
  eventBus.$on('addItem', this.handleAddTitle)
}

使用数据

methods: {
	handleAddTitle(title){
		console.log(title)
	}
}

解绑事件监听

beforeDestroy(){
	eventBus.$off('addItem', this.handleAddTitle)
}

2.2.4 PubSubJS发布订阅

组件中引入PubSubJS库

import PubSub from 'pubsub-js'

发布消息(触发事件)传递数据

PubSub.publish('deleteTodo', this.index)

订阅消息(绑定事件监听) 接收数据

export default {
	mounted () {
		// 订阅消息(deleteTodo)
		PubSub.subscribe('deleteTodo', (msg, index) => {
			this.deleteTodo(index)
		})
	}
}

2.2.5 作用域插槽

见下面的

2.3 slot插槽

2.3.1 匿名slot

子组件内部定义一个占位符,父组件使用时可以向组件标签中插入任何内容替换子组件slot中的内容

2.3.2 具名slot

给slot元素指定一个name后,可以分发多个内容

例子 子组件模板定义

<template>
  <div class="container">
    <div class="header">
      <slot name="header"></slot>
    </div>
    <div class="main">
      <slot></slot>
    </div>
    <div class="footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

父组件使用

<child-component>
  <h2 slot="header">标题</h2>
  <p>内容</p>
  <p>更多</p>
  <div slot="footer">底部</div>
</child-component>

最后相当于

<div class="container">
  <div class="header">
    <h2>标题</h2>
  </div>
  <div class="main">
    <p>内容</p>
  	<p>更多</p>
  </div>
  <div class="footer">
    <div>底部</div>
  </div>
</div>

这里的slot已经废弃了,用v-slot替代 必须由template标签包裹,且v-slot可以简写成#

<child-component>
  <template v-slot:header>
    <h2>标题</h2>
  </template>
  <p>内容</p>
  <p>更多</p>
  <template #footer>
    <div slot="footer">底部</div>
  </template>
</child-component>

2.3.3 作用域插槽

子组件的数据通过标签传给父组件

<template>
  <h1>
    <slot :user="user">
      {{ user.name }}
    </slot>
  </h1>
</template>

<script>
export default {
  data() {
    return {
      user: {
        name: 'YK',
        age: 18
      }
    }
  }
}
</script>

父组件接收数据并使用

<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.age }}
  </template>
</current-user>

对象结构赋值更简洁

<current-user>
  <template v-slot:default="{ user }">
    {{ user.age }}
  </template>
</current-user>

2.4 动态组件

Vue.js提供了一个特殊的元素<component>用来动态地挂载不同的组件,使用is特性来选择要挂载的组件 这个相当于是一个组件占位符,等待填充

示例 这里有三个组件 MyImage、MyVideo、MyTextApp 组件要根据数据来动态渲染组件

数据

data() {
  return {
    produceData: [
      { id: 1, type: 'Video' },
      { id: 2, type: 'Text' },
      { id: 3, type: 'Image' },
      { id: 4, type: 'Text' }
    ]
  }
}

引入组件

import MyImage from './components/MyImage.vue'
import MyText from './components/MyText.vue'
import MyVideo from './components/MyVideo.vue'

动态渲染组件

<div v-for="item in produceData" :key="item.id">
  <component :is="`My${item.type}`"></component>
</div>

在这里插入图片描述

2.5 异步组件

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

修改引入方式

components: {
	AsyncTest: () => import('./components/AsyncTest')
}

在这里插入图片描述 只有在点击按钮之后才会加载请求响应的组件文件

可以通过webpack魔法注释给文件起一个名字

AsyncTest: () => import(/* webpackChunkName: "AsyncTest" */'./components/AsyncTest')

在这里插入图片描述

2.6 keep-alive实现组件缓存

<h1>keep-alive</h1>
<button @click="state = 'A'">A</button>
<button @click="state = 'B'">B</button>
<button @click="state = 'C'">C</button>
<comp-a v-if="state === 'A'"></comp-a>
<comp-b v-if="state === 'B'"></comp-b>
<comp-c v-if="state === 'C'"></comp-c>

子组件

<template>
  <div>
    组件A
  </div>
</template>

<script>
export default {
  mounted() {
    console.log('组件A渲染')
  },
  destroyed() {
    console.log('组件A销毁')
  }
}
</script>

在这里插入图片描述 切换组件的时候,另外的组件就会销毁,没有缓存,频繁切换的话开销比较大

利用keep-alive将需要缓存的组件包裹起来就可以了

<keep-alive>
  <comp-a v-if="state === 'A'"></comp-a>
  <comp-b v-if="state === 'B'"></comp-b>
  <comp-c v-if="state === 'C'"></comp-c>
</keep-alive>

在这里插入图片描述 题外话,使用v-show会在页面一开始就渲染全部组件 在这里插入图片描述

2.7 mixin

抽离组件公共部分 将组件中公共的数据、方法以及生命周期钩子抽离出来保存在一个mixin.js中

export default {
  data() {
    return {
      commonData: '公共的数据'
    }
  },
  methods: {
    commonMethod() {
      console.log('公共的方法')
    }
  },
  mounted() {
    console.log('common mounted')
  }
}

在需要使用的组件中,引入mixin即可

import mixin from './mixin'
export default {
  mixins: [mixin]
}

就可以在组件中使用公共的数据方法以及生命周期钩子了 可以抽离除多个mixin

import mixin from './mixin'
import mixin2 from './mixin2'
export default {
  mixins: [ mixin, mixin2 ]
}

3. Vue家族

3.1 vue-cli

创建Vue项目

【Vue】使用vue-cli 2.x 创建项目-配置Vue脚手架-npm方法-vue-cli配置-vscode配置-项目打包与发布-ESlint使用

【Vue】vue-cli 3.x - 创建项目 - 图形化 - 命令行 - 配置项目 - axios - elementUI - 码云SSH公钥设置 - Gitee同步

3.2 vue-router

【Vue】路由 - vue-router - SPA - 基本路由 - 嵌套路由 - 编程式路由导航

3.2.1 路由的概念

  1. 后端路由 概念:根据不同的用户URL请求,返回不同的内容 本质:URL请求地址服务器资源之间的对应关系 在这里插入图片描述
  2. 前端路由 概念:根据不同的用户事件,显示不同的页面内容 本质:用户事件事件处理函数之间的对应关系 在这里插入图片描述 VueRouter官方文档

3.3.2 基本路由

  1. 引入相关的库文件【vue】【vue-router】
  2. 添加路由链接<router-link to="/about">About</router-link>
  3. 添加路由占位符<router-view></router-view>
  4. 定义路由组件
  5. 创建路由实例对象并配置路由规则new Router()【路由器】
  6. 把路由挂载到Vue根实例对象vm中 router: router
// 定义路由器
import Vue from 'vue'
import Router from 'vue-router'

import About from '../pages/About'
import Home from '../pages/Home'

Vue.use(Router)
// 创建路由实例对象
export default new Router({
  routes: [
    {
      path: '/about',
      component: About
    },
    {
      path: '/home',
      component: Home
    },
    {
      path: '/',
      redirect: About
    }
  ]
})
// 使用路由器
import Vue from 'vue'
import App from './App'
import router from './router'

new Vue({ 
  el: '#app',
  router, // 挂载路由实例对象
  components: { App },
  template: '<App/>'
})

3.3.3 嵌套路由

{
  path: '/home',
  component: Home,
  children: [
    {
      // path: '/news' // path最左侧斜杠代表根路径
      path: '/home/news', //完整写法
      component: News
    },
    {
      path: 'message', // 简化写法
      component: Message
    },
    { //设置默认显示
      path: '',
      redirect: '/home/news'
    }
  ]
}

3.3.4 动态路由匹配

设置动态路径参数,以冒号开头 【动态路径匹配】

new Router({
  routes: [
    {
      path: '/user/:id',
      component: About
    }
  ]
})

路由组件中通过$route.params获取路由参数 在组件实例中,可以通过属性this.$route来获取当前路由对象

<div>User组件——用户ID为 {{ $route.params.id }} </div>

页面使用时接收参数

<router-link to="/user/1">User</router-link>

3.3.5 向路由组件传递数据

$route与对应组件路由高度耦合,不够灵活,所以可以使用props将组件与路由解耦

  1. props的类型是布尔值 (传动态参数)

路由规则开启props传参

routes: [{ 
	path:'/user/:id', 
	component: User, 
	props: true 
}]

路由组件接收并使用路由参数

props: ['id'], // 接收路由参数
template: '<div>用户ID: {{ id }}</div>' // 使用路由参数
  1. props的值是对象类型 (传静态参数)

此时id就访问不到了

routes: [{ 
	path:'/user/:id', 
	component: User, 
	props: { uname: 'yk', age: 18 } 
}]

路由组件接收并使用路由参数

props: ['uname', 'age'], // 接收路由参数
template: '<div>用户信息: {{ uname + '---' + age }}</div>' // 使用路由参数
  1. props的值是函数 (传动态参数+静态参数)
routes: [{ 
	path:'/user/:id', 
	component: User, 
	props: route => (
		{ 
			uname: 'yk',
			age: 18,
			id: route.params.id
		}
	)
}]

路由组件接收并使用路由参数

props: ['uname', 'age', 'id'], // 接收路由参数
template: '<div>用户信息: {{ uname + '---' + age + '---' +  id }}</div>' // 使用路由参数

3.3.6 命名路由

在路由规则中命名路由 加一个name属性

routes: [{ 
	path:'/user/:id', 
	name: 'user',
	component: User
}]

使用

<router-link :to="{ name: 'user', params: {id:123}}">User</router-link>

3.3.7 编程式路由导航

  1. 声明式导航 通过点击链接实现导航的方式 如a标签,router-link标签
  2. 编程式导航 通过JavaScript的API实现导航的方式 如location.herf

VueRouter中实现编程式导航

this.$router.push('hash地址')
this.$router.go(n)

在这里插入图片描述

3.4 vuex

vuex官方文档

【Vue】vuex - 状态自管理应用 - state - view - actions

【Vue】Vuex管理状态入门到实战 - 计数器demo - todoList项目 - 组件间共享数据 - State - Mutation - Action - Getter

Vuex可以满足复杂应用中多组件进行状态共享的需求 在这里插入图片描述

使用Vuex

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  	// 定义数据
  	value: 'oldValue'
  },
  mutations: {
    // 定义方法
    setValue(state, newValue){
      state.value = newValue
	}
  },
  actions: {
    getMessage(){}
  },
  getters: {
  	// 定义getter
    getMyInfo(state){
	  return `这里可以操作state中的一些数据${state.value}并返回`
    }
  },
  modules: {
  }
})

挂载到Vue实例上

import Vue from 'vue'
import App from './App.vue'
import store from './store'

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')

3.4.1 概念

State 提供唯一的公共数据源,所有共享的数据都要统一放到 Store 的 State 中进行存储 Mutation 用于变更 Store中的数据,包含多个直接更新state 的方法(回调函数)的对象 Action 用于处理异步任务,包含多个事件回调函数的对象 Getter 用于对 Store 中的数据进行加工处理形成新的数据,包含多个计算属性(get)的对象 Module 一个module 是一个 store 的配置对象

3.4.2 组件中访问state的方式

  1. 使用 this.$store
this.$store.state.全局数据名称
  1. mapState 辅助函数
import { mapState } from 'vuex'
export default {
  computed: {
    ...mapState(['count'])
  }
}

3.4.3 组件中触发Mutation的方式

  1. 使用this.$store.commit()
this.$store.commit('setValue', 'newValue') 
  1. 使用mapMutations
// 1. 从 vuex 中按需导入 mapMutations 函数
import { mapMutations } from 'vuex'
// 2. 将指定的 mutations 函数,映射为当前组件的 methods 函数
methods: {
	...mapMutations(['mutation1', 'mutation2'])
	handle(){
		this.mutation1('params')
	}
}

需要传多个参数的情况

定义

mutations: {
  // 定义方法
  setValue(state, payload){
    const {newValue, params1} = payload;
    console.log(params1)
    state.value = newValue
  }
}

触发

this.$store.commit('setValue', {newValue, params1})

3.3.4 组件中触发getter的方式

  1. 使用this.$store.getters
data(){
  reutrn {
    myInfo: ''	
  }
},
mounted() {
  this.myInfo = this.$store.getters.getMyInfo;
}
  1. 使用mapGetters
import { mapGetters } from 'vuex'
computed: {
	...mapGetters(['showNum'])
}

3.3.5 组件中触发actions的方式

用mutation只能做到同步变更,而action则用于实现异步变更

使用store.dispatch()方法来在组件中调用getMessages

4. 源码相关

未完待续....

4.1 模板引擎

【Vue源码】mustache模板引擎 - 基本使用 - 底层原理 - 手写实现

4.2 数据响应式原理

【Vue源码】数据响应式原理 - 依赖收集 - defineReactive - Observer - Dep - Watcher

数据更新了但是页面没有变化,为什么?怎么解决

cn.vuejs.org/v2/guide/re…

  1. 数组的情况

数组中直接使用索引来修改数组中的数据vm.items[indexOfItem] = newValue 直接修改数组的长度vm.items[indexOfItem] = newValue

应该使用Vue拦截的六种数组原型上的方法push(),pop(),shift(),unshift(),splice(),sort() 或者使用 Vue.set(vm.items, indexOfItem, newValue)

  1. 对象的情况 Vue 无法检测 property 的添加或移除 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的

使用Vue.set(object, propertyName, value) 或者使用this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

4.3 虚拟DOM

【Vue源码】图解 diff算法 与 虚拟DOM-snabbdom-最小量更新原理解析-手写源码-updateChildren

4.4 diff算法

【Vue源码】图解 diff算法 与 虚拟DOM-snabbdom-最小量更新原理解析-手写源码-updateChildren

参考

  1. Vue官方文档
  2. VueRouter官方文档
  3. Vuex官方文档
  4. 《Vue.js快跑:构建触手可及的高性能Web应用》
  5. 《Vue.js实战》
  6. 【Vue】使用vue-cli 2.x 创建项目-配置Vue脚手架-npm方法-vue-cli配置-vscode配置-项目打包与发布-ESlint使用
  7. 【Vue】vue-cli 3.x - 创建项目 - 图形化 - 命令行 - 配置项目 - axios - elementUI - 码云SSH公钥设置 - Gitee同步
  8. 【Vue】路由 - vue-router - SPA - 基本路由 - 嵌套路由 - 编程式路由导航
  9. 【Vue】vuex - 状态自管理应用 - state - view - actions
  10. 【Vue】Vuex管理状态入门到实战 - 计数器demo - todoList项目 - 组件间共享数据 - State - Mutation - Action - Getter
  11. 【Vue源码】mustache模板引擎 - 基本使用 - 底层原理 - 手写实现
  12. 【Vue源码】数据响应式原理 - 依赖收集 - defineReactive - Observer - Dep - Watcher
  13. 【Vue源码】图解 diff算法 与 虚拟DOM-snabbdom-最小量更新原理解析-手写源码-updateChildren