什么是样式防御?
顾名思义,就是保护你写的样式最后是你想展现的样子。一般样式被破坏多发生于第三方应用编程中,当编写的组件被应用于第三方网站中,组件的样式经常会被网站本身的样式影响而破坏了它本来想展现的样子。
为什么会研究这个东西呢?因为有一天,我引用的第三方代码的 CSS 被我网站上的 CSS 影响了。于是,我噼里啪啦赶紧把第三方的样式又给“强调“了一遍。搞完一想,不对啊,这特么应该是第三方自己没做好对自己 CSS 代码的防御啊,我瞎操什么心呢。不过这也引出了这次的话题,假如是你来写这个第三方应用,你怎么去做这个样式防御?
为此,我特地先研究了几家比较知名的网站是怎么做的。
首先,我们先看一下畅言 http://changyan.kuaizhan.com/static/help/a-api.html,这是一个第三方评论组件,打开 console 仔细看了一下,发现其实它没怎么做样式防御,大部分样式写在 id 选择器里,防止了大部分的误伤。但还是很容易误伤,比如加个 span { color: red; }
评论就都变红色了。不过它在 logo 上倒是加了 !important
来防止隐藏,还有广告也加了一堆 !important
,效果嘛,你懂得。
接着我们来看一下 Disqus https://blog.disqus.com/why-readers-pay-for-news 是怎么做的。没错,iframe,直接赶尽杀绝。不让任何人修改我的样式,你只能用我提供的样式。用 iframe 固然可以完美的做到样式防御,不过开发的复杂度一点也没有减少,动态的计算组件的高度来改变 iframe 的高度就够让人头疼了,毕竟里面评论一条套着一条,一条行数都是不定的。不过简单的固定长宽的倒是很适合用 iframe 来开发,兼容性也好。
还有 Twitter,
你好 Twitter! #我的第一条推文
— Kieran (@1tsKieran) 2014年7月24日
用了 Shadow DOM,也算是时代先锋了。
我们先来看看 Shadow DOM 是什么,https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/%E5%BD%B1%E5%AD%90_DOM
Shadow DOM 修复了 CSS 和 DOM。它在网络平台中引入作用域样式。 无需工具或命名约定,您即可使用原生 JavaScript 捆绑 CSS 和标记、隐藏实现详情以及编写独立的组件。
看描述,感觉非常适合用来编写第三方组件,内心一喜,但是别激动,查一下兼容性先。
果不其然,天妒英才啊,不过,这不妨碍我们了解一下它。
从文档中我们知道 Shadow DOM 必须附加在一个元素上,可以是HTML文件中的一个元素,也可以是脚本中创建的元素;可以是原生的元素,如<div>、<p>
;也可以是自定义元素如 <my-element>
。 如下例所示,使用 Element.attachShadow() 来附加影子DOM:1
2
3
4
5
6
7
8<my-element id="hostElement"></my-element>
<script>
// create shadow DOM on the <p> element above
var shadow = document.querySelector('#hostElement').attachShadow({mode: 'open'});
shadow.innerHTML = '<p>我是 Shadow DOM~</p>';
// 添加CSS,将文字变红
shadow.innerHTML += '<style>p { color: red; }</style>';
</script>
这里的 p 就不会受到外部 CSS 的影响了,由此可见,Shadow DOM 很好的隔绝了外部的世界,天然的实现了样式防御。
对比到这里,大家对样式防御也有了一定的了解了。那么除了上述的三种方案,还有什么方案吗?
我们来想想为什么要做样式防御,不就是怕第三方网站的样式污染了我们组件的样式吗,如果我们能把第三方网站的样式在我们组件这里重置掉不也可以吗。
网上有很多重置 CSS 的代码,虽然写那么多略显繁琐,但有用。那有什么一句代码就能重置的呢?有吗?当然是有的了,不然我也不会写出来了。就是 CSS all 属性了。下面的一句代码,就能把标签的样式恢复到浏览器默认状态了。1
2
3element {
all: initial;
}
不过很不幸,依然不兼容全系列 IE
不过比 Shadow DOM 好多了,如果你的应用不需要兼容 IE 的话,可以一试。
作为一个第三方组件,比如评论组件,光隔绝外部样式是不够的,准确的说,是不够智能,假如外部设置的字体,颜色跟我的组件差别很大,那我的组件不就显得格格不入了吗。所以我们隔绝了外部样式后还得获取外部的核心样式来让我们的组件显得更和谐。具体要获取哪些样式,就仁者见仁智者见智了,根据你的组件来定制,没有一成不变的规则。
]]>What? 你还不知道 JSONP 是什么?赶紧去补补吧,我就不多讲了。
补个百度百科链接先,https://baike.baidu.com/item/jsonp/493658
我们假设有这样一个场景一
我登录了 www.qq.com,QQ 为了给第三方提供服务,可能会有这样的 jsonp 接口,www.qq.com/getUserInfo?callback=action,那我就可以自己构造一个恶意页面,请求这个 jsonp 接口,放在网络上,收集 qq 用户的信息了。要是 jsonp 接口还涉及了一些敏感操作或者信息,就更嗨皮了~比如登陆啊,删除啊等操作。不过国内很多后端开发并不重视此问题,突然想起来老东家的后端开发为了加速开发,写了个通用的函数,只要能 GET 访问的接口都可以 jsonp 访问。Emmm…
对于第一种情况,首先要验证 JSONP 调用的来源(Referer),这种方案利用了 js 资源加载时会发送 Referer 的特性,服务端判断 Referer 是不是白名单即可。
说起来容易,但实际还会因为过滤的正则不严谨导致绕过,比如只验证了是否存在 www.qq.com 关键字,那我可以构造 www.qq.com.domain.com 来进行攻击。又比如很多开发会允许空 Referer,而跨协议调用 js 是不发送 Referer 的,可谓是千里之堤溃于蚁穴~跨协议调用 js 的一个例子:1
<iframe src="javascript:'<script src=http://attack.com/jsonp></script>'"></iframe>
代码里使用 iframe 调用 javascript 伪协议,来发送空 Referer 的 js 请求。
所以空 Referer 还是要禁止滴。
另外就是部署随机 Token 来防御了,每一次请求都带上 token 值,token 值字母加数字长一点最好了,不然有被暴力破解的可能。
接下来是场景二
不严谨的 content-type 导致的 XSS 漏洞
想象一下 jsonp 就是你请求 http://youdomain.com?callback=douniwan, 然后返回 douniwan({ data }),那假如请求1
http://youdomain.com?callback=<script>alert(1)</script>
不就返回1
<script>alert(1)</script>({ data })
了吗,如果没有严格定义好 Content-Type( Content-Type: application/json ),再加上没有过滤 callback 参数,直接当 html 解析了,就是一个赤裸裸的 XSS 了。 Content-Type 设置成 application/javascript 在 IE 等浏览器下一样可以解析成 HTML 导致 XSS 漏洞。所以要严格定义成 pplication/json。不过即使这样,在五花八门的浏览器面前,也会有个别特殊的,比如在 IE6/7 下面,请求的 URL 文件后面加一个 /x.html 就可以解析成 html。
防御这种情况首先要严格定义 Content-Type: application/json,然后严格过滤 callback 后的参数并且限制长度。
有时候我们会在某些框架的内置 jsonp 函数返回内容里看到这样的开头:/**/, 比如 Express 框架内置的 jsonp 函数会这样返回:1
/**/ typeof func === 'function' && func({"data":"hello"});
这是为什么呢?其实这都是有历史原因的,2011 年的时候出过一个通过 mhtml 协议解析跨域的漏洞:MHTML Mime-Formatted Request Vulnerability (CVE-2011-0096),当时也是影响了一堆国际厂商,想详细了解的可以戳这里,https://technet.microsoft.com/library/security/ms11-026
防御这个就是在前面加上 /**/ 或者多个换行符就能一定程度预防其他文件格式的输出了。
接下来是场景三
假如我的网站用了第三方的服务,而对方提供的只有 jsonp 接口,万一对方的服务器被黑了,返回了一串恶意代码,那不是城门失火,殃及池鱼了吗?
请记住一句话,所有的第三方都是不可完全信任的。
防御的话,首先,考虑一下,前端能做什么防御措施?
iframe?好像可以,搜了下,网上已经有人做了尝试了,详细可以看这里 https://github.com/aui/jsonp-sandbox/issues/13
还有什么方法呢?前端好像没什么可以检测返回的 js 内容的函数了,好像没什么可以玩的了。
那换个思路,服务端转发呢,既然前端不能检测第三方的 jsonp 内容,那我用自己的服务端去检测第三方的 jsonp 内容是否合法,再返回结果不就行了。
绕来绕去,总感觉用的不太爽,可能正是由于 jsonp 的种种缺陷,才一直没有被浏览器标准采纳吧。
]]>网址:https://timemail.net
微博:时光邮局-Timemail
如果有好的建议,欢迎在下面反馈~~
]]>插件链接先亮一下,
https://github.com/SuperKieran/hexo-generator-search-zip
走过路过,不要错过,赏个 Star,给个面子
有人可能会问,hexo 的站内搜索插件不是很多吗,干嘛多此一举,又写一个,你这个有什么特别的吗?
嘿!问得好!
待我细细道来,
每一件事情的出现肯定是有原因滴,
每一个插件的出现肯定也是有原因滴。
很简单了,我的需求现在的插件无法满足而已。
我的需求很简单,不依赖第三方服务,比如 algolia,也不需要自己的服务器跑 api,有自己的服务器跑 api 我还要静态博客干啥子?简直高射炮打蚊子。我只想要简单的把所有文章数据存成 json 文件,然后打包成 zip,前端把 zip 下载下来,解压存入 LocalStorage,以便下次复用。
这么简单的需求,可惜大家只是 简单的把所有文章数据存成 json 文件,没有考虑后续的优化。
所以只能我自己动手了,先找了个已有的插件 hexo-generator-search,这个就是只做了第一步的,把它精简了下,增加了打包成 zip 功能,还生成了时间戳,前端可以根据时间戳来判断 zip 包有没有更新,没有更新就可以继续复用 LocalStorage 里的数据而不用下载了,可谓是省时省流量~~
核心代码其实就一点点
在原来生成的 json 对象基础上,加了如下代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24var json = JSON.stringify(res);
fs.stat(this.public_dir, (err, exist) => {
if (err || !exist.isDirectory) {
fs.mkdirSync(this.public_dir)
};
var output = fs.createWriteStream(this.public_dir + searchConfig.zipPath);
var archive = archiver('zip', {
zlib: { level: 9 } // Sets the compression level.
});
archive.on('error', function(err) {
throw err;
});
archive.pipe(output);
archive.append(json, { name: searchConfig.path });
archive.finalize();
})
return {
path: searchConfig.versionPath,
data: +new Date() + ''
};
最后生成的搜索文件可以看我博客的
https://go.kieran.top/search.zip
https://go.kieran.top/searchVersion.txt
至于安装也很简单了,1
$ npm install hexo-generator-search-zip --save
Duang 装好了
然后在博客的 _config.yml 文件里,注意是根目录那个,不是主题里的那个,加个配置就好了1
2
3
4
5search:
path: search.json
zipPath: search.zip
versionPath: searchVersion.txt
field: post
field 可以选 page 和 all
结束了吗?
如果你用的是我写的 TKL(https://github.com/SuperKieran/TKL) 主题,
那么把主题里面的 _config.yml 找下面改一下就可以了1
2
3local_search:
enable: false
...
如果是别的主题的话…Emmmm,那主题的前端搜索实现需要你自己动手了。
简单点的话,就去我的主题里挖代码,https://github.com/SuperKieran/TKL/blob/master/layout/_partial/search.ejs
可能得稍微改改,怎么改,就看你咯~~
1 | search: |
search.zip 下载有些用户会唤起迅雷下载(如果有设置了迅雷的用户),改成 .flv 后缀就不会了。
]]>想看购物攻略的直接拉到最后~~
四人行,我是从杭州出发,另三人从香港出发
机票很便宜,
杭州来回亚庇,1224RMB,香港就更便宜
5-20 起飞,5-26 回杭
马来西亚是免签的,但要办个 eNTRI
办理链接:点我
资料除了姓名护照照片,别的基本可以瞎填,因为没有人审核,只要你交了钱,系统立马自动生成了eNTRI
值得注意的一点是,可以从中国大陆,香港以及澳门直飞,但是不包括台湾,可能台湾有点敏感?
晚上9点半的飞机,早早值了机,
但是外面阵雨,晚点
晚了将近一小时,
无聊的我捧着手机斗地主
凌晨两点多下飞机,另三只无情的抛下了我,
好吧,这么晚了,原谅你们了
幸好提前租了车
操着一口蹩脚的英语跟的哥沟通我在哪的问题,
的哥接上我后,就快马加鞭赶往我们订在 Imago 的大公寓
的哥的车速,让我有种过山车的感觉
到了大 House,火火在等我,另两只也没睡,啦啦啦
愉快
一觉醒来已然是中午,
某晃拿着我的微单拍了一圈大 House,Airbnb 上订的, Airbnb 优惠请看最下方
我们走路去吃了肉骨茶,
火火同学十分热衷于写大众点评,所以想知道这家店怎样的,
可以直接戳下面的链接
沙巴肉骨茶,http://www.dianping.com/review/421404445
吃完打车去了沙巴大学,门口凹了好一会造型
如下图的火大佬
想进去的时候门口有个很像保安的大叔笑眯眯的走过来,no, you can not come in
嗯?难道今天不让进?于是跟大叔尬聊,聊啊聊,突然说,50马币,我可以带你们进去~
贫穷限制了我们的冲动,于是我们告别了大叔,
但仔细一想,好像是个骗子?于是回头一看,这货又在忽悠后面的游客…
Fine,byebye.
于是我们进去了。才走了一会会,同行的小伙伴们就气喘吁吁,而我还是平心静气。但是没办法,Grab打了个司机带我们逛沙巴大学,先去了里面的寺庙,看网上拍出来很好看的那个,去了发现网上的背景墙是,厕所???没错,就是厕所。于是我们在四周又凹了一会造型。
然后司机送我们去了最后一个目的地,榴莲街。同行四人,只有我一个不吃榴莲,所以我的意见并不够成主导。吃之前在那里用人民币换了点马币,那里的汇率还是挺不错的。
另外三个吃着榴莲,我吃着菠萝和西瓜,菠萝味道很不错,跟国内的不太一样,价格也便宜,西瓜不咋滴,很淡,不甜。
然后走回去,好像路过了菲律宾市场什么的,具体走过了哪些地方不是很重要了。
反正我也没记得。
晚上回来去白天路过的集装箱烧烤集市,打算搓一顿,过去后发现零零散散只开了两三家店铺,还没烧烤,一问才知道今天是周一,休息日,大家都不上班!于是只好随便点了点牛肉套餐加臭豆腐打发一下。
然后回去逛Imago,巴拉巴拉,一天过去了。
第二天又开心的睡到中午……嗯,没错,就是这样。
然后下午打车去了码头,买票坐船去了沙比岛。还买了个海上降落伞,嗯,看英文以为是降落伞,我以为是飞在半空中的那种,结果是海上拖拽伞!呵,年轻。卖票的人一开始说140马币两个人加来回船票每人30马币,我们四个人,然后跟他砍价,从400马币砍到了280马币。
出发坐船上岛,耶比✌️
哦不对,上岛前坐了拖拽伞,成功把胸部以下的身体在海水里浸泡了一遍。
原谅我把自己惊恐的小表情遮住了…Σ( ° △ °|||)︴ 瑟瑟发抖
不过这里的海滩比较脏,甚至比不上昨天沙巴大学的海滩,海滩边时不时飘着几句尸体,没错,这是在浮潜的人???请问你们在看什么?一眼见底的海滩???你们认真的样子真的很帅了,我给你们特写照一张。
离岛的时候发生了一点事,离岛竟然还要收20马币一个人的离岛费??!wtf,可以说非常坑人了。
到了岸上没多久,哗啦啦的倾盆大雨说下就下,下面两图拍摄时间不超过10分钟
滴滴滴,打车回家,又去逛了逛 Imago,去超市采购了一波,住的公寓里有烤面包机,早饭靠它解决了,真的很香
回公寓之后对白天的海水念念不忘,于是我们去了顶楼的无边界泳池,扑腾了会,
可惜没过多久开始下雨了,安保过来赶我们上岸
去码头的路上
第三天早上一大早我们就跟团出发去美人鱼岛了,美人鱼岛非常远,先坐车一个半小时左右到达码头,
坐船出海
然后又坐快艇坐了45分钟左右到达美人鱼岛,
然后到达了所谓的TW四星级度假村,出发前还发微信提醒,沙巴的空调都很厉害哦,所以大家要带外套哦~天真的我们信了
我……请问,空调在哪里???这不是露天的小木棚吗???哪里有空调了???
上图的四星级度假村了解一下~~
Fine,既来之则安之。然后寄存下行李,没有贵重物品也可以不寄存,10马币一次。
接着又坐船去浮潜,呼啦啦来到第一个浮潜点,那天浪有点大,下水后我就被冲走了,虽然有泳衣,一直在海面漂着,但是以肉眼可见的速度离船越来越远,尽管手上划着,脚下蹬着,还是越划越远,当时感觉要迷失在茫茫大海里了。整个人葛优躺在海面上,脑海中不断浮现着鲁冰逊漂流记的画面…
浮潜时的火大佬与海底的鱼
第二次浮潜,这里的海面清澈,离底下的珊瑚也更近,同行的一米九小伙子狗蛋还踩到了珊瑚。
随后返航,回到美人鱼岛上,海水是真的清澈,四星级度假村是真的一星都没有。自助饭菜也是难吃,米饭鸡肉米粉一两只虾,米粉能吃,虾还行,饭和鸡肉很难吃,哦,对了还有饮料,喝了一口就弃了。
饭后休闲尬拍一波,都是佳能M6直出无滤镜,点开可以看高清无损大图
拍完躺秋千上继续让火大佬给我拍
到了晚上,很“开心“的是,晚餐竟然跟午餐一样???下午茶是个很难吃的果冻,难是困难的意思,没给勺子全靠舌技。还有一些米粉和我不记得反正味道不咋滴的食物。吃完坐船看长鼻猴什么的,猴子没几只,回来的时候倒是发现岸边码头的屋顶上爬着几只,顺便吐槽一句,商店的长鼻猴玩偶价格都巨贵,虽然我感觉都是 made in China。
然后去了海边看落日,这里算是为数不多的亮点了。海边非常漂亮,落日也非常唯美,在这里凹了一波造型,也出了一张非常完美的落日剪影照。
请看图~~完美的剪影,完美的太阳光晕,这是我给另一对cp拍的,请叫我凯·摄影师,谢谢
还有泡泡。
接着在周杰伦想带你去看浪漫的土耳其歌声中吃晚饭,呵,晚饭。
吃完坐船看萤火虫,嗯……岸边树上一点点,这里是kawa红树林,我感觉还是单独报威士顿红树林的比较好,至少kawa这边的萤火虫不是很多,猴子也很少。
然后回程,累瘫,没想到回去四人打牌打到了凌晨一点多。(⊙﹏⊙) 瑟瑟发抖
第四天,睡到中午。科科科,换酒店。
中午吃了大茄来,极力推荐,详情请移步火大佬的点评,http://www.dianping.com/review/421071140
下午去了曙光购物中心,巴拉巴拉。晚上本来想去吃什么茶室的,一家网红店,结果营业时间到18:00,我们到的时候已经打烊了,只好去了隔壁某家,不怎么推荐,就不说了
放个曙光购物中心对面的照片证明一下我来过
总结就是买买买的一天
神山一日游套餐,189人民币一个人,这个比较休闲,就是路上比较远,两个小时的车程,而且我竟然有一点晕车,到达第一个购物集市的时候,发现我们没现金了,跟导游借了点。又是一条好汉。
然后到达温泉,然后上山,
神山游客照了解一下
走山顶吊桥,拍照收费10马币一组,我们为了省点马币,没交钱也没打算拍照,当时就剩跟导游借的钱,后来发现似乎也没人管,就呼啦呼啦拍了一堆。下来后就开始在温泉泡脚,其实也就爬了不到一个小时,很轻松。泡完脚在一家很漂亮的餐厅吃中饭,
就是上图背后的那个餐厅,
这里真的给这个团点赞,午餐真的非常满意了,景美菜好吃!在二楼平台处,往外看就是一片山林和水流,心旷神怡!否极泰来!
吃饭时间顺便回顾一下今日份火大佬,
和吃冰群众我
然后回程去牧场,路上看到很多牛和奶牛,接着到了一个奇怪的地方,吃酸奶冰激凌,顺便在这儿凹了会造型。然后回去让导游把我们在Imago放下,又是一晚上的逛吃逛吃。
最后一天大概就是睡到中午,打个车到机场
在机场,吃着KFC,望着窗外,
我随口说道:“外面的景色好漂亮!”
火大佬马上接上:“春风十里不如你~”
Emmm….被女孩子撩了是什么回事
顺便说一句,马来西亚的KFC挺好吃的
走的时候在飞机上看到了彩虹🌈
看见彩虹的人运气总不会差
最后放一张图结尾吧 🌝🌝🌝
佳能 M6
GoPro 6
小米手机 6
简称 666 组合 🌝🌝🌝
GoPro 6 拍了60GB左右的素材,有空剪,有空,嗯
买化妆品之类的推荐去 Imago 或者 曙光的 屈臣氏,比Sasa便宜点
欧莱雅比国内便宜好多,日霜夜霜之类的比国内便宜一半
欧莱雅口红也便宜
防晒也便宜好多
香奈儿就便宜一点点
各种运动品牌也便宜
VINCCI的鞋子也很便宜
优衣库竟然比国内贵
不要问我一个男的怎么知道这些的,我就是知道…🙉🙉🙉
几天的住宿都是在 Airbnb 上订的,四人的话订个大公寓或者大别墅比较划算~~
大多数都是带泳池的
通过下面链接注册还能获得满 500 减 175 的优惠哦~~
https://www.airbnbchina.cn/c/klt10
1 | 南北湖,浙江省第一批省级风景名胜区、国家AAAA级旅游景区、浙江十大“最佳休闲度假胜地”之一。南北湖,山有层次,水有曲折,海有奇景,村有故事,是我国唯一融山、海、湖、村为一体的全景度假地,依托山海湖·村特色资源,提供多种主题度假方式,感受完美全景度假生活。 |
首先下个结论,80元的门票钱值不值?
不值
虽说是个国家 4A 级景区,平日里却人迹罕至,节假日才稍有点人气,可能国庆期间才适合来这里游玩。下这个结论也可能因为我是本地居民,来过这里几次。
我是怎么进来的呢?
请看下图
没错,就是摸黑进来的
南北湖在白天 8 点前和晚上 5 点后是免费进入的,也就是
奕仙城内十分的幽静(萧条),几乎没有游人,因为我们不是节假日去的,又是小雨。路上仅有一家餐厅招呼我们要不要进来吃点本地菜,不过在网上看到别人被这里的餐厅坑了,菜没有明码标价,全凭老板娘报价。不过我也没有去吃过,大家自行评估。
奕仙城离湖大概一公里左右,走走就到了,另外还有个金牛山庄园,在奕仙城下面 800 米左右,其实就是个私家酒店,旁边有个金牛山洞,也是在景区里面,离门口更近,离湖更远。
奕仙城还有别的有特色的民宿,比如隐库,湖光山舍等等。隐库的装修比较清新,当然价格也比较高,680至980,住宿可以包门票。
下面是奕仙城的景象:
湖山真意走过去是个回环,走过去又回到奕仙城,那边可以眺望南北湖,下面是我在湖山真意处拍的
真意没领悟,野鸡看到几只,也可能是家养鸡,一路上小狗一只跟着。
艺术馆和藏书阁因为没人的缘故,都关着,所以如果想看景点的还是在节假日来比较好,只是闲逛的就随意了。
奕仙城中还有不少屋子在装修,目前还是个开发未完全的景点。
如果周末想找个清静的地方玩玩,或者登山徒步爱好者,可以来南北湖玩玩。不建议体力不好者来,因为南北湖里面景点和景点之间很分散,走的还挺多的。不过现在有景区公交,方便了一些。
最后讲一下公交路线,自驾的忽略:
嘉兴可以直接坐K176到南北湖,一个半小时左右,全程13元;
杭州可以先在杭州客运中心坐快速公交到海盐客运中心,39元,大概一小时十分钟左右,然后海盐客运中心坐K203或者K213到达南北湖,2元。杭州客运中心直达南北湖的公交99元,时间肯定快点,选哪个自行决定;
上海的可以先到嘉兴。
1月6日流水账
]]>手机电池又不行了,淘宝入了块电池自己换
换之前自然先备份下 iPhone
但 256GB 的 Mac 内存吃紧,想着备份到移动硬盘上
结果找了半天没有在 iTunes 里发现更改备份文件夹的设置
无奈搜索一番,在此记录一下
1 | 一 |
备份完你会在原来的文件夹下看到一份备份
不要惊讶,可以看做一个“快捷方式”,并不占用 Mac 空间
当推出移动硬盘时,这个“快捷方式”就会消失
当前互联网大部分的内容都使用 HTTP1.1 作为通信协议。大家在这项协议上投入了非常多的“精力”,比如从 HTTP1.0 规范的 60 页暴涨到 HTTP1.1 规范的 176 页。相信大部分人都没有兴趣完整的阅读完这份规范,这样导致的结果就是 HTTP1.1 中的很多细节都没有被很好的实现,即使一开始实现的功能也很少被使用。而随着时间推移…使用者才发现,原来这货有这么多毛病。
但在挑毛病之前,我们先来看看它相对于 HTTP1.0 有哪些改进之处:
- 缓存处理,HTTP1.0中使用 If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
- 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
- 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
- Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
- 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。以下是常见的HTTP1.0:
好了,虽然 HTTP1.1 改进了很多问题,但吃瓜群众是永远不会满足的。于是他们抛出了更多问题:
- HTTP1.1 在传输数据时,每次都需要重新建立连接,无疑增加了大量的延迟时间,特别是在移动端更为突出。
- HTTP1.1 在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性。
- HTTP1.1 在使用时,header里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求header基本不怎么变化,尤其在移动端增加用户流量。
- 虽然 HTTP1.1 支持了 keep-alive,来弥补多次创建连接产生的延迟,但是keep-alive使用多了同样会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务(例如图片存放网站),keep-alive可能会极大的影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间。
终于“讲完”了 HTTP1.1,该轮到本篇博客的主角登场了——HTTP2
HTTP2 其实是起源于一个叫 SPDY 的协议,这是由 Google 牵头开发的。HTTP2 就是在这个协议的基础上修修改改发布的。不过 Chrome 已经移除了 SPDY,就不说这货了。
HTTP2 必须维持原有的 HTTP1.1 的形式,不该改变URL结构,也不能新增结构。比较老的协议还有很多人用。
这就出现了第一个问题,浏览器怎么知道是 HTTP2 是 HTTP1.1 ?
于是规范和产商就开始撕逼了,规范表示,提供一个首部字段,表示允许服务器在收到旧协议请求的同时,可以向客户端发送新协议的内响应,但这一方案需要花费一次额外的往返通信来作为升级代价。
Google 团队很不爽,觉得这方案太次了,于是就自己搞了个 TLS 的拓展,功能是服务器可以通知客户端所有它支持的协议,让客户端自己选。
于是 Chrome 实现的 HTTP2 只能使用 TLS,也就是 HTTPS。但规范是普通的 HTTP 也能使用。这时候各家浏览器大佬纷纷跳出了站队了。Firefox 大佬表示,我跟 Chrome。这一点其实有点出乎意料,Firefox 不是曾经号称最遵循规范的浏览器嘛,不过也可能因为 Firefox 是开源的,成员为了节省精力吧。然后这时候微软也跳了出来,老子就跟你们不一样,老子就要支持普通 HTTP 的 2.0!!!
HTTP2 能有那么多新特性,最主要的功臣就是改变了数据传输的格式,变成了二进制,二进制格式在协议的解析和优化扩展上带来更多的优势和可能。 Frame 是 HTTP/2 二进制格式的基础,基本可以把它理解为它 TCP 里面的数据包一样。HTTP/2 之所以能够有如此多的新特性,正是因为底层数据格式的改变。
网页加载速度慢的罪魁祸首可以说是延迟了,所有的优化都是为了降低延迟,在 HTTP1.1 中虽然一个 TCP 连接可以发送多个请求,但还是有上限的,从一开始的两个,到五个…于是就萌生了各种减少请求数的方法,包括雪碧图,内联小图片,合并 CSS、JS,分片(Sharding)就是把你的服务分散在尽可能多的主机上。
但这些方法都有一定的副作用,并不能百分百解决问题。
于是 HTTP2 实现了让所有请求跑在一条 TCP 连接上,而且没有请求数量上限。
每个流都有一个优先级值,用来告诉对端来说哪个流更重要。当资源有限时,服务器会根据优先级来选择优先发送哪个流。客户端通用可以告知服务端哪个流优先级高,这个流依赖了哪些流,在传输的过程中,这个优先级也可以被动态的改变。
HTTPS 和 SPDY 的压缩机制都被发现有受 BREACH 和 CRIME 攻击的隐患。通过向流中注入一些已知文本来观察输出的变化,攻击者可以从加密的载荷中推导出原始发送的数据。
HPACK 是专为 HTTP2 设计的压缩格式。引入了一些新的对策来让破解压缩更加困难。如采用帧的可选填充和用一个 bit 作为标记。
这个简单举个例子就是服务器在发送 html 文件的同时把这个 html 要用到的 css 和 js 文件也推送给了浏览器,这样不需要等到浏览器解析了 html 文件才发出 css 和 js 的资源请求。
HTTP1.1 在发出含有确切的Content-Length 的 HTTP 消息之后,你就很难中断它了,除非断掉 TCP 连接,但这意味着你得重新三次握手建立一个新连接。
在 HTTP2 里可以发送一个 RST_STREAM 帧来终止当前传输的消息而不断开 TCP 连接。
了解更多:
]]>当时我的反应是——我可能打开的是假 Edge
根据之前的表现(就是点完页面刷新了一下),我一开始假设 Edge 下 a 标签 href 里写 javascript:; 不起作用,还是会跳转,于是绑定的事件也没用了。但是一看代码,好像不对。来,看代码:1
2
3
4
5$('.J_exit').on('click', function () {
$.get('/ajax/exit.do, function () {
location.reload();
});
});
点击退出按钮的时候请求退出接口,后端会删除对应的 session,然后前端刷新下也没,cookie 没有对应的 session 于是会跳到登录页,从逻辑上来说,没毛病。
所以排除我刚刚的假设,那会不会是缓存问题呢?刚想到这里……
就看到两个人气势汹汹的往我这来,是后端GG和测试MM,两人过来把我团团围住,“Hey,boy,Edge 的 Bug 改的怎么样了?”
我当时的表情是这样的, 这好像才过去三分钟,你两怎么就过来了??? 爸爸才刚打开虚拟机!
咦,不对,重点是,为啥你两亲自跑过来了? 宝宝很惶恐啊,有事私戳我就行了,没必要兴师动众的。由于我还没验证出 bug 原因,都是猜测,于是保守的说还母鸡呀。然后说了一堆有的没的,就走了,走了,了 哦,后台GG走之前意味深长的跟我说了句,可能是浏览器缓存问题。嘴角带着邪魅的笑,大概是这种 。
爸爸刚想测。于是爸爸在请求后面加了时间戳参数,第二次请求成功的发出去了。但是还是得向后台GG表示“感谢”
事后搜了下,发现 IE 浏览器对于没有明确表明是否缓存(强制缓存或者强制不缓存),默认的设置是“自动 automatically”,即在不关闭浏览器的情况下(同一个会话),刷新页面,对于相同url的请求并不会再次发送(即请求被缓存了),而 Edge 延续了 IE 这种“优良”的传统,坚决和主流浏览器保持“差异性”,所以会出现上面第二次请求退出接口的时候,请求都没有发出,直接就执行 location.reload() 了。
最后只想 po 一张图,自己体会
“每个人都有自己对战争的理解,对电影也一样。”
上面这句话来自李安《比利·林恩的中场战事》的结尾,比利对着镜头,像是对这部电影前面的总结,也像是李安的喃喃自语。有一种高处不胜寒的孤寂感。
影片一开始,就是一个超大光圈的镜头,全片多处用了大光圈来虚化背景,这在电影里很不常见,会降低电影的感觉,但在这部倾向于个人叙事视角的电影里,多了几分真实。纵观整部影片,你会发现全片的光线都是很亮的那种,但是又不会显得突兀,包括舞台上,房屋内,所有的光线都看起来那么自然。玩过摄像机和单反的人都知道,同样的环境下,摄像机的画面是偏暗的,单反的进光量比摄像机多很多,如果是日常个人拍摄,在没有打光人员的情况下,往往用单反的拍摄效果要好于摄像机。影片中要营造出那种很明亮又很自然的光线,不得不说,这片的打光绝对是顶级的。
舞台的灯光其实很难控制,我在舞台下拍摄过舞台的节目,从下往上录制演员的脸相对比较容易,因为聚光灯打在演员的脸上,非常的清晰,只要稍加调控就好,但是影片中有大量比利从舞台上往下看观众的镜头,我有幸上过一次舞台,当前面的聚光灯打在我的脸上的时候,事实上我是完全看不到观众的,前面的斜上方几个大聚光灯,像太阳一样耀眼,下面却是漆黑一片,只能听见掌声和心跳,那种感觉很奇妙,上过舞台的人可能都会上瘾。反正我上瘾了。
说回比利,这里其实有几个镜头不是很好,比如在台上时,有一个镜头是围绕着比利转了一又四分之三圈,这给我的感觉很突兀,为什么不完整的转两圈呢?转到四分之三时突然切掉,而且旋转的镜头有轻微的抖动,在 60 帧(我看的 60 帧版本)下显得格外清晰,这镜头给我的感觉就是,为了秀一把 120 帧下镜头快速移动的优越感。影片中其实有多处移动拍摄的场景,大多是以比利为第一视角,李安真的很会选择。用移动的镜头以第一人称视角拍比利,会给人更深的沉浸感,而且又恰到好处的展示了移动场景在 120 帧下的真实效果。
技术在其次,电影表达的想法才是真正值得被揣摩的。
有几个很有意思的场景
一是舞台放烟花的时候,比利和他同伴看到的却是战场上的各式导弹,流弹。这是创伤后应激障碍的一种,看到这里其实感觉很可笑,有点感同身受,当然我不是上战场,而是所学专业遗留下来的“习惯”,这里不便多说,有兴趣的话我们可以私下聊,如果你认识我的话。
二是比利和拉拉队美女的告别,比利的内心其实想留下,但是拉拉队美女很诧异他有这样的想法,于是比利假装开玩笑。看到这里其实很清楚了,拉拉队美女并不爱他,只是因为他是英雄,和他谈恋爱她回去可以和别人吹牛比。一旦他不是英雄,变回普通人,便会失去她的“爱”。而他的姐姐从一开始就让比利留下,因为她知道战场很危险,回去可能再也回不来了,那些荣耀能比比利活着更重要?但是结尾的时候比利选择回战场的时候,姐姐也只能尊重比利的选择,但这是亲情的爱。我认为,真正的爱情,从来不是电光火石之间就会产生的,所谓的一见钟情只是初始的好感度起点比较高罢了,要达到爱情的程度还需一些经历。
三是 “这感觉其实挺奇怪的,有人来表扬你这辈子最惨的一天。“ 看台词就足够悲伤了。
四是令我最难受的一个场景,最后比利和他同伴走在过道里的时候遭遇了之前在片场有冲突的两人的埋伏,这两人叫了一帮混混,按正常英雄电影的套路,绝对是应该比利冲上去干他们一顿,事实却是被按在地上。然而这确实符合事实,那大汉这么强壮,即使你是大兵,在徒手状态下被按住,几乎不可能挣脱,很无力,很难过。李安就是这样,赤裸裸的把现实拍给你看。
前面的关于电影都是 shit, 有时候觉得自己真是一台电脑,看电影的时候无时无刻不在分析打光,镜头,演员微表情,etc…什么时候养成这些习惯的呢?大概是大一,从加入**影像工作室开始。那时觉得自己未来会当一名编剧,导演之类,拍一部票房口碑爆棚的电影,拍出来后手捧奥斯卡金像奖,走上人生巅峰。好吧,其实没想那么多,当时加入这个社团只是觉得很酷。社团招新时各种显示器,摄像机摆在外面,十米摇杆吓得新人都不敢靠近,团长一脸冷酷的表情。Fine, 加入后才知道,显示器和摄像机都是临时借来的,整个社团在我加入前只有8个人,刚成立不到一年,感觉入了一个坑。
刚开始的时候做的是编剧,模仿着当时很火的万万没想到写了一些逗逼的剧本,也有比较正经的剧本,但是后来都没有拍了,原因有很多,暂此不表。
后来去拍摄组,一开始拍学校晚会,刚开始很不解学长为何总是带着大耳机拍摄,后来才发现,带着耳机好像比较酷。开玩笑,其实是为了隔离外部环境,一是拍摄时周围都是人的嘈杂声,很难专注,二是容易拍着拍着和周围的熟人聊天…原谅我的不称职。总是拍晚会未免有些无聊,于是社长大手一挥,决定再也不接学校的晚会,要拍一些自己原创的东西。
最后一个接的是拍外国留学生秋游,嗯,没错,我被派去跟拍了。免费秋游。九溪,山清水秀,茶水之乡,走在山间的小路上,看着田里的人采着茶叶,的确很诗意。但前提是没有带笨重的摄像机!!!我们两人各扛一台摄像机一路爬山涉水的跟拍这群不老实的老外。至于三公斤还是六公斤重的脚架,还是继续躺车里吧。有一个黑人特别活跃,一路唱着rap,hey,快拍我,我最帅,yoyoyo,切克闹! Fine, 你的镜头够多了,拜托,我们还要拍别的!其中有一个老外的中文说的特别溜,我们找他单独去小竹林拍了一段。后来发生一件有趣的事,中饭时间,一个老外不吃,I asked, 为什么你不吃?He said, he didn’t like Chinese food. I said, maybe you can try it today, it’s delicious. He said, 他试过几次,但都不好吃。说完他走了出去,我的内心是Orz,Fine,国际友人。我跟着他出去了,走着走着,他突然说,I like Chinese girl.They are pretty! 我突然不知道怎么接话,我想起之前看到的一篇文章Chinese girl is easy. 其实大部分在国外混得很差的老外甚至在那边都混不下去的来中国好像都能混的很好,随便说几句中文,似乎都能把他们捧上天了,做一些出格的事,或者脚踏几只船,似乎大家都觉得这是文化差异。什么时候我们对外国人的容忍度这么高了?交友的准则还是得一视同仁。
最近和一个俄罗斯人聊川普和希拉里,她特别支持川普,因为川普亲俄,希拉里可能会和俄罗斯爆发冲突。我说我看了新闻,川普和奥巴马交接期间,美国种族歧视的问题越发严重,白人打黑人小孩以至于黑人小孩都不敢来上学。其实这是常态,每一届总统上任期间都会发生这种事,都是不支持这届总统的人们搞出来的。本来也就这样,但是跟我聊天的俄罗斯人说 She do not feel sorry of them,white people hit by other black people before…还有一些话浓浓的种族歧视味。可能在中国感受不到,以为种族歧视好像世界上凤毛麟角了,但大部分白人优人一等的自我感觉一直存在。按她的逻辑,作为黄种人的我似乎也是劣等人种?跟她争论了半天,最后我实在懒得吵下去,for me,it’s not important. I don’t care 川普 and 希拉里, 但她竟然冒出来句:I like argue with people. 嗯聊了两个月的朋友感觉要闹掰了,摊手。
抱歉,跑题了。不过似乎也没有主题。
后来学了点剪辑,对着一秒 24 帧,三分钟的 MV 就是 4320 帧,但实际素材却有几个小时的活默默发呆。剪视频不仅要有创意,更重要的是耐心和细致,偶尔看到做视频剪辑的学长们时常为了剪片子而出去开房熬夜(学校23点熄灯断电),看清楚这条路不适合我后,我选择了逃离。很狼狈。
期间当了几次演员,其实当演员还是比较开心的,毕竟没心没肺,说什么台词做什么事,都是戏。不用考虑后期剪辑,不用考虑剧本和经费。
第一次当演员比较惨,演了一个被毒死的 people,就是中毒后走着走着支撑不住倒地那种,拍了好几条,摔得我遍体鳞伤。但,镜头还算多。磕磕绊绊,演了一些作品,后来,演了一部比较有意思的微电影,拿了个省一等奖。算是演艺生涯比较完美的告别。
朋友圈依然会看到那些为了梦想而努力的朋友,有在新浪跟拍记者的,有在二更施展才华的,也有还在熬夜做剪辑偶尔发个葛优瘫的。
人生其实就是一本剧本,每个人都是一名编剧,写什么剧情随你,唯一不同的是,写完没法后悔了。
平平淡淡的剧情容易掌控,跌宕起伏的剧情充满不确定性。如果是你,会写哪种剧本呢?
1 | <div class="cd"></div> |
为什么要写这个呢?
答案是 玩
没错 就是玩…
可能脑抽吧
其实我发现 CSS 动画真的很好玩!
可以实现一些炫酷的效果
接下来我会继续写几个好玩的 Demo
补充下我并不充实的 CSS3 动画知识…
玩并快乐的学习~
代码请戳下面
http://codepen.io/_kieran/pen/QNRmep
原理很简单
CD 只要用到了 box-shadow 来实现光盘面 文字分别用 before after 伪类
两个音符也是一个 div 上的伪类
CD 加上无限旋转的动画
音符加上 translate rotate 实现慢慢摇摆上升
再用透明度实现渐隐渐现就 OK 了
1 | var mongoose = require('mongoose'); |
1 | var _blog = new Blog({ |
1 | findById() // 略 |
1 | var query = {}; |
1 | var promise = Blog.remove({ _id: id }).exec(); // 略了 |
1 | var promise = Blog.count({ author: 'Kieran' }).exec(); |
未完待续也可能不续
]]>Webhook 允许第三方应用监听 Coding.net 上的特定事件,在这些事件发生时通过 HTTP POST 方式通知( 超时5秒) 到第三方应用指定的 Web URL。 例如项目有新的内容 Push,或是 Merge Request 有更新等。 WebHook 可方便用户实现自动部署,自动测试,自动打包,监控项目变化等。
Webhook 的每个 POST 请求都有包含特殊的 Header, 默认超时时间为 2s1
2Header 说明
X-Coding-Event 事件名(例如: push, Merge Request, Task)
大致原理就是 push 代码到 Coding 后,Coding 向我的服务器 post ,然后我的服务器接收到后就会执行响应的操作。每个 POST 请求都有包含特殊的 Header 里面有事件名,可以依据这个来判断是什么事件,另外为了防止伪造, post 里还包含了 Token
好了,接下来进行实际演示吧。
既然是私有项目,要想 git pull 下来,肯定要配置 ssh key 了
但是服务器上的 ssh 又不想让它有完全的权限,毕竟你总不可能在服务器上改代码然后 push 到 Coding 上吧。
好在 Coding 提供了项目部署公钥设置,对项目只拥有读权限,这样相对来说比较安全
看上图,新建一个部署公钥,填入你服务器上的 ssh-rsa 公钥后就是上面的样子了
在你的项目里加入 webhook 的操作,比如 Nodejs 的 Express 项目是加入类似下面代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const exec = require('child_process').exec;
router.post('/webhook', (req, res) => {
if ('hooks' === req.body['token']) {
exec('git pull', {'cwd': '/home/kieran/projects/app'},
(error, stdout, stderr) => {
console.log('stdout========================\n' + stdout);
console.log('stderr========================\n' + stderr);
if (error !== null) {
res.send('<pre>fail!!!\n' + stdout + error + '</pre>');
} else {
res.send('<pre>done!!!\n' + stdout + '</pre>');
}
});
} else {
console.log(' failed token ');
res.send('<pre>token不正确?</pre>');
}
});
这里有一点要注意一下,child_process 模块的 exec 命令执行时的第二个参数中,cwd 表示命令执行的目录,一开始我写的是’~/projects/app’, 然后死活没反应,折腾了 N 久,感觉不会再爱了。谨记,这里的 cwd 只能用绝对路径,不能用家路径的简写。
好了,服务器操作写完了,只要再在 Coding 上填一下服务器地址和 Token 就行了
地址是 服务器 Webhook 地址 比如我是 http://12x.xxx.xxx.xxx/webhook, Token 随便自己填一个,别被别人猜出来就行,填完点一下测试,会向你的服务器 post 一下,这时你的服务器就行自动 git pull 了!
1 | "Node.js + Express.js very slow serving static files" |
回答是1
2
3
4
5I had exactly the same issue. I just moved to a place with a pretty bad internet connection. Loading time of static files in my node.js app increased to more then 40s per file.
I just moved the static middleware
app.use(express.static(__dirname + '/public'));
to the top of app.configure function, before all the other app.* calls.
It's now working significantly faster.
没错,就是把app.use(express.static(__dirname + ‘/public’));
这句代码放到app.configure的最上面!!!
仔细思考下就知道了,客户端请求进入Express后,是按中间件的摆放顺序从上往下执行的,一开始我是放在最底下的,真的!最底下!我也不知道为什么我要放在最底下,反正我就是放在最底下了,也没人告诉我放最底下不好….TAT
放在最底下也就意味着静态资源请求进入Express后,先要经过bodyParser,session,cookie,passport等一系列中间件才会返回…饶了一大圈,静态资源完全没有必要经过这些中间件,移到最上面后果然
速度也没变多少……
看来问题不在这里
还是上面的StackOverFlow, 答题者和问题者在下面互回了好几条,挺搞笑的
下面又说了一种方案
在中间件开始处添加1
2
3
4app.use(function(req, res, next) {
req.connection.setNoDelay(true);
next();
});
这是什么呢?我谷歌了下,大概了解了下,服务器为了提高性能,会合并小的请求,等数据包达到一定大小或者超过40ms才会返回给客户端,如果设置了上面的代码就不会合并小的请求但是也不会等40ms才返回了
很显然这也不是我想要的答案。
说实话,很奇怪平时也是这样写的,放在服务器上反应都很快,这次却很慢,写到这里我感觉问题不是出在服务器上,而是前端的代码上。
再从服务器端追查也是没有结果了,还是早点睡吧,明天还有早课。改天再看看。
环境搭建在网上有很多教程,不过还是推荐去看官方文档 https://facebook.github.io/react-native/docs/android-setup.html#content , 当然,照着文档来还是会有很多问题,就我搭建的过程而言,简直就是一步三个坑,这里官方文档上有的我就一笔带过了,详细讲讲我遇到的一些坑,相信这些大家更有用一些。
之前安装过Android Studio,自然也安装过Android SDK,环境变量也设置了,并且我也确定一切都没有错。可是TM执行 react-native run-android 的时候就是报错,就是找不到SDK!于是找到这哥们的文章,腾讯的,链接如下:http://www.alloyteam.com/2015/10/react-native-android-steps-on-tour,
说是找不到android-sdk环境变量的时候,可以单独在项目根目录下,新建一个local.propertites文件,添加sdk.dir=你的android的sdk目录,然后再运行react-native run-android,报错依旧。你妹妹的!Google了半天,无果,这是连个SDK都设置不好的节奏?抓耳挠腮半天,突然觉得把local.propertites文件移进android目录里有戏,结果真的有戏!终于找到SDK了!谢天谢地!之前查stackoverflow都是说把这个文件放进root项目根目录里,最后其实是放进项目的android目录,这坑可真不浅啊~~索性咱成功填了哈。
这其实不算坑,是我自己把自己坑了,看官方文档的时候没看清楚,下了个Build Tools revision 23.0.2。没事,打开SDK manager,下一个23.0.1版本的Build Tools就好。SDK manager的设置里可以设置代理,你懂的。
继续执行一次 react-native run-android , 哦 又报错了
无法下载xxx….看这域名就知道是被墙了,挂个vpn,起~
谢天谢地!终于看到成功的字样了!
让我们把镜头转到模拟器上,起了?
起了?嗯?好像有点不对?红红的一大片怎么也不像成功的样子….好像漏了什么….
对了,就是 react-native start !
好了!总算圆满了~~
不对!还差一点~
在程序员的世界,学习一门新语言的潜规则是第一句代码向这门语言问好,这是一个初学者的礼貌,也是对前人的尊敬。
接下来,就是新的世界了
JavaScript, at its base, is a simple language that we continue to evolve with intelligent, flexible patterns. We’ve used those patterns in JavaScript frameworks which fuel our web applications today. Lost in JavaScript framework usage, which many new developers are thrust right into, are some of the very useful JavaScript techniques that make basic tasks possible. Here are seven of those basics:
JavaScript 本身可以算是一门简单的语言,但我们也不断用智慧和灵活的模式来改进它。昨天我们将这些模式应用到了 JavaScript 框架中,今天这些框架又驱动了我们的 Web 应用程序。很多新手开发者被各种强大的 JavaScript 框架吸引进来,但他们却忽略了框架身后浩如星海的 JavaScript 实用技巧。本文将为你呈献其中七个基础知识点:
One surprise to many JavaScript newbies is that String’s replace method doesn’t replace all occurrences of the needle – just the first occurrence. Of course seasoned JavaScript vets know that a regular expression and the global flag (/g) need to be used:
令很多 JavaScript 初学者意外的是,字符串的 replace 方法并不会 替换所有匹配的子串——而仅仅替换第一次匹配。当然 JavaScript 老手们都知道这里可以使用正则表达式,并且需要加上一个全局标志位(/g):1
2
3
4
5
6
7
8
9// Mistake
// 踩到坑了
var str = "David is an Arsenal fan, which means David is great";
str.replace("David", "Darren"); // "Darren is an Arsenal fan, which means David is great"
// Desired
// 符合预期
str.replace(/David/g, "Darren"); // "Darren is an Arsenal fan, which means Darren is great"
Another basic logical mistake is not ignoring case when case is not critical to the validation (letters may be uppercase or lowercase), so the /i flag is also useful:
另一个基本的逻辑错误就是在大小写不敏感的校验场合(字母可大写可小写)没有忽略大小写,此时 /i 标志位就很实用:1
str.replace(/david/gi, "Darren"); // "Darren will always be an Arsenal fan, which means Darren will always be great"
(译注:上面这段例程我没有看懂用意,可能是注释有误吧……)
Every JavaScript developer has been bitten by each of the flags in the past – so be sure to use them when when appropriate!
每个 JavaScript 开发者都曾踩过这两个标志位的坑——因此别忘了在适当的时候用上它们!
Array’s slice method is principally for grabbing segments of an array. What many developers don’t know is that slice can be used to covert Array-like objects like arguments, NodeLists, and attributes into true arrays of data:
数组的 slice 方法通常用来从一个数组中抽取片断。但很多开发者不了解的是,这个方法还可以用来将“类数组”元素(比如 arguments 参数列表、节点列表和属性列表)转换成真正的数组:(译注:DOM 元素的属性列表通过 attributes 属性获取,比如 document.body.attributes。)1
2
3
4
5
6
7var nodesArr = Array.prototype.slice.call(document.querySelectorAll("div"));
// "true" array of DIVs
// 得到一个由 div 元素组成的“真正的”数组
var argsArr = Array.prototype.slice.call(arguments);
// changes arguments to "true" array
// 把 arguments 转换成一个“真正的”数组
You can even clone an array using a simple slice call:
你还可以使用一次简单的 slice 调用来克隆一个数组:1
2var clone = myArray.slice(0); // naive clone
// 浅克隆
(译注:这里的参数 0 也可以省略,我估计 undefined 被 slice 方法自动转换为 0 了吧。)
Array.prototype.slice is an absolute gem in the world of JavaScript, one that even novice JavaScript developers don’t know the full potential of.
Array.prototype.slice 绝对是 JavaScript 世界中的一玫珍宝,但 JavaScript 初学者们显然还没有意识到它的全部潜力。
The Array sort method is vastly underused and probably a bit more powerful than most developers believe. Many developers would assume sort would do something like this:
数组的 sort 方法 远远没有被充分利用,而且可能比开发者们想像的更加强大。很多开发者可能觉得 sort 方法可以用来做这种事情:1
2
3[1, 3, 9, 2].sort();
// Returns: [1, 2, 3, 9]
// 返回 [1, 2, 3, 9]
…which is true, but sort has more powerful uses, like this:
……这没错,但它还有更强大的用法,比如这样:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15[
{ name: "Robin Van PurseStrings", age: 30 },
{ name: "Theo Walcott", age: 24 },
{ name: "Bacary Sagna", age: 28 }
].sort(function(obj1, obj2) {
// Ascending: first age less than the previous
// 实现增序排列:前者的 age 小于后者
return obj1.age - obj2.age;
});
// Returns:
// [
// { name: "Theo Walcott", age: 24 },
// { name: "Bacary Sagna", age: 28 },
// { name: "Robin Van PurseStrings", age: 30 }
// ]
You can sort objects by property, not just simple basic items. In the event that JSON is sent down from the server and objects need to be sorted, keep this in mind!
你不仅可以对简单类型的数组项进行排序,可以通过属性来排序对象。如果哪天服务器端发来一段 JSON 数据,而且其中的对象需要排序,你可别忘了这一招!
There’s not a developer out there that hasn’t been bitten by JavaScript’s pass-objects-by-reference nature. Oftentimes developers will attempt to empty an array but mistakenly create a new one instead:
几乎所有开发者都踩过 JavaScript 的这个坑——“传对象只是传引用”。开发者们经常会试图 把一个数组清空,但实际上却错误地创建了一个新数组。1
2
3
4
5
6
7
8
9
10
11
12var myArray = yourArray = [1, 2, 3];
// :(
// 囧
myArray = []; // `yourArray` is still [1, 2, 3]
// `yourArray` 仍然是 [1, 2, 3]
// The right way, keeping reference
// 正确的方法是保持引用
myArray.length = 0; // `yourArray` and `myArray` both []
// `yourArray` 和 `myArray`(以及其它所有对这个数组的引用)都变成 [] 了
What these developers probably realize is that objects are passed by reference, so while setting myArray to [] does create a new array, other references stay the same! Big mistake! Use array truncation instead.
坑里的人们终于明白,原来传对象只是在传引用。因此当我把 myArray 重新赋值为 [] 时,确实会创建出一个新的空数组,但其它对老数组的引用仍然没变!大坑啊!还是换用截断的方法吧,少年。
I showed in point 2 that Array’s slice and apply can do some cool stuff, so it shouldn’t surprise you that other Array methods can do the same trickery. This time we can merge arrays with the push method:
在上面的第 2 节里,我展示了数组的 slice 和 apply 方法所能组合出的几个小妙招,所以对于数组方法的其它技巧,你应该已经做好心理准备了吧。这次我们使用 push 方法来合并数组:1
2
3
4
5
6var mergeTo = [4,5,6];
var mergeFrom = [7,8,9];
Array.prototype.push.apply(mergeTo, mergeFrom);
mergeTo; // is: [4, 5, 6, 7, 8, 9]
A wonderful example of a lessor-known, simple native method for completing the basic task of array merging.
这是一项不为人知的小技巧,简单的原生方法就可以实现数组合并这样的常见任务。
(译注:这个方法的巧妙之外不仅在于 push 方法可以接收多个参数,还涉及到 apply 方法的第二个参数的用法。)
Oftentimes developers will use the following technique to detect a browser feature:
很多时候开发者们会像下面这样来探测浏览器的某个特性:1
2
3
4if(navigator.geolocation) {
// Do some stuff
// 在这里干点事情
}
While that works correctly, it isn’t always efficient, as that method of object detection can initialize resources in the browser. In the past, the snippet above caused memory leaks in some browsers. The better and more efficient route is checking for a key within an object:
当然这可以正常工作,但它并不一定有很好的效率。因为这个对象探测方法会在浏览器中初始化资源。在过去,上面的代码片断可能会在某些浏览器下导致内存泄露。更好、更快的方法是检查对象是否包含某个键名:1
2
3
4if("geolocation" in navigator) {
// Do some stuff
// 在这里干点事情
}
This key check is as simple as it gets and may avoid memory problems. Also note that if the value of a property is falsy, your check will fail despite the key being present.
键名检查十分简单,而且可以避免内存泄露。另外请注意,如果这个属性的值是假值,那么前一种探测方式将会得到“否”的结果,并不能真正探测出这个键名是否存在。
Oftentimes we trigger functionality when action elements like links are clicked. Obviously we don’t want the browser to follow the link upon click, so we use our handy JavaScript library’s Event.stop method:
很多时候,当一个动作元素(比如链接)被点击时,会触发某个功能。显然我们并不希望点击链接时浏览器顺着这个链接跳转,于是我们会习惯性地使用 JavaScript 类库的 Event.stop 方法:1
2
3
4
5
6$("a.trigger").on("click", function(e) {
e.stop();
// Do more stuff
// 在这里干点事情
});
(译注:不知道哪个类库有这个方法,估计其作用相当于 return false; 吧。语法看起来像 jQuery,但 jQuery 并没有这个方法,而且 jQuery 是支持 e.preventDefault 和 e.stopPropagation 方法的。)
The problem with this lazy method of stopping the event is that not only does it prevent the default action, but it stops propagation of the event, meaning other event listeners for the elements wont fire because they don’t know about the event. It’s best to simply use preventDefault!
这个懒方法有一个问题,它不仅阻止了浏览器的默认动作,同时也阻止了事件继续冒泡。这意味着元素上绑定的其它事件监听器将不会被触发,因为它们根本就不知道有事件发生。此时不妨使用 preventDefault 吧!
Seasoned JavaScript developers will see this post and say “I knew those,” but at one point or another, they got tripped up on some of these points. Be mindful of the little things in JavaScript because they can make a big difference.
JavaScript 老鸟们看到这篇文章可能会说“我早知道了”,但说不定什么时候,他们就会在某一点上栽跟头。提醒大家留意 JavaScript 中的各种小细节,失之毫厘谬以千里啊!
]]>Sublime包管理工具,必装
ctrl+` 打开控制台,输入以下代码1
import urllib2,os; pf=’Package Control.sublime-package’; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler( ))); open( os.path.join( ipp, pf), ‘wb’ ).write( urllib2.urlopen( ‘http://sublime.wbond.net/’ +pf.replace( ‘ ‘,’%20′ )).read()); print( ‘Please restart Sublime Text to finish installation’)
Zen Coding,不多解释,懂得自然懂,虽然Sublime3 原生提供了部分Emmet语法,但是不全,比如 html:5 就没有
###修改 Emmet 兼容jsx 文件
打开 preferences -> Key bindings - Users
,把下面代码复制到[]内部1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32{
"keys": [
"super+e"
],
"args": {
"action": "expand_abbreviation"
},
"command": "run_emmet_action",
"context": [{
"key": "emmet_action_enabled.expand_abbreviation"
}]
},
{
"keys": ["tab"],
"command": "expand_abbreviation_by_tab",
"context": [{
"operand": "source.js",
"operator": "equal",
"match_all": true,
"key": "selector"
}, {
"key": "preceding_text",
"operator": "regex_contains",
"operand": "(\\b(a\\b|div|span|p\\b|button)(\\.\\w*|>\\w*)?([^}]*?}$)?)",
"match_all": true
}, {
"key": "selection_empty",
"operator": "equal",
"operand": true,
"match_all": true
}]
}
代码格式管家,最主要的功能是,同一项目代码在不同编辑器下能够保持统一的代码风格(主要是缩进,如sublime默认缩进宽度是4,我写JS缩进都是2)。
EditorConfig支持除Sublime之外的很多主流编辑器与IDE,如VisualStudio, Eclipse, VIM, WebStorm等
在项目根目录新建一个文件:.editorconfig,在此文件里填写你需要的格式规则,以下是一个典型的.editorconfig1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# http://editorconfig.org
root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
JSHint不但能检查JS代码的语法错误,还能够监控代码质量(风格),很多公司跟开源项目都在使用它,比如:Facebook、jQuery、Bootstrap等等。在Github上,使用JSHint的项目多如牛毛,如果你在某个项目里面,发现有.jshintrc文件,那它就使用了JSHint。JSHint是一个独立的工具,它不直接提供对Sublime的支持,它本身只提供基于NodeJS的命令行工具。而JSHint Gutter这个插件,它能够调用JSHint的命令行工具,执行代码检查并将结果显示到Sublime界面上。
先安装好NodeJS,然后在终端/命令行中输入 npm install -g jshint
安装打开1
Preference->Package Settings->JSHint Gutter->Set 'node' Path
设置NodeJS执行文件所在的路径(node_path),并将lint_on_save(文件保存时检查)选项设为 true
在项目根目录新建一个文件:.jshintrc,在此文件里填写你的检查规则,以下是一个典型的.jshintrc1
2
3
4
5
6
7
8
9
10
11{
"curly": true,
"eqeqeq": true,
"immed": true,
"noarg": true,
"noempty": true,
"quotmark": "single",
"undef": true,
"unused": true,
"node": true
}
第二行:curly 表示所有的代码块必须使用大括号
第三行:eqeqeq 表示判断相等时,必须使用 ===
第四行:immed 表示立即执行函数必须用括号包起来 (function () { } ());
第五行:noarg 表示禁止使用 arguments.caller
和 arguments.callee
第六行:noempty 表示禁止出现空的代码块 { }
第七行:quotmark 是引号的使用规则,有以下四个选项
false : 不检查
true : 检查一致性(要么都是单引号,要么都是双引号)
single : 必须都是单引号
double : 必须都是双引号
括号高亮匹配,眼睛不好
JavaScript 代码格式化插件,快捷键是 ctrl+alt+f
跨平台取色器插件,快捷键 ctrl+shift+c
CSS中的颜色可以实时看到什么颜色
高亮显示多余的空格和Tab,在保存文件的时候自动删除他们,代码洁癖
支持ES6, React.js, jsx代码高亮,对 JavaScript, jQuery 也有很好的扩展
打开.js, .jsx 后缀的文件;
打开菜单view, Syntax -> Open all with current extension as... -> Babel -> JavaScript (Babel)
,选择babel为默认 javascript 打开syntax
JS, CSS压缩
用过 WebStorm 的应该知道,类似于本地的自动的历史版本管理,有时候能救命的东西
绿色便携版没有右键 Edit with sublime 得写注册表,我参考了网上,用ActiveX JS写了个网页脚本,用管理员权限打开IE浏览器运行这个网页
先在input框里选择你的 sublime_text.exe文件,主要是获取你的sublime文件路径,其实ActiveX里有个api是WshShell.CurrentDirectory获取当前路径,不知道为什么运行后永远是桌面路径,暂时没找到解决方法,只能退而求其次自己选择路径了
看按钮名就知道功能了,添加右键菜单的注册表和删除右键菜单的注册表
]]>暂时先这些,如有改动我会及时更新
1 | var GG = function() { |
看到这段代码我敢肯定每个人写JS的内心都有万匹草泥马呼啸奔腾而过…
要是再复杂点…中间再加点if判断什么的…这代码简直呵了个呵
嵌套加嵌套,摩擦摩擦
自己都不想看第二遍了吧
该如何写的不那么丑呢?其实也不算丑…主要我写代码比较好看,哦哈哈哈…
回归正题!该咋办呢?
幸好,我们碰到的问题前人已经碰到过了
于是诞生了一个牛逼的模块,也就是本文的主角——Async
这个模块就是用来解决异步流程控制问题的,接下来我们来看看上面这段代码如果用async写应该怎么写:
首先说一下函数功能,
async.series(tasks, [callback]) 是按顺序执行指定的函数,前一个结束了才会执行下一个
1 | var async = require("async"); |
什么?你说怎么这么长?+_+
可能我举得栗子不好…不过这不影响…你看,从上往下执行,这流程思路多清晰啊!多自然啊!
再也没有层层嵌套!
async.waterfall(tasks, [callback])
waterfall和series函数有很多相似之处,都是按顺序依次执行一组函数,不同之处是waterfall每个函数产生的值,都将传给下一个函数,而series则没有这个功能
1 | var async = require("async"); |
简单说就是第一个函数是从archives页获取所以文章的url,然后push到urls数组里,完成后把urls数组传递给第二个函数,第二个函数并发获取数组里链接的文章的title,并发数我随便写了个5,因为有些网站会因为你的并发数太多而把你的ip禁掉,虽然我的网站应该不会这样。
> node test.js
19s 342ms 正在抓取:http://go.kieran.top/post/28/
19s 349ms 正在抓取:http://go.kieran.top/post/27/
19s 350ms 正在抓取:http://go.kieran.top/post/26/
19s 353ms 正在抓取:http://go.kieran.top/post/25/
19s 361ms 正在抓取:http://go.kieran.top/post/24/
19s 619ms 正在抓取:http://go.kieran.top/post/23/
19s 642ms 正在抓取:http://go.kieran.top/post/22/
19s 686ms 正在抓取:http://go.kieran.top/post/21/
19s 724ms 正在抓取:http://go.kieran.top/post/20/
19s 750ms 正在抓取:http://go.kieran.top/post/19/
19s 924ms 正在抓取:http://go.kieran.top/post/18/
19s 958ms 正在抓取:http://go.kieran.top/post/17/
20s 6ms 正在抓取:http://go.kieran.top/post/16/
20s 19ms 正在抓取:http://go.kieran.top/post/15/
20s 61ms 正在抓取:http://go.kieran.top/post/14/
20s 187ms 正在抓取:http://go.kieran.top/post/13/
20s 260ms 正在抓取:http://go.kieran.top/post/12/
20s 286ms 正在抓取:http://go.kieran.top/post/11/
20s 318ms 正在抓取:http://go.kieran.top/post/10/
20s 343ms 正在抓取:http://go.kieran.top/post/9/
20s 415ms 正在抓取:http://go.kieran.top/post/8/
20s 537ms 正在抓取:http://go.kieran.top/post/7/
20s 564ms 正在抓取:http://go.kieran.top/post/6/
20s 586ms 正在抓取:http://go.kieran.top/post/5/
20s 621ms 正在抓取:http://go.kieran.top/post/4/
20s 681ms 正在抓取:http://go.kieran.top/post/3/
20s 823ms 正在抓取:http://go.kieran.top/post/2/
20s 847ms 正在抓取:http://go.kieran.top/post/1/
文章标题:
[ ‘树莓派教程——HC-SR04 超声波测距模块’,
‘树莓派教程——BMP180 温度气压传感器’,
‘树莓派教程——人体红外感应模块HC-SR501加有源蜂鸣器实现简易报警装置’,
‘树莓派教程——DHT22温湿度传感器AM2302’,
‘树莓派教程——LED灯’,
‘树莓派初体验——配置篇’,
‘前端黑科技——纯clip-path制作属于你的个人动画’,
‘结果’,
‘非对称密码学实验:Python实现RSA的加解密’,
‘慕课网视频下载小程序’,
‘判断 IE 全版本浏览器的几种方法’,
‘2015 阿里面试小结’,
‘HTML5 + JavaScript 读取本地文件’,
‘aircrack-ng 命令简单记录’,
‘TKL-Hexo Theme’,
‘iframe && 网页底部 动态设置高度’,
‘HTML的一些小动“画”?’,
‘Node.js初体验——从一个简单的爬虫开始’,
‘浅谈CSRF防御-基础篇’,
‘记一次有趣的CSRF’,
‘聊聊 Google Project Zero’,
‘远程线程注入(0X02)注入第一个DLL’,
‘远程线程注入(0X01)第一个动态链接库’,
‘JavaScript操作注册表’,
‘信息隐藏——LSB算法’,
‘IntelliJ IDEA 14.0正式版激活算法’,
‘MBR,GPT的小科普’,
‘Hexo 简单优化’ ]
parallel(tasks, [callback])
parallel函数是并行执行多个函数,每个函数都是立即执行,不需要等待其它函数先执行。 传给最终callback的数组中的数据按照tasks中声明的顺序,而不是执行完成的顺序
queue(worker, concurrency);
queue相当于一个加强版的parallel,主要是限制了worker数量,不再一次性全部执行。当worker数量不够用时,新加入的任务将会排队等候,直到有新的worker可用。
它有多个点可供回调,如无等候任务时(empty)、全部执行完时(drain)等。
串行控制:
series、waterfall、compose;
并行控制:
parallel、parallelLimit、queue;
循环控制:
whilst、doWhilst、until、doUntil、forever;
其他控制:
apply‘applyEach、iterator、auto;
###超声波测距模块简单介绍
电压:5V
静态电流:小于2mA
电平输出:高5V
电平输出:底0V
感应角度:不大于15度
探测距离:2cm-450cm
1 | import RPi.GPIO as GPIO |
还是很简单的,理解一下就行
下面的视频里我做了一个根据测的距离每隔两厘米亮一个灯的DEMO
如果你看过我写的前几篇教程,相信你很快就能做出来了,我这里只是抛砖引玉
推荐两个用树莓派超声波模块做的游戏,感兴趣的同学可以玩一下:
基于RasberryPi的体感音乐游戏:http://homeway.me/2015/03/30/play-music-through-senses
基于RasberryPi的打地鼠游戏: http://homeway.me/2015/05/26/play-hamster-game-through-raspberry