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
Егор Банщиков
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
FrontFest
November 27, 2017
Programming
900
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Егор Банщиков
FrontFest
November 27, 2017
More Decks by FrontFest
See All by FrontFest
Тим Чаптыков
frontfest
0
1.2k
Jose M. Perez
frontfest
0
760
Алексей Иванов
frontfest
0
1.4k
Екатерина Пригара
frontfest
0
710
Léonie Watson
frontfest
0
780
Кирилл Чернышев
frontfest
0
770
Виктор Грищенко
frontfest
0
660
Игорь Алексеенко
frontfest
0
510
Владимир Гриненко
frontfest
0
740
Other Decks in Programming
See All in Programming
楽しそうなつよつよエンジニアと目が死んでる僕/A brilliant engineer having a blast, and dead-eyed me.
3l4l5
2
190
Foundation Models frameworkで画像分析
ryodeveloper
1
620
型も通る、synthも通る、それでも危ない 〜AIのCDKの権限とコストを機械で検証する〜 / It Passes Type Checks, It Passes Synth Checks, but It’s Still Risky — Automatically Verifying Permissions and Costs in AI’s CDK —
seike460
PRO
1
570
Android CLI
fornewid
0
220
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.6k
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
700
バグを直したら useEffect が消えた
colorful12
3
620
仕様駆動開発の消費期限
watany
20
8.7k
jsmini JavaScript Engine を作ってみた話
yosuke_furukawa
PRO
0
330
in-process GraphQL のすすめ #ginzajs
izumin5210
4
1.4k
テーブルをDELETEした
yuzneri
0
150
AIを紡ぐPMのお話
swdtkuy
0
110
Featured
See All Featured
Agile that works and the tools we love
rasmusluckow
331
22k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.8k
It's Worth the Effort
3n
188
29k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
580
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
230
A designer walks into a library…
pauljervisheath
211
24k
How to Talk to Developers About Accessibility
jct
2
520
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
The Spectacular Lies of Maps
axbom
PRO
1
920
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
280
Site-Speed That Sticks
csswizardry
13
1.4k
How to Think Like a Performance Engineer
csswizardry
28
2.7k
Transcript
React, TypeScript и Redux – как сделать SPA для мобильных
браузеров
None
None
Четыре проблемы 1. Мобильный интернет 2. Мобильные устройства 3. Мобильные
браузеры 4. Баги (просто баги)
Сокращаем лаги. Анимация Мобильные браузеры / Мобильные устройства
0:00 / 0:47
None
60fps 16.7ms per frame
Жизненный цикл фрейма
Менеджмент слоев
Поддержка will-change
Старый добрый 3D-хак div { transform: translateZ(0) }
0:00 / 0:07
Animator this.state = { currentY: undefined, finishY: undefined } <div
style={{ transform: `translate3d(0, ${this.state.currentY}px, 0)` }} /> Делаем анимацию на React export class extends React.Component() { constructor(props) { super(props); } render() { return ( ) } } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12.
<div style={{ transform: `translate3d(0, ${this.state.currentY}px, 0)` }} /> onTouchStart={this._onStart} onTouchMove={this._onMove}
onTouchEnd={this._onEnd} </div> _onStart this.setState({ currentY, finishY }); _onMove this.setState({ currentY }); render() { return ( ) } = () => { let finishY; let currentY; // рассчитываем стартовую и конечную позицию по оси OY } = () => { let currentY; // рассчитываем текущую позицию блока по оси OY } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22.
None
<div style={{ transform: `translate3d(0, ${this.state.currentY}px, 0)` }} /> onTouchStart={this._onStart} onTouchMove={this._onMove}
onTouchEnd={this._onEnd} </div> _onStart this.setState({ currentY, finishY }); _onMove this.setState({ currentY }); <div ref={(el) => this.container = el} /> onTouchStart={this._onStart} onTouchMove={this._onMove} onTouchEnd={this._onEnd} </div> _onStart _onMove this.container.style.transform = `translate3d(0, ${this.currentY}px, 0)` Было render() { return ( ) } = () => { let finishY; let currentY; // рассчитываем стартовую и конечную позицию по оси OY } = () => { let currentY; // рассчитываем текущую позицию блока по оси OY } Стало componentDidMount() { this.finishY = this._getFinishY(); this.currentY = this._getCurrentY(); } render() { return ( ) } = () => { // Добавьте код по вкусу } = () => { this.currentY = this._getCurrentY(); // рассчитываем текущую позицию блока по оси OY } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24.
None
_onEnd _moveAnimator 1 — setTimeout(() => this._moveAnimator(callback), 1000 / 60)
2 — requestAnimationFrame(() => this._moveAnimator(callback) = () => this._moveAnimator(this._finishAnimation) = (callback) => { while (this.finishY - this.currentY !== 0) { // доводим события до нужной точки по ОСИ OY } callback() } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11.
// СТУЛ-1 ЧЕРВИ ПИКИ // СТУЛ-2 ЧЕРВИ ПИКИ const st
= document.querySelector('#chairs .st'); const raf = document.querySelector('#chairs .raf'); st.addEventListener('click', () => { setTimeout(() => st.innerHTML = , 0); st.innerHTML = ; }); raf.addEventListener('click', () => { requestAnimationFrame(() => raf.innerHTML = ); raf.innerHTML = ; }); СТУЛ 1 СТУЛ 2
None
setTimeout requestAnimationFrame
Коротко о scroll • На мобилках скроллят чаще • На
мобилках событие скролл срабатывает чаще
None
Итоги первой части • Жизненный цикл фрейма • Менеджмент слоев
• will-change • 60fps анимация • requestAnimationFrame vs setTimeout • О скролле
Сокращаем бандл. Выбор фреймворка Мобильный интернет
None
jsxExample() hyperScriptExample() h(nodeName, attributes, children) Что такое jsx? export function
{ return ( <div id='Hello'> Hello! <br /> </div> ); }; export function { return h('div#hello', undefined, [ 'Hello', h('br') ]); }; function { return { nodeName, attributes, ...children} }; 01. 02. 03. 04. 05. 06. 07. 08. 01. 02. 03. 04. 05. 06. 01. 02. 03.
hyperScriptExample() h(nodeName, attributes, children) vNode export function { return h('div#hello',
undefined, [ 'Hello', h('br') ]); }; function { return { nodeName, attributes, ...children} }; export const = { nodeName: 'div', attributes: { id: 'hello' } children: [ 'Hello', { nodeName: 'br' } ] }; 01. 02. 03. 04. 05. 06. 01. 02. 03. 01. 02. 03. 04. 05. 06. 07. 08.
render(vNode) Vnode -> Node export function { if (typeof vNode
=== 'string') { return document.createTextNode(vNode); } let node = document.createElement(vNode.nodeName); for (let name in Object.apply(vNode.attributes)) { let value = vNode.attributes[name] if (name in node) { node[name] = value; } else { node.setAttribute(name, value) } } for (let i =0; i < vNode.children.length; i++) { node.appendChild(render(vNode.children[i])); } return node }; 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18.
None
None
None
None
None
None
None
None
Производительность React и Preact
None
None
Итоги второй части • Что такое Preact • Как работает
VirtualDom • Какие отличия у React и Preact
Делим бандл. Code splitting Мобильный интернет
None
Текущий app.js 1. Сторонние библиотеки — react, redux, lodash и.т.д.
2. Полифилы, подключаемые в зависимости от условий 3. Код приложения
Первый этап улучшений 1. Выносим сторонние библиотеки в отдельный файл
— vendor.js 2. Добавляем хэш — 4d882c546d0ea4407c14.vendor.js 3. Выносим часто используемый код new webpack.optimize.CommonsChunkPlugin({ name: 'app', minChunks: 2, children: true }); 4. Профит 01. 02. 03. 04. 05.
this.state = { Component: null } Второй этап улучшений export
default class extends React.PureComponent { constructor(props) { super(props) } 01. 02. 03. 04. 05.
this.state = { Component: null } if (!this.state.Component) { this.props.moduleProvider().then(
({Component}) => this.setState({ Component })); } Второй этап улучшений export default class extends React.PureComponent { constructor(props) { super(props) } componentDidMount() { } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11.
this.state = { Component: null } if (!this.state.Component) { this.props.moduleProvider().then(
({Component}) => this.setState({ Component })); } return <div>{ Component ? <Component /> : <Preloader /> }>/div> Второй этап улучшений export default class extends React.PureComponent { constructor(props) { super(props) } componentDidMount() { } render() { const { Component } = this.state; ); } }; 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18.
Второй этап улучшений import someModule from './dir/someModule.js'; const path =
'./dir/someModule.js'; import(path).then(someModule => someModule.foo()); const card = () => import('modules/card/container'); <LazyLoaderComponent moduleProvider={card}/> 01. 02. 03. 04. 05. 06. 07.
Результат улучшений
Итоги третьей части • Составляющие бандла • Сплит бандла и
ленивая загрузка
Сокращаем баги. Typescript, react, redux Минимизируем баги и время разработки
Причины багов • Архитектура • Эмоциональное выгорание • Отсутствие нужных
знаний • Лень
None
None
Animal run() Human extends Animal cry() complain() askExistentialQuestions() isKindAndDecent() Пример
Typescript class { public name: string; public age: number; public sizes: { weight: number; height: number }; private { /* do something */ } } class { /* плакаться */ private { return 'Я так больше не могу!'} /* жаловаться */ private { return 'Я устал, хочу домой!'} /* задать экзистенциальный вопрос */ private { return 'Где второй носок?!'} /* добрый и порядочный? */ private { return Math.random() < 0.05 ? 'да' : 'нет' } } 01. 02. 03. 04. 05. 06. 07. 01. 02. 03. 04. 05. 06. 07. 08. 09. 10.
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var Animal = /** @class */ (function () { function Animal() { } Animal.prototype.run = function () { }; return Animal; }()); var Human = /** @class */ (function (_super) { __extends(Human, _super); function Human() { return _super !== null && _super.apply(this, arguments) || this; } Human.prototype.cry = function () { return 'Я так больше не могу!'; }; Human.prototype.complain = function () { return 'Я устал, хочу домой!'; }; Human.prototype.askExistentialQuestions = function () { return 'Где второй носок?!'; }; Human.prototype.isKindAndDecent = function () { return Math.random() < 0.05 ? 'да' : 'нет'; }; return Human; }(Animal)); 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27.
Как .ts(x) в .js(x)? module: { rules: [ { test:
/\.ts(x)?$/, use: [ { loader: 'ts-loader' } ] } ] }, 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12.
TypeErrors
PhotoViewerProps prevPhoto?: PhotoItem; nextPhoto?: PhotoItem; changePhoto: (photoId: string) => void;
close: () => void; PhotoViewerState Typescript и React interface { total: number; photoSource?: string; copyrightCode: string; url: string; icon?: string; currentIndex: number; prevPhotoSeoLink?: string; nextPhotoSeoLink?: string; backLink: string; } interface { copyrightStore: { [name: string]: string }; } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18.
class PhotoViewer extends React.Component<PhotoViewerProps & WithI18nProps, PhotoViewerState> { private prev:
HTMLDivElement; // контейнер для предыдущей фотографии private next: HTMLDivElement; // контейнер для следующей фотографии private carousel: HTMLDivElement; // контейнер для всех фотографий (от 1 до 3) private width: number; // ширина экрана private delta: number; // разница между начальным и конечным значением драга по оси ОХ private startX: number; // начальная точка драга private startY: number; // начальная точка драга по вертикали ... private ptz: PinchToZoom; constructor(props: PhotoViewerProps & WithI18nProps) { super(props); this.state = { copyrightStore: {}, }; } } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17.
None
None
UserLoginAction LoginFields userLogin UserLoginAction Typescript и Redux export const USER_LOGIN
= 'USER_LOGIN'; export interface { type: 'USER_LOGIN'; payload: LoginFields; async: true; } export interface { email: string; password: string; userId?: string; } export const = (payload: LoginFields): => ({ type: USER_LOGIN, payload, async: true }); 01. 02. 03. 04. 05. 01. 02. 03. 04. 05. 01. 02. 03. 04. 05.
User UserAction User USER_FETCH_SEARCH_HISTORY_SUCCESS: USER_START_UP_SUCCESS: USER_LOGIN_SUCCESS: USER_GET_TOKENS_VIA_LOGIN_TOKEN_SUCCESS: USER_RECOVERY_PASSWORD_SUCCESS: USER_LOGOUT: export
default function (state: = defaultState, action: ): { switch (action.type) { case // do something return newState; case // do something return newState; case // do something return newState; case // do something return newState; case // do something return newState; case // do something return newState; ...more 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
UserAction export type = UserSetParams | UserFetchSearchHistorySuccessAction | UserSetUA |
StoreLinkClick | UserUpdateOnlineStatus | UserAllowLocation | UserFollowLocation | UserSetLocation | UserLocationSuccess | UserLocationError | ... | UserLogout 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13.
None
None
None
None
Итоги последней части • Знакомство с Typescript • React и
Typescript • Redux и Typescript
Итого • Как уменьшить лаги • Как сократить бандл •
Как делить бандл • Как уменьшить число багов
THE END https://vk.com/yegorb
[email protected]