在 create-react-app 中使用 Ant Design Landing 模板:完整集成与多页面路由实战指南
在 create-react-app 中使用 Ant Design Landing 模板:完整集成与多页面路由实战指南
导读
create-react-app 是业界最优秀的 React 应用开发工具之一。本文以 Ant Design Landing 项目中下载的 Home 模板包为核心,系统讲解如何在 create-react-app 创建的工程中完成目录合并、Less 加载配置、入口文件替换,并进一步实现基于 react-router-dom 的多页面导航。阅读完本文后,你将掌握一套可直接复制运行的集成方案,能够在自己的 CRA 项目中快速落地 Ant Design Landing 的落地页模板。
安装与初始化
create-react-app 的安装与初始化流程可参考其官方文档,或查阅 antd 官方的 use-with-create-react-app 章节。安装完成后,默认工程目录结构如下:
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── serviceWorker.js
Ant Design Landing 是一个基于 Ant Design 体系的落地页模板与在线编辑器项目(仓库即 ant-design-landing),其下载的模板包以 Home 为根目录,内部同时包含 .jsx 组件文件与 .less 样式文件。关于模板包的标准目录结构与数据文件约定,可进一步参考 开始使用文档:
│── less
│ ├── index.less
│ ├── common.less # 通用样式
│ ├── content.less # 内容模块通用样式
│ ├── custom.less # less 变量文件
│ ├── edit.less # 编辑器里编辑后生成的样式
│ ├── Banner0.less * # 相关模块样式
│ ├── Content0.less *
│ ...
│── data.source.js # 内容数据文件
│── index.js # 主入口
│── Banner0.jsx * # 相关模块
│── Content0.jsx *
│ ...
以上加
*号为可配置内容,当你在编辑器里选择了哪些模块,将对应出现相应的文件。
合并目录:将 Home 模板包拷贝进 src
直接从 Landing 上下载 Home 文件夹,拷贝到新建工程的 src 目录下:
├── README.md
├── ...
├── public
│ ├── ...
│── src
+ ├── Home
+ │ ├── less
+ │ ├── index.js
+ │ ├── ...
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── serviceWorker.js
从源码结构看,模板包中的 data.source.js 是内容数据文件,所有可编辑文案、图片地址与菜单配置都集中于此;组件内部通过 { ...Banner00DataSource.title } 之类的展开语法复用这些数据并以 props 传入。以仓库中的 Nav0 模板配置 为例,其 dataSource 中定义了 wrapper、page、logo、Menu、mobileMenu 等节点,Menu.children 数组中的每一项对应一个导航项,包含 name、className、children.href、children.children(文本内容)等字段。这正是后续路由改造时修改 data.source.js 中 href 值的依据所在。
安装依赖
详细依赖清单请参考 开始使用文档中的安装依赖章节。Ant Design Landing 依赖 ant design 的相关组件,动效依赖 ant motion 中的动效组件,基本必要依赖如下:
npm install antd enquire-js rc-queue-anim rc-scroll-anim rc-tween-one --save;
npm install rc-banner-anim --save;// 如果用的是多屏滑动型的 banner,加上这条;
npm install @ant-design/compatible --save;// 如果使用了 Content7 模块,请加上这条;
关于按需加载:umi 或 ant design pro 无需安装此项,CRA 场景下如使用
babel-plugin-import需要安装npm install babel-plugin-import --save-dev,且需注意:目前加载的是全部 ant design 的 less,若使用了babel-plugin-import,请将less/custom.less里的@import "~antd/lib/style/index.less";更换成@import "~antd/lib/style/themes/default.less";。
另外,为确保移动端响应式布局效果,需要在 html 文件的 head 里配置:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
less 加载支持
CRA 默认不支持 Less 预处理,需要安装以下工具链:
npm install react-app-rewired customize-cra less less-loader
react-app-rewired:在不执行eject的前提下覆写 CRA 的 webpack 配置;customize-cra:提供override、fixBabelImports、addLessLoader等便捷组合函数;less/less-loader:Less 编译工具链。
配置加载 less
安装完 less 加载相关依赖后,需要修改 package.json 中的启动配置,将 scripts 指向 react-app-rewired:
/* package.json */
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test",
+ "test": "react-app-rewired test",
}
创建 config-overrides.js
在项目根目录创建一个 config-overrides.js 用于修改默认配置:
├── README.md
├── node_modules
├── package.json
├── .gitignore
+── config-overrides.js
├── public
│ ├── ...
│── src
│ ├── Home
│ └── ...
config-overrides.js 详解
const { override, fixBabelImports, addLessLoader } = require('customize-cra');
module.exports = override(
// 按需加载 antd
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
}),
// 添加加载 less 的 javascriptEnabled 和 antd 的主题配置。
addLessLoader({
lessOptions: {
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1DA57A' },
}
}),
);
关键配置项说明:
| 配置项 | 作用 | 说明 |
|---|---|---|
fixBabelImports('import', {...}) |
antd 按需加载 | libraryName: 'antd' 指定组件库名,libraryDirectory: 'es' 指向 ES 模块目录,style: true 自动引入组件对应样式 |
addLessLoader({...}) |
注册 Less loader | 使工程中的 .less 文件能被 webpack 编译 |
javascriptEnabled: true |
允许 Less 中执行 JS 表达式 | Ant Design 的 Less 主题变量依赖该特性,关闭会导致编译失败 |
modifyVars |
Less 变量覆盖 | 用于自定义 antd 主题,示例将主色 @primary-color 改为绿色 #1DA57A |
本文只引用 antd 的高级配置,
eject同样不在本文讨论范围,eject可参考yarn run eject(对应 create-react-app 官方文档的 "Converting to a custom setup" 章节)。值得注意的是,Ant Design Landing 默认使用 Less 作为样式语言,且整个文件的 Less 未使用css-modules,需直接使用;如果你的脚手架启用了css-modules,请在index.less里用:global包裹 import 语句(详见 开始使用文档的样式章节)。
修改入口文件
打开 src/index.js,将默认的 App 组件替换为从 Landing 下载的 Home 组件并渲染:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
- import App from './App';
+ import App from './Home';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();
从模板包源码看,Home/index.js 是模板的主入口,内部负责汇总各模块组件(如 Banner0、Content0、Nav0、Footer0)并注入各自的 dataSource 数据。因此将 index.js 的默认导出指向 ./Home 后,整个落地页即可直接渲染。
完成单页集成
完成以上步骤之后,运行 npm start 即可在浏览器中查看从 Landing 下载的模板效果。
额外内容:多页面路由
如果只是单个落地页,以下内容可以略过。当需要将落地页整合进一个多页面站点时,需要引入路由。
安装 react-router-dom
create-react-app 本身不提供路由配置,因此需要安装 react-router-dom:
npm install react-router-dom --save
配置路由
将上述修改入口文件的改动恢复为原始状态(import App from './App'),然后对 App.js 进行改造:添加 Router 入口,并提取 Header(Nav0)与 Footer(Footer0)作为全局布局组件。
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { enquireScreen } from 'enquire-js';
import Header from './Home/Nav0';
import Footer from './Home/Footer0';
import Home from './Home';
import {
Nav00DataSource,
Footer00DataSource,
} from './Home/data.source.js';
let isMobile;
enquireScreen((b) => {
isMobile = b;
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
isMobile,
};
}
componentDidMount() {
// 适配手机屏幕;
enquireScreen((b) => {
this.setState({ isMobile: !!b });
});
}
render() {
return (
<Router>
<div>
<Header dataSource={Nav00DataSource} isMobile={this.isMobile} />
<Route exact path="/" component={Home} />
<Footer dataSource={Footer00DataSource} isMobile={this.isMobile} />
</div>
</Router>
);
}
}
export default App;
代码要点解析:
enquireScreen来自enquire-js(已在依赖安装步骤中引入),用于检测当前是否为移动端屏幕,并驱动模板组件的响应式切换;Nav00DataSource与Footer00DataSource从./Home/data.source.js导出,命名遵循「模块名 + 序列号 + DataSource」的约定(详见 开始使用文档的数据文件章节);- 从源码看,Nav0 组件 接收
dataSource与isMobile两个核心 props:dataSource控制导航内容与动效参数,isMobile决定菜单以inline模式还是horizontal模式渲染;Footer0 组件 同样通过dataSource驱动版权信息与滚动动效。这正是为什么在路由方案中要将两者提升为全局布局的原因。
注:如果觉得
enquireScreen多次出现比较不舒服,可以选择用 React Context 或其它方式传递移动端状态,详见 React Context 官方文档。
添加页面
从 Landing 上下载另一个模板页面,将其重命名(如 Page2),避免与已有的 Home 目录冲突;拷贝到 src 目录下。注意:新页面中不需要 Nav 和 Footer 组件,可将其移除。然后在 App.js 中为该新页面添加路由入口:
App.js
import React, { Component } from 'react';
...
import Home from './Home';
+ import Page2 from './Page2';
....
class App extends Component {
...
render() {
return (
<Router>
<div>
<Header dataSource={Nav00DataSource} isMobile={this.isMobile} />
<Route exact path="/" component={Home} />
+ <Route exact path="/page2" component={Page2} />
<Footer dataSource={Footer00DataSource} isMobile={this.isMobile} />
</div>
</Router>
);
}
}
export default App;
修改 Nav0 相关参数
要让导航菜单支持多页面跳转,需要把菜单项从普通 <a> 标签改为 react-router 的 <Link> 组件。先修改 Home 里的 Nav0 文件。
也可以将
Nav0和Footer0提出放到一个文件包里,如Layout,便于统一管理全局布局。
./src/Home/Nav0
import React from 'react';
...
import { Menu } from 'antd';
+import { Link } from "react-router-dom";
const Item = Menu.Item;
class Header extends React.Component {
....
const navChildren = Object.keys(navData).map((key, i) => (
<Item key={i.toString()} {...navData[key]}>
- <a
+ <Link
{...navData[key].a}
+ href={navData[key].a.href}
+ to={navData[key].a.href}
target={navData[key].a.target}
>
{navData[key].a.children}
- </a>
+ </Link>
</Item>
...
}
export default Header;
对照仓库中 Nav0 的原始实现,可以看到其 render 方法中正是通过 navData.map(...) 遍历 dataSource.Menu.children 生成 Menu.Item,并以 <a {...a}> 渲染菜单链接(其中 a 即 item.children 中的链接配置对象)。此处的路由改造即是将 <a> 替换为 <Link>,同时保留 to 与 href 属性以兼容 data.source.js 中的数据字段。
接下来修改 data.source.js 中的 href 值,让导航指向对应路由:
export const Nav00DataSource = {
...
Menu: {
className: 'header0-menu',
children: [
- { name: 'item0', a: { children: '导航一', href: '' } },
+ { name: 'item0', a: { children: '导航一', href: '/' } },
- { name: 'item1', a: { children: '导航二', href: '' } },
+ { name: 'item1', a: { children: '导航二', href: '/page2' } },
{ name: 'item2', a: { children: '导航三', href: '' } },
{ name: 'item3', a: { children: '导航四', href: '' } },
],
},
...
};
该数据结构与仓库中 Nav0 模板配置 的
Menu.children定义完全对应:每个导航项包含name、className、children.href(链接地址)与children.children(导航文本,数组形式),子菜单通过subItem字段描述。
路由配置完成
完成以上步骤之后,再次运行 npm start,即可在浏览器中看到落地页,并可通过顶部导航在多个页面之间切换。至此,Ant Design Landing 模板在 create-react-app 中的单页落地与多页路由两种集成方式均已配置完成。