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 之后,实际就会发现, 源代码中的 `
111
` 标签被编译成为了 `React.createElement("div", null, "111")`, 这里就出现了 React ,所以如果没有引入 React 编译之后的代码块(右侧部分)就找不到 React 是什么了。 + +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/Z57F4-image.png) + +接着问题又来了,js 文件中的标签为啥会被编译成为 `React.createElement()` 这种形式呢?主要还是因为上面说到的 webpack.config.js 文件中的 module 对 react app 项目中的文件经过了 babel-loader 的处理,处理过程中添加了 react 相关的预设。 + +![](https://chatflow-files-cdn-1252847684.file.myqcloud.com/hPD7T-image.png) + +当用 JSX 语法写的标签被编译成 `React.createElement()` (实际这个函数返回的是一个代表 react 组件的对象, 后面会说到)之后,`ReactDOM.render` 函数将获得这个 react 对象和一个真实 dom 节点作为两个参数。 diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/dva.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/dva.md" new file mode 100644 index 000000000..8d3cea34d --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/dva.md" @@ -0,0 +1,141 @@ +--- +title: dva +date: 2020-11-21 +draft: true +--- + +## dva 是什么 + +dva 是阿里体验技术部开发的 React 应用框架,主要用于解决组件之间的通行问题,在以前 react 项目中解决数据流问题会引入 redux,又由于 redux 没有异步操作,所以需要引入 redux-saga 或 redux-thunk,这样的缺点就是是需要引入多个库,致使项目结构复杂。而现在: + +> dva = React-Router + Redux + Redux-saga + +将上面三个 React 工具库包装在一起,简化了 API,让开发 React 应用更加方便和快捷。 是一个在 saga 基础上的数据流方案。 + +了解 dva 之前可以先去了解 redux-saga. + +dva 的最简结构: + +```js +import dva from 'dva'; +const App = () =>
Hello dva
; + +// 创建应用 +const app = dva(); +app.model(model); +// 注册视图 +app.router(() => ); +// 启动应用 +app.start('#root'); +``` + +## 核心概念 + +- State:一个对象,保存整个应用状态 +- View:React 组件构成的视图层 +- Action:一个对象,描述事件 +- connect 方法:一个函数,绑定 State 到 View +- dispatch 方法:一个函数,发送 Action 到 State + +### state 和 view + +state 是用于数据存储保存全局状态。view 是 react 组件构成的 UI 层,当 state 变化时会触发 ui 层视图的变化。 + +### Action 和 dispatch + +action 是用于描述一个事件的一个对象 + +```json +{ + "type": "submit-form-data", + "payload": formData +} +``` + +dispatch 则用来发送 Action 到 State + +### connect + +connect 是一个函数,绑定 State 到 View,connect 方法返回的也是一个 React 组件,通常称为容器组件,是用于生成 State 到 Prop 的映射 + +```js +// 第一种写法这里使用来修饰器@ +@connect((state) => { + return { + dept: state.dept, + version: state.version, + }; +}) + +// 第二种写法 +connect(({ state}) => ({state}))(App); +``` + +## Model + +dva 中的 model 是所有的应用逻辑都定义在里面 + +model 的栗子 🌰: + +```js +export default { + namespace: 'modelName', + state: { + num: 0 + }, + subscriptions: { + setup({dispatch,history}){ + return history.listen(({pathname, query})=>{ + // do something... + }) + } + } + effects: { + *addAfter1Second({payload}, { call, put, select }) { + yield call(delay, 1000); + yield put({ type: 'add' , payload: 10}); + const num = yield select(state => state.modelName.num); + console.log(num) + }, + }, + reducers: { + add(state, action) { + return{ + ...state, + num: action.payload + } + }, + }, +} +``` + +model 对象的属性由 namespace,state, effect,reducers,subscriptions 组成。 + +### namespace 和 state + +namespace 当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成,当在 ui 层触发变化时,可以利用 namespace 来区分触发那个 model 的方法。从而改变 state. + +```js +dispatch({ + type: 'modelName/add', + payload: 10, +}); +``` + +数据保存在 state,直接决定了视图层的输出. + +### effects 和 reducers + +effects 和 reducers 都是 action 的处理器,不同的是 reducers 处理的是同步的 action,effects 处理的是异步的 action,effects 是基于 redux-saga 实现,effect 是一个 Generator 函数,内部使用 yield 关键字,标识每一步的操作。effect 提供了供内部使用的三个方法。 + +- call: 执行异步函数,一般用于请求数据 +- put: 相当与 dispatch,发出一个 action,触法一个同步的 action +- select: 可以用于获取全局的状态 state + +### subscriptions + +subscriptions 一般用于监听路由变化,当满足某些要求时,可以触发一些事件 + +## 数据流图 + +![image](https://user-images.githubusercontent.com/21194931/56455493-812b0a00-6391-11e9-866b-a70f6ceefb9f.png) diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/umi.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/umi.md" new file mode 100644 index 000000000..12b8ab0e5 --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React \347\224\237\346\200\201/umi.md" @@ -0,0 +1,17 @@ +--- +title: umi +date: 2020-11-21 +draft: true +--- + +## umi + +乌米,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。 + +主要还是脚手架级别的。 + +https://umijs.org/zh-CN + +## 性能和 feature + +https://zhuanlan.zhihu.com/p/385272270 diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React-Dev-Error.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React-Dev-Error.md" new file mode 100644 index 000000000..237f3ca80 --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/React-Dev-Error.md" @@ -0,0 +1,143 @@ +--- +title: React Dev Error +date: "2020-10-26" +draft: true +--- + +## React Dev Error + +### 在哪些情况下,错误边界不会捕获错误? + +以下是错误边界不起作用的情况: + +在事件处理器内。 +**setTimeout** 或 **requestAnimationFrame** 回调中的异步代码。 +在服务端渲染期间。 +错误边界代码本身中引发错误时。 + +### 为什么事件处理器不需要错误边界? + +错误边界不会捕获事件处理程序中的错误。与 render 方法或生命周期方法不同,在渲染期间事件处理器不会被执行或调用。 + +如果仍然需要在事件处理程序中捕获错误,请使用下面的常规 JavaScript `try/catch` 语句: + +```js +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + + handleClick = () => { + try { + // Do something that could throw + } catch (error) { + this.setState({ error }); + } + }; + + render() { + if (this.state.error) { + return

Caught an error.

; + } + return
Click Me
; + } +} +``` + +上面的代码使用普通的 JavaScript try/catch 块而不是错误边界来捕获错误。 + +### try catch 与错误边界有什么区别? + +Try catch 块使用命令式代码,而错误边界则是使用在屏幕上呈现声明性代码。 + +例如,以下是使用声明式代码的 try/catch 块: + +```js +try { + showButton(); +} catch (error) { + // ... +} +``` + +而错误边界包装的声明式代码如下: + +```jsx + + + +``` + +因此,如果在组件树深处某个位置组件的 **componentDidUpdate** 方法中,发生了由 **setState** 引发的错误,它仍然会正确地冒泡到最近的错误边界。 + +### react 16.0 有什么新特性 + +16.0 的新特性有: + +- Components can now return arrays and strings from render. +- Improved error handling with introduction of "error boundaries". Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. + + > 通过引入“错误边界”改进了错误处理。 错误边界是 React 组件,可以在其子组件树中的任何位置捕获 JavaScript 错误,记录这些错误并显示回退 UI,而不是崩溃的组件树。 - 翻译 + + > 什么是 Error Boundaries? + > 单一组件内部错误,不应该导致整个应用报错并显示空白页,而 Error Boundaries 解决的就是这个问题。 + > Error Boundaries 的实现 + > 需要在组件中定义个新的生命周期函数——componentDidCatch(error, info) + + ```js + class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = { hasError: false }; + } + + componentDidCatch(error, info) { + // Display fallback UI + this.setState({ hasError: true }); + // You can also log the error to an error reporting service + logErrorToMyService(error, info); + } + + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + return

Something went wrong.

; + } + return this.props.children; + } + } + ``` + + // 上述的 ErrorBoundary 就是一个“错误边界”,然后我们可以这样来使用它: + +```jsx + + {" "} + +``` + +> Error Boundaries 本质上也是一个组件,通过增加了新的生命周期函数 componentDidCatch 使其变成了一个新的组件,这个特殊组件可以捕获其子组件树中的 js 错误信息,输出错误信息或者在报错条件下,显示默认错误页。 +> **注意一个 Error Boundaries 只能捕获其子组件中的 js 错误,而不能捕获其组件本身的错误和非子组件中的 js 错误。** + +### 每次组件渲染时调用函数的常见错误是什么? + +你需要确保在将函数作为参数传递时未调用该函数。 + +```jsx +render() { + +// Wrong: handleClick is called instead of passed as a reference! +return +} +``` + +相反地,传递函数本身应该没有括号: + +```jsx +render() { + +// Correct: handleClick is passed as a reference! +return +} +``` diff --git "a/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/note.md" "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/note.md" new file mode 100644 index 000000000..f516bb19d --- /dev/null +++ "b/docs/_posts/\347\237\245\350\257\206\345\272\223/Note/note.md" @@ -0,0 +1,74 @@ +--- +title: Note +date: "2023-10-02" +draft: true +--- + +# Note + +vite 源码学习: https://vite-design.surge.sh/ + +## 设计一套解决方案的考虑? + +1. 扩展性,接口 +2. 前端: 可视化拖拽。 后端: 扩展和配置性 +3. ci/cd 发布流水线,可灰度,可回滚 +4. 平台监控 +5. 自动化测试,保证平滑升级 + +## 参与开源的流程是怎么样的? + +PMC, Issues, 会议沟通。 + +## 主管、业务总监面 + +1. 如何推进 eslint 等规范的落地的 +2. 怎么搞的分享 + +## 业务(新)方向 + +1. 大数据场景的处理 +2. 可视化 Canvas 和 SVG +3. PWA Service Worker 和 Web Worker +4. 微前端 +5. SSR 同构 +6. K8S + +## 不同组、中心的沟通技巧? + +1. 需求多沟通,评审都需要参加。 +2. 需求改动都需要落单需求单上,所有文档信息基本都会在项目中记录下,产品提供的文档要求用腾讯在线文档提供。 + +## 数据中台相关 + +1. 数据治理的作用有哪些? + 1. 辅助管理 + 2. 智能推荐? +2. 平台建设的需要足够好,而不是让用户来提需求,我需要什么样的功能,别的平台有,得不断建设平台。才能赋能客户。得先完善平台,才能吸引客户。 +3. JSON Schema 描述的内容,太静态化,不灵活。 配合 DSL 的事件描述,能够很好的配合起来。 + +## 具体问题 + +1. 前端如果加载慢,会从哪些方面进行排查 +2. 前端架构设计方向? +3. 日志监控之类的,考虑如何开放给其他团队使用? +4. 错误收集需要注意些什么? +5. js 的 sourcemap 格式是怎么样的? 如何解析成实际的代码行数? +6. 你觉得你最擅长什么? +7. 说说你业务中觉得性能优化最大的一个场景? +8. 说说你业务中至今没有解决的问题? +9. 说说你业务中遇到最难解决的问题? +10. 高效的自学能力,能举个例子体现一下高效么? +11. 除了业务上的开发,平常会有什么学习计划么? +12. 平常有什么兴趣爱好? +13. 你的职业规划是怎样的? +14. 项目中遇到了哪些挑战? +15. 说说你对前端架构的认识,如何设计出一个架构方案(说实话,我顶不住,没研究过) +16. 在一个大型项目中,如何定位发生内存泄露的代码?(没有实践过) +17. 你自己在社区做过什么具有推动性的事情? + +## React Hooks + +https://fe.azhubaby.com/React/Hooks.html + +https://juejin.cn/post/6844904080758800392 diff --git "a/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" "b/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" new file mode 100755 index 000000000..18b7f2a03 --- /dev/null +++ "b/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" @@ -0,0 +1,98 @@ +--- +title: 设计高质量的 React 组件 +date: "2020-10-26" +draft: true +--- + +## 设计高质量的 React 组件 + +### 划分组件边界的原则 + +组件的划分满足 **高内聚** 和 **低耦合** 的原则。 + +高内聚是指把逻辑紧密相关的内容放在一个组件里。React 展示内容的 JSX,定义行为的 JavaScript,甚至定义样式的 CSS 都可以放在一个 JavaScript 文件中,React 天生具有高内聚的特点。 + +低耦合是指不同组件之间的依赖关系要尽量弱化,也就是每一个组件都需要独立。 + +### React 组件的数据种类 + +React 的两种数据—prop 和 state。 无论 prop 或者 state 的改变都会引发组件的重新渲染。 + +#### prop + +外部传给组件的数据。 + +```html + +``` + +HTML 组件属性的值都是字符串类型,即使是内嵌的 JavaScript,也依然是字符串表示代码。React 组件的 prop 所能支持的类型除了字符串之外,可以是任何 JavaScript 语言支持的数据类型。 + +当 prop 的类型不是字符串类型时,在 JSX 必须用`{}`,`style`的双花括号是因为对象。 + +React 组件要反馈数据给外部世界,也可以用 prop,传递一个函数。 + +如果组件的构造函数中没有调用`super(props)`, 那么组件实例被构造之后,类实例的所有成员函数就无法通过`this.props`访问到父组件传递过来的 props 值。 很明显,给`this.props`赋值是`React.Component`构造函数的工作之一。 + +`this.onButtonClick =this.onButtonClick.bind(this);` 为成员函数绑定当前 this 的执行环境,因为 ES6 方法创建的 React 组件并不自动绑定 this 到当前实例对象。 + +**ES6 解构赋值:**`const { caption} = this.props;` + +**propTypes 检查:** + +- 组件支持哪些 prop +- 每个 prop 应该是什么样的格式 + +```js +Counter.propTypes = { + caption: PropTypes.string.isRequired, + initValue: PropTypes.number, +}; +``` + +propTypes 虽然能够在开发阶段发现代码的问题,但是放在产品环境中就不太合适了。产品环境下耗费资源,并且 console 中输出错误信息对用户来说没有什么意义。所以最好的方式是,开发者在代码中定义 propTypes, 在开发过程避免犯错,但是在发布产品时,用一种自动的方式将 propTypes 去掉,这样最终部署到产品的代码会更优 。**`babel-react-optimize`**具有这个功能,确保只在发布产品中使用它。 + +#### state + +组件的内部状态。React 组件不能修改传入的 prop,所以需要记录组件自身的数据变化,就使用 state。 + +**初始化 state:** + +```js +// 通过判断逻辑,来给定state属性的值 +constructor(prop) { + ... + this.state = { + count: props.initValue || 0 + } +} +``` + +不够让这样的判断逻辑充斥组件的构造函数并不是一件美观的事情,而且容易遗漏。我们可以使用 React 的 defaultProps 功能,让代码更加容易读懂。 + +```js +Counter.defaultProps = { + initValue: 0, +}; +``` + +有了这样的设定,`this.state`初始化中可以省略判断条件,可以认为代码执行到这,必定有 initValue 值: + +```js +this.state = { + count: props.initValue, +}; +``` + +**读取和更新 state:** + +通过`this.state`可以读取到组件当前的 state,改变组件 state 必须使用`this.setState`函数。 + +如果直接修改`this.state`的值,虽然事实上改变了组件的内部状态,但只是野蛮地修改了 state,却没有驱动组件进行重新渲染,既然组件没有重新渲染,也不会反应`this.state`值得变化; 而`this.setState`函数做的事情就是,首先改变`this.setState`的值,然后驱动组件经历更新过程。 + +#### prop 和 state 对比 + +组件不应该改变 prop 的值,而 state 存在的目的就是让组件来改变的。 + +假如父组件包含多个子组件,然后把一个 JavaScript 对象作为 props 值传给这几个子组件,如果一个子组件去修改 props 中的值,可能让程序陷入一团混乱之中,这就违背了 React 设计的初衷。 diff --git "a/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" "b/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" new file mode 100644 index 000000000..339413bf6 --- /dev/null +++ "b/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" @@ -0,0 +1,28 @@ +--- +title: 过于宽泛的软性问题 +date: "2023-10-02" +draft: true +--- + +# 过于宽泛的软性问题 + +## 前端的路怎么走? + +要做好哪四件事: + +- 选择一个好的挑战(创造价值) +- 把简单的事情想复杂(控制风险,提前预判) +- 把复杂的事情做简单(成本控制,开发效率) +- 把负责的事情讲简单(内部传承,方便运营) + +如何做更好的前端? + +- 不要仅仅局限在一些技术上,不要做码农,要更多的了解业务,这样更能够应对项目的变与不变。 +- 前端是离用户最近的工程师,要找出一些能够创造价值的事情来做,反过来驱动自己去学习某技术,最后把业务做的更好。 + +## 说说你对前端架构师的理解 + +负责前端团队的管理及与其他团队的协调工作,提升团队成员能力和整体效率; +带领团队完成研发工具及平台前端部分的设计、研发和维护; +带领团队进行前端领域前沿技术研究及新技术调研,保证团队的技术领先 +负责前端开发规范制定、功能模块化设计、公共组件搭建等工作,并组织培训。