管理系统内 Tag 功能的实现

愈发纯熟的业务代码——开始做菜

1.背景 && 需求分析

1.1 背景

某日,被后台拿着一个别人实现好的管理系统说道了一顿,说需要实现得比某框架更好

地址:pig4cloud

1.2 需求分析

需求:管理系统内实现浏览器 Tag 标签的功能

分析:

  1. 切换 Tag 不主动更新页面(重点)
  2. 关闭活动中的 Tag 切换至后一个
  3. 若关闭 Tag 为最后一个,切换至前一个

2. 实现逻辑

技术栈:react + react-router + react-redux + react-saga

重点:组件更新,页面不变

上 demo :Edit react-tag-demo

2.1 内存逻辑

思路:却分活动状态内的存,和在打开过的内存,两个内存同事更新,切换 Tag 从打开过的数组内调取数据,有数据使用该数据,无数据则发起请求

  • tagList 侧栏点击过的 Tag 数组
  • activeTag 活动状态的 Tag

操作同归在 Reduce 内,方便后续 Tag 内外部交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*.
目录:/src/store/index.js
*/
const appReducer ={
tabListStatus: (state = { tabList: [], activeTab: null }, action) => {
switch (action.type) {
case 'tabListChange':
//
case 'tabListAdd':
//
case 'tabListRemove':
//
case 'tabListClear':
//
default:
return state
}
},
// ...
}

所有内存初始状态,都在 App.js->componentDidUpdate 生命周期内存操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
目录:/src/App.js
*/

componentDidUpdate(prevProps, prevState) {
if (this.props.tabListStatus !== prevProps.tabListStatus) {
const { tabList, activeTab } = this.props.tabListStatus
sessionStorage.setItem('tabList', JSON.stringify(tabList))
sessionStorage.setItem('activeTab', JSON.stringify(activeTab))
this.setState({
tabList,
activeTab: activeTab !== null ? activeTab.key : '/'
})
}
}

2.2 外部交互逻辑

  1. 侧栏逻辑,新增
  2. 点击逻辑,切换 Tag
  3. 关闭 Tag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

class App extends Component {
// 点击侧栏
handleOnMenuItem = param => {
const { tabListAdd, tabListStatus } = this.props
const { tabList } = tabListStatus
const userMenu = menuDataSource
const menu = this.menuItemFilter(userMenu, `/nav_${param.key}`)

let paramTab = {
title: menu.name,
key: menu.path,
queryParam: {},
dataSource: {}
}

//判断是否为新增
let pushBol = true
tabList.map(tab => {
if (tab.key === paramTab.key) {
pushBol = false
paramTab = Object.assign({},paramTab,tab)
}
return tab
})

if (pushBol) {
tabList.push(paramTab)
}

tabListAdd({
tabList,
activeTab: paramTab
})

this.toPath(`nav_${param.key}`)
}
// 点击 Tag
onChange = activeKey => {
// console.log('....', activeKey)
const { tabListStatus, tabListAdd } = this.props
const { tabList } = tabListStatus
const userMenu = tabList
const menu = this.menuChangeFilter(userMenu, activeKey)

const paramTab = {
...menu
}

tabListAdd({
tabList,
activeTab: paramTab
})

this.toPath(activeKey)
}
// 关闭逻辑
remove = targetKey => {
let activeKey = this.state.activeTab
let panes = this.state.tabList
let lastIndex
panes.forEach((pane, i) => {
if (pane.key === targetKey) {
lastIndex = panes.length - 1 === i ? i - 1 : i
}
})
const panesFilter = panes.filter(pane => pane.key !== targetKey)

if (panesFilter.length && activeKey === targetKey) {
if (lastIndex >= 0) {
activeKey = panesFilter[lastIndex]
} else {
activeKey = panesFilter[0]
}
} else {
activeKey = null
}

this.props.tabListAdd({
tabList: panesFilter,
activeTab: activeKey
})

this.toPath(activeKey !== null ? activeKey.key : '/')
}

// ...
}

2.3 内部交互逻辑

  • 判断是否新增
  • 操作更新 onChange, onClear, onSubmit

使用标记符号,或者直接判断一个 key 值是否存在值,来发送请求,demo 内使用 dataSource 是否存在为空来判断是否需要发送请求

1
2
3
4
5
6
7
8
9

componentDidMount() {
const { tabListStatus, musicList_query_param, musicList } = this.props
const { dataSource } = tabListStatus.activeTab

if (!Object.keys(dataSource).length) {
musicList(musicList_query_param)
}
}

操作更新 onChange, onClear, onSubmit 除了这一些以外还会存在不同的操作,demo 大致分了这几个操作,均使用 reducer 操作,App.js 内监听操作,更改内存,模块内不参与内存更改

1
2
3
4
5
6
7
8
9
10
11
12
13

onChange = (tabList, e) => {
e.persist()
if (!e || !e.target) {
return
}
const { target } = e
const operateParam = operateQuery(tabList, {
[target.id]: target.type === 'checkbox' ? target.checked : target.value
})

this.props.tabListChange(operateParam)
}

3. 对比

3.1 Vue 的实现

  • 技术栈:vue + vue-router + vuex
  • UI 框架:ant-design

关键:keep-alive

Edit vue-tag-demo

3.2 React 的实现

  • 技术栈:react + react-router + redux
  • UI 框架:ant-design

关键:redux 与内存处理

Edit react-tag-demo

4. 总结

实现上面肯定是 Vue 更快实现的,但对使用 React 内存的控制也是一件非常好玩的事情,如果项目大了,就更好玩了

实际上这种实现有点鸡肋,因为用户一个不留意,关了页面,这些 Tag 就不存在了,无论是那种管理系统,这个实现如果配合到后台,可能就更好玩了

感谢 @白白,感谢 @同事,都提供了很不错的思路

感谢阅读,代码很烂,请轻喷