Commit c3a6de35 authored by Evan You's avatar Evan You

separate server/client api creation and add sw-precache plugin

parent 06f72f5c
...@@ -3,6 +3,11 @@ const base = require('./webpack.base.config') ...@@ -3,6 +3,11 @@ const base = require('./webpack.base.config')
const vueConfig = require('./vue-loader.config') const vueConfig = require('./vue-loader.config')
const config = Object.assign({}, base, { const config = Object.assign({}, base, {
resolve: {
alias: {
'create-api': './create-api-client.js'
}
},
plugins: (base.plugins || []).concat([ plugins: (base.plugins || []).concat([
// strip comments in Vue code // strip comments in Vue code
new webpack.DefinePlugin({ new webpack.DefinePlugin({
...@@ -15,6 +20,7 @@ if (process.env.NODE_ENV === 'production') { ...@@ -15,6 +20,7 @@ if (process.env.NODE_ENV === 'production') {
// Use ExtractTextPlugin to extract CSS into a single file // Use ExtractTextPlugin to extract CSS into a single file
// so it's applied on initial render // so it's applied on initial render
const ExtractTextPlugin = require('extract-text-webpack-plugin') const ExtractTextPlugin = require('extract-text-webpack-plugin')
const SWPrecachePlugin = require('sw-precache-webpack-plugin')
// vueConfig is already included in the config via LoaderOptionsPlugin // vueConfig is already included in the config via LoaderOptionsPlugin
// here we overwrite the loader config for <style lang="stylus"> // here we overwrite the loader config for <style lang="stylus">
...@@ -37,6 +43,10 @@ if (process.env.NODE_ENV === 'production') { ...@@ -37,6 +43,10 @@ if (process.env.NODE_ENV === 'production') {
compress: { compress: {
warnings: false warnings: false
} }
}),
new SWPrecachePlugin({
cacheId: 'vue-hn',
filename: 'service-worker.js'
}) })
) )
} }
......
...@@ -9,6 +9,11 @@ module.exports = Object.assign({}, base, { ...@@ -9,6 +9,11 @@ module.exports = Object.assign({}, base, {
filename: 'server-bundle.js', filename: 'server-bundle.js',
libraryTarget: 'commonjs2' libraryTarget: 'commonjs2'
}), }),
resolve: {
alias: {
'create-api': './create-api-server.js'
}
},
externals: Object.keys(require('../package.json').dependencies), externals: Object.keys(require('../package.json').dependencies),
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
"lru-cache": "^4.0.1", "lru-cache": "^4.0.1",
"serialize-javascript": "^1.3.0", "serialize-javascript": "^1.3.0",
"serve-favicon": "^2.3.0", "serve-favicon": "^2.3.0",
"sw-precache-webpack-plugin": "^0.5.1",
"vue": "^2.0.0", "vue": "^2.0.0",
"vue-router": "^2.0.0", "vue-router": "^2.0.0",
"vue-server-renderer": "^2.0.0", "vue-server-renderer": "^2.0.0",
......
...@@ -7,3 +7,8 @@ store.replaceState(window.__INITIAL_STATE__) ...@@ -7,3 +7,8 @@ store.replaceState(window.__INITIAL_STATE__)
// actually mount to DOM // actually mount to DOM
app.$mount('#app') app.$mount('#app')
// service worker
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
navigator.serviceWorker.register('/dist/service-worker.js')
}
import Firebase from 'firebase' // this is aliased in webpack config based on server/client build
import LRU from 'lru-cache' import api from 'create-api'
const inBrowser = typeof window !== 'undefined' // warm the front page cache every 15 min
if (api.cachedIds) {
// When using bundleRenderer, the server-side application code runs in a new
// context for each request. To allow caching across multiple requests, we need
// to attach the cache to the process which is shared across all requests.
const cache = inBrowser
? null
: (process.__API_CACHE__ || (process.__API_CACHE__ = createCache()))
function createCache () {
return LRU({
max: 1000,
maxAge: 1000 * 60 * 15 // 15 min cache
})
}
// create a single api instance for all server-side requests
const api = inBrowser
? new Firebase('https://hacker-news.firebaseio.com/v0')
: (process.__API__ || (process.__API__ = createServerSideAPI()))
function createServerSideAPI () {
const api = new Firebase('https://hacker-news.firebaseio.com/v0')
// cache the latest story ids
api.__ids__ = {}
;['top', 'new', 'show', 'ask', 'job'].forEach(type => {
api.child(`${type}stories`).on('value', snapshot => {
api.__ids__[type] = snapshot.val()
})
})
// warm the front page cache every 15 min
warmCache() warmCache()
function warmCache () { }
fetchItems((api.__ids__.top || []).slice(0, 30))
setTimeout(warmCache, 1000 * 60 * 15)
}
return api function warmCache () {
fetchItems((api.cachedIds.top || []).slice(0, 30))
setTimeout(warmCache, 1000 * 60 * 15)
} }
function fetch (child) { function fetch (child) {
const cache = api.cachedItems
if (cache && cache.has(child)) { if (cache && cache.has(child)) {
return Promise.resolve(cache.get(child)) return Promise.resolve(cache.get(child))
} else { } else {
...@@ -60,8 +29,8 @@ function fetch (child) { ...@@ -60,8 +29,8 @@ function fetch (child) {
} }
export function fetchIdsByType (type) { export function fetchIdsByType (type) {
return api.__ids__ && api.__ids__[type] return api.cachedIds && api.cachedIds[type]
? Promise.resolve(api.__ids__[type]) ? Promise.resolve(api.cachedIds[type])
: fetch(`${type}stories`) : fetch(`${type}stories`)
} }
......
import Firebase from 'firebase'
export default new Firebase('https://hacker-news.firebaseio.com/v0')
import Firebase from 'firebase'
import LRU from 'lru-cache'
let api
if (process.__API__) {
api = process.__API__
} else {
api = process.__API__ = new Firebase('https://hacker-news.firebaseio.com/v0')
// fetched item cache
api.cachedItems = LRU({
max: 1000,
maxAge: 1000 * 60 * 15 // 15 min cache
})
// cache the latest story ids
api.cachedIds = {}
;['top', 'new', 'show', 'ask', 'job'].forEach(type => {
api.child(`${type}stories`).on('value', snapshot => {
api.cachedIds[type] = snapshot.val()
})
})
}
export default api
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment