mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
Merge branch 'dev' of github.com:nuxt/nuxt.js into dev
This commit is contained in:
commit
eedd1137b6
1
lib/app/empty.js
Normal file
1
lib/app/empty.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
// This file is intentially left empty for noop aliases
|
@ -9,40 +9,16 @@ import Nuxt from './components/nuxt.vue'
|
|||||||
import App from '<%= appPath %>'
|
import App from '<%= appPath %>'
|
||||||
import { getContext } from './utils'
|
import { getContext } from './utils'
|
||||||
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
||||||
|
<% plugins.forEach(plugin => { %>import <%= plugin.name %> from '<%= plugin.name %>'
|
||||||
if (process.browser) {
|
|
||||||
// window.onNuxtReady(() => console.log('Ready')) hook
|
|
||||||
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
|
||||||
window._nuxtReadyCbs = []
|
|
||||||
window.onNuxtReady = function (cb) {
|
|
||||||
window._nuxtReadyCbs.push(cb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<% if (plugins.filter(p => p.ssr).length) { %>
|
|
||||||
// Require plugins
|
|
||||||
<% plugins.filter(p => p.ssr).forEach(plugin => { %>
|
|
||||||
let <%= plugin.name %> = require('<%= relativeToBuild(plugin.src) %>')
|
|
||||||
<%= plugin.name %> = <%= plugin.name %>.default || <%= plugin.name %>
|
|
||||||
<% }) %>
|
<% }) %>
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<% if (plugins.filter(p => !p.ssr).length) { %>
|
|
||||||
// Require browser-only plugins
|
|
||||||
if (process.browser) {
|
|
||||||
<% plugins.filter(p => !p.ssr).forEach(plugin => { %>
|
|
||||||
let <%= plugin.name %> = require('<%= relativeToBuild(plugin.src) %>')
|
|
||||||
<%= plugin.name %> = <%= plugin.name %>.default || <%= plugin.name %>
|
|
||||||
<% }) %>
|
|
||||||
}
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
|
|
||||||
// Component: <nuxt-child>
|
// Component: <nuxt-child>
|
||||||
Vue.component(NuxtChild.name, NuxtChild)
|
Vue.component(NuxtChild.name, NuxtChild)
|
||||||
|
|
||||||
// Component: <nuxt-link>
|
// Component: <nuxt-link>
|
||||||
Vue.component(NuxtLink.name, NuxtLink)
|
Vue.component(NuxtLink.name, NuxtLink)
|
||||||
// Component: <nuxt>
|
|
||||||
|
// Component: <nuxt>`
|
||||||
Vue.component(Nuxt.name, Nuxt)
|
Vue.component(Nuxt.name, Nuxt)
|
||||||
|
|
||||||
// vue-meta configuration
|
// vue-meta configuration
|
||||||
@ -132,24 +108,17 @@ async function createApp (ssrContext) {
|
|||||||
route: router.currentRoute,
|
route: router.currentRoute,
|
||||||
next,
|
next,
|
||||||
error: app._nuxt.error.bind(app),
|
error: app._nuxt.error.bind(app),
|
||||||
<% if(store) { %> store,<% } %>
|
<% if(store) { %>store,<% } %>
|
||||||
req: ssrContext ? ssrContext.req : undefined,
|
req: ssrContext ? ssrContext.req : undefined,
|
||||||
res: ssrContext ? ssrContext.res : undefined,
|
res: ssrContext ? ssrContext.res : undefined,
|
||||||
}, app)
|
}, app)
|
||||||
|
|
||||||
<% if (plugins.filter(p => p.ssr).length) { %>
|
|
||||||
<% plugins.filter(p => p.ssr).forEach(plugin => { %>
|
<% plugins.filter(p => p.ssr).forEach(plugin => { %>
|
||||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)
|
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)<% }) %>
|
||||||
<% }) %>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<% if (plugins.filter(p => !p.ssr).length) { %>
|
<% if (plugins.filter(p => !p.ssr).length) { %>
|
||||||
if (process.browser) {
|
if (process.browser) { <% plugins.filter(p => !p.ssr).forEach(plugin => { %>
|
||||||
<% plugins.filter(p => !p.ssr).forEach(plugin => { %>
|
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)<% }) %>
|
||||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)
|
}<% } %>
|
||||||
<% }) %>
|
|
||||||
}
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
app,
|
app,
|
||||||
|
@ -2,6 +2,15 @@ import Vue from 'vue'
|
|||||||
|
|
||||||
const noopData = () => ({})
|
const noopData = () => ({})
|
||||||
|
|
||||||
|
// window.onNuxtReady(() => console.log('Ready')) hook
|
||||||
|
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
||||||
|
if (process.browser) {
|
||||||
|
window._nuxtReadyCbs = []
|
||||||
|
window.onNuxtReady = function (cb) {
|
||||||
|
window._nuxtReadyCbs.push(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function applyAsyncData (Component, asyncData = {}) {
|
export function applyAsyncData (Component, asyncData = {}) {
|
||||||
const ComponentData = Component.options.data || noopData
|
const ComponentData = Component.options.data || noopData
|
||||||
// Prevent calling this method for each request on SSR context
|
// Prevent calling this method for each request on SSR context
|
||||||
|
@ -49,6 +49,14 @@ export default class Builder extends Tapable {
|
|||||||
this._buildStatus = STATUS.INITIAL
|
this._buildStatus = STATUS.INITIAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get plugins () {
|
||||||
|
return this.options.plugins.map((p, i) => {
|
||||||
|
if (typeof p === 'string') p = { src: p }
|
||||||
|
p.src = r(this.options.srcDir, p.src)
|
||||||
|
return { src: p.src, ssr: (p.ssr !== false), name: `plugin${i}` }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async build () {
|
async build () {
|
||||||
// Avoid calling build() method multiple times when dev:true
|
// Avoid calling build() method multiple times when dev:true
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
@ -119,6 +127,7 @@ export default class Builder extends Tapable {
|
|||||||
'router.js',
|
'router.js',
|
||||||
'server.js',
|
'server.js',
|
||||||
'utils.js',
|
'utils.js',
|
||||||
|
'empty.js',
|
||||||
'components/nuxt-error.vue',
|
'components/nuxt-error.vue',
|
||||||
'components/nuxt-loading.vue',
|
'components/nuxt-loading.vue',
|
||||||
'components/nuxt-child.js',
|
'components/nuxt-child.js',
|
||||||
@ -137,11 +146,7 @@ export default class Builder extends Tapable {
|
|||||||
middleware: fs.existsSync(join(this.options.srcDir, 'middleware')),
|
middleware: fs.existsSync(join(this.options.srcDir, 'middleware')),
|
||||||
store: this.options.store,
|
store: this.options.store,
|
||||||
css: this.options.css,
|
css: this.options.css,
|
||||||
plugins: this.options.plugins.map((p, i) => {
|
plugins: this.plugins,
|
||||||
if (typeof p === 'string') p = { src: p }
|
|
||||||
p.src = r(this.options.srcDir, p.src)
|
|
||||||
return { src: p.src, ssr: (p.ssr !== false), name: `plugin${i}` }
|
|
||||||
}),
|
|
||||||
appPath: './App.vue',
|
appPath: './App.vue',
|
||||||
layouts: Object.assign({}, this.options.layouts),
|
layouts: Object.assign({}, this.options.layouts),
|
||||||
loading: typeof this.options.loading === 'string' ? this.relativeToBuild(this.options.srcDir, this.options.loading) : this.options.loading,
|
loading: typeof this.options.loading === 'string' ? this.relativeToBuild(this.options.srcDir, this.options.loading) : this.options.loading,
|
||||||
@ -264,15 +269,33 @@ export default class Builder extends Tapable {
|
|||||||
|
|
||||||
async webpackBuild () {
|
async webpackBuild () {
|
||||||
debug('Building files...')
|
debug('Building files...')
|
||||||
let compilersOptions = []
|
const compilersOptions = []
|
||||||
|
|
||||||
// Client
|
// Client
|
||||||
let clientConfig = clientWebpackConfig.call(this)
|
const clientConfig = clientWebpackConfig.call(this)
|
||||||
compilersOptions.push(clientConfig)
|
compilersOptions.push(clientConfig)
|
||||||
|
|
||||||
// Server
|
// Server
|
||||||
let serverConfig = serverWebpackConfig.call(this)
|
const serverConfig = serverWebpackConfig.call(this)
|
||||||
compilersOptions.push(serverConfig)
|
if (this.options.build.ssr) {
|
||||||
|
compilersOptions.push(serverConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias plugins to their real path
|
||||||
|
this.plugins.forEach(p => {
|
||||||
|
const src = this.relativeToBuild(p.src)
|
||||||
|
|
||||||
|
// Client config
|
||||||
|
if (!clientConfig.resolve.alias[p.name]) {
|
||||||
|
clientConfig.resolve.alias[p.name] = src
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server config
|
||||||
|
if (!serverConfig.resolve.alias[p.name]) {
|
||||||
|
// Alias to noop for ssr:false plugins
|
||||||
|
serverConfig.resolve.alias[p.name] = p.ssr ? src : './empty.js'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Simulate webpack multi compiler interface
|
// Simulate webpack multi compiler interface
|
||||||
// Separate compilers are simpler, safer and faster
|
// Separate compilers are simpler, safer and faster
|
||||||
|
@ -60,11 +60,23 @@ export default function webpackClientConfig () {
|
|||||||
config.plugins = []
|
config.plugins = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate output HTML
|
// Generate output HTML for SSR
|
||||||
|
if (this.options.build.ssr) {
|
||||||
|
config.plugins.push(
|
||||||
|
new HTMLPlugin({
|
||||||
|
filename: 'index.ssr.html',
|
||||||
|
template: this.options.appTemplatePath,
|
||||||
|
inject: false // Resources will be injected using bundleRenderer
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate output HTML for SPA
|
||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
new HTMLPlugin({
|
new HTMLPlugin({
|
||||||
|
filename: 'index.spa.html',
|
||||||
template: this.options.appTemplatePath,
|
template: this.options.appTemplatePath,
|
||||||
inject: this.options.ssr === false,
|
inject: true,
|
||||||
chunksSortMode: 'dependency'
|
chunksSortMode: 'dependency'
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -46,16 +46,57 @@ export default function Options (_options) {
|
|||||||
options.store = true
|
options.store = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve mode
|
||||||
|
let mode = options.mode
|
||||||
|
if (typeof mode === 'function') {
|
||||||
|
mode = mode()
|
||||||
|
}
|
||||||
|
if (typeof mode === 'string') {
|
||||||
|
mode = Modes[mode]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply mode
|
||||||
|
_.defaultsDeep(options, mode)
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Modes = {
|
||||||
|
universal: {
|
||||||
|
build: {
|
||||||
|
ssr: true
|
||||||
|
},
|
||||||
|
render: {
|
||||||
|
ssr: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
spa: {
|
||||||
|
build: {
|
||||||
|
ssr: false
|
||||||
|
},
|
||||||
|
render: {
|
||||||
|
ssr: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
static: {
|
||||||
|
build: {
|
||||||
|
ssr: true
|
||||||
|
},
|
||||||
|
render: {
|
||||||
|
ssr: 'static'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const defaultOptions = {
|
export const defaultOptions = {
|
||||||
dev: (process.env.NODE_ENV !== 'production'),
|
mode: 'universal',
|
||||||
|
dev: process.env.NODE_ENV !== 'production',
|
||||||
buildDir: '.nuxt',
|
buildDir: '.nuxt',
|
||||||
nuxtAppDir: resolve(__dirname, '../lib/app/'), // Relative to dist
|
nuxtAppDir: resolve(__dirname, '../lib/app/'), // Relative to dist
|
||||||
build: {
|
build: {
|
||||||
analyze: false,
|
analyze: false,
|
||||||
extractCSS: false,
|
extractCSS: false,
|
||||||
|
ssr: undefined,
|
||||||
publicPath: '/_nuxt/',
|
publicPath: '/_nuxt/',
|
||||||
filenames: {
|
filenames: {
|
||||||
css: 'common.[chunkhash].css',
|
css: 'common.[chunkhash].css',
|
||||||
@ -132,10 +173,10 @@ export const defaultOptions = {
|
|||||||
scrollBehavior: null,
|
scrollBehavior: null,
|
||||||
fallback: false
|
fallback: false
|
||||||
},
|
},
|
||||||
ssr: true,
|
|
||||||
render: {
|
render: {
|
||||||
bundleRenderer: {},
|
bundleRenderer: {},
|
||||||
resourceHints: true,
|
resourceHints: true,
|
||||||
|
ssr: undefined,
|
||||||
http2: {
|
http2: {
|
||||||
push: false
|
push: false
|
||||||
},
|
},
|
||||||
|
@ -42,7 +42,8 @@ export default class Renderer extends Tapable {
|
|||||||
this.resources = {
|
this.resources = {
|
||||||
clientManifest: null,
|
clientManifest: null,
|
||||||
serverBundle: null,
|
serverBundle: null,
|
||||||
appTemplate: null,
|
ssrTemplate: null,
|
||||||
|
spaTemplate: null,
|
||||||
errorTemplate: parseTemplate('<pre>{{ stack }}</pre>') // Will be loaded on ready
|
errorTemplate: parseTemplate('<pre>{{ stack }}</pre>') // Will be loaded on ready
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +122,18 @@ export default class Renderer extends Tapable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get noSSR () {
|
get noSSR () {
|
||||||
return this.options.ssr === false
|
return this.options.render.ssr === false
|
||||||
|
}
|
||||||
|
|
||||||
|
get staticSSR () {
|
||||||
|
return this.options.render.ssr === 'static'
|
||||||
|
}
|
||||||
|
|
||||||
|
get isReady () {
|
||||||
|
if (this.noSSR) {
|
||||||
|
return this.resources.spaTemplate
|
||||||
|
}
|
||||||
|
return this.bundleRenderer && this.resources.ssrTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
createRenderer () {
|
createRenderer () {
|
||||||
@ -297,7 +309,7 @@ export default class Renderer extends Tapable {
|
|||||||
|
|
||||||
async renderRoute (url, context = {}) {
|
async renderRoute (url, context = {}) {
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (!(this.noSSR || this.bundleRenderer) || !this.resources.appTemplate) {
|
if (!this.isReady) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
setTimeout(() => resolve(this.renderRoute(url, context)), 1000)
|
setTimeout(() => resolve(this.renderRoute(url, context)), 1000)
|
||||||
})
|
})
|
||||||
@ -315,7 +327,7 @@ export default class Renderer extends Tapable {
|
|||||||
let APP = '<div id="__nuxt"></div>'
|
let APP = '<div id="__nuxt"></div>'
|
||||||
let HEAD = ''
|
let HEAD = ''
|
||||||
|
|
||||||
let html = this.resources.appTemplate({
|
let html = this.resources.spaTemplate({
|
||||||
HTML_ATTRS: '',
|
HTML_ATTRS: '',
|
||||||
BODY_ATTRS: '',
|
BODY_ATTRS: '',
|
||||||
HEAD,
|
HEAD,
|
||||||
@ -340,15 +352,19 @@ export default class Renderer extends Tapable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let resourceHints = ''
|
let resourceHints = ''
|
||||||
if (this.options.render.resourceHints) {
|
|
||||||
resourceHints = context.renderResourceHints()
|
|
||||||
HEAD += resourceHints
|
|
||||||
}
|
|
||||||
HEAD += context.renderStyles()
|
|
||||||
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};</script>`
|
|
||||||
APP += context.renderScripts()
|
|
||||||
|
|
||||||
let html = this.resources.appTemplate({
|
if (!this.staticSSR) {
|
||||||
|
if (this.options.render.resourceHints) {
|
||||||
|
resourceHints = context.renderResourceHints()
|
||||||
|
HEAD += resourceHints
|
||||||
|
}
|
||||||
|
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};</script>`
|
||||||
|
APP += context.renderScripts()
|
||||||
|
}
|
||||||
|
|
||||||
|
HEAD += context.renderStyles()
|
||||||
|
|
||||||
|
let html = this.resources.ssrTemplate({
|
||||||
HTML_ATTRS: 'data-n-head-ssr ' + m.htmlAttrs.text(),
|
HTML_ATTRS: 'data-n-head-ssr ' + m.htmlAttrs.text(),
|
||||||
BODY_ATTRS: m.bodyAttrs.text(),
|
BODY_ATTRS: m.bodyAttrs.text(),
|
||||||
HEAD,
|
HEAD,
|
||||||
@ -422,8 +438,13 @@ const resourceMap = [
|
|||||||
transform: JSON.parse
|
transform: JSON.parse
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'appTemplate',
|
key: 'ssrTemplate',
|
||||||
fileName: 'index.html',
|
fileName: 'index.ssr.html',
|
||||||
|
transform: parseTemplate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'spaTemplate',
|
||||||
|
fileName: 'index.spa.html',
|
||||||
transform: parseTemplate
|
transform: parseTemplate
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -7,3 +7,5 @@ function $reverseStr (str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vue.prototype.$reverseStr = $reverseStr
|
Vue.prototype.$reverseStr = $reverseStr
|
||||||
|
|
||||||
|
export default undefined
|
||||||
|
7
test/fixtures/module/router.js
vendored
7
test/fixtures/module/router.js
vendored
@ -3,18 +3,21 @@ import Router from 'vue-router'
|
|||||||
|
|
||||||
Vue.use(Router)
|
Vue.use(Router)
|
||||||
|
|
||||||
|
const indexPage = () => import('~/views/index.vue').then(m => m.default || m)
|
||||||
|
const aboutPage = () => import('~/views/about.vue').then(m => m.default || m)
|
||||||
|
|
||||||
export function createRouter () {
|
export function createRouter () {
|
||||||
return new Router({
|
return new Router({
|
||||||
mode: 'history',
|
mode: 'history',
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: require('~/views/index.vue').default,
|
component: indexPage,
|
||||||
name: 'index'
|
name: 'index'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/about',
|
path: '/about',
|
||||||
component: require('~/views/about.vue').default,
|
component: aboutPage,
|
||||||
name: 'about'
|
name: 'about'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -90,7 +90,7 @@ test('/test/about-bis (added with extendRoutes)', async t => {
|
|||||||
|
|
||||||
test('Check stats.json generated by build.analyze', t => {
|
test('Check stats.json generated by build.analyze', t => {
|
||||||
const stats = require(resolve(__dirname, 'fixtures/with-config/.nuxt/dist/stats.json'))
|
const stats = require(resolve(__dirname, 'fixtures/with-config/.nuxt/dist/stats.json'))
|
||||||
t.is(stats.assets.length, 26)
|
t.is(stats.assets.length, 27)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Check /test.txt with custom serve-static options', async t => {
|
test('Check /test.txt with custom serve-static options', async t => {
|
||||||
|
Loading…
Reference in New Issue
Block a user