Aztack's Blog技术,旅行,摄影,阅读zh-cnMon Oct 23 2017 08:40:07 GMT+0800 (CST)《JavaScript at Scale》到货!<p>《JavaScript at Scale》京东已到货! <img src="/static/upload/201702/kY7l8ejvtMSIEBDHj5w9sRu-.png" alt="JavaScript at Scale"></p> <ul> <li><a href="http://product.china-pub.com/5089950">互动书店China-pub</a></li> <li><a href="https://item.jd.com/12121244.html">京东</a><!--more--> <hr/> 我和奇舞团小伙伴翻译的新书<a href="https://www.amazon.com/JavaScript-at-Scale-Adam-Boduch/dp/1785282158">《JavaScript at Scale》</a>基本翻译完成。预计2017年初出版。期间有些波折,但最终我们克服苦难还是完成了整书的翻译。</li> </ul> <p><img src="/static/upload/201702/X-rYtm7AELU4ekO54ni-B15c.jpeg" alt="alt"></p> <p>相对之前的 <a href="https://item.jd.com/11678022.html">《移动Web手册》</a> 这本书更难翻译。长句比比皆是。虽然能理解意思,但是组织成中文语句很困难。其实,本书的书名翻译起来也有点让人头疼。刚开始打算偷懒沿用Packt惯例定为《JavaScript at Scale 中文版》。后来采用了李松峰老师、波波和月影的建议,改为《大型JavaScript应用 - 最佳实践指南》....</p> <p>这本书适合中高级Web前端工程师阅读。书中并没有讲解市面上的任何框架或技术。而是讲解如何设计大型JavaScript应用。</p> <p>前端技术日新月异。知识更新速度很快。半年不到就会有新的技术出现。快速理解、学习新的技术已经成为每个前端工程师必备的技能。这本书的内容正是很多框架、技术的设计基础。有了这些知识,在遇到新的前端框架、技术就能做到有的放矢。</p> <p>下面是本书的目录。</p> <p><hr/> 目 录</p> <p>第1章 扩展JavaScript应用</p> <p>第2章 可扩展性的影响因素</p> <p>第3章 组件组合</p> <p>第4章 组件的通信与职责</p> <p>第5章 寻址和导航</p> <p>第6章 用户偏好和默认设置</p> <p>第7章 加载时间和相应速度</p> <p>第8章 可以移植性和测试</p> <p>第9章 缩小规模</p> <p>第10章 处理错误</p> Sun Dec 11 2016 22:23:03 GMT+0800 (CST)http://aztack.wang/post/javascript-at-scale.htmlSapain<p>360网盘关闭了,终于有理由挤出一点时间整理一下以前的照片了。</p> <p><img src="/static/upload/201612/i3UqD4AAzIQHrWRiMSlH-HrE.jpg" alt="alt"></p> <!--more--> <h2 id="toc-7f6">塞班岛北部</h2> <p><img src="/static/upload/201612/4pf6wBuNwdTinrnSy8_TDklH.jpg" alt="alt"> <img src="/static/upload/201612/e3KHDxcqwI8hKcDz7u5u68Re.jpg" alt="alt"> <img src="/static/upload/201612/g4dWpLD6_Z6a33OcwMBrEKuX.jpg" alt="alt"> <img src="/static/upload/201612/ntARDws_1mOby3wQwNMrFI15.jpg" alt="alt"> <img src="/static/upload/201612/k9ix1tQqP1kAaXd7BDb_LNiZ.jpg" alt="alt"></p> <h2 id="toc-987">军舰岛</h2> <p><img src="/static/upload/201612/yULN4tD5mPnXxb-v0UGjYDxg.jpg" alt="alt"></p> <p><img src="/static/upload/201612/iETqBwdn94kYi-0Pwx8rfpPg.jpg" alt="alt"></p> <p><img src="/static/upload/201612/lzxhnT5MML4VPULLWilxot_D.jpg" alt="alt"> <img src="/static/upload/201612/Ok0uMDI7WZfNcJgAyM3lPpch.jpg" alt="alt"> <img src="/static/upload/201612/APYd6JB0TdyE_LjQgPlocCj9.jpg" alt="alt"> <img src="/static/upload/201612/CnLpSrnlr6OMPI49Wh5ZrsDw.jpg" alt="alt"></p> <h2 id="toc-051">坐小潜艇看海底</h2> <p><img src="/static/upload/201612/c9K3sByPO82tV3vNxG8pMPq6.jpg" alt="alt"> <img src="/static/upload/201612/A9ms_o7LqJZPuhmKbG6qL3F_.jpg" alt="alt"> <img src="/static/upload/201612/6-8wjfryPVFU2yGeMnzfqwBd.jpg" alt="alt"> <img src="/static/upload/201612/LUiNYnblM_lfUsHGb7cGFymI.jpg" alt="alt"></p> Tue Dec 06 2016 18:00:37 GMT+0800 (CST)http://aztack.wang/post/sapain-vacation.html【思维导图】Understanding ECMAScript6<p><img src="https://img3.doubanio.com/lpic/s29015001.jpg" alt="understanding-ecmascript-6"></p> <p>NCZ的书一如既往的好,深入浅出。新增语言特性;为什么这么设计;如何使用;有那些坑;没有废话、恰到好处。力荐。 <a href="https://book.douban.com/subject/26864806/">豆瓣</a>、 <a href="https://www.amazon.com/Understanding-ECMAScript-Definitive-JavaScript-Developers/dp/1593277571/ref=sr_1_1?ie=UTF8&amp;qid=1474861360&amp;sr=8-1&amp;keywords=understanding+ecmascript+6">Amazon</a></p> <p>友情提示!6MB Flash思维导图,手机党慎入</p> <!--more--> <embed src="/static/upload/201609/understanding-ecmascript-6.swf?v=201609261204" allowFullScreen="true" quality="high" width="100%" height="800" align="middle" type="application/x-shockwave-flash"></embed>Mon Sep 26 2016 13:28:25 GMT+0800 (CST)http://aztack.wang/post/understanding-ecmascript-6.html从doctype说开来<p>对大多数Web前端工程师来说,对HTML的doctype即熟悉又陌生。今天我们就八一八doctype相关的知识。</p> <!--more--> <h1 id="toc-cfb"><a class="anchor" href="#toc-cfb"></a>SGML、XML、HTML、XHTML的关系</h1> <p>说doctype之前先得说说这几个*ML之间的关系。</p> <p>最先有的SGML,它是一个通用的标记语言。然后有了HTML,基于SGML,专门为Web页面而生。</p> <p>XML是SGML的一个子集,更简单,也更严格。<a href="https://www.w3.org/TR/NOTE-sgml-xml">这里</a>是W3C在1997年发的一个对XML和SGML的对比Note。</p> <p>最后,基于XML重新制定了XHTML。 下图给出了4者之间的关系:</p> <p><img src="/static/upload/201609/iaYt7xR7GqJoFTYb9zpW94Sv.png" alt="html-sgml-xhtml-xml"></p> <p>可以看出来,SGML和XML都是通用的标记语言。HTML和XHTML都是<code>通用标记语言在Web领域的特定应用</code>。既然是特定领域,就需要对可以出现的元素做出精确的定义。所以,就有了文档类型定义---DTD</p> <h1 id="toc-bc6"><a class="anchor" href="#toc-bc6"></a>文档类型定义-DTD</h1> <p>DTD定义了XML文档的结构。定义了合法的标签、属性等。HTML对应的DTD定义了HTML文档中可以出现的标签及其属性。</p> <h2 id="toc-d6f">DTD超简介</h2> <p>想一想,作为DTD需要具备以下能力:定义合法元素、元素内可以包含的元素、元素的闭合;定义元素的属性必选、可选和取值。</p> <p>声明元素:</p> <pre><code class="hljs lang-undefined">&lt;!ELEMENT generic-identifier (content or EMPTY)&gt; </code></pre> <p>声明属性:</p> <pre><code class="hljs lang-undefined"> &lt;!ATTLIST element-name {attr-name attr-type default} ...&gt; </code></pre> <p>default = [#REQUIRED | #IMPLIED | &quot;value&quot; | #FIXED &quot;value&quot;] 含义依次为:必填、可选、可选默认、必选固定</p> <p>声明实体:</p> <pre><code class="hljs lang-undefined">&lt;!ENTITY % entity-name "text"&gt; </code></pre> <p>所有出现<code>%entity-name;</code>的地方都会被替换为<code>text</code></p> <p>(<a href="http://infohost.nmt.edu/tcc/help/pubs/dtd/">完整DTD教程</a>)</p> <p>下面是一个简单的DTD实例:</p> <pre><code class="hljs lang-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span><span class="hljs-meta">?&gt;</span></span> <span class="hljs-meta">&lt;!DOCTYPE note [ &lt;!ELEMENT note (to,from,heading,body)&gt; &lt;!ELEMENT to (#PCDATA)&gt; &lt;!ELEMENT from (#PCDATA)&gt; &lt;!ELEMENT heading (#PCDATA)&gt; &lt;!ELEMENT body (#PCDATA)&gt; ]&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">note</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">to</span>&gt;</span>Tove<span class="hljs-tag">&lt;/<span class="hljs-name">to</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">from</span>&gt;</span>Jani<span class="hljs-tag">&lt;/<span class="hljs-name">from</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">heading</span>&gt;</span>Reminder<span class="hljs-tag">&lt;/<span class="hljs-name">heading</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>Don"t forget me this weekend!<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">note</span>&gt;</span> </code></pre> <p>看,这里的<code>&lt;!DOCTYPE note...&gt;</code>是一个自定义的文档类型。 我们的html5的文档类型是<code>&lt;!DOCTYPE html&gt;</code>,是不是突然明白了什么?</p> <p>上面的<code>doctype note</code>是内联的DTD。它定义了当前XML文件可以有一个note标签,其下可包含<code>to,from,heading,body</code>元素。这几个元素的内容都是<code>PCDATA</code>类型。</p> <blockquote> <p>PCDATA(Parsed Character Data)将会被xml解析器parse,CDATA(Character Data)不会被parse。</p> </blockquote> <p>再来看我们的HTML有哪些DTD:</p> <p>HTML2.0</p> <pre><code class="hljs lang-html"><span class="hljs-meta">&lt;!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"&gt;</span> </code></pre> <p>HTML3.2</p> <pre><code class="hljs lang-html"><span class="hljs-meta">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"&gt;</span> </code></pre> <p>HTML2.0和3.2的doctype中并没有提供下载地址。不过我们也需要不关心了。 HTML4.01后都有提供dtd下载地址(下面省略了Frameset对应的doctype声明):</p> <p>HTML4.01</p> <pre><code class="hljs lang-html"><span class="hljs-meta">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&gt;</span> </code></pre> <p>HTML 4.01 Transitional</p> <pre><code class="hljs lang-html"><span class="hljs-meta">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;</span> </code></pre> <p>XHTML 1.0 Strict</p> <pre><code class="hljs lang-html"><span class="hljs-meta">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;</span> </code></pre> <p>XHTML 1.0 Transitional</p> <pre><code class="hljs lang-html"><span class="hljs-meta">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;</span> </code></pre> <p>XHTML 1.1</p> <pre><code class="hljs lang-html"><span class="hljs-meta">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&gt;</span> </code></pre> <p>XHTML1.1及之前都是有对应dtd文件的。HTML5没有了。是不是突然感觉生活美好了、世界变清爽了!</p> <p>HTML5</p> <pre><code class="hljs lang-html"><span class="hljs-meta">&lt;!DOCTYPE html&gt;</span> </code></pre> <p>为什么没有html5.dtd或html5.xsd? 简单来说,<a href="http://stackoverflow.com/questions/4053917/where-is-the-html5-document-type-definition?answertab=active#tab-top">html5已经不再是基于SGML或XML了,已经复杂到无法完全用DTD来描述了</a>。</p> <p>不过经过搜索,我倒是还真找到了一些HTML的dtd:比如,<a href="https://github.com/Komodo/KomodoEdit/blob/master/contrib/catalogs/html5/html5.dtd">html5.dtd</a>是<a href="https://github.com/Komodo/KomodoEdit">Komodo Editor</a> 中带的用来校验html5的dtd文件。</p> <p>HTML5没有对应的DTD,但是XHTML1有啊,我们下载下来看看,里面都有什么!</p> <h1 id="toc-05c"><a class="anchor" href="#toc-05c"></a>XHTML1.dtd</h1> <p>将XHTML1.dtd下载下来。(XHTML1.1被分为多个模块,为了方便我们来看XHTML1.dtd) 整体浏览一下,发现前面是一些实体定义:</p> <ol> <li>Character mnemonic entities </li> <li>Imported Names </li> <li>Generic Attributes </li> <li>Text Elements </li> <li>Block level elements</li> <li>Content models for exclusions </li> </ol> <p>后面被分为几个部分:</p> <ol> <li>Document Structure </li> <li>Document Head </li> <li>Document Body </li> <li>Paragraphs</li> <li>Headings</li> <li>Lists</li> <li>Address</li> <li>Horizontal Rule</li> <li>Preformatted Text</li> <li>Block-like Quotes </li> <li>Inserted/Deleted Text</li> <li>The Anchor Element </li> <li>Inline Elements</li> <li>Object</li> <li>Images</li> <li>Client-side image maps</li> <li>Forms</li> <li>Tables</li> </ol> <p>从文档开头搜索<code>&lt;!ELEMENT</code>,第一个就是html元素:</p> <pre><code class="hljs lang-undefined">&lt;!-- Document Structure --&gt; &lt;!ELEMENT html (head, body)&gt; &lt;!ATTLIST html %i18n; id ID #IMPLIED xmlns %URI; #FIXED "http://www.w3.org/1999/xhtml" &gt; </code></pre> <p>html元素可以包含head和body元素,有id和xmlns属性。其中id可选,xmlns规定为<code>http://www.w3.org/1999/xhtml</code>。</p> <p>接下来看</p> <pre><code class="hljs lang-undefined">&lt;!-- Document Head --&gt; &lt;!ENTITY % head.misc "(script|style|meta|link|object)*"&gt; &lt;!-- content model is %head.misc; combined with a single title and an optional base element in any order --&gt; &lt;!ELEMENT head (%head.misc;, ((title, %head.misc;, (base, %head.misc;)?) | (base, %head.misc;, (title, %head.misc;))))&gt; &lt;!ATTLIST head %i18n; id ID #IMPLIED profile %URI; #IMPLIED &gt; </code></pre> <p>如上面注释所说,head标签必须包含title,可选的base标签,若干可选的script、style、meta、link、object标签。</p> <p>整个DTD文档中包含很多信息。比如,</p> <p>通过搜索<code>EMPTY&gt;</code>你可以找到所有自闭合标签:</p> <pre><code class="hljs lang-undefined">&lt;!ELEMENT base EMPTY&gt; &lt;!ELEMENT meta EMPTY&gt; &lt;!ELEMENT link EMPTY&gt; &lt;!ELEMENT hr EMPTY&gt; &lt;!ELEMENT br EMPTY&gt; &lt;!ELEMENT param EMPTY&gt; &lt;!ELEMENT img EMPTY&gt; &lt;!ELEMENT area EMPTY&gt; &lt;!ELEMENT input EMPTY&gt; &lt;!ELEMENT col EMPTY&gt; </code></pre> <p>通过搜索<code>%Inline;&gt;</code>可以找到只允许包含内联元素的对象:</p> <pre><code class="hljs lang-undefined">&lt;!ELEMENT p %Inline;&gt; &lt;!ELEMENT h1 %Inline;&gt; &lt;!ELEMENT h2 %Inline;&gt; &lt;!ELEMENT h3 %Inline;&gt; &lt;!ELEMENT h4 %Inline;&gt; &lt;!ELEMENT h5 %Inline;&gt; &lt;!ELEMENT h6 %Inline;&gt; &lt;!ELEMENT dt %Inline;&gt; &lt;!ELEMENT address %Inline;&gt; &lt;!ELEMENT span %Inline;&gt; &lt;!ELEMENT bdo %Inline;&gt; &lt;!ELEMENT em %Inline;&gt; &lt;!ELEMENT strong %Inline;&gt; &lt;!ELEMENT dfn %Inline;&gt; &lt;!ELEMENT code %Inline;&gt; &lt;!ELEMENT samp %Inline;&gt; &lt;!ELEMENT kbd %Inline;&gt; &lt;!ELEMENT var %Inline;&gt; &lt;!ELEMENT cite %Inline;&gt; &lt;!ELEMENT abbr %Inline;&gt; &lt;!ELEMENT acronym %Inline;&gt; &lt;!ELEMENT q %Inline;&gt; &lt;!ELEMENT sub %Inline;&gt; &lt;!ELEMENT sup %Inline;&gt; &lt;!ELEMENT tt %Inline;&gt; &lt;!ELEMENT i %Inline;&gt; &lt;!ELEMENT b %Inline;&gt; &lt;!ELEMENT big %Inline;&gt; &lt;!ELEMENT small %Inline;&gt; &lt;!ELEMENT label %Inline;&gt; &lt;!ELEMENT legend %Inline;&gt; &lt;!ELEMENT caption %Inline;&gt; </code></pre> <p>通过查看<code>%Inline</code>(<code>%Block</code>)对应实体的内容,你可以知道有哪些元素是内联(快级)元素。</p> <h1 id="toc-e0d"><a class="anchor" href="#toc-e0d"></a>Java世界中的dtd(xsd)文件</h1> <p>Java世界中经常可以看到各种XML配置文件。XML之于Java就像JSON之于JavaScript。 这些XML都有对应的DTD文件或XSD。</p> <p>比如: <a href="http://mybatis.org/dtd/mybatis-3-mapper.dtd">myBatis Mapper</a> 、<a href="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;">Servlet 的web.xml</a></p> <p>Jave开发使用的的IDE多可以根据DTD(XSD)给出自动完成提示。不过,通过阅读DTD或XSD,我们还是可以从中发现很多有用的信息。其中的注释对我们理解对应框架的运行机制也是很有帮助的。</p> Tue Sep 20 2016 14:56:55 GMT+0800 (CST)http://aztack.wang/post/about-doctype.html打包Electron应用<p>基于Electron开发的客户端最终分发时需要打包。 最简单的方式就是大家喜闻乐见的Portable的压缩包形式。 当有某些特殊需求时,就需要制作安装包了。</p> <!--more--> <h1 id="windows"><a class="anchor" href="#windows"></a>Windows</h1> <p>在Windows下有很多免费、商用的安装包制作工具。功能异常强大。</p> <p>其中比较著名的免费安装包制作工具就是<a href="http://www.jrsoftware.org/isinfo.php">Inno Setup</a> Inno Setup是使用Delphi开发的,使用<a href="http://www.remobjects.com">RemObjects</a>为安装程序增加基于Pascal的脚本支持。</p> <p>当我们的应用需要某些权限时,需要弹出UAC对话框请求授权。 此时需要安装程序将应用对应的EXE文件增加到注册表对应项中。使用Inno Setup可以在注册表段增加需要设置的内容,并可以引用安装程序路径等变量:</p> <h2 id="runasadmin">RunAs Admin</h2> <p>为了让我们的应用启动时就请求管理员权限,可以写入如下内容到注册表:</p> <pre><code class="hljs lang-taggerscript">[Registry] Root: "HKCU"; Subkey: "SOFTWARE<span class="hljs-symbol">\M</span>icrosoft<span class="hljs-symbol">\W</span>indows NT<span class="hljs-symbol">\C</span>urrentVersion<span class="hljs-symbol">\A</span>ppCompatFlags<span class="hljs-symbol">\L</span>ayers<span class="hljs-symbol">\"</span>; ValueType: String; ValueName: "{app}<span class="hljs-symbol">\{</span>#MyAppExeName}"; ValueData: "RUNASADMIN"; Flags: uninsdeletekeyifempty uninsdeletevalue; MinVersion: 0,6.1 </code></pre><p>要注意的是,写入的是HKCU,不是HKLM。打开注册表找到上面的项,你会发现里面已经有一些程序<code>RunAs Admin</code>了。</p> <h2 id="createprocessfailed">CreateProcess Failed</h2> <p>另一个问题:在安装完毕后可以选择立即运行应用程序,此时会出现<code>CreateProcess</code>失败。 此时需要在Run段Flags里增加一个runascurrentuser参数:</p> <pre><code class="hljs lang-avrasm">[Run] <span class="hljs-symbol">Filename:</span> <span class="hljs-string">"{app}\{#MyAppExeName}"</span><span class="hljs-comment">; </span> <span class="hljs-symbol">Description:</span> <span class="hljs-string">"{cm:LaunchProgram,{#StringChange(MyAppName, "</span>&amp;<span class="hljs-string">", "</span>&amp;&amp;<span class="hljs-string">")}}"</span><span class="hljs-comment">; </span> <span class="hljs-symbol">Flags:</span> runascurrentuser nowait postinstall skipifsilent </code></pre><h2 id="toc-946">隐藏安装中的文件路径</h2> <p>前面提到过,Inno Setup使用<code>RemObject</code>为安装程序增加了脚本能力。 在code端可以增加特定事件的处理函数。</p> <p>在[code]段增加下面代码</p> <pre><code class="hljs lang-undefined">procedure InitializeWizard; begin with TNewStaticText.Create(WizardForm) do begin Parent := WizardForm.FilenameLabel.Parent; Left := WizardForm.FilenameLabel.Left; Top := WizardForm.FilenameLabel.Top; Width := WizardForm.FilenameLabel.Width; Height := WizardForm.FilenameLabel.Height; Caption := ""; end; WizardForm.FilenameLabel.Visible := False; end; </code></pre> <p>上面的<code>InitializeWizard</code>是一个回调函数,在安装向导初始化时被调用。上面代码创建了另一个TNewStaticText实例然后覆盖在原有的FilenameLabel上,以达到不显示安装过程时拷贝文件的路径的效果。</p> <p>2016-08-07更新:</p> <p><a href="https://github.com/felicienfrancois/node-innosetup-compiler">node-innosetup-compiler</a> InnoSetup命令行工具ISCC的Node Wrapper</p> <!-- 参考: - http://stackoverflow.com/questions/13302369/inno-setup-how-to-set-installer-run-as-administrator - http://stackoverflow.com/questions/16083187/how-to-create-a-shortcut-to-launch-an-app-with-admin-privileges-from-the-cmd-lin --> <h1 id="mac"><a class="anchor" href="#mac"></a>Mac</h1> <p>Mac下最简单的方式是使用Disk Utility(hdiutil)创建空白dmg文件,然后将要打包的app文件夹和Application文件夹的软连接放到dmg文件中。这个过程中比较tricky的部分是</p> <ol> <li>要实现计算好dmg文件的容量,使其恰好可以容纳app和背景图片</li> <li>如何设置dmg mount后的背景图</li> </ol> <p>好在已经有很多工具可以帮我们完整这个繁琐的过程: 以下几个语言都有对应的DMG生成工具,可以方便的集成到系统中:</p> <ul> <li>Shell: <a href="https://github.com/andreyvit/create-dmg">A shell script to build fancy DMGs</a></li> <li>Ruby:<a href="https://github.com/baldowl/rake_dmg">Ruby Library to build DMG files</a></li> <li>Python:<a href="https://pypi.python.org/pypi/dmgbuild">dmgbuild</a></li> <li>NodeJS:<a href="https://github.com/LinusU/node-appdmg">Generate your app dmgs</a></li> </ul> <h2 id="toc-9c8">使用node-appdmg生成dmg</h2> <p>安装:</p> <pre><code class="hljs lang-undefined">&gt; npm i --save-dev appdmg </code></pre> <p>package.json中增加:</p> <pre><code class="hljs lang-json">{ <span class="hljs-attr">"scripts"</span>: { <span class="hljs-attr">"build"</span>: <span class="hljs-string">"appdmg appdmg.json APPNAME.dmg"</span> } } </code></pre> <p>编辑appdmg.json:</p> <pre><code class="hljs lang-undefined">{ "title": "App Name", "icon": "./.VolumeIcon.icns", //dmg加载后弹窗的图标 "background": "./.background.tiff", //dmg加载后弹出窗口的背景图片 "contents": [{ "x": 448, "y": 140, "type": "link", //应用目录的软链 "path": "/Applications" }, { "x": 192, "y": 140, "type": "file", "path": "./AppName.app" //你的应用目录 }] } </code></pre> <p>运行命令生成dmg文件:</p> <pre><code class="hljs lang-bash">&gt; npm run build &gt; appdmg appdmg.json APPNAME.dmg [ 1/20] Looking <span class="hljs-keyword">for</span> target... [ OK ] [ 2/20] Reading JSON Specification... [ OK ] [ 3/20] Parsing JSON Specification... [ OK ] [ 4/20] Validating JSON Specification... [ OK ] [ 5/20] Looking <span class="hljs-keyword">for</span> files... [ OK ] [ 6/20] Calculating size of image... [ OK ] [ 7/20] Creating temporary image... [ OK ] [ 8/20] Mounting temporary image... [ OK ] [ 9/20] Making hidden background folder... [ OK ] [10/20] Copying background... [ OK ] [11/20] Reading background dimensions... [ OK ] [12/20] Copying icon... [ OK ] [13/20] Setting icon... [ OK ] [14/20] Creating links... [ OK ] [15/20] Copying files... [ OK ] [16/20] Making all the visuals... [ OK ] [17/20] Blessing image... [ OK ] [18/20] Unmounting temporary image... [ OK ] [19/20] Finalizing image... [ OK ] [20/20] Removing temporary image... [ OK ] Your image is ready: APPNAME.dmg </code></pre> <p>在dmg中可以在背景,在背景中写上客服电话。还可以在dmg里放上webloc文件,方便访问官网和帮助页面。</p> <h1 id="toc-b7a"><a class="anchor" href="#toc-b7a"></a>Flash缓存文件路径</h1> <p>Windows: 通过setPath设置userData路径。各种缓存都保存在这里</p> <pre><code class="hljs lang-javascript">app.setPath(<span class="hljs-string">"userData"</span>, path + <span class="hljs-string">"/user_data"</span>); </code></pre> <blockquote> <p>所以打包的时候要记得删除缓存!</p> </blockquote> <p>Mac下Flash的SharedObject保存在:</p> <pre><code class="hljs lang-gradle">~<span class="hljs-regexp">/Library/</span>Preferences<span class="hljs-regexp">/Macromedia/</span>Flash Player<span class="hljs-regexp">/#SharedObjects/</span> </code></pre><p>另外,sol文件编辑工具:<a href="http://blog.coursevector.com/minerva">minerva</a></p> <h1 id="toc-f87"><a class="anchor" href="#toc-f87"></a>其他Electron相关文章和链接</h1> <ul> <li><a href="http://electron.atom.io/docs/tutorial/using-pepper-flash-plugin/#add-electron-switch">Using Pepper Flash Plugin</a></li> </ul> <!--- 参考:[Building Custom DMG Background Image](https://www.youtube.com/watch?v=g0llvS40AqE) -->Fri Jun 24 2016 00:00:00 GMT+0800 (CST)http://aztack.wang/post/packe-electron-apps.html为博客启用HTTPS<p><a href="https://buy.wosign.com">沃通</a> 提供2年的免费证书,到期还可以免费续。<a href="https://buy.wosign.com/free/#ssl">点击这里</a>免费申请。</p> <!--more--> <h1 id="toc-99b"><a class="anchor" href="#toc-99b"></a>提交申请,验证域名</h1> <p>提交域名,按提示填写信息。 然后将wosign给的验证用的html放到服务器根目录。需要在nginx配置中增加如下配置:</p> <pre><code class="hljs lang-nginx"><span class="hljs-attribute">location</span> /your.domain.html { <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> = <span class="hljs-number">404</span>; } </code></pre> <p>验证完后删除这段。</p> <h1 id="toc-6f0"><a class="anchor" href="#toc-6f0"></a>下载证书配置</h1> <p>下载生成的证书(zip),并妥善保存。</p> <p>将下载的压缩包里的nginx目录下的crt和key文件放到服务器中,并在nginx配置文件中进行配置。</p> <h1 id="toc-5db"><a class="anchor" href="#toc-5db"></a>小结</h1> <p>在此之前用的是<code>Let&#39;s Encrypt</code>的90天免费证书。需要定时脚本定期更新证书。折腾一番后一劳永逸。但是七牛的CDN存储不支持短于等于90天的证书。所以需要使用七牛存储的可以考虑用沃通的2年免费证书。</p> Sun Jun 12 2016 11:14:44 GMT+0800 (CST)http://aztack.wang/post/enable-https-for-myblog.html认识Webpack<p><img src="/static/img/blog/webpack-logo.png" alt="Webpack-logo"></p> <p>网上已经有不少Webpack教程入门教程了。 本文记录了我以我的方式方法、思路认识了解Webpack。从官方的Tutorial入手,不断提出问题、解决,一步一步认识Webpack。</p> <!--more--> <p>从早期的自己写脚本,到现在的各种构建工具,前端工程化已经发展到新的阶段了。</p> <p>早先在百度地图的时候,地图代码用PHP进行简单粗放的处理。这个阶段算是最原始的自己写脚本处理。后来我用Ruby写了一套集合了开发、动态合并、mock数据、一键build的工具。这算是更进了一步。</p> <p>现在基于Nodejs的任务管理工具Grunt、Glup都提供了代码合并、压缩、各种JS Transpiler、CSS预处理、各种前端模板的处理。</p> <p>在Grunt、Gulp中是通过第三方库进行编译的。 在Webpack中也是类似的,只不过是增加了Loader的概念。通过一系列“Loader”完成处理。 处理之后统一输出为JS代码。</p> <h1 id="toc-bd6"><a class="anchor" href="#toc-bd6"></a>初步认识</h1> <p>在深入之前,你需要先照着<a href="http://webpack.github.io/docs/tutorials/getting-started/">官方教程</a>实践一下。有个感性的认识。 完成教程的getting started部分后,你可以初步得出以下结论:</p> <ol> <li>Webpack是一个用来打包js工程的工具。官方定义为<code>Module bundler</code></li> <li>Webpack命令行参数可以配置到名为<code>webpack.config.js</code>(或<code>webpackfile</code>)的配置文件中</li> <li>Webpack提供一个可以检测文件变化并编译然后刷新浏览器的<code>webpack-dev-server</code></li> </ol> <p><strong>那么问题来了</strong>: 对于如Grunt、Webpack这种通过配置工作的工具来说,有哪些配置可用,配置的行为、配置的可选值,需要完备的文档才好会用。 幸好Webpack官网提供了详尽的<a href="http://webpack.github.io/docs/configuration.html">文档</a></p> <p>下面是主要配置项的简要说明</p> <pre><code class="hljs lang-stylus">context 工程目录,必须是绝对路径 entry 打包生成的bundle。可以是多个 output 生成的文件配置选项 output<span class="hljs-selector-class">.filename</span> 生成的文件名模板,比如 <span class="hljs-string">"[name].bundle.js"</span> output<span class="hljs-selector-class">.path</span> 生成的文件目录,绝对路径 output<span class="hljs-selector-class">.publicPath</span> 线上静态资源目录 output<span class="hljs-selector-class">.chunkFilename</span> 代码块文件名模板 output<span class="hljs-selector-class">.sourceMapFilename</span> source-map文件名模板。默认是[file]<span class="hljs-selector-class">.map</span> output<span class="hljs-selector-class">.devtoolModuleFilenameTemplate</span> output<span class="hljs-selector-class">.devtoolFallbackModuleFilenameTemplate</span> output<span class="hljs-selector-class">.devtoolLineToLine</span> output<span class="hljs-selector-class">.hotUpdateChunkFilename</span> output<span class="hljs-selector-class">.hotUpdateMainFilename</span> output<span class="hljs-selector-class">.jsonpFunction</span> JSONP异步加载代码块(chunk)时JSONP函数名,默认是webpackJsonp output<span class="hljs-selector-class">.hotUpdateFunction</span> JSONP异步热更新代码块时JSONP函数名,默认是webpackHotUpdate output<span class="hljs-selector-class">.pathinfo</span> 是否以注释形式在require中增加模块path信息 output<span class="hljs-selector-class">.library</span> bundle作为库输出,值为库名 output<span class="hljs-selector-class">.libraryTarget</span> 输出库的格式。比如可选amd,umd,commonjs等 output<span class="hljs-selector-class">.umdNamedDefine</span> output<span class="hljs-selector-class">.sourcePrefix</span> output<span class="hljs-selector-class">.crossOriginLoading</span> module module<span class="hljs-selector-class">.loaders</span> Loader配置 module<span class="hljs-selector-class">.preLoaders</span>, module<span class="hljs-selector-class">.postLoaders</span> preLoader和postLoader配置 module<span class="hljs-selector-class">.noParse</span> 不需要loader编译的文件 resolve 模块决议配置 resolve<span class="hljs-selector-class">.alias</span> 模块别名 resolve<span class="hljs-selector-class">.root</span> 模块根目录,绝对路径 resolve<span class="hljs-selector-class">.modulesDirectories</span> 模块目录,工作方式类似node_modules。默认值是[<span class="hljs-string">"web_modules"</span>, <span class="hljs-string">"node_modules"</span>] resolve<span class="hljs-selector-class">.fallback</span> 如果在root和modulesDirectories都找不到,会在这里搜索 resolve<span class="hljs-selector-class">.extensions</span> 用于模块查找的扩展名。 resolve<span class="hljs-selector-class">.packageMains</span> resolve<span class="hljs-selector-class">.packageAlias</span> resolve<span class="hljs-selector-class">.unsafeCache</span> resolveLoader 与resolve类似,不过是给loader模块决议使用的配置 resolveLoader<span class="hljs-selector-class">.moduleTemplates</span> externals target 目标环境,代码是用于web还是node还是electron环境等等 bail profile 每个模块的时间打点信息 cache 是否开启编译缓存以提高性能。watch模式默认开启 debug 设置loaders为debug模式 devtool 用于方便调试的开发工具选项。比如source-map方便调试混淆后的代码 devServer 传给webpack-dev-server的参数 node 传递给node作为polyfills和mocks的参数 amd require.mad和define.amd对应的值。比如{jQuery:true} loader 提供给loader的额外信息 recordsPath, recordsInputPath, recordsOutputPath plugins 插件配置 </code></pre><h2 id="loaders">Loaders</h2> <p><strong>我们最关心的是有哪些loader可以用呢?</strong> 通过在 <a href="https://github.com/webpack">https://github.com/webpack</a> 搜索项目名中包含<code>-loader</code>。我找到了这些官方提供的loader:</p> <pre><code class="hljs lang-mipsasm"><span class="hljs-comment">#裸数据</span> raw-loader <span class="hljs-comment">#脚本代码</span> coffee-loader <span class="hljs-keyword">script-loader </span> <span class="hljs-comment">#样式相关</span> css-loader style-loader less-loader <span class="hljs-comment">#html相关</span> html-loader <span class="hljs-keyword">jade-loader </span> <span class="hljs-comment">#json相关</span> <span class="hljs-keyword">json-loader </span><span class="hljs-keyword">json5-loader </span> <span class="hljs-comment">#其他</span> worker-loader-loader imports-loader exports-loader source-map-loader coffee-redux-loader <span class="hljs-keyword">multi-loader </span>react-proxy-loader expose-loader url-loader node-loader <span class="hljs-keyword">bundle-loader </span>val-loader transform-loader <span class="hljs-keyword">jshint-loader </span>null-loader coverjs-loader </code></pre><p>咦,为什么有一个css-loader还有一个style-loader?css-loader是用来加载css文件的 style-loader是用来应用已经加载的css中的样式的。</p> <p>在配置文件中,配置需要使用的loader。<code>test</code>用来对文件名进行匹配测试,匹配成功的文件会用对应的loader处理。</p> <pre><code class="hljs lang-js"><span class="hljs-built_in">module</span>: { loaders: [ { test: <span class="hljs-regexp">/\.coffee$/</span>, loader: <span class="hljs-string">"coffee-loader"</span> }, { test: <span class="hljs-regexp">/\.js$/</span>, loader: <span class="hljs-string">"jsx-loader"</span> } ] }, </code></pre> <p>每个loader都有自己独特的配置,需要参考对应文档。 所有loader都可以配置以下项目:</p> <pre><code class="hljs lang-gradle">test 用来对文件名进行匹配测试 <span class="hljs-keyword">exclude</span> 被排除的文件名 <span class="hljs-keyword">include</span> 包含的文件名 loader 叹号分割的loaders loaders loader数组 </code></pre><p>比如<a href="https://github.com/babel/babel-loader">babel</a>的配置就有query、cacheDirectory配置项。</p> <p>可以想象,loader要做的工作无非就是拿到源码,根据参数配置进行变换,返回变换后的结果。看一下<code>less-loder</code>的源代码:</p> <pre><code class="hljs lang-js"><span class="hljs-comment">/** * 简化后的伪代码 */</span> <span class="hljs-keyword">var</span> less = <span class="hljs-built_in">require</span>(<span class="hljs-string">"less"</span>); <span class="hljs-keyword">var</span> loaderUtils = <span class="hljs-built_in">require</span>(<span class="hljs-string">"loader-utils"</span>); <span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">source</span>)</span>{ <span class="hljs-comment">//解析loader的query string</span> <span class="hljs-keyword">var</span> query = loaderUtils.parseQuery(<span class="hljs-keyword">this</span>.query); <span class="hljs-comment">//默认less编译配置</span> <span class="hljs-keyword">var</span> config = { filename: <span class="hljs-keyword">this</span>.resource, compress: !!<span class="hljs-keyword">this</span>.minimize }; <span class="hljs-comment">//将query中的配置merge到默认配置中</span> <span class="hljs-built_in">Object</span>.keys(query).forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">attr</span>)</span>{ config[attr] = query[attr] }); <span class="hljs-comment">//编译less</span> <span class="hljs-keyword">var</span> cb = <span class="hljs-keyword">this</span>.callback; less.render(source, config, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e, result</span>)</span>{ cb(<span class="hljs-literal">null</span>, result.css, result.map); }); }; </code></pre> <p>基本上就是从query读取配置,调用less编译器编译源码。</p> <p>官网编写loader的<a href="http://webpack.github.io/docs/how-to-write-a-loader.html">教程</a>验证了上述想法。同时也指出了编写loader时要注意的一些问题。</p> <p>参考:官方给出的已有loader<a href="http://webpack.github.io/docs/list-of-loaders.html">列表</a></p> <h2 id="plugins">Plugins</h2> <p><strong>有哪些plugin呢?</strong> 通过在 <a href="https://github.com/webpack">https://github.com/webpack</a> 搜索项目名中包含<code>-plugin</code>我找到了这些官方提供的plugin:</p> <pre><code class="hljs lang-stata">extract-text-webpack-<span class="hljs-keyword">plugin</span> compression-webpack-<span class="hljs-keyword">plugin</span> i18n-webpack-<span class="hljs-keyword">plugin</span> component-webpack-<span class="hljs-keyword">plugin</span> </code></pre><p>感觉不对啊,那个很多教程中常见的<code>UglifyJsPlugin</code>都没有看到啊!那么只有一个可能,这些plugin都是内置的。在源代码中一定能找到。clone下来webpack的代码。打开lib,满眼都是XXXPlugin。在optimize目录下可以找到<code>UglifyJsPlugin</code>。大致看一下这些代码可以发现,每个Plugin的原型上都有一个apply函数:</p> <pre><code class="hljs lang-js"><span class="hljs-comment">/** * 从UglifyJsPlugin.js简化而来的伪代码 */</span> ... var uglify = <span class="hljs-built_in">require</span>(<span class="hljs-string">"uglify-js"</span>); ... UglifyJsPlugin.prototype.apply = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">compiler</span>) </span>{ ... compiler.plugin(<span class="hljs-string">"compilation"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">module</span>) </span>{ ... var input = asset.source(); <span class="hljs-keyword">var</span> ast = uglify.parse(input); <span class="hljs-comment">//压缩</span> <span class="hljs-keyword">if</span> (options.compress !== <span class="hljs-literal">false</span>) { <span class="hljs-keyword">var</span> compress = uglify.Compressor(options.comrpess); ast = ast.transform(compress); } <span class="hljs-comment">//混淆</span> <span class="hljs-keyword">if</span> (options.mangle !== <span class="hljs-literal">false</span>) { ast.mangle_names(); uglify.mangle_properties(ast); } <span class="hljs-comment">//重新从ast生成代码</span> <span class="hljs-keyword">var</span> result = uglify.OutputStream(); ast.print(result); }); }; </code></pre> <p>可以想象,webpack会根据配置文件中plugins数组中的配置实例化插件,然后在适当的时候调用其apply函数。 在apply函数中,插件对感兴趣的事件(官方叫做stage)注册处理函数(<code>plugin</code>)。比如<code>UglifyJsPlugin</code>就是在<code>compilation</code>事件触发时,对源代码进行压缩混淆。</p> <p>通过官网阅读<a href="http://webpack.github.io/docs/how-to-write-a-plugin.html">how-to-write-a-plugin</a>可以验证了上面的想法。</p> <p>既然有<code>compilation</code>事件,那肯定还有其他事件喽。在lib目录下搜索源代码中的<code>compile.plugin</code>调用</p> <pre><code class="hljs lang-bash">$ ack -Q compiler.plugin( | grep plugin|gawk <span class="hljs-string">"{print(<span class="hljs-variable">$2</span>)}"</span>|sort|uniq compiler.plugin(<span class="hljs-string">"additional-pass"</span>, compiler.plugin(<span class="hljs-string">"after-compile"</span>, compiler.plugin(<span class="hljs-string">"after-environment"</span>, compiler.plugin(<span class="hljs-string">"after-resolvers"</span>, compiler.plugin(<span class="hljs-string">"compilation"</span>, compiler.plugin(<span class="hljs-string">"compile"</span>, compiler.plugin(<span class="hljs-string">"context-module-factory"</span>, compiler.plugin(<span class="hljs-string">"done"</span>, compiler.plugin(<span class="hljs-string">"emit"</span>, compiler.plugin(<span class="hljs-string">"entry-option"</span>, compiler.plugin(<span class="hljs-string">"environment"</span>, compiler.plugin(<span class="hljs-string">"invalid"</span>, compiler.plugin(<span class="hljs-string">"make"</span>, compiler.plugin(<span class="hljs-string">"normal-module-factory"</span>, compiler.plugin(<span class="hljs-string">"run"</span>, compiler.plugin(<span class="hljs-string">"should-emit"</span>, compiler.plugin(<span class="hljs-string">"this-compilation"</span>, compiler.plugin(<span class="hljs-string">"watch-run"</span>, </code></pre> <p>一共有18个事件。官方教程中只介绍了<code>done,compilation,emit</code>三个。 用同样的方法,我们还可以查出<code>compilation</code>支持的事件:</p> <pre><code class="hljs lang-bash">$ ack -Q compilation.plugin( | grep plugin|gawk <span class="hljs-string">"{print(<span class="hljs-variable">$2</span>,<span class="hljs-variable">$3</span>)}"</span>|sort|uniq compilation.plugin(<span class="hljs-string">"additional-assets"</span>, <span class="hljs-keyword">function</span>(callback) compilation.plugin(<span class="hljs-string">"additional-chunk-assets"</span>, <span class="hljs-keyword">function</span>() compilation.plugin(<span class="hljs-string">"after-hash"</span>, <span class="hljs-keyword">function</span>() compilation.plugin(<span class="hljs-string">"after-optimize-chunk-assets"</span>, <span class="hljs-keyword">function</span>(chunks) compilation.plugin(<span class="hljs-string">"after-optimize-tree"</span>, <span class="hljs-keyword">function</span>(chunks, compilation.plugin(<span class="hljs-string">"before-module-ids"</span>, <span class="hljs-keyword">function</span>(modules) compilation.plugin(<span class="hljs-string">"build-module"</span>, <span class="hljs-keyword">function</span>(module) compilation.plugin(<span class="hljs-string">"chunk-hash"</span>, <span class="hljs-keyword">function</span>(chunk, compilation.plugin(<span class="hljs-string">"failed-module"</span>, moduleDone); compilation.plugin(<span class="hljs-string">"need-additional-pass"</span>, <span class="hljs-keyword">function</span>() compilation.plugin(<span class="hljs-string">"normal-module-loader"</span>, <span class="hljs-keyword">function</span>(context, compilation.plugin(<span class="hljs-string">"normal-module-loader"</span>, <span class="hljs-keyword">function</span>(loaderContext) compilation.plugin(<span class="hljs-string">"normal-module-loader"</span>, <span class="hljs-keyword">function</span>(loaderContext, compilation.plugin(<span class="hljs-string">"optimize-assets"</span>, <span class="hljs-keyword">function</span>(assets, compilation.plugin(<span class="hljs-string">"optimize-chunk-assets"</span>, <span class="hljs-keyword">function</span>(chunks, compilation.plugin(<span class="hljs-string">"optimize-chunk-ids"</span>, <span class="hljs-keyword">function</span>(chunks) compilation.plugin(<span class="hljs-string">"optimize-chunk-order"</span>, <span class="hljs-keyword">function</span>(chunks) compilation.plugin(<span class="hljs-string">"optimize-chunks-advanced"</span>, <span class="hljs-keyword">function</span>(chunks) compilation.plugin(<span class="hljs-string">"optimize-chunks-basic"</span>, <span class="hljs-keyword">function</span>(chunks) compilation.plugin(<span class="hljs-string">"optimize-module-order"</span>, <span class="hljs-keyword">function</span>(modules) compilation.plugin(<span class="hljs-string">"optimize-modules-advanced"</span>, <span class="hljs-keyword">function</span>(modules) compilation.plugin(<span class="hljs-string">"optimize-tree"</span>, <span class="hljs-keyword">function</span>(chunks, compilation.plugin(<span class="hljs-string">"record"</span>, <span class="hljs-keyword">function</span>(compilation, compilation.plugin(<span class="hljs-string">"record-chunks"</span>, <span class="hljs-keyword">function</span>(chunks, compilation.plugin(<span class="hljs-string">"record-modules"</span>, <span class="hljs-keyword">function</span>(modules, compilation.plugin(<span class="hljs-string">"revive-chunks"</span>, <span class="hljs-keyword">function</span>(chunks, compilation.plugin(<span class="hljs-string">"revive-modules"</span>, <span class="hljs-keyword">function</span>(modules, compilation.plugin(<span class="hljs-string">"seal"</span>, <span class="hljs-keyword">function</span>() compilation.plugin(<span class="hljs-string">"should-generate-chunk-assets"</span>, <span class="hljs-keyword">function</span>() compilation.plugin(<span class="hljs-string">"should-record"</span>, <span class="hljs-keyword">function</span>() compilation.plugin(<span class="hljs-string">"succeed-module"</span>, moduleDone); compilation.plugin([<span class="hljs-string">"optimize-chunks"</span>, <span class="hljs-string">"optimize-extracted-chunks"</span>], compilation.plugin([<span class="hljs-string">"optimize-chunks-basic"</span>, <span class="hljs-string">"optimize-extracted-chunks-basic"</span>], </code></pre> <p>有了这两个列表,在自己编写插件就可以有的放矢地参考源代码了。</p> <p>参考:官方给出的已有plugin的<a href="http://webpack.github.io/docs/list-of-plugins.html">列表</a></p> <h1 id="webpack-dev-server"><a class="anchor" href="#webpack-dev-server"></a>Webpack-dev-server</h1> <p>Webpack提供一个小巧的基于express的开发服务器。支持自动刷新、模块热替换。还有代理。具体如何配置在<a href="http://webpack.github.io/docs/webpack-dev-server.html">这里</a>。</p> <p>代理(<code>proxy</code>)在开发是还是很有用的。你可以将动态请求映射到后端的开发机,方便联调。</p> <h1 id="toc-25f"><a class="anchor" href="#toc-25f"></a>总结</h1> <p>现在照着官方教程你已经可以简单地使用Webpack了。下一步要做的是</p> <ul> <li>了解webpack.config.js中如何配置,有哪些要注意的(比如路径)</li> <li>实践常用的Loader和Plugin</li> <li>实践webpack的众多配置项</li> <li>实践使用webpack-dev-server进行开发</li> </ul> <p>需要时可以更进一步:</p> <ul> <li>学习如何编写Loader和Plugin</li> <li>阅读已有Loader和Plugin的源码</li> <li>在源码中了解上面列出的stages的含义</li> </ul> <h1 id="toc-2bc"><a class="anchor" href="#toc-2bc"></a>更新:Webpack、Browserify和Gulp三者之间到底是怎样的关系?</h1> <p>下面是我在知乎的<a href="https://www.zhihu.com/question/37020798/answer/85873465">回答</a>:</p> <h2 id="taskrunner">Task Runner</h2> <p>Gulp、Grunt和Make(常见于c/cpp)、Ant、Maven、Gradle(Java/Android)、Rake、Thor(Ruby)一样,都是是Task Runner。用来将一些繁琐的task自动化并处理任务的依赖关系。 其中有些是基于配置描述的,描述逻辑比较费劲,比如Ant基于xml。还有些就是代码,比较灵活,个人偏好这种。比如Rake、Thor、Gulp、Gradle。对于Gradle来说也有些蛋疼。因为它本身是Groovy的DSL。如果要深入使用,你还得学一下Groovy语言。其他就好多了Rake、Thor就是写Ruby;Gulp就是JavaScript。相对门槛低很多。</p> <h2 id="toc-18e">模块化解决方案</h2> <p>Browserify It provides a way to bundle CommonJS modules together, adheres to the Unix philosophy(小工具协作), is in fact a good alternative to Webpack. Webpack takes a more monolithic(整体解决、大而全) approach than Browserify... is relies on configuration.</p> <p>上面这些工具在功能上有交集:代码的Minify、Concat;资源预处理等;</p> <p>其实每个工具的官网上都有对工具的设计思想、要解决的问题、与其他工具的对比。自己摘抄下来,做个表格对比一下。高亮出每个工具独特的特性。这样你就知道什么时候需要用哪个工具了。 比如,你的工程模块依赖很简单,不需要把js或各种资源打包,只需要简单的合并、压缩,在页面中引用就好了。那就不需要Browserify、Webpack。Gulp就够用了。</p> <p>反过来,如果你的工程庞大,页面中使用了很多库(SPA很容易出现这种情况),那就可以选择某种模块化方案。至于是用Browserify还是Webpack就需要根据其他因素来判断了。比如团队已经在使用了某种方案,大家都比较熟悉了。再比如,你喜欢Unix小工具协作的方式,那就Browserify。</p> <p>充分了解各种工具、方案,选择合适的和自己需要的。没有绝对的好。优点换了场景也会变成缺点。</p> <p>UPDATE</p> <p>下面是<a href="http://hotoo.me/">闲耘™</a>用Makefile管理前端工程任务的例子: <a href="https://github.com/hotoo/pinyin/blob/master/Makefile">https://github.com/hotoo/pinyin/blob/master/Makefile</a></p> <p>更多资料:</p> <ul> <li><a href="http://webpack.github.io/docs/webpack-for-browserify-users.html">Webpack for Browserify Users</a></li> <li><a href="http://survivejs.com/webpack/introduction/">SurviveJS Webpack Introduction</a></li> </ul> Sat Aug 01 2015 15:55:00 GMT+0800 (CST)http://aztack.wang/post/about-webpack.htmlWindows下Grunt使用编译好的Sassc<p>现在稍具规模的项目中已经很少裸写CSS了。 流行的css预处理器有sass、less、stylus。</p> <p>因为对Ruby比较熟悉,很早就了解了Sass。所以目前我负责的几个项目中均选用了sass。</p> <p>我建议先了解一下这几个预处理器的异同和各自的优势。根据项目情况进行选择。三者的语法都不是很复杂。有经验的前端开发人员都可以很快上手。 <!--more--></p> <p>Sass最初是用Ruby编写的。最大的问题就是编译速度比较慢。然后就有了<a href="https://github.com/sass/sassc">Sass的c++版编译器</a>本和<a href="https://github.com/sass/node-sass">node-sass</a>。虽然速度不再是问题了。但是安装编译有比较麻烦。使用Windows的环境的小伙伴们更是觉得麻烦。因为需要安装Visual Studio用于编译。幸好我的笔记本上常备VS。于是我先在本机编译好。然后写了端Grunt脚本配合编译出来的<a href="http://7xslw6.com2.z0.glb.clouddn.com/bin/sassc.exe">sassc.exe</a>填上了这个自己挖的坑。</p> <p>脚本中先判断系统,如果是win32则使用sassc.exe编译,否则使用node-sass。 用grunt.event.on(&#39;watch&#39;...)对文件变化监控,当发生sass文件创建和修改事件时,调用sassc进行编译。</p> <pre><code class="hljs lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">grunt</span>) </span>{ <span class="hljs-keyword">var</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>), fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>), isWin = <span class="hljs-regexp">/^win/</span>.test(process.platform); <span class="hljs-keyword">var</span> config = { watch: { sass: { files: [<span class="hljs-string">"src/sass/**/*.scss"</span>], tasks: [<span class="hljs-string">"sass:dev"</span>, <span class="hljs-string">"notify:sass"</span>, <span class="hljs-string">"cssmin"</span>] }, css: { files: [<span class="hljs-string">"*.css"</span>, <span class="hljs-string">"!*.min.css"</span>], tasks: [<span class="hljs-string">"cssmin"</span>] }, coffee: { files: [<span class="hljs-string">"src/scripts/**/*.coffee"</span>], tasks: [<span class="hljs-string">"coffee:dev"</span>, <span class="hljs-string">"notify:coffee"</span>] } } }; grunt.log.writeln(<span class="hljs-string">"Platform:"</span> + process.platform); <span class="hljs-keyword">var</span> defaultTasks = [<span class="hljs-string">"coffee"</span>], sassOpts = { sassStyle: <span class="hljs-string">"compact"</span> }; <span class="hljs-keyword">if</span> (isWin) { <span class="hljs-comment">/** * When running under Windows, use sassc.exe * which is much faster than ruby version */</span> <span class="hljs-keyword">var</span> useSourceMap = <span class="hljs-literal">false</span>; config.watch.sass.tasks.shift(); <span class="hljs-keyword">var</span> exec = <span class="hljs-built_in">require</span>(<span class="hljs-string">"child_process"</span>).exec; grunt.event.on(<span class="hljs-string">"watch"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">action, filepath, style</span>) </span>{ <span class="hljs-keyword">if</span> (style == <span class="hljs-string">"sass"</span>) style = <span class="hljs-string">"compact"</span> <span class="hljs-keyword">if</span> (action == <span class="hljs-string">"changed"</span> &amp;&amp; path.extname(filepath) == <span class="hljs-string">".scss"</span>) { grunt.log.writeln(<span class="hljs-string">"compiling: "</span> + filepath); <span class="hljs-keyword">var</span> csspath = <span class="hljs-string">"www/resource/css/app/"</span> + path.basename(filepath).replace(<span class="hljs-regexp">/\.scss$/</span>, <span class="hljs-string">".css"</span>), scssOpt = <span class="hljs-string">"-t "</span> + (style || sassOpts.sassStyle) + <span class="hljs-string">" "</span> + (useSourceMap ? <span class="hljs-string">"-m "</span> : <span class="hljs-string">" "</span>), cmd = <span class="hljs-string">"sassc.exe "</span> + scssOpt + <span class="hljs-string">" "</span><span class="hljs-string">" + filepath + "</span><span class="hljs-string">" "</span><span class="hljs-string">" + csspath + "</span><span class="hljs-string">""</span>; grunt.log.writeln(cmd); exec(cmd, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">error, stdout, stderr</span>) </span>{ <span class="hljs-keyword">if</span> (stdout) grunt.log.writeln(<span class="hljs-string">" stdout: "</span> + stdout); <span class="hljs-keyword">if</span> (stderr) grunt.log.writeln(<span class="hljs-string">" stderr: "</span> + stderr); grunt.log.writeln(<span class="hljs-string">"Done!"</span>); }); } }); grunt.registerTask(<span class="hljs-string">"sassc"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ fs.readdirSync(<span class="hljs-string">"src/sass"</span>).forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">name</span>) </span>{ <span class="hljs-keyword">if</span> (name.indexOf(<span class="hljs-string">".scss"</span>) &lt; <span class="hljs-number">0</span> || name.indexOf(<span class="hljs-string">"variables"</span>) &gt;= <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span>; <span class="hljs-keyword">var</span> path = <span class="hljs-string">"src/sass/"</span> + name; grunt.event.emit(<span class="hljs-string">"watch"</span>, <span class="hljs-string">"changed"</span>, path, <span class="hljs-string">"compressed"</span>); }); }); defaultTasks.push(<span class="hljs-string">"sassc"</span>); } <span class="hljs-keyword">else</span> { <span class="hljs-comment">/** * fallback to grunt-contrib-sass */</span> config.sass = { options: { sourcemap: <span class="hljs-string">"auto"</span>, style: <span class="hljs-string">"compact"</span> }, dev: { expand: <span class="hljs-literal">true</span>, cwd: <span class="hljs-string">"src/sass"</span>, src: <span class="hljs-string">"**/*.scss"</span>, dest: <span class="hljs-string">"www/resource/css/app"</span>, ext: <span class="hljs-string">".css"</span>, } }; grunt.loadNpmTasks(<span class="hljs-string">"grunt-contrib-sass"</span>); defaultTasks.push(<span class="hljs-string">"sass"</span>); } }; </code></pre> Sun Apr 19 2015 20:59:56 GMT+0800 (CST)http://aztack.wang/post/remedy-node-sass-grunt-script.html西班牙之旅<p>之前我和老婆结婚时和伴郎伴娘约定的,有一天一起去西班牙。这天来到了~只不过伴娘木有档期。只有我和老婆,加上伴郎和他老婆四个人了。出(cu)发(fa)!</p> <p><img src="/static/img/blog/spain2015-03-20%20021305.jpg" alt="路过蜜汁拥堵的七岔路口大山子"></p> <p>路过蜜汁拥堵的七岔路口大山子。镜头中间前方就是360大厦。</p> <p>此次搭乘的是<a href="http://www.finnair.com/">芬兰航空</a>。我只能说,芬兰也是个战斗民族!不管是晴空还是雨雪,爬升时都会剧烈颠簸!回程时简直吓尿了! <!--more--></p> <h1 id="hersinki"><a class="anchor" href="#hersinki"></a>Hersinki</h1> <p><img src="/static/img/blog/spain2015-03-31%20085911.jpg" alt=""></p> <p>降落赫尔辛基机场的时候正开始下雪,然后渐渐开始下大了。 <img src="/static/img/blog/spain2015-03-20%20153541.jpg" alt=""></p> <p>回程的时候天气还好。只是那云彩看着非常低。</p> <h1 id="madrid"><a class="anchor" href="#madrid"></a>Madrid</h1> <p>西班牙第一站,<a href="https://www.google.com/maps/place/Madrid,+Spain">马德里</a>! <a href="https://www.google.com/maps/place/Royal+Palace+of+Madrid/@40.4181832,-3.7138558,17z/data=!4m2!3m1!1s0x0000000000000000:0x2e7fec79d6ce4851"><img src="/static/img/blog/spain%E9%A9%AC%E5%BE%B7%E9%87%8C%E5%B8%82%E6%94%BF%E5%B9%BF%E5%9C%BA.jpg" alt="马德里市政广场"></a></p> <p>马德里市政广场</p> <h1 id="granada"><a class="anchor" href="#granada"></a>Granada</h1> <p><a href="https://www.google.com/maps/place/Granada,+Spain/@37.1809792,-3.6087813,14z/data=!4m2!3m1!1s0xd71fce62d32c27d:0x9258f79dd3600d72"><img src="/static/img/blog/spain%E5%88%9D%E5%88%B0%E6%A0%BC%E6%8B%89%E7%BA%B3%E8%BE%BE.jpg" alt="初到格拉纳达"></a></p> <h1 id="toledo"><a class="anchor" href="#toledo"></a>Toledo</h1> <p>Toledo并不在计划之内。在去Granada的路上路过而已。不过小城真是有文化有景色。</p> <p><img src="/static/img/blog/spaintoledo-1.jpg" alt=""></p> <p><img src="/static/img/blog/spaintoledo-2.jpg" alt=""></p> <p><img src="/static/img/blog/spaintoledo-3.jpg" alt=""></p> <p>看一下Lonely Planet上的Toledo的夜景,美翻了~</p> <p><img src="/static/img/blog/spaintoledo-perfect.jpg" alt=""></p> <h1 id="ronda"><a class="anchor" href="#ronda"></a>Ronda</h1> <p><img src="/static/img/blog/2015-03-24%20130305.jpg" alt=""></p> <p><img src="/static/img/blog/2015-03-25%20074528.jpg" alt=""></p> <p><img src="/static/img/blog/2015-03-25%20074451.jpg" alt=""></p> <p><img src="/static/img/blog/2015-03-24%20151417.jpg" alt=""></p> <p><img src="/static/img/blog/2015-03-24%20182313.jpg" alt=""></p> <h1 id="tarifa"><a class="anchor" href="#tarifa"></a>Tarifa</h1> <p>Tarifa位于欧洲最南端,与非洲大陆隔海相望。这里是世界上最适合海上运动的海滩之一。 <img src="/static/img/blog/2015-03-25%20135329.jpg" alt=""></p> <p>3月游人并不多</p> <p><img src="/static/img/blog/2015-03-25%20133838.jpg" alt=""></p> <p><img src="/static/img/blog/2015-03-25%20194805.jpg" alt=""></p> <p><img src="/static/img/blog/2015-03-25%20134313.jpg" alt=""></p> <p><img src="/static/img/blog/2015-03-25%20140603.jpg" alt=""></p> <p><img src="/static/img/blog/2015-03-25%20133809.jpg" alt=""></p> <h1 id="malaga"><a class="anchor" href="#malaga"></a>Malaga</h1> <h1 id="barcelona"><a class="anchor" href="#barcelona"></a>Barcelona</h1> Wed Apr 01 2015 22:46:50 GMT+0800 (CST)http://aztack.wang/post/spain-2015.html使用CoffeeScript时遇到的一些坑<p>在CoffeeScript刚推出的时候关注过,那时候编译出来的JavaScript代码调试起来是个问题。经过不断改进,编译出的JavaScript代码可读性已经不再是问题了。</p> <p>CoffeeScript是一个CoffeeScript语言到JavaScript的Transcompiler。其语法受到Ruby,Python的印象。如果你曾经使用过Ruby、Python。那么上手CoffeeScript是分分钟的事情。 <!--more--> 比如</p> <ul> <li>Parallel Assignment(ES6中的Destructing,<code>a, b = [1,2]</code>)</li> <li>Postfix modifiers(放在表达式后面的<code>exp if/unless/while</code>)</li> <li>Existential operator(<code>array.include?</code>)</li> <li>Array comprehensions等。(<code>x for x in array</code>)</li> <li>Fat Arraow (x = =&gt; {})</li> <li>String interpolation(&quot;#{variable} world&quot;)</li> </ul> <p>在使用过程中遇到了一些问题,总结如下:</p> <h1 id="toc-e45"><a class="anchor" href="#toc-e45"></a>无用的数组</h1> <p>和Ruby一样,函数总会返回最后一个表达式的结果。 所以下面的代码中的for循环会被以数组形式返回。为此,会生成一个临时变量result来保存循环中的最后一个表达式,即<code>console.log(x)</code>。而这个结果有时候是无用的。</p> <pre><code class="hljs lang-coffeescript">a = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>]; <span class="hljs-function"><span class="hljs-title">dump</span> = -&gt;</span> <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> a <span class="hljs-built_in">console</span>.log(x); </code></pre> <p>被翻译成</p> <pre><code class="hljs lang-js"><span class="hljs-keyword">var</span> a, dump; a = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]; dump = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> i, len, results, x; results = []; <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>, len = a.length; i &lt; len; i++) { x = a[i]; results.push(<span class="hljs-built_in">console</span>.log(x)); } <span class="hljs-keyword">return</span> results; }; </code></pre> <p>避免生成‘无用数组’的的方法很简单,在最后加上一个return就可以了:</p> <pre><code class="hljs lang-coffeescript">a = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>]; <span class="hljs-function"><span class="hljs-title">dump</span> = -&gt;</span> <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> a <span class="hljs-built_in">console</span>.log(x); <span class="hljs-keyword">return</span> </code></pre> <p>被翻译成</p> <pre><code class="hljs lang-js"><span class="hljs-keyword">var</span> a, dump; a = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]; dump = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> i, len, x; <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>, len = a.length; i &lt; len; i++) { x = a[i]; <span class="hljs-built_in">console</span>.log(x); } }; </code></pre> <h1 id="toc-9e4"><a class="anchor" href="#toc-9e4"></a>没有函数声明</h1> <p>CoffeeScript中无法使用函数声明,只有函数表达式,并且都是匿名的。 换句说法就是,CoffeeScript不允许函数提升(Hoisting),也不产生Named Function Expression。</p> <p>函数声明和具名函数表达式在stack trace时有一定的帮助。 <a href="https://github.com/jashkenas/coffeescript/issues/15">Issue#15</a>和<a href="https://github.com/jashkenas/coffeescript/issues/1640">Issue#1640</a>都有对这个feature的请求。</p> <p>CoffeeScript中唯一出现函数声明的地方就是类定义:</p> <pre><code class="hljs lang-coffeescript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Clazz</span></span> fn: <span class="hljs-function"><span class="hljs-params">()</span> -&gt;</span> alert(<span class="hljs-number">1</span>) </code></pre> <p>被翻译成</p> <pre><code class="hljs lang-js"><span class="hljs-keyword">var</span> Clazz; Clazz = (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Clazz</span>(<span class="hljs-params"></span>) </span>{} Clazz.prototype.fn = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">return</span> alert(<span class="hljs-number">1</span>); }; <span class="hljs-keyword">return</span> Clazz; })(); </code></pre> <h1 id="toc-e24"><a class="anchor" href="#toc-e24"></a>非primitive的类属性</h1> <pre><code class="hljs lang-coffeescript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Animal</span></span> behaviors: [<span class="hljs-string">"eat"</span>,<span class="hljs-string">"sleep"</span>,<span class="hljs-string">"drink"</span>,<span class="hljs-string">"run"</span>] cat = <span class="hljs-keyword">new</span> Animal(); dog = <span class="hljs-keyword">new</span> Animal(); cat.behaviors.push(<span class="hljs-string">"miaow"</span>); <span class="hljs-built_in">console</span>.log cat.behaviors <span class="hljs-comment"># =&gt; ["eat", "sleep", "drink", "run", "miaow"]</span> <span class="hljs-built_in">console</span>.log dog.behaviors <span class="hljs-comment"># =&gt; ["eat", "sleep", "drink", "run", "miaow"]</span> </code></pre> <p>dog也有了<code>miaow</code>的行为。这是因为<code>cat</code>和<code>dog</code>实例的<code>behaviors</code>都指向同一个数组实例。解决的方法是在构造函数中设置<code>behaviors</code>属性的值:</p> <pre><code class="hljs lang-coffeescript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Animal</span></span> constructor: <span class="hljs-function">-&gt;</span> @behaviors = [<span class="hljs-string">"eat"</span>,<span class="hljs-string">"sleep"</span>,<span class="hljs-string">"drink"</span>,<span class="hljs-string">"run"</span>] cat = <span class="hljs-keyword">new</span> Animal(); dog = <span class="hljs-keyword">new</span> Animal(); cat.behaviors.push(<span class="hljs-string">"miaow"</span>); <span class="hljs-built_in">console</span>.log cat.behaviors <span class="hljs-comment"># =&gt; ["eat", "sleep", "drink", "run", "miaow"]</span> <span class="hljs-built_in">console</span>.log dog.behaviors <span class="hljs-comment"># =&gt; ["eat", "sleep", "drink", "run"]</span> </code></pre> <h1 id="toc-203"><a class="anchor" href="#toc-203"></a>函数调用的括号</h1> <p>CoffeeScript中的函数调用可以省略括号。但是有时候会导致意想不到的问题:</p> <pre><code class="hljs lang-coffeescript">x = <span class="hljs-string">"hello"</span> <span class="hljs-built_in">console</span>.log x +<span class="hljs-number">1</span> </code></pre> <p>会被翻译成</p> <pre><code class="hljs lang-js"><span class="hljs-keyword">var</span> x; x = <span class="hljs-string">"hello"</span>; <span class="hljs-built_in">console</span>.log(x(+<span class="hljs-number">1</span>)); </code></pre> <p>注意到了么,<code>x +1</code>中加好后面没有空格。所以x被当做函数调用,而<code>+1</code>被当做“正数1”了。</p> <h1 id="toc-5e0"><a class="anchor" href="#toc-5e0"></a>在CoffeeScript源码中使用JavaScript代码</h1> <p>有时候会遇到这样的问题:有一部分历史代码是用JavaScript写的,但是翻译成CoffeeScript代价又比较大。此时可以用backtick(`)在CoffeeScript中嵌入JavaScript代码:</p> <pre><code class="hljs lang-coffeescript">f() `<span class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f</span>(<span class="hljs-params"></span>) </span>{</span>` <span class="hljs-built_in">console</span>.log <span class="hljs-string">"hello world!"</span> `<span class="javascript">}</span>` </code></pre> <p>被翻译成</p> <pre><code class="hljs lang-js">f(); <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f</span>(<span class="hljs-params"></span>) </span>{; <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"hello world!"</span>); }; </code></pre> <p>这也算是在CoffeeScript中使用函数声明的一个Trick吧。当然不推荐这样做。</p> <h2 id="toc-0d0">慎用Grunt编译CoffeeScript</h2> <p>最初项目是用Grunt进行构建。但在公司笔记本上,机械硬盘实在不给力,每次保存<code>*.coffee</code>后带来的编译都要花费1到2秒钟,这一点是无法忍受的。</p> <p>切换到Gulp之后好了许多。配合ssd效果更好。 至于在ssd上使用Grunt是否这么慢没有进行测试。</p> <h1 id="toc-25f"><a class="anchor" href="#toc-25f"></a>总结</h1> <p>CoffeeScript只是JavaScript衣柜里的一件衣服。 衣柜里比较有名的还有:</p> <ul> <li>Google的 <a href="https://www.dartlang.org/">Dart</a></li> <li>微软的<a href="https://en.wikipedia.org/wiki/TypeScript">TypeScript</a></li> <li>CoffeeScript作者的另一个语言 <a href="http://livescript.net/">LiveScript</a></li> </ul> <p>随着<a href="https://babeljs.io/">Babel</a>及其插件的不断完善、Reactjs的流行。CoffeeScript的吸引力变得越来越小。除非你实在不想打花括号和圆括号。</p> <h1 id="toc-3ba"><a class="anchor" href="#toc-3ba"></a>书籍推荐</h1> <p><a href="https://arcturo.github.io/library/coffeescript/index.html"><img src="https://akamaicovers.oreilly.com/images/0636920024309/cat.gif" alt=""></a></p> Wed Mar 25 2015 22:03:58 GMT+0800 (CST)http://aztack.wang/post/pitfalls-of-using-coffeescript-in-projects.html