Todomvc with ES6 and jspm

jspm 是一个包管理器,使用 SystemJS 这个模块加载器进行模块的管理。SystemJS 是一个所谓 universal module loader,基于 ES6 module loader,除了能加载 ES6 模块,还支持 CommonJS,AMD 及 globals 语法。

1. How jspm load modules

jspm 通过 registry 加载模块,它其实没有自己的仓库,目前只能加载 npm 和 github 上的模块,当然也可以自己通过它的 registry 机制「注册」一些模块之后使用。

jspm 要解决的就是浏览器端模块加载的问题,通过它前端代码可以无缝地使用 npm 和 github 上的大量模块,只需 jspm install example,然后在代码中 import example from "example"(ES6 语法) 就行了,SystemJS 会帮我们在浏览器加载 example,而且是异步的。

1.1 Install jspm

1.1.1 Install jspm CLI:
1
$ npm install jspm/jspm-cli -g
1.1.2 Install local jspm:
1
2
$ cd my-project
$ npm install jspm --save-dev

1.2 Create a project and setup

初始化一个 jspm 项目使用 jspm init 命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ jspm init
Package.json file does not exist, create it? [yes]:
Would you like jspm to prefix the jspm package.json properties under jspm? [yes]:
Enter server baseURL (public folder path) [.]:
Enter jspm packages folder [./jspm_packages]:
Enter config file path [./config.js]:
Configuration file config.js doesn't exist, create it? [yes]:
Enter client baseURL (public folder URL) [/]:
Which ES6 transpiler would you like to use, Traceur or Babel? [traceur]:
ok Verified package.json at package.json
Verified config file at config.js
Looking up loader files...
system.js
system.js.map
system.src.js
es6-module-loader.js.map
es6-module-loader.js
es6-module-loader.src.js
Using loader versions:
es6-module-loader@0.16.6
systemjs@0.16.11
Looking up github:jmcriffey/bower-traceur-runtime
Looking up github:jmcriffey/bower-traceur
Updating registry cache...
ok Installed traceur-runtime as github:jmcriffey/bower-traceur-runtime@0.0.88 (0.0.88)
ok Installed traceur as github:jmcriffey/bower-traceur@0.0.88 (0.0.88)
ok Loader files downloaded successfully

这会在项目根目录下创建一个 config.js 文件,一个 package.json 和一个 jspm_packages 目录。

  • config.js: 各模块的配置,比如要安装的模块,它的可引用的名称与实际位置的映射。
  • package.json: 即 npm 的 package.json,jspm 的依赖都将在这个文件中。
  • jspm_packages: 使用 jspm install 安装的模块都在这个目录下。jspm init 默认会安装 system.js,es6-module-loader.js 和 bower-traceur。

1.3 Basic usage

新建一个 test 目录,进行 jspm 的初始化:

1
2
$ mkdir test; cd $_
$ jspm init

在 test 目录下,创建一个 index.html 文件:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
System.import('./app');
</script>
</head>
<body>
</body>
</html>

以及一个 app.js:

1
console.log('Hello from future.')

现在执行 python -m SimpleHTTPServer,打开 http://localhost:8000,可以看到浏览器加载了 app.js。

安装一些依赖模块试试:

1
2
3
4
5
6
7
$ jspm install jquery
Updating registry cache...
Looking up github:components/jquery
ok Installed jquery as github:components/jquery@^2.1.4 (2.1.4)
ok Install tree has no forks.
ok Install complete.

因为 jquery 已经在 jspm 的 registry 中注册了,可以直接这么安装;对于未注册的包,需要这样:jspm install npm:some-other-lib,详细可参考 jspm geting started。

编辑 app.js:

1
2
3
import $ from 'jquery';
console.log($.fn.jquery);

刷新浏览器,页面将会加载 jquery,同时还有 traceur。

2. Write the todomvc

我们这个 todomvc 基于 todomvc-jquery 编写,所以我们不准备花太多时间讲 todomvc 本身的实现,主要讲一些 ES6 的用法,及如何使用 jspm/SystemJS 管理依赖模块。

2.1 Setup

1
2
$ mkdir todomvc-jquery-es6; cd $_
$ jspm init

2.2 Create the App

2.2.1 View

我们直接使用 todomvc-jquery 的 index.html 文件。因为它使用了 todomvc-common 和 todomvc-common-css 的 css,所以需要安装它们,安装的步骤很简单:

1
$ jspm install npm:todomvc-common npm:todomvc-app-css

之后在 index.html 中引入这两个 css:

1
2
3
4
5
6
7
...
<head>
...
<link rel="stylesheet" href="jspm_packages/npm/todomvc-common@1.0.2/base.css">
<link rel="stylesheet" href="jspm_packages/npm/todomvc-app-css@2.0.1/index.css">
</head>
...
2.2.2 Model and Controller

在项目的根目录下,创建一个 js 目录,在此目录下新建三个文件:

  • app.js: 这是我们这个 todomvc 的入口文件,在 HTML 中引入它即可;
  • todo-app.js: 这里定义了 App;
  • util.js: 一些 helper 函数。

todomvc-jquery 使用 handlebars 做模版引擎,使用 director 做客户端路由,所以我们需要安装它们,同时也别忘了安装 jquery:

1
$ jspm install jquery handlebars npm:director
todo-app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import $ from 'jquery';
import Handlebars from 'handlebars';
import director from 'director';
import * as util from './util';
const ENTER_KEY = 13;
const ESCAPE_KEY = 27;
export default class App {
constructor() {
this.todos = util.store('todos-jquery');
this.cacheElements();
this.bindEvents();
director.Router({
'/:filter': (filter) => {
this.filter = filter;
this.render();
}
}).init('/all');
}
...
};

todo-app.js 后面的内容我们就省略了,它其实就 export 了一个 App class,注意文件开头我们使用了 ES6 的模块引入语法。

app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
import $ from 'jquery';
import Handlebars from 'handlebars';
import App from './todo-app';
$(()=> {
Handlebars.registerHelper('eq', function(a, b, options) {
return a === b ? options.fn(this) : options.inverse(this);
});
new App();
});

2.3 Wrap it up

如前所述,我们直接在index.html 里 import app.js 就行了:

1
2
3
4
5
6
...
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
System.import('js/app');
</script>

2.4 Bundle for production

jspm 也提供了 bundle 的功能,只需要执行:

1
jspm bundle-sfx js/app

就可以把所有的依赖打包到 bundle.js 文件中,在 index.html 中引入它就行了:

1
2
...
<script src="bundle.js"></script>

点击查看完整代码,可以在这里看 Demo。

3. Reference