Skip to content

Latest commit

 

History

History
1492 lines (773 loc) · 26.4 KB

api.md

File metadata and controls

1492 lines (773 loc) · 26.4 KB

API Reference

__ 命名约定 __

  • Component 此接口同时属于Regular及其子类.
  • Regular 此接口只属于Regular本身
  • component: 代表Regular及其子类的实例

静态接口

Component.extend( options ) {#extend}

创建一个继承自Component的组件,参数 options 中的所有属性都会成为 原型属性.

Usage:

const Component = Regular.extend({
  template: `
  <div>
    <h2 >{count}</h2>
    <button on-click={this.tick( 'a' )} >点击加a</button>
  </div>
  `,
  tick: function(posfix) {
    this.data.count += posfix;
  }
})
<script async src="//jsfiddle.net/leeluolee/sega921w/11/embed/js,result/"></script>

Arguments

Param Type Detail
options Object 组件定义和配置,见 options

Return

Component

注意 extend 是原型继承的一个语法糖,options是实例共享的

Component.implement(options) {#implement}

扩展Component的__原型属性与方法__。 optionsComponent.extend一致. 

Arguments

Param Type Detail
options Object 组件定义和配置,见 options

Return

Component

小技巧: 通过 implementextend 扩展的方法,都可以通过this.supr(arg1, arg2..)调用父类同名函数

"Regular的类式继承体系来源于著名的ded/klass."

Example

Component.extend({
  init: function(){
    this.supr() // call the super init
  }
}).implement({
  hello: function( msg ){
    this.supr( msg ) // call the super hello
  }
})

new Component(options)

Example

var component = new Component({
  // ...other options 
  data: {
    username: "leeluolee"
  }
})

component.$inject('#container');

Arguments

Param Type Detail
options Object 组件定义和配置,见 options

Return

Component的实例: 查看实例接口

通过实例化传入的 options 将成为__实例属性__, 意味它将覆盖 extend 与 implement 的定义.并且方法中无法使用 this.supr()

Component.directive(name, definition) {#directive}

定义指令

Arguments

param type detail
name String 指令名
definition.link Function[required] 链接函数,指令编译时会被调用
definition.update Function[optional] 更新函数,指令绑定的表达式变更时会被调用
definition.params Array[optional] 指令参数,指定指令参数列表

如果 definition 是一个 Function,则视为成为link函数

definition.link(elem, value)

  • elem 绑定的元素节点
  • value 属性值(可能是字符串或是一个[Expression];
  • this 这里的this指向component组件本身

definition.update(elem, value)

  • elem 绑定的元素节点
  • value 属性值(可能是字符串或是一个[Expression];
  • this 这里的this指向component组件本身

definition.params

Example (source code of builtin r-html )

Regular.directive('r-html', function(elem, value){
  this.$watch(value, function(newValue){
    elem.innerHTML = newValue
  })
})

这里由于$watch同时接受字符串或者Expression, 所以我们可以在模板里传字符串或插值, 最终r-html的效果是一样的

  <div class='preview' r-html='content'></div>
  <!-- or -->
  <div class='preview' r-html={content}></div>

如果必要你也可以在函数返回一个destroy函数做指令的销毁工作(比如绑定了节点事件). 需要注意的是, regular中watch数据是不需要进行销毁的, regular会自动清理对应的数据绑定

Example

Regular.directive('some-directive', function(elem, value){

  return function destroy(){
    ... destroy logic
  }
})

###Component.filter

regularjs 当然也支持普遍存在于模板中的过滤器,过滤器支持链式的多重调用.

regularjs也支持双向过滤, 来帮助你解决双向数据流的需求

Usage

Component.filter(name, factory)

Syntax

{Expression|filter1: args.. | filter2: args...}

Arguments

param type detail
name string 过滤器名称
factory function object 创建新的自定义过滤器

factory

  • factory.get(origin, args...) [Function]:

    数据从终点到源的处理函数.

  • factory.set(dest, args...) [Function]:

    从最终结果反推到源头的处理函数. .

如果传入的factory是函数类型,则自动成为factory.get

Example1 >

一个简单的日期格式化过滤器

// simplest date format
var filter = function(){
  function fix(str){
    str = "" + (str || "");
    return str.length <= 1? "0" + str : str;
  }
  var maps = {
    'yyyy': function(date){return date.getFullYear()},
    'MM': function(date){return fix(date.getMonth() + 1); },
    'dd': function(date){ return fix(date.getDate()) },
    'HH': function(date){ return fix(date.getHours()) },
    'mm': function(date){ return fix(date.getMinutes())}
  }

  var trunk = new RegExp(Object.keys(maps).join('|'),'g');
  return function(value, format){
    format = format || "yyyy-MM-dd HH:mm";
    value = new Date(value);

    return format.replace(trunk, function(capture){
      return maps[capture]? maps[capture](value): "";
    });
  }
}();
Regular.filter("format", filter)

然后在模板中使用

<p>{time| format: 'yyyy-MM-dd HH:mm'}</p>

输出

<p>2014-12-31 12:30</p>

双向过滤器

双向过滤器主要是帮助我们实现数据的对流, 对任意数据读或写操作时可以进行过滤操作, 与计算属性不同的是,双向过滤器定义是不与具体的数据进行绑定,它是一种可复用的抽象.

双向过滤器如其名,经常会用在双向绑定上, 由于这个特性, r-model 得以与一个数组类型实现双向绑定。 当然你也可以使用它在其它可能有“数据回流”场合,比如内嵌组件

take {[1,2,3]|join: '-'} for example

过滤器定义

Regular.filter('join', {
  //["1","2","3"] - > '1-2-3'
  get: function(origin, split ){
    return origin.join( split || "-" );
  },
  // **Important **
  // "1"-"2"-"3" - > ["1","2","3"]
  set: function(dest, split ){
    return dest.split( split || "-" );
  }
})
{array|json}
<input r-model={array|join:'-'} >

【 DEMO : two-way filter】

####内建过滤器

#####json

这是一个双向过滤器

example

var component = new Regular({
  template: "<h2>{user|json}</h2>"
})

component.$update("user|json", "{'name': 'leeluolee', 'age': 10}")
//console.log(user) --> {'user':'leeluolee', 'age': 10}

Only Browser that support JSON API can get the json filter

#####last

获得数组最后一个元素, 这是一个单向过滤器

{[1,2,3]|last}  ===>  3

###Component.event

Usage

Component.event(name, factory)

设置自定义dom事件

Argument

Param Type Detail
name String the custom event name
factory Function Factory function for creating event type

###Component.animation

自定义一个动画command. animation接口完全是为r-animation指令服务的.

查看 指南: animation 了解更多

Usage

Component.animation(name, factory)

Arguments

Param Type Detail
name String the custom animation name
factory Function Factory function for creating command

Example

###Component.component

注册一个组件,使其可以被, 这里类似与在options中声明name

Usage

Component.component(name, factory)

Arguments

Param Type Detail
name String the name used to insert Component in template
factory Component A Component to be register

Example >

var Pager = Regular.extend({
  // other options
})

Component.component('pager', Pager)

// you can use pager as nested component
Component2 = Component.extend({
  template: "<pager></pager>"
})

###Component.use

Usage

Component.use(factory)

著名的angular中模块化的解决方案是angular.module()和依赖注入, 一个模块可以有factory可以有filter可以有directive等等.

在regular中不可能照搬这种方式, 这是因为

  • regular中没有$rootScope.$digest()这种全局性的__解药__无脑的促使所有绑定进入数据检查阶段,regular组件的生命周期都是独立的, 这就决定了必须让扩展建立与组件的关系.

    比如angular的$timeout之类的实现只需在定时器完成后$rootScope.$digest()即可进入全局的数据检查, 而regular中timeout之后必须调用组件的$update()才进入组件本身的数据检查阶段,即需建立与组件的关系.

  • 模块插件应该是与组件无关的, 绑定只应该在被使用时发生, 这样才是可复用的模块插件.

所以一个典型的插件的写法应该是这样的

function FooPlugin(Componenet){
  Component.implement()// implement method
    .filter()          // define filter
    .directive()       // define directive
    .event()           // define custom event
}

var YourComponent = Regular.extend();

FooPlugin(YourComponent);   // lazy bind
FooPlugin(Regular);         // lazy bind to globals

为了更统一, 所有Component都有一个use函数来统一'使用'插件, 如上例可以写成

YourComponent.use(FooPlugin);

// global
Regular.use(FooPlugin);

##options {#options}

new ComponentComponent.extend, Component.implement 都接受同一种 options 参数

没有提及的配置项都会自动成为Component的原型属性( 或实例属性 )

template

  • type: String | AST

模板字符串需要遵循模板语法,模板只会在首次实例化时被编译一次

你可以选择在构建时通过 Regular.parse 将模板先处理成 AST

name

注册组件到父组件的命名空间内,使其可以被声明式调用。

注意通过name注册,是全局的

const Component = SuperComponent.extend({
  //other options
  name: 'foo1'
})

const Component2 = SuperComponent.extend({
  template: "<foo1></foo1>"
})

也可使用Component.component 注册, 上例实际上等同于

const Component = SuperComponent.extend({});

Regular.component('foo1', Component)

events

  • type: Object

批量定义绑定事件,__这个在需要绑定一些内置事件时格外有用。

Regular.extend({
  events: {
    "$init": function(){
      // same in component.init
    },
    "$destroy": function(){
      // same in component.destroy
    }
  }
})

data

  • type: Object

⚠️不要在 extend 或 implement 时定义data属性 !!! ,这会导致所有实例共享数据。

永远只在 new Componentconfig 中定义初始化函数

computed

  • Type: Object

计算属性定义为键值对

  • key: 表达式名
  • value: 表达式定义

Example

comuted: {
  title: 
}

表达式定义支持几种类型

生命周期钩子

options 中还可以定义如下生命周期钩子

config( data )

  • type: Function

在模板编译 之前 被调用,config一般是用来初始化参数,它接受的 data 即你在new Component() 时传入的 data属性。

init()

  • type: Function

会在模板编译 之后( 即DOM结构已产生 )被调用. 你可以在这里处理一些与DOM相关的逻辑 

destory()

  • type: Function

如果你需要有额外的回收逻辑, 你可以重写destroy方法

记得调用this.supr()来运行默认的回收逻辑 !!!!, 否则会回收失败

var Component = Regular.extend({
//.....
  destroy: function(){
    this.supr(); // call the super destroy 
    ...other logic
  }
})

var component = new Component();

component.destory();

modifyBodyComponent( component, next ) {#modify}

##实例接口 {#instance}

component即代表组件实例, 注意这些公有都有$前缀 意味不建议进行重写

###component.$inject {#inject}

插入组件到指定位置

Usage

component.$inject(element[, direction])

Arguments

Param Type Detail
element Node false 被插入节点,如果传入__false__则代表将此组件从dom中移除
direction_(optional default:'bottom')_ String 组件的位置插入目标的位置. 可以是 'top', 'bottom', 'after', or 'before'.

Example >

假设你已经有这样一个组件

var component = new Component({
  template: "<h2>{title}</h2>",
  data: { title : "Example" }
})
var div = document.getElementById("#div");

和一段html片段

<div id="div">
  <div class='child'></div>
</div>
  • compnent.$inject( div ) or component.$inject( div, 'bottom' )

    resulting html

    <div id="div">
      <div class='child'></div>
      <h2>Example</h2>
    </div>
  • compnent.$inject( div, 'top' )

    resulting html

    <div id="div">
      <h2>Example</h2>
      <div class='child'></div>
    </div>
  • compnent.$inject( div, 'after' )

    resulting html

    <div id="div">
      <div class='child'></div>
    </div>
    <h2>Example</h2>
  • or component.$inject( div, 'before' )

    resulting html

    <h2>Example</h2>
    <div id="div">
      <div class='child'></div>
    </div>
  • component.$inject( false )(假设我们已经调用了以上方法插入了本组件)

    __ 完全从原插入位置移除它(但是没有销毁,你仍然可以再次$inject它)__

    resulting html

    <div id="div">
      <div class='child'></div>
    </div>

Tips

你通过多次调用$inject 将组件有一个位置移动到另外一个位置

###component.$watch

注册一个监听回调,一旦绑定的表达式的值发生改变,它将会被调用

Usage

component.$watch(expression, callback [, options])

Arguments

Param Type Detail
expression Expression 一旦表达式求值发生改变,回调会被触发
callback(newValue, oldValue) Function 回调接受两个参数.
1. newValue: 表达式的新值.
2.oldValue: 表达式的原值

Return

watchid [Number]: 监听id,用于方法 $unwatch

  • expression 会在每次脏检查时被调用,并比较之前的值
  • 当值与上次求值发生变化的判断依据是严格不相等即!==. 一种例外就是当求值为数组时,Regularjs会使用莱文斯坦距离计算数组差异
component.$watch("user.name", function(newValue, oldValue){
  alert("user.name changed from " + oldValue + " to " + newValue) ; 
})

###component.$unwatch

利用watchid解绑一个数据监听, 一般来讲你很少会用到它,因为所有regularjs中的数据绑定会被自动回收,除非你想在模板回收之前清除某个绑定.

Usage

var component = new Regular();

component.$watch('b', function(b){
  alert('b watcher 1');
})

var id = component.$watch('b', function(){
  alert('b watcher 2');
})

component.$unwatch(id);
component.$update('b', 100); // only alert 'watcher 1'

###component.$update

component.$update is used to synchronize data and view

由于regularjs是基于脏检查,所以当不是由regularjs本身控制的操作(如事件、指令)引起的数据操作,可能需要你手动的去同步data与view的数据. $update方法即帮助将你的data同步到view层.

Usage

component.$update([expr] [, value])

更新某个值,并强制进入digest阶段,即脏检查.

Arguments

  • expr(Optional) [Expression| Function | String] - expression可以有多种参数类型

    • String: 此字符串会先被Regular.expression处理为Expression
    • Expression: 此expression需要有set函数, 查看Expression
    • Object: 多重设值
  • value - 设置的值

Example >

var component = new Regular({
  template: "<h2 ref='h2' on-click={title=title.toLowerCase()}>{title}</h2>",
  data: {
    title: "REGULARJS"
  }
});

//=> log 'REGULARJS' , with no doubt
console.log( component.$refs.h2.innerHTML ) 

component.data.title = "LEELUOLEE";

//=> also log 'REGULARJS', regularjs don't know the value is changed.
console.log( component.$refs.h2.innerHTML ) //

// force synchronizing data and view 
component.$update()

//=> also 'REGULARJS'. synchronize now.
console.log( component.$refs.h2.innerHTML ) //


// trigger on-click event  
component.$refs.h2.click();


// should log leeluolee.
// the Expression `title=title.toLowerCase()` is actived.
// when listener is done, regularjs will enter digest phase
console.log( component.$refs.h2.innerHTML ) //

you may need check $refs first

Beacuse you may need to set a complex Expression, $update also accept optional params to set the property easily, for Example

// 1. simple
component.$update("user.name", 'leeluolee')

// is equals to

component.data.user.name = 'leeluolee'
component.$update()


// 2. multiple
component.$update({
  "user.name": "leeluolee",
  "user.age": 20
})
// is equlas to
component.data.user.name = 'leeluolee'
component.data.user.age = 20
component.$update()

你当然也可以使用更复杂的表达式,不过你必须保证你的表达式是可设值的, 不过由于会创建表达式,这显然是不高效的,作者强烈建议不怎么做, 除非你需要通过双向过滤器来设值.

// JSON.parse the title first.
component.$update('title|json', "{'title': 1}");

console.log(component.data.title) // => {title:1};

Warning: 无论传入什么参数,运行$update之后都会进行组件作用域内的dirty-check

###component.$get

Usage

component.$get(Expression|String)

获得一个Expression的值,类似于angular的$eval函数

Example >

component.data.username = "leeluolee"
component.data.job = "developer"

component.$get('username + ":" + job') // => leeluolee:developer

Arguments

Param Type Detail
expression Expression String

###component.$refs

  • type: Object

在模板中,你可以使用ref属性来标记一个节点或组件. 在实例化后,你可以通过component.$refs 来获取你标记的节点

Example >

component = new Regular({
  template: "<input ref=input> <pager ref=pager></pager>",
  init: function(){
    this.$refs.input // -> the input tag
    this.$refs.pager // -> the pager component
  }
})

The less reference the better

###component.$on

Register an event handler fn.

Usage

component.$on(event, fn])

Arguments

Param Type Detail
eventName Object String 事件名
fn Function 监听器回调

如果你传入一个Object, 会成为一个多重事件绑定

Example >

component.$on("hello", fn1)

// multiple
component.$on({
  notify: fn2,
  message: fn3
})

###component.$off

Usage

component.$off([event] [,fn])

Arguments

Param Type Detail
eventName Object String 事件名
fn Function 监听器回调
  • 如果同时传入 event和fn, 则移除指定event类型下的fn函数
  • 只传入event, 移除所有event对应的监听器
  • 什么都不传,移除所有

###component.$emit

触发指定事件

Usage

component.$emit(eventName [, args...])

Arguments

Param Type Detail
eventName Object String 事件名
args Function 剩余的参数都会作为参数传入到监听器

Example >

var component = new Regular();

var clickhandler1 = function(arg1){ console.log('clickhandler1:' + arg1)}
var clickhandler2 = function(arg1){ console.log('clickhandler2:' + arg1)}
var clickhandler3 = function(arg1){ console.log('clickhandler3:' + arg1)}

component.$on('hello', clickhandler1);
component.$on('hello', clickhandler2);
component.$on({ 
  'other': clickhandler3 
});


component.$emit('hello', 1); // handler1 handler2 trigger

component.$off('hello', clickhandler1) // hello: handler1 removed

component.$emit('hello', 2); // handler1 handler2 trigger

component.$off('hello') // all hello handler removed

component.$off() // all component's handler removed

component.$emit('other');

###component.$mute

你可以使用$mute(true)让组件失效,使其不参与到脏检查中. 后续使用 $mute(false) 来重新激活一个被失效的组件, 激活的同时,会自动进行一次数据与ui同步.

Usage

component.$mute( isMute )

Argument

Param Type Detail
mute Boolean 是否disable这个组件(可以后续重启它)

Example >

var component = new Regular({
  template: '<h2>{title}</h2>',
  data: {
    title: "hello"
  }
})

//resulting html

<h2>hello</h2>

component.$mute(true) // disable it

component.data.hello = 'title changed'
component.$update();

// resulting html

<h2>hello</h2>

###component.$bind

创建组件之间的双向绑定.

这已是一个不推荐的方法. 由于$bind过于灵活的双向绑定,极可能不当使用带来难以维护的对象间关系. 请使用事件通讯来处理组件之间的消息同步。

Usage

component.$bind(component2, expr1[, expr2])

Arguments

  1. component2: 要绑定的组件
  2. expr1 <Expression|String|Object|Array>: 此参数有多种参数类型
  • Expression|String: 本组件要绑定的表达式
  • Object: 同时绑定多个表达式对
  • Array: 表达式列表,同时实现多个同名表达式(即只传入expr1)
  1. expr2 <Expression|String>: 目标组件要绑定的表达式, 缺省为expr1
WARN
1. 如果两个表达式都是setable的,可实现双向绑定,否则只能实现单向绑定 2. 如果连个组件在bind时是不同步的,component2数据会先同步到component

create binding between pager components.

 // insert
var pager = new Pager( {data: {total: 100, current:20}} ).$inject('#bind1');
var pager2 = new Pager( {data: {total: 50, current:2}} ).$inject('#bind1');

var pager3 = new Pager({data: {total: 100, current:20} }).$inject('#bind2');
var pager4 = new Pager({data: {total: 50, current:2}}).$inject('#bind2');

var pager5 = new Pager({data: {total: 100, current:2}}).$inject('#bind3');
var pager6 = new Pager({data: {total: 50, current:20}}).$inject('#bind3');


// style 1
pager.$bind(pager2, ['current', 'total']);


// style 2
pager3.$bind(pager4, 'current', 'current')
pager3.$bind(pager4, 'total') // the same as pager3.$bind(pager4, 'total', 'total')

// style 3
pager5.$bind(pager6, {current: "current", total: "total"});


// bind chain
var pager = new Pager({data:{total: 1000, current:1}}).$inject('#bind_chain');
for(var i = 0; i < 10; i++){
  var pager = new Pager({data:{total: 1000, current:1}})
    .$bind(pager, ['total', 'current'])
    .$inject('#bind_chain');
}

Demo here

you may want the source code of pager

##其它

###Regular.dom

由于内部实现需要,Regular实现了部分常用的跨浏览器的dom方法,如果只是简单的dom处理,你可以直接使用Regular.dom.

####Regular.dom.inject(element, refer, direction)

component.$inject 依赖于此方法

Arguments

Param Type Detail
element Node false 要被插入的节点
refer Node false 参考节点
direction_(optional default:'bottom')_ String 组件的位置插入目标的位置. 可以是 'top', 'bottom', 'after', or 'before'.

####Regular.dom.on(element, event, handle)

绑定节点事件, 下列事件对象中的属性已经被修正,你可以在IE6-8使用它们. 回调的this对象也修正为element本身.

  • event.target
  • event.which
  • event.pageX
  • event.pageY
  • event.stopPropagation();
  • event.preventDefault();

Example >

var dom = Regular.dom;

dom.on(element, 'click', function(ev){
  ev.preventDefault();
})

####Regular.dom.off(node, event, handle)

移除一个事件监听器

####Regular.dom.addClass(element, className)

添加节点className

####Regular.dom.delClass(element, className)

移除节点的某段className

####Regular.dom.hasClass(element, className)

判断节点是否拥有某个className

<div class='class1 class2'></div>

dom.hasClass(element, 'class1') // => true

####Regular.dom.text(element[, value])

根据浏览器和节点, 设置节点的textContent 或 innerText

####Regular.dom.html(element[, value])

设置或获取节点的innerHTML值

####Regular.dom.attr(element, name [ , value])

设置或获取节点的指定属性

###Regular.config

配置一些全局属性, 目前主要可以用来配置模板的自定义开关符号

Usage

Regular.config( settings )

Arguments

Param Type Detail
settings.BEGIN String OPEN_TAG (default: '{')
settings.END String END_TAG (default: '}')

Example >

将默认符号{}修改为 {{}}.

Regular.config({
  BEGIN: "{{", 
  END: "}}" 
})

###Regular.parse {#parse}

Usage

Regular.parse(templateString, setting)

解析模板字符串为AST, 基本上你不会使用此方法, 你可以使用此方法来预解析你得regularjs模板

Arguments

Param Type Detail
templateString String 要解析的模板字符串
settings.BEGIN String 开符号 (default: '{'
settings.END String 关符号 (default: '}')
settings.stringify Boolean 是否stringify 输出的AST (default: false)

Usage

Example >

Regular.parse("<h2>{{page.title + page.desc}}</h2>", {
  BEGIN: '{{',
  END: '}}'
})
// output
[
  {
    "type": "element",
    "tag": "h2",
    "attrs": [],
    "children": [
      {
        "type": "expression",
        "body": "_d_['page']['title']+'-'+_d_['page']['desc']",
        "constant": false,
        "setbody": false
      }
    ]
  }
]

服务端渲染