Miniapp – SPore

preface

Native way to develop small programs, after the previous version iteration and project accumulation for refactoring, adding global storage objects, lean and remove the features not commonly used, still adhere to non-awareness, progressive enhancement and ease of use, based on events and plug-in expansion.

Platform support: wechat mini program, Alipay mini program, Taobao mini program, Nail mini program, etc.

features

  • Cross-platform support for both wechat mini program and Ali mini program
  • Global storage object
  • Very simple API, easy to get started
  • Page onBack life cycle (triggered when the page returns after jumping)
  • Component didPropsUpdate lifecycle (component props fired when updated) (Ali applet only)
  • Component props listener (Ali applet only)
  • Life cycle event system

Demo

Herbox. Online/p / 109000020…

The installation

npm i miniapp-spore -s

Introduction to use

Use the package

The app.js file is imported into the miniapp-spore package.

import spore from "miniapp-spore";
Copy the code

New Store (namespace, data, [options])

Each page of the applet can share the Store data. Note that if there are multiple Store objects, the namespace should be unique. According to the namespace, the data initialization and update will be automatically synchronized to the data[namespace] on the corresponding page.

// Import {Store} from "miniapp-spore" under app.js; new Store('$global', { count: 1024 })Copy the code

At this point, the $Global data is shared by axML templates for all pages

<view>{{$global.count}}</view> <! - $1024 - >Copy the code

SetData (update, [callback])

The change data is consistent with the use of setData for the page/component in the native framework. All page data updates are automatically triggered after the change.

import { Store } from "miniapp-spore"; Let store = new store ('$global', {count: 1024}) let store = new store ('$global', {count: 1024}) Store.data.count + 1}) // Consistent with setData for native pages and components: // 1. Modifying store.data directly is invalid, cannot change the page/component state, and will cause data inconsistency. // 2. Try to avoid setting too much data at a time. // 3. SetData takes an object as an argument. The key name of an object can be very flexible and given in the form of a data path, such as array[2]. Message, A.B.C.D, and does not need to be defined in store.data. // 4. SetData The second argument is the callback triggered after the update. After the update, you can get the latest data data.Copy the code

Additionally, an array can be updated using the store.prototype. $spliceData(update, [callback]) method

/ / in the obj. Arr under the array subscript 1 after insert the 'a', 'b', 'c' store. $spliceData ({' obj. Arr: [1, 0, 'a', 'b', 'c']})Copy the code

In addition, provides the Promise Store version setData method. The prototype. AsyncSetData (update)

await store.asyncSetData({ count: store.data.count + 1 }) //... Processing logic after the setting takes effectCopy the code

Similarly, do not change the data.$global.count on the page. This will not update the store data and will violate the consistency of the data modification (only on the same interface), so change the data only in the store instance method.

Modify global data on the page

Generally, the store is placed on the app to make it easier for the page/component to get the Store object

app.js

import { Store } from "miniapp-spore";

let store = new Store('$global', { count: 1024 })

App({
	store
})
Copy the code

pages/index/index.js

let store = getApp().store; {onLoad(){store.setdata ({count: store.data.count - 1}, ()=>{console.log(' data updated ')})}})Copy the code

Component uses global data

In the component, the mechanism is to manually configure the bound storage due to performance concerns. You need to define stores fields and array types in the component. The object is automatically bound to the object by placing an instance of the storage object in it. The unconfigured object does not take effect automatically.

components/test/test.js

let store = getApp().store; Component({// Added defining this stores field stores: [store, //... supports multiple store instances], data: {}, props: {}, didMount() {},});Copy the code

Once stores are configured, this data can be used in the component AXML template.

components/test/test.axml

<view>{{$global.count}}</view> <! - $1024 - >Copy the code

Calculate attribute

Let store = new store ("$global", {count: 1024 }, { computed:{ isOdd(){ return this.count % 2; }}});Copy the code
< view > {{$global. Count}} is odd: {{$global. IsOdd}} < / view > <! -- 1024 is odd: 0 -->Copy the code

Methods encapsulate

Modify global data on the page

Generally, the store is placed on the app to make it easier for the page/component to get the Store object

app.js

import { Store } from "miniapp-spore";

let store = new Store('$global', { count: 1024 })

App({
	store
})
Copy the code

pages/index/index.js

let store = getApp().store; {onLoad(){store.setdata ({count: store.data.count - 1}, ()=>{console.log(' data updated ')})}})Copy the code

Component uses global data

In the component, the mechanism is to manually configure the bound storage due to performance concerns. You need to define stores fields and array types in the component. The object is automatically bound to the object by placing an instance of the storage object in it. The unconfigured object does not take effect automatically.

components/test/test.js

let store = getApp().store; Component({// Added defining this stores field stores: [store, //... supports multiple store instances], data: {}, props: {}, didMount() {},});Copy the code

Once stores are configured, this data can be used in the component AXML template.

components/test/test.axml

<view>{{$global.count}}</view> <! - $1024 - >Copy the code

Calculate attribute

Let store = new store ("$global", {count: 1024 }, { computed:{ isOdd(){ return this.count % 2; }}});Copy the code
< view > {{$global. Count}} is odd: {{$global. IsOdd}} < / view > <! -- 1024 is odd: 0 -->Copy the code

Methods encapsulate

Modify global data on the page

Generally, the store is placed on the app to make it easier for the page/component to get the Store object

app.js

import { Store } from "miniapp-spore";

let store = new Store('$global', { count: 1024 })

App({
	store
})
Copy the code

pages/index/index.js

let store = getApp().store; {onLoad(){store.setdata ({count: store.data.count - 1}, ()=>{console.log(' data updated ')})}})Copy the code

Component uses global data

In the component, the mechanism is to manually configure the bound storage due to performance concerns. You need to define stores fields and array types in the component. The object is automatically bound to the object by placing an instance of the storage object in it. The unconfigured object does not take effect automatically.

components/test/test.js

let store = getApp().store; Component({// Added defining this stores field stores: [store, //... supports multiple store instances], data: {}, props: {}, didMount() {},});Copy the code

Once stores are configured, this data can be used in the component AXML template.

components/test/test.axml

<view>{{$global.count}}</view> <! - $1024 - >Copy the code

Calculate attribute

Let store = new store ("$global", {count: 1024 }, { computed:{ isOdd(){ return this.count % 2; }}});Copy the code
< view > {{$global. Count}} is odd: {{$global. IsOdd}} < / view > <! -- 1024 is odd: 0 -->Copy the code

Methods encapsulate

In order to facilitate data maintenance and management, the commonly used data modification logic is encapsulated and invoked in business code.

Let store = new store ("$global", {count: 1024}, {actions:{add(){this.setdata ({count: this.data.count+1}) } } }); store.add(); // You can change the count by calling the method in the specific business logic such as page/componentCopy the code

Data forced update

If the page/component may not have been updated after the Store has been instantiated in some lifecycle, you can force an update by calling store.prototype.update ([callback]).

Store.update ((data)=>{// force update complete});Copy the code

Performance update Scheme

By default, when the store is updated, the full setData of the store’s data is added to the page/component. In application scenarios where setData is frequently updated in stores or there are many components, this solution is preferred. However, if the amount of data is too large, the solution may fail to be updated.

In another solution, when the store is updated, the data of the store is diff compared with the page/component to obtain the difference, and the part of the difference data is updated to the page/component. In the scenario, the update frequency is small, the components are few, and the data volume is large, but the update delay is not worried.

Use the second option if necessary. You can enable diff in the configuration

// Create a global store object with diff update let store = new store ("$store", {count: 1024}, {diff: true});Copy the code

Life cycle event system

[The life cycle of Ali Mini program]

For App lifecycle are: onLaunch onShow, onHide, onError, onShareAppMessage

For the Page lifecycle there are: onLoad,onShow,onBack, onReady, onHide, onUnload, onTitleClick, onPullDownRefresh, onReachBottom, onShareAppMessage, OnOptionMenuClick, onPullIntercept, onTabItemTap, onPageScroll (where onBack is frame simulated)

For the Component lifecycle there are: OnInit, deriveDataFromProps, didMount, didUpdate, didUnmount, didPropsUpdate Where didPropsUpdate is the framework simulation with instructions behind)

[Life cycle of wechat Applet]

For App lifecycle are: onLaunch onShow, onHide, onError, onPageNotFound, onUnhandledRejection, onThemeChange

For the Page lifecycle there are: onLoad,onShow,onBack, onReady, onHide, onUnload, onPullDownRefresh, onReachBottom, onShareAppMessage, onShareTimeline, OnAddToFavorites, onPageScroll, onResize,onTabItemTap

For Component life cycles there are: Created, Attached, ready, Moved, detached

(If the life cycle is missing, welcome to mention issue, PR)

For the above lifecycle, the framework can listen to the corresponding lifecycle through events. Use :before and :after to distinguish between before and after processing logic.

Reference examples:

import { Store, on } from "miniapp-spore"; On (' {type}.{lifecycle}:{before and after} ', handler) // On ("Page. Function (){// this refers to the page instance console.log(' before the page loads ')}); / / support synchronization method on (" Component didMount: before ", async function () {/ / / async/await call interface to note before, in particular, are likely to affect the triggering time of the life cycle, The current async method will not trigger component. didMount});Copy the code

When using life cycle events, it is important to note that because they are listened to and then fired, these event listeners should not normally be listened to in any one lifetime, or in the lifetime before they are triggered.

Component Property Change lifecycle (Ali applets only)

Wechat miniprograms have observers, while Ali Miniprograms don’t have a mechanism for detecting property changes. Therefore, the framework provides a didPropsUpdate lifecycle for the Ali applet, which is triggered only when props change.

In general, when we use components to pass properties to components, the property change judgment may use didUpdate life cycle. For simple value changes, we can judge whether the value is equal, but for complex object structure, it is troublesome.

<hello foo="{{foo}}"/>
Copy the code
Component({ didUpdate(prevProps, prevData) { if(prevProps.foo ! = this.props. Foo){// foo was updated, if foo was an object type, this would be incorrect}},});Copy the code

So, this framework provides a new lifecycle for handling props when the incoming attribute foo.xxx changes, triggering didPropsUpdate

Component({didPropsUpdate(diff, prevProps) {if('foo. XXX 'in diff){// props = props}},});Copy the code

Component Property Change Observer (Ali applet only)

You can handle props with didPropsUpdate, and you can also define an observer to listen for changes in values in a specific path.

Component({watchProps: {'foo.a.b': function(diff, prevProps){//foo.a.b}, 'foo.cc': Function (diff, prevProps){//foo.cc changes}},}); // Note: This cannot be an arrow function, otherwise this points to the watchPropsCopy the code

Page/component asyncSetData

In a page or component, you can also directly use the asyncSetData method instead of the current page/component setData. The usage is the same as the Store asyncSetData.

Plug-in mechanism

By loading the plug-in using the use method, you can develop your own plug-in to add functions flexibly in the project.

import { Store, use } from "miniapp-spore";

import reduxPlugin from "./redux.plugin.js";

use(reduxPlugin);
Copy the code

The structure of the plug-in is as follows:

//redux.plugin.js export default function(spore, Options){return {install(){// Install (){// Install entry // Flexible use of life cycle event system processing related logic}}}Copy the code

The sample