1.3.7 • Published 1 year ago

vandf v1.3.7

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

Vandf

1.总体功能描述

​ 采用主流的javascript语言进行开发,兼容chorme、firefox等主流浏览器。

​ Vandf框架(The vanilla javascript data framework),由原来的D3C开关框架演变而来,采用数据驱动的底层架构,数据可以自由的将组件渲染为浏览器DOM元素构成的界面。该框架对数据没有任何约束,即不规定数据的特定结构。同一组件可以用不同结构的数据进行驱动。

​ 组件定义方式简单便捷,符合直觉,成型组件的任意组成部分仍可根据使用者的需要方便的修改。

该框架可以方便的与纯css框架配合构建现代化的响应式组件,例如可以和tailwind css框架和bulma css框架进行配合。

​ 该组件也可以与d3.js数据驱动的图形库进行很好的配合,可以有效降低d3.js使用的难度,使得更容易构建出用户需要的图表应用。

​ 通过该框架定义的组件,可以通过任意组合来轻松的实现整个用户界面的构建。

Vandf框架具有如下特点:

  • 易学易用

​ 将HMTL的DOM模型的复杂性隔离起来,开发者不需要面对复杂的DOM元素操作,只需要关心业务数据本身和视图展示的数据需求,心智负担大大减小。

  • 性能出色

​ 框架只更新数据变更的DOM元素,完全响应式的渲染系统,几乎不需要手动优化,框架未采用虚拟DOM架构,没有虚拟DOM 差异比对方面的性能消耗,可以达到非常高的渲染效率。

  • 灵活多变

    框架可以在数据构建模型时按需配置界面的显示样式生成,也可以在DOM元素创建和渲染过程中,根据不同数据和状态构建外观,提供了灵活多变的自由度。

  • 精细操控 组件中的任意部件都可以进行精细控制,在保证组件的独立完整性的基础上,仍提供了组件各个部分精细操作的通道。

2.认识Vandf

​ Vandf是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的数据驱动的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vandf 都可以胜任。示例展示了Vandf申明式组件定义、响应式视图模型、用户数据模型三个核心功能。

  • 声明式组件定义

​ Vandf组件定义采用了申明式定义方式,通过定义标签tag,样式style,属性attrs,类classes等,使得我们可以简明地描述最终输出的 HTML层次关系和展现的样式。组件定义时可以绑定视图模型和数据模型,还可以通过data(items)方法初始化数据,下例中提供了三个数据,Vandf会自动创建3个计数器按钮。如下图所示:

image-20240103222810499

  • 组件定义
let app = Van.new('app')
    
Van.new('counter', app.id)
    .tag('button')
    .view(CounterView)
    .model(CounterModel)
    .supply([0,10,20])

app.attach(document.querySelector('#app'));
  • 用户数据模型

​ 用户数据模型可以通过构造函数获取组件定义阶段的初始化数据,也可以通过data(parentDatum)方法获取父组件的数据,data()方法返回一个数组,Vandf会构建与数组个数一样多的组件单元。当未定义数据模型或data()方法时,Vandf会自动将父组件的数据(datum)原封不动的向子组件传递。

class CounterModel {
    constructor(data){
        this.count = data;
    }

    supply () {return this.count}

    add(index){ this.count[index] = this.count[index] + 1 }
}
  • 视图模型

​ Vandf视图模型可以应以组件创建(create)、渲染(render)、释放(destroy)、状态(states)方法,可以非常容易地构建响应式组件。视图模型可以在创建(create)阶段注册事件进行交互,在渲染(render)阶段根据数据和状态变化自动更新组件。可以通过states接收父级组件的状态,也可以向下传递本组件的状态变化。

class CounterView {
    create (node,model) {
        node.on('click',(e)=>{
            model.add(i);
            node.update()
        },'native')
    }
    render (node) {
        node.text(d =>`The count is : ${d}`)
    }
}

3.组件定义

​ 通过外部配置实现对组件结构、样式、属性、数据等的初始值进行定义,并可以通过绑定组件视图、用户数据模型,方便的实现数据驱动的响应式组件的构建。

Van.new("label")
        .tag("button")
        .classes("button-label no-selected")
        .styles({
            background: 'white',
            color: 'red',
            'margin-left': '5px'
        })
        .attrs({value:"1234"})
        .html("<b>Label</b>")

组件属性

属性名称属性功能说明
kids子组件数组
name组件名称
id组件id组件id实质是一个包含名称的路径字符串
views所有注册的视图类

组件方法

组件方法方法功能说明
after (name)在本组件后插入指定名称的兄弟组件用于修改第三方组件
append (kid)添加子组件kidkid是Van的实例
attach (element)将组件挂载到HTML Element下组织挂载之后才可以实例化。
before (name)在本组件前插入指定名称的兄弟组件用于修改第三方组件
config(initConfig)设定配置信息,initConfig是Object类型。用于配置一些初始化信息。
get (name)按名称获取子组件
index(idString)获取组件id为idString的子组件的索引
insert (kid, index)在index位置插入子组件kidkid是Van的实例
kid(name)创建并添加名称为name的子组件
model (model)设置或返回模型,无model参数时,返回模型。模型始终只有一个实例。参见model模型章节。
parent (name)获取指定名称为name的父组件未传name参数时,获得上一层父组件
tag (tagName)指定Html的tag名称,默认‘div’,支持svg各类tag名称。例: ‘button’,‘span’,‘p’,‘input’,‘svg'等
attrs(config)设置属性,参数config为对象例:comp.attrs({'name':'China'})
classes(names)设置类型名称例:comp.classes("label left-bar")
html (text)设置html内容例:comp.html(Label)
select(name)按名称name选择子组件,不限制选择层次。例:comp.select(“kidName”)
repeat (repeatComponent: Van): Van;重用repeatComponent组件,渲染位置为本组件所占位置。其中repeatComponent组件已在其它位置定义。例:comp.repeat(comp)
styles(config)设置样式,参数config为对象例:comp.styles({'color':'red'})
supply(initdata)用于初始化组件数据或传递外部数据提供方法intdata传递给model的构造函数
view (view)设置view类,view可以设置多个,对于第三方组件的使用和扩展特别有用。例:comp.view(class CompView {})

4.组件封装

​ 通过配置方式定义一个带标签的按钮,表达式清晰明确,label和button在html中的排列顺序就是定义的顺序,非常符合直觉的声明式。也可以如下方式对组件进行封装以便于重用。

class MyButton extends Van {
    constructor(id) {
        super(id)

        this.kid('label').tag('span').html('Button')
        this.kid('button').tag('button')
    }
}

5.组件视图模型

​ 组件视图模型用于注册响应事件、渲染组件、管理状态。组件视图允许多个视图组件渲染阶段也可以对内容、样式进行设置,响应数据和状态的变化。组件渲染阶段还可以通过node读取到元素位置、焦点、大小等元素的属性。

视图模型支持的方法方法的功能说明
construct(model, initConfig)视图模型的构造函数,可以传入数据模型,和初始化状态。
create(node, model, states, config)主要用于注册事件,响应事件,对数据模型进行操作。创建组件时调用create方法,该方法仅调用1次。
destroy(node, model, states, config)组件移除前释放资源,一般可以用于移除事件。组件移除前调用destroy方法,该方法仅调用1次。
render(node, model, states, config)组件渲染时对样式、类、属性、文本等进行计算,配置等。组件创建后以及每次更新时均调用该方法。

视图模型中的node是Node的实例,不要与HTML中的Node相混淆。Node的属性和API方法如下:

Node的属性名称Node属性功能说明
componentId组件id
datum组件数据
document获取html dom的document
first获取第一个兄弟Node实例
model获取model的实例
name获取组件名称
holder获取有兄弟的父节点实例
id获取Node实例的id
index获取组件在兄弟Node实例中的索引。
last获取最后一个兄弟Node实例
proxy获取Node实例的事件代理
serial获取holder的索引,即获取具有兄弟的父组件的索引。
sibline获取本实例和兄弟实例的容器
tag获取element的tag名称。
Node的方法Node方法的功能说明
addEventListener(type, listener, options)注册document的事件监听器
attr(key, updateValue)设置element的属性,updateValue可以是值或函数。
blur()设置失去焦点状态,需要调用node.update()后生效。
call(function)调用方法
classed(name, updateValue)设置element的类,value可以是值(true/false)或函数。
emit(eventType, data)派发事件,data为通过事件携带的数据,可以是任意类型
empty()判断node中的element是否为空。
html(updateValue)设置element的html内容,value可以是值或函数。
on(type, listener, options)注册node事件,其中options为true或非空时,则注册为原生事件监听,既dom的传统事件。详见事件部分的内容
parent(name)获取指定名称为name的父Node,未传name时,获得上一层父Node
style(key, updateValue, priority)设置或获取element的html样式,updateValue可以是值或函数。
text(updateValue)设置或获取element的文本内容,value可以是值或函数。
toggle(name)触发设置或取消element指定的类
update()刷新组件,数据或状态变更后调用该函数,重新渲染组件。

注:updateValue的函数形式为:function(datum, index, number),datum为数据,index为数据datum索引,number为数据的总数量

attr、blur、focus、classed、style、text、html方法设置数据或状态后,均需要显示调用node.update()才能更新视图。

6.组件数据模型

​ 组件数据模型用于提供、传递、转换、操作数据等工作,数据模型使用非常自由宽松,对用户约束极少,但与view模型配合,能够轻松的完成各种复杂的任务。

数据模型的方法方法的功能说明
constructor(initdata, config)构造函数,initdata:组件初始化数据,config:组件合并后的配置信息。
supply(parentDatum,parentStates,config)提供、传递、转换数据,parentDatum是父组件实例的数据,parentStates为父组件的状态,也可认为是共享状态。返回数据可以是任意数据,当返回非空数组时,则按照数组长度创建组件个数,空数组则不创建组件,其余情况则创建1个组件。

7.重复组件

​ 通过组件的repeat可以非常方便的定义反复重复的多层结构,根据数据的层次自动渲染出dom的层次结构。例如可以方便的渲染如下的结构:

image-20240103221937619

1)定义组件层次

let app = Van.new('app')

let ul = app.kid('ul').tag('ul').model(UlModel)
let li = ul.kid('li').tag('li')
li.kid('text').tag('span').view(TextView)
li.kid('repeat')
    .repeat(ul)
    .model(RepeatModel)

app.attach(document.querySelector('#app'))

2)视图模型和数据模型

class UlModel {
    supply(){ return data }
}

class TextView {
    render (node, d) {
        node.text(d.name)
    }
}

class RepeatModel {
    supply(d){
        return d?.children ? d.children : []
    }
}

3)准备数据

const data = [
    {
        name:"中国", 
        children:[
            {
                name:"江苏",
                children:[
                    {
                        name:"南京",
                        children:[
                            {name:"句容"},
                            {name:"丹阳"},
                        ]
                    },
                    {name:"扬州"},
                ]
            },
            {
                name:"湖北",
                children:[
                    {name:"武汉"},
                    {name:"黄石"},
                ]
            },
        ]
    }
]

8.事件模型

​ Vandf的事件模型包括原生事件(Raw Event)、节点事件(Node Event)、文档事件(Document Event),原生事件通过本地代理生转换成节点事件向组件传递信息。节点事件在按照组件层次逐级向上冒泡,将信息传递给父组件。文档事件(Document Event)仍以原生形态出现。

image-20240101210638566

9.与纯css库结合使用

​ 本框架与tailwind css框架、bulma css框架天然契合,通过类名即可实现无缝衔接,通过控制class名称来控制渲染的样式,而Vandf可以通过classed来的数据和索引以及node的其它属性信息来灵活控制采用什么class应该被设置或被删除。具体操作参照“设置类名”章节。

10.与d3.js结合使用

​ d3.js是著名的数据驱动的图形库,具有功能强大,使用灵活的特点,但也存在学习曲线陡峭的问题,组件化难度大的特点。通过与Vandf框架的结合使用,可以降低使用难度,易于实现组件化。以下是一个水平树形的构建例子。

image-20240102225609061

  • 引入库文件
import Van from "../core/comp";
import * as d3 from "d3";
  • 定义组件
export default class HTree extends Van {
    constructor(id){
        super(id)

        const svg = this.kid('layer').tag('svg').view(SvgView).model(SvgModel)
        const tree = svg.kid('tree').tag('g').view(TreeView).model(TreeModel)
        tree.kid('link').tag('path').view(LinkView).model(LinkModel)
        const node = tree.kid('node').tag('g').view(NodeView).model(NodeModel)
        node.kid('circle').tag('circle').view(CircleView)
        node.kid('text').tag('text').view(TextView)
    }
}
  • 定义SVG画布的视图和模型
class SvgModel {
    supply () {
        return  d3.hierarchy(data, (d) => d.children);
    }
}


class SvgView {
    constructor(_, config){
        this._states = config || {
            top: 20, right: 90, bottom: 30, left: 90,
            width: 660, height:500
        }
    }
    states(){
        return this._states
    }

    create (node) {
        let s = this._states;
        node.attr("width", s.width)
            .attr("height", s.height)
    }
}
  • 定义Tree的视图和模型
class TreeModel {
    supply (d, s) {
        let height = s.height - s.top - s.bottom;
        let width = s.width - s.left - s.right;
        const treeMap = d3.tree().size([height, width]);        
        const tree =  treeMap(d);
        return {nodes:tree.descendants()}
    }
}

class TreeView {
    constructor(model){
        this.model = model
    }
    states (s) {
        this._config = s;
    }
    create (node) {
        let s = this._config;
        node.attr("transform", "translate(" + s.left + "," + s.top + ")");
    }
}
  • 定义Link连线的视图和模型
class LinkModel {
    supply (d) {
         return d.nodes.slice(1);
    }
}

class LinkView {
    render (node) {
        node.attr("d", (d) => {
                let x = d.x, y = d.y, px = d.parent.x, py = d.parent.y;
                return `M${y},${x},C${(y+py)/2},${x} ${(y+py)/2},${px} ${py},${px}`
            })
            .style("stroke", (d) => d.data.level)
            .style('fill','none')
    }
}
  • 定义Node节点的视图和模型
class NodeModel {
    supply (d) {
        return d.nodes;
    }
}

class NodeView {
    render (node) {
        node.attr(
            "class",
            (d) => "node" + (d.children ? " node--internal" : " node--leaf")
        )
        .attr("transform", (d) => `translate(${d.y},${d.x})`)
    }
}
  • 定义Circle圆节点视图
class CircleView {
    create (node) {
        node.on('click', e=>{
            console.log(node.datum)
        },true)
    }
    render (node) {
        node.attr("r", (d) => d.data.value)
            .style("stroke", (d) => d.data.type)
            .style("fill", (d) => d.data.level)
            .style('stroke-width','3px')
    }
}
  • 定义节点文本Text视图
class TextView {
    render (node) {
        node.attr("dy", ".35em")
            .attr("x", (d) => (d.children ? (d.data.value + 5) * -1 : d.data.value + 5))
            .attr("y", (d) => (d.children && d.depth !== 0 ? -(d.data.value + 5) : d.data.value-10))
            .style("text-anchor", (d) => (d.children ? "end" : "start"))
            .text((d) => d.data.name)
    }
}
  • 使用HTree组件
let app = Van.new('app')
    
HTree.new('htree',app.id)
    .model(HtreeModel)
    .states({
        top: 100, right: 90, bottom: 30, left: 90,
        width: 660, height:500
    })
    .supply(data)

app.attach(document.querySelector('#app'));
  • 准备HTree数据
class HtreeModel {
    constructor(data){
        this._data = data;
    }

    supply () {
        return this._data
    }
}

const data = {
    "name": "1",
    "value": 15,
    "type": "black",
    "level": "yellow",
    "children": [
        {
            "name": "1-2",
            "value": 10,
            "type": "grey",
            "level": "red"
        },
        {
            "name": "1-3",
            "value": 10,
            "type": "grey",
            "level": "red",
            "children": [
                {
                    "name": "1-3-001",
                    "value": 7.5,
                    "type": "grey",
                    "level": "purple"
                },
                {
                    "name": "1-3-002",
                    "value": 7.5,
                    "type": "grey",
                    "level": "purple"
                }
            ]
        },
        {
            "name": "1-4",
            "value": 10,
            "type": "grey",
            "level": "blue"
        },
        {
            "name": "1-5",
            "value": 10,
            "type": "grey",
            "level": "green",
            "children": [
                {
                    "name": "1-5-001",
                    "value": 7.5,
                    "type": "grey",
                    "level": "orange"
                },
                {
                    "name": "1-5-002",
                    "value": 7.5,
                    "type": "grey",
                    "level": "orange"
                }
            ]
        },
        {
            "name": "1-6",
            "value": 10,
            "type": "grey",
            "level": "blue",
            "children": [
                {
                    "name": "1-6-001",
                    "value": 7.5,
                    "type": "grey",
                    "level": "purple"
                },
                {
                    "name": "1-6-002",
                    "value": 7.5,
                    "type": "grey",
                    "level": "purple"
                }
            ]
        }
    ]
}
1.3.7

1 year ago

1.3.6

1 year ago

1.3.5

1 year ago

1.3.4

1 year ago

1.3.3

1 year ago

1.3.2

1 year ago

1.3.1

1 year ago

1.3.0

1 year ago

1.2.35

1 year ago

1.2.36

1 year ago

1.2.34

1 year ago

1.2.33

1 year ago

1.2.29

1 year ago

1.2.30

1 year ago

1.2.31

1 year ago

1.2.32

1 year ago

1.2.27

1 year ago

1.2.28

1 year ago

1.2.25

1 year ago

1.2.26

1 year ago

1.2.23

1 year ago

1.2.24

1 year ago

1.2.21

1 year ago

1.2.22

1 year ago

1.2.18

1 year ago

1.2.19

1 year ago

1.2.20

1 year ago

1.2.8

1 year ago

1.2.7

1 year ago

1.2.6

1 year ago

1.2.5

1 year ago

1.2.4

1 year ago

1.2.3

1 year ago

1.2.2

1 year ago

1.2.1

1 year ago

1.2.9

1 year ago

1.2.12

1 year ago

1.2.13

1 year ago

1.2.10

1 year ago

1.2.11

1 year ago

1.2.16

1 year ago

1.2.17

1 year ago

1.2.14

1 year ago

1.2.15

1 year ago

1.2.0

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.1.4

1 year ago

1.1.3

1 year ago

1.1.2

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago