目录
1.Vue 核心
1.1. Vue 简介
1.1.1 介绍与描述
1.1.2 Vue 的特点
1.2 模板语法
1.2.1 模板的分类
1.2.2 插值语法
1.2.3 指令语法
1.2.4 实例
1.3 数据绑定
1.3.1 单向数据绑定
1.3.2 双向数据绑定
1.3.3 MVVM 模型
1.3.4 data与el的2种写法
1.3.5 实例
1.3.6 Object.defineProperty方法的介绍
1.3.7 Object.defineProperty实现的数据代理
1.3.8 Vue里面的数据代理
1.4 事件处理
1.4.1 事件的基本使用
1.4.2 事件修饰符
1.4.3 键盘事件
1.5 计算属性与监视
1.5.1 计算属性-computed
1.5.2 天气案例
1.5.3 监视属性-watch
1.5.4 深度监视-watch-deep
1.5.5 计算属性和监视属性的比较
1.6 class 与 style 绑定
1.7 条件渲染 v-if || v-show
1.7.1 条件渲染指令
1.7.2 比较 v-if 与 v-show
1.8 列表渲染 v-for
1.8.1 v-for的了解
1.8.2 v-for中的key原理
1.8.3 列表过滤案例
1.8.4 列表排序案例
1.Vue 核心
1.1. Vue 简介
1.1.1 介绍与描述
Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架。
中文官网:https://v2.cn.vuejs.org/v2/guide/installation.html
- 1.下载vue.js
- 2.然后在浏览器中下载 Vue Devtools 插件
- 3.在项目中引入vue.js文件,就可以使用
1.1.2 Vue 的特点
-
渐进式框架:Vue 可以逐步采用,允许开发者在需要的地方添加(插入)其功能,而不必重写整个项目。
-
组件化:Vue 的核心是组件。每个组件都有自己的数据、模板和逻辑,使得代码更清晰、更易于维护和复用。
-
双向数据绑定:使用 Vue 的数据绑定,模型(数据)和视图(UI)之间会保持同步。任何一方的变化都会自动反映到另一方。遵循 MVVM 模式
-
虚拟 DOM:Vue 通过虚拟 DOM 提高了渲染性能。它只会对变化的部分进行更新,而不是重新渲染整个页面。
-
灵活性:Vue 提供了多种选项,可以使用模板语法、JSX 或直接使用 JavaScript 来定义组件。
1.2 模板语法
1.2.1 模板的分类
- 1. 插值语法(双大括号表达式)
- 2. 指令(以 v-开头)
1.2.2 插值语法
- 示例:
- 数学运算:
3 + 5
,结果为8
。 - 变量赋值:
x = 10
,x
是一个表达式,结果为10
。 - 函数调用:
Math.max(1, 2)
,结果为2
。
- 数学运算:
语句:语句是执行特定操作的指令,它并不返回值,而是执行一个动作。语句通常用于控制程序的流向。
- 示例:
- 赋值语句:
let x = 10;
,这条语句将10
赋值给变量x
。 - 条件语句:
if (x > 5) { console.log('大于5'); }
,这是一个条件语句,根据条件执行特定代码块。 - 循环语句:
for (let i = 0; i < 5; i++) { console.log(i); }
,用于重复执行某段代码。
- 赋值语句:
1.2.3 指令语法
1.2.4 实例
初识Vue:
- 1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
- 2.root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
- 3.root容器里的代码被称为【Vue模板】;
- 4.Vue实例和容器是一一对应的;
- 5.真实开发中只有一个Vue实例,并且会配合着组件一起使用;
- 6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>初识Vue</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="demo">
<h1>Hello,{{name.toUpperCase()}},{{address}}</h1>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
//创建Vue实例
new Vue({
el:'#demo', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。
data:{ //data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象。
name:'atguigu',
address:'北京'
}
})
</script>
</body>
</html>
1.3 数据绑定
1.3.1 单向数据绑定
1.3.2 双向数据绑定
收集表单数据 v-model的使用:
若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
若:<input type="checkbox"/>
- 1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- 2.配置input的value属性:
- (1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- (2)v-model的初始值是数组,那么收集的的就是value组成的数组
v-model的三个修饰符:
- lazy:失去焦点再收集数据
- number:输入字符串转为有效的数字
- trim:输入首尾空格过滤
案例:
需求:使用v-model完成双向绑定
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>收集表单数据</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
密码:<input type="password" v-model="userInfo.password"> <br/><br/>
年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
性别:
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>
爱好:
学习<input type="checkbox" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
<br/><br/>
所属校区
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select>
<br/><br/>
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
age:18,
sex:'female',
hobby:[],
city:'beijing',
other:'',
agree:''
}
},
methods: {
demo(){
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
</html>
1.3.3 MVVM 模型
- M:模型(Model) :对应 data 中的数据对象
- V:视图(View) :模板,也就是 vue.js 的表达式
- VM:视图模型(ViewModel) : Vue 实例对象,相当于中间件。
1.3.4 data与el的2种写法
1.el有2种写法
- (1).new Vue时候配置el属性。
- (2).先创建Vue实例,随后再通过vm.$mount('#root')指定el的值。
- (1).对象式
- (2).函数式
3.一个重要的原则: 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了,因为后续会经常用到原型对象。
1.3.5 实例
验证单双向数据绑定
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>数据绑定</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!--
Vue中有2种数据绑定的方式:
1.单向绑定(v-bind):数据只能从data流向页面。
2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
备注:
1.双向绑定一般都应用在表单类元素上(如:input、select等)
2.v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。
-->
<!-- 准备好一个容器-->
<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-model只能应用在表单类元素(输入类元素)上 -->
<!-- <h2 v-model:x="name">你好啊</h2> -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
1.3.6 Object.defineProperty方法的介绍
Object.defineProperty
是 JavaScript 中的一个方法,用于在对象上定义新属性或修改现有属性的特性。
其基本语法如下:
Object.defineProperty(obj, prop, descriptor)
参数说明:
- obj:要在其上定义属性的对象。
- prop:要定义或修改的属性名。字符串形式。
- descriptor:描述符对象,包含了属性的特性。是一个配置对象形式。
描述符对象可以包含以下几个键:
- value:属性的值。
- writable:布尔值,指示属性是否可以被赋值(默认为
false
)。 - enumerable:布尔值,指示属性是否可以在
for...in
循环中被枚举(默认为false
)。 - configurable:布尔值,指示属性是否可以被删除或修改(默认为
false
) - get:返回具体的值,当有人读取 obj 的 prop 属性时,get函数(getter)就会被调用,且返回值就是 prop 的值
- set:当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>回顾Object.defineproperty方法</title>
</head>
<body>
<script type="text/javascript" >
// 需要注意的是这里的number和person关系只有一次,
// 因为代码是顺序执行,当你在后台修改number的值,
// 发现对象里面依赖number的属性的值并不会受到影响,
// 因此需要Object.defineProperty() 的setter来处理
let number = 18
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, //控制属性是否可以枚举,默认值是false
// writable:true, //控制属性是否可以被修改,默认值是false
// configurable:true //控制属性是否可以被删除,默认值是false
//当有人读取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(Object.keys(person))
console.log(person)
</script>
</body>
</html>
1.3.7 Object.defineProperty实现的数据代理
需求: 让 obj 的 x 属性交给 obj2 进行数据代理。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>何为数据代理</title>
</head>
<body>
<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)-->
<script type="text/javascript" >
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
</body>
</html>
可以看到上面代码,我可以通过obj2来代理obj里面的属性。
1.3.8 Vue里面的数据代理
分析Vue里面的数据代理:
1.Vue中的数据代理:
- 通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:
- 更加方便的操作data中的数据,vue把data里面的属性,都抽取到vm对象实例中
3.基本原理:
- 通过Object.defineProperty()把data对象中所有属性添加到vm上。
- 为每一个添加到vm上的属性,都指定一个getter/setter。
- 在getter/setter内部去操作(读/写)data中对应的属性。
1.4 事件处理
1.4.1 事件的基本使用
事件的基本使用:
- 1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
- 2.事件的回调需要配置在methods对象中,最终会在vm上;
- 3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;
- 4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
- 5.@click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参;
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件的基本使用</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<button @click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
},
methods:{
showInfo1(event){
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
alert('同学你好!')
},
showInfo2(event,number){
console.log(event,number)
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
alert('同学你好!!')
}
}
})
</script>
</html>
1.4.2 事件修饰符
- 1.prevent:阻止默认事件(常用);
- 2.stop:阻止事件冒泡(常用);
- 3.once:事件只触发一次(常用);
- 4.capture:使用事件的捕获模式;
- 5.self:只有event.target是当前操作的元素时才触发事件;
- 6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件修饰符</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
*{
margin-top: 20px;
}
.demo1{
height: 50px;
background-color: skyblue;
}
.box1{
padding: 5px;
background-color: skyblue;
}
.box2{
padding: 5px;
background-color: orange;
}
.list{
width: 200px;
height: 200px;
background-color: peru;
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>
<!-- 修饰符可以连续写 -->
<!-- <a href="http://www.atguigu.com" @click.prevent.stop="showInfo">点我提示信息</a> -->
</div>
<!-- 事件只触发一次(常用) -->
<button @click.once="showInfo">点我提示信息</button>
<!-- 使用事件的捕获模式 -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
<!-- 只有event.target是当前操作的元素时才触发事件; -->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕; -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
showInfo(e){
alert('同学你好!')
// console.log(e.target)
},
showMsg(msg){
console.log(msg)
},
demo(){
for (let i = 0; i < 100000; i++) {
console.log('#')
}
console.log('累坏了')
}
}
})
</script>
</html>
1.4.3 键盘事件
常用的键盘事件
- keydown:当按下键盘上的任意键时触发。
- keyup:当松开键盘上的任意键时触发。
使用方式
在 Vue 中,你可以使用 v-on
指令(或简写 @
)来监听键盘事件。
1.Vue中常用的按键别名:
- 回车 => enter
- 删除 => delete (捕获“删除”和“退格”键)
- 退出 => esc
- 空格 => space
- 换行 => tab (特殊,必须配合keydown去使用)
- 上 => up
- 下 => down
- 左 => left
- 右 => right
2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
3.系统修饰键(用法特殊):ctrl、alt、shift、meta
- (1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
- (2).配合keydown使用:正常触发事件。
4.也可以使用keyCode去指定具体的按键(不推荐)
5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
代码演示
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>键盘事件</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- huiche是自定义别名 -->
<input type="text" placeholder="按下回车提示输入" @keydown.huiche="showInfo">
<!-- 系统别名 -->
<input type="text" @keydown.enter="showInfo">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
Vue.config.keyCodes.huiche = 13 //定义了一个别名按键
new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods: {
showInfo(e){
// e.key获取按键名称,e.keyCode获取按键编号
// console.log(e.key,e.keyCode)
console.log(e.target.value)
}
},
})
</script>
</html>
注意:自定义别名按键就是定义一个名称,然后给该名称一个keyCode,系统就能识别。
1.5 计算属性与监视
1.5.1 计算属性-computed
定义: 要显示的数据不存在,要通过Vue中已有属性计算得来。
原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
get函数什么时候执行?
- (1).初次读取时会执行一次。
- (2).当依赖的数据发生改变时会被再次调用。
- 1.计算属性最终会出现在vm上,直接读取使用即可。
- 2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
使用:
- 1. 在 computed 对象中定义计算属性。
- 2. 在页面中使用{{方法名}}来显示计算的结果。
案例:
需求:输入姓和名就会拼接成全名
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_methods实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{fullName()}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
methods: {
fullName(){
console.log('@---fullName')
return this.firstName + '-' + this.lastName
}
},
})
</script>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_计算属性实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
测试:<input type="text" v-model="x"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
<!-- 全名:<span>{{fullName}}</span> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
全名:<span>{{fullName}}</span> -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
x:'你好'
},
methods: {
demo(){
}
},
computed:{
fullName:{
//get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
//get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
get(){
console.log('get被调用了')
// console.log(this) //此处的this是vm
return this.firstName + '-' + this.lastName
},
//set什么时候调用? 当fullName被修改时。
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
</script>
</html>
一般计算属性只用来读取,不用来写入,因此可以进行简写。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_计算属性实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
},
computed:{
//完整写法
/* fullName:{
get(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
},
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
} */
//简写
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
}
})
</script>
</html>
注意:只有get()方法时才可以简写,fullName本质还是属性。。。
1.5.2 天气案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../02/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 使用计算属性 -->
<h1>今天天气很{{info}}</h1>
<!-- <button @click="change">切换天气</button> -->
<!-- 如果方法体中只有一句话,可以用 -->
<button @click="isHot = !isHot">切换天气</button>
</div>
<script>
const vm = new Vue({
el: "#root",
data: {
// 因为就两个选项,因此可以用布尔来实现
isHot: true
},
// methods: {
// change() {
// return this.isHot = !this.isHot
// }
// },
computed: {
// 此处只用了get方法,因此可以这样简写
info() {
return this.isHot ? "炎热" : "凉爽"
}
}
})
</script>
</body>
</html>
1.5.3 监视属性-watch
监视属性watch:
1.当被监视的属性变化时, 回调函数handler自动调用, 进行相关操作
2.监视的属性必须存在,才能进行监视!!
3.监视的两种写法:
- (1).new Vue时传入watch配置,已经知道要监视的属性
- (2).通过vm.$watch("属性名",配置对象)监视,后面根据需求追加的属性监视
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<!-- 对data属性进行加工用计算属性 -->
<h1>今天天气很{{info}}</h1>
<button @click="change">切换天气</button>
</div>
<script>
const vm = new Vue({
el: "#root",
data: {
isHot: true
},
methods: {
change() {
return this.isHot = !this.isHot
}
},
computed: {
info() {
return this.isHot ? "炎热" : "凉快"
}
},
// watch 是用来观察数据变动的
// watch: {
// isHot: {
// // 可选,如果要在初始化时立即运行
// immediate: true,
// // handler 是一个函数,当 isHot 属性发生变化时会被调用
// handler(newValue, oldValue) {
// console.log("监视到isHot改变", newValue, "--", oldValue)
// }
// }
// }
// 如果只有handler可以简写
// isHot(newValue, oldValue) {
// console.log("监视到isHot改变", newValue, "--", oldValue)
// },
})
// 第二种方式实现,这里的属性需要加字符串
vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
// 简写
// vm.$watch("isHot", function (newValue, oldValue) {
// console.log("监视到isHot改变", newValue, "--", oldValue)
// })
</script>
</body>
</html>
1.5.4 深度监视-watch-deep
2.配置deep:true可以监测对象内部值改变(多层)。
- (1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
- (2).使用watch时根据数据的具体结构,决定是否采用深度监视。
- 直接属性监视:使用属性名即可(如
isHot
)。 - 嵌套属性监视:使用字符串表示路径(如
"numbers.a"
),要加字符串。-
这里如果不加字符串,numbers是引用数据类型,因此只有地址值改变才会被监视到
-
如果想要监视深层属性的改变,需要"numbers.a"加上字符串
-
如果想监视一个对象内的所用属性,需要配置deep:true,然后numbers就可以实现
-
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<!-- 对data属性进行加工用计算属性 -->
<h1>今天天气很{{info}}</h1>
<button @click="change">切换天气</button>
<hr />
<h1>a的值{{numbers.a}}</h1>
<button @click="numbers.a++">点击我a+1</button> <br>
<h1>b的值{{numbers.b}}</h1>
<button @click="numbers.b++">点击我b+1</button>
</div>
<script>
const vm = new Vue({
el: "#root",
data: {
isHot: true,
numbers: {
a: 1,
b: 1
}
},
methods: {
change() {
return this.isHot = !this.isHot
}
},
computed: {
info() {
return this.isHot ? "炎热" : "凉快"
}
},
// watch 是用来观察数据变动的
watch: {
isHot: {
// 可选,如果要在初始化时立即运行
immediate: true,
// handler 是一个函数,当 isHot 属性发生变化时会被调用
handler(newValue, oldValue) {
console.log("监视到isHot改变", newValue, "--", oldValue)
}
},
numbers: {
deep: true,
handler(newValue, oldValue) {
console.log("监视到numbers改变", newValue, "--", oldValue)
}
}
}
})
// 第二种方式
// vm.$watch("numbers", {
// deep: true,
// handler(newValue, oldValue) {
// console.log("监视到numbers改变", newValue, "--", oldValue)
// }
// })
</script>
</body>
</html>
1.5.5 计算属性和监视属性的比较
1.computed和watch之间的区别:
- computed能完成的功能,watch都可以完成。
- watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作(比如计时器)。
- computed的计算属性内getter有return,而watch的监视属性内的handler只是对属性进行操作,不涉及到return,因此可以进行异步操作。
2.两个重要的小原则:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
1.6 class 与 style 绑定
一般样式:固定的class属性。
绑定样式:需要动态来添加或者删除的class属性。
1. :class样式
- 字符串写法适用于:类名不确定,要动态获取。
-
字符串写法,用插值表达式
-
- 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
-
对象写法,属性名:选择器名,属性值:true代表显示,false代表不显示
-
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
-
数组写法,可以通过方法,对数组进行添加和删除操作
-
2. style样式
- :style="{fontSize: xxx}"其中xxx是动态值。一般使用对象形式。
- :style="[a,b]"其中a、b是样式对象。数组形式。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绑定样式</title>
<style>
.basic{
width: 400px;
height: 100px;
border: 1px solid black;
}
.happy{
border: 4px solid red;;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg,yellow,pink,orange,yellow);
}
.sad{
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}
.normal{
background-color: skyblue;
}
.atguigu1{
background-color: yellowgreen;
}
.atguigu2{
font-size: 30px;
text-shadow:2px 2px 10px red;
}
.atguigu3{
border-radius: 20px;
}
</style>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>
<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div> <br/><br/>
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div> <br/><br/>
<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
<!-- 绑定style样式--数组写法 -->
<div class="basic" :style="styleArr">{{name}}</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
mood:'normal',
classArr:['atguigu1','atguigu2','atguigu3'],
classObj:{
atguigu1:false,
atguigu2:false,
},
styleObj:{
fontSize: '40px',
color:'red',
},
styleObj2:{
backgroundColor:'orange'
},
styleArr:[
{
fontSize: '40px',
color:'blue',
},
{
backgroundColor:'gray'
}
]
},
methods: {
changeMood(){
const arr = ['happy','sad','normal']
const index = Math.floor(Math.random()*3)
this.mood = arr[index]
}
},
})
</script>
</html>
1.7 条件渲染 v-if || v-show
1.7.1 条件渲染指令
- v-if 、v-else-if 与 v-else
- v-show
1.7.2 比较 v-if 与 v-show
条件渲染的两种方案:
1.v-if
写法:
(1).v-if="表达式"
(2).v-else-if="表达式"
(3).v-else="表达式"
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。必须是一个整体。
2.v-show
写法:v-show="表达式"
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉。
3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
4.注意:v-if与template之间可以配合使用,<template>标签不会被解析为元素。。。不会破坏原始的结构。
<!-- v-if与template的配合使用 -->
<template v-if="n === 1">
<h2>你好</h2>
<h2>尚硅谷</h2>
<h2>北京</h2>
</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="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 === 2">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">
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
n:0
}
})
</script>
</html>
1.8 列表渲染 v-for
1.8.1 v-for的了解
- 1.用于展示列表数据
- 2.语法:v-for="(item, index) in/of xxx" :key="yyy" ,key一般为唯一标识。
- 3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
注意:如果:key没指定,默认为index。
效果实现:
代码演示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<h1>渲染列表</h1>
<ul>
<!-- 使用数组遍历 -->
<!--
张三-18
李四-19
王五-20 -->
<li v-for="(person,index) in persons" :key="person.id">{{person.name}}-{{person.age}}</li>
</ul>
<ul>
<!-- 使用对象遍历 -->
<!--
tip-日本-0
type-本田-1
maxSpeed-300码-2 -->
<li v-for="(value,key,index) in srcObj">{{key}}-{{value}}-{{index}}</li>
</ul>
<ul>
<!-- 字符串(用的很少) -->
<!--
h-0
e-1
l-2
l-3
o-4 -->
<li v-for="(char,index) in str">{{char}}-{{index}}</li>
</ul>
<ul>
<!-- 指定次数(用的很少) -->
<!--
1-0
2-1
3-2
4-3
5-4 -->
<li v-for="(number,index) in 5">{{number}}-{{index}}</li>
</ul>
</div>
<script>
new Vue({
el: "#root",
data: {
persons: [
{ id: "001", name: "张三", age: "18" },
{ id: "002", name: "李四", age: "19" },
{ id: "003", name: "王五", age: "20" }
],
srcObj: {
tip: "日本",
type: "本田",
maxSpeed: "300码"
},
str: "hello"
}
})
</script>
</body>
</html>
1.8.2 v-for中的key原理
注意:如果v-for里面没有指定 :key,该key默认值就是 index。
面试题:react、vue中的key有什么作用?(key的内部原理)
1. 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下。
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
- ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
- ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到到页面。
3. 用index作为key可能会引发的问题:
- (1). 若对数据进行:逆序添加、逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低,不能复用之前的虚拟DOM。
- (2). 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题(会报错)。
4. 开发中如何选择key?
- (1).最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
- (2).如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
原理图:
1.index作为key可能出现的问题
2.id作为key不会出现问题
1.8.3 列表过滤案例
需求:通过输入表单值,然后进行模糊查询,并且将数据展示出来。
效果图:
代码实现:
计算属性和监视属性两种方式的实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<h1>通过输入值进行模糊查询</h1>
<form action="#" autocomplete="off">
<input type="text" placeholder="请输入姓名" v-model="keyWord">
</form>
<ul>
<li v-for="person in filterArr">{{person.name}} - {{person.age}} - {{person.sex}}</li>
</ul>
</div>
<script>
// new Vue({
// el: "#root",
// data: {
// // 先给一个默认值
// keyWord: '',
// persons: [
// { id: '001', name: '马冬梅', age: 19, sex: '女' },
// { id: '002', name: '周冬雨', age: 20, sex: '女' },
// { id: '003', name: '周杰伦', age: 21, sex: '男' },
// { id: '004', name: '温兆伦', age: 22, sex: '男' }
// ],
// filterArr: []
// },
// //方式一:定义一个监视器
// watch: {
// keyWord: {
// // 立即初始化,才会一开始出现数据,空字符串为 0 ,不为-1
// immediate: true,
// handler(newValue) {
// // 注意这里一定要写箭头函数,不然this是windows
// this.filterArr = this.persons.filter(elment => {
// // 重要点:String.indexOf("") 空字符串的返回值为 0 , 不匹配的为-1
// return elment.name.indexOf(newValue) !== -1
// })
// }
// }
// },
// })
new Vue({
el: "#root",
data: {
// 先给一个默认值
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
]
},
//方式二:用计算属性实现(较第一种方式比较方便)
computed: {
// 注意:计算属性,不能出现在data中有过定义
filterArr() {
// 注意这里一定要写箭头函数,不然this是windows
// 这里必须有返回
return this.persons.filter(elment => {
// 重要点:String.indexOf("") 空字符串的返回值为 0 , 不匹配的为-1
return elment.name.indexOf(this.keyWord) !== -1
})
}
}
})
</script>
</body>
</html>
1.8.4 列表排序案例
需求:需要根据用户过滤的数据进行排序。
效果图:
代码演示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<h1>通过输入值进行模糊查询</h1>
<form action="#" autocomplete="off">
<input type="text" placeholder="请输入姓名" v-model="keyWord">
</form>
<br>
<!-- 排序 -->
<button @click="sortType = 0">原顺序</button>
<button @click="sortType = 1">降序</button>
<button @click="sortType = 2">升序</button>
<ul>
<li v-for="person in filterArr">{{person.name}} - {{person.age}} - {{person.sex}}</li>
</ul>
</div>
<script>
new Vue({
el: "#root",
data: {
// 0:原顺序 1:降序 2:升序
sortType: 0,
// 先给一个默认值
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
]
},
//方式二:用计算属性实现(较第一种方式比较方便)
computed: {
// 注意:计算属性,不能出现在data中有过定义
filterArr() {
// 注意这里一定要写箭头函数,不然this是windows
// 这里必须有返回
const arr = this.persons.filter(elment => {
// 重要点:String.indexOf("") 空字符串的返回值为 0 , 不匹配的为-1
return elment.name.indexOf(this.keyWord) !== -1
})
// 拿到过滤信息,再进行排序
if (this.sortType) {
arr.sort((e1, e2) => {
// 写花括号就必须要写return
return this.sortType === 1 ? e2.age - e1.age : e1.age - e2.age
})
}
// 将排序好的数组,赋值给filterArr计算属性
return arr
}
}
})
</script>
</body>
</html>
注意:Arrays.sort(),会直接修改原数组,因此没有返回值。