├── types
├── test
│ ├── setup.d.ts
│ └── index.test.d.ts
└── src
│ ├── behavior.d.ts
│ ├── core.d.ts
│ └── index.d.ts
├── examples
├── basic
│ ├── .gitignore
│ ├── src
│ │ ├── app.js
│ │ ├── pages
│ │ │ └── index
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ ├── index.wxss
│ │ │ │ └── index.js
│ │ ├── app.wxss
│ │ ├── index.html
│ │ └── store.js
│ ├── .eslintrc.js
│ ├── package.json
│ └── webpack.config.js
├── typescript
│ ├── .gitignore
│ ├── src
│ │ ├── app.ts
│ │ ├── pages
│ │ │ └── index
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ ├── index.wxss
│ │ │ │ └── index.ts
│ │ ├── app.wxss
│ │ ├── index.html
│ │ └── store.ts
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── package.json
│ └── webpack.config.js
├── chaining-init
│ ├── .gitignore
│ ├── src
│ │ ├── app.ts
│ │ ├── pages
│ │ │ └── index
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ ├── index.wxss
│ │ │ │ └── index.ts
│ │ ├── app.wxss
│ │ ├── index.html
│ │ └── store.ts
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── package.json
│ └── webpack.config.js
└── README.md
├── .eslintignore
├── .npmignore
├── .gitignore
├── test
├── env.js
├── env.d.ts
├── setup.ts
└── index.test.ts
├── config
├── index.ts
├── base.ts
└── build.ts
├── jest.config.js
├── .prettierrc.js
├── tsconfig.json
├── LICENSE
├── .eslintrc.js
├── gulpfile.ts
├── package.json
├── src
├── behavior.ts
├── core.ts
└── index.ts
├── dist
├── index.js
└── index.js.map
└── README.md
/types/test/setup.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/types/test/index.test.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/examples/basic/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 |
--------------------------------------------------------------------------------
/examples/typescript/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 |
--------------------------------------------------------------------------------
/examples/chaining-init/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | types
3 | demo
4 | dist
5 | swc_build
--------------------------------------------------------------------------------
/examples/basic/src/app.js:
--------------------------------------------------------------------------------
1 | // this file is always executed on startup
2 |
--------------------------------------------------------------------------------
/examples/typescript/src/app.ts:
--------------------------------------------------------------------------------
1 | // this file is always executed on startup
2 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
4 |
--------------------------------------------------------------------------------
/examples/chaining-init/src/app.ts:
--------------------------------------------------------------------------------
1 | // this file is always executed on startup
2 |
--------------------------------------------------------------------------------
/examples/typescript/src/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
4 |
--------------------------------------------------------------------------------
/examples/chaining-init/src/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
4 |
--------------------------------------------------------------------------------
/examples/basic/src/app.wxss:
--------------------------------------------------------------------------------
1 | /* this is the global stylesheet */
2 |
3 | view {
4 | display: block;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/typescript/src/app.wxss:
--------------------------------------------------------------------------------
1 | /* this is the global stylesheet */
2 |
3 | view {
4 | display: block;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/chaining-init/src/app.wxss:
--------------------------------------------------------------------------------
1 | /* this is the global stylesheet */
2 |
3 | view {
4 | display: block;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 | {{ numA }} + {{ numB }} = {{ sum }}
2 |
3 |
--------------------------------------------------------------------------------
/examples/chaining-init/src/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 | {{ numA }} + {{ numB }} = {{ sum }}
2 |
3 |
--------------------------------------------------------------------------------
/examples/typescript/src/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 | {{ numA }} + {{ numB }} = {{ sum }}
2 |
3 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | .logo {
2 | width: 256px;
3 | margin: 0 auto;
4 | }
5 |
6 | .hello {
7 | margin: 20px;
8 | text-align: center;
9 | }
10 |
--------------------------------------------------------------------------------
/examples/typescript/src/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | .logo {
2 | width: 256px;
3 | margin: 0 auto;
4 | }
5 |
6 | .hello {
7 | margin: 20px;
8 | text-align: center;
9 | }
10 |
--------------------------------------------------------------------------------
/examples/chaining-init/src/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | .logo {
2 | width: 256px;
3 | margin: 0 auto;
4 | }
5 |
6 | .hello {
7 | margin: 20px;
8 | text-align: center;
9 | }
10 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | package-lock.json
4 |
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | test
12 | tools
13 | docs
14 | miniprogram_dev
15 | node_modules
16 | coverage
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | package-lock.json
4 | .vscode
5 |
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 |
12 | miniprogram_dist
13 | miniprogram_dev
14 | node_modules
15 | coverage
16 | swc_build
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # 示例
2 |
3 | 这些示例既可以运行在小程序里,也可以运行在浏览器中。(在浏览器中运行时,它基于 [glass-easel](https://github.com/wechat-miniprogram/glass-easel) 。)
4 |
5 | 进入任何一个示例目录后,执行:
6 |
7 | ```js
8 | npm install
9 | npm run build
10 | ```
11 |
12 | 可以编译出 web 版本。直接用浏览器打开 dist/index.html 可以看到效果。
13 |
--------------------------------------------------------------------------------
/test/env.js:
--------------------------------------------------------------------------------
1 | exports.renderComponent = globalThis.__renderComponent
2 |
3 | exports.defineComponent = globalThis.__defineComponent
4 |
5 | exports.waitTick = () => {
6 | return new Promise((resolve) => {
7 | setTimeout(() => {
8 | resolve()
9 | }, 100)
10 | })
11 | }
12 |
--------------------------------------------------------------------------------
/config/index.ts:
--------------------------------------------------------------------------------
1 | import { baseConfig, BaseConfig } from './base'
2 | import { buildConfig, BuildConfig } from './build'
3 |
4 | interface Config extends BaseConfig, BuildConfig {}
5 |
6 | const config: Config = {
7 | ...baseConfig,
8 | ...buildConfig,
9 | }
10 |
11 | export default config
12 |
--------------------------------------------------------------------------------
/types/src/behavior.d.ts:
--------------------------------------------------------------------------------
1 | import 'miniprogram-api-typings';
2 | export declare const behavior: WechatMiniprogram.Behavior.BehaviorIdentifier;
5 |
--------------------------------------------------------------------------------
/examples/basic/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parserOptions: {
4 | ecmaVersion: 9,
5 | sourceType: 'module',
6 | },
7 | parser: '@typescript-eslint/parser',
8 | plugins: ['@typescript-eslint', 'import', 'promise'],
9 | globals: {
10 | Component: true,
11 | Page: true,
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/examples/typescript/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parserOptions: {
4 | ecmaVersion: 9,
5 | sourceType: 'module',
6 | },
7 | parser: '@typescript-eslint/parser',
8 | plugins: ['@typescript-eslint', 'import', 'promise'],
9 | globals: {
10 | Component: true,
11 | Page: true,
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/examples/typescript/src/pages/index/index.ts:
--------------------------------------------------------------------------------
1 | import { ComponentWithStore } from 'mobx-miniprogram-bindings'
2 | import { store } from '../../store'
3 |
4 | ComponentWithStore({
5 | storeBindings: {
6 | store,
7 | fields: ['numA', 'numB', 'sum'],
8 | actions: {
9 | buttonTap: 'update',
10 | },
11 | },
12 | })
13 |
--------------------------------------------------------------------------------
/examples/chaining-init/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parserOptions: {
4 | ecmaVersion: 9,
5 | sourceType: 'module',
6 | },
7 | parser: '@typescript-eslint/parser',
8 | plugins: ['@typescript-eslint', 'import', 'promise'],
9 | globals: {
10 | Component: true,
11 | Page: true,
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | bail: 1,
4 | verbose: true,
5 | testEnvironment: 'jsdom',
6 | moduleFileExtensions: ['js', 'ts'],
7 | setupFiles: ['/test/setup.ts'],
8 | testMatch: ['/test/**/*.test.ts'],
9 | collectCoverageFrom: ['/src/**/*.ts', '!**/__test__/**'],
10 | }
11 |
--------------------------------------------------------------------------------
/examples/chaining-init/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "module": "es6",
5 | "target": "es6",
6 | "esModuleInterop": true,
7 | "moduleResolution": "node",
8 | "lib": ["ES6", "ES7", "DOM", "ESNext"],
9 | "useDefineForClassFields": true
10 | },
11 | "include": ["src/**/*.ts", "typings"]
12 | }
13 |
--------------------------------------------------------------------------------
/examples/typescript/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "module": "es6",
5 | "target": "es5",
6 | "esModuleInterop": true,
7 | "moduleResolution": "node",
8 | "lib": ["ES6", "ES7", "DOM", "ESNext"],
9 | "useDefineForClassFields": true
10 | },
11 | "include": ["src/**/*.ts", "typings"]
12 | }
13 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/index/index.js:
--------------------------------------------------------------------------------
1 | import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
2 | import { store } from '../../store'
3 |
4 | Component({
5 | behaviors: [storeBindingsBehavior],
6 | storeBindings: {
7 | store,
8 | fields: ['numA', 'numB', 'sum'],
9 | actions: {
10 | buttonTap: 'update',
11 | },
12 | },
13 | })
14 |
--------------------------------------------------------------------------------
/examples/basic/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/typescript/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/chaining-init/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 100,
3 | tabWidth: 2,
4 | useTabs: false,
5 | semi: false,
6 | singleQuote: true,
7 | quoteProps: 'as-needed',
8 | trailingComma: 'all',
9 | bracketSpacing: true,
10 | arrowParens: 'always',
11 | requirePragma: false,
12 | insertPragma: false,
13 | proseWrap: 'preserve',
14 | endOfLine: 'lf',
15 | embeddedLanguageFormatting: 'auto',
16 | }
17 |
--------------------------------------------------------------------------------
/examples/basic/src/store.js:
--------------------------------------------------------------------------------
1 | import { makeAutoObservable } from 'mobx-miniprogram'
2 |
3 | class Store {
4 | numA = 1
5 | numB = 2
6 |
7 | constructor() {
8 | makeAutoObservable(this)
9 | }
10 |
11 | get sum() {
12 | return this.numA + this.numB
13 | }
14 |
15 | update() {
16 | const sum = this.sum
17 | this.numA = this.numB
18 | this.numB = sum
19 | }
20 | }
21 |
22 | export const store = new Store()
23 |
--------------------------------------------------------------------------------
/examples/typescript/src/store.ts:
--------------------------------------------------------------------------------
1 | import { makeAutoObservable } from 'mobx-miniprogram'
2 |
3 | class Store {
4 | numA = 1
5 | numB = 2
6 |
7 | constructor() {
8 | makeAutoObservable(this)
9 | }
10 |
11 | get sum() {
12 | return this.numA + this.numB
13 | }
14 |
15 | update() {
16 | const sum = this.sum
17 | this.numA = this.numB
18 | this.numB = sum
19 | }
20 | }
21 |
22 | export const store = new Store()
23 |
--------------------------------------------------------------------------------
/examples/chaining-init/src/store.ts:
--------------------------------------------------------------------------------
1 | import { makeAutoObservable } from 'mobx-miniprogram'
2 |
3 | class Store {
4 | numA = 1
5 | numB = 2
6 |
7 | constructor() {
8 | makeAutoObservable(this)
9 | }
10 |
11 | get sum() {
12 | return this.numA + this.numB
13 | }
14 |
15 | update() {
16 | const sum = this.sum
17 | this.numA = this.numB
18 | this.numB = sum
19 | }
20 | }
21 |
22 | export const store = new Store()
23 |
--------------------------------------------------------------------------------
/types/src/core.d.ts:
--------------------------------------------------------------------------------
1 | import { IStoreBindings } from './index';
2 | export declare const createActions: >(methods: any, options: IStoreBindings) => void;
3 | export type StoreBindingsManager = {
4 | updateStoreBindings: () => void;
5 | destroyStoreBindings: () => void;
6 | };
7 | export declare const createDataFieldsReactions: >(target: any, options: Omit, "actions">) => StoreBindingsManager;
8 |
--------------------------------------------------------------------------------
/test/env.d.ts:
--------------------------------------------------------------------------------
1 | import * as adapter from 'glass-easel-miniprogram-adapter'
2 |
3 | export const renderComponent: (
4 | path: string | undefined,
5 | template: string,
6 | f: (Component: adapter.ComponentConstructor, env: adapter.ComponentEnv) => void,
7 | ) => adapter.glassEasel.GeneralComponent
8 |
9 | export const defineComponent: (
10 | path: string | undefined,
11 | template: string,
12 | f: (Component: adapter.ComponentConstructor, env: adapter.ComponentEnv) => void,
13 | ) => adapter.glassEasel.GeneralComponent
14 |
15 | export const waitTick: () => Promise
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "module": "commonjs",
5 | "strict": true,
6 | "noImplicitAny": false,
7 | "removeComments": true,
8 | "target": "ESNEXT",
9 | "lib": ["ESNEXT", "DOM"],
10 | "preserveConstEnums": true,
11 | "sourceMap": true,
12 | "esModuleInterop": true,
13 | "allowSyntheticDefaultImports": true,
14 | "declaration": true,
15 | "declarationDir": "./types",
16 | "emitDeclarationOnly": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["./src/**/*.ts", "./test/**/*.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/chaining-init/src/pages/index/index.ts:
--------------------------------------------------------------------------------
1 | import { ComponentConstructor } from 'glass-easel-miniprogram-adapter'
2 | import { initStoreBindings } from 'mobx-miniprogram-bindings'
3 | import { store } from '../../store'
4 |
5 | declare const Component: ComponentConstructor
6 |
7 | Component()
8 | .init((ctx) => {
9 | const { listener } = ctx
10 |
11 | initStoreBindings(ctx, {
12 | store,
13 | fields: ['numA', 'numB', 'sum'],
14 | })
15 |
16 | const buttonTap = listener(() => {
17 | store.update()
18 | })
19 |
20 | return { buttonTap }
21 | })
22 | .register()
23 |
--------------------------------------------------------------------------------
/examples/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "main": "src/index.ts",
4 | "scripts": {
5 | "build": "webpack --config webpack.config.js",
6 | "dev": "webpack --config webpack.config.js --watch"
7 | },
8 | "dependencies": {
9 | "glass-easel": "0.5.2",
10 | "glass-easel-miniprogram-adapter": "0.5.2",
11 | "mobx-miniprogram": "^6.0.0",
12 | "mobx-miniprogram-bindings": "file:../.."
13 | },
14 | "devDependencies": {
15 | "css-loader": "^6.7.1",
16 | "eslint": "^7.17.0",
17 | "eslint-plugin-import": "^2.22.1",
18 | "eslint-plugin-promise": "^4.2.1",
19 | "glass-easel-miniprogram-webpack-plugin": "0.5.2",
20 | "less": "^4.1.3",
21 | "less-loader": "^11.0.0",
22 | "mini-css-extract-plugin": "^2.6.1",
23 | "webpack": "^5.85.0",
24 | "webpack-cli": "^5.0.1"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/typescript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "main": "src/index.ts",
4 | "scripts": {
5 | "build": "webpack --config webpack.config.js",
6 | "dev": "webpack --config webpack.config.js --watch"
7 | },
8 | "dependencies": {
9 | "glass-easel": "0.5.2",
10 | "glass-easel-miniprogram-adapter": "0.5.2",
11 | "mobx-miniprogram": "^6.0.0",
12 | "mobx-miniprogram-bindings": "file:../.."
13 | },
14 | "devDependencies": {
15 | "@typescript-eslint/eslint-plugin": "^6.6.0",
16 | "@typescript-eslint/parser": "^6.6.0",
17 | "css-loader": "^6.7.1",
18 | "eslint": "^7.17.0",
19 | "eslint-plugin-import": "^2.22.1",
20 | "eslint-plugin-promise": "^4.2.1",
21 | "glass-easel-miniprogram-webpack-plugin": "0.5.2",
22 | "less": "^4.1.3",
23 | "less-loader": "^11.0.0",
24 | "mini-css-extract-plugin": "^2.6.1",
25 | "ts-loader": "^9.4.2",
26 | "typescript": "^5.2.2",
27 | "webpack": "^5.85.0",
28 | "webpack-cli": "^5.0.1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/chaining-init/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "main": "src/index.ts",
4 | "scripts": {
5 | "build": "webpack --config webpack.config.js",
6 | "dev": "webpack --config webpack.config.js --watch"
7 | },
8 | "dependencies": {
9 | "glass-easel": "0.5.2",
10 | "glass-easel-miniprogram-adapter": "0.5.2",
11 | "mobx-miniprogram": "^6.0.0",
12 | "mobx-miniprogram-bindings": "file:../.."
13 | },
14 | "devDependencies": {
15 | "@typescript-eslint/eslint-plugin": "^6.6.0",
16 | "@typescript-eslint/parser": "^6.6.0",
17 | "css-loader": "^6.7.1",
18 | "eslint": "^7.17.0",
19 | "eslint-plugin-import": "^2.22.1",
20 | "eslint-plugin-promise": "^4.2.1",
21 | "glass-easel-miniprogram-webpack-plugin": "0.5.2",
22 | "less": "^4.1.3",
23 | "less-loader": "^11.0.0",
24 | "mini-css-extract-plugin": "^2.6.1",
25 | "ts-loader": "^9.4.2",
26 | "typescript": "^5.2.2",
27 | "webpack": "^5.85.0",
28 | "webpack-cli": "^5.0.1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/config/base.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 |
3 | export interface BaseConfig {
4 | entry: string
5 | srcPath: string
6 | bundlePath: string
7 | demoPath: string
8 | bundleInDemoPath: string
9 | typesPath: string
10 | swcBuildPath: string
11 | tsConfigPath: string
12 | }
13 |
14 | const srcPath = path.resolve(__dirname, '../src')
15 | const bundlePath = path.resolve(__dirname, '../dist')
16 | const demoPath = path.resolve(__dirname, '../demo')
17 | const bundleInDemoPath = path.resolve(demoPath, './src/mobx-miniprogram-bindings')
18 | const swcBuildPath = path.resolve(__dirname, '../swc_build')
19 | const typesPath = path.resolve(__dirname, '../types')
20 | const tsConfigPath = path.resolve(__dirname, '../tsconfig.json')
21 |
22 | export const baseConfig: BaseConfig = {
23 | entry: 'index',
24 | srcPath, // 源码路径
25 | bundlePath, // 编译产物路径
26 | demoPath, // demo 路径
27 | bundleInDemoPath, // 编译产物在 demo 里的路径
28 | typesPath, // .d.ts 类型声明路径
29 | swcBuildPath, // swc 转译生成路径
30 | tsConfigPath, // tsc 配置文件
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 wechat-miniprogram
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: '@typescript-eslint/parser',
4 | parserOptions: {
5 | ecmaVersion: 9,
6 | ecmaFeatures: {
7 | jsx: false,
8 | },
9 | sourceType: 'module',
10 | },
11 | env: {
12 | es6: true,
13 | node: true,
14 | jest: true,
15 | commonjs: true,
16 | },
17 | globals: {
18 | window: true,
19 | document: true,
20 | App: true,
21 | Page: true,
22 | Component: true,
23 | Behavior: true,
24 | wx: true,
25 | getCurrentPages: true,
26 | },
27 | plugins: ['@typescript-eslint', 'prettier'],
28 | extends: [
29 | 'eslint:recommended',
30 | 'plugin:@typescript-eslint/eslint-recommended',
31 | 'plugin:@typescript-eslint/recommended',
32 | 'prettier/@typescript-eslint',
33 | ],
34 | rules: {
35 | 'prettier/prettier': 'error',
36 | '@typescript-eslint/ban-ts-ignore': 'off',
37 | '@typescript-eslint/no-empty-function': 'off',
38 | '@typescript-eslint/explicit-function-return-type': 'off',
39 | '@typescript-eslint/no-explicit-any': 'off',
40 | '@typescript-eslint/no-non-null-assertion': 'off',
41 | },
42 | }
43 |
--------------------------------------------------------------------------------
/config/build.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 现在使用 swc 编译 ts 到 es5
3 | * 通过 esbuild 进行 bundle
4 | * (也可以选择直接使用 swc 一步到位)
5 | */
6 |
7 | // swc 配置
8 | const swcOptions = {
9 | jsc: {
10 | parser: {
11 | syntax: 'typescript',
12 | tsx: false,
13 | decorators: false,
14 | dynamicImport: false,
15 | },
16 | transform: null,
17 | target: 'es5',
18 | externalHelpers: false,
19 | keepClassNames: false,
20 | minify: {
21 | compress: {
22 | unused: true,
23 | },
24 | mangle: true,
25 | },
26 | },
27 | sourceMaps: true,
28 | minify: false,
29 | module: {
30 | type: 'commonjs',
31 | strict: false,
32 | strictMode: true,
33 | lazy: false,
34 | noInterop: false,
35 | },
36 | }
37 |
38 | // esbuild 配置
39 | const esbuildOptions = {
40 | outfile: 'index.js',
41 | bundle: true,
42 | format: 'cjs',
43 | minify: true, // 开启压缩混淆
44 | external: ['mobx-miniprogram'],
45 | sourcemap: 'external',
46 | }
47 |
48 | export interface BuildConfig {
49 | swcOptions: any
50 | esbuildOptions: any
51 | }
52 |
53 | export const buildConfig: BuildConfig = {
54 | swcOptions,
55 | esbuildOptions,
56 | }
57 |
--------------------------------------------------------------------------------
/examples/basic/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 |
3 | const path = require('path')
4 | const {
5 | GlassEaselMiniprogramWebpackPlugin,
6 | GlassEaselMiniprogramWxmlLoader,
7 | GlassEaselMiniprogramWxssLoader,
8 | } = require('glass-easel-miniprogram-webpack-plugin')
9 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
10 |
11 | module.exports = [
12 | {
13 | mode: 'development',
14 | entry: './src/index.js', // this file is virtually generated by the plugin
15 | output: {
16 | filename: 'index.js',
17 | path: path.join(__dirname, 'dist'),
18 | module: false,
19 | iife: true,
20 | },
21 | devtool: 'source-map',
22 | module: {
23 | rules: [
24 | {
25 | // wxml should be explicit handled with a loader
26 | test: /\.wxml$/,
27 | use: GlassEaselMiniprogramWxmlLoader,
28 | exclude: /node_modules/,
29 | },
30 | {
31 | // wxss should be explicit handled like CSS
32 | test: /\.wxss$/,
33 | use: [
34 | MiniCssExtractPlugin.loader,
35 | 'css-loader',
36 | GlassEaselMiniprogramWxssLoader,
37 | // Add more loaders here if you work with less, sass, Stylus, etc.
38 | // Currently `@import` does not work well without a preprocessor (such as `less`).
39 | // This is a bug (#113) and will be fixed in future.
40 | 'less-loader',
41 | ],
42 | exclude: /node_modules/,
43 | },
44 | ],
45 | },
46 | plugins: [
47 | new MiniCssExtractPlugin({
48 | filename: 'index.css',
49 | }),
50 | new GlassEaselMiniprogramWebpackPlugin({
51 | // the path of the mini-program code directory
52 | path: path.join(__dirname, 'src'),
53 | // the resouce files that should be copied to the dist directory
54 | resourceFilePattern: /\.(jpg|jpeg|png|gif|html)$/,
55 | // the default entry
56 | defaultEntry: 'pages/index/index',
57 | }),
58 | ],
59 | },
60 | ]
61 |
--------------------------------------------------------------------------------
/test/setup.ts:
--------------------------------------------------------------------------------
1 | import * as adapter from 'glass-easel-miniprogram-adapter'
2 | import { TmplGroup } from 'glass-easel-template-compiler'
3 |
4 | const compileTemplate = (src: string) => {
5 | const group = new TmplGroup()
6 | group.addTmpl('', src)
7 | const genObjectSrc = `return ${group.getTmplGenObjectGroups()}`
8 | group.free()
9 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
10 | const genObjectGroupList = new Function(genObjectSrc)() as {
11 | [key: string]: any
12 | }
13 | return {
14 | groupList: genObjectGroupList,
15 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
16 | content: genObjectGroupList[''],
17 | }
18 | }
19 |
20 | const setup = () => {
21 | const env = new adapter.MiniProgramEnv()
22 | const codeSpace = env.createCodeSpace('', true)
23 | const backend = env.associateBackend()
24 | codeSpace.componentEnv('', (env) => {
25 | ;(globalThis as any).Behavior = env.Behavior
26 | })
27 | globalThis.__defineComponent = (
28 | path: string,
29 | template: string,
30 | f: (Component: adapter.ComponentConstructor, _: any) => void,
31 | ) => {
32 | const compPath = path || 'TEST'
33 | codeSpace.addCompiledTemplate(compPath, compileTemplate(template))
34 | codeSpace.componentEnv(compPath, (env) => {
35 | f(env.Component, env)
36 | })
37 | const def = codeSpace.getComponentSpace().getComponentByUrl(compPath, '')
38 | codeSpace.getComponentSpace().setGlobalUsingComponent(compPath, def)
39 | }
40 | globalThis.__renderComponent = (
41 | path: string,
42 | template: string,
43 | f: (Component: adapter.ComponentConstructor, _: any) => void,
44 | ) => {
45 | const compPath = path || 'TEST'
46 | codeSpace.addCompiledTemplate(compPath, compileTemplate(template))
47 | codeSpace.componentEnv(compPath, (env) => {
48 | f(env.Component, env)
49 | })
50 | const root = backend.createRoot('glass-easel-root', codeSpace, compPath)
51 | const placeholder = document.createElement('div')
52 | document.body.appendChild(placeholder)
53 | root.attach(document.body as any, placeholder as any)
54 | return root.getComponent().getMethodCaller()
55 | }
56 | }
57 |
58 | module.exports = setup
59 |
--------------------------------------------------------------------------------
/examples/typescript/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 |
3 | const path = require('path')
4 | const {
5 | GlassEaselMiniprogramWebpackPlugin,
6 | GlassEaselMiniprogramWxmlLoader,
7 | GlassEaselMiniprogramWxssLoader,
8 | } = require('glass-easel-miniprogram-webpack-plugin')
9 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
10 |
11 | module.exports = [
12 | {
13 | mode: 'development',
14 | entry: './src/index.js', // this file is virtually generated by the plugin
15 | output: {
16 | filename: 'index.js',
17 | path: path.join(__dirname, 'dist'),
18 | module: false,
19 | iife: true,
20 | },
21 | devtool: 'source-map',
22 | resolve: {
23 | extensions: ['.ts', '.js'],
24 | },
25 | module: {
26 | rules: [
27 | {
28 | test: /\.ts$/,
29 | loader: 'ts-loader',
30 | exclude: /node_modules/,
31 | },
32 | {
33 | // wxml should be explicit handled with a loader
34 | test: /\.wxml$/,
35 | use: GlassEaselMiniprogramWxmlLoader,
36 | exclude: /node_modules/,
37 | },
38 | {
39 | // wxss should be explicit handled like CSS
40 | test: /\.wxss$/,
41 | use: [
42 | MiniCssExtractPlugin.loader,
43 | 'css-loader',
44 | GlassEaselMiniprogramWxssLoader,
45 | // Add more loaders here if you work with less, sass, Stylus, etc.
46 | // Currently `@import` does not work well without a preprocessor (such as `less`).
47 | // This is a bug (#113) and will be fixed in future.
48 | 'less-loader',
49 | ],
50 | exclude: /node_modules/,
51 | },
52 | ],
53 | },
54 | plugins: [
55 | new MiniCssExtractPlugin({
56 | filename: 'index.css',
57 | }),
58 | new GlassEaselMiniprogramWebpackPlugin({
59 | // the path of the mini-program code directory
60 | path: path.join(__dirname, 'src'),
61 | // the resouce files that should be copied to the dist directory
62 | resourceFilePattern: /\.(jpg|jpeg|png|gif|html)$/,
63 | // the default entry
64 | defaultEntry: 'pages/index/index',
65 | }),
66 | ],
67 | },
68 | ]
69 |
--------------------------------------------------------------------------------
/examples/chaining-init/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 |
3 | const path = require('path')
4 | const {
5 | GlassEaselMiniprogramWebpackPlugin,
6 | GlassEaselMiniprogramWxmlLoader,
7 | GlassEaselMiniprogramWxssLoader,
8 | } = require('glass-easel-miniprogram-webpack-plugin')
9 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
10 |
11 | module.exports = [
12 | {
13 | mode: 'development',
14 | entry: './src/index.js', // this file is virtually generated by the plugin
15 | output: {
16 | filename: 'index.js',
17 | path: path.join(__dirname, 'dist'),
18 | module: false,
19 | iife: true,
20 | },
21 | devtool: 'source-map',
22 | resolve: {
23 | extensions: ['.ts', '.js'],
24 | },
25 | module: {
26 | rules: [
27 | {
28 | test: /\.ts$/,
29 | loader: 'ts-loader',
30 | exclude: /node_modules/,
31 | },
32 | {
33 | // wxml should be explicit handled with a loader
34 | test: /\.wxml$/,
35 | use: GlassEaselMiniprogramWxmlLoader,
36 | exclude: /node_modules/,
37 | },
38 | {
39 | // wxss should be explicit handled like CSS
40 | test: /\.wxss$/,
41 | use: [
42 | MiniCssExtractPlugin.loader,
43 | 'css-loader',
44 | GlassEaselMiniprogramWxssLoader,
45 | // Add more loaders here if you work with less, sass, Stylus, etc.
46 | // Currently `@import` does not work well without a preprocessor (such as `less`).
47 | // This is a bug (#113) and will be fixed in future.
48 | 'less-loader',
49 | ],
50 | exclude: /node_modules/,
51 | },
52 | ],
53 | },
54 | plugins: [
55 | new MiniCssExtractPlugin({
56 | filename: 'index.css',
57 | }),
58 | new GlassEaselMiniprogramWebpackPlugin({
59 | // the path of the mini-program code directory
60 | path: path.join(__dirname, 'src'),
61 | // the resouce files that should be copied to the dist directory
62 | resourceFilePattern: /\.(jpg|jpeg|png|gif|html)$/,
63 | // the default entry
64 | defaultEntry: 'pages/index/index',
65 | }),
66 | ],
67 | },
68 | ]
69 |
--------------------------------------------------------------------------------
/gulpfile.ts:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp'
2 | import clean from 'gulp-clean'
3 | import tsc from 'gulp-typescript'
4 | import path from 'path'
5 | import swc from 'gulp-swc'
6 | import esbuild from 'gulp-esbuild'
7 | import watch from 'gulp-watch'
8 |
9 | import config from './config'
10 |
11 | const {
12 | srcPath,
13 | esbuildOptions,
14 | swcOptions,
15 | bundleInDemoPath,
16 | bundlePath,
17 | typesPath,
18 | tsConfigPath,
19 | swcBuildPath,
20 | entry,
21 | } = config
22 |
23 | const gen_tsc = () => {
24 | return tsc.createProject(tsConfigPath)
25 | }
26 |
27 | gulp.task('clean-dev-bundle', () => {
28 | return gulp.src(bundleInDemoPath, { allowEmpty: true }).pipe(clean())
29 | })
30 |
31 | gulp.task('clean-demo-dev-bundle', () => {
32 | return gulp.src(bundleInDemoPath, { allowEmpty: true }).pipe(clean())
33 | })
34 |
35 | gulp.task('clean-bundle', () => {
36 | return gulp.src(bundlePath, { allowEmpty: true }).pipe(clean())
37 | })
38 |
39 | gulp.task('clean-dts', () => {
40 | return gulp.src(typesPath, { allowEmpty: true }).pipe(clean())
41 | })
42 |
43 | gulp.task('gen-dts', () => {
44 | const tsc = gen_tsc()
45 | return tsc.src().pipe(tsc()).pipe(gulp.dest(typesPath))
46 | })
47 |
48 | gulp.task('swc-ts-2-js', () => {
49 | return gulp.src(path.resolve(srcPath, '*.ts')).pipe(swc(swcOptions)).pipe(gulp.dest(swcBuildPath))
50 | })
51 |
52 | gulp.task('esbuild-bundle', () => {
53 | return gulp
54 | .src(path.resolve(swcBuildPath, `${entry}.js`))
55 | .pipe(esbuild(esbuildOptions))
56 | .pipe(gulp.dest(bundlePath))
57 | })
58 |
59 | gulp.task('copy-2-demo', () => {
60 | return gulp.src(path.resolve(swcBuildPath, '*.js')).pipe(gulp.dest(bundleInDemoPath))
61 | })
62 |
63 | gulp.task('watch', () => {
64 | const ts_file = path.resolve(srcPath, '*.ts')
65 | const watcher = watch(ts_file, gulp.series('dev'))
66 | watcher.on('change', function (path, stats) {
67 | console.log(`File ${path} was changed`)
68 | })
69 | })
70 |
71 | // // build for develop
72 | // gulp.task('dev', gulp.series('clean-dev-bundle', 'clean-demo-dev-bundle', 'swc-ts-2-js', 'copy-2-demo'))
73 | // // build for develop & watch
74 | // gulp.task('dev-watch', gulp.series('demo-watch'))
75 | // generate .d.ts
76 | gulp.task('dts', gulp.series('clean-dts', 'gen-dts'))
77 | // build for publish
78 | gulp.task('default', gulp.series('clean-bundle', 'swc-ts-2-js', 'esbuild-bundle', 'dts'))
79 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mobx-miniprogram-bindings",
3 | "version": "5.1.1",
4 | "description": "Mobx binding utils for WeChat miniprogram",
5 | "main": "dist/index.js",
6 | "types": "types/src/index.d.ts",
7 | "files": [
8 | "src",
9 | "types",
10 | "dist",
11 | "LICENSE",
12 | "package.json",
13 | "README.md"
14 | ],
15 | "scripts": {
16 | "build": "gulp",
17 | "gen_dts": "gulp dts",
18 | "test": "jest ./test/* --bail",
19 | "coverage": "jest ./test/* --coverage --bail",
20 | "lint": "eslint . --fix"
21 | },
22 | "miniprogram": "dist",
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/wechat-miniprogram/mobx-miniprogram-bindings.git"
26 | },
27 | "keywords": [
28 | "mobx",
29 | "wechat",
30 | "miniprogram"
31 | ],
32 | "author": "wechat-miniprogram",
33 | "license": "MIT",
34 | "bugs": {
35 | "url": "https://github.com/wechat-miniprogram/mobx-miniprogram-bindings/issues"
36 | },
37 | "homepage": "https://github.com/wechat-miniprogram/mobx-miniprogram-bindings#readme",
38 | "devDependencies": {
39 | "@babel/runtime": "^7.24.5",
40 | "@swc/core": "^1.5.2",
41 | "@types/jest": "^29.5.12",
42 | "@types/node": "^20.12.8",
43 | "@typescript-eslint/eslint-plugin": "^7.8.0",
44 | "@typescript-eslint/parser": "^7.8.0",
45 | "colors": "^1.4.0",
46 | "esbuild": "^0.20.2",
47 | "eslint": "^8.56.0",
48 | "eslint-config-airbnb-base": "15.0.0",
49 | "eslint-config-prettier": "^9.1.0",
50 | "eslint-plugin-import": "^2.29.1",
51 | "eslint-plugin-node": "^11.1.0",
52 | "eslint-plugin-prettier": "^5.1.3",
53 | "eslint-plugin-promise": "^6.1.1",
54 | "glass-easel": "0.12.1",
55 | "glass-easel-miniprogram-adapter": "0.12.1",
56 | "glass-easel-template-compiler": "0.12.1",
57 | "gulp": "^5.0.0",
58 | "gulp-clean": "^0.4.0",
59 | "gulp-esbuild": "^0.12.0",
60 | "gulp-swc": "^2.1.0",
61 | "gulp-typescript": "6.0.0-alpha.1",
62 | "gulp-watch": "^5.0.1",
63 | "husky": "^9.0.11",
64 | "jest": "^29.7.0",
65 | "jest-environment-jsdom": "^29.7.0",
66 | "miniprogram-api-typings": "^4.0.1",
67 | "miniprogram-computed": "^4.4.0",
68 | "mobx-miniprogram": "^6.0.0",
69 | "prettier": "3.2.5",
70 | "ts-jest": "^29.1.2",
71 | "ts-node": "^10.9.2",
72 | "typescript": "^5.4.5"
73 | },
74 | "peerDependencies": {
75 | "miniprogram-api-typings": "^4.0.1",
76 | "mobx-miniprogram": "^6.0.0"
77 | },
78 | "husky": {
79 | "hooks": {
80 | "pre-commit": "npm run lint"
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/behavior.ts:
--------------------------------------------------------------------------------
1 | import 'miniprogram-api-typings'
2 | import { IStoreBindings } from './index'
3 | import { createActions, createDataFieldsReactions, StoreBindingsManager } from './core'
4 |
5 | type TDefFields = WechatMiniprogram.Component.TrivialOption & {
6 | storeBindings?: IStoreBindings | Array>
7 | }
8 |
9 | type UninitializedThis = {
10 | updateStoreBindings: () => void
11 | _mobxMiniprogramBindings: (() => IStoreBindings) | StoreBindingsManager | StoreBindingsManager[] | null
12 | }
13 |
14 | type InitializedThis = {
15 | updateStoreBindings: () => void
16 | _mobxMiniprogramBindings: StoreBindingsManager | StoreBindingsManager[] | null
17 | }
18 |
19 | export const behavior = Behavior({
20 | definitionFilter: (defFields: TDefFields) => {
21 | defFields.methods = defFields.methods || {}
22 | const { storeBindings } = defFields
23 | defFields.storeBindings = undefined
24 | if (storeBindings) {
25 | defFields.methods._mobxMiniprogramBindings = () => {
26 | return storeBindings
27 | }
28 | if (Array.isArray(storeBindings)) {
29 | storeBindings.forEach((binding) => {
30 | createActions(defFields.methods, binding)
31 | })
32 | } else {
33 | createActions(defFields.methods, storeBindings)
34 | }
35 | }
36 | },
37 | lifetimes: {
38 | attached() {
39 | const self = this as unknown as UninitializedThis
40 | if (typeof self._mobxMiniprogramBindings !== 'function') return
41 | const storeBindings = self._mobxMiniprogramBindings()
42 | if (!storeBindings) {
43 | self._mobxMiniprogramBindings = null
44 | return
45 | }
46 | if (Array.isArray(storeBindings)) {
47 | self._mobxMiniprogramBindings = storeBindings.map((item) => {
48 | const ret = createDataFieldsReactions(self, item)
49 | ret.updateStoreBindings()
50 | return ret
51 | })
52 | } else {
53 | self._mobxMiniprogramBindings = createDataFieldsReactions(this, storeBindings)
54 | self._mobxMiniprogramBindings.updateStoreBindings()
55 | }
56 | },
57 | detached() {
58 | const self = this as unknown as InitializedThis
59 | if (self._mobxMiniprogramBindings) {
60 | if (Array.isArray(self._mobxMiniprogramBindings)) {
61 | self._mobxMiniprogramBindings.forEach((item) => {
62 | item.destroyStoreBindings()
63 | })
64 | } else {
65 | self._mobxMiniprogramBindings.destroyStoreBindings()
66 | }
67 | }
68 | },
69 | },
70 | methods: {
71 | updateStoreBindings() {
72 | const self = this as unknown as UninitializedThis
73 | if (self._mobxMiniprogramBindings && typeof self._mobxMiniprogramBindings !== 'function') {
74 | if (Array.isArray(self._mobxMiniprogramBindings)) {
75 | self._mobxMiniprogramBindings.forEach((item) => {
76 | item.updateStoreBindings()
77 | })
78 | } else {
79 | self._mobxMiniprogramBindings.updateStoreBindings()
80 | }
81 | }
82 | },
83 | },
84 | })
85 |
--------------------------------------------------------------------------------
/src/core.ts:
--------------------------------------------------------------------------------
1 | import { reaction, comparer, toJS } from 'mobx-miniprogram'
2 | import { IStoreBindings } from './index'
3 |
4 | export const createActions = >(methods, options: IStoreBindings) => {
5 | const { store, actions } = options
6 | if (!actions) return
7 |
8 | // for array-typed fields definition
9 | if (typeof store === 'undefined') {
10 | throw new Error('[mobx-miniprogram] no store specified')
11 | }
12 |
13 | if (Array.isArray(actions)) {
14 | actions.forEach((field) => {
15 | if (methods[field]) {
16 | throw new Error('[mobx-miniprogram] multiple action definition')
17 | }
18 | methods[field] = (...args) => {
19 | return (store[field] as (...args: unknown[]) => unknown)(...args)
20 | }
21 | })
22 | } else if (typeof actions === 'object') {
23 | Object.keys(actions).forEach((field) => {
24 | const def = actions[field]
25 | if (typeof field !== 'string' && typeof field !== 'number') {
26 | throw new Error('[mobx-miniprogram] unrecognized field definition')
27 | }
28 | methods[field] = (...args) => {
29 | return (store[def] as (...args: unknown[]) => unknown)(...args)
30 | }
31 | })
32 | }
33 | }
34 |
35 | export type StoreBindingsManager = {
36 | updateStoreBindings: () => void
37 | destroyStoreBindings: () => void
38 | }
39 |
40 | export const createDataFieldsReactions = >(
41 | target,
42 | options: Omit, 'actions'>,
43 | ): StoreBindingsManager => {
44 | const { store, fields, structuralComparison } = options
45 |
46 | // if use namespace
47 | let namespace = options.namespace || ''
48 | if (namespace && typeof namespace !== 'string') {
49 | throw new Error('[mobx-miniprogram] namespace only expect string')
50 | }
51 | namespace = namespace.replace(/ /gm, '')
52 |
53 | let namespaceData = Object.assign({}, target[namespace])
54 |
55 | const useNamespace = (): boolean => {
56 | return namespace !== ''
57 | }
58 |
59 | // choose equal method
60 | const equals = structuralComparison ? comparer.structural : undefined
61 |
62 | // setData combination
63 | let pendingSetData: Record | null = null
64 |
65 | const applySetData = () => {
66 | if (pendingSetData === null) return
67 | const data = pendingSetData
68 | pendingSetData = null
69 | target.setData(data)
70 | }
71 |
72 | const scheduleSetData = (field, value) => {
73 | if (!pendingSetData) {
74 | pendingSetData = {}
75 | if (typeof wx !== 'undefined') wx.nextTick(applySetData)
76 | else Promise.resolve().then(applySetData)
77 | }
78 | if (useNamespace()) {
79 | namespaceData = {
80 | ...namespaceData,
81 | [field]: toJS(value),
82 | }
83 | pendingSetData[namespace] = namespaceData
84 | } else {
85 | pendingSetData[field] = toJS(value)
86 | }
87 | }
88 |
89 | // handling fields
90 | let reactions: (() => void)[] = []
91 |
92 | if (Array.isArray(fields)) {
93 | // for array-typed fields definition
94 | if (typeof store === 'undefined') {
95 | throw new Error('[mobx-miniprogram] no store specified')
96 | }
97 | reactions = fields.map((field) => {
98 | return reaction(
99 | () => store[field],
100 | (value) => {
101 | scheduleSetData(field, value)
102 | },
103 | {
104 | equals,
105 | fireImmediately: true,
106 | },
107 | )
108 | })
109 | } else if (typeof fields === 'object' && fields) {
110 | // for object-typed fields definition
111 | reactions = Object.keys(fields).map((field) => {
112 | const def = fields[field]
113 | if (typeof def === 'function') {
114 | return reaction(
115 | () => def.call(target, store),
116 | (value) => {
117 | scheduleSetData(field, value)
118 | },
119 | {
120 | equals,
121 | fireImmediately: true,
122 | },
123 | )
124 | }
125 | if (typeof field !== 'string' && typeof field !== 'number') {
126 | throw new Error('[mobx-miniprogram] unrecognized field definition')
127 | }
128 | if (typeof store === 'undefined') {
129 | throw new Error('[mobx-miniprogram] no store specified')
130 | }
131 | return reaction(
132 | () => store[def],
133 | (value) => {
134 | scheduleSetData(String(field), value)
135 | },
136 | {
137 | equals,
138 | fireImmediately: true,
139 | },
140 | )
141 | })
142 | }
143 |
144 | const destroyStoreBindings = () => {
145 | reactions.forEach((reaction) => reaction())
146 | }
147 |
148 | return {
149 | updateStoreBindings: applySetData,
150 | destroyStoreBindings,
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | "use strict";var A=(r,n)=>()=>(n||r((n={exports:{}}).exports,n),n.exports);var j=A(()=>{});var w=A(M=>{"use strict";Object.defineProperty(M,"__esModule",{value:!0});var J=M,F={createActions:function(){return T},createDataFieldsReactions:function(){return W}};for(x in F)Object.defineProperty(J,x,{enumerable:!0,get:F[x]});var x,l=require("mobx-miniprogram");function _(r,n){(n==null||n>r.length)&&(n=r.length);for(var e=0,i=Array(n);e{"use strict";Object.defineProperty(P,"__esModule",{value:!0}),Object.defineProperty(P,"behavior",{enumerable:!0,get:function(){return U}}),j();var v=w(),U=Behavior({definitionFilter:function(r){r.methods=r.methods||{};var n=r.storeBindings;r.storeBindings=void 0,n&&(r.methods._mobxMiniprogramBindings=function(){return n},Array.isArray(n)?n.forEach(function(e){(0,v.createActions)(r.methods,e)}):(0,v.createActions)(r.methods,n))},lifetimes:{attached:function(){var r=this;if(typeof r._mobxMiniprogramBindings=="function"){var n=r._mobxMiniprogramBindings();if(!n){r._mobxMiniprogramBindings=null;return}Array.isArray(n)?r._mobxMiniprogramBindings=n.map(function(e){var i=(0,v.createDataFieldsReactions)(r,e);return i.updateStoreBindings(),i}):(r._mobxMiniprogramBindings=(0,v.createDataFieldsReactions)(this,n),r._mobxMiniprogramBindings.updateStoreBindings())}},detached:function(){this._mobxMiniprogramBindings&&(Array.isArray(this._mobxMiniprogramBindings)?this._mobxMiniprogramBindings.forEach(function(r){r.destroyStoreBindings()}):this._mobxMiniprogramBindings.destroyStoreBindings())}},methods:{updateStoreBindings:function(){this._mobxMiniprogramBindings&&typeof this._mobxMiniprogramBindings!="function"&&(Array.isArray(this._mobxMiniprogramBindings)?this._mobxMiniprogramBindings.forEach(function(r){r.updateStoreBindings()}):this._mobxMiniprogramBindings.updateStoreBindings())}}})});Object.defineProperty(exports,"__esModule",{value:!0});var $=exports,z={BehaviorWithStore:function(){return H},ComponentWithStore:function(){return G},createStoreBindings:function(){return K},initStoreBindings:function(){return N},storeBindingsBehavior:function(){return L}};for(E in z)Object.defineProperty($,E,{enumerable:!0,get:z[E]});var E;j();var q=C(),D=w();function G(r){return Array.isArray(r.behaviors)||(r.behaviors=[]),r.behaviors.unshift(q.behavior),Component(r)}function H(r){return Array.isArray(r.behaviors)||(r.behaviors=[]),r.behaviors.unshift(q.behavior),Behavior(r)}var K=function(r,n){return(0,D.createActions)(r,n),(0,D.createDataFieldsReactions)(r,n)},L=q.behavior,N=function(r,n){var e,i=r.self,o=r.lifetime;return o("attached",function(){(e=(0,D.createDataFieldsReactions)(i,n)).updateStoreBindings()}),o("detached",function(){e.destroyStoreBindings()}),{updateStoreBindings:function(){e&&e.updateStoreBindings()}}};
2 |
--------------------------------------------------------------------------------
/types/src/index.d.ts:
--------------------------------------------------------------------------------
1 | import 'miniprogram-api-typings';
2 | import { StoreBindingsManager } from './core';
3 | import type * as adapter from 'glass-easel-miniprogram-adapter';
4 | type Action = string;
5 | export interface IStoreBindings> {
6 | namespace?: string;
7 | store: T;
8 | fields: (keyof T)[] | {
9 | [k: string]: (keyof T | ((...args: any) => any));
10 | };
11 | actions: (keyof T)[] | {
12 | [k: Action]: keyof T;
13 | };
14 | structuralComparison?: boolean;
15 | }
16 | type StoreData> = T['fields'] extends string[] ? {
17 | [k in T['fields'][number]]: T['store'][k];
18 | } : T['fields'] extends {
19 | [k: Action]: string | ((...args: any) => any);
20 | } ? {
21 | [k in keyof T['fields']]: (T['fields'][k] extends (...args: any) => any ? ReturnType : T['fields'][k] extends string ? T['store'][T['fields'][k]] : unknown);
22 | } : unknown;
23 | type StoreAction> = T['actions'] extends string[] ? {
24 | [k in T['actions'][number]]: T['store'][k];
25 | } : T['actions'] extends {
26 | [k: Action]: string;
27 | } ? {
28 | [k in keyof T['actions']]: T['store'][T['actions'][k]];
29 | } : unknown;
30 | type StoreOptions, TData extends WechatMiniprogram.Component.DataOption, TProperty extends WechatMiniprogram.Component.PropertyOption, TMethod extends WechatMiniprogram.Component.MethodOption, TBehavior extends WechatMiniprogram.Component.BehaviorOption, TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {}> = (Partial> & Partial> & Partial> & Partial> & Partial & Partial & {
31 | storeBindings: TStoreBindings;
32 | }) & ThisType, TProperty, TMethod & StoreAction, TBehavior, TCustomInstanceProperty>>;
33 | type StoreListOptions[], TData extends WechatMiniprogram.Component.DataOption, TProperty extends WechatMiniprogram.Component.PropertyOption, TMethod extends WechatMiniprogram.Component.MethodOption, TBehavior extends WechatMiniprogram.Component.BehaviorOption, TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {}> = (Partial> & Partial> & Partial> & Partial> & Partial & Partial & {
34 | storeBindings: TStoreBindings;
35 | }) & ThisType & StoreData & StoreData & StoreData & StoreData & StoreData & StoreData & StoreData, TProperty, TMethod & StoreAction & StoreAction & StoreAction & StoreAction & StoreAction & StoreAction & StoreAction & StoreAction, TBehavior, TCustomInstanceProperty>>;
36 | export declare function ComponentWithStore, TData extends WechatMiniprogram.Component.DataOption, TProperty extends WechatMiniprogram.Component.PropertyOption, TMethod extends WechatMiniprogram.Component.MethodOption, TBehavior extends WechatMiniprogram.Component.BehaviorOption, TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {}>(options: StoreOptions): string;
37 | export declare function ComponentWithStore[], TData extends WechatMiniprogram.Component.DataOption, TProperty extends WechatMiniprogram.Component.PropertyOption, TMethod extends WechatMiniprogram.Component.MethodOption, TBehavior extends WechatMiniprogram.Component.BehaviorOption, TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {}>(options: StoreListOptions): string;
38 | export declare function BehaviorWithStore, TData extends WechatMiniprogram.Component.DataOption, TProperty extends WechatMiniprogram.Component.PropertyOption, TMethod extends WechatMiniprogram.Component.MethodOption, TBehavior extends WechatMiniprogram.Component.BehaviorOption, TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {}>(options: StoreOptions): string;
39 | export declare function BehaviorWithStore[], TData extends WechatMiniprogram.Component.DataOption, TProperty extends WechatMiniprogram.Component.PropertyOption, TMethod extends WechatMiniprogram.Component.MethodOption, TBehavior extends WechatMiniprogram.Component.BehaviorOption, TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {}>(options: StoreListOptions): string;
40 | export declare const createStoreBindings: >(target: any, options: IStoreBindings) => StoreBindingsManager;
41 | export declare const storeBindingsBehavior: WechatMiniprogram.Behavior.BehaviorIdentifier;
44 | export type InitializedStoreBindings = {
45 | updateStoreBindings: () => void;
46 | };
47 | export declare const initStoreBindings: >(ctx: adapter.builder.BuilderContext, options: Omit, "actions">) => InitializedStoreBindings;
48 | export {};
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 小程序的 MobX 绑定辅助库
2 |
3 | 小程序的 MobX 绑定辅助库。
4 |
5 | > 此 behavior 依赖开发者工具的 npm 构建。具体详情可查阅 [官方 npm 文档](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html) 。
6 |
7 | ## 使用方法
8 |
9 | 需要小程序基础库版本 >= 2.11.0 的环境。
10 |
11 | 具体的示例完整代码,可以参考 [examples](./examples/) 。
12 |
13 | 1. 安装 `mobx-miniprogram` 和 `mobx-miniprogram-bindings` :
14 |
15 | ```shell
16 | npm install --save mobx-miniprogram mobx-miniprogram-bindings
17 | ```
18 |
19 | 2. 创建 MobX Store。
20 |
21 | ```js
22 | // store.js
23 | import { observable, action } from 'mobx-miniprogram'
24 |
25 | // 创建 store 时可以采用任何 mobx 的接口风格
26 | // 这里以传统的 observable 风格为例
27 |
28 | export const store = observable({
29 | // 数据字段
30 | numA: 1,
31 | numB: 2,
32 |
33 | // 计算属性
34 | get sum() {
35 | return this.numA + this.numB
36 | },
37 |
38 | // actions
39 | update: action(function () {
40 | const sum = this.sum
41 | this.numA = this.numB
42 | this.numB = sum
43 | }),
44 | })
45 | ```
46 |
47 | 3. 在 Component 构造器中使用:
48 |
49 | ```js
50 | import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
51 | import { store } from './store'
52 |
53 | Component({
54 | behaviors: [storeBindingsBehavior], // 添加这个 behavior
55 | data: {
56 | someData: '...',
57 | },
58 | storeBindings: {
59 | store,
60 | fields: {
61 | numA: () => store.numA,
62 | numB: (store) => store.numB,
63 | sum: 'sum',
64 | },
65 | actions: {
66 | buttonTap: 'update',
67 | },
68 | },
69 | methods: {
70 | myMethod() {
71 | this.data.sum // 来自于 MobX store 的字段
72 | },
73 | },
74 | })
75 | ```
76 |
77 | ## TypeScript 接口
78 |
79 | 在 TypeScript 下,可以使用 `ComponentWithStore` 接口。它会自动处理一些类型问题。注意:
80 |
81 | * 使用这个接口时,不要在 behaviors 中额外引入 `storeBindingsBehavior` ;
82 | * `fields` 和 `actions` 末尾需要加上 `as const` 以便更好的类型推导;
83 | * `storeBindings` 如果是一个数组,也要在数组后加上 `as const` 。
84 |
85 | ```js
86 | import { ComponentWithStore } from 'mobx-miniprogram-bindings'
87 |
88 | ComponentWithStore({
89 | data: {
90 | someData: '...',
91 | },
92 | storeBindings: {
93 | store,
94 | fields: ['numA', 'numB', 'sum'] as const,
95 | actions: {
96 | buttonTap: 'update',
97 | } as const,
98 | },
99 | })
100 | ```
101 |
102 | `BehaviorWithStore` 接口类似。
103 |
104 | ```js
105 | import { BehaviorWithStore } from 'mobx-miniprogram-bindings'
106 |
107 | export const testBehavior = BehaviorWithStore({
108 | storeBindings: {
109 | store,
110 | fields: ['numA', 'numB', 'sum'] as const,
111 | actions: ['update'] as const,
112 | },
113 | })
114 | ```
115 |
116 | 目前 TypeScript 接口定义依赖于 `miniprogram-api-typings ^4.0.0` 。
117 | (如使用老版本的 api-typings ,请使用本项目的 v4 或 v3 版本。)
118 |
119 | ## glass-easel Chaining API 接口
120 |
121 | 使用 glass-easel Chaining API 时,使用 `initStoreBindings` 更友好。
122 |
123 | ```js
124 | import { initStoreBindings } from 'mobx-miniprogram-bindings'
125 |
126 | Component()
127 | .init((ctx) => {
128 | const { listener } = ctx
129 | initStoreBindings(ctx, {
130 | store,
131 | fields: ['numA', 'numB', 'sum'],
132 | })
133 | const buttonTap = listener(() => {
134 | store.update()
135 | })
136 | return { buttonTap }
137 | })
138 | .register()
139 | ```
140 |
141 | ## 具体接口说明
142 |
143 | 将页面、自定义组件和 store 绑定有两种方式: **behavior 绑定** 和 **手工绑定** 。
144 |
145 | ### behavior 绑定
146 |
147 | **behavior 绑定** 适用于 `Component` 构造器。做法:使用 `storeBindingsBehavior` 这个 behavior 和 `storeBindings` 定义段。
148 |
149 | ```js
150 | import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
151 |
152 | Component({
153 | behaviors: [storeBindingsBehavior],
154 | storeBindings: {
155 | /* 绑定配置(见下文) */
156 | },
157 | })
158 | ```
159 |
160 | 也可以把 `storeBindings` 设置为一个数组,这样可以同时绑定多个 `store` :
161 |
162 | ```js
163 | import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
164 |
165 | Component({
166 | behaviors: [storeBindingsBehavior],
167 | storeBindings: [
168 | {
169 | /* 绑定配置 1 */
170 | },
171 | {
172 | /* 绑定配置 2 */
173 | },
174 | ],
175 | })
176 | ```
177 |
178 | ### 手工绑定
179 |
180 | **手工绑定** 更加灵活,适用于 store 需要在 `onLoad` (自定义组件 attached )时才能确定的情况。目前,在 Page 构造器内使用时,也必须用这种方式。
181 |
182 | 做法:使用 `createStoreBindings` 创建绑定,它会返回一个包含清理函数的对象用于取消绑定。
183 |
184 | 注意:在页面 onUnload (自定义组件 detached )时一定要调用清理函数,否则将导致内存泄漏!
185 |
186 | ```js
187 | import { createStoreBindings } from 'mobx-miniprogram-bindings'
188 |
189 | Page({
190 | onLoad() {
191 | this.storeBindings = createStoreBindings(this, {
192 | /* 绑定配置(见下文) */
193 | })
194 | },
195 | onUnload() {
196 | this.storeBindings.destroyStoreBindings()
197 | },
198 | })
199 | ```
200 |
201 | ### 绑定配置
202 |
203 | 无论使用哪种绑定方式,都必须提供一个绑定配置对象。这个对象包含的字段如下:
204 |
205 | | 字段名 | 类型 | 含义 |
206 | | ------- | -------------------- | ---------------------------- |
207 | | store | 一个 MobX observable | 默认的 MobX store |
208 | | fields | 数组或者对象 | 用于指定需要绑定的 data 字段 |
209 | | actions | 数组或者对象 | 用于指定需要映射的 actions |
210 |
211 | #### `fields`
212 |
213 | `fields` 有三种形式:
214 |
215 | - 数组形式:指定 data 中哪些字段来源于 `store` 。例如 `['numA', 'numB', 'sum']` 。
216 | - 映射形式:指定 data 中哪些字段来源于 `store` 以及它们在 `store` 中对应的名字。例如 `{ a: 'numA', b: 'numB' }` ,此时 `this.data.a === store.numA` `this.data.b === store.numB` 。
217 | - 函数形式:指定 data 中每个字段的计算方法。例如 `{ a: () => store.numA, b: () => anotherStore.numB }` ,此时 `this.data.a === store.numA` `this.data.b === anotherStore.numB` 。
218 |
219 | 上述三种形式中,映射形式和函数形式可以在一个配置中同时使用。
220 |
221 | 如果仅使用了函数形式,那么 `store` 字段可以为空,否则 `store` 字段必填。
222 |
223 | #### `actions`
224 |
225 | `actions` 可以用于将 store 中的一些 actions 放入页面或自定义组件的 this 下,来方便触发一些 actions 。有两种形式:
226 |
227 | - 数组形式:例如 `['update']` ,此时 `this.update === store.update` 。
228 | - 映射形式:例如 `{ buttonTap: 'update' }` ,此时 `this.buttonTap === store.update` 。
229 |
230 | 只要 `actions` 不为空,则 `store` 字段必填。
231 |
232 | ## 注意事项
233 |
234 | ### 延迟更新与立刻更新
235 |
236 | 为了提升性能,在 store 中的字段被更新后,并不会立刻同步更新到 `this.data` 上,而是等到下个 `wx.nextTick` 调用时才更新。(这样可以显著减少 setData 的调用次数。)
237 |
238 | 如果需要立刻更新,可以调用:
239 |
240 | - `this.updateStoreBindings()` (在 **behavior 绑定** 中)
241 | - `this.storeBindings.updateStoreBindings()` (在 **手工绑定** 中)
242 |
243 | ### 与 miniprogram-computed 一起使用
244 |
245 | 与 [miniprogram-computed](https://github.com/wechat-miniprogram/computed) 时,在 behaviors 列表中 `computedBehavior` 必须在后面:
246 |
247 | ```js
248 | Component({
249 | behaviors: [storeBindingsBehavior, computedBehavior],
250 | /* ... */
251 | })
252 | ```
253 |
254 | ### 关于部分更新
255 |
256 | 如果只是更新对象中的一部分(子字段),是不会引发界面变化的!例如:
257 |
258 | ```js
259 | Component({
260 | behaviors: [storeBindingsBehavior],
261 | storeBindings: {
262 | store,
263 | fields: ['someObject'],
264 | },
265 | })
266 | ```
267 |
268 | 如果尝试在 `store` 中:
269 |
270 | ```js
271 | this.someObject.someField = 'xxx'
272 | ```
273 |
274 | 这样是不会触发界面更新的。请考虑改成:
275 |
276 | ```js
277 | this.someObject = Object.assign({}, this.someObject, { someField: 'xxx' })
278 | ```
279 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import 'miniprogram-api-typings'
2 | import { behavior } from './behavior'
3 | import { StoreBindingsManager, createActions, createDataFieldsReactions } from './core'
4 | import type * as adapter from 'glass-easel-miniprogram-adapter'
5 |
6 | type Action = string
7 |
8 | export interface IStoreBindings> {
9 | namespace?: string
10 | store: T
11 | fields: (keyof T)[] | { [k: string]: (keyof T | ((...args: any) => any)) }
12 | actions: (keyof T)[] | { [k: Action]: keyof T }
13 | structuralComparison?: boolean
14 | }
15 |
16 | type StoreData> = T['fields'] extends string[]
17 | ? { [k in T['fields'][number]]: T['store'][k] }
18 | : T['fields'] extends { [k: Action]: string | ((...args: any) => any) }
19 | ? { [k in keyof T['fields']]: (
20 | T['fields'][k] extends (...args: any) => any
21 | ? ReturnType
22 | : T['fields'][k] extends string
23 | ? T['store'][T['fields'][k]]
24 | : unknown
25 | )}
26 | : unknown
27 |
28 | type StoreAction> = T['actions'] extends string[]
29 | ? { [k in T['actions'][number]]: T['store'][k] }
30 | : T['actions'] extends { [k: Action]: string }
31 | ? { [k in keyof T['actions']]: T['store'][T['actions'][k]] }
32 | : unknown
33 |
34 | type StoreOptions<
35 | TStoreBindings extends IStoreBindings,
36 | TData extends WechatMiniprogram.Component.DataOption,
37 | TProperty extends WechatMiniprogram.Component.PropertyOption,
38 | TMethod extends WechatMiniprogram.Component.MethodOption,
39 | TBehavior extends WechatMiniprogram.Component.BehaviorOption,
40 | TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {},
41 | > = (Partial> &
42 | Partial> &
43 | Partial> &
44 | Partial> &
45 | Partial &
46 | Partial & {
47 | storeBindings: TStoreBindings
48 | }) &
49 | ThisType<
50 | WechatMiniprogram.Component.Instance<
51 | TData & StoreData,
52 | TProperty,
53 | TMethod & StoreAction,
54 | TBehavior,
55 | TCustomInstanceProperty
56 | >
57 | >
58 |
59 | type StoreListOptions<
60 | TStoreBindings extends IStoreBindings[],
61 | TData extends WechatMiniprogram.Component.DataOption,
62 | TProperty extends WechatMiniprogram.Component.PropertyOption,
63 | TMethod extends WechatMiniprogram.Component.MethodOption,
64 | TBehavior extends WechatMiniprogram.Component.BehaviorOption,
65 | TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {},
66 | > = (Partial> &
67 | Partial> &
68 | Partial> &
69 | Partial> &
70 | Partial &
71 | Partial & {
72 | storeBindings: TStoreBindings
73 | }) &
74 | ThisType<
75 | WechatMiniprogram.Component.Instance<
76 | TData
77 | & StoreData
78 | & StoreData
79 | & StoreData
80 | & StoreData
81 | & StoreData
82 | & StoreData
83 | & StoreData
84 | & StoreData,
85 | TProperty,
86 | TMethod
87 | & StoreAction
88 | & StoreAction
89 | & StoreAction
90 | & StoreAction
91 | & StoreAction
92 | & StoreAction
93 | & StoreAction
94 | & StoreAction,
95 | TBehavior,
96 | TCustomInstanceProperty
97 | >
98 | >
99 |
100 | export function ComponentWithStore<
101 | TStoreBindings extends IStoreBindings,
102 | TData extends WechatMiniprogram.Component.DataOption,
103 | TProperty extends WechatMiniprogram.Component.PropertyOption,
104 | TMethod extends WechatMiniprogram.Component.MethodOption,
105 | TBehavior extends WechatMiniprogram.Component.BehaviorOption,
106 | TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {},
107 | >(options: StoreOptions): string;
108 | export function ComponentWithStore<
109 | TStoreBindings extends IStoreBindings[],
110 | TData extends WechatMiniprogram.Component.DataOption,
111 | TProperty extends WechatMiniprogram.Component.PropertyOption,
112 | TMethod extends WechatMiniprogram.Component.MethodOption,
113 | TBehavior extends WechatMiniprogram.Component.BehaviorOption,
114 | TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {},
115 | >(options: StoreListOptions): string;
116 | export function ComponentWithStore<
117 | TStoreBindings extends any,
118 | TData extends WechatMiniprogram.Component.DataOption,
119 | TProperty extends WechatMiniprogram.Component.PropertyOption,
120 | TMethod extends WechatMiniprogram.Component.MethodOption,
121 | TBehavior extends WechatMiniprogram.Component.BehaviorOption,
122 | TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {},
123 | >(options: StoreListOptions): string {
124 | if (!Array.isArray(options.behaviors)) {
125 | options.behaviors = [] as any
126 | }
127 | (options.behaviors as any).unshift(behavior)
128 | return Component(options)
129 | }
130 |
131 | export function BehaviorWithStore<
132 | TStoreBindings extends IStoreBindings,
133 | TData extends WechatMiniprogram.Component.DataOption,
134 | TProperty extends WechatMiniprogram.Component.PropertyOption,
135 | TMethod extends WechatMiniprogram.Component.MethodOption,
136 | TBehavior extends WechatMiniprogram.Component.BehaviorOption,
137 | TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {},
138 | >(options: StoreOptions): string;
139 | export function BehaviorWithStore<
140 | TStoreBindings extends IStoreBindings[],
141 | TData extends WechatMiniprogram.Component.DataOption,
142 | TProperty extends WechatMiniprogram.Component.PropertyOption,
143 | TMethod extends WechatMiniprogram.Component.MethodOption,
144 | TBehavior extends WechatMiniprogram.Component.BehaviorOption,
145 | TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {},
146 | >(options: StoreListOptions): string;
147 | export function BehaviorWithStore<
148 | TStoreBindings extends any,
149 | TData extends WechatMiniprogram.Component.DataOption,
150 | TProperty extends WechatMiniprogram.Component.PropertyOption,
151 | TMethod extends WechatMiniprogram.Component.MethodOption,
152 | TBehavior extends WechatMiniprogram.Component.BehaviorOption,
153 | TCustomInstanceProperty extends WechatMiniprogram.IAnyObject = {},
154 | >(options: StoreListOptions): string {
155 | if (!Array.isArray(options.behaviors)) {
156 | options.behaviors = [] as any
157 | }
158 | (options.behaviors as any).unshift(behavior)
159 | return Behavior(options)
160 | }
161 |
162 | export const createStoreBindings = >(target, options: IStoreBindings): StoreBindingsManager => {
163 | createActions(target, options)
164 | return createDataFieldsReactions(target, options)
165 | }
166 |
167 | export const storeBindingsBehavior = behavior
168 |
169 | export type InitializedStoreBindings = {
170 | updateStoreBindings: () => void
171 | }
172 |
173 | export const initStoreBindings = >(
174 | ctx: adapter.builder.BuilderContext,
175 | options: Omit, 'actions'>,
176 | ): InitializedStoreBindings => {
177 | const { self, lifetime } = ctx
178 |
179 | let storeBindings: StoreBindingsManager | undefined
180 | lifetime('attached', () => {
181 | storeBindings = createDataFieldsReactions(self, options)
182 | storeBindings.updateStoreBindings()
183 | })
184 | lifetime('detached', () => {
185 | storeBindings!.destroyStoreBindings()
186 | })
187 |
188 | return {
189 | updateStoreBindings: () => {
190 | if (storeBindings) {
191 | storeBindings.updateStoreBindings()
192 | }
193 | },
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/test/index.test.ts:
--------------------------------------------------------------------------------
1 | import * as adapter from 'glass-easel-miniprogram-adapter'
2 | import { configure, observable, action, makeAutoObservable } from 'mobx-miniprogram'
3 | import { behavior as computedBehavior } from 'miniprogram-computed'
4 | import { ComponentWithStore, createStoreBindings, storeBindingsBehavior } from '../src'
5 | import { defineComponent, renderComponent, waitTick } from './env'
6 |
7 | // 不允许在动作外部修改状态
8 | configure({ enforceActions: 'observed' })
9 |
10 | const innerHTML = (component: adapter.component.GeneralComponent) => {
11 | return (component._$.$$ as unknown as HTMLElement).innerHTML
12 | }
13 |
14 | test('manually creation', async () => {
15 | const store = makeAutoObservable({
16 | numA: 1,
17 | numB: 2,
18 | get sum() {
19 | return this.numA + this.numB
20 | },
21 | update: function () {
22 | const sum = this.sum
23 | this.numA = this.numB
24 | this.numB = sum
25 | },
26 | })
27 |
28 | const component = renderComponent(undefined, '{{a}}+{{b}}={{c}}', (Component) => {
29 | Component({
30 | attached() {
31 | // @ts-ignore
32 | this.storeBindings = createStoreBindings(this, {
33 | store,
34 | fields: {
35 | a: 'numA',
36 | b: 'numB',
37 | c: 'sum',
38 | },
39 | actions: ['update'],
40 | })
41 | },
42 | detached() {
43 | // @ts-ignore
44 | this.storeBindings.destroyStoreBindings()
45 | },
46 | })
47 | }) as any
48 | await waitTick()
49 | expect(innerHTML(component)).toBe('1+2=3')
50 |
51 | component.update()
52 | component.storeBindings.updateStoreBindings()
53 | expect(innerHTML(component)).toBe('2+3=5')
54 | })
55 |
56 | test('declarative creation', async () => {
57 | const store = makeAutoObservable({
58 | numA: 1,
59 | numB: 2,
60 | get sum() {
61 | return this.numA + this.numB
62 | },
63 | update: function () {
64 | const sum = this.sum
65 | this.numA = this.numB
66 | this.numB = sum
67 | },
68 | })
69 |
70 | const component = renderComponent(
71 | undefined,
72 | '{{numA}}+{{numB}}={{sum}}',
73 | (Component) => {
74 | Component({
75 | behaviors: [storeBindingsBehavior],
76 | storeBindings: {
77 | store,
78 | fields: ['numA', 'numB', 'sum'],
79 | actions: { up: 'update' },
80 | },
81 | } as any)
82 | },
83 | ) as any
84 | await waitTick()
85 | expect(innerHTML(component)).toBe('1+2=3')
86 |
87 | component.up()
88 | component.updateStoreBindings()
89 | expect(innerHTML(component)).toBe('2+3=5')
90 | })
91 |
92 | test('declarative creation with page constructor', async () => {
93 | const store = makeAutoObservable({
94 | numA: 1,
95 | numB: 2,
96 | get sum() {
97 | return this.numA + this.numB
98 | },
99 | update: function () {
100 | const sum = this.sum
101 | this.numA = this.numB
102 | this.numB = sum
103 | },
104 | })
105 |
106 | const component = renderComponent(
107 | undefined,
108 | '{{numA}}+{{numB}}={{sum}}',
109 | (_Component, env) => {
110 | env.Page({
111 | behaviors: [storeBindingsBehavior],
112 | storeBindings: {
113 | store,
114 | fields: ['numA', 'numB', 'sum'],
115 | actions: { up: 'update' },
116 | },
117 | } as any)
118 | },
119 | ) as any
120 | await waitTick()
121 | expect(innerHTML(component)).toBe('1+2=3')
122 |
123 | component.up()
124 | component.updateStoreBindings()
125 | expect(innerHTML(component)).toBe('2+3=5')
126 | })
127 |
128 | test('destroy', async () => {
129 | const store = makeAutoObservable({
130 | numA: 1,
131 | numB: 2,
132 | get sum() {
133 | return this.numA + this.numB
134 | },
135 | update: function () {
136 | const sum = this.sum
137 | this.numA = this.numB
138 | this.numB = sum
139 | },
140 | })
141 |
142 | defineComponent('custom-comp', '{{numA}}+{{numB}}={{sum}}', (Component) => {
143 | Component({
144 | behaviors: [storeBindingsBehavior],
145 | storeBindings: {
146 | store,
147 | fields: ['numA', 'numB', 'sum'],
148 | actions: { update: 'update' },
149 | },
150 | } as any)
151 | })
152 | const component = renderComponent(undefined, '', (Component) => {
153 | Component({
154 | attached() {
155 | // @ts-ignore
156 | this.storeBindings = createStoreBindings(this, { store, fields: [], actions: [] })
157 | },
158 | detached() {
159 | // @ts-ignore
160 | this.storeBindings.destroyStoreBindings()
161 | },
162 | })
163 | }) as any
164 | await waitTick()
165 | expect(innerHTML(component)).toBe('1+2=3')
166 |
167 | store.update()
168 | await waitTick()
169 | expect(innerHTML(component)).toBe('2+3=5')
170 |
171 | adapter.glassEasel.Element.pretendDetached(component._$)
172 | store.update()
173 | expect(innerHTML(component)).toBe('2+3=5')
174 | })
175 |
176 | test('function-typed fields binding', async () => {
177 | const store = makeAutoObservable({
178 | numA: 1,
179 | numB: 2,
180 | get sum() {
181 | return this.numA + this.numB
182 | },
183 | update: function () {
184 | const sum = this.sum
185 | this.numA = this.numB
186 | this.numB = sum
187 | },
188 | })
189 |
190 | const component = renderComponent(undefined, '{{a}}+{{b}}={{s}}', (Component) => {
191 | Component({
192 | behaviors: [storeBindingsBehavior],
193 | storeBindings: {
194 | fields: {
195 | a: () => store.numA,
196 | b: () => store.numB,
197 | s: () => store.sum,
198 | },
199 | },
200 | } as any)
201 | }) as any
202 | await waitTick()
203 | expect(innerHTML(component)).toBe('1+2=3')
204 |
205 | store.update()
206 | await waitTick()
207 | expect(innerHTML(component)).toBe('2+3=5')
208 | })
209 |
210 | test('binding multi store in custom components', async () => {
211 | const storeA = makeAutoObservable({
212 | a_A: 1,
213 | b_A: 2,
214 | get sum_A() {
215 | return this.a_A + this.b_A
216 | },
217 | update: function () {
218 | this.a_A = this.a_A * 10
219 | this.b_A = this.b_A * 10
220 | },
221 | })
222 |
223 | const storeB = makeAutoObservable({
224 | a_B: 1,
225 | b_B: 2,
226 | get sum_B() {
227 | return this.a_B + this.b_B
228 | },
229 | update: function () {
230 | this.a_B = this.a_B * 20
231 | this.b_B = this.b_B * 20
232 | },
233 | })
234 |
235 | const component = renderComponent(
236 | undefined,
237 | '{{a_A}}+{{b_A}}={{sum_A}}{{a_B}}+{{b_B}}={{sum_B}}',
238 | (Component) => {
239 | Component({
240 | behaviors: [storeBindingsBehavior],
241 | storeBindings: [
242 | {
243 | store: storeA,
244 | fields: ['a_A', 'b_A', 'sum_A'],
245 | actions: { updateInStoreA: 'update' },
246 | },
247 | {
248 | store: storeB,
249 | fields: ['a_B', 'b_B', 'sum_B'],
250 | actions: { updateInStoreB: 'update' },
251 | },
252 | ],
253 | } as any)
254 | },
255 | ) as any
256 | await waitTick()
257 | expect(innerHTML(component)).toBe('1+2=31+2=3')
258 |
259 | component.updateInStoreA()
260 | component.updateInStoreB()
261 | component.updateStoreBindings()
262 | expect(innerHTML(component)).toBe('10+20=3020+40=60')
263 | })
264 |
265 | test('structural comparison', async () => {
266 | const store = makeAutoObservable({
267 | nums: {
268 | a: 1,
269 | b: 2,
270 | },
271 | update: function () {
272 | this.nums = {
273 | a: this.nums.b,
274 | b: this.nums.a + this.nums.b,
275 | }
276 | },
277 | })
278 |
279 | const component = renderComponent(
280 | undefined,
281 | '{{nums.a}}+{{nums.b}}',
282 | (Component) => {
283 | Component({
284 | behaviors: [storeBindingsBehavior],
285 | storeBindings: {
286 | structuralComparison: false,
287 | store,
288 | fields: ['nums'],
289 | actions: ['update'],
290 | },
291 | } as any)
292 | },
293 | ) as any
294 | await waitTick()
295 | expect(innerHTML(component)).toBe('1+2')
296 |
297 | store.update()
298 | await waitTick()
299 | expect(innerHTML(component)).toBe('2+3')
300 | })
301 |
302 | test('cooperate with miniprogram-computed', async () => {
303 | const store = makeAutoObservable({
304 | nums: [1, 2, 3],
305 | update: function () {
306 | this.nums = this.nums.concat(this.nums.length + 1)
307 | },
308 | })
309 |
310 | const component = renderComponent(undefined, '{{sum}}', (Component) => {
311 | Component({
312 | behaviors: [storeBindingsBehavior, computedBehavior],
313 | storeBindings: {
314 | structuralComparison: false,
315 | store,
316 | fields: ['nums'],
317 | actions: ['update'],
318 | },
319 | computed: {
320 | sum(data) {
321 | const nums = data.nums
322 | return nums.reduce((s, o) => s + o, 0)
323 | },
324 | },
325 | } as any)
326 | }) as any
327 | await waitTick()
328 | expect(innerHTML(component)).toBe('6')
329 |
330 | component.update()
331 | await waitTick()
332 | expect(innerHTML(component)).toBe('10')
333 | })
334 |
335 | test('component with store constructor (array typing)', async () => {
336 | const store = makeAutoObservable({
337 | numA: 1,
338 | numB: 2,
339 | get sum(): number {
340 | return this.numA + this.numB
341 | },
342 | update: function (times: number) {
343 | for (let i = 0; i < times; i += 1) {
344 | const sum = this.sum
345 | this.numA = this.numB
346 | this.numB = sum
347 | }
348 | },
349 | })
350 |
351 | const component = renderComponent(undefined, '{{sum}}', (Component) => {
352 | ;(globalThis as any).Component = Component
353 | ComponentWithStore({
354 | storeBindings: {
355 | store,
356 | fields: ['sum'] as const,
357 | actions: ['update'] as const,
358 | },
359 | created() {
360 | this.update(1)
361 | },
362 | })
363 | ;(globalThis as any).Component = undefined
364 | }) as any
365 | await waitTick()
366 | expect(innerHTML(component)).toBe('5')
367 |
368 | component.update(2)
369 | await waitTick()
370 | expect(innerHTML(component)).toBe('13')
371 | })
372 |
373 | test('component with store constructor (map typing)', async () => {
374 | const store = makeAutoObservable({
375 | numA: 1,
376 | numB: 2,
377 | get sum(): number {
378 | return this.numA + this.numB
379 | },
380 | update: function (times: number) {
381 | for (let i = 0; i < times; i += 1) {
382 | const sum = this.sum
383 | this.numA = this.numB
384 | this.numB = sum
385 | }
386 | },
387 | })
388 |
389 | const component = renderComponent(undefined, '{{s}}', (Component) => {
390 | ;(globalThis as any).Component = Component
391 | ComponentWithStore({
392 | storeBindings: {
393 | store,
394 | fields: { a: () => store.numA, s: 'sum' } as const,
395 | actions: { up: 'update' } as const,
396 | },
397 | created() {
398 | this.up(1)
399 | },
400 | })
401 | ;(globalThis as any).Component = undefined
402 | }) as any
403 | await waitTick()
404 | expect(innerHTML(component)).toBe('5')
405 |
406 | component.up(2)
407 | await waitTick()
408 | expect(innerHTML(component)).toBe('13')
409 | })
410 |
411 | test('component with store constructor (multiple stores)', async () => {
412 | const store1 = makeAutoObservable({
413 | numA: 1,
414 | numB: 2,
415 | get sum(): number {
416 | return this.numA + this.numB
417 | },
418 | update: function (times: number) {
419 | for (let i = 0; i < times; i += 1) {
420 | const sum = this.sum
421 | this.numA = this.numB
422 | this.numB = sum
423 | }
424 | },
425 | })
426 | const store2 = makeAutoObservable({
427 | numA: 1,
428 | numB: 2,
429 | get sum(): number {
430 | return this.numA + this.numB
431 | },
432 | update: function (times: number) {
433 | for (let i = 0; i < times; i += 1) {
434 | const sum = this.sum
435 | this.numA = this.numB
436 | this.numB = sum
437 | }
438 | },
439 | })
440 |
441 | const component = renderComponent(undefined, '{{sum}}{{s}}', (Component) => {
442 | ;(globalThis as any).Component = Component
443 | ComponentWithStore({
444 | data: {},
445 | storeBindings: [
446 | {
447 | store: store1,
448 | fields: ['sum'] as const,
449 | actions: ['update'] as const,
450 | },
451 | {
452 | store: store2,
453 | fields: { a: () => store2.numA, s: 'sum' } as const,
454 | actions: { up: 'update' } as const,
455 | },
456 | ] as const,
457 | created() {
458 | this.update(1)
459 | this.up(2)
460 | },
461 | attached() {
462 | expect(this.data.sum).toBe(5)
463 | expect(this.data.s).toBe(8)
464 | },
465 | })
466 | ;(globalThis as any).Component = undefined
467 | }) as any
468 | await waitTick()
469 | expect(innerHTML(component)).toBe('58')
470 | })
471 |
--------------------------------------------------------------------------------
/dist/index.js.map:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "sources": ["node_modules/miniprogram-api-typings/index.d.ts", "swc_build/core.js", "swc_build/behavior.js", "swc_build/index.js"],
4 | "sourcesContent": ["/// \n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", {\n value: !0\n});\nvar r = exports, e = {\n createActions: function() {\n return f;\n },\n createDataFieldsReactions: function() {\n return u;\n }\n};\nfor(var t in e)Object.defineProperty(r, t, {\n enumerable: !0,\n get: e[t]\n});\nvar n = require(\"mobx-miniprogram\");\nfunction o(r, e) {\n (null == e || e > r.length) && (e = r.length);\n for(var t = 0, n = Array(e); t < e; t++)n[t] = r[t];\n return n;\n}\nfunction i(r, e, t) {\n return e in r ? Object.defineProperty(r, e, {\n value: t,\n enumerable: !0,\n configurable: !0,\n writable: !0\n }) : r[e] = t, r;\n}\nfunction a(r) {\n return function(r) {\n if (Array.isArray(r)) return o(r);\n }(r) || function(r) {\n if (\"undefined\" != typeof Symbol && null != r[Symbol.iterator] || null != r[\"@@iterator\"]) return Array.from(r);\n }(r) || function(r, e) {\n if (r) {\n if (\"string\" == typeof r) return o(r, void 0);\n var t = Object.prototype.toString.call(r).slice(8, -1);\n if (\"Object\" === t && r.constructor && (t = r.constructor.name), \"Map\" === t || \"Set\" === t) return Array.from(t);\n if (\"Arguments\" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)) return o(r, e);\n }\n }(r) || function() {\n throw TypeError(\"Invalid attempt to spread non-iterable instance.\\\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n }();\n}\nfunction c(r) {\n return r && \"undefined\" != typeof Symbol && r.constructor === Symbol ? \"symbol\" : typeof r;\n}\nvar f = function(r, e) {\n var t = e.store, n = e.actions;\n if (n) {\n if (void 0 === t) throw Error('[mobx-miniprogram] no store specified');\n Array.isArray(n) ? n.forEach(function(e) {\n if (r[e]) throw Error('[mobx-miniprogram] multiple action definition');\n r[e] = function() {\n for(var r = arguments.length, n = Array(r), o = 0; o < r; o++)n[o] = arguments[o];\n return t[e].apply(t, a(n));\n };\n }) : (void 0 === n ? \"undefined\" : c(n)) === 'object' && Object.keys(n).forEach(function(e) {\n var o = n[e];\n if ('string' != typeof e && 'number' != typeof e) throw Error('[mobx-miniprogram] unrecognized field definition');\n r[e] = function() {\n for(var r = arguments.length, e = Array(r), n = 0; n < r; n++)e[n] = arguments[n];\n return t[o].apply(t, a(e));\n };\n });\n }\n}, u = function(r, e) {\n var t = e.store, o = e.fields, a = e.structuralComparison, f = e.namespace || '';\n if (f && 'string' != typeof f) throw Error('[mobx-miniprogram] namespace only expect string');\n var u = Object.assign({}, r[f = f.replace(/ /gm, '')]), l = a ? n.comparer.structural : void 0, s = null, p = function() {\n if (null !== s) {\n var e = s;\n s = null, r.setData(e);\n }\n }, m = function(r, e) {\n if (s || (s = {}, 'undefined' != typeof wx ? wx.nextTick(p) : Promise.resolve().then(p)), '' !== f) {\n var t, o;\n t = function(r) {\n for(var e = 1; e < arguments.length; e++){\n var t = null != arguments[e] ? arguments[e] : {}, n = Object.keys(t);\n \"function\" == typeof Object.getOwnPropertySymbols && (n = n.concat(Object.getOwnPropertySymbols(t).filter(function(r) {\n return Object.getOwnPropertyDescriptor(t, r).enumerable;\n }))), n.forEach(function(e) {\n i(r, e, t[e]);\n });\n }\n return r;\n }({}, u), o = null != (o = i({}, r, (0, n.toJS)(e))) ? o : {}, Object.getOwnPropertyDescriptors ? Object.defineProperties(t, Object.getOwnPropertyDescriptors(o)) : (function(r, e) {\n var t = Object.keys(r);\n if (Object.getOwnPropertySymbols) {\n var n = Object.getOwnPropertySymbols(r);\n t.push.apply(t, n);\n }\n return t;\n })(Object(o)).forEach(function(r) {\n Object.defineProperty(t, r, Object.getOwnPropertyDescriptor(o, r));\n }), u = t, s[f] = u;\n } else s[r] = (0, n.toJS)(e);\n }, y = [];\n if (Array.isArray(o)) {\n if (void 0 === t) throw Error('[mobx-miniprogram] no store specified');\n y = o.map(function(r) {\n return (0, n.reaction)(function() {\n return t[r];\n }, function(e) {\n m(r, e);\n }, {\n equals: l,\n fireImmediately: !0\n });\n });\n } else (void 0 === o ? \"undefined\" : c(o)) === 'object' && o && (y = Object.keys(o).map(function(e) {\n var i = o[e];\n if ('function' == typeof i) return (0, n.reaction)(function() {\n return i.call(r, t);\n }, function(r) {\n m(e, r);\n }, {\n equals: l,\n fireImmediately: !0\n });\n if ('string' != typeof e && 'number' != typeof e) throw Error('[mobx-miniprogram] unrecognized field definition');\n if (void 0 === t) throw Error('[mobx-miniprogram] no store specified');\n return (0, n.reaction)(function() {\n return t[i];\n }, function(r) {\n m(String(e), r);\n }, {\n equals: l,\n fireImmediately: !0\n });\n }));\n return {\n updateStoreBindings: p,\n destroyStoreBindings: function() {\n y.forEach(function(r) {\n return r();\n });\n }\n };\n};\n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", {\n value: !0\n}), Object.defineProperty(exports, \"behavior\", {\n enumerable: !0,\n get: function() {\n return n;\n }\n}), require(\"miniprogram-api-typings\");\nvar i = require(\"./core\"), n = Behavior({\n definitionFilter: function(n) {\n n.methods = n.methods || {};\n var r = n.storeBindings;\n n.storeBindings = void 0, r && (n.methods._mobxMiniprogramBindings = function() {\n return r;\n }, Array.isArray(r) ? r.forEach(function(r) {\n (0, i.createActions)(n.methods, r);\n }) : (0, i.createActions)(n.methods, r));\n },\n lifetimes: {\n attached: function() {\n var n = this;\n if ('function' == typeof n._mobxMiniprogramBindings) {\n var r = n._mobxMiniprogramBindings();\n if (!r) {\n n._mobxMiniprogramBindings = null;\n return;\n }\n Array.isArray(r) ? n._mobxMiniprogramBindings = r.map(function(r) {\n var o = (0, i.createDataFieldsReactions)(n, r);\n return o.updateStoreBindings(), o;\n }) : (n._mobxMiniprogramBindings = (0, i.createDataFieldsReactions)(this, r), n._mobxMiniprogramBindings.updateStoreBindings());\n }\n },\n detached: function() {\n this._mobxMiniprogramBindings && (Array.isArray(this._mobxMiniprogramBindings) ? this._mobxMiniprogramBindings.forEach(function(i) {\n i.destroyStoreBindings();\n }) : this._mobxMiniprogramBindings.destroyStoreBindings());\n }\n },\n methods: {\n updateStoreBindings: function() {\n this._mobxMiniprogramBindings && 'function' != typeof this._mobxMiniprogramBindings && (Array.isArray(this._mobxMiniprogramBindings) ? this._mobxMiniprogramBindings.forEach(function(i) {\n i.updateStoreBindings();\n }) : this._mobxMiniprogramBindings.updateStoreBindings());\n }\n }\n});\n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", {\n value: !0\n});\nvar e = exports, r = {\n BehaviorWithStore: function() {\n return a;\n },\n ComponentWithStore: function() {\n return o;\n },\n createStoreBindings: function() {\n return u;\n },\n initStoreBindings: function() {\n return c;\n },\n storeBindingsBehavior: function() {\n return s;\n }\n};\nfor(var i in r)Object.defineProperty(e, i, {\n enumerable: !0,\n get: r[i]\n});\nrequire(\"miniprogram-api-typings\");\nvar t = require(\"./behavior\"), n = require(\"./core\");\nfunction o(e) {\n return Array.isArray(e.behaviors) || (e.behaviors = []), e.behaviors.unshift(t.behavior), Component(e);\n}\nfunction a(e) {\n return Array.isArray(e.behaviors) || (e.behaviors = []), e.behaviors.unshift(t.behavior), Behavior(e);\n}\nvar u = function(e, r) {\n return (0, n.createActions)(e, r), (0, n.createDataFieldsReactions)(e, r);\n}, s = t.behavior, c = function(e, r) {\n var i, t = e.self, o = e.lifetime;\n return o('attached', function() {\n (i = (0, n.createDataFieldsReactions)(t, r)).updateStoreBindings();\n }), o('detached', function() {\n i.destroyStoreBindings();\n }), {\n updateStoreBindings: function() {\n i && i.updateStoreBindings();\n }\n };\n};\n"],
5 | "mappings": "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",
6 | "names": ["require_index_d", "__commonJSMin", "require_core", "__commonJSMin", "exports", "r", "e", "f", "u", "t", "n", "o", "i", "a", "c", "l", "s", "p", "m", "y", "require_behavior", "__commonJSMin", "exports", "n", "i", "r", "o", "e", "r", "a", "o", "u", "c", "s", "i", "t", "n"]
7 | }
8 |
--------------------------------------------------------------------------------