Mithril 自动重绘系统

自动重绘系统

介绍

为了实现快速渲染,Mithril 实现了一个虚拟 DOM diff 系统,此外,还提供了各种机制来实现对渲染粒度的控制。

Mithril 的自动重绘系统,会在数据层的数据改变后,同步更改 DOM。当你调用 m.mount 后将开启自动重绘系统。当你使用 m.route 时,在调用了 m.render 后也将开启自动重绘系统。

自动重绘功能其实只是在完成某些功能后执行的重新渲染。

事件处理函数之后

Mithril 会在视图中的 DOM 事件处理函数执行完后,进行自动重绘:

var MyComponent = {
    view: function() {
        return m("div", {onclick: doSomething})
    }
}

function doSomething() {
    // 函数执行完后,同步进行重绘
}

m.mount(document.body, MyComponent)

你可以通过在事件处理函数中设置 e.redrawfalse 来禁用该事件的自动重绘:

var MyComponent = {
    view: function() {
        return m("div", {onclick: doSomething})
    }
}

function doSomething(e) {
    e.redraw = false
    // 触发该事件处理函数后,不再执行自动重绘
}

m.mount(document.body, MyComponent)

m.request 之后

Mithril 会在 m.request 完成后执行自动重绘:

m.request("/api/v1/users").then(function() {
    // 函数执行完后会进行自动重绘
})

你可以把 background 设置为 true,来禁用指定请求的自动重绘功能:

m.request("/api/v1/users", {background: true}).then(function() {
    // 不再触发自动重绘
})

路由改变之后

Mithril 会在调用 m.route.set() 后,或者点击使用了 m.route.link 的链接后,执行自动重绘:

var RoutedComponent = {
    view: function() {
        return [
            // a redraw happens asynchronously after the route changes
            m("a", {href: "/", oncreate: m.route.link}),
            m("div", {
                onclick: function() {
                    m.route.set("/")
                }
            }),
        ]
    }
}

m.route(document.body, "/", {
    "/": RoutedComponent,
})

何时不进行重绘

Mithril 在 setTimeoutsetIntervalrequestAnimationFramePromise 以及第三方库的事件处理函数(例如 Socket.io 回调)的回调中不会执行自动重绘。在这种情况下,你必须手动调用 m.redraw()

在生命周期方法执行完后,Mithril 也不会自动重绘。在 oninit 之后部分 UI 可能会重绘,但另一部分 UI 可能在触发 oninit 时已经重绘完成。oncreateonupdate 触发时 UI 已经重绘完成。

如果你希望在生命周期方法中触发重绘,可以调用 m.redraw(),这将触发异步重绘:

var StableComponent = {
    oncreate: function(vnode) {
        vnode.state.height = vnode.dom.offsetHeight
        m.redraw()
    },
    view: function() {
        return m("div", "This component is " + vnode.state.height + "px tall")
    }
}

Mithirl 不会对通过 m.render 渲染的 vnode 进行自动重绘。这意味着,通过 m.render 渲染的组件在触发事件和调用 m.request 后不会触发重绘。因此,如果你希望手动控制重绘发生的时机,则应该使用 m.render 而不是 m.mount

注意,m.render 传入的参数是一个 vnode,而 m.mount 传入的参数是一个组件:

// 使用 m.render 时需要把组件包裹在 m() 函数中
m.render(document.body, m(MyComponent))

// 使用 m.mount 时,直接传入组件即可
m.mount(document.body, MyComponent)

如果重绘的频率高于动画帧(通常为16ms),则 Mithril 会适当的避免自动重绘。这意味着,当使用 onresizeonscroll 这类事件时,Mithril 会自动调节重绘频率。