目录:看到如下目录你能想起来使用场景和用法么?想不起来可以继续看下面的细节复习一下
- 全局方法 1
- 自定义指令 1.1
- 侦听器 watch 1.2
- 过滤器 1.3
- 生命周期 1.4
- 组件使用 2
- 生命周期 2.1
- props(类型和默认值) 2.2
- v-on和$emit 2.3
- 组件间通讯 2.4
- 高级特性 3
- 自定义v-model 3.1
- $nextTick 3.2
- refs-获取dom 3.3
- slot-插槽 3.4
- 动态、异步组件 3.5
- keep-alive 3.6
- mixin 3.7
- 周边工具 4
- Vuex 4.1
- vue-router 4.2
1.全局方法
1.自定义指令
为何需要自定义指令?内置指令不满足需求
Vue.directive 注册全局指令
- 自定义指令语法(获取焦点)
vue
Vue.directive('focus', {
inserted: function (el) {
el.focus()// 获取元素的焦点
}
})
// 使用自定义指令
<input type="text" v-focus>
- 带参数的自定义指令
vue
Vue.directive('color', {
// bind声明周期, 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
// el 为当前自定义指令的DOM元素
// binding 为自定义的函数形参 通过自定义属性传递过来的值 存在 binding.value 里面
bind: function(el, binding){
// 根据指令的参数设置背景色
// console.log(binding.value.color)
el.style.backgroundColor = binding.value.color;
}
});
// 使用带参数自定义指令
<input type="text" v-color='msg'>
自定义局部指令
- 局部指令,需要定义在
directives
的选项 用法和全局用法一样 - 局部指令只能在当前组件里面使用
- 当全局指令和局部指令同名时以局部指令为准
css
directives: {
focus: {
inserted: function(el) {
el.focus();
}
}
}
复制代码
2.侦听器 watch
- 侦听器的应用场景
- 数据变化时执行
异步
或开销较大的操作
- 注意: watch 中的属性 一定是data 中 已经存在的数据
kotlin
// 当data中的: firstName属性或lastNames属性改变时, 会自动触发对应的watch
watch: {
firstName(val) { // val: 表示变化后的值
this.fullName = val + ' ' + this.lastName;
},
lastName(val) {
this.fullName = this.firstName + ' ' + val;
}
}
复制代码
3.过滤器
- 概念: Vue.js允许我们自定义过滤器,可被用作一些常见的
文本格式化/处理
。- 过滤器可以用在两个地方: mustache插值表达式、v-bind表达式。
- 过滤器应该被添加在JavaScript表达式的尾部,由"管道"符指示。
- 过滤器不改变真正的
data
,而只是改变渲染的结果,并返回过滤后的版本image-20200511204822969
- 全局注册时是filter,没有s的。而局部过滤器是filters,是有s的
- 支持级联操作(对前一个过滤的数据再次进行过滤处理)
自定义全局过滤器
- 我们可以用全局方法
Vue.filter()
自定义一个全局过滤器。这样的话,每一个Vue的对象实例(每一个VM实例
)都可以拿到这个过滤器。它接收两个参数
:过滤器的名称
、过滤器函数
。
xml
<div>{{msg | upper}}</div>
<div>{{msg | upper | lower}}</div>
<div :class="msg | upper"></div>
<!-- 带参数过滤 -->
<div>{{date | format('yyyy-MM-dd')}}</div>
<script>
// 1.全局过滤器
Vue.filter('upper', (val) => {
// val就是要处理的数据
return val.charAt(0).toUpperCase() + val.slice(1);
})
// 2.处理带参数过滤器
Vue.filter('format', (date, arg) => {
// arg: 传递的参数
if (arg === 'yyyy-MM-dd') {
return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
}
})
</script>
复制代码
自定义私有过滤器
- 私有过滤器: 在某一个vue对象内部定义的过滤器称之为私有过滤器
- 这种过滤器只有在
当前vue对象
的el指定的监管区域
有用。
xml
<!-- 管道符前面的price: 要把price这段文本进行过滤 -->
<!-- 管道符后面的showPrice: 是通过showPrice这个过滤器来进行操作 -->
<td>{{price | showPrice}}</td>
<script>
const app = new Vue({
filters: {
showPrice(price) {
return '¥' + price.toFixed(2);
}
}
})
</script>
复制代码
4.生命周期
- 事物从出生到死亡的过程
- Vue实例从创建到销毁的过程,这些过程中会伴随着一些函数的自调用。我们称这些函数为钩子函数
- 主要阶段
挂载(初始化相关属性)
- beforeCreate
- created
- beforeMount
- mounted
更新(元素或组建的变更操作)
- beforeUpdate
- updated
销毁(销毁相关属性)
- beforeDestroy
- destroyed
钩子函数 | 过程 |
---|---|
beforeCreate | 在实例初始化之后, 数据观测和事件配置之前被调用此时data和methods以及页面的DOM结构都没有初始化什么都做不了 |
created | 在实例创建完成后被立即调用此时data 和 methods已经可以使用但是页面还没有渲染出来 |
beforeMount | 在挂载开始之前被调用此时页面上还看不到真实数据只是一个模板页面而已 |
mouted | el被新创建的vm.$el替换, 并挂载到实例上去之后调用该钩子。数据已经真实渲染到页面上在这个钩子函数里面我们可以使用一些第三方的插件 |
beforeUpdate | 数据更新时调用,发生在虚拟DOM打补丁之前。 页面上数据还是旧的 |
update | 由于数据更改导致的虚拟DOM重新渲染和打补丁, 在这之后会调用该钩子。页面上数据已经替换成最新的 |
beforeDestroy | 实例销毁之前调用 |
destroyed | 实例销毁后调用 |
2.组件使用
生命周期
vue父子组件生命周期执行顺序,请完整表述
单一组件中:
beforeCreate→created→beforeMounted→mounted→beforeUpdate→update→beforeDestroy→destroyed
父子组件中:
常用钩子简易版:父create→子created→子mounted→父mounted
①加载渲染过程:
父beforeCreate→父created→父beforeMounted→子beforeCreate→子created→子beforeMounted→子mounted→父mounted
②更新过程:
父beforeUpdate→子beforeUpdate→子update→父update
③销毁过程:
父beforeDestroy→子beforeDestroy→子destroyed→父destroyed
注意问题:
在父组件调用接口数据,通过props传递给子组件,接口响应是异步的,子组件无论在哪个钩子都取不到数据。因为子组件的mounted都执行完之后,父组件的请求才返回数据。会导致,从父组件传递给子组件的数据是undefined。
解决:
①在渲染子组件时候加上一个条件,dataList是父组件调用接口返回的数据。当有数据的时候在去渲染子组件。这样就会形成天然的阻塞。在父组件的created中的请求返回数据后,才会执行子组件的created,mounted。最后执行父组件的mounted。
ini
<div class="test">
<children v-if="dataList" :data="dataList" ></children>
</div>
②在子组件中watch监听,父组件获取到的值变化,子组件就可以监听到
kotlin
watch:{
data:{
deep:true,
handler:function(newVal,oldVal) {
this.$nextTick(() => {
this.data = newVal
})
}
},
}
每个生命周期具体适用哪些场景
钩子函数 | 过程 |
---|---|
beforeCreate | 创建前,在实例初始化之后, 数据观测和事件配置之前被调用此时data和methods以及页面的DOM结构都没有初始化什么都做不了。data,computed,watch,methods 上的方法和数据均不能访问。可以在这里加个loading事件 |
created | 创建后,在实例创建完成后被立即调用,此时data、props、computed 和 methods已经可以使用但是页面还没有渲染出来。①可访问data computed watch methods上的方法数据②初始化完成时的事件写在这里,异步请求也适宜在这里调用(请求不宜过多,避免白屏时间太长)③可以在这里结束loading事件,还做一些初始化,实现函数自执行。④未挂载DOM,若在此阶段进行DOM操作一定要放在Vue.nextTick()的回调函数中 |
beforeMount | **挂载前,**在挂载开始之前被调用此时页面上还看不到真实数据只是一个模板页面而已。但vue挂载的根节点已经创建,下面vue对DOM的操作将围绕这个根元素继续进行。beforeMount这个阶段是过渡性的,一般一个项目只能用到一两次。 |
mouted | **挂载,**完成创建vm. <math xmlns="http://www.w3.org/1998/Math/MathML"> e l ,和双向绑定。 e l 被新创建的 v m . el,和双向绑定。el被新创建的vm. </math>el,和双向绑定。el被新创建的vm.el替换, 并挂载到实例上去之后调用该钩子。①数据已经真实渲染到页面上在这个钩子函数里面我们可以使用一些第三方的插件。②完成挂载DOM和渲染,可在mounted钩子函数中对挂载的DOM进行操作。③可在这发起后端请求,拿回数据,配合路由钩子做一些事情。 |
beforeUpdate | **数据更新前,数据驱动DOM。**数据更新时调用,发生在虚拟DOM打补丁之前。 页面上数据还是旧的。①可在更新前访问现有的DOM,如手动移出添加的事件监听器。 |
update | 数据更新后,完成虚拟DOM的重新渲染和打补丁。由于数据更改导致的虚拟DOM重新渲染和打补丁, 在这之后会调用该钩子。页面上数据已经替换成最新的。①组件DOM已完成更新,可执行依赖的DOM操作。②注意:不要在此函数中操作数据(修改属性),会陷入死循环。 |
activated | 在使用vue-router时有时需要使用**<keep-alive></keep-alive> **来缓存组件状态,这个时候created钩子就不会被重复调用了。 |
deactivated | <keep-alive></keep-alive> 组件被移除时使用 |
beforeDestroy | 实例销毁之前调用①可做一些删除提示,如:您确定删除xx吗? |
destroyed | 实例销毁后调用。前组件已被删除,销毁监听事件,组件、事件、子实例也被销毁。 |
props(类型和默认值)
v-on和$emit
组件间通讯---自定义事件-兄弟组件
3.高级特性
父组件:
vue
<template>
<div>
<p>vue 高级特性</p>
<hr>
<!-- 自定义 v-model -->
<p>{{name}}</p>
<CustomVModel v-model="name"/>
<!-- nextTick -->
<!-- <NextTick/> -->
<!-- slot -->
<SlotDemo :url="website.url">
{{website.title}}
</SlotDemo>
<!-- <ScopedSlotDemo :url="website.url">
<template v-slot="slotProps">
{{slotProps.slotData.title}}
</template>
</ScopedSlotDemo> -->
<!-- 动态组件 -->
<!-- <component :is="NextTickName"/> -->
<!-- 异步组件 -->
<!-- <FormDemo v-if="showFormDemo"/>
<button @click="showFormDemo = true">show form demo</button> -->
<!-- keep-alive -->
<!-- <KeepAlive/> -->
<!-- mixin -->
<MixinDemo/>
</div>
</template>
<script>
import CustomVModel from './CustomVModel'
// import NextTick from './NextTick' //同步加载
// import SlotDemo from './SlotDemo'
// import ScopedSlotDemo from './ScopedSlotDemo'
// import KeepAlive from './KeepAlive'
import MixinDemo from './MixinDemo'
export default {
components: {
// CustomVModel
// NextTick
// SlotDemo,
// ScopedSlotDemo,
// FormDemo: () => import('../BaseUse/FormDemo'), //异步加载
// KeepAlive
MixinDemo
},
data() {
return {
name: '小圆脸儿',
website: {
url: 'https://juejin.cn/user/1398234520230989/posts',
title: '掘金',
subTitle: '稀土掘进'
},
// NextTickName: "NextTick",
showFormDemo: false
}
}
}
</script>
1.自定义v-model
子组件 CustomVModel
xml
<template>
<!-- 例如:vue 颜色选择 -->
<input type="text"
:value="text1"
@input="$emit('change1', $event.target.value)"
>
<!--
1. 上面的 input 使用了 :value 而不是 v-model
2. 上面的 change1 和 model.event1 要对应起来
3. text1 属性对应起来
-->
</template>
<script>
export default {
model: {
prop: 'text1', // 对应 props text1
event: 'change1'
},
props: {
text1: String,
default() {
return ''
}
}
}
</script>
2.$nextTick
- 异步渲染(以及合并data()修改),以提高渲染性能
- $nextTick 在DOM更新哇之后,触发回调
- 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
vue
<template>
<div id="app">
<ul ref="ul1">
<li v-for="(item, index) in list" :key="index">
{{item}}
</li>
</ul>
<button @click="addItem">添加一项</button>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
list: ['a', 'b', 'c']
}
},
methods: {
addItem() {
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
// 1. 异步渲染,$nextTick 待 DOM 渲染完再回调
// 3. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
this.$nextTick(() => {
// 获取 DOM 元素
const ulElem = this.$refs.ul1
// eslint-disable-next-line
console.log( ulElem.childNodes.length )
})
}
}
}
</script>
3.refs-获取dom
vue是异步渲染
data改变之后,DOM不会立刻渲染
$nextTick会在DOM渲染之后被触发,以获取最新DOM节点
- 异步渲染(以及合并data()修改),以提高渲染性能
- $nextTick 在DOM更新哇之后,触发回调
- 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
vue
<template>
<div id="app">
<ul ref="ul1">
<li v-for="(item, index) in list" :key="index">
{{item}}
</li>
</ul>
<button @click="addItem">添加一项</button>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
list: ['a', 'b', 'c']
}
},
methods: {
addItem() {
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
// 1. 异步渲染,$nextTick 待 DOM 渲染完再回调
// 3. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
this.$nextTick(() => {
// 获取 DOM 元素
const ulElem = this.$refs.ul1
// eslint-disable-next-line
console.log( ulElem.childNodes.length )
})
}
}
}
</script>
4.slot-插槽
让用户可以扩展组件,去更好的复用组件和对其做定制化处理。eg:布局组件、表格列、卡片
①默认插槽|单个插槽|匿名插槽 :子:<slot></slot>
父:<child>内容</child>
直接在子组件的标签内写入内容即可
父:<SlotDemo :url="website.url"> {{website.title}} </SlotDemo>
子:SlotDemo.vue
xml
<template>
<a :href="url">
<slot>
默认内容,即父组件没设置内容时,这里显示
</slot>
</a>
</template>
<script>
export default {
props: ['url'],
data() {
return {}
}
}
</script>
②作用域插槽|带数据的插槽 :子:<slot name="up" :data ="data"> 父:;通过slot-scope
获取子组件的信息,在内容中使用。这里可以用解构语法去直接获取想要的属性。
父:<ScopedSlotDemo :url="website.url"><template v-slot="slotProps">{{slotProps.slotData.title}}</template></ScopedSlotDemo>
子:ScopedSlotDemo.vue
vue
<template>
<a :href="url">
<slot :slotData="website">
{{website.subTitle}} <!-- 默认值显示 subTitle ,即父组件不传内容时 -->
</slot>
</a>
</template>
<script>
export default {
props: ['url'],
data() {
return {
website: {
url: 'https://www.xiaohongshu.com/user/profile/5d3266250000000011012ea0',
title: '小圆脸儿',
subTitle: '小圆脸儿的小红书'
}
}
}
}
</script>
③具名插槽 :子:<slot **name**="up"></slot>
父:<child><div slot="up">内容</div></child>
是在默认插槽的基础上加上slot
属性,值为子组件插槽name
属性值
名字对应好不要传乱了
5.动态、异步组件
动态组件
:is ="cpmponent-name" 用法
需要根据数据,动态渲染的场景。组件类型不确定
异步组件
- import()函数
- 按需加载,异步加载大组件
FormDemo: () => import('../BaseUse/FormDemo'),
6.keep-alive
- 缓存组件
- 频繁切换,不需要重新渲染
- vue 常见性能优化
tab切换
KeepAlive.vue
vue
<template>
<div>
<button @click="changeState('A')">A</button>
<button @click="changeState('B')">B</button>
<button @click="changeState('C')">C</button>
<keep-alive> <!-- tab 切换 -->
<KeepAliveStageA v-if="state === 'A'"/> <!-- v-show -->
<KeepAliveStageB v-if="state === 'B'"/>
<KeepAliveStageC v-if="state === 'C'"/>
</keep-alive>
</div>
</template>
<script>
import KeepAliveStageA from './KeepAliveStateA'
import KeepAliveStageB from './KeepAliveStateB'
import KeepAliveStageC from './KeepAliveStateC'
export default {
components: {
KeepAliveStageA,
KeepAliveStageB,
KeepAliveStageC
},
data() {
return {
state: 'A'
}
},
methods: {
changeState(state) {
this.state = state
}
}
}
</script>
KeepAliveStateA.vue
vue
<template>
<p>state A</p>
</template>
<script>
export default {
mounted() {
// eslint-disable-next-line
console.log('A mounted')
},
destroyed() {
// eslint-disable-next-line
console.log('A destroyed')
}
}
</script>
KeepAliveStateB.vue
vue
<template>
<p>state B</p>
</template>
<script>
export default {
mounted() {
// eslint-disable-next-line
console.log('B mounted')
},
destroyed() {
// eslint-disable-next-line
console.log('B destroyed')
}
}
</script>
KeepAliveStateC.vue
vue
<template>
<p>state C</p>
</template>
<script>
export default {
mounted() {
// eslint-disable-next-line
console.log('C mounted')
},
destroyed() {
// eslint-disable-next-line
console.log('C destroyed')
}
}
</script>
7.mixin
- 多个组件之间有重复逻辑,抽离出来
- mixin并不会死完美的解决方案,会有一些问题
- Vue3提出的Composition API 旨在解决这些问题
mixin.js
javascript
export default {
data() {
return {
city: '北京'
}
},
methods: {
showName() {
// eslint-disable-next-line
console.log(this.name)
}
},
mounted() {
// eslint-disable-next-line
console.log('mixin mounted', this.name)
}
}
MixinDemo.vue
vue
<template>
<div>
<p>{{name}} {{major}} {{city}}</p>
<button @click="showName">显示姓名</button>
</div>
</template>
<script>
import myMixin from './mixin'
export default {
mixins: [myMixin], // 可以添加多个,会自动合并起来
data() {
return {
name: '双越',
major: 'web 前端'
}
},
methods: {
},
mounted() {
// eslint-disable-next-line
console.log('component mounted', this.name)
}
}
</script>
Mixin 存在的问题
- 变量来源不明确,不利于阅读
- 多mixin可能会造成页面冲突
- mixin和组件可能出现多对多的关系,复杂度较高
相关面试技巧
- 可以不太深入,但必须知道
- 熟悉基本用法,了解使用场景
- 最好能和自己的项目经验结合起来
3.周边工具
1.Vuex
state
定义了应用状态的数据结构,可以在这里设置默认的初始状态。
getters
允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
action
用于提交 mutation,而不是直接变更状态,可以包含任意异步操作
mutation
是唯一更改 store 中状态的方法,且必须是同步函数。
Module:
允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
用于Vue组件
dispatch
commit
mapState
mapGetters
mapActions
mapMutations
2.vue-router
- 路由模式(hash、H5 history)
- 路由配置(动态路由、懒加载)
hash模式 (默认),如abc.com/#/user/10
H5 history 模式,如 abc.com/user/20 需要server端支持,router.vuejs.org/zh/guide/es...
用 createWebHistory()
创建 HTML5 模式,推荐使用这个模式:
javascript
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
问题:vue-router 组件复用导致路由参数失效怎么办?
解决方法:
1.通过 watch 监听路由参数再发请求
javascript
watch: { //通过watch来监听路由变化
"$route": function(){
this.getData(this.$route.params.xxx);
}
}
2.用 :key 来阻止"复用"
html
<router-view :key="$route.fullPath" />
其他Vue详解合集如下:
- Vue基本使用 1
- vue2.x的10种组件间通信方式 2
- Vue2.X的使用 3 当前文档
- Vue面试真题演练 4
- Vue3 5
感谢阅读
「❤️关注+点赞+收藏+评论+转发❤️」,创作不易,鼓励笔者创作更好的文章