摘要: 小程序框架...

Fundebug经授权转载,版权归原作者所有。

官网:beautywejs.com

Repo: beautywe

一个简单的介绍

BeautyWe.js 是什么?

它是一套专注于微信小程序的企业级开发范式,它的愿景是:

让企业级的微信小程序项目中的代码,更加简单、漂亮。

为什么要这样命名呢?

Write beautiful code for wechat mini program by the beautiful we!

「We」 既是我们的 We,也是微信的 We,Both beautiful!

那么它有什么卖点呢?

  1. 专注于微信小程序环境,写原汁原味的微信小程序代码。
  2. 由于只专注于微信小程序,它的源码也很简单。
  3. 插件化的编程方式,让复杂逻辑更容易封装。
  4. 再加上一些配套设施:
    1. 一些官方插件。
    2. 一套开箱即用,包含了工程化、项目规范以及微信小程序环境独特问题解决方案的框架。
    3. 一个CLI工具,帮你快速创建应用,页面,组件等。

它由以下几部分组成:

  • 一个插件化的核心 - BeautyWe Core

    对 App、Page 进行抽象和包装,保持传统微信小程序开发姿势,同时开放部分原生能力,让其具有「可插件化」的能力。
  • 一些官方插件BeautyWe Plugins

    得益于 Core 的「可插件化」特性,封装复杂逻辑,实现可插拔。官方对于常见的需求提供了一些插件:如增强存储、发布/订阅、状态机、Logger、缓存策略等。
  • 一套开箱即用的项目框架 - BeautyWe Framework

    描述了一种项目的组织形式,开箱即用,集成了 BeautyWe Core ,并且提供了如:全局窗口、开发规范、多环境开发、全局配置、NPM 等解决方案。
  • 一个CLI工具 - BeautyWe Cli

    提供快速创建应用、页面、插件,以及项目构建功能的命令行工具。并且还支持自定义的创建模板。

一个简单的例子

下载

用 BeautyWe 包装你的应用

之后,你就能使用 BeautyWe Plugin 提供的能力了。

开放原生App/Page,支持插件化

new BtApp({...}) 的执行结果是对原生的应用进行包装,其中包含了「插件化」的处理,然后返回一个新的实例,这个实例适配原生的 App() 方法。

下面来讲讲「插件化」到底做了什么事情。

首先,插件化开放了原生 App 的四种能力:

  1. Data 域

    把插件的 Data 域合并到原生 App 的 Data 域中,这一块很容易理解。
  2. 原生钩子函数

    使原生钩子函数(如 onShow, onLoad)可插件化。让原生App与多个插件可以同时监听同一个钩子函数。如何工作的,下面会细说。
  3. 事件钩子函数

    使事件钩子函数(与 view 层交互的钩子函数),尽管在实现上有一些差异,但是实现原理跟「原生钩子函数」一样的。
  4. 自定义方法

    让插件能够给使用者提供 API。为了保证插件提供的 API 足够的优雅,支持当调用插件 API 的时候(如 event 插件 this.event.on(...)),API 方法内部仍然能通过 this 获取到原生实例。

钩子函数的插件化

原生钩子函数,事件钩子函数我们统一称为「钩子函数」。

对于每一个钩子函数,内部是维护一个以 Series Promise 方式执行的执行队列。

onShow 为例,将会以这样的形式执行:

native.onShow → pluginA.onShow → pluginB.onShow → ...

下面深入一下插件化的原理

工作原理是这样的:

  1. 经过 new BtApp(...) 包装,所有的钩子函数,都会有一个独立的执行队列,
  2. 首先会把原生的各个钩子函数 push 到对应的队列中。然后每 use 插件的时候,都会分解插件的钩子函数,往对应的队列 push
  3. Native App(原生)触发某个钩子的时候,BtApp 会以 Promise Series 的形式按循序执行对应队列里面的函数。
  4. 特殊的,onLaunchonLoad 的执行队列中,会在队列顶部插入一个初始化的任务(initialize),它会以同步的方式按循序执行 Initialize Queue 里面的函数。这正是插件生命周期函数中的 plugin.initialize

这种设计能提供以下功能:

  1. 可插件化。

    只需要往对应钩子函数的事件队列中插入任务。
  2. 支持异步。

    由于是以 Promise Series 方式运行的,其中一个任务返回一个 Promise,下一个任务会等待这个任务完成再开始。如果发生错误,会流转到原生的 onError() 中。
  3. 解决了微信小程序 app.jsgetApp() === undefinded 问题。

    造成这个问题,本质是因为 App() 的时候,原生实例未创建。但是由于 Promise 在 event loop 中是一个微任务,被注册在下一次循环。所以 Promise 执行的时候 App() 早已经完成了。

一些官方插件

BeautyWe 官方提供了一系列的插件:

  1. 增强存储: Storage
  2. 数据列表:List Page
  3. 缓存策略:Cache
  4. 日志:Logger
  5. 事件发布/订阅:Event
  6. 状态机:Status

它们的使用很简单,哪里需要插哪里。

由于篇幅的原因,下面挑几个比较有趣的来讲讲,更多的可以看看官方文档:BeautyWe

增强存储 Storage

该功能由 @beautywe/plugin-storage 提供。

由于微信小程序原生的数据存储生命周期跟小程序本身一致,即除用户主动删除或超过一定时间被自动清理,否则数据都一直可用。

所以该插件在 wx.getStorage/setStorage 的基础上,提供了两种扩展能力:

  1. 过期控制
  2. 版本隔离

一些简单的例子

安装

import { BtApp } from '@beautywe/core';
import storage from '@beautywe/plugin-storage'; const app = new BtApp();
app.use(storage());

过期控制

// 7天后过期
app.storage.set('name', 'jc', { expire: 7 });

版本隔离

app.use({ appVersion: '0.0.1' });
app.set('name', 'jc'); // 返回 jc
app.get('name'); // 当版本更新后
app.use({ appVersion: '0.0.2' }); // 返回 undefined;
app.get('name');

更多的查看 @beautywe/plugin-storage 官方文档

数据列表 List Page

对于十分常见的数据列表分页的业务场景,@beautywe/plugin-listpage 提供了一套打包方案:

  1. 满足常用「数据列表分页」的业务场景
  2. 支持分页
  3. 支持多个数据列表
  4. 自动捕捉下拉重载:onPullDownRefresh
  5. 自动捕捉上拉加载:onReachBottom
  6. 自带请求锁,防止帕金森氏手抖用户
  7. 简单优雅的 API

一个简单的例子:

import BeautyWe from '@beautywe/core';
import listpage from '@beautywe/plugin-listpage'; const page = new BeautyWe.BtPage(); // 使用 listpage 插件
page.use(listpage({
lists: [{
name: 'goods', // 数据名
pageSize: 20, // 每页多少条数据,默认 10 // 每一页的数据源,没次加载页面时,会调用函数,然后取返回的数据。
fetchPageData({ pageNo, pageSize }) { // 获取数据
return API.getGoodsList({ pageNo, pageSize }) // 有时候,需要对服务器的数据进行处理,dataCooker 是你定义的函数。
.then((rawData) => dataCooker(rawData));
},
}],
enabledPullDownRefresh: true, // 开启下拉重载, 默认 false
enabledReachBottom: true, // 开启上拉加载, 默认 false
})); // goods 数据会被加载到,goods 为上面定义的 name
// this.data.listPage.goods = {
// data: [...], // 视图层,通过该字段来获取具体的数据
// hasMore: true, // 视图层,通过该字段来识别是否有下一页
// currentPage: 1, // 视图层,通过该字段来识别当前第几页
// totalPage: undefined,
// }

只需要告诉 listpage 如何获取数据,它会自动处理「下拉重载」、「上拉翻页」的操作,然后把数据更新到 this.data.listPage.goods 下。

View 层只需要描述数据怎么展示:

<view class="good" wx:for="listPage.goods.data">
...
</view>
<view class="no-more" wx:if="listPage.goods.hasMore === false">
没有更多了
</view>

listpage 还支持多数据列表等其他更多配置,详情看:@beautywe/plugin-listpage

缓存策略 Cache

@beautywe/plugin-cache 提供了一个微信小程序端缓存策略,其底层由 super-cache 提供支持。

特性
  1. 提供一套「服务端接口耗时慢,但加载性能要求高」场景的解决方案
  2. 满足最基本的缓存需求,读取(get)和保存(set)
  3. 支持针对缓存进行逻辑代理
  4. 灵活可配置的数据存储方式
How it work

一般的请求数据的形式是,页面加载的时候,从服务端获取数据,然后等待数据返回之后,进行页面渲染:

但这种模式,会受到服务端接口耗时,网络环境等因素影响到加载性能。

对于加载性能要求高的页面(如首页),一般的 Web 开发我们有很多解决方案(如服务端渲染,服务端缓存,SSR 等)。

但是也有一些环境不能使用这种技术(如微信小程序)。

Super Cache 提供了一个中间数据缓存的解决方案:

思路:

  1. 当你需要获取一个数据的时候,如果有缓存,先把旧的数据给你。
  2. 然后再从服务端获取新的数据,刷新缓存。
  3. 如果一开始没有缓存,则请求服务端数据,再把数据返回。
  4. 下一次请求缓存,从第一步开始。

这种解决方案,舍弃了一点数据的实时性(非第一次请求,只能获取上一次最新数据),大大提高了前端的加载性能。

适合的场景:

  1. 数据实时性要求不高。
  2. 服务端接口耗时长。
使用
import { BtApp } from '@beautywe/core';
import cache from '@beautywe/plugin-cache'; const app = new BtApp();
app.use(cache({
adapters: [{
key: 'name',
data() {
return API.fetch('xxx/name');
}
}]
}));

假设 API.fetch('xxx/name') 是请求服务器接口,返回数据:data_from_server

那么:

app.cache.get('name').then((value) => {
// value: 'data_from_server'
});

更多的配置,详情看:@beautywe/plugin-cache

日志 Logger

@beautywe/logger-plugin 提供的一个轻量的日志处理方案,它支持:

  1. 可控的 log level
  2. 自定义前缀
  3. 日志统一处理
使用
import { BtApp } from '@beautywe/core';
import logger from '@beautywe/plugin-logger'; const page = new BtApp(); page.use(logger({
// options
}));

API

page.logger.info('this is info');
page.logger.warn('this is warn');
page.logger.error('this is error');
page.logger.debug('this is debug'); // 输出
// [info] this is info
// [warn] this is warn
// [error] this is error
// [debug] this is debug

Level control

可通过配置来控制哪些 level 该打印:

page.use(logger({
level: 'warn',
}));

那么 warn 以上的 log (info, debug)就不会被打印,这种满足于开发和生成环境对 log 的不同需求。

level 等级如下:

Logger.LEVEL = {
error: 1,
warn: 2,
info: 3,
debug: 4,
};

更多的配置,详情看:@beautywe/plugin-logger

BeautyWe Framework

@beautywe/core@beautywe/plugin-... 给小程序提供了:

  1. 开放原生,支持插件化 —— by core
  2. 各种插件 —— by plugins

但是,还有很多的开发中实际还会遇到的痛点,是上面两个解决不到的。

如项目的组织、规范、工程化、配置、多环境等等

这些就是,「BeautyWe Framework」要解决的范畴。

它作为一套开箱即用的项目框架,提供了这些功能:

  • 集成 BeautyWe Core
  • NPM 支持
  • 全局窗口
  • 全局 Page,Component
  • 全局配置文件
  • 多环境开发
  • Example Pages
  • 正常项目需要的标配:ES2015+,sass,uglify,watch 等
  • 以及我们认为良好的项目规范(eslint,commit log,目录结构等)

也是由于篇幅原因,挑几个有趣的来讲讲,更多的可以看看官方文档:BeautyWe

快速创建

首先安装 @beautywe/cli

$ npm i @beautywe/cli -g
创建应用
$ beautywe new app

> appName: my-app
> version: 0.0.1
> appid: 123456
> 这样可以么:
> {
> "appName": "my-app",
> "version": "0.0.1",
> "appid": "123456"
> }

回答几个问题之后,项目就生成了:

my-app
├── gulpfile.js
├── package.json
└── src
├── app.js
├── app.json
├── app.scss
├── assets
├── components
├── config
├── examples
├── libs
├── npm
├── pages
└── project.config.json
创建页面、组件、插件

页面

  1. 主包页面:beautywe new page <path|name>
  2. 分包页面:beautywe new page --subpkg <subPackageName> <path|name>

组件

  1. beautywe new component <name>

插件

  1. beautywe new plugin <name>
自定义模板

./.templates 目录中,存放着快速创建命令的创建模板:

$ tree .templates

.templates
├── component
│ ├── index.js
│ ├── index.json
│ ├── index.scss
│ └── index.wxml
├── page
│ ├── index.js
│ ├── index.json
│ ├── index.scss
│ └── index.wxml
└── plugin
└── index.js

可以修改里面的模板,来满足项目级别的自定义模板创建。

全局窗口

我们都知道微信小程序是「单窗口」的交互平台,一个页面对应一个窗口。

而在业务开发中,往往会有诸如这种述求:

  1. 自定义的 toast 样式
  2. 页面底部 copyright
  3. 全局的 loading 样式
  4. 全局的悬浮控件

......

稍微不优雅的实现可以是分别做成独立的组件,然后每一个页面都引入进来。

这种做法,我们会有很多的重复代码,并且每次新建页面,都要引入一遍,后期维护也会很繁琐。

而「全局窗口」的概念是:希望所有页面之上有一块地方,全局性的逻辑和交互,可以往里面搁。

global-view 组件

这是一个自定义组件,源码在 /src/components/global-view

每个页面的 wxml 只需要在顶层包一层:

<global-view id="global-view">
...
</global-view>

需要全局实现的交互、样式、组件,只需要维护这个组件就足够了。

全局配置文件

src/config/ 目录中,可以存放各种全局的配置文件,并且支持以 Node.js 的方式运行。(得益于 Node.js Power 特性)。

src/config/logger.js:

const env = process.env.RUN_ENV || 'dev';

const logger = Object.assign({
prefix: 'BeautyWe',
level: 'debug',
}, {
// 开发环境的配置
dev: {
level: 'debug',
},
// 测试环境的配置
test: {
level: 'info',
},
// 线上环境的配置
prod: {
level: 'warn',
},
}[env] || {}); module.exports.logger = logger;

然后我们可以这样读取到 config 内容:

import { logger } from '/config/index';

// logger.level 会根据环境不同而不同。

Beautywe Framework 默认会把 config 集成到 getApp() 的示例中:

getApp().config;

多环境开发

BeautyWe Framework 支持多环境开发,其中预设了三套策略:

  • dev
  • test
  • prod

我们可以通过命令来运行这三个构建策略:

beautywe run dev
beautywe run test
beautywe run prod

三套环境的差异

Beautywe Framework 源码默认在两方面使用了多环境:

  • 构建任务(gulpfile.js/env/...
  • 全局配置(src/config/...
构建任务的差异
构建任务 说明 dev test prod
clean 清除dist文件
copy 复制资源文件
scripts 编译JS文件
sass 编译scss文件
npm 编译npm文件
nodejs-power 编译Node.js文件
watch 监听文件修改
scripts-min 压缩JS文件
sass-min 压缩scss文件
npm-min 压缩npm文件
image-min 压缩图片文件
clean-example 清除示例页面
Node.js Power

Beautywe Framework 的代码有两种运行环境:

  1. Node.js 运行环境,如构建任务等。
  2. 微信小程序运行环境,如打包到 dist 文件夹的代码。

运行过程

Node.js Power 本质是一种静态编译的实现。

把某个文件在 Node.js 环境运行的结果,输出到微信小程序运行环境中,以此来满足特定的需求。

Node.js Power 会把项目中 src 目录下类似 xxx.nodepower.js 命名的文件,以 Node.js 来运行,

然后把运行的结果,以「字面量对象」的形式写到 dist 目录下对应的同名文件 xxx.nodepower.js 文件去。

src/config/index.nodepower.js 为例:

const fs = require('fs');
const path = require('path'); const files = fs.readdirSync(path.join(__dirname)); const result = {}; files
.filter(name => name !== 'index.js')
.forEach((name) => {
Object.assign(result, require(path.join(__dirname, `./${name}`)));
}); module.exports = result;

该文件,经过 Node.js Power 构建之后:

dist/config/index.nodepower.js:

module.exports = {
"appInfo": {
"version": "0.0.1",
"env": "test",
"appid": "wx85fc0d03fb0b224d",
"name": "beautywe-framework-test-app"
},
"logger": {
"prefix": "BeautyWe",
"level": "info"
}
};

这就满足了,随意往 src/config/ 目录中扩展配置文件,都能被自动打包。

Node.js Power 已经被集成到多环境开发的 dev, test, prod 中去。

当然,你可以手动运行这个构建任务:

$ gulp nodejs-power
NPM

BeautyWe Framework 实现支持 npm 的原理很简单,总结一句话:

使用 webpack 打包 src/npm/index.js ,以 commonjs 格式输出到 dist/npm/index.js

这样做的好处:

  1. 实现简单。
  2. 让 npm 包能集中管理,每次引入依赖,都好好的想一下,避免泛滥(尤其在多人开发中)。
  3. 使用 ll dist/npm/index.js 命令能快速看到项目中的 npm 包使占了多少容量。

新增 npm 依赖

src/npm/index.js 文件中,进行 export:

export { default as beautywe } from '@beautywe/core';

然后在其他文件 import:

import { beautywe } from './npm/index';

更多

总的来说,BeautyWe 是一套微信小程序的开发范式。

coreplugins 扩展原生,提供复杂逻辑的封装和插拔式使用。

framework 则负责提供一整套针对于微信小程序的企业级项目解决方案,开箱即用。

其中还有更多的内容,欢迎浏览官网:beautywejs.com

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用!

BeautyWe.js 一套专注于微信小程序的开发范式的更多相关文章

  1. 微信小程序从零开始开发步骤(六)4种页面跳转的方法

    用法:用于页面跳转,相当于html里面的<a></a>标签. API教程:https://mp.weixin.qq.com/debug/wxadoc/dev/component ...

  2. 微信小程序从零开始开发步骤(七)引入外部js 文件

    上一章讲到小程序页面的四种常见的跳转的方法,这一章写如何引入一个外部的js文件,既utils文件夹的用处,其实步骤很简单: 1:准备好外部想要引入的外部文件,命名为util.js,并且填充固定的文件内 ...

  3. 微信小程序项目开发实战:用WePY、mpvue、Taro打造高效的小程序》(笔记4)支持React.js语法的Taro框架

    Taro本身实现的情况类似于mpvue,mpvue的未来展望中也包含了支付宝小程序,现在的版本中,也可以使用不同的构建命令来构建出百度小程序的支持,如第10章所示,但是现在Taro先于mpvue实现了 ...

  4. 120多套各种类别微信小程序模板源码打包下载

    120多套各种类别微信小程序模板源码打包下载,以下是部分截图欢迎下载!120多套各种类别微信小程序模板源码打包下载 下载地址:https://pan.baidu.com/s/1Cfqyc9p2ZDOc ...

  5. 微信小程序从零开始开发步骤(八)引入框架WeUI

    首先来看下WeUI的官方介绍: WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一.在微信小程序的开发过程中,涉及到的前端 ...

  6. 【推荐】开源项目minapp-重新定义微信小程序的开发

    minapp 重新定义微信小程序的开发 官网:https://qiu8310.github.io/minapp/ 作者:Mora minapp 重新定义微信小程序的开发 使用 用 npm 安装命令行工 ...

  7. 微信小程序快速开发上手

    微信小程序快速开发上手 介绍: 从实战开发角度,完整系统地介绍了小程序的开发环境.小程序的结构.小程序的组件与小程序的API,并提供了多个开发实例帮助读者快速掌握小程序的开发技能,并能自己动手开发出小 ...

  8. 技本功丨收藏!斜杠青年与你共探微信小程序云开发(下篇)

    2019年2月26日,人们为了一个杯子疯了一天. 星巴克猫爪杯,一场已经与猫无关了的“圣杯战争“.网上的倒卖价格,已炒至近千元! 求而不得,舍而不能,得而不惜.这是人最大的悲哀... 所以,请珍惜以下 ...

  9. 微信小程序快速开发

    微信小程序快速开发 一.注册小程序账号,下载IDE 1.官网注册https://mp.weixin.qq.com/,并下载IDE. 2.官方文档一向都是最好的学习资料. 注意:1)注册账号之后会有一个 ...

随机推荐

  1. Troubleshooting ORA-30013 Error (Doc ID 1578717.1)

    Troubleshooting ORA-30013 Error (Doc ID 1578717.1) APPLIES TO: Oracle Database - Enterprise Edition ...

  2. 详解YUV数据格式

    我们在讲 FFmpeg 系列的时候,有提到 YUV 的.其中包括YUV播放器.简单的YUV格式介绍. 一.YUV简介 YUV,是一种颜色编码方法.常使用在各个影像处理元件中. YUV在对照片或影片编码 ...

  3. Centos系统配置bond0

    版权声明:本文为博主原创文章,支持原创,转载请附上原文出处链接和本声明. 本文链接地址:https://www.cnblogs.com/wannengachao/p/11942254.html 1.查 ...

  4. Shell命令-系统信息及显示之free、cal

    文件及内容处理 - free.cal 1. free:查看系统内存 free命令的功能说明 free 命令用于显示内存状态.free 指令会显示内存的使用情况,包括实体内存,虚拟的交换文件内存,共享内 ...

  5. C学习笔记(10)--- 强制类型转换,错误处理,递归

    1.强制类型转换: 强制类型转换是把变量从一种类型转换为另一种数据类型.例如,如果您想存储一个 long 类型的值到一个简单的整型中,您需要把 long 类型强制转换为 int 类型. 您可以使用强制 ...

  6. 201871010116-祁英红《面向对象程序设计(java)》第四周学习总结

    博文正文开头格式:(2分) 项目 内容 <面向对象程序设计(java)> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://ww ...

  7. Vue+cordova开发App

    Vue+cordova开发App https://www.imooc.com/article/70062

  8. 剑指Offer-18.二叉树的镜像(C++/Java)

    题目: 题目描述 操作给定的二叉树,将其变换为源二叉树的镜像. 输入描述: 二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ ...

  9. hdu6494 dp

    http://acm.hdu.edu.cn/showproblem.php?pid=6494 题意 一个长n字符串(n,1e4),'A'代表A得分,'B'代表B得分,'?'代表不确定,一局比赛先得11 ...

  10. 【2019.7.24 NOIP模拟赛 T1】道路建设(road)(水题)

    原题与此题 原题是一道神仙不可做题,两者区别在于,原题不能有重边和自环. 然而,这题可以有重边... 于是这题就变成了一道大水题. 此题的解法 考虑如何构造. 对于\(n\le10^4\)的情况: 对 ...