一、初始Vue.js
1.Vue概述
1.1.MVVM模式
MVVM设计模式是由Model(模型)、View(视图)和ViewModel(视图模型)三部分组成,是MVC设计模式的进化版,即Controller转变为ViewModel,这种模式可以使View的变化自动更新到ViewModel,而 ViewModel的变化也会自动同步到View上显示
1.2.安装Vue
<script src="js/vue.min.js"></script>
2.Vue的使用
v-model指令双向绑定
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>初始Vue</title>
</head>
<body>
<!-- mvvm -->
<!-- 1.创建视图 -->
<div id="app">
Vue绑定数据
{{message}}
<!-- {{test}} -->
<!-- 双向绑定 -->
<input type="text" v-model="message"/>
</div>
<!-- 2.导入Vue -->
<script src="js/vue.min.js"></script>
<!-- 3.创建模型 -->
<!-- 4.创建vm -->
<script>
var tmp={
message:"hello world"
}
var vm=new Vue({
el:'#app',
data:tmp
// data:{test:"玛卡巴卡"}
})
</script>
</body>
</html>
3.Vue生命周期
3.1.Vue的实例和数据
Vue应用的创建
var vm=new Vue({
//选项参数
});
Vue的常用选项参数 | |
选项参数 | 说明 |
el | 提供一个在页面上已经存在的DOM元素作为Vue实例的挂在目标 |
data | Vue实例的数据对象。Vue会递归地将data的属性转换为getter/setter,从而让data的属性能够响应数据变化 |
methods | Vue实例的方法集合,可以在Vue直接调用或将方法绑定到DOM元素的事件上 |
computed | Vue实例的计算属性集合 |
watch | 观察Vue实例变化的一个表达式或计算属性函数 |
components | 包含Vue实例可用组件的哈希表 |
filters | 包含Vue实例可用的过滤器的哈希表 |
template | 定义字符串模板作为Vue实例的标识使用 |
必不可少的选项参数是el 。el用于指定一个页面中已存在的DOM元素来挂载Vue实例,它可以是HTMLElement,也可以是CSS选择器
当挂在成功后,就可以通过vm.$el来访问元素
在Vue实例内部访问data中的数据时,一般使用“this.数据”的方式
3.2.Vue生命周期函数的使用
Vue实例从创建到销毁的过程就是它的生命周期
在Vue中每一个组件或者实例都会经历一个完整的生命周期,总共分为3个阶段:初始化、运行中和销毁阶段
3.2.1.Vue实例的生命周期过程具体说明
A.实例、组件通过new Vue()创建出来之后会初始化事件和生命周期,然后执行beforeCreate()函数,这个时候数据并没有挂载,只是一个空壳,无法访问到数据和真实DOM
B.挂载数据、绑定事件等,然后执行created()函数,这个时候已经可以使用到数据,也可以更改数据。在这里更改数据并不会触发updated()函数,在渲染前有倒数第二次更改数据的机会,这样不会触发其他的钩子函数,一般可以在这里做初始数据的获取
C.接下来开始找实例或者组件对应的模板,编译模板为虚拟DOM放入render()函数中准备渲染,然后执行beforeMount()函数,在这个函数中虚拟DOM已经创建完成,马上就要渲染,此处也可以更改数据,不会触发updated()函数,这里在渲染前有最后一次更改数据的机会,不会触发其他的钩子函数,所以一般可以在这里做初始数据的获取
D.接下来开始渲染,渲染出真实DOM,然后执行mounted()函数,此时,组件已经出现在页面中,数据、真实的DOM都已经处理好了,事件也已经挂载好了,可以在这里操作真实DOM事件
E.当组件或实例的数据更改之后,会立即执行beforeUpdated()函数,然后Vue的虚拟DOM机制会重新构建虚拟DOM并与上一次的虚拟DOM利用diff算法进行对比之后重新渲染。
F.当更新完成后,执行updated()函数,数据已经更改完成,DOM也重新渲染完成,可以操作更新后的虚拟DOM
G.当经过某种途径调用$destroy()方法后,立即执行beforeDestroy()函数,一般在这里做一些善后工作,如清除计时器和非指令绑定的事件等
H.最后将组件或实例的数据绑定、监听等去掉后只剩下DOM空客,这时也可以执行destroy()函数,在这里做善后工作
3.2.2.Vue实例的生命周期中钩子函数
created()函数 | 实例建立完成后调用。此阶段完成了数据的观测等,但尚未挂载,$el此时还不可用,需要初始化处理一些数据时会比较有用 |
mounted()函数 | el挂载到实例上之后调用。一般开发者的第一个业务逻辑会在这里开始,也可以在此处调用AJAX来获取服务端的数据 |
beforeDestroy()函数 | 实例被销毁之前调用。主要解绑一些使用addEventListener监听的事件等。 |
例:使用生命周期钩子函数实现定时显示Banner文本功能
<!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>
<style>
#app{
text-align: center;
padding: 10px;
}
#banner{
background-color: blue;
width: 600px;
height: 150px;
line-height: 150px;
font-size: 40px;
color: white;
border-radius: 50px;
margin: 50px auto;
}
</style>
</head>
<body>
<!-- 1.创建view -->
<div id="app">
<div id="banner">
{{adTitle}}
</div>
</div>
<!-- 2.导入Vue -->
<script src="js/vue.min.js"></script>
<!-- 3.创建模型 -->
<!-- 4.创建vm -->
<script>
var bannerAd={
ads:[],
adTitle:"",
index:0
}
var vm=new Vue({
el:"#app",
data:bannerAd,
//在create函数初始化模型
created:function(){
this.ads=["地球交通委提醒你","道路千万条","安全第一条","行车不规范","亲人两行泪"];
this.adTitle=this.ads[0];
},
//添加定时器
mounted:function() {
//声明一个变量指向Vue实例,保证作用域一致
var _this=this;//保留this外层指向,有回调函数
this.timer=setInterval(function(){
if(_this.index<_this.ads.length-1){
_this.index++;
}else{
_this.index=0;
}
//修改Banner中的内容
_this.adTitle=_this.ads[_this.index];
},3000)
},
beforeDestroy:function() {
if(this.timer){
//在Vue实例被销毁前,清除定时器
clearInterval(this.timer);
}
},
})
</script>
</body>
</html>
4.Vue过滤器
Vue.js支持在{{}}的插值的尾部添加一个管道符(“|”)对数据进行过滤,经常用于格式化文本,比如字母全部大写、货币千位使用逗号分隔等
4.1.全局创建过滤器
可在整个页面中使用
Vue.filter("过滤器名",function(val){
过滤语句
});
4.2.局部创建过滤器
在当前Vue实例的作用域中有效
var vm=new Vue({
el:"#app",
filter:{
过滤器名:function(val){
过滤语句
}
}
});
在HTML中使用过滤器的语法如下
<div id="app">
{{模型数据|过滤器}}
</div>
<!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>
</head>
<body>
<!-- 1.创建视图 -->
<div id="app">
{{message|Transformessage}}
</div>
<!-- 2.引入Vue -->
<script src="js/vue.min.js"></script>
<!-- 3.创建model -->
<script>
var tmp={
message:"action louder than speak"
}
var vm=new Vue({
el:"#app",
data:tmp,
filters:{
Transformessage:function(val){
//分割成数组
var str= val.split(' ');
var rs=" ";
//循环数组
for(var i=0;i<str.length;i++){
//把每一个首字母变成大写
//拼接后面的单词
rs+=str[i].charAt(0).toUpperCase()+str[i].substring(1)+' '
}
return rs;
}
}
})
</script>
<!-- 4.创建vm -->
</body>
</html>
二、Vue基本指令
1.Vue模板语法
1.1.文本插值
文本插值是最基本的形式,使用双大括号(Mustache 语法){{}}表示
<span>Text:{{text}}</span>
上述代码中的标签{{text}}将会被相应的数据对象text属性的值替换掉,当text的值改变时,文本中的值也会联动地发生变化。有时候只需渲染一次数据,后续数据变化不在关心,可以通过v-once指令来实现
<span v-once>Text:{{text}}</span>
文本插值还可以使用v-text指令来代替双大括号
<span v-text="text"></span>
双大括号和v-text指令会把匿名的值全部当作字符串来处理,如果值是HTML片段,双大括号和v-text指令会将“<>”转义为“< >”,如需要解析HTML内容,则使用v-html指令来绑定
注意:在网站上动态渲染任意HTML是非常危险的,因为容易导致XSS攻击。只在可信内容上使用v-html,永远不要用来在用户提交的内容上
说明:XSS攻击全称为跨站脚本攻击,XSS是一种在Web应用中的计算机安全漏洞,它允许恶意Web用户将代码植入到提供给其他用户使用的页面中
1.2.表达式
文本插值也接受表达式的形式,表达式由JavaScript表达式和过滤器构成,其中过滤器可以没有,也可以有多个
表达式时各种数值、变量和运算符的综合体。简单的表达式可以是常量或者变量名称。表达式的值使其运算结果
??可以为空
Vue只接受单个表达式,不支持语句和控制流
在表达式中,不能使用用户自定义的全局变量,只能使用JavaScript的全局变量,如Math和Date
1.3.指令
指令是Vue.js中一个重要的特性,主要提供了一种机制数据的变化映射为DOM行为
指令是带有v-前缀的特殊属性
指令属性的值预期时单一的JavaScript表达式(除了v-for)
指令的职责就是当起表达式的值改变时相应的将某些行为应用到DOM上
1.3.1.指令中的参数
一些指令能接受一个参数,在指令后以冒号指明
例如,v-bind指令被用来更新HTML属性
<a v-bind:href="url"></a>
在这里href是参数,告知v-bind指令将该元素的href属性与表达式url的值绑定
另一个例子是v-on指令,它用于监听DOM事件
<a v-on:click="doSomething"></a>
点击事件 方法
1.3.2.指令中的修饰符
修饰符是以半角句号“.”指明的特殊后缀,用于指出一个指令应该以特殊方式绑定
例如:.prevent修饰符告诉v-on指令对于触发的事件调用event.preventDefault()
<form v-on:submit.prevent="onSubmit"></form>
绑定前
<!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>
</head>
<body>
<!-- 文本插值 -->
<!-- 1.创建视图 -->
<div id="app">
<p>{{text}}</p>
<p v-text="text"></p>
<p v-html="text"></p>
<p v-once>{{text}}</p>
<input type="text" name="" id="" v-model="text" />
<!-- 表达式 -->
<p>{{num+1}}</p>
<p>{{1+3}}</p>
<p>三目运算符{{ok?true:false}}</p>
<p>{{message.split("").reverse().join("")}}</p>
</div>
<!-- 2.导入Vue -->
<script src="js/vue.min.js"></script>
<!-- 3.创建模型 -->
<!-- 4.创建vm -->
<script>
var tmp = {
text: "<h2>今天不学习,明天捡垃圾</h2>",
num: 1,
ok: false,
message: "Hello Word",
};
var vm = new Vue({
el: "#app",
data: tmp,
});
</script>
</body>
</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>
</head>
<body>
<h1>个人工资计算器(指令)</h1>
<!-- 1.创建视图 -->
<div id="app">
<fieldset>
<legend>个人工资计算器</legend>
<p>
<label for="">基本工资</label>
<input type="number" name="" id="" v-model="base">
</p>
<p>
<label for="">岗位工资</label>
<input type="number" name="" id="" v-model="job">
</p>
<p>
<label for="">绩效工资</label>
<input type="number" name="" id="" v-model="reward">
</p>
<span>工资总和:{{(parseFloat(job)+parseFloat(base)+parseFloat(reward)).toFixed(2)}}</span>
</fieldset>
</div>
<!-- 2.导入Vue -->
<script src="js/vue.min.js"></script>
<!-- 3.创建模型 -->
<!-- 4.创建vm -->
<script>
var tmp={
base:0,
job:0,
reward:0
}
var vm=new Vue({
el:"#app",
data:tmp
})
</script>
</body>
</html>
2.Vue绑定类样式和内联样式
2.1.Vue绑定类样式
2.1.1.对象语法
<div v-bind:class="{类样式名:绑定数据,...}"></div>
类样式名可以有多个,通过绑定数据为true或false来控制样式是否应用到元素中
2.1.2.数组语法
<div v-bind:class="[绑定数据1,绑定数据2,...]"></div>
2.2.Vue绑定内联样式
2.2.1.对象语法
v-bind:style的对象语法十分直观——看着非常像CSS,但其实是一个JavaScript对象。CSS属性名可以用驼峰式或者短横线分隔,需要用引号括起来命名
<div v-bind:style="{样式属性:绑定数据}"/>
或者
<div v-bind:style="styleObject"/>
<script>
var vm=new Vue({
el:"#app",
data:{
styleObject:{
样式属性1:样式值1,
样式属性2:样式值2
}
}
});
</script>
2.2.2.数组语法
<div v-bind:class="[baseStyles,overridingStyle]"></div>
<!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>
<style>
#app {
width: 300px;
height: 300px;
}
#app .border {
width: 300px;
height: 300px;
border: 3px solid black;
}
#app .shadow {
box-shadow: 0px 0px 10px 10px gainsboro;
}
#app .bg {
background-color: lavender;
}
#app .hover:hover {
background-color: lightcoral;
width: 100px;
height: 100px;
transition: all 3s 0.2s;
}
</style>
</head>
<body>
<!-- 1.创建视图 -->
<div id="app">
<input type="checkbox" v-model="isborder" name="" id="" />边框
<input type="checkbox" v-model="isshadow" name="" id="" />阴影
<input type="checkbox" v-model="isbg" name="" id="" />背景
<input type="checkbox" v-model="ishover" name="" id="" />悬停
<!-- 对象方式 -->
<!-- <div v-bind:class="{border:isborder,shadow:isshadow,bg:isbg,hover:ishover}"></div> -->
<!-- 等同于 -->
<!-- <div class="border"></div> -->
<!-- 数组方式 -->
<!-- <div v-bind:class="[isborder,isshadow,isbg,ishover]"></div> -->
<!-- 内联样式 -->
<div v-bind:style="[isborder,isshadow,isbg,ishover]"></div>
<div style="width: 300px; height: 300px; border: 1px solid black"></div>
</div>
<!-- 2.导入Vue -->
<script src="js/vue.min.js"></script>
<!-- 3.创建模型 -->
<!-- 4.创建vm -->
<script>
// 对象方式
// var tmp={
// isborder:true,
// isshadow:true,
// isbg:true,
// ishover:true
// }
//数组方式
// var tmp={
// isborder:"border",
// isshadow:"shadow",
// isbg:"bg",
// ishover:"hover"
// }
//内联方式
var tmp = {
isborder: {
width: "300px",
height: "300px",
border: "3px solid black",
},
isshadow: "shadow",
isbg: {
width: "300px",
height: "300px",
"background-color": "lavender",
},
ishover: "hover",
};
var vm = new Vue({
el: "#app",
data: tmp,
});
</script>
</body>
</html>
3.条件渲染指令
3.1.v-if和v-else指令
v-if是条件渲染指令,它根据表达式的真假来删除和插入元素
<div v-if="expression"></div>
expression是一个返回bool值的表达式,表达式可以是一个bool属性,也可以是一个返回bool的运算符
可以用v-else指令为v-if添加一个else块
v-else元素必须立即跟在v-if元素的后面,否则它不能被识别
3.2.v-show指令
也可以实现条件渲染
与v-if指令不同的是带有v-show指令的元素始终会被渲染并保留在DOM中,而v-show指令只是简单的切换元素的CSS属性display
注意:v-show指令不支持<template>元素,也不支持v-else指令
如果需要非常频繁的切换,则使用v-show指令较好;如果在运行时条件很少改变,则使用v-if指令较好
<!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>用户登录注册v-if v-else</title>
</head>
<body>
<div id="app">
<h1>用户登录</h1>
<input type="checkbox" name="" id="" v-model="isRegister">是否注册
<div class="box">
<div v-if="isRegister">
<h2>用户登录</h2>
<p>
用户名:<input type="text" id="" value=""/>
</p>
<p>
密码:<input type="text" id="" value=""/>
</p>
</div>
<div v-else>
<h2>用户注册</h2>
<p>
用户名:<input type="text" id="" value=""/>
</p>
<p>
密码:<input type="text" id="" value=""/>
</p>
<p>
确认密码:<input type="text" id="" value=""/>
</p>
</div>
<!-- v-show用法 -->
<!-- <div v-show="isRegister">
<h2>用户登录</h2>
<p>
用户名:<input type="text" id="" value=""/>
</p>
<p>
密码:<input type="text" id="" value=""/>
</p>
</div>
<div v-show="!isRegister">
<h2>用户注册</h2>
<p>
用户名:<input type="text" id="" value=""/>
</p>
<p>
密码:<input type="text" id="" value=""/>
</p>
<p>
确认密码:<input type="text" id="" value=""/>
</p>
</div> -->
</div>
</div>
<script src="js/vue.min.js"></script>
<script>
var tmp={
isRegister:true
}
var vm=new Vue({
el:"#app",
data:tmp
})
</script>
</body>
</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>
</head>
<body>
<!-- v-if v-show -->
<div id="app">
<div v-if="ok">
<div>满足条件输出:什么是快乐星球</div>
<p>玛卡巴卡</p>
</div>
<!-- template等于{} -->
<!-- <template v-if="ok">
<div>满足条件输出:什么是快乐星球</div>
<p>玛卡巴卡</p>
</template> -->
<div v-else>
不满足条件:这是一个测试
</div>
</div>
<script src="js/vue.min.js"></script>
<script>
var tmp = {
ok: true
}
var vm = new Vue({
el: "#app",
data: tmp
})
</script>
</body>
</html>
4.事件绑定
4.1.v-on指令
v-on指令监听DOM事件,并在触发时运行一些JavaScript代码。通过v-on指令可以绑定Vue实例选项参数methods中的方法作为事件的处理代码
“v-on:”后参数接受所有的原生事件名称
<button v-on:click='事件处理方法'>按钮</button>
可改写为
<button @:click='事件处理方法'>按钮</button>
<!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>
</head>
<body>
<div id="app">
<fieldset>
<legend>计算器</legend>
<!-- 输入框 -->
<p>
数字1:<input type="number" name="" id="" v-model="num1">
</p>
<p>
数字2:<input type="number" name="" id="" v-model="num2">
</p>
<!-- 按钮 -->
<button type="button" v-on:click="doADD">+</button>
<button type="button" @click="doSub">-</button>
<button type="button" @click="alert(vm.num1*vm.num2)">*</button>
<button type="button" @click="docf">/</button>
<p>
计算结果:{{sum}}
</p>
</fieldset>
</div>
<script src="js/vue.min.js"></script>
<script>
var tmp={
num1:0,
num2:0,
sum:0
}
var vm=new Vue({
el:"#app",
data:tmp,
methods:{
doADD(){
this.sum=parseFloat(this.num1)+parseFloat(this.num2)
},
doSub(){
this.sum=parseFloat(this.num1)-parseFloat(this.num2)
},
docf(){
this.sum=parseFloat(this.num1)/parseFloat(this.num2)
}
}
})
</script>
</body>
</html>
4.2.事件修饰符
在JavaScript的事件处理程序中一般调用event.preventDefault()方法取消事件的默认动作,以及调用event.stopPropagation()方法阻止事件冒泡
.stop | 等同于调用event.stopPropagation()方法 |
.prevent | 等同于调用event.preventDefault()方法 |
.capture | 使用capture模式添加事件监听器 |
.self | 只有当事件是从监听元素本身触发时才触发回调 |
.once | 点击事件将只会触发一次 |
当一个元素上的事件被触发的时候,比如鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发,这一过程被称为事件冒泡。这个事件从原始的元素开始将一直冒泡到DOM树的最上层
<!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>
</head>
<body>
<div id="app">
<!-- v-on=======@ v-bind=======: -->
<div @click="alert('这是父亲的点击事件')" v-bind:style="classOne">
<button type="button" @click.stop="alert('这是儿子的点击事件')" >你点击我试试看</button>
</div>
</div>
<script src="js/vue.min.js"></script>
<script>
var vm=new Vue({
el:"#app",
data:{
classOne:{
width:"300px",
height:"300px",
"background":"skyblue"
}
}
})
</script>
</body>
</html>
三、Vue列表渲染
1.v-for指令
v-for指令是基于一个数组来重复渲染的元素,通常用于显示列表和表格数据
v-for指令需要使用“item in items”形式的特殊语法,其中items是数据源,item则是被迭代的别名
v-for指令还支持一个可选的第二个参数,即当前项的索引
为了给Vue一个提示,以便它能跟踪每一行的数据,从而重用和重新排序现有元素,v-for指令需要为每项提供一个唯一的key属性
三个值:属性值,属性,索引
两个值:值,索引(index)
提示:建议尽可能在使用v-for指令时提供key属性,除非遍历输出的DOM内容非常简单,或者时刻意依赖默认行为以获取性能上的提升
注意:不要使用对象或数组之类的非基本类型值作为v-for的key属性,请用字符串或数值类型的值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-for指令</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
</head>
<body>
<div id="app">
<div>
<ul>
<!-- 不显示索引 -->
<!-- <li v-for="item in preface">
<a href="#"> {{item}}</a>
</li> -->
<!-- 显示索引 -->
<li v-for="(item,index) in preface">
<a href="#">{{index}}--------- {{item}}</a>
</li>
</ul>
</div>
<!-- 属性值,属性,key -->
<div v-for="(item,index,key) in user" :key="index">
{{key}}------------ {{index}} -------{{item}}
</div>
<!-- <div v-for="(item,key,index) in user" :key="index">
{{key}}------------ {{index}} -------{{item}}
</div> -->
<table class="table table-bordered table-striped">
<caption>学生信息表</caption>
<thead class="bg-info">
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>职位</td>
</tr>
</thead>
<tbody>
<tr v-for="(item,key,index) in stus" v-if="item.age>18">
<td>{{key}}</td>
<td>
{{item.name}}
</td>
<td>
{{item.age}}
</td>
<td>
{{item.job}}
</td>
</tr>
</tbody>
</table>
</div>
<script src="js/vue.min.js"></script>
<script>
var tmp = {
preface: ["首页", "订单管理", "购物车"],
user: {
name: "pxy",
age: 20,
job: "前端工程师",
},
stus: [{
name: "tyy",
age: 10,
job: "前端工程师",
}, {
name: "lky",
age: 20,
job: "前端工程师",
}, {
name: "hy",
age: 19,
job: "后端工程师",
}]
}
var vm = new Vue({
el: "#app",
data: tmp,
})
</script>
</body>
</html>
2.计算属性
在计算属性里可以完成各种复杂的逻辑,包括运算、函数调用等
计算属性还可以依赖多个Vue实例的数据,只要其中任一数据发生变化,计算属性就会重新执行,视图也会更新
所有计算属性都以函数的形式写在Vue实例内的computed选项中(支持缓存)
methods()方法与计算属性的区别是计算属性支持缓存,所以在遍历大数组或做大量计算时,计算属性更高效
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>计算属性</title>
</head>
<body>
<div id="app">
<p>
原始数据:{{message}}
</p>
<p>
转换后:{{message.split("").reverse().join("")}}
</p>
<p>
方法调用 {{reverMsg()}}
</p>
<p>
计算属性得到{{revolovMsg}}
</p>
</div>
<script src="js/vue.min.js"></script>
<script>
var tmp = {
message: "Hello word"
}
var vm = new Vue({
el: "#app",
data: tmp,
methods: {
reverMsg() {
return this.message.split("").reverse().join("");
}
},
computed: {
revolovMsg() {
return this.message.split("").reverse().join("");
},
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>用计算属性做筛选</title>
</head>
<body>
<div id="app">
<p>
<input type="text" placeholder="请输入搜索字符" name="" id="" v-model="wordKey">
</p>
<div v-for="item in findItems">
{{item}}
</div>
</div>
<script src="js/vue.min.js"></script>
<script>
var tmp = {
centence: ["qwer", "asdf", "zxcv", "asqw"],
wordKey: "",
stus: [{
name: "tyy",
age: 10,
job: "前端工程师",
}, {
name: "lky",
age: 20,
job: "前端工程师",
}, {
name: "hy",
age: 19,
job: "后端工程师",
}]
}
var vm = new Vue({
el: "#app",
data: tmp,
computed: {
//数组
// findItems() {
// var _this = this;
// return _this.centence.filter(function(val) {
// return val.indexOf(_this.wordKey) != -1;
// });
//数组对象
findItems() {
var _this = this;
return _this.stus.filter(function(val) {
return val.name.indexOf(_this.wordKey) != -1;
});
}
}
})
</script>
</body>
</html>
3.侦听属性
Vue提供了一种更通用的方式来观察和响应Vue实例上的数据变动——侦听属性
当有一些数据需要随着其他数据的变动而改变时,就可以使用侦听属性watch
watch是一个对象,其中watch对象的属性是需要侦听的目标,一般是data中的某个数据项,而watch对象的属性值是一个函数,是当被侦听的数据项发生变化时需要执行的函数
这个函数有两个形参:第一个是当前值(想要的值),第二个是更新后的值
如果想要一开始让最初绑定的时候就执行,就需要在watch中使用handler()方法和immediate属性
handler()方法:其值是一个回调函数,即监听到变化时应该执行的函数
immediate属性:其值是true或false,确认是否以当前的初始值执行handler的函数
说明:如果模型数据是一个对象时,Vue默认并不能侦听对象属性的变化,watch里面还有一个deep属性,默认值是false,代表是否需要使用深度侦听。当deep属性为ture时,就能侦听对象属性的变化
<!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>
<style>
.warnColor {
color: red;
}
.warnDisa {
display: none;
}
</style>
</head>
<body>
<div id="app">
<fieldset>
<legend>商品信息</legend>
<p>
商品名称:<input type="text" name="" id="" v-model="title">
<span :class="{warnColor:isname,warnDisa:!isname}">
商品名称不能为空
</span>
</p>
<p>
商品价格:<input type="number" name="" id="" v-model="price">
<span :class="{warnColor:isprice,warnDisa:!isprice}">
商品价格不能为空
</span>
</p>
<p>
商品数量:<input type="number" name="" id="" v-model="num">
<span :class="{warnColor:isnum,warnDisa:!isnum}">
商品数量不能为空
</span>
</p>
<p>
商品总额:{{sum}}
</p>
</fieldset>
</div>
<script src="js/vue.min.js"></script>
<script>
var tmp = {
title: "Vue.JS高效前端开发",
price: 39,
num: 1,
isname: false,
isprice: false,
isnum: false,
sum: 0,
}
var vm = new Vue({
el: "#app",
data: tmp,
watch: {
title: function (oldVal, newVal) {
if (oldVal != "") {
this.isname = false;
} else {
this.isname = true;
}
},
price: {
handler: function (oldVal, newVal) {
console.log(oldVal);
console.log(newVal);
if (oldVal != "") {
this.isprice = false;
} else {
this.isprice = true;
}
this.sum = parseFloat(oldVal) * parseInt(this.num);
},
immediate: true
},
num: function (oldVal, newVal) {
if (oldVal != "") {
this.isnum = false;
} else {
this.isnum = true;
}
this.sum = parseFloat(this.price) * parseInt(oldVal);
},
},
})
</script>
</body>
</html>
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>
<link rel="stylesheet" href="css/bootstrap.min.css">
</head>
<body>
<div id="app">
<fieldset>
<legend>新商品信息</legend>
<div>
<p>
名称:<input type="text" name="" id="" v-model="newProduct.name">
</p>
<p>
价格:<input type="number" name="" id="" v-model="newProduct.price">
</p>
<p>
类别:
<select name="" id="" v-model="newProduct.categray">
<option v-for="cate in categraies":value="cate">{{cate}} </option>
</select>
</p>
<p>
<button @click="createP" class="btn-info">添加</button>
</p>
</div>
<div>
查询关键字: <input type="text" name="" id="" v-model="keyWords">
</div>
<table class="table table-bordered">
<thead class="bg-info">
<tr>
<td>
编号
</td>
<td>
名称
</td>
<td>
价格
</td>
<td>
类别
</td>
<td>
删除
</td>
</tr>
</thead>
<tbody>
<tr v-for="(pro,index) in Findproducts">
<td>{{index}} </td>
<td>{{pro.name}} </td>
<td>{{pro.price}} </td>
<td>{{pro.categray}} </td>
<td>
<button class="btn-danger" @click="deleteP(index)">删除</button>
</td>
</tr>
</tbody>
</table>
</fieldset>
</div>
<script src="js/vue.min.js"></script>
<script>
var tmp = {
newProduct: {
name: "",
price: "",
categray: "",
},
categraies: ["手机/电脑", "护肤/美妆", "男装/女装"],
products: [
{
name: "华为荣耀",
price: "1659",
categray: "手机/电脑",
},
{
name: "鸿星尔克",
price: "234",
categray: "男装/女装",
},
],
keyWords: "",
}
var vm = new Vue({
el: "#app",
data: tmp,
methods: {
deleteP:function(index) {
if (confirm("你真的要删除该条数据吗")) {
this.products.splice(index, 1);
}
},
createP() {
this.products.push(this.newProduct);
this.newProduct = {
name: "",
price: "",
categray: "",
};
},
},
computed: {
Findproducts() {
var _this = this;
return _this.products.filter(function (prod) {
return prod.name.indexOf(_this.keyWords) != -1;
})
}
},
})
</script>
</body>
</html>
四、Vue组件
1.Vue组件的介绍
组件系统是Vue中一个重要的概念,它提供了一种抽象结构,可以使用独立和可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树
组件是可复用的Vue实例
组件应该挂载到某个Vue实例下,否则它不会生效
组件的使用步骤
A.创建组件:调用Vue.extend()方法创建组件
Vue.extend()方法是Vue的扩展,调用Vue.extend()方法创建的是一个组件
Vue.extend()方法有一个选项对象,选项对象的template属性用于定义组件要渲染的HTML
B.注册组件:调用Vue.component()方法注册组件
两个参数:第一个参数是组件的标签,第二个参数是组件
C.使用组件:使用Vue实例页面内自定义组件标签
注意:组件命名方式有两种短横线分割法(推荐使用)、首字母大写法
2.Vue组件的使用
2.1.组件注册
2.1.1.全局注册
在调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在当前页面的任意Vue实例下使用
<div id="app1">
<h1>Vue实例1</h1>
<my-comp></my-comp>
</div>
<div id="app2">
<h1>Vue实例2</h1>
<my-comp></my-comp>
</div>
<script src="js/Vue.min.js"></script>
<script>
//创建组件
var myComp=Vue.extend({
template:"<h3>使用全局Vue组件!</h3>"
});
//注册组件
Vue.component("my-comp",myComp)
//实例化Vue实例1
var vm1=new Vue({
el:"#app1"
});
//实例化Vue实例2
var vm1=new Vue({
el:"#app2"
});
</script>
2.1.2.局部注册
如果不需要全局组件,或者是让组件在其他组件内使用,可以在Vue实例化时,设置选项参数的components参数属性实现局部注册
<script>
//创建组件
var myComp=Vue.extend({
template:"<h3>使用局部Vue组件!</h3>"
});
//实例化Vue
var vm=new Vue({
el:"#app",
//局部注册组件
components:{
"my-comp":myComp
}
});
</script>
2.2.组件注册语法糖
即在注册组件时直接创建和注册组件,Vue在背后会自动地调用Vue.extend()
2.2.1.简化全局组件
//全局注册,my-comp是组件标签名称
Vue.component('my-comp',{
template:'<div><h1>简化全局组件</h1></div>'
})
2.2.2.简化局部组件
var vm=new Vue({
el:"#app",
//局部组件,my-comp是组件标签名称
components:{
'my-comp':{
template:'<div><h1>简化局部组件</h1></div>'
}
}
});
2.3.使用<script>或<template>
尽管语法糖简化了组件注册,但在template选项中拼接HTML元素比较麻烦,也导致了HTML和JavaScript的高耦合性。
Vue提供了两种方式将定义在JavaScript中的HTML模板分离出来
2.3.1.使用<script>标签
<div id="app">
<my-comp></my-comp>
</div>
<script type="text/x-template" id="myComp">
<div><h1>使用script标签</h1></div>
</script>
<script>
Vue.component('my-comp',{
template:'#myComp'
});
var vm=new Vue({
el:"#app",
components:{
'my-comp':{
template:'#myComp'
}
}
});
</script>
注意:使用<script>标签时,type指定为text-x-template,旨在高数浏览器这不是一段js脚本,这样浏览器在解析HTML文档时会忽略<script>标签内定义的内容
2.3.2.使用<template>标签
如果使用<template>标签,则不需要指定type属性
<div id="app">
<my-comp></my-comp>
</div>
<template id="myComp">
<div><h1>使用template标签</h1></div>
</template>
<script>
var vm=new Vue({
el:"#app",
components:{
'my-comp':{
template:'#myComp'
}
}
});
</script>
建议使用 <script>或<template>标签来定义组件的HTML模板,这使得HTML代码和JavaScript代码是分离的,便于维护阅读
2.4.组件的data和el选项
一般实例化Vue的多数选项也可以用在Vue.extend()或Vue.component()中,不过有两个特殊选项参数除外,即data和el。Vue.js规定:在定义组件的选项时,data和el选项必须使用函数
如果data选项指向某个对象,这意味着所有的组件实例共用一个data。
使用一个函数作为data选项,让这个函数返回一个新对象,语法如下:
//全局注册组件
Vue.component("组件名称",{
el:function(){...},
data:function(){
return{
属性:值
}
},
template:"组件模板"
})
或
//局部注册组件
var vm1=new Vue({
el:"#app",
components:{
"组件名称":{
el:function(){...},
data:function(){
return{
属性:值
}
},
template:"组件模板"
}
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件</title>
</head>
<body>
<div id="app">
<first-comp></first-comp>
<second-comp></second-comp>
<three-comp></three-comp>
<four-comp></four-comp>
<five-comp></five-comp>
<six-comp></six-comp>
</div>
<p>===================================================</p>
<div id="app1">
<first-comp></first-comp>
<second-comp></second-comp>
<three-comp></three-comp>
<four-comp></four-comp>
</div>
<!-- 1.4 通过template模板标签实现H5js分离 -->
<template id="twoTp">
<h2 style="color: purple">局部组件,基本类型创建</h2>
</template>
<!-- 1.3 H5 和js分开 -->
<script src="js/vue.min.js"></script>
<script type="js/x-handlebars-template" id="oneTp">
<h2 style='color: cyan'>局部组件,基本类型创建 </h2>
</script>
<script>
//组件使用三步骤
//1.创建组件
var firstComp = Vue.extend({
template: "<h2 style='color: red'>全局组件,基本类型创建</h2>"
});
//1.1创建局部组件
var secondComp = Vue.extend({
template: "<h2 style='color: orange'>局部组件,基本类型创建</h2>"
});
//2.注册组件
Vue.component("first-comp", firstComp);
//1.2全局 创建注册一步到位(语法糖)
Vue.component("three-comp", {
template: "<h2 style='color: yellow'>全局组件,基本类型创建</h2>"
});
//3.使用组件
var vm = new Vue({
el: "#app",
components: {
"second-comp": secondComp, //局部组件
//使用语法糖,一步到位创建局部组件
"four-comp": {
template: "<h2 style='color: green'>局部组件,基本类型创建</h2>"
},
"five-comp": {
template: "#oneTp"
},
"six-comp": {
template: "#twoTp"
},
}
});
var vm1 = new Vue({
el: "#app1",
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>H5 和JS 分离组件</title>
</head>
<body>
<div id="app">
<!-- 直接绑定 -->
<seven-comp message1="下雨啦"></seven-comp>
<seven-comp :message1="message"></seven-comp>
</div>
<template id="oneTp">
<p>这是data的模板 {{message1}} </p>
</template>
<script src="js/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "张三",
},
components: {
"seven-comp": {
// data: function() {
// return {
// message: "李四"
// }
// },
props:["message1"],
template: "#oneTp",
}
}
})
</script>
</body>
</html>
3.组件之间的通信
子组件只能在父组件中使用
通过props向子组件传递数据
props的值是字符串数组
var component=Vue.extend({
props:["属性名","属性名"],
template:"模板"
});
HTML属性名的大小写是不敏感的,所以浏览器会把所有的大写字符解释为小写字符
props的值是对象
var component=Vue.extend({
props:{
属性名:String,
属性名:Number
},
template:"模板"
});
<!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>
</head>
<body>
<div id="app">
<parent-comp></parent-comp>
</div>
<!-- 1.创建子组件 -->
<!-- 2.创建父组件,并在其中注册子组件 -->
<!-- 3.注册父组件 -->
<!-- 4.调用父组件 -->
<template id="oneTP">
<h2 style="color: lightblue;">我是子组件</h2>
</template>
<template id="twoTP">
<p>
我是父组件
<child-comp></child-comp>
</p>
</template>
<script src="js/vue.min.js"></script>
<script>
var childComp= Vue.extend({
template:"#oneTP"
});
var parentComp=Vue.extend({
template:"#twoTP",
components:{
"child-comp":childComp
}
});
Vue.component("parent-comp",parentComp)
var vm=new Vue({
el:"#app",
})
</script>
</body>
</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>
</head>
<body>
<div id="app">
<first-Comp :dat1="dat" :title2="title" :content3="content"></first-Comp>
</div>
<template id="oneTp">
<div>
<p>日期:{{dat1}} </p>
<p>标题:{{title2}} </p>
<p>内容:{{content3}} </p>
</div>
</template>
<script src="js/vue.min.js"></script>
<script >
Vue.component("first-comp",{
props:["dat1","title2","content3"],
template:"#oneTp"
})
var vm=new Vue({
el:"#app",
data:{
title:"卓越项目启动",
dat:"2023-06-24",
content:"今天我们正在做第二个卓越项目,我想把她改成前后端分离的项目",
}
})
</script>
</body>
</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>
<style>
.banner{
background-color: lightblue;
height: 200px;
line-height: 200px;
text-align: center;
}
</style>
</head>
<body>
<div id="app">
<parent-comp message="消暑一下" :is-style="isStyle"></parent-comp>
</div>
<!-- 1.定义子组件和父组件 -->
<!-- 子组件模板 -->
<template id="childComp">
<span>{{subMessage}} </span>
</template>
<!-- 父组件模板 -->
<template id="parentComp">
<h1 :class="{banner:isStyle}">
{{message}}
<child-comp sub-message="不玩就out了"></child-comp>
</h1>
</template>
<script src="js/vue.min.js"></script>
<script>
var vm=new Vue({
el:"#app",
data:{
isStyle:true,
},
components:{
"parent-comp":{
props:["message","isStyle"],
template:"#parentComp",
components:{
"child-comp":{
props:{
subMessage:String
},
template:"#childComp"
}
}
}
}
})
</script>
</body>
</html>
4.Vue的插槽
将所携带的内容插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性
插槽显不显示、怎么显示是由父组件来控制的,而插槽在哪里显示是由子组件来进行控制
<slot>标签是组件内部的占位符
注意:插槽是为网络组件创建”声明性API“的一种方法。它们混入到用户的DOM中,对整个组件进行渲染,从而将不同的DOM树组合在一起
4.1.默认插槽
只能有一个
4.2.具名插槽
当需要使用多个插槽时,要使用具名插槽
4.3.作用域插槽的使用
插槽可以控制HTML模板的显示与不显示
作用域插槽(slot-scope)其实就是带数据的插槽
原来父组件可以通过绑定数据传递给子组件,而作用域插槽可以通过子组件绑定数据传递给父组件
作用域插槽的使用场景既可以复用子组件的slot,又可以使slot内容不一致
<!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>
</head>
<body>
<!-- 默认插槽 没有名字 -->
<div id="app">
<input type="checkbox" name="" id="" v-model="ok">
<div v-if="ok">
<login-comp>用户登录</login-comp>
</div>
<div v-else="ok">
<login-comp>用户注册</login-comp>
</div>
</div>
<template id="tp1">
<div>
<slot></slot>
<p>
用户名<input type="text">
</p>
<p>
密码<input type="text">
</p>
</div>
</template>
<script src="js/vue.min.js"></script>
<script>
Vue.component("login-comp",{
template:"#tp1",
});
var vm=new Vue({
el:"#app",
data:{
ok:true,
},
})
</script>
</body>
</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>
</head>
<body>
<div id="app">
<book-list :books="booklist">
<template slot="book" slot-scope="props">
<li>
{{props.bookname}}
</li>
</template>
</book-list>
</div>
<template id="tp2">
<div>
<ul>
<slot name="book" v-for="item in books" :bookname="item.name"></slot>
</ul>
</div>
</template>
<script src="js/vue.min.js"></script>
<script>
var vm=new Vue({
el:"#app",
data:{
booklist:[
{name:"第一本书"},
{name:"第二本书"},
{name:"第三本书"},
{name:"第四本书"},
]
},
components:{
"book-list":{
props:["books"],
template:"#tp2"
}
}
})
</script>
</body>
</html>