Développement frontend de portails sur des logiciels open source: partager l'expérience

Dans la première partie de l' article sur la façon dont nous créons des solutions de portail pour les plus grands employeurs en Russie, l'architecture a été décrite par le backend. Dans cet article, nous passerons au frontend.



Comme déjà indiqué dans la première partie, notre objectif principal était de développer une plateforme facilement évolutive et maintenable.

Réutilisation La

face avant est principalement écrite dans Vue.js, et puisque l'ensemble du portail est divisé en portlets, chacun d'eux est une instance de Vue distincte avec ses propres Vuex, Vue Router et composants. Chacune de ces instances est déplacée vers son propre référentiel.

Étant donné que le portlet est dans son référentiel, la question se pose de savoir comment ne pas écrire beaucoup du même type de code pour différents portlets. Pour résoudre ce problème, nous transférons tout ce qui peut être réutilisé dans un référentiel séparé, qui est ensuite connecté via .gitmodules. Il existe actuellement deux sous-modules de ce type.

On stocke des fonctionnalités communes: ce sont des composants, des services, des constantes, etc. communs. Nous avons ce module appelé Vue-common.

Le deuxième sous-module contient les paramètres d'assemblage, il stocke les configurations pour webpack, ainsi que les chargeurs et assistants nécessaires lors de l'assemblage. Ce module est appelé Vue-bundler.

Pour la commodité de travailler avec l'API, les méthodes REST ont également été divisées en général et local. Dans Vue-common, des méthodes ont été introduites pour obtenir une liste des utilisateurs du portail, les méthodes d'administration, l'accès au système de fichiers du portail, etc. Tous les points de terminaison API ont été déplacés vers des services distincts qui se sont enregistrés au point d'entrée et connectés à l'instance Vue. Ensuite, ils pourraient être utilisés n'importe où dans l'application.

Chaque service individuel est enregistré dans le plugin. Vue a une fonction d'utilisation intégrée pour connecter les plugins. En savoir plus sur les plugins dans Vue ici .

Le plugin lui-même est initialisé comme ceci:

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

En plus de l'initialisation:

  1. L'instance du client http est créée. Qui définit l'URL de base de notre backend et les en-têtes

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

  2. Des services sont créés qui stockent les demandes elles-mêmes

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

    En vue commune, il suffit de créer uniquement un tel service, et il est déjà enregistré pour chaque portlet de la classe Api
  3. Les services généraux et locaux sont enregistrés

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

Dans les composants, ils sont utilisés très simplement. Par exemple:

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

Si vous devez faire des demandes en dehors de l'application, vous pouvez le faire:

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

//        
api.exampleService.exampleGetRequest()

Mise à l'échelle

Comme indiqué ci-dessus, un ensemble distinct est collecté pour chaque portail et chaque ensemble a ses propres points d'entrée. Dans chacun d'eux, les composants et les actifs sont enregistrés, l'autorisation est configurée pour le développement local et les plug-ins sont connectés.

Les composants sont enregistrés globalement pour chaque application, à la fois locale et générale.

L'enregistrement des composants ressemble à ceci:

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

Parfois, il devient nécessaire pour le portail que nous développons d'ajouter des fonctionnalités uniques, et pour cela, vous devez écrire des composants qui lui sont propres, ou simplement implémenter un composant d'une manière différente. Il suffit de créer un composant dans un dossier séparé, par exemple / components-portal / * portal name * / *. Vue, et de l'enregistrer dans le point d'entrée souhaité, en ajoutant require.context non pas pour un dossier, mais pour plusieurs.

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

Si vous définissez le même nom pour le composant d'un portail spécifique que dans la bibliothèque générale de composants, il sera simplement réécrit en tant que propriété d'objet et sera utilisé en tant que composant pour ce portail.

Les actifs tels que les icônes svg sont également enregistrés dans le monde entier. Nous utilisons svg-sprite-loader pour créer un sprite à partir des icônes svg, puis nous les utilisons via <use: xlink: href = "# * icon name *" />

Ils sont enregistrés comme ceci:

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

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

requireAll(requireContext)

Afin de mettre à l'échelle non seulement les fonctionnalités, mais également les styles de composants, nous avons mis en place un mécanisme de modification des styles pour un portail particulier. Les composants à fichier unique spécifient des styles dans la balise <style> et sont utilisés par défaut. Afin d'implémenter des styles pour un portail spécifique, vous devez les enregistrer dans une autre balise

All Articles