ice-lab / ice-next

The repo was transferred to https://github.com/alibaba/ice
https://v3.ice.work/
MIT License
53 stars 7 forks source link

[RFC]统一预请求方案 #585

Open answershuto opened 1 year ago

answershuto commented 1 year ago

现状:

PHA 中,某个 page 下面的 dataPrefetch 会编译到对应的 home-manifast 中。

export function getConfig() {
  return {
    title: 'Home',
    dataPrefetch: [
      {
        key: 'key_1',
        prefetchType: 'mtop',
        api: 'mtop.tmall.kangaroo.core.service.route.pagerecommendservice',
        v: '1.0',
        data: {
          param1: 'v1',
          param2: 'v2',
        },
        extHeaders: {
          nameAaaa: '112233',
        },
      }
    ],
  };
}

纯 Web 场景,使用 getData。

export function getData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        name: 'Home',
      });
    }, 1 * 100);
  });
}

方案

发送数据

// 多个情况
// 静态方法会包装 fetcher (默认 window.fetch)
export const dataLoader = defineDataLoader([
  {
    key: 'key_1',
    prefetchType: 'mtop',
    api: 'mtop.tmall.kangaroo.core.service.route.pagerecommendservice222',
    v: '1.0',
    data: {
      param1: 'v1',
      param2: 'v2',
    },
    extHeaders: {
      nameAaaa: '112233',
    },
  },
  async () => {
    // 当然,用户不需要同构的情况下,可以选择不用。
    const data2 = await Mtop.request({
      api: 'mtop.user.getUserSimple2',
      v: '1.0',
      data: { itemNumId: 37194529489 },
    });

    return { data, data2 };
  },
]);

// 单个静态
export const dataLoader = defineDataLoader({
  key: 'key_1',
  prefetchType: 'mtop',
  api: 'mtop.tmall.kangaroo.core.service.route.pagerecommendservice222',
  v: '1.0',
  data: {
    param1: 'v1',
    param2: 'v2',
  },
  extHeaders: {
    nameAaaa: '112233',
  },
});

// 单个动态
export const dataLoader = defineDataLoader(async () => {
  const data2 = await Mtop.request({
      api: 'mtop.user.getUserSimple2',
      v: '1.0',
      data: { itemNumId: 37194529489 },
    });

  return { data, data2 };
});

获取数据

export default function Home() {
  const data = useData('data-key'); // [null, {}, null]
}

全局的配置在 PHA 下处理不变,纯 Web 下会被构建到每一个页面下面。

PHA 改造

chenjun1011 commented 1 year ago

10-25 讨论结论

常规写法(动态)

export const dataLoaders = defineDataLoaders(() => {
  const res = await fetch('https://api.example.com/...');
  return res.json();
});

使用数据

const data = useData();

静态配置

export const dataLoaders = defineDataLoaders([
  {
    prefetchType: 'mtop',
    api: 'mtop.xxx.xxx.xxx',
    v: '1.0',
    data: {
      param1: 'v1',
      param2: 'v2',
    },
    extHeaders: {
      nameAaaa: '112233',
    },
  },
]);

使用数据

const [data] = useData();

混合使用

export const dataLoaders = defineDataLoaders([
  () => {
    const res = await fetch('https://api.example.com/...');
    return res.json();
  },
  {
    prefetchType: 'mtop',
    api: 'mtop.xxx.xxx.xxx',
    v: '1.0',
    data: {
      param1: 'v1',
      param2: 'v2',
    },
    extHeaders: {
      nameAaaa: '112233',
    },
  },
]);

使用数据

const [data1, data2] = useData();

SSR

默认情况下,SSR 下复用 dataLoaders 中定义的数据请求,当需要特殊定制 Server 端逻辑时,可以使用 defineServerDataLoaders 来定义。

export const serverDataLoaders = defineServerDataLoaders(() => {
  const res = await fetch('https://api.example.com/...');
  return res.json();
});

定义了 defineServerDataLoaders 后,SSR 下只执行 serverDataLoaders, 不再调用 dataLoaders。仅在降级为 CSR 或路由跳转时调用 dataLoaders

SSG

默认情况下,SSG 下的 useData 获取的数据为 null,可通过 defineStaticDataLoaders 定义 SSG 场景下的数据

export const staticDataLoaders = defineStaticDataLoaders(() => {
  const res = await fetch('https://api.example.com/...');
  return res.json();
});
chenjun1011 commented 1 year ago

如果不考虑一个路由组件需要发多个请求的情况,我倾向于保留现有 getData, getServerData, getStaticData 的设计,相比👆🏻的调用方式会简单很多

针对 PHA 静态配置的场景,额外提供 defineDataLoader 的能力,只支持传静态配置

export const dataLoader = defineDataLoaders({
    prefetchType: 'mtop',
    api: 'mtop.xxx.xxx.xxx',
    v: '1.0',
    data: {
      param1: 'v1',
      param2: 'v2',
    },
    extHeaders: {
      nameAaaa: '112233',
    },
});

一个页面有多个数据请求的情况,通过嵌套路由来实现

answershuto commented 1 year ago

收敛 getStaticData 以及 getServerData 的抽象,目前来看核心的同构能力需要抽象 fetcher interface(uni-fetcher) 以解决在不同平台(端)下的发送请求问题。

defineDataLoaders 保持保持之前讨论的设计:

// 多个情况
// 静态方法会包装 fetcher (默认 window.fetch)
// 动态方法会传入 fetcher (默认 window.fetch)
export const dataLoader = defineDataLoader([
  {
    key: 'key_1',
    prefetchType: 'mtop',
    api: 'mtop.tmall.kangaroo.core.service.route.pagerecommendservice222',
    v: '1.0',
    data: {
      param1: 'v1',
      param2: 'v2',
    },
    extHeaders: {
      nameAaaa: '112233',
    },
  },
  {
    key: 'key_1',
    prefetchType: 'mtop',
    api: 'mtop.tmall.kangaroo.core.service.route.pagerecommendservice222',
    v: '1.0',
    data: {
      param1: 'v1',
      param2: 'v2',
    },
    extHeaders: {
      nameAaaa: '112233',
    },
  },
  async (fetcher) => {
    const data = await fetcher({
      api: 'mtop.user.getUserSimple',
      v: '1.0',
      data: { itemNumId: 37194529489 },
    });

    // 当然,用户不需要同构的情况下,可以选择不用。
    const data2 = await Mtop.request({
      api: 'mtop.user.getUserSimple2',
      v: '1.0',
      data: { itemNumId: 37194529489 },
    });

    return { data, data2 };
  },
]);

// 单个静态
export const dataLoader = defineDataLoader({
  key: 'key_1',
  prefetchType: 'mtop',
  api: 'mtop.tmall.kangaroo.core.service.route.pagerecommendservice222',
  v: '1.0',
  data: {
    param1: 'v1',
    param2: 'v2',
  },
  extHeaders: {
    nameAaaa: '112233',
  },
});

// 单个动态
export const dataLoader = defineDataLoader(async (fetcher) => {
  const data = await fetcher({
    api: 'mtop.user.getUserSimple',
    v: '1.0',
    data: { itemNumId: 37194529489 },
  });

  const data2 = await fetcher({
    api: 'mtop.user.getUserSimple2',
    v: '1.0',
    data: { itemNumId: 37194529489 },
  });

  return { data, data2 };
});
const plugin = () => ({
  name: 'plugin-mtop',
  setup: ({ generator }) => {
    generator.addDataLoaderClientFetcher({
      source: '@ali/universal-mtop',// 需要修改时,可以 @/src/customFetcher/fetcher.js
      alias: {
        request: 'fetcher',
      },
      specifier: ['request'],
    });

    generator.addDataLoaderServerFetcher({
      source: '@ali/universal-mtop',
      alias: {
        request: 'fetcher',
      },
      specifier: ['request'],
    });
  },
});

export default plugin;
chenjun1011 commented 1 year ago

app 的数据请求方式也需要统一

注册数据请求:统一 getAppDatadataLoader 的声明方式

export const dataLoader = defineDataLoader(() => {
  const res = await fetch('https://api.example.com/...');
  return res.json();
});

消费数据:仍然保留 useAppData