perf(v2): significantly reduce bundle size & initial html payload (#1898)

* perf(v2): reduce bundle size significantly with super short chunk name and registry

* changelog

* use hash:8 as id for better long term caching

* even shorter filename. slice contenthash
This commit is contained in:
Endi 2019-10-27 21:09:19 +07:00 committed by Yangshun Tay
parent c23f981f67
commit fabaf7772b
7 changed files with 45 additions and 20 deletions

View file

@ -3,6 +3,7 @@
## Unreleased ## Unreleased
- Convert sitemap plugin to TypeScript - Convert sitemap plugin to TypeScript
- Significantly reduce main bundle size and initial HTML payload on production build. Generated JS files from webpack is also shorter in name.
## 2.0.0-alpha.31 ## 2.0.0-alpha.31

View file

@ -108,6 +108,20 @@ describe('load utils', () => {
Object.keys(secondAssert).forEach(str => { Object.keys(secondAssert).forEach(str => {
expect(genChunkName(str, undefined, 'blog')).toBe(secondAssert[str]); expect(genChunkName(str, undefined, 'blog')).toBe(secondAssert[str]);
}); });
// Only generate short unique id
const thirdAssert = {
a: '0cc175b9',
b: '92eb5ffe',
c: '4a8a08f0',
d: '8277e091',
};
Object.keys(thirdAssert).forEach(str => {
expect(genChunkName(str, undefined, undefined, true)).toBe(
thirdAssert[str],
);
});
expect(genChunkName('d', undefined, undefined, true)).toBe('8277e091');
}); });
test('idx', () => { test('idx', () => {

View file

@ -101,9 +101,16 @@ export function genChunkName(
modulePath: string, modulePath: string,
prefix?: string, prefix?: string,
preferredName?: string, preferredName?: string,
shortId?: boolean,
): string { ): string {
let chunkName: string | undefined = chunkNameCache.get(modulePath); let chunkName: string | undefined = chunkNameCache.get(modulePath);
if (!chunkName) { if (!chunkName) {
if (shortId) {
chunkName = createHash('md5')
.update(modulePath)
.digest('hex')
.substr(0, 8);
} else {
let str = modulePath; let str = modulePath;
if (preferredName) { if (preferredName) {
const shortHash = createHash('md5') const shortHash = createHash('md5')
@ -114,6 +121,7 @@ export function genChunkName(
} }
const name = str === '/' ? 'index' : docuHash(str); const name = str === '/' ? 'index' : docuHash(str);
chunkName = prefix ? `${prefix}---${name}` : name; chunkName = prefix ? `${prefix}---${name}` : name;
}
chunkNameCache.set(modulePath, chunkName); chunkNameCache.set(modulePath, chunkName);
} }
return chunkName; return chunkName;

View file

@ -56,9 +56,11 @@ function ComponentCreator(path) {
} }
const chunkRegistry = registry[target] || {}; const chunkRegistry = registry[target] || {};
optsLoader[keys.join('.')] = chunkRegistry.loader; /* eslint-disable prefer-destructuring */
optsModules.push(chunkRegistry.module); optsLoader[keys.join('.')] = chunkRegistry[0];
optsWebpack.push(chunkRegistry.webpack); optsModules.push(chunkRegistry[1]);
optsWebpack.push(chunkRegistry[2]);
/* eslint-enable prefer-destructuring */
} }
traverseChunk(chunkNames, []); traverseChunk(chunkNames, []);

View file

@ -117,11 +117,10 @@ export async function load(siteDir: string): Promise<Props> {
`export default { `export default {
${Object.keys(registry) ${Object.keys(registry)
.map( .map(
key => ` '${key}': { key =>
'loader': ${registry[key].loader}, ` '${key}': [${registry[key].loader}, ${JSON.stringify(
'module': ${JSON.stringify(registry[key].modulePath)}, registry[key].modulePath,
'webpack': require.resolveWeak(${JSON.stringify(registry[key].modulePath)}), )}, require.resolveWeak(${JSON.stringify(registry[key].modulePath)})],`,
},`,
) )
.join('\n')}};\n`, .join('\n')}};\n`,
); );

View file

@ -24,6 +24,7 @@ function getModulePath(target: Module): string {
} }
export async function loadRoutes(pluginsRouteConfigs: RouteConfig[]) { export async function loadRoutes(pluginsRouteConfigs: RouteConfig[]) {
const isProd = process.env.NODE_ENV === 'production';
const routesImports = [ const routesImports = [
`import React from 'react';`, `import React from 'react';`,
`import ComponentCreator from '@docusaurus/ComponentCreator';`, `import ComponentCreator from '@docusaurus/ComponentCreator';`,
@ -82,7 +83,7 @@ export async function loadRoutes(pluginsRouteConfigs: RouteConfig[]) {
} }
const modulePath = getModulePath(value as Module); const modulePath = getModulePath(value as Module);
const chunkName = genChunkName(modulePath, prefix, name); const chunkName = genChunkName(modulePath, prefix, name, isProd);
const loader = `() => import(/* webpackChunkName: '${chunkName}' */ ${JSON.stringify( const loader = `() => import(/* webpackChunkName: '${chunkName}' */ ${JSON.stringify(
modulePath, modulePath,
)})`; )})`;

View file

@ -32,8 +32,8 @@ export function createBaseConfig(
output: { output: {
pathinfo: false, pathinfo: false,
path: outDir, path: outDir,
filename: isProd ? '[name].[contenthash].js' : '[name].js', filename: isProd ? '[name].[contenthash:8].js' : '[name].js',
chunkFilename: isProd ? '[name].[contenthash].js' : '[name].js', chunkFilename: isProd ? '[name].[contenthash:8].js' : '[name].js',
publicPath: baseUrl, publicPath: baseUrl,
}, },
// Don't throw warning when asset created is over 250kb // Don't throw warning when asset created is over 250kb
@ -157,8 +157,8 @@ export function createBaseConfig(
}, },
plugins: [ plugins: [
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
filename: isProd ? '[name].[contenthash].css' : '[name].css', filename: isProd ? '[name].[contenthash:8].css' : '[name].css',
chunkFilename: isProd ? '[name].[contenthash].css' : '[name].css', chunkFilename: isProd ? '[name].[contenthash:8].css' : '[name].css',
}), }),
], ],
}; };