Vue.js第2课-基础
一、Vue.js 实例
一个 Vue.js 的项目,是由很多个组件组成的,Vue.js 组件也是一个实例,也可以说一个 Vue.js 的项目是由很多很多 Vue.js 实例拼装成的。
<div id="app">
<div v-on:click="clickFun">{{msg}}</div>
<test></test>
</div>
<script>
// 创建一个全局组件
Vue.component('test', {
template: "<h1>hello</h1>"
})
var app = new Vue({
el: "#app",
data: {
msg: "hello world"
},
methods: {
clickFun() {
alert("hello");
}
}
})
console.log(app); // wn {_uid: 0, _isVue: true, $options: {…}, _renderProxy: wn, _self: wn, …}
console.log(app.$el); // <div id="app"><div>hello world</div></div>
console.log(app.$data); // {__ob__: we}
console.log(app.$data.msg); // hello world
</script>
二、Vue.js 实例生命周期函数
生命周期函数就是 Vue.js 实例在某一个时间点会自动执行的函数,这些函数并不是放到 methods 中,而是直接放到 Vue.js 的实例中。
<div id="app"></div>
<script>
var app = new Vue({
el: "#app",
template: "<div>{{msg}}</div>",
data: {
msg: "hello world"
},
// Vue 实例部分初始化的时候执行
beforeCreate: function () {
console.log("beforeCreate");
},
created: function () {
console.log("create");
},
beforeMount: function () {
console.log(this.$el); // <div id="app"></div>
console.log("beforeMount");
},
mounted: function () {
console.log(this.$el); // <div>hello word</div>
console.log("mounted");
},
// beforeDestroy 和 destroyed 需要先销毁,才会执行,在控制台执行 app.$destroy
beforeDestroy: function () {
console.log("beforeDestroy");
},
destroyed: function () {
console.log("destroyed");
},
// 当数据发生改变时,beforeUpdate 和 updated 才会执行
beforeUpdate: function () {
console.log("beforeUpdate");
},
updated: function () {
console.log("updated");
}
})
</script>
下图是 Vue.js 官网上生命周期图示:
三、Vue.js 的模板语法
1、插值表达式
<div id="app">{{msg}}</div>
<script>
var app = new Vue({
el: "#app",
data: {
msg: "Hello World!"
}
})
</script>
2、v-text
v-text 指令,让标签内的 innerText 和 msg 绑定,后面的值是一个 js 表达式:
<div id="app">
<div id="Vtext" v-text="msg"></div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
msg: "Hello World!"
}
})
var Vtext = document.getElementById('Vtext');
console.log(Vtext.innerText); // Hello World!
</script>
3、v-html
v-html 让标签内的 innerHTML 和 msg 绑定:
<div id="app">
<div id="Vhtml" v-html="msg"></div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
msg: "Hello World!"
}
})
var Vhtml = document.getElementById('Vhtml');
console.log(Vhtml.innerHTML); // Hello World!
</script>
目前来看,v-text 和 v-html 没有区别,我们把 msg 中的值变成一个标签,例如放到 h1 标签中,v-text 渲染的依然是 “< h1>hello</ h1>”,而 v-html 渲染出的就是 h1 字号的 “hello”。
<div id="app">
<div v-text="msg"></div>
<div v-html="msg"></div>
---
<div v-text="msg2"></div>
<div v-html="msg2"></div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
msg: "Hello World!",
msg2: "<h1>Hello World!</h1>"
}
})
</script>
通过以上的例子得出,插值表达式和 v-text 的渲染结果是一样的。
凡是 “v-***” 后面的内容都是一个 js 表达式,在表达式中,不仅可以写一个变量,还可以往后面加一个字符串,插值表达式中也可以写 js 表达式:
<div id="app">
{{msg + ' world1'}}
<div v-text="msg + ' world!'"></div>
<div v-html="msg + ' world!'"></div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
msg: "Hello"
}
})
</script>
四、计算属性,方法和侦听器
fullName 的计算。
方式一:computed 计算属性(有缓存机制,会提高性能)
<div id="app">{{fullName}} {{age}}</div>
<script>
var app = new Vue({
el: "#app",
data: {
firstName: "Liu",
lastName: "Zhenghe",
age: 26
},
computed: {
fullName: function () {
console.log("计算了一次!");
return this.firstName + this.lastName;
}
}
})
</script>
可以看到页面渲染出 LiuZhenghe 26。
如何证明 computed 具有缓存机制?在 fullName 方法中打印一句话“计算了一次!”,在控制台改变 age 的值,再改变 fullName 中计算过的属性值。
可以看到,页面一刷新的时候,打印一遍 “计算了一次!”,改变 age,没有打印这句话,改变 lastName,又打印一遍 “计算了一次!”。
方式二:methods
注意:因为 fullName 这次写到了 methods 中,所以在模板引擎中需要加括号。
<div id="app">{{fullName()}} {{age}}</div>
<script>
var app = new Vue({
el: "#app",
data: {
firstName: "Liu",
lastName: "Zhenghe",
age: 26
},
methods: {
fullName: function () {
console.log("计算了一次!");
return this.firstName + this.lastName;
}
}
})
</script>
可以看到,页面一刷新的时候,打印一遍 “计算了一次!”,改变 age,也打印这句话,改变 firstName 和 lastName,又都打印一遍 “计算了一次!”。
所以,说明 methods 方法是没有缓存机制的,只要改变了数据,就会重新计算一次。
方式三:watch 侦听器
<div id="app">{{fullName}} {{age}}</div>
<script>
var app = new Vue({
el: "#app",
data: {
firstName: "Liu",
lastName: "Zhenghe",
fullName: "LiuZhenghe",
age: 26
},
watch: {
firstName: function () {
console.log("计算了一次!");
this.fullName = this.firstName + this.lastName;
},
lastName: function () {
console.log("计算了一次!");
this.fullName = this.firstName + this.lastName;
}
}
})
</script>
在控制台,改变 age,并没有打印出 “计算了一次!”这句话,改变 firstName 和 lastName 的值,发现打印出了这句话,说明 watch 和 computed 一样具有缓存机制,但和 computed 相比,watch 逻辑代码多出很多,所以,当这三种计算方式都能实现计算功能的时候,优先选择 computed。
五、计算属性的 setter 和 getter
computed 有这样一个特性:当他依赖的值发生变化的时候,它就会去重新的计算。
<div id="app">{{fullName}}</div>
<script>
var app = new Vue({
el: "#app",
data: {
firstName: "Liu",
lastName: "Zhenghe",
},
computed: {
fullName: {
get: function () {
return this.firstName + " " + this.lastName
},
// set 方法可以接收一个设置后的值,例如在控制台改变 fullName 的值 “app.fullName="Li Zheng"”,将会打印出 “Li Zheng”。
set: function (value) {
console.log(value);
// 将设置后的 fullName 值通过空格分开,并存放到数组 arr 中,按照上面给 fullName 设置的值,下边 arr 的值将会打印出 “(2) ["Li", "Zheng"]”
var arr = value.split(" ");
console.log(arr);
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</script>
通过这个例子,可以知道,在 computed 上,不仅可以通过 get 方法,通过其他的值算出一个新值,同时可以写 set 方法,通过设置一个值,来改变他相关联的一个值,而改变了相关的值后,又使 fullName 的值重新计算。
六、Vue.js 中的样式绑定
样式和数据绑定,实现:点击一次,元素变色,再点击一次,元素颜色变回来。
方式一、class 的对象绑定,借助 class 和对象的形式做样式和数据的绑定。
<div id="app" :class="{activted:isActivted}" v-on:click="changeColor">Hello World</div>
<script>
var app = new Vue({
el: "#app",
data: {
isActivted: false,
},
methods: {
changeColor: function () {
this.isActivted = !this.isActivted;
}
}
})
</script>
<style>
.activted {
color: red
}
</style>
上面代码中,通过给元素绑定点击事件,在点击事件中,改变 isActivted 的值,来确定是否给元素的 class 加 activted,然后给 activted 设置一个样式,来实现点击切换颜色效果。
方式二、:class 中不再写入一个对象,而是写一个数组,在数组中写一个 activted。
<div id="app" :class="[activted]" v-on:click="changeColor">Hello World</div>
<script>
var app = new Vue({
el: "#app",
data: {
activted: "",
},
methods: {
changeColor: function () {
// 用 if 判断实现点击切换效果:
// if (this.activted === "activted") {
// this.activted = "";
// } else {
// this.activted = "activted";
// };
// 用三元运算符实现点击切换效果:
this.active = this.activted === "activted" ? this.activted = "" : this.activted =
"activted";
}
}
})
</script>
<style>
.activted {
color: red
}
</style>
在上面代码中,首先将 activted 设置为空,然后在点击事件中通过 if 或三元运算符来判断,当 data 中的 activted 为空时,就设置为 “activted”,当是“activted”时,再设置为空,以此来实现点击切换颜色效果。
方式三、通过 style 来改变样式,:style 中写 js 字符串。
<div id="app" :style="styleObj" v-on:click="changeColor">Hello World</div>
<script>
var app = new Vue({
el: "#app",
data: {
styleObj: {
color: "black"
}
},
methods: {
changeColor: function () {
this.styleObj.color = this.styleObj.color === "black" ? "red" : "black";
}
}
})
</script>
上面代码中,将 data 中的样式对象 styleObj 传到 :style 中,在点击事件中,通过改变 styleObj 中的 color 值来实现点击切换颜色效果。
方式四:通过 style 来改变样式,:style 中写数组,数组中还可以挂载多个样式。
<div id="app" :style="[styleObj,{fontSize : '24px'},{fontWeight : '900'}]" v-on:click="changeColor">Hello World
</div>
<script>
var app = new Vue({
el: "#app",
data: {
styleObj: {
color: "black"
}
},
methods: {
changeColor: function () {
this.styleObj.color = this.styleObj.color === "black" ? "red" : "black";
}
}
})
</script>
无论是绑定 class 还是 style,都有两种方式,一种是通过对象的方式绑定,一种是通过数组的方式绑定。
七、Vue.js 中的条件渲染
1、条件渲染 v-if 和 v-show 对比
v-if 后面跟一个 js 表达式,它的返回值(true, false)决定了这个元素是否真实的被挂载到页面上。
<div id="app">
<div v-if="show">Hello World!</div>
<div v-show="show">Hello World!</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
show: true
}
})
</script>
在 data 中 show 默认为 true,是显示的,可以在控制台中修改一下 show 的值:“app.show = false”,发现两个 dom 不存在了,改为 true,这两个 dom 又显示了。
打开网页,可以看到,无论是 v-if 还是 v-show,如果 data 中的 show 值是 false,那么两个元素都不显示,我们在控制台中查看一下两个元素:
发现,v-if 在页面上就不显示了,而 v-show 元素是存在的,只是样式被加了 “display: none;” ,所以 v-show 的性能更会高一些,因为它不会频繁的去把有个 dom 从页面上删再添加。
2、更复杂的使用 v-if
v-if 和 v-else
<div id="app">
<div v-if="show">Hello World!</div>
<!-- 注意,v-if 和 v-else 两个元素中间不能加其他元素,否则条件判断不能生效。 -->
<div v-else>Bye World!</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
show: false
}
})
</script>
上面代码中,如果 show 的值是 false 的话,就显示 v-else 中的值,需要注意的是 v-if 和 v-else 两个元素中间不能加其他元素,否则条件判断不能生效。
多条件(v-if、v-else-if、v-else)
<div id="app">
<div v-if="show === 'a'">This is a!</div>
<div v-else-if="show === 'b'">This is b!</div>
<div v-else>This is other</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
show: a
}
})
</script>
可以在控制台中改变一下 show 的值,当时 show 的值为 a 时,显示 “This is a”,为 b 时,显示 “This is b”,为其他时,显示 “This is other。”
3、key 值
<div id="app">
<div v-if="show">
用户名:<input type="text">
</div>
<div v-else>
邮箱:<input type="text">
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
show: true
}
})
</script>
上面代码,因为 show 的值是 true,所以此时页面上显示的是用户名输入框,我们在输入框中输入内容,然后在控制台中改变 show 的值为 flase,发现输入框更换为邮箱了,但是输入框里的内容并没有变,这是因为 Vue 在重新渲染页面的时候,会尽量的复用页面上已存在的 dom,当显示用户名的时候,已经有了一个 input,当切换邮箱名的时候,Vue 会发现以前页面上有一个 input,所以他的机制会尽量的帮你复用页面上的 dom,回去尝试复用这个 input,但是内容并没有被清空。
解决方法:在 input 中加 key 值,例如给用户名加一个 key 值 “key="username"”,邮箱加一个 “key="email"”,此时再改变 show 的值,发现输入框内容被清空了功能也没有任何的 bug 了。当给元素加一个 key 值的时候,Vue 会知道他是页面上唯一的元素,如果两个元素的 key 值不一样,Vue 就不会尝试复用以前的 input 标签了。
<div id="app">
<div v-if="show">
用户名:<input type="text" key="username">
</div>
<div v-else>
邮箱:<input type="text" key="email">
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
show: true
}
})
</script>
八、Vue.js 中的列表渲染
先来看一下列表循环最基础的内容:
<div id="app">
<div v-for="item in list">{{item}}</div>
<div v-for="item of list">{{item}}</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [
"one", "two", "three", "four", "five"
]
}
})
</script>
在 v-for 中,in 和 of 的效果是一样的,可以在 item 后再加一个参数 index,代表索引下标:
<div id="app">
<div v-for="(item, index) of list">{{item}} --- {{index}}</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [
"one", "two", "three", "four", "five"
]
}
})
</script>
在实际的开发中,为了提升循环显示的性能,我们会给每一个循环项上加唯一的 key 值,可以在循环向上加一个 key 值,因为目前 index 的值是唯一的,所以使用 index 作为 key 值。
<div id="app">
<div v-for="(item, index) of list" :key="index">{{item}} --- {{index}}</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [
"one", "two", "three", "four", "five"
]
}
})
</script>
但是不推荐这样使用 index 的,因为使用 index 作为 key 值,在平凡操作 DOM 元素相对应的数据的时候,是比较费性能的,可能让 Vue 没法充分的复用 dom 节点。如果不用 index 做 key 值。那用什么呢?
一般在真正的项目中,后端向前端返回数据的时候,list 并不是写死的一个数据,后端反数据的时候,一般会携带一个数据相关的唯一标识符,一般是 id,可能是数据对应的一个随机的数据段,例如:
<div id="app">
<div v-for="(item, index) of list" :key="item.id">{{item.text}} --- {{index}}</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [{
id: "10001",
text: "one"
}, {
id: "10002",
text: "two"
}, {
id: "10003",
text: "three"
}],
}
})
</script>
这个时候,key 值就没必要是 index 了,可以用 item.id,我们可以参考之前 TodoList 在控制台中给 list 添加数据,可以看到数据添加成功,页面更新了:
如果通过下标添加数据呢?可以在控制台看到数据添加成功,但是页面却没有更新。
到这里,需要讲一个内容,当我们去尝试修改数组里的内容时,不能直接通过下标的形式,只能通过 Vue 提供的几个数组变异方法来操作数组,才能够实现数据发生变化,页面也跟着变这种响应式效果。在 Vue 中一共提供了 7 个数组变异方法来帮助我们操作数组,他们分别是:
- pop:把数组最后一项删除掉。
- push:往数组里增加一条。
- shift:把数组的第一项删除掉。
- unshift:往数组的第一项里加点儿内容。
- splice:数组的截取。
- sort:对驻足进行排序。
- reverse:对数组取反。
回到页面上,例如将第二项做一个替换,可以在控制台做如下操作:
除了变异方法可以改变页面数据的显示,还有一种方法,就是改变引用:
上边数组循环只循环了一个数据,假设有这样一个情况:不仅要根据 list2 循环一个 div,还要根据 list 循环一个 span 标签:
<div id="app">
<div v-for="(item, index) of list">
<div>{{item.text}} --- {{index}}</div>
<span>{{item.text}} --- {{index}}</span>
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [{
id: "10001",
text: "one"
}, {
id: "10002",
text: "two"
}, {
id: "10003",
text: "three"
}],
}
})
</script>
如果要想显示一个 span 一个 div 就必须在外层包裹一个 div,否则就会先显示完 div 再显示 span, 假设在需求里就不想让这个 div 存在,有什么办法么?template 占位符,可以把外层 div 改为 template,也可以理解为一个模板占位符,回到页面上,可以看到依然是 div,span 的显示方式,但是最外层的 div 就消失了。
<div id="app">
<template v-for="(item, index) of list">
<div>{{item.text}} --- {{index}}</div>
<span>{{item.text}} --- {{index}}</span>
</template>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [{
id: "10001",
text: "one"
}, {
id: "10002",
text: "two"
}, {
id: "10003",
text: "three"
}],
}
})
</script>
其实这个 template 模板占位符可以帮我们去包裹一些元素,但是在循环的时候并不会被真正的渲染到页面上。
除了数组可以做循环之外,其实还可以对对象做一个循环,接下来看一下对象怎么做循环:
<div id="app">
<div v-for="item of userInfo">{{item}}</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
userInfo: {
name: "liu",
age: "26",
gender: "male",
salary: "secret"
}
}
})
</script>
item 还可以接收其他内容 key,index:
<div id="app">
<div v-for="(item, key, index) of userInfo">
{{item}} --- {{key}} --- {{index}}
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
userInfo: {
name: "liu",
age: "26",
gender: "male",
salary: "secret"
}
}
})
</script>
我们在控制台改变一下 userInfo 中的值:
可以看到,改变 userInfo 中的某个值,页面会更新,但是新添加一个 address 值,数据变了,但页面不会更新,所以当你去便历对象的时候,如果直接动态的往里加值是不好用的,但是如果就是相加那怎么加呢?
可以和数组的方式一样,直接改变他的引用:
通过改变引用这种方式,数据变化了,页面也就变化了。
九、Vue.js 中的 set 方法
先来回顾上一节我们说过的,如果要改变对象 userInfo 中的属性和值,就要通过它的引用来改变,这样才会让页面也更新。
<div id="app">
<div v-for="(item, key, index) of userInfo">
{{item}} --- {{key}} --- {{index}}
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
userInfo: {
name: "liu",
age: "26",
gender: "male",
salary: "secret"
}
}
})
</script>
那除了改变引用是否还有其他方法能在改变数据后页面也更新呢?下面看一下 Vue.js 中的 set 方法。
1、对象中的 set 方法
还是用上的代码,可以通过构造函数来更新 userInfo 中的属性值,例如:“Vue.set(app2.userInfo,"address","Beijing")”,也可以通过实例对象来更新 userInfo 中的属性值,例如 “app2.$set(app2.userInfo,"tel","1234567")”,这两种方法在更新数据后,页面也都会同步更新。
2、数组中的 set 方法
<div id="app">
<div v-for="item of list">
{{item}}
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
list: [1, 2, 3, 4]
}
})
</script>
在控制台中,分别通过全局对象(构造函数)和实例对象来更新 list 中的数据,可以看到这两种方法在更新数据后,页面也都会同步更新。