Vue2.0
学习视频地址
文章目录
- Vue2.0
- 1.vue 简介
- 1.1.什么是vue
- 1.2.vue的两个特性
- 1.2.1.数据驱动视图
- 1.2.2.双向数据绑定
- 1.3.MVVM概述
- 1.4.MVVM工作原理
- 2.vue的基本使用
- 2.1.基本使用步骤
- 2.2.基本代码与MVVM的对应关系
- 3.vue的调试工具
- 3.1.安装vue-devtool调试工具
- 3.2.配置Chrome浏览器中的vue-devtools
- 3.3.使用vue-devtools调试vue页面
- 4.vue的指令与过滤器
- 4.1.指令的概念
- 4.2.内容渲染指令
- 4.2.1.v-text
- 4.2.2.{{ }}
- 4.2.3.v-html
- 4.3.属性绑定指令 v-bind
- 4.4.使用 Javascript 表达式
- 4.5.事件绑定指令
- 4.5.1.事件绑定的简写形式
- 4.5.2.绑定事件并传参
- 4.5.3.事件参数对象
- 4.5.4.$event
- 4.5.5.事件修饰符
- 4.5.6.按键修饰符
- 4.6.双向绑定指令
- 4.6.1.v-model 指令的修饰符
- 4.7.条件渲染指令
- 4.7.1.v-if
- 4.7.2.v-show
- 4.7.3.v-if 和 v-show 的区别
- 4.7.4.v-else
- 4.7.5.v-else-if
- 4.8.列表渲染指令
- 4.8.1.v-for 中的索引
- 4.8.2.使用 key 维护列表的状态
- 4.8.3.key 的注意事项
- 4.9.过滤器
- 4.9.1.概述
- 4.9.2.定义过滤器
- 4.9.3.私有过滤器和全局过滤器
- 4.9.4.连续调用多个过滤器
- 4.9.5.过滤器传参
- 5.品牌列表案例
- 6.侦听器
- 6.1.什么是 watch 侦听器
- 6.2.使用 watch 检测用户名是否可用
- 6.3.immediate 选项
- 6.4.deep 选项
- 6.5.监听对象单个属性的变化
- 7.计算属性
- 7.1.什么是计算属性
- 7.2.计算属性的特点
- 8.axios
- 8.1.axios 的基本使用
- 8.1.1.发起 GET 请求:
- 8.1.2.发起 POST 请求:
- 8.1.3.基于axios.get和axios.post发起请求
- 9.vue-cli
- 9.1.什么是 vue-cli
- 9.2.安装和使用
- 9.3.vue 项目的运行流程
- 9.4.vue 项目中 src 目录的构成
- 10.vue 组件
- 10.1.什么是组件化开发
- 10.2.vue 中的组件化开发
- 10.3.vue 组件的三个组成部分
- 10.3.1.template
- 10.3.2.script
- 10.3.3.style
- 10.4.组件之间的父子关系
- 10.4.1.使用组件的三个步骤
- 10.4.2.通过 components 注册的是私有子组件
- 10.4.3.注册全局组件
- 10.5.组件的 props
- 10.5.1.props 是只读的
- 10.5.2.props 的 default 默认值
- 10.5.3.props 的 type 值类型
- 10.5.4.props 的 required 必填项
- 10.6.组件之间的样式冲突问题
- 10.6.1.解决组件样式冲突的问题
- 10.6.2.style 节点的 scoped 属性
- 10.6.3./deep/ 样式穿透
- 11.组件的生命周期
- 11.1.生命周期 & 生命周期函数
- 11.2.组件生命周期函数的分类
- 11.3.生命周期图示
- 12.组件之间的数据共享
- 12.1.组件之间的关系
- 12.2.父子组件之间的数据共享
- 12.2.1.父组件向子组件共享数据
- 12.2.2.子组件向父组件共享数据
- 12.2.3.兄弟组件之间的数据共享
- 12.2.1.1.EventBus 的使用步骤
- 13.ref 引用
- 13.1.什么是 ref 引用
- 13.2.使用 ref 引用 DOM 元素
- 13.3.使用 ref 引用组件实例
- 13.4.控制文本框和按钮的按需切换
- 13.4.1.this.$nextTick(cb) 方法
- 14.购物车案例
- 15.动态组件
- 15.1.什么是动态组件
- 15.2.如何实现动态组件渲染
- 15.3.动态切换组件的展示与隐藏
- 15.4.keep-alive
- 15.4.1.使用 keep-alive 保持状态
- 15.4.2.keep-alive 对应的生命周期函数
- 15.4.3.keep-alive 的 include 和 exclude 属性
- 15.5.了解组件注册名称和组件声明时name的区别
- 16.插槽
- 16.1.什么是插槽
- 16.2.体验插槽的基础用法
- 16.3.v-slot
- 16.4.后备内容
- 16.5.具名插槽
- 16.6.为具名插槽提供内容
- 16.7.作用域插槽的解构赋值
- 17.自定义指令
- 17.1.什么是自定义指令
- 17.2.自定义指令的分类
- 17.2.1.私有自定义指令
- 17.2.1.1.通过 binding 获取指令的参数值
- 17.2.1.2.update 函数
- 17.2.1.3.函数简写
- 17.2.2.全局自定义指令
- 18.路由
- 18.1.前端路由的概念与原理
- 18.1.1.什么是路由
- 18.1.2.SPA 与前端路由
- 18.1.3.什么是前端路由
- 18.1.4.前端路由的工作方式
- 18.1.4.实现简易的前端路由
- 18.2.vue-router 的基本使用
- 18.2.1.什么是 vue-router
- 18.2.2.vue-router 安装和配置的步骤
- 18.2.2.1. 在项目中安装 vue-router
- 18.2.2.2.创建路由模块
- 18.2.2.3.导入并挂载路由模块
- 18.2.2.4.声明路由链接和占位符
- 18.2.2.5.声明路由的匹配规则
- 18.2.2.6.路由重定向 redirect
- 18.3.vue-router 的常见用法
- 18.3.1.嵌套路由
- 18.3.1.1.声明子路由链接和子路由占位符
- 18.3.1.2.通过 children 属性声明子路由规则
- 18.3.1.3.默认子路由
- 18.3.2.动态路由
- 18.3.2.1. 动态路由匹配
- 18.3.2.2.动态路由的概念
- 18.3.2.3.$route.params 参数对象
- 18.3.2.4.使用 props 接收路由参数
- 18.3.3.声明式导航 & 编程式导航
- 18.3.3.1.vue-router 中的编程式导航 API
- 18.3.4.导航守卫
- 18.3.4.1.全局前置守卫
- 18.3.4.2.守卫方法的 3 个形参
- 18.3.4.3.next 函数的 3 种调用方式
- 18.3.4.*.控制后台主页的访问权限
- 18.4.后台管理案例
- 18.3.vue-router 的常见用法
- 18.3.1.嵌套路由
- 18.3.1.1.声明子路由链接和子路由占位符
- 18.3.1.2.通过 children 属性声明子路由规则
- 18.3.1.3.默认子路由
- 18.3.2.动态路由
- 18.3.2.1. 动态路由匹配
- 18.3.2.2.动态路由的概念
- 18.3.2.3.$route.params 参数对象
- 18.3.2.4.使用 props 接收路由参数
- 18.3.3.声明式导航 & 编程式导航
- 18.3.3.1.vue-router 中的编程式导航 API
- 18.3.4.导航守卫
- 18.3.4.1.全局前置守卫
- 18.3.4.2.守卫方法的 3 个形参
- 18.3.4.3.next 函数的 3 种调用方式
- 18.3.4.*.控制后台主页的访问权限
- 18.4.后台管理案例
1.vue 简介
1.1.什么是vue
官方给出的概念:Vue (读音 /vju:/,类似于 view)是一套用于构建用户界面
的前端框架
- 构建用户界面
- 用 vue 往 html 页面中填充数据,非常的方便
- 框架
- 框架是一套现成的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能!
- 要学习 vue,就是在学习 vue 框架中规定的用法!
- vue 的指令、组件(是对 UI 结构的复用)、路由、Vuex、vue 组件库
- 只有把上面老师罗列的内容掌握以后,才有开发 vue 项目的能力!
1.2.vue的两个特性
1.2.1.数据驱动视图
- 数据的变化
会驱动视图
自动更新 - 好处:程序员只管把数据维护好,那么页面结构会被 vue 自动渲染出来!
在使用了 vue 的页面中,vue 会监听数据的变化,从而自动重新渲染页面的结构。示意图如下:
好处:当页面数据发生变化时,页面会自动重新渲染!
注意:数据驱动视图是单向的数据绑定。
1.2.2.双向数据绑定
在网页中,form 表单负责采集数据,Ajax 负责
提交数据
。
- js 数据的变化,会被自动渲染到页面上
- 页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中
在填写表单时,双向数据绑定可以辅助开发者在不操作 DOM 的前提下,自动把用户填写的内容同步到数据源
中。示意图如下:
好处:开发者不再需要手动操作 DOM 元素,来获取表单元素最新的值!
注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)
1.3.MVVM概述
MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。MVVM 指的是 Model、View 和 ViewModel,
它把每个 HTML 页面都拆分成了这三个部分,如图所示:
1.4.MVVM工作原理
ViewModel 作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起。
当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构
当表单元素的值发生变化时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中
2.vue的基本使用
2.1.基本使用步骤
1.导入 vue.js 的 script 脚本文件
2.在页面中声明一个将要被 vue 所控制的 DOM 区域
3.创建 vm 实例对象(vue 实例对象)
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
{{ username }}
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el : "#app",
// data 对象就是要渲染到页面上的数据
data : {
username : 'k'
}
})
</script>
</body>
</html>
2.2.基本代码与MVVM的对应关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FY37H3wf-1671884043145)(C:\Users\30666\AppData\Roaming\Typora\typora-user-images\image-20221216171008763.png)]
3.vue的调试工具
3.1.安装vue-devtool调试工具
vue 官方提供的 vue-devtools 调试工具,能够方便开发者对 vue 项目进行调试与开发。
Chrome 浏览器在线安装 vue-devtools :
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd
FireFox 浏览器在线安装 vue-devtools :
https://addons.mozilla.org/zh-CN/firefox/addon/vue-js-devtools/
3.2.配置Chrome浏览器中的vue-devtools
点击 Chrome 浏览器右上角的 按钮,选择更多工具 -> 扩展程序 -> Vue.js devtools 详细信息,并勾选如下
的两个选项:
注意:修改完配置项,须重启浏览器才能生效!
3.3.使用vue-devtools调试vue页面
4.vue的指令与过滤器
4.1.指令的概念
指令(Directives)是 vue 为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构。
vue 中的指令按照不同的用途可以分为如下 6 大类:
- 内容渲染指令
- 属性绑定指令
- 事件绑定指令
- 双向绑定指令
- 条件渲染指令
- 列表渲染指令
注意:指令是 vue 开发中最基础、最常用、最简单的知识点。
4.2.内容渲染指令
内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。常用的内容渲染指令有如下 3 个:
- v-text
- {{ }}
- v-html
4.2.1.v-text
代码示例:
<!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>
</head>
<body>
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<p v-text="username"></p>
<p v-text="gender">性别</p>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
username: 'k',
gender : '男'
}
})
</script>
</body>
</html>
注意:v-text 指令会覆盖元素内默认的值
4.2.2.{{ }}
vue 提供的 {{ }} 语法,专门用来解决 v-text 会覆盖默认文本内容的问题。这种 {{ }} 语法的专业名称是插值表达式(英文名为:Mustache)。
代码示例:
<!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>
</head>
<body>
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<p v-text="username"></p>
<p v-text="gender">性别</p>
<hr>
<p>姓名:{{ username }}</p>
<p>性别:{{ gender }}</p>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
username: 'k',
gender : '男'
}
})
</script>
</body>
</html>
注意:相对于 v-text 指令来说,插值表达式在开发中更常用一些!因为它不会覆盖元素中默认的文本内容。
4.2.3.v-html
v-text 指令和插值表达式只能渲染纯文本内容。如果要把包含 HTML 标签的字符串渲染为页面的 HTML 元素,
则需要用到 v-html 这个指令
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<p v-text="username"></p>
<p v-text="gender">性别</p>
<hr>
<p>姓名:{{ username }}</p>
<p>性别:{{ gender }}</p>
<hr>
<div v-text="info"></div>
<div>{{ info }}</div>
<div v-html="info"></div>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
username: 'k',
gender : '男',
info : '<h1 style="color:red; font-weight:bold;">hello Vue.js</h1>'
}
})
</script>
</body>
</html>
4.3.属性绑定指令 v-bind
注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!
如果需要为元素的属性动态绑定属性值,则需要用到 v-bind 属性绑定指令。
属性绑定指令的简写形式
由于 v-bind 指令在开发中使用频率非常高,因此,vue 官方为其提供了简写形式(简写为英文的 : )
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<input type="text" v-bind:placeholder="tips">
<hr>
<img v-bind:src="photo" alt="" style="width:150px;">
<hr>
<!-- vue 规定 v-bind 执行可以简写为 : -->
<img :src="photo" alt="" style="width:150px;">
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
tips : '请输入用户名',
photo : 'https://img1.baidu.com/it/u=1161435337,124885559&fm=253&fmt=auto&app=138&f=JPEG?w=550&h=309.png'
}
})
</script>
</body>
</html>
4.4.使用 Javascript 表达式
在 vue 提供的模板渲染语法中,除了支持绑定简单的数据值之外,还支持 Javascript 表达式的运算。
代码示例:
<!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>
</head>
<body>
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<!-- 使用 Javascript 表达式 -->
<hr>
<div>1+2的结果是:{{ 1+ 2 }}</div>
<div>{{ tips }} 反转的结果是: {{ tips.split('').reverse().join('')}}</div>
<div :title="'box' + index">这是一个 div </div>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
tips : '请输入用户名',
index : 3
}
})
</script>
</body>
</html>
4.5.事件绑定指令
vue 提供了 v-on 事件绑定指令,用来辅助程序员为 DOM 元素绑定事件监听。语法格式如下:
<p>count 的值是: {{ count }}</p>
<button v-on:click="addCount">+1</button>
<button v-on:click="subCount">-1</button>
注意:原生 DOM 对象有 onclick、oninput、onkeyup 等原生事件,替换为 vue 的事件绑定形式后,
分别为:v-on:click、v-on:input、v-on:keyup
通过 v-on 绑定的事件处理函数,需要在 methods 节点
中进行声明:
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
count : 0
},
// methods 的作用就是定义事件的处理函数
methods: {
addCount () {
// this 表示当前 new 出来的 vm 实例对象
// 通过 this 可以访问到 data 中数据
this.count++;
},
subCount(){
this.count--;
}
},
})
</script>
4.5.1.事件绑定的简写形式
代码示例:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<p>count 的值是: {{ count }}</p>
<button v-on:click="addCount">+1</button>
<button v-on:click="subCount">-1</button>
<hr>
<!-- v-on: 指令可以被简写为 @ -->
<button @click="addCount">+1</button>
<button @click="subCount">-1</button>
</div>
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<p>count 的值是: {{ count }}</p>
<button v-on:click="addCount">+1</button>
<button v-on:click="subCount">-1</button>
<hr>
<!-- v-on: 指令可以被简写为 @ -->
<button @click="addCount">+1</button>
<button @click="subCount">-1</button>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
count : 0
},
// methods 的作用就是定义事件的处理函数
methods: {
addCount () {
// this 表示当前 new 出来的 vm 实例对象
// 通过 this 可以访问到 data 中数据
this.count++;
},
subCount(){
this.count--;
}
},
})
</script>
</body>
</html>
4.5.2.绑定事件并传参
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<p>count 的值是: {{ count }}</p>
<!-- 在绑定事件处理函数的时候,可以使用()传递参数 -->
<button v-on:click="addCount(10)">+10</button>
<button v-on:click="subCount">-1</button>
<hr>
<!-- v-on: 指令可以被简写为 @ -->
<button @click="addCount(10)">+1</button>
<button @click="subCount">-1</button>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
count : 0
},
// methods 的作用就是定义事件的处理函数
methods: {
addCount (n) {
// this 表示当前 new 出来的 vm 实例对象
// 通过 this 可以访问到 data 中数据
this.count+=n;
},
subCount(){
this.count--;
}
},
})
</script>
</body>
</html>
4.5.3.事件参数对象
在原生的 DOM 事件绑定中,可以在事件处理函数的形参处,接收事件参数对象 event。同理,在 v-on 指令
(简写为 @ )所绑定的事件处理函数中,同样可以接收到事件参数对象 event,示例代码如下:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<p>count 的值是: {{ count }}</p>
<!-- 如果 count 是偶数,则背景变成红色,否则去掉背景颜色 -->
<button @click="addCount">+n</button>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
count: 0
},
// methods 的作用就是定义事件的处理函数
methods: {
addCount(e) {
this.count += 1;
// 判断 this.count 的值是否为偶数
if(this.count%2==0){
e.target.style.backgroundColor = 'red';
}else{
e.target.style.backgroundColor = '';
}
},
},
})
</script>
4.5.4.$event
e v e n t 是 v u e 提供的特殊变量,用来表示原生的事件参数对象 e v e n t 。 event 是 vue 提供的特殊变量,用来表示原生的事件参数对象 event。 event是vue提供的特殊变量,用来表示原生的事件参数对象event。event 可以解决事件参数对象 event 被覆盖的问题。示例用法如下:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<p>count 的值是: {{ count }}</p>
<!-- 如果 count 是偶数,则背景变成红色,否则去掉背景颜色 -->
<!-- vue 提供了内置变量,$event ,它就是原生 DOM 的事件对象 e-->
<button @click="addCount(1,$event)">+n</button>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
count: 0
},
// methods 的作用就是定义事件的处理函数
methods: {
addCount(n, e) {
this.count += n;
// 判断 this.count 的值是否为偶数
if(this.count%2==0){
e.target.style.backgroundColor = 'red';
}else{
e.target.style.backgroundColor = '';
}
},
},
})
</script>
</body>
4.5.5.事件修饰符
在事件处理函数中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。因此,vue 提供了事件修饰符的概念,来辅助程序员更方便的对事件的触发进行控制。常用的 5 个事件修饰符如下:
代码示例:
<!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>
</head>
<body>
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<a href=" http://www.baidu.com" @click.prevent="show">跳转到百度首页</a>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {},
methods: {
show(){
console.log('被点击了');
}
},
})
</script>
</body>
</html>
阻止事件冒泡代码示例:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<!-- 阻止事件冒泡 -->
<div style="height:150px;background-color:orange;padding-left:100px;line-height:150px" @click="divHandler">
<button @click.stop="btnHandler">按钮</button>
</div>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {},
methods: {
btnHandler(){
console.log('btnHandler');
},
divHandler(){
console.log('divHandler');
}
},
})
</script>
4.5.6.按键修饰符
在监听键盘事件时,我们经常需要判断详细的按键。此时,可以为键盘相关的事件添加按键修饰符。
代码示例:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<input type="text" @keyup.esc="clearInput" @keyup.enter="commitAjax">
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {},
methods: {
clearInput(e){
e.target.value = '';
},
commitAjax(){
console.log('commitAjax');
}
},
})
</script>
4.6.双向绑定指令
vue 提供了 v-model 双向数据绑定指令,用来辅助开发者在不操作 DOM 的前提下,快速获取表单的数据。
代码示例:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<p>用户名: {{ username }}</p>
<input type="text" v-model="username">
<hr>
<!-- : 是 v-bind 的简写 -->
<input type="text" :value="username">
<hr>
<select v-model="city">
<option value="">请选择城市...</option>
<option value="1">广州</option>
<option value="2">深圳</option>
<option value="3">汕尾</option>
</select>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
username : 'k',
city : ''
}
})
</script>
4.6.1.v-model 指令的修饰符
为了方便对用户输入的内容进行处理,vue 为 v-model 指令提供了 3 个修饰符,分别是:
代码示例:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<input type="text" v-model.number="n1"> + <input type="text" v-model.number="n2"> = <span>{{ n1+n2 }}</span>
<hr>
<input type="text" v-model.trim="username">
<button @click="showUserName">获取用户名</button>
<hr>
<input type="text" v-model.lazy="username">
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
username : 'k',
n1 : 1,
n2 : 2
},
methods: {
showUserName(){
console.log(`用户名是:"${this.username}"`);
}
},
})
</script>
4.7.条件渲染指令
条件渲染指令用来辅助开发者按需控制 DOM 的显示与隐藏。条件渲染指令有如下两个,分别是:
v-if、v-show。
4.7.1.v-if
4.7.2.v-show
代码示例:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
{{ username }}
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el : "#app",
// data 对象就是要渲染到页面上的数据
data : {
username : 'k'
}
})
</script>
4.7.3.v-if 和 v-show 的区别
实现原理不同:
- v-if 指令会动态地创建或移除 DOM 元素,从而控制元素在页面上的显示与隐藏;
- v-show 指令会动态为元素添加或移除 style=“display: none;” 样式,从而控制元素的显示与隐藏;
性能消耗不同:
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此:
- 如果需要非常频繁地切换,则使用 v-show 较好
- 如果在运行时条件很少改变,则使用 v-if 较好
4.7.4.v-else
4.7.5.v-else-if
v-else-if 指令,顾名思义,充当 v-if 的“else-if 块”,可以连续使用:
代码示例:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<input type="text" v-model="type">请输入成绩等级(A/B/C/D)</input>
<div v-if="type === 'A'">优秀</div>
<div v-else-if="type === 'B'">良好</div>
<div v-else-if="type === 'C'">一般</div>
<div v-else>差</div>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
type : 'A'
},
})
</script>
注意:v-else 指令必须配合 v-if 指令一起使用,否则它将不会被识别!
注意:v-else-if 指令必须配合 v-if 指令一起使用,否则它将不会被识别!
4.8.列表渲染指令
vue 提供了 v-for 列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构。v-for 指令需要使
用 item in items 形式的特殊语法,其中:
- items 是待循环的数组
- item 是被循环的每一项
代码示例:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<table class="table table-bordered table-hover table-striped">
<thead>
<th>id</th>
<th>姓名</th>
</thead>
<tbody>
<tr v-for="item in list">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
list : [
{ id: 1, name: 'k' },
{ id: 2, name: 'kk' },
{ id: 3, name: 'kkk' },
]
}
})
</script>
4.8.1.v-for 中的索引
v-for 指令还支持一个可选的第二个参数,即当前项的索引。语法格式为 (item, index) in items。
代码示例:
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<table class="table table-bordered table-hover table-striped">
<thead>
<th>索引</th>
<th>id</th>
<th>姓名</th>
</thead>
<tbody>
<tr v-for="(item,index) in list">
<td>{{ index }}</td>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
list : [
{ id: 1, name: 'k' },
{ id: 2, name: 'kk' },
{ id: 3, name: 'kkk' },
]
}
})
</script>
注意:v-for 指令中的 item 项和 index 索引都是形参,可以根据需要进行重命名。例如 (user, i) in userlist
4.8.2.使用 key 维护列表的状态
当列表的数据变化时,默认情况下,vue 会尽可能的复用已存在的 DOM 元素,从而提升渲染的性能。但这种
默认的性能优化策略,会导致有状态的列表无法被正确更新。
为了给 vue 一个提示,以便它能跟踪每个节点的身份,从而在保证有状态的列表被正确更新的前提下,提升渲
染的性能。此时,需要为每项提供一个唯一的 key 属性:
4.8.3.key 的注意事项
- key 的值只能是字符串或数字类型
- key 的值必须具有唯一性(即:key 的值不能重复)
- 建议把数据项 id 属性的值作为 key 的值(因为 id 属性的值具有唯一性)
- 使用 index 的值当作 key 的值没有任何意义(因为 index 的值不具有唯一性)
- 建议使用 v-for 指令时一定要指定 key 的值(既提升性能、又防止列表状态紊乱)
<!-- 希望 vue 能够控制下面的这个 div ,帮我们能够把数据填充到 div 内部 -->
<div id="app">
<table class="table table-bordered table-hover table-striped">
<thead>
<th>索引</th>
<th>id</th>
<th>姓名</th>
</thead>
<tbody>
<!-- 官方建议:只要用到了 v-for 指令,那么一定要绑定一个 :key 属性 -->
<!-- 而且,尽量把 id 作为 key 的值 -->
<!-- 官方对 key 的值类型是有要求的,字符串或数字类型 -->
<!-- key 的值是千万不能重复的,否则会终端报错:Duplicate keys detected -->
<tr v-for="(item,index) in list" :key="item.id">
<td>{{ index }}</td>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 1.导入 vue 的库文件 ,在 window 全局就有了 vue 这个构造函数-->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
// 创建 vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前的 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: "#app",
// data 对象就是要渲染到页面上的数据
data: {
list : [
{ id: 1, name: 'k' },
{ id: 2, name: 'kk' },
{ id: 3, name: 'kkk' },
]
}
})
</script>
4.9.过滤器
4.9.1.概述
过滤器(Filters)是 vue 为开发者提供的功能,常用于文本的格式化。过滤器可以用在两个地方:插值表达式
和 v-bind 属性绑定。
过滤器应该被添加在 JavaScript 表达式的尾部,由“管道符”进行调用
4.9.2.定义过滤器
在创建 vue 实例期间,可以在 filters 节点中定义过滤器。
代码示例:
<div id="app">
<p>message 的值是:{{ message | capi }}</p>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'hello Vue.js'
},
// 过滤器函数必须被顶到 filters 节点下
// 过滤器本质是函数
filters: {
// 注意:过滤器函数形参中的 val 永远都是管道符前边的那个值
capi(val){
const first = val.charAt(0).toUpperCase();
// 字符串的 slice 方法,可以截取字符串从指定索引往后截取
const other = val.slice(1)
// 强调:过滤器中,一定要有一个返回值
return first+other;
}
}
})
</script>
4.9.3.私有过滤器和全局过滤器
在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。
如果希望在多个 vue 实例之间共享过滤器,则可以按照如下的格式定义全局过滤器
。
代码示例:
<div id="app">
<p>message 的值是:{{ message | capi }}</p>
</div>
<div id="app2">
<p>message 的值是:{{ message | capi }}</p>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
// 使用 Vue.filter() 定义全局过滤器
Vue.filter('capi', function (str) {
const first = str.charAt(0).toUpperCase();
const other = str.slice(1);
return first + other+'~~~';
})
const vm = new Vue({
el: '#app',
data: {
message: 'hello Vue.js'
},
// 过滤器函数必须被顶到 filters 节点下
// 过滤器本质是函数
filters: {
// 注意:过滤器函数形参中的 val 永远都是管道符前边的那个值
capi(val) {
const first = val.charAt(0).toUpperCase();
// 字符串的 slice 方法,可以截取字符串从指定索引往后截取
const other = val.slice(1)
// 强调:过滤器中,一定要有一个返回值
return first + other;
}
}
})
// --------------------------------
const vm2 = new Vue({
el: '#app2',
data: {
message: 'cmk'
},
})
</script>
4.9.4.连续调用多个过滤器
过滤器可以串联地进行调用。
4.9.5.过滤器传参
过滤器的本质是 JavaScript 函数,因此可以接收参数
5.品牌列表案例
6.侦听器
6.1.什么是 watch 侦听器
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。
代码示例:
<div id="app">
<input type="text" v-model="username">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
username: ''
},
// 所有的侦听器都应该被定义到 watch 节点下
watch: {
// 侦听器本质上是一个函数,要监听哪个数据的变化,就把数据名作为方法即可
// 新值在前,旧值在后
username(newVal, oldVal) {
console.log('监听到了username 值的变化!', newVal, oldVal);
}
}
})
</script>
6.2.使用 watch 检测用户名是否可用
监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用
代码示例:
<div id="app">
<input type="text" v-model="username">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
username: ''
},
// 所有的侦听器都应该被定义到 watch 节点下
watch: {
// 侦听器本质上是一个函数,要监听哪个数据的变化,就把数据名作为方法即可
// 新值在前,旧值在后
username(newVal) {
if(newVal===''){
return ;
}
// 1.调用 JQ 中的 ajax 发起请求,判断 newVal 是否被占用
$.get('http://localhost:9090/user/EixtUserName/'+newVal,function(result){
console.log(result);
})
}
}
})
</script>
6.3.immediate 选项
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使
用 immediate
选项。
代码示例:
<div id="app">
<input type="text" v-model="username">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
username: 'admin'
},
// 所有的侦听器都应该被定义到 watch 节点下
watch: {
// 定义对象格式的侦听器
username: {
handler(newVal) {
if (newVal === '') {
return;
}
// 1.调用 JQ 中的 ajax 发起请求,判断 newVal 是否被占用
$.get('http://localhost:9090/user/EixtUserName/' + newVal, function (result) {
console.log(result);
})
},
// immediate 选项的默认值是 false
// immediate 的作用是:控制侦听器是否自动触发一次
immediate: true
}
}
})
</script>
6.4.deep 选项
如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选
项。
代码示例:
<div id="app">
<input type="text" v-model="info.username">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
// 用户信息对象
info: {
username: 'admin'
}
},
// 所有的侦听器都应该被定义到 watch 节点下
watch: {
info: {
handler(newVal) {
const userName = newVal.username
if (userName === '') {
return;
}
// 1.调用 JQ 中的 ajax 发起请求,判断 newVal 是否被占用
$.get('http://localhost:9090/user/EixtUserName/' + userName, function (result) {
console.log(result);
})
},
// 开启深度监听,只要对象中任何一个属性变化了,都会触发对象的侦听器
deep: true,
immediate: true
}
}
})
</script>
6.5.监听对象单个属性的变化
如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器。
代码示例:
<div id="app">
<input type="text" v-model="info.username">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
// 用户信息对象
info: {
username: 'admin'
}
},
// 所有的侦听器都应该被定义到 watch 节点下
watch: {
// 如果要监听的是对象的子属性的变化,则必须包裹一层单引号
'info.username'(newVal) {
if (newVal === '') {
return;
}
// 1.调用 JQ 中的 ajax 发起请求,判断 newVal 是否被占用
$.get('http://localhost:9090/user/EixtUserName/' + newVal, function (result) {
console.log(result);
})
},
immediate: true
}
})
</script>
7.计算属性
7.1.什么是计算属性
计算属性指的是通过一系列运算之后,最终得到一个属性值。
这个动态计算出来的属性值可以被模板结构或 methods 方法使用。
7.2.计算属性的特点
- 虽然计算属性在声明的时候被定义为方法,但是计算属性的本质是一个属性
- 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.6.12.js"></script>
<style>
.box {
width: 200px;
height: 200px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div id="app">
<div>
<span>R:</span>
<input type="text" v-model.number="r">
</div>
<div>
<span>G:</span>
<input type="text" v-model.number="g">
</div>
<div>
<span>B:</span>
<input type="text" v-model.number="b">
</div>
<hr>
<!-- 专门用户呈现颜色的 div 盒子 -->
<!-- 在属性身上,: 代表 v-bind: 属性绑定 -->
<!-- :style 代表动态绑定一个样式对象,它的值是一个 { } 样式对象 -->
<!-- 当前的样式对象中,只包含 backgroundColor 背景颜色 -->
<div class="box" :style="{ backgroundColor: rgb }">
{{ rgb }}
</div>
<button @click="show">按钮</button>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
// 红色
r: 0,
// 绿色
g: 0,
// 蓝色
b: 0
},
methods: {
// 点击按钮,在终端显示最新的颜色
show() {
console.log(this.rgb)
}
},
// 所有的计算属性都要定义到 computed 节点下
// 计算属性在定义的是时候,要定义成 方法格式
computed: {
// rgb 作为一个计算属性,被定义成了方法格式
// 最终,在这个方法中要返回一个生成好的 rgb(x,y,z) 的字符串
rgb() {
return `rgb(${this.r},${this.g},${this.b})`;
}
}
});
</script>
</body>
</html>
8.axios
axios 是一个专注于网络请求的库!
8.1.axios 的基本使用
8.1.1.发起 GET 请求:
代码示例:
<script src="./lib/axios.js"></script>
<script>
// 1.调用 axios 方法得到的返回值是 Promise 对象
axios({
// 请求方式
method:'GET',
// 请求地址
url:'http://localhost:9090/user/AllUser'
}).then(function(result){
console.log(result.data);
})
</script>
代码示例(传参):
<script src="./lib/axios.js"></script>
<script>
// 1.调用 axios 方法得到的返回值是 Promise 对象
axios({
// 请求方式
method: 'GET',
// 请求地址 http://localhost:9090/user/selectUserById?id=1
url: 'http://localhost:9090/user/selectUserById',
// URL 中的查询参数
params: {
id : 1
},
// 请求体参数
data: {}
}).then(function (result) {
console.log(result.data);
})
</script>
简化1:
<button id="btnGet">发起GET请求</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnGet').addEventListener('click', async function () {
// 如果调用某个方法的返回值是 Promise 示例,则前面可以添加 await
// await 只能用在被 async 修饰的方法中
const { data } = await axios({
// 请求方式
method: 'GET',
// 请求地址
url: 'http://localhost:9090/user/AllUser',
})
console.log(data);
})
</script>
简化2:
<button id="btnGet">发起GET请求</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnGet').addEventListener('click', async function () {
// 解构赋值的时候,使用 : 进行重命名
// 1.调用 axios 之后,使用 async / await 进行简化
// 2.使用解构赋值,从 axios 封装的大对象中,把 data 属性解构出来
// 3.把解构出来的 data 属性,使用 冒号 进行重命名,一般都重命名为 {data: res}
const { data: res } = await axios({
// 请求方式
method: 'GET',
// 请求地址
url: 'http://localhost:9090/user/AllUser',
})
console.log(res.data);
})
</script>
8.1.2.发起 POST 请求:
<button id="btnPost">发起POST请求</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnPost').addEventListener('click', function () {
axios({
// 请求方式
method: 'POST',
// 请求地址
url: 'http://localhost:9090/user/register',
// 请求体参数
data: {
userName:'post测试',
nickName:'post测试',
password:'123456',
email:'123456@qq.com'
}
}).then(function (result) {
console.log(result);
})
})
</script>
简化:
<button id="btnPost">发起POST请求</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnPost').addEventListener('click', async function () {
// 如果调用某个方法的返回值是 Promise 示例,则前面可以添加 await
// await 只能用在被 async 修饰的方法中
const { data } = await axios({
// 请求方式
method: 'POST',
// 请求地址
url: 'http://localhost:9090/user/register',
// 请求体参数
data: {
userName: 'post测试111',
nickName: 'post测试111',
password: '123456',
email: '123456111@qq.com'
}
})
console.log(data);
})
</script>
8.1.3.基于axios.get和axios.post发起请求
代码示例:
<button id="btnGet">GET</button>
<button id="btnPost">POST</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnGet').addEventListener('click', async function () {
const { data: res } = await axios.get('http://localhost:9090/user/selectUserById', {
// get参数
params: {
id: 1
}
})
console.log(res);
})
// ---------------------------------
document.querySelector('#btnPost').addEventListener('click', async function () {
const { data: res } = await axios.post('http://localhost:9090/user/register',
{userName: 'post测试222',nickName: 'post测试222',password: '123456',email: '123456222@qq.com'})
console.log(res);
})
</script>
9.vue-cli
9.1.什么是 vue-cli
vue-cli 是 Vue.js 开发的标准工具。它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程。
引用自 vue-cli 官网上的一句话:
程序员可以专注在撰写应用上,而不必花好几天去纠结 webpack 配置的问题。
中文官网:https://cli.vuejs.org/zh/
9.2.安装和使用
vue-cli 是 npm 上的一个全局包,使用 npm install 命令,即可方便的把它安装到自己的电脑上:
npm install -g @vue/cli
基于 vue-cli 快速生成工程化的 Vue 项目:
vue create 项目的名称
9.3.vue 项目的运行流程
在工程化的项目中,vue 要做的事情很单纯:通过 main.js 把 App.vue 渲染到 index.html 的指定区域中。
其中:
- App.vue 用来编写待渲染的模板结构
- index.html 中需要预留一个 el 区域
- main.js 把 App.vue 渲染到了 index.html 所预留的区域中
9.4.vue 项目中 src 目录的构成
assets 文件夹:存放项目中用到的静态资源文件,例如:css 样式表、图片资源
components 文件夹:程序员封装的、可复用的组件,都要放到 components 目录下
main.js 是项目的入口文件。整个项目的运行,要先执行 main.js
App.vue 是项目的根组件。
10.vue 组件
10.1.什么是组件化开发
组件化开发指的是:根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护。
10.2.vue 中的组件化开发
vue 是一个支持组件化开发的前端框架。
vue 中规定:组件的后缀名是 .vue。之前接触到的 App.vue 文件本质上就是一个 vue 的组件。
10.3.vue 组件的三个组成部分
每个 .vue 组件都由 3 部分构成,分别是:
- template -> 组件的模板结构
- script -> 组件的 JavaScript 行为
- style -> 组件的样式
其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分。 组件的三个组成部分
10.3.1.template
vue 规定:每个组件对应的模板结构,需要定义到 节点中。
注意:
- template 是 vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素
- template 中只能包含唯一的根节点
10.3.2.script
vue 规定:开发者可以在
.vue 组件中的 data 必须是函数
vue 规定:.vue 组件中的 data 必须是一个函数,不能直接指向一个数据对象。
10.3.3.style
vue 规定:组件内的
让 style 中支持 less 语法
在
代码示例:
<template>
<div>
<div class="box">
<h1>这是用户自定义的 Test.vue --- {{ username }}</h1>
<button @click="changeName">修改用户名</button>
</div>
<div>111</div>
</div>
</template>
<script>
// 默认导出,这是固定写法
export default {
// data 数据源
// 注意:.vue 组件中的 data 不能像之前一样指向对象
// 组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 {} 中可以定义数据
return {
username: 'k'
}
},
methods: {
changeName() {
// 在组件中,this 就表示当前组件的实例对象
this.username = 'kk'
}
},
// 侦听器
watch: {
// 当前组件中的计算属性
computed: {},
// 当前组件中的过滤器
filters: {}
}
}
</script>
<style lang="less">
.box {
background-color: aqua;
h1 {
color: red;
}
}
</style>
10.4.组件之间的父子关系
组件在被封装好之后,彼此之间是相互独立的,不存在父子关系
在使用组件的时候,根据彼此的嵌套关系,形成了父子关系、兄弟关系
10.4.1.使用组件的三个步骤
代码示例:
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 以标签的形式使用注册好的组件 -->
<Left></Left>
<Right></Right>
</div>
</div>
</template>
<script>
// 导入需要使用的 .vue 组件
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
// 注册组件
components:{
// 'Left':Left
Left,
Right
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
具体代码查看 day4/code/demo-1 文件
在VScode中配置@路径提示的插件
10.4.2.通过 components 注册的是私有子组件
例如:
在组件 A 的 components 节点下,注册了组件 F。
则组件 F 只能用在组件 A 中;不能被用在组件 C 中。
10.4.3.注册全局组件
在 vue 项目的 main.js 入口文件中,通过 Vue.component() 方法,可以注册全局组件。
代码示例:
// 导入需要全局注册的组件
import Count from '@/components/Count.vue'
// 参数1:字符串格式,表示组件的 注册名称
// 参数2:需要被全局注册的那个组件
Vue.component('MyCount', Count)
10.5.组件的 props
props 是组件的自定义属性,在封装通用组件的时候,合理地使用 props 可以极大的提高组件的复用性!
可以用于存放从其他组件传递过来的数据
10.5.1.props 是只读的
vue 规定:组件中封装的自定义属性是只读的,程序员不能直接修改 props 的值。否则会直接报错:
要想修改 props 的值,可以把 props 的值转存到 data 中,因为 data 中的数据都是可读可写的!
10.5.2.props 的 default 默认值
在声明自定义属性时,可以通过 default 来定义属性的默认值。示例代码如下:
10.5.3.props 的 type 值类型
在声明自定义属性时,可以通过 type 来定义属性的值类型。示例代码如下:
10.5.4.props 的 required 必填项
在声明自定义属性时,可以通过 required 选项,将属性设置为必填项,强制用户必须传递属性的值。示例代
码如下:
代码示例:
<template>
<div>
<h5>Count 组件</h5>
<p>count 的值是:{{ count }}</p>
<button @click="count += 1">+1</button>
<button @click="show">打印 this</button>
</div>
</template>
<script>
export default {
// props 是"自定义属性",允许使用者通过自定义属性,为当前组件指定初始值
// 自定义属性的名字,是封装者自定义的(只要名称合法即可)
// props 中的数据,可以直接在模板结构中被使用
// 注意:props 是只读的,不要直接修改 props 的值,否则终端会报错!
// props: ['init'],
props: {
// 自定义属性A : { /* 配置选项 */ },
// 自定义属性B : { /* 配置选项 */ },
// 自定义属性C : { /* 配置选项 */ },
init: {
// 如果外界使用 Count 组件的时候,没有传递 init 属性,则默认值生效
default: 10,
// init 的值类型必须是 Number 数字
type: Number,
// 必填项校验
required: true
}
},
data() {
return {
// 把 props 中的 init 值,转存到 count 上
count: this.init
}
},
methods: {
show() {
console.log(this)
}
}
}
</script>
<style lang="less">
</style>
10.6.组件之间的样式冲突问题
默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因是:
- 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的
- 每个组件中的样式,都会影响整个 index.html 页面中的 DOM 元素
10.6.1.解决组件样式冲突的问题
为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域,示例代码如下:
10.6.2.style 节点的 scoped 属性
为了提高开发效率和开发体验,vue 为 style 节点提供了 scoped 属性,从而防止组件之间的样式冲突问题:
<style lang="less" scoped>
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
h3{
color: red;
}
</style>
10.6.3./deep/ 样式穿透
如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样
式对子组件生效,可以使用 /deep/ 深度选择器。
代码示例:
<style lang="less" scoped>
h3 {
color: red;
}
/deep/ h5 {
color: green;
}
</style>
11.组件的生命周期
11.1.生命周期 & 生命周期函数
生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。
生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。
注意:生命周期强调的是时间段,生命周期函数强调的是时间点。
11.2.组件生命周期函数的分类
11.3.生命周期图示
可以参考 vue 官方文档给出的“生命周期图示”,进一步理解组件生命周期执行的过程:
https://cn.vuejs.org/v2/guide/instance.html#生命周期图示
代码示例:
<template>
<div class="test-container">
<h3 id="myh3">Test.vue 组件 --- {{ users.length }} 个用户</h3>
<p id="ppp">message 的值是: {{ message }}</p>
<button @click="message += '~'">修改 message 的值</button>
</div>
</template>
<script>
export default {
props: ['info'],
data() {
return {
message: 'Hello Test.vue',
// 定义 users 数组,存储的是所有用户的数据,默认为空数组
users: []
}
},
methods: {
show() {
console.log('调用了 Test 组件的 show 方法');
},
// 使用 Ajax 请求用户列表的数据
initUserList() {
const xhr = new XMLHttpRequest();
xhr.addEventListener('load', () => {
const result = JSON.parse(xhr.responseText);
this.users = result.data;
console.log(result);
});
xhr.open('GET', 'http://localhost:9090/user/AllUser');
xhr.send()
}
},
// 创建阶段的第一个生命周期函数
beforeCreate() {
// undefined
// console.log('beforeCreate---this.info'+this.info);
// console.log('beforeCreate---this.message'+this.message);
// this.show();
},
// 创建阶段的第二个生命周期函数
// 在这个阶段经常使用 ajax 拿取数据
created() {
// created 生命周期函数,非常常用
// 经常在它里边调用 methods 中的方法,请求服务器的数据
// 并且,把请求到的数据转存到 data 中,供 templete 模板渲染使用
this.initUserList();
console.log('created---this.info' + this.info);
console.log('created---this.message' + this.message);
this.show();
// 在 cteate 阶段拿不到这个方法
//const myh3 = document.querySelector('#myh3')
//console.log(myh3);
},
// 创建阶段的第三个生命周期函数
// 用处不大
beforeMount() {
// 在 beforeMount 阶段拿不到这个方法
const myh3 = document.querySelector('#myh3')
console.log('beforeMount---myh3:' + myh3);
},
// 创建阶段的第四个生命周期函数
// 如果要操作当前组件的 DOM ,最早只能在 mounted 阶段执行
// 主要用来操作 DOM 元素
mounted() {
const myh3 = document.querySelector('#myh3')
console.log('mounted---myh3:' + myh3);
},
// 创建阶段的第五个生命周期
beforeUpdate() {
console.log('beforeUpdate');
console.log('beforeUpdate---this.message:' + this.message);
const ppp = document.querySelector('#ppp')
console.log('beforeUpdate:' + ppp.innerHTML);
},
// 创建阶段的第六个生命周期
// 当数据变化之后,为了能够操作最新的 DOM 结构,必须把代码写到 updated 生命周期函数
updated() {
console.log('updated---this.message:' + this.message);
const ppp = document.querySelector('#ppp')
console.log('updated:' + ppp.innerHTML);
},
// 创建阶段的第七个生命周期
beforeDestroy() {
console.log('beforeDestroy');
},
// 创建阶段的第八个生命周期
destroyed() {
console.log('destroyed');
},
}
</script>
<style lang="less" scoped>
.test-container {
background-color: aqua;
height: 200px;
h3 {
color: red;
}
}
</style>
12.组件之间的数据共享
12.1.组件之间的关系
在项目开发中,组件之间的最常见的关系分为如下两种:
- 父子关系
- 兄弟关系
12.2.父子组件之间的数据共享
父子组件之间的数据共享又分为:
- 父 -> 子共享数据
- 子 -> 父共享数据
12.2.1.父组件向子组件共享数据
父组件向子组件共享数据需要使用自定义属性。示例代码如下:
父组件:App.vue
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<Left :msg="message" :user="userinfo"></Left>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
export default {
data() {
return {
message: 'Hello Vue',
userinfo: { name: 'k', age: 18 },
}
},
components:{
Left
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
子组件:Left.vue
<template>
<div class="left-container">
<h3>Left 组件</h3>
<p>msg 的值是:{{ msg }}</p>
<p>user 的值是:{{ user }}</p>
</div>
</template>
<script>
export default {
props: ['msg', 'user']
}
</script>
<style lang="less">
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
</style>
12.2.2.子组件向父组件共享数据
子组件向父组件共享数据使用自定义事件。示例代码如下:
子组件:Right.vue
<template>
<div class="right-container">
<h3>Right 组件 --- {{ count }}</h3>
<button @click="add">+1</button>
</div>
</template>
<script>
export default {
data() {
return {
// 子组件自己的数据,将来希望把 count 的值传递给父组件
count: 0,
}
},
methods: {
add() {
this.count += 1;
// 把自增结果传递给 父组件
this.$emit('numchange', this.count);
},
},
}
</script>
<style lang="less">
.right-container {
padding: 0 20px 20px;
background-color: lightskyblue;
min-height: 250px;
flex: 1;
}
</style>
父组件:App.vue
<template>
<div class="app-container">
<h1>App 根组件 --- {{ countFromSon }}</h1>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<Right @numchange="getNewCount"></Right>
</div>
</div>
</template>
<script>
import Right from '@/components/Right.vue'
export default {
data() {
return {
// 定义 countFromSon 来接收子组件传递过来的值
countFromSon:0
}
},
methods: {
// 获取子组件传递过来的数据
getNewCount(val){
this.countFromSon = val;
}
},
components:{
Right
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
12.2.3.兄弟组件之间的数据共享
在 vue2.x 中,兄弟组件之间数据共享的方案是 EventBus。
12.2.1.1.EventBus 的使用步骤
- 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
- 在数据发送方,调用 bus.$emit(‘事件名称’, 要发送的数据) 方法触发自定义事件
- 在数据接收方,调用 bus.$on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件
代码示例:
Left.vue
<template>
<div class="left-container">
<h3>Left 组件</h3>
<button @click="send">把 str 发送给 Right.vue</button>
</div>
</template>
<script>
import bus from '@/components/eventBus.js'
export default {
data() {
return {
str: '我是来自 Left 的数据'
}
},
methods: {
send() {
// 通过 eventBus 来发送数据
bus.$emit('share', this.str)
}
},
}
</script>
<style lang="less">
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
</style>
eventBus.js
import Vue from 'vue'
export default new Vue()
Right.vue
<template>
<div class="right-container">
<h3>Right 组件</h3>
<p>{{ msgFromLeft }}</p>
</div>
</template>
<script>
import bus from '@/components/eventBus.js'
export default {
data() {
return {
msgFromLeft: ''
}
},
created() {
// 为 bus 绑定自定义事件
bus.$on('share', (val) => {
this.msgFromLeft = val;
})
},
}
</script>
<style lang="less">
.right-container {
padding: 0 20px 20px;
background-color: lightskyblue;
min-height: 250px;
flex: 1;
}
</style>
13.ref 引用
13.1.什么是 ref 引用
ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,
组件的 $refs 指向一个空对象。
13.2.使用 ref 引用 DOM 元素
如果想要使用 ref 引用页面上的 DOM 元素,则可以按照如下的方式进行操作:
代码示例:
<template>
<div class="app-container">
<h1 ref="myh1">App 根组件</h1>
<button @click="showThis">打印 this</button>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
export default {
methods: {
showThis() {
// 当前 App 组件的实例对象
// console.log(this.$refs.myh1);
this.$refs.myh1.style.color = 'red'
},
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
13.3.使用 ref 引用组件实例
如果想要使用 ref 引用页面上的组件实例,则可以按照如下的方式进行操作:
代码示例:
Left.vue
<template>
<div class="left-container">
<h3>Left 组件 --- {{ count }}</h3>
<button @click="count += 1">+1</button>
<button @click="resetCount">重置</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
resetCount() {
this.count = 0;
}
},
}
</script>
<style lang="less">
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
</style>
App.vue
<template>
<div class="app-container">
<h1>App 根组件</h1>
<button @click="onReset">重置 Left 的 count 值为 0</button>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 使用 ref 拿到 Left.vue 组件的实例对象,依次来操作 Left.vue 组件里边的内容 -->
<Left ref="comLeft"></Left>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
export default {
methods: {
// 点击按钮重置 Left 的 count 值
onReset(){
// this.$refs.comLeft.resetCount();
this.$refs.comLeft.count = 0;
}
},
components: {
Left
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
13.4.控制文本框和按钮的按需切换
通过布尔值 inputVisible 来控制组件中的文本框与按钮的按需切换。
代码示例:
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<input type="text" v-if="inputVisible" @blur="showButton" />
<button v-else @click="showInput">展示输入框</button>
<hr>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
</div>
</div>
</template>
<script>
export default {
data() {
return {
// 控制输入框和按钮的按需切换
// 默认值为 false ,表示默认展示按钮,隐藏输入框
inputVisible: false
}
},
methods: {
showInput() {
this.inputVisible = true;
},
showButton() {
this.inputVisible = false;
}
},
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
13.4.1.this.$nextTick(cb) 方法
组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的
DOM 更新完成之后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。
代码示例:
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<input type="text" v-if="inputVisible" @blur="showButton" ref="iptRef"/>
<button v-else @click="showInput">展示输入框</button>
<hr>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
</div>
</div>
</template>
<script>
export default {
data() {
return {
// 控制输入框和按钮的按需切换
// 默认值为 false ,表示默认展示按钮,隐藏输入框
inputVisible: false
}
},
methods: {
showInput() {
// 1.切换布尔值,把文本框展示出来
this.inputVisible = true;
// 2.让展示出来的文本框自动获得焦点
this.$nextTick(() => this.$refs.iptRef.focus())
},
showButton() {
this.inputVisible = false;
}
},
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
14.购物车案例
15.动态组件
15.1.什么是动态组件
动态组件指的是动态切换组件的显示与隐藏。
15.2.如何实现动态组件渲染
vue 提供了一个内置的 组件,专门用来实现动态组件的渲染。
代码示例:
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1.component 标签是 vue 内置的,作用:组件的占位符 -->
<!-- 2.is 属性的值表示要渲染的组件的名字 -->
<component :is="comName"></component>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
data() {
return {
// comName 表示要展示组件的名称
comName:'Left'
}
},
components: {
Left,
Right
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
15.3.动态切换组件的展示与隐藏
代码示例:
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<button @click="comName='Left'">展示 Left</button>
<button @click="comName='Right'">展示 Right</button>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1.component 标签是 vue 内置的,作用:组件的占位符 -->
<!-- 2.is 属性的值表示要渲染的组件的名字 -->
<component :is="comName"></component>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
data() {
return {
// comName 表示要展示组件的名称
comName:'Left'
}
},
components: {
Left,
Right
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
15.4.keep-alive
15.4.1.使用 keep-alive 保持状态
默认情况下,切换动态组件时无法保持组件的状态。此时可以使用 vue 内置的 组件保持动态组
件的状态。
代码示例:
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<button @click="comName = 'Left'">展示 Left</button>
<button @click="comName = 'Right'">展示 Right</button>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1.component 标签是 vue 内置的,作用:组件的占位符 -->
<!-- 2.is 属性的值表示要渲染的组件的名字 -->
<!-- keep-alive 可以把内部的组件进行缓存,而不是销毁组件 -->
<keep-alive>
<component :is="comName"></component>
</keep-alive>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
data() {
return {
// comName 表示要展示组件的名称
comName: 'Left'
}
},
components: {
Left,
Right
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
15.4.2.keep-alive 对应的生命周期函数
当组件被缓存时,会自动触发组件的 deactivated 生命周期函数。
当组件被激活时,会自动触发组件的 activated 生命周期函数。
代码示例:
<template>
<div class="left-container">
<h3>Left 组件 --- {{ count }}</h3>
<button @click="count += 1">+1</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
// 当组件第一次被创建的时候,既会执行 created 生命周期,也会执行 activated 生命周期
// 但是,当组件被激活的时候,只会触发 activated 生命周期,不再触发 created 生命周期
activated() {
console.log('组件被激活了,activated');
},
deactivated() {
console.log('组件被缓存了,deactivated');
},
}
</script>
<style lang="less">
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
</style>
15.4.3.keep-alive 的 include 和 exclude 属性
include 属性用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号分隔
exclude 属性用来指定:名称匹配的组件不会被缓存。多个组件名之间使用英文的逗号分隔
注意:include 和 exclude 不能同时使用
代码示例:
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<button @click="comName = 'Left'">展示 Left</button>
<button @click="comName = 'Right'">展示 Right</button>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1.component 标签是 vue 内置的,作用:组件的占位符 -->
<!-- 2.is 属性的值表示要渲染的组件的名字 -->
<!-- keep-alive 可以把内部的组件进行缓存,而不是销毁组件 -->
<!-- include 表示只缓存字符串的组件 多个组件用英文逗号分隔 -->
<!-- exclude 排除项, 注意 include 和 exclude 不能同时使用 -->
<keep-alive include="Left">
<component :is="comName"></component>
</keep-alive>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
data() {
return {
// comName 表示要展示组件的名称
comName: 'Left'
}
},
components: {
Left,
Right
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
15.5.了解组件注册名称和组件声明时name的区别
代码示例:
Right.vue
<template>
<div class="right-container">
<h3>Right 组件</h3>
</div>
</template>
<script>
export default {
// 当提供了 name 属性之后,组件的名称就是 name 的值
// 对比:
// 1.组件的注册名称的主要场景是 以标签的形式,把注册好的组件渲染和使用到页面结构之中
// 2.组件声明时候的 name 名称主要应用场景:结合 <keep-alive> 标签实现组件缓存功能,以及在调试工具中看到组件的名称
name:'MyRight'
}
</script>
<style lang="less">
.right-container {
padding: 0 20px 20px;
background-color: lightskyblue;
min-height: 250px;
flex: 1;
}
</style>
App.vue
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<button @click="comName = 'Left'">展示 Left</button>
<button @click="comName = 'Right'">展示 Right</button>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1.component 标签是 vue 内置的,作用:组件的占位符 -->
<!-- 2.is 属性的值表示要渲染的组件的名字 -->
<!-- keep-alive 可以把内部的组件进行缓存,而不是销毁组件 -->
<!-- include 表示只缓存字符串的组件 多个组件用英文逗号分隔 -->
<!-- exclude 排除项, 注意 include 和 exclude 不能同时使用 -->
<keep-alive exclude="MyRight">
<component :is="comName"></component>
</keep-alive>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
data() {
return {
// comName 表示要展示组件的名称
comName: 'Left'
}
},
components: {
// 如果在声明组件的时候,没有为组件指定 name 名称,则组件的名称默认就是注册时候的名称
Left,
Right
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
16.插槽
16.1.什么是插槽
插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的
部分定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的内容的占位符。
16.2.体验插槽的基础用法
在封装组件时,可以通过 元素定义插槽,从而为用户预留内容占位符。
代码示例:
App.vue
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<Left>
<p>这个在 Left 组件的内容区域声明的 p 标签</p>
</Left>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
export default {
components:{
Left
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
Left.vue
<template>
<div class="left-container">
<h3>Left 组件</h3>
<hr>
<!-- 声明一个插槽区域 -->
<slot></slot>
</div>
</template>
<script>
export default {}
</script>
<style lang="less">
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
</style>
没有预留插槽的内容会被丢弃
如果在封装组件时没有预留任何 插槽,则用户提供的任何自定义内容都会被丢弃。
16.3.v-slot
代码示例:
Left.vue
<template>
<div class="left-container">
<h3>Left 组件</h3>
<hr>
<!-- 声明一个插槽区域 -->
<!-- vue 官方规定:每一个插槽都要有一个 name 名称 -->
<!-- 如果省略 slot 的 name 属性,则有个默认名称为 default -->
<slot name="defualt"></slot>
</div>
</template>
<script>
export default {}
</script>
<style lang="less">
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
</style>
App.vue
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<Left>
<!-- 默认的情况下,在使用组件的时候,提供的内容都会被填充到名字为 default 的插槽之中 -->
<!-- 1.如果要把内容填充到指定名称的插槽中,需要使用 v-slot:名称 这个指定-->
<!-- 2.v-slot: 后面要跟上插槽的名称 -->
<!-- 3.v-slot: 指定不能直接用在元素身上,必须用到 template 标签-->
<!-- 4.template 这个标签,它是一个虚拟的标签,只起到包裹性质的作用,不会被渲染为任何实质性的 html 元素 -->
<!-- 5.v-slot:指定的简写形式是 # -->
<template #defualt>
<p>这个在 Left 组件的内容区域声明的 p 标签</p>
</template>
</Left>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
export default {
components:{
Left
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
16.4.后备内容
封装组件时,可以为预留的 插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何
内容,则后备内容会生效。
代码示例:
Left.vue
<template>
<div class="left-container">
<h3>Left 组件</h3>
<hr>
<!-- 声明一个插槽区域 -->
<!-- vue 官方规定:每一个插槽都要有一个 name 名称 -->
<!-- 如果省略 slot 的 name 属性,则有个默认名称为 default -->
<!-- 如果这个插槽被使用,则不会显示插槽中的文字 -->
<slot name="defualt">这是 default 插槽的默认内容</slot>
</div>
</template>
<script>
export default {}
</script>
<style lang="less">
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
</style>
16.5.具名插槽
如果在封装组件时需要预留多个插槽节点,则需要为每个 插槽指定具体的 name 名称。这种带有具体
名称的插槽叫做“具名插槽”。
代码示例:
Article.vue
<template>
<div class="article-container">
<!-- 文章的标题 -->
<div class="header-box">
<slot name="title"></slot>
</div>
<!-- 文章的内容 -->
<div class="content-box">
<slot name="content"></slot>
</div>
<!-- 文章的作者 -->
<div class="footer-box">
<slot name="author"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'Article'
}
</script>
<style lang="less" scoped>
.article-container {
>div{
min-height: 150px;
}
.header-box {
background-color:#ffc7c9;
}
.content-box {
background-color: #a9d5e8;
}
.footer-box {
background-color: #ffa776;
}
}
</style>
App.vue
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<Article>
<template #title>
<h2>震惊!...</h2>
</template>
<template #content>
<div>
<p>某高校一男子...</p>
<p>荣获...</p>
<p>牛逼...</p>
</div>
</template>
<template #author>
<div>作者:彬锅果</div>
</template>
</Article>
</div>
</template>
<script>
import Article from '@/components/Article.vue'
export default {
components: {
Article
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
16.6.为具名插槽提供内容
在向具名插槽提供内容的时候,我们可以在一个 元素上使用 v-slot 指令,并以 v-slot 的参数的
形式提供其名称。
代码示例:
16.7.作用域插槽的解构赋值
代码示例:
Article.vue
<template>
<div class="article-container">
<!-- 文章的标题 -->
<div class="header-box">
<slot name="title"></slot>
</div>
<!-- 文章的内容 -->
<div class="content-box">
<!-- 在封装组件时,为预留的 <slot> 提供属性对应的值,
这种用法叫做 作用域插槽 -->
<slot name="content" msg="Hello Article.vue" :user="userinfo"></slot>
</div>
<!-- 文章的作者 -->
<div class="footer-box">
<slot name="author"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'Article',
data() {
return {
// 用户信息对象
userinfo:{
name:'k',
age:18
}
}
},
}
</script>
<style lang="less" scoped>
.article-container {
>div{
min-height: 150px;
}
.header-box {
background-color:#ffc7c9;
}
.content-box {
background-color: #a9d5e8;
}
.footer-box {
background-color: #ffa776;
}
}
</style>
App.vue
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<Article>
<template #title>
<h2>震惊!...</h2>
</template>
<template #content="{ msg, user }">
<div>
<p>某高校一男子...</p>
<p>荣获...</p>
<p>牛逼...</p>
<p>{{ msg }}</p>
<p>{{ user.name }}</p>
<p>{{ user.age }}</p>
</div>
</template>
<template #author>
<div>作者:彬锅果</div>
</template>
</Article>
<div class="box" style="display:none;">
<!-- 渲染 Left 组件和 Right 组件 -->
<Left>
<!-- 默认的情况下,在使用组件的时候,提供的内容都会被填充到名字为 default 的插槽之中 -->
<!-- 1.如果要把内容填充到指定名称的插槽中,需要使用 v-slot:名称 这个指定-->
<!-- 2.v-slot: 后面要跟上插槽的名称 -->
<!-- 3.v-slot: 指定不能直接用在元素身上,必须用到 template 标签-->
<!-- 4.template 这个标签,它是一个虚拟的标签,只起到包裹性质的作用,不会被渲染为任何实质性的 html 元素 -->
<!-- 5.v-slot:指定的简写形式是 # -->
<template #defualt>
<p>这个在 Left 组件的内容区域声明的 p 标签</p>
</template>
</Left>
</div>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
import Article from '@/components/Article.vue'
export default {
components: {
Left,
Article
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
17.自定义指令
17.1.什么是自定义指令
vue 官方提供了 v-text、v-for、v-model、v-if 等常用的指令。除此之外 vue 还允许开发者自定义指令。
17.2.自定义指令的分类
vue 中的自定义指令分为两类,分别是:
私有自定义指令
全局自定义指令
17.2.1.私有自定义指令
在每个 vue 组件中,可以在 directives 节点下声明私有自定义指令。
代码示例:
<template>
<div class="app-container">
<h1 v-color>App 根组件</h1>
<hr />
</div>
</template>
<script>
export default {
// 私有自定义指令的节点
directives:{
// 定义名为 color 的指令,指向一个配置对象
color:{
// 当指令第一次被绑定到元素上的时候,会立即触发 bind 函数
// 形参中的 el 表示当前指令所绑定到的那个 DOM 对象
bind(el){
el.style.color='red'
}
}
},
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
17.2.1.1.通过 binding 获取指令的参数值
在声明自定义指令时,可以通过形参中的第二个参数,来接收指令的参数值
代码示例:
<template>
<div class="app-container">
<h1 v-color="color">App 根组件</h1>
<p v-color="'red'">测试</p>
<hr />
</div>
</template>
<script>
export default {
data() {
return {
color: 'blue'
}
},
// 私有自定义指令的节点
directives: {
// 定义名为 color 的指令,指向一个配置对象
color: {
// 当指令第一次被绑定到元素上的时候,会立即触发 bind 函数
// 形参中的 el 表示当前指令所绑定到的那个 DOM 对象
bind(el,binding) {
el.style.color = binding.value
}
}
},
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
17.2.1.2.update 函数
bind 函数只调用 1 次:当指令第一次绑定到元素时调用,当 DOM 更新时 bind 函数不会被触发。 update 函
数会在每次 DOM 更新时被调用。
代码示例:
<template>
<div class="app-container">
<h1 v-color="color">App 根组件</h1>
<p v-color="'red'">测试</p>
<button @click="color='green'">改变 color 的颜色值</button>
<hr />
</div>
</template>
<script>
export default {
data() {
return {
color: 'blue'
}
},
// 私有自定义指令的节点
directives: {
// 定义名为 color 的指令,指向一个配置对象
color: {
// 当指令第一次被绑定到元素上的时候,会立即触发 bind 函数
// 形参中的 el 表示当前指令所绑定到的那个 DOM 对象
bind(el,binding) {
el.style.color = binding.value
},
// 每次 DOM 更新的时候,会触发update
update(el, binding) {
el.style.color = binding.value
}
}
},
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
17.2.1.3.函数简写
如果 bind 和update 函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式
17.2.2.全局自定义指令
全局共享的自定义指令需要通过“Vue.directive()”进行声明
代码示例:
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 全局自定义指令
/* Vue.directive('color', {
bind(el, binding) {
el.style.color = binding.value
},
update(el, binding) {
el.style.color = binding.value
}
}) */
Vue.directive('color', function (el, binding) {
el.style.color = binding.value
})
new Vue({
render: h => h(App),
}).$mount('#app')
Article.vue
<template>
<div class="article-container">
<h3 v-color="'red'">Article 组件</h3>
<!-- 文章的标题 -->
<div class="header-box">
<slot name="title"></slot>
</div>
<!-- 文章的内容 -->
<div class="content-box">
<!-- 在封装组件时,为预留的 <slot> 提供属性对应的值,
这种用法叫做 作用域插槽 -->
<slot name="content" msg="Hello Article.vue" :user="userinfo"></slot>
</div>
<!-- 文章的作者 -->
<div class="footer-box">
<slot name="author"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'Article',
data() {
return {
// 用户信息对象
userinfo:{
name:'k',
age:18
}
}
},
}
</script>
<style lang="less" scoped>
.article-container {
>div{
min-height: 150px;
}
.header-box {
background-color:#ffc7c9;
}
.content-box {
background-color: #a9d5e8;
}
.footer-box {
background-color: #ffa776;
}
}
</style>
18.路由
18.1.前端路由的概念与原理
18.1.1.什么是路由
路由(英文:router)就是对应关系。
18.1.2.SPA 与前端路由
SPA 指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。
此时,不同组件之间的切换需要通过前端路由来实现。
结论:在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成!
18.1.3.什么是前端路由
通俗易懂的概念:Hash 地址与组件之间的对应关系。
18.1.4.前端路由的工作方式
- 用户点击了页面上的路由链接
- 导致了 URL 地址栏中的 Hash 值发生了变化
- 前端路由监听了到 Hash 地址的变化
- 前端路由把当前 Hash 地址对应的组件渲染都浏览器中
结论:前端路由,指的是 Hash 地址与组件之间的对应关系!
18.1.4.实现简易的前端路由
步骤1:通过 标签,结合 comName 动态渲染组件。示例代码如下:
步骤2:在 App.vue 组件中,为 链接添加对应的 hash 值:
步骤3:在 created 生命周期函数中,监听浏览器地址栏中 hash 地址的变化,动态切换要展示的组件的名称:
18.2.vue-router 的基本使用
18.2.1.什么是 vue-router
vue-router 是 vue.js 官方给出的路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目
中组件的切换。
vue-router 的官方文档地址:https://router.vuejs.org/zh/
18.2.2.vue-router 安装和配置的步骤
- 安装 vue-router 包
- 创建路由模块
- 导入并挂载路由模块
- 声明路由链接和占位符
18.2.2.1. 在项目中安装 vue-router
在 vue2 的项目中,安装 vue-router 的命令如下:
npm install vue-router@3.5.2 -S
18.2.2.2.创建路由模块
在 src 源代码目录下,新建 router/index.js 路由模块,并初始化如下的代码:
// src/router/index.js 就是当前项目的路由模块
// 1.导入 Vue 和 VueRouter 的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 2.调用 Vue.use() 函数,把 VueRouter 安装为 Vue 项目的插件
// Vue.use() 用来为 vue项目安装插件
Vue.use(VueRouter)
// 3.创建路由的实例对象
const router = new VueRouter()
// 4.向外共享路由实例对象
// 只要在外使用 import 导入该路由模块就可以拿到此路由实例对象
export default router
18.2.2.3.导入并挂载路由模块
在 src/main.js 入口文件中,导入并挂载路由模块。示例代码如下:
import Vue from 'vue'
import App from './App.vue'
// 导入路由模块,目的:拿到路由的实例对象
// 在进行模块化导入的时候,如果给定的是文件夹,则默认导入这个文件夹下 名字叫做 index.js 的文件
import router from "@/router/index.js"
// 导入 bootstrap 样式
import 'bootstrap/dist/css/bootstrap.min.css'
// 全局样式
import '@/assets/global.css'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
// 在 vue 项目中,要想把路由用起来,必须把路由实例对象,通过下面的方式进行挂载
// router: 路由的实例对象
// 属性值和属性名一致,可简写
// router: router
router
}).$mount('#app')
18.2.2.4.声明路由链接和占位符
在 src/App.vue 组件中,使用 vue-router 提供的 和 声明路由链接和占位符:
未使用路由链接:
<template>
<div class="app-container">
<h1>App2 组件</h1>
<a href="#/home">首页</a>
<a href="#/movie">电影</a>
<a href="#/about">关于</a>
<hr/>
<!-- 只要在项目中安装和配置了 vue-router,就可以使用 router-view 这个组件了 -->
<!-- 它的作用:占位符 -->
<!-- 接着就到 src/route/index.js 中定义 "hash地址" 与 "组件" 之间的对应关系 -->
<router-view></router-view>
</div>
</template>
使用路由链接
<template>
<div class="app-container">
<h1>App2 组件</h1>
<!-- <a href="#/home">首页</a>
<a href="#/movie">电影</a>
<a href="#/about">关于</a> -->
<!-- 当安装了配置了 vue-router 后,就可以使用 router-link 来代替普通的 a 链接了 -->
<router-link to="/home">首页</router-link>
<router-link to="/movie">电影</router-link>
<router-link to="/about">关于</router-link>
<hr/>
<!-- 只要在项目中安装和配置了 vue-router,就可以使用 router-view 这个组件了 -->
<!-- 它的作用:占位符 -->
<!-- 接着就到 src/route/index.js 中定义 "hash地址" 与 "组件" 之间的对应关系 -->
<router-view></router-view>
</div>
</template>
18.2.2.5.声明路由的匹配规则
在 src/router/index.js 路由模块中,通过 routes 数组声明路由的匹配规则。示例代码如下:
// src/router/index.js 就是当前项目的路由模块
// 1.导入 Vue 和 VueRouter 的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入需要的组件
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'
// 2.调用 Vue.use() 函数,把 VueRouter 安装为 Vue 项目的插件
// Vue.use() 用来为 vue项目安装插件
Vue.use(VueRouter)
// 3.创建路由的实例对象
const router = new VueRouter({
// routes 是一个数组,作用:定义 "hash地址" 与 "组件" 之间的对应关系
routes: [{
path: '/home',
component: Home
},
{
path: '/movie',
component: Movie
},
{
path: '/about',
component: About
},
]
})
// 4.向外共享路由实例对象
// 只要在外使用 import 导入该路由模块就可以拿到此路由实例对象
export default router
18.2.2.6.路由重定向 redirect
路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面。
通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:
const router = new VueRouter({
// routes 是一个数组,作用:定义 "hash地址" 与 "组件" 之间的对应关系
routes: [
// redirect 路由重定向,当用户访问 / 的时候,通过 redirect 属性跳转到 /home 对应的路由规则
{
path: '/',
redirect: '/home'
},
// 路由规则
{
path: '/home',
component: Home
},
{
path: '/movie',
component: Movie
},
{
path: '/about',
component: About
},
]
})
18.3.vue-router 的常见用法
18.3.1.嵌套路由
通过路由实现组件的嵌套展示,叫做嵌套路由。
18.3.1.1.声明子路由链接和子路由占位符
在 About.vue 组件中,声明 tab1 和 tab2 的子路由链接以及子路由占位符。示例代码如下:
<template>
<div class="about-container">
<h3>About 组件</h3>
<!-- 子级路由链接 -->
<router-link to="/about/tab1">tab1</router-link>
<router-link to="/about/tab2">tab2</router-link>
<hr>
<!-- 子级路由占位符 -->
<router-view></router-view>
</div>
</template>
18.3.1.2.通过 children 属性声明子路由规则
在 src/router/index.js 路由模块中,导入需要的组件,并使用 children 属性声明子路由规则:
18.3.1.3.默认子路由
使用 redict 方式
使用默认子路由
18.3.2.动态路由
18.3.2.1. 动态路由匹配
思考:有如下 3 个路由链接:
定义如下 3 个路由规则,是否可行?
缺点:路由规则的复用性差。
18.3.2.2.动态路由的概念
动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。
在 vue-router 中使用英文的冒号(:)来定义路由的参数项。示例代码如下:
18.3.2.3.$route.params 参数对象
在动态路由渲染出来的组件中,可以使用 this.$route.params 对象访问到动态匹配的参数值。
<template>
<div class="movie-container">
<!-- this.$route 是路由的 “参数对象 ” -->
<h3>Movie 组件 --- {{ this.$route.params.id }}</h3>
<button @click="showThis">打印 this</button>
</div>
</template>
<script>
export default {
name: 'Movie',
methods: {
showThis(){
console.log(this);
}
},
}
</script>
18.3.2.4.使用 props 接收路由参数
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参。示例代码如下:
18.3.3.声明式导航 & 编程式导航
在浏览器中,点击链接实现导航的方式,叫做声明式导航。例如:
- 普通网页中点击 链接、vue 项目中点击 都属于声明式导航
在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航。例如:
- 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
18.3.3.1.vue-router 中的编程式导航 API
vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:
-
this.$router.push(‘hash 地址’)
- 跳转到指定 hash 地址,并增加一条历史记录
-
this.$router.replace(‘hash 地址’)
- 跳转到指定的 hash 地址,并替换掉当前的历史记录
-
this.$router.go(数值 n)
- 实现导航历史前进、后退
$router.push
调用 this.$router.push() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面。示例代码如下:
$router.replace
调用 this.$router.replace() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面。
push 和 replace 的区别:
- push 会增加一条历史记录
- replace 不会增加历史记录,而是替换掉当前的历史记录
$router.go
调用 this.$router.go() 方法,可以在浏览历史中前进和后退。示例代码如下:
$router.go 的简化用法
在实际开发中,一般只会前进和后退一层页面。因此 vue-router 提供了如下两个便捷方法:
-
$router.back()
- 在历史记录中,后退到上一个页面
-
$router.forward()
- 在历史记录中,前进到下一个页面
代码示例:
<template>
<div class="home-container">
<h3>Home 组件</h3>
<hr>
<button @click="pushMovie1">通过 push 跳转到 电影1 页面</button>
<button @click="replaceMovie1">通过 replace 跳转到 电影1 页面</button>
<button @click="goBack">通过 go 后退</button>
<!-- 在行内使用编程式导航跳转的时候,this 必须要省略,否则会报错 -->
<button @click="$router.back()">back 后退</button>
<button @click="$router.forward()">forward 前进</button>
</div>
</template>
<script>
export default {
name: 'Home',
methods: {
pushMovie1(){
// 通过编程式导航 api ,导航跳转到指定页面
this.$router.push('/movie/1')
},
replaceMovie1(){
this.$router.replace('/movie/1')
},
goBack(){
// go(-1) 表示后退一层
// 如果后退层数超过上限,则原地不动
this.$router.go(-1)
}
},
}
</script>
18.3.4.导航守卫
导航守卫可以控制路由的访问权限。示意图如下:
18.3.4.1.全局前置守卫
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行
访问权限的控制
18.3.4.2.守卫方法的 3 个形参
全局前置守卫的回调函数中接收 3 个形参,格式为:
18.3.4.3.next 函数的 3 种调用方式
参考示意图,分析 next 函数的 3 种调用方式最终导致的结果:
- 当前用户拥有后台主页的访问权限,直接放行:next()
- 当前用户没有后台主页的访问权限,强制其跳转到登录页面:next(‘/login’)
- 当前用户没有后台主页的访问权限,不允许跳转到后台主页:next(false)
18.3.4.*.控制后台主页的访问权限
18.4.后台管理案例
ueRouter)
// 3.创建路由的实例对象
const router = new VueRouter({
// routes 是一个数组,作用:定义 “hash地址” 与 “组件” 之间的对应关系
routes: [{
path: ‘/home’,
component: Home
},
{
path: ‘/movie’,
component: Movie
},
{
path: ‘/about’,
component: About
},
]
})
// 4.向外共享路由实例对象
// 只要在外使用 import 导入该路由模块就可以拿到此路由实例对象
export default router
##### 18.2.2.6.路由重定向 redirect
路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面。
通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:
```js
const router = new VueRouter({
// routes 是一个数组,作用:定义 "hash地址" 与 "组件" 之间的对应关系
routes: [
// redirect 路由重定向,当用户访问 / 的时候,通过 redirect 属性跳转到 /home 对应的路由规则
{
path: '/',
redirect: '/home'
},
// 路由规则
{
path: '/home',
component: Home
},
{
path: '/movie',
component: Movie
},
{
path: '/about',
component: About
},
]
})
18.3.vue-router 的常见用法
18.3.1.嵌套路由
通过路由实现组件的嵌套展示,叫做嵌套路由。
[外链图片转存中…(img-MwaZ6t3A-1671884043157)]
18.3.1.1.声明子路由链接和子路由占位符
在 About.vue 组件中,声明 tab1 和 tab2 的子路由链接以及子路由占位符。示例代码如下:
<template>
<div class="about-container">
<h3>About 组件</h3>
<!-- 子级路由链接 -->
<router-link to="/about/tab1">tab1</router-link>
<router-link to="/about/tab2">tab2</router-link>
<hr>
<!-- 子级路由占位符 -->
<router-view></router-view>
</div>
</template>
18.3.1.2.通过 children 属性声明子路由规则
在 src/router/index.js 路由模块中,导入需要的组件,并使用 children 属性声明子路由规则:
[外链图片转存中…(img-CdwiUgi4-1671884043157)]
18.3.1.3.默认子路由
使用 redict 方式
[外链图片转存中…(img-choigdMk-1671884043157)]
使用默认子路由
[外链图片转存中…(img-27mkrEAR-1671884043158)]
18.3.2.动态路由
18.3.2.1. 动态路由匹配
思考:有如下 3 个路由链接:
[外链图片转存中…(img-QxHlsnQc-1671884043158)]
定义如下 3 个路由规则,是否可行?
[外链图片转存中…(img-UxuPZrKJ-1671884043158)]
缺点:路由规则的复用性差。
18.3.2.2.动态路由的概念
动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。
在 vue-router 中使用英文的冒号(:)来定义路由的参数项。示例代码如下:
[外链图片转存中…(img-02wF6n69-1671884043158)]
18.3.2.3.$route.params 参数对象
在动态路由渲染出来的组件中,可以使用 this.$route.params 对象访问到动态匹配的参数值。
<template>
<div class="movie-container">
<!-- this.$route 是路由的 “参数对象 ” -->
<h3>Movie 组件 --- {{ this.$route.params.id }}</h3>
<button @click="showThis">打印 this</button>
</div>
</template>
<script>
export default {
name: 'Movie',
methods: {
showThis(){
console.log(this);
}
},
}
</script>
18.3.2.4.使用 props 接收路由参数
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参。示例代码如下:
[外链图片转存中…(img-aJfGfES6-1671884043159)]
18.3.3.声明式导航 & 编程式导航
在浏览器中,点击链接实现导航的方式,叫做声明式导航。例如:
- 普通网页中点击 链接、vue 项目中点击 都属于声明式导航
在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航。例如:
- 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
18.3.3.1.vue-router 中的编程式导航 API
vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:
-
this.$router.push(‘hash 地址’)
- 跳转到指定 hash 地址,并增加一条历史记录
-
this.$router.replace(‘hash 地址’)
- 跳转到指定的 hash 地址,并替换掉当前的历史记录
-
this.$router.go(数值 n)
- 实现导航历史前进、后退
$router.push
调用 this.$router.push() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面。示例代码如下:
[外链图片转存中…(img-cv945lCD-1671884043159)]
$router.replace
调用 this.$router.replace() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面。
push 和 replace 的区别:
- push 会增加一条历史记录
- replace 不会增加历史记录,而是替换掉当前的历史记录
$router.go
调用 this.$router.go() 方法,可以在浏览历史中前进和后退。示例代码如下:
[外链图片转存中…(img-7LZ14E31-1671884043159)]
$router.go 的简化用法
在实际开发中,一般只会前进和后退一层页面。因此 vue-router 提供了如下两个便捷方法:
-
$router.back()
- 在历史记录中,后退到上一个页面
-
$router.forward()
- 在历史记录中,前进到下一个页面
代码示例:
<template>
<div class="home-container">
<h3>Home 组件</h3>
<hr>
<button @click="pushMovie1">通过 push 跳转到 电影1 页面</button>
<button @click="replaceMovie1">通过 replace 跳转到 电影1 页面</button>
<button @click="goBack">通过 go 后退</button>
<!-- 在行内使用编程式导航跳转的时候,this 必须要省略,否则会报错 -->
<button @click="$router.back()">back 后退</button>
<button @click="$router.forward()">forward 前进</button>
</div>
</template>
<script>
export default {
name: 'Home',
methods: {
pushMovie1(){
// 通过编程式导航 api ,导航跳转到指定页面
this.$router.push('/movie/1')
},
replaceMovie1(){
this.$router.replace('/movie/1')
},
goBack(){
// go(-1) 表示后退一层
// 如果后退层数超过上限,则原地不动
this.$router.go(-1)
}
},
}
</script>
18.3.4.导航守卫
导航守卫可以控制路由的访问权限。示意图如下:
[外链图片转存中…(img-S5vvF3g0-1671884043159)]
18.3.4.1.全局前置守卫
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行
访问权限的控制
[外链图片转存中…(img-YNRTkS5w-1671884043159)]
18.3.4.2.守卫方法的 3 个形参
全局前置守卫的回调函数中接收 3 个形参,格式为:
[外链图片转存中…(img-uzkLqvmP-1671884043160)]
18.3.4.3.next 函数的 3 种调用方式
参考示意图,分析 next 函数的 3 种调用方式最终导致的结果:
[外链图片转存中…(img-0waQUXCZ-1671884043160)]
- 当前用户拥有后台主页的访问权限,直接放行:next()
- 当前用户没有后台主页的访问权限,强制其跳转到登录页面:next(‘/login’)
- 当前用户没有后台主页的访问权限,不允许跳转到后台主页:next(false)
18.3.4.*.控制后台主页的访问权限
[外链图片转存中…(img-vKu3HTxO-1671884043160)]