2. 底部导航栏的制作 - LoveVin/billkeeping-react GitHub Wiki

1. 安装 react router

react-router官网 查看对应的安装方法。

yarn add react-router-dom

yarn add --dev @types/react-router-dom

2. CRM官网例子

  • 从官网的基本例子中 CRM 修改
  • 设置默认路径: <Redirect exact from="/" to="money" />
  • 设置404路径:<Route path="*"> <NoMatch /> </Route>

3. Router 的两种模式

Router 有三种模式,常用的有两种,一个是 History模式,一个是 Hash模式

  • 没有后台服务器,用 Hash模式
  • 有后台服务器,配置所有路劲都到首页,使用 History模式

在本项目使用的是 Hash 模式:

import 中的 BrowserRouter as Router, 改为 HashRouter as Router,

看到 url 前都带有了 # 就是设置 hash模式成功

4. 封装 Nav组件

Nav导航栏 可以抽离成一个单独的 .tsx 组件。该组件中使用 styled-components 来自定义样式:

const NavWrapper = styled.div`
  line-height: 24px;
  box-shadow: 0 0 3px rgba(0,0,0,0.25);
  >ul{
    display:flex;
    >li{
      width: 33.333%;
      text-align: center;
      padding: 16px;
    }
  }
`;

5. svg 图片的引入

svg 图片是项目开发过程中使用最佳的图标格式,本项目在 iconfont 中选择的图标。选择好svg图片后,有两种引入方式:

  • 直接使用 img标签
import x from 'icons/money.svg';
console.log(x);  //输出是 webpack 计算出来的图片路径

<img src={x} alt=""/>

缺点:无法直接修改图标颜色,需要在 svg 的源文件里修改或添加 fill="red" 属性。

  • 使用 svg symbol 的方式

这种方式需要安装一个 loader,叫做 svg-sprite-loader

但是在项目环境中要额外加载 loader 需要在原始的 webpack 中加载,但是 cra(create-react-app) 将其 webpack 配置信息封装了,因此为了添加 svg-sprite-loader,我们需要调出原始的 webpack 配置,使用 eject,使用 eject 前先提交所有代码。

yarn eject

然后会出现询问是否要 eject,这种行为是永久的,选择是(y)

eject 完成后会在项目中出现两个新的文件夹叫做 “config” 和 “scripts”,此时 webpack 的配置文件(webpack.config.js)就在 "config" 文件夹中。所有的 loader 要在该配置文件中配置。

然后开始安装 svg-sprite-loader ,一般安装 webpack或typescript 相关的东西都是 --dev 安装的。

yarn add --dev svg-sprite-loader

安装完后,查看 package.json 中对应的 webpack 版本号,按照 svg-sprite-loader 官方文档说的,配置 webpack.config.js。

//添加如下代码到 webpack.config.js 的 rules 中的 oneOf 中,这里面是专门放 loader 的
{
  test: /\.svg$/,
  use: [
    { loader: 'svg-sprite-loader', options: {} },
    'svg-transform-loader',
    'svgo-loader'
  ]
}

该配置语句涉及到三个 loader,分别是 svg-sprite-loader 、svg-transform-loader 和 svgo-loader。这三个的作用可分别查看对应的官网那。在这里,只需要用到 svg-sprite-loader 和 svgo-loader。svgo-loader 是用于对 svg 图片进行优化的。

{
  test: /\.svg$/,
  use: [
    { loader: 'svg-sprite-loader', options: {} },
    { loader: 'svgo-loader', options: {
         plugins: [
              {removeAttrs: {attrs: 'fill'}},
         ]
      } 
    }
  ]
}

安装 svgo-loader

yarn add --dev svgo-loader

安装好 svg-sprite-loader 和 svgo-loader 并配置好对应的 webpack 后,

因为配置了 webpack,一定要 yarn start 重启项目才有效!!!重启!重启!重启!

即可使用 svg symbol,使用方法如下:其中 xlinkHref 属性值为 引入的 svg 图标的文件名加#。

import x from 'icons/money.svg';
console.log(x);

<svg>
   <use xlinkHref="#money" />
</svg>

可以比较灵活地控制 svg 图片,如添加颜色:

<svg fill="red" className="icon">
   <use xlinkHref="#money" />
</svg>

但是使用 import 的方式引入 svg文件时,由于 TreeShaking 现象的存在,如果变量不被引用,就会被渲染树自动移除,就不会正常出现 svg 图标,解决这种现象的方法就是将 import 引入改成 require 引入。因为 TreeShaking 不适用于 require

require( 'icons/money.svg');

6. 封装 Icon 组件

看到写了三遍以上的重复代码要将其简化,抽离成一个组件,避免重复代码的出现。在这里图标的加载重复了三遍,可将 svg图标 抽离成一个 Icon.tsx 组件,通过传递外部属性值图标文件的名字来显示图标:

require( 'icons/money.svg');
require('icons/tag.svg');
require('icons/chart.svg');

type Props = {
    name: string
}

const Icon = (props: Props) => {
    return (
        <svg className="icon">
            <use xlinkHref={'#' + props.name}/>
        </svg>
    );
};

7. 优化多个 require 语句

在上述的 Icon 组件中需要手动引入多个 svg 文件,当 svg 文件过多时,就会有多个 require 语句,这里使用引入目录的方式,将上述的三个 require 语句改为如下代码。

let importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext);
try {importAll(require.context('icons', true, /\.svg$/));} catch (error) {console.log(error);}

此时的 __WebpackModuleApi 会报错找不到,需要引入 typescript 的某个声明:

yarn add --dev @types/webpack-env

8. 抽离 Layout组件

首先,并不是每一个页面都需要有 Nav组件 的,因此不能将 Nav组件 放在 标签里。需要将其单独抽离成一个组件,每一个页面需要时单独加载。

const Layout = (props: any) => {
    return (
        <Wrapper>
            <Main>
                {props.children}
            </Main>
            <Nav/>
        </Wrapper>
    );
};

//使用组件
<Layout>
  <h2>统计页面</h2>
</Layout>

9. 高亮 svg 路由图标

在 Nav 导航栏组件中,在点击切换页面的图标时,需要高亮显示。这就涉及到为 svg 图标自定义颜色样式。导航栏链接不仅包含图标,还包含文字,导航栏链接是由 react-router 的 Link 标签 设置的,那么如何使 Link 元素高亮呢?这就涉及到两个方面,首先必须知道该标签是否被选中,其次是设置选中后的颜色。

  • react-router 的 active class 功能(判断是否选中 selected)

进去 react router 的官网,搜索 active class 相关的使用方法。可以看到 active class 功能只有在 NavLink 标签内有,作用就是在被选中时添加 selected 类。

<NavLink to="/tags" activeClassName="selected">
    <Icon name="tag"/>
    标签
</NavLink>
  • svgo-loader 去除 svg 图片的 fill 属性(设置高亮颜色)

首先是 NavLink 标签的文字变红,react router 中的 NavLink 标签被编译后就是 a 标签,因此在 a 标签中设置样式:

> a{
  &.selected{
    color: red;
    .icon{
       fill: red;
    }
  }
}

上述方法对于没有带有 fill 属性的 svg 图片来说是会被当成文字变红高亮的,但是对于一些带有 fill 属性的 svg 资源,上述自定义颜色不起作用。这就需要用到 svgo-loader,svgo-loader 是优化 svg 资源的,有提供删除属性的功能。查看 svgo-loadersvgo 的相关选项配置。添加如下:

{ loader: 'svgo-loader', options: {
    plugins: [
        {removeAttrs: {attrs: 'fill'}},
    ]
} }

遇到的问题

  1. 在 yarn eject 后再启动 yarn start 时,遇到报错 Cannot find module '@babel/helper-create-regexp-features-plugin',表示缺少改模块,可手动安装:yarn add @babel/helper-create-regexp-features-plugin。或者将 node_modules 重新删除再重新安装
rm -rf node_modules
yarn install
  1. 在该项目中,为了兼容移动端的字体,使用了 font.css中文 来使得在不同平台上都可以有很好的字体显示效果。具体做法是在 helper.scss 中将字体样式定义成了一个变量。
⚠️ **GitHub.com Fallback** ⚠️