mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-27 08:02:01 +00:00
Use HTML template + optimise cache control + gzip
This commit is contained in:
parent
f5300e4e67
commit
10c4b1550f
16
lib/build.js
16
lib/build.js
@ -51,9 +51,9 @@ const defaults = {
|
|||||||
analyze: false,
|
analyze: false,
|
||||||
publicPath: '/_nuxt/',
|
publicPath: '/_nuxt/',
|
||||||
filenames: {
|
filenames: {
|
||||||
css: 'style.css',
|
css: 'style.[hash].css',
|
||||||
vendor: 'vendor.bundle.js',
|
vendor: 'vendor.bundle.[hash].js',
|
||||||
app: 'nuxt.bundle.js'
|
app: 'nuxt.bundle.[chunkhash].js'
|
||||||
},
|
},
|
||||||
vendor: [],
|
vendor: [],
|
||||||
loaders: [],
|
loaders: [],
|
||||||
@ -377,6 +377,16 @@ function createWebpackMiddleware () {
|
|||||||
this.webpackHotMiddleware = pify(require('webpack-hot-middleware')(clientCompiler, {
|
this.webpackHotMiddleware = pify(require('webpack-hot-middleware')(clientCompiler, {
|
||||||
log: () => {}
|
log: () => {}
|
||||||
}))
|
}))
|
||||||
|
clientCompiler.plugin('done', () => {
|
||||||
|
const fs = this.webpackDevMiddleware.fileSystem
|
||||||
|
const filePath = join(clientConfig.output.path, 'index.html')
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
const template = fs.readFileSync(filePath, 'utf-8')
|
||||||
|
this.appTemplate = _.template(template, {
|
||||||
|
interpolate: /{{([\s\S]+?)}}/g
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function webpackWatchAndUpdate () {
|
function webpackWatchAndUpdate () {
|
||||||
|
33
lib/nuxt.js
33
lib/nuxt.js
@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import co from 'co'
|
import co from 'co'
|
||||||
|
import compression from 'compression'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import pify from 'pify'
|
import pify from 'pify'
|
||||||
import ansiHTML from 'ansi-html'
|
|
||||||
import serialize from 'serialize-javascript'
|
|
||||||
import Server from './server'
|
import Server from './server'
|
||||||
import * as build from './build'
|
import * as build from './build'
|
||||||
import * as render from './render'
|
import * as render from './render'
|
||||||
@ -13,7 +12,6 @@ import generate from './generate'
|
|||||||
import serveStatic from 'serve-static'
|
import serveStatic from 'serve-static'
|
||||||
import { resolve, join } from 'path'
|
import { resolve, join } from 'path'
|
||||||
import * as utils from './utils'
|
import * as utils from './utils'
|
||||||
utils.setAnsiColors(ansiHTML)
|
|
||||||
|
|
||||||
class Nuxt {
|
class Nuxt {
|
||||||
|
|
||||||
@ -63,27 +61,34 @@ class Nuxt {
|
|||||||
if (fs.existsSync(join(this.srcDir, 'middleware'))) {
|
if (fs.existsSync(join(this.srcDir, 'middleware'))) {
|
||||||
this.options.middleware = true
|
this.options.middleware = true
|
||||||
}
|
}
|
||||||
// Template
|
|
||||||
this.appTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'app.html'), 'utf8'), {
|
|
||||||
imports: { serialize }
|
|
||||||
})
|
|
||||||
this.errorTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'error.html'), 'utf8'), {
|
|
||||||
imports: {
|
|
||||||
ansiHTML,
|
|
||||||
encodeHtml: utils.encodeHtml
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// renderer used by Vue.js (via createBundleRenderer)
|
// renderer used by Vue.js (via createBundleRenderer)
|
||||||
this.renderer = null
|
this.renderer = null
|
||||||
// For serving static/ files to /
|
// For serving static/ files to /
|
||||||
this.serveStatic = pify(serveStatic(resolve(this.srcDir, 'static')))
|
this.serveStatic = pify(serveStatic(resolve(this.srcDir, 'static')))
|
||||||
// For serving .nuxt/dist/ files (only when build.publicPath is not an URL)
|
// For serving .nuxt/dist/ files (only when build.publicPath is not an URL)
|
||||||
this.serveStaticNuxt = pify(serveStatic(resolve(this.dir, '.nuxt', 'dist')))
|
this.serveStaticNuxt = pify(serveStatic(resolve(this.dir, '.nuxt', 'dist'), {
|
||||||
|
maxAge: (this.dev ? 0 : '1y') // 1 year in production
|
||||||
|
}))
|
||||||
|
// gzip for production
|
||||||
|
if (!this.dev) {
|
||||||
|
this.gzipMiddleware = pify(compression({
|
||||||
|
threshold: 0
|
||||||
|
}))
|
||||||
|
}
|
||||||
// Add this.Server Class
|
// Add this.Server Class
|
||||||
this.Server = Server
|
this.Server = Server
|
||||||
// Add this.build
|
// Add this.build
|
||||||
build.options.call(this) // Add build options
|
build.options.call(this) // Add build options
|
||||||
this.build = () => co(build.build.bind(this))
|
this.build = () => co(build.build.bind(this))
|
||||||
|
// Template
|
||||||
|
if (!this.dev && this.renderer) {
|
||||||
|
this.appTemplate = _.template(fs.readFileSync(resolve(this.dir, '.nuxt', 'dist', 'index.html'), 'utf8'), {
|
||||||
|
interpolate: /{{([\s\S]+?)}}/g
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.errorTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'error.html'), 'utf8'), {
|
||||||
|
interpolate: /{{([\s\S]+?)}}/g
|
||||||
|
})
|
||||||
// Add this.render and this.renderRoute
|
// Add this.render and this.renderRoute
|
||||||
this.render = render.render.bind(this)
|
this.render = render.render.bind(this)
|
||||||
this.renderRoute = render.renderRoute.bind(this)
|
this.renderRoute = render.renderRoute.bind(this)
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const debug = require('debug')('nuxt:render')
|
const debug = require('debug')('nuxt:render')
|
||||||
|
import ansiHTML from 'ansi-html'
|
||||||
import co from 'co'
|
import co from 'co'
|
||||||
import { urlJoin, getContext } from './utils'
|
import serialize from 'serialize-javascript'
|
||||||
|
import { getContext, setAnsiColors, encodeHtml } from './utils'
|
||||||
// force blue color
|
// force blue color
|
||||||
debug.color = 4
|
debug.color = 4
|
||||||
|
setAnsiColors(ansiHTML)
|
||||||
|
|
||||||
export function render (req, res) {
|
export function render (req, res) {
|
||||||
if (!this.renderer && !this.dev) {
|
if (!this.renderer && !this.dev) {
|
||||||
@ -12,7 +15,7 @@ export function render (req, res) {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (!this.renderer) {
|
if (!this.renderer || !this.appTemplate) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve(this.render(req, res))
|
resolve(this.render(req, res))
|
||||||
@ -27,6 +30,8 @@ export function render (req, res) {
|
|||||||
// Call webpack middleware only in development
|
// Call webpack middleware only in development
|
||||||
yield self.webpackDevMiddleware(req, res)
|
yield self.webpackDevMiddleware(req, res)
|
||||||
yield self.webpackHotMiddleware(req, res)
|
yield self.webpackHotMiddleware(req, res)
|
||||||
|
} else {
|
||||||
|
yield self.gzipMiddleware(req, res)
|
||||||
}
|
}
|
||||||
// If base in req.url, remove it for the middleware and vue-router
|
// If base in req.url, remove it for the middleware and vue-router
|
||||||
if (self.options.router.base !== '/' && req.url.indexOf(self.options.router.base) === 0) {
|
if (self.options.router.base !== '/' && req.url.indexOf(self.options.router.base) === 0) {
|
||||||
@ -62,8 +67,14 @@ export function render (req, res) {
|
|||||||
return html
|
return html
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
const html = this.errorTemplate({
|
||||||
|
error: err,
|
||||||
|
stack: ansiHTML(encodeHtml(err.stack))
|
||||||
|
})
|
||||||
res.statusCode = 500
|
res.statusCode = 500
|
||||||
res.end(this.errorTemplate({ err }), 'utf8')
|
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
res.setHeader('Content-Length', Buffer.byteLength(html))
|
||||||
|
res.end(html, 'utf8')
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -76,21 +87,21 @@ export function renderRoute (url, context = {}) {
|
|||||||
// Call rendertoSting from the bundleRenderer and generate the HTML (will update the context as well)
|
// Call rendertoSting from the bundleRenderer and generate the HTML (will update the context as well)
|
||||||
const self = this
|
const self = this
|
||||||
return co(function * () {
|
return co(function * () {
|
||||||
let app = yield self.renderToString(context)
|
let APP = yield self.renderToString(context)
|
||||||
if (!context.nuxt.serverRendered) {
|
if (!context.nuxt.serverRendered) {
|
||||||
app = '<div id="__nuxt"></div>'
|
APP = '<div id="__nuxt"></div>'
|
||||||
}
|
}
|
||||||
const publicPath = self.options.build.publicPath.indexOf('http') === 0 ? self.options.build.publicPath : urlJoin(self.options.router.base, self.options.build.publicPath)
|
const m = context.meta.inject()
|
||||||
|
let HEAD = m.meta.text() + m.title.text() + m.link.text() + m.style.text() + m.script.text() + m.noscript.text()
|
||||||
|
if (self.options.router.base !== '/') {
|
||||||
|
HEAD += `<base href="${self.options.router.base}">`
|
||||||
|
}
|
||||||
|
APP += `<script type="text/javascript" defer>window.__NUXT__=${serialize(context.nuxt, { isJSON: true })}</script>`
|
||||||
const html = self.appTemplate({
|
const html = self.appTemplate({
|
||||||
dev: self.dev, // Use to add the extracted CSS <link> in production
|
HTML_ATTRS: 'n-head-ssr ' + m.htmlAttrs.text(),
|
||||||
baseUrl: self.options.router.base,
|
BODY_ATTRS: m.bodyAttrs.text(),
|
||||||
APP: app,
|
HEAD,
|
||||||
context: context,
|
APP
|
||||||
files: {
|
|
||||||
css: urlJoin(publicPath, self.options.build.filenames.css),
|
|
||||||
vendor: urlJoin(publicPath, self.options.build.filenames.vendor),
|
|
||||||
app: urlJoin(publicPath, self.options.build.filenames.app)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
html,
|
html,
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<% var m = context.meta.inject() %><!DOCTYPE html>
|
|
||||||
<html n-head-ssr <%= m.htmlAttrs.text() %>>
|
|
||||||
<head>
|
|
||||||
<%= m.meta.text() %>
|
|
||||||
<%= m.title.text() %>
|
|
||||||
<%= m.link.text() %>
|
|
||||||
<%= m.style.text() %>
|
|
||||||
<%= m.script.text() %>
|
|
||||||
<%= m.noscript.text() %>
|
|
||||||
<% if (baseUrl !== '/') { %><base href="<%= baseUrl %>"><% } %>
|
|
||||||
<% if (!dev) { %><link rel="stylesheet" href="<%= files.css %>"><% } %>
|
|
||||||
</head>
|
|
||||||
<body <%= m.bodyAttrs.text() %>>
|
|
||||||
<%= APP %>
|
|
||||||
<script type="text/javascript" defer>window.__NUXT__=<%= serialize(context.nuxt, { isJSON: true }) %></script>
|
|
||||||
<script src="<%= files.vendor %>" defer></script>
|
|
||||||
<script src="<%= files.app %>" defer></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
9
lib/views/app.template.html
Normal file
9
lib/views/app.template.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html {{ HTML_ATTRS }}>
|
||||||
|
<head>
|
||||||
|
{{ HEAD }}
|
||||||
|
</head>
|
||||||
|
<body {{ BODY_ATTRS }}>
|
||||||
|
{{ APP }}
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -2,10 +2,10 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Vue.js error</title>
|
<title>Nuxt.js error</title>
|
||||||
</head>
|
</head>
|
||||||
<body style="background-color: #a6004c;color: #efe;font-family: monospace;">
|
<body style="background-color: #a6004c;color: #efe;font-family: monospace;">
|
||||||
<h2>Vue.js error</h2>
|
<h2>Nuxt.js error</h2>
|
||||||
<pre><%= ansiHTML(encodeHtml(err.stack)) %></pre>
|
<pre>{{ stack }}</pre>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
import { each } from 'lodash'
|
import { each } from 'lodash'
|
||||||
import webpack from 'webpack'
|
import webpack from 'webpack'
|
||||||
|
import HTMLPlugin from 'html-webpack-plugin'
|
||||||
|
import ScriptExtHtmlWebpackPlugin from 'script-ext-html-webpack-plugin'
|
||||||
|
import PreloadWebpackPlugin from 'preload-webpack-plugin'
|
||||||
import ExtractTextPlugin from 'extract-text-webpack-plugin'
|
import ExtractTextPlugin from 'extract-text-webpack-plugin'
|
||||||
import ProgressBarPlugin from 'progress-bar-webpack-plugin'
|
import ProgressBarPlugin from 'progress-bar-webpack-plugin'
|
||||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
|
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
|
||||||
@ -40,7 +43,7 @@ export default function () {
|
|||||||
})
|
})
|
||||||
// Webpack plugins
|
// Webpack plugins
|
||||||
config.plugins = (config.plugins || []).concat([
|
config.plugins = (config.plugins || []).concat([
|
||||||
// strip comments in Vue code
|
// Strip comments in Vue code
|
||||||
new webpack.DefinePlugin(Object.assign(env, {
|
new webpack.DefinePlugin(Object.assign(env, {
|
||||||
'process.env.NODE_ENV': JSON.stringify(this.dev ? 'development' : 'production'),
|
'process.env.NODE_ENV': JSON.stringify(this.dev ? 'development' : 'production'),
|
||||||
'process.BROWSER_BUILD': true,
|
'process.BROWSER_BUILD': true,
|
||||||
@ -50,6 +53,18 @@ export default function () {
|
|||||||
new webpack.optimize.CommonsChunkPlugin({
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
name: 'vendor',
|
name: 'vendor',
|
||||||
filename: this.options.build.filenames.vendor
|
filename: this.options.build.filenames.vendor
|
||||||
|
}),
|
||||||
|
// Generate output HTML
|
||||||
|
new HTMLPlugin({
|
||||||
|
template: resolve(__dirname, 'views/app.template.html')
|
||||||
|
}),
|
||||||
|
// Add defer to scripts
|
||||||
|
new ScriptExtHtmlWebpackPlugin({
|
||||||
|
defaultAttribute: 'defer'
|
||||||
|
}),
|
||||||
|
// Add prefetch code-splitted routes
|
||||||
|
new PreloadWebpackPlugin({
|
||||||
|
rel: 'prefetch'
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
"chalk": "^1.1.3",
|
"chalk": "^1.1.3",
|
||||||
"chokidar": "^1.6.1",
|
"chokidar": "^1.6.1",
|
||||||
"co": "^4.6.0",
|
"co": "^4.6.0",
|
||||||
|
"compression": "^1.6.2",
|
||||||
"css-loader": "^0.26.1",
|
"css-loader": "^0.26.1",
|
||||||
"debug": "^2.6.1",
|
"debug": "^2.6.1",
|
||||||
"extract-text-webpack-plugin": "2.0.0-beta.4",
|
"extract-text-webpack-plugin": "2.0.0-beta.4",
|
||||||
@ -63,13 +64,16 @@
|
|||||||
"glob": "^7.1.1",
|
"glob": "^7.1.1",
|
||||||
"hash-sum": "^1.0.2",
|
"hash-sum": "^1.0.2",
|
||||||
"html-minifier": "^3.3.1",
|
"html-minifier": "^3.3.1",
|
||||||
|
"html-webpack-plugin": "^2.28.0",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"lru-cache": "^4.0.2",
|
"lru-cache": "^4.0.2",
|
||||||
"memory-fs": "^0.4.1",
|
"memory-fs": "^0.4.1",
|
||||||
"path-to-regexp": "^1.7.0",
|
"path-to-regexp": "^1.7.0",
|
||||||
"pify": "^2.3.0",
|
"pify": "^2.3.0",
|
||||||
"post-compile-webpack-plugin": "^0.1.1",
|
"post-compile-webpack-plugin": "^0.1.1",
|
||||||
|
"preload-webpack-plugin": "^1.2.0",
|
||||||
"progress-bar-webpack-plugin": "^1.9.3",
|
"progress-bar-webpack-plugin": "^1.9.3",
|
||||||
|
"script-ext-html-webpack-plugin": "^1.7.1",
|
||||||
"serialize-javascript": "^1.3.0",
|
"serialize-javascript": "^1.3.0",
|
||||||
"serve-static": "^1.11.2",
|
"serve-static": "^1.11.2",
|
||||||
"url-loader": "^0.5.7",
|
"url-loader": "^0.5.7",
|
||||||
|
Loading…
Reference in New Issue
Block a user