开发基于 Node.js 的前端工具

发布于 2022-05-12 14:53:56 字数 11507 浏览 1155 评论 1

其实自从有了 Node.js,Jser 们可以脱离浏览器做各种各样有趣的事,在开发中,各种JS库帮助我们改善开发流程,提高开发效率,比如 webpack/babel 等等。

今天这里我们就主要讲讲怎么基于 Node.js 来开发(小)工具,提高我们的工作效率,满足各种实际需要。

一. 相关前置知识

1. Environments

The environment is an area that the shell builds every time that it starts a session that contains variables that define system properties.

每当shell新开启一个会话时,shell 都会生成 environment,environment 里都是些定义系统属性的变量。

$ env

TERM_PROGRAM=Apple_Terminal
SHELL=/bin/zsh
USER=creeper
PATH=/Users/creeper/git/depot_tools:/usr/local/sbin:/Users/creeper/.nvm/versions/node/v6.9.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/dist
...
MANPATH=/Users/creeper/.nvm/versions/node/v6.9.0/share/man:/usr/local/share/man:/usr/share/man:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/share/man:/Applications/Xcode.app/Contents/Developer/usr/share/man:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/man
NVM_PATH=/Users/creeper/.nvm/versions/node/v6.9.0/lib/node
NVM_BIN=/Users/creeper/.nvm/versions/node/v6.9.0/bin
_=/usr/bin/env

很多程序都会用到这里的变量,比如 nvm 会用这里的 NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/dist,作为代理服务器地址,去淘宝的源下载 node 来安装,提高速度。

PATH

在 environment 这么多变量里,有一个变量需要特别注意,就是PATH=/Users/creeper/git/depot_tools:/usr/local/sbin:/Users/creeper/.nvm/versions/node/v6.9.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

In UNIX / Linux file systems, the human-readable address of a resource is defined by PATH. It is an environmental variable that tells the shell which directories to search for executable files (i.e., ready-to-run programs) in response to commands issued by a user.

PATH 指定了 Shell 从哪些目录去查找执行文件(PATH 在 windows 里可以通过环境变量去设置)。当我们在shell里输入比如 ls 时,其实shell会找到 /bin/ls 来执行。

2. shebang line(#!) 与可执行文件

上面一段提到了可执行文件,在这里我们只讲其中的一块——script文件。任何以shebang line(#!),开头的文件即可执行的脚本,其中shebang line指定了用什么(解释器)来解释执行脚本。

比如常见的python脚本,你可以看到第一行是这样的:

#!/usr/bin/env python

对 node.js 来说,shebang line 通常这么写:

#!/usr/bin/env node

对有这行的文件,当你shell里执行./my_script时,系统会调用node来解释执行my_script文件。注意,shebang line 是可以加上参数的,如#!/usr/bin/env node --harmony

以常见的webpack为例,当你npm i webpack之后,你可以找到这样一个文件node_modules/.bin/webpack,它即webpack的可执行文件。你可以shell里执行node_modules/.bin/webpack,那么会输出help信息。

那么我们稍微看下node_modules/.bin/webpack这个文件:

#!/usr/bin/env node

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var path = require("path");

果然是shebang line加上js代码。

3. 全局安装的 npm 包为什么可以在 shell 里直接使用?

接着上面两段,我们差不多明白用 node.js 写工具的原理了,和 python 没什么不同。但是,我们全局安装的一些 npm 包,比如 grunt/gulp/webpack,为什么可以直接在shell里执行呢,和ls之类一样?其实就是因为:

1、全局安装的 npm 包安装的位置是固定的,可执行文件存放的位置也是固定的。比如我这里,全局包放在 /Users/creeper/.nvm/versions/node/v6.9.0/lib/node_modules,可执行文件放在/Users/creeper/.nvm/versions/node/v6.9.0/bin

$ ls -al /Users/creeper/.nvm/versions/node/v6.9.0/bin
drwxr-xr-x  19 creeper  staff       646  3 16 16:37 .
drwxr-xr-x  10 creeper  staff       340 10 27 20:01 ..
lrwxr-xr-x   1 creeper  staff        33 10 27 20:06 cnpm -> ../lib/node_modules/cnpm/bin/cnpm
lrwxr-xr-x   1 creeper  staff        43  3 16 16:37 crn-cli -> ../lib/node_modules/@ctrip/crn-cli/index.js

当然,这里的路径都是我本机的,不同机器会有不一样的路径。另外,可执行文件用放在 这个词描述可能不准确,这里其实是 软链接(symlink

2、/Users/creeper/.nvm/versions/node/v6.9.0/bin这个路径是在环境变量PATH里的,所以当你执行 crn-cli 时,shell 可以正确找到这个命令。

PATH=/Users/creeper/git/depot_tools:/usr/local/sbin:/Users/creeper/.nvm/versions/node/v6.9.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

4. 当 npm run command 时,我们是在做什么?

我们一般的开发中,经常会用到 npm run command,比如 npm test 等等,这里简单补充下。npm run-script <command> [-- <args>...] 可以执行在 package.json 的 scripts 中的相应命令。

// 来自 React repo
{
  "scripts": {
    "build": "grunt build",
    "linc": "git diff --name-only --diff-filter=ACMRTUB `git merge-base HEAD master` | grep '\.js

上面是从 React 的 package.json 截出的,当我们在 shell 里执行 npm run build 时,其实执行的就是 grunt build

注意:除了shell已经存在的PATH,npm run会添加node_modules/.bin到PATH里。也就是说,node_modules/.bin的执行文件是可以直接执行的,不用node_modules/.bin/grunt build这种。

二. 结合实例来具体阐述工具编写

前面讲完了一些前置知识,下面结合实例来讲工具编写,主要是我们组实际用到的 :)

1. 图片处理--直接写JS

虽然前面讲了一大堆可执行文件相关的,但对一些一次性工作,我们其实可以直接写JS,然后node执行就好了。这是最简单快捷的。当我们shell里执行node x.js --args,args可以通过process.argv来访问:

$ node run.js *.png

[ '/Users/creeper/.nvm/versions/node/v6.9.0/bin/node',
  '/Users/creeper/Downloads/切图/run.js',
  '*.png' ]
  
$ node run.js test/*.jpg
[ '/Users/creeper/.nvm/versions/node/v6.9.0/bin/node',
  '/Users/creeper/Downloads/切图/run.js',
  'test/00010_西安_SIA_12_中国.jpg',
  'test/00012_南京_NKG_15_中国.jpg',
  'test/00013_无锡_WUX_15_中国.jpg',
  'test/00015_扬州_YTY_15_中国.jpg',
  'test/00017_杭州_HGH_16_中国.jpg',
  'test/00019_舟山_HSN_16_中国.jpg' ]

可以看到process.argv[0]固定是node本身路径,process.argv[1]是文件路径,process.argv.slice(2)才是我们输入的参数。

具体到我们这里图片处理(UED有很多图片处理工作):

需求: 有一大堆大图(几百张),请导出640x420, 582×178, 284x178, 178x178, 268x106五种尺寸(中心缩放/切割),且每种尺寸有高斯模糊和正常两种。
方案: 手动PS处理肯定不行,所以 imagemagick(负责图片处理)+ JS(负责参数和一些额外工作,比如图片分类/改名)。

核心代码:

// ...
const GaussianBlur = `20x8`
const getCmd = (file, size, destFile, gaus) => {
    return `convert ${file} -resize "${size}^"${
      gaus ? (' -gaussian-blur ' + GaussianBlur) : ''
    } -gravity center -crop ${size}+0+0 +repage ${destFile}`
}
// ...

使用:

node processImg.js images/**/*.jpg

2017-04-05 6 42 01

注: 使用通配符时,你获取的参数是通配符匹配的文件列表(如前面代码所示),如果你想获取原字符串,请用引号,如node processImg.js "images/**/*.jpg"

2. git仓库更新后重新编译静态网站--githook + npm script

http://guide.cui.design/

这个是某种程度的css组件开发(专注CSS),一些我们机票部门的公用组件,比如paybar这些,可以通过这个项目有个公共的最优实现,并在各个应用中保持一致。

以上并没有难点,难点主要在部署:即我们希望每次提交后(gitlab),可以在我们组的服务器同步最新的代码,有最新的预览,并且这些应该完全自动化的。

代码的同步我们用了gitlab的API(这块是我同事在做),但预览呢?

  1. watch文件,有变动自动build。
  2. 使用githook,每次在服务器上仓库同步后自动build。

基于性能原因,第2种当然更好,所以我们选择hook仓库的post-merge(每册服务器本地仓库更新后调用)。稍微看下post-merge的内容:

#!/bin/sh

cd /usr/share/nginx/html/repos/Dolphin-UI/
npm run build

不能更简单了,但的确做到了自动化和高效。

注: 一个坑,需要注意/usr/share/nginx/html/repos/Dolphin-UI/.git/hooks/post-merge文件的权限,没有执行权限会导致脚本执行失败。

wechatimg243

3. sugar-cli--npm package

sugar-cli是我们组原型开发(除RN外)的工具,主要提供模版和css编译的功能。

http://cui.design/

背景和需求: 原来还是基于PHP那一套开发原型,比较笨重;新的开发环境希望基于node.js,有简单但足够的模板语法,支持一种(或多种)css预处理语言,易于部署(预览)等等。

结合这些需求,最终开发了一个npm包sugar-cli,只要全局安装后,一个命令即可快速开始开发:

  1. 开箱即用,不需要其它依赖,基本不需要额外的配置(这也是为什么选择发布一个cli工具)。
  2. 类似handlebars的完善模板。
  3. postcss/sass/less全支持,完善的sourcemap。
  4. 支持livereload和dev server,改善开发体验。

这里就不具体描述功能了,下面主要讲讲怎么开发一个cli工具。

核心很简单:代码(含shebang line) + package.json配置。

下面是sugar-clisugar static(运行一个静态文件服务器)的实现:

#!/usr/bin/env node

const program = require('commander')

program
    .option('-a, --host <host>', 'server host, default to "0.0.0.0"')
    .option('-p, --port <port>', 'server port, default to 2333')
    .on('--help', () => {
        console.log(colors.green('  Examples:'))
        console.log()
        console.log(colors.gray('    $ sugar static'))
    })
    .parse(process.argv)

const root = program.args[0]

serveStatic(root, program.host, program.port)
// 这里省略 serveStatic 具体实现

然后,我们需要在package.json中配置:

  "bin": {
    "sugar": "bin/sugar.js"
  },

bin 是个 map,其中 key 是 command,value 是对应可执行文件。当全局安装时,npm会 symlink 这个可执行文件到 prefix/bin;本地安装时,则 symlink 这个可执行文件到 ./node_modules/.bin/。 (这一段可配合上面 全局安装的 npm 包为什么可以在shell里直接使用? 一起食用)。

结合这两个,我们即可轻松开发一个前端工具。剩下的我们可以发布到 npm,然后请同学们试用即可。

2017-04-06 12 24 03

| xargs eslint --", "lint": "grunt lint", "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json", "test": "jest", "flow": "flow" } }

上面是从 React 的 package.json 截出的,当我们在 shell 里执行 npm run build 时,其实执行的就是 grunt build

注意:除了shell已经存在的PATH,npm run会添加node_modules/.bin到PATH里。也就是说,node_modules/.bin的执行文件是可以直接执行的,不用node_modules/.bin/grunt build这种。

二. 结合实例来具体阐述工具编写

前面讲完了一些前置知识,下面结合实例来讲工具编写,主要是我们组实际用到的 :)

1. 图片处理--直接写JS

虽然前面讲了一大堆可执行文件相关的,但对一些一次性工作,我们其实可以直接写JS,然后node执行就好了。这是最简单快捷的。当我们shell里执行node x.js --args,args可以通过process.argv来访问:

可以看到process.argv[0]固定是node本身路径,process.argv[1]是文件路径,process.argv.slice(2)才是我们输入的参数。

具体到我们这里图片处理(UED有很多图片处理工作):

需求: 有一大堆大图(几百张),请导出640x420, 582×178, 284x178, 178x178, 268x106五种尺寸(中心缩放/切割),且每种尺寸有高斯模糊和正常两种。
方案: 手动PS处理肯定不行,所以 imagemagick(负责图片处理)+ JS(负责参数和一些额外工作,比如图片分类/改名)。

核心代码:

使用:

2017-04-05 6 42 01

注: 使用通配符时,你获取的参数是通配符匹配的文件列表(如前面代码所示),如果你想获取原字符串,请用引号,如node processImg.js "images/**/*.jpg"

2. git仓库更新后重新编译静态网站--githook + npm script

http://guide.cui.design/

这个是某种程度的css组件开发(专注CSS),一些我们机票部门的公用组件,比如paybar这些,可以通过这个项目有个公共的最优实现,并在各个应用中保持一致。

以上并没有难点,难点主要在部署:即我们希望每次提交后(gitlab),可以在我们组的服务器同步最新的代码,有最新的预览,并且这些应该完全自动化的。

代码的同步我们用了gitlab的API(这块是我同事在做),但预览呢?

  1. watch文件,有变动自动build。
  2. 使用githook,每次在服务器上仓库同步后自动build。

基于性能原因,第2种当然更好,所以我们选择hook仓库的post-merge(每册服务器本地仓库更新后调用)。稍微看下post-merge的内容:

不能更简单了,但的确做到了自动化和高效。

注: 一个坑,需要注意/usr/share/nginx/html/repos/Dolphin-UI/.git/hooks/post-merge文件的权限,没有执行权限会导致脚本执行失败。

wechatimg243

3. sugar-cli--npm package

sugar-cli是我们组原型开发(除RN外)的工具,主要提供模版和css编译的功能。

http://cui.design/

背景和需求: 原来还是基于PHP那一套开发原型,比较笨重;新的开发环境希望基于node.js,有简单但足够的模板语法,支持一种(或多种)css预处理语言,易于部署(预览)等等。

结合这些需求,最终开发了一个npm包sugar-cli,只要全局安装后,一个命令即可快速开始开发:

  1. 开箱即用,不需要其它依赖,基本不需要额外的配置(这也是为什么选择发布一个cli工具)。
  2. 类似handlebars的完善模板。
  3. postcss/sass/less全支持,完善的sourcemap。
  4. 支持livereload和dev server,改善开发体验。

这里就不具体描述功能了,下面主要讲讲怎么开发一个cli工具。

核心很简单:代码(含shebang line) + package.json配置。

下面是sugar-clisugar static(运行一个静态文件服务器)的实现:

然后,我们需要在package.json中配置:

bin 是个 map,其中 key 是 command,value 是对应可执行文件。当全局安装时,npm会 symlink 这个可执行文件到 prefix/bin;本地安装时,则 symlink 这个可执行文件到 ./node_modules/.bin/。 (这一段可配合上面 全局安装的 npm 包为什么可以在shell里直接使用? 一起食用)。

结合这两个,我们即可轻松开发一个前端工具。剩下的我们可以发布到 npm,然后请同学们试用即可。

2017-04-06 12 24 03

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

给不了的爱 2022-05-04 11:19:25

讲的很详细,学习了,谢谢

~没有更多了~

关于作者

眼藏柔

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

烙印

文章 0 评论 0

singlesman

文章 0 评论 0

独孤求败

文章 0 评论 0

晨钟暮鼓

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文