Package.js:Browser Side JavaScript Package Manager

该项目现已停止维护!

Package.js是一个很方便的JavaScript包依赖管理及打包发布工具,最初是为公司的项目需要所开发的。它的设计目标是使浏览器端的JavaScript UIComponent/App 开发更加模块化。如果只是在开发一个小型的网站,只会混杂在HTML中写几行JS代码用于改善一下用户体验,那就不会考虑到JS模块管理的问题,Package.js也派不上用场。如果正在开发一个中到大型的WebApp,有几十甚至几百个JS和CSS文件及HTML模板,管理这些模块之间的依赖、发布到生产环境时将JS文件如何合并打包自然就需要一个强大的工具。这就是Package.js。

Package.js 主要包含两个部分

Package.js浏览器端的API部分参照了CommonJS/AMD规范,但并不完全兼容。在此基础上还扩展了一些语法,以便于开发包含CSS及HTML模板的JavaScript UI组件。开发目录结构可以体现出这个改进:

MyLib
├── dom
│    └── Style.js # 命名空间为Test.dom.Style的模块文件
├── init.js       # 根命名空间初始化文件
├── _nsconf_.js   # Package.js会读取的配置文件
├── ui
│   ├── Button
│   │    ├── img
│   │    │ └── bg.png
│   │    ├── init.js   # Test.ui.Button命名空间的模块文件
│   │    ├── style.css # UI组件的CSS文件
│   │    └── tpl.html  # UI组件的HTML模板文件
│   └── Form
│        ├── init.js
│        ├── style.css
│        └── tpl.html
├── util
│    └── Cookie.js  # 命名空间为Test.util.Cookie的JS包
└── _xproxy_.html -> ../Package/_xproxy_.html
# _xproxy_.html为Soft Link
# 指向Package.js源码中的Package/_xproxy_.html
# 用于跨域加载HTML模板文件

Package.js的模块定义语法:

//文件路径:Root/ui/Button/init.js
Package.define("Root.ui.Button",
["Root.ui.Pane","Root.util.Tpl","Root.util.Event"],
function (Pane,Tpl,Event) {
    //Pane为Root.ui.Pane
    //Tpl对应Root.util.Tpl
    //依此类推
    //.....
});

在Package.js的语法中,一个JS模块,不但可以导入其它依赖的JS包,还可以导入CSS、HTML模板、JSON数据等文件,并在运行时可以直接取到依赖的文件内容。定义语法如下:

Package.define("NS.ui.Button",["MT.ui.Component"],
{
  tpl:"tpl.html",
  _style:"style.css"
},function (Component) {
  //通过this.assets.tpl访问tpl.html内容
  var bgImgUrl=this.path+"img/bg.png",tpl=this.assets.tpl;
  function Button(opt) {
    //也可以通过当前Package对象的_pkgMeta_属性访问assets
    this.tpl=String2Dom(opt.tpl || Button._pkgMeta_.assets.tpl);
  }
  return Button;
})

在浏览器中,可以使用下面的方法导入一个JS模块:

Package.imports(["Root.ui.Button"],function (Button) {
  var btn=new Button();
  btn.renderTo(document.body);
});

在导入的过程中,Package.js自动帮您做了这些后勤工作:

  1. 加载这个模块所依赖的其它JS模块。
  2. 加载依赖的HTML及CSS文件,甚至可以跨域加载JSON文件。

发布代码:build.js

在开发时,为了模块化,您需要将JS分成一个一个小的模块文件,但发布到生产环境时,为了加载速度上的考虑,您需要将这些JS文件合并成单个的JS文件并压缩,同样,CSS文件也要合并到一起。借助Package.js提供的build.js命令,只需要写三四行JSON配置代码就OK了:

//您的打包配置文件
//build-config.json文件内容
{
  "staticUrls":{"defaults":"http://jex.im/jslibs/"},
  "nsconfs":["../../statics/jslibs/XLib/_nsconf_.js"],
  "includes":["XLib.apps.MainApp","XLib.ui.*"],
  "compress":true // 使用UglifyJS和UglifyCSS进行压缩
}

执行两个命令,分别用于生成合并压缩过后的JS与CSS文件:

# 执行命令
build.js build-config.json   js    all.min.js
build.js build-config.json   css   all.min.css
# 腰不酸了,腿不疼了!

在这期间,build.js除了将所有JS与CSS按依赖的先后顺序合并好之外,还做掉一些其它的后勤工作,如:将CSS文件中的background:url()之类的相对路径转换成绝对URL。在开发时,CSSurl()中始终只需要写相对路径,在部署到生产环境时,build.js会自动将其转换成绝对URL。如果CSS中有使用IE6 Png AlphaImageLoader滤镜,只需要在_nsconf_.js里配置该模块的loader为"wui4ie6",在开发时仍然可以在src里写相对路径,在开发模式下,Package.js也会自动生成使用绝对URL的CSS Rule,在打包时也会对 AlphaImageLoader的src作转换,这样CSS中就永远不需要写绝对URL了。

Package.js 优势和缺点

相比于Require.jsSea.js等一些自由的AMD模块管理器,Package.js 提供的不仅是一个工具,更是一整套解决方案,以及一种开发风格:

Package.js的详细文档请见:Package.js Specification

Github:https://github.com/JexCheng/package-js