├── 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 | ![test-1](../../../images/hooks/useRef-1.gif) 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 | ![test-2](../../../images/hooks/useRef-2.gif) 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 | ![old-architecture](./../../../images/rn/old-architecture.jpg) 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 | ![new-architecture](./../../../images/rn/new-architecture.jpg) 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 | ![bridge](./../../../../blog/images/rn/bridge.jpg) 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 | ![react_17_delegation](./../../../images/react/react-17-rc.jpg) 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 | 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可以减少搜索时间 --------------------------------------------------------------------------------