Skip to content
Mithril.js 2
Main Navigation 指南API

简体中文

English
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

简体中文

English
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

主题

Sidebar Navigation

API

核心 API

m(selector, attributes, children)

render(element, vnodes)

mount(root, component)

route(root, defaultRoute, routes)

request(options)

parseQueryString(string)

buildQueryString(object)

buildPathname(object)

parsePathname(string)

trust(html)

fragment(attrs, children)

redraw()

censor(object, extra)

可选 API

stream()

指南

页面导航

m(selector, attributes, children) ​

描述 ​

表示 Mithril.js 视图中的 HTML 元素。

javascript
m('div.foo', { style: { color: 'red' } }, 'hello');
// 渲染为以下 HTML:
// <div class="foo" style="color: red">hello</div>

你也可以使用类似 HTML 的语法,称为 JSX,并使用 Babel 将其转换为等效的 hyperscript 调用。以下代码与上述代码等效。

jsx
<div class="foo" style="color: red">
  hello
</div>

签名 ​

vnode = m(selector, attrs, children)

参数类型必需描述
selectorString|Object|Function是CSS 选择器或 组件
attrsObject否HTML 属性或 DOM 元素属性
childrenArray<Vnode>|String|Number|Boolean否子 vnode,可以写成 splat 参数
返回值Vnode一个 vnode

如何阅读签名

工作原理 ​

Mithril.js 提供了一个 hyperscript 函数 m(),允许使用 JavaScript 语法来描述任何 HTML 结构。它接受一个 selector 字符串(必需),一个 attrs 对象(可选)和一个 children 数组(可选)。

javascript
m('div', { id: 'box' }, 'hello');

// 渲染为以下 HTML:
// <div id="box">hello</div>

m() 函数实际上并不返回一个 DOM 元素。相反,它返回一个 虚拟 DOM 节点,也称为 vnode。vnode 是一个 JavaScript 对象,表示要创建的 DOM 元素。

javascript
// 一个 vnode
var vnode = {
  tag: 'div',
  attrs: { id: 'box' },
  children: [
    /*...*/
  ],
};

要将 vnode 转换为实际的 DOM 元素,请使用 m.render() 函数:

javascript
m.render(document.body, m('br')); // 在 <body> 中放入一个 <br>

多次调用 m.render() 不会 每次都从头开始重新创建 DOM 树。相反,每次调用仅在必要时根据传入的虚拟 DOM 树,对实际 DOM 树进行必要的修改。这种设计是合理的,因为从头开始重新创建 DOM 的代价很高,并且会导致诸如失去输入焦点之类的问题。相比之下,仅在必要时更新 DOM 速度更快,并且更容易维护处理多个用户场景的复杂 UI。

灵活性 ​

m() 函数既是 多态的 又是 参数可变的。 换句话说,它对输入参数的要求非常灵活:

javascript
// 简单的标签
m('div'); // <div></div>

// 属性和子元素是可选的
m('a', { id: 'b' }); // <a id="b"></a>
m('span', 'hello'); // <span>hello</span>

// 带有子节点的标签
m('ul', [
  // <ul>
  m('li', 'hello'), //   <li>hello</li>
  m('li', 'world'), //   <li>world</li>
]); // </ul>

// 数组是可选的
m(
  'ul', // <ul>
  m('li', 'hello'), //   <li>hello</li>
  m('li', 'world') //   <li>world</li>
); // </ul>

CSS 选择器 ​

m() 的第一个参数可以是任何可以描述 HTML 元素的 CSS 选择器。它接受 # (id)、. (class) 和 [] (attribute) 语法的任何有效的 CSS 组合。

javascript
m('div#hello');
// <div id="hello"></div>

m('section.container');
// <section class="container"></section>

m('input[type=text][placeholder=Name]');
// <input type="text" placeholder="Name" />

m("a#exit.external[href='https://example.com']", 'Leave');
// <a id="exit" class="external" href="https://example.com">Leave</a>

如果省略标签名称,Mithril.js 假定为 div 标签。

javascript
m('.box.box-bordered'); // <div class="box box-bordered"></div>

通常,建议对静态属性(即值不变的属性)使用 CSS 选择器,并为动态属性值传递一个属性对象。

javascript
var currentURL = '/';

m(
  'a.link[href=/]',
  {
    class: currentURL === '/' ? 'selected' : '',
  },
  'Home'
);

// 渲染为以下 HTML:
// <a href="/" class="link selected">Home</a>

作为第二个参数传递的属性 ​

你可以在第二个可选参数中传递属性、事件和生命周期钩子(有关详细信息,请参见下一节)。

JS
m("button", {
  class: "my-button",
  onclick: function() {/* ... */},
  oncreate: function() {/* ... */}
})

如果此类属性的值为 null 或 undefined,则该属性会被忽略。

如果 m() 的第一个和第二个参数中都存在类名,则它们会按照预期合并在一起。如果第二个参数中 class 的值为 null 或 undefined,则将其忽略。

如果同一个属性同时存在于第一个和第二个参数中,则第二个参数优先,即使它是 null 或 undefined。

DOM 属性 ​

Mithril.js 使用 JavaScript API 和 DOM API (setAttribute) 来处理属性。这意味着你可以使用两种方式来访问属性。

例如,在 JavaScript API 中,readonly 属性称为 element.readOnly(注意大写)。在 Mithril.js 中,以下所有写法都支持:

javascript
m('input', { readonly: true }); // 小写
m('input', { readOnly: true }); // 大写
m('input[readonly]');
m('input[readOnly]');

这甚至包括自定义元素。例如,你可以在 Mithril.js 中使用 A-Frame,没有问题!

javascript
m('a-scene', [
  m('a-box', {
    position: '-1 0.5 -3',
    rotation: '0 45 0',
    color: '#4CC3D9',
  }),

  m('a-sphere', {
    position: '0 1.25 -5',
    radius: '1.25',
    color: '#EF2D5E',
  }),

  m('a-cylinder', {
    position: '1 0.75 -3',
    radius: '0.5',
    height: '1.5',
    color: '#FFC65D',
  }),

  m('a-plane', {
    position: '0 0 -4',
    rotation: '-90 0 0',
    width: '4',
    height: '4',
    color: '#7BC8A4',
  }),

  m('a-sky', {
    color: '#ECECEC',
  }),
]);

对于自定义元素,Mithril.js 不会自动将属性字符串化,以防它们是对象、数字或某些其他非字符串值。因此,假设你有一个自定义元素 my-special-element,它具有一个 elem.whitelist 数组 getter/setter 属性,你可以这样做,并且它会按照预期工作:

javascript
m('my-special-element', {
  whitelist: [
    'https://example.com',
    'https://neverssl.com',
    'https://google.com',
  ],
});

如果这些元素具有类或 ID,则简写仍然可以按预期工作。再举一个 A-Frame 示例:

javascript
// 这两个是等效的
m('a-entity#player');
m('a-entity', { id: 'player' });

请注意,所有具有特殊语义的属性,例如生命周期属性、onevent 处理程序、key、class 和 style,它们的处理方式与普通 HTML 元素相同。

Style 属性 ​

Mithril.js 支持字符串和对象作为有效的 style 值。换句话说,以下所有写法都支持:

javascript
m('div', { style: 'background:red;' });
m('div', { style: { background: 'red' } });
m('div[style=background:red]');

如果使用字符串作为 style 的值,重新渲染时会覆盖元素的所有内联样式,而不仅仅是值发生改变的 CSS 规则。

你可以使用连字符 CSS 属性名称(如 background-color)和驼峰式 DOM style 属性名称(如 backgroundColor)。如果你的浏览器支持,你还可以定义 CSS 自定义属性。

Mithril.js 不会尝试向数字值添加单位。它只是将它们字符串化。

事件 ​

Mithril.js 支持所有 DOM 事件的事件处理程序绑定,包括规范中未定义 on${event} 属性的事件,例如 touchstart。

javascript
function doSomething(e) {
  console.log(e);
}

m('div', { onclick: doSomething });

Mithril.js 接受函数和 EventListener 对象。因此,以下写法也有效:

javascript
var clickListener = {
  handleEvent: function (e) {
    console.log(e);
  },
};

m('div', { onclick: clickListener });

默认情况下,当通过 hyperscript 绑定的事件触发时,这将在你的事件回调返回后触发 Mithril.js 的自动重绘(假设你使用的是 m.mount 或 m.route 而不是直接使用 m.render)。你可以通过在其上设置 e.redraw = false 来专门为单个事件禁用自动重绘:

javascript
m('div', {
  onclick: function (e) {
    // 阻止自动重绘
    e.redraw = false;
  },
});

属性 ​

Mithril.js 支持通过属性访问的 DOM 功能,例如 <select> 的 selectedIndex 和 value 属性。

javascript
m('select', { selectedIndex: 0 }, [
  m('option', 'Option A'),
  m('option', 'Option B'),
]);

组件 ​

组件 允许你将逻辑封装到一个单元中,并像使用元素一样使用它。它们是构建大型、可扩展应用程序的基础。

组件是任何包含 view 方法的 JavaScript 对象。要使用组件,请将组件作为 m() 的第一个参数传入,而不是传入 CSS 选择器字符串。你可以通过定义属性和子元素将参数传递给组件,如下例所示。

javascript
// 定义一个组件
var Greeter = {
  view: function (vnode) {
    return m('div', vnode.attrs, ['Hello ', vnode.children]);
  },
};

// 使用它
m(Greeter, { style: 'color:red;' }, 'world');

// 渲染为以下 HTML:
// <div style="color:red;">Hello world</div>

要了解有关组件的更多信息,请查看组件页面。

生命周期方法 ​

Vnode 和组件可以具有生命周期方法(也称为 hooks),这些方法在 DOM 元素生命周期的各个时间点被调用。Mithril.js 支持的生命周期方法有:oninit、oncreate、onupdate、onbeforeremove、onremove 和 onbeforeupdate。

生命周期方法的定义方式与 DOM 事件处理程序相同,但接收 vnode 作为参数,而不是 Event 对象:

javascript
function initialize(vnode) {
  console.log(vnode);
}

m('div', { oninit: initialize });
Hook描述
oninit(vnode)在 vnode 渲染为实际的 DOM 元素之前运行。
oncreate(vnode)在 vnode 附加到 DOM 后运行。
onupdate(vnode)每次在 DOM 元素附加到文档时发生重绘时运行。
onbeforeremove(vnode)在从文档中删除 DOM 元素之前运行。如果返回 Promise,Mithril.js 会等待 Promise 完成后才移除 DOM 元素。此方法只在从父 DOM 元素移除的元素上触发,不会在其子元素上触发。
onremove(vnode)在从文档中删除 DOM 元素之前运行。如果定义了 onbeforeremove 钩子,则在 onbeforeremove 执行完毕后调用 onremove。此方法在从其父元素分离的元素及其所有子元素上触发。
onbeforeupdate(vnode, old)在 onupdate 之前运行,如果返回 false,则会阻止对该元素及其所有子元素进行差异比较。

要了解有关生命周期方法的更多信息,请参见生命周期方法页面。

Keys ​

列表中的 Vnode 可以具有一个名为 key 的特殊属性,该属性可用于在生成 vnode 列表的模型数据更改时管理 DOM 元素的标识。

通常,key 应该是数据数组中对象的唯一标识符。

javascript
var users = [
  { id: 1, name: 'John' },
  { id: 2, name: 'Mary' },
];

function userInputs(users) {
  return users.map(function (u) {
    return m('input', { key: u.id }, u.name);
  });
}

m.render(document.body, userInputs(users));

设置 key 意味着,即使 users 数组的顺序被打乱,重新渲染视图后,输入框的顺序仍然保持不变,从而保持正确的焦点和 DOM 状态。

要了解有关 key 的更多信息,请参见 key 页面。

SVG 和 MathML ​

Mithril.js 完全支持 SVG。Xlink 也受支持,但与 Mithril.js 的 pre-v1.0 版本不同,必须显式定义命名空间:

javascript
m('svg', [m("image[xlink:href='image.gif']")]);

MathML 也完全受支持。

使模板动态化 ​

由于嵌套的 vnode 只是普通的 JavaScript 表达式,你可以直接使用 JavaScript 的各种特性来操作它们。

动态文本 ​

javascript
var user = { name: 'John' };

m('.name', user.name); // <div class="name">John</div>

循环 ​

使用 Array 方法,例如 map 来迭代数据列表。

javascript
var users = [{ name: 'John' }, { name: 'Mary' }];

m(
  'ul',
  users.map(function (u) {
    // <ul>
    return m('li', u.name); //   <li>John</li>
    //   <li>Mary</li>
  })
); // </ul>

// ES6+:
// m("ul", users.map(u =>
//   m("li", u.name)
// ))

条件 ​

使用三元运算符在视图中有条件地渲染内容。

javascript
var isError = false;

m('div', isError ? 'An error occurred' : 'Saved'); // <div>Saved</div>

你不能在 JavaScript 表达式中使用 JavaScript 语句,例如 if 或 for。最好完全避免使用这些语句,而是专门使用上面的构造,以便保持模板的结构线性且声明式。

转换 HTML ​

在 Mithril.js 中,格式良好的 HTML 是有效的 JSX。只需要复制粘贴,就可以将独立生成的 HTML 文件集成到使用 JSX 的项目中。

使用 hyperscript 时,需要先将 HTML 转换为 hyperscript 语法,然后才能运行代码。为了方便起见,你可以使用 HTML-to-Mithril-template 转换器。

避免反模式 ​

虽然 Mithril.js 很灵活,但有些代码模式是不推荐的:

避免动态选择器 ​

不同的 DOM 元素具有不同的属性,并且通常具有不同的行为。使选择器可配置可能会导致组件实现细节的泄露。

javascript
// 避免
var BadInput = {
  view: function (vnode) {
    return m('div', [m('label'), m(vnode.attrs.type || 'input')]);
  },
};

与其使用动态选择器,更推荐的做法是显式地编写每种可能的选择器,或者重构代码中可变的部分。

javascript
// 优先选择显式代码
var BetterInput = {
  view: function (vnode) {
    return m('div', [m('label', vnode.attrs.title), m('input')]);
  },
};
var BetterSelect = {
  view: function (vnode) {
    return m('div', [m('label', vnode.attrs.title), m('select')]);
  },
};

// 优先选择重构可变性
var BetterLabeledComponent = {
  view: function (vnode) {
    return m('div', [m('label', vnode.attrs.title), vnode.children]);
  },
};

避免在视图之外创建 vnode ​

如果重绘时发现某个 vnode 与前一次渲染的 vnode 完全相同,Mithril.js 会跳过该 vnode 及其子树的更新。虽然这看起来像是性能优化的机会,但应避免这样做,因为它会阻止该节点树中的动态更改 - 这会导致副作用,例如下游生命周期方法无法在重绘时触发。因此,Mithril.js vnode 是不可变的:新的 vnode 与旧的 vnode 进行比较;对 vnode 的修改不会被保留。

组件文档包含更多详细信息和此反模式的示例。

Pager
上一页API
下一页render(element, vnodes)

基于 MIT 许可证 发布。

版权所有 (c) 2024 Mithril Contributors

https://mithril.js.org/hyperscript.html

基于 MIT 许可证 发布。

版权所有 (c) 2024 Mithril Contributors