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
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
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
780
Кирилл Чернышев
frontfest
0
770
Виктор Грищенко
frontfest
0
660
Игорь Алексеенко
frontfest
0
510
Владимир Гриненко
frontfest
0
740
Other Decks in Programming
See All in Programming
まだ間に合う!今年の夏こそSchemeのマクロ展開器を完全理解!
omasanori
0
640
AI駆動開発にグラフDBを重ねてみた
satoshi256kbyte
2
760
書籍「プロフェッショナルAI駆動開発」紹介スライド
juntaromatsumoto
0
1.1k
【デモ】Kiroで体験する仕様駆動開発|設計からコーディングまでAIと進める開発フロー
cmkudo
0
600
iOS開発×AI駆動開発 〜最近使って便利だったスキルの話〜
nogu66
0
140
Pythonの実行はどこまで賢くなったのか? CPythonとPyPyから見る最適化のしくみ
curekoshimizu
4
2.8k
ハーネス設計入門 〜プロンプト、コンテキストの次〜
kinopeee
55
36k
『寄り添うラジオ』をAIで作る 体験価値から逆算した、会話しないUXと品質設計
theoriatec2024
3
130
「AI時代、配布するPythonコードをどう守るか: 難読化の実験と判断軸」 #PyconJP2026
pkshadeck
PRO
2
170
typoなんかねぇよ
raspython3
0
670
Go を使い始めて 2 ヶ月の学び / My first two months with Go
contour_gara
0
420
Gmail/Google DriveをトリガーにAIエージェントを動かそう! / Run AI agents with Gmail/Google Drive as triggers!
har1101
3
470
Featured
See All Featured
AI: The stuff that nobody shows you
jnunemaker
PRO
9
980
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
440
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
190
Being A Developer After 40
akosma
91
590k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
840
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
230
Into the Great Unknown - MozCon
thekraken
41
2.7k
Amusing Abliteration
ianozsvald
1
290
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.8k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
How to train your dragon (web standard)
notwaldorf
97
6.8k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.6k
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]