Preparation before starting
-
Clone elementui’s Master branch completely, and portal
-
NPM run dev:play NPM run dev:play NPM run dev:play NPM run dev:play On success, open localhost:8085,
-
Because NPM run dev:play is executed, the code is as follows
"dev:play": "npm run build:file && cross-env NODE_ENV=development PLAY_ENV=true webpack-dev-server --config build/webpack.demo.js"
Copy the code
We then look at build/webpack.demo.js and find the following code
entry: isProd ? {
docs: './examples/entry.js'
} : (isPlay ? './examples/play.js' : './examples/entry.js'),
Copy the code
Obviously, our entry point is./examples/play.js, and then we follow this file to the examples/play folder in the vue file, and we are done.
The official start of the
Document analysis
This component can be called either by instruction (via v-loading) or by Vue instance method (via this.$loading).
The directory structure
The code of the index file in the loading folder is as follows
import directive from './src/directive';
import service from './src/index';
export default {
install(Vue) {
Vue.use(directive);
Vue.prototype.$loading = service;
},
directive,
service
};
Copy the code
/ SRC /directive supports adding Vue instance method calls./ SRC /index supports adding Vue instance method calls. In addition, the install method is added on the object to facilitate the user to selectively obtain components through vue. use
Add Vue instance method
- Viewing source files
From the directory structure, we already know that the source files for both method calls are actuallyloading.vue
File, the other two are just extensions on this basis
The source code is as follows:
<template>
<transition name="el-loading-fade" @after-leave="handleAfterLeave">
<div
v-show="visible"
class="el-loading-mask"
:style="{ backgroundColor: background || '' }"
:class="[customClass, { 'is-fullscreen': fullscreen }]">
<div class="el-loading-spinner">
<svg v-if=! "" spinner" class="circular" viewBox="25 25 to 50 50">
<circle class="path" cx="50" cy="50" r="20" fill="none"/>
</svg>
<i v-else :class="spinner"></i>
<p v-if="text" class="el-loading-text">{{ text }}</p>
</div>
</div>
</transition>
</template>
<script>
export default {
data() {
return {
text: null.spinner: null.background: null.fullscreen: true.visible: false.customClass: ' '
};
},
methods: {
handleAfterLeave() {
this.$emit('after-leave');
},
setText(text) {
this.text = text; }}};</script>
Copy the code
The transition animation package is an absolute positioning box that contains the default SVG and self-positioning text, followed by an extension to index.js in the SRC folder. Because a large portion of the code deals with styles and hierarchies, I’ve left it out and just show the core code below:
import loadingVue from './loading.vue';
const LoadingConstructor = Vue.extend(loadingVue);
const defaults = {
text: null.fullscreen: true.body: false.lock: false.customClass: ' '
};
let fullscreenLoading;// To save the popover instance
// Close the popover method
function afterLeave(instance, callback, speed = 300, once = false) {
let called = false;
const afterLeaveCallback = function () {
if (called) return;
called = true;
if (callback) {
callback.apply(null.arguments); }};setTimeout(() = > {
afterLeaveCallback();
}, speed + 100);
};
LoadingConstructor.prototype.close = function () {
// Clear the popover instance
if (this.fullscreen) {
fullscreenLoading = undefined;
}
afterLeave(this._= > {
// Delete the node
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
}
this.$destroy();
}, 300);
this.visible = false;
};
let service = (options = {}) = > {
// Add Object. Assign profill
options = Object.assign({}, defaults, options);
if (typeof options.target === 'string') {
options.target = document.querySelector(options.target);
}
options.target = options.target || document.body;
if(options.target ! = =document.body) {
options.fullscreen = false;
} else {
options.body = true;
}
// There can only be one loading to cover the entire body
if (options.fullscreen && fullscreenLoading) {
return fullscreenLoading;
}
let instance = new LoadingConstructor({
el: document.createElement('div'),
data: options
});
// If there is no target, the popover will be mounted directly on the body
let parent = options.body ? document.body : options.target;
parent.appendChild(instance.$el);
Vue.nextTick(() = > {
instance.visible = true;
});
// Assign the instance to fullscreenLoading
if (options.fullscreen) {
fullscreenLoading = instance;
}
return instance;
}
Vue.prototype.$loading = service
Copy the code
So let’s verify this one last time
<div id="app">
<! As the loading component is absolutely positioned, the parent node will be added el-loading-parent--relative-->
<div id="loading" class="el-loading-parent--relative"></div>
</div>
Copy the code
instantiation
new Vue({ el: '#app', data: function () { return { visible: true } }, mounted() { const loading = this.$loading({ lock: True, text: 'Loading', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)', target: "#loading" }) setTimeout(() => { loading.close(); }, 2000); }})Copy the code
The renderings are as follows
Custom instruction
For custom directives, the detailed pre-information is still the most complete official documentation, and the next step in the portal is the source code in Elementui, which I have simplified to make it easier to read
import loadingVue from './loading.vue';
const Mask = Vue.extend(loadingVue);
Vue.directive('loading', {
// only called once when the directive is first bound to an element. This is where you can perform one-time initialization Settings
bind: function (el, binding, vnode) {
const textExr = el.getAttribute('element-loading-text');
const spinnerExr = el.getAttribute('element-loading-spinner');
const backgroundExr = el.getAttribute('element-loading-background');
const customClassExr = el.getAttribute('element-loading-custom-class');
const vm = vnode.context;
const mask = new Mask({
el: document.createElement('div'),
data: {
text: vm && vm[textExr] || textExr,
spinner: vm && vm[spinnerExr] || spinnerExr,
background: vm && vm[backgroundExr] || backgroundExr,
customClass: vm && vm[customClassExr] || customClassExr,
fullscreen:!!!!! binding.modifiers.fullscreen } }); el.instance = mask; el.mask = mask.$el; el.maskStyle = {}; binding.value && toggleLoading(el, binding); },update: function (el, binding) {
el.instance.setText(el.getAttribute('element-loading-text'));
if (binding.oldValue !== binding.value) {
toggleLoading(el, binding);
}
},
unbind: function (el, binding) {
if (el.domInserted) {
el.mask &&
el.mask.parentNode &&
el.mask.parentNode.removeChild(el.mask);
toggleLoading(el, { value: false.modifiers: binding.modifiers }); } el.instance && el.instance.$destroy(); }});const toggleLoading = (el, binding) = > {
if (binding.value) {
Vue.nextTick(() = > {
// Determine the fullscreen, if any, and bind it to the body
if (binding.modifiers.fullscreen) {
insertDom(document.body, el, binding);
} else {
// Determine whether to bind to the body
if (binding.modifiers.body) {
insertDom(document.body, el, binding);
} else{ insertDom(el, el, binding); }}}); }else {
afterLeave(el.instance, _= > {
if(! el.instance.hiding)return;
el.domVisible = false;
el.instance.hiding = false;
}, 300.true);
el.instance.visible = false;
el.instance.hiding = true; }};const insertDom = (parent, el, binding) = > {
// Determine if styles in domVisible and EL are missing or hidden
if(! el.domVisible && el.style['display']! = ='none' && el.style['visibility']! = ='hidden') {
Object.keys(el.maskStyle).forEach(property= > {
el.mask.style[property] = el.maskStyle[property];
});
el.domVisible = true;
parent.appendChild(el.mask);
Vue.nextTick(() = > {
if (el.instance.hiding) {
el.instance.$emit('after-leave');
} else {
el.instance.visible = true; }}); el.domInserted =true;
} else if (el.domVisible && el.instance.hiding === true) {
el.instance.visible = true;
el.instance.hiding = false; }};Copy the code
Add the install method to the loadingDirective. Add the install method to the loadingDirective. Finally, it is called in the outer index file through vue.use, and the simplified code is as follows
const loadingDirective = {};
loadingDirective.install = Vue= > {
// The above code
}
Copy the code
Below is a sketch of the source codeAt this point, the loading component’s source code analysis is complete