vue前端面试
1、vue的生命周期函数
2、vue MVVM模式
> MVVM介绍:
MVVM分为三个部分:分别是M(Model,模型层 ),V(View,视图层),VM(ViewModel,V与M连接的桥梁,也可以看作为控制器)
1、 M:模型层,主要负责业务数据相关;
2、 V:视图层,顾名思义,负责视图相关,细分下来就是html+css层;
3、 VM:V与M沟通的桥梁,负责监听M或者V的修改,是实现MVVM双向绑定的要点;
MVVM支持双向绑定,意思就是当M层数据进行修改时,VM层会监测到变化,并且通知V层进行相应的修改,反之修改V层则会通知M层数据进行修改,以此也实现了视图与模型层的相互解耦;
3、<keep-alive></keep-alive>的作用
<keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
prop:
include: 字符串或正则表达式。只有匹配的组件会被缓存。
exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存。
常见用法:
export default {
name: 'test-keep-alive',
data () {
return {
includedComponents: "test-keep-alive"
}
}
}
<keep-alive include="test-keep-alive">
<!-- 将缓存name为test-keep-alive的组件 -->
<component></component>
</keep-alive>
<keep-alive include="a,b">
<!-- 将缓存name为a或者b的组件,结合动态组件使用 -->
<component :is="view"></component>
</keep-alive>
<!-- 使用正则表达式,需使用v-bind -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 动态判断 -->
<keep-alive :include="includedComponents">
<router-view></router-view>
</keep-alive>
<keep-alive exclude="test-keep-alive">
<!-- 将不缓存name为test-keep-alive的组件 -->
<component></component>
</keep-alive>
结合router,缓存部分页面
使用$route.meta的keepAlive属性:
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
需要在router中设置router的元信息meta:
routes: [
{
path: '/',
name: 'Hello',
component: Hello,
meta: {
keepAlive: false // 不需要缓存
}
},
{
path: '/page1',
name: 'Page1',
component: Page1,
meta: {
keepAlive: true // 需要被缓存
}
}
]
})
keep-alive生命周期钩子函数:activated、deactivated
使用<keep-alive>会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在activated阶段获取数据,承担原来created钩子中获取数据的任务。
案例:首页是A页面;B页面跳转到A,A页面需要缓存;C页面跳转到A,A页面不需要被缓存
A的路由
{
path: '/',
name: 'A',
component: A,
meta: {
keepAlive: true // 需要被缓存
}
}
B组件
export default {
data() {
return {};
},
methods: {},
beforeRouteLeave(to, from, next) {
// 设置下一个路由的 meta
to.meta.keepAlive = true; // B 跳转到 A 时,让 A 缓存,即不刷新
next();
}
};
C组件
export default {
data() {
return {};
},
methods: {},
beforeRouteLeave(to, from, next) {
// 设置下一个路由的 meta
to.meta.keepAlive = false; // C 跳转到 A 时让 A 不缓存,即刷新
next();
}
};
4、Vue.js引入组件的步骤
1.可以作用到Vue.component 这个全局注册方法里, 可以在任意vue模板里使用<apple>组件
var apple = Vue.extend({
....
})
Vue.component('apple',apple)
2.可以作用到vue实例或者某个组件中的components属性中并在内部使用apple组件
new Vue({
components:{
apple:apple
}
})
5、Vue.js使用插件的步骤
vue-cli脚手架中:npm安装插件,然后在main.js里import引入,Vue.use(插件名)
6、Vue-router有哪几种导航钩子
1.全局导航钩子
beforeEach
beforeResolve
afterEach
const router = new VueRouter({
mode: 'history', // 路由使用history模式
routes,
linkActiveClass: 'active', // 设置当前路由的className });
// 全局导航钩子 直接挂载在router实例上的
router.beforeEach((to, from, next) => {
document.title = to.meta.title || 'demo'
if (!to.query.url && from.query.url) {
to.query.url = from.query.url
} next()
})
router.afterEach(route => { })
2.某个路由独享的导航钩子
beforeEnter
{ path: '/',
component: App,
children: [ {
path: 'apply',
name: 'apply',
component: Apply,
children: [ {
path: 'launch',
name: 'apply-launch-list',
component: applyList,
// 直接在router的路由配置中使用
beforeEnter (to, from, next) {
// 如果isBack为true时,证明是用户点击了回退,执行slide-right动画
let isBack = this.$store.state.transferBack;
if (isBack) {
this.transitionName = 'slide-right';
} else {
this.transitionName = 'slide-left';
}
// 做完回退动画后,要设置成前进动画,否则下次打开页面动画将还是回退
this.$router.isBack = false;
next()
}
},
{
path: 'draft',
name: 'apply-draft-list',
component: draftList
} ]
} ]
}
3.路由组件上的导航钩子
beforeRouteEnter
beforeRouteUpdate (2.2 新增)
beforeRouteLeave
// 定义一个vue模板,这个模板被router的配置文件的component使用
const Qux = {
data () {
return {
msg: null
}
},
template: `<div>{{ msg }}</div>`,
// 路由组件上的导航钩子beforeRouteEnter
beforeRouteEnter (to, from, next) {
setTimeout(() => {
next(vm => { vm.msg = 'Qux' })
}, 300) }
}
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/', component: Home },
// 某个路由独享的导航钩子 beforeEnter
{ path: '/foo', component: Foo, beforeEnter: guardRoute },
{ path: '/bar', component: Bar, meta: { needGuard: true }},
// 在这里使用Qux
{ path: '/qux', component: Qux },
{ path: '/qux-async', component: resolve => {
setTimeout(() => {
resolve(Qux)
}, 0)
}
},
{ path: '/quux/:id', component: Quux }
]
})
7、vue的data为什么要return返回
在简单的vue实例中看到的Vue实例中data属性是如下方式展示的:
let app= newVue({
el:"#app",
data:{
msg:''
},
methods:{
}
})
在使用组件化的项目中使用的是如下形式:
export default{
data(){
return {
}
},
methods:{
}
}
不使用return包裹的数据会在项目的全局可见,会造成变量污染 ,使用return包裹后数据中变量只在当前组件中生效,不会影响其他组件。
在创建或注册模板的时候,传入一个data属性作为用来绑定的数据。但是在组件中,data必须是一个函数,而不能直接把一个对象赋值给它。
从js原型链理解:
var MyComponent = function() {}
MyComponent.prototype.data = {
a: 1,
b: 2,
}
// 上面是一个虚拟的组件构造器,真实的组件构造器方法很多
var component1 = new MyComponent()
var component2 = new MyComponent()
// 上面实例化出来两个组件实例,也就是通过<my-component>调用,创建的两个实例
component1.data.a === component2.data.a // true
component1.data.b = 5
component2.data.b != 5
如果两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改,所以两个实例应该各有各的域。这都是因为js本身的特性带来的,跟vue本身设计无关
var MyComponent = function() {
this.data = this.data()
}
MyComponent.prototype.data = function() {
return {
a: 1,
b: 2,
}
}
8、vuex
Vuex采用和Redux类似的单向数据流的方式来管理数据。用户界面负责触发动作(Action)进而改变对应状态(State),从而反映到视图(View)上。
State负责存储整个应用的状态数据,一般需要在使用的时候在跟节点注入store对象,后期就可以使用this.$store.state直接获取状态,当然,也可以利用vuex提供的mapState辅助函数将state映射到计算属性中去
Mutations的中文意思是“变化”,利用它可以更改状态,本质就是用来处理数据的函数,其接收唯一参数值state。store.commit(mutationName)是用来触发一个mutation的方法。需要记住的是,定义的mutation必须是同步函数,否则devtool中的数据将可能出现问题,使状态改变变得难以跟踪。或者使用辅助函数mapMutations直接将触发函数映射到methods上
Actions也可以用于改变状态,不过是通过触发mutation实现的,重要的是可以包含异步操作。其辅助函数是mapActions与mapMutations类似,也是绑定在组件的methods上的。如果选择直接触发的话,使用this.$store.dispatch(actionName)方法
有些状态需要做二次处理,就可以使用getters。通过this.$store.getters.valueName对派生出来的状态进行访问。或者直接使用辅助函数mapGetters将其映射到本地计算属性中去。
Plugins插件就是一个钩子函数,在初始化store的时候引入即可。比较常用的是内置的logger插件,用于作为调试使用。
9、vue常用指令
v-if//v-show//v-else//v-for//v-bind//v-on
10、vue的组件继承
11、vue组件之间的通信方式
12、vue的底层原理
13、实现Vue SSR
14、另一种方式实现Vue的响应式原理
15实现数据双向绑定的做法
①发布者-订阅者模式:
一般通过sub, pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set('property', value),
②脏值检查:
angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,最简单的方式就是通过 setInterval() 定时轮询检测数据变动,angular只有在指定的事件触发时进入脏值检测:
DOM事件,譬如用户输入文本,点击按钮等。( ng-click )
XHR响应事件 ( $http )
浏览器Location变更事件 ( $location )
Timer事件( $timeout , $interval )
执行 $digest() 或 $apply()
③数据劫持:
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
16、vue父子组件通信以及非父子组件通信
①父传子
父组件:
<parent>
<child :child-msg="msg"></child> //这里必须要用 - 代替驼峰
</parent>
data(){
return {
msg: [1,2,3]
};
}
子组件:
//方式一
props: ['childMsg']
//方式二
props: {
childMsg: Array //这样可以指定传入的类型,如果类型不对,会警告
}
//方式三
props: {
childMsg: {
type: Array,
default: [0,0,0] //这样可以指定默认的值
}
}
②子传父(vue只允许单向数据传递,我们可以通过触发事件来通知父组件改变数据,从而达到改变子组件数据的目的.)
子组件:
<template>
<div @click="up"></div>
</template>
methods: {
up() {
this.$emit('upup','hehe'); //主动触发upup方法,'hehe'为向父组件传递的数据
}
}
父组件:
<div>
<child @upup="change" :msg="msg"></child> //监听子组件触发的upup事件,然后调用change方法
</div>
methods: {
change(msg) {
this.msg = msg;
}
}
③非父子组件
var bus = new Vue()
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
// 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
// ...
})
event bus 只适于某些不复杂的场景,在需要频繁进行组件通信的情况下,还是应该尽量使用 Vuex ,不仅使用上更加简单,同时数据流的流向也会相对清晰。