Skip to content

Commit 1003dd3

Browse files
committed
use html-webpack-plugin, better caching, handle 404
1 parent 95de885 commit 1003dd3

8 files changed

+369
-31
lines changed

build/webpack.base.config.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,22 @@ const vueConfig = require('./vue-loader.config')
33

44
module.exports = {
55
devtool: '#source-map',
6-
entry: './src/client-entry.js',
6+
entry: {
7+
app: './src/client-entry.js',
8+
vendor: [
9+
'es6-promise',
10+
'firebase/app',
11+
'firebase/database',
12+
'vue',
13+
'vue-router',
14+
'vuex',
15+
'vuex-router-sync'
16+
]
17+
},
718
output: {
819
path: path.resolve(__dirname, '../dist'),
920
publicPath: '/dist/',
10-
filename: 'client-bundle.js'
21+
filename: '[name].[chunkhash].js'
1122
},
1223
module: {
1324
noParse: /es6-promise\.js$/, // avoid webpack shimming process

build/webpack.client.config.js

+14-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
const webpack = require('webpack')
22
const base = require('./webpack.base.config')
33
const vueConfig = require('./vue-loader.config')
4+
const HTMLPlugin = require('html-webpack-plugin')
5+
const ExtractTextPlugin = require('extract-text-webpack-plugin')
6+
const SWPrecachePlugin = require('sw-precache-webpack-plugin')
47

58
const config = Object.assign({}, base, {
69
resolve: {
@@ -12,16 +15,22 @@ const config = Object.assign({}, base, {
1215
// strip comments in Vue code
1316
new webpack.DefinePlugin({
1417
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
18+
}),
19+
// extract vendor chunks for better caching
20+
new webpack.optimize.CommonsChunkPlugin({
21+
name: 'vendor',
22+
filename: '[name].[chunkhash].js'
23+
}),
24+
// generate output HTML
25+
new HTMLPlugin({
26+
template: 'src/index.template.html'
1527
})
1628
])
1729
})
1830

1931
if (process.env.NODE_ENV === 'production') {
2032
// Use ExtractTextPlugin to extract CSS into a single file
21-
// so it's applied on initial render
22-
const ExtractTextPlugin = require('extract-text-webpack-plugin')
23-
const SWPrecachePlugin = require('sw-precache-webpack-plugin')
24-
33+
// so it's applied on initial render.
2534
// vueConfig is already included in the config via LoaderOptionsPlugin
2635
// here we overwrite the loader config for <style lang="stylus">
2736
// so they are extracted.
@@ -33,7 +42,7 @@ if (process.env.NODE_ENV === 'production') {
3342
}
3443

3544
config.plugins.push(
36-
new ExtractTextPlugin('styles.css'),
45+
new ExtractTextPlugin('styles.[hash].css'),
3746
// this is needed in webpack 2 for minifying CSS
3847
new webpack.LoaderOptionsPlugin({
3948
minimize: true

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
"es6-promise": "^3.2.1",
2020
"express": "^4.14.0",
2121
"firebase": "^3.4.1",
22+
"html-webpack-plugin": "^2.24.1",
2223
"lru-cache": "^4.0.1",
2324
"serialize-javascript": "^1.3.0",
2425
"serve-favicon": "^2.3.0",
25-
"sw-precache-webpack-plugin": "^0.5.1",
2626
"vue": "^2.0.0",
2727
"vue-router": "^2.0.0",
2828
"vue-server-renderer": "^2.0.0",
@@ -31,14 +31,15 @@
3131
},
3232
"devDependencies": {
3333
"autoprefixer": "^6.4.0",
34-
"cross-env": "^2.0.0",
35-
"css-loader": "^0.25.0",
3634
"buble": "^0.14.2",
3735
"buble-loader": "^0.3.2",
36+
"cross-env": "^2.0.0",
37+
"css-loader": "^0.25.0",
3838
"extract-text-webpack-plugin": "^2.0.0-beta.3",
3939
"file-loader": "^0.9.0",
4040
"stylus": "^0.54.5",
4141
"stylus-loader": "^2.1.2",
42+
"sw-precache-webpack-plugin": "^0.5.1",
4243
"url-loader": "^0.5.7",
4344
"vue-loader": "^9.7.0",
4445
"webpack": "^2.1.0-beta.25",

server.js

+16-11
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ const isProd = process.env.NODE_ENV === 'production'
33

44
const fs = require('fs')
55
const path = require('path')
6-
const resolve = file => path.resolve(__dirname, file)
76
const express = require('express')
87
const favicon = require('serve-favicon')
9-
const serialize = require('serialize-javascript')
108
const compression = require('compression')
9+
const serialize = require('serialize-javascript')
10+
const resolve = file => path.resolve(__dirname, file)
1111

1212
// https://github.com/vuejs/vue/blob/next/packages/vue-server-renderer/README.md#why-use-bundlerenderer
1313
const createBundleRenderer = require('vue-server-renderer').createBundleRenderer
@@ -16,13 +16,12 @@ const app = express()
1616

1717
// parse index.html template
1818
const html = (() => {
19-
const template = fs.readFileSync(resolve('./index.html'), 'utf-8')
20-
const i = template.indexOf('{{ APP }}')
21-
// styles are injected dynamically via vue-style-loader in development
22-
const style = isProd ? '<link rel="stylesheet" href="/dist/styles.css">' : ''
19+
const contentMarker = '<!-- APP -->'
20+
const template = fs.readFileSync(resolve('./dist/index.html'), 'utf-8')
21+
const i = template.indexOf(contentMarker)
2322
return {
24-
head: template.slice(0, i).replace('{{ STYLE }}', style),
25-
tail: template.slice(i + '{{ APP }}'.length)
23+
head: template.slice(0, i),
24+
tail: template.slice(i + contentMarker.length)
2625
}
2726
})()
2827

@@ -47,8 +46,8 @@ function createRenderer (bundle) {
4746
})
4847
}
4948

50-
app.use(compression({threshold: 0}))
51-
app.use('/dist', express.static(resolve('./dist')))
49+
app.use(compression({ threshold: 0 }))
50+
app.use('/dist', express.static(resolve('./dist'), { maxAge: 60 * 60 * 24 * 30 }))
5251
app.use(favicon(resolve('./src/assets/logo.png')))
5352

5453
app.get('*', (req, res) => {
@@ -61,7 +60,9 @@ app.get('*', (req, res) => {
6160
const context = { url: req.url }
6261
const renderStream = renderer.renderToStream(context)
6362

64-
res.write(html.head)
63+
renderStream.once('data', () => {
64+
res.write(html.head)
65+
})
6566

6667
renderStream.on('data', chunk => {
6768
res.write(chunk)
@@ -81,6 +82,10 @@ app.get('*', (req, res) => {
8182
})
8283

8384
renderStream.on('error', err => {
85+
if (err && err.code === '404') {
86+
res.status(404).end('404 | Page Not Found')
87+
return
88+
}
8489
// Render Error Page or Redirect
8590
res.status(500).end('Internal Error 500')
8691
console.error(`error during render : ${req.url}`)

index.html renamed to src/index.template.html

+1-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
<meta name="mobile-web-app-capable" content="yes">
77
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
88
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,700' rel='stylesheet' type='text/css'>
9-
{{ STYLE }}
109
</head>
1110
<body>
12-
{{ APP }}
13-
<script src="/dist/client-bundle.js"></script>
11+
<!-- APP -->
1412
</body>
1513
</html>

src/router/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ export default new Router({
1818
{ path: '/job/:page(\\d+)?', component: createListView('job') },
1919
{ path: '/item/:id(\\d+)', component: ItemView },
2020
{ path: '/user/:id', component: UserView },
21-
{ path: '*', redirect: '/top' }
21+
{ path: '/', redirect: '/top' }
2222
]
2323
})

src/server-entry.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,22 @@ const isDev = process.env.NODE_ENV !== 'production'
88
// Since data fetching is async, this function is expected to
99
// return a Promise that resolves to the app instance.
1010
export default context => {
11+
const s = isDev && Date.now()
12+
1113
// set router's location
1214
router.push(context.url)
15+
const matchedComponents = router.getMatchedComponents()
1316

14-
const s = isDev && Date.now()
17+
// no matched routes
18+
if (!matchedComponents.length) {
19+
return Promise.reject({ code: '404' })
20+
}
1521

1622
// Call preFetch hooks on components matched by the route.
1723
// A preFetch hook dispatches a store action and returns a Promise,
1824
// which is resolved when the action is complete and store state has been
1925
// updated.
20-
return Promise.all(router.getMatchedComponents().map(component => {
26+
return Promise.all(matchedComponents.map(component => {
2127
if (component.preFetch) {
2228
return component.preFetch(store)
2329
}

0 commit comments

Comments
 (0)