01. 计算属性(computed)
要求实现下面的小Demo,可以通过计算属性得到全名,但这样无法显示计算属性的优越性,所以我还用了插值语法与methods方法实现,希望对你的理解有帮助。
01. 姓名案例_插值语法实现
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_插值语法实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br /><br />
名:<input type="text" v-model="lastName"> <br /><br />
全名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span>
<!-- 要求截取前三位,还可以在{{}}添加更多需求,但是十分不推荐,应该尽量简洁 -->
<!-- 全名:<span>{{firstName+ '-' +lastName}}</span> -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
}
})
</script>
</html>
复制代码
问题:差值表达式内部写的太过复杂 不符合代码规范(不能一眼看出代码想做啥)
02. 姓名案例_methods实现
原理:每当数据发生变化,和其相关的模板函数要重新解析(无关的不会解析)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_methods实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br /><br />
名:<input type="text" v-model="lastName"> <br /><br />
全名:<span>{{fullName()}}</span>
//fullName带括号表示将函数的返回值展示出来
</div>
</body>
<!-- data中的数据发生改变,vue模板会重新解析,对data重新读取,如果有在模板里面调方法,方法也会重新被调用 -->
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
methods: {
fullName() {
console.log('@---fullName')
return this.firstName + '-' + this.lastName
}
},
})
</script>
</html>
缺点: 每次与fullName()相关的变量发生变动时,fullName()都会变化
03. 姓名案例_计算属性实现
vue将data中的数据视为属性
计算属性:
- 1.定义: 要用的属性不存在,要通过已有属性(Vue实例中的属性)计算得来 (使用let或者 var定义的变量无法被vue实例监听到)。
- 2.原理: 底层借助了Object.defineproperty方法提供的getter和setter。
- 3.get函数什么时候执行?
- (1).初次读取时会执行一次。
- (2).当依赖的数据发生改变时会被再次调用。
- 4.优势: 与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
- 5.备注:
- 1.计算属性最终会出现在vm上,直接读取使用即可。不能写fullName.get(),没有这种写法。
- 2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变(set中要有修改属性的代码)。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_计算属性实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br /><br />
名:<input type="text" v-model="lastName"> <br /><br />
测试:<input type="text" v-model="x"> <br /><br />
全名:<span>{{fullName}}</span> <br /><br />
全名:<span>{{fullName}}</span> <br /><br />
全名:<span>{{fullName}}</span> <br /><br />
全名:<span>{{fullName}}</span>
<!-- 多个fullName初始只会调用一次get(),因为缓存了 ,用methods方法调用没有缓存-->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三',
x: '你好'
},
computed: {
fullName: {
//get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
//get什么时候调用? 1.初次读取fullName时。 2.所依赖的数据发生变化时。
get() {
console.log('get被调用了')
console.log(this) //此处的this是vm
return this.firstName + '-' + this.lastName
},
//set什么时候调用? 当fullName被修改时。
set(value) {
console.log('set', value)
const arr = value.split('-')
// 用-做分隔符,将其变为数组
this.firstName = arr[0]
this.lastName = arr[1]
//可以用vm.fullName = '李-四'更改fullname的值
}
//在多数情况下只考虑读取不考虑修改,可以把set部分删掉,简写
//注意上方模板{{}}中依然放fullName,不带括号,这里写函数是关注函数的返回值。
// fullName(){
// console.log('get被调用了')
// return this.firstName + '-' + this.lastName
//}
}
}
})
</script>
</html>
02. 监视属性(侦听器watch)
01.天气案例-监视属性
认识监视属性watch:
通过通过vm对象的
$watch()
或watch配置
来监视指定的属性当属性变化时, 回调函数自动调用, 在函数内部进行计算
-
1.当被监视的属性变化时, 回调函数自动调用(watch中调用的handler函数), 进行相关操作
-
2.监视的属性必须存在,才能进行监视!!
-
3.监视的两种写法:
-
(1).new Vue时传入watch配置
watch:{ isHot:{ immediate:true, //初始化时让handler调用一下 简写的话默认是false //handler什么时候调用?当isHot发生改变时。 handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } } }
-
(2).通过vm.$watch监视
```js
//注意变量要加双引号
vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
```
深度监视:
-
(1).Vue中的watch默认不监测对象内部值的改变(针对引用值只监听一层)。
-
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
-
(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
-
(2).使用watch时根据数据的具体结构,决定是否采用深度监视。
//正常写法
isHot:{
// immediate:true, //初始化时让handler调用一下
// deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
//简写 简写的话immediate默认是false
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
}
实例任务:按钮控制天气在’炎热’和‘凉爽’两者之间切换 常规方案methods也可以实现 现在有了监听属性有了更方便的写法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_监视属性</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 01.watch基础应用->
<h2>今天天气很{{info}} {{x}}</h2>
<!-- 绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句
比如下面两句功能相同,但是要记住模板里面找数据从vm内找,有些语句放入报错 -->
<button @click="isHot = !isHot;x++">切换天气</button>
<button @click="changeWeather">切换天气</button>
<hr />
<!-- 02.watch深度监听 -->
深度监视按钮:
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a+1</button>
<h3>b的值是:{{numbers.b}}</h3>
<button @click="numbers.b++">点我让b+1</button>
{{numbers.c.d.e}}
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
isHot: true,
x: 0,
numbers: {
a: 1,
b: 1,
c: {
d: {
e: 100
}
}
}
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
this.x++
}
},
// 方法一
/* watch:{
isHot:{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
}
//简写:不用immediate:true,deep: true等属性时可以简写
// isHot(newValue, oldValue) {
// console.log('isHot被修改了', newValue, oldValue, this)
// }
*/
//深度监视:
//监视多级结构中某个属性的变化,注意带引号
/* 'numbers.a':{
handler(){
console.log('a被改变了')
}
}
但如果多级结构中属性太多的话太过繁琐
*/
//监视多级结构中所有属性的变化
numbers: {
deep: true,//deep开启深度监视,不开启的话只监视numbers变化,不能看到numbers内的数据变化
handler() {
console.log('numbers改变了')
}
}
})
// 方法二
vm.$watch('isHot', {//注意带引号
immediate: true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
}
})
//简写
// vm.$watch('isHot', function (newValue, oldValue) {
// console.log('isHot被修改了', newValue, oldValue, this)
// })
</script>
</html>
复制代码
02.姓名案例_watch实现
通过watch实现的姓名案例与上方计算属性实现的姓名案例比较:
<div id="demo">{{ fullName }}</div>
watch实现
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
computed实现
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
computed和watch之间的区别:
-
computed能完成的功能,watch都可以完成。
-
watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。computed不可以,因为computed依赖返回值得到结果。而watch则是得到属性改变的结果。
-
例:让名字延迟1s再打印
-
watch能够实现 本质是watch无需通过一个返回值来实现任务,watch是通过让你自己执行代码来实现数据修改
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { //异步函数 setTimeout(()=>{ this.fullName = val + ' ' + this.lastName },1000) }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } })
-
使用couputed则不能实现 本质 计算属性的实现重要的是返回值 我们无法做到等一会儿再返回一个值(返回异步值)
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { setTimeout(()=>{ //这里return 是将值返回给了这个匿名函数 外部的函数实际返回的是undefined return this.firstName + ' ' + this.lastName },1000) } } })
-
-
两个重要的小原则:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_watch实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br /><br />
名:<input type="text" v-model="lastName"> <br /><br />
全名:<span>{{fullName}}</span> <br /><br />
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三',
fullName: '张-三'
},
watch: {
//简写
firstName(newValue) {
//此处定时器内部若不写成箭头函数 则内部的this会指向 windows 因为异步任务由js引擎管理 而不是vue实例 写成箭头函数 才会向外找 this
setTimeout(() => {
console.log(this)
this.fullName = newValue + '-' + this.lastName
}, 1000);
},
lastName(newValue) {
this.fullName = this.firstName + '-' + newValue
}
}
})
</script>
</html>
03. 绑定样式
绑定样式:
1. class样式绑定
常规写法:class = 'class1,class2'
v-bind 写法:class="xxx" xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。 :class="mood"
对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。 :class="['yk1', 'yk2', 'yk3']"
对象适用于:要绑定多个样式,个数确定,名字也确定,但是需要动态决定用不用。 :class="{ yk1: true,yk2: false,yk3: true}"
01.字符串
表达式是字符串:
'classA'
适用于:类名不确定,要动态获取
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绑定样式</title>
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
.happy {
border: 4px solid red;
;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg, yellow, pink, orange, yellow);
}
.sad {
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}
.normal {
background-color: skyblue;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<!-- 01.v-bind绑定类 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: 'YK菌',
mood: 'normal'//02.变量存储类名
},
methods: {
//03.方法修改变量名 每点击一次 类名随机修改一次
changeMood() {
const arr = ['happy', 'sad', 'normal']
const index = Math.floor(Math.random() * 3)//取 0-3的随机数
this.mood = arr[index]
}
},
})
</script>
</html>
02.数组
表达式是数组:
['classA', 'classB']
适用于: 要绑定多个样式,个数确定,名字也确定,但不确定用不用
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绑定样式</title>
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
.yk1 {
background-color: yellowgreen;
}
.yk2 {
font-size: 30px;
text-shadow: 2px 2px 10px red;
}
.yk3 {
border-radius: 20px;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<!-- 01.v-bond绑定一个数组-->
<div class="basic" :class="classArr">{{name}}</div> <br /><br />
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: 'YK菌',
//数组中存储 多个类名
classArr: ['yk1', 'yk2', 'yk3']
},
})
</script>
</html>
03.对象
表达式是对象:
{classA:isA, classB: isB}
适用于:要绑定多个样式,个数确定,名字也确定,但是需要动态决定用不用
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绑定样式</title>
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
.yk1 {
background-color: yellowgreen;
}
.yk2 {
font-size: 30px;
text-shadow: 2px 2px 10px red;
}
.yk3 {
border-radius: 20px;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: 'YK菌',
classObj: {
yk1: true,
yk2: false,
yk3: true
}
}
})
</script>
</html>
2. style样式绑定
基础使用 style = 'background:red'
v-bind使用 :
对象::style = '{backgroundColor: 'orange'}'
数组(用的很少): :style = '[{backgroundColor: 'orange'},{color: 'red'}]'
01.对象
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绑定样式</title>
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj, styleObj2">{{name}}</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: 'YK菌',
styleObj: {
fontSize: '40px',
color: 'red',
},
styleObj2: {
backgroundColor: 'orange'
},
},
})
</script>
</html>
02.数组
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绑定样式</title>
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 绑定style样式--数组写法 -->
<div class="basic" :style="styleArr">{{name}}</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: 'YK菌',
styleArr: [{
fontSize: '40px',
color: 'blue',
},
{
backgroundColor: 'gray'
}
]
},
})
</script>
</html>
04. 条件渲染
条件渲染:
1.v-if
写法:
(1).v-if="表达式"
(2).v-else-if="表达式"
(3).v-else="表达式"
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
附:v-if可以搭配template使用,不影响页面布局(注意和v-show使用 v-show会失效)
2.v-show
写法:v-show="表达式"
适用于:切换频率较高的场景。
特点: ,仅仅是使用样式隐藏掉,display:none
3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
实例演示
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>条件渲染</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
<!-- 使用v-show做条件渲染 元素隐藏-->
<!-- <h2 v-show="false">欢迎来到{{name}}</h2>
<h2 v-show="true">欢迎来到{{name}}</h2> -->
<!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2> -->
<!-- 使用v-if做条件渲染 元素直接被移除-->
<!-- <h2 v-if="false">欢迎来到{{name}}</h2> -->
<!-- <h2 v-if="1 === 1">欢迎来到{{name}}</h2> -->
<!-- <h2 v-show="n === 1">你好</h2>
<h2 v-show="n===2">百度</h2>
<h2 v-show="n===3">北京</h2> -->
<h2 v-if="n===1">你好</h2>
<h2 v-if="n===1">百度</h2>
<h2 v-if="n===3">北京</h2>
<!-- v-else和v-else-if -->
<div v-if="n === 1">Angular</div>
<div v-else-if="n === 1">React</div> //这里react不显示
<div v-else-if="n === 3">Vue</div>
<div v-else>哈哈</div>
<!-- v-if与template的配合使用,template不能与v-show一起使用 -->
<!-- <template v-if="n === 1">
<h2>你好</h2>
<h2>百度</h2>
<h2>北京</h2>
</template> -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: '百度',
n: 0
}
})
</script>
</html>
05. 列表渲染
01. 建立一个基本列表
v-for的基本使用
- 用于展示列表数据
- 语法:v-for="(item, index) in xxx" :key=“yyy”
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
v-for
指令需要使用item in items
形式的特殊语法,其中items
是源数据数组,而item
则是被迭代的数组元素的别名(形参)。v-for
还支持一个可选的第二个参数,即当前项的索引。- 可以用
of
替代in
作为分隔符,因为它更接近 JavaScript 迭代器的语法:
- 数组: (item, index)
- 对象: (value, key)
- 字符串:(char, index)
- 数字:(number, index)
实例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表(遍历数组)</h2>
<ul>
<li v-for="(p,index) of persons" :key="p.id">
<!-- {{p}} -->
{{p.name}}-{{p.age}}--{{p.id}}
</li>
</ul>
<!-- 遍历对象 -->
<h2>汽车信息(遍历对象)</h2>
<ul>
<li v-for="(value,name) of car" :key="name">
{{name}}-{{value}}
</li>
</ul>
<!-- 遍历字符串 -->
<h2>测试遍历字符串(用得少)</h2>
<ul>
<li v-for="(char,index) of str" :key="index">
{{char}}-{{index}}
</li>
</ul>
<!-- 遍历指定次数 -->
<h2>测试遍历指定次数(用得少)</h2>
<ul>
<li v-for="(number,index) of 5" :key="index">
{{index}}-{{number}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
persons: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 }
],
car: {
name: '奥迪A8',
price: '70万',
color: '黑色'
},
str: 'hello'
}
})
</script>
</html>
02. key的原理
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
面试题:react、vue中的key有什么作用?(key的内部原理)
虚拟DOM中key的作用:
是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。 (2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。
用index作为key可能会引发的问题:
若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
使用index作为key是没有问题的。
3.如果不写key,则默认为index
实例问题: 列表渲染, 若key指定为index,当在li前面插入一个新的li li右侧的input输入框会发生错乱,输入框的数据会对不上号
- index渲染(没绑定key也会默认按此方式渲染)
- 唯一id渲染
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>key的原理</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 遍历数组 :key="index"-->
<h2>人员列表(遍历数组):key="index"</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
<!-- 遍历数组 -->
<h2>人员列表(遍历数组):key="p.id"</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) of persons" :key="p.id">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
persons: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 }
]
},
methods: {
add() {
const p = { id: '004', name: '老刘', age: 40 }
this.persons.unshift(p)
//将p插入到persons的开头
}
},
})
</script>
</html>
复制代码
初始输入:
点击添加一个老刘:
03. 列表过滤
用computed实现(推荐)
实现思路:计算属性fillPersons的值受keyWord的值变化而变化
filPerons() { return this.persons.filter((p) => { return p.name.indexOf(this.keyWord) !== -1 }) }
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>列表过滤</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 1.收集用户输入
2.拿用户输入的东西进行数据匹配
-->
<!-- 准备好一个容器-->
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="(p,index) of filPerons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
// 用computed实现
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
]
},
computed: {
filPerons() {
return this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
</script>
</html>
用watch实现
思路:监听输入框的数据变化,使用过滤方法迭代数据,筛选数据后重新渲染li
注意indexOf()方法搜索空串会返回0,所以任何数据都会通过筛选,设置watch初始调用可解决数据第一次更新为控问题
watch: { keyWord: { immediate: true, // 初始化的时候让handler调用一下,因为开始filPerons: []为空数组, // 页面上什么内容都不显示,而恰巧handler(val)获取的值为空, // 而所有字符串第0位不仅包含自己第一个字符,也包含着一个空字符,p.name.indexOf('')为0 // 所以handler(val)函数判断成功,页面显示内容 handler(val) { this.filPerons = this.persons.filter((p) => { return p.name.indexOf(val) !== -1 // 判断p.name中是否包含有val值,如果不等于-1,则说明包含 // filter返回一个全新的数组,原数组不变 }) } } }
// #region 开始折叠
//将下面代码放入上方代码块中
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
],
filPerons: []
},
watch: {
keyWord: {
immediate: true,
// 初始化的时候让handler调用一下,因为开始filPerons: []为空数组,
// 页面上什么内容都不显示,而恰巧handler(val)获取的值为空,
// 而所有字符串第0位不仅包含自己第一个字符,也包含着一个空字符,p.name.indexOf('')为0
// 所以handler(val)函数判断成功,页面显示内容
handler(val) {
this.filPerons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
// 判断p.name中是否包含有val值,如果不等于-1,则说明包含
// filter返回一个全新的数组,原数组不变
})
}
}
}
})
复制代码
初始值:
过滤后:
04. 列表排序
在之前的列表的基础上添加年龄升序、降序、原序功能
实现思路: 对之前过滤剩下的数据进行判断操作,计算属性的强大之处在于其内部每一个数据的改变都会触发计算属性
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>列表排序</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<button @click="sortType = 2">年龄升序</button>
<button @click="sortType = 1">年龄降序</button>
<button @click="sortType = 0">原顺序</button>
<ul>
<li v-for="(p,index) of filPerons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
keyWord: '',
sortType: 0, //0原顺序 1降序 2升序
persons: [
{ id: '001', name: '马冬梅', age: 30, sex: '女' },
{ id: '002', name: '周冬雨', age: 31, sex: '女' },
{ id: '003', name: '周杰伦', age: 18, sex: '男' },
{ id: '004', name: '温兆伦', age: 19, sex: '男' }
]
},
computed: {
filPerons() {
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
//判断一下是否需要排序
if (this.sortType !== 0) {
arr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
})
}
return arr
}
}
})
</script>
</html>
05. 数据监测( Vue.set)
数据监测存在的问题
问题引入:v-for中渲染的列表中的数据,若是对属性进行单个修改,可以渲染,若是整体更改引用值,则不能监听(和vue监听数据的原理油管)
vue data数据是如何渲染监听的
vue实例中的data中存储的数据,最终在vue实例属性(vm)中也会有(数据代理的概念)
快速回顾 vm中的实例属性来自于vm._data,而之前提到vm._data是来自与vue实例中的data的直接赋值–> vm._data = data
实际上,data是经过处理加工后才传给vm._data的,如下图所示,两者有明显的区别(区别在于get和set方法),所以所做的加工就是将data中每一个key:value都形成set和get的写法
**
a.模拟一个数据监测对象(解答vue是如何检测对象中的数据变化)
瓜皮写法
进阶写法
会出现死循环的写法
比较接近vue源码的写法
1,obs把data的数据全部拷贝下来并创建get set(也就是做了data的数据代理 实际上思路就是定义一个旁观者去调用data的属性值,避免死循环)
2,把变量data的地址换成obs,今后需要对原data地址里的数据做更改时就只能通过obs来实现(不然就用不了obs里面的侦听逻辑) 引用赋值的妙用
3, 最后把obs代给vm._data,vm里面会用一次defineProperty方便我们只用vm.name就能访问或修改
4,总结一下就是vm做了obs的数据代理,obs又做了原始data的数据代理<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 1,obs把data的数据全部拷贝下来并创建get set(也就是做了data的数据代理 实际上思路就是定义一个旁观者去调用data的属性值,避免死循环) //2,把变量data的地址换成obs,今后需要对原data地址里的数据做更改时就只能通过obs来实现(不然就用不了obs里面的侦听逻辑) 引用赋值的妙用 // 3,最后把obs代给vm._data,vm里面会用一次defineProperty方便我们只用vm.name就能访问或修改 //4,总结一下就是vm做了obs的数据代理,obs又做了原始data的数据代理 let data = { name: "尚硅谷", address: '北京' } // a.创建一个监视的实例对象,用于监视data中属性的变化 data中的属性全部都装载进入了监视函数的实例对象 const obs = new Observer(data)//实际上思路就是定义一个旁观者去调用data的属性值,避免死循环 说的正经点叫 obs做了data的数据代理 使用一个中介 避免无限循环 // 准备一个实例对象 let vm = {} data = obs vm._data = obs//两个都需要连等 要把原来的data 和 vm_data一起改掉 console.log(vm); // b.封装监视函数 function Observer(obj) { // b.汇总对象中的所有的属性形成一个数组 const key = Object.keys(obj) // c.遍历 key.forEach(item => { // d.给Observer的实例对象添加属性 Object.defineProperty(this, item, { get() { // e.将对象传入的data的属性值传出 return obj[item] }, set(val) { console.log(`${k}被改变了,我要去解析模板,生成虚拟dom....我要开始忙了`); // f.收到你传入的值 然后将对象的属性值改掉 obj[k] = val } }) }) } </script> </body> </html>
vue源码的写法做的更多的地方
递归寻找data中的对象内置的对象属性,依次添加set,get
b.Vue.set()的使用
1.问题引出
问题:vue中对象中的数据在非初始状态下,后面用“点的”方式添加的属性,不具备set和get方法,无法触发响应式
解决方案
Vue.set方法添加属性
vue方法的局限性
前一个例子的数据的创建方式是在data预先创建的一个对象的中,如果想在data中直接创建一个新的属性(之前未定义过)就会报错
原因
2.vue.set方法详解
Vue.set(添加目标,‘添加属性’,‘属性值’)
- Vue.set(this.student,‘sex’,‘男’)
//添加目标可以用this.student,也可以用vm.student(_data因为数据代理可省略)
- this.$set(this.student, ‘sex’, ‘男’)
原理:
向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi'
(vm.上面的对象.添加的新属性 = ‘属性值’))
注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Vue监测数据改变的原理</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h1>学校信息</h1>
<h2>学校名称:{{school.name}}</h2>
<h2>学校地址:{{school.address}}</h2>
<h2 v-if="school.leader">校长是:{{school.leader}}</h2>
<button @click='addRen'>添加校长</button>
<hr />
<h1>学生信息</h1>
<button @click="addSex">添加一个性别属性,默认值是男</button>
<h2>姓名:{{student.name}}</h2>
<h2 v-if="student.sex">性别:{{student.sex}}</h2>
<h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2>
<h2>朋友们</h2>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
school: {
name: '尚硅谷',
address: '北京',
},
student: {
name: 'tom',
age: {
rAge: 40,
sAge: 29,
},
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 }
]
}
},
methods: {
addSex() {
// Vue.set(this.student,'sex','男')
// Vue.set(添加目标,'添加属性','属性值')
this.$set(this.student, 'sex', '男')
},
addRen() {
// Vue.set(添加目标,'添加属性','属性值')
this.$set(this.school, 'leader', '一个教授')
}
}
})
</script>
</html>
复制代码
c.Vue检测对象/数组中的元素改变
数组的监听形式的问题
查看vm_data中的数据形式
通过索引的方式修改数组值是无法监听到双向数据变化的
解决方案1
解决方案2
Vue监视数据的原理:
1. vue会监视data中所有层次的数据。
2. 如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理,即改变其值页面无变化
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm. s e t ( t a r g e t , p r o p e r t y N a m e / i n d e x , v a l u e ) ∗ ∗ ∗ ∗ / / v m . set(target,propertyName/index,value)** **//vm. set(target,propertyName/index,value)∗∗∗∗//vm.set(添加目标,‘添加属性/索引值’,‘属性值’)
3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm. s e t ( ) ∗ ∗ ∗ ∗ 3. 如 果 不 使 用 上 面 方 法 而 是 直 接 对 数 组 赋 值 则 v u e 无 法 响 应 , 例 如 : ∗ ∗ ∗ ∗ t h i s . s t u d e n t . h o b b y [ 0 ] = " 开 车 " , 数 据 已 被 更 改 , 但 页 面 中 无 任 何 反 应 ∗ ∗ ∗ ∗ 特 别 注 意 : V u e . s e t ( ) 和 v m . set()** **3.如果不使用上面方法而是直接对数组赋值则vue无法响应,例如:** **this.student.hobby[0] = "开车",数据已被更改,但页面中无任何反应** **特别注意:Vue.set() 和 vm. set()∗∗∗∗3.如果不使用上面方法而是直接对数组赋值则vue无法响应,例如:∗∗∗∗this.student.hobby[0]="开车",数据已被更改,但页面中无任何反应∗∗∗∗特别注意:Vue.set()和vm.set() 不能给vm 或 vm的根数据对象(vm._data) 添加属性!!!
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>总结数据监视</title>
<style>
button {
margin-top: 10px;
}
</style>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button> <br />
<button @click="addSex">添加性别属性,默认值:男</button> <br />
<button @click="student.sex = '未知' ">修改性别</button> <br />
<button @click="addFriend">在列表首位添加一个朋友</button> <br />
<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button> <br />
<button @click="addHobby">添加一个爱好</button> <br />
<button @click="updateHobby">修改第一个爱好为:开车</button> <br />
<button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br />
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<h3 v-if="student.sex">性别:{{student.sex}}</h3>
<h3>爱好:</h3>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h3>朋友们:</h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
student: {
name: 'tom',
age: 18,
hobby: ['抽烟', '喝酒', '烫头'],
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 }
]
}
},
methods: {
addSex() {
// Vue.set(this.student,'sex','男')
// this.$set(this.student,'sex','男')
Vue.set(vm._data.student, 'sex', '男')
// 往student对象再追加属性
},
addFriend() {
this.student.friends.unshift({ name: 'jack', age: 70 })
},
updateFirstFriendName() {
// 直接this.student.friends[0] = 'xx' 是错的,数组不能直接修改
this.student.friends[0].name = '张三'
},
addHobby() {
this.student.hobby.push('学习')
},
updateHobby() {
// this.student.hobby.splice(0,1,'开车') 用数组属性修改元素
// Vue.set(this.student.hobby,0,'开车') 直接使用Vue.set修改
this.$set(this.student.hobby, 0, '开车')
},
removeSmoke() {
// filter()、concat() 和 slice()。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组:
this.student.hobby = this.student.hobby.filter((hobby) => {
// 所有不是由vue控制的回调,尽可能写成箭头函数 (避免this的指向变为windows)
return hobby !== '抽烟'
})
}
}
})
</script>
</html>
06.收集表单数据 v-model详解
收集表单数据:
01.若:,则v-model默认收集的是value值,用户输入的就是value值。
02.若:,则v-model默认收集的是value值,用户输入的就是value值。
03.若:,则v-model默认收集的是value值,因为此类型无法输入内容,则无法
通过输入得到value值,所以要给标签手动添加value值。
04.若:
1.没有配置input的value属性,那么默认读取的的就是checked是否被勾选(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>收集表单数据</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" v-model.trim="userInfo.account">
<br/><br/>
密码:<input type="password" v-model="userInfo.password"> <br/><br/>
年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
//v-model默认收集的是value值,用户输入的就是value值。
性别:
// 因为此类型无法输入内容,则无法通过输入得到value值,所以要给标签手动添加value值。
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female">
<br/><br/>
爱好:
学习<input type="checkbox" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
<br/><br/>
所属校区
//如果是select,则value与v-model绑定的city属性的初始值相同的为初始默认
值,如果city初始值为空,则默认为第一个。
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select>
<br/><br/>
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
age:18,
sex:'female',
hobby:[],
//v-model的初始值是数组,那么收集的的就是value组成的数组
city:'beijing',
other:'',
agree:''
}
},
methods: {
demo(){
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
</html>
复制代码
07.过滤器(filter)
附:常用的js库可以再 BootCDN找到
我们接下来实验到的实例用到了 day.js
显示当前时间:三种写法
计算属性
方法函数
过滤器写法
-
//局部过滤器 filters: { //如果不传参数,则使用默认参数'YYYY年MM月DD日 HH:mm:ss',如果传参数,则使用 //传入的参数'YYYY_MM_DD' timeFormater(value, str = 'YYYY年MM月DD日 HH:mm:ss') { // console.log('@',value) return dayjs(value).format(str) } }
-
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。**
语法:
1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
备注:
1.过滤器也可以接收额外参数、多个过滤器也可以串联
2.并没有改变原本的数据, 是产生新的对应的数据
3.不是必须的属性,完全可以用methods和computed实现下面代码中的过滤功能
4.当全局过滤器和局部过滤器重名时,会采用局部过滤器。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>过滤器</title>
<script type="text/javascript" src="../js/vue.js"></script>
<!-- <script type="text/javascript" src="../js/dayjs.min.js"></script> -->
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 计算属性实现 -->
<h3>计算属性实现:{{fmtTime}}</h3>
<!-- methods实现 -->
<h3>methods实现:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3>过滤器实现:{{time | timeFormater}}</h3> //将time当参数传给timeFormater
<!-- 过滤器实现(传参) -->
<h3>过滤器实现(传参):{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
<h3 :x="msg | mySlice">{{msg}}</h3>
</div>
<div id="root2">
<h2>{{msg | mySlice}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//全局过滤器,必须在new Vue({})之前
Vue.filter('mySlice', function (value) {
return value.slice(0, 4)
})
new Vue({
el: '#root',
data: {
time: 1621561377603, //时间戳
msg: '你好,尚硅谷'
},
computed: {
fmtTime() {
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
}
},
methods: {
getFmtTime() {
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
}
},
// filters: {
// timeFormater(value) {
// return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
// }
// }
//局部过滤器
filters: {
//如果不传参数,则使用默认参数'YYYY年MM月DD日 HH:mm:ss',如果传参数,则使用
//传入的参数'YYYY_MM_DD'
timeFormater(value, str = 'YYYY年MM月DD日 HH:mm:ss') {
// console.log('@',value)
return dayjs(value).format(str)
}
}
})
new Vue({
el: '#root2',
data: {
msg: 'hello,atguigu!'
}
})
</script>
</html>
过滤器也可以串联**
2.并没有改变原本的数据, 是产生新的对应的数据
3.不是必须的属性,完全可以用methods和computed实现下面代码中的过滤功能
4.当全局过滤器和局部过滤器重名时,会采用局部过滤器。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>过滤器</title>
<script type="text/javascript" src="../js/vue.js"></script>
<!-- <script type="text/javascript" src="../js/dayjs.min.js"></script> -->
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 计算属性实现 -->
<h3>计算属性实现:{{fmtTime}}</h3>
<!-- methods实现 -->
<h3>methods实现:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3>过滤器实现:{{time | timeFormater}}</h3> //将time当参数传给timeFormater
<!-- 过滤器实现(传参) -->
<h3>过滤器实现(传参):{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
<h3 :x="msg | mySlice">{{msg}}</h3>
</div>
<div id="root2">
<h2>{{msg | mySlice}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//全局过滤器,必须在new Vue({})之前
Vue.filter('mySlice', function (value) {
return value.slice(0, 4)
})
new Vue({
el: '#root',
data: {
time: 1621561377603, //时间戳
msg: '你好,尚硅谷'
},
computed: {
fmtTime() {
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
}
},
methods: {
getFmtTime() {
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
}
},
// filters: {
// timeFormater(value) {
// return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
// }
// }
//局部过滤器
filters: {
//如果不传参数,则使用默认参数'YYYY年MM月DD日 HH:mm:ss',如果传参数,则使用
//传入的参数'YYYY_MM_DD'
timeFormater(value, str = 'YYYY年MM月DD日 HH:mm:ss') {
// console.log('@',value)
return dayjs(value).format(str)
}
}
})
new Vue({
el: '#root2',
data: {
msg: 'hello,atguigu!'
}
})
</script>
</html>