重点知识:
vue中常用的生命周期函数(created,mounted)
父→子(自定义属性);子→父(自定义事件);兄弟组件(EventBus)
使用 ref 引用 DOM 元素或组件
1 侦听器
1.1 watch 侦听器
- 什么是watch 侦听器
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。语法格式如下:
count vm=new Vue({
el:'#app',
data:{
username:''
},
watch:{
//监听 uaername 值的变化
//newVal 是"变化后的新值",oldVal 是"变化之后的旧值"
username (newVal,oldVal){
console.log(newVal,oldVal)
}
},
})
- 使用watch 检测用户名是否可用
监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用:
watch: {
// 监听 username 值的变化
async username(newVal) {
if (newVal === '') return
// 使用 axios 发起请求,判断用户名是否可用
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)
console.log(res)
}
}
- immeddiate 选项
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使用immediate 选项。示例代码如下:
watch: {
username: {
// handler 是固定写法,表示当 username 的值变化时,自动调用 handler 处理函数
handler: async function (newVal) {
if (newVal === '') return
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)
console.log(res)
},
// 表示页面初次渲染好之后,就立即触发当前的 watch 侦听器
immediate: true
}
}
- deep选项
如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项,代码示例如下:
count vm=new Vue({
el:'#app',
data:{
info: {usernaem:'admin'}
},
watch:{
info:{
handler(newVal){
console.log(newVal,oldVal)
},
deep:ture
}
},
})
- 监听对象单个属性的变化
如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器:
count vm=new Vue({
el:'#app',
data:{
info: {usernaem:'admin'}
},
watch:{
'info.username':{
handler(newVal){
console.log(newVal)
}
}
}
})
1.2 侦听器的格式
1.方法格式的侦听器
- 缺点1:无法在刚进入页面的时候,自动触发!!!
- 缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器!!!
2.对象格式的侦听器
- 好处1:可以通过 immediate 选项,让侦听器自动触发!!!
- 好处2:可以通过 deep 选项,让侦听器深度监听对象中每个属性的变化!!!
计算属性与侦听器的区别:计算属性侧重于监听多个值的变化,最终计算并返回一个新值;
侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值;
2. 计算属性
- 什么是计算属性
计算属性指的是通过一系列运算之后,最终得到一个属性值。这个动态计算出来的属性值可以被模板结构或 methods 方法使用。示例代码如下
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
// 红色
r: 0,
// 绿色
g: 0,
// 蓝色
b: 0
},
methods: {
// 点击按钮,在终端显示最新的颜色
show() {
console.log(`rgb(${this.r}, ${this.g}, ${this.b})`)
}
},
});
- 计算属性的特点
- 虽然计算属性在声明的时候被定义为方法,但是计算属性的本质是一个属性
- 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算
特点:
1.定义的时候,要被定义为“方法”
2.在使用计算属性的时候,当普通的属性使用即可
好处:
1.实现了代码的复用
2.只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值!
axios
axios 是一个专注于网络请求的库!
axios 的基本使用
- 发起 GET 请求:
axios({
// 请求方式
method: 'GET',
// 请求的地址
url: 'http://www.liulongbin.top:3006/api/getbooks',
// URL 中的查询参数
params: {
id: 1
}
}).then(function (result) {
console.log(result)
})
- 发起 POST 请求:
document.querySelector('#btnPost').addEventListener('click', async function () {
// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!
// await 只能用在被 async “修饰”的方法中
const { data: res } = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(res)
})
单页面应用程序(英文名:Single Page Application)简称 SPA,顾名
思义,指的是一个 Web 网站中只有唯一的一个 HTML 页面,所有的功能
与交互都在这唯一的一个页面内完成。
3. vue.cli
- 什么是 vue.cli
vue-cli 是 Vue.js 开发的标准工具。它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程。 - 安装和使用
vue-cli 是 npm 上的一个全局包,使用 npm install 命令,即可方便的把它安装到自己的电脑上
npm install -g @vue/cli //-g 表示全局包
基于 vue-cli 快速生成工程化的 Vue 项目:
vue create 项目的名称
vue 项目中 src 目录的构成:
assets 文件夹:存放项目中用到的静态资源文件,例如:css 样式表、图片资源
components 文件夹:程序员封装的、可复用的组件,都要放到 components 目录下
main.js 是项目的入口文件。整个项目的运行,要先执行 main.js
App.vue 是项目的根组件。
- vue项目的运行流程
在工程化的项目中,vue 要做的事情很单纯:通过 main.js 把 App.vue 渲染到 index.html 的指定区域中。其中:
① App.vue 用来编写待渲染的模板结构
② index.html 中需要预留一个 el 区域
③ main.js 把 App.vue 渲染到了 index.html 所预留的区域中
4. vue 组件
4.1 什么是组件化开发
组件化开发指的是:根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护。
4.2 vue 中的组件化开发
- vue 是一个支持组件化开发的前端框架。
- vue 中规定:组件的后缀名是 .vue。之前接触到的 App.vue 文件本质上就是一个 vue 的组件
4.3 vue 组件的三个组成部分
每个 .vue 组件都由 3 部分构成,分别是:
- template -> 组件的模板结构
- script -> 组件的 JavaScript 行为
- style -> 组件的样式
其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分。
- template
vue 规定:每个组件对应的模板结构,需要定义到<template>
节点中。
<template>
//DOM结构
<template/>
注意:
- template 是 vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素
- template 中只能包含唯一的根节点
- script
vue 规定:开发者可以在<script>
节点中封装组件的 JavaScript 业务逻辑。<script >
节点的基本结构如下
<script>
//今后组件相关的 data 数据,methods 方法
//都需要定义到 export default 所导出的对象中
export default {}
<script/>
vue 规定:.vue 组件中的 data 必须是一个函数,不能直接指向一个数据对象。会导致多个组件实例共用同一份数据的问题
- style
vue 规定:组件内的<style>
节点是可选的,开发者可以在<style>
节点中编写样式美化当前组件的 UI 结构。<script >
节点的基本结构如下:
<style>
h1 {
font-weight: normal;
}
</style>
在
<style>
标签上添加 lang=”less” 属性,即可使用 less 语法编写组件的样式
4.4 组件之间的关系(父子,兄弟)
- 使用组件的三个步骤
—步骤1:使用 import 语法导入需要的组件
—步骤2:使用 components 节点注册组件
—步骤3:以标签形式使用刚才注册的组件
- 通过 components 注册的是私有子组件
—在组件 A 的 components 节点下,注册了组件 F。则组件 F 只能用在组件 A 中;不能被用在组件 C 中
3. 注册全局组件
vue 项目的 main.js 入口文件中,通过 Vue.component() 方法,可以注册全局组件。示例代码如下
//导入需要全局注册的组件
import Count from '@/components/Count.vue'
// 参数1: 字符串格式,表示组件的"注册名称"
// 参数2: 需要被全局注册的那个组件
Vue.component('MyCount',Count)
4.5 组件的props
props 是组件的自定义属性,在封装通用组件的时候,合理地使用 props 可以极大的提高组件的复用性!它的语法格式如下:
export default {
props:[自定义属性A,自定义属性B,自定义属性...]
//组件的私有数据
data(){
return {}
}
}
- props 是只读的
vue 规定:组件中封装的自定义属性是只读的,程序员不能直接修改 props 的值。否则会直接报错
要想修改 props 的值,可以把 props 的值转存到 data 中,因为 data 中的数据都是可读可写的! - props 的default 默认值
在声明自定义属性时,可以通过 default 来定义属性的默认值。示例代码如下export default { props :{ init:{ //用default定义属性的默认值 default:0 } } }
- props 的type 值类型
在声明自定义属性时,可以通过 type 来定义属性的值类型。示例代码如下export default { props :{ init:{ //用default定义属性的默认值 default:0 //用 type 属性定义属性值的类型 // 如果传递过来的值不符合此类型,则会在终端报错 type : Number } } }
- props 的 required 必填项
在声明自定义属性时,可以通过 required 选项,将属性设置为必填项,强制用户必须传递属性的值。示例代
码如下:export default { props :{ init:{ type : Number required : true } } }
4.6 组件之间的样式冲突问题
默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因是:
① 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的
② 每个组件中的样式,都会影响整个 index.html 页面中的 DOM 元素 - style 节点的 scoped 属性
为了提高开发效率和开发体验,vue 为 style 节点提供了 scoped 属性,从而防止组件之间的样式冲突问题: - /deep/ 样式穿透
如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样式对子组件生效,可以使用 /deep/ 深度选择器; **/deep/是vue2.x中使用的,在vue3.x中推荐使用 :deep() 替代 /deep/ **
5. 组件的生命周期
生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。
生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。
- 当组件在内存中被创建完毕之后,会自动调用 created 函数
- 当组件被成功的渲染到页面上之后,会自动调用 mounted 函数
- 当组件在页面中被重新渲染完毕后,运行阶段可以调用 updated 函数
- 当组件被销毁完毕之后,会自动调用 unmounted 函数
注意:生命周期强调的是时间段,生命周期函数强调的是时间点。
组件生命周期函数的分类
第一阶段:组件创建阶段: new Vue() → beforeCreate → created (唯一1次:发Ajax请求初始数据)→ beforeMount → mounted(唯一1次:操作DOM元素)
第二阶段:组件运行阶段: beforeUpdate → updated (可以执行 0或多次)
第三阶段:组件销毁阶段: beforeUnmount → unmounted (唯一1次)
6. 组件之间的数据共享
6.1 组件之间的关系
在项目开发中,组件之间的最常见的关系分为如下两种:
① 父子关系
② 兄弟关系
6.2 父子组件之间的数据共享
- 父组件向子组件共享数据:自定义属性
- 子组件向父组件共享数据:自定义事件
6.3 兄弟组件之间的数据共享
vue2.x 中,兄弟组件之间数据共享的方案是 EventBus!!!
EventBus 的使用步骤:
① 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
② 在数据发送方,调用 bus.$emit(‘事件名称’, 要发送的数据) 方法触发自定义事件
③ 在数据接收方,调用 bus.$on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件
7. ref 引用
7.1 ref 引用的概念
ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,
组件的 $refs
指向一个空对象。
7.2 使用 ref 引用DOM 元素
如果想要使用 ref 引用页面上的 DOM 元素,则可以按照如下的方式进行操作
//使用 ref 属性,为对应的DOM 添加引用名称
<h3 ref="myh3">MyRef 组件 </h3>
<button @click="getRef">获取 $refs 引用</button>
methods:{
getRef(){
//通过 this.$refs.引用的名称 可以获取到 DOM 元素的引用
console.log(this.$refs.myh3)
//操作 DOM 元素,把文本颜色改为红色
this.$refs.myh3.style.color='red'
}
}
7.3 使用 ref 引用组件实例
如果想要使用 ref 引用页面上的组件实例,则可以按照如下的方式进行操作
//使用 ref 属性,为对应的组件添加引用名称
<my-counter ref="counterRef"></my-counter>
<button @click="getRef">获取 $refs 引用</button>
methods:{
getRef(){
//通过 this.$refs.引用的名称 可以引用组件的实例
console.log(this.$refs.counterRef)
//引用到组件的实例后, 就可以调用组件上的methods方法
this.$refs.counterRef.add()
}
}
7.4 控制文本框和按钮的按需切换
通过布尔值 inputVisible 来控制组件中的文本框与按钮的按需切换。示例代码如下:
<template>
<input type="text" v-if="inputvisible">
<button v-else @click="showInput">展示input输入框</button>
</template>
------------------------
<script>
export default {
data() {
return {
//控制文本框和按钮的按需切换inputVisible: false,
}
},
methods : {
showInput( { //切换布尔值,显示文本框
this.inputVisible = true
},
},
}
</script>
7.5 让文本框自动获得焦点
当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加 ref 引用,并调用原生 DOM 对象的
.focus() 方法即可。
7.6 this.$nextTick(cb) 方法
组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的
DOM 更新完成之后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。