Skip to content

Commit 65cb5b4

Browse files
committed
adjust async chunk inference strategy
1 parent 9a5dd1b commit 65cb5b4

11 files changed

+63
-77
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
"eslint-plugin-vue": "^2.0.0",
7878
"file-loader": "^0.10.1",
7979
"flow-bin": "^0.39.0",
80+
"hash-sum": "^1.0.2",
8081
"he": "^1.1.0",
8182
"http-server": "^0.9.0",
8283
"jasmine": "^2.5.2",

src/server/bundle-renderer/create-bundle-renderer.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function createBundleRendererCreator (createRenderer: () => Renderer) {
3232
bundle: string | RenderBundle,
3333
rendererOptions?: RenderOptions = {}
3434
) {
35-
let files, entry, maps, moduleMappings
35+
let files, entry, maps
3636
let basedir = rendererOptions.basedir
3737
const direct = rendererOptions.directMode
3838

@@ -63,7 +63,6 @@ export function createBundleRendererCreator (createRenderer: () => Renderer) {
6363
files = bundle.files
6464
basedir = basedir || bundle.basedir
6565
maps = createSourceMapConsumers(bundle.maps)
66-
moduleMappings = bundle.modules
6766
if (typeof entry !== 'string' || typeof files !== 'object') {
6867
throw new Error(INVALID_MSG)
6968
}
@@ -75,13 +74,6 @@ export function createBundleRendererCreator (createRenderer: () => Renderer) {
7574
throw new Error(INVALID_MSG)
7675
}
7776

78-
if (moduleMappings) {
79-
rendererOptions = Object.assign({}, rendererOptions, {
80-
serverManifest: {
81-
modules: moduleMappings
82-
}
83-
})
84-
}
8577
const renderer = createRenderer(rendererOptions)
8678

8779
const run = createBundleRunner(entry, files, basedir, direct)

src/server/bundle-renderer/create-bundle-runner.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function compileModule (files, basedir) {
4040
return script
4141
}
4242

43-
function evaluateModule (filename, context, evaluatedFiles) {
43+
function evaluateModule (filename, context, evaluatedFiles = {}) {
4444
if (evaluatedFiles[filename]) {
4545
return evaluatedFiles[filename]
4646
}
@@ -93,9 +93,8 @@ export function createBundleRunner (entry, files, basedir, direct) {
9393
// on each render. Ensures entire application state is fresh for each
9494
// render, but incurs extra evaluation cost.
9595
return (_context = {}) => new Promise((resolve, reject) => {
96-
const context = createContext(_context)
97-
const evaluatedFiles = _context._evaluatedFiles = {}
98-
const res = evaluate(entry, context, evaluatedFiles)
96+
_context._registeredComponents = new Set()
97+
const res = evaluate(entry, createContext(_context))
9998
resolve(typeof res === 'function' ? res(_context) : res)
10099
})
101100
} else {
@@ -105,12 +104,13 @@ export function createBundleRunner (entry, files, basedir, direct) {
105104
// slightly differently.
106105
const initialExposedContext = {}
107106
const context = createContext(initialExposedContext)
108-
const runner = evaluate(entry, context, {})
107+
const runner = evaluate(entry, context)
109108
if (typeof runner !== 'function') {
110109
throw new Error('direct mode expects bundle export to be a function.')
111110
}
112111
return (_context = {}) => {
113112
context.__VUE_SSR_CONTEXT__ = _context
113+
_context._registeredComponents = new Set()
114114
// vue-style-loader styles imported outside of component lifecycle hooks
115115
if (initialExposedContext._styles) {
116116
_context._styles = deepClone(initialExposedContext._styles)

src/server/create-renderer.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import RenderStream from './render-stream'
44
import TemplateRenderer from './template-renderer/index'
55
import { createWriteFunction } from './write'
66
import { createRenderFunction } from './render'
7-
import type { ClientManifest, ServerManifest } from './template-renderer/index'
7+
import type { ClientManifest } from './template-renderer/index'
88

99
export type Renderer = {
1010
renderToString: (component: Component, cb: (err: ?Error, res: ?string) => void) => void;
@@ -26,7 +26,6 @@ export type RenderOptions = {
2626
basedir?: string;
2727
directMode?: boolean;
2828
shouldPreload?: Function;
29-
serverManifest?: ServerManifest;
3029
clientManifest?: ClientManifest;
3130
};
3231

@@ -37,14 +36,12 @@ export function createRenderer ({
3736
template,
3837
cache,
3938
shouldPreload,
40-
serverManifest,
4139
clientManifest
4240
}: RenderOptions = {}): Renderer {
4341
const render = createRenderFunction(modules, directives, isUnaryTag, cache)
4442
const templateRenderer = new TemplateRenderer({
4543
template,
4644
shouldPreload,
47-
serverManifest,
4845
clientManifest
4946
})
5047

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
/* @flow */
22

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

9-
import type { ServerManifest, ClientManifest } from './index'
9+
import type { ClientManifest } from './index'
1010

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

1313
export function createMapper (
14-
serverManifest: ServerManifest,
1514
clientManifest: ClientManifest
1615
): AsyncFileMapper {
17-
const fileMap = createFileMap(serverManifest, clientManifest)
18-
19-
return function mapFiles (files: Array<string>): Array<string> {
16+
const map = createMap(clientManifest)
17+
// map server-side moduleIds to client-side files
18+
return function mapper (moduleIds: Array<string>): Array<string> {
2019
const res = new Set()
21-
for (let i = 0; i < files.length; i++) {
22-
const mapped = fileMap.get(files[i])
20+
for (let i = 0; i < moduleIds.length; i++) {
21+
const mapped = map.get(moduleIds[i])
2322
if (mapped) {
2423
for (let j = 0; j < mapped.length; j++) {
2524
res.add(mapped[j])
@@ -30,27 +29,25 @@ export function createMapper (
3029
}
3130
}
3231

33-
function createFileMap (serverManifest, clientManifest) {
34-
const fileMap = new Map()
35-
Object.keys(serverManifest.modules).forEach(file => {
36-
fileMap.set(file, mapFile(serverManifest.modules[file], clientManifest))
32+
function createMap (clientManifest) {
33+
const map = new Map()
34+
Object.keys(clientManifest.modules).forEach(id => {
35+
map.set(id, mapIdToFile(id, clientManifest))
3736
})
38-
return fileMap
37+
return map
3938
}
4039

41-
function mapFile (moduleIds, clientManifest) {
42-
const files = new Set()
43-
moduleIds.forEach(id => {
44-
const fileIndices = clientManifest.modules[id]
45-
if (fileIndices) {
46-
fileIndices.forEach(index => {
47-
const file = clientManifest.all[index]
48-
// only include async files or non-js assets
49-
if (clientManifest.async.indexOf(file) > -1 || !(/\.js($|\?)/.test(file))) {
50-
files.add(file)
51-
}
52-
})
53-
}
54-
})
55-
return Array.from(files)
40+
function mapIdToFile (id, clientManifest) {
41+
const files = []
42+
const fileIndices = clientManifest.modules[id]
43+
if (fileIndices) {
44+
fileIndices.forEach(index => {
45+
const file = clientManifest.all[index]
46+
// only include async files or non-js assets
47+
if (clientManifest.async.indexOf(file) > -1 || !(/\.js($|\?)/.test(file))) {
48+
files.push(file)
49+
}
50+
})
51+
}
52+
return files
5653
}

src/server/template-renderer/index.js

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,10 @@ export const isJS = (file: string): boolean => JS_RE.test(file)
1414

1515
type TemplateRendererOptions = {
1616
template: ?string;
17-
serverManifest?: ServerManifest;
1817
clientManifest?: ClientManifest;
1918
shouldPreload?: (file: string, type: string) => boolean;
2019
};
2120

22-
export type ServerManifest = {
23-
modules: {
24-
[file: string]: Array<string>;
25-
}
26-
};
27-
2821
export type ClientManifest = {
2922
publicPath: string;
3023
all: Array<string>;
@@ -42,7 +35,6 @@ export default class TemplateRenderer {
4235
options: TemplateRendererOptions;
4336
parsedTemplate: ParsedTemplate | null;
4437
publicPath: string;
45-
serverManifest: ServerManifest;
4638
clientManifest: ClientManifest;
4739
preloadFiles: Array<string>;
4840
prefetchFiles: Array<string>;
@@ -57,15 +49,14 @@ export default class TemplateRenderer {
5749
: null
5850

5951
// extra functionality with client manifest
60-
if (options.serverManifest && options.clientManifest) {
61-
const serverManifest = this.serverManifest = options.serverManifest
52+
if (options.clientManifest) {
6253
const clientManifest = this.clientManifest = options.clientManifest
6354
this.publicPath = clientManifest.publicPath.replace(/\/$/, '')
6455
// preload/prefetch drectives
6556
this.preloadFiles = clientManifest.initial
6657
this.prefetchFiles = clientManifest.async
6758
// initial async chunk mapping
68-
this.mapFiles = createMapper(serverManifest, clientManifest)
59+
this.mapFiles = createMapper(clientManifest)
6960
}
7061
}
7162

@@ -125,7 +116,7 @@ export default class TemplateRenderer {
125116

126117
renderPrefetchLinks (context: Object): string {
127118
if (this.prefetchFiles) {
128-
const usedAsyncFiles = this.getUsedAsyncFiles(context, true)
119+
const usedAsyncFiles = this.getUsedAsyncFiles(context)
129120
const alreadyRendered = file => {
130121
return usedAsyncFiles && usedAsyncFiles.some(f => f === file)
131122
}
@@ -162,25 +153,11 @@ export default class TemplateRenderer {
162153
}
163154
}
164155

165-
getUsedAsyncFiles (context: Object, raw?: boolean): ?Array<string> {
166-
if (!context._mappedfiles && context._evaluatedFiles && this.mapFiles) {
167-
let mapped = this.mapFiles(Object.keys(context._evaluatedFiles))
168-
context._rawMappedFiles = mapped
169-
// if a file has a no-css version (produced by vue-ssr-webpack-plugin),
170-
// we should use that instead.
171-
const noCssHash = this.clientManifest && this.clientManifest.hasNoCssVersion
172-
if (noCssHash) {
173-
mapped = mapped.map(file => {
174-
return noCssHash[file]
175-
? file.replace(JS_RE, '.no-css.js')
176-
: file
177-
})
178-
}
179-
context._mappedFiles = mapped
156+
getUsedAsyncFiles (context: Object): ?Array<string> {
157+
if (!context._mappedfiles && context._registeredComponents && this.mapFiles) {
158+
context._mappedFiles = this.mapFiles(Array.from(context._registeredComponents))
180159
}
181-
return raw
182-
? context._rawMappedFiles
183-
: context._mappedFiles
160+
return context._mappedFiles
184161
}
185162

186163
// create a transform stream

test/ssr/async-loader.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const hash = require('hash-sum')
2+
3+
module.exports = function (code) {
4+
const id = hash(this.request) // simulating vue-loader module id injection
5+
return code.replace('__MODULE_ID__', id)
6+
}

test/ssr/compile-with-webpack.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ export function compileWithWebpack (file, extraConfig, cb) {
1111
test: /\.js$/,
1212
loader: 'babel-loader'
1313
},
14+
{
15+
test: /async-.*\.js$/,
16+
loader: require.resolve('./async-loader')
17+
},
1418
{
1519
test: /\.(png|woff2)$/,
1620
loader: 'file-loader',

test/ssr/fixtures/async-bar.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
/* globals __VUE_SSR_CONTEXT__ */
2+
13
module.exports = {
4+
beforeCreate () {
5+
__VUE_SSR_CONTEXT__._registeredComponents.add('__MODULE_ID__')
6+
},
27
render (h) {
38
return h('div', 'async bar')
49
}

test/ssr/fixtures/async-foo.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
/* globals __VUE_SSR_CONTEXT__ */
2+
13
// import image and font
24
import font from './test.woff2'
35
import image from './test.png'
46

57
module.exports = {
8+
beforeCreate () {
9+
__VUE_SSR_CONTEXT__._registeredComponents.add('__MODULE_ID__')
10+
},
611
render (h) {
712
return h('div', `async ${font} ${image}`)
813
}

test/ssr/fixtures/nested-cache.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
/* globals __VUE_SSR_CONTEXT__ */
2+
13
import Vue from '../../../dist/vue.runtime.common.js'
24

35
function register (id, context) {
4-
context = context || __VUE_SSR_CONTEXT__ // eslint-disable-line
6+
context = context || __VUE_SSR_CONTEXT__
57
context.registered.push(id)
68
}
79

0 commit comments

Comments
 (0)