{"id":18029112,"url":"https://github.com/94dreamer/tel_react_spa","last_synced_at":"2025-03-27T03:30:54.976Z","repository":{"id":134464586,"uuid":"66074486","full_name":"94dreamer/Tel_React_SPA","owner":"94dreamer","description":"react/redux/react-router4.x/react-redux/redux-thunk/redux-devtools/ES6/Webpack的电话销控复杂SPA","archived":false,"fork":false,"pushed_at":"2017-06-09T07:34:48.000Z","size":15850,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-23T00:22:49.403Z","etag":null,"topics":["es6","react","react-redux","react-router","react-router-redux","redux","redux-devtools","redux-thunk","spa","webpack"],"latest_commit_sha":null,"homepage":"http://crm.esf.sina.com.cn/saleteltest/list/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/94dreamer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-19T10:27:11.000Z","updated_at":"2020-03-31T05:27:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"bd83d3da-7c70-44b6-ae8f-274928dc0bf0","html_url":"https://github.com/94dreamer/Tel_React_SPA","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/94dreamer%2FTel_React_SPA","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/94dreamer%2FTel_React_SPA/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/94dreamer%2FTel_React_SPA/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/94dreamer%2FTel_React_SPA/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/94dreamer","download_url":"https://codeload.github.com/94dreamer/Tel_React_SPA/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245778395,"owners_count":20670682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["es6","react","react-redux","react-router","react-router-redux","redux","redux-devtools","redux-thunk","spa","webpack"],"created_at":"2024-10-30T09:08:10.895Z","updated_at":"2025-03-27T03:30:54.969Z","avatar_url":"https://github.com/94dreamer.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 项目介绍\n\n1. 该项目是我之前用JQuery/Template/WebScoket/ES6/Gulp/Webpack实现一个较复杂的单页面Web应用，为真实业务项目。主要是为 *电话销售/直接管理/上层管理* 三种角色提供对应的业务应用功能，主要包括Home页、Work页、Record页。  \n2. 其中 电话销售 根据分配的未拨队列／已拨队列／锁定队列，来进行电话销售，三种队列有不同的配置筛选项（包括地区／队列状态／部组／日期／关键字／客户状态／标签...等等），其中电话销售有部署了呼叫中心接入的WebScoket系统和非呼叫中心入口的区别。  \n3. Work页只有销售才能够进入，其中的展示逻辑和操作流程、提交流程比较复杂，在此不做过多叙述。  \n4. 为什么重构？初衷并不是因为我想拿该项目练手React，而是因为在用原技术路线时候，复杂的业务逻辑和组件展示和视图切换由大量过程式JQuery代码结合模版的数据控制视图来控制，让我越来越感到没有安全感（其实在项目一开始leader和我就在商榷技术选型是否使用react试水，后考虑到工期的紧迫，使用react技术栈存在一定的填坑风险，只能作罢。事实证明这个选择不能算错，后面和第三个呼叫中心的对接也耗费了大量时间）。\n5. 重构之后我试用了Rect+Webpack的经典组合，基于SPA的状态管理十分复杂，我理所当用的引入了Reudx，然后在开发环境中加入了Redux-Devtools。陆续我加入了Redux-Thunk／React-Router。\n6. 用以上篇幅简单对整个React SPA做简要描述，另外也告诉大家这是一个基于真实业务重构的复杂SPA应用，而不是一个简单的React Demo，component和action、reducers我都尽可能考虑贴近真实业务实现。\n7. 感想：完全的数据到视图的映射，让我感觉十分有安全感。在非简易逻辑的交互中，操作数据来声明进而控制视图的改变，比过程时的JQuery代码更容易思考。\n\n## 项目文件配置\n\n```\n/dist/\t\t# 构建输出的文件会在这里\n\n/node_modules/\t# 第三方类库和工具\n\n/src/\t\t# 应用源码\n\n     /components/\t# React component\n     \n     /constants/\t# 常量（比如 action types等）\n     \n     /containers/\t# React containers\n     \n     /entries/ \t# 应用入口\n     \n     /reducers/ \t# reducers\n     \n     /routes/\t# 路由信息\n     \n     /sagas/\t# redux-sagas\n     \n     /services/\t# 处理和服务器的交互\n     \nproxy.config.js\t# 配置 dora-plugin-proxy,用于 mock 和在线调试\n\nwebpack.config.js\t# 扩展 webpack 配置\n\npackage.json\t# 配置入口文件、依赖和 scripts\n```\n\n### 基本写法\n\n1. 用*require.ensure*来分割代码,打包的时候会把这块的代码独立打成一个文件\n\n```\nconst Manage = (location,cb)=\u003e{\n   require.ensure([],require=\u003e{\n        cb(null,require('./manage.js'))\n   })\n}\n\n\u003cRoute path='manage' getComponent={Manage} /\u003e\n```\n\n但是 Webpack2 发布之后，有这种新的写法支持\n\n```\ngetComponent(nextState, cb) {\n    import('./Component.jsx').then(res =\u003e cb(null, res.default))\n}\n```\n\n### 深刻的坑和debugger。\n\n#### 一、AJAX\n\n\u003e 1.此处为什么放弃fetch？原因有几个\n\n1. fetch的兼容性较差\n\n2. fetch暂时不支持中断，没有相关API。\n\n3. *extra* 尴尬的是因为后来把ajax放到了redux dispatch的action函数中，而不是在组件进行ajax交互，导致ajax的abort功能也一直没有用到。。。\n4. 反而可以考虑fetch。。jquery的包太大，但是有时候也无法避免需要使用jq的api。\n\n因为这个原因所以没有办法在react的es6语法环境中，在不使用isMounted()的情况下使用类似ajax的abort()方法在组件卸载的生命周期内停止异步操作，防止报错。\n\n\u003e 2.$.ajax的坑\n\n1. ajax的success函数内使用this.setState()，调用的是XHR对象，所以需要在ajax外层that=this，保存一下this的指向于组件。或者是bind(this)。\n\n2. ajax如果是异步的 后面的如果调用到ajax内的数据取不到，解决方案是要么改成ajaxType改成同步，要么注意数据为空问题。\n\n#### 二、使用Redux/React-Redux/Redux-DevTools\n\n- 最理想的组件化开发方式是依托组件树的结构，每个组件完成自己内部事务的处理。当组件之间出现通信需求的时候，不得不借助于Redux之类的库来做转发，但是Redux的理念，又不仅仅是只定位于做转发，它更是期望能管理整个应用的状态，这反过来对组件的实现，甚至应用的整体架构造成了较大的影响。 ​​​​\n\n- connect允许传入两个参数，第一个是筛选可用的state来传入props，第二个是筛选可用的action来dispatch，注入到props。\n\n- Redux-DevTools一开始的配置走了坑,附上自己的 [redux-devtools中文文档](https://github.com/94dreamer/Note/tree/master/redux-devtools全攻略)\n\n#### 三、组件\n\n1. onClick时注意大小写 onClick={this.handleClick} ，千万不要加括号，就自动执行了。\n\n2. 循环的map内，每个标签都需要一个key值，唯一值，注意不要用index值（想想为什么）。\n\n3. Component's children should not be mutated. 使用|| 或者 \u0026\u0026 的时候一定要保证有输出 不然输出false或者true就有问题了。\n\n4. 组件的render优化很大程度靠生命周期的shouldComponentUpdate内的判断，来优化不相干子节点的更新渲染。\n\n5. 慎用setState和大量state存储，尽可能使用props。\n\n6. 把组件划分为容器组件和UI展示组件，容器组件从store拿到数据，向store发起actions，而UI展示组件从props来获取数据，从 props 调用回调函数。\n\n#### 四、npm\n\n1. \"build\": \"set NODE_ENV=production \u0026\u0026 webpack --colors --profile\"时，设置环境变量，window系统下使用set，而mac下需要使用export，有个额外的插件可以平台通用。\n\n2. --colors 输出结果带彩色，比如：会用红色显示耗时较长的步骤\n--profile输出性能数据，可以看到每一步的耗时\n--display-modules默认情况下 node_modules 下的模块会被隐藏，加上这个参数可以显示这些被隐藏的模块 这次命令行的结果已经很有参考价值，可以帮助我们定位耗时比较长的步骤\n\ncnpm install --save-dev redux-devtools redux-devtools-log-monitor redux-devtools-dock-monitor\n\n\n#### 五、react-router4\n\n1. 传递复杂参数带不去过\n\nroute 带一个/:id/  是可以的 /:id/:city/ 无法匹配\n最后用的 state\n\n```\n\u003cLink\n    to={{\n    pathname: `/saletel/list/record/`,\n    //pathname: `/saletel/list/record/${window.xkTel.citycode}/`,\n    // search: `?citycode=${window.xkTel.citycode}\u0026uid=${list.basicinfo.uid}\u0026jobid=${window.ROLE['jobid']}`,\n    state: {\n      citycode: window.xkTel.citycode,\n      uid: list.basicinfo.uid,\n      jobid: window.ROLE['jobid'],\n    }\n}}\u003e{list.basicinfo.name}\u003c/Link\u003e\n```\n但是这样也有一个问题 复制链接在新页面打开 location的state丢失了，看来只能用location.search手动解析一下的蠢办法。\n\n2. react-router-redux\n\n让我们可以在redux改变路由，而不重新加载页面。","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F94dreamer%2Ftel_react_spa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F94dreamer%2Ftel_react_spa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F94dreamer%2Ftel_react_spa/lists"}