Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Business logic in Vue.js
Search
Sobolev Nikita
August 22, 2019
Programming
330
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
240
ИИ-Агенты в каждый дом – Алексей Порядин, PythoNN
sobolevn
0
220
Внутреннее устройство сборки мусора в CPython 3.14+ – Сергей Мирянов, PythoNN
sobolevn
0
140
Генератор байткода и байткод генератора, Михаил Ефимов, PythoNN
sobolevn
0
130
Дотянуться до кремния. HighLoad Python: SIMD, GPU – Пётр Андреев, PythoNN
sobolevn
0
120
Проектирование — это когда чувствуешь, а не какие-то там циферки, Николай Хитров, PythoNN
sobolevn
0
140
Continuous profiling, Давид Джалаев, PythoNN
sobolevn
0
170
Михаил Гурбанов – Are you NATS? @ PythoNN
sobolevn
0
330
Дмитрий Бровкин – Почему исправление опечаток сложнее, чем кажется, и как мы с этим српавляемся @ PythoNN
sobolevn
0
69
Other Decks in Programming
See All in Programming
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.5k
5分で問診!Composer セキュリティ健康診断
codmoninc
0
1k
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
690
実装をデザインガイドラインに追従させるための取り組み / 260731-dip-mosh-design-system
dachi023
0
470
torikago - Ruby::Boxで照らすモジュラモノリスの実行境界
se4weed
1
380
20260722_microCMSで考える、AI時代のコンテンツ運用設計
yosh1
0
410
夏だ!祭りだ!祭りとはドメインモデリングでは?
ryugen04
0
290
Go言語とトイモデルで学ぶTransformerの気持ち / fukuokago23-transformer
monochromegane
0
180
人間の目はかわらない、だからJPEGは30年もつ
yuzneri
12
18k
為什麼你並不需要ViewModel / No, you don't need a ViewModel
lovee
1
500
Building a Meta Ray-Ban display app
akkeylab
0
160
freeeにおけるEvalsの実践例の紹介
freee
PRO
0
110
Featured
See All Featured
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4.2k
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
2
3.8k
Thoughts on Productivity
jonyablonski
76
5.3k
A Soul's Torment
seathinner
6
3.4k
Building Flexible Design Systems
yeseniaperezcruz
330
40k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
270
New Earth Scene 8
popppiees
3
2.5k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
470
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
770
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
490
Tell your own story through comics
letsgokoyo
1
1k
Navigating Team Friction
lara
192
16k
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