BeautyWe.js 一套專一於微信小程序的開發範式

歡迎來個人博客閱讀: BeautyWe.js 一套專一於微信小程序的開發範式

官網: beautywejs.com
Repo: beautywe

一個簡單的介紹

BeautyWe.js 是什麼?javascript

它是一套專一於微信小程序的企業級開發範式,它的願景是:css

讓企業級的微信小程序項目中的代碼,更加簡單、漂亮。

爲何要這樣命名呢?html

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

「We」 既是咱們的 We,也是微信的 We,Both beautiful!前端

那麼它有什麼賣點呢?java

  1. 專一於微信小程序環境,寫原汁原味的微信小程序代碼。
  2. 因爲只專一於微信小程序,它的源碼也很簡單。
  3. 插件化的編程方式,讓複雜邏輯更容易封裝。
  4. 再加上一些配套設施:node

    1. 一些官方插件。
    2. 一套開箱即用,包含了工程化、項目規範以及微信小程序環境獨特問題解決方案的框架。
    3. 一個CLI工具,幫你快速建立應用,頁面,組件等。

它由如下幾部分組成:webpack

  • 一個插件化的核心 - BeautyWe Core
    對 App、Page 進行抽象和包裝,保持傳統微信小程序開發姿式,同時開放部分原生能力,讓其具備「可插件化」的能力。
  • 一些官方插件BeautyWe Plugins
    得益於 Core 的「可插件化」特性,封裝複雜邏輯,實現可插拔。官方對於常見的需求提供了一些插件:如加強存儲、發佈/訂閱、狀態機、Logger、緩存策略等。
  • 一套開箱即用的項目框架 - BeautyWe Framework
    描述了一種項目的組織形式,開箱即用,集成了 BeautyWe Core ,而且提供瞭如:全局窗口、開發規範、多環境開發、全局配置、NPM 等解決方案。
  • 一個CLI工具 - BeautyWe Cli
    提供快速建立應用、頁面、插件,以及項目構建功能的命令行工具。而且還支持自定義的建立模板。

一個簡單的例子

下載git

用 BeautyWe 包裝你的應用github

以後,你就能使用 BeautyWe Plugin 提供的能力了。web

開放原生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 → ...

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

beautywe pluggable

工做原理是這樣的:

  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

npm-works

這樣作的好處:

  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

相關文章
相關標籤/搜索