Pull to refresh

Frontend разработки порталов на СПО: делимся опытом

Reading time4 min
Views1.9K
В первой части статьи о том, как мы создаем портальные решения для крупнейших работодателей России, была описана архитектура со стороны backend-а. В данной статье мы перейдём к frontend-у.



Как уже отмечалось в первой части, основной нашей целью было разработать платформу, которую можно легко масштабировать и поддерживать.

Переиспользуемость

Фронт написан в большей части на Vue.js, и так как весь портал разбит на портлеты, каждый из них – это отдельный инстанс Vue со своим стором (Vuex), роутом (Vue Router) и компонентами. Каждый такой инстанс вынесен в свой репозиторий.

Так как портлет лежит в своем репозитории, встает вопрос о том, как не писать много однотипного кода для разных портлетов. Для решения этой проблемы мы выносим всё, что можно переиспользовать, в отдельный репозиторий, который потом подключается через .gitmodules. В данный момент таких сабмодулей два.

Один хранит общий функционал: это общие компоненты, сервисы, константы и т.д. У нас этот модуль называется Vue-common.

Во второй субмодуль вынесены настройки для сборки, он хранит конфиги для webpack-а, а также лоадеры и хелперы, необходимые при сборке. Этот модуль называется Vue-bundler.

Для удобства работы с API методы REST-а также были разделены на общие и локальные. Во Vue-common были вынесены методы для получения списка юзеров портала, методы администрирования, доступа к файловой системе портала и прочие. Все API endpoint-ы были вынесены в отдельные сервисы, которые регистрировались в точке входа и подключались к инстансу Vue. Затем они могли использоваться в любой точке приложения.

Каждый отдельный сервис регистрируется внутри плагина. Для подключения плагинов во Vue есть встроенная функция use. Подробнее о плагинах во Vue можно почитать тут.

Сам плагин инициализируется вот так:

class Api {
    constructor () {
        // Инстанс http клиента, который формирует запросы
        this.instance = instance

        // Так происходит регистрация общих сервисов
       // В каждый сервис прокидывается this, чтобы был доступ к инстансу http клиента
        Object.keys(commonServices).forEach(name => commonServices[name](this))
        
        // Так происходит регистрация локальных сервисов
	requireService.keys().forEach(filename => requireService(filename).default(this))
    }

    install () {
	Vue.prototype.$api= this
    }
}

export default new Api()

Кроме инициализации:

  1. Создается инстанс http клиента. В котором задается baseURL нашего backend-а и заголовки

    const instance = axios.create({
    	baseURL: '/example/api',
    	responseType: 'json',
    	headers: {
    		'Content-Type': 'application/json',
    		'Cache-Control': 'no-cache',
    		'Pragma': 'no-cache',
    	}
    })
                  Так как backend у нас рестовый, мы используем axios.

  2. Создаются сервисы, хранящие сами запросы

    // api - это наш http клиент
    export default api => {
    	api.exampleService= {
    		exampleGetRequest(params) {
    			return api.instance.request({
    				method: 'get',
    				url: `example/get`,
    				params
    			})
    		},
    		examplePostRequest(data) {
    			return api.instance.request({
    				method: 'post',
    				url: `example/post`,
    				data
    			})
    		},
    	}
    }
    

    Во vue-common-е достаточно создать только такой сервис, а регистрируется он уже для каждого портлета в классе Api
  3. Регистрируются общие и локальные сервисы

         const requireService = require.context('./service', false, /.service.js$/)
    

В компонентах они используются очень просто. Например:

export default {
	methods: {
		someMethod() {
    			this.$api.exampleService.exampleGetRequest()
}
}
}

Если нужно делать запросы за пределами приложения, то можно сделать так:

// Импортировать апи класс (@ - это алиас прописанный в конфиге)
import api from ‘@/api’

// И потом просто из него дергать нужный метод
api.exampleService.exampleGetRequest()

Маштабирование

Как отмечалось выше, для каждого портала собирается отдельный бандл, а для каждого бандла есть свои entry point-ы. В каждом из них происходит регистрация компонентов и ассетов, настройка авторизации для локальной разработки и подключение плагинов.

Компоненты регистрируются глобально для каждого приложения как локальные, так и общие.

Регистрация компонентов выглядит так:

import _ from “lodash”

const requireComponent = require.context('@/components', true, /^[^_].+\.vue$/i)

requireComponent.keys().forEach(filename => {
    const componentConfig = requireComponent(filename)

    // Get PascalCase name of component
    const componentName = _.upperFirst(
        _.camelCase(/\/\w+\.vue/.exec(filename)[0].replace(/^\.\//, '').replace(/\.\w+$/, ''))
    )

    Vue.component(componentName, componentConfig.default || componentConfig)
})

Иногда возникает необходимость для разрабатываемого нами портала добавить уникальную функциональность и для этого приходится писать компоненты, свойственные только ему, или просто по-другому реализовать тот или иной компонент. Достаточно создать компонент в отдельной папке, например /components-portal/*название портала*/*.vue, и зарегистрировать его в нужном entry point-е, добавив require.context не для одной папки, а для нескольких.

const contexts = [
    require.context('@/components', true, /^[^_].+\.vue$/i),
   require.context('@/components-portal/example', true, /^[^_].+\.vue$/i)
]

contexts.forEach(requireComponent => {
    requireComponent.keys().forEach(filename => {
        const componentConfig = requireComponent(filename)

        // Get PascalCase name of component
        const componentName = _.upperFirst(
            _.camelCase(/\/\w+\.vue/.exec(filename)[0].replace(/^\.\//, '').replace(/\.\w+$/, ''))
        )

        Vue.component(componentName, componentConfig.default || componentConfig)
    })
})

Если для компонента под определенный портал задать такое же имя, как из общей библиотеки компонентов, то он просто перепишется как свойство объекта и будет использован как компонент под данный портал.

Также глобально регистрируются ассеты, например svg иконки. Мы используем svg-sprite-loader, чтобы создать спрайт из svg иконок и потом использовать их через <use :xlink:href="#*название иконки*"/>

Регистрируются они так:

const requireAll = (r) => r.keys().forEach(r)

const requireContext = require.context('@/assets/icons/', true, /\.svg$/)

requireAll(requireContext)

Чтобы масштабировать не только функционал, но и стили компонентов, у нас реализован механизм смены стилей для определенного портала. В однофайловых компонентах указываются стили в теге <style>, и они применяются по умолчанию. Для того чтобы реализовать стили под конкретный портал, необходимо прописать их в ещё одном теге
Tags:
Hubs:
+6
Comments0

Articles

Information

Website
digdes.ru
Registered
Founded
Employees
501–1,000 employees
Location
Россия