-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathm2mbob.ghost.2016-07-25.json
1 lines (1 loc) · 246 KB
/
m2mbob.ghost.2016-07-25.json
1
{"db":[{"meta":{"exported_on":1469438308919,"version":"004"},"data":{"posts":[{"id":1,"uuid":"7773c41c-7513-46d6-a5eb-a1256d0f73a2","title":"Welcome to Ghost","slug":"welcome-to-ghost","markdown":"You're live! Nice. We've put together a little post to introduce you to the Ghost editor and get you started. You can manage your content by signing in to the admin area at `<your blog URL>/ghost/`. When you arrive, you can select this post from a list on the left and see a preview of it on the right. Click the little pencil icon at the top of the preview to edit this post and read the next section!\n\n## Getting Started\n\nGhost uses something called Markdown for writing. Essentially, it's a shorthand way to manage your post formatting as you write!\n\nWriting in Markdown is really easy. In the left hand panel of Ghost, you simply write as you normally would. Where appropriate, you can use *shortcuts* to **style** your content. For example, a list:\n\n* Item number one\n* Item number two\n * A nested item\n* A final item\n\nor with numbers!\n\n1. Remember to buy some milk\n2. Drink the milk\n3. Tweet that I remembered to buy the milk, and drank it\n\n### Links\n\nWant to link to a source? No problem. If you paste in a URL, like http://ghost.org - it'll automatically be linked up. But if you want to customise your anchor text, you can do that too! Here's a link to [the Ghost website](http://ghost.org). Neat.\n\n### What about Images?\n\nImages work too! Already know the URL of the image you want to include in your article? Simply paste it in like this to make it show up:\n\n![The Ghost Logo](https://ghost.org/images/ghost.png)\n\nNot sure which image you want to use yet? That's ok too. Leave yourself a descriptive placeholder and keep writing. Come back later and drag and drop the image in to upload:\n\n![A bowl of bananas]\n\n\n### Quoting\n\nSometimes a link isn't enough, you want to quote someone on what they've said. Perhaps you've started using a new blogging platform and feel the sudden urge to share their slogan? A quote might be just the way to do it!\n\n> Ghost - Just a blogging platform\n\n### Working with Code\n\nGot a streak of geek? We've got you covered there, too. You can write inline `<code>` blocks really easily with back ticks. Want to show off something more comprehensive? 4 spaces of indentation gets you there.\n\n .awesome-thing {\n display: block;\n width: 100%;\n }\n\n### Ready for a Break? \n\nThrow 3 or more dashes down on any new line and you've got yourself a fancy new divider. Aw yeah.\n\n---\n\n### Advanced Usage\n\nThere's one fantastic secret about Markdown. If you want, you can write plain old HTML and it'll still work! Very flexible.\n\n<input type=\"text\" placeholder=\"I'm an input field!\" />\n\nThat should be enough to get you started. Have fun - and let us know what you think :)","html":"<p>You're live! Nice. We've put together a little post to introduce you to the Ghost editor and get you started. You can manage your content by signing in to the admin area at <code><your blog URL>/ghost/</code>. When you arrive, you can select this post from a list on the left and see a preview of it on the right. Click the little pencil icon at the top of the preview to edit this post and read the next section!</p>\n\n<h2 id=\"gettingstarted\">Getting Started</h2>\n\n<p>Ghost uses something called Markdown for writing. Essentially, it's a shorthand way to manage your post formatting as you write!</p>\n\n<p>Writing in Markdown is really easy. In the left hand panel of Ghost, you simply write as you normally would. Where appropriate, you can use <em>shortcuts</em> to <strong>style</strong> your content. For example, a list:</p>\n\n<ul>\n<li>Item number one</li>\n<li>Item number two\n<ul><li>A nested item</li></ul></li>\n<li>A final item</li>\n</ul>\n\n<p>or with numbers!</p>\n\n<ol>\n<li>Remember to buy some milk </li>\n<li>Drink the milk </li>\n<li>Tweet that I remembered to buy the milk, and drank it</li>\n</ol>\n\n<h3 id=\"links\">Links</h3>\n\n<p>Want to link to a source? No problem. If you paste in a URL, like <a href=\"http://ghost.org\">http://ghost.org</a> - it'll automatically be linked up. But if you want to customise your anchor text, you can do that too! Here's a link to <a href=\"http://ghost.org\">the Ghost website</a>. Neat.</p>\n\n<h3 id=\"whataboutimages\">What about Images?</h3>\n\n<p>Images work too! Already know the URL of the image you want to include in your article? Simply paste it in like this to make it show up:</p>\n\n<p><img src=\"https://ghost.org/images/ghost.png\" alt=\"The Ghost Logo\" /></p>\n\n<p>Not sure which image you want to use yet? That's ok too. Leave yourself a descriptive placeholder and keep writing. Come back later and drag and drop the image in to upload:</p>\n\n<h3 id=\"quoting\">Quoting</h3>\n\n<p>Sometimes a link isn't enough, you want to quote someone on what they've said. Perhaps you've started using a new blogging platform and feel the sudden urge to share their slogan? A quote might be just the way to do it!</p>\n\n<blockquote>\n <p>Ghost - Just a blogging platform</p>\n</blockquote>\n\n<h3 id=\"workingwithcode\">Working with Code</h3>\n\n<p>Got a streak of geek? We've got you covered there, too. You can write inline <code><code></code> blocks really easily with back ticks. Want to show off something more comprehensive? 4 spaces of indentation gets you there.</p>\n\n<pre><code>.awesome-thing {\n display: block;\n width: 100%;\n}\n</code></pre>\n\n<h3 id=\"readyforabreak\">Ready for a Break?</h3>\n\n<p>Throw 3 or more dashes down on any new line and you've got yourself a fancy new divider. Aw yeah.</p>\n\n<hr />\n\n<h3 id=\"advancedusage\">Advanced Usage</h3>\n\n<p>There's one fantastic secret about Markdown. If you want, you can write plain old HTML and it'll still work! Very flexible.</p>\n\n<p><input type=\"text\" placeholder=\"I'm an input field!\" /></p>\n\n<p>That should be enough to get you started. Have fun - and let us know what you think :)</p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463567128012,"created_by":1,"updated_at":1463567128012,"updated_by":1,"published_at":1463567128044,"published_by":1},{"id":2,"uuid":"9a9d44c4-cfa6-45eb-85aa-0ad8e81d4389","title":"redux源码分析系列之compose.js","slug":"compose-js","markdown":"## compose.js\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=27566686&auto=1&height=66\"></iframe>\n\n听宝宝的话要早点睡,所以就先写最简单的一个哈😳!废话不多说先贴源码:\n\n```\nexport default function compose(...funcs) {\n if (funcs.length === 0) {\n return arg => arg\n } else {\n const last = funcs[funcs.length - 1]\n const rest = funcs.slice(0, -1)\n return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))\n }\n}\n```\n\n先来看看代码的意思,如果没有传入函数,则返回了一个接受参数并原样返回的函数;如果传入的函数数组长度大于0,则取出右边的函数为`last`,其余的为`rest`,然后返回一个函数。这个函数用`last(...args)`为高阶函数`reduceRight`迭代的初始值进行迭代,每次迭代的结果将被传入下一个函数充当参数。\n> 注:`reduceRight`为es5引入的方法,使用形式如下Array.reduceRight((prev,current,index,array) => (...), initialValue),它会为数组中的元素从右向左执行回调方法。\n\n举例来说,有三个函数`a,b,c`,参数为args,`f=compose(a,b,c)`,`f(...args)`执行的效果就和`a(b(c(...args)))`一样,目的就是将多个函数组合起来。在redux中像`middleware`、`reducer`等多处使用到了compose。不早了,今天先到这哈😀!\n\n","html":"<h2 id=\"composejs\">compose.js</h2>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=27566686&auto=1&height=66\"></iframe>\n\n<p>听宝宝的话要早点睡,所以就先写最简单的一个哈😳!废话不多说先贴源码:</p>\n\n<pre><code>export default function compose(...funcs) { \n if (funcs.length === 0) {\n return arg => arg\n } else {\n const last = funcs[funcs.length - 1]\n const rest = funcs.slice(0, -1)\n return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))\n }\n}\n</code></pre>\n\n<p>先来看看代码的意思,如果没有传入函数,则返回了一个接受参数并原样返回的函数;如果传入的函数数组长度大于0,则取出右边的函数为<code>last</code>,其余的为<code>rest</code>,然后返回一个函数。这个函数用<code>last(...args)</code>为高阶函数<code>reduceRight</code>迭代的初始值进行迭代,每次迭代的结果将被传入下一个函数充当参数。</p>\n\n<blockquote>\n <p>注:<code>reduceRight</code>为es5引入的方法,使用形式如下Array.reduceRight((prev,current,index,array) => (...), initialValue),它会为数组中的元素从右向左执行回调方法。</p>\n</blockquote>\n\n<p>举例来说,有三个函数<code>a,b,c</code>,参数为args,<code>f=compose(a,b,c)</code>,<code>f(...args)</code>执行的效果就和<code>a(b(c(...args)))</code>一样,目的就是将多个函数组合起来。在redux中像<code>middleware</code>、<code>reducer</code>等多处使用到了compose。不早了,今天先到这哈😀!</p>","image":"/content/images/2016/05/687474703a2f2f692e696d6775722e636f6d2f4a65567164514d2e706e67-2.png","featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463567445619,"created_by":1,"updated_at":1463802780875,"updated_by":1,"published_at":1463755965662,"published_by":1},{"id":3,"uuid":"4cfddac0-e27b-478d-a53e-21edf2583e73","title":"mac木有随意行的解决方案——实践篇","slug":"macmu-you-sui-yi-xing-de-jie-jue-fang-an-shi-jian-pian","markdown":"由于把旧的电脑带回家了,宝宝再也不能够在床上玩电脑了,可恶的是还要配置mac的网络。万恶的移动没有提供mac版的随意行,因此需要自己设置vpn,可怕!宝宝慌了,还好凭借宝宝的聪明脑瓜,把网络配置好了,先拿出来分享。至于其中原理,宝宝还要研究研究,下次分享哈!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=26590186&auto=1&height=66\"></iframe>\n\n#### 第一步 买一个端口转换器\n\n> 注:附上我的购买链接[点我点我。](https://detail.tmall.com/item.htm?_u=l22095huca16&id=524667361946)不过这个上网网速还不如实验室2M小水管,建议大家上京东买正版,可恶的奎君周不早说😒!\n\n#### 第二步 连接网线、端口转换器、usb端口\n\n> 此时会自动产生如下图选中的Apple USB Ethernet Adapter。\n\n![](http://i2.buimg.com/2ccebf51fda84a4f.png)\n\n#### 第三步 选择此处的创建PPPoE服务\n\n![](http://i2.buimg.com/fbd4a285336f5a32.png)\n\n#### 第四步 设置PPPoE服务相关参数,也就是宽带连接的用户名密码\n\n![](http://i2.buimg.com/0439e1aa3491c16a.png)\n\n#### 第五步 创建vpn,选择IPSec上的L2TP\n\n![](http://ww2.sinaimg.cn/large/74311666jw1f43bdxaykmj21140va44u.jpg)\n\n### 第六步 设置vpn相关参数,服务器地址和账户名,密码,并把高级设置里的通过VPN发送所有流量钩上\n\n![](http://i2.buimg.com/313dab572cd9de9c.png)\n\n### 第七步 增加 /etc/ppp/options 配置文件,内容如下图\n\n![](http://i4.buimg.com/09da1eb1c2bff5ff.png)\n\n![](http://i4.buimg.com/cb065aab4219b762.png)\n\n","html":"<p>由于把旧的电脑带回家了,宝宝再也不能够在床上玩电脑了,可恶的是还要配置mac的网络。万恶的移动没有提供mac版的随意行,因此需要自己设置vpn,可怕!宝宝慌了,还好凭借宝宝的聪明脑瓜,把网络配置好了,先拿出来分享。至于其中原理,宝宝还要研究研究,下次分享哈!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=26590186&auto=1&height=66\"></iframe>\n\n<h4 id=\"\">第一步 买一个端口转换器</h4>\n\n<blockquote>\n <p>注:附上我的购买链接<a href=\"https://detail.tmall.com/item.htm?_u=l22095huca16&id=524667361946\">点我点我。</a>不过这个上网网速还不如实验室2M小水管,建议大家上京东买正版,可恶的奎君周不早说😒!</p>\n</blockquote>\n\n<h4 id=\"usb\">第二步 连接网线、端口转换器、usb端口</h4>\n\n<blockquote>\n <p>此时会自动产生如下图选中的Apple USB Ethernet Adapter。</p>\n</blockquote>\n\n<p><img src=\"http://i2.buimg.com/2ccebf51fda84a4f.png\" alt=\"\" /></p>\n\n<h4 id=\"pppoe\">第三步 选择此处的创建PPPoE服务</h4>\n\n<p><img src=\"http://i2.buimg.com/fbd4a285336f5a32.png\" alt=\"\" /></p>\n\n<h4 id=\"pppoe\">第四步 设置PPPoE服务相关参数,也就是宽带连接的用户名密码</h4>\n\n<p><img src=\"http://i2.buimg.com/0439e1aa3491c16a.png\" alt=\"\" /></p>\n\n<h4 id=\"vpnipsecl2tp\">第五步 创建vpn,选择IPSec上的L2TP</h4>\n\n<p><img src=\"http://ww2.sinaimg.cn/large/74311666jw1f43bdxaykmj21140va44u.jpg\" alt=\"\" /></p>\n\n<h3 id=\"vpnvpn\">第六步 设置vpn相关参数,服务器地址和账户名,密码,并把高级设置里的通过VPN发送所有流量钩上</h3>\n\n<p><img src=\"http://i2.buimg.com/313dab572cd9de9c.png\" alt=\"\" /></p>\n\n<h3 id=\"etcpppoptions\">第七步 增加 /etc/ppp/options 配置文件,内容如下图</h3>\n\n<p><img src=\"http://i4.buimg.com/09da1eb1c2bff5ff.png\" alt=\"\" /></p>\n\n<p><img src=\"http://i4.buimg.com/cb065aab4219b762.png\" alt=\"\" /></p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463834942922,"created_by":1,"updated_at":1463838155173,"updated_by":1,"published_at":1463837040160,"published_by":1},{"id":4,"uuid":"52d5cb6e-e34b-46a8-a91f-adda39d7e944","title":"mac安装mongodb","slug":"macan-zhuang-mongodb","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=41665696&auto=1&height=66\"></iframe>\n\n#### 安装Homebrew\n Homebrew是Mac OSX下一个包依赖管理工具,用它来安装软件非常的方便只需要brew install 软件名这一条命令就可以将你所需要的软件安装好,不用再操心安装过程中软件的依赖问题,这些问题Homebrew统统帮你搞定。Homebrew安装方法也很简单:打开终端,然后输入命令\n \n<!--bash-->\n\truby -e \"$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)\"\n\n#### 安装mongodb\n 如果是之前就已经安装好的Homebrew,那先要更新一下库:\n \n<!--bash-->\n\tbrew update\n 然后执行下面命令安装mongodb:\n \n<!--bash-->\n\tbrew install mongodb\n --with-openssl参数表示通过ssl源安装,--devel表示安装最新开发版。此时通过mongod --config /usr/local/etc/mongodb.conf命令已经可以启动。(3.0.7版本是如此。)\n\n#### 遇到的几个问题\n1.\t我第一次安装完以后,装了Robomongo,但是在连接时发现了bug,一直Authorization skip by you的错误。然后改dbpath、logpath等等操作,都不奏效,就把mongodb玩坏了。最后google得到Robomongo还不支持mongodb3,解决方案在此[http://http://liyanjie918.blog.163.com/blog/static/2022729020156261410274/](http://http://liyanjie918.blog.163.com/blog/static/2022729020156261410274/ \"使用Robomongo 连接MongoDB 3.x 报 Authorization failed 解决办法\")\n2.\t可能还会遇到找不到/data/db路径,这是mongdb在之前版本时默认的dbpath,如果安装的是2.*的版本可能就会遇到这个问题,就需要:mkdir -p /data/db\n另外,最后还会有一个权限问题,用sudo chmod R 当前用户名 /data 命令能够解决。\n3.\t大致问题如上,出现这些问题主要还是对mac系统文件和权限等不熟,已经太久没有动mongo,所以也有点生疏了。","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=41665696&auto=1&height=66\"></iframe>\n\n<h4 id=\"homebrew\">安装Homebrew</h4>\n\n Homebrew是Mac OSX下一个包依赖管理工具,用它来安装软件非常的方便只需要brew install 软件名这一条命令就可以将你所需要的软件安装好,不用再操心安装过程中软件的依赖问题,这些问题Homebrew统统帮你搞定。Homebrew安装方法也很简单:打开终端,然后输入命令\n \n<!--bash--> \n\n<pre><code>ruby -e \"$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)\"\n</code></pre>\n\n<h4 id=\"mongodb\">安装mongodb</h4>\n\n 如果是之前就已经安装好的Homebrew,那先要更新一下库:\n \n<!--bash--> \n\n<pre><code>brew update\n</code></pre>\n\n 然后执行下面命令安装mongodb:\n \n<!--bash--> \n\n<pre><code>brew install mongodb\n</code></pre>\n\n<p> --with-openssl参数表示通过ssl源安装,--devel表示安装最新开发版。此时通过mongod --config /usr/local/etc/mongodb.conf命令已经可以启动。(3.0.7版本是如此。)</p>\n\n<h4 id=\"\">遇到的几个问题</h4>\n\n<ol>\n<li>我第一次安装完以后,装了Robomongo,但是在连接时发现了bug,一直Authorization skip by you的错误。然后改dbpath、logpath等等操作,都不奏效,就把mongodb玩坏了。最后google得到Robomongo还不支持mongodb3,解决方案在此<a href=\"http://http://liyanjie918.blog.163.com/blog/static/2022729020156261410274/\" title=\"使用Robomongo 连接MongoDB 3.x 报 Authorization failed 解决办法\">http://http://liyanjie918.blog.163.com/blog/static/2022729020156261410274/</a> </li>\n<li>可能还会遇到找不到/data/db路径,这是mongdb在之前版本时默认的dbpath,如果安装的是2.*的版本可能就会遇到这个问题,就需要:mkdir -p /data/db <br />\n另外,最后还会有一个权限问题,用sudo chmod R 当前用户名 /data 命令能够解决。</li>\n<li>大致问题如上,出现这些问题主要还是对mac系统文件和权限等不熟,已经太久没有动mongo,所以也有点生疏了。</li>\n</ol>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463838003388,"created_by":1,"updated_at":1463838618095,"updated_by":1,"published_at":1463838208563,"published_by":1},{"id":5,"uuid":"2b51ebd5-cda4-4792-b84e-5874f72d42a2","title":"koa1源码分析之Application","slug":"koa1yuan-ma-fen-xi-zhi-application","markdown":"##Application\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=32507038&auto=1&height=66\"></iframe>\n\n原来写的文章,迁移过来,还是koa1的,koa2也出了,待更新。\n\n###依赖\n\n- debug:debug源码分析;\n- compose_es7:与co.wrap相似,不过能够接受 async/await 函数;\n- onFinished:捕捉finish或error事件,并根据第二个参数执行回调;\n- compose:compose_es7的简化版,只接受generator;\n- isJSON:顾名思义,判断传入参数是否为JSON;\n- statuses:将http码分为三类,redirect、empty以及retry,然后根据传入的http来判断属于哪一类;\n- accepts:根据req的内容判断(content negotiation)字段,如accept,Accept-Encoding,Accept-Language等字段对这些字段进行格式化,方便后续处理;\n- only:只返回在传入参数里所包含的字段;\n- co:co源码解析\n\n###构造器源码\n\n<!--javascript-->\n\t/**\n\t * Initialize a new `Application`.\n\t *\n\t * @api public\n\t */\n\tfunction Application() {\n\t if (!(this instanceof Application)) return new Application;\n\t this.env = process.env.NODE_ENV || 'development';\n\t this.subdomainOffset = 2;\n\t this.middleware = [];\n\t this.proxy = false;\n\t this.context = Object.create(context);\n\t this.request = Object.create(request);\n\t this.response = Object.create(response);\n\t}\n\n上面koa的Application的构造函数,可以看到在创建Application时有初始化如下属性:\n\n1. env表示是开发环境还是生产环境,通过process.env.NODE_ENV读取环境变量中的参数设置,如果没有设置默认为开发环境\n2. subdomainOffset,表示子域名的偏移,默认为2\n3. proxy如果为true,则解析 \"Host\" 的 header 域,并支持X-Forwarded-Host,默认为false。\n4. context上下文\n5. request请求对象\n6. response响应对象\n\n#### 有哪些方法\n\n#### app.listen\n\n<!--javascript-->\n\t/**\n\t * Shorthand for:\n\t *\n\t * http.createServer(app.callback()).listen(...)\n\t *\n\t * @param {Mixed} ...\n\t * @return {Server}\n\t * @api public\n\t */\n\tapp.listen = function(){\n\t debug('listen');\n\t var server = http.createServer(this.callback());\n\t return server.listen.apply(server, arguments);\n\t};\n\n 这个方法调用了node原生的http模块创建了一个server,要关注的是传入的这个callback。\n\n\n\n#### app.callback\n<!--javascript-->\n\n\t/**\t\n\t * Return a request handler callback\n\t * for node's native http server.\n\t *\n\t * @return {Function}\n\t * @api public\n\t */\n\tapp.callback = function(){\n\t var fn = this.experimental\n\t ? compose_es7(this.middleware)\n\t : co.wrap(compose(this.middleware));\n\t var self = this;\n\t if (!this.listeners('error').length) this.on('error', this.onerror);\n\t return function(req, res){\n\t res.statusCode = 404;\n\t var ctx = self.createContext(req, res);\n\t onFinished(res, ctx.onerror);\n\t fn.call(ctx).then(function () {\n\t respond.call(ctx);\n\t }).catch(ctx.onerror);\n\t }\n\t};\n\n 返回一个适合 http.createServer() 方法的回调函数用来处理请求。 您也可以使用这个回调函数将您的app挂载在 Connect/Express 应用上。一点点看下来,this.middleware是中间件的一个数组,用co.wrap或者compose_es7进行包装,co是tj大神的一个用来包装执行generator函数的库,而compose_es7应该是es7可能会引入的相似功能的特性,不同的是后者支持es7的async/await。后面一句处理错误,最后是返回一个请求处理函数。在函数中会调用createContext创建上下文,用onFinished模块捕捉finish或error事件,并根据第二个参数执行回调。然后调用co包装返回的promise对象进行中间件的调用,调用就由co来进行。调用成功后执行调用respond函数。\n\n#### 最后就来看看respond函数\n\n<!--javascript-->\n\n\t/**\t\n\t * Response helper.\n\t */\n\tfunction respond() {\n\t // allow bypassing koa\n\t if (false === this.respond) return;\n\t var res = this.res;\n\t if (res.headersSent || !this.writable) return;\n\t var body = this.body;\n\t var code = this.status;\n\t // ignore body\n\t if (statuses.empty[code]) {\n\t // strip headers\n\t this.body = null;\n\t return res.end();\n\t }\n\t if ('HEAD' == this.method) {\n\t if (isJSON(body)) this.length = Buffer.byteLength(JSON.stringify(body));\n\t return res.end();\n\t }\n\t // status body\n\t if (null == body) {\n\t this.type = 'text';\n\t body = this.message || String(code);\n\t this.length = Buffer.byteLength(body);\n\t return res.end(body);\n\t }\n\t // responses\n\t if (Buffer.isBuffer(body)) return res.end(body);\n\t if ('string' == typeof body) return res.end(body);\n\t if (body instanceof Stream) return body.pipe(res);\n\t // body: json\n\t body = JSON.stringify(body);\n\t this.length = Buffer.byteLength(body);\n\t res.end(body);\n\t}\n\n 在1.0.0版本中respond函数第一行就是yield *next,也就是一开始不执行,到所有中间件执行完后对res进行处理。但co进入4.*以后,用promise代替了thunk函数的实现,返回一个promise,因此当前1.1.2版本中的respond函数的调用写在了co返回的promise的第一个回调中。\n\n#### 其它\n\n- app.use向中间件数组中添加中间件\n- app.createContext创建请求上下文\n- app.inspect/toJSON返回配置的参数subdomainOffset、proxy和env。","html":"<h2 id=\"application\">Application</h2>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=32507038&auto=1&height=66\"></iframe>\n\n<p>原来写的文章,迁移过来,还是koa1的,koa2也出了,待更新。</p>\n\n<h3 id=\"\">依赖</h3>\n\n<ul>\n<li>debug:debug源码分析;</li>\n<li>compose_es7:与co.wrap相似,不过能够接受 async/await 函数;</li>\n<li>onFinished:捕捉finish或error事件,并根据第二个参数执行回调;</li>\n<li>compose:compose_es7的简化版,只接受generator;</li>\n<li>isJSON:顾名思义,判断传入参数是否为JSON;</li>\n<li>statuses:将http码分为三类,redirect、empty以及retry,然后根据传入的http来判断属于哪一类;</li>\n<li>accepts:根据req的内容判断(content negotiation)字段,如accept,Accept-Encoding,Accept-Language等字段对这些字段进行格式化,方便后续处理;</li>\n<li>only:只返回在传入参数里所包含的字段;</li>\n<li>co:co源码解析</li>\n</ul>\n\n<h3 id=\"\">构造器源码</h3>\n\n<!--javascript--> \n\n<pre><code>/**\n * Initialize a new `Application`.\n *\n * @api public\n */\nfunction Application() {\n if (!(this instanceof Application)) return new Application;\n this.env = process.env.NODE_ENV || 'development';\n this.subdomainOffset = 2;\n this.middleware = [];\n this.proxy = false;\n this.context = Object.create(context);\n this.request = Object.create(request);\n this.response = Object.create(response);\n}\n</code></pre>\n\n<p>上面koa的Application的构造函数,可以看到在创建Application时有初始化如下属性:</p>\n\n<ol>\n<li>env表示是开发环境还是生产环境,通过process.env.NODE_ENV读取环境变量中的参数设置,如果没有设置默认为开发环境 </li>\n<li>subdomainOffset,表示子域名的偏移,默认为2 </li>\n<li>proxy如果为true,则解析 \"Host\" 的 header 域,并支持X-Forwarded-Host,默认为false。 </li>\n<li>context上下文 </li>\n<li>request请求对象 </li>\n<li>response响应对象</li>\n</ol>\n\n<h4 id=\"\">有哪些方法</h4>\n\n<h4 id=\"applisten\">app.listen</h4>\n\n<!--javascript--> \n\n<pre><code>/**\n * Shorthand for:\n *\n * http.createServer(app.callback()).listen(...)\n *\n * @param {Mixed} ...\n * @return {Server}\n * @api public\n */\napp.listen = function(){\n debug('listen');\n var server = http.createServer(this.callback());\n return server.listen.apply(server, arguments);\n};\n</code></pre>\n\n<p> 这个方法调用了node原生的http模块创建了一个server,要关注的是传入的这个callback。</p>\n\n<h4 id=\"appcallback\">app.callback</h4>\n\n<!--javascript-->\n\n<pre><code>/** \n * Return a request handler callback\n * for node's native http server.\n *\n * @return {Function}\n * @api public\n */\napp.callback = function(){\n var fn = this.experimental\n ? compose_es7(this.middleware)\n : co.wrap(compose(this.middleware));\n var self = this;\n if (!this.listeners('error').length) this.on('error', this.onerror);\n return function(req, res){\n res.statusCode = 404;\n var ctx = self.createContext(req, res);\n onFinished(res, ctx.onerror);\n fn.call(ctx).then(function () {\n respond.call(ctx);\n }).catch(ctx.onerror);\n }\n};\n</code></pre>\n\n<p> 返回一个适合 http.createServer() 方法的回调函数用来处理请求。 您也可以使用这个回调函数将您的app挂载在 Connect/Express 应用上。一点点看下来,this.middleware是中间件的一个数组,用co.wrap或者compose<em>es7进行包装,co是tj大神的一个用来包装执行generator函数的库,而compose</em>es7应该是es7可能会引入的相似功能的特性,不同的是后者支持es7的async/await。后面一句处理错误,最后是返回一个请求处理函数。在函数中会调用createContext创建上下文,用onFinished模块捕捉finish或error事件,并根据第二个参数执行回调。然后调用co包装返回的promise对象进行中间件的调用,调用就由co来进行。调用成功后执行调用respond函数。</p>\n\n<h4 id=\"respond\">最后就来看看respond函数</h4>\n\n<!--javascript-->\n\n<pre><code>/** \n * Response helper.\n */\nfunction respond() {\n // allow bypassing koa\n if (false === this.respond) return;\n var res = this.res;\n if (res.headersSent || !this.writable) return;\n var body = this.body;\n var code = this.status;\n // ignore body\n if (statuses.empty[code]) {\n // strip headers\n this.body = null;\n return res.end();\n }\n if ('HEAD' == this.method) {\n if (isJSON(body)) this.length = Buffer.byteLength(JSON.stringify(body));\n return res.end();\n }\n // status body\n if (null == body) {\n this.type = 'text';\n body = this.message || String(code);\n this.length = Buffer.byteLength(body);\n return res.end(body);\n }\n // responses\n if (Buffer.isBuffer(body)) return res.end(body);\n if ('string' == typeof body) return res.end(body);\n if (body instanceof Stream) return body.pipe(res);\n // body: json\n body = JSON.stringify(body);\n this.length = Buffer.byteLength(body);\n res.end(body);\n}\n</code></pre>\n\n<p> 在1.0.0版本中respond函数第一行就是yield <em>next,也就是一开始不执行,到所有中间件执行完后对res进行处理。但co进入4.</em>以后,用promise代替了thunk函数的实现,返回一个promise,因此当前1.1.2版本中的respond函数的调用写在了co返回的promise的第一个回调中。</p>\n\n<h4 id=\"\">其它</h4>\n\n<ul>\n<li>app.use向中间件数组中添加中间件</li>\n<li>app.createContext创建请求上下文</li>\n<li>app.inspect/toJSON返回配置的参数subdomainOffset、proxy和env。</li>\n</ul>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463838292023,"created_by":1,"updated_at":1463838903875,"updated_by":1,"published_at":1463838479996,"published_by":1},{"id":6,"uuid":"9a4be2b6-a671-4302-99fe-d0a54af86417","title":"Yesterday's","slug":"yesterday","markdown":"T团20周年的开场曲,晚上写作业时又拿出来循环播放,听完又是元气满满的一天呀!歌词很棒,拿出来分享分享!\n看到网易云音乐的评论里,一个考研党在2015年9月24日23点33发了一句“正在考研的我听着每一句都是泪”,到4月13日他又评论了一句“我考上了”,中间没有任何别的回复,但多了4个赞。我看到这莫名地就释然了,点了个赞,继续写作业去了,很奇妙的感受,我叫它感同身受!\n20周年那一版因为版权无法生成外链,只有下面这个版本了,不过也很不错!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=22690729&auto=1&height=66\"></iframe>\n\n風の無い夜の公園で 動かないブランコ達は<br>\n在无风的夜晚公园里 不会摇晃的秋千们<br>\n与此刻的自己 有着可怕的相似<br>\n恐い位似てるんだよ 今の僕自身に<br>\n欲しい物は山ほどある<br>\n想要的东西如山般高<br>\n但手中却什么也没有<br>\nだけど欠片(かけら)も手にできず<br>\n無くしてきた 色んな夢を探してる<br>\n只是搜寻着 那些消逝的梦想<br>\n弱さは人の運命(さだめ)だと<br>\n命运两字是人生的无奈<br>\nなんとなく気付いてるけど<br>\n似乎我也注意到了<br>\n「支えられたい、支えてみたい」忙しく思うよ<br>\n却不停的想着「能撑得,在撑撑看」<br>\n強がる事に不器用で<br>\n就算是徒费力气<br>\n空回りしてもいいから<br>\n硬撑也无所谓<br>\n我只是想不断追寻 那变化多端的未来<br>\n追い続けたい 色んな形の未来を<br>\n觉得容易的事情却意外地<br>\n容易(たやす)く思える事が意外に結構<br>\n无法简单得手<br>\n手に入れるのが難しく思える<br>\n这难道不是跟盯着自己的脚步<br>\nそれってもしかして自分の足元を<br>\n見つめてみればきっと転がってんじゃない<br>\n就会摔跤是一样的道理吗<br>\n激しい雨が降った後にはきれいな花が咲く<br>\n在倾盆大雨过后 娇艳的花儿即将绽放<br>\nあきらめたりしない 届くはずさ想いは<br>\n千万不要放弃 因为梦想即将到达<br>\nそしていっそ昨日までの自分を捨て去ろう<br>\n然后索性忘却到昨天为止的自己吧<br>\n静けさを引き裂くように 空き缶を強く蹴り飛ばす<br>\n仿佛是要将寂静撕裂 狠狠的将罐子踢飞<br>\n胸に秘めた 色んな迷いを詰め込んで<br>\n深藏在内心的 是满满的各种迷茫<br>\n結果だけにしがみついていたよずっと<br>\n一直以来总是只紧抓住结果不放<br>\nプロセスなんか馬鹿らしく思えて<br>\n觉得过程什么的都不重要<br>\n残缺的月 带着勇敢的微笑<br>\n満ち欠ける月のけなげな微笑み<br>\n看吧 太阳一定依旧在你眼前升起<br>\n日はまた昇るきっと目の前にほら<br>\n頬を伝ったどんな涙も大きな価値がある<br>\n脸颊上滑落的 是无价眼泪带来的讯息<br>\n二度とない時を負けないように進むよ<br>\n怀着仅此一次绝不认输的心情前进<br>\n激しい雨が降った後にはきれいな花が咲く<br>\n在大雨倾盆过后 娇艳的花儿即将绽放<br>\nあきらめたりしない 届くはずさ願いは<br>\n千万不要放弃 因为梦想即将到达<br>\nそしていっそ昨日までの自分を捨て去ろう<br>\n然后索性忘却到昨天为止的自己吧<br>\n遠くない近くない捉えずらいホントにいつもやっかいもんは自分<br>\n最难琢磨的是那忽远忽近难以搞定的真实的自己<br>\nそれでも向き合って生きていかなきゃダメさ<br>\n但是不好好奋斗下去是不行的<br>\nだから「昨日までの自分を捨て去ろう」って唄おう<br>\n所以 就让我们唱着「忘却到昨天为止的自己吧」<br>\nいつかはどんな部分(こころ)も愛せる気がするよ<br>\n无论何时 无论何地都感受到爱<br>\n何が起こっても構わない荒波に打たれても<br>\n无论发生任何事 就算是再大的风浪<br>\n僕は信じている最終形の自分を<br>\n我始终相信着最真实的自己<br>\nどれ位こうしてたんだろう<br>\n到底这样过了多久<br>\n街は息を吹き返した<br>\n整个街道重新开始呼吸<br>\n不思議なんだ僕の胸に光がともってく<br>\n不可思议的是我的心也渐渐亮了起来<br>\nそよぎはじめていた風が淋しさを全部連れ去り<br>\n徐徐吹起的微风将一切寂寞带走<br>\n踊りだしたブランコ達も笑ってる<br>\n跳起舞来的秋千们也露出笑意<br>","html":"<p>T团20周年的开场曲,晚上写作业时又拿出来循环播放,听完又是元气满满的一天呀!歌词很棒,拿出来分享分享! <br />\n看到网易云音乐的评论里,一个考研党在2015年9月24日23点33发了一句“正在考研的我听着每一句都是泪”,到4月13日他又评论了一句“我考上了”,中间没有任何别的回复,但多了4个赞。我看到这莫名地就释然了,点了个赞,继续写作业去了,很奇妙的感受,我叫它感同身受!\n20周年那一版因为版权无法生成外链,只有下面这个版本了,不过也很不错!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=22690729&auto=1&height=66\"></iframe>\n\n<p>風の無い夜の公園で 動かないブランコ達は<br>\n在无风的夜晚公园里 不会摇晃的秋千们<br>\n与此刻的自己 有着可怕的相似<br>\n恐い位似てるんだよ 今の僕自身に<br>\n欲しい物は山ほどある<br>\n想要的东西如山般高<br>\n但手中却什么也没有<br>\nだけど欠片(かけら)も手にできず<br>\n無くしてきた 色んな夢を探してる<br>\n只是搜寻着 那些消逝的梦想<br>\n弱さは人の運命(さだめ)だと<br>\n命运两字是人生的无奈<br>\nなんとなく気付いてるけど<br>\n似乎我也注意到了<br>\n「支えられたい、支えてみたい」忙しく思うよ<br>\n却不停的想着「能撑得,在撑撑看」<br>\n強がる事に不器用で<br>\n就算是徒费力气<br>\n空回りしてもいいから<br>\n硬撑也无所谓<br>\n我只是想不断追寻 那变化多端的未来<br>\n追い続けたい 色んな形の未来を<br>\n觉得容易的事情却意外地<br>\n容易(たやす)く思える事が意外に結構<br>\n无法简单得手<br>\n手に入れるのが難しく思える<br>\n这难道不是跟盯着自己的脚步<br>\nそれってもしかして自分の足元を<br>\n見つめてみればきっと転がってんじゃない<br>\n就会摔跤是一样的道理吗<br>\n激しい雨が降った後にはきれいな花が咲く<br>\n在倾盆大雨过后 娇艳的花儿即将绽放<br>\nあきらめたりしない 届くはずさ想いは<br>\n千万不要放弃 因为梦想即将到达<br>\nそしていっそ昨日までの自分を捨て去ろう<br>\n然后索性忘却到昨天为止的自己吧<br>\n静けさを引き裂くように 空き缶を強く蹴り飛ばす<br>\n仿佛是要将寂静撕裂 狠狠的将罐子踢飞<br>\n胸に秘めた 色んな迷いを詰め込んで<br>\n深藏在内心的 是满满的各种迷茫<br>\n結果だけにしがみついていたよずっと<br>\n一直以来总是只紧抓住结果不放<br>\nプロセスなんか馬鹿らしく思えて<br>\n觉得过程什么的都不重要<br>\n残缺的月 带着勇敢的微笑<br>\n満ち欠ける月のけなげな微笑み<br>\n看吧 太阳一定依旧在你眼前升起<br>\n日はまた昇るきっと目の前にほら<br>\n頬を伝ったどんな涙も大きな価値がある<br>\n脸颊上滑落的 是无价眼泪带来的讯息<br>\n二度とない時を負けないように進むよ<br>\n怀着仅此一次绝不认输的心情前进<br>\n激しい雨が降った後にはきれいな花が咲く<br>\n在大雨倾盆过后 娇艳的花儿即将绽放<br>\nあきらめたりしない 届くはずさ願いは<br>\n千万不要放弃 因为梦想即将到达<br>\nそしていっそ昨日までの自分を捨て去ろう<br>\n然后索性忘却到昨天为止的自己吧<br>\n遠くない近くない捉えずらいホントにいつもやっかいもんは自分<br>\n最难琢磨的是那忽远忽近难以搞定的真实的自己<br>\nそれでも向き合って生きていかなきゃダメさ<br>\n但是不好好奋斗下去是不行的<br>\nだから「昨日までの自分を捨て去ろう」って唄おう<br>\n所以 就让我们唱着「忘却到昨天为止的自己吧」<br>\nいつかはどんな部分(こころ)も愛せる気がするよ<br>\n无论何时 无论何地都感受到爱<br>\n何が起こっても構わない荒波に打たれても<br>\n无论发生任何事 就算是再大的风浪<br>\n僕は信じている最終形の自分を<br>\n我始终相信着最真实的自己<br>\nどれ位こうしてたんだろう<br>\n到底这样过了多久<br>\n街は息を吹き返した<br>\n整个街道重新开始呼吸<br>\n不思議なんだ僕の胸に光がともってく<br>\n不可思议的是我的心也渐渐亮了起来<br>\nそよぎはじめていた風が淋しさを全部連れ去り<br>\n徐徐吹起的微风将一切寂寞带走<br>\n踊りだしたブランコ達も笑ってる<br>\n跳起舞来的秋千们也露出笑意<br></p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463918607786,"created_by":1,"updated_at":1463919901297,"updated_by":1,"published_at":1463919661601,"published_by":1},{"id":7,"uuid":"d4620769-971a-4d03-b3f7-dac29ab33ead","title":"浏览器下载Google Play的apk","slug":"liu-lan-qi-xia-zai-google-playde-apk","markdown":"","html":"","image":null,"featured":0,"page":0,"status":"draft","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464107300210,"created_by":1,"updated_at":1467246627779,"updated_by":1,"published_at":null,"published_by":null},{"id":8,"uuid":"b8b11fe7-0536-434c-ab84-10bfa539d770","title":"转:Reactjs 的 PropTypes 使用方法","slug":"zhuan-reactjs-de-proptypes-shi-yong-fang-fa","markdown":"### Reactjs 的 PropTypes 使用方法\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=35528482&auto=1&height=66\"></iframe>\n\npropTypes 使用来规范组件Props的类型\n\n```\n var Test = React.createClass({\n propTypes: {\n // required\n requiredFunc: React.PropTypes.func.isRequired,\n requiredAny: React.PropTypes.any.isRequired,\n\n // primitives, optional by default\n bool: React.PropTypes.bool,\n func: React.PropTypes.func,\n number: React.PropTypes.number,\n string: React.PropTypes.string,\n },\n render:function(){\n return <div/>\n }\n});\n\nvar component = React.render(\n <Test requiredFunc=\"bar\" bool=\"true\" requiredAny=\"a\"/>, \n document.body\n);\n```\n\n若没有按照规范,会显示警告。\n\n```\nReact.PropTypes 的种类\nReact.PropTypes.array // 数组\nReact.PropTypes.bool.isRequired // Boolean 且必要。\nReact.PropTypes.func // 函数\nReact.PropTypes.number // 数字\nReact.PropTypes.object // 对象\nReact.PropTypes.string // 字符串\nReact.PropTypes.any // 任何类型的: numbers, strings, elements等\nReact.PropTypes.element // React 元素\nReact.PropTypes.instanceOf(XXX) // 某种XXX类型的实体\nReact.PropTypes.oneOf(['foo', 'bar']) // 其中一个字符串\nReact.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.array]) // 其中一种类型\nReact.PropTypes.arrayOf(React.PropTypes.string) // 某种类型的数组\nReact.PropTypes.objectOf(React.PropTypes.string) // 某种类型的对象\nReact.PropTypes.shape({ // 是否符合指定的格式\n color: React.PropTypes.string,\n fontSize: React.PropTypes.number\n});\nReact.PropTypes.any.isRequired // 可以是任何格式,且必要。\n\n// 自定义格式(当不符合的时候,会显示Error) \n// 不要用`console.warn` 或者 throw, 因为它在`oneOfType` 的情况下无效。\ncustomPropType: function(props, propName, componentName) {\n if (!/^[0-9]/.test(props[propName])) {\n return new Error('Validation failed!');\n }\n}\n```\n\ngetDefaultProps\n\n当父组件没有提供props的属性时,可以采用getDefaultProps,预设props属性的方式,让元件使用预设的值,确保有props带入。\n\n```\n var ComponentWithDefaultProps = React.createClass({ \n getDefaultProps : function () { \n return {\n value : 'default value' \n }; \n }, \n /* ... */ \n});\n```\n\nPosted by James Yang March 19, 2015 ReactJS","html":"<h3 id=\"reactjsproptypes\">Reactjs 的 PropTypes 使用方法</h3>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=35528482&auto=1&height=66\"></iframe>\n\n<p>propTypes 使用来规范组件Props的类型</p>\n\n<pre><code> var Test = React.createClass({\n propTypes: {\n // required\n requiredFunc: React.PropTypes.func.isRequired,\n requiredAny: React.PropTypes.any.isRequired,\n\n // primitives, optional by default\n bool: React.PropTypes.bool,\n func: React.PropTypes.func,\n number: React.PropTypes.number,\n string: React.PropTypes.string,\n },\n render:function(){\n return <div/>\n }\n});\n\nvar component = React.render( \n <Test requiredFunc=\"bar\" bool=\"true\" requiredAny=\"a\"/>, \n document.body\n);\n</code></pre>\n\n<p>若没有按照规范,会显示警告。</p>\n\n<pre><code>React.PropTypes 的种类 \nReact.PropTypes.array // 数组 \nReact.PropTypes.bool.isRequired // Boolean 且必要。 \nReact.PropTypes.func // 函数 \nReact.PropTypes.number // 数字 \nReact.PropTypes.object // 对象 \nReact.PropTypes.string // 字符串 \nReact.PropTypes.any // 任何类型的: numbers, strings, elements等 \nReact.PropTypes.element // React 元素 \nReact.PropTypes.instanceOf(XXX) // 某种XXX类型的实体 \nReact.PropTypes.oneOf(['foo', 'bar']) // 其中一个字符串 \nReact.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.array]) // 其中一种类型 \nReact.PropTypes.arrayOf(React.PropTypes.string) // 某种类型的数组 \nReact.PropTypes.objectOf(React.PropTypes.string) // 某种类型的对象 \nReact.PropTypes.shape({ // 是否符合指定的格式 \n color: React.PropTypes.string,\n fontSize: React.PropTypes.number\n});\nReact.PropTypes.any.isRequired // 可以是任何格式,且必要。\n\n// 自定义格式(当不符合的时候,会显示Error) \n// 不要用`console.warn` 或者 throw, 因为它在`oneOfType` 的情况下无效。\ncustomPropType: function(props, propName, componentName) { \n if (!/^[0-9]/.test(props[propName])) {\n return new Error('Validation failed!');\n }\n}\n</code></pre>\n\n<p>getDefaultProps</p>\n\n<p>当父组件没有提供props的属性时,可以采用getDefaultProps,预设props属性的方式,让元件使用预设的值,确保有props带入。</p>\n\n<pre><code> var ComponentWithDefaultProps = React.createClass({ \n getDefaultProps : function () { \n return {\n value : 'default value' \n }; \n }, \n /* ... */ \n});\n</code></pre>\n\n<p>Posted by James Yang March 19, 2015 ReactJS</p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464234823225,"created_by":1,"updated_at":1464247927106,"updated_by":1,"published_at":1464247836046,"published_by":1},{"id":9,"uuid":"727848e2-4e25-4943-bb82-5a6f2bba13b5","title":"什么是视差滚动","slug":"shi-yao-shi-shi-chai-gun-dong","markdown":"下午在研究facebook今年f8大会上开源的基于react native的f8app的时候看到一个ParallaxBackground组件,不明觉厉啊!于是查了一下单词Parallax,意思为视差,所以ParallaxBackground其实是一个视差的背景,最后我把目光落到了视差滚动。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=385757&auto=1&height=66\"></iframe>\n\n视差滚动是近些年国外web design中的一种潮流,它能够提高网页的立体性。它是如何运作的呢!上百度百科。\n> 视差效果,原本是一个天文学术语,当我们观察星空时,离我们远的星星移动速度较慢,离我们近的星星移动速度则较快。当我们坐在车上向车窗外 看时,也会有这样的感觉,远处的群山似乎没有在动,而近处的稻田却在飞速掠过。许多游戏中都使用视差效果来增加场景的立体感。说的简单点就是网页内的元素在滚动屏幕时发生的位置的变化,然而各个不同的元素位置变化的速度不同,导致网页内的元素有层次错落的错觉,这和我们人体的眼球效果很像。我看到多家产品商用视差滚动效果来展示产品,从不同的空间角度和用户体验,起到了非常不错的效果。 目前这种视差滚动效果被越来越多的国外网站所应用, 成为网页设计的热点趋势。 通过一个很长的网页页面,其中利用一些令人惊叹的插图和图形,并使用视差滚动(Parallax Scrolling)效果,让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验。完美的展示了一个复杂的过程,让你犹如置身其中。\n\n百度百科看了还是木有很深刻的认识,那看看下面这个例子你一定会说“噢,来是这样”。[点我点我](http://hotdot.pro/#)\n\n在我看来视差滚动其实是通过页面多层次不同速度的滚动来达到一种视觉上的落差,使人感到页面有一种立体感!","html":"<p>下午在研究facebook今年f8大会上开源的基于react native的f8app的时候看到一个ParallaxBackground组件,不明觉厉啊!于是查了一下单词Parallax,意思为视差,所以ParallaxBackground其实是一个视差的背景,最后我把目光落到了视差滚动。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=385757&auto=1&height=66\"></iframe>\n\n<p>视差滚动是近些年国外web design中的一种潮流,它能够提高网页的立体性。它是如何运作的呢!上百度百科。</p>\n\n<blockquote>\n <p>视差效果,原本是一个天文学术语,当我们观察星空时,离我们远的星星移动速度较慢,离我们近的星星移动速度则较快。当我们坐在车上向车窗外 看时,也会有这样的感觉,远处的群山似乎没有在动,而近处的稻田却在飞速掠过。许多游戏中都使用视差效果来增加场景的立体感。说的简单点就是网页内的元素在滚动屏幕时发生的位置的变化,然而各个不同的元素位置变化的速度不同,导致网页内的元素有层次错落的错觉,这和我们人体的眼球效果很像。我看到多家产品商用视差滚动效果来展示产品,从不同的空间角度和用户体验,起到了非常不错的效果。 目前这种视差滚动效果被越来越多的国外网站所应用, 成为网页设计的热点趋势。 通过一个很长的网页页面,其中利用一些令人惊叹的插图和图形,并使用视差滚动(Parallax Scrolling)效果,让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验。完美的展示了一个复杂的过程,让你犹如置身其中。</p>\n</blockquote>\n\n<p>百度百科看了还是木有很深刻的认识,那看看下面这个例子你一定会说“噢,来是这样”。<a href=\"http://hotdot.pro/#\">点我点我</a></p>\n\n<p>在我看来视差滚动其实是通过页面多层次不同速度的滚动来达到一种视觉上的落差,使人感到页面有一种立体感!</p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464244796501,"created_by":1,"updated_at":1464245506730,"updated_by":1,"published_at":1464245322285,"published_by":1},{"id":10,"uuid":"5995d7ae-ad04-4a41-9710-a30426873cf3","title":"一脸懵逼:git大小写不敏感","slug":"gitda-xiao-xie-bu-min-gan","markdown":"用了这么久git,今天第一次遇到文件大小写不区分的问题,因为node找依赖模块时区分大小写,项目就跑不起来了!!!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=188647&auto=1&height=66\"></iframe>\n\n只能找办法解决:首先修改git的配置,使其大小写敏感\n```\ngit config core.ignorecase false\n```\n其次需要删除本地文件重新add和commit\n```\ngit rm -f filename\n\ngit add filename\n\ngit commit -m \"message\"\n```","html":"<p>用了这么久git,今天第一次遇到文件大小写不区分的问题,因为node找依赖模块时区分大小写,项目就跑不起来了!!!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=188647&auto=1&height=66\"></iframe>\n\n<p>只能找办法解决:首先修改git的配置,使其大小写敏感</p>\n\n<pre><code>git config core.ignorecase false \n</code></pre>\n\n<p>其次需要删除本地文件重新add和commit</p>\n\n<pre><code>git rm -f filename\n\ngit add filename\n\ngit commit -m \"message\" \n</code></pre>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464334877736,"created_by":1,"updated_at":1464336310938,"updated_by":1,"published_at":1464335179180,"published_by":1},{"id":11,"uuid":"203b9723-2d0c-4d53-8634-98ad9d8087f4","title":"一口老血:ESLint配置文档翻译","slug":"pei-zhi-eslint-2","markdown":"## 配置ESLint\nESLint 被设计为完全可配置的,这意味着你可以关闭任意规则,仅仅运行基础的句法检查,或混合和匹配捆绑的规则和你习惯的规则来最适合你的项目。这有两种主要的方式来配置 ESLint 。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=308353&auto=1&height=66\"></iframe>\n\n1. 配置注释 - 直接把配置信息通过 JavaScript 注释的方式写入文件\n2. 配置文件 - 使用一个 JavaScript ,JSON or YAML 文件来具体说明整个文件夹和所有它的子文件夹的配置信息。它可以在一个 `.eslintrc.*` 文件或在 `package.json` 文件的 `eslintConfig` 字段,ESLint 将会自动寻找并读取它们,或者你可以通过命令行具体制定配置文件。\n\n有以下几块信息是可以被配置的:\n\n* Environments - 你的脚本被设计运行在什么环境。每个环境将会带有一组预定义的全局变量。\n* Globals - 你的脚本执行期间所需的额外的全局变量。\n* Rules - 将打开的规则,以及它的错误级别。\n\nESLint 所有这些选项使你你有了对你的代码细力度的控制力。\n\n## 制定解析器选项\nESLint 允许你去指定你想要支持的 JavaScript 语言选项。默认,ESLint 支持 ES5 的句法。你可以通过覆盖解析器选项的设置去启用 ES6 和 ES7 甚至JSX的支持。\n\n请注意支持 JSX 句法不等同于支持 React 。React 应用了 ESLint 无法识别的更具体的语法。如果你在使用 React 并想要支持 React 语法,我们推荐你使用 `eslint-plugin-react` 。\n\n解析器选项在你的 `.eslintrc.*` 文件的 `parserOptions` 属性中被指定。这个可用的选项如下:\n\n* ecmaVersion - 设置你想要使用的 ECMAScript 的具体版本,可以为3、5(默认)、6或者7。\n* sourceType - 设置 **\"script\"** (默认),如果你的代码 ECMAScript 模块当中则使用 **\"module\"** 。\n* ecmaFeatures - 一个指示你所想要使用的语言特性的对象:\n * globalReturn - 允许 return 表达式在全局作用域当中\n * impliedStrict - 开启严格模式(如果 ecmaVersion 大于等于5)\n * jsx - 启用 JSX\n * experimentalObjectRestSpread -启用对于实验性的对象剩余/展开参数的支持。(重要:这是一个实验特性,将来可能会出现显著的变化。建议你不要写依赖于这一功能的规则,除非你已经愿意去维护其改变时所引起的问题。)\n\n这有一个例子 `.eslintrc.json` 文件:\n\n```\n{\n \"parserOptions\": {\n \"ecmaVersion\": 6,\n \"sourceType\": \"module\",\n \"ecmaFeatures\": {\n \"jsx\": true\n }\n },\n \"rules\": {\n \"semi\": 2\n }\n}\n```\n\n设置解析器选项帮助 ESLint 决定什么是一个解析错误。所有语言选项默认是 false 。\n\n## 指定解析器\nESLint 默认使用 `Espree` 作为它的解析器。你可以在你的配置文件当中可选的指定一个不同的解析器,只要它满足以下的要求:\n\n1. 它必须是一个本地安装的npm模块。\n2. 它必须有一个 Esprima 兼容接口(它必须导出一个 `parse()` 方法)。\n3. 它必须产出 Esprima 兼容的抽象语法树和表意的对象。\n\n注意甚至有了这些兼容性,我们不保证一个外部的解析器将和 ESLint 正确地工作,ESLint将不会解决这些由其他解析器不兼容造成的问题。\n\n你需要使用 `.eslintrc` 文件中的 `parser` 选项来指定一个npm模块作为你的解析器。例如,下面指定使用 Esprima 来代替 Espree :\n\n```\n{\n \"parser\": \"esprima\",\n \"rules\": {\n \"semi\": \"error\"\n }\n}\n```\n\n下列解析器是兼容 ESLint 的:\n* Esprima\n* Babel-ESLint - 一个 Babel 解析器的包装,使其兼容 ESLint 。\n\n注意当你使用一个习惯的解析器时, `parserOptions` 配置属性仍然是 ESLint 在非默认的ES5环境工作所必须的。解析器通过了所有的 `parserOptions` ,可以也可以不使用它们去决定启用的特性。\n\n## 指定环境\n一个环境预定义了全局变量。可用的环境如下:\n\n* browser - 浏览器全局变量。\n* node - Node.js 全局变量和 Node.js 作用域。\n* commonjs - CommonJS 全局变量和 CommonJS 作用域(只有在使用 Browserify或WebPack产生的浏览器代码中使用)。\n* shared-node-browser - Node and Browser公共的全局变量。\n* es6 - 启用所有的 ES6 module 所需要的特性。\n* worker - web workers 全局变量.\n* amd - defines require() and define() as global variables as per the amd spec.\n* mocha - 加入 Mocha 测试的所有全局变量。\n* jasmine - 加入 Jasmine 测试的所有全局变量,针对1.3和2.0版本。\n* jest - Jest 全局变量。\n* phantomjs - PhantomJS 全局变量。\n* protractor - Protractor 全局变量。\n* qunit - QUnit 全局变量。\n* jquery - jQuery 全局变量。\n* prototypejs - Prototype.js 全局变量。\n* shelljs - ShellJS 全局变量。\n* meteor - Meteor 全局变量。\n* mongo - MongoDB 全局变量。\n* applescript - AppleScript 全局变量。\n* nashorn - Java 8 Nashorn 引擎全局变量。\n* serviceworker - Service Worker 全局变量。\n* atomtest - Atom 测试 helper 变量。\n* embertest - Ember 测试 helper 变量。\n* webextensions - WebExtensions 变量。\n* greasemonkey - GreaseMonkey 变量。\n\n这些环境不相互影响,所以你可以同时定义超过一个环境。\n\nEnvironments 能够在文件当中,在配置文件中或使用命令行的 `--env` 参数指定。\n\n在你的 JavaScript 中使用注释指定环境,使用下面的格式:\n\n```\n/*eslint-env node, mocha */\n```\n\n他启用了 Node.js 和 Mocha 的环境。\n\n在配置文件当中指定环境,使用 `env` 键并把你需要启用的环境的属性设为 `true` 。例如,下面例子启用了 brower 和 Node.js 环境:\n\n```\n{\n \"env\": {\n \"browser\": true,\n \"node\": true\n }\n}\n```\n\n或者在 `package.json` 文件\n\n```\n{\n \"name\": \"mypackage\",\n \"version\": \"0.0.1\",\n \"eslintConfig\": {\n \"env\": {\n \"browser\": true,\n \"node\": true\n }\n }\n}\n```\n\n在 YAML 文件中:\n\n```\n---\n env:\n browser: true\n node: true\n```\n\n如果你想去使用一个来自插件的环境,确保指定了插件名在 `plugins` 数组,然后使用不带前缀的插件名,其次是斜杠,再其次是环境名。举例来说:\n\n```\n{\n \"plugins\": [\"example\"],\n \"env\": {\n \"example/custom\": true\n }\n}\n```\n\n在一个 `package.json` 文件中\n\n```\n{\n \"name\": \"mypackage\",\n \"version\": \"0.0.1\",\n \"eslintConfig\": {\n \"plugins\": [\"example\"],\n \"env\": {\n \"example/custom\": true\n }\n }\n}\n```\n\nYAML 文件:\n\n```\n---\n plugins:\n - example\n env:\n example/custom: true\n```\n\n## 指定全局变量\nno-undef 规则将会警告将要访问的为定义的变量。如果你使用全局变量在一个文件中,它将是值得的去定义 globals 以至于 ESLint 将不会警告它们的使用。你可以定义全局变量使用文件中的注释或者配置文件。\n\n在你的 JavaScript 文件中指定全局变量,使用下面的格式:\n\n```\n/* global var1, var2 */\n```\n\n定义两个全局变量, `var1` 和 `var2` 。如果你想要去可选的制定这些全局变量不应当被修改(只读),你可以设置每个全局变量一个 false 标记:\n\n```\n/* global var1:false, var2:false */\n```\n\n在配置文件中配置全局变量,使用 `globals` 键并指出你想要使用的全局变量。设置全局变量为 `true` 来使其可以被覆盖或制定 `false` 使其只读。举例:\n\n```\n{\n \"globals\": {\n \"var1\": true,\n \"var2\": false\n }\n}\n```\n\nYAML中:\n\n```\n---\n globals:\n var1: true\n var2: false\n```\n\n这个例子允许 `var1` 可以在代码中覆盖,但 `var2` 不允许。\n\n## 配置插件\nESLint 支持使用第三方的插件。在使用插件前,需要用 npm 将它安装。\n\n在配置文件中配置插件,使用 `plugins` 键,它包含一个插件名的列表。 `eslint-plugin-` 的前缀可以从插件名中省略。\n\n```\n{\n \"plugins\": [\n \"plugin1\",\n \"eslint-plugin-plugin2\"\n ]\n}\n```\n\nYAML中:\n```\n---\n plugins:\n - plugin1\n - eslint-plugin-plugin2\n```\n\n注意:一个全局安装的 ESLint 只能改使用全局安装的 ESLint 插件。本地安装的 ESLint 能够使用本地和全局的 ESLint 插件。\n\n## 配置规则\nESlint 自带了大量的规则。你能够通过注释和配置文件的方式修改你项目中的规则。修改一个规则的设置,你必须设置 rule ID 为下列中的一个值:\n\n* `\"off\"` 或 `0` - 关闭规则\n* `\"warn\"` 或 `1` - 打开规则起提醒作用(不退出代码执行)\n* `\"error\"` 或 `2` - 打开规则起报错作用(将触发退出代码)\n\n在文件中使用注释配置,使用下面的格式:\n\n```\n/* eslint eqeqeq: \"off\", curly: \"error\" */\n```\n\n在这个例子中 `eqeqeq` 被关闭, `curly` 被打开 error 级别。你也可以使用等价的数字表示规则的级别:\n\n```\n/* eslint eqeqeq: 0, curly: 2 */\n```\n\n这个例子和上一个例子相同,只是它使用数字编号代替了字符串值。`eqeqeq` 被关闭, `curly` 被打开 error 级别。\n\n如果一个规则有可选的选项,你可以使用数组字面量设置它们,例如:\n\n```\n/* eslint quotes: [\"error\", \"double\"], curly: 2 */\n```\n\n这个注释为 `quotes` 规则指定了两个选项。数组的第一项总是严重的规则(数字或字符串)。\n\n在配置文件配置规则,使用 `rules` 键跟着一个 error 级别和任何选项你想要使用的。例如:\n\n```\n{\n \"rules\": {\n \"eqeqeq\": \"off\",\n \"curly\": \"error\",\n \"quotes\": [\"error\", \"double\"]\n }\n}\n```\n\nYAML 文件中:\n\n```\n---\nrules:\n eqeqeq: off\n curly: error\n quotes:\n - error\n - double\n```\n\n配置一个在插件中定义的规则,你必须为 rule ID 加上插件名作为前缀,以及 `/`。举例来说:\n\n```\n{\n \"plugins\": [\n \"plugin1\"\n ],\n \"rules\": {\n \"eqeqeq\": \"off\",\n \"curly\": \"error\",\n \"quotes\": [\"error\", \"double\"],\n \"plugin1/rule1\": \"error\"\n }\n}\n```\n\nYAML 文件中:\n\n```\n---\nplugins:\n - plugin1\nrules:\n eqeqeq: 0\n curly: error\n quotes:\n - error\n - \"double\"\n plugin1/rule1: error\n```\n\n在这个配置文件中,规则 `plugin1/rule1` 来自插件 `plugin1` 。你也能够使用如下格式配置注释的方式,例如:\n\n```\n/* eslint \"plugin1/rule1\": \"error\" */\n```\n\n注意:当指定来自插件的规则,确保省略 `eslint-plugin-` 。ESLint 只使用无前缀的名字来定位内部规则。\n\n## 使用行内注释来禁用规则\n使用如下的格式来临时地禁用文件中规则的警告:\n\n```\n/* eslint-disable */\n\n// Disables all rules between comments\nalert('foo');\n\n/* eslint-enable */\n```\n\n你也可以禁用或启用指定的规则:\n\n```\n/* eslint-disable no-alert, no-console */\n\n// Disables no-alert and no-console warnings between comments\nalert('foo');\nconsole.log('bar');\n\n/* eslint-enable no-alert, no-console */\n```\n\n在整个文件中禁用规则警告,把 `/* eslint-disable */` 放到文件顶部:\n\n```\n/* eslint-disable */\n\n// Disables all rules for the rest of the file\nalert('foo');\n```\n\n你也可以在整个文件禁用指定的规则:\n\n```\n/* eslint-disable no-alert */\n\n// Disables no-alert for the rest of the file\nalert('foo');\n```\n\n在指定行禁用所有规则:\n\n```\nalert('foo'); // eslint-disable-line\n\n// eslint-disable-next-line\nalert('foo');\n```\n\n在指定行禁用指定的规则:\n\n```\nalert('foo'); // eslint-disable-line no-alert\n\n// eslint-disable-next-line no-alert\nalert('foo');\n```\n\n指定行禁用多个规则:\n\n```\nalert('foo'); // eslint-disable-line no-alert, quotes, semi\n\n// eslint-disable-next-line no-alert, quotes, semi\nalert('foo');\n```\n\n注意:注释仅仅是禁用了一部分警告,告诉 ESlint 不要报告这部分代码违规。 ESlint 解析整个文件,所以禁用的代码仍然需要符合合法的 JavaScript 语法。\n\n## 添加共享的设置\nESlint 支持向配置文件中添加共享的配置。你能够添加 `settings` 对象到 ESLint 配置文件中,它将会提供给每一个将要执行的规则。这也许是有用的如果你添加了习惯的规则并想要它们能够访问相同的信息并轻松配置。\n\n在 JSON 中:\n\n```\n{\n \"settings\": {\n \"sharedData\": \"Hello\"\n }\n}\n```\n\n在 YAML 文件中:\n\n```\n---\n settings:\n sharedData: \"Hello\"\n```\n\n## 使用配置文件\n这有两种方式去使用配置文件。第一种是保存文件到你喜欢的地方,然后传递它的地址到 CLI 使用 `-c` 选项,例如:\n\n```\neslint -c myconfig.json myfiletotest.js\n```\n\n第二种方式是去使用配置文件通过 `.eslintrc.*` 或 `package.json` 文件。ESLint 将会自动在目录中寻找它们,从连续的父目录一路找到文件系统根目录。这个选项时有用的当你想要对项目的不同部分使用不同的配置,或者当你想要别的直接去使用 ESLint 而不需要记住它在配置文件中传递。\n\n两种方式,配置文件的设置都将会覆盖默认的配置。\n\n## 配置文件格式\nESLint 支持各种格式的配置文件:\n\n* **JavaScript** - 使用 `.eslintrc.js` 并导出一个包含你配置信息的对象。\n* **YAML** - 使用 `.eslintrc.yaml` 或 `.eslintrc.yml` 来定义配置结构。\n* **JSON** - 使用 `.eslintrc.json` 来定义配置结构。 ESLint's JSON 文件也支持 JavaScript 风格的注释。\n* **Deprecated** - 使用 `eslintrc`,过时的,可以是 JSON 或 YAML。\n* **package.json** - 创建一个 `eslintConfig` 属性在你的 `package.json`文件并在这里定义你的配置。\n\n如果有多种配置文件在同一个目录,ESLint将会使用一个。它们的优先级如下:\n\n1. `.eslintrc.js`\n2. `.eslintrc.yaml`\n3. `.eslintrc.yml`\n4. `.eslintrc.json`\n5. `.eslintrc`\n6. `package.json`\n\n## 配置及联和层次结构\n当使用 `.eslintrc.*` 和 `package.json` 文件来配置,你可以利用配置级联的优势。举例来说,假设你有下列的目录结构:\n\n```\nyour-project\n├── .eslintrc\n├── lib\n│ └── source.js\n└─┬ tests\n ├── .eslintrc\n └── test.js\n```\n\n配置级联将会使用最近的 `.eslintrc` 文件作为最高优先级,然后是任何父级目录的配置文件等。当你在项目中运行 ESLint ,所有 `lib/` 目录下的文件将会使用根目录下的 `.eslintrc` 文件作为它们的配置。如果 ESLint 进入 `test/` 目录,它将会使用 `your-project/tests/.eslintrc` 而不是 `your-project/.eslintrc` 。所以 `your-project/tests/test.js` 的检测是基于目录层次结构中的两个 `.eslintrc` 文件的组合的,近的拥有更高的优先级。这样的方式,你可以拥有项目级别的 ESLint 设置,并可以用目录层次的配置来覆盖它。\n\n相同的方式,在 `package.json` 文件中也可行。根目录中 `package.json` 的 `eslintConfig` 字段将在其所有子目录生效,但是在 tests 目录的 `.eslintrc` 文件将会覆盖所有冲突的配置。\n\n```\nyour-project\n├── package.json\n├── lib\n│ └── source.js\n└─┬ tests\n ├── .eslintrc\n └── test.js\n```\n\n如果在一个目录中,同时找到了 `.eslintrc` 和 `package.json` 文件,`eslintrc` 文件将有更高的优先级, `package.json` 文件将不被使用。\n\n注意:如果你又一个个人的配置文件在你的用户目录( `~/.eslintrc` ),它将会仅仅在没有别的配置文件没发现的情况下使用。它将会对你所有用户目录下的文件生效,包括第三方的代码,因此在运行 ESLint 很可能会产生问题。\n\n默认地, ESLint 将会在所有父目录直到根目录寻找配置文件。这将是有用的,如果你想要所有你的项目去跟随某个约定,但是有时候会导致意想不到的结果。限制 ESLint 在一个指定的项目,把 `\"root\": true` 加到 `package.json` 的 `eslintConfig` 字段或者 `.eslintrc.*` 文件,在你的根目录。 ESLint 将会停止寻找父目录当它找到了 `\"root\": true` 的配置。\n\n```\n{\n \"root\": true\n}\n```\nYAML 文件中:\n\n```\n---\n root: true\n```\n\n举例来说, 这个例子当中 `main.js` 将会使用 `lib/` 中的配置,而不会使用 `productA` 目录下的 `.eslintrc` 文件的配置,因为在 `lib/` 中的 `.eslintrc` 有 `\"root\": true` 的设置。\n\n```\nhome\n└── user\n ├── .eslintrc <- Always skipped if other configs present\n └── projectA\n ├── .eslintrc <- Not used\n └── lib\n ├── .eslintrc <- { \"root\": true }\n └── main.js\n```\n\n完整的配置层次,从高到低如下:\n\n1. 行内配置\n 1. `/*eslint-disable*/` and `/*eslint-enable*/`\n 2. `/*global*/`\n 3. `/*eslint*/`\n 4. `/*eslint-env*/`\n2. 命令行配置\n 1. `--global`\n 2. `--rule`\n 3. `--env`\n 4. `-c, --config`\n3. 项目级别配置\n 1. `.eslintrc.*` 或 `package.json` 文件\n 2. 祖先目录中寻找,除非有 `\"root\": true` 将不会向上寻找\n 3. 个人某人的配置在 `~/.eslintrc`\n\n## 扩展配置文件\n如果你想要扩展一个指定的配置文件,你可以使用 `extends` 属性,并制定路径。可以是相对的或是绝对的路径。\n\n配置能够被如下文件扩展:\n\n1. YAML 文件\n2. JSON 文件\n3. JS 文件\n4. 共享的配置包\n\n扩展配置提供了基础的规则,并可以覆盖。举例:\n\n```\n{\n \"extends\": \"./node_modules/coding-standard/.eslintrc\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": 1\n }\n}\n```\n\n配置也许也可以以数组的方式提供,后面的会覆盖前面的相同的规则的配置。举例:\n\n```\n{\n \"extends\": [\n \"./node_modules/coding-standard/eslintDefaults.js\",\n // Override eslintDefaults.js\n \"./node_modules/coding-standard/.eslintrc-es6\",\n // Override .eslintrc-es6\n \"./node_modules/coding-standard/.eslintrc-jsx\",\n ],\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n```\n\n扩展配置能够包涵它们自己的 `extends` ,导致循环的引用。\n\n你也可以使用共享的配置包。你需要使用 npm 去安装它们,例如:\n\n```\n{\n \"extends\": \"eslint-config-myrules\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n```\n\n在这个例子, `eslint-config-myrules` 包将会加载为一个对象并作为这个配置的父配置。\n\n注意:你可以省略 `eslint-config-` 前缀, ESLint 将会自动为你添加,和插件工作相似。\n\nESlint 也支持插件提供的扩展配置:\n\n```\n{\n \"extends\": \"plugin:eslint-plugin-myplugin/myConfig\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n```\n\n在这个例子, `eslint-plugin-myplugin` 包包含了名为 `default` 的配置。\n\n**十分重要**:使用插件中的配置时,必须要加上 `plugin:` 前缀来指明哈。但你可以可选地省略 `eslint-plugin` 前缀。\n\n注意:对于根目录或者其它父目录, `extends` 处理路径会用当前工作目录,而不是文件自身。\n\n## 配置文件中的注释\nJSON 和 YAML 配置文件都支持注释, `package.json` 不能包括注释。你可以使用 JAVAScript 风格的注释或 YAML 风格的注释, ESLint 将安全地忽略它们。这可以允许你的配置文件跟友好。举例:\n\n```\n{\n \"env\": {\n \"browser\": true\n },\n \"rules\": {\n // Override our default settings just for this directory\n \"eqeqeq\": \"warn\",\n \"strict\": \"off\"\n }\n}\n```\n\n## 指定需要检测的文件后缀\n当前指定后缀需要在命令行选项 `--ext` 后,添加用空格分开的后缀名列表。\n\n## 忽略文件或目录\n\n\n你可以告诉 ESLint 去忽略指定的文件或目录,通过一个 `.eslintignore` 文件在你的根目录。 `.eslintignore` 文件是一个普通文本文件,每一行定义了要被忽略的文件或目录。举例:下列表达式将会省略所有 JavaScript 文件:\n\n```\n**/*.js\n```\n\n当 ESLint 运行的时候,它会寻找当前工作目录的 `.eslintignore` 文件在它决定监测之前。如果找到了文件,这里面的配置将会生效。 `.eslintignore` 只会被使用一次,因此目录中其他的 `.eslintignore` 将不会被使用。\n\n* #好开头将会被认为是注释\n* 相对于 `.eslintignore` 文件路径或当前工作目录的路径\n* 忽略规则参照 `.gitignore` 规范\n* !用来取消前面的忽略的匹配\n\n`/node_modules/*` 和 `/bower_components/*` 将默认被忽略。\n\n举例,把下面的 `.eslintignore` 文件放倒工作目录下将会忽略`/node_modules/*` 和 `/bower_components/*` 目录,任何扩展名为 `.ts.js` 或 `.coffee.js` 可能被转换,任何在 `build/*` 目录除了 `build/index.js` 将被忽略:\n\n```\n# /node_modules/* and /bower_components/* ignored by default\n\n# Ignore built files except build/index.js\nbuild/*\n!build/index.js\n```\n\n## 使用替换的文件\n如果你喜欢去使用别的文件而不是 `.eslintignore` 来工作,你在命令行中可以指定 `--ignore-path` 选项。举例来说,你可以使用 `.jshintignore` ,因为两者的配置相同:\n\n```\neslint --ignore-path .jshintignore file.js\n```\n\n你也可以使用 `.gitignore` 文件:\n\n```\neslint --ignore-path .gitignore file.js\n```\n\n任何标准的 ignore 文件可以被使用。\n\n## 忽略文件警告\n当你 eslint 一个被忽略的文件或目录时,会发出警告,你可以使用 `--no-ignore` 来进行省略。\n","html":"<h2 id=\"eslint\">配置ESLint</h2>\n\n<p>ESLint 被设计为完全可配置的,这意味着你可以关闭任意规则,仅仅运行基础的句法检查,或混合和匹配捆绑的规则和你习惯的规则来最适合你的项目。这有两种主要的方式来配置 ESLint 。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=308353&auto=1&height=66\"></iframe>\n\n<ol>\n<li>配置注释 - 直接把配置信息通过 JavaScript 注释的方式写入文件 </li>\n<li>配置文件 - 使用一个 JavaScript ,JSON or YAML 文件来具体说明整个文件夹和所有它的子文件夹的配置信息。它可以在一个 <code>.eslintrc.*</code> 文件或在 <code>package.json</code> 文件的 <code>eslintConfig</code> 字段,ESLint 将会自动寻找并读取它们,或者你可以通过命令行具体制定配置文件。</li>\n</ol>\n\n<p>有以下几块信息是可以被配置的:</p>\n\n<ul>\n<li>Environments - 你的脚本被设计运行在什么环境。每个环境将会带有一组预定义的全局变量。</li>\n<li>Globals - 你的脚本执行期间所需的额外的全局变量。</li>\n<li>Rules - 将打开的规则,以及它的错误级别。</li>\n</ul>\n\n<p>ESLint 所有这些选项使你你有了对你的代码细力度的控制力。</p>\n\n<h2 id=\"\">制定解析器选项</h2>\n\n<p>ESLint 允许你去指定你想要支持的 JavaScript 语言选项。默认,ESLint 支持 ES5 的句法。你可以通过覆盖解析器选项的设置去启用 ES6 和 ES7 甚至JSX的支持。</p>\n\n<p>请注意支持 JSX 句法不等同于支持 React 。React 应用了 ESLint 无法识别的更具体的语法。如果你在使用 React 并想要支持 React 语法,我们推荐你使用 <code>eslint-plugin-react</code> 。</p>\n\n<p>解析器选项在你的 <code>.eslintrc.*</code> 文件的 <code>parserOptions</code> 属性中被指定。这个可用的选项如下:</p>\n\n<ul>\n<li>ecmaVersion - 设置你想要使用的 ECMAScript 的具体版本,可以为3、5(默认)、6或者7。</li>\n<li>sourceType - 设置 <strong>\"script\"</strong> (默认),如果你的代码 ECMAScript 模块当中则使用 <strong>\"module\"</strong> 。</li>\n<li>ecmaFeatures - 一个指示你所想要使用的语言特性的对象:\n<ul><li>globalReturn - 允许 return 表达式在全局作用域当中</li>\n<li>impliedStrict - 开启严格模式(如果 ecmaVersion 大于等于5)</li>\n<li>jsx - 启用 JSX</li>\n<li>experimentalObjectRestSpread -启用对于实验性的对象剩余/展开参数的支持。(重要:这是一个实验特性,将来可能会出现显著的变化。建议你不要写依赖于这一功能的规则,除非你已经愿意去维护其改变时所引起的问题。)</li></ul></li>\n</ul>\n\n<p>这有一个例子 <code>.eslintrc.json</code> 文件:</p>\n\n<pre><code>{\n \"parserOptions\": {\n \"ecmaVersion\": 6,\n \"sourceType\": \"module\",\n \"ecmaFeatures\": {\n \"jsx\": true\n }\n },\n \"rules\": {\n \"semi\": 2\n }\n}\n</code></pre>\n\n<p>设置解析器选项帮助 ESLint 决定什么是一个解析错误。所有语言选项默认是 false 。</p>\n\n<h2 id=\"\">指定解析器</h2>\n\n<p>ESLint 默认使用 <code>Espree</code> 作为它的解析器。你可以在你的配置文件当中可选的指定一个不同的解析器,只要它满足以下的要求:</p>\n\n<ol>\n<li>它必须是一个本地安装的npm模块。 </li>\n<li>它必须有一个 Esprima 兼容接口(它必须导出一个 <code>parse()</code> 方法)。 </li>\n<li>它必须产出 Esprima 兼容的抽象语法树和表意的对象。</li>\n</ol>\n\n<p>注意甚至有了这些兼容性,我们不保证一个外部的解析器将和 ESLint 正确地工作,ESLint将不会解决这些由其他解析器不兼容造成的问题。</p>\n\n<p>你需要使用 <code>.eslintrc</code> 文件中的 <code>parser</code> 选项来指定一个npm模块作为你的解析器。例如,下面指定使用 Esprima 来代替 Espree :</p>\n\n<pre><code>{\n \"parser\": \"esprima\",\n \"rules\": {\n \"semi\": \"error\"\n }\n}\n</code></pre>\n\n<p>下列解析器是兼容 ESLint 的:\n* Esprima\n* Babel-ESLint - 一个 Babel 解析器的包装,使其兼容 ESLint 。</p>\n\n<p>注意当你使用一个习惯的解析器时, <code>parserOptions</code> 配置属性仍然是 ESLint 在非默认的ES5环境工作所必须的。解析器通过了所有的 <code>parserOptions</code> ,可以也可以不使用它们去决定启用的特性。</p>\n\n<h2 id=\"\">指定环境</h2>\n\n<p>一个环境预定义了全局变量。可用的环境如下:</p>\n\n<ul>\n<li>browser - 浏览器全局变量。</li>\n<li>node - Node.js 全局变量和 Node.js 作用域。</li>\n<li>commonjs - CommonJS 全局变量和 CommonJS 作用域(只有在使用 Browserify或WebPack产生的浏览器代码中使用)。</li>\n<li>shared-node-browser - Node and Browser公共的全局变量。</li>\n<li>es6 - 启用所有的 ES6 module 所需要的特性。</li>\n<li>worker - web workers 全局变量.</li>\n<li>amd - defines require() and define() as global variables as per the amd spec.</li>\n<li>mocha - 加入 Mocha 测试的所有全局变量。</li>\n<li>jasmine - 加入 Jasmine 测试的所有全局变量,针对1.3和2.0版本。</li>\n<li>jest - Jest 全局变量。</li>\n<li>phantomjs - PhantomJS 全局变量。</li>\n<li>protractor - Protractor 全局变量。</li>\n<li>qunit - QUnit 全局变量。</li>\n<li>jquery - jQuery 全局变量。</li>\n<li>prototypejs - Prototype.js 全局变量。</li>\n<li>shelljs - ShellJS 全局变量。</li>\n<li>meteor - Meteor 全局变量。</li>\n<li>mongo - MongoDB 全局变量。</li>\n<li>applescript - AppleScript 全局变量。</li>\n<li>nashorn - Java 8 Nashorn 引擎全局变量。</li>\n<li>serviceworker - Service Worker 全局变量。</li>\n<li>atomtest - Atom 测试 helper 变量。</li>\n<li>embertest - Ember 测试 helper 变量。</li>\n<li>webextensions - WebExtensions 变量。</li>\n<li>greasemonkey - GreaseMonkey 变量。</li>\n</ul>\n\n<p>这些环境不相互影响,所以你可以同时定义超过一个环境。</p>\n\n<p>Environments 能够在文件当中,在配置文件中或使用命令行的 <code>--env</code> 参数指定。</p>\n\n<p>在你的 JavaScript 中使用注释指定环境,使用下面的格式:</p>\n\n<pre><code>/*eslint-env node, mocha */\n</code></pre>\n\n<p>他启用了 Node.js 和 Mocha 的环境。</p>\n\n<p>在配置文件当中指定环境,使用 <code>env</code> 键并把你需要启用的环境的属性设为 <code>true</code> 。例如,下面例子启用了 brower 和 Node.js 环境:</p>\n\n<pre><code>{\n \"env\": {\n \"browser\": true,\n \"node\": true\n }\n}\n</code></pre>\n\n<p>或者在 <code>package.json</code> 文件</p>\n\n<pre><code>{\n \"name\": \"mypackage\",\n \"version\": \"0.0.1\",\n \"eslintConfig\": {\n \"env\": {\n \"browser\": true,\n \"node\": true\n }\n }\n}\n</code></pre>\n\n<p>在 YAML 文件中:</p>\n\n<pre><code>---\n env:\n browser: true\n node: true\n</code></pre>\n\n<p>如果你想去使用一个来自插件的环境,确保指定了插件名在 <code>plugins</code> 数组,然后使用不带前缀的插件名,其次是斜杠,再其次是环境名。举例来说:</p>\n\n<pre><code>{\n \"plugins\": [\"example\"],\n \"env\": {\n \"example/custom\": true\n }\n}\n</code></pre>\n\n<p>在一个 <code>package.json</code> 文件中</p>\n\n<pre><code>{\n \"name\": \"mypackage\",\n \"version\": \"0.0.1\",\n \"eslintConfig\": {\n \"plugins\": [\"example\"],\n \"env\": {\n \"example/custom\": true\n }\n }\n}\n</code></pre>\n\n<p>YAML 文件:</p>\n\n<pre><code>---\n plugins:\n - example\n env:\n example/custom: true\n</code></pre>\n\n<h2 id=\"\">指定全局变量</h2>\n\n<p>no-undef 规则将会警告将要访问的为定义的变量。如果你使用全局变量在一个文件中,它将是值得的去定义 globals 以至于 ESLint 将不会警告它们的使用。你可以定义全局变量使用文件中的注释或者配置文件。</p>\n\n<p>在你的 JavaScript 文件中指定全局变量,使用下面的格式:</p>\n\n<pre><code>/* global var1, var2 */\n</code></pre>\n\n<p>定义两个全局变量, <code>var1</code> 和 <code>var2</code> 。如果你想要去可选的制定这些全局变量不应当被修改(只读),你可以设置每个全局变量一个 false 标记:</p>\n\n<pre><code>/* global var1:false, var2:false */\n</code></pre>\n\n<p>在配置文件中配置全局变量,使用 <code>globals</code> 键并指出你想要使用的全局变量。设置全局变量为 <code>true</code> 来使其可以被覆盖或制定 <code>false</code> 使其只读。举例:</p>\n\n<pre><code>{\n \"globals\": {\n \"var1\": true,\n \"var2\": false\n }\n}\n</code></pre>\n\n<p>YAML中:</p>\n\n<pre><code>---\n globals:\n var1: true\n var2: false\n</code></pre>\n\n<p>这个例子允许 <code>var1</code> 可以在代码中覆盖,但 <code>var2</code> 不允许。</p>\n\n<h2 id=\"\">配置插件</h2>\n\n<p>ESLint 支持使用第三方的插件。在使用插件前,需要用 npm 将它安装。</p>\n\n<p>在配置文件中配置插件,使用 <code>plugins</code> 键,它包含一个插件名的列表。 <code>eslint-plugin-</code> 的前缀可以从插件名中省略。</p>\n\n<pre><code>{\n \"plugins\": [\n \"plugin1\",\n \"eslint-plugin-plugin2\"\n ]\n}\n</code></pre>\n\n<p>YAML中: </p>\n\n<pre><code>---\n plugins:\n - plugin1\n - eslint-plugin-plugin2\n</code></pre>\n\n<p>注意:一个全局安装的 ESLint 只能改使用全局安装的 ESLint 插件。本地安装的 ESLint 能够使用本地和全局的 ESLint 插件。</p>\n\n<h2 id=\"\">配置规则</h2>\n\n<p>ESlint 自带了大量的规则。你能够通过注释和配置文件的方式修改你项目中的规则。修改一个规则的设置,你必须设置 rule ID 为下列中的一个值:</p>\n\n<ul>\n<li><code>\"off\"</code> 或 <code>0</code> - 关闭规则</li>\n<li><code>\"warn\"</code> 或 <code>1</code> - 打开规则起提醒作用(不退出代码执行)</li>\n<li><code>\"error\"</code> 或 <code>2</code> - 打开规则起报错作用(将触发退出代码)</li>\n</ul>\n\n<p>在文件中使用注释配置,使用下面的格式:</p>\n\n<pre><code>/* eslint eqeqeq: \"off\", curly: \"error\" */\n</code></pre>\n\n<p>在这个例子中 <code>eqeqeq</code> 被关闭, <code>curly</code> 被打开 error 级别。你也可以使用等价的数字表示规则的级别:</p>\n\n<pre><code>/* eslint eqeqeq: 0, curly: 2 */\n</code></pre>\n\n<p>这个例子和上一个例子相同,只是它使用数字编号代替了字符串值。<code>eqeqeq</code> 被关闭, <code>curly</code> 被打开 error 级别。</p>\n\n<p>如果一个规则有可选的选项,你可以使用数组字面量设置它们,例如:</p>\n\n<pre><code>/* eslint quotes: [\"error\", \"double\"], curly: 2 */\n</code></pre>\n\n<p>这个注释为 <code>quotes</code> 规则指定了两个选项。数组的第一项总是严重的规则(数字或字符串)。</p>\n\n<p>在配置文件配置规则,使用 <code>rules</code> 键跟着一个 error 级别和任何选项你想要使用的。例如:</p>\n\n<pre><code>{\n \"rules\": {\n \"eqeqeq\": \"off\",\n \"curly\": \"error\",\n \"quotes\": [\"error\", \"double\"]\n }\n}\n</code></pre>\n\n<p>YAML 文件中:</p>\n\n<pre><code>---\nrules: \n eqeqeq: off\n curly: error\n quotes:\n - error\n - double\n</code></pre>\n\n<p>配置一个在插件中定义的规则,你必须为 rule ID 加上插件名作为前缀,以及 <code>/</code>。举例来说:</p>\n\n<pre><code>{\n \"plugins\": [\n \"plugin1\"\n ],\n \"rules\": {\n \"eqeqeq\": \"off\",\n \"curly\": \"error\",\n \"quotes\": [\"error\", \"double\"],\n \"plugin1/rule1\": \"error\"\n }\n}\n</code></pre>\n\n<p>YAML 文件中:</p>\n\n<pre><code>---\nplugins: \n - plugin1\nrules: \n eqeqeq: 0\n curly: error\n quotes:\n - error\n - \"double\"\n plugin1/rule1: error\n</code></pre>\n\n<p>在这个配置文件中,规则 <code>plugin1/rule1</code> 来自插件 <code>plugin1</code> 。你也能够使用如下格式配置注释的方式,例如:</p>\n\n<pre><code>/* eslint \"plugin1/rule1\": \"error\" */\n</code></pre>\n\n<p>注意:当指定来自插件的规则,确保省略 <code>eslint-plugin-</code> 。ESLint 只使用无前缀的名字来定位内部规则。</p>\n\n<h2 id=\"\">使用行内注释来禁用规则</h2>\n\n<p>使用如下的格式来临时地禁用文件中规则的警告:</p>\n\n<pre><code>/* eslint-disable */\n\n// Disables all rules between comments\nalert('foo');\n\n/* eslint-enable */\n</code></pre>\n\n<p>你也可以禁用或启用指定的规则:</p>\n\n<pre><code>/* eslint-disable no-alert, no-console */\n\n// Disables no-alert and no-console warnings between comments\nalert('foo'); \nconsole.log('bar');\n\n/* eslint-enable no-alert, no-console */\n</code></pre>\n\n<p>在整个文件中禁用规则警告,把 <code>/* eslint-disable */</code> 放到文件顶部:</p>\n\n<pre><code>/* eslint-disable */\n\n// Disables all rules for the rest of the file\nalert('foo'); \n</code></pre>\n\n<p>你也可以在整个文件禁用指定的规则:</p>\n\n<pre><code>/* eslint-disable no-alert */\n\n// Disables no-alert for the rest of the file\nalert('foo'); \n</code></pre>\n\n<p>在指定行禁用所有规则:</p>\n\n<pre><code>alert('foo'); // eslint-disable-line\n\n// eslint-disable-next-line\nalert('foo'); \n</code></pre>\n\n<p>在指定行禁用指定的规则:</p>\n\n<pre><code>alert('foo'); // eslint-disable-line no-alert\n\n// eslint-disable-next-line no-alert\nalert('foo'); \n</code></pre>\n\n<p>指定行禁用多个规则:</p>\n\n<pre><code>alert('foo'); // eslint-disable-line no-alert, quotes, semi\n\n// eslint-disable-next-line no-alert, quotes, semi\nalert('foo'); \n</code></pre>\n\n<p>注意:注释仅仅是禁用了一部分警告,告诉 ESlint 不要报告这部分代码违规。 ESlint 解析整个文件,所以禁用的代码仍然需要符合合法的 JavaScript 语法。</p>\n\n<h2 id=\"\">添加共享的设置</h2>\n\n<p>ESlint 支持向配置文件中添加共享的配置。你能够添加 <code>settings</code> 对象到 ESLint 配置文件中,它将会提供给每一个将要执行的规则。这也许是有用的如果你添加了习惯的规则并想要它们能够访问相同的信息并轻松配置。</p>\n\n<p>在 JSON 中:</p>\n\n<pre><code>{\n \"settings\": {\n \"sharedData\": \"Hello\"\n }\n}\n</code></pre>\n\n<p>在 YAML 文件中:</p>\n\n<pre><code>---\n settings:\n sharedData: \"Hello\"\n</code></pre>\n\n<h2 id=\"\">使用配置文件</h2>\n\n<p>这有两种方式去使用配置文件。第一种是保存文件到你喜欢的地方,然后传递它的地址到 CLI 使用 <code>-c</code> 选项,例如:</p>\n\n<pre><code>eslint -c myconfig.json myfiletotest.js \n</code></pre>\n\n<p>第二种方式是去使用配置文件通过 <code>.eslintrc.*</code> 或 <code>package.json</code> 文件。ESLint 将会自动在目录中寻找它们,从连续的父目录一路找到文件系统根目录。这个选项时有用的当你想要对项目的不同部分使用不同的配置,或者当你想要别的直接去使用 ESLint 而不需要记住它在配置文件中传递。</p>\n\n<p>两种方式,配置文件的设置都将会覆盖默认的配置。</p>\n\n<h2 id=\"\">配置文件格式</h2>\n\n<p>ESLint 支持各种格式的配置文件:</p>\n\n<ul>\n<li><strong>JavaScript</strong> - 使用 <code>.eslintrc.js</code> 并导出一个包含你配置信息的对象。</li>\n<li><strong>YAML</strong> - 使用 <code>.eslintrc.yaml</code> 或 <code>.eslintrc.yml</code> 来定义配置结构。</li>\n<li><strong>JSON</strong> - 使用 <code>.eslintrc.json</code> 来定义配置结构。 ESLint's JSON 文件也支持 JavaScript 风格的注释。</li>\n<li><strong>Deprecated</strong> - 使用 <code>eslintrc</code>,过时的,可以是 JSON 或 YAML。</li>\n<li><strong>package.json</strong> - 创建一个 <code>eslintConfig</code> 属性在你的 <code>package.json</code>文件并在这里定义你的配置。</li>\n</ul>\n\n<p>如果有多种配置文件在同一个目录,ESLint将会使用一个。它们的优先级如下:</p>\n\n<ol>\n<li><code>.eslintrc.js</code> </li>\n<li><code>.eslintrc.yaml</code> </li>\n<li><code>.eslintrc.yml</code> </li>\n<li><code>.eslintrc.json</code> </li>\n<li><code>.eslintrc</code> </li>\n<li><code>package.json</code></li>\n</ol>\n\n<h2 id=\"\">配置及联和层次结构</h2>\n\n<p>当使用 <code>.eslintrc.*</code> 和 <code>package.json</code> 文件来配置,你可以利用配置级联的优势。举例来说,假设你有下列的目录结构:</p>\n\n<pre><code>your-project \n├── .eslintrc\n├── lib\n│ └── source.js\n└─┬ tests\n ├── .eslintrc\n └── test.js\n</code></pre>\n\n<p>配置级联将会使用最近的 <code>.eslintrc</code> 文件作为最高优先级,然后是任何父级目录的配置文件等。当你在项目中运行 ESLint ,所有 <code>lib/</code> 目录下的文件将会使用根目录下的 <code>.eslintrc</code> 文件作为它们的配置。如果 ESLint 进入 <code>test/</code> 目录,它将会使用 <code>your-project/tests/.eslintrc</code> 而不是 <code>your-project/.eslintrc</code> 。所以 <code>your-project/tests/test.js</code> 的检测是基于目录层次结构中的两个 <code>.eslintrc</code> 文件的组合的,近的拥有更高的优先级。这样的方式,你可以拥有项目级别的 ESLint 设置,并可以用目录层次的配置来覆盖它。</p>\n\n<p>相同的方式,在 <code>package.json</code> 文件中也可行。根目录中 <code>package.json</code> 的 <code>eslintConfig</code> 字段将在其所有子目录生效,但是在 tests 目录的 <code>.eslintrc</code> 文件将会覆盖所有冲突的配置。</p>\n\n<pre><code>your-project \n├── package.json\n├── lib\n│ └── source.js\n└─┬ tests\n ├── .eslintrc\n └── test.js\n</code></pre>\n\n<p>如果在一个目录中,同时找到了 <code>.eslintrc</code> 和 <code>package.json</code> 文件,<code>eslintrc</code> 文件将有更高的优先级, <code>package.json</code> 文件将不被使用。</p>\n\n<p>注意:如果你又一个个人的配置文件在你的用户目录( <code>~/.eslintrc</code> ),它将会仅仅在没有别的配置文件没发现的情况下使用。它将会对你所有用户目录下的文件生效,包括第三方的代码,因此在运行 ESLint 很可能会产生问题。</p>\n\n<p>默认地, ESLint 将会在所有父目录直到根目录寻找配置文件。这将是有用的,如果你想要所有你的项目去跟随某个约定,但是有时候会导致意想不到的结果。限制 ESLint 在一个指定的项目,把 <code>\"root\": true</code> 加到 <code>package.json</code> 的 <code>eslintConfig</code> 字段或者 <code>.eslintrc.*</code> 文件,在你的根目录。 ESLint 将会停止寻找父目录当它找到了 <code>\"root\": true</code> 的配置。</p>\n\n<pre><code>{\n \"root\": true\n}\n</code></pre>\n\n<p>YAML 文件中:</p>\n\n<pre><code>---\n root: true\n</code></pre>\n\n<p>举例来说, 这个例子当中 <code>main.js</code> 将会使用 <code>lib/</code> 中的配置,而不会使用 <code>productA</code> 目录下的 <code>.eslintrc</code> 文件的配置,因为在 <code>lib/</code> 中的 <code>.eslintrc</code> 有 <code>\"root\": true</code> 的设置。</p>\n\n<pre><code>home \n└── user\n ├── .eslintrc <- Always skipped if other configs present\n └── projectA\n ├── .eslintrc <- Not used\n └── lib\n ├── .eslintrc <- { \"root\": true }\n └── main.js\n</code></pre>\n\n<p>完整的配置层次,从高到低如下:</p>\n\n<ol>\n<li>行内配置 <br />\n<ol><li><code>/*eslint-disable*/</code> and <code>/*eslint-enable*/</code></li>\n<li><code>/*global*/</code></li>\n<li><code>/*eslint*/</code></li>\n<li><code>/*eslint-env*/</code></li></ol></li>\n<li>命令行配置 <br />\n<ol><li><code>--global</code></li>\n<li><code>--rule</code></li>\n<li><code>--env</code></li>\n<li><code>-c, --config</code></li></ol></li>\n<li>项目级别配置 <br />\n<ol><li><code>.eslintrc.*</code> 或 <code>package.json</code> 文件</li>\n<li>祖先目录中寻找,除非有 <code>\"root\": true</code> 将不会向上寻找</li>\n<li>个人某人的配置在 <code>~/.eslintrc</code></li></ol></li>\n</ol>\n\n<h2 id=\"\">扩展配置文件</h2>\n\n<p>如果你想要扩展一个指定的配置文件,你可以使用 <code>extends</code> 属性,并制定路径。可以是相对的或是绝对的路径。</p>\n\n<p>配置能够被如下文件扩展:</p>\n\n<ol>\n<li>YAML 文件 </li>\n<li>JSON 文件 </li>\n<li>JS 文件 </li>\n<li>共享的配置包</li>\n</ol>\n\n<p>扩展配置提供了基础的规则,并可以覆盖。举例:</p>\n\n<pre><code>{\n \"extends\": \"./node_modules/coding-standard/.eslintrc\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": 1\n }\n}\n</code></pre>\n\n<p>配置也许也可以以数组的方式提供,后面的会覆盖前面的相同的规则的配置。举例:</p>\n\n<pre><code>{\n \"extends\": [\n \"./node_modules/coding-standard/eslintDefaults.js\",\n // Override eslintDefaults.js\n \"./node_modules/coding-standard/.eslintrc-es6\",\n // Override .eslintrc-es6\n \"./node_modules/coding-standard/.eslintrc-jsx\",\n ],\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n</code></pre>\n\n<p>扩展配置能够包涵它们自己的 <code>extends</code> ,导致循环的引用。</p>\n\n<p>你也可以使用共享的配置包。你需要使用 npm 去安装它们,例如:</p>\n\n<pre><code>{\n \"extends\": \"eslint-config-myrules\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n</code></pre>\n\n<p>在这个例子, <code>eslint-config-myrules</code> 包将会加载为一个对象并作为这个配置的父配置。</p>\n\n<p>注意:你可以省略 <code>eslint-config-</code> 前缀, ESLint 将会自动为你添加,和插件工作相似。</p>\n\n<p>ESlint 也支持插件提供的扩展配置:</p>\n\n<pre><code>{\n \"extends\": \"plugin:eslint-plugin-myplugin/myConfig\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n</code></pre>\n\n<p>在这个例子, <code>eslint-plugin-myplugin</code> 包包含了名为 <code>default</code> 的配置。</p>\n\n<p><strong>十分重要</strong>:使用插件中的配置时,必须要加上 <code>plugin:</code> 前缀来指明哈。但你可以可选地省略 <code>eslint-plugin</code> 前缀。</p>\n\n<p>注意:对于根目录或者其它父目录, <code>extends</code> 处理路径会用当前工作目录,而不是文件自身。</p>\n\n<h2 id=\"\">配置文件中的注释</h2>\n\n<p>JSON 和 YAML 配置文件都支持注释, <code>package.json</code> 不能包括注释。你可以使用 JAVAScript 风格的注释或 YAML 风格的注释, ESLint 将安全地忽略它们。这可以允许你的配置文件跟友好。举例:</p>\n\n<pre><code>{\n \"env\": {\n \"browser\": true\n },\n \"rules\": {\n // Override our default settings just for this directory\n \"eqeqeq\": \"warn\",\n \"strict\": \"off\"\n }\n}\n</code></pre>\n\n<h2 id=\"\">指定需要检测的文件后缀</h2>\n\n<p>当前指定后缀需要在命令行选项 <code>--ext</code> 后,添加用空格分开的后缀名列表。</p>\n\n<h2 id=\"\">忽略文件或目录</h2>\n\n<p>你可以告诉 ESLint 去忽略指定的文件或目录,通过一个 <code>.eslintignore</code> 文件在你的根目录。 <code>.eslintignore</code> 文件是一个普通文本文件,每一行定义了要被忽略的文件或目录。举例:下列表达式将会省略所有 JavaScript 文件:</p>\n\n<pre><code>**/*.js\n</code></pre>\n\n<p>当 ESLint 运行的时候,它会寻找当前工作目录的 <code>.eslintignore</code> 文件在它决定监测之前。如果找到了文件,这里面的配置将会生效。 <code>.eslintignore</code> 只会被使用一次,因此目录中其他的 <code>.eslintignore</code> 将不会被使用。</p>\n\n<ul>\n<li>#好开头将会被认为是注释</li>\n<li>相对于 <code>.eslintignore</code> 文件路径或当前工作目录的路径</li>\n<li>忽略规则参照 <code>.gitignore</code> 规范</li>\n<li>!用来取消前面的忽略的匹配</li>\n</ul>\n\n<p><code>/node_modules/*</code> 和 <code>/bower_components/*</code> 将默认被忽略。</p>\n\n<p>举例,把下面的 <code>.eslintignore</code> 文件放倒工作目录下将会忽略<code>/node_modules/*</code> 和 <code>/bower_components/*</code> 目录,任何扩展名为 <code>.ts.js</code> 或 <code>.coffee.js</code> 可能被转换,任何在 <code>build/*</code> 目录除了 <code>build/index.js</code> 将被忽略:</p>\n\n<pre><code># /node_modules/* and /bower_components/* ignored by default\n\n# Ignore built files except build/index.js\nbuild/* \n!build/index.js\n</code></pre>\n\n<h2 id=\"\">使用替换的文件</h2>\n\n<p>如果你喜欢去使用别的文件而不是 <code>.eslintignore</code> 来工作,你在命令行中可以指定 <code>--ignore-path</code> 选项。举例来说,你可以使用 <code>.jshintignore</code> ,因为两者的配置相同:</p>\n\n<pre><code>eslint --ignore-path .jshintignore file.js \n</code></pre>\n\n<p>你也可以使用 <code>.gitignore</code> 文件:</p>\n\n<pre><code>eslint --ignore-path .gitignore file.js \n</code></pre>\n\n<p>任何标准的 ignore 文件可以被使用。</p>\n\n<h2 id=\"\">忽略文件警告</h2>\n\n<p>当你 eslint 一个被忽略的文件或目录时,会发出警告,你可以使用 <code>--no-ignore</code> 来进行省略。</p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464345663531,"created_by":1,"updated_at":1464770186759,"updated_by":1,"published_at":1464348992506,"published_by":1},{"id":12,"uuid":"d0ae47ae-bcbc-4333-93af-004ae853c2a2","title":"react 的虫洞 context(官方文档翻译)","slug":"react-de-chong-dong-context","markdown":"> 中文社区还停留在 0.14.7,文档上有不少落后了,比如 shadowEqual、context 等的知识,所以只能自己来了。\n\n#### Context\n\nReact 一个强大之处就是很容易去跟踪数据在组件中的流动。当你观察一个组件时,你能够容易地确切地看出什么 props 被传递,这使得你的 apps 能够容易地推导。\n\n偶尔,你想要去传递数据贯穿整个组件树,不想手动地一级级传递下去。React 的 \"context\" 特性帮助你做到这一点。(译外音:形象地描述就是像虫洞那样达到穿越效果,props 能够在自己共享,有点像全局变量)。\n\n> 注意: Context 是一个先进的、实验性的特性。这个 API 可能会在未来的发行版有所改变。\n\n> 大多数应用将不需要去使用 context。尤其如果你仅仅是初学 React ,你应当尽量不用 context。使用 context 将会试你的代码难以去理解,因为它使得数据的流动不清晰。它就像全局变量传递变量到整个应用。\n\n> 如果你不得不使用 context,请谨慎地使用。无论你是在在构建一个应用还是库,尝试去隔离 context 到一个小的区域和避免直接地使用 context API,这样可以在 API 改变时,方便地升级。\n\n#### 在组件树中自动地传递信息\n\n假定你有如下的结构:\n\n```\nclass Button extends React.Component {\n render() {\n return (\n <button style={{background: this.props.color}}>\n {this.props.children}\n </button>\n );\n }\n}\n\nclass Message extends React.Component {\n render() {\n return (\n <div>\n {this.props.text} <Button color={this.props.color}>Delete</Button>\n </div>\n );\n }\n}\n\nclass MessageList extends React.Component {\n render() {\n const color = \"purple\";\n const children = this.props.messages.map((message) =>\n <Message text={message.text} color={color} />\n );\n return <div>{children}</div>;\n }\n}\n```\n\n在这个例子里,我们为了合适地设置 Button 和 Message 的样式,手动传递一个 color prop 。主题是一个好的例子,当你想要让整个子级可以访问一些信息片段(如:一个颜色)。 使用 context ,你可以传递这些自动地。\n\n```\nclass Button extends React.Component {\n render() {\n return (\n <button style={{background: this.context.color}}>\n {this.props.children}\n </button>\n );\n }\n}\n\nButton.contextTypes = {\n color: React.PropTypes.string\n};\n\nclass Message extends React.Component {\n render() {\n return (\n <div>\n {this.props.text} <Button>Delete</Button>\n </div>\n );\n }\n}\n\nclass MessageList extends React.Component {\n getChildContext() {\n return {color: \"purple\"};\n }\n\n render() {\n const children = this.props.messages.map((message) =>\n <Message text={message.text} />\n );\n return <div>{children}</div>;\n }\n}\n\nMessageList.childContextTypes = {\n color: React.PropTypes.string\n};\n```\n\n通过在 MessageList(context的提供者) 添加 ` childContextTypes ` 和 ` getChildContext `, React 将会自动地传递信息下去,在所有的子组件中通过定义 ` contextTypes ` 就可以访问。\n\n如果 ` contextTypes ` 没有定义,context 会是一个空的对象。\n\n#### 父子耦合(这一段优点不明觉厉)\nContext 也能够让你创建一个 API 如下:\n\n```\n<Menu>\n <MenuItem>aubergine</MenuItem>\n <MenuItem>butternut squash</MenuItem>\n <MenuItem>clementine</MenuItem>\n</Menu>\n```\n\n通过在 Menu 组件传递相关的信息,每一个 MenuItem 能够传回 Menu 容器。\n\n在你使用这个 API 构建组件之前,考虑是否有更干净的替代方案。我们盲目的传递 items 作为一个数组,如下:\n\n```\n<Menu items={['aubergine', 'butternut squash', 'clementine']} />\n```\n\n再次提醒,你能够在 props 中传递整个 React 组件如果你喜欢。\n\n#### 在生命周期方法引用 context \n\n如果 ` contextTypes ` 被定义在一个组件中,下列生命周期方法将会收到额外的参数:\n\n```\nvoid componentWillReceiveProps(\n object nextProps, object nextContext\n)\n\nboolean shouldComponentUpdate(\n object nextProps, object nextState, object nextContext\n)\n\nvoid componentWillUpdate(\n object nextProps, object nextState, object nextContext\n)\n\nvoid componentDidUpdate(\n object prevProps, object prevState, object prevContext\n)\n```\n\n#### 在无状态函数组件引用 context \n\n无状态函数组件也能够引用 context ,如果 ` contextTypes ` 被定义为函数的参数。下列代码显示了基于无状态函数的 Button 组件。\n\n```\nconst Button = ({children}, context) =>\n <button style={{background: context.color}}>\n {children}\n </button>;\n\nButton.contextTypes = {color: React.PropTypes.string};\n```\n\n#### 更新 context\n当 state 或 props 改变, ` getChildContext ` 函数将被调用。为了更新 context 中的数据, 用 ` this.setState ` 触发一个本地的 state 的更新。这将会触发一个新的 context 然后子组件将会收到改变的 context。\n\n```\nclass MediaQuery extends React.Component {\n constructor(props) {\n super(props);\n this.state = {type:'desktop'};\n }\n\n getChildContext() {\n return {type: this.state.type};\n }\n\n componentDidMount() {\n const checkMediaQuery = () => {\n const type = window.matchMedia(\"(min-width: 1025px)\").matches ? 'desktop' : 'mobile';\n if (type !== this.state.type) {\n this.setState({type});\n }\n };\n\n window.addEventListener('resize', checkMediaQuery);\n checkMediaQuery();\n }\n\n render() {\n return this.props.children;\n }\n}\n\nMediaQuery.childContextTypes = {\n type: React.PropTypes.string\n};\n```\n\n#### 什么时候不该使用 context \n\n在写干净的代码时,全局变量是最好要避免的,你应当避免使用 context 在那多数情况下。尤其是在使用它去节省代码和代替明确的传递 props 时,要思考再三。\n\n最好的方式对于 context 是绝对地向下传递 登录用户、当前语言或主题信息等。所有这些除了全局变量,context 推荐你把它们规定在一个单一的 React 组件树内。\n\n在组件中不要使用 context 去传递你的模型数据。在整个树上明确地传递你的数据是更加容易去理解的。使用 context 使你的组件更多的耦合和更少的复用性,因为它们的渲染因所处的环境而不同。\n\n#### 已知的限制\n\n如果一个 context 的值被组件的改变提供,当中间组件使用了` shouldComponentUpdate ` 并返回了 `false\n ` 那么后代的值将不会改变。详见 github issue [#2517](https://github.com/facebook/react/issues/2517)\n\n","html":"<blockquote>\n <p>中文社区还停留在 0.14.7,文档上有不少落后了,比如 shadowEqual、context 等的知识,所以只能自己来了。</p>\n</blockquote>\n\n<h4 id=\"context\">Context</h4>\n\n<p>React 一个强大之处就是很容易去跟踪数据在组件中的流动。当你观察一个组件时,你能够容易地确切地看出什么 props 被传递,这使得你的 apps 能够容易地推导。</p>\n\n<p>偶尔,你想要去传递数据贯穿整个组件树,不想手动地一级级传递下去。React 的 \"context\" 特性帮助你做到这一点。(译外音:形象地描述就是像虫洞那样达到穿越效果,props 能够在自己共享,有点像全局变量)。</p>\n\n<blockquote>\n <p>注意: Context 是一个先进的、实验性的特性。这个 API 可能会在未来的发行版有所改变。</p>\n \n <p>大多数应用将不需要去使用 context。尤其如果你仅仅是初学 React ,你应当尽量不用 context。使用 context 将会试你的代码难以去理解,因为它使得数据的流动不清晰。它就像全局变量传递变量到整个应用。</p>\n \n <p>如果你不得不使用 context,请谨慎地使用。无论你是在在构建一个应用还是库,尝试去隔离 context 到一个小的区域和避免直接地使用 context API,这样可以在 API 改变时,方便地升级。</p>\n</blockquote>\n\n<h4 id=\"\">在组件树中自动地传递信息</h4>\n\n<p>假定你有如下的结构:</p>\n\n<pre><code>class Button extends React.Component { \n render() {\n return (\n <button style={{background: this.props.color}}>\n {this.props.children}\n </button>\n );\n }\n}\n\nclass Message extends React.Component { \n render() {\n return (\n <div>\n {this.props.text} <Button color={this.props.color}>Delete</Button>\n </div>\n );\n }\n}\n\nclass MessageList extends React.Component { \n render() {\n const color = \"purple\";\n const children = this.props.messages.map((message) =>\n <Message text={message.text} color={color} />\n );\n return <div>{children}</div>;\n }\n}\n</code></pre>\n\n<p>在这个例子里,我们为了合适地设置 Button 和 Message 的样式,手动传递一个 color prop 。主题是一个好的例子,当你想要让整个子级可以访问一些信息片段(如:一个颜色)。 使用 context ,你可以传递这些自动地。</p>\n\n<pre><code>class Button extends React.Component { \n render() {\n return (\n <button style={{background: this.context.color}}>\n {this.props.children}\n </button>\n );\n }\n}\n\nButton.contextTypes = { \n color: React.PropTypes.string\n};\n\nclass Message extends React.Component { \n render() {\n return (\n <div>\n {this.props.text} <Button>Delete</Button>\n </div>\n );\n }\n}\n\nclass MessageList extends React.Component { \n getChildContext() {\n return {color: \"purple\"};\n }\n\n render() {\n const children = this.props.messages.map((message) =>\n <Message text={message.text} />\n );\n return <div>{children}</div>;\n }\n}\n\nMessageList.childContextTypes = { \n color: React.PropTypes.string\n};\n</code></pre>\n\n<p>通过在 MessageList(context的提供者) 添加 <code>childContextTypes</code> 和 <code>getChildContext</code>, React 将会自动地传递信息下去,在所有的子组件中通过定义 <code>contextTypes</code> 就可以访问。</p>\n\n<p>如果 <code>contextTypes</code> 没有定义,context 会是一个空的对象。</p>\n\n<h4 id=\"\">父子耦合(这一段优点不明觉厉)</h4>\n\n<p>Context 也能够让你创建一个 API 如下:</p>\n\n<pre><code><Menu> \n <MenuItem>aubergine</MenuItem>\n <MenuItem>butternut squash</MenuItem>\n <MenuItem>clementine</MenuItem>\n</Menu> \n</code></pre>\n\n<p>通过在 Menu 组件传递相关的信息,每一个 MenuItem 能够传回 Menu 容器。</p>\n\n<p>在你使用这个 API 构建组件之前,考虑是否有更干净的替代方案。我们盲目的传递 items 作为一个数组,如下:</p>\n\n<pre><code><Menu items={['aubergine', 'butternut squash', 'clementine']} /> \n</code></pre>\n\n<p>再次提醒,你能够在 props 中传递整个 React 组件如果你喜欢。</p>\n\n<h4 id=\"context\">在生命周期方法引用 context</h4>\n\n<p>如果 <code>contextTypes</code> 被定义在一个组件中,下列生命周期方法将会收到额外的参数:</p>\n\n<pre><code>void componentWillReceiveProps( \n object nextProps, object nextContext\n)\n\nboolean shouldComponentUpdate( \n object nextProps, object nextState, object nextContext\n)\n\nvoid componentWillUpdate( \n object nextProps, object nextState, object nextContext\n)\n\nvoid componentDidUpdate( \n object prevProps, object prevState, object prevContext\n)\n</code></pre>\n\n<h4 id=\"context\">在无状态函数组件引用 context</h4>\n\n<p>无状态函数组件也能够引用 context ,如果 <code>contextTypes</code> 被定义为函数的参数。下列代码显示了基于无状态函数的 Button 组件。</p>\n\n<pre><code>const Button = ({children}, context) => \n <button style={{background: context.color}}>\n {children}\n </button>;\n\nButton.contextTypes = {color: React.PropTypes.string}; \n</code></pre>\n\n<h4 id=\"context\">更新 context</h4>\n\n<p>当 state 或 props 改变, <code>getChildContext</code> 函数将被调用。为了更新 context 中的数据, 用 <code>this.setState</code> 触发一个本地的 state 的更新。这将会触发一个新的 context 然后子组件将会收到改变的 context。</p>\n\n<pre><code>class MediaQuery extends React.Component { \n constructor(props) {\n super(props);\n this.state = {type:'desktop'};\n }\n\n getChildContext() {\n return {type: this.state.type};\n }\n\n componentDidMount() {\n const checkMediaQuery = () => {\n const type = window.matchMedia(\"(min-width: 1025px)\").matches ? 'desktop' : 'mobile';\n if (type !== this.state.type) {\n this.setState({type});\n }\n };\n\n window.addEventListener('resize', checkMediaQuery);\n checkMediaQuery();\n }\n\n render() {\n return this.props.children;\n }\n}\n\nMediaQuery.childContextTypes = { \n type: React.PropTypes.string\n};\n</code></pre>\n\n<h4 id=\"context\">什么时候不该使用 context</h4>\n\n<p>在写干净的代码时,全局变量是最好要避免的,你应当避免使用 context 在那多数情况下。尤其是在使用它去节省代码和代替明确的传递 props 时,要思考再三。</p>\n\n<p>最好的方式对于 context 是绝对地向下传递 登录用户、当前语言或主题信息等。所有这些除了全局变量,context 推荐你把它们规定在一个单一的 React 组件树内。</p>\n\n<p>在组件中不要使用 context 去传递你的模型数据。在整个树上明确地传递你的数据是更加容易去理解的。使用 context 使你的组件更多的耦合和更少的复用性,因为它们的渲染因所处的环境而不同。</p>\n\n<h4 id=\"\">已知的限制</h4>\n\n<p>如果一个 context 的值被组件的改变提供,当中间组件使用了<code>shouldComponentUpdate</code> 并返回了 <code>false\n</code> 那么后代的值将不会改变。详见 github issue <a href=\"https://github.com/facebook/react/issues/2517\">#2517</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464684672461,"created_by":1,"updated_at":1467696306538,"updated_by":1,"published_at":1467685948549,"published_by":1},{"id":13,"uuid":"e38601e3-c8d0-4448-9fae-9f792e249b1a","title":"react native 踩坑实录","slug":"react-native-keng","markdown":"### 持续更新。。。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=26402069&auto=1&height=66\"></iframe>\n\n1、 [Android] StatusBar Translucent not working in Android 4.x.x after upgrading to react 0.23 [#6876](https://github.com/facebook/react-native/issues/6876)\n\n> solution: I changed the implementation of translucent and it only works on API 21+ now. Can you just not add top padding on Android 4.4- using Platform.Version < 21?\n\n2、 在使用pure-render相关的组件时,尽量不要使用箭头函数或者是行内的bind,因为这两种方式都是会返回一个新的实例,从而导致re-render。\n\n> solution: bind function in the constructor.\n\n3、 定时器在rn中使用还是比较广泛的,但是 timeMixin 暂时不支持在 es6 class 中的使用,于是去github看了下issue,官网也推荐使用 reactMixin 将 mixin 绑定到 es6 class ,并亲自测试了一下,通过。中文网也给出了手动 clear 的方案,相比之下复用性低,其实就是不用 mixin 的方案,但是依赖比较少,各有优势吧!\n\n```\nimport React, { Component } from 'react-native';\nimport TimerMixin from 'react-timer-mixin';\nimport reactMixin from 'react-mixin';\nclass MyClass extends Component {\n componentDidMount() {\n this.setTimeout(\n () => { console.log('tick'); },\n 5000\n );\n }\n}\nreactMixin(MyClass.prototype, TimerMixin);\nmodule.exports = MyClass;\n```\n\n4、 这两天集成 redux 时,发现rn的 ListView 经常不更新,于是深入到了源码内一看了究竟。看看是什么时候 ListView 会更新row。[有点多,新开了一篇](http://m2mbob.cn:1314/listview-row-update/)\n\n5、 不知道哪个版本 ListView 引入了 ` enableEmptySections `属性,这个属性只能有一个值true,为true时,会渲染没有 ` row ` 的 ` section ` 。下面这段代码可以看到,这个属性将在之后的版本不被推荐使用,官方建议在传参数时把空的 ` section ` 过滤掉。不过在实践过程中发现空数组实际上也会构造一个 ` section ` ,导致 warning ,强迫症想把它去掉啊!😂 \n```\nif (rowIDs.length === 0) {\n if (this.props.enableEmptySections === undefined) {\n var warning = require('fbjs/lib/warning');\n warning(false, 'In next release empty section headers will be rendered.'\n + ' In this release you can use \\'enableEmptySections\\' flag to render empty section headers.');\n continue;\n } else {\n var invariant = require('fbjs/lib/invariant');\n invariant(\n this.props.enableEmptySections,\n 'In next release \\'enableEmptySections\\' flag will be deprecated, empty section headers will always be rendered.'\n + ' If empty section headers are not desirable their indices should be excluded from sectionIDs object.'\n + ' In this release \\'enableEmptySections\\' may only have value \\'true\\' to allow empty section headers rendering.');\n }\n }\n```\n\n6、 对于 TouchableHighlight 组件,会出现没有效果的情况,解决方案是在外面包一层背景。\n\n7、 ios 模拟器点击无效的解决方案,在模拟器的Debug菜单中,关掉Slow Animations就好了。关掉Slow Animations之后,模拟器响应的都快了,非常好用,建议模拟器这一项不要开着。\n\n8、 这个不是 react native 本身的问题,而是在使用 react-native-vector-icons 的 ToolBarAndroid 组件过程中遇到的问题。首先 actions 的 title字段是必须的,其次是在 react-native-vector-icons 当中不是使用 icon 而是使用 iconName, iconColor, iconSize 这三个参数!! 😒\n\n9、 今天拿出自己的安卓机测流程时发现,文本在安卓上的 padding 不见了,然后去 github ,果然有道友提出了相同的问题[#7848](https://github.com/facebook/react-native/issues/7848#issuecomment-233517201)。不过 margin 是工作良好的,所以暂时先用 margin 替换了原来的 padding。\n\n10、 TouchableNativeFeedback 组件只支持 个人 View 作为子元素,其他元素将无法显示,多个元素则会报错。\n","html":"<h3 id=\"\">持续更新。。。</h3>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=26402069&auto=1&height=66\"></iframe>\n\n<p>1、 [Android] StatusBar Translucent not working in Android 4.x.x after upgrading to react 0.23 <a href=\"https://github.com/facebook/react-native/issues/6876\">#6876</a></p>\n\n<blockquote>\n <p>solution: I changed the implementation of translucent and it only works on API 21+ now. Can you just not add top padding on Android 4.4- using Platform.Version < 21?</p>\n</blockquote>\n\n<p>2、 在使用pure-render相关的组件时,尽量不要使用箭头函数或者是行内的bind,因为这两种方式都是会返回一个新的实例,从而导致re-render。</p>\n\n<blockquote>\n <p>solution: bind function in the constructor.</p>\n</blockquote>\n\n<p>3、 定时器在rn中使用还是比较广泛的,但是 timeMixin 暂时不支持在 es6 class 中的使用,于是去github看了下issue,官网也推荐使用 reactMixin 将 mixin 绑定到 es6 class ,并亲自测试了一下,通过。中文网也给出了手动 clear 的方案,相比之下复用性低,其实就是不用 mixin 的方案,但是依赖比较少,各有优势吧!</p>\n\n<pre><code>import React, { Component } from 'react-native'; \nimport TimerMixin from 'react-timer-mixin'; \nimport reactMixin from 'react-mixin'; \nclass MyClass extends Component { \n componentDidMount() {\n this.setTimeout(\n () => { console.log('tick'); },\n 5000\n );\n }\n}\nreactMixin(MyClass.prototype, TimerMixin); \nmodule.exports = MyClass; \n</code></pre>\n\n<p>4、 这两天集成 redux 时,发现rn的 ListView 经常不更新,于是深入到了源码内一看了究竟。看看是什么时候 ListView 会更新row。<a href=\"http://m2mbob.cn:1314/listview-row-update/\">有点多,新开了一篇</a></p>\n\n<p>5、 不知道哪个版本 ListView 引入了 <code>enableEmptySections</code>属性,这个属性只能有一个值true,为true时,会渲染没有 <code>row</code> 的 <code>section</code> 。下面这段代码可以看到,这个属性将在之后的版本不被推荐使用,官方建议在传参数时把空的 <code>section</code> 过滤掉。不过在实践过程中发现空数组实际上也会构造一个 <code>section</code> ,导致 warning ,强迫症想把它去掉啊!😂 </p>\n\n<pre><code>if (rowIDs.length === 0) { \n if (this.props.enableEmptySections === undefined) {\n var warning = require('fbjs/lib/warning');\n warning(false, 'In next release empty section headers will be rendered.'\n + ' In this release you can use \\'enableEmptySections\\' flag to render empty section headers.');\n continue;\n } else {\n var invariant = require('fbjs/lib/invariant');\n invariant(\n this.props.enableEmptySections,\n 'In next release \\'enableEmptySections\\' flag will be deprecated, empty section headers will always be rendered.'\n + ' If empty section headers are not desirable their indices should be excluded from sectionIDs object.'\n + ' In this release \\'enableEmptySections\\' may only have value \\'true\\' to allow empty section headers rendering.');\n }\n }\n</code></pre>\n\n<p>6、 对于 TouchableHighlight 组件,会出现没有效果的情况,解决方案是在外面包一层背景。</p>\n\n<p>7、 ios 模拟器点击无效的解决方案,在模拟器的Debug菜单中,关掉Slow Animations就好了。关掉Slow Animations之后,模拟器响应的都快了,非常好用,建议模拟器这一项不要开着。</p>\n\n<p>8、 这个不是 react native 本身的问题,而是在使用 react-native-vector-icons 的 ToolBarAndroid 组件过程中遇到的问题。首先 actions 的 title字段是必须的,其次是在 react-native-vector-icons 当中不是使用 icon 而是使用 iconName, iconColor, iconSize 这三个参数!! 😒</p>\n\n<p>9、 今天拿出自己的安卓机测流程时发现,文本在安卓上的 padding 不见了,然后去 github ,果然有道友提出了相同的问题<a href=\"https://github.com/facebook/react-native/issues/7848#issuecomment-233517201\">#7848</a>。不过 margin 是工作良好的,所以暂时先用 margin 替换了原来的 padding。</p>\n\n<p>10、 TouchableNativeFeedback 组件只支持 个人 View 作为子元素,其他元素将无法显示,多个元素则会报错。</p>","image":null,"featured":1,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464770450231,"created_by":1,"updated_at":1468908579682,"updated_by":1,"published_at":1466558940000,"published_by":1},{"id":14,"uuid":"5b126970-3765-4b5e-9e06-920981bfd050","title":"Immutable.js 文档翻译(API部分)","slug":"immutable-js-wen-dang-fan-yi-apibu-fen","markdown":"不可变数据鼓励纯函数(相同的数据输入得到相同的输出),适用于更加简单的应用开发,并支持延迟计算等函数式编程的技术。\n\n当设计把这些强大的功能性概念带给 ` JavaScript ` 时,它提出了一个 ` JavaScript ` 工程师熟悉的面向对象的 API并十分接近地反映了 ` Array ` , ` Map\n ` 和 ` Set ` 等数据结构。它能够很容易并高效地与普通 ` JavaScript ` 基本类型转换。 \n\n注意:所有的例子用 [ES6](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla)。为了在所有浏览器中运行,它们需要呗专为 ES3 。例如:\n\n```\n// ES6\nfoo.map(x => x * x);\n// ES3\nfoo.map(function (x) { return x * x; });\n```\n\n#### API\n\n[fromJS()](www.baidu.com)\n\n把普通的 JS 对象和数组深度转换为不可变的 Maps 和 Lists。\n\n[is()](www.baidu.com)\n\n比较直是否相等,类似于 ES6 新引入的 [Object.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) ,但是它把不可变的 [Iterable](www.baidu.com) 当作值,如果第二个 [Iterable](www.baidu.com) 包含相等的值就相等。\n\n[List](www.baidu.com)\n\nLists 是排序索引密集的集合,与 ` JavaScript ` 的 ` Array ` 十分相似。 \n\n[Map](www.baidu.com)\n\n不可变的 Map ,获取和设置的复杂度为 ` O(log32 N) ` 。 \n\n[OrderedMap](www.baidu.com)\n\n一种保证了内容顺序与 set() 顺序一致的 Map。 \n\n[Set](www.baidu.com)\n\n唯一值集合,添加和判断存在复杂度为 ` O(log32 N) ` 。 \n\n[OrderedSet](www.baidu.com)\n\n一种保证了内容顺序与 add() 顺序一致的 Set。\n\n[Stack](www.baidu.com)\n\n支持高效的添加和和删除的索引集合,栈。\n\n[Range()](www.baidu.com)","html":"<p>不可变数据鼓励纯函数(相同的数据输入得到相同的输出),适用于更加简单的应用开发,并支持延迟计算等函数式编程的技术。</p>\n\n<p>当设计把这些强大的功能性概念带给 <code>JavaScript</code> 时,它提出了一个 <code>JavaScript</code> 工程师熟悉的面向对象的 API并十分接近地反映了 <code>Array</code> , <code>Map\n</code> 和 <code>Set</code> 等数据结构。它能够很容易并高效地与普通 <code>JavaScript</code> 基本类型转换。 </p>\n\n<p>注意:所有的例子用 <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla\">ES6</a>。为了在所有浏览器中运行,它们需要呗专为 ES3 。例如:</p>\n\n<pre><code>// ES6\nfoo.map(x => x * x); \n// ES3\nfoo.map(function (x) { return x * x; }); \n</code></pre>\n\n<h4 id=\"api\">API</h4>\n\n<p><a href=\"www.baidu.com\">fromJS()</a></p>\n\n<p>把普通的 JS 对象和数组深度转换为不可变的 Maps 和 Lists。</p>\n\n<p><a href=\"www.baidu.com\">is()</a></p>\n\n<p>比较直是否相等,类似于 ES6 新引入的 <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is\">Object.is</a> ,但是它把不可变的 <a href=\"www.baidu.com\">Iterable</a> 当作值,如果第二个 <a href=\"www.baidu.com\">Iterable</a> 包含相等的值就相等。</p>\n\n<p><a href=\"www.baidu.com\">List</a></p>\n\n<p>Lists 是排序索引密集的集合,与 <code>JavaScript</code> 的 <code>Array</code> 十分相似。 </p>\n\n<p><a href=\"www.baidu.com\">Map</a></p>\n\n<p>不可变的 Map ,获取和设置的复杂度为 <code>O(log32 N)</code> 。 </p>\n\n<p><a href=\"www.baidu.com\">OrderedMap</a></p>\n\n<p>一种保证了内容顺序与 set() 顺序一致的 Map。 </p>\n\n<p><a href=\"www.baidu.com\">Set</a></p>\n\n<p>唯一值集合,添加和判断存在复杂度为 <code>O(log32 N)</code> 。 </p>\n\n<p><a href=\"www.baidu.com\">OrderedSet</a></p>\n\n<p>一种保证了内容顺序与 add() 顺序一致的 Set。</p>\n\n<p><a href=\"www.baidu.com\">Stack</a></p>\n\n<p>支持高效的添加和和删除的索引集合,栈。</p>\n\n<p><a href=\"www.baidu.com\">Range()</a></p>","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1466583064760,"created_by":1,"updated_at":1466588205800,"updated_by":1,"published_at":null,"published_by":null},{"id":15,"uuid":"d84b75b7-f57b-4d44-abee-7ba5a9c92cc3","title":"深入RN ListView源码,了解 row 更新机制","slug":"listview-row-update","markdown":"> version 0.25.1\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=31445772&auto=1&height=66\"></iframe>\n\n打开`/node_modules/react-native/Libraries/CustomComponents/ListView` 文件夹,下面有两个js,分别为 ` ListView ` 和 ` ListViewDataSource `。首先看 ` ListView ` 321 行:\n```\n componentWillReceiveProps: function(nextProps) {\n // 只有传入了新的dataSource或者initialListSize改变时才会重新渲染整个列表\n if (this.props.dataSource !== nextProps.dataSource ||\n this.props.initialListSize !== nextProps.initialListSize) {\n this.setState((state, props) => {\n // 同一个dataSource上一次以渲染的行数置为0\n this._prevRenderedRowsCount = 0;\n return {\n // 第一次要渲染的行数\n curRenderedRowsCount: Math.min(\n Math.max(\n state.curRenderedRowsCount,\n props.initialListSize\n ),\n props.enableEmptySections ? props.dataSource.getRowAndSectionCount() : props.dataSource.getRowCount()\n ),\n };\n }, () => this._renderMoreRowsIfNeeded());\n }\n },\n```\n其次是 `render` 函数:\n```\nfor (var rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) {\n var rowID = rowIDs[rowIdx];\n var comboID = sectionID + '_' + rowID;\n // 当前行数大于已经渲染的行数且dataSource.rowShouldUpdate为true才进行新的一行的渲染。\n var shouldUpdateRow = rowCount >= this._prevRenderedRowsCount &&\n dataSource.rowShouldUpdate(sectionIdx, rowIdx);\n var row =\n <StaticRenderer\n key={'r_' + comboID}\n shouldUpdate={!!shouldUpdateRow}\n render={this.props.renderRow.bind(\n null,\n dataSource.getRowData(sectionIdx, rowIdx),\n sectionID,\n rowID,\n this._onRowHighlighted\n )}\n />;\n bodyComponents.push(row);\n totalIndex++;\n```\n最后就要看看 ` dataSource.rowShouldUpdate ` 发生了什么,打开 ` ListViewDataSource ` 236行:\n```\nrowShouldUpdate(sectionIndex: number, rowIndex: number): bool {\n // 尼玛_dirtyRows是啥\n var needsUpdate = this._dirtyRows[sectionIndex][rowIndex];\n warning(needsUpdate !== undefined,\n 'missing dirtyBit for section, row: ' + sectionIndex + ', ' + rowIndex);\n return needsUpdate;\n }\n```\n再到376行吧:\n```\n this._dirtyRows[sIndex] = [];\n for (var rIndex = 0; rIndex < this.rowIdentities[sIndex].length; rIndex++) {\n var rowID = this.rowIdentities[sIndex][rIndex];\n // 如果sectionID、rowID是新的或者调用我们定义的rowHasChanged返回是true的话就是脏的,就需要更新。\n dirty =\n !prevSectionsHash[sectionID] ||\n !prevRowsHash[sectionID][rowID] ||\n this._rowHasChanged(\n this._getRowData(prevDataBlob, sectionID, rowID),\n this._getRowData(this._dataBlob, sectionID, rowID)\n );\n this._dirtyRows[sIndex].push(!!dirty);\n }\n```\n到这里我终于发现自己的 row 为何不更新了,我们平时写的 ` rowHasChanged `方法都是直接 `===` 比较的,如果内容变了引用没变比较返回的就是 ` true ` 。这就要追溯到我在 reselect 中的操作了,我只改变了 列表数据的引用,而没有改变其中每一项数据的引用,所以在 ` rowHasChanged ` 比较时出了问题。不过改变引用之后,新的问题又来了, ` rowHasChanged `无论如何返回的都是true,然后每次都会更新,这是无法接受的。临时解决方案是,使用 fbjs 中的 ` shadowEqual ` 进行比较,而不是简单的 ` === `。 `shadowEqual` 只会比较第一层的属性,还不是最好的方案,最好的方案我认为是引入 `immutablejs`。","html":"<blockquote>\n <p>version 0.25.1</p>\n</blockquote>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=31445772&auto=1&height=66\"></iframe>\n\n<p>打开<code>/node_modules/react-native/Libraries/CustomComponents/ListView</code> 文件夹,下面有两个js,分别为 <code>ListView</code> 和 <code>ListViewDataSource</code>。首先看 <code>ListView</code> 321 行:</p>\n\n<pre><code> componentWillReceiveProps: function(nextProps) {\n // 只有传入了新的dataSource或者initialListSize改变时才会重新渲染整个列表\n if (this.props.dataSource !== nextProps.dataSource ||\n this.props.initialListSize !== nextProps.initialListSize) {\n this.setState((state, props) => {\n // 同一个dataSource上一次以渲染的行数置为0\n this._prevRenderedRowsCount = 0;\n return {\n // 第一次要渲染的行数\n curRenderedRowsCount: Math.min(\n Math.max(\n state.curRenderedRowsCount,\n props.initialListSize\n ),\n props.enableEmptySections ? props.dataSource.getRowAndSectionCount() : props.dataSource.getRowCount()\n ),\n };\n }, () => this._renderMoreRowsIfNeeded());\n }\n },\n</code></pre>\n\n<p>其次是 <code>render</code> 函数:</p>\n\n<pre><code>for (var rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) { \n var rowID = rowIDs[rowIdx];\n var comboID = sectionID + '_' + rowID;\n // 当前行数大于已经渲染的行数且dataSource.rowShouldUpdate为true才进行新的一行的渲染。\n var shouldUpdateRow = rowCount >= this._prevRenderedRowsCount &&\n dataSource.rowShouldUpdate(sectionIdx, rowIdx);\n var row =\n <StaticRenderer\n key={'r_' + comboID}\n shouldUpdate={!!shouldUpdateRow}\n render={this.props.renderRow.bind(\n null,\n dataSource.getRowData(sectionIdx, rowIdx),\n sectionID,\n rowID,\n this._onRowHighlighted\n )}\n />;\n bodyComponents.push(row);\n totalIndex++;\n</code></pre>\n\n<p>最后就要看看 <code>dataSource.rowShouldUpdate</code> 发生了什么,打开 <code>ListViewDataSource</code> 236行:</p>\n\n<pre><code>rowShouldUpdate(sectionIndex: number, rowIndex: number): bool { \n // 尼玛_dirtyRows是啥\n var needsUpdate = this._dirtyRows[sectionIndex][rowIndex];\n warning(needsUpdate !== undefined,\n 'missing dirtyBit for section, row: ' + sectionIndex + ', ' + rowIndex);\n return needsUpdate;\n }\n</code></pre>\n\n<p>再到376行吧:</p>\n\n<pre><code> this._dirtyRows[sIndex] = [];\n for (var rIndex = 0; rIndex < this.rowIdentities[sIndex].length; rIndex++) {\n var rowID = this.rowIdentities[sIndex][rIndex];\n // 如果sectionID、rowID是新的或者调用我们定义的rowHasChanged返回是true的话就是脏的,就需要更新。\n dirty =\n !prevSectionsHash[sectionID] ||\n !prevRowsHash[sectionID][rowID] ||\n this._rowHasChanged(\n this._getRowData(prevDataBlob, sectionID, rowID),\n this._getRowData(this._dataBlob, sectionID, rowID)\n );\n this._dirtyRows[sIndex].push(!!dirty);\n }\n</code></pre>\n\n<p>到这里我终于发现自己的 row 为何不更新了,我们平时写的 <code>rowHasChanged</code>方法都是直接 <code>===</code> 比较的,如果内容变了引用没变比较返回的就是 <code>true</code> 。这就要追溯到我在 reselect 中的操作了,我只改变了 列表数据的引用,而没有改变其中每一项数据的引用,所以在 <code>rowHasChanged</code> 比较时出了问题。不过改变引用之后,新的问题又来了, <code>rowHasChanged</code>无论如何返回的都是true,然后每次都会更新,这是无法接受的。临时解决方案是,使用 fbjs 中的 <code>shadowEqual</code> 进行比较,而不是简单的 <code>===</code>。 <code>shadowEqual</code> 只会比较第一层的属性,还不是最好的方案,最好的方案我认为是引入 <code>immutablejs</code>。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1466662979518,"created_by":1,"updated_at":1466845709975,"updated_by":1,"published_at":1466843081339,"published_by":1},{"id":16,"uuid":"00f8ea34-3b29-434c-a49c-c4f0fab84839","title":"关于 ES 草案","slug":"guan-yu-es-cao-an","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=414611126&auto=1&height=66\"></iframe>\n\nbabel6 模块化之后,使用 Presets 来引入 babel 转译时需要支持的特性,此时我们能够看到几个 stage-x 的 persets,一脸懵逼,于是就去了解了一下这是啥。\n\n这一切要从 ES 草案的制定说起。ECMA的第39号技术专家委员会(Technical Committee 39,简称TC39)负责制订ECMAScript标准,成员包括Microsoft、Mozilla、Google等大公司。\n\n任何人都可以向TC39提案,从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都需要由TC39委员会批准。\n\n- Stage 0 - Strawman(展示阶段)\n- Stage 1 - Proposal(征求意见阶段)\n- Stage 2 - Draft(草案阶段)\n- Stage 3 - Candidate(候选人阶段)\n- Stage 4 - Finished(定案阶段) \n\n我们可以在 github 这个仓库查看各个阶段有哪些提案,[各阶段提案](https://github.com/tc39/proposals)。我们可以看到 ` Async Functions ` 已经进入 Stage 3 了呢,😬,这意味着离它进入 ES 规范的日子不远了,不过今年已经的新特性已经发布了哈哈,明年见。今年的规范更新了 ` Array.includes ` 和 指数两个特性。至于为什么今年相较于去年只更新了两个特性,可以看知乎这个回答,相当详细啊[如何评价 ECMAScript 2016(ES7)只新增2个特性?](http://www.zhihu.com/question/39993685)","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=414611126&auto=1&height=66\"></iframe>\n\n<p>babel6 模块化之后,使用 Presets 来引入 babel 转译时需要支持的特性,此时我们能够看到几个 stage-x 的 persets,一脸懵逼,于是就去了解了一下这是啥。</p>\n\n<p>这一切要从 ES 草案的制定说起。ECMA的第39号技术专家委员会(Technical Committee 39,简称TC39)负责制订ECMAScript标准,成员包括Microsoft、Mozilla、Google等大公司。</p>\n\n<p>任何人都可以向TC39提案,从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都需要由TC39委员会批准。</p>\n\n<ul>\n<li>Stage 0 - Strawman(展示阶段)</li>\n<li>Stage 1 - Proposal(征求意见阶段)</li>\n<li>Stage 2 - Draft(草案阶段)</li>\n<li>Stage 3 - Candidate(候选人阶段)</li>\n<li>Stage 4 - Finished(定案阶段) </li>\n</ul>\n\n<p>我们可以在 github 这个仓库查看各个阶段有哪些提案,<a href=\"https://github.com/tc39/proposals\">各阶段提案</a>。我们可以看到 <code>Async Functions</code> 已经进入 Stage 3 了呢,😬,这意味着离它进入 ES 规范的日子不远了,不过今年已经的新特性已经发布了哈哈,明年见。今年的规范更新了 <code>Array.includes</code> 和 指数两个特性。至于为什么今年相较于去年只更新了两个特性,可以看知乎这个回答,相当详细啊<a href=\"http://www.zhihu.com/question/39993685\">如何评价 ECMAScript 2016(ES7)只新增2个特性?</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1466844324720,"created_by":1,"updated_at":1466845592657,"updated_by":1,"published_at":1466845477628,"published_by":1},{"id":17,"uuid":"cf9bc202-eb88-46f0-ad4c-a31097b78801","title":"最后一次了我的宝贝","slug":"zui-hou-yi-ci-liao-wo-de-bao-bei-2","markdown":"疯疯癫癫了一晚,四瓶酒下去,本以为能让自己睡个好觉,但是不到十二点酒醒来,却再也睡不着了,一遍遍循环着张悬翻唱的 you'll see。想写的很多,还是留在心底吧,花几个不眠之夜慢慢地消化。\n\n谢谢你,我的宝贝,这是最后一次这么叫了。今天的你还是那么地率真、勇敢,我也还是爱你的,但是却没有理由挽留你了,我知道你已经不那么爱我了,我是那么的无奈,像个小丑说这些无聊地话。\n\n不过还是谢谢了,谢谢你给了我这么好的时光,在你身上我学到了很多,祝你幸福!为什么把你删了,是我怕自己控制不值,我还是爱你的啊!还有这首 you'll see 送给你也送给我自己。晚安!最后一次了!应该听不到了吧!\n\n还有,应该说对不起的是我,没有能够兑现我的承诺,没有给你想要的爱情,对不起!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=26590183&auto=1&height=66\"></iframe>","html":"<p>疯疯癫癫了一晚,四瓶酒下去,本以为能让自己睡个好觉,但是不到十二点酒醒来,却再也睡不着了,一遍遍循环着张悬翻唱的 you'll see。想写的很多,还是留在心底吧,花几个不眠之夜慢慢地消化。</p>\n\n<p>谢谢你,我的宝贝,这是最后一次这么叫了。今天的你还是那么地率真、勇敢,我也还是爱你的,但是却没有理由挽留你了,我知道你已经不那么爱我了,我是那么的无奈,像个小丑说这些无聊地话。</p>\n\n<p>不过还是谢谢了,谢谢你给了我这么好的时光,在你身上我学到了很多,祝你幸福!为什么把你删了,是我怕自己控制不值,我还是爱你的啊!还有这首 you'll see 送给你也送给我自己。晚安!最后一次了!应该听不到了吧!</p>\n\n<p>还有,应该说对不起的是我,没有能够兑现我的承诺,没有给你想要的爱情,对不起!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=26590183&auto=1&height=66\"></iframe>","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467047961295,"created_by":1,"updated_at":1467050137342,"updated_by":1,"published_at":1467048907781,"published_by":1},{"id":19,"uuid":"3cdea6e0-1225-42e2-bf91-42bd5bf05cb1","title":"脑洞和感动","slug":"nao-dong-he-gan-dong","markdown":"<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=3138291&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>","html":"<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=3138291&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467110745929,"created_by":1,"updated_at":1467111347667,"updated_by":1,"published_at":1467111153989,"published_by":1},{"id":20,"uuid":"217d0fe2-7ee8-44bc-9e26-e5688017cd83","title":"无题","slug":"wu-ti","markdown":"养不了你了,祝你幸福,找到那个‘尹天仇’!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=27876900&auto=1&height=66\"></iframe>","html":"<p>养不了你了,祝你幸福,找到那个‘尹天仇’!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=27876900&auto=1&height=66\"></iframe>","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467111739636,"created_by":1,"updated_at":1467120521732,"updated_by":1,"published_at":1467111989657,"published_by":1},{"id":21,"uuid":"e2fa0f4a-0bcf-49de-9520-ee74d98f21f5","title":"关于色彩搭配(基础知识篇)","slug":"guan-yu-se-cai-da-pei","markdown":"App写得差不多了,感觉其中的色彩的运用比较乱,没有统一起来,于是花了一个下午,学习了一下色彩搭配相关的一些知识,准备统一一下App的配色。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=29431066&auto=1&height=66\"></iframe>\n\n###### 一个色环\n![](http://i2.piimg.com/567571/48783fac833bb259.png)\n伊顿12色环是由近代著名的瑞士色彩学大叔😳约翰内斯•伊顿先生设计的,如上图。他把红、黄蓝作为三原色,而有两种原色不同比例混合混合所得到的为二次色,也叫间色。而由两个间色或是三个三原色混合而得到的颜色为三次色,或者称为复色,包括了除了原色和间色以外的所有颜色。\n\n###### 两种混色\n![](http://i2.piimg.com/567571/35f7a727309640d3.png)\n色光三原色(加法混色)和色料三原色(减法混色),这两个让我不明觉历啊🤔。水好深啊!\n\n加法混合是指色光的混合,两种以上的光混合在一起,光亮度会提高,混合色的光的总亮度等于相混各色光亮度之和。色光混合中,三原色是朱红、翠绿、蓝紫。这三色光是不能用其它别的色光相混而产生的。\n\n白色光线透过有色滤光片之后,一部分光线被反射而吸收其余的光线,减少掉一部分辐射功率,最后透过的光是两次减光的结果,这样的色彩混合称为减法混合。一般说来,透明性强的染料,混合后具有明显的减光作用???减法混合的三原色是加法混合的三原色的补色,即:翠绿的补色红(品红)、蓝紫的补色黄(淡黄)、朱红的补色蓝(天蓝)。\n\n###### 三种要素\n![](http://i4.piimg.com/567571/c269d49440bc44b9.png)\n色相😍😍,即各类色彩的相貌称谓,如大红、普蓝、柠檬黄等。色相是色彩的首要特征,是区别各种不同色彩的最准确的标准。事实上任何黑白灰以外的颜色都有色相的属性,而色相也就是由原色、间色和复色来构成的。\n\n饱和度(纯度),饱和度是指色彩的鲜艳程度,也称色彩的纯度。饱和度取决于该色中含色成分和消色成分(灰色)的比例。含色成分越大,饱和度越大;消色成分越大,饱和度越小。\n\n明度可以简单理解为颜色的亮度,不同的颜色具有不同的明度,例如黄色就比蓝色的明度高。任何色彩都存在明暗变化。其中黄色明度最高,紫色明度最低,绿、红、蓝、橙的明度相近,为中间明度。另外在同一色相的明度中还存在深浅的变化。如绿色中由浅到深有粉绿、淡绿、翠绿等明度变化。 \n\n###### 六种关系\n![](http://i1.piimg.com/567571/b93de3606c6214a4.png)\n接下来这个就比较重要啦,也就是各种颜色的关系,从图上我们可以看到,在伊顿12色环上,间隔180度或者两两相视的就是补色,间隔120度的是对比色,间隔90度的是中度色,间隔60度是类似色,间隔30度是相近色,0度就是同色啦,同色好像木有意义吧。\n\n未完待续。。。\n\n\n\n","html":"<p>App写得差不多了,感觉其中的色彩的运用比较乱,没有统一起来,于是花了一个下午,学习了一下色彩搭配相关的一些知识,准备统一一下App的配色。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=29431066&auto=1&height=66\"></iframe>\n\n<h6 id=\"\">一个色环</h6>\n\n<p><img src=\"http://i2.piimg.com/567571/48783fac833bb259.png\" alt=\"\" />\n伊顿12色环是由近代著名的瑞士色彩学大叔😳约翰内斯•伊顿先生设计的,如上图。他把红、黄蓝作为三原色,而有两种原色不同比例混合混合所得到的为二次色,也叫间色。而由两个间色或是三个三原色混合而得到的颜色为三次色,或者称为复色,包括了除了原色和间色以外的所有颜色。</p>\n\n<h6 id=\"\">两种混色</h6>\n\n<p><img src=\"http://i2.piimg.com/567571/35f7a727309640d3.png\" alt=\"\" />\n色光三原色(加法混色)和色料三原色(减法混色),这两个让我不明觉历啊🤔。水好深啊!</p>\n\n<p>加法混合是指色光的混合,两种以上的光混合在一起,光亮度会提高,混合色的光的总亮度等于相混各色光亮度之和。色光混合中,三原色是朱红、翠绿、蓝紫。这三色光是不能用其它别的色光相混而产生的。</p>\n\n<p>白色光线透过有色滤光片之后,一部分光线被反射而吸收其余的光线,减少掉一部分辐射功率,最后透过的光是两次减光的结果,这样的色彩混合称为减法混合。一般说来,透明性强的染料,混合后具有明显的减光作用???减法混合的三原色是加法混合的三原色的补色,即:翠绿的补色红(品红)、蓝紫的补色黄(淡黄)、朱红的补色蓝(天蓝)。</p>\n\n<h6 id=\"\">三种要素</h6>\n\n<p><img src=\"http://i4.piimg.com/567571/c269d49440bc44b9.png\" alt=\"\" />\n色相😍😍,即各类色彩的相貌称谓,如大红、普蓝、柠檬黄等。色相是色彩的首要特征,是区别各种不同色彩的最准确的标准。事实上任何黑白灰以外的颜色都有色相的属性,而色相也就是由原色、间色和复色来构成的。</p>\n\n<p>饱和度(纯度),饱和度是指色彩的鲜艳程度,也称色彩的纯度。饱和度取决于该色中含色成分和消色成分(灰色)的比例。含色成分越大,饱和度越大;消色成分越大,饱和度越小。</p>\n\n<p>明度可以简单理解为颜色的亮度,不同的颜色具有不同的明度,例如黄色就比蓝色的明度高。任何色彩都存在明暗变化。其中黄色明度最高,紫色明度最低,绿、红、蓝、橙的明度相近,为中间明度。另外在同一色相的明度中还存在深浅的变化。如绿色中由浅到深有粉绿、淡绿、翠绿等明度变化。 </p>\n\n<h6 id=\"\">六种关系</h6>\n\n<p><img src=\"http://i1.piimg.com/567571/b93de3606c6214a4.png\" alt=\"\" />\n接下来这个就比较重要啦,也就是各种颜色的关系,从图上我们可以看到,在伊顿12色环上,间隔180度或者两两相视的就是补色,间隔120度的是对比色,间隔90度的是中度色,间隔60度是类似色,间隔30度是相近色,0度就是同色啦,同色好像木有意义吧。</p>\n\n<p>未完待续。。。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467268894725,"created_by":1,"updated_at":1467282937310,"updated_by":1,"published_at":1467269256812,"published_by":1},{"id":22,"uuid":"ed8fba41-eab5-471c-b545-371e350cd4a9","title":"关于色彩搭配(实践篇)","slug":"guan-yu-se-cai-da-pei-shi-jian-pian","markdown":"先来简单介绍一下我要重构的 app 的色彩,主要包括以下几个方面,首先是主色调,例如滴滴的主色调是橙色;其次是背景色、字体颜色、图标颜色、边框颜色。相较于原来,本次需要做一个统一。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=247169&auto=1&height=66\"></iframe>\n\n主色调,主色调负责给整个应用一个整体的印象,而这个印象与每种色调所要表达的意思相关,各种颜色所能够表达的意思如下:\n\n- 红色:激情、爱情、愤怒。\n- 橙色:活力、快乐、生气。\n- 黄色:快乐、希望、虚伪。\n- 绿色:新生、富饶、自然\n- 蓝色:冷静、责任、忧伤。\n- 紫色:创造力、高贵、财富。\n- 黑色:神秘、优雅、邪恶。\n- 灰色:忧郁、保守、严谨。\n- 白色:纯洁、干净、贞洁。\n- 褐色:自然、健康、可靠。\n- 米黄色或茶色:保守、虔诚、无趣。\n- 奶油色或乳白色:平静、优雅、纯粹。 \n\n其次是背景色,背景色主要以白和灰色为主,虽然在传统的单色配色方案等中也有不同的选择,但是在 app 中,白色和灰色还更多的选择,在不同场景下也可能有别的选择,需要更多的实践。\n\n字体色,我选择了三种字体色,第一种是较深的,接近黑色,但又不那么浓黑;第二种是灰色;第三种是白色,白色字体主要使用在背景不是灰色或白色的场景,这样字能够比较突出;另外如果要增强色彩感,可以对于一些触发动作的字,使用主色调的颜色。\n\n图标颜色,暂时有三类情况,第一是两个寓意相反的图标情况,对比色能够比较好地表达;对于如尖头等图标,在一个没有色彩要求等场景中时,可以采用与字的颜色相近的色彩;第三种场景就是主色调的图标。\n\n边框:默认边框以灰色为主,而激活的边框则以主色调一致。\n\n以上是个人在简要学习了一些色彩搭配的知识后,对 app 的整体色彩进行了重构的内容。但是对于正规的项目,还是需要设计人员给出设计稿的,这样就省了开发很多的时间呢!不过作为一个有理想的开发,学习一下色彩搭配也是很有用的,比如美化一下自己的博客!\n\n未完待续。。。","html":"<p>先来简单介绍一下我要重构的 app 的色彩,主要包括以下几个方面,首先是主色调,例如滴滴的主色调是橙色;其次是背景色、字体颜色、图标颜色、边框颜色。相较于原来,本次需要做一个统一。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=247169&auto=1&height=66\"></iframe>\n\n<p>主色调,主色调负责给整个应用一个整体的印象,而这个印象与每种色调所要表达的意思相关,各种颜色所能够表达的意思如下:</p>\n\n<ul>\n<li>红色:激情、爱情、愤怒。</li>\n<li>橙色:活力、快乐、生气。</li>\n<li>黄色:快乐、希望、虚伪。</li>\n<li>绿色:新生、富饶、自然</li>\n<li>蓝色:冷静、责任、忧伤。</li>\n<li>紫色:创造力、高贵、财富。</li>\n<li>黑色:神秘、优雅、邪恶。</li>\n<li>灰色:忧郁、保守、严谨。</li>\n<li>白色:纯洁、干净、贞洁。</li>\n<li>褐色:自然、健康、可靠。</li>\n<li>米黄色或茶色:保守、虔诚、无趣。</li>\n<li>奶油色或乳白色:平静、优雅、纯粹。 </li>\n</ul>\n\n<p>其次是背景色,背景色主要以白和灰色为主,虽然在传统的单色配色方案等中也有不同的选择,但是在 app 中,白色和灰色还更多的选择,在不同场景下也可能有别的选择,需要更多的实践。</p>\n\n<p>字体色,我选择了三种字体色,第一种是较深的,接近黑色,但又不那么浓黑;第二种是灰色;第三种是白色,白色字体主要使用在背景不是灰色或白色的场景,这样字能够比较突出;另外如果要增强色彩感,可以对于一些触发动作的字,使用主色调的颜色。</p>\n\n<p>图标颜色,暂时有三类情况,第一是两个寓意相反的图标情况,对比色能够比较好地表达;对于如尖头等图标,在一个没有色彩要求等场景中时,可以采用与字的颜色相近的色彩;第三种场景就是主色调的图标。</p>\n\n<p>边框:默认边框以灰色为主,而激活的边框则以主色调一致。</p>\n\n<p>以上是个人在简要学习了一些色彩搭配的知识后,对 app 的整体色彩进行了重构的内容。但是对于正规的项目,还是需要设计人员给出设计稿的,这样就省了开发很多的时间呢!不过作为一个有理想的开发,学习一下色彩搭配也是很有用的,比如美化一下自己的博客!</p>\n\n<p>未完待续。。。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467618067380,"created_by":1,"updated_at":1467619563579,"updated_by":1,"published_at":1467619563580,"published_by":1},{"id":23,"uuid":"f5668ae7-96d1-48ff-9a50-2a811539d22e","title":"react native 布局","slug":"react-native-bu-ju","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=27514453&auto=1&height=66\"></iframe>\n\n###### 尺寸单位\n\nreact native 使用的单位是 dp 。 dp 是设备独立像素,与 px 不同,他在像素密度不同的设备上能够拥有一致的表现。\n\n对于图片来说,我们获取图片时需要利用像素密度进行一下转换,如下:\n\n```\n var image = getImage({\n width: 200 * PixelRatio.get(),\n height: 100 * PixelRatio.get()\n });\n <Image source={image} style={{width: 200, height: 100}} />\n```\n\n这样基本就不用做太多屏幕适配的工作了。\n\n###### flex的布局\n\nreact native flex 布局默认的方向为竖直方向,使用 justifyContent 指定主轴方向的对其方式,alignItems 指定侧轴的对齐方式,因此垂直居中通过设置这两个参数就 ok 了。根节点下的view 宽度默认为 100% ,可设置宽度。\n\n###### 网格布局\n\n等分的网格\n\n```\n<View style={styles.flexContainer}>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell1\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell2\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell3\n </Text>\n </View>\n </View>\n\n styles = {\n flexContainer: {\n // 容器需要添加direction才能变成让子元素flex\n flexDirection: 'row'\n },\n cell: {\n flex: 1,\n height: 50,\n backgroundColor: '#aaaaaa'\n },\n welcome: {\n fontSize: 20,\n textAlign: 'center',\n margin: 10\n },\n }\n```\n\n左边固定, 右边固定,中间flex的布局\n\n```\n <View style={styles.flexContainer}>\n <View style={styles.cellfixed}>\n <Text style={styles.welcome}>\n fixed\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n flex\n </Text>\n </View>\n <View style={styles.cellfixed}>\n <Text style={styles.welcome}>\n fixed\n </Text>\n </View>\n </View>\n\n styles = {\n flexContainer: {\n // 容器需要添加direction才能变成让子元素flex\n flexDirection: 'row'\n },\n cell: {\n flex: 1,\n height: 50,\n backgroundColor: '#aaaaaa'\n },\n welcome: {\n fontSize: 20,\n textAlign: 'center',\n margin: 10\n },\n cellfixed: {\n height: 50,\n width: 80,\n backgroundColor: '#fefefe'\n } \n }\n```\n\n###### 绝对定位和相对定位\n\n和css的标准不同的是, 元素容器不用` position:'absolute|relative' ` 。另外测试下,不支持垂直居中的写法,只支持水平居中的写法。\n\n###### padding和margin\n\n在View上设置padding很顺利,没有任何问题, 但是如果在inline元素上设置padding, 发现会出现上面的错误, paddingTop和paddingBottom都被挤成marginBottom了。 按理说,不应该对Text做padding处理, 但是确实有这样的问题存在,所以可以将这个问题mark一下。\n\n我们知道,对于inline元素,设置margin-left和margin-right有效,top和bottom按理是不会生效的, 但是上图的结果可以看到,实际是生效了的。所以现在给我的感觉是Text元素更应该理解为一个不能设置padding的block。\n\n算了不要猜了, 我们看看官方文档怎么说Text,https://facebook.github.io/react-native/docs/text.html\n\n```\n <Text>\n <Text>First part and </Text>\n <Text>second part</Text>\n </Text>\n // Text container: all the text flows as if it was one\n // |First part |\n // |and second |\n // |part |\n\n <View>\n <Text>First part and </Text>\n <Text>second part</Text>\n </View>\n // View container: each text is its own block\n // |First part |\n // |and |\n // |second part|\n```\n\n也就是如果Text元素在Text里边,可以考虑为inline, 如果单独在View里边,那就是Block。 \n下面会专门研究一下文本相关的布局\n\n###### 文本元素\n\n在实践中,我主要定义三种大小的字体,12或13的小号字体,行高20;15-17的中号字体,行高23;24号左右的大号字体,行高27。\n\n字体选择默认的,如果自己引入字体那另当别论。\n\n样式继承最近的父元素的样式。\n\n支持的属性如下:\n\n```\n Attributes.style = {\n color string\n containerBackgroundColor string\n fontFamily string\n fontSize number\n fontStyle enum('normal', 'italic')\n fontWeight enum(\"normal\", 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900')\n lineHeight number\n textAlign enum(\"auto\", 'left', 'right', 'center')\n writingDirection enum(\"auto\", 'ltr', 'rtl')\n }\n```\n\n参考: https://segmentfault.com/a/1190000002658374","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=27514453&auto=1&height=66\"></iframe>\n\n<h6 id=\"\">尺寸单位</h6>\n\n<p>react native 使用的单位是 dp 。 dp 是设备独立像素,与 px 不同,他在像素密度不同的设备上能够拥有一致的表现。</p>\n\n<p>对于图片来说,我们获取图片时需要利用像素密度进行一下转换,如下:</p>\n\n<pre><code> var image = getImage({\n width: 200 * PixelRatio.get(),\n height: 100 * PixelRatio.get()\n });\n <Image source={image} style={{width: 200, height: 100}} />\n</code></pre>\n\n<p>这样基本就不用做太多屏幕适配的工作了。</p>\n\n<h6 id=\"flex\">flex的布局</h6>\n\n<p>react native flex 布局默认的方向为竖直方向,使用 justifyContent 指定主轴方向的对其方式,alignItems 指定侧轴的对齐方式,因此垂直居中通过设置这两个参数就 ok 了。根节点下的view 宽度默认为 100% ,可设置宽度。</p>\n\n<h6 id=\"\">网格布局</h6>\n\n<p>等分的网格</p>\n\n<pre><code><View style={styles.flexContainer}> \n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell1\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell2\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell3\n </Text>\n </View>\n </View>\n\n styles = {\n flexContainer: {\n // 容器需要添加direction才能变成让子元素flex\n flexDirection: 'row'\n },\n cell: {\n flex: 1,\n height: 50,\n backgroundColor: '#aaaaaa'\n },\n welcome: {\n fontSize: 20,\n textAlign: 'center',\n margin: 10\n },\n }\n</code></pre>\n\n<p>左边固定, 右边固定,中间flex的布局</p>\n\n<pre><code> <View style={styles.flexContainer}>\n <View style={styles.cellfixed}>\n <Text style={styles.welcome}>\n fixed\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n flex\n </Text>\n </View>\n <View style={styles.cellfixed}>\n <Text style={styles.welcome}>\n fixed\n </Text>\n </View>\n </View>\n\n styles = {\n flexContainer: {\n // 容器需要添加direction才能变成让子元素flex\n flexDirection: 'row'\n },\n cell: {\n flex: 1,\n height: 50,\n backgroundColor: '#aaaaaa'\n },\n welcome: {\n fontSize: 20,\n textAlign: 'center',\n margin: 10\n },\n cellfixed: {\n height: 50,\n width: 80,\n backgroundColor: '#fefefe'\n } \n }\n</code></pre>\n\n<h6 id=\"\">绝对定位和相对定位</h6>\n\n<p>和css的标准不同的是, 元素容器不用<code>position:'absolute|relative'</code> 。另外测试下,不支持垂直居中的写法,只支持水平居中的写法。</p>\n\n<h6 id=\"paddingmargin\">padding和margin</h6>\n\n<p>在View上设置padding很顺利,没有任何问题, 但是如果在inline元素上设置padding, 发现会出现上面的错误, paddingTop和paddingBottom都被挤成marginBottom了。 按理说,不应该对Text做padding处理, 但是确实有这样的问题存在,所以可以将这个问题mark一下。</p>\n\n<p>我们知道,对于inline元素,设置margin-left和margin-right有效,top和bottom按理是不会生效的, 但是上图的结果可以看到,实际是生效了的。所以现在给我的感觉是Text元素更应该理解为一个不能设置padding的block。</p>\n\n<p>算了不要猜了, 我们看看官方文档怎么说Text,<a href=\"https://facebook.github.io/react-native/docs/text.html\">https://facebook.github.io/react-native/docs/text.html</a></p>\n\n<pre><code> <Text>\n <Text>First part and </Text>\n <Text>second part</Text>\n </Text>\n // Text container: all the text flows as if it was one\n // |First part |\n // |and second |\n // |part |\n\n <View>\n <Text>First part and </Text>\n <Text>second part</Text>\n </View>\n // View container: each text is its own block\n // |First part |\n // |and |\n // |second part|\n</code></pre>\n\n<p>也就是如果Text元素在Text里边,可以考虑为inline, 如果单独在View里边,那就是Block。 \n下面会专门研究一下文本相关的布局</p>\n\n<h6 id=\"\">文本元素</h6>\n\n<p>在实践中,我主要定义三种大小的字体,12或13的小号字体,行高20;15-17的中号字体,行高23;24号左右的大号字体,行高27。</p>\n\n<p>字体选择默认的,如果自己引入字体那另当别论。</p>\n\n<p>样式继承最近的父元素的样式。</p>\n\n<p>支持的属性如下:</p>\n\n<pre><code> Attributes.style = {\n color string\n containerBackgroundColor string\n fontFamily string\n fontSize number\n fontStyle enum('normal', 'italic')\n fontWeight enum(\"normal\", 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900')\n lineHeight number\n textAlign enum(\"auto\", 'left', 'right', 'center')\n writingDirection enum(\"auto\", 'ltr', 'rtl')\n }\n</code></pre>\n\n<p>参考: <a href=\"https://segmentfault.com/a/1190000002658374\">https://segmentfault.com/a/1190000002658374</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467619667519,"created_by":1,"updated_at":1467621941112,"updated_by":1,"published_at":1467621393993,"published_by":1},{"id":24,"uuid":"e92a7d4f-bd2a-436e-ae77-9db3d4ccac97","title":"react Stateless Functions(官方文档翻译)","slug":"react-stateless-functions-guan-fang-wen-dang-fan-yi","markdown":"> 中文社区还停留在 0.14.7,文档上有不少落后了,比如 shadowEqual、context 等的知识,所以只能自己来了。\n\n你也可以用一个普通的 JavaScript 函数定义你的 React classes 。例如使用 stateless function 语法:\n\n```\nfunction HelloMessage(props) {\n return <div>Hello {props.name}</div>;\n}\nReactDOM.render(<HelloMessage name=\"Sebastian\" />, mountNode);\n```\n\n或者使用 ES6 的 箭头函数语法:\n\n```\nconst HelloMessage = (props) => <div>Hello {props.name}</div>;\nReactDOM.render(<HelloMessage name=\"Sebastian\" />, mountNode);\n```\n\n这个简化的组件 API 设计为纯函数几它们的参数。这些组件必须不能保留内部的状态,没有支持的实例并且没有生命周期方法。 它们是转换起输入的纯函数,没有副作用。然而,你仍然可以具体说明 ` .propTypes ` 和 ` .defaultProps ` 通过设置它们喂函数的参数,就像在 ES6 class上一样设置。\n\n> 注意:\n因为 stateless functions 没有一个支持的实例,你不能使用 ref 来得到一个stateless function 组件。通常这不是一个 issue,因为 stateless functions 不提供一个必须服从的 AP I。没有一个必须服从的 API,也就没有必须要对实例要求做的事。然而,如果一个用户想要找到 stateless function 组件的 DOM 节点 ,它们必须包裹在一个有状态的组件内,然后用 ref 去访问有状态的包裹组件。\n注意:\n在 React v0.14, stateless functional 组件将不被允许去返回 ` null ` 或 ` false `(变通的方法是返回 ` <noscript /> ` 代替)。这个问题在 React v15 当中被修复了, stateless functional 组件现在被允许返回 ` null ` 。\n\n在理想的情况下,你的大部分组件都将是 stateless functions,因为在将来我们也能够给这些组件带来性能优化的规范,通过避免不需要的检查和内存分配。如果可能的话,这是理想的模式。","html":"<blockquote>\n <p>中文社区还停留在 0.14.7,文档上有不少落后了,比如 shadowEqual、context 等的知识,所以只能自己来了。</p>\n</blockquote>\n\n<p>你也可以用一个普通的 JavaScript 函数定义你的 React classes 。例如使用 stateless function 语法:</p>\n\n<pre><code>function HelloMessage(props) { \n return <div>Hello {props.name}</div>;\n}\nReactDOM.render(<HelloMessage name=\"Sebastian\" />, mountNode); \n</code></pre>\n\n<p>或者使用 ES6 的 箭头函数语法:</p>\n\n<pre><code>const HelloMessage = (props) => <div>Hello {props.name}</div>; \nReactDOM.render(<HelloMessage name=\"Sebastian\" />, mountNode); \n</code></pre>\n\n<p>这个简化的组件 API 设计为纯函数几它们的参数。这些组件必须不能保留内部的状态,没有支持的实例并且没有生命周期方法。 它们是转换起输入的纯函数,没有副作用。然而,你仍然可以具体说明 <code>.propTypes</code> 和 <code>.defaultProps</code> 通过设置它们喂函数的参数,就像在 ES6 class上一样设置。</p>\n\n<blockquote>\n <p>注意:\n 因为 stateless functions 没有一个支持的实例,你不能使用 ref 来得到一个stateless function 组件。通常这不是一个 issue,因为 stateless functions 不提供一个必须服从的 AP I。没有一个必须服从的 API,也就没有必须要对实例要求做的事。然而,如果一个用户想要找到 stateless function 组件的 DOM 节点 ,它们必须包裹在一个有状态的组件内,然后用 ref 去访问有状态的包裹组件。\n 注意:\n 在 React v0.14, stateless functional 组件将不被允许去返回 <code>null</code> 或 <code>false</code>(变通的方法是返回 <code><noscript /></code> 代替)。这个问题在 React v15 当中被修复了, stateless functional 组件现在被允许返回 <code>null</code> 。</p>\n</blockquote>\n\n<p>在理想的情况下,你的大部分组件都将是 stateless functions,因为在将来我们也能够给这些组件带来性能优化的规范,通过避免不需要的检查和内存分配。如果可能的话,这是理想的模式。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467695307621,"created_by":1,"updated_at":1467697494691,"updated_by":1,"published_at":1467696282266,"published_by":1},{"id":25,"uuid":"9b062008-ea10-4824-b493-1b795e1eae9b","title":"小葵唱的《ソラニン》","slug":"xiao-kui-chang-de-soranin","markdown":"小葵的声音好好听啊,单曲循环,元气满满!😊就是木有找到能下载的mp3。\n\n<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=180951&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>\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\n若说那天我这麼做\n若说能回到那一天\n我已无法再回到那一天\n\n如果说弛缓的幸福缓慢地延续\n这样下去坏种子一定会发芽\n还是只能说再见\n\n寒冷冬天中冷掉的罐装咖啡 七彩的长围巾\n小跑步穿过小巷中 我试著回忆起\n\n如果说弛缓的幸福缓慢地延续\n这样下去坏种子一定会发芽\n还是只能说再见\n\n说再见 这样也好\n无论在哪都要好好地过\n再见了 我也一定有办法的\n说再见 就这样吧\n\n---\n\no mo i chi ga i wa so ra no ka na ta\nsa yo na ra da ke no jin sei ka\nho n no su ko shi no mi rai wa mi e ta no ni\nsa yo na ra na n da\n\nmu ka shi su n de ta chii sa na he ya wa i ma wa da re ka ga su n de n da\nki mi ni i wa re ta hi do i ko ta ba mo\nmu da na ki ga shi ta mai ni chi mo\n\na no hi ko u shi te i re ba\na no hi ni mo do re re ba\na no ko ro no bo ku ni wa mo u mo do re nai yo\n\nta to e ba yu ru i shi a wa se ga da ra tto tsu du i ta to su ru\nki tto wa ru i ta ne ga me o da shi te\nmo u sa yo na ra na n da\n\nsa mu i fu yu no hi e ta kan ko- hi- ni ji i ro no na ga i ma fu ra-\nko ba shi ri de ro ji u ra o nu ke te o mo i da shi te mi ru\n\nta to e ba yu ru i shi a wa se ga da ra tto tsu du i ta to su ru\nki tto wa ru i ta ne ga me o da shi te\nmo u sa yo na ra na n da\n\nsa ya na ra so re mo i i sa\ndo ko ka de gen ki de ya re yo\nsa yo na ra bo ku mo do- ni ka ya ru sa\nsa yo na ra so u su ru yo","html":"<p>小葵的声音好好听啊,单曲循环,元气满满!😊就是木有找到能下载的mp3。</p>\n\n<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=180951&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>\n\n<p>思い违いは空のかなた\nさよならだけの人生か\nほんの少しの未来は见えたのに\nさよならなんだ</p>\n\n<p>昔住んでた小さな部屋は今は谁かが住んでんだ\n君に言われたひどい言叶も\n无駄な気がした毎日も</p>\n\n<p>あの日こうしていれば\nあの日に戻れれば\nあの顷の仆にはもう戻れないよ</p>\n\n<p>たとえばゆるい幸せがだらっと続いたとする\nきっと悪い种が芽を出して\nもうさよならんだ</p>\n\n<p>寒い冬の冷えた缶コーヒー 虹色の长いマフラー\n小走りで路地裏を抜けて思い出してみる</p>\n\n<p>たとえばゆるい幸せがだらっと続いたとする\nきっと悪い种が芽を出して\nもうさよならなんだ</p>\n\n<p>さよなら それもいいさ\nどこかで元気でやれよ\nさよなら 仆もどーにかやるさ\nさよなら そうするよ</p>\n\n<p>---</p>\n\n<p>误会在天空的彼端\n难道人生只充满了再见吗\n只差那麼一点就可以看见未来\n却只能说再见</p>\n\n<p>以前住过的小房间现在又是谁住在那里\n无论是你对我说过的过分话语\n还是感觉白费力气的每一天</p>\n\n<p>若说那天我这麼做\n若说能回到那一天\n我已无法再回到那一天</p>\n\n<p>如果说弛缓的幸福缓慢地延续\n这样下去坏种子一定会发芽\n还是只能说再见</p>\n\n<p>寒冷冬天中冷掉的罐装咖啡 七彩的长围巾\n小跑步穿过小巷中 我试著回忆起</p>\n\n<p>如果说弛缓的幸福缓慢地延续\n这样下去坏种子一定会发芽\n还是只能说再见</p>\n\n<p>说再见 这样也好\n无论在哪都要好好地过\n再见了 我也一定有办法的\n说再见 就这样吧</p>\n\n<p>---</p>\n\n<p>o mo i chi ga i wa so ra no ka na ta <br />\nsa yo na ra da ke no jin sei ka <br />\nho n no su ko shi no mi rai wa mi e ta no ni <br />\nsa yo na ra na n da</p>\n\n<p>mu ka shi su n de ta chii sa na he ya wa i ma wa da re ka ga su n de n da <br />\nki mi ni i wa re ta hi do i ko ta ba mo <br />\nmu da na ki ga shi ta mai ni chi mo</p>\n\n<p>a no hi ko u shi te i re ba <br />\na no hi ni mo do re re ba <br />\na no ko ro no bo ku ni wa mo u mo do re nai yo</p>\n\n<p>ta to e ba yu ru i shi a wa se ga da ra tto tsu du i ta to su ru <br />\nki tto wa ru i ta ne ga me o da shi te <br />\nmo u sa yo na ra na n da</p>\n\n<p>sa mu i fu yu no hi e ta kan ko- hi- ni ji i ro no na ga i ma fu ra- <br />\nko ba shi ri de ro ji u ra o nu ke te o mo i da shi te mi ru</p>\n\n<p>ta to e ba yu ru i shi a wa se ga da ra tto tsu du i ta to su ru <br />\nki tto wa ru i ta ne ga me o da shi te <br />\nmo u sa yo na ra na n da</p>\n\n<p>sa ya na ra so re mo i i sa <br />\ndo ko ka de gen ki de ya re yo <br />\nsa yo na ra bo ku mo do- ni ka ya ru sa <br />\nsa yo na ra so u su ru yo</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467696313896,"created_by":1,"updated_at":1467762751870,"updated_by":1,"published_at":1467696583407,"published_by":1},{"id":26,"uuid":"6c84f077-565f-4041-b904-5f22ce2b3bf4","title":"(Untitled)","slug":"untitled","markdown":"<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=5278719&page=4\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>","html":"<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=5278719&page=4\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468372912162,"created_by":1,"updated_at":1468372918140,"updated_by":1,"published_at":null,"published_by":null},{"id":27,"uuid":"7107ba84-ee23-401d-90a0-6f72c9919ec9","title":"无题","slug":"dyht","markdown":"最近忙屎了,没时间写东西🙄。马上要开工,随便写点。最近大鱼可以说是在舆论的风口浪尖,虽然没去电影院看,但是通过b站的一个个影评,已是略知一二了。\n\n作为一部花了12年撸出的一个东西,确实在剧情上如大家所说有点不如人意,但是画面、配乐都是在向国外看齐的。正如一个up主说,大鱼给他的感觉绝不是让人失望的那种,只是有点微妙。这种微妙感,来源于12年的大鱼承载了太多,来源于作者不那么会讲故事。不过对于国漫来说,这不能说是划时代,却可以称得上是一个新的开始。大雨让我看到仍有那么一批人在认真地做着动画,这是最让我欣慰的。\n\n下面是昨天在b站看到的一组和我一样大的学生做的动画,瞬间对国漫燃起了信心🤗。有这么多有才的少年在追逐着梦想,希望10年、20年后能够看到每一个短片能够出线在大荧幕之上。干活去了!😭\n\n<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=5278719&page=4\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>","html":"<p>最近忙屎了,没时间写东西🙄。马上要开工,随便写点。最近大鱼可以说是在舆论的风口浪尖,虽然没去电影院看,但是通过b站的一个个影评,已是略知一二了。</p>\n\n<p>作为一部花了12年撸出的一个东西,确实在剧情上如大家所说有点不如人意,但是画面、配乐都是在向国外看齐的。正如一个up主说,大鱼给他的感觉绝不是让人失望的那种,只是有点微妙。这种微妙感,来源于12年的大鱼承载了太多,来源于作者不那么会讲故事。不过对于国漫来说,这不能说是划时代,却可以称得上是一个新的开始。大雨让我看到仍有那么一批人在认真地做着动画,这是最让我欣慰的。</p>\n\n<p>下面是昨天在b站看到的一组和我一样大的学生做的动画,瞬间对国漫燃起了信心🤗。有这么多有才的少年在追逐着梦想,希望10年、20年后能够看到每一个短片能够出线在大荧幕之上。干活去了!😭</p>\n\n<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=5278719&page=4\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468459601710,"created_by":1,"updated_at":1468460846200,"updated_by":1,"published_at":1468460846201,"published_by":1},{"id":28,"uuid":"8aa4ff0b-b33b-4f2c-8741-d86ac04536c5","title":"转:CET,UTC,GMT,CST几种常见时间概述","slug":"zhuan-cet-utc-gmt-cstji-chong-chang-jian-shi-jian-gai-shu","markdown":"###### CET(欧洲中部时间)\n\n欧洲中部时间(英語:Central European Time,CET)是比世界标准时间(UTC)早一个小时的时区名称之一。它被大部分欧洲国家和部分北非国家采用。\n\n冬季时间为UTC+1,夏季欧洲夏令时为UTC+2。\n\n###### WET(欧洲西部时间)\n\n欧洲西部时间(Western European Time,缩写WET)和世界标准时间(UTC)相同。\n\n###### EET(欧洲东部时间)\n\n欧洲东部时间(Eastern European Time,缩写EET)是比世界标准时间(UTC)早二个小时的时区名称之一。它被部分欧洲国家、北非国家和中东国家采用。\n\n###### UTC(世界标准时间)\n\n协调世界时,又称世界标准时间或世界協調時間,简称UTC(从英文「Coordinated Universal Time」/法文「Temps Universel Cordonné」而来),是最主要的世界時間標準,其以原子时秒长为基础,在时刻上尽量接近于格林尼治平時。\n\n###### GMT(格林尼治平时)\n\n格林尼治平时(又称格林尼治平均时间或格林尼治标准时间,旧译格林威治标准时间;英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。\n\n自1924年2月5日开始,格林尼治天文台每隔一小时会向全世界发放调时信息。\n\n理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达16分钟。\n\n由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的协调世界时(UTC)。\n\n###### CST(北京时间)\n\n北京时间,China Standard Time,中国标准时间。在时区划分上,属东八区,比协调世界时早8小时,记为UTC+8。\n\n不过这个CST这个缩写比较纠结的是它可以同时代表四个不同的时间:\n\nCentral Standard Time (USA) UT-6:00\nCentral Standard Time (Australia) UT+9:30\nChina Standard Time UT+8:00\nCuba Standard Time UT-4:00\n因此你平时编写程序过程中也有可能遇到javascript客户端时间和服务端时间不一致的问题,编程的时候还是要多注意。\n\n###### ISO 日期和时间的表示方法\n\n国际标准化组织的国际标准ISO 8601是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前是第三版ISO8601:2004以替代第一版ISO8601:1988與第二版ISO8601:2000。\n\n这是仅仅就只是一个日期时间的表示方法,我们用这样的方法表示一个UTC时间。","html":"<h6 id=\"cet\">CET(欧洲中部时间)</h6>\n\n<p>欧洲中部时间(英語:Central European Time,CET)是比世界标准时间(UTC)早一个小时的时区名称之一。它被大部分欧洲国家和部分北非国家采用。</p>\n\n<p>冬季时间为UTC+1,夏季欧洲夏令时为UTC+2。</p>\n\n<h6 id=\"wet\">WET(欧洲西部时间)</h6>\n\n<p>欧洲西部时间(Western European Time,缩写WET)和世界标准时间(UTC)相同。</p>\n\n<h6 id=\"eet\">EET(欧洲东部时间)</h6>\n\n<p>欧洲东部时间(Eastern European Time,缩写EET)是比世界标准时间(UTC)早二个小时的时区名称之一。它被部分欧洲国家、北非国家和中东国家采用。</p>\n\n<h6 id=\"utc\">UTC(世界标准时间)</h6>\n\n<p>协调世界时,又称世界标准时间或世界協調時間,简称UTC(从英文「Coordinated Universal Time」/法文「Temps Universel Cordonné」而来),是最主要的世界時間標準,其以原子时秒长为基础,在时刻上尽量接近于格林尼治平時。</p>\n\n<h6 id=\"gmt\">GMT(格林尼治平时)</h6>\n\n<p>格林尼治平时(又称格林尼治平均时间或格林尼治标准时间,旧译格林威治标准时间;英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。</p>\n\n<p>自1924年2月5日开始,格林尼治天文台每隔一小时会向全世界发放调时信息。</p>\n\n<p>理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达16分钟。</p>\n\n<p>由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的协调世界时(UTC)。</p>\n\n<h6 id=\"cst\">CST(北京时间)</h6>\n\n<p>北京时间,China Standard Time,中国标准时间。在时区划分上,属东八区,比协调世界时早8小时,记为UTC+8。</p>\n\n<p>不过这个CST这个缩写比较纠结的是它可以同时代表四个不同的时间:</p>\n\n<p>Central Standard Time (USA) UT-6:00 <br />\nCentral Standard Time (Australia) UT+9:30 <br />\nChina Standard Time UT+8:00 <br />\nCuba Standard Time UT-4:00 <br />\n因此你平时编写程序过程中也有可能遇到javascript客户端时间和服务端时间不一致的问题,编程的时候还是要多注意。</p>\n\n<h6 id=\"iso\">ISO 日期和时间的表示方法</h6>\n\n<p>国际标准化组织的国际标准ISO 8601是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前是第三版ISO8601:2004以替代第一版ISO8601:1988與第二版ISO8601:2000。</p>\n\n<p>这是仅仅就只是一个日期时间的表示方法,我们用这样的方法表示一个UTC时间。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468468310903,"created_by":1,"updated_at":1468469618333,"updated_by":1,"published_at":1468469618334,"published_by":1},{"id":29,"uuid":"026a8240-4811-4fa0-af76-7301460de880","title":"react native 性能(最新官方文档翻译)","slug":"react-native-xing-neng-zui-xin-guan-fang-wen-dang-fan-yi","markdown":"","html":"","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468483412197,"created_by":1,"updated_at":1468483416012,"updated_by":1,"published_at":null,"published_by":null},{"id":30,"uuid":"e043b29e-50be-470f-9a35-e7c8d04a7bc2","title":"js Date的国际化、格式化的探究","slug":"js","markdown":"这一篇是继续上一篇而写的,大致背景就是我在 react native 中需要实现格式化时间。我首先在 MDN 上找到的是下面几个方法:\n\n```\nDate.prototype.toLocaleDateString()\nDate.prototype.toLocaleFormat()\nDate.prototype.toLocaleString()\nDate.prototype.toLocaleTimeString()\n```\n\n` Date.prototype.toLocaleFormat() ` 方法不是 ES 标准当中,所以首先就舍弃了。专向剩下的三个方法,这三个方法都可以传入两个参数 ` locates ` 和 ` options ` 。下面简单介绍下这两个参数:\n\n` locales ` 是个字符串,指定单个[语言标签](https://www.w3.org/International/articles/language-tags/),或者包含多个语言标签的类数组对象。语言标签如下面的字符串:en(普通英语),de-AT(奥地利德语),zh-Hant-TW(台湾使用的繁体中文)。语言标签可以包含一个“Unicode扩展”,形式为-u-key1-value1-key2-value2..., 其中每个key是“扩展key”。不同的构造函数对此进行具体解释。\n\n` opions ` 是个对象,其属性(如果不存在,就赋值为undefined)决定格式化器(formatter)和整理器(collator)的行为。精确的解释由构造函数决定。\n\n给定区域信息和选项,实现会尝试生成近似理想行为的最接近行为。Firefox 支持用于整理(collation)的400+区域,用于date/time和数字格式化的600+区域,所以很可能(但不保证)你想要的区域是被支持的。\n\n这两个实际上是 js 的国际化 api 里的内容,js 国际化 api 包括了 日期、数字等的格式化,不过不是本文的重点,想了解可以参考[此文](http://www.open-open.com/lib/view/open1418779460683.html#articleHeader8),虽然两年前的但是讲的很细。\n\n于是我开始使用 ` locales ` 和简单的正则来格式化我的时间,如下:\n\n```\n// 用到的地方不多,所以没用写成通用的format\nfunction DateFormat(now) {\n return now.toLocaleDateString('zh-CN').substring(5).replace(/\\//g, '月').concat('日 ', now.toLocaleTimeString().slice(0, -3));\n}\n```\n\n这段代码在 ios 下感觉良好,但是今天测试流程用了我自己的安卓机,世界就崩塌了🙄,时间是乱的。然后试了下,发现无论传入什么 ` locates ` 在安卓下都没有效果。擦擦擦,那肯定是兼容性的问题了,再次来到 MDN 翻到兼容性的地方,我方了:\n![](http://7xrn7f.com1.z0.glb.clouddn.com/16-7-19/33570730.jpg)\n\n然后只能去找别的方式了,最后用的是正则表达式,亲测可用,如下:\n\n```\n// 对Date的扩展,将 Date 转化为指定格式的String \n// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, \n// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) \n// 例子: \n// (new Date()).Format(\"yyyy-MM-dd hh:mm:ss.S\") ==> 2006-07-02 08:09:04.423 \n// (new Date()).Format(\"yyyy-M-d h:m:s.S\") ==> 2006-7-2 8:9:4.18 \nDate.prototype.Format = function(fmt) \n{ //author: meizz \n var o = { \n \"M+\" : this.getMonth()+1, //月份 \n \"d+\" : this.getDate(), //日 \n \"h+\" : this.getHours(), //小时 \n \"m+\" : this.getMinutes(), //分 \n \"s+\" : this.getSeconds(), //秒 \n \"q+\" : Math.floor((this.getMonth()+3)/3), //季度 \n \"S\" : this.getMilliseconds() //毫秒 \n }; \n if(/(y+)/.test(fmt)) \n fmt=fmt.replace(RegExp.$1, (this.getFullYear()+\"\").substr(4 - RegExp.$1.length)); \n for(var k in o) \n if(new RegExp(\"(\"+ k +\")\").test(fmt)) \n fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : ((\"00\"+ o[k]).substr((\"\"+ o[k]).length))); \n return fmt; \n} \n```\n\n虽然最后完成了,但是对于 js 国际化和正则的部分内容,需要做更深入的研究!开饭了!😬\n\n另外如果不想折腾的话,[moment](https://github.com/moment/moment)这个库可能是一个不错的选择。😜","html":"<p>这一篇是继续上一篇而写的,大致背景就是我在 react native 中需要实现格式化时间。我首先在 MDN 上找到的是下面几个方法:</p>\n\n<pre><code>Date.prototype.toLocaleDateString() \nDate.prototype.toLocaleFormat() \nDate.prototype.toLocaleString() \nDate.prototype.toLocaleTimeString() \n</code></pre>\n\n<p><code>Date.prototype.toLocaleFormat()</code> 方法不是 ES 标准当中,所以首先就舍弃了。专向剩下的三个方法,这三个方法都可以传入两个参数 <code>locates</code> 和 <code>options</code> 。下面简单介绍下这两个参数:</p>\n\n<p><code>locales</code> 是个字符串,指定单个<a href=\"https://www.w3.org/International/articles/language-tags/\">语言标签</a>,或者包含多个语言标签的类数组对象。语言标签如下面的字符串:en(普通英语),de-AT(奥地利德语),zh-Hant-TW(台湾使用的繁体中文)。语言标签可以包含一个“Unicode扩展”,形式为-u-key1-value1-key2-value2..., 其中每个key是“扩展key”。不同的构造函数对此进行具体解释。</p>\n\n<p><code>opions</code> 是个对象,其属性(如果不存在,就赋值为undefined)决定格式化器(formatter)和整理器(collator)的行为。精确的解释由构造函数决定。</p>\n\n<p>给定区域信息和选项,实现会尝试生成近似理想行为的最接近行为。Firefox 支持用于整理(collation)的400+区域,用于date/time和数字格式化的600+区域,所以很可能(但不保证)你想要的区域是被支持的。</p>\n\n<p>这两个实际上是 js 的国际化 api 里的内容,js 国际化 api 包括了 日期、数字等的格式化,不过不是本文的重点,想了解可以参考<a href=\"http://www.open-open.com/lib/view/open1418779460683.html#articleHeader8\">此文</a>,虽然两年前的但是讲的很细。</p>\n\n<p>于是我开始使用 <code>locales</code> 和简单的正则来格式化我的时间,如下:</p>\n\n<pre><code>// 用到的地方不多,所以没用写成通用的format\nfunction DateFormat(now) { \n return now.toLocaleDateString('zh-CN').substring(5).replace(/\\//g, '月').concat('日 ', now.toLocaleTimeString().slice(0, -3));\n}\n</code></pre>\n\n<p>这段代码在 ios 下感觉良好,但是今天测试流程用了我自己的安卓机,世界就崩塌了🙄,时间是乱的。然后试了下,发现无论传入什么 <code>locates</code> 在安卓下都没有效果。擦擦擦,那肯定是兼容性的问题了,再次来到 MDN 翻到兼容性的地方,我方了:\n<img src=\"http://7xrn7f.com1.z0.glb.clouddn.com/16-7-19/33570730.jpg\" alt=\"\" /></p>\n\n<p>然后只能去找别的方式了,最后用的是正则表达式,亲测可用,如下:</p>\n\n<pre><code>// 对Date的扩展,将 Date 转化为指定格式的String \n// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, \n// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) \n// 例子: \n// (new Date()).Format(\"yyyy-MM-dd hh:mm:ss.S\") ==> 2006-07-02 08:09:04.423 \n// (new Date()).Format(\"yyyy-M-d h:m:s.S\") ==> 2006-7-2 8:9:4.18 \nDate.prototype.Format = function(fmt) \n{ //author: meizz \n var o = { \n \"M+\" : this.getMonth()+1, //月份 \n \"d+\" : this.getDate(), //日 \n \"h+\" : this.getHours(), //小时 \n \"m+\" : this.getMinutes(), //分 \n \"s+\" : this.getSeconds(), //秒 \n \"q+\" : Math.floor((this.getMonth()+3)/3), //季度 \n \"S\" : this.getMilliseconds() //毫秒 \n }; \n if(/(y+)/.test(fmt)) \n fmt=fmt.replace(RegExp.$1, (this.getFullYear()+\"\").substr(4 - RegExp.$1.length)); \n for(var k in o) \n if(new RegExp(\"(\"+ k +\")\").test(fmt)) \n fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : ((\"00\"+ o[k]).substr((\"\"+ o[k]).length))); \n return fmt; \n} \n</code></pre>\n\n<p>虽然最后完成了,但是对于 js 国际化和正则的部分内容,需要做更深入的研究!开饭了!😬</p>\n\n<p>另外如果不想折腾的话,<a href=\"https://github.com/moment/moment\">moment</a>这个库可能是一个不错的选择。😜</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468860542709,"created_by":1,"updated_at":1468900149898,"updated_by":1,"published_at":1468899840142,"published_by":1},{"id":31,"uuid":"40e9f305-a20a-4f5c-b7d3-2fe928b04664","title":"自己撸表单验证插件——简陋版","slug":"zi-ji-lu-biao-dan-yan-zheng-cha-jian-jian-lou-ban","markdown":"写之前先吐槽一下:昨晚半夜起来好像把眼镜踩坏了😭,下午只好去了趟眼镜店,顶着杭城下午两点的太阳,也是很拼。没想到传来了更大的噩耗🙄,测散光时说我原来的眼镜散光做反了,没得救了😒。最后只好勉强得整了一下,但愿新镜片能够慢慢调整好😂。\n\n然后是正片,今天鱼头说把司机招募的验证加上去,刚好写完后台消息的AOP没什么事,所以我就寻思写的一劳永逸的验证模块,岂不是很好很nice。不过作为懒人还是先去 github 上着了一番,不过没有比较适合现在的项目的。主要原因还是页面已经完成了,改用其它组件的工程量太大。所以最后决定自己整一个不侵入现有组件的,只关注验证逻辑,可扩展的验证模块。\n\n先来看看引入插件之前的的表单验证:\n\n```\nhandleSubmit() {\n if(!this.state.credit_card){\n Alert.alert('必须填写银行卡号!');\n return;\n }\n if(!this.state.rest || this.state.rest < 0){\n Alert.alert('请填写正确的提现金额!');\n return;\n }\n if(this.state.rest > this.props.driver.driver_state.rest){\n Alert.alert('提现金额不能大于余额!');\n return;\n }\n rest.post...\n }\n```\n\n可以看到是通过 if 进行判断,这种做法,代码十分丑陋,而且复用性十分低。有什么办法能够改进上面的代码呢!进一步思考,其实表单验证的逻辑无非是对某个表单的输入,进行相关逻辑的判断。进行的逻辑判断是不变的,而判断的方式是可以改变的。是不是有点感觉了,我们可以把不变的逻辑固定下来,而可变的策略抽离出来。说到底就是策略模式了,策略模式就是通过把可变的判断策略提取出来,帮助我们省去那些 ` if ` 。废话不多说,先来看看在 web 上我们是如何使用的:[出处](http://brizer.github.io/2016/05/04/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5/)\n\n```\n//定义验证策略\nvar strategies = {\n inNonEmpty:function(value,errorMsg){//不能为空\n if(value === ''){\n return errorMsg;\n }\n },\n minLength:function(value,length,errorMsg){//最小长度\n if(value.length < length){\n return errorMsg;\n }\n },\n isReg:function(value,reg,errorMsg){//正则匹配\n if(!reg.test(value)){\n return errorMsg;\n }\n }\n};\n//定义验证类\nvar Validator = function(){\n this.cache = [];//保存效验规则\n};\n//添加验证规则\nValidator.prototype.add = function(dom,rules){\n var that = this;\n //多条规则分别对应\n for(var i = 0 ,rule; rule = rules[i++];){\n (function(rule){\n var stratgyAry = rule.strategy.split(':');\n var errorMsg = rule.errorMsg;\n\t\t\t//将数据按照策略的格式塞入参数数组\n that.cache.push(function(){\n var strategy = strategyAry.shift();\n strategyAry.unshift(dom.value);\n strategyAry.push(errorMsg);\n return strategies[strategy].apply(dom,strategyAry);\n });\n })(rule)\n }\n};\nValidator.prototype.start = function(){\n //依次验证\n for(var i = 0,validatorFunc;validatorFunc = this.cache[i++];){\n var errorMsg = validatorFunc();\n if(errorMsg){\n return errorMsg;\n }\n }\n};\n\n//调用代码\nvar validataFunc = function(){\n var validator = new Validator();\n //多个条件判断\n validator.add(form.name,[{\n strategy:'isNonEmpty',\n errorMsg:'用户不能为空'\n },{\n strategy:'minLength:10',\n errorMsg:'用户长度不能小于10位'\n }]);\n validator.add(form.password,[{\n strategy:'minLength:6',\n errorMsg:'密码长度不能低于6位'\n }]);\n var errorMsg = validator.start();\n return errorMsg;\n}\nform.onsubmit = function(){\n var errorMsg = validateFunc();\n if(errorMsg){\n alert(errorMsg);\n return false;\n }\n //验证通过,提交表单\n}\n```\n\n上面的代码简单来说,就是定义了一个验证器,在验证前把验证的 dom 和 对应的策略添加到验证器中,然后调用 start 函数,一次运行验证函数,如有验证错误则返回错误信息。这样我们就可以复用验证策略,只需要关心每个输入具体要符合什么规则并传入就可以了,代码也很清爽。\n\n但是这还不能直接运用到 rn 当中,需要稍微改造下,因为我没有 dom ,我有的是 state。[tcomb-form-native](https://github.com/gcanti/tcomb-form-native)这个库给了我一些灵感。\n\n```\n// here we are: define your domain model\nvar Person = t.struct({\n name: t.String, // a required string\n surname: t.maybe(t.String), // an optional string\n age: t.Number, // a required number\n rememberMe: t.Boolean // a boolean\n});\n```\n\n它是这么定义验证的信息的,那其实我的 state 当中的需要验证的字段和验证规则之间也可以建立一种类似的规则。于是就有了我最后的简陋版的验证插件,先看代码,然后再谈谈不足:\n\n```\n/**\n * Created by m2mbob on 16/7/20.\n */\nconst strategies = {\n isRequired:function(value,errorMsg) {\n if(value === ''){\n return errorMsg;\n }\n },\n maxLength:function(value,length,errorMsg) {//最小长度\n if(value.length > length){\n return errorMsg;\n }\n },\n minLength:function(value,length,errorMsg) {//最小长度\n if(value.length < length){\n return errorMsg;\n }\n },\n isPhone:function(value,errorMsg) {\n return this.isReg(value, /^1[3|4|5|7|8]\\d{9}$/g, errorMsg);\n },\n isEmail:function(value, errorMsg) {\n return this.isReg(value, /^(\\w-*\\.*)+@(\\w-?)+(\\.\\w{2,})+$/g, errorMsg);\n },\n isIdCard:function(value, errorMsg) {\n return this.isReg(value, /(^\\d{15}$)|(^\\d{17}([0-9]|X)$)/g, errorMsg);\n },\n isCar:function(value, errorMsg) {\n return this.isReg(value, /^[\\u4e00-\\u9fa5]{1}[A-Z]{1}[A-Z_0-9]{5}$/g, errorMsg);\n },\n isReg:function(value,reg,errorMsg) {//正则匹配\n if(!reg.test(value)){\n return errorMsg;\n }\n }\n};\n\nexport default class Validator {\n constructor() {\n this.errMsg = [];\n }\n\n validate(value, rules) {\n let flag = true;\n for(let i = 0 ,rule; rule = rules[i++];){\n let strategyAry = rule.strategy.split(':');\n let errorMsg = rule.errorMsg;\n let strategy = strategyAry.shift();\n strategyAry.unshift(value);\n strategyAry.push(errorMsg);\n let err = strategies[strategy].apply(strategies, strategyAry);\n if(err){\n flag = false;\n this.errMsg.push(err);\n }\n }\n return flag;\n }\n\n validates(source, rules) {\n for (let key of Object.keys(rules)){\n if(source[key]){\n let result = this.validate(source[key], rules[key]);\n if(!result){\n const firstError = this.errMsg.shift();\n this.errMsg = [];\n return firstError;\n }\n }else{\n return rules[key][0].errorMsg;\n }\n }\n\n }\n\n}\n\n// 调用\nconst firstError = new Validator().validates(this.state.driver, {\n ...\n car_model: [{\n strategy:'isRequired',\n errorMsg:'车牌号不能为空'\n },{\n strategy:'isCar',\n errorMsg:'车牌号格式错误'\n }]\n ...\n });\n if (firstError) {\n Alert.alert(firstError);\n return;\n }\n```\n\n主要改动在于 validates 函数,这个函数接受两个参数 ` source ` 和 ` rules ` , ` source ` 是一个包含了所有待验证输入字段的源对象,大部分情况下就是 state 或 state 中的对象; ` rules ` 是一个对象,它的 ` Object.keys ` 求值结果是 ` source ` 求值结果的子集,也就是说每一个 ` rules ` 的键都能够在 ` source ` 中找到,所以我遍历了 ` rules ` 的 ` key ` 然后向 ` validate ` 方法传入 ` source[key] ` 和 ` rules[key] ` 进行校验, ` validate ` 方法改动不大。\n\n最后来说说不足的地方,首先是没能够将错误反映到实际的表单当中,而是 alert 的方式,体验稍差,这部分是需要改进的。其次,实际使用当中,当字段很多时,有不少重复的规则,是否可以合并?也就是换个角度来做这个问题!不过在这个项目中已经够用了。\n\n不早了,先写到这,后面在研究下,撸一个更好,更通用的版本!","html":"<p>写之前先吐槽一下:昨晚半夜起来好像把眼镜踩坏了😭,下午只好去了趟眼镜店,顶着杭城下午两点的太阳,也是很拼。没想到传来了更大的噩耗🙄,测散光时说我原来的眼镜散光做反了,没得救了😒。最后只好勉强得整了一下,但愿新镜片能够慢慢调整好😂。</p>\n\n<p>然后是正片,今天鱼头说把司机招募的验证加上去,刚好写完后台消息的AOP没什么事,所以我就寻思写的一劳永逸的验证模块,岂不是很好很nice。不过作为懒人还是先去 github 上着了一番,不过没有比较适合现在的项目的。主要原因还是页面已经完成了,改用其它组件的工程量太大。所以最后决定自己整一个不侵入现有组件的,只关注验证逻辑,可扩展的验证模块。</p>\n\n<p>先来看看引入插件之前的的表单验证:</p>\n\n<pre><code>handleSubmit() { \n if(!this.state.credit_card){\n Alert.alert('必须填写银行卡号!');\n return;\n }\n if(!this.state.rest || this.state.rest < 0){\n Alert.alert('请填写正确的提现金额!');\n return;\n }\n if(this.state.rest > this.props.driver.driver_state.rest){\n Alert.alert('提现金额不能大于余额!');\n return;\n }\n rest.post...\n }\n</code></pre>\n\n<p>可以看到是通过 if 进行判断,这种做法,代码十分丑陋,而且复用性十分低。有什么办法能够改进上面的代码呢!进一步思考,其实表单验证的逻辑无非是对某个表单的输入,进行相关逻辑的判断。进行的逻辑判断是不变的,而判断的方式是可以改变的。是不是有点感觉了,我们可以把不变的逻辑固定下来,而可变的策略抽离出来。说到底就是策略模式了,策略模式就是通过把可变的判断策略提取出来,帮助我们省去那些 <code>if</code> 。废话不多说,先来看看在 web 上我们是如何使用的:<a href=\"http://brizer.github.io/2016/05/04/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5/\">出处</a></p>\n\n<pre><code>//定义验证策略\nvar strategies = { \n inNonEmpty:function(value,errorMsg){//不能为空\n if(value === ''){\n return errorMsg;\n }\n },\n minLength:function(value,length,errorMsg){//最小长度\n if(value.length < length){\n return errorMsg;\n }\n },\n isReg:function(value,reg,errorMsg){//正则匹配\n if(!reg.test(value)){\n return errorMsg;\n }\n }\n};\n//定义验证类\nvar Validator = function(){ \n this.cache = [];//保存效验规则\n};\n//添加验证规则\nValidator.prototype.add = function(dom,rules){ \n var that = this;\n //多条规则分别对应\n for(var i = 0 ,rule; rule = rules[i++];){\n (function(rule){\n var stratgyAry = rule.strategy.split(':');\n var errorMsg = rule.errorMsg;\n //将数据按照策略的格式塞入参数数组\n that.cache.push(function(){\n var strategy = strategyAry.shift();\n strategyAry.unshift(dom.value);\n strategyAry.push(errorMsg);\n return strategies[strategy].apply(dom,strategyAry);\n });\n })(rule)\n }\n};\nValidator.prototype.start = function(){ \n //依次验证\n for(var i = 0,validatorFunc;validatorFunc = this.cache[i++];){\n var errorMsg = validatorFunc();\n if(errorMsg){\n return errorMsg;\n }\n }\n};\n\n//调用代码\nvar validataFunc = function(){ \n var validator = new Validator();\n //多个条件判断\n validator.add(form.name,[{\n strategy:'isNonEmpty',\n errorMsg:'用户不能为空'\n },{\n strategy:'minLength:10',\n errorMsg:'用户长度不能小于10位'\n }]);\n validator.add(form.password,[{\n strategy:'minLength:6',\n errorMsg:'密码长度不能低于6位'\n }]);\n var errorMsg = validator.start();\n return errorMsg;\n}\nform.onsubmit = function(){ \n var errorMsg = validateFunc();\n if(errorMsg){\n alert(errorMsg);\n return false;\n }\n //验证通过,提交表单\n}\n</code></pre>\n\n<p>上面的代码简单来说,就是定义了一个验证器,在验证前把验证的 dom 和 对应的策略添加到验证器中,然后调用 start 函数,一次运行验证函数,如有验证错误则返回错误信息。这样我们就可以复用验证策略,只需要关心每个输入具体要符合什么规则并传入就可以了,代码也很清爽。</p>\n\n<p>但是这还不能直接运用到 rn 当中,需要稍微改造下,因为我没有 dom ,我有的是 state。<a href=\"https://github.com/gcanti/tcomb-form-native\">tcomb-form-native</a>这个库给了我一些灵感。</p>\n\n<pre><code>// here we are: define your domain model\nvar Person = t.struct({ \n name: t.String, // a required string\n surname: t.maybe(t.String), // an optional string\n age: t.Number, // a required number\n rememberMe: t.Boolean // a boolean\n});\n</code></pre>\n\n<p>它是这么定义验证的信息的,那其实我的 state 当中的需要验证的字段和验证规则之间也可以建立一种类似的规则。于是就有了我最后的简陋版的验证插件,先看代码,然后再谈谈不足:</p>\n\n<pre><code>/**\n * Created by m2mbob on 16/7/20.\n */\nconst strategies = { \n isRequired:function(value,errorMsg) {\n if(value === ''){\n return errorMsg;\n }\n },\n maxLength:function(value,length,errorMsg) {//最小长度\n if(value.length > length){\n return errorMsg;\n }\n },\n minLength:function(value,length,errorMsg) {//最小长度\n if(value.length < length){\n return errorMsg;\n }\n },\n isPhone:function(value,errorMsg) {\n return this.isReg(value, /^1[3|4|5|7|8]\\d{9}$/g, errorMsg);\n },\n isEmail:function(value, errorMsg) {\n return this.isReg(value, /^(\\w-*\\.*)+@(\\w-?)+(\\.\\w{2,})+$/g, errorMsg);\n },\n isIdCard:function(value, errorMsg) {\n return this.isReg(value, /(^\\d{15}$)|(^\\d{17}([0-9]|X)$)/g, errorMsg);\n },\n isCar:function(value, errorMsg) {\n return this.isReg(value, /^[\\u4e00-\\u9fa5]{1}[A-Z]{1}[A-Z_0-9]{5}$/g, errorMsg);\n },\n isReg:function(value,reg,errorMsg) {//正则匹配\n if(!reg.test(value)){\n return errorMsg;\n }\n }\n};\n\nexport default class Validator { \n constructor() {\n this.errMsg = [];\n }\n\n validate(value, rules) {\n let flag = true;\n for(let i = 0 ,rule; rule = rules[i++];){\n let strategyAry = rule.strategy.split(':');\n let errorMsg = rule.errorMsg;\n let strategy = strategyAry.shift();\n strategyAry.unshift(value);\n strategyAry.push(errorMsg);\n let err = strategies[strategy].apply(strategies, strategyAry);\n if(err){\n flag = false;\n this.errMsg.push(err);\n }\n }\n return flag;\n }\n\n validates(source, rules) {\n for (let key of Object.keys(rules)){\n if(source[key]){\n let result = this.validate(source[key], rules[key]);\n if(!result){\n const firstError = this.errMsg.shift();\n this.errMsg = [];\n return firstError;\n }\n }else{\n return rules[key][0].errorMsg;\n }\n }\n\n }\n\n}\n\n// 调用\nconst firstError = new Validator().validates(this.state.driver, { \n ...\n car_model: [{\n strategy:'isRequired',\n errorMsg:'车牌号不能为空'\n },{\n strategy:'isCar',\n errorMsg:'车牌号格式错误'\n }]\n ...\n });\n if (firstError) {\n Alert.alert(firstError);\n return;\n }\n</code></pre>\n\n<p>主要改动在于 validates 函数,这个函数接受两个参数 <code>source</code> 和 <code>rules</code> , <code>source</code> 是一个包含了所有待验证输入字段的源对象,大部分情况下就是 state 或 state 中的对象; <code>rules</code> 是一个对象,它的 <code>Object.keys</code> 求值结果是 <code>source</code> 求值结果的子集,也就是说每一个 <code>rules</code> 的键都能够在 <code>source</code> 中找到,所以我遍历了 <code>rules</code> 的 <code>key</code> 然后向 <code>validate</code> 方法传入 <code>source[key]</code> 和 <code>rules[key]</code> 进行校验, <code>validate</code> 方法改动不大。</p>\n\n<p>最后来说说不足的地方,首先是没能够将错误反映到实际的表单当中,而是 alert 的方式,体验稍差,这部分是需要改进的。其次,实际使用当中,当字段很多时,有不少重复的规则,是否可以合并?也就是换个角度来做这个问题!不过在这个项目中已经够用了。</p>\n\n<p>不早了,先写到这,后面在研究下,撸一个更好,更通用的版本!</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469026705376,"created_by":1,"updated_at":1469029771903,"updated_by":1,"published_at":1469029771905,"published_by":1},{"id":32,"uuid":"bc1e84b8-b50e-41b3-ad8d-acea8fa0d04c","title":"(Untitled)","slug":"untitled-2","markdown":"","html":"","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469065163248,"created_by":1,"updated_at":1469065166378,"updated_by":1,"published_at":null,"published_by":null},{"id":33,"uuid":"b0c15dab-e1f8-41fc-8bd1-d6de3326ca2b","title":"react native —— 安卓知识补充篇","slug":"react-native-an-zhuo-zhi-shi-bu-chong-pian","markdown":"","html":"","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469240531228,"created_by":1,"updated_at":1469279246152,"updated_by":1,"published_at":null,"published_by":null},{"id":34,"uuid":"44877e28-bbec-4496-a6f0-b21cc68f2c79","title":"Android Studio 中 .gitignore 的编写","slug":"zhuan-android-studio-zhong-gitignore-de-bian-xie","markdown":"<embed src=\"http://www.xiami.com/widget/49883737_1770692731/singlePlayer.swf\" type=\"application/x-shockwave-flash\" width=\"257\" height=\"33\" wmode=\"transparent\"></embed>\n\n最近花了比较大的力气,写了一个 react native 版高德地图定位及后台持续监听定位的插件,主要写的是安卓的原生代码,所以后面会有不少关于本次编写当中原生知识相关的博文!这算是第一篇😪。还有好久木有放歌了,懒得回去加了,不过这首确实很好听,如果弹过吉他更会有不同的体会,T121 3121!😊。\n\n因为之前的项目的 .gitignore 文件都是 node 或者 react 相关的模板,或者稍微定制一下,用在这个插件当中,发现传上去很多不必要的文件,所以找了一个比较通用的放在这,以后肯定用得到。\n\n```\n# built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\nout/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Windows thumbnail db\nThumbs.db\n\n# OSX files\n.DS_Store\n\n# Eclipse project files\n.classpath\n.project\n\n# Android Studio\n*.iml\n.idea\n\n# Local IDEA workspace\n.idea/workspace.xml\n\n# Gradle cache\n.gradle\n\n#NDK\nobj/\n```","html":"<p><embed src=\"http://www.xiami.com/widget/49883737_1770692731/singlePlayer.swf\" type=\"application/x-shockwave-flash\" width=\"257\" height=\"33\" wmode=\"transparent\"></embed></p>\n\n<p>最近花了比较大的力气,写了一个 react native 版高德地图定位及后台持续监听定位的插件,主要写的是安卓的原生代码,所以后面会有不少关于本次编写当中原生知识相关的博文!这算是第一篇😪。还有好久木有放歌了,懒得回去加了,不过这首确实很好听,如果弹过吉他更会有不同的体会,T121 3121!😊。</p>\n\n<p>因为之前的项目的 .gitignore 文件都是 node 或者 react 相关的模板,或者稍微定制一下,用在这个插件当中,发现传上去很多不必要的文件,所以找了一个比较通用的放在这,以后肯定用得到。</p>\n\n<pre><code># built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/ \ngen/ \nout/ \nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Windows thumbnail db\nThumbs.db\n\n# OSX files\n.DS_Store\n\n# Eclipse project files\n.classpath\n.project\n\n# Android Studio\n*.iml\n.idea\n\n# Local IDEA workspace\n.idea/workspace.xml\n\n# Gradle cache\n.gradle\n\n#NDK\nobj/ \n</code></pre>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469436400951,"created_by":1,"updated_at":1469437453475,"updated_by":1,"published_at":1469436760761,"published_by":1}],"users":[{"id":1,"uuid":"f183c222-672a-47e1-80a4-561830ba1d27","name":"m2mbob","slug":"m2mbob","password":"$2a$10$Xcdj.QcZXVRWoWmvgQTC/.QvFXigb4CHjfLM0kE.44gNKy/j0Z6oq","email":"[email protected]","image":"/content/images/2016/05/113.jpeg","cover":null,"bio":"不会开车的老司机","website":"https://xxn520.github.io/jiayi520/","location":"杭州","accessibility":null,"status":"active","language":"en_US","meta_title":null,"meta_description":null,"tour":null,"last_login":1469240452688,"created_at":1463567133080,"created_by":1,"updated_at":1469240452688,"updated_by":1},{"id":5,"uuid":"26139dd3-2a1d-41f0-a185-654731c39cef","name":"laowei38ban","slug":"laowei38ban","password":"$2a$10$5k4p6NzBZwU7vcH4zdMtl.mxkc.oXKCywxgDjRGoGXaGc0CVnjDc.","email":"[email protected]","image":null,"cover":null,"bio":null,"website":null,"location":null,"accessibility":null,"status":"invited-pending","language":"zh_CN","meta_title":null,"meta_description":null,"tour":null,"last_login":null,"created_at":1467527925700,"created_by":1,"updated_at":1467528053760,"updated_by":5}],"roles":[{"id":1,"uuid":"a205663c-0051-448b-8d64-c083afc8cd1f","name":"Administrator","description":"Administrators","created_at":1463567128189,"created_by":1,"updated_at":1463567128189,"updated_by":1},{"id":2,"uuid":"f3c3f834-15cf-4992-b37e-9fc241cce9ea","name":"Editor","description":"Editors","created_at":1463567128217,"created_by":1,"updated_at":1463567128217,"updated_by":1},{"id":3,"uuid":"974b0d0e-2a8c-4c45-9932-dbb233bc9cc8","name":"Author","description":"Authors","created_at":1463567128243,"created_by":1,"updated_at":1463567128243,"updated_by":1},{"id":4,"uuid":"e1e437c2-a08b-4152-bb91-130ff9670e31","name":"Owner","description":"Blog Owner","created_at":1463567128271,"created_by":1,"updated_at":1463567128271,"updated_by":1}],"roles_users":[{"id":1,"role_id":4,"user_id":1},{"id":2,"role_id":3,"user_id":2},{"id":3,"role_id":3,"user_id":3},{"id":4,"role_id":3,"user_id":4},{"id":5,"role_id":3,"user_id":5}],"permissions":[{"id":1,"uuid":"17ffb52c-5e47-4520-bb3a-923eee539fae","name":"Export database","object_type":"db","action_type":"exportContent","object_id":null,"created_at":1463567128298,"created_by":1,"updated_at":1463567128298,"updated_by":1},{"id":2,"uuid":"0b603a34-66db-4290-9f1c-17411a32d5af","name":"Import database","object_type":"db","action_type":"importContent","object_id":null,"created_at":1463567128326,"created_by":1,"updated_at":1463567128326,"updated_by":1},{"id":3,"uuid":"63d14f73-132e-4504-9f3c-a5496e529604","name":"Delete all content","object_type":"db","action_type":"deleteAllContent","object_id":null,"created_at":1463567128351,"created_by":1,"updated_at":1463567128351,"updated_by":1},{"id":4,"uuid":"4b3cd8a9-e96e-45f1-8746-ba36e61d837b","name":"Send mail","object_type":"mail","action_type":"send","object_id":null,"created_at":1463567128379,"created_by":1,"updated_at":1463567128379,"updated_by":1},{"id":5,"uuid":"b3a9c3fe-26ef-4f19-895a-bb6b09cfa07a","name":"Browse notifications","object_type":"notification","action_type":"browse","object_id":null,"created_at":1463567128407,"created_by":1,"updated_at":1463567128407,"updated_by":1},{"id":6,"uuid":"7d1c64de-07c8-49e8-87cf-eef785bd3500","name":"Add notifications","object_type":"notification","action_type":"add","object_id":null,"created_at":1463567128431,"created_by":1,"updated_at":1463567128431,"updated_by":1},{"id":7,"uuid":"d490cd6d-e086-4e27-86f0-4a4638f020b0","name":"Delete notifications","object_type":"notification","action_type":"destroy","object_id":null,"created_at":1463567128456,"created_by":1,"updated_at":1463567128456,"updated_by":1},{"id":8,"uuid":"e00cf85a-7d10-4908-9952-164dbb7e76d5","name":"Browse posts","object_type":"post","action_type":"browse","object_id":null,"created_at":1463567128480,"created_by":1,"updated_at":1463567128480,"updated_by":1},{"id":9,"uuid":"388edf22-9724-4936-840b-4972018c83f5","name":"Read posts","object_type":"post","action_type":"read","object_id":null,"created_at":1463567128506,"created_by":1,"updated_at":1463567128506,"updated_by":1},{"id":10,"uuid":"8cafbddb-afdb-4ce8-82be-17dd7dab9b39","name":"Edit posts","object_type":"post","action_type":"edit","object_id":null,"created_at":1463567128534,"created_by":1,"updated_at":1463567128534,"updated_by":1},{"id":11,"uuid":"2f54798f-05bc-45b4-a477-0cae3a8c15fb","name":"Add posts","object_type":"post","action_type":"add","object_id":null,"created_at":1463567128556,"created_by":1,"updated_at":1463567128556,"updated_by":1},{"id":12,"uuid":"b85480a8-ff4d-4ed1-baad-595ea2452fca","name":"Delete posts","object_type":"post","action_type":"destroy","object_id":null,"created_at":1463567128578,"created_by":1,"updated_at":1463567128578,"updated_by":1},{"id":13,"uuid":"4760b85c-cc79-4ee4-87c1-643bc500136e","name":"Browse settings","object_type":"setting","action_type":"browse","object_id":null,"created_at":1463567128602,"created_by":1,"updated_at":1463567128602,"updated_by":1},{"id":14,"uuid":"b60aa8d7-8605-4c2e-84bb-35fd98b9e05b","name":"Read settings","object_type":"setting","action_type":"read","object_id":null,"created_at":1463567128631,"created_by":1,"updated_at":1463567128631,"updated_by":1},{"id":15,"uuid":"a7437212-afe7-4df4-9370-9e4cb8507d64","name":"Edit settings","object_type":"setting","action_type":"edit","object_id":null,"created_at":1463567128655,"created_by":1,"updated_at":1463567128655,"updated_by":1},{"id":16,"uuid":"c334e5b6-c398-4b0b-ad00-9b3c747aa564","name":"Generate slugs","object_type":"slug","action_type":"generate","object_id":null,"created_at":1463567128688,"created_by":1,"updated_at":1463567128688,"updated_by":1},{"id":17,"uuid":"3338699f-c4e5-41b8-9ca4-4d22be830633","name":"Browse tags","object_type":"tag","action_type":"browse","object_id":null,"created_at":1463567128719,"created_by":1,"updated_at":1463567128719,"updated_by":1},{"id":18,"uuid":"b58d136d-763d-4cfc-bcc3-ab32fdc2d4d1","name":"Read tags","object_type":"tag","action_type":"read","object_id":null,"created_at":1463567128746,"created_by":1,"updated_at":1463567128746,"updated_by":1},{"id":19,"uuid":"76384049-8ad7-4233-9259-f7b511018001","name":"Edit tags","object_type":"tag","action_type":"edit","object_id":null,"created_at":1463567128770,"created_by":1,"updated_at":1463567128770,"updated_by":1},{"id":20,"uuid":"2c117ad5-76ab-438a-9e2e-2a707d15b2fa","name":"Add tags","object_type":"tag","action_type":"add","object_id":null,"created_at":1463567128795,"created_by":1,"updated_at":1463567128795,"updated_by":1},{"id":21,"uuid":"94bf77cf-2356-47ba-be2b-6e51ff3b4e0b","name":"Delete tags","object_type":"tag","action_type":"destroy","object_id":null,"created_at":1463567128820,"created_by":1,"updated_at":1463567128820,"updated_by":1},{"id":22,"uuid":"6f3064f3-7109-4049-807b-0b5986a00077","name":"Browse themes","object_type":"theme","action_type":"browse","object_id":null,"created_at":1463567128848,"created_by":1,"updated_at":1463567128848,"updated_by":1},{"id":23,"uuid":"589fe891-4edb-4504-bcdd-443950122687","name":"Edit themes","object_type":"theme","action_type":"edit","object_id":null,"created_at":1463567128876,"created_by":1,"updated_at":1463567128876,"updated_by":1},{"id":24,"uuid":"91e1d531-9886-4cf5-ab90-868e851150ee","name":"Browse users","object_type":"user","action_type":"browse","object_id":null,"created_at":1463567128903,"created_by":1,"updated_at":1463567128903,"updated_by":1},{"id":25,"uuid":"f898d0b8-7a24-4da8-9ae0-d2d71082c0fe","name":"Read users","object_type":"user","action_type":"read","object_id":null,"created_at":1463567128926,"created_by":1,"updated_at":1463567128926,"updated_by":1},{"id":26,"uuid":"69769fb8-0707-41d9-a941-108e78458828","name":"Edit users","object_type":"user","action_type":"edit","object_id":null,"created_at":1463567128950,"created_by":1,"updated_at":1463567128950,"updated_by":1},{"id":27,"uuid":"f077887e-a6bb-4d7e-a5a2-669f2c709db7","name":"Add users","object_type":"user","action_type":"add","object_id":null,"created_at":1463567128976,"created_by":1,"updated_at":1463567128976,"updated_by":1},{"id":28,"uuid":"1a13365e-a47e-4355-92fd-5ca74fa7259e","name":"Delete users","object_type":"user","action_type":"destroy","object_id":null,"created_at":1463567129004,"created_by":1,"updated_at":1463567129004,"updated_by":1},{"id":29,"uuid":"d331b27f-cd04-43f2-8e13-4cc90a9fe017","name":"Assign a role","object_type":"role","action_type":"assign","object_id":null,"created_at":1463567129036,"created_by":1,"updated_at":1463567129036,"updated_by":1},{"id":30,"uuid":"d6a8f870-d4aa-4565-bbb9-08274700fcd7","name":"Browse roles","object_type":"role","action_type":"browse","object_id":null,"created_at":1463567129059,"created_by":1,"updated_at":1463567129059,"updated_by":1}],"permissions_users":[],"permissions_roles":[{"id":1,"role_id":1,"permission_id":1},{"id":2,"role_id":1,"permission_id":2},{"id":3,"role_id":1,"permission_id":3},{"id":4,"role_id":1,"permission_id":4},{"id":5,"role_id":1,"permission_id":5},{"id":6,"role_id":1,"permission_id":6},{"id":7,"role_id":1,"permission_id":7},{"id":8,"role_id":1,"permission_id":8},{"id":9,"role_id":1,"permission_id":9},{"id":10,"role_id":1,"permission_id":10},{"id":11,"role_id":1,"permission_id":11},{"id":12,"role_id":1,"permission_id":12},{"id":13,"role_id":1,"permission_id":13},{"id":14,"role_id":1,"permission_id":14},{"id":15,"role_id":1,"permission_id":15},{"id":16,"role_id":1,"permission_id":16},{"id":17,"role_id":1,"permission_id":17},{"id":18,"role_id":1,"permission_id":18},{"id":19,"role_id":1,"permission_id":19},{"id":20,"role_id":1,"permission_id":20},{"id":21,"role_id":1,"permission_id":21},{"id":22,"role_id":1,"permission_id":22},{"id":23,"role_id":1,"permission_id":23},{"id":24,"role_id":1,"permission_id":24},{"id":25,"role_id":1,"permission_id":25},{"id":26,"role_id":1,"permission_id":26},{"id":27,"role_id":1,"permission_id":27},{"id":28,"role_id":1,"permission_id":28},{"id":29,"role_id":1,"permission_id":29},{"id":30,"role_id":1,"permission_id":30},{"id":31,"role_id":2,"permission_id":8},{"id":32,"role_id":2,"permission_id":9},{"id":33,"role_id":2,"permission_id":10},{"id":34,"role_id":2,"permission_id":11},{"id":35,"role_id":2,"permission_id":12},{"id":36,"role_id":2,"permission_id":13},{"id":37,"role_id":2,"permission_id":14},{"id":38,"role_id":2,"permission_id":16},{"id":39,"role_id":2,"permission_id":17},{"id":40,"role_id":2,"permission_id":18},{"id":41,"role_id":2,"permission_id":19},{"id":42,"role_id":2,"permission_id":20},{"id":43,"role_id":2,"permission_id":21},{"id":44,"role_id":2,"permission_id":24},{"id":45,"role_id":2,"permission_id":25},{"id":46,"role_id":2,"permission_id":26},{"id":47,"role_id":2,"permission_id":27},{"id":48,"role_id":2,"permission_id":28},{"id":49,"role_id":2,"permission_id":29},{"id":50,"role_id":2,"permission_id":30},{"id":51,"role_id":3,"permission_id":8},{"id":52,"role_id":3,"permission_id":9},{"id":53,"role_id":3,"permission_id":11},{"id":54,"role_id":3,"permission_id":13},{"id":55,"role_id":3,"permission_id":14},{"id":56,"role_id":3,"permission_id":16},{"id":57,"role_id":3,"permission_id":17},{"id":58,"role_id":3,"permission_id":18},{"id":59,"role_id":3,"permission_id":20},{"id":60,"role_id":3,"permission_id":24},{"id":61,"role_id":3,"permission_id":25},{"id":62,"role_id":3,"permission_id":30}],"permissions_apps":[],"settings":[{"id":1,"uuid":"07767b0a-31a8-4f7c-9089-0cac6be45867","key":"databaseVersion","value":"004","type":"core","created_at":1463567133162,"created_by":1,"updated_at":1463567133162,"updated_by":1},{"id":2,"uuid":"64a07b8e-9ca6-4464-940a-985f4241f6e7","key":"dbHash","value":"e00711f4-02e7-4f60-a9c5-b870666a2a83","type":"core","created_at":1463567133162,"created_by":1,"updated_at":1463567133836,"updated_by":1},{"id":3,"uuid":"d57d0b4c-9dbf-4810-a010-651b42825bf5","key":"nextUpdateCheck","value":"1469522736","type":"core","created_at":1463567133163,"created_by":1,"updated_at":1469436394759,"updated_by":1},{"id":4,"uuid":"b43fd992-cc8f-4f1e-bc47-c060dfdcae3f","key":"displayUpdateNotification","value":"0.7.0","type":"core","created_at":1463567133163,"created_by":1,"updated_at":1469436394761,"updated_by":1},{"id":5,"uuid":"0b0d23f5-c9f3-4a41-9c47-ba9170f1cec5","key":"title","value":"m2mbob","type":"blog","created_at":1463567133163,"created_by":1,"updated_at":1466844299104,"updated_by":1},{"id":6,"uuid":"e53b4cef-36a8-4208-9605-9dd6062e7407","key":"description","value":"Thoughts, stories and ideas.","type":"blog","created_at":1463567133163,"created_by":1,"updated_at":1466844299105,"updated_by":1},{"id":7,"uuid":"adccbe11-7283-472b-b363-b517530d874c","key":"logo","value":"","type":"blog","created_at":1463567133163,"created_by":1,"updated_at":1466844299106,"updated_by":1},{"id":8,"uuid":"9ef9dbff-161f-45db-921e-4857f9d617b3","key":"cover","value":"","type":"blog","created_at":1463567133164,"created_by":1,"updated_at":1466844299107,"updated_by":1},{"id":9,"uuid":"c205e750-b7b4-4a65-be94-e33aa231e523","key":"defaultLang","value":"en_US","type":"blog","created_at":1463567133164,"created_by":1,"updated_at":1466844299112,"updated_by":1},{"id":10,"uuid":"d7917f90-456e-42d0-a6fe-429f84898f0b","key":"postsPerPage","value":"5","type":"blog","created_at":1463567133164,"created_by":1,"updated_at":1466844299114,"updated_by":1},{"id":11,"uuid":"66503e4a-acf0-4aca-8dbc-0e36ae39bcdd","key":"forceI18n","value":"true","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1466844299115,"updated_by":1},{"id":12,"uuid":"8755c9a1-7e8c-42c6-a665-df37701f6072","key":"permalinks","value":"/:year/:month/:day/:slug/","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1466844299118,"updated_by":1},{"id":13,"uuid":"1b0a112b-d8b5-47b6-8b13-56c323eaa83e","key":"ghost_head","value":"","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1466844299120,"updated_by":1},{"id":14,"uuid":"9675e29f-a962-49a0-ad3a-f15533d0f7dc","key":"ghost_foot","value":"","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1466844299121,"updated_by":1},{"id":15,"uuid":"6d4ab5d5-75d4-4b1e-b5ec-6435f411cb3a","key":"labs","value":"{}","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1466844299123,"updated_by":1},{"id":16,"uuid":"a10d0aed-69e5-4de0-899c-8ad0fbc6c955","key":"navigation","value":"[{\"label\":\"Home\",\"url\":\"http://m2mbob.cn:1314/\"},{\"label\":\"admin\",\"url\":\"/ghost/\"}]","type":"blog","created_at":1463567133166,"created_by":1,"updated_at":1466844299124,"updated_by":1},{"id":17,"uuid":"821e540b-22d6-49b5-acb5-bc963b77100f","key":"activeApps","value":"[]","type":"app","created_at":1463567133166,"created_by":1,"updated_at":1463567133166,"updated_by":1},{"id":18,"uuid":"2a154f41-dcc1-4035-bbe9-886d2f715d8e","key":"installedApps","value":"[]","type":"app","created_at":1463567133167,"created_by":1,"updated_at":1465095629640,"updated_by":1},{"id":19,"uuid":"96f6c4a2-30d4-4490-968d-449261420d65","key":"isPrivate","value":"false","type":"private","created_at":1463567133167,"created_by":1,"updated_at":1466844299125,"updated_by":1},{"id":20,"uuid":"90857a68-2fe1-4798-9654-88a635a54ddb","key":"password","value":"null","type":"private","created_at":1463567133167,"created_by":1,"updated_at":1466844299126,"updated_by":1},{"id":21,"uuid":"fcd59843-2747-411c-8ab4-7e97a1c9c3b4","key":"activeTheme","value":"casper","type":"theme","created_at":1463567133166,"created_by":1,"updated_at":1466844299119,"updated_by":1}],"tags":[{"id":1,"uuid":"ecb77cf1-1be2-443a-b555-f1c1fa8992fb","name":"get-started","slug":"get-started","description":"get-started","image":"","hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463567128109,"created_by":1,"updated_at":1463754330921,"updated_by":1},{"id":2,"uuid":"f73b4d4f-12cc-498c-a159-be13b472fec7","name":"redux源码分析系列","slug":"redux","description":"","image":"/content/images/2016/05/687474703a2f2f692e696d6775722e636f6d2f4a65567164514d2e706e67-1.png","hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463754046804,"created_by":1,"updated_at":1463817817169,"updated_by":1},{"id":3,"uuid":"2ce8db62-0a14-47c4-b1b8-1e3b920c420a","name":"life","slug":"life","description":"关于生活","image":"/content/images/2016/05/u-1687420627-4175839764-fm-21-gp-0.jpg","hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463817797112,"created_by":1,"updated_at":1463817887321,"updated_by":1},{"id":4,"uuid":"495550a6-a385-4ff4-8e4f-afb9870f94fa","name":"乱七八糟","slug":"digression","description":"杂谈","image":"/content/images/2016/05/u-1468361829-4039309630-fm-116-gp-0.jpg","hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463834283409,"created_by":1,"updated_at":1463834926309,"updated_by":1},{"id":5,"uuid":"4723a273-0582-4a37-842d-c771a73f85f8","name":"mongodb","slug":"mongodb","description":"","image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463838088406,"created_by":1,"updated_at":1463838094311,"updated_by":1},{"id":6,"uuid":"31ada047-4501-406d-917f-488e23b03f36","name":"mac","slug":"mac","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463838131596,"created_by":1,"updated_at":1463838131596,"updated_by":1},{"id":7,"uuid":"91b7d980-5ce6-4340-aaf1-9a6b5dc80667","name":"koa","slug":"koa","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463838270749,"created_by":1,"updated_at":1463838270749,"updated_by":1},{"id":8,"uuid":"be14e3b0-fb8f-4924-81b5-0924b220508c","name":"nodejs","slug":"nodejs","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463838276529,"created_by":1,"updated_at":1463838283586,"updated_by":1},{"id":9,"uuid":"c332f075-6919-4dd1-9b6e-cdd1b93e86db","name":"react","slug":"react","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1464247836082,"created_by":1,"updated_at":1464247836082,"updated_by":1},{"id":10,"uuid":"5d8a3fba-d4a7-4e50-856f-2b3034b30d5d","name":"git","slug":"git","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1464335179221,"created_by":1,"updated_at":1464335179221,"updated_by":1},{"id":11,"uuid":"3dc60c76-7ce0-44e2-93eb-da3879afc085","name":"eslint","slug":"eslint","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1464346513640,"created_by":1,"updated_at":1464346513640,"updated_by":1},{"id":12,"uuid":"5f5ac01a-3d8d-4fde-bf30-52f04398df03","name":"react native","slug":"react-native","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1466558983520,"created_by":1,"updated_at":1466558983520,"updated_by":1},{"id":13,"uuid":"f2a13e9c-f961-4e08-96be-16fcb05eca63","name":"人生","slug":"ren-sheng","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1467111340939,"created_by":1,"updated_at":1467111340939,"updated_by":1},{"id":14,"uuid":"985d4ca6-c298-4e23-83d8-93dac19d7987","name":"有趣视频","slug":"you-qu-shi-pin","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1467111340949,"created_by":1,"updated_at":1467111340949,"updated_by":1},{"id":15,"uuid":"768f7c77-48fa-40ee-9b14-b8dd4d8e7393","name":"翻译","slug":"fan-yi","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1467688909351,"created_by":1,"updated_at":1467688909351,"updated_by":1},{"id":16,"uuid":"f3ad56e4-023f-45ee-bdd1-2bd05ea6f8d2","name":"随笔","slug":"sui-bi","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1468460842999,"created_by":1,"updated_at":1468460842999,"updated_by":1},{"id":17,"uuid":"a3c86f17-4e46-41f9-b79e-e8f12c534a1b","name":"常识","slug":"chang-shi","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1468469618360,"created_by":1,"updated_at":1468469618360,"updated_by":1},{"id":18,"uuid":"257fd3de-50cc-4358-a378-78a6e7d561d0","name":"js","slug":"js","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1468896571000,"created_by":1,"updated_at":1468896571000,"updated_by":1},{"id":19,"uuid":"052b64f0-5ab2-4dad-a3a7-63351c98ff62","name":"策略模式","slug":"ce-lue-mo-shi","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1469029769238,"created_by":1,"updated_at":1469029769238,"updated_by":1},{"id":20,"uuid":"cbb3738c-77e3-43ac-bb21-cc594f49b866","name":"android","slug":"android","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1469436760798,"created_by":1,"updated_at":1469436760798,"updated_by":1}],"posts_tags":[{"id":1,"post_id":1,"tag_id":1,"sort_order":0},{"id":2,"post_id":2,"tag_id":2,"sort_order":0},{"id":3,"post_id":3,"tag_id":4,"sort_order":0},{"id":4,"post_id":3,"tag_id":6,"sort_order":1},{"id":5,"post_id":4,"tag_id":5,"sort_order":0},{"id":6,"post_id":4,"tag_id":6,"sort_order":1},{"id":7,"post_id":5,"tag_id":7,"sort_order":0},{"id":8,"post_id":5,"tag_id":8,"sort_order":1},{"id":9,"post_id":6,"tag_id":4,"sort_order":0},{"id":10,"post_id":9,"tag_id":4,"sort_order":0},{"id":11,"post_id":8,"tag_id":9,"sort_order":0},{"id":12,"post_id":10,"tag_id":10,"sort_order":0},{"id":13,"post_id":11,"tag_id":11,"sort_order":0},{"id":14,"post_id":13,"tag_id":12,"sort_order":0},{"id":15,"post_id":15,"tag_id":12,"sort_order":0},{"id":16,"post_id":19,"tag_id":13,"sort_order":0},{"id":17,"post_id":19,"tag_id":14,"sort_order":1},{"id":18,"post_id":23,"tag_id":12,"sort_order":0},{"id":19,"post_id":12,"tag_id":9,"sort_order":0},{"id":20,"post_id":12,"tag_id":15,"sort_order":1},{"id":21,"post_id":24,"tag_id":9,"sort_order":0},{"id":22,"post_id":24,"tag_id":15,"sort_order":1},{"id":23,"post_id":27,"tag_id":16,"sort_order":0},{"id":24,"post_id":28,"tag_id":17,"sort_order":0},{"id":25,"post_id":30,"tag_id":18,"sort_order":0},{"id":26,"post_id":31,"tag_id":12,"sort_order":0},{"id":27,"post_id":31,"tag_id":18,"sort_order":1},{"id":28,"post_id":31,"tag_id":19,"sort_order":2},{"id":29,"post_id":34,"tag_id":20,"sort_order":1},{"id":30,"post_id":34,"tag_id":10,"sort_order":0}],"apps":[],"app_settings":[],"app_fields":[],"client_trusted_domains":[]}}]}