原文地址:vue基本面试题
1、vue双向数据绑定?
vue数据双向绑定是通过 数据劫持 结合 发布者-订阅者 模式的方式来实现的。
利用数据监听器 Observer 其实也就是 Object.defineProperty() 并通过该方法 劫持各个属性的setter,getter,在数据变动时发布消息给订阅者watcher来触发相应的监听回调。
具体步骤:
第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
引用了:https://www.cnblogs.com/sichaoyun/p/8406194.html
2、解释一下vuex?
vuex是用来做状态管理的,具有五个常用属性state, getter, actions, mutations, modules。state是数据源,类似vue中的data,我们可以通过两种方式来获取它,mapStates, mapGerters。并且获取state必须放到computed中这样能保证state发生改变的时候该组件中用到state的地方都发生变化。
3、使用vue遇到的问题?
1、通过js修改data中的对象数据页面并没有重新渲染,因为vue并没有对object类型对象进行深层监听,所以使用this.$set()方法来修改object类型的对象。
2、打包发布路径问题:打包的基础路径设置一定要和服务器上的相同,否则打包好后放服务器会访问不到资源。是在config/index.js中设置build对象里设置assetsPublicPath:'/路径/'。
3、发版缓存问题,由于发版是全量发版,所以如果用户浏览器有缓存的话,就会访问不到旧资源,导致无法正常查看,那么可以设置不允许index.html缓存,这样就能保证每次用户访问的都是正确的。做法是在vue发布项目根目录建立manifest文件夹里面建一个cache.manifest文件, 里面内容如下
CACHE MANIFEST
#version 1.0
#直接缓存的文件
CACHE:
#需要实时在线的文件
NETWORK:
../index.html
#替代方案
HTML页面中设置meta熟悉
4、路由跳转回退,滚动到浏览器上次访问位址。
使用vue2 中的keep-alive缓存组件 在app.vue中如下写法:
<keep-alive>
<router-view v-if='$route.meta.keepAlive'></router-view>
</keep-alive>
<router-view v-if='!$route.meta.keepAlive'></router-view>
router/index文件下
{
path: '/user',
name: 'User',
component: userComp,
meta: {
isUseCache: false,// 结合下面activated钩子函数中的判断来确定是否去刷新数据
keepAlive: true
}
}
设置了keepAlive缓存的组件的钩子函数:
第一次进入: beforeRouterEnter -> created -> activated -> ... -> deactivated
第二次进入: beforeRouterEnter -> activated -> ... -> deactivated
只有第一次进入的时候,有created钩子,那么所有的判断可以在activated中写,
这里判断是否需要重新获取数据,isUseCache默认是false,第一次进入的时候加载数据,然后在离开这个页面的时候,设置为true再次进入这个页面就可以使用缓存。
userlist页面这么写
activated() {
if(!this.$route.meta.isUseCache) {
this.list = []
this.loadData()
}
}
userDetail页面这样写
beforeRouteLeave(to, from, next) {
if(to.name == 'User') {
to.meta.isUseCache = true
}
next()
5、vue父子组件之间传值?
父组件向子组件传值可以通过props来实现:
父组件在引用子组件的时候,可以添加自定义属性如:
<child-temp :data-name='cName' :data-sex='cSex'></child-temp>
子组件接收:
props: ['cName', 'cSex']
或者用:
this.$attrs('data-name'), this.$attrs('data-sex')
子组件向父组件传值可以通过emit来实现:
子组件中使用:
this.$emit('changeName', params)
父组件接收:
<child-temp @changeName='change'></child-temp>
methods: {
change(params) {
this.name = params.name
}
}
或者
mounted() {
this.$on('changeName', function(params) {
this.name = params.name
})
}
6、vue-router传参和接收参数?
vue-router 传参方式有两种
首先在路由里面配置路由
{
path: '/user',
name: 'user',
component: User
}
跳转的时候可以这样:
this.$router.push({
path: '/user',
query: {
id: '001',
name: 'lsj'
}
})
接收参数:this.$route.query.id,this.$route.query.name
this.$router.push({
name: 'user',
params: {
id: '001',
name: 'lsj'
}
})
接收参数:this.$route.params.id, this.$route.params.name
上面的传参形式也可以直接使用
<router-link :to='{name: "user", params: {id: "001", name: "lsj"}}'></router-link>
<router-link :to='{path: "/user", query: {id: "001", name: "lsj"}}'></router-link>
7、vue-router的嵌套路由是怎么实现的?
答:是通过children实现的方式如下:
{
path: '/home',
name: 'home',
component: HomePage,
children: [
{ path: '/home/login', name: 'login', component: Login},
{ path: '/home/reg', name: 'reg', component: Reg}
]
}
8、导航钩子有哪些?
beforeRouteEnter、afterEnter、beforeRouterUpdate、beforeRouteLeave
参数: to, from, next
to(去的那个路由)、from(离开的路由)、next(一定要用这个函数才能去到下一个路由,如果不用就拦截)最常用就这几种
9、请详细说下你对vue生命周期的理解?
答:总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然
10、vue事件绑定的几种情况
可以看vue官网关于修饰符的介绍
https://cn.vuejs.org/v2/guide/events.html?#%E4%BA%8B%E4%BB%B6%E4%BF%AE%E9%A5%B0%E7%AC%A6
<!-- 阻止事件冒泡 -->
<a @click.stop='doSomething'></a>
<!-- form提交不重新渲染页面 -->
<form @submit.prevent='doSomething'></form>
<!-- 修饰符的串联使用 -->
<a @click.stop.prevent='doSomething'></a>
<!-- 添加事件监听器时使用事件捕获模式 -->
<div @click.capture='doSomething'></div>
<!-- 只在当前元素上触发事件,【而不是子元素】 -->
<div @click.self='doSomething'></div>
11、vue 获取dom元素,获取当前点击元素的父元素
可以通过 this.$refs 来直接获取需要获取的元素,效果类似于dom结构上的id
下面是具体使用方法:
<li class="sidebar-list" v-for="(item, index) in meunList" @click="setPageMenu(index)" ref="menuItem">
<router-link class="sidebar-a" :to="{ path: item.listLink }" >{{item.listTitle}}</router-link>
</li>
setPageMenu(index) {
//这个是获取当前menuItem值,用index来区分当前元素是v-for 产生的数组中的第几个数
let getMenuText = this.$refs.menuItem[index].innerText;
}
<div @click.self='doSomething($event)'></div>
methods: {
doSomething(event) {
const ClictEl = event.target // 是你当前点击的元素
const El = event.currentTarget // 是你绑定事件的元素
const ParentEl = event.currentTarget.parentNode // 当前元素的父元素
const pName = ParentEl.getAttribute('pName')
}
}