In this article
Like + attention + favorites = learned
This article explains the basic usage of various communication methods for Vue 3.2 components, using a single file component < Script Setup >.
As we all know, a very important knowledge point in vue.js is component communication. Whether it is the development of business class or component library, there are their own communication methods.
This article is suitable for:
- There are
Vue 3
Basic readers. - Readers who want to develop component libraries.
This article will cover the following points:
- Props
- emits
- expose / ref
- Non-Props
- v-model
- Slot slot.
- provide / inject
- Bus, bus
- getCurrentInstance
- Vuex
- Pinia
- mitt.js
I will write a simple demo of all the points listed above. The purpose of this article is to let you know that these methods are available, so we won’t delve into every single point.
You are advised to type through the code and then follow the links in this article to dig deeper.
Collect (learn) is their own!
Props
Parent component passes value to child component (parent to child)
Props document
The parent component
// Parent.vue
<template>
<! -- Using child components -->
<Child :msg="message" />
</template>
<script setup>
import Child from './components/Child.vue' // Introduce child components
let message = 'ray monkeys'
</script>
Copy the code
Child components
// Child.vue
<template>
<div>
{{ msg }}
</div>
</template>
<script setup>
const props = defineProps({
msg: {
type: String.default: ' '}})console.log(props.msg) // you need to use props. XXX in js. You don't need props to use in HTML
</script>
Copy the code
The defineProps API must be used to declare props in
See the documentation for more details.
In
Props can also do a lot of things, such as setting the default value default, type validation type, requiring a required pass, and customizing the validation function validator.
We can go to the official website to see, this is the knowledge point that must master!
Props document
emits
The child tells the parent to fire an event and can pass values to the parent. (Abbreviation: son to father)
Emits a document
The parent component
// Parent.vue
<template>
<div>Parent component: {{message}}</div>
<! -- Customize changeMsg events -->
<Child @changeMsg="changeMessage" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'
let message = ref('ray monkeys')
// Change the value of message, data is passed from the child component
function changeMessage(data) {
message.value = data
}
</script>
Copy the code
Child components
// Child.vue
<template>
<div>Child components:<button @click="handleClick">Buttons for child components</button>
</div>
</template>
<script setup>
// Register a custom event name that tells the parent component what event to fire when passed up.
const emit = defineEmits(['changeMsg'])
function handleClick() {
// Parameter 1: event name
// Parameter 2: the value passed to the parent component
emit('changeMsg'.'Shark Pepper')}</script>
Copy the code
As with props, the defineEmits API must be used in
In
expose / ref
Subcomponents can expose their own methods and data through expose.
The parent gets the child through ref and calls its methods or accesses data.
Expose the document
Speak by example
The parent component
// Parent.vue
<template>
<div>{{MSG}}</div>
<button @click="callChildFn">Invoke methods of child components</button>
<hr>
<Child ref="com" />
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Child from './components/Child.vue'
const com = ref(null) // Bind child components with template ref
const msg = ref(' ')
onMounted(() = > {
// Assign the message of the subcomponent to MSG after loading
msg.value = com.value.message
})
function callChildFn() {
// Call the child's changeMessage method
com.value.changeMessage('Garlic bastard')
// reassign the message of the child component to MSG
msg.value = com.value.message
}
</script>
Copy the code
Child components
// Child.vue
<template>
<div>Child component: {{message}}</div>
</template>
<script setup>
import { ref } from 'vue'
const message = ref('Cockroach Bully')
function changeMessage(data) Value = data} Use defineExpose to expose the specified data and method defineExpose({message, changeMessage})</script>
Copy the code
DefineExpose does not need to be introduced separately in < Script Setup >.
Expose the document
DefineExpose document
Non-Props
Non-props are attributes that are not Prop.
Attributes defined by prop or emits are not used in child components and can be accessed via $attrs.
Common ones are class, style, and ID.
Attribute documents that are not Prop
Let me give you an example
Case of a single root element
The parent component
// Parent.vue
<template>
<Child msg="Thunder Monkey world!" name="Shark Pepper" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'
</script>
Copy the code
Child components
// Child.vue
<template>
<div>Subcomponent: Open the console and see</div>
</template>
Copy the code
Open the console and you can see that the property is attached to the HTML element.
Multiple elements
In Vue3, however, components are no longer required to have only one root element. The above example does not work if the child component is multiple elements.
// Child.vue
<template>
<div>Subcomponent: Open the console and see</div>
<div>Subcomponent: Open the console and see</div>
</template>
Copy the code
You can use $attrs to bind.
// Child.vue
<template>
<div :message="$attrs.msg">Bind only the specified value</div>
<div v-bind="$attrs">The binding</div>
</template>
Copy the code
v-model
V-model is a syntactic sugar of Vue. The gameplay in Vue3 is much more confusing.
Single value case
V-models on components use modelValue as prop and Update :modelValue as events.
V-model parameter document
The parent component
// Parent.vue
<template>
<Child v-model="message" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'
const message = ref('ray monkeys')
</script>
Copy the code
Child components
// Child.vue
<template>
<div @click="handleClick">{{modelValue}}</div>
</template>
<script setup>
import { ref } from 'vue'
/ / receive
const props = defineProps([
'modelValue' // The receiving parent uses the value passed in by the V-model and must receive it with the name modelValue
])
const emit = defineEmits(['update:modelValue']) // The name update:modelValue must be used to inform the parent component to change the value
function handleClick() {
// Parameter 1: the name of the method that notifies the parent component to change the value
// Parameter 2: the value to be modified
emit('update:modelValue'.'Jet hippo')}</script>
Copy the code
You could also write it this way. It’s even easier
Child components
// Child.vue
<template>
<div @click=$emit('update:modelValue', 'emit hippo ')">{{modelValue}}</div>
</template>
<script setup>
import { ref } from 'vue'
/ / receive
const props = defineProps([
'modelValue' // The receiving parent uses the value passed in by the V-model and must receive it with the name modelValue
])
</script>
Copy the code
Multiple V-Model bindings
Multiple V-Model binding documents
The parent component
// Parent.vue
<template>
<Child v-model:msg1="message1" v-model:msg2="message2" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'
const message1 = ref('ray monkeys')
const message2 = ref('Cockroach Bully')
</script>
Copy the code
Child components
// Child.vue
<template>
<div><button @click="changeMsg1">Modify msg1</button> {{msg1}}</div>
<div><button @click="changeMsg2">Modify msg2</button> {{msg2}}</div>
</template>
<script setup>
import { ref } from 'vue'
/ / receive
const props = defineProps({
msg1: String.msg2: String
})
const emit = defineEmits(['update:msg1'.'update:msg2'])
function changeMsg1() {
emit('update:msg1'.'Shark Pepper')}function changeMsg2() {
emit('update:msg2'.Lele the Scorpion)}</script>
Copy the code
V – model modifier
V-models can also pass in modifiers in the form of.
V-model modifier document
The parent component
// Parent.vue
<template>
<Child v-model.uppercase="message" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'
const message = ref('hello')
</script>
Copy the code
Child components
// Child.vue
<template>
<div>{{modelValue}}</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const props = defineProps([
'modelValue'.'modelModifiers'
])
const emit = defineEmits(['update:modelValue'])
onMounted(() = > {
// Use the toUpperCase() method to check if upperCase is available
if (props.modelModifiers.uppercase) {
emit('update:modelValue', props.modelValue.toUpperCase())
}
})
</script>
Copy the code
Slot slot.
A slot can be interpreted as passing an HTML fragment to a child component. The child component uses the
element as an outlet to host the distribution.
Slot document
This article intends to talk about three types of slots that are commonly used: default slots, named slots, and scoped slots.
The default slot
The basic usage of slots is very simple, just use the
tag in the child component to render the HTML content passed in by the parent component.
Default slot document
The parent component
// Parent.vue
<template>
<Child>
<div>Ray, a monkey!</div>
</Child>
</template>
Copy the code
Child components
// Child.vue
<template>
<div>
<slot></slot>
</div>
</template>
Copy the code
A named slot
The named slot is classified on the basis of the default slot, which can be understood as the corresponding seat.
Named slot document
The parent component
// Parent.vue
<template>
<Child>
<template v-slot:monkey>
<div>Ray, a monkey!</div>
</template>
<button>Shark chili</button>
</Child>
</template>
Copy the code
Child components
// Child.vue
<template>
<div>
<! -- Default slot -->
<slot></slot>
<! -- named slot -->
<slot name="monkey"></slot>
</div>
</template>
Copy the code
The parent component needs to use the
tag with the V-solt: + name on the tag.
Child components need to receive with name= name in the
tag.
That’s how you sit.
Finally, note that the layout order of the slot contents is the same as that of the child components.
In the example above, you can take a closer look at the order in which the children are passed in and the order in which they are formatted.
Scope slot
If you’ve used tables from UI frameworks like Element-Plus, you should have a good idea what scoped slots are.
Scope slot document
The parent component
// Parent.vue
<template>
<! -- v-slot="{scope}" -->
<! -- :list="list" -->
<Child v-slot="{scope}" :list="list">
<div>
<div>Name: {{scope.name}}</div>
<div>Occupation: {{scope.occupation}}</div>
<hr>
</div>
</Child>
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'
const list = ref([
{ name: 'ray monkeys'.occupation: 'thunder'},
{ name: 'Shark Pepper'.occupation: 'swimming'},
{ name: 'Cockroach Bully'.occupation: 'sweeping'},])</script>
Copy the code
Child components
// Child.vue
<template>
<div>
<! Return each item with :scope="item" -->
<slot v-for="item in list" :scope="item" />
</div>
</template>
<script setup>
const props = defineProps({
list: {
type: Array.default: () = >[]}})</script>
Copy the code
I didn’t write a style, so using the HR element to make it visually clear is just lazy.
provide / inject
The way to use props and emit can be awkward when dealing with multiple passes. Provide and inject are available.
Provide is used in the parent component to pass down values.
Inject is used in child (descendant) components and can be value online.
No matter how deep the component hierarchy is, a parent component can act as a dependent provider for all its children.
Provide/Inject document
The parent component
// Parent.vue
<template>
<Child></Child>
</template>
<script setup>
import { ref, provide, readonly } from 'vue'
import Child from './components/Child.vue'
const name = ref('Down comes the Tiger')
const msg = ref('ray monkeys')
// Use readOnly to make it impossible for child components to modify directly; you need to call the method passed down on provide to modify
provide('name', readonly(name))
provide('msg', msg)
provide('changeName'.(value) = > {
name.value = value
})
</script>
Copy the code
Child components
// Child.vue
<template>
<div>
<div>msg: {{ msg }}</div>
<div>name: {{name}}</div>
<button @click="handleClick">Modify the</button>
</div>
</template>
<script setup>
import { inject } from 'vue'
const name = inject('name'.'hello') // Check if there is a value, if there is no value, the default value will apply (in this case, the default value is hello)
const msg = inject('msg')
const changeName = inject('changeName')
function handleClick() {
// This is not appropriate because vue recommends one-way data flow. This line of code does not take effect when the parent uses readOnly. It doesn't take effect until it's used.
// name. Value = 'name'
// The right way
changeName('A shock in the tiger's body')
// Because MSG is not readonly, you can change the value directly
msg.value = 'the world'
}
</script>
Copy the code
Provide can be used with readOnly, as shown in the examples and comments above.
In fact, provide and inject are mainly used to transmit values in deep relationships. The above example only has parent-child layer 2, just to illustrate my laziness.
Bus, bus
There is a bus-pass method in Vue2, which we can simulate ourselves in Vue3.
This approach is a bit like Vuex or Pinia, where there is a separate tool for controlling data.
But compared to Vuex or Pinia, the method we wrote ourselves doesn’t have very good features like data tracking.
The principle of
We create a bus.js file that controls data and registers events.
Bus.js has a Bus class
eventList
Is a required item used to store the event list.constructor
In addition to the ineventList
Except, all the other data is custom data, public data is stored here.$on
Method is used to register events.$emit
Method can be called$on
The events in the.$off
Method to log outeventList
The events in the.
Then the components that need to use the Bus, all import bus.js, can operate a piece of data together.
Bus.js
import { ref } from 'vue'
class Bus {
constructor() {
// Collect subscription information, dispatch center
this.eventList = {}, // Event list, this item is required
// The following are all custom values
this.msg = ref('This is a bus of information.')}/ / subscribe
$on(name, fn) {
this.eventList[name] = this.eventList[name] || []
this.eventList[name].push(fn)
}
/ / release
$emit(name, data) {
if (this.eventList[name]) {
this.eventList[name].forEach((fn) = >{ fn(data) }); }}// Unsubscribe
$off(name) {
if (this.eventList[name]) {
delete this.eventList[name]
}
}
}
export default new Bus()
Copy the code
The parent component
// Parent.vue
<template>
<div>The parent component:<span style="margin-right: 30px;">message: {{ message }}</span>
<span>msg: {{ msg }}</span>
</div>
<Child></Child>
</template>
<script setup>
import { ref } from 'vue'
import Bus from './Bus.js'
import Child from './components/Child.vue'
const msg = ref(Bus.msg)
const message = ref('hello')
// Use the listener notation
Bus.$on('changeMsg'.data= > {
message.value = data
})
</script>
Copy the code
Child components
// Child.vue
<template>
<div>Child components:<button @click="handleBusEmit">The trigger Bus. $emit</button>
<button @click="changeBusMsg">Modify the MSG in the bus</button>
</div>
</template>
<script setup>
import Bus from '.. /Bus.js'
function handleBusEmit() {
Bus.$emit('changeMsg'.'Thunder Monkey')}function changeBusMsg() {
// console.log(Bus.msg)
Bus.msg.value = 'Changed bus value in child component'
}
</script>
Copy the code
This method actually works pretty well, but it may seem a little confusing, so be sure to try it out with your own hands.
getCurrentInstance
Getcurrentinstance is a method provided by VUE that enables access to internal component instances.
GetCurrentInstance is only exposed for higher-order use scenarios, typically in libraries. The use of getCurrentInstance in application code is strongly discouraged. Don’t use it as an alternative to getting this in a composite API.
To put it bluntly, this approach is appropriate in the context of component library development, not for day-to-day business development.
getCurrentInstance
onlyinsetup 或Lifecycle hookIn the call.
Getcurrentinstance document
In
The parent component
// Parent.vue
<template>
<div>Parent component message value: {{message}}</div>
<button @click="handleClick">Get child components</button>
<Child></Child>
<Child></Child>
</template>
<script setup>
import { ref, getCurrentInstance, onMounted } from 'vue'
import Child from './components/Child.vue'
const message = ref('Thunder Monkey')
let instance = null
onMounted(() = > {
instance = getCurrentInstance()
})
// List of child components
let childrenList = []
// Register the component
function registrationCom(com) {
childrenList.push(com)
}
function handleClick() {
if (childrenList.length > 0) {
childrenList.forEach(item= > {
console.log('Component Instance:', item)
console.log('Component name:', item.type.name)
console.log('Component input field value:', item.devtoolsRawSetupState.inputValue)
console.log('-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --')}}}</script>
Copy the code
Child components
// Child.vue
<template>
<div>
<div>----------------------------</div>Child components:<button @click="handleClick">Gets the value of the parent component</button>
<br>
<input type="text" v-model="inputValue">
</div>
</template>
<script>
export default {
name: 'ccccc'
}
</script>
<script setup>
import { getCurrentInstance, onMounted, nextTick, ref } from 'vue'
const inputValue = ref(' ')
let instance = null
onMounted(() = > {
instance = getCurrentInstance()
nextTick(() = > {
instance.parent.devtoolsRawSetupState.registrationCom(instance)
})
})
function handleClick() {
let msg = instance.parent.devtoolsRawSetupState.message
msg.value = 'Hahahahahaha'
}
</script>
Copy the code
You can copy the code to your project and run it, better to type it again.
Vuex
Vuex addresses cross-component communication.
In Vue3, the Vuex v4.x version is required.
The installation
Install to the project using NPM or Yarn.
npm install vuex@next --save
# or
yarn add vuex@next --save
Copy the code
use
After the installation is successful, create a store directory in the SRC directory, and then create an index.js file in store.
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {},getters: {},mutations: {},actions: {},modules: {}})Copy the code
Enter the above content in store/index.js.
state
Data warehouse, for storing data.getters
: To get data, sort ofcomputed
In my opinion.mutations
Changes:state
The method of data should be written inmutations
In the water.actions
: async async async async, async methods are written here, but you still need to passmutations
To modify thestate
The data.modules
: the subcontract. If the project is large, the business can be divided into independent modules, and then file management and storage.
It is then introduced in SRC /main.js
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app
.use(store)
.mount('#app')
Copy the code
State
store/index.js
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
msg: 'ray monkeys'}})Copy the code
component
// xxx.vue
<script setup>
import { useStore } from 'vuex'
const store = useStore()
console.log(store.state.msg) / / ray monkey
</script>
Copy the code
Getter
I think getters are a little bit like computed.
So if we want to filter the data, or if we want to assemble the data when we return it, we can use the Getter method.
store/index.js
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
msg: 'ray monkeys'
},
getters: {
getMsg(state) {
return state.msg + 'the world! '}}})Copy the code
component
// xxx.vue
<script setup>
import { useStore } from 'vuex'
const store = useStore()
console.log(store.getters.getMsg) // Thunder Monkey world!
</script>
Copy the code
Mutation
Mutation is the only way to modify State data so that Vuex can track the data flow.
Call it from the component via commit.
store/index.js
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
msg: 'ray monkeys'
},
mutations: {
changeMsg(state, data) {
state.msg = data
}
}
})
Copy the code
component
// xxx.vue
<script setup>
import { useStore } from 'vuex'
const store = useStore()
store.commit('changeMsg'.'Garlic bastard')
console.log(store.state.msg) // Garlic
</script>
Copy the code
Action
I’m used to writing asynchronous stuff in the Action method and then calling it in the component using the Dispatch method.
store/index.js
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
msg: 'ray monkeys'
},
mutations: {
changeMsg(state, data) {
state.msg = data
}
},
actions: {
fetchMsg(context) {
// Simulate an Ajax request
setTimeout(() = > {
context.commit('changeMsg'.'Shark Pepper')},1000)}}})Copy the code
component
// xxx.vue
<script setup>
import { useStore } from 'vuex'
const store = useStore()
store.dispatch('fetchMsg')
</script>
Copy the code
Module
Module is known as subcontracting. This requires you to split the data from different modules into js files.
As an example, the table of contents is as follows
store
|- index.js
|- modules/
|- user.js
|- goods.js
Copy the code
index.js
External exit (Master document)modules/user.js
User dependent modulemodules/goods.js
The commodity module
index.js
import { createStore } from 'vuex'
import user from './modules/user'
import goods from './modules/goods'
export default createStore({
state: {},
getters: {},
mutations: {},
actions: {},
modules: {
user,
goods
}
})
Copy the code
user.js
const user = {
state: {},getters: {},mutations: {},actions: {}}export default user
Copy the code
goods.js
const goods = {
state: {},getters: {},mutations: {},actions: {}}export default goods
Copy the code
Then put the corresponding data and methods into each module.
Calling methods and accessing data in a component is pretty much the same as before.
So that’s the basic usage of Vuex. In addition, Vuex also has a variety of syntax candy, you can check the official documentation
Pinia
Pinia is one of the hottest tools these days and is also designed to handle cross-component communication, which is likely to become Vuex 5.
Pinia document
From my point of view after using Pinia for a while, Pinia has the following advantages over Vuex:
- The code is simpler when called.
- right
TS
More friendly. - merged
Vuex
的Mutation
和Action
. Natural support for asynchrony. - Natural subcontracting.
In addition, Pinia’s website says it works with Vue2 and Vue3. But I haven’t tried it in Vue2 and I’m too lazy to try it.
Pinia simplifies the state management module, using just these three things for most of your daily tasks.
state
: warehouse where data is storedgetters
: Get and filter datacomputed
A bit like)actions
: Stores “Modify”state
The method of”
Let me give you a simple example
The installation
npm install pinia
# or
yarn add pinia
Copy the code
registered
Create a store directory in SRC and create index.js and user.js in store
The directory structure is as follows
store
|- index.js
|- user.js
Copy the code
index.js
import { createPinia } from 'pinia'
const store = createPinia()
export default store
Copy the code
user.js
There are two common ways to write it, just pick one of them.
import { defineStore } from 'pinia'
/ / write 1
export const useUserStore = defineStore({
id: 'user'.// ID Must be unique
state: () = > {
return {
name: 'ray monkeys'}},getters: {
fullName: (state) = > {
return 'my name is' + state.name
}
},
actions: {
updateName(name) {
this.name = name
}
}
})
/ / write 2
export const useUserStore = defineStore('user', {state: () = > {
return {
name: 'ray monkeys'}},getters: {
fullName: (state) = > {
return 'my name is' + state.name
}
},
actions: {
updateName(name) {
this.name = name
}
}
})
Copy the code
Then add store/index.js to SRC /main.js
src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app
.use(store)
.mount('#app')
Copy the code
Used in components
component
// xxx.vue
<template>
<div>
<div>name: {{ name }}</div>
<div>FullName: {{fullName}}</div>
<button @click="handleClick">Modify the</button>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/store/user'
const userStore = useUserStore()
// const name = computed(() => userStore.name)
/ / advice
const { name, fullName } = storeToRefs(userStore)
function handleClick() {
// This is not recommended
// name. Value = 'name'
// The recommended way to write!!
userStore.updateName('bill')}</script>
Copy the code
long-winded
Pinia is similar to Vuex in that the default is subcontracting, and I support Pinia in this respect.
Pinia also offers a variety of syntax candies, which I strongly recommend reading the official documentation.
mitt.js
The Bus method we used earlier is actually similar to mitt.js, but mitt.js provides more methods.
Such as:
on
: Add eventsemit
: Execution eventoff
: Remove eventclear
: Clears all events
Mitt.js is not specifically for Vue, but Vue can use mitt.js for cross-component communication.
Making the address
NPM address
The installation
npm i mitt
Copy the code
use
Let me simulate the Bus mode.
I created three files in the same directory for emulation.
Parent.vue
Child.vue
Bus.js
Copy the code
Bus.js
// Bus.js
import mitt from 'mitt'
export default mitt()
Copy the code
Parent.vue
// Parent.vue
<template>
<div>
Mitt
<Child />
</div>
</template>
<script setup>
import Child from './Child.vue'
import Bus from './Bus.js'
Bus.on('sayHello'.() = > console.log('Thunder Monkey'))
</script>
Copy the code
Child.vue
// Child.vue
<template>
<div>Child:<button @click="handleClick">Say hello</button>
</div>
</template>
<script setup>
import Bus from './Bus.js'
function handleClick() {
Bus.emit('sayHello')}</script>
Copy the code
At this point, clicking the button on child.vue executes the methods defined in parent.vue on the console.
The usage of mitmitt. Js is actually very simple, it is recommended to follow the official example to type the code, a few minutes to get started.
Recommended reading
👍 Console. log can also be illustrated
👍 Vite Vue2 Project (Vue2 + VUE-Router + VUex)
If this article has been helpful to you, I hope you can click the “like” button