
Содержание статьи
Гарет Фуллер

Сейчас я работаю с устаревшей программой Rails. Мы медленно переходим интерфейс от представлений Rails к программе Vue.
В рамках этого перехода, у нас есть некоторые элементы, которые мы хотим превратить в глобальные компоненты Vue. Мы хотим иметь возможность использовать эти компоненты в существующих представлениях Rails и в программе Vue без необходимости настраивать каждый компонент для обработки обеих ситуаций.
Прежде чем поделиться своим решением этой проблемы, вот пример однофайлового компонента Vue, который мы хотим использовать в обоих сценариях (просмотр Rails и программа Vue):
// Payments.vue
<template lang="html"> <div id="payments> <div class="payment" v-for="payment in payments> {{ payment.amount }} </div> </div></template>
<script>export default { name: 'payments'
props: { payments: { type: Array } }}</script>
<style lang="scss" scoped></style>
В программе Vue это простой компонент для использования, не правда ли? Например:
// app/javascript/App.vue
<template lang="html"> <div id="app> <payments :payments="payments" /> </div></template>
<script>import Payments from './Payments.vue'
export default { name: 'app',
components: { Payments },
data () { return { payments: [ { amount: 123.00 }, { amount: 124.00 } ] } }</script>
<style lang="scss" scoped></style>
Но как насчет использования его в представлении Rails?
Решение
Итак, решения для использования Payments.vue компонент в Rails выглядит так:
// app/views/payments/index.haml
.global-comp = content_tag 'comp-wrapper', nil, data: { component: 'payments', props: { payments: @payments } }.to_json
Давайте разберем этот элемент.
.global-comp
есть div (с классом “global-comp”) для монтирования сверхпростого экземпляра Vue. Этот экземпляр Vue дает нам компонент-калитку для использования. CompWrapper.vue (в минуту мы разберемся, для чего предназначен CompWrapper).
Вот экземпляр Vue, к которому установлен .global-comp
:
// app/javascript/packs/global_comp.js
import Vue from 'vue/dist/vue.esm'import CompWrapper from './CompWrapper'
document.addEventListener('DOMContentLoaded', () => { const app = new Vue({ components: { CompWrapper } }).$mount('.global-comp')})
Все это делает компонент (CompWrapper.vue), доступный нам в div с классом global-comp
.
Если вы используете Webpacker из Rails, вам нужно будет включить этот пакет в ваш макет где-нибудь перед закрывающим тегом body. Например:
// app/views/layouts/application.haml
...
= javascript_pack_tag "global_comp"
CompWrapper.vue
Это самое интересное. CompWrapper.vue позволяет нам передать:
- Название компонента, который мы хотим использовать, например, «платежи»
- Реквизит, который мы хотим передать ему
Общая цель этого компонента-оболочки состоит в том, чтобы позволить нам передавать переменные экземпляра Rails, например @payments
в наши компоненты как реквизиты без необходимости обрабатывать это изнутри каждого компонента Payments.vue.
Так вот CompWrapper.vue:
// app/javascript/CompWrapper.vue
<template lang="html"> <component :is="data.component" v-bind="data.props"></component></template>
<script>import * as components from './components'
export default { name: 'comp-wrapper',
components,
data () { return { data: {} } },
created() { this.data = JSON.parse(this.$attrs.data) }}</script>
<style lang="scss" scoped></style>
Первое, что делает компонент CompWrapper, берет атрибуты данных, которые вы устанавливаете для элемента в представлении Rails, анализирует JSON и устанавливает внутренний атрибут данных Vue с анализируемыми данными:
created() { this.data = JSON.parse(this.$attrs.data)}
с this.data
set мы можем использовать его для выбора компонента, который мы хотим использовать, и передать ему свойства, которые мы предоставили в нашем представлении Rails с помощью динамического компонента Vue:
<component :is="data.component" v-bind="data.props"></component>
И все это!
Теперь мы можем использовать компоненты Vue, как они предназначены для использования, но в наших представлениях rails. ?