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
Егор Банщиков
Search
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
720
Léonie Watson
frontfest
0
790
Кирилл Чернышев
frontfest
0
770
Виктор Грищенко
frontfest
0
660
Игорь Алексеенко
frontfest
0
510
Владимир Гриненко
frontfest
0
740
Other Decks in Programming
See All in Programming
APNsからLive Activityを開始する話
yumnumm
0
150
The Rails Doctrine Decade
koic
2
470
カツオ、ご期待ください
suneo3476
0
140
マイコン向けの軽量Ruby「PicoRuby」で各種デバイスを制御するネイティブアプリの実現手法
bash0c7
0
470
Simple Storage Service(S3) is not simple
iwatsukayura
0
120
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
7
2.4k
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
Security issues being discussed on Web Platforms
petamoriken
0
1.4k
Vue Fes Japan 2026 タイムテーブル徹底解説
448jp
1
600
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
350
wkhtmltopdfの次どうするか問題2026
willnet
2
1.8k
Androidだけじゃない、DroidKaigiで広がった私の技術スタック
akkeylab
0
110
Featured
See All Featured
Designing for humans not robots
tammielis
254
26k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
2
2.2k
Building Adaptive Systems
keathley
44
3.2k
What's in a price? How to price your products and services
michaelherold
247
13k
Claude Code のすすめ
schroneko
67
230k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
760
Designing for Performance
lara
611
70k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Are puppies a ranking factor?
jonoalderson
2
4k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
730
Faster Mobile Websites
deanohume
310
32k
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]