├── LICENSE
├── README.md
├── images
├── 25.pic.jpg
├── JS
│ ├── CSRF.png
│ ├── angularJs-data-bind.png
│ ├── ast.jpg
│ ├── dos_1.png
│ ├── dos_2.png
│ ├── eventloop-2.png
│ ├── eventloop.png
│ ├── prototype-1.png
│ ├── prototype-2.png
│ ├── prototype-3.png
│ └── rxjs
│ │ ├── bufferTime.png
│ │ ├── observers.png
│ │ ├── pub-sub.gif
│ │ ├── rxjs-1.png
│ │ ├── rxjs-2.png
│ │ └── summary.jpeg
├── NG-2-transelate
│ └── 20160403
│ │ ├── tumblr_njb2puhhEa1qc0howo1_1280.png
│ │ ├── tumblr_njb2puhhEa1qc0howo2_1280.png
│ │ ├── tumblr_njb2puhhEa1qc0howo3_1280.png
│ │ ├── tumblr_njb2puhhEa1qc0howo4_1280.png
│ │ ├── tumblr_njb2puhhEa1qc0howo5_1280.png
│ │ ├── tumblr_njb2puhhEa1qc0howo6_1280 (1).png
│ │ ├── tumblr_njb2puhhEa1qc0howo6_1280.png
│ │ └── tumblr_njb2puhhEa1qc0howo7_1280.png
├── broswer
│ ├── 1594467261480.jpg
│ ├── 1594468573301.jpg
│ ├── 1594469208326.jpg
│ ├── 1594522725317.jpg
│ ├── 1594523807800.jpg
│ ├── 1594548282681.jpg
│ ├── 1594548365742.jpg
│ └── 1594550743358.jpg
├── css
│ ├── gecko-render.jpg
│ ├── renderring-pipe.jpeg
│ └── webkit-render.jpg
├── data-structure-and-algorithms
│ ├── 1572782502823.jpg
│ ├── 1572782537452.jpg
│ ├── 1572782570965.jpg
│ ├── 1572782647288.jpg
│ ├── bst.jpg
│ └── del-bst.png
├── date-format-toggle-anim.gif
├── git
│ ├── --no-ff.gif
│ ├── merge.gif
│ ├── rebase.png
│ └── squash.gif
├── hero-list.png
├── hooks
│ ├── useRef-1.gif
│ └── useRef-2.gif
├── http
│ ├── http-1.png
│ ├── http-request-headers.png
│ ├── http-resp-headers.png
│ ├── http2-binary-framing.jpg
│ ├── iso.png
│ ├── tcp-format.png
│ ├── tcp-handshake.png
│ └── tcp-header.png
├── power-boost-calculator-anim.gif
├── power-booster.png
├── react
│ ├── component-lifecycle.jpg
│ ├── dom-diff.png
│ ├── hooks-flow.jpg
│ ├── lifecycle-new.jpg
│ ├── lifeofaframe.jpg
│ ├── mobx-pic-1.jpg
│ ├── mobx.jpg
│ ├── react-17-rc.jpg
│ └── setState.png
├── rn
│ ├── bridge.jpg
│ ├── new-architecture.jpg
│ └── old-architecture.jpg
├── webpack
│ └── HMR-flow.jpg
└── wx-xcx
│ └── wx-xcx.jpg
└── src
├── Computer-Basics
└── http.md
├── Flutter
└── README.md
├── JS
├── promise.md
└── 实现一个 async await.md
├── React
├── Hooks
│ ├── README.md
│ └── useRef.md
├── README.md
├── RN
│ ├── React Native's re-architecture in 2020.md
│ ├── React Native中JS与native的通信机制.md
│ ├── discussions-and-proposals.md
│ └── react-native-web.md
├── react-v17
│ └── React 17 RC 版发布:无新特性.md
└── react
│ ├── Fiber todo.md
│ └── 理解Component&Element&Instance.md
├── nodeJs
└── README.md
├── state-management
└── Redux or Mobx --前端应用状态管理方案的探索与思考.md
└── webpack
├── HMR.md
├── Tree-sharking.md
├── Webpack-DllPlugin.md
├── Webpack打包分块.md
├── happypack.md
├── how-to-write-a-plugin.md
├── index.md
├── webpack-5.md
└── 其它.md
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Just kidding
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 大致的主题范围如下(但不局限于这些范围):
2 |
3 | ### React & RN
4 |
5 | - [理解Component&Element&Instance](./src/React/react/理解Component&Element&Instance.md)
6 | - [浅入 React Fiber 及相关资料整理](https://github.com/JTangming/blog/issues/16)
7 | - [React v-Dom & diff 算法](https://github.com/JTangming/blog/issues/17)
8 | - [Rudex 和 mobx 相关总结](https://github.com/JTangming/blog/issues/18)
9 | - [react hooks 基础整理与进阶](https://github.com/JTangming/blog/issues/19)
10 | - [关于 React setState,你了解多少?](https://github.com/JTangming/tm/issues/11)
11 | - [理解 react-redux 之 connect](https://github.com/JTangming/tm/issues/8)
12 | - [React 组件的生命周期](https://github.com/JTangming/tm/issues/6)
13 |
14 | ### react-v17/RN-re-architecture
15 |
16 | - [React 17 RC 版发布:无新特性](./src/React/react-v17/React%2017%20RC%20版发布:无新特性.md)
17 | - [React Native's re-architecture in 2020](./src/React/RN/React%20Native's%20re-architecture%20in%202020.md)
18 |
19 | ### JavaScript
20 |
21 | - [JavaScript运行机制 & Event Loop](https://github.com/JTangming/blog/issues/12)
22 | - [JavaScript 中的私有变量](https://github.com/JTangming/blog/issues/14)
23 | - [Javascript 之数据劫持](https://github.com/JTangming/blog/issues/15)
24 | - [原型、原型链以及几种继承方式](https://github.com/JTangming/blog/issues/22)
25 | - [实现 new 操作符](https://github.com/JTangming/blog/issues/35)
26 | - [从零扒一扒 promise](https://github.com/JTangming/blog/issues/38)
27 |
28 | ### 计算机网络
29 |
30 | - [HTTP/S/2 & WebSocket 阅读笔记](https://github.com/JTangming/blog/issues/20)
31 |
32 | ### 网络安全
33 |
34 | - [Web 安全之攻防总结](https://github.com/JTangming/blog/issues/21)
35 |
36 | ### 前端工程化
37 |
38 | - [webpack 相关](https://github.com/JTangming/blog/issues/24)
39 | - [前端工程化相关](https://github.com/JTangming/blog/issues/28)
40 |
41 | ### 浏览器 & CSS
42 |
43 | - [深入理解现代浏览器架构(part 1)](https://github.com/JTangming/blog/issues/46)
44 | - [深入理解现代浏览器架构(part 2)](https://github.com/JTangming/blog/issues/47)
45 | - [浏览器渲染及性能优化](https://github.com/JTangming/blog/issues/34)
46 |
47 | ### Angular 相关
48 |
49 | - [Angular Universal 相关整理](https://github.com/JTangming/tm/issues/9)
50 | - [深入理解 Zones](https://github.com/JTangming/tm/issues/5)
51 | - [深入理解Angular2变化监测和ngZone](https://github.com/JTangming/tm/issues/4)
52 |
53 | ### 文章翻译
54 |
55 | - [Angular 2之变化检测](https://github.com/JTangming/tm/issues/3)
56 |
57 | ### 英文文章翻译
58 |
59 | - [A Recap of Frontend Development in 2019](https://github.com/JTangming/blog/issues/42)
60 |
61 | ### RxJs
62 |
63 | - [理解 RxJS](https://github.com/JTangming/blog/issues/23)
64 |
65 | ### 其他
66 |
67 | - [weekly reading](https://github.com/JTangming/tm/issues/10)
68 | - [详解 git 的四种 merge 方式](https://github.com/JTangming/blog/issues/13)
69 | - [Service Worker](https://github.com/JTangming/blog/issues/26)
70 |
--------------------------------------------------------------------------------
/images/25.pic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/25.pic.jpg
--------------------------------------------------------------------------------
/images/JS/CSRF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/CSRF.png
--------------------------------------------------------------------------------
/images/JS/angularJs-data-bind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/angularJs-data-bind.png
--------------------------------------------------------------------------------
/images/JS/ast.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/ast.jpg
--------------------------------------------------------------------------------
/images/JS/dos_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/dos_1.png
--------------------------------------------------------------------------------
/images/JS/dos_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/dos_2.png
--------------------------------------------------------------------------------
/images/JS/eventloop-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/eventloop-2.png
--------------------------------------------------------------------------------
/images/JS/eventloop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/eventloop.png
--------------------------------------------------------------------------------
/images/JS/prototype-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/prototype-1.png
--------------------------------------------------------------------------------
/images/JS/prototype-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/prototype-2.png
--------------------------------------------------------------------------------
/images/JS/prototype-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/prototype-3.png
--------------------------------------------------------------------------------
/images/JS/rxjs/bufferTime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/rxjs/bufferTime.png
--------------------------------------------------------------------------------
/images/JS/rxjs/observers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/rxjs/observers.png
--------------------------------------------------------------------------------
/images/JS/rxjs/pub-sub.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/rxjs/pub-sub.gif
--------------------------------------------------------------------------------
/images/JS/rxjs/rxjs-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/rxjs/rxjs-1.png
--------------------------------------------------------------------------------
/images/JS/rxjs/rxjs-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/rxjs/rxjs-2.png
--------------------------------------------------------------------------------
/images/JS/rxjs/summary.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/JS/rxjs/summary.jpeg
--------------------------------------------------------------------------------
/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo1_1280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo1_1280.png
--------------------------------------------------------------------------------
/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo2_1280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo2_1280.png
--------------------------------------------------------------------------------
/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo3_1280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo3_1280.png
--------------------------------------------------------------------------------
/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo4_1280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo4_1280.png
--------------------------------------------------------------------------------
/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo5_1280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo5_1280.png
--------------------------------------------------------------------------------
/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo6_1280 (1).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo6_1280 (1).png
--------------------------------------------------------------------------------
/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo6_1280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo6_1280.png
--------------------------------------------------------------------------------
/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo7_1280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/NG-2-transelate/20160403/tumblr_njb2puhhEa1qc0howo7_1280.png
--------------------------------------------------------------------------------
/images/broswer/1594467261480.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/broswer/1594467261480.jpg
--------------------------------------------------------------------------------
/images/broswer/1594468573301.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/broswer/1594468573301.jpg
--------------------------------------------------------------------------------
/images/broswer/1594469208326.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/broswer/1594469208326.jpg
--------------------------------------------------------------------------------
/images/broswer/1594522725317.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/broswer/1594522725317.jpg
--------------------------------------------------------------------------------
/images/broswer/1594523807800.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/broswer/1594523807800.jpg
--------------------------------------------------------------------------------
/images/broswer/1594548282681.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/broswer/1594548282681.jpg
--------------------------------------------------------------------------------
/images/broswer/1594548365742.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/broswer/1594548365742.jpg
--------------------------------------------------------------------------------
/images/broswer/1594550743358.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/broswer/1594550743358.jpg
--------------------------------------------------------------------------------
/images/css/gecko-render.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/css/gecko-render.jpg
--------------------------------------------------------------------------------
/images/css/renderring-pipe.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/css/renderring-pipe.jpeg
--------------------------------------------------------------------------------
/images/css/webkit-render.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/css/webkit-render.jpg
--------------------------------------------------------------------------------
/images/data-structure-and-algorithms/1572782502823.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/data-structure-and-algorithms/1572782502823.jpg
--------------------------------------------------------------------------------
/images/data-structure-and-algorithms/1572782537452.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/data-structure-and-algorithms/1572782537452.jpg
--------------------------------------------------------------------------------
/images/data-structure-and-algorithms/1572782570965.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/data-structure-and-algorithms/1572782570965.jpg
--------------------------------------------------------------------------------
/images/data-structure-and-algorithms/1572782647288.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/data-structure-and-algorithms/1572782647288.jpg
--------------------------------------------------------------------------------
/images/data-structure-and-algorithms/bst.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/data-structure-and-algorithms/bst.jpg
--------------------------------------------------------------------------------
/images/data-structure-and-algorithms/del-bst.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/data-structure-and-algorithms/del-bst.png
--------------------------------------------------------------------------------
/images/date-format-toggle-anim.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/date-format-toggle-anim.gif
--------------------------------------------------------------------------------
/images/git/--no-ff.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/git/--no-ff.gif
--------------------------------------------------------------------------------
/images/git/merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/git/merge.gif
--------------------------------------------------------------------------------
/images/git/rebase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/git/rebase.png
--------------------------------------------------------------------------------
/images/git/squash.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/git/squash.gif
--------------------------------------------------------------------------------
/images/hero-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/hero-list.png
--------------------------------------------------------------------------------
/images/hooks/useRef-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/hooks/useRef-1.gif
--------------------------------------------------------------------------------
/images/hooks/useRef-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/hooks/useRef-2.gif
--------------------------------------------------------------------------------
/images/http/http-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/http/http-1.png
--------------------------------------------------------------------------------
/images/http/http-request-headers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/http/http-request-headers.png
--------------------------------------------------------------------------------
/images/http/http-resp-headers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/http/http-resp-headers.png
--------------------------------------------------------------------------------
/images/http/http2-binary-framing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/http/http2-binary-framing.jpg
--------------------------------------------------------------------------------
/images/http/iso.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/http/iso.png
--------------------------------------------------------------------------------
/images/http/tcp-format.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/http/tcp-format.png
--------------------------------------------------------------------------------
/images/http/tcp-handshake.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/http/tcp-handshake.png
--------------------------------------------------------------------------------
/images/http/tcp-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/http/tcp-header.png
--------------------------------------------------------------------------------
/images/power-boost-calculator-anim.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/power-boost-calculator-anim.gif
--------------------------------------------------------------------------------
/images/power-booster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/power-booster.png
--------------------------------------------------------------------------------
/images/react/component-lifecycle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/react/component-lifecycle.jpg
--------------------------------------------------------------------------------
/images/react/dom-diff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/react/dom-diff.png
--------------------------------------------------------------------------------
/images/react/hooks-flow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/react/hooks-flow.jpg
--------------------------------------------------------------------------------
/images/react/lifecycle-new.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/react/lifecycle-new.jpg
--------------------------------------------------------------------------------
/images/react/lifeofaframe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/react/lifeofaframe.jpg
--------------------------------------------------------------------------------
/images/react/mobx-pic-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/react/mobx-pic-1.jpg
--------------------------------------------------------------------------------
/images/react/mobx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/react/mobx.jpg
--------------------------------------------------------------------------------
/images/react/react-17-rc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/react/react-17-rc.jpg
--------------------------------------------------------------------------------
/images/react/setState.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/react/setState.png
--------------------------------------------------------------------------------
/images/rn/bridge.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/rn/bridge.jpg
--------------------------------------------------------------------------------
/images/rn/new-architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/rn/new-architecture.jpg
--------------------------------------------------------------------------------
/images/rn/old-architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/rn/old-architecture.jpg
--------------------------------------------------------------------------------
/images/webpack/HMR-flow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/webpack/HMR-flow.jpg
--------------------------------------------------------------------------------
/images/wx-xcx/wx-xcx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JTangming/blog/1cc14616c75f6fcc0c608bcf3658157c6247e494/images/wx-xcx/wx-xcx.jpg
--------------------------------------------------------------------------------
/src/Computer-Basics/http.md:
--------------------------------------------------------------------------------
1 | - [Node HTTP/2 Server Push 从了解到放弃](https://zhuanlan.zhihu.com/p/36222488)
--------------------------------------------------------------------------------
/src/Flutter/README.md:
--------------------------------------------------------------------------------
1 | # Flutter
2 | ### 自绘引擎时代
3 |
4 | 自绘引擎即客户端仅提供一块画布即可获得从业务逻辑到功能呈现的多端高度一致的渲染体验。
5 |
6 | Flutter 渲染引擎依靠跨平台的 Skia 图形库来实现,Skia 引擎会将使用 Dart 构建的抽象的视图结构数据加工成 GPU 数据,交由 OpenGL 且最终提供给 GPU 渲染,完成这样的一个渲染闭环,其优势是可以最大程度上保证应用在跨平台、不同设备上的体验一致性。
7 |
8 | 开发语言选择的是 Dart,其同时支持 JIT(Just-in-Time,即时编译)和 AOT(Ahead-of-Time,预编译)。
9 |
10 | ### 关于 Skia
11 | Skia Graphics Library(SGL)是一个由C++编写的开放源代码图形库,最初由Skia公司开发,被Google收购后以New BSD License许可下开源。
12 |
13 | Skia 在图形转换、文字渲染、位图渲染方面都表现卓越,并提供了开发者友好的 API。
14 |
15 | 因此,架构于 Skia 之上的 Flutter,也因此拥有了彻底的跨平台渲染能力。通过与 Skia 的深度定制及优化,Flutter 可以最大限度地抹平平台差异,提高渲染效率与性能。
16 |
17 | 在底层渲染能力统一的基础上,上层开发接口和功能体验也就随即统一了,开发者开发体验更佳。同样的在界面渲染、绘制的过程中,Flutter 也做了很多优化处理,提升合成、渲染效率。
--------------------------------------------------------------------------------
/src/JS/promise.md:
--------------------------------------------------------------------------------
1 | ### 你能手写一个 Promise 吗?
2 | 想要手写一个 Promise 则需要了解 Promise/A+ 规范并遵循这个规范。
3 |
4 | #### 简易版(入门)
5 | 我们是这样来使用 promise:
6 | ```js
7 | const p1 = new Promise((resolve, reject) => {
8 | setTimeout(() => {resolve('success');}, 1000);
9 | })
10 |
11 | const p2 = p1.then(val => {
12 | console.log(val)
13 | })
14 | ```
15 |
16 | 通过 Promise/A+ 规范和对其 API 的熟知,实现一个 Promise 的具体思路是:
17 | - 实例化一个 Promise 后,实例对象有一个 then 原型方法;
18 | - 构建 Promise 对象时,需要传入一个 executor 函数,主要的业务流程都在 executor 函数中执行;
19 | - executor 接收两个参数(resolve and reject),执行成功,则调用 resolve 函数,否则调用 reject 函数;
20 | - Promise 一旦执行,状态将不可逆,即只可能是 pending -> fulfilled or rejected。
21 |
22 | 一个简易版的 Promise 如下:
23 | ```js
24 | // 声明一下三种状态值
25 | const PENDING = 'PENDING';
26 | const FULFILLED = 'FULFILLED';
27 | const REJECTED = 'REJECTED';
28 |
29 | class Promise {
30 | constructor(executor) {
31 | this.status = PENDING;
32 | this.value = undefined;
33 | this.reason = undefined;
34 | // 通过发布订阅来执行 then
35 | this.onResolvedCallbacks = [];
36 | this.onRejectedCallbacks= [];
37 |
38 | let resolve = (value) => {
39 | if(this.status === PENDING) {
40 | this.status = FULFILLED;
41 | this.value = value;
42 | this.onResolvedCallbacks.forEach(fn => fn());
43 | }
44 | }
45 |
46 | let reject = (reason) => {
47 | if(this.status === PENDING) {
48 | this.status = REJECTED;
49 | this.reason = reason;
50 | this.onRejectedCallbacks.forEach(fn => fn());
51 | }
52 | }
53 |
54 | try {
55 | executor(resolve, reject)
56 | } catch (error) {
57 | reject(error)
58 | }
59 | }
60 |
61 | then(onFulfilled, onRejected) {
62 | // 兼容一下 executor 同步使用的情况
63 | this.status === FULFILLED && onFulfilled(this.value);
64 | this.status === REJECTED && onRejected(this.reason);
65 |
66 | if (this.status === PENDING) {
67 | // 这里通过发布订阅的方式,将 onFulfilled 和 onRejected 函数存放到具体状态的 callback 中
68 | this.onResolvedCallbacks.push(() => {
69 | onFulfilled(this.value)
70 | });
71 | this.onRejectedCallbacks.push(()=> {
72 | onRejected(this.reason);
73 | })
74 | }
75 | }
76 | }
77 | ```
78 |
79 | #### 链式调用
80 | 链式调用即当 Promise 的 then 函数中 return 了一个值,不管是什么值,我们都能在下一个 then 中获取到,这就是所谓的then 的链式调用。
81 |
82 | 回到第一个例子,我们改造一下使用链式写法:
83 | ```js
84 | const p1 = new Promise((resolve, reject) => {
85 | setTimeout(() => {resolve('success');}, 1000);
86 | })
87 |
88 | const p2 = p1.then(val => {
89 | console.log('p2 success:', val);
90 | // 或者 return 一个 promise/非 promise 的值
91 | throw new Error('失败了')
92 | }).then(
93 | data => {
94 | console.log('success again', data);
95 | },
96 | ex => {
97 | console.log('fail again', data);
98 | }
99 | );
100 | ```
101 |
102 | 结合 Promise/A+ 规范,咱们需要做到:
103 | - promise 的 then 可以链式调用下去,这就需要每次执行完 promise.then 后返回一个新的 promise 实例;
104 | - 如果 then 原型方法参数 fulfilled or rejected 函数返回的是一个非 promise 的值,则传递给下一个 then 的第一个回调函数(fulfilled);
105 | - 如果返回的是一个 promise,那么等待其执行完毕,成功或者失败的值通过 resolve or reject 分别传递给到下一个 then 的 fulfilled or rejected;
106 |
107 | 大致的伪代码如下:
108 | ```js
109 | // status ...
110 | const resolvePromise = (newPromise, ret, resolve, reject) => {
111 | // 如果是非基础类型的数据,另外判断处理
112 | if ((typeof ret === 'object' && ret != null) || typeof ret === 'function') {
113 | try {
114 | // 如果上一个 then 的参数方法返回的是一个 promise,则等待 promise 执行完毕,再暴露执行结果
115 | let then = ret.then;
116 | if (typeof then === 'function') {
117 | // 返回的 promise 执行后,将成功或者失败的结果暴露给当前 then 的 fulfilled or rejected
118 | then.call(ret, succ => {
119 | resolve(succ);
120 | }, fail => {
121 | reject(fail);
122 | });
123 | } else {
124 | // 如果是引用类型(非 promise 的对象),则直接 resolve
125 | resolve(ret);
126 | }
127 | } catch (ex) {
128 | reject(ex);
129 | }
130 | } else {
131 | // 如果上一个 then 参数中的函数返回值 ret 是个基本类型的值则直接 resolve
132 | resolve(ret)
133 | }
134 | }
135 |
136 | class Promise {
137 | constructor(executor) {
138 | // this.xxxs ...
139 |
140 | let resolve = (value) => {
141 | if(this.status === PENDING) {
142 | this.status = FULFILLED;
143 | this.value = value;
144 | this.onResolvedCallbacks.forEach(fn=>fn());
145 | }
146 | }
147 |
148 | let reject = (reason) => {
149 | if(this.status === PENDING) {
150 | this.status = REJECTED;
151 | this.reason = reason;
152 | this.onRejectedCallbacks.forEach(fn=>fn());
153 | }
154 | }
155 |
156 | try {
157 | executor(resolve, reject)
158 | } catch (error) {
159 | reject(error)
160 | }
161 | }
162 |
163 | then(onFulfilled, onRejected) {
164 | // 兼容 onFufilled,onRejected 不传值
165 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {};
166 | onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
167 | // then 最终返回一个新的 promise 实例
168 | let newPromise = new Promise((resolve, reject) => {
169 | // ...
170 |
171 | if (this.status === PENDING) {
172 | this.onResolvedCallbacks.push(() => {
173 | setTimeout(() => {
174 | try {
175 | let ret = onFulfilled(this.value);
176 | resolvePromise(newPromise, ret, resolve, reject);
177 | } catch (e) {
178 | reject(e)
179 | }
180 | }, 0);
181 | });
182 |
183 | this.onRejectedCallbacks.push(()=> {
184 | setTimeout(() => {
185 | try {
186 | let ret = onRejected(this.reason);
187 | resolvePromise(newPromise, ret, resolve, reject)
188 | } catch (e) {
189 | reject(e)
190 | }
191 | }, 0);
192 | });
193 | }
194 | });
195 |
196 | return newPromise;
197 | }
198 | }
199 | ```
200 |
201 | > 注,使用 setTimeout 模拟了微任务
202 |
203 | #### 静态方法
204 | 常用的静态方法有 Promise.resolve(),Promise.reject(),可实现如下:
205 | ```js
206 | static resolve/*or reject**/(data) {
207 | return new Promise((resolve, reject) => {
208 | resolve(data); // or reject(data);
209 | })
210 | }
211 | ```
212 |
213 | #### Promise.all
214 | 实现思路是:
215 | - all 返回一个 promise;
216 | - 如果每一个并发的 promise 都正常返回,则把值维护到一个数组中
217 | - 否则直接 reject 即可
218 |
219 | ```js
220 | Promise.all = function(promises) {
221 | return new Promise((resolve, reject) => {
222 | let resultArr = [];
223 | let orderIndex = 0;
224 | const processResultByKey = (value, index) => {
225 | resultArr[index] = value;
226 | if (++orderIndex === promises.length) {
227 | resolve(resultArr);
228 | }
229 | }
230 | for (let i = 0; i < promises.length; i++) {
231 | let value = promises[i];
232 | if (value && typeof value.then === 'function') {
233 | value.then((value) => {
234 | processResultByKey(value, i);
235 | }, reject);
236 | } else { // 如果并发的数组并不是一个 promise,则直接加到记录结果的数组中即可
237 | processResultByKey(value, i);
238 | }
239 | }
240 | });
241 | }
242 | ```
243 | #### Promise.race
244 | ```js
245 | Promise.race = function(promises) {
246 | return new Promise((resolve, reject) => {
247 | for (let i = 0; i < promises.length; i++) {
248 | let val = promises[i];
249 | if (val && typeof val.then === 'function') {
250 | val.then(resolve, reject);
251 | } else {
252 | resolve(val)
253 | }
254 | }
255 | });
256 | }
257 | ```
258 | - 用来处理多个并发的值,遍历执行,如果是非 promise 实例,直接 resolve 即可;
259 | - 否则的话,等待 promise 执行完毕,任何一个有结果直接 resolve or reject 即可;
260 | - 注,promise 里边有控制仅 resolve 或 reject 一次了,这里不用再判断。
--------------------------------------------------------------------------------
/src/JS/实现一个 async await.md:
--------------------------------------------------------------------------------
1 | todo: https://github.com/sl1673495/javascript-codes/blob/master/async.js
--------------------------------------------------------------------------------
/src/React/Hooks/README.md:
--------------------------------------------------------------------------------
1 | // todo
2 | - [为什么顺序调用对 React Hooks 很重要?](https://overreacted.io/zh-hans/why-do-hooks-rely-on-call-order/)
3 | - [无意识设计-复盘React Hook的创造过程](https://github.com/shanggqm/blog/issues/4)
--------------------------------------------------------------------------------
/src/React/Hooks/useRef.md:
--------------------------------------------------------------------------------
1 | [useRef 官网](https://reactjs.org/docs/hooks-reference.html#useref) 的定义如下:
2 |
3 | useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
4 |
5 | whats the difference between useref and createref?
6 |
7 | - useRef 仅能用在 FunctionComponent;
8 | - createRef 仅能用在 ClassComponent,因为 createRef 并没有 Hooks 的效果,其值会随着 FunctionComponent 重复执行而不断被初始化;
9 | - createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用。
10 |
11 | 知道了两者的区别,举个例子来运用一下:
12 |
13 | ```js
14 | function App() {
15 | const [renderIndex, setRenderIndex] = useState(1);
16 | const refFromUseRef = useRef();
17 | const refFromCreateRef = createRef();
18 | if (!refFromUseRef.current) {
19 | refFromUseRef.current = renderIndex;
20 | }
21 | if (!refFromCreateRef.current) {
22 | refFromCreateRef.current = renderIndex;
23 | }
24 | return (
25 |
26 | Current render index: {renderIndex}
27 |
28 | First render index remembered within refFromUseRef.current:
29 | {refFromUseRef.current}
30 |
31 | First render index unsuccessfully remembered within
32 | refFromCreateRef.current:
33 | {refFromCreateRef.current}
34 |
35 |
38 |
39 | );
40 | }
41 | ```
42 |
43 | 例子中,refFromUseRef.current 会随着每次渲染加 1,而 refFromCreateRef.current 不会。
44 | 可以在 [codeSandbox](https://codesandbox.io/s/1rvwnj71x3) 实践一下。
45 |
46 | 如何使用 useRef?
47 |
48 | 看下面的例子:
49 | ```js
50 | function App() {
51 | const[count, setCount] =useState(0);
52 | const handleAlertClick = () => {
53 | setTimeout(() => {
54 | console.log("You clicked on:" + count);
55 | }, 3000);
56 | }
57 |
58 | return (
59 |
60 |
You clicked {count} times
61 |
62 |
63 |
64 | )
65 | }
66 | ```
67 | 打印日志的 count 和 标签里边展示的是一样的吗?
68 |
69 | 答案如下:
70 | 
71 |
72 | 每次点击,获取到新的 count 状态,React 重新渲染组件,handleAlertClick 使用的 count 是当下渲染是值。
73 |
74 | 使用 useRef 来实现打印的 count 与
标签里边渲染的一样:
75 |
76 | ```js
77 | function App() {
78 | const [count, setCount] = useState(0);
79 | const latestCount = useRef(count);
80 |
81 | useEffect(() => {
82 | latestCount.current=count;
83 | });
84 |
85 | const handleAlertClick = () => {
86 | setTimeout(() => {
87 | alert("You clicked on:"+ latestCount.current);
88 | }, 3000);
89 | }
90 |
91 | return(
92 |
93 |
You clicked {count} times
94 |
95 |
96 |
97 | );
98 | }
99 | ```
100 | 
101 |
102 | 因为 useRef 每次渲染都返回同一个引用,所以在 useEffect 中修改后,日志打印的也就一样了。
103 |
104 | ### Reference
105 | - [精读《useRef 与 createRef 的区别》](https://zhuanlan.zhihu.com/p/110247813)
106 | - [What's the difference between `useRef` and `createRef`?](https://stackoverflow.com/questions/54620698/whats-the-difference-between-useref-and-createref)
--------------------------------------------------------------------------------
/src/React/README.md:
--------------------------------------------------------------------------------
1 | # todo
2 |
3 | React,先从 ReactDOM.render开始看,里面的涉及兼容性, hydrate,Suspense,Hooks 的就可以不看,先整个走一遍,这样 mount 的过程就基本完成了,然后就是 update,自然从 setState开始看,涉及到事件/表单的可以不看,这样可以把整个 update 的流程基本搞懂,会发现哦,原来 mount 和 update 基本没有区别,然后再细化,比如更新队列/batch 是怎么工作的,(简单的)Concurrent 是怎么工作的,Reconciler 是怎么工作的,Scheduler 是怎么工作的,Hooks 是怎么工作的,到了细化的时候,就会越来越发现底层的设计的重要性和关联性,如 fiber 里各字段的含义和用途,各种 Flags 的用途,各种 WorkType,FiberType,EffectType 的含义和用途,各种 Dev 下的 mark,warning 触发的时机,这些又会反过来帮助去理解各个功能,甚至整个体系如生命周期,UI/UX 的思考等等。
4 |
5 |
6 | 比如 React 的体积很大,就不能一直停留在知道他很大的概念上,可以做的还有很多,比如大到底大多少,20k 还是 50k,是都这么大,还是 Production 和 Dev 不同,不同的部分到底是什么引起的,比如一般 development 版会大一些,但是为什么 React 的 development 版会大好几倍,再深入去了解,就会发现,一方面不仅多了很多功能(JIT 优化,user timing,strict mode,track,profiler,fast refresh),另一方面对于 development 下开发的体验的提升(error stack 信息,更良好的开发者可能犯的一些错误/anti pattern 的校验提醒,未来的 breaking change 的提前告知,底层数据结构的变化,启发式算法的策略,钩子单词拼错都有考虑)
7 |
--------------------------------------------------------------------------------
/src/React/RN/React Native's re-architecture in 2020.md:
--------------------------------------------------------------------------------
1 | # React Native's re-architecture in 2020
2 |
3 | > 如何大幅改善应用程序性能
4 |
5 | 2015 年 Facebook 发布了 RN,开发者可基于 React 开发具有原生功能的跨平台应用,这是一种解决方案。随着时间推移和社区使用的增加,RN 暴露很多的问题或缺陷。在 2018 年,Facebook 团队宣布了要对 React Native 进行重构,在下文中,将会对比新的重构是如何提升应用性能与开发效率的。
6 |
7 | ### 原架构
8 | 在本质上,RN 旨在提供一个平台无关(platform-agnostic)的解决方案,为了达到这个目的,RN 的主要目标是让开发者们可以使用 JS 来编写 React 代码,而在底层,RN 通过打包部署机制将 React reconciliation tree 转换为 Native 可以理解的内容,不管本机基础架构是什么,即:
9 | - 正常展示 UI
10 | - 访问原生功能
11 |
12 | 通常,对于 Android/iOS 的生态,当前的机制基本如下:
13 |
14 | 
15 |
16 | 在每个 RN 应用中运行着 3 个并行线程:
17 | - JS 线程,是读取和编译所有 JS 代码的地方,用于处理应用的大部分业务逻辑。通过 Metro 来打包 js bundle,App 启动后,JSC 引擎来运行这些 bundle
18 | - Native 线程,职责是处理 UI,它在需要更新 UI 和访问原生功能时,将会与 JS 线程进行通信。它可以分为两个部分:Native UI 与 Native Modules。其中 Native Modules 在启动时就要完全准备就绪,这就意味着 RN 即便不会用到蓝牙模块,它也会把蓝牙模块准备好
19 | - Shadow线程,用来布局计算,使用 Facebook 自家的 Yoga 布局引擎来计算 flexbox 布局,并发送回 Native UI 线程
20 |
21 | JS 线程和 Native 线程交互需要使用桥接器(bridge),在底层,这个 C++ 模块的桥接器几乎是基于异步队列构建的,数据的传递是先将一端数据序列化为字符串,然后通过这个异步队列传递,再在接收端反序列化。
22 |
23 | 所有的线程都要依赖于这个桥接器来传输异步 JSON 消息,这就意味着有拥塞的风险,例如长列表滚动,当 Native 端触发 onScroll 事件后需要发送异步消息给 JS 端,因为异步的缘故就会造成一定时延,在屏幕上就是可能出现空白。
24 |
25 | 同样的,在屏幕显示之前,Native 端也需要多次通过桥接器与 Yoga 引擎通信,然后才能得到计算出的布局。
26 |
27 | 新架构引用了 JSI,一种更加屌炸天的方式。
28 |
29 | ### 新架构
30 | RN 的新架构将会渐进的废弃桥接器转而使用一个新的接口 JSI(JavaScript Interface,An enabler for Fabric and TurboModules)。
31 |
32 | JSI 带来了新的改变:
33 | - JS bundle 不再与 JSC 绑定,可使用任意的 JS 引擎,也就是说可以使用更为高效的引擎,如 V8
34 | - 第二个改动是新架构的基础,即通过使用 JSI,JavaScript 可以保留对 C++ Host Objects 的引用并且能调用它们的方法。JavaScript 和 Native 模块间将实现真正的互通互信。
35 |
36 | 换句话说,JSI 允许所有线程之间完全互通(interoperability),通过共享所有权的概念,通过 JS 引擎,JavaScript 代码可以直截了当与 native 端通信,不再需要将 JSON 消息序列化来传递,解决了桥接器的拥塞和异步问题。
37 |
38 | 
39 |
40 | 除了显着改善不同线程之间的通信之外,新架构同样允许直接控制 native modules,这就意味着可以按需加载原生模块,而不是启动的时候全部初始化,这样可以显著的提高启动性能。
41 |
42 | 这种新机制还可能使许多不同的场景受益,既然我们已经掌握了 C++ 的强大功能,那么就很容易使用 RN 来开发复杂的系统。
43 |
44 | #### 还不止这些
45 | 多年来, RN 积攒了不少可废弃、无用的或者遗产(legacy)类的部分,RN 框架将清理这些部分来提高性能和可维护性。例如 WebView 和 AsyncStorage 等模块将被逐步移出 React Native 核心库,转交给社区来维护。
46 |
47 | 凭借新架构,以及 JavaScript 代码与 Native 之间相互操作的机制,RN 的重构将带来更好的性能和优良的开发体验,拭目以待吧。
48 |
49 | If you'd like to go deeper,参考这个视频吧,[React Native: See the Past, the Present and the Future](https://www.youtube.com/watch?v=7gm0owyO8HU)。
50 |
51 | ### Reference
52 | - [React Native's re-architecture in 2020](https://medium.com/swlh/react-natives-re-architecture-in-2020-9bb82659792c)
53 |
--------------------------------------------------------------------------------
/src/React/RN/React Native中JS与native的通信机制.md:
--------------------------------------------------------------------------------
1 | > 本文不是读源码并对其进行一个详解或整理回放,而是通过理解 RN 整个通信机制以便理解其核心,贯穿始末。
2 |
3 | RN 的通信机制涉及 Objective-C/Java、C++、JavaScript 间的交互,这一节先着重介绍 bridge(以 iOS 端为例)。
4 |
5 | 在开始前,我们先以 NativeModules 数据交互来做对 JS 调用原生模块方法实现 JS to Native 的数据通信。
6 |
7 | 原生侧定义的可供 RN 调用的模块及其方法,shrinkToFloatView 旨在唤起一个浮窗效果,示例代码如下:
8 | ```objc
9 | // GFCloudManagerModule.h
10 | #import
11 |
12 | @interface GFCloudManagerModule : NSObject
13 | @end
14 |
15 | // GFCloudManagerModule.m
16 | #import "GFCloudManagerModule.h"
17 |
18 | @implementation GFCloudManagerModule
19 | RCT_EXPORT_MODULE(GFCloudManager)
20 |
21 | RCT_EXPORT_METHOD(shrinkToFloatView) {
22 | dispatch_async(dispatch_get_main_queue(), ^{
23 | [[GFRemoteVideoAssistantManager sharedInstance] fullScreenShrinkToFloatBall];
24 | });
25 | }
26 | @end
27 | ```
28 |
29 | 在 JS 侧调用原生模块并执行 shrinkToFloatView 方法:
30 | ```js
31 | import { NativeModules } from 'react-native';
32 | var GFCloudManager = NativeModules.GFCloudManager;
33 | GFCloudManager.shrinkToFloatView();
34 | ```
35 |
36 | 以上例子实现了 JS 到 Native 的数据交互通信,理论上讲,JS 和 OC 谁也不是认识谁,怎么做到模块和方法互调的呢?答案是 bridge,交互的原理图如下:
37 | 
38 |
39 | 接下来看看它做了什么,是怎样实现数据互通的。
40 |
41 | React Native 用 iOS 自带的 JavaScriptCore 作为 JS 的解析引擎,要实现 JS to Native 的通信并非难事,主要有途径也就是传统的 jsBridge,参考[JS 与 Native 的几种通信方案](https://awhisper.github.io/2018/01/02/hybrid-jscomunication/)。但 RN 并没有使用到 JSC 提供的可以让 JS 和 OC 互调的特性,而是自己实现了一套通信机制。
42 |
43 | ### 模块配置信息(JSON messages)
44 |
45 | 首先 OC 侧要告诉 JS 它有哪些模块,模块里包含有哪些方法,JS 调用原生模块的方法是建立在有这些方法后才有可能让其去调用这些方法。
46 |
47 | 我们知道,要将 Native module(类、接口)曝露给 JS,module 需要实现 RCTBridgeModule 协议,并且在实现中要插入 RCT_EXPORT_MODULE 宏,具体曝露的方法也需要通过 RCT_EXPORT_METHOD 宏定义。
48 |
49 | 先来了解上面提到的两个关键的宏,用作收集所有需要暴露给 JS 的类和方法,将此宏放在将要实现的类中,在初始化加载时能自动向 bridge 注册相应的 module。
50 |
51 | TODO: 收集 ModuleID、MethodID、CallbackID and args.
52 |
53 | #### 编译时
54 | RCT_EXPORT_MODULE以及RCT_EXPORT_METHOD宏属于编译阶段的处理
55 | #### 注册NativeModule
56 | 通过实例化 RCTBridge、RCTCxxBridge
57 |
58 | // Initialize all native modules that cannot be loaded lazily
59 | [self _initModulesWithDispatchGroup:prepareBridge];
60 |
61 | 所有需要曝露给 JS 的 module 都已注册完成,并以RCTModuleData格式存储在RCTCxxBridge中。
62 |
63 | #### Instance
64 | _initializeBridge方法做的最重要的事情就是初始化Instance实例_reactInstance,此过程将所有曝露给 JS 的 module 由RCTModuleData格式转化为ModuleRegistry格式传入Instance。
65 |
66 | // This is async, but any calls into JS are blocked by the m_syncReady CV in Instance
67 | - (std::shared_ptr)_buildModuleRegistry
68 |
69 | #### JSCExecutor
70 | JSCExecutor的构造函数做了一条非常重要的事情:在 JS Context 中设置了一个全局代理nativeModuleProxy,其最终指向JSCExecutor类的getNativeModule方法。
71 |
72 | #### NativeToJsBridge
73 | NativeToJsBridge是 Native to JS 的桥接,所有从 Native 到 JS 的调用都是从NativeToJsBridge中的接口发出去的。
74 |
75 | 在构造函数中也将native module 注册信息转换为JSCNativeModules格式存储了下来。
76 |
77 | JS 是怎样把数据传给 OC,让 OC 去调相应方法的?
78 |
79 | JS 代码是运行在 JS 线程而非 main thread,JS 不会主动传递数据给 OC,在调 OC 方法时,会在上述前面步骤收集到的 ModuleID、MethodID 等数据加到一个队列里,等 OC 过来调JS的任意方法时,再把这个队列返回给 OC,此时OC再执行这个队列里要调用的方法。
80 |
81 | native开发里,什么时候会执行代码?只在有事件触发的时候,这个事件可以是启动事件,触摸事件,timer事件,系统事件,回调事件。而在React Native里,这些事件发生时OC都会调用JS相应的模块方法去处理,处理完这些事件后再执行JS想让OC执行的方法,而没有事件发生的时候,是不会执行任何代码的,这跟native开发里事件响应机制是一致的。
82 |
83 | 这一过程不必纠结,大致是 RCTBridge、RCTCxxBridge、NativeToJSBridge、JSExecutor、JsToNativeBridge的初始化,这里仅作为一个了解。
84 |
85 | ### Bridge 的调用流程
86 | 1. JS端调用某个OC模块暴露出来的方法;
87 | 2. 把上一步的调用分解为ModuleName,MethodName,arguments,再扔给MessageQueue处理,在这一步把JS的callback函数缓存在MessageQueue的一个成员变量里;
88 | 3. 在通过保存在MessageQueue的模块配置表把上一步传进来的ModuleName和MethodName转为ModuleID和MethodID;
89 | 4.把上述步骤得到的ModuleID,MethodId,CallbackID和其他参数argus传给OC;
90 | 5. OC接收到消息,通过模块配置表拿到对应的模块和方法;
91 | 6. 根据 RCTModuleMethod 对JS传过来的每一个参数进行处理,执行对应模块对应的方法;
92 | 7.
93 |
94 |
95 | 整个流程就是这样,简单概括下,差不多就是:JS函数调用转ModuleID/MethodID -> callback转CallbackID -> OC根据ID拿到方法 -> 处理参数 -> 调用OC方法 -> 回调CallbackID -> JS通过CallbackID拿到callback执行
--------------------------------------------------------------------------------
/src/React/RN/discussions-and-proposals.md:
--------------------------------------------------------------------------------
1 | ### Terminology(术语)
2 |
3 | - JSI(JavaScript Interface): 它是一个统一的轻量级通用 API,理论上可用于任何 JavaScript VM(虚拟机)。
4 | - TurboModules: NativeModules 的新架构,同样使用的是 JSI。
5 | - CodeGen: a tool to "automate" the compatibility between JS and native side。
6 |
7 | ### 旧架构现状
8 |
9 | 所有的 UI 操作(like creating native views, managing children, etc)都被 UIManagerModule 这一个 native module 接收处理,即 React Reconciller 通过 Bridge 发送 ui 指令,在 UIManagerModule 接收并委派给 UIImplementation 来一次创建 shadow nodes。shadow nodes 一颗 layout tree,传递给 Yoga 布局引擎来进行进行计算各个节点的相对坐标。
10 |
11 | 学习材料
12 | - [PPT](https://speakerdeck.com/kelset/react-native-past-future-and-present?slide=25)
13 | - [Fabric relies on some new strategies](https://github.com/react-native-community/discussions-and-proposals/issues/4#issuecomment-413696591)
--------------------------------------------------------------------------------
/src/React/RN/react-native-web.md:
--------------------------------------------------------------------------------
1 | react-native-web 帮你把 React Native 的组件作了一个 Web 端的实现,并提供相关打包工具,能够直接打包出一份能够跑在 Web 端的代码。
--------------------------------------------------------------------------------
/src/React/react-v17/React 17 RC 版发布:无新特性.md:
--------------------------------------------------------------------------------
1 | # React v17.0 Release Candidate: No New Features
2 |
3 | 原文:https://reactjs.org/blog/2020/08/10/react-v17-rc.html
4 |
5 | React 于 2020.08.10 发布了 React v17.0 Release Candidate,距离上一个 release 大版本已有两年半的时间,下文将描述这个主版本的功能和大家的一些预期,以及如何尝试使用这个版本。
6 |
7 | #### 没有新特性
8 | React 17 release 不是一个寻常的版本,因为没有新加任何面向开发人员的特性(developer-facing features),相反,这个 release 版本主要 focus 在使得 React 自升级更加简单(making it easier to upgrade React itself)。
9 |
10 | 我们一直围绕 react 新特性积极地开展工作,React 17 是我们战略中重要的一环(to roll them out without leaving anyone behind)。
11 |
12 | 实际上,React 17 是一个”stepping stone“版本,可以更安全地将由一个版本的 React 管理的树嵌入到由其他版本的React管理的树中。
13 |
14 | #### 渐进式升级
15 | 在过去 7 年中, React 升级采用 all-or-nothing 的方式,要么依旧沿用旧版,要么将整个应用全部升级,这可不是渐进式的玩法。
16 |
17 | 这种局面到目前为止得到解决了,但是我们正陷入“全有或全无”升级策略的局限。一些 API 改变了,例如,废弃了 [legacy context API](https://reactjs.org/docs/legacy-context.html),这不可能以自动化的方式完成。即使目前很多应用已经不再使用这个 API,但我们依然在 React 中保留它。我们选择无限期地在 React 中支持,还是已有的一些应用程序不再兼容新 React 特性,这两个选项都不是很好。
18 |
19 | 所以,我们想提供另一种选择。
20 |
21 | React 17 支持渐进式升级(React 17 enables gradual React upgrades),当你更新如从 React 15 更新到 16(或从 16 -> 17),通常是直接升级整个应用,这在大多情况是有效的,但是,如果代码库是在几年前编写的,并且没有维护不再频繁,它可能会变得越来越具有挑战性。尽管可以使用不同的版本维护,但是在 React 17 之前的版本,存在很大的隐患。
22 |
23 | 我们正在 React 17 修复这些问题,这就意味着在 React 18 或者后边的特性版本出来后,你将由更多的选择。第一个选择是立即升级整个应用程序,就像以前升级的套路一样。但是,你也可以选择一点点的升级上来,例如,你可以将应用大部分程序迁移到 React 18,但任然可以保留 React 17 上的一些如延迟加载的对话框或子路由。
24 |
25 | 这并不意味着不得不逐步升级,对于大多数应用程序,一次性升级可能是最好的选择。
26 |
27 | 要启用渐进式更新,我们需要对 React 事件系统进行一些更改。React 17 作为一个主版本是因为有潜在的 breaking change,事实上,我们也确实做了大量的组件改动,以使我们的 React 17 能够顺利的升级。
28 |
29 | #### 渐进升级的例子
30 | 我们准备了一个 [example repository](https://github.com/reactjs/react-gradual-upgrade-demo/)来演示了如何在必要时延迟加载旧版本的React。这个 demo 使用 Create React App,但是可能像之前一样需要借助一些工具。
31 |
32 | > 我们已将其他更改推迟到React 17之后。此版本的目标是实现逐步升级。如果升级到React 17太困难了,那将无法实现其目标。
33 |
34 | #### 更改事件委托
35 | 在 React 17 event handlers 将不再绑定到 document元素上,取而代之的是,它将被绑定到根 DOM 容器上。
36 | ```js
37 | const rootNode = document.getElementById('root');
38 | ReactDOM.render(, rootNode);
39 | ```
40 |
41 | 在 React 16 或更早的版本, React 将通过 `document.addEventListener()` 监听绑定事件,而 React 17 调用 `rootNode.addEventListener()`。
42 |
43 | 
44 |
45 | 为什么做这样的改动?主要是为了 react tree 管理更加安全,没有嵌套带来的安全隐患。这就是为什么建议尽可能的更新到 React 17 版本。
46 |
47 | **修复潜在问题**
48 | 与以往的重大变更一样,这个版本也做了很多的代码改动,调整了至少有 10 个模块的代码来支持这个版本。
49 |
50 | 例如,通过`document.addEventListener(...)`来监听一个事件,在 React 16 中,即使在回调函数中通过`e.stopPropagation()`来阻止冒泡我们依旧在 document 能收到事件,而在 React 17 中则不然,React 17 的合成事件更加接近于原生的事件。
51 |
52 | #### 其它 breaking changes
53 | React 17 尽可能的保持变动最小,例如,不会移除任何旧版已经废弃的 API,但是,它确实包括其他一些重大更改,根据我们的经验,这些更改相对安全。由于这些原因,我们必须在100,000多个组件中调整少于20个。
54 |
55 | ##### 与浏览器对齐
56 | 我们对事件系统进行了一些较小的更改:
57 | - onScroll事件不再冒泡以防止常见的混乱
58 | - React onFocus 和 onBlur 事件改为使用原生的 focusin 和 focusout 事件
59 | - 捕获阶段事件(例如onClickCapture)现在使用实际的浏览器捕获阶段侦听器。
60 |
61 | ##### 没有事件池
62 | React 17 移除了事件池,事件池并不能带来性能改观且干扰用户对事件的理解。举个 react 16 的例子:
63 | ```js
64 | function handleChange(e) {
65 | setData(data => ({
66 | ...data,
67 | // This crashes in React 16 and earlier:
68 | text: e.target.value
69 | }));
70 | }
71 | ```
72 | 例子中,如果不使用`e.persist()`则事件引用是不行的。在 React 17 中得以改正,旧版的事件池已经完全移除了。
73 |
74 | ##### Effect Cleanup Timing
75 | 例如使用 useEffect 来管理副作用:
76 | ```js
77 | useEffect(() => {
78 | // This is the effect itself.
79 | return () => {
80 | // This is its cleanup.
81 | };
82 | });
83 | ```
84 |
85 | 在 React 16 中相当于是在 `componentWillUnmount` 中来处理副作用,但是这是个同步的操作,一定程度是能影响用户体验的,在 React 17 中改成了异步的方式,也就是说在组件卸载中的状态时,清理副作用的操作将会在屏幕渲染完成后操作。
86 |
87 | > React 16 中可以使用 useLayoutEffect 来确保页面不卡顿
88 |
89 | ##### 组件返回undefined处理
90 | 在 React 16 及更早的版本,如果在组件中返回undefined则会出错,如:
91 | ```js
92 | function Button() {
93 | return; // Error: Nothing was returned from render
94 | }
95 | ```
96 | 或
97 | ```js
98 | function Button() {
99 | // We forgot to write return, so this component returns undefined.
100 | // React surfaces this as an error instead of ignoring it.
101 | ;
102 | }
103 | ```
104 |
105 | 在函数组件或者类组件中是可以有错误边界帮忙处理,报错后沿用之前的页面来展示,但是使用了 forwardRef 和 memo 包装的组件则真的会出问题了,React 17 对此做了修复,维持和类、函数组件一致的效果。
106 |
107 | ```js
108 | let Button = memo(() => {
109 | // We forgot to write return, so this component returns undefined.
110 | // React 17 surfaces this as an error instead of ignoring it.
111 | ;
112 | });
113 | ```
114 |
115 | ##### Native Component Stacks
116 | In React 17, the component stacks are generated using a different mechanism that stitches them together from the regular native JavaScript stacks. This lets you get the fully symbolicated React component stack traces in a production environment.
117 |
118 | ##### Removing Private Exports
119 |
120 | In React 17, these private exports have been removed. As far as we’re aware, React Native for Web was the only project using them, and they have already completed a migration to a different approach that doesn’t depend on those private exports.
121 |
122 | #### 总结
123 |
124 | React 17 RC 发布,一个没有新特性的大版本更新:
125 | - 提供多版本 React 共存的能力,为此改变了 React 原有的事件代理实现,从在 document 代理事件冒泡改为了在 rootNode 代理,避免多个 React 版本都监听
126 | - document 导致 e.stopPropagation() 失效的局面。可以说正是为了这个能力,React 进行了本次大版本更新,来确保用户在之后的大版本变化中有渐进式升级项目的余地微调
127 | - onScroll、onFocus、onBlur的行为,使其与浏览器原生事件的表现更接近不再使用复用事件对象的优化,使其表现更为正常useEffect 的清空操作改为异步,将在屏幕渲染完成后调用。如果特别需要在 unmount 之前的调用,需改用 useLayoutEffect
128 | - 在生产环境抛出异常时,提供原生的组件栈,方便定位问题所在
129 | - 删除了一些私有的导出
130 |
--------------------------------------------------------------------------------
/src/React/react/Fiber todo.md:
--------------------------------------------------------------------------------
1 | # 零散整理
2 |
3 | ### 协调/渲染 phase
4 |
5 | 这是React遍历组件树的阶段,并且:
6 |
7 | - 更新状态和属性
8 | - 调用生命周期钩子
9 | - 获取组件的children
10 | - 将它们与之前的children进行对比
11 | - 并计算出需要执行的DOM更新
12 |
13 | ### requestIdleCallback
14 |
15 | [using-requestidlecallback](https://developers.google.com/web/updates/2015/08/using-requestidlecallback)
16 |
17 | 很多时候 script 的执行会阻碍用户的一些行为交互,如何在不影响用户体验的同时执行不是优先级高的 work,an API that can help: requestIdleCallback。
18 |
19 | 注意区别:
20 | requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务
21 | 而requestIdleCallback的回调则不一定,属于低优先级任务
22 |
23 | 
24 | > life of a frame
25 |
26 | 如果某一帧在16.67ms完成了上述任务的话,就有可能执行到 requestIdleCallback 的回调,如下图:
27 | 
28 | > requestIdleCallback actually fits into a typical frame
29 |
30 | 一些实践
31 |
32 | 通过上面的两张图可以看出,requestIdlCallback 的回调是在一帧执行完成所有的 work,如 layout、painting,如果要做一些 DOM 操作的话,需要到下一帧中执行。如这样的一个实践,在requestAnimationFrame callback中使用document fragment,在下一帧的requestAnimationFrame callback中才执行真正的 DOM 插入删除或者更新操作。同样的,VDOM 也是这样的原理来实践。
33 |
34 | ### Fiber Reconciler
35 | * Ability to split interruptible work in chunks.
36 | * Ability to prioritize, rebase and reuse work in progress.
37 | * Ability to yield back and forth between parents and children to support layout in React.
38 | * Ability to return multiple elements from render().
39 | * Better support for error boundaries.
40 |
41 | 为了解决这些问题,React 不得不重新写为遍历树的算法,从依赖于内置堆栈的同步递归模型,变为具有链表和指针的异步模型。
42 |
43 | 递归模型的局限性:无法分解工作为增量单元,不能暂停特定组件的工作并在稍后恢复。
44 |
45 |
46 | # recursing-on-children
47 |
48 | [diff Algorithm](https://github.com/JTangming/blog/issues/17#issue-451191811)
49 |
50 |
51 | # React Fiber Architecture
52 |
53 | React Fiber 的目的是增强对类似动画、布局和手势操作这些场景的适应性。他的headline feature是增量渲染:将渲染工作拆分成块并将其分散到多个帧的能力。其他主要特性包括在新更新进来时能够暂定、取消或者重用;给不同更新分配优先级的能力;以及新的并发原函数。
54 |
55 | 知识预备:
56 | - [理解Component&Element&Instance](./理解Component&Element&Instance.md)
57 | - [Reconciliation](https://zh-hans.reactjs.org/docs/reconciliation.html)
58 | - [React Design Principles](https://zh-hans.reactjs.org/docs/implementation-notes.html)
59 |
60 | Fiber 要达成的目标:
61 | - pause work and come back to it later.
62 | - assign priority to different types of work.
63 | - reuse previously completed work.
64 | - abort work if it's no longer needed.
65 |
66 | A fiber represents a unit of work
67 |
68 | 对于 React,我们可以用函数方式表示为:`v = f(d)`(这么类比有助于后文的理解)。如Javascript,跟踪程序执行的方式是使用 call stack(相关概念可以参考 event loop)。
69 |
70 | UI 组件通常有复杂的业务逻辑,最终才返回 React Elements,这将比 functions 的执行有更多的副作用或者考虑。
71 |
72 | 那 React 怎么做?
73 |
74 | 基于以下两个API的启发:
75 | - requestIdleCallback schedules a low priority function to be called during an idle period
76 | - requestAnimationFrame schedules a high priority function to be called on the next animation frame.
77 |
78 | 但是基于这些 API,没法打断 rendering work into incremental units,如果使用既有的执行栈(call stack),程序会一直执行到栈空为止。
79 |
80 | 所以,React 实现了自己的 stack frame。
81 | - 自定义执行栈的行为来优化渲染 UIs
82 | - 随意打断执行栈并能手动处理相关操作(interrupt the call stack at will and manipulate stack frames manually)
83 |
84 | 好处是:能保持 stack frams 在内存中,以便能在任何时候操作执行。
85 |
86 | 除了执行栈的调度(scheduling),另外一些潜能是并发和错误边界(error boundaries)。
87 |
88 | That's the purpose of React Fiber,a virtual stack frame。
89 |
90 | A fiber corresponds to(对应) a stack frame, but it also corresponds to an instance of a component.
91 |
92 | ### Structure of a fiber
93 |
94 | **type & key**
95 | 意图和React elements的一样,其实是直接copy于 Element。key 在 reconciliation 阶段用来确认 fiber 是否可以复用。
96 |
97 | ### Reference
98 | - https://github.com/acdlite/react-fiber-architecture
99 |
100 |
101 | Fiber === Virtual Stack Frame
--------------------------------------------------------------------------------
/src/React/react/理解Component&Element&Instance.md:
--------------------------------------------------------------------------------
1 | 在理解 react 原理或者阅读源码过程中,需要整明白这几个概念,Component, Element, Instance 之间有什么区别和联系?
2 |
3 | - Element 是一个纯 JavaScript Object
4 | 对于每一个组件来说,render 函数返回的也正是一个 element,而不是真正的 DOM 节点。JSX 是 React.createElement(type, props, ...children) 函数的语法糖([try it out](https://link.zhihu.com/?target=https%3A//babeljs.io/repl/)),createElement 函数最终返回的是 Element。它的结构是:
5 | ```js
6 | {
7 | type: 'div',
8 | props: {
9 | className,
10 | children,
11 | }
12 | }
13 | ```
14 |
15 | > Element 的 type 可以是 string , 也可以是 function| class。
16 |
17 | > 其中 children 的数据结构是单个 Element 或 Element 数组,这样的数据结构满足了递归的需要。
18 |
19 | - Component
20 | 组件(Component)有以下几种类型:
21 | - DOMComponent:,如 `div, span, ul` 等
22 | - Composite Component(复合组件),分为 functional component 和 class component
23 | - TextComponent,即 number or string
24 |
25 | - Instance 是 Component 的实例化之后的对象
26 | 在 React 中 instances 的概念并不是十分重要,因为我们只需通过 Elements 来描述界面 UI 或者声明 Component,React 自己会管理 Component 的 instance。通过实例化 Component 后,我们通过 this 能做许多的操作,如 setState。
27 |
28 | > 注,functional component 是纯函数,它们并没有实例
29 |
30 | ### Reference
31 | - [React Components, Elements, and Instances](https://reactjs.org/blog/2015/12/18/react-components-elements-and-instances.html)
--------------------------------------------------------------------------------
/src/nodeJs/README.md:
--------------------------------------------------------------------------------
1 | nodejs 的源码由js、c++、c组成。
2 |
3 | 1 libuv是c语言编写。理解libuv除了需要了解c语法外,更多的是对操作系统和网络的理解,有些经典的书籍可以参考,比如《unix网络编程》1,2两册,《linux系统编程手册》上下两册,《tcp/ip权威指南》等等,不过更好的理解方式还是阅读源码。
4 | 2 c++主要是利用v8提供的能力对js进行拓展,也有一部分功能使用c++实现,总的来说c++的作用更多是胶水层,利用v8作为桥梁,连接libuv和js。不会c++,也不完全影响源码的阅读,但是会c++会更好。阅读c++层代码,除了语法外,还需要对v8的概念和使用有一定的了解和理解。
--------------------------------------------------------------------------------
/src/state-management/Redux or Mobx --前端应用状态管理方案的探索与思考.md:
--------------------------------------------------------------------------------
1 | # Redux or Mobx --前端应用状态管理方案的探索与思考
2 |
3 | ## 1. 前言
4 | 前端的发展日新月异,Angular/React/Vue 等前端框架的兴起,为我们的应用开发带来新的体验。React Native/Weex/微信小程序等技术方案的出现,又进一步扩展了前端技术的应用范围。随着这些技术的革新,我们可以更加便利的编写更高复杂度、更大规模的应用,而在这一过程中,如何优雅的管理应用中的数据状态成为了一个需要解决的问题。
5 |
6 | 为解决这一问题,我们在项目中相继尝试了当前热门的前端状态管理工具,对前端应用状态管理方案进行了深入探索与思考。
7 |
8 |
9 | ## 2. Dive into Redux
10 |
11 | ### 2.1 Redux 是什么
12 | Redux 是前端应用的状态容器,提供可预测的状态管理,其基本定义可以用下列公式表示:
13 |
14 | ```
15 | (state, action) => newState
16 | ```
17 | 其特点可以用以下三个原则来描述
18 |
19 | * 单一数据源
20 |
21 | 在 Redux 中,整个应用的状态以状态树的形式,被存储在一个单例的 store 中。
22 | * 状态数据只读
23 |
24 | 惟一改变状态数据的方法是触发 action,action 是一个用于描述已发生事件的普通对象。
25 | * 使用纯函数修改状态
26 |
27 | 在 Redux 中,通过纯函数,即 reducer 来定义如何修改 state。
28 |
29 | 从上述原则中,可以看出,构成 Redux 的主要元素有 action、reducer、store,借用一张经典图示,可以进一步理解 Redux 主要元素和数据流向。
30 |
31 |
32 |
33 |
34 | ### 2.2 探索 The Redux way
35 | #### 2.2.1 异步方案选型
36 | Redux 中通过触发 action 修改状态,而 action 是一个普通对象,action creator 是一个纯函数。如何在 action creator 中融入、管理我们的异步请求,是在实际开发中首先需要解决的问题。
37 |
38 | 当前 Redux 生态活跃,出现了不少异步管理中间件。在我们的实践中,认为大致可以分为两类:
39 |
40 | ##### 1)以 redux-thunk 为代表的中间件
41 |
42 | 使用 redux-thunk 完成一个异步请求的过程如下:
43 |
44 | ```
45 | //action creator
46 | function loadData(userId){
47 | return (dispatch,getState) => {
48 | dispatch({type:'LOAD_START'})
49 | asyncRequest(userId).then(resp=>{
50 | dispatch({type:'LOAD_SUCCESS',resp})
51 | }).catch(error=>{
52 | dispatch({type:'LOAD_FAIL',error})
53 | })
54 | }
55 | }
56 |
57 | //component
58 | componentDidMount(){
59 | this.props.dispatch(loadData(this.props.userId));
60 | }
61 | ```
62 | 在上述示例中,引入 redux-thunk 后,我们将异步处理和业务逻辑定义在一个方法中,利用中间件机制,将方法的执行交由中间件管理。
63 |
64 | 上例是一个简单的异步请求,在代码中我们需要主动的根据异步请求的执行状态,分别触发请求开始、成功和失败三个 action。这一过程显得繁琐,当应用中有大量这类简单请求时,项目中会充满这种重复代码。
65 |
66 | 针对这一问题,出现了一些用于简化这类简单请求的工具。实际开发中,我们选择了 redux-promise-middleware 中间件,使用这一中间件来完成上述请求的代码示例如下:
67 |
68 | ```
69 | //action creator
70 | function loadData(userId){
71 | return {
72 | type:types.LOAD_DATA,
73 | payload:asyncRequest(userId)
74 | }
75 | }
76 |
77 | //component
78 | componentDidMount(){
79 | this.props.dispatch(loadData(this.props.userId));
80 | }
81 | ```
82 |
83 | 引入 redux-promise-middleware 中间件,我们在 action creator 中返回一个与 redux action 结构一致的普通对象,不同的是,payload 属性是一个返回 Promise 对象的异步方法。通过将异步方法的执行过程交由 redux-promise-middleware 中间件处理,中间件会帮助我们处理异步请求的状态,根据异步请求的结果为当前操作类型添加 `PEDNGING/FULFILLED/REJECTED` 状态,我们的代码得到大幅简化。
84 |
85 | **redux-promise-middleware 中间件适用于简化简单请求的代码,开发中推荐混合使用 redux-promise-middleware 中间件和 redux-thunk。**
86 |
87 | ##### 2)以 redux-saga 为代表的中间件
88 |
89 | 以 redux-thunk 为代表的中间件可以满足一般的业务场景,但当业务对用户事件、异步请求有更细粒度的控制需求时,redux-thunk 不能便利的满足。此时,可以选择以 redux-saga 为代表的中间件。
90 |
91 | redux-saga 可以理解为一个和系统交互的常驻进程,其中,Saga 可简单定义如下:
92 |
93 | ```
94 | Saga = Worker + Watcher
95 | ```
96 | 采用 redux-saga 完成异步请求,示例如下:
97 |
98 | ```
99 | //saga
100 | function* loadUserOnClick(){
101 | yield* takeLatest('LOAD_DATA',fetchUser);
102 | }
103 |
104 | function* fetchUser(action){
105 | try{
106 | yield put({type:'LOAD_START'});
107 | const user = yield call(asyncRequest,action.payload);
108 | yield put({type:'LOAD_SUCCESS',user});
109 | }catch(err){
110 | yield put({type:'LOAD_FAIL',error})
111 | }
112 | }
113 |
114 | //component
115 | dispatch({type:'LOAD_DATA',payload:'001'})}>load data
116 | ```
117 |
118 | 与 redux-thunk 相比,使用 redux-saga 有几处明显的变化:
119 |
120 | * 在组件中,不再 `dispatch(action creator)`,而是 `dispatch(pure action)`
121 | * 组件中不再关注由谁来处理当前 action,action 经由 root saga 分发
122 | * 具体业务处理方法中,通过提供的 call/put 等帮助方法,**声明式**的进行方法调用
123 | * 使用 ES6 Generator 语法,简化异步代码语法
124 |
125 | 除开上述这些不同点,**redux-saga 真正的威力,在于其提供了一系列帮助方法,使得对于各类事件可以进行更细粒度的控制,从而完成更加复杂的操作**。
126 |
127 | 简单列举如下:
128 |
129 | * 提供 takeLatest/takeEvery/throttle 方法,可以便利的实现对事件的仅关注最近事件、关注每一次、事件限频
130 | * 提供 cancel/delay 方法,可以便利的取消、延迟异步请求
131 | * 提供 race(effects),[…effects] 方法来支持竞态和并行场景
132 | * 提供 channel 机制支持外部事件
133 |
134 | 在 Redux 生态中,除了 redux-saga 中间件,还有另一个中间件,redux-observable 也可以满足这一场景。
135 |
136 | redux-observable 是基于RxJS的用于处理异步请求的中间件,借助 RxJS 的各种操作符和帮助方法,redux-observable 也能实现对各类事件的细粒度操作,比如取消、限频、延迟请求等。
137 |
138 | **redux-saga 与 redux-observable 适用于对事件操作有细粒度需求的场景,同时他们也提供了更好的可测试性,当你的应用逐渐复杂需要更加强大的工具时,他们会成为很好的帮手。**
139 |
140 | #### 2.2.2 应用状态设计
141 | 如何设计应用状态的数据结构是一个值得思考的问题,在实践中,我们总结了两点数据划分的指导性原则,应用状态扁平化和抽离公共状态。
142 | ##### 1) 应用状态扁平化
143 | 在我们的项目中,有联系人、聊天消息和当前联系人对象。最初我们采用如下数据结构:
144 |
145 | ```
146 | {
147 | contacts:[
148 | {
149 | id:'001',
150 | name:'zhangsan',
151 | messages:[
152 | {
153 | id:1,
154 | content:{
155 | text:'hello'
156 | },
157 | status:'succ'
158 | },
159 | ...
160 | ]
161 | },
162 | ...
163 | ],
164 | selectedContact:{
165 | id:'001',
166 | name:'zhangsan',
167 | messages:[
168 | {
169 | id:1,
170 | content:{
171 | text:'hello'
172 | },
173 | status:'succ'
174 | },
175 | ...
176 | ]
177 | }
178 | }
179 | ```
180 |
181 | 采用上述数据机构,带来几个问题
182 |
183 | * 消息对象与联系人对象耦合,消息对象的变更操作引发联系人对象的变更操作
184 | * 联系人集合和当前联系人对象数据冗余,当数据更新时需要多处修改来保持数据一致性
185 | * 数据结构嵌套过深,不便于数据更新,一定程度上导致更新时的耗时增加
186 |
187 |
188 | 将数据扁平化、解除耦合,得到如下数据结构
189 |
190 | ```javascript
191 | {
192 | contacts:[
193 | {
194 | id:'001',
195 | name:'zhangsan'
196 | },
197 | ...
198 | ],
199 | messages:{
200 | '001':[
201 | {
202 | id:1,
203 | content:{
204 | text:'hello'
205 | },
206 | status:'succ'
207 | },
208 | ...
209 | ],
210 | ...
211 | },
212 | selectedContactId:'001'
213 | }
214 | ```
215 | 相对于之前的问题,上述数据结构具有以下优点
216 |
217 | * 细粒度的更新数据,进而细粒度控制视图的渲染 * 结构清晰,避免更新数据时,复杂的数据操作 * 去除冗余数据,避免数据不一致 ##### 2)抽离公共状态
218 | 在领域对象之外,往往还有另外一些与请求过程相关的状态数据,如下所示
219 |
220 | ```
221 | {
222 | user: {
223 | isError: false, // 加载用户信息失败
224 | isLoading: false, // 加载用户中
225 | ...
226 | entity: { ... },
227 | },
228 | messages: {
229 | isLoading: true, // 加载消息中
230 | nextHref: '/api/messages?offset=200&size=100', // 消息分页数据
231 | ...
232 | entities: { ... },
233 | },
234 | authors: {
235 | isError: false, // 加载作者失败
236 | isLoading: false, // 加载作者中
237 | nextHref: '/api/authors?offset=50&size=25', // 作者分页数据
238 | ...
239 | entities: { ... },
240 | },
241 | }
242 | ```
243 | 上述数据结构中,我们按照功能模块将状态数据内聚。采用上述结构,会导致我们需要写很多基本重复的 action,如下所示
244 |
245 | ```
246 | {
247 | type: 'USER_IS_LOADING',
248 | payload: {
249 | isLoading,
250 | },
251 | }
252 |
253 | {
254 | type: 'MESSAGES_IS_LOADING',
255 | payload: {
256 | isLoading,
257 | },
258 | }
259 |
260 | {
261 | type: 'AUTHORS_IS_LOADING',
262 | payload: {
263 | isLoading,
264 | },
265 | }
266 | ...
267 | ```
268 | 我们分别为 user 、message 、author 定义了一系列 action,他们作用类似,代码重复。为解决这一问题,我们可以将这类状态数据抽离,不再简单的按照功能模块内聚,抽离后的状态数据如下所示
269 |
270 | ```
271 | {
272 | isLoading: {
273 | user: false,
274 | messages: true,
275 | authors: false,
276 | ...
277 | },
278 | isError: {
279 | userEdit: false,
280 | authorsFetch: false,
281 | ...
282 | },
283 | nextHref: {
284 | messages: '/api/messages?offset=200&size=100',
285 | authors: '/api/authors?offset=50&size=25',
286 | ...
287 | },
288 | user: {
289 | ...
290 | entity: { ... },
291 | },
292 | messages: {
293 | ...
294 | entities: { ... },
295 | },
296 | authors: {
297 | ...
298 | entities: { ... },
299 | },
300 | }
301 | ```
302 |
303 | 采用这一结构,可以避免定义大量相似的 action type,避免编写重复的 action。
304 |
305 | #### 2.2.3 修改状态数据
306 | 将应用状态数据不可变化是使用 Redux 的一般范式,有多种方式可以实现不可变数据的效果,我们分别尝试了 immutable.js 和 seamless-immutable.js,并在实际开发中选择了 seamless-immutable.js。
307 |
308 | ##### 1)immutable.js
309 |
310 | immutable.js 是一个知名度很高的不可变数据实现库。它为人称道的是基于共享数据结构所带来的数据修改时的高性能,但是在我们的使用过程中,发现其易用性不够友好,使用体验并不美好。
311 |
312 | * 首先,immutable.js 实现的是 shallowly immutable,如下示例中,notFullyImmutable 中的对象属性仍然是可变的
313 |
314 | ```
315 | var obj = {foo: "original"};
316 | var notFullyImmutable = Immutable.List.of(obj);
317 |
318 | notFullyImmutable.get(0) // { foo: 'original' }
319 |
320 | obj.foo = "mutated!";
321 |
322 | notFullyImmutable.get(0) // { foo: 'mutated!' }
323 | ```
324 |
325 | * 另外,immutable.js 使用了自定义的数据结构,这意味着贯穿我们的应用都需要明确当前使用的是 immutable.js 的数据结构。需要获取数据时,需要使用 get 方法,而不能使用 `obj.prop` 或者 `obj[prop]`。在需要将数据同外部交互,如存储或者请求时,需要将特有数据结构转换成原生 JavasScript 对象。
326 | * 最后,以 `state.set('key',obj)` 形式更新状态时,obj 对象不能自动的 immutable 化。
327 |
328 | ##### 2)seamless-immutable.js
329 | 上述问题使得我们在开发中不断的需要停下来思考当前写法是否正确,于是我们继续尝试,最后选择使用 seamless-immutable.js 来帮助实现不可变数据。
330 |
331 | seamless-immutable.js 意为无缝的 immutable,与 immutable.js 不同,它没有定义新的数据结构,其基本使用如下所示:
332 |
333 | ```
334 | var array = Immutable(["totally", "immutable", {hammer: "Can’t Touch This"}]);
335 |
336 | array[1] = "I'm going to mutate you!"
337 | array[1] // "immutable"
338 |
339 | array[2].hammer = "hm, surely I can mutate this nested object..."
340 | array[2].hammer // "Can’t Touch This"
341 |
342 | for (var index in array) { console.log(array[index]); }
343 | // "totally"
344 | // "immutable"
345 | // { hammer: 'Can’t Touch This' }
346 |
347 | ```
348 | 根据我们的使用体验,seamless-immutable.js 易用性优于 immutable.js。但是在选择之前,有一点需要了解的是,在数据修改时,seamless-immutable.js 性能低于 immutable.js。数据嵌套层级越深,数据量越大,性能差异越明显。这里需要根据业务特点来做选择,我们的业务没有大批量的深度数据修改需求,易用性比性能更重要。
349 | #### 2.2.4 在应用中使用
350 | Redux 可以应用在多种场景,在我们的开发中,已经将它应用到了 React Native、Angular 1.x 重构和微信小程序的项目上。
351 |
352 | 在前文介绍 Redux 三原则时提到,Redux 具有单一数据源,触发 action 时,Redux store 在执行状态更新逻辑后,会执行注册在 store 上的事件处理函数。
353 |
354 | 基于上述过程,在简单的 HTML 中可以如下使用 Redux:
355 |
356 | ```
357 | const initialState = {count:0};
358 | const counterReducer = (state=initialState,action) => {...}
359 |
360 | const {createStore} = Redux;
361 | const store = createStore(counterReducer);
362 |
363 | const renderApp = () =>{
364 | const {count} = store.getState();
365 | document.body.innerHTML = `
366 |
367 |
Clicked : ${count} times
368 |
371 |
372 | `;
373 | };
374 |
375 | store.subscribe(renderApp);
376 | renderApp();
377 | ```
378 |
379 | 结合前端框架使用 Redux 时,社区中已经有了 react-redux、ng-redux 这类的帮助工具,甚至对应微信小程序,也有了类似的实现方案。其实现原理均一致,都是通过全局对象绑定 Redux store,使得在应用组件中可以获得 store 中的状态数据,并向 store 注册事件处理函数,用来在状态变更时触发视图的更新。
380 |
381 | 当我们在项目中应用 Redux 时,也对代码文件的组织进行了一番探索。通常我们按照如下方式组织代码文件
382 |
383 | ```
384 | |--components/
385 | |--constants/
386 | ----userTypes.js
387 | |--reducers/
388 | ----userReducer.js
389 | |--actions/
390 | ----userAction.js
391 | ```
392 | 严格遵循这一模式并无不可,但是当项目规模逐渐扩大,文件数量增多后,切换多文件夹寻找文件变得有些繁琐,在这一时刻,可以考虑尝试 Redux Ducks 模式。
393 |
394 | ```
395 | |--components
396 | |--redux
397 | ----userRedux
398 | ```
399 | 所谓 Ducks 模式,也即经典的鸭子类型。这里将同一领域内,Redux 相关元素的文件合并至同一个文件 userRedux 中,可以避免为实现一个简单功能频繁在不同目录切换文件。
400 |
401 | 与此同时,根据我们的使用经验,鸭子模式与传统模式应当灵活的混合使用。**当业务逻辑复杂,action与reducer各自代码量较多时,按照传统模式拆分可能是更好的选择。**此时可以如下混合使用两种模式
402 |
403 | ```
404 | |--modules/
405 | ----users/
406 | ------userComponent.js
407 | ------userConstant.js
408 | ------userAction.js
409 | ------userReducer.js
410 | ----messages/
411 | ------messageComponent.js
412 | ------messageRedux.js
413 | ```
414 |
415 |
416 | ## 3. Dive into Mobx
417 | ### 3.1 Mobx 是什么
418 | Mobx 是一个简单、可扩展的前端应用状态管理工具。Mobx 背后的哲学很简单:当应用状态更新时,所有依赖于这些应用状态的观察者(包括UI、服务端数据同步函数等),都应该自动得到细粒度地更新。
419 |
420 | Mobx 中主要包含如下元素:
421 |
422 | * State
423 |
424 | State 是被观察的应用状态,状态是驱动你的应用的数据。
425 |
426 | * Derivations
427 |
428 | Derivations 可以理解为衍生,它是应用状态的观察者。Mobx 中有两种形式的衍生,分别是 Computed values 和 Reactions。其中,Computed values 是计算属性,它的数据通过纯函数由应用状态计算得来,当依赖的应用状态变更时,Mobx 自动触发计算属性的更新。Reactions 可简单理解为响应,与计算属性类似,它响应所依赖的应用状态的变更,不同的是,它不产生新的数据,而是输出相应的副作用(side effects),比如更新UI。
429 |
430 | * Actions
431 |
432 | Actions 可以理解为动作,由应用中的各类事件触发。Actions 是变更应用状态的地方,可以帮助你更加清晰的组织你的代码。
433 |
434 | Mobx 项目主页中的示例图,清晰的描述了上述元素的关系:
435 |
436 |
437 |
438 |
439 | ### 3.2 探索 The Mobx Way
440 | 在探索 Redux 的过程中,我们关注异步方案选型、应用状态设计、如何修改状态以及怎样在应用中使用。当我们走进 Mobx,探索 Mobx 的应用之道时,分别从应用状态设计、变更应用状态、响应状态变更以及如何在实际、复杂项目中应用进行了思考。
441 | #### 3.2.1 应用状态设计
442 | 设计应用状态是开始使用 Mobx 的第一步,让我们开始设计应用状态:
443 |
444 | ```
445 | class Contact {
446 | id = uuid();
447 | @observable firstName = "han";
448 | @observable lastName = "meimei";
449 | @observable messages = [];
450 | @observable profile = observable.map({})
451 | @computed get fullName() {
452 | return `${this.firstName}, ${this.lastName}`;
453 | }
454 | }
455 | ```
456 |
457 | 上述示例中,我们定义领域模型 Contact 类,同时使用 ES.next decorator 语法,用 @observable 修饰符定义被观察的属性。
458 |
459 | 领域模型组成了应用的状态,定义领域模型的可观察属性是 Mobx 中应用状态设计的关键步骤。在这一过程中,我们需要关注两方面内容,分别是 Mobx 数据类型和描述属性可观察性的操作符。
460 |
461 | ##### 1)Mobx 数据类型
462 |
463 | Mobx 内置了几种数据类型,包括 objects、arrays、maps 和 box values。
464 |
465 | * objects 是 Mobx 中最常见的对象,示例中 Contact 类的实例对象,或者通过 `mobx.observable({ key:value})` 定义的对象均为 Observable Objects。
466 | * box values 相对来说使用较少,它可以将 JavaScript 中的基本类型如字符串转为可观察对象。
467 | * arrays 和maps 是 Mobx 对 JavaScript 原生 Array 和 Map 的封装,用于实现对更复杂数据结构的监听。
468 |
469 | 当使用 Mobx arrays 结构时,有一个需要注意的地方,如下所示,经封装后,它不再是一个原生 Array 类型了。
470 |
471 | ```
472 | Array.isArray(observable([1,2,3])) === false
473 | ```
474 |
475 | 这是一个我们最初使用时,容易走进的陷阱。当需要将一个 observable array 与第三方库交互使用时,可以对它创建一份浅复制,像下面这样,转为原生 JavaScript:
476 |
477 | ```
478 | Array.isArray(observable([]).slice()) === true
479 | ```
480 |
481 | 默认情况下,领域模型中被预定义为可观察的属性才能被监听,而为实例对象新增的属性,不能被自动观察。使用 Mobx maps,即使新增的属性也是可观察的,我们不仅可以响应集合中某一元素的变更,也能响应新增、删除元素这些操作。
482 |
483 | 除了使用上述几种数据类型来定义可观察的属性,还有一个很常用的概念,计算属性。通常,计算属性不是领域模型中的真实属性,而是依赖其他属性计算得来。系统收集它对其他属性的依赖关系,仅当依赖属性变更时,计算属性的重新计算才会被触发。
484 |
485 | ##### 2)描述属性可观察性的操作符
486 | Mobx 中的 Modifiers 可理解为描述属性可观察性的操作符,被用来在定义可观察属性时,改变某些属性的自动转换规则。
487 |
488 | 在定义领域模型的可观察属性时,有如下三类操作符值得关注:
489 |
490 | * observable.deep
491 |
492 | deep 操作符是默认操作符,它会递归的将所有属性都转换为可观察属性。通常情况下,这是一个非常便利的方式,无需更多操作即可将定义的属性进行深度的转换。
493 | * observable.ref
494 |
495 | ref 操作符表示观察的是对象的引用关系,而不关注对象自身的变更。
496 |
497 | 例如,我们为 Contact 类增加 address 属性,值为另一个领域模型 Address 的实例对象。通过使用 ref 修饰符,在 address 实例对象的属性变更时,contact 对象不会被触发更新,而当 address 属性被修改为新的 address 实例对象,因为引用关系变更,contact 对象被触发更新。
498 |
499 | ```
500 | let address = new Address();
501 | class Contact {
502 | ...
503 | @observable.ref address = address;
504 | }
505 | let contact = new Contact();
506 | address.city = 'New York'; //不会触发更新通知
507 | contact.address = new Address();//引用关系变更,触发更新通知
508 | ```
509 | * observable.shallow
510 |
511 | shallow 操作符表示对该属性进行一个浅观察,通常用于描述数组类型属性。shallow 是与 deep 相对的概念,它不会递归的将子属性转换为可观察对象。
512 |
513 | ```
514 | let plainObj = {key:'test'};
515 | class Contact {
516 | ...
517 | @observable.shallow arr = [];
518 | }
519 | let contact = new Contact();
520 | contact.arr.push(plainObj); //plainObj还是一个plainObj
521 | //如果去掉shallow修饰符,plainObj被递归转换为observable object
522 | ```
523 |
524 | 当我们对 Mobx 的使用逐渐深入,应当再次检查项目中应用状态的设计,合理的使用这些操作符来限制可观察性,对于提升应用性能有积极意义。
525 |
526 | #### 3.2.2 修改应用状态
527 | 在 Mobx 中修改应用状态是一件很简单的事情,在前文的示例中,我们直接修改领域模型实例对象的属性值来变更应用状态。
528 |
529 | ```
530 | class Contact {
531 | @observable firstName = 'han;
532 | }
533 | let contact = new Contact();
534 | contact.firstName = 'li';
535 | ```
536 |
537 | 像这样修改应用状态很便捷,但是会来带两个问题:
538 |
539 | * 需要修改多个属性时,每次修改均会触发相关依赖的更新
540 | * 对应用状态的修改分散在项目多个地方,不便于跟踪状态变化,降低可维护性
541 |
542 | 为解决上述问题,Mobx 引入了 action。在我们的使用中,建议通过设置 `useStrict(true)`,使用 action 作为修改应用状态的唯一入口。
543 |
544 | ```
545 | class Contact {
546 | @observable firstName = 'han';
547 | @observable lastName = "meimei";
548 | @action changeName(first, last) {
549 | this.firstName = first;
550 | this.lastName = last;
551 | }
552 | }
553 | let contact = new Contact();
554 | contact.changeName('li', 'lei');
555 | ```
556 |
557 | 采用 @action 修饰符,状态修改方法被包裹在一个事务中,对多个属性的变更变成了一个原子操作,仅在方法结束时,Mobx 才会触发一次对相关依赖的更新通知。与此同时,所有对状态的修改都统一到应用状态的指定标识的方法中,一方面提升了代码可维护性,另一方面,也便于调试工具提供有效的调试信息。
558 |
559 | 需要注意的是 action 只能影响当前函数作用域,函数中如果有异步调用并且在异步请求返回时需要修改应用状态,则需要对异步调用也使用 aciton 包裹。当使用 async/await 语法处理异步请求时,可以使用 runInAction 来包裹你的异步状态修改过程。
560 |
561 | ```
562 | class Contact {
563 | @observable title ;
564 | @action getTitle() {
565 | this.pendingRequestCount++;
566 | fetch(url).then(action(resp => {
567 | this.title = resp.title;
568 | this.pendingRequestCount--;
569 | }))
570 | }
571 |
572 | @action getTitleAsync = async () => {
573 | this.pendingRequestCount++;
574 | const data = await fetchDataFromUrl(url);
575 | runInAction("update state after fetching data", () => {
576 | this.title = data.title;
577 | this.pendingRequestCount--;
578 | })
579 | }
580 | }
581 |
582 | ```
583 | 上述是示例中包含了在 Mobx action 中处理异步请求的过程,这一过程与我们在普通 JavaScript 方法中处理异步请求基本一致,唯一的差别,是对应用状态的更新需要用 action 包裹。
584 |
585 |
586 | #### 3.2.3 响应状态变更
587 |
588 | 在 Mobx action 中更新应用状态时,Mobx 自动的将变更通知到相关依赖的部分,我们仅需关注如何响应变更。Mobx 中有多种响应变更的方法,包括 autorun、reaction、when 等,本节探讨其使用场景。
589 |
590 | ##### 1)autorun
591 | autorun 是Mobx中最常用的观察者,当你需要根据依赖自动的运行一个方法,而不是产生一个新值,可以使用 autorun 来实现这一效果。
592 |
593 | ```
594 | class Contact {
595 | @observable firstName = 'Han';
596 | @observable lastName = "meimei";
597 | constructor() {
598 | autorun(()=>{
599 | console.log(`Name changed: ${this.firstName}, ${this.lastName}`);
600 | });
601 | this.firstName = 'Li';
602 | }
603 | }
604 |
605 | // Name changed: Han, meimei
606 | // Name changed: Li, meimei
607 | ```
608 |
609 | ##### 2) reaction
610 | 从上例输出的日志可以看出,autorun 在定义时会立即执行,之后在依赖的属性变更时,会重新执行。如果我们希望仅在依赖状态变更时,才执行方法,可以使用 reaction。
611 |
612 | reaction 可以如下定义:
613 |
614 | ```
615 | reaction = tracking function + effect function
616 | ```
617 | 其使用方式如下所示:
618 |
619 | ```
620 | reaction(() => data, data => { sideEffect }, options?)
621 | ```
622 |
623 | 函数定义中,第一个参数即为 tracking function,它返回需要被观察的数据。这个数据被传入第二个参数即 effect function,在 effect function 中处理逻辑,产生副作用。
624 |
625 | 在定义 reaction 方法时,effect function 不会立即执行。仅当 tracking function 返回的数据变更时,才会触发 effect function 的执行。通过将 autorun 拆分为 tracking function 和 effect function,我们可以对监听响应进行更细粒度的控制。
626 |
627 | ##### 3) when
628 |
629 | autorun 和reaction 可以视为长期运行的观察者,如果不调用销毁方法,他们会在应用的整个生命周期内有效。如果我们仅需在特定条件下执行一次目标方法,可以使用 when。
630 |
631 | when 的使用方法如下所示:
632 |
633 | ```
634 | when(debugName?, predicate: () => boolean, effect: () => void, scope?)
635 | ```
636 |
637 | 与 reaction 类似,when 主要参数有两个,第一个是 tracking function,返回一个布尔值,仅当布尔值为 true 时,第二个参数即 effect function 会被触发执行。在执行完成后,Mobx 会自动销毁这一观察者,无需手动处理。
638 |
639 |
640 | #### 3.2.4 在应用中使用
641 |
642 | Mobx 是一个独立的应用状态管理工具,可以应用在多种架构的项目中。当我们在 React 项目中使用 Mobx 时,使用 mobx-react 工具库可以帮助我们更便利的使用。
643 |
644 | Mobx 中应用状态分散在各个领域模型中,一个领域模型可视为一个 store。在我们的实际使用中,当应用规模逐渐复杂,会遇到这样的问题:
645 |
646 | 当我们需要在一个 store 中使用或更新其他 store 状态数据时,应当如何处理呢?
647 |
648 | Mobx 并不提供这方面的意见,它允许你自由的组织你的代码结构。但是在面临这一场景时,如果仅是互相引用 store,最终会将应用状态互相耦合,多个 store 被混合成一个整体,代码的可维护性降低。
649 |
650 | 为我们带来这一困扰的原因,是因为 action 处于某一 store 中,而其本身除了处理应用状态修改之外,还承载了业务逻辑如异步请求的处理过程。实际上,这违背了单一职责的设计原则,为解决这一问题,我们将业务逻辑抽离,混合使用了 Redux action creator 的结构。
651 |
652 | ```
653 | import contactStore from '../../stores/contactStore';
654 | import messageStore from '../../stores/messageStore';
655 |
656 | export function syncContactAndMessageFromServer(url) {
657 | const requestType = requestTypes.SYNC_DATA;
658 | if (requestStore.getRequestByType(requestType)) { return; }
659 | requestStore.setRequestInProcess(requestType, true);
660 | return fetch(url)
661 | .then(response => response.json())
662 | .then(data => {
663 | contactStore.setContacts(data.contacts);
664 | messageStore.setMessages(data.messages);
665 | requestStore.setRequestInProcess(requestType, false);
666 | });
667 | }
668 | ```
669 |
670 | 上述示例中,我们将业务逻辑抽离,在这一层自由的引用所需的 Mobx store,并在业务逻辑处理结束,调用各自的 action 方法去修改应用状态。
671 |
672 | 这种解决方案结合了 Redux action creator 思路,引入了单独的业务逻辑层。如果不喜欢这一方式,也可以采用另一种思路来重构我们的 store。
673 |
674 | 在这种方法里,我们将 store 与 action 拆分,在 store 中仅保留属性定义,在 action 中处理业务逻辑和状态更新,其结构如下所示:
675 |
676 | ```
677 | export class ContactStore {
678 | @observable contacts = [];
679 | // ...other properties
680 | }
681 |
682 | export class MessageStore {
683 | @observable messages = observable.map({});
684 | // ...other properties
685 | }
686 |
687 | export MainActions {
688 | constructor(contactStore,messageStore) {
689 | this.contactStore = contactStore,
690 | this.messageStore = messageStore
691 | }
692 | @action syncContactAndMessageFromServer(url) {
693 | ...
694 | }
695 | }
696 | ```
697 |
698 | 两种方法的均能解决问题,可以看出第二种方法对 store 原结构改动较大,在我们的实际开发中,使用第一种方法。
699 |
700 |
701 | ## 4. 选择你的状态管理方案
702 | ### 4.1 Redux、Mobx 关键差异
703 | 结合前文所述,Redux 与 Mobx 的有如下关键差异:
704 |
705 | * Redux 使用单一 store;Mobx 使用多个分散的 store。
706 | * Redux 状态数据采用不可变数据结构,状态修改必须在 reducer 中;Mobx 状态数据可以随处更改,仅在严格模式时强制在 action 中修改。
707 | * Redux 中脚手架代码更多,明确提出操作处理过程中的相关步骤;Mobx 脚手架代码很少,不关注项目代码的组织方式。
708 | * Redux 手动 `dispatch(action)`;Mobx自动触发相关依赖的更新通知。
709 | * Redux 在 `mapStateToProps` 中订阅当前组件关注的应用状态;Mobx 根据当前组件中对应用状态的使用,自动收集依赖关系。
710 | * Redux 中应用状态为普通对象;Mobx 应用状态为可观察对象。
711 |
712 | 实际使用中,两者在对用户事件的处理流程上有明显不同:
713 |
714 | * Mobx 中,一个典型的用户事件处理流程是 component -》 Mobx action -》 component re-render。如果将业务逻辑从 action 抽离,流程为 component -》business logic -》 Mobx action -》 component re-render。在这一过程中,Mobx 根据收集组件中对应用状态的依赖关系,在 action 更新应用状态后,自动触发组件的刷新。
715 | * Redux 中,一个典型的用户事件处理流程是 component -》action creator -》 redux-thunk middleware -》 reducer -》 component re-render。这一过程中,异步处理需要借助中间件完成,业务逻辑与 reducer 间需要手动 `dispatch(action)`,并在 reducer 中完成对应用状态的修改。应用状态变更后,需要在 component 与 store 间声明当前组件关注的应用状态。
716 |
717 | ### 4.2 选择时的几点考虑
718 |
719 | Mobx 为人称道的是使用上的便捷,结合我们的实际体验,建议在引入 Mobx 之前,考虑如下问题:
720 |
721 | * 首先,Mobx 中应用状态为可观察对象,被观察属性使用 Mobx 数据类型定义,这意味着领域模型的定义与 Mobx 耦合。在我们的项目中,领域模型往往与框架无关,可以在不同框架中无缝使用。所以选择 Mobx 需要对这一问题需要有一个考量。
722 | * 其次,Mobx 的简洁之处在于它自动收集观察者对被观察属性的依赖关系,在状态变更时,自动的触发相关依赖的更新。这是一个相对黑盒的过程,正确的使用需要对 Mobx 响应的内容有深入的理解。在使用之初,我们遇到过一些未能正常触发刷新的场景,原因就在于没有完全理解究竟哪些操作可以触发 Mobx 的更新。
723 | * 最后,Mobx 是一个相对较新的工具,处于快速迭代之中。从我们的学习使用体验来看,目前它的文档还不够完善,存在部分内容过时和过于简单的情况。从开发调试的工具链来看,一些常用工具如日志输出已有,开发中可以满足基本需要。但是在遇到更复杂的业务场景,如需要类似 redux-saga 这类中间件工具,对用户事件进行更细粒度操作控制时,缺少便利的工具。
724 |
725 | Redux 被批评较多的是它的代码冗长,不够简洁。引入 Redux 之前,重点需要考虑的问题,是评估引入的必要性,这需要评估项目复杂度。根据项目规模、是否多人维护等因素,考量简洁快速和高可维护性的优先级。
726 |
727 | 社区中对于消除 Redux 的冗余代码也有一些尝试,如使用工具简化 action 的定义,甚至有些更加激进的方案,在一些场景下,可以消除手动 `disptach(action)`和 reducer 的编写。
728 |
729 |
730 | ## 5. 总结
731 | Redux 和 Mobx 是当前比较热门的应用状态管理工具,在项目开发中,我们先后引入 Redux 和 Mobx 分别进行了尝试。本文总结了我们在使用 Redux 和 Mobx 过程中,对几个重要方面的探索过程,最后将这两个工具进行了一些对比,并给出我们在选型时的一些思考。
732 |
733 | 总得来说,在解决异步方案选型、应用状态设计与变更等主要问题后,我们可以在项目中优雅的使用 Redux。通过引入社区的一些工具,也在一定程度上减少了冗余代码,在这样的项目中,我们认为引入 Mobx 进行重构的必要性不大。在新开始的中等规模项目中,我们引入了 Mobx,经过一段时间的学习与探索,在掌握 Mobx 数据结构,理解其响应的内容,抽离业务逻辑与 Mobx action 后,在开发中也开始感受到它的简洁与便利。
734 |
735 | Mobx or Redux,没有一定之规,需要结合你的业务场景和团队经验进行选择,希望本文对此有所帮助。
--------------------------------------------------------------------------------
/src/webpack/HMR.md:
--------------------------------------------------------------------------------
1 | # HMR
2 |
3 | 模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面,主要有以下几种方式:
4 |
5 | - 保留应用程序状态;
6 | - 只更新变更内容;
7 | - 在源代码中 CSS/JS 产生修改时,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。
8 |
9 |
10 |
11 |
12 |
13 | webpack-dev-middleware和webpack-dev-server的区别
14 |
15 | 其实就是因为webpack-dev-server只负责启动服务和前置准备工作,所有文件相关的操作都抽离到webpack-dev-middleware库了,主要是本地文件的编译和输出以及监听,无非就是职责的划分更清晰了。
16 |
17 |
18 | // todo
19 | - [轻松理解webpack热更新原理](https://juejin.cn/post/6844904008432222215)
20 | - [官文](https://www.webpackjs.com/concepts/hot-module-replacement/)
--------------------------------------------------------------------------------
/src/webpack/Tree-sharking.md:
--------------------------------------------------------------------------------
1 | tree shaking,你必须……
2 |
3 | 使用 ES2015 模块语法(即 import 和 export)。
4 | 在项目 package.json 文件中,添加一个 "sideEffects" 入口。
5 | 引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。
6 |
7 | // todo
8 | - [Webpack 中的 sideEffects 到底该怎么用?](https://github.com/kuitos/kuitos.github.io/issues/41)
9 | - [你的Tree-Shaking并没什么卵用](https://zhuanlan.zhihu.com/p/32831172)
10 |
--------------------------------------------------------------------------------
/src/webpack/Webpack-DllPlugin.md:
--------------------------------------------------------------------------------
1 | # Webpack DllPlugin
2 |
3 | dll(Dynamic-link library 动态链接库) 其实就是缓存.
4 |
5 | 具体到 webpack 这块儿,就是事先把常用但又构建时间长的代码提前打包好(例如 react、react-dom),取个名字叫 dll。后面再打包的时候就跳过原来的未打包代码,直接用 dll。这样一来,构建时间就会缩短,提高 webpack 打包速度。
6 |
7 | 干的活就是空间换时间。
8 |
9 | ## Reference
10 |
11 | - [动态链接库(DLL)](https://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E5%BA%93)
12 | - [你真的需要 Webpack DllPlugin 吗?](https://blog.csdn.net/xiaoyaGrace/article/details/106328441)
13 |
--------------------------------------------------------------------------------
/src/webpack/Webpack打包分块.md:
--------------------------------------------------------------------------------
1 | # 代码分离
2 |
3 |
4 |
5 | 代码分离能够把代码分离到不同的 bundle 中,之后可以按需加载或并行加载这些文件。
6 |
7 | 有三种常用的代码分离方法:
8 |
9 | - 入口起点:使用 entry 配置手动地分离代码;
10 | - 防止重复:使用 CommonsChunkPlugin 去重和分离 chunk;
11 | - 动态导入:通过模块的内联函数调用来分离代码。
12 |
13 | 打包分离背后的思想非常简单。如果你有一个体积巨大的文件,并且只改了一行代码,用户仍然需要重新下载整个文件。但是如果你把它分为了两个文件,那么用户只需要下载那个被修改的文件,而浏览器则可以从缓存中加载另一个文件。
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/webpack/happypack.md:
--------------------------------------------------------------------------------
1 | - [happy pack 原理解析](https://segmentfault.com/a/1190000021037299)
2 | - [happypack 原理解析](https://juejin.cn/post/6844903456436649998)
3 | - [使用happypack](https://webpack.wuhaolin.cn/4%E4%BC%98%E5%8C%96/4-3%E4%BD%BF%E7%94%A8HappyPack.html)
4 |
5 | HappyPack 原理
6 | 在整个 Webpack 构建流程中,最耗时的流程可能就是 Loader 对文件的转换操作了,因为要转换的文件数据巨多,而且这些转换操作都只能一个个挨着处理。 HappyPack 的核心原理就是把这部分任务分解到多个进程去并行处理,从而减少了总的构建时间。
7 |
8 | 从前面的使用中可以看出所有需要通过 Loader 处理的文件都先交给了 happypack/loader 去处理,收集到了这些文件的处理权后 HappyPack 就好统一分配了。
9 |
10 | 每通过 new HappyPack() 实例化一个 HappyPack 其实就是告诉 HappyPack 核心调度器如何通过一系列 Loader 去转换一类文件,并且可以指定如何给这类转换操作分配子进程。
11 |
12 | 核心调度器的逻辑代码在主进程中,也就是运行着 Webpack 的进程中,核心调度器会把一个个任务分配给当前空闲的子进程,子进程处理完毕后把结果发送给核心调度器,它们之间的数据交换是通过进程间通信 API 实现的。
13 |
14 | 核心调度器收到来自子进程处理完毕的结果后会通知 Webpack 该文件处理完毕。
--------------------------------------------------------------------------------
/src/webpack/how-to-write-a-plugin.md:
--------------------------------------------------------------------------------
1 | # how-to-write-a-plugin
2 |
3 | 直接上 webpack docs 的 [how-to-write-a-plugin](https://github.com/webpack/docs/wiki/how-to-write-a-plugin)
4 |
5 | > webapck 事件流的运行就是基于 webpack 自己写的基础类Tapable,Webpack 的事件流机制应用了发布订阅模式,和 Node.js 中的 EventEmitter非常相似。
6 |
--------------------------------------------------------------------------------
/src/webpack/index.md:
--------------------------------------------------------------------------------
1 | # 基本概念
2 |
3 | loader 用于转换某些类型的模块,如用于对模块的源代码进行转换。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL,loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!
4 |
5 | Plugin插件则可以用于执行范围更广的任务,目的在于解决 loader 无法实现的其他事,包括:打包优化,资源管理,注入环境变量。
6 |
7 | webpack 插件是一个具有 apply 方法的 JavaScript 对象。apply 方法会被 webpack compiler 调用,并且在整个编译生命周期都可以访问 compiler 对象。
8 |
9 | runtime 和 manifest
10 |
11 | runtime 在浏览器运行过程中,在模块交互时,连接模块所需的加载和解析逻辑。
12 |
13 | manifest 数据用途即管理 webpack 所有所需模块之间的交互。当 compiler 开始执行、解析和映射应用程序时,它会保留所有模块的详细要点,这个数据集合称为 "manifest"。
14 |
15 | 当完成打包并发送到浏览器时,runtime 会通过 manifest 来解析和加载模块。
16 |
17 | 而每个模块间的依赖关系,则依赖于AST语法树。每个模块文件在通过Loader解析完成之后,会通过[acorn库](https://github.com/acornjs/acorn)生成模块代码的AST语法树,通过语法树就可以分析这个模块是否还有依赖的模块,进而继续循环执行下一个模块的编译解析。
18 |
19 | 如 import 或 require 语句现在都已经转换为 __webpack_require__ 方法,此方法指向模块标识符(module identifier)。通过使用 manifest 中的数据,runtime 将能够检索这些标识符,找出每个标识符背后对应的模块。
20 |
21 | 在webpack源码中主要依赖于 compiler 和 compilation 两个核心对象实现。
22 |
23 | - compiler 对象是一个全局单例,包含了完整的 webpack 配置,他负责把控整个 webpack 打包的构建流程,即负责文件监听和编译。
24 | - compilation 对象是每一次构建的上下文对象,它包含了当次构建所需要的所有信息,每次热更新和重新构建,compiler 都会重新生成一个新的 compilation 对象,负责此次更新的构建过程。
25 |
--------------------------------------------------------------------------------
/src/webpack/webpack-5.md:
--------------------------------------------------------------------------------
1 | 参考:https://webpack.docschina.org/blog/2020-10-10-webpack-5-release/
2 |
3 | 升级实验:https://zhuanlan.zhihu.com/p/81122986
--------------------------------------------------------------------------------
/src/webpack/其它.md:
--------------------------------------------------------------------------------
1 | 目前Webpack与Rollup都支持Scope Hoisting。它们可以检查import链,并尽可能的将散乱的模块放到一个函数中,前提是不能造成代码冗余。所以只有被引用了一次的模块才会被合并。使用Scope Hoisting可以让代码体积更小并且可以降低代码在运行时的内存开销,同时它的运行速度更快。前面2.1节介绍了变量从局部作用域到全局作用域的搜索过程越长执行速度越慢,Scope Hoisting可以减少搜索时间
--------------------------------------------------------------------------------