Mithril JSX

JSX

描述

JSX 是一个语法扩展,可以让你在 JavaScript 中直接使用 HTML 语法。它不是任何 JavaScript 标准的一部分,也不是构建应用所必需的,你可以根据你的喜好来决定是否使用它。

var MyComponent = {
    view: function() {
        return m("main", [
            m("h1", "Hello world"),
        ])
    }
}

// 使用 JSX 可以写成:
var MyComponent = {
    view: function() {
        return (
            <main>
                <h1>Hello world</h1>
            </main>
        )
    }
}

当使用 JSX 语法时,可以使用大括号在 JSX 中插入 JavaScript 表达式:

var greeting = "Hello"
var url = "http://google.com"
var link = <a href={url}>{greeting + "!"}</a>
// 以上代码将生成 <a href="http://google.com">Hello</a>

可以通过首字母大写的组件名来调用组件:

m.mount(document.body, <MyComponent />)
// 等效于 m.mount(document.body, m(MyComponent))

安装

可以通过 Babel 插件来使用 JSX。

独立安装 Babel

如果你没有使用 Webpack,可以把 Babel 作为一个独立的工具进行使用。

确保你已经安装 Node.js,在项目文件夹下执行命令来初始化项目:

npm init -y

执行以下命令来安装 Babel:

npm install babel-cli babel-preset-es2015 babel-plugin-transform-react-jsx --save-dev

安装完后会创建 .babelrc 文件:

{
    "presets": ["es2015"],
    "plugins": [
        ["transform-react-jsx", {
            "pragma": "m"
        }]
    ]
}

然后在命令行中运行:

babel src --out-dir bin --source-maps

通过 Webpack 使用 Babel

如果你的项目已经使用了 Webpack,你可以按以下步骤把 Babel 集成到 Webpack 中。首先执行以下命令安装 Babel:

npm install babel-core babel-loader babel-preset-es2015 babel-plugin-transform-react-jsx --save-dev

安装完后会创建 .babelrc 文件:

{
    "presets": ["es2015"],
    "plugins": [
        ["transform-react-jsx", {
            "pragma": "m"
        }]
    ]
}

然后,创建一个 webpack.config.js 配置文件:

module.exports = {
    entry: './src/index.js',
    output: {
        path: './bin',
        filename: 'app.js',
    },
    module: {
        loaders: [{
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader'
        }]
    }
}

这个配置文件设置了应用的入口文件为 src/index.js,且打包后输出到 bin/app.js

要运行 bundler,需要设置一个 npm 脚本。打开 package.json 文件并在 "scripts" 中添加一行命令:

{
    "name": "my-project",
    "scripts": {
        "start": "webpack -d --watch"
    }
}

现在,可以在命令行中执行以下命令来运行 bundler:

npm start

生产环境构建

要压缩生成的文件,请打开 package.json 文件,并添加一个新的 npm 脚本 build

{
    "name": "my-project",
    "scripts": {
        "start": "webpack -d --watch",
        "build": "webpack -p",
    }
}

在生产环境中,你可以使用钩子来自动运行构建脚本。这是一个使用 Heroku 的例子:

{
    "name": "my-project",
    "scripts": {
        "start": "webpack -d --watch",
        "build": "webpack -p",
        "heroku-postbuild": "webpack -p"
    }
}

JSX vs hyperscript

JSX 引入了一个非标准的语法,它必须借助工具才能运行,但它允许开发者直接在 JavaScript 使用 HTML 语法。使用 JSX 代替 HTML 语法的好处是 JSX 的语法更加严格,它会在适当时候提示语法错误;而 HTML 的语法太宽松了,可能产生了语法错误但你很难发现。

和 HTML 不同的是,JSX 区分大小写。比如 <div className="test"></div><div classname="test"></div> 是不同的,前者会编译成 m("div", {className: "test"}),而后者会编译成 m("div", {classname: "test"}),后者并不是创建类属性的正确方式。幸运的时,Mithril 支持标准的 HTML 属性名,因此,这个例子可以像普通 HTML 一样来写:<div class="test"></div>

JSX 对于那些以写 HTML 为主,且 JavaScript 经验不足的团队比较有用,但它需要大量的工具来维护,而纯 HTML 大多数情况下都可以直接在浏览器中打开。

Hyperscript 是指 JSX 编译后生成的语法。Hyperscript 的语法也是可读的,你可以直接使用它的语法来编写,不一定要用 JSX 来生成(文档中大多数地方用的都是 Hyperscript)。Hyperscript 往往比 JSX 更简洁,因为:

此外,因为 hyperscript 是标准的 javaScript 语法,所以它的缩进会比 JSX 更自然:

//JSX
var BigComponent = {
    activate: function() {/*...*/},
    deactivate: function() {/*...*/},
    update: function() {/*...*/},
    view: function(vnode) {
        return [
            {vnode.attrs.items.map(function(item) {
                return <div>{item.name}</div>
            })}
            <div
                ondragover={this.activate}
                ondragleave={this.deactivate}
                ondragend={this.deactivate}
                ondrop={this.update}
                onblur={this.deactivate}
            ></div>
        ]
    }
}

// hyperscript
var BigComponent = {
    activate: function() {/*...*/},
    deactivate: function() {/*...*/},
    update: function() {/*...*/},
    view: function(vnode) {
        return [
            vnode.attrs.items.map(function(item) {
                return m("div", item.name)
            }),
            m("div", {
                ondragover: this.activate,
                ondragleave: this.deactivate,
                ondragend: this.deactivate,
                ondrop: this.update,
                onblur: this.deactivate,
            })
        ]
    }
}

在较复杂的应用中,组件的控制流和配置的代码可能比标签更多,因此 hyperscript 比 JSX 代码可能有更好的可读性。

另外,hyperscript 是纯 JavaScript 代码,不像 JSX 那样需要编译才能产生可运行的代码。

HTML 转换为 hyperscript

在 Mithril 中,要把一个格式规范的 HTML 文件集成到使用 JSX 的项目中,除了复制粘贴外,只需要做少量的修改。

使用 hyperscript 时,需要把 HTML 代码转为 hyperscript 语法才能运行。为方便起见,你可以使用HTML-to-Mithril 模版转换器