Skip to content

Commit f2cff55

Browse files
committedFeb 27, 2021
feat: beforeCreateVueInstance can now return a promise
1 parent c0f3e50 commit f2cff55

File tree

3 files changed

+98
-90
lines changed

3 files changed

+98
-90
lines changed
 

‎index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ declare namespace VueCustomElement {
1414
connectedCallback?: () => void;
1515
disconnectedCallback?: () => void;
1616
attributeChangedCallback?: (name: string, oldValue: any, value: any) => void;
17-
beforeCreateVueInstance?: (rootElement: ComponentOptions<Vue>) => ComponentOptions<Vue>;
17+
beforeCreateVueInstance?: (rootElement: ComponentOptions<Vue>) => ComponentOptions<Vue> | Promise<ComponentOptions<Vue>>;
1818
vueInstanceCreatedCallback?: () => void;
1919
destroyTimeout?: number;
2020
props?: ComponentOptions<Vue>['props'];

‎src/utils/createVueInstance.js

+89-83
Original file line numberDiff line numberDiff line change
@@ -10,110 +10,116 @@ import { customEmit } from './customEvent';
1010
* @param componentDefinition
1111
* @param props
1212
* @param options
13+
* @returns {Promise} true if vue instance was created, false if instance already existed
1314
*/
1415
export default function createVueInstance(element, Vue, componentDefinition, props, options) {
15-
if (!element.__vue_custom_element__) {
16-
const ComponentDefinition = Vue.util.extend({}, componentDefinition);
17-
const propsData = getPropsData(element, ComponentDefinition, props);
18-
const vueVersion = (Vue.version && parseInt(Vue.version.split('.')[0], 10)) || 0;
16+
if (element.__vue_custom_element__) {
17+
return Promise.resolve(element);
18+
}
19+
const ComponentDefinition = Vue.util.extend({}, componentDefinition);
20+
const propsData = getPropsData(element, ComponentDefinition, props);
21+
const vueVersion = (Vue.version && parseInt(Vue.version.split('.')[0], 10)) || 0;
1922

20-
// Auto event handling based on $emit
21-
function beforeCreate() { // eslint-disable-line no-inner-declarations
22-
this.$emit = function emit(...args) {
23-
customEmit(element, ...args);
24-
this.__proto__ && this.__proto__.$emit.call(this, ...args); // eslint-disable-line no-proto
25-
};
26-
}
27-
ComponentDefinition.beforeCreate = [].concat(ComponentDefinition.beforeCreate || [], beforeCreate);
23+
// Auto event handling based on $emit
24+
function beforeCreate() { // eslint-disable-line no-inner-declarations
25+
this.$emit = function emit(...args) {
26+
customEmit(element, ...args);
27+
this.__proto__ && this.__proto__.$emit.call(this, ...args); // eslint-disable-line no-proto
28+
};
29+
}
30+
ComponentDefinition.beforeCreate = [].concat(ComponentDefinition.beforeCreate || [], beforeCreate);
2831

29-
if (ComponentDefinition._compiled) { // eslint-disable-line no-underscore-dangle
30-
let constructorOptions = {}; // adjust vue-loader cache object if necessary - https://github.com/vuejs/vue-loader/issues/83
31-
const constructor = ComponentDefinition._Ctor; // eslint-disable-line no-underscore-dangle
32-
if (constructor) { // eslint-disable-line no-underscore-dangle
33-
constructorOptions = Object.keys(constructor).map(key => constructor[key])[0].options; // eslint-disable-line no-underscore-dangle
34-
}
35-
constructorOptions.beforeCreate = ComponentDefinition.beforeCreate;
32+
if (ComponentDefinition._compiled) { // eslint-disable-line no-underscore-dangle
33+
let constructorOptions = {}; // adjust vue-loader cache object if necessary - https://github.com/vuejs/vue-loader/issues/83
34+
const constructor = ComponentDefinition._Ctor; // eslint-disable-line no-underscore-dangle
35+
if (constructor) { // eslint-disable-line no-underscore-dangle
36+
constructorOptions = Object.keys(constructor).map(key => constructor[key])[0].options; // eslint-disable-line no-underscore-dangle
3637
}
38+
constructorOptions.beforeCreate = ComponentDefinition.beforeCreate;
39+
}
3740

38-
let rootElement;
39-
40-
if (vueVersion >= 2) {
41-
const elementOriginalChildren = element.cloneNode(true).childNodes; // clone hack due to IE compatibility
42-
// Vue 2+
43-
rootElement = {
44-
propsData,
45-
props: props.camelCase,
46-
computed: {
47-
reactiveProps() {
48-
const reactivePropsList = {};
49-
props.camelCase.forEach((prop) => {
50-
typeof this[prop] !== 'undefined' && (reactivePropsList[prop] = this[prop]);
51-
});
41+
let rootElement;
5242

53-
return reactivePropsList;
54-
}
55-
},
56-
/* eslint-disable */
57-
render(createElement) {
58-
const data = {
59-
props: this.reactiveProps
60-
};
43+
if (vueVersion >= 2) {
44+
const elementOriginalChildren = element.cloneNode(true).childNodes; // clone hack due to IE compatibility
45+
// Vue 2+
46+
rootElement = {
47+
propsData,
48+
props: props.camelCase,
49+
computed: {
50+
reactiveProps() {
51+
const reactivePropsList = {};
52+
props.camelCase.forEach((prop) => {
53+
typeof this[prop] !== 'undefined' && (reactivePropsList[prop] = this[prop]);
54+
});
6155

62-
return createElement(
63-
ComponentDefinition,
64-
data,
65-
getSlots(elementOriginalChildren, createElement)
66-
);
56+
return reactivePropsList;
6757
}
68-
/* eslint-enable */
69-
};
70-
} else if (vueVersion === 1) {
71-
// Fallback for Vue 1.x
72-
rootElement = ComponentDefinition;
73-
rootElement.propsData = propsData;
74-
} else {
75-
// Fallback for older Vue versions
76-
rootElement = ComponentDefinition;
77-
const propsWithDefault = {};
78-
Object.keys(propsData)
79-
.forEach((prop) => {
80-
propsWithDefault[prop] = { default: propsData[prop] };
81-
});
82-
rootElement.props = propsWithDefault;
83-
}
58+
},
59+
/* eslint-disable */
60+
render(createElement) {
61+
const data = {
62+
props: this.reactiveProps
63+
};
8464

85-
const elementInnerHtml = vueVersion >= 2 ? '<div></div>' : `<div>${element.innerHTML}</div>`.replace(/vue-slot=/g, 'slot=');
86-
if (options.shadow && element.shadowRoot) {
87-
element.shadowRoot.innerHTML = elementInnerHtml;
88-
rootElement.el = element.shadowRoot.children[0];
89-
} else {
90-
element.innerHTML = elementInnerHtml;
91-
rootElement.el = element.children[0];
92-
}
65+
return createElement(
66+
ComponentDefinition,
67+
data,
68+
getSlots(elementOriginalChildren, createElement)
69+
);
70+
}
71+
/* eslint-enable */
72+
};
73+
} else if (vueVersion === 1) {
74+
// Fallback for Vue 1.x
75+
rootElement = ComponentDefinition;
76+
rootElement.propsData = propsData;
77+
} else {
78+
// Fallback for older Vue versions
79+
rootElement = ComponentDefinition;
80+
const propsWithDefault = {};
81+
Object.keys(propsData)
82+
.forEach((prop) => {
83+
propsWithDefault[prop] = { default: propsData[prop] };
84+
});
85+
rootElement.props = propsWithDefault;
86+
}
9387

94-
reactiveProps(element, props);
88+
const elementInnerHtml = vueVersion >= 2 ? '<div></div>' : `<div>${element.innerHTML}</div>`.replace(/vue-slot=/g, 'slot=');
89+
if (options.shadow && element.shadowRoot) {
90+
element.shadowRoot.innerHTML = elementInnerHtml;
91+
rootElement.el = element.shadowRoot.children[0];
92+
} else {
93+
element.innerHTML = elementInnerHtml;
94+
rootElement.el = element.children[0];
95+
}
9596

96-
if (typeof options.beforeCreateVueInstance === 'function') {
97-
rootElement = options.beforeCreateVueInstance(rootElement) || rootElement;
98-
}
97+
if (options.shadow && options.shadowCss && element.shadowRoot) {
98+
const style = document.createElement('style');
99+
style.type = 'text/css';
100+
style.appendChild(document.createTextNode(options.shadowCss));
101+
102+
element.shadowRoot.appendChild(style);
103+
}
104+
105+
reactiveProps(element, props);
99106

107+
if (typeof options.beforeCreateVueInstance === 'function') {
108+
rootElement = options.beforeCreateVueInstance(rootElement) || rootElement;
109+
}
110+
111+
return Promise.resolve(rootElement).then((vueOpts) => {
100112
// Define the Vue constructor to manage the element
101-
element.__vue_custom_element__ = new Vue(rootElement);
113+
element.__vue_custom_element__ = new Vue(vueOpts);
102114
element.__vue_custom_element_props__ = props;
103115
element.getVueInstance = () => {
104116
const vueInstance = element.__vue_custom_element__;
105117
return vueInstance.$children.length ? vueInstance.$children[0] : vueInstance;
106118
};
107119

108-
if (options.shadow && options.shadowCss && element.shadowRoot) {
109-
const style = document.createElement('style');
110-
style.type = 'text/css';
111-
style.appendChild(document.createTextNode(options.shadowCss));
112-
113-
element.shadowRoot.appendChild(style);
114-
}
115120
element.removeAttribute('vce-cloak');
116121
element.setAttribute('vce-ready', '');
117122
customEmit(element, 'vce-ready');
118-
}
123+
return element;
124+
});
119125
}

‎src/vue-custom-element.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ function install(Vue) {
2525
}
2626
if (!this.__detached__) {
2727
if (isAsyncComponentPromise) {
28-
asyncComponentPromise.then((lazyLoadedComponent) => {
29-
const lazyLoadedComponentProps = getProps(lazyLoadedComponent);
30-
createVueInstance(this, Vue, lazyLoadedComponent, lazyLoadedComponentProps, options);
31-
typeof options.vueInstanceCreatedCallback === 'function' && options.vueInstanceCreatedCallback.call(this);
28+
asyncComponentPromise.then((lazyComponent) => {
29+
const lazyProps = getProps(lazyComponent);
30+
createVueInstance(this, Vue, lazyComponent, lazyProps, options).then(() => {
31+
typeof options.vueInstanceCreatedCallback === 'function' && options.vueInstanceCreatedCallback.call(this);
32+
});
3233
});
3334
} else {
34-
createVueInstance(this, Vue, componentDefinition, props, options);
35-
typeof options.vueInstanceCreatedCallback === 'function' && options.vueInstanceCreatedCallback.call(this);
35+
createVueInstance(this, Vue, componentDefinition, props, options).then(() => {
36+
typeof options.vueInstanceCreatedCallback === 'function' && options.vueInstanceCreatedCallback.call(this);
37+
});
3638
}
3739
}
3840

0 commit comments

Comments
 (0)
Please sign in to comment.