计算属性
当需要对数据进行某种转化,并返回转化后的数据时,我们可以使用计算属性。
<h1>{{fullName}}</h1>
//注意:fullName是属性,不是函数,不用加()
data: {
firstName:'LeBron',
lastName:'James'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
看起来 computed 与 methods 都可以实现我们想要的功能,区别在于计算机属性会进行缓存,如果多次使用时,computed 属性只会调用一次。
-
计算属性有缓存,计算属性会把函数执行一次,把结果存起来,依赖的值改变,会重新赋值。
-
函数是每次模板编译都会执行。只要有响应式属性改变,视图刷新,函数就执行。
-
计算属性的 setter 和 getter
计算属性的完整写法:
computed: {
fullName: {
set: function () {
},
get: function () {
return this.firstName + ' ' + this.lastName
}
}
}
所以调用时直接使用{{fullName}}
调用属性,它并不是一个方法。
- 计算属性一般没有 set 方法(只读属性)。在上面的例子中我们只是使用 getter 来读取,在某些情况下,也可以提供一个 setter 方法:
computed: {
fullName: {
set(newValue) {
console.log('----调用了 fullName 的 set 方法')
const names = newValue.split('')
this.firstName = names[0]
this.lastName = names[1]
},
get() {
console.log('----调用了 fullName 的 get 方法')
return this.firstName + ' ' + this.lastName
}
}
}
data: {
comics: [
{id: 1, name: 'Naruto', price: 101},
{id: 2, name: 'OnePiece', price: 204},
{id: 3, name: 'Conan', price: 143},
{id: 4, name: 'Sherlock', price: 242}
]
},
computed: {
totalPrice: function () {
let result = 0;
for (let i = 0; i < this.comics.length; i++) {
result += this.comics[i].price
}
return result
}
}
根据条件筛选品牌
- 筛选框绑定到 VM 实例中的
searchName
属性:
<label>
search:
<input type="text" class="form-control" v-model="keywords">
</label>
- 在使用
v-for
指令循环每一行数据的时候,不再直接item in list
,而是in
一个过滤的methods 方法,同时,把过滤条件searchName
传递进去:
<tr v-for="item in search(keywords)" :key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td><a href="" @click.prevent="del(item.id)">delete</a></td>
</tr>
search
过滤方法中,使用 数组的filter
方法进行过滤:
search(keywords) {
return this.list.filter(item => {
if (item.name.includes(keywords)) {
return item
}
})
}
过滤器
过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。
调用格式:
//过滤器的定义语法
{{ name | 过滤器的名称 }}
//过滤器调用时的格式
//过滤器中的function,第一个参数是规定死的,永远是 过滤器 管道符前面 传过来的数据
Vue.filter('过滤器的名称',function (data) {
return data
})
可以传多个参数,也可多次调用过滤器:
<script>
Vue.filter('msgFormat', function (msg,arg,arg2) {
return msg.replace(/4/g, arg + arg2)
})
Vue.filter('test', function (msg) {
return msg+'---'
})
var vm = new Vue({
el: '#app',
data: {
msg: '111111224442233333'
},
methods: {
}
})
</script>
过滤器的调用采用就近原则
,如果私有和全局过滤器名称一致,优先调用私有过滤器。
<div id="ban">{{dt | dateFormat}}</div>
var vm2 = new Vue({
el: '#ban',
data: {
dt: new Date()
},
methods: {},
filters: {
dateFormat: function (dateStr, pattern = '') {
var dt = new Date(dateStr)
var y = dt.getFullYear()
var m = dt.getMonth() + 1
var d = dt.getDate()
// return `${y}-${m}-${d}`
if (pattern && pattern.toLowerCase() === 'yy-mm-dd') {
return `${y}-${m}-${d}`
} else {
var hh = dt.getHours()
var mm = dt.getMinutes()
var ss = dt.getSeconds()
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
}
}
})
- 使用ES6中的字符串新方法
String.prototype.padStart(maxLength, fillString='')
或String.prototype.padEnd(maxLength, fillString='')
来填充字符串;
var mm = dt.getMinutes().toString().padStart(2,'0')
使用前:2019-9-24 15:37:32
使用后:2019-09-24 15:37:58
键盘修饰符及自定义
- 通过
Vue.config.keyCodes.名称 = 按键值
来自定义案件修饰符的别名:
Vue.config.keyCodes.f2 = 113;
- 使用自定义的按键修饰符:
<input type="text" v-model="name" @keyup.f2="add">
自定义指令
- 通过
Vue.directive
定义指令
在定义时指令时不加 v- 前缀
//定义全局指令(全局不加 s,私有加 s)
//参数1:指令名称
//参数2:是一个对象,在这个对象身上有指令相关的函数,这些函数在特定阶段执行相关操作
Vue.directive('focus', {
bind: function (el) {
//每当指令绑定到元素上的时候,会立刻执行,只执行一次
//注意在每个函数中,第一个参数永远是el,表示被绑定指令的那个元素。
//在元素刚绑定指令的时候,还没插入到 DOM 中去,调用 focus 方法没有用。
//因为一个元素只有在插入 DOM 之后才能获取焦点
// el.focus()
},
inserted: function (el) { //当元素插入到DOM中的时候,会立刻执行,只执行一次
el.focus()
},
updated: function () { //当 VNode 更新的时候执行,可能会触发多次
}
})
//定义设置字体颜色指令
Vue.directive('color', {
//样式只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了内联样式
//将来元素肯定会显示到页面中,此时浏览器的渲染引擎必然会解析样式,应用给这个元素
//和样式相关的操作,一般都可以在 bind 中执行
bind: function (el,binding) {
el.style.color = binding.value
},
//和 JS 行为有关的操作,最好在 inserted 中执行,放置 JS 行为不生效
inserted: function () {
},
updated: function () {
}
})
- 指令调用
Vue中所有指令在调用时,必须以v-开头
<input type="text" class="form-control" v-model="keywords" v-focus v-color="'green'">
- 定义私有指令
directives: { //自定义私有指令
'fontweight': {
bind: function (el, binding) {
el.style.fontWeight = binding.value
}
}
}
- 私有指令调用
<div id="ban">
<p v-color="'pink'" v-fontweight="200">{{dt|dateFormat}}</p>
</div>
- 函数简写
当我们想在bind 和 update钩子上做重复动作,而不想关心其他钩子函数,可以这么写:
<div id="ban">
<p v-color="'pink'" v-fontweight="200">{{dt|dateFormat}}</p>
</div>
调用
directives: { //自定义私有指令
'fontsize': function (el, binding) {
el.style.fontSize = parseInt(binding.value) + 'px'
}
}
监测数组更新(响应式)
Vue 中包含了一组观察数组编译的方法,使用他们改变数组也会触发视图的更新。
data: {
list: ['a', 'b', 'c']
}
push()
this.list.push('d')
//(a,b,c → a,b,c,d)
pop()
:删除数组中的最后一个元素
this.list.pop()
//(a,b,c → a,b)
shift()
:删除数组中的第一个元素
this.list.shift()
//(a,b,c → b,c)
unshift()
:在数组最前面添加元素
this.list.unshift
//('d','e','f')
splice()
:删除、插入、替换元素
删除元素:第二个参数传入你要删除几个元素(不传就删除后面所有元素)
this.list.splice(1,1)
//(a,b,c → a,c)
替换元素:第二个参数表示我们要替换几个元素,后面是用于替换前面的元素
this.list.splice(1,2,'o','p')
//(a,b,c → a,o,p)
插入元素:第二个参数传入 0,后面跟上要插入的元素
this.list.splice(0,0,'e','f')
//(a,b,c → e,f,a,o,p)
sort()
:整理数组
this.list.sort()
reverse()
:反转数组
this.list.reverse()
注意:通过索引值
修改数组中的元素不是响应式的
this.list[0] = 'v'
//(a,b,c)
console.log(this.list)
//log: ["v","b","c"]
this.list.splice(0, 1, 'v')
//(v,b,c)
//set(要修改的对象,索引值,修改后的值)
vue.set(this.list, 0, 'v')
//(v,b,c)
高阶函数 filter
/map
/reduce
const nums = [10, 20, 30, 40]
filter 中的回调函数有一个要求:必须返回一个 bool 值
- 当返回 true 时,函数内部会自动将这次回调的 n 加入到新的数组中
- 当返回 false 时,函数内部会过滤掉这次的 n
let newNums = nums.filter(function (n) {
return n < 130
})
console.log(newNums) // [10, 20]
let newNums = nums.map(function (n) {
return n * 2
})
console.log(newNums) //[20, 40, 60, 80]
作用:对数组中所有内容进行汇总
let total = nums.reduce(function (preValue, n) {
return preValue + n
}, 0)
console.log(total) //100
// 遍历过程
// (preValue,n)→(10,0)
// (preValue,n)→(20,10)
// (preValue,n)→(30,30)
// (preValue,n)→(40,60)
直接结构化访问对象
let total = nums.reduce(function (preValue, movies) {
return preValue + movies.price * movies.count
}, 0)
- 链式语法
let total = nums.filter(function (n) {
return n < 100
}).map(function (n) {
return n * 2
}).reduce(function (preValue, n) {
return preValue + n
}, 0)
- 箭头函数
let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n)
ES6 语法补充
区别:let
有块级作用域,而var
没有
在 ES5 之前,因为 if 和 for 都没有块级作用域的概念,所以很多时候我们必须借助 function 的作用域来解决应用外面变量的问题。
定义 5 个按钮:
<div id="app">
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
</div>
var
(ES5)
const btns = document.getElementsByTagName('button')
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击')
})
}
//点击按钮1
第5个按钮被点击
函数是一个作用域,所以闭包可以解决作用域引起的问题
闭包
(ES5)
const btns = document.getElementsByTagName('button')
for (var i = 0; i < btns.length; i++) {
(function (i) {
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击')
})
})(i)
}
//点击按钮1
第0个按钮被点击
let
(ES6)
const btns = document.getElementsByTagName('button')
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击')
})
}
//点击按钮1
第0个按钮被点击
当我们修饰的标识符不会被再次赋值时,就可以使用 const 来保证数据的安全性。
建议:
在 ES6 的开发中,优先使用 const ,只有需要改变某一个标识符的时候才使用 let 。
- const修饰的标识符,被赋值后不能修改
const a = 20;
a = 30 //错误,const不能被改变
- 使用const定义标识符,必须赋值
const a; //错误,const修饰的标识符必须赋值
- 常量的含义是,指向的对象不能修改,但是可以改变对象内部的属性(const 指向的内存地址不变)
const obj = {
name: 'puppy',
age: 18,
height: 0.5
}
obj.name = 'cat'
obj.age = 7
console.log(obj)
{name: “cat”, age: 7, height: 0.5}
const obj = new Object() //对象
const obj = {} //对象字面量
//ES5
const obj = {
run: function () {
},
eat: function () {
}
}
//ES6
const obj = {
run() {
},
eat() {
}
}
Vue 实例的生命周期
-
什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!
-
生命周期钩子 = 生命周期函数 = 生命周期事件
-
主要的生命周期函数分类:
- 创建期间的生命周期函数:
- beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
- created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板
- beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
- mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
- 运行期间的生命周期函数:
- beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
- updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
- 销毁期间的生命周期函数:
- beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
- destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
生命周期完整示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HelloVue</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<h3 id="msg">{{ msg }}</h3>
<input type="button" value="修改 msg" @click="msg='No'">
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'datamsg'
},
methods: {
show() {
console.log('执行了 show 方法')
}
},
//注意,在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的数据都还没有初始化
beforeCreate() {
// console.log(this.msg) //报错,说明 data 内数据未被初始化
// this.show() //不执行
},
//在 created中,data 和 methods 都已经被初始化好了,
created() { //这是遇到的第二个生命周期函数
// console.log('this.msg')
// this.show()
},
//这是遇到的第三个生命周期函数,表示模板已经在内存中编译完成了,但是尚未把模板渲染到页面中
beforeMount() {
// console.log(document.getElementById('msg').innerText) //输出{{msg}}
//在 beforeMount 执行的时候,页面中的元素还没被真正替换过来,只是之前写的一些模板字符串
},
//这是遇到的第四个生命周期函数,表示内存中的模板已经真实地
mounted() {
// console.log(document.getElementById('msg').innerText) //输出 datamsg
//注意,mounted 是实例创建的最后一个生命周期函数,当执行完就表示实例已经被完全创建好了,
// 此时如果没有其他操作的话,这个实例就静静的躺在内存中不动了
},
//接下来的是运行中的两个事件,点击按钮更新 data 触发
beforeUpdate(){ //此时数据已更新,页面未更新
// console.log('界面上元素内容:'+ document.getElementById('msg').innerText) //console:界面上元素内容:datamsg
// console.log('data 中的msg数据:' + this.msg)//console:data 中的msg数据:No
},
updated(){ //此时数据与页面都已更新
console.log('界面上元素内容:'+ document.getElementById('msg').innerText) //console:界面上元素内容:No
console.log('data 中的msg数据:' + this.msg)//console:data 中的msg数据:No
}
})
</script>
</html>
参考视频:Vue黑马视频教程
参考视频:小码哥 Vue 视频教程