ego008
ego008
2247 0 0

mithril 组件学习记录

基本组件:

var Example = {
    view: function() {
        return m("div", "Hello")
    }
}

m(Example)

组件传递参数:

var Example = {
    view: function (vnode) {
        return m("div", "Hello, " + vnode.attrs.name)
    }
}

m(Example, {name: "Floyd"})

组件事件函数:

定义在组件内部的函数 oninit, oncreate, onupdate, onbeforeremove, onremove and onbeforeupdate.

var ComponentWithHooks = {
    oninit: function(vnode) {
        console.log("initialized")
    },
    oncreate: function(vnode) {
        console.log("DOM created")
    },
    onupdate: function(vnode) {
        console.log("DOM updated")
    },
    onbeforeremove: function(vnode) {
        console.log("exit animation can start")
        return new Promise(function(resolve) {
            // call after animation completes
            resolve()
        })
    },
    onremove: function(vnode) {
        console.log("removing DOM element")
    },
    onbeforeupdate: function(vnode, old) {
        return true
    },
    view: function(vnode) {
        return "hello"
    }
}

也可以指定外部函数作为事件函数:

function initialize() {
    console.log("initialized as vnode")
}

m(ComponentWithHooks, {oninit: initialize})

在组件内部定义变量:

function closureComponent(vnode) {
    // vnode.state is undefined at this point
    var kind = "closure component"

    return {
        view: function() {
            return m("div", "Hello from a " + kind)
        },
        oncreate: function() {
            console.log("We've created a " + kind)
        }
    }
}

利用 State 存储数据:

var ComponentWithInitialState = {
    data: "Initial content",
    view: function(vnode) {
        return m("div", vnode.state.data)
    }
}

m(ComponentWithInitialState)

给 state 赋值

var ComponentWithDynamicState = {
    oninit: function(vnode) {
        vnode.state.data = vnode.attrs.text
    },
    view: function(vnode) {
        return m("div", vnode.state.data)
    }
}

m(ComponentWithDynamicState, {text: "Hello"})

也可以通过关键字this 来访问state

var ComponentUsingThis = {
    oninit: function(vnode) {
        this.data = vnode.attrs.text
    },
    view: function(vnode) {
        return m("div", this.data)
    }
}

m(ComponentUsingThis, {text: "Hello"})

避免过于庞大的组件

// 应该避免下面的大组件
var Login = {
    username: "",
    password: "",
    setUsername: function(value) {
        this.username = value
    },
    setPassword: function(value) {
        this.password = value
    },
    canSubmit: function() {
        return this.username !== "" && this.password !== ""
    },
    login: function() {/*...*/},
    view: function() {
        return m(".login", [
            m("input[type=text]", {oninput: m.withAttr("value", this.setUsername.bind(this)), value: this.username}),
            m("input[type=password]", {oninput: m.withAttr("value", this.setPassword.bind(this)), value: this.password}),
            m("button", {disabled: !this.canSubmit(), onclick: this.login}, "Login"),
        ])
    }
}

定义的组件应该能复用

// models/Auth.js
// PREFER
var Auth = {
    username: "",
    password: "",
    setUsername: function(value) {
        Auth.username = value
    },
    setPassword: function(value) {
        Auth.password = value
    },
    canSubmit: function() {
        return Auth.username !== "" && Auth.password !== ""
    },
    login: function() {/*...*/},
}

module.exports = Auth

// views/Login.js
// PREFER
var Auth = require("../models/Auth")

var Login = {
    view: function() {
        return m(".login", [
            m("input[type=text]", {oninput: m.withAttr("value", Auth.setUsername), value: Auth.username}),
            m("input[type=password]", {oninput: m.withAttr("value", Auth.setPassword), value: Auth.password}),
            m("button", {disabled: !Auth.canSubmit(), onclick: Auth.login}, "Login"),
        ])
    }
}

避免把属性写死

// button 的onclick 属性写死了
var RestrictiveComponent = {
    view: function(vnode) {
        return m("button", {onclick: vnode.attrs.onclick}, [
            "Click to " + vnode.attrs.text
        ])
    }
}

// 应该传入属性
var FlexibleComponent = {
    view: function(vnode) {
        return m("button", vnode.attrs, [
            "Click to ", vnode.children
        ])
    }
}

避免操作children

// 避免
var Header = {
    view: function(vnode) {
        return m(".section", [
            m(".header", vnode.children[0]),
            m(".tagline", vnode.children[1]),
        ])
    }
}

m(Header, [
    m("h1", "My title"),
    m("h2", "Lorem ipsum"),
])

// awkward consumption use case
m(Header, [
    [
        m("h1", "My title"),
        m("small", "A small note"),
    ],
    m("h2", "Lorem ipsum"),
])

// 应该
var BetterHeader = {
    view: function(vnode) {
        return m(".section", [
            m(".header", vnode.attrs.title),
            m(".tagline", vnode.attrs.tagline),
        ])
    }
}

m(BetterHeader, {
    title: m("h1", "My title"),
    tagline: m("h2", "Lorem ipsum"),
})

// clearer consumption use case
m(BetterHeader, {
    title: [
        m("h1", "My title"),
        m("small", "A small note"),
    ],
    tagline: m("h2", "Lorem ipsum"),
})

静态定义组件,动态调用它们

// 避免
var ComponentFactory = function(greeting) {
    // creates a new component on every call
    return {
        view: function() {
            return m("div", greeting)
        }
    }
}
m.render(document.body, m(ComponentFactory("hello")))
// calling a second time recreates div from scratch rather than doing nothing
m.render(document.body, m(ComponentFactory("hello")))

// 应该
var Component = {
    view: function(vnode) {
        return m("div", vnode.attrs.greeting)
    }
}
m.render(document.body, m(Component, {greeting: "hello"}))
// calling a second time does not modify DOM
m.render(document.body, m(Component, {greeting: "hello"}))

避免在view 之外创建组件实例

// AVOID
var Counter = {
    count: 0,
    view: function(vnode) {
        return m("div",
            m("p", "Count: " + vnode.state.count ),

            m("button", {
                onclick: function() {
                    vnode.state.count++
                }
            }, "Increase count")
        )
    }
}

var counter = m(Counter)

m.mount(document.body, {
    view: function(vnode) {
        return [
            m("h1", "My app"),
            counter
        ]
    }
})
// PREFER
var Counter = {
    count: 0,
    view: function(vnode) {
        return m("div",
            m("p", "Count: " + vnode.state.count ),

            m("button", {
                onclick: function() {
                    vnode.state.count++
                }
            }, "Increase count")
        )
    }
}

m.mount(document.body, {
    view: function(vnode) {
        return [
            m("h1", "My app"),
            m(Counter)
        ]
    }
})
0

See Also

Nearby


Discussion

Login Topics