SegmentFault 目田最新的文章
2016-11-16T22:39:05+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
小程序 102800 以及新版 WEPT 的改动
https://segmentfault.com/a/1190000007505803
2016-11-16T22:39:05+08:00
2016-11-16T22:39:05+08:00
chemzqm
https://segmentfault.com/u/chemzqm
0
<p>上一版微信小程序 0.10.102800 带来了许多的变化,以至于我一度以为 <a href="https://link.segmentfault.com/?enc=65LNi0Q%2F64rax9zd%2BRAvHw%3D%3D.lFx6qgAiBhZdbwBdbWcL5Rpt4zA%2BhBmQWC8mdWXB93Q%3D" rel="nofollow">WEPT</a> 这个主打热更新的小工具没什么继续存在的必要了,然而尽管我看到了小程序团队对于完善开发工具的不懈努力,它的体验在很多方面还是不那么让我满意(经常性崩溃以及内存泄露)。</p>
<p>102800 版本具体新增和修复的改动 官方文档 里面已经做了详细的说明(虽然他们代码未必很好,但是文档很专业)。我来谈一些内部实现上的一些变化,希望能帮大家对于小程序结构有个更好的了解,被坑了时候也能大概知道是怎么回事 ?</p>
<p><strong>新的 wxss 构建方式</strong></p>
<p>之前版本 wxss 中的 rpx 单位使用的是 rem 进行的模拟,但是微信团队发现这种方式部分机型上会有边框显示不全等问题。 新版中的 rpx 会通过当前页面的 screenWidth 和 deviceRatio 动态计算出一个 px 数值,然后取整数。</p>
<p>官方工具中 wxss 加载过程为后台编译 wxss 生成 javscript 文件,然后前端 view 层加载并执行这个 script 生成 css 文本, 最后生成 css 标签添加到 head 元素里。这种方式缺点在于如果我们修改了 wxss 或者调整页面宽度,所有页面都需要重新刷新, 而不仅仅是重新加载 css 那么简单,另一个问题是生成的内联 css 无法使用 css souremap 支持,不过官方工具提供了自己的 wxml 面板并且加入了相应 wxss 文件的跳转功能。</p>
<p><strong>新的交互反馈 API</strong></p>
<p>102800 版本加入了 wx.showToast wx.showModal wx.hideToast wx.showActionSheet 4 个交互 API, 方便开发者更容易的使用组件。这几个 API 的 UI 是由底层直接生成的,所以它的缺点就是不能使用 css 重写样式了, 设计的时候要注意了,如果需要好看的样式只能自己实现相应功能。</p>
<p><strong>更加完善的 wxml 面板</strong></p>
<p>新版的 wxml 支持属性页面同步、编辑元素属性、显示元素样式和 Dataset 等功能,尽管没有 Chrome 自带的 Elements 面板那么灵活,但是已经非常实用了。</p>
<p>WEPT 0.5.0 在兼容现有小程序 API 的基础上,同时也做出了一些改进。</p>
<p><strong>css sourcemap 支持</strong></p>
<p>WEPT 使用后端将 wxss 文件转换为 css 文件,并且添加了 css sourcemap 功能, 开发者可以直接从样式面板跳转到对应文件的对应行,如果你在 Chrome 的 devtools 中添加了 “映射到本地文件”, 可以直接去编辑对应文件,因为 WEPT 支持 wxss 文件热更新,所以保存后样式会立即生效。</p>
<p><strong>JavaScript sourcemap 支持</strong></p>
<p>官方工具支持 JavaScript 的 souremap,但是目前的实现有 bug,使用本地文件映射功能会报错:</p>
<p>WPET 中的 souremap 可以映射到本地文件,开发者可以直接在 Chrome 的 sources 面板下修改相应文件。</p>
<p><strong>代理配置支持</strong></p>
<p>WEPT 支持在项目目录下的 wept.json 配置文件,开发者可以添加该项目使用的代理服务器配置, 具体使用方法请参考 wept 配置文档。</p>
<p>如果你需要使用工具调整修改请求或者服务端响应,别忘了调整 app.json 里面配置的超时时间: networkTimeout</p>
<p><strong>重做了 wxml,wxss 以及 javscript 热更新</strong></p>
<p>wxml 改为由控制层发起新模板请求, 生成新的 generateFunction 全局变量到 view 层,再通过发送 appDataChange 事件让 view 层重新渲染,避免原先修改 view 层代码可能导致的 bug。</p>
<p>wxss 文件的热更新支持新版的 rpx 计算方式,支持 window resize 后自动重刷新,支持 <a href="/u/import">@import</a> 引入文件修改后自动刷新相应 css。</p>
<p>javascript 文件修复了之前路径上 query 参数无法正确传递的 bug, 相比于官方工具修改 js 文件后重置到项目主页,WEPT 针对页面 JavaScript 文件使用动态热更新,即便是其它 js 文件,在页面刷新后也将自动导航到之前页面。</p>
<p><strong>启动时编译所有文件</strong></p>
<p>现在 WEPT 启动时会将所有的 wxml、wxss 以及 JavaScript 进行解析后放入缓存对象,这有助于帮助开发者尽早发现编译期的 bug, 同时一定程度提高了页面载入速度(每个 wxss 文件大概 30ms 左右)</p>
<p><strong>端口自动查找</strong></p>
<p>新版会自动从 3000 端口往上查找可用的端口号,避免端口不可用导致报错中止的尴尬情况。</p>
<p>相比与之前较多改动官方 view 层和 service 层代码的方式,新版 WEPT 采用了新的代码注入方式,只需对官方代码做出 少量修改 即可完成新版升级,之后的代码更进不会再像这次便秘这么久了?</p>
文件保存后自动刷新微信小程序
https://segmentfault.com/a/1190000007005666
2016-09-26T08:00:00+08:00
2016-09-26T08:00:00+08:00
chemzqm
https://segmentfault.com/u/chemzqm
0
<p>微信开发者工具支持刷新和重建的快捷键,所以我们可以发送一段 apple script 脚本来达到刷新/重建项目的效果,<br>从而免去手工切换再使用快捷键的麻烦。例如:</p>
<pre><code class="applescript">tell application "wechatwebdevtools"
activate
delay 0.2
tell application "System Events"
key code {55, 15}
end tell
end tell</code></pre>
<p>这段代码可以使用命令 <code>osascript</code> 执行,效果就是聚焦微信开发者工具,然后发送 <code>Command + R</code> 刷新界面。</p>
<p>这里的问题是我们还是需要每次刷新都要切换到微信开发者工具界面,这对于开发的流畅度还是有影响的。</p>
<p>一个简单的解决办法就是修改开发者工具源码,让它支持全局快捷键,经过一番查找,找到了文件:<br><code>/Applications/wechatwebdevtools.app/Contents/Resources/app.nw/app/dist/common/menu/menu.js</code>,<br>添加代码:</p>
<pre><code class="js">function registShortcut(key, onactive) {
var option = {
key : key,
active : onactive,
failed : function(msg) {
// :(, fail to register the |key| or couldn't parse the |key|.
console.log(msg);
}
};
// Create a shortcut with |option|.
var shortcut = new nw.Shortcut(option);
// Register global desktop shortcut, which can work without focus.
nw.App.registerGlobalHotKey(shortcut);
}</code></pre>
<p>以及</p>
<pre><code class="js">registShortcut("Command+Shift+R", function () {
e.reload()
})
registShortcut("Command+Shift+B", function () {
e.reBuild()
})</code></pre>
<p>到 init 函数下面就可以让开发者工具支持全局刷新的热键了(Command+Shift+R 和 Command+Shift+B),<br><a href="https://gist.github.com/chemzqm/9db067cd99054332a94be49accc29da7">完整文件下载</a></p>
<p>然后只需要让 vim 保存 wxss 和 wxml 文件后发送快捷键就可以了。使用一键安装命令:</p>
<pre><code>curl https://raw.githubusercontent.com/chemzqm/vim-macos/master/autoload/macos.vim > ~/.vim/autoload/macos.vim</code></pre>
<p>安装 <a href="https://link.segmentfault.com/?enc=dPNcCjSf%2BhCZZl7xfjJ89w%3D%3D.ZUA6ufqbE9T6HFaFKI7z3nEXRnaLuXRuMtKsjoUdWPQGV2b9pb6mq5rRW0oMTYA7" rel="nofollow">vim-macos</a> 插件, 然后在 <code>~/.vimrc</code> 中配置:</p>
<pre><code class="viml">autocmd BufWritePost *.wxml call macos#keycodes('command', 'shift', 'r')
autocmd BufWritePost *.wxss call macos#keycodes('command', 'shift', 'r')</code></pre>
<p>大功告成。</p>
<p><em>(完)</em></p>
个人使用的 vim 插件集合
https://segmentfault.com/a/1190000006894422
2016-09-13T13:41:20+08:00
2016-09-13T13:41:20+08:00
chemzqm
https://segmentfault.com/u/chemzqm
2
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=n0fBkW59n9mb32X2UNpj9w%3D%3D.kML7iYkpOglTpjzhz7%2BdQ6Ekvamf0drYAyZeksdO7HHlzvj3TA6eWnVlZWD0Q4TLbJKO6n7KvVFuoe9PeTsjFQ%3D%3D" rel="nofollow">matchit.vim</a> 扩展了 vim 的 <code>%</code>,用于快速跳转到配对的标签(对编辑 html 有很大帮助), 新版 vim 7.0 以上自带该插件</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=pzXusLQ2zEy0FJFKKaTYgg%3D%3D.TIknqWhXkzUkgH4dD%2BSZTjyCvA7azoU3ngvYFr98mYk%2BkofHP8aSp88pTT9GIZ0f" rel="nofollow">emmet.vim</a> 快速编写 html 的神级插件,可支持 jsx</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=NG6m%2FSkmQDWdm5rl%2BGzqoA%3D%3D.guThqm%2Fc5NTC%2BmD5q9GmYEEjNm06yz%2FP8q6Quu79GDI%2BqDnpK00aVbpTcRplRVAI" rel="nofollow">xml.vim</a> 快速编辑 html 和 xml 的插件,可支持 jsx</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=zkQpQ8tyuxDcSESK6MovnA%3D%3D.AAjj8PlFyskVErk7hqydWDfMpG81SS85ISLuaqSMB9SDqrtC12fjOM1FXoNBx0H8" rel="nofollow">html5.vim</a> 为 html 文件提供 omnicomplete 函数</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=XRtgwFcdu0odSQMwQODsIQ%3D%3D.HTEjYQglwI12E%2Febe0MMSjrnd7DoSXpLl76wXotmS4x7knPVaVdNPJf9xtQNUpcr" rel="nofollow">vim-easy-align</a> 提供快速对齐操作符</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=jqhSmKVPvX%2BHkW0XIYu7rw%3D%3D.BeIKu4JPD83ClqUgKuQL4MTA1B2nhSvIPo9XclTo1FvIc7FMxamEpH7ec729H7eE" rel="nofollow">vim-css-color</a> 解析 css 颜色语法并用想对应颜色做为背景色显示,支持多种语法和文件类型,支持 neovim 的 truecolor</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=2mOe%2BG976ND9p4kmMuSNLQ%3D%3D.SOTHzWpZGMiv3v%2Bcr4duQX4VICw1zQpFqodUYzBEivhlEAFVZmot3cF1J5fv2roW" rel="nofollow">ultisnips</a> 代码段管理插件,优点是功能全面、易于扩展,缺点是因为是 python 插件,所以启动略慢</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=QnY3pZ5YkT2s1HLyhbvMOA%3D%3D.sOUe3DKs68Taw2%2FKackwc6oDgN%2BdgMqMxVjFqjf6YgsHg7rIsGTEfu7%2Fw3UKDRFq" rel="nofollow">exchange.vim</a> 提供文件快速交换的操作符,方便文本位置互换</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=XdSAaaszhh6IOR7YXQCRhQ%3D%3D.jmVG8MqcAPSdgTGghrzccxli13%2BiW5N6%2F1w7Eejp%2Fa1VV%2BMoMpNY6PyEjwu3lPoo" rel="nofollow">vim-surround</a> 提供快速操作配对字符的操作符, 例如可使用 <code>ds'</code> 删除前后的 <code>'</code></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=30x9etwuS%2BP1FDPTDhjldA%3D%3D.85Eh1w3h5PBqyxwMbnodkwZGuldH1fk%2BZtWPfvlJbBCNZo0kZQPyUsSdSqnS%2FPjA" rel="nofollow">vim-sneak</a> 通过快捷键和双字符快速定位光标位置</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=ZinfHxugvS9wPdOH9BOcFQ%3D%3D.xDu1L5oIz%2BzreA6y1Eg7s6isGBnliDr3sq2M5d7%2BgghkPX0qWGo4KDMmrn06n9cr" rel="nofollow">vim-stay</a> 自动记录和恢复光标在 buffer 之前的位置,该插件可能会对 vim 启动时间影响较大</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=uK%2FpN1p%2BQkJNtcIMUUTj%2Fw%3D%3D.d847n1c6SHNbuuYmA2t0xYYM%2B6u6UopjeCHfBMhFA%2F8%2FJVUe6NSjG5OpkuI5iZZN" rel="nofollow">tern_for_vim</a> 提供 javascript 代码解析、自动完成、函数跳转、查看定义等智能功能的 vim 插件,新版包含新的 webpack 插件,可以理解指定 webpack 配置文件</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=4tmdUrM8zys2%2FBF5MOGG8A%3D%3D.F%2BLiIYSiADpjS8uBnIH2f6qjpe7EF3etVqUjyfhArYbnDMnofvwp31Rqnzcjcugd" rel="nofollow">vim-gitgutter</a> 在侧边栏显示 git 变动行状态的插件</p></li>
<li><p><a href="https://gist.github.com/chemzqm/79c542efef8ddf98a732">simple_pairs.vim</a> <strong>[原创]</strong> 自动输入配对字符, 改自 <a href="https://link.segmentfault.com/?enc=v0QMvKDfAahR2KPyCBdOFQ%3D%3D.sEba8u16FUZPde9KvE5IGYGVyQzzscz56lQE3EGQCH2Y1%2FC0y8CbjEwOaNFzrrYJyMn%2BoTYtVC7Gv8fiDozRqA%3D%3D" rel="nofollow">simple_pairs.vim</a>, 使用 vimscript 实现加载时间更短,同时改进了配对出现规则,适合所有语言使用。</p></li>
<li><p><a href="https://gist.github.com/chemzqm/287b0e98560e2e0a1491">complete.vim</a> <strong>[原创]</strong> 关键词补全插件,只做了 tab 到 <code><c-n></code> <code><c-p></code> 的映射,使用 vim 的 <code>complete</code> 和 <code>completeopt</code> 选项配置, 新手推荐 <a href="https://link.segmentfault.com/?enc=gVQobOu%2BZlT6hW4asktlDg%3D%3D.I6nSCNHGJn%2FMEiZqHCrLER%2BKyUyLezyxcES21o5iLBmSIftfK88GlMG9HDTTrHcq" rel="nofollow">supertab</a></p></li>
<li><p><a href="https://gist.github.com/chemzqm/a235e5151d530cdef760">open.vim</a> <strong>[原创]</strong> Mac 下快速打开当前行的 url 和 email,简单调用了下 <code>open</code> 命令</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=DH2RfNxJeZfjflDGaYisaQ%3D%3D.5P0DDPyXnBrHl%2FAUa6Syyu1guDZBB1oVEF5%2B4UAgdo%2BLd8nboWDJ%2F%2Bhr9MkYNxX9" rel="nofollow">plug.vim</a> <strong>[原创]</strong> 专注插件路径管理的 vim 插件</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=6NGI7S%2FJWlswidh0CBwA6A%3D%3D.mAcRvlFgU4NHRxctiIKiIlRlGrb3NpX%2Ff394Hk4fYK9%2BmQ9AkHK56hK0NSxul09y" rel="nofollow">mycomment.vim</a> <strong>[原创]</strong> 堪称完美的注释解决方案,一个快捷键搞定注释问题,喜欢配置的可以使用 <a href="https://link.segmentfault.com/?enc=O2OeenagsUNy%2BGP7PEiH%2FA%3D%3D.Ni4bmD401Nciq1DAoQTaa93FS5CzVxz9X0NWAldPxFTE%2FvdGwfryxZgyXRxeZs6y" rel="nofollow">nerdcommenter</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=8YC3AcId3Z4EoFROC3g2%2FA%3D%3D.cahAdAx3DkOwr6zGchYcxtzDIcFg7g9E6lVN6nJGHpEtUa5hT1aD%2F1kqT33QZ1sA" rel="nofollow">vim-easygit</a> <strong>[原创]</strong> 轻量级 git wrapper 插件,拥有所有 <a href="https://link.segmentfault.com/?enc=0xdhqneOjJuyTsIUcwWqcA%3D%3D.Yr45rSpXmu9je%2FtAVCBEKAX3Um99QGYBtPon72SYJsWeT4NTBlSV10LW%2B2%2FnxyWv" rel="nofollow">fugitive</a> 具有的功能,同时解决其难以扩展的问题</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=maib6p7%2FlBqy8uP64MZnQg%3D%3D.r3wn8oNO0WT3azRicFXaVV9oOrO7wT5Da7WgIwfXRdW%2FbbrPxa7YX8WpMDSJhizg" rel="nofollow">unite.vim</a> 提供资源操作的统一接口,以下部分组件基于其进行开发</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=s9q6N5FVNzBffQYKol5WDA%3D%3D.y6f%2Fzmttg6nGO%2BbN1IBr4CNUYdevrN7Ica8PFxkqB%2BtZylRyEXWW7hpzpURmI4da" rel="nofollow">unite-outline</a> 基于 unite 的提纲列表插件,用与当前文件内函数快速跳转</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=A%2B9GohMcl66LvEVYYXQeqQ%3D%3D.H2Y6b9jskWF1NKYx47xyCdK%2BsTAfC%2Bia4nJoMgLMCW4YPy%2BIfhjOcp6lxa9d9XOa" rel="nofollow">unite-extra</a> <strong>[原创]</strong> 一些实用的 unite 插件集合,包括 emoji command project 和 node</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=K8RyEOpzxL2EyYDK3U0cug%3D%3D.m4pAD2wCWI3DSVnXzBWN%2B2m9%2FtLMeZmrHPZLngBHs%2FkvewaHs3vCK6M06t12Gyll" rel="nofollow">unite-git-log</a> <strong>[原创]</strong> 整合 gitlog 到 unite 界面的插件, 基于 <a href="https://link.segmentfault.com/?enc=YyC34vrNJP0xcB07HV7MLQ%3D%3D.ZtQwTrpDV1qfYuurc3g%2Fg7ChIRYYIF1lKpsNXbLti7AuyboMnWPUNhuOMyBiPifW" rel="nofollow">vim-easygit</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=jqAyXomTIDh5w%2BM6OyajcQ%3D%3D.yGRWvLpVhL6xSj76nOkrNIIeOVOU0yfiTg5aF4GfOB%2BrX%2F2Pf8co2MoSj53ijUXe" rel="nofollow">unite-js-func</a> <strong>[原创]</strong> 快速定位 js 函数的 unite 插件, 已支持 es6 语法</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=1E2fYwP4rahQnAkETxMDZQ%3D%3D.QrlcK0VSZYGjqujM%2F%2FEzK0tsfF345gk2Zb9R%2FFTewxgDwSQn4SmifmD8f%2BsrzvMU" rel="nofollow">unite-location</a> <strong>[原创]</strong> 整合 vim quickfix 和 location 列表到 unite 的插件</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=d3%2FvX%2B6wg4RrQ9%2FXt4FDkg%3D%3D.uVXU3cEMLZ19%2FGYDRe12Kzs2lH%2BStsKhbEJS%2FrPwbCd2V%2FqlbJceOaQLxwsQ3XVm" rel="nofollow">vim-iterm2-start</a> <strong>[原创]</strong> 让 iterm2 执行任务,主要解决 vim 不能异步,执行耗时任务会卡住的问题。</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=Ebu1nZ9Fz7U3lXNQs7z4Vw%3D%3D.V%2BpVp25Y94HQSOaLZg97OXvV9Qssx9fQRXzSGrtfLaP8P9tk3QmOaZL13E8EX6UD" rel="nofollow">vim-macos</a> <strong>[原创]</strong> 包含些与 macos 交互的函数,包括 ItermOpen, 发送系统 keycodes 等</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=AzRC9Hzp8mpHo3M0meklMA%3D%3D.H%2B8TMkhR7soHjdkhvyhRacXnyF5KYN2tu%2FcrLZjgBMpoJGSZuFcptyosKEOIi%2FhV" rel="nofollow">vim-run</a> <strong>[原创]</strong> 快速执行当前文件并看到结果的小工具,主要用于学习新语言。</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=POzMfFgW%2BizfI2VbYu2MoA%3D%3D.hz9jTc6%2Fj3XbBMmyNU0KceGCwprOeSqIPl9Elh2r8cwdGQ6Tbc3%2FImErQOwyzD%2FS" rel="nofollow">vim-v2ex</a> <strong>[原创]</strong> 异步方式刷新 v2ex 的小插件 (已废弃)</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=5nQi5C5Nis%2FvtagVIrWgbw%3D%3D.LnNK%2BkwDfAgISQgvt9AxVIHNMJPCrAf8V0IpuJJTAtvf7PiUfEJDHqVBPIBRZXPB" rel="nofollow">macdown.vim</a> <strong>[原创]</strong> 使用 applescript 在 chrome 内快速预览 markdown 效果的插件</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=AZBXYJ7OpkDUQJrDZfBJHA%3D%3D.UqFGKvLC0WJ%2BPsz4wkkgObFLz2sQy8JeBUQFBKmO4dvSAKcZdaSQWy8dwCzAPjmI" rel="nofollow">macnote.vim</a> <strong>[原创]</strong> 使用 markdown 写笔记,支持 unite 和命令行接口进行笔记管理、Chrome 快速预览等功能</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=tFwkRbotrVX7Fx0BYfEyFw%3D%3D.OmgcmXAmDr4WPnrcNjGXLSKRdZ6yCQL9PGjLc0OZkLG%2FloR6LT%2BRsyyqjB6AxcYB" rel="nofollow">todoapp.vim</a> <strong>[原创]</strong> 支持 unite 接口的 todo 管理插件</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=Wpnake2AyIIlPHzeWjME0Q%3D%3D.kwA3%2Fmls7CmE%2B25JjoT%2F1q92SIvfmyGpgp2HUrLHtZ9Tj1t0jYBoGxL911j%2BtM4b" rel="nofollow">unite-session</a> <strong>[原创]</strong> 支持 unite 接口的 session 管理插件,改自 <a href="https://link.segmentfault.com/?enc=lVWIOrkIGfl6AC%2FjkkIeiw%3D%3D.Mi9QsRgRg%2BaJISy%2Fy%2Bj3mOfLJ%2FqeHaUPsWOc1qui7ZXR6%2Br7N1UsMUp%2BPwQWH%2FAH" rel="nofollow">Shougou/unite-session</a></p></li>
<li><p><a href="https://gist.github.com/chemzqm/f155aec0eff3d09b31eb0f502b7b7934">devlorem.vim</a> <strong>[原创]</strong> 通过网络 API 获取若干段文字</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=oF%2Fy3gwCPPkfmgCoV8%2Fejg%3D%3D.%2F2cox1DUV6lNu1vrRGDIhq3YJAsGoePHRtmAozYDw3Rc6%2Bnzv3Z0ch12h6I6qG8%2F" rel="nofollow">redismru.vim</a> <strong>[原创]</strong> 使用 redis 的异步 近期文件列表插件,主要解决同步方式占用启动时间过长的问题</p></li>
<li><p><a href="https://gist.github.com/chemzqm/b7d1091d5b4b85ebd622">qdo.vim</a> <strong>[原创]</strong> 主要用于对 quickfix 搜索结果进行批量替换,例如:<code>:Qdo %s/\<abc\>/ABC/gc | update</code>, 新手推荐使用 <a href="https://link.segmentfault.com/?enc=QNuatOjv4eDygzIbBty7Zg%3D%3D.g1lpJBTWwH4uA%2F%2FITG1GWks4EdX31CN1LmEu0f1Y14Mbgbcl6fpPAffUOqA3sZ62" rel="nofollow">ctrlsf.vim</a></p></li>
<li><p><a href="https://gist.github.com/chemzqm/b798a1c51f533bbc9667">system.vim</a> <strong>[原创]</strong> 整合了一些系统相关操作,包括 <code>rm</code> <code>rename</code> <code>mkdir</code> <code>copy to clipboard</code>,新手推荐 <a href="https://link.segmentfault.com/?enc=kLEN4Vybi0tNGk3NwZiY7w%3D%3D.cwIT4WqWEyaEOYVBs9BCTU2StgjSBi1hoHIhItyP0X0vUOZU8nxs4RLvuB3kC9pc" rel="nofollow">nerdtree</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=mwfLtYY5VkmqeEGmDAiHHQ%3D%3D.FoFcer0IdI0LTynNFrOBfVtSo%2BydUZVaEBeMcUYvYf7L6lrUFo7XVCy3QDnaFkFR" rel="nofollow">vim-bbye</a> 移除 buffer 同时尽可能保留原有的窗口,方便切换 buffer 的同时保留窗体布局</p></li>
</ul>
<p>原文地址 <a href="https://link.segmentfault.com/?enc=fsIdeIk4m19jSw9gbzEmSA%3D%3D.W1Wov8joszUWqQdgfs3%2Fa79fJvEJouAjG%2Fzgvcetlk8%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=kD41l%2B21p4bO6Yy92iWUlw%3D%3D.TO%2BmrdI9l%2FW2JlfOs8zD7C5rHgs9%2BfxUg%2FdTxRioloQ%3D" rel="nofollow">https://chemzqm.me/vim-plugins</a></p>
webpack sourcemap 选项多种模式的一些解释
https://segmentfault.com/a/1190000004280859
2016-01-10T14:24:05+08:00
2016-01-10T14:24:05+08:00
chemzqm
https://segmentfault.com/u/chemzqm
39
<p><img src="/img/bVr7HN" alt="" title=""><br>有人说 Webpack 难用,原因是 sourcemap 有 7 种,本文就尝试解释下这些选项都是什么意思,不仅如此,本文还会分析如何用最好的姿势使用这些配置。</p>
<hr>
<p>先一项一项来看:</p>
<ul>
<li><p><code>eval</code> 文档上解释的很明白,每个模块都封装到 eval 包裹起来,并在后面添加 <code>//# sourceURL</code></p></li>
<li><p><code>source-map</code> 这是最原始的 source-map 实现方式,其实现是打包代码同时创建一个新的 sourcemap 文件, 并在打包文件的末尾添加 <code>//# sourceURL </code> 注释行告诉 JS 引擎文件在哪儿</p></li>
<li><p><code>hidden-source-map</code> 文档上也说了,就是 soucremap 但没注释,没注释怎么找文件呢?貌似只能靠后缀,譬如 <code>xxx/bundle.js</code> 文件,某些引擎会尝试去找 <code>xxx/bundle.js.map</code></p></li>
<li><p><code>inline-source-map</code> 为每一个文件添加 sourcemap 的 DataUrl,注意这里的文件是打包前的每一个文件而不是最后打包出来的,同时这个 DataUrl 是包含一个文件完整 souremap 信息的 Base64 格式化后的字符串,而不是一个 url。</p></li>
<li><p><code>eval-source-map</code> 这个就是把 <code>eval</code> 的 sourceURL 换成了完整 souremap 信息的 DataUrl</p></li>
<li><p><code>cheap-source-map</code> 不包含列信息,不包含 loader 的 sourcemap,(譬如 babel 的 sourcemap)</p></li>
<li><p><code>cheap-module-source-map</code> 不包含列信息,同时 loader 的 sourcemap 也被简化为只包含对应行的。最终的 sourcemap 只有一份,它是 webpack 对 loader 生成的 sourcemap 进行简化,然后再次生成的。</p></li>
</ul>
<p>webpack 不仅支持这 7 种,而且它们还是可以任意组合的,就如文档所说,你可以设置 souremap 选项为 <code>cheap-module-inline-source-map</code>。</p>
<hr>
<p><strong>这么多模式,到底该用哪个?</strong></p>
<p><code>cheap-module-eval-source-map</code> 绝大多数情况下都会是最好的选择,这也是下版本 webpack 的默认选项。</p>
<p>相关解释:</p>
<ol>
<li><p>大部分情况我们调试并不关心列信息,而且就算 sourcemap 没有列,有些浏览器引擎(例如 v8) 也会给出列信息,所以我们使用 cheap 模式可以大幅提高 souremap 生成的效率。</p></li>
<li><p>使用 eval 方式可大幅提高持续构建效率,参考 <a href="https://link.segmentfault.com/?enc=088sE8P7tc4RzYKgNYy1PQ%3D%3D.uUWRXpuRBQiu4n2x6JhD2Q%2FwuPyKJmFUsdoRgbiMdvFKmA2lzDr3WQPRgKZoMWUyr122kVHgYBnoTilqNJ72TA%3D%3D" rel="nofollow">webapck devtool 文档</a> 下方速度对比表格,这对经常需要边改边调的前端开发而言,非常重要!</p></li>
<li><p>使用 module 可支持 babel 这种预编译工具(在 webpack 里做为 loader 使用)。</p></li>
<li><p><code>eval-source-map</code> 使用 DataUrl 本身包含完整 sourcemap 信息,并不需要像 <code>sourceURL</code> 那样,浏览器需要发送一个完整请求去获取 sourcemap 文件,这会略微提高点效率</p></li>
</ol>
<hr>
<p><strong>现实很残忍</strong></p>
<p>大部分浏览器对于 sourcemap 的支持都非常有限,支持最好的 V8 也仅限于在 debugger 里面支持 sourcemap,对于错误的调用栈,我们看到的结果还是各种指向 bundle.js (除非是使用 eval + sourceURL 的方式)。</p>
<p>所以我做了 <a href="https://link.segmentfault.com/?enc=itJ0UqeFTR9KkmMxSF3ILA%3D%3D.wBb%2BkjdPUJGWuQRyHJUDWG%2B5vUI4UodKJW4SqAKbuhF2zEjVX%2BvCq9CczsrFDw9c" rel="nofollow">stack-source-map</a> 这个项目,它能让 Chrome 的 error stack 支持所有的 webpack 打包模式,这样测试工具报错时,你就能第一时间找到错误处,后续还在考虑支持 vim 的 quickfix 列表,这样一旦出错,开发者就可以直接在编辑器内跳转到错误代码处。</p>
<p>如果您发现这个工具有什么问题,欢迎任何形式 <a href="https://link.segmentfault.com/?enc=UIezB9LKCCi2XFxZnCFZ5A%3D%3D.WrvUbtxX%2Fi098WRI6p27JTp4uz983qMQ%2Bn0buEIKb%2BTinnlN5GBuH1ujjqBYGe8pRWpnrPtmmnYjI3WU%2BXNogg%3D%3D" rel="nofollow">反馈</a></p>
<hr>
<p>其实只要你了解 sourcemap 相关知识就会发现官方文档并不难懂,只是表述的比较简约,而且正是因为有了灵活的配置方式,我们才能在不同场景下获得最佳的效果。</p>
<p><em>本文同步发布与 [<a href="https://link.segmentfault.com/?enc=fKvE1koq5qZJirDwoWn1UA%3D%3D.gQfZw11KOgZenGY6Lb7XjjMW8E82g6RA7TJKd6mIBo9CQTplX92aX49gZeMyWzwW" rel="nofollow">https://chemzqm.me/webpack-sourcemap/</a>]</em></p>
搜索体验改进尝试
https://segmentfault.com/a/1190000004173673
2015-12-20T23:46:41+08:00
2015-12-20T23:46:41+08:00
chemzqm
https://segmentfault.com/u/chemzqm
0
<p>包含工具:git,ag,bash,vim</p>
<h3>增强版 git grep</h3>
<ul>
<li><p><code>--no-index --exclude-standard</code> 支持搜索未加入版本控制但是忽略 gitignore 的文件</p></li>
<li><p>通过 sed awk 的处理让 git grep 支持显示列,后面会需要</p></li>
</ul>
<pre><code class="bash">#! /bin/bash
git --no-pager grep --no-color --no-index --exclude-standard -n $1 | while read git_grep; do
file_and_line=$(echo "$git_grep" | cut -d : -f 1,2)
match=$(echo "$git_grep" | sed 's/[^:]*:[^:]*:\(.*\)/\1/')
column=$(echo "$match" | awk "{print index(\$0, \"$1\")}")
echo "$file_and_line:$column:$match"
done</code></pre>
<p>保存为 <code>grepprg</code></p>
<h3>让 vim 使用 grepprg 做 grep</h3>
<pre><code class="vim"> set grepprg=grepprg\ $*
set grepformat=%f:%l:%c:%m</code></pre>
<h3>让 vim 使用 ag 做 grep</h3>
<pre><code class="vim"> set grepprg=ag\ --vimgrep\ $*
set grepformat=%f:%l:%c:%m</code></pre>
<h3>通过映射命令灵活切换 ag 和 grep</h3>
<pre><code class="vim">nnoremap <leader>ag :call <SID>SwitchGrepCmd()<cr>
let g:g:grep_using_git = 0
function! s:SwitchGrepCmd()
if g:grep_using_git
set grepprg=ag\ --vimgrep\ $*
let g:grep_using_git = 0
echohl Identifier | echon 'grep by ag' | echohl None
else
set grepprg=grepprg\ $*
let g:grep_using_git = 1
echohl Identifier | echon 'grep by git' | echohl None
endif
endfunction</code></pre>
<h3>添加快捷命令搜索完成时自动弹出 quickfix 列表</h3>
<p>用自定义 <code>Ag</code> 命令执行 vim grep</p>
<pre><code class="vim">command! -nargs=+ -bar -complete=file Ag silent! grep! <args>|cwindow|redraw!</code></pre>
<h3>自定义操作命令快速搜索</h3>
<p>可以减少搜索所需输入</p>
<pre><code class="vim">vnoremap <leader>g :<C-u>call <SID>GrepFromSelected(visualmode())<cr>
nnoremap <leader>g :<C-u>set operatorfunc=<SID>GrepFromSelected<cr>g@
function! s:GrepFromSelected(type)
let saved_unnamed_register = @@
if a:type ==# 'v'
normal! `<v`>y
let g:grep_word = @@
elseif a:type ==# 'char'
normal! `[v`]y
let g:grep_word = '\b' . @@ . '\b'
else
return
endif
silent execute "Ag '" . g:grep_word ."'"
let @@ = saved_unnamed_register
endfunction</code></pre>
<ul>
<li><p><code><leader>giw</code> 可搜索光标下单词</p></li>
<li><p><code>v***<leader>g</code> 可搜索选中字符串</p></li>
</ul>
<h3>使用 Unite 控制 quickfix 列表</h3>
<p>首先需要安装 <a href="https://link.segmentfault.com/?enc=9f7EZ%2B0CpARxXCXtwh%2BRUQ%3D%3D.gnYliotCd7wKPeg5x1iHULB8YVVPWmOp73UjWdWb8P3gi9HGg1tWMVvud7KeTnqh" rel="nofollow">unite-location</a> 插件</p>
<p>修改自定义 <code>Ag</code> 命令为:</p>
<pre><code class="vim">command! -nargs=+ -bar -complete=file Ag silent! grep! <args>|execute "Unite -buffer-name=quickfix quickfix"</code></pre>
<p>让 Unite 的 quickfix 列表选中后不要退出</p>
<pre><code class="vim">call unite#custom#profile('quickfix', 'context', {
\ 'no_quit': 1
\ })</code></pre>
<h2>添加映射快速控制 Unite 列表</h2>
<pre><code class="vim">function! s:ToggleUnite()
for i in range(1, winnr('$'))
let name = bufname(winbufnr(i))
if match(name, '^\[unite\]') == 0
UniteClose
return
endif
endfor
UniteResume
endfunction
" <space>u 显示/关闭列表
nmap <silent> <space>u :call <SID>ToggleUnite()<cr>
" [count]<space>j/[count]<space>k 在上/下 count(默认为 1) 个列表项上执行默认操作,对于 quickfix 来说就是跳转
nmap <space>j :<C-u>call <SID>Jump(v:count1, 'Next')<cr>
nmap <space>k :<C-u>call <SID>Jump(v:count1, 'Previous')<cr>
function! s:Jump(count, dir)
execute a:count . 'Unite' . a:dir
endfunction</code></pre>
<p>简单些可以直接配置 unite 的 grep 使用 ag 命令,我放弃那种用法是因为不想再为它做单独的搜索设置,另外<a href="https://link.segmentfault.com/?enc=Hon2PuoYV2gxWHQfk9mFVw%3D%3D.SGf104l7pdbcjauv4l%2Bfd5EeyghLNz3Pr1dNhPEtFCJ%2F7xHWhczu8pPMU5ywFOTA" rel="nofollow">vim-qargs</a> 这样的插件来对搜索结果做快速替换等操作。</p>
常用移动端开发组件
https://segmentfault.com/a/1190000003942054
2015-11-02T19:28:25+08:00
2015-11-02T19:28:25+08:00
chemzqm
https://segmentfault.com/u/chemzqm
2
<p>今天把之前的移动端组件迁移到了 npm ,支持使用 webpack 或者 brwoserify 来构建。</p>
<p>介绍下常用组件,如果你喜欢轻量灵活的移动端解决方案不妨看看,如果是依赖框架开发的话,还是最好使用框架提供的功能。</p>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=PLmFMbJaUzZqEt7ZZojdIQ%3D%3D.Nc0NpqMj3jsDdb%2B%2F47r%2Bg3oOx6sQJl2PxPPwgaqXwZ0u2TxjxUG1657LYK7QZlaA" rel="nofollow">tap-event</a> 正确的 tap event</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=7vvIfFm8T%2FRGjrhQB%2FO%2FKA%3D%3D.dxIlNOpbwpNpFon8ntrN3RUrIfuX34eEawocoyKW9yml0g1Frju%2BAJW4pSDogGvL" rel="nofollow">component-tap</a> 支持 unbind 的 tap,但是没有函数不能代理事件</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=HIOAJsEAhDs6s%2B2MtXVOaw%3D%3D.Hfk1YgLSOL0WfAcwxauyO5hk69qHnvDTruqPkKPXoK8DTtP6AjAepkCupbPz%2BlaS" rel="nofollow">component-raf</a> 兼容的 request animation frame</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=lBswUBOV%2BGATcXJKBLc6Pw%3D%3D.146yi%2F1P6k88rRZjIXgc%2BYOT7scR2vZ0lqo5Uc9KTCLgFbm%2Fbjz5qXpYx2VRe8zK" rel="nofollow">ease-component</a> 提供缓动函数 <a href="https://link.segmentfault.com/?enc=cG212PX2oZ5RNL3TFn3xVQ%3D%3D.lJWPxszKoH1LEyNF2q33t9UIFFICoQ81JsHVbligIuU%3D" rel="nofollow">http://easings.net/</a> 实现</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=BoXQ%2BNlkVYGV28q2cjAmAw%3D%3D.5TcTanj6bHQYJdLqjElRrmol4f4JOH5d5lGy2vzEezxhmWTjuI1fRyMT5AfvH6Ny" rel="nofollow">component-tween</a> 使用 ease 实现高性能js动画的基础库</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=E10xVBkS9bJEr9BtpU6jXQ%3D%3D.n6eD4sBue27Et1OJ3rxvliUqTI52D9ruECgpdZ9U%2BwJRweDNa3DoxkfHBU%2FCz6pE" rel="nofollow">swipe</a> 提供滑动切换效果</p></li>
</ul>
<p>下面是我自己的:</p>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=LOnU%2FjMDgGhk%2FBpn2r2sdg%3D%3D.1aUIGmCLDUMvlOxUzidn1PFHhtL8CFqG8TsO8T6CpStJ2PpY4OIMHgt75%2Ft4hLRm" rel="nofollow">component-scrollfix</a> 解决 ios 上页面整体拖动的问题</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=aAd%2F5jSdN%2FPCEA7P8e1bGA%3D%3D.63WBUtmaF5CLVvnetN8yNKXoSa2kL8v%2FCjfWwGQOgncXg%2FOGY%2Buh2FT%2BpgG5o7Ea" rel="nofollow">component-livechart</a> 自适应的动态 canvas 图表库</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=ulA%2BHkFIj3NOrTEA0y1zQA%3D%3D.mydsnIXknPnbOuy8iFLQj2hEq0Ozxjzf2mGbk8XYNK8sot6EhfmokubhZPrh0h%2F6" rel="nofollow">iscroll-component</a> 模拟滚动效果,跟<a href="https://link.segmentfault.com/?enc=lwsY103M1hAlY3YxrZMMLw%3D%3D.rrjBoKAijkg5HoRj2xBYMMJKmkR1N%2FgEar7wPyOVwm8%3D" rel="nofollow">iscroll</a> 类似,但是更快</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=nJuGRc9ak%2BX7lSMinxgdMw%3D%3D.b8LduNleSnKdO%2FtWyn7fmHIsCmXo%2Fj%2BuYN3zLKwjfAlJ%2BsUBoOTo3cl2szEibMYx" rel="nofollow">pull-to-refresh</a> 兼容 ios 和 android 的下拉刷新库</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=BwBjLyPW5O6DbtK2%2F0HRmQ%3D%3D.xY8AdjYLgQnb3Dbk%2ByHWDjV%2B7TOB2nIWSExCgMnTGjs%3D" rel="nofollow">more-mobile</a> 实现列表加载更多的功能</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=4WZiGEICFKlVZaqrDPIzpw%3D%3D.EEXPBDOQnqxFQ1BeQ4pAvIelmUdW37R8fdmkZ5dO2cRWgt1DrjbaV9YlgNq6ZkzT" rel="nofollow">component-notice</a> 简洁的 notice 实现</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=VoKpQOcqwdhvjqWWEvXTzQ%3D%3D.8IbCv8P5XfuEESpm4xbLe01UTNR4ea7uYsLBCAVi%2F8kHQUwRRi6U9VxRRcVNGblt" rel="nofollow">component-overlay</a> 简洁的 overlay 实现</p></li>
</ul>
<p>以上组件都由一些基础的 component 组件实现,最后项目的大小要相比使用框架小很多,因为代码简单,维护起来也相对节省时间。</p>
基于tern和webpack的前端代码智能感知
https://segmentfault.com/a/1190000003887409
2015-10-21T17:13:40+08:00
2015-10-21T17:13:40+08:00
chemzqm
https://segmentfault.com/u/chemzqm
1
<p>代码智能感知一直被各种 IDE 工具所津津乐道,基于智能感知我们可以实现一些极大提高开发效率的事:</p>
<ul>
<li><p>方法、变量名智能补全</p></li>
<li><p>点击跳转到定义处</p></li>
<li><p>参数返回值类型提示</p></li>
<li><p>变量重命名</p></li>
</ul>
<p><a href="https://link.segmentfault.com/?enc=I9svtpdZG3uqW7IOjYaOuA%3D%3D.uHA%2BOlcFqepV8pVCrNOJCWuimshFmNuWml8xDQ%2BD%2BRc%3D" rel="nofollow">Tern</a> 是一个完全开源免费的javascript解析引擎,同时它也为很多文本编辑都提供了 <a href="https://link.segmentfault.com/?enc=7NzQpWgYYJ67%2FJQMzgkTlg%3D%3D.80qh2TMytpj%2FhcP7YzLkNf5iZ2seObaG%2Fa5KcImq60WwdPVNjVDEfTDp0oxBRXTx" rel="nofollow">插件</a>,它不止能实现上面那些功能,而且还内置了 node文档 和 mdn 的链接,通过插件我们最快的看到官方的文档。通过这个视频 <a href="https://link.segmentfault.com/?enc=uG1EMBbL55NRNAIsuo4luA%3D%3D.ss1HoHlvJmbLAbSP%2BczzeOfSHH55qeVYLIMyojiXUsg%3D" rel="nofollow">https://vimeo.com/67215272</a> 可以更直观的了解。</p>
<p>记得1年多以前我在 vim 下用 tern 的时候经常出现编辑器卡死、服务器没有响应等状况,所以就放弃了,但是最近使用感觉完全不同了,以前糟糕的情况再没有出现。</p>
<p>Tern 可以理解浏览器的API,通过设置插件理解 node 模块的 API, 但是它对于 webpack 这种前端构建工具是无知的,所以我们需要一种方法让它也能理解 webpack 依赖解析逻辑。为此,我实现一个 Tern 插件 <a href="https://gist.github.com/chemzqm/7ceee3040f1377a534e6">https://gist.github.com/chemzqm/7ceee3040f1377a534e6</a>,它使用和 webpack 相同 <a href="https://link.segmentfault.com/?enc=F1%2BAEhnm6iIN0pfzZnygzQ%3D%3D.4d4LvQw50%2FWV3dHk35poueBjh4SfjC4dy7iBCaOYYx73UFFGIrsvK%2F5LoU%2BF41y7" rel="nofollow">enhanced-resolve</a> 来对资源进行解析,通过使用<a href="https://link.segmentfault.com/?enc=9G2QxNyRUaZuZl%2BfGGUk4w%3D%3D.fsO%2BoFyrW36VEA8UZNSO1bQPnSveP7JMtwEYX5y%2FnjfJfAx%2BiNQ3X%2BfnSSvO465k" rel="nofollow">neocomplete</a>和一点简单的配置,我的 vim 就可以像 IDE 一样对代码进行智能感知了。</p>
<p>下面是智能补全和函数定义提示的截图,overlay 是 npm 上面的 component 组件 <a href="https://link.segmentfault.com/?enc=NOCpbU097LG8gZexZj6j7g%3D%3D.vaoC2arFmcvJ4fITnVGk5ZYAsPLmN5L3UzBbK8rlFPnZir5tT3cQLUz%2FWw3HbHAo" rel="nofollow">overlay-component</a><br><img src="/img/bVqtqb" alt="clipboard.png" title="clipboard.png"></p>
<p>下面是函数跳转的动态图,可以看到 Emitter 组件是在 package.json 里面通过 browser 属性链接的 component-emitter<br><img src="/img/bVqtrm" alt="animated.gif" title="animated.gif"></p>
<p>最后分享下 <a href="https://link.segmentfault.com/?enc=z2D%2BG3UhPlcN2vhGcYPZiw%3D%3D.lSIy%2FPct4jfYUvAcKsQqmUgYoWuBUNcLQjHD0tZzCbI2idINqy8EYnhpkTTGFgND" rel="nofollow">tern-for-vim</a> 的配置:</p>
<pre><code>" 鼠标停留在方法内时显示参数提示
let g:tern_show_argument_hints = 'on_hold'
" 补全时显示函数类型定义
let g:tern_show_signature_in_pum = 1
" 跳转到浏览器
nnoremap <leader>tb :TernDocBrowse<cr>
" 显示变量定义
nnoremap <leader>tt :TernType<cr>
" 跳转到定义处
nnoremap <leader>tf :TernDef<cr>
" 显示文档
nnoremap <leader>td :TernDoc<cr>
" 预览窗口显示定义处代码
nnoremap <leader>tp :TernDefPreview<cr>
" 变量重命名
nnoremap <leader>tr :TernRename<cr>
" location 列表显示全部引用行
nnoremap <leader>ts :TernRefs<cr></code></pre>
<p>以后不用再费劲的找文档或者翻代码了:)</p>
基于gulp,webpack实现灵活的 livereload 服务
https://segmentfault.com/a/1190000003881087
2015-10-20T13:11:37+08:00
2015-10-20T13:11:37+08:00
chemzqm
https://segmentfault.com/u/chemzqm
1
<p>webpack 提供了 webpack-dev-server 可以实现高效的页面自动刷新,但是这个 server 有一点局限性:</p>
<ul>
<li><p>虽然可以做为 server 中间件使用,但本身不提供其它资源的静态服务</p></li>
<li><p>对于打包文件以外的变化它是无知的,也就无法去刷新</p></li>
<li><p>如果是自己的页面需要引入 script,修改配置 entry,自己另外提供文件服务,步骤繁琐</p></li>
</ul>
<p>现在假设我们有自己的 html 页面,页面引入了本地的 css,但是 js 是由 webpack 打包的,需要实现文件修改自动刷新的页面的话我们需要以下几个模块:</p>
<ul>
<li><p>文件监视 - gulp.watch()</p></li>
<li><p>静态资源服务 - gulp-serve 模块</p></li>
<li><p>livereload服务 (负责接受变化请求并通知页面刷新) - gulp-livereload 模块</p></li>
<li><p>自动添加 livereload 前端脚本 - connect-livereload 模块 (或者使用livereload浏览器插件)</p></li>
</ul>
<p>代码如下:</p>
<pre><code>// npm i gulp-serve gulp-livereload webpack gulp-util gulp connect-livereload -D
var serve = require('gulp-serve')
var livereload = require('gulp-livereload')
var webpack = require('webpack')
var gutil = require('gulp-util')
var gulp = require('gulp')
var webpackConfig = require('./webpack.config.js')
var inject = require('connect-livereload')()
var path = require('path')
var myConfig = Object.create(webpackConfig)
// for debugging
myConfig.devtool = 'sourcemap'
myConfig.debug = true
var paths = {
scripts: ['index.js'],
// file list for watching
asserts: ['*.css', 'index.html']
}
gulp.task('default', ['build-dev'])
gulp.task('build-dev', ['webpack:build-dev', 'serve'], function () {
livereload.listen({
start: true
})
// build js files on change
gulp.watch(paths.scripts, ['webpack:build-dev'])
var watcher = gulp.watch(paths.asserts)
watcher.on('change', function (e) {
livereload.changed(e.path)
})
})
// static server
gulp.task('serve', serve({
root: [__dirname],
// inject livereload script ot html
middleware: inject
}))
var devCompiler = webpack(myConfig)
var outputFile = path.resolve(myConfig.output.path, myConfig.output.filename)
gulp.task('webpack:build-dev', function (callback) {
devCompiler.run(function (err, stats) {
if (err) throw new gutil.pluginError('webpack:build-dev', err) //eslint-disable-line
gutil.log('[webpack:build-dev]', stats.toString({
colors: true
}))
livereload.changed(outputFile)
callback()
})
})</code></pre>
<p>这种做法最大的好处就是 html 和 css 不用都让 webpack 去处理也能做到自动刷新,但是相比 webpack-dev-server, 它是JS全部重新打包的,所以性能会差很多,此时我们可以使用 webpack-dev-server 实现打包文件的监听,同时让原来后台提供其它文件服务。假设后台使用 3000 端口,我们使用 webpack-dev-server inline模式需要如下配置:</p>
<pre><code>gulp.task('webpack:dev-server', function () {
var devServerConfig = Object.create(myConfig)
// webpack need this to send request to webpack-dev-server
devServerConfig.output.publicPath = 'http://localhost:8080/'
devServerConfig.plugins = devServerConfig.plugins || []
devServerConfig.plugins.push(new webpack.HotModuleReplacementPlugin())
// inline mode
devServerConfig.entry.unshift('webpack-dev-server/client?http://localhost:8080', 'webpack/hot/dev-server')
var compiler = webpack(devServerConfig)
new WebpackDevServer(compiler, {
contentBase: 'http://localhost:3000/',
// Set this as true if you want to access dev server from arbitrary url.
// This is handy if you are using a html5 router.
historyApiFallback: false,
// Don't forget this for dev-server
publicPath: '/build/',
lazy: false,
hot: true
}).listen(8080, 'localhost', function (err) {
if (err) throw new gutil.PluginError('webpack-dev-server', err)
// Server listening
gutil.log('[webpack-dev-server]', 'http://localhost:8080/')
})
})</code></pre>
<p><code>gulp webpack:dev-server</code> 现在可以启动服务了,但同时请求 bundle.js script也要修改为:</p>
<pre><code><script src="http://localhost:8080/bundle.js" type="text/javascript" charset="utf-8"></script></code></pre>
<p>这样 webpack-dev-server 才能进行热加载。</p>
<p>还有一种看起来更好的方式是使用 proxy 来代理,此时你访问 webpack-dev-server时它无法处理的请求都会转发给你的后台服务,配置起来相比上面要简单一些:</p>
<ul>
<li><p>html 页面无需修改</p></li>
<li><p>config.output.publicPath 使用原来的相对路径</p></li>
<li><p>webpack-dev-server 配置 proxy 选项</p></li>
</ul>
<pre><code>gulp.task('webpack:dev-server', function () {
var devServerConfig = Object.create(myConfig)
// webpack need this to send request to webpack-dev-server
devServerConfig.plugins = devServerConfig.plugins || []
devServerConfig.plugins.push(new webpack.HotModuleReplacementPlugin())
// inline mode
devServerConfig.entry.unshift('webpack-dev-server/client?http://localhost:8080', 'webpack/hot/dev-server')
var compiler = webpack(devServerConfig)
new WebpackDevServer(compiler, {
// contentBase: {target: 'http://localhost:3000/'},
// Set this as true if you want to access dev server from arbitrary url.
// This is handy if you are using a html5 router.
historyApiFallback: false,
proxy: {
'*': 'http://localhost:3000'
},
publicPath: '/build/',
lazy: false,
hot: true
}).listen(8080, 'localhost', function (err) {
if (err) throw new gutil.PluginError('webpack-dev-server', err)
// Server listening
gutil.log('[webpack-dev-server]', 'http://localhost:8080/')
})
})</code></pre>
<p>这么做只需要访问 webpack-dev-server 就可以直接访问页面,其它设备通过 ip 也能直接访问到,免去了修改的麻烦。其实 webpack-dev-server 如果可以直接添加静态文件服务的功能,我们有时就不必折腾的这么麻烦了,大概是作者不想破坏它功能上的简洁以及避免滥用导致不良的后果吧。</p>
现代JS代码测试流程
https://segmentfault.com/a/1190000003869696
2015-10-17T10:51:57+08:00
2015-10-17T10:51:57+08:00
chemzqm
https://segmentfault.com/u/chemzqm
1
<p>有质量的代码是要有代码测试来保证的,本文就大致谈谈现在我们是如何实现使用es6甚至es7(async/await)标准的JS代码的高效测试的。</p>
<p>我们会用到以下工具</p>
<ul>
<li><p>webpack 前端打包工具</p></li>
<li><p>mocha 测试框架</p></li>
<li><p>babel 编译es6/7代码</p></li>
<li><p>karma 命令行下执行浏览器测试</p></li>
<li><p>istanbul 代码覆盖率工具</p></li>
<li><p>coveralls 代码覆盖统计服务商</p></li>
<li><p>travis-ci 自动集成</p></li>
</ul>
<hr>
<p>安装mocha</p>
<pre><code>npm i -D mocha</code></pre>
<p>代码没有后端依赖可直接测试:</p>
<pre><code>mocha test.js
</code></pre>
<p>使用浏览器/node不支持的es6需要babel来编译代码</p>
<pre><code>npm i babel -D</code></pre>
<p>启用es6,这种方式新版本的babel会自动启动async/await</p>
<pre><code>mocha --compilers js:babel/register test.js</code></pre>
<p>如果用到了前端的代码,就需要webpack这样的工具</p>
<pre><code>npm i webpack mocha-loader -D
webpack 'mocha!./test.js' testBundle.js
//index.html 包含 testBUndle.js
open index.html</code></pre>
<p>需要babel和webpack一起使用的话,先装loader</p>
<pre><code>npm i babel-loader -D</code></pre>
<p>命令行的 babel-loader 只会编译当前的文件,如需编译其它文件需要在 require 处添加才能编译成功,如果需要启用 async/await 还需要加入 runtime 选项,例如:<br><code>require('babel?optional[]=runtime!./index.js')</code></p>
<p>如果每次require都去加就显得太繁琐了,所以建议使用 webpack.config.js 添加</p>
<pre><code class="js">module.exports = {
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?optional[]=runtime'
}
]
}
}</code></pre>
<p>到 webpack.config.js, 现在可以在命令行和require上省去babel了。</p>
<p>这种方式每次修改都要手动去编译再刷新浏览器,比较繁琐,所以推荐使用webpack-dev-server实时调试。</p>
<pre><code>npm i webpack-dev-server -D
webpack-dev-server 'mocha!./test.js' --hot --inline --output-filename test.js
open http://localhost:8080/test</code></pre>
<p>每次代码修改页面都会自动刷新,更强大的是它只会执行你修改了代码的测试用例 (简直神器)。</p>
<hr>
<p>现在可以用浏览器测试我们的es6代码了,下一步是在命令行也能测试浏览器跑的代码,这里我们用 karma 以启用 firefox 为例,安装依赖:</p>
<pre><code>npm i -D karma karma-cli karma-firefox-launcher karma-mocha karma-webpack
</code></pre>
<p>然后在 karma.conf.js 文件中加入:</p>
<pre><code class="js">// Karma configuration
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['mocha'],
files: [ 'test/test.js' ],
exclude: [ ],
preprocessors: {
'test/test.js': ['webpack']
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Firefox'],
webpack: {
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?optional[]=runtime'
}
]
}
},
webpackMiddleware: {
noInfo: true
},
singleRun: true
})
}</code></pre>
<p>执行</p>
<pre><code>./node_modules/.bin/karma start
</code></pre>
<p>开始测试</p>
<hr>
<p>光有测试还不足以让你信服你的代码,我们还需要测试覆盖,这里以使用 istanbul 为例:</p>
<pre><code>npm i -D istanbul babel-istanbul-instrumenter-loader karma-coverage
</code></pre>
<p>修改 karma.conf.js</p>
<pre><code class="js">...
reporters: ['progress', 'coverage']
...
webpack: {
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?optional[]=runtime'
}
],
preLoaders: [
// instrument only testing sources with Istanbul
{
test: /\.js$/,
include: path.resolve('lib/'),
loader: 'babel-istanbul-instrumenter'
}
]
}
}
....
coverageReporter: {
dir: 'coverage/',
reporters: [
{type: text}
{ type: 'html', subdir: 'html' },
{ type: 'lcovonly', subdir: 'lcov' },
{ type: 'cobertura', subdir: 'cobertura' }
]
}</code></pre>
<p>这里生成了3中报告,html是给人看的(浏览器打开 coverage/html/index.html 可以直接查看),另外两种便于其它的工具分析。</p>
<p>以上工具的用法搭配多样,这里只是粗浅介绍,真正使用请认真阅读相关文档。</p>
<hr>
<p>最后是如何持续集成,ravis-ci 和 coveralls.io 可以很好的合作解决这个问题。</p>
<p>你需要先到 <a href="https://link.segmentfault.com/?enc=S%2BnhEXNRNc1Sf5ZiIDNyoQ%3D%3D.miNmqxkSSu5k1xqMC2203TZMygJPd%2B7nUFlvFrQpVds%3D" rel="nofollow">travis-ci</a> 注册,然后通过 github 授权,启用相关的项目。在项目中添加 .travis.yml 配置文件</p>
<pre><code class="yml">language: node_js
node_js:
- '0.12'
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start</code></pre>
<p>推送到 github 之后,每次提交 travis-ci 都会先执行 <code>npm install</code> 然后执行 <code>npm test</code> 中的命令进行测试 (还有其它自定义的设置,本文略过)</p>
<p>使用 coveralls.io 需要先安装一个包来发送数据</p>
<pre><code>npm i -D coveralls</code></pre>
<p>然后也是注册、授权、启用相关项目,最后是做一个测试命令把测试报告发给它,以 Makefile 为例:</p>
<pre><code>test-coveralls:
@echo TRAVIS_JOB_ID $(TRAVIS_JOB_ID)
@node_modules/.bin/karma start --single-run && \
cat ./coverage/lcov/lcov.info | ./node_modules/coveralls/bin/coveralls.js</code></pre>
<p>修改<code>package.json</code>的测试命令</p>
<pre><code>scripts: {
test: make test-coveralls
}</code></pre>
<p>这样 travis-ci 每次执行完测试都会把 lcov.info 里的测试结果通过 coveralls.js 发送给 coveralls.io 网站。</p>
<p>做为示范,这是一个现实的组件项目 <a href="https://link.segmentfault.com/?enc=xqbOeeie5ZTdksiOxcsk%2FA%3D%3D.QGz%2FLM%2BFc%2FFPg4HR%2F0ydY5T9wTdowcXaH9Ci2TioU2qBqjgsXglU4%2F%2FIr0KAmgSs" rel="nofollow">chemzqm/exgrid</a>,还有一个样板项目 <a href="https://link.segmentfault.com/?enc=KkPwLEJHCPsOHnUKcgiv%2FA%3D%3D.dQKTa%2B6wFXq8JVkfPfWsWkPP68i2n4WRsSEBAEnwgHr1qE2PtRWSyZSaRVdVsYOc" rel="nofollow">chemzqm/es6-test-sample</a>。</p>
<p>做为一个有追求的开发者,向着100%的覆盖率努力吧!</p>
奥法控蓝
https://segmentfault.com/a/1190000002484678
2015-01-13T21:38:16+08:00
2015-01-13T21:38:16+08:00
chemzqm
https://segmentfault.com/u/chemzqm
0
<p>奥冲监视的wa字符串,中间显示蓝量百分比 蓝高于86%或者奥冲cd小于15s显示<br>
dG0GeaqEq16HOY(j1ggH0LermkcvNIqzvIqmlqOLfrv7sKYVebzyqYXGultv6zIkttu01OeBdK6BIKACGKZjknprqDpLk2NiGdQu1cPKEiiQlQsSrrsojiyLqyMqKBcIStL4NIcdLqwQivpfzQeCvru9vrIU8isZvKWIaL2R0FjYGvvhwWIvQ0JPutwL6YO2SQ4Ze1OHOCAswTiqVweLzRIBRu2nu)MIHROlnc1YbEUqtNQRRW2fv9DL04vjDlqXfDfkTv6U0DfkzpIUztGV6LEgyBxzW6V4IPrO)6V(R)hJD4O)tZkd0)D0)KJvUiZkdeh25jaNbWcr9NeDbjKKeLrkVKUeK2VSxmnc9x)1F9)ySdh9FAwzG0w4Q(VJ(NCSYfzwzaKcxfh25jaNbWcr9NeDbjKKeLrkVKUeK2VSxmnc9x)1F9hW(aG1)Oduy50EmGTGloSjj3nSqu)rJcvImnRmiHMMvgiTfUkMgbJV1i0i0i0i0i0i0i0ikbpJNNi7nvWav2Cqd1l0VOTyjl0OY92hyY0sjhOKLzqfknzGnJ9PzLbsMjdcUbukb2PKdFYLIEWhXstgyZyFAwzqjOGU0aZ5zSxkmCtPrKLMMvg0DlX5zSxsKzLbLgrwAGvETwAAwzGdFYDbvPrKLMmWMX(0SYGAT0iYstZkdC4tUwRxczSsgzExY9w6yc3vOeWC4kuABCCvfQxVKhom2RqPTXXvvOE9sGGnxHsBJJRQq961l9yWoPqoUlVOkDRIZtao5b7KflLe7sawwzWcWNCjLTbxsDxkPjoXjnrr(s3QycoW2UkpxRLc33kxzWHJKduYYmiwHUGUcLCGswMbvOKdFYLIEWhXsdmNNXEPWWnLa8wqHvyzjZKbb3akLW7uIZZyVeKnMZTzfR)IagzahJ6V40KphXkfh7Y5zSlcWpQRLirxqcPsJilfN85Se24ATeG3ckSclxcTLsXXUCEg7045rDTKeK3tcsKW(0fknIS0aR8AT0aR8sZtaodknISeG3ckSclxRLIt(CwcBCxq)IYc66LgyhOKLzqfkfN85Se24UG(fLf01RxV0Q62rwxY9w6Agppr2BQGbAlOYIg1BUS5Yc1B2udDFGjtlLgyhOKLzqfknIS0aR8AT0aR8sZtaodQxcp2eakzzge7YBPBvCEcWHGTbxccyNbpbBofqW2GJPGM85iThyhWSK1ti6LIEWozrrgf2lb5llcxsVEPyP30qLw20GcMmHroNfoRfOsDP8DbDMVOQ3c</p>
<p>唤醒字符串</p>
<p>daendaqEjy)IqByHulziQPSKKrPKWPusXKwsv3suzxIiddIoguwMe9mLW0evDnHABkrFtuACIuNtempfsUNsbhuHyHIIhQuQlQu0gfjnsLeDsHuZuPQDcPFkezOkulvPqpv1uLQRQuXCvQ0Ej)vbdwe1HL0IfcpwcnzP0LbBwk(SIgTiXPrSALuA2uCBKA3O8BOA4i56uA5cEoQMovNEjP2oe(UsjJxi5YlPYctDDIE0mhcn1IWUrxeNX39uGXmGwDBaggBdtqNRxMeYKsiP05YNBUigsioDwDA9w9w11Bj81AzfDccqz0TmpqMtiOUoNcmg0kniuSsKXyY19azoHG66womea6kHrytLrpa0vcJWMd4uqO64b9rTbDabWC9TXXnT4BXsm5Xb8za48etEfNcmM1Oha6kHrytDSL6wgGayUE1646womyzexz0TCyGtbgdALgugDVafOZ9QB56CkWyqR0GqXkrgJjxpfGmtXf6IsDdETvD9aUbuxN2ACI6YLR7vdWC11PTgNOUC56HArqDDARXjQlxUC9amj4SEbkqNueNPtA1x3Qw)QhDK1RTTeNGZQMbpqMtiWvxOyQR7bYCcb119cuGo3RULRBzacG56vRJRdiaMRpsrNGZsm5D41imGo3gbGay(4a0qIs)J3C)EDofymOvAqOyLiJX0TCyGtbgdALgugDofymr3YvxUo3gbGay(TnnKO0h2EK3hEFKn21TCyWYiUYOBzexNYulabDwLg4QlxVfIW200lqb6RCZ9JtDN9Y1TmpqMtiOUoNcmg0kniuSsKXyYLlxFlsRNIqxuQxisnnCWvQ5YhNDrclJXIrUOSCz60Qjx(86mlDpqMtiWfAPEurQPHdUsnxwmKzlJXYgJLoB6LXlvtU8XY1riuS8LiLlb</p>
Android 2.x webview兼容注意事项
https://segmentfault.com/a/1190000000602846
2014-07-12T16:09:52+08:00
2014-07-12T16:09:52+08:00
chemzqm
https://segmentfault.com/u/chemzqm
1
<h3>不可使用 javascript 方法列表:</h3>
<ol>
<li><p><code>Element.prototype.classList</code> 只能通过正则低效的操作 className <a href="https://link.segmentfault.com/?enc=ttt%2F%2FqDtjs3KBqmALn975g%3D%3D.svzXOGjBPkjX1Yc8SkF32YBYzL3hu3HEK7jQx1YlsMTCz2goJML%2FqLfAEnw2ndN9" rel="nofollow">Can I use</a></p></li>
<li><p><code>Element.prototype.dataset</code> 只能通过 <code>getAttribute</code> 和 <code>setAttribute</code> 获取设置 <code>data-*</code>属性 <a href="https://link.segmentfault.com/?enc=MMY5Qb49LK%2FOpHbxbCVqmg%3D%3D.YRB8YMSpSXEkkjD99F6maSF5n0qet0be5ZT54wRgXPXjKwnmiP5kJxhWdDn6vGbK" rel="nofollow">Can I use</a></p></li>
<li><p><code>Function.prototype.bind</code> 可通过添加 <code>polyfill</code> 方式使用 <a href="https://link.segmentfault.com/?enc=z3a6wuxRXudtxAlut%2BihNA%3D%3D.ncAcNSyiW89%2BvOIEDBY7OPSKVjWOEaIZyMgGM83aMgW12WPHxE4loSv2OsQdSfiWOEh5w7dQDaY947d%2FE2A2CHBvcDxbN%2BWkj9WOIahR6%2Fa2WGvnOWzLbe1%2B9DhRJoRpzr1yCCjtLkk1GoTtNmXOww%3D%3D" rel="nofollow">MDN</a></p></li>
<li><p><code>html5 history</code> API 包括 <code>history.pushState</code> 、<code>history.replaceStat</code>、<code>popstate </code> 都能使用</p></li>
</ol>
<h3>有问题的 css 声明列表</h3>
<ol>
<li><p><code>position: fixed</code> 某些 webview 上元素会跟着手势滚动后再弹回到固定位置,如果是<code>body</code>直接子节点则没有此问题,建议是不用 <code>position:fixed</code></p></li>
<li><p><code>overflow: auto</code> 和 <code>overflow: scroll</code>, 不会实现可滚动元素,建议是只让<code>body</code>去滚动,万不得已使用 <code>iscroll</code></p></li>
<li><p><code>animation-fill-mode</code> 在 android 2.3之前不被支持</p></li>
</ol>
<h3>需要 sdk 设置开启才能使用的方法</h3>
<ol>
<li><p><code>localStorage</code> API</p></li>
<li><p><code>alert</code> <code>confirm</code></p></li>
</ol>
前端动画效果实现的简单比较
https://segmentfault.com/a/1190000000594926
2014-07-03T17:11:12+08:00
2014-07-03T17:11:12+08:00
chemzqm
https://segmentfault.com/u/chemzqm
10
<p>合适的动画不仅更能吸引人们的眼球,也能让你的应用体验更为流畅,而将动画的效果做到极致,才能让用户感到使用你的应用是一种享受,而不是觉得生硬和枯燥。本文旨在探讨各种前端动画效果实现方式的异同,具体应用中如何实现,以及实现的效果还得根据自身的情况进行考量。</p>
<h2>Javscript 动画</h2>
<p>因为没有其它可用的实现方式,最初的前端动画都是JS来实现,实现上就是通过一个定时器<code>setInterval</code> 每隔一定时间来改变元素的样式,动画结束时<code>clearInterval</code>即可。早期的类库包括 jquery、prototype、mootools 等等都是这种方式。</p>
<p>尽管这种方式动画的可控性很强,但是问题也很明显:</p>
<ul>
<li>性能不佳,因为需要不断获取和修改Dom的布局,所以导致了大量页面重排(repaint)</li>
<li>缺乏标准,不同的库使用了不同的API,导致即使是简单的动画也有各不相同的实现方式,调整起来比较耗时</li>
<li>带宽消耗,相对丰富的动画库代码量都很大,结果就是增加了http请求的大小,降低了页面的载入时间</li>
</ul>
<h2>css3 动画</h2>
<p>css3 加了两种动画的实现方式,一种是 <code>transition</code>, 一种是 <code>animation</code>。</p>
<p>transition 包含4种属性:<code>transition-delay</code> <code>transition-duration</code> <code>transition-property</code> <code>transition-timing-function</code>,对应动画的4种属性: 延迟、持续时间、对应css属性和缓动函数,</p>
<p>transform 包含7种属性:<code>animation-name</code> <code>animation-duration</code> <code>animation-timing-function</code> <code>animation-delay</code> <code>animation-direction</code> <code>animation-iteration-count</code> <code>animation-fill-mode</code> <code>animation-play-state</code>,它们可以定义动画名称,持续时间,缓动函数,动画延迟,动画方向,重复次数,填充模式。</p>
<p>总的来书,css 动画相比与JS更轻量,性能更好,更易于实现,同时也不必担心缺乏标准和增加带宽消耗的问题。animation 相比 transtion 使用起来更为复杂,但也提供了更多的控制,其中最重要的就是 frame 的支持,不过通过一些简单的JS库,例如 TJ 的 <a rel="nofollow" href="https://github.com/visionmedia/move.js">move.js</a>, 我们也能在JS中通过 transition 来实现更复杂的控制。</p>
<h2>html5 动画</h2>
<p>html5 定义了三种绘图的方式,<code>canvas</code> <code>svg</code> <code>webgl</code>,其中<code>svg</code>做为一种可缩放矢量图形的实现是基于xml标签定义的,它有专门的 <code>animate</code> 标签来定义动画。而为 <code>canvas</code> 或者 <code>webgl</code> 实现动画则需要通过 <a rel="nofollow" href="https://developer.mozilla.org/en/docs/Web/API/window.requestAnimationFrame">requestAnimationFrame</a> (简称 raf) 来定期刷新画布。 尽管说 raf 的方式会让代码变得复杂,但是因为不需要那么多的文档对象(通常浏览器只需要管理一个画布),它的性能也好很多,尤其是在内存吃紧的移动端上面。</p>
<p>通过新的 raf 接口以及一些改进手段我们也可以用JS来实现高性能的动画。主要手段如下:<br>
1. 减少 Dom 样式属性查询,Dom 样式属性的查询会导致页面重排,从而消耗性能,通过将属性保存在JS变量中就可以避免在动画时去查询,从而减少卡顿。<br>
2. 使用性能更好的 css <code>transform</code> 替代改变绝对定位元素的定位属性<br>
3. 在移动设备上使用 3d 硬件加速,最简单办法就是添加 <code>-webkit-transform: translateZ(0)</code>,原因是移动端的显卡有很强的图形渲染能力,而每个应用的 webview 内存却是极其有限的。</p>
<p>使用JS的动画可控性更好,比如说通过事件捕捉可以很容易的设定不同参数。这方面做的最全面的有 <a rel="nofollow" href="http://julian.com/research/velocity/">Velocity.js</a>,它可做为jquery 插件使用,对于初学者很友好,我个人会倾向于使用 <a rel="nofollow" href="https://github.com/sole/tween.js/">tween.js</a>,因为它只专注与动画的数值计算,不仅仅只适用与 Dom 操作。</p>
移动端的touch事件处理
https://segmentfault.com/a/1190000000515816
2014-05-21T19:38:25+08:00
2014-05-21T19:38:25+08:00
chemzqm
https://segmentfault.com/u/chemzqm
19
<p>简要的探讨一下移动端 touch 事件处理几个坑,以及相应的简单处理方法。</p>
<h2>click 穿透</h2>
<p>假设有个弹出层,上面有个关闭的按钮支持 touchend 触发后关闭,若正好下方有个元素支持 click 事件,在弹出层关闭后将会在下方元素触发 click 事件。这种效果肯定不是我们需要的,而且我们无法确定合适会在上方出现一个支持 touch 的弹出层,所以我认为最好的处理方式是禁用所有元素的 click 事件,相比 click 需要长达 1s 的触发时间,使用 touchend 可以获得更好的体验。</p>
<h2>tap 事件的判定</h2>
<p>一个正确的 tap 事件应当满足一下条件:</p>
<ol>
<li>用户手指从屏幕移开时触发</li>
<li>不能在用户移动手指时触发(防止和滚动、拖拽事件的冲突)</li>
<li>多个手指同时触摸屏幕时不能触发</li>
<li>不应该触发 click 事件</li>
</ol>
<p>具体实现代码可以参考 <a rel="nofollow" href="https://github.com/component/tap-event/blob/master/index.js">tap-event</a>。</p>
<h2>使用原生的滚动事件</h2>
<p>Android 4.0 以下是不支持原生的 webview 滚动的,所以只能使用 <a rel="nofollow" href="https://github.com/cubiq/iscroll">iscroll</a> 之类的工具来模拟元素滚动。它的缺点就是有些过于的复杂,所以我还是会在条件允许的情况下使用原生的滚动。</p>
<p>启用原生滚动只需要给外层元素加上样式 <code>-webkit-overflow-scrolling: touch;</code> 即可,如果你的监听函数比较占用资源我们可以通过一个简单的 buffer 函数来限制它的触发间隔,例如:</p>
<pre><code class="lang-js">function buffer(fn, ms) {
var timeout;
return function() {
if (timeout) return;
var args = arguments;
timeout = setTimeout(function() {
timeout = null;
fn.apply(null, args);
}, ms);
}
}
document.querySelector('.scrollable').onscroll = buffer(onScroll, 100);
</code></pre>
<p>另外的建议就是不要在可滚动元素上使用阴影样式(text-shadow 和 box-shadow),因为它们非常影响性能,而且看上去也不怎么美观。</p>
<p>还有需要注意的是如果你需要启用<code>apple-mobile-web-app-capable</code>, 注意将<code>apple-mobile-web-app-status-bar-style</code>设置为<code>black-translucent</code>,否则会出现还差 22 像素滚动不到头的坑爹 bug。</p>
<h2>禁用页面整体拖动</h2>
<p>IOS下默认情况下用户的拖动操作在scroll滚到头以后会导致整体页面的滚动,一种方式是禁用掉 document 的 touchmove 原生触发</p>
<pre><code class="lang-js">document.addEventListener('touchmove', function(e) {
e.preventDefault();
});
</code></pre>
<p>此时原生的滚动是无法工作的,解决办法就是禁用滚动元素的 touchmove 事件冒泡</p>
<pre><code class="lang-js">scrollable.addEventListener('touchmove', function (e) {
e.stopPropagation();
});
</code></pre>
<p>另一种方式是判定滚动元素滚到头之后禁用掉默认的处理</p>
<pre><code class="lang-js">var el = document.querySelector('.scrollable');
var sy = 0;
events.bind(el, 'touchstart', function (e) {
sy = e.pageY;
})
events.bind(el, 'touchmove', function (e) {
var down = (e.pageY - sy > 0);
//top
if (down && el.scrollTop <= 0) {
e.preventDefault();
}
//bottom
if (!down && el.scrollTop >= el.scrollHeight - el.clientHeight) {
e.preventDefault();
}
})
</code></pre>
<p>我个人倾向于第二种方案,因为如果单纯的禁用 document 的 touchmove 监听,会导致一些处理的失效,比如说上面提到的 <code>tap-event</code> 模块。</p>
<h2>拖动方向与距离</h2>
<p>通过 event 的 pageX 和 pageY 属性即可计算,可参考 <a rel="nofollow" href="https://github.com/component/hammer.js/blob/master/index.js#L127">hammer.js</a></p>
移动端页面快速调试
https://segmentfault.com/a/1190000000515642
2014-05-21T17:06:07+08:00
2014-05-21T17:06:07+08:00
chemzqm
https://segmentfault.com/u/chemzqm
0
<p>本文试图探讨一些移动端页面的快速调试方法,如果你有更好的方案,不妨留言告诉我。</p>
<h3>快速打开指定url</h3>
<p>最开始我的方法是使用二维码,但是这种方案流程过于繁琐,要生成url,打开浏览器,然后打开扫码工具对准图片扫码打开。后来的办法是 imessage 发送 url 到手机上再点击链接打开,这种办法会快一点,但还是没有办法支持webview。</p>
<p>我最后想到的方案是用node做一个跳转的服务,代码很简单</p>
<pre><code class="lang-js">#! /usr/bin/env node
var http = require('http');
var exec = require('child_process').exec;
var url = process.argv[2];
var port = process.argv[3]|| 3000;
http.createServer(function (req, res) {
exec('ifconfig | grep 192.168 | awk \'{print $2}\'', function(err, stdout) {
if (err) throw err;
var ip = stdout.replace('\n', '');
url = url.replace('localhost', ip);
res.setHeader('Location', url);
res.statusCode = 302;
res.end();
})
}).listen(port);
//console.log('server started at ' + port);
</code></pre>
<p>保存为 <code>redirect</code> 然后执行 <code>redirect http://localhost...</code> 启动服务。这样做的好处就是可以把移动端需要请求的url固定(当然本机的ip最好是固定的),调试移动端只需要访问固定的url <code>http://192.168.xx.xx:3000</code>,webview里面可以把这个url写死,<strong>每次变更调试的url也只需要重新启动服务,然后刷新客户端。</strong></p>
<h2>页面自动刷新</h2>
<p>因为 webview 不像浏览器有刷新按钮,所以一开始我们的办法是在让原生的客户端加个refresh的按钮,二来每次要点击刷新也是比较耗时的。最后办法是使用 tiny-lr ,我在之前的<a rel="nofollow" href="http://blog.segmentfault.com/chemzqm/1190000000512702">文章</a>有过介绍,其使用方法分三步</p>
<p>1 启动 tiny-lr 服务,命令 <code>tiny-lr</code> (-h 参看帮助)</p>
<p>2.html页面嵌入与后端通讯的代码</p>
<pre><code> <!-- livereload snippet -->
<script>
document.write('<script src="http://'
+ (location.host || 'localhost').split(':')[0]
+ ':35729/livereload.js?snipver=1"><\/script>')
</script>
</code></pre>
<p>3.发送h ttp 请求到 tiny-lr 刷新浏览器</p>
<pre><code> curl -X POST http://localhost:35729/changed -d '{ "files": "build.css" }'
</code></pre>
<p>这种方式不仅方便,而且也不会限制你的后台语言或者代码构建方式。</p>
<h2>样式调试和控制台</h2>
<p>我使用的工具是<a rel="nofollow" href="http://people.apache.org/~pmuellr/weinre/docs/latest/Home.html">weinre</a>, 执行命令</p>
<pre><code> npm install -g weinre
</code></pre>
<p>即可安装,使用方法也是3步,</p>
<p>1.启动 weinre 服务</p>
<pre><code> weinre --httpPort 8081 --boundHost `ifconfig | grep 192.168.199 | awk '{print $2}'`
</code></pre>
<p>(boundHost 参数执行了一个获取本机 ip 的子任务,我也不知这玩意为啥要绑定 ip 才能用)<br>
建议将此命令写个<code>alias</code>到你的 shell profile <code>.zshrc</code>或者 <code>.profile</code> 之类的文件内简化调用</p>
<p>2.页面内嵌入 weinre 服务通讯的前端代码</p>
<pre><code class="lang-html"><script>
var host = location.host || '';
if (/192\.168/.test(host)) {
document.write('<script src="http://'
+ host.split(':')[0]
+ ':8081/target/target-script.js"><\/script>');
}
</script>
</code></pre>
<p>判断了一下host,这样就算不小心提交到线上也不可能会报错。</p>
<p>3.浏览器打开<code>http://192.168.199.88:8081/client/#anonymous</code>(换成你的本机ip),就可以查看样式以及<code>console</code>输出了。</p>
grunt快速上手
https://segmentfault.com/a/1190000000513028
2014-05-19T19:22:32+08:00
2014-05-19T19:22:32+08:00
chemzqm
https://segmentfault.com/u/chemzqm
2
<p>这篇文章的目标是帮助大家快速上手grunt,适用的grunt版本为0.4.x,本文只是大致介绍,如果想做深入了解请阅读<a rel="nofollow" href="http://gruntjs.com/getting-started">grunt官方文档</a>。</p>
<h2>安装grunt命令行工具</h2>
<p>首先确保你的node版本在0.8以上(暂时不建议适用0.10.0),命令:</p>
<pre><code class="lang-bash">node -v
</code></pre>
<p>然后安装grunt命令行工具grunt-cli</p>
<pre><code class="lang-bash">npm install -g grunt-cli
</code></pre>
<p>可能需要前面加上sudo(例如 OSX, *nix)。</p>
<p>如果你只前装过grunt的老版本的话则需要卸载:</p>
<pre><code class="lang-bash">npm uninstall -g grunt
</code></pre>
<h2>安装grunt模板</h2>
<p>grunt模板的作用是帮助你生成初始的Gruntfile.js文件,当然你也可以直接把其它项目的Gruntfile.js文件拷贝过来使用。</p>
<p>首先要安装命令行工具grunt-init:</p>
<pre><code class="lang-bash">npm install -g grunt-init
</code></pre>
<p>然后安装模板,目前有三种模板是由grunt官方做维护的,还有别的可在github上找到,或者你自己实现一个。 官方模板的安装命令如下:</p>
<pre><code class="lang-bash">git clone git://github.com/gruntjs/grunt-init-gruntfile.git $HOME/.grunt-init/
git clone git://github.com/gruntjs/grunt-init-jquery.git $HOME/.grunt-init/
git clone git://github.com/gruntjs/grunt-init-node.git $HOME/.grunt-init/
</code></pre>
<p>三种分别对应默认grunt模板,jquery插件的grunt模板,node包的grunt模板。</p>
<p>然后就可以适用grunt-init命令来初始化你的Gruntfile.js文件了,例如你要安装默认模板:</p>
<pre><code class="lang-bash">grunt-init grunt-init-gruntfile #最后一个参数也可以是模板所在的文件夹
</code></pre>
<p>它会问你一些问题,然后根据你的答案创建当前项目的Gruntfile.js文件。</p>
<h2>安装所需node包</h2>
<p>首先是创建package.json文件(npm的<a rel="nofollow" href="https://npmjs.org/doc/json.html">包管理配置文件</a>)。<br>
你可以使用<code>npm init</code>命令创建这个文件或者直接拷贝其它项目的。</p>
<p>然后在项目的根目录下安装你开发依赖的各种grunt包,首先是grunt:</p>
<pre><code class="lang-bash">npm install grunt --save-dev
</code></pre>
<p><code>--save-dev</code>可以将你所安装的包自动保存到package.json文件中的<code>devDependencies</code>属性中去,如果你的项目使用时(不仅仅是开发)也需要用到某个包,<br>
你应该使用<code>--save</code>将其保存在<code>dependcies</code>属性中。</p>
<p>你肯定要用到需要其它grunt模块来帮你完成构建任务,这时你就可以用这种方法把他们都加到项目中,例如最常用的<code>concat</code> <code>jshint</code> <code>uglify</code>模块:</p>
<pre><code class="lang-bash">npm install grunt-contrib-concat --save-dev
npm install grunt-contrib-jshint --save-dev
npm install grunt-contrib-uglify --save-dev
</code></pre>
<p>还有种方式就是先把依赖的包写到package.json中的<code>devDependencies</code>中去,然后直接使用<code>npm nistall</code>来安装。</p>
<h2>编辑Gruntfile.js文件使之工作</h2>
<p>我们以本站当前使用的Gruntfile.js为例来了解一下grunt设置中最重要的部分。</p>
<pre><code class="lang-javascript">var path = require('path');
var snippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
// Task configuration.
concat: {
dist: {
src: ['javascripts/common.js','javascripts/respond.js'],
dest: 'javascripts/main.js'
}
},
uglify: {
options: {
banner: '/*script for site: http://chemzqm.me*/'
},
dist: {
src: '<%= concat.dist.dest %>',
dest: 'javascripts/main.min.js'
}
},
cssmin: {
compress:{
files:{
"stylesheets/styles.min.css":["stylesheets/styles.css"]
}
}
},
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
unused: false,
boss: true,
eqnull: true,
browser: true,
node: true,
globals: {
jQuery: true
}
},
all: ['javascripts/common.js','Gruntfile.js']
},
regarde: {
livereload: {
files: ['_posts/*.md', 'javascripts/common.js', 'stylesheets/styles.css', '*.html'],
tasks: ['default', 'livereload']
}
},
connect: {
livereload: {
options: {
port: 8000,
middleware: function(connect, options) {
return [snippet, connect.static(path.resolve(process.cwd(), '_site'))];
}
}
}
},
bgShell:{
jekyll:{
cmd: 'jekyll'
}
}
});
// These plugins provide necessary tasks.
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-regarde');
grunt.loadNpmTasks('grunt-bg-shell');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-livereload');
// Default task.
grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'cssmin', 'bgShell:jekyll']);
// start livereload server
grunt.registerTask('server', ['livereload-start', 'connect', 'regarde']);
};
</code></pre>
<ul>
<li><p><code>module.exports</code>指向调用该模块时将返回的对象,而当你使用<code>grunt</code>命令的时候这个名为<code>Gruntfile.js</code>的文件就会被<code>grunt</code>找到(0.4.0以上会找Gruntfile.js,而之前是<code>grunt.js</code>,具体的找法也很简单,就是从运行命令的目录开始一直往上找,直到找到为止),然后以<code>grunt</code>对象为参数执行该模块返回的函数。</p></li>
<li>
<p><code>grunt.initConfig</code>方法接受一个Plain Object做为参数,该对象的每个属性名代表一个顶层task名字,而值则是具体的配置。配置任务时要注意不能把子任务名配置成任务要读取的配置属性,例如uglify需要options做为配置属性,这时就不能把子任务名也设置成options。如果任务是<code>multitask</code>则可以为一个任务配置多个子任务,例如配置多个合并文件的任务:</p>
<pre><code>concat: {
dist: {
src: ['javascripts/common.js','javascripts/respond.js'],
dest: 'javascripts/main.js'
},
dist2:{
src: ['javascripts/*.js'],
dest:'javascripts/all.js'
}
},
</code></pre>
<p>这样你就可以通过<code>grunt concat</code>来顺序执行两个合并任务,或者<code>grunt concat:dist</code>只执行<code>dist</code>任务。上面用到的任务都是<code>multitask</code>,具体实践的时候从文档或者源码都可以判断。此外,grunt还支持文件名的模式匹配和模板替换的功能,例如匹配所有js文件可以用<code>*.js</code>,想引用<code>concat:dist</code>任务的<code>dist</code>属性可以用<code><%= concat.dist.dest %></code>, 详细的说明还请阅读<a rel="nofollow" href="https://github.com/gruntjs/grunt/wiki/Configuring-tasks">官方文档</a>。</p>
</li>
<li><p><code>grunt.loadNpmTasks</code>负责载入npm模块定义的任务,grunt 0.4.0之后没有了核心任务,所以每个任务都需要开发者写代码载入(还有一个<code>grunt.task.loadTasks</code>方法用来加载指定目录task文件,具体参阅<a rel="nofollow" href="https://github.com/gruntjs/grunt/wiki/grunt.task">task官方文档</a>)。</p></li>
<li><p><code>grunt.registerTask</code>用来注册新的任务,它最简单的形式如下:<br>
grunt.task.registerTask(taskName, taskList)<br>
名为<code>taskname</code>的任务出发后,<code>tasklist</code>数组(grunt 0.4.0之前是空格分格的字符串)中的任务将被顺序执行, 比较特殊的是名为<code>default</code>的任务,它将在命令行输入<code>grunt</code>后被调用。<code>grunt.registerTask</code>和<code>grunt.registerMultiTask</code>还可通过传入函数的方式创建新的任务,具体参阅<a rel="nofollow" href="https://github.com/gruntjs/grunt/wiki/Creating-tasks">创建task文档</a>。</p></li>
</ul>
<p>最后重点介绍一下<a rel="nofollow" href="https://github.com/gruntjs/grunt-contrib-livereload">livereload模块</a>,这个模块分两个任务<code>liveload-start</code>和<code>liveload</code> ,前一个任务启动livereload服务,后一个任务负责通知浏览器进行刷新。livereload本身不提供文件的web服务,所以我们需要connect任务插件来提供。然后通过<code>require('grune-contrib-livereload/lib/utils').livereloadSnippet</code>获取到<code>livereload</code>插件提供的中间件函数(实质上就是参数为<code>req, res, next</code>的函数),然后配置到<code>connect</code>的<code>middleware</code>参数中,这时请求connect服务<br>
(<a rel="nofollow" href="http://localhost:8000">http://localhost:8000</a>) 得到的html就会被动态的加入负责与<code>livereload</code>服务端通讯的一段代码。<code>grunt-regarde</code>模块负责监控文件变化(不用<code>grunt-watch</code>是因为那个获得不了变化的文件名),这里我们根据需要配置成每当文件变化后就执行<code>default</code>任务和<code>livereload</code>任务。最后,配置</p>
<pre><code>grunt.registerTask('server', ['livereload-start', 'connect', 'regarde']);
</code></pre>
<p>就可以通过<code>grunt server</code>命令同时启用liveload, connect, 和regard服务(顺序可以任意)</p>
<p>grunt已经开始被越来越多的知名项目所使用了(例如jquery和AngularJS),而且它的任务插件也在不断的扩展之中(例如coffeescript, jade, stylus)。虽说相比Makefile笨拙了一些,但更容易满足团队中各种操作系统下开发的需要</p>
前端模块化实践(约定篇)
https://segmentfault.com/a/1190000000513018
2014-05-19T19:09:47+08:00
2014-05-19T19:09:47+08:00
chemzqm
https://segmentfault.com/u/chemzqm
1
<p>做为<a rel="nofollow" href="http://en.wikipedia.org/wiki/Worse_is_better">新泽西风格</a>的追随者,我们追求设计最简化,最大限度减少任何冗余的设计负担。</p>
<p>因为我们的开发基于 component,所以这里要谈的部分内容多数只适用于使用 <a rel="nofollow" href="https://github.com/component/component">Component</a> 的模块化开发。</p>
<h3>项目结构</h3>
<ol>
<li>
<p>扁平化,项目直接使用的模块都在<code>client</code>文件夹下面,远程模块都在<code>components</code>文件夹下面。示例如下:</p>
<p><img src="/images/folders.png" alt="folder.png"></p>
</li>
<li><p>每个页面一个文件夹,如果需要多页面的话。</p></li>
</ol>
<h3>命名</h3>
<ol>
<li><p>view模块后面应带有<code>-view</code>后缀,其它不限制(因为暂时还没有很多)。</p></li>
<li><p>每个模块下的模板文件统一命名为<code>template.html</code>,css文件命名为<code>style.css</code>,主要js文件命名为<code>index.js</code>,这样可省去大部分起名的苦恼,使用快捷键找到文件也容易的多。</p></li>
<li>
<p>css样式命名。因为组件很可能在一个项目中可以多次出现,所以统一采用className做为一个顶级父类,这个className应当就是组件的名称,这样完全可以避免冲突 ,例如组件<code>data-view</code>的样式大致如下:</p>
<pre><code class="lang-css">.data-view .list {
}
.data-view .list li {
}
.data-view .input {
}
</code></pre>
</li>
</ol>
<h3>接口</h3>
<ol>
<li>
<p>避免选项式接口,使用链式接口,因为选项式接口往往需要猜测参数的默认值,造成不必要的理解成本,另外链式的接口也更容易掌握必填的参数。</p>
<pre><code class="lang-js">//不好的方式
var dialog = new Dialog({
el: el,
closable: true,
modal: true,
resiable: true
})
//好的方式
var dailog = new Dialog(el)
.closable()
.modal()
.resiable()
</code></pre>
</li>
<li><p>以DOM为参数的接口应当统一使用原生的Element对象(或者统一jQuery对象,不过要注意component官方组件都是原生的Element对象做参数)</p></li>
<li><p>严格限定可变参数接口,最多一个可变参数。可变参数确实可以增强调用的灵活性,但是过于灵活会破坏自身的简单性,得不偿失。</p></li>
</ol>
<h3>调用</h3>
<ol>
<li><p>禁止使用jQuery的Ajax方法,统一使用<a rel="nofollow" href="https://github.com/visionmedia/superagent">superagent</a>,因为jQuey的Ajax内部过于复杂化,API设计也不够简单。</p></li>
<li><p>需要同时发起多个请求时才可以使用Promise模式,但是应当避免多处使用相同promise对象,因为这会很容易带来不必要的耦合,并增大调试的难度,正确的做法应当是传递回调函数。</p></li>
<li><p>父view可以获取子view的属性,但是必须避免直接改变子view的属性,应当由子view暴露方法给父view调用,这样做的目的是提高模块的内聚性。</p></li>
<li><p>任何view模块都必须实现<code>remove</code>方法,它的作用是销毁自身dom,并移除全部事件。</p></li>
</ol>
<p>下一篇将介绍一些我最常用的轻量级工具,它们不限于模块化开发使用,但是在模块化的开发中可以更大程度发挥它们的效力。</p>
前端模块化实践(方法篇)
https://segmentfault.com/a/1190000000513015
2014-05-19T19:07:58+08:00
2014-05-19T19:07:58+08:00
chemzqm
https://segmentfault.com/u/chemzqm
3
<p>从 jQuery 的一统江湖再到 Angular 的异常火爆,我们可以看到工程师们对于开发效率孜孜不倦的追求,大家都渴望着能够快速从这个充满纷争的互联网时代脱颖而出。尽管说“天下武功,唯快不破”,但是我们不该忘记一个事实,那就是一个长期产品的维护成本要远远高于开发的成本。相比与单纯的代码量的减少,良好的整体设计更应该足够简单,容易被他人完全正确的理解、可以快速的定位和处理bug、能够轻易的改变结构或者添加功能、支持轻松的编写测试。</p>
<p>下文中我会以 <a rel="nofollow" href="https://github.com/component/component">Component</a> 为例,来讲讲我们是如何基于 <a rel="nofollow" href="http://en.wikipedia.org/wiki/KISS_principle">KISS原则</a> 来实现整体的前端模块化设计(以下简称前端模块化设计),从而帮助产品更好的应对将来代码维护和升级需要的。</p>
<p>先来明确下前端模块化这个概念,以及它所针对的问题。</p>
<h3>什么是前端模块化设计</h3>
<ul>
<li><p>它所针对的不仅仅是常用前端小组件的设计,而是整个产品的所有前端部分的全部模块化。</p></li>
<li><p>不仅仅是JS代码的模块化,一个模块可以包含js、css、html以及图片等其它资源。</p></li>
<li><p>不同模块间的依赖和调用关系都应该是清晰简单的,事件流程也应该是可以被容易读懂和理解的。</p></li>
<li><p>前后端要做到完全分离,后端不再提供渲染功能,仅仅做为数据的提供者(rest接口),否则无法确保调整的可靠性。</p></li>
<li><p>它只针对应用型产品,对于追求性能和 SEO 的展示性页面来说,前端的功能是有限的,用不着做整体的模块化设计。</p></li>
<li><p>它不是企业级组件,所谓企业级组件一般都是前后端一体的、跟具体某个业务紧密联系的,而前端模块化只包括前端,它只对应一个产品的一个部分,不一定对应某个具体业务(不同业务同时依赖一个模块是很常见的)。</p></li>
</ul>
<h3>为什么要做前端模块化设计</h3>
<ol>
<li><p>我们的产品需要针对不同客户进行界面定制调整,它需要足够灵活应对各种组件上的变化。</p></li>
<li><p>我们希望我们的产品能够带给用户 app 一样的流畅访问体验,而不是不停的重复刷新页面。</p></li>
<li><p>我们的产品需要支持不同的设备,我们希望大部分模块是可重用的,而不必再去重新开发。</p></li>
<li><p>我们的前后端是分离的,前端额外还承载了所有的渲染、路由、以及组件生命周期管理的逻辑,我们希望它简单可靠。</p></li>
</ol>
<h3>前端模块化设计的方法</h3>
<ul>
<li>
<p><strong>MVX模式</strong>。我们的界面只由三种简单的模块构成,M是Model,V是View,X就是其它。</p>
<p><img src="/upload/fa24a5ef862d7c48dcc89aa93d009796.png" alt="mvx.png"></p>
<ol>
<li><p>Model,负责定义各种模型,以及向后端发送对应的资源增删改查请求并完成对应模型的转化。</p></li>
<li><p>View,使用模板并提供渲染方法、绑定对应Model、创建并管理子view、响应各种事件、发送事件,以上只有第一点是必须的。</p></li>
<li><p>其它包含通用的组件,例如:tip、menu、dialog等等,以及各种全局通用的模块,提供例如错误提示、多语言、保存获取当前用户信息等功能。</p></li>
</ol>
</li>
<li>
<p><strong>严格依赖关系</strong>。简单来说就是层次化,具体包含以下几点:</p>
<ol>
<li><p>通用组件和全局模块绝不应当依赖任何Model和View。</p></li>
<li><p>Model只能依赖全局模块和通用组件。</p></li>
<li><p>View可以依赖任何组件,但它绝不可以依赖除了自身创建的子View之外的任何View组件(保证独立性)。</p></li>
</ol>
<p>以下是一个真实项目中的模块依赖关系,可以看到清晰的层次关系<br><img src="http://chemzqm.me/upload/2d4c0679d94dc81e9798da39c0ac5741.png" alt="out.png"><br><em>(其中user、client、board、widget是model)</em></p>
</li>
<li><p><strong>合理划分界面模块</strong>。可以参考 <a rel="nofollow" href="http://docs.sencha.com/extjs/4.2.1/#!/guide/mvc_pt1">Sencha的这篇文章</a>,简言之就是不能太大,太大承载的逻辑太多,也不能太小,太小则过于碎片化,增大管理的难度。我们的做法是先按逻辑上互相独立的大区域做成最底层的view,逻辑上应当独立的块做成独立的view,对于中间的view层则最开始尽量少做。如果发现由于业务的变更中间的view层承接了太多功能再进行拆分,因为view只会被它的上一层创建者调用,而且它依赖的模块因为使用 CMD 也能非常清楚的看到,所以重构起来还是非常容易的。</p></li>
<li><p><strong>可控的消息机制</strong>。父view可以直接调用子view的方法,但是子view发生变化,需要通知其它的view该怎么办呢?我们不喜欢 pagebus 那种不可控的全局消息模式,所以我们采用了类似浏览器事件冒泡的简单的事件冒泡方式,而且限制了子view的事件只能由上一层接受,如果还需要向上传递则让上一层继续发送事件,实践中这种需求还是非常少的。</p></li>
</ul>
<h3>代码示例</h3>
<ul>
<li>用户模块</li>
</ul>
<pre><code class="lang-js">var model = require('model');
/**
* User model.
*/
module.exports = model('User')
.attr('id')
.attr('name')
.attr('email')
</code></pre>
<ul>
<li>视图模块</li>
</ul>
<pre><code class="lang-js">var dom = require('dom');
var User = require('user');
var Emitter = require('emitter');
var template = require('./template');
//parent为渲染该view的dom节点
function UsersView(parent) {
var el = dom(template);
el.appendTo(parent);
this.el = el;
el.on('click', '.add', this.addUser.bind(this));
//load all users
User.all(function(err, users){
if(err) throw err;
users.each(function(user){
this.appendUser(user);
}.bind(this));
}.bind(this));
}
Emitter(UsersView.prototype);
UsersView.prototype.addUser = function(){
var user = new User({
name: this.el.find('[name="name"]').value(),
email: this.el.find('[name="email"]').value()
});
user.save(function(err, user){
if(err) throw err;
this.appendUser(user);
//向父层发送通知
this.emit('new', user);
}.bind(this));
}
UsersView.prototype.appendUser = function(user){
//渲染新用户,此处省略
}
module.exports = UsersView;
</code></pre>
<ul>
<li>父视图模块</li>
</ul>
<pre><code class="lang-js">var template = require('./template');
var dom = require('dom');
//模块名采用小写加中横线命名,因为文件夹采用小写命名更好
var UserView = require('user-view');
function MainView(parent) {
var el = dom(template);
el.appendTo(parent);
this.userView = new UserView(el.get(0));
//事件接受
this.userView.on('new', function(user){
console.log('new user ' + user.name);
}.bind(this));
}
</code></pre>
<p>通过遵循简单的方法和约定,我们可以做到不论是框架整体,还是具体到某个调用,某个流程都非常容易被开发者所理解,从而极大的提升了后期继续开发和维护的效率。</p>
<p>下一篇我会谈谈我们基于应用模块化所做的各种约定,它们可以让整个前端体系保持简单一致,有效的避免冲突,并且帮助我们快速定位和处理各种问题。</p>
那些年我们用的工具(Linux安装脚本,不定期更新)
https://segmentfault.com/a/1190000000513013
2014-05-19T19:06:30+08:00
2014-05-19T19:06:30+08:00
chemzqm
https://segmentfault.com/u/chemzqm
1
<p>介绍一下做为前端开发的我常用的命令行工具,node包,以及前端类库。说是Linux安装脚本,其实只适用于Debain(Ubuntu)系列, 因为我现在基本上只用Ubuntu,而且我也不喜欢把事情搞太复杂。</p>
<p>如果你某个工具感兴趣,还请到官网认真学习,这里就不多罗嗦了。还有要注意部分工具需要根据个人喜好进行配置(例如:vim, mutt, git,bash), 这些工具的配置我都会放到Ubuntu One中帮我做自动同步(方便重装系统,以及不同机器共享),工具设置非常重要,好的设置可以让你事半功倍,但是篇幅限制就不在这里介绍了。</p>
<pre><code class="lang-bash">#! /bin/bash
#mutt 最好用的电子邮件程序
sudo apt-get -y install msmtp-mta msmtp mutt getmail4 procmail lynx wv w3m
mkdir -p ~/Mail
(cd ~/Mail && touch inbox sent postponed)
touch ~/.msmtprc
chmod -v 600 ~/.msmtprc
chmod -v 700 ~/.getmail
#ssh 最常用的安全通信工具
sudo apt-get -y install ssh
mkdir -p ~/.ssh
touch ~/.ssh/config
chmod -v 600 ~/.ssh/config
cat << EOF > ~/.ssh/config
TCPKeepAlive=yes
ServerAliveInterval=60
ServerAliveCountMax=6
StrictHostKeyChecking=no
Compression=yes
ForwardAgent=yes
EOF
#node http://nodejs.org/ 稳定源 服务端的JS环境
sudo apt-get -y install python-software-properties
sudo add-apt-repository -y ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get -y install nodejs npm
#mongodb http://www.mongodb.org/ 稳定源 轻量级的nosql数据库
wget http://docs.mongodb.org/10gen-gpg-key.asc
sudo apt-key add 10gen-gpg-key.asc
sudo sh -c 'echo deb http://downloads-distro.mongodb.org/repo/debian-sysvinit dist 10gen > /etc/apt/sources.list.d/10gen.list'
sudo apt-get update
sudo apt-get -y install mongodb-10gen
#git 最强版本控制系统
sudo apt-get -y install git
git config --global user.name "chemzqm"
git config --global user.email "chemzqm@gmail.com"
rm -rf ~/.ssh
ssh-keygen -t rsa -C "chemzqm@gmail.com"
#将屏幕输出的key加入github
cat ~/.ssh/id_rsa.pub
read -p continue?
ssh -T git@github.com
#shutter 截图工具
sudo add-apt-repository ppa:shutter/ppa
sudo apt-get update
sudo apt-get install shutter
#tools
#vim Linux系统最常用编辑器,ubuntu默认带的是阉割版的vim来做为vi的替代品
#ctags 产生标记文件以帮助编辑器在源文件中定位对象
#xmlstarlet 编辑xml的命令行工具,自动化脚本有用
#curl 从url获取数据的强大自动化工具,支持多种协议
#axel 轻量的多线程下载工具
#imagemagick 用于创建、修改和显示图片的二进制工具包
#privoxy 代理工具,可以用来设置转发,修改请求头,改变响应内容等等
#colordiff 用来代替默认diff显示彩色结果,可做为svn的默认diff工具
#subversion SVN代码管理命令行工具
#pngcrush png图像优化工具,主要用于压缩
#jpegoptim jpeg图像压缩优化工具
#expect 对交互式应用提供进行可编程会话的工具,例如自动化需要密码的ssh登录
sudo apt-get -y install vim ctags dos2unix xmlstarlet curl axel imagemagick privoxy colordiff subversion pngcrush jpegoptim expect
sudo apt-get -y install phantomjs
sudo sh -c 'echo forward-socks5 / 127.0.0.1:8888 . >> /etc/privoxy/config'
#设置svn使用colordiff
mkdir -p ~/.subversion
cat << EOF > ~/.subversion/config
[helpers]
diff-cmd = colordiff
EOF
#phantomjs 基于webkit内核的无头浏览器,这里下的是32位的(默认包安装的版本太老了)
version=1.8.2
wget http://phantomjs.googlecode.com/files/phantomjs-${version}-linux-i686.tar.bz2
tar -jxf phantomjs-${version}-linux-i386.tar.bz2
mkdir -p ~/programs
mv phantomjs-${version}-linux-i686 ~/programs/phantomjs
cd ~/bin
ln -s ~/programs/phantomjs/bin/phantomjs
#apache + php + mysql 常用web服务组件
sudo apt-get -y install apache2 php5 libapache2-mod-php5 mysql-server libapache2-mod-auth-mysql php5-mysql apache2.2-common
sudo sh -c 'echo "<?php phpinfo(); ?>" > /var/www/info.php'
firefox http://localhost/info.php &
#sublime text 一款编辑器
sudo add-apt-repository -y ppa:webupd8team/sublime-text-2
sudo apt-get update
sudo apt-get -y install sublime-text
#node相关工具
mkdir -p ~/programs ~/bin
cd ~/programs
#node-dev 代替node命令调试服务,会监测源码改变自动重启服务
git clone git://github.com/fgnass/node-dev.git
(cd node-dev && npm install)
(cd ~/bin && ln -s ~/programs/node-dev/node-dev)
#mon https://github.com/visionmedia/mon 单线程监控程序, 轻量级C程序
git clone git://github.com/visionmedia/mon.git
(cd mon && sudo make install)
#mongroup https://github.com/jgallen23/mongroup 线程组监控程序
git clone git://github.com/jgallen23/mongroup.git
(cd mongroup && sudo make install)
#UglifyJS JS压缩美化必备,支持source-map
git clone git://github.com/mishoo/UglifyJS.git
(cd UglifyJS && sudo make install)
(cd ~/bin && ln -s ~/programs/UglifyJS/bin/uglifyjs)
#jshint JS代码检查工具
sudo npm install -g jshint
#coffee-script coffeescript编译器
sudo npm install -g coffee-script
#mocha http://visionmedia.github.com/mocha 简单、灵活、有趣的测试框架,支持node和浏览器环境
sudo npm install -g mocha
#docco http://jashkenas.github.com/docco/ 生成左右注释和代码左右对照的html页面,注释通过markdown解析器解析,源码通过Pygments高亮
sudo npm install -g docco
#grunt http://gruntjs.com/ 最流行的任务执行工具,包括jquery之内的很多框架和公司都在用了
sudo npm install -g grunt-cli
#queen http://queenjs.com/ 一个帮你把js代码推送到多个浏览器运行的工具,提供监控页面等辅助功能
sudo npm install -g queen
#trill http://thrilljs.com/ 利用queen帮你在多个浏览器同时跑测试
sudo npm install -g thrill
#component http://component.io 一个基于commonjs的模块化组建库
sudo npm install -g component
#sqwish https://github.com/ded/sqwish 一个简单的css压缩工具
sudo npm install -g sqwish
#jekyll http://jekyllrb.com/ 一个简单的,有blog意识的静态站点生成器,类似cms
sudo apt-get install ruby1.9.1-dev
sudo gem install jekyll
#rdiscount 一个解析markdown文本的ruby包
sudo gem install rdiscount
#vimpager http://www.vim.org/scripts/script.php?script_id=1723 使用vim来代替less做为分页器(主要是有语法高亮)
git clone git://github.com/rkitover/vimpager.git
(cd vimpager && sudo make install)
#node modules 这些是我常用的,所以放到home下面了
mkdir -p ~/modules
cd ~/modules
#jade http://github.com/visionmedia/jade#readme 基于node的一款简洁、快速、高效、强大、跨环境的模板引擎
git clone git://github.com/visionmedia/jade.git
(cd jade && npm install && cd ~/bin && ln -s ~/modules/jade/bin/jade)
#express http://expressjs.com/ 基于node的轻量级web应该开发框架
git clone git://github.com/visionmedia/express.git
(cd express && npm install && cd ~/bin && ln -s ~/modules/express/bin/express)
#sylus http://learnboost.github.com/stylus/ 提供更快,更便捷的css编写方式,比less更强
git clone git://github.com/LearnBoost/stylus.git
(cd stylus && npm install && cd ~/bin && ln -s ~/modules/stylus/bin/stylus)
#marked https://github.com:chemzqm/marked/ 只是代码高亮的(类似github)markdown解析器
git clone git@github.com:chemzqm/marked.git
(cd marked && npm install && cd ~/bin && ln -s ~/modules/marked/bin/marked)
#liveload https://github.com/chemzqm/liveload/ 监控源码变化自动更新浏览器的辅助工具
git clone git@github.com:chemzqm/liveload.git
(cd liveload && npm install && cd ~/bin && ln -s ~/modules/liveload/bin/liveload)
#前端框架和类库
mkdir -p ~/lib
cd ~/lib
#jquery 需安装grunt
git clone git://github.com/jquery/jquery.git
(cd jquery && npm install -d && grunt)
#bootstrap
git clone git://github.com/twitter/bootstrap.git
(cd bootstrap && npm install && make && make test)
#angular 需要grunt
git clone git://github.com/angular/angular.js.git
(cd angular.js && npm install && grunt)
#jasmine 测试工具
git clone git://github.com/pivotal/jasmine.git
git clone git://github.com/FortAwesome/Font-Awesome.git
#python tool
#pygments 语法高亮工具
sudo apt-get -y install python-setuptools
sudo easy_install pygments
sudo npm install -g pygments
sudo chown -R $USER:$USER ~/.npm ~/tmp
#增加文件监控数量,否则莫些方法可能报错,例如node中的:fs.watch
sudo sh -c 'echo fs.inotify.max_user_watches = 524288 > etc/sysctl.conf'
sudo sysctl -p
#安装区域设置,某些比较坑的程序需要特别的区域设置
sudo sh -c 'echo en_US.ISO-8859-1 ISO-8859-1 >> /var/lib/locales/supported.d/local'
sudo sh -c 'echo zh_CN.GBK GBK >> /var/lib/locales/supported.d/local'
sudo sh -c 'echo en_US.ISO-zh_CN.GB2312 GB2312 >> /var/lib/locales/supported.d/local'
sudo dpkg-reconfigure locales
#安装中文输入包
sudo apt-get install language-pack-zh-hans
</code></pre>
<p>这样我就再不害怕重装系统了,希望这里部分的工具也能够帮到你:)</p>
<p>_(完)</p>
设定Mac的命令行环境
https://segmentfault.com/a/1190000000513010
2014-05-19T19:04:11+08:00
2014-05-19T19:04:11+08:00
chemzqm
https://segmentfault.com/u/chemzqm
1
<p><img src="http://everchangingmedia.com/wp-content/uploads/2013/01/iterm2-150x150.png" alt="iterm2"></p>
<p>非常喜欢Mac简约实用的设计风格,再配上Retina显示屏,设觉体验大大提升了,不过对于习惯Linux命令行的<br>
人来说还是有必要再做些设置。</p>
<h2>安装<a rel="nofollow" href="http://www.iterm2.com">iTerm2</a>
</h2>
<p>xTerm2 可支持多窗口、鼠标选中即复制等等众多实用的功能,建议用它做为你的 Mac 默认终端程序。详细使用<br>
方法请阅读<a rel="nofollow" href="http://www.iterm2.com/#/section/documentation">官方文档</a>。有个比较怪异的地方就是<br>
iTerm2的全屏快捷键是 <code>cmd+enter</code> 而不是默认的 <code>ctrl+cmd+f</code></p>
<p>建议自己设置颜色模式,我使用的是 <a rel="nofollow" href="http://www.iterm2.com/hostedcolors/Solarized%20Dark.itermcolors">Solarized dark</a></p>
<h2>安装Command line tools</h2>
<p>苹果的 Command line tools 是专为开发者使用的,包括 <code>gcc</code> 等常用的基本工具。</p>
<p>推荐登陆 <a rel="nofollow" href="http://connect.apple.com">http://connect.apple.com</a> ,然后搜索 <code>command line tools</code> 选择对应版本进行安装,你也<br>
可以通过<code>Xcode</code>进行安装</p>
<h2>安装<a rel="nofollow" href="http://mxcl.github.io/homebrew/">homebrew</a>
</h2>
<p>homebrew是Mac下目前最常用的包管理工具,相当于debain下的 <code>apt</code> , Red hat系列的 <code>yum</code> ,帮你安装、<br>
升级、移除软件工具包。软件安装完成后homebrew有时会给出进一步设置的提示,强烈建议仔细阅读。</p>
<pre><code class="lang-bash">ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"
</code></pre>
<p>homebrew默认会把可执行文件装在目录 <code>/usr/local/bin</code> 下面,建议修改 path 路径,让你通过 homebrew<br>
安装的工具可以覆盖掉Mac默认的(例如git,Mac自带1.7版本的git,过老了)。使用管理员权限修改文件<br><code>/etc/paths</code> 将 <code>/usr/local/bin</code> 移动到第一行。</p>
<h2>安装Bash</h2>
<p>Mac 虽然默认也是使用 GNU Bash,不过使用命令 <code>/bin/bash --version</code> 可看到版本只有 <code>3.2</code>,该版本<br>
不支持 <code>4.0</code> 版本后添加的关联数组等功能,为了脚本的通用,建议升级到最新版本。</p>
<pre><code class="lang-bash">brew install bash
</code></pre>
<p>安装完要设置新安装的 bash 为默认 bash ,用超级用户编辑文件:<code>/etc/shells</code>,加入<br><code>/usr/local/bin/bash</code>到第一行</p>
<h2>安装bash-complete</h2>
<p>bash 自动完成功能在 linux 发行版里一般都会自带,不过通过 homebrew 安装也很简单</p>
<pre><code class="lang-bash">brew install bash-completion
</code></pre>
<p>安装完成后需要根据提示在你的 <code>.profile</code> 文件中添加几行:</p>
<pre><code class="lang-bash">if [ -f $(brew --prefix)/etc/bash_completion ]; then
. $(brew --prefix)/etc/bash_completion
fi
</code></pre>
<h2>安装coreutils</h2>
<p>Mac底层是基于freeBSD的,所以常用工具例如 <code>ls</code> 、<code>grep</code> 也都是freeBSD版本的,为了让我们的脚本可以更容易<br>
的跨平台,我们可以安装coreutils</p>
<pre><code class="lang-bash">brew install coreutils
</code></pre>
<p>装完需要根据提示进行设置,在<code>.profile</code>文件中加入</p>
<pre><code>export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
export MANPATH="/usr/local/opt/coreutils/libexec/gnuman:$MANPATH"
</code></pre>
<p>让GNU的工具覆盖freeBSD的,并且使用man时显示的是GNU工具的文档。</p>
<h2>安装dircolors-solarized</h2>
<p>因为默认的 <code>ls</code> 配色实在很土,所以我用solarized配色方案:</p>
<pre><code class="lang-bash">mkdir ~/lib
cd ~/lib
git clone git@github.com:seebi/dircolors-solarized.git
echo 'eval `dircolors ~/lib/dircolors-solarized/dircolors.256dark`' >> ~/.profile
</code></pre>
<p><em>(完)</em></p>
前端模块化实践(工具篇)
https://segmentfault.com/a/1190000000513008
2014-05-19T19:02:34+08:00
2014-05-19T19:02:34+08:00
chemzqm
https://segmentfault.com/u/chemzqm
0
<blockquote>
<p>相比与那些看上去很高明、很完善的玩具,我更喜欢那些简单的、笨笨的工具,哪怕它们看上去需要更多时间去学习,因为我清楚,它们才能最终让我更高效。</p>
</blockquote>
<p>本文仅限于粗浅的介绍我在开发过程中用到的一些工具,如果需要更进一步的研究,多花些时间去看看文档这样的笨功夫恐怕还是免不了的。</p>
<h3>使用<a rel="nofollow" href="http://nodejs.org/">nodejs</a>搭建web服务</h3>
<p>前后端的分离是必然的,如果你需要一个后端的接口,但是此时接口还没实现你该怎么办呢? 使用node可以非常简便的开启web服务,以下我们以使用<a rel="nofollow" href="https://github.com/senchalabs/connect">connect</a>为例搭建一个动态web服务,它将返回json格式的请求url。</p>
<ul>
<li>安装好nodejs后新建任意目录然后进入;</li>
<li>运行命令<code>npm install connect</code>
</li>
<li>新建文件<code>app.js</code>,加入以下一段代码:</li>
</ul>
<pre><code class="lang-js">var connect = require('connect');
var http = require('http');
var app = connect();
var server = http.createServer(app);
//输出请求信息的日志
app.use(connect.logger('dev'))
.use(function(req, res, next) {
res.setHeader('Content-type', 'application/json');
res.end(JSON.stringify({
url: req.url
}));
})
server.listen(3000);
</code></pre>
<ul>
<li>运行<code>node app.js</code>,浏览器访问<code>http://localhost:3000</code>即可。</li>
</ul>
<h3>代码检测与调试</h3>
<p>我使用<code>jshint</code>来检测基本的语法错误,每次保存文件时vim都会调用它进行语法检测,错误会被直接标示在左侧标示栏,简单而且直接。之前使用webstorm发现它只能使用自带的且定制功能非常少的jshint,结果一屏幕的红黄线,极其让人不舒服,而sublime的jshint插件每次都会在下面弹出一个新窗口,这种打断用户的体验实在很影响效率。</p>
<p>jshint还可以支持自定义<code>reporter</code>,你可以在reporter里面过滤不需要的警告,显示你最关注的具体错误信息,或者改变结果的输出流,一个简单的例子如下:</p>
<pre><code class="lang-js">'use strict';
module.exports = {
reporter: function (res) {
var len = res.length;
var str = '';
var codes = [ 'I003', 'W098', 'W033', 'W106', 'W015', 'W014', 'W116', 'W109', 'W064',
'I001', 'W079', 'W103', 'W069', 'W068', 'W003', 'W083' ];
res.forEach(function (r) {
var file = r.file;
var err = r.error;
var code = err.code;
if (codes.indexOf(code) !== -1) {
return;
}
str += code + ' ' + file + ': line ' + err.line + ', col ' +
err.character + ', ' + err.reason + '\n';
});
if (str) {
process.stdout.write(str + '\n' + len + ' error' +
((len === 1) ? '' : 's') + '\n');
}
}
};
</code></pre>
<p>我们的代码总是被合并到一个文件后加载到页面上的,借助<a rel="nofollow" href="http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/">Source Map</a>功能可以快速的在源码中进行调试。使用<code>Source Map</code>在component中非常简单,只需要在<code>component build</code>命令后加入<code>--dev</code>参数即可。</p>
<h3>脚本</h3>
<p>使用git自带的<code>post-receive</code>脚本可以帮我们一键搞定代码检查、打包、压缩等等繁琐的部署工作,一个使用component的例子如下:</p>
<pre><code class="lang-bash">#! /bin/bash
base=/home/chemzqm/blog
# 导出master到项目路径
git archive master | tar -x -C ${base}
cd ${base}
# 安装node包
npm install
cd public
stamp=`date +%s`
# 替换页面上的为压缩文件名称并加入时间戳
sed -i "s/build.js/build.min.js?d=${stamp}/" index.html
sed -i "s/build.css/build.min.css?d=${stamp}/" index.html
sed -i 's/\s*--dev//' Makefile
# 编译component
component build
# 压缩
uglifyjs build/build.js > build/build.min.js
cssmin build/build.css > build/build.min.css
# 调用blog脚本重启服务
/home/chemzqm/blog restart
echo 'done'
</code></pre>
<p>component结合Makefile可以非常方便而且灵活的完成日常构建任务,Makefile主要优点如下:</p>
<ul>
<li>简短,不用去维护大段大段的构建脚本</li>
<li>足够灵活,可以直接使用shell脚本</li>
<li>时间戳检测,当源文件时间戳早于目标文件时,任务不会被重复执行,可以结合watch工具实现灵活的自动构建。</li>
</ul>
<h3>web接口调试</h3>
<p>随着Saas、laas之类的概念大行其道,前端也从传统的接受后端html页面输出变成了调用各种Rest接口。这种开发方式的优势很明显,一方面Rest接口做为后端可以独立的服务各种前端的应用(不再需要考虑你是那种屏幕,或是需要整合成什么格式),另一方面前后端可以更容易的独立并行开发。</p>
<p>我使用<a rel="nofollow" href="https://github.com/visionmedia/supertest">supertest</a>进行Rest接口测试,最大好处是我可以直接把<a rel="nofollow" href="https://github.com/visionmedia/superagent">superagent</a>写好的代码直接复制过去使用,因为它们的接口几乎完全一样。</p>
<p><a rel="nofollow" href="https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm">Postman</a>做为一款Chrome的插件可以让我们快速的查看和使用Rest接口,它支持url参数设定、环境变量和分组,相比与同类<a rel="nofollow" href="https://chrome.google.com/webstore/detail/dev-http-client/aejoelaoggembcahagimdiliamlcdmfm">Dev HTTP Client</a>功能更加完备。</p>
<h3>其它</h3>
<ul>
<li>
<a rel="nofollow" href="https://github.com/fgnass/node-dev">node-dev</a> 监测文件变化并自动重启服务,开发nodejs必备。</li>
<li>
<a rel="nofollow" href="https://github.com/chemzqm/liveload">liveload</a> 监测文件变化并自动刷新浏览器,在开发手机app或者电视app这种不便于重加载的条件下很有帮助。</li>
<li>
<a rel="nofollow" href="https://github.com/visionmedia/watch">watch</a> 重复执行一项任务,可以结合Makefile完成任意自动化编译。</li>
<li>
<a rel="nofollow" href="https://github.com/ai/autoprefixer">autoprefixer</a> 可以从<a rel="nofollow" href="http://caniuse.com/">Can I use</a>上面获取浏览器兼容信息,并根据你给出的策略(默认常规浏览器最新的两个版本)自动补全css前缀。</li>
</ul>
<p>以上只是一些我常用到的工具,web开发的复杂性导致我们最终需要的工具肯定远远不止这些。为了可以总体上节约时间,我个人更喜欢使用专注于做好某一方面的小工具,而不是妄想着借助某种神器一劳永逸。</p>
<p><em>(完)</em></p>
实践:为jekyll构建的博客添加基础功能
https://segmentfault.com/a/1190000000513006
2014-05-19T19:00:23+08:00
2014-05-19T19:00:23+08:00
chemzqm
https://segmentfault.com/u/chemzqm
2
<p>通过github默认构建工具生成的博客功能还是非常单薄的,所以就需要做点后期工作添加一些常见的功能,例如:搜索、评论、订阅和归档页面。</p>
<h2>构建工具</h2>
<p>写了一个简单的Makefile来做自动构建的工作:</p>
<pre><code class="lang-make"><br>all: css js site
css:
@sqwish stylesheets/styles.css
js:
@cat javascripts/respond.js javascripts/common.js | uglifyjs > javascripts/main.min.js
site:
@jekyll --rdiscount
watch:
@jekyll --rdiscount --server --auto
.PHONY: css
</code></pre>
<p>这样我在vim调用:make命令就完成压缩合并资源文件和本地部署文件的任务了。</p>
<h2>图标</h2>
<p>使用了<a rel="nofollow" href="http://one-div.com/">One div</a>上面的几个css样式,这样可以减少图片文件的请求数量,加快响应时间,不过现在图标在移动设备的Chrome上显示有点大,暂时还没解决。</p>
<h2>订阅功能</h2>
<p>订阅有两种:一种是rss,一种是atom,都是从githab上面找的模板。</p>
<pre><code class="lang-xml">---
layout: none
---
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ site.name }}</title>
<description>{{ site.description }}</description>
<link>{{ site.url }}</link>
<atom:link href="{{ site.url }}/feed.xml" rel="self" type="application/rss+xml" />
{% for post in site.posts limit:10 %}
<item>
<title>{{ post.title }}</title>
<description>{{ post.content | xml_escape }}</description>
<pubDate>{{ post.date | date: "%a, %d %b %Y %H:%M:%S %z" }}</pubDate>
<link>{{ site.url }}{{ post.url }}</link>
<guid isPermaLink="true">{{ site.url }}{{ post.url }}</guid>
</item>
{% endfor %}
</channel>
</rss>
</code></pre>
<p><em>注意这里有使用部分_config.xml里的变量</em></p>
<pre><code class="lang-xml">---
layout: nil
title : Atom Feed
---
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ site.title }}</title>
<link href="{{ site.url }}/atom.xml" rel="self"/>
<link href="{{ site.url }}"/>
<updated>{{ site.time | date_to_xmlschema }}</updated>
<id>{{ site.url }}</id>
<author>
<name>{{ site.author.name }}</name>
<email>{{ site.author.email }}</email>
</author>
{% for post in site.posts %}
<entry>
<title>{{ post.title }}</title>
<link href="{{ site.url }}{{ post.url }}"/>
<updated>{{ post.date | date_to_xmlschema }}</updated>
<id>{{ site.url }}{{ post.id }}</id>
<content type="html">{{ post.content | xml_escape }}</content>
</entry>
{% endfor %}
</feed>
</code></pre>
<p><em>注意这里有使用部分_config.xml里的变量</em></p>
<h2>搜索和评论服务</h2>
<p>搜索用的google:</p>
<pre><code class="lang-html"><form class="search" method="GET" action="https://www.google.com.hk/search">
<input type="text" name="as_q" class="search-query" placeholder="Search">
<input type="hidden" name="as_sitesearch" value="chemzqm.me">
</form>
</code></pre>
<p>样式是由<a rel="nofollow" href="angularjs.org">angularjs.org</a>借鉴过来的(感谢),另加了动画和阴影。</p>
<p>评论用的是Disqus(因为没有找到国内提供静态页面评论服务的提供商)</p>
<pre><code class="lang-javascript">function _load(src, callback){ //load script file async
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = src;
script.onload = callback;
document.getElementsByTagName('HEAD')[0].appendChild(script);
}
_load('http://{{site.disqus_name}}.disqus.com/embed.js');
</code></pre>
<p><em>注意这里有使用部分_config.xml里的变量</em></p>
<h2>站点地图</h2>
<p>创建站点地图索引文件可以有效的帮助搜索引擎找到你的文章, 具体用法请参考<a rel="nofollow" href="http://support.google.com/webmasters/bin/answer.py?hl=zh-Hans&answer=71453">google的文章</a>, sitemap可以支持多种格式,我这里还是用的xml:</p>
<pre><code class="lang-xml">---
---
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% for post in site.posts %}
<url>
<loc>{{ site.url }}{{ post.url }}</loc>
<lastmod>{{ post.date | date_to_xmlschema }}</lastmod>
<changefreq>monthly</changefreq>
</url>
{% endfor %}
</urlset>
</code></pre>
<h2>定制页面</h2>
<p>写了两个分类的页面,一个是按时间分类的 <a rel="nofollow" href="http://chemzqm.github.io/archive.html">archive.html</a>,另一个是按tag分类的 <a rel="nofollow" href="http://chemzqm.github.io/tags.html">tags.html</a>. 两个页面模板代码差不多,例如tags.html:</p>
<pre><code class="lang-html">---
layout: default
title: 按标记归档
---
<ul id="tagbox" class="clearfix">
{% for tag in site.tags %}
<li><a href="{{ BASE_PATH }}/tags.html#{{ tag[0] }}-ref">
{{ tag[0] | join: "/" }} <span>{{ tag[1].size }}</span>
</a></li>
{% endfor %}
</ul>
{% for tag in site.tags %}
<h2 id="{{ tag[0] }}-ref">{{ tag[0] | join: "/" }}</h2>
<ul>
{% assign pages_list = tag[1] %}
{% for node in pages_list %}
{% if node.title != null %}
{% if group == null or group == node.group %}
{% if page.url == node.url %}
<li class="active"><a href="{{ BASE_PATH }}{{node.url}}" class="active">{{node.title}}</a></li>
{% else %}
<li><a href="{{ BASE_PATH }}{{node.url}}">{{node.title}}</a></li>
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
</ul>
{% endfor %}
</code></pre>
<h2>配置文件</h2>
<pre><code class="lang-yaml">name: jack's blog
author:
name: Qiming Zhao
email: chemzqm@gmail.com
github: chemzqm
description: blog of jack(chemzqm)
#google analysis track id
track: UA-22751097-2
disqus_name: zqm
url: http://chemzqm.me
markdown: rdiscount
pygments: true
</code></pre>
<p>其中前面的参数用做变量,可在其它文件中使用<code>{{ site.name }}</code>来引用。后面两个是jekyll运行参数,具体请参考<a rel="nofollow" href="https://github.com/mojombo/jekyll/wiki/Configuration">官方文档</a></p>
<p>尽管使用jekyll来扩展功能不像wordpress中安装插件那样的方便,但是代码的加入和编写的过程完全可控,不必理会操作数据库的繁琐操作,同时liquid模板的可读性相比php要高的多,结果就是管理者在修改的时候有章可循,很大程度降低了维护的成本。</p>
<p>如果你感兴趣,可以到<a rel="nofollow" href="https://github.com/chemzqm/chemzqm.github.com">github</a>上下载本站的最新源代码</p>
使用jekyll和github搭建个人博客
https://segmentfault.com/a/1190000000513001
2014-05-19T18:59:06+08:00
2014-05-19T18:59:06+08:00
chemzqm
https://segmentfault.com/u/chemzqm
1
<h2>起因</h2>
<p>最开始我也是尝试用wordpress来做blog,也写过几篇东西,而且花了不少经历来定制样式和插件,<br>
不过后来那个东西实在是不太适合我。</p>
<ol>
<li>添加新功能非常费劲,因为它的文件非常的分散,而且使用php这种松散的脚本语言,一大堆的php,js,css文件搞的我真是头晕目炫。</li>
<li>各种插件资源文件分散到不同的文件夹里,合并优化根本无从谈起,因为你很难搞清楚它们的依赖。要知道网站的加载速度可比那些乱七八糟的插件功能重要的多!</li>
<li>默认编辑器可用性极差,记得当时在可视化编辑和默认编辑方式下一切换,页面直接就凌乱了,现在也许好些了,不过再怎么比都不可能超过我用自己的vim写markdown的效率。</li>
</ol>
<p>后来我到<a rel="nofollow" href="http://www.diandian.com/">点点网</a>上面看了看, 在那个网站创建博客不仅简单、免费,<br>
而且还有很多漂亮模板。<br>
不过等我试着新建了一篇文章便发现这家伙居然用iframe来显示文章来达到漂亮的切换效果,<br>
试问我连文章的url都拿不到,要那炫动画干啥?给我自己看吗?</p>
<h2>确定目标</h2>
<p>我期待的blog要至少满足以下要求:</p>
<ul>
<li>易用性原则:一方面使用者可以方便的编辑、管理文章,另一方面最大程度的方便用户获取信息</li>
<li>简单性原则:不添加过多功能,最大程度保证用户不被无用信息所干扰</li>
<li>可用性原则:适应不同设备、浏览器访问,支持无障碍访问,只是关闭javascript访问</li>
</ul>
<h2>解决:github + Jekyll</h2>
<p>这是我目前的解决方式,基本上完成了我的目标。使用github一方面能保证你可以保存你blog的所有历史记录,<br>
另一方面它可是完全免费的哦,具体怎么做请参考这篇<br><a rel="nofollow" href="http://www.ruanyifeng.com/blog/2012/08/blogging_with_jekyll.html">搭建一个免费的,无限流量的Blog----github Pages和Jekyll入门</a>。<br>
Jekyll是ruby语言做的一个静态网站生成工具,以下是一篇介绍为何使用Jekyll的文章 <a rel="nofollow" href="http://qubitlogs.com/Workflow/2013/01/22/jekyll-blogging-reference-and-perfect-workflow-guide/">Jekyll blogging reference and perfect workflow guide</a>。</p>
<p>另外,使用github的<img src="http://segmentfault.com/img/bVcjB7" alt="Automatic Page Generator">会帮你自动加入一些有用的东西,例如:</p>
<ul>
<li>google cdn 上的 jquery</li>
<li>respond.js:解决响应式样式在ie下的显示问题</li>
<li>pygments_style.css pygment 代码高亮样式</li>
<li>google analytics网站分析所需的js代码块</li>
</ul>
<p>总而言之,使用github加Jekyll搭建一个blog还是非常快速的,不过劣势也很明显,想要更多功能需要自己实现或者去找插件(不像wordpress可以直接在控制台里面安装),也没什么绚丽的模板(github上就几个模板可用)。也正因如此,使用者对框架的可控性被大大的增强,而这正是我需要的。</p>
<h2>优化</h2>
<p>刚生成的项目加载起来很慢,于是我做了一些简单的优化, 包括</p>
<ul>
<li>去掉没用JS,例如jquery。</li>
<li>压缩图片,原来的导航背景图片大小是1.5kb截图转成png大小只有439个字节。</li>
<li>合并压缩js,css文件,合并可以减少请求数,压缩可以大大缩短接收所用时间。</li>
</ul>
<p>我们可以从下面两张图中很明显的看到优化的效果:<br><img src="http://segmentfault.com/img/bVcjCc" alt="优化前"><br><img src="http://segmentfault.com/img/bVcjCi" alt="优化后"></p>
<p>页面内容的加载时间从3秒降到了1秒,图片的下载速度也有了很明显提高。</p>
<p>不过还没完,注意到这1秒的加载其实有很大一部分时间是在等待服务器响应,如果我们使用延迟更低的服务器,而不是境外的github,页面的加载效率还会有质的飞跃(300毫秒以内)。另外,github上面托管的页面也无法使用配置.htaccess之类的方式来优化缓存,而默认的缓存过期时间只有10分钟</p>
componet介绍之一:基本特点
https://segmentfault.com/a/1190000000512982
2014-05-19T18:52:53+08:00
2014-05-19T18:52:53+08:00
chemzqm
https://segmentfault.com/u/chemzqm
0
<p><img src="http://component.io/boot/images/logo.png" alt="component"></p>
<p><a rel="nofollow" href="https://github.com/component/component">component</a> 是javascript大牛<a rel="nofollow" href="https://github.com/visionmedia">TJ</a><br>
的又一力作,有别于那些大型javascript开发库,component本身其实只是一个基于<a rel="nofollow" href="http://en.wikipedia.org/wiki/CommonJS">CommmonJS</a> module/1.0 规范(同Node.js一样)的模块化管理工具,类似于Node.js<br>
里面的npm工具,特别之处是它使用github做为代码托管工具,还有大部分常用的组件都不支持ie6和ie7。</p>
<p>我先解释一下componet最重要的一些特点:<br><em>(不理解没关系,记住就对了:))</em></p>
<ul>
<li><p><strong>模块化</strong>,所有依赖关系都通过一个简单的<code>require</code>函数获得,重用组件只需要执行<code>componnent install [repo]</code>命<br>
令,分分钟搞定,不过更为重要的是代码变得易读,看下require你就知道变量是哪里来的了。</p></li>
<li><p><strong>组件化</strong>,很多框架都实现了javascript的模块化加载,例如著名的<code>requirejs</code>和国内广受好评的<code>SeaJS</code>,<br>
component更进一步,它能帮你同时管理组件依赖的其它资源,包括 html、css、images 等等,使用组件仅仅安装<br>
即可,其它文件打包、路径调整等等琐碎之事它都帮你做好了,不用你再操心。</p></li>
<li><p><strong>轻量级</strong>,不像那些个复杂框架,各种神奇好用看上去很美的牛逼功能,component的基本组件非常的中规中<br>
矩,不过是些非常常用的设计模式罢了(MVC、观察者、原型继承),你不用再花大量的时间去学习那些聪明人折<br>
腾出来的一堆难懂API和奇技淫巧,不仅调试的时候纠结万分,而且很可能过两年你会发现那个框架不再维护了,<br>
新的牛逼框架又来了,于是你又陷入了对自己智商的深深纠结之中。</p></li>
<li><p><strong>简洁</strong>,虽然你可以使用<a rel="nofollow" href="https://github.com/component/jquery">jquery</a>这样的复合模块,但是你应该尽量使用<br>
和编写简洁的模块,例如最基础的组件<a rel="nofollow" href="http://component.io/timoxley/react">react</a>、<a rel="nofollow" href="https://github.com/component/model">model</a>,功能都非常的单一,代码很容易理解和扩展。开发人员花费在理解代码<br>
上的时间往往远大于编写代码的时间,所以代码的易于理解才是真正的提高生产力。</p></li>
<li>
<p><strong>版本控制</strong>,喜欢 git 的人回想当年使用 SVN 的历史满满都是心酸的血泪史,多半是基于它基于文件夹的设计<br>
和使用单一的集中远程仓库,不管是分支还是标记都会让人很是痛苦。使用github可以让分支的过程简单无比,比<br>
如有个<a rel="nofollow" href="https://github.com/JayceTDE/serialize">serialize</a> 组件,大概能满足我需求,不过还是需要稍做修改,于是<br>
我建立一个自己的分支,提交我的修改到我的github分支,安装我修改的组件只需要</p>
<pre><code>component install chemzqm/serialize
</code></pre>
<p>就可以了,不管以后有多少项目在用这个组件,只需要再次在项目里面执行install命令即可。如果项目只需要<br>
某个模块的特定版本,弄个tag就行,分分钟搞定,例如</p>
<pre><code>component installl chemzqm/serialize@1.0.0
</code></pre>
</li>
<li><p><strong>组件仓库</strong>,基础性的组件别人都已经做的很好了,如何只是为了实现功能何苦再重复造轮子呢?<a rel="nofollow" href="http://component.io/">component官网</a><br>
上组件库已经相当的丰富了,但凡你能想到的功能几乎无所不包,而且官方给出的插件质量都很高(你应该花些时间去好好读读它们的代码),完全不必考虑不兼容的问题。</p></li>
</ul>
<p>以上只是一些稍大的方面,component在许多具体的细节上面还有不少贴心的设计,例如你可以用<code>component create [name]</code> 命令生成模块模板,其中包括一个Makefile,每次代码有什么修改,只需要make一下就行,非常<br>
方便快捷,还可以整个我之前介绍的工具<a rel="nofollow" href="http://chemzqm.me/v/6">tiny-lr</a>,配合<br><a rel="nofollow" href="https://github.com/visionmedia/watch">watch</a> 和 <a rel="nofollow" href="http://growl.info/">growl</a> 轻松实现实时编辑,再比如它会自动<br>
添加<a rel="nofollow" href="http://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html">Source Map</a>到你使用命令<code>component build --dev</code><br>
生成的js文件中,这样你使用Chrome就可以直接在文件的源代码上调试了。</p>
<p>动心了吗?为什么不尽早让重复的开发和模块管理使用的繁重难问题变为历史呢?</p>
<p><em>(完)</em></p>
谈谈co模块的设计实现
https://segmentfault.com/a/1190000000512976
2014-05-19T18:51:01+08:00
2014-05-19T18:51:01+08:00
chemzqm
https://segmentfault.com/u/chemzqm
0
<p><a rel="nofollow" href="https://github.com/visionmedia/co/">co</a> 是TJ Holowaychuk基于ECMAScript 6 generator 特性开发的一个用于简化异步开发的模块。如果你想尝试使用,请先升级node到0.11版本(不稳定分支)并启用<code>--harmony-generators</code>选项,或者使用<a rel="nofollow" href="https://github.com/TooTallNate/gnode">gnode</a>。co的基本使用方法很简单:</p>
<pre><code class="lang-js">var co = require('..');
var fs = require('fs');
function read(file) {
return function(fn){
fs.readFile(file, 'utf8', fn);
}
}
co(function *(){
var a = yield read('.gitignore');
var b = yield read('Makefile');
var c = yield read('package.json');
//输出a的内容
console.log(a);
//输出b的内容
console.log(b);
//输出c的内容
console.log(c);
})()
</code></pre>
<p>想弄清楚这段代码里面发生的事情,我们首先需要明白 generator 是怎么玩的(本文只介绍 generator 定义中常用的一部分,详细信息可以参考<a rel="nofollow" href="http://wiki.ecmascript.org/doku.php?id=harmony:generators">在线文档</a>)。</p>
<ul>
<li>
<p>首先是创建 generator:</p>
<pre><code class="lang-js">function * gen (){
for (var i = 0; i < arguments.length; i++) {
yield arguments[i];
}
}
var g = gen(1, 2, 3);
</code></pre>
<p>通过在 <code>function</code> 关键字后面加入<code>*</code>我们可以创建一个 generator 构造函数,调用这个构造函数我们就生成了一个 generator 对象。</p>
</li>
<li>
<p>然后是理解yield关键字。首先注意yield只能接收一个参数。generator 对象上有个<code>next</code>方法用于迭代,这里迭代的意思就是执行代码到下一个yield关键字,<code>next</code>方法返回一个对象,对象只有两个属性,<code>value</code>属性表示 yield 右侧传入的参数,<code>done</code>属性是布尔值,表示是否所有的 yield 语句均执行完毕。你可以把yield放到无限循环里面,这样done属性永远都不会变为true。比如说</p>
<pre><code class="lang-js">function * gen (){
console.log(1);
yield 1;
console.log(2);
yield 2;
}
var g = gen();
console.log(g.next().value);
console.log(g.next().value);
console.log(g.next());
</code></pre>
<p>第一次调用<code>next</code>方法输出两个1,第二次调用<code>next</code>方法输出两个2,第三次调用<code>next</code>方法时由于yield已经全部执行完毕,所以返回的对象是<code>{ value: undefined, done: true }</code></p>
</li>
<li>
<p>最后,通过向<code>next</code>方法传入参数我们可以设定上一次yield调用的返回值,要注意的是yield只能接收一个参数,而且第一次调用next方法传入的参数是无意义的,例如:</p>
<pre><code class="lang-js">function * gen (){
var x = yield 1;
console.log(x);
}
var g = gen();
g.next();
//将x赋值为2
g.next(2);
</code></pre>
</li>
</ul>
<p>总之,通过generator,我们可以控制函数内部执行到哪个yield,可以知道是否所有yield声明执行完毕,可以对上一次的yield语句赋予一个返回值。</p>
<p>我们来实现一个简易版的co模块,目标是实现上文第一个例子中接受回调函数做为yield参数的API,至于co中支持的其它类型(promise、数组、对象、generator)为了简化程序便于理解,先不做支持。</p>
<pre><code class="lang-js">var slice = Array.prototype.slice;
function co(fn) {
//只支持generator函数做为参数
if (!isGeneratorFunction(fn)) return new Error('only generator supported');
return function(done) {
var ctx = this;
var args = slice.call(arguments);
if (!arguments.length) done = error;
//最后一个参数如果是函数则做为回调函数,其它的传给generator构造函数
else if ('function' == typeof args[args.length - 1]) done = args.pop();
else done = error;
var gen = fn.apply(this, args);
next();
function next(err, res) {
var ret;
if (err) return done(err);
// 多于2个参数时,将除了err的参数整合为数组
if (arguments.length > 2) res = slice.call(arguments, 1);
try {
ret = gen.next(res);
} catch(e) {
return done(e);
}
//所有yield执行完毕, 结束next递归,调用最终回调函数
if(ret.done) return done(null, ret.value);
var called = false;
try {
//ret.value是yield接收的参数,必须为一个接收一个回调函数为参数的函数,而这个回调函数必须把error对象做为第一个参数
ret.value.call(ctx, function() {
if (called) return;
called = true;
//递归调用next方法,这里的arguments第一个总是error,其余为数据
next.apply(ctx, arguments);
})
} catch(e) {
//总是在下次事件轮询时抛错,这样能防止同步异步导致状态不一致的情况
setImmediate(function(){
if (called) return;
called = true;
next(e);
});
}
}
}
}
function error(err) {
if (!err) return;
//下次轮询时抛出错误,正式使用时应该总是传入回调函数,此方法仅仅用于演示
setImmediate(function() {
throw err;
})
}
function isGeneratorFunction(obj) {
return obj && obj.constructor && 'GeneratorFunction' == obj.constructor.name;
}
</code></pre>
<p>其实质就是递归的调用一个自己构建的<code>next</code>函数这么的简单,做为练习,你可以考虑考虑如何让co中的yield支持数组做为参数,从而实现并发的支持。</p>
<p>_(完)</p>
学习HTML5 history API
https://segmentfault.com/a/1190000000512953
2014-05-19T18:42:21+08:00
2014-05-19T18:42:21+08:00
chemzqm
https://segmentfault.com/u/chemzqm
3
<p>html5 在 history 对象上添加几个新的方法、事件、属性,用以增强开发者对于浏览器历史记录的控制。大体上说,新的API可以帮助我们在无刷新的情况下改变浏览器的url,新增或者替换之前的历史记录。建议先看看这个<a rel="nofollow" href="http://html5demos.com/history">示例</a>,<br>
具体的文档推荐参考<a rel="nofollow" href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history?redirectlocale=en-US&redirectslug=Web%2FGuide%2FDOM%2FManipulating_the_browser_history">MDN</a>。</p>
<p>这里谈谈使用新的 History API时,<strong>需要特别注意的地方</strong>。</p>
<ul>
<li><p><code>state</code> 保存不了完整对象,只能保存一般对象(plain object),因为可能浏览器使用了JSON方法,方法都被移除掉了。这个我是被坑过的,明明保存了对象,并且在state上面找到了,结果调用方法就报错。</p></li>
<li><p><code>popstate</code> 事件在firefox下,页面直接载入时是不触发的,而 Chrome 和 Safari 下是触发的。这个问题很容易解决,因为我们总是可以通过 <code>history.state</code> 对象获得当前路径的 state 对象,只是在绑定事件的时候需要额外考虑一下第一次的情况(有些库会帮你考虑的)。</p></li>
<li><p>每次浏览器历史记录发生变化都会触发<code>popstate</code>事件,包括用户使用前进或者后退按钮,以及调用history接口。如果历史记录是由调用<code>pushState</code>或者<code>replaceState</code>改变的,state对象可通过<code>event</code>对象的<code>state</code>属性获得。</p></li>
</ul>
<p>最后,推荐一下TJ写的<a rel="nofollow" href="https://github.com/visionmedia/page.js">page.js</a>,一个不仅使用简单,而且对于异步操作非常友好的客户端路由库。</p>
<p><em>(完)</em></p>
设置JSHint,不要让工具成了绊脚石
https://segmentfault.com/a/1190000000512948
2014-05-19T18:37:32+08:00
2014-05-19T18:37:32+08:00
chemzqm
https://segmentfault.com/u/chemzqm
7
<p><a rel="nofollow" href="http://www.jshint.com/">JSHint</a>是一个强大的javascript代码检测工具,不仅可以帮助我们检测到可能的代码错误,也能帮助我们有效的避免编码的错误。</p>
<p>JSHint本身是个命令行工具,它提供了灵活了的选项设置方式,不过如果你要使用那种毫无节操的内置JSHint的IDE(例如IntellJ)的话,就只能选择一些有限的选项,然后终日忍受那满屏幕都是红黄错误警告了。JSHint的选项大体可以分三种:</p>
<h3>基于文件夹的设置方式</h3>
<p>JSHint默认使用用户home目录下的<code>.jshintrc</code>文件(json格式)作为配置文件,例如我的文件是这样的:</p>
<pre><code class="lang-json">{
"sub":true,
"laxbreak":true,
"laxcomma":true,
"regexp":true,
"asi": true,
"browser": true,
"loopfunc":true,
"expr":true,
"node": true,
"es5": true,
"esnext": true,
"bitwise": true,
"curly": true,
"immed": true,
"latedef": false,
"expr": true,
"eqeqeq": false,
"eqnull": false,
"newcap": true,
"noarg": true,
"undef": true,
"proto": true,
"strict": false,
"smarttabs": true
}
</code></pre>
<p>具体的含义请参考<a rel="nofollow" href="http://www.jshint.com/docs/options/">文档</a>。你也可以在你的项目目录下添加一个<code>.jshintrc</code>文件,JSHint会在检测文件时从文件所在目录往上找,直到找到jshintrc文件为止。</p>
<h3>基于文件的设置方式</h3>
<p>对于需要对单个文件进行特定设置的需求,我们可以在文件最上方使用注释行来设定JSHint,例如:</p>
<pre><code class="lang-js">/* jshint undef: true, unused: true */
/* global MY_GLOBAL */
</code></pre>
<p>某些警告是无法通过错误来进行屏蔽的,这时候你首先需要知道你要屏蔽错误的错误代码,通过添加<code>--verbose</code>参数来获得:</p>
<pre><code class="lang-sh">$ jshint --verbose myfile.js
myfile.js: line 6, col 3, Unnecessary directive "use strict". (W034)
</code></pre>
<p>如果需要屏蔽这个错误,可在文件中加入:</p>
<pre><code class="lang-js">/* jshint -W034 */
</code></pre>
<h3>基于函数的设定方式</h3>
<p>类似基于文件的设定,只要把相应的注释行移到函数内就行了,例如:</p>
<pre><code class="lang-js">// From another file
function b() {
"use strict";
/* ... */
}
</code></pre>
<h3>hacker的方式</h3>
<p>上面说的方式官方的文档都有更加详细的说明,可如果你总是想禁用掉特定的警告该怎么办呢?比方说对于以下代码:</p>
<pre><code class="lang-js">if (err) return next(err);
</code></pre>
<p>JSHint会警告<code>Expected '{' and instead saw 'return'.</code>,因为我(node社区很多人也是)不喜欢在单行的返回代码前后添加大括号,但是我也不想总是在文件最上面添加注释添加一块专门的注释来禁用它。一种可行的办法是修改编辑器的代码检测插件,不过难度有点高,另一种方法就是修改JSHint 的代码,我们只需要对一个<code>reporter</code>文件稍做修改即可。打开文件<code>jshint/src/reporters/default.js</code>,在<code>var error = result.error;</code>下面加入两行代码:</p>
<pre><code class="lang-js">var code = error.code;
if (code === 'W116') return;
</code></pre>
<p>大功告成,保存以后再用JSHint就再也看不到那样的警告了。(当然,如果你的JSHint使用别的reporter那就另当别论了)</p>
<p>相信通过以上方式可以帮你免除99%以上无用警告的困扰了,对于代码有洁癖同时有比较懒的人来说,这很重要。</p>
<p><em>(完)</em></p>
使用tiny-lr自动刷新页面
https://segmentfault.com/a/1190000000512702
2014-05-19T16:16:08+08:00
2014-05-19T16:16:08+08:00
chemzqm
https://segmentfault.com/u/chemzqm
1
<p>编辑完代码,前端页面就可以自动刷新是一件很酷的事,可以显著提高调试的效率。</p>
<p>我来说说自己使用自动刷新页面的几种情况,如果是 node 项目我会使用自己做的模块,一个是 koajs 的中间件 <a rel="nofollow" href="https://github.com/chemzqm/koa-liveload">koa-liveload</a>,另一个是 connect 的中间件 <a rel="nofollow" href="https://github.com/chemzqm/liveload">liveload</a>,在 express 项目里面使用。</p>
<p>组件的详细用法请参考文档,这里只想说这样做的方便之处在于只需要 2~3 行代码就可以启用 liveload 功能,而不用写什么 grunt 或者 gulp 那种看起来头疼的代码。</p>
<p>可是如果项目不是 nodejs 就不太好办了,幸运的是 <a rel="nofollow" href="https://github.com/mklabs/tiny-lr">tiny-lr</a> 还提供了 <a rel="nofollow" href="https://github.com/mklabs/tiny-lr/blob/master/tinylr.mk">Makefile</a> 脚本,不过我一般是用自己写的,因为引用的 Makefile 不容易理解,其实不过是做了两件事,一是启动服务:</p>
<pre><code class="lang-Makefile">watch:
@echo ... Starting server, running in background ...
@echo ... Run: "make stop" to stop the server ...
@tiny-lr &
</code></pre>
<p>二是变化时通过post请求通知前端页面,</p>
<pre><code class="lang-Makefile">build:
//构建过程的代码
@curl -X POST http://localhost:35729/changed -d '{ "files": "build.js" }'
</code></pre>
<p>如果你还需要监视文件变化的话,我个人推荐 <a rel="nofollow" href="https://github.com/lepture/rewatch">rewatch</a> 这个小工具,大概用法如此:</p>
<pre><code class="lang-bash">rewatch index.js *.css -c "make build"
</code></pre>
<p>最后的 Makfile 大概是这样子的:</p>
<pre><code class="lang-Makfile">build: components index.js template.html
//构建前端项目
@curl -X POST http://localhost:35729/changed -d '{ "files": "build.js" }'
watch:
@echo ... Starting server, running in background ...
@echo ... Run: "make stop" to stop the server ...
@tiny-lr &
@rewatch index.js *.css -c "make build"
</code></pre>
<p>最后一件事就是在 html 页面里面嵌入 tiny-lr 的前端代码</p>
<pre><code class="lang-html"><!-- livereload snippet -->
<script>document.write('<script src="http://'
(location.host || 'localhost').split(':')[0]
':35729/livereload.js?snipver=1"><\/script>')
</script>
</code></pre>
<p>如果只调 Chrome 的话可以使用 <a rel="nofollow" href="https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei">livereload</a> 嵌入脚本代码。</p>
<p>使用命令 <code>make watch</code> 启动服务,便可享受保存代码后项目自动构建然后浏览器自动刷新了。</p>
<p>还有一个剩下的问题就是我不知道有没有什么好办法把改变的文件名传到那个 curl 的命令里面(除非单独写一条指令),如果tiny-lr知道改变的文件是css的话,它只会重加载那个css,这样调试起来更方便些。</p>