Skip to content

Commit

Permalink
feat(SSR): per instance Platform and Cookies
Browse files Browse the repository at this point in the history
  • Loading branch information
rstoenescu committed Jun 13, 2018
1 parent 9757996 commit 8b8c0aa
Show file tree
Hide file tree
Showing 35 changed files with 265 additions and 288 deletions.
18 changes: 14 additions & 4 deletions build/script.dev.ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ const
microcache = require('route-cache'),
{ createBundleRenderer } = require('vue-server-renderer'),
MFS = require('memory-fs'),
clientConfig = require('./webpack.ssr.client'),
serverConfig = require('./webpack.ssr.server')
chokidar = require('chokidar')

const
clientConfig = require('./webpack.ssr.client'),
serverConfig = require('./webpack.ssr.server'),
env = require('./env'),
resolve = file => path.resolve(__dirname, '..', file),
useMicroCache = true,
Expand Down Expand Up @@ -44,11 +45,17 @@ function showBanner () {
function setupDevServer (app, templatePath, cb) {
let clientManifest, bundle, ready
let clientReady, serverReady
let template = fs.readFileSync(templatePath, 'utf-8')
const
template = fs.readFileSync(templatePath, 'utf-8'),
clientPromise = new Promise((resolve, reject) => { clientReady = resolve }),
serverPromise = new Promise((resolve, reject) => { serverReady = resolve })

chokidar.watch(templatePath).on('change', () => {
template = fs.readFileSync(templatePath, 'utf-8')
console.log(' 🖖 ssr.index.html template updated')
update()
})

const readyPromise = new Promise((resolve, reject) => { ready = resolve })
const update = () => {
if (bundle && clientManifest) {
Expand Down Expand Up @@ -185,7 +192,10 @@ function render (req, res) {
const context = {
url: req.url,
req,
res
res,
bodyClasses: '',
htmlAttrs: '',
baseHref: env.devServerConfig.publicPath
}

renderer.renderToString(context, (err, html) => {
Expand Down
2 changes: 1 addition & 1 deletion build/webpack.spa.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const chain = new WebpackChain()
require('./webpack.inject.base')(chain)

chain.entry('app')
.add(resolve('dev/spa.app.js'))
.add(resolve('dev/app.entry-client.js'))

chain.plugin('define')
.use(webpack.DefinePlugin, [{
Expand Down
4 changes: 3 additions & 1 deletion build/webpack.ssr.client.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ const
// inject base
require('./webpack.inject.base')(chain)

chain.devtool('#cheap-module-source-map')

chain.entry('client')
.add('webpack-hot-middleware/client')
.add(resolve('dev/ssr.app.entry-client.js'))
.add(resolve('dev/app.entry-client.js'))

chain.output
.path(resolve('dev'))
Expand Down
3 changes: 2 additions & 1 deletion build/webpack.ssr.server.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ const
require('./webpack.inject.base')(chain)

chain.target('node')
chain.devtool('#source-map')

chain.entry('server')
.add(resolve('dev/ssr.app.entry-server.js'))
.add(resolve('dev/app.entry-server.js'))

chain.output
.filename('server-bundle.js')
Expand Down
2 changes: 1 addition & 1 deletion dev/ssr.app.entry-client.js → dev/app.entry-client.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createApp } from './ssr.app'
import { createApp } from './app'

const { app, router } = createApp()

Expand Down
12 changes: 6 additions & 6 deletions dev/ssr.app.entry-server.js → dev/app.entry-server.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { createApp } from './ssr.app'
import { createApp } from './app'

export default context => {
export default ssrContext => {
// since there could potentially be asynchronous route hooks or components,
// we will be returning a Promise so that the server can wait until
// everything is ready before rendering.

return new Promise((resolve, reject) => {
const { app, router } = createApp(context)
const { app, router } = createApp(ssrContext)

const { fullPath } = router.resolve(context.url).route
const { fullPath } = router.resolve(ssrContext.url).route

if (fullPath !== context.url) {
if (fullPath !== ssrContext.url) {
return reject({ url: fullPath })
}

// set server-side router's location
router.push(context.url)
router.push(ssrContext.url)

// wait until router has resolved possible async components and hooks
router.onReady(() => {
Expand Down
66 changes: 33 additions & 33 deletions dev/ssr.app.js → dev/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,19 @@ import { createRouter } from './router'
import Quasar, * as Everything from 'quasar'

import 'quasar-css'

/*
if (process.env.THEME === 'mat') {
require('quasar-extras/roboto-font')
}
import 'quasar-extras/material-icons'
import 'quasar-extras/ionicons'
import 'quasar-extras/fontawesome'
import 'quasar-extras/mdi'
import 'quasar-extras/animate'
*/

// import iconSet from '../icons/fontawesome'

Vue.use(Quasar, {
components: Everything,
directives: Everything,
plugins: Everything,
// iconSet,
config: {}
})

// export a factory function for creating fresh app, router and store
// instances
export function createApp (ctx) {
const config = {}

if (ctx) {
config.ssr = {
req: ctx.req,
res: ctx.res
}
}

Vue.use(Quasar, {
components: Everything,
directives: Everything,
plugins: Everything,
config
// iconSet
})

export function createApp (ssrContext) {
const router = createRouter()

if (process.env.VUE_ENV === 'client') {
Expand All @@ -57,10 +36,31 @@ export function createApp (ctx) {
})
}

const app = new Vue({
const app = {
router,
...App
})
}

return { app, router }
if (ssrContext) {
Quasar.ssrUpdate({
app,
req: ssrContext.req,
res: ssrContext.res,
setBodyClasses (cls) {
ssrContext.bodyClasses = cls.join(' ')
},
setHtmlAttrs (attrs) {
const str = []
for (let key in attrs) {
str.push(`${key}=${attrs[key]}`)
}
ssrContext.htmlAttrs = str.join(' ')
}
})
}

return {
app: new Vue(app),
router
}
}
11 changes: 0 additions & 11 deletions dev/components/error404.vue

This file was deleted.

6 changes: 2 additions & 4 deletions dev/components/other/platform-detection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,15 @@
</template>

<script>
import { Platform } from 'quasar'
export default {
data () {
return {
platform: Platform.is
platform: this.$q.platform.is
}
},
computed: {
touch () {
return Platform.has.touch ? 'has' : 'does not have'
return this.$q.platform.has.touch ? 'has' : 'does not have'
}
}
}
Expand Down
4 changes: 0 additions & 4 deletions dev/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ pages.forEach(page => {
routes.push(component(page))
})

if (!process.env.QUASAR_SSR) {
routes.push({path: '*', component: load('error404')})
}

export function createRouter () {
return new VueRouter({
mode: 'history',
Expand Down
44 changes: 0 additions & 44 deletions dev/spa.app.js

This file was deleted.

3 changes: 2 additions & 1 deletion dev/spa.index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
<base href="/">
<title>Quasar Development</title>
<link rel="icon" href="statics/quasar-logo.png" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Material+Icons">
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Material+Icons&Roboto:100,300,400,500,700,900">
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/animate.css@^3.5.2/animate.min.css">
</head>
<body class="demo-site">
<div id="q-app"></div>
Expand Down
9 changes: 5 additions & 4 deletions dev/ssr.index.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
<!DOCTYPE html>
<html lang="en-us">
<html {{ htmlAttrs }}>
<head>
<meta charset="utf-8">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">

<base href="/">
<base href="{{ baseHref }}">
<title>Quasar Development</title>
<link rel="icon" href="statics/quasar-logo.png" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Material+Icons">
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Material+Icons&Roboto:100,300,400,500,700,900">
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/animate.css@^3.5.2/animate.min.css">
</head>
<body class="demo-site">
<body class="demo-site {{ bodyClasses }}">
<!--vue-ssr-outlet-->
<!-- built files will be auto injected -->
</body>
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"babel-eslint": "^8.2.2",
"babel-loader": "^8.0.0-beta.2",
"babel-preset-es2015-rollup": "^3.0.0",
"chokidar": "^2.0.3",
"connect-history-api-fallback": "^1.5.0",
"css-loader": "^0.28.11",
"eslint": "^4.15.0",
Expand Down
68 changes: 68 additions & 0 deletions src/body.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ready } from './utils/dom'
import { setBrand } from './utils/colors'

function getBodyClasses ({ is, has, within }, cfg = {}) {
const cls = [
process.env.THEME,
is.desktop ? 'desktop' : 'mobile',
has.touch ? 'touch' : 'no-touch',
`platform-${is.ios ? 'ios' : 'mat'}`
]

if (is.cordova) {
cls.push('cordova')

if (is.ios && (cfg.cordova === void 0 || cfg.cordova.iosStatusBarPadding !== false)) {
const
ratio = window.devicePixelRatio || 1,
width = window.screen.width * ratio,
height = window.screen.height * ratio

if (width !== 1125 && height !== 2001 /* 2436 for iPhoneX fullscreen */) {
cls.push('q-ios-statusbar-padding')
}
}
}
within.iframe && cls.push('within-iframe')
is.electron && cls.push('electron')

return cls
}

function bodyInit (Platform, cfg) {
const cls = getBodyClasses(Platform, cfg)

if (Platform.is.ie && Platform.is.versionNumber === 11) {
cls.forEach(c => document.body.classList.add(c))
}
else {
document.body.classList.add.apply(document.body.classList, cls)
}

if (Platform.is.ios) {
// needed for iOS button active state
document.body.addEventListener('touchstart', () => {})
}
}

function setColors (brand) {
for (let color in brand) {
setBrand(color, brand[color])
}
}

export function ssrUpdateBody (ssr) {
const update = ssr.setBodyClasses
if (typeof update === 'function') {
update(getBodyClasses(ssr.app.$q.platform))
}
}

export function clientUpdateBody ({ $q, cfg }) {
const init = cfg.brand && document.body
init && setColors(cfg.brand)
ready(() => {
!init && setColors(cfg.brand)
bodyInit($q.platform, cfg)
})
}
Loading

0 comments on commit 8b8c0aa

Please sign in to comment.