目录
vue有非常棒的官方文档,基本上看了就会用,其实没有再写博客的必要。因为最近在开发vue3的项目,所以初衷是为了系统性的归纳一下常常用到的内容,作为总结提炼的同时也和大家一起分享...
vue3官方文档:简介 | Vue.js
内部指令
vue2:
v-text | 更新元素的 textContent 。 |
v-html | 更新元素的 innerHTML 。 |
v-show | 根据表达式之真假值,切换元素的 display CSS property。 |
v-if | 根据表达式的值的 truthiness 来有条件地渲染元素。 |
v-else-if | 表示 v-if 的“else if 块”。可以链式调用。 |
v-else | 为 v-if 或者 v-else-if 添加“else 块”。 |
v-for | 基于源数据多次渲染元素或模板块。 |
v-on | 绑定事件监听器。缩写是 @。注意了解修饰符的使用。 |
v-bind | 动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。缩写是 : 。注意了解修饰符的使用。 |
v-model | 在表单控件或者组件上创建双向绑定。注意了解修饰符的使用。 |
v-slot | 提供具名插槽或需要接收 prop 的插槽。缩写是 #。 |
v-pre | 跳过这个元素和它的子元素的编译过程。 |
v-cloak | 这个指令保持在元素上直到关联实例结束编译。 |
v-once | 只渲染元素和组件一次。 |
vue3:
v-memo | 3.2+。缓存一个模板的子树。在元素和组件上都可以使用。 |
v-bind | 修饰符:.prop和.attr调整。 |
v-on | 修饰符:不再支持使用数字 (即键码) 作为 v-on 修饰符,不再支持 .keyCodes |
组件之间通信
vue2:
vm.$props | 父组件传值给子组件,子组件使用props进行接收。 |
vm.$emit | vm.$emit( eventName, […args] )。触发当前实例上的事件。附加参数都会传给监听器回调。子组件传值给父组件。 |
vm.$parent、vm.$children和vm.$root | 组件中可以使用 $parent 和 $children 获取到父组件实例和子组件实例,进 而获取数据。 |
vm.$attrs和vm.$listeners | 可以通过 v-bind="$attrs" 和v-on="$listeners"传入内部组件。如:在对一些组件进行二次封装时可以方便传值。 |
vm.$refs | 一个对象,持有注册过 ref attribute 的所有 DOM 元素和组件实例,直接获取数据。 |
Vuex | 使用 Vuex 进行状态管理。 |
EventBus | EventBus又称事件总线,相当于一个全局的仓库,任何组件都可以去这个仓库里获取事件。跨组件触发事件,进而传递数据。 |
provide / inject | 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。 |
浏览器缓存 | 例如 localStorage。 |
vue3:
Props
<script setup>
import { defineProps } from 'vue'
//一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute
const props = defineProps({
tableData: {
type: Array,
default: () => {
return []
}
},
})
//依然是所有的 props 都遵循着单向绑定原则,子组件不能修改父组件传过来的值。
props.tableData.push(...[2,3,4])
</script>
emit
<!-- 子组件 -->
<script setup>
import { defineEmits } from 'vue'
//显式声明emit,触发多个事件时以数组的形式声明
const emit = defineEmits(['emit1', 'emit2'])
const handleChange = (val) => {
emit('emit1', val.emit1)
emit('emit2', val.emit2)
}
</script>
<!-- 父组件 -->
<common-item @emit1="getEmit1" @emit2="getEmit2"></common-item>
<script setup>
const getEmit1 = (val) => {
console.log('emit1',val)
};
const getEmit2 = (val) => {
console.log('emit2',val)
};
</script>
# 注:官方文档说defineProps 和 defineEmits 都是只能在 <script setup> 中使用的编译器宏。他们不需要导入,且会随着 <script setup> 的处理过程一同被编译掉。但我在开发electron项目的时候不导入会报错,提示defineProps或defineEmits is not defined.
$parent、$children和$root
vue3 setup里面没有this(官方:在 setup() 内部,this 不会是该活跃实例的引用,因为 setup() 是在解析其它组件选项之前被调用的,所以 setup() 内部的 this 的行为与其它选项中的 this 完全不同。这在和其它选项式 API 一起使用 setup() 时可能会导致混淆。),而且废除了$children。
$attrs和$listeners
1.vue2 中可以通过 this.$attrs 访问传递给组件的 attribute,以及通过 this.$listeners 访问传递给组件的事件监听器。
2.vue3 的虚拟 DOM 中,事件监听器现在只是以 on 为前缀的 attribute,这样事件监听器就成为了 $attrs 对象的一部分,因此 $listeners 被移除了。
$refs
Vuex
EventBus
EventBus 又称为事件总线。在Vue中可以使用 EventBus 来作为中间商,所有组件都可以访问,可以向该中心注册发送事件或接收事件,通过事件进行组件间通信,所有的组件都可以无阻碍地和其他组件通信。因为太方便,所以可能会造成难以维护的“灾难”,开发过程中往往需要慎重使用,一般不推荐,害怕导致问题需要系统性排查。因此有了更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。
vue2:
初始化事件总线 EventBus 并将其导出,以便其它模块可以发送事件或者接收事件。
初始化:
//方法一:
//创建一个 .js 文件,比如 eventBus.js
import Vue from 'vue'
export default new Vue()
//方法二:
//可以直接在项目中的 main.js 初始化 EventBus
// main.js
Vue.prototype.$EventBus = new Vue()
发送事件:
//引入事件总线
import EventBus from "@/utils/eventBus";
//发送事件“websocket”
EventBus.$emit("websocket", tipData);
接收事件:
//引入事件总线
import EventBus from "@/utils/eventBus";
//接收事件“websocket”
EventBus.$on("websocket", val => {
if (val.typeDesc === "算法触发的消息") {
this.getTableData();
}
});
移除事件:
//引入事件总线
import EventBus from "@/utils/eventBus";
//移除事件“websocket”
EventBus.$off('websocket', {})
vue3:
正如所料,在vue3中$on, $once, $off实例方法已经被移除,组件实例不再实现事件触发接口,EventBus 已经成为了历史。vue3可以用发布订阅者模式实现的插件库(mitt)也可以自己模拟。
这个方式其实有点像 Vuex 或者 Pinia 那样,创建一个 Bus.js
文件,用来控制数据和注册事件。
Bus.js
import { ref } from 'vue'
class Bus {
constructor() {
// 收集订阅信息,调度中心
this.eventList = {}, // 事件列表,这项是必须的
// 下面的都是自定义值
this.msg = ref('这是一条总线的信息')
}
// 订阅
$on(name, fn) {
this.eventList[name] = this.eventList[name] || []
this.eventList[name].push(fn)
}
// 发布
$emit(name, data) {
if (this.eventList[name]) {
this.eventList[name].forEach((fn) => {
fn(data)
});
}
}
// 取消订阅
$off(name) {
if (this.eventList[name]) {
delete this.eventList[name]
}
}
}
export default new Bus()
用法:
组件A:
<script setup>
import Bus from '../Bus.js'
//发布
let exitClick = () => {
Bus.$emit('exit', false)
}
</script>
组件B:
<script setup>
import Bus from './Bus.js'
// 订阅
Bus.$on('exit', data => {
isShowPage.value = data
})
</script>
localStorage
这个兄弟不归vue管,没有什么你的我的,用起来也比较简单,存取操作。
计算属性computed
<!-- vue2 -->
// html
<div>{{ total(3) }}</div>
<div v-if="isShow">测试元素</div>
// js
computed: {
total() {
return function (n) {
return n * this.num
}
},
isShow: function() {
return this.role === 'admin' && this.age > '18'
}
}
<!-- vue3 -->
// html
<div>{{ total(3) }}</div>
<div v-if="isShow">测试元素</div>
// js
<script setup>
import { computed } from 'vue'
const total = computed(() => (n) => {
return n * this.num
})
const isShow = computed(() => {
return this.role === 'admin' && this.age > '18'
})
</script>
watch
<!-- vue2 -->
watch: {
formData: {
handler(newForm, oldForm) {
console.log(newForm)
},
immediate: true,
deep: true
}
}
<!-- vue3 -->
<script setup>
import { watch } from 'vue'
watch(
() => formData,
(newForm, oldForm) => {
console.log(newForm)
},
{ immediate: true, deep: true }
)
</script>
生命周期钩子
我们可以使用生命周期钩子在组件各个生命阶段添加逻辑,如常见的onMounted。
vue3图示:生命周期钩子 | Vue.jsVue.js - 渐进式的 JavaScript 框架https://cn.vuejs.org/guide/essentials/lifecycle.html
生命周期钩子:
- beforeCreate -> 使用 setup()
- created -> 使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy-> onBeforeUnmount
- destroyed-> onUnmounted
nextTick
<!-- vue2 -->
data(){
return {
visible: false
}
}
// 直接使用
this.$nextTick(() => {
// 执行操作
this.visible = true
this.getData()
})
<!-- vue3 -->
<template>
<div class="main-content">
<p v-if="visible">hello</p>
</div>
</template>
<script setup>
import { ref, nextTick } from 'vue'
const visible = ref(false)
// 使用
nextTick(() => {
// 执行操作
visible.value = true
this.getData()
})
</script>
官方:
const count = ref(0)
async function increment() {
count.value++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}