From e66262746afa996eeb4b57178c1606841f8d025d Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Mon, 2 Oct 2023 23:45:10 +0800 Subject: [PATCH] docs: docs --- ...36\347\255\224\351\227\256\351\242\230.md" | 98 +++++ ...41\347\253\257\346\270\262\346\237\223.md" | 30 ++ .../redux-helper.md" | 72 ++++ .../redux-saga.md" | 364 ++++++++++++++++++ .../redux-thunk.md" | 11 + .../cra \346\272\220\347\240\201.md" | 66 ++++ .../React \347\224\237\346\200\201/dva.md" | 141 +++++++ .../React \347\224\237\346\200\201/umi.md" | 17 + .../Note/React-Dev-Error.md" | 143 +++++++ .../Note/note.md" | 74 ++++ ...232\204 React \347\273\204\344\273\266.md" | 98 +++++ ...57\346\200\247\351\227\256\351\242\230.md" | 28 ++ 12 files changed, 1142 insertions(+) create mode 100644 "docs/_posts/\347\237\245\350\257\206\345\272\223/2. \345\246\202\344\275\225\345\233\236\347\255\224\351\227\256\351\242\230.md" create mode 100644 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \346\234\215\345\212\241\347\253\257\346\270\262\346\237\223.md" create mode 100755 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-helper.md" create mode 100755 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-saga.md" create mode 100755 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-thunk.md" create mode 100644 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/cra \346\272\220\347\240\201.md" create mode 100644 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/dva.md" create mode 100644 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/umi.md" create mode 100644 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React-Dev-Error.md" create mode 100644 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/note.md" create mode 100755 "docs/_posts/\347\237\245\350\257\206\345\272\223/Note/\350\256\276\350\256\241\351\253\230\350\264\250\351\207\217\347\232\204 React \347\273\204\344\273\266.md" create mode 100644 "docs/_posts/\347\237\245\350\257\206\345\272\223/\350\277\207\344\272\216\345\256\275\346\263\233\347\232\204\350\275\257\346\200\247\351\227\256\351\242\230.md" diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/2. \345\246\202\344\275\225\345\233\236\347\255\224\351\227\256\351\242\230.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/2. \345\246\202\344\275\225\345\233\236\347\255\224\351\227\256\351\242\230.md" new file mode 100644 index 000000000..1a814ed32 --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/2. \345\246\202\344\275\225\345\233\236\347\255\224\351\227\256\351\242\230.md" @@ -0,0 +1,98 @@ +--- +title: 如何回答问题 +date: 2020-11-21 +draft: true +--- + +# 如何回答问题 + +## STAR 法则 + +STAR 由 4 个词组合起来的,分别代表不同含义: + +- S: Situation,指的是项目背景 +- T: Task,项目任务 +- A: Action,你自己承担的工作 +- R: Result,工作结果 + +**总结**: + +1. 列出的这几条工作经验里,需要突出自己的专业能力。如果提到带领项目去工作,需要数据支持,比如多少个项目,什么级别的项目,技术人简历里要提什么类型的项目需要有一个优先级。 +2. 对于工作经历一定要提到 R,没有结果就算你做了很多,也不知道是 100 分还是 0 分,所以结果是体现你能力非常重要的一部分。 +3. 对于项目经历,每个项目都先描述项目背景,以及做项目使用的技术,描述完使用了什么技术之后,如果缺少 T 或者 A,只是提到了做交互功能,给人一种没有出多大力的感觉。 +4. 对于项目的收获,一定要结合自己的思考在里面。 + +## 把握面试时的关键点 + +在面试过程中一定要突出以下几个点:做过什么、有哪些工作业绩、优势是什么,这样可以很好的突出自己。 + +- **做过什么**:介绍自己,把自己曾经做过的事情说清楚,每段工作对应时间节点的公司名称、担任职务、工作内容等,尤其是对最近两份工作做过的事情要重点说说,较早之前的工作经验,或者学习的经验可以一带而过,**要把握“重点突出”的原则**。 + +- **有哪些工作业绩**:把自己在不同阶段做成的有代表性的项目经验介绍清楚,但是一定要注意:(1) 应与应聘岗位需要的能力相关的业绩多介绍,不相关的一笔带过或不介绍,因为面试官关注的是对用人单位有用的业绩;(2)要注意介绍你个人的业绩而不是团队业绩,要把自己最精彩的一两段业绩加以重点呈现。当然也要做好充足的准备,可以迎接面试官的提问。 + +- **突出自己的优势**:注意介绍自己的优势一定要与应聘的岗位密切相关,主要是围绕自己专业特长来介绍。除专业特长以外的特长,特别突出可以介绍,但要点到为止。 + +举个例子:你好,我是某某,2018 年 3 月加入 XXX 公司,担任产品经理一职,主要负责公司核心产品的规划和设计工作;在这段期间,我独立完成过 XX 项目的产品跟进和上线的工作,将产品的数据提升了 30%,业绩突出,获得了公司的认可。在项目中,我通过学习和与外部专家的沟通,获许了 XXX 新策略的信息,并积极尝试,达成了我的目标。 + +### 如何“讲故事” + +比如我,我会说,我之前在公司,做了我们前端的项目标准化工作,做了我们内部的组件库建设,集成了我们自己的脚手架工具。 +这是我觉得,我和其他大多数前端不一样的点,面试官的焦点就会集中在,什么是项目标准化,定义了哪些维度的标准?组件库的建设,解决了什么问题?脚手架的出现,又解决了什么问题? + +任何一个人,都是唯一的,任何一个项目,也是不同的,作为面试者的我们,重要的是,要把这些重点要素提取出来,形成自己的“故事”。 + +比如,我独立承担了公司的 xxx 项目。遇到了哪些挑战,我如何在不利的条件下成长起来的。 +比如,我通过 xxx 渠道,研究了多少个项目的架构工作。 + +一定要相信,因为你,什么才发生了变化。其实大多数人是一个被动的状态,不知道自己有什么用,自己在团队中的定位,自己的职责是什么。在工作中,我希望每个人相信自己的是有用的,然后寻找你可以发力的点,去做真正的改变。如果你在找工作,那就好好思考,自己确实在团队中,承担了什么。 + +### 面试该注意的点 + +面试的核心是什么?一定要记住,那就是沟通!而我们做技术的,其实最大的硬伤,多半是沟通,至少对于我自己,就是有这个问题的。 +沟通的意义,一方面,是你要让别人听懂你表达的东西;另一方面,是你要听懂别人的话。 + +1. 我向面试官说的所有的话,都是成体系的,都是逻辑清晰的,我都先停顿两秒以上,思考清楚了再说话。基本的语句通畅、逻辑清晰,在程序员中,做到的应该不多。 + +2. 学会倾听,让你懂别人。很多情况下,其实面试官在表达的时候,很可能我们自己没听懂面试官的意思,这个时候,就会特别的尴尬。 + 我一般会这么做,我会说,刚才您说的问题,我来描述一遍 xxxxxxx。描述完之后,你再问面试官,您表达的是这个意思吗?一般而言,面试官会进行更详细的举例和描述。一开始,我其实完全没明白,面试官说的是什么东西,但是我通过自己的方式,引导面试官说的更多,然后到某个程度,达成一个共识,这就是比较愉快的一个交流。 + +## 如何回答问题 + +尽量不要止步于问题,也就是面试官问什么你答什么,而是把回答的点**发散**出去,**引导**面试官提问,展示自己的水平。 + +比如说面试官提问了一个通过 DNS 查找 IP 过程的一个问题。那么在回答好这个问题的同时,可以指出获得 IP 以后就会发生 TCP 三次握手等等的情况,然后就可以引导面试官提问网络协议相关的问题了。 + +当然引导面试官的前提是你确实熟悉这一块的内容,否则就是给自己挖坑了。 + +**很推荐大家在准备面试的过程中,挖掘出自己擅长的技术内容,然后在面试的过程中,寻找机会引导面试官提问你擅长的技术点。** + +最后需要注意一点,如果你不能很好的理解面试官的提问,最好先**弄明白**面试官到底想问什么,而不是直接回答问题导致出现文不对题的情况。 + +## 如何应对可能答不好的题目 + +假如你遇到了一道不会的题目,但是稍微有一点想法,你可以先坦白说这道题目不怎么会,但是愿意尝试回答一下,这样即使回答错了,也不会有什么问题。但是千万不要不懂装懂,弄巧成拙。 + +## 面试的注意点 + +1. 回答问题不要过于着急,一定要耐心等待面试官把问题说完 +2. 回答问题要有逻辑、干练简洁 +3. 如果面试官打断你说话,此时一定要谨慎回答,因为很有可能你回答过于繁琐且他对你当下的回答不满意 +4. 一个问题不要纠结很久 +5. 不会的面试题必须干脆利落的说不会 +6. 面试的时间最好控制在 30 ~ 40 分钟左右,这样互相之间的体验不会很差 +7. 面试是一个挖掘面试者能力和潜力的过程 +8. 面试官不是全能的,面试一定是一个互相学习的过程 +9. 一定要提前准备好自己想要问的问题,最致命的是别人把你安排的明明白白结果你对别人一无所知 + +## 面试禁忌 + +1. 不要对老东家有太多埋怨和负面评价 +2. 不要有太多负面情绪,多表现自己阳光的一面 +3. 不要夸大其词,尤其是数据方面 +4. 不要贬低任何人,包括自己之前的同事 +5. 不要过多争辩。你是来展现自己胜任能力的,不是来证明面试官很蠢的 +6. 来突出自己的优秀 + +## 面试后的总结和思考 + +一场面试结束以后,尽快的将面试中遇到的问题记录下来,然后**复盘**整个面试。对于涉及到的题目,可以查询下资料验证自己是否答错了,如果答错了,就应该把这个知识漏洞补起来。如果知识点答对了,但是语言组织的不好,那么就需要重新组织下措辞和表达方式。 diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \346\234\215\345\212\241\347\253\257\346\270\262\346\237\223.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \346\234\215\345\212\241\347\253\257\346\270\262\346\237\223.md" new file mode 100644 index 000000000..ad340dfa1 --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \346\234\215\345\212\241\347\253\257\346\270\262\346\237\223.md" @@ -0,0 +1,30 @@ +--- +title: React-SSR +date: "2020-10-26" +draft: true +--- + +# React-SSR + +### 客户端渲染与服务端渲染 + +客户端渲染即普通的 React 项目渲染方式。 + +渲染流程: + +1. 浏览器发送请求 +2. 服务器返回 HTML +3. 浏览器发送 bundle.js 请求 +4. 服务器返回 bundle.js +5. 浏览器执行 bundle.js 中的 React 代码 + +带来的问题: + +1. 首屏加载时间过长 +2. SEO 不友好 + +因为时间在往返的几次网络请求中就耽搁了,而且因为 CSR 返回到页面的 HTML 中没有内容,就只有一个 root 空元素,页面内容是靠 js 渲染出来的,爬虫在读取网页时就抓不到信息,所以 SEO 不友好 + +SSR 带来的问题: + +1. React 代码在服务器端执行,很大的消耗了服务器的性能 diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-helper.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-helper.md" new file mode 100755 index 000000000..a58746090 --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-helper.md" @@ -0,0 +1,72 @@ +--- +title: redux-helper +date: '2020-10-26' +draft: true +--- + +### oh-my-redux(redux-family-helper) + +- create constants + - 一种类型是 start perform done fail reset cancel 五种状态 start 是设置参数,perform 是实际请求接口 + - 一种类型是 start done fail 三种状态,不需要设置参数,done 直接赋值 +- create actions + - 对应第一种类型 + - 对应第二种类型 +- create sagas +- Create thunk +- create rxjs +- Create promise +- create selectors +- Create connect 创建 redux 到 component 的映射。不要再重复输入了。 + +### fetch 操作 + +- fetch 需要取得结果 data + +- operation 结果只做为一个状态 ret + +- 简单的异步操作确实需要拉取接口的状态, 没有任何的副作用,不会有相关的后续操作 + +### 多步异步操作 + +- 封装 redux-thunk saga promise 等操作,需要更符合平常的操作。 + +### FSA(Flux Standard Action) + +https://segmentfault.com/a/1190000010113847 + +必须的: + +- 一个 action 必须是一个普通的 JavaScript 对象,有一个 type 字段。 +- 一个 action 可能有 error 字段、payload 字段、meta 字段。 +- 一个 action 必须不能包含除 type、payload、error 及 meta 以外的其他字段。 + +简单校验一个 action 是否是 FSA: + +```js +let isFSA = Object.keys(action).every(item => { + return ['payload', 'type', 'error', 'meta'].indexOf(item) > -1; +}); +``` + +## 思路 + +### 为什么需要分 Fetch 和 Workflow ? + +Fetch 的状态主要是对于 拉取接口的时候,将拉取的状态存储大 redux, 页面就可以针对状态显示不同的状态; + +而引入 WorkFlow 的概念,主要是由于考虑到异步操作之间的工作流顺序,比如说提交表单之前,拉一个接口现在是否可以提交,如果接口返回 ok,则提交;否则就不提交。 提交表单这一个操作完全是根据拉是否能够提交这个异步接口返回的结果决定的。因此,helpers 中引入 redux-thunk ,在声明某一个 action 时就直接将整个执行这个 action 之前、之后、进行时.... 值了操作一并声明好了。 额。。。其实这样做有蛮多缺点的, 代码大量重复肯定是一个(如果很多 action 触发之前都需要某一个操作,那这个操作都需要在声明异步函数阶段就写入 action 了);另外,具体执行流程对开发者不够透明,从 react 组件中,很难一眼看出这个异步操作之前做了什么动作。 + +#### 结论 + +如果需要将处理异步操作的中间件融入 helpers 中,那么肯定是有 Fetch 和 WorkFlow 之分的。 + +但是如果只是简单处理 redux 的状态,减少使用 redux 过程中的重复代码,其实只需要一个 Fetch 就够了,另外的异步处理中间件,开发者自行添加 redux-saga 也好,redux-thunk 也好,这个跟 helpers 没有关系。 + +### Redux-promise 的另一种探索思路 + +在 react 组件中 dispatch 一个 action, 合理的异步思路是(假设支持 async 和 await),等待某一个 action 触发的事件结束,之后,再执行下一个操作。这里的异步不仅仅指的是访问接口的操作,简单的 redux 赋值操作也是异步的。很多时候,我需要赋值给 redux 结束之后,再从 redux 中取值(赋值之前判断,再执行之后的异步操作,也是合理的。) + +那么,能够监听 dispatch 触发的事件? 并 await 呢? + +实际 dispatch(action) 之后是 return 当前的 action ,这可能是一个比较好的突破口 diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-saga.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-saga.md" new file mode 100755 index 000000000..c42af4165 --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-saga.md" @@ -0,0 +1,364 @@ +--- +title: redux-saga +date: '2020-10-26' +draft: true +--- + +## redux-saga 简述 + +redux-saga 就是用于管理副作用如异步获取数据,访问浏览器缓存的一个中间件。其中 reducer 负责处理 state 更新,sagas 负责协调异步操作,并提供一系列的 API(take,put,call 等)让副作用管理更容易,执行更高效,测试更简单。 + +redux-saga 是 redux 的中间件,中间件的作用是为 redux 提供额外的功能。 + +我们都知道 reducers 中的所有操作都是同步的并且是纯粹的,即 reducer 都是纯函数,**纯函数是指一个函数返回的结果只依赖于它的参数,并且在执行过程中不会对外部产生副作用**,即给它传什么,就吐出什么。但是在实际开发应用的过程中,我们希望做一些异步的(比如 AJAX 请求)且不纯粹的操作(比如改变外部状态),这些在函数式编程范式中被称为“副作用”。 + +Redux 的作者将这些副作用的处理通过中间件的方式让开发者自行选择进行实现。 + +**redux-saga 就是用来处理上述副作用(异步任务)的一个中间件。它是一个接收事件,并可能触发新事件的过程管理者,为应用管理复杂的流程。** + +https://segmentfault.com/a/1190000007261052?_ea=1290634 + +## redux-saga + +sagas 采用 Generator 函数来 `yield` Effects (包含指令的文本对象)。**Generator 函数的作用是可以暂停执行,再次执行的时候从上次暂停的地方继续执行。** Effect 是一个简单的对象,该对象包含了一些给中间件解释执行的额信息,可以通过使用`Effect API` 如 `fork` `call` take put cancel 等来创建 Effect [saga api](https://redux-saga-in-chinese.js.org/docs/api/index.html#takepattern) + +- put 触发某个 action, 作用和 dispatch 相同 +- take 等待 dispatch 匹配某个 action + +### bindActionCreators + +``` +bindActionCreator 这个函数的主要作用就是返回一个函数,当我们调用返回的这个函数的时候,就会自动的dispatch对应的action +``` + +## 源码分析 + +由于是 redux 的异步扩展,redux-saga 中广泛应用了 redux 中的很多函数,比如 applyMiddleware、dispatch、getState 等。如对 redux 不熟悉,建议看下[redux 源码分析](https://github.com/LuoShengMen/StudyNotes/issues/169),我们会通过[该例子](https://github.com/LuoShengMen/MyBlog-front/blob/master/src/store/index.js)来分析 redux-saga 源码 + +### 内部执行逻辑 + +![image](https://user-images.githubusercontent.com/21194931/56470449-dbeb6100-6478-11e9-819e-41dfa39d2cb8.png) + +### 入口文件 + +在 store/index.js 中通过 createSagaMiddleware 和 sagaMiddleware.run(rootSaga)引入 redux-saga 逻辑。sagaMiddleware.run 其实调用的是 runsage。 + +```js +// 这是redux-saga中间件的入口函数,是按照中间件的基本编写方式来写的 +// context、options默认是空的,分析的时候可以忽略 +function sagaMiddlewareFactory({ context = {}, ...options } = {}) { + const { sagaMonitor, logger, onError, effectMiddlewares } = options; + let boundRunSaga; + + // 下面就是中间件基本的编写方式 + // sagaMiddleware.run其实调用的是runsage + function sagaMiddleware({ getState, dispatch }) { + const channel = stdChannel(); + channel.put = (options.emitter || identity)(channel.put); + // 为runSaga提供redux的函数以及subscribe + boundRunSaga = runSaga.bind(null, { + context, + channel, + dispatch, + getState, + sagaMonitor, + logger, + onError, + effectMiddlewares, + }); + + return next => action => { + if (sagaMonitor && sagaMonitor.actionDispatched) { + sagaMonitor.actionDispatched(action); + } + const result = next(action); + channel.put(action); + return result; + }; + } + // 负责启动中间件的函数,下一小节讲述 + sagaMiddleware.run = (...args) => { + return boundRunSaga(...args); + }; + + sagaMiddleware.setContext = props => { + assignWithSymbols(context, props); + }; + + return sagaMiddleware; +} +``` + +### runSaga 函数 + +你传入的 saga 函数是一个 generator 函数。 + +```js +function runSaga(options, saga, ...args) { + // saga就是传过来的saga函数 + const iterator = saga(...args); + + const { + channel = stdChannel(), + dispatch, + getState, + context = {}, + sagaMonitor, + logger, + effectMiddlewares, + onError, + } = options; + + const effectId = nextSagaId(); + // 日志 + const log = logger || _log; + const logError = err => { + log('error', err); + if (err && err.sagaStack) { + log('error', err.sagaStack); + } + }; + // 是否有effectMiddlewares + const middleware = effectMiddlewares && compose(...effectMiddlewares); + const finalizeRunEffect = runEffect => { + if (is.func(middleware)) { + return function finalRunEffect(effect, effectId, currCb) { + const plainRunEffect = eff => runEffect(eff, effectId, currCb); + return middleware(plainRunEffect)(effect); + }; + } else { + return runEffect; + } + }; + + const env = { + stdChannel: channel, + dispatch: wrapSagaDispatch(dispatch), + getState, + sagaMonitor, + logError, + onError, + finalizeRunEffect, + }; + + try { + suspend(); + // 这一行是最终执行的 + const task = proc(env, iterator, context, effectId, getMetaInfo(saga), null); + + if (sagaMonitor) { + sagaMonitor.effectResolved(effectId, task); + } + + return task; + } finally { + flush(); + } +} +``` + +### oasp 函数 + +asap 是一个调度策略,存放了一个 quene,然后每次只允许一个任务执行 + +```js +const queue = []; +let semaphore = 0; + +function exec(task) { + try { + suspend(); + task(); + } finally { + release(); + } +} + +export function asap(task) { + queue.push(task); + + if (!semaphore) { + suspend(); + flush(); + } +} + +export function suspend() { + semaphore++; +} + +function release() { + semaphore--; +} +// while循环,将队列中执行完成,直到为空 +export function flush() { + release(); + + let task; + while (!semaphore && (task = queue.shift()) !== undefined) { + exec(task); + } +} +``` + +### stdChannel 函数 + +stdChannel 函数运行时才去运行 multicastChannel,最终返回 multicastChannel 的运行结果,该结果为一个对象 + +```js +function stdChannel() { + const chan = multicastChannel(); + const { put } = chan; + chan.put = input => { + // SAGA_ACTION :一个字符串,模版字符串 `@@redux-saga/${name}` + if (input[SAGA_ACTION]) { + put(input); + return; + } + // asap是一个调度策略,存放了一个quene,然后每次只允许一个任务执行 + asap(() => { + put(input); + }); + }; + return chan; +} +``` + +上面函数中的 chan 是 multicastChannel 函数执行的结果,返回了一个对象,对象包含 put、take 方法 + +- take 方法:将回调函数存入 nextTakers +- put 方法:执行相应的回调函数 + +```js +function multicastChannel() { + let closed = false; + + // 在状态管理中,经常碰到current和next的操作,为了保持一致性 + // 一个代表当前状态(�任务队列), + // 一个代表下一个状态(任务队列), + // 初始状态两个是一致的 + let currentTakers = []; + let nextTakers = currentTakers; + // 下面函数做的操作是,将当前的队列,复制给下一个队列 + const ensureCanMutateNextTakers = () => { + if (nextTakers !== currentTakers) { + return; + } + nextTakers = currentTakers.slice(); + }; + + const close = () => { + closed = true; + const takers = (currentTakers = nextTakers); + nextTakers = []; + takers.forEach(taker => { + // END是一个对象,END = { type: CHANNEL_END_TYPE } + taker(END); + }); + }; + + return { + [MULTICAST]: true, + put(input) { + if (closed) { + return; + } + // isEND是一个函数,判断是不是已经结束了 + // isEnd = a => a && a.type === CHANNEL_END_TYPE + if (isEnd(input)) { + close(); + return; + } + + const takers = (currentTakers = nextTakers); + + for (let i = 0, len = takers.length; i < len; i++) { + const taker = takers[i]; + + if (taker[MATCH](input)) { + taker.cancel(); + taker(input); + } + } + }, + take(cb, matcher = matchers.wildcard) { + if (closed) { + cb(END); + return; + } + cb[MATCH] = matcher; + ensureCanMutateNextTakers(); + nextTakers.push(cb); + + cb.cancel = once(() => { + ensureCanMutateNextTakers(); + remove(nextTakers, cb); + }); + }, + close, + }; +} +``` + +### redux-saga + +1. saga 的作用是什么? + +### redux-saga + +```js +function* watchFetchModel() { + // global saga永不cancel + yield takeLatest(getFetchActions(FETCH_MODEL).Start, fetchModel); +} +``` + +### redux saga 和 thunk 传一个回调给 action,保证 state 更新之后同步获取。 + +### saga 为什么要手动触发 LOCATION_CHANGE 这个 action + +https://neue.v2ex.com/t/300257, 重复触发 LOCATION_CHANGE 会导致 saga 异常,不执行异步操作。 + +### saga 的 cancel 操作, 什么时候 saga 异步操作需要退出? 等多久? + +https://redux-saga-in-chinese.js.org/docs/api/index.html#canceltask + +### redux saga 存在子组件的 saga 存在一个多次注册的 bug,对于各个子组件的 saga task ,需要在当前的作用域内 take 之后 cancel 掉才能避免被多次监听。 + +### 什么是 redux-saga? + +`redux-saga`是一个库,旨在使 React/Redux 项目中的副作用(数据获取等异步操作和访问浏览器缓存等可能产生副作用的动作)更容易,更好。 + +这个包在 NPM 上有发布: + +``` +$ npm install --save redux-saga +``` + +### redux-saga 的模型概念是什么? + +*Saga*就像你的项目中的一个单独的线程,它独自负责副作用。`redux-saga` 是一个 redux _中间件_,这意味着它可以在项目启动中使用正常的 Redux 操作,暂停和取消该线程,它可以访问完整的 Redux 应用程序状态,并且它也可以调度 Redux 操作。 + +### 在 redux-saga 中 `call()` 和 `put()` 之间有什么区别? + +`call()`和`put()`都是 Effect 创建函数。 `call()`函数用于创建 Effect 描述,指示中间件调用 promise。`put()`函数创建一个 Effect,指示中间件将一个 Action 分派给 Store。 + +让我们举例说明这些 Effect 如何用于获取特定用户数据。 + +```js +function* fetchUserSaga(action) { + // `call` function accepts rest arguments, which will be passed to `api.fetchUser` function. + // Instructing middleware to call promise, it resolved value will be assigned to `userData` variable + const userData = yield call(api.fetchUser, action.userId); + + // Instructing middleware to dispatch corresponding action. + yield put({ + type: 'FETCH_USER_SUCCESS', + userData, + }); +} +``` + +### `redux-saga` 和 `redux-thunk` 之间有什么区别? + +*Redux Thunk*和*Redux Saga*都负责处理副作用。在大多数场景中,Thunk 使用*Promises*来处理它们,而 Saga 使用*Generators*。Thunk 易于使用,因为许多开发人员都熟悉 Promise,Sagas/Generators 功能更强大,但您需要学习它们。但是这两个中间件可以共存,所以你可以从 Thunks 开始,并在需要时引入 Sagas。 diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-thunk.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-thunk.md" new file mode 100755 index 000000000..a04c17ff3 --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\212\266\346\200\201\347\256\241\347\220\206/redux-thunk.md" @@ -0,0 +1,11 @@ +--- +title: redux-thunk +date: '2020-10-26' +draft: true +--- + +## redux-thunk + +**redux-thunk 和 redux-saga 是 redux 应用中最常用的两种异步处理方式。** + +Redux Thunk 中间件允许您编写返回函数而不是 Action 的创建者。thunk 则可以用来延迟 action 的派发(dispatch),这可以处理异步 action 的派发(dispatch)。 thunk 可用于延迟 Action 的发送,或仅在满足某个条件时发送。内部函数接收 Store 的方法`dispatch()`和`getState()`作为参数。 diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/cra \346\272\220\347\240\201.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/cra \346\272\220\347\240\201.md" new file mode 100644 index 000000000..6a9b1b569 --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/cra \346\272\220\347\240\201.md" @@ -0,0 +1,66 @@ +--- +title: create-react-app 源码 +date: "2020-10-26" +draft: true +--- + +## 入口 + + + +### /bin/react-scripts.js + +cra 项目创建的项目目录结构,已经 package.json 中的 npm start 执行脚本 ![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/Ybwd8-image.png) + +在 node_modules 中找到 react-scripts 文件夹,查看 /bin/react-scripts.js 文件执行的脚本内容 ![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/76cBP-image.png) + +用的不是很多的 api: + +1. `Array.prototype.findIndex()` 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置 +2. `[].filter(Boolean)` 直接过滤到数组中 == false 的值,比如 undefined null 0 false 等 + +`node` 执行的过程中 `process` 这个全局变量有一个属性 `argv` 是此次脚本执行的所有的参数集合。 比如我们执行 `npm start` 的时候, `argv` 的值是 + +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/yJEj7-image.png) + +```js +const args = process.argv.slice(2); +``` + +所以 `args` 的值最终会是 `['start']`. 所以最终下面一段代码其实就是 `node ../scripts/start` + +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/kXXpZ-image.png). + +### `/scripts/start.js` + +整个 start.js 文件内容比较冗杂,其实这里是在做一些浏览器相关的事情以及 webpack-dev-server 的配置、执行工作。浏览器相关的事情我们暂时先忽略。 + +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/5cdGj-image.png) + +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/a2Kk3-image.png) + +webpack 具体如何配置不在这篇文章的讨论范围之内,这里我们只粗略看一下 entry output 以及 module。 entry 配置 webpack 打包的入口,output 则是输入目录文件,包括在 dev 阶段的时候临时产出目录。 module 则是 loader 的配置,比如 babel-loader 等。 + +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/zDeYM-image.png) +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/TkMbR-image.png) +忽略了 entry 中热加载相关的路径之后,入口文件是 `paths.appIndexJs`. + +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/eYRA4-image.png) + +那么接下来就从 webpack 打包的入口文件开始看起 `src/index.js` + +### `src/index.js` + +暂时忽略 serviceWorker +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/PtAGS-image.png) +这个文件中我们需要关注具体内容之前,先来想一个常见的问题。index.js 文件中明明没有调用 react 但是却引入了 react ? + +我们通过将代码在精简一下复制到 babel 官网的编辑框中,选择 preset-react 之后,实际就会发现, 源代码中的 `