UmiMax学习笔记与umi + ssr + koa实现服务端渲染

#### 安装:pnpm dlx create-umi@latest 

/**
 * access: 权限
 * 1. 在app.ts中配置全局初始化函数getInitialState
 * 2. 在根目录下的access.ts中配置自定义要暴露的权限种类
 * 3. 在routes中可直接使用access.ts中暴露的权限key(字符串形式)
 * 4. 在组件中使用const access = useAccess() 可拿到暴露的所有权限种类
 * 5. 搭配layout使用:当routes配置了access, 并且access值和app.tsx中的layout配置key相同时
 * 权限不满足则返回对应的页面内容
 * 如:app.tsx中
 *  export const layout = () => { 
        isAdmin: <div>您不是admin 暂时无权访问!</div> // 匹配routes中带有access并且access: 'isAdmin'
      };
    };
 * 
 *
 * Model: 数据流
 * 1. 直接在根目录下的models中添加文件xxx.ts
 * 获取:const {导出的变量} = useModel('xxx')
 * 
 * 2. 在pages下创建文件夹 aaa/models/bbb.ts
 * 获取:const {导出的变量} = useModel('aaa.bbb')
 * 
 * 3. 获取初始化initialState(在app.ts中通过getInitialState返回的数据)
 * const initialState = useModel('@@initialState')
 * 
 * 
 * 请求
 * 1. app.ts中配置export const request: RequestConfig = MyRequest
 * 2. utils下创建request.ts并导出MyRequest
 * import type { RequestConfig } from 'umi';

    const MyRequest: RequestConfig = {
      timeout: 1000,
      headers: {
        'Content-Type': 'application/json;charset:utf-8;'
      },
      // other axios options you want
      errorConfig: {
        errorHandler(error: any, opts) {
          switch (error?.code) {
            default:
              '请求错误'
          }
          return Promise.reject(error)
        },
        errorThrower() {
        }
      },
      requestInterceptors: [
        (url, options) => {
          const headers = options?.headers || {}
          // do something
          return {
            url,
            options: {
              ...options,
              headers: {
                ...headers,
                token: 'aaa'
              }
            }
          }
        },
      ],
      responseInterceptors: [
        (response) => { 
          // do something
          return response
        },
      ]
    };

    export default MyRequest
 * 
 * 3. src下创建services文件夹并创建user.ts,写入接口api
 *  import { request } from "@umijs/max"
    export const userList = (params?: {}) => {
      return request('/api/v1/queryUserList', {
        method: 'GET',
        ...params
      })
    }
 * 
 * 
 * 4. 使用useRequest
 * import { useRequest } from 'umi';
 * !!!hook形式调用,不能写在函数中调用,会报错
 *  const { data, error, loading, run }= useRequest(getUser, {
    manual: true, // 禁止自动调用
    loadingDelay: 200, // 延迟loading出现时间
   // refreshDeps: [name], // 必须 !! manual: false
  })

 // 在需要接口请求的地方调用run()
 */
UMI + Koa 实现ssr
1. 项目搭建 yarn create @umijs/umi-app

!!!umi4版本以上使用ssr目前会报错,安装的是umi3版本的

项目搭建成功后,在根目录下创建server.js文件,内容如下:
const Koa = require('koa');

const app = new Koa();

app.use(async (ctx) => {
  const { request: req, response: res } = ctx;
  // 或者从 CDN 上下载到 server 端
  // const serverPath = await downloadServerBundle('http://cdn.com/bar/umi.server.js');
  const render = require('./dist/umi.server');
  res.set('Content-Type', 'text/html');

  const context = {};
  // 发起 API 请求查询
  const result = await fetch(
    `https://jsonplaceholder.typicode.com/todos/1`,
  ).then((res) => res.json());
  const { html, error, rootContainer } = await render({
    path: req.url,
    context,

    // 可自定义 html 模板
    // htmlTemplate: defaultHtml,

    // 启用流式渲染
    // mode: 'stream',

    // html 片段静态标记(适用于静态站点生成)
    // staticMarkup: false,

    // 扩展 getInitialProps 在服务端渲染中的参数
    getInitialPropsCtx: {
      todoData: result,
    },

    // manifest,正常情况下不需要
  });

  ctx.body = html;
});

app.listen(3001);
module.exports = app.callback();
在项目中的.umirc.ts中配置开启ssr及静态资源路径(防止css等在服务端不生效)
import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  routes: [{ path: '/', component: '@/pages/index' }],
  fastRefresh: {},
  ssr: {
    // devServerRender: false, // 根据需要选择是否配置
  },
  publicPath: 'http://localhost:8000/', // 一定要配置 否则样式文件等不生效
}); 
!!!前后端两个服务都开启
1.前端启动: npm start
2.服务端启动: node server.js
访问 localhost:3001(上面server.js文件中启动的端口号)

在这里插入图片描述
在这里插入图片描述

可以看到服务端渲染已经成功
动态加载导航文件实现路径切换

在这里插入图片描述

文件夹层级

在这里插入图片描述

!!!!index.tsx代码
import styles from './index.less';
import { history } from 'umi';
import QuickNavJumper from '@/components/QuickNavJumper';

const moduleList = [
  { name: '页面A', key: 'pageA' },
  { name: '页面B', key: 'pageB' },
];

const IndexPage = () => {
  const getCurrentModule = () => {
    const location = history?.location;
    return location?.query?.module || moduleList[0]?.key;
  };

  const currentModule = getCurrentModule(); 
  // 通过require().default加载对应文件内容
  const dynamicLoadComponent = () => {
    return require(`@/pages/detail/${currentModule}/index.tsx`).default;
  };
  const DynamicComponent = dynamicLoadComponent(); 
  return (
    <div className={styles.container}>
      <QuickNavJumper moduleList={moduleList} /> 
      <div className="content">
        <DynamicComponent />
      </div>
    </div>
  );
}; 
export default IndexPage;
===========================================
QuickNavJumper代码:
import React from 'react';
import styles from './index.less';
import { history } from 'umi';

type IndexPageProps = {
  moduleList: any;
};
const IndexPage = (props: IndexPageProps) => {
  const { moduleList } = props;
  const getCurrentModule = () => {
    const location = history?.location;
    return location?.query?.module || props.moduleList[0]?.key;
  };

  const onClickNav = (item: any) => {
    history.push(`/detail?module=${item.key}`);
  };

  const currentModule = getCurrentModule();
  return (
    <div className={styles.nav}>
      {moduleList?.map((item) => (
        <div
          key={item.key}
          className={currentModule === item.key ? 'active' : ''}
          onClick={() => onClickNav(item)}
        >
          {item.name}
        </div>
      ))}
    </div>
  );
};

export default IndexPage;

PageA组件:
import React from 'react'; 
const IndexPage = () => {
  return <div>pageA</div>;
}; 
export default IndexPage;
===========================================
PageB组件:
import React from 'react'; 
const IndexPage = () => {
  return <div>pageB</div>;
}; 
export default IndexPage;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值