文章目录
- 前言
- 一、Vue框架(简化DOM操作的一个前端框架):基础入门
- 1 Vue基本概念
- 2 快速入门:创建Vue实例,初始化渲染
- (1)创建一个入门Vue实例
- (2)插值表达式:{{表达式}}
- (3)Vue实例中的传入的对象中键的解释(重要)
- ---- 数据模型data
- ---- 定义方法:methods
- ---- mounted()钩子方法 :挂载完成
- (4)Vue响应式特性
- 二、Vue常用指令
- 1 v-html指令:设置元素的innerHTML
- 2 v-on指令:为html标签绑定事件
- (1)v-on:事件名="内联语句"
- (2)v-on:事件名= "methods中的函数名"
- ---- 不传参
- ---- 传参
- 3 v-model指令:在表单元素上创建双向数据绑定
- (1)v-model 应用于输入框 input:text
- (2)v-model 应用于其他各种表单组件
- 4 条件指令:v-if , v-else-if, v-else:根据if条件渲染某元素(标签),判定为true展示标签,否则不展示
- 5 v-show指令(和v-if效果完全一样):根据条件渲染某元素(标签),判定为true展示标签,否则不展示
- 6 v-for指令: 基于数据循环,多次渲染整个元素
- (1)基本用法
- (2)扩展:v-for 遍历渲染列表中 key属性的使用
- 7 v-bind指令:动态的设置html的标签属性→ src url title...,如设置 href,css样式等(超级灵活这个指令)
- (1)基本使用:动态的设置html的标签属性→ src url title...
- (2)进阶用法:v-bind 对于样式控制的增强
- ---- v-bind 对于样式控制的增强-操作class
- ---- v-bind 对于样式控制的增强-操作style
- 9 指令修饰符:简化代码
- (1)按键修饰符(用于v-on键盘事件)
- (2)鼠标修饰符(用于v-on鼠标事件)
- (3)表单修饰符(用于 v-model)
- (4)事件修饰符(用于 v-on)
- ---- @事件名.stop:阻止冒泡
- ---- @事件名.prevent:阻止默认行为
- 8 案例练习
- (1)案例1:图片切换(波仔的学习之旅)
- (2)案例2:图书管理案例:小黑的书架
- (3)案例3(综合案例):小黑记事本
- ---- step1: 使用AI生成ui布局
- ---- step2: 在ai生成的ui的html架构上面进一步开发
- (4)案例4:通过Vue完成表格数据的渲染展示
- 三、计算属性和监视器
- 1 计算属性
- (1)基本概念
- (2)使用计算属性和methods的区别
- ---- 使用 计算属性 computed
- ---- 使用methods
- (3)计算属性的完整写法(了解即可)
- 2 watch 侦听器(监视器):监视数据模型中数据的变化(待定!!!!!下面的案例建议先将ajox技术学好再回来重新学习)
- (1)watch 侦听器(监视器):简写语法
- (2)watch 侦听器(监视器):完整语法(进阶)
- 3 案例
- 综合案例1:成绩表格
- 三 Vue生命周期和Vue中发送请求的位置
- (1)Vue对象的生命周期
- (2)mounted阶段:发送请求的时刻
- 四、Ajax技术(从服务端获取数据,发送各种请求)
- 0 接口文档管理:使用apipost等接口测试软件创建接口便于前端后端分离测试
- 1 基本概念
- 2 原生Ajax(几年前的早期用法)
- 3 Axios(对原生的Ajax进行了封装)
- 4 案例练习:Axios+Vue,基于Vue及Axios完成数据的动态加载展示(重要!!!!!)
前言
一、Vue框架(简化DOM操作的一个前端框架):基础入门
Vue的使用在前端想学习深入还是有很多要学习的,但我们重点不在前端。我们快速入门一下,能够简单使用就可以了,如果想深入了解这个十分重要的前端框架,就需要专门去找Vue这门课好好学习一下了。
1 Vue基本概念
-
Vue 是一套前端框架,免除原生JavaScript中的DOM操作,简化书写。
前面学DOM操作,会发现相当繁琐,方法属性一大堆,不方便。因此就衍生出了Vue这么一个框架可以帮助我们简化DOM操作的书写。- 优点:大大提升开发效率(70%个)
- 缺点︰需要理解记忆规则→官网
使用框架就要遵守框架中的一些人为规定的规则
-
基于MVVM(Model-View-ViewModel)思想,实现数据的双向绑定,将编程的关注点放在数据上
Vue的学习,其实关注一点:
数据模型 <=====>前端界面展示 ,这二者是相互影响的
(1)数据模型里面对应的数据值发生了变化,前端展示中所有应用这个数据值都会同时发生变化
(2)前端展示的所有数据值只要有一个地方修改了这个值,就会导致数据模型中对应的值发生变化,进而导致前端其他引用这个数据值的地方也发生变化
-
官网: https://v2.cn.vuejs.org/
-
框架:是一个半成品软件,是一套可重用的、通用的、软件基础代码模型。基于框架进行开发,更加快捷、更加高效。
问题来了,数据模型是什么?下面通过一个Vue的快速入门来了解上述这些概念。
2 快速入门:创建Vue实例,初始化渲染
(1)创建一个入门Vue实例
参考视频
- Vue的基本使用只有三步
-
(1)新建HTML页面,引入Vue.js文件(引包)
官网: https://v2.cn.vuejs.org/提供了两种模式的Vue.js文件
开发版本包含完整的警告和调试模式,更加适合我们开发者。如果产品上线了换成生产环境版本,对用户更加友好,出现报错屏蔽了报错信息。- <!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src=“https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js”></script> - <!-- 生产环境版本,优化了尺寸和速度 -->
<script src=“https://cdn.jsdelivr.net/npm/vue@2”></script>
- <!-- 开发环境版本,包含了有帮助的命令行警告 -->
-
(2)在JS代码区域,创建Vue核心对象,定义数据模型
- 知识点:JS中的自定义类,自定义对象、JSON知识;参考上一篇JS博客
- 代码解释:
- new Vue( JS对象 ) : 这里是new出来一个 Vue对象,里面传参传一个自定义的对象(这个对象就是数据模型)
- 数据模型:可以发现是一个JS对象(键值对)
- el : 指定挂载点(要控制渲染的是哪一个容器)
用于规定这个 Vue 能控制html中的哪些元素(标签等),里面的语法类似CSS选择器
例如这里 el: "#app"就是表示能控制id="app"的这个元素 - data : 数据模型,值是一个{ }(也就是一个JSON对象),里面的键值对可以放各种前端要展示的数据,甚至是css样式(可能还有更加高级的用法)
- el : 指定挂载点(要控制渲染的是哪一个容器)
-
(3)编写视图(准备容器,要渲染的标签,一般我们都是使用一个div标签容器)
{{ }} :插值表达式语法,可以直接获取数据模型中的数据用于展示
-
示例:
<!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="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <!-- 引入Vue.js -->
</head>
<body>
<!-- 定义要渲染的容器 -->
<div id="app">
{{ message }} <!-- 使用双大括号(插值表达式)绑定数据 -->
</div>
</body>
<script>
// 定义Vue对象
new Vue({
el: '#app', // 挂载点,绑定id为app的元素
data: {
message: 'Hello Vue.js!' // 数据
}
})
</script>
</html>
渲染结果:
(2)插值表达式:{{表达式}}
参考视频
插值表达式是一种Vue的模板语法
- 作用: 利用表达式进行插值(访问data数据模型中数据),渲染到页面中
表达式: 是可以被求值的代码,JS引擎会将其计算出一个结果 - 语法: {{表达式}}
- 注意点:
- 使用的数据必须存在( data )
- 支持的是表达式(理解成能够求出一个值的js代码即可),而非语句,比如:if for …
- 不能在标签属性中使用{{}}插值
标签属性中访问data数据模型中数据必须使用v-bind指令
示例:
<!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="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <!-- 引入Vue.js -->
</head>
<body>
<!-- 插值表达式: {{ }} <!-- 插值表达式,用于输出数据 -->
<!-- 里面的表达式可以是任意的JavaScript表达式,可以是变量,也可以是函数,也可以是运算符等等,只要最终能够返回一个值即可 -->
<div id="app">
{{ nickname }} <br> <!-- 输出 tony -->
{{nickname.toUpperCase()}} <br> <!-- 输出 TONY -->
{{nickname.length}} <br> <!-- 输出 4 -->
{{nickname.split('').reverse().join('')}} <br> <!-- 输出 ynot -->
{{age + 2}} <br> <!-- 输出 20 -->
{{age > 18 ? '成年人' : '未成年人'}} <br> <!-- 输出 未成年人 -->
<!-- 拿到对象的属性: -->
{{friend.name}} <br> <!-- 输出 jack -->
{{friend.age}} <br> <!-- 输出 18 -->
</div>
</body>
<script>
// 定义Vue对象
const app = new Vue({
el: '#app', // 挂载点,绑定id为app的元素
data: {
nickname: 'tony',
age : 16,
friend : {
name : 'jack',
age : 18
}
}
})
</script>
</html>
渲染结果:
(3)Vue实例中的传入的对象中键的解释(重要)
new Vue() 实例化时,除了 el 和 data 之外,Vue 还支持许多其他属性来配置组件的行为。这些属性可以帮助你定义组件的生命周期、计算属性、方法、监听器、子组件等。下面会列出比较常见的几种:
- 常见属性:
- el string :指定挂载点(CSS 选择器),将 Vue 实例挂载到 DOM 元素上。
- data Object: (数据模型)定义组件的初始数据,必须是函数形式(返回一个对象)。
- methods Object :定义组件的方法,可以在模板或逻辑中调用。
- 钩子方法:
- mounted() :挂载完成。
- mounted() :挂载完成。
---- 数据模型data
- data中的数据, 最终会被添加到对应的Vue实例上
- 访问数据: “实例.属性名”
- 修改数据: “实例.属性名”=“值”
- 在Vue内部修改data里面的数据,可以直接使用this.属性名访问到
例如在methods中我们如果有方法需要修改data里面的数据,直接this.属性名就可以进行修改或者其他操作了
先记住这几条规则后面会频繁用到
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <!-- 引入Vue.js -->
<body>
<div id="app">
{{nickname}} <!-- 渲染结果 : 张三 -->
</div>
</body>
<script>
// 定义Vue对象
const app = new Vue({
el: '#app', // 挂载点,绑定id为app的元素
data: {
nickname: 'tony'
}
})
app.nickname = '张三' // 修改数据
</script>
---- 定义方法:methods
具体使用见下面的【v-on:事件名= “methods中的函数名”】
---- mounted()钩子方法 :挂载完成
具体使用见下面【案例练习:Axios+Vue,基于Vue及Axios完成数据的动态加载展示】
(4)Vue响应式特性
Vue核心特性: 响应式
- 我们已经掌握了基础的模板渲染,其实除了基本的模板渲染,Vue背后还做了大量工作。
- 比如: 数据的响应式处理 → 响应式: 数据模型里面数据发生变化,对应的渲染视图自动更新
- 聚焦于数据 → 数据驱动视图
- 使用Vue开发,关注业务的核心逻辑,根据业务修改数据即可
示例:
下面这个还是上面的插值表达式的示例代码,我们在浏览器的控制台上面修改数据模型data里面的数据:
更加具体清晰反映这种Vue核心特性: 响应式的例子见下面的:【v-model指令:在表单元素上创建双向数据绑定】
二、Vue常用指令
学习Vue指令建议先看v-on 这个给标签绑定事件的指令再学习其他指令会更加轻松。
Vue会根据不同的【指令】,针对标签实现不同的【功能】。
通过Vue的各种指定就可以实现各种数据模型和前端展示相互依赖的效果,下面我们就要来学习一些常用指令
- 指令: HTML标签上带有v-前缀 的特殊标签属性,不同指令具有不同含义。例如:v-if,v-for…
- 常用指令:
1 v-html指令:设置元素的innerHTML
- 作用: 设置元素的innerHTML
很显然和传统的DOM操作中:Element对象的属性 ---- innerHTML属性 获取或设置元素的 HTML 内容的作用是一样的 - 语法: v-html = “表达式”
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <!-- 引入Vue.js -->
<body>
<div id="app">
<div v-html="msg"></div> <!-- 使用v-html指令插入 html -->
</div>
</body>
<script>
// 定义Vue对象
const app = new Vue({
el: '#app', // 挂载点,绑定id为app的元素
data: {
msg:
'<a href="https://www.bilibili.com/">哔哩哔哩</a>'
}
})
</script>
渲染结果:
2 v-on指令:为html标签绑定事件
-
作用: 注册事件 = 添加监听 + 提供处理逻辑
-
语法:
- v-on:事件名=“内联语句”
内联语句:理解成一段可执行的代码 - v-on:事件名= “methods中的函数名”
- v-on:事件名=“内联语句”
-
简写:v-on:事件名 <==> @:事件名
例如:v-on:click 监听按钮的点击事件,触发 handleClick 方法。
可以简写为 @click=“handleClick”。 -
v-on 可以监听任何 DOM 事件,例如:
- @input:输入事件
- @keyup:键盘按键松开事件
- @submit:表单提交事件
- @mouseover:鼠标悬停事件
- 等等事件都可以上网去搜索(一般会结合指令修饰符一起使用)
(1)v-on:事件名=“内联语句”
直接将要执行的代码写成字符串就可以执行了
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <!-- 引入Vue.js -->
<body>
<div id="app">
<button @click = 'count = count - 1'>-</button>
<span>{{ count }}</span>
<button @click = 'count = count + 1'>+</button>
</div>
</body>
<script>
// 定义Vue对象
const app = new Vue({
el: '#app', // 挂载点,绑定id为app的元素
data: {
count : 100
}
})
</script>
渲染结果:
(2)v-on:事件名= “methods中的函数名”
Vue示例中的
methods中可以定义各种事件触发的方法
---- 不传参
示例1:
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
<input type="button" value="按钮" @click = "handle()">
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
message : "我被点击了!!!!"
},
methods: {
// methods中的所有函数,this都指向当前实例
handle(){
alert(this.message); // 这个this语法下面会解释
}
/*
完整写法,上面是简写
handle : function(){
alert(this.message); // 这个this语法下面会解释
}
*/
}
});
</script>
- this 在 Vue 实例的方法中指向当前 Vue 实例。
通过 this 可以访问 data中的键。 - 典型错误:在 Vue.js 中,this.data.message 是错误的写法。
- 在 Vue 中,data 是一个函数,它返回一个对象。
- Vue 会将 data 返回的对象中的属性直接挂载到 Vue 实例上,而不是保留在 data 对象中。
- Vue 会将 message 直接挂载到实例上,而不是保留在 data 对象中。
- 因此,你可以通过 this.message 访问 message,而不是 this.data.message。
- 总结一下,我们传人的对象到Vue中,Vue会将data下一级的键直接挂载在上面,而不是直接挂载data。其余的也可以按照这个逻辑类比推导。
示例2:
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
<button @click = "fn">切换显示隐藏</button>
<h1 v-show="isShow">黑马程序员</h1>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
isShow: true
},
methods: {
fn(){
this.isShow = !this.isShow; // 点击按钮时,切换显示隐藏
}
}
});
</script>
渲染结果:
---- 传参
- 基本语法:
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
<div class="box">
<h3>小黑自动售货机</h3>
<button @click = "buy(5)">可乐5元</button>
<button @click = "buy(10)">雪碧10元</button>
</div>
<p>银行卡余额:{{money}}元</p>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
money: 100
},
methods: {
buy(price){
this.money -= price;
}
}
});
</script>
渲染结果:
3 v-model指令:在表单元素上创建双向数据绑定
- 作用: 给 表单元素 使用,双向数据绑定 → 可以快速 获取 或 设置 表单元素内容
① 数据变化 → 视图自动更新
② 视图变化 → 数据自动更新 - 语法:v-model=‘变量’
(1)v-model 应用于输入框 input:text
这个指令就简单多了,用处也比较单一只能用在表单元素上。
v-model 是 Vue.js 中用于实现表单输入元素和 Vue 实例数据之间双向绑定的指令。它主要用于 <input>、<textarea>、<select> 等表单元素,能够自动同步用户输入的值和 Vue 实例中的数据。
示例1:
- v-model 将输入框的值与 Vue 实例中的 message 数据绑定。
- 当用户输入内容时,message 会自动更新;反之,如果 message 的值发生变化,输入框的内容也会同步更新。
<body>
<div id = "app">
<input type="text" v-model="message"> <!-- v-model指令用于在表单控件元素上创建双向数据绑定 -->
<p>{{ message }}</p> <!-- {{}}用于输出数据 -->
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <!-- 引入Vue.js -->
<script>
// 定义Vue对象
new Vue({
el: '#app', // 挂载点,绑定id为app的元素
data: {
message: 'Hello Vue.js!' // 数据
}
})
</script>
下面就演示什么叫数据模型与前端展示视图相互依赖
原始界面:
将input输入框里面文字改成:曹潇潇我喜欢你!会发现下面的段落文字也会同步改变,这就是相互依赖,十分强大这个功能,我们压根不需要去在操作DOM就能实现这样的同步效果。
示例2:
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<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>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
username: '',
password: ''
},
methods: {
login() {
// 登录的逻辑,直接就可以通过 this.username 和 this.password 快速获取到输入框的值
console.log('账户:' + this.username)
console.log('密码:' + this.password)
alert("用户"+this.username+"登录成功")
},
reset() {
// 重置的逻辑
this.username = ''
this.password = ''
}
}
})
</script>
渲染结果:
(2)v-model 应用于其他各种表单组件
参考视频
常见的表单元素都可以用 v-model 绑定关联 → 快速 获取 或 设置 表单元素的值
它会根据 控件类型 自动选取 正确的方法 来更新元素
-
输入框 input:text
-
文本域 textarea
-
复选框 input:checkbox
-
单选框 input:radio
-
下拉菜单 select
-
案例:通过这个案例将上述所有组件都演示一下使用
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小黑记事本</title>
<style>
textarea {
display: block;
width: 240px;
height: 100px;
margin: 10px 0;
}
</style>
</head>
<body>
<div id="app">
<h3>小黑学习网</h3>
<!-- 输入框 -->
姓名:
<input type="text" v-model="username"> <!-- 输入框绑定是是 字符串类型 -->
<br><br>
<!-- 复选框 -->
是否单身:
<input type="checkbox" v-model="isSingle"> <!-- 复选框绑定是是 布尔类型 -->
<br><br>
<!--
前置理解:
1. name: 给单选框加上 name 属性 可以分组 → 同一组互相会互斥
2. value: 给单选框加上 value 属性,用于提交给后台的数据
结合 Vue 使用 → v-model
-->
<!-- 单选框 -->
性别:
<input type="radio" name="gender" value="1" v-model="gender">男 <!-- 单选框绑定是是 字符串类型和你设置的value值一样 -->
<input type="radio" name="gender" value="2" v-model="gender">女
<br><br>
<!--
前置理解:
1. option 需要设置 value 值,提交给后台 ,假设 101 代表北京,102 代表上海...
2. select 的 value 值,关联了选中的 option 的 value 值
结合 Vue 使用 → v-model
-->
<!-- 下拉框 -->
所在城市:
<select v-model="city_id">
<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>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
isSingle: true,
gender : '1',
city_id: '103',
desc: ''
}
})
</script>
</html>
4 条件指令:v-if , v-else-if, v-else:根据if条件渲染某元素(标签),判定为true展示标签,否则不展示
-
v-if : if
- 作用: 控制元素显示隐藏(条件渲染)
- 语法: v-if = “表达式”
- 表达式值true显示, false隐藏
-
v-else-if:else if
- 语法:v-else-if = “表达式”
- 注意: 需要紧挨着v-if 一起使用
-
v-else: else
- 语法:v-else
- 注意: 需要紧挨着v-if 一起使用
-
等价于:if(条件){}else if(条件){}else{}
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
年龄<input type="text" v-model="age">经判定,为:
<span v-if = "age <= 35">年轻人(35及以下)</span> <!-- if条件成立展示这条标签,不成立就不展示 -->
<span v-else-if = "age>35 && age<60">中年人(35-60)</span> <!-- else-if -->
<span v-else>老年人(60以上)</span> <!-- else -->
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
age : 20
},
methods: {
}
});
</script>
5 v-show指令(和v-if效果完全一样):根据条件渲染某元素(标签),判定为true展示标签,否则不展示
- 作用: 控制元素显示隐藏
- 语法: v-show = "表达式”
表达式值true显示, false隐藏 - v-show、v-if控制元素显示隐藏底层原理
- v-show底层原理: 切换css 的display : none 来控制显示隐藏
适用于:频繁切换显示隐藏的场景 - v-if底层原理: 根据判断条件控制DOM元素的创建和移除
适用于:要么显示,要么隐藏,不频繁切换的场景 - 具体的二者的使用场景有什么不同
参考视频
- v-show底层原理: 切换css 的display : none 来控制显示隐藏
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
年龄<input type="text" v-model="age">经判定,为:
<span v-show = "age <= 35">年轻人(35及以下)</span>
<span v-show = "age>35 && age<60">中年人(35-60)</span>
<span v-show = "age >= 60">老年人(60以上)</span>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
age : 20
},
methods: {
}
});
</script>
- 和v-if区别
-
v-if: 实际上浏览器界面中的html如果v-if条件不成立,连标签都不会在html中
-
v-show: 实际上再浏览器中是通过设置 display为none来不展示这条标签的,但是标签还是在html中
-
6 v-for指令: 基于数据循环,多次渲染整个元素
(1)基本用法
- 作用: 基于数据循环,多次渲染整个元素 遍历的可以是数组、对象、数字等
遍历的数组,对象,数字都是来源与数据模型data - 遍历数组语法:
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
<!-- 多次渲染div -->
<div v-for="(item, index) in addrs">{{index+1}}:{{item}}</div>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
addrs : ['北京', '上海', '广州', '深圳']
},
methods: {
}
});
</script>
(2)扩展:v-for 遍历渲染列表中 key属性的使用
参考视频1
参考视频2
-
语法: key属性=“唯一标识”
这个唯一标识我们无论是数据库建表还是实体都会有这么一个唯一标识 -
作用: 给列表项添加的唯一标识。便于Vue进行列表项的正确排序复用
key 的作用是给每个节点一个唯一标识(类似数据库优化中给字段加上索引),帮助 Vue 快速判断:- 哪些节点是新增的?
- 哪些节点是被删除的?
- 哪些节点只是位置发生了变化?
-
举一个例子:没有 key 的问题
假设有一个列表 [A, B, C] 变为 [B, C, A]:
没有 key:Vue 会认为“第一个节点从 A 变成了 B”,导致所有子节点被重新渲染(性能差)。
有 key:Vue 发现节点只是顺序变化,直接移动 DOM 元素位置即可(性能高)。 -
通常情况下,不加上key属性功能也能正确实现,只是效率低。部分特殊情况下进行了增删改查操作可以导致顺序混乱
所以我们使用v-for渲染列表,开发规范是要求加上的 -
最佳实践:必须使用唯一且稳定的标识符(如 id),不要用数组索引(index)!
- 错误用法:v-for=“(item, index) in list” :key=“index”
当列表顺序变化时,index 会变化,导致 key 不稳定,失去优化意义。 - 正确用法:v-for=“item in list” :key=“item.id”
- 错误用法:v-for=“(item, index) in list” :key=“index”
-
注意事项
- key 的值只能是 字符串 或 数字类型
- key 的值必须具有 唯一性
- 推荐使用 id 作为 key(唯一),不推荐使用 index作为 key(会变化,不对应)
具体demo演示可以参考【案例2:图书管理案例:小黑的书架】
7 v-bind指令:动态的设置html的标签属性→ src url title…,如设置 href,css样式等(超级灵活这个指令)
之前我们学习插值表达式中讲过标签属性里面是不能使用插值表达式的,这样标签属性就不能访问数据模型里面的数据了!
只能通过指令(v-bind,v-show等等这些指令)可以解决这个问题
通过 v-bind,你可以将 Vue 实例中的数据与 DOM 元素的属性关联起来,实现动态更新。
-
作用: 动态的设置html的标签属性→ src url title…
-
基本语法:
v-bind 的基本语法是 v-bind:属性名=“表达式”
也可以简写为 :属性名=“表达式”
(1)基本使用:动态的设置html的标签属性→ src url title…
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
<!-- <a href="{{url_1}}"></a> 这里是错误的写法,插值表达式不能作用于属性,我们必须使用v-bind指令 -->
<a :href="url_1">百度</a> <!-- 动态绑定数据模型中的url_1到a标签的href属性上 -->
<br>
<a :href="url_2">哔哩哔哩</a> <!-- 动态绑定数据模型中的url_2到a标签的href属性上 -->
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
url_1: "https://www.baidu.com",
url_2: "https://www.bilibili.com"
}
})
</script>
渲染结果:
(2)进阶用法:v-bind 对于样式控制的增强
对应样式的控制,无非是class 类名 和 style 行内样式 这两种方法
为了方便开发者进行样式控制,Vue 扩展了 v-bind 的语法,可以针对 class 类名 和 style 行内样式 进行控制 。
---- v-bind 对于样式控制的增强-操作class
参考视频
- 语法:class="对象/数组”
- ① 对象 → 键就是类名,值是布尔值。如果值为 true,有这个类,否则没有这个类
- ② 数组 →数组中所有的类,都会添加到盒子上,本质就是一个 class 列表
- ① 对象 → 键就是类名,值是布尔值。如果值为 true,有这个类,否则没有这个类
案例1:
(1)动态绑定两个 class:active 和 large
active 控制背景颜色和文字颜色
large 控制按钮的大小
(2)点击按钮时,切换 active 和 large 的状态
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 动态 Class 示例</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<style>
/* 定义 active 类的样式 */
.active {
background-color: green; /* 背景颜色变为绿色 */
color: white; /* 文字颜色变为白色 */
}
/* 定义 large 类的样式 */
.large {
font-size: 20px; /* 文字大小变大 */
padding: 15px 30px; /* 按钮内边距变大 */
}
</style>
</head>
<body>
<div id="app">
<!-- 动态绑定多个 class -->
<button :class="{ active: isActive, large: isLarge }" @click="toggleClasses">点击我</button>
</div>
<script>
new Vue({
el: '#app',
data: {
isActive: false, // 初始状态为 false
isLarge: false // 初始状态为 false
},
methods: {
toggleClasses() {
this.isActive = !this.isActive; // 切换 active 状态
this.isLarge = !this.isLarge; // 切换 large 状态
}
}
});
</script>
</body>
</html>
运行效果
(1)初始状态:
按钮没有 active 和 large 类,样式为默认样式
(2)点击按钮后:
按钮的背景颜色变成绿色,文字颜色变成白色(active 类生效)
按钮的文字大小变大,内边距变大(large 类生效)
(3)再次点击按钮:
按钮恢复默认样式
- 案例2:京东秒杀导航tab高亮
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小黑记事本</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>
<div id="app">
<ul>
<li v-for="(item,index) in list" :key="item.id" @click="activate_index = index"> <!-- @click="activate_index = index" // 点击事件,点击时将当前索引赋值给activate_index -->
<a :class="{active:index === activate_index}" href="#"> <!-- :class="{active:index === activate_index} // 根据当前索引是否等于activate_index来判断是否添加active类 -->
{{item.name}}
</a>
</li>
</ul>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
activate_index: 0, // 记录当前激活的索引,要高亮的tab
list: [
{ id: 1, name: '京东秒杀' },
{ id: 2, name: '每日特价' },
{ id: 3, name: '品类秒杀' }
]
}
})
</script>
</html>
渲染结果:
---- v-bind 对于样式控制的增强-操作style
参考视频
- 语法: style=“样式对象”
- 适用场景: 某个具体属性的动态设置
功能:点击按钮时,按钮的背景颜色和文字大小会动态变化。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 动态 Class 示例</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 动态绑定 style -->
<button :style="{ backgroundColor: buttonColor, fontSize: fontSize + 'px' }" @click="toggleStyle">点击我</button>
</div>
<script>
new Vue({
el: '#app',
data: {
buttonColor: 'blue', // 初始背景颜色
fontSize: 16 // 初始文字大小
},
methods: {
toggleStyle() {
this.buttonColor = this.buttonColor === 'blue' ? 'green' : 'blue'; // 切换背景颜色
this.fontSize += 2; // 每次点击增加文字大小
}
}
});
</script>
</body>
</html>
9 指令修饰符:简化代码
通过"."指明一些指令 后缀,不同 后缀 封装了不同的处理操作 → 进一步简化代码
(1)按键修饰符(用于v-on键盘事件)
- 按键修饰符(用于键盘事件)
常用按键别名:.enter .tab .delete .esc .space .up .down .left .right
@keyup表示监听键盘上任何键,按下任何键都会触发这个事件- @keyup.enter:按下回车键时触发
- @keyup.ctrl.enter:组合修饰符:Ctrl + Enter
(2)鼠标修饰符(用于v-on鼠标事件)
- 鼠标修饰符(用于鼠标事件)
- @click.left:左键点击
- @click.right:右键点击
- @click.middle:中键点击
(3)表单修饰符(用于 v-model)
- 表单修饰符(用于 v-model)
- v-model.trim:自动去除表单中输入的文本中的首尾空格
- v-model.number:自动将表单中输入的文本转为数值类型
注意,如果用户输入是 “abc”这种无法转number的,那底层不会强转,在数据模型中存成字符串;当然如果是“12”这种,数据模型中就会转成number保存了。为了用户的体验这样也非常合理
(4)事件修饰符(用于 v-on)
参考视频
- 事件修饰符
- @事件名.stop:阻止冒泡
@click.stop:点击事件停止冒泡 - @事件名.prevent:阻止默认行为
- @事件名.stop:阻止冒泡
---- @事件名.stop:阻止冒泡
在html中给元素绑定事件会触发一种事件冒泡的现象:
事件冒泡是指当一个元素上的事件被触发后,会按照从内到外的顺序依次触发父元素上的同类型事件(例如都是点击事件)。
- 事件冒泡(Event Bubbling) 是浏览器的事件传播机制之一,表现为:
触发顺序:当子元素触发事件时,事件会 从触发元素开始,逐级 向上层父元素传播,依次触发各级父元素的 同类型事件。
传播路径:子元素 → 父元素 → 祖父元素 → … → document
例如,点击一个按钮,如果按钮在表单里,那么按钮的点击事件会先触发,然后表单的点击事件也会触发。这可能不是用户想要的行为,这时候就需要阻止事件冒泡。
当点击一个嵌套在父元素中的按钮时,浏览器会按照 事件冒泡 机制触发事件:
<div @click="handleDivClick">
<button @click="handleButtonClick">点击我</button>
</div>
默认行为:点击按钮时,会先触发 handleButtonClick,接着触发 handleDivClick
问题:如果希望点击按钮时 不触发父元素的点击事件,就需要阻止事件冒泡
<div @click="handleDivClick">
<button @click.stop="handleButtonClick">点击我</button>
</div>
---- @事件名.prevent:阻止默认行为
浏览器为某些事件预设了 自动触发的行为,例如:
(1)表单提交(submit 事件):页面会刷新或跳转
当用户按下回车键时,浏览器会自动触发表单的提交行为(如果表单中有输入框)。如果没有 <form> 标签,这种默认行为将无法实现。
(2)链接点击(click 事件):跳转到 href 指定的 URL
(3)右键菜单(contextmenu 事件):弹出系统默认菜单
(4)输入框回车(keydown.enter 事件):可能触发表单提交
这些行为是浏览器自带的,但有时会干扰我们的自定义逻辑。@事件名.prevent 的作用就是禁用这些默认行为。
以右键菜单这个默认行为为例:任何浏览器页面鼠标右键都会弹出浏览器菜单界面栏:
如果我们在开发过程中,想要讲鼠标右键绑定自定义的事件就需要阻止这个弹出默认菜单栏的事件了
示例:阻止右键菜单
<div @contextmenu.prevent="showCustomMenu">
右键点击我(不会弹出系统菜单)
</div>
<script>
new Vue({
methods: {
showCustomMenu() {
// 显示自定义右键菜单
console.log('显示自定义菜单');
}
}
});
</script>
默认行为:弹出浏览器默认右键菜单
使用 .prevent:不弹出默认菜单,执行自定义逻辑showCustomMenu,你可以在这个里面定义自己想执行的逻辑
示例:阻止默认表单提交,采用自定义逻辑提交表单(表单提交常用)
<body>
<div id="app">
<form @submit.prevent="handleSubmit">
<div>
<input v-model="email" type="email" required placeholder="请输入邮箱">
<button type="submit">提交</button>
</div>
</form>
</div>
<!-- 引入 Vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
// 创建 Vue 实例
new Vue({
el: '#app', // 指定挂载点
data: {
email: '' // 用于绑定输入框的值
},
methods: {
handleSubmit() {
if (this.email) {
console.log('提交的邮箱地址是:', this.email);
// 在这里可以执行进一步的逻辑,例如发送数据到服务器
} else {
console.log('邮箱地址不能为空');
}
}
}
});
</script>
</body>
在form的表单提交中如果不指定@submit.prevent,那么表单就会安装form上面指定的url(form中的属性可以指定)等直接发送请求提交;如果指定了就会执行@submit.prevent="handleSubmit"里面自定义的handleSubmit方法逻辑,我们可以在这个里面发送ajox请求,这样我们不需要再form属性里面指定发送的方式和url,更加灵活
8 案例练习
(1)案例1:图片切换(波仔的学习之旅)
参考视频
- 需求:有6张照片,通过上一页、下一页按钮进行切换
- 思路分析:
- 数组存储图片路径 [ 图片1,图片2,图片3,… ]
- 准备下标 index,数组[下标] → v-bind 设置 src 展示图片 → 修改下标切换图片
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
<button v-show = "index > 0" @click = "index--">上一页</button>
<div>
<img :src="list[index]" alt="">
</div>
<button v-show = "index < list.length-1 " @click = "index++">下一页</button>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
index : 0, // 动态绑定的索引,当前显示的图片在数组中的索引
list : [
"./img/1.png",
"./img/2.png",
"./img/3.png",
"./img/4.png",
"./img/5.png",
"./img/6.png",
] // 图片数组,都是相对路径放在img文件夹下了
}
})
</script>
渲染结果:
(2)案例2:图书管理案例:小黑的书架
参考视频1
参考视频2
- 需求:
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
<h3>小黑的书架</h3>
<ul>
<li v-for="(book,idx) in bookList" :key="book.id">
<!-- key属性的作用是为了提高Vue的性能,当数据发生变化时,Vue会根据key的值来判断哪些元素是新增的,哪些元素是删除的,从而减少页面的更新。
并且key属性要使用唯一的标识,不能使用index作为key,因为index在增删改查时会发生变化,不利于Vue的性能优化。 -->
<span>{{book.name}}</span>
<span>{{book.author}}</span>
<span>{{book.price}}</span>
<!-- 注册点击事件,通过id删除对应的书记 -->
<button @click = "removeBook(book.id)">删除</button>
</li>
</ul>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
bookList: [
{id:1, name: '《算法导论》' , author : 'Thomas H. Cormen' , price : 85.00},
{id:2, name: '《UNIX编程艺术》' , author : 'Eric S. Raymond' , price : 59.00},
{id:3, name: '《编程珠玑》' , author : 'Jon Bentley' , price : 39.00},
{id:4, name: '《代码大全》' , author : 'Steve McConnell' , price : 128.00}
]
},
methods: {
// 删除书籍,删除功能我们只用删除数据模型中的数据即可,页面会自动更新,这就是Vue的响应式特性
removeBook(id){
// filter:根据条件,保留满足条件的对应项,得到一个新数组。
this.bookList = this.bookList.filter(item => item.id !== id)
}
}
})
</script>
渲染结果:
(3)案例3(综合案例):小黑记事本
- 需求:
- 列表渲染
- 删除功能
- 添加功能
- 底部统计 和 清空
---- step1: 使用AI生成ui布局
由于我对前端的页面排版布局了解很有限,所以这里借用deepseek设计生成html和css的基本布局结构
- 上传上面的ui图片,提问:帮我根据上传的图片生成html ui样式?
ai生成结果:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小黑记事本</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', sans-serif;
background-color: #f5f5f5;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
padding: 24px;
}
h3 {
color: #333;
font-size: 24px;
margin-bottom: 20px;
text-align: center;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
input[type="text"] {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: 0.3s;
}
.add-btn {
background-color: #409eff;
color: white;
}
.add-btn:hover {
background-color: #66b1ff;
}
.task-list {
list-style: none;
margin-bottom: 20px;
}
.task-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
border-bottom: 1px solid #eee;
}
.task-item:last-child {
border-bottom: none;
}
.task-content {
display: flex;
align-items: center;
gap: 8px;
}
.task-index {
color: #666;
}
.delete-btn {
color: #f56c6c;
background: none;
padding: 4px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 16px;
border-top: 1px solid #eee;
}
.total {
color: #666;
}
.clear-btn {
background-color: #f56c6c;
color: white;
}
</style>
</head>
<body>
<div class="container">
<h3>小黑记事本</h3>
<div class="input-group">
<input type="text" placeholder="请输入任务">
<button class="add-btn">添加任务</button>
</div>
<ul class="task-list">
<li class="task-item">
<div class="task-content">
<span class="task-index">1.</span>
<span>跑步锻炼20分钟</span>
</div>
<button class="delete-btn">×</button>
</li>
<li class="task-item">
<div class="task-content">
<span class="task-index">2.</span>
<span>复习数组语法</span>
</div>
<button class="delete-btn">×</button>
</li>
</ul>
<div class="footer">
<span class="total">合计:2</span>
<button class="clear-btn">清空任务</button>
</div>
</div>
</body>
</html>
渲染结果:
可以看到,生成的UI框架可以满足我们的需求,下面我们在这份框架上面进一步开发即可。
---- step2: 在ai生成的ui的html架构上面进一步开发
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小黑记事本</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', sans-serif;
background-color: #f5f5f5;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
padding: 24px;
}
h3 {
color: #333;
font-size: 24px;
margin-bottom: 20px;
text-align: center;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
input[type="text"] {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: 0.3s;
}
.add-btn {
background-color: #409eff;
color: white;
}
.add-btn:hover {
background-color: #66b1ff;
}
.task-list {
list-style: none;
margin-bottom: 20px;
}
.task-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
border-bottom: 1px solid #eee;
}
.task-item:last-child {
border-bottom: none;
}
.task-content {
display: flex;
align-items: center;
gap: 8px;
}
.task-index {
color: #666;
}
.delete-btn {
color: #f56c6c;
background: none;
padding: 4px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 16px;
border-top: 1px solid #eee;
}
.total {
color: #666;
}
.clear-btn {
background-color: #f56c6c;
color: white;
}
</style>
</head>
<body>
<div class="container">
<h3>小黑记事本</h3>
<div class="input-group">
<input type="text" placeholder="请输入任务" v-model="to_add_task" @keyup.enter="add_task"> <!-- @keyup.enter:绑定键盘回车事件 -->
<button class="add-btn" @click="add_task">添加任务</button>
</div>
<ul class="task-list">
<li class="task-item" v-for="(task, index) in tasks" :key="task.id">
<div class="task-content">
<span class="task-index">{{index + 1 }}</span>
<span>{{task.name}}</span>
</div>
<button class="delete-btn" @click = "del(task.id)">×</button>
</li>
</ul>
<div class="footer">
<span class="total" v-show="tasks.length > 0">合计:{{tasks.length}}</span>
<button class="clear-btn" @click="clear" v-show="tasks.length > 0">清空任务</button> <!-- v-show 控制元素的显示和隐藏,当tasks数组长度大于0时,才显示清空任务按钮 -->
</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const container = new Vue({
el: '.container', // 挂载点 , id使用 # ,class使用 . 一般更加推荐id选择器挂载,因为id选择器唯一,class选择器可以有多个
data: {
to_add_task: '',
tasks: [
{ id : 1,name: '跑步锻炼20分钟' },
{ id : 2 ,name: '复习数组语法' },
{ id : 3 ,name: '游泳100里' }
]
},
methods: {
// 删除任务
del(id){
this.tasks = this.tasks.filter(task => task.id !== id)
},
// 添加任务
// 1.通过 v-model 绑定 输入框 → 实时获取表单元素的内容
// 2.点击按钮,进行新增,往数组最前面加 unshift
add_task(){
if (this.to_add_task.trim() === ''){
alert('请输入任务内容')
return
}
// 使用unshift方法,将新任务添加到数组的最前面
this.tasks.unshift({
id: +new Date(), // 为了保证id唯一性,前端临时可以使用时间戳
name: this.to_add_task
})
this.to_add_task = ''
},
// 清空任务
clear(){
this.tasks = []
}
}
})
</script>
</html>
渲染结果:
(4)案例4:通过Vue完成表格数据的渲染展示
- 需求:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 动态 Class 示例</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<table border="1" cellspacing="0" width="60%">
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>成绩</th>
<th>等级</th>
</tr>
<tr align="center" v-for="(user, index) in users">
<td>{{index+1}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
<td>
<span v-show="user.gender === 1">男</span>
<span v-show="user.gender === 2">女</span>
</td>
<td>{{user.score}}</td>
<td>
<span v-show="user.score>=85">优秀</span>
<span v-show="user.score>=60 && user.score<85">及格</span>
<span v-show="user.score<60" style="color: red;">不及格</span>
</td>
</tr>
</table>
</div>
<script>
new Vue({
el: '#app',
data: {
users:[
{
name: "Tom",
age: 18,
gender: 1,
score: 78
},
{
name:"Rose",
age: 20,
gender: 2,
score: 86
},
{
name: "Jerry",
age: 22,
gender: 1,
score: 90
},
{
name: "Jack",
age: 19,
gender: 1,
score: 52
}
]
},
methods: {
}
})
</script>
</body>
</html>
三、计算属性和监视器
1 计算属性
(1)基本概念
- 概念: 基于现有的数据,计算出来的新属性。依赖的数据变化,自动重新计算。
- 语法:
-
(1)声明在 computed 配置项中,一个计算属性对应一个函数
-
(2)使用起来和普通属性一样使用{{ 计算属性名 }},和data里面的插值表达式一样的语法
-
- 其实就是将methods方法里面的有返回值的函数移到了现在的computed里面
计算属性 → 可以将一段 求值的代码 进行封装
(2)使用计算属性和methods的区别
- computed 计算属性:
- 作用: 封装了一段对于数据的处理,求得一个结果。
- 语法:
① 写在 computed 配置项中
② 作为属性,直接使用 → this.计算属性或者 {{ 计算属性 }} - 缓存特性(提升性能)
计算属性会对计算出来的结果缓存,再次使用直接读取缓存
依赖项变化了,会自动重新计算 →并再次缓存
- methods 方法:
- 作用: 给实例提供一个方法,调用以处理业务逻辑。
- 语法:
① 写在 methods 配置项中
② 作为方法,需要调用 → this.方法名() 或者 {{ 方法名() }} 或者 @事件名="方法名
下面将用计算礼物数这个案例来展示二者的不同之处:
---- 使用 计算属性 computed
<!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>Document</title>
<style>
table {
border: 1px solid #000;
text-align: center;
width: 240px;
}
th,td {
border: 1px solid #000;
}
h3 {
position: relative;
}
</style>
</head>
<body>
<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>
渲染结果:
---- 使用methods
<!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>Document</title>
<style>
table {
border: 1px solid #000;
text-align: center;
width: 240px;
}
th,td {
border: 1px solid #000;
}
h3 {
position: relative;
}
</style>
</head>
<body>
<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>
<!-- 修改点1:改用方法调用 -->
<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 },
]
},
methods: { // 修改点2:将计算属性改为方法
totalCount() {
return this.list.reduce((sum, item) => sum + item.num, 0)
}
}
})
</script>
</body>
</html>
渲染结果:
(3)计算属性的完整写法(了解即可)
参考视频
- 计算属性默认的简写,只能读取访问,不能"修改"
- 如果要"修改"→ 需要写计算属性的完整写法
注:这里这种完整写法太麻烦了,特别是里面的set逻辑,不如交给java后端,这里我们不详细说明了,几乎不可能用到。
<!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>Document</title>
<style>
input {
width: 30px;
}
</style>
</head>
<body>
<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>
渲染结果:
2 watch 侦听器(监视器):监视数据模型中数据的变化(待定!!!!!下面的案例建议先将ajox技术学好再回来重新学习)
-
作用: 监视数据变化,执行一些 业务逻辑 或 异步操作
需要注意的是,watch 侦听器(监视器)监视的是数据模型里面的数据变化,和我们之前的事件监视中监视点击事件这些有点区别
一个最典型的应用就是:实时翻译功能 -
语法:
① 简单写法 → 简单类型数据,直接监视
② 完整写法 → 添加额外配置项
(1)watch 侦听器(监视器):简写语法
参考视频
- 语法:
- 需求:输入内容,实时翻译
<!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>Document</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">{{transResult}}</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
// 返回: {"message": "ok","data": "Qffo"} ,接口的翻译是随机生成的字符结果
// 请求方式:get
// 请求参数:
// (1)words:需要被翻译的文本(必传)
// (2)lang: 需要被翻译成的语言(可选)默认值-意大利
// -----------------------------------------------
const app = new Vue({
el: '#app',
data: {
// words: ''
obj: {
words: ''
},
// 翻译结果
transResult: ''
},
// 具体讲解:(1) watch语法 (2) 具体业务实现
watch: {
// 该方法会在数据变化时调用执行
// newValue新值, oldValue老值(一般不用)
// words (newValue) {
// console.log('变化了', newValue)
// }
'obj.words' (newValue) {
console.log('变化了', newValue)
// 使用axios发送请求
axios ({
method: "get",
url:"https://applet-base-api-t.itheima.net/api/translate",
params:{
words: newValue,
}
}).then(res =>{
this.transResult = res.data.data
})
}
}
})
</script>
</body>
</html>
结果:
(2)watch 侦听器(监视器):完整语法(进阶)
3 案例
综合案例1:成绩表格
参考视频
- 需求:
index.css
.score-case {
width: 1000px;
margin: 50px auto;
display: flex;
}
.score-case .table {
flex: 4;
}
.score-case .table table {
width: 100%;
border-spacing: 0;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
}
.score-case .table table th {
background: #f5f5f5;
}
.score-case .table table tr:hover td {
background: #f5f5f5;
}
.score-case .table table td,
.score-case .table table th {
border-bottom: 1px solid #ccc;
border-right: 1px solid #ccc;
text-align: center;
padding: 10px;
}
.score-case .table table td.red,
.score-case .table table th.red {
color: red;
}
.score-case .table .none {
height: 100px;
line-height: 100px;
color: #999;
}
.score-case .form {
flex: 1;
padding: 20px;
}
.score-case .form .form-item {
display: flex;
margin-bottom: 20px;
align-items: center;
}
.score-case .form .form-item .label {
width: 60px;
text-align: right;
font-size: 14px;
}
.score-case .form .form-item .input {
flex: 1;
}
.score-case .form .form-item input,
.score-case .form .form-item select {
appearance: none;
outline: none;
border: 1px solid #ccc;
width: 200px;
height: 40px;
box-sizing: border-box;
padding: 10px;
color: #666;
}
.score-case .form .form-item input::placeholder {
color: #666;
}
.score-case .form .form-item .cancel,
.score-case .form .form-item .submit {
appearance: none;
outline: none;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 10px;
margin-right: 10px;
font-size: 12px;
background: #ccc;
}
.score-case .form .form-item .submit {
border-color: #069;
background: #069;
color: #fff;
}
静态模版
<!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" />
<link rel="stylesheet" href="./index.css" />
<title>Document</title>
</head>
<body>
<div id="app" class="score-case">
<div class="table">
<table>
<thead>
<tr>
<th>编号</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>语文</td>
<td class="red">46</td>
<td><a href="#">删除</a></td>
</tr>
<tr>
<td>2</td>
<td>英语</td>
<td>80</td>
<td><a href="#">删除</a></td>
</tr>
<tr>
<td>3</td>
<td>数学</td>
<td>100</td>
<td><a href="#">删除</a></td>
</tr>
</tbody>
<tbody>
<tr>
<td colspan="5">
<span class="none">暂无数据</span>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">
<span>总分:246</span>
<span style="margin-left: 50px">平均分:79</span>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="form">
<div class="form-item">
<div class="label">科目:</div>
<div class="input">
<input
type="text"
placeholder="请输入科目"
/>
</div>
</div>
<div class="form-item">
<div class="label">分数:</div>
<div class="input">
<input
type="text"
placeholder="请输入分数"
/>
</div>
</div>
<div class="form-item">
<div class="label"></div>
<div class="input">
<button class="submit" >添加</button>
</div>
</div>
</div>
</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, subject: '语文', score: 20 },
{ id: 7, subject: '数学', score: 99 },
{ id: 12, subject: '英语', score: 70 },
],
subject: '',
score: ''
}
})
</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" />
<link rel="stylesheet" href="./index.css" />
<title>Document</title>
</head>
<body>
<div id="app" class="score-case">
<div class="table">
<table>
<thead>
<tr>
<th>编号</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody v-if="list.length > 0">
<tr v-for="(item,index) in list" :key="item.id">
<td>{{index + 1}}</td>
<td>{{item.subject}}</td>
<!-- 需求:不及格的标红,<60分,加上 red 类 -->
<!-- <td class="red">{{item.score}}</td> -->
<td :class="{red : item.score < 60}">{{item.score}}</td>
<td><a href="#" @click.prevent="del(item.id)">删除</a></td>
</tr>
</tbody>
<tbody v-else>
<tr>
<td colspan="5">
<span class="none">暂无数据</span>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">
<span>总分:{{total}}</span>
<span style="margin-left: 50px">平均分:{{avg}}</span>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="form">
<div class="form-item">
<div class="label">科目:</div>
<div class="input">
<input
type="text"
placeholder="请输入科目"
v-model.trim="subject"
/>
</div>
</div>
<div class="form-item">
<div class="label">分数:</div>
<div class="input">
<input
type="text"
placeholder="请输入分数"
v-model.number="score"
/>
</div>
</div>
<div class="form-item">
<div class="label"></div>
<div class="input">
<button class="submit" @click="add">添加</button>
</div>
</div>
</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
list: [
{ id: 1, subject: '语文', score: 20 },
{ id: 7, subject: '数学', score: 99 },
{ id: 12, subject: '英语', score: 70 },
],
subject: '',
score: ''
},
methods: {
del(id) {
this.list = this.list.filter(item => item.id !== id)
},
add(){
if(!this.subject || !this.score){
alert('请输入科目和分数')
return
}
this.list.unshift({
id: +new Date(),
subject: this.subject,
score: this.score
})
this.subject = ''
this.score = ''
}
},
computed: {
total() {
return this.list.reduce((prev, cur) => prev + cur.score, 0)
},
avg() {
if (this.list.length === 0) {
return 0
}
return (this.total / this.list.length).toFixed(2)
}
},
})
</script>
</html>
三 Vue生命周期和Vue中发送请求的位置
参考视频
(1)Vue对象的生命周期
- 生命周期: 指一个对象从创建到销毁的整个过程。
- 生命周期的八个阶段: 每触发一个生命周期事件,会自动执行一个生命周期方法(钩子)
对于上述生命周期的8个阶段,我们作为一个Java程序员只需要知道mounted(挂载完成,理解成Vue彻底创建成功时),这个时刻也是我们发送前端可以发送Web请求的时刻。
(2)mounted阶段:发送请求的时刻
需要结合axios技术发送异步请求结合使用,具体详细使用见:【4 案例练习:Axios+Vue,基于Vue及Axios完成数据的动态加载展示】
- 生命周期的八个阶段:每触发一个生命周期事件,会自动执行一个生命周期方法(钩子)
- mounted:挂载完成,Vue初始化成功,HTML页面染成功。(发送请求到服务端,加载数据)
<body>
<div id="app">
</div>
<script>
new Vue({
el: '#app',
data: {
},
methods: {
},
mounted() {
alert("Vue 实例已经挂载完成,发送请求到服务器获取数据") /* mounted钩子函数中发送请求到服务器获取数据,并且这个方法是自动执行的 */
// 后面会学习怎么发送请求并且将返回的响应数据保存到data中
},
})
</script>
</body>
四、Ajax技术(从服务端获取数据,发送各种请求)
参考视频
官方文档
0 接口文档管理:使用apipost等接口测试软件创建接口便于前端后端分离测试
参考视频
- 接口文档管理:
- 在线
- apipost
- apifox
- postman等等
- 离线
- word
- md
- 在线
在线的apipost这些测试工具功能很多,具体的后面不断深入学习慢慢了解这个测试工具,这个测试工具必须会用,后面无论是前端还是后端都需要频繁使用这个工具来进行测试。
下面的Ajax技术案例中的后端返回json数据都是通过这些接口工具的mock功能模拟生成的。
1 基本概念
学习本节前建议先去学习什么是GET、POST请求这些
- 概念: Asynchronous JavaScript And XML,异步的JavaScript和XML。
Ajax技术是一个异步交互技术,通过这个Ajax技术我们就可以从服务端获取数据 - 作用:
- 数据交换: 通过Ajax可以给服务器发送请求,并获取服务器响应的数据。
- 异步交互: 可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术,如: 搜索联想、用户名是否可
用的校验等等。
- 同步与异步请求区别
- 举一个例子就好理解了
我们在点击某些页面的时候,如果网络不好界面就会一直卡在转圈圈的界面,我们不能进行任何操作,这就是同步请求;如果我们点击某些页面就算网络不好我们还是可以操作页面,例如我们下载文件这个请求就是典型的异步请求。
- 举一个例子就好理解了
- Ajax技术就是一项发送异步请求的技术了。
2 原生Ajax(几年前的早期用法)
-
太繁琐,现在已经淘汰,了解一下
可以使用网页版的apifox、或者apipost(个人感觉apifox更好用,界面更加清晰,apipost界面有点复杂,好多功能要摸索)生成一个get请求响应数据作为测试 -
使用
-
(1)准备数据地址: https://mock.apipost.net/mock/3d9177ae94de000/user/getById?apipost_id=192051f4324002
用apipost先构建一个这样的mock地址
-
(2)创建XMLHttpRequest对象: 用于和服务器交换数据
-
(3)向服务器发送请求
-
(4)获取服务器响应数据
-
<body>
<input type="button" value="获取数据" onclick = "getData()">
<div id="div1"></div>
</body>
<script>
function getData(){
// 1 创建 XMLHttpRequest 对象
var xmlHttpRequest = new XMLHttpRequest();
// 2 发送异步请求
xmlHttpRequest.open("GET","https://mock.apipost.net/mock/3d9177ae94de000/user/getById?apipost_id=192051f4324002");
xmlHttpRequest.send(); // 发送请求
// 3 获取服务器响应的数据
xmlHttpRequest.onreadystatechange = function(){
if(xmlHttpRequest.readyState == 4 && xmlHttpRequest.status == 200){ // 判断服务器是否响应成功
//var data = JSON.parse(xmlHttpRequest.responseText);
document.getElementById("div1").innerHTML = xmlHttpRequest.responseText;
// xmlHttpRequest.responseText :返回服务器响应的数据,以字符串形式返回
}
}
}
</script>
点击按钮后:
- 代码解释
- 官方文档
3 Axios(对原生的Ajax进行了封装)
参考视频
-
介绍:Axios 对原生的Ajax进行了封装,简化书写,快速开发。
-
官网(使用文档等): https://www.axios-http.cn/
Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js -
使用
- 引入Axios的js文件
<script src=“https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js”></script>
上面官网有这么引用的介绍 - 使用Axios发送请求,并获取响应结果
- 引入Axios的js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Axios基本演示</title>
<!-- 引入axios -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<input type="button" value="获取数据" onclick = "getData()">
<div id="div1"></div>
</body>
<script>
function getData(){
// 通过axios发送异步get请求
axios({
method: "get",
url: "https://mock.apipost.net/mock/3d9177ae94de000/user/getById?apipost_id=192051f4324002"
}).then(result =>{
document.getElementById("div1").innerHTML = result.data.name;
// 通过 result.data 获取服务器返回的的JSON对象
})
// 通过axios发送异步post请求 ..... 后面有需要再补充
}
</script>
</html>
- 请求方式别名
axios提供了发送各种请求的别名简化调用方式,后面我们也更多会使用这种进行开发-
axios.get(url [, config])
-
axios.delete(url [, config])
-
axios.post(url [, data[, config]l)
-
axios.put(url [, data[, config]])
-
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Axios基本演示</title>
<!-- 引入axios -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<input type="button" value="获取数据" onclick = "getData()">
<div id="div1"></div>
</body>
<script>
function getData(){
// 通过axios发送异步get请求
/* axios({
method: "get",
url: "https://mock.apipost.net/mock/3d9177ae94de000/user/getById?apipost_id=192051f4324002"
}).then(result =>{
document.getElementById("div1").innerHTML = result.data.name;
// 通过 result.data 获取服务器返回的的JSON对象
}) */
axios.get("https://mock.apipost.net/mock/3d9177ae94de000/user/getById?apipost_id=192051f4324002").then(
result =>{
document.getElementById("div1").innerHTML =
"ID: " + result.data.id + "<br>" +
"Name: " + result.data.name + "<br>" +
"Age: " + result.data.age + "<br>" +
"Gender: " + result.data.gender;
}
)
// 通过axios发送异步post请求 ..... 后面有需要再补充
}
</script>
</html>
注意:
axios技术可以用来前端发送各种get、post请求等等,这里只是简单演示了怎么发送get请求并对其返回的数据进行简单处理。后面的不断深入学习前端会不断演示各种请求的发送与处理,特别是后面学习了Web请求的知识后。
4 案例练习:Axios+Vue,基于Vue及Axios完成数据的动态加载展示(重要!!!!!)
参考视频
-
数据准备的url: https://mock.apipost.net/mock/3d9177ae94de000/user/getUsers?apipost_id=1dafc1b2fc9005
-
在页面加载完成后,自动发送异步请求,加载数据,渲染展示页面。
使用Vue只要在mounted阶段:发送异步请求即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue + axios发送请求使用</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 引入axios -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<table border="1" cellspacing="0" width="60%">
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>职位</th>
<th>入职日期</th>
<th>最后操作时间</th>
</tr>
<tr align="center" v-for="user in users">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
<td>
<span v-show="user.gender === 1">男</span>
<span v-show="user.gender === 2">女</span>
</td>
<td>{{user.job}}</td>
<td>{{user.entrydate}}</td>
<td>{{user.updatetime}}</td>
</tr>
</table>
</div>
<script>
new Vue({
el: '#app',
data: {
users: [] // 定义一个空数组,用于存放请求到的数据,必须要有这个,不然直接this.users会报错
},
mounted() {
// 使用axios发送异步请求
axios.get("https://mock.apipost.net/mock/3d9177ae94de000/user/getUsers?apipost_id=1dafc1b2fc9005").then(
result =>{
this.users = result.data // 将请求到的数据赋值给users,使用 this挂载到Vue实例上
}
)
}
})
</script>
</body>
</html>