Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] icestark prefetch #188

Closed
maoxiaoke opened this issue Nov 9, 2020 · 2 comments · Fixed by #278
Closed

[RFC] icestark prefetch #188

maoxiaoke opened this issue Nov 9, 2020 · 2 comments · Fixed by #278
Labels
supported available in icestark

Comments

@maoxiaoke
Copy link
Collaborator

maoxiaoke commented Nov 9, 2020

prefetch

目标

  • 实现预加载子应用,提升子应用加载体验

API 设计

### AppConfig 增加 prefetch 字段
interface AppConfig {
  ...
  prefetch?: boolean; // 是否预获取该资源
}

prefetch 字段仅在使用 registerMicroApp 和 registerMicroApps API 进行微应用注册时生效,用于细粒度控制需要预加载的应用。

StartConfiguration 增加 prefetch 字段

export interface StartConfiguration {
  ...
  prefetch?: boolean | string[] | Function;
}

针对所有微应用,是否支持预加载。

若同时配置全局 prefetch 和应用 prefetch,则全局 prefetch 生效

增加 prefetchApps 方法

针对手动加载微应用的场景,增加 prefetchApps 方法。

declare function prefetchApps(apps: AppConfig[]): Promise<void>;

使用示例

通过 React Component 的方式使用

import React from 'react';
import { AppRouter, AppRoute } from '@ice/stark';

// 使用方式一,所有子应用资源会被 prefetch
const App = () => {
	return (
    <AppRoute
      prefetch
      >
      <AppRoute
        name="waiter"
        title="商家平台"
        url={[
          '//unpkg.com/icestark-child-waiter/build/js/index.js',
          '//unpkg.com/icestark-child-waiter/build/css/index.css',
        ]}
      />
    </AppRouter>
  )
}

// 使用方式二,只有 name 为 waiter 的子应用资源会被 prefetch
const App = () => {
  return (
    <AppRoute
      prefetch={['waiter']}
      >
      <AppRoute
        name="waiter"
        title="商家平台"
        url={[
          '//unpkg.com/icestark-child-waiter/build/js/index.js',
          '//unpkg.com/icestark-child-waiter/build/css/index.css',
        ]}
      />
    </AppRouter>
  )
}

// 使用方式三,只有 name 为 waiter 的子应用资源会被 prefetch
const App = () => {
  return (
    <AppRoute
      prefetch={ (app: Appconfig) => {
          return app.name === 'waiter'
       }}
      >
      <AppRoute
        name="waiter"
        title="商家平台"
        url={[
          '//unpkg.com/icestark-child-waiter/build/js/index.js',
          '//unpkg.com/icestark-child-waiter/build/css/index.css',
        ]}
      />
    </AppRouter>
  )
}

基于路由控制的模式

import { registerMicroApps, start } from '@ice/stark';
import customFetcher from './customFetcher';

// 方式一
registerMicroApps([
  {
   	path: '/waiter',
    title: '商家平台',
    sandbox: true,
    url: [
      '//ice.alicdn.com/icestark/child-waiter-vue/app.js',
      '//ice.alicdn.com/icestark/child-waiter-vue/app.css',
    ],
  },
  {
   	path: '/seller',
    title: '小二平台',
    sandbox: true,
    url: [
      '//ice.alicdn.com/icestark/child-seller-vue/app.js',
      '//ice.alicdn.com/icestark/child-seller-vue/app.css',
    ],
  }
], {
	afterMount: () => { console.log('mounted') },
});

start({
	prefetch: true,
})

// 方式二
registerMicroApps([
  {
   	path: '/waiter',
    title: '商家平台',
    sandbox: true,
    url: [
      '//ice.alicdn.com/icestark/child-waiter-vue/app.js',
      '//ice.alicdn.com/icestark/child-waiter-vue/app.css',
    ],
    prefetch: true
  },
  {
   	path: '/seller',
    title: '小二平台',
    sandbox: true,
    url: [
      '//ice.alicdn.com/icestark/child-seller-vue/app.js',
      '//ice.alicdn.com/icestark/child-seller-vue/app.css',
    ],
  }
], {
	afterMount: () => { console.log('mounted') },
});

start();

基于手动控制的方式

// 在某个比较好的时机
import { prefetchApps } from '@ice/stark';

prefetchApps([{
	name: 'microapp',
  url: [
    '//ice.alicdn.com/icestark/child-waiter-vue/app.js',
    '//ice.alicdn.com/icestark/child-waiter-vue/app.css',
  ]
}]);

// 渲染子应用
import { prefetchApps, createMicroApp, unloadMicroApp } from '@ice/stark';
import customFetcher from './customFetcher';

const App = () => {
  const appContainer = useRef(null);
  useEffect(() => {
    createMicroApp(
      {
        name: 'microapp',
        url: [
          '//ice.alicdn.com/icestark/child-waiter-vue/app.js',
          '//ice.alicdn.com/icestark/child-waiter-vue/app.css',
        ],
        container: appContainer.current
      },
      undefined,
      {
        fetch: customFetcher
      }
    );
    return () => {
      unloadMicroApp('microapp');
    }
  }, []);
  return <div ref={appContainer}></div>
};

export default App;

实现思路

通过 requestIdleCallback 在浏览器空闲时段加载资源并缓存。

和 cached、shouldAssetsRemove 的区别

两者的主要区别如下:

  • shouldAssetsRemove 和 cached 主要提升 非首次加载子应用 的子应用的渲染速度
  • prefetch 主要提升 非首屏首次加载子应用 时子应用的渲染速度
@maoxiaoke maoxiaoke mentioned this issue Dec 29, 2020
5 tasks
@maoxiaoke
Copy link
Collaborator Author

  1. 无网络和弱网情况下,不执行 prefetch
  2. 若当前路由匹配到子应用,不 prefetch 改子应用资源

@maoxiaoke
Copy link
Collaborator Author

maoxiaoke commented Jan 27, 2021

实现方案
方案一:通过 <link rel="prefetch" /> 的方式
缺点:Browser support is different - safari 及 ie10 以下,不支持该能力,且无法提供支持
优点:浏览器提供加载优化策略(比如加载一半被暂停;当前页面正在请求会被取消);无跨域问题

image

方案二:通过 requestIdleCallback 和 fetch
优点:兼容性更好,requestIdleCallback 可以使用 polyfill 来处理
缺点:需要处理很多边缘 case;可能存在跨域问题

@maoxiaoke maoxiaoke added the beta has beta version label Mar 24, 2021
@maoxiaoke maoxiaoke added supported available in icestark and removed beta has beta version labels Mar 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
supported available in icestark
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant