├── examples ├── app.wxss ├── pages │ ├── home │ │ ├── home.js │ │ ├── home.wxml │ │ ├── home.wxss │ │ └── home.json │ └── list │ │ ├── list.wxml │ │ ├── list.wxss │ │ ├── components │ │ └── item │ │ │ ├── item.wxml │ │ │ ├── item.wxss │ │ │ ├── item.json │ │ │ └── item.js │ │ ├── list.json │ │ └── list.js ├── app.js ├── sitemap.json ├── app.json ├── package.json ├── project.config.json └── package-lock.json ├── .gitignore ├── packages ├── reactivity │ ├── .gitignore │ ├── src │ │ ├── customRef.ts │ │ ├── index.ts │ │ ├── utils.ts │ │ ├── dep.ts │ │ ├── reactive.ts │ │ ├── watch.ts │ │ └── ref.ts │ ├── lib │ │ ├── customRef.d.ts │ │ ├── customRef.js │ │ ├── index.d.ts │ │ ├── utils.d.ts │ │ ├── dep.d.ts │ │ ├── utils.js │ │ ├── reactive.d.ts │ │ ├── watch.d.ts │ │ ├── index.js │ │ ├── reactive.js │ │ ├── ref.d.ts │ │ ├── dep.js │ │ ├── watch.js │ │ └── ref.js │ ├── .npmignore │ ├── test │ │ └── ref.ts │ ├── tsconfig.json │ ├── rollup.config.js │ ├── package.json │ └── package-lock.json └── miniprogram-composition-api │ ├── test │ ├── inter.ts │ ├── diff.ts │ └── constate.ts │ ├── .gitignore │ ├── lib │ ├── diff.d.ts │ ├── over.d.ts │ ├── interface.js │ ├── app.d.ts │ ├── computed.d.ts │ ├── watch.d.ts │ ├── interface.d.ts │ ├── constate.d.ts │ ├── inject.d.ts │ ├── context.d.ts │ ├── computed.js │ ├── utils.d.ts │ ├── over.js │ ├── page.d.ts │ ├── router.d.ts │ ├── index.d.ts │ ├── component.d.ts │ ├── watch.js │ ├── instance.js │ ├── router.js │ ├── mitt.d.ts │ ├── context.js │ ├── app.js │ ├── utils.js │ ├── index.js │ ├── lifecycle.js │ ├── constate.js │ ├── lifecycle.d.ts │ ├── shared.d.ts │ ├── mitt.js │ ├── shared.js │ ├── inject.js │ ├── page.js │ ├── diff.js │ └── component.js │ ├── .prettierrc │ ├── .npmignore │ ├── src │ ├── ref.ts │ ├── over.ts │ ├── interface.ts │ ├── computed.ts │ ├── watch.ts │ ├── index.ts │ ├── router.ts │ ├── utils.ts │ ├── instance.ts │ ├── context.ts │ ├── app.ts │ ├── constate.ts │ ├── mitt.ts │ ├── inject.ts │ ├── diff.ts │ ├── page.ts │ ├── lifecycle.ts │ ├── shared.ts │ └── component.ts │ ├── tsconfig.json │ ├── rollup.config.js │ ├── package.json │ ├── beta │ └── constate.ts │ └── package-lock.json ├── lerna.json ├── package.json ├── TENET.MD ├── index.html ├── TODO.md ├── README.md └── EXAMPLE.md /examples/app.wxss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /examples/pages/home/home.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/reactivity/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/test/inter.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/pages/home/home.wxml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/pages/list/list.wxml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/pages/home/home.wxss: -------------------------------------------------------------------------------- 1 | /* required by usingComponents */ -------------------------------------------------------------------------------- /examples/pages/list/list.wxss: -------------------------------------------------------------------------------- 1 | /* required by usingComponents */ -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/reactivity/src/customRef.ts: -------------------------------------------------------------------------------- 1 | export const customRef = {} -------------------------------------------------------------------------------- /examples/pages/home/home.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | } 4 | } -------------------------------------------------------------------------------- /examples/pages/list/components/item/item.wxml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/pages/list/list.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | } 4 | } -------------------------------------------------------------------------------- /examples/pages/list/components/item/item.wxss: -------------------------------------------------------------------------------- 1 | /* required by usingComponents */ -------------------------------------------------------------------------------- /packages/reactivity/lib/customRef.d.ts: -------------------------------------------------------------------------------- 1 | export declare const customRef: {}; 2 | -------------------------------------------------------------------------------- /examples/pages/list/components/item/item.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | } 4 | } -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.0.0" 6 | } 7 | -------------------------------------------------------------------------------- /examples/app.js: -------------------------------------------------------------------------------- 1 | App({ 2 | onLaunch() {}, 3 | onShow() {}, 4 | onHide() {}, 5 | }) 6 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/diff.d.ts: -------------------------------------------------------------------------------- 1 | export declare function diff(current: any, pre: any): {}; 2 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "semi": false 5 | } -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/over.d.ts: -------------------------------------------------------------------------------- 1 | export declare function overCloneDeep(callback: T): T; 2 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/interface.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/reactivity/lib/customRef.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.customRef = {}; 4 | -------------------------------------------------------------------------------- /packages/reactivity/src/index.ts: -------------------------------------------------------------------------------- 1 | export { useRef, isRef, IRef } from './ref' 2 | export { Dep } from './dep' 3 | export { isObserve, useEffect } from './watch' 4 | -------------------------------------------------------------------------------- /examples/pages/list/list.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const {defineComponent} = require('miniprogram-composition-api') 3 | 4 | defineComponent(() => { 5 | return {} 6 | }) -------------------------------------------------------------------------------- /packages/reactivity/lib/index.d.ts: -------------------------------------------------------------------------------- 1 | export { useRef, isRef, IRef } from './ref'; 2 | export { Dep } from './dep'; 3 | export { isObserve, useEffect } from './watch'; 4 | -------------------------------------------------------------------------------- /packages/reactivity/lib/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function isFunction(x: unknown): x is Function; 2 | export declare function isObject(x: unknown): x is object; 3 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/app.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare function defineApp(params: WechatMiniprogram.App.Options): void; 3 | -------------------------------------------------------------------------------- /examples/pages/list/components/item/item.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | const {defineComponent} = require('miniprogram-composition-api') 4 | 5 | defineComponent(() => { 6 | return {} 7 | }) -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | demo/ 4 | example/ 5 | test/ 6 | ISSUE_TEMPLATE.md 7 | appveyor.yml 8 | *.map 9 | src/ 10 | coverage -------------------------------------------------------------------------------- /packages/reactivity/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | demo/ 4 | example/ 5 | test/ 6 | ISSUE_TEMPLATE.md 7 | appveyor.yml 8 | README_zh-CN.md 9 | *.map 10 | src/ 11 | coverage -------------------------------------------------------------------------------- /packages/reactivity/test/ref.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from '../src/index' 2 | 3 | const age = useRef(0) 4 | useEffect(() => { 5 | console.log(age.value) 6 | }, [age]) 7 | age.set(1) -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/computed.d.ts: -------------------------------------------------------------------------------- 1 | import { IRef } from 'miniprogram-reactivity'; 2 | export declare function useComputed(callback: () => T, refs: IRef[]): IRef; 3 | -------------------------------------------------------------------------------- /packages/reactivity/lib/dep.d.ts: -------------------------------------------------------------------------------- 1 | export declare class Dep { 2 | private deps; 3 | depend(callback: Function): () => void; 4 | notify(...arg: any[]): void; 5 | clear(): void; 6 | } 7 | -------------------------------------------------------------------------------- /examples/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "*" 6 | }] 7 | } -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/ref.ts: -------------------------------------------------------------------------------- 1 | import { IRef, useRef as _useRef } from 'miniprogram-reactivity' 2 | 3 | export function useRef (value: T, { diff = true }): IRef{ 4 | return _useRef(value) 5 | } -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/watch.d.ts: -------------------------------------------------------------------------------- 1 | import { IRef } from 'miniprogram-reactivity'; 2 | export declare function useEffect(callback: (newValue: T, oldValue: T) => any, refs: IRef[]): () => void; 3 | -------------------------------------------------------------------------------- /packages/reactivity/src/utils.ts: -------------------------------------------------------------------------------- 1 | export function isFunction (x: unknown): x is Function{ 2 | return typeof x === 'function' 3 | } 4 | 5 | export function isObject (x: unknown): x is object{ 6 | return x !== null && typeof x === 'object' 7 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "devDependencies": { 5 | "lerna": "^3.22.1" 6 | }, 7 | "dependencies": { 8 | "@types/node": "^14.6.3", 9 | "miniprogram-composition-api": "^0.2.4" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/over.ts: -------------------------------------------------------------------------------- 1 | import { clone } from '@jsmini/clone' 2 | 3 | export function overCloneDeep (callback: T): T{ 4 | // @ts-ignore 5 | return function (...args: any[]){ 6 | return clone(callback.apply(this, args)) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/interface.d.ts: -------------------------------------------------------------------------------- 1 | export declare type Parameters any> = T extends (...args: infer P) => any ? P : never; 2 | export declare type ReturnType any> = T extends (...args: any) => infer R ? R : any; 3 | -------------------------------------------------------------------------------- /examples/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/home/home", 4 | "pages/list/list" 5 | ], 6 | "window": { 7 | "defaultTitle": "案例", 8 | "backgroundColor": "#F5F5F9", 9 | "pullRefresh": false, 10 | "allowsBounceVertical": true 11 | }, 12 | "sitemapLocation": "sitemap.json" 13 | } -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/test/diff.ts: -------------------------------------------------------------------------------- 1 | import { diff } from '../src/diff' 2 | 3 | console.log( 4 | diff( 5 | { 6 | arr: [ 7 | 1, 8 | 2, 9 | 45, 10 | 111 11 | ] 12 | }, 13 | { 14 | arr: [ 15 | 1, 16 | 2, 17 | 3, 18 | ] 19 | } 20 | ) 21 | ) 22 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/interface.ts: -------------------------------------------------------------------------------- 1 | 2 | export type Parameters any> = T extends ( 3 | ...args: infer P 4 | ) => any 5 | ? P 6 | : never 7 | export type ReturnType any> = T extends ( 8 | ...args: any 9 | ) => infer R 10 | ? R 11 | : any -------------------------------------------------------------------------------- /packages/reactivity/lib/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | function isFunction(x) { 4 | return typeof x === 'function'; 5 | } 6 | exports.isFunction = isFunction; 7 | function isObject(x) { 8 | return x !== null && typeof x === 'object'; 9 | } 10 | exports.isObject = isObject; 11 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "miniprogram-composition-api": "^0.2.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/constate.d.ts: -------------------------------------------------------------------------------- 1 | import { Parameters, ReturnType } from './interface'; 2 | /** 3 | * 4 | * 存在期间的 单一实例 5 | * 所有自定义组件/页面共享数据, 当被依赖的页面/组件都被销毁时,重新加载第一遍会被执行一次 6 | * 请在setup期间调用!! 7 | */ 8 | export declare function createConstate any>(callback: T): (...args: Parameters) => ReturnType; 9 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/computed.ts: -------------------------------------------------------------------------------- 1 | import { IRef, useRef } from 'miniprogram-reactivity' 2 | import { useEffect } from './watch' 3 | 4 | export function useComputed (callback: () => T, refs: IRef[]): IRef{ 5 | const ref = useRef(callback()) 6 | useEffect(() => { 7 | ref.set(callback()) 8 | }, refs) 9 | 10 | return ref 11 | } -------------------------------------------------------------------------------- /packages/reactivity/lib/reactive.d.ts: -------------------------------------------------------------------------------- 1 | export interface IReactive { 2 | /** 赋值 */ 3 | set(params: T | ((value: T) => T)): void; 4 | __v_isReactive: boolean; 5 | } 6 | export declare function isReactive(r: any): r is IReactive; 7 | /** 8 | * 响应式对象 9 | * @param value 10 | */ 11 | export declare function useReactive(value: T): any; 12 | -------------------------------------------------------------------------------- /packages/reactivity/lib/watch.d.ts: -------------------------------------------------------------------------------- 1 | import { IRef } from './ref'; 2 | /** 3 | * 可观测对象 4 | * @param value 5 | */ 6 | export declare function isObserve(value: any): value is IRef; 7 | /** 8 | * 监听ref做出回应 9 | * @return {function} 丢弃监听 10 | */ 11 | export declare function useEffect(callback: (newValue: T, oldValue: T) => any, refs: IRef[]): () => void; 12 | -------------------------------------------------------------------------------- /packages/reactivity/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var ref_1 = require("./ref"); 4 | exports.useRef = ref_1.useRef; 5 | exports.isRef = ref_1.isRef; 6 | var dep_1 = require("./dep"); 7 | exports.Dep = dep_1.Dep; 8 | var watch_1 = require("./watch"); 9 | exports.isObserve = watch_1.isObserve; 10 | exports.useEffect = watch_1.useEffect; 11 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/inject.d.ts: -------------------------------------------------------------------------------- 1 | import { Parameters, ReturnType } from './interface'; 2 | /** 3 | * 4 | * create and use point 5 | */ 6 | export declare function useProvide any>(callback: T, ...args: Parameters): ReturnType; 7 | /** 8 | * 9 | * use point 10 | */ 11 | export declare function useInject any>(callback: T, ...args: Parameters): ReturnType; 12 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/context.d.ts: -------------------------------------------------------------------------------- 1 | import { ICurrentModuleInstance } from './instance'; 2 | import { Emitter } from './mitt'; 3 | import { router } from './router'; 4 | export declare type IContext = { 5 | setData: (params: { 6 | [key: string]: any; 7 | }) => () => void; 8 | event: Emitter; 9 | router: typeof router; 10 | }; 11 | export declare function createContext(target: ICurrentModuleInstance): IContext; 12 | -------------------------------------------------------------------------------- /packages/reactivity/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "CommonJS", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "outDir": "./lib", 8 | "strict": false, 9 | "experimentalDecorators": true, 10 | "typeRoots": [ 11 | "./node_modules/@types", 12 | "./node_modules/miniprogram-api-typings" 13 | ] 14 | }, 15 | "exclude": ["dist", "example", "test"] 16 | } -------------------------------------------------------------------------------- /packages/reactivity/src/dep.ts: -------------------------------------------------------------------------------- 1 | export class Dep { 2 | private deps: Function[] = [] 3 | 4 | depend (callback: Function) { 5 | this.deps.push(callback) 6 | return () => { 7 | const findIndex = this.deps.indexOf(callback) 8 | if (~findIndex) { 9 | this.deps.splice(findIndex, 1) 10 | } 11 | } 12 | } 13 | 14 | notify (...arg: any[]) { 15 | this.deps.forEach((dep) => { 16 | dep(...arg) 17 | }) 18 | } 19 | 20 | clear () { 21 | this.deps = [] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/reactivity/rollup.config.js: -------------------------------------------------------------------------------- 1 | const typescript = require("rollup-plugin-typescript2") 2 | const resolve = require("rollup-plugin-node-resolve") 3 | const commonjs = require("rollup-plugin-commonjs") 4 | 5 | 6 | module.exports = { 7 | input: './src/index.ts', 8 | output: { 9 | file: './lib/index.js', 10 | name: 'People', 11 | format: 'umd' 12 | }, 13 | plugins: [ 14 | typescript(), 15 | commonjs(), 16 | resolve(), 17 | ] 18 | } -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/computed.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var miniprogram_reactivity_1 = require("miniprogram-reactivity"); 4 | var watch_1 = require("./watch"); 5 | function useComputed(callback, refs) { 6 | var ref = miniprogram_reactivity_1.useRef(callback()); 7 | watch_1.useEffect(function () { 8 | ref.set(callback()); 9 | }, refs); 10 | return ref; 11 | } 12 | exports.useComputed = useComputed; 13 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "CommonJS", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "outDir": "./lib", 8 | "strict": false, 9 | "experimentalDecorators": true, 10 | "typeRoots": [ 11 | "./node_modules/@types", 12 | "./node_modules/miniprogram-api-typings" 13 | ] 14 | }, 15 | "exclude": ["dist", "example", "test", "beta"] 16 | } -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/rollup.config.js: -------------------------------------------------------------------------------- 1 | const typescript = require("rollup-plugin-typescript2") 2 | const resolve = require("rollup-plugin-node-resolve") 3 | const commonjs = require("rollup-plugin-commonjs") 4 | 5 | 6 | module.exports = { 7 | input: './src/index.ts', 8 | output: { 9 | file: './lib/index.js', 10 | name: 'People', 11 | format: 'umd' 12 | }, 13 | plugins: [ 14 | typescript(), 15 | commonjs(), 16 | resolve(), 17 | ] 18 | } -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare const isArray: (arg: any) => arg is any[]; 2 | export declare function getType(x: unknown): string; 3 | export declare function isObject(x: unknown): x is object; 4 | export declare function isFunction(x: unknown): x is Function; 5 | export declare function isPlainObject(x: unknown): x is Record; 6 | export declare function wrapFuns(...args: Function[]): (...params: any[]) => void[]; 7 | export declare function createShortName(name: string): string; 8 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/over.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var clone_1 = require("@jsmini/clone"); 4 | function overCloneDeep(callback) { 5 | // @ts-ignore 6 | return function () { 7 | var args = []; 8 | for (var _i = 0; _i < arguments.length; _i++) { 9 | args[_i] = arguments[_i]; 10 | } 11 | return clone_1.clone(callback.apply(this, args)); 12 | }; 13 | } 14 | exports.overCloneDeep = overCloneDeep; 15 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/page.d.ts: -------------------------------------------------------------------------------- 1 | import { ISetup } from './shared'; 2 | export declare function definePage any; 4 | }, INJECT extends { 5 | [key: string]: () => any; 6 | }>(pageOptions: { 7 | /** 注册服务 */ 8 | provide?: PROVIDE; 9 | /** 注入 */ 10 | inject?: INJECT; 11 | /** 静态属性,可以被覆盖,初始化显示更快 */ 12 | data?: { 13 | [key: string]: any; 14 | }; 15 | setup?: ISetup; 16 | } | ISetup): any; 17 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/router.d.ts: -------------------------------------------------------------------------------- 1 | import { ICurrentModuleInstance } from './instance'; 2 | export declare const router: { 3 | go(url: string, params?: { 4 | [key: string]: any; 5 | }): void; 6 | /** 7 | * 后退页面 8 | * @param target - 接受number或者页面对象, 表示将退出直到显示该页面 9 | */ 10 | back(target?: number | ICurrentModuleInstance): void; 11 | /** 12 | * 离开页面 13 | * @param target - 往后退,直到离开这个页面 14 | */ 15 | leave(target: ICurrentModuleInstance): void; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/index.d.ts: -------------------------------------------------------------------------------- 1 | export { useRef, isRef, IRef } from 'miniprogram-reactivity'; 2 | export { defineComponent } from './component'; 3 | export { definePage } from './page'; 4 | export { router } from './router'; 5 | export { createConstate } from './constate'; 6 | export { onLoad, onReady, onUnLoad, onShow, onHide, onPullDownRefresh, onShareAppMessage, onReachBottom, onPageScroll, } from './lifecycle'; 7 | export { useEffect } from './watch'; 8 | export { useComputed } from './computed'; 9 | export { useInject, useProvide } from './inject'; 10 | export { defineApp } from './app'; 11 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/watch.ts: -------------------------------------------------------------------------------- 1 | import { IRef, useEffect as _useEffect } from 'miniprogram-reactivity' 2 | import { overInCurrentModule } from './instance' 3 | import { injectHook, ExtendLefecycle } from './lifecycle' 4 | 5 | export function useEffect( 6 | callback: (newValue: T, oldValue: T) => any, 7 | refs: IRef[] 8 | ): () => void { 9 | const stopHandle = _useEffect(callback, refs) 10 | 11 | return overInCurrentModule((currentInstance) => { 12 | currentInstance && 13 | injectHook(currentInstance, ExtendLefecycle.EFFECT, stopHandle) 14 | return stopHandle 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/component.d.ts: -------------------------------------------------------------------------------- 1 | import { ISetup, AllProperty } from './shared'; 2 | export declare function defineComponent any; 6 | }, INJECT extends { 7 | [key: string]: () => any; 8 | }>(componentOptions: { 9 | props?: PROPS; 10 | /** 注册 */ 11 | provide?: PROVIDE; 12 | /** 注入 */ 13 | inject?: INJECT; 14 | /** 静态属性,可以被覆盖,初始化显示更快 */ 15 | data?: { 16 | [key: string]: any; 17 | }; 18 | setup?: ISetup; 19 | [key: string]: any; 20 | } | ISetup): any; 21 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/watch.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var miniprogram_reactivity_1 = require("miniprogram-reactivity"); 4 | var instance_1 = require("./instance"); 5 | var lifecycle_1 = require("./lifecycle"); 6 | function useEffect(callback, refs) { 7 | var stopHandle = miniprogram_reactivity_1.useEffect(callback, refs); 8 | return instance_1.overInCurrentModule(function (currentInstance) { 9 | currentInstance && 10 | lifecycle_1.injectHook(currentInstance, "effect" /* EFFECT */, stopHandle); 11 | return stopHandle; 12 | }); 13 | } 14 | exports.useEffect = useEffect; 15 | -------------------------------------------------------------------------------- /packages/reactivity/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-reactivity", 3 | "version": "0.3.2", 4 | "description": "reactivity", 5 | "main": "./lib/index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "dependencies": { 10 | "@jsmini/clone": "^0.4.2", 11 | "@jsmini/isequal": "^0.4.2" 12 | }, 13 | "devDependencies": { 14 | "rollup-plugin-babel": "^4.4.0", 15 | "rollup-plugin-commonjs": "^10.1.0", 16 | "rollup-plugin-node-resolve": "^5.2.0", 17 | "rollup-plugin-typescript2": "^0.27.2", 18 | "typescript": "^4.0.2" 19 | }, 20 | "scripts": { 21 | "test": "echo \"Error: no test specified\" && exit 1" 22 | }, 23 | "author": "", 24 | "license": "ISC" 25 | } 26 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/index.ts: -------------------------------------------------------------------------------- 1 | export { useRef, isRef, IRef } from 'miniprogram-reactivity' 2 | 3 | export { defineComponent } from './component' 4 | 5 | export { definePage } from './page' 6 | 7 | export { router } from './router' 8 | 9 | export { createConstate } from './constate' 10 | 11 | export { 12 | onLoad, 13 | onReady, 14 | onUnLoad, 15 | onShow, 16 | onHide, 17 | onPullDownRefresh, 18 | onShareAppMessage, 19 | onReachBottom, 20 | onPageScroll, 21 | } from './lifecycle' 22 | 23 | export { useEffect } from './watch' 24 | 25 | export { useComputed } from './computed' 26 | 27 | export { useInject, useProvide } from './inject' 28 | 29 | export { defineApp } from './app' 30 | -------------------------------------------------------------------------------- /packages/reactivity/lib/reactive.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var utils_1 = require("./utils"); 4 | function isReactive(r) { 5 | return r ? r.__v_isReactive === true : false; 6 | } 7 | exports.isReactive = isReactive; 8 | /** 9 | * 响应式对象 10 | * @param value 11 | */ 12 | function useReactive(value) { 13 | return createReactiveObject(value); 14 | } 15 | exports.useReactive = useReactive; 16 | function createReactiveObject(value) { 17 | if (isReactive(value)) { 18 | return value; 19 | } 20 | if (!utils_1.isObject(value)) { 21 | console.warn("value cannot be made reactive: " + String(value) + ", please use the useRef"); 22 | return value; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/test/constate.ts: -------------------------------------------------------------------------------- 1 | import { useRef, createConstate } from '../src/index' 2 | 3 | const useUserModal = createConstate(function (params: { name: string }) { 4 | const name = useRef(params.name) 5 | const changeName = (_name: string) => { 6 | name.set(_name) 7 | } 8 | 9 | return { 10 | name, 11 | changeName, 12 | } 13 | }) 14 | 15 | const demo = useUserModal({ 16 | name: 'demo', 17 | }) 18 | 19 | const demo2 = useUserModal({ 20 | name: 'demo', 21 | }) 22 | 23 | const demo3 = useUserModal({ 24 | name: 'demo3', 25 | }) 26 | 27 | const demo4 = useUserModal({ 28 | name: 'demo3', 29 | }) 30 | 31 | demo2.changeName('changedemo2') 32 | 33 | console.log(demo.name.value, demo2.name.value, demo3.name.value, demo4) 34 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/instance.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var currentModule = null; 4 | /** 5 | * 要求注入的函数第一个参数是 current对象 6 | * @param callback 7 | */ 8 | function overCurrentModule(callback) { 9 | return function () { 10 | var arg = []; 11 | for (var _i = 0; _i < arguments.length; _i++) { 12 | arg[_i] = arguments[_i]; 13 | } 14 | currentModule = this; 15 | var reuslt = callback.apply(this, arg); 16 | currentModule = null; 17 | return reuslt; 18 | }; 19 | } 20 | exports.overCurrentModule = overCurrentModule; 21 | function overInCurrentModule(callback) { 22 | return callback(currentModule); 23 | } 24 | exports.overInCurrentModule = overInCurrentModule; 25 | -------------------------------------------------------------------------------- /packages/reactivity/src/reactive.ts: -------------------------------------------------------------------------------- 1 | import { isObject } from './utils' 2 | 3 | export interface IReactive { 4 | /** 赋值 */ 5 | set(params: T | ((value: T) => T)): void 6 | 7 | __v_isReactive: boolean 8 | } 9 | 10 | export function isReactive(r: any): r is IReactive { 11 | return r ? r.__v_isReactive === true : false 12 | } 13 | 14 | /** 15 | * 响应式对象 16 | * @param value 17 | */ 18 | export function useReactive (value: T){ 19 | return createReactiveObject(value) 20 | } 21 | 22 | function createReactiveObject(value) { 23 | if (isReactive(value)) { 24 | return value; 25 | } 26 | if (!isObject(value)) { 27 | console.warn( 28 | `value cannot be made reactive: ${String(value)}, please use the useRef` 29 | ) 30 | return value 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /packages/reactivity/lib/ref.d.ts: -------------------------------------------------------------------------------- 1 | export interface IRef { 2 | value: T; 3 | /** 提取值 */ 4 | get(): T; 5 | /** 6 | * @param {any} params 值 7 | * @param {object} config 8 | * @param {boolean} config.notify - 是否强制更新 9 | */ 10 | set(params: T | ((value: T) => T), config?: { 11 | notify: boolean; 12 | }): void; 13 | __v_isRef: boolean; 14 | /** 15 | * 更新通知 16 | */ 17 | __v_change: (callback: (newValue: T, oldValue: T) => any) => () => any; 18 | /** 19 | * 清除所有的监听 20 | */ 21 | __v_clear: () => void; 22 | } 23 | export declare function isRef(r: IRef | unknown): r is IRef; 24 | /** 25 | * @param {any} value - 初始值 26 | * ```js 27 | const number = useRef(0) 28 | number.set({ 29 | name: 2 30 | }); 31 | * 32 | * ``` 33 | */ 34 | export declare function useRef(value: T): IRef; 35 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-composition-api", 3 | "version": "0.3.5", 4 | "description": "类 vue3 composition-api 写法写小程序 小程序mixins替代方案, 计算属性, watch", 5 | "main": "lib/index.js", 6 | "directories": { 7 | "lib": "lib", 8 | "test": "test" 9 | }, 10 | "dependencies": { 11 | "@jsmini/clone": "^0.4.2", 12 | "@jsmini/isequal": "^0.4.2", 13 | "miniprogram-reactivity": "^0.3.1" 14 | }, 15 | "devDependencies": { 16 | "miniprogram-api-typings": "*", 17 | "rollup-plugin-babel": "^4.4.0", 18 | "rollup-plugin-commonjs": "^10.1.0", 19 | "rollup-plugin-node-resolve": "^5.2.0", 20 | "rollup-plugin-typescript2": "^0.27.2", 21 | "typescript": "^4.0.2" 22 | }, 23 | "scripts": { 24 | "test": "echo \"Error: no test specified\" && exit 1" 25 | }, 26 | "author": "", 27 | "license": "ISC" 28 | } 29 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/router.ts: -------------------------------------------------------------------------------- 1 | import { ICurrentModuleInstance } from './instance' 2 | 3 | const routerParams = { 4 | store: {}, 5 | take (key) { 6 | let value = this.store[key] || {} 7 | this.store[key] = null 8 | 9 | return value 10 | }, 11 | set (key, value) { 12 | this.store[key] = value 13 | } 14 | } 15 | 16 | export const router = { 17 | go (url: string, params: { [key: string]: any } = {}) { 18 | wx.navigateTo({ 19 | url 20 | }) 21 | }, 22 | 23 | /** 24 | * 后退页面 25 | * @param target - 接受number或者页面对象, 表示将退出直到显示该页面 26 | */ 27 | back (target: number | ICurrentModuleInstance = 1) { 28 | let delta = typeof target === 'number' ? target : 1 29 | wx.navigateBack({ 30 | delta 31 | }) 32 | }, 33 | 34 | /** 35 | * 离开页面 36 | * @param target - 往后退,直到离开这个页面 37 | */ 38 | leave (target: ICurrentModuleInstance) {} 39 | } 40 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/utils.ts: -------------------------------------------------------------------------------- 1 | export const { isArray } = Array 2 | 3 | export function getType (x: unknown): string{ 4 | return Object.prototype.toString.call(x).slice(8, -1) 5 | } 6 | 7 | export function isObject (x: unknown): x is object{ 8 | return x !== null && typeof x === 'object' 9 | } 10 | 11 | export function isFunction (x: unknown): x is Function{ 12 | return typeof x === 'function' 13 | } 14 | 15 | export function isPlainObject (x: unknown): x is Record{ 16 | return x !== null && getType(x) === 'Object' 17 | } 18 | 19 | export function wrapFuns (...args: Function[]){ 20 | return function (...params: any[]){ 21 | return args.forEach((fun) => { 22 | try { 23 | fun && fun.apply(this, params) 24 | } catch (e) { 25 | console.error(e) 26 | /** ignore */ 27 | } 28 | }) 29 | } 30 | } 31 | 32 | export function createShortName (name: string){ 33 | return `__${name}__` 34 | } 35 | -------------------------------------------------------------------------------- /packages/reactivity/lib/dep.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var Dep = /** @class */ (function () { 4 | function Dep() { 5 | this.deps = []; 6 | } 7 | Dep.prototype.depend = function (callback) { 8 | var _this = this; 9 | this.deps.push(callback); 10 | return function () { 11 | var findIndex = _this.deps.indexOf(callback); 12 | if (~findIndex) { 13 | _this.deps.splice(findIndex, 1); 14 | } 15 | }; 16 | }; 17 | Dep.prototype.notify = function () { 18 | var arg = []; 19 | for (var _i = 0; _i < arguments.length; _i++) { 20 | arg[_i] = arguments[_i]; 21 | } 22 | this.deps.forEach(function (dep) { 23 | dep.apply(void 0, arg); 24 | }); 25 | }; 26 | Dep.prototype.clear = function () { 27 | this.deps = []; 28 | }; 29 | return Dep; 30 | }()); 31 | exports.Dep = Dep; 32 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/router.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var routerParams = { 4 | store: {}, 5 | take: function (key) { 6 | var value = this.store[key] || {}; 7 | this.store[key] = null; 8 | return value; 9 | }, 10 | set: function (key, value) { 11 | this.store[key] = value; 12 | } 13 | }; 14 | exports.router = { 15 | go: function (url, params) { 16 | if (params === void 0) { params = {}; } 17 | wx.navigateTo({ 18 | url: url 19 | }); 20 | }, 21 | /** 22 | * 后退页面 23 | * @param target - 接受number或者页面对象, 表示将退出直到显示该页面 24 | */ 25 | back: function (target) { 26 | if (target === void 0) { target = 1; } 27 | var delta = typeof target === 'number' ? target : 1; 28 | wx.navigateBack({ 29 | delta: delta 30 | }); 31 | }, 32 | /** 33 | * 离开页面 34 | * @param target - 往后退,直到离开这个页面 35 | */ 36 | leave: function (target) { } 37 | }; 38 | -------------------------------------------------------------------------------- /packages/reactivity/src/watch.ts: -------------------------------------------------------------------------------- 1 | import { IRef, isRef } from './ref' 2 | import { isFunction } from './utils' 3 | 4 | /** 5 | * 可观测对象 6 | * @param value 7 | */ 8 | export function isObserve (value): value is IRef{ 9 | return isRef(value) 10 | } 11 | 12 | /** 13 | * 监听ref做出回应 14 | * @return {function} 丢弃监听 15 | */ 16 | export function useEffect (callback: (newValue: T, oldValue: T) => any, refs: IRef[]){ 17 | if (!isFunction(callback)) { 18 | return void console.error(` 19 | useEffect callback must be functions 20 | `) 21 | } 22 | if (!refs || !refs.length) { 23 | return void console.error(` 24 | refs must be Ref[] 25 | `) 26 | } 27 | 28 | const handles = refs.map((ref) => { 29 | if (!isObserve(ref)) { 30 | return void console.error(`useEffect refs incloud cant Observe object`) 31 | } 32 | return ref.observe((newValue, oldValue) => { 33 | callback(newValue, oldValue) 34 | }) 35 | }) 36 | 37 | /** 38 | * 移除监听 39 | */ 40 | return () => { 41 | handles.forEach((handle) => { 42 | handle && handle() 43 | }) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/mitt.d.ts: -------------------------------------------------------------------------------- 1 | export declare type EventType = string | symbol; 2 | export declare type Handler = (event?: any) => void; 3 | export declare type WildcardHandler = (type: EventType, event?: any) => void; 4 | export declare type EventHandlerList = Array; 5 | export declare type WildCardEventHandlerList = Array; 6 | export declare type EventHandlerMap = Map; 7 | export interface Emitter { 8 | on(type: EventType, handler: Handler): () => void; 9 | on(type: '*', handler: WildcardHandler): () => void; 10 | once(type: EventType, handler: Handler): () => void; 11 | once(type: '*', handler: WildcardHandler): () => void; 12 | off(type: EventType, handler: Handler): void; 13 | off(type: '*', handler: WildcardHandler): void; 14 | clear(): void; 15 | emit(type: EventType, event?: T): void; 16 | emit(type: '*', event?: any): void; 17 | } 18 | /** Mitt: Tiny (~200b) functional event emitter / pubsub. 19 | * @name mitt 20 | * @returns {Mitt} 21 | */ 22 | export declare function mitt(all?: EventHandlerMap): Emitter; 23 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/instance.ts: -------------------------------------------------------------------------------- 1 | import { ExtendLefecycle } from './lifecycle' 2 | 3 | let currentModule: ICurrentModuleInstance | null = null 4 | 5 | export type ICurrentModuleInstance = WechatMiniprogram.Component.InstanceProperties & 6 | WechatMiniprogram.Component.InstanceMethods> & { 7 | /** 上级页面对象 */ 8 | [ExtendLefecycle.PARENT]?: ICurrentModuleInstance 9 | /** 转发props更新,props变化,会主动调用改方法传值 */ 10 | [ExtendLefecycle.WATCH_PROPERTY]?: { 11 | [propName: string]: (value: any) => void 12 | } 13 | /** loc注册的内容 */ 14 | [ExtendLefecycle.LOC_INJECT]?: { 15 | // 方法体 16 | function_target: Function 17 | // 执行结果集 18 | caches: [any, any][] 19 | }[] 20 | [key: string]: any 21 | } 22 | 23 | /** 24 | * 要求注入的函数第一个参数是 current对象 25 | * @param callback 26 | */ 27 | export function overCurrentModule (callback: () => T){ 28 | return function (this: ICurrentModuleInstance, ...arg: any[]): T{ 29 | currentModule = this 30 | 31 | const reuslt = callback.apply(this, arg) 32 | 33 | currentModule = null 34 | 35 | return reuslt 36 | } 37 | } 38 | 39 | export function overInCurrentModule (callback: (current: ICurrentModuleInstance) => T): T{ 40 | return callback(currentModule) 41 | } 42 | -------------------------------------------------------------------------------- /TENET.MD: -------------------------------------------------------------------------------- 1 | ### 原则 2 | 3 | 1. 避免setup面条式代码 4 | 尽量分化成多个组合函数,以`use`开头 5 | ```js 6 | setup() { 7 | const appInfo = useAppInfo() 8 | 9 | return { appInfo } 10 | } 11 | 12 | ``` 13 | 14 | 2. 为了避免追踪响应式数据变化困难,一个变量值应该只能在其所在组合函数被修改 15 | 16 | ```js 17 | setup(() => { 18 | const appInfo = useAppInfo() 19 | // 不应该这样 20 | useUpdate(appInfo) 21 | 22 | return { 23 | appInfo 24 | } 25 | }) 26 | function useAppInfo () { 27 | const info = ref({}) 28 | return info 29 | } 30 | // 不应该修改其他函数体的响应式数据 31 | function useUpdate(info) { 32 | info.set(false) 33 | } 34 | 35 | ``` 36 | 37 | 3. 当遇到需要公用的模块,可以提取到 [page.name].services.ts中 38 | 小程序中import 页面/组件的 会出现不可预料的问题 39 | 40 | 4. 提取到services下的代码尽量不要使用`onPullDownRefresh`生命周期 41 | 避免最后发现数据莫名加载问题,可以使用onLoad作为初始化数据使用,这些声明周期应该放在index, action层做 42 | 可以index下在进行组合 43 | 但是感觉有点啰嗦 44 | 45 | ```js 46 | setup() { 47 | const {} = useDemo() 48 | } 49 | function useDemo() { 50 | const { renderList, pageList } = useJJJ() 51 | 52 | onPullDownRefresh(() => { 53 | renderList() 54 | }) 55 | 56 | return pageList 57 | } 58 | 59 | ``` 60 | ```js 61 | export function useJJJ () { 62 | 63 | return { 64 | pageList: ref([]), 65 | renderList: () => {} 66 | } 67 | } 68 | ``` -------------------------------------------------------------------------------- /packages/reactivity/lib/watch.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var ref_1 = require("./ref"); 4 | var utils_1 = require("./utils"); 5 | /** 6 | * 可观测对象 7 | * @param value 8 | */ 9 | function isObserve(value) { 10 | return ref_1.isRef(value); 11 | } 12 | exports.isObserve = isObserve; 13 | /** 14 | * 监听ref做出回应 15 | * @return {function} 丢弃监听 16 | */ 17 | function useEffect(callback, refs) { 18 | if (!utils_1.isFunction(callback)) { 19 | return void console.warn("\n useEffect callback must be functions\n "); 20 | } 21 | if (!refs || !refs.length) { 22 | return void console.warn("\n refs must be Ref[]\n "); 23 | } 24 | var handles = refs.map(function (ref) { 25 | if (!isObserve(ref)) { 26 | return void console.warn("useEffect refs incloud cant Observe object"); 27 | } 28 | return ref.__v_change(function (newValue, oldValue) { 29 | callback(newValue, oldValue); 30 | }); 31 | }); 32 | /** 33 | * 移除监听 34 | */ 35 | return function () { 36 | handles.forEach(function (handle) { 37 | handle && handle(); 38 | }); 39 | }; 40 | } 41 | exports.useEffect = useEffect; 42 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/context.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var shared_1 = require("./shared"); 4 | var mitt_1 = require("./mitt"); 5 | var utils_1 = require("./utils"); 6 | var router_1 = require("./router"); 7 | function createContext(target) { 8 | function setData(binding) { 9 | var _this = this; 10 | if (!binding) 11 | return function () { }; 12 | var stopHandels = Object.keys(binding).map(function (key) { 13 | var _a; 14 | var value = binding[key]; 15 | if (utils_1.isFunction(value)) { 16 | _this[key] = value; 17 | return; 18 | } 19 | _this.setData((_a = {}, 20 | _a[key] = shared_1.deepToRaw(value), 21 | _a)); 22 | return shared_1.deepWatch(_this, key, value); 23 | }); 24 | return function () { 25 | stopHandels.forEach(function (stopHandle) { 26 | stopHandle && stopHandle(); 27 | }); 28 | }; 29 | } 30 | return { 31 | setData: setData.bind(target), 32 | event: mitt_1.mitt(), 33 | router: router_1.router, 34 | }; 35 | } 36 | exports.createContext = createContext; 37 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // defineApp({ 3 | // // 跳转规则, 找到对应别名alias/page, 匹配到别名采用别名,没有则匹配page, 最后 则尝试直接跳转页面 4 | // router: { 5 | // tabbar: [ 6 | // { 7 | // // 路径 8 | // url: '/page/index/index', 9 | // // 别名 10 | // alias: 'index' 11 | // } 12 | // ], 13 | // pages: [ 14 | // { 15 | // // 路径 16 | // url: '/page/index/index', 17 | // // 别名 18 | // alias: 'index' 19 | // }, 20 | // { 21 | // url: '/page/message/message', 22 | // // 重定向 {query, params}, query 是路由跳转载荷, params 是路径上的参数对象 23 | // // to,要前往的页面, from来自哪个页面 24 | // beforeEnter: (to, from, next) => { 25 | Object.defineProperty(exports, "__esModule", { value: true }); 26 | // // 调用 next,则跳转到该 url 上,挈带 params,query 27 | // next({ 28 | // url: '', 29 | // alias: '', 30 | // params: {}, 31 | // query: {} 32 | // }) 33 | // } 34 | // } 35 | // ] 36 | // } 37 | // }) 38 | function defineApp(params) { 39 | App(params); 40 | } 41 | exports.defineApp = defineApp; 42 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/context.ts: -------------------------------------------------------------------------------- 1 | import { ICurrentModuleInstance } from './instance' 2 | import { deepToRaw, deepWatch } from './shared' 3 | import { mitt, Emitter } from './mitt' 4 | import { isFunction } from './utils' 5 | import { router } from './router' 6 | 7 | export type IContext = { 8 | setData: (params: { [key: string]: any }) => () => void 9 | event: Emitter 10 | router: typeof router 11 | } 12 | 13 | export function createContext(target: ICurrentModuleInstance): IContext { 14 | function setData( 15 | this: ICurrentModuleInstance, 16 | binding: { [key: string]: any } 17 | ): () => any { 18 | if (!binding) return () => {} 19 | 20 | const stopHandels = Object.keys(binding).map((key) => { 21 | const value = binding[key] 22 | 23 | if (isFunction(value)) { 24 | this[key] = value 25 | return 26 | } 27 | 28 | this.setData({ 29 | [key]: deepToRaw(value), 30 | }) 31 | 32 | return deepWatch(this, key, value) 33 | }) 34 | 35 | return () => { 36 | stopHandels.forEach((stopHandle) => { 37 | stopHandle && stopHandle() 38 | }) 39 | } 40 | } 41 | 42 | return { 43 | setData: setData.bind(target), 44 | event: mitt(), 45 | router, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.isArray = Array.isArray; 4 | function getType(x) { 5 | return Object.prototype.toString.call(x).slice(8, -1); 6 | } 7 | exports.getType = getType; 8 | function isObject(x) { 9 | return x !== null && typeof x === 'object'; 10 | } 11 | exports.isObject = isObject; 12 | function isFunction(x) { 13 | return typeof x === 'function'; 14 | } 15 | exports.isFunction = isFunction; 16 | function isPlainObject(x) { 17 | return x !== null && getType(x) === 'Object'; 18 | } 19 | exports.isPlainObject = isPlainObject; 20 | function wrapFuns() { 21 | var args = []; 22 | for (var _i = 0; _i < arguments.length; _i++) { 23 | args[_i] = arguments[_i]; 24 | } 25 | return function () { 26 | var _this = this; 27 | var params = []; 28 | for (var _i = 0; _i < arguments.length; _i++) { 29 | params[_i] = arguments[_i]; 30 | } 31 | return args.map(function (fun) { 32 | try { 33 | fun && fun.apply(_this, params); 34 | } 35 | catch (e) { 36 | console.error(e); 37 | /** ignore */ 38 | } 39 | }); 40 | }; 41 | } 42 | exports.wrapFuns = wrapFuns; 43 | function createShortName(name) { 44 | return "__" + name + "__"; 45 | } 46 | exports.createShortName = createShortName; 47 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var miniprogram_reactivity_1 = require("miniprogram-reactivity"); 4 | exports.useRef = miniprogram_reactivity_1.useRef; 5 | exports.isRef = miniprogram_reactivity_1.isRef; 6 | var component_1 = require("./component"); 7 | exports.defineComponent = component_1.defineComponent; 8 | var page_1 = require("./page"); 9 | exports.definePage = page_1.definePage; 10 | var router_1 = require("./router"); 11 | exports.router = router_1.router; 12 | var constate_1 = require("./constate"); 13 | exports.createConstate = constate_1.createConstate; 14 | var lifecycle_1 = require("./lifecycle"); 15 | exports.onLoad = lifecycle_1.onLoad; 16 | exports.onReady = lifecycle_1.onReady; 17 | exports.onUnLoad = lifecycle_1.onUnLoad; 18 | exports.onShow = lifecycle_1.onShow; 19 | exports.onHide = lifecycle_1.onHide; 20 | exports.onPullDownRefresh = lifecycle_1.onPullDownRefresh; 21 | exports.onShareAppMessage = lifecycle_1.onShareAppMessage; 22 | exports.onReachBottom = lifecycle_1.onReachBottom; 23 | exports.onPageScroll = lifecycle_1.onPageScroll; 24 | var watch_1 = require("./watch"); 25 | exports.useEffect = watch_1.useEffect; 26 | var computed_1 = require("./computed"); 27 | exports.useComputed = computed_1.useComputed; 28 | var inject_1 = require("./inject"); 29 | exports.useInject = inject_1.useInject; 30 | exports.useProvide = inject_1.useProvide; 31 | var app_1 = require("./app"); 32 | exports.defineApp = app_1.defineApp; 33 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/app.ts: -------------------------------------------------------------------------------- 1 | // defineApp({ 2 | // // 跳转规则, 找到对应别名alias/page, 匹配到别名采用别名,没有则匹配page, 最后 则尝试直接跳转页面 3 | // router: { 4 | // tabbar: [ 5 | // { 6 | // // 路径 7 | // url: '/page/index/index', 8 | // // 别名 9 | // alias: 'index' 10 | // } 11 | // ], 12 | // pages: [ 13 | // { 14 | // // 路径 15 | // url: '/page/index/index', 16 | // // 别名 17 | // alias: 'index' 18 | // }, 19 | // { 20 | // url: '/page/message/message', 21 | // // 重定向 {query, params}, query 是路由跳转载荷, params 是路径上的参数对象 22 | // // to,要前往的页面, from来自哪个页面 23 | // beforeEnter: (to, from, next) => { 24 | 25 | // // 调用 next,则跳转到该 url 上,挈带 params,query 26 | // next({ 27 | // url: '', 28 | // alias: '', 29 | // params: {}, 30 | // query: {} 31 | // }) 32 | // } 33 | // } 34 | // ] 35 | // } 36 | // }) 37 | 38 | export function defineApp ( 39 | params: WechatMiniprogram.App.Options & { 40 | /** 尝试引入 */ 41 | use?: ((app: T) => any)[] 42 | } 43 | ){ 44 | App(params) 45 | } 46 | 47 | defineApp({ 48 | use: [(APP) => { 49 | APP 50 | }], 51 | onLaunch() { 52 | 53 | } 54 | }) 55 | 56 | export function getApp (){} 57 | -------------------------------------------------------------------------------- /examples/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件", 3 | "packOptions": { 4 | "ignore": [] 5 | }, 6 | "setting": { 7 | "urlCheck": false, 8 | "es6": true, 9 | "enhance": false, 10 | "postcss": true, 11 | "preloadBackgroundData": false, 12 | "minified": true, 13 | "newFeature": false, 14 | "coverView": true, 15 | "nodeModules": true, 16 | "autoAudits": false, 17 | "showShadowRootInWxmlPanel": true, 18 | "scopeDataCheck": false, 19 | "uglifyFileName": false, 20 | "checkInvalidKey": true, 21 | "checkSiteMap": true, 22 | "uploadWithSourceMap": true, 23 | "compileHotReLoad": false, 24 | "babelSetting": { 25 | "ignore": [], 26 | "disablePlugins": [], 27 | "outputPath": "" 28 | }, 29 | "useIsolateContext": true, 30 | "useCompilerModule": true, 31 | "userConfirmedUseCompilerModuleSwitch": true 32 | }, 33 | "compileType": "miniprogram", 34 | "libVersion": "2.12.2", 35 | "appid": "wx465870d682edccad", 36 | "projectname": "miniprogram-1", 37 | "debugOptions": { 38 | "hidedInDevtools": [] 39 | }, 40 | "scripts": {}, 41 | "isGameTourist": false, 42 | "simulatorType": "wechat", 43 | "simulatorPluginLibVersion": {}, 44 | "condition": { 45 | "search": { 46 | "current": -1, 47 | "list": [] 48 | }, 49 | "conversation": { 50 | "current": -1, 51 | "list": [] 52 | }, 53 | "game": { 54 | "current": -1, 55 | "list": [] 56 | }, 57 | "plugin": { 58 | "current": -1, 59 | "list": [] 60 | }, 61 | "gamePlugin": { 62 | "current": -1, 63 | "list": [] 64 | }, 65 | "miniprogram": { 66 | "current": -1, 67 | "list": [] 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | Document 11 | 19 | 20 | 21 | 22 |

欢迎使用风火递小程序

23 |
    24 |
  1. 需先在电脑端, 绑定店铺
  2. 25 |
  3. 在微信小程序“风火递”中同步订单
  4. 26 |
27 |

绑定店铺步骤

28 |
    29 |
  1. 30 |
    请在电脑上微信登录风火递通用版(www.fhd001.com),按引导完成关联
    31 |
    复制链接
    36 |
  2. 37 |
38 |

如何使用微信小程序

39 |
    40 |
  • 微信小程序搜索“风火递”
  • 41 |
  • 截屏以下二维码, 打开微信扫一扫, 扫描保存的截图 42 | 43 |
  • 44 |
45 | 46 | 47 | 55 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/lifecycle.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var utils_1 = require("./utils"); 4 | var instance_1 = require("./instance"); 5 | /**注入hooks */ 6 | function injectHook(currentInstance, lifecycle, hook) { 7 | var hiddenField = utils_1.createShortName(lifecycle); 8 | if (currentInstance[hiddenField] === undefined) { 9 | currentInstance[hiddenField] = []; 10 | } 11 | currentInstance[hiddenField].push(hook); 12 | } 13 | exports.injectHook = injectHook; 14 | /**执行hooks */ 15 | function conductHook(currentInstance, lifecycle, params) { 16 | var hiddenField = utils_1.createShortName(lifecycle); 17 | var injectLifes = currentInstance[hiddenField] || []; 18 | return injectLifes.map(function (life) { return typeof life === 'function' && life.apply(currentInstance, params); }); 19 | } 20 | exports.conductHook = conductHook; 21 | function createCurrentModuleHook(lifecycle) { 22 | return function (callback) { 23 | instance_1.overInCurrentModule(function (currentInstance) { 24 | currentInstance && injectHook(currentInstance, lifecycle, callback); 25 | }); 26 | }; 27 | } 28 | /** 实例被加载, Page.onLoad, Components.attached */ 29 | exports.onLoad = createCurrentModuleHook("onLoad" /* ON_LOAD */); 30 | /** 实例被销毁, Page.onUnLoad, Components.destory */ 31 | exports.onUnLoad = createCurrentModuleHook("onUnload" /* ON_UN_LOAD */); 32 | /** 实例装载完成, Page.onReady, Components.ready */ 33 | exports.onReady = createCurrentModuleHook("onReady" /* ON_READY */); 34 | /** 页面显示 */ 35 | exports.onShow = createCurrentModuleHook("onShow" /* ON_SHOW */); 36 | /** 页面隐藏 */ 37 | exports.onHide = createCurrentModuleHook("onHide" /* ON_HIDE */); 38 | /** 下拉刷新 */ 39 | exports.onPullDownRefresh = createCurrentModuleHook("onPullDownRefresh" /* ON_PULL_DOWN_REFRESH */); 40 | /** 滚动到底部 */ 41 | exports.onReachBottom = createCurrentModuleHook("onReachBottom" /* ON_REACH_BOTTOM */); 42 | /** 转发 */ 43 | exports.onShareAppMessage = createCurrentModuleHook("onShareAppMessage" /* ON_SHARE_APP_MESSAGE */); 44 | /** 页面滚动 */ 45 | exports.onPageScroll = createCurrentModuleHook("onPageScroll" /* ON_PAGE_SCROLL */); 46 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/constate.ts: -------------------------------------------------------------------------------- 1 | import { injectHook, ExtendLefecycle } from './lifecycle' 2 | import { overInCurrentModule } from './instance' 3 | import { Parameters, ReturnType } from './interface' 4 | 5 | import { clone } from '@jsmini/clone' 6 | import { isEqual } from '@jsmini/isequal' 7 | 8 | /** 9 | * 10 | * 存在期间的 单一实例 11 | * 所有自定义组件/页面共享数据, 当被依赖的页面/组件都被销毁时,重新加载第一遍会被执行一次 12 | * 请在setup期间调用!! 13 | */ 14 | export function createConstate any>(callback: T) { 15 | function useConstate() { 16 | let EMPYT_KEY = Symbol() 17 | let CACHE: any = EMPYT_KEY 18 | let injectCurrentModules = [] 19 | 20 | return (args: any) => { 21 | overInCurrentModule((currentInstance) => { 22 | if (!currentInstance) { 23 | return 24 | } 25 | injectCurrentModules.unshift(currentInstance) 26 | injectHook(currentInstance, ExtendLefecycle.EFFECT, () => { 27 | const findIndex = injectCurrentModules.findIndex( 28 | (mo) => mo === currentInstance 29 | ) 30 | if (~findIndex) { 31 | injectCurrentModules.splice(findIndex, 1) 32 | } 33 | 34 | if (injectCurrentModules.length === 0) { 35 | CACHE = EMPYT_KEY 36 | } 37 | }) 38 | }) 39 | 40 | if (CACHE !== EMPYT_KEY) { 41 | return CACHE 42 | } 43 | 44 | CACHE = callback(...args) 45 | 46 | return CACHE 47 | } 48 | } 49 | 50 | let CACHE_PARAMS: { params: any; constate: any }[] = [] 51 | 52 | return (...args: Parameters): ReturnType => { 53 | let findIndex = CACHE_PARAMS.findIndex(({ params }) => { 54 | return isEqual(params, args) 55 | }) 56 | 57 | if (!~findIndex) { 58 | CACHE_PARAMS.unshift({ 59 | params: clone(args), 60 | constate: useConstate(), 61 | }) 62 | findIndex = 0 63 | } 64 | 65 | return CACHE_PARAMS[findIndex].constate(args) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/runtime": { 8 | "version": "7.11.2", 9 | "resolved": "http://192.168.0.233:4873/@babel%2fruntime/-/runtime-7.11.2.tgz", 10 | "integrity": "sha1-9UnBPHVMxAuHZEufqfCaapX+BzY=", 11 | "requires": { 12 | "regenerator-runtime": "^0.13.4" 13 | } 14 | }, 15 | "@jsmini/clone": { 16 | "version": "0.4.2", 17 | "resolved": "http://192.168.0.233:4873/@jsmini%2fclone/-/clone-0.4.2.tgz", 18 | "integrity": "sha1-Wt0KYWi4FUVSNJ74IIbH+VYZX+E=", 19 | "requires": { 20 | "@babel/runtime": "^7.1.2", 21 | "@jsmini/type": "^0.9.1" 22 | } 23 | }, 24 | "@jsmini/functional": { 25 | "version": "0.2.3", 26 | "resolved": "http://192.168.0.233:4873/@jsmini%2ffunctional/-/functional-0.2.3.tgz", 27 | "integrity": "sha1-Pgcud7wC1UQ2jg/lJJRFBomMq1U=", 28 | "requires": { 29 | "@babel/runtime": "^7.1.2", 30 | "@jsmini/type": "^0.9.0" 31 | } 32 | }, 33 | "@jsmini/isequal": { 34 | "version": "0.4.2", 35 | "resolved": "http://192.168.0.233:4873/@jsmini%2fisequal/-/isequal-0.4.2.tgz", 36 | "integrity": "sha1-GPGetMuv2DKaK/Dk7wI0w/me/IQ=", 37 | "requires": { 38 | "@jsmini/functional": "^0.2.2", 39 | "@jsmini/type": "^0.9.0" 40 | } 41 | }, 42 | "@jsmini/type": { 43 | "version": "0.9.2", 44 | "resolved": "http://192.168.0.233:4873/@jsmini%2ftype/-/type-0.9.2.tgz", 45 | "integrity": "sha1-01M5V7791KJL9ep2E//0U7WOL/k=", 46 | "requires": { 47 | "@babel/runtime": "^7.1.2" 48 | } 49 | }, 50 | "miniprogram-composition-api": { 51 | "version": "0.2.4", 52 | "resolved": "http://192.168.0.233:4873/miniprogram-composition-api/-/miniprogram-composition-api-0.2.4.tgz", 53 | "integrity": "sha1-fnQnORMN3yhJYFeI08qurT3XYWY=", 54 | "requires": { 55 | "@jsmini/clone": "^0.4.2", 56 | "@jsmini/isequal": "^0.4.2" 57 | } 58 | }, 59 | "regenerator-runtime": { 60 | "version": "0.13.7", 61 | "resolved": "http://192.168.0.233:4873/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", 62 | "integrity": "sha1-ysLazIoepnX+qrrriugziYrkb1U=" 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/constate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var lifecycle_1 = require("./lifecycle"); 4 | var instance_1 = require("./instance"); 5 | var clone_1 = require("@jsmini/clone"); 6 | var isequal_1 = require("@jsmini/isequal"); 7 | /** 8 | * 9 | * 存在期间的 单一实例 10 | * 所有自定义组件/页面共享数据, 当被依赖的页面/组件都被销毁时,重新加载第一遍会被执行一次 11 | * 请在setup期间调用!! 12 | */ 13 | function createConstate(callback) { 14 | function useConstate() { 15 | var EMPYT_KEY = Symbol(); 16 | var CACHE = EMPYT_KEY; 17 | var injectCurrentModules = []; 18 | return function (args) { 19 | instance_1.overInCurrentModule(function (currentInstance) { 20 | if (!currentInstance) { 21 | return; 22 | } 23 | injectCurrentModules.unshift(currentInstance); 24 | lifecycle_1.injectHook(currentInstance, "effect" /* EFFECT */, function () { 25 | var findIndex = injectCurrentModules.findIndex(function (mo) { return mo === currentInstance; }); 26 | if (~findIndex) { 27 | injectCurrentModules.splice(findIndex, 1); 28 | } 29 | if (injectCurrentModules.length === 0) { 30 | CACHE = EMPYT_KEY; 31 | } 32 | }); 33 | }); 34 | if (CACHE !== EMPYT_KEY) { 35 | return CACHE; 36 | } 37 | CACHE = callback.apply(void 0, args); 38 | return CACHE; 39 | }; 40 | } 41 | var CACHE_PARAMS = []; 42 | return function () { 43 | var args = []; 44 | for (var _i = 0; _i < arguments.length; _i++) { 45 | args[_i] = arguments[_i]; 46 | } 47 | var findIndex = CACHE_PARAMS.findIndex(function (_a) { 48 | var params = _a.params; 49 | return isequal_1.isEqual(params, args); 50 | }); 51 | if (!~findIndex) { 52 | CACHE_PARAMS.unshift({ 53 | params: clone_1.clone(args), 54 | constate: useConstate(), 55 | }); 56 | findIndex = 0; 57 | } 58 | return CACHE_PARAMS[findIndex].constate(args); 59 | }; 60 | } 61 | exports.createConstate = createConstate; 62 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/lifecycle.d.ts: -------------------------------------------------------------------------------- 1 | import { ICurrentModuleInstance } from './instance'; 2 | export declare const enum ComponentLifecycle { 3 | CREATED = "created", 4 | ATTACHED = "attached", 5 | READY = "ready", 6 | DETACHED = "detached" 7 | } 8 | export declare const enum PageLifecycle { 9 | ON_LOAD = "onLoad", 10 | ON_SHOW = "onShow", 11 | ON_READY = "onReady", 12 | ON_HIDE = "onHide", 13 | ON_UNLOAD = "onUnload", 14 | ON_PULL_DOWN_REFRESH = "onPullDownRefresh", 15 | ON_REACH_BOTTOM = "onReachBottom", 16 | ON_PAGE_SCROLL = "onPageScroll", 17 | ON_SHARE_APP_MESSAGE = "onShareAppMessage", 18 | ON_RESIZE = "onResize", 19 | ON_TAB_ITEM_TAP = "onTabItemTap" 20 | } 21 | export declare const enum CommonLifecycle { 22 | ON_LOAD = "onLoad", 23 | ON_UN_LOAD = "onUnload", 24 | ON_READY = "onReady" 25 | } 26 | export declare const enum ExtendLefecycle { 27 | /** 副作用 */ 28 | EFFECT = "effect", 29 | /** props代理 */ 30 | WATCH_PROPERTY = "__watchProperty__", 31 | /** 父实例 */ 32 | PARENT = "__parent__", 33 | LOC_INJECT = "__loc_inject__" 34 | } 35 | /**注入hooks */ 36 | export declare function injectHook(currentInstance: ICurrentModuleInstance, lifecycle: PageLifecycle | ComponentLifecycle | ExtendLefecycle | CommonLifecycle, hook: Function): void; 37 | /**执行hooks */ 38 | export declare function conductHook(currentInstance: ICurrentModuleInstance, lifecycle: PageLifecycle | ComponentLifecycle | ExtendLefecycle | CommonLifecycle, params: any[]): any[]; 39 | /** 实例被加载, Page.onLoad, Components.attached */ 40 | export declare const onLoad: (callback: Function) => void; 41 | /** 实例被销毁, Page.onUnLoad, Components.destory */ 42 | export declare const onUnLoad: (callback: Function) => void; 43 | /** 实例装载完成, Page.onReady, Components.ready */ 44 | export declare const onReady: (callback: Function) => void; 45 | /** 页面显示 */ 46 | export declare const onShow: (callback: Function) => void; 47 | /** 页面隐藏 */ 48 | export declare const onHide: (callback: Function) => void; 49 | /** 下拉刷新 */ 50 | export declare const onPullDownRefresh: (callback: Function) => void; 51 | /** 滚动到底部 */ 52 | export declare const onReachBottom: (callback: Function) => void; 53 | /** 转发 */ 54 | export declare const onShareAppMessage: (callback: Function) => void; 55 | /** 页面滚动 */ 56 | export declare const onPageScroll: (callback: Function) => void; 57 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/beta/constate.ts: -------------------------------------------------------------------------------- 1 | import { injectHook, ExtendLefecycle } from '../src/lifecycle' 2 | import { overInCurrentModule } from '../src/instance' 3 | 4 | import { clone } from '@jsmini/clone' 5 | import { isEqual } from '@jsmini/isequal' 6 | const RUN_HANDLES: { fun: any; params: { limit: any; done: Function }[] }[] = [] 7 | 8 | /** 9 | * 10 | * 创建单例共享空间, 用于实现单例依赖注入 11 | * 12 | * 和 export 定义区别在于, 这个示例是在执行期间被执行, 在页面都被销毁后才一起销毁, 只示例一次, 13 | * 支持传参数,如果传了参数,那么参数不同,将会渲染不同的实例!!! 14 | */ 15 | export function useConstate any>( 16 | fun: F, 17 | ...args: Parameters 18 | ): ReturnType { 19 | let findFunIndex = RUN_HANDLES.findIndex((handle) => { 20 | return handle.fun === fun 21 | }) 22 | 23 | if (!~findFunIndex) { 24 | RUN_HANDLES.unshift({ 25 | fun, 26 | params: [], 27 | }) 28 | findFunIndex = 0 29 | } 30 | 31 | let findFunParamsIndex = RUN_HANDLES[findFunIndex].params.findIndex( 32 | ({ limit }) => { 33 | return isEqual(limit, args) 34 | } 35 | ) 36 | if (!~findFunParamsIndex) { 37 | RUN_HANDLES[findFunIndex].params.unshift({ 38 | limit: clone(args), 39 | done: initConstate(fun), 40 | }) 41 | findFunParamsIndex = 0 42 | } 43 | 44 | return RUN_HANDLES[findFunIndex].params[findFunParamsIndex].done(args) 45 | } 46 | 47 | function initConstate(callback: (...params: any[]) => any) { 48 | let EMPTY_KEY = Symbol() 49 | let RUN_RESULT: Symbol | any = EMPTY_KEY 50 | let injectCurrentModules = [] 51 | return (params: any[]) => { 52 | overInCurrentModule((currentInstance) => { 53 | if (!currentInstance) { 54 | return 55 | } 56 | injectCurrentModules.push(currentInstance) 57 | injectHook(currentInstance, ExtendLefecycle.EFFECT, () => { 58 | const findIndex = injectCurrentModules.findIndex( 59 | (mo) => mo === currentInstance 60 | ) 61 | if (~findIndex) { 62 | injectCurrentModules.splice(findIndex, 1) 63 | } 64 | 65 | if (injectCurrentModules.length === 0) { 66 | RUN_RESULT = EMPTY_KEY 67 | } 68 | }) 69 | }) 70 | 71 | if (RUN_RESULT !== EMPTY_KEY) { 72 | return RUN_RESULT 73 | } 74 | 75 | RUN_RESULT = callback(...params) 76 | 77 | return RUN_RESULT 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### TODO 4 | 1. 部分api静止set 5 | 5. router.go({ url: '', params:{} }), 自定义路由方法, params支持传入方法, 子页面可以被正常调用被传入的方法 6 | 5.1. router.back({ delta: 1, params: {}}) 后退的参数, 是否允许带到 onShow, 是否有必要 7 | 6. router支持别名, 用于解决以前是 /pages/logistics, 现在是 /sub-logistics/logsitcs 路径问题, 拦截这个别名, 跳转到我指定的路径 8 | 7. 对于tabbar页面实现页面传参, 额外添加声明周期 onTabPageShow 可以接受到 跳转到当前页, 相当于 onShow生命周期,用于解决tab页面第二次进入onLoad不触发, onShow也没有参数的问题, 还需要配置 让框架知道 哪些页面是tabbar页面, onTabPageShow需兼容直接进入的情况, 不通过自带的参数进来也需要能参数带来 9 | 8. 全局Components, Page混入还是有必要的, 比如 小程序双向绑定通过 bind:ing="$", 需要功能混入 $方法 10 | 11. context event 允许监听 声明周期方法 11 | 12. 自定义组件和page组件生命周期统一√ 12 | 13. 自定义get,set ref, 实现set转意, get也能格式化 13 | 14. 事件传递很麻烦,例如input基础上又来了个inputCacle, inputCacle需要把input所有的事件再统一传递出去,很烦人, 该怎么解决没想好 14 | 15 | 16 | ### 获取父亲的声明周期 17 | 1. 18 | ```js 19 | 20 | onPageLife(() => { 21 | onReachBottom(() => { 22 | 23 | }) 24 | 25 | onPullDownRefresh(() => { 26 | 27 | }) 28 | }) 29 | ``` 30 | 31 | 2. 32 | ```js 33 | usePageEvent('onReachBottom', () => { 34 | console.log('onReachBottom'); 35 | }); 36 | usePageEvent('onShow', () => { 37 | console.log('onShow'); 38 | }); 39 | ``` 40 | 41 | ### 共享空间 42 | 这个目前打算创建一个单例工程模式 useConstate ,用于在setup期间创建单例 43 | 44 | ```js 45 | function useName (params) { 46 | const name = useRef(1) 47 | onLoad(() => { 48 | name.set(2) 49 | }) 50 | 51 | return name 52 | } 53 | 54 | // 因为考虑到很多业务的情况, 有些空间都带有一些异步请求后的参数才能被加载, 对于那些需要难搞的参数才能初始化的api,于是用异步的injectContext来实现加载 55 | // 也就是说采用了 injectContext, 将意味着 无法通过setup注入(setup不允许异步这是铁板钉钉上的事情) 56 | // 要不展示不考虑? 57 | 58 | ``` 59 | ```js 60 | // A页面 61 | defineComponent(() => { 62 | const a = useConstate(useName)() 63 | 64 | onReachBottom(() => { 65 | a.renderList() 66 | }) 67 | }) 68 | 69 | // B页面 70 | defineComponent(() => { 71 | const b = useConstate(useName) 72 | 73 | return { 74 | ...b 75 | } 76 | }) 77 | ``` 78 | 这样,对a进行更改,b也会同步修改了,原本是想,后来发现,这样和依赖注入没啥区别,不能很好的找到, 都有谁共享了我 79 | ```js 80 | setup(() => { 81 | const a = useConstate(useName); 82 | }) 83 | ``` 84 | 85 | ### 下一版本将支持router带方法传递 86 | 87 | 方法可以类似于props被带过来, 主要为了减少事件发布订阅 88 | 89 | ```js 90 | 91 | import { router } from ''; 92 | 93 | router.go({ 94 | url: '/pages/createSuccess?isplit=123', 95 | params: { 96 | onSubmit() { 97 | 98 | } 99 | } 100 | }) 101 | 102 | // /pages/createSuccess 103 | definePage((props) => { 104 | props.onSubmit && props.onSubmit(); 105 | }); 106 | 107 | ``` 108 | 109 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/shared.d.ts: -------------------------------------------------------------------------------- 1 | import { IRef } from 'miniprogram-reactivity'; 2 | import { ComponentLifecycle, PageLifecycle, CommonLifecycle } from './lifecycle'; 3 | import { ICurrentModuleInstance } from './instance'; 4 | import { IContext } from './context'; 5 | /** 副作用, 如果是方法,返回null */ 6 | export declare const deepToRaw: (x: unknown) => any; 7 | /** 8 | * Page/Component 与 watch 中转 9 | * @return {function} 抛弃监听 10 | */ 11 | export declare function deepWatch(target: any, key: string, value: any): () => void; 12 | /** 13 | * 执行注册的生命周期 14 | */ 15 | export declare function createLifecycleMethods(lifecycle: ComponentLifecycle | PageLifecycle | CommonLifecycle, options: Object | Function | undefined): (...args: any[]) => any[]; 16 | export declare type IBindings = { 17 | [key: string]: any; 18 | }; 19 | declare type IAnyObject = Record; 20 | declare type PropertyType = StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor | null; 21 | declare type ValueType = T extends StringConstructor ? string : T extends NumberConstructor ? number : T extends BooleanConstructor ? boolean : T extends ArrayConstructor ? any[] : T extends ObjectConstructor ? IAnyObject : any; 22 | declare type FullProperty = { 23 | /** 属性类型 */ 24 | type: T; 25 | /** 默认值 */ 26 | value?: ValueType; 27 | }; 28 | export declare type AllFullProperty = FullProperty | FullProperty | FullProperty | FullProperty | FullProperty | FullProperty; 29 | declare type ShortProperty = StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor | null; 30 | export declare type AllProperty = AllFullProperty | ShortProperty; 31 | interface IProps { 32 | [keyName: string]: AllProperty; 33 | } 34 | declare type PropertyToData = T extends ShortProperty ? ValueType : T extends AllFullProperty ? ValueType : any; 35 | declare type PropertyOptionToData

= { 36 | [name in keyof P]: IRef>; 37 | }; 38 | export declare type ISetup

any; 40 | }, INJECT extends { 41 | [key: string]: () => any; 42 | }> = (this: ICurrentModuleInstance, props: PropertyOptionToData

, context: IContext & { 43 | provide: ParamsCallback; 44 | inject: ParamsCallback; 45 | }) => IBindings; 46 | declare type ParamsCallback

any; 48 | }> = { 49 | [name in keyof P]: ReturnType; 50 | }; 51 | export declare function createDI

any; 53 | }>(params: P, next: (callback: any) => any): ParamsCallback

; 54 | export {}; 55 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/mitt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | /** Mitt: Tiny (~200b) functional event emitter / pubsub. 4 | * @name mitt 5 | * @returns {Mitt} 6 | */ 7 | function mitt(all) { 8 | all = all || new Map(); 9 | return { 10 | /** 11 | * Register an event handler for the given type. 12 | * @param {string|symbol} type Type of event to listen for, or `"*"` for all events 13 | * @param {Function} handler Function to call in response to given event 14 | * @memberOf mitt 15 | */ 16 | on: function (type, handler) { 17 | var _this_1 = this; 18 | var handlers = all.get(type); 19 | var added = handlers && handlers.unshift(handler); 20 | if (!added) { 21 | all.set(type, [handler]); 22 | } 23 | return function () { 24 | _this_1.off(type, handler); 25 | }; 26 | }, 27 | once: function (type, handler) { 28 | var _this = this; 29 | var mergeHandle = function () { 30 | var params = []; 31 | for (var _i = 0; _i < arguments.length; _i++) { 32 | params[_i] = arguments[_i]; 33 | } 34 | handler.apply(null, params); 35 | _this.off(type, mergeHandle); 36 | }; 37 | return this.on(type, mergeHandle); 38 | }, 39 | /** 40 | * Remove an event handler for the given type. 41 | * 42 | * @param {string|symbol} type Type of event to unregister `handler` from, or `"*"` 43 | * @param {Function} handler Handler function to remove 44 | * @memberOf mitt 45 | */ 46 | off: function (type, handler) { 47 | var handlers = all.get(type); 48 | if (handlers) { 49 | handlers.splice(handlers.indexOf(handler) >>> 0, 1); 50 | } 51 | }, 52 | clear: function () { 53 | all.clear(); 54 | }, 55 | /** 56 | * Invoke all handlers for the given type. 57 | * If present, `"*"` handlers are invoked after type-matched handlers. 58 | * 59 | * Note: Manually firing "*" handlers is not supported. 60 | * 61 | * @param {string|symbol} type The event type to invoke 62 | * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler 63 | * @memberOf mitt 64 | */ 65 | emit: function (type, evt) { 66 | (all.get(type) || []).slice().map(function (handler) { handler(evt); }); 67 | (all.get('*') || []).slice().map(function (handler) { handler(type, evt); }); 68 | } 69 | }; 70 | } 71 | exports.mitt = mitt; 72 | -------------------------------------------------------------------------------- /packages/reactivity/src/ref.ts: -------------------------------------------------------------------------------- 1 | import { clone } from '@jsmini/clone' 2 | import { isEqual } from '@jsmini/isequal' 3 | 4 | import { Dep } from './dep' 5 | import { isFunction } from './utils' 6 | 7 | export interface IRef { 8 | value: T 9 | /** 提取值 */ 10 | get(): T 11 | /** 12 | * @param {any} params 值 13 | * @param {object} config 14 | * @param {boolean} config.notify - 是否强制更新 15 | */ 16 | set(params: T | ((value: T) => T), config?: { notify: boolean }): void 17 | 18 | __v_isRef: boolean 19 | /** 20 | * 更新通知 21 | */ 22 | observe: (callback: (newValue: T, oldValue: T) => any) => /** 清除句柄 */ () => any 23 | /** 24 | * 清除所有的监听 25 | */ 26 | __v_clear: () => void 27 | } 28 | 29 | export function isRef (r: IRef | unknown): r is IRef 30 | export function isRef (r: any): r is IRef{ 31 | return r ? r.__v_isRef === true : false 32 | } 33 | 34 | /** 35 | * @param {any} value - 初始值 36 | * ```js 37 | const number = useRef(0) 38 | number.set({ 39 | name: 2 40 | }); 41 | * 42 | * ``` 43 | */ 44 | export function useRef (value: T): IRef{ 45 | return createRef(value) 46 | } 47 | 48 | function createRef (viewDate: T){ 49 | /** 视图层的数据,只有在set的时候才能被获取更改 */ 50 | viewDate = clone(viewDate) 51 | /** 对外的数据,允许更改 */ 52 | let outDate = clone(viewDate) 53 | 54 | const dep = new Dep() 55 | const ref = Object.create(null) 56 | 57 | Object.defineProperties(ref, { 58 | value: { 59 | get () { 60 | return outDate 61 | }, 62 | set () { 63 | console.error(` 64 | 请不要直接修改 ref.value 值 65 | `) 66 | } 67 | }, 68 | get: { 69 | value: () => { 70 | return outDate 71 | }, 72 | configurable: false, 73 | writable: false, 74 | enumerable: false 75 | }, 76 | set: { 77 | value: (value: ((params: T) => T) | T, config = { notify: false }) => { 78 | let updateValue: T 79 | if (isFunction(value)) { 80 | updateValue = value(clone(outDate)) 81 | } else { 82 | updateValue = value 83 | } 84 | 85 | if (config.notify || !isEqual(viewDate, updateValue)) { 86 | let beforeViewDate = clone(viewDate) 87 | 88 | viewDate = clone(updateValue) 89 | outDate = clone(updateValue) 90 | 91 | dep.notify(updateValue, beforeViewDate) 92 | } 93 | }, 94 | configurable: false, 95 | writable: false, 96 | enumerable: false 97 | }, 98 | toString: { 99 | value: () => String(viewDate), 100 | configurable: false, 101 | writable: false, 102 | enumerable: false 103 | }, 104 | __v_isRef: { 105 | value: true, 106 | configurable: false, 107 | writable: false, 108 | enumerable: false 109 | }, 110 | observe: { 111 | value: (callback: Function) => { 112 | return dep.depend(callback) 113 | }, 114 | configurable: false, 115 | writable: false, 116 | enumerable: false 117 | }, 118 | __v_clear: { 119 | value: () => { 120 | dep.clear() 121 | }, 122 | configurable: false, 123 | writable: false, 124 | enumerable: false 125 | } 126 | }) 127 | 128 | return ref 129 | } -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/shared.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var miniprogram_reactivity_1 = require("miniprogram-reactivity"); 4 | var utils_1 = require("./utils"); 5 | var diff_1 = require("./diff"); 6 | var over_1 = require("./over"); 7 | var lifecycle_1 = require("./lifecycle"); 8 | var watch_1 = require("./watch"); 9 | /** 副作用, 如果是方法,返回null */ 10 | exports.deepToRaw = over_1.overCloneDeep(function (x) { 11 | if (utils_1.isFunction(x)) { 12 | return null; 13 | } 14 | if (miniprogram_reactivity_1.isRef(x)) { 15 | return x.value; 16 | } 17 | if (utils_1.isArray(x)) { 18 | return x.map(function (item) { return exports.deepToRaw(item); }); 19 | } 20 | if (utils_1.isPlainObject(x)) { 21 | var obj_1 = {}; 22 | Object.keys(x).forEach(function (key) { 23 | obj_1[key] = exports.deepToRaw(x[key]); 24 | }); 25 | return obj_1; 26 | } 27 | return x; 28 | }); 29 | /** 30 | * Page/Component 与 watch 中转 31 | * @return {function} 抛弃监听 32 | */ 33 | function deepWatch(target, key, value) { 34 | var deepEffects = []; 35 | (function observerEffects(x) { 36 | /** 37 | * isObserve必须是在最前面,因为会被isPlainObject解析 38 | */ 39 | if (miniprogram_reactivity_1.isObserve(x)) { 40 | return void deepEffects.push(x); 41 | } 42 | if (utils_1.isArray(x)) { 43 | return void x.map(function (item) { return observerEffects(item); }); 44 | } 45 | if (utils_1.isPlainObject(x)) { 46 | return void Object.keys(x).forEach(function (key) { 47 | observerEffects(x[key]); 48 | }); 49 | } 50 | })(value); 51 | if (!deepEffects.length) { 52 | return; 53 | } 54 | return watch_1.useEffect(function () { 55 | var _a, _b; 56 | target.setData(diff_1.diff((_a = {}, 57 | _a[key] = exports.deepToRaw(value), 58 | _a), (_b = {}, 59 | _b[key] = target.data[key], 60 | _b))); 61 | }, deepEffects); 62 | } 63 | exports.deepWatch = deepWatch; 64 | /** 65 | * 执行注册的生命周期 66 | */ 67 | function createLifecycleMethods(lifecycle, options) { 68 | var lifeMethod = typeof options === 'function' 69 | ? options 70 | : typeof options === 'undefined' 71 | ? undefined 72 | : options[lifecycle]; 73 | return function () { 74 | var args = []; 75 | for (var _i = 0; _i < arguments.length; _i++) { 76 | args[_i] = arguments[_i]; 77 | } 78 | var injectLifes = lifecycle_1.conductHook(this, lifecycle, args); 79 | if (lifeMethod) { 80 | injectLifes.push(lifeMethod.call(this, args)); 81 | } 82 | return injectLifes; 83 | }; 84 | } 85 | exports.createLifecycleMethods = createLifecycleMethods; 86 | function createDI(params, next) { 87 | if (!params) { 88 | // @ts-ignore 89 | return {}; 90 | } 91 | var result = {}; 92 | Object.keys(params).forEach(function (KEY) { 93 | result[KEY] = next(params[KEY]); 94 | }); 95 | return result; 96 | } 97 | exports.createDI = createDI; 98 | -------------------------------------------------------------------------------- /packages/reactivity/lib/ref.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var clone_1 = require("@jsmini/clone"); 4 | var isequal_1 = require("@jsmini/isequal"); 5 | var dep_1 = require("./dep"); 6 | var utils_1 = require("./utils"); 7 | function isRef(r) { 8 | return r ? r.__v_isRef === true : false; 9 | } 10 | exports.isRef = isRef; 11 | /** 12 | * @param {any} value - 初始值 13 | * ```js 14 | const number = useRef(0) 15 | number.set({ 16 | name: 2 17 | }); 18 | * 19 | * ``` 20 | */ 21 | function useRef(value) { 22 | return createRef(value); 23 | } 24 | exports.useRef = useRef; 25 | function createRef(viewDate) { 26 | /** 视图层的数据,只有在set的时候才能被获取更改 */ 27 | viewDate = clone_1.clone(viewDate); 28 | /** 对外的数据,允许更改 */ 29 | var outDate = clone_1.clone(viewDate); 30 | var dep = new dep_1.Dep(); 31 | var ref = Object.create(null); 32 | Object.defineProperties(ref, { 33 | value: { 34 | get: function () { 35 | return outDate; 36 | }, 37 | set: function () { 38 | console.error("\n \u8BF7\u4E0D\u8981\u76F4\u63A5\u4FEE\u6539 ref.value \u503C\n "); 39 | } 40 | }, 41 | get: { 42 | value: function () { 43 | return outDate; 44 | }, 45 | configurable: false, 46 | writable: false, 47 | enumerable: false 48 | }, 49 | set: { 50 | value: function (value, config) { 51 | if (config === void 0) { config = { notify: false }; } 52 | var updateValue; 53 | if (utils_1.isFunction(value)) { 54 | updateValue = value(clone_1.clone(outDate)); 55 | } 56 | else { 57 | updateValue = value; 58 | } 59 | if (config.notify || !isequal_1.isEqual(viewDate, updateValue)) { 60 | var beforeViewDate = clone_1.clone(viewDate); 61 | viewDate = clone_1.clone(updateValue); 62 | outDate = clone_1.clone(updateValue); 63 | dep.notify(updateValue, beforeViewDate); 64 | } 65 | }, 66 | configurable: false, 67 | writable: false, 68 | enumerable: false 69 | }, 70 | toString: { 71 | value: function () { return String(viewDate); }, 72 | configurable: false, 73 | writable: false, 74 | enumerable: false 75 | }, 76 | __v_isRef: { 77 | value: true, 78 | configurable: false, 79 | writable: false, 80 | enumerable: false 81 | }, 82 | __v_change: { 83 | value: function (callback) { 84 | return dep.depend(callback); 85 | }, 86 | configurable: false, 87 | writable: false, 88 | enumerable: false 89 | }, 90 | __v_clear: { 91 | value: function () { 92 | dep.clear(); 93 | }, 94 | configurable: false, 95 | writable: false, 96 | enumerable: false 97 | } 98 | }); 99 | return ref; 100 | } 101 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/inject.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var clone_1 = require("@jsmini/clone"); 4 | var isequal_1 = require("@jsmini/isequal"); 5 | var instance_1 = require("./instance"); 6 | var provides = getApp(); 7 | /** 8 | * 9 | * create and use point 10 | */ 11 | function useProvide(callback) { 12 | var args = []; 13 | for (var _i = 1; _i < arguments.length; _i++) { 14 | args[_i - 1] = arguments[_i]; 15 | } 16 | return instance_1.overInCurrentModule(function (_current) { 17 | var current = _current ? _current : provides; 18 | if (!current["__loc_inject__" /* LOC_INJECT */]) { 19 | current["__loc_inject__" /* LOC_INJECT */] = []; 20 | } 21 | var targetInject = current["__loc_inject__" /* LOC_INJECT */]; 22 | var findFunctionIndex = targetInject.findIndex(function (target) { 23 | return target.function_target === callback; 24 | }); 25 | if (!~findFunctionIndex) { 26 | targetInject.unshift({ 27 | function_target: callback, 28 | caches: [], 29 | }); 30 | findFunctionIndex = 0; 31 | } 32 | var findFunctionResultIndex = targetInject[findFunctionIndex].caches.findIndex(function (cache) { 33 | return isequal_1.isEqual(cache[0], args); 34 | }); 35 | if (!~findFunctionResultIndex) { 36 | targetInject[findFunctionIndex].caches.unshift([ 37 | clone_1.clone(args), 38 | callback.apply(void 0, args), 39 | ]); 40 | findFunctionResultIndex = 0; 41 | } 42 | return targetInject[findFunctionIndex].caches[findFunctionResultIndex][1]; 43 | }); 44 | } 45 | exports.useProvide = useProvide; 46 | /** 47 | * 48 | * use point 49 | */ 50 | function useInject(callback) { 51 | var args = []; 52 | for (var _i = 1; _i < arguments.length; _i++) { 53 | args[_i - 1] = arguments[_i]; 54 | } 55 | var CANT_FIND_KEY = Symbol(); 56 | function getProvide(target) { 57 | if (!target) { 58 | return CANT_FIND_KEY; 59 | } 60 | var targetInject = target["__loc_inject__" /* LOC_INJECT */] || []; 61 | if (!targetInject.length) { 62 | return getProvide(target["__parent__" /* PARENT */]); 63 | } 64 | var findFunctionIndex = targetInject.findIndex(function (target) { 65 | return target.function_target === callback; 66 | }); 67 | if (!~findFunctionIndex) { 68 | return getProvide(target["__parent__" /* PARENT */]); 69 | } 70 | var findFunctionResultIndex = targetInject[findFunctionIndex].caches.findIndex(function (cache) { 71 | return isequal_1.isEqual(cache[0], args); 72 | }); 73 | if (!~findFunctionResultIndex) { 74 | return getProvide(target["__parent__" /* PARENT */]); 75 | } 76 | return targetInject[findFunctionIndex].caches[findFunctionResultIndex][1]; 77 | } 78 | return instance_1.overInCurrentModule(function (_current) { 79 | var current = _current ? _current : provides; 80 | var runResult = getProvide(current); 81 | if (runResult === CANT_FIND_KEY) { 82 | return callback.apply(void 0, args); 83 | } 84 | return runResult; 85 | }); 86 | } 87 | exports.useInject = useInject; 88 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/mitt.ts: -------------------------------------------------------------------------------- 1 | export type EventType = string | symbol; 2 | 3 | // An event handler can take an optional event argument 4 | // and should not return a value 5 | export type Handler = (event?: any) => void; 6 | export type WildcardHandler= (type: EventType, event?: any) => void 7 | 8 | // An array of all currently registered event handlers for a type 9 | export type EventHandlerList = Array; 10 | export type WildCardEventHandlerList = Array; 11 | 12 | // A map of event types and their corresponding event handlers. 13 | export type EventHandlerMap = Map; 14 | 15 | export interface Emitter { 16 | on(type: EventType, handler: Handler): () => void; 17 | on(type: '*', handler: WildcardHandler): () => void; 18 | 19 | once(type: EventType, handler: Handler): () => void; 20 | once(type: '*', handler: WildcardHandler): () => void; 21 | 22 | off(type: EventType, handler: Handler): void; 23 | off(type: '*', handler: WildcardHandler): void; 24 | 25 | clear(): void; 26 | 27 | emit(type: EventType, event?: T): void; 28 | emit(type: '*', event?: any): void; 29 | } 30 | 31 | /** Mitt: Tiny (~200b) functional event emitter / pubsub. 32 | * @name mitt 33 | * @returns {Mitt} 34 | */ 35 | export function mitt(all?: EventHandlerMap): Emitter { 36 | all = all || new Map(); 37 | 38 | return { 39 | 40 | /** 41 | * Register an event handler for the given type. 42 | * @param {string|symbol} type Type of event to listen for, or `"*"` for all events 43 | * @param {Function} handler Function to call in response to given event 44 | * @memberOf mitt 45 | */ 46 | on(type: EventType, handler: Handler) { 47 | const handlers = all.get(type); 48 | const added = handlers && handlers.unshift(handler); 49 | if (!added) { 50 | all.set(type, [handler]); 51 | } 52 | 53 | return () => { 54 | this.off(type, handler) 55 | } 56 | }, 57 | 58 | once(type: EventType, handler: Handler) { 59 | const _this = this; 60 | const mergeHandle = function (...params: any[]) { 61 | handler.apply(null, params); 62 | _this.off(type, mergeHandle); 63 | } 64 | 65 | return this.on(type, mergeHandle); 66 | }, 67 | 68 | /** 69 | * Remove an event handler for the given type. 70 | * 71 | * @param {string|symbol} type Type of event to unregister `handler` from, or `"*"` 72 | * @param {Function} handler Handler function to remove 73 | * @memberOf mitt 74 | */ 75 | off(type: EventType, handler: Handler) { 76 | const handlers = all.get(type); 77 | if (handlers) { 78 | handlers.splice(handlers.indexOf(handler) >>> 0, 1); 79 | } 80 | }, 81 | 82 | clear() { 83 | all.clear(); 84 | }, 85 | 86 | /** 87 | * Invoke all handlers for the given type. 88 | * If present, `"*"` handlers are invoked after type-matched handlers. 89 | * 90 | * Note: Manually firing "*" handlers is not supported. 91 | * 92 | * @param {string|symbol} type The event type to invoke 93 | * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler 94 | * @memberOf mitt 95 | */ 96 | emit(type: EventType, evt: any) { 97 | ((all.get(type) || []) as EventHandlerList).slice().map((handler) => { handler(evt); }); 98 | ((all.get('*') || []) as WildCardEventHandlerList).slice().map((handler) => { handler(type, evt); }); 99 | } 100 | }; 101 | } -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/inject.ts: -------------------------------------------------------------------------------- 1 | import { clone } from '@jsmini/clone' 2 | import { isEqual } from '@jsmini/isequal' 3 | 4 | import { overInCurrentModule, ICurrentModuleInstance } from './instance' 5 | import { ExtendLefecycle } from './lifecycle' 6 | import { Parameters, ReturnType } from './interface' 7 | 8 | /** 9 | * 10 | * create new (function block) if not init that 11 | */ 12 | export function useProvide any> ( 13 | callback: T, 14 | ...args: Parameters 15 | ): ReturnType{ 16 | const that = this 17 | 18 | return overInCurrentModule((current) => { 19 | current = current ? current : that 20 | 21 | if (!current) { 22 | throw new Error('useProvide 缺少 页面实例, 或者你想 useProvide.call(this)?') 23 | } 24 | 25 | if (!current[ExtendLefecycle.LOC_INJECT]) { 26 | current[ExtendLefecycle.LOC_INJECT] = [] 27 | } 28 | 29 | let targetInject = current[ExtendLefecycle.LOC_INJECT] 30 | let findFunctionIndex = targetInject.findIndex((target) => { 31 | return target.function_target === callback 32 | }) 33 | if (!~findFunctionIndex) { 34 | targetInject.unshift({ 35 | function_target: callback, 36 | caches: [] 37 | }) 38 | findFunctionIndex = 0 39 | } 40 | 41 | let findFunctionResultIndex = targetInject[ 42 | findFunctionIndex 43 | ].caches.findIndex((cache) => { 44 | return isEqual(cache[0], args) 45 | }) 46 | 47 | if (!~findFunctionResultIndex) { 48 | targetInject[findFunctionIndex].caches.unshift([ clone(args), callback(...args) ]) 49 | findFunctionResultIndex = 0 50 | } 51 | 52 | return targetInject[findFunctionIndex].caches[findFunctionResultIndex][1] 53 | }) 54 | } 55 | 56 | /** 57 | * 58 | * find and use (function block) if created 59 | * if not find, run useProvide 60 | */ 61 | export function useInject any> ( 62 | callback: T, 63 | ...args: Parameters 64 | ): ReturnType{ 65 | const that = this 66 | const CANT_FIND_KEY = Symbol() 67 | 68 | /** find (function block) */ 69 | function getProvide (target: ICurrentModuleInstance): typeof CANT_FIND_KEY | ReturnType{ 70 | if (!target) { 71 | return CANT_FIND_KEY 72 | } 73 | 74 | /** if not find parent, return app */ 75 | function getParent (target: ICurrentModuleInstance){ 76 | const app = getApp() as ICurrentModuleInstance 77 | 78 | if (target === app) { 79 | return null 80 | } 81 | 82 | return target[ExtendLefecycle.PARENT] || app 83 | } 84 | 85 | const targetInject = target[ExtendLefecycle.LOC_INJECT] || [] 86 | if (!targetInject.length) { 87 | return getProvide(getParent(target)) 88 | } 89 | 90 | const findFunctionIndex = targetInject.findIndex((target) => { 91 | return target.function_target === callback 92 | }) 93 | 94 | if (!~findFunctionIndex) { 95 | return getProvide(getParent(target)) 96 | } 97 | 98 | const findFunctionResultIndex = targetInject[ 99 | findFunctionIndex 100 | ].caches.findIndex((cache) => { 101 | return isEqual(cache[0], args) 102 | }) 103 | 104 | if (!~findFunctionResultIndex) { 105 | return getProvide(getParent(target)) 106 | } 107 | 108 | return targetInject[findFunctionIndex].caches[findFunctionResultIndex][1] 109 | } 110 | 111 | return overInCurrentModule((current) => { 112 | current = current ? current : that 113 | 114 | if (!current) { 115 | throw new Error('useInject 缺少 页面实例, 或者你想 useInject.call(this)?') 116 | } 117 | 118 | const runResult = getProvide(current) 119 | if (runResult !== CANT_FIND_KEY) { 120 | return runResult 121 | } 122 | 123 | return useProvide.call(current, callback, ...args) 124 | }) 125 | } 126 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/diff.ts: -------------------------------------------------------------------------------- 1 | const ARRAYTYPE = '[object Array]' 2 | const OBJECTTYPE = '[object Object]' 3 | const FUNCTIONTYPE = '[object Function]' 4 | 5 | export function diff (current, pre){ 6 | const result = {} 7 | syncKeys(current, pre) 8 | console.log(current, pre) 9 | _diff(current, pre, '', result) 10 | return result 11 | } 12 | 13 | function syncKeys (current, pre){ 14 | if (current === pre) return 15 | const rootCurrentType = type(current) 16 | const rootPreType = type(pre) 17 | if (rootCurrentType == OBJECTTYPE && rootPreType == OBJECTTYPE) { 18 | // if (Object.keys(current).length >= Object.keys(pre).length) { 19 | for (let key in pre) { 20 | const currentValue = current[key] 21 | if (currentValue === undefined) { 22 | current[key] = null 23 | } else { 24 | syncKeys(currentValue, pre[key]) 25 | } 26 | } 27 | // } 28 | } else if (rootCurrentType == ARRAYTYPE && rootPreType == ARRAYTYPE) { 29 | if (current.length >= pre.length) { 30 | pre.forEach((item, index) => { 31 | syncKeys(current[index], item) 32 | }) 33 | } 34 | } 35 | } 36 | 37 | function _diff (current, pre, path, result){ 38 | if (current === pre) return 39 | const rootCurrentType = type(current) 40 | const rootPreType = type(pre) 41 | if (rootCurrentType == OBJECTTYPE) { 42 | if ( 43 | rootPreType != OBJECTTYPE || 44 | Object.keys(current).length < Object.keys(pre).length 45 | ) { 46 | setResult(result, path, current) 47 | } else { 48 | for (let key in current) { 49 | const currentValue = current[key] 50 | const preValue = pre[key] 51 | const currentType = type(currentValue) 52 | const preType = type(preValue) 53 | if (currentType != ARRAYTYPE && currentType != OBJECTTYPE) { 54 | if (currentValue != pre[key]) { 55 | setResult( 56 | result, 57 | (path == '' ? '' : path + '.') + key, 58 | currentValue 59 | ) 60 | } 61 | } else if (currentType == ARRAYTYPE) { 62 | if (preType != ARRAYTYPE) { 63 | setResult( 64 | result, 65 | (path == '' ? '' : path + '.') + key, 66 | currentValue 67 | ) 68 | } else { 69 | if (currentValue.length < preValue.length) { 70 | setResult( 71 | result, 72 | (path == '' ? '' : path + '.') + key, 73 | currentValue 74 | ) 75 | } else { 76 | currentValue.forEach((item, index) => { 77 | _diff( 78 | item, 79 | preValue[index], 80 | (path == '' ? '' : path + '.') + 81 | key + 82 | '[' + 83 | index + 84 | ']', 85 | result 86 | ) 87 | }) 88 | } 89 | } 90 | } else if (currentType == OBJECTTYPE) { 91 | if ( 92 | preType != OBJECTTYPE || 93 | Object.keys(currentValue).length < Object.keys(preValue).length 94 | ) { 95 | setResult( 96 | result, 97 | (path == '' ? '' : path + '.') + key, 98 | currentValue 99 | ) 100 | } else { 101 | for (let subKey in currentValue) { 102 | _diff( 103 | currentValue[subKey], 104 | preValue[subKey], 105 | (path == '' ? '' : path + '.') + key + '.' + subKey, 106 | result 107 | ) 108 | } 109 | } 110 | } 111 | } 112 | } 113 | } else if (rootCurrentType == ARRAYTYPE) { 114 | if (rootPreType != ARRAYTYPE) { 115 | setResult(result, path, current) 116 | } else { 117 | if (current.length < pre.length) { 118 | setResult(result, path, current) 119 | } else { 120 | current.forEach((item, index) => { 121 | _diff(item, pre[index], path + '[' + index + ']', result) 122 | }) 123 | } 124 | } 125 | } else { 126 | setResult(result, path, current) 127 | } 128 | } 129 | 130 | function setResult (result, k, v){ 131 | if (type(v) != FUNCTIONTYPE) { 132 | result[k] = v 133 | } 134 | } 135 | 136 | function type (obj){ 137 | return Object.prototype.toString.call(obj) 138 | } 139 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/page.ts: -------------------------------------------------------------------------------- 1 | import { isFunction, wrapFuns } from './utils' 2 | import { PageLifecycle, conductHook, ExtendLefecycle, CommonLifecycle } from './lifecycle' 3 | import { createContext } from './context' 4 | import { 5 | createDI, 6 | createLifecycleMethods, 7 | createSingleCallbackResultLifecycle, 8 | ISetup 9 | } from './shared' 10 | import { ICurrentModuleInstance, overCurrentModule } from './instance' 11 | import { useInject, useProvide } from './inject' 12 | 13 | export function definePage< 14 | PROVIDE extends { 15 | [key: string]: () => any 16 | }, 17 | INJECT extends { 18 | [key: string]: () => any 19 | } 20 | > ( 21 | pageOptions: 22 | | { 23 | /** 注册服务 */ 24 | provide?: PROVIDE 25 | /** 注入 */ 26 | inject?: INJECT 27 | /** 静态属性,可以被覆盖,初始化显示更快 */ 28 | data?: { 29 | [key: string]: any 30 | } 31 | setup?: ISetup 32 | } 33 | | ISetup 34 | ): any{ 35 | let setupFun: Function 36 | 37 | let options: { 38 | methods?: { 39 | [key: string]: (...args: any[]) => any 40 | } 41 | [key: string]: any 42 | } 43 | 44 | if (isFunction(pageOptions)) { 45 | setupFun = pageOptions 46 | options = {} 47 | } else { 48 | if (pageOptions.setup === void 0) { 49 | return Page(pageOptions) 50 | } 51 | 52 | const { setup: setupOption, ...otherOptions } = pageOptions 53 | setupFun = setupOption 54 | options = otherOptions 55 | } 56 | 57 | /** 绑定上下文 */ 58 | options['$'] = function ( 59 | this: ICurrentModuleInstance, 60 | { detail }: { detail: ICurrentModuleInstance } 61 | ){ 62 | detail[ExtendLefecycle.PARENT] = this 63 | } 64 | 65 | options[PageLifecycle.ON_LOAD] = overCurrentModule( 66 | wrapFuns( 67 | function (this: ICurrentModuleInstance){ 68 | typeof this.triggerEvent === 'function' && 69 | this.triggerEvent('component', this) 70 | }, 71 | function (params){ 72 | const context = createContext(this) 73 | const inject = createDI(options.inject, useInject) 74 | const provide = createDI(options.provide, useProvide) 75 | const binds = setupFun.call( 76 | this, 77 | params, 78 | Object.assign(context, { inject, provide }) 79 | ) 80 | if (binds instanceof Promise) { 81 | return console.error(` 82 | setup不支持返回promise 83 | `) 84 | } 85 | context.setData(binds) 86 | }, 87 | createLifecycleMethods(CommonLifecycle.ON_LOAD, options[PageLifecycle.ON_LOAD]) 88 | ) 89 | ) 90 | 91 | options[PageLifecycle.ON_READY] = createLifecycleMethods( 92 | CommonLifecycle.ON_READY, 93 | options[PageLifecycle.ON_READY] 94 | ) 95 | 96 | options[PageLifecycle.ON_UNLOAD] = wrapFuns(function (){ 97 | conductHook(this, ExtendLefecycle.EFFECT, []) 98 | }, createLifecycleMethods(CommonLifecycle.ON_UN_LOAD, options[PageLifecycle.ON_UNLOAD])) 99 | 100 | options[PageLifecycle.ON_SHOW] = createLifecycleMethods( 101 | PageLifecycle.ON_SHOW, 102 | options[PageLifecycle.ON_SHOW] 103 | ) 104 | 105 | options[PageLifecycle.ON_HIDE] = createLifecycleMethods( 106 | PageLifecycle.ON_HIDE, 107 | options[PageLifecycle.ON_HIDE] 108 | ) 109 | 110 | options[PageLifecycle.ON_RESIZE] = createLifecycleMethods( 111 | PageLifecycle.ON_RESIZE, 112 | options[PageLifecycle.ON_RESIZE] 113 | ) 114 | 115 | options[PageLifecycle.ON_TAB_ITEM_TAP] = createLifecycleMethods( 116 | PageLifecycle.ON_TAB_ITEM_TAP, 117 | options[PageLifecycle.ON_TAB_ITEM_TAP] 118 | ) 119 | 120 | options[PageLifecycle.ON_PULL_DOWN_REFRESH] = createLifecycleMethods( 121 | PageLifecycle.ON_PULL_DOWN_REFRESH, 122 | options[PageLifecycle.ON_PULL_DOWN_REFRESH] 123 | ) 124 | 125 | options[PageLifecycle.ON_REACH_BOTTOM] = createLifecycleMethods( 126 | PageLifecycle.ON_REACH_BOTTOM, 127 | options[PageLifecycle.ON_REACH_BOTTOM] 128 | ) 129 | 130 | options[PageLifecycle.ON_PAGE_SCROLL] = createLifecycleMethods( 131 | PageLifecycle.ON_PAGE_SCROLL, 132 | options[PageLifecycle.ON_PAGE_SCROLL] 133 | ) 134 | 135 | options[PageLifecycle.ON_ADD_TO_FAVORITES] = createSingleCallbackResultLifecycle( 136 | PageLifecycle.ON_ADD_TO_FAVORITES, 137 | options[PageLifecycle.ON_ADD_TO_FAVORITES] 138 | ) 139 | 140 | options[PageLifecycle.ON_SHARE_APP_MESSAGE] = createSingleCallbackResultLifecycle( 141 | PageLifecycle.ON_SHARE_APP_MESSAGE, 142 | options[PageLifecycle.ON_SHARE_APP_MESSAGE] 143 | ) 144 | 145 | options[PageLifecycle.ON_SHARE_TIME_LINE] = createSingleCallbackResultLifecycle( 146 | PageLifecycle.ON_SHARE_TIME_LINE, 147 | options[PageLifecycle.ON_SHARE_TIME_LINE] 148 | ) 149 | 150 | return Page(options) 151 | } 152 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/page.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __rest = (this && this.__rest) || function (s, e) { 3 | var t = {}; 4 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) 5 | t[p] = s[p]; 6 | if (s != null && typeof Object.getOwnPropertySymbols === "function") 7 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { 8 | if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) 9 | t[p[i]] = s[p[i]]; 10 | } 11 | return t; 12 | }; 13 | var __spreadArrays = (this && this.__spreadArrays) || function () { 14 | for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; 15 | for (var r = Array(s), k = 0, i = 0; i < il; i++) 16 | for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) 17 | r[k] = a[j]; 18 | return r; 19 | }; 20 | Object.defineProperty(exports, "__esModule", { value: true }); 21 | var utils_1 = require("./utils"); 22 | var lifecycle_1 = require("./lifecycle"); 23 | var context_1 = require("./context"); 24 | var shared_1 = require("./shared"); 25 | var instance_1 = require("./instance"); 26 | var inject_1 = require("./inject"); 27 | function definePage(pageOptions) { 28 | var setupFun; 29 | var options; 30 | if (utils_1.isFunction(pageOptions)) { 31 | setupFun = pageOptions; 32 | options = {}; 33 | } 34 | else { 35 | if (pageOptions.setup === void 0) { 36 | return Page(pageOptions); 37 | } 38 | var setupOption = pageOptions.setup, otherOptions = __rest(pageOptions, ["setup"]); 39 | setupFun = setupOption; 40 | options = otherOptions; 41 | } 42 | /** 绑定上下文 */ 43 | options['$'] = function (_a) { 44 | var detail = _a.detail; 45 | detail["__parent__" /* PARENT */] = this; 46 | }; 47 | options["onLoad" /* ON_LOAD */] = instance_1.overCurrentModule(utils_1.wrapFuns(function () { 48 | typeof this.triggerEvent === 'function' && 49 | this.triggerEvent('component', this); 50 | }, function (params) { 51 | var context = context_1.createContext(this); 52 | var inject = shared_1.createDI(options.inject, inject_1.useInject); 53 | var provide = shared_1.createDI(options.provide, inject_1.useProvide); 54 | var binds = setupFun.call(this, params, Object.assign(context, { inject: inject, provide: provide })); 55 | if (binds instanceof Promise) { 56 | return console.error("\n setup\u4E0D\u652F\u6301\u8FD4\u56DEpromise\n "); 57 | } 58 | context.setData(binds); 59 | }, shared_1.createLifecycleMethods("onLoad" /* ON_LOAD */, options["onLoad" /* ON_LOAD */]))); 60 | options["onReady" /* ON_READY */] = shared_1.createLifecycleMethods("onReady" /* ON_READY */, options["onReady" /* ON_READY */]); 61 | options["onUnload" /* ON_UNLOAD */] = utils_1.wrapFuns(function () { 62 | lifecycle_1.conductHook(this, "effect" /* EFFECT */, []); 63 | }, shared_1.createLifecycleMethods("onUnload" /* ON_UN_LOAD */, options["onUnload" /* ON_UNLOAD */])); 64 | options["onShow" /* ON_SHOW */] = shared_1.createLifecycleMethods("onShow" /* ON_SHOW */, options); 65 | options["onHide" /* ON_HIDE */] = shared_1.createLifecycleMethods("onHide" /* ON_HIDE */, options); 66 | options["onResize" /* ON_RESIZE */] = shared_1.createLifecycleMethods("onResize" /* ON_RESIZE */, options); 67 | options["onTabItemTap" /* ON_TAB_ITEM_TAP */] = shared_1.createLifecycleMethods("onTabItemTap" /* ON_TAB_ITEM_TAP */, options); 68 | options["onPullDownRefresh" /* ON_PULL_DOWN_REFRESH */] = shared_1.createLifecycleMethods("onPullDownRefresh" /* ON_PULL_DOWN_REFRESH */, options); 69 | options["onReachBottom" /* ON_REACH_BOTTOM */] = shared_1.createLifecycleMethods("onReachBottom" /* ON_REACH_BOTTOM */, options); 70 | options["onPageScroll" /* ON_PAGE_SCROLL */] = shared_1.createLifecycleMethods("onPageScroll" /* ON_PAGE_SCROLL */, options); 71 | options["onShareAppMessage" /* ON_SHARE_APP_MESSAGE */] = (function () { 72 | var lifecycleMethod = shared_1.createLifecycleMethods("onShareAppMessage" /* ON_SHARE_APP_MESSAGE */, options); 73 | return function () { 74 | var args = []; 75 | for (var _i = 0; _i < arguments.length; _i++) { 76 | args[_i] = arguments[_i]; 77 | } 78 | var runResults = lifecycleMethod.apply.apply(lifecycleMethod, __spreadArrays([this], args)); 79 | return runResults[runResults.length - 1]; 80 | }; 81 | })(); 82 | return Page(options); 83 | } 84 | exports.definePage = definePage; 85 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/lifecycle.ts: -------------------------------------------------------------------------------- 1 | import { createShortName } from './utils' 2 | import { ICurrentModuleInstance, overInCurrentModule } from './instance' 3 | 4 | export const enum AppLifecycle { 5 | ON_LAUNCH = 'onLaunch', 6 | ON_SHOW = 'onShow', 7 | ON_HIDE = 'onHide', 8 | ON_ERROR = 'onError', 9 | ON_PAGE_NOT_FOUND = 'onPageNotFound', 10 | ON_THEME_CHANGE = 'onThemeChange' 11 | } 12 | 13 | export const enum ComponentLifecycle { 14 | CREATED = 'created', 15 | ATTACHED = 'attached', 16 | READY = 'ready', 17 | DETACHED = 'detached' 18 | } 19 | 20 | export const enum PageLifecycle { 21 | ON_LOAD = 'onLoad', 22 | ON_SHOW = 'onShow', 23 | ON_READY = 'onReady', 24 | ON_HIDE = 'onHide', 25 | ON_UNLOAD = 'onUnload', 26 | ON_PULL_DOWN_REFRESH = 'onPullDownRefresh', 27 | ON_REACH_BOTTOM = 'onReachBottom', 28 | ON_PAGE_SCROLL = 'onPageScroll', 29 | ON_SHARE_APP_MESSAGE = 'onShareAppMessage', 30 | ON_RESIZE = 'onResize', 31 | ON_TAB_ITEM_TAP = 'onTabItemTap', 32 | ON_ADD_TO_FAVORITES = 'onAddToFavorites', 33 | ON_SHARE_TIME_LINE = 'onShareTimeline' 34 | } 35 | 36 | export const enum CommonLifecycle { 37 | ON_LOAD = 'onLoad', 38 | ON_UN_LOAD = 'onUnload', 39 | ON_READY = 'onReady' 40 | } 41 | 42 | export const enum ExtendLefecycle { 43 | /** 副作用 */ 44 | EFFECT = 'effect', 45 | /** props代理 */ 46 | WATCH_PROPERTY = '__watchProperty__', 47 | /** 父实例 */ 48 | PARENT = '__parent__', 49 | /** 依赖实例容器 */ 50 | LOC_INJECT = '__loc_inject__' 51 | } 52 | 53 | /**注入hooks */ 54 | export function injectHook ( 55 | currentInstance: ICurrentModuleInstance, 56 | lifecycle: PageLifecycle | ComponentLifecycle | ExtendLefecycle | CommonLifecycle, 57 | hook: Function 58 | ){ 59 | const hiddenField = createShortName(lifecycle) 60 | if (currentInstance[hiddenField] === undefined) { 61 | currentInstance[hiddenField] = [] 62 | } 63 | 64 | currentInstance[hiddenField].push(hook) 65 | } 66 | 67 | /**执行hooks */ 68 | export function conductHook ( 69 | currentInstance: ICurrentModuleInstance, 70 | lifecycle: PageLifecycle | ComponentLifecycle | ExtendLefecycle | CommonLifecycle, 71 | params: any[] 72 | ){ 73 | const hiddenField = createShortName(lifecycle) 74 | const injectLifes: Function[] = currentInstance[hiddenField] || [] 75 | 76 | return injectLifes.map( 77 | (life) => typeof life === 'function' && life.apply(currentInstance, params) 78 | ) 79 | } 80 | 81 | function createCurrentModuleHook ( 82 | lifecycle: ComponentLifecycle | PageLifecycle | CommonLifecycle 83 | ){ 84 | return function (callback: T){ 85 | overInCurrentModule((currentInstance) => { 86 | currentInstance && injectHook(currentInstance, lifecycle, callback) 87 | }) 88 | } 89 | } 90 | 91 | /** 实例被加载, Page.onLoad, Components.attached, App.onLaunch */ 92 | export const onLoad = createCurrentModuleHook(CommonLifecycle.ON_LOAD) 93 | 94 | /** 实例装载完成, Page.onReady, Components.ready, App.onLaunch */ 95 | export const onReady = createCurrentModuleHook(CommonLifecycle.ON_READY) 96 | 97 | /** 实例被销毁, Page.onUnLoad, Components.destory */ 98 | export const onUnLoad = createCurrentModuleHook(CommonLifecycle.ON_UN_LOAD) 99 | 100 | /** 页面显示, Page.onShow, App.onShow */ 101 | export const onShow = createCurrentModuleHook(PageLifecycle.ON_SHOW) 102 | 103 | /** 页面隐藏 Page.onHide, App.onHide */ 104 | export const onHide = createCurrentModuleHook(PageLifecycle.ON_HIDE) 105 | 106 | /** 下拉刷新 */ 107 | export const onPullDownRefresh = createCurrentModuleHook(PageLifecycle.ON_PULL_DOWN_REFRESH) 108 | 109 | /** 滚动到底部 */ 110 | export const onReachBottom = createCurrentModuleHook(PageLifecycle.ON_REACH_BOTTOM) 111 | 112 | /** 页面滚动 */ 113 | export const onPageScroll = createCurrentModuleHook(PageLifecycle.ON_PAGE_SCROLL) 114 | 115 | /** 小程序屏幕旋转时触发 */ 116 | export const onResize = createCurrentModuleHook(PageLifecycle.ON_RESIZE) 117 | 118 | /** tab页面点击时触发 */ 119 | export const onTabItemTap = createCurrentModuleHook< 120 | ( 121 | e: { 122 | index: string 123 | pagePath: string 124 | text: string 125 | } 126 | ) => any 127 | >(PageLifecycle.ON_TAB_ITEM_TAP) 128 | 129 | /** 用户点击右上角收藏 */ 130 | export const onAddToFavorites = createCurrentModuleHook< 131 | ( 132 | e: { webviewUrl?: string } 133 | ) => { 134 | title?: string 135 | imageUrl?: string 136 | query?: string 137 | } 138 | >(PageLifecycle.ON_ADD_TO_FAVORITES) 139 | 140 | /** 转发 */ 141 | export const onShareAppMessage = createCurrentModuleHook< 142 | ( 143 | e: { from: 'button' | 'menu'; target: any; webViewUrl?: string } 144 | ) => { 145 | title?: string 146 | path?: string 147 | imageUrl?: string 148 | } 149 | >(PageLifecycle.ON_SHARE_APP_MESSAGE) 150 | 151 | /** 用户点击右上角转发到朋友圈 */ 152 | export const onShareTimeline = createCurrentModuleHook< 153 | () => { 154 | title?: string 155 | query?: string 156 | imageUrl?: string 157 | } 158 | >(PageLifecycle.ON_SHARE_TIME_LINE) 159 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/diff.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var ARRAYTYPE = '[object Array]'; 4 | var OBJECTTYPE = '[object Object]'; 5 | var FUNCTIONTYPE = '[object Function]'; 6 | function diff(current, pre) { 7 | var result = {}; 8 | syncKeys(current, pre); 9 | _diff(current, pre, '', result); 10 | return result; 11 | } 12 | exports.diff = diff; 13 | function syncKeys(current, pre) { 14 | if (current === pre) 15 | return; 16 | var rootCurrentType = type(current); 17 | var rootPreType = type(pre); 18 | if (rootCurrentType == OBJECTTYPE && rootPreType == OBJECTTYPE) { 19 | //if(Object.keys(current).length >= Object.keys(pre).length){ 20 | for (var key in pre) { 21 | var currentValue = current[key]; 22 | if (currentValue === undefined) { 23 | current[key] = null; 24 | } 25 | else { 26 | syncKeys(currentValue, pre[key]); 27 | } 28 | } 29 | //} 30 | } 31 | else if (rootCurrentType == ARRAYTYPE && rootPreType == ARRAYTYPE) { 32 | if (current.length >= pre.length) { 33 | pre.forEach(function (item, index) { 34 | syncKeys(current[index], item); 35 | }); 36 | } 37 | } 38 | } 39 | function _diff(current, pre, path, result) { 40 | if (current === pre) 41 | return; 42 | var rootCurrentType = type(current); 43 | var rootPreType = type(pre); 44 | if (rootCurrentType == OBJECTTYPE) { 45 | if (rootPreType != OBJECTTYPE || 46 | Object.keys(current).length < Object.keys(pre).length) { 47 | setResult(result, path, current); 48 | } 49 | else { 50 | var _loop_1 = function (key) { 51 | var currentValue = current[key]; 52 | var preValue = pre[key]; 53 | var currentType = type(currentValue); 54 | var preType = type(preValue); 55 | if (currentType != ARRAYTYPE && currentType != OBJECTTYPE) { 56 | if (currentValue != pre[key]) { 57 | setResult(result, (path == '' ? '' : path + '.') + key, currentValue); 58 | } 59 | } 60 | else if (currentType == ARRAYTYPE) { 61 | if (preType != ARRAYTYPE) { 62 | setResult(result, (path == '' ? '' : path + '.') + key, currentValue); 63 | } 64 | else { 65 | if (currentValue.length < preValue.length) { 66 | setResult(result, (path == '' ? '' : path + '.') + key, currentValue); 67 | } 68 | else { 69 | currentValue.forEach(function (item, index) { 70 | _diff(item, preValue[index], (path == '' ? '' : path + '.') + 71 | key + 72 | '[' + 73 | index + 74 | ']', result); 75 | }); 76 | } 77 | } 78 | } 79 | else if (currentType == OBJECTTYPE) { 80 | if (preType != OBJECTTYPE || 81 | Object.keys(currentValue).length < Object.keys(preValue).length) { 82 | setResult(result, (path == '' ? '' : path + '.') + key, currentValue); 83 | } 84 | else { 85 | for (var subKey in currentValue) { 86 | _diff(currentValue[subKey], preValue[subKey], (path == '' ? '' : path + '.') + key + '.' + subKey, result); 87 | } 88 | } 89 | } 90 | }; 91 | for (var key in current) { 92 | _loop_1(key); 93 | } 94 | } 95 | } 96 | else if (rootCurrentType == ARRAYTYPE) { 97 | if (rootPreType != ARRAYTYPE) { 98 | setResult(result, path, current); 99 | } 100 | else { 101 | if (current.length < pre.length) { 102 | setResult(result, path, current); 103 | } 104 | else { 105 | current.forEach(function (item, index) { 106 | _diff(item, pre[index], path + '[' + index + ']', result); 107 | }); 108 | } 109 | } 110 | } 111 | else { 112 | setResult(result, path, current); 113 | } 114 | } 115 | function setResult(result, k, v) { 116 | if (type(v) != FUNCTIONTYPE) { 117 | result[k] = v; 118 | } 119 | } 120 | function type(obj) { 121 | return Object.prototype.toString.call(obj); 122 | } 123 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/shared.ts: -------------------------------------------------------------------------------- 1 | import { isRef, IRef, isObserve, useRef } from 'miniprogram-reactivity' 2 | import { isPlainObject, isArray, isFunction } from './utils' 3 | import { diff } from './diff' 4 | import { overCloneDeep } from './over' 5 | import { 6 | ComponentLifecycle, 7 | PageLifecycle, 8 | conductHook, 9 | CommonLifecycle, 10 | } from './lifecycle' 11 | import { ICurrentModuleInstance } from './instance' 12 | import { IContext } from './context' 13 | import { useEffect } from './watch' 14 | 15 | /** 副作用, 如果是方法,返回null */ 16 | export const deepToRaw = overCloneDeep(function (x: unknown) { 17 | if (isFunction(x)) { 18 | return null 19 | } 20 | if (isRef(x)) { 21 | return x.value 22 | } 23 | if (isArray(x)) { 24 | return x.map((item) => deepToRaw(item)) 25 | } 26 | if (isPlainObject(x)) { 27 | const obj: { [key: string]: unknown } = {} 28 | Object.keys(x).forEach((key) => { 29 | obj[key] = deepToRaw(x[key]) 30 | }) 31 | return obj 32 | } 33 | 34 | return x 35 | }) 36 | 37 | /** 38 | * Page/Component 与 watch 中转 39 | * @return {function} 抛弃监听 40 | */ 41 | export function deepWatch(target: any, key: string, value: any) { 42 | const deepEffects: IRef[] = [] 43 | ;(function observerEffects(x: any) { 44 | /** 45 | * isObserve必须是在最前面,因为会被isPlainObject解析 46 | */ 47 | if (isObserve(x)) { 48 | return void deepEffects.push(x) 49 | } 50 | if (isArray(x)) { 51 | return void x.map((item) => observerEffects(item)) 52 | } 53 | if (isPlainObject(x)) { 54 | return void Object.keys(x).forEach((key) => { 55 | observerEffects(x[key]) 56 | }) 57 | } 58 | })(value) 59 | 60 | if (!deepEffects.length) { 61 | return 62 | } 63 | 64 | return useEffect(() => { 65 | target.setData( 66 | diff( 67 | { 68 | [key]: deepToRaw(value), 69 | }, 70 | { 71 | [key]: target.data[key], 72 | } 73 | ) 74 | ) 75 | }, deepEffects) 76 | } 77 | 78 | /** 79 | * 80 | * 执行注册的声明周期 81 | * @param {string} lifecycle 需要执行的某个生命周期 82 | * @param {string} extendFunction 额外需要追加执行的方法 83 | * @return {function} 返回一个方法, 调用时, 执行该this下所有指定的生命周期 84 | */ 85 | export function createLifecycleMethods( 86 | lifecycle: ComponentLifecycle | PageLifecycle | CommonLifecycle, 87 | extendFunction?: Function | undefined 88 | ): (...args: any[]) => any[] { 89 | 90 | return function (this: ICurrentModuleInstance, ...args: any[]) { 91 | const injectLifes: any[] = conductHook(this, lifecycle, args) 92 | 93 | if (extendFunction) { 94 | injectLifes.push(extendFunction.call(this, args)) 95 | } 96 | 97 | return injectLifes 98 | } 99 | } 100 | 101 | 102 | /** 103 | * 104 | * 适配于需要一个返回值 105 | * @param lifecycle 106 | * @param extendFunction 107 | */ 108 | export function createSingleCallbackResultLifecycle ( 109 | lifecycle: PageLifecycle, 110 | extendFunction: Function | undefined 111 | ){ 112 | const lifecycleMethod = createLifecycleMethods(lifecycle, extendFunction) 113 | return function (...args: any){ 114 | const runResults = lifecycleMethod.apply(this, ...args) 115 | return runResults[runResults.length - 1] 116 | } 117 | } 118 | 119 | export type IBindings = { [key: string]: any } 120 | 121 | type IAnyObject = Record 122 | type PropertyType = 123 | | StringConstructor 124 | | NumberConstructor 125 | | BooleanConstructor 126 | | ArrayConstructor 127 | | ObjectConstructor 128 | | null 129 | type ValueType = T extends StringConstructor 130 | ? string 131 | : T extends NumberConstructor 132 | ? number 133 | : T extends BooleanConstructor 134 | ? boolean 135 | : T extends ArrayConstructor 136 | ? any[] 137 | : T extends ObjectConstructor 138 | ? IAnyObject 139 | : any 140 | type FullProperty = { 141 | /** 属性类型 */ 142 | type: T 143 | /** 默认值 */ 144 | value?: ValueType 145 | } 146 | export type AllFullProperty = 147 | | FullProperty 148 | | FullProperty 149 | | FullProperty 150 | | FullProperty 151 | | FullProperty 152 | | FullProperty 153 | type ShortProperty = 154 | | StringConstructor 155 | | NumberConstructor 156 | | BooleanConstructor 157 | | ArrayConstructor 158 | | ObjectConstructor 159 | | null 160 | 161 | export type AllProperty = AllFullProperty | ShortProperty 162 | 163 | interface IProps { 164 | [keyName: string]: AllProperty 165 | } 166 | 167 | type PropertyToData = T extends ShortProperty 168 | ? ValueType 169 | : T extends AllFullProperty 170 | ? ValueType 171 | : any 172 | 173 | type PropertyOptionToData

= { 174 | [name in keyof P]: IRef> 175 | } 176 | 177 | export type ISetup< 178 | P extends IProps, 179 | PROVIDE extends { 180 | [key: string]: () => any 181 | }, 182 | INJECT extends { 183 | [key: string]: () => any 184 | } 185 | > = ( 186 | this: ICurrentModuleInstance, 187 | props: PropertyOptionToData

, 188 | context: IContext & { 189 | provide: ParamsCallback 190 | inject: ParamsCallback 191 | } 192 | ) => IBindings 193 | 194 | type ParamsFunDI = () => any 195 | 196 | type ParamsCallback< 197 | P extends { 198 | [key: string]: ParamsFunDI 199 | } 200 | > = { 201 | [name in keyof P]: ReturnType 202 | } 203 | 204 | export function createDI< 205 | P extends { 206 | [key: string]: ParamsFunDI 207 | } 208 | >(params: P, next: (callback: any) => any): ParamsCallback

{ 209 | if (!params) { 210 | // @ts-ignore 211 | return {} 212 | } 213 | const result: any = {} 214 | Object.keys(params).forEach((KEY) => { 215 | result[KEY] = next(params[KEY]) 216 | }) 217 | 218 | return result 219 | } 220 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/lib/component.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __rest = (this && this.__rest) || function (s, e) { 3 | var t = {}; 4 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) 5 | t[p] = s[p]; 6 | if (s != null && typeof Object.getOwnPropertySymbols === "function") 7 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { 8 | if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) 9 | t[p[i]] = s[p[i]]; 10 | } 11 | return t; 12 | }; 13 | Object.defineProperty(exports, "__esModule", { value: true }); 14 | var lifecycle_1 = require("./lifecycle"); 15 | var utils_1 = require("./utils"); 16 | var instance_1 = require("./instance"); 17 | var context_1 = require("./context"); 18 | var shared_1 = require("./shared"); 19 | var inject_1 = require("./inject"); 20 | var miniprogram_reactivity_1 = require("miniprogram-reactivity"); 21 | function defineComponent(componentOptions) { 22 | var setupFun; 23 | var options; 24 | if (utils_1.isFunction(componentOptions)) { 25 | setupFun = componentOptions; 26 | options = {}; 27 | } 28 | else { 29 | componentOptions.properties = 30 | componentOptions.props || componentOptions.properties || {}; 31 | if (componentOptions.setup === void 0) { 32 | return Component(componentOptions); 33 | } 34 | var setupOption = componentOptions.setup, otherOptions = __rest(componentOptions, ["setup"]); 35 | setupFun = setupOption; 36 | options = otherOptions; 37 | } 38 | options.properties && 39 | Object.keys(options.properties).forEach(function (KEY) { 40 | var prop = options.properties[KEY]; 41 | var proxy_prop; 42 | if (typeof prop === 'function' || prop === null) { 43 | proxy_prop = { 44 | type: prop, 45 | value: null, 46 | }; 47 | } 48 | else { 49 | proxy_prop = prop; 50 | } 51 | proxy_prop.observer = function (newValue) { 52 | var sortName = "__watchProperty__" /* WATCH_PROPERTY */; 53 | this[sortName] && 54 | this[sortName][KEY] && 55 | this[sortName][KEY](newValue); 56 | }; 57 | options.properties[KEY] = proxy_prop; 58 | }); 59 | function createProxyProperty() { 60 | var _this = this; 61 | var proxy = {}; 62 | options.properties && 63 | Object.keys(options.properties).forEach(function (KEY) { 64 | proxy[KEY] = miniprogram_reactivity_1.useRef(_this.properties[KEY]); 65 | if (!_this["__watchProperty__" /* WATCH_PROPERTY */]) { 66 | _this["__watchProperty__" /* WATCH_PROPERTY */] = {}; 67 | } 68 | _this["__watchProperty__" /* WATCH_PROPERTY */][KEY] = function (value) { 69 | proxy[KEY].set(value); 70 | }; 71 | }); 72 | return proxy; 73 | } 74 | options.methods = options.methods || {}; 75 | /** 绑定上下文 */ 76 | options.methods['$'] = function (_a) { 77 | var detail = _a.detail; 78 | detail["__parent__" /* PARENT */] = this; 79 | }; 80 | /** 81 | * 82 | * TODO 下一个版本将props转化为ref对象,进行监听 83 | */ 84 | options["attached" /* ATTACHED */] = instance_1.overCurrentModule(utils_1.wrapFuns(function () { 85 | this.triggerEvent('component', this); 86 | }, function () { 87 | var context = context_1.createContext(this); 88 | var inject = shared_1.createDI(options.inject, inject_1.useInject); 89 | var provide = shared_1.createDI(options.provide, inject_1.useProvide); 90 | var props = createProxyProperty.call(this); 91 | var binds = setupFun.call(this, props, Object.assign(context, { inject: inject, provide: provide })); 92 | if (binds instanceof Promise) { 93 | return console.error("\n setup\u8FD4\u56DE\u503C\u4E0D\u652F\u6301promise\n "); 94 | } 95 | var calcKeys = Object.keys(binds).filter(function (val) { 96 | return Object.keys(props).indexOf(val) > -1; 97 | }); 98 | if (calcKeys.length) { 99 | console.error("\u6CE8\u610F!" + calcKeys + "\u5DF2\u5B58\u5728props\u4E2D,setup\u671F\u95F4\u8FD4\u56DE\u540C\u540D\u5C5E\u6027,\u5C06\u4F1A\u89E6\u53D1props\u503C\u6539\u53D8"); 100 | } 101 | context.setData(binds); 102 | }, shared_1.createLifecycleMethods("onLoad" /* ON_LOAD */, options["attached" /* ATTACHED */]))); 103 | options["ready" /* READY */] = shared_1.createLifecycleMethods("onReady" /* ON_READY */, options["ready" /* READY */]); 104 | options["detached" /* DETACHED */] = utils_1.wrapFuns(function () { 105 | lifecycle_1.conductHook(this, "effect" /* EFFECT */, []); 106 | }, shared_1.createLifecycleMethods("onUnload" /* ON_UN_LOAD */, options["detached" /* DETACHED */])); 107 | options.methods["onShow" /* ON_SHOW */] = shared_1.createLifecycleMethods("onShow" /* ON_SHOW */, options["onShow" /* ON_SHOW */]); 108 | options.methods["onHide" /* ON_HIDE */] = shared_1.createLifecycleMethods("onHide" /* ON_HIDE */, options["onHide" /* ON_HIDE */]); 109 | options.methods["onPullDownRefresh" /* ON_PULL_DOWN_REFRESH */] = shared_1.createLifecycleMethods("onPullDownRefresh" /* ON_PULL_DOWN_REFRESH */, options["onPullDownRefresh" /* ON_PULL_DOWN_REFRESH */]); 110 | options.methods["onReachBottom" /* ON_REACH_BOTTOM */] = shared_1.createLifecycleMethods("onReachBottom" /* ON_REACH_BOTTOM */, options["onReachBottom" /* ON_REACH_BOTTOM */]); 111 | options.methods["onPageScroll" /* ON_PAGE_SCROLL */] = shared_1.createLifecycleMethods("onPageScroll" /* ON_PAGE_SCROLL */, options["onPageScroll" /* ON_PAGE_SCROLL */]); 112 | options.methods["onShareAppMessage" /* ON_SHARE_APP_MESSAGE */] = (function () { 113 | var lifecycleMethod = shared_1.createLifecycleMethods("onShareAppMessage" /* ON_SHARE_APP_MESSAGE */, options["onShareAppMessage" /* ON_SHARE_APP_MESSAGE */]); 114 | return function () { 115 | var params = []; 116 | for (var _i = 0; _i < arguments.length; _i++) { 117 | params[_i] = arguments[_i]; 118 | } 119 | var runResults = lifecycleMethod.apply(this, params); 120 | return runResults[runResults.length - 1]; 121 | }; 122 | })(); 123 | return Component(options); 124 | } 125 | exports.defineComponent = defineComponent; 126 | -------------------------------------------------------------------------------- /packages/miniprogram-composition-api/src/component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentLifecycle, 3 | PageLifecycle, 4 | conductHook, 5 | ExtendLefecycle, 6 | CommonLifecycle 7 | } from './lifecycle' 8 | import { isFunction, wrapFuns } from './utils' 9 | import { ICurrentModuleInstance, overCurrentModule } from './instance' 10 | import { createContext } from './context' 11 | import { 12 | createLifecycleMethods, 13 | createSingleCallbackResultLifecycle, 14 | ISetup, 15 | AllProperty, 16 | createDI 17 | } from './shared' 18 | import { useInject, useProvide } from './inject' 19 | import { useRef, IRef } from 'miniprogram-reactivity' 20 | 21 | export function defineComponent< 22 | PROPS extends { 23 | [key: string]: AllProperty 24 | }, 25 | PROVIDE extends { 26 | [key: string]: () => any 27 | }, 28 | INJECT extends { 29 | [key: string]: () => any 30 | } 31 | > ( 32 | componentOptions: 33 | | { 34 | props?: PROPS 35 | /** 注册 */ 36 | provide?: PROVIDE 37 | /** 注入 */ 38 | inject?: INJECT 39 | /** 静态属性,可以被覆盖,初始化显示更快 */ 40 | data?: { 41 | [key: string]: any 42 | } 43 | setup?: ISetup 44 | [key: string]: any 45 | } 46 | | ISetup 47 | ): any{ 48 | let setupFun: Function 49 | let options: { 50 | methods?: { 51 | [key: string]: (...args: any[]) => any 52 | } 53 | properties?: { 54 | [key: string]: AllProperty 55 | } 56 | [key: string]: any 57 | } 58 | 59 | if (isFunction(componentOptions)) { 60 | setupFun = componentOptions 61 | options = {} 62 | } else { 63 | componentOptions.properties = 64 | componentOptions.props || componentOptions.properties || {} 65 | 66 | if (componentOptions.setup === void 0) { 67 | return Component(componentOptions) 68 | } 69 | 70 | const { setup: setupOption, ...otherOptions } = componentOptions 71 | setupFun = setupOption 72 | options = otherOptions 73 | } 74 | 75 | options.methods = options.methods || {} 76 | 77 | options.properties && 78 | Object.keys(options.properties).forEach((KEY) => { 79 | let prop = options.properties[KEY] 80 | let proxy_prop 81 | if (typeof prop === 'function' || prop === null) { 82 | proxy_prop = { 83 | type: prop, 84 | value: null 85 | } 86 | } else { 87 | proxy_prop = prop 88 | } 89 | 90 | proxy_prop.observer = function (this: ICurrentModuleInstance, newValue){ 91 | const sortName = ExtendLefecycle.WATCH_PROPERTY 92 | 93 | this[sortName] && this[sortName][KEY] && this[sortName][KEY](newValue) 94 | } 95 | 96 | options.properties[KEY] = proxy_prop 97 | }) 98 | 99 | function createProxyProperty (this: ICurrentModuleInstance){ 100 | const proxy: { 101 | [key: string]: IRef 102 | } = {} 103 | 104 | options.properties && 105 | Object.keys(options.properties).forEach((KEY) => { 106 | proxy[KEY] = useRef(this.properties[KEY]) 107 | 108 | if (!this[ExtendLefecycle.WATCH_PROPERTY]) { 109 | this[ExtendLefecycle.WATCH_PROPERTY] = {} 110 | } 111 | this[ExtendLefecycle.WATCH_PROPERTY][KEY] = function (value){ 112 | proxy[KEY].set(value) 113 | } 114 | }) 115 | 116 | return proxy 117 | } 118 | 119 | /** 子组件通知 */ 120 | options.methods['$'] = function ( 121 | this: ICurrentModuleInstance, 122 | { detail: child }: { detail: ICurrentModuleInstance } 123 | ){ 124 | child[ExtendLefecycle.PARENT] = this 125 | } 126 | 127 | /** 128 | * 129 | * TODO 下一个版本将props转化为ref对象,进行监听 130 | */ 131 | options[ComponentLifecycle.ATTACHED] = overCurrentModule( 132 | wrapFuns( 133 | function (this: ICurrentModuleInstance){ 134 | this.triggerEvent('component', this) 135 | }, 136 | function (this: ICurrentModuleInstance){ 137 | const context = createContext(this) 138 | const inject = createDI(options.inject, useInject) 139 | const provide = createDI(options.provide, useProvide) 140 | const props = createProxyProperty.call(this) 141 | const binds = setupFun.call( 142 | this, 143 | props, 144 | Object.assign(context, { inject, provide }) 145 | ) 146 | if (binds instanceof Promise) { 147 | return console.error(` 148 | setup返回值不支持promise 149 | `) 150 | } 151 | 152 | const calcKeys = Object.keys(binds).filter(function (val){ 153 | return Object.keys(props).indexOf(val) > -1 154 | }) 155 | 156 | if (calcKeys.length) { 157 | console.error(`注意!${calcKeys}已存在props中,setup期间返回同名属性,将会触发props值改变`) 158 | } 159 | 160 | context.setData(binds) 161 | }, 162 | createLifecycleMethods( 163 | CommonLifecycle.ON_LOAD, 164 | options[ComponentLifecycle.ATTACHED] 165 | ) 166 | ) 167 | ) 168 | 169 | options[ComponentLifecycle.READY] = createLifecycleMethods( 170 | CommonLifecycle.ON_READY, 171 | options[ComponentLifecycle.READY] 172 | ) 173 | 174 | options[ComponentLifecycle.DETACHED] = wrapFuns(function (){ 175 | conductHook(this, ExtendLefecycle.EFFECT, []) 176 | }, createLifecycleMethods(CommonLifecycle.ON_UN_LOAD, options[ComponentLifecycle.DETACHED])) 177 | 178 | options.methods[PageLifecycle.ON_SHOW] = createLifecycleMethods( 179 | PageLifecycle.ON_SHOW, 180 | options.methods[PageLifecycle.ON_SHOW] 181 | ) 182 | 183 | options.methods[PageLifecycle.ON_HIDE] = createLifecycleMethods( 184 | PageLifecycle.ON_HIDE, 185 | options.methods[PageLifecycle.ON_HIDE] 186 | ) 187 | 188 | options.methods[PageLifecycle.ON_RESIZE] = createLifecycleMethods( 189 | PageLifecycle.ON_RESIZE, 190 | options.methods[PageLifecycle.ON_RESIZE] 191 | ) 192 | 193 | options.methods[PageLifecycle.ON_TAB_ITEM_TAP] = createLifecycleMethods( 194 | PageLifecycle.ON_TAB_ITEM_TAP, 195 | options.methods[PageLifecycle.ON_TAB_ITEM_TAP] 196 | ) 197 | 198 | options.methods[PageLifecycle.ON_PULL_DOWN_REFRESH] = createLifecycleMethods( 199 | PageLifecycle.ON_PULL_DOWN_REFRESH, 200 | options.methods[PageLifecycle.ON_PULL_DOWN_REFRESH] 201 | ) 202 | 203 | options.methods[PageLifecycle.ON_REACH_BOTTOM] = createLifecycleMethods( 204 | PageLifecycle.ON_REACH_BOTTOM, 205 | options.methods[PageLifecycle.ON_REACH_BOTTOM] 206 | ) 207 | 208 | options.methods[PageLifecycle.ON_PAGE_SCROLL] = createLifecycleMethods( 209 | PageLifecycle.ON_PAGE_SCROLL, 210 | options.methods[PageLifecycle.ON_PAGE_SCROLL] 211 | ) 212 | 213 | options.methods[PageLifecycle.ON_ADD_TO_FAVORITES] = createSingleCallbackResultLifecycle( 214 | PageLifecycle.ON_ADD_TO_FAVORITES, 215 | options.methods[PageLifecycle.ON_ADD_TO_FAVORITES] 216 | ) 217 | 218 | options.methods[PageLifecycle.ON_SHARE_APP_MESSAGE] = createSingleCallbackResultLifecycle( 219 | PageLifecycle.ON_SHARE_APP_MESSAGE, 220 | options.methods[PageLifecycle.ON_SHARE_APP_MESSAGE] 221 | ) 222 | 223 | options.methods[PageLifecycle.ON_SHARE_TIME_LINE] = createSingleCallbackResultLifecycle( 224 | PageLifecycle.ON_SHARE_TIME_LINE, 225 | options.methods[PageLifecycle.ON_SHARE_TIME_LINE] 226 | ) 227 | 228 | return Component(options) 229 | } 230 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 尝试在小程序里使用 composition-api 2 | 3 | - [案例](./EXAMPLE.md) 4 | - [版本更新](./TODO.md) 5 | 6 | ### 下载 7 | 8 | > npm install miniprogram-composition-api --save 9 | > [仓库地址](https://github.com/clevok/miniprogram-composition-api) 10 | 11 | ### 使用前请考虑 12 | 13 | 这个框架只是参考vue composition api思路, 因为得考虑到 `IOS10` 以下的客户无法使用Proxy, 出于这个原因, 没有采用`@vue/reactivity`做响应式数据, 所以api是不一致的, 最后的如果不再考虑Proxy兼容性, 将不维护这个了 14 | 15 | 1. 它是过度产品, 是因为我想使用`function api`的方式写小程序, 但是可能会面临`兼容性问题`可能会被上司要求解决,除非可以让用户升级系统版本(曾经考虑过`Object.defineProperty`和脏检查) 16 | 17 | 2. 最终ref对象采用的是[mobx box方案](https://cn.mobx.js.org/refguide/boxed.html)方案, 意味着要通过`.set`读取值, `.get/.value`获取值, 有些人可能会很不喜欢, 因为`.set`方式 和 直接修改`.value`值的方式, 直觉上的相差太多, 并且常常会忘记需要 `.value` 来获取真正的值 18 | 19 | 3. 已经有真正使用`@vue/reactivity` 的[小程序框架 了](https://github.com/yangmingshan/vue-mini) 直接采用了[@vue/reactivity](https://github.com/vuejs/vue-next/tree/master/packages/reactivity) 做响应式数据, 不考虑proxy问题,可以考虑这个框架 20 | 21 | --- 22 | 23 | ### 必读 24 | 25 | 1. 比较别扭的是, 更新 useRef 对象的值必须通过 `.set`方法, `读取`必须是 `.value`或者`.get()` 26 | 27 | 2. 响应式数据, 会通过 `westore.diff` 转换到 视图层, 某些情况下, 视图层数据 会与逻辑层中不一致 28 | 1. 删除数组中的某一项, 其实会把 这个项目变成 null, 例如 [1,2,3] => [1,null,3] 29 | 2. 删除对象某个属性, 会把这个属性设置成 null 例如 { name: 'along' } => {name: null} 30 | 3. 之后会尝试优化 westore 的 diff, 如果设置成了空 数组 或者空对象, 就直接变成 全部复制, 以及提供 diff: false, 不经过他的 diff, 直接整个赋值 31 | 4. 将会建立自己一套 diff 树,目前更新颗粒度只有一层.. 32 | 3. `useRef`将改名为`useBox`,ref对象将和vue3保持一致 33 | 34 | ### 解决什么 35 | 36 | 1. 带来新的代码复用方案(比 mixins 更友好) 37 | 2. 带来另一个种全局状态管理思路 38 | 3. `计算属性computed`,`监听watch`也都有, 没有基础库限制要求(定义的时候需要用户主动传入依赖项) 39 | 4. 新的写法,不再是 {data:{},methods,生命周期方法}这类写法 40 | 41 | ## 使用入门 42 | 43 | ### 基础概念 44 | 45 | 最基础的思想: 通过 将`响应式对象` _注入_ 到 `视图层` 中, (`响应式对象`的值更改, 将同步更新视图层) 46 | (缺陷: 因为版本兼容性问题, 还是通过显式的 `.set` 方式更新 `响应式对象值`) 47 | 48 | 而不在是 页面定义 data, 通过 setData 更新页面值 49 | 50 | 将 `同一个响应式对象`注入到`多个页面`的中, 就实现了 多个页面的的数据保持一致(`状态管理`), (将这个响应式对象放在 app 中,每个页面拿过来注入到视图层中,就实现了`全局状态`) 51 | 52 | ### setup 入口 53 | 54 | setup 函数是一个新声明周期, 在 onLoad 期间调用, 它会起到将`返回值`*注入*到`视图层`中的作用 55 | 56 | `函数`也同样也可以`return`回去,便成了页面点击回调事件 57 | 58 | > 也就是只有需要在视图层上需要渲染的变量/方法,才需 return 回去, 否者将不会会渲染到视图中 59 | 60 | ```html 61 | 62 | 78 | ``` 79 | 80 | ### useRef 对象 81 | 82 | 这是一套响应式数据 83 | 通过`setup`可以将`响应式对象`*`注入`*到`视图层`中,只要这个`响应式对象`值变化了,那么注入到的视图层里的值都会变,(也就是说定义一个叫 cart 的 ref 对象,A 页面注入了 cart,B 页面也注入了 cart, 只要 cart 值变了, A 页面和 B 页面的对应的值都会变化) 84 | 85 | ```js 86 | // global.js 87 | export const cart = useRef({ name: "小兰" }) 88 | ``` 89 | 90 | ```html 91 | // A页面和B页面都这样写 92 | 93 | 105 | ``` 106 | 107 | A 页面和 B 页面注入了 同一个响应式对象, 只要 `cart`发送了变化, 所有页面上的 name 值都会变化!这就是响应式对象 108 | 109 | ### 包裹对象 useRef 110 | 111 | **创建 ref 对象** 112 | useRef 对象接受一个参数作为初始值, 通过 .value 获取值 113 | 114 | ```js 115 | const count = useRef(0) 116 | console.log(count.value) // 0 117 | ``` 118 | 119 | **如何更新 ref 对象的值** 120 | 必须通过`.set`更新 ref 的值,才能正常使用 121 | 122 | ```js 123 | const count = useRef(0) 124 | console.log(count.value) // 0 125 | 126 | count.set(1) 127 | 128 | console.log(count.value) // 1 129 | ``` 130 | 131 | .set 有两种用法 132 | 133 | 1. 接受一个一个非方法对象,将会直接改变这个 ref 的值 134 | 2. 接受一个方法, 将会调用这个方法并传入原来的值, 接受这个方法返回的值作为更改后的值 135 | 136 | ```js 137 | const count = useRef(0) 138 | 139 | count.set((value) => value + 1) 140 | count.set(value + 1) 141 | ``` 142 | 143 | 为了避免按引用类型对象不小心被刚刚坑,我们断绝了除 .set 方法外一切 可能更改 ref 内部值的途径 144 | 145 | ```js 146 | var a = { name: 123 } 147 | var b = useRef(a) 148 | var c = b.value 149 | var d = b.get() 150 | 151 | // 他们的关系分别是 152 | // a , b , [ c, d ], 只有 c和d 是指针指向相同的 153 | // a !== b !== (c === d) 154 | ``` 155 | 156 | 1. 在视图层中读取 157 | 158 | 更新值已经做了 diff, 两次赋同一值将不会触发改变 159 | 160 | 采用了[westore](https://github.com/Tencent/westore) 的 json diff,用于对比文件并提取需要更改的路径, 用于最小化 setData 161 | 162 | 当该值被`setup`返回, 将进入 data 值, 可在模板中被读取到, 会自动解套,无需在模板中额外书写`.value` 163 | 164 | ```html 165 | 168 | 181 | ``` 182 | 183 | ### 计算属性 184 | 185 | **`useComputed`** 186 | 返回一个 不可手动修改的 ref 对象。可以理解为没有 set 方法返回的 useRef 187 | 188 | ```js 189 | const count = useRef(1) 190 | const plusOne = useComputed(() => count.value + 1, [count]) 191 | 192 | console.log(plusOne.value) // 2 193 | ``` 194 | 195 | `参数` 196 | 197 | 1. `callback` 监听变化的回调, 返回任意值 198 | 2. `any[]` 这个框架没有做依赖收集, 需要用户主动传入所有的依赖, 当里面的依赖变化时, 会触发回调函数执行,计算 199 | 200 | 计算属性总是最少会执行一次,为了第一次赋值 201 | 202 | --- 203 | 204 | ### 监听 Ref 值更新 205 | 206 | **`useEffect`** 207 | 当被监听的 ref 对象变化时, 将触发, 返回值是个方法, 用于停止监听 208 | 209 | `参数` 210 | 211 | 1. `callback` 监听变化的回调 212 | 2. `any[]` 这个框架没有做依赖收集, 需要用户主动传入所有的依赖, 当里面的依赖变化时, 会触发回调函数执行 213 | 214 | ```js 215 | const count = useRef(1) 216 | const stopHandle = useEffect(() => { 217 | console.log("我发送了变化") 218 | stopHandle() 219 | }, [count]) 220 | 221 | count.set(2) 222 | ``` 223 | 224 | --- 225 | 226 | ### 声明周期函数 227 | 228 | 可以直接导入 `onXXX` 一族的函数来注册生命周期钩子: 229 | 特殊, Component 和 Page 都是 onLoad, onUnLoad, onReady 230 | 231 | ```js 232 | import { onLoad, onUnLoad onHide, onReady, onShow } from 'vue' 233 | 234 | const MyComponent = { 235 | setup() { 236 | onLoad(() => { 237 | console.log('onLoad!') 238 | }) 239 | onUnLoad(() => { 240 | console.log('onUnLoad!') 241 | }) 242 | onReady(() => { 243 | console.log('onReady!') 244 | }) 245 | onHide(() => { 246 | console.log('updated!') 247 | }) 248 | onShow(() => { 249 | console.log('unmounted!') 250 | }) 251 | }, 252 | } 253 | 254 | ``` 255 | 256 | ## 高级功能 257 | 258 | ### 依赖注入 259 | 260 | 依赖注入参考了 angular, 这一点和 vue 的 inject,provied 有所区别一样 261 | 依赖注入除了逻辑复用外,还实现了组件树上共享数据,不再需要疯狂传递 props,疯狂 targetEvent 事件 262 | 263 | 这里直接上代码体现它的用处 264 | 265 | #### 需求 266 | 267 | 有个店铺消息模块,可以更新店铺名字, 268 | 有一个页面,上面需要展示店铺名字,他还有很多页面级别组件,需要修改姓名,也需要显示店铺姓名,传统主页面通过 props 传下去也可以,修改姓名的话,再通过事件传上来,调用页面上修改店铺姓名的方法。如果层级多就很麻烦,而且还没有 ts 提示 269 | 270 | #### 改用依赖注入 271 | 272 | 可以先创建公共的模块 273 | 274 | ```js 275 | function useShopInfo() { 276 | const shopInfo = useRef({ name: '店铺名字' }); 277 | const updateShopName = (name: string) => { 278 | shopInfo.set(value => { ...value, name }) 279 | } 280 | 281 | return { 282 | shopInfo, 283 | updateShopName 284 | } 285 | } 286 | 287 | ``` 288 | 289 | 页面 290 | 291 | ```js 292 | import { useShopInfo } from "shopServices" 293 | definePage({ 294 | provide: { useShopInfo }, 295 | setup(props, { provide }) { 296 | const { useShopInfo } = provide 297 | return { 298 | shopInfo: useShopInfo.shopInfo, 299 | } 300 | }, 301 | }) 302 | ``` 303 | 304 | 组件 305 | 306 | ```js 307 | 308 | import { useShopInfo } from 'shopServices'; 309 | defineComponent({ 310 | inject: {useShopInfo}, 311 | setup(props, { inject } ) { 312 | const { useShopInfo } = inject 313 | 314 | return { 315 | shopInfo: useShopInfo.shopInfo 316 | onChangeName() { 317 | useShopInfo.updateShopName('along') 318 | } 319 | } 320 | } 321 | }) 322 | ``` 323 | 324 | 以上代码页面和组件将共用一个数据 325 | 因为 inject 将会往其父级寻找已经`实例`的该函数,如果组件数上没有找到,那么将会往 app 上寻找,如果还没有,那么自身将会`主动实例化`,最后一点和 vue 不一样,因为 vue 你还得考虑父级没有的情况. 326 | 327 | 注意了,找上级需要建立组件树, 因为小程序目前的 api 问题,需要开发者主动通过 在组件上写 `bind:component="$"` 建立上下关系, 因为目前小程序 api 问题 328 | 329 | 除了 `inject`, `provide` 方式,也可以使用 `useInject`, `useProvide` 来手动注入一些需要参数的`函数api`, 注意咯, 不同的函数会实现不同的注入 330 | 331 | ```js 332 |