二、Vue试题分析
- v-for可以实现数据遍历显示,不仅可以遍历数组,也可以遍历对象,还可以从数值中取值:
- v-for='n in 10'
- n会打印1-10
- vue的生命周期钩子:
- 通用:
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
- 路由守卫:
- beforeRouteEnter
- beforeRouteUpdate (2.2 新增)
- beforeRouteLeave
- keep-alive:
- activated
- deactivated
- 通用:
- v-if v-show
- v-if是真正的条件渲染,会确保在切换中条件块内的事件监听、子组件都会适当的被销毁和重建
- v-show总是将节点渲染在dom中,只是基于css:display来控制节点的显示和隐藏
- v-if有更高的切换开始,v-show有更高的初始渲染开销
- v-if是惰性的,初始条件为假,就不会渲染
- axios相关
- axios请求的时候不会带上cookie,不会影响带宽,可以通过withCredentials:true来设置
- 对axios 的请求头进行设置:
- axios.defaults.headers = {'Content-Type':'...'}
- vue2.0不在更新维护vue-resource,官方推荐使用axios
- axios拦截器可以拦截请求和响应,在then、catch之前拦截
- 组件实例的作用域是孤立的,意味着不能(不应该)在子组件模板里直接引用父组件的数据,要让子组件使用父组件数据的话,需要通过props来将父组件的数据传递给子组件,子组件不能也不应该修改父组件传入的数据,但是可以通过传入引用类型的数据来实现数据共享
- 为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发 (即 Angular 用户熟知的“transclusion”)。Vue.js 实现了一个内容分发 API,参照了当前 Web Components 规范草案,使用特殊的
<slot>元素作为原始内容的插槽。
a-template:<p>hello world</p> <b> <h1>hello world</h1> </b>b-template:<slot></slot>
....
8. 如果把切换出去的组件保存在内存中,保留状态避免重新渲染,可以使用keep-alive
- include exclude
9. 注册方式:
- 全局:Vue.component(name,Vue.extend({}))
- 局部:{ components:{name:Vue.extend({})} }
10. 事件总线实现非父子组件通信
//创建bus
let bus = new Vue()
//a
new Vue({
template:'...',
mounted(){
bus.$on('emit-a',function(){
alert(1)
})
}
})
//b
new Vue({
template:'...',
methods:{
emitA(){
bus.$emit('emit-a')
}
}
})
//当b组件的emitA方法被调用的时候,A组件就会执行alert(1)
- methods和计算属性的区别
- 假设我们有一个数据为num,还希望拥有一个数据为doublenum,而且希望doublenum的值永远都是num的二倍
- 方法:
- 因为是直接显示在模板中,也就是说,我们可以来一个doublenum的方法,这个方法返回num的二倍,将这个方法放到模板中的某个地方执行
{{doublenum()}}但是,当无关的例如一个str的数据更改的时候,组件会重新创建虚拟dom树,与上一次的虚拟dom树对比之后重新渲染,这个时候在重新渲染模板的时候doublenum函数会被再次的调用,造成不必要的性能浪费 - 创建一个doublenum数据,使其初始值为num的二倍,然后利用watch来监听这两个数据,在改变的时候更改对应的数据,但是需要初始的为doublenum赋值为num的二倍,如果num是动态获取到的,doublenun赋值会更繁琐
- computed计算数据,我们可以利用computed来创建一条新的doublenum数据。并且设置它的getter和setter,并与num建立关系,且computed会缓存,在重新渲染的时候,不会重新执行getter和setter
computed:{ doublenum:{ get(){ return this.num*2 }, set(val){ this.num = val/2 } } } - 因为是直接显示在模板中,也就是说,我们可以来一个doublenum的方法,这个方法返回num的二倍,将这个方法放到模板中的某个地方执行
- 绑定class的对象语法和数组语法
<a :class="{a:true,b:false,c:1}"> => </a> => <a class='a c'></a>
data(){
return {
c:'c'
}
}
<a :class = '["a","b",c]'></a> => </a> => <a class='a b c'></a>
new Vue({
el:"#example-3",
methods:{
say(str){
alert(str)
}
}
})
- 单向数据流
- prop是单向绑定的,父组件属性变化,传递给子组件,但是,子组件数据变化,不能直接传递给父组件,也就是数据的流行是从父组件流向子组件的,为了防止子组件修改父组件的数据(会让应用的数据流变的更难开发、更新、维护)
- 使用了vuex工具的时候,store中数据在组件中使用的过程也是单向数据流,state->vue component->actions->mutations->state->vue component
this.$router.push/replace({name:'user',params:{userId:1})
this.$router.push/replace({path:'/register',query:{plan:private})
key相关
- 当数据改变之后,vue会创建新的虚拟dom来和原来的虚拟dom做对比,在创建新的虚拟的dom的时候,会根据key来查找在原来的虚拟dom中有没有某个部分,如果原来的有,这次的也需要,就会实现复用,而且在做diff对比的时候,如果有key会加快对比的查找速度,提高性能
- 尽量循环的时候不要将key设置为数组的索引,因为当删除某一个元素的时候,就会导致删除位置下面的所有元素的key值都与上一次虚拟dom的key值不同,导致复用失败,这个时候我们最好使用关键的唯一的,例如id这样的数据作为key
- 如果数据变化只是值的变化而不是条数和位置的变化,可以使用索引作为key
Vue.use()
- Vue.use会查找插件对象里的install方法去执行,并且给install方法里传入Vue对象
var a = {
install(Vue){
Vue.component("my-a",{...})
}
}
Vue.use(a)
进入域后根据不同的情况显示不同的页面(PC/MOBILE)
- 很多情况下,一个应用会有PC和移动端两个版本,而这两个版本因为差别大,内容多,所以不能用响应式开发但是单独开发,而域名只有一个,用户进入域后直接返回对应设备的应用,做法主要有两种:
- 前端判断并跳转 进入一个应用或者一个空白页面后,通过navigator.userAgent来判断用户访问的设备类型,进行跳转
- 后端判断并响应对应的应用 用户地址栏进入域的时候,服务器能接收到请求头上包含的userAgent信息,判断之后返回对应应用
function foo(){// 第16行
getName = function(){console.log(1)}
return this
}
foo.getName = function(){console.log(2)}
foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){console.log(5)}
foo.getName()//2
//foo是一个函数,也可以说是一个对象,所以它也可以挂载一些属性和方法,18行在其上挂载了一个getName方法
//执行的结果是
getName()//4
//21行有一个全局函数,全局函数声明提前后被20行的getName覆盖,所以输出4
foo().getName()//1
//foo()执行完成后,将全局的getName也就是window.getName给更改后返回this,而在这里this执行的就是window,所以最后执行的就是window.getName,所以输出1
getName()//1
//在上面已经更改全局的getName,所以依然是1
new foo.getName()//2
//new 操作符在实例化构造器的时候,会执行构造器函数,也就是说,foo.getName会执行,输出2
new foo().getName()//3
//new操作符的优先级较高,所以会先new foo()得到一个实例,然后再执行实例的getName方法,这个时候,实例的构造器里没有getName方法,就会执行构造器原型上的getName方法
new new foo().getName()//3
//先执行new foo()得到一个实例,然后在new 这个实例的getName方法,这个时候会执行这个方法,所以输出3
//除了本地对象的方法,其他的函数都能new