vue-component-holder
v0.0.17
Published
Inject asynchronized hooks on vue components
Downloads
7
Maintainers
Readme
vue-component-holder
Navigation
Introduction
This plugin introduces a component placeholder machanism. A placeholder component replaces the original child component in the template, and the plugin creates/mounts/destroys the child component by itself. This child component then becomes an "mvm" (managed vm). If vue updates the placeholder, the plugin applies the same changes to the mvm. Since the mvm is totally managed by the plugin, it is possible to inject some (asynchronized) custom hooks before and after the creation. For example, the plugin injects an "asyncData" hook in each mvm to prefetch asynchronous data (Nuxt is only able to do it in page components).
Component Tree and Mvm Tree
It also supports user-defined hooks. The graph below shows the calling sequence with two user-defined hooks: "beforeInit" and "inited"
The Calling Sequence with user-defined "beforeInit" and "inited" hooks
To declare an mvm, just wrap a child component with "vue-holder" as follows:
<!-- MyParentComponent.vue -->
<template>
<div>
<vue-holder name="MyFavoriteHolderName">
<MyChildComponent />
</vue-holder>
</template>
To prevent vue from managing mvms, we need to edit the template AST generated by vue-component-compiler. This is done by the "holdify" function we inject into "vue-loader" (details in Installation). In the previous example, the child's template is moved from the default slot to the template attribute as follows:
<vue-holder name="MyFavoriteHolderName" template="<MyChildComponent />">
</vue-holder>
Then, the plugin could make a render function for this mvm based on the results of "holdify". The mvm is created after running this render function.
Below is an example of declaring "fetch" and "asyncData" hooks:
/** MyChildComponent.vue */
<template>
<div>
<div>Name: {{ name }}</div>
<div>Recommendations: {{ $store.state.recommendations }} </div>
</div>
</template>
<script>
export default {
async fetch({ $store, $route }) {
const { data } = await axios.get(`/api/my-recommendations/${$route.params.id}`)
$store.commit('updateRecommandations', data)
},
async asyncData({ $route }) {
const { data } = await axios.get(`/api/my-data/${$route.params.id}`)
return { name: data.name }
}
}
</script>
Please refer vue-component-holder-demo for more examples.
Installation
npm
npm install vue-component-holder --save
yarn
yarn add vue-component-holder
Add the following in vue.config.js:
/** vue.config.js */
module.exports = {
// ...
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
options.compilerModules = options.compilerModules || [];
options.compilerModules.push({
postTransformNode: require('vue-component-holder/holdify')()
});
return options;
});
// Cache may cause the injected 'holdify' option skipped
config.module
.rule('vue')
.uses.delete('cache-loader');
}
}
If you are using webpack.config.js, add the following at module.exports -> modules -> rules -> (vue) -> use -> options:
/** webpack.config.js */
compilerModules: [{
postTransformNode: require('vue-component-holder/holdify')()
}]
Install the plugin in the early stage of your project:
import Vue from 'vue'
import VueComponentHolder from 'vue-component-holder'
Vue.use(VueComponentHolder)
Holdify
HTML Template
If the child component is in "v-for" loop(s), it could have multiple mvm instances. In this case, the "holdify" function will add more attributes on the placeholder component. For example, the "uid" attribute is added to identify different mvms, and the "vars" attribute is added to pass local variables to the mvm's render function. Please view our "holdify" examples or try it yourself on Holdify Demo.
JSX
Automatic "holdify" in JSX has not been implemented yet. To use "vue-holder" with JSX, you have to do "holdify" manually by providing the neccessary attributes.
Configuration
A "holder.config.js" file is used to configure the plugin. Place it at the same level with the "node_modules" folder where the plugin was installed. Normally, it should be placed in the project root folder.
/** holder.config.js */
module.exports = {
// plugin configs as below
}
globalHolderMixin
This option is default to "true". If it is "false", you need to setup "HolderMixin" manually as follows:
/** holder.config.js */
module.exports = {
globalHolderMixin: false
}
/** MyParentComponent.vue */
import { HolderMixin } from 'vue-component-holder'
export default {
mixins: [ HolderMixin /* more mixins*/ ]
}
/** MyChildComponent.vue */
import { HolderMixin } from 'vue-component-holder'
export default {
mixins: [ HolderMixin /* more mixins*/ ]
}
customHooks
You could inject asynchronized custom hooks in the parent component. They are partitioned into two groups: "preInitMvms" and "postInitMvms", indicating if they are excuted before or after the mvms are initialized.
/** holder.config.js */
module.exports = {
customHooks: {
preInitMvms: [ 'beforeInit' ],
postInitMvms: [ 'inited' ]
}
}
/** MyParentComponent.vue */
export default {
async beforeInit() {
},
async inited() {
}
}
APIs and Hooks
Please refer vue-component-holder-demo for examples.
mvmsUpdated
A hook, defined in the parent component, is called everytime there are some mvms created/deleted/updated.
asyncData
A hook, defined in the child component, is called before an mvm is created, which helps to fetch async data.
fetch
A hook, defined in the child component, is called before an mvm is created, which helps to update stores.
$intf
This function retrieves the interface object of a specific mvm with the holder name and uid.
$publish (or @Public)
With the "$publish" function, an mvm reveals some functions via its interface object. If working with vue-class-component, it could also use the "@Public" decorator.
registerHolders
A hook, defined in the parent component, is used for registering holders manually. Normally, a holder is automatically registered in the "created" hook of the 'vue-holder' component. However, if a "vue-holder" component is defined in "v-if" or "v-for", the parent component may not be able to perceive it at run time. This may prevent an mvm root from calling its user-defined hooks. In this case, we need to register it manually as follows.
<!-- MyMvmRootComponent.vue -->
<template>
<div>
<div v-if="isDataLoaded">
<vue-holder name="MyFavoriteHolderName">
<MyChildComponent />
</vue-holder>
</div>
</template>
<script>
export default {
// ...
registerHolders() {
return { name: 'MyFavoriteHolderName' };
},
// ...
}
</script>