视频笔记是根据B站 千锋 涛哥 - SpringBoot+vue+前后端分离项目《锋迷商城》实战课-完结版 进行整理的
笔记可上 gitee仓库 自取
千锋 Vue 笔记整理
- [一、vue 的简介](#一、vue 的简介)
-
- [1.1 使用 JQuery 的复杂性问题](#1.1 使用 JQuery 的复杂性问题)
- [1.2 VUE 简介](#1.2 VUE 简介)
-
- [1.2.1 前端框架](#1.2.1 前端框架)
- [1.2.2 MVVM](#1.2.2 MVVM)
- [二、 vue 入门使用](#二、 vue 入门使用)
-
- [2.1 vue 的引入](#2.1 vue 的引入)
- [2.2 入门案例 -- Hello World](#2.2 入门案例 -- Hello World)
- [三、 vue 的语法](#三、 vue 的语法)
-
- [3.1 基本类型数据和字符串](#3.1 基本类型数据和字符串)
- [3.2 对象类型数据](#3.2 对象类型数据)
- [3.3 条件 v-if](#3.3 条件 v-if)
- [3.4 循环 v-for](#3.4 循环 v-for)
- [3.5 绑定标签属性 v-bind](#3.5 绑定标签属性 v-bind)
- [3.6 表单标签的双向绑定 v-model](#3.6 表单标签的双向绑定 v-model)
- [四、vue 实例](#四、vue 实例)
-
- [4.1 vue 实例的生命周期](#4.1 vue 实例的生命周期)
- [4.2 钩子函数](#4.2 钩子函数)
- [五、 计算属性和侦听器](#五、 计算属性和侦听器)
-
- [5.1 计算属性](#5.1 计算属性)
- [5.2 侦听器](#5.2 侦听器)
- [六、class 与 style 绑定](#六、class 与 style 绑定)
-
- [6.1 class 绑定](#6.1 class 绑定)
- [6.2 style 绑定](#6.2 style 绑定)
- 七、条件与列表渲染
-
- [7.1 条件渲染](#7.1 条件渲染)
-
- [7.1.1 v-if](#7.1.1 v-if)
- [7.1.2 v-else](#7.1.2 v-else)
- [7.1.3 v-else-if](#7.1.3 v-else-if)
- [7.1.4 v-show](#7.1.4 v-show)
- [7.2 列表渲染](#7.2 列表渲染)
- 八、事件处理
-
- [8.1 使用 JS 函数传值](#8.1 使用 JS 函数传值)
- [8.2 使用 dataset 对象传值 `(推荐)`](#8.2 使用 dataset 对象传值
(推荐)
) - [8.3 混合使用](#8.3 混合使用)
- [8.4 事件修饰符](#8.4 事件修饰符)
-
- [8.4.1 事件修饰符使用示例](#8.4.1 事件修饰符使用示例)
- [8.4.2 事件修饰符](#8.4.2 事件修饰符)
- [8.5 按键修饰符](#8.5 按键修饰符)
- [8.6 系统修饰符](#8.6 系统修饰符)
- 九、表单输入绑定
- 十、组件
-
- [10.1 组件介绍及示例](#10.1 组件介绍及示例)
-
- [10.1.1 组件注册](#10.1.1 组件注册)
- [10.1.2 组件引用](#10.1.2 组件引用)
- [10.2 组件注册](#10.2 组件注册)
-
- [10.2.1 自定义组件的结构](#10.2.1 自定义组件的结构)
- [10.2.2 组件的封装](#10.2.2 组件的封装)
- [10.2.3 组件的复用](#10.2.3 组件的复用)
- [10.3 组件通信](#10.3 组件通信)
-
- [10.3.1 父传子](#10.3.1 父传子)
- [10.3.2 子传父 -- 使用 `props` 属性动态传递参数](#10.3.2 子传父 -- 使用
props
属性动态传递参数) - [10.3.3 总结](#10.3.3 总结)
- [10.4 组件插槽](#10.4 组件插槽)
-
- [10.4.1 插槽的使用](#10.4.1 插槽的使用)
- [10.4.2 具名插槽](#10.4.2 具名插槽)
- [10.4.3 插槽作用域](#10.4.3 插槽作用域)
- [十一、axios 异步通信](#十一、axios 异步通信)
-
- [11.1 axios 介绍](#11.1 axios 介绍)
- [11.2 axios 入门使用](#11.2 axios 入门使用)
- [11.3 axios 异步请求方法](#11.3 axios 异步请求方法)
-
- [11.3.1 GET 格式的请求](#11.3.1 GET 格式的请求)
- [11.3.2 POST 格式的请求](#11.3.2 POST 格式的请求)
- [11.3.3 自定义请求](#11.3.3 自定义请求)
- [11.3.4 请求方法别名](#11.3.4 请求方法别名)
- [11.4 并发请求](#11.4 并发请求)
- [11.5 箭头函数](#11.5 箭头函数)
-
- [11.5.1 axios 回调函数的参数 res](#11.5.1 axios 回调函数的参数 res)
- [11.5.2 箭头函数](#11.5.2 箭头函数)
- [十二、 路由 router](#十二、 路由 router)
-
- [12.1 路由插件的引用](#12.1 路由插件的引用)
-
- [12.1.1 离线](#12.1.1 离线)
- [12.1.2 在线 CDN](#12.1.2 在线 CDN)
- [12.2 动态路由匹配](#12.2 动态路由匹配)
-
- [12.2.1 通配符](#12.2.1 通配符)
- [12.2.2 路由参数](#12.2.2 路由参数)
- [12.2.3 优先级](#12.2.3 优先级)
- [12.3 嵌套路由](#12.3 嵌套路由)
- [12.4 编程式导航](#12.4 编程式导航)
-
- [12.4.1 push()](#12.4.1 push())
- [12.4.2 push() 参数](#12.4.2 push() 参数)
- [12.4.3 replace()](#12.4.3 replace())
- [12.4.4 go()](#12.4.4 go())
- [12.5 命名路由](#12.5 命名路由)
- [12.6 命名视图](#12.6 命名视图)
- [12.7 重定向](#12.7 重定向)
-
- [12.7.1 重定向](#12.7.1 重定向)
- [12.7.2 路由别名](#12.7.2 路由别名)
- [12.8 路由组件传参](#12.8 路由组件传参)
一、vue 的简介
1.1 使用 JQuery 的复杂性问题
- 使用 JQuery 进行前后端分离开发,既可以实现前后端交互 (ajax) ,又可以完成数据渲染
- 存在的问题: JQuery 需要通过 HTML 标签拼接、DOM 节点操作完成数据的显示,开发效率低且容易出错,渲染效率较低
- vue 是继 JQuery 之后的又一优秀的前端框架:专注于前端数据的渲染 ------------ 语法简单、渲染效率高
1.2 VUE 简介
1.2.1 前端框架
-
前端三框架:HTML、CSS、JavaScript
- HTML 决定网页结构
- CSS 决定显示效果
- JavaScript 决定网页功能 (交互、数据显示)
-
UI 框架:(只提供样式、显示效果)
- Bootstrap
- amazeUI
- Layui
-
JS 框架:
- JQuery (JQuery UI)
- React
- angular
- nodejs ---- 后端开发
- vue 集各种前端框架的优势发展而来
1.2.2 MVVM
项目结构经历的三个阶段:
后端 MVC :可以理解为单体结构,流程控制是由后端控制器来完成
前端 MVC :前后端分离开发,后端只负责接收响应请求
MVVM是MVC的增强版,实质上和MVC没有本质区别,只是代码的位置变动而已
MVVM 前端请求后端接口,后端返回数据,前端接收数据,并将接收到的数据设置为 "VM",HTML 从 vm 取值
- M model 数据模型,指的是后端接口返回的数据
- V view 视图
- VM ViewModel 视图模型 数据模型与视图之间的桥梁,后端返回的 model 转换前端所需的 vm,视图层可以直接从 vm 中提取数据
Model-View-ViewModel ------ 概念图
二、 vue 入门使用
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
2.1 vue 的引入
-
离线引用:下载 vue 的 js 文件,添加到前端项目,在网页中通过 script 标签引用 vue.js 文件
-
CDN 引用:
直接使用在线 CDN 的方式引入
html
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
2.2 入门案例 -- Hello World
文本:数据绑定最常见的形式就是使用"Mustache"语法 (双大括号) 的文本插值:
{``{message}}
-
创建一个 HTML 文件
-
引入 vue.js 文件
javascript<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
-
示例:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 引入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app-1">
从 0 开始: {{message}}
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-1',
data: {
message: 'Hello World!'
}
})
</script>
</body>
</html>
Mustache 标签将会被替代为对应数据对象上 message
property 的值。无论何时,绑定的数据对象上 message
property 发生了改变,插值处的内容都会更新。
三、 vue 的语法
3.1 基本类型数据和字符串
html
<div id="app-2">
{{code}}
从 0 开始: {{message}}
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-2',
data: {
code: 1_3_3,
message: 'Hello World!'
}
})
</script>
3.2 对象类型数据
- 支持 ognl 语法
html
<div id="app-3">
学号:{{stu.stuNum}} <br />
姓名:{{stu.stuName}} <br />
性别:{{stu.stuGender}} <br />
年龄:{{stu.stuAge}}
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-3',
data: {
stu: {
stuNum: '10001',
stuName: '张三',
stuGender: 'M',
stuAge: 20
}
}
})
</script>
3.3 条件 v-if
v-if : 用来控制切换一个元素是否显示 (底层控制是 DOM 元素,操作 DOM)
注:在浏览器中网页打开这个文件 F12,从标签上可以看到没有 stu.stuGender == 'F'
对应的元素。即条件不成立,网页不会渲染该 DOM,连标签都不会有。
html
<div id="app-4">
学号:{{stu.stuNum}} <br />
姓名:{{stu.stuName}} <br />
性别:
<label v-if="stu.stuGender == 'M'">
男
</label>
<label v-if="stu.stuGender == 'F'">
女
</label>
<br />
年龄:{{stu.stuAge}}
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-4',
data: {
stu: {
stuNum: '10001',
stuName: '张三',
stuGender: 'M',
stuAge: 20
}
}
})
</script>
3.4 循环 v-for
v-for
指令基于一个数组来渲染一个列表。在
v-for
块中,我们可以访问所有父作用域的 property。v-for
还支持一个可选的第二个参数,即当前项的索引。
html
<div id="app-5">
<table border="1" cellspacing="0" width="400">
<tr>
<th>序号</th>
<th>学号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
</tr>
<tr v-for="(stu, index) in stus">
<td>{{index + 1}}</td>
<td>{{stu.stuNum}}</td>
<td>{{stu.stuName}}</td>
<td>
<table v-if="stu.stuGender == 'M'">
男
</table>
<table v-if="stu.stuGender == 'F'">
女
</table>
</td>
<td>{{stu.stuAge}}</td>
</tr>
</table>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-5',
data: {
stus: [
{
stuNum: '10001',
stuName: '张三',
stuGender: 'M',
stuAge: 20
},
{
stuNum: '10002',
stuName: '李四',
stuGender: 'M',
stuAge: 23
},
{
stuNum: '10003',
stuName: '郑红',
stuGender: 'F',
stuAge: 19
}
]
}
})
</script>
3.5 绑定标签属性 v-bind
- v-bind:属性名 缩写 :
:属性名
html
<div id="app-6">
<input type="text" v-bind:value="message">
<hr /><br />
<table border="1" cellspacing="0" width="400">
<tr>
<th>序号</th>
<th>学号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
</tr>
<tr v-for="(stu, index) in stus">
<td>{{index + 1}}</td>
<td>{{stu.stuNum}}</td>
<td><img height="30" :src="stu.stuImg" /> </td>
<td>{{stu.stuName}}</td>
<td>
<table v-if="stu.stuGender == 'M'">
男
</table>
<table v-if="stu.stuGender == 'F'">
女
</table>
</td>
<td>{{stu.stuAge}}</td>
</tr>
</table>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-6',
data: {
message: 'Hello World!!',
stus: [
{
stuNum: '10001',
stuName: '张三',
stuGender: 'M',
stuAge: 20,
stuImg: 'img/1.jpg'
},
{
stuNum: '10002',
stuName: '李四',
stuGender: 'M',
stuAge: 23,
stuImg: 'img/2.jpg'
},
{
stuNum: '10003',
stuName: '郑红',
stuGender: 'F',
stuAge: 19,
stuImg: 'img/3.jpg'
}
]
}
})
</script>
3.6 表单标签的双向绑定 v-model
- 只能使用在表单输入标签
- v-model:value 可以简写为 v-model
html
<div id="app-7">
<input type="text" v-model="message">
<hr /><br />
双向绑定:{{message}}
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-7',
data: {
message: 'Hello World!!'
}
})
</script>
四、vue 实例
每个使用 vue 进行数据渲染的网页文档都需要创建一个 vue 实例 --- --- ViewModel
4.1 vue 实例的生命周期
vue 实例生命周期 --- --- vue 实例从创建到销毁的过程
- 创建 vue 实例 (初始化 data,加载 el)
- 数据挂载 (将 vue 实例 data 中的数据渲染到网页 HTML 标签)
- 重新渲染 (当 vue 的 data 数据发生变化,会重新渲染到 HTML 标签)
- 销毁实例
创建对象 ---- 属性初始化 ---- 获取属性值 ----- GC 回收
4.2 钩子函数
为了便于开发者在 vue 实例生命周期的不同阶段进行特定的操作,vue 在生命周期四个阶段的前后分别提供了一个函数,这个函数无需开发者调用,当 vue 实例到达生命周期的指定阶段会自动调用对应的函数。
html
<div id="app-8">
<label v-once>{{message}}</label><br />
<label>{{message}}</label><br />
<input type="text" v-model="message">
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-8',
data: {
message: 'Hello World!!'
},
beforeCreate: function () {
// 1. data 初始化之前执行,不能操作 data
},
create: function () {
// 2. data 初始化之后执行,模板加载之前,可以修改 / 获取 data 中的值
console.log(this.message);
// this.message = 'Hello World!! create()';
},
beforeMount: function () {
// 3. 模板加载之后,数据初始渲染(挂载)之前,可以修改 / 获取 data 中的值
// this.message = 'Hello World!! beforeMount';
},
mounted: function () {
// 4. 数据初始渲染(挂载)之后,可以对 data 中的变量进行修改,但是不会影响 v-once 的渲染
// this.message = "Hello World!! mounted";
},
beforeUpdate: function () {
// 5. 数据渲染之后,当 data 中的数据发生变化触发重新渲染,渲染之前执行此函数
console.log("---" + this.message);
this.message = 'Hello World!! beforeUpdate';
},
update: function () {
// 6. data 数据被修改之后,重新渲染到页面之后
// this.message = "Hello World!! update";
},
beforeDestroy: function () {
// 7. 实例销毁之前
},
destroy: function () {
// 8. 实例销毁之后
}
})
</script>
五、 计算属性和侦听器
5.1 计算属性
data 中的属性可以通过声明获得,也可以通过在 computed 计算属性的 getter 获得
特性:计算属性所依赖的属性值发生变化会影响计算属性的值同时发生变化
示例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-9">
<input type="text" v-model="message1"><br />
<input type="text" v-model="message2"><br />
{{message3}}
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-9',
data: {
message1: 'Hello',
message2: 'World'
},
computed: {
message3: function () {
return this.message1 + this.message2;
}
}
})
</script>
</body>
</html>
5.2 侦听器
侦听器,就是 data 中属性的侦听器,当 data 中的属性值发生变化就会触发侦听器函数的执行
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-10">
<input type="text" v-model="message1"><br />
<input type="text" v-model="message2"><br />
{{message3}}
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-10',
data: {
message1: 'Hello',
message2: 'World',
message3: 'Hello World'
},
watch: {
message1: function () {
this.message3 = this.message1 + this.message2;
},
message2: function () {
this.message3 = this.message1 + this.message2;
}
}
})
</script>
</body>
</html>
六、class 与 style 绑定
6.1 class 绑定
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.my-style1 {
width: 200px;
height: 100px;
background: orange;
}
.my-style2 {
border-radius: 10px;
}
.my-style3 {
width: 200px;
height: 100px;
background: black;
}
</style>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-11">
<!-- 如果 message1 为 true, 就加载 my-style1,如果 message2 为 true, 就加载 my-style2 -->
<div :class="{'my-style1': message1, 'my-style2' : message2}">
</div><hr />
<!-- 为 class 属性加载多个样式名 -->
<div :class="[booleanValue1, booleanValue2]">
</div><hr />
<!-- 如果 message3 为 true, 则 class = 'my-style3',否则 class = 'my-style1'
如果在三目运算中使用样式名则需要加单引号,不加单引号则表示从 data 变量中获取样式名
-->
<div :class="[message3 ? 'my-style3' : 'my-style1']">
</div>
<div :class="[message1 ? booleanValue1 : booleanValue3]"></div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-11',
data: {
message1: true,
message2: true,
message3: true,
booleanValue1: "my-style1",
booleanValue2: "my-style2",
booleanValue3: "my-style3"
}
})
</script>
</body>
</html>
6.2 style 绑定
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-12">
<!-- 当使用 v-bind 绑定内联样式时:
1. 使用 {} 定义 style 样式,才能获取 data 的值, {} 要遵循 JSON 格式
2. {} 中不在使用 style 样式属性名 "font-size", 而要使用对应的 js 属性名
border-style-width --- borderStyleWidth
-->
<div v-bind:style="{color: colorName, fontSize: fontsize + 'px' }">
Hello World!
</div>
<!-- 我们可以直接为 style 属性绑定一个 data 中定义好的内联样式的字符串 -->
<div v-bind:style="myStyle1">
Hello World!!
</div>
<!-- 我们可以直接为 style 属性绑定一个 data 中定义好的内联样式的对象 -->
<div v-bind:style="myStyle2">
Hello World!!!
</div>
<!-- 我们可以在同一个 style 属性通过数组引用多个内联样式的对象 -->
<div v-bind:style="[myStyle2, myStyle3]">
Hello World!!!!
</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-12',
data: {
colorName: "yellow",
fontsize: "40",
myStyle1: "color: orange; font-size: 50px",
myStyle2: {
color: "blue",
fontSize: "60px"
},
myStyle3: {
textShadow: "orange 3px 3px 5px"
}
}
})
</script>
</body>
</html>
七、条件与列表渲染
7.1 条件渲染
条件判断语句:
v-if
v-else-if
v-else
7.1.1 v-if
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-13">
<h3 v-if="code == 1">Hello :可以看到</h3>
<h3 v-if="flag">flag 为 true, 可以看到</h3>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-13',
data: {
code: 1,
flag: false
}
})
</script>
</body>
</html>
7.1.2 v-else
v-else
指令来表示v-if
的"else 块"
html
<div id="app-13">
<!-- v-else 标签需要紧跟在 v-if 的标签之后,中间不能有其他标签, 否则它将不会被识别 -->
<h3 v-if="code == 1">Hello :可以看到</h3>
<h3 v-else>World :可以看到</h3>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-13',
data: {
code: 1
}
})
</script>
7.1.3 v-else-if
html
<div id="app-13">
分数 {{code}}
对应的等级:
<h3 v-if="code >= 90">优秀</h3>
<h3 v-else-if="code >= 80">良好</h3>
<h3 v-else-if="code >= 70">中等</h3>
<h3 v-else-if="code >= 60">及格</h3>
<h3 v-else>挂科</h3>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-13',
data: {
code: 85
}
})
</script>
7.1.4 v-show
v-show
:同样用于根据条件展示元素。从功能上 v-show 与 v-if 作用是相同的,只是渲染过程有区别。
v-if 与 v-show 的区别:
v-if
是"真正"的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做------直到条件第一次变为真时,才会开始渲染条件块。- 相比之下,
v-show
就简单得多------不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。 - 一般来说,
v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show
较好;如果在运行时条件很少改变,则使用v-if
较好。
7.2 列表渲染
编码过程中,发现网页出现报错:
Uncaught Error: Bootstrap's JavaScript requires jQuery at bootstrap.min.js:6
解决方法:在引进JQuery文件时,将其放在 bootstrap 前面即可。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-14">
<ul>
<li v-for="c in categories">
<a :href="'query?cid=' + c.cid">{{c.cname}}</a>
</li>
</ul>
<table class="table table-bordered">
<tr>
<th>学号</th>
<th>照片</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>操作</th>
</tr>
<template v-for="(s, index) in stu">
<tr :id="'tr' + s.stuNum">
<td>{{s.stuNum}}</td>
<td>
<img height="30" :src="s.stuImg" />
</td>
<td>{{s.stuName}}</td>
<td>
<!-- {{s.stuGender == 'M' ? '男' : '女'}} -->
<img v-if="s.stuGender == 'M'" src="img/m.bmp">
<img v-else src="img/f.bmp">
</td>
<td>{{s.stuAge}}</td>
<td>
<a class="btn btn-danger" :href="'stu/delete?cid=' + s.stuNum">删除</a>
<a class="btn btn-success" :href="'stu/update?cid=' + s.stuNum">修改</a>
</td>
</tr>
</template>
</table>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-14',
data: {
categories:[
{
cid: 1,
cname: "华为"
},
{
cid: 2,
cname: "小米"
},
{
cid: 3,
cname: "OPPO"
},
{
cid: 4,
cname: "VIVO"
}
],
stu: [
{
stuNum: "10010",
stuImg: "img/1.jpg",
stuName: "Tom",
stuGender: "M",
stuAge: 20
},
{
stuNum: "10011",
stuImg: "img/2.jpg",
stuName: "Joker",
stuGender: "M",
stuAge: 21
},
{
stuNum: "10012",
stuImg: "img/3.jpg",
stuName: "Ling",
stuGender: "F",
stuAge: 20
},
{
stuNum: "10013",
stuImg: "img/1.jpg",
stuName: "Jack",
stuGender: "F",
stuAge: 18
},
]
}
})
</script>
</body>
</html>
八、事件处理
在使用 vue 进行数据渲染时,如果使用原生 js 事件绑定 (例如 onclick),如果需要获取 vue 实例中的数据并传参则需要通过拼接来完成
vue 提供了 v-on 指令用于绑定各种事件 (v-on:click),简化了从 vue 取值的过程,但是触发的方法需要定义在 vue 实例的 methods 中
html<button class="btn btn-danger" v-on:click="doDelete(s.stuNum,s.stuName)">删除</button> <script type="text/javascript"> var vm = new Vue({ el: '#app-15', data: { stu: [ { stuNum: "10010", stuImg: "img/1.jpg", stuName: "Tom", stuGender: "M", stuAge: 20 } ] }, methods: { doDelete: function (sNum, sName) { alert("---delete:" + sNum + " " + sName) } } }) </script>
v-on:click
可以简写为@click
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-15">
<ul>
<li v-for="c in categories">
<a :href="'query?cid=' + c.cid">{{c.cname}}</a>
</li>
</ul>
<table class="table table-bordered">
<tr>
<th>学号</th>
<th>照片</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>操作</th>
</tr>
<template v-for="(s, index) in stu">
<tr :id="'tr' + s.stuNum">
<td>{{s.stuNum}}</td>
<td>
<img height="30" :src="s.stuImg" />
</td>
<td>{{s.stuName}}</td>
<td>
<!-- {{s.stuGender == 'M' ? '男' : '女'}} -->
<img v-if="s.stuGender == 'M'" src="img/m.bmp">
<img v-else src="img/f.bmp">
</td>
<td>{{s.stuAge}}</td>
<td>
<button class="btn btn-danger" v-on:click="doDelete(s.stuNum,s.stuName)">删除</button>
<button class="btn btn-success" @click="doUpdate" :data-snum="s.stuNum"
:data-sname="s.stuName" :data-simg="s.stuImg">修改</button>
</td>
</tr>
</template>
</table>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-15',
data: {
categories:[
{
cid: 1,
cname: "华为"
},
{
cid: 2,
cname: "小米"
},
{
cid: 3,
cname: "OPPO"
},
{
cid: 4,
cname: "VIVO"
}
],
stu: [
{
stuNum: "10010",
stuImg: "img/1.jpg",
stuName: "Tom",
stuGender: "M",
stuAge: 20
},
{
stuNum: "10011",
stuImg: "img/2.jpg",
stuName: "Joker",
stuGender: "M",
stuAge: 21
},
{
stuNum: "10012",
stuImg: "img/3.jpg",
stuName: "Ling",
stuGender: "F",
stuAge: 20
},
{
stuNum: "10013",
stuImg: "img/1.jpg",
stuName: "Jack",
stuGender: "F",
stuAge: 18
},
]
},
methods: {
doDelete: function (sNum, sName) {
alert("---delete:" + sNum + " " + sName)
},
doUpdate: function (event) {
// 如果 v-on 绑定的 js 函数没有参数,调用的时候可以省略 (), 同时可以给 js 函数一个 event 参数(事件对象)
// 1. event 表示触发当前函数的事件
// 2. event.srcElement 表示发生事件的元素 --- 修改按钮
// 3. event.srcElement.dataset 表示按钮上绑定的数据集 (data-开头的属性)
alert("---update:");
let stu = event.srcElement.dataset;
}
}
})
</script>
</body>
</html>
8.1 使用 JS 函数传值
html
<button class="btn btn-danger" v-on:click="doDelete(s.stuNum,s.stuName)">删除</button>
<script type="text/javascript">
var vm = new Vue({
el: '#app-15',
data: {
stu: [
{
stuNum: "10010",
stuImg: "img/1.jpg",
stuName: "Tom",
stuGender: "M",
stuAge: 20
}
]
},
methods: {
doDelete: function (sNum, sName) {
alert("---delete:" + sNum + " " + sName)
}
}
})
</script>
8.2 使用 dataset 对象传值 (推荐)
html
<button class="btn btn-success" @click="doUpdate" :data-snum="s.stuNum"
:data-sname="s.stuName" :data-simg="s.stuImg">修改</button>
<script type="text/javascript">
var vm = new Vue({
el: '#app-15',
data: {
stu: [
{
stuNum: "10010",
stuImg: "img/1.jpg",
stuName: "Tom",
stuGender: "M",
stuAge: 20
}
]
},
methods: {
doUpdate: function (event) {
// 如果 v-on 绑定的 js 函数没有参数,调用的时候可以省略 (), 同时可以给 js 函数一个 event 参数(事件对象)
// 1. event 表示触发当前函数的事件
// 2. event.srcElement 表示发生事件的元素 --- 修改按钮
// 3. event.srcElement.dataset 表示按钮上绑定的数据集 (data-开头的属性)
alert("---update:");
let stu = event.srcElement.dataset;
}
}
})
</script>
8.3 混合使用
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event
把它传入方法
html
<button class="btn btn-danger" v-on:click="doDelete(s.stuNum,s.stuName, $event)" :data-simg="s.stuImg">删除</button>
<script type="text/javascript">
var vm = new Vue({
el: '#app-15',
data: {
stu: [
{
stuNum: "10010",
stuImg: "img/1.jpg",
stuName: "Tom",
stuGender: "M",
stuAge: 20
}
]
},
methods: {
doDelete: function (sNum, sName, event) {
alert("---delete:" + sNum + " " + sName)
console.log(event.srcElement.dataset)
}
}
})
</script>
8.4 事件修饰符
修饰符是由点开头的指令后缀来表示的。
当使用 v-on 进行事件绑定的时候,可以添加特定后缀,设置事件触发的特性
8.4.1 事件修饰符使用示例
html
<button type="submit" class="btn btn-success" @click.prevent="test">修改</button>
8.4.2 事件修饰符
常用的事件修饰符:
.stop
.prevent
.capture
.self
.once
.passive
.prevent 用来阻止标签的默认行为
html
<div id="app-16">
<form action="https://www.baidu.com">
<!-- 此处不加 .prevent ,网页会跳转到 https://www.baidu.com -->
<button type="submit" class="btn btn-success" @click.prevent="test">修改</button>
</form>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-16',
data: {},
methods: {
test: function (event) {
alert("--test");
}
}
})
</script>
.stop 阻止事件冒泡
.self 设置只能自己触发事件(子标签不能触发)
html
<div id="app-16">
<!-- .prevent 示例 -->
<form action="https://www.baidu.com">
<!-- 此处不加 .prevent ,网页会跳转到 https://www.baidu.com -->
<button type="submit" class="btn btn-success" @click层="test">修改</button>
</form>
<!-- .stop 示例 .self 示例:只能点击自己触发 -->
<div style="width: 200px; height: 200px; background: red;" @click.self="outside">
<div style="width: 100px; height: 100px; background: yellow;" @click="inside">
<!-- 此处加了 .stop , 网页的外层 div 父标签不会触发 -->
<button @click.stop="itself">test stop 修饰符</button>
</div>
</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-16',
data: {},
methods: {
test: function (event) {
alert("--test");
},
inside: function () {
alert("--inside");
},
outside: function () {
alert("--outside");
},
itself: function () {
alert("--itself");
}
}
})
</script>
.once 限定事件只能触发一次
html
<!-- .once 示例:test()方法只执行一次, -->
<form action="https://www.baidu.com">
<!-- 加了 .prevent,第一次点击执行 test(),默认跳转被阻止, 第二次点击按钮,直接跳转, test()不执行 -->
<button type="submit" class="btn btn-success" @click.prevent.once="test">修改</button>
</form>
8.5 按键修饰符
按键修饰符:针对键盘事件的修饰符,限定哪个按键会触发事件
常用的按键修饰符:
.enter
.tab
.delete
(捕获"删除"和"退格"键).esc
.space
.up
.down
.left
.right
.enter 只有在 key
是 Enter
时调用(触发 enter
按键之后触发事件)
html
<div id="app-17">
<input type="text" @keyup.enter="test">
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-17',
data: {},
methods: {
test: function () {
alert("enter松开触发")
}
}
})
</script>
还可以通过全局 config.keyCodes
对象自定义按键修饰符别名:
html
// 输入 按键 j 触发事件 test
<input type="text" @keyup.aaa="test">
// 按键 j 的别名
Vue.config.keyCodes.aaa = 74
8.6 系统修饰符
组合键
示例:CTRL + J 触发事件
html
<div id="app-17">
<input type="text" @keyup.ctrl.j="test">
</div>
<script type="text/javascript">
Vue.config.keyCodes.j = 74
var vm = new Vue({
el: '#app-17',
data: {},
methods: {
test: function () {
alert("enter松开触发")
}
}
})
</script>
常用的修饰符:
.ctrl
.alt
.shift
.meta
windows 键
九、表单输入绑定
表单输入绑定,即双向绑定:就是能够将 vue 实例的 data 数据渲染到表单输入视图 (input\textarea\select),
也能够将输入视图的数据同步到 vue 实例的 data中。
html
<div id="app-18">
<!-- 文本输入框 密码输入框 -->
<input type="text" v-model="name" /><br />
<input type="password" v-model="password" />
<hr />
<!-- 单选按钮 -->
<input type="radio" v-model="radioTest" value="A" /> A
<input type="radio" v-model="radioTest" value="B" /> B
<input type="radio" v-model="radioTest" value="C" /> C
<input type="radio" v-model="radioTest" value="D" /> D
<hr />
<!-- 复选框,绑定的是一个数组 -->
<input type="checkbox" v-model="checkBox" value="篮球" /> 篮球 <br />
<input type="checkbox" v-model="checkBox" value="足球" /> 足球 <br />
<input type="checkbox" v-model="checkBox" value="羽毛球" /> 羽毛球 <br />
<input type="checkbox" v-model="checkBox" value="乒乓球" /> 乒乓球 <br />
<!-- 下拉菜单 select:绑定一个字符串 -->
<select v-model="city">
<option value="BJ">北京</option>
<option value="SH">上海</option>
<option value="SZ">深圳</option>
<option value="GZ">广州</option>
</select>
<hr />
<!-- 下拉菜单 select:加上了 multiple ,表示可多选,需要绑定一个数组 -->
<select v-model="cities" multiple>
<option value="BJ">北京</option>
<option value="SH">上海</option>
<option value="SZ">深圳</option>
<option value="GZ">广州</option>
</select>
<button type="button" @click="test">测试</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app-18',
data: {
name: "admin",
password: "123456",
radioTest: "C",
checkBox: [],
city: "",
cities: []
},
methods: {
test: function () {
alert(vm.cities)
}
}
})
</script>
十、组件
10.1 组件介绍及示例
组件,就是讲通用的 HTML 模块进行封装 ------ 可复用的 Vue 实例
通常一个应用会以一棵嵌套的组件树的形式来组织:
10.1.1 组件注册
将通用的 HTML 模块封装注册到 vue 中
自定义组件 my-components.js:
javascript
Vue.component('header-button', {
template: `<div style="width: 100%; height: 80px; background: salmon">
<table width="100%">
<tr>
<td width="200" align="right" valign="middle">
<img src="img/1.jpg" height="80" />
</td>
<td>
<label style="color: deepskyblue; font-size: 32px; font-family: 'Adobe 楷体 Std R'; margin-left: 30px">
登录
</label>
</td>
</tr>
</table>
</div>`
});
10.1.2 组件引用
- 定义组件需要依赖 vue.js,在引用自定义组件 js 文件要先引用 vue.js
- 组件的引用必须在 vue 实例 el 指定的容器中 ,即要在Vue实例范围内
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-19">
<header-button></header-button>
<hr />
<header-button></header-button>
</div>
<script src="js/my-components.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app-19'
})
</script>
</body>
</html>
10.2 组件注册
10.2.1 自定义组件的结构
Vue.component()
注册组件data
定义组件的模板渲染的数据template
组件的 HTML 模块(HTML 标签 \ CSS 样式)methods
定义组件中的标签事件中绑定的 JS 函数
my-components.js:
javascript
Vue.component('header-button', {
data: function () {
// 组件中 data 是通过函数返回的对象
return {
name: "貂蝉"
};
},
template: `<div style="width: 100%; height: 80px; background: salmon">
<table width="100%">
<tr>
<td width="200" align="right" valign="middle">
<img src="img/1.jpg" height="80" />
</td>
<td>
<label style="color: deepskyblue; font-size: 32px; font-family: 'Adobe 楷体 Std R'; margin-left: 30px">
登录人: {{name}}
</label>
</td>
<td>
<button @click="test">组件按钮</button>
</td>
</tr>
</table>
</div>`,
methods: {
test: function () {
alert("组件中 header-button 定义的函数事件")
}
}
});
10.2.2 组件的封装
- 将模板中的 css 样式提取出来,单独封装到 css 文件存储在 css 目录
- 将模板中的图片存储在 img 目录
- 将定义组件的 js 文件和 vue 文件存放到 js 目录
Vue 组件封装的结构 |
---|
注:在编码过程中学到,Vue 中同一个 DOM 元素绑定多个点击事件:可以使用逗号分隔。
10.2.3 组件的复用
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="css/my-components.css" />
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-19">
<header-button></header-button>
<hr />
<header-button></header-button>
</div>
<script src="js/my-components.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app-19'
})
</script>
</body>
</html>
10.3 组件通信
vue 实例本身就是一个组件(模板就是 el 指定容器,data 就是组件数据,methods 就是组件的事件函数)
在 vue 实例指定的 el 容器中引用的组件称为子组件,当前 vue 实例就是父组件
注:子组件按钮模板不能触发父组件的方法,子组件的按钮可以触发子组件的方法。
10.3.1 父传子
vue 实例引用组件的时候,传递数据到引用的组件中
通过组件的属性实现父组件传递数据到子组件
示意图 |
---|
10.3.2 子传父 -- 使用 props
属性动态传递参数
通过子组件的按钮"调用"父组件的函数,通过函数传值
流程示意图 |
---|
父组件:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="css/my-components.css" />
<script src="js/vue.js"></script>
</head>
<body>
<div id="app-21">
<!-- 组件的引用必须在 vue 实例指定的容器中 -->
<header-bar :name="message" @my-event="parentMethod"></header-bar>
子组件传到父组件的标题名称: {{title}}
<hr />
<header-bar name="关羽"></header-bar>
</div>
<script src="js/my-components-bar2.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app-21',
data: {
message: "貂蝉",
title: ""
},
methods: {
parentMethod: function (res) {
vm.title = res;
}
}
})
</script>
</body>
</html>
子组件:
javascript
Vue.component('header-bar', {
data: function() {
return {
title: "三国 -- √"
}
},
template: `<div class="divStyle">
<table class="tableStyle">
<tr>
<td width="200" align="right" valign="middle">
<img src="img/1.jpg" class="logoImg" />
</td>
<td>
<label class="nameStyle">
登录人: {{name}}
</label>
</td>
<td>
<button @click="childMethod">组件按钮点击次数</button>
</td>
</tr>
</table>
</div>`,
props: ["name"],
methods: {
childMethod: function () {
this.$emit("my-event", this.title);
}
}
});
10.3.3 总结
父组件通信子组件
-
props
: 子组件通过props获取定义父组件传递的自定义属性 -
this.$refs
: 引用子组件 -
this.$children
: 父组件childrens属性,存储着所有的子组件
子组件通信父组件(或根组件)
-
this.$emit
: 子组件通过$emit
访问父组件传递的自定义事件 -
this.$parent
: 访问父组件 -
this.$root
: 访问根组件。根组件root就是new Vue({el: "#app"});
中的el元素
10.4 组件插槽
当我们自定义 vue 组件时,允许组件中的部分内容在调用组件时进行定义 ------ 插槽
10.4.1 插槽的使用
- 在自定义组件时通过
slot
标签在组件的模板中定义插槽
my-components-bar-slot.js:
javascript
Vue.component('header-bar', {
data: function() {
return {
title: "三国 -- √"
}
},
template: `<div class="divStyle">
<table class="tableStyle">
<tr>
<td width="200" align="right" valign="middle">
<img src="img/1.jpg" class="logoImg" />
</td>
<td>
<label class="nameStyle">
登录人: {{name}}
</label>
</td>
<td>
<slot></slot>
</td>
<td>
<button @click="childMethod">组件按钮点击次数</button>
</td>
</tr>
</table>
</div>`,
props: ["name"],
methods: {
childMethod: function () {
this.$emit("my-event", this.title);
}
}
});
- 在父组件中调用此组件时,指定插槽填充的模板
示例1:插槽填充 搜索框
html
<header-bar :name="message">
<input /> <button>搜索</button>
</header-bar>
示例2:插槽填充 按钮
html
<header-bar :name="message" @my-event="parentMethod">
<div>
<a href="#">首页</a>
<a href="#">后台</a>
<a href="#">管理</a>
<a href="#">关于</a>
</div>
</header-bar>
10.4.2 具名插槽
当组件中的插槽数量 > 1 时,需要给组件中的
slot
标签添加name
属性指定插槽的名字
- 定义组件
javascript
Vue.component('page-frame', {
template: `<div>
<div id="header" style="width: 100%; height: 100px; background: pink">
{{title}}<br />
slot1 : <br />
<slot name="slot1"></slot>
</div>
<div style="width: 100%; height: 580px">
slot2 : <br />
<slot name="slot2"></slot>
</div>
<div id="footer" style="width: 100%; height: 40px; background: lightblue">{{cr}}</div>
</div>`,
props: ["title", "cr"]
});
- 引用组件
html
<div id="app-22">
<!-- 组件的引用必须在 vue 实例指定的容器中 -->
<page-frame title="标题" cr="地址">
<template slot="slot1">
<input /> <button>搜索</button>
</template>
<template slot="slot2">
<div>
<a href="#">首页</a>
<a href="#">后台</a>
<a href="#">管理</a>
<a href="#">关于</a>
</div>
</template>
</page-frame>
</div>
10.4.3 插槽作用域
- 定义组件时,将组件中的数据绑定到
slot
标签
my-components-bar-slot.js:
javascript
Vue.component('page-frame-scope', {
template: `<div>
<div id="header" style="width: 100%; height: 100px; background: pink">
{{title}}<br />
slot1 : <br />
<slot name="slot1"></slot>
</div>
<div style="width: 100%; height: 580px">
slot2 : <br />
<slot name="slot2" v-bind:sites="sites"></slot>
</div>
<div id="footer" style="width: 100%; height: 40px; background: lightblue">{{cr}}</div>
</div>`,
props: ["title", "cr"],
data: function () {
return {
sites: [
{
"name": "菜鸟教程",
"url": "www.runoob.com"
},
{
"name": "google",
"url": "www.google.com"
},
{
"name": "微博",
"url": "www.weibo.com"
}
]
}
}
});
- 引用组件时,在填充插槽的模板上使用
slot-scopt
属性获取插槽绑定的值
html
<page-frame-scope title="标题" cr="地址">
<template slot="slot1">
<input /> <button>搜索</button>
</template>
<template slot="slot2" slot-scope="res">
<table class="table table-bordered">
<tr>
<th>名称</th>
<th>网站</th>
</tr>
<tr v-for="site in res.sites">
<td>{{site.name}}</td>
<td>{{site.url}}</td>
</tr>
</table>
</template>
</page-frame-scope>
十一、axios 异步通信
11.1 axios 介绍
vue 可以实现数据的渲染,但是如何获取数据呢?
vue 本身不具备通信能力,通常结合 axios ------ 一个专注于异步通信的 js 框架来使用(Vue.js 2.0 版本推荐使用 axios 来完成 ajax 请求。)
- axios 数据通信
- vue 数据渲染
- 什么是 axios ?
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
- 菜鸟教程
axios
说明文档:https://www.runoob.com/vue2/vuejs-ajax-axios.html - 中文文档:http://www.axios-js.com/
11.2 axios 入门使用
- axios ------ 实现步骤复杂
- JQuery 笨重
- axios 简洁、高效,对 RESTful 支持良好
11.3 axios 异步请求方法
axios 提供了多种异步请求方法,实现对 RESTful 风格的支持
11.3.1 GET 格式的请求
-
axios.get(url).then(function);
-
使用 response.data 读取 JSON 数据:
javascriptaxios .get('json/json_demo.json') .then(response => (this.info = response.data.sites)) .catch(function (error) { console.log(error) })
-
-
axios.get(url,{}).then(function);
-
GET 方法传递参数格式 (使用 axios 的 get 请求传递参数,需要将参数设置在 params 下)
javascript// 直接在 URL 上添加参数 ID=12345 axios.get('/user?ID=12345') .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); // 也可以通过 params 设置参数: axios.get('/user', { params: { ID: 12345 } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
-
11.3.2 POST 格式的请求
-
axios.post(url, {}).then(function);
javascriptaxios .post("https://localhost:9098/blog/upload", { name: "张三", age: "20" }) .then(response => (this.info2 = response)) .catch(function (error) { console.log(error) })
11.3.3 自定义请求
自定义请求:自定义请求方式、请求参数、请求头、请求体(post)
javascript
axios({
url: "https://localhost:9098/blog/upload",
method: "post",
params: {
// 设置请求行传值
name: "张三",
limit: 15
},
headers: {
// 设置请求头
},
data: {
// 设置请求体 (post / put)
}
}).then(function (res) {
console.log(res)
});
11.3.4 请求方法别名
- axios.request(config)
- axios.get(url[, config])
- axios.delete(url[, config])
- axios.head(url[, config])
- axios.post(url[, data[, config]])
- axios.put(url[, data[, config]])
- axios.patch(url[, data[, config]])
当使用别名方法时,不需要在config中指定url,method和data属性。
11.4 并发请求
axios.all()、axios.spread() 两个辅助函数用于处理同时发送多个请求,可以实现在多个请求都完成后再执行一些逻辑。
处理并发请求的助手函数:
- axios.all(iterable)
- axios.spread(callback)
html
<div id="app-25">
<button type="button" @click="test">测试</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app-25",
data: {
},
methods: {
test: function () {
// 发送异步请求
axios
.all([f1(), f2()])
.then(axios.spread(function (res1, res2) {
// 两个请求都要执行完毕
console.log("所有请求都完成")
console.log(res1);
console.log(res2);
}));
}
}
});
function f1() {
console.log('调用第一个接口')
return axios.get("json/json_demo.json");
}
function f2() {
console.log('调用第二个接口')
return axios.get("json/json_demo2.json");
}
</script>
注:两个请求执行完成后,才执行 axios.spread() 中的函数,且 axios.spread() 回调函数的的返回值中的请求结果的顺序和请求的顺序一致
F12 查看控制台输出情况
11.5 箭头函数
11.5.1 axios 回调函数的参数 res
res 并不是接口返回的数据,而是表示一个响应数据:res.data 才表示接口响应的数据
11.5.2 箭头函数
html
<script type="text/javascript">
var vm = new Vue({
el: "#app-24",
data: {
songs: ""
},
methods: {
test4: function () {
// 发送异步请求
axios
.get("json/json_demo2.json")
.then((res) => {
// res 并不是接口返回的数据,而是表示一个响应数据:res.data 才表示接口响应的数据
this.songs = res.data;
console.log(res.data)
})
}
}
})
</script>
十二、 路由 router
router 是由 vue 官方提供的用于实现组件跳转的插件。
Vue Router 是 Vue.js (opens new window)官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
12.1 路由插件的引用
12.1.1 离线
12.1.2 在线 CDN
javascript
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
当你要把 Vue Router 添加进来,我们需要做的是,将组件 (components) 映射到路由 (routes),然后告诉 Vue Router 在哪里渲染它们。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
body {
padding: 0px;
margin: 0px;
}
ul {
list-style: none;
}
ul li {
display: inline;
float: left;
margin-left: 15px;
}
</style>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app-26">
<div style="width: 100%; height: 70px; background: #00BFFF;">
<table>
<tr>
<td>
<img src="img/1.jpg" height="70" style="margin-left: 100px;" />
</td>
<td>
<ul>
<li>
<router-link to="/index">首页</router-link>
</li>
<li>
<router-link to="/java">Java</router-link>
</li>
<li>
<router-link to="/python">Python</router-link>
</li>
<li>
<router-link to="/vue">Vue</router-link>
</li>
</ul>
</td>
</tr>
</table>
</div>
<div style="width: 100%; height: 680px; background: salmon;">
<router-view></router-view>
</div>
</div>
<script type="text/javascript">
<!-- vue 的路由旨在为单页面应用开发提供便携 -->
// 定义链接跳转的模板(组件)
const t1 = {
template: `<p align="center">index</p>`
};
const t2 = {
template: `<p align="center" >Java</p>`
};
const t3 = {
template: `<p align="center">Python</p>`
};
const t4 = {
template: `<p align="center">Vue</p>`
};
const my_router = new VueRouter({
routes: [
{
path: '/index',
component: t1
},
{
path: '/java',
component: t2
},
{
path: '/python',
component: t3
},
{
path: '/vue',
component: t4
}
]
});
var vm = new Vue({
el: '#app-26',
router: my_router
});
</script>
</body>
</html>
点击链接,根据路由,跳转并显示对应的组件模板
12.2 动态路由匹配
12.2.1 通配符
*
可以匹配任意路径
例如:
/user-*
匹配所有以user-
开头的任意路径/*
匹配所有路径
javascript
const my_router = new VueRouter({
routes: [
{
path: '/user-*',
component: t4
},
{
path: '/*',
component: t5
}
]
});
注意:如果使用通配符定义路径,需要注意路由声明的顺序
12.2.2 路由参数
/index/:id
可以匹配/index/
开头的路径
html
<div id="app-27">
<router-link to="/index/101">首页</router-link>
<router-view></router-view>
</div>
<script type="text/javascript">
<!-- vue 的路由旨在为单页面应用开发提供便携 -->
// 1. 定义链接跳转的模板(组件)
const t1 = {
template: `<p align="center">index {{$route.params.id}}</p>`
};
// 2. 定义路由
const my_router = new VueRouter({
routes: [
{
path: '/index/:id',
component: t1
}
]
});
// 3. 引用路由
var vm = new Vue({
el: '#app-27',
router: my_router
});
</script>
12.2.3 优先级
如果一个路径匹配了多个路由,则按照路由的配置顺序:路由定义的越早优先级就越高。
12.3 嵌套路由
在一级路由的组件中显示二级路由
html
<div id="app-28">
<div style="width: 100%; height: 20px; background: #00BFFF;">
<router-link to="/index">首页</router-link>
<router-link to="/index/t2">首页-t2</router-link>
<router-link to="/index/t3">首页-t3</router-link>
<router-view></router-view>
</div>
</div>
<script type="text/javascript">
<!-- vue 的路由旨在为单页面应用开发提供便携 -->
// 1. 定义链接跳转的模板(组件)
const t1 = {
template: `<div style="width: 400px; height: 200px; border: blue 1px solid" >
index
<hr />
<router-view></router-view>
</div>`
};
const t2 = {
template: `<div>t2</div>`
};
const t3 = {
template: `<div>t3</div>`
};
// 2. 定义路由
const my_router = new VueRouter({
routes: [
{
path: '/index',
component: t1,
children: [
{
path: "t2",
component: t2
},
{
path: "t3",
component: t3
}
]
},
]
});
// 3. 引用路由
var vm = new Vue({
el: '#app-28',
router: my_router
});
</script>
12.4 编程式导航
12.4.1 push()
html
<div id="app-29">
<div style="width: 100%; height: 20px; background: #00BFFF;">
<!-- <router-link to="/index">首页</router-link> -->
<button type="button" @click="test">首页按钮</button>
<router-view></router-view>
</div>
</div>
<script type="text/javascript">
<!-- vue 的路由旨在为单页面应用开发提供便携 -->
// 1. 定义链接跳转的模板(组件)
const t1 = {
template: `<div style="width: 400px; height: 200px; border: blue 1px solid" >
index
</div>`
};
// 2. 定义路由
const my_router = new VueRouter({
routes: [
{
path: '/index',
component: t1
}
]
});
// 3. 引用路由
var vm = new Vue({
el: '#app-29',
router: my_router,
methods: {
test: function () {
// js 代码实现路由跳转,编程式导航
my_router.push("/index");
}
}
});
</script>
12.4.2 push() 参数
javascript
// 1. 字符串
my_router.push("/index");
// 2. 对象
my_router.push({path: "/index"});
// 3. 命名的路由 name 参数指的是定义路由时指定的名字
my_router.push({name: "r1", params: {id: 101}});
// 4. URL 传值,相当于 /index?id=101
my_router.push({path: "/index", query: {id: 101}});
12.4.3 replace()
功能与 push() 一致,区别在于 replace() 不会向 history 添加新的浏览记录
12.4.4 go()
参数为一个整数,表示在浏览器历史记录中前进或后退多少步 相当于
windows.history.go(-1)
的作用
12.5 命名路由
命名路由:在定义路由的时候可以给路由指定 name,我们在进行路由导航时可以通过路由的名字导航
html
<div id="app-30">
<div style="width: 100%; height: 20px; background: #00BFFF;">
<input type="text" v-model="rName" />
<router-link :to="{name: rName}">t1</router-link>
<button type="button" @click="test">首页按钮2</button>
<router-view></router-view>
</div>
</div>
<script type="text/javascript">
<!-- vue 的路由旨在为单页面应用开发提供便携 -->
// 1. 定义链接跳转的模板(组件)
const t1 = {
template: `<div style="width: 400px; height: 200px; border: blue 1px solid" >
t1
</div>`
};
const t2 = {
template: `<div style="width: 400px; height: 200px; border: blue 1px solid" >
t2
</div>`
};
// 2. 定义路由
const my_router = new VueRouter({
routes: [
{
path: '/index',
name: "r1",
component: t1
},
{
path: '/index2',
name: "r2",
component: t2
}
]
});
// 3. 引用路由
var vm = new Vue({
el: '#app-30',
data: {
rName: "r1"
},
router: my_router,
methods: {
test: function () {
// js 代码实现路由跳转,编程式导航
my_router.push({name: vm.rName});
}
}
});
</script>
12.6 命名视图
html
<div id="app-31">
<div style="width: 100%; height: 20px; background: #00BFFF;">
<router-link to="/index">t1</router-link>
<router-link to="/index2">t2</router-link>
<!-- 路由视图 -->
<!-- 如果在 HTML 中有一个以上的路由视图 router-view,需要给 router-view 指定 name,
在路由中需要使用 components 映射多个组件根据 name 设置组件与 router-view 的绑定关系 -->
<router-view name="v1"></router-view>
<router-view name="v2"></router-view>
</div>
</div>
<script type="text/javascript">
<!-- vue 的路由旨在为单页面应用开发提供便携 -->
// 1. 定义链接跳转的模板(组件)
const t11 = {
template: `<div style="width: 400px; height: 200px; border: blue 1px solid" >
t11
</div>`
};
const t12 = {
template: `<div style="width: 400px; height: 200px; border: red 1px solid" >
t12
</div>`
};
const t21 = {
template: `<div style="width: 400px; height: 200px; border: yellow 1px solid" >
t21
</div>`
};
const t22 = {
template: `<div style="width: 400px; height: 200px; border: wheat 1px solid" >
t22
</div>`
};
// 2. 定义路由
const my_router = new VueRouter({
routes: [
{
path: '/index',
components: {
v1: t11,
v2: t12
}
},
{
path: '/index2',
components: {
v1: t21,
v2: t22
}
}
]
});
// 3. 引用路由
var vm = new Vue({
el: '#app-31',
data: {
rName: "r1"
},
router: my_router,
methods: {
test: function () {
// js 代码实现路由跳转,编程式导航
my_router.push({name: vm.rName});
}
}
});
</script>
12.7 重定向
12.7.1 重定向
访问 /index
, 重定向到 /login
html
<div id="app-32">
<router-link to="/login">登录</router-link>
<router-link to="/index">首页</router-link>
<router-view></router-view>
</div>
<script type="text/javascript">
<!-- vue 的路由旨在为单页面应用开发提供便携 -->
// 1. 定义链接跳转的模板(组件)
const t1 = {
template: `<div style="width: 400px; height: 200px; border: blue 1px solid" >
登录
</div>`
};
// 2. 定义路由
const my_router = new VueRouter({
routes: [
{
path: '/login',
component: t1
},
{
path: '/index',
redirect: "/login"
}
]
});
// 3. 引用路由
var vm = new Vue({
el: '#app-32',
router: my_router
});
</script>
- 根据路由命名重定向
javascript
// 2. 定义路由
const my_router = new VueRouter({
routes: [
{
path: '/login',
name: "r1",
component: t1
},
{
path: '/index',
// 根据路由路径重定向
// redirect: "/login"
// 根据路由命名重定向
redirect: {name: "r1"}
}
]
});
12.7.2 路由别名
html
<div id="app-32">
<router-link to="/login">登录</router-link><br />
<router-link to="/alias-login">(别名)-登录</router-link> <br />
<router-view></router-view>
</div>
<script type="text/javascript">
const t1 = {
template: `<div style="width: 400px; height: 200px; border: blue 1px solid" >
登录
</div>`
};
// 2. 定义路由
const my_router = new VueRouter({
routes: [
{
path: '/login',
alias: "/alias-login",
component: t1
}
]
});
// 3. 引用路由
var vm = new Vue({
el: '#app-32',
router: my_router
});
</script>
12.8 路由组件传参
可以通过 /url/:attr
方式实现通过路由传值给组件
html
<div id="app-33">
<router-link to="/login/101">登录</router-link><br />
<router-view></router-view>
</div>
<script type="text/javascript">
const t1 = {
template: `<div style="width: 400px; height: 200px; border: blue 1px solid" >
登录:{{$route.params.id}}
</div>`
};
// 2. 定义路由
const my_router = new VueRouter({
routes: [
{
path: '/login/:id',
component: t1
}
]
});
// 3. 引用路由
var vm = new Vue({
el: '#app-33',
router: my_router
});
</script>
通过 props
传参
html
<div id="app-33">
<router-link to="/index/102">首页t2</router-link><br />
<router-view></router-view>
</div>
<script type="text/javascript">
const t2 = {
props:["id"],
template: `<div style="width: 400px; height: 200px; border: blue 1px solid" >
首页t2:{{id}}
</div>`
};
const my_router = new VueRouter({
routes: [
{
path: '/index/:id',
component: t2,
props: true
}
]
});
// 3. 引用路由
var vm = new Vue({
el: '#app-33',
router: my_router
});
</script>