Vue2、Vue3知识总结---完整版(上)✨
Vue
Vue 2
是一套用于构建用户界面的框架
Vue 的特性
-
数据驱动视图
-
双向数据绑定
-
MVVM
- MVVM 指的是 Model、View 和 ViewModel,它把每个 HTML 页面都拆分成了这三个部分
- Model 表示当前页面渲染时所依赖的数据源。
- View 表示当前页面所渲染的 DOM 结构。
- ViewModel 表示 vue 的实例,它是 MVVM 的核心。
- MVVM 指的是 Model、View 和 ViewModel,它把每个 HTML 页面都拆分成了这三个部分
-
数据代理
通过vm对象来代理data对象中属性的操作(读/写)
-
更加方便的操作data中的数据
-
基本原理
-
通过Object.defineProperty()把data对象中所有属性添加到vm上。
-
为每一个添加到vm上的属性,都指定一个getter/setter。
-
在getter/setter内部去操作(读/写)data中对应的属性。
-
-
Vue监视数据的原理:
1. vue会监视data中所有层次的数据。
2. 如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
1 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2 Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
Vue 的基本使用
-
使用步骤
- 1 导入 vue.js 的script 脚本文件
- 2 在页面中声明一个将被 Vue 所控制的 DOM 区域
- 3 创建 vm 实例对象
<!-- 希望 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: 'zhangsan' } }) </script>
指令
指令 是 Vue 为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构
按照不同用途可分为:
1 内容渲染指令
-
v-text
- 示例:
<p v-text="gender">性别:</p>
- :heavy_exclamation_mark:v-text 指令会
覆盖
元素内默认的值
- 示例:
-
:star:
{{ }}
<p>性别:{{ gender }}</p>
- 不会覆盖元素内默认的值
-
v-html
-
把包含 HTML 标签的字符串渲染为页面的 HTML 元素
-
<div v-html="info"></div>
-
:heavy_exclamation_mark:v-html有安全性问题
-
(1).在网站上动态渲染任意HTML是非常危险的,容易导致 XSS 攻击。
-
(2).一定要在可信的内容上使用v-html,不要用在用户提交的内容上!(和eval()有点像)
-
-
-
v-cloak
- 1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
- 2.使用css(
display:'none'
)配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
-
v-once
- 1.v-once所在节点在初次动态渲染后,就视为静态内容了。
- 2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
-
v-pre
- 1.跳过其所在节点的编译过程。
- 2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
2 属性绑定
-
v-bind:
-
v-bind: 指令可以简写为
:
-
<input type="text" :placeholder="tips">
-
<img :src="photo" alt="">
-
<div title="'box'+index"></div>
-
动态绑定 class / style / checked
-
:checked="isChecked"
isChekced
是 Boolean值
-
字符串:
- 适用于:类名不确定,要动态获取。
:class="classes"
-
classes是一个计算属性
-
-
对象语法:
-
要绑定多个样式,个数确定,名字也确定,但不确定用不用
-
:class="{active:isActive}"
-
:class="{active:isActive,line:isLine}"
-
class="title":class="{active:isActive,line:isLine}"
-
-
数组语法:
-
要绑定多个样式,个数不确定,名字也不确定。
-
:style='[styleobj,overridingStyles]'
-
-
使用 JavaScript 表达式 的运算
-
3 事件绑定
-
v-on :简写形式:@
- eg: @click='addCount' @keyup='count += 1'
-
需要在 methods 节点中进行声明
-
事件参数对象
-
$(event) 指原生的事件参数对象 event
-
绑定事件并
传参
<button @click="add($event, 1)">+N</button>
-
-
事件修饰符
事件修饰符 说明 .prevent 阻止默认行为(eg:阻止 a 链接的跳转、阻止表单的提交等) .stop 阻止事件冒泡 .capture 以捕获模式触发当前的事件处理函数 .once 绑定的事件只触发一次 .self 只有在 event.target 是当前元素自身时触发事件处理函数 <a href="http://www.baidu.com" @click.prevent="show">跳转到百度首页</a>
-
按键修饰符
<input type="text" @keyup.esc="clearInput" @keyup.enter="commitAjax">
.enter
.delete
.esc
.space
.up
.down
.left
.right
tab
换行 (必须配合 keydown去使用)
-
可以执行少量代码
count++
-
可以写函数传参(可以获取事件对象,没写参数默认有,写参数要
(a,$event)
- 给调用函数+个()eg:
@change="handelClick()"
- 如果只有一个 e 不加 ()
e.target.value/checkded
- 给调用函数+个()eg:
4 双向绑定
-
v-model
-
实现
表单
与数据
的双向绑定 -
用于获取
表单
(输入类)的数据 -
场景
- 表单(value / checked)、全选(状态在 computed )反选(状态在 data )
-
收集表单数据: 若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。 若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。 若:<input type="checkbox"/> 1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值) 2.配置input的value属性: (1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值) (2)v-model的初始值是数组,那么收集的的就是value组成的数组(多选)
-
v-model 修饰符
修饰符 作用 示例 .number 自动把用户输入的值转为数值类型 <input type="number" v-model.number="age">
.trim 自动过滤用户输入 的首尾空白字符 <input v-model.number="msg">
.lazy 在“change”时而非“input”时更新 <input v-model.number="msg">
-
5 条件渲染
-
条件渲染指令用来辅助开发者按需控制 DOM 的显示与隐藏
-
v-if
-
<p v-if="flag">这是被 v-if 控制的元素</p>
-
会动态地创建或移除 DOM 元素
v-else
、v-else-if=“ ”
- 必须相邻
-
只能在与 v-if 平级的时候使用
优秀良好一般差 -
移除时 dom 不存在
-
-
v-show
-
<p v-show="flag">这是被 v-show 控制的元素</p>
- 会动态为元素添加或移除 style="display: none;" 样式
- 移除时 dom 存在
v-if v-show 动态地创建或移除 DOM 元素 动态为元素添加或移除 style="display: none;"样式 支持多条件显示 不支持多条件显示 有更高的开销 有更高的初始渲染开销 使用场景:切换频率较低、判断条件较多的场景 使用场景:非常频繁地切换 -
6 列表渲染
-
v-for
- 基于一个数组来循环渲染一个列表结构
- 需要使用 item in items 形式的特殊语法
<tr v-for="(item, index) in list" :key="item.id"> <td>{{ index }}</td> <td>{{ item.id }}</td> <td>{{ item.name }}</td> </tr>
- 1 用于 循环 数组
(item,index) in array
- 2 用于循环 对象
(item,key) in obj
- 3 用于循环 数字
item in num
- 使用 key 维护列表的状态
- Vue 复用已存在的DOM元素提升渲染性能 但导致有状态的列表无法被正确更新
- key 注意事项
- key 的值只能是字符串或数字类型
- key 的值必须 具有唯一性
- 建议把数据项的 id 属性值作为 key 的值
- 使用 index 的值当做 key 的值没有意义( index 不具唯一性)
- 建议使用 v-for 指令时要指定 key 的值(既提升性能,又防止列表状态紊乱)
- label 的 for 属性
:for="'cb' + item.id"
input里面:id="'cb' + item.id"
过滤器
过滤器(Filters)是 vue 为开发者提供的功能,常用于文本的格式化。本质是 js 函数
-
过滤器放在 js 表达式的尾部 由“管道符”进行调用
- 插值表达式
<p>message 的值是:{{ message | capi }}</p>
- 不能给属性用,直接“ ”
- v-bind 属性绑定
<div v-bind:id="rawId | formatId"></div>
- 插值表达式
-
定义过滤器
-
在创建 vue 实例期间,可以在 filters 节点中定义过滤器
filters: { // 注意:过滤器函数形参中的 val,永远都是“管道符”前面的那个值 capi(val) { // 字符串有 charAt 方法,这个方法接收索引值,表示从字符串中把索引对应的字符,获取出来 // val.charAt(0) const first = val.charAt(0).toUpperCase() // 字符串的 slice 方法,可以截取字符串,从指定索引往后截取 const other = val.slice(1) // 强调:过滤器中,一定要有一个返回值 return first + other } }
-
私有过滤器
在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。
-
全局过滤器
// 使用 Vue.filter() 定义全局过滤器 Vue.filter('capi', function (str) { const first = str.charAt(0).toUpperCase() const other = str.slice(1) return first + other + '~~~' })
-
-
连续调用多个过滤器
<p>message 的值是:{{ message | capi | maxLength }}</p>
-
过滤器传参
-
本质是 js 函数
<p>{{ message | filterA(arg1,arg2)}</p> Vue.filter('filterA',(mesg,arg1,arg2)=>{})
-
-
兼容性
- 仅在 Vue1、2中受支持
- vue3 不支持
- 官方建议使用 计算属性 或 方法 代替过滤器功能
- 参考
侦听器
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。
当被监视的属性变化时, 回调函数自动调用, 进行相关操作
- 1 在 watch 节点进行声明
const vm = new Vue({
el: '#app',
data: {
username: 'admin'
},
// 所有的侦听器,都应该被定义到 watch 节点下
watch: {
// 侦听器本质上是一个函数,要监视哪个数据的变化,就把数据名作为方法名即可
// 新值在前,旧值在后
username(newVal) {
if (newVal === '') return
// 1. 调用 jQuery 中的 Ajax 发起请求,判断 newVal 是否被占用!!!
$.get('https://www.escook.cn/api/finduser/' + newVal, function (result) {
console.log(result)
})
}
}
})
-
2 使用 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) } }
-
3 immediate选项
默认情况下,组件在初次加载完毕后不会调用 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 } }
-
4 deep 选项
如果 watch 侦听的是一个
对象
,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项- 在上面的基础上加一个
deep:true
- 在上面的基础上加一个
-
5 监听对象单个属性的变化
watch:{ info: { handler(newVal) { console.log(newVal) }, deep:true, } // 如果要侦听的是 子属性 的变换,则必须包裹一层单引号 'info.username'(newVal) { console.log(newVal) } // 配置对象 'info.username':{ async handler(newVal){ const { data:res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal.username) console.log(res) } } }
监视的属性必须存在,才能进行监视!!
-
应用场景
-
本地存储
subjectList() { // 要侦听的属性 localStorage.setItem('scoreMsg', JSON.stringify(this.subjectList)) }
-
当监听 对象数组 时,数组的长度变化时,不用 deep 也可以,但是监听不到对象内部的 变化
-
数据变化时,发起 ajax 请求
-
计算属性
计算属性指的是通过一系列运算之后,最终得到一个属性值
-
个动态计算出来的属性值可以被模板结构或 methods 方法使用。
<div id="app"> <!-- 专门用户呈现颜色的 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,x,x) 的字符串 rgb() { return `rgb(${this.r}, ${this.g}, ${this.b})` } } }); console.log(vm) </script>
- 所有的计算属性,都要定义到 computed 节点之下
- 计算属性在定义的时候,要定义成“方法格式”
-
计算属性的特点
- 1 虽然计算属性在声明的时候被定义为方法,但是计算属性的本质是一个属性
- 2 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算
-
好处:
- 代码复用
- data变化,计算属性也变化
-
应用场景
- 反选
return this.list.every(item => item.checked === true)
- total
return this.subjectList.reduce((pre, current) => (pre += current.score), 0)
- 反选
全选:v-model="allChecked" 反选:computed
computed: {
allChecked: {
get() {
return this.list.every(item => item.checked === true)
},
set(allChecked) {
this.checked = !allChecked
this.list.forEach(item => (item.checked = allChecked))
}
}
}
计算属性:
1.定义:要用的属性不存在,要通过`已有属性`计算得来。
2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
计算属性 vs 侦听器
1 计算属性侧重于监听多个值的变化
,最终计算并返回
一个新值
2 侦听器侧重于监听单个
数据的变化,最终执行特定的业务处理
,不需要有任何返回值
3 computed能完成的功能,watch都可以完成。watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
axios
axios 是一个专注于网络请求的库
-
调用 axios 方法得到的返回值是 Promise对象
-
const result = axios({ method:'GET', url:'', // URL 中的查询参数 GET // params:{}, // 请求体参数 POST // data:{} }) result.then(res=>{ console.log(res.data) }) $('#btnPost').on('click',async ()=>{ const { data:res } = await axios({ method:'POST', url:'', data:{ name:'zs', age:20 } }) // 返回的是数据对象 可以解构单独拿出 data 把 data 重命名为 res })
如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await
await 只能在被 async “修饰”的方法中
用 jQuery 可以发起 $.ajax()
$.get()
$.post()
请求
axios:axios() axios.get() axios.post() axios.delete() axios.put() axios.patch()
axios.get()
$('#btnPost').on('click',async ()=>{
const { data:res } = await axios.get('url',{params:{id:1}})
})
axios.post()
$('#btnPost').on('click',async ()=>{
const { data:res } = await axios.post('url',{name:'zs',gender:'女'}) // axios.post()里面的请求体直接写数据对象
})
组件中发起axios 请求,不用每个组件都要导入 axios,在 main.js 导入,变成 Vue内置的成员
// main.js
import axios from 'axios'
// 配置请求根路径
axios.defaults.baseURL = 'http://www.itcbc.com:3006'
// 把 axios 挂载到 Vue.prototype上,供每个组件的实例直接使用
// 缺点: 不利于接口的 复用
Vue.propotype.$http = axios
// 组件中
methods:{
async getBooks(){
const {data : res} = await this.$http.get('http://www.itcbc.com:3006/api/getbooks')
}
}
$mount方法
const vm = new Vue({
data: {
username: 'admin'
}
})
vm.$mount('#app')
vue/cli
vue-cli 是 Vue.js 开发的标准工具。它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程
下载: npm i -g @vue/cli
查找安装: vue -V
-
在指定目录的 终端下 创建指定名称的项目
-
vue create demo-first
-
项目名称不能有空格、中文、大写字母
-
创建冻结 按 ctrl + c
-
创建成功
cd 项目名称
npm run serve
不要关掉终端
-
-
vue 项目中 src 目录的构成
-
assets 放:图片、css 样式等静态资源
-
components :放封装好的 组件
-
main.js :是项目的入口文件,整个项目的运行,要先执行 main.js

- app.vue:是项目的根组件(render渲染的组件就是根组件)
-
vue 项目运行流程
- 通过 main.js 把 App.vue 渲染到 HTML 页面
单页面应用程序
单页面应用程序(英文名:Single Page Application)简称 SPA,顾名思义,指的是一个 Web 网站中只有唯一的一个 HTML 页面,所有的功能与交互都在这唯一的一个页面内完成。
vue组件
组件是对 UI 结构的复用
组件化开发
组件化开发指的是:根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护。
组件的后缀名是 .vue
一个重要的内置关系
- VueComponent.prototype.proto === Vue.prototype
为什么要有这个关系
- 让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
this.$refs.xx(ref=xx).属性/方法()
定义组件
组成部分:
-
1 template -> 组件的模板结构(必须包含)
<template> 当前组件的 DOM 结构,需要被定义到 template 标签的内部 </template>
- template 中只能包含唯一的根节点
-
2 script -> 组件的 JavaScript 行为
在
-
基本结构
<script> // 组件相关的 data 数据、methods 方法等都需要定义到 export default 所导出的对象中 exports default{} </script>
-
data 必须是一个
函数
,不能直接指向一个数据对象data:{count:0// 错误 会导致多个组件实例共用同一份数据的问题 }
data(){return {}}
-
-
3 style -> 组件的样式
-
支持 less 写法
-
scoped 属性
-
防止组件之间的样式冲突问题
-
<style scoped> </style>
-
-
组件配置中: data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】
new Vue(options)配置中: data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
使用组件
- 1 使用 import 语法导入需要的组件
- @ 表示定位到 src
import MyCount from 'xxx'
- 2 使用 components 节点注册组件
- 3 以标签的形式使用刚才注册的组件
组件间的父子关系
- 组件封装之后,彼此之间是相互独立的,不存在父子关系
- 在使用组件的时候,根据彼此的嵌套关系构成 父子、兄弟关系
私有组件
- 使用 components 注册的是 私有组件
- 在组件 A 的 components 节点下,注册了组件 F。 则组件 F 只能用在组件 A 中;不能被用在组件 C 中。
component:{ MyCount }
- :star:
component:{ 'my-count', MyCount }
注册全局组件
- 在 vue 项目的 main.js 入口文件中,通过 Vue.component() 方法,可以注册全局组件。
- 1
import
- 2
Vue.component('MyCount',MyCount)
- 1
组件的 name 会显示在 devtools 上
组件的 props
props 是组件的自定义属性,在封装通用组件的时候,合理地使用 props 可以极大的提高组件的复用性
exports default{
props:['自定义属性A','自定义属性B','...'],
data(){
return {}
}
}
-
props 是只读的
-
修改:把 props 的值 转存 到 data 中,data 中的数据都是可读写的
-
不要直接在 子组件里修改 props 的值 (会报错,父组件没跟着变)
props:['init'], data(){ return{ count:this.init } }
-
-
props 的 default 默认值
-
props:{ init: { default: 0 }}
-
用户没传 init 的值时,default 的值生效
-
对象或数组默认值必须从一个工厂函数获取
default:function() { return {message:'hello'} }
-
-
type 值类型
- eg:
type: Number
:init="9"
v-bind: 加上 js 的数字- 写在 props 里面
props:{name:String}
- eg:
-
required 必填项
required: true
使用:<my-count :init="9"></my-count>
组件之间的样式冲突问题
默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
-
根本原因
- 1 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的
- 2 每个组件中的样式,都会影响整个 index.html 页面中的 DOM 元素
-
解决
-
1 为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域
<style> .container[data-v-0001]{ border: 1px solid red;} </style>
-
2 style 的 scoped 属性
- 防止组件之间的样式冲突问题
<style scoped> </style>
- 则当前组件的样式对其子组件是不生效的
-
3 /deep/ 样式穿透
- 让某些样式对子组件生效
-
-
当使用第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到 /deep/
组件的生命周期
生命周期
生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。
生命周期函数
是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行,强调的是时间点
-
组件生命周期函数的分类
-
组件创建阶段、组件运行阶段
-
-
beforeCreate()
- 创建阶段的第一个生命周期函数
- 组件的 props/data/methods 尚未被创建,都处于 不可用 状态
-
:star:created()
- 组件的 props/data/methods 已创建好,都处于 可用 状态,但是组件的模板结构未生成
- 在里面调用 methods 方法,请求服务器的数据,并且,把
请求
到的数据,转存到 data 中,供 template 使用 - 有些
bus.$on()
写在 created 里面
-
beforeMount
- 浏览器还没当前组件的 DOM结构
-
:star:mounted
- 已渲染 HTML,第一次取到 DOM 结构
启动定时器
、绑定自定义事件
、订阅消息
等【初始化操作】
-
beforeUpdate
- 将要 根据变化、更新后的数据,重新渲染组件的模板结构
-
:star:updated
- 已根据最新的数据,完成了组件 DOM 结构 的重新渲染
- 当数据变化之后,为了能够操作到最新的 DOM 结构,必须把代码写到 updated 生命周期函数中
- 但是一般不在这里做什么,因为一个属性变化就会触发 updated
-
beforeDestroy
- 尚未销毁组件,还处于 正常工作状态
清除定时器
、解绑自定义事件
、取消订阅消息
等【收尾工作】
-
destroyed
- 组件已被销毁,DOM 结构已被完全移除
- 销毁后自定义事件会失效,但原生DOM事件依然有效。
- 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
:star:组件之间的数据共享
1 父组件向子组件共享数据
- 需要使用自定义属性
- 也就是 使用自定义属性的方法
2 子组件向父组件共享数据
- 使用自定义事件(以传参的形式 去共享数据)
- 父:函数
- 函数接收数据
- 子:props:['函数名称']
- 调用函数,传递参数数据
3 兄弟组件之间的数据共享
-
在 vue2 中,兄弟组件之间的数据共享的方案是 EventBus
-
EventBus 的使用步骤
- 1 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
- 2 在数据发送方,调用
bus.**$emit(**'事件名称', 要发送的数据)
方法触发自定义事件 - 3 在数据接收方,调用
bus.**$on**('事件名称', 事件处理函数)
方法注册一个自定义事件 bus.$off('xxx')
解绑
4 :star:全局事件总线:任意组件间通信
-
main.js
- 在vue 实例
beforeCreate(){ Vue.prototype.$bus = this}
- 在vue 实例
-
发送方
this.$bus.$emit('deleteTodo', 参数)
-
接收方
mounted() { this.$bus.$on('checkTodo', this.checkTodo) this.$bus.$on('deleteTodo', this.deleteTodo) }, beforeDestroy() { this.$bus.$off('checkTodo') this.$bus.$off('deleteTodo') }
5 消息订阅与发布 实现任意组件间通讯
使用步骤:
-
安装pubsub:
npm i pubsub-js
-
引入:
import pubsub from 'pubsub-js'
-
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){ demo(data){......} =》或者写在回调函数里 } ...... mounted() { this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 (msgName,data)=>{...} 写箭头函数 } beforeDestroy(){ pubsub.unsunscribe(this.pid)}
-
提供数据:
pubsub.publish('xxx',数据)
-
最好在beforeDestroy钩子中,用
PubSub.unsubscribe(pid)
去取消订阅。
ref 引用
ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。
默认情况下, 组件的 this.$refs 指向一个空对象。
- 使用 ref 引用 DOM 元素
- 使用 ref 引用组件实例
-
ref=' '
写在 组件使用标签上 -
引用到组件的实例后,就可以调用组件上的 methods 方法
-
控制文本框和按钮的按需切换
通过布尔值 inputVisible 来控制组件中的文本框与按钮的按需切换。
-
让文本框自动获得焦点
当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加 ref 引用,并调用原生 DOM 对象的 .focus() 方法即可。
-
this.$nextTick(cb) 方法
组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。
即等组件的 DOM 更新完成之后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。
@ 从 src 源目录从外往里找
:heavy_exclamation_mark:一个重要的内置关系:
VueComponent.prototype.__proto__ === Vue.prototype
让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
动态组件
动态组件指的是动态切换组件的显示与隐藏。
动态组件的渲染
vue 提供了一个内置的
<component>
组件,专门用来实现动态组件的渲染。
data(){ // 函数 避免组件被复用时,数据存在引用关系。
return {comName:'Left'}
}
<componet :is="comName"></componet>
<button @click="comName = 'Left'">切换 Left 组件</button>
<button @click="comName = 'Right'">切换 Right 组件</button>
使用 内置的 keep-alive 保持状态
解决默认情况下,切换动态组件时无法保持组件的状态的问题。
<keep-alive>
<component :is="comName"></component>
</keep-alive>
keep-alive 对应的生命周期函数
- 当组件被缓存时,会自动触发组件的 deactivated 生命周期函数。
- 当组件被激活时,会自动触发组件的 activated 生命周期函数。
- 当组件第一次被创建是,即会触发created 也会触发 actived
- 当组件被激活,只会触发 actived,不再触发 created,因为组件没有被重新创建
keep-alive 的 include 属性
include 属性用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号分隔:
- 与 include 相对的是 exclude(指定哪些组件不需要被缓存)只能二选一
<keep-alive include="MyLeft,MyRight">
<component :is="comName"></component>
</keep-alive>
组件的name属性:
当提供了 name 属性之后,组件的名称,就是 name 属性的值
export default{
name:'MyRight'
}
对比:
- name
- 1 调试的时候 出现的 组件名称
- 2 与
<keep-alive>
结合 指定被缓存与不被缓存
- 注册名称
- 应用场景:以标签的形式,把注册好的组件,渲染和使用到页面结构之中
mixin(混入)
可以把多个组件共用的配置提取成一个混入对象
独立的 js 文件
使用:
1 定义混合
export const xxx = {
data(){...}
methods(){...}
...
}
export const yyy = {
data(){...}
methods(){...}
...
}
2 使用混合
- 全局混入
- 在 main.js 中
Vue.mixin(xxx)
- 在 main.js 中
- 局部混入 // App.vue or 某一组件中
import {xxx,yyy} from '/mixin'
mixins:[xxx,yyy]
插件
独立的 js 文件
// 1 定义插件 plugins.js
export default {
install(Vue,x,y,z)
// 1. 添加全局过滤器
Vue.filter('mySlice',function(value){
return value.sclice(0,4)
})
// 2. 添加全局指令
Vue.directive('fbind',{bind(el,binding){ el.value = binding.value},
insert(el,binding){el.focus()},update(el,binding){el.value = binding.value}
})
// 3. 配置全局混入(合)
Vue.mixin({data(){ return {x:100,y:200 }}})
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
// 5. 添加全局组件
Vue.component('myButton',{})
}
// 2 引入插件 main.js
import myBtn from '@/plugin/plugins.js'
// 3 使用自定义插件
<my-button/>
插槽
插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的内容的占位符。
基础用法:
在 <template>
中 通过<slot></slot>
,为用户预留内容占位符
<my-com-1> <p>用户自定义的内容 </p> </my-com-1>
- 默认情况下,在使用组件时,提供的内容会被填充到名字为
default
的插槽中
没有预留插槽的内容会被丢弃
后备内容
即
<slot>
标签里面的 默认内容
具名插槽
如果在封装组件时需要预留多个插槽节点,则需要为每个 插槽指定具体的 name 名称。这种带有具体名称的插槽叫做“具名插槽”。
<slot name="header"></slot>
如果省略了 slot 的 name 属性就是 <slot name="default"></slot>
接Vue2、Vue3知识总结---完整版(下)✨
转载自:https://juejin.cn/post/7134179833570918430