Доброго времени суток, уважаемые Хабровчане! С недавнего времени, мы, в нашей команде начали использовать фреймворк Vue.js включая серверный рендеринг, после чего столкнулись с рядом проблем, в частности для меня как программиста.
Любое изменение в верстке сайта, происходило через меня. Мне скидывали часть html кода, будь то изменение заголовка, или смена мест блоков, далее было необходимо вставить эту часть в требуемый компонент, подставить необходимые переменные и методы, запустить webpack, залить код на сервер.
Можно было бы использовать на сервере webpack в режиме наблюдения, или дать перечень необходимых команд своим коллегам, что для них оказывается несколько сложным.
Поэтому приняли решение сделать динамическую загрузку шаблона с помощью получения данных с сервера.
В качестве примера, рассмотрим упрощенный вариант такого подхода.
Структура следующая:
Что-бы использовать предзагрузку шаблона в компонент, необходимо дождаться получения этих данных с сервера, для чего прекрасно подойдут Promis'ы. В итоге у нас получилось две обертки над Vue компонентами.
wrapComponent — для глобальной регистрации Vue-компонета
wrapPageComponent — для возврата Vue-компонента.
Большая часть кода используемая ниже в большей степени взята с официальной документации по серверному рендеру vue.js (ssr.vuejs.org), поэтому детально на этом останавливаться не стану.
В качестве серверного фреймворка используется KoaJS, хотя Вы можете использовать любые другие или вовсе без них.
Далее рассмотрим серверную точку входа Vue-приложения, в ней все просто, дожидаемся генерации приложения и возвращаем результат. В дальнейшем данная точка входа расширяется роутингом, хранилищами и т.д.
Примерно тоже самое происходит и в клиентской точке входа
И наконец мы подобрались к самому Vue-приложению.
И не посредственно сами Vue-компоненты обернуты нашими обертками (извините за тавтологию).
И соответствующие данным компонентам шаблоны которые находятся в public/templates/
Вот и все. Теперь все шаблоны подгружаются с сервера, и для своих коллег я могу дать список переменных и методов которые они могут подставлять в тот или иной шаблон и мое участие сводится лишь к добавлению новых методов и переменных и минимум работы с html — шаблонами. Так же оказалось гораздо проще объяснить использование директив v-show,v-if,v-for.
Спасибо за внимание!
Любое изменение в верстке сайта, происходило через меня. Мне скидывали часть html кода, будь то изменение заголовка, или смена мест блоков, далее было необходимо вставить эту часть в требуемый компонент, подставить необходимые переменные и методы, запустить webpack, залить код на сервер.
Можно было бы использовать на сервере webpack в режиме наблюдения, или дать перечень необходимых команд своим коллегам, что для них оказывается несколько сложным.
Поэтому приняли решение сделать динамическую загрузку шаблона с помощью получения данных с сервера.
В качестве примера, рассмотрим упрощенный вариант такого подхода.
Структура следующая:
- public/ — директория содержащая статические файлы
- templates/ — директория содержащая шаблоны компонентов
- server/ — код серверной части
- app/ — код Vue приложения
- client.js — точка входа клиентской части Vue приложения
- serverEntry.js — точка входа серверной части Vue приложения
Что-бы использовать предзагрузку шаблона в компонент, необходимо дождаться получения этих данных с сервера, для чего прекрасно подойдут Promis'ы. В итоге у нас получилось две обертки над Vue компонентами.
wrapComponent — для глобальной регистрации Vue-компонета
// ./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 — для возврата Vue-компонента.
// ./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, }); }); }); }; }
Большая часть кода используемая ниже в большей степени взята с официальной документации по серверному рендеру vue.js (ssr.vuejs.org), поэтому детально на этом останавливаться не стану.
// ./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, хотя Вы можете использовать любые другие или вовсе без них.
Далее рассмотрим серверную точку входа Vue-приложения, в ней все просто, дожидаемся генерации приложения и возвращаем результат. В дальнейшем данная точка входа расширяется роутингом, хранилищами и т.д.
// ./serverEntry.js import { createApp } from './app'; export default async (context) => { const { app } = await createApp(); return app; };
Примерно тоже самое происходит и в клиентской точке входа
// ./client.js import { createApp } from './app'; createApp() .then(({ app }) => { app.$mount('#app'); });
И наконец мы подобрались к самому Vue-приложению.
// ./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 }); }); }); }
И не посредственно сами Vue-компоненты обернуты нашими обертками (извините за тавтологию).
// ./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('И еще раз привет'); } } });
И соответствующие данным компонентам шаблоны которые находятся в 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>
Вот и все. Теперь все шаблоны подгружаются с сервера, и для своих коллег я могу дать список переменных и методов которые они могут подставлять в тот или иной шаблон и мое участие сводится лишь к добавлению новых методов и переменных и минимум работы с html — шаблонами. Так же оказалось гораздо проще объяснить использование директив v-show,v-if,v-for.
Спасибо за внимание!
