第一章–Vue核心02
文章目录
1、列表渲染
1.1、基本列表
v-for
- 遍历数组
- 遍历对象
- 遍历字符串
- 遍历指定次数
<!--容器-->
<div id="hello">
<ul>
<!-- 遍历数组-->
<li v-for="(p,index) in persons" :key="index">{{p.name}}---{{p.age}}---{{index}}</li>
<!-- 遍历对象-->
<li v-for="(value,k) in car" :key="index">{{k}}-{{value}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
// 实例
const vue = new Vue({
el: '#hello', // 指定实例为哪个容器服务
data(){
return{
persons:[
{id:'01',name:'张三',age:2},
{id:'02',name:'张三',age:2},
{id:'03',name:'张三',age:2},
],
car:{
name: '奥迪',
price: '112'
}
}
}
});
// vue.$mount('#hello')
</script>
1.2、key的原理
key给节点作为标识
初始数据—》初始虚拟DOM—》将虚拟DOM转为真实DOM
虚拟DOM对比算法
<!--容器-->
<div id="hello">
<ul>
<!-- 遍历数组-->
<button @click.once="add">添加一个人</button>
<li v-for="(p,index) in persons" :key="index">{{p.name}}---{{p.age}}---{{index}}<input type="text"></li>
<li v-for="(p,index) in persons" :key="p.id">{{p.name}}---{{p.age}}---{{index}}<input type="text"></li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
// 实例
const vue = new Vue({
el: '#hello', // 指定实例为哪个容器服务
data(){
return{
persons:[
{id:'01',name:'张三',age:2},
{id:'02',name:'张三',age:2},
{id:'03',name:'张三',age:2},
],
car:{
name: '奥迪',
price: '112'
}
}
},
methods:{
add(){
const p = {id:'04',name:'王五',age:2}
// The unshift() method adds one or more elements to the beginning of an array and returns the new length of the array.
this.persons.unshift(p)
}
}
});
// vue.$mount('#hello')
</script>
- key的作用:key是虚拟DOM对象的表示,数据发生变化时,Vue根据新数据生成新的虚拟DOM,然后会有新虚拟DOM和旧虚拟DOM的差异比较
- 比较规则:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key
- DOM内容没变,直接使用之前的
- DOM内容变化,生成新的真实DOM,替换之前的
- 没有新的key
- 创建真实DOM,渲染到页面
- 旧虚拟DOM中找到了与新虚拟DOM相同的key
- index作为key的问题
- 对数据进行:逆序添加和删除会破坏顺序操作。产生没必要的真实DOM更新(界面效率低)
- 结构中包含真实的输入类DOM:产生错误DOM更新,界面出错
- 开发中最好使用数据的唯一标识作为key
1.3、列表过滤
- watch实现
- computed实现,更简单方便
<!--容器-->
<div id="hello">
<input type="text" placeholder="输入名字" v-model="keyword">
<ul>
<!-- 遍历数组-->
<button @click.once="add">添加一个人</button>
<li v-for="(p,index) in filpersons" :key="index">{{p.name}}---{{p.age}}---{{index}}<input type="text"></li>
<!-- <li v-for="(p,index) in filpersons" :key="p.id">{{p.name}}---{{p.age}}---{{index}}<input type="text"></li>-->
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
// 实例
const vue = new Vue({
el: '#hello', // 指定实例为哪个容器服务
data(){
return{
keyword:'',
persons:[
{id:'01',name:'张三',age:2},
{id:'02',name:'李四',age:2},
{id:'03',name:'王五',age:2},
],
// filpersons: [],
car:{
name: '奥迪',
price: '112'
}
}
},
methods:{
add(){
const p = {id:'04',name:'赵六',age:2}
// The unshift() method adds one or more elements to the beginning of an array and returns the new length of the array.
this.persons.unshift(p)
}
},
// watch实现
/*watch:{
keyword:{
immediate: true,
handler(val){
this.filpersons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1;
})
}
}
}*/
// computed实现
computed:{
filpersons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyword) !== -1;
})
}
}
});
// vue.$mount('#hello')
</script>
1.4、列表排序
使用一个字段标识:顺序 降序 升序
拿到过滤后的数组进行排序
<!--容器-->
<div id="hello">
<input type="text" placeholder="输入名字" v-model="keyword">
<button @click="sortType=0">原顺序</button>
<button @click="sortType=1">降序</button>
<button @click="sortType=2">升序</button>
<ul>
<!-- 遍历数组-->
<button @click.once="add">添加一个人</button>
<li v-for="(p,index) in filpersons" :key="index">{{p.name}}---{{p.age}}---{{index}}<input type="text"></li>
<!-- <li v-for="(p,index) in filpersons" :key="p.id">{{p.name}}---{{p.age}}---{{index}}<input type="text"></li>-->
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
// 实例
const vue = new Vue({
el: '#hello', // 指定实例为哪个容器服务
data(){
return{
keyword:'',
sortType: 0, // 0原顺序 1降序 2升序
persons:[
{id:'01',name:'张三',age:2},
{id:'02',name:'李四',age:4},
{id:'03',name:'王五',age:1},
],
// filpersons: [],
car:{
name: '奥迪',
price: '112'
}
}
},
methods:{
add(){
const p = {id:'04',name:'赵六',age:2}
// The unshift() method adds one or more elements to the beginning of an array and returns the new length of the array.
this.persons.unshift(p)
}
},
// watch实现
/*watch:{
keyword:{
immediate: true,
handler(val){
this.filpersons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1;
})
}
}
}*/
// computed实现
computed:{
filpersons(){
const arr = this.persons.filter((p)=>{
return p.name.indexOf(this.keyword) !== -1;
})
if(this.sortType){
// 降序
arr.sort((a,b)=>{
return this.sortType === 1?b.age-a.age:a.age-b.age;
})
}
return arr;
}
}
});
// vue.$mount('#hello')
</script>
1.5、Vue检测数据的原理
数据代理:
- 加工data
- vm._data=data
加一层,观察者对象
let person = {
name: '张三',
address: 'llli'
}
let obj = new Observer(person);
console.log(obj)
let vm = {}
vm._data = person = obj
function Observer(obj){
const keys = Object.keys(obj)
keys.forEach((k)=>{
Object.defineProperty(this,k,{
get(){
return obj[k];
},
set(val){
console.log('被修改了')
obj[k] = val;
}
})
})
}
Vue.set()
vm.$set()
vm._data.student = vm.student
<!--容器-->
<div id="hello">
<h1>学生信息</h1>
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<!-- <h3 v-show=this.show>性别:{{student.sex}}</h3>-->
<h3 v-if="student.sex">性别:{{student.sex}}</h3>
<h3>爱好:</h3>
<ul>
<li v-for="(h,index) in student.hobby">{{h}}</li>
</ul>
<h3>朋友:</h3>
<ul>
<li v-for="(f,index) in student.friend">{{f.name}}--{{f.age}}</li>
</ul>
<button @click="addSex">添加性别属性</button>
<button @click="addFriend">添加朋友</button>
<button @click="updateFirstFriendName">更新第一个朋友姓名</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el: '#hello',
data:{
show:false,
student:{
name: '张三',
age: 12,
hobby: ['吃饭','睡觉','打豆豆'],
friend: [
{name:'李四',age:12},
{name:'王麻子',age:23}
]
}
},
methods:{
addSex(){
// Vue.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
this.show = !this.show
},
addFriend(){
this.student.friend.unshift({name:'往无',age:12})
},
updateFirstFriendName(){
this.student.friend[0].name='找一'
}
}
})
</script>
- vue会监视data中所有层次的数据
- 如何监视?通过setter实现,在new Vue是就传入要监测的数据
- 对象中后追加的属性,默认不做响应式处理
- 有两种方法添加属性
- 检测数组中的数据
- 调用原生对应的方法对数组进行更新
- 重新解析模板,更新页面
- vue修改数组中的元素
- 使用API:push pop等
- Vue.set()或vm.$set()
Vue.set()和vm.$set()不能给vm或vm的根数据对象添加属性
数据劫持
2、收集表单数据
<div id="root">
<form>
账号:<input type="text" v-model="userInfo.account"> <br/>
密码:<input type="password" v-model="userInfo.password"> <br/>
性别:<br/>
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/>
年龄:<input type="number" v-model.number="userInfo.age"> <br/>
</form>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
new Vue({
el: '#root',
data:{
userInfo:{
account:'',
password:'',
age:12,
sex:'female'
}
}
})
</script>
- input为text,v-model收集的是value值,用户输入的是value值
- input为radio v-model收集的是value值,且要给标签配置value值
- checkbox
- 没有配置input的value属性,收集的是checked 是布尔值
- v-model初始值是数组,收集value组成的数组
- v-model初始值是非数组,收集的是checked
- v-model的三个修饰符
- lazy 失去焦点后收集数据
- number 输入字符串转为有效数字
- trim 首尾空格过滤
3、过滤器
时间戳
BootCDN:第三方库网站
<p>{{str|splice}}</p>
filters:{
splice(value){
return value.slice(0,1)
}
}
定义:对要显示的数据进行特定格式化后
语法:
- 注册过滤器:Vue.filter(name,callback)或new Vue{filters:{}}
- 使用过滤器:{{xxx|过滤器名}}或v-bind:属性=“xxx|过滤器名”
备注:
- 过滤器可以接收额外的参数,多个过滤器可以串联
- 并没有改变原本的数据,是产生新的对应的数据
4、内置指令
v-on v-bind
4.1、v-text
4.2、v-html
<!--容器-->
<div id="hello">
<div v-html="str"></div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
// 实例
const vue = new Vue({
el: '#hello', // 指定实例为哪个容器服务
data:{
str: '<h1>你好</h1>'
}
});
// vue.$mount('#hello')
</script>
- 向指定节点中渲染包含html结构的内容
- 与插值语法的区别
- v-html会替换节点中所有内容
- 可以识别html结构
- v-html有安全性问题
- 在网站上动态渲染节点容易导致XSS攻击
- 要在可信的内容上使用v-html,永远不要在用户提交的内容
4.3、v-cloak指令
阻塞
没有纸
- 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删除v-cloak属性
- 使用css配合v-cloak可以解决网速较慢是页面显示出{{xxx}}的问题
4.4、v-once
- 所在节点在初次动态渲染后,就视为静态内容
- 以后数据改变不会引起v-once所在结构的更新,可以用于优化性能
4.5、v-pre指令
- 跳过节点的编译过程
- 可利用它跳过;没有使用指令语法,没有使用插值语法的节点,会加快编译
}
});
// vue.$mount(’#hello’)
- 向指定节点中渲染包含html结构的内容
- 与插值语法的区别
- v-html会替换节点中所有内容
- 可以识别html结构
- v-html有安全性问题
- 在网站上动态渲染节点容易导致XSS攻击
- 要在可信的内容上使用v-html,永远不要在用户提交的内容
4.3、v-cloak指令
阻塞
没有纸
- 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删除v-cloak属性
- 使用css配合v-cloak可以解决网速较慢是页面显示出{{xxx}}的问题
4.4、v-once
- 所在节点在初次动态渲染后,就视为静态内容
- 以后数据改变不会引起v-once所在结构的更新,可以用于优化性能
4.5、v-pre指令
- 跳过节点的编译过程
- 可利用它跳过;没有使用指令语法,没有使用插值语法的节点,会加快编译
5、自定义指令
v-big
<!--容器-->
<div id="hello">
<p>当前n的值是:{{n}}</p>
<p>放大10倍后的值是:<span v-big="n"></span></p>
<button @click="n++">点我n+1</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
// 实例
const vue = new Vue({
el: '#hello', // 指定实例为哪个容器服务
data:{
n: 10,
},
directives:{
big(element,binding){
element.innerText = binding.value * 10
}
}
});
// vue.$mount('#hello')
</script>
big调用时机:1.指定与元素绑定时 2.指令所在模板被重新解析时
-
定义语法:
- 局部指令:new Vue({directives:{指令名:配置对象}}) new Vue({directives:{指令名:回调函数}})
- 全局指令:Vue.directive(指令名,配置对象)或Vue.directive(指令名,回调函数)
-
配置对象中常用的3个回调:
- .bind:指令与元素成功绑定时调用
- .inserted:指令所在元素被插入页面时调用
- .update:指令所在模板结构被重新解析时调用
6、生命周期
6.1、引出
- 名称:生命周期回调函数,生命周期函数,生命周期钩子
- Vue在关键时刻帮我们调用的一些特殊名称的函数
- 生命周期函数的名字不可更改,但是函数的具体内容是根据需求编写的
- 生命周期函数中的this指向是vm或者组件实例对象
6.2、挂载流程
-
new Vue() 创建实例
-
Init Events &LifeCycle 初始化:生命周期、时间,但数据代理还未开始
-
beforeCreate 无法通过vm访问到data中的数据
-
Init injections & reactivity 初始化:数据监测、数据代理
-
Created 可以通过vm访问到data中的数据、methods中配置的方法
-
此阶段Vue开始解析模板,生成虚拟DOM(内存中),页面还不能显示解析好的内容
- Has el
- Has template option
-
beforeMount 页面呈现的是未经Vue编译的DOM结构,所有对DOM的操作,最终都不奏效
-
create vm.$el 将内存中的虚拟DOM转为真实DOM插入页面
-
mounted
- 页面中呈现的是经过Vue编译的DOM
- 对DOM的操作均有效。至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义时间、等初始化操作
-
beforeUpdate 此时**数据是新的,但页面是旧的,**页面尚未和数据保持同步
-
Virtual DOM re-render and patch 根据新数据,生成新的虚拟DOM,随后和旧的虚拟DOM进行比较,最终完成页面更新
-
updated 数据是新的,页面也是新的,数据和页面同步
-
当vm.$destroy被调用时,完全销毁一个实例,清理它与其他实例的连接,解绑它的全部指令及自定义事件监听器(原生自定义事件依旧存在)
-
beforeDestory vm中所有的data methods、指令等等,都处于可用状态,马上要执行销毁过程,一般在此阶段:关闭定时器、取消订阅消息、解绑自定义时间等收尾操作
-
teardown watchers
-
destroyed
<!--容器-->
<div id="hello">
<p>当前n的值是:{{n}}</p>
<button @click="add">点我n+1</button>
<button @click="destroyEvent">点我销毁事件</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动时生成生产提示
// 实例
const vm = new Vue({
el: '#hello', // 指定实例为哪个容器服务
data:{
n: 10,
},
methods:{
add(){
this.n++
},
destroyEvent(){
console.log('destroyEvent...')
this.$destroy();
}
},
watch:{
},
beforeCreate(){
console.log('beforeCreate')
console.log(this)
// debugger;
},
created(){
// 数据检测 数据代理
console.log(this)
console.log('created')
},
beforeMount(){
console.log('beforeMount')
// 修改DOM 会发现不起作用,此时是虚拟DOM
// debugger;
},
mounted(){
// 注释掉el 修改template
console.log('mounted')
},
beforeUpdate(){
console.log('beforeUpdate')
// 数据和页面还未更新
// debugger;
},
updated(){
console.log('updated')
},
beforeDestroy(){
console.log("beforeDestroy")
console.log(this.n)
},
destroyed(){
console.log("destroyed")
}
})
// vue.$mount('#hello')
</script>
- 将要创建
- 创建完毕
- 将要挂载
- 挂载完毕
- 将要更新
- 更新完毕
- 将要销毁
- 销毁完毕
常用:
- mounted 发送ajax其你去、启动定时器、绑定自定义时间、订阅消息等【初始化操作】
- beforeDestroy:清除定时器、解绑自定义时间、取消订阅消息等【收尾操作】
销毁Vue实例:
- 销毁后借助Vue开发者工具看不到任何信息
- 销毁后自定义时间会失效,但原生DOM事件依然有效
- 一般不会在beforeDestory操作数据,因为即时操作数据,也不会再触发更新流程