在开发中,我们经常会编写或者使用很多常见的小功能,就拿前端来讲,解析 URL 查询字符串、时间格式化、日期格式化,这些功能或者说方法再常见不过了。当然,现在很多常用的小功能,大多都能在 NPM 上找到。但这些模块的功能通常都比较全面,功能全面的模块,代码体积(size)也是可想而知的,而且可能还包含了你并不需要的功能。所以,有时候整理下你自己写过的代码,你会发现,你也可以整出个小工具箱来,而且对于一些小项目特别适用。

可能你会认为,接下来的内容是要开始讲“如何创建 JavaScript 库”了,对不起,可能要让你失望了。因为,这篇文章意在提高我们平时的开发效率,是一些使用常用的、可封装代码的方式的总结,让自己写过的代码也可以开箱即用。

代码片段

最简单的方式,是直接使用代码片段(code snippets)。现在大多数代码编辑器都带有代码片段功能,比如 Visual Studio Code。

在 Visual Studio Code 中内置了很多默认的代码片段,比如在编辑器中输入 forof

在弹出的提示列表中,只要列表项左侧的图标是一个底部是点线,其他边是实线的矩形方块,都是可以直接使用的代码片段。

除了内置的代码片段,你还可以创建用户代码片段(Code - 首选项 - 用户代码片段),新建或者选择一个代码片段的类型,然后,在打开的一个 *.json 文件中会显示带有注释的、预设的部分代码片段(console.log),比如像下面这个:

{
  // Place your snippets for javascript here. Each snippet is defined under a snippet name and has a prefix, body and
  // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
  // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
  // same ids are connected.
  // Example:
  "Print to console": {
    "prefix": "log",
    "body": [
      "console.log('$1');",
      "$2"
    ],
    "description": "Log output to console"
  }
}

其中,需要设置的字段也很容易看懂,比如 prefix ,就是在你写代码时输入的代码片段快捷指令,选择后,就可以生成 body 中的代码。像上面这段,当输入 log 时,编辑器会自动弹出帮助提示列表,然后,选择你预设的代码片段指令,并按下回车,代码片段就会立刻自动生成。

下面是一个创建 React 标准组件的代码片段:

{
  "React standard class component": {
    "scope": "javascript,typescript",
    "prefix": "rc",
    "body": [
      "import React, { Component } from 'react';",
      "",
      "class $1 extends Component {",
      "  render() {",
      "    return (",
      "      <div>$1</div>",
      "    );",
      "  }",
      "}",
      "",
      "export default $1;"
    ],
    "description": "init React standard class component"
  },

  ...
}

当你在编辑器中输入 rc,就会出现如下提示:

Gist

自定义的用户 Code Snippets 只能在本地使用,如果换一台电脑,你就无能为力了。也许你也曾经历过,把 WebStorm 等编辑器的配置导出,并保存起来(云端、U 盘等),等下次换一台电脑时,可以重新导入。尽管如此,也还是相当麻烦的,而且也不那么科技。

所以,GithubGist 就可以登场了。

Gist 的功能非常简单实用。如上图所示,输入描述、文件名(可包含文件类型)、代码内容,另外,还可以设置代码缩进格式等。

当我们需要创建一组代码片段时,可以点击“Add file”按钮增加文件。

Gist 还可以创建私密的(无法被搜索到的)和公开的代码片段,可以登录 Github ,也可以匿名使用。

创建的代码片段,Gist 还会帮你生成一个 JS 文件地址,可以在 HTML 中直接引用以方便调试。而且,你可以创建无限数量的代码片段,非常 Cool。

代码仓库

当你常用的代码片段有足够多,并且可以归类时,创建一些可以直接使用的 JS 文件,或者模块可能更为合适。这时候,我们需要有一个可以归档的 Git 代码仓库。

有了这个代码仓库,我们可以随时 clone 下来,寻找并复制一些需要的文件或者模块,然后在项目中可以直接使用(引用),比如:

// toolbox.js
var Toolbox = {};
Toolbox.formatDate = function(date) {
  ...
};
<html>
  <body>
    ...
    <script src="toolbox.js"></script>
  </body>
</html>

虽然这样的方式,可以收集和归类成你的工具库,但现在已经 2019 年了,直接写 ES5 的代码,显然很不现代。这时候,你需要制作一个 Library,也就是我们在开头提到的。

Library

JavaScript Library 有别于代码片段,使用场景也有些不同。但开发一个 Library 有一个明显的好处,除了可以像上面那样,直接 src 引用完整的包之外,还可以在现代的前端项目中,通过 ESModule 方式导入。

import { formatDate } from 'toolbox';

当然,前提是你需要发布你的 Library 到 NPM。

现在,制作一个 Library 也有很多方式,如果是个比较简单的 Library,可以参考下 webpack 的官方教程 Authoring Libraries。但现代化的前端项目,工程化要求越来越高,在导入模块后,最终构建生成的 bundle 不希望把未使用的代码打包进来,开启 Tree Shaking 功能是个非常不错的选择。但用 webpack 构建的 Library 无法生成 ESModule 的包,也就没办法体验 Tree Shaking 的价值了。

使用 rollup.js 构建 Library 就酷很多,而且,该工具的构建配置也相当简单。下面是一个生成 ESModule bundle 的配置示例:

// rollup.config.js
export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'esm'
  }
};

和 webpack 比起来是不是简单的令人发指?目前,已经有很多流行的 Library 都使用了 Rollup 来构建项目,比如:React。

当然,Rollup 和 webpack 都有各自的优缺点和使用场景。但 Rollup 就是为 Library 而生的,而且,生成的 ESM 可以直接支持 Tree Shaking。这里,墙裂推荐一个内置 Rollup 的 CLI 脚手架生成器:create-react-library

虽然比起代码片段来讲,Library 的开发成本有点小高,但随着自己的经验积累,自己手边可拿来使用的代码也会越来越多,如果制作一个按模块类型分类的、开箱即用的 Library 对自己还是非常有帮助的。

下图是我自己个人用的一个 Library —— tote-box 的模块分类截图:

总结

程序员平时的时间,会被无数的工作和项目需求堆满。当我们项目经历的越多,就会积累很多好用的、可封装再利用的代码。通过代码片段、代码仓库、Library 等开箱即用的方法可以在一定程度上提高我们的开发效率,从而腾出时间来学习更多技能,让自己变的更加优秀。