Desenvolvimento front-end de portais em software de código aberto: compartilhe experiência

Na primeira parte do artigo sobre como criamos soluções de portal para os maiores empregadores da Rússia, a arquitetura foi descrita pelo back-end. Neste artigo, passaremos para o frontend.



Como já observado na primeira parte, nosso principal objetivo era desenvolver uma plataforma que possa ser facilmente dimensionada e mantida.

A reutilização do

Front é escrita principalmente no Vue.js e, como todo o portal é dividido em portlets, cada um deles é uma instância separada do Vue com seu próprio lado (Vuex), rota (Vue Router) e componentes. Cada uma dessas instâncias é movida para seu próprio repositório.

Como o portlet está em seu repositório, surge a questão de como não escrever muito do mesmo tipo de código para portlets diferentes. Para resolver esse problema, transferimos tudo o que pode ser reutilizado para um repositório separado, que é então conectado via .gitmodules. No momento, existem dois desses submódulos.

Um armazena funcionalidade comum: estes são componentes, serviços, constantes, etc. Temos este módulo chamado Vue-common.

O segundo submódulo contém configurações para montagem, armazena configurações para webpack, além de carregadores e auxiliares necessários durante a montagem. Este módulo é chamado Vue-bundler.

Para facilitar o trabalho com a API, os métodos REST também foram divididos em geral e local. No Vue-common, foram introduzidos métodos para obter uma lista de usuários do portal, métodos de administração, acesso ao sistema de arquivos do portal e outros. Todos os pontos de extremidade da API foram movidos para serviços separados, registrados no ponto de entrada e conectados à instância do Vue. Em seguida, eles podem ser usados ​​em qualquer lugar do aplicativo.

Cada serviço individual é registrado dentro do plug-in. O Vue possui uma função de uso integrada para conectar plug-ins. Leia mais sobre plugins no Vue aqui .

O próprio plugin é inicializado assim:

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()

Além da inicialização:

  1. A instância do cliente http é criada. Que define a baseURL do nosso back-end e os cabeçalhos

    const instance = axios.create({
    	baseURL: '/example/api',
    	responseType: 'json',
    	headers: {
    		'Content-Type': 'application/json',
    		'Cache-Control': 'no-cache',
    		'Pragma': 'no-cache',
    	}
    })
                    backend   ,   axios.

  2. São criados serviços que armazenam as próprias solicitações

    // 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
    			})
    		},
    	}
    }
    

    No vue-common, basta criar apenas um serviço desse tipo e já está registrado para cada portlet na classe Api
  3. Serviços gerais e locais são registrados

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

Nos componentes, eles são usados ​​de maneira muito simples. Por exemplo:

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

Se você precisar fazer solicitações fora do aplicativo, poderá fazer o seguinte:

//    (@ -     )
import api from ‘@/api’

//        
api.exampleService.exampleGetRequest()

Dimensionamento

Conforme observado acima, um pacote configurável separado é coletado para cada portal e cada pacote configurável possui seus próprios pontos de entrada. Em cada um deles, os componentes e ativos são registrados, a autorização é configurada para o desenvolvimento local e os plug-ins são conectados.

Os componentes são registrados globalmente para cada aplicativo, local e geral.

O registro do componente se parece com o seguinte:

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)
})

Às vezes, torna-se necessário que o portal que estamos desenvolvendo adicione funcionalidade exclusiva e, para isso, você precise escrever componentes exclusivos ou simplesmente implementar um componente de uma maneira diferente. É suficiente criar um componente em uma pasta separada, por exemplo / components-portal / * nome do portal * / *. Vue, e registrá-lo no ponto de entrada desejado, adicionando require.context não para uma pasta, mas para várias.

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)
    })
})

Se você definir o mesmo nome para o componente para um portal específico a partir da biblioteca geral de componentes, ele será simplesmente reescrito como uma propriedade do objeto e será usado como um componente para este portal.

Ativos como ícones svg também são registrados globalmente. Usamos svg-sprite-loader para criar um sprite a partir de ícones svg e depois os usamos via <use: xlink: href = "# * icon name *" />

Eles são registrados assim:

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

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

requireAll(requireContext)

Para dimensionar não apenas a funcionalidade, mas também os estilos de componentes, implementamos um mecanismo para alterar os estilos de um portal específico. Os componentes de arquivo único especificam estilos na tag <style> e são usados ​​por padrão. Para implementar estilos para um portal específico, é necessário registrá-los em outra tag

All Articles