Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Business logic in Vue.js
Search
Sobolev Nikita
August 22, 2019
Programming
340
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Business logic in Vue.js
Sobolev Nikita
August 22, 2019
More Decks by Sobolev Nikita
See All by Sobolev Nikita
Чего вы не знали о строках в Python – Василий Рябов, PythoNN
sobolevn
0
250
ИИ-Агенты в каждый дом – Алексей Порядин, PythoNN
sobolevn
0
220
Внутреннее устройство сборки мусора в CPython 3.14+ – Сергей Мирянов, PythoNN
sobolevn
0
140
Генератор байткода и байткод генератора, Михаил Ефимов, PythoNN
sobolevn
0
140
Дотянуться до кремния. HighLoad Python: SIMD, GPU – Пётр Андреев, PythoNN
sobolevn
0
130
Проектирование — это когда чувствуешь, а не какие-то там циферки, Николай Хитров, PythoNN
sobolevn
0
150
Continuous profiling, Давид Джалаев, PythoNN
sobolevn
0
180
Михаил Гурбанов – Are you NATS? @ PythoNN
sobolevn
0
340
Дмитрий Бровкин – Почему исправление опечаток сложнее, чем кажется, и как мы с этим српавляемся @ PythoNN
sobolevn
0
79
Other Decks in Programming
See All in Programming
Building an Out-of-Order CPU
latte72
1
760
AI駆動開発にグラフDBを重ねてみた
satoshi256kbyte
2
790
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.5k
AI × TiDD / 2026.09.05 Redmine 大阪
tokudiro
1
130
How I Stole PSI from Android Studio - DroidKaigi2026
worker8
0
110
LL言語やWebフレームワークのPostgreSQL対応 〜DBの機能がユーザーに届くまで〜
kentaroutakeda
0
150
テストを司るデーモンに会いに行く 〜隔離した仮想マシンでテストを通すまで〜
h1d3mun3
1
210
The Good Stuff, Not the Slop: Engineering High-Quality Android Apps with Modern AI Tooling
danybony
1
240
What We Talk About When We Talk About XP
m_seki
2
580
自分的「カンファレンスの楽しみ方」
syumai
0
200
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
300
From 6 People Classroom Meetup to 100 People Regional Conference / FOSS4G Hiroshima 2026
furukawayasuto
0
150
Featured
See All Featured
For a Future-Friendly Web
brad_frost
183
10k
Heart Work Chapter 1 - Part 1
lfama
PRO
9
36k
Embracing the Ebb and Flow
colly
88
5.2k
Git: the NoSQL Database
bkeepers
PRO
432
67k
How STYLIGHT went responsive
nonsquared
100
6.3k
Statistics for Hackers
jakevdp
799
230k
Un-Boring Meetings
codingconduct
0
410
Fireside Chat
paigeccino
43
4k
Deep Space Network (abreviated)
tonyrice
0
300
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Marketing to machines
jonoalderson
1
5.8k
Transcript
Никита Соболев github.com/sobolevn 1
Бизнес логика и Vue.js !2
Композиция и абстракция !3
Но во Vue.js - компоненты! !4
отображение = f(состояния) !5
отображение = f(состояния) !6
export default { data () { return { username: ''
} }, template: ` <button click="loadUser">Hello {{ username }}</button> `, loadUser () { this.username = this.$axios.get('...').data }, } !7
отображение = f(состояния) !8
export default { data () { return { username: ''
} }, template: ` <button click="loadUser">Hello {{ username }}</button> `, loadUser () { this.username = this.$axios.get('...').data }, } !9
export default { data () { return { username: ''
} }, template: ` <button click="loadUser">Hello {{ username }}</button> `, loadUser () { this.username = this.$axios.get('...').data }, } !10
f = check( transform( fetch() ) ) !11
f = fetch() |> transform() |> check() !12
Кто видит проблему? !13
Изменение логики !14
export default { data () { return { username: ''
} }, template: ` <button click="loadUser">Hello {{ username }}</button> `, loadUser () { this.username = localStorage.getItem('user') }, } !15
Плохие решения • Сделать два компонента • Передавать callback в
props !16
export default { // ... props: ['callback'], loadUser () {
this.username = this.callback(...) }, } !17
Вопросики • А если у нас нет возможности передать callback?
• А если очень глубокая вложенность? !18
Плохие решения • Сделать два компонента • Передавать callback в
props !19
Плохие решения • Сделать два компонента • Передавать callback в
props • Передавать флаг для поведения !20
export default { // ... props: ['http'], loadUser () {
if (this.http) { this.username = this.$axios.get(...) } else { this.username = localStorage(...) } }, } !21
Вопросики • А если поведений много? • А если у
нас нет возможности выставить флаг? • А если очень глубокая вложенность? • А как его тестировать? !22
Тестирование !23
import { mount, createLocalVue } from '@vue/test-utils' import MockAdapter from
'axios-mock-adapter' import MyComponent from '~/components/MyComponent' const localVue = createLocalVue() describe('unit tests for MyComponent', () => { test('should have correct logic', () => { const wrapper = mount(MyComponent, { localVue }) new MockAdapter(wrapper.axios) .onGet('...') .reply(200, ['sobolevn']) wrapper.loadUser() expect(wrapper.username).toBe('sobolevn') }) }) !24
Плохие решения • Терпеть !25
Хорошие решения: композиция и абстракция !26
provide / inject !27
import { mount, createLocalVue } from '@vue/test-utils' import MyComponent from
'~/components/MyComponent' const localVue = createLocalVue() describe('unit tests for MyComponent', () => { test('should have correct logic', () => { const wrapper = mount(MyComponent, { localVue, provide: { buttonCallback: () => 'sobolevn' }, }) wrapper.loadUser() expect(wrapper.username).toBe('sobolevn') }) }) !28
Рефакторинг компонента !29
export default { data () { return { username: ''
} }, template: ` <button click="loadUser">Hello {{ username }}</button> `, inject: ['buttonCallback'], loadUser () { this.username = this.buttonCallback() }, } !30
Создание контекста * https://mmhaskell.com/monads-4 !31
export default { provide: { buttonCallback: () => this.$axios.get('...'), },
components: { MyComponent }, template: `<MyComponent />`, } !32
Плохие решения • Пихать логику в компоненты • Делать вид,
что ты не пихаешь логику в компоненты !33
Рефакторинг компонента !34
import buttonCallbackFactory from 'logic/http' export default { provide: { buttonCallback:
buttonCallbackFactory(...), }, components: { MyComponent }, template: `<MyComponent />`, } !35
И не только для компонентов !36
C Vue понятно, а Vuex? !37
Плохие решения • Пихать логику во Vuex !38
отображение = f(состояния) !39
Vuex !40
import { Inject, Injectable } from 'vue-typedi' import { Action
} from 'vuex-simple' @Injectable() export default class UsernameModule { @Inject(tokens.USERNAME_SERVICE) public service // type: UsernameServiceProtocol @Action() public async fetchUsername () { const username = await this.service.fetchUsername() // ... } } !41
// type: UsernameServiceProtocol
Вставляй любую абстракцию! !43
provide / inject vs TypedDI !44
И не только DI • Higher Order Components • Slots,
Named Slots • https://github.com/vuejs/vue-function-api • Event Bus • Vue.observable, https://github.com/vuejs/vue-rx !45
Сегодня мы многое поняли !46
github.com/wemake-services/ wemake-vue-template !47
Полезные инструменты • https://github.com/sascha245/vue-typedi • https://github.com/typestack/typedi • https://github.com/gcanti/io-ts • https://github.com/gcanti/fp-ts
!48
Полезные ссылки • https://guide.elm-lang.org/architecture/ • https://markus.oberlehner.net/blog • https://blog.ploeh.dk/2016/03/18/functional- architecture-is-ports-and-adapters/ •
https://fsharpforfunandprofit.com/rop/ • https://www.destroyallsoftware.com/screencasts/ catalog/functional-core-imperative-shell • https://sobolevn.me/ !49
tlg.name/ opensource_findings !50
Вопросы? github.com/sobolevn sobolevn.me 51