├── .commitlintrc.cjs
├── .czrc
├── .editorconfig
├── .github
└── workflows
│ ├── ci.yml
│ ├── deploy-docs.yml
│ └── release.yml
├── .gitignore
├── .lintstagedrc.cjs
├── .npmrc
├── .nvmrc
├── .simple-git-hooks.cjs
├── .vitepress
├── components
│ └── HomeTeam.vue
├── config.ts
└── theme
│ ├── index.ts
│ └── style.css
├── .vscode
├── extensions.json
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.config.ts
├── docs
├── guide
│ ├── index.md
│ └── notice.md
└── index.md
├── eslint.config.js
├── package.json
├── playground
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── main.ts
│ ├── manifest.json
│ ├── pages.json
│ ├── pages
│ │ └── index
│ │ │ └── index.vue
│ └── static
│ │ └── logo.png
├── tsconfig.json
└── vite.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── public
└── logo.png
├── src
├── exports.ts
├── index.md
├── index.ts
├── tryOnBackPress
│ ├── index.md
│ └── index.ts
├── tryOnHide
│ ├── index.md
│ └── index.ts
├── tryOnInit
│ ├── index.md
│ └── index.ts
├── tryOnLoad
│ ├── index.md
│ └── index.ts
├── tryOnReady
│ ├── index.md
│ └── index.ts
├── tryOnScopeDispose
│ ├── index.md
│ └── index.ts
├── tryOnShow
│ ├── index.md
│ └── index.ts
├── tryOnUnload
│ ├── index.md
│ └── index.ts
├── types.ts
├── useActionSheet
│ ├── index.md
│ └── index.ts
├── useClipboardData
│ ├── index.md
│ └── index.ts
├── useDownloadFile
│ ├── index.md
│ └── index.ts
├── useGlobalData
│ ├── index.md
│ └── index.ts
├── useInterceptor
│ ├── index.md
│ ├── index.test.ts
│ └── index.ts
├── useLoading
│ ├── index.md
│ └── index.ts
├── useModal
│ ├── index.md
│ └── index.ts
├── useNetwork
│ ├── index.md
│ └── index.ts
├── useOnline
│ ├── index.md
│ └── index.ts
├── usePage
│ ├── index.md
│ └── index.ts
├── usePageScroll
│ ├── index.md
│ └── index.ts
├── usePages
│ ├── index.md
│ └── index.ts
├── usePreferredDark
│ ├── index.md
│ └── index.ts
├── usePreferredLanguage
│ ├── index.md
│ └── index.ts
├── usePrevPage
│ ├── index.md
│ └── index.ts
├── usePrevRoute
│ ├── index.md
│ └── index.ts
├── useProvider
│ ├── index.md
│ └── index.ts
├── useRequest
│ ├── index.md
│ └── index.ts
├── useRoute
│ ├── index.md
│ └── index.ts
├── useRouter
│ ├── index.md
│ └── index.ts
├── useScanCode
│ ├── index.md
│ └── index.ts
├── useScreenBrightness
│ ├── index.md
│ └── index.ts
├── useSelectorQuery
│ ├── index.md
│ └── index.ts
├── useSocket
│ ├── index.md
│ └── index.ts
├── useStorage
│ ├── index.md
│ ├── index.test.ts
│ └── index.ts
├── useStorageAsync
│ ├── index.md
│ └── index.ts
├── useStorageSync
│ ├── index.md
│ └── index.ts
├── useToast
│ ├── index.md
│ └── index.ts
├── useUploadFile
│ ├── index.md
│ └── index.ts
├── useVisible
│ ├── index.md
│ └── index.ts
├── utils.test.ts
└── utils.ts
├── test
├── index.ts
├── mount.ts
└── setup.ts
├── tsconfig.json
├── types
└── global.d.ts
└── vitest.config.ts
/.commitlintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional'],
3 | };
4 |
--------------------------------------------------------------------------------
/.czrc:
--------------------------------------------------------------------------------
1 | {
2 | "path": "@commitlint/prompt"
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 2
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | lint:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: pnpm/action-setup@v3
17 | - uses: actions/setup-node@v3
18 | with:
19 | node-version-file: .nvmrc
20 | cache: pnpm
21 | registry-url: https://registry.npmjs.org
22 | - run: corepack enable
23 | - run: pnpm install
24 | - run: pnpm run build
25 | - run: pnpm run lint
26 |
27 | typecheck:
28 | runs-on: ubuntu-latest
29 | steps:
30 | - uses: actions/checkout@v4
31 | - uses: pnpm/action-setup@v3
32 | - uses: actions/setup-node@v3
33 | with:
34 | node-version-file: .nvmrc
35 | cache: pnpm
36 | registry-url: https://registry.npmjs.org
37 | - run: corepack enable
38 | - run: pnpm install
39 | - run: pnpm run check:types
40 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-docs.yml:
--------------------------------------------------------------------------------
1 | # Sample workflow for building and deploying a VitePress site to GitHub Pages
2 | #
3 | name: Deploy VitePress site to Pages
4 |
5 | on:
6 | # Runs on pushes targeting the `main` branch. Change this to `master` if you're
7 | # using the `master` branch as the default branch.
8 | push:
9 | branches: [main]
10 |
11 | # Allows you to run this workflow manually from the Actions tab
12 | workflow_dispatch:
13 |
14 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
15 | permissions:
16 | contents: read
17 | pages: write
18 | id-token: write
19 |
20 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
21 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
22 | concurrency:
23 | group: pages
24 | cancel-in-progress: false
25 |
26 | jobs:
27 | # Build job
28 | build:
29 | runs-on: ubuntu-latest
30 | steps:
31 | - name: Checkout
32 | uses: actions/checkout@v4
33 | with:
34 | fetch-depth: 0 # Not needed if lastUpdated is not enabled
35 | - uses: pnpm/action-setup@v3
36 | - uses: actions/setup-node@v4
37 | with:
38 | node-version-file: .nvmrc
39 | cache: pnpm
40 | registry-url: https://registry.npmjs.org
41 | - name: Setup Pages
42 | uses: actions/configure-pages@v4
43 | - name: Install dependencies
44 | run: pnpm install # or pnpm install / yarn install / bun install
45 | - name: Build with VitePress
46 | run: pnpm run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build
47 | - name: Upload artifact
48 | uses: actions/upload-pages-artifact@v3
49 | with:
50 | path: .vitepress/dist
51 |
52 | # Deployment job
53 | deploy:
54 | environment:
55 | name: github-pages
56 | url: ${{ steps.deployment.outputs.page_url }}
57 | needs: build
58 | runs-on: ubuntu-latest
59 | name: Deploy
60 | steps:
61 | - name: Deploy to GitHub Pages
62 | id: deployment
63 | uses: actions/deploy-pages@v4
64 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | check:
13 | runs-on: ubuntu-latest
14 | outputs:
15 | version: ${{ steps.info.outputs.version }}
16 | exists: ${{ steps.checkTag.outputs.exists }}
17 | steps:
18 | - name: Checkout repository
19 | uses: actions/checkout@v4
20 |
21 | - name: Read package.json INFO
22 | id: info
23 | uses: jaywcjlove/github-action-package@main
24 |
25 | - run: echo "version - ${{ steps.info.outputs.version }}"
26 |
27 | - name: Check if tag exist
28 | id: checkTag
29 | uses: mukunku/tag-exists-action@v1.6.0
30 | if: steps.info.outputs.version != ''
31 | with:
32 | tag: 'v${{steps.info.outputs.version}}'
33 |
34 | - run: echo "exists - ${{ steps.checkTag.outputs.exists }}"
35 |
36 | release:
37 | runs-on: ubuntu-latest
38 | needs: check
39 | if: needs.check.outputs.exists == 'false'
40 |
41 | steps:
42 | - uses: actions/checkout@v4
43 | - uses: pnpm/action-setup@v3
44 | - uses: actions/setup-node@v3
45 | with:
46 | node-version-file: .nvmrc
47 | cache: pnpm
48 | registry-url: https://registry.npmjs.org
49 | - run: corepack enable
50 | - run: pnpm install
51 | - run: pnpm publish --access public --no-git-checks
52 | env:
53 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
54 |
55 | - name: Release
56 | uses: softprops/action-gh-release@v1
57 | with:
58 | tag_name: 'v${{needs.check.outputs.version}}'
59 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | pnpm-debug.log*
9 | run
10 |
11 | # Diagnostic reports (https://nodejs.org/api/report.html)
12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
13 |
14 | # Runtime data
15 | pids
16 | *.pid
17 | *.seed
18 | *.pid.lock
19 |
20 | # Directory for instrumented libs generated by jscoverage/JSCover
21 | lib-cov
22 |
23 | # Coverage directory used by tools like istanbul
24 | coverage
25 | *.lcov
26 |
27 | # nyc test coverage
28 | .nyc_output
29 |
30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
31 | .grunt
32 |
33 | # Bower dependency directory (https://bower.io/)
34 | bower_components
35 |
36 | # node-waf configuration
37 | .lock-wscript
38 |
39 | # Compiled binary addons (https://nodejs.org/api/addons.html)
40 | build/Release
41 |
42 | # Dependency directories
43 | node_modules
44 | jspm_packages
45 |
46 | # Snowpack dependency directory (https://snowpack.dev/)
47 | web_modules
48 |
49 | # Temp directory
50 | .temp
51 | .tmp
52 |
53 | # TypeScript cache
54 | *.tsbuildinfo
55 |
56 | # Optional npm cache directory
57 | .npm
58 |
59 | # Optional eslint cache
60 | .eslintcache
61 |
62 | # Microbundle cache
63 | .rpt2_cache
64 | .rts2_cache_cjs
65 | .rts2_cache_es
66 | .rts2_cache_umd
67 |
68 | # Optional REPL history
69 | .node_repl_history
70 |
71 | # Output of 'npm pack'
72 | *.tgz
73 |
74 | # Yarn Integrity file
75 | .yarn-integrity
76 |
77 | # dotenv environment variables file
78 | .env
79 | .env.test
80 | .env.*.test
81 | .env.local
82 | .env.*.local
83 | *.local
84 |
85 | # parcel-bundler cache (https://parceljs.org/)
86 | .cache
87 | .parcel-cache
88 |
89 | # Electron
90 | dist-electron
91 | dist_electron
92 |
93 | # Tauri
94 | dist-tauri
95 | dist_tauri
96 |
97 | # Cordova
98 | dist-cordova
99 | dist_cordova
100 |
101 | # Capacitor
102 | dist-capacitor
103 | dist_capacitor
104 |
105 | # Next.js build output
106 | .next
107 | out
108 |
109 | # Umi.js build output
110 | .umi
111 | .umi-production
112 | .umi-test
113 |
114 | # Nuxt.js build / generate output
115 | .nuxt
116 | dist
117 |
118 | # Rax.js build
119 | .rax
120 |
121 | # Vuepress build output
122 | .vuepress/dist
123 |
124 | # SSR
125 | dist-ssr
126 | dist_ssr
127 |
128 | # SSG
129 | dist-ssg
130 | dist_ssg
131 |
132 | # Serverless
133 | .serverless
134 | .dynamodb
135 | .s3
136 | .buckets
137 | .seeds
138 |
139 | # FuseBox cache
140 | .fusebox
141 |
142 | # TernJS port file
143 | .tern-port
144 |
145 | # Cypress
146 | /cypress/videos/
147 | /cypress/screenshots/
148 |
149 | # Editor
150 | .vscode-test
151 | .vscode/**
152 | !.vscode/extensions.json
153 | !.vscode/settings.json
154 | *.vsix
155 | .idea
156 | .hbuilder
157 | .hbuilderx
158 | *.suo
159 | *.ntvs*
160 | *.njsproj
161 | *.sln
162 | *.sw?
163 |
164 | # yarn v2
165 | .yarn/cache
166 | .yarn/unplugged
167 | .yarn/build-state.yml
168 | .yarn/install-state.gz
169 | .pnp.*
170 |
171 | # Apple
172 | .DS_Store
173 | *.p12
174 | *.mobileprovision
175 |
176 | # Android
177 | *.keystore
178 |
179 | # Vitepress
180 | cache
--------------------------------------------------------------------------------
/.lintstagedrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | '*.{js,cjs,mjs,ts,cts,mts,md}': () => [
3 | 'pnpm check:types',
4 | 'pnpm lint:eslint',
5 | 'pnpm lint:publint',
6 | ],
7 | };
8 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | registry=https://registry.npmjs.org/
3 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 20
2 |
--------------------------------------------------------------------------------
/.simple-git-hooks.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-template-curly-in-string */
2 | module.exports = {
3 | 'pre-commit': 'npx lint-staged',
4 | 'commit-msg': 'npx commitlint --edit ${1}',
5 | };
6 |
--------------------------------------------------------------------------------
/.vitepress/components/HomeTeam.vue:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 |
27 | 团队成员
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/.vitepress/config.ts:
--------------------------------------------------------------------------------
1 | import { dirname, join } from 'node:path';
2 | import fg from 'fast-glob';
3 | import { defineConfig } from 'vitepress';
4 |
5 | // https://vitepress.dev/reference/site-config
6 | export default async () => {
7 | const files = await fg('**/*.md', {
8 | cwd: join(__dirname, '..', 'src'),
9 | });
10 |
11 | const dirs = [...new Set(files.map(f => dirname(f)).filter(d => d !== '.' && d !== '..'))];
12 |
13 | return defineConfig({
14 | title: 'UniUse',
15 | description: 'uni-app (vue3) 组合式工具集。',
16 | lang: 'zh-CN',
17 | base: '/uni-use/',
18 | srcDir: '.',
19 | // srcExclude: ['!(docs|src)/**'],
20 | rewrites: {
21 | 'docs/:name.md': ':name.md',
22 | 'docs/:doc/:name.md': ':doc/:name.md',
23 | 'src/index.md': 'apis.md',
24 | 'src/:pkg/:name.md': ':pkg/:name.md',
25 | },
26 |
27 | themeConfig: {
28 | logo: '/logo.png',
29 |
30 | nav: [
31 | { text: '指南', link: '/guide/' },
32 | { text: 'API', link: '/apis/' },
33 | { text: 'ChangeLog', link: '/CHANGELOG.md' },
34 | ],
35 |
36 | sidebar: {
37 | '/guide/': [
38 | {
39 | text: '指南',
40 | items: [
41 | {
42 | text: '开始',
43 | link: '/guide/',
44 | },
45 | {
46 | text: '注意事项',
47 | link: '/guide/notice',
48 | },
49 | {
50 | text: '更新日志',
51 | link: '/CHANGELOG.md',
52 | },
53 | {
54 | text: '所有函数',
55 | link: '/apis/',
56 | },
57 | ],
58 | },
59 | ],
60 | '/': [
61 | {
62 | text: 'All Functions',
63 | items: [
64 | {
65 | text: '函数列表',
66 | link: '/apis/',
67 | },
68 | ],
69 | },
70 | {
71 | text: 'API',
72 | items: dirs.map(dir => ({
73 | text: dir,
74 | link: `/${dir}/`,
75 | })),
76 | },
77 | ],
78 | },
79 |
80 | editLink: {
81 | pattern: 'https://github.com/uni-helper/uni-use/tree/main/src/:path',
82 | text: '为这个页面提供建议',
83 | },
84 |
85 | footer: {
86 | message: 'Released under the MIT License.',
87 | copyright: 'Copyright © 2022-PRESENT uni-helper and uni-helper contributors',
88 | },
89 |
90 | docFooter: {
91 | prev: '上一页',
92 | next: '下一页',
93 | },
94 |
95 | outline: {
96 | label: '页面导航',
97 | },
98 |
99 | search: {
100 | provider: 'local',
101 | options: {
102 | locales: {
103 | 'zh-CN': {
104 | translations: {
105 | button: {
106 | buttonText: '搜索文档',
107 | buttonAriaLabel: '搜索文档',
108 | },
109 | modal: {
110 | noResultsText: '无法找到相关结果',
111 | resetButtonTitle: '清除查询条件',
112 | footer: {
113 | selectText: '选择',
114 | navigateText: '切换',
115 | closeText: '关闭',
116 | },
117 | },
118 | },
119 | },
120 | },
121 | },
122 | },
123 |
124 | lastUpdated: {
125 | text: '最后更新于',
126 | formatOptions: {
127 | dateStyle: 'short',
128 | timeStyle: 'medium',
129 | },
130 | },
131 |
132 | langMenuLabel: '多语言',
133 | returnToTopLabel: '回到顶部',
134 | sidebarMenuLabel: '菜单',
135 | darkModeSwitchLabel: '主题',
136 | lightModeSwitchTitle: '切换到浅色模式',
137 | darkModeSwitchTitle: '切换到深色模式',
138 |
139 | socialLinks: [
140 | { icon: 'github', link: 'https://github.com/uni-helper/uni-use' },
141 | ],
142 | },
143 |
144 | });
145 | };
146 |
--------------------------------------------------------------------------------
/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | // https://vitepress.dev/guide/custom-theme
2 | import type { Theme } from 'vitepress';
3 | import DefaultTheme from 'vitepress/theme';
4 | import { h } from 'vue';
5 | import HomeTeam from '../components/HomeTeam.vue';
6 | import './style.css';
7 |
8 | export default {
9 | extends: DefaultTheme,
10 | Layout: () => {
11 | return h(DefaultTheme.Layout, null, {
12 | // https://vitepress.dev/guide/extending-default-theme#layout-slots
13 | });
14 | },
15 | enhanceApp({ app }) {
16 | app.component('HomeTeam', HomeTeam);
17 | },
18 | } satisfies Theme;
19 |
--------------------------------------------------------------------------------
/.vitepress/theme/style.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Customize default theme styling by overriding CSS variables:
3 | * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
4 | */
5 |
6 | /**
7 | * Colors
8 | *
9 | * Each colors have exact same color scale system with 3 levels of solid
10 | * colors with different brightness, and 1 soft color.
11 | *
12 | * - `XXX-1`: The most solid color used mainly for colored text. It must
13 | * satisfy the contrast ratio against when used on top of `XXX-soft`.
14 | *
15 | * - `XXX-2`: The color used mainly for hover state of the button.
16 | *
17 | * - `XXX-3`: The color for solid background, such as bg color of the button.
18 | * It must satisfy the contrast ratio with pure white (#ffffff) text on
19 | * top of it.
20 | *
21 | * - `XXX-soft`: The color used for subtle background such as custom container
22 | * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
23 | * on top of it.
24 | *
25 | * The soft color must be semi transparent alpha channel. This is crucial
26 | * because it allows adding multiple "soft" colors on top of each other
27 | * to create a accent, such as when having inline code block inside
28 | * custom containers.
29 | *
30 | * - `default`: The color used purely for subtle indication without any
31 | * special meanings attached to it such as bg color for menu hover state.
32 | *
33 | * - `brand`: Used for primary brand colors, such as link text, button with
34 | * brand theme, etc.
35 | *
36 | * - `tip`: Used to indicate useful information. The default theme uses the
37 | * brand color for this by default.
38 | *
39 | * - `warning`: Used to indicate warning to the users. Used in custom
40 | * container, badges, etc.
41 | *
42 | * - `danger`: Used to show error, or dangerous message to the users. Used
43 | * in custom container, badges, etc.
44 | * -------------------------------------------------------------------------- */
45 |
46 | :root {
47 | --vp-c-default-1: var(--vp-c-gray-1);
48 | --vp-c-default-2: var(--vp-c-gray-2);
49 | --vp-c-default-3: var(--vp-c-gray-3);
50 | --vp-c-default-soft: var(--vp-c-gray-soft);
51 |
52 | --vp-c-brand-1: var(--vp-c-indigo-1);
53 | --vp-c-brand-2: var(--vp-c-indigo-2);
54 | --vp-c-brand-3: var(--vp-c-indigo-3);
55 | --vp-c-brand-soft: var(--vp-c-indigo-soft);
56 |
57 | --vp-c-tip-1: var(--vp-c-brand-1);
58 | --vp-c-tip-2: var(--vp-c-brand-2);
59 | --vp-c-tip-3: var(--vp-c-brand-3);
60 | --vp-c-tip-soft: var(--vp-c-brand-soft);
61 |
62 | --vp-c-warning-1: var(--vp-c-yellow-1);
63 | --vp-c-warning-2: var(--vp-c-yellow-2);
64 | --vp-c-warning-3: var(--vp-c-yellow-3);
65 | --vp-c-warning-soft: var(--vp-c-yellow-soft);
66 |
67 | --vp-c-danger-1: var(--vp-c-red-1);
68 | --vp-c-danger-2: var(--vp-c-red-2);
69 | --vp-c-danger-3: var(--vp-c-red-3);
70 | --vp-c-danger-soft: var(--vp-c-red-soft);
71 | }
72 |
73 | /**
74 | * Component: Button
75 | * -------------------------------------------------------------------------- */
76 |
77 | :root {
78 | --vp-button-brand-border: transparent;
79 | --vp-button-brand-text: var(--vp-c-white);
80 | --vp-button-brand-bg: var(--vp-c-brand-3);
81 | --vp-button-brand-hover-border: transparent;
82 | --vp-button-brand-hover-text: var(--vp-c-white);
83 | --vp-button-brand-hover-bg: var(--vp-c-brand-2);
84 | --vp-button-brand-active-border: transparent;
85 | --vp-button-brand-active-text: var(--vp-c-white);
86 | --vp-button-brand-active-bg: var(--vp-c-brand-1);
87 | }
88 |
89 | /**
90 | * Component: Home
91 | * -------------------------------------------------------------------------- */
92 |
93 | :root {
94 | --vp-home-hero-name-color: transparent;
95 | --vp-home-hero-name-background: -webkit-linear-gradient(
96 | 120deg,
97 | #bd34fe 30%,
98 | #41d1ff
99 | );
100 |
101 | --vp-home-hero-image-background-image: linear-gradient(
102 | -45deg,
103 | #bd34fe 50%,
104 | #47caff 50%
105 | );
106 | --vp-home-hero-image-filter: blur(44px);
107 | }
108 |
109 | @media (min-width: 640px) {
110 | :root {
111 | --vp-home-hero-image-filter: blur(56px);
112 | }
113 | }
114 |
115 | @media (min-width: 960px) {
116 | :root {
117 | --vp-home-hero-image-filter: blur(68px);
118 | }
119 | }
120 |
121 | /**
122 | * Component: Custom Block
123 | * -------------------------------------------------------------------------- */
124 |
125 | :root {
126 | --vp-custom-block-tip-border: transparent;
127 | --vp-custom-block-tip-text: var(--vp-c-text-1);
128 | --vp-custom-block-tip-bg: var(--vp-c-brand-soft);
129 | --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
130 | }
131 |
132 | /**
133 | * Component: Algolia
134 | * -------------------------------------------------------------------------- */
135 |
136 | .DocSearch {
137 | --docsearch-primary-color: var(--vp-c-brand-1) !important;
138 | }
139 |
140 | /**
141 | * Component: Home
142 | * -------------------------------------------------------------------------- */
143 |
144 | :root {
145 | --vp-home-hero-name-color: transparent;
146 | --vp-home-hero-name-background: -webkit-linear-gradient(
147 | 315deg,
148 | #42d392 25%,
149 | #647eff
150 | );
151 | --vp-home-hero-image-background-image: linear-gradient(
152 | -45deg,
153 | #41b88380 30%,
154 | #35495e80
155 | );
156 | --vp-home-hero-image-filter: blur(30px);
157 | }
158 |
159 | @media (min-width: 640px) {
160 | :root {
161 | --vp-home-hero-image-filter: blur(56px);
162 | }
163 | }
164 |
165 | @media (min-width: 960px) {
166 | :root {
167 | --vp-home-hero-image-filter: blur(72px);
168 | }
169 | }
170 |
171 | /**
172 | * VitePress: Custom fix
173 | * -------------------------------------------------------------------------- */
174 |
175 | .VPLocalSearchBox .result {
176 | --vp-c-bg-search-result: var(--vp-c-bg);
177 | background: var(--vp-c-bg-search-result) !important;
178 | padding: 4px !important;
179 | border: 1px solid var(--vp-c-divider) !important;
180 | }
181 | .VPLocalSearchBox .result.selected {
182 | --vp-c-bg-search-result: var(--vp-c-bg-soft) !important;
183 | }
184 | .VPLocalSearchBox .result .excerpt-gradient-top {
185 | background: linear-gradient(
186 | var(--vp-c-bg-search-result),
187 | transparent
188 | ) !important;
189 | }
190 | .VPLocalSearchBox .result .excerpt-gradient-bottom {
191 | background: linear-gradient(
192 | transparent,
193 | var(--vp-c-bg-search-result)
194 | ) !important;
195 | }
196 | .VPLocalSearchBox .title-icon {
197 | display: none;
198 | }
199 | .VPLocalSearchBox .excerpt-wrapper {
200 | margin-top: 4px;
201 | }
202 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "dbaeumer.vscode-eslint"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.associations": {
3 | "manifest.json": "jsonc",
4 | "pages.json": "jsonc"
5 | },
6 |
7 | // Disable the default formatter, use eslint instead
8 | "prettier.enable": false,
9 | "editor.formatOnSave": false,
10 |
11 | // Auto fix
12 | "editor.codeActionsOnSave": {
13 | "source.fixAll.eslint": "always"
14 | },
15 |
16 | // Silent the stylistic rules in you IDE, but still auto fix them
17 | "eslint.rules.customizations": [
18 | { "rule": "style/*", "severity": "off" },
19 | { "rule": "format/*", "severity": "off" },
20 | { "rule": "*-indent", "severity": "off" },
21 | { "rule": "*-spacing", "severity": "off" },
22 | { "rule": "*-spaces", "severity": "off" },
23 | { "rule": "*-order", "severity": "off" },
24 | { "rule": "*-dangle", "severity": "off" },
25 | { "rule": "*-newline", "severity": "off" },
26 | { "rule": "*quotes", "severity": "off" },
27 | { "rule": "*semi", "severity": "off" }
28 | ],
29 |
30 | // Enable eslint for all supported languages
31 | "eslint.validate": [
32 | "javascript",
33 | "javascriptreact",
34 | "typescript",
35 | "typescriptreact",
36 | "vue",
37 | "html",
38 | "markdown",
39 | "json",
40 | "jsonc",
41 | "yaml",
42 | "toml"
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 改动日志
2 |
3 | ## 0.19.0 (2024-01-17)
4 |
5 | - fix: 重构 `useStorage` / `useStorageSync`,使其更贴合 `uniapp` 的使用习惯
6 | - feat: 强化 `useRouter`,增加功能。
7 | - feat: 增加 `uniUseAutoImports` 函数。
8 | - feat: 增加 `tryOnBackPress` 函数
9 |
10 | ## 0.18.0 (2023-01-15)
11 |
12 | - feat!: 调整 `useLoading`,现在返回是一个对象,其中包含 `showLoading` 和 `hideLoading` 两个方法
13 | - fix: 调整类型实现,支持 `vue` v3.2.47 和 `@vueuse/core` v10,但仍需要用户侧做部分调整,参考 [uni-app#4604](https://github.com/dcloudio/uni-app/issues/4604)
14 | - feat: `useDownloadFile`、`useRequest`、`useSocket`、`useStorageAsync`、`useUploadFile` 对标 `@vueuse/core` v10.7.1
15 |
16 | ## 0.17.1 (2023-11-14)
17 |
18 | - fix: 修复 `useStorage`,关闭 [#28](https://github.com/uni-helper/uni-use/issues/28)
19 |
20 | ## 0.17.0 (2023-11-13)
21 |
22 | - fix: 修复 `useStorage`,关闭 [#28](https://github.com/uni-helper/uni-use/issues/28)
23 | - feat!: 要求 `node>=18`
24 |
25 | ## 0.16.0 (2023-10-17)
26 |
27 | - feat: 增加 `usePageScroll`,感谢 [okxiaoliang4](https://github.com/okxiaoliang4) 在 [#27](https://github.com/uni-helper/uni-use/pull/27) 的贡献!
28 |
29 | ## 0.15.1 (2023-09-22)
30 |
31 | - fix: 修复导出
32 |
33 | ## 0.15.0 (2023-08-31)
34 |
35 | - feat: 增加 `UniUseAutoImports`,方便和 `unplugin-auto-import` 结合使用
36 |
37 | ## 0.14.1 (2023-08-15)
38 |
39 | - fix: 修复 `useStorageAsync` 逻辑判断,关闭 [#25](https://github.com/uni-helper/uni-use/issues/25)
40 |
41 | ## 0.14.0 (2023-07-28)
42 |
43 | - feat: 新增 `useSelectorQuery`,感谢 [edwinhuish](https://github.com/edwinhuish) 在 [#23](https://github.com/uni-helper/uni-use/pull/23) 的贡献!
44 |
45 | ## 0.13.0 (2023-05-05)
46 |
47 | - feat!: 移除 `useImmer`
48 | - fix: 修复 `useSocket` 类型
49 |
50 | ## 0.12.0 (2023-02-16)
51 |
52 | - build: 设置目标为 `es2017`
53 | - fix: 修复 `useDownloadFile` 没有正确中止的问题
54 | - fix: 修复 `useRequest` 没有正确中止的问题
55 | - fix: 修复 `useUploadFile` 没有正确中止的问题
56 | - fix: 修复 `useSocket` 内部实现
57 | - feat: 新增 `useStorageAsync`,和 `vue-use` 的 `useStorageAsync` 类似
58 | - feat!: 移除遗留的组件版本
59 | - feat!: 移除大量无状态逻辑方法,避免过度封装
60 | - feat!: 调整 `useClipboardData` 实现,现在会返回一个 `Ref`
61 | - feat!: 调整 `useGlobalData` 实现,现在会返回一个 `Ref`
62 | - feat!: 调整 `useLoading` 实现,现在会返回可调用的方法,和 `useActionSheet` 类似
63 | - feat!: 调整 `useNetwork` 初始默认值为 `none` 并移除传参
64 | - feat!: 调整 `useRouter` 实现,移除了所有方法
65 | - feat!: 调整 `useScreenBrightness` 实现,现在会返回一个 `Ref`
66 | - feat!: 调整 `useSocket` 实现,现在和 `vue-use` 的 `useWebSocket` 类似
67 | - feat!: 移除 `useStorage`
68 | - feat!: 调整 `useToast` 实现,现在会返回可调用的方法,和 `useActionSheet` 类似
69 |
70 | 请先阅读 [组合式函数](https://cn.vuejs.org/guide/reusability/composables.html) 和 [组合式 API 常见问答](https://cn.vuejs.org/guide/extras/composition-api-faq.html),以了解为什么这个版本移除了大量无状态逻辑方法。核心内容摘录如下。
71 |
72 | > 在 Vue 应用的概念中,“组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数。
73 | >
74 | > 当构建前端应用时,我们常常需要复用公共任务的逻辑。例如为了在不同地方格式化时间,我们可能会抽取一个可复用的日期格式化函数。这个函数封装了无状态的逻辑:它在接收一些输入后立刻返回所期望的输出。复用无状态逻辑的库有很多,比如你可能已经用过的 lodash 或是 date-fns。
75 | >
76 | > 相比之下,有状态逻辑负责管理会随时间而变化的状态。一个简单的例子是跟踪当前鼠标在页面中的位置。在实际应用中,也可能是像触摸手势或与数据库的连接状态这样的更复杂的逻辑。
77 |
78 | ## 0.11.0 (2023-01-10)
79 |
80 | - feat!: 因难以保证稳定性移除所有组件版本
81 |
82 | ## 0.10.4 (2023-01-04)
83 |
84 | - fix: 修复情况判断
85 | - build: 切换到 `rollup`
86 |
87 | ## 0.10.3 (2022-12-28)
88 |
89 | - fix: 修复导出
90 |
91 | ## 0.10.2 (2022-12-26)
92 |
93 | - fix: 修复类型
94 |
95 | ## 0.10.1 (2022-12-23)
96 |
97 | - fix: 修复构建
98 |
99 | ## 0.10.0 (2022-12-15)
100 |
101 | - feat!: 要求 `node >= 14.18`,这是为了对标 `rollup` 和 `vite`
102 | - feat: 默认为 esm 包,但仍支持 cjs
103 |
104 | ## 0.9.1 (2022-11-16)
105 |
106 | - fix: 修复构建
107 |
108 | ## 0.9.0 (2022-11-16)
109 |
110 | - feat!: 迁移到 `@uni-helper/uni-use`
111 |
112 | ## 0.8.0 (2022-10-24)
113 |
114 | - feat: 新增 `UseAccountInfo`
115 | - feat: 新增 `useAddress`
116 | - feat: 新增 `useAppBaseInfo` 和 `UseAppBaseInfo`
117 | - feat: 新增 `useAudio`
118 | - feat: 新增 `useAuthorize`
119 | - feat: 新增 `useBackground` 导出
120 | - feat: 新增 `useCamera`
121 | - feat: 新增 `useDeviceInfo` 和 `UseDeviceInfo`
122 | - feat: 新增 `useFile`
123 | - feat: 新增 `useImage` 导出
124 | - feat: 新增 `useImmer`
125 | - feat: 新增 `useInvoice`
126 | - feat: 新增 `useLocation` 导出
127 | - feat: 新增 `useMap`
128 | - feat: 新增 `useMenuButtonBoundingClientRect`
129 | - feat: 新增 `useRecorder`
130 | - feat: 新增 `useSubscription`
131 | - feat: 新增 `useSystemSetting`
132 | - feat: 新增 `useUpdate`
133 | - feat: 新增 `useUser`
134 | - feat: 新增 `useVideo`
135 | - feat: 增加 `useWindowInfo` 和 `UseWindowInfo`
136 | - fix: 修复组件没有正确导出的问题
137 |
138 | ## 0.7.2 (2022-10-12)
139 |
140 | - perf: 优化 `useRequest`、`useDownloadFile` 和 `useUploadFile` 类型
141 | - fix: 修复构建
142 |
143 | ## 0.7.1 (2022-09-30)
144 |
145 | - fix: 修复了构建不正常的问题
146 |
147 | ## 0.7.0 (2022-09-29)
148 |
149 | - feat!: 现在要求使用 `node >= 14.16`
150 | - feat!: 现在构建目标是 `esnext`
151 | - fix: 修复了构建不正常的问题
152 | - feat: 新增 `useAccountInfo`
153 | - feat: 新增 `useLaunchOptions`
154 | - feat: 新增 `useEnterOptions`
155 |
156 | ## 0.6.1
157 |
158 | - fix: 修复 `useSelectorQuery` 类型
159 |
160 | ## 0.6.0
161 |
162 | - feat!: 放弃 `vue@2` 支持
163 | - perf!: 调整 `useApp` 导出
164 | - perf: 优化 `useActionSheet` 类型
165 | - feat: `useArrayBufferToBase64` 支持传入 `ref`
166 | - feat: `useBase64ToArrayBuffer` 支持传入 `ref`
167 | - perf: 优化 `useBackground` 类型
168 | - perf: 优化 `useClipboardData` 类型
169 | - fix: 修复 `useGlobalData` 错误赋值
170 | - perf: 优化 `useGlobalData` 类型
171 | - fix: 修复 `useGlobalData` 导出
172 | - perf: 优化 `useImage` 类型
173 | - perf: 优化 `useLoading` 类型
174 | - perf: 优化 `useLocation` 类型
175 | - perf: 优化 `useModal` 类型
176 | - perf: 优化 `useNavigationBar` 类型
177 | - fix: 替换 `useRouter` 中的 `at`
178 | - perf: 优化 `useRouter` 类型
179 | - perf: 优化 `useScanCode` 类型
180 | - perf: 优化 `useScreenBrightness` 类型
181 | - perf: 优化 `useStorage` 类型
182 | - perf: 优化 `useSupported` 类型
183 | - perf: 优化 `useTabBar` 类型
184 | - perf: 优化 `useToast` 类型
185 | - perf: 优化 `useVibrate` 类型
186 |
187 | ## 0.5.0
188 |
189 | - feat: 新增 `useActionSheet`
190 | - perf!: 调整 `useArrayBufferToBase64` 实现
191 | - feat: 新增 `useBackground`
192 | - perf!: 调整 `useBase64ToArrayBuffer` 实现
193 | - fix: 修复 `useClipboardData` 监听
194 | - perf: 调整 `useClipboardData` 实现
195 | - feat!: 移除 `useColorMode`
196 | - feat!: 移除 `useDark`
197 | - perf!: 调整 `useGlobalData` 实现
198 | - feat: 新增 `useImage`
199 | - fix: 修复 `useInterceptor` 监听
200 | - feat: 新增 `useLoading`
201 | - feat: 新增 `useLocation`
202 | - feat: 新增 `useModal`
203 | - feat: 新增 `useNavigationBar`
204 | - perf: 调整 `useNetwork` 实现
205 | - perf: 调整 `UsePreferredDark` 实现
206 | - perf: 调整 `UsePreferredLanguage` 实现
207 | - feat: 新增 `usePullDownRefresh` 实现
208 | - feat: 新增 `useScanCode`
209 | - feat: 新增 `useScreenBrightness` 和 `UseScreenBrightness`
210 | - feat: 新增 `useSelectorQuery`
211 | - feat: 新增 `useStorage`
212 | - feat!: 移除 `useStorageAsync`
213 | - feat: 新增 `useSystemInfo` 和 `UseSystemInfo`
214 | - feat: 新增 `useTabBar`
215 | - feat: 新增 `useToast`
216 | - perf: 调整 `useVibrate` 实现
217 |
218 | ## 0.4.2
219 |
220 | - fix: 修复链接
221 |
222 | ## 0.4.1
223 |
224 | - fix: 修复 `setClipboardData` 未正确更新的问题
225 | - fix: 修复 `UseGlobalData` 导出错误
226 | - fix: 修复 `UseUniPlatform` 导出错误
227 | - perf: 调整 `useRouter` 导出
228 |
229 | ## 0.4.0
230 |
231 | - feat: 新增 `usePrevPage`
232 | - feat: 新增 `usePrevRoute`
233 | - feat: 新增 `useRoute`
234 | - feat: 新增 `useVibrate`
235 | - fix: 调整 `useArrayBufferToBase64` 类型
236 | - fix: 调整 `useBase64ToArrayBuffer` 类型
237 | - fix: 调整 `useClipboardData` 参数和内部实现
238 |
239 | ## 0.3.0
240 |
241 | - feat: 新增 `tryOnHide`
242 | - feat: 新增 `tryOnInit`
243 | - feat: 新增 `tryOnLoad`
244 | - feat: 新增 `tryOnReady`
245 | - feat: 新增 `tryOnShow`
246 | - feat: 新增 `tryOnUnload`
247 | - feat: 新增 `useApp`
248 | - feat: 新增 `useArrayBufferToBase64`
249 | - feat: 新增 `useBase64ToArrayBuffer`
250 | - feat: 新增 `useClipboardData` 和 `UseClipboardData`
251 | - feat: 新增 `useGlobalData` 和 `useGlobalData`
252 | - feat: 新增 `usePage`
253 | - feat: 新增 `usePages`
254 | - feat: 新增 `useRouter`
255 | - feat: 新增 `useUniPlatform` 和 `UseUniPlatform`
256 | - feat: 新增 `useSocket`
257 | - feat: 新增 `useSupported`
258 | - feat: 新增 `useVisible`
259 | - fix: 修复 `useStorageAsync` 判断
260 |
261 | ## 0.2.0
262 |
263 | - feat: 新增 `useColorMode` 和 `UseColorMode`
264 | - feat: 新增 `useDark` 和 `UseDark`
265 | - fix: 修复 `UseNetwork` 导入
266 | - fix: 修复 `UseOnline` 导入
267 | - feat: 新增 `usePreferredDark` 和 `UsePreferredDark`
268 | - feat: 新增 `usePreferredLanguage` 和 `UsePreferredLanguage`
269 |
270 | ## 0.1.0
271 |
272 | - feat: 新增 `useDownloadFile`
273 | - feat: 新增 `useInterceptor`
274 | - feat: 新增 `useNetwork` 和 `UseNetwork`
275 | - feat: 新增 `useOnline` 和 `UseOnline`
276 | - feat: 新增 `useRequest`
277 | - feat: 新增 `useStorageAsync`
278 | - feat: 新增 `useUploadFile`
279 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022-present uni-helper
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @uni-helper/uni-use
2 |
3 | [](https://github.com/uni-helper/uni-use/blob/main/LICENSE)
4 | [](https://www.npmjs.com/package/@uni-helper/uni-use)
5 |
6 | `uni-app (vue3)` 组合式工具集。要求 `node>=18`。
7 |
8 | - [@uni-helper/uni-use](#uni-helperuni-use)
9 | - [安装依赖](#安装依赖)
10 | - [使用](#使用)
11 | - [其它](#其它)
12 | - [限制](#限制)
13 | - [构建](#构建)
14 | - [和 `unplugin-auto-import` 结合使用](#和-unplugin-auto-import-结合使用)
15 | - [EventBus](#eventbus)
16 | - [TypeScript](#typescript)
17 | - [资源](#资源)
18 | - [致谢](#致谢)
19 |
20 | ## 安装依赖
21 |
22 | ```shell
23 | npm install @uni-helper/uni-use @vueuse/core@9
24 | ```
25 |
26 | 如果你希望使用 `@vueuse/core` v10,请参考 [uni-app#4604](https://github.com/dcloudio/uni-app/issues/4604)。
27 |
28 |
29 | yarn v2 或以上
30 | 请参考 文档 设置 nodeLinker
为 node_modules
。
31 |
32 |
33 |
34 | pnpm
35 | 请参考 文档 设置 shamefully-hoist
为 true
。
36 |
37 |
38 | 目前没有支持 `uni_modules` 的计划,但欢迎 PR 贡献。
39 |
40 | ## 使用
41 |
42 | 详情请看 [`uni-use`函数列表](./src/index.md)
43 |
44 | ## 其它
45 |
46 | ### 限制
47 |
48 | 在小程序和移动应用环境下有如下无法避开的限制:
49 |
50 | - 缺失某些全局变量(如 `window`、`navigator` 等)
51 | - 必须使用 `uni-app` 提供的 API 实现功能(如拦截器、存储等),API 不支持的也就无法支持,比如拦截同步 API、监听其它地方引起的剪切板变化等
52 | - 无法使用顶层 `await`
53 |
54 | 在开发网页时,建议直接使用 `vue`,避免过多的环境判断代码,同时也能享受更好的生态,如 `vueuse` 的完整支持。
55 |
56 | ### 构建
57 |
58 | 目前 `@uni-helper/uni-use` 会使用 `unbuild` 将 `uni` API 之外的部分转译到 `ES2017`(即 `ES8`)。`uni` API 需要在项目构建时由 `uni-app` 官方提供的插件处理。
59 |
60 | 对于 `vite + vue3` 项目,请先设置 `build.target` 为 `ES6`。
61 |
62 | ```typescript
63 | import uni from '@dcloudio/vite-plugin-uni';
64 | import { defineConfig } from 'vite';
65 |
66 | // https://vitejs.dev/config/
67 | export default defineConfig({
68 | build: {
69 | target: 'es6',
70 | cssTarget: 'chrome61', // https://cn.vitejs.dev/config/build-options.html#build-csstarget
71 | },
72 | optimizeDeps: {
73 | exclude: ['vue-demi'],
74 | },
75 | plugins: [
76 | // ...,
77 | uni(),
78 | // ...,
79 | ],
80 | });
81 | ```
82 |
83 | 然后在 `src/main.ts` 或 `src/main.js` 处自行添加 polyfill。以下是使用 [core-js](https://github.com/zloirock/core-js) 的示例(需要自行安装 `core-js`),你也可以使用 [es-shims](https://github.com/es-shims)。
84 |
85 | ```typescript
86 | import 'core-js/actual/array/iterator';
87 | import 'core-js/actual/promise';
88 | import 'core-js/actual/object/assign';
89 | import 'core-js/actual/promise/finally';
90 | // 你可以根据需要自行添加额外的 polyfills
91 | // import 'core-js/actual/object/values'
92 | import { createSSRApp } from 'vue';
93 | import App from './App.vue';
94 |
95 | export function createApp() {
96 | const app = createSSRApp(App);
97 | return {
98 | app,
99 | };
100 | }
101 | ```
102 |
103 | 微信小程序的 JavaScript 支持度见 [wechat-miniprogram/miniprogram-compat](https://github.com/wechat-miniprogram/miniprogram-compat)。微信小程序要支持 `vue3`,需设置基础库最低版本为 2.11.2 或以上,2.11.2 对应 `chrome>=53,ios>=10`。
104 |
105 | ### 和 `unplugin-auto-import` 结合使用
106 |
107 | ```typescript
108 | // vite.config.ts
109 | import { fileURLToPath } from 'node:url';
110 | import uni from '@dcloudio/vite-plugin-uni';
111 | import { uniuseAutoImports } from '@uni-helper/uni-use';
112 | import autoImport from 'unplugin-auto-import/vite';
113 | import { defineConfig } from 'vitest/config';
114 |
115 | // https://vitejs.dev/config/
116 | export default defineConfig({
117 | plugins: [
118 | autoImport({
119 | imports: [
120 | uniuseAutoImports(),
121 | ],
122 | }),
123 | uni({ /* ... */ }),
124 | ],
125 | });
126 | ```
127 |
128 | ### EventBus
129 |
130 | 如果你想使用 `EventBus`,请考虑使用 [VueUse - useEventBus](https://vueuse.org/core/useeventbus/#useeventbus)、[mitt](https://github.com/developit/mitt) 或 [nanoevents](https://github.com/ai/nanoevents)。这个库不再重复提供类似功能。
131 |
132 | ### TypeScript
133 |
134 | `@uni-helper/uni-use` 本身使用 [TypeScript](https://www.typescriptlang.org/) 开发,拥有类型提示。
135 |
136 | ## 资源
137 |
138 | - [改动日志](/CHANGELOG.md)
139 |
140 | ## 致谢
141 |
142 | - [vueuse](https://vueuse.org/) [#1073](https://github.com/vueuse/vueuse/pull/1073)
143 | - [taro-hooks](https://taro-hooks-innocces.vercel.app/)
144 | - [tob-use](https://tob-use.netlify.app/)
145 |
--------------------------------------------------------------------------------
/build.config.ts:
--------------------------------------------------------------------------------
1 | import { defineBuildConfig } from 'unbuild';
2 |
3 | export default defineBuildConfig({
4 | entries: ['./src/index'],
5 | clean: true,
6 | declaration: true,
7 | externals: [
8 | '@dcloudio/uni-cli-shared',
9 | '@vue/runtime-core',
10 | ],
11 | rollup: {
12 | emitCJS: true,
13 | esbuild: {
14 | target: 'es2017',
15 | },
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/docs/guide/index.md:
--------------------------------------------------------------------------------
1 | # 开始
2 |
3 | `uni-app (vue3)` 组合式工具集。要求 `node>=18`。
4 |
5 | ## 安装
6 |
7 | ```bash
8 | npm install @uni-helper/uni-use @vueuse/core@9
9 | ```
10 |
11 | 如果你希望使用 `@vueuse/core` v10+,请参考 [uni-app#4604](https://github.com/dcloudio/uni-app/issues/4604), 自行提供 `polyfill` 或者参考使用 [`vite-plugin-uni-polyfill`](https://github.com/Ares-Chang/vite-plugin-uni-polyfill)。
12 |
13 | ::: details yarn v2 或以上
14 | 请参考 [文档](https://yarnpkg.com/configuration/yarnrc/#nodeLinker) 设置 `nodeLinker` 为 `node_modules`。
15 | :::
16 |
17 | ::: details pnpm
18 | 请参考 [文档](https://pnpm.io/npmrc#shamefully-hoist) 设置 `shamefully-hoist` 为 `true`。
19 | :::
20 |
21 | > 目前没有支持 uni_modules 的计划,但欢迎 PR 贡献。
22 |
23 | ## 使用
24 |
25 | ```ts
26 | import { tryOnLoad } from '@uni-helper/uni-use';
27 |
28 | tryOnLoad(() => {
29 | console.log('onLoad');
30 | });
31 | ```
32 |
33 | 其它详情请查看所有 [API](/apis.md)。
34 |
35 | ### 和 `unplugin-auto-import` 结合使用
36 |
37 | ```typescript
38 | // vite.config.ts
39 | import { fileURLToPath } from 'node:url';
40 | import uni from '@dcloudio/vite-plugin-uni';
41 | import { uniuseAutoImports } from '@uni-helper/uni-use';
42 | import autoImport from 'unplugin-auto-import/vite';
43 | import { defineConfig } from 'vitest/config';
44 |
45 | // https://vitejs.dev/config/
46 | export default defineConfig({
47 | plugins: [
48 | autoImport({
49 | imports: [
50 | uniuseAutoImports(),
51 | ],
52 | }),
53 | uni({ /* ... */ }),
54 | ],
55 | });
56 | ```
57 |
58 | ### TypeScript
59 |
60 | `@uni-helper/uni-use` 本身使用 [TypeScript](https://www.typescriptlang.org/) 开发,天然具有类型提示。
61 |
62 | ## 贡献
63 |
64 | 如果有新想法,热爱开源,欢迎 PR 贡献。
65 |
66 | ## 感谢
67 |
68 | 感谢以下项目提供的灵感及帮助。
69 |
70 | - [vueuse](https://vueuse.org/) and [#1073](https://github.com/vueuse/vueuse/pull/1073)
71 | - [taro-hooks](https://taro-hooks-innocces.vercel.app/)
72 | - [tob-use](https://tob-use.netlify.app/)
73 |
--------------------------------------------------------------------------------
/docs/guide/notice.md:
--------------------------------------------------------------------------------
1 | # 注意事项
2 |
3 | ## 限制
4 |
5 | 在小程序和移动应用环境下有如下无法避开的限制:
6 |
7 | - 缺失某些全局变量(如 `window`、`navigator` 等)
8 | - 必须使用 `uni-app` 提供的 API 实现功能(如拦截器、存储等),API 不支持的也就无法支持,比如拦截同步 API、监听其它地方引起的剪切板变化等
9 | - 无法使用顶层 `await`
10 |
11 | 在开发网页时,建议直接使用 `vue`,避免过多的环境判断代码,同时也能享受更好的生态,如 `vueuse` 的完整支持。
12 |
13 | ## 构建
14 |
15 | 目前 `@uni-helper/uni-use` 会使用 `unbuild` 将 `uni` API 之外的部分转译到 `ES2017`(即 `ES8`)。`uni` API 需要在项目构建时由 `uni-app` 官方提供的插件处理。
16 |
17 | 对于 `vite + vue3` 项目,请先设置 `build.target` 为 `ES6`。
18 |
19 | ```typescript
20 | import uni from '@dcloudio/vite-plugin-uni';
21 | import { defineConfig } from 'vite';
22 |
23 | // https://vitejs.dev/config/
24 | export default defineConfig({
25 | build: {
26 | target: 'es6',
27 | cssTarget: 'chrome61', // https://cn.vitejs.dev/config/build-options.html#build-csstarget
28 | },
29 | optimizeDeps: {
30 | exclude: ['vue-demi'],
31 | },
32 | plugins: [
33 | // ...,
34 | uni(),
35 | // ...,
36 | ],
37 | });
38 | ```
39 |
40 | 然后在 `src/main.ts` 或 `src/main.js` 处自行添加 polyfill。以下是使用 [core-js](https://github.com/zloirock/core-js) 的示例(需要自行安装 `core-js`),你也可以使用 [es-shims](https://github.com/es-shims)。
41 |
42 | ```typescript
43 | import 'core-js/actual/array/iterator';
44 | import 'core-js/actual/promise';
45 | import 'core-js/actual/object/assign';
46 | import 'core-js/actual/promise/finally';
47 | // 你可以根据需要自行添加额外的 polyfills
48 | // import 'core-js/actual/object/values'
49 | import { createSSRApp } from 'vue';
50 | import App from './App.vue';
51 |
52 | export function createApp() {
53 | const app = createSSRApp(App);
54 | return {
55 | app,
56 | };
57 | }
58 | ```
59 |
60 | 微信小程序的 JavaScript 支持度见 [wechat-miniprogram/miniprogram-compat](https://github.com/wechat-miniprogram/miniprogram-compat)。微信小程序要支持 `vue3`,需设置基础库最低版本为 2.11.2 或以上,2.11.2 对应 `chrome>=53,ios>=10`。
61 |
62 | ## EventBus
63 |
64 | 如果你想使用 `EventBus`,请考虑使用 [VueUse - useEventBus](https://vueuse.org/core/useeventbus/#useeventbus)、[mitt](https://github.com/developit/mitt) 或 [nanoevents](https://github.com/ai/nanoevents)。这个库不再重复提供类似功能。
65 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 |
4 | hero:
5 | name: UniUse
6 | text: uni-app 组合式工具集
7 | tagline: 为 uni-app 量身打造的组合式工具集
8 | image:
9 | src: /logo.png
10 | alt: UniUse
11 | actions:
12 | - theme: brand
13 | text: 开始
14 | link: /guide/
15 | - theme: alt
16 | text: API 集合
17 | link: /apis
18 |
19 | features:
20 | - icon: 🎛
21 | title: 功能丰富
22 | details: 提供了丰富的功能,封装了 uni-app 中常用的功能。
23 | - icon: 💚
24 | title: 开箱即用
25 | details: 开箱即用的支持了 uni-app Vue3 的 Composition API。
26 | - icon: 🦾
27 | title: 类型安全
28 | details: 所有函数都支持 TS 类型推导,无需手动标注类型
29 | ---
30 |
31 |
32 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import antfu from '@antfu/eslint-config';
2 |
3 | export default antfu(
4 | { },
5 | {
6 | // setting
7 | languageOptions: {
8 | globals: {
9 | UniApp: 'readonly',
10 | uni: 'readonly',
11 | plus: 'readonly',
12 | process: 'readonly',
13 | browser: 'readonly',
14 | },
15 | },
16 | // style
17 | rules: {
18 | 'style/quote-props': ['error', 'as-needed'],
19 | 'style/semi': ['error', 'always'],
20 | 'style/max-statements-per-line': ['error', { max: 1 }],
21 | curly: ['warn', 'all'],
22 | 'style/member-delimiter-style': ['warn', {
23 | multiline: { delimiter: 'semi', requireLast: true },
24 | singleline: { delimiter: 'semi', requireLast: false },
25 | multilineDetection: 'brackets',
26 | }],
27 | },
28 | },
29 | {
30 | rules: {
31 | 'no-console': 'off',
32 | },
33 | },
34 | {
35 | files: ['**/manifest.json'],
36 | rules: {
37 | indent: ['error', 4],
38 | 'jsonc/indent': ['error', 4],
39 | },
40 | },
41 | );
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uni-helper/uni-use",
3 | "type": "module",
4 | "version": "0.19.14",
5 | "packageManager": "pnpm@8.14.1",
6 | "description": "uni-app (vue3) 组合式工具集",
7 | "author": {
8 | "name": "ModyQyW",
9 | "email": "wurui-dev@foxmail.com",
10 | "url": "https://modyqyw.github.io"
11 | },
12 | "license": "MIT",
13 | "homepage": "https://github.com/uni-helper/uni-use#readme",
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/uni-helper/uni-use.git"
17 | },
18 | "bugs": {
19 | "url": "https://github.com/uni-helper/uni-use/issues"
20 | },
21 | "keywords": [
22 | "uni-app",
23 | "uniapp",
24 | "uni",
25 | "use",
26 | "composition",
27 | "composable"
28 | ],
29 | "exports": {
30 | ".": {
31 | "import": {
32 | "types": "./dist/index.d.mts",
33 | "default": "./dist/index.mjs"
34 | },
35 | "require": {
36 | "types": "./dist/index.d.cts",
37 | "default": "./dist/index.cjs"
38 | }
39 | }
40 | },
41 | "main": "./dist/index.cjs",
42 | "module": "./dist/index.mjs",
43 | "types": "./dist/index.d.ts",
44 | "files": [
45 | "dist"
46 | ],
47 | "engines": {
48 | "node": ">=18"
49 | },
50 | "scripts": {
51 | "build": "unbuild",
52 | "check:deps": "taze -f",
53 | "check:types": "tsc --noEmit",
54 | "commit": "git-cz",
55 | "dev": "unbuild --stub",
56 | "lint": "pnpm build && conc \"pnpm:check:types\" \"pnpm:lint:eslint\" \"pnpm:lint:publint\"",
57 | "lint:eslint": "eslint . --fix --cache",
58 | "lint:publint": "publint",
59 | "prepare": "is-ci || simple-git-hooks",
60 | "prepublishOnly": "pnpm run build",
61 | "release": "pnpm install && pnpm run lint && bumpp",
62 | "test": "vitest",
63 | "docs:dev": "vitepress dev",
64 | "docs:build": "vitepress build",
65 | "docs:preview": "vitepress preview"
66 | },
67 | "peerDependencies": {
68 | "@vueuse/core": "^9.0.0 || ^10.0.0",
69 | "typescript": "^4.5.0 || ^5.0.0",
70 | "vue": "^3.2.47"
71 | },
72 | "peerDependenciesMeta": {
73 | "typescript": {
74 | "optional": true
75 | }
76 | },
77 | "dependencies": {
78 | "@dcloudio/types": "^3.4.7",
79 | "@dcloudio/uni-app": "^3.0.0-3090920231225001"
80 | },
81 | "devDependencies": {
82 | "@antfu/eslint-config": "^3.8.0",
83 | "@commitlint/cli": "^18.4.4",
84 | "@commitlint/config-conventional": "^18.4.4",
85 | "@commitlint/prompt": "^18.4.4",
86 | "@dcloudio/uni-cli-shared": "^3.0.0-3090920231225001",
87 | "@dcloudio/vite-plugin-uni": "3.0.0-3090920231225001",
88 | "@tsconfig/node18": "^18.2.2",
89 | "@types/node": "^20.11.0",
90 | "@typescript-eslint/eslint-plugin": "^8.9.0",
91 | "@typescript-eslint/parser": "^8.9.0",
92 | "@vueuse/core": "^9.13.0",
93 | "bumpp": "^9.2.1",
94 | "commitizen": "^4.3.0",
95 | "concurrently": "^8.2.2",
96 | "eslint": "^9.12.0",
97 | "fast-glob": "^3.3.2",
98 | "is-ci": "^3.0.1",
99 | "lint-staged": "^15.2.0",
100 | "prettier": "^3.2.2",
101 | "publint": "^0.2.7",
102 | "simple-git-hooks": "^2.9.0",
103 | "taze": "^0.13.1",
104 | "ts-node": "^10.9.2",
105 | "tsx": "^4.7.0",
106 | "typescript": "^5.3.3",
107 | "unbuild": "^2.0.0",
108 | "vite": "^4.0.0",
109 | "vitepress": "^1.4.0",
110 | "vitest": "^1.4.0"
111 | },
112 | "publishConfig": {
113 | "access": "public",
114 | "registry": "https://registry.npmjs.org/"
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "uni-preset-vue",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "build:app": "uni build -p app",
6 | "build:app-android": "uni build -p app-android",
7 | "build:app-ios": "uni build -p app-ios",
8 | "build:custom": "uni build -p",
9 | "build:h5": "uni build",
10 | "build:h5:ssr": "uni build --ssr",
11 | "build:mp-alipay": "uni build -p mp-alipay",
12 | "build:mp-baidu": "uni build -p mp-baidu",
13 | "build:mp-kuaishou": "uni build -p mp-kuaishou",
14 | "build:mp-lark": "uni build -p mp-lark",
15 | "build:mp-qq": "uni build -p mp-qq",
16 | "build:mp-toutiao": "uni build -p mp-toutiao",
17 | "build:mp-weixin": "uni build -p mp-weixin",
18 | "build:quickapp-webview": "uni build -p quickapp-webview",
19 | "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
20 | "build:quickapp-webview-union": "uni build -p quickapp-webview-union",
21 | "dev:app": "uni -p app",
22 | "dev:app-android": "uni -p app-android",
23 | "dev:app-ios": "uni -p app-ios",
24 | "dev:custom": "uni -p",
25 | "dev:h5": "uni",
26 | "dev:h5:ssr": "uni --ssr",
27 | "dev:mp-alipay": "uni -p mp-alipay",
28 | "dev:mp-baidu": "uni -p mp-baidu",
29 | "dev:mp-kuaishou": "uni -p mp-kuaishou",
30 | "dev:mp-lark": "uni -p mp-lark",
31 | "dev:mp-qq": "uni -p mp-qq",
32 | "dev:mp-toutiao": "uni -p mp-toutiao",
33 | "dev:mp-weixin": "uni -p mp-weixin",
34 | "dev:quickapp-webview": "uni -p quickapp-webview",
35 | "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
36 | "dev:quickapp-webview-union": "uni -p quickapp-webview-union"
37 | },
38 | "dependencies": {
39 | "@dcloudio/uni-app": "3.0.0-3090920231225001",
40 | "@dcloudio/uni-app-plus": "3.0.0-3090920231225001",
41 | "@dcloudio/uni-components": "3.0.0-3090920231225001",
42 | "@dcloudio/uni-h5": "3.0.0-3090920231225001",
43 | "@dcloudio/uni-mp-alipay": "3.0.0-3090920231225001",
44 | "@dcloudio/uni-mp-baidu": "3.0.0-3090920231225001",
45 | "@dcloudio/uni-mp-kuaishou": "3.0.0-3090920231225001",
46 | "@dcloudio/uni-mp-lark": "3.0.0-3090920231225001",
47 | "@dcloudio/uni-mp-qq": "3.0.0-3090920231225001",
48 | "@dcloudio/uni-mp-toutiao": "3.0.0-3090920231225001",
49 | "@dcloudio/uni-mp-weixin": "3.0.0-3090920231225001",
50 | "@dcloudio/uni-quickapp-webview": "3.0.0-3090920231225001",
51 | "@uni-helper/uni-use": "workspace:*",
52 | "vue": "3.2.47",
53 | "vue-i18n": "9.9.0"
54 | },
55 | "devDependencies": {
56 | "@dcloudio/types": "3.4.7",
57 | "@dcloudio/uni-automator": "3.0.0-3090920231225001",
58 | "@dcloudio/uni-cli-shared": "3.0.0-3090920231225001",
59 | "@dcloudio/uni-stacktracey": "3.0.0-3090920231225001",
60 | "@dcloudio/vite-plugin-uni": "3.0.0-3090920231225001",
61 | "typescript": "5.3.3",
62 | "vite": "4.5.1"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/playground/src/App.vue:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/playground/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createSSRApp } from 'vue';
2 | import App from './App.vue';
3 |
4 | export function createApp() {
5 | const app = createSSRApp(App);
6 | return {
7 | app,
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/playground/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "appid": "",
4 | "description": "",
5 | "versionName": "1.0.0",
6 | "versionCode": "100",
7 | "transformPx": false,
8 | /* 5+App特有相关 */
9 | "app-plus": {
10 | "usingComponents": true,
11 | "nvueStyleCompiler": "uni-app",
12 | "compilerVersion": 3,
13 | "splashscreen": {
14 | "alwaysShowBeforeRender": true,
15 | "waiting": true,
16 | "autoclose": true,
17 | "delay": 0
18 | },
19 | /* 模块配置 */
20 | "modules": {},
21 | /* 应用发布信息 */
22 | "distribute": {
23 | /* android打包配置 */
24 | "android": {
25 | "permissions": [
26 | "",
27 | "",
28 | "",
29 | "",
30 | "",
31 | "",
32 | "",
33 | "",
34 | "",
35 | "",
36 | "",
37 | "",
38 | "",
39 | "",
40 | ""
41 | ]
42 | },
43 | /* ios打包配置 */
44 | "ios": {},
45 | /* SDK配置 */
46 | "sdkConfigs": {}
47 | }
48 | },
49 | /* 快应用特有相关 */
50 | "quickapp": {},
51 | /* 小程序特有相关 */
52 | "mp-weixin": {
53 | "appid": "",
54 | "setting": {
55 | "urlCheck": false
56 | },
57 | "usingComponents": true
58 | },
59 | "mp-alipay": {
60 | "usingComponents": true
61 | },
62 | "mp-baidu": {
63 | "usingComponents": true
64 | },
65 | "mp-toutiao": {
66 | "usingComponents": true
67 | },
68 | "uniStatistics": {
69 | "enable": false
70 | },
71 | "vueVersion": "3"
72 | }
73 |
--------------------------------------------------------------------------------
/playground/src/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [ // pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
3 | {
4 | "path": "pages/index/index",
5 | "style": {
6 | "navigationBarTitleText": "uni-app"
7 | }
8 | }
9 | ],
10 | "globalStyle": {
11 | "navigationBarTextStyle": "black",
12 | "navigationBarTitleText": "uni-app",
13 | "navigationBarBackgroundColor": "#F8F8F8",
14 | "backgroundColor": "#F8F8F8"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/playground/src/pages/index/index.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{ title }}
22 |
23 |
24 | {{ clipboardData }}
25 |
28 |
29 |
30 |
31 |
58 |
--------------------------------------------------------------------------------
/playground/src/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-helper/uni-use/40b4fccbb18b89fdd661d88c52c6f6b40845815f/playground/src/static/logo.png
--------------------------------------------------------------------------------
/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "jsx": "preserve",
5 | "lib": ["esnext", "dom"],
6 | "useDefineForClassFields": true,
7 | "module": "esnext",
8 | "moduleResolution": "node",
9 | "paths": {
10 | "@/*": ["./src/*"]
11 | },
12 | "resolveJsonModule": true,
13 | "types": ["vite/client", "@dcloudio/types"],
14 | "strict": true,
15 | "sourceMap": true,
16 | "esModuleInterop": true
17 | },
18 | "vueCompilerOptions": {
19 | "experimentalRuntimeMode": "runtime-uni-app",
20 | "nativeTags": ["block", "component", "template", "slot"]
21 | },
22 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
23 | }
24 |
--------------------------------------------------------------------------------
/playground/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath } from 'node:url';
2 | import uni from '@dcloudio/vite-plugin-uni';
3 | import { defineConfig } from 'vite';
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [uni()],
8 | resolve: {
9 | alias: {
10 | '@': fileURLToPath(new URL('src', import.meta.url)),
11 | },
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - playground
3 |
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-helper/uni-use/40b4fccbb18b89fdd661d88c52c6f6b40845815f/public/logo.png
--------------------------------------------------------------------------------
/src/exports.ts:
--------------------------------------------------------------------------------
1 | export * from './tryOnBackPress';
2 | export * from './tryOnHide';
3 | export * from './tryOnInit';
4 | export * from './tryOnLoad';
5 | export * from './tryOnReady';
6 | export * from './tryOnScopeDispose';
7 | export * from './tryOnShow';
8 | export * from './tryOnUnload';
9 | export * from './useActionSheet';
10 | export * from './useClipboardData';
11 | export * from './useDownloadFile';
12 | export * from './useGlobalData';
13 | export * from './useInterceptor';
14 | export * from './useLoading';
15 | export * from './useModal';
16 | export * from './useNetwork';
17 | export * from './useOnline';
18 | export * from './usePage';
19 | export * from './usePages';
20 | export * from './usePageScroll';
21 | export * from './usePreferredDark';
22 | export * from './usePreferredLanguage';
23 | export * from './usePrevPage';
24 | export * from './usePrevRoute';
25 | export * from './useProvider';
26 | export * from './useRequest';
27 | export * from './useRoute';
28 | export * from './useRouter';
29 | export * from './useScanCode';
30 | export * from './useScreenBrightness';
31 | export * from './useSelectorQuery';
32 | export * from './useSocket';
33 | export * from './useStorage';
34 | export * from './useStorageAsync';
35 | export * from './useStorageSync';
36 | export * from './useToast';
37 | export * from './useUploadFile';
38 | export * from './useVisible';
39 |
--------------------------------------------------------------------------------
/src/index.md:
--------------------------------------------------------------------------------
1 | # 函数列表
2 |
3 | - [tryOnBackPress](./tryOnBackPress/index.md)
4 | - [tryOnHide](./tryOnHide/index.md)
5 | - [tryOnInit](./tryOnInit/index.md)
6 | - [tryOnLoad](./tryOnLoad/index.md)
7 | - [tryOnReady](./tryOnReady/index.md)
8 | - [tryOnScopeDispose](./tryOnScopeDispose/index.md)
9 | - [tryOnShow](./tryOnShow/index.md)
10 | - [tryOnUnload](./tryOnUnload/index.md)
11 | - [useActionSheet](./useActionSheet/index.md)
12 | - [useClipboardData](./useClipboardData/index.md)
13 | - [useDownloadFile](./useDownloadFile/index.md)
14 | - [useGlobalData](./useGlobalData/index.md)
15 | - [useInterceptor](./useInterceptor/index.md)
16 | - [useLoading](./useLoading/index.md)
17 | - [useModal](./useModal/index.md)
18 | - [useNetwork](./useNetwork/index.md)
19 | - [useOnline](./useOnline/index.md)
20 | - [usePage](./usePage/index.md)
21 | - [usePages](./usePages/index.md)
22 | - [usePageScroll](./usePageScroll/index.md)
23 | - [usePreferredDark](./usePreferredDark/index.md)
24 | - [usePreferredLanguage](./usePreferredLanguage/index.md)
25 | - [usePrevPage](./usePrevPage/index.md)
26 | - [usePrevRoute](./usePrevRoute/index.md)
27 | - [useProvider](./useProvider/index.md)
28 | - [useRequest](./useRequest/index.md)
29 | - [useRoute](./useRoute/index.md)
30 | - [useRouter](./useRouter/index.md)
31 | - [useScanCode](./useScanCode/index.md)
32 | - [useScreenBrightness](./useScreenBrightness/index.md)
33 | - [useSelectorQuery](./useSelectorQuery/index.md)
34 | - [useSocket](./useSocket/index.md)
35 | - [useStorage](./useStorage/index.md)
36 | - [useStorageSync](./useStorageSync/index.md)
37 | - [useToast](./useToast/index.md)
38 | - [useUploadFile](./useUploadFile/index.md)
39 | - [useVisible](./useVisible/index.md)
40 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { name } from '../package.json';
2 | import * as UniUse from './exports';
3 |
4 | export * from './exports';
5 |
6 | /** @deprecated 建议使用 `uniuseAutoImports` 函数 */
7 | export const UniUseAutoImports: Record> = {
8 | [name]: Object.keys(UniUse),
9 | };
10 |
11 | export type UniUseFunction = keyof typeof UniUse;
12 |
13 | export interface UniUseAutoImportsOptions {
14 | only?: UniUseFunction[];
15 | except?: UniUseFunction[];
16 | }
17 |
18 | /** 自定义配置 unplugin-auto-import */
19 | export function uniuseAutoImports(options: UniUseAutoImportsOptions = {}) {
20 | let exports = Object.keys(UniUse);
21 |
22 | if (options.only) {
23 | exports = exports.filter(fn => (options.only as string[])!.includes(fn));
24 | }
25 |
26 | if (options.except) {
27 | exports = exports.filter(fn => !(options.except as string[])!.includes(fn));
28 | }
29 |
30 | return {
31 | [name]: exports,
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/src/tryOnBackPress/index.md:
--------------------------------------------------------------------------------
1 | # tryOnBackPress
2 |
3 | 尝试执行 onBackPress。如果是在组件生命周期内,就直接调用 `onBackPress`;如果不是,就根据设定间隔重新尝试。
4 |
5 | 前两个参数和 `onBackPress` 完全一致。
6 |
7 | ```typescript
8 | import { tryOnBackPress } from '@uni-helper/uni-use';
9 |
10 | tryOnBackPress((e) => {
11 | if (e.from === 'navigateBack') {
12 | // do somthing
13 | }
14 |
15 | if (e.from === 'backbutton') {
16 | // do something
17 | }
18 | }, null, {
19 | retry: 5, // optional
20 | interval: 100, // optional
21 | });
22 | ```
23 |
--------------------------------------------------------------------------------
/src/tryOnBackPress/index.ts:
--------------------------------------------------------------------------------
1 | import { onBackPress } from '@dcloudio/uni-app';
2 | import { getCurrentInstance } from 'vue';
3 | import { sleep } from '../utils';
4 |
5 | export interface TryOnBackPressOptions {
6 | /**
7 | * 最大尝试次数
8 | *
9 | * @default 3
10 | */
11 | retry?: number;
12 | /**
13 | * 尝试间隔时长,单位 ms
14 | *
15 | * @default 500
16 | */
17 | interval?: number;
18 | }
19 |
20 | type OnBackPressParameters = Parameters;
21 |
22 | /** 尝试绑定 onBackPress 超出尝试次数将调用 onError */
23 | export async function tryOnBackPress(
24 | hook: OnBackPressParameters[0],
25 | target?: OnBackPressParameters[1],
26 | options: TryOnBackPressOptions = {},
27 | ) {
28 | const {
29 | retry = 3,
30 | interval = 500,
31 | } = options;
32 |
33 | function tryBind() {
34 | const instance = (target || getCurrentInstance()) as OnBackPressParameters[1] | undefined;
35 | if (instance) {
36 | onBackPress(hook, instance);
37 | return true;
38 | }
39 |
40 | return false;
41 | }
42 |
43 | for (let circle = 1; circle <= retry; circle++) {
44 | if (tryBind()) {
45 | return;
46 | }
47 | await sleep(interval);
48 | }
49 |
50 | throw new Error('Binding onBackPress failed, maximum number of attempts exceeded.');
51 | }
52 |
--------------------------------------------------------------------------------
/src/tryOnHide/index.md:
--------------------------------------------------------------------------------
1 | # tryOnHide
2 |
3 | 尝试获取组件生命周期,并调用 `onHide`
4 |
5 | 超过重试次数,根据 `runFinally` 直接执行或抛出异常
6 |
7 | ```typescript
8 | import { tryOnHide } from '@uni-helper/uni-use';
9 |
10 | tryOnHide(() => {
11 | // ...
12 | });
13 | ```
14 |
--------------------------------------------------------------------------------
/src/tryOnHide/index.ts:
--------------------------------------------------------------------------------
1 | import type { TryOptions } from '../types';
2 | import { onHide } from '@dcloudio/uni-app';
3 | import { getCurrentInstance } from 'vue';
4 | import { sleep } from '../utils';
5 |
6 | type OnHideParameters = Parameters;
7 |
8 | export type TryOnHideOptions = TryOptions;
9 |
10 | /**
11 | * 尝试获取组件生命周期,并调用 onHide
12 | *
13 | * 超过重试次数,根据 runFinally 直接执行或抛出异常
14 | */
15 | export async function tryOnHide(
16 | hook: OnHideParameters[0],
17 | target?: OnHideParameters[1],
18 | options: TryOnHideOptions = {},
19 | ) {
20 | const {
21 | retry = 3,
22 | interval = 500,
23 | runFinally = true,
24 | } = options;
25 |
26 | function tryBind() {
27 | const instance = (target || getCurrentInstance()) as OnHideParameters[1] | undefined;
28 | if (instance) {
29 | onHide(hook, instance);
30 | return true;
31 | }
32 |
33 | return false;
34 | }
35 | for (let circle = 1; circle <= retry; circle++) {
36 | if (tryBind()) {
37 | return;
38 | }
39 | await sleep(interval);
40 | }
41 |
42 | if (runFinally) {
43 | return onHide(hook);
44 | }
45 |
46 | throw new Error('Binding onHide failed, maximum number of attempts exceeded.');
47 | }
48 |
--------------------------------------------------------------------------------
/src/tryOnInit/index.md:
--------------------------------------------------------------------------------
1 | # tryOnInit
2 |
3 | 尝试获取组件生命周期,并调用 `onInit`
4 |
5 | 超过重试次数,根据 `runFinally` 直接执行或抛出异常
6 |
7 | ```typescript
8 | import { tryOnInit } from '@uni-helper/uni-use';
9 |
10 | tryOnInit(() => {
11 | // ...
12 | });
13 | ```
14 |
--------------------------------------------------------------------------------
/src/tryOnInit/index.ts:
--------------------------------------------------------------------------------
1 | import type { TryOptions } from '../types';
2 | import { onInit } from '@dcloudio/uni-app';
3 | import { getCurrentInstance } from 'vue';
4 | import { sleep } from '../utils';
5 |
6 | type OnInitParameters = Parameters;
7 |
8 | export type TryOnInitOptions = TryOptions;
9 |
10 | /**
11 | * 尝试获取组件生命周期,并调用 onInit
12 | *
13 | * 超过重试次数,根据 runFinally 直接执行或抛出异常
14 | */
15 | export async function tryOnInit(
16 | hook: OnInitParameters[0],
17 | target?: OnInitParameters[1],
18 | options: TryOnInitOptions = {},
19 | ) {
20 | const {
21 | retry = 3,
22 | interval = 500,
23 | runFinally = true,
24 | } = options;
25 |
26 | function tryBind() {
27 | const instance = (target || getCurrentInstance()) as OnInitParameters[1] | undefined;
28 | if (instance) {
29 | onInit(hook, instance);
30 | return true;
31 | }
32 |
33 | return false;
34 | }
35 | for (let circle = 1; circle <= retry; circle++) {
36 | if (tryBind()) {
37 | return;
38 | }
39 | await sleep(interval);
40 | }
41 |
42 | if (runFinally) {
43 | return onInit(hook);
44 | }
45 |
46 | throw new Error('Binding onInit failed, maximum number of attempts exceeded.');
47 | }
48 |
--------------------------------------------------------------------------------
/src/tryOnLoad/index.md:
--------------------------------------------------------------------------------
1 | # tryOnLoad
2 |
3 | 尝试获取组件生命周期,并调用 `onLoad`
4 |
5 | 超过重试次数,根据 `runFinally` 直接执行或抛出异常
6 |
7 | ```typescript
8 | import { tryOnLoad } from '@uni-helper/uni-use';
9 |
10 | tryOnLoad(() => {
11 | // ...
12 | });
13 | ```
14 |
--------------------------------------------------------------------------------
/src/tryOnLoad/index.ts:
--------------------------------------------------------------------------------
1 | import type { TryOptions } from '../types';
2 | import { onLoad } from '@dcloudio/uni-app';
3 | import { getCurrentInstance } from 'vue';
4 | import { sleep } from '../utils';
5 |
6 | type OnLoadParameters = Parameters;
7 |
8 | export type TryOnLoadOptions = TryOptions;
9 |
10 | /**
11 | * 尝试获取组件生命周期,并调用 onLoad
12 | *
13 | * 超过重试次数,根据 runFinally 直接执行或抛出异常
14 | */
15 | export async function tryOnLoad(
16 | hook: OnLoadParameters[0],
17 | target?: OnLoadParameters[1],
18 | options: TryOnLoadOptions = {},
19 | ) {
20 | const {
21 | retry = 3,
22 | interval = 500,
23 | runFinally = true,
24 | } = options;
25 |
26 | function tryBind() {
27 | const instance = (target || getCurrentInstance()) as OnLoadParameters[1] | undefined;
28 | if (instance) {
29 | onLoad(hook, instance);
30 | return true;
31 | }
32 |
33 | return false;
34 | }
35 | for (let circle = 1; circle <= retry; circle++) {
36 | if (tryBind()) {
37 | return;
38 | }
39 | await sleep(interval);
40 | }
41 |
42 | if (runFinally) {
43 | return onLoad(hook);
44 | }
45 |
46 | throw new Error('Binding onLoad failed, maximum number of attempts exceeded.');
47 | }
48 |
--------------------------------------------------------------------------------
/src/tryOnReady/index.md:
--------------------------------------------------------------------------------
1 | # tryOnReady
2 |
3 | 尝试获取组件生命周期,并调用 `onReady`
4 |
5 | 超过重试次数,根据 `runFinally` 直接执行或抛出异常
6 |
7 | ```typescript
8 | import { tryOnReady } from '@uni-helper/uni-use';
9 |
10 | tryOnReady(() => {
11 | // ...
12 | });
13 | ```
14 |
--------------------------------------------------------------------------------
/src/tryOnReady/index.ts:
--------------------------------------------------------------------------------
1 | import type { TryOptions } from '../types';
2 | import { onReady } from '@dcloudio/uni-app';
3 | import { getCurrentInstance } from 'vue';
4 | import { sleep } from '../utils';
5 |
6 | type OnReadyParameters = Parameters;
7 |
8 | export type TryOnReadyOptions = TryOptions;
9 |
10 | /**
11 | * 尝试获取组件生命周期,并调用 onReady
12 | *
13 | * 超过重试次数,根据 runFinally 直接执行或抛出异常
14 | */
15 | export async function tryOnReady(
16 | hook: OnReadyParameters[0],
17 | target?: OnReadyParameters[1],
18 | options: TryOnReadyOptions = {},
19 | ) {
20 | const {
21 | retry = 3,
22 | interval = 500,
23 | runFinally = true,
24 | } = options;
25 |
26 | function tryBind() {
27 | const instance = (target || getCurrentInstance()) as OnReadyParameters[1] | undefined;
28 | if (instance) {
29 | onReady(hook, instance);
30 | return true;
31 | }
32 |
33 | return false;
34 | }
35 | for (let circle = 1; circle <= retry; circle++) {
36 | if (tryBind()) {
37 | return;
38 | }
39 | await sleep(interval);
40 | }
41 |
42 | if (runFinally) {
43 | return onReady(hook);
44 | }
45 |
46 | throw new Error('Binding onReady failed, maximum number of attempts exceeded.');
47 | }
48 |
--------------------------------------------------------------------------------
/src/tryOnScopeDispose/index.md:
--------------------------------------------------------------------------------
1 | # tryOnScopeDispose
2 |
3 | Safe `onScopeDispose`. Call `onScopeDispose()` if it's inside an effect scope lifecycle, if not, do nothing
4 |
5 | ## Usage
6 |
7 | ```js
8 | import { tryOnScopeDispose } from '@uni-helper/uni-use';
9 |
10 | tryOnScopeDispose(() => {
11 |
12 | });
13 | ```
14 |
--------------------------------------------------------------------------------
/src/tryOnScopeDispose/index.ts:
--------------------------------------------------------------------------------
1 | import { getCurrentScope, onScopeDispose } from 'vue';
2 |
3 | /**
4 | * Call onScopeDispose() if it's inside an effect scope lifecycle, if not, do nothing
5 | *
6 | * @param fn
7 | */
8 | export function tryOnScopeDispose(fn: () => void) {
9 | if (getCurrentScope()) {
10 | onScopeDispose(fn);
11 | return true;
12 | }
13 | return false;
14 | }
15 |
--------------------------------------------------------------------------------
/src/tryOnShow/index.md:
--------------------------------------------------------------------------------
1 | # tryOnShow
2 |
3 | 尝试获取组件生命周期,并调用 `onShow`
4 |
5 | 超过重试次数,根据 `runFinally` 直接执行或抛出异常
6 |
7 | ```typescript
8 | import { tryOnShow } from '@uni-helper/uni-use';
9 |
10 | tryOnShow(() => {
11 | // ...
12 | });
13 | ```
14 |
--------------------------------------------------------------------------------
/src/tryOnShow/index.ts:
--------------------------------------------------------------------------------
1 | import type { TryOptions } from '../types';
2 | import { onShow } from '@dcloudio/uni-app';
3 | import { getCurrentInstance } from 'vue';
4 | import { sleep } from '../utils';
5 |
6 | type OnShowParameters = Parameters;
7 |
8 | export type TryOnShowOptions = TryOptions;
9 |
10 | /**
11 | * 尝试获取组件生命周期,并调用 onShow
12 | *
13 | * 超过重试次数,根据 runFinally 直接执行或抛出异常
14 | */
15 | export async function tryOnShow(
16 | hook: OnShowParameters[0],
17 | target?: OnShowParameters[1],
18 | options: TryOnShowOptions = {},
19 | ) {
20 | const {
21 | retry = 3,
22 | interval = 500,
23 | runFinally = true,
24 | } = options;
25 |
26 | function tryBind() {
27 | const instance = (target || getCurrentInstance()) as OnShowParameters[1] | undefined;
28 | if (instance) {
29 | onShow(hook, instance);
30 | return true;
31 | }
32 |
33 | return false;
34 | }
35 | for (let circle = 1; circle <= retry; circle++) {
36 | if (tryBind()) {
37 | return;
38 | }
39 | await sleep(interval);
40 | }
41 |
42 | if (runFinally) {
43 | return onShow(hook);
44 | }
45 |
46 | throw new Error('Binding onShow failed, maximum number of attempts exceeded.');
47 | }
48 |
--------------------------------------------------------------------------------
/src/tryOnUnload/index.md:
--------------------------------------------------------------------------------
1 | # tryOnUnload
2 |
3 | 尝试获取组件生命周期,并调用 `onUnload`
4 |
5 | 超过重试次数,根据 `runFinally` 直接执行或抛出异常
6 |
7 | ```typescript
8 | import { tryOnUnload } from '@uni-helper/uni-use';
9 |
10 | tryOnUnload(() => {
11 | // ...
12 | });
13 | ```
14 |
--------------------------------------------------------------------------------
/src/tryOnUnload/index.ts:
--------------------------------------------------------------------------------
1 | import type { TryOptions } from '../types';
2 | import { onUnload } from '@dcloudio/uni-app';
3 | import { getCurrentInstance } from 'vue';
4 | import { sleep } from '../utils';
5 |
6 | type OnUnloadParameters = Parameters;
7 |
8 | export type TryOnUnloadOptions = TryOptions;
9 |
10 | /**
11 | * 尝试获取组件生命周期,并调用 onUnload
12 | *
13 | * 超过重试次数,根据 runFinally 直接执行或抛出异常
14 | */
15 | export async function tryOnUnload(
16 | hook: OnUnloadParameters[0],
17 | target?: OnUnloadParameters[1],
18 | options: TryOnUnloadOptions = {},
19 | ) {
20 | const {
21 | retry = 3,
22 | interval = 500,
23 | runFinally = true,
24 | } = options;
25 |
26 | function tryBind() {
27 | const instance = (target || getCurrentInstance()) as OnUnloadParameters[1] | undefined;
28 | if (instance) {
29 | onUnload(hook, instance);
30 | return true;
31 | }
32 |
33 | return false;
34 | }
35 | for (let circle = 1; circle <= retry; circle++) {
36 | if (tryBind()) {
37 | return;
38 | }
39 | await sleep(interval);
40 | }
41 |
42 | if (runFinally) {
43 | return onUnload(hook);
44 | }
45 |
46 | throw new Error('Binding onUnload failed, maximum number of attempts exceeded.');
47 | }
48 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { Ref } from 'vue';
2 |
3 | export type AnyRecord = Record;
4 |
5 | export type MaybeRef = T | Ref;
6 |
7 | export type MaybeRefOrGetter = MaybeRef | (() => T);
8 |
9 | export type MaybeComputedRef = MaybeRefOrGetter;
10 |
11 | export type Optional = Pick, K> & Omit;
12 |
13 | export type RequiredProperty = T & { [P in K]-?: T[P] };
14 |
15 | export type RequiredOnly = RequiredProperty, K>;
16 |
17 | export type MaybePromise = T | Promise;
18 |
19 | export interface TryOptions {
20 | /**
21 | * 最大尝试次数
22 | *
23 | * @default 3
24 | */
25 | retry?: number;
26 | /**
27 | * 尝试间隔时长,单位 ms
28 | *
29 | * @default 500
30 | */
31 | interval?: number;
32 |
33 | /**
34 | * 当超时时是否立即执行, 值为false时将在最后无法运行时抛出异常
35 | *
36 | * @default true
37 | */
38 | runFinally?: boolean;
39 | }
40 |
--------------------------------------------------------------------------------
/src/useActionSheet/index.md:
--------------------------------------------------------------------------------
1 | # useActionSheet
2 |
3 | 返回一个方法,调用后从底部向上弹出操作菜单。
4 |
5 | ```typescript
6 | import { useActionSheet } from '@uni-helper/uni-use';
7 |
8 | const showActionSheet = useActionSheet({
9 | /* 传入配置 */
10 | });
11 | showActionSheet(); // 从底部向上弹出操作菜单
12 | ```
13 |
14 | 调用方法时,可以传入一个对象来更新已有配置,这样会使用 [扩展运算符](https://es6.ruanyifeng.com/#docs/object#%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%89%A9%E5%B1%95%E8%BF%90%E7%AE%97%E7%AC%A6) 来确认最终配置。
15 |
16 | ```typescript
17 | showActionSheet({
18 | /* 新传入配置 */
19 | });
20 | ```
21 |
--------------------------------------------------------------------------------
/src/useActionSheet/index.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeComputedRef } from '../types';
2 | import { resolveUnref } from '@vueuse/core';
3 | import { reactive } from 'vue';
4 |
5 | export interface UniShowActionSheetOptions extends Omit {
6 | /** 文字数组 */
7 | itemList: string[];
8 | }
9 | export type ShowActionSheetOptions = MaybeComputedRef;
10 | export type UseActionSheetOptions = ShowActionSheetOptions;
11 |
12 | /**
13 | * 返回一个方法,调用后从底部向上弹出操作菜单
14 | *
15 | * https://uniapp.dcloud.net.cn/api/ui/prompt.html#showactionsheet
16 | */
17 | export function useActionSheet(options?: UseActionSheetOptions) {
18 | /**
19 | * 从底部向上弹出操作菜单
20 | *
21 | * https://uniapp.dcloud.net.cn/api/ui/prompt.html#showactionsheet
22 | */
23 | return function showActionSheet(newOptions?: ShowActionSheetOptions) {
24 | return uni.showActionSheet(
25 | reactive({
26 | itemList: [],
27 | ...resolveUnref(options),
28 | ...resolveUnref(newOptions),
29 | }),
30 | );
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/useClipboardData/index.md:
--------------------------------------------------------------------------------
1 | # useClipboardData
2 |
3 | 获取和设置剪切板数据。你需要将默认值作为第一个参数传入。
4 |
5 | ```typescript
6 | import { useClipboardData } from '@uni-helper/uni-use';
7 |
8 | const clipboardData = useClipboardData('');
9 |
10 | // 查看剪切板数据
11 | console.log('clipboardData', clipboardData.value);
12 | // 设置剪切板数据
13 | clipboardData.value = 'abc';
14 | ```
15 |
16 | 为了在操作数据后不显示消息提示框,你可以传递第二个参数。
17 |
18 | ```typescript
19 | import { useClipboardData } from '@uni-helper/uni-use';
20 |
21 | const clipboardData = useClipboardData('', { showToast: false });
22 | ```
23 |
24 | 默认使用 `console.error` 输出错误信息,你也可以自定义错误处理。
25 |
26 | ```typescript
27 | import { useClipboardData } from '@uni-helper/uni-use';
28 |
29 | const clipboardData = useClipboardData('', {
30 | onError: (error) => {
31 | console.log(error);
32 | }
33 | });
34 | ```
35 |
--------------------------------------------------------------------------------
/src/useClipboardData/index.ts:
--------------------------------------------------------------------------------
1 | import type { ConfigurableEventFilter, ConfigurableFlush } from '@vueuse/core';
2 | import type { Ref } from 'vue';
3 | import type { MaybeComputedRef } from '../types';
4 | import { watchWithFilter } from '@vueuse/core';
5 | import { ref } from 'vue';
6 | import { useInterceptor } from '../useInterceptor';
7 |
8 | function getClipboardData(showToast = true) {
9 | return new Promise((resolve, reject) => {
10 | uni.getClipboardData({
11 | showToast,
12 | success: ({ data }) => resolve(data),
13 | fail: error => reject(error),
14 | complete: () => {
15 | if (!showToast) {
16 | uni.hideToast();
17 | }
18 | },
19 | });
20 | if (!showToast) {
21 | uni.hideToast();
22 | }
23 | });
24 | }
25 |
26 | function setClipboardData(data: string, showToast = true) {
27 | return new Promise((resolve, reject) => {
28 | uni.setClipboardData({
29 | data,
30 | showToast,
31 | success: ({ data }) => resolve(data),
32 | fail: error => reject(error),
33 | complete: () => {
34 | if (!showToast) {
35 | uni.hideToast();
36 | }
37 | },
38 | });
39 | if (!showToast) {
40 | uni.hideToast();
41 | }
42 | });
43 | }
44 |
45 | export interface UseClipboardDataOptions extends ConfigurableEventFilter, ConfigurableFlush {
46 | /**
47 | * 操作剪切板数据后是否显示 toast
48 | *
49 | * @default true
50 | */
51 | showToast?: boolean;
52 | /**
53 | * 是否监听 setClipboardData 引起的剪切板变化
54 | *
55 | * @default true
56 | */
57 | listenToClipboardDataChanges?: boolean;
58 | /**
59 | * 错误回调
60 | *
61 | * 默认用 `console.error` 打印错误
62 | */
63 | onError?: (error: unknown) => void;
64 | }
65 |
66 | /**
67 | * 剪切板
68 | *
69 | * https://uniapp.dcloud.net.cn/api/system/clipboard.html
70 | */
71 | export function useClipboardData(
72 | initialValue: MaybeComputedRef,
73 | options: UseClipboardDataOptions = {},
74 | ) {
75 | const {
76 | showToast = true,
77 | listenToClipboardDataChanges = true,
78 | onError = error => console.error(error),
79 | flush = 'pre',
80 | eventFilter,
81 | } = options;
82 |
83 | const data = ref(initialValue) as Ref;
84 |
85 | async function read() {
86 | try {
87 | data.value = await getClipboardData(showToast);
88 | }
89 | catch (error) {
90 | onError(error);
91 | }
92 | }
93 |
94 | read();
95 |
96 | if (listenToClipboardDataChanges) {
97 | useInterceptor('setClipboardData', { complete: () => setTimeout(() => read(), 0) });
98 | }
99 |
100 | watchWithFilter(
101 | data,
102 | async () => {
103 | try {
104 | await setClipboardData(data.value);
105 | }
106 | catch (error) {
107 | onError(error);
108 | }
109 | },
110 | { flush, eventFilter },
111 | );
112 |
113 | return data;
114 | }
115 |
--------------------------------------------------------------------------------
/src/useDownloadFile/index.md:
--------------------------------------------------------------------------------
1 | # useDownloadFile
2 |
3 | `uni.downloadFile` 的封装,对标 `@vueuse/core` v10.7.1。使用方法参见 。
4 |
5 | **返回值中含有 task,可自行操作。**
6 |
--------------------------------------------------------------------------------
/src/useDownloadFile/index.ts:
--------------------------------------------------------------------------------
1 | import { until } from '@vueuse/core';
2 | import { type Ref, ref, type ShallowRef, shallowRef } from 'vue';
3 | import { isString, noop } from '../utils';
4 |
5 | /** 对标 @vueuse/core v10.7.1 useAxios */
6 |
7 | export interface UseDownloadFileReturn {
8 | task: ShallowRef;
9 |
10 | /** uni.downloadFile 响应 */
11 | response: ShallowRef;
12 |
13 | /** uni.downloadFile 响应内的数据 */
14 | data: Ref;
15 |
16 | /** 下载是否完成 */
17 | isFinished: Ref;
18 |
19 | /** 下载是否进行中 */
20 | isLoading: Ref;
21 |
22 | /** 下载是否中止 */
23 | isAborted: Ref;
24 |
25 | /** 下载间发生的错误 */
26 | error: ShallowRef;
27 |
28 | /** 中止当前下载 */
29 | abort: (message?: string | undefined) => void;
30 |
31 | /** abort 别名 */
32 | cancel: (message?: string | undefined) => void;
33 |
34 | /** isAborted 别名 */
35 | isCanceled: Ref;
36 | }
37 | export interface StrictUseDownloadFileReturn extends UseDownloadFileReturn {
38 | /** 手动开始下载 */
39 | execute: (
40 | url?: string | UniApp.DownloadFileOption,
41 | config?: UniApp.DownloadFileOption,
42 | ) => PromiseLike>;
43 | }
44 | export interface EasyUseDownloadFileReturn extends UseDownloadFileReturn {
45 | /** 手动开始下载 */
46 | execute: (
47 | url: string,
48 | config?: UniApp.DownloadFileOption,
49 | ) => PromiseLike>;
50 | }
51 | export type OverallUseDownloadFileReturn =
52 | | StrictUseDownloadFileReturn
53 | | EasyUseDownloadFileReturn;
54 |
55 | export interface UseDownloadFileOptions {
56 | /** 是否自动开始下载 */
57 | immediate?: boolean;
58 |
59 | /**
60 | * 是否使用 shallowRef
61 | *
62 | * @default true
63 | */
64 | shallow?: boolean;
65 |
66 | /** 下载错误时的回调 */
67 | onError?: (e: UniApp.GeneralCallbackResult) => void;
68 |
69 | /** 下载成功时的回调 */
70 | onSuccess?: (data: T) => void;
71 |
72 | /** 要使用的初始化数据 */
73 | initialData?: T;
74 |
75 | /** 是否在执行承诺之前将状态设置为初始状态 */
76 | resetOnExecute?: boolean;
77 |
78 | /** 下载结束时的回调 */
79 | onFinish?: (result?: UniApp.GeneralCallbackResult) => void;
80 | }
81 |
82 | export function useDownloadFile(
83 | url: string,
84 | config?: UniApp.DownloadFileOption,
85 | options?: UseDownloadFileOptions,
86 | ): StrictUseDownloadFileReturn & PromiseLike>;
87 | export function useDownloadFile(
88 | config?: UniApp.DownloadFileOption,
89 | ): EasyUseDownloadFileReturn & PromiseLike>;
90 |
91 | /** uni.downloadFile 的封装 */
92 | export function useDownloadFile(
93 | ...args: any[]
94 | ): OverallUseDownloadFileReturn & PromiseLike> {
95 | const url: string | undefined = typeof args[0] === 'string' ? args[0] : undefined;
96 | const argsPlaceholder = isString(url) ? 1 : 0;
97 | const defaultOptions: UseDownloadFileOptions = {
98 | immediate: !!argsPlaceholder,
99 | shallow: true,
100 | };
101 | let defaultConfig: Partial = {};
102 | let options: UseDownloadFileOptions = defaultOptions;
103 |
104 | if (args.length > 0 + argsPlaceholder) {
105 | defaultConfig = args[0 + argsPlaceholder];
106 | }
107 |
108 | if (args.length === 3) {
109 | options = args[0 + argsPlaceholder];
110 | }
111 |
112 | const {
113 | initialData,
114 | shallow,
115 | onSuccess = noop,
116 | onError = noop,
117 | onFinish = noop,
118 | immediate,
119 | resetOnExecute = false,
120 | } = options;
121 |
122 | const task = shallowRef();
123 | const response = shallowRef();
124 | const data = shallow ? shallowRef() : ref();
125 | const isFinished = ref(false);
126 | const isLoading = ref(false);
127 | const isAborted = ref(false);
128 | const error = shallowRef();
129 |
130 | const abort = (message?: string) => {
131 | if (isFinished.value || !isLoading.value) {
132 | return;
133 | }
134 | // @ts-expect-error no types
135 | task.value?.abort(message);
136 | isAborted.value = true;
137 | isLoading.value = false;
138 | isFinished.value = false;
139 | };
140 |
141 | const loading = (loading: boolean) => {
142 | isLoading.value = loading;
143 | isFinished.value = !loading;
144 | };
145 |
146 | const resetData = () => {
147 | if (resetOnExecute) {
148 | data.value = initialData;
149 | }
150 | };
151 |
152 | const promise = {
153 | then: (...args) => waitUntilFinished().then(...args),
154 | catch: (...args) => waitUntilFinished().catch(...args),
155 | } as Promise>;
156 |
157 | let executeCounter = 0;
158 | const execute: OverallUseDownloadFileReturn['execute'] = (
159 | executeUrl: string | UniApp.DownloadFileOption | undefined = url,
160 | config: Partial = {},
161 | ) => {
162 | error.value = undefined;
163 | const _url = typeof executeUrl === 'string' ? executeUrl : url ?? config.url;
164 |
165 | if (_url === undefined) {
166 | error.value = {
167 | errMsg: 'Invalid URL provided for uni.request.',
168 | };
169 | isFinished.value = true;
170 | return promise;
171 | }
172 | resetData();
173 | abort();
174 | loading(true);
175 |
176 | executeCounter += 1;
177 | const currentExecuteCounter = executeCounter;
178 | isAborted.value = false;
179 |
180 | const _config = {
181 | ...defaultConfig,
182 | ...(typeof executeUrl === 'object' ? executeUrl : config),
183 | url: _url,
184 | };
185 | task.value = uni.downloadFile({
186 | ..._config,
187 | success: (r) => {
188 | if (isAborted.value) {
189 | return;
190 | }
191 | _config.success?.(r);
192 | response.value = r;
193 | const result
194 | // @ts-expect-error no types
195 | = r?.data
196 | ?? ({
197 | tempFilePath: r?.tempFilePath,
198 | } as unknown as T);
199 | data.value = result;
200 | onSuccess(result);
201 | },
202 | fail: (e) => {
203 | _config.fail?.(e);
204 | error.value = e;
205 | onError(e);
206 | },
207 | complete: (r) => {
208 | _config.complete?.(r);
209 | onFinish(r);
210 | if (currentExecuteCounter === executeCounter) {
211 | loading(false);
212 | }
213 | },
214 | });
215 | return promise;
216 | };
217 | if (immediate && url) {
218 | (execute as StrictUseDownloadFileReturn['execute'])();
219 | }
220 |
221 | const result = {
222 | task,
223 | response,
224 | data,
225 | error,
226 | isFinished,
227 | isLoading,
228 | cancel: abort,
229 | isAborted,
230 | isCanceled: isAborted,
231 | abort,
232 | execute,
233 | } as OverallUseDownloadFileReturn;
234 |
235 | function waitUntilFinished() {
236 | return new Promise>((resolve, reject) => {
237 | until(isFinished)
238 | .toBe(true)
239 | .then(() => (error.value ? reject(error.value) : resolve(result)));
240 | });
241 | }
242 |
243 | return {
244 | ...result,
245 | ...promise,
246 | };
247 | }
248 |
--------------------------------------------------------------------------------
/src/useGlobalData/index.md:
--------------------------------------------------------------------------------
1 | # useGlobalData
2 |
3 | 获取和设置当前应用实例的 `globalData`。你需要将默认值作为第一个参数传入。
4 |
5 | ```typescript
6 | import { useGlobalData } from '@uni-helper/uni-use';
7 |
8 | const globalData = useGlobalData({});
9 | ```
10 |
11 | 如果你需要使用 `shallowRef`,需要在第二个参数中指明。如果你需要设置一个很大的数据,`shallowRef` 会很有用。
12 |
13 | ```typescript
14 | useGlobalData({}, { shallow: true });
15 | ```
16 |
17 | 我们建议直接使用 [pinia](https://pinia.vuejs.org/zh/) 作为状态管理工具。
18 |
--------------------------------------------------------------------------------
/src/useGlobalData/index.ts:
--------------------------------------------------------------------------------
1 | import type { ConfigurableEventFilter, ConfigurableFlush, RemovableRef } from '@vueuse/core';
2 | import type { MaybeComputedRef } from '../types';
3 | import { resolveUnref, watchWithFilter } from '@vueuse/core';
4 | import { ref, shallowRef } from 'vue';
5 | import { isFunction } from '../utils';
6 |
7 | export interface UseGlobalDataOptions
8 | extends ConfigurableEventFilter,
9 | ConfigurableFlush {
10 | /**
11 | * 当 globalData 还没有已有值时,是否写入 globalData
12 | *
13 | * @default true
14 | */
15 | writeDefaults?: boolean;
16 | /**
17 | * 是否合并默认值和已有值
18 | *
19 | * 当设置为 true 时,浅合并对象
20 | *
21 | * 你也可以传一个方法来自定义合并
22 | *
23 | * @default false
24 | */
25 | mergeDefaults?: boolean | ((nextValue: T, prevValue: T) => T);
26 | /**
27 | * 是否使用 shallowRef
28 | *
29 | * @default false
30 | */
31 | shallow?: boolean;
32 | /**
33 | * 是否监听深层变化
34 | *
35 | * @default true
36 | */
37 | deep?: boolean;
38 | }
39 |
40 | /**
41 | * globalData
42 | *
43 | * https://uniapp.dcloud.net.cn/collocation/App.html#globaldata
44 | */
45 |
46 | export function useGlobalData(
47 | initialValue: MaybeComputedRef,
48 | options: UseGlobalDataOptions = {},
49 | ) {
50 | const {
51 | writeDefaults = true,
52 | mergeDefaults = false,
53 | shallow = false,
54 | deep = true,
55 | flush = 'pre',
56 | eventFilter,
57 | } = options;
58 |
59 | const app = ref(getApp());
60 |
61 | const rawInit: T = resolveUnref(initialValue);
62 |
63 | const data = (shallow ? shallowRef : ref)(initialValue) as RemovableRef;
64 |
65 | watchWithFilter(data, () => (app.value.globalData = data.value ?? undefined), {
66 | flush,
67 | deep,
68 | eventFilter,
69 | });
70 |
71 | function read() {
72 | // 读取已有值
73 | const rawValue = app.value.globalData;
74 | if (rawValue == null) {
75 | if (writeDefaults && rawInit !== null) {
76 | app.value.globalData = rawInit;
77 | }
78 | return rawInit;
79 | }
80 | else if (mergeDefaults) {
81 | const value = rawValue as T;
82 | return isFunction(mergeDefaults)
83 | ? mergeDefaults(value, rawInit)
84 | : { ...(rawInit as any), ...value };
85 | }
86 | else {
87 | return rawValue;
88 | }
89 | }
90 |
91 | data.value = read();
92 |
93 | return data;
94 | }
95 |
--------------------------------------------------------------------------------
/src/useInterceptor/index.md:
--------------------------------------------------------------------------------
1 | # useInterceptor
2 |
3 | 设置拦截器。 支持拦截 sync 函数,并且 `invoke` 返回 false 将终止拦截器继续执行
4 |
5 | ```typescript
6 | import { useInterceptor } from '@uni-helper/uni-use';
7 |
8 | const event = 'request';
9 |
10 | // 设置拦截器
11 | const stop = useInterceptor(event, {
12 | invoke: (args) => {
13 | args[0].url = `https://www.example.com/${args[0].url}`;
14 | },
15 | success: (response) => {
16 | console.log('interceptor-success', response);
17 | response.data.code = 1;
18 | },
19 | fail: (error) => {
20 | console.log('interceptor-fail', error);
21 | },
22 | complete: () => {
23 | console.log('interceptor-complete');
24 | },
25 | });
26 |
27 | // 删除拦截器
28 | stop();
29 | ```
30 |
--------------------------------------------------------------------------------
/src/useInterceptor/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest';
2 | import { useInterceptor } from '.';
3 |
4 | describe('useInterceptor', () => {
5 | it('export module', () => {
6 | expect(useInterceptor).toBeDefined();
7 | });
8 |
9 | it('invoke args', () => {
10 | const key = 'custom-key';
11 |
12 | const stop = useInterceptor('getStorage', { invoke: (args) => {
13 | expect(args[0].key).toBe(key);
14 | } });
15 |
16 | uni.getStorage({ key });
17 |
18 | stop();
19 | });
20 |
21 | it('return value ASYNC', () => {
22 | const key = 'custom-key';
23 |
24 | uni.setStorageSync(key, 'a');
25 |
26 | const stop = useInterceptor('getStorage', { invoke: (args) => {
27 | expect(args[0].key).toBe(key);
28 | } });
29 |
30 | const returnVal = uni.getStorage({ key });
31 |
32 | expect(returnVal.then).toBeDefined();
33 |
34 | returnVal.then((val: any) => {
35 | expect(val).toStrictEqual({
36 | data: 'a',
37 | errMsg: 'getStorage:ok',
38 | });
39 | });
40 |
41 | stop();
42 | });
43 |
44 | it('return value SYNC', () => {
45 | const key = 'custom-key';
46 |
47 | uni.setStorageSync(key, 'b');
48 |
49 | const stop = useInterceptor('getStorageSync', { invoke: (args) => {
50 | expect(args[0]).toBe(key);
51 | } });
52 |
53 | const returnVal = uni.getStorageSync(key);
54 |
55 | expect(returnVal).toBe('b');
56 |
57 | stop();
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/src/useInterceptor/index.ts:
--------------------------------------------------------------------------------
1 | import { tryOnScopeDispose } from '../tryOnScopeDispose';
2 | import { isThenable } from '../utils';
3 |
4 | type FunctionKeys = {
5 | // eslint-disable-next-line ts/no-unsafe-function-type
6 | [K in keyof T]: T[K] extends Function ? K : never;
7 | }[keyof T];
8 |
9 | type UniMethod = FunctionKeys;
10 |
11 | export interface InterceptorOptions {
12 | /** 返回 false 则终止执行 */
13 | invoke?: (args: Parameters) => void | boolean;
14 |
15 | success?: Parameters[0]['success'] | ReturnType;
16 |
17 | fail?: Parameters[0]['fail'] | ((err: any) => void);
18 |
19 | complete?: Parameters[0]['complete'] | (() => void);
20 | }
21 |
22 | const globalInterceptors: Record> = {};
23 | const originMethods = {} as Record;
24 |
25 | /**
26 | * 包装uni-app中的方法,添加拦截器功能
27 | *
28 | * @param method uni-app中的方法名
29 | * @returns 包装后的方法
30 | */
31 | function wrappMethod(method: UniMethod) {
32 | // 判断是否已经包装过
33 | if (method in originMethods) {
34 | // 直接返回
35 | return uni[method];
36 | }
37 |
38 | // 获取原始方法
39 | const origin = uni[method];
40 | // 记录原始方法
41 | originMethods[method] = origin;
42 | // 原函数的类型定义
43 | type FN = typeof origin;
44 |
45 | // 开始包裹函数
46 | uni[method] = ((...args: Parameters) => {
47 | // 获取拦截器
48 | const interceptors = globalInterceptors[method] || {};
49 | // 实际起作用的拦截器
50 | const effectInterceptors: InterceptorOptions[] = [];
51 |
52 | // invoke 在函数执行前运行,返回false则终止此拦截器执行后续的 success / fail / complete 回调
53 | for (const [_key, interceptor] of Object.entries(interceptors)) {
54 | if (interceptor.invoke && interceptor.invoke(args) === false) {
55 | continue;
56 | }
57 |
58 | effectInterceptors.push(interceptor);
59 | }
60 |
61 | /**
62 | * 判断函数是否符合异步函数的参数
63 | * 含有 success / fail / complete 的async函数将不会返回promise
64 | * @see https://uniapp.dcloud.net.cn/api/#api-promise-%E5%8C%96
65 | */
66 | const hasAsyncOption = args.length === 1 && ((args[0] as any).success || (args[0] as any).fail || (args[0] as any).complete);
67 |
68 | if (hasAsyncOption) {
69 | const opt = args[0];
70 |
71 | const oldSuccess = opt.success;
72 | opt.success = (result: any) => {
73 | for (const interceptor of effectInterceptors) {
74 | interceptor.success && interceptor.success(result);
75 | }
76 | oldSuccess && oldSuccess(result);
77 | };
78 |
79 | const oldFail = opt.fail;
80 | opt.fail = (err: any) => {
81 | for (const interceptor of effectInterceptors) {
82 | interceptor.fail && interceptor.fail(err);
83 | }
84 | oldFail && oldFail(err);
85 | };
86 |
87 | const oldComplete = opt.complete;
88 | opt.complete = () => {
89 | for (const interceptor of effectInterceptors) {
90 | interceptor.complete && interceptor.complete();
91 | }
92 | oldComplete && oldComplete();
93 | };
94 |
95 | return (origin as any)(opt); // 保持和官方一致,不返回promise
96 | }
97 | else {
98 | try {
99 | const result = (origin as any)(...args);
100 |
101 | // is promise
102 | if (isThenable(result)) {
103 | // 如果返回值是 Promise,则将回调挂载在 Promise 上,直接返回当前 Promise
104 | return result.then((res: any) => {
105 | for (const interceptor of effectInterceptors) {
106 | interceptor.success && interceptor.success(res);
107 | }
108 | return res;
109 | }).catch((err: any) => {
110 | for (const interceptor of effectInterceptors) {
111 | interceptor.fail && interceptor.fail(err);
112 | }
113 | return err;
114 | });
115 | }
116 |
117 | // 不是 Promise,且未报错,执行 success 回调
118 | for (const interceptor of effectInterceptors) {
119 | interceptor.success && interceptor.success(result);
120 | }
121 |
122 | return result;
123 | }
124 | catch (err: any) { // only catch for not thenable
125 | // 不是 Promise,且报错,执行 fail 回调
126 | for (const interceptor of effectInterceptors) {
127 | interceptor.fail && interceptor.fail(err);
128 | }
129 | }
130 | finally { // finally for ALL (thenable and normal)
131 | // 无论是否 Promise 都执行的 complete 回调
132 | for (const interceptor of effectInterceptors) {
133 | interceptor.complete && interceptor.complete();
134 | }
135 | }
136 | }
137 | }) as any;
138 |
139 | return uni[method];
140 | }
141 |
142 | /**
143 | * 注册拦截器,在活跃的 effect 作用域停止时自动移除
144 | *
145 | * https://cn.vuejs.org/api/reactivity-advanced.htmlSeffectscope
146 | */
147 | export function useInterceptor(method: F, interceptor: InterceptorOptions) {
148 | // 包裹、封装函数,注入拦截器操作
149 | wrappMethod(method);
150 |
151 | globalInterceptors[method] = globalInterceptors[method] || {};
152 | const key = Math.random().toString(36).slice(-8);
153 | globalInterceptors[method][key] = interceptor;
154 |
155 | const stop = () => {
156 | delete globalInterceptors[method][key];
157 | };
158 |
159 | tryOnScopeDispose(stop);
160 |
161 | return stop;
162 | }
163 |
--------------------------------------------------------------------------------
/src/useLoading/index.md:
--------------------------------------------------------------------------------
1 | # useLoading
2 |
3 | 返回一个对象,包含两个方法。其中`showLoading` 调用后显示加载提示框,`hideLoading` 调用后隐藏加载提示框。
4 |
5 | ```typescript
6 | import { useLoading } from '@uni-helper/uni-use';
7 |
8 | const { showLoading, hideLoading } = useLoading({
9 | /* 传入配置 */
10 | });
11 | showLoading(); // 显示加载提示框
12 |
13 | hideLoading(); // 隐藏加载提示框
14 | ```
15 |
16 | 你也可以通过调用 `showLoading` 来获取 `hideLoading`。
17 |
18 | ```typescript
19 | import { useLoading } from '@uni-helper/uni-use';
20 |
21 | const { showLoading, hideLoading } = useLoading({
22 | /* 传入配置 */
23 | });
24 | const hideLoading = showLoading();
25 | ```
26 |
27 | 可以传入一个对象来更新已有配置,这样会使用 [扩展运算符](https://es6.ruanyifeng.com/#docs/object#%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%89%A9%E5%B1%95%E8%BF%90%E7%AE%97%E7%AC%A6) 来确认最终配置。
28 |
29 | ```typescript
30 | showLoading({
31 | /* 新传入配置 */
32 | });
33 | ```
34 |
--------------------------------------------------------------------------------
/src/useLoading/index.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeComputedRef } from '../types';
2 | import { resolveUnref } from '@vueuse/core';
3 | import { reactive } from 'vue';
4 |
5 | export interface UniShowLoadingOptions extends UniApp.ShowLoadingOptions {}
6 | export type ShowLoadingOptions = MaybeComputedRef;
7 | export type UseLoadingOptions = ShowLoadingOptions;
8 |
9 | function hideLoading() {
10 | return uni.hideLoading();
11 | }
12 |
13 | /**
14 | * 返回一个对象,包含两个方法
15 | *
16 | * 其中`showLoading` 调用后显示加载提示框,`hideLoading` 调用后隐藏加载提示框
17 | *
18 | * https://uniapp.dcloud.net.cn/api/ui/prompt.html#showloading
19 | */
20 | export function useLoading(options?: UseLoadingOptions) {
21 | function showLoading(newOptions?: ShowLoadingOptions) {
22 | uni.showLoading(
23 | reactive({
24 | ...resolveUnref(options),
25 | ...resolveUnref(newOptions),
26 | }),
27 | );
28 | return hideLoading;
29 | }
30 |
31 | return {
32 | /**
33 | * 显示加载提示框
34 | *
35 | * https://uniapp.dcloud.net.cn/api/ui/prompt.html#showloading
36 | */
37 | showLoading,
38 |
39 | /**
40 | * 隐藏加载提示框
41 | *
42 | * https://uniapp.dcloud.net.cn/api/ui/prompt.html#hideloading
43 | */
44 | hideLoading,
45 | };
46 | }
47 |
--------------------------------------------------------------------------------
/src/useModal/index.md:
--------------------------------------------------------------------------------
1 | # useModal
2 |
3 | 返回一个方法,调用后显示模态弹窗。
4 |
5 | ```typescript
6 | import { useModal } from '@uni-helper/uni-use';
7 |
8 | const showModal = useModal({
9 | /* 传入配置 */
10 | });
11 | showModal(); // 显示模态弹窗
12 | ```
13 |
14 | 可以传入一个对象来更新已有配置,这样会使用 [扩展运算符](https://es6.ruanyifeng.com/#docs/object#%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%89%A9%E5%B1%95%E8%BF%90%E7%AE%97%E7%AC%A6) 来确认最终配置。
15 |
16 | ```typescript
17 | showModal({
18 | /* 新传入配置 */
19 | });
20 | ```
21 |
--------------------------------------------------------------------------------
/src/useModal/index.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeComputedRef } from '../types';
2 | import { resolveUnref } from '@vueuse/core';
3 | import { reactive } from 'vue';
4 |
5 | export interface UniShowModalOptions extends UniApp.ShowModalOptions {}
6 | export type ShowModalOptions = MaybeComputedRef;
7 | export type UseModalOptions = ShowModalOptions;
8 |
9 | /**
10 | * 返回一个方法,调用后显示模态弹窗
11 | *
12 | * https://uniapp.dcloud.net.cn/api/ui/prompt?id=showmodal
13 | */
14 | export function useModal(options?: UseModalOptions) {
15 | /**
16 | * 显示模态弹窗
17 | *
18 | * https://uniapp.dcloud.net.cn/api/ui/prompt?id=showmodal
19 | */
20 | return function showModal(newOptions?: ShowModalOptions) {
21 | return uni.showModal(
22 | reactive({
23 | ...resolveUnref(options),
24 | ...resolveUnref(newOptions),
25 | }),
26 | );
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/src/useNetwork/index.md:
--------------------------------------------------------------------------------
1 | # useNetwork
2 |
3 | 获取网络信息。
4 |
5 | ```typescript
6 | import { useNetwork } from '@uni-helper/uni-use';
7 |
8 | const { type, isWifi, is2g, is3g, is4g, is5g, isEthernet, isUnknown, isOnline, isOffline }
9 | = useNetwork();
10 | ```
11 |
--------------------------------------------------------------------------------
/src/useNetwork/index.ts:
--------------------------------------------------------------------------------
1 | import { tryOnScopeDispose } from '@vueuse/core';
2 | import { computed, ref } from 'vue';
3 |
4 | export type NetworkType = 'ethernet' | 'none' | 'wifi' | 'unknown' | '2g' | '3g' | '4g' | '5g';
5 |
6 | /**
7 | * 获取网络信息
8 | *
9 | * https://uniapp.dcloud.net.cn/api/system/network.html
10 | */
11 | export function useNetwork() {
12 | const type = ref('none');
13 | const isWifi = computed(() => type.value === 'wifi');
14 | const is2g = computed(() => type.value === '2g');
15 | const is3g = computed(() => type.value === '3g');
16 | const is4g = computed(() => type.value === '4g');
17 | const is5g = computed(() => type.value === '5g');
18 | const isEthernet = computed(() => type.value === 'ethernet');
19 | const isUnknown = computed(() => type.value === 'unknown');
20 |
21 | const isOffline = computed(() => type.value === 'none');
22 | const isOnline = computed(() => !isOffline.value);
23 |
24 | const updateNetwork = (
25 | result: UniApp.GetNetworkTypeSuccess | UniApp.OnNetworkStatusChangeSuccess,
26 | ) => {
27 | type.value = (result?.networkType ?? 'unknown') as NetworkType;
28 | };
29 |
30 | uni.getNetworkType({
31 | success: result => updateNetwork(result),
32 | });
33 |
34 | const callback = (result: UniApp.OnNetworkStatusChangeSuccess) => updateNetwork(result);
35 | uni.onNetworkStatusChange(callback);
36 | const stop = () => uni.offNetworkStatusChange(callback);
37 | tryOnScopeDispose(stop);
38 |
39 | return {
40 | type,
41 | isWifi,
42 | is2g,
43 | is3g,
44 | is4g,
45 | is5g,
46 | isEthernet,
47 | isUnknown,
48 | isOnline,
49 | isOffline,
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/src/useOnline/index.md:
--------------------------------------------------------------------------------
1 | # useOnline
2 |
3 | 获取是否在线。基于 `useNetwork`。
4 |
5 | ```typescript
6 | import { useOnline } from '@uni-helper/uni-use';
7 |
8 | const isOnline = useOnline();
9 | ```
10 |
--------------------------------------------------------------------------------
/src/useOnline/index.ts:
--------------------------------------------------------------------------------
1 | import { useNetwork } from '../useNetwork';
2 |
3 | /**
4 | * 获取是否在线
5 | *
6 | * https://uniapp.dcloud.net.cn/api/system/network.html
7 | */
8 | export function useOnline() {
9 | const { isOnline } = useNetwork();
10 | return isOnline;
11 | }
12 |
--------------------------------------------------------------------------------
/src/usePage/index.md:
--------------------------------------------------------------------------------
1 | # usePage
2 |
3 | 获取当前页信息。
4 |
5 | ```typescript
6 | import { usePage } from '@uni-helper/uni-use';
7 |
8 | const page = usePage();
9 | ```
10 |
--------------------------------------------------------------------------------
/src/usePage/index.ts:
--------------------------------------------------------------------------------
1 | import { useRouter } from '../useRouter';
2 |
3 | /** 获取当前页信息 */
4 | export function usePage() {
5 | const { page } = useRouter();
6 | return page;
7 | }
8 |
--------------------------------------------------------------------------------
/src/usePageScroll/index.md:
--------------------------------------------------------------------------------
1 | # usePageScroll
2 |
3 | 控制与监听页面滚动。
4 |
5 | ```typescript
6 | import { usePageScroll } from '@uni-helper/uni-use';
7 |
8 | const { scrollTop } = usePageScroll({
9 | onPageScroll: true,
10 | });
11 | ```
12 |
--------------------------------------------------------------------------------
/src/usePageScroll/index.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeRef } from '../types';
2 | import { onPageScroll } from '@dcloudio/uni-app';
3 | import { watchWithFilter } from '@vueuse/core';
4 | import { computed, isRef, ref } from 'vue';
5 |
6 | export interface UsePageScrollOptions {
7 | /**
8 | * 此选项不可缺!
9 | * uniapp 必须在页面内检测到 onPageScroll 关键词才会注册事件。
10 | * @see https://github.com/dcloudio/uni-app/issues/3099 让页面被正则捕获从而开启监听
11 | */
12 | onPageScroll: any;
13 | /**
14 | * 滚动到指定选择器
15 | *
16 | * @see https://uniapp.dcloud.net.cn/api/ui/scroll?id=pagescrollto
17 | */
18 | scrollToSelector?: MaybeRef;
19 | /**
20 | * 滚动动画时长
21 | *
22 | * @default 300
23 | * @see https://uniapp.dcloud.net.cn/api/ui/scroll?id=pagescrollto
24 | */
25 | duration?: number;
26 | }
27 |
28 | /**
29 | * 页面滚动
30 | *
31 | * @param options 配置项
32 | * @see https://uniapp.dcloud.net.cn/tutorial/page.html#onpagescroll
33 | */
34 | export function usePageScroll(options: UsePageScrollOptions) {
35 | const { duration = 300 } = options;
36 |
37 | const _scrollTop = ref(0);
38 | const scrollTop = computed({
39 | get() {
40 | return _scrollTop.value;
41 | },
42 | set(val) {
43 | uni.pageScrollTo({
44 | scrollTop: val,
45 | duration,
46 | });
47 | },
48 | });
49 |
50 | onPageScroll((e) => {
51 | _scrollTop.value = e.scrollTop;
52 | });
53 |
54 | const scrollToSelector = isRef(options?.scrollToSelector)
55 | ? options.scrollToSelector
56 | : ref(options?.scrollToSelector || '');
57 |
58 | watchWithFilter(
59 | () => scrollToSelector.value,
60 | (newValue) => {
61 | uni.pageScrollTo({
62 | selector: newValue,
63 | duration,
64 | });
65 | },
66 | {
67 | eventFilter: e => e !== undefined,
68 | },
69 | );
70 |
71 | return {
72 | scrollTop,
73 | scrollToSelector,
74 | };
75 | }
76 |
--------------------------------------------------------------------------------
/src/usePages/index.md:
--------------------------------------------------------------------------------
1 | # usePages
2 |
3 | 获取当前页面栈信息。
4 |
5 | ```typescript
6 | import { usePages } from '@uni-helper/uni-use';
7 |
8 | const pages = usePages();
9 | ```
10 |
--------------------------------------------------------------------------------
/src/usePages/index.ts:
--------------------------------------------------------------------------------
1 | import { useRouter } from '../useRouter';
2 |
3 | /** 获取当前页面栈信息 */
4 | export function usePages() {
5 | const { pages } = useRouter();
6 | return pages;
7 | }
8 |
--------------------------------------------------------------------------------
/src/usePreferredDark/index.md:
--------------------------------------------------------------------------------
1 | # usePreferredDark
2 |
3 | 响应式的暗黑主题偏好。
4 |
5 | ```typescript
6 | import { usePreferredDark } from '@uni-helper/uni-use';
7 |
8 | const prefersDark = usePreferredDark();
9 | ```
10 |
--------------------------------------------------------------------------------
/src/usePreferredDark/index.ts:
--------------------------------------------------------------------------------
1 | import { tryOnScopeDispose } from '@vueuse/core';
2 | import { readonly, ref } from 'vue';
3 |
4 | /**
5 | * 响应式的暗黑主题偏好
6 | *
7 | * https://zh.uniapp.dcloud.io/api/system/theme.html
8 | *
9 | * https://uniapp.dcloud.net.cn/tutorial/darkmode.html
10 | */
11 | export function usePreferredDark() {
12 | const prefersDark = ref(uni.getSystemInfoSync().osTheme === 'dark');
13 |
14 | const callback = ({ theme }: UniApp.OnThemeChangeCallbackResult) => {
15 | prefersDark.value = theme === 'dark';
16 | };
17 | uni.onThemeChange(callback);
18 | const stop = () => uni.offThemeChange(callback);
19 | tryOnScopeDispose(stop);
20 |
21 | return readonly(prefersDark);
22 | }
23 |
--------------------------------------------------------------------------------
/src/usePreferredLanguage/index.md:
--------------------------------------------------------------------------------
1 | # usePreferredLanguage
2 |
3 | 响应式的语言偏好。
4 |
5 | ```typescript
6 | import { usePreferredLanguage } from '@uni-helper/uni-use';
7 |
8 | const language = usePreferredLanguage();
9 | ```
10 |
--------------------------------------------------------------------------------
/src/usePreferredLanguage/index.ts:
--------------------------------------------------------------------------------
1 | import { tryOnScopeDispose } from '@vueuse/core';
2 | import { readonly, ref } from 'vue';
3 |
4 | /**
5 | * 响应式的语言偏好
6 | *
7 | * https://uniapp.dcloud.net.cn/api/ui/locale.html
8 | *
9 | * https://uniapp.dcloud.net.cn/tutorial/i18n
10 | */
11 | export function usePreferredLanguage() {
12 | const locale = ref(uni.getLocale());
13 |
14 | const callback = (result: UniApp.OnLocaleChangeCallbackResult) => {
15 | locale.value = result.locale ?? locale.value;
16 | };
17 | uni.onLocaleChange(callback);
18 | const stop = () => {
19 | // @ts-expect-error no types
20 | if (uni.offLocaleChange) {
21 | // @ts-expect-error no types
22 | uni.offLocaleChange(callback);
23 | }
24 | };
25 | tryOnScopeDispose(stop);
26 |
27 | return readonly(locale);
28 | }
29 |
--------------------------------------------------------------------------------
/src/usePrevPage/index.md:
--------------------------------------------------------------------------------
1 | # usePrevPage
2 |
3 | 获取前一页信息。
4 |
5 | ```typescript
6 | import { usePrevPage } from '@uni-helper/uni-use';
7 |
8 | const prevPage = usePrevPage();
9 | ```
10 |
--------------------------------------------------------------------------------
/src/usePrevPage/index.ts:
--------------------------------------------------------------------------------
1 | import { useRouter } from '../useRouter';
2 |
3 | /** 获取前一页信息 */
4 | export function usePrevPage() {
5 | const { prevPage } = useRouter();
6 | return prevPage;
7 | }
8 |
--------------------------------------------------------------------------------
/src/usePrevRoute/index.md:
--------------------------------------------------------------------------------
1 | # usePrevRoute
2 |
3 | 获取前一页面路由信息。
4 |
5 | ```typescript
6 | import { usePrevRoute } from '@uni-helper/uni-use';
7 |
8 | const prevRoute = usePrevRoute();
9 | ```
10 |
--------------------------------------------------------------------------------
/src/usePrevRoute/index.ts:
--------------------------------------------------------------------------------
1 | import { useRouter } from '../useRouter';
2 |
3 | /** 获取前一页路由信息 */
4 | export function usePrevRoute() {
5 | const { prevRoute } = useRouter();
6 | return prevRoute;
7 | }
8 |
--------------------------------------------------------------------------------
/src/useProvider/index.md:
--------------------------------------------------------------------------------
1 | # useProvider
2 |
3 | 设置服务供应商参数,调用返回方法获取服务供应商。
4 |
5 | ```typescript
6 | import { useProvider } from '@uni-helper/uni-use';
7 |
8 | const getProvider = useProvider({
9 | /* 传入配置 */
10 | });
11 | getProvider(); // 获取服务供应商
12 | ```
13 |
14 | 可以传入一个对象来更新已有配置,这样会使用 [扩展运算符](https://es6.ruanyifeng.com/#docs/object#%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%89%A9%E5%B1%95%E8%BF%90%E7%AE%97%E7%AC%A6) 来确认最终配置。
15 |
16 | ```typescript
17 | getProvider({
18 | /* 新传入配置 */
19 | });
20 | ```
21 |
--------------------------------------------------------------------------------
/src/useProvider/index.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeComputedRef } from '../types';
2 | import { resolveUnref } from '@vueuse/core';
3 | import { reactive } from 'vue';
4 |
5 | export interface UniGetProviderOptions extends UniApp.GetProviderOptions {}
6 | export type GetProviderOptions = MaybeComputedRef;
7 | export type UseProviderOptions = GetProviderOptions;
8 |
9 | /**
10 | * 返回一个方法,调用后获取服务供应商
11 | *
12 | * https://uniapp.dcloud.net.cn/api/plugins/provider?id=getprovider
13 | */
14 | export function useProvider(options?: UseProviderOptions) {
15 | /**
16 | * 获取服务供应商
17 | *
18 | * https://uniapp.dcloud.net.cn/api/plugins/provider?id=getprovider
19 | */
20 | return function getProvider(newOptions?: GetProviderOptions) {
21 | return uni.getProvider(
22 | reactive({
23 | service: 'oauth',
24 | ...resolveUnref(options),
25 | ...resolveUnref(newOptions),
26 | }),
27 | );
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/src/useRequest/index.md:
--------------------------------------------------------------------------------
1 | # useRequest
2 |
3 | `uni.request` 的封装,对标 `@vueuse/core` v10.7.1。使用方法参见 。
4 |
5 | **返回值中含有 task,可自行操作。**
6 |
--------------------------------------------------------------------------------
/src/useRequest/index.ts:
--------------------------------------------------------------------------------
1 | import { until } from '@vueuse/core';
2 | import { type Ref, ref, type ShallowRef, shallowRef } from 'vue';
3 | import { isString, noop } from '../utils';
4 |
5 | /** 对标 @vueuse/core v10.7.1 useAxios */
6 |
7 | export interface UseRequestReturn {
8 | task: ShallowRef;
9 |
10 | /** uni.request 响应 */
11 | response: ShallowRef;
12 |
13 | /** uni.request 响应内的数据 */
14 | data: Ref;
15 |
16 | /** 请求是否完成 */
17 | isFinished: Ref;
18 |
19 | /** 请求是否进行中 */
20 | isLoading: Ref;
21 |
22 | /** 请求是否中止 */
23 | isAborted: Ref;
24 |
25 | /** 请求间发生的错误 */
26 | error: ShallowRef;
27 |
28 | /** 中止当前请求 */
29 | abort: (message?: string | undefined) => void;
30 |
31 | /** abort 别名 */
32 | cancel: (message?: string | undefined) => void;
33 |
34 | /** isAborted 别名 */
35 | isCanceled: Ref;
36 | }
37 | export interface StrictUseRequestReturn extends UseRequestReturn {
38 | /** 手动开始请求 */
39 | execute: (
40 | url?: string | UniApp.RequestOptions,
41 | config?: UniApp.RequestOptions,
42 | ) => PromiseLike>;
43 | }
44 | export interface EasyUseRequestReturn extends UseRequestReturn {
45 | /** 手动开始下载 */
46 | execute: (url: string, config?: UniApp.RequestOptions) => PromiseLike>;
47 | }
48 | export type OverallUseRequestReturn = StrictUseRequestReturn | EasyUseRequestReturn;
49 |
50 | export interface UseRequestOptions {
51 | /** 是否自动开始请求 */
52 | immediate?: boolean;
53 |
54 | /**
55 | * 是否使用 shallowRef
56 | *
57 | * @default true
58 | */
59 | shallow?: boolean;
60 |
61 | /** 请求错误时的回调 */
62 | onError?: (e: UniApp.GeneralCallbackResult) => void;
63 |
64 | /** 请求成功时的回调 */
65 | onSuccess?: (data: T) => void;
66 |
67 | /** 要使用的初始化数据 */
68 | initialData?: T;
69 |
70 | /** 是否在执行承诺之前将状态设置为初始状态 */
71 | resetOnExecute?: boolean;
72 |
73 | /** 请求结束时的回调 */
74 | onFinish?: (result?: UniApp.GeneralCallbackResult) => void;
75 | }
76 |
77 | export function useRequest(
78 | url: string,
79 | config?: UniApp.RequestOptions,
80 | options?: UseRequestOptions,
81 | ): StrictUseRequestReturn & PromiseLike>;
82 | export function useRequest(
83 | config?: UniApp.RequestOptions,
84 | ): EasyUseRequestReturn & PromiseLike>;
85 |
86 | /** uni.request 的封装 */
87 | export function useRequest(
88 | ...args: any[]
89 | ): OverallUseRequestReturn & PromiseLike> {
90 | const url: string | undefined = typeof args[0] === 'string' ? args[0] : undefined;
91 | const argsPlaceholder = isString(url) ? 1 : 0;
92 | const defaultOptions: UseRequestOptions = {
93 | immediate: !!argsPlaceholder,
94 | shallow: true,
95 | };
96 | let defaultConfig: Partial = {};
97 | let options: UseRequestOptions = defaultOptions;
98 |
99 | if (args.length > 0 + argsPlaceholder) {
100 | defaultConfig = args[0 + argsPlaceholder];
101 | }
102 |
103 | if (args.length === 3) {
104 | options = args[0 + argsPlaceholder];
105 | }
106 |
107 | const {
108 | initialData,
109 | shallow,
110 | onSuccess = noop,
111 | onError = noop,
112 | onFinish = noop,
113 | immediate,
114 | resetOnExecute = false,
115 | } = options;
116 |
117 | const task = shallowRef();
118 | const response = shallowRef();
119 | const data = shallow ? shallowRef() : ref();
120 | const isFinished = ref(false);
121 | const isLoading = ref(false);
122 | const isAborted = ref(false);
123 | const error = shallowRef();
124 |
125 | const abort = (message?: string) => {
126 | if (isFinished.value || !isLoading.value) {
127 | return;
128 | }
129 | // @ts-expect-error no types
130 | task.value?.abort(message);
131 | isAborted.value = true;
132 | isLoading.value = false;
133 | isFinished.value = false;
134 | };
135 |
136 | const loading = (loading: boolean) => {
137 | isLoading.value = loading;
138 | isFinished.value = !loading;
139 | };
140 |
141 | const resetData = () => {
142 | if (resetOnExecute) {
143 | data.value = initialData;
144 | }
145 | };
146 |
147 | const promise = {
148 | then: (...args) => waitUntilFinished().then(...args),
149 | catch: (...args) => waitUntilFinished().catch(...args),
150 | } as Promise>;
151 |
152 | let executeCounter = 0;
153 | const execute: OverallUseRequestReturn['execute'] = (
154 | executeUrl: string | UniApp.RequestOptions | undefined = url,
155 | config: Partial = {},
156 | ) => {
157 | error.value = undefined;
158 | const _url = typeof executeUrl === 'string' ? executeUrl : url ?? config.url;
159 |
160 | if (_url === undefined) {
161 | error.value = {
162 | errMsg: 'Invalid URL provided for uni.request.',
163 | };
164 | isFinished.value = true;
165 | return promise;
166 | }
167 | resetData();
168 | abort();
169 | loading(true);
170 |
171 | executeCounter += 1;
172 | const currentExecuteCounter = executeCounter;
173 | isAborted.value = false;
174 |
175 | const _config = {
176 | ...defaultConfig,
177 | ...(typeof executeUrl === 'object' ? executeUrl : config),
178 | url: _url,
179 | };
180 | task.value = uni.request({
181 | ..._config,
182 | success: (r) => {
183 | if (isAborted.value) {
184 | return;
185 | }
186 | _config.success?.(r);
187 | response.value = r;
188 | const result = r.data as unknown as T;
189 | data.value = result;
190 | onSuccess(result);
191 | },
192 | fail: (e) => {
193 | _config.fail?.(e);
194 | error.value = e;
195 | onError(e);
196 | },
197 | complete: (r) => {
198 | _config.complete?.(r);
199 | onFinish(r);
200 | if (currentExecuteCounter === executeCounter) {
201 | loading(false);
202 | }
203 | },
204 | });
205 | return promise;
206 | };
207 | if (immediate && url) {
208 | (execute as StrictUseRequestReturn['execute'])();
209 | }
210 |
211 | const result = {
212 | task,
213 | response,
214 | data,
215 | error,
216 | isFinished,
217 | isLoading,
218 | cancel: abort,
219 | isAborted,
220 | isCanceled: isAborted,
221 | abort,
222 | execute,
223 | } as OverallUseRequestReturn;
224 |
225 | function waitUntilFinished() {
226 | return new Promise>((resolve, reject) => {
227 | until(isFinished)
228 | .toBe(true)
229 | .then(() => (error.value ? reject(error.value) : resolve(result)));
230 | });
231 | }
232 |
233 | return {
234 | ...result,
235 | ...promise,
236 | };
237 | }
238 |
--------------------------------------------------------------------------------
/src/useRoute/index.md:
--------------------------------------------------------------------------------
1 | # useRoute
2 |
3 | 获取当前页路由信息。
4 |
5 | ```typescript
6 | import { useRoute } from '@uni-helper/uni-use';
7 |
8 | const route = useRoute();
9 | ```
10 |
--------------------------------------------------------------------------------
/src/useRoute/index.ts:
--------------------------------------------------------------------------------
1 | import { useRouter } from '../useRouter';
2 |
3 | /**
4 | * 获取当前页路由信息
5 | *
6 | * @deprecated use `useRouter().currentUrl` instead
7 | */
8 | export function useRoute() {
9 | const { route } = useRouter();
10 | return route;
11 | }
12 |
--------------------------------------------------------------------------------
/src/useRouter/index.md:
--------------------------------------------------------------------------------
1 | # useRouter
2 |
3 | 路由相关的操作和变量
4 |
5 | ## 使用方式
6 |
7 | ```ts
8 | import { tabBar } from '@/pages.json';
9 | import { useRouter } from '@uni-helper/uni-use';
10 |
11 | const router = useRouter({
12 | /**
13 | * 是否尝试跳转 tabBar
14 | * 开启后,使用 navigate / redirect 将会先尝试 tabBar
15 | * @default true
16 | */
17 | tryTabBar: true,
18 | /**
19 | * pages.json 里的 tabBar list 配置
20 | * tryTabBar 开启时,会判断跳转页面
21 | * 全局配置,仅需要配置一次
22 | */
23 | tabBarList: tabBar.list,
24 | });
25 |
26 | // 如果上面的 tryTabBar 设定为 false,或非常确定是 tabbar 页面,可以直接使用 switchTab
27 | router.switchTab({ url: '/pages/tabbar/tabbar1' });
28 |
29 | // 路由跳转,参数和 uniapp 的一致
30 | // 当 tryTabBar = true 时,会自动判断 tabBar 页面进行跳转
31 | router.navigate({ url: '/pages/topics/index' });
32 |
33 | // 路由重定向,参数和 uniapp 的一致
34 | // 当 tryTabBar = true 时,会自动判断 tabBar 页面进行重定向
35 | router.redirect({ url: '/pages/auth/login' });
36 |
37 | // 路由重定向,并清空当前页面栈
38 | router.reLaunch({ url: '/pages/auth/login' });
39 |
40 | // 后退
41 | router.back();
42 | ```
43 |
--------------------------------------------------------------------------------
/src/useRouter/index.ts:
--------------------------------------------------------------------------------
1 | import type { AppJson } from '@dcloudio/uni-cli-shared';
2 | import type { RequiredOnly } from '../types';
3 | import { computed, ref } from 'vue';
4 | import { tryOnBackPress } from '../tryOnBackPress';
5 | import { pathResolve } from '../utils';
6 |
7 | /** 获取当前页面栈信息 */
8 | const pages = ref([]);
9 | const pageLength = computed(() => pages.value.length); // 使用 computed 可触发依赖项更新
10 |
11 | /** 获取当前页信息 */
12 | // at is not supported
13 | const current = computed(() => pages.value?.[pageLength.value - 1]);
14 | /** 获取前一页信息 */
15 | const prev = computed(() =>
16 | pageLength.value > 1 ? pages.value[pageLength.value - 2] : pages.value?.[pageLength.value - 1],
17 | );
18 |
19 | /** 获取当前页路由信息 */
20 | const currentUrl = computed(() => current.value?.route || '/');
21 | /** 获取前一页路由信息 */
22 | const prevUrl = computed(() => prev.value?.route);
23 |
24 | let tabBarUrls: string[] = [];
25 |
26 | let isAddInterceptors = false;
27 | let isBindBackPress = false;
28 |
29 | function initIfNotInited() {
30 | // 默认路由的拦截
31 | if (!isAddInterceptors) {
32 | isAddInterceptors = true;
33 |
34 | uni.addInterceptor('navigateTo', { complete: refreshCurrentPages });
35 | uni.addInterceptor('redirectTo', { complete: refreshCurrentPages });
36 | uni.addInterceptor('reLaunch', { complete: refreshCurrentPages });
37 | uni.addInterceptor('switchTab', { complete: refreshCurrentPages });
38 | uni.addInterceptor('navigateBack', { complete: refreshCurrentPages });
39 | }
40 |
41 | // 对实体按键 / 顶部导航栏返回按钮进行监听
42 | if (!isBindBackPress) {
43 | isBindBackPress = true;
44 |
45 | tryOnBackPress((e) => {
46 | if (e.from === 'navigateBack') {
47 | return;
48 | }
49 | refreshCurrentPages();
50 | }).catch(() => {
51 | isBindBackPress = false;
52 | });
53 | }
54 |
55 | // 每次 init 都更新一次
56 | refreshCurrentPages();
57 | }
58 |
59 | function refreshCurrentPages() {
60 | pages.value = getCurrentPages();
61 | }
62 |
63 | function warpPromiseOptions(
64 | opts: T,
65 | resolve: (res: any) => any,
66 | reject: (err: any) => any,
67 | ) {
68 | let { fail, success, complete } = opts as any;
69 |
70 | fail = fail || ((err: any) => err);
71 | success = success || ((res: any) => res);
72 | complete = complete || (() => {});
73 |
74 | return {
75 | ...opts,
76 | success: (res: any) => resolve(success(res)),
77 | fail: (err: any) => reject(fail(err)),
78 | complete,
79 | };
80 | }
81 |
82 | /** 切换 tabbar 页面 */
83 | function switchTab(options: UniNamespace.SwitchTabOptions): Promise {
84 | return new Promise((resolve, reject) => {
85 | uni.switchTab(warpPromiseOptions(options, resolve, reject));
86 | });
87 | }
88 |
89 | function navigateTo(options: UniNamespace.NavigateToOptions) {
90 | return new Promise((resolve, reject) => {
91 | uni.navigateTo(warpPromiseOptions(options, resolve, reject));
92 | });
93 | }
94 |
95 | function redirectTo(options: UniNamespace.RedirectToOptions) {
96 | return new Promise((resolve, reject) => {
97 | uni.redirectTo(warpPromiseOptions(options, resolve, reject));
98 | });
99 | }
100 |
101 | /** 重定向,并清空当前页面栈 */
102 | function reLaunch(options: UniNamespace.ReLaunchOptions): Promise {
103 | return new Promise((resolve, reject) => {
104 | uni.reLaunch(warpPromiseOptions(options, resolve, reject));
105 | });
106 | }
107 |
108 | /** 后退 */
109 | function back(options?: UniNamespace.NavigateBackOptions): Promise {
110 | return new Promise((resolve, reject) => {
111 | uni.navigateBack(warpPromiseOptions(options || {}, resolve, reject));
112 | });
113 | }
114 |
115 | /** 路由跳转 `tryTabBar = true` 时,自动判断是否 tabbar 页面 */
116 | function trySwitchTab(
117 | tryTabBar: boolean,
118 | forward: FN,
119 | options: Parameters[0],
120 | ): Promise {
121 | // 不尝试 tabbar 页面,直接跳转
122 | if (!tryTabBar) {
123 | return forward(options);
124 | }
125 |
126 | // 未设置 tabBarList,先尝试 switchTab,报错再尝试跳转
127 | if (tabBarUrls.length === 0) {
128 | return switchTab(options).catch(() => forward(options));
129 | }
130 |
131 | const url = typeof options.url === 'string' ? options.url : options.url.toString();
132 |
133 | // 如果是 tabBar 页面,则直接 switchTab
134 | if (isTabBarPath(url)) {
135 | return switchTab(options);
136 | }
137 |
138 | // 不是 tabBar,直接跳转
139 | return forward(options);
140 | }
141 |
142 | function isTabBarPath(path: string) {
143 | const target = pathResolve(path);
144 | const tabbar = tabBarUrls.find(url => url === target || `${url}/` === target);
145 | return tabbar !== undefined;
146 | }
147 |
148 | type UniTabBarItem = Exclude['list'][number];
149 |
150 | type TabBarItem = RequiredOnly;
151 |
152 | export interface UseRouterOptions {
153 | /**
154 | * 是否尝试跳转 tabBar 开启后,使用 navigate / redirect 将会先尝试 tabBar
155 | *
156 | * @default true
157 | */
158 | tryTabBar?: boolean;
159 | /**
160 | * 全局配置,仅需要配置一次
161 | * 配置 tryTabBar 开启时,会判断跳转页面
162 | *
163 | * 可填入 pages.json 里的 tabBar list 或仅 tabbar 的 url 数组
164 | */
165 | tabBarList?: Array;
166 | }
167 |
168 | /**
169 | * 路由操作的封装
170 | *
171 | * UNIAPP 官方文档 @see https://uniapp.dcloud.net.cn/api/router.html
172 | */
173 | export function useRouter(options: UseRouterOptions = {}) {
174 | initIfNotInited();
175 |
176 | const { tryTabBar = true } = options;
177 |
178 | if (options.tabBarList) {
179 | const urls: string[] = [];
180 | for (const item of options.tabBarList) {
181 | if (typeof item === 'string') {
182 | urls.push(item);
183 | }
184 | else {
185 | urls.push(item.pagePath);
186 | }
187 | }
188 | tabBarUrls = urls.filter(url => !!url);
189 | }
190 |
191 | /** 路由跳转 */
192 | function navigate(options: UniNamespace.NavigateToOptions): Promise {
193 | return trySwitchTab(tryTabBar, navigateTo, options);
194 | }
195 |
196 | /** 路由重定向 */
197 | function redirect(options: UniNamespace.RedirectToOptions): Promise {
198 | return trySwitchTab(tryTabBar, redirectTo, options);
199 | }
200 |
201 | return {
202 | /** 获取当前页面栈信息 */
203 | pages,
204 | /** 获取当前页信息 */
205 | current,
206 | /** @deprecated 弃用,请使用 current */
207 | page: current,
208 | /** 获取当前页路由信息 */
209 | currentUrl,
210 | /** @deprecated 弃用,请使用 currentUrl */
211 | route: currentUrl,
212 | /** 获取前一页信息 */
213 | prev,
214 | /** @deprecated 弃用,请使用 prev */
215 | prevPage: prev,
216 | /** 获取前一页路由信息 */
217 | prevUrl,
218 | /** @deprecated 弃用,请使用 prevUrl */
219 | prevRoute: prevUrl,
220 | /** 切换 tabbar 页面。 */
221 | switchTab,
222 | /** 路由跳转 */
223 | navigate,
224 | /** 路由重定向 */
225 | redirect,
226 | /** 重定向,并清空当前页面栈 */
227 | reLaunch,
228 | /** 后退 */
229 | back,
230 | };
231 | }
232 |
--------------------------------------------------------------------------------
/src/useScanCode/index.md:
--------------------------------------------------------------------------------
1 | # useScanCode
2 |
3 | 返回一个方法,调用后调起客户端扫码界面。
4 |
5 | ```typescript
6 | import { useScanCode } from '@uni-helper/uni-use';
7 |
8 | const scan = useScanCode({
9 | /* 传入配置 */
10 | });
11 | scan(); // 调起扫码
12 | ```
13 |
14 | 可以传入一个对象来更新已有配置,这样会使用 [扩展运算符](https://es6.ruanyifeng.com/#docs/object#%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%89%A9%E5%B1%95%E8%BF%90%E7%AE%97%E7%AC%A6) 来确认最终配置。
15 |
16 | ```typescript
17 | scan({
18 | /* 新传入配置 */
19 | });
20 | ```
21 |
--------------------------------------------------------------------------------
/src/useScanCode/index.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeComputedRef } from '../types';
2 | import { resolveUnref } from '@vueuse/core';
3 | import { reactive } from 'vue';
4 |
5 | export interface UniScanCodeOptions extends UniApp.ScanCodeOptions {}
6 | export type ScanCodeOptions = MaybeComputedRef;
7 | export type UseScanCodeOptions = ScanCodeOptions;
8 |
9 | /**
10 | * 返回一个方法,调用后调起客户端扫码界面
11 | *
12 | * https://uniapp.dcloud.net.cn/api/system/barcode?id=scancode
13 | */
14 | export function useScanCode(options?: UseScanCodeOptions) {
15 | /**
16 | * 调起客户端扫码界面
17 | *
18 | * https://uniapp.dcloud.net.cn/api/system/barcode?id=scancode
19 | */
20 | return function scanCode(newOptions?: ScanCodeOptions) {
21 | return uni.scanCode(
22 | reactive({
23 | ...resolveUnref(options),
24 | ...resolveUnref(newOptions),
25 | }),
26 | );
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/src/useScreenBrightness/index.md:
--------------------------------------------------------------------------------
1 | # useScreenBrightness
2 |
3 | 获取和设置屏幕亮度。你需要将默认值作为第一个参数传入。
4 |
5 | ```typescript
6 | import { useScreenBrightness } from '@uni-helper/uni-use';
7 |
8 | const screenBrightness = useScreenBrightness(1);
9 |
10 | // 查看屏幕亮度
11 | console.log('screenBrightness', screenBrightness.value);
12 | // 设置屏幕亮度
13 | screenBrightness.value = 0;
14 | ```
15 |
16 | 默认使用 `console.error` 输出错误信息,你也可以自定义错误处理。
17 |
18 | ```typescript
19 | import { useScreenBrightness } from '@uni-helper/uni-use';
20 |
21 | const screenBrightness = useScreenBrightness('', {
22 | onError: (error) => {
23 | console.log(error);
24 | }
25 | });
26 | ```
27 |
--------------------------------------------------------------------------------
/src/useScreenBrightness/index.ts:
--------------------------------------------------------------------------------
1 | import type { ConfigurableEventFilter, ConfigurableFlush } from '@vueuse/core';
2 | import type { Ref } from 'vue';
3 | import type { MaybeComputedRef } from '../types';
4 | import { watchWithFilter } from '@vueuse/core';
5 | import { ref } from 'vue';
6 | import { useInterceptor } from '../useInterceptor';
7 |
8 | function getScreenBrightness() {
9 | return new Promise((resolve, reject) => {
10 | uni.getScreenBrightness({
11 | success: ({ value }) => resolve(value),
12 | fail: error => reject(error),
13 | });
14 | });
15 | }
16 |
17 | function setScreenBrightness(value: number) {
18 | return new Promise((resolve, reject) => {
19 | uni.setScreenBrightness({
20 | value,
21 | success: () => resolve(),
22 | fail: error => reject(error),
23 | });
24 | });
25 | }
26 |
27 | export interface UseScreenBrightnessOptions extends ConfigurableEventFilter, ConfigurableFlush {
28 | /**
29 | * 是否监听 setScreenBrightness 引起的屏幕亮度变化
30 | *
31 | * @default true
32 | */
33 | listenToScreenBrightnessChanges?: boolean;
34 | /**
35 | * 错误回调
36 | *
37 | * 默认用 `console.error` 打印错误
38 | */
39 | onError?: (error: unknown) => void;
40 | }
41 |
42 | /**
43 | * 屏幕亮度
44 | *
45 | * https://uniapp.dcloud.net.cn/api/system/brightness.html
46 | */
47 | export function useScreenBrightness(
48 | initialValue: MaybeComputedRef,
49 | options: UseScreenBrightnessOptions = {},
50 | ) {
51 | const {
52 | listenToScreenBrightnessChanges = true,
53 | onError = error => console.error(error),
54 | flush = 'pre',
55 | eventFilter,
56 | } = options;
57 |
58 | const data = ref(initialValue) as Ref;
59 |
60 | async function read() {
61 | try {
62 | data.value = await getScreenBrightness();
63 | }
64 | catch (error) {
65 | onError(error);
66 | }
67 | }
68 |
69 | read();
70 |
71 | if (listenToScreenBrightnessChanges) {
72 | useInterceptor('setScreenBrightness', { complete: () => setTimeout(() => read(), 0) });
73 | }
74 |
75 | watchWithFilter(
76 | data,
77 | async () => {
78 | try {
79 | await setScreenBrightness(data.value);
80 | }
81 | catch (error) {
82 | onError(error);
83 | }
84 | },
85 | { flush, eventFilter },
86 | );
87 |
88 | return data;
89 | }
90 |
--------------------------------------------------------------------------------
/src/useSelectorQuery/index.md:
--------------------------------------------------------------------------------
1 | # useSelectorQuery
2 |
3 | `uni.createSelectorQuery` 的封装。
4 |
5 | ```typescript
6 | import { useSelectorQuery } from '@uni-helper/uni-use';
7 |
8 | const { select, getBoundingClientRect, getFields, getScrollOffset, getContext }
9 | = useSelectorQuery();
10 |
11 | // 获取 NodeRef
12 | const node = select('#id');
13 |
14 | // 获取单个 rect
15 | const rect = await getBoundingClientRect('#id');
16 |
17 | // 获取所有 .selector 的 rect,返回值为 UniApp.NodeInfo[]
18 | const rects = await getBoundingClientRect('.selector', true);
19 |
20 | // getFields,getScrollOffset,getContext 使用方式和 getBoundingClientRect 一致
21 | ```
22 |
--------------------------------------------------------------------------------
/src/useSelectorQuery/index.ts:
--------------------------------------------------------------------------------
1 | import { getCurrentInstance, onMounted, ref } from 'vue';
2 |
3 | export type SelectAll = boolean;
4 | export type QueryResult = M extends true ? UniApp.NodeInfo[] : UniApp.NodeInfo;
5 |
6 | export function useSelectorQuery() {
7 | const query = ref();
8 |
9 | // 尝试初始化一次,覆盖该方法在 onMounted 之后调用的情况
10 | initQuery();
11 | onMounted(initQuery);
12 |
13 | function initQuery() {
14 | if (query.value) {
15 | return;
16 | }
17 | const instance = getCurrentInstance();
18 | if (instance == null) {
19 | return;
20 | }
21 | query.value = uni.createSelectorQuery().in(instance);
22 | }
23 |
24 | function getQuery(): UniApp.SelectorQuery {
25 | initQuery();
26 | if (query.value == null) {
27 | throw new Error('SelectorQuery initialization failed');
28 | }
29 | return query.value;
30 | }
31 |
32 | function select(selector: string | UniApp.NodesRef, all?: SelectAll) {
33 | return typeof selector === 'string'
34 | ? all
35 | ? getQuery().selectAll(selector)
36 | : getQuery().select(selector)
37 | : selector;
38 | }
39 |
40 | function getBoundingClientRect>(
41 | selector: string | UniApp.NodesRef,
42 | all?: T,
43 | ) {
44 | return new Promise((resolve) => {
45 | select(selector, all)
46 | .boundingClientRect(res => resolve(res as R))
47 | .exec();
48 | });
49 | }
50 |
51 | function getFields>(
52 | selector: string | UniApp.NodesRef,
53 | fields: UniApp.NodeField,
54 | all?: T,
55 | ) {
56 | return new Promise((resolve) => {
57 | select(selector, all)
58 | .fields(fields, res => resolve(res as R))
59 | .exec();
60 | });
61 | }
62 |
63 | function getScrollOffset>(
64 | selector?: string | UniApp.NodesRef,
65 | ) {
66 | return new Promise((resolve) => {
67 | const node = selector === undefined ? getQuery().selectViewport() : select(selector);
68 | node.scrollOffset(res => resolve(res as R)).exec();
69 | });
70 | }
71 |
72 | function getContext>(
73 | selector: string | UniApp.NodesRef,
74 | all?: T,
75 | ) {
76 | return new Promise((resolve) => {
77 | select(selector, all)
78 | .context(res => resolve(res as R))
79 | .exec();
80 | });
81 | }
82 |
83 | return {
84 | query,
85 | getQuery,
86 | select,
87 | getNode: select,
88 | getBoundingClientRect,
89 | getFields,
90 | getScrollOffset,
91 | getContext,
92 | };
93 | }
94 |
--------------------------------------------------------------------------------
/src/useSocket/index.md:
--------------------------------------------------------------------------------
1 | # useSocket
2 |
3 | `uni-app` 关于 `socket` 的封装,对标 `@vueuse/core` v10.7.1。使用方法参见 。
4 |
5 | **返回值中含有 task,可自行操作。**
6 |
--------------------------------------------------------------------------------
/src/useSocket/index.ts:
--------------------------------------------------------------------------------
1 | import type { ComputedRef, Ref } from 'vue';
2 | import type { MaybeComputedRef } from '../types';
3 | import { type Fn, resolveRef, tryOnScopeDispose, useIntervalFn } from '@vueuse/core';
4 | import { computed, ref, watch } from 'vue';
5 | import { tryOnUnload } from '../tryOnUnload';
6 |
7 | /** 对标 @vueuse/core v10.7.1 useWebSocket */
8 |
9 | export type SocketTask = UniApp.SocketTask;
10 |
11 | export type SocketStatus = 'OPEN' | 'CONNECTING' | 'CLOSED';
12 |
13 | const DEFAULT_PING_MESSAGE = 'ping';
14 |
15 | export interface UseSocketOptions {
16 | onConnected?: (task: SocketTask, result: UniApp.OnSocketOpenCallbackResult) => void;
17 | onClosed?: (task: SocketTask, result: UniApp.OnSocketCloseOptions) => void;
18 | onError?: (task: SocketTask, error: UniApp.GeneralCallbackResult) => void;
19 | onMessage?: (task: SocketTask, result: UniApp.OnSocketMessageCallbackResult) => void;
20 |
21 | /**
22 | * 是否每过 x 毫秒发送一次心跳信号
23 | *
24 | * @default false
25 | */
26 | heartbeat?:
27 | | boolean
28 | | {
29 | /**
30 | * 心跳信号信息
31 | *
32 | * @default 'ping'
33 | */
34 | message?: string | ArrayBuffer;
35 |
36 | /**
37 | * 毫秒级时间间隔
38 | *
39 | * @default 1000
40 | */
41 | interval?: number;
42 |
43 | /**
44 | * 毫秒级心跳信号响应超时时间
45 | *
46 | * @default 1000
47 | */
48 | pongTimeout?: number;
49 | };
50 |
51 | /**
52 | * 是否允许自动重连
53 | *
54 | * @default false
55 | */
56 | autoReconnect?:
57 | | boolean
58 | | {
59 | /**
60 | * 最大重连次数
61 | *
62 | * 你也可以传一个方法,返回 true 表示需要重连
63 | *
64 | * @default -1
65 | */
66 | retries?: number | (() => boolean);
67 |
68 | /**
69 | * 毫秒级重连延迟
70 | *
71 | * @default 1000
72 | */
73 | delay?: number;
74 |
75 | /** 到达最大重连次数时触发 */
76 | onFailed?: Fn;
77 | };
78 |
79 | /**
80 | * 是否自动打开连接
81 | *
82 | * @default true
83 | */
84 | immediate?: boolean;
85 |
86 | /**
87 | * 是否自动关闭连接
88 | *
89 | * @default true
90 | */
91 | autoClose?: boolean;
92 |
93 | /**
94 | * 是否多实例
95 | *
96 | * @default false
97 | */
98 | multiple?: boolean;
99 |
100 | /** 头部 */
101 | headers?: Record;
102 |
103 | /**
104 | * 请求方法
105 | *
106 | * @default 'GET'
107 | */
108 | method?: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT';
109 |
110 | /**
111 | * 一个或多个子协议字符串的列表
112 | *
113 | * @default [ ]
114 | */
115 | protocols?: string[];
116 | }
117 |
118 | export interface UseSocketReturn {
119 | /** websocket 收到的最新数据的引用,可以观察到对传入信息的响应 */
120 | data: Ref;
121 |
122 | /** 当前 websocket 状态,只能是 OPEN、CONNECTING 和 CLOSED 之一 */
123 | status: Ref;
124 |
125 | /** 当前 websocket 是否处于 OPEN 状态 */
126 | isOpen: ComputedRef;
127 |
128 | /** 当前 websocket 是否处于 CONNECTING 状态 */
129 | isConnecting: ComputedRef;
130 |
131 | /** 当前 websocket 是否处于 CLOSED 状态 */
132 | isClosed: ComputedRef;
133 |
134 | /** 关闭 websocket 连接 */
135 | close: SocketTask['close'];
136 |
137 | /**
138 | * 打开 websocket 连接
139 | *
140 | * 如果存在已打开的 websocket 连接,会先关闭它再打开一个新的连接
141 | */
142 | open: Fn;
143 |
144 | /**
145 | * 发送数据
146 | *
147 | * @param data
148 | * @param useBuffer 当 websocket 连接尚未打开时,是否把数据存储在 buffer 中并在连接打开时发送它们。默认为 true。
149 | */
150 | send: (data: string | ArrayBuffer, useBuffer?: boolean) => boolean;
151 |
152 | /** SocketTask 实例引用 */
153 | task: Ref;
154 | }
155 |
156 | function resolveNestedOptions(options: T | true): T {
157 | if (options === true) {
158 | return {} as T;
159 | }
160 | return options;
161 | }
162 |
163 | /**
164 | * 响应式的 Socket 客户端
165 | *
166 | * https://uniapp.dcloud.net.cn/api/request/websocket.html
167 | */
168 | export function useSocket(
169 | url: MaybeComputedRef,
170 | options: UseSocketOptions = {},
171 | ): UseSocketReturn {
172 | const {
173 | onConnected,
174 | onClosed,
175 | onError,
176 | onMessage,
177 | heartbeat = false,
178 | autoReconnect = false,
179 | immediate = true,
180 | autoClose = true,
181 | multiple = false,
182 | headers,
183 | method = 'GET',
184 | protocols = [],
185 | } = options;
186 |
187 | const data: Ref = ref(null);
188 | const status = ref('CLOSED');
189 | const isOpen = computed(() => status.value === 'OPEN');
190 | const isConnecting = computed(() => status.value === 'CONNECTING');
191 | const isClosed = computed(() => status.value === 'CLOSED');
192 | const taskRef = ref();
193 | const urlRef = resolveRef(url);
194 |
195 | let heartbeatPause: Fn | undefined;
196 | let heartbeatResume: Fn | undefined;
197 |
198 | let explicitlyClosed = false;
199 | let retried = 0;
200 |
201 | let bufferedData: (string | ArrayBuffer)[] = [];
202 |
203 | let pongTimeoutWait: ReturnType | undefined;
204 |
205 | const _sendBuffer = () => {
206 | if (bufferedData.length > 0 && taskRef.value && status.value === 'OPEN') {
207 | for (const buffer of bufferedData) {
208 | taskRef.value.send({ data: buffer });
209 | }
210 | bufferedData = [];
211 | }
212 | };
213 |
214 | const resetHeartbeat = () => {
215 | clearTimeout(pongTimeoutWait);
216 | pongTimeoutWait = undefined;
217 | };
218 |
219 | // 1000 表示正常关闭
220 | // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code
221 | // https://uniapp.dcloud.net.cn/api/request/socket-task.html#sockettask-close
222 | const close: SocketTask['close'] = ({ code = 1000, reason } = {}) => {
223 | if (!taskRef.value) {
224 | return;
225 | }
226 | explicitlyClosed = true;
227 | heartbeatPause?.();
228 | taskRef.value.close({ code, reason });
229 | };
230 |
231 | const send = (data: string | ArrayBuffer, useBuffer = true) => {
232 | if (!taskRef.value || status.value !== 'OPEN') {
233 | if (useBuffer) {
234 | bufferedData.push(data);
235 | }
236 | return false;
237 | }
238 | _sendBuffer();
239 | taskRef.value.send({ data });
240 | return true;
241 | };
242 |
243 | const _init = () => {
244 | if (explicitlyClosed || urlRef.value === undefined) {
245 | return;
246 | }
247 |
248 | const task = uni.connectSocket({
249 | url: urlRef.value,
250 | multiple,
251 | header: headers,
252 | method,
253 | protocols,
254 | complete: () => {
255 | // just for correct types
256 | },
257 | } as UniApp.ConnectSocketOption) as unknown as UniApp.SocketTask;
258 | taskRef.value = task;
259 | status.value = 'CONNECTING';
260 |
261 | task.onOpen((result) => {
262 | status.value = 'OPEN';
263 | onConnected?.(task, result);
264 | heartbeatResume?.();
265 | _sendBuffer();
266 | });
267 |
268 | task.onClose((result) => {
269 | status.value = 'CLOSED';
270 | taskRef.value = undefined;
271 | onClosed?.(task, result);
272 | if (!explicitlyClosed && autoReconnect) {
273 | const { retries = -1, delay = 1000, onFailed } = resolveNestedOptions(autoReconnect);
274 | retried += 1;
275 | if (typeof retries === 'number' && (retries < 0 || retried < retries)) {
276 | setTimeout(_init, delay);
277 | }
278 | else if (typeof retries === 'function' && retries()) {
279 | setTimeout(_init, delay);
280 | }
281 | else { onFailed?.(); }
282 | }
283 | });
284 |
285 | task.onError((error) => {
286 | onError?.(task, error);
287 | });
288 |
289 | task.onMessage((result) => {
290 | if (heartbeat) {
291 | resetHeartbeat();
292 | const { message = DEFAULT_PING_MESSAGE } = resolveNestedOptions(heartbeat);
293 | if (result.data === message) {
294 | return;
295 | }
296 | }
297 | data.value = result.data;
298 | onMessage?.(task, result);
299 | });
300 | };
301 |
302 | if (heartbeat) {
303 | const {
304 | message = DEFAULT_PING_MESSAGE,
305 | interval = 1000,
306 | pongTimeout = 1000,
307 | } = resolveNestedOptions(heartbeat);
308 |
309 | const { pause, resume } = useIntervalFn(
310 | () => {
311 | send(message, false);
312 | if (pongTimeoutWait != null) {
313 | return;
314 | }
315 | pongTimeoutWait = setTimeout(() => {
316 | // 明确关闭连接以避免重试
317 | close({});
318 | }, pongTimeout);
319 | },
320 | interval,
321 | { immediate: false },
322 | );
323 |
324 | heartbeatPause = pause;
325 | heartbeatResume = resume;
326 | }
327 |
328 | if (autoClose) {
329 | tryOnUnload(() => close({}));
330 | tryOnScopeDispose(() => close({}));
331 | }
332 |
333 | const open = () => {
334 | close({});
335 | explicitlyClosed = false;
336 | retried = 0;
337 | _init();
338 | };
339 |
340 | if (immediate) {
341 | watch(urlRef, open, { immediate: true });
342 | }
343 |
344 | return {
345 | data,
346 | status,
347 | isOpen,
348 | isConnecting,
349 | isClosed,
350 | close,
351 | send,
352 | open,
353 | task: taskRef,
354 | };
355 | }
356 |
--------------------------------------------------------------------------------
/src/useStorage/index.md:
--------------------------------------------------------------------------------
1 | # useStorage
2 |
3 | `uni-app` 关于 `异步 storage 操作` 的封装。
4 |
5 | UNIAPP官网文档:
6 |
7 | 具体实现借鉴了 `@vueuse/core` v10.7.1。。
8 |
9 | **注意:**
10 |
11 | - 这是异步操作,赋值后,ref 的值会立即生效,但并不会马上写入storage
12 | - 和 uni 的原生 getStorageSync 混用的情况下,有可能会导致 getStorageSync 读取不到值。
13 | - 如需和 uni 的原生 storage 操作混用,或需要同步操作,请使用[`useStorageSync`](../useStorageSync/index.md)
14 |
15 | ```typescript
16 | import { useStorage } from '@uni-helper/uni-use';
17 |
18 | const token = useStorage('authorization', '');
19 |
20 | // 赋值
21 | token.value = 'authorization-token';
22 |
23 | // 读取
24 | console.log(token.value); // authorization-token
25 | ```
26 |
--------------------------------------------------------------------------------
/src/useStorage/index.test.ts:
--------------------------------------------------------------------------------
1 | import { afterEach, describe, expect, it, vi } from 'vitest';
2 | import { useStorage } from '.';
3 | import { useSetup } from '../../test';
4 | import { sleep } from '../utils';
5 |
6 | describe('useStorage', () => {
7 | const getItemSpy = vi.spyOn(uni, 'getStorage');
8 | const setItemSpy = vi.spyOn(uni, 'setStorage');
9 |
10 | afterEach(() => {
11 | uni.clearStorage(); // uni 的 interceptor 仅支持异步接口
12 | });
13 |
14 | it('export module', () => {
15 | expect(useStorage).toBeDefined();
16 | });
17 |
18 | it('string', async () => {
19 | const key = 'string';
20 |
21 | const vm = useSetup(() => {
22 | const ref = useStorage(key, 'a');
23 | return { ref };
24 | });
25 | expect(vm.ref).toBe('a');
26 | expect(getItemSpy).toHaveBeenLastCalledWith({ key });
27 |
28 | vm.ref = 'b';
29 | await sleep(200);
30 | expect(vm.ref).toBe('b');
31 | expect(setItemSpy).toHaveBeenLastCalledWith({ key, data: 'b' });
32 | });
33 |
34 | it('number', async () => {
35 | const key = 'number';
36 |
37 | await sleep(200);
38 | uni.setStorageSync(key, '0'); // 模拟先有值
39 |
40 | await sleep(200);
41 | const store = useStorage(key, 1); // 再初始化
42 | expect(store.value).toBe(0);
43 |
44 | store.value = 2;
45 | await sleep(200);
46 | expect(setItemSpy).toHaveBeenLastCalledWith({ key, data: '2' });
47 |
48 | store.value = -1;
49 | await sleep(200);
50 | expect(setItemSpy).toHaveBeenLastCalledWith({ key, data: '-1' });
51 | });
52 |
53 | it('boolean', async () => {
54 | const key = 'boolean';
55 | const store = useStorage(key, true);
56 | await sleep(200);
57 | expect(store.value).toBe(true);
58 | });
59 |
60 | it('null string', async () => {
61 | const key = 'null_string';
62 |
63 | uni.setStorage({ key, data: 'null' });
64 |
65 | const store = useStorage(key, null);
66 | await sleep(200);
67 | const { data: storedValue } = await uni.getStorage({ key });
68 |
69 | expect(store.value).toBe('null');
70 | expect(storedValue).toBe('null');
71 | });
72 |
73 | it('null value', async () => {
74 | const key = 'null_value';
75 | uni.removeStorage({ key });
76 |
77 | const store = useStorage(key, null);
78 |
79 | expect(store.value).toBe(null);
80 |
81 | await sleep(200);
82 | expect(uni.getStorage({ key })).rejects.toThrowError(); // 将storage设为null将自动删除uni storage
83 | });
84 |
85 | it('undefined value', async () => {
86 | const key = 'undefined_value';
87 | uni.removeStorage({ key });
88 |
89 | const store = useStorage(key, undefined);
90 |
91 | expect(store.value).toBe(undefined);
92 |
93 | await sleep(200);
94 | expect(uni.getStorage({ key })).rejects.toThrowError(); // 将storage设为undefined将自动删除uni storage
95 | });
96 |
97 | it('remove value', async () => {
98 | const key = 'remove_value';
99 | uni.getStorage({ key, data: 'random' });
100 |
101 | const store = useStorage(key, null);
102 |
103 | store.value = null;
104 |
105 | await sleep(200);
106 |
107 | expect(store.value).toBe(null);
108 |
109 | expect(uni.getStorage({ key })).rejects.toThrowError();
110 | });
111 | });
112 |
--------------------------------------------------------------------------------
/src/useStorage/index.ts:
--------------------------------------------------------------------------------
1 | import type { ConfigurableEventFilter, ConfigurableFlush, RemovableRef, WatchPausableReturn } from '@vueuse/core';
2 | import type { Ref } from 'vue';
3 | import type { MaybeComputedRef } from '../types';
4 | import { pausableWatch, resolveUnref, tryOnMounted, tryOnScopeDispose } from '@vueuse/core';
5 | import { ref, shallowRef } from 'vue';
6 | import { useInterceptor } from '../useInterceptor';
7 |
8 | export type UniStorageLike = Pick;
9 |
10 | export interface Serializer {
11 | read: (raw: string) => T;
12 | write: (value: T) => string;
13 | }
14 |
15 | export type DataType = string | number | boolean | object | null;
16 |
17 | export function guessSerializerType(
18 | raw: T,
19 | ) {
20 | return raw == null
21 | ? 'any'
22 | : raw instanceof Set
23 | ? 'set'
24 | : raw instanceof Map
25 | ? 'map'
26 | : raw instanceof Date
27 | ? 'date'
28 | : typeof raw === 'boolean'
29 | ? 'boolean'
30 | : typeof raw === 'string'
31 | ? 'string'
32 | : typeof raw === 'object'
33 | ? 'object'
34 | : Number.isNaN(raw)
35 | ? 'any'
36 | : 'number';
37 | }
38 |
39 | const StorageSerializers: Record<
40 | 'boolean' | 'object' | 'number' | 'any' | 'string' | 'map' | 'set' | 'date',
41 | Serializer
42 | > = {
43 | boolean: {
44 | read: (v: any) => v === 'true',
45 | write: String,
46 | },
47 | object: {
48 | read: (v: any) => JSON.parse(v),
49 | write: (v: any) => JSON.stringify(v),
50 | },
51 | number: {
52 | read: (v: any) => Number.parseFloat(v),
53 | write: String,
54 | },
55 | any: {
56 | read: (v: any) => v,
57 | write: String,
58 | },
59 | string: {
60 | read: (v: any) => v,
61 | write: String,
62 | },
63 | map: {
64 | read: (v: any) => new Map(JSON.parse(v)),
65 | write: (v: any) => JSON.stringify([...(v as Map).entries()]),
66 | },
67 | set: {
68 | read: (v: any) => new Set(JSON.parse(v)),
69 | write: (v: any) => JSON.stringify([...(v as Set)]),
70 | },
71 | date: {
72 | read: (v: any) => new Date(v),
73 | write: (v: any) => v.toISOString(),
74 | },
75 | };
76 |
77 | interface Data extends Ref {
78 | key: string;
79 | type: 'boolean' | 'object' | 'number' | 'any' | 'string' | 'map' | 'set' | 'date';
80 | isUpdating: boolean;
81 | serializer: Serializer;
82 | default: T;
83 | /** 写入 storage 的 timer */
84 | timer: NodeJS.Timeout;
85 | /** data 修改监听器 */
86 | watch: WatchPausableReturn;
87 | read: () => T;
88 | write: (val: T) => void;
89 | /** 根据 storage 的值,刷新变量。 */
90 | refresh: () => Data;
91 | /** 将变量的值写入 storage */
92 | sync: () => void;
93 | /** 清除写入操作的timer */
94 | clearTimer: () => void;
95 | /** 使用 raw 值更新 data */
96 | updateByRaw: (raw: string | null | undefined) => void;
97 | }
98 |
99 | const store: Record> = {};
100 |
101 | export interface UseStorageOptions extends ConfigurableEventFilter, ConfigurableFlush {
102 | /**
103 | * 是否监听深层变化
104 | *
105 | * @default true
106 | */
107 | deep?: boolean;
108 | /**
109 | * 是否监听 setStorage、removeStorage 和 clearStorage 引起的本地缓存变化
110 | *
111 | * @default true
112 | */
113 | listenToStorageChanges?: boolean;
114 | /**
115 | * 当本地缓存不存在时,是否把默认值写入缓存
116 | *
117 | * @deprecated 变量ref和storage是响应式的,当storage没值,返回带默认值的ref必然会写入storage
118 | * @default true
119 | */
120 | writeDefaults?: boolean;
121 | /**
122 | * 是否合并默认值和本地缓存值
123 | *
124 | * 当设置为 true 时,浅合并对象
125 | *
126 | * 你也可以传一个方法来自定义合并
127 | *
128 | * @default false
129 | */
130 | mergeDefaults?: boolean | ((storageValue: T, defaults: T) => T);
131 | /** 自定义数据序列化 */
132 | serializer?: Serializer;
133 | /**
134 | * 错误回调
135 | *
136 | * 默认用 `console.error` 打印错误
137 | */
138 | onError?: (error: unknown) => void;
139 | /**
140 | * 是否使用 shallowRef
141 | *
142 | * @default false
143 | */
144 | shallow?: boolean;
145 | /**
146 | * Wait for the component to be mounted before reading the storage.
147 | *
148 | * @default false
149 | */
150 | initOnMounted?: boolean;
151 | /** 异步 storage */
152 | storage?: UniStorageLike;
153 | }
154 |
155 | export function useStorage(
156 | key: string,
157 | initialValue?: MaybeComputedRef,
158 | options?: UseStorageOptions,
159 | ): RemovableRef;
160 |
161 | export function useStorage(
162 | key: string,
163 | initialValue: MaybeComputedRef,
164 | options?: UseStorageOptions,
165 | ): RemovableRef;
166 |
167 | /**
168 | * 响应式的本地缓存(异步)
169 | *
170 | * https://uniapp.dcloud.net.cn/api/storage/storage.html
171 | */
172 | export function useStorage(
173 | key: string,
174 | initialValue?: MaybeComputedRef,
175 | options: UseStorageOptions = {},
176 | ): RemovableRef {
177 | const {
178 | flush = 'pre',
179 | deep = true,
180 | listenToStorageChanges = true,
181 | mergeDefaults = false,
182 | shallow = false,
183 | eventFilter,
184 | onError = error => console.error(error),
185 | initOnMounted,
186 | storage = uni as UniStorageLike,
187 | } = options;
188 |
189 | const rawInit = resolveUnref(initialValue) as T;
190 |
191 | const type = guessSerializerType(rawInit);
192 |
193 | const hasStore = !!store[key];
194 |
195 | const data = hasStore ? store[key] : (shallow ? shallowRef : ref)(rawInit) as Ref as Data;
196 |
197 | const serializer = options.serializer ?? StorageSerializers[type];
198 |
199 | data.key = key;
200 | data.type = type;
201 | data.serializer = serializer;
202 | data.isUpdating = false;
203 | data.default = rawInit;
204 | data.read = readStorage;
205 | data.write = writeStorageImmediately;
206 | data.refresh = () => {
207 | data.read();
208 | return data;
209 | };
210 | data.sync = () => data.write(data.value);
211 | data.clearTimer = clearTimer;
212 | data.updateByRaw = updateByRaw;
213 |
214 | store[key] = data; // 重新映射
215 |
216 | if (hasStore) {
217 | // 不重复读数据
218 | return data;
219 | }
220 |
221 | if (initOnMounted) {
222 | tryOnMounted(data.read);
223 | }
224 | else {
225 | data.read();
226 | }
227 |
228 | data.watch = pausableWatch(data, () => !data.isUpdating && writeStorage(data.value), { flush, deep, eventFilter });
229 |
230 | if (listenToStorageChanges) {
231 | listenDataChange(data);
232 | }
233 |
234 | tryOnScopeDispose(clearTimer);
235 |
236 | function clearTimer() {
237 | data.timer && clearTimeout(data.timer);
238 | }
239 |
240 | function writeStorage(val: T) {
241 | clearTimer();
242 | // 如果是同步操作,则直接写 storage
243 | if (flush === 'sync') {
244 | writeStorageImmediately(val);
245 | return;
246 | }
247 |
248 | data.timer = setTimeout(() => writeStorageImmediately(val), 100);
249 | }
250 |
251 | function writeStorageImmediately(val: T) {
252 | clearTimer();
253 |
254 | if (data.isUpdating) {
255 | return;
256 | }
257 |
258 | try {
259 | data.isUpdating = true;
260 |
261 | if (val == null) {
262 | storage.removeStorage({
263 | key,
264 | fail: error => onError(error),
265 | });
266 | clearTimer();
267 | return;
268 | }
269 | const serialized = data.serializer.write(val);
270 | storage.setStorage({
271 | key,
272 | data: serialized,
273 | fail: error => onError(error),
274 | });
275 | }
276 | catch (error) {
277 | onError(error);
278 | }
279 | finally {
280 | data.isUpdating = false;
281 | }
282 | }
283 |
284 | function updateByRaw(raw: string | null | undefined) {
285 | try {
286 | if (raw == null) {
287 | // 没有对应的值,直接使用默认值
288 | data.value = data.default;
289 | return;
290 | }
291 |
292 | // 解析 value
293 | const value: T = data.serializer.read(raw);
294 |
295 | if (mergeDefaults) {
296 | // 如果是方法,调用
297 | if (typeof mergeDefaults === 'function') {
298 | data.value = mergeDefaults(value, data.default);
299 | return;
300 | }
301 |
302 | // 如果是对象,浅合并
303 | if (type === 'object' && !Array.isArray(value)) {
304 | data.value = { ...(data.default as any), ...(value as any) };
305 | return;
306 | }
307 | }
308 |
309 | // 有对应的值,不需要合并
310 | data.value = value;
311 | }
312 | catch (err: any) {
313 | onError(err);
314 | }
315 | };
316 |
317 | function readStorage() {
318 | // 读取本地缓存值
319 | storage.getStorage({
320 | key: data.key,
321 | success: ({ data: raw }) => {
322 | updateByRaw(raw);
323 | },
324 | fail: () => {
325 | updateByRaw(undefined);
326 | },
327 | });
328 |
329 | return data.value;
330 | }
331 |
332 | return data;
333 | }
334 |
335 | function listenDataChange(data: Data) {
336 | useInterceptor('setStorage', {
337 | invoke: (args) => {
338 | if (args[0].key !== data.key) {
339 | return false;
340 | }
341 | // 非主动更新
342 | if (!data.isUpdating) {
343 | data.isUpdating = true;
344 |
345 | const raw = (typeof args[0].data !== 'string' && args[0].data != null)
346 | ? JSON.stringify(args[0].data)
347 | : args[0].data;
348 |
349 | data.updateByRaw(raw);
350 |
351 | data.isUpdating = false;
352 | }
353 | },
354 | });
355 | useInterceptor('removeStorage', {
356 | invoke: (args) => {
357 | if (args[0].key !== data.key) {
358 | return false;
359 | }
360 | // 非主动更新
361 | if (!data.isUpdating) {
362 | data.isUpdating = true;
363 | data.value = undefined as unknown as T;
364 | data.isUpdating = false;
365 | }
366 | },
367 | });
368 | useInterceptor('clearStorage', {
369 | complete: () => {
370 | data.isUpdating = true;
371 | data.value = undefined as unknown as T;
372 | data.isUpdating = false;
373 | },
374 | });
375 |
376 | useInterceptor('setStorageSync', {
377 | invoke: (args) => {
378 | if (args[0] !== data.key) {
379 | return false;
380 | }
381 | // 非主动更新
382 | if (!data.isUpdating) {
383 | data.isUpdating = true;
384 |
385 | const raw = (typeof args[1] !== 'string' && args[1] != null)
386 | ? JSON.stringify(args[1])
387 | : args[1];
388 |
389 | data.updateByRaw(raw);
390 |
391 | data.isUpdating = false;
392 | }
393 | },
394 | });
395 | useInterceptor('removeStorageSync', {
396 | invoke: (args) => {
397 | if (args[0] !== data.key) {
398 | return false;
399 | }
400 | // 非主动更新
401 | if (!data.isUpdating) {
402 | data.isUpdating = true;
403 | data.value = undefined as unknown as T;
404 | data.isUpdating = false;
405 | }
406 | },
407 | });
408 | useInterceptor('clearStorageSync', {
409 | complete: () => {
410 | data.isUpdating = true;
411 | data.value = undefined as unknown as T;
412 | data.isUpdating = false;
413 | },
414 | });
415 | }
416 |
--------------------------------------------------------------------------------
/src/useStorageAsync/index.md:
--------------------------------------------------------------------------------
1 | # useStorageAsync (废弃,请使用 useStorage)
2 |
3 | `uni-app` 关于 `异步 storage 操作` 的封装。
4 |
5 | UNIAPP官网文档:
6 |
7 | 具体实现借鉴了 `@vueuse/core` v10.7.1。。
8 |
9 | **注意:**
10 |
11 | - 这是异步操作,赋值后,ref 的值会生效,但并不会马上写入storage
12 | - 和 uni 的原生 getStorageSync 混用的情况下,有可能会导致 getStorageSync 读取不到值。
13 | - 如需和 uni 的原生 storage 操作混用,或需要同步操作,请使用[`useStorageSync`](../useStorageSync/index.md)
14 |
15 | ```typescript
16 | import { useStorageAsync } from '@uni-helper/uni-use';
17 |
18 | const token = useStorageAsync('authorization', '');
19 |
20 | // 赋值
21 | token.value = 'authorization-token';
22 |
23 | // 读取
24 | console.log(token.value); // authorization-token
25 | ```
26 |
--------------------------------------------------------------------------------
/src/useStorageAsync/index.ts:
--------------------------------------------------------------------------------
1 | /** @deprecated use `useStorage` instead */
2 | export { useStorage as useStorageAsync } from '../useStorage';
3 |
--------------------------------------------------------------------------------
/src/useStorageSync/index.md:
--------------------------------------------------------------------------------
1 | # useStorageSync
2 |
3 | `uni-app` 关于 `同步 storage 操作` 的封装。
4 |
5 | UNIAPP官网文档:
6 |
7 | **注意:**
8 |
9 | - 这是同步操作,赋值后会立即写入storage,直到写入结束前会一直阻塞线程
10 | - 如果需要非阻塞,请使用[`useStorage`](../useStorage/index.md)
11 | - 如无须使用 uni 原生的 storage 操作,建议使用异步 [`useStorage`](../useStorage/index.md)
12 |
13 | ```typescript
14 | import { useStorageSync } from '@uni-helper/uni-use';
15 |
16 | const token = useStorageSync('authorization', '');
17 |
18 | // 赋值
19 | token.value = 'authorization-token';
20 |
21 | // 读取
22 | console.log(token.value); // authorization-token
23 | ```
24 |
--------------------------------------------------------------------------------
/src/useStorageSync/index.ts:
--------------------------------------------------------------------------------
1 | import type { ConfigurableFlushSync, RemovableRef } from '@vueuse/core';
2 | import type { MaybeComputedRef } from '../types';
3 | import type { UniStorageLike, UseStorageOptions } from '../useStorage';
4 | import { useStorage } from '../useStorage';
5 |
6 | export type UniStorageSyncLike = Pick;
7 |
8 | // uni is not defined
9 | let UniStorage: UniStorageLike;
10 | function initUniStorageIfNotInited() {
11 | if (UniStorage) {
12 | return;
13 | }
14 |
15 | UniStorage = parseUniStorageLike({
16 | getStorageSync: uni.getStorageSync,
17 | setStorageSync: uni.setStorageSync,
18 | removeStorageSync: uni.removeStorageSync,
19 | });
20 | }
21 |
22 | function parseUniStorageLike(storageSync: UniStorageSyncLike) {
23 | const storage: UniStorageLike = {
24 | getStorage: ({ key, success, fail, complete }: UniNamespace.GetStorageOptions) => {
25 | try {
26 | const data = storageSync.getStorageSync(key);
27 | success && success({ data });
28 | }
29 | catch (error) {
30 | fail && fail(error);
31 | }
32 | finally {
33 | complete && complete(void 0);
34 | }
35 | },
36 | setStorage: ({ key, data, success, fail, complete }: UniNamespace.SetStorageOptions) => {
37 | try {
38 | const raw = storageSync.setStorageSync(key, data);
39 | success && success({ data: raw });
40 | }
41 | catch (error) {
42 | fail && fail(error);
43 | }
44 | finally {
45 | complete && complete(void 0);
46 | }
47 | },
48 | removeStorage: ({ key, success, fail, complete }: UniNamespace.RemoveStorageOptions) => {
49 | try {
50 | storageSync.removeStorageSync(key);
51 | success && success({ data: void 0 });
52 | }
53 | catch (error) {
54 | fail && fail(error);
55 | }
56 | finally {
57 | complete && complete(void 0);
58 | }
59 | },
60 | };
61 |
62 | return storage;
63 | }
64 |
65 | export interface UseStorageSyncOptions
66 | extends Omit, 'flush' | 'storage'>,
67 | ConfigurableFlushSync {
68 | /** 同步 storage */
69 | storage?: UniStorageSyncLike;
70 | }
71 |
72 | export function useStorageSync(
73 | key: string,
74 | initialValue: MaybeComputedRef,
75 | options?: UseStorageSyncOptions,
76 | ): RemovableRef;
77 | export function useStorageSync(
78 | key: string,
79 | initialValue: MaybeComputedRef,
80 | options?: UseStorageSyncOptions,
81 | ): RemovableRef;
82 | export function useStorageSync(
83 | key: string,
84 | initialValue: MaybeComputedRef,
85 | options?: UseStorageSyncOptions,
86 | ): RemovableRef;
87 | export function useStorageSync(
88 | key: string,
89 | initialValue: MaybeComputedRef,
90 | options?: UseStorageSyncOptions,
91 | ): RemovableRef;
92 | export function useStorageSync(
93 | key: string,
94 | initialValue: MaybeComputedRef,
95 | options?: UseStorageSyncOptions,
96 | ): RemovableRef;
97 |
98 | /**
99 | * 响应式的本地缓存 (同步)
100 | *
101 | * https://uniapp.dcloud.net.cn/api/storage/storage.html
102 | */
103 | export function useStorageSync(
104 | key: string,
105 | initialValue: MaybeComputedRef,
106 | options: UseStorageSyncOptions = {},
107 | ): RemovableRef {
108 | // fix uni is not defined
109 | initUniStorageIfNotInited();
110 |
111 | const { flush = 'sync', storage, ...others } = options;
112 |
113 | const storageAsync = storage ? parseUniStorageLike(storage) : UniStorage;
114 |
115 | return useStorage(key, initialValue, { flush, storage: storageAsync, ...others });
116 | }
117 |
--------------------------------------------------------------------------------
/src/useToast/index.md:
--------------------------------------------------------------------------------
1 | # useToast
2 |
3 | 返回一个方法,调用后显示消息提示框。
4 |
5 | ```typescript
6 | import { useToast } from '@uni-helper/uni-use';
7 |
8 | const showToast = useToast({
9 | /* 传入配置 */
10 | });
11 | const hideToast = showToast(); // 显示消息提示框
12 | hideToast(); // 隐藏消息提示框
13 | ```
14 |
15 | 可以传入一个对象来更新已有配置,这样会使用 [扩展运算符](https://es6.ruanyifeng.com/#docs/object#%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%89%A9%E5%B1%95%E8%BF%90%E7%AE%97%E7%AC%A6) 来确认最终配置。
16 |
17 | ```typescript
18 | showToast({
19 | /* 新传入配置 */
20 | });
21 | ```
22 |
--------------------------------------------------------------------------------
/src/useToast/index.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeComputedRef } from '../types';
2 | import { resolveUnref } from '@vueuse/core';
3 |
4 | export interface UniShowToastOptions extends UniApp.ShowToastOptions {}
5 | export type ShowToastOptions = MaybeComputedRef