开发基于 Node.js 的前端工具
其实自从有了 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注: 使用通配符时,你获取的参数是通配符匹配的文件列表(如前面代码所示),如果你想获取原字符串,请用引号,如
node processImg.js "images/**/*.jpg"
。2. git仓库更新后重新编译静态网站--githook + npm script
这个是某种程度的css组件开发(专注CSS),一些我们机票部门的公用组件,比如paybar这些,可以通过这个项目有个公共的最优实现,并在各个应用中保持一致。
以上并没有难点,难点主要在部署:即我们希望每次提交后(gitlab),可以在我们组的服务器同步最新的代码,有最新的预览,并且这些应该完全自动化的。
代码的同步我们用了gitlab的API(这块是我同事在做),但预览呢?
- watch文件,有变动自动build。
- 使用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
文件的权限,没有执行权限会导致脚本执行失败。3.
sugar-cli
--npm package
sugar-cli
是我们组原型开发(除RN外)的工具,主要提供模版和css编译的功能。背景和需求: 原来还是基于PHP那一套开发原型,比较笨重;新的开发环境希望基于node.js,有简单但足够的模板语法,支持一种(或多种)css预处理语言,易于部署(预览)等等。
结合这些需求,最终开发了一个npm包
sugar-cli
,只要全局安装后,一个命令即可快速开始开发:
- 开箱即用,不需要其它依赖,基本不需要额外的配置(这也是为什么选择发布一个cli工具)。
- 类似handlebars的完善模板。
postcss/sass/less
全支持,完善的sourcemap。- 支持livereload和dev server,改善开发体验。
这里就不具体描述功能了,下面主要讲讲怎么开发一个cli工具。
核心很简单:代码(含shebang line) + package.json配置。
下面是
sugar-cli
中sugar 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,然后请同学们试用即可。
| 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(负责参数和一些额外工作,比如图片分类/改名)。
核心代码:
使用:
注: 使用通配符时,你获取的参数是通配符匹配的文件列表(如前面代码所示),如果你想获取原字符串,请用引号,如node processImg.js "images/**/*.jpg"
。
2. git仓库更新后重新编译静态网站--githook + npm script
这个是某种程度的css组件开发(专注CSS),一些我们机票部门的公用组件,比如paybar这些,可以通过这个项目有个公共的最优实现,并在各个应用中保持一致。
以上并没有难点,难点主要在部署:即我们希望每次提交后(gitlab),可以在我们组的服务器同步最新的代码,有最新的预览,并且这些应该完全自动化的。
代码的同步我们用了gitlab的API(这块是我同事在做),但预览呢?
- watch文件,有变动自动build。
- 使用githook,每次在服务器上仓库同步后自动build。
基于性能原因,第2种当然更好,所以我们选择hook仓库的post-merge
(每册服务器本地仓库更新后调用)。稍微看下post-merge
的内容:
不能更简单了,但的确做到了自动化和高效。
注: 一个坑,需要注意/usr/share/nginx/html/repos/Dolphin-UI/.git/hooks/post-merge
文件的权限,没有执行权限会导致脚本执行失败。
3. sugar-cli
--npm package
sugar-cli
是我们组原型开发(除RN外)的工具,主要提供模版和css编译的功能。
背景和需求: 原来还是基于PHP那一套开发原型,比较笨重;新的开发环境希望基于node.js,有简单但足够的模板语法,支持一种(或多种)css预处理语言,易于部署(预览)等等。
结合这些需求,最终开发了一个npm包sugar-cli
,只要全局安装后,一个命令即可快速开始开发:
- 开箱即用,不需要其它依赖,基本不需要额外的配置(这也是为什么选择发布一个cli工具)。
- 类似handlebars的完善模板。
postcss/sass/less
全支持,完善的sourcemap。- 支持livereload和dev server,改善开发体验。
这里就不具体描述功能了,下面主要讲讲怎么开发一个cli工具。
核心很简单:代码(含shebang line) + package.json配置。
下面是sugar-cli
中sugar static
(运行一个静态文件服务器)的实现:
然后,我们需要在package.json
中配置:
bin
是个 map,其中 key 是 command,value 是对应可执行文件。当全局安装时,npm会 symlink
这个可执行文件到 prefix/bin
;本地安装时,则 symlink
这个可执行文件到 ./node_modules/.bin/
。 (这一段可配合上面 全局安装的 npm 包为什么可以在shell里直接使用? 一起食用)。
结合这两个,我们即可轻松开发一个前端工具。剩下的我们可以发布到 npm,然后请同学们试用即可。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
讲的很详细,学习了,谢谢