# begonia **Repository Path**: begoniajs/begonia ## Basic Information - **Project Name**: begonia - **Description**: begonia是一个为微信小程序开发而建立的开源、简单和轻量的开发框架。 - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-09-16 - **Last Updated**: 2021-12-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Begonia 介绍 [begonia](https://github.com/begoniajs/begonia/tree/dev-1.x)是一个为微信小程序开发而建立的开源、简单和轻量的开发框架。 具有如下特性: - 基于代理模式构建小程序App实例、Page实例和Component实例 - 提供属性延迟生效的功能,减少多次调用setData;通过延迟时间在宏观上控制setData提交的频次和数量。 - 自动将Page实例的`data`和Component实例的`data`中的属性转换为同名的`getter`和`setter`(自动提交属性变动),简化使用方式。 - 提供增强模块管理,可以基于begonia扩展功能。并借由模块生命周期细粒度的控制创建小程序Page实例和Component实例的过程。 - 支持npm安装,并符合小程序npm开发部署要求。 ## 微信小程序基础库版本 ``` ^2.2.1 ``` ## 快速使用 ### 1. 安装 请在小程序项目的根目录下(即 `project.config.js` 中的 `miniprogramRoot` 字段)执行`npm`安装: ``` $ npm install begonia ``` 接下来,我们需要做的,就是使用微信开发者工具中的工具->构建npm命令,对已经安装好的npm模块进行构建。 关于如何使用微信开发者工具构建npm模块,你可以参考[官方文档](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html) ### 2. 导入模块 在小程序的`app.js`、页面或者自定义组件实例中,导入入口文件: ```js import BE from 'begonia'; ``` ### 3. 创建实例 然后,就可以通过变量`BE`来使用套件的具体功能: #### 创建根级`App`实例 ```js import BE, { APP } from 'begonia'; // 打开debug模式,可以在console面板查看运行时的日志输出 BE.debug = true; // app.js BE({ // 将转化为globalData data() { return {}; }, onLaunch(options) {}, onShow(options) {}, onHide() {}, onError(msg) { console.error(msg); }, onPageNotFound() { console.error(msg); }, methods: { // 自定义方法 myInit() { this.foo(); }, setUIDatas() { this.bar(); } }, methodAres: [ { // 自定义方法分组 foo() {}, bar() {} } ] }, ['myInit', 'setUIDatas'])(APP); // 最后使用app方式渲染 ``` #### 创建页面对象 ```js // /pages/example/example.js import BE, { PAGE } from 'begonia'; BE({ /** * 页面的初始数据 */ data(){ return { userId: 0 }; }, /** * 生命周期函数--监听页面加载 * 相当于onLoad */ created(options) { // 使用begonia为实例提供的commit()方法提交属性变动 this.commit('userId', 10); // 另外一种提交形式 this.commit({ userId: 12 }); // 甚至,还可以这样 this.userId = 14; }, /** * 生命周期函数--监听页面初次渲染完成 * 相当于onReady */ ready () { }, /** * 生命周期函数--监听页面显示 */ onShow () { }, /** * 生命周期函数--监听页面隐藏 */ onHide () { }, /** * 生命周期函数--监听页面卸载 * 相当于onUnload */ destroyed () { }, // 自定义方法 methods: { pageInit() { this.foo(); }, setUIData() { this.bar(); } }, // 函数分组 methodAreas: [ { foo() {}, bar() {} } ] }, ['pageInit', 'setUIData'])(PAGE); ``` 在`created`方法中,有3次属性的提交,但是最终会真正采用`setData()`改变的只有第三次的提交,框架在内部会舍去前两次的重复提交。 如果您想在提交一次属性变动之后,立即使其生效。可以使用如下形式: ```js //... this.commit('userId', 10); this.validateNow(); //... ``` 通过调用框架赋予实例的`validateNow()`方法,会使属性变动立即生效,其本质和直接使用`setData`没有区别。当然,为了避免频繁使用`setData`提交变动,请谨慎使用`validateNow`。确保只在必要时使用它。 #### 创建组件实例 ```js import BE, { COMPONENT } from 'begonia'; BE({ /** * 组件的属性列表 */ properties: { sysId: { type: Number, value: 0 } }, /** * 组件的初始数据 */ data(){ return { userId: 0 }; }, // 相当于lifetimes.created created() { // 根据小程序组件的内部机制,不可以在此提交属性变动 }, // 相当于lifetimes.attached mounted() { // 使用begonia为实例提供的commit()方法提交属性变动 this.commit('userId', 10); // 另外一种提交形式 this.commit({ userId: 12 }); // 甚至,还可以这样 this.userId = 14; }, // 相当于lifetimes.ready ready() {}, // 相当于lifetimes.detached destroyed() {}, // 相当于lifetimes.error error(err) {}, // 相当于pageLifetimes.show pageShow() {}, // 相当于pageLifetimes.hide pageHide() {}, // 相当于pageLifetimes.resize pageResize() {}, /** * 组件的方法列表 */ methods: { comInit() { this.foo(); } setUIDatas() { this.bar(); } }, methodAreas: [ { foo() {}, bar() {} } ] }, ['comInit', 'setUIDatas'])(COMPONENT); ``` ### 编排方法队列 你可能注意到上面的例子在使用`BE()`时传入了第二个参数,是一个元素为字符串的数组。 这是一个自定义的函数执行队列,可以编排方法的执行顺序。实际则是在特定的方法执行时(App是在`onLaunch`执行时,Page在`onLoad`执行时,Component是在`lifetimes.attached`执行时)。 假设,我们在编码时,会这样调用函数: ```js Page({ onLoad() { this.foo(); this.bar(); }, foo() { }, bar() { } }); ``` 当我们利用`BE()`的第二个参数时,却可以这样: ```js BE({ created() { }, methods: { foo() { }, bar() { } } }, ['foo', 'bar'])(PAGE); ``` 其本质等同于第一个例子中调用`foo()`和`bar()`的方式。 #### 考虑异步 普通的数组所形成的方法队列只能适用于同步的方式,而对于串行异步执行的方法则不能保证正确的顺序。 此时,我们可以使用`begonia`提供的异步数组包装器来编排串行异步执行函数队列: ```js import BE, { PAGE, BEAsyncList } from 'begonia'; BE( { created() { }, methods: { foo({ next, error, end }, ...args) { post('/api/foo', {}) .then(function(res) { next(res); }); .catch(function(err) { console.error(err); error(err); // or end() }); }, bar({ next, error, end }, ...args) { console.log(args[0]); // 等于foo函数传入next()的res // 执行同步逻辑或异步逻辑 end(); // 队列执行到此为止 // 或者 next(),执行下一步,如果bar是队列的最后一个,那么等同于end() } } }, BEAsyncList( ['foo', 'bar'], function(err, ...args) { // Be invoked when async-list executed complete } ) )(PAGE); ``` #### 使用序列的目的 当你的方法函数,纯度很高(不需要绝对),只专注于完成一项功能时,预定义的函数执行序列,可以更清楚的标明函数的执行顺序。 此外,数组的元素也不仅限于字符串。这里使用字符串的意思是,指明方法和函数时挂载在`this`上的。 如果是一个单独的函数,也可以直接传入函数对象: ```js import BE, { PAGE, BEAsyncList } from 'begonia'; function baz() { console.log('other function', this); // this 为page的实例 } BE({ created() { }, methods: { foo() { }, bar() { } } }, ['foo', 'bar', baz])(PAGE); ``` 当`baz`被调用时,函数的`this`变量将会指向页面的实例。 ## 其他功能 ### 使用默认带有的延迟属性变更的功能 小程序开发中,如果想要修改实例的`data`对象中属性,必须使用`setData()`方法。 begonia框架内部使用`ViewModelProxy`(`VMP`)提供了延迟属性更改,从而可以收集一段时间内的属性变动, 在规定的更新间隔到来时,一次性的提交属性变动。 在实例创建的过程中,框架会为每个实例附加2个方法`commit()`和`validateNow()`,用来提交属性变动。 此外,对于页面实例和组件实例的`data`,框架也为实例创建了同名属性的`getter`和`setter`方法,用于获取属性值和提交属性值。 其中`setter`方法的内部,也采用了延迟生效的机制(即与使用`commit()`情况类似)。 ##### 形式1:提交一项属性更改: ```js this.commit('groupList',[{ id:'1800', name:'一年级B班', }]); ``` ##### 形式2:直接传入一个对象: ```js this.commit({ groupList:[{ id:'1800', name:'一年级B班', }], }); ``` ##### 形式3:直接为属性赋值(本质是利用setter间接提交): ```js this.groupList = [{ id:'1800', name:'一年级B班', }]; ``` #### 更新间隔到期,属性变动生效 默认设置是`100`毫秒更新间隔,当间隔到期,VMP内部会使用`setData()`方法,将搜集到的属性改动一次性提交微信小程序框架进行计算生效。 某些情况下,也许你想将提交的属性立即生效,可以使用 ```js this.validateNow(); ``` 这样,到当前时间为止,所有提交的属性变更将会立即生效。VMP会清空缓存的对象,等待下一轮时间间隔内提交的属性变动。 延迟更新的时间间隔是可以改变的,首先使用`BE`的方法`getModules`获取已经装载模块的映射,然后调用`vmp`模块的`interval`即可,方法接受安全的、非零自然数作为值。 这种方式较`1.x`有了改变,原因是内部将`VMP`模块隔离为一个更加独立的模块进行装载,减少了`1.x`中的耦合部分。 ```js import BE from 'begonia'; //... BE.getModules.vmp.interval = 200; //设置200毫秒间隔 ``` ### 销毁并回收 当页面发生切换,页面实例和组件实例会被微信小程序框架销毁,此时`onUnload`方法会被触发。 在钩子函数中,begonia会**自动清理**附加在实例对象上的内部属性和方法,释放内部的代理对象。 ## 使用增强模块 begonia遵循模块化原则,提供的各项功能均保持相对独立的状态。包括内部使用的代理对象和延迟提交模块`VMP`也是独立的。 只不过,框架在建立之初就进行了自动装载。 `begonia`提供的装载模块的方法非常简单: ```js BE.use(MyModule); ``` 例如,我们也提供了一个简版的`redux`模块,您可以首先在一个独立的名为`store.js`的文件中,编写`store`相关的代码: ```js // store.js //导入begonia import BE from 'begonia'; //导入beleaf模块 import Bex from 'beleaf'; //=====>>>装载bex模块<<<========= BE.use(Bex); ``` 完成之后,在小程序页面或组件实例中: ```js Page(BE.page({ //... onLoad: function () { //访问store实例 let store = this.$store; //查看state状态树 let state = this.$store.state; //通过getters对象访问具体的state属性 let list = this.$getters.groupList; //发起一个action,引起状态变更 this.$actions.getGroupList('10'); }, //... })); ``` 更多关于`beleaf`的使用细节,您可以访问模块的[GitHub项目仓库](https://github.com/begoniajs/begonia-leaf),详细了解。 ## 说明文档和开发文档 诚如所见,为方便使用,`begonia`和`beleaf`在API的名称设计上借鉴了很多[vue框架](https://cn.vuejs.org/)的API名称, 不过内部实现和实际使用还是不同的。并且从整体上来说,begonia也远不如vue框架及其衍生的众多代码库的功能强大,限于其使用的场景,够用即好。 如果想要具体了解各种使用方法,可以查看如下文档: - [创建小程序页面和组件对象](https://github.com/begoniajs/begonia/tree/master/doc) - [VMP代理对象](https://github.com/begoniajs/begonia/tree/master/doc) - [开发自定义模块](https://github.com/begoniajs/begonia/blob/dev-1.x/doc/%E5%BC%80%E5%8F%91%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A2%9E%E5%BC%BA%E6%A8%A1%E5%9D%97.md) - [API](https://github.com/begoniajs/begonia/tree/master/doc/api) ## 初衷 begonia框架是适用于小程序开发的框架,其所追求的也仅仅是提高小程序开发的效率和改善。 更多的意义在于借由对其他框架优点的认识,自行实现,进而达到研究原理的目的。 也许以后的发展中会追求多种小程序平台开发的统一,但绝不会努力实现在多种设备终端开发中的统一。 # 开源协议 MIT ## 关于名称 秋海棠(学名:`Begonia` grandis Dry):秋海棠科秋海棠属多年生草本植物。根状茎近球形,茎直立,高可达60厘米,有纵棱无毛。茎生叶互生,叶片轮廓宽卵形至卵形两侧不相等,上面褐绿色,常有红晕,下面色淡,带紫红色,托叶长圆形至披针形膜质,花葶有纵棱,无毛;花较多数粉红色,苞片长圆形,先端钝,早落;花药倒卵球形,子房长圆形,蒴果下垂,轮廓长圆形,种子长圆形小,淡褐色数极多,7月开花,8月开始结果。 分布中国河北、河南、湖北、福建等地。有栽培。生山谷潮湿石壁上、密林、灌丛中,该种花、叶、茎、根均可入药。花形多姿,叶色柔媚。盆栽秋海棠常用来点缀客厅、橱窗或装点家庭窗台、阳台、茶几等地方。