feat(modern): auto detect modern mode (#4422)

This commit is contained in:
Clark Du 2018-11-26 22:49:47 +00:00 committed by GitHub
parent 8cc954d48e
commit fe492d8363
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 101 additions and 18 deletions

View File

@ -12,7 +12,7 @@ export default () => ({
// Mode // Mode
mode: 'universal', mode: 'universal',
modern: false, modern: undefined,
// Globals // Globals
globalName: `nuxt`, globalName: `nuxt`,

View File

@ -253,10 +253,6 @@ export function getNuxtConfig(_options) {
} }
defaultsDeep(options, modePreset || options.modes.universal) defaultsDeep(options, modePreset || options.modes.universal)
if (options.modern === true) {
options.modern = 'server'
}
// If no server-side rendering, add appear true transition // If no server-side rendering, add appear true transition
/* istanbul ignore if */ /* istanbul ignore if */
if (options.render.ssr === false && options.transition) { if (options.render.ssr === false && options.transition) {

View File

@ -1,3 +1,5 @@
import chalk from 'chalk'
import consola from 'consola'
import { ModernBrowsers } from '@nuxt/common' import { ModernBrowsers } from '@nuxt/common'
import { matchesUA } from 'browserslist-useragent' import { matchesUA } from 'browserslist-useragent'
@ -11,12 +13,33 @@ const isModernBrowser = (ua) => {
}) })
} }
export default function (req, res, next) { let detected = false
const detectModernBuild = ({ options, resources }) => {
if (detected === false && ![false, 'client', 'server'].includes(options.modern)) {
detected = true
if (resources.modernManifest) {
options.modern = options.render.ssr ? 'server' : 'client'
consola.info(`Modern bundles are detected. Modern mode (${chalk.green.bold(options.modern)}) is enabled now.`)
} else {
options.modern = false
}
}
}
const detectModernBrowser = (req, options) => {
if (options.modern === 'server') {
const { socket = {}, headers } = req const { socket = {}, headers } = req
if (socket.modernMode === undefined) { if (socket.modernMode === undefined) {
const ua = headers && headers['user-agent'] const ua = headers && headers['user-agent']
socket.modernMode = isModernBrowser(ua) socket.modernMode = isModernBrowser(ua)
} }
req.modernMode = socket.modernMode req.modernMode = socket.modernMode
}
}
export default ({ context }) => (req, res, next) => {
detectModernBuild(context)
detectModernBrowser(req, context.options)
next() next()
} }

View File

@ -10,7 +10,7 @@ import renderAndGetWindow from './jsdom'
import nuxtMiddleware from './middleware/nuxt' import nuxtMiddleware from './middleware/nuxt'
import errorMiddleware from './middleware/error' import errorMiddleware from './middleware/error'
import Listener from './listener' import Listener from './listener'
import modernMiddleware from './middleware/modern' import createModernMiddleware from './middleware/modern'
export default class Server { export default class Server {
constructor(nuxt) { constructor(nuxt) {
@ -79,12 +79,13 @@ export default class Server {
} }
} }
if (this.options.modern === 'server') { const modernMiddleware = createModernMiddleware({
this.useMiddleware(modernMiddleware) context: this.renderer.context
} })
// Add webpack middleware support only for development // Add webpack middleware support only for development
if (this.options.dev) { if (this.options.dev) {
this.useMiddleware(modernMiddleware)
this.useMiddleware(async (req, res, next) => { this.useMiddleware(async (req, res, next) => {
const name = req.modernMode ? 'modern' : 'client' const name = req.modernMode ? 'modern' : 'client'
if (this.devMiddleware[name]) { if (this.devMiddleware[name]) {
@ -124,6 +125,7 @@ export default class Server {
this.options.render.dist this.options.render.dist
) )
}) })
this.useMiddleware(modernMiddleware)
} }
// Add user provided middleware // Add user provided middleware

View File

@ -223,7 +223,8 @@ export default class VueRenderer {
rendererOptions rendererOptions
) )
if (this.context.options.modern === 'server') { if (this.context.resources.modernManifest &&
!['client', false].includes(this.context.options.modern)) {
this.renderer.modern = createBundleRenderer( this.renderer.modern = createBundleRenderer(
this.context.resources.serverBundle, this.context.resources.serverBundle,
{ {

View File

@ -3,7 +3,7 @@ import { loadFixture, getPort, Nuxt, rp, wChunk } from '../utils'
let nuxt, port let nuxt, port
const url = route => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
describe('modern client mode', () => { describe('modern client mode (SSR)', () => {
beforeAll(async () => { beforeAll(async () => {
const options = await loadFixture('modern', { modern: 'client' }) const options = await loadFixture('modern', { modern: 'client' })
nuxt = new Nuxt(options) nuxt = new Nuxt(options)

View File

@ -1,17 +1,25 @@
import chalk from 'chalk'
import consola from 'consola'
import { loadFixture, getPort, Nuxt, rp, wChunk } from '../utils' import { loadFixture, getPort, Nuxt, rp, wChunk } from '../utils'
let nuxt, port let nuxt, port
const url = route => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
const modernUA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36' const modernUA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
const modernInfo = mode => `Modern bundles are detected. Modern mode (${chalk.green.bold(mode)}) is enabled now.`
describe('modern server mode', () => { describe('modern server mode', () => {
beforeAll(async () => { beforeAll(async () => {
const options = await loadFixture('modern', { modern: 'server' }) const options = await loadFixture('modern')
nuxt = new Nuxt(options) nuxt = new Nuxt(options)
port = await getPort() port = await getPort()
await nuxt.server.listen(port, 'localhost') await nuxt.server.listen(port, 'localhost')
}) })
test('should detect server modern mode', async () => {
await nuxt.server.renderAndGetWindow(url('/'))
expect(consola.info).toHaveBeenCalledWith(modernInfo('server'))
})
test('should use legacy resources by default', async () => { test('should use legacy resources by default', async () => {
const response = await rp(url('/')) const response = await rp(url('/'))
expect(response).toContain('/_nuxt/app.js') expect(response).toContain('/_nuxt/app.js')

View File

@ -0,0 +1,53 @@
import chalk from 'chalk'
import consola from 'consola'
import { loadFixture, getPort, Nuxt, rp } from '../utils'
let nuxt, port, options
const url = route => 'http://localhost:' + port + route
const modernInfo = mode => `Modern bundles are detected. Modern mode (${chalk.green.bold(mode)}) is enabled now.`
describe('modern client mode (SPA)', () => {
beforeAll(async () => {
options = await loadFixture('modern', { render: { ssr: false } })
nuxt = new Nuxt(options)
port = await getPort()
await nuxt.server.listen(port, 'localhost')
})
test('should detect client modern mode', async () => {
await nuxt.server.renderAndGetWindow(url('/'))
expect(consola.info).toHaveBeenCalledWith(modernInfo('client'))
})
test('should contain nomodule legacy resources', async () => {
const response = await rp(url('/'))
expect(response).toContain('src="/_nuxt/app.js" nomodule')
expect(response).toContain('src="/_nuxt/commons.app.js" nomodule')
})
test('should contain module modern resources', async () => {
const response = await rp(url('/'))
expect(response).toContain('<script type="module" src="/_nuxt/modern-app.js"')
expect(response).toContain('<script type="module" src="/_nuxt/modern-commons.app.js"')
})
test.skip('should contain module preload resources', async () => {
const response = await rp(url('/'))
expect(response).toContain('<link rel="modulepreload" href="/_nuxt/modern-app.js" as="script">')
expect(response).toContain('<link rel="modulepreload" href="/_nuxt/modern-commons.app.js" as="script">')
})
test('should contain module http2 pushed resources', async () => {
const { headers: { link } } = await rp(url('/'), { resolveWithFullResponse: true })
expect(link).toEqual([
'</_nuxt/modern-runtime.js>; rel=preload; as=script',
'</_nuxt/modern-commons.app.js>; rel=preload; as=script',
'</_nuxt/modern-app.js>; rel=preload; as=script'
].join(', '))
})
// Close server and ask nuxt to stop listening to file changes
afterAll(async () => {
await nuxt.close()
})
})