{"action":"created","issue":{"url":"https://api.github.com/repos/lcxfs1991/blog/issues/8","repository_url":"https://api.github.com/repos/lcxfs1991/blog","labels_url":"https://api.github.com/repos/lcxfs1991/blog/issues/8/labels{/name}","comments_url":"https://api.github.com/repos/lcxfs1991/blog/issues/8/comments","events_url":"https://api.github.com/repos/lcxfs1991/blog/issues/8/events","html_url":"https://github.com/lcxfs1991/blog/issues/8","id":148960494,"number":8,"title":"React移动web极致优化","user":{"login":"lcxfs1991","id":3348398,"avatar_url":"https://avatars.githubusercontent.com/u/3348398?v=3","gravatar_id":"","url":"https://api.github.com/users/lcxfs1991","html_url":"https://github.com/lcxfs1991","followers_url":"https://api.github.com/users/lcxfs1991/followers","following_url":"https://api.github.com/users/lcxfs1991/following{/other_user}","gists_url":"https://api.github.com/users/lcxfs1991/gists{/gist_id}","starred_url":"https://api.github.com/users/lcxfs1991/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lcxfs1991/subscriptions","organizations_url":"https://api.github.com/users/lcxfs1991/orgs","repos_url":"https://api.github.com/users/lcxfs1991/repos","events_url":"https://api.github.com/users/lcxfs1991/events{/privacy}","received_events_url":"https://api.github.com/users/lcxfs1991/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2016-04-17T14:51:48Z","updated_at":"2016-06-01T01:45:11Z","closed_at":null,"body":"最近一个季度，我们都在为手Q家校群做重构优化，将原有那套问题不断的框架换掉。经过一些斟酌，决定使用react 进行重构。选择react，其实也主要是因为它具有下面的三大特性。\r\n\r\n\r\n\r\n## React的特性\r\n### 1. Learn once, write anywhere\r\n\r\n学习React的好处就是，学了一遍之后，能够写web, node直出，以及native，能够适应各种纷繁复杂的业务。需要轻量快捷的，直接可以用Reactjs；需要提升首屏时间的，可以结合React Server Render；需要更好的性能的，可以上React Native。\r\n\r\n但是，这其实暗示学习的曲线非常陡峭。单单是Webpack+ React + Redux就已够一个入门者够呛，更何况还要兼顾直出和手机客户端。不是一般人能hold住所有端。\r\n\r\n### 2. Virtual Dom\r\nVirtual Dom（下称vd）算是React的一个重大的特色，因为Facebook宣称由于vd的帮助，React能够达到很好的性能。是的，Facebook说的没错，但只说了一半，它说漏的一半是：“除非你能正确的采用一系列优化手段”。\r\n\r\n### 3. 组件化\r\n另一个被大家所推崇的React优势在于，它能令到你的代码组织更清晰，维护起来更容易。我们在写的时候也有同感，但那是直到我们踩了一些坑，并且渐渐熟悉React+ Redux所推崇的那套代码组织规范之后。\r\n\r\n### 那么？\r\n上面的描述不免有些先扬后抑的感觉，那是因为往往作为React的刚入门者，都会像我们初入的时候一样，对React满怀希望，指意它帮我们做好一切，但随着了解的深入，发现需要做一些额外的事情来达到我们的期待。\r\n\r\n\r\n\r\n## 对React的期待\r\n初学者对React可能满怀期待，觉得React可能完爆其它一切框架，甚至不切实际地认为React可能连原生的渲染都能完爆——对框架的狂热确实会出现这样的不切实际的期待。让我们来看看React的官方是怎么说的。React官方文档在Advanced Performanec这一节，这样写道：\r\n\r\n```\r\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\r\n```\r\n\r\n显然React自己也其实只是想尽量达到跟非React版本相若的性能。React在减少重复渲染方面确实是有一套独特的处理办法，那就是vd，但显示在首次渲染的时候React绝无可能超越原生的速度，或者一定能将其它的框架比下去。因此，我们在做优化的时候，可的期待的东西有：\r\n\r\n* 首屏时间可能会比较原生的慢一些，但可以尝试用React Server Render (又称Isomorphic)去提高效率\r\n* 用户进行交互的时候，有可能会比原生的响应快一些，前提是你做了一些优化避免了浪费性能的重复渲染。\r\n\r\n\r\n\r\n## 以手Q家校群功能页React重构优化为例\r\n手Q家校群功能页主要由三个页面构成，分别是列表页、布置页和详情页。列表页已经重构完成并已发布，布置页已重构完毕准备提测，详情页正在重构。与此同时我们已完成对列表页的同构直出优化，并已正在做React Native优化的铺垫。\r\n\r\n这三个页面的重构其实覆盖了不少页面的案例，所以还是蛮有代表性的，我们会将重构之中遇到的一些经验穿插在文章里论述。\r\n\r\n在手Q家校群重构之前，其实我们已经做了一版PC家校群。当时将native的页面全部web化，直接就采用了React比较常用的全家桶套装：\r\n* 构建工具 => gulp + webpack\r\n* 开发效率提升 => redux-dev-tools + hot-reload\r\n* 统一数据管理=> redux\r\n* 性能提升 => immutable + purerender\r\n* 路由控制器 => react-router(手Q暂时没采用)\r\n\r\n为什么我们在优化的时候主要讲手Q呢？毕竟PC的性能在大部份情况下已经很好，在PC上一些存在的问题都被PC良好的性能掩盖下去。手机的性能不如PC，因此有更多有价值的东西深挖。开发的时候我就跟同事开玩笑说：“没做过手机web优化的都真不好意思说自己做过性能优化啊“。\r\n\r\n## 构建针对React做的优化\r\n我在《性能优化三部曲之一——构建篇》提出，“通过构建，我们可以达成开发效率的提升，以及对项目最基本的优化”。在进行React重构优化的过程中，构建对项目的优化作用必不可少。在本文暂时不赘述，我另外开辟了一篇[《webpack使用优化（react篇）》](https://github.com/lcxfs1991/blog/issues/7)进行具体论述。\r\n\r\n\r\n\r\n## 开发效率提升工具\r\n![1](https://cloud.githubusercontent.com/assets/3348398/15649370/ab4db266-26a3-11e6-909d-958c3c795975.png)\r\n\r\n在PC端使用Redux的时候，我们都很喜欢使用Redux-Devtools来查看Redux触发的action，以及对应的数据变化。PC端使用的时候，我们习惯摆在右边。但移动端的屏幕较少，因此家校群项目使用的时候放在底部，而且由于性能问题，我们在constant里设一个debug参数，然后在chrome调试时打开，移动端非必须的时候关闭。否则，它会导致移动web的渲染比较低下。\r\n\r\n\r\n\r\n## 数据管理及性能优化\r\n### Redux统一管理数据\r\n这一部份算是重头戏吧。React作为View层的框架，已经通过vd帮助我们解决重复渲染的问题。但vd是通过看数据的前后差异去判断是否要重复渲染的，但React并没有帮助我们去做这层比较。因此我们需要使用一整套数据管理工具及对应的优化方法去达成。在这方法，我们选择了Redux。\r\n\r\nRedux整个数据流大体可以用下图来描述：\r\n\r\n![2](https://cloud.githubusercontent.com/assets/3348398/15649583/41f46b46-26a5-11e6-9da9-71d90b2b4e04.png)\r\n\r\nRedux这个框架的好处在于能够统一在自己定义的reducer函数里面去进行数据处理，在View层中只需要通过事件去处触发一些action就可以改变地应的数据，这样能够使数据处理和dom渲染更好地分离，而避免手动地去设置state。\r\n\r\n在重构的时候，我们倾向于将功能类似的数据归类到一起，并建立对应的reducer文件对数据进行处理。如下图，是手Q家校群布置页的数据结构。有些大型的SPA项目可能会将初始数据分开在不同的reducer文件里，但这里我们倾向于归到一个store文件，这样能够清晰地知道整个文件的数据结构，也符合Redux想统一管理数据的想法。然后数据的每个层级与reducer文件都是一一对应的关系。\r\n\r\n![3](https://cloud.githubusercontent.com/assets/3348398/15649727/4fa31034-26a6-11e6-9e9a-3220eed40bab.png)\r\n\r\n### 重复渲染导致卡顿\r\n这套React + Redux的东西在PC家校群页面上用得很欢乐， 以至于不用怎么写shouldComponentUpdate都没遇到过什么性能问题。但放到移动端上，我们在列表页重构的时候就马上遇到卡顿的问题了。\r\n\r\n什么原因呢？是重复渲染导致的！！！！！！\r\n\r\n说好的React vd可以减少重复渲染呢？！！！\r\n\r\n请别忘记前提条件！！！！\r\n\r\n你可以在每个component的render里，放一个console.log(\"xxx component\")。然后触发一个action，在优化之前，几乎全部的component都打出这个log，表明都重复渲染了。\r\n\r\n### React性能的救星Immutablejs\r\n![4](https://cloud.githubusercontent.com/assets/3348398/15650035/4c5eb69c-26a8-11e6-8dd3-26ddc32f3293.png) (网图，引用的文章太多以致于不知道哪篇才是出处)\r\n\r\n上图是React的生命周期，还没熟悉的同学可以去熟悉一下。因为其中的shouldComponentUpdate是优化的关键。React的重复渲染优化的核心其实就是在shouldComponentUpdate里面做数据比较。在优化之前，shouldComponentUpdate是默认返回true的，这导致任何时候触发任何的数据变化都会使component重新渲染。这必然会导致资源的浪费和性能的低下——你可能会感觉比较原生的响应更慢。\r\n\r\n这时你开始怀疑这世界——是不是Facebook在骗我。\r\n\r\n当时遇到这个问题我的开始翻阅文档，也是在Facebook的Advanced Performance一节中找到答案：Immutablejs。这个框架已被吹了有一年多了吧，吹这些框架的人理解它的原理，但不一定实践过——因为作为一线移动端开发者，打开它的github主页看dist文件，50kb，我就已经打退堂鼓了。只是遇到了性能问题，我们才再认真地去了解一遍。\r\n\r\nImmutable这个的意思就是不可变，Immutablejs就是一个生成数据不可变的框架。一开始你并不理解不可变有什么用。最开始的时候Immutable这种数据结构是为了解决数据锁的问题，而对于js，就可以借用来解决前后数据比较的问题——因为同时Immutablejs还提供了很好的数据比较方法——Immutable.is()。小结一下就是：\r\n\r\n* Immutablejs本身就能生成不可变数据，这样就不需要开发者自己去做数据深拷贝，可以直接拿prevProps/prevState和nextProps/nextState来比较。\r\n* Immutable本身还提供了数据的比较方法，这样开发者也不用自己去写数据深比较的方法。\r\n\r\n说到这里，已万事俱备了。那东风呢？我们还欠的东风就是应该在哪里写这个比较。答案就是shouldComponentUpdate。这个生命周期会传入nextProps和nextState，可以跟component当前的props和state直接比较。这个就可以参考pure-render的做法，去重写shouldComponentUpdate，在里面写数据比较的逻辑。\r\n\r\n其中一位同事polarjiang利用Immutablejs的is方法，参考pure-render-decorator写了一个[immutable-pure-render-decorator](https://github.com/lcxfs1991/pure-render-deepCompare-decorator/blob/master/src/immutable-pure-render-decorator)。\r\n\r\n那具体怎么使用immutable + pure-render呢？\r\n\r\n对于immutable，我们需要改写一下reducer functions里面的处理逻辑，一律换成Immutable的api。\r\n\r\n至于pure-render，若是es5写法，可以用使mixin；若是es6/es7写法，需要使用decorator，在js的babel loader里面，新增plugins: [‘transform-decorators-legacy’]。其es6的写法是\r\n```\r\n@pureRender\r\nexport default class List extends Component { ... }\r\n```\r\n\r\n### Immutablejs带来的一些问题\r\n#### 不重新渲染\r\n你可能会想到Immutable能减少无谓的重新渲染，但可能没想过会导致页面不能正确地重新渲染。目前列表页在老师进入的时候是有2个tab的，tab的切换会让列表也切换。目前手Q的列表页学习PC的列表页，两个列表共用一套dom结构（因为除了作业布置者名字之外，两个列表一模一样）。上了Immutablejs之后，当碰巧“我发布的“列表和”全部“列表开头的几个作业都是同一个人布置的时候，列表切换就不重新渲染了。\r\n\r\n引入immutable和pureRender后，render里的JSX注意一定不要有同样的key（如两个列表，有重复的数据，此时以数据id来作为key就不太合适，应该要用数据id + 列表类型作为key），会造成不渲染新数据情况。列表页目前的处理办法是将key值换成id + listType。\r\n\r\n![4](https://cloud.githubusercontent.com/assets/3348398/15650651/aefcb0ac-26ac-11e6-920a-c429200a8137.png)(列表页两个列表的切换)\r\n\r\n这样写除了保证在父元素那一层知晓数据(key值）不同需要重新渲染之外，也保证了React底层渲染知道这是两组不同的数据。在React源文件里有一个ReactChildReconciler.js主要是写children的渲染逻辑。其中的updateChildren里面有具体如何比较前后children，然后再决定是否要重新渲染。在比较的时候它调用了shouldUpdateReactComponent方法。我们看到它有对key值做比较。在两个列表中有不同的key，在数据相似的情况下，能保证两者切换的时候能重新渲染。\r\n```\r\nfunction shouldUpdateReactComponent(prevElement, nextElement) {\r\n  var prevEmpty = prevElement === null || prevElement === false;\r\n  var nextEmpty = nextElement === null || nextElement === false;\r\n  if (prevEmpty || nextEmpty) {\r\n    return prevEmpty === nextEmpty;\r\n  }\r\n\r\n  var prevType = typeof prevElement;\r\n  var nextType = typeof nextElement;\r\n  if (prevType === 'string' || prevType === 'number') {\r\n    return nextType === 'string' || nextType === 'number';\r\n  } else {\r\n    return nextType === 'object' && prevElement.type === nextElement.type && prevElement.key === nextElement.key;\r\n  }\r\n}\r\n```\r\n\r\n#### Immutablejs太大了\r\n上文也提到Immutablejs编译后的包也有50kb。对于PC端来说可能无所谓，网速足够快，但对于移动端来说压力就大了。有人写了个[seamless-immutable](https://github.com/rtfeldman/seamless-immutable)，算是简易版的Immutablejs，只有2kb，只支持Object和Array。\r\n\r\n但其实数据比较逻辑写起来也并不难，因此再去review代码的时候，我决定尝试自己写一个，也是这个决定让我发现了更多的奥秘。\r\n\r\n针对React的这个数据比较的深比较deepCompare，要点有2个：\r\n* 尽量使传入的数据扁平化一点\r\n* 比较的时候做一些限制，避免溢出栈\r\n\r\n先上一下列表页的代码，如下图。这里当时是学习了PC家校群的做法，将component作为props传入。这里的```<Scroll>```封装的是滚动检测的逻辑，而```<List>```则是列表页的渲染，```<Empty>```是列表为空的时候展示的内容，```<Loading>```是列表底部加载的显示横条。\r\n\r\n![5](https://cloud.githubusercontent.com/assets/3348398/15650834/f629ef0c-26ad-11e6-8aae-6826ed95134f.png)\r\n\r\n针对deepCompare的第1个要点，扁平化数据，我们很明显就能定位出其中一个问题了。例如```<Empty>```，我们传入了props.hw，这个props包括了两个列表的数据。但这样的结构就会是这样\r\n```\r\nprops.hw = {\r\n    listMine: [\r\n        {...}, {...}, ...\r\n    ],\r\n    listAll: [\r\n        {...}, {...}, ...\r\n    ],\r\n}\r\n```\r\n但如果我们提前在传入之前判断当前在哪个列表，然后传入对应列表的数量，则会像这样：\r\nprops.hw = 20;\r\n\r\n两者比较起来，显示是后者简单得多。\r\n\r\n针对deepCompare第2点，限制比较的条件。首先让我们想到的是比较的深度。一般而言，对于Object和Array数据，我们都需要递归去进行比较，出于性能的考虑，我们都会限制比较的深度。\r\n\r\n除此之外，我们回顾一下上面的代码，我们将几个React component作为props传进去了，这会在shouldComponentUpdate里面显示出来。这些component的结构大概如下：\r\n\r\n![6](https://cloud.githubusercontent.com/assets/3348398/15651086/d9a3e6ce-26af-11e6-84b8-724b7c35a7e4.png)\r\n\r\n```\r\n$$typeof // 类型\r\n_owner // 父组件\r\n_self: // 仅开发模式出现\r\n_source: //  仅开发模式出现\r\n_store //  仅开发模式出现\r\nkey // 组件的key属性值\r\nprops // 从传入的props\r\nref // 组件的ref属性值\r\ntype 本组件ReactComponent\r\n```\r\n\r\n因此，针对component的比较，有一些是可以忽略的，例如```$$typeof, _store, _self, _source, _owner```。```type```这个比较复杂，可以比较，但仅限于我们定好的比较深度。如果不做这些忽略，这个深比较将会比较消耗性能。关于这个deepCompare的代码，我放在了[pure-render-deepCompare-decorator](https://github.com/lcxfs1991/pure-render-deepCompare-decorator)。\r\n\r\n不过其实，将component当作props传入更为灵活，而且能够增加组件的复用性，但从上面看来，是比较消耗性能的。看了官方文档之后，我们尝试换种写法，主要就是采用```<Scroll>```包裹```<List>```的做法，然后用this.props.children在```<Scroll>```里面渲染，并将```<Empty>```, ```<Loading>```抽出来。\r\n\r\n![7](https://cloud.githubusercontent.com/assets/3348398/15651574/0a952a24-26b3-11e6-9b35-b6dc3f2407ee.png)\r\n\r\n![8](https://cloud.githubusercontent.com/assets/3348398/15651568/06274ff8-26b3-11e6-9310-26f7d71cb67e.png)\r\n\r\n本以为React可能会对children这个props有什么特殊处理，但它依然是将children当作props，传和shouldComponentUpdate，这就迫使父元素```<Scroll>```要去判断是否要重新渲染，进而跳到子无素```<List>```再去判断是否要进入步判断。\r\n\r\n那```<Scroll>```究竟要不要去做这重判断呢？针对列表页这种情况，我们觉得可以暂时不做，由于```<Scroll>```包裹的元素不多，```<Scroll>```可以先重复渲染，然后再交由子元素```<List>```自己再去判断。这样我们对[pure-render-deepCompare-decorator](https://github.com/lcxfs1991/pure-render-deepCompare-decorator)要进行一些修改，当轮到props.children判断的时候，我们要求父元素直接重新渲染，这样就能交给子元素去做下一步的处理。\r\n\r\n如果```<Scroll>```包裹的只有```<List>```还好，如果还有像```<Empty>```, ```<Loading>```甚至其它更多的子元素，那```<Scroll>```重新渲染会触发其它子元素去运算，判断自己是否要做重新渲染，这就造成了浪费。react的官方论坛上已经有人提出，React的将父子元素的重复渲染的决策都放在shouldComponentUpdate，可能导致了耦合[Shouldcomponentupdate And Children](https://discuss.reactjs.org/t/shouldcomponentupdate-and-children/2055)。\r\n\r\n\r\n\r\n## 性能优化小Tips\r\n这里归纳了一些其它性能优化的小Tips\r\n\r\n### 请慎用setState，因其容易导致重新渲染\r\n既然将数据主要交给了Redux来管理，那就尽量使用Redux管理你的数据和状态state，除了少数情况外，别忘了shouldComponentUpdate也需要比较state。\r\n\r\n### 请将方法的bind一律置于constructor\r\nComponent的render里不动态bind方法，方法都在constructor里bind好，如果要动态传参，方法可使用闭包返回一个最终可执行函数。如：showDelBtn(item) { return (e) => {}; }。如果每次都在render里面的jsx去bind这个方法，每次都要绑定会消耗性能。\r\n\r\n\r\n### 请只传递component需要的props\r\n传得太多，或者层次传得太深，都会加重shouldComponentUpdate里面的数据比较负担，因此，也请慎用spread attributes（<Component {...props} />）。\r\n\r\n### 请尽量使用const element\r\n这个用法是工业聚在React讨论微信群里教会的，我们可以将不怎么变动，或者不需要传入状态的component写成const element的形式，这样能加快这个element的初始渲染速度。\r\n\r\n\r\n\r\n## 路由控制与拆包\r\n当项目变得更大规模与复杂的时候，我们需要设计成SPA，这时路由管理就非常重要了，这使特定url参数能够对应一个页面。\r\n\r\n![9](https://cloud.githubusercontent.com/assets/3348398/15652361/41e198b4-26b8-11e6-8e11-6a82e6878c81.png)\r\n\r\nPC家校群整个设计是一个中型的SPA，当js bundle太大的时候，需要拆分成几个小的bundle，进行异步加载。这时可以用到webpack的异步加载打包功能，require。\r\n\r\n![10](https://cloud.githubusercontent.com/assets/3348398/15652396/7cceff34-26b8-11e6-886f-755d7fe6d1cc.png)\r\n\r\n在重构手Q家校群布置页的时候，我们有不少的浮层，列表有布置页内容主浮层、同步到多群浮层、科目管理浮层以及指定群成员浮层。这些完全可以使用react-router进行管理。但是由于当时一早使用了Immutablejs，js bundle已经比较大，我们就不打算使用react-router了。但后面仍然发现包比重构前要大一些，因此为了保证首屏时间不慢于重构前，我们希望在不用react-router的情况下进行分包，其实也并不难，如下面2幅图：\r\n\r\n![12](https://cloud.githubusercontent.com/assets/3348398/15652452/e5c08490-26b8-11e6-9210-83128fea8064.png)\r\n\r\n![11](https://cloud.githubusercontent.com/assets/3348398/15652444/d628272c-26b8-11e6-8524-4fa8321aa9ce.png)\r\n\r\n首先在切换浮层方法里面，使用require.ensure，指定要加载哪个包。\r\n在setComponent方法里，将component存在state里面。\r\n在父元素的渲染方法里，当state有值的时候，就会自动渲染加载回来的component。\r\n\r\n\r\n\r\n## 性能数据\r\n### 首屏可交互时间\r\n目前只有列表页发布外网了，我们比较了优化前后的首屏可交互时间，分别有18%和5.3%的提升。\r\n\r\n![13](https://cloud.githubusercontent.com/assets/3348398/15652517/4c911874-26b9-11e6-8316-aa45bbe254c8.png)\r\n\r\n![14](https://cloud.githubusercontent.com/assets/3348398/15652518/4c986bba-26b9-11e6-8f03-c0eb0870c736.png)\r\n\r\n### FPS时间\r\n稍后补上\r\n\r\n## React性能优化军规\r\n我们在开发的过程中，将上面所论述的内容，总结成一个基本的军规，铭记于心，就可以保证React应用的性能不至于太差。\r\n\r\n### 渲染相关\r\n* 提升级项目性能，请使用immutable(props、state、store)\r\n* 请pure-render-decorator与immutablejs搭配使用\r\n* 请慎用setState，因其容易导致重新渲染\r\n* 谨慎将component当作props传入\r\n* 请将方法的bind一律置于constructor\r\n* 请只传递component需要的props，避免其它props变化导致重新渲染（慎用spread attributes）\r\n* 请在你希望发生重新渲染的dom上设置可被react识别的同级唯一key，否则react在某些情况可能不会重新渲染。\r\n* 请尽量使用const element\r\n\r\n### tap事件\r\n* 简单的tap事件，请使用react-tap-event-plugin\r\n开发环境时，最好引入webpack的环境变量（仅在开发环境中初始化），在container中初始化。生产环境的时候，请将plugin跟react打包到一起（需要打包在一起才能正常使用，因为plugin对react有好多依赖），外链引入。\r\n\r\n目前参考了这个项目的打包方案：\r\nhttps://github.com/hartmamt/react-with-tap-events\r\nFacebook官方issue: https://github.com/facebook/react/blob/bef45b0b1a98ea9b472ba664d955a039cf2f8068/src/renderers/dom/client/eventPlugins/TapEventPlugin.js\r\nReact-tap-event-plugin github:\r\nhttps://github.com/zilverline/react-tap-event-plugin\r\n\r\n* 复杂的tap事件，建议使用tap component\r\n家校群列表页的每个作业的tap交互都比较复杂，出了普通的tap之外，还需要long tap和swipe。因此我们只好自己封装了一个tap component\r\n\r\n### Debug相关\r\n* 移动端请慎用redux-devtools，易造成卡顿\r\n* Webpack慎用devtools的inline-source-map模式\r\n使用此模式会内联一大段便于定位bug的字符串，查错时可以开启，不是查错时建议关闭，否则开发时加载的包会非常大。\r\n\r\n### 其它\r\n* 慎用太新的es6语法。\r\nObject.assign等较新的类库避免在移动端上使用，会报错。\r\nObject.assign目前使用object-assign包\r\n\r\n如有错误，请斧正！"},"comment":{"url":"https://api.github.com/repos/lcxfs1991/blog/issues/comments/222870727","html_url":"https://github.com/lcxfs1991/blog/issues/8#issuecomment-222870727","issue_url":"https://api.github.com/repos/lcxfs1991/blog/issues/8","id":222870727,"user":{"login":"stonewl897","id":4489775,"avatar_url":"https://avatars.githubusercontent.com/u/4489775?v=3","gravatar_id":"","url":"https://api.github.com/users/stonewl897","html_url":"https://github.com/stonewl897","followers_url":"https://api.github.com/users/stonewl897/followers","following_url":"https://api.github.com/users/stonewl897/following{/other_user}","gists_url":"https://api.github.com/users/stonewl897/gists{/gist_id}","starred_url":"https://api.github.com/users/stonewl897/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stonewl897/subscriptions","organizations_url":"https://api.github.com/users/stonewl897/orgs","repos_url":"https://api.github.com/users/stonewl897/repos","events_url":"https://api.github.com/users/stonewl897/events{/privacy}","received_events_url":"https://api.github.com/users/stonewl897/received_events","type":"User","site_admin":false},"created_at":"2016-06-01T01:45:11Z","updated_at":"2016-06-01T01:45:11Z","body":"赞"}}
{"actor":{"display_login":"stonewl897"}}