1. 计算属性
1.1 什么是计算属性 ?
我们知道,在模板中可以直接通过插值语法显示一些
data中
的数据。但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示。比如我们有firstName
和lastName
两个变量,我们需要显示完整的名称。但是如果多个地方都需要显示完整的名称,我们就需要写多个{{firstName}} {{lastName}}
。我们可以将上面的代码换成计算属性:我们发现计算属性是写在实例的
computed
选项中的。
<div id="app">
<h2>{{firstName + ' ' + lastName }}</h2>
<h2>{{firstName}} {{lastName }}</h2>
<h2>{{getFullName()}}</h2>
<!-- 计算属性在使用的时候不需要加上括号 -->
<h2>{{fullName}}</h2>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Lebron',
lastName: 'James'
},
methods: {
getFullName() {
return this.firstName + ' ' + this.lastName;
}
},
/* 计算属性 */
computed: {
// 计算属性一般不加动词 get 类似这些
fullName() {
return this.firstName + ' ' + this.lastName;
}
}
});
1.2 使用计算属性计算图书总价格
在计算图书总价的时候尽量使用计算属性的方式,计算属性有缓存功能,在值不改变的时候他只会计算一次。
而使用方法返回值的方式,如果其中出现循环的时候就会降低计算效率。
<div id="app">
<h2>总价格:{{totalPrice}}</h2>
</div>
<!-- 计算属性在多次调用的时候只会调用一次 -->
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
books: [
{id: '110', name: 'Unix编程艺术', price: 120},
{id: '111', name: '代码大全', price: 120},
{id: '112', name: '深入理解计算机原理', price: 120},
{id: '112', name: 'Unix编程艺术', price: 120},
{id: '112', name: '现代操作系统', price: 120}
]
},
computed: {
totalPrice() {
let result = 0;
for (let item of this.books) {
result += item.price;
}
return result;
}
}
});
1.3 计算属性的 setter 和 getter
- 每个计算属性都包含一个
getter
和一个setter
,在获取值时都是使用计算属性的getter
方法读取,在某些情况下,你可以提供一个setter
方法(不常用)。
<div id="app">
<h2>{{fullName}}</h2>
<h2>{{fullNameSetAndGet}}</h2>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Kobe',
lastName: 'Bryant'
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName;
},
fullNameSetAndGet: {
// set 方法一般是被删除的
/**
*
* @param newVal
*/
set(newVal) {
console.log('set方法');
console.log(newVal);
const names = newVal.split(' ');
this.firstName = names[0];
this.lastName = names[1];
},
get() {
return this.firstName + ' ' + this.lastName;
}
}
}
});
1.4 计算属性的缓存
- 计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。如果值发生改变才进行重新计算。
<div id="app">
<!-- 1. 直接拼接 -->
<h2>{{firstName}} {{lastName}}</h2>
<!-- 2. 通过定义methods-->
<!--<h2>{{getFullName()}}</h2>-->
<!--<h2>{{getFullName()}}</h2>-->
<!--<h2>{{getFullName()}}</h2>-->
<!--<h2>{{getFullName()}}</h2>-->
<!-- 3. 通过computed -->
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
message: '您好啊!',
firstName: 'Lebron',
lastName: 'James'
},
methods: {
getFullName() {
console.log('getFullName');
return this.firstName + this.lastName;
}
},
/* 计算属性 */
computed: {
fullName() {
console.log('fullName');
return this.firstName + this.lastName;
}
}
});
2. ES6语法补充
ES6中 ,新出现
let
和const
关键字,用于代替var
,使用let
声明变量,使用const
声明一个常量。let
关键字声明的变量有块级作用域,var
声明的变量没有块级作用域。在ES6之前
if 和 for
代码块是没有块级作用域的。在Es6
之前因为if
和for
都没有块级作用域所以在很多时候,我们都必须借助与function的作用域来解决应用外边变量的问题。
2.1 let 和 var 的块级作用域
- 在
ES6
中 使用let
关键字替换var
关键字声明变量,var
关键字声明变量没有块级作用域,容易引发一些问题。
// ==========================================================
// 块级作用域
{
var name = 'lyp';
console.log(name);
}
console.log(name); // lyp
{
let age = 23;
console.log(age);
}
// console.log(age); // Uncaught ReferenceError: age is not defined
// ==========================================================
// ==========================================================
// 没有块级作用域引起的问题
var func;
if (true) {
var firstName = 'andy';
func = function () {
console.log(firstName);
}
}
// 在这里修改 firstName
firstName = 'kobe';
func(); // kobe
// ==========================================================
2.2 块级作用域对 for 循环的影响
- 因为回调函数是一个异步的操作,导致循环执行完成之后才会值回调函数,解决这个问题可以使用闭包。使用立即函数 + 闭包,因为函数拥有自己的作用域。
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
const btns = document.querySelectorAll('button');
/**
* ES5中的var是没有块级作用域的
*
* 使用闭包 :
* 为什么闭包可以解决问题 , 因为函数是有作用域的
* javascript 类似 if 和 for 都是没有作用域的
* 在Es6之前因为if和for都没有块级作用域所以在很多时候,我们都必须借助与function的作用域来解决应用外边变量1的问题
*
* 在ES6中加入的let是有块级作用域的
*/
for (let i = 0; i < btns.length; i++) {
// 这里面的i使用let声明有自己的块级作用域
btns[i].addEventListener('click', () => {
console.log('第' + (i + 1) + '个按钮被点击');
});
}
2.3 使用let关键字改写 2.1 的案例解决问题
- 因为
let
有自己的块级作用域。
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
for (let i = 0; i < btns.length; i++) {
// 这里面的i使用let声明有自己的块级作用域
btns[i].addEventListener('click', () => {
console.log('第' + (i + 1) + '个按钮被点击');
});
}
2.4 三种方案对比
- 定义多个按钮的点击事件。由于
var
没有块级作用域而引发的问题。有两种解决方案:
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
var btns = document.querySelectorAll('button');
console.log(btns);
// 1. 不使用闭包的方式
/*for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', () => {
console.log('第' + (i + 1) + '个按钮被点击了!');
});
}*/
// 2. 使用闭包处理
/*for (var i = 0; i < btns.length; i++) {
(function (i) {
btns[i].addEventListener('click', () => {
console.log('第' + (i + 1) + '个按钮被点击了!');
});
})(i)
}*/
// 3. 使用let关键字
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', () => {
console.log('第' + (i + 1) + '个按钮被点击了!');
});
}
2.5 const的使用和注意点
建议 : 在开发中优先使用
const
只有需要在改变某个标识符的时候才使用let
。常量是不允许被修改的 一旦给
const
修饰的标识符被赋值之后不能进行修改。在使用
const
定义标识符,必须进行赋值。常量的含义是指向的对象不能修改,但是可以改变对象内部的属性。
// 建议 : 在开发中优先使用const只有需要在改变某个标识符的时候才使用let
const name = 'sa easy';
// 1. 常量是不允许被修改的 一旦给const修饰的标识符被赋值之后不能进行修改。
name = 'change';
// 2. 在使用const定义标识符,必须进行赋值
const name;
// 3. 常量的含义是指向的对象不能修改,但是可以改变对象内部的属性
const obj = {
name: 'lyp',
age: 23,
height: 181
};
obj.height = 192;
// 不能改变其指向
obj = 123;
2.6 对象字面量的增强写法
/**
* 对象字面量写法
* @type {{}}
*/
const objA = {
name: '张三',
age: 23,
run: function () {
console.log('您在干嘛');
}
};
const name = '王五';
const age = 25;
const height = 181;
/**
* ES5写法
* */
const objC = {
name: name,
age: age,
height: height
};
/**
* Es6 增强写法
* @type {{}}
*/
const objB = {
name,
age,
height
}
// 方法增强写法
/**
* ES5 写法
* @type {{run: objD.run}}
*/
const objD = {
run: function () {
}
}
/**
* ES6写法
* @type {{}}
*/
const objE = {
run() {
}
}
3. 事件监听
- 使用
v-on
绑定事件监听器,可以缩写成@
。 是它的语法糖简写形式。
3.1 v-on 的基本使用
- 下面的代码中,我们使用了
v-on:click="counter++"
。另外,我们可以将事件指向一个在methods中定义的函数。
<div id="app">
<h2>{{counter}}</h2>
<!--<button v-on:click="increment">+</button>-->
<!--<button v-on:click="decrement">-</button>-->
<!-- 语法糖简写形式 -->
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
increment() {
this.counter++;
},
decrement() {
this.counter--;
}
}
});
3.2 v-on 的参数
当通过methods
中定义方法,以供@click
调用时,需要注意参数问题:
- 情况一:如果该方法不需要额外参数,那么方法后的()可以不添加。但是注意:如果方法本身中有一个参数,那么会默认将原生事件
event
参数传递进去。
- 情况二:如果需要同时传入某个参数,同时需要
event
时,可以通过$event
传入事件。
<!-- 1. 函数中的默认参数是事件对象 event -->
<button @click="btnClick">按钮1</button>
<!-- 2. 如果明确的写出则第一个参数就是 -->
<button @click="btnClick1('abc' , $event)">按钮2</button>
<!-- 3. 传递的变量不存在而且未使用引号标明 -->
<button @click="btnClick2(abc , $event)">按钮3</button>
const app = new Vue({
el: '#app',
data: {},
methods: {
btnClick(event) {
console.log(event);// 事件对象 MouseEvent {isTrusted: true, screenX: 88, screenY: 116, clientX: 88, clientY: 13, …}
},
btnClick1(abc, event) {
console.log(abc, event); // abc MouseEvent {isTrusted: true, screenX: 88, screenY: 116, clientX: 88, clientY: 13, …}
},
btnClick2(abc,event) {
console.log(abc, event); // Property or method "abc" is not defined on the instance
// undefined MouseEvent {isTrusted: true, screenX: 152, screenY: 119, clientX: 152, clientY: 16, …}
}
}
});
3.3 v-on 的修饰符
- 在某些情况下,我们拿到
event
的目的可能是进行一些事件处理。
Vue
提供了修饰符来帮助我们方便的处理一些事件:
.stop
- 调用event.stopPropagation()
。.prevent
- 调用event.preventDefault()
。.{keyCode | keyAlias}
- 只当事件是从特定键触发时才触发回调。.native
- 监听组件根元素的原生事件。.once
- 只触发一次回调。
4. v-if、v-else-if、v-else
这三个指令与
JavaScript
的条件语句if、else、else if
类似。Vue
的条件指令可以根据表达式的值在DOM
中渲染或销毁元素或组件。
v-if 的基本使用
<div id="app">
<div v-if="isShow">
<h1>abc</h1>
<h1>abc</h1>
<h1>abc</h1>
<h1>abc</h1>
<h1>abc</h1>
</div>
<div v-else>
<h1>当isShow为false的时候显示</h1>
</div>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
isShow: true
}
});
v-if不同分数显示不同的词
<div id="app">
<div>
<h2 v-if="score>=90">优秀</h2>
<h2 v-else-if="score>=80">良好</h2>
<h2 v-else-if="score>=60">及格</h2>
<h2 v-else>不及格</h2>
</div>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el:'#app',
data:{
score:99
}
});
当出现复杂的逻辑的时候不建议在标签中使用多个v-if
标签。
<div id="app">
<div>
<h2>{{result}}</h2>
</div>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
score: 99
},
computed: {
result() {
let showMessage = '';
if (this.score >= 90) {
showMessage = '优秀';
} else if (this.score >= 80) {
showMessage = '良好';
} else if (this.score >= 60) {
showMessage = '及格';
} else {
showMessage = '不及格';
}
return showMessage;
}
}
});
v-if
的原理:v-if
后面的条件为false
时,对应的元素以及其子元素不会渲染。也就是根本没有不会有对应的标签出现在DOM
中。
4.1 条件渲染案例
- 点击切换登录方式按钮,切换不同的登录方式。
<div id="app">
<span v-if="isUser">
<label for="username">用户名:</label>
<input type="text" name="username" id="username" placeholder="输入用户名">
</span>
<span v-else="isUser">
<label for="email">邮箱名:</label>
<input type="text" name="email" id="email" placeholder="输入邮箱地址" >
</span>
<button @click="isUser = !isUser">切换类型</button>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
isUser: true
}
});
问题:如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入内容。
问题解答:这是因为
Vue
在进行DOM
渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。解决办法:如果我们不希望
Vue
出现类似重复利用的问题,可以给对应的input
添加key
,并且我们需要保证key
的不同。
5. v-for
- 当我们有一组数据需要进行渲染时,我们就可以使用
v-for
来完成。v-for
的语法类似于JavaScript
中的for
循环。格式如下:item in items
的形式。
5.1 v-for 遍历数组
<div id="app">
<ul>
<!-- 1. 获取数组的元素 -->
<li v-for="item in arr">{{item}}</li>
</ul>
<ul>
<!-- 2. 获取数组元素和 index -->
<li v-for="(item,index) in arr">{{index + 1}} . {{item}}</li>
</ul>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
arr: ['盲僧', '雷霆咆哮', '腕豪', '石头人', '瑟提', '寡妇', '男枪']
}
});
5.2 v-for 遍历对象
<div id="app">
<!-- 1. 获取对象的值 -->
<ul>
<li v-for="value in obj">{{value}}</li>
</ul>
<!-- 2. 获取对象的 键 和 值 -->
<ul>
<li v-for="(value , key) in obj">{{ key }} => {{value}}</li>
</ul>
<!-- 3. 获取对象的 value 和 key 和 index -->
<ul>
<li v-for="(value,key ,index) in obj">{{index + 1}} . {{key}} => {{value}}</li>
</ul>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
obj: {
name: '张三',
age: 23,
height: 188
}
}
});
5.3 组件的 key 属性
- 官方推荐我们在使用
v-for
时,给对应的元素或组件添加上一个:key
属性。这个key
的值需要和遍历的值是一一对应的才能提高渲染效率。
为什么需要这个key属性呢(了解)?
这个其实和
Vue
的虚拟DOM
的Diff
算法有关系。这里我们借用
React’s diff algorithm
中的一张图来简单说明一下:
说明: 当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点
我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的。
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
所以我们需要使用key
来给每个节点做一个唯一标识
Diff
算法就可以正确的识别此节点;找到正确的位置区插入新的节点。