SlideShare una empresa de Scribd logo
1 de 125
Descargar para leer sin conexión
Server-side Rendering of JavaScript
in PHP
Nacho Martín
Nacho Martín
I write code at Limenius.
We build tailor-made projects,
and provide consultancy and formation.
We are very happy with React, and have been
dealing with how to integrate with PHP for
some time now & publishing libraries about
it.
What is the problem that
Server Side Rendering
adresses?
Nacho Martín
nacho@limenius.com
@nacmartin
A long time ago in a galaxy far, far away
Server
Nacho Martín
nacho@limenius.com
@nacmartin
A long time ago in a galaxy far, far away
Server
HTML
</>
HTML
</> Client
Nacho Martín
nacho@limenius.com
@nacmartin
Very simple model that works
But to show a change we have to do a full
page reload.
Even for things like “your password must have
at least 6 characters”.
Nacho Martín
nacho@limenius.com
@nacmartin
Even for things like this
New password
New Submit
Nacho Martín
nacho@limenius.com
@nacmartin
Even for things like this
New password
New Submit
****
Nacho Martín
nacho@limenius.com
@nacmartin
Even for things like this
New password
New Submit
****
Your password must contain 6 characters!
Nacho Martín
nacho@limenius.com
@nacmartin
Adding dynamic elements
HTML
</>
Client
HTML
</>
Server
Nacho Martín
nacho@limenius.com
@nacmartin
Adding dynamic elements
HTML
</>
Client
HTML
</>
JS JS
Server
Nacho Martín
nacho@limenius.com
@nacmartin
Step 1: Client uses JS to modify the DOM
Client
HTML
</>
JS
$( "p" ).addClass( “myClass" );
Nacho Martín
nacho@limenius.com
@nacmartin
With DOM modification
We can now modify the document reacting to
user interaction.
What about loading new content based on
content interaction?
Nacho Martín
nacho@limenius.com
@nacmartin
Example
1 2 3 4 5
Nacho Martín
nacho@limenius.com
@nacmartin
Adding dynamic content
HTML
</>
Client
HTML
</>
JS JS
Server
Nacho Martín
nacho@limenius.com
@nacmartin
Adding dynamic content
HTML
</>
Client
HTML
</>
JS JS
Server
API
Nacho Martín
nacho@limenius.com
@nacmartin
Step 2: Dynamic content
Client
HTML
</>
JS
$(“#grid").load( “api/page2.html“ );
API
Nacho Martín
nacho@limenius.com
@nacmartin
Step 2: Dynamic content
Client
HTML
</>
JS
$(“#grid").load( “api/page2.html“ );
API
$.get( “api/page2.json“,
function(data) {
$(“#grid”).html(renderPage(data));
}
);
Nacho Martín
nacho@limenius.com
@nacmartin
DOM Manipulation
HTML
</>
This happens in the Browser
Element
<body>
Element
<div id=“grid”>
Element
<h1>
Text
“Hi there”
…
Element
<div>
Element
<div>
Nacho Martín
nacho@limenius.com
@nacmartin
DOM Manipulation
This happens in the Browser
Element
<body>
Element
<div id=“grid”>
Element
<h1>
Text
“Hi there”
…
Element
<div>
Element
<div>
API
$.get( “api/page2.json“,
function(data) {
$(“#grid”).html(renderPage(data));
}
);
Nacho Martín
nacho@limenius.com
@nacmartin
DOM Manipulation
This happens in the Browser
Element
<body>
Element
<div id=“grid”>
Element
<h1>
Text
“Hi there”
API
$.get( “api/page2.json“,
function(data) {
$(“#grid”).html(renderPage(data));
}
);
Nacho Martín
nacho@limenius.com
@nacmartin
DOM Manipulation
This happens in the Browser
Element
<body>
Element
<div id=“grid”>
Element
<h1>
Text
“Hi there”
…
Element
<div>
Element
<div>
API
$.get( “api/page2.json“,
function(data) {
$(“#grid”).html(renderPage(data));
}
);
Nacho Martín
nacho@limenius.com
@nacmartin
Problem: duplication of work
HTML
</>
We need a mechanism in the server to build the initial HTML…
Nacho Martín
nacho@limenius.com
@nacmartin
Problem: duplication of work
…and another to update the DOM in the client
API
$(“#grid”).html(renderPage(data));
Nacho Martín
nacho@limenius.com
@nacmartin
Possible solution: don’t render the content in HTML
HTML
</>
<div id=“grid”>
Nacho Martín
nacho@limenius.com
@nacmartin
And on document load do an API call
<div id=“grid”>
API
$(“#grid”).html(renderPage(data));
Nacho Martín
nacho@limenius.com
@nacmartin
This means that the first thing the user sees is this
…and also crawlers :(
Nacho Martín
nacho@limenius.com
@nacmartin
Slow page loads in mobile users
https://www.doubleclickbygoogle.com/articles/mobile-speed-matters/
• Average load time over 3G: 19 seconds.
• 53% of sites that take longer than 3s are abandoned.
• Going from 19s to 5s means:
• 25% more impressions of ads.
• 70% longer sessions.
• 35% lower bounce race.
• 2x ad revenue.
Nacho Martín
nacho@limenius.com
@nacmartin
When are these problems worse
Apps. Bearable.
Content pages. Probably unbearable.
Concurrent problem:
DOM manipulation
vs
State based JS libraries
Nacho Martín
nacho@limenius.com
@nacmartin
We want to build a TODO list
Pour eggs in the pan
How to cook an omelette
Buy eggs
Break eggs
Nacho Martín
nacho@limenius.com
@nacmartin
We want to build a TODO list
Pour eggs in the pan
Beat eggs
How to cook an omelette
Buy eggs
Break eggs
Nacho Martín
nacho@limenius.com
@nacmartin
Options
Nacho Martín
nacho@limenius.com
@nacmartin
Options
1: Re-render everything.
Nacho Martín
nacho@limenius.com
@nacmartin
Options
1: Re-render everything. Simple
Nacho Martín
nacho@limenius.com
@nacmartin
Options
1: Re-render everything. Simple Not efficient
Nacho Martín
nacho@limenius.com
@nacmartin
Options
2: Find in the DOM where to
insert elements, what to move,
what to remove…
1: Re-render everything. Simple Not efficient
Nacho Martín
nacho@limenius.com
@nacmartin
Options
2: Find in the DOM where to
insert elements, what to move,
what to remove…
1: Re-render everything. Simple
Complex
Not efficient
Nacho Martín
nacho@limenius.com
@nacmartin
Options
2: Find in the DOM where to
insert elements, what to move,
what to remove…
1: Re-render everything. Simple
EfficientComplex
Not efficient
Nacho Martín
nacho@limenius.com
@nacmartin
Options
2: Find in the DOM where to
insert elements, what to move,
what to remove…
1: Re-render everything. Simple
EfficientComplex
Not efficient
React allows us to do 1, although it does 2 behind the scenes
Nacho Martín
nacho@limenius.com
@nacmartin
Fundamental premise
Give me a state and a render() method that depends
on it and forget about how and when to render.
Nacho Martín
nacho@limenius.com
@nacmartin
Let’s write a React component
Click me! Clicks: 0
Nacho Martín
nacho@limenius.com
@nacmartin
Let’s write a React component
Click me! Clicks: 1Click me!
Nacho Martín
nacho@limenius.com
@nacmartin
Let’s write a React component
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
tick() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
}
export default Counter;
Nacho Martín
nacho@limenius.com
@nacmartin
Let’s write a React component
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
tick() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
}
export default Counter;
Initial state
Nacho Martín
nacho@limenius.com
@nacmartin
Let’s write a React component
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
tick() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
}
export default Counter;
Set new state
Initial state
Nacho Martín
nacho@limenius.com
@nacmartin
Let’s write a React component
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
tick() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
}
export default Counter;
Set new state
render(), called by React
Initial state
Nacho Martín
nacho@limenius.com
@nacmartin
Working with state
constructor(props) {
super(props);
this.state = {count: 1};
}
Initial state
Nacho Martín
nacho@limenius.com
@nacmartin
Working with state
constructor(props) {
super(props);
this.state = {count: 1};
}
Initial state
this.setState({count: this.state.count + 1});
Assign state
Nacho Martín
nacho@limenius.com
@nacmartin
render() and JSX
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Clícame!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
It is not HTML, it is JSX.
React transforms it internally to HTML elements.
Good practice: make render() as clean as possible, only a return.
Nacho Martín
nacho@limenius.com
@nacmartin
render() and JSX
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Nacho Martín
nacho@limenius.com
@nacmartin
render() and JSX
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Here we don’t modify the state
Nacho Martín
nacho@limenius.com
@nacmartin
render() and JSX
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Here we don’t make Ajax calls
Nacho Martín
nacho@limenius.com
@nacmartin
render() and JSX
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Here we don’t calculate decimals of PI and send an e-mail
with the result
Nacho Martín
nacho@limenius.com
@nacmartin
Components hierarchy
Nacho Martín
nacho@limenius.com
@nacmartin
Components hierarchy
Nacho Martín
nacho@limenius.com
@nacmartin
Components hierarchy: props
class CounterGroup extends Component {
render() {
return (
<div>
<Counter name="amigo"/>
<Counter name="señor"/>
</div>
);
}
}
Nacho Martín
nacho@limenius.com
@nacmartin
Components hierarchy: props
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>
Click me! {this.props.name}
</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
and in Counter…
class CounterGroup extends Component {
render() {
return (
<div>
<Counter name="amigo"/>
<Counter name="señor"/>
</div>
);
}
}
Nacho Martín
nacho@limenius.com
@nacmartin
Components hierarchy: props
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>
Click me! {this.props.name}
</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
and in Counter…
class CounterGroup extends Component {
render() {
return (
<div>
<Counter name="amigo"/>
<Counter name="señor"/>
</div>
);
}
}
Nacho Martín
nacho@limenius.com
@nacmartin
Everything depends on the state, therefore we can:
Nacho Martín
nacho@limenius.com
@nacmartin
Everything depends on the state, therefore we can:
•Reproduce states,
Nacho Martín
nacho@limenius.com
@nacmartin
Everything depends on the state, therefore we can:
•Reproduce states,
•Rewind,
Nacho Martín
nacho@limenius.com
@nacmartin
Everything depends on the state, therefore we can:
•Reproduce states,
•Rewind,
•Log state changes,
Nacho Martín
nacho@limenius.com
@nacmartin
Everything depends on the state, therefore we can:
•Reproduce states,
•Rewind,
•Log state changes,
•…
Nacho Martín
nacho@limenius.com
@nacmartin
Everything depends on the state, therefore we can
Nacho Martín
nacho@limenius.com
@nacmartin
Everything depends on the state, therefore we can
render the initial state to a HTML string
Server side rendering
Nacho Martín
nacho@limenius.com
@nacmartin
First page load
HTML
</>
Client
HTML
</>
JS JS
Server
API
Includes initial state
Nacho Martín
nacho@limenius.com
@nacmartin
When we need more data
Client
Server
APIAPI
API calls to update the state
and therefore update
its representation (UI)
Nacho Martín
nacho@limenius.com
@nacmartin
ReactDOMServer.renderToString(element)
ReactDOM.hydrate(element, container[, callback])
SSR in React
Nacho Martín
nacho@limenius.com
@nacmartin
ReactDOMServer.renderToString(<MyApp/>)
SSR in React. 1) In the server:
<div data-reactroot="">
This is some <span>server-generated</span> <span>HTML.</span>
</div>
Nacho Martín
nacho@limenius.com
@nacmartin
SSR in React. 2) insert in our template
<html>
…
<body>
<div id=“root”>
<div data-reactroot="">
This is some <span>server-generated</span> <span>HTML.</span>
</div>
</div>
…
Nacho Martín
nacho@limenius.com
@nacmartin
ReactDOM.hydrate(
<MyApp/>,
document.getElementById('root')
)
SSR in React. 1) In the client:
<div id=“root”>
<div data-reactroot="">
This is some <span>server-generated</span> <span>HTML.</span>
</div>
</div>
…
The client takes control over it
Nacho Martín
nacho@limenius.com
@nacmartin
renderer.renderToString(app, (err, html) => {
if (err) throw err
console.log(html)
// => <div data-server-rendered="true">Hello World</div>
})
var app = new Vue({
el: ‘#root',
data: {
message: 'Hello Vue!'
}
})
Similar, in Vue
Nacho Martín
nacho@limenius.com
@nacmartin
React, Vue, but does my library support this?
If what is rendered depends on the state then
we are probably good.
If your JS depends on having the DOM loaded
and manipulate it, then we are not good.
Nacho Martín
nacho@limenius.com
@nacmartin
Problematic Example
Page
Nacho Martín
nacho@limenius.com
@nacmartin
Problematic Example
Page
API
My React Component
Nacho Martín
nacho@limenius.com
@nacmartin
Problematic Example
Page
API
My React Component
API
“React Component” that renders CkEditor,
d3, or other lib that relies on DOM manipulation
Nacho Martín
nacho@limenius.com
@nacmartin
Problematic Example
Page
API
My React Component
API
“React Component” that renders CkEditor,
d3, or other lib that relies on DOM manipulation
SSR in PHP
Nacho Martín
nacho@limenius.com
@nacmartin
Why?
SSR in JavaScript (node.js) is more natural.
But it may not be the right choice for the project.
Maybe the team has expertise in PHP.
Maybe the project already exists.
Maybe we want to combine it with sections
rendered from PHP.
The Real World is not as simple as tutorials.
Nacho Martín
nacho@limenius.com
@nacmartin
We need
Nacho Martín
nacho@limenius.com
@nacmartin
WebpackAssets
JS JS
TS SASS
PNG JPEG
JS
Client App
Nacho Martín
nacho@limenius.com
@nacmartin
WebpackAssets
JS JS
TS SASS
PNG JPEG
JS
Client App
JS
Server side App
Nacho Martín
nacho@limenius.com
@nacmartin
JS Code to execute
Server side JS App
+
Component and state that we want to render
A few bytes,
Changes between
requests
As big as your app.
Doesn’t change
between requests
Options
Nacho Martín
nacho@limenius.com
@nacmartin
This is what we would do for SSR in JS
Client
JS
Front
PHP
API
JS
Client side App
Server side JS App
+
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
This is what we would do for SSR in JS
Client
JS
Front
PHP
API
JS
Client side App
Server side JS App
+
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
This is what we would do for SSR in JS
Client
JS
Front
PHP
API
JS
Client side App
Server side JS App
+
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
Option 1: node.js as subprocess
Client
PHP
App
Node.js
JS
Client side App
Server side JS App
+
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
Option 1: node.js as subprocess
Client
PHP
App
Node.js
JS
Client side App
Server side JS App
+
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
Make a call to node.js using Symfony Process component
* Easy to setup.
* Slow.
Library: https://github.com/nacmartin/phpexecjs
Option 1: Call a node.js subprocess
Nacho Martín
nacho@limenius.com
@nacmartin
Option 2: V8JS
Client
PHP
App
V8js
JS
Client side App
Server side JS App
+
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
Option 2: V8JS
Client
PHP
App
V8js
JS
Client side App
Server side JS App
+
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
But we can cache
public function createContext($code, $cachename = null)
{
if ($cachename) {
$cacheItem = $this->cache->getItem($cachename);
if ($cacheItem->isHit()) {
$snapshot = $cacheItem->get();
} else {
$snapshot = V8Js::createSnapshot($code);
$cacheItem->set($snapshot);
$this->cache->save($cacheItem);
}
} else {
$snapshot = V8Js::createSnapshot($code);
}
$this->v8 = new V8Js('PHP', [], [], true, $snapshot);
}
Nacho Martín
nacho@limenius.com
@nacmartin
Option 2: V8JS
Client
PHP
App
V8js
JS
Client side App
Server side JS App
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
Option 2: V8JS
Client
PHP
App
V8js
JS
Client side App
Server side JS App
Component and state
📷
Nacho Martín
nacho@limenius.com
@nacmartin
Option 2: V8JS
Client
PHP
App
V8js
JS
Client side App
Server side JS App
Component and state
📷
Nacho Martín
nacho@limenius.com
@nacmartin
Use PHP extension v8js
* Fast.
* Need to compile v8, v8js, find Docker images… (this problem is not
small).
Library: https://github.com/nacmartin/phpexecjs
Option 2: v8js PHP extension
Nacho Martín
nacho@limenius.com
@nacmartin
Option 3: External JS server
Client
PHP
App
JS
renderer
JS
Client side App
Server side JS App
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
Option 3: External JS server
Client
PHP
App
JS
renderer
JS
Client side App
Server side JS App
Component and state
Nacho Martín
nacho@limenius.com
@nacmartin
We have “stupid” node.js server used only to render components.
It has <100 LoC, and it doesn’t know anything about our logic.
* Fast.
There is an example a dummy JS server for this purpose at
https://github.com/Limenius/symfony-react-sandbox
Option 3: External node.js server
ReactRenderer &
ReactBundle
Nacho Martín
nacho@limenius.com
@nacmartin
Some libraries
phpexecjsReactRendererReactBundle
node.js
v8js
…
Twig extension
External renderer
Selects JS runner
Runs it
Uses snapshots if available
Integration with
Symfony
Nacho Martín
nacho@limenius.com
@nacmartin
Options 1 & 2
$renderer = new PhpExecJsReactRenderer(‘path_to/server-bundle.js’);
$ext = new ReactRenderExtension($renderer, 'both');
$twig->addExtension($ext);
phpexecjs detects the presence of the extension v8js,
if not, calls node.js
Nacho Martín
nacho@limenius.com
@nacmartin
Option 3: external renderer
$renderer = new ExternalServerReactRenderer(‘../some_path/node.sock’);
$ext = new ReactRenderExtension($renderer, 'both');
$twig->addExtension($ext);
Nacho Martín
nacho@limenius.com
@nacmartin
JS side part: React on Rails
https://github.com/shakacode/react_on_rails
Used among others by
Nacho Martín
nacho@limenius.com
@nacmartin
JS side part: React on Rails
{{ react_component('RecipesApp', {'props': props}) }}
import ReactOnRails from 'react-on-rails';
import RecipesApp from './RecipesAppServer';
ReactOnRails.register({ RecipesApp });
Twig:
JavaScript:
Nacho Martín
nacho@limenius.com
@nacmartin
Redux integration
Nacho Martín
nacho@limenius.com
@nacmartin
Redux integration
Redux
Store
Nacho Martín
nacho@limenius.com
@nacmartin
Redux integration
import ReactOnRails from 'react-on-rails'
import RecipesAppRedux from './RecipesApp'
import configureStore from ' ../store/RecipesStore'
const recipesStore = configureStore
ReactOnRails.registerStore({ recipesStore })
ReactOnRails.register({ RecipesAppRedux })
Twig:
JavaScript:
{{ redux_store(‘recipesStore’, initialState) }}
{{ react_component('RecipesAppRedux') }}
Nacho Martín
nacho@limenius.com
@nacmartin
Share store between components
Nacho Martín
nacho@limenius.com
@nacmartin
React
React
React
Twig
Twig
React
By sharing store they
can share state
Twig
Share store between components
Things to consider
Context
Nacho Martín
nacho@limenius.com
@nacmartin
Context variables. PHP
[
'serverSide' => $serverSide,
'href' => $request ->getSchemeAndHttpHost() . $request ->getRequestUri(),
'location' => $request ->getRequestUri(),
'scheme' => $request ->getScheme(),
'host' => $request ->getHost(),
'port' => $request ->getPort(),
'base' => $request ->getBaseUrl(),
'pathname' => $request ->getPathInfo(),
'search' => $request ->getQueryString(),
];
Nacho Martín
nacho@limenius.com
@nacmartin
Context in the JS side
export default (initialProps, context) => {
if (context.serverSide) {
return <StaticRouter basename={context.base} location={context.location} />;
} else {
return <BrowserRouter basename={context.base} />;
}
};
Header tags
Nacho Martín
nacho@limenius.com
@nacmartin
Extracting headers
react-helmet (vue-helmet)
import { Helmet } from "react-helmet";
class Application extends React.Component {
render() {
return (
<div className="application">
<Helmet>
<meta charSet="utf-8" />
<title>My Title </title>
<link rel="canonical" href="http: //mysite.com/example" />
</Helmet>
...
</div>
);
}
}
Nacho Martín
nacho@limenius.com
@nacmartin
Extracting headers
export default (initialProps, context) => ({
renderedHtml: {
componentHtml: renderToString(
<StaticRouter
basename={context.base}
location={context.location}
context={{}}>
<App initialProps={initialProps} appContext={context} />
</StaticRouter>
),
title: Helmet.renderStatic().title.toString()
}
});
Return array instead of component
Nacho Martín
nacho@limenius.com
@nacmartin
Then in Twig
{% set recipes = react_component_array('RecipesApp', {'props': props}) %}
{% block title %}
{{ recipes.title is defined ? recipes.title | raw : '' }}
{% endblock title %}
{% block body %}
{{ recipes.componentHtml | raw }}{{ redux_store('recipesStore', initialState) }}
{% endblock body %}
Make reality checks
Nacho Martín
nacho@limenius.com
@nacmartin
Better to try it soon
Certain JS code doesn’t make sense in SSR:
• Timers: SetTimeout, setInterval.
• Access to window, or document objects.
• Access to the DOM.
• First render that depends on weird things like API calls (this is a smell).
Don’t wait until the last moment to check SSR if it is in the roadmap
Summary:
What is SSR and what is it for
Ways to do it in PHP (pros & cons)
Some libraries
Tips & practical problems
Thanks!
Questions?
Nacho Martín
nacho@limenius.com
@nacmartin

Más contenido relacionado

Similar a Server Side Rendering of JavaScript in PHP

Similar a Server Side Rendering of JavaScript in PHP (20)

Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
 
M365 global developer bootcamp 2019 PA
M365 global developer bootcamp 2019  PAM365 global developer bootcamp 2019  PA
M365 global developer bootcamp 2019 PA
 
Introduction to React Native Workshop
Introduction to React Native WorkshopIntroduction to React Native Workshop
Introduction to React Native Workshop
 
M365 global developer bootcamp 2019
M365 global developer bootcamp 2019M365 global developer bootcamp 2019
M365 global developer bootcamp 2019
 
Chris Wilson: Progressive Web Apps
Chris Wilson: Progressive Web AppsChris Wilson: Progressive Web Apps
Chris Wilson: Progressive Web Apps
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projects
 
Composable and streamable Play apps
Composable and streamable Play appsComposable and streamable Play apps
Composable and streamable Play apps
 
URL Hacking 101: An Easy Way to Streamline Processes in Salesforce
URL Hacking 101: An Easy Way to Streamline Processes in Salesforce URL Hacking 101: An Easy Way to Streamline Processes in Salesforce
URL Hacking 101: An Easy Way to Streamline Processes in Salesforce
 
The Art of Gherkin Scripting - Matt Eakin
The Art of Gherkin Scripting - Matt EakinThe Art of Gherkin Scripting - Matt Eakin
The Art of Gherkin Scripting - Matt Eakin
 
Developing Business Blockchain Applications on Hyperledger
Developing Business  Blockchain Applications on Hyperledger Developing Business  Blockchain Applications on Hyperledger
Developing Business Blockchain Applications on Hyperledger
 
MongoDB.local Dallas 2019: MongoDB Stitch Tutorial
MongoDB.local Dallas 2019: MongoDB Stitch TutorialMongoDB.local Dallas 2019: MongoDB Stitch Tutorial
MongoDB.local Dallas 2019: MongoDB Stitch Tutorial
 
SRV328 Designing and Implementing a Serverless Media-Processing Workflow
SRV328 Designing and Implementing a Serverless Media-Processing WorkflowSRV328 Designing and Implementing a Serverless Media-Processing Workflow
SRV328 Designing and Implementing a Serverless Media-Processing Workflow
 
Battle of React State Managers in frontend applications
Battle of React State Managers in frontend applicationsBattle of React State Managers in frontend applications
Battle of React State Managers in frontend applications
 
ASP DOT NET
ASP DOT NETASP DOT NET
ASP DOT NET
 
[Meetup] a successful migration from elastic search to clickhouse
[Meetup] a successful migration from elastic search to clickhouse[Meetup] a successful migration from elastic search to clickhouse
[Meetup] a successful migration from elastic search to clickhouse
 
Bootstrapping an App for Launch
Bootstrapping an App for LaunchBootstrapping an App for Launch
Bootstrapping an App for Launch
 
MongoDB.local Atlanta: MongoDB Stitch Tutorial
MongoDB.local Atlanta: MongoDB Stitch TutorialMongoDB.local Atlanta: MongoDB Stitch Tutorial
MongoDB.local Atlanta: MongoDB Stitch Tutorial
 
Intro to WebSockets (in Java)
Intro to WebSockets (in Java)Intro to WebSockets (in Java)
Intro to WebSockets (in Java)
 
A Journey with React
A Journey with ReactA Journey with React
A Journey with React
 
Presentation Tier optimizations
Presentation Tier optimizationsPresentation Tier optimizations
Presentation Tier optimizations
 

Más de Ignacio Martín

Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worlds
Ignacio Martín
 

Más de Ignacio Martín (15)

Elixir/OTP for PHP developers
Elixir/OTP for PHP developersElixir/OTP for PHP developers
Elixir/OTP for PHP developers
 
Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - Limenius
 
Extending Redux in the Server Side
Extending Redux in the Server SideExtending Redux in the Server Side
Extending Redux in the Server Side
 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React Alicante
 
React Native Workshop - React Alicante
React Native Workshop - React AlicanteReact Native Workshop - React Alicante
React Native Workshop - React Alicante
 
Asegurando APIs en Symfony con JWT
Asegurando APIs en Symfony con JWTAsegurando APIs en Symfony con JWT
Asegurando APIs en Symfony con JWT
 
Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
 
Keeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and WebpackKeeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and Webpack
 
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
 
Adding Realtime to your Projects
Adding Realtime to your ProjectsAdding Realtime to your Projects
Adding Realtime to your Projects
 
Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worlds
 
Symfony 2 CMF
Symfony 2 CMFSymfony 2 CMF
Symfony 2 CMF
 
Doctrine2 sf2Vigo
Doctrine2 sf2VigoDoctrine2 sf2Vigo
Doctrine2 sf2Vigo
 
Presentacion git
Presentacion gitPresentacion git
Presentacion git
 

Último

+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 

Último (20)

Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...
 

Server Side Rendering of JavaScript in PHP