- Регистрация
- 14.05.16
- Сообщения
- 11.398
- Реакции
- 501
- Репутация
- 0
В
Как уже отмечалось в первой части, основной нашей целью было разработать платформу, которую можно легко масштабировать и поддерживать.
Переиспользуемость
Фронт написан в большей части на 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()
Кроме инициализации:
В компонентах они используются очень просто. Например:
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 иконок и потом использовать их через
Регистрируются они так:
const requireAll = (r) => r.keys().forEach(r)
const requireContext = require.context('@/assets/icons/', true, /\.svg$/)
requireAll(requireContext)
Чтобы масштабировать не только функционал, но и стили компонентов, у нас реализован механизм смены стилей для определенного портала. В однофайловых компонентах указываются стили в теге
You must be registered for see links
статьи о том, как мы создаем портальные решения для крупнейших работодателей России, была описана архитектура со стороны backend-а. В данной статье мы перейдём к frontend-у.Как уже отмечалось в первой части, основной нашей целью было разработать платформу, которую можно легко масштабировать и поддерживать.
Переиспользуемость
Фронт написан в большей части на Vue.js, и так как весь портал разбит на портлеты, каждый из них – это отдельный инстанс Vue со своим стором (Vuex), роутом (Vue Router) и компонентами. Каждый такой инстанс вынесен в свой репозиторий.
Так как портлет лежит в своем репозитории, встает вопрос о том, как не писать много однотипного кода для разных портлетов. Для решения этой проблемы мы выносим всё, что можно переиспользовать, в отдельный репозиторий, который потом подключается через .gitmodules. В данный момент таких сабмодулей два.
Один хранит общий функционал: это общие компоненты, сервисы, константы и т.д. У нас этот модуль называется Vue-common.
Во второй субмодуль вынесены настройки для сборки, он хранит конфиги для webpack-а, а также лоадеры и хелперы, необходимые при сборке. Этот модуль называется Vue-bundler.
Для удобства работы с API методы REST-а также были разделены на общие и локальные. Во Vue-common были вынесены методы для получения списка юзеров портала, методы администрирования, доступа к файловой системе портала и прочие. Все API endpoint-ы были вынесены в отдельные сервисы, которые регистрировались в точке входа и подключались к инстансу Vue. Затем они могли использоваться в любой точке приложения.
Каждый отдельный сервис регистрируется внутри плагина. Для подключения плагинов во Vue есть встроенная функция use. Подробнее о плагинах во Vue можно почитать
You must be registered for see links
.Сам плагин инициализируется вот так:
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()
Кроме инициализации:
- Создается инстанс 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.
- Создаются сервисы, хранящие сами запросы
// 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 - Регистрируются общие и локальные сервисы
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 иконок и потом использовать их через
Регистрируются они так:
const requireAll = (r) => r.keys().forEach(r)
const requireContext = require.context('@/assets/icons/', true, /\.svg$/)
requireAll(requireContext)
Чтобы масштабировать не только функционал, но и стили компонентов, у нас реализован механизм смены стилей для определенного портала. В однофайловых компонентах указываются стили в теге