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
手動確認はもう限界 〜XCUITestでCustom URL Schemeの遷移を起動種別ごとに自動テストする〜 / Testing Custom URL Schemes with XCUITest
otouto
0
360
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
400
AGENTS.md Is Not Enough:Build Skills, Don't Download Them
lx_t
0
130
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
スマートフォンでモールス信号を送受信する 〜スマートフォンのLEDとカメラで作る光通信の設計と実装〜
atsuki_seo
0
210
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
440
そのリトライ、死んだコネクションを使い回していませんか ── GoのHTTPクライアントとHTTP/2を実プロダクト障害から学び直す
myus4a
0
340
Heart of Swift Concurrency
koher
0
1.1k
Androidだけじゃない、DroidKaigiで広がった私の技術スタック
akkeylab
0
100
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
30
13k
大喜利で理解するLLM as a Judge / Understanding LLM-as-a-Judge through Ogiri
rockname
0
180
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
360
Featured
See All Featured
RailsConf 2023
tenderlove
30
1.6k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
290
Accessibility Awareness
sabderemane
1
220
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
470
Navigating Weather and Climate Data
rabernat
0
540
Building the Perfect Custom Keyboard
takai
2
890
Making Projects Easy
brettharned
120
6.8k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Designing for Timeless Needs
cassininazir
1
510
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
3
3.8k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
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]