preface
We already know how to create a tag component by creating the first vUE component air-UI (5) — Button. In this section we continue to look at how to create service-type components.
Create a component of service type
Take the notification component as an example. It is a typical built-in service component. It is bound to the prototype of Vue and used as a global method.
The global method of setting up a VUE is quite common, such as using AXIos to mount a Vue prototype when making ajax requests, as follows:
// 1 introduces vue and axios
import Vue from 'vue'
import axios from 'axios'
// 2 Some encapsulation of AXIos
// code ...
// 3 Then mount it to the prototype
Vue.prototype.$axios = axios
Copy the code
Use this.$axios
// The axios.get() method works like this
this.$axios.get()
Copy the code
It’s really convenient that you don’t have to import every component that uses Axios. This approach is quite simple, and the built-in services work much the same way, but with some DOM manipulation involved. Take notification for example: Element-UI notification notifications
There are three steps, and the directory structure is as follows:
components/
| |--- notification/
| | |--- src/
| | | |--- main.js
| | | |--- main.vue
| | |--- index.js
Copy the code
Create a VUE component
Because involves the dom manipulation, so the first step is to create a notification corresponding vue component, is the notification/SRC/main vue:
<template> <transition name="air-notification-fade"> <div :class="['air-notification', customClass, horizontalClass]" v-show="visible" :style="positionStyle" @mouseenter="clearTimer()" @mouseleave="startTimer()" @click="click" role="alert" > <i class="air-notification__icon" :class="[ typeClass, iconClass ]" v-if="type || iconClass"> </i> <div class="air-notification__group" :class="{ 'is-with-icon': typeClass || iconClass }"> <h2 class="air-notification__title" v-text="title"></h2> <div class="air-notification__content" v-show="message"> <slot> <p v-if="! dangerouslyUseHTMLString">{{ message }}</p> <p v-else v-html="message"></p> </slot> </div> <div class="air-notification__closeBtn air-icon-close" v-if="showClose" @click.stop="close"></div> </div> </div> </transition> </template> <script type="text/babel"> let typeMap = { success: 'success', info: 'info', warning: 'warning', error: 'error' }; export default { data() { return { visible: false, title: '', message: '', duration: 4500, type: '', showClose: true, customClass: '', iconClass: '', onClose: null, onClick: null, closed: false, verticalOffset: 0, timer: null, dangerouslyUseHTMLString: false, position: 'top-right' }; }, computed: { typeClass() { return this.type && typeMap[this.type] ? `air-icon-${ typeMap[this.type] }` : ''; }, horizontalClass() { return this.position.indexOf('right') > -1 ? 'right' : 'left'; }, verticalProperty() { return /^top-/.test(this.position) ? 'top' : 'bottom'; }, positionStyle() { return { [this.verticalProperty]: `${ this.verticalOffset }px` }; } }, watch: { closed(newVal) { if (newVal) { this.visible = false; this.$el.addEventListener('transitionend', this.destroyElement); } } }, methods: { destroyElement() { this.$el.removeEventListener('transitionend', this.destroyElement); this.$destroy(true); this.$el.parentNode.removeChild(this.$el); }, click() { if (typeof this.onClick === 'function') { this.onClick(); } }, close() { this.closed = true; if (typeof this.onClose === 'function') { this.onClose(); } }, clearTimer() { clearTimeout(this.timer); }, startTimer() { if (this.duration > 0) { this.timer = setTimeout(() => { if (! this.closed) { this.close(); } }, this.duration); } }, keydown(e) { if (e.keyCode === 46 || e.keyCode === 8) { this.clearTimer(); Else if (e.keycode === 27) {// Esc closes message if (! this.closed) { this.close(); } } else { this.startTimer(); }}, mounted() {if (this.duration > 0) {this.timer = setTimeout(() => {if (! this.closed) { this.close(); } }, this.duration); } document.addEventListener('keydown', this.keydown); }, beforeDestroy() { document.removeEventListener('keydown', this.keydown); }}; </script>Copy the code
The specific logic is not detailed, it is an ordinary VUE component, nothing more than a DOM structure, and the triggering and monitoring of various events and parameters, it is not difficult to understand, can be understood as the DOM rendering of notification component.
Constructs the structure of notification
Now that the DOM is ready, it’s just a matter of building the Notification structure and letting the various methods of that structure manipulate the DOM. So the notification/SRC/main. Js:
import Vue from 'vue';
import Main from './main.vue';
import merge from '.. /.. /.. /.. /src/utils/merge';
import { PopupManager } from '.. /.. /.. /.. /src/utils/popup';
import { isVNode } from '.. /.. /.. /.. /src/utils/vdom';
const NotificationConstructor = Vue.extend(Main);
let instance;
let instances = [];
let seed = 1;
const Notification = function(options) {
if (Vue.prototype.$isServer) return;
options = merge({}, options);
const userOnClose = options.onClose;
const id = 'notification_' + seed++;
const position = options.position || 'top-right';
options.onClose = function() {
Notification.close(id, userOnClose);
};
instance = new NotificationConstructor({
data: options
});
if (isVNode(options.message)) {
instance.$slots.default = [options.message];
options.message = 'REPLACED_BY_VNODE';
}
instance.id = id;
instance.$mount();
document.body.appendChild(instance.$el);
instance.visible = true;
instance.dom = instance.$el;
instance.dom.style.zIndex = PopupManager.nextZIndex();
let verticalOffset = options.offset || 0;
instances.filter(item= > item.position === position).forEach(item= > {
verticalOffset += item.$el.offsetHeight + 16;
});
verticalOffset += 16;
instance.verticalOffset = verticalOffset;
instances.push(instance);
return instance;
};
['success'.'warning'.'info'.'error'].forEach(type= > {
Notification[type] = options= > {
if (typeof options === 'string' || isVNode(options)) {
options = {
message: options
};
}
options.type = type;
return Notification(options);
};
});
Notification.close = function(id, userOnClose) {
let index = - 1;
const len = instances.length;
const instance = instances.filter((instance, i) = > {
if (instance.id === id) {
index = i;
return true;
}
return false; }) [0];
if(! instance)return;
if (typeof userOnClose === 'function') {
userOnClose(instance);
}
instances.splice(index, 1);
if (len <= 1) return;
const position = instance.position;
const removedHeight = instance.dom.offsetHeight;
for (let i = index; i < len - 1; i++) {
if (instances[i].position === position) {
instances[i].dom.style[instance.verticalProperty] =
parseInt(instances[i].dom.style[instance.verticalProperty], 10) - removedHeight - 16 + 'px'; }}}; Notification.closeAll =function() {
for (let i = instances.length - 1; i >= 0; i--) { instances[i].close(); }};export default Notification;
Copy the code
This logic is not difficult to understand, mainly divided into several steps:
- Previously created vUE components, through
Vue.extend
To generate aA subclass
const NotificationConstructor = Vue.extend(Main);
Copy the code
This can be understood as a constructor for Notification. As long as new initializes this function, it can generate an instantiation object for Notification:
instance = new NotificationConstructor({
data: options
});
Copy the code
So the Notification object is essentially a factory function that, whenever called, instantiates a brand new object with a Notification Vue component.
- Instantiate the object, mount it, and add it to the body to display
instance.$mount(); // Without el, an unmounted instance will be mounted and the template will be rendered as an element outside the document
document.body.appendChild(instance.$el); // You must use the native DOM API to insert it into the document
Copy the code
- And then we have this
Notification
Other methods on this object, for exampleclose
And so on, with a global object in a closure stateinstances
To hold these instantiated objects and operate on these instances by id
In general, the logic is not complicated.
The final index. Js
Notification /index.js: notification/index.js:
import Notification from './src/main.js';
export default Notification;
Copy the code
Mount to the Vue prototype
The next step is to mount it, again in the components/index.js install method, but instead of using component syntax, mount it directly to the vue prototype:
import Button from './button'
import ButtonGroup from './button-group'
import Notification from './notification'
const components = {
Button,
ButtonGroup
}
const install = function (Vue) {
Object.keys(components).forEach(key= > {
Vue.component(components[key].name, components[key])
})
Vue.prototype.$notify = Notification
}
export default {
install
}
Copy the code
SRC /styles/index.scss SRC /styles/index.scss
@import "./base.scss";
@import "./button.scss";
@import "./button-group.scss";
@import "./notification.scss";
Copy the code
Write down the example
Home. Vue:
<air-button plain @click="open1">Automatic closing</air-button>
<air-button plain @click="open2">Does not automatically shut down</air-button>
<air-button plain @click="open3">successful</air-button>
<air-button plain @click="open4">warning</air-button>
<air-button plain @click="open5">The message</air-button>
<air-button plain @click="open6">error</air-button>
Copy the code
Script fills the corresponding trigger method:
<script> export default {data () {return {MSG: 'air-ui - based on vue2.x, reusable UI components'}}, methods: { open1() { const h = this.$createElement; This.$notify({title: 'title ', message: h(' I ', {style: 'color: teal'},' teal')}); }, open2() {this.$notify({title: 'warning ', message:' this is a message that does not close automatically ', duration: 0}); }, open3() {this.$notify({title: 'success', message: 'success', type: 'success'}); }, open4() {this.$notify({title: 'warning', message: 'warning', type: 'warning'}); }, open5() {this.$notify.info({title: 'message ', message:' this is a message '}); }, open6() {this.$notify.error({title: 'error ', message:' error '}); } } } </script>Copy the code
Notice how this.$nofify is called: this.$nofify
In this way, a component of the built-in service is created.
Pay attention to
Note that the components of the built-in service cannot be called with vue.use (), otherwise the project will run once by default, even if they are not used. For example, I changed SRC /main.js to show a reference to notification mount and use:
import Notification from './components/notification'
Vue.prototype.$notify = Notification
Vue.use(Notification)
Copy the code
In this case, compilation is fine, but when I load the page, it will default to an empty popup box.
This is because when I call use, I execute a Notification object by default to instantiate a Notification object, but because no arguments are passed in, it is an empty popup. So for built-in service components such as Notification, Message, MessageBox, as long as they are bound to the VUE prototype object, do not use to introduce.
conclusion
The creation of built-in service components, and that’s about it. In the next section we’ll see how to create a directive component.
Series of articles:
- Air-ui (1) — Why do I need to build an Element UI component
- Self-built VUE component AIR-UI (2) — Take a look at the Element UI project
- Self-built VUE component AIR-UI (3) – CSS development specification
- Air-ui (4) — Air-UI environment setup and directory structure
- Air-ui (5) — Create the first vUE component, Button
- Self-built VUE component AIR-UI (6) – Creates built-in service components
- Build vUE component AIR-UI (7) – Create command component
- Self-built VUE component AIR-UI (8) — Implementation part introduces components
- Build your own VUE component air-UI (9) — document with Vuepress
- Air-ui (10) — Vuepress Documentation (Advanced version)
- Vue Component Air-UI (11) — Vuepress Documentation (Crawl version)
- Self-built VUE component AIR-UI (12) — Internationalization mechanism
- Self-built VUE Component AIR-UI (13) — Internationalization Mechanism (Advanced Version)
- Self-built VUE component AIR-UI (14) — Packaged Build (Dev and Dist)
- Self-built VUE component AIR-UI (15) — Theme customization
- Self-built VUE component AIR-UI (16) – Packages to build pub tasks
- Build your own VUE component AIR-UI (17) – Develop a pit crawl and summary