📜 ⬆️ ⬇️

Dynamic loading of the template Vue component

Good day, dear Habrovchane! Recently, we, in our team, started using the Vue.js framework, including server rendering, after which we encountered a number of problems, in particular for me as a programmer.

Any change in the layout of the site took place through me. I was thrown off a part of the html code, be it a change in the header, or a change in the block locations, then it was necessary to insert this part into the required component, substitute the necessary variables and methods, launch the webpack, upload the code to the server.

It would be possible to use the webpack server in the monitoring mode, or give a list of necessary commands to their colleagues, which is somewhat complicated for them.
Therefore, we decided to make a dynamic loading of the template using data retrieval from the server.

As an example, consider a simplified version of this approach.
')
The structure is as follows:


In order to use template preloading in a component, you must wait for this data to be received from the server, for which purpose Promis are perfect. As a result, we got two wrappers over Vue components.

wrapComponent - for global registration of a Vue component

// ./wrapComponent.js import Vue from 'vue'; import axios from 'axios'; export default function wrapComponenet(name, template, component) { return () => { return new Promise((resolve, reject) => { axios.get(template).then((fetchData) => { const template = fetchData.data; Vue.component(name, { ...component, template, }); resolve(); }); }); }; } 

wrapPageComponent - to return a Vue component.

 // ./wrapPageComponent.js import axios from 'axios'; export default function wrapPageComponent(name, template, component) { return () => { return new Promise((resolve, reject) => { axios.get(template).then((fetchData) => { const template = fetchData.data; resolve({ ...component, template, }); }); }); }; } 

Most of the code used below is mostly taken from the official documentation on the server renderer vue.js ( ssr.vuejs.org ), so I’ll not dwell on this in detail.

 // ./server/index.js // Koa import Koa from 'koa'; import staticFile from 'koa-static'; //   Vue- import createApp from '../serverEntry.js'; // Vue  import { createRenderer } from 'vue-server-renderer'; const PORT = 4000; const server = new Koa(); server.use(staticFile('public')); server.use((ctx) => { //    const app = await createApp(); //  html -  const html = await renderer.renderToString(app); const page = ` <!DOCTYPE html> <html lang="ru"> <head> <title>Vue App</title> <base href="/"> <meta charset="utf-8"> </head> <body> <div id="root">${html}</div> <script src="js/app.js"></script> </body> </html> `); ctx.body = page; }); server.listen(PORT, (err) => { if (err) console.log(err); console.log(`Server started on ${PORT} port`); }); export default server; 

KoaJS is used as a server framework, although you can use any others or without them.

Next, we will consider the server entry point of the Vue application, everything is simple in it, wait for the application to be generated and return the result. In the future, this entry point is expanded by routing, storage, etc.

 // ./serverEntry.js import { createApp } from './app'; export default async (context) => { const { app } = await createApp(); return app; }; 

Approximately the same thing happens in the client entry point

 // ./client.js import { createApp } from './app'; createApp() .then(({ app }) => { app.$mount('#app'); }); 

Finally, we got to the Vue application itself.

 // ./app/index.js //  Vue import Vue from 'vue'; import VueAxios from 'vue-axios'; //   import axios from 'axios'; //  Vue import App from './App'; //   import mainMenu from './Components/MainMenu'; import mainContent from './Components/MainContent'; Vue.use(VueAxios, axios); axios.defaults.baseURL = 'http://localhost:4000/'; export async function createApp( context ) { const appComponent = await App(); const app = new Vue({ render: (h) => h(App), }); return new Promise((resolve, reject) => { //      Promise const allComponents = [ mainMenu(), mainContent(), ]; Promise.all(allComponents) .then(() => { resolve({ app, router }); }); }); } 

And not without reason, the Vue components themselves are wrapped in our wrappers (sorry for the tautology).

 // ./app/App.js import wrapPageComponenets from '../wrapPageComponents'; export default wrapAppComponenets('App', '/template/App.html', { name: 'App', }); 

 // ./app/Components/MainMenu.js import wrapComponenets from '../../wrapComponents'; export default wrapComponenets('main-menu', '/templates/MainMenu.html', { data() { return { title: 'VueJS App'}; } }) 

 // ./app/Components/MainContent.js import wrapComponenets from '../../wrapComponents'; export default wrapComponenets('main-component', '/templates/MainContent.html', { data() { return { name: ' !'}; }, methods: { clickHandle() { alert('   '); } } }); 

And templates that correspond to these components are in public / templates /

 <!-- ./public/templates/App.html --> <div> <main-menu></main-menu> <main-content></main-content> </div> 

 <!-- ./public/templates/MainMenu.html --> <nav> <ul> <li class="logo">{{title}}</li> </ul> </nav> 

 <!-- ./public/templates/MainContent.html --> <div> <h1 @click="clickHandle()">{{name}}</h1> </div> 

That's all. Now all templates are loaded from the server, and for my colleagues I can give a list of variables and methods that they can substitute into one or another template and my participation is reduced only to adding new methods and variables and a minimum of working with html templates. It also turned out to be much easier to explain the use of v-show, v-if, v-for directives.

Thanks for attention!

Source: https://habr.com/ru/post/345814/


All Articles