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 加载模块,它其实没有自己的仓库,目前只能加载 npmgithub 上的模块,当然也可以自己通过它的 registry 机制「注册」一些模块之后使用。

jspm 要解决的就是浏览器端模块加载的问题,通过它前端代码可以无缝地使用 npmgithub 上的大量模块,只需 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.jses6-module-loader.jsbower-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-jqueryindex.html 文件。因为它使用了 todomvc-commontodomvc-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.htmlimport 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