完成英雄页banner - 图1

这是一篇纯写样式的代码,旨在让你再次熟悉 umi 的数据流,和页面渲染。如果你能够独立完成上面的效果,那你就不用继续阅读这篇文章了。

1. 添加mock数据

新建 ./mock/api.ts

  1. import herolist from './herolist.json';
  2. export default {
  3. 'POST /apimock/freeheros.json': (req, res) => {
  4. const { number } = req.body;
  5. function getRandomArrayElements(arr, count) {
  6. var shuffled = arr.slice(0),
  7. i = arr.length,
  8. min = i - count,
  9. temp,
  10. index;
  11. while (i-- > min) {
  12. index = Math.floor((i + 1) * Math.random());
  13. temp = shuffled[index];
  14. shuffled[index] = shuffled[i];
  15. shuffled[i] = temp;
  16. }
  17. return shuffled.slice(min);
  18. }
  19. const freeheros = getRandomArrayElements(herolist, number);
  20. res.send(freeheros);
  21. }
  22. }

这里我们增加了周免英雄的接口,从英雄池里面随机取出了几个对象,这个方法是我找来的,旨在做一个简单的演示,不要太在意,如果你有更好的方法,完全可以用你自己的。

修改代理,把匹配前缀改成 /api/,这样最终发出的请求如果是 /api/ 将会使用代理,如果是 /apimock/ 将会使用mock 数据,这是一种小手段。

  1. proxy: {
  2. - "/api": {
  3. + "/api/": {
  4. - "target": "https://pvp.qq.com",
  5. + "target": "https://pvp.qq.com/",
  6. "changeOrigin": true,
  7. "pathRewrite": { "^/api/": "" }
  8. }
  9. }

请求地址前缀是 /apimock 这里是为了让代理和mock数据同时生效。因为我没有自己的服务器,代理的数据是来自腾讯游戏,mock数据在本地。

2. 在 model 中请求数据

./src/pages/hero/models/hero.ts

  1. import { Effect, Reducer, Subscription, request } from 'umi';
  2. export default {
  3. state: {
  4. heros: [],
  5. + freeheros: [],
  6. filterKey: 0,
  7. + itemHover:0 //因为周免英雄列表里面有一个一直是详情图,所以这里给一个标记
  8. },
  9. subscriptions: {
  10. ...
  11. },
  12. reducers: {
  13. ...
  14. },
  15. effects: {
  16. *fetch({ type, payload }, { put, call, select }) {
  17. const herolist = yield call(queryHeroList);
  18. const herodetails = yield call(getHeroDetails, { ename: 110 });
  19. + const freeheros = yield request('mock/freeheros.json', {
  20. + method: 'POST',
  21. + headers: {
  22. + Accept: 'application/json',
  23. + 'Content-Type': 'application/json; charset=utf-8',
  24. + },
  25. + body: JSON.stringify({
  26. + number: 10,
  27. + }),
  28. + });
  29. yield put({
  30. type: 'save',
  31. payload: {
  32. heros: herolist,
  33. + freeheros: freeheros
  34. },
  35. });
  36. },
  37. },
  38. };

4. 编写FreeHeroItem组件

./src/components/FreeHeroItem.tsx

  1. import React, { FC } from 'react';
  2. import { HeroProps } from 'umi';
  3. interface FreeHeroItemProps {
  4. data: HeroProps;
  5. thisIndex: number;
  6. onItemHover: (thisIndex: number) => void;
  7. itemHover: number;
  8. }
  9. const FreeHeroItem: FC<FreeHeroItemProps> = ({ data, thisIndex, onItemHover, itemHover }) => {
  10. if (!data || !data.ename) return null;
  11. return (
  12. <img
  13. onMouseEnter={() => {
  14. itemHover !== thisIndex && onItemHover(thisIndex);
  15. }} //步骤7 需要
  16. style={{
  17. borderRadius: '5px',
  18. height: '69px',
  19. margin: '5px',
  20. width: itemHover === thisIndex ? '224px' : '69px',
  21. }}
  22. src={`https://game.gtimg.cn/images/yxzj/img201606/heroimg/${data.ename}/${data.ename}${
  23. itemHover === thisIndex ? '-freehover.png' : '.jpg'
  24. }`}
  25. />
  26. );
  27. }
  28. export default FreeHeroItem

因为这个组件的样式很少,所以我们把样式写在 style 中,这样可以减少一个 less 文件。

修改 src/models/hero.ts ,将 HeroProps 暴露出来,这样就可以从 umi 引入了。因为 plugin-dva 的功能, models 中导出的类型和方法,都会通过 umi 导出。

  1. - interface HeroProps {
  2. + export interface HeroProps {
  3. ename: number;
  4. cname: string;
  5. title: string;
  6. new_type: number;
  7. hero_type: number;
  8. skin_name: string;
  9. }

5. 在页面中渲染数据

./src/pages/hero.tsx

  1. import FreeHeroItem from '@/components/FreeHeroItem';
  2. // 这里的 @ 标示 src目录,这是umi中自带的别名。
  3. ... ...
  4. const { heros = [], filterKey = 0, freeheros = [] ,itemHover=0} = hero;
  5. ... ...
  6. return (
  7. <div className={styles.normal}>
  8. <div className={styles.info}>
  9. <Row className={styles.freehero}>
  10. <Col span={24}>
  11. <p>周免英雄</p>
  12. <div>
  13. {freeheros.map((data,index) => (
  14. <FreeHeroItem
  15. data={data}
  16. itemHover={itemHover}
  17. onItemHover={onItemHover}
  18. thisIndex={index}
  19. key={index}
  20. />
  21. )}
  22. </div>
  23. </Col>
  24. </Row>
  25. </div>
  26. ... ...
  27. </div>
  28. )

6. 为页面添加样式

./src/pages/hero.less

  1. .normal {
  2. background: url(//game.gtimg.cn/images/yxzj/web201605/top_banner/bg_wrapper2.jpg) no-repeat center
  3. top;
  4. padding-top: 500px;
  5. }
  6. .info {
  7. padding: 8px 20px 0;
  8. margin-bottom: 20px;
  9. .freehero {
  10. overflow: hidden;
  11. width: 100%;
  12. border-radius: 5px;
  13. height: 115px;
  14. padding: 0 15px;
  15. background-color: #123564;
  16. p {
  17. color: #a6afbc;
  18. margin: 5px;
  19. }
  20. }
  21. }

7. 为页面增加响应事件

./src/components/FreeHeroItem.tsx

  1. // 详细代码见步骤4
  2. + onMouseEnter={() => {
  3. + itemHover !== thisIndex && onItemHover(thisIndex);
  4. + }}

./src/pages/hero.tsx

  1. const onChange = e => {
  2. ...
  3. };
  4. + const onItemHover=e=>{
  5. + dispatch({
  6. + type: 'hero/save',
  7. + payload: {
  8. + itemHover: e
  9. + },
  10. + });
  11. + }
  12. return (
  13. //这里面的详细代码见步骤5
  14. )