├── CHANGELOG.md ├── .npmignore ├── .gitignore ├── uni_modules ├── tob-use │ ├── watch │ │ ├── watchWithFilter │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── watchOnce │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── whenever │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── debouncedWatch │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── throttledWatch │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── pausableWatch │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── watchAtMost │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── ignorableWatch │ │ │ ├── README.md │ │ │ └── index.js │ │ └── until │ │ │ └── index.js │ ├── utilities │ │ ├── createReactiveFn │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── throttledRef │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── debouncedRef │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── isDefined │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── not │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── or │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── and │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── get │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── reactivePick │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── createUnrefFn │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── eagerComputed │ │ │ └── index.js │ │ ├── refDefault │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── reactify │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── set │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useDebounceFn │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useLastChanged │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useThrottleFn │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── toReactive │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── useClamp │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── autoResetRef │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── biSyncRef │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── syncRef │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── createEventHook │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useDebounce │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── makeDestructurable │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── useCounter │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useThrottle │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── toRefs │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── useToggle │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useEventBus │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── controlledComputed │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── extendRef │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── reactifyObject │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useCycleList │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── useConfirmDialog │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useMemoize │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useAsyncQueue │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── useOffsetPagination │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── controlledRef │ │ │ ├── index.js │ │ │ └── README.md │ │ └── asyncComputed │ │ │ └── index.js │ ├── component │ │ ├── tryOnUnmounted │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── tryOnBeforeUnmount │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useMounted │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── tryOnScopeDispose │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useVModels │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── tryOnMounted │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useTemplateRefsList │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── computedInject │ │ │ ├── index.js │ │ │ └── README.md │ │ └── useVModel │ │ │ ├── index.js │ │ │ └── README.md │ ├── state │ │ └── useStorage │ │ │ ├── README.md │ │ │ ├── guess.js │ │ │ └── index.js │ ├── vite.config.js │ ├── changelog.md │ ├── shared │ │ ├── create.js │ │ ├── base.js │ │ └── is.js │ ├── animation │ │ ├── useTimeoutFn │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── useTimeout │ │ │ ├── index.js │ │ │ └── README.md │ │ ├── useInterval │ │ │ ├── index.js │ │ │ └── README.md │ │ └── useIntervalFn │ │ │ ├── README.md │ │ │ └── index.js │ ├── readme.md │ ├── package.json │ └── media │ │ ├── createAudio │ │ └── README.md │ │ └── useAudio │ │ ├── index.js │ │ └── README.md └── tob-less │ ├── changelog.md │ ├── readme.md │ ├── index.js │ └── package.json ├── tea.yaml ├── docs ├── .vuepress │ ├── public │ │ └── images │ │ │ ├── install.png │ │ │ └── back-to-top.svg │ ├── clientAppEnhance.ts │ ├── types │ │ ├── env.d.ts │ │ └── components.d.ts │ ├── components │ │ ├── useInterval.vue │ │ ├── eagerComputed.vue │ │ ├── eagerComputed2.vue │ │ ├── useInterval2.vue │ │ └── eagerComputed3.vue │ └── theme │ │ ├── shared.ts │ │ ├── layouts │ │ └── 404.vue │ │ └── index.ts ├── api │ ├── utilities │ │ ├── not.md │ │ ├── or.md │ │ ├── debouncedRef.md │ │ ├── createReactiveFn.md │ │ ├── throttledRef.md │ │ ├── isDefined.md │ │ ├── reactivePick.md │ │ ├── refDefault.md │ │ ├── createUnrefFn.md │ │ ├── toReactive.md │ │ ├── get.md │ │ ├── autoResetRef.md │ │ ├── set.md │ │ ├── useClamp.md │ │ ├── biSyncRef.md │ │ ├── makeDestructurable.md │ │ ├── and.md │ │ ├── toRefs.md │ │ ├── useEventBus.md │ │ ├── useToggle.md │ │ ├── controlledComputed.md │ │ ├── extendRef.md │ │ ├── useDebounceFn.md │ │ ├── useDebounce.md │ │ ├── useThrottle.md │ │ ├── useCycleList.md │ │ ├── useThrottleFn.md │ │ ├── useLastChanged.md │ │ ├── reactifyObject.md │ │ ├── useCounter.md │ │ ├── reactify.md │ │ ├── useAsyncQueue.md │ │ ├── useOffsetPagination.md │ │ ├── syncRef.md │ │ ├── createEventHook.md │ │ ├── useConfirmDialog.md │ │ ├── useMemoize.md │ │ └── controlledRef.md │ ├── component │ │ ├── useMounted.md │ │ ├── useTemplateRefsList.md │ │ ├── tryOnUnmounted.md │ │ ├── tryOnBeforeUnmount.md │ │ ├── tryOnMounted.md │ │ ├── tryOnScopeDispose.md │ │ ├── useVModels.md │ │ ├── computedInject.md │ │ └── useVModel.md │ ├── state │ │ └── useStorage.md │ ├── animation │ │ ├── useTimeoutFn.md │ │ ├── useTimeout.md │ │ ├── useIntervalFn.md │ │ └── useInterval.md │ ├── watch │ │ ├── watchOnce.md │ │ ├── whenever.md │ │ ├── watchAtMost.md │ │ ├── watchWithFilter.md │ │ ├── throttledWatch.md │ │ ├── debouncedWatch.md │ │ ├── pausableWatch.md │ │ └── ignorableWatch.md │ └── media │ │ ├── createAudio.md │ │ └── useAudio.md ├── index.md ├── guide │ ├── start.md │ └── index.md └── about │ └── index.md ├── scripts ├── templates │ ├── api.js │ ├── api.md │ └── page.vue ├── shared │ ├── modules.js │ ├── utils.js │ ├── log.js │ └── paths.js └── remove.js ├── pages.json ├── App.vue ├── main.js ├── pages └── index │ └── index.vue ├── index.html ├── .hbuilderx └── launch.json ├── LICENSE ├── README.md ├── package.json └── uni.scss /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | dist 2 | unpackage 3 | .hbuilderx 4 | node_modules -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .cache 3 | .temp 4 | unpackage 5 | node_modules 6 | .idea 7 | 8 | -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/watchWithFilter/index.js: -------------------------------------------------------------------------------- 1 | export { watchWithFilter } from '../../shared/filters' 2 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1.0.0 3 | codeOwners: 4 | - "0x1D22736578C632DB6275d6478f251e5871a669D1" 5 | quorum: 1 6 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/createReactiveFn/index.js: -------------------------------------------------------------------------------- 1 | export { reactify as createReactiveFn } from '../reactify' 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/images/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dishait/tob-use/HEAD/docs/.vuepress/public/images/install.png -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/throttledRef/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 节流的 ref,useThrottle 的别名 3 | */ 4 | export { useThrottle as throttledRef } from '../useThrottle' 5 | -------------------------------------------------------------------------------- /scripts/templates/api.js: -------------------------------------------------------------------------------- 1 | import { ref, computed, watch } from 'vue' 2 | 3 | /** 4 | * {{desc}} 5 | */ 6 | export const {{name}} = () => { 7 | return 8 | } 9 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/debouncedRef/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 防抖的 ref, useDebounce 的别名 3 | */ 4 | export { useDebounce as debouncedRef } from '../useDebounce' 5 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/isDefined/index.js: -------------------------------------------------------------------------------- 1 | import { unref } from 'vue' 2 | 3 | /** 4 | * 是否定义判断 5 | */ 6 | export const isDefined = v => unref(v) != null 7 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/not/index.js: -------------------------------------------------------------------------------- 1 | import { computed, unref } from 'vue' 2 | 3 | /** 4 | * 取非 5 | */ 6 | export const not = v => computed(() => !unref(v)) 7 | -------------------------------------------------------------------------------- /uni_modules/tob-less/changelog.md: -------------------------------------------------------------------------------- 1 | ## 1.0.1(2021-12-29) 2 | 无 3 | # changelog 4 | 5 | ## 1.0.0(2021-12-29) 6 | 1. 新特性 7 | - 原子的 8 | - mixins 9 | - 动态主题 10 | - 自定义主题 11 | -------------------------------------------------------------------------------- /scripts/shared/modules.js: -------------------------------------------------------------------------------- 1 | const modules = { 2 | api: 'api', 3 | page: '页面' 4 | } 5 | 6 | const getModulesName = t => modules[t] 7 | 8 | module.exports = { 9 | getModulesName 10 | } 11 | -------------------------------------------------------------------------------- /pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | { 4 | "path": "pages/index/index" 5 | } 6 | ], 7 | "globalStyle": { 8 | "navigationStyle": "custom", 9 | "navigationBarTextStyle": "black" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/templates/api.md: -------------------------------------------------------------------------------- 1 | # {{name}} 2 | 3 | {{desc}} 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { {{name}} } from '@/uni_modules/tob-use' 10 | 11 | 12 | ``` 13 | 14 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/or/index.js: -------------------------------------------------------------------------------- 1 | import { unref, computed } from 'vue' 2 | 3 | /** 4 | * 或运算 5 | */ 6 | export const or = (...args) => { 7 | return computed(() => args.some(v => unref(v))) 8 | } 9 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/and/index.js: -------------------------------------------------------------------------------- 1 | import { unref, computed } from 'vue' 2 | 3 | /** 4 | * AND 判断 5 | */ 6 | export const and = (...args) => { 7 | return computed(() => args.every(v => unref(v))) 8 | } 9 | -------------------------------------------------------------------------------- /docs/.vuepress/clientAppEnhance.ts: -------------------------------------------------------------------------------- 1 | import { defineClientAppEnhance } from '@vuepress/client' 2 | 3 | // 你自定义的 css 4 | import './index.scss' 5 | 6 | const noop = () => {} 7 | 8 | export default defineClientAppEnhance(noop) 9 | -------------------------------------------------------------------------------- /scripts/templates/page.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/.vuepress/types/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | const component: DefineComponent<{}, {}, any> 6 | export default component 7 | } 8 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/get/index.js: -------------------------------------------------------------------------------- 1 | import { unref } from 'vue' 2 | 3 | /** 4 | * 获取 5 | */ 6 | export const get = (obj, key) => { 7 | if (key == null) { 8 | return unref(obj) 9 | } 10 | return unref(obj)[key] 11 | } 12 | -------------------------------------------------------------------------------- /docs/api/utilities/not.md: -------------------------------------------------------------------------------- 1 | # not 2 | 3 | 取非 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { not } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(true) 12 | 13 | console.log(not(a)) // false 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/tryOnUnmounted/index.js: -------------------------------------------------------------------------------- 1 | import { getCurrentInstance, onUnmounted } from 'vue' 2 | 3 | /** 4 | * 卸载时触发 5 | */ 6 | export const tryOnUnmounted = fn => { 7 | if (getCurrentInstance()) { 8 | onUnmounted(fn) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/not/README.md: -------------------------------------------------------------------------------- 1 | # not 2 | 3 | 取非 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { not } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(true) 12 | 13 | console.log(not(a)) // false 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /docs/api/utilities/or.md: -------------------------------------------------------------------------------- 1 | # or 2 | 3 | 或运算 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { or } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(true) 12 | const b = ref(false) 13 | 14 | console.log(or(a, b)) // true 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/tryOnBeforeUnmount/index.js: -------------------------------------------------------------------------------- 1 | import { getCurrentInstance, onBeforeUnmount } from 'vue' 2 | 3 | /** 4 | * 在卸载前触发 5 | */ 6 | export const tryOnBeforeUnmount = fn => { 7 | if (getCurrentInstance()) { 8 | onBeforeUnmount(fn) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/reactivePick/index.js: -------------------------------------------------------------------------------- 1 | import { toRef, reactive } from 'vue' 2 | 3 | /** 4 | * 响应式的pick 5 | */ 6 | export const reactivePick = (obj, ...keys) => { 7 | return reactive( 8 | Object.fromEntries(keys.map(k => [k, toRef(obj, k)])) 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /docs/api/utilities/debouncedRef.md: -------------------------------------------------------------------------------- 1 | # debouncedRef 2 | 3 | 防抖的 ref 4 | 5 | ## Usage 6 | 7 | [useDebounce](/api/utilities/useDebounce) 的别名,具体用法可见 👉 [useDebounce](/api/utilities/useDebounce)。 8 | 9 | ```js 10 | import { debouncedRef } from '@/uni_modules/tob-use' 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/or/README.md: -------------------------------------------------------------------------------- 1 | # or 2 | 3 | 或运算 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { or } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(true) 12 | const b = ref(false) 13 | 14 | console.log(or(a, b)) // true 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /docs/api/utilities/createReactiveFn.md: -------------------------------------------------------------------------------- 1 | # createReactiveFn 2 | 3 | 将普通函数转换为响应式函数 4 | 5 | ## Usage 6 | 7 | [reactify](/api/utilities/reactify) 的别名,具体用法可见 👉 [reactify](/api/utilities/reactify)。 8 | 9 | ```ts 10 | import { createReactiveFn } from '@/uni_modules/tob-use' 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /docs/api/utilities/throttledRef.md: -------------------------------------------------------------------------------- 1 | # throttledRef 2 | 3 | 节流的 ref 4 | 5 | ## Usage 6 | 7 | [useThrottle](/api/utilities/useThrottle) 的别名,具体用法可见 👉 [useThrottle](/api/utilities/useThrottle)。 8 | 9 | ```js 10 | import { throttledRef } from '@/uni_modules/tob-use' 11 | ``` 12 | 13 | 14 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/useMounted/index.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | /** 4 | * 使用被挂载状态 5 | */ 6 | export const useMounted = () => { 7 | const isMounted = ref(false) 8 | 9 | onMounted(() => { 10 | isMounted.value = true 11 | }) 12 | 13 | return isMounted 14 | } 15 | -------------------------------------------------------------------------------- /docs/.vuepress/types/components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/vue-next/pull/3399 4 | 5 | declare module 'vue' { 6 | export interface GlobalComponents {} 7 | } 8 | 9 | export {} 10 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/debouncedRef/README.md: -------------------------------------------------------------------------------- 1 | # debouncedRef 2 | 3 | 防抖的 ref 4 | 5 | ## Usage 6 | 7 | [useDebounce](/api/utilities/useDebounce) 的别名,具体用法可见 👉 [useDebounce](/api/utilities/useDebounce)。 8 | 9 | ```js 10 | import { debouncedRef } from '@/uni_modules/tob-use' 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/createReactiveFn/README.md: -------------------------------------------------------------------------------- 1 | # createReactiveFn 2 | 3 | 将普通函数转换为响应式函数 4 | 5 | ## Usage 6 | 7 | [reactify](/api/utilities/reactify) 的别名,具体用法可见 👉 [reactify](/api/utilities/reactify)。 8 | 9 | ```ts 10 | import { createReactiveFn } from '@/uni_modules/tob-use' 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/throttledRef/README.md: -------------------------------------------------------------------------------- 1 | # throttledRef 2 | 3 | 节流的 ref 4 | 5 | ## Usage 6 | 7 | [useThrottle](/api/utilities/useThrottle) 的别名,具体用法可见 👉 [useThrottle](/api/utilities/useThrottle)。 8 | 9 | ```js 10 | import { throttledRef } from '@/uni_modules/tob-use' 11 | ``` 12 | 13 | 14 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/createUnrefFn/index.js: -------------------------------------------------------------------------------- 1 | import { unref } from 'vue' 2 | 3 | /** 4 | * 创建解 ref 函数 5 | */ 6 | export const createUnrefFn = fn => { 7 | return function (...args) { 8 | return fn.apply( 9 | this, 10 | // 解除所有 ref 的参数 11 | args.map(v => unref(v)) 12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /scripts/shared/utils.js: -------------------------------------------------------------------------------- 1 | // 空函数 2 | const noop = () => {} 3 | 4 | // 创建一个防抖函数 5 | const useDebounce = (fn = noop, delay = 1000) => { 6 | let timeout = null 7 | return () => { 8 | clearTimeout(timeout) 9 | timeout = setTimeout(fn, delay) 10 | } 11 | } 12 | 13 | module.exports = { 14 | useDebounce 15 | } 16 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/tryOnScopeDispose/index.js: -------------------------------------------------------------------------------- 1 | import { getCurrentScope, onScopeDispose } from 'vue' 2 | 3 | /** 4 | * 在副作用作用域被 stop 时,触发回调 5 | */ 6 | export const tryOnScopeDispose = fn => { 7 | if (getCurrentScope()) { 8 | onScopeDispose(fn) 9 | return true 10 | } 11 | return false 12 | } 13 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/useVModels/index.js: -------------------------------------------------------------------------------- 1 | import { useVModel } from '../useVModel' 2 | 3 | /** 4 | * 使用多个的 v-model 5 | */ 6 | export const useVModels = (props, emit, options = {}) => { 7 | const ret = {} 8 | for (const key in props) { 9 | ret[key] = useVModel(props, key, emit, options) 10 | } 11 | return ret 12 | } 13 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/eagerComputed/index.js: -------------------------------------------------------------------------------- 1 | import { shallowRef, watchSyncEffect, readonly } from 'vue' 2 | 3 | /** 4 | * 及时的计算属性 5 | */ 6 | export const eagerComputed = fn => { 7 | const result = shallowRef() 8 | 9 | watchSyncEffect(() => { 10 | result.value = fn() 11 | }) 12 | 13 | return readonly(result) 14 | } 15 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/refDefault/index.js: -------------------------------------------------------------------------------- 1 | import { computed } from 'vue' 2 | 3 | /** 4 | * ref 的默认值 5 | */ 6 | export const refDefault = (source, defaultValue) => { 7 | return computed({ 8 | get() { 9 | return source.value ?? defaultValue 10 | }, 11 | set(value) { 12 | source.value = value 13 | } 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/reactify/index.js: -------------------------------------------------------------------------------- 1 | import { computed, unref } from 'vue' 2 | 3 | /** 4 | * 将普通函数转换为响应式函数 5 | */ 6 | export const reactify = fn => { 7 | return function (...args) { 8 | return computed(() => 9 | fn.apply( 10 | this, 11 | // 解除所有 ref 的参数 12 | args.map(v => unref(v)) 13 | ) 14 | ) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/set/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 设置 3 | */ 4 | export const set = (...args) => { 5 | // 双参数时,直接赋值 6 | if (args.length === 2) { 7 | const [ref, value] = args 8 | ref.value = value 9 | } 10 | 11 | // 三参数时,赋值到某个键 12 | if (args.length === 3) { 13 | const [target, key, value] = args 14 | target[key] = value 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/watchOnce/index.js: -------------------------------------------------------------------------------- 1 | import { nextTick, watch } from 'vue' 2 | 3 | /** 4 | * 一次性监听 5 | */ 6 | export const watchOnce = (source, cb, options = {}) => { 7 | const stop = watch( 8 | source, 9 | (...args) => { 10 | // 触发后直接 stop 掉监听 11 | nextTick(stop) 12 | return cb(...args) 13 | }, 14 | options 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/whenever/index.js: -------------------------------------------------------------------------------- 1 | import { watch } from 'vue' 2 | 3 | /** 4 | * 仅当为真时触发 5 | */ 6 | export const whenever = (source, cb, options) => { 7 | return watch( 8 | source, 9 | (v, ov, onInvalidate) => { 10 | // 仅当为 truthy 时触发回调 11 | if (v) { 12 | cb(v, ov, onInvalidate) 13 | } 14 | }, 15 | options 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /docs/api/utilities/isDefined.md: -------------------------------------------------------------------------------- 1 | # isDefined 2 | 3 | 是否定义判断 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { isDefined } from '@/uni_modules/tob-use' 10 | 11 | const example = ref(undefined) 12 | 13 | if (isDefined(example)) { 14 | console.log("定义了") 15 | } else { 16 | console.log("未定义") // 将输出 '未定义' 17 | } 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useDebounceFn/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | debounceFilter, 3 | createFilterWrapper 4 | } from '../../shared/filters' 5 | 6 | /** 7 | * 使用防抖函数 8 | */ 9 | export const useDebounceFn = ( 10 | fn, 11 | ms = 200, 12 | options = {} 13 | ) => { 14 | return createFilterWrapper( 15 | debounceFilter(ms, options), 16 | fn 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/tryOnMounted/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | getCurrentInstance, 3 | nextTick, 4 | onMounted 5 | } from 'vue' 6 | 7 | /** 8 | * 在挂载后触发 9 | */ 10 | export const tryOnMounted = (fn, sync = true) => { 11 | if (getCurrentInstance()) { 12 | onMounted(fn) 13 | } else if (sync) { 14 | fn() 15 | } else { 16 | nextTick(fn) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/isDefined/README.md: -------------------------------------------------------------------------------- 1 | # isDefined 2 | 3 | 是否定义判断 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { isDefined } from '@/uni_modules/tob-use' 10 | 11 | const example = ref(undefined) 12 | 13 | if (isDefined(example)) { 14 | console.log("定义了") 15 | } else { 16 | console.log("未定义") // 将输出 '未定义' 17 | } 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useLastChanged/index.js: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { timestamp } from '../../shared/base' 3 | 4 | /** 5 | * 获取最后一次更新 6 | */ 7 | export const useLastChanged = (source, options = {}) => { 8 | const ms = ref(options.initialValue ?? null) 9 | 10 | watch(source, () => (ms.value = timestamp()), options) 11 | 12 | return ms 13 | } 14 | -------------------------------------------------------------------------------- /docs/api/component/useMounted.md: -------------------------------------------------------------------------------- 1 | # useMounted 2 | 3 | 使用被挂载状态 4 | 5 | ## Usage 6 | 7 | ```html 8 | 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /docs/api/utilities/reactivePick.md: -------------------------------------------------------------------------------- 1 | # reactivePick 2 | 3 | 响应式的 pick 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { reactivePick } from '@/uni_modules/tob-use' 10 | 11 | const obj = reactive({ 12 | x: 0, 13 | y: 0, 14 | elementX: 0, 15 | elementY: 0, 16 | }) 17 | 18 | const picked = reactivePick(obj, 'x', 'elementX') // { x: 0, elementX: 0 } 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useThrottleFn/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | throttleFilter, 3 | createFilterWrapper 4 | } from '../../shared/filters' 5 | 6 | /** 7 | * 使用节流函数 8 | */ 9 | export const useThrottleFn = ( 10 | fn, 11 | ms = 200, 12 | trailing = true, 13 | leading = true 14 | ) => { 15 | return createFilterWrapper( 16 | throttleFilter(ms, trailing, leading), 17 | fn 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /docs/api/state/useStorage.md: -------------------------------------------------------------------------------- 1 | # useStorage 2 | 3 | 使用存储 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { useStorage } from '@/uni_modules/tob-use' 12 | 13 | const state = useStorage('vue-use-local-storage', { 14 | name: 'Banana', 15 | color: 'Yellow', 16 | size: 'Medium', 17 | }) 18 | 19 | // delete data from storage 20 | state.value = null 21 | ``` 22 | -------------------------------------------------------------------------------- /scripts/shared/log.js: -------------------------------------------------------------------------------- 1 | const { terminalColor } = require('@markthree/node-shared') 2 | 3 | const noticeSuccess = (type = '创建') => { 4 | const msg = terminalColor.green(type + '成功') 5 | console.log(msg) 6 | } 7 | 8 | const noticeFail = (type = '创建') => { 9 | const msg = terminalColor.red(type + '失败') 10 | console.log(msg) 11 | } 12 | 13 | module.exports = { 14 | noticeFail, 15 | noticeSuccess 16 | } 17 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/useMounted/README.md: -------------------------------------------------------------------------------- 1 | # useMounted 2 | 3 | 使用被挂载状态 4 | 5 | ## Usage 6 | 7 | ```html 8 | 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import App from './App' 2 | 3 | // #ifndef VUE3 4 | import Vue from 'vue' 5 | Vue.config.productionTip = false 6 | App.mpType = 'app' 7 | const app = new Vue({ 8 | ...App 9 | }) 10 | app.$mount() 11 | // #endif 12 | 13 | // #ifdef VUE3 14 | import { createSSRApp } from 'vue' 15 | export function createApp() { 16 | const app = createSSRApp(App) 17 | return { 18 | app 19 | } 20 | } 21 | // #endif -------------------------------------------------------------------------------- /uni_modules/tob-use/state/useStorage/README.md: -------------------------------------------------------------------------------- 1 | # useStorage 2 | 3 | 使用存储 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { useStorage } from '@/uni_modules/tob-use' 12 | 13 | const state = useStorage('vue-use-local-storage', { 14 | name: 'Banana', 15 | color: 'Yellow', 16 | size: 'Medium', 17 | }) 18 | 19 | // delete data from storage 20 | state.value = null 21 | ``` 22 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/reactivePick/README.md: -------------------------------------------------------------------------------- 1 | # reactivePick 2 | 3 | 响应式的 pick 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { reactivePick } from '@/uni_modules/tob-use' 10 | 11 | const obj = reactive({ 12 | x: 0, 13 | y: 0, 14 | elementX: 0, 15 | elementY: 0, 16 | }) 17 | 18 | const picked = reactivePick(obj, 'x', 'elementX') // { x: 0, elementX: 0 } 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /docs/api/utilities/refDefault.md: -------------------------------------------------------------------------------- 1 | # refDefault 2 | 3 | ref 的默认值 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { refDefault } from '@/uni_modules/tob-use' 10 | 11 | // 未定义的 ref 12 | const raw = ref() 13 | 14 | // 返回一个计算属性 15 | const state = refDefault(raw, 0) 16 | 17 | state.value // 0 18 | 19 | raw.value = 1 20 | state.value // 1 21 | 22 | state.value = 2 23 | raw.value // 2 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /docs/api/utilities/createUnrefFn.md: -------------------------------------------------------------------------------- 1 | # createUnrefFn 2 | 3 | 创建解 ref 函数 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { createUnrefFn } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(1) 12 | const b = ref(2) 13 | 14 | const sum = (a, b) => a + b 15 | 16 | const unrefSum = createUnrefFn(sum) 17 | 18 | sum(a, b) /* ❌ 错误的 ref 参数相加 */ 19 | unrefSum(a, b) /* ✔️ 将自动解 ref 后相加 */ 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /docs/.vuepress/components/useInterval.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/refDefault/README.md: -------------------------------------------------------------------------------- 1 | # refDefault 2 | 3 | ref 的默认值 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { refDefault } from '@/uni_modules/tob-use' 10 | 11 | // 未定义的 ref 12 | const raw = ref() 13 | 14 | // 返回一个计算属性 15 | const state = refDefault(raw, 0) 16 | 17 | state.value // 0 18 | 19 | raw.value = 1 20 | state.value // 1 21 | 22 | state.value = 2 23 | raw.value // 2 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/debouncedWatch/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | debounceFilter, 3 | watchWithFilter 4 | } from '../../shared/filters' 5 | 6 | /** 7 | * 防抖监听 8 | */ 9 | export const debouncedWatch = ( 10 | source, 11 | cb, 12 | options = {} 13 | ) => { 14 | const { debounce = 0, ...watchOptions } = options 15 | 16 | return watchWithFilter(source, cb, { 17 | ...watchOptions, 18 | eventFilter: debounceFilter(debounce) 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /docs/api/utilities/toReactive.md: -------------------------------------------------------------------------------- 1 | # toReactive 2 | 3 | 转化为 Reactive 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { toReactive } from '@/uni_modules/tob-use' 10 | 11 | const origin = ref({ foo: 'bar' }) 12 | 13 | origin.value.foo // bar 14 | 15 | const state = toReactive(origin) 16 | 17 | state.foo // bar 18 | 19 | origin.value = { bar: 'foo' } 20 | 21 | state.foo // undefined 22 | state.bar // foo 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /pages/index/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/useTemplateRefsList/index.js: -------------------------------------------------------------------------------- 1 | import { onBeforeUpdate, ref } from 'vue' 2 | 3 | /** 4 | * 获取模板元素的列表 5 | */ 6 | export const useTemplateRefsList = () => { 7 | const refs = ref([]) 8 | 9 | // 获取 ref 元素 10 | refs.value.set = el => { 11 | if (el) { 12 | refs.value.push(el) 13 | } 14 | } 15 | 16 | // 更新前重置列表 17 | const reset = () => (refs.value.length = 0) 18 | onBeforeUpdate(reset) 19 | return refs 20 | } 21 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/createUnrefFn/README.md: -------------------------------------------------------------------------------- 1 | # createUnrefFn 2 | 3 | 创建解 ref 函数 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { createUnrefFn } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(1) 12 | const b = ref(2) 13 | 14 | const sum = (a, b) => a + b 15 | 16 | const unrefSum = createUnrefFn(sum) 17 | 18 | sum(a, b) /* ❌ 错误的 ref 参数相加 */ 19 | unrefSum(a, b) /* ✔️ 将自动解 ref 后相加 */ 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /docs/api/utilities/get.md: -------------------------------------------------------------------------------- 1 | # get 2 | 3 | 获取 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { get } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(42) 12 | 13 | console.log(get(a)) // 42 14 | ``` 15 | 16 | 或者获取具体对象的 `key` 17 | 18 | 19 | ```js 20 | import { ref } from 'vue' 21 | import { get } from '@/uni_modules/tob-use' 22 | 23 | const foo = ref({ 24 | bar: 100 25 | }) 26 | 27 | console.log(get(foo, 'bar')) // 100 28 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/autoResetRef.md: -------------------------------------------------------------------------------- 1 | # autoResetRef 2 | 3 | 自动重置 ref 4 | 5 | 6 | ## Usage 7 | 8 | ```js 9 | import { ref } from 'vue' 10 | import { autoResetRef } from '@/uni_modules/tob-use' 11 | 12 | // 返回一个 ref,第二个参数可设置延迟,默认为 10 秒 13 | const message = autoResetRef('默认', 1000) 14 | 15 | message.value = '更新了' 16 | 17 | console.log(message.value) // 输出 更新了 18 | 19 | setTimeout(() => { 20 | console.log(message.value) // 输出 默认 21 | }, 2000) 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/toReactive/README.md: -------------------------------------------------------------------------------- 1 | # toReactive 2 | 3 | 转化为 Reactive 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { toReactive } from '@/uni_modules/tob-use' 10 | 11 | const origin = ref({ foo: 'bar' }) 12 | 13 | origin.value.foo // bar 14 | 15 | const state = toReactive(origin) 16 | 17 | state.foo // bar 18 | 19 | origin.value = { bar: 'foo' } 20 | 21 | state.foo // undefined 22 | state.bar // foo 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useClamp/index.js: -------------------------------------------------------------------------------- 1 | import { clamp } from '../../shared/is' 2 | import { ref, computed, unref } from 'vue' 3 | 4 | /** 5 | * 在两个值范围之间取一个值 6 | */ 7 | export const useClamp = (value, min, max) => { 8 | const _value = ref(value) 9 | return computed({ 10 | get() { 11 | return clamp(_value.value, unref(min), unref(max)) 12 | }, 13 | set(value) { 14 | _value.value = clamp(value, unref(min), unref(max)) 15 | } 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /docs/.vuepress/components/eagerComputed.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/get/README.md: -------------------------------------------------------------------------------- 1 | # get 2 | 3 | 获取 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { get } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(42) 12 | 13 | console.log(get(a)) // 42 14 | ``` 15 | 16 | 或者获取具体对象的 `key` 17 | 18 | 19 | ```js 20 | import { ref } from 'vue' 21 | import { get } from '@/uni_modules/tob-use' 22 | 23 | const foo = ref({ 24 | bar: 100 25 | }) 26 | 27 | console.log(get(foo, 'bar')) // 100 28 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/vite.config.js: -------------------------------------------------------------------------------- 1 | // 引入 vite 是为了打包后发布 npm 2 | import path from 'path' 3 | import { defineConfig } from 'vite' 4 | 5 | export default defineConfig({ 6 | build: { 7 | lib: { 8 | name: 'index', 9 | fileName: format => `index.${format}.js`, 10 | entry: path.resolve(__dirname, './index.js') 11 | }, 12 | rollupOptions: { 13 | external: ['vue'], 14 | output: { 15 | globals: { 16 | vue: 'Vue' 17 | } 18 | } 19 | } 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/autoResetRef/README.md: -------------------------------------------------------------------------------- 1 | # autoResetRef 2 | 3 | 自动重置 ref 4 | 5 | 6 | ## Usage 7 | 8 | ```js 9 | import { ref } from 'vue' 10 | import { autoResetRef } from '@/uni_modules/tob-use' 11 | 12 | // 返回一个 ref,第二个参数可设置延迟,默认为 10 秒 13 | const message = autoResetRef('默认', 1000) 14 | 15 | message.value = '更新了' 16 | 17 | console.log(message.value) // 输出 更新了 18 | 19 | setTimeout(() => { 20 | console.log(message.value) // 输出 默认 21 | }, 2000) 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /docs/api/component/useTemplateRefsList.md: -------------------------------------------------------------------------------- 1 | # useTemplateRefsList 2 | 3 | 获取模板元素的列表 4 | 5 | ## Usage 6 | 7 | ```html 8 | 11 | 12 | 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /docs/api/utilities/set.md: -------------------------------------------------------------------------------- 1 | # set 2 | 3 | 设置 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { set } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(0) 12 | 13 | set(a, 1) 14 | 15 | a.value // 1 16 | ``` 17 | 18 | 或者对对象具体的 `key` 设置 19 | 20 | ```js 21 | import { reactive } from 'vue' 22 | import { set } from '@/uni_modules/tob-use' 23 | 24 | const foo = reactive({ 25 | bar: 0 26 | }) 27 | 28 | set(foo, 'bar', 1) 29 | 30 | foo.bar // 1 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /uni_modules/tob-use/changelog.md: -------------------------------------------------------------------------------- 1 | ## 1.1.0(2022-05-17) 2 | ## feat 3 | - useStorage 4 | ## 1.0.14(2022-03-11) 5 | - 更小的包引入 6 | ## 1.0.13(2022-03-11) 7 | - 优化音频自然播放体验 8 | ## 1.0.12(2022-03-11) 9 | - 添加音频播放导出 10 | ## 1.0.11(2022-03-11) 11 | - 添加音频播放 12 | ## 1.0.10(2022-03-02) 13 | - 更新文档 14 | ## 1.0.9(2022-03-02) 15 | - 更新文档链接 16 | ## 1.0.8(2022-03-02) 17 | - 更新文档引用 18 | ## 1.0.7(2022-03-02) 19 | - 更新文档链接 20 | ## 1.0.6(2022-03-02) 21 | - 60+ 基础 api 更新 22 | ## 0.0.1(2022-01-23) 23 | - test 24 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/useTemplateRefsList/README.md: -------------------------------------------------------------------------------- 1 | # useTemplateRefsList 2 | 3 | 获取模板元素的列表 4 | 5 | ## Usage 6 | 7 | ```html 8 | 11 | 12 | 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/set/README.md: -------------------------------------------------------------------------------- 1 | # set 2 | 3 | 设置 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { set } from '@/uni_modules/tob-use' 10 | 11 | const a = ref(0) 12 | 13 | set(a, 1) 14 | 15 | a.value // 1 16 | ``` 17 | 18 | 或者对对象具体的 `key` 设置 19 | 20 | ```js 21 | import { reactive } from 'vue' 22 | import { set } from '@/uni_modules/tob-use' 23 | 24 | const foo = reactive({ 25 | bar: 0 26 | }) 27 | 28 | set(foo, 'bar', 1) 29 | 30 | foo.bar // 1 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /scripts/shared/paths.js: -------------------------------------------------------------------------------- 1 | const { createPath } = require('@markthree/node-shared') 2 | 3 | // 辅助路径工具 4 | const p = createPath(__dirname) 5 | 6 | // 模板路径 7 | const templatePaths = { 8 | apiJs: p('../templates/api.js'), 9 | apiMd: p('../templates/api.md'), 10 | page: p('../templates/page.vue') 11 | } 12 | 13 | // 基础目标路径 14 | const baseDestPaths = { 15 | api: p('../../uni_modules/tob-use'), 16 | page: p('../../pages') 17 | } 18 | 19 | module.exports = { 20 | p, 21 | templatePaths, 22 | baseDestPaths 23 | } 24 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/biSyncRef/index.js: -------------------------------------------------------------------------------- 1 | import { watch } from 'vue' 2 | 3 | /** 4 | * 双向同步 5 | */ 6 | export const biSyncRef = (l, r) => { 7 | const watchOptions = { 8 | flush: 'sync', 9 | immediate: true 10 | } 11 | 12 | const sync1 = newValue => (r.value = newValue) 13 | const stop1 = watch(l, sync1, watchOptions) 14 | 15 | const sync2 = newValue => (l.value = newValue) 16 | const stop2 = watch(r, sync2, watchOptions) 17 | 18 | return () => { 19 | stop1() 20 | stop2() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/syncRef/index.js: -------------------------------------------------------------------------------- 1 | import { watch } from 'vue' 2 | 3 | /** 4 | * 保持目标 ref 跟源同步 5 | */ 6 | export const syncRef = (source, targets, options = {}) => { 7 | const { 8 | deep = false, 9 | flush = 'sync', 10 | immediate = true 11 | } = options 12 | 13 | if (!Array.isArray(targets)) { 14 | targets = [targets] 15 | } 16 | 17 | return watch( 18 | source, 19 | newValue => 20 | targets.forEach(target => (target.value = newValue)), 21 | { flush, deep, immediate } 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /docs/api/component/tryOnUnmounted.md: -------------------------------------------------------------------------------- 1 | # tryOnUnmounted 2 | 3 | 卸载时触发 4 | 5 | 比原生的 `onUnmounted` 更安全 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { tryOnUnmounted } from '@/uni_modules/tob-use' 11 | 12 | // 不在组件内,将不会注册回调 13 | tryOnUnmounted(() => { 14 | console.log("这将不做任何事情") 15 | }) 16 | ``` 17 | 18 | ```html 19 | 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/throttledWatch/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | watchWithFilter, 3 | throttleFilter 4 | } from '../../shared/filters' 5 | 6 | /** 7 | * 节流型监听 8 | */ 9 | export const throttledWatch = ( 10 | source, 11 | cb, 12 | options = {} 13 | ) => { 14 | const { 15 | throttle = 0, 16 | trailing = true, 17 | leading = true, 18 | ...watchOptions 19 | } = options 20 | 21 | return watchWithFilter(source, cb, { 22 | ...watchOptions, 23 | eventFilter: throttleFilter(throttle, trailing, leading) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /docs/api/utilities/useClamp.md: -------------------------------------------------------------------------------- 1 | # useClamp 2 | 3 | 在两个值范围之间取一个值 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useClamp } from '@/uni_modules/tob-use' 10 | 11 | const min = ref(0) 12 | const max = ref(10) 13 | 14 | // 限定范围 0 - 10 15 | const result = useClamp(0, min, max) 16 | 17 | result.value = 20 18 | 19 | result.value // 超出最大,仍然是 10 20 | 21 | result.value = -1 22 | 23 | result.value // 超出最小,仍然是 0 24 | 25 | max.value = 20 // 调整最大值为 20 26 | 27 | result.value = 20 28 | 29 | result.value // 20 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/tryOnUnmounted/README.md: -------------------------------------------------------------------------------- 1 | # tryOnUnmounted 2 | 3 | 卸载时触发 4 | 5 | 比原生的 `onUnmounted` 更安全 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { tryOnUnmounted } from '@/uni_modules/tob-use' 11 | 12 | // 不在组件内,将不会注册回调 13 | tryOnUnmounted(() => { 14 | console.log("这将不做任何事情") 15 | }) 16 | ``` 17 | 18 | ```html 19 | 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/pausableWatch/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | watchWithFilter, 3 | pausableFilter 4 | } from '../../shared/filters' 5 | 6 | /** 7 | * 可暂停的监听 8 | */ 9 | export const pausableWatch = (source, cb, options = {}) => { 10 | const { eventFilter: filter, ...watchOptions } = options 11 | 12 | const { eventFilter, pause, resume, isActive } = 13 | pausableFilter(filter) 14 | 15 | const stop = watchWithFilter(source, cb, { 16 | ...watchOptions, 17 | eventFilter 18 | }) 19 | 20 | return { stop, pause, resume, isActive } 21 | } 22 | -------------------------------------------------------------------------------- /docs/api/component/tryOnBeforeUnmount.md: -------------------------------------------------------------------------------- 1 | # tryOnBeforeUnmount 2 | 3 | 在卸载前触发 4 | 5 | 比原生的 `onBeforeUnmount` 更安全 6 | ## Usage 7 | 8 | ```js 9 | import { tryOnBeforeUnmount } from '@/uni_modules/tob-use' 10 | 11 | // 不在组件内,将不会注册回调 12 | tryOnBeforeUnmount(() => { 13 | console.log("这将不做任何事情") 14 | }) 15 | ``` 16 | 17 | ```html 18 | 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /uni_modules/tob-use/shared/create.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 创建一个共享的 composition-api 3 | */ 4 | export const createSharedComposable = composable => { 5 | let subscribers = 0 6 | let state, scope 7 | 8 | const dispose = () => { 9 | if (scope && --subscribers <= 0) { 10 | scope.stop() 11 | state = scope = null 12 | } 13 | } 14 | 15 | return (...args) => { 16 | subscribers++ 17 | if (!state) { 18 | scope = effectScope(true) 19 | state = scope.run(() => composable(...args)) 20 | } 21 | onScopeDispose(dispose) 22 | return state 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/.vuepress/public/images/back-to-top.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/api/animation/useTimeoutFn.md: -------------------------------------------------------------------------------- 1 | # useTimeoutFn 2 | 3 | 定时器方法 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useTimeoutFn } from '@/uni_modules/tob-use' 11 | 12 | const { 13 | stop, 14 | start, 15 | isPending, 16 | } = useTimeoutFn(() => { 17 | // 你希望执行的函数 18 | }, 3000) 19 | ``` 20 | 21 |
22 | 23 | ### 立即开始 24 | 25 | ```js 26 | import { useTimeoutFn } from '@/uni_modules/tob-use' 27 | 28 | const instance = useTimeoutFn(() => { 29 | // 你希望执行的函数 30 | }, 3000, { 31 | immediate: false // 立即开始,默认为 true 32 | }) 33 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/createEventHook/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 创建事件 hook 3 | */ 4 | export const createEventHook = () => { 5 | const fns = [] 6 | 7 | // 卸载 8 | const off = fn => { 9 | const index = fns.indexOf(fn) 10 | if (index !== -1) fns.splice(index, 1) 11 | } 12 | 13 | // 注册 14 | const on = fn => { 15 | fns.push(fn) 16 | return { 17 | off: () => off(fn) 18 | } 19 | } 20 | 21 | // 触发 22 | const trigger = param => { 23 | fns.forEach(fn => fn(param)) 24 | } 25 | 26 | return { 27 | on, 28 | off, 29 | trigger 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useClamp/README.md: -------------------------------------------------------------------------------- 1 | # useClamp 2 | 3 | 在两个值范围之间取一个值 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useClamp } from '@/uni_modules/tob-use' 10 | 11 | const min = ref(0) 12 | const max = ref(10) 13 | 14 | // 限定范围 0 - 10 15 | const result = useClamp(0, min, max) 16 | 17 | result.value = 20 18 | 19 | result.value // 超出最大,仍然是 10 20 | 21 | result.value = -1 22 | 23 | result.value // 超出最小,仍然是 0 24 | 25 | max.value = 20 // 调整最大值为 20 26 | 27 | result.value = 20 28 | 29 | result.value // 20 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /docs/api/utilities/biSyncRef.md: -------------------------------------------------------------------------------- 1 | # biSyncRef 2 | 3 | 双向同步 4 | 5 | 同步两个 `ref` 参数 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { biSyncRef } from '@/uni_modules/tob-use' 12 | 13 | const a = ref('a') 14 | const b = ref('b') 15 | 16 | const stop = biSyncRef(a, b) 17 | 18 | console.log(a.value) // a 19 | 20 | b.value = 'foo' 21 | 22 | console.log(a.value) // foo 23 | 24 | a.value = 'bar' 25 | 26 | console.log(b.value) // bar 27 | 28 | stop() // 停止同步 29 | 30 | a.value = 'foo' 31 | 32 | console.log(b.value) // 仍然是 bar 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /docs/api/utilities/makeDestructurable.md: -------------------------------------------------------------------------------- 1 | # makeDestructurable 2 | 3 | 使得更好分解 4 | 5 | 即让结果可以使用对象解构或数组解构两种方式 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { makeDestructurable } from '@/uni_modules/tob-use' 12 | 13 | const bar = 1024 14 | const foo = { name: 'foo' } 15 | 16 | const obj = makeDestructurable( 17 | { foo, bar }, 18 | [ foo, bar ] 19 | ) 20 | ``` 21 | 22 | 使用时就可以用数组解构或对象解构了 👇 23 | 24 | ```ts 25 | // 对象解构 26 | let { foo, bar } = obj 27 | ``` 28 | 29 | 或者 30 | 31 | ```ts 32 | // 数组解构 33 | let [ foo, bar ] = obj 34 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/component/tryOnBeforeUnmount/README.md: -------------------------------------------------------------------------------- 1 | # tryOnBeforeUnmount 2 | 3 | 在卸载前触发 4 | 5 | 比原生的 `onBeforeUnmount` 更安全 6 | ## Usage 7 | 8 | ```js 9 | import { tryOnBeforeUnmount } from '@/uni_modules/tob-use' 10 | 11 | // 不在组件内,将不会注册回调 12 | tryOnBeforeUnmount(() => { 13 | console.log("这将不做任何事情") 14 | }) 15 | ``` 16 | 17 | ```html 18 | 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useDebounce/index.js: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { useDebounceFn } from '../useDebounceFn' 3 | 4 | /** 5 | * 使用防抖 ref 6 | */ 7 | export const useDebounce = ( 8 | value, 9 | ms = 200, 10 | options = {} 11 | ) => { 12 | if (ms <= 0) { 13 | return value 14 | } 15 | 16 | const debounced = ref(value.value) 17 | 18 | const updater = useDebounceFn( 19 | () => { 20 | debounced.value = value.value 21 | }, 22 | ms, 23 | options 24 | ) 25 | 26 | watch(value, () => updater()) 27 | 28 | return debounced 29 | } 30 | -------------------------------------------------------------------------------- /uni_modules/tob-use/animation/useTimeoutFn/README.md: -------------------------------------------------------------------------------- 1 | # useTimeoutFn 2 | 3 | 定时器方法 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useTimeoutFn } from '@/uni_modules/tob-use' 11 | 12 | const { 13 | stop, 14 | start, 15 | isPending, 16 | } = useTimeoutFn(() => { 17 | // 你希望执行的函数 18 | }, 3000) 19 | ``` 20 | 21 |
22 | 23 | ### 立即开始 24 | 25 | ```js 26 | import { useTimeoutFn } from '@/uni_modules/tob-use' 27 | 28 | const instance = useTimeoutFn(() => { 29 | // 你希望执行的函数 30 | }, 3000, { 31 | immediate: false // 立即开始,默认为 true 32 | }) 33 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/makeDestructurable/README.md: -------------------------------------------------------------------------------- 1 | # makeDestructurable 2 | 3 | 使得更好分解 4 | 5 | 即让结果可以使用对象解构或数组解构两种方式 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { makeDestructurable } from '@/uni_modules/tob-use' 12 | 13 | const bar = 1024 14 | const foo = { name: 'foo' } 15 | 16 | const obj = makeDestructurable( 17 | { foo, bar }, 18 | [ foo, bar ] 19 | ) 20 | ``` 21 | 22 | 使用时就可以用数组解构或对象解构了 👇 23 | 24 | ```ts 25 | // 对象解构 26 | let { foo, bar } = obj 27 | ``` 28 | 29 | 或者 30 | 31 | ```ts 32 | // 数组解构 33 | let [ foo, bar ] = obj 34 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/and.md: -------------------------------------------------------------------------------- 1 | # and 2 | 3 | AND 判断 4 | 5 | 当所有参数都为 [Truthy](https://developer.mozilla.org/zh-CN/docs/Glossary/Truthy) 时,才为 [Truthy](https://developer.mozilla.org/zh-CN/docs/Glossary/Truthy)。 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { ref, watch } from 'vue' 11 | import { and } from '@/uni_modules/tob-use' 12 | 13 | // 支持 ref 包裹的参数 14 | const foo = ref(false) 15 | const bar = ref(true) 16 | 17 | const result = and(foo, bar) // 返回一个计算属性结果 18 | 19 | watch(result, (v) => { 20 | // 只有当 foo 和 bar 的 value 都为 true 时,才输出 true 21 | console.log(v) 22 | }) 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /uni_modules/tob-use/shared/base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 空函数 3 | */ 4 | export const noop = () => {} 5 | 6 | /** 7 | * 事件 8 | */ 9 | export const events = new Map() 10 | 11 | /** 12 | * 时间戳获取 13 | */ 14 | export const timestamp = () => +Date.now() 15 | 16 | /** 17 | * Promise 型定时器 18 | */ 19 | export function promiseTimeout( 20 | ms, 21 | throwOnTimeout = false, 22 | reason = 'Timeout' 23 | ) { 24 | return new Promise((resolve, reject) => { 25 | if (throwOnTimeout) { 26 | setTimeout(() => reject(reason), ms) 27 | } else { 28 | setTimeout(resolve, ms) 29 | } 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/biSyncRef/README.md: -------------------------------------------------------------------------------- 1 | # biSyncRef 2 | 3 | 双向同步 4 | 5 | 同步两个 `ref` 参数 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { biSyncRef } from '@/uni_modules/tob-use' 12 | 13 | const a = ref('a') 14 | const b = ref('b') 15 | 16 | const stop = biSyncRef(a, b) 17 | 18 | console.log(a.value) // a 19 | 20 | b.value = 'foo' 21 | 22 | console.log(a.value) // foo 23 | 24 | a.value = 'bar' 25 | 26 | console.log(b.value) // bar 27 | 28 | stop() // 停止同步 29 | 30 | a.value = 'foo' 31 | 32 | console.log(b.value) // 仍然是 bar 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /uni_modules/tob-use/state/useStorage/guess.js: -------------------------------------------------------------------------------- 1 | export function guessSerializerType(rawInit) { 2 | return rawInit == null 3 | ? 'any' 4 | : rawInit instanceof Set 5 | ? 'set' 6 | : rawInit instanceof Map 7 | ? 'map' 8 | : rawInit instanceof Date 9 | ? 'date' 10 | : typeof rawInit === 'boolean' 11 | ? 'boolean' 12 | : typeof rawInit === 'string' 13 | ? 'string' 14 | : typeof rawInit === 'object' 15 | ? 'object' 16 | : Array.isArray(rawInit) 17 | ? 'object' 18 | : !Number.isNaN(rawInit) 19 | ? 'number' 20 | : 'any' 21 | } 22 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/and/README.md: -------------------------------------------------------------------------------- 1 | # and 2 | 3 | AND 判断 4 | 5 | 当所有参数都为 [Truthy](https://developer.mozilla.org/zh-CN/docs/Glossary/Truthy) 时,才为 [Truthy](https://developer.mozilla.org/zh-CN/docs/Glossary/Truthy)。 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { ref, watch } from 'vue' 11 | import { and } from '@/uni_modules/tob-use' 12 | 13 | // 支持 ref 包裹的参数 14 | const foo = ref(false) 15 | const bar = ref(true) 16 | 17 | const result = and(foo, bar) // 返回一个计算属性结果 18 | 19 | watch(result, (v) => { 20 | // 只有当 foo 和 bar 的 value 都为 true 时,才输出 true 21 | console.log(v) 22 | }) 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useCounter/index.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | /** 4 | * 计数器 5 | */ 6 | export const useCounter = (initialValue = 0) => { 7 | const count = ref(initialValue) 8 | 9 | // 递增 10 | const inc = (delta = 1) => (count.value += delta) 11 | // 递减 12 | const dec = (delta = 1) => (count.value -= delta) 13 | // 获取 14 | const get = () => count.value 15 | // 设置 16 | const set = val => (count.value = val) 17 | // 重置 18 | const reset = (val = initialValue) => { 19 | initialValue = val 20 | return set(val) 21 | } 22 | 23 | return { count, inc, dec, get, set, reset } 24 | } 25 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useThrottle/index.js: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { useThrottleFn } from '../useThrottleFn' 3 | 4 | /** 5 | * 使用节流 ref 6 | */ 7 | export const useThrottle = ( 8 | value, 9 | delay = 200, 10 | trailing = true, 11 | leading = true 12 | ) => { 13 | if (delay <= 0) { 14 | return value 15 | } 16 | 17 | const throttled = ref(value.value) 18 | 19 | const updater = useThrottleFn( 20 | () => { 21 | throttled.value = value.value 22 | }, 23 | delay, 24 | trailing, 25 | leading 26 | ) 27 | 28 | watch(value, () => updater()) 29 | 30 | return throttled 31 | } 32 | -------------------------------------------------------------------------------- /uni_modules/tob-use/shared/is.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 范围限定 3 | */ 4 | export const clamp = (n, min, max) => { 5 | return Math.min(max, Math.max(min, n)) 6 | } 7 | 8 | /** 9 | * 是否是布尔类型 10 | */ 11 | export const isBoolean = v => typeof v === 'boolean' 12 | 13 | /** 14 | * 是否是函数类型 15 | */ 16 | export const isFunction = v => typeof v === 'function' 17 | 18 | /** 19 | * 是否是字符串类型 20 | */ 21 | export const isString = v => typeof v === 'string' 22 | 23 | /** 24 | * 是否是数字类型 25 | */ 26 | export const isNumber = v => typeof v === 'number' 27 | 28 | /** 29 | * 是否未定义 30 | */ 31 | export const isUndefined = v => typeof v === 'undefined' 32 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/makeDestructurable/index.js: -------------------------------------------------------------------------------- 1 | import { isUndefined } from '../../shared/is' 2 | 3 | /** 4 | * 使得更好分解 5 | */ 6 | export const makeDestructurable = (obj, arr) => { 7 | if (!isUndefined(Symbol)) { 8 | const clone = { ...obj } 9 | 10 | Object.defineProperty(clone, Symbol.iterator, { 11 | enumerable: false, 12 | value() { 13 | let index = 0 14 | return { 15 | next: () => ({ 16 | value: arr[index++], 17 | done: index > arr.length 18 | }) 19 | } 20 | } 21 | }) 22 | 23 | return clone 24 | } else { 25 | return Object.assign([...arr], obj) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/api/utilities/toRefs.md: -------------------------------------------------------------------------------- 1 | # toRefs 2 | 3 | 扩展之后的 toRefs,允许接受对象型的 ref 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { toRefs } from '@/uni_modules/tob-use' 10 | 11 | const obj = reactive({ a: 'a', b: 0 }) 12 | const arr = reactive(['c', 0]) 13 | 14 | const { a, b } = toRefs(obj) 15 | const [ c, d ] = toRefs(arr) 16 | ``` 17 | 18 | `ref` 类型的也支持 19 | 20 | ```ts 21 | import { ref } from 'vue' 22 | import { toRefs } from '@/uni_modules/tob-use' 23 | 24 | const objRef = ref({ a: 'a', b: 0 }) 25 | const arrRef = ref(['c', 0]) 26 | 27 | const { a, b } = toRefs(objRef) 28 | const [ c, d ] = toRefs(arrRef) 29 | ``` -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroText: tob-use 4 | tagline: uniapp 的高效 use 库 5 | actions: 6 | - text: Get Started 👉 7 | link: /guide/ 8 | - text: install 9 | link: /guide/start/ 10 | type: secondary 11 | features: 12 | - title: 🦖 功能丰富 13 | details: 60+ 的功能供你选择 14 | - title: 🦕 按需加载 15 | details: 让你的应用体积更小 16 | - title: 🐊 灵活可配置 17 | details: 可配置的事件过滤器 18 | - title: 🐳 支持多端 19 | details: 支持 app,h5 和 小程序 20 | - title: 🐬 composition 21 | details: 完全由 composition-api 构建 22 | - title: 🐟 vue3 23 | details: 支持最现代化的 vue3 版本 24 | footer: --- thanks for vueuse 🤗 --- 25 | --- 26 | 27 | -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/watchAtMost/index.js: -------------------------------------------------------------------------------- 1 | import { nextTick, ref, unref } from 'vue' 2 | import { watchWithFilter } from '../../shared/filters' 3 | 4 | /** 5 | * 限制次数型监听 6 | */ 7 | export const watchAtMost = (source, cb, options = {}) => { 8 | const { count, ...watchOptions } = options 9 | 10 | const current = ref(0) 11 | 12 | const stop = watchWithFilter( 13 | source, 14 | (...args) => { 15 | current.value += 1 16 | // 到达指定次数后暂停副作用并触发回调 17 | if (current.value >= unref(count)) { 18 | nextTick(stop) 19 | } 20 | cb(...args) 21 | }, 22 | watchOptions 23 | ) 24 | 25 | return { count: current, stop } 26 | } 27 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/toRefs/README.md: -------------------------------------------------------------------------------- 1 | # toRefs 2 | 3 | 扩展之后的 toRefs,允许接受对象型的 ref 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { toRefs } from '@/uni_modules/tob-use' 10 | 11 | const obj = reactive({ a: 'a', b: 0 }) 12 | const arr = reactive(['c', 0]) 13 | 14 | const { a, b } = toRefs(obj) 15 | const [ c, d ] = toRefs(arr) 16 | ``` 17 | 18 | `ref` 类型的也支持 19 | 20 | ```ts 21 | import { ref } from 'vue' 22 | import { toRefs } from '@/uni_modules/tob-use' 23 | 24 | const objRef = ref({ a: 'a', b: 0 }) 25 | const arrRef = ref(['c', 0]) 26 | 27 | const { a, b } = toRefs(objRef) 28 | const [ c, d ] = toRefs(arrRef) 29 | ``` -------------------------------------------------------------------------------- /.hbuilderx/launch.json: -------------------------------------------------------------------------------- 1 | { // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/ 2 | // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数 3 | "version": "0.0", 4 | "configurations": [{ 5 | "app-plus" : 6 | { 7 | "launchtype" : "local" 8 | }, 9 | "default" : 10 | { 11 | "launchtype" : "local" 12 | }, 13 | "h5" : 14 | { 15 | "launchtype" : "local" 16 | }, 17 | "mp-weixin" : 18 | { 19 | "launchtype" : "local" 20 | }, 21 | "type" : "uniCloud" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /uni_modules/tob-use/animation/useTimeout/index.js: -------------------------------------------------------------------------------- 1 | import { computed } from 'vue' 2 | import { noop } from '../../shared/base' 3 | import { useTimeoutFn } from '../useTimeoutFn' 4 | 5 | /** 6 | * 定时器 7 | */ 8 | export const useTimeout = ( 9 | interval = 1000, 10 | options = {} 11 | ) => { 12 | const { controls: exposeControls = false } = options 13 | 14 | const controls = useTimeoutFn(noop, interval, options) 15 | 16 | // 定时器触发完成标志 17 | const ready = computed(() => !controls.isPending.value) 18 | 19 | // 暴露定时器控制权 20 | if (exposeControls) { 21 | return { 22 | ready, 23 | ...controls 24 | } 25 | } else { 26 | return ready 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/guide/start.md: -------------------------------------------------------------------------------- 1 | # 开始 2 | 3 | ## IDE 4 | 5 | ### 1. 安装 6 | 7 | 如果你是直接使用 `HBuilderx` 这个 `IDE` 进行开发的,你可以通过 👉 [插件市场](https://ext.dcloud.net.cn/plugin?id=7308) 直接导入插件。 8 | 9 | ![install](/images/install.png) 10 | 11 |
12 | 13 | ### 2. 使用 14 | 15 | 直接从 `@/uni_modules/tob-use` 引入即可。 16 | 17 | ```html 18 | 24 | 25 | 30 | ``` 31 | 32 | 更多 `api` 可见 👉 [API 参考](/api/utilities/and) -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useToggle/index.js: -------------------------------------------------------------------------------- 1 | import { ref, isRef } from 'vue' 2 | import { isBoolean } from '../../shared/is' 3 | 4 | /** 5 | * 切换 6 | */ 7 | export const useToggle = (initialValue = false) => { 8 | if (isRef(initialValue)) { 9 | return value => { 10 | initialValue.value = isBoolean(value) 11 | ? value 12 | : !initialValue.value 13 | return initialValue.value 14 | } 15 | } else { 16 | const boolean = ref(initialValue) 17 | const toggle = value => { 18 | boolean.value = isBoolean(value) 19 | ? value 20 | : !boolean.value 21 | return boolean.value 22 | } 23 | 24 | return [boolean, toggle] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /uni_modules/tob-use/animation/useInterval/index.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { useIntervalFn } from '../useIntervalFn' 3 | 4 | /** 5 | * 间隔 6 | */ 7 | export const useInterval = ( 8 | interval = 1000, 9 | options = {} 10 | ) => { 11 | const { 12 | controls: exposeControls = false, 13 | immediate = true 14 | } = options 15 | 16 | const counter = ref(0) 17 | // 调用间隔循环函数 18 | const controls = useIntervalFn( 19 | () => (counter.value += 1), 20 | interval, 21 | { immediate } 22 | ) 23 | 24 | // 暴露控制权 25 | if (exposeControls) { 26 | return { 27 | counter, 28 | ...controls 29 | } 30 | } else { 31 | return counter 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs/api/utilities/useEventBus.md: -------------------------------------------------------------------------------- 1 | # useEventBus 2 | 3 | 使用事件 bus 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useEventBus } from '@/uni_modules/tob-use' 10 | 11 | const bus = useEventBus('news') 12 | 13 | const listener = (event) => { 14 | console.log(`事件: ${event}`) 15 | } 16 | 17 | // 监听,返回取消订阅函数 18 | const unsubscribe = bus.on(listener) 19 | 20 | // 触发 21 | bus.emit('触发') // 输出 '事件: 触发' 22 | 23 | // 卸载之前注册的事件回调 24 | unsubscribe() 25 | // 或者用 bus.off 26 | bus.off(listener) 27 | 28 | bus.once(() => console.log('一次性事件')) 29 | 30 | bus.emit() // 输出: 一次性事件 31 | 32 | bus.emit() // 啥都不做 33 | 34 | // 清理所有的事件回调 35 | bus.reset() 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/autoResetRef/index.js: -------------------------------------------------------------------------------- 1 | import { customRef, unref } from 'vue' 2 | 3 | /** 4 | * 自动重置ref 5 | */ 6 | export const autoResetRef = ( 7 | defaultValue, 8 | afterMs = 10000 9 | ) => { 10 | return customRef((track, trigger) => { 11 | let value 12 | let timer 13 | const resetAfter = () => { 14 | setTimeout(() => { 15 | value = defaultValue 16 | trigger() 17 | }, unref(afterMs)) 18 | } 19 | 20 | return { 21 | get() { 22 | track() 23 | return value 24 | }, 25 | set(newValue) { 26 | value = newValue 27 | trigger() 28 | clearTimeout(timer) 29 | timer = resetAfter() 30 | } 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /docs/api/utilities/useToggle.md: -------------------------------------------------------------------------------- 1 | # useToggle 2 | 3 | 切换 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { useToggle } from '@/uni_modules/tob-use' 9 | 10 | const [state, toggle] = useToggle() 11 | 12 | state.value // false 13 | 14 | toggle() 15 | 16 | state.value // true 17 | 18 | toggle(true) 19 | 20 | state.value // true 21 | ``` 22 | 23 | 当传入一个 `ref` 时,`useToggle` 将只返回 `toggle` 函数 👇 24 | 25 | ```ts 26 | import { useToggle } from '@/uni_modules/tob-use' 27 | 28 | const state = ref(false) 29 | 30 | const toggle = useToggle(state) 31 | 32 | state.value // false 33 | 34 | toggle() 35 | 36 | state.value // true 37 | 38 | toggle(true) 39 | 40 | state.value // true 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/api/utilities/controlledComputed.md: -------------------------------------------------------------------------------- 1 | # controlledComputed 2 | 3 | 受控型计算属性 4 | 5 | 只有源更新了,才会触发更新,适合频繁更新的场景。 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { controlledComputed } from '@/uni_modules/tob-use' 12 | 13 | let source = ref('foo') 14 | let counter = ref(0) 15 | 16 | const computedRef = controlledComputed( 17 | source, // 监听源,有点像 watch,支持原生 watch 的各种形式 18 | () => counter.value // 返回值函数,有点像 computed 19 | ) 20 | 21 | console.log(computedRef.value) // 0 22 | 23 | counter.value += 1 24 | 25 | console.log(computedRef.value) // 仍然是 0 26 | 27 | source.value = 'bar' // 更新源,才会同步最新的返回值函数 28 | 29 | console.log(computedRef.value) // 1 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useToggle/README.md: -------------------------------------------------------------------------------- 1 | # useToggle 2 | 3 | 切换 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { useToggle } from '@/uni_modules/tob-use' 9 | 10 | const [state, toggle] = useToggle() 11 | 12 | state.value // false 13 | 14 | toggle() 15 | 16 | state.value // true 17 | 18 | toggle(true) 19 | 20 | state.value // true 21 | ``` 22 | 23 | 当传入一个 `ref` 时,`useToggle` 将只返回 `toggle` 函数 👇 24 | 25 | ```ts 26 | import { useToggle } from '@/uni_modules/tob-use' 27 | 28 | const state = ref(false) 29 | 30 | const toggle = useToggle() 31 | 32 | state.value // false 33 | 34 | toggle() 35 | 36 | state.value // true 37 | 38 | toggle(true) 39 | 40 | state.value // true 41 | ``` -------------------------------------------------------------------------------- /docs/api/animation/useTimeout.md: -------------------------------------------------------------------------------- 1 | # useTimeout 2 | 3 | 定时器 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useTimeout } from '@/uni_modules/tob-use' 11 | 12 | // 设置间隔,单位为毫秒,默认为 1000 毫秒 13 | const ready = useTimeout(1000) // 1 秒后,ready.value 将变成 true 14 | 15 | ready.value // 一个布尔类型的计算属性 16 | ``` 17 | 18 |
19 | 20 | #### 可控制 21 | 22 | ```js 23 | import { useTimeout } from '@/uni_modules/tob-use' 24 | 25 | const { 26 | stop, // 停止,函数 27 | start, // 开启,函数 28 | ready, // 完成,布尔类型的计算属性 29 | } = useTimeout(1000, { 30 | controls: true // 开启控制权 31 | }) 32 | 33 | start() // 需要手动开启 34 | 35 | setTimeout(() => { 36 | ready.value // true 37 | }, 2000) 38 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/extendRef.md: -------------------------------------------------------------------------------- 1 | # extendRef 2 | 3 | ref 扩展 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { extendRef } from '@/uni_modules/tob-use' 10 | 11 | const myRef = ref('content') 12 | 13 | const extended = extendRef(myRef, { foo: 'extra data' }) 14 | 15 | extended.value === 'content' 16 | extended.foo === 'extra data' 17 | ``` 18 | 19 | `ref` 将被解包并且是响应式的 20 | 21 | ```ts 22 | const myRef = ref('content') 23 | const extraRef = ref('extra') 24 | 25 | const extended = extendRef(myRef, { extra: extraRef }) 26 | 27 | extended.value === 'content' 28 | extended.extra === 'extra' 29 | 30 | extended.extra = 'new data' // 将触发源更新 31 | extraRef.value === 'new data' 32 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useEventBus/README.md: -------------------------------------------------------------------------------- 1 | # useEventBus 2 | 3 | 使用事件 bus 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useEventBus } from '@/uni_modules/tob-use' 10 | 11 | const bus = useEventBus('news') 12 | 13 | const listener = (event) => { 14 | console.log(`事件: ${event}`) 15 | } 16 | 17 | // 监听,返回取消订阅函数 18 | const unsubscribe = bus.on(listener) 19 | 20 | // 触发 21 | bus.emit('触发') // 输出 '事件: 触发' 22 | 23 | // 卸载之前注册的事件回调 24 | unsubscribe() 25 | // 或者用 bus.off 26 | bus.off(listener) 27 | 28 | bus.once(() => console.log('一次性事件')) 29 | 30 | bus.emit() // 输出: 一次性事件 31 | 32 | bus.emit() // 啥都不做 33 | 34 | // 清理所有的事件回调 35 | bus.reset() 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /uni_modules/tob-use/animation/useTimeout/README.md: -------------------------------------------------------------------------------- 1 | # useTimeout 2 | 3 | 定时器 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useTimeout } from '@/uni_modules/tob-use' 11 | 12 | // 设置间隔,单位为毫秒,默认为 1000 毫秒 13 | const ready = useTimeout(1000) // 1 秒后,ready.value 将变成 true 14 | 15 | ready.value // 一个布尔类型的计算属性 16 | ``` 17 | 18 |
19 | 20 | #### 可控制 21 | 22 | ```js 23 | import { useTimeout } from '@/uni_modules/tob-use' 24 | 25 | const { 26 | stop, // 停止,函数 27 | start, // 开启,函数 28 | ready, // 完成,布尔类型的计算属性 29 | } = useTimeout(1000, { 30 | controls: true // 开启控制权 31 | }) 32 | 33 | start() // 需要手动开启 34 | 35 | setTimeout(() => { 36 | ready.value // true 37 | }, 2000) 38 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/controlledComputed/README.md: -------------------------------------------------------------------------------- 1 | # controlledComputed 2 | 3 | 受控型计算属性 4 | 5 | 只有源更新了,才会触发更新,适合频繁更新的场景。 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { controlledComputed } from '@/uni_modules/tob-use' 12 | 13 | let source = ref('foo') 14 | let counter = ref(0) 15 | 16 | const computedRef = controlledComputed( 17 | source, // 监听源,有点像 watch,支持原生 watch 的各种形式 18 | () => counter.value // 返回值函数,有点像 computed 19 | ) 20 | 21 | console.log(computedRef.value) // 0 22 | 23 | counter.value += 1 24 | 25 | console.log(computedRef.value) // 仍然是 0 26 | 27 | source.value = 'bar' // 更新源,才会同步最新的返回值函数 28 | 29 | console.log(computedRef.value) // 1 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/extendRef/index.js: -------------------------------------------------------------------------------- 1 | import { isRef } from 'vue' 2 | 3 | /** 4 | * ref 扩展 5 | */ 6 | export const extendRef = (ref, extend, options = {}) => { 7 | const { enumerable = false, unwrap = true } = options 8 | 9 | for (const [key, value] of Object.entries(extend)) { 10 | if (key === 'value') continue // 忽略 .value 键 11 | 12 | // 保留原有 ref 13 | if (isRef(value) && unwrap) { 14 | Object.defineProperty(ref, key, { 15 | get() { 16 | return value.value 17 | }, 18 | set(v) { 19 | value.value = v 20 | }, 21 | enumerable 22 | }) 23 | } else { 24 | Object.defineProperty(ref, key, { value, enumerable }) 25 | } 26 | } 27 | return ref 28 | } 29 | -------------------------------------------------------------------------------- /uni_modules/tob-less/readme.md: -------------------------------------------------------------------------------- 1 | # tob-less 2 | 3 | ## 介绍 👀 4 | 5 | 一个主题驱动的 **uniapp** 样式库 6 | 7 |
8 |
9 |
10 | 11 | ## 特点 👍 12 | 13 | 1. 原子的 14 | - 开发效率直接翻一番 15 | 2. 主题驱动 16 | - 自定义主题,满足个性化需求 17 | 3. 支持Mixins 18 | - 语义化的模板,更清晰的结构 19 | 20 |
21 |
22 |
23 | 24 | 25 | ## 文档 😋 26 | 27 | [tob-less 文档](https://tob-less.netlify.app/) 28 | 29 |
30 |
31 |
32 | 33 | ## 启发 😀 34 | 35 | 该库受以下技术启发 36 | 37 | - [Less](https://less.bootcss.com/) 38 | - [Css 变量](https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties) 39 | - [Windicss](https://cn.windicss.org/) 40 | - [Tailwindcss](https://www.tailwindcss.cn/) 41 | 42 |
-------------------------------------------------------------------------------- /docs/api/animation/useIntervalFn.md: -------------------------------------------------------------------------------- 1 | # useIntervalFn 2 | 3 | 使用间隔函数 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useIntervalFn } from '@/uni_modules/tob-use' 10 | 11 | // 第二个参数设置间隔,单位为毫秒,默认为 1000 毫秒 12 | const { pause, resume, isActive } = useIntervalFn(() => { 13 | // 你希望执行的函数 14 | }, 1000) 15 | 16 | isActive.value // 是否在执行中 17 | 18 | pause() // 暂停 19 | 20 | resume() // 恢复 21 | ``` 22 | 23 |
24 | 25 | ### Watch 选项 26 | 27 | ```js 28 | import { useInterval } from '@/uni_modules/tob-use' 29 | 30 | const { pause, resume, isActive } = useIntervalFn(200, { 31 | immediate: false, // 立即开启,默认为 true 32 | immediateCallback: false // 立即触发,不需要等待第一个间隔,默认为 false 33 | }) 34 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/extendRef/README.md: -------------------------------------------------------------------------------- 1 | # extendRef 2 | 3 | ref 扩展 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { extendRef } from '@/uni_modules/tob-use' 10 | 11 | const myRef = ref('content') 12 | 13 | const extended = extendRef(myRef, { foo: 'extra data' }) 14 | 15 | extended.value === 'content' 16 | extended.foo === 'extra data' 17 | ``` 18 | 19 | `ref` 将被解包并且是响应式的 20 | 21 | ```ts 22 | const myRef = ref('content') 23 | const extraRef = ref('extra') 24 | 25 | const extended = extendRef(myRef, { extra: extraRef }) 26 | 27 | extended.value === 'content' 28 | extended.extra === 'extra' 29 | 30 | extended.extra = 'new data' // 将触发源更新 31 | extraRef.value === 'new data' 32 | ``` -------------------------------------------------------------------------------- /docs/api/component/tryOnMounted.md: -------------------------------------------------------------------------------- 1 | # tryOnMounted 2 | 3 | 在挂载后触发 4 | 5 | 比原生的 `onMount` 更安全 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { tryOnMounted } from '@/uni_modules/tob-use' 11 | 12 | // 不在组件内,将直接调用 13 | tryOnMounted(() => { 14 | console.log("这将不做任何事情") 15 | }) 16 | ``` 17 | 18 | ```html 19 | 27 | ``` 28 | 29 |
30 | 31 | ### 非同步 32 | 33 | ```js 34 | import { tryOnMounted } from '@/uni_modules/tob-use' 35 | 36 | // 不在组件内,设置第二参数,将在 nextTick 调用 37 | tryOnMounted(() => { 38 | console.log("这将不做任何事情") 39 | }, false) 40 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/controlledComputed/index.js: -------------------------------------------------------------------------------- 1 | import { ref, customRef } from 'vue' 2 | 3 | /** 4 | * 受控型计算属性 5 | */ 6 | export const controlledComputed = (source, fn) => { 7 | let v 8 | let track 9 | let trigger 10 | const dirty = ref(true) 11 | 12 | // 只有 source 源改变时才去触发 13 | watch( 14 | source, 15 | () => { 16 | dirty.value = true 17 | trigger() 18 | }, 19 | { flush: 'sync' } 20 | ) 21 | 22 | return customRef((_track, _trigger) => { 23 | track = _track 24 | trigger = _trigger 25 | 26 | return { 27 | get() { 28 | if (dirty.value) { 29 | v = fn() 30 | dirty.value = false 31 | } 32 | track() 33 | return v 34 | }, 35 | set() {} 36 | } 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /uni_modules/tob-use/animation/useIntervalFn/README.md: -------------------------------------------------------------------------------- 1 | # useIntervalFn 2 | 3 | 使用间隔函数 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useIntervalFn } from '@/uni_modules/tob-use' 10 | 11 | // 第二个参数设置间隔,单位为毫秒,默认为 1000 毫秒 12 | const { pause, resume, isActive } = useIntervalFn(() => { 13 | // 你希望执行的函数 14 | }, 1000) 15 | 16 | isActive.value // 是否在执行中 17 | 18 | pause() // 暂停 19 | 20 | resume() // 恢复 21 | ``` 22 | 23 |
24 | 25 | ### Watch 选项 26 | 27 | ```js 28 | import { useInterval } from '@/uni_modules/tob-use' 29 | 30 | const { pause, resume, isActive } = useIntervalFn(200, { 31 | immediate: false, // 立即开启,默认为 true 32 | immediateCallback: false // 立即触发,不需要等待第一个间隔,默认为 false 33 | }) 34 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/component/tryOnMounted/README.md: -------------------------------------------------------------------------------- 1 | # tryOnMounted 2 | 3 | 在挂载后触发 4 | 5 | 比原生的 `onMount` 更安全 6 | 7 | ## Usage 8 | 9 | ```js 10 | import { tryOnMounted } from '@/uni_modules/tob-use' 11 | 12 | // 不在组件内,将直接调用 13 | tryOnMounted(() => { 14 | console.log("这将不做任何事情") 15 | }) 16 | ``` 17 | 18 | ```html 19 | 27 | ``` 28 | 29 |
30 | 31 | ### 非同步 32 | 33 | ```js 34 | import { tryOnMounted } from '@/uni_modules/tob-use' 35 | 36 | // 不在组件内,设置第二参数,将在 nextTick 调用 37 | tryOnMounted(() => { 38 | console.log("这将不做任何事情") 39 | }, false) 40 | ``` -------------------------------------------------------------------------------- /docs/api/watch/watchOnce.md: -------------------------------------------------------------------------------- 1 | # watchOnce 2 | 3 | 一次性监听 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { watchOnce } from '@/uni_modules/tob-use' 12 | 13 | const source = ref('foo') 14 | 15 | const stop = watchOnce(source, () => { 16 | // 只会触发一次 17 | console.log('source changed!') 18 | }) 19 | ``` 20 | 21 |
22 | 23 | ### Watch 选项 24 | 25 | ```js 26 | import { ref } from 'vue' 27 | import { watchOnce } from '@/uni_modules/tob-use' 28 | 29 | const source = ref('old') 30 | 31 | const changed = () => console.log('trigger!') 32 | 33 | watchOnce(source, changed, { 34 | deep: true, // 深度同步 35 | immediate: true, // 立即同步,默认为 false 36 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 37 | }) 38 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/watchOnce/README.md: -------------------------------------------------------------------------------- 1 | # watchOnce 2 | 3 | 一次性监听 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { watchOnce } from '@/uni_modules/tob-use' 12 | 13 | const source = ref('foo') 14 | 15 | const stop = watchOnce(source, () => { 16 | // 只会触发一次 17 | console.log('source changed!') 18 | }) 19 | ``` 20 | 21 |
22 | 23 | ### Watch 选项 24 | 25 | ```js 26 | import { ref } from 'vue' 27 | import { watchOnce } from '@/uni_modules/tob-use' 28 | 29 | const source = ref('old') 30 | 31 | const changed = () => console.log('trigger!') 32 | 33 | watchOnce(source, changed, { 34 | deep: true, // 深度同步 35 | immediate: true, // 立即同步,默认为 false 36 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 37 | }) 38 | ``` -------------------------------------------------------------------------------- /docs/api/watch/whenever.md: -------------------------------------------------------------------------------- 1 | # whenever 2 | 3 | 仅当为真时触发 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { whenever } from '@/uni_modules/tob-use' 12 | 13 | const source = ref(false) 14 | 15 | const changed = () => console.log("触发了") 16 | 17 | const stop = whenever(source, changed) 18 | 19 | source.value = true // 输出 '触发了' 20 | ``` 21 | 22 |
23 | 24 |
25 | 26 | ### Watch 选项 27 | 28 | ```js 29 | import { ref } from 'vue' 30 | import { whenever } from '@/uni_modules/tob-use' 31 | 32 | const source = ref('old') 33 | 34 | const changed = () => console.log('trigger!') 35 | 36 | whenever(source, changed, { 37 | deep: true, // 深度同步 38 | immediate: true, // 立即同步,默认为 false 39 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 40 | }) 41 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/component/computedInject/index.js: -------------------------------------------------------------------------------- 1 | import { computed, inject } from 'vue' 2 | import { isFunction } from '../../shared/is' 3 | 4 | /** 5 | * 计算属性型 inject 6 | */ 7 | export const computedInject = ( 8 | key, 9 | options, 10 | defaultSource, 11 | treatDefaultAsFactory 12 | ) => { 13 | let source = inject(key) 14 | 15 | if (defaultSource) { 16 | source = inject(key, defaultSource) 17 | } 18 | 19 | // 使得默认值作为工厂函数 20 | if (treatDefaultAsFactory) { 21 | source = inject( 22 | key, 23 | defaultSource, 24 | treatDefaultAsFactory 25 | ) 26 | } 27 | 28 | 29 | if (isFunction(options)) { 30 | return computed(ctx => options(source, ctx)) 31 | } else { 32 | return computed({ 33 | get: ctx => options.get(source, ctx), 34 | set: options.set 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/api/utilities/useDebounceFn.md: -------------------------------------------------------------------------------- 1 | # useDebounceFn 2 | 3 | 使用防抖函数 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useDebounceFn } from '@/uni_modules/tob-use' 10 | 11 | // 第二个参数设置防抖间隔,单位为毫秒,默认为 200 毫秒 12 | const debouncedFn = useDebounceFn(() => { 13 | console.log('执行了') 14 | }, 1000) 15 | 16 | // 调用多次 17 | debouncedFn() 18 | debouncedFn() 19 | debouncedFn() 20 | debouncedFn() 21 | 22 | setTimeout(() => { 23 | debouncedFn() // 最终只会执行一次,输出 '打印了' 24 | }, 600) 25 | ``` 26 | 27 |
28 | 29 | ### 最后期限 30 | 31 | ```ts 32 | import { ref } from 'vue' 33 | import { useDebounceFn } from '@/uni_modules/tob-use' 34 | 35 | // 第三个参数可接受配置 36 | const debouncedFn = useDebounceFn(() => { 37 | console.log('执行了') 38 | }, 1000, { 39 | maxWait: 3000 // 最后期限,不管是否在防抖中,一律触发 40 | }) 41 | ``` -------------------------------------------------------------------------------- /docs/api/watch/watchAtMost.md: -------------------------------------------------------------------------------- 1 | # watchAtMost 2 | 3 | 限制次数型监听 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { watchAtMost } from '@/uni_modules/tob-use' 10 | 11 | const source = ref(1) 12 | 13 | const changed = () => console.log('trigger!') 14 | 15 | const stop = watchAtMost( 16 | source, 17 | changed, 18 | { 19 | count: 3, // 最多触发 3 次 20 | } 21 | ) 22 | ``` 23 | 24 |
25 | 26 | ### Watch 选项 27 | 28 | ```js 29 | import { ref } from 'vue' 30 | import { watchAtMost } from '@/uni_modules/tob-use' 31 | 32 | const source = ref('old') 33 | 34 | const changed = () => console.log('trigger!') 35 | 36 | watchAtMost(source, changed, { 37 | deep: true, // 深度同步 38 | immediate: true, // 立即同步,默认为 false 39 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 40 | }) 41 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/whenever/README.md: -------------------------------------------------------------------------------- 1 | # whenever 2 | 3 | 仅当为真时触发 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { whenever } from '@/uni_modules/tob-use' 12 | 13 | const source = ref(false) 14 | 15 | const changed = () => console.log("触发了") 16 | 17 | const stop = whenever(source, changed) 18 | 19 | source.value = true // 输出 '触发了' 20 | ``` 21 | 22 |
23 | 24 |
25 | 26 | ### Watch 选项 27 | 28 | ```js 29 | import { ref } from 'vue' 30 | import { whenever } from '@/uni_modules/tob-use' 31 | 32 | const source = ref('old') 33 | 34 | const changed = () => console.log('trigger!') 35 | 36 | whenever(source, changed, { 37 | deep: true, // 深度同步 38 | immediate: true, // 立即同步,默认为 false 39 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 40 | }) 41 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/reactifyObject/index.js: -------------------------------------------------------------------------------- 1 | import { reactify } from '../reactify' 2 | import { isFunction } from '../../shared/is' 3 | 4 | /** 5 | * 对象的响应式转换 6 | */ 7 | export const reactifyObject = (obj, optionsOrKeys = {}) => { 8 | let keys = [] 9 | if (Array.isArray(optionsOrKeys)) { 10 | keys = optionsOrKeys 11 | } else { 12 | const { includeOwnProperties = true } = optionsOrKeys 13 | 14 | keys.push(...Object.keys(obj)) 15 | if (includeOwnProperties) { 16 | keys.push(...Object.getOwnPropertyNames(obj)) 17 | } 18 | } 19 | 20 | return Object.fromEntries( 21 | keys.map(key => { 22 | const value = obj[key] 23 | return [ 24 | key, 25 | // 将对象上所有的函数转换为解除 ref 参数的函数 26 | isFunction(value) 27 | ? reactify(value.bind(obj)) 28 | : value 29 | ] 30 | }) 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useDebounceFn/README.md: -------------------------------------------------------------------------------- 1 | # useDebounceFn 2 | 3 | 使用防抖函数 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useDebounceFn } from '@/uni_modules/tob-use' 10 | 11 | // 第二个参数设置防抖间隔,单位为毫秒,默认为 200 毫秒 12 | const debouncedFn = useDebounceFn(() => { 13 | console.log('执行了') 14 | }, 1000) 15 | 16 | // 调用多次 17 | debouncedFn() 18 | debouncedFn() 19 | debouncedFn() 20 | debouncedFn() 21 | 22 | setTimeout(() => { 23 | debouncedFn() // 最终只会执行一次,输出 '打印了' 24 | }, 600) 25 | ``` 26 | 27 |
28 | 29 | ### 最后期限 30 | 31 | ```ts 32 | import { ref } from 'vue' 33 | import { useDebounceFn } from '@/uni_modules/tob-use' 34 | 35 | // 第三个参数可接受配置 36 | const debouncedFn = useDebounceFn(() => { 37 | console.log('执行了') 38 | }, 1000, { 39 | maxWait: 3000 // 最后期限,不管是否在防抖中,一律触发 40 | }) 41 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/watchAtMost/README.md: -------------------------------------------------------------------------------- 1 | # watchAtMost 2 | 3 | 限制次数型监听 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { watchAtMost } from '@/uni_modules/tob-use' 10 | 11 | const source = ref(1) 12 | 13 | const changed = () => console.log('trigger!') 14 | 15 | const stop = watchAtMost( 16 | source, 17 | changed, 18 | { 19 | count: 3, // 最多触发 3 次 20 | } 21 | ) 22 | ``` 23 | 24 |
25 | 26 | ### Watch 选项 27 | 28 | ```js 29 | import { ref } from 'vue' 30 | import { watchAtMost } from '@/uni_modules/tob-use' 31 | 32 | const source = ref('old') 33 | 34 | const changed = () => console.log('trigger!') 35 | 36 | watchAtMost(source, changed, { 37 | deep: true, // 深度同步 38 | immediate: true, // 立即同步,默认为 false 39 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 40 | }) 41 | ``` -------------------------------------------------------------------------------- /docs/.vuepress/components/eagerComputed2.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/toRefs/index.js: -------------------------------------------------------------------------------- 1 | import { toRefs as _toRefs, customRef, isRef } from 'vue' 2 | 3 | /** 4 | * 扩展之后的 toRefs,允许接受对象型的 ref 5 | */ 6 | export const toRefs = objectRef => { 7 | if (!isRef(objectRef)) return _toRefs(objectRef) 8 | 9 | const result = Array.isArray(objectRef.value) 10 | ? new Array(objectRef.value.length) 11 | : {} 12 | 13 | for (const key in objectRef.value) { 14 | result[key] = customRef(() => ({ 15 | get() { 16 | return objectRef.value[key] 17 | }, 18 | set(v) { 19 | if (Array.isArray(objectRef.value)) { 20 | const copy = [...objectRef.value] 21 | copy[key] = v 22 | objectRef.value = copy 23 | } else { 24 | objectRef.value = { 25 | ...objectRef.value, 26 | [key]: v 27 | } 28 | } 29 | } 30 | })) 31 | } 32 | return result 33 | } 34 | -------------------------------------------------------------------------------- /docs/api/component/tryOnScopeDispose.md: -------------------------------------------------------------------------------- 1 | # tryOnScopeDispose 2 | 3 | 尝试获取 `effect` 区域内的副作用 4 | 5 | 这是一个较为底层的 `api`,需要你对 `effect` 作用域有非常好的理解。 6 | 7 | 大多数情况下,它相当于 [tryOnUnmounted](/api/component/tryOnUnmounted) 8 | 9 | ## Usage 10 | 11 | ```js 12 | import { tryOnScopeDispose } from '@/uni_modules/tob-use' 13 | 14 | // 不在组件内,将不会注册回调,并返回 false 15 | const result = tryOnScopeDispose(() => { 16 | console.log("这将不做任何事情") 17 | }) 18 | 19 | result // false 20 | ``` 21 | 22 | ```html 23 | 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/tryOnScopeDispose/README.md: -------------------------------------------------------------------------------- 1 | # tryOnScopeDispose 2 | 3 | 尝试获取 `effect` 区域内的副作用 4 | 5 | 这是一个较为底层的 `api`,需要你对 `effect` 作用域有非常好的理解。 6 | 7 | 大多数情况下,它相当于 [tryOnUnmounted](/api/component/tryOnUnmounted) 8 | 9 | ## Usage 10 | 11 | ```js 12 | import { tryOnScopeDispose } from '@/uni_modules/tob-use' 13 | 14 | // 不在组件内,将不会注册回调,并返回 false 15 | const result = tryOnScopeDispose(() => { 16 | console.log("这将不做任何事情") 17 | }) 18 | 19 | result // false 20 | ``` 21 | 22 | ```html 23 | 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /uni_modules/tob-less/index.js: -------------------------------------------------------------------------------- 1 | const appTheme = uni.getStorageSync('AppTheme') || '' 2 | 3 | // #ifdef VUE2 4 | import Vue from 'vue' 5 | const shared = Vue.observable({ appTheme }) 6 | // #endif 7 | 8 | // #ifdef VUE3 9 | import { reactive } from 'vue' 10 | const shared = reactive({ appTheme }) 11 | // #endif 12 | 13 | export default (V, options = {}) => { 14 | const { initAppTheme = '' } = options 15 | 16 | shared.appTheme = shared.appTheme 17 | ? shared.appTheme 18 | : initAppTheme 19 | 20 | V.mixin({ 21 | computed: { 22 | // app主题 23 | AppTheme() { 24 | const { appTheme } = shared 25 | const AppTheme = appTheme ? `theme-${appTheme}` : '' 26 | uni.setStorageSync('AppTheme', AppTheme) 27 | return AppTheme 28 | } 29 | }, 30 | methods: { 31 | // 切换app主题 32 | ToggleAppTheme(t) { 33 | shared.appTheme = t 34 | } 35 | } 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /docs/api/utilities/useDebounce.md: -------------------------------------------------------------------------------- 1 | # useDebounce 2 | 3 | 使用防抖 ref 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { useDebounce } from '@/uni_modules/tob-use' 12 | 13 | const input = ref('foo') 14 | 15 | // 第二个参数设置防抖间隔,单位为毫秒,默认为 200 毫秒 16 | const debounced = useDebounce(input, 1000) 17 | 18 | // 频繁的更新 19 | input.value = 'jack' 20 | input.value = 'tom' 21 | input.value = 'bar' 22 | 23 | // 仍然是 foo 24 | console.log(debounced.value) 25 | 26 | setTimeout(() => { 27 | // 只有最有一个生效,bar 28 | console.log(debounced.value) 29 | }, 2000) 30 | ``` 31 | 32 |
33 | 34 | ### 最后期限 35 | 36 | ```ts 37 | import { ref } from 'vue' 38 | import { useDebounce } from '@/uni_modules/tob-use' 39 | 40 | const input = ref('foo') 41 | 42 | // 第三个参数可接受配置 43 | const debounced = useDebounce(input, 1000, { 44 | maxWait: 3000 // 最后期限,不管是否在防抖中,一律触发 45 | }) 46 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useDebounce/README.md: -------------------------------------------------------------------------------- 1 | # useDebounce 2 | 3 | 使用防抖 ref 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { useDebounce } from '@/uni_modules/tob-use' 12 | 13 | const input = ref('foo') 14 | 15 | // 第二个参数设置防抖间隔,单位为毫秒,默认为 200 毫秒 16 | const debounced = useDebounce(input, 1000) 17 | 18 | // 频繁的更新 19 | input.value = 'jack' 20 | input.value = 'tom' 21 | input.value = 'bar' 22 | 23 | // 仍然是 foo 24 | console.log(debounced.value) 25 | 26 | setTimeout(() => { 27 | // 只有最有一个生效,bar 28 | console.log(debounced.value) 29 | }, 2000) 30 | ``` 31 | 32 |
33 | 34 | ### 最后期限 35 | 36 | ```ts 37 | import { ref } from 'vue' 38 | import { useDebounce } from '@/uni_modules/tob-use' 39 | 40 | const input = ref('foo') 41 | 42 | // 第三个参数可接受配置 43 | const debounced = useDebounce(input, 1000, { 44 | maxWait: 3000 // 最后期限,不管是否在防抖中,一律触发 45 | }) 46 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/useThrottle.md: -------------------------------------------------------------------------------- 1 | # useThrottle 2 | 3 | 使用节流 ref 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { useThrottle } from '@/uni_modules/tob-use' 12 | 13 | const input = ref('') 14 | 15 | // 第二个参数设置节流间隔,单位为毫秒,默认为 200 毫秒 16 | const throttled = useThrottle(input, 1000) 17 | ``` 18 | 19 |
20 | 21 | ### Trailing 22 | 23 | 如果您不想让尾随更改,即确保时间到达后触发,可以设置第三个参数为 `false`,默认为 `true` 24 | 25 | ```ts 26 | import { ref } from 'vue' 27 | import { useThrottle } from '@/uni_modules/tob-use' 28 | 29 | const input = ref('') 30 | const throttled = useThrottle(input, 1000, false) 31 | ``` 32 | 33 |
34 | 35 | ### Leading 36 | 37 | 如果你不想一上来就执行,可以设置第四个参数为 `false`,默认为 `true` 38 | 39 | ```ts 40 | import { ref } from 'vue' 41 | import { useThrottle } from '@/uni_modules/tob-use' 42 | 43 | const input = ref('') 44 | const throttled = useThrottle(input, 1000, true, false) 45 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/toReactive/index.js: -------------------------------------------------------------------------------- 1 | import { isRef, reactive } from 'vue' 2 | 3 | /** 4 | * 转化为 Reactive 5 | */ 6 | export const toReactive = objectRef => { 7 | if (!isRef(objectRef)) { 8 | return reactive(objectRef) 9 | } 10 | 11 | const proxy = new Proxy( 12 | {}, 13 | { 14 | get(_, p, receiver) { 15 | return Reflect.get(objectRef.value, p, receiver) 16 | }, 17 | set(_, p, value) { 18 | objectRef.value[p] = value 19 | return true 20 | }, 21 | deleteProperty(_, p) { 22 | return Reflect.deleteProperty(objectRef.value, p) 23 | }, 24 | has(_, p) { 25 | return Reflect.has(objectRef.value, p) 26 | }, 27 | ownKeys() { 28 | return Object.keys(objectRef.value) 29 | }, 30 | getOwnPropertyDescriptor() { 31 | return { 32 | enumerable: true, 33 | configurable: true 34 | } 35 | } 36 | } 37 | ) 38 | 39 | return reactive(proxy) 40 | } 41 | -------------------------------------------------------------------------------- /docs/api/utilities/useCycleList.md: -------------------------------------------------------------------------------- 1 | # useCycleList 2 | 3 | 环表 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useCycleList } from '@/uni_modules/tob-use' 11 | 12 | const { state, next, prev, index } = useCycleList([ 13 | 'Dog', 14 | 'Cat', 15 | 'Lizard', 16 | ]) 17 | 18 | state.value // 'Dog' 19 | index.value // 0 20 | 21 | prev() // 上一个 22 | state.value // 'Lizard' 23 | index.value // 2 24 | 25 | next() // 下一个 26 | state.value // 'Dog' 27 | index.value // 0 28 | 29 | next(2) // 下两个 30 | state.value // 'Lizard' 31 | 32 | prev(2) // 上两个 33 | state.value // 'Dog' 34 | ``` 35 | 36 |
37 | 38 | ### 初始值 39 | 40 | ```ts 41 | import { useCycleList } from '@/uni_modules/tob-use' 42 | 43 | const { state, next, prev } = useCycleList([ 44 | 'Dog', 45 | 'Cat', 46 | 'Lizard', 47 | ], { initialValue: 'init' }) 48 | 49 | state.value // 'init' 50 | 51 | next() // 下一个 52 | 53 | state.value // 'Dog' 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/api/watch/watchWithFilter.md: -------------------------------------------------------------------------------- 1 | # watchWithFilter 2 | 3 | 带过滤器的监听 4 | 5 | 这是一个较为底层的 `api`,大多数情况你不需要它,除非你要进行特别的自定义 6 | ## Usage 7 | 8 | ```js 9 | import { ref } from 'vue' 10 | import { watchWithFilter } from '@/uni_modules/tob-use' 11 | 12 | const source = ref('foo') 13 | 14 | const changed = () => console.log('changed!') 15 | 16 | watchWithFilter( 17 | source, 18 | changed, 19 | { 20 | eventFilter(changedHandle) { 21 | changedHandle() // 就是上边的 changed 22 | } 23 | } 24 | ) 25 | ``` 26 | 27 |
28 | 29 | ### Watch 选项 30 | 31 | ```js 32 | import { ref } from 'vue' 33 | import { watchOnce } from '@/uni_modules/tob-use' 34 | 35 | const source = ref('old') 36 | 37 | const changed = () => console.log('trigger!') 38 | 39 | watchWithFilter(source, changed, { 40 | deep: true, // 深度同步 41 | immediate: true, // 立即同步,默认为 false 42 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 43 | }) 44 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useThrottle/README.md: -------------------------------------------------------------------------------- 1 | # useThrottle 2 | 3 | 使用节流 ref 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { useThrottle } from '@/uni_modules/tob-use' 12 | 13 | const input = ref('') 14 | 15 | // 第二个参数设置节流间隔,单位为毫秒,默认为 200 毫秒 16 | const throttled = useThrottle(input, 1000) 17 | ``` 18 | 19 |
20 | 21 | ### Trailing 22 | 23 | 如果您不想让尾随更改,即确保时间到达后触发,可以设置第三个参数为 `false`,默认为 `true` 24 | 25 | ```ts 26 | import { ref } from 'vue' 27 | import { useThrottle } from '@/uni_modules/tob-use' 28 | 29 | const input = ref('') 30 | const throttled = useThrottle(input, 1000, false) 31 | ``` 32 | 33 |
34 | 35 | ### Leading 36 | 37 | 如果你不想一上来就执行,可以设置第四个参数为 `false`,默认为 `true` 38 | 39 | ```ts 40 | import { ref } from 'vue' 41 | import { useThrottle } from '@/uni_modules/tob-use' 42 | 43 | const input = ref('') 44 | const throttled = useThrottle(input, 1000, true, false) 45 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/watchWithFilter/README.md: -------------------------------------------------------------------------------- 1 | # watchWithFilter 2 | 3 | 带过滤器的监听 4 | 5 | 这是一个较为底层的 `api`,大多数情况你不需要它,除非你要进行特别的自定义 6 | ## Usage 7 | 8 | ```js 9 | import { ref } from 'vue' 10 | import { watchWithFilter } from '@/uni_modules/tob-use' 11 | 12 | const source = ref('foo') 13 | 14 | const changed = () => console.log('changed!') 15 | 16 | watchWithFilter( 17 | source, 18 | changed, 19 | { 20 | eventFilter(changedHandle) { 21 | changedHandle() // 就是上边的 changed 22 | } 23 | } 24 | ) 25 | ``` 26 | 27 |
28 | 29 | ### Watch 选项 30 | 31 | ```js 32 | import { ref } from 'vue' 33 | import { watchOnce } from '@/uni_modules/tob-use' 34 | 35 | const source = ref('old') 36 | 37 | const changed = () => console.log('trigger!') 38 | 39 | watchWithFilter(source, changed, { 40 | deep: true, // 深度同步 41 | immediate: true, // 立即同步,默认为 false 42 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 43 | }) 44 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useCycleList/README.md: -------------------------------------------------------------------------------- 1 | # useCycleList 2 | 3 | 环表 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useCycleList } from '@/uni_modules/tob-use' 11 | 12 | const { state, next, prev, index } = useCycleList([ 13 | 'Dog', 14 | 'Cat', 15 | 'Lizard', 16 | ]) 17 | 18 | state.value // 'Dog' 19 | index.value // 0 20 | 21 | prev() // 上一个 22 | state.value // 'Lizard' 23 | index.value // 2 24 | 25 | next() // 下一个 26 | state.value // 'Dog' 27 | index.value // 0 28 | 29 | next(2) // 下两个 30 | state.value // 'Lizard' 31 | 32 | prev(2) // 上两个 33 | state.value // 'Dog' 34 | ``` 35 | 36 |
37 | 38 | ### 初始值 39 | 40 | ```ts 41 | import { useCycleList } from '@/uni_modules/tob-use' 42 | 43 | const { state, next, prev } = useCycleList([ 44 | 'Dog', 45 | 'Cat', 46 | 'Lizard', 47 | ], { initialValue: 'init' }) 48 | 49 | state.value // 'init' 50 | 51 | next() // 下一个 52 | 53 | state.value // 'Dog' 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/api/utilities/useThrottleFn.md: -------------------------------------------------------------------------------- 1 | # useThrottleFn 2 | 3 | 使用节流函数 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useThrottleFn } from '@/uni_modules/tob-use' 10 | 11 | // 第二个参数设置节流间隔,单位为毫秒,默认为 200 毫秒 12 | const throttledFn = useThrottleFn(() => { 13 | console.log('执行了') 14 | }, 1000) 15 | 16 | throttledFn() 17 | ``` 18 | 19 |
20 | 21 | ### Trailing 22 | 23 | 如果您不想让尾随更改,即确保时间到达后触发,可以设置第三个参数为 `false`,默认为 `true` 24 | 25 | ```ts 26 | import { ref } from 'vue' 27 | import { useThrottle } from '@/uni_modules/tob-use' 28 | 29 | const throttledFn = useThrottleFn(() => { 30 | console.log('执行了') 31 | }, 1000, false) 32 | ``` 33 | 34 |
35 | 36 | ### Leading 37 | 38 | 如果你不想一上来就执行,可以设置第四个参数为 `false`,默认为 `true` 39 | 40 | ```ts 41 | import { ref } from 'vue' 42 | import { useThrottle } from '@/uni_modules/tob-use' 43 | 44 | const throttledFn = useThrottleFn(() => { 45 | console.log('执行了') 46 | }, 1000, true, false) 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/api/watch/throttledWatch.md: -------------------------------------------------------------------------------- 1 | # throttledWatch 2 | 3 | 节流型监听 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { throttledWatch } from '@/uni_modules/tob-use' 10 | 11 | const source = ref('old') 12 | 13 | const watchOptions = { 14 | throttle: 500 // 节流间隔,单位为毫秒,默认为 0 15 | } 16 | 17 | const watchChanged = (newValue, oldValue) => console.log(oldValue, '->', newValue) 18 | 19 | const stop = throttledWatch(source, watchChanged, watchOptions) 20 | 21 | source.value = 'new' 22 | ``` 23 | 24 |
25 | 26 | ### Watch 选项 27 | 28 | ```ts 29 | import { ref } from 'vue' 30 | import { throttledWatch } from '@/uni_modules/tob-use' 31 | 32 | const source = ref('old') 33 | 34 | const watchChanged = (newValue, oldValue) => console.log(oldValue, '->', newValue) 35 | 36 | throttledWatch(source, watchChanged, { 37 | deep: true, // 深度同步 38 | immediate: true, // 立即同步,默认为 false 39 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 40 | }) 41 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/throttledWatch/README.md: -------------------------------------------------------------------------------- 1 | # throttledWatch 2 | 3 | 节流型监听 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { throttledWatch } from '@/uni_modules/tob-use' 10 | 11 | const source = ref('old') 12 | 13 | const watchOptions = { 14 | throttle: 500 // 节流间隔,单位为毫秒,默认为 0 15 | } 16 | 17 | const watchChanged = (newValue, oldValue) => console.log(oldValue, '->', newValue) 18 | 19 | const stop = throttledWatch(source, watchChanged, watchOptions) 20 | 21 | source.value = 'new' 22 | ``` 23 | 24 |
25 | 26 | ### Watch 选项 27 | 28 | ```ts 29 | import { ref } from 'vue' 30 | import { throttledWatch } from '@/uni_modules/tob-use' 31 | 32 | const source = ref('old') 33 | 34 | const watchChanged = (newValue, oldValue) => console.log(oldValue, '->', newValue) 35 | 36 | throttledWatch(source, watchChanged, { 37 | deep: true, // 深度同步 38 | immediate: true, // 立即同步,默认为 false 39 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 40 | }) 41 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useThrottleFn/README.md: -------------------------------------------------------------------------------- 1 | # useThrottleFn 2 | 3 | 使用节流函数 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { useThrottleFn } from '@/uni_modules/tob-use' 10 | 11 | // 第二个参数设置节流间隔,单位为毫秒,默认为 200 毫秒 12 | const throttledFn = useThrottleFn(() => { 13 | console.log('执行了') 14 | }, 1000) 15 | 16 | throttledFn() 17 | ``` 18 | 19 |
20 | 21 | ### Trailing 22 | 23 | 如果您不想让尾随更改,即确保时间到达后触发,可以设置第三个参数为 `false`,默认为 `true` 24 | 25 | ```ts 26 | import { ref } from 'vue' 27 | import { useThrottle } from '@/uni_modules/tob-use' 28 | 29 | const throttledFn = useThrottleFn(() => { 30 | console.log('执行了') 31 | }, 1000, false) 32 | ``` 33 | 34 |
35 | 36 | ### Leading 37 | 38 | 如果你不想一上来就执行,可以设置第四个参数为 `false`,默认为 `true` 39 | 40 | ```ts 41 | import { ref } from 'vue' 42 | import { useThrottle } from '@/uni_modules/tob-use' 43 | 44 | const throttledFn = useThrottleFn(() => { 45 | console.log('执行了') 46 | }, 1000, true, false) 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/api/watch/debouncedWatch.md: -------------------------------------------------------------------------------- 1 | # debouncedWatch 2 | 3 | 防抖监听 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { debouncedWatch } from '@/uni_modules/tob-use' 12 | 13 | const source = ref('old') 14 | 15 | const watchOptions = { 16 | debounce: 500 // 防抖间隔,单位为毫秒,默认为 0 17 | } 18 | 19 | const watchChanged = (newValue, oldValue) => console.log(oldValue, '->', newValue) 20 | 21 | const stop = debouncedWatch(source, watchChanged, watchOptions) 22 | 23 | source.value = 'new' // 500 毫秒后输出 old -> new 24 | ``` 25 | 26 |
27 | 28 | ### Watch 选项 29 | 30 | ```ts 31 | import { ref } from 'vue' 32 | import { debouncedWatch } from '@/uni_modules/tob-use' 33 | 34 | const source = ref('old') 35 | 36 | const watchChanged = (newValue, oldValue) => console.log(oldValue, '->', newValue) 37 | 38 | debouncedWatch(source, watchChanged, { 39 | deep: true, // 深度同步 40 | immediate: true, // 立即同步,默认为 false 41 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 42 | }) 43 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/debouncedWatch/README.md: -------------------------------------------------------------------------------- 1 | # debouncedWatch 2 | 3 | 防抖监听 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { debouncedWatch } from '@/uni_modules/tob-use' 12 | 13 | const source = ref('old') 14 | 15 | const watchOptions = { 16 | debounce: 500 // 防抖间隔,单位为毫秒,默认为 0 17 | } 18 | 19 | const watchChanged = (newValue, oldValue) => console.log(oldValue, '->', newValue) 20 | 21 | const stop = debouncedWatch(source, watchChanged, watchOptions) 22 | 23 | source.value = 'new' // 500 毫秒后输出 old -> new 24 | ``` 25 | 26 |
27 | 28 | ### Watch 选项 29 | 30 | ```ts 31 | import { ref } from 'vue' 32 | import { debouncedWatch } from '@/uni_modules/tob-use' 33 | 34 | const source = ref('old') 35 | 36 | const watchChanged = (newValue, oldValue) => console.log(oldValue, '->', newValue) 37 | 38 | debouncedWatch(source, watchChanged, { 39 | deep: true, // 深度同步 40 | immediate: true, // 立即同步,默认为 false 41 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 42 | }) 43 | ``` -------------------------------------------------------------------------------- /docs/.vuepress/components/useInterval2.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | -------------------------------------------------------------------------------- /uni_modules/tob-use/component/useVModel/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | ref, 3 | watch, 4 | computed, 5 | getCurrentInstance 6 | } from 'vue' 7 | 8 | /** 9 | * 使用 v-model 10 | */ 11 | export const useVModel = ( 12 | props, 13 | key = 'modelValue', 14 | emit, 15 | options = {} 16 | ) => { 17 | const { 18 | eventName, 19 | deep = false, 20 | passive = false 21 | } = options 22 | 23 | const vm = getCurrentInstance() 24 | 25 | // 获取 emit 26 | const _emit = emit || vm?.emit || vm?.$emit?.bind(vm) 27 | 28 | // 获取事件名 29 | let event = eventName || `update:${key}` 30 | 31 | if (passive) { 32 | const proxy = ref(props[key]) 33 | 34 | watch( 35 | () => props[key], 36 | v => (proxy.value = v) 37 | ) 38 | 39 | watch( 40 | proxy, 41 | v => { 42 | if (v !== props[key] || deep) _emit(event, v) 43 | }, 44 | { 45 | deep 46 | } 47 | ) 48 | 49 | return proxy 50 | } else { 51 | return computed({ 52 | get() { 53 | return props[key] 54 | }, 55 | set(value) { 56 | _emit(event, value) 57 | } 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /uni_modules/tob-use/animation/useTimeoutFn/index.js: -------------------------------------------------------------------------------- 1 | import { ref, unref } from 'vue' 2 | import { tryOnScopeDispose } from '../../component/tryOnScopeDispose' 3 | 4 | /** 5 | * 定时器方法 6 | */ 7 | export const useTimeoutFn = ( 8 | cb, 9 | interval, 10 | options = {} 11 | ) => { 12 | const { 13 | immediate = true // 立即开始 14 | } = options 15 | 16 | const isPending = ref(false) 17 | 18 | let timer = null 19 | 20 | // 清除定时器 21 | const clear = () => { 22 | if (timer) { 23 | clearTimeout(timer) 24 | timer = null 25 | } 26 | } 27 | 28 | // 暂停 29 | const stop = () => { 30 | isPending.value = false 31 | clear() 32 | } 33 | 34 | // 启动 35 | const start = (...args) => { 36 | clear() 37 | isPending.value = true 38 | timer = setTimeout(() => { 39 | isPending.value = false 40 | timer = null 41 | cb(...args) 42 | }, unref(interval)) 43 | } 44 | 45 | // 立即开始 46 | if (immediate) { 47 | isPending.value = true 48 | start() 49 | } 50 | 51 | tryOnScopeDispose(stop) 52 | 53 | return { 54 | isPending, 55 | start, 56 | stop 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/api/utilities/useLastChanged.md: -------------------------------------------------------------------------------- 1 | # useLastChanged 2 | 3 | 获取最后一次更新 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { useLastChanged } from '@/uni_modules/tob-use' 12 | 13 | const a = ref(0) 14 | 15 | const lastChanged = useLastChanged(a) 16 | 17 | a.value = 1 18 | 19 | lastChanged.value // 最后一次更新的时间 20 | ``` 21 | 22 | ### 初始值 23 | 24 | 大多数情况下你并不需要设置该项 👇 25 | 26 | ```js 27 | import { ref } from 'vue' 28 | import { useLastChanged } from '@/uni_modules/tob-use' 29 | 30 | const a = ref(0) 31 | 32 | const lastChanged = useLastChanged(a, { 33 | initialValue: +Date.now() 34 | }) 35 | 36 | lastChanged.value // 初始化时间 37 | 38 | a.value = 1 39 | 40 | lastChanged.value // 最后一次更新的时间 41 | ``` 42 | 43 |
44 | 45 | ### Watch 选项 46 | 47 | ```ts 48 | import { ref } from 'vue' 49 | import { useLastChanged } from '@/uni_modules/tob-use' 50 | 51 | const a = ref(0) 52 | 53 | const lastChanged = useLastChanged(a, { 54 | deep: true, // 深度同步 55 | immediate: true, // 立即同步,默认为 false 56 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 57 | }) 58 | 59 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/reactifyObject.md: -------------------------------------------------------------------------------- 1 | # reactifyObject 2 | 3 | 对象的响应式转换 4 | 5 | 大白话就是将对象上的方法参数去 `ref` 化 👇 6 | 7 | ## Usage 8 | 9 | ### 基础 10 | 11 | ```js 12 | import { ref } from 'vue' 13 | import { reactifyObject } from '@/uni_modules/tob-use' 14 | 15 | const a = ref(1) 16 | 17 | console.log(a.value) // 需要 .value 18 | 19 | const reactifiedConsole = reactifyObject(console) 20 | 21 | reactifiedConsole.log(a) // 不需要 .value 22 | ``` 23 | 24 |
25 | 26 | ### 包含自身属性 27 | 28 | 包含自身属性,包括不可枚举属性但不包括 `Symbol` 值作为名称的属性 29 | 30 | ```ts 31 | import { reactifyObject } from '@/uni_modules/tob-use' 32 | 33 | const reactifiedConsole = reactifyObject(console, { 34 | includeOwnProperties: true // 默认为 true 35 | }) 36 | ``` 37 | 38 |
39 | 40 | ### 限定范围 41 | 42 | 只允许限定范围内的方法被处理 43 | 44 | ```ts 45 | import { ref } from 'vue' 46 | import { reactifyObject } from '@/uni_modules/tob-use' 47 | 48 | // 只允许 log 被处理 49 | const reactifiedConsole = reactifyObject(console, ['log']) 50 | 51 | const a = ref(1) 52 | 53 | reactifiedConsole.log(a) // 不需要 .value 54 | 55 | reactifiedConsole.warn(a.value) // 需要 .value 56 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2022 markthree 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 8 | to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 13 | THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 15 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 16 | IN THE SOFTWARE. -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useLastChanged/README.md: -------------------------------------------------------------------------------- 1 | # useLastChanged 2 | 3 | 获取最后一次更新 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { useLastChanged } from '@/uni_modules/tob-use' 12 | 13 | const a = ref(0) 14 | 15 | const lastChanged = useLastChanged(a) 16 | 17 | a.value = 1 18 | 19 | lastChanged.value // 最后一次更新的时间 20 | ``` 21 | 22 | ### 初始值 23 | 24 | 大多数情况下你并不需要设置该项 👇 25 | 26 | ```js 27 | import { ref } from 'vue' 28 | import { useLastChanged } from '@/uni_modules/tob-use' 29 | 30 | const a = ref(0) 31 | 32 | const lastChanged = useLastChanged(a, { 33 | initialValue: +Date.now() 34 | }) 35 | 36 | lastChanged.value // 初始化时间 37 | 38 | a.value = 1 39 | 40 | lastChanged.value // 最后一次更新的时间 41 | ``` 42 | 43 |
44 | 45 | ### Watch 选项 46 | 47 | ```ts 48 | import { ref } from 'vue' 49 | import { useLastChanged } from '@/uni_modules/tob-use' 50 | 51 | const a = ref(0) 52 | 53 | const lastChanged = useLastChanged(a, { 54 | deep: true, // 深度同步 55 | immediate: true, // 立即同步,默认为 false 56 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 57 | }) 58 | 59 | ``` -------------------------------------------------------------------------------- /docs/.vuepress/components/eagerComputed3.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/reactifyObject/README.md: -------------------------------------------------------------------------------- 1 | # reactifyObject 2 | 3 | 对象的响应式转换 4 | 5 | 大白话就是将对象上的方法参数去 `ref` 化 👇 6 | 7 | ## Usage 8 | 9 | ### 基础 10 | 11 | ```js 12 | import { ref } from 'vue' 13 | import { reactifyObject } from '@/uni_modules/tob-use' 14 | 15 | const a = ref(1) 16 | 17 | console.log(a.value) // 需要 .value 18 | 19 | const reactifiedConsole = reactifyObject(console) 20 | 21 | reactifiedConsole.log(a) // 不需要 .value 22 | ``` 23 | 24 |
25 | 26 | ### 包含自身属性 27 | 28 | 包含自身属性,包括不可枚举属性但不包括 `Symbol` 值作为名称的属性 29 | 30 | ```ts 31 | import { reactifyObject } from '@/uni_modules/tob-use' 32 | 33 | const reactifiedConsole = reactifyObject(console, { 34 | includeOwnProperties: true // 默认为 true 35 | }) 36 | ``` 37 | 38 |
39 | 40 | ### 限定范围 41 | 42 | 只允许限定范围内的方法被处理 43 | 44 | ```ts 45 | import { ref } from 'vue' 46 | import { reactifyObject } from '@/uni_modules/tob-use' 47 | 48 | // 只允许 log 被处理 49 | const reactifiedConsole = reactifyObject(console, ['log']) 50 | 51 | const a = ref(1) 52 | 53 | reactifiedConsole.log(a) // 不需要 .value 54 | 55 | reactifiedConsole.warn(a.value) // 需要 .value 56 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/useCounter.md: -------------------------------------------------------------------------------- 1 | # useCounter 2 | 3 | 计数器 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useCounter } from '@/uni_modules/tob-use' 11 | 12 | const { count, inc, dec, set, reset } = useCounter() 13 | 14 | count.value // 0 15 | 16 | inc() // +1 17 | count.value // 1 18 | 19 | dec() // -1 20 | count.value // 0 21 | 22 | set(100) // 设置 23 | count.value // 100 24 | 25 | reset() // 重置为 0 26 | count.value // 0 27 | 28 | inc(100) // + 100 29 | count.value // 100 30 | 31 | dec(100) // - 100 32 | count.value // 0 33 | 34 | reset(200) // 重置为 200 35 | count.value // 200 36 | ``` 37 | 38 | ### 初始值 39 | 40 | ```ts 41 | import { useCounter } from '@/uni_modules/tob-use' 42 | 43 | const { count, inc, dec, set, reset } = useCounter(10) 44 | 45 | count.value // 10 46 | 47 | inc() // +1 48 | count.value // 11 49 | 50 | dec() // -1 51 | count.value // 10 52 | 53 | set(100) // 设置 54 | count.value // 100 55 | 56 | reset() // 重置为 10 57 | count.value // 10 58 | 59 | inc(100) // + 100 60 | count.value // 110 61 | 62 | dec(100) // - 100 63 | count.value // 10 64 | 65 | reset(200) // 重置为 200 66 | count.value // 200 67 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useCycleList/index.js: -------------------------------------------------------------------------------- 1 | import { computed, shallowRef } from 'vue' 2 | 3 | /** 4 | * 环表 5 | */ 6 | export const useCycleList = (list, options) => { 7 | // 有初始值则用初始值,无则使用第一个参数 8 | const state = shallowRef(options?.initialValue ?? list[0]) 9 | 10 | // 标识 11 | const index = computed({ 12 | get() { 13 | let index = options?.getIndexOf 14 | ? options.getIndexOf(state.value, list) 15 | : list.indexOf(state.value) 16 | 17 | if (index < 0) index = options?.fallbackIndex ?? 0 18 | 19 | return index 20 | }, 21 | set(v) { 22 | set(v) 23 | } 24 | }) 25 | 26 | // 设置 27 | function set(i) { 28 | const { length } = list 29 | const index = ((i % length) + length) % length 30 | const value = list[index] 31 | state.value = value 32 | return value 33 | } 34 | 35 | // 偏移 36 | function shift(delta = 1) { 37 | return set(index.value + delta) 38 | } 39 | 40 | // 下一个 41 | function next(n = 1) { 42 | return shift(n) 43 | } 44 | 45 | // 上一个 46 | function prev(n = 1) { 47 | return shift(-n) 48 | } 49 | 50 | return { 51 | state, 52 | index, 53 | next, 54 | prev 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useCounter/README.md: -------------------------------------------------------------------------------- 1 | # useCounter 2 | 3 | 计数器 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useCounter } from '@/uni_modules/tob-use' 11 | 12 | const { count, inc, dec, set, reset } = useCounter() 13 | 14 | count.value // 0 15 | 16 | inc() // +1 17 | count.value // 1 18 | 19 | dec() // -1 20 | count.value // 0 21 | 22 | set(100) // 设置 23 | count.value // 100 24 | 25 | reset() // 重置为 0 26 | count.value // 0 27 | 28 | inc(100) // + 100 29 | count.value // 100 30 | 31 | dec(100) // - 100 32 | count.value // 0 33 | 34 | reset(200) // 重置为 200 35 | count.value // 200 36 | ``` 37 | 38 | ### 初始值 39 | 40 | ```ts 41 | import { useCounter } from '@/uni_modules/tob-use' 42 | 43 | const { count, inc, dec, set, reset } = useCounter(10) 44 | 45 | count.value // 10 46 | 47 | inc() // +1 48 | count.value // 11 49 | 50 | dec() // -1 51 | count.value // 10 52 | 53 | set(100) // 设置 54 | count.value // 100 55 | 56 | reset() // 重置为 10 57 | count.value // 10 58 | 59 | inc(100) // + 100 60 | count.value // 110 61 | 62 | dec(100) // - 100 63 | count.value // 10 64 | 65 | reset(200) // 重置为 200 66 | count.value // 200 67 | ``` -------------------------------------------------------------------------------- /docs/api/animation/useInterval.md: -------------------------------------------------------------------------------- 1 | # useInterval 2 | 3 | 间隔 4 | 5 | ## Usage 6 | 7 | 8 | 9 | ```html 10 | 16 | 17 | 20 | ``` 21 | 22 | ### 可控制 23 | 24 | 25 | 26 | ```html 27 | 43 | 44 | 47 | ``` 48 | 49 |
50 | 51 | ### 立即执行 52 | 53 | ```js 54 | import { useInterval } from '@/uni_modules/tob-use' 55 | 56 | const counter = useInterval(200, { 57 | immediate: false // 立即开启,默认为 true 58 | }) 59 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/animation/useInterval/README.md: -------------------------------------------------------------------------------- 1 | # useInterval 2 | 3 | 间隔 4 | 5 | ## Usage 6 | 7 | 8 | 9 | ```html 10 | 16 | 17 | 20 | ``` 21 | 22 | ### 可控制 23 | 24 | 25 | 26 | ```html 27 | 43 | 44 | 47 | ``` 48 | 49 |
50 | 51 | ### 立即执行 52 | 53 | ```js 54 | import { useInterval } from '@/uni_modules/tob-use' 55 | 56 | const counter = useInterval(200, { 57 | immediate: false // 立即开启,默认为 true 58 | }) 59 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useConfirmDialog/index.js: -------------------------------------------------------------------------------- 1 | import { computed, ref } from 'vue' 2 | import { noop } from '../../shared/base' 3 | import { createEventHook } from '../createEventHook' 4 | 5 | /** 6 | * 确认对话框 7 | */ 8 | export const useConfirmDialog = (revealed = ref(false)) => { 9 | const confirmHook = createEventHook() 10 | const cancelHook = createEventHook() 11 | const revealHook = createEventHook() 12 | 13 | let _resolve = noop 14 | 15 | // 显示 16 | const reveal = data => { 17 | revealed.value = true 18 | revealHook.trigger(data) 19 | 20 | return new Promise(resolve => { 21 | _resolve = resolve 22 | }) 23 | } 24 | 25 | // 确认 26 | const confirm = data => { 27 | revealed.value = false 28 | confirmHook.trigger(data) 29 | 30 | _resolve({ data, isCanceled: false }) 31 | } 32 | 33 | // 取消 34 | const cancel = data => { 35 | revealed.value = false 36 | cancelHook.trigger(data) 37 | 38 | _resolve({ data, isCanceled: true }) 39 | } 40 | 41 | return { 42 | reveal, 43 | cancel, 44 | confirm, 45 | onReveal: revealHook.on, 46 | onCancel: cancelHook.on, 47 | onConfirm: confirmHook.on, 48 | isRevealed: computed(() => revealed.value) // 是否显示 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useEventBus/index.js: -------------------------------------------------------------------------------- 1 | import { getCurrentScope } from 'vue' 2 | import { events } from '../../shared/base' 3 | 4 | /** 5 | * 使用事件 bus 6 | */ 7 | export const useEventBus = key => { 8 | const scope = getCurrentScope() 9 | 10 | // 注册 11 | const on = listener => { 12 | const listeners = events.get(key) || [] 13 | listeners.push(listener) 14 | events.set(key, listeners) 15 | 16 | const _off = () => off(listener) 17 | // auto unsubscribe when scope get disposed 18 | scope?.cleanups.push(_off) 19 | return _off 20 | } 21 | 22 | // 注册一次 23 | const once = listener => { 24 | function _listener(...args) { 25 | off(_listener) 26 | listener(...args) 27 | } 28 | return on(_listener) 29 | } 30 | 31 | // 卸载 32 | const off = listener => { 33 | const listeners = events.get(key) 34 | if (!listeners) return 35 | 36 | const index = listeners.indexOf(listener) 37 | if (index > -1) listeners.splice(index, 1) 38 | if (!listeners.length) events.delete(key) 39 | } 40 | 41 | // 重置 42 | const reset = () => events.delete(key) 43 | 44 | // 触发 45 | const emit = event => { 46 | return events.get(key)?.forEach(v => v(event)) 47 | } 48 | 49 | return { on, once, off, emit, reset } 50 | } 51 | -------------------------------------------------------------------------------- /docs/api/watch/pausableWatch.md: -------------------------------------------------------------------------------- 1 | # pausableWatch 2 | 3 | 可暂停的监听 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { pausableWatch } from '@/uni_modules/tob-use' 10 | 11 | const source = ref('foo') 12 | 13 | const { stop, pause, resume, isActive } = pausableWatch( 14 | source, 15 | (v) => console.log(v), 16 | ) 17 | 18 | const run = async () => { 19 | isActive.value // 监听中,true 20 | source.value = 'bar' 21 | await nextTick() // 输出 bar 22 | 23 | pause() // 暂停 24 | isActive.value // 监听中,false 25 | source.value = 'foobar' 26 | await nextTick() // 啥事都不会发生 27 | 28 | resume() // 恢复 29 | isActive.value // 监听中,true 30 | source.value = 'hello' 31 | await nextTick() // 输出 hello 32 | 33 | stop() // 完全停止监听 34 | } 35 | 36 | run() 37 | ``` 38 | 39 |
40 | 41 | ### Watch 选项 42 | 43 | ```ts 44 | import { ref } from 'vue' 45 | import { pausableWatch } from '@/uni_modules/tob-use' 46 | 47 | const source = ref('foo') 48 | const callback = (v) => console.log(`更新 ${v}!`) 49 | 50 | pausableWatch( 51 | source, 52 | callback, 53 | { 54 | deep: true, // 深度同步 55 | immediate: true, // 立即同步,默认为 false 56 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 57 | } 58 | ) 59 | ``` -------------------------------------------------------------------------------- /docs/api/component/useVModels.md: -------------------------------------------------------------------------------- 1 | # useVModels 2 | 3 | 使用多个的 v-model 4 | 5 | 大多数情况下,可能你只需要使用 [useVModel](/api/component/useVModel) 6 | 7 | ## Usage 8 | 9 | ### 基础 10 | 11 | ```html 12 | 33 | ``` 34 | 35 |
36 | 37 | ### 选项 38 | 39 | ```html 40 | 59 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/pausableWatch/README.md: -------------------------------------------------------------------------------- 1 | # pausableWatch 2 | 3 | 可暂停的监听 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { ref } from 'vue' 9 | import { pausableWatch } from '@/uni_modules/tob-use' 10 | 11 | const source = ref('foo') 12 | 13 | const { stop, pause, resume, isActive } = pausableWatch( 14 | source, 15 | (v) => console.log(v), 16 | ) 17 | 18 | const run = async () => { 19 | isActive.value // 监听中,true 20 | source.value = 'bar' 21 | await nextTick() // 输出 bar 22 | 23 | pause() // 暂停 24 | isActive.value // 监听中,false 25 | source.value = 'foobar' 26 | await nextTick() // 啥事都不会发生 27 | 28 | resume() // 恢复 29 | isActive.value // 监听中,true 30 | source.value = 'hello' 31 | await nextTick() // 输出 hello 32 | 33 | stop() // 完全停止监听 34 | } 35 | 36 | run() 37 | ``` 38 | 39 |
40 | 41 | ### Watch 选项 42 | 43 | ```ts 44 | import { ref } from 'vue' 45 | import { pausableWatch } from '@/uni_modules/tob-use' 46 | 47 | const source = ref('foo') 48 | const callback = (v) => console.log(`更新 ${v}!`) 49 | 50 | pausableWatch( 51 | source, 52 | callback, 53 | { 54 | deep: true, // 深度同步 55 | immediate: true, // 立即同步,默认为 false 56 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 57 | } 58 | ) 59 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/component/useVModels/README.md: -------------------------------------------------------------------------------- 1 | # useVModels 2 | 3 | 使用多个的 v-model 4 | 5 | 大多数情况下,可能你只需要使用 [useVModel](/api/component/useVModel) 6 | 7 | ## Usage 8 | 9 | ### 基础 10 | 11 | ```html 12 | 33 | ``` 34 | 35 |
36 | 37 | ### 选项 38 | 39 | ```html 40 | 59 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useMemoize/index.js: -------------------------------------------------------------------------------- 1 | import { reactive } from 'vue' 2 | 3 | /** 4 | * 使用备份 5 | */ 6 | export const useMemoize = (resolver, options) => { 7 | const initCache = () => { 8 | if (options?.cache) { 9 | return reactive(options.cache) 10 | } 11 | 12 | return reactive(new Map()) 13 | } 14 | const cache = initCache() 15 | 16 | const generateKey = (...args) => 17 | options?.getKey 18 | ? options.getKey(...args) 19 | : JSON.stringify(args) 20 | 21 | // 加载数据(私有) 22 | const _loadData = (key, ...args) => { 23 | cache.set(key, resolver(...args)) 24 | return cache.get(key) 25 | } 26 | // 加载数据 27 | const loadData = (...args) => 28 | _loadData(generateKey(...args), ...args) 29 | 30 | // 删除数据 31 | const deleteData = (...args) => { 32 | cache.delete(generateKey(...args)) 33 | } 34 | 35 | // 清空数据 36 | const clearData = () => cache.clear() 37 | 38 | // 记忆函数 (有缓存时走缓存) 39 | const memoized = (...args) => { 40 | const key = generateKey(...args) 41 | if (cache.has(key)) return cache.get(key) 42 | return _loadData(key, ...args) 43 | } 44 | memoized.load = loadData 45 | memoized.delete = deleteData 46 | memoized.clear = clearData 47 | memoized.generateKey = generateKey 48 | memoized.cache = cache 49 | 50 | return memoized 51 | } 52 | -------------------------------------------------------------------------------- /docs/api/utilities/reactify.md: -------------------------------------------------------------------------------- 1 | # reactify 2 | 3 | 将普通函数转换为响应式函数 4 | 5 | 大白话就是对函数参数去 `ref` 化 👇 6 | 7 | ## Usage 8 | 9 | ### 基础 10 | 11 | ```ts 12 | import { ref } from 'vue' 13 | import { reactify } from '@/uni_modules/tob-use' 14 | 15 | const count = ref(1) 16 | 17 | // reactify 注册一个回调,返回一个工厂函数 18 | const createDouble = reactify(v => { 19 | return v * 2 // 这里的 v 就是后边 count.value 20 | }) 21 | 22 | // 返回一个计算属性 23 | const double = createDouble(count) 24 | 25 | console.log(double.value) // 2 26 | 27 | count.value = 2 28 | 29 | console.log(double.value) // 4 30 | ``` 31 | 32 |
33 | 34 | ### 对比 35 | 36 | 对比原生计算属性 👇 37 | 38 | ```ts 39 | import { ref, computed } from 'vue' 40 | 41 | const a = ref(1) 42 | const b = 2 43 | const c = ref(3) 44 | 45 | const sum = computed(() => { 46 | // 需要考虑 .value 47 | return a.value + b + c.value 48 | }) 49 | 50 | console.log(sum.value) // 6 51 | ``` 52 | 53 | 用 `reactify` 👇 54 | 55 | ```ts 56 | import { ref } from 'vue' 57 | import { reactify } from '@/uni_modules/tob-use' 58 | 59 | const createSum = reactify((a, b, c) => { 60 | // 不需要考虑 .value 61 | return a + b + c 62 | }) 63 | 64 | const a = ref(1) 65 | const b = 2 66 | const c = ref(3) 67 | 68 | const sum = createSum(a, b, c) 69 | 70 | console.log(sum.value) 71 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/useAsyncQueue.md: -------------------------------------------------------------------------------- 1 | # useAsyncQueue 2 | 3 | 使用异步队列 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useAsyncQueue } from '@/uni_modules/tob-use' 11 | 12 | const p1 = () => { 13 | return new Promise((resolve) => { 14 | setTimeout(() => { 15 | resolve(1000) 16 | }, 10) 17 | }) 18 | } 19 | 20 | // result 是上一个任务的结果 21 | const p2 = result => { 22 | return new Promise((resolve) => { 23 | setTimeout(() => { 24 | resolve(1000 + result) 25 | }, 20) 26 | }) 27 | } 28 | 29 | const { activeIndex, result } = useAsyncQueue([p1, p2]) 30 | 31 | 32 | setTimeout(() => { 33 | result // 结果 34 | activeIndex.value // 当前执行到的任务 index 35 | }, 1000) 36 | ``` 37 | 38 |
39 | 40 | ### 打断 41 | 42 | ```ts 43 | import { useAsyncQueue } from '@/uni_modules/tob-use' 44 | 45 | const { activeIndex, result } = useAsyncQueue([ 46 | // ... 省略异步任务 47 | ], { 48 | interrupt: true // 允许失败时打断,默认为 true 49 | }) 50 | ``` 51 | 52 |
53 | 54 | ### 回调 55 | 56 | ```ts 57 | import { useAsyncQueue } from '@/uni_modules/tob-use' 58 | 59 | const { activeIndex, result } = useAsyncQueue([ 60 | // ... 省略异步任务 61 | ], { 62 | // 出错时回调 63 | onError() { 64 | 65 | }, 66 | // 成功时回调 67 | onFinished() { 68 | 69 | } 70 | }) 71 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/reactify/README.md: -------------------------------------------------------------------------------- 1 | # reactify 2 | 3 | 将普通函数转换为响应式函数 4 | 5 | 大白话就是对函数参数去 `ref` 化 👇 6 | 7 | ## Usage 8 | 9 | ### 基础 10 | 11 | ```ts 12 | import { ref } from 'vue' 13 | import { reactify } from '@/uni_modules/tob-use' 14 | 15 | const count = ref(1) 16 | 17 | // reactify 注册一个回调,返回一个工厂函数 18 | const createDouble = reactify(v => { 19 | return v * 2 // 这里的 v 就是后边 count.value 20 | }) 21 | 22 | // 返回一个计算属性 23 | const double = createDouble(count) 24 | 25 | console.log(double.value) // 2 26 | 27 | count.value = 2 28 | 29 | console.log(double.value) // 4 30 | ``` 31 | 32 |
33 | 34 | ### 对比 35 | 36 | 对比原生计算属性 👇 37 | 38 | ```ts 39 | import { ref, computed } from 'vue' 40 | 41 | const a = ref(1) 42 | const b = 2 43 | const c = ref(3) 44 | 45 | const sum = computed(() => { 46 | // 需要考虑 .value 47 | return a.value + b + c.value 48 | }) 49 | 50 | console.log(sum.value) // 6 51 | ``` 52 | 53 | 用 `reactify` 👇 54 | 55 | ```ts 56 | import { ref } from 'vue' 57 | import { reactify } from '@/uni_modules/tob-use' 58 | 59 | const createSum = reactify((a, b, c) => { 60 | // 不需要考虑 .value 61 | return a + b + c 62 | }) 63 | 64 | const a = ref(1) 65 | const b = 2 66 | const c = ref(3) 67 | 68 | const sum = createSum(a, b, c) 69 | 70 | console.log(sum.value) 71 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useAsyncQueue/README.md: -------------------------------------------------------------------------------- 1 | # useAsyncQueue 2 | 3 | 使用异步队列 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useAsyncQueue } from '@/uni_modules/tob-use' 11 | 12 | const p1 = () => { 13 | return new Promise((resolve) => { 14 | setTimeout(() => { 15 | resolve(1000) 16 | }, 10) 17 | }) 18 | } 19 | 20 | // result 是上一个任务的结果 21 | const p2 = result => { 22 | return new Promise((resolve) => { 23 | setTimeout(() => { 24 | resolve(1000 + result) 25 | }, 20) 26 | }) 27 | } 28 | 29 | const { activeIndex, result } = useAsyncQueue([p1, p2]) 30 | 31 | 32 | setTimeout(() => { 33 | result // 结果 34 | activeIndex.value // 当前执行到的任务 index 35 | }, 1000) 36 | ``` 37 | 38 |
39 | 40 | ### 打断 41 | 42 | ```ts 43 | import { useAsyncQueue } from '@/uni_modules/tob-use' 44 | 45 | const { activeIndex, result } = useAsyncQueue([ 46 | // ... 省略异步任务 47 | ], { 48 | interrupt: true // 允许失败时打断,默认为 true 49 | }) 50 | ``` 51 | 52 |
53 | 54 | ### 回调 55 | 56 | ```ts 57 | import { useAsyncQueue } from '@/uni_modules/tob-use' 58 | 59 | const { activeIndex, result } = useAsyncQueue([ 60 | // ... 省略异步任务 61 | ], { 62 | // 出错时回调 63 | onError() { 64 | 65 | }, 66 | // 成功时回调 67 | onFinished() { 68 | 69 | } 70 | }) 71 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/useOffsetPagination.md: -------------------------------------------------------------------------------- 1 | # useOffsetPagination 2 | 3 | 偏移分页 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useOffsetPagination } from '@/uni_modules/tob-use' 11 | 12 | const result = useOffsetPagination({ 13 | page: 1, // 初始当前第几页,默认为 1 14 | pageSize: 20, // 每页元素数量,默认为 10 15 | total: 100 // 总元素数量,默认为 Infinity 无限 16 | }) 17 | 18 | result.pageCount.value // 总页数,计算属性 19 | 20 | result.currentPage.value // 当前第几页,计算属性 21 | 22 | result.isFirstPage.value // 是否是第一页,计算属性 23 | result.isLastPage.value // 是否是最后一页,计算属性 24 | 25 | result.currentPageSize.value // 当前每页元素数量,计算属性 26 | 27 | result.prev() // 上一页,触发 result.currentPage.value++ 28 | result.next() // 下一页,触发 result.currentPage.value-- 29 | ``` 30 | 31 |
32 | 33 | ### 回调 34 | 35 | ```ts 36 | import { useOffsetPagination } from '@/uni_modules/tob-use' 37 | 38 | const result = useOffsetPagination({ 39 | // 省略其他配置 ... 40 | // 总页数 result.pageCount 变化时触发 41 | onPageCountChange(result) { 42 | result // 上一次的结果,不过是一个 reactive 43 | }, 44 | // 当前第几页 result.currentPage 变更时触发 45 | onPageChange(result) { 46 | result // 上一次的结果,不过是一个 reactive 47 | }, 48 | // 当前每页元素数量 result.currentPageSize 变化时触发 49 | onPageSizeChange(result) { 50 | result // 上一次的结果,不过是一个 reactive 51 | } 52 | }) 53 | ``` -------------------------------------------------------------------------------- /docs/api/watch/ignorableWatch.md: -------------------------------------------------------------------------------- 1 | # ignorableWatch 2 | 3 | 忽略型监听 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref, nextTick } from 'vue' 11 | import { ignorableWatch } from '@/uni_modules/tob-use' 12 | 13 | const source = ref('foo') 14 | 15 | const { stop, ignoreUpdates, ignorePrevAsyncUpdates } = ignorableWatch( 16 | source, 17 | (v) => console.log(`更新 ${v}!`), 18 | ) 19 | 20 | const run = async () => { 21 | source.value = 'bar' 22 | await nextTick() // 输出 '更新 bar' 23 | 24 | ignoreUpdates(() => { 25 | source.value = 'foobar' // 忽略了此处更新,不会触发回调 26 | }) 27 | await nextTick() // 无事发生 28 | 29 | source.value = 'good' 30 | ignorePrevAsyncUpdates() // 忽略上一次仍未处理的的更新,所以不会触发回调 31 | await nextTick() // 无事发生 32 | 33 | stop() // 暂停监听 34 | 35 | source.value = 'jack' // 不会触发回调,因为已经暂停了 36 | } 37 | 38 | run() 39 | ``` 40 | 41 |
42 | 43 | ### Watch 选项 44 | 45 | ```ts 46 | import { ref } from 'vue' 47 | import { ignorableWatch } from '@/uni_modules/tob-use' 48 | 49 | const source = ref('foo') 50 | const callback = (v) => console.log(`更新 ${v}!`) 51 | 52 | ignorableWatch( 53 | source, 54 | callback, 55 | { 56 | deep: true, // 深度同步 57 | immediate: true, // 立即同步,默认为 false 58 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 59 | } 60 | ) 61 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/syncRef.md: -------------------------------------------------------------------------------- 1 | # syncRef 2 | 3 | 保持目标 ref 跟源同步 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { syncRef } from '@/uni_modules/tob-use' 12 | 13 | const source = ref(1) 14 | const target = ref(2) 15 | 16 | // 保持目标 target 跟源 source 同步,并返回 stop 暂停同步函数 17 | const stop = syncRef(source, target) 18 | 19 | target.value // 1 20 | 21 | source.value = 10 22 | 23 | target.value // 10 24 | 25 | stop() // 暂停同步 26 | 27 | source.value = 100 28 | 29 | target.value // 仍然是 10 30 | ``` 31 | 32 | ### 多个 33 | 34 | ```ts 35 | import { ref } from 'vue' 36 | import { syncRef } from '@/uni_modules/tob-use' 37 | 38 | const a = ref(1) 39 | const b = ref(2) 40 | const c = ref(2) 41 | 42 | // 保持目标 b, c 跟源 a 同步,并返回 stop 暂停函数 43 | const stop = syncRef(a, [b, c]) 44 | 45 | b.value // 1 46 | c.value // 1 47 | 48 | a.value = 2 49 | 50 | b.value // 2 51 | c.value // 2 52 | ``` 53 | 54 | ### 配置 55 | 56 | `syncRef` 第三个参数,支持 `watch` 配置 57 | 58 | ```ts 59 | import { ref } from 'vue' 60 | import { syncRef } from '@/uni_modules/tob-use' 61 | 62 | const source = ref(1) 63 | const target = ref(2) 64 | 65 | const stop = syncRef(source, target, { 66 | deep: false, // 深度同步,默认为 false 67 | immediate: true, // 立即同步,默认为 true 68 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 sync 69 | }) 70 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useOffsetPagination/README.md: -------------------------------------------------------------------------------- 1 | # useOffsetPagination 2 | 3 | 偏移分页 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { useOffsetPagination } from '@/uni_modules/tob-use' 11 | 12 | const result = useOffsetPagination({ 13 | page: 1, // 初始当前第几页,默认为 1 14 | pageSize: 20, // 每页元素数量,默认为 10 15 | total: 100 // 总元素数量,默认为 Infinity 无限 16 | }) 17 | 18 | result.pageCount.value // 总页数,计算属性 19 | 20 | result.currentPage.value // 当前第几页,计算属性 21 | 22 | result.isFirstPage.value // 是否是第一页,计算属性 23 | result.isLastPage.value // 是否是最后一页,计算属性 24 | 25 | result.currentPageSize.value // 当前每页元素数量,计算属性 26 | 27 | result.prev() // 上一页,触发 result.currentPage.value++ 28 | result.next() // 下一页,触发 result.currentPage.value-- 29 | ``` 30 | 31 |
32 | 33 | ### 回调 34 | 35 | ```ts 36 | import { useOffsetPagination } from '@/uni_modules/tob-use' 37 | 38 | const result = useOffsetPagination({ 39 | // 省略其他配置 ... 40 | // 总页数 result.pageCount 变化时触发 41 | onPageCountChange(result) { 42 | result // 上一次的结果,不过是一个 reactive 43 | }, 44 | // 当前第几页 result.currentPage 变更时触发 45 | onPageChange(result) { 46 | result // 上一次的结果,不过是一个 reactive 47 | }, 48 | // 当前每页元素数量 result.currentPageSize 变化时触发 49 | onPageSizeChange(result) { 50 | result // 上一次的结果,不过是一个 reactive 51 | } 52 | }) 53 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/animation/useIntervalFn/index.js: -------------------------------------------------------------------------------- 1 | import { isRef, ref, unref, watch } from 'vue' 2 | import { tryOnScopeDispose } from '../../component/tryOnScopeDispose' 3 | 4 | /** 5 | * 间隔循环函数 6 | */ 7 | export const useIntervalFn = ( 8 | cb, 9 | interval = 1000, 10 | options = {} 11 | ) => { 12 | const { 13 | immediate = true, // 立即开启间隔函数 14 | immediateCallback = false // 立即触发间隔函数 15 | } = options 16 | 17 | let timer = null 18 | const isActive = ref(false) // 是否处于开启间隔状态 19 | 20 | // 清除 21 | const clean = () => { 22 | if (timer) { 23 | clearInterval(timer) 24 | timer = null 25 | } 26 | } 27 | 28 | // 暂停 29 | const pause = () => { 30 | isActive.value = false 31 | clean() 32 | } 33 | 34 | // 恢复 35 | const resume = () => { 36 | if (interval <= 0) return 37 | isActive.value = true 38 | if (immediateCallback) cb() 39 | clean() 40 | timer = setInterval(cb, unref(interval)) 41 | } 42 | 43 | // 立即触发间隔函数 44 | if (immediate) resume() 45 | 46 | // interval 为 ref 时,可动态设置间隔大小 47 | if (isRef(interval)) { 48 | const stopWatch = watch(interval, () => { 49 | if (immediate) resume() 50 | }) 51 | // 尝试获取区域内副作用暂停 52 | tryOnScopeDispose(stopWatch) 53 | } 54 | 55 | //尝试获取区域内副作用暂停 56 | tryOnScopeDispose(pause) 57 | 58 | return { 59 | isActive, 60 | pause, 61 | resume 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/syncRef/README.md: -------------------------------------------------------------------------------- 1 | # syncRef 2 | 3 | 保持目标 ref 跟源同步 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref } from 'vue' 11 | import { syncRef } from '@/uni_modules/tob-use' 12 | 13 | const source = ref(1) 14 | const target = ref(2) 15 | 16 | // 保持目标 target 跟源 source 同步,并返回 stop 暂停同步函数 17 | const stop = syncRef(source, target) 18 | 19 | target.value // 1 20 | 21 | source.value = 10 22 | 23 | target.value // 10 24 | 25 | stop() // 暂停同步 26 | 27 | source.value = 100 28 | 29 | target.value // 仍然是 10 30 | ``` 31 | 32 | ### 多个 33 | 34 | ```ts 35 | import { ref } from 'vue' 36 | import { syncRef } from '@/uni_modules/tob-use' 37 | 38 | const a = ref(1) 39 | const b = ref(2) 40 | const c = ref(2) 41 | 42 | // 保持目标 b, c 跟源 a 同步,并返回 stop 暂停函数 43 | const stop = syncRef(a, [b, c]) 44 | 45 | b.value // 1 46 | c.value // 1 47 | 48 | a.value = 2 49 | 50 | b.value // 2 51 | c.value // 2 52 | ``` 53 | 54 | ### 配置 55 | 56 | `syncRef` 第三个参数,支持 `watch` 配置 57 | 58 | ```ts 59 | import { ref } from 'vue' 60 | import { syncRef } from '@/uni_modules/tob-use' 61 | 62 | const source = ref(1) 63 | const target = ref(2) 64 | 65 | const stop = syncRef(source, target, { 66 | deep: false, // 深度同步,默认为 false 67 | immediate: true, // 立即同步,默认为 true 68 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 sync 69 | }) 70 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/ignorableWatch/README.md: -------------------------------------------------------------------------------- 1 | # ignorableWatch 2 | 3 | 忽略型监听 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { ref, nextTick } from 'vue' 11 | import { ignorableWatch } from '@/uni_modules/tob-use' 12 | 13 | const source = ref('foo') 14 | 15 | const { stop, ignoreUpdates, ignorePrevAsyncUpdates } = ignorableWatch( 16 | source, 17 | (v) => console.log(`更新 ${v}!`), 18 | ) 19 | 20 | const run = async () => { 21 | source.value = 'bar' 22 | await nextTick() // 输出 '更新 bar' 23 | 24 | ignoreUpdates(() => { 25 | source.value = 'foobar' // 忽略了此处更新,不会触发回调 26 | }) 27 | await nextTick() // 无事发生 28 | 29 | source.value = 'good' 30 | ignorePrevAsyncUpdates() // 忽略上一次仍未处理的的更新,所以不会触发回调 31 | await nextTick() // 无事发生 32 | 33 | stop() // 暂停监听 34 | 35 | source.value = 'jack' // 不会触发回调,因为已经暂停了 36 | } 37 | 38 | run() 39 | ``` 40 | 41 |
42 | 43 | ### Watch 选项 44 | 45 | ```ts 46 | import { ref } from 'vue' 47 | import { ignorableWatch } from '@/uni_modules/tob-use' 48 | 49 | const source = ref('foo') 50 | const callback = (v) => console.log(`更新 ${v}!`) 51 | 52 | ignorableWatch( 53 | source, 54 | callback, 55 | { 56 | deep: true, // 深度同步 57 | immediate: true, // 立即同步,默认为 false 58 | flush: 'sync', // 同步时机,支持 pre,post,sync,默认为 pre 59 | } 60 | ) 61 | ``` -------------------------------------------------------------------------------- /docs/guide/index.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | ## 是什么? 4 | 5 | **tob-use** 是由 **composition-api** 构建的 **use** 库,主要面向 **vue3** 下的 **uniapp** 高效开发。 6 | 7 | 8 |
9 |
10 |
11 | 12 | ## 为什么? 13 | 14 | 为什么要做这个 **use 库** 呢? 15 | 16 | 1. 为了使用强大的 `composition-api` 17 | 2. 为了让 `uniapp` 的开发更高效,节省时间做更多其他的事情 18 | 19 | 20 |
21 |
22 | 23 | 24 | ## 特点 25 | 26 | 1. 功能丰富 27 | - `60+` 的功能供你选择 28 | 2. 按需加载 29 | - 让你的应用体积更小 30 | 3. 灵活可配置 31 | - 可配置的事件过滤器 32 | 4. 支持多端 33 | - 支持 `app`,`h5` 和小程序 34 | 5. composition 35 | - 完全由 `composition-api` 构建 36 | 6. vue3 37 | - 支持最现代化的 `vue3` 版本 38 | 39 | 40 |
41 |
42 |
43 | 44 | ## 启发 45 | 46 | 该 `use` 库 受 [vueuse](https://vueuse.org/) 启发,同时该 `use` 库绝大部分 `api` 来自其不需要依赖 `web` 环境的部分。 47 | 48 | 如果你希望在 `vue3` 下为构建纯 `web` 项目提高开发效率,节省时间,那么推荐你使用 👉 [vueuse](https://vueuse.org/)。 49 | 50 |
51 |
52 |
53 | 54 | ## 组织 55 | 56 | 欢迎关注 **帝莎编程** 57 | 58 | - [官网](http://dishaxy.dishait.cn/) 59 | - [Gitee](https://gitee.com/dishait) 60 | - [Github](https://github.com/dishait) 61 | - [网易云课堂](https://study.163.com/provider/480000001892585/index.htm?share=2&shareId=480000001892585) 62 | 63 |
64 |
65 |
66 | 67 | ## 仓库 68 | 69 | - [Github](https://github.com/dishait/tob-use) 70 | - [Gitee](https://gitee.com/dishait/tob-use) 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tob-use 2 | 3 | **tob-use** 是由 **composition-api** 构建的 **use** 库,主要面向 **vue3** 下的 **uniapp** 高效开发 4 | 5 |
6 |
7 | 8 | ## 特点 🐳 9 | 10 | 1. 功能丰富 11 | - `60+` 的功能供你选择 12 | 2. 按需加载 13 | - 让你的应用体积更小 14 | 3. 灵活可配置 15 | - 可配置的事件过滤器 16 | 4. 支持多端 17 | - 支持 `app`,`h5` 和小程序 18 | 5. composition 19 | - 完全由 `composition-api` 构建 20 | 6. vue3 21 | - 支持最现代化的 `vue3` 版本 22 | 23 | 24 | 25 |
26 |
27 | 28 | ## [在线文档](https://tob-use.netlify.app/) 🐇 29 | 30 | 点击跳转 👉 [tob-use](https://tob-use.netlify.app/) 31 | 32 | 33 |
34 |
35 | 36 | ## 动机 🦕 37 | 38 | 为什么要做这个 **use 库** 呢? 39 | 40 | 1. 为了使用强大的 `composition-api` 41 | 2. 为了让 `uniapp` 的开发更高效,节省时间做更多其他的事情 42 | 43 | 44 | 45 | 46 |
47 |
48 | 49 | ## 启发 🦖 50 | 51 | 该 `use` 库 受 [vueuse](https://vueuse.org/) 启发,同时该 `use` 库绝大部分 `api` 来自其不需要依赖 `web` 环境的部分。 52 | 53 | 如果你希望在 `vue3` 下为构建纯 `web` 项目提高开发效率,节省时间,那么推荐你使用 👉 [vueuse](https://vueuse.org/)。 54 | 55 |
56 |
57 | 58 | ## 组织 🦔 59 | 60 | 欢迎关注 **帝莎编程** 61 | - [官网](http://dishaxy.dishait.cn/) 62 | - [Gitee](https://gitee.com/dishait) 63 | 64 | - [Github](https://github.com/dishait) 65 | 66 | - [网易云课堂](https://study.163.com/provider/480000001892585/index.htm?share=2&shareId=480000001892585) 67 | 68 |
69 |
70 | 71 | ## 仓库 📦 72 | 73 | - [Github](https://github.com/dishait/tob-use) 74 | - [Gitee](https://gitee.com/dishait/tob-use) 75 | -------------------------------------------------------------------------------- /uni_modules/tob-use/readme.md: -------------------------------------------------------------------------------- 1 | # tob-use 2 | 3 | **tob-use** 是由 **composition-api** 构建的 **use** 库,主要面向 **vue3** 下的 **uniapp** 高效开发 4 | 5 |
6 |
7 | 8 | ## 特点 🐳 9 | 10 | 1. 功能丰富 11 | - `60+` 的功能供你选择 12 | 2. 按需加载 13 | - 让你的应用体积更小 14 | 3. 灵活可配置 15 | - 可配置的事件过滤器 16 | 4. 支持多端 17 | - 支持 `app`,`h5` 和小程序 18 | 5. composition 19 | - 完全由 `composition-api` 构建 20 | 6. vue3 21 | - 支持最现代化的 `vue3` 版本 22 | 23 | 24 | 25 |
26 |
27 | 28 | ## [在线文档](https://tob-use.netlify.app/) 🐇 29 | 30 | 点击跳转 👉 [tob-use](https://tob-use.netlify.app/) 31 | 32 | 33 |
34 |
35 | 36 | ## 动机 🦕 37 | 38 | 为什么要做这个 **use 库** 呢? 39 | 40 | 1. 为了使用强大的 `composition-api` 41 | 2. 为了让 `uniapp` 的开发更高效,节省时间做更多其他的事情 42 | 43 | 44 | 45 | 46 |
47 |
48 | 49 | ## 启发 🦖 50 | 51 | 该 `use` 库 受 [vueuse](https://vueuse.org/) 启发,同时该 `use` 库绝大部分 `api` 来自其不需要依赖 `web` 环境的部分。 52 | 53 | 如果你希望在 `vue3` 下为构建纯 `web` 项目提高开发效率,节省时间,那么推荐你使用 👉 [vueuse](https://vueuse.org/)。 54 | 55 |
56 |
57 | 58 | ## 组织 🦔 59 | 60 | 欢迎关注 **帝莎编程** 61 | - [官网](http://dishaxy.dishait.cn/) 62 | - [Gitee](https://gitee.com/dishait) 63 | 64 | - [Github](https://github.com/dishait) 65 | 66 | - [网易云课堂](https://study.163.com/provider/480000001892585/index.htm?share=2&shareId=480000001892585) 67 | 68 |
69 |
70 | 71 | ## 仓库 📦 72 | 73 | - [Github](https://github.com/dishait/tob-use) 74 | - [Gitee](https://gitee.com/dishait/tob-use) 75 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/controlledRef/index.js: -------------------------------------------------------------------------------- 1 | import { customRef } from 'vue' 2 | import { extendRef } from '../extendRef' 3 | 4 | /** 5 | * 受控型 ref 6 | */ 7 | export const controlledRef = (initial, options = {}) => { 8 | let source = initial 9 | let track 10 | let trigger 11 | 12 | const ref = customRef((_track, _trigger) => { 13 | track = _track 14 | trigger = _trigger 15 | 16 | return { 17 | get() { 18 | return get() 19 | }, 20 | set(v) { 21 | set(v) 22 | } 23 | } 24 | }) 25 | 26 | // 获取 27 | const get = (tracking = true) => { 28 | if (tracking) { 29 | track() 30 | } 31 | return source 32 | } 33 | 34 | // 设置 35 | const set = (value, triggering = true) => { 36 | const shouldUpdate = value !== source 37 | if (!shouldUpdate) return 38 | 39 | const old = source 40 | // 调用变更之前的 hook 41 | if (options.onBeforeChange?.(value, old) === false) { 42 | return 43 | } 44 | 45 | source = value 46 | 47 | // 调用变更之后的 hook 48 | options.onChanged?.(value, old) 49 | 50 | if (triggering) { 51 | trigger() 52 | } 53 | } 54 | 55 | // 不触发收集的获取 56 | const untrackedGet = () => get(false) 57 | 58 | // 单纯的设置,单不触发副作用 59 | const silentSet = v => set(v, false) 60 | 61 | // 偷窥,untrackedGet 的别名 62 | const peek = () => get(false) 63 | 64 | // 轻放,silentSet 的别名 65 | const lay = v => set(v, false) 66 | 67 | // 扩展原有的 ref 68 | return extendRef( 69 | ref, 70 | { 71 | get, 72 | set, 73 | untrackedGet, 74 | silentSet, 75 | peek, 76 | lay 77 | }, 78 | { enumerable: true } 79 | ) 80 | } 81 | -------------------------------------------------------------------------------- /docs/api/utilities/createEventHook.md: -------------------------------------------------------------------------------- 1 | # createEventHook 2 | 3 | 创建事件 hook 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```ts 10 | import { createEventHook } from '@/uni_modules/tob-use' 11 | 12 | const event = createEventHook() 13 | 14 | // on 可以注册回调 15 | event.on(v => { 16 | console.log(v) 17 | }) 18 | 19 | event.trigger(0) // 触发 on 注册的回调,打印 0 20 | 21 | // 再注册一个 22 | event.on(v => { 23 | console.log(v + 1) 24 | }) 25 | 26 | event.trigger(1) // 触发所有 on 注册的回调,打印 1,2 27 | 28 | // 可解构获取卸载函数 29 | const { off } = event.on(v => { 30 | console.log('触发了') 31 | }) 32 | 33 | off() // 卸载刚注册的回调 34 | 35 | event.trigger(1) // 打印 1,2,但不会打印 '触发了',因为刚刚被卸载了 36 | ``` 37 | 38 |
39 | 40 | ### 封装请求 41 | 42 | ```js 43 | import { createEventHook } from '@/uni_modules/tob-use' 44 | 45 | /** 46 | * 封装请求 47 | */ 48 | const useUserInfo = () => { 49 | const failEvent = createEventHook() 50 | const successEvent = createEventHook() 51 | uni.request({ 52 | url: 'https://www.example.com/request', // 虚拟请求地址 53 | fail(err) { 54 | failEvent.trigger(err) // 失败时触发 55 | }, 56 | success(res) { 57 | successEvent.trigger(res) // 成功时触发 58 | }, 59 | }) 60 | 61 | return { 62 | onFail: failEvent.on, // 失败监听 63 | onSuccess: successEvent.on // 成功监听 64 | } 65 | } 66 | 67 | /** 68 | * 使用时 69 | */ 70 | const { onFail, onSuccess } = useUserInfo() 71 | 72 | // 注册失败回调,失败时将被触发 73 | onFail(err => { 74 | console.log(err) 75 | }) 76 | 77 | // 注册成功回调,成功时将被触发 78 | onSuccess(res => { 79 | console.log(res) 80 | }) 81 | ``` 82 | 83 | 84 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/shared.ts: -------------------------------------------------------------------------------- 1 | import { sync } from 'fast-glob' 2 | import { dirname, basename } from 'path' 3 | import { ensureLinkSync } from 'fs-extra' 4 | import { utimesSync, openSync, closeSync } from 'fs' 5 | 6 | export const _DEV_ = process.env.NODE_ENV === 'development' 7 | 8 | export const touch = (path: string) => { 9 | const time = new Date() 10 | try { 11 | utimesSync(path, time, time) 12 | } catch (error) { 13 | closeSync(openSync(path, 'w')) 14 | } 15 | } 16 | 17 | export const showName = (path: string) => { 18 | return basename(dirname(path)) 19 | } 20 | 21 | export const showType = (path: string) => { 22 | return basename(dirname(dirname(path))) 23 | } 24 | 25 | export const useDest = (path: string) => { 26 | const name = showName(path) 27 | // 跳过根目录下的文件 28 | const skip = name === 'tob-use' 29 | if (skip) { 30 | return { skip: true } 31 | } 32 | const type = showType(path) 33 | const dest = `./docs/api/${type}/${name}.md` 34 | 35 | return { dest, type, name } 36 | } 37 | 38 | interface Routes { 39 | [k: string]: string[] 40 | } 41 | 42 | // 生成 api 的路由表 43 | export const generateApiRoutes = (): Routes => { 44 | const srcPaths = sync(`./uni_modules/tob-use/**/*.md`) 45 | const routes: Routes = {} 46 | srcPaths.forEach(src => { 47 | const { dest, type, skip, name } = useDest(src) 48 | 49 | if (skip) { 50 | return 51 | } 52 | 53 | // 补充硬链接的更新 54 | _DEV_ && ensureLinkSync(src, dest) 55 | 56 | let sidebar = routes[type] 57 | if (!sidebar) { 58 | sidebar = routes[type] = [] 59 | } 60 | sidebar.push(`/api/${type}/${name}.md`) 61 | }) 62 | return routes 63 | } 64 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/createEventHook/README.md: -------------------------------------------------------------------------------- 1 | # createEventHook 2 | 3 | 创建事件 hook 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```ts 10 | import { createEventHook } from '@/uni_modules/tob-use' 11 | 12 | const event = createEventHook() 13 | 14 | // on 可以注册回调 15 | event.on(v => { 16 | console.log(v) 17 | }) 18 | 19 | event.trigger(0) // 触发 on 注册的回调,打印 0 20 | 21 | // 再注册一个 22 | event.on(v => { 23 | console.log(v + 1) 24 | }) 25 | 26 | event.trigger(1) // 触发所有 on 注册的回调,打印 1,2 27 | 28 | // 可解构获取卸载函数 29 | const { off } = event.on(v => { 30 | console.log('触发了') 31 | }) 32 | 33 | off() // 卸载刚注册的回调 34 | 35 | event.trigger(1) // 打印 1,2,但不会打印 '触发了',因为刚刚被卸载了 36 | ``` 37 | 38 |
39 | 40 | ### 封装请求 41 | 42 | ```js 43 | import { createEventHook } from '@/uni_modules/tob-use' 44 | 45 | /** 46 | * 封装请求 47 | */ 48 | const useUserInfo = () => { 49 | const failEvent = createEventHook() 50 | const successEvent = createEventHook() 51 | uni.request({ 52 | url: 'https://www.example.com/request', // 虚拟请求地址 53 | fail(err) { 54 | failEvent.trigger(err) // 失败时触发 55 | }, 56 | success(res) { 57 | successEvent.trigger(res) // 成功时触发 58 | }, 59 | }) 60 | 61 | return { 62 | onFail: failEvent.on, // 失败监听 63 | onSuccess: successEvent.on // 成功监听 64 | } 65 | } 66 | 67 | /** 68 | * 使用时 69 | */ 70 | const { onFail, onSuccess } = useUserInfo() 71 | 72 | // 注册失败回调,失败时将被触发 73 | onFail(err => { 74 | console.log(err) 75 | }) 76 | 77 | // 注册成功回调,成功时将被触发 78 | onSuccess(res => { 79 | console.log(res) 80 | }) 81 | ``` 82 | 83 | 84 | -------------------------------------------------------------------------------- /uni_modules/tob-less/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "tob-less", 3 | "displayName": "tob-less", 4 | "version": "1.0.1", 5 | "description": "主题驱动的uniapp样式库", 6 | "keywords": [ 7 | "tob-less", 8 | "theme", 9 | "less", 10 | "css" 11 | ], 12 | "repository": "https://github.com/markthree/tob-less", 13 | "engines": { 14 | "HBuilderX": "^3.1.0" 15 | }, 16 | "dcloudext": { 17 | "category": [ 18 | "JS SDK", 19 | "通用 SDK" 20 | ], 21 | "sale": { 22 | "regular": { 23 | "price": "0.00" 24 | }, 25 | "sourcecode": { 26 | "price": "0.00" 27 | } 28 | }, 29 | "contact": { 30 | "qq": "" 31 | }, 32 | "declaration": { 33 | "ads": "无", 34 | "data": "无", 35 | "permissions": "无" 36 | }, 37 | "npmurl": "https://www.npmjs.com/package/tob-less" 38 | }, 39 | "uni_modules": { 40 | "dependencies": [], 41 | "encrypt": [], 42 | "platforms": { 43 | "cloud": { 44 | "tcb": "y", 45 | "aliyun": "y" 46 | }, 47 | "client": { 48 | "Vue": { 49 | "vue2": "y", 50 | "vue3": "y" 51 | }, 52 | "App": { 53 | "app-vue": "y", 54 | "app-nvue": "n" 55 | }, 56 | "H5-mobile": { 57 | "Safari": "u", 58 | "Android Browser": "u", 59 | "微信浏览器(Android)": "y", 60 | "QQ浏览器(Android)": "u" 61 | }, 62 | "H5-pc": { 63 | "Chrome": "y", 64 | "IE": "u", 65 | "Edge": "y", 66 | "Firefox": "u", 67 | "Safari": "u" 68 | }, 69 | "小程序": { 70 | "微信": "y", 71 | "阿里": "u", 72 | "百度": "u", 73 | "字节跳动": "u", 74 | "QQ": "u" 75 | }, 76 | "快应用": { 77 | "华为": "u", 78 | "联盟": "u" 79 | } 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/layouts/404.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 42 | 43 | 76 | -------------------------------------------------------------------------------- /uni_modules/tob-use/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "tob-use", 3 | "displayName": "tob-use 最高效好用的 composition-api 库", 4 | "version": "1.1.0", 5 | "description": "uniapp 的高效 use 库", 6 | "keywords": [ 7 | "use", 8 | "hooks", 9 | "vue3", 10 | "vueuse", 11 | "composition-api" 12 | ], 13 | "repository": "https://github.com/dishait/tob-use", 14 | "engines": { 15 | "HBuilderX": "^3.3.11" 16 | }, 17 | "dcloudext": { 18 | "category": [ 19 | "JS SDK", 20 | "通用 SDK" 21 | ], 22 | "sale": { 23 | "regular": { 24 | "price": "0.00" 25 | }, 26 | "sourcecode": { 27 | "price": "0.00" 28 | } 29 | }, 30 | "contact": { 31 | "qq": "" 32 | }, 33 | "declaration": { 34 | "ads": "无", 35 | "data": "无", 36 | "permissions": "无" 37 | }, 38 | "npmurl": "https://www.npmjs.com/package/tob-use" 39 | }, 40 | "uni_modules": { 41 | "dependencies": [], 42 | "encrypt": [], 43 | "platforms": { 44 | "cloud": { 45 | "tcb": "y", 46 | "aliyun": "y" 47 | }, 48 | "client": { 49 | "Vue": { 50 | "vue2": "n", 51 | "vue3": "y" 52 | }, 53 | "App": { 54 | "app-vue": "y", 55 | "app-nvue": "u" 56 | }, 57 | "H5-mobile": { 58 | "Safari": "y", 59 | "Android Browser": "y", 60 | "微信浏览器(Android)": "y", 61 | "QQ浏览器(Android)": "y" 62 | }, 63 | "H5-pc": { 64 | "Chrome": "y", 65 | "IE": "n", 66 | "Edge": "y", 67 | "Firefox": "y", 68 | "Safari": "y" 69 | }, 70 | "小程序": { 71 | "微信": "y", 72 | "阿里": "y", 73 | "百度": "y", 74 | "字节跳动": "y", 75 | "QQ": "y" 76 | }, 77 | "快应用": { 78 | "华为": "y", 79 | "联盟": "y" 80 | } 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path' 2 | import { watch } from 'chokidar' 3 | import { remove } from 'fs-extra' 4 | import { ThemeObject } from 'vuepress' 5 | import { touch, useDest } from './shared' 6 | import { _DEV_ } from './shared' 7 | 8 | const theme: ThemeObject = { 9 | name: 'docs', 10 | extends: '@vuepress/theme-default', 11 | layouts: { 12 | '404': resolve(__dirname, './layouts/404.vue') 13 | }, 14 | onInitialized(app) { 15 | // 修改 api 文档的侧边栏层级 16 | app.pages.forEach(page => { 17 | if (/^\/api\/.+\.html$/.test(page.pathInferred)) { 18 | page.frontmatter.sidebarDepth = 0 19 | } 20 | }) 21 | }, 22 | onWatched(app, watchers, restart) { 23 | if (_DEV_) { 24 | // 更新 api 监听器 25 | const flushApiWatcher = watch( 26 | './uni_modules/tob-use/**/*.md', 27 | { 28 | ignoreInitial: true 29 | } 30 | ) 31 | 32 | flushApiWatcher.on('change', src => { 33 | const { dest, skip } = useDest(src) 34 | if (skip) { 35 | return 36 | } 37 | touch(dest) 38 | }) 39 | 40 | flushApiWatcher.once('add', src => { 41 | const { skip } = useDest(src) 42 | if (skip) { 43 | return 44 | } 45 | restart() 46 | }) 47 | 48 | flushApiWatcher.once('unlink', src => { 49 | const { dest, skip } = useDest(src) 50 | 51 | if (skip) { 52 | return 53 | } 54 | 55 | remove(dest) 56 | restart() 57 | }) 58 | 59 | // 主题监听器 60 | const themeWatcher = watch( 61 | resolve(__dirname, './index.ts'), 62 | { 63 | ignoreInitial: true 64 | } 65 | ) 66 | 67 | // 变更时重启 68 | themeWatcher.on('change', restart) 69 | 70 | watchers.push(themeWatcher, flushApiWatcher) 71 | } 72 | } 73 | } 74 | 75 | export default theme 76 | -------------------------------------------------------------------------------- /docs/about/index.md: -------------------------------------------------------------------------------- 1 | # 关于 2 | 3 |
4 | 5 | ## 组织 :t-rex: 6 | 7 | 8 | 欢迎关注 [**帝莎编程**](http://dishaxy.dishait.cn/) 9 | - [官网](http://dishaxy.dishait.cn/) 10 | - [Gitee](https://gitee.com/dishait) 11 | 12 | - [Github](https://github.com/dishait) 13 | 14 | - [网易云课堂](https://study.163.com/provider/480000001892585/index.htm?share=2&shareId=480000001892585) 15 | 16 | 17 |
18 |
19 |
20 | 21 | ## 开源 :sauropod: 22 | 23 | ### 模板 24 | 25 | 1. [tov-template](https://github.com/dishait/tov-template) 26 | - `vite + vue3 + ts` 开箱即用现代开发模板 27 | 28 | 2. [vite-plugin-template](https://github.com/dishait/vite-plugin-template) 29 | - 开箱即用的 `vite` 插件模板 30 | 31 | 32 | 3. [vuepress-docs-template](https://github.com/dishait/vuepress-docs-template) 33 | - 基于 `vuepress-next` 的文档模板 34 | 35 | 36 |
37 | 38 | 39 | ### vite 40 | 41 | 1. [vite-auto-import-resolvers](https://github.com/dishait/vite-auto-import-resolvers) 42 | - `unplugin-auto-import` 的 `vite resolver` 43 | 44 | 2. [vite-plugin-watcher](https://github.com/dishait/vite-plugin-watcher) 45 | - `vite` 的文件监听插件 46 | 47 | 3. [vite-plugin-builded-force-exit](https://github.com/dishait/vite-plugin-builded-force-exit) 48 | - `vite` 打包后强制退出的插件 49 | 50 | 51 | 4. [vite-plugin-vue-custom-blocks](https://github.com/dishait/vite-plugin-vue-custom-blocks) 52 | - `vue sfc` 的自定义块 `vite` 插件 53 | 54 | 55 |
56 | 57 | ### uniapp 58 | 59 | 1. [tob-ui](https://github.com/dishait/tob-ui) 60 | - 更现代的 `uniapp ui` 61 | 62 | 2. [tob-less](https://github.com/dishait/tob-less) 63 | - 主题驱动的 `uniapp` 样式库 64 | 65 |
66 |
😁 更多开源进行中.....
67 |
68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tob-use", 3 | "version": "1.1.1", 4 | "description": "uniapp 的高效 use 库", 5 | "main": "./dist/index.umd.js", 6 | "module": "./dist/index.es.js", 7 | "exports": { 8 | ".": { 9 | "import": "./dist/index.es.js", 10 | "require": "./dist/index.umd.js" 11 | } 12 | }, 13 | "scripts": { 14 | "prepublishOnly": "npm run build", 15 | "auto:create": "node scripts/create.js", 16 | "auto:remove": "node scripts/remove.js", 17 | "docs:dev": "vuepress dev docs", 18 | "docs:build": "vuepress build docs", 19 | "docs:dev:open": "vuepress dev docs --open", 20 | "build": "vite build --config uni_modules/tob-use/vite.config.js", 21 | "release": "bumpp --commit --push --tag && npm publish" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/dishait/tob-use.git" 26 | }, 27 | "files": [ 28 | "dist" 29 | ], 30 | "keywords": [ 31 | "tob", 32 | "use", 33 | "vue3", 34 | "uniapp", 35 | "reactivity", 36 | "composition-api" 37 | ], 38 | "author": { 39 | "name": "markthree", 40 | "email": "1801982702@qq.com", 41 | "url": "https://github.com/markthree" 42 | }, 43 | "license": "MIT", 44 | "devDependencies": { 45 | "@markthree/ilazy": "^1.0.2", 46 | "@markthree/node-shared": "^1.3.2", 47 | "@types/node": "^17.0.21", 48 | "@vuepress/plugin-register-components": "^2.0.0-beta.35", 49 | "@vuepress/plugin-search": "^2.0.0-beta.35", 50 | "@vueuse/core": "^7.7.0", 51 | "bumpp": "^7.1.1", 52 | "chokidar": "^3.5.3", 53 | "fast-glob": "^3.2.11", 54 | "fs-extra": "^10.0.1", 55 | "ityped": "^1.0.3", 56 | "unocss": "^0.28.1", 57 | "unplugin-auto-import": "^0.6.1", 58 | "unplugin-vue-components": "^0.17.21", 59 | "vite": "^2.8.5", 60 | "vite-plugin-inspect": "^0.4.3", 61 | "vuepress": "^2.0.0-beta.35" 62 | }, 63 | "dependencies": { 64 | "vue": "^3.2.31" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useAsyncQueue/index.js: -------------------------------------------------------------------------------- 1 | import { reactive, ref } from 'vue' 2 | import { noop } from '../../shared/base' 3 | 4 | /** 5 | * 使用异步队列 6 | */ 7 | export const useAsyncQueue = (tasks, options = {}) => { 8 | const { 9 | interrupt = true, // 允许失败时打断 10 | onError = noop, 11 | onFinished = noop 12 | } = options 13 | 14 | const promiseState = { 15 | pending: 'pending', 16 | rejected: 'rejected', 17 | fulfilled: 'fulfilled' 18 | } 19 | const initialResult = Array.from( 20 | new Array(tasks.length), 21 | () => ({ state: promiseState.pending, data: null }) 22 | ) 23 | const result = reactive(initialResult) 24 | 25 | const activeIndex = ref(-1) 26 | 27 | // 无任务时,直接返回 28 | const isEmptyTasks = !tasks || tasks.length === 0 29 | if (isEmptyTasks) { 30 | onFinished() 31 | return { 32 | activeIndex, 33 | result 34 | } 35 | } 36 | 37 | // 更新结果 38 | const updateResult = (state, res) => { 39 | activeIndex.value++ 40 | result[activeIndex.value].data = res 41 | result[activeIndex.value].state = state 42 | } 43 | 44 | tasks.reduce((prev, curr) => { 45 | return prev 46 | .then(prevRes => { 47 | // 上一个失败时打断并调用完成回调 48 | const isRejected = 49 | result[activeIndex.value]?.state === 50 | promiseState.rejected 51 | if (isRejected && interrupt) { 52 | onFinished() 53 | return 54 | } 55 | 56 | return curr(prevRes).then(currentRes => { 57 | updateResult(promiseState.fulfilled, currentRes) 58 | // 结束时调用完成 hook 59 | const isFinished = 60 | activeIndex.value === tasks.length - 1 61 | if (isFinished) { 62 | onFinished() 63 | } 64 | 65 | return currentRes 66 | }) 67 | }) 68 | .catch(e => { 69 | // 错误处理,调用失败回调 70 | updateResult(promiseState.rejected, e) 71 | onError() 72 | return e 73 | }) 74 | }, Promise.resolve()) 75 | 76 | return { 77 | activeIndex, 78 | result 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /uni.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里是uni-app内置的常用样式变量 3 | * 4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App 6 | * 7 | */ 8 | 9 | /** 10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 11 | * 12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 13 | */ 14 | 15 | /* 颜色变量 */ 16 | 17 | /* 行为相关颜色 */ 18 | $uni-color-primary: #007aff; 19 | $uni-color-success: #4cd964; 20 | $uni-color-warning: #f0ad4e; 21 | $uni-color-error: #dd524d; 22 | 23 | /* 文字基本颜色 */ 24 | $uni-text-color:#333;//基本色 25 | $uni-text-color-inverse:#fff;//反色 26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 27 | $uni-text-color-placeholder: #808080; 28 | $uni-text-color-disable:#c0c0c0; 29 | 30 | /* 背景颜色 */ 31 | $uni-bg-color:#ffffff; 32 | $uni-bg-color-grey:#f8f8f8; 33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色 34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 35 | 36 | /* 边框颜色 */ 37 | $uni-border-color:#c8c7cc; 38 | 39 | /* 尺寸变量 */ 40 | 41 | /* 文字尺寸 */ 42 | $uni-font-size-sm:12px; 43 | $uni-font-size-base:14px; 44 | $uni-font-size-lg:16; 45 | 46 | /* 图片尺寸 */ 47 | $uni-img-size-sm:20px; 48 | $uni-img-size-base:26px; 49 | $uni-img-size-lg:40px; 50 | 51 | /* Border Radius */ 52 | $uni-border-radius-sm: 2px; 53 | $uni-border-radius-base: 3px; 54 | $uni-border-radius-lg: 6px; 55 | $uni-border-radius-circle: 50%; 56 | 57 | /* 水平间距 */ 58 | $uni-spacing-row-sm: 5px; 59 | $uni-spacing-row-base: 10px; 60 | $uni-spacing-row-lg: 15px; 61 | 62 | /* 垂直间距 */ 63 | $uni-spacing-col-sm: 4px; 64 | $uni-spacing-col-base: 8px; 65 | $uni-spacing-col-lg: 12px; 66 | 67 | /* 透明度 */ 68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度 69 | 70 | /* 文章场景相关 */ 71 | $uni-color-title: #2C405A; // 文章标题颜色 72 | $uni-font-size-title:20px; 73 | $uni-color-subtitle: #555555; // 二级标题颜色 74 | $uni-font-size-subtitle:26px; 75 | $uni-color-paragraph: #3F536E; // 文章段落颜色 76 | $uni-font-size-paragraph:15px; 77 | -------------------------------------------------------------------------------- /docs/api/utilities/useConfirmDialog.md: -------------------------------------------------------------------------------- 1 | # useConfirmDialog 2 | 3 | 确认对话框 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```html 10 | 38 | 39 | 50 | ``` 51 | 52 |
53 | 54 | ### Promise 55 | 56 | ```html 57 | 78 | 79 | 90 | ``` 91 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useOffsetPagination/index.js: -------------------------------------------------------------------------------- 1 | import { noop } from '../../shared/base' 2 | import { biSyncRef } from '../biSyncRef' 3 | import { useClamp } from '../useClamp' 4 | import { 5 | isRef, 6 | computed, 7 | reactive, 8 | unref, 9 | watch 10 | } from 'vue' 11 | 12 | /** 13 | * 偏移分页 14 | */ 15 | export const useOffsetPagination = (options = {}) => { 16 | const { 17 | page = 1, 18 | pageSize = 10, 19 | total = Infinity, 20 | onPageChange = noop, 21 | onPageSizeChange = noop, 22 | onPageCountChange = noop 23 | } = options 24 | 25 | // 当前每页元素数量 26 | const currentPageSize = useClamp(pageSize, 1, Infinity) 27 | 28 | // 总页数 29 | const pageCount = computed(() => 30 | Math.ceil(unref(total) / unref(currentPageSize)) 31 | ) 32 | 33 | // 当前第几页 34 | const currentPage = useClamp(page, 1, pageCount) 35 | 36 | // 是否是第一页 37 | const isFirstPage = computed( 38 | () => currentPage.value === 1 39 | ) 40 | 41 | // 是否是最后一页 42 | const isLastPage = computed( 43 | () => currentPage.value === pageCount.value 44 | ) 45 | 46 | // 同步 第几页 47 | if (isRef(page)) { 48 | biSyncRef(page, currentPage) 49 | } 50 | 51 | // 同步 每页元素数量 52 | if (isRef(pageSize)) { 53 | biSyncRef(pageSize, currentPageSize) 54 | } 55 | 56 | // 上一页 57 | const prev = () => currentPage.value-- 58 | 59 | // 下一页 60 | const next = () => currentPage.value++ 61 | 62 | const returnValue = { 63 | currentPage, 64 | currentPageSize, 65 | pageCount, 66 | isFirstPage, 67 | isLastPage, 68 | prev, 69 | next 70 | } 71 | 72 | // 当前第几页变化时,触发 onPageChange 73 | watch(currentPage, () => { 74 | onPageChange(reactive(returnValue)) 75 | }) 76 | 77 | // 当前每页元素数量变化时,触发 onPageSizeChange 78 | watch(currentPageSize, () => { 79 | onPageSizeChange(reactive(returnValue)) 80 | }) 81 | 82 | // 总页数变化时,触发 onPageCountChange 83 | watch(pageCount, () => { 84 | onPageCountChange(reactive(returnValue)) 85 | }) 86 | 87 | return returnValue 88 | } 89 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useConfirmDialog/README.md: -------------------------------------------------------------------------------- 1 | # useConfirmDialog 2 | 3 | 确认对话框 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```html 10 | 38 | 39 | 50 | ``` 51 | 52 |
53 | 54 | ### Promise 55 | 56 | ```html 57 | 78 | 79 | 90 | ``` 91 | -------------------------------------------------------------------------------- /docs/api/component/computedInject.md: -------------------------------------------------------------------------------- 1 | # computedInject 2 | 3 | 计算属性型 inject 4 | 5 | ## Usage 6 | 7 | 上层组件提供 `provide` 8 | 9 | ```html 10 | 20 | ``` 21 | 22 | 下层组件注入 `injcet` 23 | 24 | ```html 25 | 37 | ``` 38 | 39 |
40 | 41 | ### 默认值 42 | 43 | 当不确定上层组件是否提供 `provide` 时,可以设置第三个参数作为默认值 44 | 45 | ```html 46 | 60 | ``` 61 | 62 | 工厂函数型默认值 63 | 64 | ```html 65 | 82 | ``` 83 | 84 |
85 | 86 | ### setter 87 | 88 | ```html 89 | 103 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/component/computedInject/README.md: -------------------------------------------------------------------------------- 1 | # computedInject 2 | 3 | 计算属性型 inject 4 | 5 | ## Usage 6 | 7 | 上层组件提供 `provide` 8 | 9 | ```html 10 | 20 | ``` 21 | 22 | 下层组件注入 `injcet` 23 | 24 | ```html 25 | 37 | ``` 38 | 39 |
40 | 41 | ### 默认值 42 | 43 | 当不确定上层组件是否提供 `provide` 时,可以设置第三个参数作为默认值 44 | 45 | ```html 46 | 60 | ``` 61 | 62 | 工厂函数型默认值 63 | 64 | ```html 65 | 82 | ``` 83 | 84 |
85 | 86 | ### setter 87 | 88 | ```html 89 | 103 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/useMemoize.md: -------------------------------------------------------------------------------- 1 | # useMemoize 2 | 3 | 使用备份 4 | 5 | 即输入相同参数的情况下,直接走缓存。 6 | 7 | ## Usage 8 | 9 | ### 基础 10 | 11 | ```js 12 | import { useMemoize } from '@/uni_modules/tob-use' 13 | 14 | // 生成数组 15 | const generateArray = length => { 16 | return Array.from({ length }, (_, i) => i) 17 | } 18 | 19 | const useArray = useMemoize( 20 | length => { 21 | return Array.from({ length }, (_, i) => i) 22 | } 23 | ) 24 | 25 | const a1 = useArray(1) // 使用长度为 1 的新数组 26 | const a2 = useArray(2) // 使用长度为 2 的新数组 27 | 28 | const a3 = useArray(1) // 从缓存里拿到之前长度为 1 的数组 29 | 30 | 31 | const a4 = useArray.load(1) // 重新生成数组长度为 1 的新数组(即不用缓存里的) 32 | 33 | useArray.cache // 当前缓存,一个 reactive 的 map 34 | 35 | useArray.delete(1) // 将数组长度为 1 的数组从缓存中删除 36 | useArray.clear() // 清除所有缓存 37 | ``` 38 | 39 |
40 | 41 | ### 缓存 42 | 43 | 大多数情况你不需要手动生成缓存,`useMemoize` 内部会自动生成 `reactive` 的 `map` 缓存。 44 | 45 | 当然有时候我们希望让两个 `useMemoize` 共用缓存。那么可以这样 👇 46 | 47 | ```ts 48 | import { useMemoize } from '@/uni_modules/tob-use' 49 | 50 | // 被共用的缓存 51 | const cache = new Map() 52 | 53 | const generateArray = length => { 54 | return Array.from({ length }, (_, i) => i) 55 | } 56 | 57 | const useArray1 = useMemoize(generateArray, { cache }) 58 | const useArray2 = useMemoize(generateArray, { cache }) 59 | 60 | const a1 = useArray1(1) // 使用长度为 1 的新数组 61 | 62 | const a2 = useArray2(1) // 使用缓存里长度为 1 的数组 63 | ``` 64 | 65 |
66 | 67 | ### 自定义 Key 68 | 69 | 默认情况下,`useMemoize` 会自动根据函数所有的参数作为缓存的 `key` 70 | 71 | ```ts 72 | import { useMemoize } from '@/uni_modules/tob-use' 73 | 74 | // 生成数组,但可以传入第二个参数递增倍数 75 | const generateArray = (length, multiple = 1) => { 76 | return Array.from({ length }, (_, i) => i * multiple) 77 | } 78 | const useArray = useMemoize(generateArray, { 79 | getKey(length, multiple) { 80 | return length // 默认以所有参数为 key,但这里只让第一个参数为 key 81 | } 82 | }) 83 | 84 | const a1 = useArray(10, 2) // 使用长度为 10,2 倍递增的新数组 85 | 86 | // 即使设置了 3 倍递增,但是我们只以第一参数为 key,所以仍然复用缓存 87 | const a2 = useArray(10, 3) 88 | ``` -------------------------------------------------------------------------------- /docs/api/media/createAudio.md: -------------------------------------------------------------------------------- 1 | # createAudio 2 | 3 | :::warning 4 | 暂时不支持小程序端 5 | ::: 6 | 7 | 创建音频 8 | 9 | ## Usage 10 | 11 | ### 基础 12 | 13 | ```ts 14 | import { createAudio } from '@/uni_modules/tob-use' 15 | 16 | // createAudio 可接受所有的原生配置,例如 src, autoplay, loop 等 17 | const { toggle } = createAudio({ 18 | src: 'your src' 19 | }) 20 | 21 | toggle() // 播放,开 22 | toggle() // 暂停,关 23 | toggle() // 播放,开 24 | // ... 不断对当前状态取反 25 | 26 | toggle(false) // 暂停 27 | toggle(true) // 播放 28 | 29 | toggle(true, 'new src') // 播放新的音频 30 | ``` 31 | 32 |
33 | 34 | ### 其他 35 | 36 | ```js 37 | import { createAudio } from '@/uni_modules/tob-use' 38 | 39 | const { 40 | play, 41 | stop, 42 | seek, 43 | reset, 44 | audio, 45 | pause, 46 | replay, 47 | destroy, 48 | buffered, 49 | isActive, 50 | duration, 51 | isWaiting, 52 | currentTime, 53 | } = createAudio() 54 | 55 | play('your src') // 播放 56 | 57 | pause() // 暂停 58 | 59 | seek(20) // 跳转到对应位置 60 | 61 | reset() // 重置 62 | 63 | replay() // 重播 64 | 65 | stop() // 停止 66 | 67 | destroy() // 销毁 68 | 69 | isActive.value // 是否播放中 70 | 71 | isWaiting.value // 是否加载数据中 72 | 73 | duration.value // 总时长 74 | 75 | currentTime.value // 当前进度 76 | 77 | buffered.value // 当前缓冲点 78 | 79 | audio // 原生 audio 对象 80 | ``` 81 | 82 |
83 |
84 | 85 | ## 注意事项 86 | 87 | ### changing 88 | 89 | 结合原生的 `slider` 组件的 `changing` 时,为了更好的性能,你需要注意 `seek` 的使用 90 | 91 | ```html 92 | 104 | 105 | 110 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/useMemoize/README.md: -------------------------------------------------------------------------------- 1 | # useMemoize 2 | 3 | 使用备份 4 | 5 | 即输入相同参数的情况下,直接走缓存。 6 | 7 | ## Usage 8 | 9 | ### 基础 10 | 11 | ```js 12 | import { useMemoize } from '@/uni_modules/tob-use' 13 | 14 | // 生成数组 15 | const generateArray = length => { 16 | return Array.from({ length }, (_, i) => i) 17 | } 18 | 19 | const useArray = useMemoize( 20 | length => { 21 | return Array.from({ length }, (_, i) => i) 22 | } 23 | ) 24 | 25 | const a1 = useArray(1) // 使用长度为 1 的新数组 26 | const a2 = useArray(2) // 使用长度为 2 的新数组 27 | 28 | const a3 = useArray(1) // 从缓存里拿到之前长度为 1 的数组 29 | 30 | 31 | const a4 = useArray.load(1) // 重新生成数组长度为 1 的新数组(即不用缓存里的) 32 | 33 | useArray.cache // 当前缓存,一个 reactive 的 map 34 | 35 | useArray.delete(1) // 将数组长度为 1 的数组从缓存中删除 36 | useArray.clear() // 清除所有缓存 37 | ``` 38 | 39 |
40 | 41 | ### 缓存 42 | 43 | 大多数情况你不需要手动生成缓存,`useMemoize` 内部会自动生成 `reactive` 的 `map` 缓存。 44 | 45 | 当然有时候我们希望让两个 `useMemoize` 共用缓存。那么可以这样 👇 46 | 47 | ```ts 48 | import { useMemoize } from '@/uni_modules/tob-use' 49 | 50 | // 被共用的缓存 51 | const cache = new Map() 52 | 53 | const generateArray = length => { 54 | return Array.from({ length }, (_, i) => i) 55 | } 56 | 57 | const useArray1 = useMemoize(generateArray, { cache }) 58 | const useArray2 = useMemoize(generateArray, { cache }) 59 | 60 | const a1 = useArray1(1) // 使用长度为 1 的新数组 61 | 62 | const a2 = useArray2(1) // 使用缓存里长度为 1 的数组 63 | ``` 64 | 65 |
66 | 67 | ### 自定义 Key 68 | 69 | 默认情况下,`useMemoize` 会自动根据函数所有的参数作为缓存的 `key` 70 | 71 | ```ts 72 | import { useMemoize } from '@/uni_modules/tob-use' 73 | 74 | // 生成数组,但可以传入第二个参数递增倍数 75 | const generateArray = (length, multiple = 1) => { 76 | return Array.from({ length }, (_, i) => i * multiple) 77 | } 78 | const useArray = useMemoize(generateArray, { 79 | getKey(length, multiple) { 80 | return length // 默认以所有参数为 key,但这里只让第一个参数为 key 81 | } 82 | }) 83 | 84 | const a1 = useArray(10, 2) // 使用长度为 10,2 倍递增的新数组 85 | 86 | // 即使设置了 3 倍递增,但是我们只以第一参数为 key,所以仍然复用缓存 87 | const a2 = useArray(10, 3) 88 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/media/createAudio/README.md: -------------------------------------------------------------------------------- 1 | # createAudio 2 | 3 | :::warning 4 | 暂时不支持小程序端 5 | ::: 6 | 7 | 创建音频 8 | 9 | ## Usage 10 | 11 | ### 基础 12 | 13 | ```ts 14 | import { createAudio } from '@/uni_modules/tob-use' 15 | 16 | // createAudio 可接受所有的原生配置,例如 src, autoplay, loop 等 17 | const { toggle } = createAudio({ 18 | src: 'your src' 19 | }) 20 | 21 | toggle() // 播放,开 22 | toggle() // 暂停,关 23 | toggle() // 播放,开 24 | // ... 不断对当前状态取反 25 | 26 | toggle(false) // 暂停 27 | toggle(true) // 播放 28 | 29 | toggle(true, 'new src') // 播放新的音频 30 | ``` 31 | 32 |
33 | 34 | ### 其他 35 | 36 | ```js 37 | import { createAudio } from '@/uni_modules/tob-use' 38 | 39 | const { 40 | play, 41 | stop, 42 | seek, 43 | reset, 44 | audio, 45 | pause, 46 | replay, 47 | destroy, 48 | buffered, 49 | isActive, 50 | duration, 51 | isWaiting, 52 | currentTime, 53 | } = createAudio() 54 | 55 | play('your src') // 播放 56 | 57 | pause() // 暂停 58 | 59 | seek(20) // 跳转到对应位置 60 | 61 | reset() // 重置 62 | 63 | replay() // 重播 64 | 65 | stop() // 停止 66 | 67 | destroy() // 销毁 68 | 69 | isActive.value // 是否播放中 70 | 71 | isWaiting.value // 是否加载数据中 72 | 73 | duration.value // 总时长 74 | 75 | currentTime.value // 当前进度 76 | 77 | buffered.value // 当前缓冲点 78 | 79 | audio // 原生 audio 对象 80 | ``` 81 | 82 |
83 |
84 | 85 | ## 注意事项 86 | 87 | ### changing 88 | 89 | 结合原生的 `slider` 组件的 `changing` 时,为了更好的性能,你需要注意 `seek` 的使用 90 | 91 | ```html 92 | 104 | 105 | 110 | ``` -------------------------------------------------------------------------------- /docs/api/utilities/controlledRef.md: -------------------------------------------------------------------------------- 1 | # controlledRef 2 | 3 | 受控型 ref 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { computed } from 'vue' 11 | import { controlledRef } from '@/uni_modules/tob-use' 12 | 13 | let num = controlledRef(0) 14 | const doubled = computed(() => num.value * 2) 15 | 16 | // 原生行为 17 | num.value = 42 18 | console.log(num.value) // 42 19 | console.log(doubled.value) // 84 20 | 21 | // 设置值,但是不触发更新 (第二参数设置为 false 时) 22 | num.set(30, false) 23 | console.log(num.value) // 30 24 | console.log(doubled.value) // 84 (仍然是 84,不会更新) 25 | 26 | // 偷窥,获取值,但不 track 收集副作用 27 | watchEffect(() => { 28 | console.log(num.peek()) // 30 29 | }) 30 | 31 | // 即使更新了依赖 32 | // 也不会 trigger 触发上边的 watchEffect 注册的副作用回调 33 | num.value = 50 34 | console.log(doubled.value) // 100 35 | ``` 36 | 37 |
38 | 39 | ### 静默 40 | 41 | 有时候我们不希望 `getter` 访问时 `track` 收集副作用,或 `setter` 设置时 `trigger` 触发副作用。 42 | 43 | ```ts 44 | import { controlledRef } from '@/uni_modules/tob-use' 45 | 46 | const foo = controlledRef('foo') 47 | ``` 48 | 49 | ```ts 50 | // 静默访问,以下 api 等效,选择其一即可 51 | foo.get(false) 52 | foo.untrackedGet() 53 | foo.peek() 54 | ``` 55 | 56 | ```ts 57 | // 静默设置,以下 api 等效,选择其一即可 58 | foo.set('bar', false) 59 | foo.silentSet('bar') 60 | foo.lay('bar') 61 | ``` 62 | 63 |
64 | 65 | ### 配置 66 | 67 | #### onBeforeChange 68 | 69 | 配置 `onBeforeChange` 可以判断是否接受新值 70 | 71 | ```ts 72 | import { controlledRef } from '@/uni_modules/tob-use' 73 | 74 | const num = controlledRef(0, { 75 | onBeforeChange(value, oldValue) { 76 | if (Math.abs(value - oldValue) > 5) { 77 | return false // 返回 false 时,该值被忽略 78 | } 79 | } 80 | }) 81 | 82 | num.value = 1 83 | console.log(num.value) // 1 84 | 85 | num.value = 7 86 | console.log(num.value) // 1 (新值被忽略了) 87 | ``` 88 | 89 | #### onChanged 90 | 91 | 类似原生的 `watch`,可以监听值的变更。 92 | 93 | ```ts 94 | import { controlledRef } from '@/uni_modules/tob-use' 95 | 96 | const num = controlledRef(0, { 97 | onChanged(value, oldValue) { 98 | console.log(value) 99 | } 100 | }) 101 | 102 | 103 | num.value = 1 // 打印 1 104 | 105 | num.value = 2 // 打印 2 106 | ``` 107 | 108 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/controlledRef/README.md: -------------------------------------------------------------------------------- 1 | # controlledRef 2 | 3 | 受控型 ref 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | ```js 10 | import { computed } from 'vue' 11 | import { controlledRef } from '@/uni_modules/tob-use' 12 | 13 | let num = controlledRef(0) 14 | const doubled = computed(() => num.value * 2) 15 | 16 | // 原生行为 17 | num.value = 42 18 | console.log(num.value) // 42 19 | console.log(doubled.value) // 84 20 | 21 | // 设置值,但是不触发更新 (第二参数设置为 false 时) 22 | num.set(30, false) 23 | console.log(num.value) // 30 24 | console.log(doubled.value) // 84 (仍然是 84,不会更新) 25 | 26 | // 偷窥,获取值,但不 track 收集副作用 27 | watchEffect(() => { 28 | console.log(num.peek()) // 30 29 | }) 30 | 31 | // 即使更新了依赖 32 | // 也不会 trigger 触发上边的 watchEffect 注册的副作用回调 33 | num.value = 50 34 | console.log(doubled.value) // 100 35 | ``` 36 | 37 |
38 | 39 | ### 静默 40 | 41 | 有时候我们不希望 `getter` 访问时 `track` 收集副作用,或 `setter` 设置时 `trigger` 触发副作用。 42 | 43 | ```ts 44 | import { controlledRef } from '@/uni_modules/tob-use' 45 | 46 | const foo = controlledRef('foo') 47 | ``` 48 | 49 | ```ts 50 | // 静默访问,以下 api 等效,选择其一即可 51 | foo.get(false) 52 | foo.untrackedGet() 53 | foo.peek() 54 | ``` 55 | 56 | ```ts 57 | // 静默设置,以下 api 等效,选择其一即可 58 | foo.set('bar', false) 59 | foo.silentSet('bar') 60 | foo.lay('bar') 61 | ``` 62 | 63 |
64 | 65 | ### 配置 66 | 67 | #### onBeforeChange 68 | 69 | 配置 `onBeforeChange` 可以判断是否接受新值 70 | 71 | ```ts 72 | import { controlledRef } from '@/uni_modules/tob-use' 73 | 74 | const num = controlledRef(0, { 75 | onBeforeChange(value, oldValue) { 76 | if (Math.abs(value - oldValue) > 5) { 77 | return false // 返回 false 时,该值被忽略 78 | } 79 | } 80 | }) 81 | 82 | num.value = 1 83 | console.log(num.value) // 1 84 | 85 | num.value = 7 86 | console.log(num.value) // 1 (新值被忽略了) 87 | ``` 88 | 89 | #### onChanged 90 | 91 | 类似原生的 `watch`,可以监听值的变更。 92 | 93 | ```ts 94 | import { controlledRef } from '@/uni_modules/tob-use' 95 | 96 | const num = controlledRef(0, { 97 | onChanged(value, oldValue) { 98 | console.log(value) 99 | } 100 | }) 101 | 102 | 103 | num.value = 1 // 打印 1 104 | 105 | num.value = 2 // 打印 2 106 | ``` 107 | 108 | -------------------------------------------------------------------------------- /uni_modules/tob-use/media/useAudio/index.js: -------------------------------------------------------------------------------- 1 | import { watch } from 'vue' 2 | import { createAudio } from '../createAudio' 3 | import { useCycleList } from '../../utilities/useCycleList' 4 | import { eagerComputed } from "../../utilities/eagerComputed" 5 | 6 | /** 7 | * 正确化时间 8 | */ 9 | const normalizeTime = time => { 10 | if (time === Infinity) return '00:00' 11 | 12 | const division = Math.floor(time / 60) 13 | const remainder = Math.floor(time % 60) 14 | const zero = x => '0'.repeat(2 - String(x).length) 15 | return `${zero(division) + division}:${ 16 | zero(remainder) + remainder 17 | }` 18 | } 19 | 20 | /** 21 | * 使用 audio 22 | */ 23 | export const useAudio = (list = [], options = {}) => { 24 | const { 25 | play, 26 | stop, 27 | seek, 28 | reset, 29 | audio, 30 | pause, 31 | error, 32 | toggle, 33 | replay, 34 | destroy, 35 | buffered, 36 | duration, 37 | isActive, 38 | isWaiting, 39 | currentTime 40 | } = createAudio({ 41 | src: list[0], 42 | ...options 43 | }) 44 | 45 | const { state, next, prev, index } = useCycleList(list) 46 | 47 | watch(state, src => play(src)) 48 | 49 | // 需要重置 50 | const shouldReset = list.length === 1 51 | // 需要重新播放 52 | const shouldReplay = shouldReset 53 | 54 | audio.onEnded(() => (shouldReset ? reset() : next())) 55 | 56 | // 规范后的总时长 57 | const normalizedDuration = eagerComputed(() => { 58 | return normalizeTime(duration.value) 59 | }) 60 | 61 | // 规范化后的进度 62 | const normalizedCurrentTime = eagerComputed(() => { 63 | return normalizeTime(currentTime.value) 64 | }) 65 | 66 | return { 67 | play, 68 | stop, 69 | seek, 70 | audio, 71 | error, 72 | pause, 73 | index, 74 | toggle, 75 | replay, 76 | destroy, 77 | buffered, 78 | duration, 79 | isActive, 80 | isWaiting, 81 | currentTime, 82 | normalizedDuration, 83 | normalizedCurrentTime, 84 | src: state, 85 | next: n => { 86 | if (shouldReplay) { 87 | return replay() 88 | } else { 89 | reset() 90 | next(n) 91 | } 92 | }, 93 | prev: n => { 94 | if (shouldReplay) { 95 | return replay() 96 | } else { 97 | reset() 98 | prev(n) 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /uni_modules/tob-use/utilities/asyncComputed/index.js: -------------------------------------------------------------------------------- 1 | import { noop } from '../../shared/base' 2 | import { ref, computed, isRef } from 'vue' 3 | 4 | /** 5 | * 异步的计算属性 6 | */ 7 | export const asyncComputed = ( 8 | evaluationCallback, 9 | initialState, 10 | optionsOrRef 11 | ) => { 12 | const options = formatOptions(optionsOrRef) 13 | const { 14 | lazy = false, // 懒处理 15 | onError = noop, // 错误处理 16 | evaluating = undefined // 异步是否在调用中 17 | } = options 18 | 19 | const started = ref(!lazy) 20 | const current = ref(initialState) 21 | let counter = 0 // 计数器,用来协调异步调用次数 22 | 23 | watchEffect(async onInvalidate => { 24 | if (!started.value) { 25 | return 26 | } 27 | 28 | counter++ 29 | 30 | const counterAtBeginning = counter 31 | 32 | let hasFinished = false 33 | 34 | if (evaluating) { 35 | runNextMicroTask(() => { 36 | evaluating.value = true 37 | }) 38 | } 39 | 40 | try { 41 | const result = await evaluationCallback( 42 | // 副作用清除函数 43 | cancelCallback => { 44 | onInvalidate(() => { 45 | if (evaluating) { 46 | evaluating.value = false 47 | } 48 | if (!hasFinished) { 49 | cancelCallback() 50 | } 51 | }) 52 | } 53 | ) 54 | 55 | // 当计算器与当前的调用相同时才允许更新 56 | // 防止多次调用异步触发重复的更新 57 | const shouldUpdate = counterAtBeginning === counter 58 | if (shouldUpdate) { 59 | current.value = result 60 | } 61 | } catch (e) { 62 | // 错误处理,默认为 noop 不处理 63 | onError(e) 64 | } finally { 65 | // 完成后 66 | if (evaluating) { 67 | evaluating.value = false 68 | } 69 | hasFinished = true 70 | } 71 | }) 72 | 73 | // lazy 选项开启时,将在第一次 getter 访问时触发 74 | if (lazy) { 75 | return computed(() => { 76 | started.value = true 77 | return current.value 78 | }) 79 | } 80 | 81 | return current 82 | } 83 | 84 | /** 85 | * 格式化选项 86 | */ 87 | const formatOptions = optionsOrRef => { 88 | let options 89 | if (isRef(optionsOrRef)) { 90 | options = { evaluating: optionsOrRef } 91 | } else { 92 | options = optionsOrRef || {} 93 | } 94 | return options 95 | } 96 | 97 | /** 98 | * 运行在下次微任务 99 | */ 100 | const runNextMicroTask = cb => Promise.resolve().then(cb) 101 | -------------------------------------------------------------------------------- /scripts/remove.js: -------------------------------------------------------------------------------- 1 | const { 2 | noticeFail, 3 | noticeSuccess 4 | } = require('./shared/log') 5 | const { getModulesName } = require('./shared/modules') 6 | const { 7 | useInquirerQuestion, 8 | useInquirerConfirm, 9 | useInquirerList 10 | } = require('@markthree/ilazy') 11 | const { 12 | remove, 13 | writeJson, 14 | createPath, 15 | pathExistsSync 16 | } = require('@markthree/node-shared') 17 | const pagesJson = require('../pages.json') 18 | 19 | const p = createPath(__dirname) 20 | 21 | const autoRemove = async () => { 22 | const type = await useInquirerList( 23 | '请选择需要删除的模块类型', 24 | { choices: ['api', 'page'] } 25 | ) 26 | 27 | const n = getModulesName(type) 28 | const sort = await useInquirerList( 29 | `请选择该${n}属于的种类`, 30 | { choices: ['utilities'] } 31 | ) 32 | 33 | const name = await useInquirerQuestion( 34 | `请输入该${n}的名字` 35 | ) 36 | 37 | const isApi = type === 'api' 38 | if (isApi) { 39 | return await removeApi({ name, type, sort }) 40 | } 41 | 42 | const isPage = type === 'page' 43 | if (isPage) { 44 | return await removePage({ name, type, sort }) 45 | } 46 | } 47 | 48 | // 删除api 49 | const removeApi = async (options = {}) => { 50 | const { name, sort } = options 51 | const src = p(`../uni_modules/tob-use/${sort}/${name}`) 52 | const shouldRemove = await isWillRemove(src, 'api') 53 | if (shouldRemove) { 54 | await remove(src) 55 | return noticeSuccess('删除') 56 | } 57 | noticeFail('删除') 58 | } 59 | 60 | // 删除页面 61 | const removePage = async (options = {}) => { 62 | const { sort, name } = options 63 | const src = p(`../pages/${sort}/${name}`) 64 | const shouldRemove = await isWillRemove(src, '页面') 65 | if (shouldRemove) { 66 | const path = `pages/${sort}/${name}/${name}` 67 | pagesJson.pages = pagesJson.pages.filter(page => { 68 | return page.path !== path 69 | }) 70 | await remove(src) 71 | await writeJson(p('../pages.json'), pagesJson, { 72 | spaces: '\t' 73 | }) 74 | return noticeSuccess('删除') 75 | } 76 | noticeFail('删除') 77 | } 78 | 79 | // 判断是否要删除 80 | const isWillRemove = async (src, type = '文件') => { 81 | if (pathExistsSync(src)) { 82 | const isRemove = await useInquirerConfirm( 83 | '再次确认是否删除' 84 | ) 85 | return isRemove 86 | } 87 | console.log(`该${type}不存在`) 88 | return false 89 | } 90 | 91 | autoRemove() 92 | -------------------------------------------------------------------------------- /docs/api/component/useVModel.md: -------------------------------------------------------------------------------- 1 | # useVModel 2 | 3 | 使用 v-model 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | 子组件 👇 10 | 11 | ```html 12 | 13 | 30 | ``` 31 | 32 | 父组件 👇 33 | 34 | ```html 35 | 40 | 41 | 46 | ``` 47 | 48 |
49 | 50 | ### key 51 | 52 | 我们可以通过第二个参数设置 `key` 👇 53 | 54 | ```html 55 | 56 | 74 | ``` 75 | 76 | 父组件 👇 77 | 78 | ```html 79 | 80 | 86 | ``` 87 | 88 | 89 |
90 | 91 | ### 选项 92 | 93 | 第四个参数可以设置一些选项,不过大多数情况下,你并不需要关注 94 | 95 | ```html 96 | 112 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/component/useVModel/README.md: -------------------------------------------------------------------------------- 1 | # useVModel 2 | 3 | 使用 v-model 4 | 5 | ## Usage 6 | 7 | ### 基础 8 | 9 | 子组件 👇 10 | 11 | ```html 12 | 13 | 30 | ``` 31 | 32 | 父组件 👇 33 | 34 | ```html 35 | 40 | 41 | 46 | ``` 47 | 48 |
49 | 50 | ### key 51 | 52 | 我们可以通过第二个参数设置 `key` 👇 53 | 54 | ```html 55 | 56 | 74 | ``` 75 | 76 | 父组件 👇 77 | 78 | ```html 79 | 80 | 86 | ``` 87 | 88 | 89 |
90 | 91 | ### 选项 92 | 93 | 第四个参数可以设置一些选项,不过大多数情况下,你并不需要关注 94 | 95 | ```html 96 | 112 | ``` -------------------------------------------------------------------------------- /docs/api/media/useAudio.md: -------------------------------------------------------------------------------- 1 | # useAudio 2 | 3 | :::warning 4 | 暂时不支持小程序端 5 | ::: 6 | 7 | 使用音频 8 | 9 | ## Usage 10 | 11 | ### 基础 12 | 13 | ```ts 14 | import { useAudio } from '@/uni_modules/tob-use' 15 | 16 | // 播放列表 17 | const playlist = ['src1', 'src2', '...'] 18 | 19 | const { 20 | prev, 21 | next, 22 | toggle, 23 | } = useAudio(playlist) 24 | 25 | toggle() // 播放,开 26 | toggle() // 暂停,关 27 | toggle() // 播放,开 28 | // ... 不断对当前状态取反 29 | 30 | toggle(false) // 暂停 31 | toggle(true) // 播放 32 | 33 | toggle(true, 'new src') // 播放新的音频 34 | 35 | prev() // 上一首 36 | next() // 下一首 37 | 38 | prev(2) // 上上首 39 | next(2) // 下下首 40 | ``` 41 | 42 |
43 | 44 | ### 其他 45 | 46 | ```js 47 | import { useAudio } from '@/uni_modules/tob-use' 48 | 49 | // 播放列表 50 | const playlist = ['src1', 'src2', '...'] 51 | 52 | // useAudio 的第二参数可接受所有的原生配置,例如 src, autoplay, loop 等 53 | const { 54 | src, 55 | play, 56 | stop, 57 | seek, 58 | reset, 59 | index, 60 | audio, 61 | pause, 62 | replay, 63 | destroy, 64 | buffered, 65 | isActive, 66 | duration, 67 | isWaiting, 68 | currentTime, 69 | normalizedDuration, 70 | normalizedCurrentTime 71 | } = useAudio(playlist, { 72 | autoplay: true // 设置自动播放 73 | }) 74 | 75 | play('your src') // 播放 76 | 77 | pause() // 暂停 78 | 79 | seek(20) // 跳转到对应位置 80 | 81 | reset() // 重置 82 | 83 | replay() // 重播 84 | 85 | stop() // 停止 86 | 87 | destroy() // 销毁 88 | 89 | isActive.value // 是否播放中 90 | 91 | isWaiting.value // 是否加载数据中 92 | 93 | duration.value // 总时长 94 | 95 | currentTime.value // 当前进度 96 | 97 | buffered.value // 当前缓冲点 98 | 99 | src.value // 当前播放的音频地址 100 | 101 | index.value // 当前播放的 index 102 | 103 | audio // 原生 audio 对象 104 | 105 | normalizedDuration.value // 规范化后的时长,即 00:00 格式 106 | normalizedCurrentTime.value // 规范化后的当前进度,即 00:00 格式 107 | ``` 108 | 109 |
110 |
111 | 112 | ## 注意事项 113 | 114 | ### changing 115 | 116 | 结合原生的 `slider` 组件的 `changing` 时,为了更好的性能,你需要注意 `seek` 的使用 117 | 118 | ```html 119 | 131 | 132 | 137 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/ignorableWatch/index.js: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { 3 | bypassFilter, 4 | createFilterWrapper 5 | } from '../../shared/filters' 6 | import { noop } from '../../shared/base' 7 | 8 | /** 9 | * 可忽略的监听 10 | */ 11 | export const ignorableWatch = ( 12 | source, 13 | cb, 14 | options = {} 15 | ) => { 16 | const { eventFilter = bypassFilter, ...watchOptions } = 17 | options 18 | 19 | const filteredCb = createFilterWrapper(eventFilter, cb) 20 | 21 | let stop 22 | let ignoreUpdates 23 | let ignorePrevAsyncUpdates 24 | 25 | const syncFlush = watchOptions.flush === 'sync' 26 | if (syncFlush) { 27 | const ignore = ref(false) 28 | 29 | ignorePrevAsyncUpdates = noop 30 | 31 | // syncFlush 同步更新时,直接加锁进行更新 32 | ignoreUpdates = updater => { 33 | ignore.value = true 34 | updater() 35 | ignore.value = false 36 | } 37 | 38 | stop = watch( 39 | source, 40 | (...args) => { 41 | if (!ignore.value) filteredCb(...args) 42 | }, 43 | watchOptions 44 | ) 45 | } else { 46 | // 临时数组,用来存储 watch 之后的 stop 47 | const disposables = [] 48 | 49 | const syncCounter = ref(0) // 同步计数器 50 | const ignoreCounter = ref(0) // 忽略计数器 51 | 52 | // 忽略上一次同步更新(使得忽略计数器与同步计数器相同) 53 | ignorePrevAsyncUpdates = () => { 54 | ignoreCounter.value = syncCounter.value 55 | } 56 | 57 | // 监听源更新,递增同步计数器,并收集其 stop 58 | disposables.push( 59 | watch( 60 | source, 61 | () => { 62 | syncCounter.value++ 63 | }, 64 | { ...watchOptions, flush: 'sync' } 65 | ) 66 | ) 67 | 68 | // 忽略更新 (调用回调的同时更新忽略计数器) 69 | ignoreUpdates = updater => { 70 | const syncCounterPrev = syncCounter.value 71 | updater() 72 | ignoreCounter.value += 73 | syncCounter.value - syncCounterPrev 74 | } 75 | 76 | // 重置所有计数器 77 | const resetAllCounter = () => { 78 | syncCounter.value = 0 79 | ignoreCounter.value = 0 80 | } 81 | 82 | // 监听监听源更新,判断是否忽略副作用,并收集其 stop 83 | disposables.push( 84 | watch( 85 | source, 86 | (...args) => { 87 | // 忽略计数器存在并与同步计数器相同时,需要忽略此次更新 88 | const ignore = 89 | ignoreCounter.value > 0 && 90 | ignoreCounter.value === syncCounter.value 91 | // 重置所有计数器 92 | resetAllCounter() 93 | if (ignore) return 94 | 95 | filteredCb(...args) 96 | }, 97 | watchOptions 98 | ) 99 | ) 100 | 101 | stop = () => { 102 | // stop 掉所有的副作用 103 | disposables.forEach(fn => fn()) 104 | } 105 | } 106 | 107 | return { stop, ignoreUpdates, ignorePrevAsyncUpdates } 108 | } 109 | -------------------------------------------------------------------------------- /uni_modules/tob-use/media/useAudio/README.md: -------------------------------------------------------------------------------- 1 | # useAudio 2 | 3 | :::warning 4 | 暂时不支持小程序端 5 | ::: 6 | 7 | 使用音频 8 | 9 | ## Usage 10 | 11 | ### 基础 12 | 13 | ```ts 14 | import { useAudio } from '@/uni_modules/tob-use' 15 | 16 | // 播放列表 17 | const playlist = ['src1', 'src2', '...'] 18 | 19 | const { 20 | prev, 21 | next, 22 | toggle, 23 | } = useAudio(playlist) 24 | 25 | toggle() // 播放,开 26 | toggle() // 暂停,关 27 | toggle() // 播放,开 28 | // ... 不断对当前状态取反 29 | 30 | toggle(false) // 暂停 31 | toggle(true) // 播放 32 | 33 | toggle(true, 'new src') // 播放新的音频 34 | 35 | prev() // 上一首 36 | next() // 下一首 37 | 38 | prev(2) // 上上首 39 | next(2) // 下下首 40 | ``` 41 | 42 |
43 | 44 | ### 其他 45 | 46 | ```js 47 | import { useAudio } from '@/uni_modules/tob-use' 48 | 49 | // 播放列表 50 | const playlist = ['src1', 'src2', '...'] 51 | 52 | // useAudio 的第二参数可接受所有的原生配置,例如 src, autoplay, loop 等 53 | const { 54 | src, 55 | play, 56 | stop, 57 | seek, 58 | reset, 59 | index, 60 | audio, 61 | pause, 62 | replay, 63 | destroy, 64 | buffered, 65 | isActive, 66 | duration, 67 | isWaiting, 68 | currentTime, 69 | normalizedDuration, 70 | normalizedCurrentTime 71 | } = useAudio(playlist, { 72 | autoplay: true // 设置自动播放 73 | }) 74 | 75 | play('your src') // 播放 76 | 77 | pause() // 暂停 78 | 79 | seek(20) // 跳转到对应位置 80 | 81 | reset() // 重置 82 | 83 | replay() // 重播 84 | 85 | stop() // 停止 86 | 87 | destroy() // 销毁 88 | 89 | isActive.value // 是否播放中 90 | 91 | isWaiting.value // 是否加载数据中 92 | 93 | duration.value // 总时长 94 | 95 | currentTime.value // 当前进度 96 | 97 | buffered.value // 当前缓冲点 98 | 99 | src.value // 当前播放的音频地址 100 | 101 | index.value // 当前播放的 index 102 | 103 | audio // 原生 audio 对象 104 | 105 | normalizedDuration.value // 规范化后的时长,即 00:00 格式 106 | normalizedCurrentTime.value // 规范化后的当前进度,即 00:00 格式 107 | ``` 108 | 109 |
110 |
111 | 112 | ## 注意事项 113 | 114 | ### changing 115 | 116 | 结合原生的 `slider` 组件的 `changing` 时,为了更好的性能,你需要注意 `seek` 的使用 117 | 118 | ```html 119 | 131 | 132 | 137 | ``` -------------------------------------------------------------------------------- /uni_modules/tob-use/watch/until/index.js: -------------------------------------------------------------------------------- 1 | import { unref, watch } from 'vue' 2 | import { promiseTimeout } from '../../shared/base' 3 | 4 | /** 5 | * 直到 6 | */ 7 | export const until = r => { 8 | let isNot = false 9 | 10 | // 匹配,接收回调 condition,返回 false 即不匹配 11 | const toMatch = (condition, options = {}) => { 12 | let { 13 | timeout, 14 | deep = false, 15 | flush = 'sync', 16 | throwOnTimeout 17 | } = options 18 | let stop = null 19 | const watcher = new Promise(resolve => { 20 | stop = watch( 21 | r, 22 | v => { 23 | if (condition(v) === !isNot) { 24 | stop?.() 25 | resolve() 26 | } 27 | }, 28 | { 29 | flush, 30 | deep, 31 | immediate: true 32 | } 33 | ) 34 | }) 35 | 36 | const promises = [watcher] 37 | if (timeout) { 38 | promises.push( 39 | promiseTimeout(timeout, throwOnTimeout).finally( 40 | () => stop?.() 41 | ) 42 | ) 43 | } 44 | 45 | return Promise.race(promises) 46 | } 47 | 48 | // 是否为 第一参数 value 49 | const toBe = (value, options) => 50 | toMatch(v => v === unref(value), options) 51 | 52 | // 是否为 truthy 53 | const toBeTruthy = options => 54 | toMatch(v => Boolean(v), options) 55 | 56 | // 是否为 null 57 | const toBeNull = options => toBe(null, options) 58 | 59 | // 是否未定义 60 | const toBeUndefined = options => toBe(undefined, options) 61 | 62 | // 是否为 NaN 63 | const toBeNaN = options => toMatch(Number.isNaN, options) 64 | 65 | // 是否为指定类数组内的值 66 | const toContains = (value, options) => { 67 | return toMatch(v => { 68 | const array = Array.from(v) 69 | return ( 70 | array.includes(value) || 71 | array.includes(unref(value)) 72 | ) 73 | }, options) 74 | } 75 | 76 | // 是否变更了 77 | const changed = options => changedTimes(1, options) 78 | 79 | // 是否变更了 n 次 80 | const changedTimes = (n = 1, options) => { 81 | let count = -1 82 | return toMatch(() => { 83 | count += 1 84 | return count >= n 85 | }, options) 86 | } 87 | 88 | if (Array.isArray(unref(r))) { 89 | const instance = { 90 | toMatch, 91 | toContains, 92 | changed, 93 | changedTimes, 94 | get not() { 95 | isNot = !isNot 96 | return this 97 | } 98 | } 99 | return instance 100 | } else { 101 | const instance = { 102 | toMatch, 103 | toBe, 104 | toBeTruthy, 105 | toBeNull, 106 | toBeNaN, 107 | toBeUndefined, 108 | changed, 109 | changedTimes, 110 | get not() { 111 | isNot = !isNot 112 | return this 113 | } 114 | } 115 | 116 | return instance 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /uni_modules/tob-use/state/useStorage/index.js: -------------------------------------------------------------------------------- 1 | import { ref, shallowRef, unref } from 'vue' 2 | import { pausableWatch } from '../../watch/pausableWatch' 3 | import { guessSerializerType } from './guess' 4 | 5 | export const StorageSerializers = { 6 | boolean: { 7 | read: (v) => v === 'true', 8 | write: (v) => String(v) 9 | }, 10 | object: { 11 | read: (v) => JSON.parse(v), 12 | write: (v) => JSON.stringify(v) 13 | }, 14 | number: { 15 | read: (v) => Number.parseFloat(v), 16 | write: (v) => String(v) 17 | }, 18 | any: { 19 | read: (v) => v, 20 | write: (v) => String(v) 21 | }, 22 | string: { 23 | read: (v) => v, 24 | write: (v) => String(v) 25 | }, 26 | map: { 27 | read: (v) => new Map(JSON.parse(v)), 28 | write: (v) => JSON.stringify(Array.from(v.entries())) 29 | }, 30 | set: { 31 | read: (v) => new Set(JSON.parse(v)), 32 | write: (v) => JSON.stringify(Array.from(v)) 33 | }, 34 | date: { 35 | read: (v) => new Date(v), 36 | write: (v) => v.toISOString() 37 | } 38 | } 39 | 40 | export function useStorage(key, initialValue, options = {}) { 41 | const { 42 | flush = 'pre', 43 | deep = true, 44 | writeDefaults = true, 45 | shallow, 46 | eventFilter, 47 | onError = (e) => { 48 | console.error(e) 49 | } 50 | } = options 51 | const data = (shallow ? shallowRef : ref)(initialValue) 52 | const rawInit = unref(initialValue) 53 | const type = guessSerializerType(rawInit) 54 | const serializer = StorageSerializers[type] 55 | 56 | const { pause, resume } = pausableWatch(data, () => write(data.value), { 57 | flush, 58 | deep, 59 | eventFilter 60 | }) 61 | 62 | update() 63 | 64 | return data 65 | 66 | function write(v) { 67 | try { 68 | if (v == null) { 69 | uni.removeStorageSync(key) 70 | } else { 71 | uni.setStorageSync(key, serializer.write(v)) 72 | } 73 | } catch (e) { 74 | onError(e) 75 | } 76 | } 77 | 78 | function read(event) { 79 | if (event && event.key !== key) { 80 | return 81 | } 82 | 83 | pause() 84 | 85 | try { 86 | const rawValue = event ? event.newValue : uni.getStorageSync(key) 87 | if (rawValue == null || rawValue == '') { 88 | if (writeDefaults && rawInit !== null) { 89 | uni.setStorageSync(key, serializer.write(rawInit)) 90 | } 91 | return rawInit 92 | } else if (typeof rawValue !== 'string') { 93 | return rawValue 94 | } else { 95 | return serializer.read(rawValue) 96 | } 97 | } catch (e) { 98 | onError(e) 99 | } finally { 100 | resume() 101 | } 102 | } 103 | 104 | function update(event) { 105 | if (event && event.key !== key) { 106 | return 107 | } 108 | data.value = read(event) 109 | } 110 | } 111 | --------------------------------------------------------------------------------