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

入门

安装

简单应用

资源

JSX

在旧版浏览器上使用 ES6+

动画

测试

示例

第三方库集成

路径处理

关键概念

虚拟 DOM

组件

生命周期方法

键(Key)

自动重绘系统

杂项

框架对比

从 v1.x 迁移

从 v0.2.x 迁移

API

页面导航

生命周期方法 ​

用法 ​

组件 和 虚拟 DOM 节点 可以拥有生命周期方法,也称为 钩子(hooks),它们在 DOM 元素生命周期的各个阶段被调用。

javascript
// 组件中的示例钩子
var ComponentWithHook = {
  oninit: function (vnode) {
    console.log('initialize component'); // 组件初始化
  },
  view: function () {
    return 'hello';
  },
};

// vnode 中的示例钩子
function initializeVnode() {
  console.log('initialize vnode'); // vnode 初始化
}

m(ComponentWithHook, { oninit: initializeVnode });

所有生命周期方法都接收 vnode 作为它们的第一个参数,并且它们的 this 关键字绑定到 vnode.state。

生命周期方法仅作为 m.render() 调用的副作用被调用。如果 DOM 在 Mithril 之外被修改,它们不会被调用。

DOM 元素生命周期 ​

一个 DOM 元素通常被创建并附加到文档中。然后,当 UI 事件被触发并且数据被更改时,它可能会更新属性或子节点;并且该元素可能会从文档中移除。

元素被移除后,可能会临时保存在内存池中。被缓存到内存池中的元素可能会在后续的更新中被重用(这个过程称为 DOM 回收(DOM recycling))。回收元素可以避免重新创建最近存在的元素副本,从而节省性能开销。

oninit ​

oninit(vnode) 钩子在 vnode 被虚拟 DOM 引擎处理之前被调用。oninit 保证在其 DOM 元素附加到文档之前被调用,并且保证在父 vnode 的子节点之前执行,但是它不保证祖先或后代 DOM 元素是否存在。你不应该在 oninit 方法中访问 vnode.dom。

当一个元素被更新时,这个钩子不会被调用,但是如果一个元素被回收,它会被调用。

与其他钩子一样,oninit 回调中的 this 关键字指向 vnode.state。

oninit 钩子对于根据通过 vnode.attrs 或 vnode.children 传递的参数初始化组件状态非常有用。

javascript
function ComponentWithState() {
  var initialData;
  return {
    oninit: function (vnode) {
      initialData = vnode.attrs.data;
    },
    view: function (vnode) {
      return [
        // 显示来自初始化时间的数据:
        m('div', 'Initial: ' + initialData),
        // 显示当前数据:
        m('div', 'Current: ' + vnode.attrs.data),
      ];
    },
  };
}

m(ComponentWithState, { data: 'Hello' });

你不应该在此方法中同步修改模型数据。由于 oninit 不保证其他元素的状态,因此,通过此方法创建的模型更改可能不会立即反映在 UI 的所有部分,直到下一个渲染周期。

oncreate ​

oncreate(vnode) 钩子在一个 DOM 元素被创建并附加到文档之后被调用。oncreate 保证在渲染周期结束时运行,因此从此方法读取布局值(例如 vnode.dom.offsetHeight 和 vnode.dom.getBoundingClientRect())是安全的。

当一个元素被更新时,这个钩子不会被调用。

与其他钩子一样,oncreate 回调中的 this 关键字指向 vnode.state。带有 oncreate 钩子的 vnode 对应的 DOM 元素不会被回收。

oncreate 钩子对于读取可能触发重绘的布局值、启动动画以及初始化需要引用 DOM 元素的第三方库非常有用。

javascript
var HeightReporter = {
  oncreate: function (vnode) {
    console.log('Initialized with height of: ', vnode.dom.offsetHeight); // 使用高度初始化:
  },
  view: function () {},
};

m(HeightReporter, { data: 'Hello' });

你不应该在此方法中同步修改模型数据。由于 oncreate 在渲染周期结束时运行,因此从这个方法创建的模型更改将不会反映在 UI 中,直到下一个渲染周期。

onupdate ​

onupdate(vnode) 钩子在一个 DOM 元素被更新时被调用,并且仍附加在文档中。onupdate 保证在渲染周期结束时运行,因此从此方法读取布局值(例如 vnode.dom.offsetHeight 和 vnode.dom.getBoundingClientRect())是安全的。

只有当元素在之前的渲染周期中存在时,此钩子才会被调用。当一个元素被创建或被回收时,它不会被调用。

带有 onupdate 钩子的 vnode 对应的 DOM 元素不会被回收。

onupdate 钩子对于读取可能触发重绘的布局值,以及在模型数据更改后动态更新第三方库中影响 UI 的状态非常有用。

javascript
function RedrawReporter() {
  var count = 0;
  return {
    onupdate: function () {
      console.log('Redraws so far: ', ++count); // 到目前为止的重绘次数:
    },
    view: function () {},
  };
}

m(RedrawReporter, { data: 'Hello' });

onbeforeremove ​

onbeforeremove(vnode) 钩子在 DOM 元素从文档中分离之前被调用。如果返回一个 Promise,Mithril.js 仅在 promise 完成后才分离 DOM 元素。

这个钩子仅在失去其 parentNode 的 DOM 元素上被调用,而不会在其子元素中被调用。

与其他钩子一样,onbeforeremove 回调中的 this 关键字指向 vnode.state。拥有 onbeforeremove 钩子的 vnode 对应的 DOM 元素不会被回收。

javascript
var Fader = {
  onbeforeremove: function (vnode) {
    vnode.dom.classList.add('fade-out');
    return new Promise(function (resolve) {
      setTimeout(resolve, 1000);
    });
  },
  view: function () {
    return m('div', 'Bye');
  },
};

onremove ​

onremove(vnode) 钩子在 DOM 元素从文档中移除之后调用。如果也定义了一个 onbeforeremove 钩子,onremove 钩子在 onbeforeremove 返回的 promise 完成后运行。

这个钩子在从文档中移除的任何元素上被调用,无论它是直接从其父元素分离,还是作为另一个被分离的元素的子元素。

与其他钩子一样,onremove 回调中的 this 关键字指向 vnode.state。拥有 onremove 钩子的 vnode 对应的 DOM 元素不会被回收。

onremove 钩子对于运行清理任务非常有用。

javascript
function Timer() {
  var timeout = setTimeout(function () {
    console.log('timed out'); // 超时
  }, 1000);

  return {
    onremove: function () {
      clearTimeout(timeout);
    },
    view: function () {},
  };
}

onbeforeupdate ​

onbeforeupdate(vnode, old) 钩子在一个 vnode 更新前,在进行 diff 之前被调用。如果定义了此函数并返回 false,Mithril.js 将阻止对 vnode 及其子节点进行 diff 操作。

除非子树被封装在一个组件中,否则这个钩子本身不会阻止虚拟 DOM 子树的创建。

与其他钩子一样,onbeforeupdate 回调中的 this 关键字指向 vnode.state。

当 DOM 树过大时,这个钩子对于减少更新延迟非常有用。

避免反模式 ​

虽然 Mithril.js 很灵活,但一些代码模式是不鼓励的:

避免过早优化 ​

你应只在万不得已时使用 onbeforeupdate 跳过 diff。除非你有一个明显的性能问题,否则避免使用它。

通常,可以通过 onbeforeupdate 解决的性能问题可以归结为大型项目数组。在这种情况下,通常“大”意味着任何包含大量节点的数组,无论是在广泛的分布中(典型的5000行表格案例),还是在深度、密集的树中。

如果确实存在性能问题,首先考虑 UI 是否提供良好的用户体验,如不提供,则进行更改。例如,用户很少会浏览5000行原始表格数据,并且用户更有可能使用搜索功能,该功能仅返回最相关的几个项目。

如果基于设计的解决方案不可行,并且你必须优化具有大量 DOM 元素的 UI,请在最大数组的父节点上应用 onbeforeupdate 并重新评估性能。在绝大多数情况下,单个检查应该足够了。在极少数情况下,如果不是,请重复执行,但你应该越来越警惕每个新的 onbeforeupdate 声明。多个 onbeforeupdate 是代码问题,表明设计工作流程的优先级存在问题。

避免“以防万一”而将此优化应用于应用程序的其他区域。请记住,一般来说,更多的代码比更少的代码产生更高的维护成本,并且如果你依赖对象标识进行条件检查,则与 onbeforeupdate 相关的错误可能特别难以排除故障。

再次强调,onbeforeupdate 钩子应该只作为最后的手段使用。

Pager
上一页组件
下一页键(Key)

基于 MIT 许可证 发布。

版权所有 (c) 2024 Mithril Contributors

https://mithril.js.org/lifecycle-methods.html

基于 MIT 许可证 发布。

版权所有 (c) 2024 Mithril Contributors