《React后台管理系统实战 零》:基础笔记

释放双眼,带上耳机,听听看~!

day01

1. 项目开发准备


1
2
3
4
11). 描述项目
22). 技术选型
33). API接口/接口文档/测试接口
4

2. 启动项目开发


1
2
3
4
11). 使用react脚手架创建项目
22). 开发环境运行: npm start
33). 生产环境打包运行: npm run build   serve build
4

3. git管理项目


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
11). 创建远程仓库
22). 创建本地仓库
3    a. 配置.gitignore
4    b. git init
5    c. git add .
6    d. git commit -m "init"
73). 将本地仓库推送到远程仓库
8    git remote add origin url
9    git push origin master
104). 在本地创建dev分支, 并推送到远程
11    git checkout -b dev
12    git push origin dev
135). 如果本地有修改
14    git add .
15    git commit -m "xxx"
16    git push origin dev
176). 新的同事: 克隆仓库
18    git clone url
19    git checkout -b dev origin/dev
20    git pull origin dev
217). 如果远程修改
22    git pull origin dev
23

4. 创建项目的基本结构


1
2
3
4
5
6
1api: ajax请求的模块
2components: 非路由组件
3pages: 路由组件
4App.js: 应用的根组件
5index.js: 入口js
6

5 引入antd


1
2
3
4
5
6
7
8
9
10
11
1下载antd的包
2按需打包: 只打包import引入组件的js/css
3    下载工具包
4    config-overrides.js
5    package.json
6自定义主题
7    下载工具包
8    config-overrides.js
9使用antd的组件
10    根据antd的文档编写
11

6. 引入路由


1
2
3
4
5
6
7
8
9
1下载包: react-router-dom
2拆分应用路由:
3  Login: 登陆
4  Admin: 后台管理界面
5注册路由:
6  <BrowserRouter>
7  <Switch>
8  <Route path='' component={}/>
9

7. Login的静态组件


1
2
3
4
5
6
7
11). 自定义了一部分样式布局
22). 使用antd的组件实现登陆表单界面
3  Form  / Form.Item
4  Input
5  Icon
6  Button
7

8. 收集表单数据和表单的前台验证


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
11). form对象
2    如何让包含<Form>的组件得到form对象?  WrapLoginForm = Form.create()(LoginForm)
3    WrapLoginForm是LoginForm的父组件, 它给LoginForm传入form属性
4    用到了高阶函数和高阶组件的技术
52). 操作表单数据
6    form.getFieldDecorator('标识名称', {initialValue: 初始值, rules: []})(<Input/>)包装表单项组件标签
7    form.getFieldsValue(): 得到包含所有输入数据的对象
8    form.getFieldValue(id): 根据标识得到对应字段输入的数据
9
103). 前台表单验证
11    a. 声明式实时表单验证:
12        form.getFieldDecorator('标识名称', {rules: [{min: 4, message: '错误提示信息'}]})(<Input/>)
13    b. 自定义表单验证
14        form.getFieldDecorator('标识名称', {rules: [{validator: this.validatePwd}]})(<Input/>)
15        validatePwd = (rule, value, callback) => {
16          if(有问题) callback('错误提示信息') else callack()
17        }
18    c. 点击提示时统一验证
19        form.validateFields((error, values) => {
20          if(!error) {通过了验证, 发送ajax请求}
21        })
22

9. 高阶函数与高阶组件


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
11. 高阶函数
2    1). 一类特别的函数
3        a. 接受函数类型的参数
4        b. 返回值是函数
5    2). 常见
6        a. 定时器: setTimeout()/setInterval()
7        b. Promise: Promise(() => {}) then(value => {}, reason => {})
8        c. 数组遍历相关的方法: forEach()/filter()/map()/reduce()/find()/findIndex()
9        d. 函数对象的bind()
10        e. Form.create()() / getFieldDecorator()()
11    3). 高阶函数更新动态, 更加具有扩展性
12
132. 高阶组件
14    1). 本质就是一个函数
15    2). 接收一个组件(被包装组件), 返回一个新的组件(包装组件), 包装组件会向被包装组件传入特定属性
16    3). 作用: 扩展组件的功能
17    
183. 高阶组件与高阶函数的关系
19    高阶组件是特别的高阶函数
20    接收一个组件函数, 返回是一个新的组件函数
21

day02

1. 后台应用


1
2
3
4
5
6
1启动后台应用: mongodb服务必须启动
2使用postman测试接口(根据接口文档):
3    访问测试: post请求的参数在body中设置
4    保存测试接口
5    导出/导入所有测试接口
6

2. 编写ajax代码


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
11). ajax请求函数模块: api/ajax.js
2    封装axios + Promise
3    函数的返回值是promise对象  ===> 后面用上async/await
4    自己创建Promise
5      1. 内部统一处理请求异常: 外部的调用都不用使用try..catch来处理请求异常
6      2. 异步返回是响应数据(而不是响应对象): 外部的调用异步得到的就直接是数据了(response --> response.data)
72). 接口请求函数模块: api/index.js
8    根据接口文档编写(一定要具备这个能力)
9    接口请求函数: 使用ajax(), 返回值promise对象
103). 解决ajax跨域请求问题(开发时)
11    办法: 配置代理  ==> 只能解决开发环境
12    编码: package.json: proxy: "http://localhost:5000"
134). 对代理的理解
14    1). 是什么?
15        具有特定功能的程序
16    2). 运行在哪?
17        前台应用端
18        只能在开发时使用
19    3). 作用?
20        解决开发时的ajax请求跨域问题
21        a. 监视并拦截请求(3000)
22        b. 转发请求(4000)
23    4). 配置代理
24        告诉代理服务器一些信息: 比如转发的目标地址
25        开发环境: 前端工程师
26        生产环境: 后端工程师
275). async和await
28    a. 作用?
29       简化promise对象的使用: 不用再使用then()来指定成功/失败的回调函数
30       以同步编码(没有回调函数了)方式实现异步流程
31    b. 哪里写await?
32        在返回promise的表达式左侧写await: 不想要promise, 想要promise异步执行的成功的value数据
33    c. 哪里写async?
34        await所在函数(最近的)定义的左侧写async
35

3. 实现登陆(包含自动登陆)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1login.jsx
2    1). 调用登陆的接口请求
3    2). 如果失败, 显示错误提示信息
4    3). 如果成功了:
5        保存user到local/内存中
6        跳转到admin
7    4). 如果内存中的user有值, 自动跳转到admin
8src/index.js
9    读取local中user到内存中保存
10admin.jsx
11    判断如果内存中没有user(_id没有值), 自动跳转到login
12storageUtils.js
13    包含使用localStorage来保存user相关操作的工具模块
14    使用第三库store
15        简化编码
16        兼容不同的浏览器
17memoryUtils.js
18    用来在内存中保存数据(user)的工具类
19

4. 搭建admin的整体界面结构


1
2
3
4
5
6
7
8
11). 整体布局使用antd的Layout组件
22). 拆分组件
3    LeftNav: 左侧导航
4    Header: 右侧头部
53). 子路由
6    定义路由组件
7    注册路由
8

5. LeftNav组件


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
11). 使用antd的组件
2    Menu / Item / SubMenu
3
42). 使用react-router
5    withRouter(): 包装非路由组件, 给其传入history/location/match属性
6    history: push()/replace()/goBack()
7    location: pathname属性
8    match: params属性
9
103). componentWillMount与componentDidMount的比较
11    componentWillMount: 在第一次render()前调用一次, 为第一次render()准备数据(同步)
12    componentDidMount: 在第一次render()之后调用一次, 启动异步任务, 后面异步更新状态重新render
13
144). 根据动态生成Item和SubMenu的数组
15    map() + 递归: 多级菜单列表
16    reduce() + 递归: 多级菜单列表
17
185). 2个问题?
19    刷新时如何选中对应的菜单项?
20        selectedKey是当前请求的path
21    刷新子菜单路径时, 自动打开子菜单列表?
22        openKey是 一级列表项的某个子菜单项是当前对应的菜单项
23

day03

1. Header组件


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
11). 界面静态布局
2    三角形效果
32). 获取登陆用户的名称显示
4    MemoryUtils
53). 当前时间
6    循环定时器, 每隔1s更新当前时间状态
7    格式化指定时间: dateUtils
84). 天气预报
9    使用jsonp库发jsonp请求百度天气预报接口
10    对jsonp请求的理解
115). 当前导航项的标题
12    得到当前请求的路由path: withRouter()包装非路由组件
13    根据path在menuList中遍历查找对应的item的title
146). 退出登陆
15    Modal组件显示提示
16    清除保存的user
17    跳转到login
187). 抽取通用的类链接按钮组件
19    通过...透传所有接收的属性: <Button {...props} />    <LinkButton>xxxx</LinkButton>
20    组件标签的所有子节点都会成为组件的children属性
21

2. jsonp解决ajax跨域的原理


1
2
3
4
5
6
7
8
9
10
11
11). jsonp只能解决GET类型的ajax请求跨域问题
22). jsonp请求不是ajax请求, 而是一般的get请求
33). 基本原理
4    浏览器端:
5        动态生成<script>来请求后台接口(src就是接口的url)
6        定义好用于接收响应数据的函数(fn), 并将函数名通过请求参数提交给后台(如: callback=fn)
7    服务器端:
8        接收到请求处理产生结果数据后, 返回一个函数调用的js代码, 并将结果数据作为实参传入函数调用
9    浏览器端:
10        收到响应自动执行函数调用的js代码, 也就执行了提前定义好的回调函数, 并得到了需要的结果数据
11

day04: Category组件

1. 使用antd组件构建分类列表界面


1
2
3
4
5
1Card
2Table
3Button
4Icon
5

2. 相关接口请求函数


1
2
3
4
1获取一级/二级分类列表
2添加分类
3更新分类
4

3. 异步显示一级分类列表


1
2
3
4
1设计一级分类列表的状态: categorys
2异步获取一级分类列表: componentDidMount(){}
3更新状态, 显示
4

4. 显示二级分类列表


1
2
3
4
5
6
1设计状态: subCategorys / parentId / parentName
2显示二级分类列表: 根据parentId状态值, 异步获取分类列表
3setState()的问题
4    setState()更新状态是异步更新的, 直接读取状态值还是旧的状态值
5    setState({}, [callback]), 回调函数是在状态更新且界面更新之后执行, 可以在此获取最新的状态
6

5. 更新分类


1
2
3
4
5
6
7
8
9
11). 界面
2    antd组件: Modal, Form, Input
3    显示/隐藏: showStatus状态为2/0
4    
52). 功能
6    父组(Category)件得到子组件(AddForm)的数据(form)
7    调用更新分类的接口
8    重新获取分类列表
9

day05

1. 添加分类


1
2
3
4
5
6
7
8
9
11). 界面
2    antd组件: Modal, Form, Select, Input
3    显示/隐藏: showStatus状态为1/0
4    
52). 功能
6    父组(Category)件得到子组件(AddForm)的数据(form)
7    调用添加分类的接口
8    重新获取分类列表
9

2. Product整体路由


1
2
3
4
5
6
7
8
11). 配置子路由:
2    ProductHome / ProductDetail / ProductAddUpdate
3    <Route> / <Switch> / <Redirect>
4
52). 匹配路由的逻辑:
6    默认: 逐层匹配   <Route path='/product' component={ProductHome}/>
7    exact属性: 完全匹配
8

3. 分页实现技术(2种)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
11). 前台分页
2    请求获取数据: 一次获取所有数据, 翻页时不需要再发请求
3    请求接口:
4        不需要指定请求参数: 页码(pageNum)和每页数量(pageSize)
5        响应数据: 所有数据的数组
6
72). 基于后台的分页
8    请求获取数据: 每次只获取当前页的数据, 翻页时要发请求
9    请求接口:
10        需要指定请求参数: 页码(pageNum)和每页数量(pageSize)
11        响应数据: 当前页数据的数组 + 总记录数(total)
12
133). 如何选择?
14    基本根据数据多少来选择
15

4. ProductHome组件


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
11). 分页显示
2   界面: <Card> / <Table> / Select / Icon / Input / Button
3   状态: products / total
4   接口请求函数需要的数据: pageNum, pageSize
5   异步获取第一页数据显示
6       调用分页的接口请求函数, 获取到当前页的products和总记录数total
7       更新状态: products / total
8   翻页:
9       绑定翻页的监听, 监听回调需要得到pageNum
10       异步获取指定页码的数据显示  
11
122). 搜索分页
13   接口请求函数需要的数据:
14       pageSize: 每页的条目数
15       pageNum: 当前请求第几页 (从1开始)
16       productDesc / productName: searchName 根据商品描述/名称搜索
17   状态:  searchType / searchName  / 在用户操作时实时收集数据
18   异步搜索显示分页列表
19       如果searchName有值, 调用搜索的接口请求函数获取数据并更新状态
20      
213). 更新商品的状态
22   初始显示: 根据product的status属性来显示  status = 1/2
23   点击切换:
24       绑定点击监听
25       异步请求更新状态
26
274). 进入详情界面
28   history.push('/product/detail', {product})
29
305). 进入添加界面
31    history.push('/product/addupdate')
32

5. ProductDetail组件


1
2
3
4
5
6
7
8
9
10
11). 读取商品数据: this.props.location.state.product
22). 显示商品信息: <Card> / List
33). 异步显示商品所属分类的名称
4    pCategoryId==0 : 异步获取categoryId的分类名称
5    pCategoryId!=0: 异步获取 pCategoryId/categoryId的分类名称
64). Promise.all([promise1, promise2])
7    返回值是promise
8    异步得到的是所有promsie的结果的数组
9    特点: 一次发多个请求, 只有当所有请求都成功, 才成功, 并得到成功的数据,一旦有一个失败, 整个都失败
10

day06

1. ProductAddUpdate


1
2
3
4
5
6
7
8
9
10
11
12
13
11). 基本界面
2    Card / Form / Input / TextArea / Button
3    FormItem的label标题和layout
4    
52). 分类的级联列表
6    Cascader的基本使用
7    异步获取一级分类列表, 生成一级分类options
8    如果当前是更新二级分类的商品, 异步获取对应的二级分类列表, 生成二级分类options, 并添加为对应option的children
9    async函数返回值是一个新promise对象, promise的结果和值由async函数的结果决定
10    当选择某个一级分类项时, 异步获取对应的二级分类列表, 生成二级分类options, 并添加为当前option的children
11
123). 表单数据收集与表单验证
13

2. PicturesWall


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
11). antd组件
2    Upload / Modal / Icon
3    根据示例DEMO改造编写
42). 上传图片
5    在<Upload>上配置接口的path和请求参数名
6    监视文件状态的改变: 上传中 / 上传完成/ 删除
7    在上传成功时, 保存好相关信息: name / url
8    为父组件提供获取已上传图片文件名数组的方法
93). 删除图片
10    当文件状态变为删除时, 调用删除图片的接口删除上传到后台的图片
114). 父组件调用子组件对象的方法: 使用ref技术
12    1. 创建ref容器: thi.pw = React.createRef()
13    2. 将ref容器交给需要获取的标签元素: <PicturesWall ref={this.pw} />  // 自动将将标签对象添加为pw对象的current属性
14    3. 通过ref容器读取标签元素: this.pw.current
15

day07

1. RichTextEditor


1
2
3
4
11). 使用基于react的富文本编程器插件库: react-draft-wysiwyg
22). 参考库的DEMO和API文档编写
33). 如果还有不确定的, 百度搜索, 指定相对准确的关键字
4

2. 完成商品添加与修改功能


1
2
3
4
5
6
7
8
9
11). 收集输入数据
2    通过form收集: name/desc/price/pCategoryId/categoryId
3    通过ref收集: imgs/detail
4    如果是更新收集: _id
5    将收集数据封装成product对象
62). 更新商品
7    定义添加和更新的接口请求函数
8    调用接口请求函数, 如果成功并返回商品列表界面
9

3. 角色管理


1
2
3
4
5
6
7
8
9
10
11). 角色前台分页显示
22). 添加角色
33). 给指定角色授权
4    界面: Tree
5    状态: checkedKeys, 根据传入的role的menus进行初始化
6    勾选某个Node时, 更新checkedKeys
7    点击OK时: 通过ref读取到子组件中的checkedKeys作为要更新product新的menus
8            发请求更新product
9    解决默认勾选不正常的bug: 利用组件的componentWillReceiveProps()
10

day08

1. setState()的使用


1
2
3
4
5
6
7
8
9
10
11
12
11). setState(updater, [callback]),
2    updater为返回stateChange对象的函数: (state, props) => stateChange
3    接收的state和props被保证为最新的
42). setState(stateChange, [callback])
5    stateChange为对象,
6    callback是可选的回调函数, 在状态更新且界面更新后才执行
73). 总结:
8    对象方式是函数方式的简写方式
9        如果新状态不依赖于原状态 ===> 使用对象方式
10        如果新状态依赖于原状态 ===> 使用函数方式
11    如果需要在setState()后获取最新的状态数据, 在第二个callback函数中读取
12

2. setState()的异步与同步


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
11). setState()更新状态是异步还是同步的?
2    a. 执行setState()的位置?
3        在react控制的回调函数中: 生命周期勾子 / react事件监听回调
4        非react控制的异步回调函数中: 定时器回调 / 原生事件监听回调 / promise回调 /...
5    b. 异步 OR 同步?
6        react相关回调中: 异步
7        其它异步回调中: 同步
8
92). 关于异步的setState()
10    a. 多次调用, 如何处理?
11        setState({}): 合并更新一次状态, 只调用一次render()更新界面 ---状态更新和界面更新都合并了
12        setState(fn): 更新多次状态, 但只调用一次render()更新界面  ---状态更新没有合并, 但界面更新合并了
13    b. 如何得到异步更新后的状态数据?
14        在setState()的callback回调函数中
15

3. Component与PureComponent


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
11). Component存在的问题?
2    a. 父组件重新render(), 当前组件也会重新执行render(), 即使没有任何变化
3    b. 当前组件setState(), 重新执行render(), 即使state没有任何变化
4  
52). 解决Component存在的问题
6    a. 原因: 组件的shouldcomponentUpdate()默认返回true, 即使数据没有变化render()都会重新执行
7    b. 办法1: 重写shouldComponentUpdate(), 判断如果数据有变化返回true, 否则返回false
8    c. 办法2: 使用PureComponent代替Component
9    d. 说明: 一般都使用PureComponent来优化组件性能
10  
113). PureComponent的基本原理
12    a. 重写实现shouldComponentUpdate()
13    b. 对组件的新/旧state和props中的数据进行浅比较, 如果都没有变化, 返回false, 否则返回true
14    c. 一旦componentShouldUpdate()返回false不再执行用于更新的render()
15  
164). 面试题:
17    组件的哪个生命周期勾子能实现组件优化?
18    PureComponent的原理?
19    区别Component与PureComponent?
20

4. 用户管理


1
2
3
4
5
11). 显示用户分页列表
22). 添加用户
33). 修改用户
44). 删除用户
5

5. 导航菜单权限控制


1
2
3
4
5
6
7
8
9
10
11
11). 基本思路(依赖于后台):
2    角色: 包含所拥有权限的所有菜单项key的数组: menus=[key1, key2, key3]
3    用户: 包含所属角色的ID: role_id
4    当前登陆用户: user中已经包含了所属role对象
5    遍历显示菜单项时: 判断只有当有对应的权限才显示
62). 判断是否有权限的条件?
7    a. 如果当前用户是admin
8    b. 如果当前item是公开的
9    c. 当前用户有此item的权限: key有没有menus中
10    d. 如果当前用户有此item的某个子item的权限
11

day09

1. redux理解


1
2
3
4
1什么?: redux是专门做状态管理的独立第3方库, 不是react插件, 但一般都用在react项目中
2作用?: 对应用中状态进行集中式的管理(写/读)
3开发: 与react-redux, redux-thunk等插件配合使用
4

2. redux相关API


1
2
3
4
5
6
7
8
9
10
11
1redux中包含: createStore(), applyMiddleware(), combineReducers()
2store对象: getState(), dispatch(), subscribe()
3react-redux:
4    <Provider store={store}>: 向所有的容器组件提供store
5    connect(
6        state => ({xxx: state.xxx}),
7        {actionCreator1, actionCreator2}
8    )(UI组件):
9        产生的就是容器组件, 负责向UI组件传递标签属性,
10        一般属性值从state中获取, 函数属性内部会执行dispatch分发action
11

3. redux核心概念(3个)


1
2
3
4
5
6
7
8
9
10
11
1action:
2    默认是对象(同步action), {type: 'xxx', data: value}, 需要通过对应的actionCreator产生,
3    它的值也可以是函数(异步action), 需要引入redux-thunk才可以
4reducer
5    根据老的state和指定的action, 返回一个新的state
6    不能修改老的state
7store
8    redux最核心的管理对象
9    内部管理着: state和reducer
10    提供方法: getState(), dispatch(action), subscribe(listener)
11

4. redux工作流程

《React后台管理系统实战 零》:基础笔记

5. 使用redux及相关库编码


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1需要引入的库:
2    redux
3    react-redux
4    redux-thunk
5    redux-devtools-extension(这个只在开发时需要)
6redux文件夹:
7    action-types.js
8    actions.js
9    reducers.js
10    store.js
11组件分2类:
12    ui组件(components): 不使用redux相关API
13    容器组件(containers): 通过connect()()生成的组件
14

day10

1. 在项目中搭建redux整套环境


1
2
3
4
5
6
7
11). store.js
22). reducer.js
33). actions.js
44). action-types.js
55). index.js
66). 在需要与redux进行状态数据通信(读/写)的UI组件包装生成容器组件
7

2. 通过redux管理头部标题headTitle数据


1
2
3
4
5
6
7
11). action-types.js
22). actoins.js
33). reducer.js
44). 相关组件:
5    left-nav.js
6    header.js
7

3. 通过redux管理登陆用户信息user数据


1
2
3
4
5
6
7
8
9
10
11). action-types.js
22). actoin.js
33). reducer.js
44). 相关组件:
5    login.js
6    admin.js
7    left-nav.js
8    header.js
9    role.js
10

4. 自定义redux库


1
2
3
4
5
6
7
8
9
10
11
12
13
14
11). redux库向外暴露下面几个函数
2    createStore(): 接收的参数为reducer函数, 返回为store对象
3    combineReducers(): 接收包含n个reducer方法的对象, 返回一个新的reducer函数
4    applyMiddleware() // 暂不实现
5
62). store对象的内部结构
7    getState(): 返回值为内部保存的state数据
8    dispatch(): 参数为action对象
9    subscribe(): 参数为监听内部state更新的回调函数
10
113). combineReducers函数:
12    返回的总reducer函数内部会根据总的state和指定的action,
13    调用每个reducer函数得到对应的新的state, 并封装成一个新的总state对象返回
14

5. 自定义react-redux库


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
11). react-redux向外暴露了2个API
2    a. Provider组件类
3    b. connect函数
4
52). Provider组件
6    接收store属性
7    通过context将store暴露给所有的容器子组件
8    Provider原样渲染其所有标签子节点
9    
103). connect函数
11    接收2个参数: mapStateToProps和mapDispatchToProps
12    connect()执行的返回值为一个高阶组件: 包装UI组件, 返回一个新的容器组件
13    mapStateToProps:
14        为一个函数, 返回包含n个一般属性对象,
15        容器组件中调用得到对象后, 初始化为容器组件的初始状态, 并指定为UI组件标签的一般属性
16    mapDispatchToProps:
17        如果为函数, 调用得到包含n个dispatch方法的对象
18        如果为对象, 遍历封装成包含n个dispatch方法的对象
19        将包含n个dispatch方法的对象分别作为函数属性传入UI组件
20    通过store绑定state变化的监听, 在回调函数中根据store中最新的state数据更新容器组件状态, 从而更新UI组件
21

day11

1. 数据可视化


1
2
3
4
11). echarts(百度) ==> echarts-for-react
22). g2(阿里) ==> bizCharts
33). d3(国外)
4

2. 前台404界面


1
2
3
1<Redirect from='/' to='/home' exact/>
2<Route component={NotFound}/>
3

3. 打包应用运行


1
2
3
4
5
6
7
8
11). 解决生产环境ajax跨域问题
2    使用nginx的反向代理解决(一般由后台配置)
3    CORS: 允许浏览器端跨域
42). BrowserRouter模式刷新404的问题
5    a. 问题: 刷新某个路由路径时, 会出现404的错误
6    b. 原因: 项目根路径后的path路径会被当作后台路由路径, 去请求对应的后台路由, 但没有
7    c. 解决: 使用自定义中间件去读取返回index页面展现
8

给TA打赏
共{{data.count}}人
人已打赏
安全技术

【JWT】JWT+HA256加密 Token验证

2021-8-18 16:36:11

安全技术

C++ 高性能服务器网络框架设计细节

2022-1-11 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索