├── 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": "2EAAA,IAAAA,EAAAC,EAAA,QCAA,IAAAC,EAAAC,EAAAC,GAAA,cACA,OAAO,eAAeA,EAAS,aAAc,CACzC,MAAO,EACX,CAAC,EACD,IAAIC,EAAID,EAASE,EAAI,CACjB,cAAe,UAAW,CACtB,OAAOC,CACX,EACA,0BAA2B,UAAW,CAClC,OAAOC,CACX,CACJ,EACA,IAAQC,KAAKH,EAAE,OAAO,eAAeD,EAAGI,EAAG,CACvC,WAAY,GACZ,IAAKH,EAAEG,CAAC,CACZ,CAAC,EAHO,IAAAA,EAIJC,EAAI,QAAQ,kBAAkB,EAClC,SAASC,EAAE,EAAGL,EAAG,EACJA,GAAR,MAAaA,EAAI,EAAE,UAAYA,EAAI,EAAE,QACtC,QAAQG,EAAI,EAAGC,EAAI,MAAMJ,CAAC,EAAGG,EAAIH,EAAGG,IAAIC,EAAED,CAAC,EAAI,EAAEA,CAAC,EAClD,OAAOC,CACX,CACA,SAASE,EAAE,EAAGN,EAAGG,EAAG,CAChB,OAAOH,KAAK,EAAI,OAAO,eAAe,EAAGA,EAAG,CACxC,MAAOG,EACP,WAAY,GACZ,aAAc,GACd,SAAU,EACd,CAAC,EAAI,EAAEH,CAAC,EAAIG,EAAG,CACnB,CACA,SAASI,EAAE,EAAG,CACV,OAAO,SAASR,EAAG,CACf,GAAI,MAAM,QAAQA,CAAC,EAAG,OAAOM,EAAEN,CAAC,CACpC,EAAE,CAAC,GAAK,SAASA,EAAG,CAChB,GAAmB,OAAO,OAAtB,KAAwCA,EAAE,OAAO,QAAQ,GAAzB,MAAsCA,EAAE,YAAY,GAAtB,KAAyB,OAAO,MAAM,KAAKA,CAAC,CAClH,EAAE,CAAC,GAAK,SAASA,EAAG,EAAG,CACnB,GAAIA,EAAG,CACH,GAAgB,OAAOA,GAAnB,SAAsB,OAAOM,EAAEN,EAAG,MAAM,EAC5C,IAAII,EAAI,OAAO,UAAU,SAAS,KAAKJ,CAAC,EAAE,MAAM,EAAG,EAAE,EACrD,GAAiBI,IAAb,UAAkBJ,EAAE,cAAgBI,EAAIJ,EAAE,YAAY,MAAiBI,IAAV,OAAyBA,IAAV,MAAa,OAAO,MAAM,KAAKA,CAAC,EAChH,GAAoBA,IAAhB,aAAqB,2CAA2C,KAAKA,CAAC,EAAG,OAAOE,EAAEN,EAAG,CAAC,CAC9F,CACJ,EAAE,CAAC,GAAK,UAAW,CACf,MAAM,UAAU,uIAAuI,CAC3J,EAAE,CACN,CACA,SAASS,EAAE,EAAG,CACV,OAAO,GAAoB,OAAO,OAAtB,KAAgC,EAAE,cAAgB,OAAS,SAAW,OAAO,CAC7F,CACA,IAAIP,EAAI,SAAS,EAAGD,EAAG,CACnB,IAAIG,EAAIH,EAAE,MAAOI,EAAIJ,EAAE,QACvB,GAAII,EAAG,CACH,GAAeD,IAAX,OAAc,MAAM,MAAM,uCAAuC,EACrE,MAAM,QAAQC,CAAC,EAAIA,EAAE,QAAQ,SAASJ,EAAG,CACrC,GAAI,EAAEA,CAAC,EAAG,MAAM,MAAM,+CAA+C,EACrE,EAAEA,CAAC,EAAI,UAAW,CACd,QAAQD,EAAI,UAAU,OAAQK,EAAI,MAAML,CAAC,EAAGM,EAAI,EAAGA,EAAIN,EAAGM,IAAID,EAAEC,CAAC,EAAI,UAAUA,CAAC,EAChF,OAAOF,EAAEH,CAAC,EAAE,MAAMG,EAAGI,EAAEH,CAAC,CAAC,CAC7B,CACJ,CAAC,GAAgBA,IAAX,OAAe,YAAcI,EAAEJ,CAAC,KAAO,UAAY,OAAO,KAAKA,CAAC,EAAE,QAAQ,SAASJ,EAAG,CACxF,IAAIK,EAAID,EAAEJ,CAAC,EACX,GAAgB,OAAOA,GAAnB,UAAoC,OAAOA,GAAnB,SAAsB,MAAM,MAAM,kDAAkD,EAChH,EAAEA,CAAC,EAAI,UAAW,CACd,QAAQD,EAAI,UAAU,OAAQC,EAAI,MAAMD,CAAC,EAAGK,EAAI,EAAGA,EAAIL,EAAGK,IAAIJ,EAAEI,CAAC,EAAI,UAAUA,CAAC,EAChF,OAAOD,EAAEE,CAAC,EAAE,MAAMF,EAAGI,EAAEP,CAAC,CAAC,CAC7B,CACJ,CAAC,CACL,CACJ,EAAGE,EAAI,SAAS,EAAGF,EAAG,CAClB,IAAIG,EAAIH,EAAE,MAAOK,EAAIL,EAAE,OAAQO,EAAIP,EAAE,qBAAsBC,EAAID,EAAE,WAAa,GAC9E,GAAIC,GAAiB,OAAOA,GAAnB,SAAsB,MAAM,MAAM,iDAAiD,EAC5F,IAAIC,EAAI,OAAO,OAAO,CAAC,EAAG,EAAED,EAAIA,EAAE,QAAQ,MAAO,EAAE,CAAC,CAAC,EAAGQ,EAAIF,EAAIH,EAAE,SAAS,WAAa,OAAQM,EAAI,KAAMC,EAAI,UAAW,CACrH,GAAaD,IAAT,KAAY,CACZ,IAAIV,EAAIU,EACRA,EAAI,KAAM,EAAE,QAAQV,CAAC,CACzB,CACJ,EAAGY,EAAI,SAASb,EAAGC,EAAG,CAClB,GAAIU,IAAMA,EAAI,CAAC,EAAkB,OAAO,GAAtB,IAA2B,GAAG,SAASC,CAAC,EAAI,QAAQ,QAAQ,EAAE,KAAKA,CAAC,GAAWV,IAAP,GAAU,CAChG,IAAIE,EAAGE,EACPF,EAAI,SAASJ,EAAG,CACZ,QAAQC,EAAI,EAAGA,EAAI,UAAU,OAAQA,IAAI,CACrC,IAAIG,EAAY,UAAUH,CAAC,GAAnB,KAAuB,UAAUA,CAAC,EAAI,CAAC,EAAGI,EAAI,OAAO,KAAKD,CAAC,EACrD,OAAO,OAAO,uBAA5B,aAAsDC,EAAIA,EAAE,OAAO,OAAO,sBAAsBD,CAAC,EAAE,OAAO,SAASJ,EAAG,CAClH,OAAO,OAAO,yBAAyBI,EAAGJ,CAAC,EAAE,UACjD,CAAC,CAAC,GAAIK,EAAE,QAAQ,SAASJ,EAAG,CACxBM,EAAEP,EAAGC,EAAGG,EAAEH,CAAC,CAAC,CAChB,CAAC,CACL,CACA,OAAOD,CACX,EAAE,CAAC,EAAGG,CAAC,EAAGG,GAAaA,EAAIC,EAAE,CAAC,EAAGP,KAAOK,EAAE,MAAMJ,CAAC,CAAC,IAApC,KAAyCK,EAAI,CAAC,EAAG,OAAO,0BAA4B,OAAO,iBAAiBF,EAAG,OAAO,0BAA0BE,CAAC,CAAC,EAAK,SAASN,EAAGC,EAAG,CAChL,IAAIG,EAAI,OAAO,KAAKJ,CAAC,EACrB,GAAI,OAAO,sBAAuB,CAC9B,IAAIK,EAAI,OAAO,sBAAsBL,CAAC,EACtCI,EAAE,KAAK,MAAMA,EAAGC,CAAC,CACrB,CACA,OAAOD,CACX,EAAG,OAAOE,CAAC,CAAC,EAAE,QAAQ,SAASN,EAAG,CAC9B,OAAO,eAAeI,EAAGJ,EAAG,OAAO,yBAAyBM,EAAGN,CAAC,CAAC,CACrE,CAAC,EAAGG,EAAIC,EAAGO,EAAET,CAAC,EAAIC,CACtB,MAAOQ,EAAEX,CAAC,KAAQK,EAAE,MAAMJ,CAAC,CAC/B,EAAGa,EAAI,CAAC,EACR,GAAI,MAAM,QAAQR,CAAC,EAAG,CAClB,GAAeF,IAAX,OAAc,MAAM,MAAM,uCAAuC,EACrEU,EAAIR,EAAE,IAAI,SAASN,EAAG,CAClB,SAAWK,EAAE,UAAU,UAAW,CAC9B,OAAOD,EAAEJ,CAAC,CACd,EAAG,SAASC,EAAG,CACXY,EAAEb,EAAGC,CAAC,CACV,EAAG,CACC,OAAQS,EACR,gBAAiB,EACrB,CAAC,CACL,CAAC,CACL,MAAmBJ,IAAX,OAAe,YAAcG,EAAEH,CAAC,KAAO,UAAYA,IAAMQ,EAAI,OAAO,KAAKR,CAAC,EAAE,IAAI,SAASL,EAAG,CAChG,IAAIM,EAAID,EAAEL,CAAC,EACX,GAAkB,OAAOM,GAArB,WAAwB,SAAWF,EAAE,UAAU,UAAW,CAC1D,OAAOE,EAAE,KAAK,EAAGH,CAAC,CACtB,EAAG,SAASJ,EAAG,CACXa,EAAEZ,EAAGD,CAAC,CACV,EAAG,CACC,OAAQU,EACR,gBAAiB,EACrB,CAAC,EACD,GAAgB,OAAOT,GAAnB,UAAoC,OAAOA,GAAnB,SAAsB,MAAM,MAAM,kDAAkD,EAChH,GAAeG,IAAX,OAAc,MAAM,MAAM,uCAAuC,EACrE,SAAWC,EAAE,UAAU,UAAW,CAC9B,OAAOD,EAAEG,CAAC,CACd,EAAG,SAASP,EAAG,CACXa,EAAE,OAAOZ,CAAC,EAAGD,CAAC,CAClB,EAAG,CACC,OAAQU,EACR,gBAAiB,EACrB,CAAC,CACL,CAAC,GACD,MAAO,CACH,oBAAqBE,EACrB,qBAAsB,UAAW,CAC7BE,EAAE,QAAQ,SAASd,EAAG,CAClB,OAAOA,EAAE,CACb,CAAC,CACL,CACJ,CACJ,IC9IA,IAAAe,EAAAC,EAAAC,GAAA,cACA,OAAO,eAAeA,EAAS,aAAc,CACzC,MAAO,EACX,CAAC,EAAG,OAAO,eAAeA,EAAS,WAAY,CAC3C,WAAY,GACZ,IAAK,UAAW,CACZ,OAAOC,CACX,CACJ,CAAC,EAAG,IACJ,IAAIC,EAAI,IAAmBD,EAAI,SAAS,CACpC,iBAAkB,SAASA,EAAG,CAC1BA,EAAE,QAAUA,EAAE,SAAW,CAAC,EAC1B,IAAIE,EAAIF,EAAE,cACVA,EAAE,cAAgB,OAAQE,IAAMF,EAAE,QAAQ,yBAA2B,UAAW,CAC5E,OAAOE,CACX,EAAG,MAAM,QAAQA,CAAC,EAAIA,EAAE,QAAQ,SAASA,EAAG,IACpCD,EAAE,eAAeD,EAAE,QAASE,CAAC,CACrC,CAAC,KAAQD,EAAE,eAAeD,EAAE,QAASE,CAAC,EAC1C,EACA,UAAW,CACP,SAAU,UAAW,CACjB,IAAIF,EAAI,KACR,GAAkB,OAAOA,EAAE,0BAAvB,WAAiD,CACjD,IAAIE,EAAIF,EAAE,yBAAyB,EACnC,GAAI,CAACE,EAAG,CACJF,EAAE,yBAA2B,KAC7B,MACJ,CACA,MAAM,QAAQE,CAAC,EAAIF,EAAE,yBAA2BE,EAAE,IAAI,SAASA,EAAG,CAC9D,IAAIC,KAAQF,EAAE,2BAA2BD,EAAGE,CAAC,EAC7C,OAAOC,EAAE,oBAAoB,EAAGA,CACpC,CAAC,GAAKH,EAAE,4BAA+BC,EAAE,2BAA2B,KAAMC,CAAC,EAAGF,EAAE,yBAAyB,oBAAoB,EACjI,CACJ,EACA,SAAU,UAAW,CACjB,KAAK,2BAA6B,MAAM,QAAQ,KAAK,wBAAwB,EAAI,KAAK,yBAAyB,QAAQ,SAASC,EAAG,CAC/HA,EAAE,qBAAqB,CAC3B,CAAC,EAAI,KAAK,yBAAyB,qBAAqB,EAC5D,CACJ,EACA,QAAS,CACL,oBAAqB,UAAW,CAC5B,KAAK,0BAA0C,OAAO,KAAK,0BAA1B,aAAuD,MAAM,QAAQ,KAAK,wBAAwB,EAAI,KAAK,yBAAyB,QAAQ,SAASA,EAAG,CACrLA,EAAE,oBAAoB,CAC1B,CAAC,EAAI,KAAK,yBAAyB,oBAAoB,EAC3D,CACJ,CACJ,CAAC,IC9CD,OAAO,eAAe,QAAS,aAAc,CACzC,MAAO,EACX,CAAC,EACD,IAAIG,EAAI,QAASC,EAAI,CACjB,kBAAmB,UAAW,CAC1B,OAAOC,CACX,EACA,mBAAoB,UAAW,CAC3B,OAAOC,CACX,EACA,oBAAqB,UAAW,CAC5B,OAAOC,CACX,EACA,kBAAmB,UAAW,CAC1B,OAAOC,CACX,EACA,sBAAuB,UAAW,CAC9B,OAAOC,CACX,CACJ,EACA,IAAQC,KAAKN,EAAE,OAAO,eAAeD,EAAGO,EAAG,CACvC,WAAY,GACZ,IAAKN,EAAEM,CAAC,CACZ,CAAC,EAHO,IAAAA,EAIR,IACA,IAAIC,EAAI,IAAuBC,EAAI,IACnC,SAASN,EAAEH,EAAG,CACV,OAAO,MAAM,QAAQA,EAAE,SAAS,IAAMA,EAAE,UAAY,CAAC,GAAIA,EAAE,UAAU,QAAQQ,EAAE,QAAQ,EAAG,UAAUR,CAAC,CACzG,CACA,SAASE,EAAEF,EAAG,CACV,OAAO,MAAM,QAAQA,EAAE,SAAS,IAAMA,EAAE,UAAY,CAAC,GAAIA,EAAE,UAAU,QAAQQ,EAAE,QAAQ,EAAG,SAASR,CAAC,CACxG,CACA,IAAII,EAAI,SAASJ,EAAGC,EAAG,CACnB,SAAWQ,EAAE,eAAeT,EAAGC,CAAC,KAAOQ,EAAE,2BAA2BT,EAAGC,CAAC,CAC5E,EAAGK,EAAIE,EAAE,SAAUH,EAAI,SAASL,EAAGC,EAAG,CAClC,IAAIM,EAAGC,EAAIR,EAAE,KAAM,EAAIA,EAAE,SACzB,OAAO,EAAE,WAAY,UAAW,EAC3BO,KAAQE,EAAE,2BAA2BD,EAAGP,CAAC,GAAG,oBAAoB,CACrE,CAAC,EAAG,EAAE,WAAY,UAAW,CACzBM,EAAE,qBAAqB,CAC3B,CAAC,EAAG,CACA,oBAAqB,UAAW,CAC5BA,GAAKA,EAAE,oBAAoB,CAC/B,CACJ,CACJ", 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 | --------------------------------------------------------------------------------