脚本宝典收集整理的这篇文章主要介绍了

2020年如何写一个现代的JavaScript库

脚本宝典小编觉得挺不错的,现在分享给大家,也给大家做个参考,希望能帮助你少写一行代码,多一份安全和惬意。
<p><code></p> <p>我写过一些<a href="https://github.com/yanhaijing" rel="nofollow noreferrer" target="_blank">开源项目</a>,在开源方面有一些经验,最近开到了阮老师的微博,深有感触,现在一个开源项目涉及的东西确实挺多的,特别是对于新手来说非常不友好</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016613993?w=1075&amp;h=656" src="/img/remote/1460000016613993?w=1075&amp;h=656" alt="" title="" style="cursor: pointer; display: inline;"></span></p> <p>最近我写了一个<a href="https://github.com/yanhaijing/jslib-base" rel="nofollow noreferrer" target="_blank">jslib-base</a>,旨在从多方面快速帮大家搭建一个标准的js库,本文将已jslib-base为例,介绍写一个开源库的知识</p> <blockquote><p>jslib-base 最好用的js第三方库脚手架,赋能js第三方库开源,让开发一个js库更简单,更专业</p></blockquote> <h2 id="articleHeader0">文档</h2> <p>所谓代码未动,文档先行,文档对于一个项目非常重要,一个项目的文档包括</p> <ul> <li>README.md</li> <li>TODO.md</li> <li>CHANGELOG.md</li> <li>LICENSE</li> <li><a href="http://www.js-code.com/tag/do" title="do" target="_blank">do</a>c</li> </ul> <h3 id="articleHeader1">README.md</h3> <p>README是一个项目的门面,应该简单明了的呈现用户最关心的问题,一个开源库的用户包括使用者和贡献者,所以一个文档应该包括项目简介,使用者指南,贡献者指南三部分</p> <p>项目简介用该简单介绍项目功能,使用场景,兼容性的相关知识,这里重点介绍下徽章,相信大家都见过别人项目中的徽章,如下所示</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016613994?w=1621&amp;h=238" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="" title="" style="cursor: pointer;"></span></p> <p>徽章通过更直观的方式,将更多的信息呈现出来,还能够提高颜值,有一个网站专门制作各种徽章,可以看<a href="https://shields.io/#/" rel="nofollow noreferrer" target="_blank">这里</a></p> <p>这里有一个README的<a href="https://github.com/yanhaijing/jslib-base/blob/master/README.md" rel="nofollow noreferrer" target="_blank">完整的例子</a></p> <h3 id="articleHeader2">TODO.md</h3> <p>TODO应该记录项目的未来计划,这对于贡献者和使用者都有很重要的意义,下面是TODO的例子</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="- [X] 已完成 - [ ] 未完成" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs markdown"><code><span class="hljs-bullet">- </span>[X] 已完成 <span class="hljs-bullet">- </span>[ ] 未完成</code></pre> <h3 id="articleHeader3">CHANGELOG.md</h3> <p>CHANGELOG记录项目的变更日志,对项目使用者非常重要,特别是在升级使用版本时,CHANGELOG需要记录项目的版本,发版时间和版本变更记录</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="## 0.1.0 / 2018-10-6 - 新增xxx功能 - 删除xxx功能 - 更改xxx功能" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs markdown"><code><span class="hljs-section">## 0.1.0 / 2018-10-6</span> <span class="hljs-bullet">- </span>新增xxx功能 <span class="hljs-bullet">- </span>删除xxx功能 <span class="hljs-bullet">- </span>更改xxx功能</code></pre> <h3 id="articleHeader4">LICENSE</h3> <p>开源项目必须要选择一个协议,因为没有协议的项目是没有人敢使用的,关于不同协议的区别可以看下面这张图(出自阮老师博客),我的建议是选择MIT或者BSD协议</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016613995" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="" title="" style="cursor: pointer;"></span></p> <h3 id="articleHeader5"><a href="http://www.js-code.com/tag/do" title="do" target="_blank">do</a>c</h3> <p>开源项目还应该提供详细的使用文档,一份详细文档的每个函数介绍都应该包括如下信息:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="函数简单介绍 函数详细介绍 函数参数和返回值(要遵守下面的例子的规则) - param {string} name1 name1描述 - return {string} 返回值描述 举个例子(要包含代码用例) // 代码 特殊说明,比如特殊情况下会报错等" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs typescript"><code>函数简单介绍 函数详细介绍 函数参数和返回值(要遵守下面的例子的规则) - param {<span class="hljs-built_in">str<a href="http://www.js-code.com/tag/in" title="浏览关于“in”的文章" target="_blank" class="tag_link">in</a>g</span>} <a href="http://www.js-code.com/tag/name" title="浏览关于“name”的文章" target="_blank" class="tag_link">name</a>1 name1描述 - <span class="hljs-keyword"><a href="http://www.js-code.com/tag/return" title="浏览关于“return”的文章" target="_blank" class="tag_link">return</a></span> {<span class="hljs-built_in">string</span>} 返回值描述 举个例子(要包含代码用例) <span class="hljs-comment">// 代码</span> 特殊说明,比如特殊情况下会报错等</code></pre> <h2 id="articleHeader6">构建</h2> <p>理想的情况如下:</p> <ul> <li>库开发者美滋滋的写ES6+的代码;</li> <li>库使用者能够运行在浏览器(ie6-11)和<a href="http://www.js-code.com/tag/node" title="node" target="_blank">node</a>(0.12-10)中</li> <li>库使用者能够使用AMD或CMD模块方案</li> <li>库使用者能够使用webpack、rollup或fis等预编译工具</li> </ul> <p>理想很丰满,现实很。。。,如何才能够让开发者和使用者都能够开心呢,jslib-base通过<a href="http://www.js-code.com/tag/babel" title="babel" target="_blank">babel</a>+rollup提供了解决方案</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016613996?w=1856&amp;h=878" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="" title="" style="cursor: pointer;"></span></p> <h3 id="articleHeader7">编译</h3> <p>通过<a href="http://www.js-code.com/tag/babel" title="babel" target="_blank">babel</a>可以把ES6+的代码编译成ES5的代码,<a href="http://www.js-code.com/tag/babel" title="浏览关于“babel”的文章" target="_blank" class="tag_link">babel</a>经理了5到6的进化,下面一张图总结了babel使用方式的变迁</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016613997?w=1765&amp;h=483" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="" title="" style="cursor: pointer;"></span></p> <p>本文不讨论babel的进化史(后面会单独开一片博文介绍),而是选择最现代化的<code>babel-p<a href="http://www.js-code.com/tag/reset" title="reset" target="_blank">reset</a>-env</code>方案,babel-p<a href="http://www.js-code.com/tag/reset" title="reset" target="_blank">reset</a>-env可以通过提供提供兼容环境,而决定要编译那些ES特性</p> <p>其原理大概如下,首先通过ES的特性和[特性的兼容列表](<a href="http://kangax.github.io/compat-table/es6/" rel="nofollow noreferrer" target="_blank">http://kangax.github.io/compa...</a><br />)计算出每个特性的兼容性信息,再通过给定兼容性要求,计算出要使用的babel插件</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016613998?w=1708&amp;h=744" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="" title="" style="cursor: pointer;"></span></p> <p>首先需要安装<code>babel-p<a href="http://www.js-code.com/tag/reset" title="浏览关于“reset”的文章" target="_blank" class="tag_link">reset</a>-env</code></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="$ npm i --save-dev babel-preset-env" title="" data-original-title="复制"></span> </div> </p></div> <pre class="bash hljs"><code class="bash" style="word-break: break-word; white-space: initial;">$ npm i --save-dev babel-preset-env</code></pre> <p>然后新增一个.babelrc文件,添加下面的内容</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="{ &quot;presets&quot;: [ [&quot;env&quot;, { &quot;targets&quot;: { &quot;browsers&quot;: &quot;last 2 versions, > 1%, ie >= 6, Android >= 4, iOS >= 6, and_uc > 9&quot;,<br /> &quot;node&quot;: &quot;0.10&quot;<br /> },<br /> &quot;modules&quot;: false,<br /> &quot;loose&quot;: false<br /> }]<br /> ]<br /> }<br /> " title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs json"><code>{ <span class="hljs-attr">"presets"</span>: [ [<span class="hljs-string">"env"</span>, { <span class="hljs-attr">"targets"</span>: { <span class="hljs-attr">"browsers"</span>: <span class="hljs-string">"last 2 versions, &gt; 1%, ie &gt;= 6, Android &gt;= 4, iOS &gt;= 6, and_uc &gt; 9"</span>, <span class="hljs-attr">"<a href="http://www.js-code.com/tag/node" title="浏览关于“node”的文章" target="_blank" class="tag_link">node</a>"</span>: <span class="hljs-string">"0.10"</span> }, <span class="hljs-attr">"modules"</span>: <span class="hljs-literal"><a href="http://www.js-code.com/tag/false" title="浏览关于“false”的文章" target="_blank" class="tag_link">false</a></span>, <span class="hljs-attr">"loose"</span>: <span class="hljs-literal">false</span> }] ] } </code></pre> <p><code>targets</code>中配置需要兼容的环境,关于浏览器配置对应的浏览器列表,可以从<a href="http://browserl.ist/" rel="nofollow noreferrer" target="_blank">browserl.ist</a>上查看</p> <p><code>modules</code>表示编出输出的模块类型,支持"amd","umd","systemjs","commonjs",<a href="http://www.js-code.com/tag/false" title="false" target="_blank">false</a>这些选项,<a href="http://www.js-code.com/tag/false" title="false" target="_blank">false</a>表示不输出任何模块类型</p> <p><code>loose</code>代表松散模式,将loose设置为<a href="http://www.js-code.com/tag/true" title="true" target="_blank">true</a>,能够更好地兼容ie8以下环境,下面是一个例子(ie8不支持<code><a href="http://www.js-code.com/tag/Object" title="Object" target="_blank">Object</a>.def<a href="http://www.js-code.com/tag/in" title="in" target="_blank">in</a>e<a href="http://www.js-code.com/tag/prop" title="Prop" target="_blank">Prop</a>erty</code>)</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// 源代码 const aaa = 1; export default aaa; // loose false Object.defineProperty(exports, '__esModule', { value: true }); var aaa = 1; exports.default = 1; // loose true exports.__esModule = true; var aaa = 1; exports.default = 1;" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// 源代码</span> <span class="hljs-keyword"><a href="http://www.js-code.com/tag/const" title="浏览关于“const”的文章" target="_blank" class="tag_link">const</a></span> aaa = <span class="hljs-number">1</span>; <span class="hljs-keyword"><a href="http://www.js-code.com/tag/export" title="浏览关于“export”的文章" target="_blank" class="tag_link">export</a></span> <span class="hljs-keyword"><a href="http://www.js-code.com/tag/default" title="浏览关于“default”的文章" target="_blank" class="tag_link">default</a></span> aaa; <span class="hljs-comment">// loose false</span> <span class="hljs-built_in"><a href="http://www.js-code.com/tag/Object" title="浏览关于“Object”的文章" target="_blank" class="tag_link">Object</a></span>.define<a href="http://www.js-code.com/tag/prop" title="浏览关于“Prop”的文章" target="_blank" class="tag_link">Prop</a>erty(exports, <span class="hljs-string">'__esModule'</span>, { <span class="hljs-attr">value</span>: <span class="hljs-literal"><a href="http://www.js-code.com/tag/true" title="浏览关于“true”的文章" target="_blank" class="tag_link">true</a></span> }); <span class="hljs-keyword"><a href="http://www.js-code.com/tag/var" title="浏览关于“var”的文章" target="_blank" class="tag_link">var</a></span> aaa = <span class="hljs-number">1</span>; exports.default = <span class="hljs-number">1</span>; <span class="hljs-comment">// loose true</span> exports.__esModule = <span class="hljs-literal">true</span>; <span class="hljs-keyword">var</span> aaa = <span class="hljs-number">1</span>; exports.default = <span class="hljs-number">1</span>;</code></pre> <p><code>babel-preset-env</code>解决了语法新特性的兼容问题,如果想使用api新特性,在babel中一般通过babel-polyfill来解决,babel-polyfill通过引入一个polyfill文件来解决问题,这对于普通项目很实用,但对于库来说就不太友好了</p> <p>babel给库开发者提供的方案是<code>babel-trans<a href="http://www.js-code.com/tag/for" title="for" target="_blank">for</a>m-runtime</code>,runtime提供类似程序运行时,可以将全局的polyfill沙盒化</p> <p>首先需要安装<code>babel-trans<a href="http://www.js-code.com/tag/form" title="form" target="_blank">form</a>-runtime</code></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="$ npm i --save-dev babel-plugin-transform-runtime" title="" data-original-title="复制"></span> </div> </p></div> <pre class="bash hljs"><code class="bash" style="word-break: break-word; white-space: initial;">$ npm i --save-dev babel-<a href="http://www.js-code.com/tag/plugin" title="浏览关于“plugin”的文章" target="_blank" class="tag_link">plugin</a>-trans<a href="http://www.js-code.com/tag/form" title="浏览关于“form”的文章" target="_blank" class="tag_link">form</a>-runtime</code></pre> <p>在.babelrc增加下面的配置</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="&quot;plugins&quot;: [ [&quot;transform-runtime&quot;, { &quot;helpers&quot;: false, &quot;polyfill&quot;: false, &quot;regenerator&quot;: false, &quot;moduleName&quot;: &quot;babel-runtime&quot; }] ]" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs objectivec"><code><span class="hljs-string">"plugins"</span>: [ [<span class="hljs-string">"trans<a href="http://www.js-code.com/tag/for" title="浏览关于“for”的文章" target="_blank" class="tag_link">for</a>m-runtime"</span>, { <span class="hljs-string">"helpers"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"polyfill"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"regenerator"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"moduleName"</span>: <span class="hljs-string">"babel-runtime"</span> }] ]</code></pre> <p>trans<a href="http://www.js-code.com/tag/for" title="for" target="_blank">for</a>m-runtime,支持三种运行时,下面是polyfill的例子</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// 源代码 var a = Promise.resolve(1); // 编译后的代码 var _promise = require('babel-runtime/core-js/promise'); var a = _promise.resolve(1); // Promise被替换为_promise" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// 源代码</span> <span class="hljs-keyword">var</span> a = <span class="hljs-built_in"><a href="http://www.js-code.com/tag/promise" title="浏览关于“Promise”的文章" target="_blank" class="tag_link">Promise</a></span>.resolve(<span class="hljs-number">1</span>); <span class="hljs-comment">// 编译后的代码</span> <span class="hljs-keyword">var</span> _promise = <span class="hljs-built_in">require</span>(<span class="hljs-string">'babel-runtime/core-js/promise'</span>); <span class="hljs-keyword">var</span> a = _promise.resolve(<span class="hljs-number">1</span>); <span class="hljs-comment">// <a href="http://www.js-code.com/tag/promis" title="浏览关于“Promis”的文章" target="_blank" class="tag_link">Promis</a>e被替换为_promise</span></code></pre> <p>虽然虽然可以优雅的解决问题,但是引入的文件非常之大,比如只用了ES6中<a href="http://www.js-code.com/tag/%e6%95%b0%e7%bb%84" title="数组" target="_blank">数组</a>的f<a href="http://www.js-code.com/tag/in" title="in" target="_blank">in</a>d功能,可能就会引入一个几千行的代码,我的建议对于库来说能不用最好不用</p> <h3 id="articleHeader8">打包</h3> <p>编译解决了ES6到ES5的问题,打包可以把多个文件合并成一个文件,对外提供统一的文件入口,打包解决的是依赖引入的问题</p> <h4>rollup vs webpack</h4> <p>我选择的rollup作为打包工具,rollup号称下一代打包方案,其有如下功能</p> <ul> <li>依赖解析,打包构建</li> <li>仅支持<a href="http://www.js-code.com/tag/es6%e6%a8%a1%e5%9d%97" title="ES6模块" target="_blank">ES6模块</a></li> <li>Tree shaking</li> </ul> <p>webpack作为最流行的打包方案,rollup作为下一代打包方案,其实一句话就可以总结二者的区别:库使用rollup,其他场景使用webpack</p> <p>为什么我会这么说呢?下面通过例子对比下webpack和rollup的区别</p> <p>假设我们有两个文件,index.js和bar.js,其代码如下</p> <p>bar.js对外暴漏一个函数<code>bar</code></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="export default function bar() { console.log('bar') }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword"><a href="http://www.js-code.com/tag/function" title="浏览关于“function”的文章" target="_blank" class="tag_link">function</a></span> <span class="hljs-title">bar</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'bar'</span>) }</code></pre> <p>index.js引用bar.js</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="import bar from './bar'; bar()" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">import</span> bar <span class="hljs-keyword">from</span> <span class="hljs-string">'./bar'</span>; bar()</code></pre> <p>下面是webpack的配置文件webpack.config.js</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="const path = require('path'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' } };" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>); <span class="hljs-built_in">module</span>.exports = { <span class="hljs-attr">en<a href="http://www.js-code.com/tag/try" title="浏览关于“try”的文章" target="_blank" class="tag_link">try</a></span>: <span class="hljs-string">'./src/index.js'</span>, <span class="hljs-attr">output</span>: { <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">'dist'</span>), <span class="hljs-attr">filename</span>: <span class="hljs-string">'bundle.js'</span> } };</code></pre> <p>下面来看一下webpack打包输出的内容,o(╯□╰)o,别着急,我们的代码在最下面的几行,上面这一大片代码其实是webpack生成的简易模块系统,webpack的方案问题在于会生成很多冗余代码,这对于业务代码来说没什么问题,但对于库来说就不太友好了</p> <p><em>注意:下面的代码基于webpack3,webpack4增加了scope hoisting,已经把多个模块合并到一个匿名函数中</em></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if (installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if (!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module &amp;&amp; module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = &quot;&quot;; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { &quot;use strict&quot;; Object.defineProperty(__webpack_exports__, &quot;__esModule&quot;, { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__bar__ = __webpack_require__(1); Object(__WEBPACK_IMPORTED_MODULE_0__bar__[&quot;a&quot; /* default */ ])() /***/ }), /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { &quot;use strict&quot;; /* harmony export (immutable) */ __webpack_exports__[&quot;a&quot;] = bar; function bar() { // console.log('bar') } /***/ }) /******/ ]);" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">/******/</span> (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">modules</span>) </span>{ <span class="hljs-comment">// webpackBootstrap</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// The module cache</span> <span class="hljs-comment">/******/</span> <span class="hljs-keyword">var</span> inst<a href="http://www.js-code.com/tag/all" title="浏览关于“all”的文章" target="_blank" class="tag_link">all</a>edModules = {}; <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// The require function</span> <span class="hljs-comment">/******/</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__webpack_require__</span>(<span class="hljs-params">moduleId</span>) </span>{ <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// Check <a href="http://www.js-code.com/tag/if" title="浏览关于“if”的文章" target="_blank" class="tag_link">if</a> module is in cache</span> <span class="hljs-comment">/******/</span> <span class="hljs-keyword">if</span> (installedModules[moduleId]) { <span class="hljs-comment">/******/</span> <span class="hljs-keyword">return</span> installedModules[moduleId].exports; <span class="hljs-comment">/******/</span> } <span class="hljs-comment">/******/</span> <span class="hljs-comment">// Create a <a href="http://www.js-code.com/tag/new" title="浏览关于“new”的文章" target="_blank" class="tag_link">new</a> module (and put it <a href="http://www.js-code.com/tag/int" title="浏览关于“int”的文章" target="_blank" class="tag_link">int</a>o the cache)</span> <span class="hljs-comment">/******/</span> <span class="hljs-keyword">var</span> <span class="hljs-built_in">module</span> = installedModules[moduleId] = { <span class="hljs-comment">/******/</span> i: moduleId, <span class="hljs-comment">/******/</span> l: <span class="hljs-literal">false</span>, <span class="hljs-comment">/******/</span> exports: {} <span class="hljs-comment">/******/</span> }; <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// Execute the module function</span> <span class="hljs-comment">/******/</span> modules[moduleId].call(<span class="hljs-built_in">module</span>.exports, <span class="hljs-built_in">module</span>, <span class="hljs-built_in">module</span>.exports, __webpack_require__); <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// Flag the module as loaded</span> <span class="hljs-comment">/******/</span> <span class="hljs-built_in">module</span>.l = <span class="hljs-literal">true</span>; <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// Return the exports of the module</span> <span class="hljs-comment">/******/</span> <span class="hljs-keyword">return</span> <span class="hljs-built_in">module</span>.exports; <span class="hljs-comment">/******/</span> } <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// expose the modules object (__webpack_modules__)</span> <span class="hljs-comment">/******/</span> __webpack_require__.m = modules; <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// expose the module cache</span> <span class="hljs-comment">/******/</span> __webpack_require__.c = installedModules; <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// define getter function for harmony exports</span> <span class="hljs-comment">/******/</span> __webpack_require__.d = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">exports, name, getter</span>) </span>{ <span class="hljs-comment">/******/</span> <span class="hljs-keyword">if</span> (!__webpack_require__.o(exports, name)) { <span class="hljs-comment">/******/</span> <span class="hljs-built_in">Object</span>.defineProperty(exports, name, { <span class="hljs-comment">/******/</span> configurable: <span class="hljs-literal">false</span>, <span class="hljs-comment">/******/</span> <a href="http://www.js-code.com/tag/enum" title="浏览关于“enum”的文章" target="_blank" class="tag_link">enum</a>erable: <span class="hljs-literal">true</span>, <span class="hljs-comment">/******/</span> get: getter <span class="hljs-comment">/******/</span> }); <span class="hljs-comment">/******/</span> } <span class="hljs-comment">/******/</span> }; <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// getDefaultExport function for compatibility with non-harmony modules</span> <span class="hljs-comment">/******/</span> __webpack_require__.n = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">module</span>) </span>{ <span class="hljs-comment">/******/</span> <span class="hljs-keyword">var</span> getter = <span class="hljs-built_in">module</span> &amp;&amp; <span class="hljs-built_in">module</span>.__esModule ? <span class="hljs-comment">/******/</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDefault</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">return</span> <span class="hljs-built_in">module</span>[<span class="hljs-string">'default'</span>]; } : <span class="hljs-comment">/******/</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getModuleExports</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">return</span> <span class="hljs-built_in">module</span>; }; <span class="hljs-comment">/******/</span> __webpack_require__.d(getter, <span class="hljs-string">'a'</span>, getter); <span class="hljs-comment">/******/</span> <span class="hljs-keyword">return</span> getter; <span class="hljs-comment">/******/</span> }; <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// Object.<a href="http://www.js-code.com/tag/prototype" title="浏览关于“prototype”的文章" target="_blank" class="tag_link">prototype</a>.<a href="http://www.js-code.com/tag/hasOwnProperty" title="浏览关于“hasOwnProperty”的文章" target="_blank" class="tag_link">hasOwnProperty</a>.call</span> <span class="hljs-comment">/******/</span> __webpack_require__.o = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">object, property</span>) </span>{ <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.prototype.hasOwnProperty.call(object, property); }; <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// __webpack_<a href="http://www.js-code.com/tag/public" title="浏览关于“public”的文章" target="_blank" class="tag_link">public</a>_path__</span> <span class="hljs-comment">/******/</span> __webpack_require__.p = <span class="hljs-string">""</span>; <span class="hljs-comment">/******/</span> <span class="hljs-comment">/******/</span> <span class="hljs-comment">// Load entry module and return exports</span> <span class="hljs-comment">/******/</span> <span class="hljs-keyword">return</span> __webpack_require__(__webpack_require__.s = <span class="hljs-number">0</span>); <span class="hljs-comment">/******/</span> }) <span class="hljs-comment">/************************************************************************/</span> <span class="hljs-comment">/******/</span> ([ <span class="hljs-comment">/* 0 */</span> <span class="hljs-comment">/***/</span> (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">module, __webpack_exports__, __webpack_require__</span>) </span>{ <span class="hljs-meta"> "use strict"</span>; <span class="hljs-built_in">Object</span>.defineProperty(__webpack_exports__, <span class="hljs-string">"__esModule"</span>, { <span class="hljs-attr">value</span>: <span class="hljs-literal">true</span> }); <span class="hljs-comment">/* harmony import */</span> <span class="hljs-keyword">var</span> __WEBPACK_IMPORTED_MODULE_0__bar__ = __webpack_require__(<span class="hljs-number">1</span>); <span class="hljs-built_in">Object</span>(__WEBPACK_IMPORTED_MODULE_0__bar__[<span class="hljs-string">"a"</span> <span class="hljs-comment">/* default */</span> ])() <span class="hljs-comment">/***/</span> }), <span class="hljs-comment">/* 1 */</span> <span class="hljs-comment">/***/</span> (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">module, __webpack_exports__, __webpack_require__</span>) </span>{ <span class="hljs-meta"> "use strict"</span>; <span class="hljs-comment">/* harmony export (immutable) */</span> __webpack_exports__[<span class="hljs-string">"a"</span>] = bar; <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bar</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-comment">//</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'bar'</span>) } <span class="hljs-comment">/***/</span> }) <span class="hljs-comment">/******/</span> ]);</code></pre> <p>下面来看看rollup的结果,rollup的配置和webpack类似</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="export default { input: 'src/index.js', output: { file: 'dist/bundle2.js', format: 'cjs' } };" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> { <span class="hljs-attr">input</span>: <span class="hljs-string">'src/index.js'</span>, <span class="hljs-attr">output</span>: { <span class="hljs-attr">file</span>: <span class="hljs-string">'dist/bundle2.js'</span>, <span class="hljs-attr">format</span>: <span class="hljs-string">'cjs'</span> } };</code></pre> <p>下面看看rollup的产出,简直完美有没有,模块完全消失了,rollup通过顺序引入到同一个文件来解决模块依赖问题,rollup的方案如果要做拆包的话就会有问题,因为模块完全透明了,但这对于库开发者来说简直就是最完美的方案</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="'use strict'; function bar() { // console.log('bar'); } bar();" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-meta">'use strict'</span>; <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bar</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-comment">//</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'bar'</span>); } bar();</code></pre> <h4>模块化方案</h4> <p>在<a href="http://www.js-code.com/tag/es6%e6%a8%a1%e5%9d%97" title="ES6模块" target="_blank">ES6模块</a>化之前,JS社区探索出了一些模块系统,比如<a href="http://www.js-code.com/tag/node" title="node" target="_blank">node</a>中的commonjs,浏览器中的AMD,还有可以同时兼容不同模块系统的UMD,如果对这部分内容感兴趣,可以看我之前的一篇文章《<a href="https://yanhaijing.com/javascript/2015/03/28/js-module/" rel="nofollow noreferrer" target="_blank">JavaScript模块的前世今生</a>》</p> <p>对于浏览器原生,预编译工具和node,不同环境中的模块化方案也不同;由于浏览器环境不能够解析第三方依赖,所以浏览器环境需要把依赖也进行打包处理;不同环境下引用的文件也不相同,下面通过一个表格对比下</p> <table> <thead> <tr> <th> </th> <th>浏览器(script,AMD,CMD)</th> <th>预编译工具(webpack,rollup,fis)</th> <th>Node</th> </tr> </thead> <tbody> <tr> <td>引用文件</td> <td>index.aio.js</td> <td>index.esm.js</td> <td>index.js</td> </tr> <tr> <td>模块化方案</td> <td>UMD</td> <td>ES Module</td> <td>commonjs</td> </tr> <tr> <td>自身依赖</td> <td>打包</td> <td>打包</td> <td>打包</td> </tr> <tr> <td>第三方依赖</td> <td>打包</td> <td>不打包</td> <td>不打包</td> </tr> </tbody> </table> <p>*注意: legacy模式下的模块系统可以兼容ie6-8,但由于rollup的一个[bug](<a href="https://github.com/rollup/rollup/issues/2088" rel="nofollow noreferrer" target="_blank">https://github.com/rollup/rol...</a><br />)(这个bug是我发现的,但rollup并不打算修复,╮(╯▽╰)╭哎),legacy模式下,不可同时使用 <a href="http://www.js-code.com/tag/export" title="export" target="_blank">export</a> 与 <a href="http://www.js-code.com/tag/export" title="export" target="_blank">export</a> <a href="http://www.js-code.com/tag/default" title="default" target="_blank">default</a>*</p> <h4>tree shaking</h4> <p>rollup是天然支持tree shaking,tree shaking可以提出依赖模块中没有被使用的部分,这对于第三方依赖非常有帮助,可以极大的降低包的体积</p> <p>举个例子,假设index.js只是用了第三方包is.js中的一个函数<code>is<a href="http://www.js-code.com/tag/String" title="String" target="_blank">String</a></code>,没有treeshaking会将is.js全部引用进来</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016613999?w=1266&amp;h=518" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="" title="" style="cursor: pointer;"></span></p> <p>而使用了treeshaking的话则可以将is.js中的其他函数剔除,仅保留<code>is<a href="http://www.js-code.com/tag/String" title="String" target="_blank">String</a></code>函数</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016614000?w=1238&amp;h=524" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="" title="" style="cursor: pointer;"></span></p> <h2 id="articleHeader9">规范</h2> <p>无规矩不成方圆,特别是对于开源项目,由于会有多人参与,所以大家遵守一份规范会事半功倍</p> <h3 id="articleHeader10">编辑器规范</h3> <p>首先可以通过<code>.editorconfig</code>来保证缩进、换行的一致性,目前绝大部分浏览器都已经支持,可以看<a href="http://editorconfig.org/" rel="nofollow noreferrer" target="_blank">这里</a></p> <p>下面的配置设置在js,css和html中都用空格代替tab,tab为4个空格,使用unix换行符,使用utf8字符集,每个文件结尾添加一个空行</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="root = true [{*.js,*.css,*.html}] indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 insert_final_newline = true [{package.json,.*rc,*.yml}] indent_style = space indent_size = 2 " title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs ini"><code><span class="hljs-attr">root</span> = <span class="hljs-literal">true</span> <span class="hljs-section"> [{*.js,*.css,*.html}]</span> <span class="hljs-attr">indent_style</span> = space <span class="hljs-attr">indent_size</span> = <span class="hljs-number">4</span> <span class="hljs-attr">end_of_line</span> = lf <span class="hljs-attr"><a href="http://www.js-code.com/tag/char" title="浏览关于“char”的文章" target="_blank" class="tag_link">char</a>set</span> = utf-<span class="hljs-number">8</span> <span class="hljs-attr">insert_<a href="http://www.js-code.com/tag/final" title="浏览关于“final”的文章" target="_blank" class="tag_link">final</a>_newline</span> = <span class="hljs-literal">true</span> <span class="hljs-section"> [{<a href="http://www.js-code.com/tag/package" title="浏览关于“package”的文章" target="_blank" class="tag_link">package</a>.json,.*rc,*.yml}]</span> <span class="hljs-attr">indent_style</span> = space <span class="hljs-attr">indent_size</span> = <span class="hljs-number">2</span> </code></pre> <h3 id="articleHeader11">代码风格</h3> <p>其次可以通过esl<a href="http://www.js-code.com/tag/int" title="int" target="_blank">int</a>来保证代码风格一致,关于esl<a href="http://www.js-code.com/tag/int" title="int" target="_blank">int</a>的安装和配置这里不再展开解释了,在jslib-base中只需要运行下面的命令就可以进行代码校验了,eslint的配置文件位于<code>config/.eslintrc.js</code></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="$ npm run lint" title="" data-original-title="复制"></span> </div> </p></div> <pre class="bash hljs"><code class="bash" style="word-break: break-word; white-space: initial;">$ npm run lint</code></pre> <h3 id="articleHeader12">设计规范</h3> <p>eslint只能够保证代码规范,却不能保证提供优秀的接口设计,关于函数接口设计有一些指导规则</p> <p>参数数量</p> <ul> <li>函数的参数个数最多不要超过5个</li> </ul> <p>可选参数</p> <ul> <li>可选参数应该放到后面</li> <li>可选参数数量超过三个时,可以使用对象传入</li> <li>可选参数,应该提供默认值</li> </ul> <p>参数校验与类型转换</p> <ul> <li>必传参数,如果不传要报错</li> <li>对下列类型要做强制检验,类型不对要报错(object, array, <a href="http://www.js-code.com/tag/function" title="function" target="_blank">function</a>)</li> <li>对下列类型要做自动转换(number, string, <a href="http://www.js-code.com/tag/boolean" title="boolean" target="_blank">boolean</a>)</li> <li>对于复合类型的内部数据,也要做上面的两个步骤</li> <li>对于number转换后如果为<a href="http://www.js-code.com/tag/NaN" title="NaN" target="_blank">NaN</a>,要做特殊处理(有默认值的赋值为默认值,无默认值的要报错)</li> </ul> <p>参数类型</p> <ul> <li>参数尽量使用值类型(简单类型)</li> <li>参数尽量不要使用复杂类型(避免副作用)</li> <li>使用复杂类型时,层级不要过深</li> <li>使用复杂数据类型时,应该进行深拷贝(避免副作用)</li> </ul> <p>函数返回值</p> <ul> <li>返回值可返回操作结果(获取接口),操作是否成功(保存接口)</li> <li>返回值的类型要保持一致</li> <li>返回值尽量使用值类型(简单类型)</li> <li>返回值尽量不要使用复杂类型(避免副作用)</li> </ul> <h3 id="articleHeader13">版本规范</h3> <p>版本应该遵守开源社区通用的[语义化版本](<a href="https://semver.org/lang/zh-CN/" rel="nofollow noreferrer" target="_blank">https://semver.org/lang/zh-CN/</a><br />)</p> <p>版本号格式:x.y.z</p> <ul> <li>x 主版本号,不兼容的改动</li> <li>y 次版本号,兼容的改动</li> <li>z 修订版本号,bug修复</li> </ul> <h3 id="articleHeader14">Git commit规范</h3> <p>代码的提交应该遵守规范,这里推荐一个<a href="https://yanhaijing.com/git/2016/02/17/my-commit-message/" rel="nofollow noreferrer" target="_blank">我的规范</a></p> <h2 id="articleHeader15">测试</h2> <p>没有单元测试的库都是耍流氓,单元测试能够保证每次交付都是有质量保证的,业务代码由于一次性和时间成本可以不做单元测试,但开源库由于需要反复迭代,对质量要求又极高,所以单元测试是必不可少的</p> <p>关于单元测试有很多技术方案,其中一种选择是[mocha](<a href="https://mochajs.org/" rel="nofollow noreferrer" target="_blank">https://mochajs.org/</a><br />)+[chai](<a href="http://www.chaijs.com/" rel="nofollow noreferrer" target="_blank">http://www.chaijs.com/</a><br />),mocha是一个单元测试框架,用来组织、运行单元测试,并输出测试报告;chai是一个断言库,用来做单元测试的断言功能</p> <p>由于chai不能够兼容ie6-8,所以选择了另一个断言库——<a href="http://chaijs.com/" rel="nofollow noreferrer" target="_blank">expect.js</a>,expect是一个BDD断言库,兼容性非常好,所以我选择的是mocha+expect.js</p> <p><em>关于BDD与TDD的区别这里不再赘述,感兴趣的同学可以自行查阅相关资料</em></p> <p>有了测试的框架,还需要写单元测试的代码,下面是一个例子</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="var expect = require('expect.js'); var base = require('../dist/index.js'); describe('单元测试', function() { describe('功能1', function() { it('相等', function() { expect(1).to.equal(1); }); }); }); " title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">var</span> expect = <span class="hljs-built_in">require</span>(<span class="hljs-string">'expect.js'</span>); <span class="hljs-keyword">var</span> base = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../dist/index.js'</span>); describe(<span class="hljs-string">'单元测试'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ describe(<span class="hljs-string">'功能1'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ it(<span class="hljs-string">'相等'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ expect(<span class="hljs-number">1</span>).to.equal(<span class="hljs-number">1</span>); }); }); }); </code></pre> <p>然后只需运行下面的命令,mocha会自动运行test目录下面的js文件</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="$ mocha" title="" data-original-title="复制"></span> </div> </p></div> <pre class="bash hljs"><code class="bash" style="word-break: break-word; white-space: initial;">$ mocha</code></pre> <p>mocha支持在node和浏览器中测试,但上面的框架在浏览器下有一个问题,浏览器没法支持<code>require('expect.js')</code>,我用了一个比较hack的方法解决问题,早浏览器中重新定义了require的含义</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="<a href="http://www.js-code.com/tag/button" title="button" target="_blank">button</a>" <a href="http://www.js-code.com/tag/class" title="class" target="_blank">class</a>="copyCode code-tool" data-toggle="tooltip" data-placement="<a href="http://www.js-code.com/tag/top" title="top" target="_blank">top</a>" data-clipboard-<a href="http://www.js-code.com/tag/text" title="text" target="_blank">text</a>="<script src=&quot;../../node_modules/mocha/mocha.js&quot;></script><br /> <script src=&quot;../../node_modules/expect.js/index.js&quot;></script><br /> <script> <a href="http://www.js-code.com/tag/var" title="var" target="_blank">var</a> libs = { 'expect.js': expect, '../dist/index.js': jslib_base }; <a href="http://www.js-code.com/tag/var" title="var" target="_blank">var</a> require = <a href="http://www.js-code.com/tag/function" title="function" target="_blank">function</a>(path) { <a href="http://www.js-code.com/tag/return" title="return" target="_blank">return</a> libs[path]; } </script>" title="" data-original-title="复制"></span> </div> </p></div> <pre class="xml hljs"><code class="html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"../../node_modules/mocha/mocha.js"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"../../node_modules/expect.js/index.js"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"> <span class="hljs-keyword">var</span> libs = { <span class="hljs-string">'expect.js'</span>: expect, <span class="hljs-string">'../dist/index.js'</span>: jslib_base }; <span class="hljs-keyword">var</span> <span class="hljs-built_in">require</span> = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">path</span>) </span>{ <span class="hljs-keyword">return</span> libs[path]; } </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></code></pre> <p>下面是用mocha生成测试报告的例子,左边是在node中,右边是在浏览器中</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016614001?w=1539&amp;h=1049" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="" title="" style="cursor: pointer;"></span></p> <h2 id="articleHeader16">可持续集成</h2> <p>没有可持续集成的库都是原始人,如果每次push都能够自动运行单元测试就好了,这样就省去了手动运行的繁琐,好在[travis-ci](<a href="https://www.travis-ci.org/" rel="nofollow noreferrer" target="_blank">https://www.travis-ci.org/</a><br />)已经为我们提供了这个功能</p> <p>用GitHub登录travis-ci,就可以看到自己在GitHub上的项目了,然后需要打开下项目的开关,才能够打开自动集成功能</p> <p><span class="img-wrap"><img data-src="/img/remote/1460000016614002?w=1326&amp;h=718" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="" title="" style="cursor: pointer;"></span></p> <p>第二步,还需要在项目中添加一个文件<code>.travis.yml</code>,内容如下,这样就可以在每次push时自动在node 4 6 8版本下运行<code>npm test</code>命令,从而实现自动测试的目的</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="language: node_js node_js: - &quot;8&quot; - &quot;6&quot; - &quot;4&quot;" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs groovy"><code><span class="hljs-string">language:</span> node_js <span class="hljs-string">node_js:</span> - <span class="hljs-string">"8"</span> - <span class="hljs-string">"6"</span> - <span class="hljs-string">"4"</span></code></pre> <h2 id="articleHeader17">其他内容</h2> <p>开源库希望得到用户的反馈,如果对用户提的issue有要求,可以设置一个模版,用来规范github上用户反馈的issue需要制定一些信息</p> <p>通过提供<code>.github/ISSUE_TEMPLATE</code>文件可以给issue提供模版,下面是一个例子,用户提issue时会自动带上如下的提示信息</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="### 问题是什么 问题的具体描述,尽量详细 ### 环境 - 手机: 小米6 - 系统:安卓7.1.1 - 浏览器:chrome 61 - jslib-base版本:0.2.0 - 其他版本信息 ### 在线例子 如果有请提供在线例子 ### 其他 其他信息 " title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs markdown"><code><span class="hljs-section">### 问题是什么</span> 问题的具体描述,尽量详细 <span class="hljs-section">### 环境</span> <span class="hljs-bullet">- </span>手机: 小米6 <span class="hljs-bullet">- </span>系统:安卓7.1.1 <span class="hljs-bullet">- </span>浏览器:chrome 61 <span class="hljs-bullet">- </span>jslib-base版本:0.2.0 <span class="hljs-bullet">- </span>其他版本信息 <span class="hljs-section">### 在线例子</span> 如果有请提供在线例子 <span class="hljs-section">### 其他</span> 其他信息 </code></pre> <h2 id="articleHeader18">jsmini</h2> <p><a href="https://github.com/jsmini" rel="nofollow noreferrer" target="_blank">jsmini</a>是基于jslib-base的一系列库,jsmini的理念是小而美,并且无第三方依赖,开源了很多能力,能够<br />助力库开发者</p> <h2 id="articleHeader19">总结</h2> <p>五年弹指一挥间,本文总结了自己做开源项目的一些经验,希望能够帮助大家,所有介绍的内容都可以在<a href="https://github.com/yanhaijing/jslib-base" rel="nofollow noreferrer" target="_blank">jslib-base</a>里面找到</p> <p>jslib-base是一个拿来即用脚手架,赋能js第三方库开源,快速开源一个标准的js库</p> <p>最后再送给大家一句话,开源一个项目,<code>重在开始,贵在坚持</code></p> <p>最后推荐下我的新书《<a href="http://www.js-code.com/tag/react" title="React" target="_blank">React</a>状态管理与同构实战》,深入解读前沿同构技术,感谢大家支持</p> <p>京东:<a href="https://item.jd.com/12403508.html" rel="nofollow noreferrer" target="_blank">https://item.jd.com/12403508.html</a></p> <p>当当:<a href="http://product.dangdang.com/25308679.html" rel="nofollow noreferrer" target="_blank">http://product.dangdang.com/25308679.html</a></p> <p>最后最后招聘前端,后端,客户端啦!地点:北京+上海+成都,感兴趣的同学,可以把简历发到我的邮箱: yanhaijing@yeah.net</p> <p>原文网址:<a href="http://yanhaijing.com/javascript/2018/08/17/2020-js-lib/" rel="nofollow noreferrer" target="_blank">http://yanhaijing.com/javascr...</a></p> <p></code></p>

总结

以上是脚本宝典为你收集整理的

2020年如何写一个现代的JavaScript库

全部内容,希望文章能够帮你解决

2020年如何写一个现代的JavaScript库

所遇到的程序开发问题,欢迎加入QQ群277859234一起讨论学习。如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典网站推荐给程序员好友。 本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。

80%的人都看过