本篇博客针对 github 的wills-react-pro项目一些知识点讲解。
或者直接看原小册react-boilerplate-pro-2020项目
redux-thunk
项目使用了redux-thunk来处理异步请求,redux-thunk最重要的思想,就是可以接受一个返回函数的action creator。如果这个action creator 返回的是一个函数,就执行它,如果不是,就按照原来的next(action)执行。
正因为这个action creator可以返回一个函数,那么就可以在这个函数中执行一些异步的操作。
参考
详细示例可参考 项目的tag login_pro_v1.01
2
3
4//src/views/login/index.js
const mapDispatchToProps = {
loginUser: appAction.loginUser,
};
ajax封装
什么时候需要success提示
ajax分为两种:
一种是进入页面请求数据渲染页面的,此时不需要提示接口响应成功,只需loading即可。
一种是与后台交互,需要响应结果的,如交易、删除、编辑等,提交到后台,后台告知是否成功。
因此在做ajax封装时,可以不用配置success 的提示处理,需要提示的接口自行配置success提示处理。
什么时候需要loading
原则上每个请求都应该有loading,但是有很多组件有自己的loading样式,例如在非刷新整个页面的情况下,组件内刷新gird,只需在grid内显示loading,不需要统一的loading格式。
因此ajax封装时,配置可选的 loading标识是否loading。
什么时候需要error提示
ajax封装需要封装请求异常处理并提示,统一的异常处理和提示应该放在最后,为了不同接口个性化errorhandle,配置errorhandle,优先级高于统一处理模式。
断网时、请求超时的的error提示
断网和请求响应超时时,应该统一处理,并优先级最高。
使用options
loading,errorhandle等等,有很多参数,使用一个对象参数options。
ajax设计
项目通过两方面来封装 ajax:
- 通过api.js 封装三个常用的ajax方法 post、get、delete,在此js上,主要封装axios相关。
- 通过createAsyncAction.js 抽象出 公共的请求的成功和异常处理。
这样的设计好处在于
可以将axios与 回调处理的代码分离管理,减少耦合性。
详细示例可参考 项目的tag login_pro_v1.01
2
3
4//src/views/login/index.js
const mapDispatchToProps = {
loginUser: appAction.loginUser,
};
ajax过程的三次dispatch
1 | dispatch({ |
这三次dispatch可以用作如loading的控制等等。
eslint
参考《eslint笔记》
配置文件或相关
jsconfig.json
解决webpack别名点击不跳转问题
参考1
2
3
4
5
6
7
8
9
10{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}
vscode对webpack别名提示补全
配置与 《解决webpack别名点击不跳转问题》类似,可自行网上查询。
tsconfig.json
解决webpack别名点击不跳转问题
道理同《jsconfig.json》
其他功能
待研究
jest
mock的用法
示例见 wills-react-pro 的 Login.test.js1
const mockPost = jest.fn(testPost({ success: false }));
enzyme
shallow的两种情况
shallow一个组件时,和shallow组件内一个返回div的函数用法稍微有区别,
前者需要通过’<>‘括号起来,后者不需要。
示例见 wills-react-pro 的 Login.test.js
shallow一个组件
这种情况最通用,不多说:1
shallow(<Login />);
shallow组件内的函数返回的div
1 | renderErrorMsg = () => { |
shallow测试hoc
shallow一个经过hoc的组件时,只能shallow到hoc层面,为了能直接测试该组件时,需要特殊处理;
在hoc层面上,要写一句这样的代码:1
FinalComponent.WrappedComponent = WrappedComponent;
然后通过type属性,可以捕捉这个组件:1
2const getComponentFromHoc = warp => (warp.type && warp.type.WrappedComponent
? <warp.type.WrappedComponent {...warp.props} /> : warp);
详细demo见:1
2
3https://github.com/YeWills/wills-react-pro/blob/master/src/utils/testUtil.js
https://github.com/YeWills/wills-react-pro/blob/master/src/utils/connectWills.js
https://github.com/YeWills/wills-react-pro/blob/master/test/Login.test.js
connected-react-router 与 history
这是一种固定写法,不用过多关注:1
2
3
4
5//参考 项目的tag login_pro_v1.0
//src/app/init/createStore.js
import { connectRouter, routerMiddleware } from 'connected-react-router';
connectRouter(history)(combineReducers(reducers)),
路由设计
ConnectedRouter配置
ConnectedRouter类似BrowserRouter。MultiIntlProvider可以不用管就是一个高阶组件。
1 | <ConnectedRouter history={history}> |
使用BrowserRouter
项目的具体布局主要看 src/src-acl-router/AclRouter.jsx;
本项目应该用的是BrowserRouter,而非HashRouter,因为页面的路由都没有#。
整理出来如下: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<BrowserRouter history={history}>
<Switch>
<Route
path="/"
render={() => <Redirect to="/outlets" />}
/>
<Route
path="/login"
render={props => (
<NormalLayout {...props}>
<RouteComponent {...props} />
</NormalLayout>
)}
/>
<Route
path="/outlets"
render={props => (
<BasicLayout {...props}>
<RouteComponent {...props} />
</BasicLayout>
)}
/>
<Route
path="/exception/403"
render={props => (
<BasicLayout {...props}>
<Unauthorized {...props} />
</BasicLayout>
)}
/>
<Route
render={props => (
<NotFound {...props} />
)}
/>
</Switch>
</BrowserRouter>
页面权限管理
通过 permissions 配置,通过比对 登陆后 个人的权限user.authorities 与 页面的 permissions,来重组拼合 上面的 《使用BrowserRouter》:
本项目在登陆后会重新重组渲染上面的 《使用BrowserRouter》
1 | { |
重定向
场景一:当用户对某个页面没有权限时,AclRouter会将此页面 重定向到403页面1
2
3
4<Route
path="/outlets"
render={() => <Redirect to="/exception/403" />}
/>
NotFound
在《使用BrowserRouter》中的NotFound页面的路由设计挺好,此路由没有配置path,当上面的路由都未匹配时,就顺延到NotFound页面。
路由配置项介绍
1 | { |
AclRouter
所有路由重组,全部在 AclRouter.js.
这个js亮点在于,在登陆前与登陆后,改变 mapStateToProps 中的 user props值。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24const Router = ({ history, user }) => (
<ConnectedRouter history={history}>
<MultiIntlProvider
defaultLocale={locale}
messageMap={messages}
>
<AclRouter
authorities={user.authorities}
authorizedRoutes={authorizedRoutes}
authorizedLayout={BasicLayout}
normalRoutes={normalRoutes}
normalLayout={NormalLayout}
notFound={NotFound}
/>
</MultiIntlProvider>
</ConnectedRouter>
);
const mapStateToProps = state => ({
user: state.app.user,
});
Router.propTypes = propTypes;
export default connect(mapStateToProps)(Router);
根据登陆前后的user props值在 AclRouter.js中重组1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<BrowserRouter history={history}>
<Switch>
<Route
path="/"
render={() => <Redirect to="/outlets" />}
/>
<Route
path="/login"
render={props => (
<NormalLayout {...props}>
<RouteComponent {...props} />
</NormalLayout>
)}
/>
......
</Switch>
</BrowserRouter>
真正做到了根据用户权限,动态改变重组整个BrowserRouter组件。
BrowserRouter是组件
如上,BrowserRouter 可通过connect 的 mapStateToProps 中的 user props值 重新渲染 BrowserRouter。
这也验证了 react-router中说的所有router都是组件的说法。
因为BrowserRouter是组件,所以能理所当然地使用connect
见《BrowserRouter是组件》
参考demo /src/app/init/Router.js
redux 设计
概述
在初始化公共目录下的js中统一注入reducer,并写了一个公共的action和reducer,此公共的action和reducer可能很多页面都要用,因此写在公共目录下,供很多页面使用
:参考:src/app.
每个页面的reducer与action写在每个页面目录下,例如:1
2
3
4
5- outlets
- action.js
- index.js
- index.scss
- reducer.js
reducer
在初始化js中,统一注入reducer1
2
3
4
5
6
7
8
9import outlets from 'views/outlets/reducer';
import outletDetail from 'views/outletDetail/reducer';
import app from '../reducer';
export default {
app,
outlets,
outletDetail,
};
每个页面的reducer写在每个页面的目录下。
action
参考上面。
connect
每个页面都要处理 connect(mapStateToProps, mapDispatchToProps)(injectIntl(OutletDetail));
等待改进部分
connect 和 公共的action 单独整理 成connect高阶件,然后对比 考虑如何将post等继承其中
待做
- js源码调试
- css-source map
- mock
- 跨域请求 koa
- webpack 代码分离 DllPlugin
版本v0.01
版本介绍
此版本是一个最简单的工程配置版本,指包含一个简单界面和项目最基本的html js css webpack处理。
如何开始从0启动一个webpack项目
注重 webpack 入口 :
1 | "scripts": { |
项目最基本配置:
一个项目无非围绕 html,css,js,图片,axios进行,因此对应的配置如下,本版本下的package.json配置的依赖都是基于以下最基本的配置:
html
html模版插件
css
scss、css、图片 loader
css、图片与html分离
css3加兼容性前缀
js
es6、es7编译成es5
es6、es7的api(如Promise等等)运行profill
一些es6等相关的babel js插件
webpack
axios
小结
webpack无非就是对html css js 图片文件的打包,因为又多了babel对js的打包,可以说工程项目中对js的打包是最丰富的。