一、什么是Antd
AntDesign ,简称 antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。
- 中后台的产品 属于工具性产品,很多优秀的设计团队通过自身的探索和积累,形成了自己的设计体系。
AntDesign的特点:
- 提炼自企业级中后台产品的交互语言和视觉风格。
- 开箱即用的高质量 React 组件。
- 使用 TypeScript 开发,提供完整的类型定义文件。
- 全链路开发(
从业务战略—用户场景—设计目标—交互体验—用户流程—预期效率全方面进行分析和考
)和设计工具体系。 - 数十个国际化语言支持。
- 深入每个细节的主题定制能力
安装
npm install antd –save
# 或者使用 (注意: 库名叫做antd 不是ant-design)
yarn add antd
tips:默认情况下引入的antd的组件是没有样式的,如果需要引入的组件存在样式,需要将antd的样式在全局进行引入
import "antd/dist/antd.css";
tips: 在默认情况下,antd将其所有的icons导出为了一个单独的包,如果需要使用antd中的icons,则需要单独下载并引入对应包中的icon
yarn add '@ant-design/icons'
import { PoweroffOutlined } from '@ant-design/icons';
Antd是否会将一些没有用的代码(组件或者逻辑代码)引入,造成包很大呢?
antd 官网有提到:antd 的 JS 代码默认支持基于
ES modules
的tree shaking
,对于 js 部分,直接引入 import { Button } from 'antd' 就会有按需加载的效果什么是
tree shaking
所谓的
tree shaking
就是在打包的过程中,可能存在一些引入的代码中存在一些重来就没有被使用过的代码(dead-code)
于是在编译的时候
摇一摇这颗组件树
,将没有使用的函数,组件,和变量进行移除,这个操作就是tree shaking
这个过程被称为
DCE( dead code elimination(消除死代码))
,tree shaking
是DCE概念
的一种技术实现这个概念最早是由
rollup
完成的(rollup是基于ES2015实现的,tree shaking
是ES6 moudle中特性, ES6引入自己的模块格式的一个原因是启用静态结构
。静态结构
意味着您可以在编译时
确定导入和导出(静态) --- 您只需要查看源代码,而不必执行它)
webpack和rollup的区别?
webpack的优势在于静态资源分析和代码切割
rollup轻量,高效,打包后体积更小
所以webpack适合于有大量静态资源的项目(有大量图片,html,css,js)的项目
rollup适合于那些不是特别注重界面的项目(如vue,react)
总结:
在开发应用时使用 Webpack,开发库时使用 Rollup
二、 高级配置
对Antd中主题等相关的高级特性进行配置,需要修改create-react-app 的默认配置。
如何修改create-react-app 的默认配置呢?
- 前面我们讲过,可以通过
yarn run eject(CRA)
来暴露出来对应的配置信息进行修改; - 但是对于webpack并不熟悉的人来说,直接修改 CRA 的配置是否会给你的项目带来负担,甚至会增加项目的隐患和不稳定 性呢?
- 所以,在项目开发中是不建议大家直接去修改 CRA 的配置信息的;
那么如何来进行修改默认配置呢?社区目前有两个比较常见的方案:
- react-app-rewired + customize-cra;(这个是antd早期推荐的方案)
- craco;(目前antd推荐的方案)
craco是第三方的社区解决方案,不是react自带的解决方案
2.1 craco的基本使用
1. 安装
# @scope/project-name 这是npm提供的范围包的命名规范
# 之前的@xxx 可以是一个用户也可以是一个机构
# 这样可以在多个人或机构之间发布同名的包,而不会产生冲突
yarn add @craco/craco
2. 修改配置 将原来使用react-scripts启动的脚本修改为使用craco来启动
"scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
+ "start": "craco start",
+ "build": "craco build",
+ "test": "craco test",
}
3.在根目录下创建craco.config.js文件用于修改默认配置
// 这个文件一般被设置在根目录下,以便于在设置类似于别名等配置的时候,查找路径方便
module.exports = {
// 配置文件
}
2.2 修改antd默认主题
主题 === 全局样式
具体使用步骤参考:在 create-react-app 中使用中的高级配置
-
因为antd的样式使用的是less进行编写,所以如果需要我们自己去定义配置修改默认配置的话,必须使用的是less格式的样式,所以我们需要引入的是less格式的antd全局样式,而不是css格式的antd全局样式
- import 'antd/dist/antd.css' + import 'antd/dist/antd.less'
-
在CRA中默认对于webpack的配置是隐藏的,所以我们需要按
craco
来帮助我们修改webpack的样式,(其会将craco.config.js
中的样式和webpack.config.js
中的样式进行合并,且craco.config.js
中的样式会覆盖webpack.config.js
中的样式)
craco.config.js
// 在这里需要引入craco-less来帮助我们在解析的时候,将modifyVars中设置的值覆盖原本antd中的原本值
const CracoLessPlugin = require('craco-less');
module.exports = { // => 导出配置
plugins: [ // => 配置的插件列表 因为可能存在多个plugin所以使用数组的方式
{
plugin: CracoLessPlugin, //=> 插件名称
options: { // => 配置选项,具体查看文档
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
},
},
},
},
],
};
因为修改了配置文件,所以需要重启项目,才可以使配置文件生效
配置别名
const CracoLessPlugin = require('craco-less')
const path = require('path')
// 定义公共方法
const resolve = dir => path.resolve(__dirname, dir)
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
},
},
},
},
],
webpack: { // =>所有的webpack中的配置可以配置在这里,其会和webpack中的配置进行合并,如果冲突,这里的配置会覆盖webpack中的配置
alias: { // => 起别名
'@': resolve('src') //=> 此时@代码的就是项目根目录下的src文件夹
// 其他还有一些别名components => src/components utils => src/utils .... (了解,不常用)
}
}
}
三、 阶段案例
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
// 默认引入的antd是没有样式的,如果需要使用其样式,需要单独将其样式文件进行引入
import 'antd/dist/antd.css';
ReactDOM.render(<App/>, document.getElementById('root'))
App.js
import React, { PureComponent } from 'react'
import CommentInput from "./CommentInput"
import CommentItem from './CommentItem'
export default class extends PureComponent {
constructor(props) {
super(props)
this.state = {
comments: []
}
}
render() {
return (
<div style={{ width: '500px', padding: '20px' }}>
{
this.state.comments.map((comment, index) =>
/*
实际调用这个deleteComment函数的时候,
因为使用了箭头函数,所以这个组件的this是App组件
因此index可以传入App的deleteComment组件
*/
<CommentItem
key={comment.create_at}
comment={comment}
deleteComment={ e => this.deleteComment(index) }
/>
)
}
{/* 这里需要给自组件传入的是函数对象,不是函数调用后的返回值,所以不要加括号 */}
{/* 这里是传递给子组件,由子组件进行调用的,所以实际调用是子组件的props对象
但是在这个函数中调用的this应该是App组件,所以需要重新绑定this为当前App对象
*/}
<CommentInput submitComment={ this.addComment.bind(this) } />
{/* <CommentInput submitComment={ info => this.addComment(info) } /> */}
</div>
)
}
addComment(info) {
// 不要直接修改state中的对象类型数据,需要浅克隆一个新的对象
const newComments = [...this.state.comments, info]
// 调用setState修改界面
this.setState({
comments: newComments
})
}
deleteComment(index) {
const newComments = [...this.state.comments]
newComments.splice(index, 1)
this.setState({
comments: newComments
})
}
}
CommentInput.js
import React, { PureComponent } from 'react'
import dayjs from 'dayjs'
import { Input, Button } from 'antd'
// 或者直接使用 Input.TextArea
const { TextArea } = Input
export default class CommentInput extends PureComponent {
constructor(props) {
super(props)
this.state = {
content: ''
}
}
render() {
return (
<>
<TextArea
rows={5}
value={ this.state.content }
onChange={ e => this.handleChange(e) }
/>
{/* React中的样式名使用驼峰法 */}
<Button
type="primary"
style={{ 'marginTop': '10px' }}
onClick={ _e => this.addComment() }
>
添加评论
</Button>
</>
)
}
addComment() {
// 暂时写死一个数据对象
const data = {
create_at: Date.now(),
avatar: 'https://upload.jianshu.io/users/upload_avatars/10598662/3dc22fee-5e4f-4fbe-b692-882a87025fd8.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240',
author: 'Klaus',
content: this.state.content,
dateTime: dayjs()
}
// 调用父组件传入的方法
this.props.submitComment(data)
this.setState({
content: ''
})
}
// 定义受控组件的change事件
handleChange(e) {
this.setState({
content:e.target.value
})
}
}
CommentItem.js
import React, { PureComponent } from 'react'
import { Comment, Avatar, Tooltip } from 'antd';
import { DeleteOutlined } from '@ant-design/icons'
// 和moment不一样,dayjs将fromNow方法单独抽离出来形成一个单独的plugin
// 具体使用查看 https://day.js.org/docs/zh-CN/plugin/relative-time
// 修改中文配置 https://day.js.org/docs/zh-CN/i18n/changing-locale
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime' // 引入含有fromNow的插件
import 'dayjs/locale/zh-cn' // 引入中文模块
dayjs.locale('zh-cn') // 使用dayjs的中文模块(默认输出是英文 可以使用dayjs.locale()来查看当前语言配置)
dayjs.extend(relativeTime) // 使用fromNow插件
export default class CommentItem extends PureComponent {
render() {
const { avatar, author, content, dateTime } = this.props.comment
return (
/*
如果一个超链接暂时没有设置跳转的地址的时候,应该设置为\#
如果设置为的是#的话,其依旧是无法通过ESLint的编译的
对于content和datatime这一类的属性既可以传入String,也可以传入ReactNode
也就是既可以传入一个字符串类型的值,也可以传入一个子组件
*/
<Comment
author={<a href="/#">{author}</a>}
avatar={<Avatar src={ avatar } alt={ author } />}
content={ <p> {content} </p> }
datetime={
<Tooltip title={ dateTime.format('YYYY-MM-DD HH:mm:ss')}>
{ dateTime.fromNow() }
</Tooltip>
}
actions={[
<span onClick={e => this.deleteComment()} >
<DeleteOutlined />删除
</span>
]}
/>
)
}
deleteComment() {
this.props.deleteComment()
}
}