ݺߣ

ݺߣShare a Scribd company logo
Rafał Filipek
Frontend & Platform Architect / Team Leader
Platforma Kontentowa
Jak chcemy wprowadzić hybrydowe aplikacje w 150 serwisach.
React, NodeJS i przyjaciele
Historia
Apetyt rośnie
• Rewitalizacja 150 serwisów
• Szybkość
• Więcej klików, dłuższy czas wizyty
• Krótszy development
• $$$
PLATFORMA
KONTENTOWA
Platforma Kontentowa
Platforma Kontentowa
stara wersja
nowa wersja
Platforma Kontentowa
Kilka faktów
• 10 tygodni
• dwie lokalizacje
• 3 osoby od “frontendu” (2 x junior dev + ja)
• API tworzone od zera
• system do deploymentu in progress
Jak?
WP kobieta
NodeJS 6.xAPI
Backend
* w uproszczeniu
*
Elementy
f(s) = UI
f(s) = g(s1) + h(s2) … z(sx) = UI
f(s)
renderToString(
<Provider store={store}>
<RouterContext {...props} />
</Provider>
)
ReactDOM.render(
<Provider store={store}>
<Router routes={routes} history={browserHistory}
render={applyRouterMiddleware(useScroll())} />
</Provider>,
document.getElementById(id)
) client
server
classnames by Jed Watson
npm.im/classnames
❤
CSS
CSS
import styes from './Teaser.css'
import cx from 'classnames'
const Teaser = ({ title, img, important, visited }) => (
<div className={cx(
{
[styles.visited]: visited,
[styles.hot]: important
},
styles.wrap
)}>
{ /* ... */}
</div>
)
<div
class="_3W-1w _37ZLm _2Anks"
style="background-color:#000;"
data-reactid=“186"
>
stuff
</div>
import { createStore } from 'redux'
const counter = (state = 0, action) => {
if (action.type === 'INCREMENT') return state + 1
if (action.type === 'DECREMENT') return state - 1
return state
}
const store = createStore(counter)
store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'DECREMENT' })
state: {
teasers: { lists }
content: { items }
comments: {
comments, userComments, lists, replies,
votes, top, reports, scroll, counters,
info, settings
}
reactions: { votes, items, fb, instagram, twitter shoplov }
adv: { ima, rc }
ui: { menuVisible, headerFixed }
}
Platforma Kontentowa
// okeyish
export default connect((state) => {
return { a: state.foo.a, b: state.bar.b }
})(View)
// good
const selector = createSelector(
(state) => state.foo.a,
(state) => state.bar.b,
(a, b) => {
return { a, b }
}
)
export default connect(selector)(View)
npm.im/reselect
reselect by ReactJS
Dane i API
import { actions } from 'pkf-teasers'
import { trigger } from 'redial'
import { provideHooks } from 'redial'
/* ... */
export const hooks = {
fetch: ({ dispatch }) => (
Promise.all([
dispatch(actions.fetch('dla-ciebie')),
dispatch(actions.fetchSections())
])
),
defer: ({ dispatch }) => {
Promise.all([
dispatch(actions.fetch('dla-ciebie')),
dispatch(actions.fetchSections())
dispatch(actions.fetch('najnowsze')),
dispatch(actions.fetch('popularne', { count: 20 })),
])
}
}
/* ... */
const App = provideHooks(hooks)(View)
trigger('fetch', App).then(() => renderToString(<App />))
npm.im/redial
redial by Mark Dalgleish
Pakiety
• pkf-kobieta
• pkf-app
• pkf-comments
• pkf-commons
• pkf-content
• pkf-social-reactions
• pkf-teasers
Pakiety
"pkf-app": “… pkf-app …”,
"pkf-comments": "… pkf-comments …”,
"pkf-commons": "… pkf-commons …”,
"pkf-content": "… pkf-content …”,
"pkf-social-reactions": "… pkf-social-reactions …”,
"pkf-teasers": "… pkf-teaser …”,
pkf-kobieta
• config.js
• client.js
• server.js
• pliki statyczne
import './styles.css'
import './config.js'
import { runClient } from 'pkf-app/lib/run-client.js'
import './config-analytics.js'
if (process.env.NODE_ENV !== 'production') {
const Perf = window.Perf = require('react-addons-perf')
Perf.start()
}
runClient()
import './styles.css'
import { runServer } from 'pkf-app/lib/run-server.js'
import { setConfig } from 'pkf-app/lib/config.js'
import { configs } from './config.js'
setConfig(configs)
runServer(process.env['APP_SERVER_PORT'] || 3000)
Biblioteki
Biblioteki
Hammer.js i WPJSLib
npm.im/hammerjs
hammerjs by Alexander Schmitz
Biblioteki
export class Video extends React.Component {
// ...
componentDidMount() {
this.player = WP.player({ url: this.props.url, target: this.el })
window.wp_player_osadzony = 1 // Przepraszamy internet za nazwę
}
shouldComponentUpdate() { return false }
componentWillUnmount() {
if (this.player) {
this.player.destroy()
this.player = null
window.wp_player_osadzony = 0 // Jeszcze raz sorry
}
}
render() {
return <div ref={(ref) => { this.el = ref }} id={this.getId()}></div>
}
}
SEO
Dostępność
• Statycznie renderowany html
• Dodatkowe części serwisu renderowane po stronie
serwera tylko dla botów
• Stronę można przeglądać bez JS
✔
✔
✔
SEO
og:image
twitter:image
article:published_time
al:ios:url
og:url
og:type
og:title
og:description
twitter:url
canonical
twitter:card
SEO
<Helmet
title="My Title"
meta={[
{"name": "description", "content": "Helmet application"},
{"property": "og:type", "content": "article"}
]}
link={[
{"rel": "canonical", "href": "http://mysite.com/example"}
]}
script={[
{"src": "http://include.com/pathtojs.js", "type": "text/javascript"},
{"type": "application/ld+json", innerHTML: `{ "@context": "http://schema.org" }`}
]}
onChangeClientState={(newState) => console.log(newState)}
/>
npm.im/react-helmet
react-helmet
Responsywność
• ~80% komponentów jest współdzielona przez desktop i mobile
• 100% logiki w innych warstwach jest współdzielone
Statystyki
reklamy
analyticsEventDispatcher({
action: 'page:view',
dot: /* ... */,
gfk: /* ... */,
gemius: /* ... */
})
advertisementEventDispatcher({
action: 'bunch:load',
dot: {
bunch: 'index'
}
})
Na co uważać
• Trzeba poświęcić czas na budowę środowiska
developerskiego
• HTML + CSS != React + css
• warstwa danych (kontrola)
• “Śmietnik” w widoku
Co się zmieniło
• Jakość
• Czas developmentu
• Obszary “zainteresowania” programistów frontend /
backend
• Zbliżenie do aplikacji mobilnych
• Morale :)
Dalszy rozwój
• nowe funkcjonalności
• zmiana podejścia do CSS
• optymalizacje
• service workers
ٳę쾱
Ad

Recommended

PPTX
Aplikacje internetowe real-time w oparciu o React/Redux
Dawid Rusnak
PDF
Laravelowe paczki do uwierzytelniania
Laravel Poland MeetUp
PDF
Budowa elementów GUI za pomocą biblioteki React - szybki start
Sages
PDF
Cykl życia zapytania HTTP (pod maską)
Laravel Poland MeetUp
PDF
Kickoff to Node.js
The Software House
PDF
Hugo - make webdev fun again
Marcin Gajda
PDF
Obalamy mity o wydajności frameworka Laravel cz. II
Laravel Poland MeetUp
PDF
Metaprogramowanie w JS
Dawid Rusnak
PDF
Podstawy AngularJS
Sages
PDF
Webpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótka
Marcin Gajda
PDF
Automatyzacja utrzymania jakości w środowisku PHP
Laravel Poland MeetUp
PPTX
[BDD] Introduction to Behat (PL)
Piotr Pelczar
PDF
NSOperation(Queue)
Krzysztof Profic
PPTX
Jak zostać mobile deweloperem w 1 dzień
Paweł Kondraciuk
PDF
PHP-PM. Hit czy kit?
The Software House
PPTX
AADays 2015 - Jak to zrobic w JavaScript
Jacek Okrojek
PDF
Infrastructure As Code
Kamil Grabowski
PDF
“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...
The Software House
PDF
Technologia Xamarin i wprowadzenie do Windows IoT core
Sages
PDF
Swoole w PHP. Czy to ma sens?
The Software House
PDF
Deployment kodu z Capistrano
Michał Szajbe
PDF
AngularJS - podstawy
Apptension
PDF
Testowanie rozwiązań serverless z LocalStack
The Software House
PDF
Jak działa CPython
Wojciech Lichota
PDF
Xdebug – debugowanie i profilowanie aplikacji PHP
3camp
PDF
Angular 4 pragmatycznie
Sages
PDF
Ansible w praktyce
Kamil Grabowski
PDF
Jakub Mrowiec (Grand Parade Poland) - Monumentum Case Study
Business Link Krakow

More Related Content

What's hot (20)

PDF
Podstawy AngularJS
Sages
PDF
Webpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótka
Marcin Gajda
PDF
Automatyzacja utrzymania jakości w środowisku PHP
Laravel Poland MeetUp
PPTX
[BDD] Introduction to Behat (PL)
Piotr Pelczar
PDF
NSOperation(Queue)
Krzysztof Profic
PPTX
Jak zostać mobile deweloperem w 1 dzień
Paweł Kondraciuk
PDF
PHP-PM. Hit czy kit?
The Software House
PPTX
AADays 2015 - Jak to zrobic w JavaScript
Jacek Okrojek
PDF
Infrastructure As Code
Kamil Grabowski
PDF
“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...
The Software House
PDF
Technologia Xamarin i wprowadzenie do Windows IoT core
Sages
PDF
Swoole w PHP. Czy to ma sens?
The Software House
PDF
Deployment kodu z Capistrano
Michał Szajbe
PDF
AngularJS - podstawy
Apptension
PDF
Testowanie rozwiązań serverless z LocalStack
The Software House
PDF
Jak działa CPython
Wojciech Lichota
PDF
Xdebug – debugowanie i profilowanie aplikacji PHP
3camp
PDF
Angular 4 pragmatycznie
Sages
PDF
Ansible w praktyce
Kamil Grabowski
PDF
Jakub Mrowiec (Grand Parade Poland) - Monumentum Case Study
Business Link Krakow
Podstawy AngularJS
Sages
Webpack - Czym jest webpack i dlaczego chcesz go używać? - wersja krótka
Marcin Gajda
Automatyzacja utrzymania jakości w środowisku PHP
Laravel Poland MeetUp
[BDD] Introduction to Behat (PL)
Piotr Pelczar
NSOperation(Queue)
Krzysztof Profic
Jak zostać mobile deweloperem w 1 dzień
Paweł Kondraciuk
PHP-PM. Hit czy kit?
The Software House
AADays 2015 - Jak to zrobic w JavaScript
Jacek Okrojek
Infrastructure As Code
Kamil Grabowski
“Dziesięć serwerów poproszę!“, czyli co może Ci zaoferować definiowanie infra...
The Software House
Technologia Xamarin i wprowadzenie do Windows IoT core
Sages
Swoole w PHP. Czy to ma sens?
The Software House
Deployment kodu z Capistrano
Michał Szajbe
AngularJS - podstawy
Apptension
Testowanie rozwiązań serverless z LocalStack
The Software House
Jak działa CPython
Wojciech Lichota
Xdebug – debugowanie i profilowanie aplikacji PHP
3camp
Angular 4 pragmatycznie
Sages
Ansible w praktyce
Kamil Grabowski
Jakub Mrowiec (Grand Parade Poland) - Monumentum Case Study
Business Link Krakow

Platforma Kontentowa

  • 1. Rafał Filipek Frontend & Platform Architect / Team Leader
  • 2. Platforma Kontentowa Jak chcemy wprowadzić hybrydowe aplikacje w 150 serwisach. React, NodeJS i przyjaciele
  • 4. Apetyt rośnie • Rewitalizacja 150 serwisów • Szybkość • Więcej klików, dłuższy czas wizyty • Krótszy development • $$$
  • 10. Kilka faktów • 10 tygodni • dwie lokalizacje • 3 osoby od “frontendu” (2 x junior dev + ja) • API tworzone od zera • system do deploymentu in progress
  • 13. f(s) = UI f(s) = g(s1) + h(s2) … z(sx) = UI
  • 14. f(s)
  • 15. renderToString( <Provider store={store}> <RouterContext {...props} /> </Provider> ) ReactDOM.render( <Provider store={store}> <Router routes={routes} history={browserHistory} render={applyRouterMiddleware(useScroll())} /> </Provider>, document.getElementById(id) ) client server
  • 16. classnames by Jed Watson npm.im/classnames ❤ CSS
  • 17. CSS import styes from './Teaser.css' import cx from 'classnames' const Teaser = ({ title, img, important, visited }) => ( <div className={cx( { [styles.visited]: visited, [styles.hot]: important }, styles.wrap )}> { /* ... */} </div> ) <div class="_3W-1w _37ZLm _2Anks" style="background-color:#000;" data-reactid=“186" > stuff </div>
  • 18. import { createStore } from 'redux' const counter = (state = 0, action) => { if (action.type === 'INCREMENT') return state + 1 if (action.type === 'DECREMENT') return state - 1 return state } const store = createStore(counter) store.dispatch({ type: 'INCREMENT' }) store.dispatch({ type: 'DECREMENT' })
  • 19. state: { teasers: { lists } content: { items } comments: { comments, userComments, lists, replies, votes, top, reports, scroll, counters, info, settings } reactions: { votes, items, fb, instagram, twitter shoplov } adv: { ima, rc } ui: { menuVisible, headerFixed } }
  • 21. // okeyish export default connect((state) => { return { a: state.foo.a, b: state.bar.b } })(View) // good const selector = createSelector( (state) => state.foo.a, (state) => state.bar.b, (a, b) => { return { a, b } } ) export default connect(selector)(View) npm.im/reselect reselect by ReactJS
  • 22. Dane i API import { actions } from 'pkf-teasers' import { trigger } from 'redial' import { provideHooks } from 'redial' /* ... */ export const hooks = { fetch: ({ dispatch }) => ( Promise.all([ dispatch(actions.fetch('dla-ciebie')), dispatch(actions.fetchSections()) ]) ), defer: ({ dispatch }) => { Promise.all([ dispatch(actions.fetch('dla-ciebie')), dispatch(actions.fetchSections()) dispatch(actions.fetch('najnowsze')), dispatch(actions.fetch('popularne', { count: 20 })), ]) } } /* ... */ const App = provideHooks(hooks)(View) trigger('fetch', App).then(() => renderToString(<App />)) npm.im/redial redial by Mark Dalgleish
  • 24. • pkf-kobieta • pkf-app • pkf-comments • pkf-commons • pkf-content • pkf-social-reactions • pkf-teasers Pakiety "pkf-app": “… pkf-app …”, "pkf-comments": "… pkf-comments …”, "pkf-commons": "… pkf-commons …”, "pkf-content": "… pkf-content …”, "pkf-social-reactions": "… pkf-social-reactions …”, "pkf-teasers": "… pkf-teaser …”,
  • 25. pkf-kobieta • config.js • client.js • server.js • pliki statyczne import './styles.css' import './config.js' import { runClient } from 'pkf-app/lib/run-client.js' import './config-analytics.js' if (process.env.NODE_ENV !== 'production') { const Perf = window.Perf = require('react-addons-perf') Perf.start() } runClient() import './styles.css' import { runServer } from 'pkf-app/lib/run-server.js' import { setConfig } from 'pkf-app/lib/config.js' import { configs } from './config.js' setConfig(configs) runServer(process.env['APP_SERVER_PORT'] || 3000)
  • 28. Biblioteki export class Video extends React.Component { // ... componentDidMount() { this.player = WP.player({ url: this.props.url, target: this.el }) window.wp_player_osadzony = 1 // Przepraszamy internet za nazwę } shouldComponentUpdate() { return false } componentWillUnmount() { if (this.player) { this.player.destroy() this.player = null window.wp_player_osadzony = 0 // Jeszcze raz sorry } } render() { return <div ref={(ref) => { this.el = ref }} id={this.getId()}></div> } }
  • 29. SEO Dostępność • Statycznie renderowany html • Dodatkowe części serwisu renderowane po stronie serwera tylko dla botów • Stronę można przeglądać bez JS ✔ ✔ ✔
  • 31. SEO <Helmet title="My Title" meta={[ {"name": "description", "content": "Helmet application"}, {"property": "og:type", "content": "article"} ]} link={[ {"rel": "canonical", "href": "http://mysite.com/example"} ]} script={[ {"src": "http://include.com/pathtojs.js", "type": "text/javascript"}, {"type": "application/ld+json", innerHTML: `{ "@context": "http://schema.org" }`} ]} onChangeClientState={(newState) => console.log(newState)} /> npm.im/react-helmet react-helmet
  • 32. Responsywność • ~80% komponentów jest współdzielona przez desktop i mobile • 100% logiki w innych warstwach jest współdzielone
  • 33. Statystyki reklamy analyticsEventDispatcher({ action: 'page:view', dot: /* ... */, gfk: /* ... */, gemius: /* ... */ }) advertisementEventDispatcher({ action: 'bunch:load', dot: { bunch: 'index' } })
  • 34. Na co uważać • Trzeba poświęcić czas na budowę środowiska developerskiego • HTML + CSS != React + css • warstwa danych (kontrola) • “Śmietnik” w widoku
  • 35. Co się zmieniło • Jakość • Czas developmentu • Obszary “zainteresowania” programistów frontend / backend • Zbliżenie do aplikacji mobilnych • Morale :)
  • 36. Dalszy rozwój • nowe funkcjonalności • zmiana podejścia do CSS • optymalizacje • service workers

Editor's Notes

  • #4: 2015 React 100% client side Tv, Radio, Quizy Pierwsza strona portalowa, która nie jest jedynie miejscem przekliku do innych serwisów.
  • #13: Webpack
  • #15: Fakt użycia React’a nie zwalnia z konieczności myślenia o złożoności aplikacji oraz o ciągłej optymalizacji budowy i układu komponentów.