Cool Tricks: Code Splitting
Code splitting or lazy-loading is the process of writing an app such that the user only loads the code they need when they need it, instead of loading the entire app in one huge bundle up front.
Imagine your single-page app has two kinds of users: regular users and admins. The admins have access to different pages than the regular users, and those pages have whole complicated state trees which are irrelevant to regular users.
It doesn't make sense to force regular users to download, parse, and compile the code for those pages which they'll never see and can't use. Instead, we can lazily load those components using dynamic import()
, and register their state trees after-the-fact.
TypePoliciesMixin
To facilitate this, import the TypePoliciesMixin
so you can lazy-load portions of your client-side state store along with their UI components.
Let's say your client side router loads a different component depending on the user role:
// router.ts
export const router = new Router({
routes: [{
path: '/profile',
async onEnter(context) {
if (context.user.roles.includes('admin'))
await import('../components/admin-profile');
else
await import('../components/profile');
},
}]
})
And let's say you want to apply different type policies depending on the type of user:
// src/components/user/typePolicies.ts
export const UserTypePolicies = {
User: {
fields: {
fullName(_, { readField }) {
return `${readField('firstName')} ${readField('lastName')}`;
}
}
}
};
export const AdminTypePolicies = {
User: {
fields: {
role() {
return 'admin';
}
}
}
};
import TypePoliciesMixin
from the mixins package to easily register type policies on a component. For example, in src/components/profile/profile.ts
you can use UserTypePolicies
<p>HTML apps should set all their type policies on the <code>apollo-client</code> element</p>
<apollo-client>...</apollo-client>
<script>
import('./typePolicies')
.then(({ UserTypePolicies }) => {
document
.currentScript
.getRootNode()
.querySelector('apollo-client')
.typePolicies = UserTypePolicies;
});
</script>
import { ApolloQueryMixin, TypePoliciesMixin } from '@apollo-elements/mixins';
import { UserTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
export class ProfilePage extends TypePoliciesMixin(ApolloQueryMixin(HTMLElement))<typeof UserQuery> {
query = UserQuery;
typePolicies = UserTypePolicies;
}
customElements.define('profile-page', ProfilePage);
import { ApolloQuery, customElement } from '@apollo-elements/lit-apollo';
import { TypePoliciesMixin } from '@apollo-elements/mixins/type-policies-mixin';
import { UserTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
@customElement('profile-page')
export class ProfilePage extends TypePoliciesMixin(ApolloQuery)<typeof UserQuery> {
query = UserQuery;
typePolicies = UserTypePolicies;
}
import { ApolloQuery } from '@apollo-elements/fast/bases/apollo-query';
import { customElement } from '@microsoft/fast-element';
import { TypePoliciesMixin } from '@apollo-elements/mixins/type-policies-mixin';
import { UserTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
@customElement({ name: 'profile-page' })
export class ProfilePage extends TypePoliciesMixin(ApolloQuery)<typeof UserQuery> {
query = UserQuery;
typePolicies = UserTypePolicies;
}
import { useQuery, useEffect, component, html } from '@apollo-elements/haunted';
import { UserTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
function ProfilePage({ client }) {
const { client, data } = useQuery(UserQuery);
/**
* There's no TypePoliciesMixin for haunted,
* but you can use the `useEffect` hook to do the same
*/
useEffect(() => {
client.cache.policies.addTypePolicies(UserTypePolicies);
}, [client]);
return html`...`;
}
customElements.define('profile-page', component(ProfilePage));
import { useQuery, useEffect, c } from '@apollo-elements/atomico';
import { UserTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
function ProfilePage({ client }) {
const { client, data } = useQuery(UserQuery);
/**
* There's no TypePoliciesMixin for atomico,
* but you can use the `useEffect` hook to do the same
*/
useEffect(() => {
client.cache.policies.addTypePolicies(UserTypePolicies);
}, [client]);
return <host>...</host>;
}
customElements.define('profile-page', c(ProfilePage));
import { query, define } from '@apollo-elements/hybrids';
import { UserTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
define('profile-page', {
query: query(UserQuery),
__typePolicies: {
/**
* There's no TypePoliciesMixin for hybrids,
* but you can use this one-line function to do the same
*/
connect(host) {
host.query.client.cache.policies.addTypePolicies(UserTypePolicies);
}
},
});
And you can lazy-load that same code for use in src/components/admin-profile/admin-profile.ts
.
<p>HTML apps should set all their type policies on the <code>apollo-client</code> element</p>
<apollo-client>...</apollo-client>
<script>
import('./typePolicies')
.then(({ UserTypePolicies }) => {
document
.currentScript
.getRootNode()
.querySelector('apollo-client')
.typePolicies = UserTypePolicies;
});
</script>
import { ApolloQueryMixin, TypePoliciesMixin } from '@apollo-elements/mixins';
import { UserTypePolicies, AdminTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
export class AdminProfilePage
extends TypePoliciesMixin(ApolloQueryMixin(HTMLElement))<typeof UserQuery> {
query = UserQuery;
typePolicies = { ...UserTypePolicies, ...AdminTypePolicies };
}
customElements.define('admin-profile-page', AdminProfilePage);
import { ApolloQuery, customElement } from '@apollo-elements/lit-apollo';
import { TypePoliciesMixin } from '@apollo-elements/mixins/type-policies-mixin';
import { UserTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
@customElement('admin-profile-page')
export class AdminProfilePage extends TypePoliciesMixin(ApolloQuery)<typeof UserQuery> {
query = UserQuery;
typePolicies = { ...UserTypePolicies, ...AdminTypePolicies };
}
import { customElement } from '@microsoft/fast-element';
import { ApolloQuery } from '@apollo-elements/fast/bases/apollo-query';
import { TypePoliciesMixin } from '@apollo-elements/mixins/type-policies-mixin';
import { UserTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
@customElement({ name: 'admin-profile-page' })
export class AdminProfilePage extends TypePoliciesMixin(ApolloQuery)<typeof UserQuery> {
query = UserQuery;
typePolicies = { ...UserTypePolicies, ...AdminTypePolicies };
}
import { useQuery, useEffect, component, html } from '@apollo-elements/haunted';
import { UserTypePolicies, AdminTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
function AdminProfilePage() {
const { client, data } = useQuery(UserQuery);
/**
* There's no TypePoliciesMixin for haunted,
* but you can use the `useEffect` hook to do the same
*/
useEffect(() => {
client.cache.policies.addTypePolicies(UserTypePolicies),
client.cache.policies.addTypePolicies(AdminTypePolicies),
}, [client]);
return html`...`;
}
customElements.define('admin-profile-page', component(AdminProfilePage));
import { query, define } from '@apollo-elements/hybrids';
import { UserTypePolicies, AdminTypePolicies } from './typePolicies';
import { UserQuery } from './User.query.graphql';
define('admin-profile-page', {
query: query(UserQuery),
__typePolicies: {
/**
* There's no TypePoliciesMixin for hybrids,
* but you can use this one-line function to do the same
*/
connect(host) {
host.query.client.cache.policies.addTypePolicies(UserTypePolicies),
host.query.client.cache.policies.addTypePolicies(AdminTypePolicies),
}
}
});
When using Apollo*Controllers
, take inspiration from the haunted
and hybrids
examples and call addTypePolicies
on the controller's cache when the host connects.