jianaj-render

Browser rendering Library Based on virtual DOM.

Usage no npm install needed!

<script type="module">
  import jianajRender from 'https://cdn.skypack.dev/jianaj-render';
</script>

README

jianaj-render.js

基于虚拟DOM技术的浏览器端渲染库。

安装

# npm:
npm install jianaj-render
# yarn:
yarn add jianaj-render

引入和render()

import render from 'jianaj-render';

/* 
render(selector, options)
-----------------------------
渲染一个DOM元素。

@param: selector, 一个DOM元素或css3选择器。
@param: options, 包含renders['main']主方法的object配置项。
*/

render('#app', {

    // 渲染数据:

    options: {
        text: 'Hello World!'
    },
    
    // 渲染方法:

    renders: {
        main (o, w){

            // o: this.options渲染数据
            // w: this整个对象

            return ["this", {onclick: w.clickApp}, ['span', o.text]];
        }
    },

    // 其它方法:

    clickApp(){
        this._update(o => {
            o.text = 'Hello App!'
        });
    }
});

采用纯js数据表达html

// html: 
let elem = '<div></div>';
// render:
let elem = ["div"];

// html: 
let elem = '<div id="app"></div>';
// render:
let elem = ["div#app"];
let elem = ["div", {id: "app"}];

// html: 
let elem = '<div id="app" class="d-flex flex-row"></div>';
// render:
let elem = ["div#app.d-flex.flex-row"];
let elem = ["div#app.d-flex", {class: "flex-row"}];
let elem = ["div#app", {class: "d-flex flex-row"}];
let elem = ["div", {id: "app", class: "d-flex flex-row"}];

// html:
let elem = '<div id="app" class="d-flex flex-row" style="display:none;">content</div>';
// render:
let elem = ["div#app.d-flex.flex-row[style=display:none;]", "content"];
let elem = ["div#app.d-flex.flex-row", {style: "display:none;"}, "content"];
let elem = ["div", {id: "app", class: "d-flex flex-row", style: "display:none;"}, "content"];

// html:
let elem = '<div id="app"><h6>title</h6><p>text</p></div>';
// render:
let elem = ["div#app", ["h6", "title"], ["p", "text"]];
let elem = ["div#app", [
               ["h6", "title"],
               ["p", "text"]
           ]];

在渲染方法中使用 ["render[name=*]", data]

import render from 'jianaj-render';

/* 
["render[name=*]", data]
------------------------
在一个渲染方法中调用另一个渲染方法。

@attr: name, 被调用的渲染方法名称
@data: data, 传递给被调用渲染方法的参数。如果data是数组,则遍历数组调用渲染方法。
*/

render("#app", {

    options: {
        user: {
            name: "user",
            email: "user@email.com"            
        }  
    },

    renders: {
        main(o, w){
            return ["this", 
                // main方法中调用user方法    
                ["render[name=user]", o.user],

                ["div", "something else here"]
            ];
        },
        user(user, o, w){
            return ["div.user", {
                onclick: [w.clickUser, user]   
            }, [
               ["div.name", user.name],
               ["div.email", user.email]
            ]];
        }
    },
    
    clickUser(user, e, raw){       
        user.email = "render@email.com";
        // 更新
        raw.update();
    }
});

// 另一个例子, 当data是数组的时候:

render("#app", {
    options: {
        items: [
            {text: "action"},
            {text: "another action"},
            {text: "something else here"}
        ]  
    },
    renders: {
        main(o, w){
            return ["ul", [
                // o.items 是数组, item方法被遍历调用三次。
                ["render[name=item]", o.items]
            ]];
        },
        item(item, i, o, w){
            return ["li", {key: i}, ["span", item.text]];
        }
    }
});

定义组件render.widget()

import render from 'jianaj-render';

/* 
render.widget(name, options)
------------------------
定义一个组件。

@param: name, 组件名称
@param: options, 包含renders['main']主方法的object配置项。
*/

render.widget("helloworld", {
    options: {        
        text: "Hello World"    
    },
    renders: {
        main(o, w){
            return ["span", { onclick: w._click}, o.text]
        },
    },
    _click(e, raw) {
        this._update(o => {
            o.text = "Hello World";
        });
    }
});

在渲染方法中使用["widget[name=*]", data, children]

import render from 'jianaj-render';

/* 
["widget[name=*]", data, children]
------------------------
在一个渲染方法中调用组件。

@attr: name, 被调用的组件名称
@options: data, 传递给被调用组件的options。会覆盖组件默认options值。
@slots: children, 传递给被调用组件的slots,组件通过["slot[name=*]",fn]接收处理。
*/

// 定义一个helloworld组件:

render.widget("helloworld", {
    options: {        
        text: "Hello World"    
    },
    renders: {
        main(o, w){
            return ["this", {onclick: w._click}, [
                ["span", o.text],

                // 接收处理default默认插槽。
                ["slot[name=default]", (s, o, w) => {

                    // s.data = {}, s.text = "", s.children = ["p", "this is p"]

                    return s.text || s.children;
                }],

                // 接收处理demo插槽。
                ["slot[name=demo]", (s, o, w) => {

                    // s.data = {text: "demo"}, s.text = "", s.children = ["div", "demo"]

                    return s.text || s.children;
                }]            
            ]];            
        },
    },
    _click(e, raw) {
        this._update(o => {
            o.text = "Hello World";
        });
    }
});

// 调用helloworld组件:

render("#app", {
    options: {
        demo: {text: "demo"}
    },  
    renders: {
        main(o, w){
            return [  

                // 调用hello渲染方法
                ["render[name=hello]"],  
              
                ["div", "something else here"]
            ];
        },
        hello(o, w){

            // 调用helloworld组件
            return [
                "widget[name=helloworld]", 

                // options    
                {text: "hello ~"}, 

                // slots
                [ 
                    // 做为default默认插槽传给组件
                    ["p", "this is p"],
    
                    // 做为demo插槽传给组件
                    ["slot[name=demo]", o.demo, [
                        ["div", "demo"]
                    ]
                ] 
            ]];
        }
    }
});

在渲染方法中使用["slot[name=*]"]

import render from 'jianaj-render';

/* 
["slot[name=*]", data, children]
--------------------------------
调用组件的时候,向组件传递一个具名插槽。

@attr: name, 插槽名称
@data: data, 传递给插槽的数据。
@elems: children, 传递给插槽的DOM元素。
*/

render("#app", {
    options: {
        demo: {text: "demo"}
    },  
    renders: {
        main(o, w){
            return [  

                // 调用hello渲染方法
                ["render[name=hello]"],  
              
                ["div", "something else here"]
            ];
        },
        hello(o, w){

            // 调用helloworld组件
            return [
                "widget[name=helloworld]", 

                // options    
                {text: "hello ~"}, 

                // slots
                [ 
                    // 做为default默认插槽传给组件
                    ["p", "this is p"],
    
                    // 做为demo插槽传给组件
                    ["slot[name=demo]", o.demo, [
                        ["div", "demo"]
                    ]
                ] 
            ]];
        }
    }
});

/* 
["slot[name=*]", fn]
----------------------
组件对传递进来的slot做接收和处理。

@attr: name, 传递进来的slot名称, 不通过具名slot传递进来的DOM元素归到slot[name=default]。
@handler: fn, 接收slot值,处理和返回处理结果。
*/

render.widget("helloworld", {
    options: {        
        text: "Hello World"    
    },
    renders: {
        main(o, w){
            return ["this", {onclick: w.click}, [
                ["span", o.text],

                // 接收处理default默认插槽。
                ["slot[name=default]", (s, o, w) => {

                    // s.data = {}, s.text = "", s.children = ["p", "this is p"]

                    return s.text || s.children;
                }],

                // 接收处理demo插槽。
                ["slot[name=demo]", (s, o, w) => {

                    // s.data = {text: "demo"}, s.text = "", s.children = ["div", "demo"]

                    return s.text || s.children;
                }]            
            ]];            
        },
    },
    click(e, raw) {
        this._update(o => {
            o.text = "Hello World";
        });
    }
});

使用jquery

import render from 'jianaj-render';
import select2 from 'select2';

const $ = render.jquery;

render("#app", {
    renders: {
        main(o, w){
            // "this" 指代挂载元素。
            return ["this", {
                oncreate(){
                    $.ajax({
                        ...
                    }).then(res => {
                        w._update();
                    });
                }                      
            }, [
                ["div", "this is div"],
                ["select", {
                    "oncreate,onupdate,ondestroy": w.setSelect
                }]
            ]];
        }
    },
    
    setSelect(raw, rm){
        // create
        if(!rm){ 
            $(raw.node).select2({...});
        }
        // update
        else if(!$.isFunction(rm)){
            $(raw.node).select2("update", {...});
        } 
        // destroy
        else{
            $(raw.node).select2("destroy");
            rm();    
        }   
    }
});