1. vue简介
1.1 什么是vue
是一套 用于构建用户界面的前端 框架。
- 构建用户界面
- 用vue往html页面中填充数据,非常的方便
- 框架
- 框架是一套现成的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能!
- 要学习vue,就是学习vue框架中规定的用法!
- vue指令、组件(是对UI结构的复用)、路由、Vuex、vue组件库
- 只有把上面老师罗列的我内容掌握以后,才有开发vue项目的能力
1.2 vue的特性
vue框架的特性,主要体现在如下量方面:
-
数据驱动视图:数据的变化 会驱动视图 的自动更新
- 好处:程序员只需要把数据维护好,那么页面结构会被vue自动渲染出来!
- 注意:数据驱动视图是单向的数据绑定
-
双向数据绑定:在 填写表单 时,双向数据绑定可以在辅助开发者在 不操作DOM的前提下,自动 把用户填写的内容 同步到 数据源中。
在网页中,form 表单负责采集数据,Ajax 负责提交数据。
- 好处:开发者不再需要手动操作DOM元素,来获取表单元素最新的值!
- js 的数据变化,会被自动渲染到页面上
- 页面上表单采集的数据发生变化时,会被vue自动获取到,并更新到 js 数据中
1.3 MVVM
MVVM是 vue 实现 数据驱动视图 和 双向数据绑定 的核心原理。MVVM 指的是 Model、View 和 ViewModel,它把每个 HTML 页面都拆分成了这三个部分:
- Model:表示当前页面渲染是所依赖的数据源。
- View:表示当前页面所渲染的 DOM 结构。
- ViewModel:表示 vue 的实例,它是 MVVM 核心
1.4 MVVM 工作原理
ViewModel 作为MVVM 的核心,是它把当前页面的 数据源(Model)和 页面的结构(View)连接在一起。
- 当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源 自动更新 页面的结构
- 当 表单元素的值发生变化时,也会被 VM 监听到,VM 会把变化过后最新的值 自动同步 到 Model 数据源中
1.5 vue 的版本
当前,vue 共有3个大版本,其中:
2.x 版本的 vue 是目前企业级项目开发中的主流版本
3.x 版本的 vue 于 2020-9-19 发布,生态还不完善,尚未在企业级项目开发中普及和推广
1.x 版本的 vue 几乎被淘汰,不再建议学习与使用
总结:
3.x 版本的 vue 是未来企业及项目开发的趋势
2.x 版本的 vue 在未来的(1-2年内)会被逐渐淘汰
2. vue 的指令
2.1 指令的概念
指令(Directives)是 vue 为开发者提供的模板语法,用于 辅助开发者渲染页面的结构。
vue 中的指令 按照不同的用途 可以分为 6 大类:
- 内容渲染 指令
- 属性绑定 指令
- 事件绑定 指令
- 双向绑定 指令
- 条件渲染 指令
- 列表渲染 指令
注意:指令是 vue 开发中最基础、最常用、最简单的 知识点。
2.2 内容渲染指令
内容渲染指令 用来辅助开发者渲染 DOM元素的文本内容。常用的内容渲染指令有如下3个:
- v-text
- 缺点:会覆盖元素内部原有的内容
- {{ }}
- 优点:专门用来解决 v-text 会覆盖默认文本内容的问题,在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容
- v-html
- 优点:v-text和 插值表达式 只能渲染 纯文本内容,如果把包含 HTML 标签的字符串 渲染为页面的 HTML 元素,则需要用到 v-html 这个指令. 可以把带有标签的字符串,渲染成真的 HTML 内容
2.3 属性绑定指令
注意:插值表达式只能用在元素的 内容节点 中,不能用在元素的属性节点中!
-
如果需要 为元素的属性 动态绑定 属性值,则需要用到 v-bind 属性绑定指令 v-bind
-
简写为 英文
:
-
在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:
<div :title="'box' + index"> 这是一个div </div>
使用 JavaScript 表达式
在 vue 提供的模板渲染法中,除了支持 绑定简单的数据值之外,还 支持JavaScript表达式的运算,例如:
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id = " 'list-' + id "></div>
2.4 事件绑定指令
vue 提供了 v-on 事件绑定指令,用来辅助程序员为 DOM 元素绑定事件监听。
-
v-on
简写是@
-
语法格式为:
<button @click="add"></button> methods: { add() { // 如果在方法中要修改 data 中的数据,可以通过 this 访问到 this.count += 1 } }
-
$event
的应用场景,如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event,例如:<button @click="add(3,$event)"></button> methods: { add(n,e){ // 如果在方法中要修改 data 数据,可以通过 this 访问到 this.count += n } }
-
事件修饰符:
.prevent
<a @click.prevent = "xxx">链接</a>
.stop
<button @click.stop="xxx"></button>
-
按键修饰符
在监听 键盘事件 时,我们经常需要 判断详细的按键。此时,可以为 键盘相关的事件 添加 案件修饰符,例如:<!-- 只有在 'key' 是 'Enter' 时调用 'vm.submit()' --> <input @keyup.enter='submit'/> <!-- 只有在 'key' 是 'Esc' 时调用 'vm.clearInput()' --> <input @keyup.esc='clearInput'/>
2.5 双向绑定指令
vue 提供了 v-model
双向数据绑定 指令,用来辅助开发者在 不操作DOM 的前提下,快速获取表单的数据。
<p>用户名是:{{username}}</p>
<input type="text" v-model="username"/>
<p>选中的省份是:{{province}}</p>
<select v-model="province">
<option value="">请选择</option>
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">南京</option>
</select>
data: {
return:{
province: ''
}
}
-
v-model 指令的修饰符
为了方便对用户输入的内容进行处理,vue 为 v-model 指令提供了 3 个修饰符,分别是:
.number
:自动将用户输入值转为数值类型<input v-model.number = "age"/>
.trim
:自动过滤用户输入的首尾空白字符<input v-model.trim = "msg"/>
.lazy
:在 “change” 时而非 “input” 时更新<input v-model.lazy = "msg"/>
示例用法如下:
<input type = "text" v-model.number = "n1"/> + <input type = "text" v-model.number = "n2"/> = <span> {{ n1 + n2 }}
2.6 条件渲染指令
条件渲染指令用来辅助开发者 按需求控制 DOM 的显示与隐藏。条件渲染指令有如下两个,分别是:
v-if
的原理:每次动态创建或移除元素,实现元素的显示与隐藏- 如果刚进入页面的时候,某些元素默认不需要被展示,而且后期这个元素也很可能不需要被展示出来,此时 v-if 性能更好
v-show
的原理:动态为元素添加或移除display: none
样式,来实现元素的显示与隐藏- 如果要频繁的切换元素的显示状态,用 v-show 性能更好
示例用法如下:
<div id = "app">
<p v-if = "networkState === 200" >请求成功 --- 被 v-if 控制</p>
<p v-show = "networkState === 200" >请求成功 --- 被 v-show 控制</p>
</div>
v-else-if
充当 v-if 的 “else-if 块”,可以连续使用:
<div v-if = "type === A">优秀</div>
<div v-else-if = "type === B">良好</div>
<div v-else-if = "type === C">一般</div>
<div v-else>差</div>
注意:v-else-if 指令必须配合 v-if 指令一起使用,否则它将不会被识别!
2.7 列表渲染指令
vue 提供了 v-for 列表渲染指令,用来辅助开发者 基于一个数组来循环渲染一个列表结构。v-for 指令需要使用 item in items
形式的特殊语法,其中:
-
items
是待循环的数组 -
item
是被循环的每一项data: { list: [ {id: 1,name: 'zs'} {id: 2,name: 'ls'} ] } // ---------------------分割线----------------------- <ul> <li v-for = "item in items">姓名是:{{ item.name }}</li> </ul>
-
v-for 指令还支持一个 可选的 第二个参数,即 当前项的索引。语法格式为
(item,index) in items
,示例代码如下:data: { list: [ {id: 1,name: 'zs'} {id: 2,name: 'ls'} ] } // ---------------------分割线----------------------- <ul> <li v-for = "(item,index) in items">姓名是:{{ item.name }}</li> </ul>
注意:v-for 指令中的 item项 和index索引 都是形参,可以根据需要进行 重命名。例如(user,i)in userList。
-
官方建议:只要用到了 v-for 指令,那么一定要绑定一个
:key
属性,而且,尽量把
id
作为 key 的值。官方对 key 的值类型,是有要求的:字符串或数字类型。
key 的值是不能重复的,否则会 终端报错:
Duplicate keys detected
-
key 的注意事项
- key 的值只能是 字符串 或 数字 类型
- key 的值必须具有唯一性(即:key的值不能重复)
- 建议把
数据项id
属性的值 作为key的值(因为id属性的值具有唯一性) - 使用 index 的值 当作 key 的值 没有任何意义(因为 index 的值不具有唯一性)
- 建议使用 v-for 指令时 一定要指定key的值(既提升性能,又防止列表状态紊乱)
总结:
-
能够知道 vue 的基本使用步骤
- 导入 vue.js 文件
- new Vue()构造函数,得到 vm 实例对象
- 声明 el 和 data 数据节点
- MVVM 的对应关系
-
掌握 vue 中常见 指令 的基本用法
- 插值表达式、v-bind、v-on、v-if 和 v-else
- v-for 和 :key、v-model
- 掌握 vue 中 过滤器 的基本用法
3. 过滤器
3.1 什么是过滤器
过滤器是 vue 为开发者提供的功能,常用于 文本的格式化。过滤器可以用在两个地方:插值表达式 和 v-bind 属性绑定。
过滤器应该被添加在 JavaScript 表达式的 尾部,由 “管道符” 进行调用,示例代码如下:
<!-- 在双花括号中通过“管道符”调用 capitalize 过滤器,对 message 的值进行格式化 -->
<p>
{{ message | capitalize }}
</p>
<!-- 在 v-bind 中通过 “管道符” 调用 formatId 过滤器,对 rawId 的值进行格式化 -->
<div v-bind: id = "rawId | formatId"></div>
特点:
-
过滤器函数,必须被定义到 filters 节点之下
-
过滤器本质上是函数
-
注意:过滤器函数形参中的 val,永远是 ”管道符“ 前面的那个值
-
强调:过滤器中,一定要有一个返回值
-
字符串有个
charAt
方法,这个方法接收索引值,表示从字符串中把索引对应的字符,获取出来
3.2 私有过滤器和全局过滤器
在 filters 节点下定义的过滤器,称为 “私有过滤器”,因为它 只能在当前 vm 实例所控制的 el 区域内使用。如果希望在多个 vue实例之间共享过滤器,则可以按照如下的格式定义 全局过滤器:
// 全局过滤器 - 独立于每个 vm 实例之外
// Vue.filter() 方法接收两个参数
// 第 1 个参数,是全局过滤器的“名字”
// 第 2 个参数,是全局过滤器的“处理函数”
Vue.filter('capitalize',(str) => {
return str.charAt(0).toUpperCase() + str.slice(1) + "--"
})
- 如果 全局过滤器和私有过滤器名字一致,此时按照 “就近原则”,调用的是 “私有过滤器”
- 基本上都是用的全局过滤器
声明 格式化时间 的全局过滤器
// 1. 对 time 进行格式化处理,得到 YYYY-MM-DD
// 2. 把格式化的结果,return 出去
// 调用 day.js 文件
// 直接调用 day.js 得到的是当前的时间
// day.js(给定的日期) 得到指定的日期
Vue.filter('dataFormat',function(time){
const dtStr = day.js(time).format('YYYY-MM-DD HH-mm-ss')
return dtStr
})
3.3 连续调用多个过滤器
过滤器可以 串联地 进行调用,例如:
<!-- 把 message 的值,交给 filterA 进行处理 -->
<!-- 把 filterA 处理的结果,再交给 filterB 进行处理-->
<!-- 最终把 filterB 处理的结果,作为最终的值渲染到页面上 -->
{{ message | filterA | filterB }}
3.4 过滤器传参
过滤器的本质是 JavaScript 函数,因此可以接收参数,格式如下:
<!-- arg1 和 arg2 是传递给 filterA 的参数 -->
<p>{{ message | filterA(arg1,arg2) }}</p>
// 过滤器处理函数的形参列表中:
// 第一个参数:永远都是“管道符”前面待处理的值
// 从第二个参数开始,才是调用过滤器时传递过来的 arg1 和 arg2 参数
Vue.filter('filterA',(msg,arg1,arg2)=>{
// 过滤器的代码逻辑...
})
3.5 过滤器的兼容性
过滤器仅在 vue2.x 和 1.x 中受支持,在 vue3.x 的版本中 剔除了过滤器 相关的功能。
在企业级项目开发中:
- 如果使用的是 2.x 版本的 vue,则依然可以使用过滤器相关的功能
- 如果项目已经升级到了 3.x 版本的 vue,官方建议使用 计算属性 或 方法 代替被剔除的过滤器功能
具体的迁移指南,请参考 vue3.x 的官方文档给出的说明:https://v3.vuejs.org/guide/migration/filters.html#migration-strategy
4. 侦听器
4.1 什么是侦听器
watch 侦听器 允许开发者监视数据的变化,从而 针对数据的变化做特定的操作。
语法格式如下:
const vm = new Vue({
el: "#app",
data: {username: ''},
watch: {
// 监听 username 值的变化
// newVal 是 “变化后的新值”,oldVal 是“变化之前的旧值”
username(newVal,oldVal){
if (newVal === '') return
//console.log(newVal,oldVal)
// 1. 调用 jQuery 中的 Ajax 发起请求,判断 newVal 是否被占用
$.get('https://www.escook.cn/api/finduser' + newVal,function(result){
consolt.log(result)
})
}
}
})
- 所有的侦听器,都应该被定义到 watch 节点下
- 侦听器本质上是一个函数,要侦听哪个数据的变化,就把数据作为方法名即可
- 新值在前,旧值在后
- 应用场景:判断用户名是否被占用
4.2 侦听器的格式
方法格式的侦听器
- 缺点1:无法在刚进入页面的时候,自动触发!!!
- 缺点2:如果侦听的是一个对象,如果对象的属性发生了变化,不会触发侦听器!!!
对象格式的侦听器
好处:可以通过 immediate 选项,让侦听器自动触发!!!
示例代码如下:
const vm = new Vue({
el: "#app",
data: {username: 'admin'},
watch: {
// 定义对象格式的侦听器
username: {
// 侦听器的处理函数
handler(newVal,oldVal){
console.log(newVal,oldVal)
},
// immediate 选项的默认值为 false
// immediate 的作用是,控制侦听器是否自动触发一次!
immediate: true
}
}
})
4.3 深度侦听
const vm = new Vue({
el: "#app",
data: {
info:{
username: 'admin'
}
},
watch: {
// 定义对象格式的侦听器
info: {
// 侦听器的处理函数
handler(newVal,oldVal){
console.log(newVal,oldVal)
},
// 开启深度监听,只要对象中任何一个属性变化了,都会触发 对象的侦听器
deep: true
}
}
})
4.4 只想监听单个属性的变化
const vm = new Vue({
el: "#app",
data: {
info:{
username: 'admin'
}
},
watch: {
// 定义对象格式的侦听器
// 如果要侦听的是对象的子属性变化,则必须包裹一层单引号
'info.username': {
handler(newVal){
console.log(newVal)
}
}
}
})
5. 计算属性
5.1 什么是计算属性
计算属性指的是 通过一系列运算 之后,最终得到一个 属性值。
这个动态计算出来的属性值 可以被模板结构或 methods 方法使用。示例代码如下:
var vm = new Vue({
el: '#app',
data: {
r: 0, g: 0, b: 0
},
computed: {
rgb() { return 'rgb(${this.r,${this.g},${this.b})' }
},
methods: {
show() {console.log(this.rgb)}
}
})
- 模板字符串
:style
代表动态绑定一个样式对象,它的值是一个 { } 样式对象- 所有的计算属性,都要定义到 computed 节点之下
- 计算属性在定义的时候,要定义成”方法格式“
- 声明的时候 是一个方法格式,用的时候则是 属性格式
好处:
- 实现了代码的复用
- 只要计算属性中,依赖的数据源变化了,则计算属性会自动重新求值!
6. axios
6.1 什么是 axios
专注于 数据库请求 的库。
Vue、React 课程中都会用到 axios 来请求数据。
中文官网地址:[http://ww.axios-js.com/]
英文官网地址:[https://www.npmjs.com/package/axios]
6.2 基础用法如下:
axios({
method: '请求的类型',
url: '请求的 URL 地址'
}).then((result)=>{
// .then 用来指定请求成功之后的回调函数
// 形参中的 result 是请求成功之后的结果
})
-
调用 axios 方法得到的返回值是 Promise
-
axios 在请求到数据之后,在真正的数据之外,套了一层壳
{ config: {}, data: {真实的数据}, headers: {}, request: {}, status: xxx, statusTest: '' }
6.3 传参
- params: { } // URL中的查询参数
- data: { } // 请求体参数
6.4 基本使用
-
发起 Get 请求
axios({ // 请求方式 method: 'GET', // 请求的地址 url: 'http://www.lfdkfk/api/getCats', // URL 中的查询参数 params: { id: 1 } }).then((result)=>{ consol.log(result) })
-
发起 Post 请求
axios({ // 请求方式 method: 'POST', // 请求的地址 url: 'http://www.lfdkfk/api/getCats', // URL 中的查询参数 data: { name: 'zs', age: 20 } }).then((result)=>{ consol.log(result) })
6.5 async 和 await 使用
- 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await !
- await 只能用在被 async “修饰” 的方法中
6.6 解构赋值
-
调用 axios 之后,使用 async 和 await 进行简化
-
使用解构赋值,从 axios 封装的大对象中,把 data 属性解构出来
-
把解构出来的 data 属性,使用 冒号 进行重命名,一般都重命名为 { data: res }
async loadMoviesList() { // 结构赋值的时候, 使用 : 进行重命名 const { data: res } = await axios({ method: 'GET', url: 'http://www.fakdfalfk/api/getMovies' }) }
6.7 基于 axios.get 和 axios.post 发起请求
-
get 请求
async loadMoviesList(){ const { data: res } = await axios.get('http://www.fjdkfj/api/getMovies',{ params: { id: 1 } }) console.log(res.data) }
-
post 请求
async loadMoviesList(){ const { data: res } = await axios.post('http://www.fjdkfj/api/getMovies',{ name: 'zs', age: 20 }) console.log(res.data) }
7. vue-cli
7.1 什么是单页面应用程序
单页面应用程序(Single Page Application)简称 SPA,顾名思义,指的是 一个Web 网站中只有唯一的一个HTML页面,所有的功能与交互都在这唯一的一个页面内完成。
7.2 什么是 vue-cli
vue-cli 是 Vue.js 开发的标准工具。它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程。
引用 vue-cli 官网上的一句话:
程序员可以 专注在撰写应用上,而不必 花好几天 去 纠结 webpack 配置的问题。
中文官网:https://cli.vuejs.org/zh/
7.3 安装和使用
vue-cli 是 npm 上的一个 全局包
,使用 npm install
命令,即可方便的把它安装到自己的电脑上:
npm install -g @vue/cli
vue 项目中 src 目录的构成:
assets文件夹:存放项目中用到的静态资源文件,例如:css 样式表、图片资源
components文件夹:程序员封装的、可复用的组件,都要放到 components 目录下
main.js 是项目的入口文件,整个项目的运行,要先执行 main.js
App.vue 是项目的根组件
7.4 vue 项目运行流程
在工程化项目中,vue 要做的事情很单纯:通过 main.js 把 App.vue 渲染到 index.html 的指定区域中。
其中:
- App.vue 用来编写待渲染的 模板结构
- index.html 中需要预留一个 el 区域
- main.js 把 App.vue 渲染到了 index.html 所预留的区域中
8. vue 组件
8.1 什么是组件化开发
组件化开发指的是:根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护。
vue 是一个 支持组件化开发 的前端框架。
vue 中规定:组件的后缀名是 .vue
。之前接触到的 App.vue 文件本质上就是一个 vue 的组件。
8.2 vue 组件的三个组成部分
每个 .vue 组件都由 3 部分构成,分别是:
- template -> 组件的模板结构
- script -> 组件的 JavaScript 行为
- style -> 组件的样式
注意:默认导出,这是固有写法!
注意:data 数据源
注意:.vue 组件中的 data 不能像之前一样,不能指向对象
注意:组件中的 data 必须是一个函数
// 默认导出,这是固有写法!
export default {
// data 数据源
// .vue 组件中的 data 不能像之前一样,不能指向对象
// 组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 { }中,可以定义数据
return{
username: 'admin'
}
}
}
8.3 在组件中定义 methods 方法
<template>
<div>
<h3>这是用户自定义的 Test.vue --- {{ username }}</h3>
<button @click = "changeName">修改用户名</button>
</div>
</template>
<script>
// 默认导出,这是固有写法!
export default {
// data 数据源
// .vue 组件中的 data 不能像之前一样,不能指向对象
// 组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 { }中,可以定义数据
return{
username: 'admin'
}
},
methods: {
changeName() {
// 在组件中,this 就表示当前组件的实例对象
console.log(this);
this.username = '爽歪歪';
}
},
// 当前组件中的侦听器
watch: { },
// 当前组件中的计算属性
computed: { },
// 当前组件中的过滤器
filters: { }
}
</script>
注意:在组件中,this 就表示当前组件的实例对象
8.4 启用 less 语法 以及 唯一根节点
上来首先就在 template 中定义 一个 div 标签
<template>
<div></div>
</template>
想要用 less 语法,需要在 style 标签中 加入 lang = "less"
<style lang = "less">
</style>
8.5 组件之间的关系
组件在被封装好之后,彼此之间是相互独立的,不存在父子关系。
在使用组件的时候,根据彼此之间的嵌套关系,形成了 父子关系、兄弟关系。
8.6 在项目中使用 组件的三个步骤
步骤一:使用 import 语法 导入需要的组件
步骤二:使用 components 节点注册组件
步骤三:以 标签形式 使用刚才注册的组件
示例如下:
<template>
<div>
<!-- 3. 使用组件 -->
<Left></Left>
</div>
</template>
<script>
// 1. 导入需要的vue组件
import Left from '@/components/Left.vue'
export default: {
// 2. 注册组件
components: {
Left; //(全写应该是一个键值对:Left:'Left')
}
}
</script>
注意:
@
指的是./src
在 webpack.config.js 中
module.exports = {
resolve: {
alias: {
@:path.join(__dirname,'./src')
}
}
}
8.7 在 VScode 中配置 @ 路径提示的插件
步骤一:需要装一个插件:Path Autocomplete
步骤二:设置 --> settings.json,加入两句话
-
// 导入文件时 是否携带文件的扩展名:“path-autocomplete.extensionOnImport”:true,
-
// 配置 @ 的路径提示:“path-autocomplete.pathMappings”:{ “@”: “${folder}/src”}
8.8 使用 Vue.component 全局注册组件
原因:通过 components 注册的组件是 私有子组件
例如:在 组件A 的 components 节点下,注册了 组件F。则 组件F 只能用在 组件A 中,不能被用在 组件C 中。
思考问题:
- 为什么 F 不能用在 组件C 中?
- 怎样 才能在 组件C 中使用 F ?
在 vue 项目的 main.js 入后文件中,通过 Vue.component() 方法,可以注册全局组件。示例代码如下:
// 1. 导入需要全局注册的组件
import Count from '@/components/Count.vue'
// 2. 调用 Vue中的 component方法
// 参数1:字符串格式,表示组件的“注册名称”
// 参数2:需要被全局注册的那个组件
Vue.component('MyCount',Count);
8.9 auto close tag 自动闭合标签的插件
三个经常用的插件:
- vetur
- vue3 Snippets
- Auto Close Tag
9. 动态组件
9.1 什么是动态组件?
动态组件指的是动态切换组件的显示与隐藏。
9.2 如何实现动态组件渲染
vue 提供了一个内置的 组件,专门用来实现动态组件的渲染。实例如下:
data() {
// 1. 当前要渲染的组件名称
return { comName: 'Left' }
}
<!-- 2.通过 is 属性,动态指定要渲染的组件 -->
<component :is ="comName"></component>
<!-- 3. 点击按钮,动态切换组件的名称 -->
<button @click="comName = 'Left'">展示Left组件</button>
<button @click="comName = 'Right'">展示Right组件</button>
第一个大写 直接 shit + 字母 Sift
10. 路由
10.1 什么是路由
路由 就是对应关系。
10.2 SPA 与前端路由
SPA:单页面应用程序。
10.3 什么是前端路由
Hash 地址 与 组件 之间的 对应关系。
10.4 前端路由的工作方式
- 用户点击了页面上的链接
- 导致了 URL 地址栏中的 Hash值 发生了变化
- 前端路由监听到了 Hash 地址的变化
- 前端路由把当前 Hash地址对应的组件 渲染到浏览器中
10.5 vue-router 的基本使用
10.5.1 什么是 vue-router
vue-router 是 vue.js 官方给出的 路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目中组件的切换。
vue-router 的官方文档地址:[https://router.vuejs.org//zh/]
10.5.2 vue-router 安装和配置的步骤
- 安装 vue-router包
- 创建路由模块
- 导入并挂载路由模块
- 声明路由链接和占位符
10.5.3 在项目中安装 vue-router
在 vue2 的项目中,安装 vue-router 的命令如下:
npm i vue-router@3.5.2 -S
10.5.4 创建路由模块
在 src 源代码目录下,新建 router/index.js 路由模块,并初始化如下的代码:
// 1. 导入 Vue 和 VueRouter 的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 2. 调用 Vue.use() 函数,把 VueRouter 安装为 Vue 的插件
Vue.use(VueRouter)
// 3. 创建路由的实例对象
const router = new VueRouter({
// routes 是一个数组,作用:定义“hash地址”与“组件”之间的对应关系
// 路由规则
routes:[
{path: "/home",component: Home},
{path: "/about",component: About},
{path: "/movie",component: Movie}
]
})
// 4. 向外共享路由的实例对象
export default router
小记:在进行模块化导入的时候,如果给定的是文件夹,默认导入的是这个文件夹下,名字叫 index.js 文件
当安装和配置了 vue-router 后,就可以用 router-link 来代替普通的 a 链接了
原来:< a href = “#/home” > 首页
现在:< router-link to= “/home”>首页
10.5.5 router-view
- 只要在项目中安装和配置了 vue-router,就可以使用 router-view 这个组件了
- 作用很单纯,占位符
10.5.6 路由重定向
路由重定向 指的是:用户在访问 地址 A 的时候,强制用户跳转 到地址 C ,从而展示特定的组件页面。通过路由规则的 redirect
属性,指定一个新的路由地址,可以很方便的设置路由的重定向:
const router = new VueRouter({
// 在 routes 数组中,声明路由的匹配规则
routes: [
{path: '/',redirect: '/home'}
{path: '/home',component: Home}
{path: '/movie',component: Movie}
{path: '/about',component: About}
]
})
10.5.7 嵌套路由
通过路由实现 组件的嵌套展示,叫做嵌套路由。
- 模板内容中又有 子级路由链接
- 点击 子级路由链接 显示 子级模板内容
通过 children
属性声明 子路由规则
在 src/router/index.js 路由模块中,导入需要的组件,并使用 children属性 声明子路由规则:
import Tab1 from '@/component/tabs/Tab1.vue'
import Tab2 from '@/component/tabs/Tab2.vue'
const router = new VueRouter({
routes: [
{ // about 页面的路由规则(父级路由规则)
path: '/about',
component: About,
children: [ // 1.通过children属性,嵌套声明子路由规则
{path: 'tab1',component: Tab1}//2.访问/about/tab1时,展示Tab1组件
{path: 'tab2',component: Tab2}//2.访问/about/tab2时,展示Tab2组件
]
}
]
})
注意:children 路由下,path 路径不要加
/
默认子路由,如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做 ”默认子路由“ === redirect : “” ,示例如下:
{path: ’ ', component: Tab1} === redirect: ‘/about/tab1’
10.5.8 动态路由匹配
问题:路由规则的复用性差。
动态路由指的是:把 Hash 地址中 可变的部分 定义为 参数项,从而 提高路由规则的复用性。
在 vue-router 中使用 英文的冒号 :
来定义路由的参数项。示例代码如下:
// 路由中的动态参数以 : 进行声明,冒号后面的是动态参数名称
{ path: '/movie/:id', component: Movie }
// 将以下 3 个路由规则,合并成了一个,提高了路由规则的复用性
{ path: '/movie/1', component: Movie }
{ path: '/movie/2', component: Movie }
{ path: '/movie/3', component: Movie }
10.5.9 $route 和 $router
this.$route
是路由的 “参数对象”
this.$router
是路由的 “导航对象”
10.5.10 为路由规则开启 props 传参
// 可以为路由规则开启 props 传参,从而方便的拿到 动态参数的值
{ path: '/movie/:mid', component: Movie, props: true }
export default {
name: 'Movie',
// 接收 props 数据
props: ['mid']
}
10.5.11 路径参数 和 查询参数
注意1:在 hash 地址中,
/
后面的参数项,叫做 ”路径参数“
在路由 ”参数对象“ 中,需要使用 this.$route.params
来访问路径参数
注意2:在 hash 地址中,
?
后面的参数项,叫做 ”查询参数“
在路由 ”参数对象“ 中,需要使用 this.$route.query
来访问查询参数
10.5.12 path 和 fullPath
注意3:在
this.$route
中,path 只是经过部分,fullPath 是完整的地址
10.5.13 声明式导航 & 编程式导航
在浏览器中,点击链接 实现导航的方式,叫做 声明式导航。例如:
- 普通网页中点击链接、vue 项目中点击都属于声明式导航
在浏览器中,调用 API方法 实现导航的方式,叫做 编程式导航。例如:
- 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
10.5.14 vue-router 中的编程式导航 API
vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:
-
this.$router.push('hash地址')
作用:跳转到指定hash地址,并增加一条历史记录
-
this.$router.replace('hash地址')
作用:跳转都指定hash地址,并替换到当前的历史记录
-
this.$router.go(数值n)
作用:调用 this.$router.go()方法,可以在浏览历史中前进和后退。示例代码如下:
<template> <h3>MyMovie组件 --- {{id}}</h3> <button @click="goBack">后退</button> </template> <script> export default{ // props: ['id'], methods: { goBack(){this.$router.go(-1)} //后退到之前的组件页面,后退一层 } } </script>
在实际开发中,一般只会前进和后退一层页面。因此 vue-router 提供了如下两个便捷方法:
$router.back()
: 在历史记录中,后退到上一个页面
$router.forward()
:在历史记录中,前进到下一个页面
可以简写到行内:
<button @click="this.$router.back()">后退</button>
<button @click="this.$router.forward()">前进</button>
11.导航守卫
11.1 目的
目的:可以控制路由的访问权限。
11.2 全局前置守卫
每次发生路由的 导航跳转 时,都会触发 全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行 访问权限 的控制:
// 创建路由实例对象
const router = new VueRouter({...})
// 调用路由实例对象的 beforeEash 方法,即可声明 “全局前置守卫”
// 每次发生路由导航跳转的时候,都会自动触动 fn 这个 “回调函数”
router.beforeEach(fn)
11.3 守卫方法的三个形参
全局前置守卫的回调函数中接收 3 个形参,格式为:
// 创建路由实例对象
const router = new VueRouter({...})
// 全局前置守卫
router.beforeEach((to,from,next)=>{
// to 是将要访问的路由的信息对象
// from 是将要离开的路由的信息对象
// next 是一个函数,调用 next() 表示放行,允许这次路由导航
// 分析:
// 1. 要拿到用户将要访问的 hash 地址
// 2. 判断 hash 地址是否等于 /main
// 2.1 如果等于 /main,证明需要登陆之后,才能访问成功
// 2.2 如果不等于 /main,则不需要登陆,直接放行 next()
// 3. 如果访问的地址是 /main,则需要读取 localstorage 中的 token 值
// 3.1 如果有 token 则放行
// 3.2 如果没有 token 则强制跳转到 /login 登陆页面
})
11.4 next 函数的 3 种调用方式
当前用户 拥有 后台主页的访问权限,直接放行:next()
当前用户 没有 后台主页的访问权限,强制其跳转到登陆页面:next(’/login’)
当前用户 没有 后台主页的访问权限,不允许跳转到后台主页:next(false)
12. Props
12.1 什么是 props
props 是组件的 自定义属性,在 封装通用组件 的时候,合理地使用 props 可以极大的 提高组件的复用性!
语法格式如下:
<script>
export default {
// 组件的自定义属性
props: ['自定义属性A','自定义属性A','其它自定义属性...'],
// 组件的私有数据
data() {
return {
}
}
}
</script>
示例:
Count.vue
<template>
<div>
<h5>Count 组件</h5>
<p>count的值是:{{ init }}</p>
<button @click="count += 1"> +1 </button>
</div>
</template>
<script>
// props 是自定义属性,允许使用者通过自定义属性,为当前组件指定初始值
// 自定义属性的名字,是封装者自定义的 {只要名称合法就行}
props: ['init'],
data() {
return {
count: 0
}
}
</script>
Left.vue
<template>
<div>
<h3>Left 组件</h3>
<hr/>
<Count init="6"></Count>
</div>
</template>
<script>
export default {
data() {
return {
}
}
}
</script>
Right.vue
<template>
<div>
<h3>Right 组件</h3>
<hr/>
<Count init="9"></Count>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
12.2 结合 v-bind 使用自定义属性
组件的封装者 vs 组件的使用者
在 v-bind 中写的是 js 表达式
<Count init="9"></Count> // 此时 9 是字符串
<Count :init="9"></Count>// 此时 9 是数字
12.3 props 是只读的
props 中的数据,可以直接在模板结构中被使用。
<template>
<div>
<h5>Count 组件</h5>
<p>count的值是:{{ init }}</p>
<button @click="init += 1"> +1 </button> // 这种写法是错误的!!!
</div>
</template>
<script>
// props 是自定义属性,允许使用者通过自定义属性,为当前组件指定初始值
// 自定义属性的名字,是封装者自定义的 {只要名称合法就行}
// props 中的数据,可以直接在模板结构中被使用
// 注意:props是只读的,不要直接修改 props 的值,否则终端会报错!
props: ['init'],
data() {
return {
count: 0
}
}
</script>
vue规定:组件中封装的自定义属性是 只读的,程序员 不能直接修改 props 的值。否则会直接报错。
问题:如何修改 props 属性值?
方法:要想修改 props 的值,可以把 props 的值转存到 data 中,因为 data 中的数据都是 可读写的。
<template>
<div>
<h5>Count 组件</h5>
<p>count的值是:{{ count }}</p>
<button @click="count += 1"> +1 </button> // 这种写法是正确的。。。
</div>
</template>
<script>
// props 是自定义属性,允许使用者通过自定义属性,为当前组件指定初始值
// 自定义属性的名字,是封装者自定义的 {只要名称合法就行}
// props 中的数据,可以直接在模板结构中被使用
// 注意:props是只读的,不要直接修改 props 的值,否则终端会报错!
props: ['init'],
data() {
return {
count: this.init
}
}
</script>
12.4 props 的 default 默认值
在声明自定义属性时,可以通过 default 来 定义属性的默认值。示例代码如下:
<script>
export default {
props: {
init: {
// 用default 属性定义属性的默认值
default: 0
}
}
}
</script>
将 props 指向一个对象:
<script>
export default {
// props: ['init']
props: {
// 自定义属性 A:{ /* 配置选项 */},
// 自定义属性 B:{ /* 配置选项 */},
// 自定义属性 C:{ /* 配置选项 */},
init: {
// 如果外界使用 Count 组件的时候,没有传递 init,则默认 init为0
default: 0
}
}
}
</script>
12.5 props 的 type 值类型
在声明自定义属性时,可以通过 type 来 定义属性的值类型。示例代码如下:
<script>
export default {
// props: ['init']
props: {
init: {
// 用 default 属性定义属性的默认值
default: 0,
// 用 type 属性定义属性的值类型
// 如果传递过来的值不符合此类型,则会在终端报错
type: Number
}
}
}
</script>
12.6 props 的 required 必填项
必填项校验,如果没传定义的属性,则会报错。
<script>
export default {
// props: ['init']
props: {
init: {
// 用 default 属性定义属性的默认值
default: 0,
// 用 type 属性定义属性的值类型
// 如果传递过来的值不符合此类型,则会在终端报错
type: Number,
required: true
}
}
}
</script>
13. 组件之间样式冲突
13.1 组件之间的样式冲突问题
默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成 多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因是:
- 单页面应用程序中,所有组件的 DOM 结构,都是基于 唯一的 index.html 页面进行呈现的
- 每个组件中的样式,都会 影响整个 index.html 页面中的 DOM 元素
**思考:**如何解决样式冲突问题?
方法:通过scoped:自动为每一个标签生成 data-v-001
13.2 使用 deep 修改子组件中的样式
思考:如何在 父组件中 修改 子组件 样式?
方法:通过 deep
应用场景:当使用第三方组件库的时候,如果有修改 第三方组件默认样式的需求,需要用到 /deep/
<style class="less" scoped>
// h5[data-v-001]
// [data-v-001] h5
/deep/ h5 {
}
</style>
14. 组件之间的数据共享
14.1组件之间的关系
在项目开发中,组件之间的 最常见的关系 分为以下两种:
- 父子关系
- 兄弟关系
14.2 数据共享
14.2.1 父组件向子组件数据共享
父组件向子组件共享数据需要使用 自定义属性。示例代码如下:
父组件:
<template>
<div>
<Son :msg="message" :user="userInfo"></Son>
</div>
</template>
<script>
export default{
data() {
return {
message: 'hello vue.js',
userinfo: { name:'zs', age: 20 }
}
}
}
</script>
子组件:
<template>
<div>
<h5>Son 组件</h5>
<p>父组件传递过来的 msg 值是: {{ msg }}</p>
<p>父组件传递过来的 user 值是: {{ user }}</p>
</div>
</template>
<script>
props: ['msg','user']
</script>
14.2.2 不要修改 props 的值
不建议修改 props数组中 值
想要修改,需要转存一份。
14.2.3 子向父传值
子组件 向 父组件共享数据使用 自定义事件。示例代码如下:
子组件:
<script>
export default {
data() {
return {
count: 0
}
},
method: {
add() {
this.count += 1;
// 修改数据时,通过 $emit() 触发自定义事件
this.$emit('numchange',this.count)
}
}
}
</script>
父组件:
<template>
<div>
<Son @numchange="getNewCount"></Son>
</div>
</template>
<script>
export default {
data() {
return {
countFromSon: 0
}
},
method: {
getNewCount(val) {
this.countFromSon = val
}
}
}
</script>
15. ref引用
vue 优势:MVVM。数据驱动视图。
假设:在 vue 中,需要操作 DOM 了,需要拿到页面上某个 DOM 元素的引用,此时该怎么办?
15.1 什么是 ref 引用
ref 用来辅助开发者在 不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,组件的 $refs 指向一个空对象。示例如下:
父组件:
<template>
<div>
<h1 ref="myh12">App 根组件</h1>
<button @click="showThis">打印 this</button>
<button @click="onReset">重置 Left 组件的 count 值为 0</button>
<hr/>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件-->
<Left ref="comeLeft"> </Left>
</div>
</div>
</template>
<script>
import Left from '@/component/Left.vue'
export default {
methods: {
showThis() {
// this 是当前 App 组件的实例对象
console.log(this)
this.$refs.myh12.style.color = 'red'
},
onReset() {
// this.$refs.comLeft.resetCount = 0
this.$refs.comLeft.count = 0
}
}
}
</script>
子组件:
<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>
16. 了解 this.$nextTick(cb) 方法
组件的 $nextTick(cb) 方法,会把 cb 回调 推迟到下一个 DOM 更新周期之后执行。通俗的理解就是:等组件的 DOM 更新完成之后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。