m
紧接之前的 超好用的 Mithril.js 教程之核心概览,我们知道 m 函数是构建、渲染、数据绑定一体化的函数,他负责生成虚拟 DOM(后面使用 vnode 代替),并且绑定 data,承接 vnode 之间的数据传递关系,如:
m('.parent', m('.child'))
那么上面的 .child 就是 .parent 的子 vnode,而反过来 .parent 就是 .child 的父 vnode。
标准形式 m(selector, [attrs, children]) 后两者可选,可选表示请看:函数文档表示规范
一定要注意最后的 redraw,这个才是开发中最常见的坑。
本文核心
推荐写法
个人推荐
| 参数 | 类型 | 必选 | 描述 | 
|---|---|---|---|
| selector | String or Object | Yes | CSS 选择器或 m 对象(如上) | 
| attributes | Object | No | HTML 标签属性或自定义属性(无需 data-) | 
| children | Array or String or Number or Boolean | No | 数组,其他不推荐 | 
| returns | Vnode | 虚拟 DOM | 
按照推荐写法,上面例子如下:
m('.parent', [m('.child')])
原因:
- 数组易拓展。
- 编辑器排版,这个你可以使用编辑器格式化试试看。
所以这里推荐,所有含有子 vnode 的,全部使用数组,哪怕目前是只有一个。
单实例
m("div.class1#id1", { title: "title" }, "im div")
组件化
如果将上例的 .child 组件化,就是如下:
const Child = {
	view() {
		return m('.child')
	}
}
m('.parent', [m(Child)])
传参
上例传参如下:
const Child = {
	view(vnode) {
		console.log(vnode.attrs.param1) // im param1
		v.node.attrs.func1() // run func1
		return m('.child')
	}
}
m('.parent', [m(Child), { param1: 'im param1', func1: () => console.log('run func1') }])
建议单向数据流,子 vnode 修改父级数据什么的,通过回调函数处理,如上的 func1.
获取参数通过生命周期函数或 view 函数获取,共以下函数可获取:
| 函数 | 参数 | 函数描述 | 
|---|---|---|
| oninit | vnode | 初始化 | 
| oncreate | vnode | 创建真实 DOM 成功 | 
| onbeforeupdate | vnode, old | 组件 UI 内容刷新前 | 
| onupdate | vnode | 组件 UI 内容刷新后 | 
| onbeforeremove | vnode | 组件销毁前 | 
| onremove | vnode | 组件销毁 | 
| view | vnode | 组件 vnode 结构函数 | 
redraw
这是问题关键!
m.mount 或 m.route 渲染的结构树一般来说会根据数据更新来重新渲染,这个没问题。
但是当你使用 setTimeout / setInterval / requestAnimationFrame 以及第三方框架如 hammer.js 时候,便脱离了 Mithril 的数据监听流,此时修改绑定的数据便不会自动 diff 树且重新渲染了。
如果出现不自动渲染的情况,第一时间检查所处修改数据的上下文是否在上述情况中,具体表格如下:
| 上下文 | 是否自动渲染 | 
|---|---|
| 常规情况 | yes | 
| m.request | yes | 
| m.jsonp | yes | 
| m.route | yes,使用 m.route.set(),或 a 标签申明 m.route.link | 
| m.route | no,除上述情况外的特殊情况,不常见 | 
| oninit | no | 
| oncreate | no | 
| onupdate | no | 
| setTimeout | no | 
| setInterval | no | 
| requestAnimationFrame | no | 
| Promise | no | 
| 第三方组件库 | no | 
当页面出现上述情况,不自动渲染的情况下,使用 m.redraw() 即可强制重新渲染。
这里不用担心性能,因为本身自动渲染也是调用这个函数,这里只是给了一个迂回策略。
简单来说,绝大部分情况都是自动渲染的,如果开发出现不自动渲染,除了去找意外,最简单就是直接加上:m.redraw()
小结
- 推荐写法,子组件使用数组包起来
- 单实例
- 组件化,子组件使用 m()包起来
- 传参,获取 vnode 方式
- redraw,不自动重新渲染的时候手动调用
欢迎关注,如有需要 Web,App,小程序,请留言联系。