avalon2学习教程13组件使用
avalon2最引以为豪的东西是,终于有一套强大的类Web Component的组件系统。这个组件系统媲美于React的JSX,并且能更好地控制子组件的传参。
avalon自诞生以来,就一直探索如何优雅的定义组件使用组件。从avalon1.4的ms-widget,到avalon1.5的自定义标签。而现在的版本恰好是它们的结合体,并从web component那里借鉴了slot插入点机制及生命周期管理,从react那里抄来了render字符串模板。
在avalon1.4中,ms-widget指令的值是一个字符串,使用逗号隔开几个有限的元消息
<div ms-widget="widgetType, widgetVmID, widgetOption"></div>
在avalon1.5中,改成自定义标签做载体,使用config对象属性作为widgetOption, 使用$id或identifier属性来指定组件VM的$id, 使用标签名来指定组件的类型。
<ms:button ms-repeat="array" ms-attr-config="x{{$index}}"></ms:button>
此外还有其他夹七夹八的东西,功能更强大了,但上手更难了。
现在细细回想起来,其中重要的配置项就只有两个组件的ID,组件的类型。其他的配置项需要用更优雅的方式加入去。幸好在开始写新组件指令前,我已经解决了。大家可以回去看一下, ms-attr, ms-css. 让指令的属性值以对象或对象数组的形式存在,不就能放许多东西吗。
<xmp ms-widget="@obj"></xmp>
<xmp ms-widget="{is:'panel',$id:'aaa', title:@title}"></xmp>
<xmp ms-widget="[{is:'panel',$id:'aaa', title:@title},@otherConfig,@thirdConfig]"></xmp>
其次是生命周期。avalon2的组件生命周期更完善。
从上表可以看到,avalon2与Web Component的生命周期很相近了。
onInit,这是组件的vm创建完毕就立即调用时,这时它对应的元素节点或虚拟DOM都不存在。只有当这个组件里面不存在子组件或子组件的构造器都加载回来,那么它才开始创建其虚拟DOM。否则原位置上被一个注释节点占着。
onReady,当其虚拟DOM构建完毕,它就生成其真实DOM,并用它插入到DOM树,替换掉那个注释节点。相当于其他框架的attachedCallback, inserted, componentDidMount.
onViewChange,当这个组件或其子孙节点的某些属性值或文本内容发生变化,就会触发它。它是比Web Component的attributeChangedCallback更加给力。
onDispose,当这个组件的元素被移出DOM树,就会执行此回调,它会移除相应的事件,数据与vmodel。
我们再来看一下如何定义组件。上面只是说如何添加配置项。onInit, onReady, onViewChagne, onDispose只是其中的四个配置项。
avalon2 的默认配置项比avalon1.5 少许多。
- is, 字符串, 指定组件的类型。如果你使用了自定义标签,这个还可以省去。
- $id, 字符串, 指定组件vm的$id,这是可选项。
- define, 函数, 自己决定如何创建vm,这是可选项。
- onInit, onReady, onViewChange, onDispose四大生命周期钩子。
然后就没有了, 没有$replace, $slot, $template, $extend, $container, $construct, $$template 这些怪怪的东西。
说起自定义标签。之前1.5为了兼容IE6-8,是使用旧式的带命名空间的标签作为容器,而Web Component则是使用中间带杠的标签,如<ms-button>,风格大相径庭。显然后者是主流,是未来!
经过一番研究,发掘出三大标签作为组件定义时的容器。
xmp, wbr, template
xmp是闭合标签,与div一样,需要写开标签与闭标签。但它里面的内容全部作为文本存在,因此在它里面写带杠的自定义标签完全没问题。并且有一个好处时,它是能减少真实DOM的生成(内部就只有一个文本节点)。
<xmp ms-widget="@config"><ms-button ms-widget="@btn1"><ms-button><div></div><ms-tab ms-widget="@tab"><ms-tab></xmp>
wbr与xmp一样,是一个很古老的标签。它是一个空标签,或者说是半闭合标签,像br, area, hr, map, col都是空标签。我们知道,自定义标签都是闭合标签,后面部分根本不没有携带更多有用的信息,因此对我们来说,没多大用处。
<wbr ms-widget="@config" />
template是HTML5添加的标签,它在IE9-11中不认,但也能正确解析得出来。它与xmp, wbr都有一个共同特点,能节省我们定义组件时页面上的节点规模。xmp只有一个文本节点作为孩子,wbr没有孩子,template也没有孩子,并且用content属性将内容转换为文档碎片藏起来。
<template ms-widget="@config" ><ms-dialog ms-widget="@config"></ms-dialog></template>
当然如果你不打算兼容IE6-8,可以直接上ms-button这样标签。自定义标签比起上面三大容器标签,只是让你少写了is配置项而已,但多写了一个无用的闭标签。
<ms-dialog ms-widget="@config" ><ms-panel ms-widget="@config2"></ms-panel></ms-dialog>
<!--比对下面的写法-->
<xmp ms-widget="@config" ><wbr ms-widget="@config2"/></xmp>
如果你想在页面上使用ms-button组件,只能用于以下四种方式
<!--在自定义标签中,ms-widget不是必须的-->
<ms-button></ms-button>
<!--下面三种方式,ms-widget才是存在,其中的is也是必须的-->
<xmp ms-widget='{is:"ms-button"}'></xmp>
<wbr ms-widget='{is:"ms-button"}'/>
<template ms-widget='{is:"ms-button"}'></template>
在JS中,我们是这样使用它
<!DOCTYPE html>
<html>
<head>
<title>ms-validate</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script src="../dist/avalon.js"></script>
<script>
var vm = avalon.define({
$id: 'test',
button: {//注意这里不能以 $开头
buttonText: "VM内容"
}
})
avalon.component('ms-button', {
template: '<button type="button"><span><slot name="buttonText"></slot></span></button>',
defaults: {
buttonText: "默认内容"
},
soleSlot: 'buttonText'
})
</script>
</head>
<body ms-controller="test">
<!--在自定义标签中,ms-widget不是必须的-->
<ms-button ></ms-button>
<!--下面三种方式,ms-widget才是存在,其中的is也是必须的-->
<xmp ms-widget='{is:"ms-button"}'></xmp>
<wbr ms-widget='{is:"ms-button"}'/>
<template ms-widget='{is:"ms-button"}'></template>
</body>
</html>
但这样我们就不好控制组件的更新。我们改一改。
<!DOCTYPE html>
<html>
<head>
<title>ms-validate</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script src="../dist/avalon.js"></script>
<script>
var vm = avalon.define({
$id: 'test',
button: {//注意这里不能以 $开头
buttonText: "按钮内容"
}
})
avalon.component('ms-button', {
template: '<button type="button"><span><slot name="buttonText"></slot></span></button>',
defaults: {
buttonText: "button"
},
soleSlot: 'buttonText'
})
</script>
</head>
<body ms-controller="test">
<!--在自定义标签中,ms-widget不是必须的-->
<ms-button ms-widget="@button"></ms-button>
<!--下面三种方式,ms-widget才是存在,其中的is也是必须的-->
<xmp ms-widget='[{is:"ms-button"},@button]'></xmp>
<wbr ms-widget='[{is:"ms-button"},@button]'/>
<template ms-widget='[{is:"ms-button"},@button]'></template>
</body>
</html>
这样我们直接操作 vm中的button对象中对应属性就能更新组件了。这比原来avalon1.*好用一万倍。
此外,avalon2还支持Web Components规范中所说的slot插入点机制,它是用来配置
一些字符串长度很长的属性。比如说ms-tabs组件,通常有一个数组属性,
而数组的每个元素都是一个很长的文本,用于以应一个面板。这时我们可以在自定义标签的
innerHTML内,添加一些slot元素,并且指定其name就行了。
当我们不使用slot,又不愿意写面板内部放进vm时,你的页面会是这样的:
<ms-tabs ms-widget='{panels:[
"第一个面板的内部dfsdfsdfsdfdsfdsf",
"第二个面板的内部dfsdfsdfsdfdsfdsf"
"第三个面板的内部dfsdfsdfsdfdsfdsf"] }'
></ms-tabs>
使用了slot后
<ms-tabs>
<div slot='panels'>第一个面板的内部dfsdfsdfsdfdsfdsf</div>
<div slot='panels'>第二个面板的内部dfsdfsdfsdfdsfdsf</div>
<div slot='panels'>第三个面板的内部dfsdfsdfsdfdsfdsf</div>
</ms-tabs>
而你的组件是这样定义
<ms-tabs>
<slot name='panels'></solt>
<slot name='panels'></solt>
<slot name='panels'></solt>
</ms-tabs>
上面的div会依次替代slot元素。
此外,如果我们只有一个插槽,不想在页面上slot属性,那么可以在组件里使用soleSlot。
注意avalon.component的第二个参数,是一个对象,它里面有三个配置项,template是必须的, defaults、 soleSlot是可选的。
组件属性的寻找顺序,会优先找配置对象,然后是innerHTML,然后是defaults中的默认值.我们可以看一下测试
div.innerHTML = heredoc(function () {
/*
<div ms-controller='widget0' >
<xmp ms-widget="{is:'ms-button'}">{{@btn}}</xmp>
<ms-button>这是标签里面的TEXT</ms-button>
<ms-button ms-widget='{buttonText:"这是属性中的TEXT"}'></ms-button>
<ms-button></ms-button>
</div>
*/
})
vm = avalon.define({
$id: 'widget0',
btn: '这是VM中的TEXT'
})
avalon.scan(div)
setTimeout(function () {
var span = div.getElementsByTagName('span')
expect(span[0].innerHTML).to.equal('这是VM中的TEXT')
expect(span[1].innerHTML).to.equal('这是标签里面的TEXT')
expect(span[2].innerHTML).to.equal('这是属性中的TEXT')
expect(span[3].innerHTML).to.equal('button')
vm.btn = '改动'
setTimeout(function () {
expect(span[0].innerHTML).to.equal('改动')
done()
})
})
生命周期回调的例子.
avalon是使用多种策略来监听元素是否移除,确保onDispose回调会触发!
<!DOCTYPE html>
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="./dist/avalon.js"></script>
<script>
avalon.component('ms-button', {
template: '<button type="button"><span><slot name="buttonText"></slot></span></button>',
defaults: {
buttonText: "button"
},
soleSlot: 'buttonText'
})
var vm = avalon.define({
$id: 'widget0',
config: {
buttonText: '按钮',
onInit: function (a) {
console.log("onInit!!")
},
onReady: function (a) {
console.log("onReady!!")
},
onViewChange: function () {
console.log("onViewChange!!")
},
onDispose: function () {
console.log("onDispose!!")
}
}
})
setTimeout(function () {
vm.config.buttonText = 'change'
setTimeout(function () {
document.body.innerHTML = ""
}, 1000)
}, 1000)
</script>
</head>
<body>
<div ms-controller='widget0' >
<div><wbr ms-widget="[{is:'ms-button'},@config]"/></div>
</div>
</body>
</html>
在avalon仓库中有许多简单的例子,大家可以下回来研究研究。
avalon2学习教程13组件使用的更多相关文章
- avalon2学习教程09循环操作
avalon2的循环指令的用法完全改变了.avalon最早期从knockout那样抄来ms-each,ms-with,分别用于数组循环与对象循环.它们都是针对元素内容进行循环.后来又从angular那 ...
- avalon2学习教程06样式操作
avalon2的ms-css的变革思路与ms-attr一样,将多个操作合并成到一个对象里面处理,因此没有ms-css-name="value",只有ms-css="Obj ...
- avalon2学习教程15指令总结
avalon的指令在上一节已经全部介绍完毕,当然有的语焉不详,如ms-js.本节主要总结我对这方面的思考与探索. MVVM的成功很大一语分是来自于其指令,或叫绑定.让操作视图的功能交由形形式式的指令来 ...
- avalon2学习教程14动画使用
avalon2实际上没有实现完整的动画模块,它只是对现有的CSS3动画或jquery animate再包装一层. 我们先说如何用CSS3为avalon实现动画效果.首先要使用avalon.effect ...
- avalon2学习教程12数据验证
avalon2砍掉了不少功能(如ms-include,ms-data),腾出空间加了其他更有用的功能.数据验证就是其中之一.现在avalon2内置的验证指令是参考之前的oniui验证框架与jquery ...
- avalon2学习教程11数据联动
在许多表单应用,我们经常遇到点击一个复选框(或下拉框)会引发旁边的复选框(或下拉框)发生改变,这种联动效果用avalon来做是非常简单的.因为avalon拥有经典MVVM框架的一大利器,双向绑定!绝大 ...
- avalon2学习教程08插入移除操作
本节介绍的ms-if指令与ms-visible很相似,都是让某元素"看不见",不同的是ms-visible是通过CSS实现,ms-if是通过移除插入节点实现. ms-if的用法与1 ...
- avalon2学习教程05属性操作
avalon2与avalon1的属性操作虽然都是使用ms-attr,但用法完全不一样. avalon1是这样操作属性的 其语法为 ms-attr-valueName="vmProp" ...
- avalon2学习教程 03数据填充
数据填充是一个模版最基础的功能,直接从JSON(vm)取出数据,放到适当的位置上.在静态模板中,不区分文本与HTML,只看你的字符串是否有< >来决定生成文本节点与元素节点.但MVVM中, ...
随机推荐
- IOS 取消表格单元格 TableViewCell 去掉高亮状态 点击Cell取消选择状态
以下是两种实现效果 1. 自定义cell 继承UITableViewCell 重写 -(void)setSelected:(BOOL)selected animated:(BOOL)animated ...
- Qt 手动添加ui文件到工程(转)
制作ui文件 先应该用Qt Designer绘制一个自己的界面,并存为myform.ui(这里的myform可以用自己喜欢的名字代替).在制作自己的界面文件时要注意以下几个要点: 1.要记住ui文件的 ...
- Java 报表之JFreeChart(第一讲)
1.利用 JFreeChart 创建垂直柱状报表 package com.wcy.chart.bar; import javax.servlet.http.HttpSession; import or ...
- MSP430学习笔记:UART
串通可以两种方法其现 一.USART硬件直接实现 二.通过定时器软件实现 该模块可现现: UART异步串行通讯 SPI同步串行通讯 I2C同步串行通讯 UxCTL控制寄存器 7:PENA 6:PEV ...
- WPF 中保存 window(窗口)或者canvas成图片
最近需要用到这个功能,搜了一下不少代码有问题 ,找到一个效果比较好的,支持多级子元素 记一下. private void button_save_window_Click(object sender, ...
- SQL ServerOVER 子句,over开窗函数,SQL SERVER 开窗函数
https://technet.microsoft.com/zh-cn/library/ms189461(v=sql.105).aspx http://www.cnblogs.com/85538649 ...
- spring 3 mvc hello world + mavern +jetty
Spring 3 MVC hello world example By mkyong | August 2, 2011 | Updated : June 15, 2015 In this tutori ...
- 【C】 02 - 程序结构和预处理
在正式进入C的语法之前,有必要对其整体外观和组成元素作一个浏览.这部分内容对大多数人是比较陌生的,但它们却是C的起点和骨架.而这些内容涉及的背景或细节又可以展开为专门的课题,这里也只是浅尝则止,说明个 ...
- JSTL中的scope属性
前两天接到老大一个任务:要验证一下<c:import>能否动态地引入不同的文件.当时很兴奋,要知道在对日外包里面,这个也得算技术活了.呵呵! 说干就干,写了两个jsp文件:m.jsp t ...
- phpnow修改默认站点根目录的方法
本文转载自:http://blog.csdn.net/andy_eeipla/article/details/7832082 对于phpnow,经测试,修改Apache-20\conf\extra\h ...