ant design pro v5

bug

问题描述

今天准备使用一下Ant Design Pro,全程按照使用文档进行项目的构建和启动,结果启动时报如下错误:

ERROR in ./src/components/index.md 
Module build failed (from ./node_modules/@umijs/preset-dumi/lib/loader/index.js): Error: [BABEL] @babel/helper-compilation-targets: 'opera_mobile' is not a valid target
### 问题出现的环境背景及自己尝试过哪些方法
Ant Design Pro版本:5.2.0
node.js:v18.12.1
npm:8.19.2
目前已经试过删除node_modules文件夹,并重新安装依赖包,依旧报错。
 

解决办法参考:https://github.com/ant-design/ant-design-pro/issues/10829 ,就是删除这个文件 ./src/components/index.md,我建的项目是 simple,删了之后直接可以运行。

配置指南

React入门第一章:了解Ant Design Pro基础框架 - 掘金

打造属于你的Ant Design Pro V5(一) - 掘金(提取了常用的几个方法)

1、更换名称、logo、主题颜色等

在开发之前我们首先要把名称、logo、主题颜色进行修改,那么该如何修改呢

位置 \config\defaultSetting.js

import { Settings as LayoutSettings } from '@ant-design/pro-layout';

const Settings: LayoutSettings & {
  pwa?: boolean;
} = {
  navTheme: 'dark', // 主题颜色
  primaryColor: '#1890ff', //颜色
  layout: 'side', // 菜单模式 side:右侧导航,top:顶部导航
  contentWidth: 'Fluid', // 内容模式 Fluid:自适应,Fixed:定宽 1200px
  fixedHeader: false, // 是否固定头部
  fixSiderbar: true, // 是否固定导航
  colorWeak: false,
  // headerRender: false, // 是否拥有头部
  // menuRender: false, // 是否拥有菜单
  title: 'Domesy',
  pwa: false,
  iconfontUrl: '', //icon
};

export default Settings;

2、跟换icon

这是一个小细节,需要去稍微改动

我们需要将项目的小图标做替换即可, 有的时候替换后没有效果,是因为缓存的问题,我们只需要重新启动下浏览器,或是本地服务即可

位置 /public/favicon.ico

3、清除头部小组件或是修改

我们看到在原本的上面有 全局搜索、使用文档、消息提示、国际化,这时我们可以根据自己的需求去做清楚或是修改

位置: src/components/RightContent

搜索框位置:src/components/HeaderSearch

消息通知:src/components/NoticeIcon

头像: src/components/RightContent/AvatarDropdown

4、加载页面

我们在启动整个项目的时候、或者在刷新的时候会出现一个加载页面,这个页面的位置在 src\pages\document.ejs 中。他在基础上使用了图片,可以进行修改,换成项目的名称和加载图片

 5、去除水印

我们可以全局设置自己的水印,或者取消,那么这些在哪里配置呢

位置 /src/app.tsx

为了简便期间我在这个做了些全局配置,可直接更改 不用去懂 app.tsx 的代码

6、修改页脚

 要想每个页面都有页脚,我们只需要更改 /src/component/Footer 即可

8、分模块打包

在我们的日常开发中,往往会有多个环境,一般的都是测试环境和线上环境,当然环境对于前端而言是接口的域名地址等,如果每次要发环境控制一个变量去改变地址,或者监听地址等操作太过麻烦,那么我们可以通过命令来改变打包的环境

那么改在哪里配置呢?

位置: /package.json

{
   ...
  "scripts": {
   ...
   "build:pre": "cross-env REACT_APP_ENV=pre umi build",
    ...
  }
}

加上这段话即可,简单的介绍一下

build:pre 它就打包命令,使用跟普通的打包命令一样 yarn run build:pre

REACT_APP_ENV 就是我们区别到底是哪个环境的

cross-env 这个主要是处理 windows 的,如果不加,windows 是无法正常打包的

那么有小伙伴问了,我在这里改完如何更换地址呢

我在 src/utils/config.ts 下做了个简单配置就行了

let host = 'http://www.domesy.cn:8081';

if (REACT_APP_ENV === 'pre') {
  host = 'http://www.domesy.cn:8083';
}

export { host };

11.其余配置

除此之外,模板里也提供了直接修改的地方

文件位置:config/defaultSettings.ts

import { Settings as LayoutSettings } from '@ant-design/pro-layout';

const Settings: LayoutSettings & {
  pwa?: boolean;
} = {
  navTheme: 'dark', // 主题颜色
  primaryColor: '#1890ff', //颜色
  layout: 'side', // 菜单模式 side:右侧导航,top:顶部导航
  contentWidth: 'Fluid', // 内容模式 Fluid:自适应,Fixed:定宽 1200px
  fixedHeader: false, // 是否固定头部
  fixSiderbar: true, // 是否固定导航
  colorWeak: false,
  // headerRender: false, // 是否拥有头部
  // menuRender: false, // 是否拥有菜单
  title: 'Domesy',
  pwa: false,
  iconfontUrl: '', //icon
};

export default Settings;

去除国际化(打造属于你的Ant Design Pro V5(二) - 掘金)

国际化是Ant Design Pro 一个非常强大的功能,但对国内的项目并不需要要国际化,所以当自己的项目不需要这个功能时,我们可以考虑去除这个功能

我们只需要执行 npm run i18n-remove 这个命令即可,但此时我们再将 local 删掉,还是会发现有大量的报错原因是代码中只用了 umi的 useIntl 这个方法, 那么现在只需要把文件的所有代码删除就可以了

小问题

当我们去除国际化后我们还是会遇见一些小问题

1.浏览器自带的翻译功能

这是因为在 src/page/document.ejs 文件中的 lang 是 en 的原因

我们需要将它改为zh-CN就行了

2.Ant Design 的部分组件会显示英文(如日期组件)

这时我们还需要在 config/config.js 中的 locale 配置 default: 'zh-CN' 即可

数据的装载与搬运---useModel

首先 V5 自带一个全局状态 (initialState),用官方的话说是:

initialState 在 v5 中替代了原来的自带 model,global,login,setting 都并入了 initialState 中。我们需要删除 src/models/global.ts,src/models/login.ts,src/models/setting.ts ,并且将请求用户信息和登陆拦截放到 src/app.tsx 中

理解下官方的话:

  • 首先,他是一个由官方内置的model,可以当做最外层的model
  • 其次,我们可以将全局的状态放进去,以供单独的文件去调用,如:用户信息,权限等级等~

看看如何使用吧

import { useModel } from 'umi';

export default () => {
  const { initialState, loading, error, refresh, setInitialState } = useModel('@@initialState');
  return <>{initialState}</>
};

使用起来非常的方便,先介绍下所有参数的用途

initialState : 返回全局状态,也就是 getInitialState 的返回值

setInitialState: (state:any) => 手动设置 initialState 的值,手动设置完毕会将 loading 置为 false.

loading: getInitialState 是否处于 loading 状态,在首次获取到初始状态前,页面其他部分的渲染都会被阻止。loading 可用于判断 refresh 是否在进行中。

error: 当运行时配置中,getInitialState throw Error 时,会将错误储存在 error 中。

refresh: () => void 重新执行 getInitialState 方法,并获取新数据。

如何独自创建 model

讲完了官方自带的 initialState,接下来我们单独讲讲如何创建属于自己的 model

这块没有太多要讲解的地方,我们直接上代码吧~,以最简单的计时器即可

首先我们需要在 src/model 创建自己模块

文件位置 src/models/test/modelTest.ts

import { useState, useCallback } from 'react';

interface Props {
  count?: number
}

const initInfoValue: Props = {
  count: 1,
}

export default function modelTest() {

  const [init, setInitValue] = useState(initInfoValue);
  const [loading, setLoading] = useState(false);

  const waitTime = (time: number = 2000) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(true);
      }, time);
    });
  };

  const setInit = useCallback(async(res:any) => {
    setLoading(true)
    await waitTime()
    setLoading(false)
    setInitValue({count: res})
  }, [init])

  const setAdd= useCallback((res:any) => {
    setInitValue({ count: res +1})
  }, [init])

  return {
    loading,
    init,
    setAdd,
    setInit
  };
}

然后在所需的页面直接通过useModel,获取就 OK 了

import React from 'react';
import { useModel } from 'umi';
import { Button } from 'antd';

export const MockModel: React.FC<any> = () => {
  const { init, setInit, setAdd, loading } = useModel('test.modelTest');

  return <div>
    <div style={{ marginBottom: 14 }}> count 对应的值{init.count}</div>
    <Button loading={loading} style={{ marginBottom: 18 }} type='primary' onClick={() => setInit(5)} >设置count为5</Button>
    <br />
    <Button type='primary' onClick={() => setAdd(init.count)} >累加1</Button>
  </div>
}

总的来说,只要把对应的方法,值全部返回,然后在调用就OK了,是不是很简单~

性能优化

当我们存在 model 的数据越来越多,就需要使用 useModel 的第二个可选参数来进行性能优化,只消费model 中的部分参数,而不使用其他参数,并返回的值则是 useModel 最终的返回值

我们以上述为例

路由详解(动态菜单)

路由的配置

首先,所有的路由都在config/routes下,我们配置路由都在此文件下进行

我们先建一个一级目录

export default [
  {
     path: '/test',
     name: '一级目录',
     icon: 'smile',
     component: './Welcome'
  }
]

看看此时实现的效果

而多级目录只需要使用 routes 这个参数即可,其他配置一样

我们在这里在创建一个二级目录,在创建一个二级目录的子目录,我们希望由二级目录跳入这个子目录,但在菜单上不显示,这时我们只需要使用hideInMenu即可在菜单上隐藏,但我们会发现变成了这样

 这时我们发现了两个问题:一个是左边的菜单栏并没有高亮,二是面包屑展示的不对,这是因为我们写的路劲不对,我们需要把这个子页面挂载二级目录的下面就能完美解决了~

export default [
	{
    path: '/test',
    name: '一级目录',
    icon: 'smile',
    routes: [
      {
        path: '/test',
        redirect: '/test/twotest',
      },
      {
        path: '/test/twotest',
        name: '二级目录',
        component: './Welcome',
      },
      {
        path: '/test/twotest/threetest',
        name: '二级目录的子页面',
        component: './Welcome',
        hideInMenu: true
      }
    ]
  }
]

效果:

我们再来总结下常用路由的参数(其余的参数可看官网):

  • path: 地址栏的访问路径

  • name : 名称

  • icon:前面的小图标

  • component:对应的文件夹目录

  • redirect:重定向后的地址

  • authority:权限,大型项目不建议使用,直接用动态菜单即可

  • hideInMenu: 是否影藏菜单栏

  • routes:对应的子路由

动态菜单

在 V5 中,提供两种,一种是 权限, 一种是动态菜单,在这里建议使用动态菜单,所以只介绍下动态菜单的用法。

所谓动态菜单,需要接口的配合,返回什么菜单就展示什么,在本人的案例中,我通过 mock 模拟出数据,并将它放入 utils/initData 中

使用中发现的问题:

  1. 动态路由里的地址只能根据已配置原有的路由去打包,如果没有则会出现问题,(也就是说,不管有没有动态路由,都要在原有的路径里写上对应的名字)
  2. 其中配置的 component 无用,redirect的也无用。
  3. 动态路由的 icon不显示
  4. 动态路由就算隐藏了对应的路径,也可以通过地址栏输入地址进入到该页面
  5. redirect 重定向不管用,就算设置上也是默认原有的路由的重定向定义的,这样点击头部的时候会跳转到原有的页面
  6. 登录,登录的时候跳转的页面如果没有重定向地址,会跳转 / ,应为动态路由里的重定向不管用,所以会跳向原有的/页面

如有写的不对请留言指出~

解决方法:

针对问题1和问题2,我们必须跟后端协商好,这个路由必须与接口返回的字段对应,并且只需要返回对应的名称、icon、路径即可

问题3,我们需要单独写个方法来适配就行了

使用: menuData: formatter(menuData.data)

const formatter = (data: any[]) => {
  data.forEach((item) => {
    if (item.icon) {
      const { icon } = item;
      const v4IconName = toHump(icon.replace(icon[0], icon[0].toUpperCase()));
      const NewIcon = allIcons[icon] || allIcons[''.concat(v4IconName, 'Outlined')];

      if (NewIcon) {
        try {
          // eslint-disable-next-line no-param-reassign
          item.icon = React.createElement(NewIcon);
        } catch (error) {
          console.log(error);
        }
      }
    }

    if (item.routes || item.children) {
      const children = formatter(item.routes || item.children); // Reduce memory usage

      item.children = children;
    }
  });
  return data;
};

const toHump = (name: string) => name.replace(/-(\w)/g, (all: string, letter: any) => letter.toUpperCase());

针对问题5和问题6,我们进行详细的描述下:

比如说我现在原有的页面配置上A页面(第一个页面),但我在其权限下不想展示A页面,不显示的时候,就会出现这个问题

解决方法

在点击头部的方法和登录的方法(不包括重定向)跳转到获取路由的第一个上,并且,将取消原有路由的重定向。在getInitialState上统一设置,如果路径是/则自动获取第一个参数的路径,就能解决了~

链接的桥梁--网络请求

网络请求可以说是前端与后端的,我们需要这个桥将后端绑定起来

对于一个系统来说,请求的方法与接受的参数都是统一的,所以我们需要集中配置我们的请求模块,来适配自己的系统。

在 V5 中设置请求的模块在src/app.tsx

在V5中我们需要在 umi 中引入,并且相对于 V4 ,V5扩张了一个配置 skipErrorHandler, 这个配置的作用是:跳过默认的错误处理,用于处理特殊的接口

import { request } from 'umi';

request('/api/user', {
  params: {
    name: 1,
  },
  skipErrorHandler: true,
});

 在 Ant Design Pro 中,可以封装一个请求工具类来统一管理网络请求

,并提供一些公共的配置和方法。以下是一个简单的封装示例:

// request.js

import { extend } from 'umi-request';

const request = extend({
  // 全局配置
  timeout: 10000, // 请求超时时间
  headers: {
    'Content-Type': 'application/json', // 默认请求头
  },
});

// 请求拦截器
request.interceptors.request.use((url, options) => {
  // 添加请求头、请求体等处理逻辑
  return {
    url,
    options,
  };
});

// 响应拦截器
request.interceptors.response.use(async (response) => {
  // 处理响应数据
  const data = await response.clone().json();
  // 如果响应状态码不为 200,抛出异常
  if (response.status !== 200) {
    throw new Error(data.message || '请求失败');
  }
  // 如果响应状态码为 200,返回响应数据
  return data;
});

export default request;

在上述代码中,我们使用 extend 方法创建了一个全局的 request 实例,并进行了一些全局配置,如超时时间和默认请求头。然后,通过 interceptors 方法设置了请求拦截器和响应拦截器,用于在请求和响应过程中进行一些公共处理逻辑。

接下来,在需要进行网络请求的文件中,可以直接引入封装好的 request 对象,并使用它来发送请求

import request from '@/utils/request';

request('/api/user').then(response => {
  console.log(response);
}).catch(error => {
  console.error(error);
});

 这样,我们就可以通过封装的 request 对象来发送请求,并在请求和响应过程中进行一些公共处理。同时,也可以根据实际需求,进一步封装一些常用的请求方法,如 GET、POST 等,以便更方便地使用

如果需要在请求中传递参数,可以在调用 request 方法时,传递一个 params 参数。params 参数是一个对象,其中的键值对表示请求的参数名和参数值。

以下是一个示例:

import request from '@/utils/request';

request('/api/user', {
  params: {
    id: 1,
    name: 'John',
  },
}).then(response => {
  console.log(response);
}).catch(error => {
  console.error(error);
});

在上述代码中,我们通过传递 params 参数来设置请求的参数。在请求的 URL 中,会自动将参数拼接在 URL 后面,形成类似 /api/user?id=1&name=John 的形式。

如果需要发送 POST 请求,可以将请求的方法设置为 post,并将参数放在 data 参数中。例如:

import request from '@/utils/request';

request('/api/user', {
  method: 'post',
  data: {
    id: 1,
    name: 'John',
  },
}).then(response => {
  console.log(response);
}).catch(error => {
  console.error(error);
});

 在上述代码中,我们将请求的方法设置为 post,并将参数放在 data 参数中。这样,参数会以 JSON 格式发送到服务器。

需要注意的是,request 方法的第二个参数是一个配置对象,可以设置请求的方法、请求体、请求头等。具体的配置项可以参考 umi-request 的官方文档:https://github.com/umijs/umi-request

 进一步封装成get 和 post 请求 加tocken

要在请求中添加身份验证令牌(token),你可以在请求头中设置一个名为 `Authorization` 的字段,并将令牌值作为其值。这样,在发送请求时,服务器就可以验证该令牌来进行身份验证。

下面是如何修改 `request.js` 文件来添加身份验证令牌的示例:


import { extend } from 'umi-request';

const request = extend({
  // 全局配置
  timeout: 10000, // 请求超时时间
  headers: {
    'Content-Type': 'application/json', // 默认请求头
  },
});

// 请求拦截器
request.interceptors.request.use((url, options) => {
  // 添加请求头、请求体等处理逻辑
  const token = localStorage.getItem('token'); // 从本地存储中获取令牌
  if (token) {
    options.headers['Authorization'] = `Bearer ${token}`; // 添加身份验证令牌
  }
  return {
    url,
    options,
  };
});

// 响应拦截器
request.interceptors.response.use(async (response) => {
  // 处理响应数据
  const data = await response.clone().json();
  // 如果响应状态码不为 200,抛出异常
  if (response.status !== 200) {
    throw new Error(data.message || '请求失败');
  }
  // 如果响应状态码为 200,返回响应数据
  return data;
});

// 封装 GET 请求
export const get = (url, params) => {
  return request(url, {
    method: 'get',
    params,
  });
};

// 封装 POST 请求
export const post = (url, data) => {
  return request(url, {
    method: 'post',
    data,
  });
};


 

在这个示例中,我们在请求拦截器中添加了一个逻辑,用于从本地存储中获取令牌,并将其添加到请求头的 `Authorization` 字段中。我们使用 `Bearer` 方案作为身份验证令牌的类型,这是一种常见的做法,你也可以根据你的需求进行调整。

在实际使用时,你需要在用户登录成功后将令牌存储在本地存储中(例如使用 `localStorage.setItem('token', token)`),并在用户退出登录或令牌过期后将其删除(例如使用 `localStorage.removeItem('token')`)。

请注意,这只是一个示例,你需要根据你的身份验证机制和实际需求进行适配和修改。

在上述代码中,我们通过封装 get 和 post 方法,将请求的方法和参数进行了封装。这样,在项目中使用时,只需引入相应的方法,并传递对应的 URL 和参数即可。

以下是一个使用示例:

import { get, post } from '@/utils/request';

// 发送 GET 请求
get('/api/user', { id: 1, name: 'John' })
  .then(response => {
    console.log(response);
  })
  .catch(error => {
    console.error(error);
  });

// 发送 POST 请求
post('/api/user', { id: 1, name: 'John' })
  .then(response => {
    console.log(response);
  })
  .catch(error => {
    console.error(error);
  });

 通过封装的 get 和 post 方法,可以更方便地发送 GET 和 POST 请求,并且可以在请求和响应过程中进行一些公共处理。

统一地址

在上篇文章介绍了分模块打包 其本质就是通过不同的命令,打出不同的包,请求的接口就需要在这里通过prefix来配置

export const request: RequestConfig = {
  prefix: process.env.NODE_ENV === "production" ? host : '/api/',
};

拦截器

每个系统对应的后端请求都不相同,所以应该在请求前和请求后做一些特定的处理,以此来帮助我们快速开发,比如:在请求的时,请求头上加入 token

因此,V5提供了两种方式,一是中间件(middlewares),另一种则是拦截器

这两种方式都可以优雅地做网络请求前后的增强处理,但中间件使用起来比较复杂,所以这里只介绍拦截器

请求拦截--requestInterceptors

首先,我们来说说请求拦截需要配置什么

  • 后端的不同发送网络的格式,方式都不通,比如说配置 Content-Type
  • 此外,现在大多数项目都会有一个token,用来判断

这里要说明一点 token 通常需要存储到本地的,原因是每次启动项目都会用到,当然缓存能少用就少用,尽量使用数据流做处理。

因此 我们需要设置一个变量来存储 token

另外我们需要注意一点,在未登录的时候并无token,并且在退出登录后,要清空缓存

废话有点多~ 直接看代码吧~

/**请求拦截 */
export const requestInterceptors: any = (url: string, options: RequestInit) => {
  if (storageSy.token) {
    const token = `Bearer ` + localStorage.getItem(storageSy.token);//存储的token
    options.headers = {
      ...options.headers,
      "Authorization": token,
      'Content-Type': 'application/json',
    }
  }
  return { url, options };
}

响应拦截 responseInterceptors

跟请求拦截一样,我们先来说说响应拦截做的的做了什么吧

  • 统一的错误处理,如:在网络不好的情况下,请求不到数据,这时我们可以给一个统一的提示语来告诉用户
  • 统一报错,有的时候返回的状态是不正常的,这时我们就可以做处理,给出接口给的提示语,并且我在这里统一设置了一下,如果返回的不是 200(成功)将统一设置为 false, 这样就不需要用 catch 来进行捕获了。
  • 用户登录时限,一个系统中,我们希望用户登录这个是有时效性,这是接口就回返回特定的状态码,来告诉我们用户信息不匹配,或者登录时间到了,这时我们需要在响应拦截中给出对应的提示,并清空缓存,退出系统 关于第二点,其实有个小问题,就是他什么都不返回,但状态码为 200 ,这时在具体页面中如何判定成功呢,其实只要判定 返回的类型 不等于 布尔值就行了
// 响应拦截
export const responseInterceptors:any = async (response: Response) => {
  if (!response) {
    notification.error({
      description: '您的网络发生异常,无法连接服务器',
      message: '网络异常',
    });
    return;
  }
  const data = await response.clone().json();
  if ([10001,10008].includes(data.resultCode)) {
    message.error(data.message);
    localStorage.clear();
    return false;
  }
  if (data.code !== 200) {
    message.error(data.message);
    return false;
  }
  return data.data;
}

数据模拟--mock

mock 是什么呢? 他是模拟接口请求的数据,并且可以随机生成测试数据,当后端还未好时,我们可以与后端沟通变量的名称,之后再靠 mock 来模拟数据,实现开发,等后端好了,直接替换接口就行了~~

文档请参考: mock官网

mock服务

mock 是模拟的接口,所以在正式打包后,mock 数据是无法使用,那么如果在开发时候不用mock数据,该怎么处理呢?

命令 npm run start:no-mock 就行

独立的mock服务

我们知道,有的时候 mock 数据 是可以和真实的 Api 请求并存的

但在打包后 mock 是无法使用的,那么能否启动一个 mock 服务呢?然后通过 nginx 代理到这个mock服务呢?

官方给出了一种方法: umi-serve

安装命令 yarn add global umi-serve

为了方便起见

我们可以再 package.json 中的 script 中加入 "serve":"umi-serve" 即可

下次启动 umi-serve 服务,只需在控制台中输入:npm run serve ,即可。

GET请求和POST请求

这块内容比较简单,就没必要说了,直接提供两种请求方式就ok了

  'GET /api/form/queryDetail': async (req: Request, res: Response) => {
    const { detail } = req.query;
    if (detail === 'introduce') {
      res.send(
        resData({
          list: introduce,
          anchorList: introduceAnchorList
        }
      ))
    } 
    
    res.send({
      code: 400,
      detail,
      message: '请输入参数'
    })
  }
  
    'POST /api/domesy/queryDetail': async (req: Request, res: Response) => {
    const { detail } = req.query
    if(detail === 'welcome') {
      res.send(
        resData({
          list: welcome,
          anchorList: welcomeAnchorList
        }
      ))
      return
    }

    res.send({
      code: 400,
      detail,
      message: '请输入参数'
    })
  },

Ant Design Pro V5(三)(打造属于你的Ant Design Pro V5(三) - 掘金)

文件结构

├── config                   # umi 配置,包含路由,构建等配置
├── mock                     # 本地模拟数据
├── public
│   └── favicon.png          # Favicon
├── src
│   ├── assets               # 本地静态资源
│   ├── commonPages          # 公共页面
│   ├── components           # 业务通用组件
│   ├── e2e                  # 集成测试用例
│   ├── layouts              # 通用布局
│   ├── models               # 全局 dva model
│   ├── pages                # 业务页面入口和常用模板
│   ├── services             # 后台接口服务
│   ├── utils                # 工具库
│   ├── locales              # 国际化资源
│   ├── global.less          # 全局样式
│   └── global.ts            # 全局 JS
├── tests                    # 测试工具
├── README.md
└── package.json
 

 V5 页面执行顺序

1.首先执行

我们先看看 /scr/app.tsx 这段代码

 

 首先页面打开(无论哪个页面都会执行),会执行 app.tsx 里的 getInitialState ,然后走向queryCurrentUser 这个函数,在这个函数上他会判断 access 是否存在,如果不存在则会报错,发送状态码为401, 然后就会走向登录页面,反之则会停留在当前页面

2.抵达登录页面

到达登录页面后。输入账号密码时走向 fetchUserInfo 这个方法,这个方法主要的作用是存储了一开始登录接口所需的函数

注意,这里进去的页面不会执行 getInitialState 这个函数,所以再次要执行获取用户信息的方法

3.页面切换

我们做的页面会放在浏览器上,我们需要登录信息才能够打开,但由于许多外部原因会导致存储的信息发生变化,如清储缓存,时间过长导致登录信息失效等,那么在 V5 中如何判断的呢?

会将登录信息放在 initialState 中,我们需要在 app,tsx 中的 onPageChange 这个方法里

这个方法是通过页面转换而触发的,在这里也会判断用户信息是否存在,如果不存在,则会重新跳转登录界面

其他

 @ 做了什么

我们随意的可以看见类似这样的引入

import Footer from '@/components/Footer';

那么 @是干嘛的呢?

其实在项目中引入分为绝对路径和相对路径,我们通常将组件放置在 component 模块下,配置放置在 utils模块下,那么 @ 实际上就是 相当于绝对路径 也就是 /src 的作用,他就是别名

帮助我更快速的引入,详细的可参考 webpack别名设置

@@initialState

@@initialState是一个特殊的关键字,用于在UmiJS中获取应用程序的初始状态。它是通过使用useModel钩子来获取的。

在UmiJS中,可以通过在src/app.tsx文件中的getInitialState函数中设置初始状态。这个函数会在应用程序初始化时被调用,并返回一个包含特定结构的对象,表示应用程序的初始状态。

使用@@initialState关键字和useModel钩子,可以在任何组件中获取和使用应用程序的初始状态。例如,在上面提到的代码中,通过const { initialState, setInitialState } = useModel('@@initialState'),我们可以获取到应用程序的初始状态,并在组件中使用它。

这样做的好处是,我们可以在应用程序的任何地方获取和使用初始状态,而不需要显式地传递它。这使得状态管理更加方便和灵活。

希望这样解释清楚了@@initialState的作用。如果还有其他问题,请随时提问!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
回答: 根据引用\[1\],报错信息显示在`./src/components/Swiper.vue`文件中找不到依赖项`swiper`和`swiper/css/swiper.css`。根据引用\[2\],可以通过在文件中引入`vue-awesome-swiper`和相应的样式文件来解决这个问题。具体的引入方式如下所示: ```javascript import Vue from 'vue' import VueAwesomeSwiper from 'vue-awesome-swiper' // 引入样式(Swiper 6.x及以上版本) import 'swiper/swiper-bundle.css' // 引入样式(Swiper 5.x及以下版本) import 'swiper/css/swiper.css' Vue.use(VueAwesomeSwiper, /* { default options with global component } */) ``` 根据引用\[3\],还需要在组件中引入`Swiper`和相应的样式文件,并在`mounted`钩子函数中实例化`Swiper`对象。具体的代码如下所示: ```vue <template> <div class="swiper-container"> <div class="swiper-wrapper"> <slot></slot> </div> </div> </template> <script> import Swiper from 'swiper' import 'swiper/css/swiper.css' export default { mounted() { new Swiper(".swiper-container") } } </script> ``` 最后,将封装的`Swiper`组件引入到需要使用的页面中即可解决问题。 #### 引用[.reference_title] - *1* *2* [Vue中引入swiper报错--已解决](https://blog.csdn.net/darabiuz/article/details/120930860)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [关于在VUE中封装一个Swiper组件的方法-----详解](https://blog.csdn.net/weixin_46058357/article/details/104591118)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yzhSWJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值