- thin.js 使用文档
- 1. 开始使用
- 2. render 渲染器和模板
- 2.1 基本语法
- 2.2 模板 template
- 2.2.1 string template 字符串模板
- 2.2.2 element template
- 2.2.3 模板:函数
- 2.2.4 模板:if 结构
- 2.2.5 模板:switch case 结构
- 2.2.6 when 结构
- 2.2.7 组合模板
- 3. poplayer 弹出层
- 4. tab 标签+multiview 多视图切换
- 5. 简化写法以及补充写法
thin.js:一个羽量级的前端框架,基于 html5+css3+jquery。
本文档介绍 thin.js 的使用语法
- 添加对 jquery 的引用,thinjs.com 上的版本为 3.4.1,你也可以从其他源引用 jquery。
<scriptsrc="https://com.wf.pub/jquery.js"></script>- 添加对 thin.js 的引用。
<scriptsrc="https://com.wf.pub/thin.js"></script>- 如果你用到 render 以外的其他功能,还需要添加对 thin.css 的引用。(可选)
<linkrel="stylesheet" href="https://com.wf.pub/thin.css" />渲染器是一个 jQuery 扩展,语法如下:
$(selector).render({data: data,template: template});按照 template 依据 data 渲染到 selector 指定的元素中。
data:数据,可以是object或者array类型。
template: 模板
thin.js 目前可以使用以下六种模板:
- string template
- element template
- 模板函数
- if 结构
- switch case 结构
- when 结构
- 前述六种模板组成的数组
vardata={name: 'wanfang'};$(selector).render({data: data,template: 'this is [[name]]'});element 模板本质上是一个包含"e"属性成员的 javascript 对象,可以根据 e 指定的 element name 生成 html 元素,并设置属性、样式、绑定数据、事件。
vardata={name: 'wanfang'};$(selector).render({data: data,template: {e: 'p',t: 'this is [[name]]'}});对象可以有以下成员
| 成员 | 类型 | 说明 |
|---|---|---|
| e | string | element 元素标签名 |
| t | string,object,array,function | template 子模板 |
| id | string | 标签 id |
| class | string | 标签类名 |
| name | string | 标签的 name 属性 |
| style | object | 行内样式表 |
| width/height | string | 元素宽/高(与写在 style 无区别) |
| a | object | attribute, html 属性(name、src、href......) |
| click | function | 点击事件处理函数 |
| event | object | 事件处理函数 |
| data | object | 数据 |
| datapath | string | 数据路径 |
| foreach | string | 数据循环 |
| bind | 双向数据绑定 | |
| selected | string | 给select下拉选择框设置默认值 |
| value | string | 输入项设置值 |
| options | string,array,object | 选项值 |
| timer | object | 元素绑定定时/延时任务 |
| if | string,function | 控制渲染 |
| switch case | string | 控制渲染 |
| when | string,function | 控制渲染 |
| title | string | 生成标题标签,当 e=fieldset 时,生成 legend,当 e=field/f1/f2/f3 时在元素内生成 label 标签,e 为其他值是无效 |
在 element 模板中可以用 a 属性来控制渲染出的 dom element 的属性。
{e:"div",a:{id:"sampleid",class:"sampleclass"}}渲染结果:
<divid="sampleid" class="sampleclass"></div>a 的成员的属性名和值将用作渲染出来的节点的属性名和值。 当需要生成的属性名包含特殊字符时,可以使用引号将属性名包括在内。
a:{"data-sample":"sample data"}使用数据漫游器 a 的成员的值串中可以使用数据漫游器'[[]]'将数据填充到值串中。
a:{"data-sample": "[[sample]]"}a 的成员也可是函数,以根据数据生成不同的属性。
函数需要返回字符串作为生成的属性值。
a:{class: function(param){if(param.data.field==="a"){return"classa";}else{return"classb";}}}传入参数 param 的结构如下:
{container: container,//当前容器data: data// 当前容器所绑定的数据}id,name,class 因为经常被使用到,所以可以直接在 element 中用相应的属性设置,但是取值不允许使用函数或者数据漫游器,如果需要根据数据决定取值,请使用 a 属性的成员进行设置。
在 thin.js 中可以用以下三种方式将数据源绑定到模板。
- render 参数的 data 成员
- element 模板的 datapath/foreach 属性
- element 模板的 data 属性
给模板提供数据最常用的方式是作为 render 的参数的 data 成员传入。
$(selector).render({data: data,template: template});datapath:
// 对象数组的渲染vardata={arr: [{name: 'name1'},{name: 'name2'},{name: 'name3'},{name: 'name4'}]};// datapath 数据绑定// 渲染的为dapath所在的DOM分支$(selector).render({data: data,template: {e: 'div',// foreach: 'arr',t: {e: 'p',datapath: 'arr',t: 'this is [[name]]'}}});foreach:
// foreach 数据绑定// 渲染的为foreach所在的DOM分支的子元素$(selector).render({data: data,template: {e: 'div',foreach: 'arr',t: {e: 'p',// datapath: 'arr',t: 'this is [[name]]'}}});- 当 datapath/foreach 指向的数据是对象时模板只渲染一次。
- 当 datapath/foreach 指向的数据是数组时模板会对每一行数据渲染一次。
- 当 datapath/foreach 指向的数据为 null 时模板不渲染。
两者的区别:datapath 需绑定在需要数据的元素上;foreach 需要绑定在需要数据元素的父元素上。
// 绑定的数据类型为对象时$(selector).render({data: data,template: {e: 'div',// foreach: 'newData',t: {e: 'p',data: {sex: 'woman',address: '北京市'},t: '[[sex]][[address]]'}}});- 在字符串模板中使用数据漫游器展示数据: 所有末级模板都一定是字符串模板,我们可以在字符串模板中用双方括号调用数据漫游器从绑定的数据源中取值。
vardata={location: {city: '天津'}};$(selector).render({data: data,template: {e: 'div',// 当前绑定的数据为datat: '<p>[[location/city]]</p>'// 根据数据路径,当前绑定的数据为data.location.city = data/location/city}});除子模版外,element 模板的 a,style,value,selected 的取值也可以用字符串模板调用数据漫游器生成。
在函数模板中使用数据:在函数模板和其他允许使用函数的属性中,thin.js 都会将数据和当前 dom 元素作为参数的成员传递给函数。
event : 添加事件侦听器
event传入的值为对象,格式为 事件名:处理函数
2021-03-08更新:event支持传入数组对象的形式。[{事件名1: 处理函数1}, {事件名2: 处理函数2}]
vardata={name: 'wanfang',age: '20'};$(selector).render({data: data,template: {e: 'form',t: [{e: 'label',t: ['姓名:',{e: 'input',a: {name: 'name',type: 'text',value: '[[name]]'}}]},{e: 'label',t: ['年龄:',{e: 'input',a: {name: 'age',type: 'text',value: '[[age]]'}}]},{e: 'button',a: {type: 'button'},t: '提交',click: function(param){//因为click事件经常使用,所以不用放在event对象中console.log(param);// {// event: k.Event {// originalEvent: MouseEvent,// type: "click",// target: button,// currentTarget: button,// isDefaultPrevented: ƒ,// …// } 事件// new_data: {//可认为当前表单要提交的数据// name: "wanfang",// age: "50"// }// org_data: {//初始时对表单传入的数据// name: "wanfang",// age: "20"// }// sender: button 事件源// type: "click" 事件类型// }},event: {dbclick: function(param){console.log(param);},mouseover: function(param){console.log(param);}},// event: [// {// dbclick: function (param) {// console.log(param);// },// mouseover: function (param) {// console.log(param);// }// },// {// mouseout: function (param) {// console.log(param);// }// }// ]}]}});传入的参数结构
| 名称 | 描述 |
|---|---|
| sender | 事件源 |
| event | 事件 |
| type | 事件类型 |
| org_data | 原始数据 |
| new_data | 新数据 |
Q:事件event中function传入的参数结构和函数模板(例:if:function(param){}、when:function(param){}...)中function的参数结构有何区别?
A:事件中传入参数结构为:
{sender:{事件源},event:{事件},type:{事件类型},org_data:{原始数据},new_data:{新数据}}
函数模板中传入参数结构为:
{container: 当前绑定的容器,data: 当前容器绑定的数据}
函数模板具体使用情况请参考2.3 模板:函数
Q:为什么有的时候new_data里面没有值,是个空对象?
A:可能的情况:
⑴. 因为最外层template是一个数组,数据绑定时不确定绑定在哪一个父容器上。 例如:
template: [{e: 'field',t: [{label: '资源分组'},{e: 'input',a: {name: 'scope'}}]},{e: 'field',t: [{label: '参考价格'},{e: 'input',a: {name: 'price'}}]},{e: 'div',t: [{e: 'button',t: '确定',click: function(param){// 此时打印的param中的new_data就为空对象。console.log(param);}}]}]
⑵. 表单里的input、textarea、select等表单元素没有name属性。
style : 设置行间样式
{e:"div",style:{width:"300px",height:"100px"}}渲染结果:
<divstyle="width:300px;height:100px"></div>style 的成员的属性名和值将用作渲染出来的节点的样式的名和值。 当需要生成的属性名包含特殊字符时,可以使用引号将属性名包括在内。
style:{"font-size":"14px"}width / height : 设置宽度和高度
{e:"div",width:300,height:100}生成结果:
<divstyle="width:300px;height:100px"></div>width,height 为快捷样式,不用写在 style 里面。
style 的成员也可是函数,以根据数据生成不同的属性。
style:{"font-size":function(param){if(param.data.field===true){return"14px";}else{return"12px";}}}注:函数需要返回字符串作为生成的样式的值。
timer 提供 thin 定时/延时功能
定时 setInterval:
varindex=1;$(selector).render({template: {e: 'p',t: {timer: {interval: 1000,do: function(param){console.log(index);index++;if(index>4){$(param.container).remove();}}}}}});当定时器绑定的容器被移除时,定时器也会被移除。
延时 setTimeout:
$(selector).render({template: {e: 'p',t: {timer: {delay: 1000,do: function(param){console.log('我延时了1s');}}}}});options接收三种参数值object、array、string;
- object
$(selector).render({template: {e: 'form',t: {e: "label",t: ["性别:",{e: 'select',a: {name: "sex",},options: {男: 'man',女: 'woman'}}]}}});object的key为option标签的text;object的value为option标签的value值。
渲染结果:
<form><label>性别:
<selectname="sex"><optionvalue='man'>男</option><optionvalue='woman'>女</option></select></label></form>- array
$(selector).render({template: {e: 'form',t: {e: "label",t: ["性别:",{e: 'select',a: {name: "sex",},options: ['男','女']}]}}});该方法只渲染option标签的text属性(不建议使用)。
渲染结果:
<form><label>性别:
<selectname="sex"><option>男</option><option>女</option></select></label></form>- string
$(selector).render({template: {e: 'form',t: {e: "label",t: ["性别:",{e: 'select',a: {name: "sex",},options: '男,女'}]}}});该方法只渲染option标签的text属性(不建议使用)。
渲染结果:
<form><label>性别:
<selectname="sex"><option>男</option><option>女</option></select></label></form>value和selected可以给input和select标签默认值。
vardata={name: 'wanfang',age: '20',sex: 'woman'};$(selector).render({data: data,template: {e: 'form',t: [{e: "label",t: ["姓名:",{e: 'input',a: {name: "name",type: 'text'},value: '[[name]]'}]},{e: "label",t: ["年龄:",{e: 'input',a: {name: "age",type: 'text'},value: '[[age]]'}]},{e: "label",t: ["性别:",{e: 'select',a: {name: "sex",},options: {男: 'man',女: 'woman'},// 对于select标签来说selected和value都可以// selected: '[[sex]]',value: '[[sex]]'}]}]}});正常情况下事件中打印的org_data永远是传入的绑定数据data。 如果在element模板中使用了bind属性指定了数据路径,则在元素渲染时可以将相应的数据作为dom元素的值渲染出来,同时当dom元素的值发生变化时,thin.js也会用onchange事件捕获,并根据数据路径反向将数据自动更新到数据源中。
vardata={name: 'wanfang',age: '20'};$(selector).render({data: data,template: {e: 'form',t: [{e: "label",t: ["姓名:",{e: 'input',a: {name: "name",type: 'text'},bind: 'name'}]},{e: "label",t: ["年龄:",{e: 'input',a: {name: "age",type: 'text'},bind: 'age'}]},{e: 'button',a: {type: "button"},t: "提交",click: function(param){//因为click事件经常使用,所以不用放在event对象中console.log(param);// 如果改变了输入框的值,此时打印org_data为输入框最新的值。说明bind产生效果,改变了原有的data_of_thin}}]}});模板可以是一个函数,渲染时会将当前位置的 dom 元素和绑定的数据传递给函数,用户可以编写自己的 javascript 代码进行渲染或其他操作。
{t: function(param){// some code here.console.log(param)}}param对象:
| 成员 | 描述 |
|---|---|
| container | 当前dom容器 |
| data | 容器绑定的数据 |
例子:
vardata={name: 'test1'};$(selector).render({data: data,template: {e: 'div',t: {e: 'div',class: 'test_div',t: function(param){console.log(param);// {// container:当前dom容器// data:当前容器绑定的数据// }$(param.container).text(param.data.name);}}}});Q:函数模板中参数param为什么没有org_data、new_data等属性?
A:org_data、new_data等属性只有在事件event中才会被function(param){}中的param形参传入,其他函数模板结构均为data,container。
用于根据条件真假决定使用哪个模板。
{if: 布尔表达式|判断函数,then: 真模板,else: 假模板}例子:
vardata={name: 'test1'};$(selector).render({data: data,template: {e: 'div',t: {if: function(param){returnparam.data.name==='test1';},// if: true,then: {e: 'p',t: '真模板'},else: {e: 'p',t: '假模板'}}}});param对象:
| 成员 | 描述 |
|---|---|
| container | 当前dom容器 |
| data | 容器绑定的数据 |
switch case 结构
{switch:"[[参数]]",case:{"条件1":"template1","条件2":"template2",default:"default template"}}例子:
vardata={name: 'test1'};$(selector).render({data: data,template: {e: 'div',t: {switch: '[[name]]',case: {test1: 'template1',test2: 'template2',default: 'default template'}}}});when写法可以根据传入的值控制容器是否渲染。也可根据传入的函数进行判断,然会相应是否渲染的值。
// when的使用varobj={// boolen: ''// boolen: '值'boolen: true}// when$(selector).render({data: obj,template: {e: 'p',t: {e: 'span',t: 'test',// 如果该数据路径值存在且不为空,渲染当前容器;反之不渲染。// 也可填入布尔值:true渲染;false不渲染when: 'boolen'// 也可根据数据进行判断,返回相应渲染结果// when: function (r) {// if (r.data.boolen) {// return true;// } else {// return false;// }// }}}});由前述五种模板组成的数组
vardata={name: 'test1'};$(selector).render({data: data,template: {e: 'div',class: 'test_div',t: [// 字符串模板'string template [[name]]',// element模板{e: 'p',t: '[[name]]'},// if模板{if: function(param){returnparam.data.name==='test1';},then: '真模板',else: '假模板'},// switch case模板{switch: '[[name]]',case: {test1: 'template1',test2: 'template2',default: 'default template'}},// 函数模板function(param){$(param.container).append(param.data.name);}]}});<divclass="test_div">
string template test1
<p>test1</p>
真模板 template1 test1
</div>poplayer弹出层可以在界面上弹出一个浮层,浮层的高度、宽度都可以自定义。
poplayer({header: headertext,data: data,template: template});template 语法与 render 完全一样。
| 成员 | 类型 | 描述 |
|---|---|---|
| header | string | 弹出框头部展示文字 |
| height | string | 高度 |
| width | string | 宽度 |
| data | 数据 | |
| template | 模板 | |
| render | function | 自定义渲染器 |
| onclose | function | 浮层关闭时的回调函数(仅用于浮层右上角按钮关闭) |
基本写法:
poplayer({header: headertext,render: function(param){renderFun(param);}});functionrenderFun(param){// to render// param结构:// {// container:{popbodycontainer}// }}简单写法:
poplayer({header: headertext,render: renderFun});functionrenderFun(param){// to renderconsole.log(param);// param结构:// {// container:{popbodycontainer}// }}注:poplayer中render函数不属于函数模板。该函数param传入参数只有popbody容器。
vardata={name: 'test1',arr: [{name: 'test1',age: 18},{name: 'test2',age: 18},{name: 'test3',age: 18},{name: 'test4',age: 18}]};poplayer({data: data,header: '弹出框',onclose: function(){console.log('我关闭了');},template: {e: 'div',t: [{e: 'ul',t: {e: 'li',datapath: 'arr',t: '[[name]][[age]]'}}]}});注:onclose 只为 poplayer 弹出层右上角关闭按钮的回调函数。其他 jq 操作移出弹出层都不会触发 onclose 事件
poplayer 函数会将当前弹出窗口的 jq 对象 renturn 出来。当我们进行操作结束之后想关闭弹窗时可以进行 jq 删除操作。(该操作不会触发 onclose 事件)
vartestPop=poplayer({data: data,header: '弹出框',onclose: function(){console.log('我关闭了');},template: {e: 'div',t: [{e: 'ul',t: {e: 'li',datapath: 'arr',t: '[[name]][[age]]'}},{e: 'div',t: {e: 'button',t: '关闭',click: function(){testPop.remove();}}}]}});tab标签可以通过固定语法渲染出一组tab导航栏;点击tab导航栏切换对应的内容页面。
静态资源的tab导航栏,在项目中可以展示一些静态资源。通过tab-nav标签的view属性和view标签的id进行绑定。用户只需要进行渲染标签,省去了js操作。(动态资源不建议使用)
$(selector).render({data: '',template: [{multiview: [{e: 'tab',t: [{e: 'tab-nav',t: 'nav1',a: {class: 'active',view: 'nav1'}},{e: 'tab-nav',t: 'nav2',a: {view: 'nav2'}}// more tab-nav]},{e: 'view',t: '1',id: 'nav1',class: 'active'},{e: 'view',t: '2',id: 'nav2'}// more view]}]});渲染结构:
<multiview><tab><tab-navclass="active" view="nav1">nav1</tab-nav><tab-navview="nav2">nav2</tab-nav></tab><viewid="nav1" class="active">1</view><viewid="nav2">2</view></multiview>通过点击事件和渲染模板进行的动态资源渲染,可以添加hash路由使界面刷新之后渲染hash路由对应的界面;也可以添加default使每次重新加载默认选中default项。
点击时进行渲染,可用于异步渲染。
| 成员 | 类型 | 描述 |
|---|---|---|
| default | number | 默认渲染第几个导航 |
| hashpath | string | 给tab-nav添加哈希路由 |
| click | function | 点击事件,在点击事件里面动态渲染内容 |
$(selector).render({data: '',template: [{tab: {nav: {nav1: {// 自动向浏览器添加哈希路由(可选)// 刷新页面自动调用当前哈希路由的click事件hashpath: '#nav1',click: function(){// 渲染(可异步)$('#show_container').text(1);}},nav2: {hashpath: '#nav2',click: function(){$('#show_container').text(2);}},nav3: {hashpath: '#nav3',click: function(){$('#show_container').text(3);}},nav4: {hashpath: '#nav4',click: function(){$('#show_container').text(4);}}},// 默认渲染第几个default: 2}},{e: 'div',id: 'show_container'}]});渲染结构:
<div><tab><tab-navhashpath="#nav1">nav1</tab-nav><tab-navhashpath="#nav2" class="active">nav2</tab-nav><tab-navhashpath="#nav3">nav3</tab-nav><tab-navhashpath="#nav4">nav4</tab-nav></tab><divid="show_container">2</div></div>Q:动态渲染中的tab: {}的写法和第五章的简化写法一样么?
A:不一样。动态渲染中的tab: {}为固定语法。
简化之前基本语法:
$(selector).render({data: data,template: {e: 'p',t: 'this is [[name]]'}});template:{e:'标签名',t:'html'}简化之后的基本写法:
$(seletor).render({data: data,template: {p: 'this is [[name]]'}});template:{'标签名':'html'}closeon 通过事件移除标签
closeon:事件名称(多个事件用,隔开)
在哪里写的 closeon,该事件就绑定在哪里
after 将 DOM 树添加到指定选择器之后
- after: 'selector'
before 将 DOM 树添加到指定选择器之前
- before: 'selector'
in 将 DOM 树添加到指定选择器之内
- in: 'selector'
vartestDiv={e: 'ul',closeon: 'click',//该点击事件绑定在当前的ul上in: '.test_div',// before: '.test_div',// after: '.test_div',t: [{e: 'li',t: '1'},{e: 'li',t: '2'},{e: 'li',t: '3'},{e: 'li',t: '4'}]};$(selector).render({template: {e: 'div',t: ['<div class="test_div">我是容器</div>',{e: 'button',t: 'test',click: testDiv//点击渲染一组DOM树在指定位置}]}});虚拟DOM树中第一层DOM必须使用基础写法({e: '标签名',t: html}),简化写法目前不被支持({'标签名':html})
渲染结果
before:
<divclass="test_container"><div><ul><li>1</li><li>2</li><li>3</li><li>4</li></ul><divclass="test_div">我是容器</div>
测试
</div></div>after:
<divclass="test_container"><div><divclass="test_div">我是容器</div><ul><li>1</li><li>2</li><li>3</li><li>4</li></ul>
测试
</div></div>in:
<divclass="test_container"><div><divclass="test_div">
我是容器
<ul><li>1</li><li>2</li><li>3</li><li>4</li></ul></div>
测试
</div></div>click 接收的值为一个虚拟 DOM 树的话,会将当前的 DOM 树 append(默认为 append)到触发 click 事件的当前 DOM 里
对于纯数组而言,我们想遍历数组中的每一项,可以在数据访问器里填写符号 "."。
// 纯数组的渲染vararr=['name1','name2','name3','name4','name5','name6'];$('.test_container').render({data: arr,template: {e: 'p',t: '[[.]]'}});渲染结果:
<divclass="test_container"><p>name1</p><p>name2</p><p>name3</p><p>name4</p><p>name5</p><p>name6</p></div>