CSS extraction and code-splitting

closes #388
This commit is contained in:
Rich Harris
2018-09-02 14:46:27 -04:00
committed by GitHub
parent afba0491ed
commit bebb0dd595
62 changed files with 885 additions and 484 deletions

View File

@@ -1,5 +1,5 @@
import { detach, findAnchor, scroll_state, which } from './utils';
import { Component, ComponentConstructor, Params, Query, Redirect, Manifest, RouteData, ScrollPosition, Store, Target } from './interfaces';
import { Component, ComponentConstructor, Params, Query, Redirect, Manifest, RouteData, ScrollPosition, Store, Target, ComponentLoader } from './interfaces';
const initial_data = typeof window !== 'undefined' && window.__SAPPER__;
@@ -131,6 +131,30 @@ function changed(a: Record<string, string | true>, b: Record<string, string | tr
let root_preload: Promise<any>;
let root_data: any;
function load_css(chunk: string) {
const href = `${initial_data.baseUrl}client/${chunk}`;
if (document.querySelector(`link[href="${href}"]`)) return;
return new Promise((fulfil, reject) => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
link.onload = () => fulfil();
link.onerror = reject;
document.head.appendChild(link);
});
}
function load_component(component: ComponentLoader): Promise<ComponentConstructor> {
// TODO this is temporary — once placeholders are
// always rewritten, scratch the ternary
const promises: Array<Promise<any>> = (typeof component.css === 'string' ? [] : component.css.map(load_css));
promises.unshift(component.js());
return Promise.all(promises).then(values => values[0].default);
}
function prepare_page(target: Target): Promise<{
redirect?: Redirect;
data?: any;
@@ -177,7 +201,8 @@ function prepare_page(target: Target): Promise<{
if (i < changed_from) return null;
if (!part) return null;
const { default: Component } = await part.component();
const Component = await load_component(part.component);
const req = {
path,
query,
@@ -468,9 +493,9 @@ export function prefetchRoutes(pathnames: string[]) {
if (!pathnames) return true;
return pathnames.some(pathname => route.pattern.test(pathname));
})
.reduce((promise: Promise<any>, route) => {
return promise.then(route.load);
}, Promise.resolve());
.reduce((promise: Promise<any>, route) => promise.then(() => {
return Promise.all(route.parts.map(part => part && load_component(part.component)));
}), Promise.resolve());
}
// remove this in 0.9

View File

@@ -15,10 +15,15 @@ export interface Component {
destroy: () => void;
}
export type ComponentLoader = {
js: () => Promise<{ default: ComponentConstructor }>,
css: string[]
};
export type Page = {
pattern: RegExp;
parts: Array<{
component: () => Promise<{ default: ComponentConstructor }>;
component: ComponentLoader;
params?: (match: RegExpExecArray) => Record<string, string>;
}>;
};