1、基于社区定制统一可维护的前端架构状态管理The essential of modern frontend architecture is state managementRedux一致性资源管理泛式化使用唯一的资源池管理所有实体后端是唯一的资源数据来源绝大多数资源来自于HTTP请求的响应整体API偏向RESTful风格可直接将响应与Store进行整合 entities:posts:123:.,456:.,index:posts:123,456 ,reading:postId:456 UIStoreRequestBackendResponseThunk?ActionReducer?https:/
2、 keyBy from lodash/fp;import withTableUpdate from./fetch;const listItems=params=get(/items,params);export const fetchItems=withTableUpdate(keyBy(id),items)(listItems);const items=await fetchItems();id:123,.,id:456,.store:entities:items:123:.,456:.基于API规范自动泛式化$entry:entity:card,key:123,entity:card,ke
3、y:456$entities:card:123:id:123,space:$ref:entity:space,key:ee-fe ,456:id:456,space:$ref:entity:space,key:ee-fe ,spaces:ee-fe:name:EE-FE,members:$ref:entity:user,key:hanzhixing,$ref:entity:user,key:zhanglili01 ,user:.HTTP/1.1 200 OKX-Total-Count:3243有序性有效性乱序的解决文案不提供并发异步所有任务串行,严重损害性能异步有序完成使用异步管理器,需要复杂
4、的依赖图结构管理异步完成后校验状态根据场景编码实现,无效的结果丢弃浪费移除状态共享不同输入的异步结果相互隔离,消耗少量额外内存多个并行异步过程 异步完成顺序不确定 没有对“异步结果是否可用”的校验 多个过程共享状态定义查询状态标准 postList:keyword=abc,pageIndex:1:params:keyword:abc,pageIndex:1 ,pendingMutex:0,response:arrivedAt:25,data:/.,error:/.,nextResponse:/Same as response 参数序列化作为索引原始参数运行中的请求数量响应
5、内容请求集响应到达时间响应数据响应异常还未合入的后续响应快速请求Action/actions/posts.jsimport getPostsByQuery from api;import thunkCreatorFor from standard-redux-shape;export const FETCH_POSTS=FETCH_POSTS;export const RECEIVE_POSTS=RECEIVE_POSTS;export const fetchPostsByQuery=thunkCreatorFor(getPostsByQuery,/API请求函数 FETCH_POSTS,/发
6、起请求的Action RECEIVE_POSTS,/请求到达的Action once:true,/只请求一次 trustPending:true,/同一时间仅一个请求在途 selectQuerySet(state)/请求响应的存放位置 return state.queries.postList;);使用策略管理响应/reducers/posts.jsimport acceptLatest from standard-redux-shape;/keepEarliest 只使用最初的响应/maxAge 保留时长import combineReducers from redux;import FET
7、CH_POSTS,RECEIVE_POSTS from actions;const strategies=/Always accept the latest arrived response immediately postList:acceptLatest(FETCH_POSTS,RECEIVE_POSTS);const queries=combineReducers(strategies);const ui=(state=keyword:,author:,type,payload)=/.;export default combineReducers(queries,ui);从Store获取
8、响应import createSelector from reselect;import createQueryDataSelector from standard-redux-shape;const selectFilter=createSelector(state=state.ui.keyword,state=state.ui.author,(keyword,author)=(keyword,author);const selectPostList=createQueryDataSelector(state=state.queries.postList/获取响应存放位置 selectFil
9、ter/获取参数对象);const mapStateToProps=state=(filter:selectFilter(state),posts:selectPostList(state);export default connect(mapStateToProps)(Component);响应的接收 params:/.,pendingMutex:0,response:null,nextResponse:null params:/.,pendingMutex:1,response:null,nextResponse:null params:/.,pendingMutex:0,response
10、:null,nextResponse:arrivedAt:25,data:/.params:/.,pendingMutex:0,response:arrivedAt:25,data:/.,nextResponse:nullFETCHRECEIVEACCEPTRECEIVE与ACCEPT往往合并为同一个步骤实时性积极化UIimport addLog from./log;import saveTodo from./api;const newTodo=todo=(type:NEW_TODO,todo:todo);const createTodo=todo=
11、async dispatch=dispatch(addLog(Submitted$todo.title);const createdTodo=await saveTodo(todo);/异步 dispatch(addLog(Created$createdTodo.title);dispatch(newTodo(createdTodo);,dispatch=dispatch(addLog(Created$todo.title);dispatch(newTodo(.todo,pending:true);一真一假2个函数异步结束后自动回滚假更新逻辑侵入性小https:/ Context APIGen
12、erator处理异步逻辑state+workflow+selector=region小Region+组合可在任意位置挂载https:/ initialState=todos:Buy milk,Meet John at peace park ,filter:,submitting:false;const workflows=*saveTodo(todo)yield submitting:true;const newTodo=yield postTodo(todo);yield state=const todos=state;return todos:.todos,newTodo,submitti
13、ng:false ;,filterByKeyword(keyword)return filter:keyword;const selectors=filterVisibleTodos(todos,filter)return filter?todos.filter(.):todos;export default defineRegion(initialState,workflows,selectors);使用状态const mapToProps=(filterVisibleTodos)=(todos:filterVisibleTodos();export default region.join(
14、mapToProps)(TodoList);const Todo=()=();export default region.establish(Todo)(Todo);顶层建立Region下层加入Region复用状态import defineQueryRegion from react-kiss;import getUser,getCard from api;const getUserRegion=defineQueryRegion(getUser);const getCardRegion=defineQueryRegion(getCard);const mapper=key=state=(ke
15、y:state);class View extends Component componentDidMount()this.props.user.request(this.props.username);this.props.card.request(this.props.cardID);render()const user=this.props.user.findData(this.props.username);const card=this.props.card.findData(this.props.cardID);export default getUserRegion.join(m
16、apper(user)(getCardRegion.join(card)(View);URL管理URL被大量硬编码在业务中修改URL容易造成遗漏字符串拼装URL不友好对URI Encode处理不统一 axios.get(/items/+params.id)统一URL模板const userProfileURL=new ManagedURI(/users/:name(a-zA-Z+),query:view?,from?);const UserProfileLink=createLink(userProfileURL);https:/ Route from react-router;import
17、TrackRoute from ecomfe/react-tracker;用户事件采集 灵活的采集与发送const provider=process.env.NODE_ENV=production?composeProvider(holmes(siteID),myOwnProvider():print();const collect=combineCollects(context(username,appVersion),/静态信息 browser(),/浏览器信息 session()/追踪单个用户访问);构建平衡Chunk体积稳定Chunk Hashimport map from lodas
18、h;map(array,toItem);import map,filter from lodash;map(filter(array,isValid),toItem);vendor-76c12fe7cfcaf77b0958.jsvendor-f312ed687dfc52db579d.js选择稳定的Chunk内容 lodash-app.js 小体积package放弃Tree Shaking多Feature构建ALL IN ONE单一工具封装所有配置强加构建选型限制,统一各产品技术栈和开发模式0成本实现项目代码库初始化依赖升级检测,强制Major版本下定期更新依赖同时包含Lint等功能et build -mode=development -build-target=stable -clean -analyze自动化工具更新自动测试编译全部产品一键自动创建Pull Request更新工具版本后续研究基于flag并行多重构建时的中间结果的复用基于历史构建结果自动选择hot patch或者全新bundle考虑缓存有效性与chunk体积平衡的自动chunk split算法通过依赖在NPM上的更新频率,预测并自动优化的DLL生成社区合作Q&A