In most Web products, the global Message component occupies a large usage scenario, often in the context of giving user feedback, Message prompts, and conversations with the system. If you use traditional component writing, you need to import the component, register it in components, and then call it as a tag in the template, passing in custom props and firing an event through the emit. This type of component has the following disadvantages:
- Frequent introduction and registration is required
- Components need to be used as labels in a template
- Additional parameters are required to control the properties and state of the component
- The mount location of an uncustomizable component may be affected by other components
So for a component like Message, we want to be able to call it in JavaScript, pass in custom parameters to control the state of the component, and not have to manually mount the component to the end of the body when called. If you’ve used major third-party libraries such as ElementUI Plus or Ant Design for Vue, you’re familiar with their Message component apis, so let’s work together to implement a global Message component using Vue3.
Component final implementation effect
Component design
Define the final component API
Implement a simple Message component with type apis such as text, Success, and error, which support passing in text directly or through component-specific option configuration. From defining the message content, the close delay, and whether to display the close button.
// Message type (type) : text, success, or failure
["text"."success"."error"]
// Message (option)
[String]: Message content [Object]: Message configuration/ / configuration option
text [String] ""Duration message content [Number] 0The number of milliseconds of automatic closing delay,0To not automatically close close [Boolean] falseWhether to display the close button// Call mode
Message[type](option);
Copy the code
Invoke the sample
Message.text("This is a message tip.");
Message.error({
text: "Network error, please try again later".duration: 3000.close: true
});
Copy the code
Defining the component structure
Create a Message folder to store the overall structure of the component, where SRC contains the component’s templates, styles, and instance files, and, at the same level, create index.js to expose the entire component for inclusion in project and business components.
|--- Message
|--- src
| |--- Message.vue // Component templates
| |--- Message.less // Provide component style support
| |--- Message.js // Read the configuration and render the component instance
| |--- Instance.js // Component instance
|---index.js // Expose components
Copy the code
Templates and Styles
Template Template
The template is relatively simple and is wrapped in an animation component that controls message display and closing through v-show. The content section includes ICONS, message text, and a configurable manual close button.
<template>
<! -- Message list -->
<transition name="slide-fade">
<div class="message-container" v-show="visibled">
<! - content - >
<div class="message-content">
<! -- The icon of the message type is determined by the message type. The icon of the text type is not configured -->
<div class="message-icon" v-if="config.icon">
<i :class="config.icon"></i>
</div>
<! -- Message text -->
<span v-text="config.content"></span>
<! -- Turn off messages manually -->
<div class="option" v-if=! "" config.close">
<i class="ri-close-fill" @click="onClose"></i>
</div>
</div>
</div>
</transition>
</template>
Copy the code
The message icon
It is important to note that the Icon is determined by the type in the calling API. The Icon type is determined when the instance is created. This is the open source Icon library Remix Icon.remixicon.cn/
style
Define styles and animations in message.less.
@radius: 4px;
@normalHeight: 34px;
.message {
position: fixed;
top: 0;
left: 0;
width: 100%;
text-align: center;
box-sizing: border-box;
z-index: 9999;
transform: translateZ(9999px);
padding-top: 28px;
transition: top .4s ease;
.message-container {
margin-bottom: 14px;
.message-icon {
display: inline-block;
i {
font-size: 18px;
font-weight: 400;
margin-top: -3px;
margin-right: 6px;
display: inline-block;
box-sizing: border-box;
vertical-align: middle;
}
.ri-checkbox-circle-fill {
color: #58c05b;
}
.ri-close-circle-fill {
color: #fd4f4d;
}
.message-content {
display: inline-block;
padding: 4px 18px;
height: @normalHeight;
text-align: left;
line-height: @normalHeight;
font-size: 14px;
font-weight: 400;
border-radius: @radius;
color: # 595959;
box-shadow: 0 4px 12px rgba(0.0.0.15);
background: #ffffff;
.option {
display: inline-block;
pointer-events: all;
margin-left: 18px;
i {
font-size: 18px;
font-weight: 400;
margin-top: -3px;
display: inline-block;
box-sizing: border-box;
vertical-align: middle;
cursor: pointer;
color: #d9d9d9;
transition: color 0.2 s ease;
&:hover {
color: #ff7c75;
transition: color 0.2 s ease;
}
}
}
}
}
.slide-fade-enter-active {
transition: all .2s ease-out;
}
.slide-fade-leave-active {
transition: all .2s ease;
}
.slide-fade-enter-from..slide-fade-leave-to {
transform: translateY(-20px);
opacity: 0; }}Copy the code
Component script
The component implements rendering and unmounting by obtaining the incoming config configuration and remove. The onOpen and onClose methods control the opening and manual closing of messages. The specific code is as follows:
<script>
import { reactive, toRefs } from "vue";
export default {
props: {
config: { type: Object.default: () = >{}},// Message configuration items
remove: { type: Function.default: () = >{}},// Cancel the mount callback
},
setup(props) {
const state = reactive({
visibled: false,})// Open the message
const onOpen = (config) = > {
setTimeout(() = > {
state.visibled = true;
}, 10)
// Remove the message after the specified time
if(config.duration ! = =0) {
setTimeout(() = > {
onClose();
}, config.duration);
}
}
onOpen(props.config)
// The message is closed
const onClose = () = > {
state.visibled = false;
setTimeout(() = > {
props.remove()
}, 200)};return{... toRefs(state), onOpen, onClose, }; }}; </script>Copy the code
Creating a component Instance
In instance.js, we will write apis for creating, mounting, and destructing components when a component is called. In the header, we will introduce Vue’s create Instance method and the component template written above:
import { createApp } from 'vue'
import Message from './Message.vue'
Copy the code
Declare the instance action and accept a message configuration parameter CFG
/** * Message instance operation *@param {Object} CFG instance configuration */
const createInstance = cfg= > {
const config = cfg || {}
// 1, create a container, and set the outer Class attribute, message count
// 2. Create an instance and mount it to the body
// 3. Realize the method of canceling mount, and re-count after canceling mount
}
export default createInstance
Copy the code
1, create a wrapper container and set the Class attribute for the outer layer
Create a DIV as an outer container to wrap the component and set the corresponding class attribute
let messageNode = document.createElement('div')
let attr = document.createAttribute("class")
attr.value = "message"
messageNode.setAttributeNode(attr)
Copy the code
Message count, we define a message box height of 54 px, when multiple messages are queued open, by setting the top value to stagger the components.
const height = 54 // The height of a single message box
const messageList = document.getElementsByClassName('message')
messageNode.style.top = `${messageList.length * height}px`
Copy the code
2, create an instance and mount it to body
const app = createApp(Message, {
config,
remove() {
handleRemove()// Remove the element. When the message is closed, unmount and remove it from the Dom}})// Mount the instance and append it to the end of body
app.vm = app.mount(messageNode)
document.body.appendChild(messageNode)
app.close = () = > {
handleRemove()
}
return app
Copy the code
3. The methods for unmounting and resetting the top value are defined
const handleRemove = () = >{
app.unmount(messageNode)
document.body.removeChild(messageNode)
resetMsgTop()
}
const resetMsgTop = () = > {
for (let i = 0; i < messageList.length; i++) {
messageList[i].style.top = `${i * height}px`}}Copy the code
Implement the rendering instance API
Message.js reads the configuration and renders it.
import createInstance from './Instance.js'
/** * read the configuration and render Message *@param {Object} TypeCfg Type configuration *@param {Object/String} CFG User-defined configuration */
function renderMsg(typeCfg = {}, cfg = ' ') {
// Direct incoming message content is allowed, so determine the type of CFG that is passed in
const isContent = typeof cfg === 'string'
// Integrate custom configurations
cfg = isContent ? {
content: cfg
} : cfg
const config = Object.assign({}, typeCfg, cfg) // Merge the configurations
const {
type = 'text'.// Message type
content = ' '.// Message content
icon = ' '.// Message icon
duration = 3000.// Automatically close the delay time
close = false // Whether to display the close button
} = config
// Create an instance
return createInstance({
type,
content,
duration,
icon,
close
})
}
Copy the code
exposedtext
,success
,error
Such as API.
export default {
// A plain text message
text(cfg = "") {
const textCfg = {
type: "text".icon: ' '
}
return renderMsg(textCfg, cfg);
},
// Success
success(cfg = "") {
const successCfg = {
type: "success".icon: 'ri-checkbox-circle-fill'
}
return renderMsg(successCfg, cfg);
},
// Error message
error(cfg = "") {
const errorCfg = {
type: "error".icon: 'ri-close-circle-fill'
}
returnrenderMsg(errorCfg, cfg); }},Copy the code
Finally, open the component for invocation in the outermost index.js.
import Message from './src/Message.js';
export default Message;
Copy the code