raab-js v1.1.1
Raab-js
简单的前端响应式框架实现(我更愿意称之为伪响应式
,因为操作响应数据需要使用特定暴露出的api进行,而不是传统意义上直接操作数据,不过这样做的好处是可以更加精准且减少了代理数据的额外开销)
另外RaabJS内置了路由管理,可以方便的开发单页面应用
安装
npm install raab-js
快速实践
// main.ts
import { App, creatApp, RouteNextFunctionType, RouteType } from 'raab-js'
import MainPage from './mainPage'
class app extends App {
handleRoute(): RouteType[] {
return [
{ path: '/', page: new MainPage },
]
}
RouteMiddle(to: string, from: string, next: RouteNextFunctionType): void {
next()
}
}
creatApp(app)
// mainPage.ts
import { BasePage, c, cVal } from 'raab-js'
class MainPage extends BasePage {
context = rVal<string>('Hello, World!')
setup() {
return (
c('h1', {$text: this.context.bindPath(), style: 'color: red;'})
)
}
enter() {
return true
}
leave() {
return true
}
}
export default MainPage
文档
在RaabJS里,所有html元素需要被封装成组件和页面,页面可以包含许多组件,且每一个页面需要有一个路由与其对应。
响应式
RaabJS响应式支持单一数据与数组数据,且数组数据依赖于单一数据
rVal() 单一数据
rVal函数,需要传入相应数据,并支持泛型,与vue的ref相似的是,rVal读取与赋值时需要访问.value属性,另外rVal绑定元素需要调用bindPath方法,如:
const text = rVal<boolean>(true) // 读取 text.value // 赋值 text.value = false // 绑定元素 c('div', {$text: text.bindPath()})
rList() 数组数据
rList函数,需要传入相应数组,支持泛型,且数组中的响应数据需要包裹rVal函数,另外绑定元素时需要调用bindPath方法并传入数据路径,如:
const list = rList([ {text1: rVal('这是第一个数据')}, {text2: rVal('这是第二个数据')}, {text3: rVal('这是第三个数据')}, ]) // 对应 这是第一个数据 c('div', {$text: list.bindPath('0/text1')}) // 对应 这是第二个数据 c('div', {$text: list.bindPath('1/text2')}) // 对应 这是第三个数据 c('div', {$text: list.bindPath('2/text3')}
rList具体操作数组方法如下:
forEach(fn: rListForEachFunctionType<T>): void
循环数组
append(list: T[]): void
追加数组,且需要注意的是必须传入数组而不是单一数据
insert(index: number, val: T): void
插入数组,index指定下标,val数据
delete(index: number): void
删除数据,index指定下标
setVal(path: string, val: T): void
设置数据,path为数据路径,val数据
getVal(path: string): any
获取指定路径数据
模板
用于构建html元素,RaabJS中为c,cFor方法,其中c方法接收三个参数为元素名称,元素属性,以及子元素,如:
c('div', {}, [
c('h1', {$text: 'test'}),
])
其中带$的参数为特殊属性,在函数执行时会进行特殊处理,一般用作事件绑定
- $text:元素文本
- $click:绑定点击事件
.........
cFor方法用于循环创建多个元素,其接收两个参数为响应式数组(类型为rListObject,且需要响应的数据必须包裹rVal方法),回调函数,函数会传入index为数组下标已方便绑定数据,如:
const list = rList([
{c: rVal('123')},
{c: rVal('456')},
{c: rVal('789')},
{c: rVal('012')},
])
c('div', {}, cFor(list, index => {
return (
c('div', {}, [
c('div', {$text: list.bindPath(`${index}/c`)}),
c('button', {$text: 'delete', $click: (e, i)=>list.delete(i)})
])
)
}))
需要注意的是,回调函数的结果需要进行返回,另外在cFor内部绑定事件函数会被传入两个参数反别是Event与index,其中index为绑定的对应数组下标...
Init
首先,启动一个Raab app需要创建类并继承App基类,实现handleRoute方法与RouteMiddle方法,其中handleRoute需要返回RouteType[]类型结构,具体信息如下
type RouteType = {
path: string // 路由路径(支持动态路由,需要在路由参数名前加:,如:id,匹配到后会在页面的enter方法传入相关数据)
page: BasePage // 页面类实例
children?: RouteType[] // 子路由信息
}
RouteMiddle路由中间件控制路由转跳,传入三个参数to
路由去向,from
路由来自,next
中间方法,next方法必须放置在函数内,否则无法执行路由转跳,且next支持重定向,需要传入指定路由如:
next('/test')
另外关于子路由显示的问题,需要在父路由页面引入Route类并新建实例充当容器以此来展示子路由,如:
import { BasePage, c, Route, router } from 'raab-js'
// router为路由方法,其中changeRoute改变路由不添加history历史,toRoute为前往目标路由并添加历史
class MainPage extends BasePage {
setup() {
return (
c('div', {}, [
c('div', {style: 'border: red solid 1px'}, [
// 展示子路由信息
new Route,
]),
c('button', {$text: 'AAA', $click: ()=>router.changeRoute('/aaa')}),
c('button', {$text: 'BBB', $click: ()=>router.changeRoute('/bbb')}),
])
)
}
enter() {
return true
}
leave() {
return true
}
}
export default MainPage
页面
书写页面需要创建类并继承BasePage基类,并实现setup(),enter(),leave()方法,其中setup返回html模板,需要使用c或cFor方法,enter与leave为页面钩子函数,是抽象方法所有必须实现,一般enter会传入页面进入后的相关参数,且需要返回布尔类型或者字符串,true为允许进入或离开此页面,反之亦然,也可以进行重定向如:
enter(p: any) {
return '/test'
}
组件
书写组件需要创建类并继承BaseComponent基类,并实现setup(),Created(),Destroy()方法,其中Created(),Destroy()为组件生命周期钩子函数,Created为组件被挂载到页面后,Destroy为组件将卸载时(由于目前元素被创建在页面后只实现了隐藏显示并不会真正卸载出dom树,所有Destroy方法暂时无效)
import { BaseComponent, c } from 'raab-js'
class Com extends BaseComponent {
setup() {
return (
c('h1', {$text: 'Hello, Rab!!!', style: 'color: red;'})
)
}
constructor() {
super()
}
Created() {
}
Destroy() {
}
}
export default Com