面试题: v-if vs v-show区别?
v-show 如果为false,会将元素display:none
v-if 如果为false,会将元素直接从dom树删除掉
v-else 需配合 v-if一起使用,不能单独使用
template因为这个标签不会被浏览器解析,所以v-show对其无效,只能用v-if
v-if 是否可以与 v-for同时使用? 不行的? v-for优先级与v-if优先级高
为什么用data()?
每个组件或实例都有一个被对象返回独立的拷贝
polyfill:
判断当前浏览器的版本是否支持fetch,支持就直接用,若不支持就用polyfill
polyfill只是做了个兼容
1、$ npm install fetch-ie8 --save 安装
2、$ npm install es6-promise 安装
3、require(‘es6-promise’).polyfill(); 项目入口文件调用该方法
get请求:
get(){
fetch("./json/temp.json").then(res=>{
// return res.text() //返回了string格式的数据
return res.json() //返回了json格式的数据
}).then(res=>{
console.log(res)
})
}post请求:
post(){
fetch("http://localhost:3000/add",{
method:"post",
headers:{"Content-Type":"application/x-www-form-urlencoded"},
body:"name=XXX&age=10"
}).then(res=>{return res.json()}).then(res=>{console.log(res)})
}
//第二种方法
post(){
fetch("地址",{
method:"post",
headers:{
"Content-Type":‘’application/json"
},
body:JSON.stringify({
name:"123",
age:12
})
}).then(res=>{
return res.json()
}).then(res=>{
console.log(res);
})
}
为什么v-for指令当中添加key?
(key值是唯一的,不变的,尽量不要用index下标,除非你知道下标是不变的)
1、虚拟dom对比的时候,添加元素、删除元素的时候 key 提高对比效率
2、因为如果没有加key,内部插入新的元素,后面的元素都会经历卸载与重新装载的过程,效率太低了
3、添加key之后,内部本着key值相同,dom节点复用原则。
虚拟DOM
vue中引入了虚拟dom(真实dom映射,js对象) 属于内存数据的
1.vue在内存中生成一颗虚拟dom树
*var* vDom = {
tag:"div",
attr:{id:"content"},
children:[
{tag:"p",content:"2"},
{tag:"ul",attr:{className:"list-group"}}
]
}
2.将内存当中的虚拟dom将其初始化渲染,渲染成真实dom树,浏览器就可以看到了。
3.当我们修改vue实例当中data数据项的时候
this.arr.push("<li>1111</li><li>2222</li>")
4.将之前的虚拟dom结合更改后的数据,生成一颗新的虚拟dom数
*var* newVDom = {
tag:"div",
attr:{id:"content"},
children:[
{tag:"p",content:"2"},
{tag:"ul",attr:{className:"list-group"},children:[
{tag:"li",content:"1111"},
{tag:"li",content:"2222"}
]}
]
}
//5.将新的虚拟dom树 与 之前的虚拟dom树,进行diff算法的比较,对比差异
//6.再去将对比后的差异的部分进行重新的真实dom的渲染操作。
虚拟dom的原理
频繁且复杂的dom操作通常是前端性能瓶颈的产生点,Vue提供了虚拟dom的解决办法
虚拟的DOM的核心思想是:对复杂的文档DOM结构,提供一种方便的工具,进行最小化地DOM操作。这句话,也许过于抽象,却基本概况了虚拟DOM的设计思想
(1) 提供一种方便的工具,使得开发效率得到保证
(2) 保证最小化的DOM操作,使得执行效率得到保证
也就是说,虚拟dom的框架/工具都是这么做的:
- 根据虚拟dom树最初渲染成真实dom
- 当数据变化,或者说是页面需要重新渲染的时候,会重新生成一个新的完整的虚拟dom
- 拿新的虚拟dom来和旧的虚拟dom做对比(使用diff算法)。得到需要更新的地方之后,更新内容
这样的话,就能大量减少真实dom的操作,提高性能
什么是虚拟dom?与key值的关系?
Virual DOM是用JS对象记录一个dom节点的副本,当dom发生更改时候,先用虚拟dom进行diff,算出最小差异,然后再修改真实dom。
当用传统的方式操作DOM的时候,浏览器会从构建DOM树开始从头到尾执行一遍流程,效率很低。而虚拟DOM是用javascript对象表示的,而操作javascript是很简便高效的。虚拟DOM和真正的DOM有一层映射关系,很多需要操作DOM的地方都会去操作虚拟DOM,最后统一一次更新DOM。因而可以提高性能
虚拟DOM的Diff算法
虚拟DOM中,在DOM的状态发生变化时,虚拟DOM会进行Diff运算,来更新只需要被替换的DOM,而不是全部重绘。
在Diff算法中,只平层的比较前后两棵虚拟DOM树的节点,没有进行深度的遍历。
1.如果节点类型改变,直接将旧节点卸载,替换为新节点,旧节点包括下面的子节点都将被卸载,如果新节点和旧节点仅仅是类型不同,但下面的所有子节点都一样时,这样做也是效率不高的一个地方。
2.节点类型不变,属性或者属性值改变,不会卸载节点,执行节点更新的操作。
3.文本改变,直接修改文字内容。
4.移动,增加,删除子节点时:
如果想在中间插入节点F,简单粗暴的做法是:卸载C,装载F,卸载D,装载C,卸载E,装载D,装载E。如下图:
写代码时,如果没有给数组或枚举类型定义一个key,就会采用上面的粗暴算法。
如果为元素增加key后,Vue就能根据key,直接找到具体的位置进行操作,效率比较高。如下图:
本寻着key值相同的即可复用的原则。
*在v-for中提供key,一方面可以提高性能,一方面也会避免出错
生命周期中的8个钩子函数和keep-alive动态组件内的2个钩子函数:
生命周期
每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁
-
实例、组件通过new Vue() 创建出来之后会初始化事件和生命周期,然后就会执行beforeCreate钩子函数,这个时候,数据还没有挂载到,只是一个空壳,无法访问到数据和真实的dom,一般不做操作
-
挂载数据,绑定事件等等,然后执行created函数,这个时候已经可以使用到数据,也可以更改数据,在这里同步更改数据不会触发updated函数,一般可以在这里做初始数据的获取。 做异步ajax,绑定初始化事件
-
接下来开始找实例或者组件对应的模板,编译模板为虚拟dom放入到render函数中准备渲染,然后执行beforeMount钩子函数,在这个函数中虚拟dom已经创建完成,马上就要渲染,在这里也可以更改数据,不会触发updated,这是在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始化数据的获取
-
接下来开始render,渲染出真实dom,然后执行mounted钩子函数,此时,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了,可以在这里操作真实dom等事情…
-
当组件或实例的数据更改之后,会立即执行beforeUpdate,然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染,一般不做什么事儿
-
当更新完成后,执行updated,数据已经更改完成,dom也重新render完成,可以操作更新后的dom
-
当经过某种途径调用$destroy方法后,立即执行beforeDestroy,一般在这里做一些善后工作,例如清除计时器、清除非指令绑定的事件等等
-
组件的数据绑定、监听…去掉后只剩下dom空壳,这个时候,执行destroyed,在这里做善后工作也可以
<div id="app">
<my-component></my-component>
</div>
<!--定义组件的模板-->
<template id="my-component">
<div>
<h1 id="title">我是h1 -- {{msg}}</h1>
<input type="text" v-model="msg">
<button @click="destroy">销毁组件</button>
</div>
</template>
<script>
/*
组件从创建到销毁的一系列过程叫做组件的声明周期。
vue在整个生命周期里面提供了一些函数,可以在内部实现一些业务逻辑,
并且这些函数会在一些特定的场合下去执行。(在生命周期的某一个时刻进行触发)
组件的生命周期大体可以分为三个阶段: 初始化、运行中、销毁
初始化阶段:beforeCreate created beforeMount (render) mounted
运行中阶段:beforeUpdate updated
销毁阶段: beforeDestroy destroyed
*/
//初始化阶段
//1.一个组件或者实例的生命周期都是从new开始的
//2.实例化之后,内部会做一些初始化的事件与生命周期相关的配置
Vue.component("my-component",{
template:"#my-component",
data(){
return {
msg:"hello"
}
},
methods:{
destroy(){
this.$destroy() //组件销毁方法
}
},
//3.beforeCreate这个钩子函数初始化的时候就会执行
//但是数据是获取不到的,并且真实dom元素也是没有渲染出来的
beforeCreate(){
console.log("beforeCreate...")
console.log(this.msg,document.getElementById("title"))
},
//4.created钩子函数内部可以访问到数据了,但是页面当中真实dom节点还是没有渲染出来
//在这个钩子函数里面,可以进行相关初始化事件的绑定、发送ajax操作
//当组件还没有挂载完毕之前,更改数据的话,是不会触发运行时钩子函数的执行!
created(){
console.log("created...")
this.timer = setInterval(()=>{
console.log("定时器开着哦...")
this.msg = this.msg+"!"
},2000)
// this.msg = 11111111111
console.log(this.msg,document.getElementById("title"))
},
//5.接下来的过程,就是组件或者实例去查找各自的模板结构,然后将其编译成虚拟dom
//6.beforeMount代表dom马上就要被渲染出来了,但是却还没有真正的渲染出来
//这个钩子函数与created钩子函数用法基本一致,可以进行相关初始化事件的绑定、发送ajax操作
beforeMount(){
console.log("beforeMount...")
console.log(this.msg,document.getElementById("title"))
},
//生成好虚拟dom,然后内部通过render函数将对应的el进行替换,做一个初始化的虚拟dom渲染真实dom过程
// render(){
// console.log("render....")
// }
//7.mounted钩子函数是挂载阶段的最后一个钩子函数
//数据挂载完毕,真实dom元素也已经渲染完成了
//这个钩子函数内部可以做一些实例化相关的操作 拖拽
mounted(){
console.log("mounted...")
console.log(this.msg,document.getElementById("title"))
},
//8.这个钩子函数初始化的不会执行
//当组件挂载完毕的时候,并且当数据改变的时候,才会立马执行
//这个钩子函数获取dom的内容是更新之前的内容
beforeUpdate(){
console.log("beforeUpdate...",this.msg ,document.getElementById("title").innerHTML)
},
//9.这个钩子函数获取dom的内容是更新之后的内容
//生成新的虚拟dom,新的虚拟dom与之前的虚拟dom进行比对,差异之后,就会进行真实dom渲染。
//在updated钩子函数里面就可以获取到因diff算法比较差异得出来的真实dom渲染了。
updated(){
console.log("updated....", this.msg,document.getElementById("title").innerHTML)
},
//10.当组件销毁的时候,就会触发
//这个钩子函数代表销毁之前,可以做一些善后操作
//可以清除一些初始化事件、定时器相关的东西。
beforeDestroy(){
console.log("beforeDestory....")
clearInterval(this.timer)
},
//11.组件销毁的时候执行
//watch/数据劫持等功能已经完全丧失
destroyed(){
console.log("destroyed...")
}
})
new Vue().$mount("#app");
</script>
keep-alive动态组件
用keep-alive标签包裹动态组件的时候,会缓存不活动的组件实例,就不会去执行beforeDestroy钩子函数,那么动态组件就不会被销毁,
当用keep-alive包裹动态组件的时候,他也提供两个钩子函数:activated和deactivated(这两个函数只有在被keep-包裹的时候才会触发)
activated放的是一些初始化事件,而deactived是将初始化的一些事件除去(可以将created和beforeDestroy对应的替换掉)
include只有名称匹配的组件会被缓存,exclude只有名称匹配的组件不会被缓存,与include相反,默认缓存所有动态组件
<div id="app">
<button @click="type=type=='mya'?'myb':'mya'">切换</button>
<keep-alive include="mya">
<component :is="type"></component>
</keep-alive>
</div>
<script>
Vue.component("mya",{
template:"<h3>A组件</h3>",
created(){ //created钩子函数只执行一次
console.log("created...A")
},
beforeDestroy(){
console.log("beforeDestroy...A")
},
activated(){
console.log("activated...A")
},
deactivated(){
console.log("deactivated...A")
}
})
Vue.component("myb",{
template:"<h3>B组件</h3>",
created(){
console.log("created...B")
},
beforeDestroy(){
console.log("deforeDestroy...B")
},
activated(){
console.log("activated...B")
},
deactivated(){
console.log("deactivated...B")
}
})
new Vue({
el:"#app",
data:{
type:"mya"
}
})
</script>