omi/tutorial/all.md

15 KiB
Raw Blame History

精通Omi框架这篇足够(持续更新..)

简介

Omi框架目前最新版本为1.6.1提供了渐进增强式的Web开发解决方案内置完善的支持无限声明式嵌套的组件系统。概括起来包含下面优点和特性:

  • 良好的兼容性 - 兼容IE8及IE8以上版本
  • 超小的尺寸 - 7 kb (gzip) 虽然文件尺寸小
  • 面向未来的架构体系 - 未来DOM很快而且越来来快! 其实现在DOM已经足够快了:)
  • ES6+ 和 ES5都可以 - Omi提供了ES6+和ES5的两种开发方案。你可以自有选择你喜爱的方式
  • 局部CSS - HTML+ Scoped CSS + JS组成可复用的组件。不用担心组件的CSS会污染组件外的,Omi会帮你处理好一切
  • 模板或指令系统可替换 - 默认使用soda指令系统开发者可以重写Omi.template方法来使用任意模板引擎或者指令引擎
  • 完全面向对象 - 函数式和面向对象各有优劣Omi使用完全的面向对象的方式来构建Web程序。而且支持使用TypeScript来编写Omi程序
  • 更自由的更新 - 每个组件都有update和updateSelf方法自由选择你认为最佳的更新时机和最佳的更新方式。updateSelf不会更新子组件

安装Omi

npm install omi

你也可以通过cdn获取omi并在HTML里声明script变迁:

<script src="https://unpkg.com/omi@1.6.0/dist/omi.min.js"></script>

Hello Omi

import Omi from 'omi';

class Hello extends Omi.Component {

    style () {
      return  `
          h1{
          	cursor:pointer;
          }`
    }
  
    handleClick(evt){
      alert(evt.target.innerHTML)
    }
  
    render() {
      return  `
      <div>
      	<h1 onclick="handleClick">Hello ,{{name}}!</h1>
      </div>`
    }
}

Omi.tag('hello', Hello)

class App extends Omi.Component {
    render() {
        return  `
        <div>
            <hello data-name="Omi"></hello> 
        </div>`
    }
}

Omi.render(new App(),"#container")
  • 组件继承自Omi.Component
  • render返回的是组件HTML片段一般带有指令或者模板语法
  • style返回的是组件的CSS是局部的不会污染组件以外的
  • 通过Omi.tag('hello', Hello)把组件变成可声明在其他组件中的标签,即:<hello></hello>
  • 通过data-name="Omi"把传递给子组件hello子组件直接在render里可以使用{{name}}来使用传递来的数据
  • 通过onclick="handleClick"给HTML元素绑定事件在handleClick回调中可以拿到event对象即:handleClick(evt){ }

你也可以使用ES5的方式进行开发:

var Hello =  Omi.create("hello", {
  
    style: function () {
        return "h1{ cursor:pointer }"
    },
  
    handleClick: function (evt) {
        alert(evt.target.innerHTML)
    },
  
    render: function () {
        return ' <div>\
                        <h1 onclick="handleClick">\
                            Hello ,{{name}}!\
                        </h1>\
                </div>'
    }
});

var App =  Omi.create("App", {
 
    render: function () {
        return '<div>\
                    <hello data-name="Omi"></hello>\
                </div>'
    }
  
});

Omi.render(new App(),'#container')

组件

Omi框架完全基于组件体系设计我们希望开发者可以像搭积木一样制作Web程序一切皆是组件组件也可以嵌套子组件形成新的组件新的组件又可以当作子组件嵌套至任意组件形成新的组件...

上面的Hello Omi的例子已经说明了组件大概这里再使用Todo的例子来讲解Omi组件体系的使用。

先定义一个List组件用来展示列表:

class List extends Omi.Component {
    render () {
        return `<ul>
                    <li o-repeat="item in items">{{item.text}}</li>
                </ul>`;
    }
}
  • 使用o-repeat="item in items"来遍历传入的items当然也可以拿到item的索引通过{{$index}}就可以:
<li o-repeat="item in items">{{$index}} - {{item}}</li>

怎么使用这个List我们需要使用Omi.tag把List制作成可以声明式的标签在render方法中就能直接使用该标签。如下所示

import List from './list.js';

Omi.tag('list', List);

class Todo extends Omi.Component {
    constructor(data) {
        super(data)
    }

    add (evt) {
        evt.preventDefault();
        this.data.items.push({text: this.data.text})
        this.data.text = ''
        this.update()
    }

    style () {
        return `
        h3 { color:red; }
        button{ color:green;}
        `;
    }

    handleChange(target){
        this.data.text = target.value;
    }

    render () {
        return `<div>
                    <h3>TODO</h3>
                    <list ::data-items="data.items" ></list>
                    <form onsubmit="add(event)" >
                        <input type="text" onchange="handleChange(this)"  value="{{text}}"  />
                        <button>Add #${this.data.items.length}</button>
                    </form>
                </div>`;
    }
}

Omi.render(new Todo({ items: [] ,text : '' }),"body");

注意看上面的这行代码<list ::data-items="data.items" ></list>。这样就把父组件的data.items传递给了子组件的data.items。这里解释下没有冒号一个冒号和两个冒号:

  • data-* 代表直接传递字符串
  • :data-* 代表传递javascript表达式比如data-num="1" 代表传递数字1而非字符串data-num="1+1"可以传递2。
  • ::data-* 代表传递父组件的属性,比如上面的::data-items="data.items"就代表传递this.data.items给子组件

多个中划线会形成驼峰在子组件中访问。比如 data-page-index 在子组件中使用 this.data.pageIndex 访问。

这里需要特别强调的是为了更加的自由和灵活度。Omi没有内置数据变更的自动更新需要开发者自己调用update方法。 关于Omi组件通讯其实有4种方案这个后续教程会专门来讲。

点击这里→在线试试

组件通讯

通讯概览

这一节主要是解释上面这张图。

Omi框架组建间的通讯非常便利灵活,足够满足业务场景需要。因为有许多可选方案进行通讯:

  • on-* 代表传递父组件向子组件注入的回调函数,比on-page-change="pageChangeHandler"
  • data-* 代表直接传递字符串
  • :data-* 代表传递javascript表达式比如data-num="1" 代表传递数字1而非字符串data-num="1+1"可以传递2。
  • ::data-* 代表传递父组件的属性,比如上面的::data-items="data.items"就代表传递this.data.items给子组件
  • data 代表传递父组件的属性,比如data="user"代表传递this.user给子组件
  • :data 代表传递javascript表达式比如data="{ name : 'dntzhang' , age : 18 }"代表传递{ name : 'dntzhang' , age : 18 }给子组件
  • group-data 代表传递父组件的数组一一映射到子组件

on-*传递回调函数

data-*通讯

class Hello extends Omi.Component {
    constructor(data) {
      super(data);
    }
    
    render() {
      return  `
      <div>
      	<h1 onclick="handleClick(this, event)">Hello ,{{name}}!</h1>
      </div>
  		`;
    }
}

Omi.tag('hello', Hello);

class App extends Omi.Component {
    render() {
        return  `
        <div>
            <hello data-name="Omi" ></hello>
        </div>
        `;
    }
}

Omi.render(new App(),"#container");

一般data-*用来传递值类型如string、number。值得注意的是通过data-*接收到的数据类型都是string需要自行转成number类型或者使用:data-*来传递number。

data通讯

如上面代码所示,通过 data-name="Omi"可以把name传递给子组件。下面的代码也可以达到同样的效果。

...
class App extends Omi.Component {
    constructor(data) {
      super(data);
      this.helloData = { name : 'Omi' };
    }
  
    render() {
        return  `
        <div>
            <hello data="helloData" ></hello>
        </div>
        `;
    }
}

Omi.render(new App(),"#container");

使用data声明会去组件的instance也就是this下找对应的属性this下可以挂载任意复杂的对象。所以这也就突破了data-*的局限性。

如果instance下面的某个属性下面的某个属性下面的某个数组的第一个元素的某个属性要作为data传递Hello怎么办 没关系data声明是支持复杂类型的使用方式如下:

...
class App extends Omi.Component {
    constructor(data) {
        super(data);
        this.complexData ={
            a:{
                b:{
                    c:[
                        {
                            e:[{
                                name:'ComplexData Support1'
                            },{
                                name:'ComplexData Support2'
                            }]
                        },
                        {
                            name: 'ComplexData Support3'
                        }
                    ]
                }
            }
        };
    }
  
    render() {
        return  `
        <div>
            <hello data="complexData.a.b.c[1]" ></hello>
        </div>
        `;
    }
}
...

点击这里→data映射复杂数据

group-data通讯

group-data专门是为了给一组组件批量传递data而设计。

import Hello from './hello.js';


Omi.makeHTML('Hello', Hello);

class App extends Omi.Component {
    constructor(data) {
        super(data);
        this.testData = [{name: 'Omi'}, {name: 'dntzhang'}, {name: 'AlloyTeam'}];
    }

    render() {
        return  `
        <div>
            <hello group-data="testData" ></hello>
            <hello group-data="testData" ></hello>
            <hello group-data="testData" ></hello>
        </div>
        `;

    }
}

Omi.render(new App(),"#container");

只需要在声明的子组件上标记group-data就会去当前组件的instance也就是this下面找对应的属性然后根据当前的位置和对应数组的位置会一一对应起来。

运行结果如下:

点击这里→group-data

同样group-data支持复杂数据类型的映射需要注意的是group-data映射的终点必须是一个数组:

import Hello from './hello.js';


Omi.tag('hello', Hello);

class App extends Omi.Component {
    constructor(data) {
        super(data);
        this.complexData ={
            a:{
                b:{
                    c:[
                        {
                            e:[{
                                name:'ComplexData Support1'
                            },{
                                name:'ComplexData Support2'
                            }]
                        },
                        {
                            name: 'ComplexData Support3'
                        }
                    ]
                }
            }
        };
    }

    render() {
        return  `
        <div>
            <hello group-data="complexData.a.b.c[0].e" ></hello>
            <hello group-data="complexData.a.b.c[0].e" ></hello>
        </div>
        `;

    }
}

Omi.render(new App(),"#container");

点击这里→group-data映射复杂数据

通过对象实例

...
class App extends Omi.Component {
    constructor(data) {
        super(data);
    }
    
    installed(){
        this.hello.data.name = "Omi";
        this.update()
    }
  
    render() {
        return  `
        <div>
            <hello name="hello" ></hello>
        </div>
        `;
    }
}

Omi.render(new App(),"#container");

通过omi-id

...
class App extends Omi.Component {
    constructor(data) {
        super(data);
    }
    
    installed(){
        Omi.get("hello").data.name = "Omi";
        this.update()
    }
  
    render() {
        return  `
        <div>
            <hello omi-id="hello" ></hello>
        </div>
        `;
    }
}

Omi.render(new App(),"#container");

通过在组件上声明omi-id在程序任何地方拿到该对象的实例。

组件通讯实战

render () {
    return `<div>
                <h1>Pagination Example</h1>
                <content name="content" ></content>
                <pagination
                    name="pagination"
                    :data-total="100"
                    :data-page-size="10"
                    :data-num-edge="1"
                    :data-num-display="4"
                    on-page-change="handlePageChange" ></pagination>
            </div>`;
}

未完待续...