又到了金三银四跳槽黄金时期,are you ready 😊😊? 想必又有大批同行要跳槽~涨薪💲💲💲... 所以小编给大家总结了一份Vue面试礼包💟,这篇文章小编是自己经过查阅资料、参考官方文档以及自己动手实践总结的。如果发现有问题,欢迎到评论区交流😁😁
小编希望读者看到题目时,自己先思考一下,想想你的答案是啥?然后再查看小编给提供的答案。
话不多说,直接上题 🏆
四、Vuex
1. Vuex是什么?
展开查看
Vuex是一个专门为vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用状态,并以相应的规则保证状态以一种可预测的方式发生变化。主要是为了多页面、多组件之间通信。
2. Vuex解决了什么问题?
展开查看
解决两个问题:
-
多个组件依赖同一状态时,对于多层嵌套的组件的传参会将非常繁琐,并且对于兄弟组件间的状态传递无能为力;
-
来自不同组件的行为需要变更同一状态。
3. Vuex统一管理状态的优势
展开查看
-
能够在vuex中集中管理共享的数据,易于开发和后期维护;
-
能够高效地实现组件之间的数据共享,提高开发效率;
-
存储在vuex中的数据都是响应式的,能够实时保持数据与页面同步
4. 简述Vuex工作流程?
展开查看
(1)在Vue组件里面,通过dispatch
来触发actions提交
修改数据操作。
(2)然后再通过actions的commit
来触发mutations
来修改数据。
(3)mutations
接收到commit
的请求,就会修改state
里面的数据。
(4)最后由store触发每一个调用它的组件的更新
。
5. 什么时候适合使用Vuex?
展开查看
当项目遇到以下两种场景时:
- 多个组件依赖于同一状态时。
- 来自不同组件的行为需要变更同一状态。
6. 怎么引用Vuex?
展开查看
先安装依赖npm install vuex --save
在项目目录src中建立store
文件夹
在store文件夹下新建index.js
文件,写入
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
//创建Vuex实例对象
const store = new Vuex.Store({
state:{
},
getters:{
},
mutations:{
},
actions:{
}
})
export default store;
然后再main.js
文件中引入Vuex
,这么写
import Vue from 'vue';
import App from './App.vue';
import store from './store';
const vm = new Vue({
store:store,
render: h => h(App)
}).$mount('#app')
7. Vuex的5个核心属性是什么??
展开查看
分别是
state
、getters
、mutations
、actions
、modules
。
8. Vuex中状态储存在哪里,怎么改变它?
展开查看
存储在
state
中,改变Vuex
中的状态的唯一途径就是显式地提交(commit) mutation
。
9. 简单介绍一下Vuex的state?
展开查看
在Vuex中,
State提供唯一的公共数据源
, 所有共享的数据都要统一放到State中进行存储, 这里的Store相当于存储数据的公共容器。
const store = new Vuex.Store({
state:{
count:0
}
})
组件的访问方式: <h3>当前值:{{$store.state.count}}</h3>
10. 如何在组件中批量使用Vuex的state状态?
展开查看
使用
mapState
辅助函数, 利用对象展开运算符将state混入当前组件的计computed
计算属性。
import{mapState} from 'vuex'
computed:{
...mapState(['count']])
}
<p>{{count}}<p>
或者使用别名的方式:
import{mapState} from 'vuex'
computed:{
...mapState({
counter: 'count'
})
}
<p>{{counter}}<p>
11. 简单介绍一个Vuex的getters?
展开查看
Vuex的getters可以认为是store的计算属性
,像计算属性一样,getter的返回值会根据它的依赖会被缓存
起来,且只有当它的依赖值发生变化才会重新计算。 getters可以接收2个参数,第一个参数是state
, 第二个参数是getters
.
getters: {
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
组件中使用的方式: <p>this.$store.getters.doneTodosCount</p>
12. 如何在组件中批量使用Vuex的getter属性?
展开查看
使用
mapGetters
辅助函数, 利用对象展开运算符将getter
混入computed 对象中.
import {mapGetters} from 'vuex'
export default{
computed:{
...mapGetters(['total','discountTotal'])
}
}
或者使用别名的方式:
import {mapGetters} from 'vuex'
export default{
computed:{
...mapGetters({
myTotal:'total',
myDiscountTotal:'discountTotal',
})
}
}
13. 简单介绍一下Vuex的mutations?
展开查看
更改Vuex中store中唯一的方法就是提交mutation,每个mutation都有一个事件类型(type)和回调函数(handler)。
只能通过mutation变更Store数据,不可以直接操作Store的数据。
mutation的接收的第一个参数是state:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
组件内页可以想mutation传参:
methods:{
btnHandeler(){
this.$store.commit('increment', 10)
}
}
//或者组件中对象风格的提交方式:
methods:{
btnHandeler(){
this.$store.commit({
type: 'increment',
count: 10
})
}
}
//vuex中
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state, payload) {
state.count += payload.count
}
}
})
mutation必须是同步函数。
14. 如何在组件中批量提交mutation?
展开查看
使用mapMutations
辅助函数,在组件中这么使用:
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
然后调用this.increment(10)
相当调用this.$store.commit('increment',10)
15. 简单介绍一下Vuex的actions?
展开查看
action和mutation类似, 区别:
action
提交的是mutation
,而不是直接修改state;- action可以包含任意
异步操作
;
如果通过异步操作
变更数据,必须通过Action
, 而不能直接使用Mutation
, 但是在Action
中还是要通过触发Mutation
的方式间接变更数据
。
Action的接收的第一个参数是与 store 实例具有相同方法和属性的 context 对象: 可以使用解构赋值: { state, rootState, commit, dispatch, getters, rootGetters } = context;
- state, // 等同于
store.state
,若在模块中则为局部状态 - rootState, // 等同于
store.state
,只存在于模块中 - commit, // 等同于
store.commit
- dispatch, // 等同于
store.dispatch
- getters, // 等同于
store.getters
- rootGetters // 等同于
store.getters
,只存在于模块中
因此你可以调用 context.commit
提交一个 mutation
,或者通过 context.state
和 context.getters
来获取 state
和 getters
。
actions: {
increment ({ commit }) {
commit('increment')
}
}
action页可以传参:
//store.dispatch('increment')
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
在store中:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state,payload) {
state.count += payload;
}
},
actions: {
incrementAsync (context, num) {
context.commit('increment',num)
}
//等同于
incrementAsync ({ commit }, num) {
context.commit('increment',num)
}
}
})
16. 如何在组件中批量触发actions?
展开查看
使用mapActions
辅助函数,在组件中这么使用:
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}
然后调用this.add(10)
相当调用this.$store.commit('add',10)
17. Action 通常是异步的,那么如何知道 action 什么时候结束呢?
展开查看
store.dispatch 可以处理被触发的 action 的处理函数返回的
Promise
,并且 store.dispatch 仍旧返回 Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
this.$store.dispatch('SET_NUMBER_A').then(() => {
// ...
})
18. 组合 Action的使用方法:
展开查看
比如有2个action分别是actionA和actionB,都是异步操作,actionB提交actionA,这种情况我们就可以使用组合action.
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
如果我们利用 async / await (opens new window)
,我们可以如下组合 action:
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
19. Vuex中action和mutation有什么区别?
展开查看
相同点:
-
action 提交的是
mutation
,而不是直接变更状态。mutation
可以直接变更状态。 -
action 可以包含任意异步操作。mutation只能是同步操作。
-
提交方式不同,action 是用
this.$store.dispatch('ACTION_NAME',data)
来提交, mutation是用this.$store.commit('SET_NUMBER',10)
来提交。 -
接收参数不同,mutation第一个参数是
state
,而action第一个参数是context
;
相同点:
第二参数都可以接收外部提交时传来的参数。 this.$store.dispatch('ACTION_NAME',data)
和this.$store.commit('SET_NUMBER',10)
20. 简单介绍以下modules?
展开查看
由于使用单一状态数,应用的所有状态会集中到一个比较大的对象,当应用相对复杂时, store对象就会变得相当臃肿。
为了解决这个问题, Vuex允许我们把store分割成模块(module),每个模块拥有自己的
state、mutation、action、getter
、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
模块的局部状态: 对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
那么如何获取根节点的状态呢?
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
const moduleA = {
// ...
getters: {
increment (state, getters, rootState) {
return state.count + rootState.count
}
}
}
对于模块内部的action,根节点状态会包含在context中:
{ state, rootState, commit, dispatch, getters, rootGetters } = context;
const moduleA = {
// ...
actions: {
asyncIncrement ({ state, commit, rootState }) {
commit('increment')
}
}
}
21.vuex中module的命名空间概念
展开查看
默认情况下,模块内部的state
, action
、mutation
和 getter
是注册在全局命名空间的。
-
弊端1:不同模块中有相同命名的
mutations
、actions
时,不同模块对同一mutation
或action
作出响应。 -
弊端2:当一个项目中store分了很多模块的时候,在使用辅助函数
mapState
、mapGetters
、mapMutations
、mapActions
时,很难查询,引用的state、getters、mutations、actions
来自于哪个模块,不便于后期维护。
可以通过添加 namespaced: true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
在模块中添加 namespaced: true
, 开始命名空间
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
}
}
})
组件中使用模块中定义的“permissionList” state:
// 1. 导入辅助函数mapState
import { mapState } from "vuex";
export default {
props: {
data: {
type: Object,
default: "chart"
}
},
data() {
return {
list: {}
};
},
computed: {
//2. 在辅助函数mapState的第一参数上,填写上模块的命名空间名。根据挂载方式不同,此处的命名空间名就是 wechatType 或 aaa。
...mapState('命名空间名', ["permissionList"])
},
第二种方式:
//通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数。
//它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数
import { createNamespacedHelpers } from "vuex";
const { mapState } = createNamespacedHelpers('命名空间名')
export default {
computed: {
...mapState(["permissionList"])
}
}
参考文档: Module
22.vue 中 ajax 请求代码应该写在组件的methods中还是vuex 的action中
展开查看
-
如果请求来的数据不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入
vuex
的state
里; -
如果被其他地方复用,请将请求放入
action
里,方便复用,并包装成promise
返回;
23.vuex如何开启严格模式?有什么作用?
展开查看
开启严格模式,仅需在创建 store
的时候传入 strict: true
:
const store = new Vuex.Store({
// ...
strict: true
})
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
不要在发布环境下启用严格模式 , 严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。
五、Vue原理方面总结
1. 简单说一下Vue2.0响应式数据原理?
展开查看
在介绍 vue2 响应式原理之前,我先介绍一下 Object.defineProperty
Object.defineProperty
: 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
语法
: Object.defineProperty(obj, prop, descriptor)
参数
:
- obj:要定义属性的对象。
- prop:要定义或修改的属性的名称或 Symbol 。
- descriptor:要定义或修改的属性描述符。
返回值
:被传递给函数的对象, 即第一个参数obj
。
vue2响应式原理主要做了这么3件事:数据劫持
、收集依赖
、派发更新
;
-
数据劫持: new Vue的时候遍历data对象,用
Object.defineProperty
给所有属性加上了getter
和setter
; -
依赖的收集: render的过程,
new Dep()依赖收集类
会触发数据的getter,在getter的时候把当前的watcher对象收集起来; -
派发更新: setter的时候,遍历这个数据的依赖对象(
watcher对象
),进行更新
。
将上面3个步骤整合为一段话:👀👀
当创建Vue实例时,vue会遍历data
选项的属性,利用Object.defineProperty
为属性添加getter
和setter
,对数据的读取进行劫持(getter用来依赖收集
,setter用来派发更新
),并且在内部追踪依赖
,在属性被访问和修改时通知变化
。
(1)缺点:
1.1 无法监听到对象属性
的动态添加和删除;
1.2 无法监听到数组下标和length长度的变化; 1.3 需要递归,消耗大;
(2)如何解决这个问题?
使用了函数劫持
的方法,重写了数组的方法
,Vue将data中的数组进行了原型链重写
,指向了自己定义的数组原型方法
。
2. 简单说一下Vue3.x响应式数据原理?
展开查看
在介绍vue 3.x响应式原理之前,先介绍一下 proxy
Proxy
对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
const p = new Proxy(target, handler);
Proxy对象的所有用法,都是上面这种形式,不同的只是handle参数的写法。其中new Proxy用来生成Proxy实例,target是表示所要拦截的对象,handle是用来定制拦截行为的对象。
const target = []
const proxy = new Proxy(target, {
get: (obj, prop) => {
console.log('设置 get 操作')
return obj[prop];
},
set: (obj, prop, value) => {
console.log('set 操作')
obj[prop] = value;
return true
}
});
proxy.push(1) // 设置 get 操作*2 set 操作*2
proxy[0] // 设置 get 操作
当给目标对象进行赋值或获取属性时,就会分别触发get和set方法,get和set就是我们设置的代理,覆盖了默认的赋值或获取行为。 当然,除了get和set,Proxy还可以拦截其他共计13种操作.
Proxy可以在目标对象上加一层拦截/代理
,外界目标对象的操作,都会经过这层拦截
。相比 Object.defineProperty ,Proxy支持的对象操作十分全面:get、set、has、deleteProperty、ownKeys、defineProperty
......等等
Vue3.x
改用Proxy
替代Object.defineProperty
。因为Proxy
可以直接监听对象和数组的变化,并且有多达13种拦截方法。
(1) Proxy只会代理对象的第一层,那么Vue3.x是如何解决这个问题呢?
判断当前Relect.get
的返回值是否为Object
,如果是则通过reactive方
法做代理,这样就实现了深度观测。
(2) 数据检测的时候可能触发多次get/set,那么如何防止多次触发呢?
我们可以判断key
是否为当前被代理对象target
自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger.
3. vue中是如何检测数组变化的呢
展开查看
vue2:
数组就是使用 object.defineProperty
重新定义数组的每一项,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样实现了监测数组变化。
vue3: 改用 proxy ,可直接监听对象数组的变化。
4. Vue2.x和Vue3.x渲染的diff算法分别说一下
展开查看
Vue2的核心Diff算法采用了双端比较
的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。相比React的Diff算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅.
Vue3.x借鉴了 ivi算法
和 inferno算法
。在创建VNode时就确定其类型,以及在mount/patch的过程中采用位运算来判断一个VNode
的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。
5. Vue模板编译原理说一下
展开查看
简单说,Vue的编译过程就是将template转化为render函数的过程。
会经历以下阶段:
(1)生成AST树 首先解析模板,生成AST语法树(一种用JS对象的形式来描述整个模板)。使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。
(2)优化 Vue的数据时响应式的,但是其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会在变化,对应的DOM节点也不会变化。那么我们优化过程中就是深度遍历AST树,按照相关条件对树节点进行标记。这些被标记的节点我们就可以跳过对他们的对比,对运行时的模板起到很大的优化作用。
(3)codegen 将优化后的AST树转化为可执行的代码
6. 虚拟DOM实现原理
展开查看
虚拟DOM的实现原理主要包括一下3部分:
(1)用Javascript对象模拟真实的DOM树
, 对真实的DOM树进行抽象;
(2)diff算法——比较两颗虚拟DOM树的差异
;
(3)pach算法——将两个虚拟DOM对象的差异应用到真实的DOM树
;
7. V-mode的原理
展开查看
我们在Vue项目中主要使用v-model
指令在表单input
、textarea
、select
等元素上创建双向数据绑定
,我们知道v-model本质上不过是语法糖
,v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件:
(1)text
和textarea
使用value
属性和input
事件;
(2)checkbox
和radio
使用checked
属性和change
事件;
(3)select字段
将value
作为prop
并将change
作为事件。
以input表单元素为例:
<input v-model="something">
相当于
<input v-bind:value="something" v-on:input="something=$event.target.value"/>
8. 谈谈Vue虚拟DOM(Virtual DOM)
展开查看
参考文档:www.pianshen.com/article/438… Vue实例挂在的最后一个环节:渲染DOM节点。
在渲染真实DOM的过程中,Vue引进了虚拟DOM的概念,虚拟DOM作为JS对象和真实DOM中间的一个缓冲存,极大的优化了JS频繁操作DOM的性能问题。
浏览器的渲染流程:
当浏览器接收到一个Html文件时,JS引擎和浏览器的渲染引擎便开始工作。从渲染引擎的角度,它首先将html文件解析成一个DOM树,与此同时,浏览器将识别并加载CSS样式,并和DOM树一起合并成一个渲染树,有了渲染树后,渲染引擎将计算所有元素的位置信息,最后通过绘制,在屏幕上显示最终的内容。而JS引擎的作用是通过DOM相关的API去操作DOM对象,我们操作DOM时,很容易触发到渲染引擎的回流和重绘。
简单描述为:
1.构建DOM树和CSSOM树(构建DOM树、构建CSSOM树、加载JS);
2.构建渲染树;
3.页面的重绘与重排(回流)
**回流(重排):**当我们对DOM的修改引发了元素尺寸的变化时,浏览器需要重新计算元素的大小和位置,最后将重新计算的结果绘制出来,这个过程称为回流。
重绘: 当我们对DOM的修改只是单纯的改变元素的颜色时,浏览器此时不需要重新计算元素的大小和位置,而只是要重新绘制新样式,这个过程我们称为重绘。重排必将引起重绘,但是重绘不一定引起重排; 很显然回流比重绘更加消耗性能。 通过浏览器的渲染机制,我们知道,当不断的通过JS修改DOM时,会触发渲染引擎的回流或者重绘,而这个操作的性能开销非常大。为了降低开销,我们尽可能减少DOM操作。
缓冲层-虚拟DOM
虚拟DOM是优化频繁操作DOM引发性能问题的产物,虚拟DOM是将页面的状态抽象为JS对象的形式,本质上是JS和真实DOM的中间层,当我们想用JS脚本大批量进行DOM操作时,会优先作用于虚拟DOM这个JS对象,最后通过diff算法进行对比,将要改动的部分通知并更新到真实的DOM。尽管最终操作的还是真实的DOM,但是虚拟DOM可以将多个改动合并成一个批量的操作,从而减少DOM的重排次数,进而缩短了生成渲染树和重绘所花的时间。
9. Vue.use() 的使用和基本原理
展开查看
Vue.use()是一个全局注册一个组件或者插件的方法。
每次注册前,都会判断一下这个组件或者插件是否注册过,如果注册过,就不会再次注册了。
Vue.use() 的参数必须时一个function函数
或者一个Object对象
,如果是对象的话,必须在对象中提供一个install方法。之后会将Vue作为参数传入。
也就是说:
-
参数为
函数
时,函数的参数是Vue对象
; -
参数为
对象
时, 它提供的install
方法中参数就是Vue对象
10. Vue双向数据绑定原理
展开查看
单项绑定: 指的是Model(模型)更新时,View(视图)会自动更新;
双向绑定: 指的是Model更新时View(视图)会自动更新,反过来View(视图)更新时Model的数据也会自动更新。
Object.defineProperty(obj, prop, desc) 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
-
obj: 要定义属性的对象;
-
prop: 要定义或修改的属性的名称;
-
desc: 要定义或修改的属性的描述符;
举例:
const obj = {};
Object.defineProperty(obj, 'address', {
value: '宁夏回族自治区',
writable: true //只有该属性的值为true,属性的值才能被修改
})
console.log(obj); //{address: "宁夏回族自治区"}
obj.address = '陕西省西安市'
console.log(obj); //{address: "陕西省西安市"}
Object.defineProperty的几个要点:
(1)读取或设置访问器属性的值,试即上是调用其内部特性:get和set函数;
(2)get和set方法内部的this都指向obj,这意味着其可以操作对象内部的值;
(3)访问器属性会“覆盖”同名的普通属性,因为访问器会被优先访问,与其同名的普通属性则会被忽视。
实现一个简单的数据双向绑定
:
<body>
<input type="text" id="inputValue">
<span id="spanValue"></span>
<script>
const obj = {};
Object.defineProperty(obj, 'attr',{
set(newVal){
document.getElementById('inputValue').value = newVal;
document.getElementById('spanValue').innerHTML = newVal;
}
})
document.addEventListener('keyup', function(e){
obj.attr = e.target.value;
})
</script>
</body>
结论: Vue的双向绑定采用数据劫持结合发布-订阅模式
实现,数据劫持即使用Object.defineProperty
把传入的data选项(一个Javascript对象)的属性转换为getter / setter
, 发布-订阅即模板解析过程中,与渲染相关的数据属性会监听到相应的Watcher
,该属性的setter
触发时就会通知对应的Watcher
更新视图。
11. vuex和redux比较,他们的相同点和不同点
展开查看
相同点:
-
都是通过store来作为全局状态存储对象;
-
改变store的直接方法(vuex中的mutation和redux的reducer)只允许同步操作;
不同点:
-
vuex只有展示组件(通过全局根部植入直接访问store),而redux中展示组件通过容器组件连接store再进行访问;
-
vuex中消除了action的概念;
-
vuex中的异步操作只能再action中进行,而redux中没有特别的为异步操作创建一个方法;
-
Vuex是吸收了Redux的经验,放弃了一些特性并做了一些优化,代价就是只能和Vuex配合,而Redux则是一个纯粹的状态管理系统,React利用React-redux将它与React框架结合起来;
12. 为什么Vue采用异步渲染呢?
展开查看
Vue 是组件级更新
,如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能, Vue 会在本轮数据更新后,在异步更新视图。核心思想 nextTick
。
dep.notify()
通知 watcher
进行更新, subs[i].update
依次调用 watcher
的 update
, queueWatcher
将watcher
去重放入队列, nextTick( flushSchedulerQueue )
在下一tick中刷新watche
r队列(异步)。
13. 什么是发布/订阅者、观察者模式?
展开查看
1.1 观察者模式:
观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。
它是由两类对象组成:Subject主题
+ Observer观察者
。主题发布事件,观察者通过订阅事件观察主题。
观察者模式提供给关联对象一种同步通信的手段,使得主题和观察者之间保持同步通信,主题和观察者之间存在依赖关系,存在耦合
。
2.发布-订阅模式:
订阅者
(Subscriber)把自己想订阅的事件注册(Subscribe
)到调度中心(Topic),当发布者(Publisher
)发布该事件(Publish topic
)到调度中心,也就是该事件触发时,由调度中心统一调度
(Fire Event)订阅者注册到调度中心的处理代码。
完全解耦
,发布者和订阅者彼此不知道对方的存在,二者共享一个自定义事件的名称(事件调度中心
)。它的优点非常明显,一为时间上的解耦
,二为对象之间的解耦
。
3.区别:
观察者模式种,目标对象就是主题管理者,发布-订阅模式种多了一个中间层通道
。
观察者和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心
。
14. axios主要有哪些特性?
展开查看
14.1 axios是什么?
1. 前端最流行的ajax请求库;
2. react/vue官方都推介使用axios发ajax请求
14.2 axios的特点
1. 基于promise的异步ajax请求库
2. 浏览器端/node端都可以使用
3. 支持请求/相应拦截器
4. 支持请求取消
5. 请求/响应数据转换
6. 批量发多个请求
14.3 axios常用语法
axios(config):通用/最本质的发任意类型请求的fs
aios(url[, config]):可以指定url发get请求
axios.request(config):等同于axios(config)
axios.get(url[, config]):发get请求
axios.delete(url[, config]):发delete请求
axios.post(url[, data, config]):发post请求
axios.put(url[, data, config]):发put请求
axios.defaults.xxx:请求的默认全局配置
axios.interceptors.request.use():添加请求拦截器
axios.interceptors.response.use():添加响应拦截器
axios.create([config]):创建一个新的axios(它没有下面的功能)
axios.Cancle():用于创建取消请求的错误对象
axios.CancleToken():用于创建取消请求的token对象
axios.isCancle():是否是一个取消请求的错误
axios.all(promise):用于批量执行多个异步请求
axios.spread():用来指定接收所有成功数据的回调函数的方法
其他Vue系列面试题
如果你看到这里的话,麻烦动动你的大拇,给个❤️关注,👍👍点赞,❤️鼓励一下小编。