Skip to content

Commit

Permalink
adjust async chunk inference strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 14, 2017
1 parent 9a5dd1b commit 65cb5b4
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 77 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"eslint-plugin-vue": "^2.0.0",
"file-loader": "^0.10.1",
"flow-bin": "^0.39.0",
"hash-sum": "^1.0.2",
"he": "^1.1.0",
"http-server": "^0.9.0",
"jasmine": "^2.5.2",
Expand Down
10 changes: 1 addition & 9 deletions src/server/bundle-renderer/create-bundle-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function createBundleRendererCreator (createRenderer: () => Renderer) {
bundle: string | RenderBundle,
rendererOptions?: RenderOptions = {}
) {
let files, entry, maps, moduleMappings
let files, entry, maps
let basedir = rendererOptions.basedir
const direct = rendererOptions.directMode

Expand Down Expand Up @@ -63,7 +63,6 @@ export function createBundleRendererCreator (createRenderer: () => Renderer) {
files = bundle.files
basedir = basedir || bundle.basedir
maps = createSourceMapConsumers(bundle.maps)
moduleMappings = bundle.modules
if (typeof entry !== 'string' || typeof files !== 'object') {
throw new Error(INVALID_MSG)
}
Expand All @@ -75,13 +74,6 @@ export function createBundleRendererCreator (createRenderer: () => Renderer) {
throw new Error(INVALID_MSG)
}

if (moduleMappings) {
rendererOptions = Object.assign({}, rendererOptions, {
serverManifest: {
modules: moduleMappings
}
})
}
const renderer = createRenderer(rendererOptions)

const run = createBundleRunner(entry, files, basedir, direct)
Expand Down
10 changes: 5 additions & 5 deletions src/server/bundle-renderer/create-bundle-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function compileModule (files, basedir) {
return script
}

function evaluateModule (filename, context, evaluatedFiles) {
function evaluateModule (filename, context, evaluatedFiles = {}) {
if (evaluatedFiles[filename]) {
return evaluatedFiles[filename]
}
Expand Down Expand Up @@ -93,9 +93,8 @@ export function createBundleRunner (entry, files, basedir, direct) {
// on each render. Ensures entire application state is fresh for each
// render, but incurs extra evaluation cost.
return (_context = {}) => new Promise((resolve, reject) => {
const context = createContext(_context)
const evaluatedFiles = _context._evaluatedFiles = {}
const res = evaluate(entry, context, evaluatedFiles)
_context._registeredComponents = new Set()
const res = evaluate(entry, createContext(_context))
resolve(typeof res === 'function' ? res(_context) : res)
})
} else {
Expand All @@ -105,12 +104,13 @@ export function createBundleRunner (entry, files, basedir, direct) {
// slightly differently.
const initialExposedContext = {}
const context = createContext(initialExposedContext)
const runner = evaluate(entry, context, {})
const runner = evaluate(entry, context)
if (typeof runner !== 'function') {
throw new Error('direct mode expects bundle export to be a function.')
}
return (_context = {}) => {
context.__VUE_SSR_CONTEXT__ = _context
_context._registeredComponents = new Set()
// vue-style-loader styles imported outside of component lifecycle hooks
if (initialExposedContext._styles) {
_context._styles = deepClone(initialExposedContext._styles)
Expand Down
5 changes: 1 addition & 4 deletions src/server/create-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import RenderStream from './render-stream'
import TemplateRenderer from './template-renderer/index'
import { createWriteFunction } from './write'
import { createRenderFunction } from './render'
import type { ClientManifest, ServerManifest } from './template-renderer/index'
import type { ClientManifest } from './template-renderer/index'

export type Renderer = {
renderToString: (component: Component, cb: (err: ?Error, res: ?string) => void) => void;
Expand All @@ -26,7 +26,6 @@ export type RenderOptions = {
basedir?: string;
directMode?: boolean;
shouldPreload?: Function;
serverManifest?: ServerManifest;
clientManifest?: ClientManifest;
};

Expand All @@ -37,14 +36,12 @@ export function createRenderer ({
template,
cache,
shouldPreload,
serverManifest,
clientManifest
}: RenderOptions = {}): Renderer {
const render = createRenderFunction(modules, directives, isUnaryTag, cache)
const templateRenderer = new TemplateRenderer({
template,
shouldPreload,
serverManifest,
clientManifest
})

Expand Down
53 changes: 25 additions & 28 deletions src/server/template-renderer/create-async-file-mapper.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
/* @flow */

/**
* Creates a mapper that maps files used during a server-side render
* Creates a mapper that maps components used during a server-side render
* to async chunk files in the client-side build, so that we can inline them
* directly in the rendered HTML to avoid waterfall requests.
*/

import type { ServerManifest, ClientManifest } from './index'
import type { ClientManifest } from './index'

export type AsyncFileMapper = (files: Array<string>) => Array<string>;

export function createMapper (
serverManifest: ServerManifest,
clientManifest: ClientManifest
): AsyncFileMapper {
const fileMap = createFileMap(serverManifest, clientManifest)

return function mapFiles (files: Array<string>): Array<string> {
const map = createMap(clientManifest)
// map server-side moduleIds to client-side files
return function mapper (moduleIds: Array<string>): Array<string> {
const res = new Set()
for (let i = 0; i < files.length; i++) {
const mapped = fileMap.get(files[i])
for (let i = 0; i < moduleIds.length; i++) {
const mapped = map.get(moduleIds[i])
if (mapped) {
for (let j = 0; j < mapped.length; j++) {
res.add(mapped[j])
Expand All @@ -30,27 +29,25 @@ export function createMapper (
}
}

function createFileMap (serverManifest, clientManifest) {
const fileMap = new Map()
Object.keys(serverManifest.modules).forEach(file => {
fileMap.set(file, mapFile(serverManifest.modules[file], clientManifest))
function createMap (clientManifest) {
const map = new Map()
Object.keys(clientManifest.modules).forEach(id => {
map.set(id, mapIdToFile(id, clientManifest))
})
return fileMap
return map
}

function mapFile (moduleIds, clientManifest) {
const files = new Set()
moduleIds.forEach(id => {
const fileIndices = clientManifest.modules[id]
if (fileIndices) {
fileIndices.forEach(index => {
const file = clientManifest.all[index]
// only include async files or non-js assets
if (clientManifest.async.indexOf(file) > -1 || !(/\.js($|\?)/.test(file))) {
files.add(file)
}
})
}
})
return Array.from(files)
function mapIdToFile (id, clientManifest) {
const files = []
const fileIndices = clientManifest.modules[id]
if (fileIndices) {
fileIndices.forEach(index => {
const file = clientManifest.all[index]
// only include async files or non-js assets
if (clientManifest.async.indexOf(file) > -1 || !(/\.js($|\?)/.test(file))) {
files.push(file)
}
})
}
return files
}
37 changes: 7 additions & 30 deletions src/server/template-renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,10 @@ export const isJS = (file: string): boolean => JS_RE.test(file)

type TemplateRendererOptions = {
template: ?string;
serverManifest?: ServerManifest;
clientManifest?: ClientManifest;
shouldPreload?: (file: string, type: string) => boolean;
};

export type ServerManifest = {
modules: {
[file: string]: Array<string>;
}
};

export type ClientManifest = {
publicPath: string;
all: Array<string>;
Expand All @@ -42,7 +35,6 @@ export default class TemplateRenderer {
options: TemplateRendererOptions;
parsedTemplate: ParsedTemplate | null;
publicPath: string;
serverManifest: ServerManifest;
clientManifest: ClientManifest;
preloadFiles: Array<string>;
prefetchFiles: Array<string>;
Expand All @@ -57,15 +49,14 @@ export default class TemplateRenderer {
: null

// extra functionality with client manifest
if (options.serverManifest && options.clientManifest) {
const serverManifest = this.serverManifest = options.serverManifest
if (options.clientManifest) {
const clientManifest = this.clientManifest = options.clientManifest
this.publicPath = clientManifest.publicPath.replace(/\/$/, '')
// preload/prefetch drectives
this.preloadFiles = clientManifest.initial
this.prefetchFiles = clientManifest.async
// initial async chunk mapping
this.mapFiles = createMapper(serverManifest, clientManifest)
this.mapFiles = createMapper(clientManifest)
}
}

Expand Down Expand Up @@ -125,7 +116,7 @@ export default class TemplateRenderer {

renderPrefetchLinks (context: Object): string {
if (this.prefetchFiles) {
const usedAsyncFiles = this.getUsedAsyncFiles(context, true)
const usedAsyncFiles = this.getUsedAsyncFiles(context)
const alreadyRendered = file => {
return usedAsyncFiles && usedAsyncFiles.some(f => f === file)
}
Expand Down Expand Up @@ -162,25 +153,11 @@ export default class TemplateRenderer {
}
}

getUsedAsyncFiles (context: Object, raw?: boolean): ?Array<string> {
if (!context._mappedfiles && context._evaluatedFiles && this.mapFiles) {
let mapped = this.mapFiles(Object.keys(context._evaluatedFiles))
context._rawMappedFiles = mapped
// if a file has a no-css version (produced by vue-ssr-webpack-plugin),
// we should use that instead.
const noCssHash = this.clientManifest && this.clientManifest.hasNoCssVersion
if (noCssHash) {
mapped = mapped.map(file => {
return noCssHash[file]
? file.replace(JS_RE, '.no-css.js')
: file
})
}
context._mappedFiles = mapped
getUsedAsyncFiles (context: Object): ?Array<string> {
if (!context._mappedfiles && context._registeredComponents && this.mapFiles) {
context._mappedFiles = this.mapFiles(Array.from(context._registeredComponents))
}
return raw
? context._rawMappedFiles
: context._mappedFiles
return context._mappedFiles
}

// create a transform stream
Expand Down
6 changes: 6 additions & 0 deletions test/ssr/async-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const hash = require('hash-sum')

module.exports = function (code) {
const id = hash(this.request) // simulating vue-loader module id injection
return code.replace('__MODULE_ID__', id)
}
4 changes: 4 additions & 0 deletions test/ssr/compile-with-webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export function compileWithWebpack (file, extraConfig, cb) {
test: /\.js$/,
loader: 'babel-loader'
},
{
test: /async-.*\.js$/,
loader: require.resolve('./async-loader')
},
{
test: /\.(png|woff2)$/,
loader: 'file-loader',
Expand Down
5 changes: 5 additions & 0 deletions test/ssr/fixtures/async-bar.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
/* globals __VUE_SSR_CONTEXT__ */

module.exports = {
beforeCreate () {
__VUE_SSR_CONTEXT__._registeredComponents.add('__MODULE_ID__')
},
render (h) {
return h('div', 'async bar')
}
Expand Down
5 changes: 5 additions & 0 deletions test/ssr/fixtures/async-foo.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
/* globals __VUE_SSR_CONTEXT__ */

// import image and font
import font from './test.woff2'
import image from './test.png'

module.exports = {
beforeCreate () {
__VUE_SSR_CONTEXT__._registeredComponents.add('__MODULE_ID__')
},
render (h) {
return h('div', `async ${font} ${image}`)
}
Expand Down
4 changes: 3 additions & 1 deletion test/ssr/fixtures/nested-cache.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* globals __VUE_SSR_CONTEXT__ */

import Vue from '../../../dist/vue.runtime.common.js'

function register (id, context) {
context = context || __VUE_SSR_CONTEXT__ // eslint-disable-line
context = context || __VUE_SSR_CONTEXT__
context.registered.push(id)
}

Expand Down

0 comments on commit 65cb5b4

Please sign in to comment.