[
  {
    "path": "HybirdAPI/AudioPlayer.md",
    "content": "# AudioPlayer\n\n# 概述\n\n音频播放对象，播放本地音频。\n\n注：网络音频请使用h5的audio标签。\n\n# 常量\n\n### TYPE_SPEAKER\n### TYPE_EARPIECE\n\n# 方法\n\n### play\n### pause\n### stop\n### setCurrentTime(long ms)\n### (long)getDuration\n### (long)getCurrentTime\n### setType(int AudioPlayer.TYPE_*)\n\n# 事件\n\n### onPlay\n### onPause\n### onFail\n### onEnded\n### onTimeupdate\n\n\n\n"
  },
  {
    "path": "HybirdAPI/AudioRecorder.md",
    "content": "# AudioRecorder\n\n# 概述\n\n调用麦克风进行录音操作\n\n# 方法\n\n### record\n\nfilePath\nsamplerate\nformat\n\n### stop\n### getFilePath\n### clear\n\n# 事件\n\n### onRecord\n### onFail\n### onEnded\n### onTimeupdate\n\n\n\n\n"
  },
  {
    "path": "HybirdAPI/index.md",
    "content": "# HybirdAPI\n\n# 概述\n\n此系列文档是为了描述app中如何提供一套统一的原生接口，供js调用。\n\napp中webview中的js可以调用。\n\napp中reactnative模块可以调用。\n\n"
  },
  {
    "path": "README.md",
    "content": "# 文章目录\n\n大家有问题欢迎留言issues讨论。\n\n### [【翻译】理解ios11中的webview viewport（第一部分）](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/Understanding%20the%20WebView%20Viewport%20in%20iOS%2011%20~%201.md)\n\n### [【翻译】让网站适配iphoneX](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/Designing%20Websites%20for%20iPhone%20X.md)\n\n### [【原创】富文本编辑器4~站外图片的粘贴](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A84~%E7%AB%99%E5%A4%96%E5%9B%BE%E7%89%87%E7%9A%84%E7%B2%98%E8%B4%B4.md)\n\n### [【原创】富文本编辑器3~自定义元素](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A83~%E8%87%AA%E5%AE%9A%E4%B9%89%E5%85%83%E7%B4%A0.md)\n\n### [【原创】富文本编辑器2~复制粘贴的处理](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A82~%E5%A4%8D%E5%88%B6%E7%B2%98%E8%B4%B4%E7%9A%84%E5%A4%84%E7%90%86.md)\n\n### [【原创】富文本编辑器1~html转结构化数据](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A81~html%E8%BD%AC%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE.md)\n\n### [【翻译】ReactNative:关于苹果审核警告](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/ReactNative%20:%20A%20warning%20from%20Apple.md)\n\n### [【翻译】Reconciliation React比对算法](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/Reconciliation.md)\n\n### [【原创】数据结构--树形数据的处理(2)](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84--%E6%A0%91%E5%BD%A2%E6%95%B0%E6%8D%AE%E7%9A%84%E5%A4%84%E7%90%862.md)\n\n### [【原创】数据结构--树形数据的处理(1)](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84--%E6%A0%91%E5%9E%8B%E6%95%B0%E6%8D%AE%E7%9A%84%E5%A4%84%E7%90%86(1).md)\n\n### [【翻译】React Advanced Performance 性能进阶](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/React%20Advanced%20Performance.md)\n\n### [【原创】弱特征广告方案](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%BC%B1%E7%89%B9%E5%BE%81%E5%B9%BF%E5%91%8A%E6%96%B9%E6%A1%88.md)\n\n### [【原创】前端和开闭原则](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%89%8D%E7%AB%AF%E5%92%8C%E5%BC%80%E9%97%AD%E5%8E%9F%E5%88%99.md)\n\n### [【原创】React的render优化通用解决方案](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/React%E7%9A%84render%E4%BC%98%E5%8C%96%E6%A1%86%E6%9E%B6.md)\n\n### [【原创】一个类似微信群聊程序的技术方案设计过程](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E4%B8%80%E4%B8%AA%E7%B1%BB%E4%BC%BC%E5%BE%AE%E4%BF%A1%E7%BE%A4%E8%81%8A%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88%E8%AE%BE%E8%AE%A1%E8%BF%87%E7%A8%8B.md)\n\n### [【原创】ReactNative的ios崩溃整理分析](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%9A%84ios%E5%B4%A9%E6%BA%83%E6%95%B4%E7%90%86%E5%88%86%E6%9E%90.md)\n\n### [【原创】ReactNative团队分享提纲](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%88%86%E4%BA%AB.md)\n\n### [【翻译】React PureRenderMixin](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/React%20PureRenderMixin.md)\n\n### [【原创】ReactNative增量升级解决方案](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%A2%9E%E9%87%8F%E5%8D%87%E7%BA%A7%E6%96%B9%E6%A1%88.md)\n\n### [【翻译】React 浅比对 Shallow Compare](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/React%20Shallow%20Compare.md)\n\n### [【原创】ReactNative安卓首屏白屏优化](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%AE%89%E5%8D%93%E9%A6%96%E5%B1%8F%E7%99%BD%E5%B1%8F%E4%BC%98%E5%8C%96.md)\n\n### [【原创】ReactNative导航设计与实现](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%AF%BC%E8%88%AA%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0.md)\n\n### [【原创】ReactNative打离线包-android篇](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E6%89%93%E7%A6%BB%E7%BA%BF%E5%8C%85-android%E7%AF%87.md)\n\n### [【原创】ReactNative打离线包-ios篇](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E6%89%93%E7%A6%BB%E7%BA%BF%E5%8C%85-ios%E7%AF%87.md)\n\n### [【原创】ReactNative的组件架构设计](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%9A%84%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.md)\n\n### [【原创】ReactNative组件状态设计思考](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%BB%84%E4%BB%B6%E7%8A%B6%E6%80%81%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%80%83.md)\n\n### [【翻译】ReactNative的定时器Timers](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/react-native%E7%9A%84%E5%AE%9A%E6%97%B6%E5%99%A8.md)\n\n### [【原创】已有工程集成ReactNative-IOS](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/%E5%B7%B2%E6%9C%89%E5%B7%A5%E7%A8%8B%E9%9B%86%E6%88%90ReactNaitve-IOS.md)\n\n### [【翻译】ReactNative的性能](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/react-native%E7%9A%84%E6%80%A7%E8%83%BD.md)\n\n### [【原创】ReactNative的组件架构设计](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%9A%84%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.md)\n\n### [【转载】ReactNative变革无线前端-淘宝d2分享](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/%E6%B7%98%E5%AE%9Dd2%E5%88%86%E4%BA%AB-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF.md)\n\n### [【原创】CodePush](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/CodePush.md)\n\n### [【原创】ReactNative开发技巧总结](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%BC%80%E5%8F%91%E6%8A%80%E5%B7%A7%E6%80%BB%E7%BB%93.md)\n\n### [【原创】ReactNative入门知识](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%85%A5%E9%97%A8%E7%9F%A5%E8%AF%86.md)\n\n\n\n\n\n"
  },
  {
    "path": "ReactNative开发指导/CodePush.md",
    "content": "#CodePush\n\nhttps://github.com/Microsoft/react-native-code-push\n\n#重要说明\n\n<http://microsoft.github.io/code-push/faq/index.html#3-how-should-i-interpret-the-beta-status-of-the-service>\n\nWhile the service is in beta, we don’t recommend using it for doing production deployments, since we don’t currently offer any specific SLA.因为现在是beta版本，不建议用做生产版本的发布。\n\n确实不能用于生产,目前只在美国有服务器，大陆访问非常慢。<https://github.com/Microsoft/code-push/issues/70>\n\n所以，增量升级只能自力更生了。\n\n#工作方式\n可以只升级Rn的js和图片，不用使用二进制包升级。\n\n通过CodePush服务器进行升级。\n\n服务端目前没有开源，不能用自己的服务器\n\n原生代码的改动是不能通过codepush升级的。\n\n# 支持平台\n\nAndroid (asset support coming soon!)\n\n安卓暂不支持asset\n\n#基本流程\n1. 安装CodePush CLI\n2. 创建codePush账户\n3. 注册app到codepush服务器\n4. app中加入codepush客户端代码，并开发升级相关代码。 \n5. 发布更新到codepush服务器\n6. app收到了升级推送\n\n\n具体配置过程请参考官网<http://microsoft.github.io/code-push/>\n\n网上资料参考<http://blog.csdn.net/oiken/article/details/50279871>\n\n\n#下一步工作\n\n因为codepush服务器端不开源，而且服务器还在国外的关系，不能保证服务端的稳定性，所以很遗憾不能应用于真正的项目中。\n\n针对目前的情况，制定下一步工作如下：\n\n1. 研读codepush客户端源码，理解android和ios在客户端下载资源文件后，如何切换到新资源的方法。\n2. 设计服务端，服务端仅提供文件资源下载服务和升级信息描述服务即可，不需要较复杂的逻辑。\n3. 设计自动化打包解决方案，包括升级补丁打包，完整app打包。\n\n\n\n\n#源码阅读记录\n1. js版本同时需要考虑对app版本的依赖问题，例如，js1.0版本必须运行在iosApp2.0，安卓app2.6上等。\n2. 提供了一系列js接口，方便应用更细节的控制升级的过程。\n3. 对于assets中的图片内容，可以自动计算与上一版本的差别，只下载增量部分。\n4. 如果app升级了，js应自动使用app中版本。todo一个升级算法状态图。\n4. 客户端应有2部分组件-codepush文档很详细，可以参考\n\t1. native部分，安卓和ios分别提供启动时bundle资源位置的服务\n\t2. js部分，提供对更新和下载的细粒度控制接口.\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative入门知识.md",
    "content": "# ReactNative入门知识\n\n本文也可以作为RN的入门知识范围，为入门RN提供了一个知识大纲。掌握这些知识帮助你快速入门RN，少走弯路。\n\n#es6\n\nlet和const\n\n箭头函数\n\n类定义\n\n解构赋值Destructuring\n\n函数参数的默认值\n\n函数的rest参数【形式为“...变量名”】；及其逆运算；\n\nimport export\n\n参考地址：http://facebook.github.io/react-native/docs/javascript-environment.html#content\n\n参考地址：http://www.ghugo.com/react-native-es6/\n\n#Flow\n\n\n#react\njsx\n\n生命周期事件\n\nrender方法\n\nstate相关\n\nprops相关\n\n\n#react-naive常用组件\nview\n\ntext\n\ntouch\n\nimage\n\ntextInput\n\nscrollview\n\nlistview\n\nnavigator\n\n\n#flexbox\n\n\n#Dimensions\n\n#fetch\n\n#console\n断点\n\n#定时任务\n参考地址：https://github.com/cnsnake11/blog/blob/master/ReactNative翻译/react-native的定时器.md\n\n#交互管理器\n参考地址：https://github.com/cnsnake11/blog/blob/master/ReactNative翻译/react-native的定时器.md\n\n#调试\nconsole.log()\n\n在chrome中调试js\n\n#Immutable.js\n\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative分享.md",
    "content": "\n# ReactNative分享提纲\n\n# 分享的目的\n\n1. 了解rn是什么，特点和使用场景\n2. 本次重点是学习架构（思想）而不是学习框架（用法）\n3. 学习组件化的思想及其好处，目标是能运用到工作中\n4. 希望随时打断进行交流讨论\n\n# reactnative是个啥\n\n用js写原生app的开源框架，目前官方支持ios和android。\n\nfacebook2015年发布。社区关注度非常高。版本迭代很快，基本上2周左右一个版本。\n\n# 安卓demo包下载\n\n[RnDemo.apk,9MB](http://pan.baidu.com/s/1mhX6VoO)\n\n\n# 优势\n\n## 开发体验\n\t\n1. 改代码，即时编译，即时加载\n1. 兼容性好，代码可预测，各系统的模拟器、各型号的真机基本无差异\n1. 组件化支撑，代码更易复用\n1. flexbox布局，自适应布局更容易\n1. 手势操控系统，更易开发体验佳的触屏应用\n1. 支持应用内更新，自由控制发版\n1. es6新特性，各种语法黑魔法\n\t1. 箭头函数\n\t1. promise\n\t1. 解构赋值\n\t1. 类定义、继承\n\t1. fetch\n\t2. 等\n\n## 用户体验\n1. 加载性能好，无白屏\n2. 动画性能好，无卡顿\n2. 顺畅的手势操作\n\n# 劣势\n1. 无web版，分享页等还要开发,react-web目前还不成熟\n1. 无选择器的概念，定义样式很繁琐,通过开发规范部分解决\n1. 尚未发布1.0\n\n程序员首先要思考的是让用户爽，然后才是让自己爽。如果按此原则的话，劣势还是在我们接受范围内的。（用户体验和开发体验同样重要，最好是有更完美的解决方案。）\n\n# react基本概念\n\n## 组件化编程\n\n### 组件化定义\n组件的定义：独立的可完成某些工作的部件。可能是一个button，或者一个util类。\n\n组件化编程的目的：复用复用复用。站在别人肩膀上可以极大的提升开发测试效率。\n\n遇到的困难：个性化定制。通过属性、事件等扩展。\n\n举个web组件的例子，参考此文档的前半部分：[链接](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%BB%84%E4%BB%B6%E7%8A%B6%E6%80%81%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%80%83.md)\n\n### react做了什么\n\nreact是一个组件化编程的框架，提供了组件化编程的完整解决方案。[例子](http://facebook.github.io/react/)\n\nreact组件的定义。 [链接](http://facebook.github.io/react/docs/component-specs.html)\n\nreact组件生命周期及其生命周期事件。[链接](http://facebook.github.io/react/docs/component-specs.html#lifecycle-methods)\n\n## 面向状态编程\n\n页面中任何组件的state的改变，都会引起页面所有组件的render方法的执行。react会根据render执行的结果与当前的虚拟dom进行比对，只进行增量的更新。[链接](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%BB%84%E4%BB%B6%E7%8A%B6%E6%80%81%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%80%83.md#rn设计思路)\n\n## jsx\njs中的xml。[例子](http://facebook.github.io/react/)\n\n# reactnative基本概念\n\n## 和react的关系\n\n1. rn使用react的组件化规范。\n2. rn使用react的面向状态编程的机制。\n3. rn使用react的jsx机制。\n4. rn提供了一系列app开发所需要的组件。\n\n## 常用组件\n1. div View\n2. span Text\n3. img Image\n4. input TextInput\n5. button Touch*\n\n## 样式定义\n不支持选择器。不支持选择器。不支持选择器。\n\n只能内联或者在每一个节点上声明外部css定义。所以，一般没有复用的样式使用内联，有复用的才使用外部定义。\n\n[链接](http://reactnative.cn/docs/0.24/style.html#content)\n\n\n# 开发环境\n\n## ios\n\nxcode+app的ios代码+js编辑器+rn开发服务器+真机or模拟器\n\n## android\n\njdk+androidSdk+androidStudio+app的android代码+js编辑器+rn开发服务器+真机or模拟器\n\n# 开发过程\n\n1. 启动rn开发服务器\n2. 启动模拟器，点开app，设置自动reload\n3. 启动js代码编辑器，coding---save---看效果\n4. tips:可以直接让首页显示你要开发的页面，可以快速查看效果\n\n```\n// tips用到的代码，替换index页面的最后一行\nAppRegistry.registerComponent('Ask', () => require('../../../demo/demo1/animate/drag2'));\n```\n\n# 调试过程\n\n1. console.log\n2. js断点调试\n3. alert\n3. 页面元素检测器\n\n# 组件设计\n[链接](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%9A%84%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.md)\n\n\n# 坑坑坑\n\n1. android的textinput不能设置边框圆角\n1. android的图片resizemodal=contain不好用\n1. 循环依赖报错的问题\n1. 安卓rn首屏白屏时间长\n1. 安卓的中绝对定位元素移动出父元素会不可见，设置overflow=hidden也不好使\n1. 不支持隐藏元素\n1. 安卓中，使用缓存rootview，并且键盘打开情况下，直接后退出rn模块，在进入会报错\n1. 魅族smartbar不占高\n1. android中，设置overflow=hidden，父容器高度不能为0，至少为0.01，否则hidden无效\n1. 加固后动画有掉帧现象\n\n\n# 架构设计\n```\n业务模块\n业务框架\n技术框架\nreactnative\nandroid、ios操作系统\n```\n\n# 框架设计\n\n参考技术框架和业务框架的目录结构介绍。\n\n# 版本发布\n\n## 全量发布\n\n全量发布是将rn相关资源打包进app中，是无需网络下载的。\n\n一般过程：\n\n1. coding结束\n2. 使用全量打包命令，打出android和ios全量包\n3. 将包拷贝到android和ios工程约定好的位置\n4. 按照android和ios原有打包方式进行打包即可\n\n## 增量发布\n\n[增量升级方案](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%A2%9E%E9%87%8F%E5%8D%87%E7%BA%A7%E6%96%B9%E6%A1%88.md)\n\n一般过程：\n\n1. coding结束\n2. 使用全量打包命令，打出android和ios全量包\n3. 使用增量打包命令，打出android和ios增量包\n4. 将增量包上传到后台管理系统，上线即可\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative增量升级方案.md",
    "content": "# ReactNative增量升级方案\n\n# 前言\nfacebook的react-native给我们带来了用js写出原生应用的同时，也使得使用RN编写的代码的在线升级变得可能，终于可以不通过应用市场来进行升级，极大的提升了app修bug和赋予新功能的能力。----使用h5的方式也可以做到，但是rn的用户体验可要远远超过h5啊。\n\n一般使用RN编写的app的线上使用方式，是将react-native bundle命令打出bundle文件和assets文件夹，直接内置到app中，app在viewcontroller或者activity中直接加载app内部的bundle文件，比如下图。\n\n![](media/14534499414604.jpg)\n\n\n当修改了代码或者图片的时候，只要app使用新的bundle文件和assets文件夹，就完成了一次在线升级。\n\n本文主要基于以上思路，讲解增量升级的解决方案。\n\n# 何为增量？\n\n 一个完整的RN-app程序通常包含以下几个部分：\n \n 1. native代码部分-objc或者java\n 2. js代码部分-rn代码、依赖的第三方库、业务代码等\n 3. 图片资源部分\n\nnative代码别想了，没法在线升级，要是能大家就都不使用应用市场ota升级了。\n \n能进行在线升级的是js代码部分和图片资源部分，具体到代码就是bundle文件和assets文件夹。\n\n因为在线升级是要走网络的，我们要想办法将网络消耗降到最低，所以要使用增量升级的方式。\n\n针对js代码部分（即bundle文件）的增量指的是，代码的改动有多少，增量patch的补丁就有多少，那些没有改动的代码部分是不在补丁的范围内的。\n\n针对图片部分（即assets）的增量指的是，升级补丁包中只包含新增的图片和有改动的图片。\n\n那么在app端，下载升级补丁包，只需要和现有的版本进行合并，就能计算出最新版本的全量包。\n\n总结下流程：()中为例子\n\n首先，计算增量包：新版本(v10) - 旧版本(v1到v9) = 增量包 （会有9个包，v1~v10.zip,v2~v10.zip,,,,,v9-v10.zip）\n\n然后，app根据自己的当前版本(比如V6)，下载对应的增量包（V6-V10.zip）。\n\n最后，app中通过 旧版本(v6) + 增量包(v6~v10.zip) = 新版本(v10) ，计算出了新版本的全量包。\n\n \n# 增量算法\n \n assets增量算法，比较简单，就是比对，可以很容易的比较出新增的文件，和不同的文件（使用md5）。\n \n bundle文件的增量算法，确实比较复杂，刚开始没有什么头绪，后来在leader的指引下，很幸运的找到了google写的一个开源的库，可以对大字符串进行diff和patch，并且支持java、objc、js等等语言，完全的满足了我们的需求。\n\n只用到2个接口，具体请参考github上的文档\n\n1. 生成增量包时候：patch_make(text1, text2) => patches \n2. app生成全量包时候：patch_apply(patches, text1) => [text2, results]\n\ngoogle开源库地址：https://github.com/bystep15/google-diff-match-patch\n\n\n# codepush\n\n微软的codepush也做了类似的事情，不过由于以下原因，我们团队没敢使用其作为解决方案。\n\n1. 其增量升级仅仅是针对图片资源的 \n2. 其升级服务器端程序并不开源\n3. 其升级服务器在美国，国内访问很慢且不稳定\n\n不过，codepush客户端的源码和文档也给我们提供了很多思路，在此感谢codepush团队。\n\ncodepush地址：http://microsoft.github.io/code-push/\n\n# bundle要求的app最小版本\n\n本文中一般用min-v或者appMinV表示。\n\n因为js代码是依赖于native代码的，所以，jsbundle对app的版本有要求，所以有这个概念。\n\n试想，如果bundle依赖了一个native的一个新的接口，这个接口在v3版本的app中才发布，如果v2版本的app升级了这个bundle，那么必然会报错，严重的可能会导致app的崩溃。\n\n\n# 系统结构设计与各模块职责\n![312313123](media/312313123.png)\n\n# bundle仓库设计\n\n存储全量bundle和生成增量patch\n\n\n## node patch 命令\n\n在bundle目录下放入一个符合要求【参考目录结构说明】的新版本目录，比如0.3.0,然后执行以下命令。\n\n命令：node patch 版本号 ， 示例：node patch 0.3.0\n\n-d参数: node patch 版本号 -d ，如果加入-d参数，会先删除patch目录下的对应版本目录，然后进行patch生成\n\n然后在patch目录中就会生成0.3.0的增量包，同时patch目录下的update.json文件也会重新生成.\n\n## node update.json 命令\n\n在patch目录下重新生成update.json文件\n\n## 目录结构说明\n![](media/14534529649291.jpg)\n\n```\nbundle   存放全量bundle和全量assets的目录,里面的文件基本上是使用react-native bundle命令生成的\n    0.1.0\n        略\n    0.2.0\n        android\n            略，同ios\n        ios\n            config.json    此版本的配置信息，包含要求app的最低版本等，手动配置\n            index.jsbundle    全量jsbundle文件，使用react-native bundle命令生成\n            assets    全量图片目录，使用react-native bundle命令生成\n    0.3.0\n        略\n        \npatch   存放增量补丁的目录，里面文件都是命令生成的，无需手动维护    \n    0.1.0\n        第一版本无文件\n    0.2.0\n        android\n                略，同ios\n        ios\n            0.1.0-0.2.0.zip    增量包.zip\n    0.3.0\n        android\n                略，同ios\n        ios\n            0.1.0-0.3.0.zip    增量包.zip\n            0.2.0-0.3.0.zip    增量包.zip\n    update.json    所有的升级包信息\nsrc    存放打包用的源码\nlib    存放打包用依赖的第三方的源码\npatch.js    patch命令入口\nupdate.json.js    update.json命令入口\n```\n## config.json示例\n\n```\n{\n  \"v\": \"0.3.0\", //版本\n  \"min-v\": \"4.0.0\", //此版本要求的最小app版本\n  \"date\": \"2016-01-01\", //打包日期\n  \"des\": [\n    \"修复xxbug\", \"添加xx功能\"\n  ]\n}\n\n```\n\n\n## update.json示例\n\n```\n[\n  {\n    \"v\": \"0.1.0\",\n    \"min-v\": \"4.0.0\",\n    \"date\": \"2016-01-01\",\n    \"des\": [\n      \"修复xxbug\",\n      \"添加xx功能\"\n    ],\n    \"iosBundleMd5\": \"11f82563f8fd3f22dccb80ad2297f7bc\",\n    \"androidBundleMd5\": \"11f82563f8fd3f22dccb80ad2297f7bc\"\n  },\n  {\n    \"v\": \"0.2.0\",\n    \"min-v\": \"4.0.0\",\n    \"date\": \"2016-01-01\",\n    \"des\": [\n      \"修复xxbug\",\n      \"添加xx功能\"\n    ],\n    \"iosBundleMd5\": \"3ca2824b008132cee515c0ea29938ff2\",\n    \"androidBundleMd5\": \"3ca2824b008132cee515c0ea29938ff2\"\n  },\n  {\n    \"v\": \"0.3.0\",\n    \"min-v\": \"4.0.0\",\n    \"date\": \"2016-01-01\",\n    \"des\": [\n      \"修复xxbug\",\n      \"添加xx功能\"\n    ],\n    \"iosBundleMd5\": \"dbb81d2383112abb50eb19970c486acd\",\n    \"androidBundleMd5\": \"dbb81d2383112abb50eb19970c486acd\"\n  }\n]\n\n```\n\n# 升级服务器设计\n\n## 接口patch/query\n\n例如：http://localhost:3000/patch/query?bundleV=0.2.0&appV=4.0.0&platform=ios\n\n此接口会有以下4个场景的使用情况，每个场景返回的json示例已经提供在后面文档中。\n\n### 输入参数\n\n    ```\n    bundleV : app中的bundle版本\n    appV : app版本\n    platform : app的平台\n\n    ```\n\n### 返回json各项说明\n\n\n 1. status : 本次请求后台是否发生了错误\n 1. msg : 给用户看的中文提示信息，静默升级时候没什么用\n 1. latestBundleV : 当前的最新bundle版本\n 1. latestAppMinV : 最新bundle要求的app最低版本\n 1. canUpdate : 能否升级，boolean\n 1. canUpdateBundleV : 能升级的bundle版本\n 1. canUpdateAppMinV : 能升级的bundle要求的app最低版本\n 1. patchUrl : 补丁包相对地址\n 1. platform : 平台：ios or android\n\n\n\n### 场景1\n\n能升级，且能升级到最新版\n\n```\n{\n   status: 'success',\n   msg: '可以升级，bundle最新版为0.3.0',\n   latestBundleV: '0.3.0',\n   latestAppMinV: '4.0.0',\n   canUpdate: true,\n   canUpdateBundleV: '0.3.0',\n   canUpdateAppMinV: '4.0.0',\n   patchUrl: 'patch/0.3.0/ios/0.2.0-0.3.0.zip',\n   platform: 'ios'\n}\n```\n\n### 场景2\n无需升级,已经是最新版本\n\n```\n{\n   status: 'success',\n   msg: '无需升级，已经是最新版本',\n   canUpdate: false,\n   platform: 'ios'\n}\n```\n\n### 场景3\n能升级,能升级到当前appMinV的最新bundle版本,但不是最新的bundle,想升最新bundle，必须先升app\n\n```\n{\n   status: 'success',\n   msg: '可以升级，但app版本3.0.0太低，只能升到bundleV0.2.0，bundleV最新为0.3.0',\n   latestBundleV: '0.3.0',\n   latestAppMinV: '4.0.0',\n   canUpdate: true,\n   canUpdateBundleV: '0.2.0',\n   canUpdateAppMinV: '3.0.0',\n   patchUrl: 'patch/0.2.0/ios/0.1.0-0.2.0.zip',\n   platform: 'ios'\n}\n```\n\n### 场景4\n不能升级,已经是当前appMinV的最新bundle,但不是最新的bundle,想升最新bundle，必须先升app\n\n```\n{\n   status: 'success',\n   msg: '不能升级，当前已经是app3.0.0的最新bundle了，但不是最新bundle0.3.0，想升级bundle到最新，请先升级app',\n   latestBundleV: '0.3.0',\n   latestAppMinV: '4.0.0',\n   canUpdate: false,\n   platform: 'ios'\n}\n```\n\n# native客户端设计\n\n客户端的主要工作就是发请求到升级服务器询问是否能升级，然后根据返回的信息，下载升级包，解压升级包，安装升级包。\n\n过程中要保证下载的文件是正确的（md5校验），要保证补丁安装之后的全量bundle文件是正确的（md5校验）。\n\n整个过程用户无感知。\n\n此部分详细设计后边会补充。\n\n# 遗留问题\n目前rn将安卓的图片放入到res目录中，导致安卓图片不能使用在线升级的解决方案，但是codepush的作者已经重构了此部分内容，并提pr到rn，rn团队也接受了这个pr，会在近期的版本中发布。\n\n参考地址：https://github.com/facebook/react-native/pull/4527\n\n# 近期开源\n本部分内容核心的几个部分都已经完成，近期完善之后，会开源出来。\n\n\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative安卓首屏白屏优化.md",
    "content": "# ReactNative安卓首屏白屏优化\n\n#问题描述\n公司现有app中部分模块使用reactnative开发，在实施的过程中，rn良好的兼容性，极佳的加载、动画性能，提升了我们的开发、测试效率，提升了用户体验。\n\n但是，在android中，当点击某个rn模块的入口按钮，弹出rn的activity到rn的页面展现出来的过程中，会有很明显的白屏现象，不同的机型不同（cpu好的白屏时间短），大概1s到2s的时间。\n\n注意，只有在真机上才会有此现象，在模拟器上没有此现象完全是秒开。ios上也是秒开，测试的最低版本是ios7，iphone4s。\n\nreactnative版本0.20.0。\n\njsbundle文件大小717kb。\n\n#优化效果\n经过了大量的源码阅读，和网上资料查找，最终完美的解决了这个问题，无论什么机型，都可以达到秒开，如图（虽然下图是模拟器的截图，但是真机效果基本一样）：\n\n![1](media/1.gif)\n\n#优化过程\n\n##时间分布\n一般优化速度问题，首先就是要找到时间分布，然后根据二八原则，先优化耗时最长的部分。\n\nandroid集成rn都会继承官方提供的ReactActivity\n\n```\npublic class MainActivity extends ReactActivity {\n```\n然后只在自己的activity中覆盖一些配置项。\n\n在官方的ReactActivity中的onCreate方法中\n\n```\nprotected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n\n    if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {\n      // Get permission to show redbox in dev builds.\n      if (!Settings.canDrawOverlays(this)) {\n        Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);\n        startActivity(serviceIntent);\n        FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);\n        Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();\n      }\n    }\n\n    mReactInstanceManager = createReactInstanceManager();\n    ReactRootView mReactRootView = createRootView();\n    mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());\n    setContentView(mReactRootView);\n  }\n```\n最慢的就是这两行代码，占了90%以上的时间。\n\n```\nReactRootView mReactRootView = createRootView();\nmReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());\n```\n\n这两行代码就是把jsbundle文件读入到内存中，并进行执行，然后初始化各个对象。\n\n##优化思路---内存换时间\n\n在app启动时候，就将mReactRootView初始化出来，并缓存起来，在用的时候直接setContentView(mReactRootView)，达到秒开。\n\n###步骤1 缓存rootview管理器\n\n缓存rootview管理器主要用于初始化和缓存rootview对象。\n\n```\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.ViewParent;\n\nimport com.facebook.react.LifecycleState;\nimport com.facebook.react.ReactInstanceManager;\nimport com.facebook.react.ReactPackage;\nimport com.facebook.react.ReactRootView;\n\nimport java.lang.reflect.Field;\n\n/**\n * 缓存view管理\n */\npublic class RNCacheViewManager {\n\n    private static ReactRootView mRootView = null;\n    private static ReactInstanceManager mManager = null;\n    private static AbsRnInfo mRnInfo = null;\n\n    //初始化\n    public static void init(Activity act, AbsRnInfo rnInfo) {\n        init(act, rnInfo, null);\n    }\n\n    public static void init(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) {\n        if (mManager == null) {\n            updateCache(act, rnInfo, launchOptions);\n        }\n    }\n\n    public static void updateCache(Activity act, AbsRnInfo rnInfo) {\n        updateCache(act, rnInfo, null);\n    }\n\n    //更新cache，适合于版本升级时候更新cache\n    public static void updateCache(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) {\n        mRnInfo = rnInfo;\n        mManager = createReactInstanceManager(act);\n        mRootView = new ReactRootView(act);\n        mRootView.startReactApplication(mManager, rnInfo.getMainComponentName(), launchOptions);\n    }\n\n//设置模块名称，因为是private，只能通过反射赋值\n    public static void setModuleName(String moduleName) {\n        try {\n            Field field = ReactRootView.class.getDeclaredField(\"mJSModuleName\");\n            field.setAccessible(true);\n            field.set(getReactRootView(), moduleName);\n        } catch (Throwable e) {\n            throw new RuntimeException(e);\n        }\n    }\n    \n//设置启动参数，因为是private，只能通过反射赋值\n    public static void setLaunchOptions(Bundle launchOptions) {\n        try {\n            Field field = ReactRootView.class.getDeclaredField(\"mLaunchOptions\");\n            field.setAccessible(true);\n            field.set(getReactRootView(), launchOptions);\n        } catch (Throwable e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static ReactRootView getReactRootView() {\n        if(mRootView==null){\n            throw new RuntimeException(\"缓存view管理器尚未初始化！\");\n        }\n        return mRootView;\n    }\n\n    public static ReactInstanceManager getReactInstanceManager() {\n        if(mManager==null){\n            throw new RuntimeException(\"缓存view管理器尚未初始化！\");\n        }\n        return mManager;\n    }\n\n    public static AbsRnInfo getRnInfo() {\n        if(mRnInfo==null){\n            throw  new RuntimeException(\"缓存view管理器尚未初始化！\");\n        }\n        return mRnInfo;\n    }\n\n    public static void onDestroy() {\n        try {\n            ViewParent parent = getReactRootView().getParent();\n            if (parent != null)\n                ((android.view.ViewGroup) parent).removeView(getReactRootView());\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n    public static void clear() {\n        try {\n            if (mManager != null) {\n                mManager.onDestroy();\n                mManager = null;\n            }\n            if (mRootView != null) {\n                onDestroy();\n                mRootView = null;\n            }\n            mRnInfo = null;\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static ReactInstanceManager createReactInstanceManager(Activity act) {\n\n        ReactInstanceManager.Builder builder = ReactInstanceManager.builder()\n                .setApplication(act.getApplication())\n                .setJSMainModuleName(getRnInfo().getJSMainModuleName())\n                .setUseDeveloperSupport(getRnInfo().getUseDeveloperSupport())\n                .setInitialLifecycleState(LifecycleState.BEFORE_RESUME);\n\n        for (ReactPackage reactPackage : getRnInfo().getPackages()) {\n            builder.addPackage(reactPackage);\n        }\n\n        String jsBundleFile = getRnInfo().getJSBundleFile();\n\n        if (jsBundleFile != null) {\n            builder.setJSBundleFile(jsBundleFile);\n        } else {\n            builder.setBundleAssetName(getRnInfo().getBundleAssetName());\n        }\n        return builder.build();\n    }\n}\n\n```\n\n###步骤2 重写ReactActivity\n将官方的ReactActivity粘出来，重写2个方法，onCreate和onDestroy，其余代码不动。\n\nonCreate方法中使用缓存rootview管理器来获得rootview对象，而不是重新创建。\n\n这里曾尝试继承ReactActivity，而不是重写这个类，但是子类覆盖onCreate方法时候，必须要调用super.onCreate，否则编译会报错，但是super.onCreate方法会重新创建rootview，所以实在是绕不过去了。\n\n```\nprotected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        if (RNCacheViewManager.getRnInfo().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {\n            // Get permission to show redbox in dev builds.\n            if (!Settings.canDrawOverlays(this)) {\n                Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);\n                startActivity(serviceIntent);\n                FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);\n                Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();\n            }\n        }\n\n        mReactInstanceManager = RNCacheViewManager.getReactInstanceManager();\n        ReactRootView mReactRootView = RNCacheViewManager.getReactRootView();\n        setContentView(mReactRootView);\n    }\n```\n\n\nonDestroy方法中，不能再调用原有的mReactInstanceManager.destroy()方法了，否则rn初始化出来的对象会被销毁，下次就用不了了。同时，要卸载掉rootview的parent对象，否则下次再setContentView时候回报错。\n\n```\nprotected void onDestroy() {\n        RNCacheViewManager.onDestroy();\n        super.onDestroy();\n    }\n```    \n    \nRNCacheViewManager.onDestroy的方法：\n\n```\npublic static void onDestroy() {\n        try {\n            ViewParent parent = getReactRootView().getParent();\n            if (parent != null)\n                ((android.view.ViewGroup) parent).removeView(getReactRootView());\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n```\n\n\n###步骤3 在app启动时候初始化缓存rootview管理器\n\n```\nRNCacheViewManager.init((Activity) context, new RnInfo(moduleName, launchOptions));\n```\n\n其中RnInfo如下：\n\n```\npublic class RnInfo extends AbsRnInfo {\n\n    private String mModuleName;\n    private Bundle mLaunchOptions;\n\n    public RnInfo(String moduleName) {\n        this.mModuleName = moduleName;\n    }\n\n    public RnInfo(String moduleName, Bundle launchOptions) {\n        this.mModuleName = moduleName;\n        this.mLaunchOptions = launchOptions;\n    }\n\n    @Nullable\n    @Override\n    public Bundle getLaunchOptions() {\n        return mLaunchOptions;\n    }\n\n    @Override\n    public String getMainComponentName() {\n        return mModuleName;\n    }\n\n    @Override\n    public String getJSMainModuleName() {\n        return RNKeys.Default.DEf_JS_MAIN_MODULE_NAME;\n    }\n\n    @Nullable\n    @Override\n    public String getJSBundleFile() {\n        return RNManager.getJsBundlePath();\n    }\n\n    @Override\n    public boolean getUseDeveloperSupport() {\n        return true;\n    }\n\n    @Override\n    public List<ReactPackage> getPackages() {\n        return Arrays.asList(\n                new MainReactPackage(),\n                new BBReactPackage()\n        );\n    }\n}\n```\n\n#结语\n希望本篇文档能帮助遇到类似问题的小伙伴们。\n\nreactnative虽然不是银弹，但是在目前移动端浏览器兼容性弱爆了的情况下，还是能极大的提升开发测试效率的，性能也是极佳的，看好rn的未来。\n\n\n#参考文章\nhttp://zhuanlan.zhihu.com/magilu/20587485\n\nhttp://zhuanlan.zhihu.com/magilu/20259704\n\nhttps://yq.aliyun.com/articles/3208?spm=5176.100239.yqblog1.39.t2g49u&utm_source=tuicool&utm_medium=referral\n\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative导航设计与实现.md",
    "content": "# ReactNative导航设计与实现\n\n# 前言\n\n关于reactnaitve的导航，官方提供了2个组件，NavigatorIOS和Navigator，其中官方并不推荐使用NavigatorIOS，它不是官方维护的，不能保证及时的更新和维护。\n\n所以本文中是以Navigator组件为基础，进行导航的设计和实现。\n\nNavigator的劣势：Navigator组件是纯js的实现，所以在页面进行转场动画的过程中，如果js不能保证在16ms内完成其它操作的话，转场动画会有卡顿现象，后面会介绍优化的方案。\n\n官方的Navigator组件使用方式较为灵活，本文的目的是选取一种最佳用法，并提取出通用功能应对常用场景，规范和设计项目中导航的使用。\n\n# 定义\n\nrn应用：全站rn应用，简称rn应用。\n\nrn模块：部分模块使用rn，简称rn模块。\n\nrn首页：无论是rn应用还是rn模块，进入rn页面的第一屏，简称rn首页。\n\nnav：Navigator组件对象的简称，注意是实例化好的对象，不是类。页面间传递的导航对象统一使用此命名。\n\nHeader：自定义的导航栏组件。\n\n# 体系结构、设计原则\n\n一个rn应用或者一个rn模块，有且只有一个Navigator组件被定义。\n\n在rn首页定义Navigator组件。\n\n各个子页面统一使用首页定义的Navigator组件对象nav。\n\n不要使用Navigator的navigationBar，请自定义导航栏组件，例如Header组件。\n\n\n# Navigator组件的定义和初始化\n\n在rn首页中的render方法中，定义一个Navigator组件，并做好以下几件事：\n\n1. 实现好通用的renderScene方法，\n2. 实现好android的物理返回按键\n3. 初始化真正的rn首页\n\n## 实现统一路由函数renderScene\n\nrenderScene函数是Navigator组件的必填函数，入参是route对象和当前的nav对象，返回值是jsx。\n\n此函数的意思是根据传入的route，返回一个作为新页面的jsx，也就是说所有的路由算法都是在此函数中实现的。\n\n其中route对象是一个自定义的对象，是nav.push方法中传入的对象。\n\n此函数设计相当于门面模式，此函数是路由的统一处理器，所有的页面跳转请求都会通过此函数的计算来获得具体的jsx页面。\n\n既然是统一的路由处理器，必然要求传入的route对象要满足统一的规则，否则无法实现统一的算法。\n\n在此，设计route对象如下：\n\n```\n{\n\tname: 'page2', //名字用来做上下文判断和日志输出\n\tpage: <Page2 />, //jsx形式的page，作为新页面的jsx\n\t// page: () => <Page2 />, //或者函数形式的page，此函数必须返回jsx，此jsx作为新页面的jsx\n}\n```\n根据route对象设计，设计统一的renderScene方法如下：\n\n```\n_renderPage(route, nav) {\n\n        if (!route.page) {\n            console.error('页面导航请求没有传入page参数.');\n            return null;\n        }\n\n        let page;\n\n        if (typeof route.page === 'function') {\n            page = route.page();\n        } else {\n            page = route.page;\n        }\n\n\n        let name = route.name;\n        if (!name) {\n            if (page) {\n                name = page.type.name;\n            }\n        }\n        console.log(`in render page ${name}`);\n\n        return page;\n    }\n```\n\n业务代码中，页面跳转的时候，只需要如下代码\n\n```\nnav.push({\n\tname: 'page2',\n\tpage: <Page2 nav={nav}/>,\n});\n```\n\n## android物理返回按键的处理\n如果你的应用需要支持android的话，那就要实现andorid的物理返回按键的对应处理。\n\n一般按物理返回按键要么是返回上一页面，要么是返回页面的上一状态【例如，有打开的弹窗，按返回是关闭这个弹窗】。\n\n返回上一页面因为有通用路由器的存在，所以可以通用处理，直接使用nav.pop()即可。\n\n但是返回页面上一状态，并不容易统一处理，所以使用基于事件扩展的方式，交给业务代码自行实现。\n\n在此重构route对象的规则，添加事件onHardwareBackPress，如下\n\n```\n{\n\tname: 'page2', //名字用来做上下文判断和日志输出\n\tpage: <Page2 />, //jsx形式的page，作为新页面的jsx\n\t// page: () => <Page2 />, //或者函数形式的page，此函数必须返回jsx，此jsx作为新页面的jsx\n\tonHardwareBackPress: () => alert('点物理按键会触发我'), // 返回false就终止统一路由器的默认动作，即终止页面返回动作,可以在此方法中实现返回页面上一状态的相关实现\n}\n```\n\nandroid物理返回按键的统一处理代码如下，\n\n```\ncomponentWillMount() {\n\n        BackAndroid.addEventListener('hardwareBackPress', () => {\n\n            if (this.refs.nav) {\n\n                let routes = this.refs.nav.getCurrentRoutes();\n                let lastRoute = routes[routes.length - 1]; // 当前页面对应的route对象\n\n                if (lastRoute.onHardwareBackPress) {// 先执行route注册的事件\n                    let flag = lastRoute.onHardwareBackPress();\n                    if (flag === false) {// 返回值为false就终止后续操作\n                        return true;\n                    }\n                }\n\n\n                if (routes.length === 1) {// 在第一页了\n\n                    // 此处可以根据情况实现 点2次就退出应用，或者弹出rn视图等\n                    \n                } else {\n                    \n                    this.refs.nav.pop();\n                    \n                }\n            }\n\n            return true;\n        });\n    }\n    \n```\n\n\n## 初始化真正的rn首页\n\n此处较为简单，直接使用Navigator组件的initialRoute属性来指定初始化的route对象。\n\n```\n<Navigator initialRoute={{\n           page: <Home />, // Home为伪代码，自定义的首页组件\n           name: 'home',\n       }} />\n```\n\n# 页面跳转\n\n根据前面设计好的renderScene方法，直接使用如下代码,即可跳转到Page2，并将nav对象传递给了Page2.\n\n```\nnav.push({\n\tname: 'page2',\n\tpage: <Page2 nav={nav}/>,\n});\n```\n\n# 页面返回\n\n页面返回直接使用\n\n```\nnav.pop();\n```\n\n# 页面转场优化\n\n前面提到，Navigator组件完全使用js实现，由于js的单线程特点，如果在页面转场动画过程中，js干其他事情【比如渲染个某个jsx】超过了16ms，那么转场动画将不足60帧，给用户的感觉就是动画有卡顿。\n\n为了避免这种情况，一种简单粗暴的办法就是在转场动画中不要让js来干别的事情。\n\n那么我们如何知道转场动画什么时候结束呢，官方提供了动画交互管理器InteractionManager，示例伪代码如下：\n\n```\nInteractionManager.runAfterInteractions(() => {\n      alert('哈哈 转场动画结束了！');\n    });\n```\n\n大多数的场景：点击page1的某个按钮，要跳转到page2，并且page2要和服务器请求数据，根据返回的数据来渲染page2的部分or全部内容。\n\n针对上述场景，解决方案如下，用伪代码描述：\n\n1. page2的state至少有2个值，转场动画进行中=true，服务器查询=true\n1. page2的componentWillMount方法中发起异步服务器交互请求,当请求结束setState:服务器查询=false\n2. page2的componentWillMount方法中注册InteractionManager.runAfterInteractions事件，当转场结束setState:转场动画进行中=false\n3. page2的render方法中，先判断（转场动画进行中=true || 服务器查询=true）就返回一个loading的提示，否则返回真正的jsx，并且此时，服务器返回的数据已经可用了\n\n也可以参考官方文档： http://reactnative.cn/docs/0.22/performance.html#content\n\n# 刷新的实现\n\n目标：实现类似于html中window.reload的方法。\n\n由于我们对route的规则限定，所以我们可以做到统一的刷新页面的逻辑。\n\n思路是，\n\n1. 首先获得当前页面对应的route对象\n2. 然后获取route中的page属性，page属性可能是当前页面的jsx，也可能是可以产生当前页面jsx的方法\n3. 最后使用官方Navigator组件提供的replace方法，来用新的route替换掉原有的route\n\n示例参考代码如下：\n\n```\n/**\n     * 刷新页面，route可以为空，会刷新当前页面\n     * @param nav\n     * @param route\n     */\n   refresh(nav, route) {\n\n        if (!route) {\n            let routes = nav.getCurrentRoutes();\n            let length = routes.length;\n            route = routes[length - 1]; // 使用当前页对应的route\n        }\n\n        // todo 最好的方式是直接使用route.page，但是不好使，这种写法只支持一层节点，如果有多层会有问题\n        // todo 暂时未处理page是function的情况\n        let Tag = route.page.type;\n        nav.replace({\n            page: <Tag {...route.page.props} />,\n        });\n\n    }\n```\n\n然后业务代码中这样调用,当前页面就被刷新了。\n\n```\nUtil.refresh(nav); //Util是伪代码，是你定义refresh方法的对应对象\n\n```\n\n\n# rn首页直接跳转子页面\n\n如果你开发的是rn模块【rn模块嵌入到已有app中，定义可以参考前面定义一节】，可能进入rn模块的入口会很多，比如，用rn开发一个论坛模块，正常入口进来是直接展现帖子列表，也可能会有点击某个其它按钮【此按钮是不是rn的】会直接跳转到某个帖子的详情页。\n\n使用官方Navigator组件提供的initialRouteStack属性，可以完美的解决此问题，官方文档对此属性的说明如下：提供一个路由集合用来初始化。如果没有设置初始路由的话则必须设置该属性。如果没有提供该属性，它将被默认设置成一个只含有initialRoute的数组。\n\n说白了就是，initialRouteStack要定义一个数组，里面是很多route对象，然后Navigator对象会展现到最后一个，而且数组中的其他route也都被初始化过了，你想返回到任何一个route都是可以的，是不是爽歪歪了。\n\n给个示例代码吧，这是我项目中真正的代码，请当伪代码来阅读:\n\n```\ngetInitialRouteStack() {\n\n        let props = this.getProps();\n\n        let detailId = props.detailId;\n        if (detailId) { // 如果传入了详情id，那么跳转到详情页\n            return [{name: 'home', },\n            {\n                page: <AskDetail data={{id: detailId, }}/>,\n                backIsClose: true,\n            }];\n        }\n\n\n        let wantAsk = props.wantAsk;\n        if (wantAsk === true || wantAsk === 'true') { // 如果传入了提问属性=true，那么直接跳转到提问页面\n            return [{name: 'home', },\n            {\n                page: <WantAsk backIsClose={true}/>,\n                backIsClose: true,\n            }];\n        }\n\n        // 跳转到首页\n        return [{name: 'home', }];\n\n    }\n```\n\n# 实现代码参考：\n根据以上设计思路，笔者封装了一个Navigator组件，是对官方的navigator组件进行了一层封装，供大家参考：\n\n```\nimport React from \"react-native\";\n\nconst {\n    Platform,\n    Animated,\n    View,\n    DeviceEventEmitter,\n    Dimensions,\n    Navigator,\n    BackAndroid,\n    } = React;\n\nclass Navigator2 extends React.Component {\n\n    componentWillMount() {\n\n        BackAndroid.addEventListener('hardwareBackPress', () => {\n\n            if (this.refs.nav) {\n\n                let routes = this.refs.nav.getCurrentRoutes();\n                let lastRoute = routes[routes.length - 1];\n\n                if (lastRoute.onHardwareBackPress) {// 先执行route注册的事件\n                    let flag = lastRoute.onHardwareBackPress();\n                    if (flag === false) {// 返回值为false就终止后续操作\n                        return true;\n                    }\n                }\n\n\n                if (routes.length === 1) {// 在第一页了\n\n                    if (this.props.nav) {// 父页面仍有nav\n                        this.props.nav.pop();\n                    }\n\n                    if (this.props.onHardwareBackPressInFirstPage) {\n                        this.props.onHardwareBackPressInFirstPage();\n                    }\n\n                } else {\n\n                    if (lastRoute.backIsClose === true) {\n                        if (this.props.onHardwareBackPressInFirstPage) {\n                            this.props.onHardwareBackPressInFirstPage();\n                        }\n                    } else {\n                        this.refs.nav.pop();\n                    }\n                }\n            }\n\n            return true;\n        });\n    }\n\n\n    getLastRoute() {\n        if (this.refs.nav) {\n            let routes = this.getCurrentRoutes();\n            let lastRoute = routes[routes.length - 1];\n            return lastRoute;\n        }\n\n        return null;\n    }\n\n    render() {\n        return <Navigator renderScene={this._renderPage.bind(this)}\n                          {...this.props}\n                          ref='nav'\n            />;\n    }\n\n\n    _renderPage(route, nav) {\n\n        if (!route.page) {\n            console.error('页面导航请求没有传入page参数.');\n            return null;\n        }\n\n        let page;\n\n        if (typeof route.page === 'function') {\n            page = route.page();\n        } else {\n            page = route.page;\n        }\n\n\n        let name = route.name;\n        if (!name) {\n            if (page) {\n                name = page.type.name;\n            }\n        }\n        console.log(`in render page ${name}`);\n\n        return page;\n    }\n\n    // todo 以下的方法为实现原版navigator的方法，这样做不好，但是没想到其它好办法\n    getCurrentRoutes() {\n        return this.refs.nav.getCurrentRoutes(...arguments);\n    }\n    jumpBack() {\n        return this.refs.nav.jumpBack(...arguments);\n    }\n    jumpForward() {\n        return this.refs.nav.jumpForward(...arguments);\n    }\n    jumpTo(route) {\n        return this.refs.nav.jumpTo(...arguments);\n    }\n    push(route) {\n        return this.refs.nav.push(...arguments);\n    }\n    pop() {\n        return this.refs.nav.pop(...arguments);\n    }\n    replace(route) {\n        return this.refs.nav.replace(...arguments);\n    }\n    replaceAtIndex(route, index) {\n        return this.refs.nav.replaceAtIndex(...arguments);\n    }\n    replacePrevious(route) {\n        return this.refs.nav.replacePrevious(...arguments);\n    }\n    immediatelyResetRouteStack(routeStack) {\n        return this.refs.nav.immediatelyResetRouteStack(...arguments);\n    }\n    popToRoute(route) {\n        return this.refs.nav.popToRoute(...arguments);\n    }\n    popToTop() {\n        return this.refs.nav.popToTop(...arguments);\n    }\n\n}\n\nmodule.exports = Navigator2;\n\n```\n\n#参考地址\n\nhttp://reactnative.cn/docs/0.21/navigator.html#content\n\nhttp://reactnative.cn/docs/0.22/performance.html#content\n\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative开发技巧总结.md",
    "content": "# ReactNative开发技巧总结\n\n#样式\n不支持选择器\n\n无复用就尽量用行内，避免命名难题\n\n不支持position=fixed，利用flexbox可以搞定。<https://medium.com/@Nayflix/building-a-soundcloud-app-with-react-native-1144b6d3773a#.n6bzcbm43>\n\n\t\t\nRN样式没有提供类似于display=none的方式,使用BaseCss提供的hidden样式,原理是绝对定位，left=-9999\n\t\n总结的较好：http://segmentfault.com/a/1190000002658374\n\n\n#调试\n在系统首页调试可以极大的提供效率，系统首页直接使用你要调试的组件作为app\n\t\njs可以用chrome来调试\n\ncss和结构就靠刷新了，官方给的结构查看器不是很好用\n\n在一些重要逻辑部分多用console.log，特别是在触摸事件中，有时候还可以发现隐藏问题，习惯用console.log代替一些注释\n\n\n#touch系列\n一般包裹在view的外面，margin写在touch上，不要写在view上，否则margin会有动画效果，padding写在view上。\n\t\n一般使用touchOpacity，不使用touchHeighlight\n\n\n\n#text\n一定不要有padding margin，如果要有请在外面套一个view，写在view上\n\n#listview，scrollview\n必须有高，否则不能滚动，flex=1也可以，相当于给了高\n\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative打离线包-android篇.md",
    "content": "# ReactNative打离线包-android篇\n\n\n官方文档：<http://facebook.github.io/react-native/docs/running-on-device-android.html#content>\n\n官方文档2：<http://facebook.github.io/react-native/docs/signed-apk-android.html#content>\n\n离线包就是把RN和你写的js图片等资源都打包放入app，不需要走网络下载。\n\n#打包命令说明\n\nreact-native bundle\n\nOptions:\n\n  --entry-file        Path to the root JS file, either absolute or relative to JS root                                   [required]\n  \n  --platform          Either \"ios\" or \"android\"         \n                                                                 \n  --transformer       Specify a custom transformer to be used (absolute path)                                            [default: \"/Users/babytree-mbp13/projects/xcodeProjects/AwesomeProject/node_modules/react-native/packager/transformer.js\"]\n  \n  --dev               If false, warnings are disabled and the bundle is minified                                         [default: true]\n  \n  --prepack           If true, the output bundle will use the Prepack format.                                            [default: false]\n  \n  --bridge-config     File name of a a JSON export of __fbBatchedBridgeConfig. Used by Prepack. Ex. ./bridgeconfig.json\n  \n  --bundle-output     File name where to store the resulting bundle, ex. /tmp/groups.bundle                              [required]\n  \n  --bundle-encoding   Encoding the bundle should be written in (https://nodejs.org/api/buffer.html#buffer_buffer).       [default: \"utf8\"]\n  \n  --sourcemap-output  File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map       \n       \n  --assets-dest       Directory name where to store assets referenced in the bundle                     \n                 \n  --verbose           Enables logging                                                                                    [default: false]\n\n\n#安卓打包步骤\n\n1. 在工程根目录下执行打包命令，比如``` react-native bundle --entry-file demo/index.js --bundle-output ./android/app/src/main/assets/index.android.jsbundle --platform android --assets-dest ./android/app/src/main/res/ --dev false ```请参考上面命令说明，根据自己的情况进行修改再执行。注意要先保证[./android/app/src/main/assets/]文件夹存在。\n2. ps:增量升级的话不要把图片资源直接打包到res中，脚本如下：```  react-native bundle --entry-file demo/index.js --bundle-output ./bundle/androidBundle/index.android.jsbundle --platform android --assets-dest ./bundle/androidBundle --dev false  ```\n1. 命令执行完生成资源如图![2015-12-24 11.05.31](media/2015-12-24%2011.05.31.png)\n\n1. 保证MainActivity.java中的setBundleAssetName与你的jsbundle文件名一致，比如`.setBundleAssetName(\"index.android.jsbundle\")`就与我生成的资源名一致\n2. 一切OK 打包测试吧\n\n\n\n#To disable the developer menu for production builds:\n\nFor iOS open your project in Xcode and select Product → Scheme → Edit Scheme... (or press ⌘ + <). Next, select Run from the menu on the left and change the Build Configuration to Release.\n\n\nFor Android, by default, developer menu will be disabled in release builds done by gradle (e.g with gradle assembleRelease task). Although this behavior can be customized by passing proper value to ReactInstanceManager#setUseDeveloperSupport.\n\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative打离线包-ios篇.md",
    "content": "# ReactNative打离线包-ios篇\n\n官方文档，内容很旧：<http://facebook.github.io/react-native/docs/running-on-device-ios.html#content>\n\n相关链接：<https://github.com/facebook/react-native/issues/4084>\n\n离线包就是把RN和你写的js图片等资源都打包放入app，不需要走网络下载。\n\n升级服务器的使用方法：\n在mobile-rn/bbt-rn-update-server目录中，执行命令npm start，然后访问http://localhost:3000/\n\n#打包命令说明\n\nreact-native bundle\n\nOptions:\n\n  --entry-file        Path to the root JS file, either absolute or relative to JS root                                   [required]\n  \n  --platform          Either \"ios\" or \"android\"         \n                                                                 \n  --transformer       Specify a custom transformer to be used (absolute path)                                            [default: \"/Users/babytree-mbp13/projects/xcodeProjects/AwesomeProject/node_modules/react-native/packager/transformer.js\"]\n  \n  --dev               If false, warnings are disabled and the bundle is minified                                         [default: true]\n  \n  --prepack           If true, the output bundle will use the Prepack format.                                            [default: false]\n  \n  --bridge-config     File name of a a JSON export of __fbBatchedBridgeConfig. Used by Prepack. Ex. ./bridgeconfig.json\n  \n  --bundle-output     File name where to store the resulting bundle, ex. /tmp/groups.bundle                              [required]\n  \n  --bundle-encoding   Encoding the bundle should be written in (https://nodejs.org/api/buffer.html#buffer_buffer).       [default: \"utf8\"]\n  \n  --sourcemap-output  File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map       \n       \n  --assets-dest       Directory name where to store assets referenced in the bundle                     \n                 \n  --verbose           Enables logging                                                                                    [default: false]\n\n\n \n#ios打包步骤\n1. 在工程根目录下执行打包命令，比如``` react-native bundle --entry-file demo/index.js --bundle-output ./bundle/iosBundle/index.ios.jsbundle --platform ios --assets-dest ./bundle/iosBundle --dev false ```请参考上面命令说明，根据自己的情况进行修改再执行。注意要先保证bundle文件夹存在。\n2. 命令执行完生成如下资源 ![323424sf](media/323424sf-1.png)\n\n\n\n\n2. 在xcode中添加assets【必须用Create folder references的方式，添加完是蓝色文件夹图标】和index.ios.jsbundle，如图![2015-12-23 17.35.50](media/2015-12-23%2017.35.50.png)\n3. 参考官方文档，修改AppDelegate.m文件,使用OPTION 2处的代码\n\t```\n\tjsCodeLocation = [[NSBundle mainBundle] URLForResource:@\"index.ios\" withExtension:@\"jsbundle\"];\n\t```\n4. 一切OK 运行模拟器看效果吧\n \n#ios打包遇到的问题\n1. 离线包如果开启了chrome调试，会访问调试服务器，而且会一直loading出不来。 \n2. 如果bundle的名字是main.jsbundle,app会一直读取旧的,改名就好了。。。非常奇葩的问题，我重新删了app，clean工程都没用，就是不能用main.jsbundle这个名字。\n3. 必须用Create folder references【蓝色文件夹图标】的方式引入图片的assets，否则引用不到图片\n4. 执行bundle命令之前，要保证相关的文件夹都存在\n\n\n#To disable the developer menu for production builds:\n\nFor iOS open your project in Xcode and select Product → Scheme → Edit Scheme... (or press ⌘ + <). Next, select Run from the menu on the left and change the Build Configuration to Release.\n\n\nFor Android, by default, developer menu will be disabled in release builds done by gradle (e.g with gradle assembleRelease task). Although this behavior can be customized by passing proper value to ReactInstanceManager#setUseDeveloperSupport.\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative的ios崩溃整理分析.md",
    "content": "# ReactNative的ios崩溃整理分析\n\n共12种异常,如下：\n\n```\nwanghonglu  18:14\n>---------------------- Row   1 -----------------------<\n=> Start Unable to execute JS call: __fbBatchedBridge is undefined \n\t-> translating『 0x100ac2b60x100aa8004 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120\n\t-> translating『 0x100ac2b60 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476\n\t-> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13\n=> End Unable to execute JS call: __fbBatchedBridge is undefined \n>------------------------------------------------------<\n\n\n>---------------------- Row   2 -----------------------<\n=> Start Application received signal SIGSEGV \n\t-> translating『 0x1012891bc 』=> \n\t-> translating『 0x100abf734 』=> __64-[RCTJSCExecutor executeApplicationScript:sourceURL:onComplete:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 515\n\t-> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547\n\t-> translating『 0x100abdc3c 』=> +[RCTJSCExecutor runRunLoopThread] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 151\n=> End Application received signal SIGSEGV \n>------------------------------------------------------<\nwanghonglu  18:14\n>---------------------- Row   4 -----------------------<\n=> Start Application received signal SIGSEGV \n\t-> translating『 0x1012891bc 』=> \n\t-> translating『 0x100abf734 』=> __64-[RCTJSCExecutor executeApplicationScript:sourceURL:onComplete:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 515\n\t-> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547\n\t-> translating『 0x100abdc3c 』=> +[RCTJSCExecutor runRunLoopThread] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 151\n=> End Application received signal SIGSEGV\nwanghonglu  18:14\n>---------------------- Row   7 -----------------------<\n=> Start SyntaxError: Unexpected end of script \n\t-> translating『 0x100aa8004 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120\n\t-> translating『 0x100ac2b60 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476\n\t-> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13\n=> End SyntaxError: Unexpected end of script \n>------------------------------------------------------<\n\n\n>---------------------- Row   8 -----------------------<\n=> Start Unable to execute JS call: __fbBatchedBridge is undefined \n\t-> translating『 0x100aa8004 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120\n\t-> translating『 0x100ac2b60 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476\n\t-> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13\n=> End Unable to execute JS call: __fbBatchedBridge is undefined \n>------------------------------------------------------<\nwanghonglu  18:14\n>---------------------- Row  11 -----------------------<\n=> Start Application received signal SIGSEGV \n\t-> translating『 0x1012891bc 』=> \n\t-> translating『 0x100a92140 』=> __46-[RCTNetworking buildRequest:completionBlock:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/Libraries/Network/RCTNetworking.m: line 215\n\t-> translating『 0x100a923bc 』=> -[RCTNetworking processDataForHTTPQuery:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/Libraries/Network/RCTNetworking.m: line 264\n\t-> translating『 0x100a91e2c 』=> -[RCTNetworking buildRequest:completionBlock:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/Libraries/Network/RCTNetworking.m: line 204\n\t-> translating『 0x100a940a8 』=> -[RCTNetworking sendRequest:responseSender:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/Libraries/Network/RCTNetworking.m: line 442\n\t-> translating『 0x100aa67ec 』=> -[RCTModuleMethod invokeWithBridge:module:arguments:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleMethod.m: line 426\n\t-> translating『 0x100ac53f0 』=> -[RCTBatchedBridge _handleRequestNumber:moduleID:methodID:params:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 899\n\t-> translating『 0x100ac4de0 』=> __33-[RCTBatchedBridge handleBuffer:]_block_invoke.383 /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 848\n=> End Application received signal SIGSEGV \n>------------------------------------------------------<\nwanghonglu  18:15\n>---------------------- Row  16 -----------------------<\n=> Start Unable to execute JS call: __fbBatchedBridge is undefined \n\t-> translating『 0xa3b4ef 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120\n\t-> translating『 0xa52b37 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476\n\t-> translating『 0x68b883 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13\n=> End Unable to execute JS call: __fbBatchedBridge is undefined \n>------------------------------------------------------<\nwanghonglu  18:15\n>---------------------- Row  22 -----------------------<\n=> Start Application received signal SIGSEGV \n\t-> translating『 0x1012891bc 』=> \n\t-> translating『 0x100ac1c30 』=> -[RCTBatchedBridge registerModuleForFrameUpdates:withModuleData:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 357\n\t-> translating『 0x100ac707c 』=> -[RCTModuleData finishSetupForInstance] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleData.m: line 73\n\t-> translating『 0x100ac1a74 』=> -[RCTBatchedBridge initModules] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 249\n\t-> translating『 0x100ac02a8 』=> -[RCTBatchedBridge start] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 99\n\t-> translating『 0x100ac016c 』=> -[RCTBatchedBridge initWithParentBridge:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 68\n\t-> translating『 0x100ad5648 』=> -[RCTBridge setUp] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 257\n\t-> translating『 0x100ad5068 』=> -[RCTBridge initWithDelegate:launchOptions:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 156\n\t-> translating『 0x100071c60 』=> __36-[BBRNManager reactNativeBridgeInit]_block_invoke /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/ReactNative/Utils/BBRNManager.m: line 56\n\t-> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13\n=> End Application received signal SIGSEGV \n>------------------------------------------------------<\nwanghonglu  18:15\n>---------------------- Row  24 -----------------------<\n=> Start Application received signal SIGABRT \n\t-> translating『 0x1012891bc 』=> \n\t-> translating『 0x100abf290 』=> __52-[RCTJSCExecutor _executeJSCall:arguments:callback:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 391\n\t-> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547\n\t-> translating『 0x100abeda4 』=> -[RCTJSCExecutor _executeJSCall:arguments:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 385\n\t-> translating『 0x100abeb7c 』=> -[RCTJSCExecutor callFunctionOnModule:method:arguments:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 368\n\t-> translating『 0x100ac43b0 』=> -[RCTBatchedBridge _actuallyInvokeAndProcessModule:method:arguments:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 747\n\t-> translating『 0x100ac3774 』=> __39-[RCTBatchedBridge enqueueJSCall:args:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 642\n\t-> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547\n\t-> translating『 0x100abdc3c 』=> +[RCTJSCExecutor runRunLoopThread] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 151\n=> End Application received signal SIGABRT \n>------------------------------------------------------<\nwanghonglu  18:15\n>---------------------- Row  25 -----------------------<\n=> Start Application received signal SIGABRT \n\t-> translating『 0x1012891bc 』=> \n\t-> translating『 0x100ace79c 』=> _RCTJSONStringifyNoRetry /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTUtils.m: line 26\n\t-> translating『 0x100ace5bc 』=> RCTJSONStringify /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTUtils.m: line 71\n\t-> translating『 0x100abeeec 』=> __52-[RCTJSCExecutor _executeJSCall:arguments:callback:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 391\n\t-> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547\n\t-> translating『 0x100abeda4 』=> -[RCTJSCExecutor _executeJSCall:arguments:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 385\n\t-> translating『 0x100abeb7c 』=> -[RCTJSCExecutor callFunctionOnModule:method:arguments:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 368\n\t-> translating『 0x100ac43b0 』=> -[RCTBatchedBridge _actuallyInvokeAndProcessModule:method:arguments:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 747\n\t-> translating『 0x100ac3774 』=> __39-[RCTBatchedBridge enqueueJSCall:args:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 642\n\t-> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547\n\t-> translating『 0x100abdc3c 』=> +[RCTJSCExecutor runRunLoopThread] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 151\n=> End Application received signal SIGABRT \n>------------------------------------------------------<\nwanghonglu  18:16\n>---------------------- Row  26 -----------------------<\n=> Start Application received signal SIGSEGV \n\t-> translating『 0x1012891bc 』=> \n\t-> translating『 0x100ac1c30 』=> -[RCTBatchedBridge registerModuleForFrameUpdates:withModuleData:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 357\n\t-> translating『 0x100ac707c 』=> -[RCTModuleData finishSetupForInstance] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleData.m: line 73\n\t-> translating『 0x100ac7400 』=> -[RCTModuleData instance] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleData.m: line 122\n\t-> translating『 0x100ac113c 』=> -[RCTBatchedBridge moduleForName:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 231\n\t-> translating『 0x100ad540c 』=> -[RCTBridge moduleForClass:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 236\n\t-> translating『 0x100ae16d4 』=> -[RCTUIManager setBridge:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Modules/RCTUIManager.m: line 270\n\t-> translating『 0x100ac6f2c 』=> -[RCTModuleData setBridgeForInstance] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleData.m: line 58\n\t-> translating『 0x100ac19b0 』=> -[RCTBatchedBridge initModules] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 249\n\t-> translating『 0x100ac02a8 』=> -[RCTBatchedBridge start] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 99\n\t-> translating『 0x100ac016c 』=> -[RCTBatchedBridge initWithParentBridge:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 68\n\t-> translating『 0x100ad5648 』=> -[RCTBridge setUp] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 257\n\t-> translating『 0x100ad5068 』=> -[RCTBridge initWithDelegate:launchOptions:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 156\n\t-> translating『 0x100071c60 』=> __36-[BBRNManager reactNativeBridgeInit]_block_invoke /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/ReactNative/Utils/BBRNManager.m: line 56\n\t-> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13\n=> End Application received signal SIGSEGV \n>------------------------------------------------------<\nwanghonglu  18:16\n>---------------------- Row  27 -----------------------<\n=> Start SyntaxError: Unexpected EOF \n\t-> translating『 0x100aa8004 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120\n\t-> translating『 0x100ac2b60 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476\n\t-> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13\n=> End SyntaxError: Unexpected EOF \n>------------------------------------------------------<\n```\n\n# __fbBatchedBridge is undefined 3次；Unexpected end of script 1次；Unexpected EOF 1次；\n\n场景：异常从RCTBatchedBridge stopLoadingWithError方法【此方法是当jsbundle加载出现异常时就会执行】抛出，应为加载的jsbundle文件有问题，有可能是文件下载不对或存储空间不足等，建议加载前，使用md5进行校验。\n\n解决：建议加载前，使用md5进行校验。校验不通过的不进行加载，尝试回退or重新下载补丁等动作。\n\nandroid端也出现了__fbBatchedBridge is undefined的异常，解法也是使用md5来校验jsbundle。\n\n参考链接：http://www.jianshu.com/p/3db03c8c4ae7\n\n\n# Start Application received signal SIGSEGV\n\n## [RCTJSCExecutor executeApplicationScript:sourceURL:onComplete:]2次\n\n-> translating『 0x1012891bc 』=> \n\n最根的没有转换出来\n\nRCTJSCExecutor executeApplicationScrip\n\nRCTJSCExecutor executeBlockOnJavaScriptQueue\n\n场景：暂时没有线索\n\n解决： todo\n\n\n## [RCTNetworking buildRequest:completionBlock:] 1次\n\n-> translating『 0x1012891bc 』=> \n\n最根的没有转换出来\n\n场景：js调用原生，发送网络请求\n\n解决：todo\n\n## [RCTBatchedBridge registerModuleForFrameUpdates:withModuleData:] 2次\n\n-> translating『 0x1012891bc 』=> \n\n最根的没有转换出来\n\n场景：初始化bridge的过程，[BBRNManager reactNativeBridgeInit]中调用[RCTBridge initWithDelegate:launchOptions:]的时候。\n\n解决：todo\n\n# Start Application received signal SIGABRT\n\n## [RCTJSCExecutor _executeJSCall:arguments:callback:] 1次\n\n-> translating『 0x1012891bc 』=> \n\n最根的没有转换出来\n\n场景：是由RCTBatchedBridge enqueueJSCall调用，是native调用js。\n\n解决：todo\n\n## RCTUtils.m 中的 _RCTJSONStringifyNoRetry 1次\n\n-> translating『 0x1012891bc 』=> \n\n最根的没有转换出来\n\n场景：将json对象转换为json字符串。也是由[RCTJSCExecutor _executeJSCall:arguments:callback:]调用造成的。是由RCTBatchedBridge enqueueJSCall调用，是native调用js。\n\n解决：todo\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative的架构设计.md",
    "content": "# ReactNative的组件架构设计\n\n还有一篇较早的文章，也是分析的过程，可以对本篇文章进行一个补全：RN组件状态设计思考：http://segmentfault.com/a/1190000004180955 \n\n请注意，本篇写的是react native的架构设计，如果你用react来开发web程序，本篇文章只能仅供参考，问题都没有在web上去考虑过。\n\n本篇较长，前面是目前flux开源框架的一些分析，后面是架构设计过程。您可以直奔主题。\n\n用RN最大的难题是设计思想的转变，以前的设计方法论已经不太适用了。而RN仅仅提供了view的框架，构建完整app的架构并没有直接提供。\n\n考虑目前遇到的如下问题，希望架构给出解决方案。\n\n1. **交互**：如何解决组件间通信【父子、子父、兄弟等，特别是跨层or反向数据流动等】；用state还是接口操作组件；\n3. **职责**：组件状态放哪，业务逻辑放哪，数据放哪，因为太灵活了，怎么做都可以实现功能，但是怎么做才是最好的，才是最正确的呢？\n\n*todo一个问题：由于react是面向状态编程，相当于react的组件只关注数据的最终状态，数据是怎么产生的并不关心，但是某些场景下，数据如何产生的是会影响到组件的一些行为的【比如一个新增行要求有动画效果，查询出的行就不需要等】，这在RN中很难描述。。。。。*\n\nRN架构就是为解决上述问题提供的指导和方法论，是通盘考虑整个开发、测试、运维的状况，做出的考虑最全面的抉择，或者为抉择提供依据。\n\n目前为react服务的架构也有一些了，如Flux，Reflux，Redux，Relay，Marty。\n\n##Flux\nflux是官方提供的架构，目的是分层解耦，职责划分清晰，谁负责干啥很明确。具体描述可以参考官方文档，这里不详述。\n\n1. action 封装请求\n2. dispatcher 注册处理器、分发请求\n3. store 是处理器，处理业务逻辑，保存数据\n4. view 根据store提供的数据进行展现;接受用户的输入并发出action请求。\n\n\n![](media/14500845993004.jpg)\n\n\n数据流动：\nAction-> Dispatcher -> Store -> Component\n\n但我觉得解耦的太细了，干一个事，要做太多太多的额外工作了。\n\n光注册监听动作就2次，一次是store注册到dispatcher，一次是view注册到store中。\n\n而且，注册到dispatcher的监听应该都不叫注册，架构完全没有提供任何封装，直接暴露一个统一的回调方法，里面自行if else路由不同的store。\n\n\n##Reflux\n结构上与flux架构基本一致，去掉了flux的一些冗余操作【比如没有了dispatcher】，架构更加简洁和紧凑，用到了一些约定大于配置的理念。\n\n基本上将flux的架构冗余都简化了，可以说是flux的去冗余提升版，但是没有本质的变化。\n\n```\n╔═════════╗       ╔════════╗       ╔═════════════════╗\n║ Actions ║──────>║ Stores ║──────>║ View Components ║\n╚═════════╝       ╚════════╝       ╚═════════════════╝\n     ^                                      │\n     └──────────────────────────────────────┘\n\n```\n\n1. 更容易的监听。listenables和约定以on开头的方法。等。\n2. 去掉了dispatcher。\n3. action可以进行aop编程。\n4. 去掉了waitfor。store可以监听store。\n5. component提供了一系列mixin，方便注册\\卸载到store的监听和与store交互等。\n\n##Redux\n\n社区内比较受推崇，因为用起来相对比较简单\n\n\n![](media/14502335830493.jpg)\n\n\n特性：\n\n1. 分层设计，职责清晰。\n2. 要求store reducer都是页面单例，易于管理。\n2. action为请求dto对象，是请求类型，请求数据的载体。\n3. reducer是处理请求的方法。不允许有状态，必须是纯方法。必须严格遵守输入输出，中间不允许有异步调用。不允许对state直接进行修改，要想修改必须返回新对象。\n4. store\n\t5. 维持应用的state；\n\t1. 提供 getState() 方法获取 state；\n\t2. 提供 dispatch(action) 方法分发请求来更新 state；门面模式，要求所有的请求满足统一的格式【可以进行路由、监控、日志等】，统一的调用方式。\n\t1. 通过 subscribe(listener) 注册监听器监听state的变化。\n1. 官方文档写的较为详细，从设计到开发都有，比flux要好\n\n痛处如下，看能否接受或者解决：\n\n1. redux的原则1：state不能被修改。 \n\t4. 其实这个用react的state也会有同样的问题，最好把state设计的没有冗余，尽量少出这种情况\n\t4. **解决方案：**参考官方：因为我们不能直接修改却要更新数组中指定的一项数据，这里需要先把前面和后面都切开。如果经常需要这类的操作，可以选择使用帮助类 React.addons.update，updeep，或者使用原生支持深度更新的库 Immutable。最后，时刻谨记永远不要在克隆 state 前修改它。\n4. 单一的庞大的reducer的拆分\n\t5. 这块设计也不好做，会让人疑惑\n\t6. 官方给的demo中直接按state的内容区分，我觉得这样做不好，如果后期有跨内容的情况，就比较奇怪了。官方给的combineReducers方案，也只是减少代码量，本质没有变化，state还是拆分处理，路由还是业务逻辑自己来做。\n\t7. **解决方案**：还是处理一整个state，可以按照约定写reducer类而不是方法，类里按照actionType建方法，架构自动路由并调用。\n\t8. 以前做java架构，路由一定是架构来调用的，目前感觉各大flux框架都是解决问题不彻底。\n1. 官方建议设计模式：顶层容器组件才对redux有依赖，组件间通过props来传递数据。按照这样设计还是没有解决组件间交互和数据传递的问题。官方react设计建议：react的设计建议：http://camsong.github.io/redux-in-chinese/docs/basics/UsageWithReact.htm\n2. 使用connect将state绑定到component。此处有些黑盒了。\n2. 异步action用来请求服务端数据,利用middleware增强createStore的dispatch后即支持。\n\n##Relay\n没有时间，没做研究\n##Marty\n没有时间，没做研究\n\n##结论\n\n开源架构封装的简单的flux会产生较多的冗余代码。\n\n开源架构封装的复杂的redux，其和RN绑定封装了一些东西，是一个黑盒，不易理解和维护。\n\n介于上述两者之间的开源架构reflux，文档较上述2个少，不知道其可持续性如何。如果一定要用开源架构的话，我觉得他稍加封装是一个较为推荐的选择。\n\n不是特复杂的程序【一般spa的程序会更复杂一些，而RN并不是spa】，这些概念只会增加你的开发难度，并且对后面维护的人要求更高。\n\n我们继续头脑风暴，继续抽象总结一下flux系列框架， flux系列框架干了什么，没干什么，针对开篇提出的问题。\n\n2. 【解决职责】flux系列框架都做到了解耦，分层，谁该干什么就干什么，不许干别的，让代码读起来更有预测性和一致性，方便维护\n3. 【解决通信】继续解耦，flux系列框架采用事件机制解决各层之间通信，采用props传递解决各组件之间通信。\n\n\n####事件系统是关键\n\nflux系列架构解决通信问题的方法是使用事件系统，事件系统中的回调函数是业务逻辑，redux是【store action reducer】，flux是【action dispacher store】。\n\n我们真的需要事件系统吗？\n\n事件系统的好处： \n\n1. 一个事件可以注册多个回调函数\n2. 各回调函数间没有耦合。\n\n关于1\n\n需要注册多个的这种情况并不多见，不信你去翻看你已经写好的代码，是不是大部分都是注册一个。\n\n关于2 \n\n解耦确实很彻底，但是当我需要控制执行顺序，需要等a执行完在执行b，怎么办？ok你可以先注册a在注册b啊。那a要是一个fetch或ajax操作呢？这时候只能乖乖的在a的请求结束回调函数中进行调用b了。又变成a依赖b了。当然，你可以继续dispatch(b)，这就没有耦合了。但是你要知道注册一个事件是要有成本的，要写action，而且这种dispatch的方式，真的不太适合人类的阅读，dispatch一下，下一步都有谁来执行都不知道，这哪有直接调用来的爽快。\n\n\n好吧说到这，最后的结论也出来了，不使用开源架构，借助其好的思想，替换其事件系统为面向对象结构，自行封装架构。\n\n\n## 架构设计\n\n再次强调：目前仅考虑如何应用于react native\n\n###先扣题，针对开篇问题的解决方案如下\n\n####交互\n1. 组件对外发布：组件对外只允许使用props来暴露功能，不允许使用接口及其它一切方式\n2. 父子组件间：组件的子组件通过父组件传递的接口来与父组件通信\n3. 兄弟组件间:\n\t4. 方案1：假设a要调用b，参考第一条的话，其实就是a要改变b的props，那么a只要改b的props的来源即可，b的props的来源一般就是根组件的state。那么根组件就要有组织和协调的能力。\n\t5. 方案2：利用事件机制，基本同flux架构。略复杂，且我们并不需要事件的特性，本架构设计不推荐。\t\n#### 职责\n\n1. root-存放state，组织子view组件，组织业务逻辑对象等\n2. 子view组件-根据this.props渲染view。\n3. 业务逻辑对象-提供业务逻辑方法\n\n\n\n根据以上推导，我将其命名为面向对象的ReactNative组件架构设计，它与flux系列架构的最大的不同之处在于，用业务逻辑对象来代替了【store action dispatcher】or【store reducer】的事件系统。业务逻辑对象就是一组对象，用面向对象的设计理念设计出的n个对象，其负责处理整个页面的业务逻辑。\n\n以上为推导过程，干货才开始。。。。\n\n### 面向对象的ReactNative组件\\页面架构设计\n\n 一个独立完整的组件\\页面一般由以下元素构成：\n \n1. root组件，1个， \n\t1. 负责初始化state\n\t2. 负责提供对外props列表\n\t2. 负责组合子view组件形成页面效果\n\t3. 负责注册业务逻辑对象提供的业务逻辑方法\n\t4. 负责管理业务逻辑对象\n1. view子组件，0-n个，\n\t1. 根据props进行视图的渲染\n1. 业务逻辑对象，0-n个，\n\t2. 提供业务逻辑方法 \n\n\t\t\n####root组件\nroot组件由以下元素组成：\n\n1. props-公有属性\n2. state-RN体系的状态,必须使用Immutable对象\n3. 私有属性\n4. 业务逻辑对象的引用-在componentWillMount中初始化\n4. 私有方法-以下划线开头，内部使用or传递给子组件使用\n5. 公有方法【不推荐】，子组件和外部组件都可以用，但不推荐用公有方法来对外发布功能，破坏了面向状态编程，尽可能的使用props来发布功能\n\n\n![root](media/root.png)\n\n\t\n\n####子view组件\n\n子view组件中包含：\n\n1. props-公有属性\n2. 私有属性-强烈不建议有，除非你能理解以下几点，建议放在父组件or业务逻辑对象中\n\t3. 绝对不允许和父组件的属性or状态有冗余。无论是显性冗余还是计算结果冗余，除非你能确定结算是性能的瓶颈。\n\t4. 此属性只有自己会用，父组件和兄弟组件不会使用，如果你不确定这点，请把这个组件放到父组件上，方便组件间通信\n3. 私有方法-仅作为渲染view的使用，不许有业务逻辑\n4. 公有方法【不推荐，理由同root组件】 \n\n![gsddfa](media/gsddfa.png)\n\n\n\t\n\n\t\n####业务逻辑对象\n业务逻辑对象由以下元素组成：\n\n3. root组件对象引用-this.root\n2. 构造器-初始化root对象，初始化私有属性\n2. 私有属性\n3. 公有方法-对外提供业务逻辑\n3. 私有方法-以下划线开头，内部使用\n\n\t\n![dafa](media/dafa.png)\n\n\n\n\t \n#### ps1：通用型组件只要求尽量满足上述架构设计\n\n通用型组件一般为不包含任何业务的纯技术组件，具有高复用价值、高定制性、通常不能直接使用需要代码定制等特点。\n\n可以说是一个系统的各个基础零件，比如一个蒙板效果，或者一个模态弹出框。\n\n架构的最终目的是保证系统整体结构良好，代码质量良好，易于维护。一般编写通用型组件的人也是经验较为丰富的工程师，代码质量会有保证。而且，作为零件的通用组件的使用场景和生命周期都和普通组件\\页面不同，所以，仅要求通用组件编写尽量满足架构设计即可。\n\n\n\n####ps2：view子组件复用问题\n\t\n抛出一个问题，设计的过程中，子组件是否需要复用？子组件是否需要复用会影响到组件设计。\n\t\n6. 需复用，只暴露props，可以内部自行管理state【尽量避免除非业务需要】\n7. 不需复用，只暴露props，内部无state【因为不会单独使用，不需要setState来触发渲染】\n \n其实， 一般按照不需复用的情况设计，除非复用很明确，但这时候应该抽出去，变成独立的组件存在就可以了，所以这个问题是不存在的。\n\t\n\t\n\t\n##适用场景分析\n\n###flux系列框架\n\nflux系列框架的适用场景我觉得应具有以下特点：\n\n**一个页面中组件较多，组件之间较为独立，但是重叠使用模型，模型的变化会影响很多组件的展现和行为。**\n\n比如，开发一个类似qq的聊天页面，左侧是联系人列表，右侧是与某人的消息对话框，当收到一个消息之后，1要刷新左侧联系人列表的最近联系人，2要右侧的消息对话框中显示这个消息，3要页面title要提示新消息。这就是典型的一个新消息到来事件【消息模型发生了变化】触发三个无关联的组件都有行为和展现的变化。如果用事件系统来开发就做到了解耦的极致，未来如果还要加入第4种处理也不用修改原来的逻辑，就直接注册一下就可以了，满足了开闭原则。\n\n**需要对app运行过程进行监控，数据采样等**\n\nflux系列框架是一个典型的门面模式，业务动作都要通过统一的门面dispatch进行，天生具有良好的监控解决方案。\n\n\n###面向对象的RN组件架构\n\n面向对象的RN组件架构的使用场景特点我没有总结出来，我觉得所有场景都可以用，只要你业务逻辑对象设计的好，都不是问题。\n\n还拿上面聊天界面举例子，面向对象的RN组件架构其实也可以解耦的写出写上述场景，你完全可以将业务逻辑对象之间的交互设计成一个小的事件系统，只是架构没有直接约束这种解耦，flux系列架构直接在架构中就强制编码人员做到了解耦，但是如果我不需要解耦的时候就相当于增加了复杂度，得不偿失了。\n\n所以面向对象的RN组件架构要更灵活，也更简单更容易让人理解，更容易预测代码的执行流向，但同时因为灵活对业务逻辑对象设计者的要求也较高，针对较为复杂or重要页面建议进行详细设计并leader检查来保证质量。\n\n\n##如何做监控\n\n因为面向对象的RN架构中去掉了统一的业务逻辑调用facade入口dispatch，那我们如何来做监控呢。\n\n###方案1：在需要监控的地方人为加入监控点。\n这个方案对业务代码和监控代码的耦合确实有点大，是最差的解决方案了。不推荐。\n###方案2：在基类BaseLogicObj的构造器中对对象的所有方法进行代理-todo待验证\n这个方案对业务代码透明，但是还只是个想法，未进行代码测试和验证。\n###方案3.....还没有想出别的方案，有没有同学给点思路？\n\n\n##架构之美\n\n最后在分享demo代码之前，摘抄了天猫前端架构师团队对架构的认识，个人觉得十分认同。\n\n简单：\n简单的东西才能长久，HTML、CSS和JavaScript之所以能够活到现在，而其他类似的很牛的方案都死掉了，原因之一是简单，才有那么多人用它，所以我们需要把技术和产品方案朝着简单的思路发展，简单才是本质，复杂一定是临时的会过时的。天猫的前端技术架构为什么都基于Kissy，为什么是两层架构，就是朝着简单的方式去思考。看起来简单，用起来简单。\n\n高效：\n简单是高效的前提，复杂的高效都是临时的会过时的，技术架构一定要提高团队的工作效率，否则一定会被抛弃，因此把简单的规则自动化，把精确的重复的事情让机器去做，前端这么多年为什么开发环境不够成熟，就是自动化的工具太少，前端又很少能力驾驭编写工具的语言，而Nodejs的出现是一个前所未有的机会。\n\n灵活：\n高效往往和灵活是对立的，就像移动上Native和Web的关系，而我们就需要思考如何做到两者兼顾，既高效又灵活，所以要不断把事情做简单，思考本质、看到本质，基于本质去实现。比如Apple为什么敢于把鼠标和键盘去掉，是因为确信人直接和界面打交道比借助一个中间硬件更能够表达人机交互的本质。\n\n新鲜：\n面向未来，前端需要不停地更新自己，无论是思想还是技术。比如整个天猫基于Kissy，那么就使用最新的Kissy版本，基础设施能够升级是一种能力，如果有一天基础设施升不了啦，那么这套技术架构就老去了。比如发现Gulp比Grunt更能够代表未来，那么我们毫不犹豫地整个团队开始进行升级。\n\n##完整demo代码\n\n此demo仿照redux提供的todolist demo编写。\n\nredux demo 地址：http://camsong.github.io/redux-in-chinese/docs/basics/ExampleTodoList.html\n\ndemo截图：\n\n![](media/14503398097842.jpg)\n\n \n\ntodolist页面：\n\n```\n\n\n'use strict'\n\n\nlet React=require('react-native');\nlet Immutable = require('immutable');\nvar BbtRN=require('../../../bbt-react-native');\n\n\nvar {\n    BaseLogicObj,\n    }=BbtRN;\n\n\nlet {\n    AppRegistry,\n    Component,\n    StyleSheet,\n    Text,\n    View,\n    Navigator,\n    TouchableHighlight,\n    TouchableOpacity,\n    Platform,\n    ListView,\n    TextInput,\n    ScrollView,\n    }=React;\n\n//root组件开始-----------------\n\nlet  Root =React.createClass({\n\n    //初始化模拟数据，\n    data:[{\n        name:'aaaaa',\n        completed:true,\n    },{\n        name:'bbbbb',\n        completed:false,\n    },{\n        name:'ccccc',\n        completed:false,\n    }\n    ,{\n        name:'ddddd',\n        completed:true,\n    }],\n\n\n    componentWillMount(){\n\n        //初始化业务逻辑对象\n        this.addTodoObj=new AddTodoObj(this);\n        this.todoListObj=new TodoListObj(this);\n        this.filterObj=new FilterObj(this);\n\n        //下面可以继续做一些组件初始化动作，比如请求数据等.\n        //当然了这些动作最好是业务逻辑对象提供的，这样root组件将非常干净.\n        //例如这样：this.todoListObj.queryData();\n    },\n\n\n    //状态初始化\n    getInitialState(){\n      return {\n          data:Immutable.fromJS(this.data),//模拟的初始化数据\n          todoName:'',//新任务的text\n          curFilter:'all',//过滤条件 all no ok\n      }\n    },\n\n\n\n    //这里组合子view组件 并 注册业务逻辑对象提供的方法到各个子view组件上\n    render(){\n\n        return (\n            <View style={{marginTop:40,flex:1}}>\n\n                <AddTodo todoName={this.state.todoName}\n                        changeText={this.addTodoObj.change.bind(this.addTodoObj)}\n                         pressAdd={this.addTodoObj.press.bind(this.addTodoObj)} />\n\n                <TodoList todos={this.state.data}\n                          onTodoPress={this.todoListObj.pressTodo.bind(this.todoListObj)} />\n\n                <Footer curFilter={this.state.curFilter}\n                    onFilterPress={this.filterObj.filter.bind(this.filterObj)} />\n\n            </View>\n        );\n    },\n\n\n\n});\n\n\n\n\n\n\n//业务逻辑对象开始-------------------------可以使用OO的设计方式设计成多个对象\n\n//业务逻辑对象要符合命名规范：以Obj结尾\n//BaseLogicObj是架构提供的基类，里面封装了构造器和一些常用取值函数\nclass AddTodoObj extends BaseLogicObj{\n\n    press(){\n        if(!this.getState().todoName)return;\n        let list=this.getState().data;\n        let todo=Immutable.fromJS({name:this.getState().todoName,completed:false,});\n        this.setState({data:list.push(todo),todoName:''});\n    }\n\n    change(e){\n        this.setState({todoName:e.nativeEvent.text});\n    }\n\n}\n\n\nclass TodoListObj extends BaseLogicObj {\n\n\n\n\n    pressTodo(todo){\n\n        let data=this.getState().data;\n\n        let i=data.indexOf(todo);\n\n        let todo2=todo.set('completed',!todo.get('completed'));\n\n        this.setState({data:data.set(i,todo2)});\n    }\n}\n\n\nclass FilterObj extends BaseLogicObj {\n\n\n    filter(type){\n\n        let data=this.getState().data.toJS();\n        if(type=='all'){\n            data.map((todo)=>{\n                todo.show=true;\n            });\n        }else if(type=='no'){\n            data.map((todo)=>{\n                if(todo.completed)todo.show=false;\n                else todo.show=true;\n             });\n        }else if(type=='ok'){\n            data.map((todo)=>{\n                if(todo.completed)todo.show=true;\n                else todo.show=false;\n            });\n        }\n\n\n        this.setState({curFilter:type,data:Immutable.fromJS(data)});\n    }\n\n\n\n}\n\n\n//view子组件开始---------------------------\n\n\n//子view对象中仅仅关注：从this.props转化成view\nlet Footer=React.createClass({\n\n    render(){\n\n        return (\n\n\n            <View style={{flexDirection:'row', justifyContent:'flex-end',marginBottom:10,}}>\n\n                <FooterBtn {...this.props} title='全部' name='all'  cur={this.props.curFilter=='all'?true:false} />\n                <FooterBtn {...this.props} title='未完成' name='no' cur={this.props.curFilter=='no'?true:false} />\n                <FooterBtn {...this.props} title='已完成' name='ok' cur={this.props.curFilter=='ok'?true:false} />\n\n            </View>\n\n\n\n        );\n    },\n\n\n});\n\n\nlet FooterBtn=React.createClass({\n\n    render(){\n\n        return (\n\n            <TouchableOpacity onPress={()=>this.props.onFilterPress(this.props.name)}\n                              style={[{padding:10,marginRight:10},this.props.cur?{backgroundColor:'green'}:null]} >\n                <Text style={[this.props.cur?{color:'fff'}:null]}>\n                    {this.props.title}\n                </Text>\n            </TouchableOpacity>\n\n        );\n    },\n\n\n});\n\n\nlet AddTodo=React.createClass({\n\n    render(){\n\n        return (\n\n\n            <View style={{flexDirection:'row', alignItems:'center'}}>\n\n\n                <TextInput value={this.props.todoName}\n                    onChange={this.props.changeText}\n                    style={{width:200,height:40,borderWidth:1,borderColor:'e5e5e5',margin:10,}}></TextInput>\n\n\n                <TouchableOpacity onPress={this.props.pressAdd}\n                    style={{backgroundColor:'green',padding:10}} >\n                    <Text style={{color:'fff'}} >\n                        添加任务\n                    </Text>\n                </TouchableOpacity>\n\n            </View>\n\n\n\n        );\n    },\n\n\n});\n\n\n\nlet Todo=React.createClass({\n\n    render(){\n        let todo=this.props.todo;\n        return (\n            todo.get(\"show\")!=false?\n            <TouchableOpacity  onPress={()=>this.props.onTodoPress(todo)}\n                style={{padding:10,borderBottomWidth:1,borderBottomColor:'#e5e5e5'}}>\n                <Text style={[todo.get('completed')==true?{textDecorationLine:'line-through',color:'#999'}:null]} >\n                    {todo.get('completed')==true?'已完成   ':'未完成   '} {todo.get('name')}\n                </Text>\n            </TouchableOpacity>\n             :null\n        );\n    },\n\n\n});\n\n\nlet TodoList=React.createClass({\n    render(){\n        return (\n            <ScrollView style={{flex:1}}>\n                {this.props.todos.reverse().map((todo, index) => <Todo {...this.props} todo={todo} key={index}  />)}\n            </ScrollView>\n        );\n    },\n});\n\n\n\n\nmodule.exports=Root;\n\n\n```\n\n业务逻辑对象基类BaseLogicObj:\n\n```\n\n\n'use strict'\n\nclass BaseLogicObj{\n\n\n    constructor(root){\n        if(!root){\n            console.error('实例化BaseLogicObj必须传入root组件对象.');\n        }\n        this.root=root;\n    }\n\n    getState(){\n        return this.root.state;\n    }\n\n    setState(s){\n        this.root.setState(s);\n    }\n\n    getRefs(){\n        return this.root.refs;\n    }\n\n    getProps(){\n        return this.root.props;\n    }\n\n}\n\nmodule.exports=BaseLogicObj;\n\n```\n\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative组件状态设计思考.md",
    "content": "# ReactNative组件状态设计思考\n\n\n这篇文章写的较早，是刚接触RN的时候进行的思考总结，是一个分析的过程，您还可以参考思想更成熟一点的这篇：RN组件架构设计：http://segmentfault.com/a/1190000004161358\n\n设计React组件与设计一个jquery组件或者一个原生js组件最大的区别就是状态的设计。\n\n##术语定义\n1. 属性 \n\t2. 泛指用户在初始化组件时候可以传给组件的\n\t3. 有些框架也叫options\n\t4. 是public的\n\t5. 在RN体系中，叫props，其中还包含了事件\n2. 事件\n\t3. 是public的\n\t4. 是function类型的\n\t5. 在RN体系中使用props来引用\n3. 接口\n\t4. 是public的\n\t5. 是function类型的\n\t6. 通过组件实例化之后的对象，可以进行调用\n4. 内部属性\n\t5. 是private的\n\t6. 传统设计方案中一般用于存储组件的状态，数据等\n\t6. RN体系中没有对其进行明确，可以自由设计 \n5. 状态\n\t6. RN体系中明确提出的概念 \n\t7. 传统设计方案中一般使用内部属性来表示\n\n\n##传统设计思路\n\n按照原来设计组件的方法论，一个UI组件对外应该具有属性、事件、接口，对内具有内部属性，内部接口。\n\n1. 属性就像一份配置文件，描述了组件应该具有的功能、外观或者初始状态。\n2. 事件是组件在工作工程中，当满足某种条件的时候触发的回调函数\n3. 接口是组件对象的方法，可以让组件去执行某个任务\n\n在原来的设计理念中，并没有提出组件状态的概念，但是其也是一直存在的，通常是使用组件私有属性来存储。\n\n比如，你设计一个按钮，那么他可能有正常状态，禁用状态，那么我们会设计一个属性disable={true|false}来通知组件初始化的具有的状态，设计2个接口disable、enable来赋予js有动态改变其状态的能力，设计2个事件onEnable、onDisable来通知回调函数组件状态发生了变化。伪代码如下：\n\n```\n//组件定义\nclass button{\n\n\tconstructor(disable,pid){//构造函数\n\t\n\t\tthis.disable=disable,//属性：组件初始化使用这个作为状态\n\t\tthis.pid=pid;//属性：父容器的id\n\t\t\n\t\tthis._disable=null,//状态:私有属性\n\t\tthis._el=null,//对html节点的引用，私有属性\n\n\t\tif(this.disable==true){//根据属性来决定初始化状态\n\t\t\tthis.disable();\n\t\t}else{\n\t\t\tthis.enable();\n\t\t}\n\t\tthis.render();//渲染组件\n\t}\n\t\t\n\t\n\tenable(){\n\t\tthis._disable=false;\n\t\tif(this._el)this._el.set('disable',false);\n\t\tthis.fireEvent('onEnable');//触发事件\n\t}\n\t\n\tdisable(){\n\t\tthis._disable=true;\n\t\tif(this._el)this._el.set('disable',true);\n\t\tthis.fireEvent('onDisable');//触发事件\n\t}\n\t\n\t\n\trender(){\n\t\t//渲染组件，状态直接影响了组件的表现和功能\n\t\t$(this.pid).innerHTML='<button disable='+this._disable+' />';\n\t\t\n\t\t//初始化对dom节点的引用\n\t\tthis._el=$(this.pid).getChild();\n\t}\n\t\n}\n\n//父容器\n<div id='a'></div>\n\n//实例化组件并使用\nvar btn = new button(false,'a');\n\n//调用btn提供的方法，此时会触发onEnable事件\nbtn.enable();\n```\n\n##RN设计思路\n\n上面的示例中，表示了一个传统UI组件的设计思路，_disable就是这个button组件的关键状态，它直接影响了组件的表现和行为。\n\n而react架构直接把组件状态提升到了一个新的高度，主要有以下几点：\n\n1. 使用固定的接口来声明组件状态和状态默认值\n2. 使用固定的接口来获得和改变组件状态的值\n2. 状态的改变一定会改变组件的表现，会导致组件的重新渲染\n\n前两项都不是问题，因为我们原来也得有这些东西，只不过写法发生了变化，关键是最后一项。`这里说的不是问题，指的是思路上比较容易接受。这种固定写法，问题也很明显，因为要想把一个状态从RN状态体系拿进拿出，会产生一定的工作成本，很是不爽。`\n\n我一直在思索，组件状态的变化一定要导致view的变化吗？\n\n答案肯定是no，因为有些状态仅仅影响组件的行为，并不影响表现。`比如说，我赋予按钮一个新功能，它可以提交某个表单的数据，也就是需要一个新的状态formName,他就不影响表现，只影响行为。`\n\n考虑到性能的极致，我们就只能把这种状态放到RN的状态体系之外，作为对象的私有属性存在。\n\nRN号称diff算法性能很高，但也不是0损耗，如果对RN的渲染理解很透彻，你当然也可以不这样设计，但是我这里还是倾向于保守一些，否则后期的调整也是有工作量的，因为定义、初始化、取值、改值这些操作代码都不一样。`写到这里我突然想到，如果可以，我们可以通过某种方法方便的修改某个状态是否影响表现，不用改其它代码的话，那么我们设计状态的时候就不用区分了，就能让我们更关注设计本身。`\n\n\n##结论\n\n说道这里，基本可以理清思路了。\n\n组件按照传统方式设计，该怎么设计就怎么设计，在设计原来组件内部属性的时候，多考虑一步，哪些内部属性的改变是影响表现的，哪些内部属性是不影响表现的。将影响表现的放入到RN状态体系中，将不影响表现的，放入内部属性中。即可。\n\n##tips\n1. 耦合性很强的父子组件，建议将状态统一放到父组件中，子组件中不要放状态了，无论是状态的跨组件共享，还是对view渲染的触发都很方便。除非你很确定某个状态只子组件内部使用，通常这种情况后边可能也会发生变化。\n2. 以下问题可以帮助识别是否是state，摘自官网\n\t3. 是否是从父级通过 props 传入的？如果是，可能不是 state 。\n\t1. 是否会随着时间改变？如果不是，可能不是 state 。\n\t1. 能根据组件中其它 state 数据或者 props 计算出来吗？如果是，就不是 state 。\n2. 根据state的特点进行识别，加入到上一条\n\t3. 属性的改变会影响到view的变化就可能是state\n2. 参考地址\n\t3. https://facebook.github.io/react/docs/thinking-in-react.html\n\t4. http://wiki.jikexueyuan.com/project/react/thinking-in-react.html\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "ReactNative开发指导/ReactNative调试菜单.md",
    "content": "# ReactNative调试菜单\n\n\n加上这2个就有rn调试菜单了\n\n![](media/14773029386169.jpg)\n\n\n"
  },
  {
    "path": "ReactNative开发指导/React的render优化框架.md",
    "content": "# React的render优化通用解决方案\n\n# 前言\n\n在使用react框架(无论是单独使用react还是reactnative)的过程中，性能都是我们不得不考虑的因素，虽然react使用虚拟dom比对的方式来避免了频繁且冗余的真实dom操作，但是，虚拟dom的生成和比对并不是0损耗，仍然会耗费性能，当生成dom的逻辑过于复杂，当数据量非常大，当dom的节点非常多，都会放大这个损耗，都会使应用的帧数不足导致卡顿。\n\n如何来降低这个损耗呢，让我们先来看一看官方提供的解决办法。\n\n# 官方建议方案--shouldComponentUpdate\n\n在说shouldComponentUpdate之前，先说一下setState的过程。\n\n## setState的过程\n\nsetState是react的关键函数，对state的修改，会触发组件及其所有子组件的重绘动作。整个过程大致如下：\n\n1. 组件A的state被改变了\n1. 执行组件A的render方法。\n2. 在组件A的render方法执行过程中，组件A的所有子组件也会执行render方法，子组件的子自己也会执行，以此类推，这是一个递归动作。\n3. 当所有子组件、孙子组件等所有的后代组件都执行完render方法，react引擎就获得了组件A的虚拟dom。\n4. react引擎使用组件A的虚拟dom和组件A的真实dom做diff\n5. react引擎根据diff的结果去增量更新真实dom，这样一次setState的动作就完成了。\n\n## shouldComponentUpdate\n\n官方提供了shouldComponentUpdate生命周期事件来使业务代码获得控制组件是否需要执行render方法，整个setState过程变成如下：\n\n1. 组件A的state被改变了\n1. 执行组件的shouldComponentUpdate方法，如果返回false，就终止后续所有操作\n1. 执行组件A的render方法。\n2. 在组件A的render方法执行过程中，组件A的所有子组件也会执行render方法，子组件的子自己也会执行，以此类推，这是一个递归动作。\n3. 当所有子组件、孙子组件等所有的后代组件都执行完render方法，react引擎就获得了组件A的虚拟dom。\n4. react引擎使用组件A的虚拟dom和组件A的真实dom做diff\n5. react引擎根据diff的结果去增量更新真实dom，这样一次setState的动作就完成了。\n\n\n# 官方方案带来的问题\n\nshouldComponentUpdate方法将react频繁的render动作转变成频繁的state和props的比对动作，但是因为shouldComponentUpdate方法会被频繁的调用，如果实现的不佳，性能也会出现问题。\n\n那么如何优化shouldComponentUpdate方法，就成为了我们的关键路径问题.\n\n# shouldComponentUpdate优化方案1----不可变数据类型\n\n这是官方提供的推荐方案，确实比对的性能非常高，但是也有2个缺点，1是会创建更多的对象，占用了更多的内存，但是也还好，属于可接受范围内，2是改变了我们使用对象的原有方式和思路，习惯性的使用原有的操作对象的方法，就会产生bug，而且很难排查。\n\n# shouldComponentUpdate优化方案2----浅比对+forceUpdate\n\n首先设计state和props对象要尽量使用简单数据结构，也就是一层的对象结构。\n\n然后在组件基类对象中实现默认的shouldComponentUpdate实现，并使用浅比对的方式比对state和props。\n\n所有的组建都继承这个组件基类。\n\n当不得不使用了复杂对象结构的时候，使用forceUpdate或者自行实现shouldComponentUpdate方法来解决。\n\n这个方案的好处是，使用了折中的方式来解决这个问题，大部分情况使用浅比对，小部分情况使用forceUpdate；浅比对的性能虽然没有不可变数据类型好，但也不是很差，完全可以接受，而且没有引入颠倒开发思维的不可变数据类型，让出bug的几率降低，在取舍中达到一个比较令人满意的点。\n\n\n\n[评论直达连接](https://github.com/cnsnake11/blog/issues/23)\n\n\n\n\n\n\n"
  },
  {
    "path": "ReactNative开发指导/已有工程集成ReactNaitve-IOS.md",
    "content": "# 已有工程集成ReactNative-IOS\n\n以下步骤为手动添加的方式，使用rnpm的方式请参考官方文档。\n\n本文是对官方文档的一个补充，大部分内容来源于官网文档。\n\n官方参考地址： http://facebook.github.io/react-native/docs/linking-libraries-ios.html#content\n\n中文参考地址：http://reactnative.cn/docs/linking-libraries-ios.html#content\n\n#第一步：添加.xcodeproj文件\n把需要的.xcodeproj文件，拖到你的XCode工程下（通常拖到XCode的Libraries分组里）\n\n\n![](media/14519905288921.jpg)\n\n\nreact-native需要集成的.xcodeproj文件清单如下：\n\n```\nnode_modules/react-native/React/React.xcodeproj\nnode_modules/react-native/Libraries/Image/RCTImage.xcodeproj\nnode_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj\nnode_modules/react-native/Libraries/Text/RCTText.xcodeproj\nnode_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj\nnode_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj\nnode_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj\nnode_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj\n\n```\n\n#第二步添加.a文件\n\n点击你的主工程文件，选择Build Phases，然后把刚才所添加进去的.xcodeproj下的Products文件夹中的静态库文件（.a文件），拖到Link Binary With Libraries组内。\n\n\n![](media/14519905532406.jpg)\n\n\n#第三步注册头文件路径-不是所有依赖都需要，注册React.xcodeproj即可\n\n需要在原生代码中使用这个库，还是只需要通过JavaScript访问？\n如果你只需要通过JavaScript访问这个库，你就可以跳过这步了。\n\n\n打开你的工程文件，选择Build Settings，然后搜索Header Search Paths，然后添加库所在的目录（如果它还有像React这样的子目录需要包含，注意要选中recursive选项）\n\n![](media/14519906182589.jpg)\n\n\n注意：React.xcodeproj是需要注册的。下面的目录要改成自己的目录。\n\n$(SRCROOT)/RN/node_modules/react-native/React  \n\nrecursive\n\n\n#报错： App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure\n\n报错原因是需要开启内网http的访问权限。\n\n1. 在Info.plist中添加 NSAppTransportSecurity 类型 Dictionary ;\n1. 在 NSAppTransportSecurity 下添加 NSAllowsArbitraryLoads 类型Boolean ,值设为 YES;\n\n参考地址：\n\nhttp://www.cnblogs.com/chglog/p/4746683.html\n\n\n#报错：unrecognized selector sent to instance\n\n解决方案：\n\nBuild Settings -> other linker flags -> -ObjC\n\n\n参考地址1：https://github.com/facebook/react-native/issues/2396\n\n参考地址2：http://stackoverflow.com/questions/32775481/rctbatchedbridge-perfstats-unrecognized-selector-sent-to-instance\n\n\n#报错：RCTStatusBarManager module requires that the UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO\n\n错误信息说的很清楚了。\n\n在Info.plist中添加View controller-based status bar appearance为NO。\n\n\n#关闭自动启动调试服务器\n\n在React.xcodeproj的Build Phases中的第一个（有8081那个）Run Script。用    <<!   代码代码代码  ！  注释掉即可。\n\n![123213123](media/123213123.png)\n\n\n"
  },
  {
    "path": "ReactNative开发指导/淘宝d2分享-ReactNative变革无线前端.md",
    "content": "#ReactNative变革无线前端-淘宝d2分享\n\n![淘宝-ReactNative变革无线前端 49-1](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2049-1.jpeg)\n![淘宝-ReactNative变革无线前端 50-2](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2050-2.jpeg)\n![淘宝-ReactNative变革无线前端 51-3](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2051-3.jpeg)\n![淘宝-ReactNative变革无线前端 52-4](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2052-4.jpeg)\n![淘宝-ReactNative变革无线前端 53-5](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2053-5.jpeg)\n![淘宝-ReactNative变革无线前端 54-6](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2054-6.jpeg)\n![淘宝-ReactNative变革无线前端 55-7](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2055-7.jpeg)\n![淘宝-ReactNative变革无线前端 56-8](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2056-8.jpeg)\n![淘宝-ReactNative变革无线前端 57-9](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2057-9.jpeg)\n![淘宝-ReactNative变革无线前端 58-10](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2058-10.jpeg)\n![淘宝-ReactNative变革无线前端 59-11](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2059-11.jpeg)\n![淘宝-ReactNative变革无线前端 60-12](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2060-12.jpeg)\n![淘宝-ReactNative变革无线前端 61-13](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2061-13.jpeg)\n![淘宝-ReactNative变革无线前端 62-14](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2062-14.jpeg)\n![淘宝-ReactNative变革无线前端 63-15](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2063-15.jpeg)\n![淘宝-ReactNative变革无线前端 64-16](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2064-16.jpeg)\n![淘宝-ReactNative变革无线前端 65-17](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2065-17.jpeg)\n![淘宝-ReactNative变革无线前端 66-18](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2066-18.jpeg)\n![淘宝-ReactNative变革无线前端 67-19](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2067-19.jpeg)\n![淘宝-ReactNative变革无线前端 68-20](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2068-20.jpeg)\n![淘宝-ReactNative变革无线前端 69-21](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2069-21.jpeg)\n![淘宝-ReactNative变革无线前端 70-22](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2070-22.jpeg)\n![淘宝-ReactNative变革无线前端 71-23](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2071-23.jpeg)\n![淘宝-ReactNative变革无线前端 72-24](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2072-24.jpeg)\n![淘宝-ReactNative变革无线前端 73-25](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2073-25.jpeg)\n![淘宝-ReactNative变革无线前端 74-26](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2074-26.jpeg)\n![淘宝-ReactNative变革无线前端 75-27](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2075-27.jpeg)\n![淘宝-ReactNative变革无线前端 76-28](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2076-28.jpeg)\n![淘宝-ReactNative变革无线前端 77-29](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2077-29.jpeg)\n![淘宝-ReactNative变革无线前端 78-30](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2078-30.jpeg)\n![淘宝-ReactNative变革无线前端 79-31](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2079-31.jpeg)\n![淘宝-ReactNative变革无线前端 80-32](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2080-32.jpeg)\n![淘宝-ReactNative变革无线前端 81-33](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2081-33.jpeg)\n![淘宝-ReactNative变革无线前端 82-34](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2082-34.jpeg)\n![淘宝-ReactNative变革无线前端 83-35](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2083-35.jpeg)\n![淘宝-ReactNative变革无线前端 84-36](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2084-36.jpeg)\n![淘宝-ReactNative变革无线前端 85-37](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2085-37.jpeg)\n![淘宝-ReactNative变革无线前端 86-38](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2086-38.jpeg)\n![淘宝-ReactNative变革无线前端 87-39](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2087-39.jpeg)\n![淘宝-ReactNative变革无线前端 88-40](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2088-40.jpeg)\n![淘宝-ReactNative变革无线前端 89-41](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2089-41.jpeg)\n![淘宝-ReactNative变革无线前端 90-42](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2090-42.jpeg)\n![淘宝-ReactNative变革无线前端 91-43](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2091-43.jpeg)\n![淘宝-ReactNative变革无线前端 92-44](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2092-44.jpeg)\n![淘宝-ReactNative变革无线前端 93-45](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2093-45.jpeg)\n![淘宝-ReactNative变革无线前端 94-46](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2094-46.jpeg)\n![淘宝-ReactNative变革无线前端 95-47](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2095-47.jpeg)\n\n![淘宝-ReactNative变革无线前端 96-48](media/%E6%B7%98%E5%AE%9D-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF%2096-48.jpeg)\n\n"
  },
  {
    "path": "ReactNative开发规范/目录与命名规范.md",
    "content": "#ReactNative目录与命名规范 \n\n##RN项目结构规划如下：\n![2015-12-22 11.47.01](media/2015-12-2211.47.01.png)\n\n\n##业务模块目录与命名规范\n\n###业务模块目录规范\n![015-12-22 14.22.04](media/015-12-2214.22.04.png)\n\n\n\n###业务模块文件规范\n\n\n![2015-12-22 14.25.19](media/2015-12-2214.25.19.png)\n\n\n\n##基础框架目录与命名规范\n\n![2015-12-22 14.28.32](media/2015-12-2214.28.32.png)\n\n\n##总结\n\n1. 扩展名统一为.js\n1. 样式文件要如下格式：组件名称.css.js\n1. 紧挨着文件的文件夹名，直接使用组件/页面名称命名\n1. 组件or页面的首字母要大写,驼峰命名\n2. 如果同一个文件夹下有同名而不同作用的js文件，则通过中缀（小写）进一步区分，例如：HomeView.component.js,HomeView.css.js,HomeView.action.js等\n1. img文件夹可选\n\n\n\n\n\n\n"
  },
  {
    "path": "ReactNative开发规范/组件、页面结构规范.md",
    "content": "#ReactNative组件/页面结构规范\n\n## 业务页面/组件结构规范\n\n 一个独立完整的组件\\页面一般由以下元素构成：\n \n1. root组件，1个， \n\t1. 负责初始化state\n\t2. 负责提供对外props列表\n\t2. 负责组合子view组件形成页面效果\n\t3. 负责注册业务逻辑对象提供的业务逻辑方法\n\t4. 负责管理业务逻辑对象\n1. view子组件，0-n个，\n\t1. 根据props进行视图的渲染\n1. 业务逻辑对象，0-n个，\n\t2. 提供业务逻辑方法 \n\n\t\t\n##root组件结构\nroot组件由以下元素组成：\n\n1. props-公有属性\n2. state-RN体系的状态,必须使用Immutable对象\n3. 私有属性\n4. 业务逻辑对象的引用-在componentWillMount中初始化\n4. 私有方法-以下划线开头，内部使用or传递给子组件使用\n5. 公有方法【不推荐】，子组件和外部组件都可以用，但不推荐用公有方法来对外发布功能，破坏了面向状态编程，尽可能的使用props来发布功能\n\n\n![afasdfsa](media/afasdfsa.png)\n\n\n##子view组件结构\n\n子view组件中包含：\n\n1. props-公有属性\n2. 私有属性-强烈不建议有，除非你能理解以下几点，建议放在父组件or业务逻辑对象中\n\t3. 绝对不允许和父组件的属性or状态有冗余。无论是显性冗余还是计算结果冗余，除非你能确定结算是性能的瓶颈。\n\t4. 此属性只有自己会用，父组件和兄弟组件不会使用，如果你不确定这点，请把这个组件放到父组件上，方便组件间通信\n3. 私有方法-仅作为渲染view的使用，不许有业务逻辑\n4. 公有方法【不推荐，理由同root组件】 \n\n![](media/14507661283304.jpg)\n\n\t\n\n\t\n##业务逻辑对象结构\n业务逻辑对象由以下元素组成：\n\n3. root组件对象引用-this.root\n2. 构造器-初始化root对象，初始化私有属性\n2. 私有属性\n3. 公有方法-对外提供业务逻辑\n3. 私有方法-以下划线开头，内部使用\n\n\t\n![](media/14507661365142.jpg)\n\n####注意：定义root组件的state的时候，如果使用es6的方式，要把state的初始化放到componentWillMount中，如果在构造器中this.props为空。\n\t\n\t\n##通用组件结构\n\t\n参考业务组件结构，尽量遵守，灵活调整。\n\t\nPropTypes ，必须要有且必须要写注释。\n\n公用方法不推荐有，理由同业务开发规范，如果有，要求写注释。\n\n\n\n\n## 组件方法声明的顺序\n\n  - 原则上按如下顺序排列React组件的各个方法（生命周期）：\n  \n  1. constructor\n  1. 静态方法（static methods)\n  1. 生命周期事件\n  1. render\n\n## 按如下方式定义propTypes, defaultProps, contextTypes等  \n\n  ```javascript\n  import React, { Component, PropTypes } from 'react';\n  \n  const propTypes = {\n    id: PropTypes.number.isRequired,\n    url: PropTypes.string.isRequired,\n    text: PropTypes.string,\n  };\n  \n  const defaultProps = {\n    text: 'Hello World',\n  };\n  \n  class Link extends Component {\n    static methodsAreOk() {\n      return true;\n    }\n  \n    render() {\n      return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>\n    }\n  }\n  \n  Link.propTypes = propTypes;\n  Link.defaultProps = defaultProps;\n  \n  export default Link;\n  ```\n\n\n\n\n\n##相关参考地址\n\nhttps://github.com/cnsnake11/blog/blob/master/ReactNative开发指导/ReactNative的架构设计.md\n\nhttps://github.com/cnsnake11/blog/blob/master/ReactNative开发指导/ReactNative组件状态设计思考.md\n\n"
  },
  {
    "path": "ReactNative开发规范/编码规范.md",
    "content": "#ReactNative编码规范\n\n\n##组件引入\n\n使用import\n\n尽量用解构赋值React和BbtReact\n\n*.css.js文件的引入，直接用css命名\n\n```javascript\n// bad\nimport Footer from './Component/Footer/FooterView'\n\n// good\nimport Footer from './Component/Footer/Footer'\n\n//good\nimport HomeView from './HomeView';\n\n//good\nimport css from './HomeView.css';\n\n//good\nimport * as HomeViewAction from './HomeView.action';\n\n//good\nimport React,{ListView,Text,View}  from 'react-native';\n\n\n```\n\n## 组件声明\n 使用class与extends关键字。不使用React.createClass方法。需要导出的组件直接在class关键字前使用export default。\n\n注意export default当前版本RN（0.17.0）还不支持，需要等下一版本修复。\n\n    ```javascript\n    // bad\n    export default React.createClass({\n    });\n\n    // good\n    export default class HomeView extends React.Component {\n    }\n    ```\n\n##业务逻辑类创建\n使用 class AaaObj extends BaseLogicObj 的方式创建\n\n首字母大写，以Obj结尾\n\n## 方法\n   下划线开头为私有方法，否则为public方法\n\n **todo es6 规范 babel flow**\n \n##样式\n\n一般情况建议使用行内样式\n\n用style={[]}来定义样式，方便后期添加新样式\n\n如果有复用，抽取为样式类，一般使用css来命名，例如var css=StyleSheet.create()\n\n如果复用的样式较多，请将样式抽取到单独文件：组件名.css.js\n\n**todo 使用var style代替var css 语义更准确;组件名.style.js**\n\n##页面初始化\n\n使用交互管理器保证转场动画流畅，页面直接显示loading和少量的不变组件比如header等，其余的渲染动作放到转场动画结束后执行。\n\n如果页面有初始化数据，请立即执行请求，不要等转场动画结束执行。\n\n##与服务器交互\n统一使用fetch方法\n\n##图片\n静态图直接打包入app，使用相对路径的方式引入，source={require('相对路径')}\n\n注意：服务器上的图注意必须要有高宽才能显示出来\n\n##盒子模型属性\n\n一定要尽可能的在view组件上写盒子模型属性,虽然有可能会造成标签冗余，但是不会出各种诡异问题。\n\ntext元素就不要写盒子模型属性，支持的很诡异\n\n\n##touch*组件\n\nonpress事件只能加在touch*组件上\n\n一般不使用TouchHighlight，优先使用TouchOpacity\n\ntouch*一般包在view外面，如要加padding在view上，加margin加在touch*上\n\n\n##jsx\n\n不能写if else，可以用条件表达式代替\n\n经常用array.map方式来渲染数组\n\t\n参考地址：http://segmentfault.com/a/1190000003748270\n\n##定时任务\n不允许使用window.setTimeout和window.setInterval\n\n要求使用TimerMixin\n\n参考地址：https://github.com/cnsnake11/blog/blob/master/ReactNative翻译/react-native的定时器.md\n\n\n\n\n## 对齐\n   按下面的案例对齐：\n\n    ```javascript\n    // bad\n    <Foo superLongParam=\"bar\"\n         anotherSuperLongParam=\"baz\" />\n\n    // good\n    <Foo\n      superLongParam=\"bar\"\n      anotherSuperLongParam=\"baz\"\n    />\n\n    // 如果一行能摆下props，那就摆在一行\n    <Foo bar=\"bar\" />\n\n    // 子组件照常缩进\n    <Foo\n      superLongParam=\"bar\"\n      anotherSuperLongParam=\"baz\"\n    >\n      <Spazz />\n    </Foo>\n    ```\n\n## state/props\n  - 对于多个单词组成的props，使用驼峰命名法。不使用下划线或连接线。\n    ```javascript\n    // bad\n    <Foo\n      UserName=\"hello\"\n      phone_number={12345678}\n    />\n\n    // good\n    <Foo\n      userName=\"hello\"\n      phoneNumber={12345678}\n    />\n    ```\n  - 读取state和props时，使用const与解构，必要时可使用let。不使用var。\n    ```javascript\n    // bad\n    var userName = this.props.userName;\n    let checked = this.state.checked;\n\n    // good\n    const { userName, age, sex } = this.props;\n    const { checked } = this.state;\n    ```  \n    \n## 括号\n  - 当JSX标签超过一行时，使用括号包裹。\n    ```javascript\n    /// bad\n    render() {\n      return <MyComponent className=\"long body\" foo=\"bar\">\n               <MyChild />\n             </MyComponent>;\n    }\n\n    // good\n    render() {\n      return (\n        <MyComponent className=\"long body\" foo=\"bar\">\n          <MyChild />\n        </MyComponent>\n      );\n    }\n\n    // good, when single line\n    render() {\n      const body = <div>hello</div>;\n      return <MyComponent>{body}</MyComponent>;\n    }\n    ```\n\n## 标签\n  - 对于没有子组件的JSX标签，始终自闭合。\n    ```javascript\n    // bad\n    <Foo className=\"stuff\"></Foo>\n\n    // good\n    <Foo className=\"stuff\" />\n    ```\n\n  - 如果组件有多行属性，则另起一行进行自闭合。\n    ```javascript\n    // bad\n    <Foo\n      bar=\"bar\"\n      baz=\"baz\" />\n\n    // good\n    <Foo\n      bar=\"bar\"\n      baz=\"baz\"\n    />\n    ```\n\n"
  },
  {
    "path": "ReactNative翻译/React Advanced Performance.md",
    "content": "# React Advanced Performance 性能进阶\n\n英文原文地址：https://facebook.github.io/react/docs/advanced-performance.html\n\nOne of the first questions people ask when considering React for a project is whether their application will be as fast and responsive as an equivalent non-React version. The idea of re-rendering an entire subtree of components in response to every state change makes people wonder whether this process negatively impacts performance. React uses several clever techniques to minimize the number of costly DOM operations required to update the UI.\n\n是否选择使用react，最常考虑的一点就是react的性能。每一次state的改变都会触发整个组件树的渲染，这种机制对性能的影响一定存在。react提供了几种办法来最小化这种影响。\n\n#Use the production build 使用生产模式\n\nIf you're benchmarking or experiencing performance problems in your React apps, make sure you're testing with the minified production build. The development build includes extra warnings that are helpful when building your apps, but it is slower due to the extra bookkeeping it does.\n\n如果你遇到了性能瓶颈，首先确保你使用了压缩过的生产模式版本。开发模式版本提供了更多的警告和提示信息，这会耗费一定的性能。\n\n#Avoiding reconciling the DOM 避免dom重绘\n\nReact makes use of a virtual DOM, which is a descriptor of a DOM subtree rendered in the browser. This parallel representation allows React to avoid creating DOM nodes and accessing existing ones, which is slower than operations on JavaScript objects. When a component's props or state change, React decides whether an actual DOM update is necessary by constructing a new virtual DOM and comparing it to the old one. Only in the case they are not equal, will React reconcile the DOM, applying as few mutations as possible.\n\nreact使用了虚拟dom，它就是浏览器中dom树的一个镜像。所以，react可以避免直接操作真实dom，提升了速度。当组件的props或state发生了变化，react会去比对虚拟dom和真实dom的差异。当他们不一致，react才会去修改真实dom，并且只针对不一致的地方进行增量修改。【译者注：虚拟dom比较和真实dom的修改都属于react重绘的过程】\n\nOn top of this, React provides a component lifecycle function, shouldComponentUpdate, which is triggered before the re-rendering process starts (virtual DOM comparison and possible eventual DOM reconciliation), giving the developer the ability to short circuit this process. The default implementation of this function returns true, leaving React to perform the update:\n\n基于上述原理，react提供了一个组件生命周期事件，shouldComponentUpdate，当重绘开始之前会触发（包括虚拟dom比较和真实dom修改之前），使得开发者可以精确的控制是否需要执行重绘。此事件的默认实现返回true，所以，默认情况下，react每次都会执行重绘的算法。\n\n```\nshouldComponentUpdate: function(nextProps, nextState) { // 默认实现\n  return true;\n}\n```\n\nKeep in mind that React will invoke this function pretty often, so the implementation has to be fast.\n\n注意，react会非常频繁的执行这个事件，所以要保证你的实现性能极佳。\n\nSay you have a messaging application with several chat threads. Suppose only one of the threads has changed. If we implement shouldComponentUpdate on the ChatThread component, React can skip the rendering step for the other threads:\n\n假如你开发了一个聊天程序，里面会有很多的对话线程。当其中一个对话线程发生了变化。如果对话线程实现了shouldComponentUpdate，react可以取消掉其余没有改变的对话线程的重绘。\n\n```\nshouldComponentUpdate: function(nextProps, nextState) {\n  // TODO: return whether or not current chat thread is\n  // different to former one.\n  \n  // todo: 当前对话线程如果发生了改变返回true，否则返回false\n}\n```\n\nSo, in summary, React avoids carrying out expensive DOM operations required to reconcile subtrees of the DOM by allowing the user to short circuit the process using shouldComponentUpdate, and, for those which should update, by comparing virtual DOMs.\n\n总结，react利用开发者实现的shouldComponentUpdate事件来避免不必要的重绘过程，当需要真实dom变化的时候，通过与虚拟dom的比对，对真实dom进行增量修改。\n\n#shouldComponentUpdate in action 如何使用shouldComponentUpdate\n\nHere's a subtree of components. For each one is indicated what shouldComponentUpdate returned and whether or not the virtual DOMs were equivalent. Finally, the circle's color indicates whether the component had to be reconciled or not.\n\n下图是一个组件树。每一个组件都标识出了shouldComponentUpdate的返回值、虚拟dom是否相等2个状态。节点的颜色表明了是否需要修改真实dom。\n\n![](media/14720341503791.jpg)\n\n绿色scu - shouldComponentUpdate返回true\n\n红色scu - shouldComponentUpdate返回false\n\n绿色vDOMEq - 虚拟dom和真实dom一致\n\n红色vDOMEq - 虚拟dom和真实dom不一致\n\n绿色节点 - 无需修改真实dom\n\n红色节点 - 需修改真实dom\n\nIn the example above, since shouldComponentUpdate returned false for the subtree rooted at C2, React had no need to generate the new virtual DOM, and therefore, it neither needed to reconcile the DOM. Note that React didn't even have to invoke shouldComponentUpdate on C4 and C5.\n\n上面的例子中，如果shouldComponentUpdate返回false就像c2，react连c2及其子节点的虚拟dom都不会生成，当然，他们的真实dom节点也不会被修改。同时，c2的子节点c4和c5的shouldComponentUpdate都不会被触发。\n\nFor C1 and C3 shouldComponentUpdate returned true, so React had to go down to the leaves and check them. For C6 it returned true; since the virtual DOMs weren't equivalent it had to reconcile the DOM. The last interesting case is C8. For this node React had to compute the virtual DOM, but since it was equal to the old one, it didn't have to reconcile it's DOM.\n\n当shouldComponentUpdate返回true就像c1和c3，react会继续询问他们的子组件的shouldComponentUpdate。c6返回了true，同时c6的虚拟dom和真实dom不一致，所以c6执行了真实dom增量修改操作。c8比较有趣，它的真实dom和虚拟dom节点是相等的，所以他不会执行真实dom增量修改动作。\n\nNote that React only had to do DOM mutations for C6, which was inevitable. For C8, it bailed out by comparing the virtual DOMs, and for C2's subtree and C7, it didn't even have to compute the virtual DOM as we bailed out on shouldComponentUpdate.\n\n上述例子中，真正执行了真实dom修改动作的只有c6；c8执行了真实dom和虚拟dom比较的动作；c2和c7因为他们shouldComponentUpdate返回了false，他们甚至都没有进行虚拟dom的计算。\n\nSo, how should we implement shouldComponentUpdate? Say that you have a component that just renders a string value:\n\n综上，该如何实现shouldComponentUpdate方法呢？假如写一个显示一个字符串的组件。\n\n```\nReact.createClass({\n  propTypes: {\n    value: React.PropTypes.string.isRequired\n  },\n\n  render: function() {\n    return <div>{this.props.value}</div>;\n  }\n});\n```\n\nWe could easily implement shouldComponentUpdate as follows:\nshouldComponentUpdate如下：\n\n```\nshouldComponentUpdate: function(nextProps, nextState) {\n  return this.props.value !== nextProps.value;\n}\n```\n\nSo far so good, dealing with such simple props/state structures is easy. We could even generalize an implementation based on shallow equality and mix it into components. In fact, React already provides such implementation: PureRenderMixin.\n\n针对简单数据结构的props/state很容易。我们可以实现一个通用的方法来针对简单数据类型进行比对工作。并且react已经实现好了：PureRenderMixin。\n\nBut what if your components' props or state are mutable data structures? Say the prop the component receives, instead of being a string like 'bar', is a JavaScript object that contains a string such as, { foo: 'bar' }:\n\n当props和state是复杂数据类型该如何处理呢?假如上个组件要显示的字符串使用了一个js对象中的一个属性，例如{ foo: 'bar' }。\n\n```\nReact.createClass({\n  propTypes: {\n    value: React.PropTypes.object.isRequired\n  },\n\n  render: function() {\n    return <div>{this.props.value.foo}</div>;\n  }\n});\n```\n\nThe implementation of shouldComponentUpdate we had before wouldn't always work as expected:\n\n我们之前实现的shouldComponentUpdate将不会发生作用。\n\n```\n// assume this.props.value is { foo: 'bar' }\n// assume nextProps.value is { foo: 'bar' },\n// but this reference is different to this.props.value 因为引用不同，所以不相等\nthis.props.value !== nextProps.value; // true\n```\n\nThe problem is shouldComponentUpdate will return true when the prop actually didn't change. To fix this, we could come up with this alternative implementation:\n\n上面例子里props没有改变shouldComponentUpdate也返回了true，我们换一种实现方式如下：\n\n```\nshouldComponentUpdate: function(nextProps, nextState) {\n  return this.props.value.foo !== nextProps.value.foo;\n}\n```\n\nBasically, we ended up doing a deep comparison to make sure we properly track changes. In terms of performance, this approach is pretty expensive. It doesn't scale as we would have to write different deep equality code for each model. On top of that, it might not even work if we don't carefully manage object references. Say this component is used by a parent:\n\n原则上来讲，为了保证判断的准确性，必须要进行深比对。从性能的角度考虑，这种方式的损耗较大。而且可能会根据不同的模型写不同的深比对代码，具体的损耗不太好估量。更重要的是，如果对象引用关系处理的不好，会产生bug。比如下面这个父子组件通信的例子：\n\n```\nReact.createClass({\n  getInitialState: function() {\n    return { value: { foo: 'bar' } };\n  },\n\n  onClick: function() {\n    var value = this.state.value;\n    value.foo += 'bar'; // ANTI-PATTERN! 反模式！\n    this.setState({ value: value });\n  },\n\n  render: function() {\n    return (\n      <div>\n        <InnerComponent value={this.state.value} />\n        <a onClick={this.onClick}>Click me</a>\n      </div>\n    );\n  }\n});\n```\n\nThe first time the inner component gets rendered, it will have { foo: 'bar' } as the value prop. If the user clicks on the anchor, the parent component's state will get updated to { value: { foo: 'barbar' } }, triggering the re-rendering process of the inner component, which will receive { foo: 'barbar' } as the new value for the prop.\n\n子组件第一次渲染的时候，属性value的值是{ foo: 'bar'}。如果用户点击了锚点，父组件的state会变成{ value: { foo: 'barbar' } }，同时会触发子组件的渲染过程，子组件会的属性value也会同时变成{ foo: 'barbar' }。\n\nThe problem is that since the parent and inner components share a reference to the same object, when the object gets mutated on line 2 of the onClick function, the prop the inner component had will change. So, when the re-rendering process starts, and shouldComponentUpdate gets invoked, this.props.value.foo will be equal to nextProps.value.foo, because in fact, this.props.value references the same object as nextProps.value.\n\n现在的问题是，因为父组件和子组件都依赖了同一个对象引用，当这个对象在click方法中的第二行被改变了的时候，此时还没有setState，子组件依赖的对象也就立刻发生了变化，因为是同一个对象引用。所以，当渲染过程开始的时候，shouldComponentUpdate先被触发，this.props.value.foo和nextProps.value.foo是相等的，因为，实际上this.props.value和nextProps.value都引用了同一个对象。\n\nConsequently, since we'll miss the change on the prop and short circuit the re-rendering process, the UI won't get updated from 'bar' to 'barbar'.\n\n所以，我们就错过了属性值的改变和重新渲染的过程，页面也就不会从bar变成barbar。\n\n#Immutable-js to the rescue 不可变数据类型的解决方案\n\nImmutable-js is a JavaScript collections library written by Lee Byron, which Facebook recently open-sourced. It provides immutable persistent collections via structural sharing. Let's see what these properties mean:\n\nImmutable-js是一个开源的js的集合库，作者是facebook的Lee Byron。他提供了可以通过结构共享的不可变、持久化集合类型。具体描述如下：\n\n1. Immutable: once created, a collection cannot be altered at another point in time.\n2. Persistent: new collections can be created from a previous collection and a mutation such as set. The original collection is still valid after the new collection is created.\n3. Structural Sharing: new collections are created using as much of the same structure as the original collection as possible, reducing copying to a minimum to achieve space efficiency and acceptable performance. If the new collection is equal to the original, the original is often returned.\n\n1. 不可变：集合类型一旦创建，就不会改变。\n2. 持久化：新的集合可以通过老的集合或者通过老的集合的set方法来创建。此时老的集合仍然是可用的。\n3. 结构共享：新创建的集合的结构和原始集合的保持一致，在创建过程中会尽可能的提升性能和降低内存损耗。如果新集合和原始集合是一模一样的，就不会创建新集合，而是直接返回原始集合。\n\nImmutability makes tracking changes cheap; a change will always result in a new object so we only need to check if the reference to the object has changed. For example, in this regular JavaScript code:\n\n不可变的特性可以使判断对象是否发生了变化变得很简单；因为发生了改变就会使用新的对象，所以只要判断对象的引用是否发生了改变就可以了。看下面这个例子，首先是正常的js代码：\n\n```\nvar x = { foo: \"bar\" };\nvar y = x;\ny.foo = \"baz\";\nx === y; // true\n```\n\nAlthough y was edited, since it's a reference to the same object as x, this comparison returns true. However, this code could be written using immutable-js as follows:\n\n尽管y被修改了，但是对象x的引用和y的引用还是同一个对象，并没有发生变化，所以y===x仍会返回true。下面是用immutable-js实现的代码示例：\n\n```\nvar SomeRecord = Immutable.Record({ foo: null });\nvar x = new SomeRecord({ foo: 'bar'  });\nvar y = x.set('foo', 'baz');\nx === y; // false\n```\n\nIn this case, since a new reference is returned when mutating x, we can safely assume that x has changed.\n\n在此示例中，当x被调用了set方法同时确实发生了变化，就会返回一个新的对象，所以x和y不是同一个对象的引用了，x===y是false，我们就可以方便的通过x和y是否相等来判断x是否发生了变化。\n\nAnother possible way to track changes could be doing dirty checking by having a flag set by setters. A problem with this approach is that it forces you to use setters and, either write a lot of additional code, or somehow instrument your classes. Alternatively, you could deep copy the object just before the mutations and deep compare to determine whether there was a change or not. A problem with this approach is both deepCopy and deepCompare are expensive operations.\n\n侦测对象变化还有另外的办法，比如在对象的setter方法中加一个标识。这个方法的问题是你必须使用setter方法，并且还要添加很多额外的代码和使用说明。或者，你可以在对象变化的时候使用深拷贝，也或者，在判断对象变化的时候直接使用深比对。这2种方法的问题都是性能损耗过大。\n\nSo, Immutable data structures provides you a cheap and less verbose way to track changes on objects, which is all we need to implement shouldComponentUpdate. Therefore, if we model props and state attributes using the abstractions provided by immutable-js we'll be able to use PureRenderMixin and get a nice boost in perf.\n\n所以，使用不可变数据类型的方案来侦测对象改变是性能最好效率最高的方式，我们只需要直接实现shouldComponentUpdate方法就好了。并且，如果model的props和state都是不可变数据类型，我们只需直接引入PureRenderMixin就可以得到性能的提升。\n\n#Immutable-js and Flux 不可变数据类型和flux\n\nIf you're using Flux, you should start writing your stores using immutable-js. Take a look at the full API.\n\n如果使用flux的话，可以使用immutable-js来编写store。\n\nLet's see one possible way to model the thread example using Immutable data structures. First, we need to define a Record for each of the entities we're trying to model. Records are just immutable containers that hold values for a specific set of fields:\n\n我们来看一看用不可变数据结构对the thread 例子的建模。首先，我们定义一个记录对象。记录对象是不可变数据容器，一般会有下面这些字段：\n\n```\nvar User = Immutable.Record({\n  id: undefined,\n  name: undefined,\n  email: undefined\n});\n\nvar Message = Immutable.Record({\n  timestamp: new Date(),\n  sender: undefined,\n  text: ''\n});\n```\n\nThe Record function receives an object that defines the fields the object has and its default values.\n\nRecord方法的输入参数是定义字段和字段默认值的对象。\n\nThe messages store could keep track of the users and messages using two lists:\n\n消息store由users和messages组成：\n\n```\nthis.users = Immutable.List();\nthis.messages = Immutable.List();\n```\n\nIt should be pretty straightforward to implement functions to process each payload type. For instance, when the store sees a payload representing a new message, we can just create a new record and append it to the messages list:\n\n处理这个数据结构很简单。例如，当收到一个新消息，我只需要新创建一个record并将其放入message list中：\n\n```\nthis.messages = this.messages.push(new Message({\n  timestamp: payload.timestamp,\n  sender: payload.sender,\n  text: payload.text\n});\n```\n\nNote that since the data structures are immutable, we need to assign the result of the push function to this.messages.\n\n注意，因为数据类型是不可变类型，我们需要将push的返回值从新赋给this.messages。\n\nOn the React side, if we also use immutable-js data structures to hold the components' state, we could mix PureRenderMixin into all our components and short circuit the re-rendering process.\n\n在和react结合使用的时候，如果使用不可变数据类型作为组件的state，直接将PureRenderMixin应用到组件中，就能在渲染的过程中享用到极高的性能。\n\n\n\n"
  },
  {
    "path": "ReactNative翻译/React PureRenderMixin.md",
    "content": "# React PureRenderMixin\n\n英文原文地址：https://facebook.github.io/react/docs/pure-render-mixin.html\n\nIf your React component's render function is \"pure\" (in other words, it renders the same result given the same props and state), you can use this mixin for a performance boost in some cases.\n\n如果你的react组件是纯粹的（换句话说，如果传入相同的props和state，每次都将渲染相同的结果），在某些情况下，你就可以使用PureRenderMixin来提升性能。\n\nExample:\n\n```\nvar PureRenderMixin = require('react-addons-pure-render-mixin');\nReact.createClass({\n  mixins: [PureRenderMixin],\n\n  render: function() {\n    return <div className={this.props.className}>foo</div>;\n  }\n});\n```\n\nExample using ES6 class syntax:\n\n```\nimport PureRenderMixin from 'react-addons-pure-render-mixin';\nclass FooComponent extends React.Component {\n  constructor(props) {\n    super(props);\n    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);\n  }\n\n  render() {\n    return <div className={this.props.className}>foo</div>;\n  }\n}\n```\n\nUnder the hood, the mixin implements shouldComponentUpdate, in which it compares the current props and state with the next ones and returns false if the equalities pass.\n\n深入来讲，这个mixin实现了shouldComponentUpdate方法，它对当前props、state和即将要使用的props、state进行了比较，如果一致就返回false，有不相等项就返回true。\n\nNote:\nThis only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only mix into components which have simple props and state, or use forceUpdate() when you know deep data structures have changed. Or, consider using immutable objects to facilitate fast comparisons of nested data.\nFurthermore, shouldComponentUpdate skips updates for the whole component subtree. Make sure all the children components are also \"pure\".\n\n注意：\n这个方案仅仅对object进行了浅比对。如果是复杂数据类型，可能会出现错误。请将此mixin应用到使用了简单数据类型props和state的组件，或者当复杂数据类型发生变化时调用forceUpdate()方法。还有一个更好更快的方案，使用不可变数据类型。\nshouldComponentUpdate会忽略掉整个组件树的更新操作，确保所有的子组件都是纯粹的。\n\n\n"
  },
  {
    "path": "ReactNative翻译/React Shallow Compare.md",
    "content": "# React Shallow Compare\n\n英文原文地址：https://facebook.github.io/react/docs/shallow-compare.html\n\nshallowCompare is a helper function to achieve the same functionality as PureRenderMixin while using ES6 classes with React.\n\nshallowCompare是用es6方式来实现与PureRenderMixin一样的功能的函数。\n\nIf your React component's render function is \"pure\" (in other words, it renders the same result given the same props and state), you can use this helper function for a performance boost in some cases.\n\n如果你的react组件是纯粹的（换句话说，如果传入相同的props和state，每次都将渲染相同的结果），在某些情况下，你就可以使用shallowCompare来提升性能。\n\nExample:\n\n```\nvar shallowCompare = require('react-addons-shallow-compare');\nexport class SampleComponent extends React.Component {\n  shouldComponentUpdate(nextProps, nextState) {\n    return shallowCompare(this, nextProps, nextState);\n  }\n\n  render() {\n    return <div className={this.props.className}>foo</div>;\n  }\n}\n```\n\nshallowCompare performs a shallow equality check on the current props and nextProps objects as well as the current state and nextState objects.\nIt does this by iterating on the keys of the objects being compared and returning true when the values of a key in each object are not strictly equal.\n\nshallowCompare在当前props和未来props（当前state和未来state）进行了浅比对。过程是遍历object的所有属性值并进行===的判断，当有不相等项的时候就返回true。\n\nshallowCompare returns true if the shallow comparison for props or state fails and therefore the component should update.\nshallowCompare returns false if the shallow comparison for props and state both pass and therefore the component does not need to update.\n\nshallowCompare返回true当props或state有不相等项，此时组件将会更新。\nshallowCompare返回true当props和state的所有项目都相等，此时组件不需要更新。\n\n\n"
  },
  {
    "path": "ReactNative翻译/ReactNative : A warning from Apple.md",
    "content": "# ReactNative : A warning from Apple\n\n2017-03-08这一天，突然听到ios的同事说，收到了苹果的警告email，说rn有可能触犯了苹果的审核规则，有可能被拒。所以，去rn的官方看，发现已经有对应的issue被提出，rn官方也在积极的回应这件事，此处翻译一下issue的关键内容。\n\n需要看结论的同学直接看最后一段即可。\n\n原文地址：https://github.com/facebook/react-native/issues/12778\n\n# 问题\n\nI received a warning from Apple this morning , how to solve it :\n\n今天早上我收到了苹果的警告邮件，该如何处理呢：\n\nDear Developer,\n\nYour app, extension, and/or linked framework appears to contain code designed explicitly with the capability to change your app’s behavior or functionality after App Review approval, which is not in compliance with section 3.3.2 of the Apple Developer Program License Agreement and App Store Review Guideline 2.5.2. This code, combined with a remote resource, can facilitate significant changes to your app’s behavior compared to when it was initially reviewed for the App Store. While you may not be using this functionality currently, it has the potential to load private frameworks, private methods, and enable future feature changes.\n\n您的app或者您app中使用的框架，被发现含有在苹果审核之后能修改app功能和行为的代码，这不符合Apple Developer Program License Agreement中的3.3.2章节，也不符合App Store Review Guideline的2.5.2章节。这些代码可以对app进行完全的修改，使其和审核时候的初始版本有很大的差异。也许你现在并没有使用这个功能，但是仍具有调用私有框架，私有方法，改变产品特性的潜在可能。\n\nThis includes any code which passes arbitrary parameters to dynamic methods such as dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(), and running remote scripts in order to change app behavior or call SPI, based on the contents of the downloaded script. Even if the remote resource is not intentionally malicious, it could easily be hijacked via a Man In The Middle (MiTM) attack, which can pose a serious security vulnerability to users of your app.\n\n像这种动态调用函数的代码，比如dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(),还有为了改变app行为的执行远程脚本的行为，也叫SPI。尽管远程资源并不是有害的，但却很容易通过中间人攻击被劫持，这是一个很严重的安全漏洞。\n\nPlease perform an in-depth review of your app and remove any code, frameworks, or SDKs that fall in line with the functionality described above before submitting the next update for your app for review.\n\n请仔细检查你的代码，并删除符合上述描述的代码，然后重新提交审核。\n\nBest regards,\n\n# grabbou解释了相关的苹果审核规则\n\n3.3.2 of Apple Developer Program License:\n\n苹果开发者协议的3.3.2章节：\n\nAn Application may not download or install executable code. Interpreted code may only be used in an Application if all scripts, code and interpreters are packaged in the Application and not downloaded. The only exception to the foregoing is scripts and code downloaded and run by Apple's built-in WebKit framework or JavascriptCore, provided that such scripts and code do not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store.\n\napp不允许下载安装可执行代码。解释型的代码只允许打包到app中才可以被执行，下载的解释型代码不允许被执行。有一种情况是例外的，通过WebKit或者JavascriptCore执行的远程代码，并且这些远程代码并没有违反app提交审核时候的描述和声明。\n\nexplicitly mentions that: An Application may not download or install executable code\n\n规则中明确的指出了：app不能下载安装可以执行代码。\n\nReact Native does none of these. And so, using React Native doesn't expose you to the aforementioned issue.\n\nReact Native并没有违反这一条。所以，使用React Native也并没有违反这一条。\n\nAs @ide mentioned, there are reports this message is addressed to those using Rollout.io. My assumption is that it uses aforementioned methods to patch executable code.\n\n就像@ide提到的，使用Rollout的都被发警告邮件了。我的猜测是，Rollout使用了上述的方法来给可执行代码打补丁了。\n\nWith React Native, you can do so called OTA update, but this is updating Javascript, not native code. Whenever native code changes, you have to make a new release. That OTA update of Javascript code is explicitly allowed in the 3.3.2:The only exception to the foregoing is scripts and code downloaded and run by Apple's built-in WebKit framework or JavascriptCore。\n\n使用React Native可以进行OTA在线更新，但是这是更新Javascript的代码，并不是更新原生代码。当有原生代码改变，你必须要重新发布版本提交审核。在开发者协议的3.3.2章节中，OTA更新Javascript代码是明确指出被允许的：有一种情况是例外的，通过WebKit或者JavascriptCore执行的远程代码，并且这些远程代码并没有违反app提交审核时候的描述和声明。\n\nI believe everyone should follow up on this issue with Apple and sort out what's exactly causing the warning.\n\n我建议大家还是仔细检查一下代码或者和apple沟通，看究竟是什么问题触发的警告。\n\n# 官方最后给的结论\n\nFrom reading the message from Apple and the data points we've observed, this warning is not about React Native.\n\n根据苹果的信息和我们目前获得的情况，这个警告和React Native无关。\n\n### On the technical concern of dynamically executing Objective-C described in Apple's email:苹果邮件中关于动态执行oc代码的分析\n\nApple's message reads to me that they're concerned about libraries like Rollout and JSPatch, which expose uncontrolled and direct access to native APIs (including private APIs) or enable dynamic loading of native code. Rollout and JSPatch are the only two libraries I've heard to be correlated with the warning.\n\n苹果的描述在我看来，他们关系的是Rollout和JSPatch这类的库，这类的库可以在运行时直接进行无控制的并且直接的对原生代码的调用，甚至包括私有api。Rollout和JSPatch是我唯一知道和这个警告有关的库。\n\nReact Native is different from those libraries because it doesn't expose uncontrolled access to native APIs at runtime. Instead, the developer writes native modules that define some functions the app can call from JavaScript, like setting a timer or playing a sound. This is the same strategy that \"hybrid\" apps that use a UIWebView/WKWebView have been using for many years. From a technical perspective, React Native is basically a hybrid app except that it calls into more UI APIs.\n\n而React Native和他们是不同的，RN在运行时候并没有暴露原生API的能力。相反，开发者必须按照RN的规范进行原生方法的定义，这样js才能调用得到，比如设置一个定时器，或者播放一段音频等。这种策略，hybrid应用已经使用了很长时间了。从技术的角度上说，React Native也是一个hybrid应用，只不过RN使用的是原生UI。\n\nTechnically it is possible for a WebView app or a React Native app also to contain code that exposes uncontrolled access to native APIs. This could happen unintentionally; someone using React Native might also use Rollout. But this isn't something specific to or systemic about React Native nor WebViews anyway\n\n其实无论是WebView应用还是RN应用都可以做到运行时动态执行任何原生API。但这不是有意识的设计；比如你用RN也可以同时使用Rollout。但这不是RN或者Webview设计的初衷。\n\n### On the strategy of calling native code from JavaScript:js调用原生代码的分析\n\nApple has been fine with WebViews in apps since the beginning of the App Store, including WebViews that make calls to native code, like in the Quip app. WKWebView even has APIs for native and web code to communicate. It's OK for a WebView to call out to your native code that saves data to disk, registers for push notifications, plays a sound, and so on.\n\n在最开始，Apple就允许app中含有WebViews，并且允许WebViews中的js可以调用原生代码，就像Quip应用。WKWebView还发布了原生和web通信的接口。webview中的js可以通过调用原生api进行保存数据到存储，注册接收通知，播放音频，等等。\n\nReact Native is very much like a WebView except it calls out to one more native API (UIKit) for views and animations instead of using HTML.\n\nReact Native的机制其实和webview非常类似，只不过RN使用了更多的原生api来渲染页面和动画，而不是用html。\n\nWhat neither WebViews nor React Native do is expose the ability to dynamically call any native method at runtime. You write regular Objective-C methods that are statically analyzed by Xcode and Apple and it's no easier to call unauthorized, private APIs.\n\nwebview和RN都没有暴露运行时动态执行原生代码的功能。你必须仍按照xcode和apple的规范编写oc的方法，并且使用静态编译的方式来执行，很难执行那些未授权的或者私有的api。\n\n### Data points\n\nThe developers who received this email were using Rollout or JSPatch in their apps. People who are using only React Native or libraries/frameworks like CodePush and Expo are not affected and are continuing to have their apps accepted by the App Store review.\n\n收到苹果警告的开发者一般是使用了Rollout或者jsPatch。如果仅仅是使用了RN，或者codepush等，并不会受到影响。\n\n### Recommendations 建议\n\nIf you're writing a React Native or WebView app, be sure not to expose dynamic, uncontrolled access to native APIs and you should be OK. There was one third-party RN module that did this and the maintainers have taken it down. And in accordance with the iOS developer terms, make sure your updates don't change the primary purpose of your app and that the changes are consistent with the intended and advertised purpose of your app.\n\n如果你正在进行RN或者webview应用的开发，请确保不要使用动态执行原生代码的功能，这样就不会有问题。有一个第三方的RN模块使用了类似的功能，他的开发者已经将其去掉了。并且，就像ios开发团队那样，保证你的在线升级不要违反app在提交审核时候声明的原则。\n\n"
  },
  {
    "path": "ReactNative翻译/Reconciliation.md",
    "content": "# 【翻译】Reconciliation React比对算法\n\n原文地址：https://facebook.github.io/react/docs/reconciliation.html\n\nReact provides a declarative API so that you don't have to worry about exactly what changes on every update. This makes writing applications a lot easier, but it might not be obvious how this is implemented within React. This article explains the choices we made in React's \"diffing\" algorithm so that component updates are predictable while being fast enough for high-performance apps.\n\n使用react你就不用去关注每次更新UI的时候真正更新了哪些节点。这样的话开发业务代码会非常简单，但并不能理解react的实现原理。本文会解释react的比对算法，理解了这个算法，会让你更容易构建高性能的应用。\n\n# Motivation 动机\n\nWhen you use React, at a single point in time you can think of the render() function as creating a tree of React elements. On the next state or props update, that render() function will return a different tree of React elements. React then needs to figure out how to efficiently update the UI to match the most recent tree.\n\n在某一时刻，react的render方法会返回一组树形节点。当state发生变化，render方法会返回一组不同的树形节点。react需要分析出如何更新UI是最高效、损耗最小的。\n\nThere are some generic solutions to this algorithmic problem of generating the minimum number of operations to transform one tree into another. However, the state of the art algorithms have a complexity in the order of O(n3) where n is the number of elements in the tree.\n\n会有几种算法来保证最小化的更新操作。然而，这些算法都还是有O(n3)的复杂度，n是节点的数量，n3是n的3次方。\n\nIf we used this in React, displaying 1000 elements would require in the order of one billion comparisons. This is far too expensive. Instead, React implements a heuristic O(n) algorithm based on two assumptions:\n\n1. Two elements of different types will produce different trees.\n2. The developer can hint at which child elements may be stable across different renders with a key prop.\n\nIn practice, these assumptions are valid for almost all practical use cases.\n\n也就是说，如果有1000个节点，那就要比对1000000000次。这损耗太高了。所以，react实现了新的O(n)算法，但是需要满足如下2个假设：\n\n1. 2个不同类型的节点，会产生不同的UI树\n2. 开发者可以提供一个叫key的属性，来指出是否为同一个节点\n\n在实际应用中，这2个假设都是普遍成立的。\n\n# The Diffing Algorithm 比对算法\n\nWhen diffing two trees, React first compares the two root elements. The behavior is different depending on the types of the root elements.\n\nreact比对2棵树的时候，会首先比对根节点。会根据根节点的类型来比对。\n\n### Elements Of Different Types 不同类型的节点\n\nWhenever the root elements have different types, React will tear down the old tree and build the new tree from scratch. Going from a to img, or from Article to Comment, or from Button to div - any of those will lead to a full rebuild.\n\n当根节点是不同类型的时候，react将会移除整棵老树，完全新建一棵树替代。比如，a to img或者from Article to Comment, 或者 from Button to div，所有这些改变都会触发一次完整的重新构建动作。\n\nWhen tearing down a tree, old DOM nodes are destroyed. Component instances receive componentWillUnmount(). When building up a new tree, new DOM nodes are inserted into the DOM. Component instances receive componentWillMount() and then componentDidMount(). Any state associated with the old tree is lost.\n\n在移除老树的过程中，所有的dom节点都会被销毁。根组件实例会触发componentWillUnmount事件。当新建一棵新树，新的dom节点会别插入到dom中。根组件实例会触发componentWillMount及componentDidMount。所有老树的state也会被销毁。\n\nAny components below the root will also get unmounted and have their state destroyed. For example, when diffing:\n\n老树的所有孩子节点也会被完全的销毁，同时他们的state也会被销毁。比如下面的例子:\n\n```\n<div>\n  <Counter />\n</div>\n\n<span>\n  <Counter />\n</span>\n```\n\nThis will destroy the old Counter and remount a new one.\n\n老的Counter会被销毁，会新建一个Counter替代。\n\n### DOM Elements Of The Same Type 相同类型的dom节点\n\nWhen comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes. For example:\n\n在比较相同类型的节点的时候，react会比较两个节点的属性，保持dom节点对象不变，然后会更新值发生改变的属性。例如：\n\n```\n<div className=\"before\" title=\"stuff\" />\n\n<div className=\"after\" title=\"stuff\" />\n```\n\nBy comparing these two elements, React knows to only modify the className on the underlying DOM node.\n\nreact比对这2个节点后，只会更新className。\n\nWhen updating style, React also knows to update only the properties that changed. For example:\n\n当更新样式的时候，react也只更新改变的属性。例如:\n\n```\n<div style={{color: 'red', fontWeight: 'bold'}} />\n\n<div style={{color: 'green', fontWeight: 'bold'}} />\n\n```\n\nWhen converting between these two elements, React knows to only modify the color style, not the fontWeight.\n\n\n针对这2个节点，react会只更新color，而不会更新fontWeight。\n\nAfter handling the DOM node, React then recurses on the children.\n\n当比对完一个节点后，react会继续递归这个节点的孩子节点。\n\n### Component Elements Of The Same Type 相同类型的component组件节点\n\nWhen a component updates, the instance stays the same, so that state is maintained across renders. React updates the props of the underlying component instance to match the new element, and calls componentWillReceiveProps() and componentWillUpdate() on the underlying instance.\n\n当组件节点更新，会保持同一个对象实例，state会保持不变。react会更新那些改变过组件节点的属性，并且触发他们的componentWillReceiveProps和componentWillUpdate。\n\nNext, the render() method is called and the diff algorithm recurses on the previous result and the new result.\n\n下一次改变发生时，整个流程会再次执行。\n\n### Recursing On Children 递归孩子节点\n\nBy default, when recursing on the children of a DOM node, React just iterates over both lists of children at the same time and generates a mutation whenever there's a difference.\n\n默认情况下，在递归孩子节点的时候，react会同时遍历新老树的孩子节点，并标记处出改变的地方。\n\nFor example, when adding an element at the end of the children, converting between these two trees works well:\n\n举个栗子，在某个节点的末尾插入一个新节点，比对算法会高效的执行：\n\n```\n<ul>\n  <li>first</li>\n  <li>second</li>\n</ul>\n\n<ul>\n  <li>first</li>\n  <li>second</li>\n  <li>third</li>\n</ul>\n```\n\nReact will match the two li first trees, match the two li second trees, and then insert the li third tree.\n\nreact会认为2个li first是相同的，2个li second是相同的，并找出了新增的li third。\n\nIf you implement it naively, inserting an element at the beginning has worse performance. For example, converting between these two trees works poorly:\n\n但是，如果在某个几点的开头插入一个节点，比对算法会执行的很低效，例如：\n\n```\n<ul>\n  <li>Duke</li>\n  <li>Villanova</li>\n</ul>\n\n<ul>\n  <li>Connecticut</li>\n  <li>Duke</li>\n  <li>Villanova</li>\n</ul>\n```\n\nReact will mutate every child instead of realizing it can keep the li Duke and li Villanova subtrees intact. This inefficiency can be a problem.\n\nreact会认为每一个孩子节点都发生了变化，而不是保持li Duke和li Villanova不变。这种低效的执行很成问题。\n\n\n### Keys\n\nIn order to solve this issue, React supports a key attribute. When children have keys, React uses the key to match children in the original tree with children in the subsequent tree. For example, adding a key to our inefficient example above can make the tree conversion efficient:\n\n为了解决这个问题，react使用了key属性。当孩子节点有key属性的时候，react会使用key属性的值去匹配2棵树的孩子节点。例如，在我们上面低效的例子中，为孩子节点添加key属性如下：\n\n```\n<ul>\n  <li key=\"2015\">Duke</li>\n  <li key=\"2016\">Villanova</li>\n</ul>\n\n<ul>\n  <li key=\"2014\">Connecticut</li>\n  <li key=\"2015\">Duke</li>\n  <li key=\"2016\">Villanova</li>\n</ul>\n```\n\nNow React knows that the element with key '2014' is the new one, and the elements with the keys '2015' and '2016' have just moved.\n\n这样的话，react就知道key是2014的是新增的，而2015和2016仅仅是移动了。\n\nIn practice, finding a key is usually not hard. The element you are going to display may already have a unique ID, so the key can just come from your data:\n\n在实践中，使用key属性并不难。要显示的元素通常都有一个唯一的id，所以，使用这个id就可以了：\n\n```\n<li key={item.id}>{item.name}</li>\n```\n\nWhen that's not the case, you can add a new ID property to your model or hash some parts of the content to generate a key. The key only has to be unique among its siblings, not globally unique.\n\n当没有合适的值作为key的时候，你最好在模型中加一个id属性，或者hash某个值作为key。这个key只需要在兄弟节点中唯一，并不需要全局唯一。\n\nAs a last resort, you can pass item's index in the array as a key. This can work well if the items are never reordered, but reorders will be slow.\n\n还有一个办法，你可以使用数组的序号作为key。如果你的元素没有改变顺序的需求的话，就没有问题，但是如果有改变顺序的功能就会非常慢。\n\n# Tradeoffs 设计的折中\n\nIt is important to remember that the reconciliation algorithm is an implementation detail. React could rerender the whole app on every action; the end result would be the same. We are regularly refining the heuristics in order to make common use cases faster.\n\n目前，比对算法仍在改进中。react有可能会在每个动作之后都重新渲染整个app；并且渲染的结果可能都是相同的。我们正在不停的改进算法保证大部分的场景都是最高效的。\n\nIn the current implementation, you can express the fact that a subtree has been moved amongst its siblings, but you cannot tell that it has moved somewhere else. The algorithm will rerender that full subtree.\n\n在目前的实现中，如果在兄弟节点中移动一个节点，是没有问题的，如果将这个节点移动到其它的地方去，会导致重新构建这个节点。\n\nBecause React relies on heuristics, if the assumptions behind them are not met, performance will suffer.\n\n1. The algorithm will not try to match subtrees of different component types. If you see yourself alternating between two component types with very similar output, you may want to make it the same type. In practice, we haven't found this to be an issue.\n2. Keys should be stable, predictable, and unique. Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.\n\n如果上面提到的2个条件不能被满足，性能会比较差。\n\n1. 目前的算法不会去比对类型不同节点的孩子节点。如果你的两个节点有着很类似的输出，最好保证其有相同的类型。在实际的应用中，这一般都不是一个问题。 \n\n2. keys应该是稳定不变的，可预见的，并且唯一的。不稳定的key（比如用Math.random()产生的）将会导致大量的重复构建，这将会导致性能下降，也会导致孩子节点的state状态丢失。\n\n"
  },
  {
    "path": "ReactNative翻译/react-native的定时器.md",
    "content": "# 定时器 Timers \n\n英文原文地址：http://facebook.github.io/react-native/docs/timers.html\n\n定时器是应用程序的重要组件，ReactNative实现了浏览器的定时器。\n\n###定时器\n1. setTimeout, clearTimeout\n1. setInterval, clearInterval\n1. setImmediate, clearImmediate\n1. requestAnimationFrame, cancelAnimationFrame\n\nrequestAnimationFrame(fn) 与setTimeout（fn，0）并不一样，前面的是会等待当前的所有帧结束之后才触发，后面的是会尽快的被触发（over 1000x per second on a iPhone 5S 这句不会翻）。\n\nsetImmediate会在当前js执行块的最后立刻被执行，在与native通信之前。如果你使用这个方法，它会被立刻执行，不会被任何代码所有阻断包括native代码。\n\nPromise的实现就是基于setImmediate作为基础的。\n\n###InteractionManager交互管理器\n原生app流畅的一大原因就是利用多线程在交互和动画的线程中避免同时执行大量计 算。在ReactNative中，会受到js只有一个执行线程的限制，但是你可以使用交互管\n理器来保证耗时复杂工作在交互动画完成之后才开始执行。\n\n示例代码：\n\n```\nInteractionManager.runAfterInteractions(() => {\n   // ...long-running synchronous task...\n});\n```\n\n几个定时器的比较：\n\n1. requestAnimationFrame(): 在一个动画效果完成之后执行代码。\n1. setImmediate/setTimeout/setInterval():稍后执行代码，这些方法可能会拖慢动画效果。\n1. runAfterInteractions():稍后执行代码，不会拖慢当前的动画效果。\n\n触摸系统会把一组触摸事件作为一次交互，runAfterInteractions()注册的回调函数会在当前的所有交互结束后触发。\n\n你也可以使用交互管理器在自己的应用程序中，在动画的开始创建一个handle，在动画结束的时候清除掉它。\n\n```\nvar handle = InteractionManager.createInteractionHandle();\n\n//执行动画。。。。runAfterInteractions注册的函数会在稍后动画结束后触发。\n\nInteractionManager.clearInteractionHandle(handle)\n\n//所有的handle都clear之后就会触发注册的事件了\n\n```\n\n###TimerMixin \n\n用RN构建的app，发生系统崩溃通常都是因为定时任务在组件已经被卸载之后触发造成的。解决这个问题最好的办法就是使用TimerMixin。如果你引入了TimerMixin，你就可以使用this.setTimeout代替setTimeout，这个方法可以保证当组件被卸载时所有的定时任务都被正确的清除了。\n\nRN中并没有带这个库，你可以在你的工程里用命令npm i react-timer-mixin --save来安装。\n\n示例代码：\n\n```\nvar TimerMixin = require('react-timer-mixin');\n\nvar Component = React.createClass({\n  mixins: [TimerMixin],\n  componentDidMount: function() {\n    this.setTimeout(\n      () => { console.log('I do not leak!'); },\n      500\n    );\n  }\n});\n\n```\n\n我们特别不建议使用原生的全局方法setTimeOut，强烈建议使用react-timer-mixin提供的this。setTimeout。这会消除很多隐性的bug，比如说在组件卸载后执行定时任务引起的app崩溃。\n\n\n\n\n\n"
  },
  {
    "path": "ReactNative翻译/react-native的性能.md",
    "content": "# react-native的性能\n\n英文原文地址： http://facebook.github.io/react-native/docs/performance.html\n\n使用react native 代替webview技术来构建app最大的理由就是60FPS流畅的体验。我们尽可能的让开发者可以专注于app开发而不是性能优化，但是目前RN还不能完全的达到此目标，并不能自动选择最优的方式，所以，还需要开发者关注并进行调整。\n\n本篇文章介绍了性能调优的基本技巧，同时讨论一些常见问题及其解决方案。\n## 帧数\n动画效果其实就是静态图片的连续播放，每秒图片播放的越多，动画就越平滑流畅。60FPS的意思就是1秒有60张图片进行了播放，一张图叫一帧，如果想达到60FPS，需要16.67ms就要播放一帧，所以就要求程序在16.67ms就要生成一帧，如果超过这个时间，就会掉帧，就会觉得程序不流畅。\n\n打开开发者菜单，点击 show fps monitor 会有2个帧数显示在屏幕上。\n### Javascript frame rate【js帧数】\n大多数情况下，你的业务逻辑会运行在js线程下。这个线程处理了api调用，触摸事件，等等...对原生view的更新是批量提交的，也是尽力保持在每帧之前提交。如果js线程在下一帧来到之前不能进行提交，就会掉帧。举个例子，如果你在一个复杂的组件的根节点调用`this.setState`方法，就会导致一次复杂的diff计算，如果这次计算耗时200ms就相当于掉了12帧。在这过程中，所有js控制的动画都会卡住。事实上只要超过100ms用户就会感觉到。\n\n卡顿经常会发生在页面转场动画上：当进行页面转场的时候，js线程要计算新页面中所有的组件，以便通知原生去展现view。如果动画是js控制的，就通常会消耗几帧。所以，组件在`componentDidMount`中进行的工作会导致转场动画的掉帧。\n\n另一个例子是触摸事件的反馈：如果你的js线程正在忙碌着，点击TouchableOpacity会有延迟。这是因为js不能及时的处理触摸事件，导致原生view 不能及时的做出反应。\n### Main thread（UI thread） frame rate【UI线程帧数】\n你可能会注意到`NavigatorIOS`的性能要比`Navigator`好，这是因为`Navigator`的转场动画是由主线程控制的，js的掉帧并不能影响它。参考阅读来帮助你选择导航器http://facebook.github.io/react-native/docs/navigator-comparison.html\n\n同样的，ScrollView的滚动永远不会受到js的影响，这是因为ScrollView完全是基于原生的主线程的。尽管会有滚动事件分发到js，但这并不影响滚动的动作。\n\n\n##常见问题和解决方案\n###开发模式 dev=true\n在开发模式下js的性能会受到严重的影响。这是不可避免的:要提供详细的提示和错误检查js就要有大量的工作要做。\n\n###转场动画不流畅\n如上所述，`Navigator`的转场动画是js控制的。就比如说从右推进来的这个动画效果：每一帧新页面都会从右像左移动一点，其实就是改变新页面的x轴坐标。在这过程中的每一帧，js都要告诉主线程新的x坐标是多少。如果js被别的任务锁定了，那这一帧就不能及时的更新x坐标了，你就会觉得动画卡顿了。\n\n这个问题的最有效的解决办法就是把js控制的动画效果让主线程自己来控制。如果套用上面的场景可以这样重构，在动画的开始，js就计算好这次动画的所有x坐标值的数组，传递给主线程。这样的话，动画开始后，就不受js性能的影响了，就算动画刚开始有一些卡顿，你可能也注意不到。\n\n但是，目前`Navigator`还不是这样实现的，所以，我们提供了`InteractionManager`来帮助你在转场动画的过程中，新页面只渲染必要的少量的内容。\n\n`InteractionManager.runAfterInteractions`只有一个函数类型的参数，当转场动画结束，这个回调函数就会被触发(所有基于`Animated`API的动画都会触发InteractionManager.runAfterInteractions)。\n\n示例代码如下：\n\n```\nclass ExpensiveScene extends React.Component {\n  constructor(props, context) {\n    super(props, context);\n    this.state = {renderPlaceholderOnly: true};\n  }\n\n  componentDidMount() {\n    InteractionManager.runAfterInteractions(() => {\n      this.setState({renderPlaceholderOnly: false});\n    });\n  }\n\n  render() {\n    if (this.state.renderPlaceholderOnly) {\n      return this._renderPlaceholderView();\n    }\n\n    return (\n      <View>\n        <Text>Your full view goes here</Text>\n      </View>\n    );\n  }\n\n\n  _renderPlaceholderView() {\n    return (\n      <View>\n        <Text>Loading...</Text>\n      </View>\n    );\n  }\n};\n```\n你不仅可以渲染一个loading指示器，也可以渲染你新页面内容的一小部分。比如说，当你打开Facebook你会看到将要放入文本的灰色的矩形。如果你要在新页面要放入一个地图，你可以放一个灰色的占位符或者一个loading直到动画结束，否则地图的初始化一定会导致转场动画的掉帧。\n\n### 大数据量的ListView初始化慢或者滚动卡顿\n我们提供了一些方法来调优，但是尚没有万能的办法，你可以尝试以下选项。\n####initialListSize 初始帧渲染行数\n这个属性定义了第一帧渲染list的行数。如果我们希望某些内容非常快的展现在页面上，可以设置`initialListSize`等于1，其余的行会在后边的帧里来渲染。每一帧渲染的行数是由`pageSize`决定的。\n####pageSize 每一帧渲染行数\n初始化使用`initialListSize`，`pageSzie`决定了每一帧渲染的行数。默认值是1，如果你的view很小而且渲染很简单，你可以加大这个数量。你可以通过测试来找到你满足你需求的值。\n####scrollRenderAheadDistance 触发渲染的距离\n在距离显示区域多远就开始渲染。\n\n如果你的列表有2000行，那么一次渲染所有会耗费大量内存和计算资源。`scrollRenderAheadDistance`可以让你定义距离显示区域多远之内的rows可以被渲染。\n\n####removeClippedSubviews删除屏幕外子视图\n此值为true表示屏幕外的字视图(行容器是overflow=hidden的)会被从原生视图中移除。他可以提升列表滚动的性能。这个属性默认值就是ture。\n这个属性对大数据列表非常重要。在安卓端overflow的默认值就是hidden，但是在ios端需要手动设置overflow的值为hidden在行容器上。\n\n###不需要立即展现的组件渲染很慢\n这通常是由listvew引起的，你先想办法调整一下listview。上面我们也提供了很多手段来分步渲染视图。Listview也可以横向展现，好好利用这一点。\n\n###重新渲染那些很少改变的视图很慢\nListview可以使用`rowHasChanged`方法来降低diff判断的计算损耗。如果你使用了不可变数据类型，使用`=`就可以简单的判断是否是同一对象了。\n\n同样的，`shouldComponentUpdate`可以精确的描述出需要重新渲染的状态。如果你正在写纯组件（render方法的返回完全由props和state来决定），使用`PureRenderMixin`可以帮助你提升性能。同样的，不可变数据类型也同样会提升性能和降低代码复杂度，特别是在大数据量的对象深比对上。\n\n###js线程在同一时间做太多的事就会掉帧\n过场动画卡顿就是因为这个原因，在某些场景下也会出现这个问题。使用InteractionManager是比较好的办法，但当你由于某些原因不能延迟执行任务时，你可以试试LayoutAnimation。\n\nAnimatedApi的动画效果依赖于js，而LaoutAnimation使用了CoreAnimation 完全不受js线程和主线程掉帧的影响。\n\n你可以查看相关文档来学习如何使用。\n\n注意，只支持iOS。而且，只支持静态动画。如果需要动画过程有交互，还要使用Animated。\n###在屏幕上移动view (scrolling, translating, rotating) ，UI线程会掉帧\n这经常发生在一个背景透明的text在一个图片上，或者需要alpha合成并重新渲染view的时候。你可以使用shouldRasterizeIOS或者renderToHardwareTextureAndroid来改变这种情况。\n\n但是这会极大的耗费内存。最好使用以上属性的时候监控一下内存，看能否接受。如果，不移动view的话，就一定要关了这2个属性。\n### 改变图片大小的动画会使UI线程掉帧\n在ios中，调整图片的高宽会导致从原始图片的重新剪裁。这很耗费资源，特别是大图。\n\n最好用transform: [{scale}]来实现改变图片大小的动画。\n\n一个场景是点击一个图并全屏展现的时候。\n###Touch系列组件响应很慢\n有时候点击一个touch组件，onPress事件会触发setSate，导致js线程立刻进行大量的计算工作，最终给用户的效果就是高亮或者透明的效果有延迟。\n\n解决办法是使用requestAnimationFrame\n\n```\nhandleOnPress() {\n  // Always use TimerMixin with requestAnimationFrame, setTimeout and\n  // setInterval\n  this.requestAnimationFrame(() => {\n    this.doExpensiveAction();\n  });\n}\n```\n###分析工具\n用一些工具来获取js线程和UI主线程的细节信息。\n\n在ios中使用an invaluable tool，安卓使用systrace。\n\nFor iOS, Instruments are an invaluable tool, and on Android you should learn to use systrace.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "ReactNative翻译/导航器比较.md",
    "content": "# Navigator Comparison \n\n\n英文原文地址：http://facebook.github.io/react-native/docs/navigator-comparison.html\n\n###这篇看完发现没啥可翻的，记住结论就可以了：使用Navigator，不要用NavigatorIOS。\n\nThe differences between Navigator and NavigatorIOS are a common source of confusion for newcomers.\n\nBoth Navigator and NavigatorIOS are components that allow you to manage the navigation in your app between various \"scenes\" (another word for screens). They manage a route stack and allow you to pop, push, and replace states. In this way, they are similar to the html5 history API. The primary distinction between the two is that NavigatorIOS leverages the iOS UINavigationController class, and Navigator re-implements that functionality entirely in JavaScript as a React component. A corollary of this is that Navigator will be compatible with Android and iOS, whereas NavigatorIOS will only work on the one platform. Below is an itemized list of differences between the two.\n\n###Navigator \n1. Extensive API makes it completely customizable from JavaScript.\n1. Under active development from the React Native team.\n1. Written in JavaScript.\n1. Works on iOS and Android.\n1. Includes a simple navigation bar component similar to the default NavigatorIOS bar: Navigator.NavigationBar, and another with breadcrumbs called Navigator.BreadcrumbNavigationBar. See the UIExplorer demo to try them out and see how to use them.\n\t1. Currently animations are good and improving, but they are still less refined than Apple's, which you get from NavigatorIOS.\n1. You can provide your own navigation bar by passing it through the navigationBar prop.\n\n\n###NavigatorIOS \n\n \n\n1. Small, limited API makes it much less customizable than Navigator in its current form.\n1. Development belongs to open-source community - not used by the React Native team on their apps.\n\t1. A result of this is that there is currently a backlog of unresolved bugs, nobody who uses this has stepped up to take ownership for it yet.\n1. Wraps UIKit, so it works exactly the same as it would on another native app. Lives in Objective-C and JavaScript.\n\t1. Consequently, you get the animations and behaviour that Apple has developed.\n1. iOS only.\n1. Includes a navigation bar by default; this navigation bar is not a React Native view component and the style can only be slightly modified.\n\nFor most non-trivial apps, you will want to use Navigator - it won't be long before you run into issues when trying to do anything complex with NavigatorIOS.\n\n"
  },
  {
    "path": "ReactNative问题汇总/Immutable.js的问题.md",
    "content": "#Immutable.js的问题\n\n\n##容易与原生对象混淆\n\n参考地址：http://zhuanlan.zhihu.com/purerender/20295971\n\n这点是我们使用 Immutable.js 过程中遇到最大的问题。写代码要做思维上的转变。\n\n虽然 Immutable.js 尽量尝试把 API 设计的原生对象类似，有的时候还是很难区别到底是 Immutable 对象还是原生对象，容易混淆操作。\n\nImmutable 中的 Map 和 List 虽对应原生 Object 和 Array，但操作非常不同，比如你要用 `map.get('key')` 而不是 `map.key`，`array.get(0)` 而不是 `array[0]`。另外 Immutable 每次修改都会返回新对象，也很容易忘记赋值。\n\n当使用外部库的时候，一般需要使用原生对象，也很容易忘记转换。\n\n下面给出一些办法来避免类似问题发生：\n\n1. 使用 Flow 或 TypeScript 这类有静态类型检查的工具约定变量命名规则：\n1. 如所有 Immutable 类型对象以 `$$` 开头。\n2. 使用 `Immutable.fromJS` 而不是 `Immutable.Map` 或 `Immutable.List` 来创建对象，这样可以避免 Immutable 和原生对象间的混用。\n\n\n##tips 有时候还原回普通对象进行批量操作更方便\n\n"
  },
  {
    "path": "ReactNative问题汇总/ReactNative-0.14.2.md",
    "content": "# ReactNative-0.14.2\n\n[toc]\n\n##如果引入一个模块，但是这个模块没有写【module.exports】，报的错误描述不对\n![](media/14486188240052.jpg)\n##定义组件不能使用class name extends Component的形式，有state取不到的bug.\n\n改成React.createClass后就好了\n\n##TextInput的borederColor,color在安卓下不好使用\n\n##如果listview有父节点但不满足以下条件，就会出现onEndReached不停触发的bug\n\n####所有的祖宗节点都必须定义flex=1。\n其实是因为没定义flex，高度就是无限高，就不停触发了，所以只要给固定高就行了\n\n####所有的祖宗节点不能有ScrollVIew，只能是View。\n解决方案：用listview的renderHeader和renderFooter方法来创建头尾，而不要嵌套用scrollview\n\n感觉耦合略强.本不是列表组件的部分，也得和列表产生依赖。\n\n##ListView的Android版本不支持sticky-header特性\nhttps://github.com/facebook/react-native/issues/2700\n\n##ListView的数据源切换导致sectionHeader重新渲染状态重置的bug\n如果切换的数据源的dataSource.cloneWithRows传入的数组中没有元素，会导致已有的section都重新渲染。section相关的状态都会重置，相当于重新创建。【在RN0.15.0下，状态不会重置了，但是还会重新渲染，sectionHeader部分会空白一阵】\n\n解决方案：可以让第二个listview加载任何一个数据就可以了，模拟的空数据也可以，比如这样的[{}].\n##android不支持textDecoration系列\n\n##安卓图片contain不管用，显示不对\nhttps://github.com/facebook/react-native/issues/4031\n\n##\t安卓边框圆角单独设置不管用\nhttps://github.com/facebook/react-native/issues/3040\n\n设置全部borderRadius是管用的\n\n##text不支持样式text-overflow\n可以用属性 numberOfLines 替代\n\n##\tlistview如果来回切换数据源，有时候会导致onEndReached事件不能触发\n在RN0.15.0版本下，没有再复现过，当做已经修复了吧。\n\n\n"
  },
  {
    "path": "ReactNative问题汇总/ReactNative-0.15.0.md",
    "content": "# ReactNative-0.15.0\n\n[toc]\n\n\n##当图片位置发生调整，还加载原来位置\n必须关了调试服务器，重新启动调试服务器，否则还加载原来的位置，并报错。\n\n场景是把一个图剪切到新的位置。\n\n\n## 安卓，textinput，当高度较小的时候文字显示不全\nhttps://github.com/facebook/react-native/issues/3182\n\nhttps://github.com/facebook/react-native/pull/2730\n\n解决方案：设置padding=0，同时保证高度大于等于20就正常了\n\n\n##Textinput不支持直接获得value\nhttp://stackoverflow.com/questions/32913338/react-native-get-textinput-value\n\n查遍了资料，翻看了源码，确实没有直接获得value的办法，放弃了。\n\n官方给的方案是通过onchange不停的记录value，真不知道为啥要这样设计，太反人类了。\n\n##安卓textinput不支持圆角\n理论上可以利用外层view来进行圆角工作，未测试\n\n##在【render中】获得不到同级组件的ref,因为组件还没渲染完，所以没有生成refs.\nhttp://stackoverflow.com/questions/32680725/show-drawerlayoutandroid-via-toolbarandroid-oniconclicked\n\n首先这不是一个bug。\n\n这个问题的实质是，因为在render中，就想依赖同级组件，但是同级组件不能保证已经渲染完成，所以RN就干脆让你都取不到。\n\n如果不是在render中，就没有这个问题了\n\nthis.refs['DRAWER_REF'] will only be accessible from the component that renders the drawer. If you want to access it from some component down the view hierarchy you need to either: pass ref down the view hierarchy as a property OR pass a callback method down the view hierarchy that will be defined on drawer parent component level\n\n\n####解决方案1。\n最后不得不把所有状态放到父组件中，然后通过props传递给子组件，达到了同级组件之间通信。\n\n这样会导致设计组件状态时候要考虑组件间通信的问题，有时候子组件状态不得不升级到父组件中，影响设计逻辑\n\n####解决方案2 \n让子组件初始化的时候把this传递给父组件，这样父组件就有所有子组件的引用了，所有子组件通过父组件来来获得同级组件对象\n\n这样做，如果组件渲染顺序不是预期的，仍然会拿不到。\n\n####此问题还需要思考更好的解决方案\n\n##经过各种测试，终于找到了一个隐藏组件的好办法\n\n\n```\n/**\n* 隐藏\n*/\nhidden:{\n  position:'absolute',\n  left:-9999,\n  height:10, //加高是为了解决listview不停触发底部滚动事件的\n  width:10,  //加宽是为了解决viewpager隐藏时候，ios会崩溃的问题\n},\n        \n```\n\n\n\n##text组件的opacity从他的父亲view继承，且不能修改\nhttps://github.com/facebook/react-native/issues/1314\n\n解决方案：父组件使用rgba来写透明度，而不是opacity。请参考：https://github.com/facebook/react-native/commit/3c04bfcf53ced10aa3afc378b2b35afbfebc9678\n\n##绝对定位是基于父组件的，这与css很不同。\n要想基于页面进行定位，必须要放到根节点下面，不知道还有没有别的解决方案。参考地址：https://github.com/facebook/react-native/issues/3210\n\n这是一个解决方案，是用原生来做的，只支持ios。地址：https://github.com/brentvatne/react-native-overlay\n\n## 不支持zIndex\n参考地址：https://github.com/facebook/react-native/issues/1076\n\n##安卓和ios绝对定位对overflow的反应不同\n```\n<View style={css.parent}>\n\t<View style={css.child}>\n\t</View>\n</View>\n        \nparent:{\n   width:200,\n   height:200,\n   backgroundColor:'green',\n   overflow:'visible',\n},\n\nchild:{\n   position:'absolute',\n   top: 10,\n   bottom: 0,\n   left: 10,\n   right: 0,\n\n   width:400,\n   height:500,\n   backgroundColor:'red',\n\n},             \n```\n1. parent上overflow=hidden，安卓ios表现一致，parent会挡住绝对定位的child\n2. parent上overflow=visible，安卓parent仍然会挡住child，ios不会挡住。\n\n注意：overflow在安卓下默认是hidden，在ios下默认是visible\n\n##overflow在安卓下默认是hidden，在ios下默认是visible\n\noverflow在安卓下默认是hidden，在ios下默认是visible\n##TouchableOpacity和TouchableHighlight，第一次点击不触发press\n\n场景：\n \n1. 当touch组件在listview or scrollView的时候\n2. 焦点在某个输入框中\n\n解决方案：ListView or ScrollView上加入属性keyboardShouldPersistTaps={true}\n\n \n####scrollview的keyboardShouldPersistTaps bool \nWhen false, tapping outside of the focused text input when the keyboard is up dismisses the keyboard. When true, the scroll view will not catch taps, and the keyboard will not dismiss automatically. The default value is false.\n\nfalse的时候scrollview会catch事件，然后消失键盘，事件不会传递到touch。\n\ntrue的时候相反。\n\n经过测试这个属性ListView组件也可以用。\n\n参考地址：https://github.com/facebook/react-native/issues/4087\n\n\n##安卓不支持textDecorationLine的所有值\n参考地址：https://github.com/facebook/react-native/pull/3816\n\n##遇到了一个require有循环引用的情况，然后会报错\n\n报错如下：\n\nInvariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of `Result`.\n\n解决方案：当时是header依赖search，search依赖result，result依赖header。然后我把header对search的依赖去掉，移动到更上一级中，就好了。\n\n这个问题未来可能是个大坑。\n\n##对自定义组件写style，不会自动传递给此组件的render根节点\n\n比如自定组件A的render方法如下\n\n\trender(){\n\t\treturn <View></View>\n\t}\n\n使用A的时候如下\n\n\t<A style={{backgroundColor:'red'}}/>\n\t\n背景色不会有任何的改变。\n\n解决方案：可以这样定义A的render\n\n\trender(){\n\t\treturn <View style={this.props.style}></View>\n\t}\n\t\n\t\n\t\n##SwitchAndroid组件不能用，引入就报错\n\n这个问题开始时真没想到会是个大坑，弄了差不多快一天的时间。。。。。。\n\n一开始，发现官方其实还发布了平台通用的Switch组件,不过文档中没有，so翻看源码看怎么用，里面注释都很清楚。\n\n在一个新的工程里能正常使用，但是在我的工程里就是死也显示不出来。\n\n我就开始各种测试，各种升级，各种删了重装。。。。\n\n最后终于发现是我工程里的android打包环境可能太旧了或者被我改过了啥【我印象中是没改过的】，把这个环境删了，然后用`react-native android`命令重新生成，然后重新打包，就一切都ok了。\n\n##安卓中switch左侧显示不全\n尝试用paddingLeft width等值让其显示全，没有用。\n\n坐等官网修复。\n\n\n## 安卓中image不支持defaultSource\n\n## RN中的服务器地址的image，必须要给一个高宽\n动态高宽的图片，如何支持还需要找解决方案\n\n**如果父容器是flexDirection=column的，宽度貌似可以不写，因为宽默认就是100%了，待测试**\n\n##安卓中dimensions获得window的高不准，差了20左右，\n华为p8\n\nhttps://github.com/facebook/react-native/issues/4934\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "ReactNative问题汇总/ReactNative-0.16.0.md",
    "content": "# ReactNative-0.16.0\n\n\n## 升级后，测试服务器Whole Program Optimisations，非常慢\n在17版本里dev=true的时候不会执行了，dev=false的时候还是很慢\n\n## 升级后，ios和android必须一个是dev=true一个是false，否则报错\n\n## 每个page的根节点都要声明背景色，否则页面转场的时候会是透明的。\n\n"
  },
  {
    "path": "ReactNative问题汇总/ReactNative-0.17.0.md",
    "content": "# ReactNative-0.17.0\n\n\n#不支持export default 的写法\n\nhttps://github.com/facebook/react-native/issues/4798\n\nwe should get updating to the latest Babel on the release schedule for .17 stable if possible.会在0.17.0的stable版本升级babel并修复。\n\n"
  },
  {
    "path": "ReactNative问题汇总/总体说明.md",
    "content": "# 总体说明\n\n本文档描述了在实际使用ReactNative的过程中所遇到的问题和可能的解决方案。\n\n了解这些，能帮助你在写代码之前就知道如何避开一些坑。\n\n问题按照使用的ReactNative的版本进行的分类，不保证问题已经在新版本修复了，仅供参考。\n\n"
  },
  {
    "path": "data.json",
    "content": "[\n    {\n        \"title\": \"ReactNative增量升级方案\",\n        \"url\": \"https://raw.githubusercontent.com/cnsnake11/blog/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%A2%9E%E9%87%8F%E5%8D%87%E7%BA%A7%E6%96%B9%E6%A1%88.md\",\n        \"content\": \"facebook的react-native给我们带来了用js写出原生应用的同时，也使得使用RN编写的代码的在线升级变得可能，终于可以不通过应用市场来进行升级，极大的提升了app修bug和赋予新功能的能力。----使用h5的方式也可以做到，但是rn的用户体验可要远远超过h5啊。一般使用RN编写的app的线上使用方式，是将react-native bundle命令打出bundle文件和assets文件夹，直接内置到app中，app在viewcontroller或者activity中直接加载app内部的bundle文件，比如下图。当修改了代码或者图片的时候，只要app使用新的bundle文件和assets文件夹，就完成了一次在线升级。\",\n        \"imgUrl\": \"http://p0.so.qhimg.com/bdr/_240_/t0130ce82fe2c9e1b9a.jpg\"\n    },\n\n    {\n        \"title\": \"ReactNative的组件架构设计\",\n        \"url\": \"https://raw.githubusercontent.com/cnsnake11/blog/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%9A%84%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.md\",\n        \"content\": \"请注意，本篇写的是react native的架构设计，如果你用react来开发web程序，本篇文章只能仅供参考，问题都没有在web上去考虑过。本篇较长，前面是目前flux开源框架的一些分析，后面是架构设计过程。您可以直奔主题。用RN最大的难题是设计思想的转变，以前的设计方法论已经不太适用了。而RN仅仅提供了view的框架，构建完整app的架构并没有直接提供。考虑目前遇到的如下问题，希望架构给出解决方案。\",\n        \"imgUrl\": \"http://p3.so.qhimg.com/bdr/_240_/t0147ebd93e5aec3513.jpg\"\n    },\n\n    {\n        \"title\": \"ReactNative组件状态设计思考\",\n        \"url\": \"https://raw.githubusercontent.com/cnsnake11/blog/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%BB%84%E4%BB%B6%E7%8A%B6%E6%80%81%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%80%83.md\",\n        \"content\": \"设计React组件与设计一个jquery组件或者一个原生js组件最大的区别就是状态的设计。按照原来设计组件的方法论，一个UI组件对外应该具有属性、事件、接口，对内具有内部属性，内部接口。1. 属性就像一份配置文件，描述了组件应该具有的功能、外观或者初始状态。2. 事件是组件在工作工程中，当满足某种条件的时候触发的回调函数3. 接口是组件对象的方法，可以让组件去执行某个任务\",\n        \"imgUrl\": \"http://p3.so.qhimg.com/bdr/_240_/t013b826be69e97f2d8.jpg\"\n    },\n\n    {\n        \"title\": \"ReactNative安卓首屏白屏优化\",\n        \"url\": \"https://raw.githubusercontent.com/cnsnake11/blog/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%AE%89%E5%8D%93%E9%A6%96%E5%B1%8F%E7%99%BD%E5%B1%8F%E4%BC%98%E5%8C%96.md\",\n        \"content\": \"公司现有app中部分模块使用reactnative开发，在实施的过程中，rn良好的兼容性，极佳的加载、动画性能，提升了我们的开发、测试效率，提升了用户体验。但是，在android中，当点击某个rn模块的入口按钮，弹出rn的activity到rn的页面展现出来的过程中，会有很明显的白屏现象，不同的机型不同（cpu好的白屏时间短），大概1s到2s的时间。注意，只有在真机上才会有此现象，在模拟器上没有此现象完全是秒开。ios上也是秒开，测试的最低版本是ios7，iphone4s。\",\n        \"imgUrl\": \"http://p3.so.qhimg.com/bdr/_240_/t01d369ccc394843f33.jpg\"\n    },\n\n    {\n        \"title\": \"ReactNative导航设计与实现\",\n        \"url\": \"https://raw.githubusercontent.com/cnsnake11/blog/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%AF%BC%E8%88%AA%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0.md\",\n        \"content\": \"关于reactnaitve的导航，官方提供了2个组件，NavigatorIOS和Navigator，其中官方并不推荐使用NavigatorIOS，它不是官方维护的，不能保证及时的更新和维护。所以本文中是以Navigator组件为基础，进行导航的设计和实现。Navigator的劣势：Navigator组件是纯js的实现，所以在页面进行转场动画的过程中，如果js不能保证在16ms内完成其它操作的话，转场动画会有卡顿现象，后面会介绍优化的方案。官方的Navigator组件使用方式较为灵活，本文的目的是选取一种最佳用法，并提取出通用功能应对常用场景，规范和设计项目中导航的使用。\",\n        \"imgUrl\": \"http://p2.so.qhimg.com/bdr/_240_/t018c3358b3c28ed25c.jpg\"\n    }\n]"
  },
  {
    "path": "其它/2",
    "content": "# 弱特征广告方案\n\n# 目标\n\n一般浏览器拦截页面内广告会使用css选择器，比如qq浏览器的规则：\n\n```\nhide=###app_mask,##.load-app,##div[class^=\"bbt-pregnant-timeline bbt-pregnant-timeline-\"],##div[class^=\"bottom-layer-\"],##div[class=\"bbt-header-dingtong\"],##div[class*=\"-header-pregnancy\"],##div[class*=\"-footer-pregnancy\"],##section[class$=\"-brand\"],##section[class$=\"-tool\"],##div[class$=\"-footer-expand\"]\nfilter=/base.js?v*&method=$third-party /www/default/base.js$third-party\n```\n从规则可以看出，弱特征化就是将class弱特征化。\n\n# 方案1 纯前端方案\n\n方案描述：\n1. 广告模板包括css，html和js，页面直接输出此模板\n2. 广告引擎js会读取模板，并将css中的class名称替换成随机，同时替换html和js的class名称\n3. 最后将替换后的模板嵌入到页面中\n\n优势：\n1. 每次算法都是随机，通过classname来定位广告基本不可能\n\n劣势：\n1. 插入html的位置不好确定，位置可能会成为新的特征\n2. 广告模板是特征很强的元素，不能加密，直接输出到html中，线索被暴露了\n3. 广告模板转换为真正的广告html是一个关键路径方法，理论上，可以通过上述线索较为容易的找到，找到后，浏览器引擎可以在方法执行前，将此方法置空\n4. 纯前端渲染，页面会有一定时间的阻塞\n\n# 方案2 前后端结合方案\n\n方案描述：\n\n\n优势：\n1.\n\n劣势：\n1. css文件增大，浪费了流量\n2. js编码复杂一些，不过广告功能一般比较简单，可以接受\n3. 如果广告拦截使用枚举的方式和你死磕，好像没有什么好的办法，因为换一组盐，就要重新生成css文件，流量较为浪费。\n4. 盐如果发生了变化，\n\n\n\n\n\n"
  },
  {
    "path": "其它/Building the DOM faster: speculative parsing, async, defer and preload(part 1).md",
    "content": "# 【翻译】Building the DOM faster: speculative parsing, async, defer and preload (part 1)\n\n# 更快的dom渲染：预解析，异步，延迟和提前加载 ~ 第一部分\n\n原文地址：https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/\n\nIn 2017, the toolbox for making sure your web page loads fast includes everything from minification and asset optimization to caching, CDNs, code splitting and tree shaking. However, you can get big performance boosts with just a few keywords and mindful code structuring, even if you’re not yet familiar with the concepts above and you’re not sure how to get started.\n\n优化网页打开速度的办法有很多，比如，压缩静态资源文件，使用缓存，使用cdn，代码拆分等。然而，尽管你可能不了解上述的办法，你也可以通过一些少量的代码和特定的代码结构来大幅提升网页的性能。\n\nThe fresh web standard ``` <link rel=\"preload\"> ```, that allows you to load critical resources faster, is coming to Firefox later this month. You can already try it out in Firefox Nightly or Developer Edition, and in the meantime, this is a great chance to review some fundamentals and dive deeper into performance associated with parsing the DOM.\n\n最新的web标准提供了``` <link rel=\"preload\"> ```的写法，可以更快的加载特定的资源，这个特性在FIrefox中即将支持。现在，可以在firefox的nightly版本或者开发者版本中试用，同时在试用的时候，我们可以深入研究一下浏览器解析dom的细节。\n\nUnderstanding what goes on inside a browser is the most powerful tool for every web developer. We’ll look at how browsers interpret your code and how they help you load pages faster with speculative parsing. We’ll break down how defer and async work and how you can leverage the new keyword preload.\n\n其实，对浏览器渲染原理的深入理解才是web开发者最强大的武器。在这篇文章中，将会介绍浏览器是如何解析代码，并且是如何使用预解析来提升页面的加载速度的。还会对defer和async的工作原理进行说明，并且会介绍最新的属性preload。\n\n# Building blocks\n\nHTML describes the structure of a web page. To make any sense of the HTML, browsers first have to convert it into a format they understand – the Document Object Model, or DOM. Browser engines have a special piece of code called a parser that’s used to convert data from one format to another. An HTML parser converts data from HTML into the DOM.\n\nHTML语言描述了网页的结构。为了解析html，浏览器首先会将其转换为文档对象模型（Document Object Model），简称DOM。浏览器引擎有一个专门的模块来处理这种转换，这个模块称为html解析器。\n\nIn HTML, nesting defines the parent-child relationships between different tags. In the DOM, objects are linked in a tree data structure capturing those relationships. Each HTML tag is represented by a node of the tree (a DOM node).\n\nhtml语言可以描述出不同标签之间的父子关系。在dom中，使用树形结构来描述这种父子关系。每一个html标签都会被映射到dom树中的一个节点上。\n\nThe browser builds up the DOM bit by bit. As soon as the first chunks of code come in, it starts parsing the HTML, adding nodes to the tree structure.\n\n浏览器会一点一点的增量构建dom。只要html的代码一抵达浏览器，就会立刻开始一点一点的构建dom树，而无需等待html代码全部下载完。\n\n![](media/15118651219648.gif)\n\nThe DOM has two roles: it is the object representation of the HTML document, and it acts as an interface connecting the page to the outside world, like JavaScript. When you call document.getElementById(), the element that is returned is a DOM node. Each DOM node has many functions you can use to access and change it, and what the user sees changes accordingly.\n\nDOM树有2个主要的职责：他是html文档的对象结构，并且，他是页面与外界交互的桥梁。当你使用document.getELementById()方法，返回的就是一个dom节点。每一个dom节点都有很多操作接口，可以对其进行更改。\n\n![](media/2.gif)\n\nCSS styles found on a web page are mapped onto the CSSOM – the CSS Object Model. It is much like the DOM, but for the CSS rather than the HTML. Unlike the DOM, it cannot be built incrementally. Because CSS rules can override each other, the browser engine has to do complex calculations to figure out how the CSS code applies to the DOM.\n\ncss样式表会被映射为CSSOM---css对象模型（CSS Object Model）。他与DOM类似，但是，cssom不能被一点点的增量构建。因为，css规则存在互相覆盖的可能性，所以，浏览器引擎会进行复杂的计算来分析出最后生效的css代码。\n\n![](media/15120386476962.jpg)\n\n# The history of the ```<script>``` tag. ```<script>```标签的历史\n\nAs the browser is constructing the DOM, if it comes across a ```<script>```...```</script>``` tag in the HTML, it must execute it right away. If the script is external, it has to download the script first.\n\n当浏览器构建dom的过程中，如果遇到了```<script>```...```</script>```标签段，必须要将其中的js代码先执行完。如果是引用的方式的js，还必须要将其先下载下来。\n\nBack in the old days, in order to execute a script, parsing had to be paused. It would only start up again after the JavaScript engine had executed code from a script.\n\n为了执行script代码，html解析dom的工作必须要暂停，当js的代码执行完之后才会继续解析dom。\n\n![](media/15120386940727.jpg)\n\nWhy did the parsing have to stop? Well, scripts can change both the HTML and its product―the DOM. Scripts can change the DOM structure by adding nodes with document.createElement(). To change the HTML, scripts can add content with the notorious document.write() function. It’s notorious because it can change the HTML in ways that can affect further parsing. For example, the function could insert an opening comment tag making the rest of the HTML invalid.\n\n这里解释一下执行js的时候为啥要停止dom的解析。因为js是可以改变html同时也是可以改变dom的。比如，js可以通过添加节点来更改dom树结构的改变。比如，js可以通过document.write()方法来改变html，虽然这个方法是不建议使用的。不建议使用的原因是，这个方法有可能会影响未来的dom解析。比如，使用document.write()插入一个html注释符的左半边来让后面的html都失效。\n\n![](media/3.gif)\n\nScripts can also query something about the DOM, and if that happens while the DOM is still being constructed, it could return unexpected results.\n\njs脚本也可以对dom节点进行查询，如果在查询的过程中进行dom构建，很可能会返回异常的结果。\n\n![](media/15120388518927.jpg)\n\n\ndocument.write() is a legacy function that can break your page in unexpected ways and you shouldn’t use it, even though browsers still support it. For these reasons, browsers have developed sophisticated techniques to get around the performance issues caused by script blocking that I will explain shortly.\n\ndocument.write()已经是很老旧的方法了，同时这个方法会对页面带来不好的影响，是不推荐使用的，尽管目前的浏览器还支持这个方法。因而，浏览器规范的制定者们也在探索更佳的解决方案来解决这种由于js脚本带来的对dom渲染的阻塞，后边就会介绍这些方案。\n\n\n"
  },
  {
    "path": "其它/Building the DOM faster: speculative parsing, async, defer and preload(part 2).md",
    "content": "# 【翻译】Building the DOM faster: speculative parsing, async, defer and preload (part 2)\n\n# 更快的dom构建：预解析，异步，延迟和提前加载 ~ 第二部分\n\n原文地址：https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/\n\n# What about CSS? css的情况如何呢？\n\nJavaScript blocks parsing because it can modify the document. CSS can’t modify the document, so it seems like there is no reason for it to block parsing, right?\n\njs会阻断页面的dom构建是因为js可以修改html和dom。那么，css不能修改html和dom，所以理论上css应该不会阻断页面dom的构建，对吗？\n\nHowever, what if a script asks for style information that hasn’t been parsed yet? The browser doesn’t know what the script is about to execute—it may ask for something like the DOM node’s background-color which depends on the style sheet, or it may expect to access the CSSOM directly.\n\n然而，当js需要css的信息但是css还未解析完怎么办呢？浏览器并不知道js在执行的过程中是否需要访问到类似节点背景色这种依赖css的信息。\n\n![](media/15120388873174.jpg)\n\nBecause of this, CSS may block parsing depending on the order of external style sheets and scripts in the document. If there are external style sheets placed before scripts in the document, the construction of DOM and CSSOM objects can interfere with each other. When the parser gets to a script tag, DOM construction cannot proceed until the JavaScript finishes executing, and the JavaScript cannot be executed until the CSS is downloaded, parsed, and the CSSOM is available.\n\n所以，css是否会阻塞dom的构建要看引入css和js的顺序。如果，css是在js之前被引入的，dom的构建和cssom的构建互不影响。当解析到script标签，dom构建就会停下来直到js下载并执行完毕，同时，如果cssom还没有解析完成，js的执行也会被阻塞。下图很清楚的说明了这种关系：\n\n![](media/15120389027978.jpg)\n\nAnother thing to keep in mind is that even if the CSS doesn’t block DOM construction, it blocks rendering. The browser won’t display anything until it has both the DOM and the CSSOM. This is because pages without CSS are often unusable. If a browser showed you a messy page without CSS, then a few moments later snapped into a styled page, the shifting content and sudden visual changes would make a turbulent user experience.\n\n另外一个需要注意的点是尽管css不会阻断DOM的构建，但是却会阻断页面的渲染。直到dom和cssom都准备完毕了，页面才会显示出来。这是因为，未加载完css的页面是不稳定的。如果浏览器一开始加载了一个没有css的页面，然后很快样式又加载了，这个页面样子转换的过程给用户的体验很不好。\n\nThat poor user experience has a name – Flash of Unstyled Content or FOUC\n\n这种不佳的用户体验有一个名字 --- 无样式闪屏 或者 FOUC\n\nTo get around these issues, you should aim to deliver the CSS as soon as possible. Recall the popular “styles at the top, scripts at the bottom” best practice? Now you know why it was there!\n\n所以，为了让页面更快的展现出来，需要尽快的让css加载完成。还记得网页优化军规中的“将css放在页面上面，script放在页面下面”吗？以上就是这样做的原因。\n\n# Back to the future – speculative parsing 更先进的算法--预解析\n\nPausing the parser whenever a script is encountered means that every script you load delays the discovery of the rest of the resources that were linked in the HTML.\n\n遇到script标签就停止解析html的算法会延迟页面其它资源文件的下载。\n\nIf you have a few scripts and images to load, for example–\n\n如果有如下的代码：\n\n```\n<script src=\"slider.js\"></script>\n<script src=\"animate.js\"></script>\n<script src=\"cookie.js\"></script>\n<img src=\"slide1.png\">\n<img src=\"slide2.png\">\n```\n\n–the process used to go like this:\n\n会有如下的过程：\n\n![](media/15120389720745.jpg)\n\nThat changed around 2008 when IE introduced something they called “the lookahead downloader”. It was a way to keep downloading the files that were needed while the synchronous script was being executed. Firefox, Chrome and Safari soon followed, and today most browsers use this technique under different names. Chrome and Safari have “the preload scanner” and Firefox – the speculative parser.\n\n在2008年的时候，ie首先提出了一个预先下载的算法。在解析到srcipt的标签的时候仍然继续下载页面的其它资源。firefox和chrome还有safari很快也使用了这个算法。不同的浏览器给他们起了不同的名字，chrome和safari称其为the preload scanner【提前加载扫描器】，firefox称其为speculative parser【预解析】。\n\nThe idea is: even though it’s not safe to build the DOM while executing a script, you can still parse the HTML to see what other resources need to be retrieved. Discovered files are added to a list and start downloading in the background on parallel connections. By the time the script finishes executing, the files may have already been downloaded.\n\n这个思路很简单：虽然在执行js的过程中构建dom不安全，但是，可以继续解析html来下载其它的资源。这些资源会在后台并行的进行下载，当js执行完后，这些资源的下载可能已经完成。\n\nThe waterfall chart for the example above now looks more like this:\n\n这个算法的图如下：\n\n![](media/15120389914931.jpg)\n\nThe download requests triggered this way are called “speculative” because it is still possible that the script could change the HTML structure (remember document.write ?), resulting in wasted guesswork. While this is possible, it is not common, and that’s why speculative parsing still gives big performance improvements.\n\n这个算法被称为预解析的原因是，因为js可能会改变html的结构（比如使用document.write），所以被下载的资源有可能是浪费的。虽然可能会存在浪费，但是这种情况并不常见，所以这个预解析的算法可以极大的提升页面加载性能。\n\nWhile other browsers only preload linked resources this way, in Firefox the HTML parser also runs the DOM tree construction algorithm speculatively. The upside is that when a speculation succeeds, there’s no need to re-parse a part of the file to actually compose the DOM. The downside is that there’s more work lost if and when the speculation fails.\n\n在这个算法中，其它的浏览器都仅仅是执行下载，而firefox同时也会继续执行dom的构建。这样做的优势很明显，当预解析成功，dom的构建会更快的完成。但是，劣势也存在，当预解析失败，浏览器执行了很多无效的工作。\n\n\n"
  },
  {
    "path": "其它/Building the DOM faster: speculative parsing, async, defer and preload(part 3).md",
    "content": "# 【翻译】Building the DOM faster: speculative parsing, async, defer and preload (part 3)\n\n# 更快的dom构建：预解析，异步，延迟和提前加载 ~ 第三部分\n\n原文地址：https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/\n\n# (Pre)loading stuff 预解析的内容\n\nThis manner of resource loading delivers a significant performance boost, and you don’t need to do anything special to take advantage of it. However, as a web developer, knowing how speculative parsing works can help you get the most out of it.\n\n预解析算法提升了浏览器加载页面的性能，并且，无需开发者做什么特殊的工作就可以享用到这个算法带来的性能提升。但是，作为web开发者来说，能深刻理解预解析算法的原理和工作过程才能更好的利用这个算法。\n\nThe set of things that can be preloaded varies between browsers. All major browsers preload:\n\n1. scripts\n1. external CSS\n1. and images from the ```<img>``` tag\n\n不同的浏览器会预解析不同的内容。所有主流浏览器都预解析如下内容：\n\n1. scripts\n1. 外部CSS\n1. 使用```<img>``` 标签定义的图片\n\n\nFirefox also preloads the poster attribute of video elements, while Chrome and Safari preload @import rules from inlined styles.\n\nfirefox也会预解析vidoe标签上定义的poster，而chrome和safari会预解析通过@import引入的css。\n\nThere are limits to how many files a browser can download in parallel. The limits vary between browsers and depend on many factors, like whether you’re downloading all files from one or from several different servers and whether you are using HTTP/1.1 or HTTP/2 protocol. To render the page as quickly as possible, browsers optimize downloads by assigning priority to each file. To figure out these priorities, they follow complex schemes based on resource type, position in the markup, and progress of the page rendering.\n\n预解析内容的同时并行下载数量是有限制的。这个限制不同浏览器不同，在不同的情况下也会不同，比如说，下载的资源是同一个域名还是多个域名，或者使用的是HTTP/1.1还是HTTP/2的协议。浏览器会对预加载内容的下载顺序进行优化来保证更快的页面加载速度。为了计算内容的下载优先级顺序，浏览器会通过资源的类型位置和当前页面的状态来进行计算。\n\nWhile doing speculative parsing, the browser does not execute inline JavaScript blocks. This means that it won’t discover any script-injected resources, and those will likely be last in line in the fetching queue.\n\n当执行预加载算法的时候，浏览器将不会执行页面上的行内js。这意味着如果在页面的行内js中有js注入的下载资源，他们将会在执行完预加载后才会执行加载。比如下面的这种代码：\n\n```\nvar script = document.createElement('script');\nscript.src = \"//somehost.com/widget.js\";\ndocument.getElementsByTagName('head')[0].appendChild(script);\n```\n\nYou should make it easy for the browser to access important resources as soon as possible. You can either put them in HTML tags or include the loading script inline and early in the document. However, sometimes you want some resources to load later because they are less important. In that case, you can hide them from the speculative parser by loading them with JavaScript late in the document.\n\n如果需要让浏览器立即下载某些主要的资源，你可以将其放在页面的最顶部。如果某些资源并不重要，并不影响页面的展现，你可以将其通过行内js来加载，并将这段行内js放到页面的最底部。\n\n# defer and async\n\nStill, synchronous scripts blocking the parser remains an issue. And not all scripts are equally important for the user experience, such as those for tracking and analytics. Solution? Make it possible to load these less important scripts asynchronously.\n\n通过预解析js对文件下载的阻断已经解决了，但是js阻断dom构建还仍然是个问题。其实，并不是所有的js都在页面加载过程是必须的，比如说一些记录日志的js。我们可以让其不阻断页面dom的构建。\n\nThe defer and async attributes were introduced to give developers a way to tell the browser which scripts to handle asynchronously.\n\ndefer和async属性可以设置script标签进行异步的加载，而不影响页面dom的构建。\n\nBoth of these attributes tell the browser that it may go on parsing the HTML while loading the script “in background”, and then execute the script after it loads. This way, script downloads don’t block DOM construction and page rendering. Result: the user can see the page before all scripts have finished loading.\n\n这2个属性都会使浏览器在遇到script标签后不停止对dom的构建和对页面的渲染。所以，用户看到页面的时候，设置了这2个属性的js可能还没有执行完成。\n\nThe difference between defer and async is which moment they start executing the scripts.\n\n这2个属性的区别是js执行的时机。\n\ndefer was introduced before async. Its execution starts after parsing is completely finished, but before the DOMContentLoaded event. It guarantees scripts will be executed in the order they appear in the HTML and will not block the parser.\n\n一般更推荐使用defer属性，而不是async属性。defer属性的js会在页面完全的解析渲染完成的时候执行，并且是在DOMContentLoaded事件之前执行。同时，defer属性会保证按照在页面中定义的顺序执行，而且也保证绝对不会阻断页面的渲染。如图：\n\n![](media/15120391161617.jpg)\n\nasync scripts execute at the first opportunity after they finish downloading and before the window’s load event. This means it’s possible (and likely) that async scripts are not executed in the order in which they appear in the HTML. It also means they can interrupt DOM building.\n\nasync属性的js仅仅是下载不阻断dom构建，执行仍会阻断dom构建，它执行的时机是当js一下载完成就立即执行，这时会阻断dom的构建，而且也不保证会按照js在页面中定义的顺序执行。\n\nWherever they are specified, async scripts load at a low priority. They often load after all other scripts, without blocking DOM building. However, if an async script finishes downloading sooner, its execution can block DOM building and all synchronous scripts that finish downloading afterwards.\n\n定义了async属性的script标签的加载优先级很低，无论他们定义在页面的什么位置。他们经常会在所有其他不阻断dom构建的js加载完后才被加载。然而，一旦一个async的js下载完，他的执行仍然会阻断dom的构建，也会阻断页面上之后其他同步js的下载。\n\n![](media/15120391311853.jpg)\n\nNote: Attributes async and defer work only for external scripts. They are ignored if there’s no src.\n\n注意：async和defer属性都只对外部js生效，如果script标签上没定义src，将会忽略这2个属性。\n\n# preload\n\nasync and defer are great if you want to put off handling some scripts, but what about stuff on your web page that’s critical for user experience? Speculative parsers are handy, but they preload only a handful of resource types and follow their own logic. The general goal is to deliver CSS first because it blocks rendering. Synchronous scripts will always have higher priority than asynchronous. Images visible within the viewport should be downloaded before those below the fold. And there are also fonts, videos, SVGs… In short – it’s complicated.\n\nasync和defer可以处理掉那些页面初始化过程中不重要的scripts，那么那些重要的资源还有更好的处理办法吗？浏览器的预解析引擎虽然用起来简单，但是他只能解析小部分的资源类型，而且算法也较为固定。我们的目标是让css尽早下载解析完成，同步的脚本的优先级高于异步脚本，在视区内的图片应该尽早下载，而且还有像字体文件，视频，svg等等资源需要考虑，总之，情况很复杂。\n\nAs an author, you know which resources are the most important for rendering your page. Some of them are often buried in CSS or scripts and it can take the browser quite a while before it even discovers them. For those important resources you can now use ```<link rel=\"preload\">``` to communicate to the browser that you want to load them as soon as possible.\n\n只有开发者才能知道哪些资源对页面初始化过程是最重要的。这些资源很可能是通过css或者js来引入的，浏览器对他们的下载时机是很晚的。那么现在，你可以使用```<link rel=\"preload\">```来加载这类资源，浏览器会在第一时间加载这些资源。\n\nAll you need to write is:\n\n你可以这样写：\n\n```\n<link rel=\"preload\" href=\"very_important.js\" as=\"script\">\n```\n\nYou can link pretty much anything and the as attribute tells the browser what it will be downloading. Some of the possible values are:\n\n这中preload的写法支持很多种资源类型，比如：\n\n1. script\n1. style\n1. image\n1. font\n1. audio\n1. video\n\nYou can check out the rest of the content types on MDN.\n\n你可以查看MDN的官方文档：https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content#What_types_of_content_can_be_preloaded\n\nFonts are probably the most important thing that gets hidden in the CSS. They are critical for rendering the text on the page, but they don’t get loaded until browser is sure that they are going to be used. That check happens only after CSS has been parsed, and applied, and the browser has matched CSS rules to the DOM nodes. This happens fairly late in the page loading process and it often results in an unnecessary delay in text rendering. You can avoid it by using the preload attribute when you link fonts.\n\n字体文件就是被隐藏在css文件中并且页面初始化过程很重要的资源。但是，加载字体文件的时机一般都比较晚，因为需要等css先加载完成，并且应用到dom节点上之后，字体文件才会被加载。所以，文本的渲染可能会有延迟的感觉。现在，可以通过使用preload来尽早加载字体文件了。\n\nOne thing to pay attention to when preloading fonts is that you also have to set the crossorigin attribute even if the font is on the same domain:\n\n加载字体文件的时候，通常要加一个crossorigin的属性，这是固定写法。\n\n```\n<link rel=\"preload\" href=\"font.woff\" as=\"font\" crossorigin>\n```\n\nThe preload feature has limited support at the moment as the browsers are still rolling it out, but you can check the progress here.\n\n目前preload属性只有少部分的浏览器支持，可以通过 https://caniuse.com/#search=preload 查看。\n\n# Conclusion 总结\n\nBrowsers are complex beasts that have been evolving since the 90s. We’ve covered some of the quirks from that legacy and some of the newest standards in web development. Writing your code with these guidelines will help you pick the best strategies for delivering a smooth browsing experience.\n\n浏览器重90年代发展到现在已经越来越复杂了。很多遗留的特性和未来的标准混杂在一起。通过这篇文章，你可以理解如何让页面的渲染更快速的原理。\n\n\n"
  },
  {
    "path": "其它/Designing Websites for iPhone X.md",
    "content": "# 【翻译】Designing Websites for iPhone X \n\n# 让网站适配 iphone X \n\n原文地址： https://webkit.org/blog/7929/designing-websites-for-iphone-x/\n\nThe section below about safe area insets was updated on Oct 31, 2017 to reflect changes in the iOS 11.2 beta.\n\n以下关于safe area insets的内容已经在20171031的时候进行了修改，会在ios 11.2beta中表现出来。\n\nOut of the box, Safari displays your existing websites beautifully on the edge-to-edge display of the new iPhone X. Content is automatically inset within the display’s safe area so it is not obscured by the rounded corners, or the device’s sensor housing.\n\n无需任何修改，在ihponeX中safari可以将网页显示的很美观。网页的内容，会在safe area中显示出来，所以，网页的内容不会被圆角和设备的传感器条挡住。\n\nThe inset area is filled with the page’s background-color (as specified on the <body>or <html> elements) to blend in with the rest of the page. For many websites, this is enough. If your page has only text and images above a solid background color, the default insets will look great.\n\n安全区域以外的地方（也就是屏幕上下奇形怪状的地方），会自动被填充背景色（在body或者html元素上定义的背景色）。在大多数情况下，这样进行兼容就足够了。只要你的页面是固定背景色的，这种默认的填充方式就足够用了。\n\nOther pages — especially those designed with full-width horizontal navigation bars, like the page below — can optionally go a little further to take full advantage of the features of the new display. The iPhone X Human Interface Guidelines detail a few of the general design principles to keep in mind, and the UIKit documentation discusses specific mechanisms native apps can adopt to ensure that they look good. Your website can make use of a few similar new pieces of WebKit API introduced in iOS 11 to take full advantage of the edge-to-edge nature of the display.\n\n另外的一些页面，特别是那种有导航条的-可以有选择的进行更深层次的优化来利用新的显示特性。在ios11中，你的网站可以使用新的webkit api来适配显示。\n\nWhile reading this post you can tap on any of the images to visit a corresponding live demo page and take a peek at the source code.\n\n本篇文章的图片都可以点击到对应的demo页面。【注：请点击原文中的图片】\n\n\n![](media/15099614906501.png)\n图：safari默认的兼容行为。\n\n\n# Using the Whole Screen 使用全部的屏幕\n\n\nThe first new feature is an extension to the existing viewport meta tag called viewport-fit, which provides control over the insetting behavior. viewport-fit is available in iOS 11.\n\nviewport标签有一个新属性viewport-fit，这个属性可以控制视图的显示行为。viewport-fit在ios11上可以使用。\n\nThe default value of viewport-fit is auto, which results in the automatic insetting behavior seen above. In order to disable that behavior and cause the page to lay out to the full size of the screen, you can set viewport-fit to cover. After doing so, ourviewport meta tag now looks like this:\n\nviewport-fit的默认值是auto，这个值的行为就像上面的截图那些，页面不会撑到屏幕边缘。设置这个值为cover就会让页面覆盖整个页面，包括屏幕边缘。示例代码：\n\n```\n<meta name='viewport' content='initial-scale=1, viewport-fit=cover’>\n```\n\nAfter reloading, the navigation bar looks much better, running from edge to edge. However, it is immediately clear why it is important to respect the system’s safe area insets: some of the page’s content is obscured by the device’s sensor housing, and the bottom navigation bar is very hard to use.\n\n页面刷新之后，导航条已经可以撑满屏幕边缘了。同时，问题也很明显：内容被传感器条挡住了，并且，底部的导航条和虚拟按键也重叠了。\n\n![](media/15099634742457.png)\n图：Use `viewport-fit=cover` to fill the whole screen.使用viewport-fit=cover来铺满整个屏幕。\n\n# Respecting the Safe Areas 安全区域\n\nThe next step towards making our page usable again after adopting viewport-fit=cover is to selectively apply padding to elements that contain important content, in order to ensure that they are not obscured by the shape of the screen. This will result in a page that takes full advantage of the increased screen real estate on iPhone X while adjusting dynamically to avoid the corners, sensor housing, and indicator for accessing the Home screen.\n\n在使用了viewport-fit=cover之后，需要在一些地方加padding来避免被遮挡。如果你能动态的适配iPhone X屏幕的圆角，顶部传感器条，底部虚拟按键，那么就可以完全的享用到iPhone X的大屏幕。\n\n![](media/15099656914598.png)\n图：The safe and unsafe areas on iPhone X in the landscape orientation, with insets indicated.iPhone X的横屏安全区域，及几个固定变量的示意图。\n\nTo achieve this, WebKit in iOS 11 includes a new CSS function, env(), and a set of four pre-defined environment variables, safe-area-inset-left, safe-area-inset-right, safe-area-inset-top, and safe-area-inset-bottom. When combined, these allow style declarations to reference the current size of the safe area insets on each side.\n\n为了达成动态自适应的目的，ios 11 的webkit提供了一个新的css方法:env(),和有四个预先定义的环境变量，safe-area-inset-left, safe-area-inset-right, safe-area-inset-top, and safe-area-inset-bottom。使用这些变量，就可以获得屏幕的安全区域距离屏幕边缘的距离。\n\nThe env() function shipped in iOS 11 with the name constant(). Beginning with Safari Technology Preview 41 and the iOS 11.2 beta, constant() has been removed and replaced with env(). You can use the CSS fallback mechanism to support both versions, if necessary, but should prefer env() going forward.\n\nenv()方法是在ios11中被支持的，一开始他被命名为constant()。在Safari Technology Preview 41 and the iOS 11.2 beta的版本中，constant()已经被重命名为env()。你可以使用css的权重机制来适配所有的版本，如果不是必须的话，使用env()来适配最新的版本即可。\n\nenv() works anywhere var() does — for example, inside the padding properties:\n\nenv()使用的方式和var()基本一致，例如，在定义padding的时候：\n\n```\n.post {\n    padding: 12px;\n    padding-left: env(safe-area-inset-left);\n    padding-right: env(safe-area-inset-right);\n}\n\n```\n\nFor browsers that do not support env(), the style rule that includes it will be ignored; for this reason, it is important to continue to separately specify fallback rules for any declarations using env().\n\n当浏览器不支持env()的时候，这条样式会失效；所以，要将这种使用env()的样式，独自定义。\n\n![](media/15100240680308.png)\n图：Respect safe area insets so that important content is visible.使用了环境变量的适配效果。\n\n# Bringing It All Together, With min() and max() 使用min()和max()\n\nThis section covers features that are available starting in Safari Technology Preview 41 and the iOS 11.2 beta.\n\n本节讲的内容在 Safari Technology Preview 41 and the iOS 11.2 beta中开始支持。\n\nIf you adopt safe area insets in your website design, you might notice that it is somewhat difficult to specify that you want a minimum padding in addition to the safe area inset. In the page above, where we replaced our 12px left padding with env(safe-area-inset-left), when we rotate back to portrait, the left safe area inset becomes 0px, and the text sits immediately adjacent to the screen edge.\n\n当使用了安全区域变量，并不能解决所有的问题。比如，上面的页面，当横屏的时候， env(safe-area-inset-left)是有值的，当竖屏的时候，env(safe-area-inset-left)=0px，此时，文本就会挤到屏幕的边缘了。\n\n![](media/15100270621233.png)\n图：Safe area insets are not a replacement for margins.使用Safe area insets带来的问题。\n\nTo solve this, we want to specify that our padding should be the default padding or the safe area inset, whichever is greater. This can be achieved with the brand-new CSS functions min() and max() which will be available in a future Safari Technology Preview release. Both functions take an arbitrary number of arguments and return the minimum or maximum. They can be used inside of calc(), or nested inside each other, and both functions allow calc()-like math inside of them.\n\n解决这个问题，其实是需要给padding设置一个默认值，当safe-area-inset-left有值的时候，设置成safe-area-inset-left，没值的时候使用默认值。我们可以使用一组新的css函数min() and max()来解决这个问题。这2个函数可以接受任意个数的参数，并返回最大或者最小的那个。他们也可以用到calc()中，也可以相互嵌套使用。\n\nFor this case, we want to use max():\n\n解决上述问题的示例：\n\n```\n@supports(padding: max(0px)) {\n    .post {\n        padding-left: max(12px, env(safe-area-inset-left));\n        padding-right: max(12px, env(safe-area-inset-right));\n    }\n}\n```\n\nIt is important to use @supports to feature-detect min and max, because they are not supported everywhere, and due to CSS’s treatment of invalid variables, to not specify a variable inside your @supports query.\n\n注意：@supports语句可以检查是否支持max，但不要在其中使用变量，例如：@supports(padding: max(env(safe-area-inset-left)))，因为css对待无效的变量是返回默认值，也就是这个例子中的padding的初始值。【此处具体的细节可以参考：https://drafts.csswg.org/css-variables/#invalid-variables，本文最后也翻译了一下这块。】\n\nIn our example page, in portrait orientation, env(safe-area-inset-left) resolves to 0px, so the max() function resolves to 12px. In landscape, when env(safe-area-inset-left) is larger due to the sensor housing, the max() function will resolve to that size instead, ensuring that the important content within is always visible.\n\n在上述的示例中，当竖屏时， env(safe-area-inset-left)是0，所以max函数返回了12px。当横屏时，env(safe-area-inset-left)的值会大于12，所以，max函数会返回env(safe-area-inset-left)的值。这就保证了页面的动态适应性。\n\n![](media/15100271200055.png)\n图：Use max() to combine safe area insets with traditional margins.使用max函数来保证竖屏的兼容。\n\nExperienced web developers might have previously encountered the “CSS locks” mechanism, commonly used to clamp CSS properties to a particular range of values. Using min() and max() together makes this much easier, and will be very helpful in implementing effective responsive designs in the future.\n\nmin() and max()函数可以使用到更多的场景中，他们可以帮助开发者更容易的创建兼容性更好的页面。\n\n# 参考：无效变量的说明\n\n原文地址：https://drafts.csswg.org/css-variables/#invalid-variables\n\n直接翻译了3.1节中的例子，比较直观\n\nFor example, in the following code:\n\n例如，如下的代码：\n\n```\n:root { --not-a-color: 20px; }\np { background-color: red; }\np { background-color: var(--not-a-color); }\n\n```\n\nthe <p> elements will have transparent backgrounds (the initial value for background-color), rather than red backgrounds. The same would happen if the custom property itself was unset, or contained an invalid var() function.\n\np元素的背景将是transparent（也就是background-color的初始值），而不是红色。这种变量值无效的情况和没写background-color的表现是一致的。\n\nNote the difference between this and what happens if the author had just written background-color: 20px directly in their stylesheet - that would be a normal syntax error, which would cause the rule to be discarded, so the background-color: red rule would be used instead.\n\n注意这种情况和直接写错background-color: 20px的区别，如果直接写错成ackground-color: 20px，会导致错误的这条样式失效，background-color: red仍会生效。\n\nNote: The invalid at computed-value time concept exists because variables can’t \"fail early\" like other syntax errors can, so by the time the user agent realizes a property value is invalid, it’s already thrown away the other cascaded values.\n\n说明：不合法的变量值问题是因为变量的出错时机是比较晚的，所以，当浏览器识别到变量值无效的时候，已经将其它的有效的之前定义的值抛弃了。\n\n\n补充说明：最后这里说的是【变量】值不合法才会导致整条样式失效。如果是直接不支持env或者不支持env里写的属性名称（这些都不是变量的情况），就不会导致之前的有效定义被抛弃（在chrome里做过测试）。\n\n\n\n"
  },
  {
    "path": "其它/FlatList.md",
    "content": "高性能的简单列表组件，支持下面这些常用的功能：\n\n完全跨平台。\n支持水平布局模式。\n行组件显示或隐藏时可配置回调事件。\n支持单独的头部组件。\n支持单独的尾部组件。\n支持自定义行间分隔线。\n支持下拉刷新。\n支持上拉加载。\n支持跳转到指定行（ScrollToIndex）。\n\n\n本组件实质是基于<VirtualizedList>组件的封装，因此也有下面这些需要注意的事项：\n\n1. 当某行滑出渲染区域之外后，其内部状态将不会保留。请确保你在行组件以外的地方保留了数据。\n1. 为了优化内存占用同时保持滑动的流畅，列表内容会在屏幕外异步绘制。这意味着如果用户滑动的速度超过渲染的速度，则会先看到空白的内容。这是为了优化不得不作出的妥协，而我们也在设法持续改进。\n1. 本组件继承自PureComponent而非通常的Component，这意味着如果其props在浅比较中是相等的，则不会重新渲染。所以请先检查你的renderItem函数所依赖的props数据（包括data属性以及可能用到的父组件的state），如果是一个引用类型（Object或者数组都是引用类型），则需要先修改其引用地址（比如先复制到一个新的Object或者数组中），然后再修改其值，否则界面很可能不会刷新。（译注：这一段不了解的朋友建议先学习下js中的基本类型和引用类型。）\n1. 默认情况下每行都需要提供一个不重复的key属性。你也可以提供一个keyExtractor函数来生成key。\n\n\n# 常用属性\n\n#### data: ?Array<ItemT> \n\n为了简化起见，data属性目前只支持普通数组。如果需要使用其他特殊数据结构，例如immutable数组，请直接使用更底层的VirtualizedList组件。\n\n\n#### getItemLayout?: (data: ?Array<ItemT>, index: number) =>\n  {length: number, offset: number, index: number} \n\ngetItemLayout是一个可选的优化，用于避免动态测量内容尺寸的开销，不过前提是你可以提前知道内容的高度。如果你的行高是固定的，getItemLayout用起来就既高效又简单，类似下面这样：\n\ngetItemLayout={(data, index) => ( {length: 行高, offset: 行高 * index, index} )}\n注意如果你指定了SeparatorComponent，请把分隔线的尺寸也考虑到offset的计算之中。\n\n\n#### initialNumToRender: number \n\n指定一开始渲染的元素数量，最好刚刚够填满一个屏幕，这样保证了用最短的时间给用户呈现可见的内容。注意这第一批次渲染的元素不会在滑动过程中被卸载，这样是为了保证用户执行返回顶部的操作时，不需要重新渲染首批元素。\n\n\n#### initialScrollIndex?: ?number \n\n开始时屏幕顶端的元素是列表中的第 initialScrollIndex 个元素, 而不是第一个元素。设置这个属性会关闭对“滚动到顶端”这个动作的优化（参见VirtualizedList 的 initialNumToRender 属性)。位于 initialScrollIndex 位置的元素总是会被立刻渲染。需要先设置 getItemLayout 属性。\n\n#### keyExtractor: (item: ItemT, index: number) => string \n\n此函数用于为给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体，以便在刷新时能够确定其变化的位置，减少重新渲染的开销。若不指定此函数，则默认抽取item.key作为key值。若item.key也不存在，则使用数组下标。\n\n\n#### onEndReached?: ?(info: {distanceFromEnd: number}) => void \n\n当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用。\n\n#### onEndReachedThreshold?: ?number \n\n决定当距离内容最底部还有多远时触发onEndReached回调。注意此参数是一个比值而非像素单位。比如，0.5表示距离内容最底部的距离为当前列表可见长度的一半时触发。\n\n# 常用方法\n\n#### scrollToEnd(params?: object) \n\n滚动到底部。如果不设置getItemLayout属性的话，可能会比较卡。\n\n#### scrollToIndex(params: object) \n\n将位于指定位置的元素滚动到可视区的指定位置，当 viewPosition 为 0 时将它滚动到屏幕顶部，为 1 时将它滚动到屏幕底部，为 0.5 时将它滚动到屏幕中央。\n\n如果不设置getItemLayout属性的话，无法跳转到当前可视区域以外的位置。\n\n#### scrollToItem(params: object) \n\n这个方法会顺序遍历元素。尽可能使用 scrollToIndex 。 如果不设置getItemLayout属性的话，可能会比较卡。\n\n#### scrollToOffset(params: object) \n\n滚动列表到指定的偏移（以像素为单位），等同于 ScrollView 的 scrollTo 方法。\n"
  },
  {
    "path": "其它/How TensorFlow Lite Brings Machine Learning to Mobile Devices.md",
    "content": "# 【翻译】How TensorFlow Lite Brings Machine Learning to Mobile Devices\n\n# TensorFlow Lite --- 赋予移动设备机器学习的能力\n\n原文地址： http://www.tomsitpro.com/articles/tensorflow-light-machine-learning-mobile,1-3670.html\n\nAndroid developers will appreciate TensorFlow Lite, which promises to give them machine learning tools for mobile apps.\n\n安卓的开发者将会使用到TensorFlow Lite用于移动设备机器学习的开发。\n\n![](media/15090124219258.jpg)\n\nMachine learning requires incredibly complex computing power and resources. While research and artificial intelligence breakthroughs gain most of the attention, the most dramatic difference in daily computing happens on mobile devices. It's smartphones where the user is typically interacting with information that is personal, and would benefit from a computer that anticipates one's needs.\n\n机器学习需要大量的计算资源。伴随着人工智能领域研究的突破，移动设备的计算方式也在发生着巨大的变化。我们通过智能手机获取信息的方式，可以从计算机的智能预测需求中获益。\n\nBusinesses are counting on machine learning to enhance productivity and do things that were previously impossible. Google recently offered some insights into how the company wants to affect change with its own mobile operating system.\n\n商业可以利用机器学习提高生产力，并且做到之前做不到的事情。谷歌最近提出了一些观点：公司如何改善自己的移动操作软件。\n\nAndroid developers should keep an eye on TensorFlow Lite, which is promised to give them machine learning tools for building such smarts into their own mobile applications. It's an optimized version of TensorFlow, which is Google's popular open-source library that enables researchers and developers to apply machine learning to their applications.\n\n安卓开发者应该时刻关注TensorFlow Lite，它会为应用带来机器学习的能力。TensorFlow Lite是TensorFlow的优化版，TensorFlow是谷歌的开源机器学习框架。\n\nThe news about TensorFlow Lite was first announced at Google I/O, where the company illustrated the potentials for machine learning on mobile devices by showing off how Google Lens will be able to recognize objects and infuse artificial intelligence in other ways into image recognition. At I/O and in other announcements, Google has made a point of casting itself as an \"AI-first\" company.\n\nTensorFlow Lite是在谷歌IO大会行公布的，大会上演示了Google Lens的图片识别及人工智能技术。google已经宣称自己是AI-first公司。\n\n# Arrival of new APIs 新的api\n\nWhile developers await the arrival of TensorFlow Lite, they can look to some new APIs rolling out to the standard version of TensorFlow for inspiration. While it's not a direct indication of what the Lite model will look like, they exhibit further insight into what TensorFlow and its machine learning capabilities make possible.\n\n在开发者等待TensorFlow Lite正式发布的过程中，可以先调研一下基于TensorFlow衍生出来的心api，这会带来新的灵感。这虽然不能看出TensorFlow Lite的模型是什么样的，但却能看出未来机器学习的潜力都是什么。\n\nGoogle recently also release a new Object Detection API to assist those working with computer vision models. As outlined in a Google Research blog post, the new set of APIs enables researchers to create more complex object detection. An example of TensorFlow at work illustrates how machine learning enables recognition of kites and people from the following scene.\n\ngoogle最近发布了对象检测api，他可以为视觉分析提供帮助。Google Research blog概括了一下，新的api可以让开发者对复杂的对象进行识别。下边的场景会说明TensorFlow是怎样应用到机器学习中进行图形识别的。\n\n\"This codebase is an open-source framework built on top of TensorFlow that makes it easy to construct, train and deploy object detection models. Our goals in designing this system was to support state-of-the-art models while allowing for rapid exploration and research,\" wrote research scientist Jonathan Huang and software engineer Vivek Rathod on the Google Research blog.\n\n“这个代码库是基于TensorFlow开发的开源框架，它可以很简单进行构造，训练，部署对象检测模型。我们设计这个系统的目标是去支持顶尖的模型，来进行快速的研究工作”，开发者们说。\n\nGoogle, of course, isn't the only one offering machine learning resources to developers. Facebook, Apple and Microsoft have been building their own resources. Facebook's Caffe2Go framework has its own models that can run on smartphones. Apple's Core ML is built for running machine learning on iOS. Microsoft Azure also offers machine learning as a service.\n\nGoogle并不是唯一一家提供机器学习开发资源的公司。facebook，apple，微软，都在建设他们自己的资源。facebook的Caffe2Go框架也已经可以运行在智能手机上了。苹果的Core ML是ios上运行机器学习的解决方案。Microsoft Azure也提供了机器学习作为一个服务。\n\nThe capabilities are growing quickly, as illustrated by the current release of the TensorFlow APIs. The ongoing challenge of performing machine learning on device is the resource constraints inherent to mobile computing. While doing such computing on the device itself enhances privacy and in some cases may be more efficient, much of the work must still be performed in the cloud.\n\n目前版本的TensorFlow的功能增长非常快。在移动设备上运行机器学习的最大挑战是硬件资源。有时候需要设备有非常快的运算速度，所以部分工作可能还是需要在云端处理。\n\nAlso at I/O, Google also promised that Android O would include a framework for more accelerated neural computation. Google, Apple and other companies that make devices will also have to think about machine learning as they design their hardware and software. SSD models are lightweight enough, for example, to run in real-time on mobile devices. But as phones become always-aware, intelligent assistants, the role of machine learning in everything from at the system level to third-party applications will become larger.\n\n在IO大会上，谷歌也表示在android O上，会内置性能很高的神经网络计算框架。谷歌，苹果等公司已经开始去考虑如何设计软件硬件来支持机器学习。SSD模型就是一个轻量级的即时运算的机器学习模型，可以运行在移动设备上。随着手机越来越智能，机器学习将会在系统级别和第三方应用中大展宏图。\n\n\n"
  },
  {
    "path": "其它/PanResponder.md",
    "content": "# PanResponder\n\n移动设备上的手势识别要比在web上复杂得多。用户的一次触摸操作的真实意图是什么，App要经过好几个阶段才能判断。比如App需要判断用户的触摸到底是在滚动页面，还是滑动一个widget，或者只是一个单纯的点击。甚至随着持续时间的不同，这些操作还会转化。此外，还有多点同时触控的情况。\n\nPanResponder是reactnative中实现手势系统的类。\n\nPanResponder类可以将多点触摸操作协调成一个手势。它使得一个单点触摸可以接受更多的触摸操作，也可以用于识别简单的多点触摸手势。\n\n它提供了一个对触摸响应系统响应器的可预测的包装。对于每一个处理函数，它在原生事件之外提供了一个新的gestureState对象。\n\nonPanResponderMove: (event, gestureState) => {}\n\ngestureState对象有如下的字段：\n\n\t•\tstateID - 触摸状态的ID。在屏幕上有至少一个触摸点的情况下，这个ID会一直有效。\n\t•\tmoveX - 最近一次移动时的屏幕横坐标\n\t•\tmoveY - 最近一次移动时的屏幕纵坐标\n\t•\tx0 - 当响应器产生时的屏幕坐标\n\t•\ty0 - 当响应器产生时的屏幕坐标\n\t•\tdx - 从触摸操作开始时的累计横向路程\n\t•\tdy - 从触摸操作开始时的累计纵向路程\n\t•\tvx - 当前的横向移动速度\n\t•\tvy - 当前的纵向移动速度\n\t•\tnumberActiveTouches - 当前在屏幕上的有效触摸点的数量\n\n\n示例代码:\n\n```\ncomponentWillMount: function() {\n    this._panResponder = PanResponder.create({\n      // 要求成为响应者：\n      onStartShouldSetPanResponder: (evt, gestureState) => true,\n      onStartShouldSetPanResponderCapture: (evt, gestureState) => true,\n      onMoveShouldSetPanResponder: (evt, gestureState) => true,\n      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,\n\n      onPanResponderGrant: (evt, gestureState) => {\n        // 开始手势操作。给用户一些视觉反馈，让他们知道发生了什么事情！\n\n        // gestureState.{x,y} 现在会被设置为0\n      },\n      onPanResponderMove: (evt, gestureState) => {\n        // 最近一次的移动距离为gestureState.move{X,Y}\n\n        // 从成为响应者开始时的累计手势移动距离为gestureState.d{x,y}\n      },\n      onPanResponderTerminationRequest: (evt, gestureState) => true,\n      onPanResponderRelease: (evt, gestureState) => {\n        // 用户放开了所有的触摸点，且此时视图已经成为了响应者。\n        // 一般来说这意味着一个手势操作已经成功完成。\n      },\n      onPanResponderTerminate: (evt, gestureState) => {\n        // 另一个组件已经成为了新的响应者，所以当前手势将被取消。\n      },\n      onShouldBlockNativeResponder: (evt, gestureState) => {\n        // 返回一个布尔值，决定当前组件是否应该阻止原生组件成为JS响应者\n        // 默认返回true。目前暂时只支持android。\n        return true;\n      },\n    });\n  },\n\n  render: function() {\n    return (\n      <View {...this._panResponder.panHandlers} />\n    );\n  },\n\n\n```\n\n"
  },
  {
    "path": "其它/RN分享提纲.md",
    "content": "# RN分享提纲\n\n# 目标\n\n最终目标：能够在孕育app中进行rn开发。\n\n本期目标：了解孕育中开发rn的一般过程。\n\n# 实施项目\n\n孕育中专家答和记录tab。\n\n# 环境搭建\n\n#### ios\n\nxcode+app的ios代码+js编辑器+rn开发服务器+真机or模拟器\n\n#### android（不推荐，建议直接真机）\n\njdk+androidSdk+androidStudio+app的android代码+js编辑器+rn开发服务器+真机or模拟器\n\n#### 安装react-native cli\n\n#### 安装xcode\n\n#### git库下载代码\n\n\n# 开发过程\n\n1. 启动rn开发服务器\n2. 启动模拟器，点开app，设置自动reload\n3. 启动js代码编辑器，coding---save---看效果\n4. tips:可以直接让首页显示你要开发的页面，可以快速查看效果\n\n```\n// tips用到的代码，替换index页面的最后一行\nAppRegistry.registerComponent('Ask', () => require('../../../demo/demo1/animate/drag2'));\n```\n\n# 调试过程\n\n1. console.log\n2. 断点调试\n\n\n# 发版过程\n\n### 全量发布\n\n全量发布是将rn相关资源打包进app中，是无需网络下载的。\n\n一般过程：\n\n1. coding结束\n2. 使用全量打包命令，打出android和ios全量包\n3. 将包发给android和ios的工程师即可\n\n### 增量发布\n\n[增量升级方案](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%A2%9E%E9%87%8F%E5%8D%87%E7%BA%A7%E6%96%B9%E6%A1%88.md)\n\n一般过程：\n\n1. coding结束\n2. 使用全量打包命令，打出android和ios全量包\n3. 使用增量打包命令，打出android和ios增量包\n4. 将增量包上传到后台管理系统，上线即可\n\n### 增量包测试过程\n\n1. 打出增量包\n2. 将增量包上传到升级后台，上线标识为false\n3. 手机开启测试模式（isDev=true）\n4. 手动更新增量包\n\n# 课后希望\n\n搭建开发环境，把上面的内容都操作一下。\n\n\n\n****************************  和下期分享的分割线  *********************************\n\n# 开发规范\n\n# 状态管理\n\n# 常用组件和接口\n\n### 导航、列表等\n\n### 与app的交互\n\n### 埋点\n\n# 自定义原生组件\n\n# 性能tips\n\n\n"
  },
  {
    "path": "其它/Range.md",
    "content": "# Range\n\n在做富文本编辑器的开发过程中，Selection和Range对象是用来操作选中区域的办法，本篇主要介绍一下Range对象的常用接口和属性。\n\n# 介绍\n\nRange可以用 Document 对象的 createRange方法创建，也可以用Selection对象的getRangeAt方法取得。另外，可以通过构造函数 Range() 来获得一个 Range 。\n\n\n# 属性\n\n#### Range.collapsed 只读\n返回一个用于判断 Range 起始位置和终止位置是否相同的布尔值。\n#### Range.commonAncestorContainer 只读\n返回包含 startContainer 和 endContainer 的最深的节点。\n#### Range.endContainer 只读\n返回包含 Range 终点的节点。\n#### Range.endOffset 只读\n返回 endContainer 中表示Range终点位置的数字。\n#### Range.startContainer 只读\n返回包含 Range 开始的节点。\n#### Range.startOffset 只读\n返回 startContainer 中表示 Range 起始位置的数字。\n\n# 方法\n\n#### Range.setStart()\n设置 Range 的起点。\n#### Range.setEnd()\n设置 Range 的终点。\n#### Range.setStartBefore()\n以其它节点 （ Node）为基准，设置 Range 的起点。\n#### Range.setStartAfter()\n以其它节点为基准，设置 Range 的始点。\n#### Range.setEndBefore()\n以其它节点为基准，设置 Range 的终点。\n#### Range.setEndAfter()\n以其它节点为基准，设置 Range 的终点。\n#### Range.selectNode()\n设定一个包含节点和节点内容的 Range 。\n#### Range.selectNodeContents()\n设定一个包含某个节点内容的 Range 。\n#### Range.collapse()\n向指定端点折叠该 Range 。\n\n\n#### Range.cloneContents()\n返回 Range 当中节点的文档片段（DocumentFragment）。\n#### Range.deleteContents()\n从文档（Document）中移除 Range 中的内容。\n#### Range.extractContents()\n把 Range 的内容从文档树移动到文档片段中。\n#### Range.insertNode()\n在 Range 的起点处插入节点。\n#### Range.surroundContents()\n将 Range 的内容移动到一个新的节点中。\n\n\n#### Range.compareBoundaryPoints()\n比较两个 Range 的端点。\n#### Range.cloneRange()\n返回拥有和原 Range 相同端点的克隆 Range 对象。\n#### Range.detach()\n从使用状态释放 Range，此方法用于改善性能。\n#### Range.toString()\n把Range内容作为字符串返回。\n\n\n\n"
  },
  {
    "path": "其它/ReactNative分享议题.md",
    "content": "# ReactNative分享议题\n\n# 所属专题\n\n前端，混合开发，App开发，React，React Native\n\n# 演讲主题\n\nReact Native项目实战之最后一公里——增量升级解决方案（未分享过）\n\n# 主题摘要\n\n宝宝树在最新上线的专家答模块中，完整的应用了React Native技术体系，除了享受到React Native带来的组件化、跨平台、原生体验等红利外，还自主研发了React Native增量升级系统，达成了不发版字节级的升级模式，目前已经运行在线上环境中。\n本次演讲介绍了React Native技术体系在宝宝树核心产品中的集成和使用方式；重点介绍宝宝树React Native增量升级方案，包括React Native打包工具，增量包算法，app升级策略，服务端升级api算法，升级后台，升级流程等内容。\n\n# 听众受益\n\n1. React Native在核心产品中的应用\n2. React Native在已有app中的集成方式和发版方式\n3. 宝宝树的React Native的增量更新机制和关键算法\n\n\n\n\n\nrn简单介绍\n获得到的好处和坏处\napp集成方式、app瘦身、安卓首屏白屏优化等。\n基础框架，性能优化办法。\n增量升级\n发版模式\n\n\n\n"
  },
  {
    "path": "其它/ReactNative分享议题PPT大纲.md",
    "content": "# ReactNative分享议题PPT大纲\n\n# React Native项目实战之最后一公里——增量升级解决方案\n\n# 主题摘要\n\n宝宝树在最新上线的专家答模块中，完整的应用了React Native技术体系，除了享受到React Native带来的组件化、跨平台、原生体验等红利外，还自主研发了React Native增量升级系统，达成了不发版字节级的升级模式，目前已经运行在线上环境中。\n本次演讲介绍了React Native技术体系在宝宝树核心产品中的集成和使用方式；重点介绍宝宝树React Native增量升级方案，包括React Native打包工具，增量包算法，app升级策略，服务端升级api算法，升级后台，升级流程等内容。\n\n现状介绍-包含目前的一些问题，为使用rn做铺垫\nrn简单介绍\n获得到的好处和坏处\napp集成方式、app瘦身、安卓首屏白屏优化等。\n基础框架，性能优化办法。\n增量升级\n发版模式\n\n背景 方案 实现 成果 未来\n\n\n# APP开发现状\n\n## 原生开发现状\n\n不能跨平台，一个逻辑两套代码，开发、测试成本都比较高；\n开发效率一般，coding-》编译—》打包-》调试-》coding；\n版本发布不灵活，；\n\n总结：开发体验差，用户体验好\n\n## H5开发现状\n\n用户体验不如原生，优化成本较高；\n\n总结：开发体验好，用户体验差\n\n# 怎么办呢？【配图】\n\n## ReactNative技术【配图】\n\nH5的开发体验，原生的用户体验；\n\n## 我们宝宝树怎么用的\n\n# \n\n\n\n\n\n"
  },
  {
    "path": "其它/Scrollview.md",
    "content": "\n一个包装了平台的ScrollView（滚动视图）的组件，同时还集成了触摸锁定的“响应者”系统。\n\nScrollView和ListView/FlatList应该如何选择？ScrollView会简单粗暴地把所有子元素一次性全部渲染出来。其原理浅显易懂，使用上自然也最简单。然而这样简单的渲染逻辑自然带来了性能上的不足。想象一下你有一个特别长的列表需要显示，可能有好几屏的高度。创建和渲染那些屏幕以外的JS组件和原生视图，显然对于渲染性能和内存占用都是一种极大的拖累和浪费。\n\n这就是为什么我们还有专门的ListView组件。ListView会惰性渲染子元素，只在它们将要出现在屏幕中时开始渲染。这种惰性渲染逻辑要复杂很多，因而API在使用上也更为繁琐。除非你要渲染的数据特别少，否则你都应该尽量使用ListView，哪怕它们用起来更麻烦。\n\nFlatList是0.43版本开始新出的改进版的ListView，性能更优，但可能不够稳定，尚待时间考验。\n\n\n# 常用属性\n\n#### contentContainerStyle\n\n这些样式会应用到一个内层的内容容器上，所有的子视图都会包裹在内容容器内\n\n#### onMomentumScrollStart \n\n滚动动画开始时调用此函数。\n\n#### onMomentumScrollEnd \n\n滚动动画结束时调用此函数。\n\n#### onScroll\n\n在滚动的过程中，每帧最多调用一次此回调函数。调用的频率可以用scrollEventThrottle属性来控制。\n\n#### refreshControl \n\n指定RefreshControl组件，用于为ScrollView提供下拉刷新功能。\n\n#### scrollEventThrottle\n\n这个属性控制在滚动过程中，scroll事件被调用的频率（单位是每秒事件数量）。更大的数值能够更及时的跟踪滚动位置，不过可能会带来性能问题，因为更多的信息会通过bridge传递。默认值为0，意味着每次视图被滚动，scroll事件只会被调用一次。\n\n\n# 常用方法\n\n#### scrollTo(y: number | { x?: number, y?: number, animated?: boolean }, x: number, animated: boolean) \n\n滚动到指定的x, y偏移处。第三个参数为是否启用平滑滚动动画。\n\n使用示例:\n\nscrollTo({x: 0, y: 0, animated: true})\n\n#### scrollToEnd(options?) \n\n滚动到视图底部（水平方向的视图则滚动到最右边）。\n\n加上动画参数 scrollToEnd({animated: true})则启用平滑滚动动画，或是调用 scrollToEnd({animated: false})来立即跳转。如果不使用参数，则animated选项默认启用。\n"
  },
  {
    "path": "其它/Selection.md",
    "content": "# Selection\n\n在做富文本编辑器的开发过程中，Selection和Range对象是用来操作选中区域的办法，本篇主要介绍一下Selection对象的常用接口和属性。\n\n# 介绍\n\nwindow.getSelection()可以获得Selection对象。\n\nSelection对象表示用户选择的文本范围或插入符号的当前位置。它代表页面中的文本选区，可能横跨多个元素。文本选区由用户拖拽鼠标经过文字而产生。\n\n# 属性\n\n#### anchorNode\n返回该选区起点所在的节点（Node）。\n\n\n#### anchorOffset\n返回一个数字，其表示的是选区起点在 anchorNode 中的位置偏移量。\n\t1.\t如果 anchorNode 是文字节点，那么返回的就是从该文字节点的第一个字开始，直到被选中的第一个字之间的字数（如果第一个字就被选中，那么偏移量为零）。\n\t2.\t如果 anchorNode 是一个元素，那么返回的就是在选区第一个节点之前的同级节点总数。(这些节点都是 anchorNode 的子节点)\n\n\t\n#### focusNode\n返回该选区终点所在的节点。\n\n#### focusOffset\n返回一个数字，其表示的是选区终点在 focusNode 中的位置偏移量。\n\t1.\t如果 focusNode 是文字节点，那么选区末尾未被选中的第一个字，在该文字节点中是第几个字（从0开始计），就返回它。\n\t2.\t如果 focusNode 是一个元素，那么返回的就是在选区末尾之后第一个节点之前的同级节点总数。\n\n\t\n#### isCollapsed\n返回一个布尔值，用于判断选区的起始点和终点是否在同一个位置。\n\n#### rangeCount\n返回该选区所包含的连续范围的数量。\n\n# 方法\n\n#### getRangeAt\n返回选区开始的节点（Node）。\n#### collapse\n将当前的选区折叠为一个点。\n#### extend\n将选区的焦点移动到一个特定的位置。\n#### modify\n修改当前的选区。\n#### collapseToStart\n将当前的选区折叠到起始点。\n#### collapseToEnd\n将当前的选区折叠到最末尾的一个点。\n#### selectAllChildren\n将某一指定节点的子节点框入选区。\n#### addRange\n一个区域（Range）对象将被加入选区。\n#### removeRange\n从选区中移除一个区域。\n#### removeAllRanges\n将所有的区域都从选区中移除。\n#### deleteFromDocument\n从页面中删除选区中的内容。\n#### selectionLanguageChange\n当键盘的朝向发生改变后修改指针的Bidi优先级。\n#### toString\n返回当前选区的纯文本内容。\n#### containsNode\n判断某一个node是否为当前选区的一部分。\n\n\n"
  },
  {
    "path": "其它/Understanding the WebView Viewport in iOS 11 ~ 1.md",
    "content": "# 【翻译】Understanding the WebView Viewport in iOS 11（part 1）\n\n# 理解ios11中的webview viewport（第一部分）\n\n原文地址：http://ayogo.com/blog/ios11-viewport/\n\n![](media/15100486170268.jpg)\n\niOS 11 brings some new, perhaps unintuitive, behaviour around the status bar area which will be particularly important for developers using tools like Apache Cordova or Ionic. In particular, this change in behaviour affects any web-based apps that use fixed position header bars when they are built for iOS 11. This post helps you understand the Webview Viewport in iOS 11.\n\n关于顶部状态栏，ios11带来了一些新的但是并不直观的变化，这些变化对于使用类似Apache Cordova or Ionic框架的开发者来说非常重要【其实，对于原生中嵌入webview的混合开发模式都会造成影响】。当html中使用了fixed定位的头，并且使用了xcode9打包目标为ios11的时候，会有明显的影响。本篇文章会帮你理解ios11中的Webview Viewport。\n\nNote: Existing apps will continue working as they always have with no changes to their viewport behaviour. This only affects apps that are compiled with Xcode 9 and a target of iOS 11.\n\n注意：现存的app不会有任何影响。影响只发生在使用xcode9并且编译目标为ios11的时候打出的包。\n\nTo understand the change, we need to look at the context for it.\n\n首先，要了解下ios状态栏的发展历程。\n\n# Status Bars & Safe Areas 状态栏和安全区域\n\nOn the early versions of iOS, the status bar was a constant black bar across the top of the screen that was largely untouchable. It was a piece of system UI and your app ran in the space underneath it.\n\n在最开始的时候，ios的状态栏在最上面是黑色的。它是系统UI的一部分，app运行在他的下面。\n\nThat changed with the introduction of iOS 7, which had a transparent status bar that took the colour of the app’s navigation bar. For apps showing in a webview like Cordova, this often meant detecting the iOS version and adding 20px of padding to the top of your fixed header so that it would fill the space correctly.\n\nios7的时候将状态栏调整成了透明的，并且覆盖了app的上面，所以在webview中，需要为fixed的header设置一个paddingTop=20px来进行正确的显示。\n\nNewer versions of iOS introduced some minor revisions, including features where an additional banner could be shown in the status bar when on a call or when an app was using geolocation in the background.\n\n最新的ios中又进行了调整并同时加入了一些新特性，比如，来电话的显示和定位信息的显示等。\n\nOn the native side, a lot of this was handled automatically by UINavigationBar and autolayout guides. There were layout guides for the top and bottom of the screen that automatically adjusted to the correct height of the status bar(s), ensuring that the app content was in a “safe area” where the status bar would not obscure it. If you had a UINavigationBar aligned to the top layout guide, iOS would also automatically extend its colour behind the status bar. For web, there was unfortunately no equivalent.\n\n在原生端，这些改动带来的影响都会被自动的处理掉。但对于webview中的代码，就没有这种机制了。\n\n# iOS 11 Changes ios11的改变\n\n![](media/15108152765423.png)\n图：The default viewport behaviour in iOS 11 on an iPhone 8。ios11的默认显示情况。\n\nWhere iOS 11 differs from earlier versions is that the webview content now respects the safe areas. This means that if you have a header bar that is a fixed position element with top: 0, it will initially render 20px below the top of the screen: aligned to the bottom of the status bar. As you scroll down, it will move up behind the status bar. As you scroll up, it will again fall down below the status bar (leaving an awkward gap where content shows through in the 20px gap).\n\n默认情况下，ios11中的webview中的内容会显示在安全区域内。也就说，如果你的header是paddingTop=0的话，他却会显示在距离屏幕顶部20px的地方，避开了状态栏的位置（这与ios7-ios10的情况完全相反了，原来是会被状态栏盖住的）。而且，当你向上滚动的时候，webview的内容又会跑到stats bar的后面去（此时和ios7-ios10的情况一致）。当你向下滚动的时候，webview的内容又跑到了status bar的下面去（此时和ios11默认不滚动的情况一致），同时，webview的一些内容会透过status bar显示出来。\n\nYou can see just how bad it is in this video clip:\n\n看视频体验一下这是多糟糕的改动吧：\n\n视频地址：http://ayogo.com/wp-content/uploads/2017/09/ios11-viewport.mp4?_=1\n\n# Why on earth would Apple make this change? 如此改动的原因\n\nIf you’ve seen the iPhone X design, it makes sense: The iPhone X features an irregular screen shape with an inset “cut out” at the top for the phone speaker and camera. If fixed position elements aligned to the real top of the screen, they would end up being inaccessible behind that speaker cutout.\n\n其实苹果是考虑到了iPhone X的情况：iPhone X 是一个不规则的显示屏，尤其是顶部，突出了一块。如果是fixed定位的元素基于真正的屏幕顶部定位，那么他们有可能被顶部的突出部分挡住。【笔者补充：因为大部分的混合开发的应用，webview中的header都会避开顶部20px，而iPhone X 的顶部突出部分却有 44px ，所以，如果按照原来的方式不做调整，就会有24px的部分被挡住】\n By aligning to the bottom of the status bar, it ensures that whatever is in the header will be accessible.\n\n所以，在ios11中，苹果将webview内容的默认渲染位置调整到了安全区域，也就是基于状态栏之下，或者基于iPhone X 的突出部分之下作为起点。\n\nCool… except now the app looks terrible with the awkward header moving up and down and content visibly scrolling behind the status bar.\n\n虽然可以理解苹果的苦心，但是，这个默认的行为实在是太low了，特别是页面滚动过程中那些透过状态栏显示出来的页面内容。\n\n未完待续。\n\n\n"
  },
  {
    "path": "其它/Understanding the WebView Viewport in iOS 11 ~ 2.md",
    "content": "# 【翻译】Understanding the WebView Viewport in iOS 11（part 2）\n\n# 理解ios11中的webview viewport（第二部分）\n\n原文地址：http://ayogo.com/blog/ios11-viewport/\n\n书接上回。在第一部分中，主要描述了不同ios版本下，webview的不同显示方式。本篇主要介绍解决方案。\n\n# iOS 11 Fixes 如何修改呢\n\nLuckily, Apple gave us a way to control this behaviour via the viewport meta tag. Even more luckily, they even backported this new viewport behaviour fix to the older, deprecated UIWebView!\n\n苹果在viewport的meta标签上提供了新的属性来解决上述的问题。并且，在已经不推荐使用的UIWebView上也支持了这个新的属性。\n\nThe viewport option you’ll be looking for is viewport-fit. It has three possible values:\n\n1. contain: The viewport should fully contain the web content. This means position fixed elements will be contained within the safe area on iOS 11.\n2. cover: The web content should fully cover the viewport. This means position fixed elements will be fixed to the viewport, even if that means they will be obscured. This restores the behaviour we had on iOS 10.\n3. auto: The default value, in this case it behaves the same as contain.\n\n具体来说就是在viewport标签中使用viewport-fit属性，这个属性可以设置三个值：\n\n1. contain：视窗会将内容显示全。也就是说，fixed元素会基于安全区域定位。【译者注：也就是会基于状态栏的下边框定位，在非iphoneX上会距离屏幕顶部20px，ihponeX下距离屏幕顶部44px】\n2. cover：内容会尽量的铺满整个视窗。此种情况，fixed元素会基于屏幕边缘定位，内容会被屏幕的状态栏或者iphoneX的感应条挡住。ios7~ios10其实就是这种模式。\n3. auto：默认值，与contain的行为一致。\n\nSo to restore your header bar to the very top of the screen, behind the status bar like it was in iOS 10, you’ll want to add viewport-fit=cover to your viewport meta tag.\n\n所以，如果想让header bar充满屏幕的最上面，就像在ios10那样，只需要在viewport的标签中加入viewport-fit=cover。\n\n![](media/15108155929454.png)\n图：Looking good with viewport-fit set to cover in iOS 11 on an iPhone 8。设置viewport-fit=cover后，iphone8的显示就正常了。\n\n# iPhone X\n\nBut what about the iPhone X with its irregular shape? The status bar is no longer 20px tall, and with the inset for the camera and speaker, your header bars contents will be entirely inaccessible to users. It’s important to note that this also applies to footer bars pinned to the bottom of the screen, which will be obstructed by the microphone.\n\niPhone X这种不规则屏幕该如何处理呢？iphone x 下状态栏不再是20px，并且还有突出的感应条，只有paddingTop=20px的fixed头部一定会被挡住。同样的，对应定位在底部的footer，也会被底部的虚拟按键挡住一些。\n\nNote: Your app will only use the full screen space on the iPhone X if you have a launch storyboard. Existing apps will be shown in a view box with black space at the top and bottom.\n\n注意：只有当你使用launch storyboard的时候，你的app才会使用iPhone X的全部屏幕。现有的app默认会运行在安全区域内，也就是距离屏幕上边和下边会有一定距离。\n\n![](media/15109098965191.png)\n图：iPhone X brings some new challenges, even with viewport-fit set to cover.尽管设置了viewport-fit=cover，iPhone X仍会存在一些需要解决的问题。\n\nLuckily, Apple added a way to expose the safe area layout guides to CSS. They added a concept similar to CSS variables, originally called CSS constants. Think of these like CSS variables that are set by the system and cannot be overridden. They were proposed to the CSS Working Group for standardization, and accepted with one change: instead of using a function called constant() to access these variables, they’ll use a function called env().\n\n苹果给出了在css中声明安全区域的办法。加入了一类新的css变量，用constants()命名。这些变量是系统来声明和赋值的，开发者无法对其进行声明和赋值。并且，苹果已经将这类变量提交到了css的规范管理组中，管理组已经接受了这组变量，但是，将其重命名为env()。\n\nNote: iOS 11.0 uses the constant() syntax, but future versions will only support env()!\n\n说明：ios11.0使用constant()，在之后的版本中，使用env()。\n\nThe 4 layout guide constants are:\n\n1. env(safe-area-inset-top): The safe area inset amount (in CSS pixels) from the top of the viewport.\n2. env(safe-area-inset-bottom): The safe area inset amount (in CSS pixels) from the bottom of the viewport.\n3. env(safe-area-inset-left): The safe area inset amount (in CSS pixels) from the left of the viewport.\n4. env(safe-area-inset-right): The safe area inset amount (in CSS pixels) from the right of the viewport.\n\n这组变量共有四个值：\n\n1. env(safe-area-inset-top): 安全区域距离屏幕顶部的距离。\n2. env(safe-area-inset-bottom): 安全区域距离屏幕底部的距离。\n3. env(safe-area-inset-left): 安全区域距离屏幕左侧的距离。\n4. env(safe-area-inset-right): 安全区域距离屏幕右侧的距离。\n\nApple’s final gift to us is that these variables have also been backported to UIWebView.\n\n在已经不推荐使用的UIWebView中也支持这些变量。\n\n# Example with CSS constants 示例代码\n\nLet’s say you have a fixed position header bar, and your CSS for iOS 10 currently looks like this:\n\n一般在ios10中，定义的fixed header会用用到如下的样式：\n\n```\nheader {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    height: 44px;\n\n    padding-top: 20px; /* Status bar height */\n}\n\n```\n\nTo make that adjust automatically for iPhone X and other iOS 11 devices, you would add a viewport-fit=cover option to your viewport meta tag, and change the CSS to reference the constant:\n\n为了适配ios11和iPhone X首先要在viewport标签上设置viewport-fit=cover，同时，在css中要如下处理：\n\n```\nheader {\n    /* ... */\n\n    /* Status bar height on iOS 10 */\n    padding-top: 20px;\n\n    /* Status bar height on iOS 11.0 */\n    padding-top: constant(safe-area-inset-top);\n\n    /* Status bar height on iOS 11+ */\n    padding-top: env(safe-area-inset-top);\n}\n```\n\nIt’s important to keep the fallback value there for older devices that won’t know how to interpret the constant() or env() syntax. You can also use constants in CSS calc() expressions.\n\n要注意老版本系统的兼容性，老系统不支持constant() 和 env()。上述的变量也可以被用在css的calc()函数中。\n\n![](media/15109100299889.png)\n图：iPhone X fixed with automatic device padding added。兼容了iPhone X的效果。\n\nYou would also want to remember to do this for bottom navigation bars as well.\n\n如果需要处理的底部的话，原理也基本一致。\n\n"
  },
  {
    "path": "其它/WebAssembly(1) A cartoon intro to WebAssembly.md",
    "content": "# [翻译] WebAssembly(1) A cartoon intro to WebAssembly 看卡通，入门WebAssembly\n\n原文地址： https://hacks.mozilla.org/2017/02/a-cartoon-intro-to-webassembly/\n\nWebAssembly is fast. You’ve probably heard this. But what is it that makes WebAssembly fast?\n\nWebAssembly执行很快。你可能已经听说了。但是原理你可能还不清楚。\n\nIn this series, I want to explain to you why WebAssembly is fast.\n\n在这个系列文章中，我们来解释一下为什么WebAssembly会执行这么快。\n\n# Wait, so what is WebAssembly? WebAssembly是什么东东\n\nWebAssembly is a way of taking code written in programming languages other than JavaScript and running that code in the browser. So when people say that WebAssembly is fast, what they are comparing it to is JavaScript.\n\nWebAssembly是一门可以运行在浏览器中的编程语言。所以，说WebAssembly执行很快一般都是和JavaScript进行比较。\n\nNow, I don’t want to imply that it’s an either/or situation — that you’re either using WebAssembly or using JavaScript. In fact, we expect that developers will use both WebAssembly and JavaScript in the same application.\n\n首先，我的意图不是去做出用JavaScript还是WebAssembly技术的选择。实际上，我希望开发者能同时使用JavaScript和WebAssembly。\n\nBut it is useful to compare the two, so you can understand the potential impact that WebAssembly will have.\n\n不过，做出他们的比较也有一定的价值，可以让你认识到WebAssembly未来可能带来的潜力。\n\n# A little performance history 性能简史\n\nJavaScript was created in 1995. It wasn’t designed to be fast, and for the first decade, it wasn’t fast.\n\nJavaScript是1995年诞生的。当时的设计并没有考虑到性能的问题，在前十年中，js的性能一直很差。\n\nThen the browsers started getting more competitive.\n\n然后，浏览器大战爆发了。\n\nIn 2008, a period that people call the performance wars began. Multiple browsers added just-in-time compilers, also called JITs. As JavaScript was running, the JIT could see patterns and make the code run faster based on those patterns.\n\n在2008年，浏览器的性能大战开始了。很多浏览器都加入了即时编译机制，也就是JIT。当js执行的同时，JIT可以进行各种模式的优化，使js运行更快。\n\nThe introduction of these JITs led to an inflection point in the performance of JavaScript. Execution of JS was 10x faster.\n\nJIT的引入使得js的性能曲线图出现了拐点。提升了10倍。\n\n![](media/14896484712443.png)\n\nWith this improved performance, JavaScript started being used for things no one ever expected it to be used for, like server-side programming with Node.js. The performance improvement made it feasible to use JavaScript on a whole new class of problems.\n\n由于性能的大幅提升，JavaScript被用在更广泛的领域中，比如利用node.js进行服务端的开发等。由于性能的大幅提示，JavaScript可以被用来解决更多类型的问题。\n\nWe may be at another one of those inflection points now, with WebAssembly.\n\n现在，WebAssembly的出现，可能又是一个新的性能拐点。\n\n![](media/14896484892321.png)\n\n\nSo, let’s dive into the details to understand what makes WebAssembly fast.\n\n好了，下一期我们开始研究WebAssembly执行快的原因和原理。\n\n\n译者：第一篇没啥干货~~~/(ㄒoㄒ)/~~~还请期待下一篇。\n\n\n"
  },
  {
    "path": "其它/WebAssembly(2-1)A crash course in just-in-time (JIT) compilers.md",
    "content": "# [翻译] WebAssembly(2-1) A crash course in just-in-time (JIT) compilers JIT编译原理速成\n\n原文地址：https://hacks.mozilla.org/2017/02/a-crash-course-in-just-in-time-jit-compilers/\n\nJavaScript started out slow, but then got faster thanks to something called the JIT. But how does the JIT work?\n\nJavaScript起初是很慢的，后来由于JIT的出现效率大幅提升。这是怎么做到的呢？\n\n# How JavaScript is run in the browser JavaScript的浏览器运行原理\n\nWhen you as a developer add JavaScript to the page, you have a goal and a problem.\n\n开发者编写JavaScript的时候，会有一个目的和一个问题。\n\nGoal: you want to tell the computer what to do.\n\n目的：告诉计算机你的意图。\n\nProblem: you and the computer speak different languages.\n\n问题：你和计算机使用不同的语言。\n\nYou speak a human language, and the computer speaks a machine language. Even if you don’t think about JavaScript or other high-level programming languages as human languages, they really are. They’ve been designed for human cognition, not for machine cognition.\n\n你使用的是人类的语言，而计算机使用的是机器语言。尽管，你可能不认为JavaScript是人类语言，但实际情况并非如此。JavaScript是被设计成人类易懂的，而不是机器易懂的。\n\nSo the job of the JavaScript engine is to take your human language and turn it into something the machine understands.\n\n所以，JavaScript引擎要做的第一件事就是把人类语言翻译成机器语言。\n\nI think of this like the movie Arrival, where you have humans and aliens who are trying to talk to each other.\n\n这就像Arrival电影中，人类和外星人试图去交流。\n\n![](media/14903411551086.png)\n\nIn that movie, the humans and aliens don’t just do word-for-word translations. The two groups have different ways of thinking about the world. And that’s true of humans and machines too (I’ll explain this more in the next post).\n\n在这个电影中，人类和外星人无法进行逐个单词的翻译。因为他们的思维方式和对世界的认识是完全不同的。人和机器其实也是这样的（后面会详细解释）。\n\nSo how does the translation happen?\n\n那该怎么进行翻译呢？\n\nIn programming, there are generally two ways of translating to machine language. You can use an interpreter or a compiler.\n\n一般会有两种方法进行机器语言的翻译。解释器或者编译器。\n\nWith an interpreter, this translation happens pretty much line-by-line, on the fly.\n\n解释器，会在程序执行的过程中进行时时的翻译。\n\n![](media/14903411909801.png)\n\nA compiler on the other hand doesn’t translate on the fly. It works ahead of time to create that translation and write it down.\n\n编译器，不会进行实时的翻译。他会预先进行翻译，并将其保存下来。\n\n![](media/14903412122116.png)\n\nThere are pros and cons to each of these ways of handling the translation.\n\n他们各有优缺点。\n\n# Interpreter pros and cons 解释器的优缺点\n\nInterpreters are quick to get up and running. You don’t have to go through that whole compilation step before you can start running your code. You just start translating that first line and running it.\n\n解释器的启动和执行是很迅速的。你在执行代码之前不用等待整个编译过程结束。当第一行被编译之后就可以立即执行你的代码。\n\nBecause of this, an interpreter seems like a natural fit for something like JavaScript. It’s important for a web developer to be able to get going and run their code quickly.\n\n因为这样，解释器是很适合来进行翻译JavaScript的。对web开发者来说，迅速的执行代码是很重要的。\n\nAnd that’s why browsers used JavaScript interpreters in the beginning.\nBut the con of using an interpreter comes when you’re running the same code more than once. For example, if you’re in a loop. Then you have to do the same translation over and over and over again.\n\n这就是浏览器在一开始使用JavaScript解释器的原因。但是当执行相同代码的时候，缺点也随之而来。比如说，当你执行一个循环，解释器也会一遍又一遍的执行翻译的工作。\n\n# Compiler pros and cons 编译器的优缺点\n\nThe compiler has the opposite trade-offs.\n\n编译器的取舍正相反。\n\nIt takes a little bit more time to start up because it has to go through that compilation step at the beginning. But then code in loops runs faster, because it doesn’t need to repeat the translation for each pass through that loop.\n\n因为编译器要执行整个的编译过程，所以程序要先等编译完才能执行。但是，循环的代码执行会很快速，因为不需要再进行翻译工作了。\n\nAnother difference is that the compiler has more time to look at the code and make edits to it so that it will run faster. These edits are called optimizations.\n\n还有一点不同，编译器会在编译的过程中进行代码的修改，这些修改会使代码执行的更快。修改的过程我们叫做优化。\n\nThe interpreter is doing its work during runtime, so it can’t take much time during the translation phase to figure out these optimizations.\n\n而解释器是在运行时工作的，它没有什么时间去进行代码优化的工作。\n\n# Just-in-time compilers: the best of both worlds JIT编译器：更佳的办法\n\nAs a way of getting rid of the interpreter’s inefficiency—where the interpreter has to keep retranslating the code every time they go through the loop—browsers started mixing compilers in.\n\n浏览器用来解决解释器在执行循环过程中重复进行翻译工作所带来的低效问题的办法，是将编译器混合进来。\n\nDifferent browsers do this in slightly different ways, but the basic idea is the same. They added a new part to the JavaScript engine, called a monitor (aka a profiler). That monitor watches the code as it runs, and makes a note of how many times it is run and what types are used.\n\n不同的浏览器的处理可能会有细微的差别，但原理是相同的。他们为JavaScript引擎新增了一个监控器，也叫分析器。监控器观察代码的执行，并将代码的执行次数和执行类型都记录了下来。\n\nAt first, the monitor just runs everything through the interpreter.\n\n首先，监控器允许解释器执行所有的代码。\n\n![](media/14903436616168.png)\n\nIf the same lines of code are run a few times, that segment of code is called warm. If it’s run a lot, then it’s called hot.\n\n如果同一行代码被执行了几次，这段代码会被标记为温热。如果执行了很多次，它会被标记为炎热。\n\n### Baseline compiler 基线编译器\n\nWhen a function starts getting warm, the JIT will send it off to be compiled. Then it will store that compilation.\n\n当一个方法变为温热，JIT就会将其单独编译，并将其保存。\n\n![](media/14903466613772.png)\n\nEach line of the function is compiled to a “stub”. The stubs are indexed by line number and variable type (I’ll explain why that’s important later). If the monitor sees that execution is hitting the same code again with the same variable types, it will just pull out its compiled version.\n\n方法的每一行都没编译成一个存根。这些存根被通过行号和变量类型来进行索引（后面会详细解释）。如果监控器发现有相同的代码被执行，他就会使用已经被编译的版本来执行。\n\nThat helps speed things up. But like I said, there’s more a compiler can do. It can take some time to figure out the most efficient way to do things… to make optimizations.\n\n这会提升一定的执行速度。但是，编译器还能做更多。他可以花费一些时间来分析出更有效率的执行办法，来进行优化。\n\nThe baseline compiler will make some of these optimizations (I give an example of one below). It doesn’t want to take too much time, though, because it doesn’t want to hold up execution too long.\n\n基线编译器会做一些优化（后面有例子）。但不会占用太长时间，因为他不想占用执行过程太久。\n\nHowever, if the code is really hot—if it’s being run a whole bunch of times—then it’s worth taking the extra time to make more optimizations.\n\n然而，如果代码已经被执行了很多遍，已经是炎热的级别了，这就值得去花费更多的时间来进行分析优化。\n\n### Optimizing compiler 优化编译器\n\nWhen a part of the code is very hot, the monitor will send it off to the optimizing compiler. This will create another, even faster, version of the function that will also be stored.\n\n当一段代码的热度很高，监控器就会将其发送给优化编译器。他会将其编译成一个执行速度更快的版本，并将其保存起来。\n\n![](media/14903484530831.png)\n\nIn order to make a faster version of the code, the optimizing compiler has to make some assumptions.\n\n为了保证编译结果的执行效率，优化编译器会做几个假设。\n\nFor example, if it can assume that all objects created by a particular constructor have the same shape—that is, that they always have the same property names, and that those properties were added in the same order— then it can cut some corners based on that.\n\n比如，如果可以假设所有对象都是被具有相同结构的特定构造器创建的，他们都具有相同的属性名，并且他们的属性也是按照同一顺序添加，这就可以在代码中简化一部分内容。\n\nThe optimizing compiler uses the information the monitor has gathered by watching code execution to make these judgments. If something has been true for all previous passes through a loop, it assumes it will continue to be true.\n\n监控器会将代码执行过程中收集到的信息传给优化编译器，优化编译器根据这些信息作出决策。如果某个值在之前的循环处理中一直是true，那么，就假设他在未来的执行中仍然是true。\n\nBut of course with JavaScript, there are never any guarantees. You could have 99 objects that all have the same shape, but then the 100th might be missing a property.\n\n但是，在JavaScript的执行中，不能保证上述的假设是正确的。你可能前99个对象都是相同结构，但第一百个就可能少了一个属性。\n\nSo the compiled code needs to check before it runs to see whether the assumptions are valid. If they are, then the compiled code runs. But if not, the JIT assumes that it made the wrong assumptions and trashes the optimized code.\n\n所以，编译结果必须要在执行前进行检验其是否是正确的。如果是，就可以用这个编译结果。但如果不正确，JIT就会丢弃掉这个编译结果。\n\n![](media/14903484796574.png)\n\nThen execution goes back to the interpreter or baseline compiled version. This process is called deoptimization (or bailing out).\n\n然后程序就会仍然使用解释器或者基线编译器的编译结果。这个过程叫做反优化（或bailing out）。\n\nUsually optimizing compilers make code faster, but sometimes they can cause unexpected performance problems. If you have code that keeps getting optimized and then deoptimized, it ends up being slower than just executing the baseline compiled version.\n\n通常，优化编译器会让代码执行变快，但有时候，却会造成性能问题。如果代码不停的被反优化，这就会比只执行基线编译器的时候要更慢。\n\nMost browsers have added limits to break out of these optimization/deoptimization cycles when they happen. If the JIT has made more than, say, 10 attempts at optimizing and keeps having to throw it out, it will just stop trying.\n\n大部分浏览器都会有一个反优化次数的上限。比如，当进行了十次的反优化，JIT就会停止尝试优化动作。\n\n未完待续....\n\n\n\n"
  },
  {
    "path": "其它/WebAssembly(2-2)A crash course in just-in-time (JIT) compilers.md",
    "content": "# [翻译] WebAssembly(2-2) A crash course in just-in-time (JIT) compilers JIT编译原理速成\n\n原文地址：https://hacks.mozilla.org/2017/02/a-crash-course-in-just-in-time-jit-compilers/\n\n书接上回....\n\n### An example optimization: Type specialization 优化的示例：类型特殊化\n\nThere are a lot of different kinds of optimizations, but I want to take a look at one type so you can get a feel for how optimization happens. One of the biggest wins in optimizing compilers comes from something called type specialization.\n\n优化的方法有很多，这里我举一个例子来说明。一个特别有效果的优化方法是类型特殊化。\n\nThe dynamic type system that JavaScript uses requires a little bit of extra work at runtime. For example, consider this code:\n\n像JavaScript这种动态类型的语言在执行的过程中会产生一些额外的工作。比如，下面这段代码：\n\n```\nfunction arraySum(arr) {\n  var sum = 0;\n  for (var i = 0; i < arr.length; i++) {\n    sum += arr[i];\n  }\n}\n```\n\nThe += step in the loop may seem simple. It may seem like you can compute this in one step, but because of dynamic typing, it takes more steps than you would expect.\n\n在循环中使用+=运算符是很常见的。一般大家都认为这个动作执行一步就可以计算完成，但实际上，由于JavaScript的动态类型，这需要执行好几步。\n\nLet’s assume that arr is an array of 100 integers. Once the code warms up, the baseline compiler will create a stub for each operation in the function. So there will be a stub for sum += arr[i], which will handle the+= operation as integer addition.\n\n假设arr这个数组中有100个整形。当代码达到温热级别，基线编译器会为每一次操作创建一个存根（stub）。所以，会创建一个 sum += arr[i] 的存根，并且，这个stub会认为每次的+=动作是一次整数加法。\n\nHowever,sum and arr[i] aren’t guaranteed to be integers. Because types are dynamic in JavaScript, there’s a chance that in a later iteration of the loop, arr[i] will be a string. Integer addition and string concatenation are two very different operations, so they would compile to very different machine code.\n\n然而，arr[i]并不能保证一直都是整形。因为JavaScript的动态类型的特点，arr[i]很有可能有时候会是一个string。整数加法和字符串连接是完全不同的两种操作，他们会被编译成完全不同的机器码。\n\nThe way the JIT handles this is by compiling multiple baseline stubs. If a piece of code is monomorphic (that is, always called with the same types) it will get one stub. If it is polymorphic (called with different types from one pass through the code to another), then it will get a stub for each combination of types that has come through that operation.\n\nJIT编译出多种不同的存根来解决这个问题。如果代码是同态的（总是同一种类型）就只使用到一个类型。如果代码是多态的（会有多种类型），就会根据当前的类型还使用对应的stub。\n\nThis means that the JIT has to ask a lot of questions before it chooses a stub.\n\n所以，在决定用哪个stub之前，JIT编译器会做很多类型判断的工作。\n\n![](media/14903485643832.png)\n\nBecause each line of code has its own set of stubs in the baseline compiler, the JIT needs to keep checking the types each time the line of code is executed. So for each iteration through the loop, it will have to ask the same questions.\n\n基线编译器在代码的每一次执行都会有一组stubs，所以，JIT必须要针对每一次代码的执行都进行全部的类型检查。所以，在循环的时候，会有很多重复的检查被执行。\n\n![](media/14903485765562.png)\n\nThe code would execute a lot faster if the JIT didn’t need to repeat those checks. And that’s one of the things the optimizing compiler does.\n\n如果能去掉这些重复的检查，执行速度一定会提升很多。这就是优化编译器做的其中一件事。\n\nIn the optimizing compiler, the whole function is compiled together. The type checks are moved so that they happen before the loop.\n\n优化编译器会将整个方法编译而不是逐行编译。所以，它可以将类型检查移动到循环之前进行。\n\n![](media/14903485965091.png)\n\nSome JITs optimize this even further. For example, in Firefox there’s a special classification for arrays that only contain integers. If arr is one of these arrays, then the JIT doesn’t need to check if arr[i] is an integer. This means that the JIT can do all of the type checks before it enters the loop.\n\n有些JIT会优化的更进一步。比如，在Firefox中，会有一个整形数组的类型。如果arr是这种类型的数组，那么JIT就不需要检查arr[i]是否是一个整形了。这样，JIT就可以将所有的类型检查都放在循环之前进行了。\n\n# Conclusion 结论\n\nThat is the JIT in a nutshell. It makes JavaScript run faster by monitoring the code as it’s running it and sending hot code paths to be optimized. This has resulted in many-fold performance improvements for most JavaScript applications.\n\nJIT的概述已经介绍完了。它使JavaScript提速的原理是通过监控代码的执行，找出执行次数多的代码段并将其优化编译。这种方式让大部分JavaScript应用提升了可观的性能。\n\nEven with these improvements, though, the performance of JavaScript can be unpredictable. And to make things faster, the JIT has added some overhead during runtime, including:\n\n然而，目前来说JavaScript的性能还仍是不可控的。因为，JIT在工作过程中本身的损耗，如下：\n\n1. optimization and deoptimization\n2. memory used for the monitor’s bookkeeping and recovery information for when bailouts happen\n3. memory used to store baseline and optimized versions of a function\n\n1. 优化和反优化\n2. 监控器记录代码运行信息和查询此信息的内存损耗\n3. 基线编译器和优化编译器保存stub的内存损耗\n\nThere’s room for improvement here: that overhead could be removed, making performance more predictable. And that’s one of the things that WebAssembly does.\n\n所以，仍有提升空间：如果将这些损耗都去除，执行性能就更加可控了。这就是WebAssembly要做的事情。\n\nIn the next article, I’ll explain more about assembly and how compilers work with it.\n\n下一篇文章中会讲讲assembly的内容，同时也会讲assembly是如何和编译器共同工作的。\n\n\n\n\n"
  },
  {
    "path": "其它/WebAssembly(3)A crash course in assembly.md",
    "content": "# [翻译] WebAssembly(3) A crash course in assembly 汇编原理速成\n\n原文地址：https://hacks.mozilla.org/2017/02/a-crash-course-in-assembly/\n\nTo understand how WebAssembly works, it helps to understand what assembly is and how compilers produce it.\n\n理解汇编和编译器如何生成它有助于你后续理解 WebAssembly 的工作原理，。\n\nIn the article on the JIT, I talked about how communicating with the machine is like communicating with an alien.\n\n在jit的文章中已经提到过，与计算机沟通就像与外星人沟通一样。\n\n![](media/14906864086806.png)\n\nI want to take a look now at how that alien brain works—how the machine’s brain parses and understands the communication coming in to it.\n\n让我们来看看机器的大脑是如何进行分析并理解沟通的内容的。\n\nThere’s a part of this brain that’s dedicated to the thinking—things like adding and subtracting, or logical operations. There’s also a part of the brain near that which provides short-term memory, and another part that provides longer-term memory.\n\n这个大脑中有一部分是负责思考的，比如加减法或者其它逻辑计算。还有一部分是用作短时间的记忆，另外一部分是负责长时间的记忆。\n\nThese different parts have names.\n\n1. The part that does the thinking is the Arithmetic-logic Unit (ALU).\n2. The short term memory is provided by registers.\n3. The longer term memory is the Random Access Memory (or RAM).\n\n这些部分都有自己的名字。\n\n1. 负责思考的部分叫算法逻辑单元 (ALU).\n2. 负责短时记忆的部分叫暂存器。\n3. 负责长时记忆的部分叫随机存取存储器也叫RAM。\n\n\n![](media/14906864456114.png)\n\nThe sentences in machine code are called instructions.\n\n我们将机器语言称作指令。\n\nWhat happens when one of these instructions comes into the brain? It gets split up into different parts that mean different things.\nThe way that this instruction is split up is specific to the wiring of this brain.\n\n当指令发给大脑后会发生什么？首先会将其分解成不同的部分。分解的方式是根据大脑特定的结构进行的。\n\nFor example, a brain that is wired like this might always take the first six bits and pipe that in to the ALU. The ALU will figure out, based on the location of ones and zeros, that it needs to add two things together.\n\n比如，有一种电脑会将前面的六个字节传送给ALU。ALU 根据接收到的序列中 1 和 0 的排列，就会明白需要将两个东西加在一起。\n\nThis chunk is called the “opcode”, or operation code, because it tells the ALU what operation to perform.\n\n这个字段称为操作码(opcode)，它的作用是告诉 ALU 要执行的操作。\n\n![](media/14906864745249.png)\n\nThen this brain would take the next two chunks of three bits each to determine which two numbers it should add. These would be addresses of the registers.\n\n接下来大脑会取后续两个三字节的字段来确定要相加的两个数。这两个数会存储在寄存器中。\n\n![](media/14906864930031.png)\n\nNote the annotations above the machine code here, which make it easier for us humans to understand what’s going on. This is what assembly is. It’s called symbolic machine code. It’s a way for humans to make sense of the machine code.\n\n注意这里机器码上方的注释，有助于我们理解这个过程。这就叫做汇编。这段代码称为符号机器码。符号机器码是人类理解机器码的一种方式。\n\nYou can see here there is a pretty direct relationship between the assembly and the machine code for this machine. Because of this, there are different kinds of assembly for the different kinds of machine architectures that you can have. When you have a different architecture inside of a machine, it is likely to require its own dialect of assembly.\n\n你会发现汇编和这台机器的机器码有很直接的关系。因此不同的机器架构对应有不同的汇编方式。当你遇到使用不同架构的机器时，可能就得按它们自己的方式进行汇编。\n\nSo we don’t just have one target for our translation. It’s not just one language called machine code. It’s many different kinds of machine code. Just as we speak different languages as people, machines speak different languages.\n\n所以，翻译的过程会有很多种。并不是只有一种机器码。有很多种机器码。就像人类有很多语言一样，机器也有很多语言。\n\nWith human to alien translation, you may be going from English, or Russian, or Mandarin to Alien Language A or Alien language B. In programming terms, this is like going from C, or C++, or Rust to x86 or to ARM.\n\n比如说从人类语言翻译到外星语言的过程，你可以将英语、俄罗斯语等翻译成外星语A或者外星语B。在编程语言中，就是讲C或者C++或者Rust翻译成X86或者ARM。\n\nYou want to be able to translate any one of these high-level programming languages down to any one of these assembly languages (which corresponds to the different architectures). One way to do this would be to create a whole bunch of different translators that can go from each language to each assembly.\n\n你希望能将任何高级编程语言反义词任何一种汇编语言（不同架构的）。一种方法是创建一整套不同语言到不同汇编的转化器。\n\n![](media/14906865211708.png)\n\nThat’s going to be pretty inefficient. To solve this, most compilers put at least one layer in between. The compiler will take this high-level programming language and translate it into something that’s not quite as high level, but also isn’t working at the level of machine code. And that’s called an intermediate representation (IR).\n\n但这样的做法非常低效。大部分编译器会在中间放置至少一个中间层。编译器接收高级编程语言并将其转化成相对底层的形式，转化结果也不能和机器码一样直接运行。这类形式称为中间表示(IR)。\n\n![](media/14906865489710.png)\n\nThis means the compiler can take any one of these higher-level languages and translate it to the one IR language. From there, another part of the compiler can take that IR and compile it down to something specific to the target architecture.\n\n这意味着编译器可以将任意一种高级编程语言翻译成一种 IR 语言。编译器的另一部分将得到的 IR 内容编译成特定于目标架构的语言。\n\nThe compiler’s front-end translates the higher-level programming language to the IR. The compiler’s backend goes from IR to the target architecture’s assembly code.\n\n编译器的前端部分将高级编程语言翻译成 IR 语言，再由后端将它们从 IR 语言编译成目标架构的汇编代码。\n\n![](media/14906865692649.png)\n\n# Conclusion 结论\n\nThat’s what assembly is and how compilers translate higher-level programming languages to assembly. In the next article, we’ll see how WebAssembly fits in to this.\n\n以上就是汇编的简要说明，以及编译器将高级程序语言转成汇编的过程。在下一篇文章里，我们将会看到 WebAssembly 是如何实现的。\n\n\n\n\n\n"
  },
  {
    "path": "其它/WebAssembly(4-1)Creating and working with WebAssembly modules.md",
    "content": "# [翻译] WebAssembly(4-1) Creating and working with WebAssembly modules\n\n原文地址：https://hacks.mozilla.org/2017/02/creating-and-working-with-webassembly-modules/\n\nWebAssembly is a way to run programming languages other than JavaScript on web pages. In the past when you wanted to run code in the browser to interact with the different parts of the web page, your only option was JavaScript.\n\n除了JavaScript，WebAssembly是在浏览器中运行程序的另外一种方式。在过去，只能用JavaScript。\n\nSo when people talk about WebAssembly being fast, the apples to apples comparison is to JavaScript. But that doesn’t mean that it’s an either/or situation—that you are either using WebAssembly, or you’re using JavaScript.\n\n所以，当说道WebAssembly的性能更快时，都是和JavaScript进行比较。但这并不意味着你就要从JavaScript和WebAssembly二选一了。\n\nIn fact, we expect that developers are going to use both WebAssembly and JavaScript in the same application. Even if you don’t write WebAssembly yourself, you can take advantage of it.\n\n实际上，我们希望开发者能够在同一个应用中同时使用WebAssembly和JavaScript。即使你自己不会做WebAssembly的编程，你也可以应用到他。\n\nWebAssembly modules define functions that can be used from JavaScript. So just like you download a module like lodash from npm today and call functions that are part of its API, you will be able to download WebAssembly modules in the future.\n\nWebAssembly模块定义的方法可以被JavaScript调用。就像你使用npm下载安装loadsh并使用它的api一样，未来你也可以下载WebAssembly模块并使用。\n\nSo let’s see how we can create WebAssembly modules, and then how we can use them from JavaScript.\n\n现在，我们来看看如何来创建一个WebAssembly模块，和如何在JavaScript中来使用他。\n\n# Where does WebAssembly fit? WebAssembly的定位\n\nIn the article about assembly, I talked about how compilers take high-level programming languages and translate them to machine code.\n\n在讲解汇编的文章中，我们提到了如何将高级语言转换成机器码。\n\n![](media/14924145446275.png)\n\nWhere does WebAssembly fit into this picture?\n\n那么WebAssembly是处于图中的哪一个部分呢。\n\nYou might think it is just another one of the target assembly languages. That is kind of true, except that each one of those languages (x86, ARM ) corresponds to a particular machine architecture.\n\n你可能会认为WebAssembly是另外一种的汇编语言。这确实是对的，除了传统汇编语言会有不同机器架构（x86，arm）的区分。\n\nWhen you’re delivering code to be executed on the user’s machine across the web, you don’t know what your target architecture the code will be running on.\n\n当你在浏览器中运行你的代码的时候，你不用清楚你的机器架构是什么。\n\nSo WebAssembly is a little bit different than other kinds of assembly. It’s a machine language for a conceptual machine, not an actual, physical machine.\n\n所以，WebAssembly和传统汇编语言还是有一点区别。他是一种用于虚拟机的汇编语言，并不是实际的，物理上存在的机器。\n\nBecause of this, WebAssembly instructions are sometimes called virtual instructions. They have a much more direct mapping to machine code than JavaScript source code. They represent a sort of intersection of what can be done efficiently across common popular hardware. But they aren’t direct mappings to the particular machine code of one specific hardware.\n\n因为如此，WebAssembly指令也被称为虚拟指令。它比 JavaScript 代码更直接地映射到机器码，它也能在通用的硬件上更有效地执行代码。所以它并不直接映射成特定硬件的机器码。\n\n\n![](media/14924146091466.png)\n\nThe browser downloads the WebAssembly. Then, it can make the short hop from WebAssembly to that target machine’s assembly code.\n\n浏览器下载完WebAssembly后，会先经过WebAssembly的处理，然后再到目标机器的汇编语言。\n\n# Compiling to .wasm\n\nThe compiler tool chain that currently has the most support for WebAssembly is called LLVM. There are a number of different front-ends and back-ends that can be plugged into LLVM.\n\n目前对于 WebAssembly 支持情况最好的编译器工具链是 LLVM。有很多不同的前端和后端插件可以用在 LLVM 上。\n\nNote: Most WebAssembly module developers will code in languages like C and Rust and then compile to WebAssembly, but there are other ways to create a WebAssembly module. For example, there is an experimental tool that helps you build a WebAssembly module using TypeScript, or you cancode in the text representation of WebAssembly directly.\n\n注意：很多 WebAssembly 开发者用 C 语言或者 Rust 开发，再编译成 WebAssembly。其实还有其他的方式来开发 WebAssembly 模块。例如利用 TypeScript 开发 WebAssembly 模块，或者直接用 WebAssembly 文本也可以。 \nLet’s say that we wanted to go from C to WebAssembly. We could use the clang front-end to go from C to the LLVM intermediate representation. Once it’s in LLVM’s IR, LLVM understands it, so LLVM can perform some optimizations.\n\n假设想从 C 语言到 WebAssembly，我们就需要 clang 前端来把 C 代码变成 LLVM 中间代码。当变换成了 LLVM IR 时，说明 LLVM 已经理解了代码，它会对代码自动地做一些优化。\n\nTo go from LLVM’s IR (intermediate representation) to WebAssembly, we need a back-end. There is one that’s currently in progress in the LLVM project. That back-end is most of the way there and should be finalized soon. However, it can be tricky to get it working today.\n\n为了从 LLVM IR 生成 WebAssembly，还需要后端编译器。在 LLVM 的工程中有正在开发中的后端，而且应该很快就开发完成了，现在这个时间节点，暂时还看不到它是如何起作用的。\n\nThere’s another tool called Emscripten which is a bit easier to use at the moment. It has its own back-end that can produce WebAssembly by compiling to another target (called asm.js) and then converting that to WebAssembly. It uses LLVM under the hood, though, so you can switch between the two back-ends from Emscripten.\n\n还有一个易用的工具，叫做 Emscripten。它通过自己的后端先把代码转换成自己的中间代码（叫做 asm.js），然后再转化成 WebAssembly。实际上它背后也是使用的 LLVM。\n\n![](media/14924146553887.png)\n\nEmscripten includes many additional tools and libraries to allow porting whole C/C++ codebases, so it’s more of a software developer kit (SDK) than a compiler. For example, systems developers are used to having a filesystem that they can read from and write to, so Emscripten can simulate a file system using IndexedDB.\n\nEmscripten 还包含了许多额外的工具和库来包容整个 C/C++ 代码库，所以它更像是一个软件开发者工具包（SDK）而不是编译器。例如系统开发者需要文件系统以对文件进行读写，Emscripten 就有一个 IndexedDB 来模拟文件系统。\n\nRegardless of the toolchain you’ve used, the end result is a file that ends in .wasm. I’ll explain more about the structure of the .wasm file below. First, let’s look at how you can use it in JS.\n\n不考虑太多的这些工具链，只要知道最终生成了 .wasm 文件就可以了。后面我会介绍 .wasm 文件的结构，在这之前先一起了解一下在 JS 中如何使用它。\n\n未完待续。\n\n"
  },
  {
    "path": "其它/WebAssembly(4-2)Creating and working with WebAssembly modules.md",
    "content": "# [翻译] WebAssembly(4-2) Creating and working with WebAssembly modules\n\n原文地址：https://hacks.mozilla.org/2017/02/creating-and-working-with-webassembly-modules/\n\n# Loading a .wasm module in JavaScript 用js加载wasm模块\n\nThe .wasm file is the WebAssembly module, and it can be loaded in JavaScript. As of this moment, the loading process is a little bit complicated.\n\n.wasm文件是WebAssembly模块，他可以被js加载。目前，加载的过程稍微有点复杂。\n\n```\nfunction fetchAndInstantiate(url, importObject) {\n  return fetch(url).then(response =>\n    response.arrayBuffer()\n  ).then(bytes =>\n    WebAssembly.instantiate(bytes, importObject)\n  ).then(results =>\n    results.instance\n  );\n}\n\n```\n\nWe’re working on making this process easier. We expect to make improvements to the toolchain and integrate with existing module bundlers like webpack or loaders like SystemJS. We believe that loading WebAssembly modules can be as easy as as loading JavaScript ones.\n\n我们正努力使这个过程变得更简单。我们准备提供更多更好的工具，也准备将其加载过程和目前的模块打包工具比如webpack进行整合。我们相信在未来加载WebAssembly模块就像加载js模块那样简单。\n\nThere is a major difference between WebAssembly modules and JS modules, though. Currently, functions in WebAssembly can only use numbers (integers or floating point numbers) as parameters or return values.\n\nWebAssembly模块和js模块有一个最主要的不同。目前，WebAssembly的只能使用数字类型作为方法的参数和返回值。\n\n![](media/14924147235980.png)\n\nFor any data types that are more complex, like strings, you have to use the WebAssembly module’s memory.\n\n当用到更复杂的数据类型，比如字符串，你必须使用WebAssembly的模块内存操作。\n\nIf you’ve mostly worked with JavaScript, having direct access to memory isn’t so familiar. More performant languages like C, C++, and Rust, tend to have manual memory management. The WebAssembly module’s memory simulates the heap that you would find in those languages.\n\n如果你是js的开发人员，那么你对直接内存操作可能不太熟悉。一些更底层的语言，比如c，c++，rust，都有手动操作内存的功能。WebAssembly模块内存就像是上述语言中使用的堆内存。\n\nTo do this, it uses something in JavaScript called an ArrayBuffer. The array buffer is an array of bytes. So the indexes of the array serve as memory addresses.\n\n为了在js中实现内存操作，使用了js的arraybuffer。array buffer 是一个字节数组。数组的索引映射了内存的地址。\n\nIf you want to pass a string between the JavaScript and the WebAssembly, you convert the characters to their character code equivalent. Then you write that into the memory array. Since indexes are integers, an index can be passed in to the WebAssembly function. Thus, the index of the first character of the string can be used as a pointer.\n\n如果你想让WebAssembly和js交换一个字符串，可以利用 ArrayBuffer 将其写入内存中，这时候 ArrayBuffer的索引就是整型了，可以把它传递给 WebAssembly 函数。此时，第一个字符的索引就可以当做指针来使用。\n\n![](media/14924147439939.png)\n\nIt’s likely that anybody who’s developing a WebAssembly module to be used by web developers is going to create a wrapper around that module. That way, you as a consumer of the module don’t need to know about memory management.\n\n这就好像一个web开发者在开发WebAssembly模块时，把这个模块包装了一层。这样其他使用者在使用这个模块的时候，就不用关心内存管理的细节。\n\nIf you want to learn more, check out our docs on working with WebAssembly’s memory.\n\n如果你想了解更多的内存管理，看一下我们写的WebAssembly的内存操作。\n\n# The structure of a .wasm file .wasm文件结构\n\nIf you are writing code in a higher level language and then compiling it to WebAssembly, you don’t need to know how the WebAssembly module is structured. But it can help to understand the basics.\n\n如果你是写高级语言的开发者，并且通过编译器编译成WebAssembly，那你不用关心WebAssembly模块的结构。但是了解它的结构有助于你理解一些基本问题。\n\nIf you haven’t already, we suggest reading the article on assembly (part 3 of the series).\n\n如果你对编译器还不了解，建议先读一下“WebAssembly 系列（三）”这篇文章。\n\nHere’s a C function that we’ll turn into WebAssembly:\n\n这段代码是生成 WebAssembly 的 C 代码：\n\n```\nint add42(int num) {\n  return num + 42;\n}\n\n```\n\nYou can try using the WASM Explorer to compile this function.\n\n你可以使用 WASM Explorer 来编译这个函数。\n\nIf you open up the .wasm file (and if your editor supports displaying it), you’ll see something like this.\n\n打开.wasm 文件（假设你的编辑器支持的话），可以看到下面代码：\n\n```\n00 61 73 6D 0D 00 00 00 01 86 80 80 80 00 01 60\n01 7F 01 7F 03 82 80 80 80 00 01 00 04 84 80 80\n80 00 01 70 00 00 05 83 80 80 80 00 01 00 01 06\n81 80 80 80 00 00 07 96 80 80 80 00 02 06 6D 65\n6D 6F 72 79 02 00 09 5F 5A 35 61 64 64 34 32 69\n00 00 0A 8D 80 80 80 00 01 87 80 80 80 00 00 20\n00 41 2A 6A 0B\n```\n\nThat is the module in its “binary” representation. I put quotes around binary because it’s usually displayed in hexadecimal notation, but that can be easily converted to binary notation, or to a human readable format.\n\n这是模块的“二进制”表示。之所以用引号把“二进制”引起来，是因为上面其实是用十六进制表示的，不过把它变成二进制或者人们能看懂的十进制表示也很容易。\n\nFor example, here’s what num + 42 looks like.\n\n例如，下面是 num + 42 的各种表示方法。\n\n![](media/14924148174927.png)\n\n\n# How the code works: a stack machine 代码如何工作：基于栈的机器\n\nIn case you’re wondering, here’s what those instructions would do.\n\n如果你对具体的操作过程很好奇，那么这幅图可以告诉你指令都做了什么。\n\n![](media/14924148445982.png)\n\nYou might have noticed that the add operation didn’t say where its values should come from. This is because WebAssembly is an example of something called a stack machine. This means that all of the values an operation needs are queued up on the stack before the operation is performed.\n\n从图中我们可以注意到“加”操作并没有指定哪两个数字进行加。这是因为WebAssembly 是采用“基于栈的机器”的机制。即一个操作符所需要的所有值，在操作进行之前都已经存放在堆栈中。\n\nOperations like add know how many values they need. Since add needs two, it will take two values from the top of the stack. This means that theadd instruction can be short (a single byte), because the instruction doesn’t need to specify source or destination registers. This reduces the size of the .wasm file, which means it takes less time to download.\n\n所有的操作符，比如加法，都知道自己需要多少个值。”加“需要两个值，所以它从堆栈顶部取两个值就可以了。那么加指令就可以变的更短（单字节），因为指令不需要指定源寄存器和目的寄存器。这也使得 .wasm 文件变得更小，进而使得加载 .wasm 文件更快。\n\nEven though WebAssembly is specified in terms of a stack machine, that’s not how it works on the physical machine. When the browser translates WebAssembly to the machine code for the machine the browser is running on, it will use registers. Since the WebAssembly code doesn’t specify registers, it gives the browser more flexibility to use the best register allocation for that machine.\n\n尽管 WebAssembly 使用基于栈的机制，但是并不是说在实际的物理机器上它就是这么生效的。当浏览器翻译 WebAssembly到机器码时，浏览器会使用寄存器， WebAssembly 代码并不指定用哪些寄存器，这样做的好处是给浏览器最大的自由度，让其自己来进行寄存器的最佳分配。\n\n# Sections of the module 模块的其它部分\n\nBesides the add42 function itself, there are other parts in the .wasm file. These are called sections. Some of the sections are required for any module, and some are optional.\n\n除了上面介绍的，.wasm 文件还有其他部分，通常把它们叫做部件。一些部件对于模块来讲是必须的，一些是可选的。\n\nRequired:\n\n\t1.\tType. Contains the function signatures for functions defined in this module and any imported functions.\n\t2.\tFunction. Gives an index to each function defined in this module.\n\t3.\tCode. The actual function bodies for each function in this module.\n\n必须部分：\n\n\t1.\tType。在模块中定义的函数的函数声明和所有引入函数的函数声明。\n\t2.\tFunction。给出模块中每个函数一个索引。\n\t3.\tCode。模块中每个函数的实际函数体。\n\nOptional:\n\n\t1.\tExport. Makes functions, memories, tables, and globals available to other WebAssembly modules and JavaScript. This allows separately-compiled modules to be dynamically linked together. This is WebAssembly’s version of a .dll.\n\t2.\tImport. Specifies functions, memories, tables, and globals to import from other WebAssembly modules or JavaScript.\n\t3.\tStart. A function that will automatically run when the WebAssembly module is loaded (basically like a main function).\n\t4.\tGlobal. Declares global variables for the module.\n\t5.\tMemory. Defines the memory this module will use.\n\t6.\tTable. Makes it possible to map to values outside of the WebAssembly module, such as JavaScript objects. This is especially useful for allowing indirect function calls.\n\t7.\tData. Initializes imported or local memory.\n\t8.\tElement. Initializes an imported or local table.\n\n可选部分：\n\n\t1.\tExport。使函数、内存、表单（table）、全局变量等对其他 WebAssembly 或 JavaScript 可见，允许动态链接一些分开编译的组件，即 .dll 的WebAssembly 版本。\n\t2.\tImport。允许从其他 WebAssembly 或者 JavaScript 中引入指定的函数、内存、表单或者全局变量。\n\t3.\tStart。当 WebAssembly 模块加载进来的时候，可以自动运行的函数（类似于 main 函数）。\n\t4.\tGlobal。声明模块的全局变量。\n\t5.\tMemory。定义模块用到的内存。\n\t6.\tTable。使得可以映射到 WebAssembly 模块以外的值，如映射到 JavaScript 对象中。这在间接函数调用时很有用。\n\t7.\tData。初始化内存。\n\t8.\tElement。初始化表单（table）。\n\nFor more on sections, here’s a great in-depth explanation of how these sections work.\n\n如果想要了解更多的部件，可以在“如何使用部件”中深入了解。\n\n# Coming up next 后续\n\nNow that you know how to work with WebAssembly modules, let’s look atwhy WebAssembly is fast.\n\n现在你已经了解了WebAssembly模块的工作原理，下面将会介绍为什么WebAssembly运行的更快。\n\n\n\n\n\n\n"
  },
  {
    "path": "其它/WebAssembly(5-1)What makes WebAssembly fast?.md",
    "content": "# [翻译]WebAssembly(5-1) What makes WebAssembly fast? WebAssembly运行快速的原理\n\nIn the last article, I explained that programming with WebAssembly or JavaScript is not an either/or choice. We don’t expect that too many developers will be writing full WebAssembly code bases.\n\n在前面的文章里，说明了WebAssembly和JavaScript是可以同时使用的。并不需要太多的开发者来只编写WebAssembly的代码。\n\nSo developers don’t need to choose between WebAssembly and JavaScript for their applications. However, we do expect that developers will swap out parts of their JavaScript code for WebAssembly.\n\n开发者不必为他们的应有只选择一种开发语言。并且，我们推荐开发者将一部分js实现的功能用WebAssembly来实现。\n\nFor example, the team working on React could replace their reconciler code (aka the virtual DOM) with a WebAssembly version. People who use React wouldn’t have to do anything… their apps would work exactly as before, except they’d get the benefits of WebAssembly.\n\n举个例子，react的团队可以将其虚拟dom这层用WebAssembly重写。这对使用react的用户是完全透明的，使用react开发的应用什么都不用改，就能获得WebAssembly带来的收益。\n\nThe reason developers like those on the React team would make this swap is because WebAssembly is faster. But what makes it faster?\n\n开发者对WebAssembly版本的react肯定会更加支持，因为这会让react运行的更快。但运行的更快的原理是什么呢？\n\n# What does JavaScript performance look like today? 现阶段js的性能情况\n\nBefore we can understand the differences in performance between JavaScript and WebAssembly, we need to understand the work that the JS engine does.\n\n在我们理解WebAssembly的高性能原理之前，先了解一些js引擎的工作机制。\n\nThis diagram gives a rough picture of what the start-up performance of an application might look like today.\n\n下面这个图描述了js引擎在执行过程中的性能情况草图。\n\nThe time that the JS engine spends doing any one of these tasks depends on the JavaScript the page uses. This diagram isn’t meant to represent precise performance numbers. Instead, it’s meant to provide a high-level model of how performance for the same functionality would be different in JS vs WebAssembly.\n\njs引擎执行任务的时间是和具体的js代码有关的。这个图并不能表明精确的时间。他只是为了说明在执行同样功能的代码时，js和WebAssembly的区别。\n\n![](media/14933755565734.png)\n\nEach bar shows the time spent doing a particular task.\n\n图中的每一段都是一个具体的任务。\n\n\t•\tParsing ： the time it takes to process the source code into something that the interpreter can run.\n\t•\tCompiling + optimizing ： the time that is spent in the baseline compiler and optimizing compiler. Some of the optimizing compiler’s work is not on the main thread, so it is not included here.\n\t•\tRe-optimizing ： the time the JIT spends readjusting when its assumptions have failed, both re-optimizing code and bailing out of optimized code back to the baseline code.\n\t•\tExecution ： the time it takes to run the code.\n\t•\tGarbage collection ： the time spent cleaning up memory.\n\n\n\t1. parse：将代码转换成解释器能够执行的格式的时间\n\t2. compile+optimize：基线编译器和优化编译器花费的时间。一些优化编译器会在主线程之外的线程中执行，所以也可以不包含他。\n\t3. re-optimize：当 JIT 发现优化假设错误，丢弃优化代码所花的时间。包括重优化的时间、抛弃并返回到基线编译器的时间。\n\t4. execute：代码执行的时间\n\t5. Garbage collection：内存回收的时间\n\nOne important thing to note: these tasks don’t happen in discrete chunks or in a particular sequence. Instead, they will be interleaved. A little bit of parsing will happen, then some execution, then some compiling, then some more parsing, then some more execution, etc.\n\n一个重要的点：这些任务并不会按照特定的顺序执行的。他们是交叉执行的。比如正在执行了一parse，同时执行了一部分代码执行，然后执行了一部分编译，同时又执行了一部分的parse，一部分代码执行，等等。\n\nThe performance this breakdown brings is a big improvement from the early days of JavaScript, which would have looked more like this:\n\n这种交叉的执行比早期的JavaScript引擎执行的快了很多，如下图是早期的js引擎的执行方式：\n\n![](media/14933755837728.png)\n\nIn the beginning, when it was just an interpreter running the JavaScript, execution was pretty slow. When JITs were introduced, it drastically sped up execution time.\n\n早期使用解释器来执行js，执行的非常慢。JIT编译器的出现极大的提升了js的执行速度。\n\nThe tradeoff is the overhead of monitoring and compiling the code. If JavaScript developers kept writing JavaScript in the same way that they did then, the parse and compile times would be tiny. But the improved performance led developers to create larger JavaScript applications.\n\nJIT要权衡和取舍的主要是代码监控和优化的损耗。如果JavaScript开发者仍然像之前那样来使用JavaScript，这些损耗是很小的。但是，因为性能的提升，使得开发者去开发设计更大型更复杂的JavaScript应用。\n\nThis means there’s still room for improvement.\n\n这意味着性能优化永无止境。\n\n# How does WebAssembly compare? WebAssembly是怎么做的？\n\nHere’s an approximation of how WebAssembly would compare for a typical web application.\n\n下面是一个WebAssembly处理一个web应用的近似图。\n\n![](media/14933756436344.png)\n\nThere are slight variations between browsers in how they handle all of these phases. I’m using SpiderMonkey as my model here.\n\n不同的浏览器处理这些阶段会有细微的不同，这里使用SpiderMonkey模型。\n\n# Fetching 下载\n\nThis isn’t shown in the diagram, but one thing that takes up time is simply fetching the file from the server.\n\n在图中并没有这个阶段，但是从服务器上下载文件仍然会占用一定的时间。\n\nBecause WebAssembly is more compact than JavaScript, fetching it is faster. Even though compaction algorithms can significantly reduce the size of a JavaScript bundle, the compressed binary representation of WebAssembly is still smaller.\n\n因为WebAssembly会比JavaScript更小，所以下载的也会更快。尽管压缩算法可以极大的缩小js的体积，但是WebAssembly是压缩的二进制文件，会更小。\n\nThis means it takes less time to transfer it between the server and the client. This is especially true over slow networks.\n\n这就意味着WebAssembly下载文件会更快，特别是在网络差的情况下。\n\n# Parsing 解析\n\nOnce it reaches the browser, JavaScript source gets parsed into an Abstract Syntax Tree.\n\n当文件下载到浏览器后，JavaScript就会被转换成抽象语法树。\n\nBrowsers often do this lazily, only parsing what they really need to at first and just creating stubs for functions which haven’t been called yet.\n\n浏览器通常是在使用懒解析的方式，就是只在真正需要解析之前才会调用解析。\n\nFrom there, the AST is converted to an intermediate representation (called bytecode) that is specific to that JS engine.\n\n解析完成后，抽象语法树就被转换成字节码提供给js引擎。\n\nIn contrast, WebAssembly doesn’t need to go through this transformation because it is already an intermediate representation. It just needs to be decoded and validated to make sure there aren’t any errors in it.\n\nWebAssembly并不需要有这个转换的过程，因为他已经是字节码了。他仅仅需要被解码和校验通过就可以被执行了。\n\n![](media/14933756962625.png)\n\n\n未完待续\n\n\n"
  },
  {
    "path": "其它/WebAssembly(5-2)What makes WebAssembly fast?.md",
    "content": "# [翻译]WebAssembly(5-2) What makes WebAssembly fast? WebAssembly运行快速的原理\n\n# Compiling + optimizing 编译+优化\n\nAs I explained in the article about the JIT, JavaScript is compiled during the execution of the code. Depending on what types are used at runtime, multiple versions of the same code may need to be compiled.\n\n在jit的文章里已经说过，JavaScript是在运行时候被编译的。根据运行时的类型使用情况，相同的代码段会被编译。\n\nDifferent browsers handle compiling WebAssembly differently. Some browsers do a baseline compilation of WebAssembly before starting to execute it, and others use a JIT.\n\n不同的浏览器处理WebAssembly的编译不太相同。有的浏览器会会在运行WebAssembly之前进行一个基线的编译，有的浏览器会使用即时编译jit。\n\nEither way, the WebAssembly starts off much closer to machine code. For example, the types are part of the program. This is faster for a few reasons:\n\n无论哪种方式，WebAssembly在开始的时候都更接近机器码。比如，程序的部分类型。运行的更快有如下的原因：\n\n\t1.\tThe compiler doesn’t have to spend time running the code to observe what types are being used before it starts compiling optimized code.\n\t2.\tThe compiler doesn’t have to compile different versions of the same code based on those different types it observes.\n\t3.\tMore optimizations have already been done ahead of time in LLVM. So less work is needed to compile and optimize it.\n\n\t1. 编译器在编译优化之前，不需要再花费时间去监控代码的运行\n\t2. 编译器无需再为相同代码的不同类型编译出不同的版本\n\t3. 更多的优化工作已经提前完成。只剩下很少的工作。\n\n![](media/14933757335448.png)\n\n\n# Reoptimizing 再优化\n\nSometimes the JIT has to throw out an optimized version of the code and retry it.\n\n有时JIT会重新优化已经优化过的部分代码。\n\nThis happens when assumptions that the JIT makes based on running code turn out to be incorrect. For example, deoptimization happens when the variables coming into a loop are different than they were in previous iterations, or when a new function is inserted in the prototype chain.\n\n这在jit发现运行的代码有优化错误的时候会发生。比如，在循环中，如果某次的类型与之前的循环不同，就会取消优化，或者，在原型链中加入一个新的方法。\n\nThere are two costs to deoptimization. First, it takes some time to bail out of the optimized code and go back to the baseline version. Second, if that function is still being called a lot, the JIT may decide to send it through the optimizing compiler again, so there’s the cost of compiling it a second time.\n\n这种反优化会有两个损耗。第一，回退代码的损耗。第二，如果这个方法还会被大量的调用，jit还会将其发送给优化编译器进行优化，所以，就会增加一次编译的时间。\n\nIn WebAssembly, things like types are explicit, so the JIT doesn’t need to make assumptions about types based on data it gathers during runtime. This means it doesn’t have to go through reoptimization cycles.\n\n在WebAssembly中，类型是明确定义好的，所以jit无需去假设类型。这就意味着也无需有反优化的过程。\n\n![](media/14933757656495.png)\n\n# Executing 执行\n\nIt is possible to write JavaScript that executes performantly. To do it, you need to know about the optimizations that the JIT makes. For example, you need to know how to write code so that the compiler can type specialize it, as explained in the article on the JIT.\n\n要写出执行效率很高的JavaScript代码。你需要了解JIT的优化机制，例如你要知道什么样的代码编译器会对其进行特殊处理。\n\nHowever, most developers don’t know about JIT internals. Even for those developers who do know about JIT internals, it can be hard to hit the sweet spot. Many coding patterns that people use to make their code more readable (such as abstracting common tasks into functions that work across types) get in the way of the compiler when it’s trying to optimize the code.\n\n然而大多数的开发者是不知道JIT内部的实现机制。即使开发者知道JIT的内部机制，也很难写出符合JIT标准的代码，因为人们通常为了代码可读性更好而使用的编码模式，恰恰不合适编译器对代码的优化。\n\nPlus, the optimizations a JIT uses are different between browsers, so coding to the internals of one browser can make your code less performant in another.\nBecause of this, executing code in WebAssembly is generally faster. Many of the optimizations that JITs make to JavaScript (such as type specialization) just aren’t necessary with WebAssembly.\n\n加之 JIT 会针对不同的浏览器做不同的优化，所以对于一个浏览器优化的比较好，很可能在另外一个浏览器上执行效率就比较差。\n\nIn addition, WebAssembly was designed as a compiler target. This means it was designed for compilers to generate, and not for human programmers to write.Since human programmers don’t need to program it directly, WebAssembly can provide a set of instructions that are more ideal for machines. \n\n正是因为这样，执行WebAssembly通常会比较快，很多JIT为JavaScript所做的优化在WebAssembly并不需要。另外，WebAssembly就是为了编译器而设计的，开发人员不直接对其进行编程，这样就使得 WebAssembly专注于提供更加理想的指令（执行效率更高的指令）给机器就好了。\n\nDepending on what kind of work your code is doing, these instructions run anywhere from 10% to 800% faster.\n\n执行效率方面，不同的代码功能有不同的效果，一般来讲执行效率会提高 10% - 800%。\n\n\n![](media/14933758010185.png)\n\n# Garbage collection 垃圾回收\n\nIn JavaScript, the developer doesn’t have to worry about clearing out old variables from memory when they aren’t needed anymore. Instead, the JS engine does that automatically using something called a garbage collector.\n\nJavaScript 中，开发者不需要手动清理内存中不用的变量。JS 引擎会自动地做这件事情，这个过程叫做垃圾回收。\n\nThis can be a problem if you want predictable performance, though. You don’t control when the garbage collector does its work, so it may come at an inconvenient time. Most browsers have gotten pretty good at scheduling it, but it’s still overhead that can get in the way of your code’s execution.\n\n可是，当你想要实现性能可控，垃圾回收可能就是个问题了。垃圾回收器会自动开始，这是不受你控制的，所以很有可能它会在一个不合适的时机启动。目前的大多数浏览器已经能给垃圾回收安排一个合理的启动时间，不过这还是会增加代码执行的开销。\n\nAt least for now, WebAssembly does not support garbage collection at all. Memory is managed manually (as it is in languages like C and C++). While this can make programming more difficult for the developer, it does also make performance more consistent.\n\n目前为止，WebAssembly 不支持垃圾回收。内存操作都是手动控制的（像 C、C++一样）。这对于开发者来讲确实增加了些开发成本，不过这也使代码的执行效率更高。\n\n![](media/14933758261491.png)\n\n# Conclusion 结论\n\nWebAssembly is faster than JavaScript in many cases because:\n\n\t•\tfetching WebAssembly takes less time because it is more compact than JavaScript, even when compressed.\n\t•\tdecoding WebAssembly takes less time than parsing JavaScript.\n\t•\tcompiling and optimizing takes less time because WebAssembly is closer to machine code than JavaScript and already has gone through optimization on the server side.\n\t•\treoptimizing doesn’t need to happen because WebAssembly has types and other information built in, so the JS engine doesn’t need to speculate when it optimizes the way it does with JavaScript.\n\t•\texecuting often takes less time because there are fewer compiler tricks and gotchas that the developer needs to know to write consistently performant code, plus WebAssembly’s set of instructions are more ideal for machines.\n\t•\tgarbage collection is not required since the memory is managed manually.\n\t\nWebAssembly 比 JavaScript 执行更快是因为：\n\n1. 文件加载，WebAssembly 比 JavaScript 加载文件更快。即使 JavaScript 进行了压缩，WebAssembly 文件的体积也比 JavaScript 更小；\n2. 解码，WebAssembly 的解码时间比 JavaScript 的解析时间更短；\n3. 编译和优化，WebAssembly 更具优势，因为 WebAssembly 的代码更接近机器码，并且大部分优化工作已经事先做好了。\n4. 再优化，WebAssembly 不会发生再优化。而 JS 引擎的优化假设则可能会发生“抛弃优化代码<->再优化”现象。\n5. 执行，WebAssembly 更快是因为开发人员不需要懂太多的编译器技巧，而这在 JavaScript 中是需要的。WebAssembly 代码也更适合生成机器执行效率更高的指令。\n6. 垃圾回收，WebAssembly 垃圾回收都是手动控制的，效率比自动回收更高。\n\nThis is why, in many cases, WebAssembly will outperform JavaScript when doing the same task.\n\n这就是为什么在大多数情况下，同一个任务 WebAssembly 比 JavaScript 性能更好的原因。\n\nThere are some cases where WebAssembly doesn’t perform as well as expected, and there are also some changes on the horizon that will make it faster. I’ll cover those in the next article.\n\n但是，还有一些情况 WebAssembly 表现的会不如预期；同时 WebAssembly 的未来也会朝着使 WebAssembly 执行效率更高的方向发展。这些我会在下一篇文章中介绍。\n\n\n"
  },
  {
    "path": "其它/WebAssembly(6-1)Where is WebAssembly now and what’s next?.md",
    "content": "# [翻译]WebAssembly(6-1) Where is WebAssembly now and what’s next? WebAssembly的现在和未来\n\nOn February 28, the four major browsers announced their consensus that the MVP of WebAssembly is complete. This provides a stable initial version that browsers can start shipping.\n\n2月28号，四大主流浏览器宣布WebAssembly的最小可用产品已经完成。这意味着浏览器已经提供WebAssembly的稳定初始版本了。\n\n![](media/14945852794105.jpg)\n\nThis provides a stable core that browsers can ship. This core doesn’t contain all of the features that the community group is planning, but it does provide enough to make WebAssembly fast and usable.\n\n浏览器提供了一个稳定的内核。内核并没有包含社区计划的所有功能，仅提供了一个快速的可用版本。\n\nWith this, developers can start shipping WebAssembly code. For earlier versions of browsers, developers can send down an asm.js version of the code. Because asm.js is a subset of JavaScript, any JS engine can run it. With Emscripten, you can compile the same app to both WebAssembly and asm.js.\n\n所以，开发者已经可以开始编写WebAssembly的代码了。在老版本的浏览器中，开发者可以使用asm.js来向下兼容。因为asm.js是JavaScript的一个子集，所有的js引擎都可以运行它。\n\nEven in the initial release, WebAssembly will be fast. But it should get even faster in the future, through a combination of fixes and new features.\n\n尽管刚刚发版，WebAssembly仍然很快。在未来他会更快，并且会有很多新的特性。\n\n# Improving WebAssembly performance in browsers 在浏览器中提升WebAssembly的性能\n\nSome speed improvements will come as browsers improve WebAssembly support in their engines. The browser vendors are working on these issues independently.\n\n随着浏览器引擎不断优化WebAssembly的支持，运行WebAssembly也会越来越快。浏览器厂商目前都在解决下述的问题。\n\n### Faster function calls between JS and WebAssembly js和WebAssembly之间更快的调用方式\n\nCurrently, calling a WebAssembly function in JS code is slower than it needs to be. That’s because it has to do something called “trampolining”. The JIT doesn’t know how to deal directly with WebAssembly, so it has to route the WebAssembly to something that does. This is a slow piece of code in the engine itself, which does setup to run the optimized WebAssembly code.\n\n目前，js调用WebAssembly方法还是比预期的要慢。因为需要称之为跳板的过程。jit还无法直接调用WebAssembly，所以他需要先将WebAssembly进行一些预处理。预处理的过程是比较慢的，他会为WebAssembly的运行做准备工作。\n\n![](media/14945853329335.png)\n\n\nThis can be up to 100x slower than it would be if the JIT knew how to handle it directly.\n\n这个过程会比jit直接调用WebAssembly慢100倍。\n\nYou won’t notice this overhead if you’re passing a single large task off to the WebAssembly module. But if you have lots of back-and-forth between WebAssembly and JS (as you do with smaller tasks), then this overhead is noticeable.\n\n如果你调用一个WebAssembly的大型任务你并不会对预处理有感知。但是，如果你不停的调用WebAssembly（好多小任务），这个损耗就会被感知到了。\n\n### Faster load time 更快的加载时间\n\nJITs have to manage the tradeoff between faster load times and faster execution times. If you spend more time compiling and optimizing ahead of time, that speeds up execution, but it slows down start up.\n\njit需求权衡更快的加载速度还是更快的执行速度。如果你花费更多的编译和优化时间，那么执行时间就会快，但是程序启动的加载时间就会变慢。\n\nThere’s a lot of ongoing work to balance up-front compilation (which ensures there is no jank once the code starts running) and the basic fact that most parts of the code won’t be run enough times to make optimization worth it.\n\n目前有大量的工作正在研究，如何使预编译时间和程序真正执行时间两者平衡。\n\nSince WebAssembly doesn’t need to speculate what types will be used, the engines don’t have to worry about monitoring the types at runtime. This gives them more options, for example parallelizing compilation work with execution.\n\nWebAssembly 不需要对变量类型做优化假设，所以引擎也不关心在运行时的变量类型。这就给效率的提升提供了更多的可能性，比如可以使编译和执行这两个过程并行。\n\nPlus, recent additions to the JavaScript API will allow streaming compilation of WebAssembly. This means that the engine can start compiling while bytes are still being downloaded.\n\n加之最新增加的 JavaScript API 允许 WebAssembly 的流编译，这就使得在字节流还在下载的时候就启动编译。\n\nIn Firefox we’re working on a two-compiler system. One compiler will run ahead of time and do a pretty good job at optimizing the code. While that’s running code, another compiler will do a full optimization in the background. The fully-optimized version of the code will be swapped in when it’s ready.\n\nFireFox 目前正在开发两个编译器系统。一个编译器先启动，对代码进行部分优化。在代码已经开始运行时，第二个编译器会在后台对代码进行全优化，当全优化过程完毕，就会将代码替换成全优化版本继续执行。\n\n未完待续。\n\n"
  },
  {
    "path": "其它/WebAssembly(6-2)Where is WebAssembly now and what’s next?.md",
    "content": "# [翻译]WebAssembly(6-2) Where is WebAssembly now and what’s next? WebAssembly的现在和未来\n\n\n# Adding post-MVP features to the spec 将要支持的特性\n\nOne of the goals of WebAssembly is to specify in small chunks and test along the way, rather than designing everything up front.\n\nWebAssembly使用迭代的方式研发，而不是事先设计出一切。\n\nThis means there are lots of features that are expected, but haven’t been 100% thought-through yet. They will have to go through the specification process, which all of the browser vendors are active in.\n\n这就意味着还有很多的特性还在酝酿之中。这些特性会逐步的被加入到标准中来，这个过程所有的浏览器厂商都会加入进来。\n\nThese features are called future features. Here are just a few.\n\n这些特性叫做未来的特性，这里介绍一些。\n\n### Working directly with the DOM dom的直接操作\n\nCurrently, there’s no way to interact with the DOM. This means you can’t do something like element.innerHTML to update a node from WebAssembly.\n\n目前，WebAssembly没有办法去直接操作dom。就是说WebAssembly不能使用element.innerHTML的方式来更新一个节点。\n\nInstead, you have to go through JS to set the value. This can mean passing a value back to the JavaScript caller. On the other hand, it can mean calling a JavaScript function from within WebAssembly—both JavaScript and WebAssembly functions can be used as imports in a WebAssembly module.\n\n所以，你需要通过js来完成这个操作。这就要在WebAssembly中调用JavaScript函数。\n\n![](media/14945854095569.png)\n\nEither way, it is likely that going through JavaScript is slower than direct access would be. Some applications of WebAssembly may be held up until this is resolved.\n\n无论如何，通过JavaScript的调用是比直接操作要慢的。如果WebAssembly想应用起来，这个问题务必是要解决的。\n\n### Shared memory concurrency 共享内存并发\n\nOne way to speed up code is to make it possible for different parts of the code to run at the same time, in parallel. This can sometimes backfire, though, since the overhead of communication between threads can take up more time than the task would have in the first place.\n\n提升代码执行速度的一个方法是使代码并行运行，不过有时也会适得其反，因为不同的线程在同步的时候可能会花费更多的时间。\n\nBut if you can share memory between threads, it reduces this overhead. To do this, WebAssembly will use JavaScript’s new SharedArrayBuffer. Once that is in place in the browsers, the working group can start specifying how WebAssembly should work with them.\n\n这时如果能够使不同的线程共享内存，那就能降低这种开销。实现这一功能 WebAssembly 将会使用 JavaScript 中的 SharedArrayBuffer，而这一功能的实现将会提高程序执行的效率。\n\n### SIMD\n\nIf you read other posts or watch talks about WebAssembly, you may hear about SIMD support. The acronym stands for single instruction, multiple data. It’s another way of running things in parallel.\n\n如果你之前了解过 WebAssembly 相关的内容，你可能会听说过 SIMD，全称是：Single Instruction, Multiple Data（单指令，多数据），这是并行化的另一种方法。\n\nSIMD makes it possible to take a large data structure, like a vector of different numbers, and apply the same instruction to different parts at the same time. In this way, it can drastically speed up the kinds of complex computations you need for games or VR.\n\nSIMD 在处理存放大量数据的数据结构有其独特的优势。比如存放了很多不同数据的 vector（容器），就可以用同一个指令同时对容器的不同部分做处理。这种方法会大幅提高复杂计算的效率，比如游戏或者 VR。\n\nThis is not too important for the average web app developer. But it is very important to developers working with multimedia, such as game developers.\n\n这对于普通 web 应用开发者不是很重要，但是对于多媒体、游戏开发者非常关键。\n \n### Exception handling 处理异常\n\nMany code bases in languages like C++ use exceptions. However, exceptions aren’t yet specified as part of WebAssembly.\n\n许多语言都仿照 C++ 式的异常处理，但是 WebAssembly 并没有包含异常处理。\n\nIf you are compiling your code with Emscripten, it will emulate exception handling for some compiler optimization levels. This is pretty slow, though, so you may want to use the DISABLE_EXCEPTION_CATCHING flag to turn it off.\n\n如果你用 Emscripten 编译代码，就知道它会模拟异常处理，但是这一过程非常之慢，慢到你都想用 “DISABLE_EXCEPTION_CATCHING” 标记把异常处理关掉。\n\nOnce exceptions are handled natively in WebAssembly, this emulation won’t be necessary.\n\n如果异常处理加入到了 WebAssembly，那就不用采用模拟的方式了。而异常处理对于开发者来讲又特别重要，所以这也是未来的一大功能点。\n\n### Other improvements—making things easier for developers 其它让开发更简单的事\n\nSome future features don’t affect performance, but will make it easier for developers to work with WebAssembly.\n\n一些未来特性不是针对性能的，而是使开发者开发 WebAssembly 更方便。\n\n\t•\tFirst-class source-level developer tools. Currently, debugging WebAssembly in the browser is like debugging raw assembly. Very few developers can mentally map their source code to assembly, though. We’re looking at how we can improve tooling support so that developers can debug their source code.\n\t•\tGarbage collection. If you can define your types ahead of time, you should be able to turn your code into WebAssembly. So code using something like TypeScript should be compilable to WebAssembly. The only hitch currently, though, is that WebAssembly doesn’t know how to interact with existing garbage collectors, like the one built in to the JS engine. The idea of this future feature is to give WebAssembly first-class access to the builtin GC with a set of low-level GC primitive types and operations.\n\t•\tES6 Module integration. Browsers are currently adding support for loading JavaScript modules using the script tag. Once this feature is added, a tag like <script src=url type=\"module\"> could work even if url points to a WebAssembly module.\n\t\n1. 一流的开发者工具。目前在浏览器中调试 WebAssembly 就像调试汇编一样，很少的开发者可以手动地把自己的源代码和汇编代码对应起来。我们在致力于开发出更加适合开发者调试源代码的工具。\n1. 垃圾回收。如果你能提前确定变量类型，那就可以把你的代码变成 WebAssembly，例如 TypeScript 代码就可以编译成 WebAssembly。但是现在的问题是 WebAssembly 没办法处理垃圾回收的问题，WebAssembly 中的内存操作都是手动的。所以 WebAssembly 会考虑提供方便的 GC 功能，以方便开发者使用。\n1. ES6 模块集成。目前浏览器在逐渐支持用 script 标记来加载 JavaScript 模块。一旦这一功能被完美执行，那么像 <script src=url type=\"module\"> 这样的标记就可以运行了，这里的 url 可以换成 WebAssembly 模块。\n\n# Conclusion 结论\n\nWebAssembly is fast today, and with new features and improvements to the implementation in browsers, it should get even faster.\n\nWebAssembly 执行起来很快，随着浏览器逐步支持了 WebAssembly 的更多特性，WebAssembly 将会变得更快。\n\n"
  },
  {
    "path": "其它/ios11和iPhone X中Webview的适配.md",
    "content": "# ios11和iPhone X中Webview的适配.md\n\n原理和原因请参考：\n\nios11的webview的默认渲染页面的方式发生了变化，不再和ios7~ios10一致：\n\nios7~ios10：会将页面铺满整个屏幕，也就相当于是viewport-fit=cover。\n\nios11：默认将页面铺满到安全区域中，非iphoneX就是顶部状态条之下，iPhone X就是留海之下开始。也就是相当于viewport-fit=contain。\n\n所以，有3种情况要适配，1是适配ios11的非iPhone X手机，2是适配iPhone X手机，3是其它的情况。\n\n\n\n"
  },
  {
    "path": "其它/node与shell的交互与通讯.md",
    "content": "#  node与shell的交互与通讯\n\nnode提供了调用shell的能力，主要是通过子进程模块来实现。\n\n示例代码：\n\n```\nconst { spawn } = require('child_process');\nconst ls = spawn('ls', ['-lh', '/usr']);\n\nls.stdout.on('data', (data) => {\n  console.log(`输出：${data}`);\n});\n\nls.stderr.on('data', (data) => {\n  console.log(`错误：${data}`);\n});\n\nls.on('close', (code) => {\n  console.log(`子进程退出码：${code}`);\n});\n```\n\n默认情况下，在 Node.js 的父进程与衍生的子进程之间会建立 stdin、stdout 和 stderr 的管道。 数据能以非阻塞的方式在管道中流通。 注意，有些程序会在内部使用行缓冲 I/O。 虽然这并不影响 Node.js，但这意味着发送到子过程的数据可能无法被立即使用。\n\nchild_process.spawn() 方法会异步地衍生子进程，且不会阻塞 Node.js 事件循环。 child_process.spawnSync() 方法则以同步的方式提供同样的功能，但会阻塞事件循环，直到衍生的子进程退出或终止。\n\n为了方便起见，child_process 模块提供了一些同步和异步的替代方法用于 child_process.spawn() 和 child_process.spawnSync()。 注意，每个替代方法都是在 child_process.spawn() 或 child_process.spawnSync() 的基础上实现的。\n\n1. child_process.exec(): 衍生一个 shell 并在 shell 上运行命令，当完成时会传入 stdout 和 stderr 到回调函数。\n1. child_process.execFile(): 类似 child_process.exec()，但直接衍生命令，且无需先衍生一个 shell。\n1. child_process.fork(): 衍生一个新的 Node.js 进程，并通过建立一个 IPC 通讯通道来调用一个指定的模块，该通道允许父进程与子进程之间相互发送信息。\n1. child_process.execSync(): child_process.exec() 的同步方法，会阻塞 Node.js 事件循环。\n1. child_process.execFileSync(): child_process.execFile() 的同步方法，会阻塞 Node.js 事件循环。\n\n对于某些用例，如自动化的 shell 脚本，同步的方法 可能更方便。 大多数情况下，同步的方法会明显影响性能，因为它会拖延事件循环直到衍生进程完成。\n\n\n"
  },
  {
    "path": "其它/node中文件操作的简单介绍.md",
    "content": "# node中文件操作的简单介绍\n\n通过 require('fs') 使用文件系统模块。 所有的方法都有异步和同步的形式。\n\n异步方法的最后一个参数都是一个回调函数。 传给回调函数的参数取决于具体方法，但回调函数的第一个参数都会保留给异常。 如果操作成功完成，则第一个参数会是 null 或 undefined。\n\n当使用同步方法时，任何异常都会被立即抛出。 可以使用 try/catch 来处理异常，或让异常向上冒泡。\n\n异步方法的例子：\n\n\n```\nconst fs = require('fs');\n\nfs.unlink('/tmp/hello', (err) => {\n  if (err) throw err;\n  console.log('成功删除 /tmp/hello');\n});\n```\n\n同步方法的例子：\n\n```\nconst fs = require('fs');\n\nfs.unlinkSync('/tmp/hello');\nconsole.log('成功删除 /tmp/hello');\n```\n\n异步的方法不能保证执行顺序。 所以下面的例子可能会出错：\n\n```\nfs.rename('/tmp/hello', '/tmp/world', (err) => {\n  if (err) throw err;\n  console.log('重命名完成');\n});\nfs.stat('/tmp/world', (err, stats) => {\n  if (err) throw err;\n  console.log(`文件属性: ${JSON.stringify(stats)}`);\n});\n```\n\nfs.stat 可能在 fs.rename 之前执行。 正确的方法是把回调链起来。\n\n```\nfs.rename('/tmp/hello', '/tmp/world', (err) => {\n  if (err) throw err;\n  fs.stat('/tmp/world', (err, stats) => {\n    if (err) throw err;\n    console.log(`文件属性: ${JSON.stringify(stats)}`);\n  });\n});\n```\n\n在繁忙的进程中，建议使用异步的方法。 同步的方法会阻塞整个进程，直到完成（停止所有连接）。\n\n可以使用文件名的相对路径。 路径是相对 process.cwd() 的。\n\n大多数 fs 函数可以省略回调函数，在这种情况下，会使用默认的回调函数。 若要追踪最初的调用点，可设置 NODE_DEBUG 环境变量：\n\n注意：不建议省略异步方法的回调函数，未来的版本可能会导致抛出错误。\n\n```\n$ cat script.js\nfunction bad() {\n  require('fs').readFile('/');\n}\nbad();\n\n$ env NODE_DEBUG=fs node script.js\nfs.js:88\n        throw backtrace;\n        ^\nError: EISDIR: illegal operation on a directory, read\n    <stack trace.>\n    \n ```\n \n注意：在 Windows 上 Node.js 遵循单驱动器工作目录的理念。 当使用驱动器路径且不带反斜杠时就能体验到该特征。 例如，fs.readdirSync('c:\\\\') 可能返回与 fs.readdirSync('c:') 不同的结果。 详见 MSDN 路径文档。\n\n注意: 在 Windows 上，使用 w 选项(通过 fs.open 或 fs.writeFile) 打开已有隐藏文件将会失败，错误信息为 EPERM 。已有隐藏文件可以通过 r+ 选项打开。调用 fs.ftruncate 可以用来重置文件内容。\n"
  },
  {
    "path": "其它/package.json中版本号的含义.md",
    "content": "# package.json中版本号的含义\n\n\n参考地址：https://docs.npmjs.com/files/package.json#dependencies\n\n\n```\nversion Must match version exactly\n>version Must be greater than version\n>=version etc\n<version\n<=version\n~version \"Approximately equivalent to version\" See semver\n^version \"Compatible with version\" See semver\n1.2.x 1.2.0, 1.2.1, etc., but not 1.3.0\nhttp://... See 'URLs as Dependencies' below\n* Matches any version\n\"\" (just an empty string) Same as *\nversion1 - version2 Same as >=version1 <=version2.\nrange1 || range2 Passes if either range1 or range2 are satisfied.\ngit... See 'Git URLs as Dependencies' below\nuser/repo See 'GitHub URLs' below\ntag A specific version tagged and published as tag See npm-tag\npath/path/path See Local Paths below\n```\n\n"
  },
  {
    "path": "其它/performance.timing.md",
    "content": "# performance.timing\n\n#### PerformanceTiming.navigationStart 只读\n是一个无符号long long 型的毫秒数，表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档，这个值会和PerformanceTiming.fetchStart相同。\n\n#### PerformanceTiming.unloadEventStart 只读\n是一个无符号long long 型的毫秒数，表征了unload事件抛出时的UNIX时间戳。如果没有上一个文档，or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.\n\n#### PerformanceTiming.unloadEventEnd 只读\n是一个无符号long long 型的毫秒数，表征了unload事件处理完成时的UNIX时间戳。如果没有上一个文档，or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.\n\n#### PerformanceTiming.redirectStart 只读\n是一个无符号long long 型的毫秒数，表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向，或者重定向中的一个不同源，这个值会返回0.\n\n#### PerformanceTiming.redirectEnd 只读\n是一个无符号long long 型的毫秒数，表征了最后一个HTTP重定向完成时（也就是说是HTTP响应的最后一个比特直接被收到的时间）的UNIX时间戳。如果没有重定向，或者重定向中的一个不同源，这个值会返回0.\n\n#### PerformanceTiming.fetchStart 只读\n是一个无符号long long 型的毫秒数，表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。\n\n#### PerformanceTiming.domainLookupStart 只读\n是一个无符号long long 型的毫秒数，表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection)，或者这个信息存储到了缓存或者本地资源上，这个值将和 PerformanceTiming.fetchStart一致。\n\n#### PerformanceTiming.domainLookupEnd 只读\n是一个无符号long long 型的毫秒数，表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection)，或者这个信息存储到了缓存或者本地资源上，这个值将和 PerformanceTiming.fetchStart一致。\n\n#### PerformanceTiming.connectStart 只读\n是一个无符号long long 型的毫秒数，返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接（persistent connection），则返回值等同于fetchStart属性的值。\n\n#### PerformanceTiming.connectEnd 只读\n是一个无符号long long 型的毫秒数，返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接，则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。\n\n#### PerformanceTiming.secureConnectionStart 只读\n是一个无符号long long 型的毫秒数，返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接，则返回0。\n\n#### PerformanceTiming.requestStart 只读\n是一个无符号long long 型的毫秒数，返回浏览器向服务器发出HTTP请求时（或开始读取本地缓存时）的Unix毫秒时间戳。\n\n#### PerformanceTiming.responseStart 只读\n是一个无符号long long 型的毫秒数，返回浏览器从服务器收到（或从本地缓存读取）第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开，该属性将会被数制成新的请求的相对应的发起时间。\n\n#### PerformanceTiming.responseEnd 只读\n是一个无符号long long 型的毫秒数，返回浏览器从服务器收到（或从本地缓存读取，或从本地资源读取）最后一个字节时（如果在此之前HTTP连接已经关闭，则返回关闭时）的Unix毫秒时间戳。\n\n#### PerformanceTiming.domLoading 只读\n是一个无符号long long 型的毫秒数，返回当前网页DOM结构开始解析时（即Document.readyState属性变为“loading”、相应的 readystatechange事件触发时）的Unix毫秒时间戳。\n\n#### PerformanceTiming.domInteractive 只读\n是一个无符号long long 型的毫秒数，返回当前网页DOM结构结束解析、开始加载内嵌资源时（即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时）的Unix毫秒时间戳。\n\n#### PerformanceTiming.domContentLoadedEventStart 只读\n是一个无符号long long 型的毫秒数，返回当解析器发送DOMContentLoaded 事件，即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。\n\n#### PerformanceTiming.domContentLoadedEventEnd 只读\n是一个无符号long long 型的毫秒数，返回当所有需要立即执行的脚本已经被执行（不论执行顺序）时的Unix毫秒时间戳。\n\n#### PerformanceTiming.domComplete 只读\n是一个无符号long long 型的毫秒数，返回当前文档解析完成，即Document.readyState 变为 'complete'且相对应的readystatechange 被触发时的Unix毫秒时间戳。\n\n#### PerformanceTiming.loadEventStart 只读\n是一个无符号long long 型的毫秒数，返回该文档下，load事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送，它的值将会是0。\n\n#### PerformanceTiming.loadEventEnd 只读\n是一个无符号long long 型的毫秒数，返回当load事件结束，即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送，或者尚未完成，它的值将会是0.\n"
  },
  {
    "path": "其它/rn分享5.md",
    "content": "# RN分享5\n\n# 目录结构\n\n![](media/15064850344896.jpg)\n\nbbt-react-native：基础技术框架。存放业务无关的可复用的组件、接口等。\n\nbbt-rn-bundle-repository：包仓库。打包命令；存放全量包、增量包。\n\nbbt-rn-update-server：增量升级后台（已废弃）\n\nbundle：打包配置。业务模块的打包配置。\n\ndemo：（已废弃）\n\nlama：小时光app模块（尚未使用）。\n\nmock：数据模拟接口（已废弃）\n\nnode_modules：node模块安装包\n\npregnancy：孕育app模块。包含，专家答，记录tab，好玩等。\n\n### app模块目录结构\n\n![](media/15064918953054.jpg)\n\nask：问答（未启用）\n\ncan-eat：能不能吃（未启用）\n\ncomm：基础业务框架。存放业务有关的可复用的组件、接口等。\n\ndemo：demo\n\nfd：专家答\n\nhw：好玩\n\njl：记录tab\n\n#### 业务模块\n\ncomm：本模块内的可复用的接口等。\n\ncomp：本模块内的组件（不一定可复用）。\n\nimg：图片。\n\npage：页面级组件。\n\n# 打包配置\n\n![](media/15094337861989.jpg)\n\n\nbundle目录下的文件主要有2个作用。\n\n1. 开发模式下的入口文件配置。\n\n孕育:localhost:8081/bundle/pregnancy-dev.bundle?platform=ios&dev=false\n\n时光:localhost:8081/bundle/lama-dev.bundle?platform=ios&dev=false\n\n2. 打正式包的入口文件配置。\n\n执行打包命令可以看到执行的命令，里面包含这个位置。\n\n文件说明，每个app都会有如下的文件，比如孕育中的：\n\n1. pregnancy.js。孕育app的入口文件配置。\n2. pregnancy-dev.js。开发环境下的孕育app入口文件配置。\n3. pregnancy-pro.js。正式环境下的孕育app入口文件配置，并屏蔽了默认的错误处理，同理其它正式环境下的一些配置和代码也可以写在这个文件中。打包命令会使用这个文件作为打包的入口文件。\n\n# 官方提供的基础组件\n\n1. view\n1. text\n1. touch*\n1. scrollview\n1. listview\n\n# 框架体系结构及其引用方式\n\n### 体系结构\n\n快速开发出质量高的功能是我们追求的目标。\n\n答案：复用！！！\n\n1. 快速：无需开发功能，只需将可复用成果引入并配置即可使用。\n2. 高质量：可复用成果是经历过测试验证的。\n\n复用带来的问题：个性化。\n\n答案：分层。不同层解决不同范围内个性化的问题。\n\n```\n业务组件和接口。业务模块内可复用。pregnancy/hw/comp,pregnancy/hw/comm.\n业务页面。不需复用。pregnancy/hw/page。\n业务模块。不需复用。pregnancy/hw。\n基础业务框架。app内可复用。pregnancy/comm。\n基础技术框架。任何项目都可复用。bbt-react-native。\n```\n\n各层的职责划分，讲bbtFetch的例子，直接跳讲到bbtFetch章节。\n\n### 引用方式\n\n这里主要说的是技术框架和业务框架的引用方式。其余直接按相对目录引用即可。\n\n1. 基础技术框架：业务无关，只提供纯技术的可复用组件。\n2. 基础业务框架：依赖于app的某些接口或者实现了某些app的特有业务。会对基础技术框架中的内容进行业务扩展。\n\n我们开发时直接面对的是【基础业务框架】。所以只引用它即可。\n\n基础业务框架的入口文件会自动的将技术框架中的内容发布出来。\n\n```\nimport {\n    Navigator, // 其实是属于技术框架的，但是也可以引用到\n    BaseComponent,\n} from '../../comm';\n```\n\n1. 基础业务框架入口文件：pregnancy/comm/index.js。\n2. 基础技术框架入口文件：bbt-react-native/index.js。\n\n\n# 业务模块注册\n\n需要与app约定好的业务模块名称。\n\n```\n// demo\nAppRegistry.registerComponent('demo', () => DemoIndex); \n// 好玩频道\nAppRegistry.registerComponent('hw', () => HwIndex);\n\n```\n\n# 导航\n\n### 业务模块的一般页面结构\n\n1. index页面：只定义nav对象，无任何显示view\n2. home页面：本模块的首页\n3. 其它页面页面\n\n### route对象\n\n处于页面栈中的每一个页面，都会有一个route对象与其对应。\n\n它大概长这个样子：\n\n```\n{\n\tname: 'PageA', // 必填\n\tpage: () => <PageA />, // 必填\n\t\n\tonHardwareBackPress: ()=> true,\n}\n```\n\n### 如何使用\n\n一个业务模块，有且只有一个Navigator组件被定义。\n\n在rn首页定义Navigator组件。\n\n```\nrender() {\n    return (\n            <Navigator nav={this.props.nav}            \n                       ref={(nav) => {\n                               global.nav = nav;\n                       }}\n                       initialRouteStack={this.getInitialRouteStack()}         \n            />\n            );\n       }\n\n```\n\n各个子页面统一使用首页定义的Navigator组件对象，通过global对象传递。\n\n跳转：global.nav.push(route);\n\n返回：global.nav.pop();\n\n### 转场动画的流畅性保证\n\nNavigator组件完全使用js实现，由于js的单线程特点，如果在页面转场动画过程中，js干其他事情【比如渲染个某个jsx】超过了16ms，那么转场动画将不足60帧，给用户的感觉就是动画有卡顿。\n\n为了避免这种情况，办法就是在转场动画中不要让js来干别的事情。\n\n那么我们如何知道转场动画什么时候结束呢，官方提供了动画交互管理器InteractionManager，示例伪代码如下：\n\n```\nInteractionManager.runAfterInteractions(() => {\n      alert('转场动画结束了！');\n    });\n```\n\n大多数的场景：点击page1的某个按钮，要跳转到page2，并且page2要和服务器请求数据，根据返回的数据来渲染page2的部分or全部内容。\n\n针对上述场景，解决方案如下，用伪代码描述：\n\n1. page2的state至少有2个值，转场动画进行中=true，服务器查询中=true\n1. page2的componentWillMount方法中发起异步服务器交互请求,当请求结束setState:服务器查询中=false\n2. page2的componentWillMount方法中注册InteractionManager.runAfterInteractions事件，当转场结束setState:转场动画进行中=false\n3. page2的render方法中，先判断（转场动画进行中=true || 服务器查询中=true）就返回一个loading的提示，否则返回真正的jsx，并且此时，服务器返回的数据已经可用了\n4. listview组件已经实现了这个算法，无需单独实现\n\n示例代码：FdMyInfo\n\n### 场景 参数传递\n\n1. 属性 page1传给page2\n2. 事件 page2传给page1\n\n### 场景 直接跳转xxx页面\n\n1. app会将需要显示的页面的url传递到index页面\n2. 利用index页面声明nav时定义的initialRouteStack来路由\n\n### 场景 android物理返回建的拦截。比如模态窗口按返回键。\n\n主要通过route中定义的onHardwareBackPress的事件来实现。\n\n返回true，继续本次返回动作。\n\n返回false，终止本次返回动作。\n\n参考modal的示例\n\n# 网络请求\n\nbbtFetch统一了http请求的入口，并封装了如下功能：\n\n技术层：\n\n4. 断网时的处理\n5. 将参数对象转换为http请求格式字符串【将{a: '1'，b: '2'}转换为a=1&b=2的格式】\n4. 特殊字符替换\n4. 请求和返回的日志打印\n\n业务层：\n\n1. 域名拼装 \n2. 公用参数拼装 和 生成加密串\n\n\n示例代码：FdMyInfo\n\n```\nbbtFetch(url).     then((res) => res.json()).then((res) => {         this.setState({data: res.data});     }).catch((e) => {         console.log(e);         FetchErrorApi.showPage({             header: <FdHeader title='个人信息' />,         });     });\n```\n\n# 网络错误处理\n\n### 需页面刷新的处理\n\n参考代码：FdMyInfo\n```\nbbtFetch(url).     then((res) => res.json()).then((res) => {         this.setState({data: res.data});     }).catch((e) => {         console.log(e);         FetchErrorApi.showPage({             header: <FdHeader title='个人信息' />,         });     });\n```\n\n### 无需页面刷新的处理\n\n参考代码：FdAnswerUserInfo的focus方法\n\n```\ncatch((e) => {     this.setRootState({loading: false});     FetchErrorApi.showMsg();     console.log(e); });\n\n```\n\n# 列表\n\n提供的功能：\n\n1. 页面转场的流畅保证；\n2. 无网络情况的处理；\n3. 无数据的提示文案；\n4. 数据缓存机制；\n5. 上拉分页；\n6. 下拉刷新；\n7. 返回顶部的按钮；\n8. 更丰富的滚动事件；\n9. 增量刷新列表的接口；\n\n三个必填属性\n\n1. url的拼装\n2. 列表数据的获得\n3. row的渲染\n\n### 场景：列表的增量更新\n\nlist.refresh接口\n\n参考代码：评论点赞\n\n### 场景：头尾有固定的内容\n\n使用renderHeader renderFooter方法。\n\n参考代码：游戏列表\n\n注意：不要和scrollview嵌套使用！\n\n### 场景：滚动事件的使用\n\nonScroll onRowShow onRow***等\n\n参考代码：视频列表 视频详情 用户主页\n\n### 场景：特殊的下拉刷新场景-模拟智能推荐\n\n巧妙的利用getUrl接口\n\n参考代码：视频列表\n\n\n# 其它内容\n\nloading。\n模态窗口。\n输入框和键盘。\n\n动画和动画view。\n\n与原生的交互-rnm。\n自定义原生组件。音频，视频。\n\n"
  },
  {
    "path": "其它/一个类似微信群聊程序的技术方案设计过程.md",
    "content": "# 一个类似微信群聊程序的技术方案设计过程\n\n本次项目的技术方案抉择很有趣也很具有典型性，印证了没有最好的技术，只有最适合的技术，所以在此进行记录。\n\n# 项目背景\n\n公司新立的一个项目，项目还处于保密阶段，他的具体背景在这不太方便说，简单用技术语言概括一下就是，要做一个类似于微信群聊的东西。\n\n项目周期：一个月左右。\n\n# 思维历程\n\n即时通讯类程序最重要的特征就是需要有服务器推送的能力，传统的BS结构的web应用，请求都是由浏览器发出，服务响应返回结果，而即时通讯类程序要求服务器有主动给浏览器or客户端发送请求的能力。\n\n## 长连接\n\n从产品经理那拿到这个需求之后，我们第一个反应就是使用长连接技术，因为长连接技术是做即时通讯类程序比较成熟比较完美的解决方案，但是我们公司技术架构平台并没有这块的支撑经验，也没有相应的配套技术框架，结合一个月的项目周期来考虑，风险确实较大。\n\n所以我们就想到了目前市场上很多云平台都提供了即时通讯的解决方案，可不可以使用现成的解决方案来降低我们的风险。在调研了一圈后发现，还是存在两个问题，第一是花钱，一般都是按流量收费，这个可能还需要领导审批一下，第二是风险虽然降低，但是还是有风险，并且风险不可控，担心平台方如果响应不及时或者有不可见坑，在这么短的工期下可能没有调整的空间。\n\n还有没有更佳的解决办法呢。\n\n## 轮询\n\n轮询是使用间隔固定时间的短连接来和服务器保持联系的一种手段，他有2个很致命的弱点。\n\n第一是做不到消息的即时性，因为客户端需要间隔一个时间段才向服务器发送一次请求，那么理论上，客户端接收到的消息会有最长等于间隔时间的延迟。可以通过调小间隔时间来降低延迟。\n\n第二是对服务器的性能损耗较大（相比较于长连接技术），客户端并不知道服务器有没有新消息，永远都是固定时间后就发送一次请求，其中会有一大部分请求返回值都是空，是浪费的，当使用用户很多的情况下，这些请求也是比较密集的，对服务器产生一定的压力。可以通过调大间隔时间来减小压力。\n\n轮询方案的优点是无需调整公司现有的技术架构体系，就可以很好的进行支撑，开发成本和风险都是最低的。\n\n那么有没有办法解决掉上述的两个弱点呢。\n\n关于消息及时性，属于业务范畴的问题，主要看业务场景的容忍度，所以我们和产品经理进行了充分的沟通，很幸运，本次的业务对及时性的要求并不高，最长都可以容忍一分钟的延迟，ok，第一个问题竟然已经不是问题了。\n\n关于服务器性能的损耗，我们又与后台架构的同学交流了服务器对瞬时并发的承载能力，与产品经理交流了业务场景可能参与的用户数量，后台架构同学按照最大的用户使用数量，给我们的建议是间隔时间设置为5秒服务器集群都应该没有任何问题，同时业务的最长容忍时间是1分钟，我们拥有了很大的调整空间，心中的一块石头落了地，第二个问题竟然也不是问题了。\n\n# 设计方案结论\n\n结论显而易见，在考虑到开发成本，业务场景，工期要求等综合因素后，我们选择了轮询的解决方案。同时，针对轮询方案的弱点，我们还设计了2个补救措施。\n\n## 延迟消息的显示方案\n\n因为轮询的特点，一次请求可能带回来很多条新消息，我们会立刻显示这些消息中的第一条，其它的消息按照与第一条时间差逐条显示。这样，从用户看来，还是逐条显示的，好像是刚发的一样，让用户感觉不到是轮询。\n\n## 服务器压力缓解方案\n\n我们把间隔时间放到后台来配置，每一次的轮询结果里，都会把间隔时间带回来，前端使用这个最新的间隔时间来启动下一次的轮询，这样我们就可以动态的调整所有客户端的间隔时间，当服务器压力过大或者过小的时候，我们都可以随时调整这个间隔时间，同时保证服务器的稳定性和用户的良好体验。\n\n# 总结思考\n\n其实在这次选型的过程中，轮询方案是最先想到也是最先抛弃的，因为他的弱点太显而易见了，而且技术上是不可解决的，但是，戏剧性的是通过我们和产品经理对业务场景的深层次讨论，发现轮询才是最适合的解决方案，是性价比最高的，而且没有什么坑。\n\n所以，根据上述过程，总结了一句话就是，技术方案设计一定要结合业务场景进行，脱离业务场景的技术都是纸上谈兵，没有最好的技术方案，只有最适合的技术方案。\n\n\n\n[评论直达连接](https://github.com/cnsnake11/blog/issues/22)\n\n\n\n"
  },
  {
    "path": "其它/了解一个新概念---区块链.md",
    "content": "# 了解一个新概念---区块链\n\n### 前言\n\n大家都知道，比特币在2017年底疯涨，最高突破过2w美金，目前也在1w3美金。\n\n区块链就是比特币的底层技术。\n\n但区块链可以应用到更多的领域中去，不仅仅是在虚拟货币的领域中，有很多人也把区块链技术看成是继移动互联网，大数据，人工智能，之后的下一代的技术变革的核心技术。\n\n### 简介\n\n区块链是一串使用密码学方法相关联产生的数据块，每一个数据块中包含了过去十分钟内所有比特币网络交易的信息，用于验证其信息的有效性(防伪)和生成下一个区块。该概念在中本聪的白皮书中提出，中本聪持有第一个区块，即\"创世区块\"。\n\n区块链的最新技术应用：脱胎于2008年出现的比特币技术，它提供了一种去中心化的、无需信任积累的信用建立范式。区块链技术本质是去中心化且寓于分布式结构的数据存储、传输和证明的方法，用数据区块(Block)取代了目前互联网对中心服务器的依赖，使得所有数据变更或者交易项目都记录在一个云系统之上，理论上实现了数据传输中对数据的自我证明，深远来说，这超越了传统和常规意义上需要依赖中心的信息验证范式，降低了全球\"信用\"的建立成本，这种点对点验证将会产生一种\"基础协议\"，是分布式人工智能的一种新形式，将建立人脑智能和机器智能的全新接口和共享界面。\n\n### 特点\n\n区块链技术有如下的特点：\n\n1 去中心化\n\n由于使用分布式核算和存储，不存在中心化的硬件或管理机构，任意节点的权利和义务都是均等的，系统中的数据块由整个系统中具有维护功能的节点来共同维护。\n\n2 开放性\n\n系统是开放的，除了交易各方的私有信息被加密外，区块链的数据对所有人公开，任何人都可以通过公开的接口查询区块链数据和开发相关应用，因此整个系统信息高度透明。\n\n3 自治性\n\n区块链采用基于协商一致的规范和协议（比如一套公开透明的算法）使得整个系统中的所有节点能够在去信任的环境自由安全的交换数据，使得对“人”的信任改成了对机器的信任，任何人为的干预不起作用。\n\n4 信息不可篡改\n\n一旦信息经过验证并添加至区块链，就会永久的存储起来，除非能够同时控制住系统中超过51%的节点，否则单个节点上对数据库的修改是无效的，因此区块链的数据稳定性和可靠性极高。\n\n5 匿名性\n\n由于节点之间的交换遵循固定的算法，其数据交互是无需信任的（区块链中的程序规则会自行判断活动是否有效），因此交易对手无须通过公开身份的方式让对方自己产生信任，对信用的累积非常有帮助。\n\n### 发展\n\n区块链诞生自中本聪的比特币，自2009年以来，出现了各种个样的类比特币的数字货币，都是基于公有区块链的。\n\n数字货币的现状是百花齐放，列出一些常见的：bitcoin、litecoin、dogecoin、OKcoinetc，除了货币的应用之外，还有各种衍生应用，如NXT，SIA，比特股，MaidSafe，Ripple，Ethereum等等。\n\n可以用区块链的一些领域可以是：\n\n1. 智能合约\n1. 证券交易\n1. 电子商务\n1. 物联网\n1. 社交通讯\n1. 文件存储\n1. 存在性证明\n1. 身份验证\n1. 股权众筹\n\n可以把区块链的发展类比互联网本身的发展，未来会在internet上形成一个比如叫做finance-internet的东西，而这个东西就是基于区块链，它的前驱就是bitcoin，即传统金融从私有链、行业链出发（局域网），bitcoin系列从公有链（广域网）出发，都表达了同一种概念——数字资产（DigitalAsset），最终向一个中间平衡点收敛。\n\n区块链的进化方式是：\n\n1. 区块链1.0——数字货币\n1. 区块链2.0——数字资产与智能合约\n1. 区块链3.0——DAO、DAC（区块链自洽组织、区块链自洽公司）-->区块链大社会（科学，医疗，教育etc，区块链+人工智能）。\n\n\n\n\n\n"
  },
  {
    "path": "其它/前端和开闭原则.md",
    "content": "# 前端和开闭原则\n\n# 前言\n\n在前端工作日趋复杂的今天，需要有良好的设计思想来指导我们进行程序设计，使我们开发出的系统结构优美，更稳定，更易维护，更易扩展，让我们有更多喝喝茶看看书陪陪家人的时间。\n\n# 什么是开闭原则\n\n## 开闭原则的定义\n\nOpen-Closed Principle 简称 OCP。\n\n1988年，勃兰特·梅耶(Bertrand Meyer)在他的著作《面向对象软件构造(Object Oriented Software Construction)》中提出了开闭原则，它的原文是这样:Software entities should be open for extension,but closed for modification.\n\n一个软件实体应当对扩展开放,对修改关闭.也就是说,我们在设计一个系统/模块/功能的时候,应当使这个模块可以在不被修改的前提下被扩展,换句话说就是,应当可以在不必修改源代码的情况下改变这个系统/模块/功能的行为.\n\n开闭原则是面向对象设计中\"可复用设计\"的基石，是面向对象设计中最重要的原则之一，很多的设计模式都是实现开闭原则的一种手段。\n\n## 解读\n\n软件系统的需求变化是一定并且一直存在的，在变化的过程中，已有的模块不被修改或者修改量极少，没有关键路径上的修改，这就使变化中的软件系统有很强的稳定性。\n\n# 如何实现\n\n怎么做到不改代码就能改变原有系统的功能呢。\n\n1. 识别出系统的可变化和可能可变化的点\n2. 抽象化可变化的点【接口】，并单独实现可变化的点的抽象【接口的实现】\n3. 系统依赖抽象化【接口】而不是依赖于实现。\n4. 对实现的路由单独封装，最好以配置的方式提供。（配置不一定是配置文件，可以是代码，但形式一定要简单直观）\n5. 需要改变功能的时候，只需要提供新的满足抽象化【接口】的实现，并且更改下路由的配置即可\n\n以上过程，基本实现了对原有代码没有任何修改【对配置的修改不算在内,对配置如果做好约定，也可以不用修改，后面例子会讲到】，就改变了系统原有功能。\n\n## 举个栗子\n\n需求：商品列表中，如果是男装类型，商品背景色使用蓝色，点击后弹出男装的价格；如果是女装，商品背景色使用红色，点击后弹出女装的品牌。\n\n以下伪代码实现：\n\n普通代码,我们可能会这么做：\n\n```\n// 渲染html的函数中\nif (商品.type === '男装') {\n    商品.css(背景色,蓝色);\n} else {\n   商品.css(背景色，红色);\n}\n\n\n// 点击事件的函数中\nif (商品.type === '男装') {\n    alert(商品.价格);\n} else {\n    alert(商品.品牌);\n}\n\n```\n\nok，看起来一切都很好，代码上线了。过了一阵，pm告知添加了一种商品类型，童装，商品背景色使用黄色，点击后弹出童装的销量。\n\n那么，代码会被改成下面这样：\n\n```\n// 渲染html的函数中\nif (商品.type === '男装') {\n    商品.css(背景色,蓝色);\n} else if (商品.type === '女装') { // 修改点1 增加女装类型判断\n   商品.css(背景色,红色);\n} else { // 修改点2 增加童装html渲染处理\n   商品.css(背景色,黄色)；\n}\n\n\n// 点击事件的函数中\nif (商品.type === '男装') {\n    alert(商品.价格);\n} else if (商品.type === '女装') { // 修改点3 增加女装类型判断\n    alert(商品.品牌);\n} else { // 修改点4 增加童装点击处理\n    alert(商品.销量);\n}\n```\n\nok，以上是可能的正常代码编写方式，当需求发生变化时，会对原有代码很多地方都要进行修改，因为修改的地方过于琐碎，修改后，心里一定会慌慌的，要嘱咐测试多回归回归原有的逻辑，不能只看新的逻辑，同时修改的过程也要火眼金睛，生怕漏掉某一处的修改。。。代码质量可想而知，我们喝喝茶看看书陪家人的时间可能就这样浪费掉了。\n\n下面看一下符合开闭原则的代码：\n\n只有男装和女装的情况：\n\n```\n// html渲染的函数中\ngetManager(商品).设置背景色();\n\n// 点击事件的函数中\ngetManager(商品).弹出提示();\n\n\n// getManager的实现\nfunction getManager(商品) {\n\n   if (商品.type === '男装') return 男装manager;\n\n   if (商品.type === '女装') return 女装manager；\n}\n\nlet 男装manager = {\n   设置背景色: function() {\n      商品.css(背景色，蓝色);  \n   }\n\n   弹出提示: function() {\n      alert(商品.价格);\n   }\n}；\n\nlet 女装manager = {\n   设置背景色: function() {\n      商品.css(背景色，红色);  \n   }\n\n   弹出提示: function() {\n      alert(商品.品牌);\n   }\n}；\n\n```\nok，代码量好像多了，多了好几个对象和方法。。。我们接着往下看，当需求发生了变化，童装出现的时候，代码如下：\n\n```\n// html渲染的函数中\ngetManager(商品).设置背景色();\n\n// 点击事件的函数中\ngetManager(商品).弹出提示();\n\n\n// getManager的实现\nfunction getManager(商品) {\n\n   if (商品.type === '男装') return 男装manager;\n\n   if (商品.type === '女装') return 女装manager；\n\n   if (商品.type === '童装') return 童装manager；// 修改点1 添加童装管理器的路由，此处可以利用约定的方式而不用修改，后面再讲\n\n}\n\nlet 男装manager = {\n   设置背景色: function() {\n      商品.css(背景色，蓝色);  \n   }\n\n   弹出提示: function() {\n      alert(商品.价格);\n   }\n}；\n\nlet 女装manager = {\n   设置背景色: function() {\n      商品.css(背景色，红色);  \n   }\n\n   弹出提示: function() {\n      alert(商品.品牌);\n   }\n}；\n\n// 修改点2 添加童装管理器，此处其实不算修改，是新增一个对象\nlet 童装manager = {\n   设置背景色: function() {\n      商品.css(背景色，黄色);  \n   }\n\n   弹出提示: function() {\n      alert(商品.销量);\n   }\n}；\n\n\n```\n\n我们可以看到，按照开闭原则设计后的代码，修改点只有一处，修改的地方也可以预判，修改路由方法getManager即可（此处修改其实可以避免）；然后新增一个童装manager即可。\n\n因为修改处变少，我们可以很自信的和测试的同学说，主要测新类型就行了，原来的地方都没改；修改的地方也可以预判到，我们也不会担心遗漏什么修改。\n\n效率不知不觉就提高了，我们多出时间可以喝喝茶看看书陪家人了。\n\n\n如果getManager也无需修改的话，我们只需约定*manager们的对象命名就可以了，比如，统一使用【商品类型+mannager】命名。示例代码如下：\n\n```\n\nfunction getManager(商品) {\n\n   // 这里使用window是假设我们的manager都暴露在window对象里\n   return window[商品.类型+'manager'];\n\n}\n\n```\n按照以上代码，假如现在又加入一个新的类型孕妇装，我们只需实现【孕妇装manager】就可以了，无需修改getmanager的代码，完全的符合开闭原则。\n\n## 接口\n\n接口是面向对象编程语言中很重要的概念，在js语言里其实并没有，不过其实我们已经不知不觉的使用到了接口的概念，比如getManager就返回了一个接口，男装女装童装manager也都实现了这个接口。\n\n前面提到的对变化点的抽象化，其实就是设计一个接口。\n\n### 定义\n\n接口：http://baike.baidu.com/item/%E6%8E%A5%E5%8F%A3/2886384\n\njava接口：http://baike.baidu.com/item/java%E6%8E%A5%E5%8F%A3\n\n一句话理解：接口就是约定。\n\n### 接口的uml类图\n\nhttp://www.cnblogs.com/AngelLee2009/p/3602499.html\n\n# 设计模式\n\n开闭原则是设计模式的基石，设计模式是前人们验证并总结过的如何在满足开闭原则的条件下设计系统的各种设计方法。\n\n设计模式除了让我们复用大师们的成果，还可以让团队间沟通概念统一，更通顺。\n\n我们前面的设计其实是符合普通工厂模式的。\n\nhttp://baike.baidu.com/item/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1212549\n\n# 切忌--过度设计\n\n开闭原则实现的关键点在于抽象化变化，也许我们刚开始不知道该把那部分抽象出来，但是这并不是问题，我们可以遵循简单设计的原则，当变化来了的时候，再重构代码，做到一种满足开闭原则的设计。\n\n切忌到处都抽象化，如果到处都抽象就会导致系统过度设计，过度复杂。这反而是不利于系统的维护。完全的开闭原则是不可能实现的，所以请保持简单设计，在需要的时候做符合开闭原则的设计。\n\n在我们的日常工作中，接到新任务后，在动手前与pm深度的沟通，自己深度的思考，将系统未来可能发生的变化都理解透彻，结合实际情况该抽象化的地方抽象化，不该抽象化的地方简单设计。\n\n# 总结\n\n软件开发过程是条条大路通罗马，让我们选一条最好走的路。\n\n# 后期学习内容推荐\n\n面向对象编程\n\numl统计建模语言\n\n设计模式\n\n\n[评论直达连接](https://github.com/cnsnake11/blog/issues/24)\n"
  },
  {
    "path": "其它/前端应该掌握的一些app开发知识-android篇（1）.md",
    "content": "# 前端应该掌握的一些app开发知识-android篇（1）.md\n\n移动端混合开发是各大公司非常常见的开发模式，作为前端能了解一些原生开发的基础知识，对程序设计，问题分析都会有很大的益处。\n\n此系列文章对android原生app开发的一些基础知识做出了归纳和总结，方便前端的同学快速的了解android开发的知识。\n\n本文主要对知识点进行归纳和总结，不做细节的描述。\n\n在android的第一篇中，我们先总结一下java。\n\n# java\n\nJava是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性，广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网，同时拥有全球最大的开发者专业社群。目前已被oracle收购。\n\n# jdk\n\njdk是Java语言的软件开发工具包。\n\n提供了常用的工具。比如，编译器，打包工具，文档工具，监控工具等。\n\n提供了常用的基础api。比如，基础数据类型，文件操作流，网络操作，线程等。\n\n\n# jvm\n\nJVM是Java Virtual Machine(Java虚拟机)的缩写，JVM是一种用于计算设备的规范，它是一个虚构出来的计算机，是通过在实际的计算机上仿真模拟各种计算机功能来实现的。\n\nJava语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行，至少需要编译成不同的目标代码。而引入Java语言虚拟机后，Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息，使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码)，就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时，把字节码解释成具体平台上的机器指令执行。这就是Java的能够\"一次编译，到处运行\"的原因。\n\n简单来说，jvm就是java语言的宿主环境，就像js和浏览器之间的关系，浏览器就是js语言的宿主环境。\n\n# 编译\n\n因为js是解释型语言，无需编译就能执行。所以，前端的同学们可能还不太理解编译时咋回事。其实编译就是把人编写好的源文件转换成java虚拟机执行效率更高的字节码文件。\n\n字节码文件是经过编译器预处理过的一种文件，是JAVA的执行文件存在形式，它本身是二进制文件，但是不可以被系统直接执行，而是需要虚拟机解释执行，由于被预处理过，所以比一般的解释代码要快，但是仍然会比系统直接执行的慢。\n\n我们编写java的时候使用的是.java文件，编译后会在编译目录生成对应的.class文件。\n\n# ide\n\n开发java常用的ide，有eclipse，idea，NetBeans，jbuilder等。笔者比较推荐idea，后边会介绍的androidStudio，或者前端用的webstorm也都是他家出的，很好用！\n\n可以帮助你做代码提示，校验，编译，打包，单元测试，调试，等等。\n\njava开发如果没有ide的帮助，效率会很低，不像前端开发只要有文本编辑器就可以开工了。\n\njdk jvm 编译 ide 包 类 对象 接口 异常 jar 多线程\n\n# 类class\n\n面向对象编程中的重要的概念，代表的是一个类别，不代表真正的实体（对象）。\n\n比如汽车是一个类别，你家楼下停着的大奔是一个实体（对象），它属于汽车这个类别。\n\n类中可以定义这一个类别所具有的通用的属性（比如，车的长度、宽度等）和功能（比如，车的启动、刹车等）。\n\n# 对象\n\n类是客观存在的，抽象的，概念的东西。而对象是具体的，实际的，代表一个事物。比如汽车是一个类别，你家楼下停着的大奔是一个实体（对象），它属于汽车这个类别。\n\n也可以这样理解：类是对象的模版，对象是类的一个个体。\n\n对象用new关键字实例化。例如 Car car = new Car('蓝色大奔驰');\n\n# 接口\n\nJava接口是一系列方法的声明，是一些方法特征的集合，一个接口只有方法的特征没有方法的实现，因此这些方法可以在不同的地方被不同的类实现，而这些实现可以具有不同的行为(功能)。\n\n接口的重要性毋庸置疑，想设计好一个良好的程序，必须深层次理解接口的意义。\n\n# 多线程\n\njava是支持多线程的语言，这是比js强大的地方。\n\n用好线程是提升性能提高并发的重要手段，但也增加了编程模型的复杂度。\n\n"
  },
  {
    "path": "其它/前端应该掌握的一些app开发知识-android篇（2）.md",
    "content": "# 前端应该掌握的一些app开发知识-android篇（2）.md\n\n接前篇，本次主要整理一下前端应了解的android的一些重要的概念。\n\n# ide\n\n目前几个主流java的ide都支持android的开发，但是目前最主流的，也是官方推荐的还是AndroidStudio。所以，对前端来说，如果你想跑个android的工程来玩，还是用AndroidStudio比较好。\n\n# android Sdk\n\nSDK是(software development kit)软件开发工具包。被软件开发工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。\n\n因此，Android SDK 指的是Android专属的软件开发工具包。\n\n# 模拟器 genymotion\n\n对于开发者来说，模拟器是可选物品，因为你也可以选择真机调试。\n\n如果用模拟器的话，genymotion是首选，因为它的性能是最佳的。\n\nGenymotion是一套完整的工具，它提供了Android虚拟环境。\n\nGenymotion支持Windows、Linux和Mac OS等操作系统，非常容易安装和使用。\n\n# AndroidManifest.xml\n\n学习android，不能不了解AndroidManifest.xml，他是整个安卓程序的入口和描述文件。\n\nAndroidManifest.xml 是每个android程序中必须的文件。\n\n它位于整个项目的根目录，描述了package中暴露的组件（activities, services, 等等），他们各自的实现类，各种能被处理的数据和启动位置。 除了能声明程序中的Activities, ContentProviders, Services, 和Intent Receivers,还能指定permissions和instrumentation。\n\n\n# android四大组件\n\n整个android应用的功能都是由四大组件来完成的，这里我们来总结下。\n\nActivity 显示界面（显示的界面都是继承activity完成的）\n\nService 服务（后台运行的，可以理解为没有界面的activity）\n\nBroadcast Receiver 广播（做广播，通知时候用到）\n\nContent Provider  数据通信（数据之间通信，同个程序间数据，或者是不同程序间通信）\n\n# Activity\n\n对于前端来讲，四大组件中最应该了解的就是Activity了。\n\nandroid中，每一个页面都是一个activity，它上面可以显示一些view控件也可以监听并处理用户的事件做出响应。\n\nactivity就像是页面容器，上面可以放很多的view组件。\n\nactivity本身还具有完整的生命周期事件，在页面的初始化，展现，置于后台，销毁等生命周期事件中，会触发相应的函数。类似小程序中page里的onLoad，onShow等方法。\n\n# 前端最应该了解的view组件---webview\n\nAndroid和ios都有webview。只是其引擎不同，android及ios的webview的引擎都是webkit，对Html5提供支持。\n\n#### 如何使用\n\n1. 添加权限：AndroidManifest.xml中必须使用许可\"android.permission.INTERNET\",否则会出Web page not available错误。\n2. 在要Activity中生成一个WebView组件：WebView webView = new WebView(this);或者可以在activity的layout文件里添加webview控件：\n3.  设置WebView基本信息：如果访问的页面中有Javascript，则webview必须设置支持Javascript。 webview.getSettings().setJavaScriptEnabled(true);  \n4. 设置WevView要显示的网页：互联网用：webView.loadUrl(\"http://www.google.com\"); 本地文件用：webView.loadUrl(\"file:///android_asset/XX.html\");  本地文件存放在：assets文件中。\n5. 如果希望点击链接由自己处理，而不是新开Android的系统browser中响应该链接。给WebView添加一个事件监听对象（WebViewClient)并重写其中的一些方法：shouldOverrideUrlLoading：对网页中超链接按钮的响应。当按下某个连接时WebViewClient会调用这个方法，并传递参数：按下的url。比如当webview内嵌网页的某个数字被点击时，它会自动认为这是一个电话请求，会传递url：tel:123,如果你不希望如此可通过重写shouldOverrideUrlLoading函数解决\n\n#### Webview与js交互\n\nWebView不但可以运行一段HTML代码，还有一个重要特点，就是WebView可以同Javascript互相调用。\n\n通过addJavascriptInterface(Object obj,String interfaceName)方法将一个Java对象绑定到一个Javascript对象中，Javascript对象名就是interfaceName，作用域是Global，这样便可以扩展Javascript的API，获取Android的数据。\n\n同时，在Java代码中也可以直接调用Javascript方法，这样就可以互相调用取得数据了，代码如下：\nWebView.loadUrl(\"javascript:方法名()\");\n\n\n"
  },
  {
    "path": "其它/前端应该掌握的一些app开发知识-ios篇（1）.md",
    "content": "# 前端应该掌握的一些app开发知识-ios篇（1）\n\n移动端混合开发是各大公司非常常见的开发模式，作为前端能了解一些原生开发的基础知识，对程序设计，问题分析都会有很大的益处。\n\n此系列文章对ios原生app开发的一些基础知识做出了归纳和总结，方便前端的同学快速的了解ios开发的知识。\n\n本文主要对知识点进行归纳和总结，不做细节的描述。\n\n在ios的第一篇中，我们先总结一下object-c。\n\n# IDE\n\n编写OC程序最主要的编译环境是Xcode,它是苹果官方提供的IDE,官网中的SDK包括Xcode,可以通过下载SDK来获得它。但是Xcode只支持MacOSX,所以如果要在其它环境下编写OC程序,要使用其它IDE。Linux/FreeBSD用GNUStep,Windows NT5.x(2000,XP)要先安装cywin或mingw,然后安装GNUStep。同时仅仅通过文本编辑器,GCC的make工具也可以用于开发。\n注:如果要使用到Cocoa的话，只能在Apple公司的Xcode上\n\n# 框架\n\nOC编程中主要用到的框架是Cocoa,它是MacOS X中五大API之一,它由两个不同的框架组成FoundationKit 和ApplicationKit。Foundation框架拥有100多个类,其中有很多有用的、面向数据的低级类和数据类型,如NSString,NSArray,NSEnumerator和NSNumber。ApplicationKit包含了所有的用户接口对象和高级类。这些框架本文不做重点介绍,如果要深入了解可以去看Xcode自带的文档。\n\n# object-c\n\nobject-c通常写作objective-c或者obj-c，是根据C语言所衍生出来的语言，继承了C语言的特性，是扩充C的面向对象编程语言。它主要使用于Mac OS X。在MAC OSX系统下，运用苹果提供的SDK等开发工具包，可以用来做IOS开发，开发后的程序在Iphone虚拟机中进行测试，运用的主要语言为Object-c。\n\n# swift\n\nSwift，苹果于2014年WWDC(苹果开发者大会)发布的新开发语言，可与Objective-C*共同运行于Mac OS和iOS平台，用于搭建基于苹果平台的应用程序。\n\nSwift是苹果公司在WWDC2014上发布的全新开发语言。从演示视频及随后在appstore上线的标准文档看来，语法内容混合了OC,JS,Python，语法简单，使用方便，并可与OC混合使用。\n\nXcode目前已有1400万次下载量，而全新的编程语言Swift改变了Obejective-C复杂的语法，并保留了Smalltalk的动态特性，简而言之就是敏捷易用，大家都说苹果的生态圈要优于Google，现在苹果又进一步完善了开发生态圈。\n\n\n# object-c or swift\n\noc的优势是使用时间久，轮子多，社区成熟度高，缺点是语法过于复杂，不易人类阅读。\n\nswift的优缺点正好和oc相反。\n\nSwift是未来的趋势，但是OC还是很重要，因为很多公司的项目都是基于oc的。\n\n\n# 反人类之处\n\n初次接触OC时,会发现许多和其它语言不同的地方,会看到很多的+,-,[ ,] ,@,NS等符号。\n\n例如，反人类的字符串写法是以@开头，@“hello”\n\n# 扩展名\n\nOC是ANSI版本C的一个超集,它支持相同的C语言基本语法。与C一样,文件分为头文件和源文件,扩展名分别为.h和.m。\n\n.h  头文件。头文件包涵类的定义、类型、方法以及常量的声明\n        \n.m  源文件。这个典型的扩展名用来定义源文件，可以同时包含C和Objective-C的代码。\n\n# #import\n\n在OC里,包含头文件有比#include更好的方法#import。它的使用和#include相同,并且可以保证你的程序只包含相同的头文件一次。相当于#include+ #pragma once的组合。每个框架有一个主的头文件,只要包含了这个文件,框架中的所有特性都可以被使用。\n\n# @符号\n\n@符号是OC在C基础上新加的特性之一。常见到的形式有@”字符串”,%@ , @interface,@implement等。@”字符串”表示引用的字符串应该作为Cocoa的NSString元素来处理。@interface等则是对于C的扩展,是OC面向对象特性的体现。\n\n# NSLog()\n\n在OC中用的打印函数是NSLog(),因为OC是加了一点”特殊语料”的C语言,所以也可以用printf()但是NSLog()提供了一些特性,如时间戳,日期戳和自动加换行符等,用起来更方便,所以推荐使用NSLog()。\n\n# id\n\n这是OC新加的一个数据类型,它是一般的对象类型,能够存储任何类型的方法。\n\n# nil\n\n在OC中,相对于C中的NULL,用的是nil。这两者是等价的。\n\n# 定义方法\n\n在OC中一个类中的方法有两种类型：实例方法，类方法。实例方法前用(-)号表明,类方法用(+)表明。\n\n"
  },
  {
    "path": "其它/前端应该掌握的一些app开发知识-ios篇（2）.md",
    "content": "# 前端应该掌握的一些app开发知识-ios篇（2）\n\n移动端混合开发是各大公司非常常见的开发模式，作为前端能了解一些原生开发的基础知识，对程序设计，问题分析都会有很大的益处。\n\n此系列文章对ios原生app开发的一些基础知识做出了归纳和总结，方便前端的同学快速的了解ios开发的知识。\n\n本文主要对知识点进行归纳和总结，不做细节的描述。\n\n在上一篇中已经简单整理了object-c的内容，因为oc的语法结构与js差异化较大，所以，在第二篇文章中，继续总结一下oc的基础。\n\n# NSString\n\n字符串类型，相当于java中的String，js中的\"\"。\n\nNSString *string = @\"Hello World!\";\n\n# NSArray\n\n数组类型，相当于java中的数组，js中的Array。\n\nNSArray *array = [NSArray arrayWithObjects:@\"a\", @\"b\", @\"c\", nil];\n\n\n# NSDictionary\n\n字典类型，相当于java中的Map，js中的object，即键值对的集合。\n\n```\nNSDictionary *dictinary;\ndictionary = [NSDictionary dictionaryWithObjectsAndKeys:\n @\"value1\",@\"key1\",\n@\"value2\",@\"key2\",\nnil,\n];\n\n```\n\n# .h 和 .m\n\n类的声明一般写在.h文件中，而实现则写在.m文件中。.h文件又称为接口文件，它只会规定一个类有哪些成员变量和成员函数，而不去具体实现它。\n\n# 对象的创建和内存的释放\n\n创建一个对象，使用alloc+init系列。然后使用releaseh或者autorelease的方式释放。\n 因为没有垃圾回收机制，Objective-C采用计数器的方式来管理内存，使用的时候要特别小心。\n用retain方法对计数器加1，用release方法对计数器减1\n\n\n初始化时，用alloc函数+init系列函数方法时，计数器的状态会被设为1，使用完毕后，务必记得要用release方法来释放内存。\n\n```\nNSArray *array = [[NSArray alloc] initWithObjects:@\"a\", @\"b\", @\"c\", nil];\n// 对array的一些处理\n[array release]; // 释放array的内存\n```\n 用alloc函数+init系列函数的方法生成的对象也可以委托autorelease来释放。\n\n```\nNSArray *array = [[[NSArray alloc] initWithObjects:@\"a\", @\"b\", @\"c\", nil] autorelease];\n```\n\n# Protocol\n\n类如果声明为符合某种Protocol的话，就要按照Protocol所规定的方法来实现这个类。Protocol和Java中的Interface类似。\n\n# 类的定义\n\n![](media/14881923486983.jpg)\n\n1. 类定义在@interface和@end之间。\n2. MyClass:NSObject的方式定义子类和父类。\n3. {}中定义类的对象成员\n4. －／＋定义的是方法，－定义的是对象的方法，+定义的是类的方法。对象是类的一个实例。对于对象的方法，也即是，当使用该方法之前必须先生成该类的一个特定对象。\n\n# 函数定义和调用\n\n![](media/14881924660850.jpg)\n\n函数的定义由函数的类型标记（Method type identifier）、返回值(return type)、一个或者多个的函数签名关键字(Method signature)、参数的类型和命名信息组成。\n\n函数的真实命名由各个级联的函数签名关键字组成。例如上述函数定义中函数真实的命名是（insertObject:atIndex:）冒号隔开参数的声明。\n\n上述的函数调用可以写成 [myArray insertObject:anObject atIndex:0]; \n\n\n\n\n"
  },
  {
    "path": "其它/前端监控平台.md",
    "content": "# 前端监控平台\n\n# 背景-为什么需要它\n\n# 目标-要实现什么功能，达到什么要求\n\n# 实现思路-根据具体目标，逐个描述\n\n# 架构设计\n\n\n\n"
  },
  {
    "path": "其它/前端资源打包设计.md",
    "content": "# 前端资源打包设计\n\n# 前言\n\n一般我们现在开发web应用都会做模块化开发，上线一个web应用前，都会把前端资源进行打包，也就是合并压缩编译等工作，那么打包的输出结构是如何进行设计的，为什么要这么设计。\n\n最近的面试工作中和很多的面试者聊过这个话题，还没有人能把这个事的来龙去脉说清楚，好点的能说出来打包进行了合并压缩是为了更小的带宽，更少的请求数量；差点的干脆说我执行个gulp命令就好了，具体细节不太清楚。\n\n所以在此把这个问题系统的总结一下。\n\n# 打包的目的\n\n其实整个打包过程会包含很多功能，比如一些编译动作，像jsx编译、es6语法编译、less sass编译等，还会有图片的压缩、图片base64转换等等。\n\n那么除了这些必要的功能性需求外，还有一个非常重要的非功能性需求，需要用设计打包输出结构的方式来解决。\n\n这个非功能需求就是，让用户用最快的时间打开一个页面。\n\n# 浏览器缓存机制\n\n\n# 举个栗子webpack\n\n\n\n\n\n"
  },
  {
    "path": "其它/富文本编辑器1~html转结构化数据.md",
    "content": "# 富文本编辑器1~html转结构化数据\n\n# 行业背景\n\n在目前的互联网环境下，能源源不断产生优质的内容的平台，一定会受到用户的喜爱。\n\n那么如何能让作者们快速产生优质的内容是内容平台一个不可或缺的功能。\n\n一般来说，会使用一个叫富文本编辑器的东东，它大概长成这样：\n\n![](media/15060671959019.jpg)\n\n通过它，作者们就能快速的编写自己的文章，可以进行排版，设置字体样式，插入图片、视频等等内容，让文章美观漂亮内容丰富。\n\n本系列文章会介绍我们在富文本编辑器开发过程中遇到的各种问题及其解决方案。\n\n# 业务描述\n\n富文本编辑器最终产生的是一段html字符串，这段字符串里包含了排版，样式，等信息。\n\n如果把这个字符串直接拿到浏览器【pc浏览器，手机浏览器，手机webview均可】中渲染，不费吹灰之力就会原封不动的展现出来，可以说是省时省力省心。\n\n但现实情况往往不这么理想。。。o(╯□╰)o\n\n公司项目的显示终端有：pc网站，手机端网站，手机端app中，网站都还好是用浏览器来渲染的，无需操心。但手机app中显示内容的时候并没有用webview作为载体，而是原生代码根据json格式的结构化数据来渲染的。比如这样的json数据：\n\n```\n\n[\n{\n\tcontent:'文字文字文字',\n\ttype: '文字'\n},\n{\n\turl:'http://图片地址',\n\ttype: '图片'\n},\n{\n\turl:'http://视频地址',\n\tcover: 'http://视频封面地址',\n\ttitle: '视频标题',\n\ttype: '视频'\n}\n]\n\n```\n\n而富文本编辑器生成的html字符串是这样的：\n\n```\n<p>段落1段落1段落1段落1段落1段落1段落1段落1段落1<img src=\"http://pic09.babytreeimg.com/contentplatform/20170922/FueIRKRv3t6fFQa0kBr8sg5caEQA\" _bbt_json=\"{&quot;t&quot;:&quot;2&quot;,&quot;id&quot;:&quot;4962&quot;,&quot;path&quot;:&quot;http://pic09.babytreeimg.com/contentplatform/20170922/FueIRKRv3t6fFQa0kBr8sg5caEQA&quot;}\">段落2段落2段落2<b>段落2段落2段落2段落2段<i>落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落</i></b><i>2段落2段落2段落</i>2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2</p><br><p><img src=\"http://pic06.babytreeimg.com/contentplatform/20170922/FmSyH6SL_8BD5TYIWERjOqjwPcZ9\" _bbt_json=\"{&quot;t&quot;:&quot;2&quot;,&quot;id&quot;:&quot;4960&quot;,&quot;path&quot;:&quot;http://pic06.babytreeimg.com/contentplatform/20170922/FmSyH6SL_8BD5TYIWERjOqjwPcZ9&quot;}\"><br></p><p>段落3段落3段落3段落3段落3段落3段落3段落3段落3段落3</p>\n```\n\n\n所以，就需要一个转换引擎，来将html字符串转换为结构化的数据。\n\n# 难点\n\n比如我们如下的html：\n\n```\n\n<h1>\n     <p>\n             <i>\n                 我是文本1\n                 <img src=\"地址\" />\n                 我是<u>文本</u>2\n                 \n            </i>\n    </p>\n</h1>\n\n```\n\n拆成结构化json，应该是这样的：\n\n```\n\n[\n{\n\tcontent:'<h1><p><i>我是文本1</i></p></h1>',\n\ttype: '文字'\n},\n{\n\turl:'http://图片地址',\n\ttype: '图片'\n},\n{\n\tcontent:'<h1><p><i>我是<u>文本</u>2</i></p></h1>',\n\ttype: '文字'\n}\n]\n\n```\n\n仔细看一下就会发现，除了执行字符串分割之外，还要进行html开始标签和结束标签的补位。\n\n文本1需要对html结束标签进行补位，也就是```</i></p></h1>```。\n\n文本2需要对html开始标签进行补位，也就是```<h1><p><i>```。\n\n\n# 算法原理\n\n一开始，想直接对html字符串的解析工作来把补位标签计算出来，发现实现的复杂度有点高，而且对字符串编程的代码可读性也比较差。\n\n后来突然想到，html字符串本身就是一个dom树形结构，可不可以利用一下dom树的接口来辅助进行解析呢。\n\n所以整个算法主要分成2步骤。\n\n### 步骤1 利用dom树接口，分析出中间结果\n\n上面例子中的中间结果应该是这样一个对象的数组：\n\n```\n\n[\n{\n\ttype: 图片,\n\tsrc: '图片地址',\n\tstartTag: '<h1><p><i>',\n\tendTag: '</i></p></h1>'\n}\n]\n\n```\n如何分析出这样的中间结果呢？\n\n首先，对dom树进行递归遍历，遍历到图片的el的时候，在对图片el不停的获取parentNode，直到需要解析的dom容器的顶端，按顺序记录下parentNode的tag名称，就是我们要的结果。比如如下的代码：\n\n```\n\ngetTags = (el) => {\n        let el2 = el;\n        let start = '';\n        let end = '';\n        while (true) {\n            el2 = el2.parentNode;\n            if (el2 === this.container) {\n                return { start, end };\n            }\n            if (el2 && el2.tagName) {\n                end = `${end}</${el2.tagName.toLowerCase()}>`;\n                start = `<${el2.tagName.toLowerCase()}>${start}`;\n            }\n        }\n    }\n\n```\n所以，实际上我们是利用了dom树的childNodes接口进行递归遍历，找到目标元素之后，又利用dom树的parentNode接口进行向上查找。\n\n### 步骤2 利用中间结果，分割字符串，补齐html标签\n\n有了中间结果数组之后，后面就简单了，遍历中间结果数组，针对数组的每一项，找到html字符串中对应的字符串【比如```<img src=\"地址\" />```】，将其前面的部分分割出来【也就是```<h1><p><i>```我是文本1】，并在其后面补上中间结果中的endTag【```</i></p></h1>```】，然后将中间结果后面的字符串【```我是<u>文本</u>2</i></p></h1>```】加上startTag【```<i><p><h1>```】，然后继续遍历即可。代码如下：\n\n```\n\nprocessHTML = () => {\n        let html = this.container.innerHTML;\n        if (this.domResult.length === 0) {\n            this.result.push({\n                t: '1',\n                txt: html,\n            });\n            return;\n        }\n        for (let i = 0; i < this.domResult.length; i += 1) {\n            const one = this.domResult[i];\n            const index = html.indexOf(one.html);\n            const txt = html.substring(0, index) + one.tags.end;\n\n            this.result.push({\n                t: '1',\n                txt,\n            });\n            this.result.push(one.json);\n\n            html = html.substring(index + one.html.length, html.length);\n            html = one.tags.start + html;\n\n            if (i === this.domResult.length - 1 && html) {\n                this.result.push({\n                    t: '1',\n                    txt: html,\n                });\n            }\n        }\n    }\n\n```\n\n# 完整代码\n\n```\n\nimport { BBT_JSON } from './Constant';\n\nexport default class HtmlParser {\n\n    constructor(container) {\n        this.container = container; // 容器对象\n        this.domResult = []; // 解析dom的结果 用于替换字符串用\n        this.result = []; // 最终的运行结果\n    }\n\n    getData = () => {\n        // 1 遍历dom树 解析需要替换的节点和替换的内容 使用标记标签替换原标签\n        this.processDom(this.container);\n\n        // 2 执行html字符串的分割和标签补齐 并生成格式化数据\n        this.processHTML();\n\n        return this.result;\n    }\n\n    processDom = (root) => {\n        const childs = root.childNodes;\n        for (let i = 0; i < childs.length; i += 1) {\n            const el = childs[i];\n            if (el.getAttribute && el.getAttribute(BBT_JSON)) {\n                const tags = this.getTags(el);\n                this.domResult.push({\n                    html: el.outerHTML,\n                    json: JSON.parse(el.getAttribute(BBT_JSON)),\n                    tags,\n                });\n            }\n            this.processDom(el);\n        }\n    }\n\n    processHTML = () => {\n        let html = this.container.innerHTML;\n        if (this.domResult.length === 0) {\n            this.result.push({\n                t: '1',\n                txt: html,\n            });\n            return;\n        }\n        for (let i = 0; i < this.domResult.length; i += 1) {\n            const one = this.domResult[i];\n            const index = html.indexOf(one.html);\n            const txt = html.substring(0, index) + one.tags.end;\n\n            this.result.push({\n                t: '1',\n                txt,\n            });\n            this.result.push(one.json);\n\n            html = html.substring(index + one.html.length, html.length);\n            html = one.tags.start + html;\n\n            if (i === this.domResult.length - 1 && html) {\n                this.result.push({\n                    t: '1',\n                    txt: html,\n                });\n            }\n        }\n    }\n\n    getTags = (el) => {\n        let el2 = el;\n        let start = '';\n        let end = '';\n        while (true) {\n            el2 = el2.parentNode;\n            if (el2 === this.container) {\n                return { start, end };\n            }\n            if (el2 && el2.tagName) {\n                end = `${end}</${el2.tagName.toLowerCase()}>`;\n                start = `<${el2.tagName.toLowerCase()}>${start}`;\n            }\n        }\n    }\n}\n\n\n```\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "其它/富文本编辑器2~复制粘贴的处理.md",
    "content": "# 富文本编辑器2~复制粘贴的处理\n\n# 业务场景分析\n\n在编辑文章的过程中，复制粘贴是一个很常见的场景，一般会有2种情况。\n\n#### 1 站外复制\n\n用户的文章已经写好，并保存在第三方平台，比如微信公众平台、头条编辑平台、word中等。此时，用户会直接复制第三方平台中的文章，然后在我们的编辑器中直接粘贴。\n\n最理想的情况是，将用户复制的文本、样式布局，及其包含的图片、音频、视频等内容全部能粘贴到编辑器中。\n\n但这里面会有一些问题：\n\n1. 样式布局，会自动被转成行内样式，如果在webview中展示没有问题，但我们需要显示到原生app页面中，是无法显示的\n2. 图片，其实就是一个url连接，我们不能用这个url，因为这个url是站外的，很不安全，也很可能被防盗链拦住。【这里其实可以想办法把图片通过url下载下来，然后传到我们的平台中，然后图片就可以一键复制过来了】\n3. 视频、音频，不同平台的实现不同，到我们这也不同，没法统一处理，并且，也存在防盗链的问题。\n\n综上，从站外复制的内容，我们只能去掉其中的样式布局、图片等内容，只保留文本和段落信息。\n\n#### 2 在编辑器中复制\n\n用户编辑的过程中，可能希望通过编辑器调整文章内容的时候，会用到剪切粘贴，复制粘贴。这时候，我们必须做到内容中的样式布局，图片，视频，音频等内容都能原样保持不变。\n\n# 相关事件、接口\n\n会用到的事件有：\n\n1. cut 剪切会触发\n2. copy 复制会触发\n3. paste 粘贴会触发\n\n会用到的接口有：\n\n1. clipboardData.getData('text/plain')以纯文本的形式，获得剪贴板里的数据\n2. clipboardData.getData('text/html')以html的格式，获得剪贴板里的数据\n2. window.getSelection 在复制的事件中，可以获得选中的内容\n2. document.execCommand('insertText'，str) 把str作为text插入到编辑器中\n3. document.execCommand('insertHTML', str) 把str作为html插入到编辑器中\n\n# 站内站外的识别算法 --- 不完美算法1\n\n首先，最重要的算法是，要能识别出来，本次粘贴，是从站外复制的内容，还是从站内复制的内容。\n\n翻阅了mdn关于剪贴板、复制粘贴的相关接口，并没有接口或者事件能直接作出判断，所以得自己想办法，而我们的能用到的接口就是上面列举的。\n\n所以，只想到了并不完美的算法，某些情况会失效。\n\n1. 在编辑器中，监听cut和copy事件，并记录下复制或粘贴的字符串。\n2. 在paste事件中，从剪贴板中获得当前要粘贴的字符串\n3. 将两者进行比较，相等说明是站内，否则是站外\n\n很明显，如果刚好站内先复制一个内容，然后去站外也复制一个内容，两个内容的字符串完全相等，判断就会产生错误，但这种情况发生的几率应该极低，所以，暂且忽略。\n\n#### 一个大坑\n\n某些情况会出现，算法失效的情况，后来发现和空格有关。\n\n同样是看起来都一样的空格，但是有2种ASCII码，一个是160，一个是32，平时我们用的都是32，但是在复制的时候可能会产生160的，原因未知。\n\n所以，在保存第一个字符串的时候，要将160的转成32的，否则判断会不准确。\n\n```\nconst space160 = String.fromCharCode(160);\n const re = new RegExp(space160, 'g'); // 替换160的空格为正常32的空格 \nthis.lastCopyStr = window.getSelection().toString().replace(re, ' ');\n```\n\n# 站外数据的格式化\n\n站外粘贴的时候，我们要从剪贴板获得纯文本的数据【clipboardData.getData('text/plain')】，然后调用document.execCommand('insertText'，str) 即可。\n\n#### 解决图片alt问题 --- 不完美算法2\n\n在从今日头条的文章中复制粘贴的时候，如果复制的内容中有图片，会将图片的alt的内容作为文本也复制进来。\n\n如何去掉呢？\n\n1. 通过clipboardData.getData('text/html')获得html信息，可以html信息，同时，可以通过正则来找出alt的内容都是什么。\n2. 然后通过clipboardData.getData('text/html')获得纯文本信息，将第一步找到的alt的内容统统替换为空字符串。\n\n产生的问题也很明显，如果文本中有和alt内容一致的内容，就会产生误操作。所以算法里增加了一项判断：\n\n文本中，只出现一次alt的内容时才进行删除动作。\n\n在这种极端情况下，用户就会把alt的内容也粘贴进来，但是，没什么大的影响，可以忽略。\n\n# 站内数据的格式化\n\n在站内复制粘贴的时候，某些浏览器会自动的往html标签上添加行内样式，很恶心。。。。。所以，站内的粘贴，我们要拦截paste事件，然后，从剪贴板中拿到要粘贴的内容【clipboardData.getData('text/html')】，去掉其中的行内样式，然后调用document.execCommand('insertHTML', str)即可。\n\n# 心得\n\n算法有时候并不完美，但只要满足了大部分的主要使用场景，也是可以使用的。\n\n所以，在开发设计过程中，要能识别出主要场景，要尽量保证主要使用场景的功能，次要场景可以进行必要的忽略。\n\n任何事物都是符合幂次定律的。牢记这一点，事半功倍！\n\n\n"
  },
  {
    "path": "其它/富文本编辑器3~自定义元素.md",
    "content": "# 富文本编辑器3~自定义元素\n\n# 业务场景\n\n编写文章的过程中需要插入一些特殊的元素。\n\n比如，商品：\n\n![](media/15078008076304.jpg)\n\n比如，音频：\n\n![](media/15078008240143.jpg)\n\n总结一下，自定义元素就是文字图片等基础元素组成的，具有某种固定样式的html结构。\n\n# 方案探索过程\n\n其中主要要解决的问题就是：\n\n**自定义元素是一个整体，内部的元素不能被编辑**\n\n因为整个富文本编辑器都是用contenteditable=true实现的，所以很自然的想到了方案1.\n\n#### 方案1 contenteditable=\"false\"\n\n在自定义元素上添加contenteditable=\"false\"，就可以保证其子元素不可编辑。但是经过测试，这个方案会有2个不好解决的问题：\n\n1. 光标选择自定义元素的开始和结束会选不上\n2. 复制粘贴自定义元素会自动在头尾加br\n\n所以放弃了方案1。\n\n然后又想到了iframe，因为iframe的内部是独立的一个页面，不会受父页面的根组件定义的contenteditable=\"true\"的影响，所以，应该可以保证自定义元素的完整性。\n\n#### 方案2 iframe\n\n经过测试，iframe只存在一个问题：\n\n**iframe被复制粘贴后，里面的html会被丢弃**\n\n其实这个问题是因为，当复制剪切粘贴iframe元素后，iframe都会被重新加载，由于iframe里面的html是添加的时候用js装载进去的，所以，当复制剪切粘贴的时候，并没有触发这个动作。\n\n因为是被重新加载，所以复制剪切粘贴会触发iframe的onload事件，利用这个事件来触发js的装载html动作即可。\n\n# 方案设计\n\n因为考虑到后边会添加很多新的自定义元素，所以，要把通用的逻辑进行封装，后边只需添加新元素的个性化定义就可以了。\n\n这里应用了模板模式：定义一个操作中的算法的骨架，而将步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。\n\n将【onload事件，自定义元素数据的获取，不同自定义元素渲染器的路由，html装载到iframe】作为算法的骨架，这部分代码写完就会保持不变，未来会自动复用。\n\n将【生成html的算法，getHtml，getCss，getClass】作为抽象的接口，再有新的自定义元素只需实现这2个接口即可。\n\n# 部分代码\n\n骨架代码：\n\n```\nexport default class TextInput2IframeRender {\n\n    static render = (event) => {\n        const ifr = event.target;\n        const doc = ifr.contentDocument;\n        // const win = ifr.contentWindow;\n        const json = JSON.parse(ifr.getAttribute(BBT_JSON));\n\n        let render; // 路由要使用的render\n        if (json.t === '3') { // 商品\n            render = GoodsRender;\n        } else if (json.t === '5') { // 音频\n            render = AudioRender;\n        }\n\n        if (render) {\n            ifr.setAttribute('class', render.getClass());\n            doc.body.insertAdjacentHTML('beforeend', render.getCss());\n            doc.body.insertAdjacentHTML('beforeend', render.getHtml(json));\n        } else {\n            throw new Error(`找不到t=${json.t}的iframe处理器`);\n        }\n    }\n}\n\n```\n\n商品接口的实现代码：\n\n```\n// render需要实现getHtml 和 getCss 和 getClass 三个接口即可\nclass GoodsRender {\n    static getHtml(content) {\n        const html = [];\n        html.push('<div class=\"content-good-wrapper2\" >');\n        html.push(`<div class=\"img\" style=\"background-image: url(${content.main_picture})\" ></div>`);\n        html.push('<div class=\"content\" >');\n        html.push(`<a href=${content.pc_link} target=\"blank\" >`);\n        html.push(content.main_title);\n        html.push('</a>');\n        html.push('<div>');\n        html.push(`￥${content.price}&nbsp;&nbsp;`);\n        html.push(`<span>${content.sales_count}人已囤</span>`);\n        html.push('</div></div></div>');\n        return html.join('');\n    }\n    static getCss() {\n        const css = [];\n        css.push('<style>');\n        css.push('.content-good-wrapper2{padding-left: 90px;position: relative;max-width: 530px;}');\n        css.push('.content-good-wrapper2 .img {width: 80px;height: 80px;position: absolute;left: 0;top: 0;background-position: center;background-size: cover;}');\n        css.push('.content-good-wrapper2 .content {height: 80px;}');\n        css.push('.content-good-wrapper2 a{color: #23344B;font-size: 14px;display: inline-block;padding-top: 6px;height: 50px;}');\n        css.push('.content-good-wrapper2 a:hover{text-decoration: underline;color: #28B3CA;}');\n        css.push('.content-good-wrapper2 div{color: #E7317D;font-size: 18px;}');\n        css.push('.content-good-wrapper2 span{color: #6B7C93;font-size: 12px;text-indent: 10px;}');\n        css.push('</style>');\n        return css.join('');\n    }\n    static getClass() {\n        return 'goods';\n    }\n}\n```\n\n\n"
  },
  {
    "path": "其它/富文本编辑器4~站外图片的粘贴.md",
    "content": "# 富文本编辑器4~站外图片的粘贴\n\n# 业务场景分析\n\n在第二篇文章中，介绍过如何进行站外内容的复制粘贴，当时将html元素都转换为带段落的文字，并且将图片等其它标签直接去掉了。\n\n这样做，对用户的使用并不友好，用户想把站外的文章连文字一起粘贴到本站的过程，被割裂了，所以，要想办法将站外图片一并粘贴过来。\n\n# 方案过程\n\n在粘贴的时候，我们获得的剪贴板的内容其实是一段html字符串.\n\n首先我们要将html字符串中的图片元素的src获得到，然后想办法将图片的url转存到我们自己的文件服务器上。\n\n这里我们使用了七牛的url转存服务，简单来说，这个服务的输入就是一个第三方的图片url，返回就是，这个图片被转存到七牛我们自己账户下面后的url。真是轻松加愉快。\n\n但是要注意的是，这个过程可能会比较慢，也会有失败的可能性，所以要做好特殊情况的处理。\n\n再获得了我们自己的图片url后，我们需要将原有的html字符串中的图片替换为我们自己的url，然后删掉没用的html标签和样式，再粘贴到我们的编辑器中就可以了。\n\n应用了这个方案后，再大部分情况下，用户可以直接ctrl + a,ctrl + c, ctrl + v,就将一篇完整的带图片的文章粘贴到我们的编辑器中了。\n\n\n# 关键代码\n\n```\n\ndealOutter = (text, html) => { // 站外处理     if (html.indexOf('<img ') === -1) { // 没有图片 走原来的算法         this.pasteText(text, html);     } else {         this.pasteSelection = document.getSelection().getRangeAt(0);         this.userCancelPaste = false;         const uploadId = String(window.Math.random()); // 上传id 保证多线程操作不会互相影响         this.uploadId = uploadId;         const imgs = html.match(/<img [^>]+>/g);         for (let i = 0; i < imgs.length; i += 1) {             // todo 容错 和 兼容不同网站             imgs[i] = imgs[i].match(/src=\"[^\"]+\"/)[0]                 .replace('src=\"', '')                 .replace('\"', '')                 .replace(/&amp;/g, '&');         }         let html2 = html.replace(/<img [^>]+>/g, '@@@@BBT_IMG@@@@');         html2 = html2             .replace(/<\\/p>/g, '\\n')             .replace(/<\\/div>/g, '\\n')             .replace(/<br>/g, '\\n')             .replace(/<[^>]+>/g, '')             .replace(/&nbsp;/g, ' ');         this.parseImgUrls(imgs, html2, 0, uploadId);     } }\n\n\nparseImgUrls = (imgs2, html, index, uploadId) => {     if (this.isCanceled(uploadId)) {         return;     }     const imgs = imgs2;     const fd = new window.FormData();     this.showMsg(`<br/>您粘贴的内容中含有${imgs.length}张图片<br/>正在处理第${index + 1}张...`);     fd.append('url_list[]', [imgs[index]]);     window.fetch('/image/upload_by_urls', {         method: 'POST',         credentials: 'include',         body: fd,     })         .then(res => res.json())         .then((res) => {             if (res.status !== 'success' || res.data.length !== 1 || !res.data[0].photo_id) {                 imgs[index] = { t: '100', url: errorImgSrc, alt: '图片解析失败.' };             } else {                 imgs[index] = res.data[0];             }             this.parseImgUrl(imgs, html, index, uploadId);         })         .catch((e) => {             console.log(e);             imgs[index] = { t: '100', url: errorImgSrc, alt: '图片解析失败.' };             this.parseImgUrl(imgs, html, index, uploadId);         }); }  parseImgUrl = (imgs, html, index, uploadId) => {     if (this.isCanceled(uploadId)) {         return;     }     if (imgs.length - 1 === index) { // 最后一个         const texts = html.split('@@@@BBT_IMG@@@@');         document.getSelection().removeAllRanges();         document.getSelection().addRange(this.pasteSelection);         texts.map((text, i) => {             document.execCommand('insertText', false, text);             if (i < imgs.length) {                 const json = {                     t: imgs[i].t || '2',                     id: imgs[i].photo_id,                     path: imgs[i].url,                 };                 const imgHtml = `<img alt=\"${imgs[i].alt}\" src=\"${imgs[i].url}\" ${BBT_JSON}='${JSON.stringify(json)}' />`;                 document.execCommand('insertHtml', false, imgHtml);             }             return null;         });         this.hideMsg();     } else {         this.parseImgUrls(imgs, html, index + 1, uploadId);     } }\n\n```\n\n"
  },
  {
    "path": "其它/富文本编辑器的命令执行.md",
    "content": "# 富文本编辑器的命令执行\n\n当一个HTML文档切换到设计模式(designMode)时，文档对象暴露 execCommand方法，该方法允许运行命令来操纵可编辑区域的内容。大多数命令影响文档的选择（粗体，斜体等），而其他命令插入新元素（添加链接）或影响整行（缩进）。\n\n使用此方式对内容产生的修改，会进入浏览器的执行动作堆栈，所以，浏览器的撤销和重做也会对其有效。\n\n## 语法\n\n```\nbool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)\n\n```\n\n返回值：Boolean ，如果是 false 则表示操作不被支持或未被启用。\n\n参数：\n\n1. aCommandName 一个 DOMString ，命令的名称。可用命令请看后面的常用命令清单 。\n1. aShowDefaultUI 一个 Boolean， 是否展示用户界面，一般为 false。Mozilla 没有实现。\n1. aValueArgument 一些命令（例如insertImage）需要额外的参数（insertImage需要提供插入image的url），默认为null。\n\n\n## 常用命令清单：\n\n#### bold\n开启或关闭选中文字或插入点的粗体字效果。\n\n#### formatBlock\n添加一个HTML块式标签在包含当前选择的行, 如果已经存在了，更换包含该行的块元素 (在 Firefox中, BLOCKQUOTE 是一个例外 -它将包含任何包含块元素). 需要提供一个标签名称字符串作为参数。几乎所有的块样式标签都可以使用(例如. \"H1\", \"P\", \"DL\", \"BLOCKQUOTE\"). \n\n\n#### insertHTML\n在插入点插入一个HTML字符串（删除选中的部分）。需要一个HTML字符串作为参数。\n\n#### insertText\n在光标插入位置插入文本内容或者覆盖所选的文本内容。\n\n\n#### italic\n在光标插入点开启或关闭斜体字。\n\n#### justifyCenter\n对光标插入位置或者所选内容进行文字居中。\n\n#### justifyFull\n对光标插入位置或者所选内容进行文本对齐。\n\n#### justifyLeft\n对光标插入位置或者所选内容进行左对齐。\n\n#### justifyRight\n对光标插入位置或者所选内容进行右对齐。\n\n#### removeFormat\n对所选内容去除所有格式\n\n#### underline\n在光标插入点开启或关闭下划线。\n\n\n\n"
  },
  {
    "path": "其它/开坑机器学习？.md",
    "content": "# 开坑机器学习？\n\n# 来由\n\n如果你关注了前一阵的柯杰大战阿尔法狗，你就会知道，人工智能阿尔法狗3比0完胜了人类最强柯杰，并且阿尔法狗被各围棋八段九段誉为神一样的对手。\n\n在围棋领域，人工智能的水平已经远远超出人类的最高水平。\n\n最近几年，得益于信息数据、云计算等技术的爆炸式发展，人工智能领域也飞速发展，以前只有在科幻小说、科幻电影里才能看到的机器智慧也许很快就会变为现实。\n\n所以，在看完人机大战之后，除了感叹科技的进步之外，心里对人工智能的原理充满了无限的好奇，很迫切的想知道阿尔法狗是个什么东西，是怎么做出来的，人工智能领域是如何把人类智慧转换为计算机逻辑算法的。\n\n在好奇心的驱使下，准备开一个新坑 --- 机器学习 。\n\n# 为什么标题是问号\n\n本篇的标题【开坑机器学习？】是问号结尾的，这不是笔误。\n\n因为我预判到写这一系列文章会异常的艰难，因为涉及到各种数学、概率的让人头大的算法理论，各种分布式计算的复杂框架，不知道自己是否能坚持下去，能坚持下去多久。。。\n\n真的是好奇害死猫，努力吧。\n\n\n\n\n"
  },
  {
    "path": "其它/弱特征广告方案.md",
    "content": "# 弱特征广告方案\n\n# 前言\n\n本文参考了以下两篇文章，如有雷同，也是必然。\n\nhttp://testudy.cc/tech/2016/11/29/ad.html\n\nhttp://testudy.cc/tech/2016/12/01/ad-manager.html\n\n# 原理\n\n浏览器对广告的屏蔽主要有两种手段，一个是拦截广告相关的请求，一个是使用样式来隐藏掉广告。本文主要针对第二个手段进行若特征处理。\n\n在手段二中，一般浏览器拦截页面内广告会使用css选择器，比如qq浏览器的规则：\n\n```\nhide=###app_mask,##.load-app,##div[class^=\"bbt-pregnant-timeline bbt-pregnant-timeline-\"],##div[class^=\"bottom-layer-\"],##div[class=\"bbt-header-dingtong\"],##div[class*=\"-header-pregnancy\"],##div[class*=\"-footer-pregnancy\"],##section[class$=\"-brand\"],##section[class$=\"-tool\"],##div[class$=\"-footer-expand\"]\nfilter=/base.js?v*&method=$third-party /www/default/base.js$third-party\n```\n\n从规则可以看出，弱特征化其实就是将class弱特征化。\n\n# 方案1 纯前端方案\n\n方案描述：\n\n1. 广告模板包括css，html和js，页面直接输出此模板\n2. 广告引擎js会读取模板，并将css中的class名称替换成随机，同时替换html和js的class名称\n3. 最后将替换后的模板嵌入到页面中\n\n优势：\n\n1. 每次算法都是随机，通过classname来定位广告基本不可能\n\n劣势：\n\n1. 插入html的位置不好确定，位置可能会成为新的特征\n2. 广告模板是特征很强的元素，不能加密，直接输出到html中，线索被暴露了\n3. 广告模板转换为真正的广告html是一个关键路径方法，理论上，可以通过上述线索较为容易的找到，找到后，浏览器引擎可以在方法执行前，将此方法置空\n4. 纯前端渲染，页面会有一定时间的阻塞\n\n总结：\n\n被破解的线索会比较多，会阻塞页面渲染。\n\n# 方案2 前后端结合方案\n\n方案描述：\n\n1. html和css中使用php函数getName(name)来定义class,css要定义在php模板中，不能外联，外联就不能使用php方法了。\n2. getName中根据传入的name和盐做md5，将此md5作为返回值\n3. js中实现同样的getName函数来计算真实的classname\n4. js盐的获取方式要隐藏在页面中\n5. 所有的js文件必须加密\n6. 盐是随机生成的，由php决定每一次的请求的盐\n\n优势：\n\n1. 因为在html中name是看不到的，只能看到classname，在js里只有name而没有classname，且js文件是加密的，所以在js中不宜搜索，不宜找到关键算法\n2. 不会阻塞页面渲染\n3. 盐是php定义的，在js获取盐的时候会暴露一次，理论上有暴露的可能性，但几率不高，而且就算暴露了，也还需要破解我们的加盐的位置，这在js里都是加密存放的，难度较大\n4. 每次classname都是随机，想要通过classname破解基本不可能。\n\n劣势：\n\n1. 因为把css定义在页面中，使得html文件增大，浪费了流量，还好这个流量不大。\n\n总结：\n\n算法不宜破解，html文件增大\n\n# 方案3 全部使用行内样式\n\n优势：\n\n1. 毫无特征？？？\n\n劣势：\n\n1. 写代码挺费劲。。。\n2. 如果js想操作元素，因为毫无特征，好像操作不了了。。。\n\n总结\n\n此路不通，毫无特征对业务js来说也是不行的。\n\n# 结论\n\n我们最后决定使用方案二，虽然有一定的流量的损耗，但不能被破解是我们更关注的点，总不能费半天劲，只能防备一时。\n\n# 改进的思考\n\n方案二的硬伤是css写到了html中，增大了html文件，且不能享用到css外联带来的缓存等好处。也想到了如下的方案进行调整：\n\n方案四：前后端结合且css静态化方案\n\n方案描述：\n\n1. 基本思路同方案二，这里描述不同的点\n2. css写到单独的css文件中，在编写css文件中的classname时，使用变量的方式来写，比如.title写成.${title}\n3. 与php约定盐的声明，每一个版本都会有多个盐，考虑放到一份配置文件中，php和css处理插件都能读取到即可。\n4. 写gulp/grup/webpack插件，在编译css文件时，将变量替换成加盐并md5后的结果，也就是css文件中内置了多套盐产生的classname\n5. php在处理每一次请求中，随机决定使用哪个盐，并告知js。\n\n优势：\n\n1. 方案二的优点加上可以将css文件外联引用\n\n劣势：\n\n1. 每一个版本都有固定数目的盐，有可能会被暴力枚举破解，但是这种情况我们只要换一组盐就好了，比如我们可以定期或者每次发版的时候来换盐，问题不大。\n2. 盐生成的所有classname都在css文件中，较为好找，所以一定不能使用逗号连写的方式生成，且同一组不同盐的样式不要放到一起，增大被破解的难度。而且一定要做混淆。\n3. 增大了css文件，但是量应该并不大，且能利用到缓存。\n\n总结：\n\n理论上有被暴力破解的可能，被暴力破解后就要换盐重新生成发布一次；css使用了外联的方式，可以利用到缓存。\n\n因为会被暴力破解，所以也并不完美。暂时还是推荐使用方案二。\n\n[评论直达连接](https://github.com/cnsnake11/blog/issues/27)\n"
  },
  {
    "path": "其它/微信二次分享.md",
    "content": "# 微信二次分享遇到的一些坑\n\n# 什么是微信二次分享\n\n微信二次分享指的是，当从app中调用微信sdk分享一篇文章之后，这个分享会在微信中按照固定的格式进行显示，比如下面这样：\n\n![](media/15084912795540.jpg)\n\n\n\n但是，如果在微信中打开这个文章，再次将其分享给别人，就会显示成这样：\n\n![](media/15084912948926.jpg)\n\n如何让这类二次分享也能显示成第一种的样式呢，微信官方已经给了解决方案。\n\n# 官方使用条件\n\n1. 微信公众平台配置安全域名。\n\t\n\t先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”，可以按照提示进行填写，最多填写三个。\n\n2. 引入js文件。\n\n在需要调用JS接口的页面引入如下JS文件，（支持https）：http://res.wx.qq.com/open/js/jweixin-1.2.0.js\n\n3. 通过wx.config接口认证权限等信息。\n\n所有需要使用JS-SDK的页面必须先注入配置信息，否则将无法调用。其中，签名等信息需要后端生成。\n\n```\nwx.config({\n    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来，若要查看传入的参数，可以在pc端打开，参数信息会通过log打出，仅在pc端时才会打印。\n    appId: '', // 必填，公众号的唯一标识\n    timestamp: , // 必填，生成签名的时间戳\n    nonceStr: '', // 必填，生成签名的随机串\n    signature: '',// 必填，签名，见附录1\n    jsApiList: [] // 必填，需要使用的JS接口列表，所有JS接口列表见附录2\n});\n```\n\n4. 设置分享的数据\n\n比如分享给朋友的\n```\nwx.onMenuShareAppMessage({\n    title: '', // 分享标题\n    desc: '', // 分享描述\n    link: '', // 分享链接\n    imgUrl: '', // 分享图标\n    type: '', // 分享类型,music、video或link，不填默认为link\n    dataUrl: '', // 如果type是music或video，则要提供数据链接，默认为空\n    success: function () { \n        // 用户确认分享后执行的回调函数\n    },\n    cancel: function () { \n        // 用户取消分享后执行的回调函数\n    }\n});\n```\n\n# 遇到的问题\n\n1. 域名不对\n\n这个问题是因为页面的地址的域名必须要在微信公众平台后台配置，否则就会通不过校验。要注意，源头url是app调用微信sdk产生的，所以，要在app中修改这部分代码。\n\n因为公众平台配置的数量是有限制的，而且只能配置外网url，所以建议app中判断如果是测试环境就写死一个测试环境的url。\n\n2. 签名不对 invalid signature \n\n签名是由后端通过当前页面的url和其它一些参数生成的，一般是由于后端获取的当前页面的url不对造成的，所以，此处建议要在后端生成签名之前将url打印出来，然后与当前页面的url就行比较，差一个字符都不行。\n\n\n"
  },
  {
    "path": "其它/数据结构--树型数据的处理(1).md",
    "content": "# 数据结构--树形数据的处理(1)\n\n# 定义\n\n以下定义摘自互联网。\n\n在计算机科学中，树是非常有用的抽象概念。我们形象的去描述一棵树，一个家族的老祖可能有两个儿子，这两个儿子一个有一个儿子，一个有三个儿子，像这样发展下去的一个族谱，就是一个树，如图所示。\n\n![image](http://www.cppblog.com/images/cppblog_com/cxiaojia/tree_1.png?_=2637948)\n\n就像一棵真正的树一样，我们把老祖称为树根,两个字儿是分叉开的两个树枝，这两棵树枝可以继续向下分成N个树枝，循环下去，一直到长出叶子为止。\n\n我们把老祖或者树根称为根（root）节点，老祖的儿子称为子节点，每个儿子作为根节点又可以形成一棵树，我们把这样的树称为根节点的子树。\n\n树的标准定义：\n\n树（tree）是包含n（n>0）个节点的有穷集合，其中：\n\n1. 每个元素称为节点（node）；\n1. 有一个特定的节点被称为根节点或树根（root）。\n1. 除根节点之外的其余数据元素被分为m（m≥0）个互不相交的结合T1，T2，……Tm-1，其中每一个集合Ti（1<=i<=m）本身也是一棵树，被称作原树的子树（subtree）。\n\n树具有以下特点：\n\n1. 每个节点有零个或多个子节点。\n1. 每个子节点只有一个父节点。\n1. 没有父节点的节点称为根节点。\n\n\n# 概述\n\n树形数据结构通常用来描述目录结构、菜单结构、组织机构树、级联选择器等等。\n\n树形结构是n维的数据结构，但是树形数据结构在关系型数据库中通常的存储形态为二维形式，数据库中使用单表存储，必有列id和列parentId，比如如下的表结构：\n\n```\nid    parentId   title\n1       0         地球\n2       1         亚洲\n3       1         欧洲\n4       2         中国\n5       2         韩国\n6       3         英国\n7       3         法国\n```\n\nps：如果是层数固定的树，还可以使用多表存储，处理起来会简单很多，本篇中主要讨论不定层数的树。\n\n那么返回到前端的数据，以json为例，一般是如下格式：\n\n```\n{\n    treeData: [\n\t{id: 1, parentId: 0, title: 地球},\n\t{id: 2, parentId: 1, title: 亚洲},\n\t{id: 3, parentId: 1, title: 欧洲},\n\t{id: 4, parentId: 2, title: 中国},\n\t{id: 5, parentId: 2, title: 韩国},\n\t{id: 6, parentId: 3, title: 英国},\n\t{id: 7, parentId: 3, title: 法国},\n    ]\n}\n\n```\n\n用一维数组来表示的n维树，如何处理这种数据结构呢？并且在处理过程中能保证较高的性能。\n\n# 思路\n\n如果我们要从根到叶子的顺序去生成一个菜单\n\n### 常规做法如下：\n\n1. 先寻找出所有的根节点（地球），判断条件为：数据节点的parentId没有对应的数据节点，在寻找的过程中需要第二个遍历，那么，假如有m条数据的话，需要遍历m*m次后，就可以找到所有的根节点了\n2. 先渲染根节点，然后从根节点开始，找到当前节点的儿子节点并渲染，然后用相同的算法递归儿子节点。寻找儿子节点的过程中需要遍历一次数据，判断条件为：数据节点的parentId等于当前节点的id。那么渲染完所有节点后，需要再次遍历m*m次。\n\n按常规做法，一共会遍历 m * m * 2 次，遍历次数和数据条目是指数递增关系，当数据量增大时，这种遍历相当于一个瞬时的死循环，cpu使用率会瞬时上升，给用户的感受就是页面卡住或者卡死了。\n\n### 考虑性能和代码复用的思路：\n\n1. 设计一个树节点对象，此对象拥有parent和children属性，parent是父节点，也是树节点数据，children是儿子树节点的数组。\n2. 那么问题就变成了，如何在遍历次数最少的情况下将所有数据节点都转换成树节点对象。\n\n```\n// 树节点对象\n    function TreeBean(data){\n        this.code ;\n        this.pcode;\n        this.data;\n        this.root=false;\n        this.parent;\n        this.children=[];\n\n        this._init=function (data){\n\n            this.data=data;\n            this.code=data.code;\n            this.pcode=data.pcode;\n\n        }\n\n        this._init(data);\n    }\n```\n\n# 未完待续\n\n下一篇主要对本章提出的思路做出实现。\n\n\n\n"
  },
  {
    "path": "其它/数据结构--树形数据的处理2.md",
    "content": "# 数据结构--树形数据的处理(2)\n\n# 前文提要\n\n假如有这样一组树形数据：\n\n```\n{\n    treeData: [\n        {id: 1, parentId: 0, title: 地球},\n        {id: 2, parentId: 1, title: 亚洲},\n        {id: 3, parentId: 1, title: 欧洲},\n        {id: 4, parentId: 2, title: 中国},\n        {id: 5, parentId: 2, title: 韩国},\n        {id: 6, parentId: 3, title: 英国},\n        {id: 7, parentId: 3, title: 法国},\n    ]\n}\n\n```\n\n如果我们要从根到叶子的顺序去生成一个菜单,同时要考虑到性能和代码复用，该如何去做呢。\n\n\n### 常规做法如下：\n\n1. 先寻找出所有的根节点（地球），判断条件为：数据节点的parentId没有对应的数据节点，\n在寻找的过程中需要第二个遍历，那么，假如有m条数据的话，需要遍历m*m次后，就可以找到>所有的根节点了\n2. 先渲染根节点，然后从根节点开始，找到当前节点的儿子节点并渲染，然后用相同的算法递\n归儿子节点。寻找儿子节点的过程中需要遍历一次数据，判断条件为：数据节点的parentId等>于当前节点的id。那么渲染完所有节点后，需要再次遍历m*m次。\n\n按常规做法，一共会遍历 m * m * 2 次，遍历次数和数据条目是指数递增关系，当数据量增大\n时，这种遍历相当于一个瞬时的死循环，cpu使用率会瞬时上升，给用户的感受就是页面卡住或\n者卡死了。\n\n\n# 分析问题\n\n目标是减少循环，那么在常规做法中，我们循环都用来做什么了呢，一是用来找root节点，二是用来找节点的所有孩子节点。\n\n1. 找root节点，判断条件为：root节点的parentId在数据中没有对应的节点。\n2. 找节点的所有孩子节点，判断条件为：父亲节点的id等于孩子节点的parentId\n\n针对以上的判断条件，抽象成算法问题，变为：\n\n1. 知道一个id，想找到数据中有这个id的节点，没有返回null\n2. 知道一个id，想找到数据中parentId=这个id的所有节点的数组，没有返回空数组\n\n这2个问题都有同样的规律，输入是一个id的值，输出是这个id对应的节点，或者parentId是这个id的节点数组，也就是知道一个key，找到一个value或者value的集合，那么不用遍历的方式来找，如何能实现呢。\n\n其实，这种key找value的算法，利用hash的数据结构是最容易、性能也较高的办法。\n\n什么是哈希表？\n\n哈希表（Hash table，也叫散列表），是根据关键码值(Key value)而直接进行访问的数据结构。也就是说，它通过把关键码值映射到表中一个位置来访问记录，以加快查找的速度。这个映射函数叫做散列函数，存放记录的数组叫做散列表。\n\n哈希表的做法其实很简单，就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字，然后就将该数字对数组长度进行取余，取余结果就当作数组的下标，将value存储在以该数字为下标的数组空间里。而当使用哈希表进行查询的时候，就是再次使用哈希函数将key转换为对应的数组下标，并定位到该空间获取value，如此一来，就可以充分利用到数组的定位性能进行数据定位。\n\n也就是说，我们只要有一个key是id，value是节点的，和一个key是id，value是parentId=id的节点数组的，这样的两个hash就可以了。\n\n在js中，{}其实就是一个hash结构，所以，我们的解决办法就是：\n\n1. 遍历一次数据，获得{key(id):value(node)}命名为idHash和{key(id):value(array(node))}命名为parentIdHash\n2. 遍历一次数据，找root节点，算法为：idHash.get(节点的parentId)===null的节点\n3. 递归渲染孩子节点的时候，找孩子节点的算法为：parentIdHash.get(节点的id)\n\n常规做法的m * m * 2 次的循环，变成了2次循环和m * 2 次的hash查找，性能得到了大幅的提升。\n\n# 附上代码\n\n```\n\n$treeData=(function(){\n\n\n    function TreeData(data){\n\n        this.getRoot=function(){\n            return this._rootList;\n        };\n        this.getTreeBeanByCode=function(code){\n            return this._codeMap[code];\n        } ;\n\n\n\n        this._srcData={data:[]};\n        this._codeMap={};\n        this._pcodeMap={};\n        this._rootList=[];\n\n\n        this._initTree=function (data){\n            this._srcData=data;\n            this._getCodeMap();\n            this._getPcodeMap();\n            this._initRoot();\n\n            this._rootList.forEach(function(tb){\n                this._initTb(tb);\n            }.bind(this));\n\n\n        }\n\n        this._initTb=function (tb){\n            tb.parent=this._codeMap[tb.pcode];\n            if(this._pcodeMap[tb.code]){\n                tb.children=this._pcodeMap[tb.code];\n            }\n\n            for(var i=0;i<tb.children.length;i++){\n                var child=tb.children[i];\n                this._initTb(child);\n            }\n        }\n\n\n        this._initRoot=function (){\n            for(var key in this._codeMap){\n                var tb=  this._codeMap[key];\n                var code=key;\n                if(!this._codeMap[tb.pcode]){\n                    tb.root=true;\n                    tb.parent=null;\n                    this._rootList.push(tb);\n                }\n            }\n        }\n\n        this._getPcodeMap=function (){\n            for(var key in this._codeMap){\n                var tb=  this._codeMap[key];\n                var pcode=tb.pcode;\n                if(!pcode){\n                    continue;  //是root节点,不用处理\n                }\n                if(!this._pcodeMap[pcode]){\n                    this._pcodeMap[pcode]=[];\n                }\n                this._pcodeMap[pcode].push(tb);\n            }\n        }\n\n        this._getCodeMap=function (){\n            this._srcData.data.forEach(function(d){\n                var tb=new TreeBean(d);\n                this. _codeMap[d.code]=tb;\n            }.bind(this));\n        }\n\n\n\n        this._initTree(data);\n        return this;\n    }\n\n    function TreeBean(data){\n        this.code ;\n        this.pcode;\n        this.data;\n        this.root=false;\n        this.parent;\n        this.children=[];\n\n        this._init=function (data){\n\n            this.data=data;\n            this.code=data.code;\n            this.pcode=data.pcode;\n\n        }\n\n        this._init(data);\n    }\n\n\n\n    return{init:TreeData };\n})();\n\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "其它/机器学习-入门篇（1）.md",
    "content": "# 机器学习-入门篇（1）\n\n# 我们的世界正在发生着巨大的改变\n\n我们的世界正在发生着巨大的改变，伴随着海量数据的积累和计算机计算能力的不断增强，人工智能领域发展迅速，正在逐渐的改变着我们的生活，原来科幻小说和机器猫里的桥段已经和正在变为现实。\n\n随着高考的来临，柯洁VS阿尔法狗的绝世之战被人们渐渐抛在了脑后，但是其实人工智能的话题可是一点也没有减少，就连高考，人工智能也前来参加了。\n\n在没有网络和题库的支持下，它完成了考卷，得分105分，对于许多人来说，这个分数并不是很高，而且还是一向简单的文科数学试卷。但是“高考机器人”仅仅是用了22分钟的时间，就完成了学生们所需要2小时完成的内容，平均下来，连奋笔疾书都不能形容他的速度了。\n\n前有“阿尔法狗”站到世界围棋巅峰，后有“高考机器人”迎战高考，号称2020年考上清华北大，让我们不禁期待，人工智能的时代是不是真的就要到来了。\n\n其实，人工智能早已融入了我们的生活：\n\n语音识别，苹果的siri，开各种峰会时候用的同声翻译，苹果新发布的智能音箱等。\n\n人脸识别，照相时自动人脸对焦，美剧中FBI用的人脸识别系统，美图秀秀自动美颜等。\n\n精准推送，今日头条、网易新闻的内容推送算法，百度、google广告的推送算法等。\n\n垃圾邮件拦截，邮箱中自动识别垃圾邮件的算法。\n\n无人驾驶汽车，用到了很多人工智能的技术，识别人，识别汽车，识别路等。\n\n阿尔法狗，在对战柯杰的比赛中，下出了很多人类想不到的棋路，完全颠覆了围棋界的认知。\n\n。。。。等等等等。\n\n机器学习是人工智能领域中的一个核心技术体系,研究的是怎样让机器能够通过经验数据自我学习，怎样让机器在没有明显规律的情况下获得人类的智慧，获得类似人类凭经验判断、决策的能力。\n\n# 基本概念\n\n顾名思义，机器学习的目的就是让机器具有类似于人类的学习、认识、理解事物的能力。\n\n一个典型的机器学习系统可以用下面的图来表示：\n\n![](media/14967424814685.jpg)\n\n其中，系统S是我们研究的对象，它在给定一个输入x的情况下，得到一定的输出y,LM是我们所求的学习机，其输出为y'。机器学习的目的是根据给定的训练样本（一堆已有的数据，包含x和y）求取系统输入输出之间的依赖关系的估计（图中的学习机），使它能够对未知的输出做出尽可能准确的预测。\n\n# 举个栗子\n\n我们以“房屋价格测算”作为例子，一起来看看：\n\n一套房子的价格，会受到很多因素的影响，例如地段、朝向、房龄、面积、银行利率等等，这些因素如果细分，可能会有几十个。一般在机器学习模型里，这些影响结果的因素我们称之为特征。我们先假设一种极端的场景，比如影响价格的特征只有一种，就是房子面积。然后我们收集一批相关的数据，例如，50平米50万、93平米95万等一系列样本数据，如果将这些样本数据放到二维坐标里看，则如下图：（绿点表示了所有的已知经验数据）\n\n![](media/14967445118064.jpg)\n\n然后，正如我们前面所说的，我们尝试用一个“函数”去表示这个输入（面积x）和输出（价格y），简而言之，我们就是要通过一条直线或者曲线将这些点“拟合”起来。\n\n假设情况也比较极端，这些点刚好可以用一条“直线”拟合（真实情况通常不会是直线），如下图：\n\n![](media/14967445446249.jpg)\n\n那么我们的函数是一个一次元方程f(x) = ax +b（这个就是机器学习训练出来的模型），当然，如果是曲线的话，我们得到的将是多次元方程。我们获得这个f(x) = ax +b的函数之后，接下来就可以做房价“预测”，例如，我们可以计算一个我们从未看见的面积案例81.5平方米，它究竟是多少钱？\n\n这个新的样本案例，可以通过直线找到对应的点（黄色的点），如图下：\n\n![](media/14967445652144.jpg)\n\n粗略的理解，上面就是AI的概括性的运作方式。这一切似乎显得过于简单了？当然不会，因为，我们前面提到，影响房价其实远不止一个特征，而是有几十个，这样问题就比较复杂了。\n\n\n本篇介绍了机器学习的基本概念，并用一个简单的例子描述了AI的基本运作模式，后面文章会介绍当特征增多的时候，如何处理。\n\n\nps: 听说人工智能已经能从UI设计稿生成UI代码了。。。。。。\n\n"
  },
  {
    "path": "其它/机器学习-入门篇（2）.md",
    "content": "# 机器学习-入门篇（2）\n\n在我们更深入之前要先了解一些机器学习的基本概念。\n\n机器学习算法分为两大类：监督学习和无监督学习。\n\n# 监督学习\n\n延续我们第一篇文章中估计房价的例子，假设你是一名房产中介，生意越做越大，因此你雇了一批实习生来帮你。但是问题来了，你可以看一眼房子就知道它到底值多少钱，实习生没有经验，不知道如何估价。\n\n为了帮助你的实习生，你决定写个小软件，可以根据房屋大小、地段以及类似房屋的成交价等因素来评估你所在地区房屋的价值。\n\n你把3个月来城里每笔房屋交易都写了下来，每一单你都记录了一长串的细节——卧室数量、房屋大小、地段等等。但最重要的是，你在每个房子的后面都写下了最终的成交价。\n\n这里，卧室数量、房屋大小、地段等称为特征（或者维度）。房价称为目标变量。每一单这些数据的集合称为一个样本。所有单数据的集合称为样本集。\n\n我们要利用这些已有的数据来编写一个程序来估算该地区其他房屋的价值。这些已知的数据集合成为训练数据或者训练样本集。\n\n这就称为监督学习。在已有数据中，你已经知道目标变量也就是每一栋房屋的售价，换句话说，你知道已有问题的答案，并可以反向找出解题的逻辑模型，然后根据逻辑模型来预测新数据的答案。\n\n为了编写软件，你将包含每一套房产的训练数据输入你的机器学习算法。算法尝试找出其中的逻辑模型。\n\n这就像是算术练习题，算式中的运算符号都被擦去了：\n\n2 4 5 = 3 ; 6 2 2 = 10 ; 4 2 2 = 6 ; 等等。\n\n看了这些题，你能明白这些测验里面是什么样的数学问题吗？你知道，你应该对算式左边的数字“做些什么”以得出算式右边的答案。\n\n在监督学习中，你是让计算机为你算出数字间的关系。而一旦你知道了解决这类特定问题所需要的数学方法后，你就可以解答同类的其它问题了。\n\n# 无监督学习\n\n让我们回到开头那个房地产中介的例子。要是你也不知道训练数据中的目标变量也就是每栋房子的售价怎么办？即使你所知道的只是房屋的大小、位置等信息，你也可以搞出很多的花样。这就是所谓的无监督学习（没有目标变量）。\n\n这就有点像有人给你一张纸，上面列出了很多数字，然后对你说:“我不知道这些数字有什么意义，也许你能从中找出规律或是能将它们分类，或是其它什么！”\n\n你该怎么处理这些数据呢？首先，你可以用个算法自动地从数据中划分出不同的细分市场。也许你会发现大学附近的买房者喜欢户型小但卧室多的房子，而郊区的买房者偏好三卧室的大户型。这些信息可以直接帮助你的营销。\n\n# 总结\n\n本篇文章，我们介绍了机器学习的几个基本概念：特征，目标变量，样本，样本集，训练数据，监督学习，无监督学习等。\n\n# 下期预告\n\n#### 房价预估栗子~续\n\n在下一篇文章中会继续第一篇文章中房价预估的例子，第一篇文章的例子里只考虑了一个特征--房屋面积，下一篇文章会介绍当考虑多个特征的时候，机器如何进行训练并计算出模型的。\n\nps：思考题：你知道这个例子是监督学习还是无监督学习吗？\n\n\n\n\n"
  },
  {
    "path": "其它/机器学习-入门篇（3）.md",
    "content": "# 机器学习-入门篇（3）\n\n\n让我们继续第一篇文章里房价评估的例子。\n\n#### 先按正常方式写个程序\n\n如果按照我们正常的逻辑可能会这么写我们的房价评估程序：\n\n```\nfunction 房价评估程序(卧室数量, 面积, 位置, 楼层){\n\tlet 价格 = 0；\n\tlet 每平米单价 = 40000; // 默认是40000\n\tif (位置 === '海淀') {\n\t\t每平米单价 = 60000;\n\t} else if (位置 === '朝阳') {\n\t\t每平米单价 = 50000;\n\t}\n\t价格 = 每平米单价 * 面积 ;\n\t\n\tif (楼层 不是顶楼 不是1楼) {\n\t\t价格 = 价格 + 楼层 * 1000; // 楼层差\n\t} else {\n\t\t价格 = 价格 - 1000; // 楼层差\n\t}\n\n\treturn 价格;\n}\n```\n\nok，看起来是实现出来了，但是这个程序基本是不可用的，因为里面的算法考虑到的情况实在是太有限了，也就是说找不到算法和模型来算出房价。\n\n那如果把找算法的过程让计算机来做，让计算机通过已有的数据来进行学习呢。\n\n我们换种思路来写这个程序：\n\n```\nfunction 房价评估程序(卧室数量, 面积, 位置, 楼层){\n\tlet 价格 = 0；\n\t价格 = 价格 + 卧室数量 * 12.12312.213;\n\t价格 = 价格 + 面积 * 324.234234;\n\t价格 = 价格 + 位置 * 654.424234234;\n\t价格 = 价格 + 楼层 * 9.24334;\n\treturn 价格;\n}\n```\n\n请注意程序里的那些神奇的数字。它们称为权重。如果我们能找出对每栋房子都适用的完美权重，我们的函数就能预测所有的房价！\n\n真的存在吗？我们来试试。\n\n#### 步骤1：将每个权重设置为1.\n\n```\nfunction 房价评估程序(卧室数量, 面积, 位置, 楼层){\n\tlet 价格 = 0；\n\t价格 = 价格 + 卧室数量 * 1;\n\t价格 = 价格 + 面积 * 1;\n\t价格 = 价格 + 位置 * 1;\n\t价格 = 价格 + 楼层 * 1;\n\treturn 价格;\n}\n```\n\n#### 步骤2：将已有数据输入到函数中，检验估算值与正确价格的偏离程度：\n\n比如：\n\n有一套房产实际成交价为225万元，你的函数估价为217.8万，这一套房产你就差了7.2万。\n\n再将历史数据集中的每套房产估价后偏离值平方后求和。\n\n假设数据集中有500套房产交易，估价偏离值平方求和总计为86,123,373元。这就反映了你的函数现在的“正确”程度。\n\n现在，将总计值除以500，得到每套房产的估价偏离平均值。将这个平均误差值称为你函数的代价。\n\n如果你能调整权重使得这个代价变为0，你的函数就完美了。\n\n它意味着，根据输入的数据，你的程序对每一笔房产交易的估价都是分毫不差。\n\n而这就是我们的目标——尝试不同的权重值以使代价尽可能的低。\n\n#### 步骤3：不断重复步骤2\n\n尝试所有可能的权重值组合。哪一个组合使得代价最接近于0，它就是你要使用的，你只要找到了这样的组合，问题就得到了解决!\n\n#### 很神奇，是不是\n\n试想一下，如果你的程序里没有类似“面积”和“卧室数”这样的参数，而是接受了一组数字。\n\n假设每个数字代表了你车顶安装的摄像头捕捉的画面中的一个像素，再将预测的输出不称为“价格”而是叫做“方向盘转动度数”，这样你就得到了一个程序可以自动操纵你的汽车了！\n\n#### 没那么简单\n\n好吧，当然你不可能尝试所有可能的权重值来找到效果最好的组合。那可真要花几个世纪的时间，因为要尝试的数字真的是无穷无尽。\n\n为避免这种情况，数学家们找到了很多聪明的办法来快速找到优秀的权重值，而不需要尝试过多。\n\n下一篇我们再继续介绍。\n\n\n"
  },
  {
    "path": "其它/机器学习-入门篇（4）.md",
    "content": "# 机器学习-入门篇（4）\n\n书接上回，如何能快速的找到一组优秀的权重值呢。\n\n首先，写出一个简单的等式表示前述步骤2：\n\n这是你的代价函数：\n\n![](media/14981234076616.jpg)\n\n接着，让我们将这同一个等式用机器学习的数学术语，进行重写：\n \n![](media/14981234294491.jpg)\n\nθ表示当前的权重值。J(θ) 意为“当前权重值对应的代价”。\n\n这个等式表示我们的估价程序在当前权重值下偏离程度的大小。\n\n如果将所有赋给卧室数和面积的可能权重值以图形形式显示，我们会得到类似下图的图表：\n\n![](media/14981234423036.jpg)\n\n代价函数的图形像一支碗。纵轴表示代价。\n\n图中蓝色的最低点就是代价最低的地方——即我们的程序偏离最小。最高点意味着偏离最大。所以，如果我们能找到一组权重值带领我们到达图中的最低点，我们就找到了答案！\n\n因此，我们只需要调整权重值使我们在图上能向着最低点“走下坡路”。如果对于权重的细小调节能一直使我们保持向最低点移动，那么最终我们不用尝试太多权重值就能到达那里。\n\n如果你还记得一点微积分的话，你也许记得如果你对一个函数求导，结果会告诉你函数在任一点的斜率。换句话说，对于图上给定一点，它告诉我们那条路是下坡路。我们可以利用这一点朝底部进发。\n\n所以，如果我们对代价函数关于每一个权重求偏导，那么我们就可以从每一个权重中减去该值。这样可以让我们更加接近山底。一直这样做，最终我们将到达底部，得到权重的最优值。\n\n这种找出最佳权重的办法被称为批量梯度下降，上面是对它的高度概括。\n\n上面我描述的三步算法被称为多元线性回归。你估算等式是在求一条能够拟合所有房价数据点的直线。然后，你再根据房价在你的直线上可能出现的位置用这个等式来估算从未见过的房屋的价格。\n\n但是，我为你展示的这种方法可能在简单的情况下有效，它不会在所有情况下都有用。原因之一是因为房价不会一直那么简单地跟随一条连续直线。\n\n但是，幸运的是，有很多办法来处理这种情况。对于非线性数据，很多其他类型的机器学习算法可以处理（如神经网络或有核向量机）。还有很多方法运用线性回归更灵活，想到了用更复杂的线条来拟合。在所有的情况中，寻找最优权重值这一基本思路依然适用。\n\n还有，我忽略了过拟合的概念。很容易碰上这样一组权重值，它们对于你原始数据集中的房价都能完美预测，但对于原始数据集之外的任何新房屋都预测不准。这种情况的解决之道也有不少（如正则化以及使用交叉验证数据集）。学会如何处理这一问题对于顺利应用机器学习至关重要。\n\n换言之，基本概念非常简单，要想运用机器学习得到有用的结果还需要一些技巧和经验。\n\n一旦你开始明白机器学习技术很容易应用于解决貌似很困难的问题（如手写识别），你心中会有一种感觉，只要有足够的数据，你就能够用机器学习解决任何问题。只需要将数据输入进去，就能看到计算机变戏法一样找出拟合数据的等式。\n\n但是很重要的一点你要记住，机器学习只能对用你占有的数据实际可解的问题才适用。\n\n例如，如果你建立了一个模型来根据每套房屋内盆栽数量来预测房价，它就永远不会成功。房屋内盆栽数量和房价之间没有任何的关系。所以，无论它怎么去尝试，计算机也推导不出两者之间的关系。\n\n只能对实际存在的关系建模。\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "其它/架构/通用组件规范.md",
    "content": "\n# 目标\n\n本文档主要描述出，封装通用组件的全过程和一些建议和原则。\n\n# 定义\n\n1. 通用组件：不依赖任何第三方库的组件。本文档中的组件，都指的是通用组件。\n2. 通用view组件：不依赖任何第三方库的且含有视图（html和css）的组件。本文档中的view组件，都指的是通用view组件。\n3. 通用功能组件：不依赖任何第三方库的不含有视图的组件。本文档中的功能组件，都指的是通用功能组件。\n4. 容器标签：view组件要显示的位置，view组件会初始化到此标签中\n5. 组件实例对象:每一个view组件在调用过初始化方法后，会实例化出一个组件实例对象，此对象，会被绑定到容器标签上的组件名称属性上。比如，视频组件会被绑定到BFFVideo属性上。\n6. 通用组件关键字：BFF（Babytree Front-end Framework）\n\n\n# 业务使用方式\n\n首先我们从期望的业务中的使用方式，来对组件有个感性的认识。\n\n## 组件引入\n\n组件需要支持三种引入方式，以支持不同技术体系的项目。\n\n#### npm安装\n\n示例：\n```\nnpm i git+ssh://git@192.168.24.32:front-end/native.git\n```\n\n注意：目前npm的安装方式，只能安装最新版，不能按版本安装（支持版本需要将组件发布到npm仓库中）\n\n#### url直接引入\n\n示例：\n```\n<script src=\"//static02.babytreeimg.com/img/bca/native/0.1.4/native.min.js\"></script>\n```\n\n#### php方法引入\n\n示例：\n```\nStaticFileControl::addXXX('/img/bca/native/0.1.4/native.min.js');\n```\n\n## 功能组件使用\n\n功能组件就是一组js接口，所以功能组件的资源一般就只有js。\n\n首先，将功能组件js引入到页面。\n\n然后，根据约定的变量或者import到页面的变量，按照文档直接使用即可。\n\n例如：与原生通信组件bbtNative\n\n页面引入:\n```\n<script src=\"//static02.babytreeimg.com/img/bca/native/0.1.4/native.min.js\"></script>\n```\n\n调用登录接口：\n```\n<script>\nbbtNative.login();\n</script>\n```\n\n## view组件使用\n\nview组件会提供视图，比功能组件的使用会稍微复杂一些，可能会涉及到css等资源。\n\n首先，将view组件js引入到页面中，如果使用url方式引入，可能还需要引入css，具体需要查看组件使用文档。\n\n然后，在html中，定义view组件的显示位置，也就是组件的容器标签位置，比如一个特殊标记的div。\n\n最后，根据约定的变量或者import到页面的变量，执行view组件初始化方法，传入那个刚定义的特殊标记的div，会实例化出来的组件实例对象，并将其写到容器标签上\n\n在想使用组件接口的时候，可以从组件容器div上获得对应的组件实例对象，可以调用组件实例对象对应的接口。\n\n例如：视频组件BFFVideo。\n\n页面引入:\n```\n<script src=\"//static02.babytreeimg.com/img/bca/native/0.1.4/BFFvideo.min.js\"></script>\n```\n```\n<link href=\"//static02.babytreeimg.com/img/bca/native/0.1.4/BFFvideo.min.css\"></link>\n```\n\nhtml中 标记video组件显示的位置:\n```\n<div id=\"i-am-video\" ></div>\n```\n\njs中 调用video组件初始化接口：\n```\n<script>\nBFFVideo.init({ container: '#i-am-video', url: 'http://xxx/a.video' });\n</script>\n```\n\njs中 调用组件实例对象的play接口：\n```\n<script>\ndocument.getElementById(#i-am-video).BFFVideo.play();\n</script>\n```\n\n# 组件命名规范\n\n命名一个组件，使用首字母大写的驼峰命名方式。\n\n请使用英文，不要使用拼音。\n\n尽量保持简洁。\n\n使用BFF开头。\n\n例如：视频组件，命名为： BFFVideo\n\n\n# 代码库\n\n使用gitlab管理，每个组件是一个project。\n\ngitlab地址：http://192.168.24.32/groups/front-end\n\nmaster分支为最新代码分支。\n\n开发需要自行建立开发分支。\n\n发布后将分支合并到master，同时使用版本号打tag，打tag的时候要编写详细的升级和改动说明。\n\n# 发布流程\n\n#### npm发布\n\n将代码合并到master后，再npm安装就可以安装到最新版。\n\n注意：npm的安装方式只能安装到最新版，不能安装到某一版本。（支持版本需要将组件发布到npm仓库中）\n\n#### url发布\n\n将build出来的成果物，js，css，图片等，提交到bbt-common-asset库中。\n\n此种方式，支持版本号，业务代码如果想升级或者降级到某一版本，修改引入的url路径上的版本号即可。\n\n# 版本号规则\n\n使用三位版本号，a.b.c。例如0.1.3。\n\n初始版本建议：1.0.0\n\n如有大的功能升级或调整，需要a+1，b置为0，c置为0。\n\n正常的功能升级b+1，c置为0。\n\n由于bug的修改产生的升级c+1。\n\n# 命名空间约束\n\n命名空间约束主要约束的是可能发生的在同一命名空间下，命名可能会相互污染的问题。\n\n可能产生的命名污染会有以下几种情况：\n\n1. 通过url引入的组件，需要在window空间中设置一个js全局变量，这个变量名当引入多个组件的时候可能会互相污染。\n2. view组件初始化产生的组件实例对象，在容器标签上的属性名称，可能会与容器标签上其它的属性名称互相污染\n3. view组件的css中使用的class名称，可能会与当前页面使用的其它css文件中的class命名相互污染。\n\n所以，做出如下约束：\n\n#### 组件的全局变量名称\n\n直接使用组件名称。\n\n#### 组件实例对象的属性名称\n\n直接使用组件名称。\n\n#### css的class名称\n\n原则上只允许污染一次命名空间，使用【.组件名称】作为最上层class的名称。\n\n其余class都作为子选择器。\n\n# 功能组件定义\n\n功能组件一般会有两种情况。\n\n## 无状态功能组件\n\n无需保存状态，都是纯方法的组件，功能类似于util。\n\n#### 对外暴露规范：\n\n建议对外暴露如下结构对象：\n\n```\n{\n\tfunc1: () => {},\n\tfunc2: () => {},\n}\n```\n\n此对象中所有方法，相当于public方法，可以被业务代码使用。\n\npublic方法命名要求使用首字母小写的驼峰命名，使用英文而不要使用拼音。\n\n非public的方法，不要写到这个对象中。\n\n不要直接暴露属性，如果需要访问属性请暴露相应get和set方法来操作。\n\n假设业务代码在使用BFFXxxUtil组件的时候,直接使用其方法即可，如下：\n\n```\nBFFXxxUtil.func1();\n```\n\n#### 接口入参规范\n\n原则上要求接口入参只能有0~1个参数。\n\n如果有1个参数的时候，要求参数类型必须是对象结构{}。例如{url: 'http://a.b.c', title: '标题名称'}\n\n这样可以保持最佳的扩展性和语义性。\n\n#### 事件规范\n\n自定义事件的命名要以on开头并遵循驼峰命名方式，请使用英文，不要使用拼音。\n\n自定义事件的入参，要符合接口入参规范。\n\n\n## 有状态功能组件\n\n#### 对外暴露规范：\n\n这种情况要使用面向对象的方式设计和编程。\n\n对外暴露的是es6的class。\n\n假设业务在使用BFFVersion的时候，要先实例化对象，然后通过实例化的对象进行后续操作。\n\npublic方法命名要求使用首字母小写的驼峰命名，使用英文而不要使用拼音。\n\n非public方法请用_下划线开头定义。\n\n```\nconst ver789 = new BFFVersion({v: '7.8.9'});\nconsole.log(ver789.equals('7.8.10'));\n```\n\n#### 构造器规范\n\n构造器参数要求符合接口入参规范。\n\n必填参数要求必须使用构造器传入。\n\n非必填参数可以使用构造器传入，也可以后边通过接口传入，根据情况灵活设计即可。\n\n\n#### 接口入参规范\n\n同无状态组件的接口入参规范。\n\n#### 事件规范\n\n同无状态组件的事件规范。\n\n# view组件定义\n\n### view组件定义-js\n\n### view组件定义-css\n\n### view组件定义-html\n\n### view组件生命周期\n\n# 文档要求\n\n# 示例代码要求\n\n# 测试要求\n\n\n\n\n\n\n"
  },
  {
    "path": "其它/模式/代理模式.md",
    "content": "# 代理模式\n\n代理模式就是多一个代理类出来，替原对象进行一些操作，比如我们在租房子的时候回去找中介，为什么呢？因为你对该地区房屋的信息掌握的不够全面，希望找一个更熟悉的人去帮你做，此处的代理就是这个意思。再如我们有的时候打官司，我们需要请律师，因为律师在法律方面有专长，可以替我们进行操作，表达我们的想法。先来看看关系图：\n\n\n![](media/15284521546687.jpg)\n\n\n根据上文的阐述，代理模式就比较容易的理解了，我们看下代码：\n\n\n```\n\t1.\tpublic interface Sourceable {  \n\t2.\t    public void method();  \n\t3.\t}  \n\n\t1.\tpublic class Source implements Sourceable {  \n\t2.\t  \n\t3.\t    @Override  \n\t4.\t    public void method() {  \n\t5.\t        System.out.println(\"the original method!\");  \n\t6.\t    }  \n\t7.\t}  \n\n\t1.\tpublic class Proxy implements Sourceable {  \n\t2.\t  \n\t3.\t    private Source source;  \n\t4.\t    public Proxy(){  \n\t5.\t        super();  \n\t6.\t        this.source = new Source();  \n\t7.\t    }  \n\t8.\t    @Override  \n\t9.\t    public void method() {  \n\t10.\t        before();  \n\t11.\t        source.method();  \n\t12.\t        atfer();  \n\t13.\t    }  \n\t14.\t    private void atfer() {  \n\t15.\t        System.out.println(\"after proxy!\");  \n\t16.\t    }  \n\t17.\t    private void before() {  \n\t18.\t        System.out.println(\"before proxy!\");  \n\t19.\t    }  \n\t20.\t}  \n\n\t\n测试类：\n\n\t1.\tpublic class ProxyTest {  \n\t2.\t  \n\t3.\t    public static void main(String[] args) {  \n\t4.\t        Sourceable source = new Proxy();  \n\t5.\t        source.method();  \n\t6.\t    }  \n\t7.\t  \n\t8.\t}  \n\n\n输出：\nbefore proxy! the original method! after proxy!\n\n\n```\n\n代理模式的应用场景：\n\n如果已有的方法在使用的时候需要对原有的方法进行改进，此时有两种办法：\n\n1. 修改原有的方法来适应。这样违反了“对扩展开放，对修改关闭”的原则。\n2. 就是采用一个代理类调用原有的方法，且对产生的结果进行控制。这种方法就是代理模式。\n\n使用代理模式，可以将功能划分的更加清晰，有助于后期维护！\n\n\n"
  },
  {
    "path": "其它/模式/单例模式（1）.md",
    "content": "# 单例模式（1）\n\n单例对象（Singleton）是一种常用的设计模式。在Java应用中，单例对象能保证在一个JVM中，该对象只有一个实例存在。这样的模式有几个好处：\n\n1. 某些类创建比较频繁，对于一些大型的对象，这是一笔很大的系统开销。\n1. 省去了new操作符，降低了系统内存的使用频率，减轻GC压力。\n1. 有些类如交易所的核心交易引擎，控制着交易流程，如果该类可以创建多个的话，系统完全乱了。（比如一个军队出现了多个司令员同时指挥，肯定会乱成一团），所以只有使用单例模式，才能保证核心交易服务器独立控制整个流程。\n\n首先我们写一个简单的单例类：\n\n```\n\t1.\tpublic class Singleton {  \n\t2.\t  \n\t3.\t    /* 持有私有静态实例，防止被引用，此处赋值为null，目的是实现延迟加载 */  \n\t4.\t    private static Singleton instance = null;  \n\t5.\t  \n\t6.\t    /* 私有构造方法，防止被实例化 */  \n\t7.\t    private Singleton() {  \n\t8.\t    }  \n\t9.\t  \n\t10.\t    /* 静态工程方法，创建实例 */  \n\t11.\t    public static Singleton getInstance() {  \n\t12.\t        if (instance == null) {  \n\t13.\t            instance = new Singleton();  \n\t14.\t        }  \n\t15.\t        return instance;  \n\t16.\t    }  \n\t17.\t  \n\t18.\t    /* 如果该对象被用于序列化，可以保证对象在序列化前后保持一致 */  \n\t19.\t    public Object readResolve() {  \n\t20.\t        return instance;  \n\t21.\t    }  \n\t22.\t}  \n\n\n```\n\n这个类可以满足基本要求，但是，像这样毫无线程安全保护的类，如果我们把它放入多线程的环境下，肯定就会出现问题了，如何解决？我们首先会想到对getInstance方法加synchronized关键字，如下：\n\n\n```\n\t1.\tpublic static synchronized Singleton getInstance() {  \n\t2.\t        if (instance == null) {  \n\t3.\t            instance = new Singleton();  \n\t4.\t        }  \n\t5.\t        return instance;  \n\t6.\t    }  \n\n```\n\n但是，synchronized关键字锁住的是这个对象，这样的用法，在性能上会有所下降，因为每次调用getInstance()，都要对对象上锁，事实上，只有在第一次创建对象的时候需要加锁，之后就不需要了，所以，这个地方需要改进。我们改成下面这个：\n\n```\n\t1.\tpublic static Singleton getInstance() {  \n\t2.\t        if (instance == null) {  \n\t3.\t            synchronized (instance) {  \n\t4.\t                if (instance == null) {  \n\t5.\t                    instance = new Singleton();  \n\t6.\t                }  \n\t7.\t            }  \n\t8.\t        }  \n\t9.\t        return instance;  \n\t10.\t    }  \n\n```\n\n待续。\n\n\n"
  },
  {
    "path": "其它/模式/单例模式（2）.md",
    "content": "# 单例模式（2）\n\n上一篇文章中最后的写法似乎解决了之前提到的问题，将synchronized关键字加在了内部，也就是说当调用的时候是不需要加锁的，只有在instance为null，并创建对象的时候才需要加锁，性能有一定的提升。但是，这样的情况，还是有可能有问题的，看下面的情况：在Java指令中创建对象和赋值操作是分开进行的，也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序，也就是说有可能JVM会为新的Singleton实例分配空间，然后直接赋值给instance成员，然后再去初始化这个Singleton实例。这样就可能出错了，我们以A、B两个线程为例：\n\n1. A、B线程同时进入了第一个if判断\n1. A首先进入synchronized块，由于instance为null，所以它执行instance = new Singleton();\n1. 由于JVM内部的优化机制，JVM先画出了一些分配给Singleton实例的空白内存，并赋值给instance成员（注意此时JVM没有开始初始化这个实例），然后A离开了synchronized块。\n1. B进入synchronized块，由于instance此时不是null，因此它马上离开了synchronized块并将结果返回给调用该方法的程序。\n1. 此时B线程打算使用Singleton实例，却发现它没有被初始化，于是错误发生了。\n\n\n所以程序还是有可能发生错误，其实程序在运行过程是很复杂的，从这点我们就可以看出，尤其是在写多线程环境下的程序更有难度，有挑战性。我们对该程序做进一步优化：\n\n```\n\t1.\tprivate static class SingletonFactory{           \n\t2.\t        private static Singleton instance = new Singleton();           \n\t3.\t    }           \n\t4.\t    public static Singleton getInstance(){           \n\t5.\t        return SingletonFactory.instance;           \n\t6.\t    }   \n\n```\n\n\n实际情况是，单例模式使用内部类来维护单例的实现，JVM内部的机制能够保证当一个类被加载的时候，这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候，JVM能够帮我们保证instance只被创建一次，并且会保证把赋值给instance的内存初始化完毕，这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制，这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式：\n\n```\n\t1.\tpublic class Singleton {  \n\t2.\t  \n\t3.\t    /* 私有构造方法，防止被实例化 */  \n\t4.\t    private Singleton() {  \n\t5.\t    }  \n\t6.\t  \n\t7.\t    /* 此处使用一个内部类来维护单例 */  \n\t8.\t    private static class SingletonFactory {  \n\t9.\t        private static Singleton instance = new Singleton();  \n\t10.\t    }  \n\t11.\t  \n\t12.\t    /* 获取实例 */  \n\t13.\t    public static Singleton getInstance() {  \n\t14.\t        return SingletonFactory.instance;  \n\t15.\t    }  \n\t16.\t  \n\t17.\t    /* 如果该对象被用于序列化，可以保证对象在序列化前后保持一致 */  \n\t18.\t    public Object readResolve() {  \n\t19.\t        return getInstance();  \n\t20.\t    }  \n\t21.\t}  \n\n```\n\n其实说它完美，也不一定，如果在构造函数中抛出异常，实例将永远得不到创建，也会出错。所以说，十分完美的东西是没有的，我们只能根据实际情况，选择最适合自己应用场景的实现方法。也有人这样实现：因为我们只需要在创建类的时候进行同步，所以只要将创建和getInstance()分开，单独为创建加synchronized关键字，也是可以的：\n\n```\n\t1.\tpublic class SingletonTest {  \n\t2.\t  \n\t3.\t    private static SingletonTest instance = null;  \n\t4.\t  \n\t5.\t    private SingletonTest() {  \n\t6.\t    }  \n\t7.\t  \n\t8.\t    private static synchronized void syncInit() {  \n\t9.\t        if (instance == null) {  \n\t10.\t            instance = new SingletonTest();  \n\t11.\t        }  \n\t12.\t    }  \n\t13.\t  \n\t14.\t    public static SingletonTest getInstance() {  \n\t15.\t        if (instance == null) {  \n\t16.\t            syncInit();  \n\t17.\t        }  \n\t18.\t        return instance;  \n\t19.\t    }  \n\t20.\t}  \n\n```\n\n通过单例模式的学习告诉我们：\n\n1. 单例模式理解起来简单，但是具体实现起来还是有一定的难度。\n2. synchronized关键字锁定的是对象，在用的时候，一定要在恰当的地方使用（注意需要使用锁的对象和过程，可能有的时候并不是整个对象及整个过程都需要锁）。\n\n\n"
  },
  {
    "path": "其它/模式/原型模式.md",
    "content": "# 原型模式\n\n原型模式虽然是创建型的模式，但是与工程模式没有关系，从名字即可看出，该模式的思想就是将一个对象作为原型，对其进行复制、克隆，产生一个和原对象类似的新对象。在Java中，复制对象是通过clone()实现的，先创建一个原型类：\n\n```\n\t1.\tpublic class Prototype implements Cloneable {  \n\t2.\t  \n\t3.\t    public Object clone() throws CloneNotSupportedException {  \n\t4.\t        Prototype proto = (Prototype) super.clone();  \n\t5.\t        return proto;  \n\t6.\t    }  \n\t7.\t}  \n\n```\n\n很简单，一个原型类，只需要实现Cloneable接口，覆写clone方法，此处clone方法可以改成任意的名称，因为Cloneable接口是个空接口，你可以任意定义实现类的方法名，如cloneA或者cloneB，因为此处的重点是super.clone()这句话，super.clone()调用的是Object的clone()方法，而在Object类中，clone()是native的。在这儿，结合对象的浅复制和深复制来说一下，首先需要了解对象深、浅复制的概念：\n\n浅复制：将一个对象复制后，基本数据类型的变量都会重新创建，而引用类型，指向的还是原对象所指向的。\n\n深复制：将一个对象复制后，不论是基本数据类型还有引用类型，都是重新创建的。简单来说，就是深复制进行了完全彻底的复制，而浅复制不彻底。\n\n此处，写一个深浅复制的例子：\n\n```\n\t1.\tpublic class Prototype implements Cloneable, Serializable {  \n\t2.\t  \n\t3.\t    private static final long serialVersionUID = 1L;  \n\t4.\t    private String string;  \n\t5.\t  \n\t6.\t    private SerializableObject obj;  \n\t7.\t  \n\t8.\t    /* 浅复制 */  \n\t9.\t    public Object clone() throws CloneNotSupportedException {  \n\t10.\t        Prototype proto = (Prototype) super.clone();  \n\t11.\t        return proto;  \n\t12.\t    }  \n\t13.\t  \n\t14.\t    /* 深复制 */  \n\t15.\t    public Object deepClone() throws IOException, ClassNotFoundException {  \n\t16.\t  \n\t17.\t        /* 写入当前对象的二进制流 */  \n\t18.\t        ByteArrayOutputStream bos = new ByteArrayOutputStream();  \n\t19.\t        ObjectOutputStream oos = new ObjectOutputStream(bos);  \n\t20.\t        oos.writeObject(this);  \n\t21.\t  \n\t22.\t        /* 读出二进制流产生的新对象 */  \n\t23.\t        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());  \n\t24.\t        ObjectInputStream ois = new ObjectInputStream(bis);  \n\t25.\t        return ois.readObject();  \n\t26.\t    }  \n\t27.\t  \n\t28.\t    public String getString() {  \n\t29.\t        return string;  \n\t30.\t    }  \n\t31.\t  \n\t32.\t    public void setString(String string) {  \n\t33.\t        this.string = string;  \n\t34.\t    }  \n\t35.\t  \n\t36.\t    public SerializableObject getObj() {  \n\t37.\t        return obj;  \n\t38.\t    }  \n\t39.\t  \n\t40.\t    public void setObj(SerializableObject obj) {  \n\t41.\t        this.obj = obj;  \n\t42.\t    }  \n\t43.\t  \n\t44.\t}  \n\t45.\t  \n\t46.\tclass SerializableObject implements Serializable {  \n\t47.\t    private static final long serialVersionUID = 1L;  \n\t48.\t}  \n\n```\n\n"
  },
  {
    "path": "其它/模式/多个工厂方法模式.md",
    "content": "# 多个工厂方法模式\n\n多个工厂方法模式，是对普通工厂方法模式的改进，在普通工厂方法模式中，如果传递的字符串出错，则不能正确创建对象，而多个工厂方法模式是提供多个工厂方法，分别创建对象。关系图：\n\n![](media/15223782818687.jpg)\n\n\n\n将上面的代码做下修改，改动下SendFactory类就行，如下：\n\n```\nclass SendFactory {  \n   public Sender produceMail(){  \n         return new MailSender();  \n \t    }  \n       \n \t    public Sender produceSms(){  \n \t        return new SmsSender();  \n \t    }  \n}  \n\n```\n\n测试类如下：\n\n```\n\t1.\tpublic class FactoryTest {  \n\t2.\t  \n\t3.\t    public static void main(String[] args) {  \n\t4.\t        SendFactory factory = new SendFactory();  \n\t5.\t        Sender sender = factory.produceMail();  \n\t6.\t        sender.Send();  \n\t7.\t    }  \n\t8.\t}  \n\n```\n\n输出：this is mailsender!\n\n\n"
  },
  {
    "path": "其它/模式/建造者模式.md",
    "content": "# 建造者模式\n\n\n工厂类模式提供的是创建单个类的模式，而建造者模式则是将各种产品集中起来进行管理，用来创建复合对象，所谓复合对象就是指某个类具有不同的属性，其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。我们看一下代码：\n\n还和前面一样，一个Sender接口，两个实现类MailSender和SmsSender。最后，建造者类如下：\n\n```\n\t1.\tpublic class Builder {  \n\t2.\t      \n\t3.\t    private List<Sender> list = new ArrayList<Sender>();  \n\t4.\t      \n\t5.\t    public void produceMailSender(int count){  \n\t6.\t        for(int i=0; i<count; i++){  \n\t7.\t            list.add(new MailSender());  \n\t8.\t        }  \n\t9.\t    }  \n\t10.\t      \n\t11.\t    public void produceSmsSender(int count){  \n\t12.\t        for(int i=0; i<count; i++){  \n\t13.\t            list.add(new SmsSender());  \n\t14.\t        }  \n\t15.\t    }  \n\t16.\t}  \n\n```\n\n```\n\t1.\tpublic class Test {  \n\t2.\t  \n\t3.\t    public static void main(String[] args) {  \n\t4.\t        Builder builder = new Builder();  \n\t5.\t        builder.produceMailSender(10);  \n\t6.\t    }  \n\t7.\t}  \n\n```\n\n从这点看出，建造者模式将很多功能集成到一个类里，这个类可以创造出比较复杂的东西。所以与工厂模式的区别就是：工厂模式关注的是创建单个产品，而建造者模式则关注创建符合对象，多个部分。因此，是选择工厂模式还是建造者模式，依实际情况而定。\n\n\n"
  },
  {
    "path": "其它/模式/抽象工厂模式.md",
    "content": "# 抽象工厂模式\n\n工厂方法模式有一个问题就是，类的创建依赖工厂类，也就是说，如果想要拓展程序，必须对工厂类进行修改，这违背了闭包原则，所以，从设计角度考虑，有一定的问题，如何解决？就用到抽象工厂模式，创建多个工厂类，这样一旦需要增加新的功能，直接增加新的工厂类就可以了，不需要修改之前的代码。因为抽象工厂不太好理解，我们先看看图，然后就和代码，就比较容易理解。\n\n![](media/15242068526643.jpg)\n\n请看例子：\n\n```\n\n\t1.\tpublic interface Sender {  \n\t2.\t    public void Send();  \n\t3.\t}  \n两个实现类：\n \n\t1.\tpublic class MailSender implements Sender {  \n\t2.\t    @Override  \n\t3.\t    public void Send() {  \n\t4.\t        System.out.println(\"this is mailsender!\");  \n\t5.\t    }  \n\t6.\t}  \n \n\t1.\tpublic class SmsSender implements Sender {  \n\t2.\t  \n\t3.\t    @Override  \n\t4.\t    public void Send() {  \n\t5.\t        System.out.println(\"this is sms sender!\");  \n\t6.\t    }  \n\t7.\t} \n\n\t \n两个工厂类：\n \n\t1.\tpublic class SendMailFactory implements Provider {  \n\t2.\t      \n\t3.\t    @Override  \n\t4.\t    public Sender produce(){  \n\t5.\t        return new MailSender();  \n\t6.\t    }  \n\t7.\t}  \n \n\t1.\tpublic class SendSmsFactory implements Provider{  \n\t2.\t  \n\t3.\t    @Override  \n\t4.\t    public Sender produce() {  \n\t5.\t        return new SmsSender();  \n\t6.\t    }  \n\t7.\t}  \n\n\t\n在提供一个接口：\n \n\t1.\tpublic interface Provider {  \n\t2.\t    public Sender produce();  \n\t3.\t}  \n\n\t\n测试类：\n \n\t1.\tpublic class Test {  \n\t2.\t  \n\t3.\t    public static void main(String[] args) {  \n\t4.\t        Provider provider = new SendMailFactory();  \n\t5.\t        Sender sender = provider.produce();  \n\t6.\t        sender.Send();  \n\t7.\t    }  \n\t8.\t}  \n\t\n```\n\n\n其实这个模式的好处就是，如果你现在想增加一个功能：发及时信息，则只需做一个实现类，实现Sender接口，同时做一个工厂类，实现Provider接口，就OK了，无需去改动现成的代码。\n\n"
  },
  {
    "path": "其它/模式/普通工厂模式.md",
    "content": "# 普通工厂模式\n\n普通工厂模式是最常用的设计模式之一。这种类型的设计模式属于创建型模式，它提供了一种创建对象的最佳方式。\n\n在工厂模式中，我们在创建对象时不会对客户端暴露创建逻辑，并且是通过使用一个共同的接口来指向新创建的对象。\n\n意图：定义一个创建对象的接口，让其子类自己决定实例化哪一个工厂类，工厂模式使其创建过程延迟到子类进行。\n\n主要解决：主要解决接口选择的问题。\n\n何时使用：我们明确地计划不同条件下创建不同实例时。\n\n如何解决：让其子类实现工厂接口，返回的也是一个抽象的产品。\n\n关键代码：创建过程在其子类执行。\n\n应用实例： 1、您需要一辆汽车，可以直接从工厂里面提货，而不用去管这辆汽车是怎么做出来的，以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。\n\n优点： 1、一个调用者想创建一个对象，只要知道其名称就可以了。 2、扩展性高，如果想增加一个产品，只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现，调用者只关心产品的接口。\n\n缺点：每次增加一个产品时，都需要增加一个具体类和对象实现工厂，使得系统中类的个数成倍增加，在一定程度上增加了系统的复杂度，同时也增加了系统具体类的依赖。这并不是什么好事。\n\n使用场景： 1、日志记录器：记录可能记录到本地硬盘、系统事件、远程服务器等，用户可以选择记录日志到什么地方。 2、数据库访问，当用户不知道最后系统采用哪一类数据库，以及数据库可能有变化时。 3、设计一个连接服务器的框架，需要三个协议，\"POP3\"、\"IMAP\"、\"HTTP\"，可以把这三个作为产品类，共同实现一个接口。\n\n注意事项：作为一种创建类模式，在任何需要生成复杂对象的地方，都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式，而简单对象，特别是只需要通过 new 就可以完成创建的对象，无需使用工厂模式。如果使用工厂模式，就需要引入一个工厂类，会增加系统的复杂度。\n\n\n我们将创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory。\n\nFactoryPatternDemo，我们的演示类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息（CIRCLE / RECTANGLE / SQUARE），以便获取它所需对象的类型。\n\n![](media/15223783261656.jpg)\n\n\n举例如下：（我们举一个发送邮件和短信的例子）\n首先，创建二者的共同接口：\n\n```\n\t \tpublic interface Sender {  \n\t \t    public void Send();  \n\t \t}\n```\n\n其次，创建实现类：\n\n```\n\t1.\tpublic class MailSender implements Sender {  \n\t2.\t    @Override  \n\t3.\t    public void Send() {  \n\t4.\t        System.out.println(\"this is mailsender!\");  \n\t5.\t    }  \n\t6.\t}  \n\n\t\n\t1.\tpublic class SmsSender implements Sender {  \n\t2.\t  \n\t3.\t    @Override  \n\t4.\t    public void Send() {  \n\t5.\t        System.out.println(\"this is sms sender!\");  \n\t6.\t    }  \n\t7.\t}  \n\n```\n\t\n最后，建工厂类：\n\n```\n\t1.\tpublic class SendFactory {  \n\t2.\t  \n\t3.\t    public Sender produce(String type) {  \n\t4.\t        if (\"mail\".equals(type)) {  \n\t5.\t            return new MailSender();  \n\t6.\t        } else if (\"sms\".equals(type)) {  \n\t7.\t            return new SmsSender();  \n\t8.\t        } else {  \n\t9.\t            System.out.println(\"请输入正确的类型!\");  \n\t10.\t            return null;  \n\t11.\t        }  \n\t12.\t    }  \n\t13.\t}  \n\n```\n\t\n我们来测试下：\n\n```\n\t1.\tpublic class FactoryTest {  \n\t2.\t  \n\t3.\t    public static void main(String[] args) {  \n\t4.\t        SendFactory factory = new SendFactory();  \n\t5.\t        Sender sender = factory.produce(\"sms\");  \n\t6.\t        sender.Send();  \n\t7.\t    }  \n\t8.\t}  \n\n```\n\n输出：this is sms sender!\n\n\n\n\n"
  },
  {
    "path": "其它/模式/桥接模式.md",
    "content": "# 桥接模式\n\n桥接模式就是把事物和其具体实现分开，使他们可以各自独立的变化。桥接的用意是：将抽象化与实现化解耦，使得二者可以独立变化，像我们常用的JDBC桥DriverManager一样，JDBC进行连接数据库的时候，在各个数据库之间进行切换，基本不需要动太多的代码，甚至丝毫不用动，原因就是JDBC提供统一接口，每个数据库提供各自的实现，用一个叫做数据库驱动的程序来桥接就行了。我们来看看关系图：\n\n![](media/15302772700638.jpg)\n\n```\n先定义接口：\n\t1.\tpublic interface Sourceable {  \n\t2.\t    public void method();  \n\t3.\t}  \n\n\t\n分别定义两个实现类：\n\t1.\tpublic class SourceSub1 implements Sourceable {  \n\t2.\t  \n\t3.\t    @Override  \n\t4.\t    public void method() {  \n\t5.\t        System.out.println(\"this is the first sub!\");  \n\t6.\t    }  \n\t7.\t}  \n\n\t1.\tpublic class SourceSub2 implements Sourceable {  \n\t2.\t  \n\t3.\t    @Override  \n\t4.\t    public void method() {  \n\t5.\t        System.out.println(\"this is the second sub!\");  \n\t6.\t    }  \n\t7.\t}  \n定义一个桥，持有Sourceable的一个实例：\n\t1.\tpublic abstract class Bridge {  \n\t2.\t    private Sourceable source;  \n\t3.\t  \n\t4.\t    public void method(){  \n\t5.\t        source.method();  \n\t6.\t    }  \n\t7.\t      \n\t8.\t    public Sourceable getSource() {  \n\t9.\t        return source;  \n\t10.\t    }  \n\t11.\t  \n\t12.\t    public void setSource(Sourceable source) {  \n\t13.\t        this.source = source;  \n\t14.\t    }  \n\t15.\t}  \n\t1.\tpublic class MyBridge extends Bridge {  \n\t2.\t    public void method(){  \n\t3.\t        getSource().method();  \n\t4.\t    }  \n\t5.\t}  \n测试类：\n\t1.\tpublic class BridgeTest {  \n\t2.\t      \n\t3.\t    public static void main(String[] args) {  \n\t4.\t          \n\t5.\t        Bridge bridge = new MyBridge();  \n\t6.\t          \n\t7.\t        /*调用第一个对象*/  \n\t8.\t        Sourceable source1 = new SourceSub1();  \n\t9.\t        bridge.setSource(source1);  \n\t10.\t        bridge.method();  \n\t11.\t          \n\t12.\t        /*调用第二个对象*/  \n\t13.\t        Sourceable source2 = new SourceSub2();  \n\t14.\t        bridge.setSource(source2);  \n\t15.\t        bridge.method();  \n\t16.\t    }  \n\t17.\t}  \noutput：\nthis is the first sub! this is the second sub!\n\n\n```\n\n这样，就通过对Bridge类的调用，实现了对接口Sourceable的实现类SourceSub1和SourceSub2的调用。接下来我再画个图，大家就应该明白了，因为这个图是我们JDBC连接的原理，有数据库学习基础的，一结合就都懂了。\n\n\n![](media/15302773539085.jpg)\n\n\n"
  },
  {
    "path": "其它/模式/装饰器模式.md",
    "content": "# 装饰器模式\n\n顾名思义，装饰模式就是给一个对象增加一些新的功能，而且是动态的，要求装饰对象和被装饰对象实现同一个接口，装饰对象持有被装饰对象的实例，关系图如下：\n\n![](media/15272464307619.jpg)\n\n\nSource类是被装饰类，Decorator类是一个装饰类，可以为Source类动态的添加一些功能，代码如下：\n\n```\n\t1.\tpublic interface Sourceable {  \n\t2.\t    public void method();  \n\t3.\t}  \n\n\n\t1.\tpublic class Source implements Sourceable {  \n\t2.\t  \n\t3.\t    @Override  \n\t4.\t    public void method() {  \n\t5.\t        System.out.println(\"the original method!\");  \n\t6.\t    }  \n\t7.\t}  \n\n\t1.\tpublic class Decorator implements Sourceable {  \n\t2.\t  \n\t3.\t    private Sourceable source;  \n\t4.\t      \n\t5.\t    public Decorator(Sourceable source){  \n\t6.\t        super();  \n\t7.\t        this.source = source;  \n\t8.\t    }  \n\t9.\t    @Override  \n\t10.\t    public void method() {  \n\t11.\t        System.out.println(\"before decorator!\");  \n\t12.\t        source.method();  \n\t13.\t        System.out.println(\"after decorator!\");  \n\t14.\t    }  \n\t15.\t}  \n\n\n\t1.\tpublic class DecoratorTest {  \n\t2.\t  \n\t3.\t    public static void main(String[] args) {  \n\t4.\t        Sourceable source = new Source();  \n\t5.\t        Sourceable obj = new Decorator(source);  \n\t6.\t        obj.method();  \n\t7.\t    }  \n\t8.\t}  \n\n```\n\n输出：\nbefore decorator!\n the original method!\n after decorator!\n\n装饰器模式的应用场景：\n1. 需要扩展一个类的功能。\n2. 动态的为一个对象增加功能，而且还能动态撤销。（继承不能做到这一点，继承的功能是静态的，不能动态增删。）\n\n\n\n"
  },
  {
    "path": "其它/模式/门面模式.md",
    "content": "# 门面模式\n\n门面模式是为了解决类与类之家的依赖关系的，像spring一样，可以将类和类之间的关系配置到配置文件中，而门面模式就是将他们的关系放在一个Facade类中，降低了类类之间的耦合度，该模式中没有涉及到接口，看下类图：（我们以一个计算机的启动过程为例）\n\n![](media/15296368881817.jpg)\n\n\n```\n我们先看下实现类：\n\n\t1.\tpublic class CPU {  \n\t2.\t      \n\t3.\t    public void startup(){  \n\t4.\t        System.out.println(\"cpu startup!\");  \n\t5.\t    }  \n\t6.\t      \n\t7.\t    public void shutdown(){  \n\t8.\t        System.out.println(\"cpu shutdown!\");  \n\t9.\t    }  \n\t10.\t}  \n\n\t1.\tpublic class Memory {  \n\t2.\t      \n\t3.\t    public void startup(){  \n\t4.\t        System.out.println(\"memory startup!\");  \n\t5.\t    }  \n\t6.\t      \n\t7.\t    public void shutdown(){  \n\t8.\t        System.out.println(\"memory shutdown!\");  \n\t9.\t    }  \n\t10.\t}  \n\n\n\t1.\tpublic class Disk {  \n\t2.\t      \n\t3.\t    public void startup(){  \n\t4.\t        System.out.println(\"disk startup!\");  \n\t5.\t    }  \n\t6.\t      \n\t7.\t    public void shutdown(){  \n\t8.\t        System.out.println(\"disk shutdown!\");  \n\t9.\t    }  \n\t10.\t}  \n\n\n\t1.\tpublic class Computer {  \n\t2.\t    private CPU cpu;  \n\t3.\t    private Memory memory;  \n\t4.\t    private Disk disk;  \n\t5.\t      \n\t6.\t    public Computer(){  \n\t7.\t        cpu = new CPU();  \n\t8.\t        memory = new Memory();  \n\t9.\t        disk = new Disk();  \n\t10.\t    }  \n\t11.\t      \n\t12.\t    public void startup(){  \n\t13.\t        System.out.println(\"start the computer!\");  \n\t14.\t        cpu.startup();  \n\t15.\t        memory.startup();  \n\t16.\t        disk.startup();  \n\t17.\t        System.out.println(\"start computer finished!\");  \n\t18.\t    }  \n\t19.\t      \n\t20.\t    public void shutdown(){  \n\t21.\t        System.out.println(\"begin to close the computer!\");  \n\t22.\t        cpu.shutdown();  \n\t23.\t        memory.shutdown();  \n\t24.\t        disk.shutdown();  \n\t25.\t        System.out.println(\"computer closed!\");  \n\t26.\t    }  \n\t27.\t}  \n\t\nUser类如下：\n\n\t1.\tpublic class User {  \n\t2.\t  \n\t3.\t    public static void main(String[] args) {  \n\t4.\t        Computer computer = new Computer();  \n\t5.\t        computer.startup();  \n\t6.\t        computer.shutdown();  \n\t7.\t    }  \n\t8.\t}  \n\n```\n\n输出：\n\nstart the computer!\n\n cpu startup!\n\n memory startup!\n\n disk startup!\n\n start computer finished!\n\n begin to close the computer!\n\n cpu shutdown!\n\n memory shutdown!\n\n disk shutdown!\n\n computer closed!\n\n\n如果我们没有Computer类，那么，CPU、Memory、Disk他们之间将会相互持有实例，产生关系，这样会造成严重的依赖，修改一个类，可能会带来其他类的修改，这不是我们想要看到的，有了Computer类，他们之间的关系被放在了Computer类里，这样就起到了解耦的作用，这，就是门面模式！\n\n\n"
  },
  {
    "path": "其它/模式/静态工厂方法模式.md",
    "content": "# 静态工厂方法模式\n\n将上面的多个工厂方法模式里的方法置为静态的，不需要创建实例，直接调用即可。\n\n```\n\n\t1.\tpublic class SendFactory {  \n\t2.\t      \n\t3.\t    public static Sender produceMail(){  \n\t4.\t        return new MailSender();  \n\t5.\t    }  \n\t6.\t      \n\t7.\t    public static Sender produceSms(){  \n\t8.\t        return new SmsSender();  \n\t9.\t    }  \n\t10.\t}  \n\n\n\t1.\tpublic class FactoryTest {  \n\t2.\t  \n\t3.\t    public static void main(String[] args) {      \n\t4.\t        Sender sender = SendFactory.produceMail();  \n\t5.\t        sender.Send();  \n\t6.\t    }  \n\t7.\t}  \n\n```\n\t\n输出：this is mailsender!\n\n\n# 工厂模式总结\n\n工厂模式适合：凡是出现了大量的产品需要创建，并且具有共同的接口时，可以通过工厂方法模式进行创建。在以上的三种模式中，第一种如果传入的字符串有误，不能正确创建对象，第三种相对于第二种，不需要实例化工厂类，所以，大多数情况下，我们会选用第三种——静态工厂方法模式。\n\n\n"
  },
  {
    "path": "日常记录与计划/2015总结与2016畅想.md",
    "content": "# 2015总结与2016畅想\n\n#2015总结\n\n我是11月9号加入宝宝树FE团队的，在这2个月的时间里认识了很多新朋友，也逐渐的适应了新的环境。目前主要负责跟进react-native在宝宝树产品中的实施和推广工作，使用了react-native一个月左右，对这个产品越来越喜欢，希望在2016能和伙伴们一起将RN技术顺利愉快的应用起来，为团队、为公司创造实际的价值。\n\n#2016畅想\n\n本来想叫2016规划的，但是觉得我来宝宝树团队的时间太短了，有些东西理解的并不透彻，有些见解也不准确，所以改名叫2016畅想了，里面写了一些我认为2016可以做的事，仅当做抛砖引玉，还望伙伴们多参考，多提意见。\n\n#原则\n\n我习惯在做事情之前先定下原则和愿景，原则是做事情的底线，愿景是做事情的目标。\n\n###原则1：不要为了追求新技术而使用新技术\n技术每天都在变化，每天都有新技术出现，不要被其创新的思想，精妙的设计遮蔽了双眼，一定要多问自己几个为什么，我为什么要选择这个技术？这个技术究竟能解决我们什么问题？解决的问题真的是问题吗？\n\n弄清楚这些之后，再来抉择，会让我们的抉择更加准确，更加能解决实际问题。\n###原则2：一切抉择要落地到我们的愿景\n愿景就是我们做事的目标，是我们长跑路上的终点，它是我们抉择的重要依据，所以，我们做的一切抉择都不要忘了初心，不要忘了我们的终点在哪里，不要偏离方向。\n\n#愿景\n\n我们宝宝树团队2016的工作目标应该不限于以下几点，这是我能想到的几点，也是我认为比较重要的几点。\n\n1. 最终用户产品体验提升\n\t1. 响应快\n\t1. 效果炫\n1. 产品交付能力提升\n\t1. 交付快\n\t1. 质量好\n1. 团队能力提升\n\t1. 技术氛围像bat看齐\n\t1. 技术追求极致\n\t1. 成果乐于分享\n\t\n#痛点\n**此部分内容可能很不准确，信息来源于我日常看到听到，并不是我实际工作遇到。**\n\n痛点里写的是目前的现状情况，是为了让我们找到我们目前最难受的点，最需要解决的点，根据28原则，也就是2的部分，解决了2，我们也就不2了。\n\n1. 有需求偏差导致的返工\n1. 有些已有的通用接口or功能不知道，重复造轮子，或者找不到示例代码\n1. 调试的效率低，改代码-》手动编译-》刷新浏览器（编译的速度不知道如何）\n1. 各种浏览器的兼容性问题较多，尤其移动端\n1. 使用文本编辑器写代码，错误看不出来，手写的慢等\n1. 前端等后端工程师接口，耽误了时间\n1. 基于开源库编程，某些工作是重复的\n\n#解决痛点\n\n1. 有需求偏差导致的返工-**能否更严格的定义需求输入，这只能解决因为需求和技术沟通造成的信息损失。有时候由于工期的原因，需求分析师都还没理清该怎么做，这种情况是解决不了的。**\n1. 有些已有的通用接口or功能不知道，重复造轮子，或者找不到示例代码-**对通用接口，要求代码即文档，有统一规范，有注释要求，通过查看源码，就能找到想要的东西。通用接口最好有统一的发布方式。**\n1. 调试的效率低，改代码-》手动编译-》刷新浏览器-**监控资源改变自动编译，增量编译。重构之后，变成了改代码=》刷新浏览器，少了一个步骤。**\n1. 各种浏览器的兼容性问题较多，尤其移动端-**总结并分享出来【那些年我们掉过的坑】，让伙伴们不在掉入同样的坑**\n1. 使用vim等编辑器写代码，错误提示不好，手写的慢等-**可能是因为我一直用ide开发，不熟悉vim等编辑器，可以让大家试试ide，看看哪个更适合自己**\n1. 前端等后端工程师接口，耽误了时间-**引入mock框架，前后端定好数据格式，前端写好mock数据，就可以开发了，而且可以进行单元测试，提升了测试效率**\n1. 基于开源库编程，某些工作是重复的-**沉淀我们自己的库吧，前提是按照统一的规范，bbt-webframe！**\n\n#想法整理\n\n梳理了一下解决痛点里的内容，并加入了我目前负责的react-native的相关内容，觉得2016我们可以这样：\n\n###1.开源bbt-react-native框架\nreact-native我已经使用了1个月左右，觉得其确实潜力无穷，确实很值得我们去尝试探索。\n\nbbt-react-native是以react-native为基础的纯技术框架，为业务开发提供更多的组件、功能和规范，是提升开发效率、提升开发质量、积累成果的保证。\n\n为什么要开源它呢？\n\n####1. 回馈开源\n我记得在我来宝宝树面试的时候，问了胡老师一个问题【您对待开源产品是什么态度？是义无反顾的用还是乐于自己造轮子？】，胡老师说，要在深入了解开源之后用，并回馈于它。\n\n我们已经在开源产品中获得了大量的益处了，在我们有机会为软件开源界贡献力量的时候，我们为什么不去做呢，反正也没耽误我们额外的时间和精力。\n\n####2. 提升质量\n试想你正在写的代码是要被很多人阅读的，你就一定会注意你的代码质量，该加注释的加注释，该写文档的写文档，该提供demo的提供demo。可以帮助我们建立良好的工作习惯。\n####3. 聚集更大的力量\n如果有一定的关注度，那么给你提issue，提pr的人会很多，这聚集了更多人的智慧，你的产品想不好都难。\n####4. 向bat看齐\n在母婴行业，宝宝树已经是行业内领军企业了，但是我们的技术在技术圈内好像并没有那么大的影响力，其实不是我们不行，而是我们没有去做。\n\n看看bat各种开源产品，各种软件服务，其实都是在他们日常工作过程中多做了一些，多有了一些开源意识，把应用于日常业务中的一些技术、产品直接放在互联网上给其它人使用，像百度echart，阿里云等。\n\n\nps1：公司内部的所有技术都可以按照此方式来做，但是没必要特意去做\n\nps2：如果开源公司不允许，毕竟知识产权属于公司，那这条作废。\n\n###2.建立bbt-fe技术博客\n这个博客是组内所有人一起维护的，大家都要参与，可以起个酷酷的笔名了。\n\n内容可以是原创，转载，读书笔记，但是一定要都是干货。\n\n写东西，写总结其实是思维提升的一个过程，可以对问题有更全面更深刻的认识，分享给他人的同时自己也受益匪浅，所以鼓励原创。\n\n前面提到的专题【那些年我们掉过的坑】可以帮助大家少走弯路。\n\n读书笔记可以分享你的读书心得，把书看薄，帮助别人更快的了解一本书。\n\n现在组内每周的分享会我觉得特别棒，博客里可以加入每周分享的内容，方便大家查阅。\n\n我们组内如果做的好的话，可以推广到公司内其它组，整个公司的学习分享氛围会越来越好。\n\n###3.完善bbt-fe技术架构体系\n\n以下内容都可以作为专题深入探讨，这不一一列举原因，一切都是为了愿景。\n\n1. 引入单元测试\n1. 引入mock机制\n1. 监控资源改动的自动编译，且支持增量编译\n1. fiddler调试线上\n1. 对通用接口，要求代码即文档，有统一规范，有注释要求，通过查看源码，就能找到想要的东西。通用接口最好有统一的发布方式。\n1. 沉淀bbt-fe技术框架，通用组件开发规范，积累成果。\n2. 建立交互效果库，类似姚琪上次分享的，积累一些内容，可以提供给设计，作为设计素材。\n\n"
  },
  {
    "path": "日常记录与计划/2016-09-13.md",
    "content": "# 混合开发技术体系支撑计划\n\n# 愿景目标（现状问题）\n\n当前孕育app的dau已经是500W+，目前公司的期望是dau再突破一个新的数量级。\n\t\n# 解决思路\n\n主要从2个方面来解决，1是留存，2是新增。\n\n## 留存\n\n留存，是最重要的指标。\n\n留存的定义，\n\n## 新增\n\n新增，在留存已经做到位的前提下，可以考虑去突破新增这个指标。\n\n# 技术能做些什么？\n\n提升页面打开速度。\n\n不要崩溃闪退。\n\n提供精准的用户使用数据，支撑数据分析。【无埋点技术】【如何帮助BI提升数据准确性】\n\nAB测试。【版本代码不混编；用户选择算法；用户使用数据采集；版本主动控制；】\n\n流畅的使用体验。【滚动；动画；】\n\n\n\n前端直接进行数据分析，上报分析结果。\n在关键步骤，弹出反馈or使用帮助。\n\n# 计划\n\n\n"
  },
  {
    "path": "日常记录与计划/todo list.md",
    "content": "# todo list\n1. 相对路径的图片，是否是从服务器下载的，如何做到下载一次就就不用下载了，脱离服务器 ok\n\t2. 应该是相对这个生成的js的某个文件夹会存，和js一起打包到app中即可，待验证\n2. 高宽未知的图片的应用方案。 ok：0.18.0中提供了ios的解决方案\n1. 初步了解不可变数据框架 ok\n2. 初步了解mixin ok：已经不推荐使用\n2. 将通用组件提交到npm中\n1. 重构使用react提供的propTypes，而不是options，并更新文档;重构propsConcat，只校验，不赋值，使用...this.props的方式赋值。这样不好，一些没用的也会赋值上去，要找一个好的解决方案; ok\n2. 使用RN组件规范的displayName代替自定义的_compName; ok\n2. 搜索历史的存储和展现\n5. listview的下拉刷新\n6. detail页面的上拉查看详情\n6. RN的mvc开发框架flux ok\n7. RN动画与CSS3动画的异同\n9. filder的使用推广，mac中没有，找找类似的\n10. fetch是否需要封装 \n11. 校验的封装\n12. 第三方开发组件的集成规范和方案\n13. 组件间通信的封装 ok\n\t14. http://www.ghugo.com/react-native-communicate/\n\t15. http://www.ghugo.com/react-native-event-emitter/\n16. 面向状态设计还是面向接口设计的思考 ok\n\t17. 尽量沿用RN思想，面向状态设计，参考modal组件\n17. 文档采用代码即文档的模式\n18. 因为css里没有选择器，给css起名字很痛苦，有没有好办法 ok：尽量使用inline样式\n19. 原生在调用一个rn页面时候，如何给rn页面传递参数？ ok：官方提供方法\n20. ReactNative的组件状态对渲染范围的影响.todo待测试 ok：不仅仅影响自身\n\t21. //todo 思考：再封装一个按钮的组件，自己来存自己是否选中的状态；\n\t22. //todo 但是此组件如何与自己的兄弟组件进行交互呢？点击此此组件要消除到其它已选的组件。\n\t23. //兄弟组件间通信，肯定要借助父组件进行，在理解RN特性【state的改变只影响自己和自己的子组件的渲染】\n\t24. 【这个特性理解的好像不对，有空测试一下】的基础下，尽量只改变本应该改变的组件的state的状态值\n1. 在基类BaseLogicObj的构造器中对对象的所有方法进行代理-todo待验证\n2. gradle\n3. graphql\n4. 编码规范重构，参考：https://github.com/sunnylqm/react-native-coding-style ok\n5. className react 的 插件\n6. listviewbindwithurl在unmount方法中停止组件，避免warniing\n7. eslint集成\n\t8. 规则配置梳理---ok\n\t9. 集成到rn脚本中or单独定义脚本\n2. **codepush客户端源码研究**\n1. **RN中cookie的使用与测试**\n1. **页面再次展现出来的事件,类似安卓的onResume**\n3. **安卓studio工程的搭建**\n4. **fetch还是需要封装一层，方便监控、mock等操作**\n\n\n\n"
  },
  {
    "path": "日常记录与计划/week2015-11-23.md",
    "content": "# week 2015-12-23\n##本周的工作思路是用开发实际应用继续加深理解RN。\n\n###20151125周三\n1. 将代码提交到了github上\n2. 首页中今日特卖和即将上线的功能开发\n\t1.\t对同一个listview，动态切换不同数据源的场景加深了理解 \n3. app主界面的tab页切换功能完善\n\t1. 终于找到了一个用css隐藏显示的办法【同时不会不停触发listview的onendreached】，RN你快支持display=none吧\n4. 部分翻译官网Performance一节，同时进行测试和应用\n\t1. 加深了对动画为何会卡顿的理解，能帮助理清优化的思路\n\t2. dev模式=false确实可以提升很大的性能。同时没有发现true false有何区别\n\t3. 要善于用shouldComponentUpdate来规避无必要的diff算法\n\t\n###20151126 周四\n1. 升级RN到0.15.0\n\t1. 安卓升级后，因为有接口调整，我就直接复制了一个类过来，但是忘记改包路径了，编译又不报错，app启动就退出\n\t2. 升级后listview的onendreached事件有时候不触发的bug没有了，很棒。\n2. 开发分类页面\n\t1. 组件间依赖、通信的理解\n\t2. 用户点击多次进行提交的问题要时刻考虑着\n\t3. 开始使用console.log替代大部分代码日志，比alert舒服多了，查看日志有时候也能发现一些隐藏的bug\n3. 完成github文档库的建立\n\t1. 同时固化写文档与发布的一般过程\n\t2. 使用mweb lite 在本地写md，同时git hub上建一个blog的库，git push上去就是blog了，可惜不支持评论功能，只能借助issues了\n\t3. 怎么解决在onenote上写，然后发布到blog上呢，难道就不能用onenote来写了吗？目前是没有办法了，只能迁移日志环境到github上了。\n4. 今天感觉开发快了一些也顺利了一些，不到一下午的事件就完成了分类页面的开发，可能也是因为功能较简单\n5. 昨天没翻译完的performance继续做了一些，但是不多\n\n### 20151127周五\n1. 计划\n\t1. 完成一个页面跳转的开发比如点击今日特卖的一项或者搜索按钮\n\t2. 整理这一周的成果，完善开发规范和其它文档，发布到github上\n\t3. 完成一篇官方文档的翻译，并发布到github上\n\t4. 制定下一周的工作主题和计划\n1. 完成了对搜索页面和商品活动页面的跳转\n2. 完成了header组件的封装\n3. 搜索页面中用到了textinput组件，不能直接获得其value，有点坑啊\n4. textinput组件在安卓下的高度如果太低文字还显示不全，跳转padding和height后还可以解决\n5. 文档工作今天基本没有做，抽空就做\n\n\n\n"
  },
  {
    "path": "日常记录与计划/week2015-11-30.md",
    "content": "# week 2015-11-30\n\n##本周的工作主题\n### 通用组件的封装，包括但不限于\n1. 后台分页listview\n2. 通用导航组件\n3. 基础样式组，比如隐藏等\n4. tab页组件\n5. 提示框组件\n6. loading组件\n7. fetchAPI\n8. 页面转场API\n\n\n###2015-11-30周一\n1. 计划\n\t1. 通用组件loading组件的封装\n\t2. 通用组件后台分页list的封装\n\t2. 利用上述组件完成美囤搜索界面\n\t3. 继续翻译官网性能一章\n2. 完成了loading组件的封装，只提供loading的内容封装，具体显示位置由使用组件的样式代码决定。\n3. 完成了ListViewBindUrl的组件。明天还需要完成一些细节工作，主体功能已经跑通。\n\t1. 组件封装之后，后台分页数据的列表查询显示开发非常简单，业务代码只需要关心如何显示，分页、数据提交、列表状态提示、等都进行了封装。\n4. 利用上述组件完成了美囤搜索界面的开发，明天还需要调整一些页面细节，主体已经完成。\n\n\n\n###2015-12-01周二\n1. 计划\n\t1. 完成昨天未完成的细节工作\n\t4. 制定通用组件开发规范\n\t3. 制定月计划\n\t4. 官网性能一章翻译完成\n1. 完成了通用组件开发规范\n2. 建立了通用组件目录结构，目前分为base和views\n3. 完成了属性检测器和属性连接器的开发\n\t1. 属性检测器帮助通用技术组件校验和发布属性\n\t2. 属性连接器帮助通用技术组件直接将业务定义的属性全部传递给底层组件\n4. 基于以上成果，重构了工程的代码\n5. 完善了搜索列表页面中的价格折扣等信息。\n6. 完成了月计划\n7. 完成了官网性能一章的翻译\n\n\t\n\t\n###2015-12-02周三\n1. 计划\n\t3. tab页组件开发\n\t4. 基础样式库开发\n\t5. 基于以上成果，美囤妈妈搜索结构列表头部tab开发\n1. 完成TabApi、基础样式库等开发\n2. 完成搜索列表头部tab开发\n3. listview支持切换数据源，并对多线程情况下，加入线程保护的机制\n4. listview在切换数据源时候，安卓下要先滚动到头部，否则过高的height会导致endreached事件的不停触发。\n3. 遇到了循环require引用报错的问题。多拆出一层，就好了。类似问题恐怕是个大坑。**todo有空RN的issues提一下。todo有空好好测试一下，找一下规律。**\n\n\t\n\t\n\t\n###2015-12-03周四\n1. 计划\n\t3. 搜索的筛选功能的开发\n\t4. 编写组件设计思考初稿\n1. 编写了RN组件设计思考初稿，一直在思索RN组件的使用模式，还需要继续沉淀沉淀。\n2. 完成了筛选tab的开发，点击筛选会缓存list内容，筛选关闭时会显示缓存内容\n\t2. 加入筛选功能的tab开发中遇到了**多个按钮是一个类，但不是一个对象的问题，this并不是一个**，所以A按钮赋予this的值，B按钮通过this是获得不到的，以后要注意。 \n\t3. 觉得这块写的逻辑好复杂，**todo考虑这块的重构or封装or反思**\n3. 参加了技术分享【贝塞尔曲线】和计划讨论和年会抽奖程序讨论会。\n\n\n\n\n###2015-12-04周五\n1. 计划\n\t3. 搜索的筛选功能的开发\n\t6. 安卓对实体键的处理：返回，菜单等\n\t8. 解决SwitchAndroid不好使的问题\n1. 解决了安卓下switch不显示的问题，耽误了好久，最后发现是安卓打包工具比较旧的问题，坑啊。。\n2. 完成筛选功能的页面开发，筛选的具体功能还没有做，延后\n3. 安卓实体返回键的处理很简单，直接在主页使用BackAndroid即可\n4. 安卓菜单键需要在activity中进行调整\n3. 今天阅读react文档，发现react提供了一套属性定义和检查器机制**propTypes**，和我其几天封装的基本一致，理解消化这套机制后准备把代码重构下。\n\t4. 以前忽略了仔细看react的文档，一直都在看react native的文档，看来要补一补\n\n\n"
  },
  {
    "path": "日常记录与计划/week2015-12-07.md",
    "content": "#### week 2015-12-07\n\n##本周的工作主题\n### 继续 通用组件的封装，这周应完成组件的第一轮开发工作，至少列出的要开发完，包括但不限于：\n1. 后台分页listview -ok\n2. 通用导航组件\n3. 基础样式组，比如隐藏等 -ok\n4. tab页组件 \n5. 提示框组件\n6. loading组件 -ok\n7. fetchAPI\n8. 页面转场API\n\n### 业务上，ssale页面开发完，筛选功能开发完，登录功能开发完，添加购物车功能开发完。\n\n\n###2015-12-07周一\n1. 计划\n\t7. 卖场页面的开发ssale\n\t8. detail页面开发\n\t9. 筛选功能开发\n1. 完成筛选页面\n2. 完成ssale页面\n3. 添加商品详情页面\n3. RN的服务器图片对动态高宽的支持并不好，`需要探索解决方案`\n4. RN的服务器图片，在android下不支持下载的时候的loading，ios中支持【defaultSource】，`需要探索解决方案`\n\n\n\n###2015-12-08周二\n1. 计划\n\t3. 支持安卓IOS的跑马灯，改or找\n\t5. detail页面开发\n1. 详细页面完成50%\n2. **动态图的高宽问题还没有解决**\n3. **下拉查看详情，上拉刷新都还没有解决**\n2. 更换了swiper，支持安卓\n3. 初步使用了proptypes，只有dev=true的时候才有校验\n\n\n###2015-12-09周三\n1. 计划\n\t4. detail页面去掉header使用透明按钮\n\t6. listview的上拉展示详情\n\t\t7.  RN动画类的初步使用\n\t7. 登录功能\n\t8. 加入购物车功能\n\t9. 立刻购买功能\n\t1. 评论列表功能\n2. 主要进行了detail页面上拉加载详情的功能，涉及到RN动画相关的很多东西，进行了研究并制作demo实验，上拉加载的方案有些眉目了，但是还是有问题没有解决。晚上想想，看明天是否还继续。动画没有计划这么早进行。\n3. 完成了detail头部的透明返回按钮\n\n\n###2015-12-10周四\n1. 计划\n\t6. listview的上拉展示详情\n\t\t7.  RN动画类的初步使用\n\t7. 登录功能\n\t8. 加入购物车功能\n\t9. 立刻购买功能\n2. **开发技巧：在开发的时候，如果能把页面直接作为系统首页进行测试的效率会很高**\n\t3. 页面不能有前后的关联\n\t4. 当写静态页面结构的时候肯定是没有关联的\n3. 完成了登录功能，不过有验证码的时候会失败\n4. 启动了modal组建的封装\n\n\n\n###2015-12-11周五\n1. 计划\n\t6. listview的上拉展示详情-**延后**\n\t\t7.  RN动画类的初步使用\n\t8. 加入购物车功能\n\t9. 完成modal和mask组件封装\n\t1. 测试cookie\n\t1. 立刻购买功能-**暂停，没有意义**\n2. 完成了对bbtRN的抽取和整体模块化\n3. 解决了viewpage在ios下隐藏之后崩溃的问题，隐藏的时候要加宽，估计宽又撑爆了\n4. 完成了modal的1.0版本，支持最简单的从侧面划出，并固定在底部的功能。**后期可以加入可全面的配置参数**.安卓下高度不准造成的bug，等RN升级解决吧。\n5. 登录成功之后，cookie并没有发生变化，**有时间好好测试一下**，需要自己搭建服务器\n\n\n\n\n"
  },
  {
    "path": "日常记录与计划/week2015-12-14.md",
    "content": "#### week 2015-12-14\n\n##本周的工作主题\n### 整理重构一切成果，包括文档编写，代码重构。原则上不启动新的开发工作。\n### 本周要发布出BbtRN的1.0版本。重点是标准和规范。\n### 遗留的难点问题和todolist可以尝试解决，但不要耽误太久。\n\n\n###2015-12-07周一\n1. 计划\n\t1. RN升级到最新\n\n \n\n\n\n\n"
  },
  {
    "path": "日常记录与计划/week2016-01-04.md",
    "content": "# 2016-01-04\n\n本周基本计划，从以下任务中按顺序尽量执行。\n\n1. 集成RN到app中-ios版本\n\t2. 集成方案验证\n\t3. 与native交互接口验证\n2. 集成RN到app中-安卓版本\n\t2. gradle深入学习\n2. eslint规范使用和调整\n2. codepush客户端源码分析\n5. react-web启动\n\n"
  },
  {
    "path": "日常记录与计划/总体计划1.md",
    "content": "# RN总体计划\n\t1. 相关地址\n\t\ta. 文档发布地址：https://github.com/cnsnake11/blog\n\t\tb. 工程地址：https://github.com/cnsnake11/AwesomeProject\n\t\t\ti. 目前有 BbtReactNative框架、demo、美囤妈妈等\n\t\t\t\n\t2. 总体目标\n\t\ta. 逐步推广RN在项目中应用\n\t\t\ti. 开发规范\n\t\t\tii. 开发指导\n\t\t\tiii. 部署升级\n\t\t\tiv. 沉淀基于RN的技术框架\n\t\t\t\n\t3. 步骤、计划\n\t\n\t\ta. 里程碑1\n\t\t\ti. 时间：2周左右。11月16日开始。\n\t\t\tii. 内容\n\t\t\t\t1) 相关知识学习\n\t\t\t\t2) 环境搭建\n\t\t\t\t3) 编写demo\n\t\t\tiii. 成果物\n\t\t\t\t1) 文档\n\t\t\t\t\ta) 学习RN之前需要掌握的知识 ok\n\t\t\t\t\tb) RN学习路线图 ok\n\t\t\t\t\tc) RN问题总结 ok\n\t\t\t\t2) 代码\n\t\t\t\t\ta) demo工程 ok\n\t\t\tiv. 状态\n\t\t\t\t1) 已完成\n\t\t\t\t2) demo工程已经提交到github上\n\t\t\t\t3) 文档github已经建立\n\t\t\t\t5) todo:安卓的ide环境搭建最后还是没有成功。\n\t\t\t\t\n\t\t\t\t\n\t\tb. 里程碑2【进行中】\n\t\t\ti. 时间\n\t\t\t\t1) 4周左右。11月23日开始。\n\t\t\t\t\n\t\t\tii. 内容\n\t\t\t\t1) 开发美囤妈妈来深入学习、试坑等\n\t\t\t\t2) 基于RN组件开发自定义组件、API层，包括但不限于\n\t\t\t\t\ta) 后台分页listview\n\t\t\t\t\tb) 基础样式组，比如隐藏等\n\t\t\t\t\tc) tab页组件\n\t\t\t\t\td) 提示框组件\n\t\t\t\t\te) loading组件\n\t\t\t\t\tf) 校验组件\n\t\t\t\t3) 沉淀出BbtReactNative开发框架第一版\n\t\t\t\t\n\t\t\tiii. 成果物\n\t\t\t\t1) 文档\n\t\t\t\t\ta) 开发规范系列 ok\n\t\t\t\t\t\ti) 目录与命名规范 ok\n\t\t\t\t\t\tii) 通用组件开发规范 ok\n\t\t\t\t\t\tiii) 业务开发规范 ok \n\t\t\t\t\tb) 开发指导系列 ok\n\t\t\t\t\t\ti) RN常用组件使用技巧 ok\n\t\t\t\t\t\tii) RN样式使用技巧 ok\n\t\t\t\t\t\tiii) RN组件设计思考 ok\n\t\t\t\t\t\tiv) RN调试技巧 ok\n\t\t\t\t\t\tv) RN问题总结 ok\n\t\t\t\t\tc) 官网重要文档翻译2~3篇 ok\n\t\t\t\t\t\ti) 性能\n\t\t\t\t\t\tii) 等。\n\t\t\t\t\td) 自定义组件文档系列 ok\n\t\t\t\t\t\ti) 属性 事件 接口的说明 ok\n\t\t\t\t\t\tii) 示例demo ok\n\t\t\t\t\t\tiii) 原理说明，可不写 ok\n\t\t\t\t2) 代码 ok\n\t\t\t\t\ta) 美囤妈妈工程 ok\n\t\t\t\t\tb) BbtReactNative框架 ok\n\t\t\t\t\t\n\t\t\tiv. 状态\n\t\t\t\t1) 已完成\n\t\t\t\t\n\t\t\t\t\n\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\n\n\n"
  },
  {
    "path": "日常记录与计划/总体计划2.md",
    "content": "#总体计划2\n\n1. 里程碑3\n\t2. 内容\n\t\t1. 工程化 \n\t\t\t2. 能不能吃集成到现有app \n\t\t\t3. 打包-离线模式\n\t\t\t4. 自动化打包\n\t\t\t4. 增量升级\n\t\t1. 基于native开发自定义组件、API层\n\t\t1. 动画与交互视觉效果\n\t\t1. react-web调研 \n\t1. 时间 201601-201602\n\t1. 成果物\n\t\t1. 集成文档\n\t\t2. 打包文档\n\t\t3. 增量升级文档\n\t\t3. 自定义组件自定义API开发规范\n\t\t4. 至少完成页面下拉刷新，上拉查看详情。\n\t\t5. react-web的问题记录文档\n\t1. 状态 进行中\n\n\t\n1. 里程碑4\n\t2. 内容\n\t\t1. cpu 内存 等调优\n\t\t2. 继续并深入动画与交互视觉效果\n\t\t3. react-web集成\n\t1. 时间 待定\n\t1. 成果物 待定\n\t1. 状态 未开始\n\n"
  }
]