Riot - 你的下一个框架

前端,你是文艺界的程序员

Riot - 你的下一个框架_第1张图片

我为什么要用Riot

 优点明显,体积小,加载快,继承了react,Polymer等框架的优势,并简化他们,主要从以下几个方面考虑:

1. 自定义标签。

 Riot 在所有浏览器上支持自定义标签,我们能将页面组件化,一个自定义标签结构如下所示:


  
  

{ item }

2. 对阅读友好

 有了自定义标签功能后,我们可以用很简洁语言‘拼凑’出复杂的用户界面,加上语义化标签定义,在阅读的时候,很容易看清楚哪个标签是给html加入了什么组件,一共有多少个组件,这样一个页面大致实现什么功能,甚至不用看浏览器展示都能明白。你的代码可能是这样的


  

Acme community

 很清楚,页面被分成四三个模块,一个标题h1,一个header,一个content和一个footer,这样就在我们脑海构成一个基础的网页模型
 Riot 标签首先被 编译 成标准 JavaScript,然后在浏览器中运行。

3. 虚拟 DOM
  • 保证最少量的DOM 更新和数据流动
  • 单向数据流: 更新和删除操作由父组件向子组件传播
  • 表达式会进行预编译和缓存以保证性能
  • 为更精细的控制提供生命周期事件
  • 支持自定义标签的服务端渲染,支持单语言应用
4. 与标准保持一致
  • 没有专有的事件系统
  • 渲染出的 DOM 节点可以放心地用其它工具(库)进行操作
  • 不要求额外的 HTML 根元素或 data- 属性
  • 与 jQuery 友好共存

5. 友好的语法

(1).强大的简写语法

class={ enabled: is_enabled, hidden: hasError() }

(2).语义化强,不需要费脑记忆

render, state, constructor 或 shouldComponentUpdate

(3).直接插值

Add #{ items.length + 1 } 或 class="item { selected: flag }"

(4).用

2. 标签语法

Riot标签是布局(HTML)与逻辑(JavaScript)的组合。以下是基本的规则:

  • 先定义HTML,然后将逻辑包含在一个可选的
    5. 标签 css

     在标签内部可以放置一个 style 标签,Riot.js 会自动将它提取出来并放到 部分,因为放到了head部分,所以其他文件也能调用到该样式

    
      
      

    { opts.title }

    6. 局部 CSS

     支持局部 CSS 。

    
      
      

    { opts.title }

     css的提取和移动只执行一次,无论此自定义标签被初始化多少次。 为了能够方便地覆盖CSS,你可以指定Riot在中的哪个位置插入标签所定义的css:

    
    

     例如,在某些场景下可以指定将riot组件库的标签css放在normalize.css后面,而放在网站的整体主题CSS之前,这样可以覆盖组件库的默认风格。

    二、自定义标签的加载

    1. 直观体验

     自定义标签实例被创建后,就可以象这样将其加载到页面上:

    
    
      
      
    
      
      
    
      
      
    
      
      
    
    
    

     放置在页面 body 中的自定义标签必须使用正常关闭方式: ,自关闭的写法: 不支持。

    2. mount 方法的使用方法

     将自定义标签放在 后,我们还需要调用riot.mount()才能将其加载进来。一个html文档中可以包含一个自定义标签的多个实例。

    // mount 页面中所有的自定义标签
    riot.mount('*')
    
    // mount 自定义标签到指定id的html元素
    riot.mount('#my-element')
    
    // mount 自定义标签到选择器选中的html元素
    riot.mount('todo, forum, comments')
    
    3. 标签生命周期

    自定义标签的创建过程是这样的:

    1. 创建标签实例
    2. 标签定义中的JavaScript被执行
    3. HTML 中的表达式被首次计算并首次触发 “update” 事件
    4. 标签被加载 (mount) 到页面上,触发 “mount” 事件

    加载完成后,表达式会在以下时机被更新:

    1. 当一个事件处理器被调用(如上面ToDo示例中的toggle方法)后自动更新。你也可以在事件处理器中设置 e.preventUpdate = true 来禁止这种行为。
    2. 当前标签实例的 this.update() 方法被调用时
    3. 当前标签的任何一个祖先的 this.update() 被调用时. 更新从父亲到儿子单向传播。
    4. riot.update() 方法被调用时, 会更新页面上所有的表达式。

    每次标签实例被更新,都会触发“update” 事件。
    由于表达式的首次计算发生在加载之前,所以不会有类似 计算失败之类的意外问题。

    4. 监听生命周期事件

    标签定义内部可以这样监听各种生命周期事件:

    
      this.on('before-mount', function() {
        // 标签被加载之前
      })
    
      this.on('mount', function() {
        // 标签实例被加载到页面上以后
      })
    
      this.on('update', function() {
        // 允许在更新之前重新计算上下文数据
      })
    
      this.on('updated', function() {
          // 标签模板更新后
        })
    
      this.on('before-unmount', function() {
        // 标签实例被删除之前
      })
    
      this.on('unmount', function() {
        // 标签实例被从页面上删除后
      })
    
      // 想监听所有事件?
      this.on('all', function(eventName) {
        console.info(eventName)
      })
    
    
    5. 访问 DOM 元素

     Riot 允许开发人员通过 this 实例直接访问设置了 name 属性的元素,也提供了各种简化的属性方法如 if="{...}",但偶尔你还是需要直接完成这些内置手段所不支持的DOM操作。

    6. 如何使用 jQuery, Zepto, querySelector, 等等

     如果需要在Riot中访问DOM,要注意 DOM 元素的初始化发生在第一个 update() 事件被触发之后,这意味着在这之前试图选择这个元素将都失败。

    
      

    Do I even Exist?

     你可能并不打算在每次update时都去取一下你想要的元素,而更倾向于在 mount 事件中做这件事。

    
      

    Do I even Exist?

    7. 基于上下文的 DOM 查询

     现在我们知道了如何在处理 updatemount 事件时获取 DOM 元素,现在我们可以利用这一点,将 根元素 (我们所创建的 riot 标签实例) 作为DOM元素查询的上下文。

    
      

    Do I even Exist?

    Is this real life?

    Or just fantasy?

    8. 标签选项(参数)

    mount 方法的第二个参数用来传递标签选项

    
    

     在标签内部,通过 opts 变量来访问这些参数,如下:

    
      
      

    { opts.title }

    // 在 JavaScript 中访问参数 var title = opts.title
    9. Mixin

     Mixin 可以将公共代码不同标签之间方便地共享。

    var OptsMixin = {
        init: function() {
          this.on('updated', function() { console.log('Updated!') })
        },
    
        getOpts: function() {
            return this.opts
        },
    
        setOpts: function(opts, update) {
            this.opts = opts
    
            if(!update) {
                this.update()
            }
    
            return this
        }
    }
    
    
        

    { opts.title }

    this.mixin(OptsMixin) // 用 mixin() 加上mixin名字来将mixin混入标签.

     上例中,我们为所有 my-tag 标签实例混入了 OptsMixin ,它提供 getOptssetOpts 方法. init 是个特殊方法,用来在标签载入时对mixin进行初始化。 (init 方法不能混入此mixin的标签中访问)

    var my_tag_instance = riot.mount('my-tag')[0]
    
    console.log(my_tag_instance.getOpts()) // 输出的所有的标签选项
    

     标签的mixin可以是 object – {'key': 'val'} var mix = new function(...) – 混入任何其它类型的东西将报错.

    现在:my-tag 定义又加入了一个 getId 方法,以及OptMixin中除init以外的所有其它方法

    function IdMixin() {
        this.getId = function() {
            return this._id
        }
    }
    
    var id_mixin_instance = new IdMixin()
    
    
        

    { opts.title }

    this.mixin(OptsMixin, id_mixin_instance)

     由于定义在标签这个级别,mixin不仅仅扩展了你的标签的功能, 也能够在重复的界面中使用. 每次标签被加载时,即使是子标签, 标签实例也获得了mixin中的代码功能.

    10. 共享 mixin

     为了能够在文件之间和项目之间共享mixin,提供了 riot.mixin API. 你可以像这样全局性地注册mixin :

    riot.mixin('mixinName', mixinObject)
    

    用 mixin() 加上mixin名字来将mixin混入标签.

    
        

    { opts.title }

    this.mixin('mixinName')

    表达式

    1, 直观理解

    在 HTML 中可以混合写入表达式,用花括号括起来。[ style 标签中的表达式将被忽略.]

    { /* 某个表达式 */ }
    

    表达式可以放在html属性里,也可以作为文本节点嵌入:

    { /* 嵌入表达式 */ } // 文本节点

    当然,并不是什么表达式都是能嵌入,因为Riot只支持属性(值)表达式和嵌入的文本表达式,以下将会执行失败。

    
    

    表达式是 100% 纯 JavaScript. 一些例子:

    { title || 'Untitled' }
    { results ? 'ready' : 'loading' }
    { new Date() }
    { message.length > 140 && 'Message is too long' }
    { Math.round(rating) }
    

     建议的设计方法是使表达式保持最简从而使你的HTML尽量干净。如果你的表达式变得太复杂,建议你考虑将它的逻辑转移到 “update” 事件的处理逻辑中. 例如:

    
      
      

    { val }

    // 每次更新时计算 this.on('update', function() { this.val = some / complex * expression ^ here })
    2. 布尔属性

    如果表达式的值为非真,则布尔属性 (checked, selected 等..) 将不被渲染:
    渲染为
    这与 W3C 有很大区别,W3C规范是只要布尔属性存在即为true,即使他的值为或者false

    3. class 属性简化写法

    Riot 为 CSS class 名称提供了特殊语法. 看一个例子

    该表达式最终被计算为 “foo baz zorro”.,只有表达式中为真值的属性名会被加入到class名称列表中. 这种用法并不限于用在计算class名称的场合。

    4. 转义

    用以下的写法来对花括号进行转义:

    \\{ this is not evaluated \\}  输出为  { this is not evaluated }
    
    5. 渲染原始HTML

    Riot 表达式只能渲染不带HTML格式的文本值。如果真的需要,可以写一个自定义标签来做这件事. 例如:

    
      
    
      this.root.innerHTML = opts.content
    
    

    这个标签定义以后,可以被用在其它的标签里. 例如

    
      

    原始HTML:

    this.html = 'Hello, world!'

    嵌套标签

    我们来定义一个父标签 ,其中嵌套一个子标签 :

    
      
    
    
    // 子标签
    
      

    { opts.plan.name }

    // 取得标签选项 var plan = opts.plan, show_details = opts.show_details // 取出子标签的标签属性 // 访问父标签实例 var parent = this.parent // 获取父标签的标签实例

    注意: 我们使用下划线的风格(而不是驼峰风格)对 show_details 进行命名,由于浏览器的约定,驼峰风格的命名会被自动转换成小写.
    如果在页面上加载 account 标签,带 plan 选项,调用riot.mount()方法。

    
      
    
    
    
    

    注意: 嵌套的标签只能定义在自定义父标签里,如果定义在页面上,将不会被初始化。


    嵌套 HTML

    “HTML transclusion” 是处理页面上标签内部 HTML 的方法. 通过内置的 标签来实现.

    
      

    Hello

    this.text = 'world'

    页面上放置自定义标签,并包含嵌套的 HTML

    
      { text }
    
    

    结果得到

    
      

    Hello world


    DOM元素与name自动绑定

     我感觉这个功能真的是帅炸了。当html被定义好了之后,带有 ref 属性的DOM元素将自动被绑定到上下文中,这样就可以从JavaScript中方便地访问它们:

    
      

    当然,因为DOM已经被绑定到上下文中,所以我们也可以直接在HTML中以表达式形式引用:

    { username.value }

    事件处理器

    1. 一般处理

    响应DOM事件的函数称为 “事件处理器”.

    
      
    // 上面的表单提交时调用此方法 submit(e) { }

     以”on” (onclick, onsubmit, oninput等…)开头的属性的值是一个函数名,当相应的事件发生时,此函数被调用. 函数也可以通过表达式来动态定义:

     在此函数中,this指向当前标签实例。当处理器被调用之后, this.update() 将被自动调用,将所有可能的变化体现到 UI 上。

    2. 阻止默认行为

     如果事件的目标元素不是checkboxradio按钮,默认的事件处理器行为是 自动取消事件 . 意思是它总会自动调用 e.preventDefault() , 因为通常都需要调用它,而且容易被遗忘。如果要让浏览器执行默认的操作,在处理器中返回 true 就可以了.

    submit() {
      return true
    }
    
    3. 事件对象

     事件处理器的第一个参数是标准的事件对象。事件对象的以下属性已经被Riot进行了跨浏览器兼容

    • e.currentTarget 指向事件处理器的所属元素.
    • e.target 是发起事件的元素,与 currentTarget 不一定相同.
    • e.which 是键盘事件(keypress, keyup, 等…)中的键值 .
    • e.item 是 循环 中的当前元素.

    渲染条件 - show / hide / if

    可以基于条件来决定显示或隐藏元素。例如:

    This is for premium users only

    同样, 渲染条件中的表达式也可以是一个简单属性,或一个完整的 JavaScript 表达式. 有以下选择:

    • show – 当值为真时用 style="display: ''" 显示元素
    • hide – 当值为真时用 style="display: none" 隐藏元素
    • if – 在 document 中添加 (真值) 或删除 (假值) 元素

    判断用的操作符是 == 而非 ===. 例如: 'a string' == true.


    循环

    1. 循环是用 each 属性来实现:
    
      
    • { title }
    this.items = [ { title: 'First item', done: true }, { title: 'Second item' }, { title: 'Third item' } ]

     定义 each 属性的html元素根据对数组中的所有项进行重复。 当数组使用 push(), slice()splice 方法进行操作后,新的元素将被自动添加或删除。

    2. 上下文

     循环中的每一项将创建一个新的上下文(标签实例);如果有嵌套的循环,循环中的子标签都会继承父循环中定义了而自己未定义的属性和方法。Riot通过这种方法来避免重写不应在父标签中重写的东西。
    从子上下文中可以通过显式地调用 parent 变量来访问上级上下文.

    
      

    { title }

    Remove
    this.items = [ { title: 'First' }, { title: 'Second' } ] remove(event) { }

     该例中,除了 each 属性外,其它都属于子上下文, 因此 title 可以被直接访问而 remove 需要从 parent. 中访问,因为remove方法并不是循环元素的属性.
     每一个循环项都是一个标签实例. Riot 不会修改原始数据项,因此不会为其添加新的属性。

    3. 循环项的事件处理器

    事件处理器中可以通过 event.item 来访问单个集合项。这种办法采用了事件委托机制,极大减少了对DOM的访问。
    下面我们来实现上方的 remove 函数:

    
      

    { title }

    Remove
    this.items = [ { title: 'First' }, { title: 'Second' } ] remove(event) { // 循环项 var item = event.item // 在集合中的索引 var index = this.items.indexOf(item) // 从集合中删除 this.items.splice(index, 1) }

     事件处理器被执行后,当前标签实例会自动调用 this.update()(你也可以在事件处理器中设置 e.preventUpdate = true 来禁止这种行为)从而导致所有循环项也被更新. 父亲会发现集合中被删除了一项,从而将对应的DOM结点从document中删除。

    4. 循环自定义标签

    自定义标签也可以被循环

    
    

    当前循环项可以用 this 来引用,你可以用它来将循环项作为一个参数传递给循环标签。

    5. 非对象数组

    数组元素不要求是对象. 也可以是字符串或数字. 这时可以用 { name, i in items } 写法

    
      

    { i }: { name }

    this.arr = [ true, 110, Math.random(), 'fourth']

    name 是元素的名字,i 是索引. 这两个变量的变量名可以自由选择。

    6. 对象循环

    也可以对普通对象做循环. 例如:

    
      

    { name } = { value }

    this.obj = { key1: 'value1', key2: 1110.8900, key3: Math.random() }

     不太建议使用对象循环,因为在内部实现中,Riot使用 JSON.stringify 来探测对象内容的改变. 整个 对象都会被检查,只要有一处改变,整个循环将会被重新渲染. 会很慢. 普通的数组要快得多,而且只有变化的部分会在页面上体现。

    7. 循环的高级技巧

     在 riot v2.3 中,为了使循环渲染更可靠,DOM 结点的移动,插入和删除总是与数据集合同步的: 这种策略会导致渲染过程比之前的版本慢一些。要使用更快的渲染算法,可以在循环结点上加上 no-reorder 属性。

    
      
    { item }

    使用标准 HTML 元素作为标签 | #riot-tag

    页面body中的标准 HTML 元素也可以作为riot标签来使用,只要加上 riot-tag 属性.

      这为用户提供了一种选择,与css框架的兼容性更好. 这些标签将被与其它自定义标签一样进行处理。

      riot.mount('my-tag')
      

      会将 my-tag 标签 加载到 ul 元素上


      服务端渲染 | #server-side

      Riot 支持服务端渲染,使用 Node/io.js 可以方便地引用标签定义并渲染成 html:

      var riot = require('riot')
      var timer = require('timer.tag')
      
      var html = riot.render(timer, { start: 42 })
      
      console.log(html) // 

      Seconds Elapsed: 42

      循环和条件渲染都支持.

    你可能感兴趣的:(Riot - 你的下一个框架)