react-slickhyj v1.1.1
基于antd和umi的前端架构设计
- 技术选型
- 环境搭建
- 启动说明
- 命令
- 发布
- 目录结构
- 路由和菜单
- 数据mock
- 接口代理
- 国际化
- 数据流方案
- 组件
- 兼容性
- 主题配置
- 异步方法封装
- 动态加载组件
- umi约定
- z-index
- 自定义图标
- 代码规范
- eslint
- 常见eslint错误
- 集成设计
- 样式主题
- 其他
- todo
技术选型
类型 | 名称 |
---|---|
MVVM | react@16.8.3 |
视图框架 | antd@3.13.0、pro-antd@2.0.0 |
数据流方案 | dva@2.0 |
构建工具 | umi@2.6.13 |
其 他 | less、css modules、lodash、jquery、moment |
环境搭建
- 安装 NodeJS。__确保 node 版本是 >=10.0.0
安装完成后,执行下面的命令确认是否安装成功。
node -v
npm -v
统一 使用 yarn 管理 npm 依赖:
npm i yarn -g
- 下载依赖包
yarn install
注:如果用的是svn,install成功后会在目录下生成yarn-offline-mirror
和node_modules
文件夹,记得把他们添加到忽略名单,无需提交到版本库。git无需关注。
- 安装IDE插件
以vscode 为例,需安装下列3个插件
- Prettier - Code formatter 插件
- EditorConfig for VS Code 插件
- vscode-stylelint
至此,环境配置完毕!
启动说明
npm run dev
启动- 登录账号密码 admin/123
命令
npm run dev // 以开发模式启动
npm run dev:no-mock // 以开发模式启动(无mock)
npm run prettier // 格式化src下的.js .jsx .ts .less,以及config和scripts下的.js文件并保存
npm run analyze // 分析bundle构成,分析依赖模块的体积分布
npm run build // 打包生产代码,此命令静态资源路径默认是指向根目录
npm run build API_HOST=http://www.baidu.com // 打包时request 会取这里的值来补全apiurl
npm run lint // 检验src下的.js .jsx .ts .less
npm run lint:fix // 检验并修复src下的.js .jsx .ts .less
发布
命令行执行npm run build
,会在根目录生成dist
文件夹。把dist
提交到服务器即可。
注:打包之前,先确认发布的环境是否带上下文,npm run build默认是指向根目录
可以通过npm run build API_HOST=http://www.baidu.com
指定异步请求的host部分。具体逻辑如下:
{
url: /^http(s)?:\/\//.test(url) ? url : window['API_HOST'] + url
}
目录结构
🔒 表示文件(夹)名是umi的约定关键字
|--- config 🔒
| |--- config.js 🔒 umi 配置,同 .umirc.js,二选一
|--- mock 🔒 mock 文件所在目录
|--- public 🔒
|--- dist # 源码编译生成的目录
|--- src 🔒 源码目录
| |--- pages 🔒
| | |--- Log # 业务模块1:日志管理
| | | |--- models 🔒
| | | | |--- log.js # 业务级的models是局部作用域,数据不涉及模块间共享时,建议放在模块目录下
| | | |--- services 🔒
| | | | |--- log.js # 业务级 异步请求服务
| | | |--- css
| | | | |--- index.less # 业务级样式
| | | |--- img
| | | | |--- xx.jpg # 业务级图片资源
| | | |--- index.js # 视图
| | | |--- _mock.js 🔒 业务级mock
| | | |--- ...
| | |--- Tasks # 业务模块2
| | |--- User # 业务模块3
| | |--- document.ejs # HTML模板
| |--- layouts # 布局模板
| | |--- BasicLayout.js
| | |--- UserLayout.js
| |--- components # pro-antd组件
| | |--- Charts
| | |--- Exception
| |--- assets # 静态资源
| |--- utils # 工具方法
| |--- models 🔒 数据模型
| |--- services 🔒 异步请求服务
| |--- locales 🔒 国际化
| |--- defaultSettings.js # 项目配置,可设置主题色、异步超时时长、菜单宽度等
| |--- global.js 🔒 全局JS
| |--- global.less 🔒 约定的全局样式,自动引入
|--- .editorconfig 🔒 与支持EditorConfig 插件的IDE协同,达到一个配置适配多种IDE的目的
|--- .eslintignore 🔒
|--- .eslintrc.js 🔒
|--- .gitignore 🔒
|--- .prettierignore 🔒
|--- .prettierrc 🔒
|--- .stylelintrc.json 🔒
|--- .jsconfig.json 🔒
|--- package.json 🔒 依赖包配置文件
|--- yarn.lock 🔒 锁定依赖版本号,yarn install时会按里面记录的版本安装
命名规范
目录和文件名均采用 驼峰结构
目录名:pages下存放react组件的,首字母大写
文件名:js类型如果是 react组件 首字母大写(index.js除外),其他js以及其他类型(less、jpg,ejs)首字母小写。
更多说明(https://umijs.org/zh/guide/app-structure.html)
路由和菜单
路由
采用UMI的约定式路由
路由权限判断逻辑:当前访问的url pathname是否存在于后台返回的菜单数据中。
菜单
菜单数据由后台返回一个对象数组,对象必须包含下面3个属性
{
"menuId":88461,
"urlAddr":"/bulletin",
"menuName":"系统公告"
}
注:urlAddr值有两种形式 "/xxx"和"iframehttp://www.xxx.com"。后者表示以iframe方式嵌入
数据mock
npm run dev
默认启动的是Mock模式。除了可以模拟POST\GET\请求之前,还可以模拟500、404等错误。
引入Mock.js可以让数据更逼真。
引入roadhog-api-doc 可以模拟延迟返回
Mock语法如下:
export default {
// 支持值为 Object 和 Array
'GET /api/users': { users: [1, 2] },
// GET POST 可省略
'/api/users/1': { id: 1 },
// 支持自定义函数,API 参考 express@4
'POST /api/users/create': (req, res) => {
res.status(200).send({
resultCode: '0',
resultMsg: '成功',
resultObject: {
name: 'soon',
userid: '00000001',
},
});
},
};
接口代理
在config.js添加proxy进行配置
{
proxy: {
'/portal': {
target: 'http://172.16.81.126:9999/portal-react/',
changeOrigin: true,
},
'/orgauth': {
target: 'http://172.16.81.126:9999/portal-react/',
changeOrigin: true,
},
},
}
国际化
当前暂且只支持zh-CN
和en-US
。建议按模块组织
配置中文包 src/locales/zh-CN.js
export default {
'menu.dashboard': '首页'
}
调用
import { FormattedMessage} from 'umi/locale';
export default () => {
return <div><FormattedMessage id="menu.dashboard" /></div>
}
可通过src/defaultSettings.js
中的 language 或 baseNavigator 设置默认语言。
手动切换语言时,会在localStorage中保存 umi_locale 字段。
注 : 在每个异步请求头包含自定义字段language,用于标识当前的语言环境。如:language: 'zh-CN | en-US'
数据流方案
React间组件的通信机制是单向向下逐级分发,在应对复杂项目时显得捉襟见肘。所以,需要引入一种数据流方案。我们采用的是基于redux和redux-saga的dva数据流方案。
数据流转描述如下:
- 用
connect
方法把model
和component
链接起来 - 页面事件触发,开始
dispatch
action
action
到达model
中的effects
中间件- 中间件获取数据,通过
put
方法把数据流转到指定的reducer
reducer
改变state后重新注入组件- 组件的props被改变,触发页面重新渲染
model、dispatch、action、reducer和effects都是redux中的重要概念
组件
小颗粒组件
- Table
- Tabs
- Button
- Icon
- DatePicker
- Switch
- Dropdown
- Message
- Card
- Tag
- Divider
- Step
- Popover
- Tooltip
- Tree
- Modal
- Slider
大约有二十多种常用的UI组件,更多查看antd官网
大颗粒组件
- Charts
- DescriptionsList
- Exception
- CountDown
- Ellipsis
- NumberInfo
- Trend
- PageHeaderWrapper
- Result
这块由pro-antd输出,更多查看pro-antd官网
兼容性
兼容IE9+,以及其他现代浏览器
针对IE9的样式统一定义在 src/fix-ie9.less
已知IE9问题
- Upload组件 不支持file.size 无法限制附件上传大小
主题配置
可以通过配置defaultSettings.js
文件快速更改主题效果
module.exports = {
themeConfig: {
'@ant-prefix': 'ant', // theme主题配置项
},
language: 'zh-CN', // 表示默认的语言,暂且支持 'zh-CN' 'en-US'。在baseNavigator=true时失效
baseNavigator: true, // true 表示用navigator.language的值作为默认语言。优先级比language高,比localStorage内的umi_locale低
timeout: 1000 * 60, // 1分钟超时
delay: 300, // mock接口延迟返回的时长,单位毫秒
theme: 'blue', // 可选值 "default | blue"
SESSION_PREFIX: 'SLICK_',
/**
* 页面风格设置
*/
showLeftMenu: true, // 默认开启左侧菜单
enableWatermark: false, // 开启水印
enableLanguage: false, // 开启国际化选择器
}
异步方法封装
因为需要兼容低版本浏览器IE9,所以放弃使用fetch类的库,(如:Axios,isomorphic-fetch等)。而直接引入jquery,但进行了二次封装。
封装内容
一、约定回参格式,主动提示请求错误。
{
resultCode:"0",
resultMsg:"成功返归约定数据",
resultObject:{}
}
主动提示会发生于下列两种场景:
- 异步服务出错时。如:500,404等,提示信息由 url, http code 和 code含义 组成。
- 在resultCode非0时。提示信息来源 resultMsg 。(可通过showError=false 拦截这种提示)
二、扩展了3个属性:expiry,showMask 和 showError。其中设置 expiry 可以缓存数据。根据场景合理应用可以有效 减少请求,提升用户体验。
三、对post请求的入参进行了JSON.stringify()处理
四、请求头追加 language 字段,用于标识当前的语言环境。如:language: 'zh-CN | en-US'
使用方法
request(url[,options])
options是一个对象,与jquery.ajax中的settings用法一致。
import request from '@/utils/request';
//默认为post请求
request('/api/project/notice')
//get
request('/api/project/notice',{method:'get'})
//带入参
request('/api/project/notice',{data:{name:'soon',uid:'8088'}})
//缓存60秒(即60秒内相同的地址和入参请求,不向服务器发请求,直接从sessionStorage取值)
request('/api/project/notice',{expiry:1000*60})
//开启一个全屏遮罩(不推荐这种用法,建议以dva的loading形式实行遮罩)
request('/api/project/notice',{showMask:true})
动态加载组件
只加载当前页面需要用到的组件,其余的按需加载,对 性能优化 很有意义。
import dynamic from 'umi/dynamic';
import LoadingComponent from '@/components/PageLoading/index';
const LoadableRoleInfo = dynamic({
loader: () => import('./RoleInfo'),
loading: LoadingComponent,
});
import('./RoleInfo')
引入组件
LoadingComponent
是全局使用的loading效果
通过调用dynamic重新生成一个新的组件,这个组件只会在被调用时,才会异步去取./RoleInfo
文件
umi约定
@
指向src 目录,如@/components
。~
指向node_modules目录- 路由表中的component 是相对于 src/pages 目录的。详解
- page(s) 文件夹下的 _mock.js 文件即 mock 文件。详解
- model 分两类,一是全局 model,二是页面 model。全局 model 存于 /src/models/ 目录,所有页面都可引用;页面 model 不能被其他页面所引用。详解
z-index
关于页面层级的设计
@zindex-table-fixed: auto;
@zindex-affix: 10;
@zindex-back-top: 10;
@zindex-badge: 10;
@zindex-picker-panel: 10;
@zindex-popup-close: 10;
@zindex-modal: 1000;
@zindex-modal-mask: 1000;
@zindex-message: 1010;
@zindex-notification: 1010;
@zindex-popover: 1030;
@zindex-dropdown: 1050;
@zindex-picker: 1050;
@zindex-tooltip: 1060;
以上是antd的层级逻辑
以下是关于SiderMenu、Header和Fixed Tabbar的设计
@zindex-sider-menu: 950;
@zindex-header: 900;
@zindex-fixe-tabbar: 900;
自定义图标
首先,把iconfont上对应的项目图标库的Symbol资源下载并放到项目目录third-party
:
其次,按如下调用。type
值为对应图标name
import IconFont from '@/components/IconFont'
<IconFont type="icon-hongbao" />
代码规范
规范简介
采用 Airbnb 代码规范
问题来了,规范写的越详细,开发人员遵守起来就越难。因为这些规范条目就像字典一样,谁也背不下这么多的条目,更不用说在日常开发过程中来运用了。
所以需要引入工具,结合代码规范实现 自动化 校验。
安装工具
只需要关心IDE插件部分, npm包在yarn install时已经安装并配置完毕
- vscode 插件
Prettier - Code formatter
插件EditorConfig for VS Code
插件vscode-stylelint
- 各种npm包
babel-eslint
给eslint指定的解析器,通常react项目都需要这个prettier
用来格式化js,lesseslint
校验模块 需与.eslintrc.js和.eslintignore两个文件配合使用eslint-config-prettier
让eslint继承prettier的配置eslint-config-airbnb
让eslint继承airbnb的配置eslint-plugin-react
eslint-plugin-jsx-a11y
eslint-plugin-import
airbnb关联的3个模块eslint-plugin-babel
与babel-eslint配套的ESlint规则插件eslint-plugin-compat
让eslint对代码进行浏览器兼容检测,需在package.json配置browserslist属性。如果有引入polyfill.js,可以在.eslintrc.js的settings.polyfills配置,校验时剔除指定的api。
stylelint
CSS 审查工具,统一样式代码规范,避免样式错误stylelint-config-standard
stylelint官方的规则,汲取了GitHub、Google、Airbnb 多家之长。stylelint-config-css-modules
让stylelint支持css modules的特殊语法stylelint-config-prettier
用来关闭所有不必要的或可能与prettier的规则冲突的规则stylelint-order
stylelint规则,作用是强制你按照某个顺序编写 css。例如先写定位,再写盒模型,再写内容区样式,最后写 CSS3 相关属性。比如这样stylelint-config-rational-order
stylelint-order关联模块,搭配使用stylelint-declaration-block-no-ignored-properties
不允许css中定义无效的属性。比如这样
注: 如果eslint是全局安装,则配置中使用的任何插件也必须是全局安装
eslint
上面安装的vscode插件和 npm 包要结合在一起使用,一共需要五个配置文件:
.editorconfig - EditorConfig配置文件
.eslintrc - ESLint规则配置文件
.eslintignore - 忽略使用ESLint规则的文件配置
.prettierrc - Prettier规则配置文件
.prettierignore - 忽略使用Prettier规则的文件配置
简介
options
通过.eslintrc.js
文件配置校验规则,.eslintignore
文件用来配置忽略。具体options如下:
env
你的脚本将要运行在什么环境中,可以预设好的其他环境的全局变量,如brower、node环境变量、es6环境变量、mocha环境变量等
globals
当访问当前源文件内未定义的变量时,no-undef 规则将发出警告。如果你想在一个源文件里使用全局变量,推荐你在 ESLint 中定义这些全局变量,这样 ESLint 就不会发出警告了。你可以使用注释或在配置文件中定义全局变量。
方式一:注释
在你的 JavaScript 文件中,用注释指定全局变量,格式如下
/* global var1:false, var2:false */
上面定义了两个全局变量:var1 和 var2。如果你想指定这些变量不应被重写(只读),你可以将它们设置为 false:
/* global var1:false, var2:false */
方式二:配置文件
使用 globals 指出你要使用的全局变量。将变量设置为 true 将允许变量被重写,或 false 将不允许被重写。比如:
{
"globals": {
"var1": true,
"var2": false
}
}
plugins
在配置文件里配置插件时,可以使用 plugins 关键字来存放插件名字的列表。插件名称可以省略 eslint-plugin-
前缀。
{
"plugins": [
"react"
],
}
由于 Node.js 的 require 函数的行为,如果eslint是全局安装,则配置中使用的任何插件也必须是全局安装
extends
从基础配置中继承已启用的规则,属性值可以是:
- 在配置中指定的一个字符串
- 字符串数组:每个配置继承它前面的配置
{
...
extends: ['airbnb', 'prettier', 'plugin:compat/recommended'],
}
注:属性值省略包名的前缀 eslint-config-
属性值除了是包名,还允许是基本配置文件的绝对路径或相对路径。
{
...
"extends": [
"./node_modules/coding-standard/eslintDefaults.js",
"./node_modules/coding-standard/.eslintrc-es6",
"./node_modules/coding-standard/.eslintrc-jsx"
],
}
rules
可以做下面的任何事情以扩展(或覆盖)规则
- 启用额外的规则
- 改变继承的规则级别而不改变它的选项:
- 基础配置:"eqeqeq": "error", "allow-null"
- 派生的配置:"eqeqeq": "warn"
- 最后生成的配置:"eqeqeq": "warn", "allow-null"
- 覆盖基础配置中的规则的选项
- 基础配置:"quotes": "error", "single", "avoid-escape"
- 派生的配置:"quotes": "error", "single"
- 最后生成的配置:"quotes": "error", "single"
开启规则和发生错误时报告的等级
{
...
rules: {
'no-console': 'off',
'indent': [ 'error', 4 ],
'quotes': [ 'error', 'single' ],
'import/no-unresolved': [2, { ignore: ['^@/', '^umi/'] }],
},
}
规则的错误等级有三种:
0或’off’:关闭规则。 1或’warn’:打开规则,并且作为一个警告(并不会导致检查不通过)。 2或’error’:打开规则,并且作为一个错误 (退出码为1,检查不通过)。
参数说明: 参数1 : 错误等级 参数2 : 处理方式
settings
在配置文件添加共享设置,你可以添加 settings 对象到配置文件,它将提供给每一个将被执行的规则。
parser
指定解析器,当前项目指定的是'babel-eslint' 。更多说明
overrides
暂无
Disabling Rules with Inline Comments
除了通过配置文件设置校验规则,还可以在文件中使用以下格式的注释,进行校验规则设置
代码块范围
/* eslint-disable */
alert('foo');
/* eslint-enable */
指定禁用的具体规则
/* eslint-disable no-alert, no-console */
alert('foo');
console.log('bar');
/* eslint-enable no-alert, no-console */
除了no-alert
, no-console
,还有更多规则
整个文件范围
将 /* eslint-disable */
块注释放在文件顶部:
/* eslint-disable */
alert('foo');
指定禁用的具体规则
/* eslint-disable no-alert */
// Disables no-alert for the rest of the file
alert('foo');
针对某一特定的行
alert('foo'); // eslint-disable-line
// eslint-disable-next-line
alert('foo');
/* eslint-disable-next-line */
alert('foo');
alert('foo'); /* eslint-disable-line */
指定禁用的具体规则
alert('foo'); // eslint-disable-line no-alert, quotes, semi
// eslint-disable-next-line no-alert, quotes, semi
alert('foo');
alert('foo'); /* eslint-disable-line no-alert, quotes, semi */
/* eslint-disable-next-line no-alert, quotes, semi */
alert('foo');
常用命令
eslint --init //引导生成一个配置文件 .eslintrc.js
常见eslint错误
prefer-default-export
只有一个导出时 需要加上 'default'
// 有错误提示
export const foo = 'foo';
// 没有错误提示
export const foo = 'foo';
export const bar = 'bar';
Must use destructuring props assignment react/destructuring-assignment
必须用解构赋值
//错误
this.props.dispatch
//正确
const {dispatch} = this.props;
Declare only one React component per file react/no-multi-comp
一个js文件 只能声明一个组件
//错误
class A extends PureComponent{}
class B extends PureComponent{}
Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use
export @dec classinstead.
不允许在装饰器和类之间使用export关键字。
//错误
@connect(({ form }) => ({
data: form.step,
}))
export default class StepForm extends PureComponent {
}
//正确
@connect(({ form }) => ({
data: form.step,
}))
class StepForm extends PureComponent {
}
export default StepForm
Disallow property values that are ignored due to another property value in the same rule.
不允许css中声明的某个属性 因为另外一个属性的声明而失效
//错误,因为内联元素 设置width无效
.a{
display: inline;
width: 0;
}
//正确
.a{
display: inline;
}
集成设计
菜单数据中urlAddr字段的值形如[iframe]http://www.xxx.com
。即表示以iframe方式嵌入
注意事项
1、被集成的第三方页面需要保证最高度撑满、且没有内边距
2、门户会以url方式往第三方页面传入bss3SessionId。如:http://www.xxx.com?bss3SessionId=36df5002-f51e-4f85-9ca3-45e61cc7803f
3、第三方页面如果使用了dva这类的数据流方案,需要避免模块间的数据引用。因为每个模块都是被单独初始化的。上面所有字段都是必填项
与门户通信
import { Portal } from '@/utils/utils';
Portal.open('/notice'); // 打开门户的消息界面
Portal.close('/notice'); // 关闭门户的消息界面
注意事项:
1、pathname 首字母带斜杆,且不以斜杆结尾
2、子系统间通信,只能通过url的传参。如:Portal.open('/notice?name=soon')
,然后在notice
页面解析url
3、子系统如果是fish界面,需要复制Portal到子系统
样式主题
目前集成了2套主题:config/theme/default/config.js
和 config/theme/bss/config.js
切换bss主题
1、config/config.js 中 引入config/theme/bss/config.js 配置 然后挂载在 lessLoaderOptions下 2、src/global.less 中引入 config/theme/bss/index.less
切换default主题
1、config/config.js 中 引入 config/theme/default/config.js 配置 然后挂载在 lessLoaderOptions下
其他
推荐开发工具 Vistual Studio Code
实现优雅提交git commit
由于不规范的 commit message,在 git log 和 code review 时显得很无用,同时也不利于编写 changelog。这里推荐采用 Angular 团队的规范。这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。
- feat 新功能
- fix Bug 修复
- docs 文档更新
- style 代码的格式,标点符号的更新
- refactor 代码重构
- perf 性能优化
- test 测试更新
- build 构建系统或者包依赖更新
- ci CI 配置,脚本文件等更新
- chore 非 src 或者 测试文件的更新
- revert commit 回退
如用vscode 可下插件 如果手动可以执行命令行 npm run commit
fix: xxxxx修复
feat: 新增xxxxx功能
日志更新:
npm run changelog
npm run version
todo
- 接入husky,进行版本管理
- 脚手架在iframe嵌套时关闭postMessage监听,避免与门户的监听重复
- 个性化部分的代码管理:限制在pages/local下,并且要满足logo、代理配置、打包的publicPath、portlet增量的需求
- useRequest
- year datepicker
- 在线主题切换
- 前端微服务化
- layout模块化
- 新窗口打开第三方页面,并实现单点登录