1.第一个vue实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>01.创建第一个Vue实例</title>
</head>
<body>
<!--创建Vue实例,初始化渲染-->
<!--1.容器(Vue所管理的范围)-->
<!--2.导包(开发版本包/生产版本包) 官网查找-->
<!--(1) 开发环境版本,包含了有帮助的命令行警告 (使用)-->
<!--<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>-->
<!--(2) 生产环境版本,优化了尺寸和速度 -->
<!--<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>-->
<!--3.实例(new对象)-->
<!--4.配置项(el:挂载点,data:数据)-->
<!--5.完成渲染-->
<!--1.容器-->
<div class="box"></div>
<div class="box2"></div>
<div id="app">
<!-- 编写用于渲染的代码逻辑-->
<h1>{{msg}}</h1>
{{count}}
</div>
<!--2.引入开发版本核心包,该包包含完整的注释和警告-->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
<!-- 一旦引入VueJS核心包,在全局环境中,就有了Vue构造函数,有了构造函数就可以创建实例-->
//基于引入的核心包,创建实例对象,在对象中写入配置项(el,data)
const app = new Vue({
//通过el配置选择器(指定渲染的容器),只当Vue管理的是哪个盒子
el: "#app",
//通过data提供数据
data: {
msg: "hello!!!",
count: 666
}
})
</script>
</body>
</html>
2.插值表达式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>02.插值表达式</title>
</head>
<body>
<!--插值表达式:Vue的一种模板语法
作用:利用表达式,进行插值渲染
语法:{{表达式}}
使用的注意:
(1)不能在标签属性中使用{{}}
(2)使用的数据要存在
(3)支持的式表达式,不是语句 if for
-->
<div id="app">
<p>{{nickname}}</p>
<p>{{nickname.toUpperCase()}}</p>
<p>{{nickname + ',hello!!!'}}</p>
<p>{{age>=18?'成年':'未成年'}}</p>
<p>{{nickname}}的朋友:{{friend.name}}</p>
<p>{{friend.desc}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
nickname: "cc",
age:22,
friend:{
name:"lucy",
desc:"热爱学习"
}
}
})
</script>
</body>
</html>
3.Vue响应式特性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>03.Vue的响应式特性</title>
</head>
<body>
<!--数据的响应式处理--》响应式:数据变化,视图自动更新-->
<!--使用Vue开发--》专注于业务核心逻辑即可-->
<div id="app">
{{msg}}
{{count}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
// 响应式数据
msg: "你好,cc",
count: 100
}
})
//data中的数据,是会被添加到实例上
//1.访问数据 实例.属性名
//2.修改数据 实例.属性名=新值
</script>
</body>
</html>
4.Vue指令
4.1 vue-html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>04.Vue指令</title>
</head>
<body>
<!--指令:带有v-前缀的特殊标签属性,不同属性对应不同的功能-->
<!--vue-html:
作用:设置元素的innerHTML,可以解析标签中的数据;
语法:v-html="表达式"-->
<div id="app">
<div v-html="msg"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
//注:数据值要用''包裹
msg:'<a href="https://www.baidu.com/">百度</a>'
// msg:'<h3>百度</h3>'
}
})
</script>
</body>
</html>
4.2 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>04.vue指令(2)</title>
</head>
<body>
<!--vue指令:v-show和v-if-->
<!--(1)v-show
作用:控制元素显示隐藏
语法:v-show="表达式",表达式值true显示,false隐藏
底层原理:切换css的display:none来控制显示隐藏
适用场景:频繁切换需要隐藏的场景-->
<!--(2)v-if
作用:控制元素显示隐藏(条件渲染)
语法:v-if="表达式",表达式值true显示,false隐藏
底层原理:根据判断条件 控制元素的创建和移除(条件渲染)
适用场景:要么显示,要么隐藏,不频繁切换的场景-->
<div id="app">
<div v-show="flag" class="box">v-show控制</div>
<div v-if="flag" class="box">v-if控制</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
flag:true
}
})
</script>
<style>
.box{
text-align: center;
border-radius: 5px;
box-shadow: 2px 2px 2px #ccc;
}
</style>
</body>
</html>
4.3 v-else 和 v-else-if
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>04.Vue指令(3)</title>
</head>
<body>
<!--vue指令:v-else 和 v-else-if-->
<!--作用:辅助v-if进行判断渲染
语法:v-else v-else-if="表达式"
注意:需要紧挨着v-if一起使用-->
<div id="app">
<p v-if="gender===1">性别:男</p>
<p v-else>性别:女</p>
<hr>
<p v-if="score>=90">成绩评定A:奖励电脑一台</p>
<p v-else-if="score>=80">成绩评定B:奖励周末郊游</p>
<p v-else-if="score>=70">成绩评定C:奖励零食礼包</p>
<p v-else>成绩评定D:惩罚一周不能玩手机</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
gender:0,
score:80
}
})
</script>
</body>
</html>
4.4 v-on
<!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>04.Vue指令(4)</title>
</head>
<body>
<!--vue指令:v-on
作用:注册事件,注册事件=添加监听+提供处理逻辑
语法:(1)v-on:事件名=“内联语句(可执行语句)"
(2)v-on:事件名=”methods中的函数名“
简写: @事件名
-->
<div id="app">
<!-- (1)v-on:事件名=”内联语句“-->
<button v-on:click="count--">-1</button>
<span>{{count}}</span>
<button v-on:click="count++">+1</button>
<button v-on:click="count=count+2">+2</button>
<!-- v-on简写方式:@事件名(将v-on:替换成@)-->
<button v-on:click="count=count+2">+2</button>
<hr>
<!-- (2)v-on:事件名=”methods中的函数名"-->
<button @click="fn">切换显示隐藏</button>
<h1 v-show="isShow">hello</h1>
<hr>
<!-- v-on调用传参-->
<button @click="price(-5)">-5</button>
<span>{{count}}</span>
<button @click="price(5)">+5</button>
<hr>
<div class="box">
<h1>yueyue自动售货机</h1>
<button @click="yueyue(5)">可乐5元</button>
<button @click="yueyue(8)">牛奶10元</button>
<button @click="yueyue(10)">咖啡10元</button>
</div>
<p>余额:{{money}}元</p>
<hr>
<div class="box2">
<button @click="datasum(1,1)">{{param1}}+{{param2}}={{sum}}</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
//提供数据
data: {
count: 100,
isShow: true,
money:100,
param1:0,
param2:0,
sum:0
},
//提供方法
methods: {
//让提供的所有methods中的函数,this都指向当前实例
fn() {
// console.log("执行了fn",app.isShow)
console.log(app === this) //true
app.isShow = !app.isShow
this.isShow = !this.isShow
},
price(x){
this.count+=x
console.log("count=",this.count)
},
yueyue(x){
this.money-=x
if(this.money<0){
alert("余额不足,努力挣钱!!!")
return
}
console.log("money=",this.money)
},
datasum(x,y){
this.param1=x
this.param2=y
this.sum=x+y
console.log(this.param1,"+",this.param2,"=",this.sum(x,y))
}
}
})
</script>
</body>
</html>
4.5 v-bind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>04.Vue指令(5)</title>
</head>
<body>
<!--vue指令:v-bind-->
<!--作用:动态的设置html的标签属性->src url
语法:v-bind:属性名="表达式"
简写: :事件
-->
<div id="app">
<img v-bind:src="imgUrl" v-bind:title="msg" alt=""/>
<!-- 简写-->
<img :src="imgUrl" :title="msg" alt=""/>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
msg: "波仔",
imgUrl: "../images/01.imgs/10-02.png"
},
})
</script>
</body>
</html>
4.6 v-for
<!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>04.vue指令(6)</title>
</head>
<body>
<!--vue指令:v-for-->
<!--作用:基于数据循环,多次渲染整个元素->数组(常用)、对象、数字
语法:v-for="(item,index) in 数组",item每一项,index下标
注意:v-for中使用的是()
-->
<!--v-for="(item,index) in 数组" :key="item.id"
语法:key属性="唯一标识"
作用:给列表项添加的唯一标识,便于vue进行列表项的正确排序复用
注意点:
(1)key的值只能是字符串或数字类型
(2)key的值必须具有唯一性
(3)推荐使用id作为key(唯一),不推荐使用index作为key(会变化,不对应)-->
<div id="app">
<h3>yueyue水果店</h3>
<ul>
<!-- v-for中的index是可以省略的,且当只有一个参数时,括号()也是可以省略的-->
<li v-for="item in list">
{{item}}
</li>
<hr>
<li v-for="(item,index) in list ">
{{item}}---{{index}}
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
list: ['西瓜', '苹果', '香蕉']
},
methods: {}
})
</script>
</body>
</html>
4.7 v-model
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>04.Vue指令(7)</title>
</head>
<body>
<!--vue指令:v-model-->
<!--作用:给表单元素使用,双向数据绑定->可以快速获取或设置表单元素内容
(1)数据变化->视图自动更新
(2)视图变化->数据自动更新-->
<!--语法:v-model='变量'-->
<div id="app">
账户:<input type="text" v-model="username"><br><br>
密码:<input type="password" v-model="password"><br><br>
<button @click="login">登录</button>
<button @click="reset">重置</button>
<hr>
<hr>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
password: ''
},
methods: {
login(username, password) {
this.username = username,
this.password = password
console.log(this.username, this.password)
},
reset() {
this.username = '',
this.password = ''
}
}
})
</script>
</body>
</html>
4.8 案例
4.8.1 指令案例(yueyue书屋)
<!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>01.yueyue书屋</title>
</head>
<body>
<!--需求:
(1)列表的渲染
(2)删除功能
-->
<div id="app">
<h3>yueyue书屋</h3>
<ul>
<li v-for="(item,index) in bookList" :key="item.id">
<span>{{item.name}}</span>
<span>{{item.author}}</span>
<button @click="del(item.id)">删除</button>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
bookList: [
{id: 1, name: "《红楼梦》", author: "曹雪芹"},
{id: 2, name: "《西游记》", author: "吴承恩"},
{id: 3, name: "《水浒传》", author: "施耐庵"},
{id: 4, name: "《三国演义》", author: "罗贯中"}
]
},
methods: {
del(id) {
// console.log("删除",id)
// 通过id进行删除数组中的对应项->filter(不会改变原数组)
// filter:根据条件,保留满足条件的对应项,得到一个新数组
// console.log(this.bookList.filter(item => item.id !== id));
this.bookList=this.bookList.filter(item => item.id !== id)
}
}
})
</script>
</body>
</html>
4.8.2 指令案例(yueyue记事本)
<!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>02.yueyue记事本</title>
</head>
<body>
<!--需求:
(1)列表渲染
(2)删除功能
(3)添加功能
(4)底部统计和清空
-->
<!--主体区域-->
<section id="app">
<!-- 输入框-->
<header class="header">
<h1>yueyue记事本</h1>
<input v-model="todoName" placeholder="请输入任务" class="new-todo"/>
<button @click="add()" class="add">添加任务</button>
</header>
<!--列表区域-->
<section class="main">
<ul class="todo-list">
<li class="todo" v-for="(item,index) in list" :key="item.id">
<div class="view">
<span class="index">{{index + 1}}.</span><label>{{item.name}}</label>
<button @click="del(item.id)" class="destroy"></button>
</div>
</li>
</ul>
</section>
<!--统计和清空->如果没有任务了,底部隐藏掉(v-show)-->
<footer class="footer" v-show="list.length>0">
<!-- 统计-->
<span class="todo-count">合计:<strong>{{list.length}}</strong></span>
<!-- 清空-->
<button @click="clear" class="clear-completed">
清空任务
</button>
</footer>
</section>
<!--底部-->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
//添加功能
//(1)通过v-model绑定输入框-》实时获取表单元素的内容
//(2)点击按钮,进行新增,往数组最前面加(unshift)
const app = new Vue({
// el: ".main", //删除
el: "#app", //添加
data: {
todoName:"",
list: [
{id: 1, name: "跑步一公里"},
{id: 2, name: "跳绳200次"},
{id: 3, name: "游泳100米"}
]
},
methods: {
//删除
del(id) {
console.log(id)
// filter():保留所有不等于该id的项
this.list = this.list.filter(item => item.id !== id)
},
//添加
add(){
if(this.todoName.trim()==''){
alert("请输入任务名称")
return
}
this.list.unshift({
id: +new Date(),
name: this.todoName
})
this.todoName=''
},
//清空
clear(){
this.list=[]
}
}
})
</script>
</body>
</html>
5.指令修改符
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>01.指令修饰符</title>
</head>
<body>
<!--指令修饰符:通过"."指明一些指令后缀,不同后缀封装了不同的操作->简化代码-->
<!--
(1)按键修饰符
@keyup.enter:键盘回车监听
(2)v-model修饰符
v-model.trim:去除首尾空格
v-model.number:转数字
(3)事件修饰符
@事件名.stop:阻止冒泡
@事件名.prevent:阻止默认行为
-->
<!--主体区域-->
<section id="app">
<!-- 输入框-->
<header class="header">
<h1>yueyue记事本</h1>
<!-- 使用指令修饰符-->
<input @keyup.enter="add" v-model="todoName" placeholder="请输入任务" class="text">
<button @click="add" class="add">添加任务</button>
</header>
<!-- 列表区域-->
<section class="main">
<ul class="todo-list">
<li class="todo" v-for="(item,index) in list" :key="item.id">
<div class="view">
<span class="index">{{index + 1}}.</span><label>{{item.name}}</label>
<button @click="del(item.id)" class="destroy"></button>
</div>
</li>
</ul>
</section>
<!--统计和清空->如果没有任务了,底部隐藏掉(v-show)-->
<footer class="footer" v-show="list.length>0">
<!-- 统计-->
<span class="todo-count">合计:<strong>{{list.length}}</strong></span>
<!-- 清空-->
<button @click="clear" class="clear-completed">
清空任务
</button>
</footer>
</section>
<!--底部-->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
//添加功能
//(1)通过v-model绑定输入框-》实时获取表单元素的内容
//(2)点击按钮,进行新增,往数组最前面加(unshift)
const app = new Vue({
// el: ".main", //删除
el: "#app", //添加
data: {
todoName:"",
list: [
{id: 1, name: "跑步一公里"},
{id: 2, name: "跳绳200次"},
{id: 3, name: "游泳100米"}
]
},
methods: {
//删除
del(id) {
console.log(id)
// filter():保留所有不等于该id的项
this.list = this.list.filter(item => item.id !== id)
},
//添加
add(){
if(this.todoName.trim()==''){
alert("请输入任务名称")
return
}
this.list.unshift({
id: +new Date(),
name: this.todoName
})
this.todoName=''
},
//清空
clear(){
this.list=[]
}
}
})
</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>02.指令修饰符(1)</title>
</head>
<body>
<div id="app">
<h3>@keyup.enter->监听键盘回车事件</h3>
<input @keyup.enter="fn" v-model="username" type="text">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: ''
},
methods: {
fn(e) {
// if(e.key==='Enter'){
console.log('键盘回车时触发', this.username)
}
// }
}
})
</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>02.指令修饰符(2)</title>
<style>
.father {
width: 200px;
height: 200px;
background-color: pink;
margin-top: 20px;
}
.son {
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
</head>
<body>
<div id="app">
<h3>v-model修饰符 .trim // .number</h3>
姓名:<input v-model.trim="username" type="text"><br>
年纪:<input v-model.number="age" type="text"><br>
<h3>@事件名.stop → 阻止冒泡</h3>
<div @click="fatherFn" class="father">
<div @click.stop="sonFn" class="son">son</div>
</div>
<h3>@事件名.prevent → 阻止默认行为</h3>
<a @click.prevent href="http://www.baidu.com">阻止默认行为</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
age: '',
},
methods: {
fatherFn () {
alert('father被点击了')
},
sonFn (e) {
// e.stopPropagation()
alert('son被点击了')
}
}
})
</script>
</body>
</html>
6.V-bind增强
6.1 v-bind对于样式控制
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>03.v-bind对于样式控制的增强</title>
<style>
.box {
width: 200px;
height: 200px;
border: 3px solid #000;
font-size: 30px;
margin-top: 10px;
}
.pink {
background-color: pink;
}
.big {
width: 300px;
height: 300px;
}
</style>
</head>
<body>
<!--v-bind对于样式控制的增强-->
<!--为了方便开发者进行样式控制,Vue拓展了v-bind的语法,可以针对class类名和style行内样式进行控制-->
<!--v-bind对于样式控制的增强--操作class -->
<!--语法:class="对象/数组"
(1)对象->键就是类名,值是布尔值,如果值为true,有这个类,否则没有这个类
<div class="box" :class="{类名1:布尔值,类名2:布尔值}"></div>
(2)数组->数组中所有的类,都会添加到盒子上,本质就是一个class列表
<div class="box" :class="[类名1,类名2,类名3]"></div>
-->
<div id="app">
<div class="box" :class="{ pink: true, big: true }">黑马程序员</div>
<div class="box" :class="['pink', 'big']">黑马程序员</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
6.2 v-bind对于样式控制增强
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>03.v-bind对于样式控制的增强-操作style</title>
<style>
.box {
width: 200px;
height: 200px;
background-color: rgb(187, 150, 156);
}
.progress {
height: 25px;
width: 400px;
border-radius: 15px;
background-color: #272425;
border: 3px solid #272425;
box-sizing: border-box;
margin-bottom: 30px;
}
.inner {
width: 50%;
height: 20px;
border-radius: 10px;
text-align: right;
position: relative;
background-color: #409eff;
background-size: 20px 20px;
box-sizing: border-box;
transition: all 1s;
}
.inner span {
position: absolute;
right: -20px;
bottom: -25px;
}
</style>
</head>
<body>
<!--语法::style="样式对象"
<div class="box" :style="{CSS属性名1:CSS属性值,CSS属性名2:CSS属性值}"></div>
适用场景:某个具体属性的动态设置
-->
<div id="app2">
<div class="box" :style="{ width: '400px', height: '400px', backgroundColor: 'green' }"></div>
</div>
<div id="app">
<!-- 外层盒子底色 (黑色) -->
<div class="progress">
<!-- 内层盒子 - 进度(蓝色) -->
<div class="inner" :style="{ width: percent + '%' }">
<span>{{ percent }}%</span>
</div>
</div>
<button @click="percent = 25">设置25%</button>
<button @click="percent = 50">设置50%</button>
<button @click="percent = 75">设置75%</button>
<button @click="percent = 100">设置100%</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
percent: 30
}
})
</script>
</body>
</html>
6.3 案例
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>03.案例—京东秒杀tab导航高亮</title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
display: flex;
border-bottom: 2px solid #e01222;
padding: 0 10px;
}
li {
width: 100px;
height: 50px;
line-height: 50px;
list-style: none;
text-align: center;
}
li a {
display: block;
text-decoration: none;
font-weight: bold;
color: #333333;
}
li a.active {
background-color: #e01222;
color: #fff;
}
</style>
</head>
<body>
<!--核心思路:
(1)基于数据动态渲染tab,->v-for
(2)准备下标高亮的是哪一个tab->activeIndex
(3)基于下标,动态控制class类名 v-bind:class
-->
<div id="app">
<ul>
<!-- 所谓高亮,其实就是修改下标-->
<li v-for="(item, index) in list" :key="item.id" @click="activeIndex = index">
<a :class="{ active: index === activeIndex }" href="#">{{ item.name }}</a>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
activeIndex: 2, // 记录高亮
list: [
{ id: 1, name: '京东秒杀' },
{ id: 2, name: '每日特价' },
{ id: 3, name: '品类秒杀' }
]
}
})
</script>
</body>
</html>
7. v-model应用于其他表单
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>04.v-model应用于其他表单元素</title>
</head>
<body>
<!--常见的表单元素都可以用v-model绑定关联->快速获取或设置表单元素的值
它会根据控件类型自动选取正确的方法来更新元素-->
<div id="app">
<h1>yueyue学习网</h1>
姓名:<input type="text" v-model="username">
<br><br>
是否单身:<input type="radio">男
<input type="radio">女
<br><br>
<!-- 所在城市:-->
所在城市:
<select>
<option>北京</option>
<option>上海</option>
<option>河南省</option>
</select>
<!--
前置理解:
1. name: 给单选框加上 name 属性 可以分组 → 同一组互相会互斥
2. value: 给单选框加上 value 属性,用于提交给后台的数据
结合 Vue 使用 → v-model
-->
性别:
<input v-model="gender" type="radio" name="gender" value="1">男
<input v-model="gender" type="radio" name="gender" value="2">女
<br><br>
<!--
前置理解:
1. option 需要设置 value 值,提交给后台
2. select 的 value 值,关联了选中的 option 的 value 值
结合 Vue 使用 → v-model
-->
所在城市:
<select v-model="cityId">
<option value="101">北京</option>
<option value="102">上海</option>
<option value="103">成都</option>
<option value="104">南京</option>
</select>
<br><br>
自我描述:
<textarea v-model="desc"></textarea>
<button>立即注册</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
isSingle: false,
gender: "2",
cityId: '102',
desc: ""
}
})
</script>
</div>
</body>
</html>
8.计算属性
8.1 计算属性的默认用法
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>05.计算属性</title>
<style>
table {
border: 1px solid #000;
text-align: center;
width: 240px;
}
th,td {
border: 1px solid #000;
}
h3 {
position: relative;
}
</style>
</head>
<body>
<!--计算属性:基于现有的数据,计算出来的新属性,依赖的数据变化,自动重新计算
计算属性->可以将一段求值的代码进行封装-->
<!--语法:
(1)声明再computed配置项中,一个计算属性对应一个函数
(2)使用起来和普通属性一样使用{{计算属性名}}-->
<div id="app">
<h3>小黑的礼物清单</h3>
<table>
<tr>
<th>名字</th>
<th>数量</th>
</tr>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.num }}个</td>
</tr>
</table>
<!-- 目标:统计求和,求得礼物总数 -->
<p>礼物总数:{{ totalCount }} 个</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
// 现有的数据
list: [
{ id: 1, name: '篮球', num: 1 },
{ id: 2, name: '玩具', num: 2 },
{ id: 3, name: '铅笔', num: 5 },
]
},
computed: {
totalCount () {
// 基于现有的数据,编写求值逻辑
// 计算属性函数内部,可以直接通过 this 访问到 app 实例
// console.log(this.list)
// 需求:对 this.list 数组里面的 num 进行求和 → reduce
let total = this.list.reduce((sum, item) => sum + item.num, 0)
return total
}
}
})
</script>
</body>
</html>
8.2.computed和methods
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>06.computed计算属性和methods方法</title>
<style>
table {
border: 1px solid #000;
text-align: center;
width: 300px;
}
th,td {
border: 1px solid #000;
}
h3 {
position: relative;
}
span {
position: absolute;
left: 145px;
top: -4px;
width: 16px;
height: 16px;
color: white;
font-size: 12px;
text-align: center;
border-radius: 50%;
background-color: #e63f32;
}
</style>
</head>
<body>
<!--
作用方面:
(1)计算属性:封装了一段对于数据的处理,求得一个结果
(2)methods:给实例提供一个方法,调用以处理业务逻辑
语法方面:
(1)计算属性
1)写在computed配置项中
2)作为属性,直接使用->this.计算属性{{计算属性}}
(2)methods
1)写在methods配置项中;
2)作为方法,需要调用->this.方法名() {{方法名()}} @事件名=“方法名”
使用computed计算属性值的优势:缓存特性(提升性能)
计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖项变化了,会自动重新计算->并再次缓存
-->
<div id="app">
<h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
<h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
<h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
<h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
<table>
<tr>
<th>名字</th>
<th>数量</th>
</tr>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.num }}个</td>
</tr>
</table>
<p>礼物总数:{{ totalCountFn() }} 个</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
// 现有的数据
list: [
{ id: 1, name: '篮球', num: 3 },
{ id: 2, name: '玩具', num: 2 },
{ id: 3, name: '铅笔', num: 5 },
]
},
methods: {
totalCountFn () {
console.log('methods方法执行了')
let total = this.list.reduce((sum, item) => sum + item.num, 0)
return total
}
},
computed: {
// 计算属性:有缓存的,一旦计算出来结果,就会立刻缓存
// 下一次读取 → 直接读缓存就行 → 性能特别高
// totalCount () {
// console.log('计算属性执行了')
// let total = this.list.reduce((sum, item) => sum + item.num, 0)
// return total
// }
}
})
</script>
</body>
</html>
8.3 计算属性的完整写法
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>07.计算属性完整写法</title>
<style>
input {
width: 30px;
}
</style>
</head>
<body>
<!--计算属性完整写法
计算属性默认的简写:只能读取访问,不能“修改”
如果需要“修改”->需要写计算属性的完整写法
语法(默认写法):
computed:{
计算属性名(){
一段代码逻辑(计算逻辑)
return 结果
}
}
语法(完整写法):
computed:{
计算属性名:{
get(){
一段代码逻辑(计算逻辑)
return 结果
},
set(修改的值){
一段代码逻辑(修改逻辑)
}
}
}
-->
<div id="app">
姓:<input type="text" v-model="firstName"> +
名:<input type="text" v-model="lastName"> =
<span>{{ fullName }}</span><br><br>
<button @click="changeName">改名卡</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: '刘',
lastName: '备',
},
methods: {
changeName () {
this.fullName = '黄忠'
}
},
computed: {
// 简写 → 获取,没有配置设置的逻辑
// fullName () {
// return this.firstName + this.lastName
// }
// 完整写法 → 获取 + 设置
fullName: {
// (1) 当fullName计算属性,被获取求值时,执行get(有缓存,优先读缓存)
// 会将返回值作为,求值的结果
get () {
return this.firstName + this.lastName
},
// (2) 当fullName计算属性,被修改赋值时,执行set
// 修改的值,传递给set方法的形参
set (value) {
// console.log(value.slice(0, 1))
// console.log(value.slice(1))
this.firstName = value.slice(0, 1)
this.lastName = value.slice(1)
}
}
}
})
</script>
</body>
</html>
9.watch监听器
watch是一种用于监视数据变化并执行相应操作的机制,它通常用于Vue.js框架中,可以监视vue实例中的数据变化,并在数据变化时,执行相应的回调函数或触发其他操作;
在Vue.js中,可以通过在Vue实例的选项中定义一个watch对象来使用watch监视,watch对象的属性是要监视的数据属性,值是一个回调函数,用于在数据变化时执行相应的操作。
9.1 watch监听器(监视器)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>08.watch侦听器(监视器)</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 18px;
}
#app {
padding: 10px 20px;
}
.query {
margin: 10px 0;
}
.box {
display: flex;
}
textarea {
width: 300px;
height: 160px;
font-size: 18px;
border: 1px solid #dedede;
outline: none;
resize: none;
padding: 10px;
}
textarea:hover {
border: 1px solid #1589f5;
}
.transbox {
width: 300px;
height: 160px;
background-color: #f0f0f0;
padding: 10px;
border: none;
}
.tip-box {
width: 300px;
height: 25px;
line-height: 25px;
display: flex;
}
.tip-box span {
flex: 1;
text-align: center;
}
.query span {
font-size: 18px;
}
.input-wrap {
position: relative;
}
.input-wrap span {
position: absolute;
right: 15px;
bottom: 15px;
font-size: 12px;
}
.input-wrap i {
font-size: 20px;
font-style: normal;
}
</style>
</head>
<body>
<!--watch侦听器(监视器)
作用:监视数据变化,执行一些业务逻辑或异步操作
语法:
(1)简单写法->简单类型数据,直接监视
data:{
word:'苹果',
obj:{
words:'苹果'
}
},
watch:{
//该方法会在数据变化时,触发执行
数据属性名(newValue,oldValue){
一些业务逻辑 或 异步操作
},
对象.属性名(newValue,oldValue){
一些业务逻辑 或 异步操作
}
}
(2)完整写法->添加额外配置项
-->
<div id="app">
<!-- 条件选择框 -->
<div class="query">
<span>翻译成的语言:</span>
<select>
<option value="italy">意大利</option>
<option value="english">英语</option>
<option value="german">德语</option>
</select>
</div>
<!-- 翻译框 -->
<div class="box">
<div class="input-wrap">
<textarea v-model="obj.words"></textarea>
<span><i>⌨️</i>文档翻译</span>
</div>
<div class="output-wrap">
<div class="transbox">mela</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// 接口地址:https://applet-base-api-t.itheima.net/api/translate
// 请求方式:get
// 请求参数:
// (1)words:需要被翻译的文本(必传)
// (2)lang: 需要被翻译成的语言(可选)默认值-意大利
// -----------------------------------------------
const app = new Vue({
el: '#app',
data: {
// words: ''
obj: {
words: ''
}
},
// 具体讲解:(1) watch语法 (2) 具体业务实现
watch: {
// 该方法会在数据变化时调用执行
// newValue新值, oldValue老值(一般不用)
// words (newValue) {
// console.log('变化了', newValue)
// }
//用于监视obj中的words数据值的变化,如果变化了就执行console.log(...)方法,注意此处属性名监视属性要使用单引号''包裹,具体是否使用''或反引号``包裹看下面图片内容;
'obj.words' (newValue) {
console.log('变化了', newValue)
}
}
})
</script>
</body>
</html>
9.2 watch监听器(简写)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>08.watch简写-业务实现</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 18px;
}
#app {
padding: 10px 20px;
}
.query {
margin: 10px 0;
}
.box {
display: flex;
}
textarea {
width: 300px;
height: 160px;
font-size: 18px;
border: 1px solid #dedede;
outline: none;
resize: none;
padding: 10px;
}
textarea:hover {
border: 1px solid #1589f5;
}
.transbox {
width: 300px;
height: 160px;
background-color: #f0f0f0;
padding: 10px;
border: none;
}
.tip-box {
width: 300px;
height: 25px;
line-height: 25px;
display: flex;
}
.tip-box span {
flex: 1;
text-align: center;
}
.query span {
font-size: 18px;
}
.input-wrap {
position: relative;
}
.input-wrap span {
position: absolute;
right: 15px;
bottom: 15px;
font-size: 12px;
}
.input-wrap i {
font-size: 20px;
font-style: normal;
}
</style>
</head>
<body>
<div id="app">
<!-- 条件选择框 -->
<div class="query">
<span>翻译成的语言:</span>
<select>
<option value="italy">意大利</option>
<option value="english">英语</option>
<option value="german">德语</option>
</select>
</div>
<!-- 翻译框 -->
<div class="box">
<div class="input-wrap">
<textarea v-model="obj.words"></textarea>
<span><i>⌨️</i>文档翻译</span>
</div>
<div class="output-wrap">
<div class="transbox">{{ result }}</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// 接口地址:https://applet-base-api-t.itheima.net/api/translate
// 请求方式:get
// 请求参数:
// (1)words:需要被翻译的文本(必传)
// (2)lang: 需要被翻译成的语言(可选)默认值-意大利
// -----------------------------------------------
const app = new Vue({
el: '#app',
data: {
// words: ''
obj: {
words: ''
},
result: '', // 翻译结果
// timer: null // 延时器id
},
// 具体讲解:(1) watch语法 (2) 具体业务实现
watch: {
// 该方法会在数据变化时调用执行
// newValue新值, oldValue老值(一般不用)
// words (newValue) {
// console.log('变化了', newValue)
// }
'obj.words' (newValue) {
// console.log('变化了', newValue)
// 防抖: 延迟执行 → 干啥事先等一等,延迟一会,一段时间内没有再次触发,才执行
clearTimeout(this.timer)
this.timer = setTimeout(async () => {
const res = await axios({
url: 'https://applet-base-api-t.itheima.net/api/translate',
params: {
words: newValue
}
})
this.result = res.data.data
console.log(res.data.data)
}, 300)
}
}
})
</script>
</body>
</html>
9.3 watch监听器(完整写法)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>08.watch侦听器完整写法</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 18px;
}
#app {
padding: 10px 20px;
}
.query {
margin: 10px 0;
}
.box {
display: flex;
}
textarea {
width: 300px;
height: 160px;
font-size: 18px;
border: 1px solid #dedede;
outline: none;
resize: none;
padding: 10px;
}
textarea:hover {
border: 1px solid #1589f5;
}
.transbox {
width: 300px;
height: 160px;
background-color: #f0f0f0;
padding: 10px;
border: none;
}
.tip-box {
width: 300px;
height: 25px;
line-height: 25px;
display: flex;
}
.tip-box span {
flex: 1;
text-align: center;
}
.query span {
font-size: 18px;
}
.input-wrap {
position: relative;
}
.input-wrap span {
position: absolute;
right: 15px;
bottom: 15px;
font-size: 12px;
}
.input-wrap i {
font-size: 20px;
font-style: normal;
}
</style>
</head>
<body>
<!--watch侦听器完整写法->添加额外配置项
(1)deep:true对复杂类型深度监视
(2)immediate:true初始化立刻执行一次handler方法
-->
<!--
data:{
obj:{
words:'苹果',
lang:'italy'
},
},
watch:{ //watch完整写法
数据属性名:{
deep:true,//深度监视
handler(newValue){
console.log(newValue)
}
}
}
-->
<div id="app">
<!-- 条件选择框 -->
<div class="query">
<span>翻译成的语言:</span>
<select v-model="obj.lang">
<option value="italy">意大利</option>
<option value="english">英语</option>
<option value="german">德语</option>
</select>
</div>
<!-- 翻译框 -->
<div class="box">
<div class="input-wrap">
<textarea v-model="obj.words"></textarea>
<span><i>⌨️</i>文档翻译</span>
</div>
<div class="output-wrap">
<div class="transbox">{{ result }}</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// 需求:输入内容,修改语言,都实时翻译
// 接口地址:https://applet-base-api-t.itheima.net/api/translate
// 请求方式:get
// 请求参数:
// (1)words:需要被翻译的文本(必传)
// (2)lang: 需要被翻译成的语言(可选)默认值-意大利
// -----------------------------------------------
const app = new Vue({
el: '#app',
data: {
obj: {
words: '小黑',
lang: 'italy'
},
result: '', // 翻译结果
},
watch: {
obj: {
deep: true, // 深度监视(对象中的数据可以全部进行监视)
immediate: true, // 立刻执行,一进入页面handler就立刻执行一次
handler (newValue) {
clearTimeout(this.timer)
this.timer = setTimeout(async () => {
const res = await axios({
url: 'https://applet-base-api-t.itheima.net/api/translate',
params: newValue
})
this.result = res.data.data
console.log(res.data.data)
}, 300)
}
}
// 'obj.words' (newValue) {
// clearTimeout(this.timer)
// this.timer = setTimeout(async () => {
// const res = await axios({
// url: 'https://applet-base-api-t.itheima.net/api/translate',
// params: {
// words: newValue
// }
// })
// this.result = res.data.data
// console.log(res.data.data)
// }, 300)
// }
}
})
</script>
</body>
</html>
10. Vue的生命周期
10.1 vue的四个生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>01.Vue生命周期(4个阶段)</title>
</head>
<body>
<!--vue生命周期:一个Vue实例从创建到销毁的整个过程;
生命周期的四个阶段:(1)创建;(2)挂载;(3)更新;(4)销毁
(1)创建阶段:new VUe(),初始化操作,将普通数据转化为响应式数据;
(2)挂载阶段:渲染模板,将数据放到标签模板中;
(3)更新阶段:数据修改,更新视图(用户使用);
(4)销毁阶段:浏览器关闭;-->
<!--1)发初始化渲染请求要在创建阶段的最后,响应式数据完成之后才可以;
2)操作dom:挂载阶段结束之后,模板数据渲染后;-->
<!--Vue生命周期函数(钩子函数)-->
<!--Vue生命周期过程中,会自动运行一些函数,被称为【生命周期钩子】,让开发者可以在【特定阶段】运行自己的代码。-->
<!--在生命周期4个阶段中会vue提供8个函数(钩子函数),分别位于每个阶段的前和后,每个钩子函数中可以写自己要执行的逻辑代码,这样可以实现开发者在特定阶段执行想要执行的代码-->
<!--钩子函数:就是承载vue生命周期中每个阶段要执行的代码,例如:在创建阶段后的钩子函数中可以写发送初始化请求的代码、在挂载阶段后可以写操作dom的代码-->
<!--钩子函数(8个):
(1)before Create:
(2)created(常用):发送初始化,渲染请求;
(3)before Mount
(4)mounted(常用):操作dom
(5)before Update
(6)updated
(7)before Destroy:释放Vue以外的资源(清除定时器,延时器...(会配合组件使用))
(8)destroyed:销毁Vue实例
-->
<div id="app">
<h3>{{title}}</h3>
<div>
<button @click="count--">-</button>
<span>{{count}}</span>
<button @click="count++">+</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
count: 100,
title: '计数器'
},
// 1.创建阶段(准备阶段)
beforeCreate() {
console.log("beforeCreate 响应式数据准备好之前")
},
created() {
console.log("created 响应式数据准备好之后",this.count)
//this.数据名=请求回来的数据
// 可以开始发送初始化渲染的请求
},
// 2.挂载阶段(渲染阶段)
beforeMount() {
console.log("beforeMount 模板渲染之前",document.querySelector('h3').innerHTML)
},
mounted() {
console.log("mounted 模板渲染之后",document.querySelector('h3').innerHTML)
},
// 3.更新阶段
beforeUpdate(){
console.log("beforeUpdate 更新阶段之前(数据修改了,但视图还没更新)",document.querySelector('h3').innerHTML)
},
updated(){
console.log("updated 更新之后(数据修改了,视图也更新了)",document.querySelector('h3').innerHTML)
},
// 4.销毁阶段
beforeDestory() {
console.log("beforeDestory (卸载vue实例)")
},
destoryed() {
console.log("destoryed 销毁之后")
}
})
</script>
</body>
</html>
10.2 vue的生命周期—created
<!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>02.created应用</title>
<style>
* {
margin: 0;
padding: 0;
list-style: none;
}
.news {
display: flex;
height: 120px;
width: 600px;
margin: 0 auto;
padding: 20px 0;
cursor: pointer;
}
.news .left {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding-right: 10px;
}
.news .left .title {
font-size: 20px;
}
.news .left .info {
color: #999999;
}
.news .left .info span {
margin-right: 20px;
}
.news .right {
width: 160px;
height: 120px;
}
.news .right img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
<!-- created:响应式数据准备好了,可以开始发送初始化渲染请求-->
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in list" :key="item.id" class="news">
<div class="left">
<div class="title">{{item.title}}</div>
<div class="info">
<span>{{item.source}}</span>
<span>{{item.time}}</span>
</div>
</div>
<div class="right">
<img src="item.img" alt="">
</div>
</li>
</ul>
</div>
<script src="./tools/vue.js"></script>
<script src="./tools/axios.js"></script>
<script>
// 接口地址:http://hmajax.itheima.net/api/news
// 请求方式:get
const app = new Vue({
el: '#app',
data: {
list: []
},
async created(){
//1.发送请求,获取数据
const res=await axios.get('http://hmajax.itheima.net/api/news')
//2.将数据更新给data中的list
this.list=res.data.data
// console.log(res)
}
})
</script>
</body>
</html>
10.3 vue的生命周期—mounted
<!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>03.mounted应用</title>
<!-- mounted:模板渲染完成,可以开始操作DOM-->
<!-- 在mounted完成之后,就代表着dom模板已经渲染完成了,才可以操作dom-->
<!-- 初始化样式 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset.css@2.0.2/reset.min.css">
<!-- 核心样式 -->
<style>
html,
body {
height: 100%;
}
.search-container {
position: absolute;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.search-container .search-box {
display: flex;
}
.search-container img {
margin-bottom: 30px;
}
.search-container .search-box input {
width: 512px;
height: 16px;
padding: 12px 16px;
font-size: 16px;
margin: 0;
vertical-align: top;
outline: 0;
box-shadow: none;
border-radius: 10px 0 0 10px;
border: 2px solid #c4c7ce;
background: #fff;
color: #222;
overflow: hidden;
box-sizing: content-box;
-webkit-tap-highlight-color: transparent;
}
.search-container .search-box button {
cursor: pointer;
width: 112px;
height: 44px;
line-height: 41px;
line-height: 42px;
background-color: #ad2a27;
border-radius: 0 10px 10px 0;
font-size: 17px;
box-shadow: none;
font-weight: 400;
border: 0;
outline: 0;
letter-spacing: normal;
color: white;
}
body {
background: no-repeat center /cover;
background-color: #edf0f5;
}
</style>
</head>
<body>
<div class="container" id="app">
<div class="search-container">
<img src="https://www.itheima.com/images/logo.png" alt="">
<div class="search-box">
<input type="text" v-model="words" id="inp">
<button>搜索一下</button>
</div>
</div>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
words: ''
},
// 核心思路:
// 1.等输入框渲染出来
// 2.让输入框获取焦点
mounted() {
console.log(document.querySelector('#inp'));
document.querySelector('#inp').focus()
}
})
</script>
</body>
</html>
10.4 案例-yueyue记账清单
10.4.1 需求介绍
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>04.yueyue记账清单</title>
<!-- yueyue记账清单
功能需求:
(1)基本渲染;
(2)添加功能;
(3)删除功能;
(4)饼图渲染;
-->
<!-- CSS only -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
/>
<style>
.red {
color: red!important;
}
.search {
width: 300px;
margin: 20px 0;
}
.my-form {
display: flex;
margin: 20px 0;
}
.my-form input {
flex: 1;
margin-right: 20px;
}
.table > :not(:first-child) {
border-top: none;
}
.contain {
display: flex;
padding: 10px;
}
.list-box {
flex: 1;
padding: 0 30px;
}
.list-box a {
text-decoration: none;
}
.echarts-box {
width: 600px;
height: 400px;
padding: 30px;
margin: 0 auto;
border: 1px solid #ccc;
}
tfoot {
font-weight: bold;
}
@media screen and (max-width: 1000px) {
.contain {
flex-wrap: wrap;
}
.list-box {
width: 100%;
}
.echarts-box {
margin-top: 30px;
}
}
</style>
</head>
<body>
<div id="app">
<div class="contain">
<!-- 左侧列表 -->
<div class="list-box">
<!-- 添加资产 -->
<form class="my-form">
<input type="text" class="form-control" placeholder="消费名称" />
<input type="text" class="form-control" placeholder="消费价格" />
<button type="button" class="btn btn-primary">添加账单</button>
</form>
<table class="table table-hover">
<thead>
<tr>
<th>编号</th>
<th>消费名称</th>
<th>消费价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>帽子</td>
<td>99.00</td>
<td><a href="javascript:;">删除</a></td>
</tr>
<tr>
<td>2</td>
<td>大衣</td>
<td class="red">199.00</td>
<td><a href="javascript:;">删除</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4">消费总计: 298.00</td>
</tr>
</tfoot>
</table>
</div>
<!-- 右侧图表 -->
<div class="echarts-box" id="main"></div>
</div>
</div>
<script src="../echarts.min.js"></script>
<script src="../vue.js"></script>
<script src="../axios.js"></script>
<script>
/**
* 接口文档地址:
* https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
*
* 功能需求:
* 1. 基本渲染
* 2. 添加功能
* 3. 删除功能
* 4. 饼图渲染
*/
const app = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
</html>
10.4.2 删除功能
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>04.yueyue记账清单-删除功能</title>
<!-- CSS only -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
/>
<style>
.red {
color: red!important;
}
.search {
width: 300px;
margin: 20px 0;
}
.my-form {
display: flex;
margin: 20px 0;
}
.my-form input {
flex: 1;
margin-right: 20px;
}
.table > :not(:first-child) {
border-top: none;
}
.contain {
display: flex;
padding: 10px;
}
.list-box {
flex: 1;
padding: 0 30px;
}
.list-box a {
text-decoration: none;
}
.echarts-box {
width: 600px;
height: 400px;
padding: 30px;
margin: 0 auto;
border: 1px solid #ccc;
}
tfoot {
font-weight: bold;
}
@media screen and (max-width: 1000px) {
.contain {
flex-wrap: wrap;
}
.list-box {
width: 100%;
}
.echarts-box {
margin-top: 30px;
}
}
</style>
</head>
<body>
<div id="app">
<div class="contain">
<!-- 左侧列表 -->
<div class="list-box">
<!-- 添加资产 -->
<form class="my-form">
<input v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
<input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
<button @click="add" type="button" class="btn btn-primary">添加账单</button>
</form>
<table class="table table-hover">
<thead>
<tr>
<th>编号</th>
<th>消费名称</th>
<th>消费价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
<td><a @click="del(item.id)" href="javascript:;">删除</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
</tr>
</tfoot>
</table>
</div>
<!-- 右侧图表 -->
<div class="echarts-box" id="main"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 接口文档地址:
* https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
*
* 功能需求:
* 1. 基本渲染
* (1) 立刻发送请求获取数据 created
* (2) 拿到数据,存到data的响应式数据中
* (3) 结合数据,进行渲染 v-for
* (4) 消费统计 => 计算属性
* 2. 添加功能
* (1) 收集表单数据 v-model
* (2) 给添加按钮注册点击事件,发送添加请求
* (3) 需要重新渲染
* 3. 删除功能
* (1) 注册点击事件,传参传 id
* (2) 根据 id 发送删除请求
* (3) 需要重新渲染
* 4. 饼图渲染
*/
const app = new Vue({
el: '#app',
data: {
list: [],
name: '',
price: ''
},
computed: {
totalPrice () {
return this.list.reduce((sum, item) => sum + item.price, 0)
}
},
created () {
// const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
// params: {
// creator: '小黑'
// }
// })
// this.list = res.data.data
this.getList()
},
methods: {
async getList () {
const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
params: {
creator: '小黑'
}
})
this.list = res.data.data
},
async add () {
if (!this.name) {
alert('请输入消费名称')
return
}
if (typeof this.price !== 'number') {
alert('请输入正确的消费价格')
return
}
// 发送添加请求
const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
creator: '小黑',
name: this.name,
price: this.price
})
// 重新渲染一次
this.getList()
this.name = ''
this.price = ''
},
async del (id) {
// 根据 id 发送删除请求
const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)
// 重新渲染
this.getList()
}
}
})
</script>
</body>
</html>
10.4.3 基本渲染
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>yueyue记账清单-基本渲染</title>
<!-- CSS only -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
/>
<style>
.red {
color: red!important;
}
.search {
width: 300px;
margin: 20px 0;
}
.my-form {
display: flex;
margin: 20px 0;
}
.my-form input {
flex: 1;
margin-right: 20px;
}
.table > :not(:first-child) {
border-top: none;
}
.contain {
display: flex;
padding: 10px;
}
.list-box {
flex: 1;
padding: 0 30px;
}
.list-box a {
text-decoration: none;
}
.echarts-box {
width: 600px;
height: 400px;
padding: 30px;
margin: 0 auto;
border: 1px solid #ccc;
}
tfoot {
font-weight: bold;
}
@media screen and (max-width: 1000px) {
.contain {
flex-wrap: wrap;
}
.list-box {
width: 100%;
}
.echarts-box {
margin-top: 30px;
}
}
</style>
</head>
<body>
<div id="app">
<div class="contain">
<!-- 左侧列表 -->
<div class="list-box">
<!-- 添加资产 -->
<form class="my-form">
<input type="text" class="form-control" placeholder="消费名称" />
<input type="text" class="form-control" placeholder="消费价格" />
<button type="button" class="btn btn-primary">添加账单</button>
</form>
<table class="table table-hover">
<thead>
<tr>
<th>编号</th>
<th>消费名称</th>
<th>消费价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
<td><a href="javascript:;">删除</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
</tr>
</tfoot>
</table>
</div>
<!-- 右侧图表 -->
<div class="echarts-box" id="main"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 接口文档地址:
* https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
*
* 功能需求:
* 1. 基本渲染
* (1) 立刻发送请求获取数据 created
* (2) 拿到数据,存到data的响应式数据中
* (3) 结合数据,进行渲染 v-for
* (4) 消费统计 => 计算属性
* 2. 添加功能
* 3. 删除功能
* 4. 饼图渲染
*/
const app = new Vue({
el: '#app',
data: {
list: []
},
computed: {
totalPrice () {
return this.list.reduce((sum, item) => sum + item.price, 0)
}
},
async created () {
const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
params: {
creator: 'yueyue'
}
})
this.list = res.data.data
}
})
</script>
</body>
</html>
10.4.4 添加功能
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>04.yueyue记账清单-添加功能</title>
<!-- CSS only -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
/>
<style>
.red {
color: red!important;
}
.search {
width: 300px;
margin: 20px 0;
}
.my-form {
display: flex;
margin: 20px 0;
}
.my-form input {
flex: 1;
margin-right: 20px;
}
.table > :not(:first-child) {
border-top: none;
}
.contain {
display: flex;
padding: 10px;
}
.list-box {
flex: 1;
padding: 0 30px;
}
.list-box a {
text-decoration: none;
}
.echarts-box {
width: 600px;
height: 400px;
padding: 30px;
margin: 0 auto;
border: 1px solid #ccc;
}
tfoot {
font-weight: bold;
}
@media screen and (max-width: 1000px) {
.contain {
flex-wrap: wrap;
}
.list-box {
width: 100%;
}
.echarts-box {
margin-top: 30px;
}
}
</style>
</head>
<body>
<div id="app">
<div class="contain">
<!-- 左侧列表 -->
<div class="list-box">
<!-- 添加资产 -->
<form class="my-form">
<input v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
<input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
<button @click="add" type="button" class="btn btn-primary">添加账单</button>
</form>
<table class="table table-hover">
<thead>
<tr>
<th>编号</th>
<th>消费名称</th>
<th>消费价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
<td><a href="javascript:;">删除</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
</tr>
</tfoot>
</table>
</div>
<!-- 右侧图表 -->
<div class="echarts-box" id="main"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 接口文档地址:
* https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
*
* 功能需求:
* 1. 基本渲染
* (1) 立刻发送请求获取数据 created
* (2) 拿到数据,存到data的响应式数据中
* (3) 结合数据,进行渲染 v-for
* (4) 消费统计 => 计算属性
* 2. 添加功能
* (1) 收集表单数据 v-model
* (2) 给添加按钮注册点击事件,发送添加请求
* (3) 需要重新渲染
* 3. 删除功能
* 4. 饼图渲染
*/
const app = new Vue({
el: '#app',
data: {
list: [],
name: '',
price: ''
},
computed: {
totalPrice () {
return this.list.reduce((sum, item) => sum + item.price, 0)
}
},
created () {
// const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
// params: {
// creator: '小黑'
// }
// })
// this.list = res.data.data
this.getList()
},
methods: {
async getList () {
const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
params: {
creator: '小黑'
}
})
this.list = res.data.data
},
async add () {
if (!this.name) {
alert('请输入消费名称')
return
}
if (typeof this.price !== 'number') {
alert('请输入正确的消费价格')
return
}
// 发送添加请求
const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
creator: 'yueyue',
name: this.name,
price: this.price
})
// 重新渲染一次
this.getList()
this.name = ''
this.price = ''
}
}
})
</script>
</body>
</html>
10.4.5 静态结构
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>04.yueyue记账清单-静态结构</title>
<!-- CSS only -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
/>
<style>
.red {
color: red!important;
}
.search {
width: 300px;
margin: 20px 0;
}
.my-form {
display: flex;
margin: 20px 0;
}
.my-form input {
flex: 1;
margin-right: 20px;
}
.table > :not(:first-child) {
border-top: none;
}
.contain {
display: flex;
padding: 10px;
}
.list-box {
flex: 1;
padding: 0 30px;
}
.list-box a {
text-decoration: none;
}
.echarts-box {
width: 600px;
height: 400px;
padding: 30px;
margin: 0 auto;
border: 1px solid #ccc;
}
tfoot {
font-weight: bold;
}
@media screen and (max-width: 1000px) {
.contain {
flex-wrap: wrap;
}
.list-box {
width: 100%;
}
.echarts-box {
margin-top: 30px;
}
}
</style>
</head>
<body>
<div id="app">
<div class="contain">
<!-- 左侧列表 -->
<div class="list-box">
<!-- 添加资产 -->
<form class="my-form">
<input type="text" class="form-control" placeholder="消费名称" />
<input type="text" class="form-control" placeholder="消费价格" />
<button type="button" class="btn btn-primary">添加账单</button>
</form>
<table class="table table-hover">
<thead>
<tr>
<th>编号</th>
<th>消费名称</th>
<th>消费价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>帽子</td>
<td>99.00</td>
<td><a href="javascript:;">删除</a></td>
</tr>
<tr>
<td>2</td>
<td>大衣</td>
<td class="red">199.00</td>
<td><a href="javascript:;">删除</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4">消费总计: 298.00</td>
</tr>
</tfoot>
</table>
</div>
<!-- 右侧图表 -->
<div class="echarts-box" id="main"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 接口文档地址:
* https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
*
* 功能需求:
* 1. 基本渲染
* 2. 添加功能
* 3. 删除功能
* 4. 饼图渲染
*/
const app = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
</html>
10.4.6 饼图渲染
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>04.yueyue记账清单-饼图渲染</title>
<!-- CSS only -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
/>
<style>
.red {
color: red!important;
}
.search {
width: 300px;
margin: 20px 0;
}
.my-form {
display: flex;
margin: 20px 0;
}
.my-form input {
flex: 1;
margin-right: 20px;
}
.table > :not(:first-child) {
border-top: none;
}
.contain {
display: flex;
padding: 10px;
}
.list-box {
flex: 1;
padding: 0 30px;
}
.list-box a {
text-decoration: none;
}
.echarts-box {
width: 600px;
height: 400px;
padding: 30px;
margin: 0 auto;
border: 1px solid #ccc;
}
tfoot {
font-weight: bold;
}
@media screen and (max-width: 1000px) {
.contain {
flex-wrap: wrap;
}
.list-box {
width: 100%;
}
.echarts-box {
margin-top: 30px;
}
}
</style>
</head>
<body>
<div id="app">
<div class="contain">
<!-- 左侧列表 -->
<div class="list-box">
<!-- 添加资产 -->
<form class="my-form">
<input v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
<input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
<button @click="add" type="button" class="btn btn-primary">添加账单</button>
</form>
<table class="table table-hover">
<thead>
<tr>
<th>编号</th>
<th>消费名称</th>
<th>消费价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
<td><a @click="del(item.id)" href="javascript:;">删除</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
</tr>
</tfoot>
</table>
</div>
<!-- 右侧图表 -->
<div class="echarts-box" id="main"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 接口文档地址:
* https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
*
* 功能需求:
* 1. 基本渲染
* (1) 立刻发送请求获取数据 created
* (2) 拿到数据,存到data的响应式数据中
* (3) 结合数据,进行渲染 v-for
* (4) 消费统计 => 计算属性
* 2. 添加功能
* (1) 收集表单数据 v-model
* (2) 给添加按钮注册点击事件,发送添加请求
* (3) 需要重新渲染
* 3. 删除功能
* (1) 注册点击事件,传参传 id
* (2) 根据 id 发送删除请求
* (3) 需要重新渲染
* 4. 饼图渲染
* (1) 初始化一个饼图 echarts.init(dom) mounted钩子实现
* (2) 根据数据实时更新饼图 echarts.setOption({ ... })
*/
const app = new Vue({
el: '#app',
data: {
list: [],
name: '',
price: ''
},
computed: {
totalPrice () {
return this.list.reduce((sum, item) => sum + item.price, 0)
}
},
created () {
// const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
// params: {
// creator: '小黑'
// }
// })
// this.list = res.data.data
this.getList()
},
mounted () {
this.myChart = echarts.init(document.querySelector('#main'))
this.myChart.setOption({
// 大标题
title: {
text: '消费账单列表',
left: 'center'
},
// 提示框
tooltip: {
trigger: 'item'
},
// 图例
legend: {
orient: 'vertical',
left: 'left'
},
// 数据项
series: [
{
name: '消费账单',
type: 'pie',
radius: '50%', // 饼的半径
data: [
// { value: 1048, name: '球鞋' },
// { value: 735, name: '防晒霜' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
})
},
methods: {
async getList () {
const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
params: {
creator: '小黑'
}
})
this.list = res.data.data
// 更新图表
this.myChart.setOption({
// 数据项
series: [
{
// data: [
// { value: 1048, name: '球鞋' },
// { value: 735, name: '防晒霜' }
// ]
data: this.list.map(item => ({ value: item.price, name: item.name}))
}
]
})
},
async add () {
if (!this.name) {
alert('请输入消费名称')
return
}
if (typeof this.price !== 'number') {
alert('请输入正确的消费价格')
return
}
// 发送添加请求
const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
creator: '小黑',
name: this.name,
price: this.price
})
// 重新渲染一次
this.getList()
this.name = ''
this.price = ''
},
async del (id) {
// 根据 id 发送删除请求
const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)
// 重新渲染
this.getList()
}
}
})
</script>
</body>
</html>