Full Code of yanzhandong/v3hooks for AI

master 71a54b57668c cached
108 files
155.5 KB
46.7k tokens
87 symbols
1 requests
Download .txt
Repository: yanzhandong/v3hooks
Branch: master
Commit: 71a54b57668c
Files: 108
Total size: 155.5 KB

Directory structure:
gitextract_yd9gghg3/

├── .gitignore
├── README.md
├── babel.config.js
├── docs/
│   └── question.md
├── example/
│   ├── .eslintignore
│   ├── README.md
│   ├── babel.config.js
│   ├── jsconfig.json
│   ├── package.json
│   ├── public/
│   │   └── index.html
│   ├── src/
│   │   ├── App.vue
│   │   ├── components/
│   │   │   └── HelloWorld.vue
│   │   ├── main.ts
│   │   ├── pages/
│   │   │   ├── home/
│   │   │   │   └── index.vue
│   │   │   ├── useBoolean/
│   │   │   │   └── index.vue
│   │   │   ├── useCookie/
│   │   │   │   └── index.vue
│   │   │   ├── useDate/
│   │   │   │   └── index.vue
│   │   │   ├── useDynamicList/
│   │   │   │   └── index.vue
│   │   │   ├── useExternal/
│   │   │   │   └── index.vue
│   │   │   ├── useFullscreen/
│   │   │   │   └── index.vue
│   │   │   ├── useInterval/
│   │   │   │   └── index.vue
│   │   │   ├── useLocalStorage/
│   │   │   │   └── index.vue
│   │   │   ├── useLockFn/
│   │   │   │   └── index.vue
│   │   │   ├── useMediaQuery/
│   │   │   │   └── index.vue
│   │   │   ├── useNetwork/
│   │   │   │   └── index.vue
│   │   │   ├── useQRCode/
│   │   │   │   └── index.vue
│   │   │   ├── useRouteQuery/
│   │   │   │   └── index.vue
│   │   │   ├── useSessionStorage/
│   │   │   │   └── index.vue
│   │   │   ├── useSetAndUseMap/
│   │   │   │   └── index.vue
│   │   │   ├── useTextSelection/
│   │   │   │   └── index.vue
│   │   │   ├── useToggle/
│   │   │   │   └── index.vue
│   │   │   ├── useVirtualList/
│   │   │   │   └── index.vue
│   │   │   └── useWebSocket/
│   │   │       └── index.vue
│   │   ├── router.ts
│   │   └── shims-vue.d.ts
│   ├── tsconfig.json
│   └── vue.config.js
├── jest.config.js
├── package.json
├── packages/
│   ├── index.ts
│   ├── useBoolean/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useCookie/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDate/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDebounce/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDebounceFn/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDocumentVisibility/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDynamicList/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useExternal/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useFullscreen/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useInterval/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useLocalStorage/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useLockFn/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useMap/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useMediaQuery/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useNetwork/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useQRCode/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useRequest/
│   │   ├── __tests__/
│   │   │   └── index.test.ts
│   │   ├── index.md
│   │   ├── index.ts
│   │   ├── src/
│   │   │   ├── cache.ts
│   │   │   ├── fetch.ts
│   │   │   ├── loadingDelay.ts
│   │   │   ├── polling.ts
│   │   │   ├── service.ts
│   │   │   └── visibility.ts
│   │   └── types.d.ts
│   ├── useRouteQuery/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useSessionStorage/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useSet/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useTextSelection/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useThrottle/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useThrottleFn/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useTimeout/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useToggle/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useUnmount/
│   │   └── index.ts
│   ├── useVirtualList/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useWebSocket/
│   │   ├── index.md
│   │   └── index.ts
│   └── utils/
│       ├── index.ts
│       ├── memoryCache.ts
│       └── testingHelpers.ts
├── rollup.config.js
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# compiled output
/node_modules
/dist
example/node_modules
example/dist

# Logs
logs
*.log

# OS
.DS_Store

# Tests
packages/*/coverage
packages/*/.nyc_output

# IDEs and editors
.idea
.project
.classpath
.c9
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

================================================
FILE: README.md
================================================
<p align="center">
  <img align="center" style="width:200px" src="https://img-steward-online.goodaa.com.cn/568d4fc225de4b7e8067f9505ce97acb.png"/>
</p><br/>
<h1 align="center"><b>V3Hooks</b></h1>
<h4 align="center">针对 Vue3 的实用Hooks集合</h4>


<h4  align="center">
  V3Hooks也可以说是<a href="https://github.com/alibaba/hooks">ahooks</a>的Vue实现,绝大部分Api是保持一致的
</h4>
<br>

## 🔨安装
<h4 align="center">
  <pre>npm i <a href="https://www.npmjs.com/package/v3hooks">v3hooks</a> --save</pre>
  <i>or</i>
  <pre>yarn add <a href="https://www.npmjs.com/package/v3hooks">v3hooks</a></pre>
</h4>

## 🏃文档
<h4 align="center">
  <a href="https://yanzhandong868.gitbook.io/v3hooks/">使用文档</a>
</h4>

<br>

## ⚡使用

- **Async**
  - `useRequest` — 一个完整的管理异步数据请求的Hook,<a href="https://ahooks.js.org/zh-CN/hooks/async">aHook useRequest</a>的Vue3实现,Api完全一致,如果你使用过aHook这将无缝衔接到Vue3.
- **Side**
  - `useDebounce` — 用于处理防抖值的 Hook.
  - `useDebounceFn` — 用于处理防抖函数的 Hook.
  - `useThrottle` — 用于处理节流值的 Hook.
  - `useThrottleFn` — 用于处理节流函数的 Hook.
  - `useInterval` — 用于处理interval的 Hook.
  - `useTimeout` — 用于处理timeout的 Hook.
<!-- - **Browser** -->
- **State**
  - `useToggle` — 用于在两个状态值间切换的 Hook.
  - `useBoolean` — 优雅的管理 boolean 值的 Hook.
  - `useDate` — 用于处理时间格式化 Hook.
  - `useLocalStorage` — 简单高效管理localStorage的 Hook.
  - `useSessionStorage` — 简单高效管理SessionStorage的 Hook.
  - `useCookie` — 用于管理本地Cookie Hook.
  - `useNetwork` — 用于获取网络状态 Hook.
  - `useSet` — 用于管理Set的 Hook.
  - `useMap` — 用于管理Map的 Hook.
  - `useWebSocket` — 用于处理 WebSocket 的 Hook。
  <!-- - `useRouteQuery` — 用于获取url query值的 Hook. -->
- **UI**
  - `useVirtualList` — 用于长列表虚拟化列表的 Hook.
  - `useDynamicList` — 用于管理列表状态 Hook.
  - `useMediaQuery` — 用于监听 mediaQuery 状态的 Hook。
  - `useExternal` — 用于加载异步资源的 Hook.
  - `useFullscreen` — 一个用于处理 dom 全屏的 Hook.
  - `useDocumentVisibility` — 可以获取页面可见状态的 Hook.
  - `useTextSelection` — 实时获取用户当前选取的文本内容及位置Hook.
  - `useQRCode` — 用来生成二维码的Hook.
- **Advanced**
  - `useLockFn` — 用于增加异步函数增加竞态锁,防并发 Hook.


## 常见问题
常见问题请见 [文档](https://github.com/yanzhandong/v3hooks/blob/master/docs/question.md)


## 🤝 感谢
如果这个项目对您有帮助,欢迎Star

================================================
FILE: babel.config.js
================================================
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        modules: false,
      },
    ],
    '@babel/preset-react',
  ],
};


================================================
FILE: docs/question.md
================================================
## 常见问题

### setup中使用data为undefined
因为data是被Ref嵌套的响应式, 直接return到Template中使用没问题,如果想要在Setup中使用需要嵌套一层watchEffect来获取异步数据。
```
const { data, run, cancel } = useRequest(
    () => {
      return axios.get(
          `https://xxx.com/application/list`
      );
    }
);

watchEffect(()=>{
  console.log( data?.value );
})
```
可以看到下面demo中React中Ahooks也是需要这么使用的
https://codesandbox.io/s/determined-glitter-m77g6?file=/src/App.js
### 在tsx中使用useRequest
  在一个tsx项目中使用useRequest,不能直接使用data返回值到html中,因为data是一个被Ref嵌套的响应式数据,在html中使用便利需要使用.value来获取真实数据,可以参考以下例子做法
```
import { defineComponent, watchEffect, ref } from 'vue';
import { useRequest,useTimeout } from 'v3hooks';

// 模拟列表请求
const mockRequest = ()=>{
  return new Promise((resolve,reject)=>{
    useTimeout(()=>{
      resolve({code:200,data:[{name:'aaa'},{name:'bbbbb'},{name:'ccccc'}]})
    },ref(500))
  })
};

interface MockDataItem{
  name:string
}

export default defineComponent({
  name: 'App',
  setup() {
    const content = ref<MockDataItem[]>([]);
    const { data } = useRequest<any>(() => mockRequest() );

    watchEffect(()=>{
      if(data?.value && data.value.code === 200){
        content.value = data.value.data;
      }
    })
  
    return () => (
      <>
        <h1>姓名表</h1>
        {
          content.value.map((item:MockDataItem)=>{
            return (
              <h3>{item.name}</h3>
            )
          })
        }
      </>
    );
  }
});
```

================================================
FILE: example/.eslintignore
================================================
dist

================================================
FILE: example/README.md
================================================
# example

## Project setup
```
npm install
```

### Compiles and hot-reloads for development
```
npm run serve
```

### Compiles and minifies for production
```
npm run build
```

### Lints and fixes files
```
npm run lint
```

### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).


================================================
FILE: example/babel.config.js
================================================
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ]
}


================================================
FILE: example/jsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "baseUrl": "./",
    "moduleResolution": "node",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  }
}


================================================
FILE: example/package.json
================================================
{
  "name": "example",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "@vueuse/core": "^5.0.3",
    "axios": "^0.21.1",
    "core-js": "^3.8.3",
    "v3hooks": "^1.5.0",
    "vue": "^3.0.4",
    "vue-router": "4.0.3"
  },
  "devDependencies": {
    "@babel/core": "^7.12.16",
    "@babel/eslint-parser": "^7.12.16",
    "@typescript-eslint/eslint-plugin": "^4.15.1",
    "@typescript-eslint/parser": "^4.15.1",
    "@vue/cli-plugin-babel": "~5.0.0-beta.2",
    "@vue/cli-plugin-eslint": "~5.0.0-beta.2",
    "@vue/cli-plugin-typescript": "~4.5.0",
    "@vue/cli-service": "~5.0.0-beta.2",
    "@vue/compiler-sfc": "^3.0.4",
    "@vue/eslint-config-typescript": "^7.0.0",
    "eslint": "^7.20.0",
    "eslint-plugin-vue": "^7.2.0",
    "typescript": "~4.1.5"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/vue3-essential",
      "eslint:recommended",
      "@vue/typescript"
    ],
    "parserOptions": {
      "parser": "@typescript-eslint/parser"
    },
    "rules": {}
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead",
    "not ie 11"
  ]
}


================================================
FILE: example/public/index.html
================================================
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>


================================================
FILE: example/src/App.vue
================================================
<template>
  <router-view v-slot="{ Component }">
    <Suspense>
      <component :is="Component" />
    </Suspense>
  </router-view>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'App'
});
</script>

<style>
*{
  margin: 0;
  padding: 0;
}
button{
  display: block;
  padding: 10px 20px;
}
</style>


================================================
FILE: example/src/components/HelloWorld.vue
================================================
<template>
  <div class="hello">
    <p>{{ loading ? "loading" : data }}</p>
    <button @click="run">发起</button>
    <button @click="cancel">取消</button>
    <button @click="handleMutate">突变测试</button>
    <button @click="handleRefreshDeps">refreshDeps测试</button>


    <!-- useDebounceFn demo测试 -->
    <p style="marginTop: 16"> Clicked count: {{debounceFnValue}} </p>
    <button type="button" @click="debounceFnRun">
      useDebounceFn测试
    </button>

    <!-- useDebounce demo测试 -->
    <br/>
    <input
      v-model="debounceCurrValue"
      placeholder="Typed value"
      style=" width: 280 "
    />
    <p style="marginTop: 16">DebouncedValue: {{debounceValue}}</p>

    <!-- useThrottleFn demo测试 -->
    <p style="marginTop: 16"> Clicked count: {{throttleFnValue}} </p>
    <button type="button" @click="throttleFnRun">
      useThrottleFn测试
    </button>

    <!-- useThrottle demo测试 -->
    <br/>
    <input
      v-model="throttleCurrValue"
      placeholder="Typed value"
      style=" width: 280 "
    />
    <p style="marginTop: 16">throttleValue: {{throttleValue}}</p>

    <!-- useToggle测试 -->
    <p>useToggleDemoState: {{useToggleDemoState}}</p>
    <button @click="handleUseTToggle">直接设置</button>
    <button @click="useTToggle">useTToggle</button>
    <button @click="useTSetLeft">useTSetLeft</button>
    <button @click="useTSetCenter">useTSetCenter</button>
    <button @click="useTSetRight">useTSetRight</button>

    <p>{{ useBooleanState }}</p>
    <button @click="useBooleanToggle">toggle</button>
    <button @click="setTrue">setTrue</button>
    <button @click="setFalse">setFalse</button>


    <!-- useVirtualList测试 -->
    <br/>
    <button
      style="margin-top:30px;"
      type="button"
      @click="handleVirtualScrollTo"
    >
      scroll to
    </button>
    <div
      :ref="virtualContainerProps.ref" 
      @scroll="virtualContainerProps.onScroll"
      style="height: 300px; overflow: auto"
    >
      <div :style="virtualWrapperStyle">
        <div 
          v-for="active in virtualList" 
          :key="active"
          style="height:59px;border-bottom: 1px solid #cccccc;background-color: white"
        >
          {{ active}}
        </div>
      </div>
    </div>
    
  </div>
</template>

<script lang="ts">

import { 
  useRequest, 
  useDebounce, 
  useDebounceFn,
  useThrottleFn,
  useThrottle,
  useToggle,
  useBoolean,
  useVirtualList
} from "../../dist/index.js";

import { ref } from 'vue';
import axios from "axios";

export default {
  setup() {

    // useRequest demo测试
    const refreshTest = ref(11);
    // const refreshTest2 = ref(11);

    const { data, run, loading, cancel, mutate } = useRequest(
      (mobile:number, code:string) => {
        return axios.post(
          `http://localhost:8080?mobile=${mobile}&code=${code}`
        );
      },
      {
        // manual: true,
        defaultParams:[
          15652922446,
          '0000'
        ],
        refreshDeps: [ refreshTest ],
        refreshOnWindowFocus: true
      }
    );
    // run(1562922446,'0000');
    
    const handleRefreshDeps = ()=>{
      refreshTest.value = Math.random();
      console.log(refreshTest);
    }


    const handleMutate = ()=>{
      mutate({
        code: Math.random(),
        data: {
          id: +new Date()
        },
      });
    };


    // useDebounceFn demo测试
    const debounceFnValue = ref<number>(1);
    const { run:debounceFnRun } = useDebounceFn(()=>{
      debounceFnValue.value++
    },1000)

    // useDebounce demo测试
    const debounceCurrValue = ref(1);
    const debounceValue = useDebounce(debounceCurrValue);

    // useThrottleFn demo测试
    const throttleFnValue = ref(1);
    const { run:throttleFnRun } = useThrottleFn(()=>{
      throttleFnValue.value++
    })

    // useThrottle demo测试
    const throttleCurrValue = ref(1);
    const throttleValue = useThrottle(throttleCurrValue);

    //useToggle 测试
    const [ useToggleDemoState, [ useTToggle, useTSetLeft, useTSetCenter, useTSetRight]] = useToggle('left','center','right');
    const handleUseTToggle = ()=>{
      useTToggle('center')
    };

    //useBoolean 测试
    const [ useBooleanState,{ toggle: useBooleanToggle, setTrue, setFalse}] = useBoolean();


    // useVirtualList测试
    
    const {
      list: virtualList,
      wrapperStyle: virtualWrapperStyle,
      containerProps: virtualContainerProps,
      scrollTo: virtualScrollTo
    } =useVirtualList(Array.from(Array(99999).keys()),{
      itemHeight: 60,
      overscan: 10
    })

    const handleVirtualScrollTo = ()=>{
      virtualScrollTo(22);  
    }


    return {
      loading,
      data,
      run,
      cancel,
      handleMutate,
      handleRefreshDeps,

      debounceFnValue,
      debounceFnRun,

      debounceCurrValue,
      debounceValue,

      throttleFnValue,
      throttleFnRun,

      throttleCurrValue,
      throttleValue,

      useToggleDemoState,
      handleUseTToggle,
      useTToggle,
      useTSetLeft,
      useTSetCenter,
      useTSetRight,

      useBooleanState,
      useBooleanToggle,
      setTrue,
      setFalse,

      virtualContainerProps,
      virtualWrapperStyle,
      virtualList,
      handleVirtualScrollTo
    };
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>


================================================
FILE: example/src/main.ts
================================================
import { createApp } from 'vue'
import App from './App.vue'
import { createRouter } from './router'

const app = createApp(App);
const router = createRouter(); 
app.use(router);
app.mount('#app')


================================================
FILE: example/src/pages/home/index.vue
================================================
<template>
  <div class="hello">
    <h4>第一测试</h4>
    <p>{{ loading ? "loading" : data }}</p>
    <button @click="run">发起</button>
    <button @click="cancel">取消</button>
    <button @click="handleMutate">突变测试</button>
    <button @click="handleRefreshDeps">refreshDeps测试</button>
    
    <h4>第二Cache测试</h4>
    <p>{{ loading2 ? "loading" : data2 }}</p>
    <button @click="run2">发起</button>
  </div>
</template>

<script lang="ts">

import { 
  useRequest, 
  useTimeout
} from "v3hooks";

import { ref } from 'vue';
// import axios from "axios";


// 模拟列表请求
const mockRequest = ()=>{
  return new Promise((resolve)=>{
    console.log(1231)
    useTimeout(()=>{
      resolve({code:200,time:+(new Date()),data:[{name:'aaa'},{name:'bbbbb'},{name:'ccccc'}]})
    },500)
  })
};


export default {
  
  
  setup() {

    // useRequest demo测试
    const refreshTest = ref(11);
    // const refreshTest2 = ref(11);
    const isReady = ref(false);

    const { data, run, loading, cancel, mutate } = useRequest(
      () => {
        // return axios.post(
        //   `https://xxxx/auth/login`
        // );
        // 暂时使用mock
        return mockRequest();
      },
      {
        manual: true,
        pollingInterval: 1000,
        ready:isReady,
        // loadingDelay: 1000,
        refreshDeps: [ refreshTest ],
        onSuccess(data){
          console.log(data,'success')
        },
        refreshOnWindowFocus: true,
      }
    );

    setTimeout(()=>{
      console.log('isReady');
      isReady.value = true
    },1000)

    const { data:data2, run:run2, loading:loading2 } = useRequest(
      () => {
        return mockRequest();
      },
      {
        // manual: true,
        refreshDeps: [ refreshTest ],
        refreshOnWindowFocus: true,
        cacheKey: 'mock1',
        cacheTime: 6000,
        staleTime: 3000
      }
    );
    
    const handleRefreshDeps = ()=>{
      refreshTest.value = Math.random();
      console.log(refreshTest);
    }


    const handleMutate = ()=>{
      mutate({
        code: Math.random(),
        data: {
          id: +new Date()
        },
      });
    };




    return {
      loading,
      data,
      run,
      cancel,
      handleMutate,
      handleRefreshDeps,


      data2,
      run2,
      loading2
    };
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
* {
  margin: 0 auto;
  text-align: center;
  margin-bottom: 10px;
}
</style>


================================================
FILE: example/src/pages/useBoolean/index.vue
================================================
<template>
  <div class="hello">
    <div> {{useBooleanState}}</div>
    <button @click="toggle">toggle</button>
    <button @click="setTrue">setTrue</button>
    <button @click="setFalse">setFalse</button>
  </div>
</template>

<script lang="ts">
import {useBoolean } from "../../../dist/index.js";
export default {
  
  setup() {

    //useBoolean 测试
    const [ useBooleanState,{ toggle, setTrue, setFalse}] = useBoolean();
    
    return {
      useBooleanState,
      toggle,
      setTrue,
      setFalse
    };
  },
};
</script>

<style scoped>
  .hello{
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

================================================
FILE: example/src/pages/useCookie/index.vue
================================================
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <p> value:{{ state }}</p>
    <button @click="handlerUpdateState">修改Cookie</button>
  </div>
</template>

<script lang="ts">
import { useCookie } from "../../../dist/index.js";

export default {
  
  setup() {
    // 获取query中的a
    const state = useCookie('a',{
        defaultValue: '1111',
        watch: true,
        path: '/useCookie',
        expires: 1,

    });

    const handlerUpdateState = ()=>{
      state.value = String( Math.random() );
    };

    // useVirtualList测试
    return {
      state,
      handlerUpdateState
    };
  },
};
</script>

================================================
FILE: example/src/pages/useDate/index.vue
================================================
<template>
  <div class="hello">
    <div> value:{{ data }}</div>
    <button @click="handleUpdateTime">无参数刷新</button>
    <button @click="handleUpdateTimeParam">有参数刷新</button>
  </div>
</template>

<script lang="ts">
import { useDate } from "../../../dist/index.js";

export default {
  
  setup() {
    const { data, refresh } = useDate(+new Date(),{
        format: 'YYYY-MM-DD HH:mm:ss',
        method: 'hour',
        methodParam: 3
    });

    const handleUpdateTime = ()=>{
        refresh();
    }

    const handleUpdateTimeParam = ()=>{
        refresh('2021-7-16 12:17:00');
    }

    return {
      data,
      refresh,
      handleUpdateTime,
      handleUpdateTimeParam
    };
  },
};
</script>

<style scoped>
    .hello{
        display:flex;
        flex-direction: column;
        justify-content: flex-start;
        align-items: center;
        margin-top: 30px;
    }
    
</style>

================================================
FILE: example/src/pages/useDynamicList/index.vue
================================================
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <div style="width: 60vw">
      <p 
        v-for=" (active,index) in list" 
        :key="getKey(index)"
        style="width: 100%;height:60px;border:1px solid #cccccc;line-height:60px;"
      > value:{{ active }} uuid:{{getKey(index)}}</p>
    </div>
    <div style="width:39vw">
      <button @click="()=> insert(0,Math.random())">insert头部插入</button>
      <button @click="()=> resetList(['a','b','c'])">重置</button>
      <button @click="()=> merge(0, [Math.random(),Math.random()])">头部插入多个</button>
      <button @click="()=> replace(1, Math.random())">第二个替换</button>
      <button @click="()=> remove(1)">删除第二个</button>
      <button @click="()=> move(0,2)">一三互换位置</button>
      <button @click="()=> push(Math.random())">尾部插入</button>
      <button @click="()=> pop()">尾部删除</button>
      <button @click="()=> unshift(Math.random())">头部插入</button>
      <button @click="()=> shift()">头部删除</button>
    </div>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useDynamicList } from "../../../dist/index.js";

export default {
  
  setup() {
    const defalutValue = ref(['a','b','c'])
    const { 
      list,
      insert,
      resetList,
      merge,
      replace,
      remove,
      move,
      getKey,
      push,
      pop,
      unshift,
      shift,
    } = useDynamicList(defalutValue);

    // useVirtualList测试
    return {
      list,
      insert,
      resetList,
      merge,
      replace,
      remove,
      move,
      getKey,
      push,
      pop,
      unshift,
      shift,
    };
  },
};
</script>

================================================
FILE: example/src/pages/useExternal/index.vue
================================================
<template>
  <div class="hello">
    <div id="aa" ref="aa"></div>
    <div class="bd-example"><span class="badge badge-pill badge-primary">Primary</span><span class="badge badge-pill badge-secondary">Secondary</span><span class="badge badge-pill badge-success">Success</span><span class="badge badge-pill badge-danger">Danger</span><span class="badge badge-pill badge-warning">Warning</span><span class="badge badge-pill badge-info">Info</span><span class="badge badge-pill badge-light">Light</span><span class="badge badge-pill badge-dark">Dark</span></div>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useExternal } from "../../../dist/index.js";

export default {
  
  setup() {
    const aa = ref();
    const { load, unload } = useExternal(
        // 'http://img-steward-online.goodaa.com.cn/449c2e5dd6a948e3af67b69c4ece907a.js',
        // 'https://ahooks.js.org/useExternal/bootstrap-badge.css',
        'https://img-steward-test.goodaa.com.cn/99b2b706a5b942c2bcbfe211107c7b80.jpeg',
        (el)=>{ console.log(el) },
        {
            manual: true,
            target: aa
        }
    );

    setTimeout(()=>{
        load()
    },1000)

    setTimeout(()=>{
        unload()
    },8000)
    return {
      aa
    }
  },
};
</script>

================================================
FILE: example/src/pages/useFullscreen/index.vue
================================================
<template>
  <div class="hello">
    <div ref="fullScreen" style="background: white">
        <p>是否全屏: {{isFullscreen}}</p>
        <button @click="setFull" id="a">全屏</button>
        <button @click="exitFull">退出全屏</button>
        <button @click="toggle">toggle切换</button>
    </div>
  </div>
</template>

<script lang="ts">
import { ref, watch } from 'vue';
import { useFullscreen, useDocumentVisibility } from "../../../dist/index.js";

export default {
  setup() {
    const fullScreen = ref();
    const documentVisibility = useDocumentVisibility();
    const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen(fullScreen, {
        onFull: ()=>{
            console.log('全屏')
        },
        onExitFull: ()=>{
            console.log('非全屏')
        }
    });
    // const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen();
    // const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen(document.body);

    watch(
      documentVisibility,
      (value)=>{
        if(value) console.log('重新触发active')
      }
    )
    
    // useVirtualList测试
    return {
      isFullscreen,
      setFull,
      exitFull,
      toggle,
      fullScreen
    };
  }
};
</script>

================================================
FILE: example/src/pages/useInterval/index.vue
================================================
<template>
  <div class="hello">
    <div>value:{{ data }}</div>
    <button @click="clear">关闭</button>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useInterval,useTimeout } from "../../../dist/index.js";


export default {
  
  setup() {
    const data = ref(1);
    let delay = ref<null | number>(3000);

    useInterval(()=>{
      data.value++
    },delay);

    const clear = ()=>{ 
      delay.value = null;
    };

    useTimeout(()=>{
      console.log('fuck')
    },delay)

    useTimeout(()=>{
      console.log('fuck2')
    },1000)

    return {
      data,
      clear
    };
  },
};
</script>


================================================
FILE: example/src/pages/useLocalStorage/index.vue
================================================
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <div style="width: 60vw">
      <p> value:{{ state }}</p>
    </div>
    <div style="width:39vw">
      <button @click="handleUpdate">更新storage</button>
      <button @click="handleDelete">删除storage</button>
    </div>
  </div>
</template>

<script lang="ts">
import { useLocalStorage } from "../../../dist/index.js";

export default {
  
  setup() {
    const state = useLocalStorage('useLocalStorage');

    const handleUpdate = ()=>{
        state.value = { 'a': Math.random() };
    };

    const handleDelete = ()=>{
        state.value = undefined;
    };

    // useVirtualList测试
    return {
      state,
      handleUpdate,
      handleDelete
    };
  },
};
</script>

================================================
FILE: example/src/pages/useLockFn/index.vue
================================================
<template>
  <div class="hello">
    <div>value:{{ data }}</div>
    <button @click="submit">submit</button>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useLockFn } from "../../../dist/index.js";

function mockApiRequest() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(1);
    }, 2000);
  });
}

export default {
  
  setup() {
    const data = ref(1);
    const submit = useLockFn(async ()=> {
        await mockApiRequest();
        data.value++
    });

    return {
        data,
      submit,
    };
  },
};
</script>

<style scoped>
.hello {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
  margin-top: 30px;
}
</style>

================================================
FILE: example/src/pages/useMediaQuery/index.vue
================================================
<template>
  <div class="hello">
    <div>
      <p> value:{{ state }}</p>
    </div>
  </div>
</template>

<script lang="ts">
import { watch } from 'vue'
import { useMediaQuery } from "../../../dist/index.js";

export default {
  
  setup() {
    const state = useMediaQuery('(min-width: 480px)');

    watch(
      state,
      (value)=>{
        if(!value) console.log('已经是480px以下了')
      }
    )
    
    // useVirtualList测试
    return {
      state
    };
  },
};
</script>

================================================
FILE: example/src/pages/useNetwork/index.vue
================================================
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <p> 网络状态:{{ state }}</p>
  </div>
</template>

<script lang="ts">
import { useNetwork } from "../../../dist/index.js";

export default {
  
  setup() {
    // 获取query中的a
    const state = useNetwork();

    // useVirtualList测试
    return {
      state
    };
  },
};
</script>

================================================
FILE: example/src/pages/useQRCode/index.vue
================================================
<template>
  <div class="hello">
    <div> 二维码:</div>
    <img :src="state" alt="">
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useQRCode } from "../../../dist/index.js";

const logo = require('../../assets/logo.png')

export default {
  
  setup() {
    const text = ref<string>('https://www.baidu.com/')
    // let text2 = 'https://www.baidu.com/';

    const state = useQRCode(text,{
      logo: logo.default,
      colorDark: '#000000'
    });

    setTimeout(()=>{
      text.value = 'https://www.acfun.cn/'
    },2000)

    return {
      state
    };
  },
};
</script>

<style scoped>
  .hello{
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

================================================
FILE: example/src/pages/useRouteQuery/index.vue
================================================
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <p> value:{{ state }}</p>
    <button @click="handlerUpdateState">修改query</button>
  </div>
</template>

<script lang="ts">
import { useRouteQuery } from "../../../dist/index.js";

export default {
  
  setup() {
    // 获取query中的a
    const state = useRouteQuery('a');

    // console.log(state.value)

    const handlerUpdateState = ()=>{
      state.value = String( Math.random() );
    };

    // useVirtualList测试
    return {
      state,
      handlerUpdateState
    };
  },
};
</script>

================================================
FILE: example/src/pages/useSessionStorage/index.vue
================================================
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <div style="width: 60vw">
      <p> value:{{ state }}</p>
    </div>
    <div style="width:39vw">
      <button @click="handleUpdate">更新storage</button>
      <button @click="handleDelete">删除storage</button>
    </div>
  </div>
</template>

<script lang="ts">
import { useSessionStorage } from "../../../dist/index.js";

export default {
  
  setup() {
    const state = useSessionStorage('useLocalStorage',{a:111});

    const handleUpdate = ()=>{
        state.value = { 'a': Math.random() };
    };

    const handleDelete = ()=>{
        state.value = undefined;
    };

    // useVirtualList测试
    return {
      state,
      handleUpdate,
      handleDelete
    };
  },
};
</script>

================================================
FILE: example/src/pages/useSetAndUseMap/index.vue
================================================
<template>
  <div class="hello">
    <div>
      <p> value:{{ state }}</p>
      <button @click="()=> add(Math.random())">add</button>
    </div>

    <div>
      <p> value:{{ state2 }}</p>
      <button @click="()=> set('1',Math.random())">set</button>
      <button @click="()=> remove('1')">remove</button>
      <button @click="()=> setAll([ ['1','111'], ['2','2222'] ])">setAll</button>
    </div>
    
  </div>
</template>

<script lang="ts">
import { useSet, useMap } from "../../../dist/index.js";

export default {
  
  setup() {
    const [state , { add } ] = useSet();
    const [state2, { set, setAll, remove }] = useMap([
      ['1','321']
    ]);

    // const add = ()=>{};

    // useVirtualList测试
    return {
      state,
      add,

      state2,
      set,
      setAll,
      remove
    };
  },
};
</script>

================================================
FILE: example/src/pages/useTextSelection/index.vue
================================================
<template>
  <div style="text-align: center">
    <p ref="p"> 可选择区域: 123111111111111aaaaaaaaaaabbbbbbbbbbb eeeeeeeeeeeeeeee</p>
    <p>已选择的值:{{ text }}</p>
    <p>位置信息:rect: {{ rect }}</p>
    <p>left: {{ rect.left }}</p>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useTextSelection } from "../../../dist/index.js";

export default {
  setup() {
    const p = ref();
    const { text, rect } = useTextSelection();

    return {
        text,
        p,
        rect
    };
  },
};
</script>

================================================
FILE: example/src/pages/useToggle/index.vue
================================================
<template>
  <div class="hello">
    <div> {{state}}</div>
    <button @click="toggle">toggle</button>
    <button @click="setToggle">setToggle</button>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useToggle,useTimeout } from "../../../dist/index.js";
export default {
  
  setup() {

    const platform = ref<string>('安装 App');
    const platform2 = ref<string>('安装中...');
    const [state, [toggle]] = useToggle(platform, platform2,'不安装');

    useTimeout(() => {
      platform.value = `安装 ios App`
      platform2.value = '安装中2....'
    }, ref(3000));

    const setToggle = ()=>{
      toggle(platform)
    }
    
    return {
      state,
      toggle,
      setToggle
    };
  },
};
</script>

<style scoped>
  .hello{
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

================================================
FILE: example/src/pages/useVirtualList/index.vue
================================================
<template>
  <div class="hello">
    <button
      type="button"
      @click="handleVirtualScrollTo"
    >
      跳转到第222排
    </button>
    <div
      :ref="containerProps.ref"
      @scroll="containerProps.onScroll"
      class="container"
    >
      <div :style="wrapperStyle" class="wrapper">
        <div
          v-for="active in list"
          :key="active"
          class="item"
        >
          {{active}}
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { useVirtualList } from "../../../dist/index.js";

export default {
  
  data(){
    return {
    }
  },
  setup() {
    // useVirtualList测试

    const { list, wrapperStyle, containerProps, scrollTo } = useVirtualList(
      Array.from(Array(9999).keys()),
      {
        itemHeight: 60,
        overscan: 10,
      }
    );

    const handleVirtualScrollTo = () => {
      scrollTo(222);
    };

    return {
      list,
      wrapperStyle,
      containerProps,
      handleVirtualScrollTo,
    };
  },
};
</script>


<style scoped>
::-webkit-scrollbar {display:none}
.hello{
  display: flex;
  flex-direction: column;
  align-items: center;
}
.container{
  height: calc(100vh - 130px); 
  overflow-y: auto;
  margin-top: 30px; 
}
.wrapper{
  display: flex;
  flex-direction: column;
  align-items: center;
}
.item{
  display: flex;
  justify-content: center;
  align-items: center;
  width: 800px;
  height: 52px;
  border: 1px solid #cccccc;
  margin-bottom: 8px;
  background-color: white;
  position: relative;
}
button{
  margin-top: 30px;
}
</style>

================================================
FILE: example/src/pages/useWebSocket/index.vue
================================================
<template>
  <div class="hello">
    <div>webScoket状态: {{readyState}}</div>
    <button @click="connect">连接webScoket</button>
    <button @click="disconnect">关闭webScoket</button>
    <button @click="handleSendMessage">发送消息</button>
  </div>
</template>

<script lang="ts">
import { useWebSocket } from "../../../dist/index.js";
import { watchEffect } from '@vue/runtime-core';
export default {
  
  setup() {

    const { 
      readyState,
      latestMessage,
      disconnect,
      sendMessage,
    } = useWebSocket('ws://82.157.123.54:9010/ajaxchattest',{
      onMessage(event){
        console.log(event)
      }
    })

    const handleSendMessage = ()=>{
      sendMessage('hello v3hooks')
    }

    watchEffect(()=>{
      console.log(latestMessage.value)
    })

    return {
      readyState,
      disconnect,
      handleSendMessage
    };
  },
};
</script>

<style scoped>
  .hello{
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

================================================
FILE: example/src/router.ts
================================================
import {
    createRouter as _createRouter,
    createWebHashHistory
} from 'vue-router'

// Auto generates routes from vue files under ./pages
// https://vitejs.dev/guide/features.html#glob-import
// @ts-ignore
// const pages = import.meta.glob('./pages/*/index.vue')

// const routes = Object.keys(pages).map((path) => {
//     // @ts-ignore
//     const name = path.match(/\.\/pages(.*)\/index\.vue$/)[1].toLowerCase();
//     console.log(name,'name');
//     return {
//         path: name === '/home' ? '/' : name,
//         component: pages[path] // () => import('./pages/*.vue')
//     }
// })

export function createRouter() {
    return _createRouter({
        // use appropriate history implementation for server/client
        // import.meta.env.SSR is injected by Vite.
        history: createWebHashHistory(),
        routes:[
            {
                path:'/',
                component: () => import('./pages/home/index.vue')
            },
            {
                path:'/useVirtualList',
                component: () => import('./pages/useVirtualList/index.vue')
            },
            {
                path:'/useDynamicList',
                component: () => import('./pages/useDynamicList/index.vue')
            },
            {
                path:'/useLocalStorage',
                component: () => import('./pages/useLocalStorage/index.vue')
            },
            {
                path:'/useSessionStorage',
                component: () => import('./pages/useSessionStorage/index.vue')
            },
            // {
            //     path:'/useRouteQuery',
            //     component: () => import('./pages/useRouteQuery/index.vue')
            // },
            {
                path:'/useCookie',
                component: () => import('./pages/useCookie/index.vue')
            },
            {
                path:'/useDate',
                component: () => import('./pages/useDate/index.vue')
            },
            {
                path:'/useNetwork',
                component: () => import('./pages/useNetwork/index.vue')
            },
            {
                path:'/useLockFn',
                component: () => import('./pages/useLockFn/index.vue')
            },
            {
                path:'/useSetAndUseMap',
                component: () => import('./pages/useSetAndUseMap/index.vue')
            },
            {
                path:'/useMediaQuery',
                component: () => import('./pages/useMediaQuery/index.vue')
            },
            {
                path:'/useExternal',
                component: () => import('./pages/useExternal/index.vue')
            },
            {
                path:'/useFullscreen',
                component: () => import('./pages/useFullscreen/index.vue')
            },
            {
                path:'/useTextSelection',
                component: () => import('./pages/useTextSelection/index.vue')
            },
            {
                path:'/useInterval',
                component: () => import('./pages/useInterval/index.vue')
            },
            {
                path:'/useQRCode',
                component: () => import('./pages/useQRCode/index.vue')
            },
            {
                path:'/useToggle',
                component: () => import('./pages/useToggle/index.vue')
            },
            {
                path:'/useBoolean',
                component: () => import('./pages/useBoolean/index.vue')
            },
            {
                path:'/useWebSocket',
                component: () => import('./pages/useWebSocket/index.vue')
            }
        ]
    })
}

================================================
FILE: example/src/shims-vue.d.ts
================================================
/* eslint-disable */
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}


================================================
FILE: example/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}


================================================
FILE: example/vue.config.js
================================================
module.exports = {
    devServer: {
        port: 8081
    }
}

================================================
FILE: jest.config.js
================================================
module.exports = {
  moduleFileExtensions: ['js', 'ts', 'jsx', 'tsx'],
  transform: {
    '^.+\\.(js|jsx)$': 'babel-jest',
    '^.+\\.(ts|tsx)$': 'ts-jest',
  },
  testEnvironment: 'jsdom',
  collectCoverageFrom: [
    '<rootDir>/packages/**/*.{ts,tsx}',
    '!**/node_modules/**',
    '!<rootDir>/packages/__tests__/**/*',
    '!<rootDir>/dist/**/*',
  ],
  coveragePathIgnorePatterns: ['/node_modules/', '/__fixtures__/', '/fixtures/', '/__tests__/helpers/', '/__tests__/utils/', '__mocks__'],
  testMatch: ['**/__tests__/**/*.test.[jt]s?(x)'],
  globals: {
    'ts-jest': {
      babelConfig: './babel.config.js',
    },
  }
};

================================================
FILE: package.json
================================================
{
  "name": "v3hooks",
  "version": "1.5.0",
  "author": "yanzhandong",
  "description": "针对 Vue3 的实用Hooks集合",
  "keywords": [
    "vuehook",
    "hook",
    "vue3",
    "vue3hook",
    "v3hooks",
    "vueHook"
  ],
  "homepage": "https://yanzhandong868.gitbook.io/v3hooks/",
  "main": "dist/index.cjs.js",
  "module": "dist/index.es.js",
  "unpkg": "dist/index.js",
  "types": "dist/index.d.ts",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/yanzhandong/v3hooks.git"
  },
  "files": [
    "dist"
  ],
  "license": "MIT",
  "scripts": {
    "test": "jest",
    "watch": "rollup -c --watch",
    "build": "cross-env NODE_ENV=production rollup -c"
  },
  "devDependencies": {
    "@babel/core": "^7.16.12",
    "@babel/plugin-transform-runtime": "^7.16.10",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-react": "^7.16.7",
    "@rollup/plugin-babel": "^5.3.0",
    "@rollup/plugin-commonjs": "^21.0.1",
    "@rollup/plugin-node-resolve": "^13.1.3",
    "@types/jest": "^27.4.0",
    "@types/js-cookie": "^2.2.7",
    "@vue/test-utils": "^2.0.0-rc.17",
    "babel-jest": "^27.5.1",
    "cross-env": "^7.0.3",
    "jest": "^27.5.1",
    "mockdate": "^3.0.5",
    "rollup": "^2.52.6",
    "rollup-plugin-dts": "^3.0.2",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-typescript2": "^0.30.0",
    "rollup-plugin-vue": "^6.0.0",
    "ts-jest": "^27.1.3",
    "tslib": "^2.3.0",
    "typescript": "^4.3.5",
    "vue": "^3.0.11",
    "vue-router": "4.0.3"
  },
  "dependencies": {
    "@babel/runtime": "^7.16.7",
    "dayjs": "^1.10.5",
    "easyqrcodejs": "^4.4.5",
    "js-cookie": "^2.2.1"
  }
}


================================================
FILE: packages/index.ts
================================================
import useRequest from './useRequest/index';
import useDate from './useDate/index';
import useDebounce from './useDebounce/index';
import useDebounceFn from './useDebounceFn/index';
import useThrottle from './useThrottle/index';
import useThrottleFn from './useThrottleFn/index';
import useBoolean from './useBoolean/index';
import useToggle from './useToggle/index';
import useVirtualList from './useVirtualList/index';
import useDynamicList from './useDynamicList/index';
import useLocalStorage from './useLocalStorage/index';
import useSessionStorage from './useSessionStorage/index';
import useNetwork from './useNetwork/index';
import useCookie from './useCookie/index';
import useLockFn from './useLockFn/index';
import useSet from './useSet/index';
import useMap from './useMap/index';
import useMediaQuery from './useMediaQuery/index';
import useExternal from './useExternal/index';
import useFullscreen from './useFullscreen/index';
import useDocumentVisibility from './useDocumentVisibility/index'
import useTextSelection from './useTextSelection/index'
import useInterval from './useInterval/index'
import useTimeout from './useTimeout/index'
import useQRCode from './useQRCode/index'
import useWebSocket from './useWebSocket/index'
import useUnmount from './useUnmount/index'

// 暂时无法@/分包 未引入vue-router会导致都不能用
// import useRouteQuery from './useRouteQuery/index';


export {
    // Async
    useRequest,

    // Side
    useDebounce,
    useDebounceFn,
    useThrottle,
    useThrottleFn,
    useInterval,
    useTimeout,

    // State
    useBoolean,
    useToggle,
    useDate,
    useCookie,
    useLocalStorage,
    useSessionStorage,
    // useRouteQuery,
    useNetwork,
    useSet,
    useMap,
    useWebSocket,

    // UI
    useVirtualList,
    useDynamicList,
    useMediaQuery,
    useExternal,
    useFullscreen,
    useDocumentVisibility,
    useTextSelection,
    useQRCode,

    //Advanced
    useLockFn,
    useUnmount

}

================================================
FILE: packages/useBoolean/index.md
================================================
# useBoolean

优雅的管理 boolean 值的 Hook。


## 使用

```
<template>
    <div>
      <p>{{ useBooleanState }}</p>
      <button @click="useBooleanToggle">toggle</button>
      <button @click="setTrue">setTrue</button>
      <button @click="setFalse">setFalse</button>
    </div>
</template>

<script lang="ts">

import { ref } from 'vue';
import { useBoolean } from 'v3hooks';


export default {
  
  

  setup() {
    //useToggle 测试
    const [ useBooleanState,{ toggle: useBooleanToggle, setTrue, setFalse}] = useBoolean();


    return {
      useBooleanState,
      useBooleanToggle,
      setTrue,
      setFalse
    }
  }
}
</script>
```

useBoolean默认切换布尔值状态,也可以接收一个参数作为新的值。



## Api

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| defaultValue | 可选项,传入默认的状态值	 | Ref<boolean> | false |


### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state | 状态值 | - |
| actions | 操作集合	 | Actions |

### Actions

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| toggle | 触发状态更改的函数,可以接受一个可选参数修改状态值 | (value?: boolean) => void |
| setTrue | 设置状态值为 true | () => void |
| setFalse | 设置状态值为 false | () => void |

================================================
FILE: packages/useBoolean/index.ts
================================================
import { Ref } from 'vue';
import useToggle from '../useToggle';

// 默认值
const defaultValue = false;

interface Actions{
    toggle: ()=> void;
    setTrue: ()=> void;
    setFalse: ()=> void;
}

function useBoolean(
    value?: boolean
): [Ref<boolean>,Actions]
/**
 * 
 * @param defaultValue
 * @returns 
 */
function useBoolean (value?: boolean){

    value = value || defaultValue;

    const [ state, [ toggle ]] = useToggle(value,!value);
    
    const setTrue = () => toggle(true);
    const setFalse = () => toggle(false);

    const actions: Actions = { toggle, setTrue, setFalse }
    return [state, actions ]
}

export default useBoolean

================================================
FILE: packages/useCookie/index.md
================================================
# useCookie

一个用来操作Cookie的 Hook 。

## 使用Demo

```vue
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <p> value:{{ state }}</p>
    <button @click="handlerUpdateState">修改Cookie</button>
  </div>
</template>

<script lang="ts">
import { useCookie } from "v3hooks";

export default {
  
  setup() {
    // 获取cookie中的a
    const state = useCookie('a',{
        defaultValue: '1111',
        watch: true,
        path: '/useCookie',
        expires: 1,

    });

    const handlerUpdateState = ()=>{
      state.value = String( Math.random() );
    };

    // useVirtualList测试
    return {
      state,
      handlerUpdateState
    };
  },
};
</script>
```

useCookie接受一个key是cookie中的键名。 修改返回的state可直接修改cookie.

## 注意点
* state等于undefined或者null可用于删除本地Cookie  例:`state.value = undefined;`

## Api
```

interface Options{
    watch?: boolean,
    defaultValue?: string | undefined,
    expires?: number | Date,
    path?: string,
    domain?: string,
    secure?: boolean,
    sameSite?: 'strict' | 'lax' | 'none'
}

const state = useCookieState(
  key: string,
  options?: Options,
)
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| key | 存储在本地 cookie 的 key 值 | string	 | - |
| options |可选项,配置 cookie 属性,详见 Options | Options	 | - |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state | 本地 cookie 值 | Ref<any> |


### Options

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| defaultValue | 可选,定义 cookie 默认值,但不同步到本地 cookie | string	 | undefined |
| expires |可选,定义 cookie 存储有效时间 | number | Date	 | - |
| path | 可选,定义 cookie 可用的路径 | string 	| / |
| domain | 可选,定义 cookie 可用的域,默认为 cookie 创建的域名 | string | - |
| secure | 可选,cookie 传输是否需要 https 安全协议 | boolean | - |
| sameSite | 可选,cookie 不能与跨域请求一起发送 | `strict` | `lax` | `none`	 | - |

================================================
FILE: packages/useCookie/index.ts
================================================
import { ref, watch as vueWatch } from 'vue';
import Cookies from 'js-cookie';

interface Options{
    watch?: boolean,
    defaultValue?: string | undefined,
    expires?: number | Date,
    path?: string,
    domain?: string,
    secure?: boolean,
    sameSite?: 'strict' | 'lax' | 'none'
}

const defaultOptions = {
    watch: false,
    defaultValue: undefined
};
const useCookie = (key: string, options?:Options)=>{

    const {
        watch, 
        defaultValue
    } = { ...defaultOptions, ...options };

    const state = ref( Cookies.get(key) || defaultValue );

    const setCookie = ( value: any )=>{
        Cookies.set(key, value, { ...options });
        state.value = value;
    };

    if( watch ){
        vueWatch(
            state,
            (value)=>{
                if( value === null || value === undefined ){
                    Cookies.remove(key);
                    return
                }
                setCookie(value);
            },
            {
                deep: true
            }
        )
    }
    return state
};


export default useCookie

================================================
FILE: packages/useDate/index.md
================================================
# useDate

一个用来操作时间的 Hook 。

内部使用了 dayjs 作为format工具

## 使用Demo

```vue
<template>
  <div class="hello">
    <div> value:{{ data }}</div>
    <button @click="handleUpdateTime">无参数刷新</button>
    <button @click="handleUpdateTimeParam">有参数刷新</button>
  </div>
</template>

<script lang="ts">
import { useDate } from "v3hooks";

export default {
  
  setup() {
    const { data, refresh } = useDate(+new Date(),{
        format: 'YYYY-MM-DD HH:mm:ss',
        method: 'hour',
        methodParam: 3
    });

    const handleUpdateTime = ()=>{
        refresh();
    }

    const handleUpdateTimeParam = ()=>{
        refresh('2021-7-16 12:17:00');
    }

    return {
      data,
      refresh,
      handleUpdateTime,
      handleUpdateTimeParam
    };
  },
};
</script>
```

useDate接受一个时间并根据options来返回格式化后的数据。 可根据返回的refresh来进行更新调用.

其中method的参数具体使用可以参考dayjs的 [取值/赋值](https://dayjs.gitee.io/docs/zh-CN/get-set/millisecond)

## Api
```

interface Options{
    format?: string
    method?: 'format' | 'millisecond' | 'second' | 'minute' | 'hour' | 'date' |'day'  | 'month' | 'year',
    methodParam?: number 
}

function useDate(
    value?: Value | undefined, 
    options?: Options
): {
    readonly data: any,
    refresh: () => void;
}
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| initialValue | 初始化的时间值 |  string - number - Date | Date |
| options | 可选项,配置时间属性,详见 Options | Options | - |


### Options

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| format | 针对日期格式化 | string | `YYYY-MM-DD HH:mm:ss` |
| method | 获取时间的操作方法 | 见Api Options.method | `format` |
| methodParam | 针对日期格式化的操作方法的参数 | number  | - |


### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| data | 格式化后的时间值 | Ref<string> |
| refresh | 格式化后的时间值 | (refreshValue)=> void  |

================================================
FILE: packages/useDate/index.ts
================================================
import { ref, readonly } from 'vue';
import dayjs from 'dayjs';


const defaultOptions ={
    format: 'YYYY-MM-DD HH:mm:ss',
    method: 'format'
};

type Value =  string | number | Date;

interface Options{
    format?: string
    method?: 'format' | 'millisecond' | 'second' | 'minute' | 'hour' | 'date' |'day'  | 'month' | 'year',
    methodParam?: number 
}

function useDate(
    value?: Value | undefined, 
    options?: Options
): {
    readonly data: any,
    refresh: (refreshValue?: Value) => void;
}

function useDate( initialValue?:Value, options?: Options ){
    
    const state = ref<string>();
    
    let value = initialValue || +new Date();
    const {
        format,
        method,
        methodParam
    } = { ...defaultOptions, ...options }


    const refresh = ( refreshValue?:Value )=>{
        console.log(refreshValue);
        value = refreshValue || +new Date();
        switch( method ){
            case 'format':
                state.value = dayjs(value).format(format);
                break;
            case undefined :
                break;
            default:
                let data: any = dayjs(value);
                if( methodParam ){
                    data = data[method](methodParam);
                    if( options && options.format ){
                        data = data.format(format);
                    }
                }
                state.value = data
        }
       
    };

    refresh();

    const data = readonly( state);

    return {
        data,
        refresh
    }
};


export default useDate

================================================
FILE: packages/useDebounce/index.md
================================================
# useDebounce

用来处理防抖值的 Hook。

## 使用

```
<template>
    <div>
        <input
            v-model="debounceCurrValue"
            placeholder="Typed value"
            style="width: 280"
        />
        <p style="marginTop: 16">DebouncedValue: {{debounceValue}}</p>
    </div>
</template>

<script lang="ts">

import { ref } from 'vue';
import { useDebounce } from 'v3hooks';


export default {
  
  

  setup() {
    const debounceCurrValue = ref(1);
    const debounceValue = useDebounce(debounceCurrValue,500);

    return {
        debounceCurrValue,
        debounceValue,
    }
  }
}
</script>
```

使用useDebounce后,频繁设置debounceCurrValue不会立刻改变debounceValue, debounceValue只会在输入结束 500ms 后变化。

## Api

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| value | 需要防抖的值 | any | - |
| wait | 超时时间,单位为毫秒 | number | 1000 |

================================================
FILE: packages/useDebounce/index.ts
================================================
// import { debounce } from '../utils'
import useDebounceFn from '../useDebounceFn'
import { ref,Ref, watch } from 'vue';

// 默认值
const defaultDelay = 1000;

/**
 * 处理防抖值
 * @param value 
 * @param delay 
 * @returns 
 */
const useDebounce = <T>( value: Ref<T> , delay?:number )=>{
    
    delay = delay || defaultDelay;

    const res = ref<T>(value.value) as Ref<T>;

    // 利用useDebounceFn来简化处理值
    const { run } = useDebounceFn(()=> res.value = value.value ,delay);

    watch(value,()=> run(),{ deep: true });

    return res;
};


export default useDebounce

================================================
FILE: packages/useDebounceFn/index.md
================================================
# useDebounceFn

用来处理防抖函数的 Hook。


## 使用

```
<template>
    <div>
      <p style="marginTop: 16"> Clicked count: {{debounceFnValue}} </p>
      <button type="button" @click="debounceFnRun">
        useDebounceFn测试
      </button>
    </div>
</template>

<script lang="ts">

import { ref } from 'vue';
import { useDebounceFn } from 'v3hooks';


export default {
  
  

  setup() {
    const debounceFnValue = ref<number>(1);
    const { run:debounceFnRun } = useDebounceFn(()=>{
      debounceFnValue.value++
    },500)

    return {
      debounceFnValue,
      debounceFnRun,
    }
  }
}
</script>
```

频繁调用 debounceFnRun,但只会在所有点击完成 500ms 后执行一次相关函数



## Api

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| fn | 需要防抖执行的函数 | () => void | - |
| wait | 超时时间,单位为毫秒 | number | 1000 |

================================================
FILE: packages/useDebounceFn/index.ts
================================================
import { Fn, debounce } from '../utils'

const defaultDelay = 1000;
/**
 * 处理防抖函数
 * @param fn 
 * @param delay 
 * @returns 
 */
const useDebounceFn = ( fn:Fn, delay?: number )=>{
    const run = debounce( fn, typeof( delay ) === 'number'? delay : defaultDelay );
    return { run };
}


export default useDebounceFn

================================================
FILE: packages/useDocumentVisibility/index.md
================================================
# useDocumentVisibility

可以获取页面可见状态的 Hook。

## 使用Demo

### 基础用法
```vue
<script lang="ts">
import { watch } from 'vue';
import { useDocumentVisibility } from "v3hooks";

export default {
  setup() {
    const documentVisibility = useDocumentVisibility();
    watch(
      documentVisibility,
      (value)=>{
        if(value) console.log('重新触发active')
      }
    )
  }
};
</script>
```
监听 document 的可见状态


## Api
```
const useDocumentVisibility: () => Ref<boolean>;
```

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| documentVisibility |  判断 document 是否在是否处于可见状态	 | boolean |


================================================
FILE: packages/useDocumentVisibility/index.ts
================================================
import { ref, Ref } from 'vue';

const useDocumentVisibility = (): Ref<boolean> =>{
    const documentVisibility = ref<boolean>( document.hidden );
    let handler = ()=> {
        documentVisibility.value = document.hidden;
    };
    document.addEventListener('visibilitychange',handler );
    return documentVisibility
}

export default useDocumentVisibility

================================================
FILE: packages/useDynamicList/index.md
================================================
# useDynamicList

一个帮助你管理列表状态,并能生成唯一 key 的 Hook。


## 使用Demo

```vue
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <div style="width: 60vw">
      <p 
        v-for=" (active,index) in list" 
        :key="getKey(index)"
        style="width: 100%;height:60px;border:1px solid #cccccc;line-height:60px;"
      > value:{{ active }} uuid:{{getKey(index)}}</p>
    </div>
    <div style="width:39vw">
      <button @click="()=> insert(0,Math.random())">insert头部插入</button>
      <button @click="()=> resetList(['a','b','c'])">重置</button>
      <button @click="()=> merge(0, [Math.random(),Math.random()])">头部插入多个</button>
      <button @click="()=> replace(1, Math.random())">第二个替换</button>
      <button @click="()=> remove(1)">删除第二个</button>
      <button @click="()=> move(0,2)">一三互换位置</button>
      <button @click="()=> push(Math.random())">尾部插入</button>
      <button @click="()=> pop()">尾部删除</button>
      <button @click="()=> unshift(Math.random())">头部插入</button>
      <button @click="()=> shift()">头部删除</button>
    </div>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useDynamicList } from "v3hooks";

export default {
  
  setup() {
    const defalutValue = ref(['a','b','c'])
    const { 
      list,
      insert,
      resetList,
      merge,
      replace,
      remove,
      move,
      getKey,
      push,
      pop,
      unshift,
      shift,
    } = useDynamicList(defalutValue);

    // useVirtualList测试
    return {
      list,
      insert,
      resetList,
      merge,
      replace,
      remove,
      move,
      getKey,
      push,
      pop,
      unshift,
      shift
    };
  },
};
</script>
```

useDynamicList接受一个数组,导出一个list及一系列操作数组的方法。


## Api

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| initialValue | 列表的初始值		 | T[]	 | [] |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| list | 当前的列表	 | T[] |
| resetList | 重新设置 list 的值		 | (list: T[]) => void |
| insert | 在指定位置插入元素	 | (index: number, obj: T) => void	 |
| merge | 在指定位置插入多个元素 | (index: number, obj: T[]) => void	 |
| replace | 替换指定元素 | (index: number, obj: T) => void	 |
| remove | 删除指定元素	 | (index: number) => void	 |
| move | 移动元素	 | (oldIndex: number, newIndex: number) => void		 |
| getKey | 获得某个元素的 uuid | (index: number) => number |
| getIndex | 获得某个key的 index	 | (key: number) => number	 |
| push | 在列表末尾添加元素 | (obj: T) => void	|
| pop | 移除末尾元素 | () => void	 |
| unshift | 在列表起始位置添加元素 | (obj: T) => void	 |
| shift | 移除起始位置元素 | () => void	 |



================================================
FILE: packages/useDynamicList/index.ts
================================================
import { ref, Ref, isRef } from 'vue';

const useDynamicList = <T = any>(initialValue: Ref<T[]>)=>{

    let uuid = <number>(0);
    const uuidKeys = ref<number[]>([]);

    const setUUID = (index?:number)=>{
        index = index === undefined ? uuidKeys.value.length : index;
        uuidKeys.value.splice( index, 0 ,uuid++ );
    };

    (()=>{
        initialValue.value.forEach(()=> setUUID() );
    })()

    const resetList = (resetList: Ref<T[]> | T[])=>{
        uuidKeys.value = [];
        if( isRef(resetList) ){
            resetList.value.forEach(()=> setUUID() );
            initialValue = resetList;
            return
        }
        resetList.forEach(()=> setUUID() );
        initialValue.value = resetList;
    };

    const insert = (index: number, obj: T)=>{
        initialValue.value.splice( index, 0, obj);
        setUUID(index);
    };

    const merge = (index: number, obj: T[])=>{
        obj.forEach((active,i) => setUUID(index + i) );
        initialValue.value.splice( index, 0, ...obj);
    };

    const replace = (index: number, obj: T)=>{
        initialValue.value.splice( index, 1, obj);
    };

    const remove = (index: number)=>{
        uuidKeys.value.splice( index, 1);
        initialValue.value.splice( index, 1);
    };

    const move = (oldIndex: number, newIndex: number)=>{
        if (oldIndex === newIndex)  return;
        [ initialValue.value[oldIndex], initialValue.value[newIndex] ] = [ initialValue.value[newIndex], initialValue.value[oldIndex] ];
        [ uuidKeys.value[oldIndex], uuidKeys.value[newIndex] ] = [ uuidKeys.value[newIndex], uuidKeys.value[oldIndex] ];
    };

    const getKey = (index: number)=> uuidKeys.value[index];
    const getIndex = (key: number)=> uuidKeys.value.indexOf(key);

    const push = (obj: T)=>{
        initialValue.value.push( obj );
        setUUID();
    };

    const pop = ()=>{
        initialValue.value.pop();
        uuidKeys.value.pop();
    };

    const unshift = (obj: T)=>{
        initialValue.value.unshift( obj );
        setUUID(0);
    };

    const shift = ()=>{
        initialValue.value.shift();
        uuidKeys.value.shift();
    };

    return {
        list: initialValue,
        resetList,
        insert,
        merge,
        replace,
        remove,
        move,
        getKey,
        getIndex,
        push,
        pop,
        unshift,
        shift
    }
}


export default useDynamicList

================================================
FILE: packages/useExternal/index.md
================================================
# useExternal

一个用于动态地向页面加载或卸载外部资源的 Hook。

## 使用Demo
### 基本使用
```vue
<script lang="ts">
import { useExternal } from "v3hooks";

export default {
  setup() {
    const { resources } = useExternal(
        'http://img-steward-online.goodaa.com.cn/449c2e5dd6a948e3af67b69c4ece907a.js',
        (el)=>{ console.log(el) }
    );
  },
};
</script>
```
页面上加载外部 javascript 文件,例如引入 449c2e5dd6a948e3af67b69c4ece907a.js

### 引入css
```vue
<template>
  <div class="hello">
    <div class="bd-example">
      <span class="badge badge-pill badge-primary">Primary</span>
      <span class="badge badge-pill badge-secondary">Secondary</span>
      <span class="badge badge-pill badge-success">Success</span>
      <span class="badge badge-pill badge-danger">Danger</span>
      <span class="badge badge-pill badge-warning">Warning</span>
      <span class="badge badge-pill badge-info">Info</span>
      <span class="badge badge-pill badge-light">Light</span>
      <span class="badge badge-pill badge-dark">Dark</span>
    </div>
  </div>
</template>

<script lang="ts">
import { useExternal } from "v3hooks";

export default {
  setup() {
    const { load, unload } = useExternal(
        'https://ahooks.js.org/useExternal/bootstrap-badge.css',
        (el)=>{ console.log(el) },
        {
            manual: true
        }
    );

    setTimeout(()=>{
        load()
    },1000)

    setTimeout(()=>{
        unload()
    },8000)
  },
};
</script>
```
页面上加载外部 css 文件,例如引入 bootstrap-badge.css

### 引入图片
```vue
<template>
  <div class="hello">
    <div ref="parent"></div>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useExternal } from "v3hooks";

export default {
  
  setup() {
    const parent = ref();
    const { load, unload } = useExternal(
        'https://img-steward-test.goodaa.com.cn/99b2b706a5b942c2bcbfe211107c7b80.jpeg',
        (el)=>{ console.log(el) },
        {
            manual: true,
            target: parent
        }
    );

    setTimeout(()=>{
        load()
    },1000)

    setTimeout(()=>{
        unload()
    },8000)
    return {
      parent
    }
  },
};
</script>
```
加载一个静态图片作为外部资源引入页面



useExternal接受一个src路径, 导出资源本身和操作方法.

## Api
```
interface Options {
    manual?: boolean;
    async?: boolean;
    crossOrigin?: 'anonymous' | 'use-credentials';
    referrerPolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
    noModule?: boolean;
    defer?: boolean;
    media?: string;
    target?: HTMLElement | Ref<HTMLElement>;
}
const useExternal: (src: string, onLoaded?: ((el: Elements) => void) | undefined, options?: Options) => {
    resources: Ref<Elements | null>;
    load: () => Promise<unknown>;
    unload: () => void;
};
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| src |  外部资源 url 地址	| string | - |
| onLoaded |  资源加载成功回调  | (el: Elements) => void) | - |
| options |  参数  | Options | - |

### Options

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| manual |  是否手动执行  | boolean | false |
| async |  script标签是否增加async属性  | boolean | - |
| crossOrigin |  script标签是否增加crossOrigin属性  | `'anonymous' - 'use-credentials'` | - |
| referrerPolicy |  script标签是否增加referrerPolicy属性  | 原生referrerPolicy |  | - |
| noModule |  script标签是否增加noModule属性  | boolean | - |
| defer |  script标签是否增加defer属性  | boolean | - |
| media |  引入外链样式表 <link> 的 media 属性, 如 all/screen/print/handheld  | string | all |
| target |  需插入外部图片资源 <img> 的父容器 DOM 节点或者 Ref  |  HTMLElement | Ref<HTMLElement> | - |


### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| resources	 | 加载后的资源	 | any |
| load	 | 开始加载外部资源	 | () => void |
| unload	 | 卸载外部资源	 | () => void |

================================================
FILE: packages/useExternal/index.ts
================================================
import { ref, Ref, isRef } from 'vue';

type Elements = HTMLScriptElement| HTMLLinkElement | HTMLImageElement;

interface Options{
    manual?: boolean,
    async?: boolean,
    crossOrigin?: 'anonymous' | 'use-credentials'
    referrerPolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url'
    noModule?: boolean
    defer?: boolean,
    media?: string,
    target?: HTMLElement | Ref<HTMLElement>
}


const useExternal = (
    src: string,
    onLoaded?: (el: Elements) => void,
    options: Options = {},
)=>{
    const resources = ref<Elements | null>( null );

    const {
        manual = false,
        async,
        crossOrigin,
        referrerPolicy,
        noModule,
        defer,
        media = 'all',
        target = document.body
    } = options;

    let el: Elements = document.createElement('script');

    let parentEl: Element | HTMLElement  = document.head;

    const loadScript = ()=> new Promise((resolve)=>{
        const isExist = document.querySelector(`script[src="${src}"]`);
        if( isExist ) return

        el = document.createElement('script');
        el.src = src;
        el.type = 'text/javascript';
        if( async ) el.async = async;
        if( defer ) el.defer = defer;
        if( noModule ) el.noModule = noModule;
        if( crossOrigin ) el.crossOrigin = crossOrigin;
        if( referrerPolicy ) el.referrerPolicy = referrerPolicy;

        el = parentEl.appendChild(el);
        resolve(el);
    });

    const loadCss = ()=> new Promise((resolve)=>{
        const isExist = document.querySelector(`link[href="${src}"]`);
        if( isExist ) return

        el = document.createElement('link');
        el.href = src;
        el.rel = 'stylesheet'
        el.type='text/css';
        el.media = media;

        el = parentEl.appendChild(el);
        resolve(el);
    });

    const loadImage = ()=> new Promise((resolve)=>{
        const isExist = document.querySelector(`img[src="${src}"]`);
        if( isExist ) return

        el = document.createElement('img');
        el.src = src;
        parentEl = isRef( target) ? target.value :  target;
        parentEl.appendChild(el);
        resolve(el);
    });

    const load = ()=> new Promise(async (resolve, reject)=>{
        if( /\.js$/.test(src) ){
            await loadScript();
        }
        if( /\.css$/.test(src)){
            await loadCss();
        }
        if( /\.(gif|jpg|jpeg|png|svg|GIF|JPG|PNG|)$/.test(src)){
            await loadImage()
        }
        resources.value = el;
        el.addEventListener('error', (event: any) => reject(event));
        el.addEventListener('abort', (event: any) => reject(event));
        el.addEventListener('load', () => {
            onLoaded && onLoaded(el)
        });
        resolve(el)
    });

    const unload = ()=>{
        if( resources.value ){
            parentEl.removeChild(resources.value);
        }
    }

    if( !manual ) load()

    return { resources, load, unload }
}

export default useExternal

================================================
FILE: packages/useFullscreen/index.md
================================================
# useFullscreen

一个用于处理 dom 全屏的 Hook。

## 使用Demo

### 基础用法
```vue
<template>
  <div class="hello">
    <div ref="fullScreen" style="background: white">
        <p>是否全屏: {{isFullscreen}}</p>
        <button @click="setFull" id="a">全屏</button>
        <button @click="exitFull">退出全屏</button>
        <button @click="toggle">toggle切换</button>
    </div>
  </div>
</template>

<script lang="ts">
import { useFullscreen } from "v3hooks";

export default {
  setup() {
    const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen();
    
    // useVirtualList测试
    return {
      isFullscreen,
      setFull,
      exitFull,
      toggle
    };
  }
};
</script>
```
没有传值默认为document.body

### 局部全屏
```vue
<template>
  <div class="hello">
    <div ref="fullScreen" style="background: white">
        <p>是否全屏: {{isFullscreen}}</p>
        <button @click="setFull" id="a">全屏</button>
        <button @click="exitFull">退出全屏</button>
        <button @click="toggle">toggle切换</button>
    </div>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useFullscreen } from "../../../dist/index.js";

export default {
  setup() {
    const fullScreen = ref();
    const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen(fullScreen, {
        onFull: ()=>{
            console.log('全屏')
        },
        onExitFull: ()=>{
            console.log('非全屏')
        }
    });
    
    return {
      isFullscreen,
      setFull,
      exitFull,
      toggle,
      fullScreen
    };
  }
};
</script>
```
Ref传值为fullScreen的div标签,则只会此区域全屏

## 介绍
useFullscreen接受一个HTMLElement, 导出操作方法.

## Api
```
interface Actions {
    setFull: () => void;
    exitFull: () => void;
    toggle: () => void;
}
const useFullscreen: (
  target?: Target$1 | undefined, 
  options?: Options | undefined
) => [
  isFullscreen: Ref<boolean>,
  actions: Actions
];
```

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| target |  原生Dom或者被Ref嵌套的Dom	|  `HTMLElement or Ref<HTMLElement> or (() => HTMLElement)` | document.body |
| options |  设置(可选)	| Options  | - |

### Options

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| onExitFull | 	监听退出全屏	 | ()=>void |
| onFull	 | 监听全屏		 | ()=>void |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| isFullscreen	 | 是否全屏		 | boolean |
| setFull	 | 设置全屏		 | ()=>void |
| exitFull | 退出全屏 | ()=>void |
| toggleFull | 切换全屏 | ()=>void |


================================================
FILE: packages/useFullscreen/index.ts
================================================
import { ref, Ref, isRef, onMounted, onUnmounted } from 'vue';

interface Options{
    onFull?: ()=> void,
    onExitFull?: ()=> void
}
interface Actions{
    setFull: ()=> void,
    exitFull: ()=> void,
    toggle: ()=> void
}

type Target = HTMLElement | ( () => HTMLElement ) | Ref<HTMLElement>;

const defaultOptions = {
    onFull: function(){},
    onExitFull: function(){},
};

const useFullscreen = (
    target?: Target, 
    options?: Options
):[
    isFullscreen: Ref<boolean>,
    actions: Actions
]=>{
    const fullScreenElement = !!document.fullscreenElement;
    const isFullscreen = ref(fullScreenElement);

    const {
        onFull,
        onExitFull
    } = { ...defaultOptions, ...options};

    let el:HTMLElement = document.body;

    const getEl = ()=>{
        if( typeof target === 'function'){
            return target()
        }
        return isRef( target ) ? target.value : target;
    };

    const handler = ()=>{
        if(isFullscreen.value){
            onFull()
        }else{
            onExitFull()
        }
    };

    onMounted(()=>{
        el = getEl() || el;
        el.addEventListener('fullscreenchange',handler) 
    });

    onUnmounted(()=>{
        el.removeEventListener('fullscreenchange',handler) 
    });

    const actions:Actions = {
        setFull: ()=>{
            if( isFullscreen.value ) return
            el.requestFullscreen();
            isFullscreen.value = true;
        },
        exitFull: ()=>{
            if( !isFullscreen.value ) return
            document.exitFullscreen();
            isFullscreen.value = false;
        },
        toggle: ()=>{
            isFullscreen.value ? actions.exitFull(): actions.setFull()
        }
    };
    

    return [ isFullscreen, actions ]
};

export default useFullscreen

================================================
FILE: packages/useInterval/index.md
================================================
# useInterval

一个可以处理 setInterval 的 Hook。


## 基础使用

```vue
<template>
  <div class="hello">
    <div>value:{{ data }}</div>
    <button @click="clear">关闭</button>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useInterval } from "../../../dist/index.js";


export default {
  
  setup() {
    const data = ref(1);
    let delay = ref<null | number>(1000);

    useInterval(()=>{
      data.value++
    },delay);

    const clear = ()=>{ 
      delay.value = null;
    };

    return {
      data,
      clear
    };
  },
};
</script>

```

每1000ms,执行一次,设置delay为null则立即中断


## 非中断式调用
```vue
<script lang="ts">
import { useInterval } from "v3hooks";
export default {
  
  setup() {
    useInterval(()=>{
      console.log('每 3s 执行一次')
    },3000)
  },
};
</script>

```

useInterval可以接受一个普通number参数,这样的useInterval可以接受一个普通number参数,不会被中断会一直被执行,谨慎使用!!


## Api
```
interface UseIntervalOptions {
    immediate?: boolean;
}
const useInterval: (
  fn: Fn, 
  delay: number | Ref<number | undefined | null>, 
  options?: UseIntervalOptions | undefined
) => void;
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| fn	 | 要重复调用的函数	 | (...args: any[]) => void | - |
| delay	 | 间隔时间,当取值为 null 或 undefined 时会停止计时器	 | number - Ref<number> - Ref<undefined> - Ref<null> | - |
| options	 | 配置计时器的行为,详见下面的 Options	 | Options | - |

### Options

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| immediate	 | 参数可以用来控制是否在首次渲染时立即执行 | boolean| false |

================================================
FILE: packages/useInterval/index.ts
================================================
import { ref, Ref, onMounted, onUnmounted, isRef } from 'vue';
import { Fn } from '../utils';

interface UseIntervalOptions{
    immediate?: boolean
}

const defaultOptions = {
    immediate: false,
};

const useInterval = (
    fn: Fn,
    delay: number | Ref<number | undefined | null>,
    options?: UseIntervalOptions
)=>{

    const{
        immediate
    } = {...defaultOptions,...options};
    
    const state = isRef(delay) ? delay : ref(delay);

    if( immediate ) fn()

    let timer: null | NodeJS.Timeout = null;
    
    const clear = ()=> timer && clearTimeout(timer)

    const handler = ()=>{
        if( state.value === undefined || state.value === null ) return
        fn();
        run();
    };

    const run = ()=>{
        if( state.value === undefined || state.value === null ){
            clear();
            return
        }
        setTimeout(handler,state.value)
    }
    
    run();

    onUnmounted(()=> clear() )
};

export default useInterval

================================================
FILE: packages/useLocalStorage/index.md
================================================
# useLocalStorage

一个可以将状态持久化存储在 localStorage 中的 Hook 。


## 使用Demo

```vue
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <div style="width: 60vw">
      <p> value:{{ state }}</p>
    </div>
    <div style="width:39vw">
      <button @click="handleUpdate">更新storage</button>
      <button @click="handleDelete">删除storage</button>
    </div>
  </div>
</template>

<script lang="ts">
import { useLocalStorage } from "v3hooks";

export default {
  
  setup() {
    const state = useLocalStorage('useLocalStorage',{a:231});

    const handleUpdate = ()=>{
        state.value = { a: Math.random()};
    };

    const handleDelete = ()=>{
        state.value = undefined;
    };

    // useVirtualList测试
    return {
      state,
      handleUpdate,
      handleDelete
    };
  },
};
</script>
```

useLocalStorage接受一个key和一个value,导出一个响应式的state, 用户直接赋值state.value可自动修改本地localStorage。

## 注意点
* 不设置value可用于获取本地LocalStorage  例:`useLocalStorage('useLocalStorage')`
* value等于undefined或者null可用于删除本地Storage  例:`state.value = undefined;`


## Api
```
const state = useLocalStorage(
  key: string,
  initialValue?: any,
  options?: Options
);

```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| key | LocalStorage存储键名 | any	 | - |
| initialValue | 初始值 | any	 | {} |
| options | 配置 | Options	 | - |

### Options

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| watch | 是否实时修改LocalStorage | boolean | true |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state | 可以被修改的数据源 | Ref<any> |



================================================
FILE: packages/useLocalStorage/index.ts
================================================
import { ref, Ref, isRef, watch as vueWatch } from 'vue';
import { TypeSerializers,getValueType } from '../utils'
const storage = localStorage;
interface Options{
    watch: boolean
}

const defaultOptions = {
    watch: true
}



const useLocalStorage = <T = any>( key: string, initialValue?: T | Ref<T>, options?:Options)=>{
    
    const { watch } = { ...defaultOptions, ...options };
    
    const data = ref() as Ref<T | undefined | null>;
    try{
        if( initialValue !== undefined ){
            data.value = isRef( initialValue ) ? initialValue.value : initialValue;
        }else{
            data.value = JSON.parse( storage.getItem(key) || '{}' );
        }
    }catch(error){
        console.log(error,'useLocalStorage初始化失败')
    }

    const type = getValueType(data.value);

    // 判断类型取格式化方法
    let serializer = TypeSerializers[type];


    const setStorage = ()=> storage.setItem( key, serializer.write(data.value) );;

    // 状态监听
    if( watch ){
        vueWatch(
            data,
            (newValue)=>{
                if( newValue === undefined || newValue === null ){
                    storage.removeItem(key);
                    return
                }
                setStorage();
            },
            {
                deep:true
            }
        )
    }
    
    setStorage()

    return data
};

export default useLocalStorage

================================================
FILE: packages/useLockFn/index.md
================================================
# useLockFn

用于给一个异步函数增加竞态锁,防止并发执行。


## 使用Demo

```vue
<template>
  <div class="hello">
    <div>value:{{ data }}</div>
    <button @click="submit">submit</button>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useLockFn } from "v3hooks";

function mockApiRequest() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(1);
    }, 2000);
  });
}

export default {
  
  setup() {
    const data = ref(1);
    const submit = useLockFn(async ()=> {
        await mockApiRequest();
        data.value++
    });

    return {
        data,
      submit,
    };
  },
};
</script>

<style scoped>
.hello {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
  margin-top: 30px;
}
</style>
```

useLockFn接受一个异步的函数, 并返回一个具有执行锁的函数

## Api
```
const useLockFn: (fn: Fn) => (...args: ArgsAny) => Promise<any>;
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| fn	 | 需要增加竞态锁的函数	 | (...args: any[]) => any | - |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| fn	 | 增加了竞态锁的函数 | (...args: any[]) => any |

================================================
FILE: packages/useLockFn/index.ts
================================================
import { ref } from 'vue';

type ArgsAny = any[];

type Fn = (...args: ArgsAny)=> Promise<any>;

const useLockFn = (fn: Fn)=>{
    const lock = ref(false);
    return async( ...args: ArgsAny )=>{
        if( lock.value ) return
        lock.value = true;
        try{
            const ret = await fn(...args);
            lock.value = false;
            return ret;
        }catch ( error ){
            lock.value = false;
            throw error;
        }
    }
};

export default useLockFn

================================================
FILE: packages/useMap/index.md
================================================
# useMap

一个可以管理 Map 类型状态的 Hook。


## 使用Demo

```vue
<template>
  <div class="hello">
    <div>
      <p> value:{{ state }}</p>
      <button @click="()=> set('1',Math.random())">set</button>
      <button @click="()=> remove('1')">remove</button>
      <button @click="()=> setAll([ ['1','111'], ['2','2222'] ])">setAll</button>
    </div>
    
  </div>
</template>

<script lang="ts">
import { useSet, useMap } from "../../../dist/index.js";

export default {
  
  setup() {
    const [state, { set, setAll, remove }] = useMap([
      ['1','321']
    ]);
    return {
      state,
      set,
      setAll,
      remove
    };
  },
};
</script>
```

useMap接受一个 Map 可接受的参数, 并导出以下方法.

## Api
```
type MapValue = readonly (readonly [any, any])[]

interface Actions<T>{
    set: ( key: string, value: T)=> void,
    get: ( key: string )=> T,
    remove: ( key: string )=> void,
    has: ( key: string )=> boolean,
    clear: ()=> void,
    setAll: (newMap: MapValue)=> void;
    reset: ()=> void,
}

function useMap <T = any>(initialValue?:MapValue) : [
    state: Ref<Map<any,any>>,
    actions: Actions<T>
]

```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| initialValue | 可选项,传入默认的 Map 参数	 | readonly[any,any] | - |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state	 | Map 对象	 | Map |
| set	 | 添加元素	 | ( key: string, value: T)=> void |
| get	| 移除元素	 | ( key: string )=> T |
| remove	| 移除元素 | ( key: string )=> void |
| has	| 判断是否存在元素 |( key: string )=> boolean |
| clear	| 清空Map	 | ()=> void |
| setAll	| 添加并生成一个新的 Map 对象 | (newMap: (readonly [any, any])[])=> void |
| reset	| 重置为默认值	 | ()=> void |

================================================
FILE: packages/useMap/index.ts
================================================
import { ref, Ref, markRaw } from 'vue';


type MapValue = readonly (readonly [any, any])[]

interface Actions<T>{
    set: ( key: string, value: T)=> void,
    get: ( key: string )=> T,
    remove: ( key: string )=> void,
    has: ( key: string )=> boolean,
    clear: ()=> void,
    setAll: (newMap: MapValue)=> void;
    reset: ()=> void,
}

function useMap <T = any>(initialValue?:MapValue) : [
    state: Ref<Map<any,any>>,
    actions: Actions<T>
]

function useMap<T = any>(initialValue?:MapValue){
    
    const initialMap = initialValue ? new Map(initialValue) :  new Map();
    const state = ref(initialMap) as Ref<Map<any,any>>;

    const actions:Actions<T> = {
        set:( key: any, value: T )=>{
            state.value.set(key,value);
        },
        get:( key: any )=>{
            return state.value.get(key);
        },
        remove: ( key: any )=>{
            state.value.delete( key );
        },
        has: ( key: any )=> state.value.has(key),
        clear: ()=> state.value.clear(),
        setAll: ( newMap: MapValue )=>{
            state.value = new Map(newMap);    
        },
        reset: ()=> state.value = initialMap
    };

    return [ state, markRaw(actions) ]
};

export default useMap

================================================
FILE: packages/useMediaQuery/index.md
================================================
# useMediaQuery

一个监听 mediaQuery 状态的 Hook。


## 使用Demo

```vue
<template>
  <div class="hello">
    <div>
      <p> value:{{ state }}</p>
    </div>
  </div>
</template>

<script lang="ts">
import { watch } from 'vue'
import { useMediaQuery } from "v3hooks";

export default {
  
  setup() {
    const state = useMediaQuery('(min-width: 480px)');

    watch(
      state,
      (value)=>{
        if(!value) console.log('已经是480px以下了')
      }
    )
    
    // useVirtualList测试
    return {
      state
    };
  },
};
</script>
```

useMediaQuery接受一个MediaQuery条件, 导出一个state boolean值来判断是否满足MediaQuery条件

## Api
```
const useMediaQuery: (query: string) => vue.Ref<boolean>;
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| query |  MediaQuery条件	| string | - |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state	 | 否满足MediaQuery条件	 | boolean |

================================================
FILE: packages/useMediaQuery/index.ts
================================================
import { ref, onUnmounted } from 'vue';

const useMediaQuery = (query:string)=>{
    const mediaQuery = window.matchMedia(query);
    const stata = ref<boolean>( mediaQuery.matches );
    
    const handleChange = ( event: MediaQueryListEvent )=> stata.value = event.matches;

    mediaQuery.addEventListener('change',handleChange)

    onUnmounted(()=>{
        mediaQuery.removeEventListener('change',handleChange)
    });

    return stata
};

export default useMediaQuery

================================================
FILE: packages/useNetwork/index.md
================================================
# useNetwork

一个用来获取网络状态的 Hook 。

## 使用Demo

```vue
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <p> 网络状态:{{ state }}</p>
  </div>
</template>

<script lang="ts">
import { useNetwork } from "v3hooks";

export default {
  
  setup() {
    // 获取query中的a
    const state = useNetwork();

    // useVirtualList测试
    return {
      state
    };
  },
};
</script>
```

useNetwork返回网络状态信息


## Api
```
const useNetwork: () => {
    since?: number | Date,
    online?: boolean,
    rtt?: number,
    type?: string,
    downlink?: number,
    saveData?: boolean,
    downlinkMax?: number,
    effectiveType?: string,
};
```
### Result

| 属性 | 描述                                         | 类型                 |
|----------|--------------------------------------|----------------------|
| online  | 网络是否为在线 | `boolean` |
| since  | 在线与不在线最后改变时间 | `Date` |
| rtt  | 当前连接下评估的往返时延 | `number` |
| type  | 设备使用与所述网络进行通信的连接的类型 | `bluetooth` \| `cellular` \| `ethernet` \| `none` \| `wifi` \| `wimax` \| `other` \| `unknown` |
| downlink  | 有效带宽估算(单位:兆比特/秒) | `number` |
| downlinkMax  | 最大下行速度(单位:兆比特/秒) | `number` |
| saveData  | 用户代理是否设置了减少数据使用的选项 | `boolean`  |
| effectiveType  | 网络连接的类型 | `slow-2g` \| `2g` \| `3g` \| `4g`  |


================================================
FILE: packages/useNetwork/index.ts
================================================
import { reactive, onMounted, onUnmounted } from "vue";


const getConnection = ()=> {
    const nav = navigator as any;
    if (typeof nav !== 'object') return null;
    return nav.connection || nav.mozConnection || nav.webkitConnection;
}


export interface NetworkState {
    since?: number | Date;
    online?: boolean;
    rtt?: number;
    type?: string;
    downlink?: number;
    saveData?: boolean;
    downlinkMax?: number;
    effectiveType?: string;
  }
  

const handlerSetConnection = ()=>{
    const connection = getConnection();
    return {
        rtt: connection.rtt,
        type: connection.type,
        saveData: connection.saveData,
        downlink: connection.downlink,
        downlinkMax: connection.downlinkMax,
        effectiveType: connection.effectiveType,
    } as NetworkState;
};

const useNetwork = ()=>{
    const state = reactive<NetworkState>({
        online: navigator.onLine,
        since: +new Date(),
        ...handlerSetConnection()
    });

    const onOnline = ()=>{
        state.online = true;
        state.since = +new Date();
    };

    const onOffline = ()=>{
        state.online = false;
        state.since = +new Date();
    };

    const onConnectionChange = <T extends keyof NetworkState>() => {
        const connectionData = handlerSetConnection();
        Object.keys(connectionData).forEach((key)=>{
            let propertyKey = key as T;
            state[propertyKey] = connectionData[propertyKey];
        })
    };

    onMounted(()=>{
        window.addEventListener('online', onOnline);
        window.addEventListener('offline', onOffline);
        getConnection()?.addEventListener('change', onConnectionChange);
    })

    onUnmounted(()=>{
        window.removeEventListener('online',onOnline);
        window.removeEventListener('offline',onOffline);
        getConnection()?.removeEventListener('change', onConnectionChange);
    })
    
    return state
}

export default useNetwork

================================================
FILE: packages/useQRCode/index.md
================================================
# useQRCode

一个用来生成二维码的 Hook 。

## 使用Demo

```vue
<template>
  <div class="hello">
    <div> 二维码:</div>
    <img :src="state" alt="">
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useQRCode } from "../../../dist/index.js";

const logo = require('../../assets/logo.png')

export default {
  
  setup() {
    const text = ref<string>('https://www.baidu.com/')
    // let text2 = 'https://www.baidu.com/';

    const state = useQRCode(text,{
      logo: logo.default,
      colorDark: '#000000'
    });

    setTimeout(()=>{
      text.value = 'https://www.acfun.cn/'
    },2000)

    return {
      state
    };
  },
};
</script>
```

usQRCide接受一个静态url,也可以是一个被Ref包裹的url,当Ref值发生变化时,二维码会跟随内容进行变化。


## Api
```
type Text = Ref<string> | string;
interface useQRCodeOptions {
    onRenderingStart?: (qrCodeOptions: any) => void;
    onRenderingEnd?: (qrCodeOptions: any, dataURL: string) => void;
    [key: string]: any;
}
const useQRCode: (text: Text, options?: useQRCodeOptions | undefined) => Ref<string | undefined>;
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| text |  需要生成二维码的url或text	| `string` \| `Ref<string>` | - |
| options |  二维码配置项	| Options | - |

### Options

Options配置项可以参考<a href="https://github.com/ushelp/EasyQRCodeJS#qrcode-api">EasyQRCodeJS</a>useQRCode的底层是使用了EasyQRCodeJS来作为二维码的实现。

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state	 | base64格式的二维码图片	 | string |


================================================
FILE: packages/useQRCode/index.ts
================================================
import qrcode from "easyqrcodejs";
import { ref, Ref, watch, isRef } from "vue";

type Text = Ref<string> | string;

interface useQRCodeOptions{
    onRenderingStart?: (qrCodeOptions:any)=> void;
    onRenderingEnd?: (qrCodeOptions:any,dataURL:string)=>void;
    [key:string]: any;
}

const defaultUseQRCodeOptions = {
    onRenderingEnd:()=>{}
};

const useQRCode = (
    text:Text,
    options?: useQRCodeOptions
)=>{
    const state = ref<string>();

    const {
        onRenderingEnd,
        ...otherOptions
    } = {...defaultUseQRCodeOptions,...options};

    const Qrcode = new qrcode(document.createElement('div'),{
        text: isRef(text) ? text.value : text,
        ...otherOptions,
        onRenderingEnd(qrCodeOptions:any, dataURL:string){
            state.value = dataURL;
            onRenderingEnd(qrCodeOptions, dataURL)
        }
    })
    if(isRef(text)){
        watch(text,()=>{
            Qrcode.makeCode(text.value)
        })
    }

    return state
}


export default useQRCode

================================================
FILE: packages/useRequest/__tests__/index.test.ts
================================================
import { shallowMount } from '@vue/test-utils';
import { defineComponent, ref } from 'vue';
import MockDate from 'mockdate';
import { sleep } from '../../utils/testingHelpers';
import useRequest from '../index';
import { Service, BaseOptions, Result } from '../types'

describe('useRequest', () => {
  const originalError = console.error;
  const originalWarn = console.warn;
  const requestDelayTime = 500;

  beforeAll(() => {
    console.error = (...args) => {
      if (/Vue warn/.test(args[0])) {
        return;
      }
      originalError.call(console, ...args);
    };
    console.warn = (...args) => {
      if (/Vue warn/.test(args[0])) {
        return;
      }
      originalWarn.call(console, ...args);
    };
  });
  afterAll(() => {
    console.error = originalError;
    console.warn = originalWarn;
  });

  const request: Service = (req:any) =>
    new Promise((resolve, reject) => {
      setTimeout(() => {
        if (req === 0) {
          reject(new Error('fail'));
        } else {
          resolve('success');
        }
      }, requestDelayTime);
    });

  it('should be defined', () => {
    expect(useRequest).toBeDefined();
  });

  const setUp = (service:Service, options?:BaseOptions) => useRequest(service, options);

  let hook:Result<any>;
  it('useRequest should auto run', async () => {
    let successValue:string = '';
    const successCallback = (text:string) => {
      successValue = text;
    };
    const errorCallback = jest.fn();

    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            onSuccess: successCallback,
            onError: errorCallback,
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(true);
    await sleep(requestDelayTime);
    expect(hook.loading.value).toEqual(false);
    expect(hook.data.value).toEqual('success');
    expect(successValue).toEqual('success');
    expect(errorCallback).not.toHaveBeenCalled();

    hook.run(0);
    expect(hook.loading.value).toEqual(true);
    await sleep(requestDelayTime);
    expect(hook.error.value).toEqual(new Error('fail'));
    expect(hook.loading.value).toEqual(false);
    expect(errorCallback).toHaveBeenCalled();

    hook.run(1);
    await sleep(requestDelayTime);
    expect(hook.data.value).toEqual('success');
    expect(hook.loading.value).toEqual(false);
    expect(errorCallback).toHaveBeenCalled();
    wrapper.unmount();
  });

  it('useRequest should be manually triggered', async () => {
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            manual: true,
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(false);
    hook.run(1);
    expect(hook.loading.value).toEqual(true);
    await sleep(requestDelayTime);
    expect(hook.loading.value).toEqual(false);
    expect(hook.data.value).toEqual('success');
    wrapper.unmount();
  });

  it('useRequest polling should work', async () => {
    const callback = jest.fn();
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(
            () => {
              callback();
              return request();
            },
            {
              pollingInterval: 100,
              pollingWhenHidden: true,
            },
          );
        },
      }),
    );
    expect(hook.loading.value).toEqual(true);
    expect(callback).toHaveBeenCalled();
    expect(callback).toHaveBeenCalledTimes(1);
    await sleep(100);
    expect(callback).toHaveBeenCalledTimes(2);
    await sleep(100);
    expect(callback).toHaveBeenCalledTimes(3);
    hook.cancel();
    expect(callback).toHaveBeenCalledTimes(3);

    hook.run();
    expect(callback).toHaveBeenCalledTimes(4);
    wrapper.unmount();
  });

  it('useRequest debounceInterval should work', async () => {
    const callback = jest.fn();

    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(
            () => {
              callback();
              return request();
            },
            {
              manual: true,
              debounceInterval: 100,
            },
          );
        },
      }),
    );

    hook.run(1);
    hook.run(2);
    hook.run(3);
    hook.run(4);
    await sleep(100);
    expect(callback).toHaveBeenCalled();
    expect(callback).toHaveBeenCalledTimes(1);

    hook.run(1);
    hook.run(2);
    hook.run(3);
    hook.run(4);
    await sleep(100);
    expect(callback).toHaveBeenCalled();
    expect(callback).toHaveBeenCalledTimes(2);

    wrapper.unmount();
  });

  it('useRequest throttleInterval should work', async () => {
    const callback = jest.fn();

    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(
            () => {
              callback();
              return request();
            },
            {
              manual: true,
              throttleInterval: 100,
            },
          );
        },
      }),
    );
    await sleep(100);
    hook.run(1);
    hook.run(2);
    await sleep(100);
    hook.run(3);
    hook.run(4);

    expect(callback).toHaveBeenCalledTimes(2);

    wrapper.unmount();
  });

  it('useRequest mutate should work', async () => {
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request);
        },
      }),
    );
    await sleep(requestDelayTime);
    expect(hook.data.value).toEqual('success');
    hook.mutate('hello');
    expect(hook.data.value).toEqual('hello');
    wrapper.unmount();
  });

  it('useRequest loadingDelay should work', async () => {
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            loadingDelay: 2000,
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(false);
    await sleep(1000);
    expect(hook.loading.value).toEqual(false);
    wrapper.unmount();
  });

  it('useRequest loadingDelay should delay', async () => {
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            loadingDelay: 300,
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(false);
    await sleep(requestDelayTime);
    expect(hook.loading.value).toEqual(false);
    wrapper.unmount();
  });

  it('useRequest refreshDeps should work', async () => {
    const refreshValue = ref(1);
    const callback = jest.fn();
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(
            () => {
              callback();
              return request();
            },
            {
              refreshDeps: [refreshValue],
            },
          );
        },
      }),
    );
    expect(hook.loading.value).toEqual(true);
    expect(callback).toHaveBeenCalledTimes(1);
    await sleep(requestDelayTime);
    expect(hook.loading.value).toEqual(false);

    refreshValue.value = 2;
    await sleep(0);
    expect(hook.loading.value).toEqual(true);
    expect(callback).toHaveBeenCalledTimes(2);
    await sleep(requestDelayTime);
    expect(hook.loading.value).toEqual(false);
    wrapper.unmount();
  });

  it('useRequest ready should work', async () => {
    const ready = ref(false);
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            ready,
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(false);
    ready.value = true;
    await sleep(0);
    expect(hook.loading.value).toEqual(true);
    wrapper.unmount();
  });

  it('useRequest initialData should work', async () => {
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            initialData: 'hello',
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(true);
    expect(hook.data.value).toEqual('hello');
    await sleep(requestDelayTime);
    expect(hook.data.value).toEqual('success');
    wrapper.unmount();
  });

  it('useRequest formatResult should work', async () => {
    let formarParams = '';
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            formatResult: p => {
              formarParams = p;
              return 'hello';
            },
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(true);
    await sleep(requestDelayTime);
    expect(hook.loading.value).toEqual(false);
    expect(formarParams).toEqual('success');
    expect(hook.data.value).toEqual('hello');
    wrapper.unmount();
  });

  it('useRequest defaultParams should work', async () => {
    const defaultParamsRequest = (...req:any[]) =>
      new Promise(resolve => {
        setTimeout(() => {
          resolve(req);
        }, 500);
      });
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(defaultParamsRequest, {
            defaultParams: [1, 2, 3],
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(true);
    await sleep(requestDelayTime);
    expect(hook.data.value).toEqual([1, 2, 3]);
    wrapper.unmount();
  });

  it('useRequest throwOnError to be false should work', async () => {
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            manual: true,
          });
        },
      }),
    );
    hook.run(0);
    await sleep(requestDelayTime);
    expect(hook.data.value).toEqual(undefined);
    expect(hook.error.value).toEqual(new Error('fail'));
    wrapper.unmount();
  });

  it('useRequest cacheKey should work', async () => {
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            cacheKey: 'testCacheKey',
          });
        },
      }),
    );
    await sleep(requestDelayTime);
    expect(hook.loading.value).toEqual(false);
    expect(hook.data.value).toEqual('success');
    wrapper.unmount();

    let hook2:any;
    const wrapper2 = shallowMount(
      defineComponent({
        setup() {
          hook2 = setUp(request, {
            cacheKey: 'testCacheKey',
          });
        },
      }),
    );
    expect(hook2.data.value).toEqual('success');
    await sleep(requestDelayTime);
    expect(hook2.loading.value).toEqual(false);
    wrapper2.unmount();
  });

  it('useRequest staleTime should work', async () => {
    MockDate.set(0);
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            cacheKey: 'testStaleTime',
            staleTime: 3000,
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(true);
    await sleep(requestDelayTime);
    expect(hook.loading.value).toEqual(false);
    expect(hook.data.value).toEqual('success');
    wrapper.unmount();
    MockDate.set(1000);

    let hook2:any;
    const wrapper2 = shallowMount(
      defineComponent({
        setup() {
          hook2 = setUp(request, {
            cacheKey: 'testStaleTime',
            staleTime: 3000,
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(false);
    expect(hook2.data.value).toEqual('success');
    wrapper2.unmount();

    MockDate.set(3001);
    let hook3:any;
    const wrapper3 = shallowMount(
      defineComponent({
        setup() {
          hook3 = setUp(request, {
            cacheKey: 'testStaleTime',
            staleTime: 3000,
          });
        },
      }),
    );
    expect(hook3.data.value).toEqual('success');
    await sleep(requestDelayTime);
    expect(hook3.loading.value).toEqual(false);
    wrapper3.unmount();
  });

  it('useRequest cacheTime should work', async () => {
    MockDate.set(0);
    const wrapper = shallowMount(
      defineComponent({
        setup() {
          hook = setUp(request, {
            cacheKey: 'testCacheTime',
            cacheTime: 2000,
          });
        },
      }),
    );
    expect(hook.loading.value).toEqual(true);
    await sleep(requestDelayTime);
    expect(hook.loading.value).toEqual(false);
    expect(hook.data.value).toEqual('success');
    wrapper.unmount();
    MockDate.set(500);
    await sleep(500);

    let hook2:any;
    const wrapper2 = shallowMount(
      defineComponent({
        setup() {
          hook2 = setUp(request, {
            cacheKey: 'testCacheTime',
            cacheTime: 2000,
          });
        },
      }),
    );
    expect(hook2.data.value).toEqual('success');
    wrapper2.unmount();
    MockDate.set(3001);
    await sleep(3001);

    let hook3:any;
    const wrapper3 = shallowMount(
      defineComponent({
        setup() {
          hook3 = setUp(request, {
            cacheKey: 'testCacheTime',
            cacheTime: 2000,
          });
        },
      }),
    );
    expect(hook3.data.value).toEqual(undefined);
    await sleep(requestDelayTime);
    expect(hook3.loading.value).toEqual(false);
    expect(hook3.data.value).toEqual('success');
    wrapper3.unmount();
  });
});


================================================
FILE: packages/useRequest/index.md
================================================
# useRequest

专注于管理异步请求的Hook,加速你的日常开发

* 自动请求/手动请求
* SWR(stale-while-revalidate)
* 缓存/预加载
* 屏幕聚焦重新请求
* 轮询
* 防抖
* 节流
* 依赖请求
* 突变
* loading delay

## 请求方式
如果service是object,useRequest会使用 Fetch 来发送网络请求
```
const { data } = useRequest(
    {
    url: 'http://xxx.xx.com/api/userInfo',
    method: 'POST'
    }
);
```
如果service是async函数,useRequest会调用此函数来发送网络请求
```
const { data } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
);
```

## 使用
### 默认请求
```
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
);
watchEffect(()=>{
    console.log( data?.value );
})
```
在这个例子中, useRequest 接收了一个异步函数 ,在组件初次加载时, 自动触发该函数执行。同时 useRequest 会自动管理异步请求的 loading , data 状态。你可以通过data和loading来实现你的需求


因为返回的data为响应式,js中获取data.value需要在watchEffect中使用,而在template中使用是不需要的。

### 手动触发

```
const { data, run, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        manual: true
    }
);
```
通过设置 options.manual = true , 则需要手动调用 run 时才会触发执行异步函数。

### 轮询
```
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        pollingInterval: 1000
    }
);
```

通过设置 options.pollingInterval ,进入轮询模式,定时触发函数执行。

### 依赖请求
```
import { onMounted, ref } from 'vue'

const isReady = ref(false);
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        ready: isReady
    }
);
onMounted(() => {
    isReady.value = true;
})
```
只有当 options.ready 变为 true 时, 才会发起请求,基于该特性可以实现串行请求,依赖请求等。

### 防抖

```
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        debounceInterval: 1000
    }
);
```
通过设置 options.debounceInterval ,则进入防抖模式。此时如果频繁触发 run ,则会以防抖策略进行请求。


### 节流

```
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        throttleInterval: 1000
    }
);
```
通过设置 options.throttleInterval ,则进入节流模式。此时如果频繁触发 run ,则会以节流策略进行请求。



### 缓存 & SWR

```
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        caccacheKey: 'mock1'
    }
);
```
如果设置了 options.cacheKey , useRequest 会将当前请求结束数据缓存起来。下次组件初始化时,如果有缓存数据,我们会优先返回缓存数据,然后在背后发送新请求,也就是 SWR 的能力。你可以通过 cacheTime 设置缓存数据回收时间,也可以通过 staleTime 设置数据保持新鲜时间。


### 预加载

```
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        caccacheKey: 'mock1'
    }
);
```
同一个 cacheKey 的请求,是全局共享的,也就是你可以提前请求数据。后续请求会提前返回之前预加载的数据,利用该特性,可以很方便的实现预加载。


### 屏幕聚焦重新请求

```
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        refreshOnWindowFocus: true,
        focusTimespan: 2000
    }
);
```

如果你设置了 options.refreshOnWindowFocus = true ,则在浏览器窗口 refocus 和 revisible 时,会重新发起请求。你可以通过设置 options.focusTimespan 来设置请求间隔,默认无 。

### 突变

```
const { data, mutate } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    }
);

<button type="button" @click={() => mutate({code:'1',msg:'test'})}>
    突变测试
</button>
```
你可以通过 mutate ,直接修改 data.

### Loading Delay

```
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        loadingDelay: 300
    }
);
```
通过设置 options.loadingDelay ,可以延迟 loading 变成 true 的时间,有效防止闪烁。

### refreshDeps

开发中会经常碰到这个需求,当某些 state 变化时,我们需要重新执行异步请求,使用useRequest将很方便的实现此功能。

```
import { onMounted, ref } from 'vue'

const random = ref(1);
const { data, loading } = useRequest(
    () => {
        return axios.post(
            `http://xxx.xx.com/api/userInfo`
        );
    },
    {
        refreshDeps: [ random ]
    }
);

setInterval(()=>{
    random.value = Math.random()
},1000)
```

当例子中 random 发生变化时,会重新执行 service。

## Api

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| data | service 返回的数据,默认为 undefined | undefined / any |
| error | service 抛出的异常,默认为 undefined | undefined / Error |
| loading | service 是否正在执行 | boolean |
| run | 手动触发 service 执行,参数会传递给 service | (...args: any[]) => void |
| refresh | 使用上一次的 params,重新执行 service| () => void |
| cancel | 如果有轮询,停止 | () => void |
| mutate | 突变直接修改 data	 | (newData) => void |
| loading | service 是否正在执行 | boolean |

### Params
所有的 Options 均是可选的。

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| manual | 是否需要手动执行 | boolean | false |
| defaultParams | 如果 manual=false ,自动执行 run 的时候,默认带上的参数 | any[] | - |
| refreshDeps | 在 manual = false 时,refreshDeps 变化,会触发 service 重新执行 | any[] | [] |
| loadingDelay | 设置显示 loading 的延迟时间,避免闪烁 | number | - |
| pollingInterval | 轮询间隔,单位为毫秒。设置后,将进入轮询模式,定时触发 run | number | - |
| pollingWhenHidden | 在页面隐藏时,是否继续轮询。默认为 true,即不会停止轮询 | boolean | true |
| refreshOnWindowFocus | 屏幕重新聚焦时间间隔,在当前时间间隔内,不会重新发起请求 | number | - |
| focusTimespan | 在页面隐藏时,是否继续轮询。默认为 true,即不会停止轮询 | boolean | true |
| debounceInterval | 防抖间隔, 单位为毫秒,设置后,请求进入防抖模式。 | number | - |
| throttleInterval | 节流间隔, 单位为毫秒,设置后,请求进入节流模式。 | number | - |
| ready | 只有当 ready 为 true 时,才会发起请求 | boolean | - |
| cacheKey | * 请求唯一标识。如果设置了 cacheKey,我们会启用缓存机制 * 我们会缓存每次请求的 data , error , params , loading在缓存机制下 * 同样的请求我们会先返回缓存中的数据,同时会在背后发送新的请求,待新数据返回后,重新触发数据更新 | string | - |
| cacheTime | 设置缓存数据回收时间。默认缓存数据 5 分钟后回收,需要配和 cacheKey 使用 | number | 300000 |
| staleTime | 缓存数据保持新鲜时间。在该时间间隔内,认为数据是新鲜的,不会重新发请求,需要配和 cacheKey 使用 | number | 0 |


## 致敬

因为本人之前React中一直使用 umi ,在 umi 中使用了useRequest, Api使用非常的顺手。到了vue3中没有此hook,所以实现一份Vue的简版 useRequest来供Vue3项目使用。

React Ahooks [原版链接](https://ahooks.js.org/zh-CN/hooks/async)


================================================
FILE: packages/useRequest/index.ts
================================================
import { 
    BaseOptions,
    Result,
    Run,
    Service,
    FetchService,
    Cancel
} from './types';
import { 
    shallowRef, 
    ref,
    watch
} from 'vue';
import { 
    debounce, 
    throttle, 
    throttleAndDeBounce 
} from '../utils';
import { loadingDelayAsync, clearLoadingDelayTimer } from './src/loadingDelay';
import { serveiceProxy, argsSymbolKey } from './src/service';
import Polling from './src/polling';
import { visibility } from './src/visibility';
import { handleResCache } from './src/cache'
import memoryCache from '../utils/memoryCache'

// service请求参数缓存
const reqCache = new memoryCache();

// 请求参数缓存
const resCache = new memoryCache();


// 默认参数
const defaultOptions = {
    manual: false,
    initialData: undefined,
    onSuccess: ()=>{},
    onError: ()=>{},
    formatResult: (data:any)=> data,
    defaultParams: [], 
    pollingInterval: 0,
    pollingWhenHidden: true,
    ready: undefined,
    debounceInterval: undefined,
    throttleInterval: undefined,
    refreshOnWindowFocus: false,
    focusTimespan: undefined,
    loadingDelay: 0,
    refreshDeps: [],
    cacheTime: 300000,
    staleTime: 0,
};


const useRequest = <T>(service: Service | FetchService,options?:BaseOptions )=>{
     
    const { 
        manual,
        initialData,
        onSuccess,
        onError,
        formatResult,
        defaultParams,
        pollingInterval,
        pollingWhenHidden,
        ready,
        debounceInterval,
        throttleInterval,
        refreshOnWindowFocus,
        focusTimespan,
        loadingDelay,
        refreshDeps,
        cacheKey,
        cacheTime,
        staleTime
    } = { ...defaultOptions, ...options };

    const data = shallowRef<T | undefined>(initialData);
    const error = ref<Error | undefined >(undefined);
    const loading = ref(false);
    const latestTime = ref<number>(0);

    // 取消轮询
    const cancel:Cancel = () => Polling.cancel();

    // 执行轮询
    const pollingRun = ()=>{
        if( pollingInterval < 4 || Polling.isActive){
            return
        }
        Polling.run( run, pollingInterval, pollingWhenHidden );
    };

    // 执行网络请求
    let run:Run = (...args: any[])=>{
        
        // 请求开始时间
        const reqTime = +new Date();

        // 判断开启缓存 && 有缓存,先返回缓存
        // 缓存修改并不会阻止顺序执行,service请求会继续发出
        // 也就是所谓SWR能力
        if( cacheKey && resCache.has(cacheKey)){
            data.value = resCache.get(cacheKey)
            
            if( latestTime.value + staleTime > reqTime ){
                return
            }
        }else if(loadingDelay > 0){
            loadingDelayAsync( loadingDelay ).then(()=> loading.value = true)
        }else {
            loading.value = true;
        }

        
        // 更新最新一次请求开始时间
        latestTime.value = reqTime;

        serveiceProxy( service, args, reqCache ).then(( responseData )=>{
            clearLoadingDelayTimer();

            responseData = formatResult(responseData)
            data.value = responseData as any;

            loading.value = false;

            onSuccess(responseData,args)

            // 处理缓存
            handleResCache(
                responseData,
                resCache,
                cacheKey,
                cacheTime
            )
        }).catch(( e:Error )=>{
            loading.value = false;

            error.value = e;

            onError(error.value,args)
        });
        // 非激活状态执行轮询
        pollingRun()
    };

    const refresh = ()=>{
        const args = reqCache.get(argsSymbolKey) || [];
        run(...args)
    };


    // 是否自动执行
    if( !manual && ready === undefined){

        // 是否携带默认参数
        defaultParams.length > 0 ? run(...defaultParams) : run()

        // 是否执行轮询
        pollingRun()
    }


    //监听依赖请求是否执行
    watch(()=> ready,(curr)=>{
        if( curr?.value === true){
            refresh()
        }
    },{ deep: true })

    //多个监听依赖请求是否执行
    watch(refreshDeps,()=> refresh() , { deep: true });

    
    // 防抖+节流
    if(
        debounceInterval !== undefined 
        && throttleInterval !== undefined
        && typeof(debounceInterval) === 'number'
        && typeof(throttleInterval) === 'number'
    ){
        run = throttleAndDeBounce(run,debounceInterval,throttleInterval);
    }else{
        // 防抖
        if( 
            debounceInterval !== undefined 
            && typeof(debounceInterval) === 'number'
        ){
            run = debounce(run,debounceInterval)
        }

        // 节流
        if(
            throttleInterval !== undefined 
            && typeof(throttleInterval) === 'number'
        ){
            run = throttle(run,throttleInterval)
        }
    }

    // 屏幕聚焦重新请求
    if(refreshOnWindowFocus === true){
        visibility( refresh, focusTimespan );
    }


    // 突变改变data值
    const mutate = (state:any)=>{
        data.value = state;
    };


    // 返回值
    const res: Result<T> = {
        data,
        error,
        run,
        refresh,
        loading,
        cancel,
        mutate
    };

    return res
};



export default useRequest

================================================
FILE: packages/useRequest/src/cache.ts
================================================

import memoryCache from '../../utils/memoryCache'

const handleResCache = (
    data: any,
    resCache: memoryCache,
    cacheKey?: string,
    cacheTime?: number,
)=>{
    if( cacheKey ){
        if( resCache.has(cacheKey)){
            resCache.put(cacheKey,data)
            return
        }
        resCache.put(cacheKey,data,cacheTime)
    }
} 

export {
    handleResCache
}

================================================
FILE: packages/useRequest/src/fetch.ts
================================================
export interface FetchParams extends RequestInit{
    url: RequestInfo,
}

const defaultParams = {
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, *cors, same-origin
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *same-origin, omit
    headers: {
      'Content-Type': 'application/json'
    },
    redirect: 'follow', // manual, *follow, error
    referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
};

/**
 * 使用fetch发起请求
 * @param Params 
 * @returns FetchData
 */

function fetchData( p:FetchParams ){
    const params = { ...defaultParams, ...p } as FetchParams;
    return fetch( params.url, params);
}


export default fetchData

================================================
FILE: packages/useRequest/src/loadingDelay.ts
================================================
/**
 * loading的延迟计算
 * @param startTime 
 * @param loadingDelay 
 * @param args 
 * @returns 
 */
let loadingDelayTimer:NodeJS.Timeout | null = null;
export const loadingDelayAsync = ( loadingDelay:number)=>{
    clearLoadingDelayTimer();
    return new Promise((resolve)=>{
        loadingDelayTimer = setTimeout(()=> resolve(loadingDelayTimer),Math.max( loadingDelay, 0 ) );
    })
};

/**
 * 取消loading延迟计算Timer
 */
export const clearLoadingDelayTimer = ()=>{
    if( loadingDelayTimer ){
        clearTimeout(loadingDelayTimer)
    }
} 

================================================
FILE: packages/useRequest/src/polling.ts
================================================
import { Run } from '../types'

/**
 * 执行轮询
 */
class Polling{
    isActive: boolean; // 是否是激活状态
    private pollingInterval: number;
    private pollingWhenHidden: boolean;
    constructor() {
        this.isActive = false;
        this.pollingInterval = 0;
        this.pollingWhenHidden = true;
    }

    run(
        run:Run,
        pollingInterval: number, 
        pollingWhenHidden: boolean
    ){
        this.isActive = true;
        this.pollingInterval = pollingInterval;
        this.pollingWhenHidden = pollingWhenHidden;
        this.task(run);
    }

    cancel(){
        this.isActive = false
    }

    private task(
        run:Run
    ){
        setTimeout(()=>{
            if( !this.isActive ) return
            if( this.pollingWhenHidden ){
                run();
            }else{
                if(!document.hidden){
                    run();
                }
            }
            this.task(run);
        },this.pollingInterval)
    }
}


export default new Polling()


================================================
FILE: packages/useRequest/src/service.ts
================================================

import fetchData from './fetch'
import MemoryCache from '../../utils/memoryCache'

const argsSymbolKey = 'argsKey';

const serveiceProxy = async (service: any, args: any[], reqParamsCache: MemoryCache)=>{
    try{
        if( args.length > 0 ){
            reqParamsCache.put(argsSymbolKey,args);
        }
        if( Object.prototype.toString.call(service) === "[object Function]"){
            return await service(...args);
        }
        if( Object.prototype.toString.call(service) === "[object Object]" ){
            const response = await fetchData(service);
            return response.json()
        }
    }catch(error){
        return Promise.reject(error)
    }
}


export { serveiceProxy, argsSymbolKey }

================================================
FILE: packages/useRequest/src/visibility.ts
================================================
import { Run } from '../types'
import { throttle } from '../../utils'


/**
 * 监听屏幕是否聚焦
 * @param run 
 * @param focusTimespan 
 */
const visibility = (run:Run ,focusTimespan?: undefined | number)=>{
    let handler = ()=> {
        if (!document.hidden) {
            run()
        }
    };
    if(focusTimespan !== undefined && typeof(focusTimespan) === 'number'){
        handler = throttle(handler,focusTimespan);
    }
    document.addEventListener('visibilitychange',handler );
}

export { visibility }

================================================
FILE: packages/useRequest/types.d.ts
================================================
import { Ref } from 'vue';
import { FetchParams } from './src/fetch'

// Service请求实例
export type Service = (...args: any[]) => Promise<any>;

export interface FetchService extends FetchParams{};


// 请求参数
export interface BaseOptions {
    manual?: boolean; // 是否需要手动触发
    initialData?: any; //默认的 data
    formatResult?: (response: any) => any; // 格式化请求结果	
    onSuccess?: (data: any, params: any[]) => void; // 成功回调
    onError?: (error: Error, params: any[]) => void; // 失败回调
    defaultParams?: any[]; // 如果 manual=false ,自动执行 run 的时候,默认带上的参数
    pollingInterval?: number; // 轮询请求时间
    pollingWhenHidden?: boolean; // 在屏幕不可见时,暂时暂停定时任务。
    ready?: undefined | Ref<boolean>; // 依赖请求
    debounceInterval?: undefined | number; // 防抖
    throttleInterval?: undefined | number; // 节流
    refreshOnWindowFocus?: boolean, // 屏幕聚焦重新请求
    focusTimespan?: undefined | number, // 屏幕聚焦重新间隔
    loadingDelay?: number, //loading延迟时间
    refreshDeps?: any[], //依赖刷新监听
    cacheKey?: string, //缓存key
    cacheTime?: number, //缓存数据回收时间
    staleTime?: number //缓存数据新鲜时间
};

// 执行请求
export type Run = (...args: any[])=> void;

// 重新执行
export type Refresh = ()=> void;

//取消请求
export type Cancel = (()=> void);

// 突变
export type Mutate = (state:any)=> void;


// hook返回值
export interface Result<T> {
    data: Ref<T | undefined>; // 是否需要手动触发
    loading: Ref<boolean>;
    run: Run;
    refresh: Refresh;
    cancel: Cancel;
    mutate: Mutate;
    error: Ref<Error | undefined>;
};

================================================
FILE: packages/useRouteQuery/index.md
================================================
# useRouteQuery

一个获取vueRouter query的 Hook 。

请确保项目已安装Vue Router v4.x版本及以上,否则将不能使用此Hook.


## 使用Demo

```vue
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <p> value:{{ state }}</p>
    <button @click="handlerUpdateState">修改query</button>
  </div>
</template>

<script lang="ts">
import { useRouteQuery } from "v3hooks";

export default {
  
  setup() {
    // 获取query中的a
    const state = useRouteQuery('a');

    const handlerUpdateState = ()=>{
      state.value = String( Math.random() );
    };

    // useVirtualList测试
    return {
      state,
      handlerUpdateState
    };
  },
};
</script>
```

useRouteQuery接受一个key是query中的参数key。 修改返回的state可直接修改url中的query。

## Api
```
const state = useRouteQuery(
  key: string,
);

```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| key | query中的键名 | string	 | - |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state | query值也可以直接被修改,这样将同步修改query | Ref<any> |



================================================
FILE: packages/useRouteQuery/index.ts
================================================
import { computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';


const useRouteQuery = ( key: string)=>{
  
    const route = useRoute();
    const router = useRouter();
    return computed({
        get:()=>{
            return route.query[key];
        },
        set: val => {
            router.replace({ query: { ...route.query ,[key]: val } });
        }
    })
};

export default useRouteQuery

================================================
FILE: packages/useSessionStorage/index.md
================================================
# useSessionStorage

一个可以将状态持久化存储在 sessionStorage 中的 Hook 。


## 使用Demo

```vue
<template>
  <div class="hello" style="display:flex;align-items:flex-start;">
    <div style="width: 60vw">
      <p> value:{{ state }}</p>
    </div>
    <div style="width:39vw">
      <button @click="handleUpdate">更新storage</button>
      <button @click="handleDelete">删除storage</button>
    </div>
  </div>
</template>

<script lang="ts">
import { useSessionStorage } from "v3hooks";

export default {
  
  setup() {
    const state = useSessionStorage('useSessionStorage',{a:231});

    const handleUpdate = ()=>{
        state.value = { a: Math.random()};
    };

    const handleDelete = ()=>{
        state.value = undefined;
    };

    // useVirtualList测试
    return {
      state,
      handleUpdate,
      handleDelete
    };
  },
};
</script>
```

useSessionStorage接受一个key和一个value,导出一个响应式的state, 用户直接赋值state.value可自动修改本地sessionStorage。

## 注意点
* 不设置value可用于获取本地sessionStorage  例:`useSessionStorage('useSessionStorage')`
* value等于undefined或者null可用于删除本地Storage  例:`state.value = undefined;`


## Api
```
const state = useSessionStorage(
  key: string,
  initialValue?: any,
  options?: Options
);

```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| key | sessionStorage存储键名 | any	 | - |
| initialValue | 初始值 | any	 | {} |
| options | 配置 | Options	 | - |

### Options

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| watch | 是否实时修改sessionStorage | boolean | true |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state | 可以被修改的数据源 | Ref<any> |



================================================
FILE: packages/useSessionStorage/index.ts
================================================
import { ref, Ref, isRef, watch as vueWatch } from 'vue';
import { TypeSerializers, getValueType } from '../utils'
const storage = sessionStorage;
interface Options{
    watch: boolean
}

const defaultOptions = {
    watch: true
}

const useSessionStorage = <T = any>( key: string, initialValue?: T | Ref<T>, options?:Options)=>{

    const { watch } = { ...defaultOptions, ...options };

    const data = ref() as Ref<T | undefined | null>;
    try{
        if( initialValue !== undefined ){
            data.value = isRef( initialValue ) ? initialValue.value : initialValue;
        }else{
            data.value = JSON.parse( storage.getItem(key) || '{}' );
        }
    }catch(error){
        console.log(error,'useLocalStorage初始化失败')
    }

    const type = getValueType(data.value);

    // 判断类型取格式化方法
    let serializer = TypeSerializers[type];


    const setStorage = ()=> storage.setItem( key, serializer.write(data.value) );

    // 状态监听
    if( watch ){
        vueWatch(
            data,
            (newValue)=>{
                if( newValue === undefined || newValue === null ){
                    storage.removeItem(key);
                    return
                }
                setStorage();
            },
            {
                deep:true
            }
        )
    }

    setStorage()

    return data
};

export default useSessionStorage


================================================
FILE: packages/useSet/index.md
================================================
# useSet

一个可以管理 Set 类型状态的 Hook。


## 使用Demo

```vue
<template>
  <div class="hello">
    <div>
      <p> value:{{ state }}</p>
      <button @click="()=> add(Math.random())">add</button>
    </div>
  </div>
</template>

<script lang="ts">
import { useSet, useMap } from "../../../dist/index.js";

export default {
  
  setup() {
    const [state , { add } ] = useSet([1]);
    return {
      state,
      add
    };
  },
};
</script>
```

useSet接受一个 Set 可接受的参数, 并导出以下方法.

## Api
```
interface Actions<T>{
    add: (value: T)=> void,
    remove: (value: T)=> void,
    has: (value: T)=> boolean,
    clear: ()=> void,
    reset: ()=> void,
}

function useSet <T = any>(initialValue?:T[]) : [
    state: Ref<Set<any>>,
    actions: Actions<T>
]
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| initialValue | 可选项,传入默认的 Set 参数	 | T[] | - |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state	 | Set 对象	 | Set |
| add	 | 添加元素	 | (value: T)=> void |
| remove	| 移除元素	 | (value: T)=> void |
| has	| 判断是否存在元素	 | (value: T)=> void |
| clear	| 清空set	 | ()=> void |
| reset	| 重置为默认值	 | ()=> void |

================================================
FILE: packages/useSet/index.ts
================================================
import { ref, Ref, markRaw } from 'vue';

interface Actions<T>{
    add: (value: T)=> void,
    remove: (value: T)=> void,
    has: (value: T)=> boolean,
    clear: ()=> void,
    reset: ()=> void,
}

function useSet <T = any>(initialValue?:T[]) : [
    state: Ref<Set<any>>,
    actions: Actions<T>
]

function useSet<T = any>(initialValue?:T[]){
    
    const initialSet = initialValue ? new Set(initialValue) : new Set();
    const state = ref(initialSet);

    const actions:Actions<T> = {
        add: ( value: T )=>{
            state.value.add(value);
        },
        remove: ( value: T )=>{
            state.value.delete(value);
        },
        has: ( value: T )=> state.value.has(value),
        clear: ()=> state.value.clear(),
        reset: ()=>{
            state.value = new Set();
        }
    };

    return [ state, markRaw(actions) ]
};

export default useSet

================================================
FILE: packages/useTextSelection/index.md
================================================
# useTextSelection

实时获取用户当前选取的文本内容及位置的hook。


## 使用Demo

### 基础用法
```vue
<template>
  <div style="text-align: center">
    <p ref="p"> 可选择区域: 123111111111111aaaaaaaaaaabbbbbbbbbbb eeeeeeeeeeeeeeee</p>
    <p>已选择的值:{{ text }}</p>
    <p>位置信息:rect: {{ rect }}</p>
    <p>left: {{ rect.left }}</p>
  </div>
</template>

<script lang="ts">
import { useTextSelection } from "v3hooks";
export default {
  setup() {
    const { text, rect } = useTextSelection();
    return {
        text,
        rect
    };
  },
};
</script>
```
没有传值默认为document, 页面上可选择的文本都会被计算

### 监听特定区域文本选择
```vue
<template>
  <div style="text-align: center">
    <p ref="p"> 可选择区域: 123111111111111aaaaaaaaaaabbbbbbbbbbb eeeeeeeeeeeeeeee</p>
    <p>已选择的值:{{ text }}</p>
    <p>位置信息:rect: {{ rect }}</p>
    <p>left: {{ rect.left }}</p>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useTextSelection } from "v3hooks";

export default {
  setup() {
    const p = ref();
    const { text, rect } = useTextSelection(p);

    return {
        text,
        p,
        rect
    };
  },
};
</script>
```
传值为Ref的P标签,只会监听特定区域。


## 介绍
useTextSelection接受一个HTMLElement, 导出已选择的文本和位置信息。

## Api
```
const useTextSelection: (
  target?: HTMLElement | Ref<HTMLElement> | (() => HTMLElement) | Document
) => vue.ToRefs<{
    text: string;
    rect: {
        left: number;
        right: number;
        top: number;
        bottom: number;
        height: number;
        width: number;
    };
}>;
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| target |  原生Dom或者被Ref嵌套的Dom	|  `HTMLElement or Ref<HTMLElement> or (() => HTMLElement) or Document` | document |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| text	 | 用户选取的文本值	 | string |
| rect	 | 位置信息	 | Rect |


### Rect

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| left	 | 文本的左坐标	 | number |
| right	 | 文本的右坐标		 | number |
| top	 | 文本的顶坐标		 | number |
| bottom	 | 文本的底坐标		 | number |
| height	 | 文本的高度		 | number |
| width	 | 文本的宽度		 | number |

================================================
FILE: packages/useTextSelection/index.ts
================================================
import {
    Ref,
    isRef,
    reactive,
    onMounted,
    onUnmounted,
    toRefs
} from 'vue';

type Target = HTMLElement | Ref<HTMLElement> | (() => HTMLElement ) | Document;

const defaultReact = {
    left: NaN,
    right: NaN,
    top: NaN,
    bottom: NaN,
    height: NaN,
    width: NaN
};

const useTextSelection = (
    target:Target = document
)=>{
    let state = reactive({
        text: '',
        rect: defaultReact
    })

    let el:HTMLElement | Document = document;

    const getEl = ()=>{
        if( typeof target === 'function'){
            return target()
        }
        return isRef( target ) ? target.value : target;
    };

    const getRect = (selection: Selection | null)=>{
        if( !selection ) return defaultReact
        const range = selection.getRangeAt(0);
        const {  height, width, top, left, right, bottom } = range.getBoundingClientRect();
        return {
            height,
            width,
            top,
            left,
            right,
            bottom,
        };
    };

    const handleMouseup = ()=>{
        let currSelection: Selection | null = window.getSelection();
        let text = currSelection ? currSelection.toString() : '';
        let rect = getRect( currSelection );

        state.text = text;
        state.rect = rect;
    };

    const handleMousedown = ()=>{
        if( state.text ){
            state.text = ''
            state.rect = defaultReact;
        }
        const currSelection:Selection | null = window.getSelection();
        if (!currSelection) return;
        currSelection.removeAllRanges();
    };

    onMounted(()=>{
        el = getEl();
        el.addEventListener('mouseup',handleMouseup);
        document.addEventListener('mousedown', handleMousedown);
    })

    onUnmounted(()=>{
        el.removeEventListener('mouseup',handleMouseup);
        document.removeEventListener('mousedown', handleMousedown);
    })

    return toRefs( state )
};

export default useTextSelection


================================================
FILE: packages/useThrottle/index.md
================================================
# useThrottle

用来处理节流值的 Hook。


## 使用

```
<template>
    <div>
        <input
            v-model="throttleCurrValue"
            placeholder="Typed value"
            style="width: 280"
        />
        <p style="marginTop: 16">throttleValue: {{throttleValue}}</p>
    </div>
</template>

<script lang="ts">

import { ref } from 'vue';
import { useThrottle } from 'v3hooks';


export default {
  
  

  setup() {
    const throttleCurrValue = ref(1);
    const throttleValue = useThrottle(throttleCurrValue,500);

    return {
        throttleCurrValue,
        throttleValue,
    }
  }
}
</script>
```

使用useThrottle后,频繁设置throttleCurrValue, throttleValue每隔 500ms 变化一次。。

## Api

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| value | 需要防抖的值 | any | - |
| wait | 超时时间,单位为毫秒 | number | 1000 |

================================================
FILE: packages/useThrottle/index.ts
================================================
// import { debounce } from '../utils'
import useThrottleFn from '../useThrottleFn'
import { ref,Ref, watch } from 'vue';

// 默认值
const defaultDelay = 1000;

/**
 * 处理防抖值
 * @param value 
 * @param delay 
 * @returns 
 */
const useThrottle = <T>( value: Ref<T> , delay?:number )=>{

    delay = delay || defaultDelay;

    const res = ref<T>(value.value) as Ref<T>;

    // 利用useDebounceFn来简化处理值
    const { run } = useThrottleFn(()=> res.value = value.value ,delay);

    watch(value,()=> run(),{ deep: true });

    return res;
};


export default useThrottle

================================================
FILE: packages/useThrottleFn/index.md
================================================
# useThrottleFn

用来处理节流函数的 Hook。


## 使用

```
<template>
    <div>
      <p style="marginTop: 16"> Clicked count: {{throttleFnValue}} </p>
      <button type="button" @click="run">
        useThrottleFn测试
      </button>
    </div>
</template>

<script lang="ts">

import { ref } from 'vue';
import { useThrottleFn } from 'v3hooks';


export default {
  
  

  setup() {
    const throttleFnValue = ref(1);
    const { run } = useThrottleFn(()=>{
      throttleFnValue.value++
    },500)

    return {
      throttleFnValue,
      run,
    }
  }
}
</script>
```

频繁调用 run,但只会每隔 500ms 执行一次相关函数。



## Api

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| fn | 需要节流执行的函数	 | () => void | - |
| wait | 超时时间,单位为毫秒 | number | 1000 |

================================================
FILE: packages/useThrottleFn/index.ts
================================================
import { Fn, throttle } from '../utils'

const defaultDelay = 1000;

/**
 * 处理节流函数
 * @param fn 
 * @param delay 
 * @returns 
 */
const useThrottleFn = ( fn:Fn, delay?: number )=>{
    const run = throttle( fn, typeof( delay ) === 'number'? delay : defaultDelay );
    return { run };
}


export default useThrottleFn

================================================
FILE: packages/useTimeout/index.md
================================================
# useTimeout

一个可以处理 setTimeout 计时器函数的 Hook。


## 基础使用

```vue
<template>
  <div class="hello">
    <div>value:{{ data }}</div>
    <button @click="clear">关闭</button>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useTimeout } from "v3hooks";


export default {
  
  setup() {
    const data = ref(1);
    let delay = ref<null | number>(1000);


    useTimeout(()=>{
      data.value++
    },delay)

    const clear = ()=>{ 
      delay.value = null;
    };

    return {
      data,
      clear
    };
  },
};
</script>

```

1000ms执行一次,设置delay为null则立即中断

## 非中断式调用
```vue
<script lang="ts">
import { useTimeout } from "v3hooks";
export default {
  
  setup() {
    useTimeout(()=>{
      console.log(' 3s 后执行')
    },3000)
  },
};
</script>

```

useTimeout可以接受一个普通number参数,这样的timeout不能被中断

## Api
```
const useTimeout: (
  fn: Fn,
  delay: number | Ref<number | undefined | null>
) => void;
```
### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| fn	 | 要重复调用的函数	 | (...args: any[]) => void | - |
| delay	 | 间隔时间,当取值为 null 或 undefined 时会停止计时器	 | Ref<number> | Ref<undefined> | Ref<null> | - |

================================================
FILE: packages/useTimeout/index.ts
================================================
import { ref, isRef, Ref, onUnmounted } from 'vue';
import { Fn } from '../utils';


const useTimeout = (
    fn: Fn,
    delay: number | Ref<number | undefined | null>
)=>{
    const state = isRef(delay) ? delay : ref(delay);

    let timer: null | NodeJS.Timeout = null;
    
    const clear = ()=> timer && clearTimeout(timer)

    const handler = ()=>{
        if(state.value === undefined || state.value === null ) return
        fn();
    };

    const run = ()=>{
        if( state.value === undefined || state.value === null ){
            clear();
            return
        }
        setTimeout(handler,state.value)
    }
    
    run()
    onUnmounted(()=> clear())
};

export default useTimeout

================================================
FILE: packages/useToggle/index.md
================================================
# useToggle

用于在多个状态值间切换的 Hook。
(此处与 ahooks 略有不同,ahooks只能两个状态切换,本hook支持N个状态切换)


## 基础使用

```
<template>
    <div>
      <p>useToggleDemoState: {{useToggleDemoState}}</p>
      <button @click="handleUseTToggle">设置指定值</button>
      <button @click="useTToggle">useTToggle</button>
      <button @click="useTSetLeft">useTSetLeft</button>
      <button @click="useTSetCenter">useTSetCenter</button>
      <button @click="useTSetRight">useTSetRight</button>
    </div>
</template>

<script lang="ts">

import { ref } from 'vue';
import { useToggle } from 'v3hooks';


export default {
  
  

  setup() {
    //useToggle 测试
    const [ useToggleDemoState, [ useTToggle, useTSetLeft, useTSetCenter, useTSetRight]] = useToggle('left','center','right');

    const handleUseTToggle = ()=>{
      useTToggle('center')
    };

    return {
      useToggleDemoState,
      handleUseTToggle,
      useTToggle,
      useTSetLeft,
      useTSetCenter,
      useTSetRight,
    }
  }
}
</script>
```

useToggle接受多个参数,且在actions中进行同等数量导出。Actions中第一个为toggle切换,其余为设置对应参数。


## 异步值Toggle

```
<template>
  <div class="hello">
    <div> {{state}}</div>
    <button @click="toggle">toggle</button>
    <button @click="setToggle">setToggle</button>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
import { useToggle,useTimeout } from "../../../dist/index.js";
export default {
  
  setup() {

    const platform = ref<string>('安装 App');
    const platform2 = ref<string>('安装中...');
    const [state, [toggle]] = useToggle(platform, platform2,'不安装');

    useTimeout(() => {
      platform.value = `安装 ios App`
      platform2.value = '安装中2....'
    }, ref(3000));

    const setToggle = ()=>{
      toggle(platform)
    }
    
    return {
      state,
      toggle,
      setToggle
    };
  },
};
</script>
```

useToggle可以接受ref值的切换,内部支持了响应式,如果ref值发生变化,state会监听其变化同步修改。



## Api

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| value | 需要切换的值 | string - number - boolean - undefined | - |
| ... | 同上 | 同上 | - |


### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| state | 状态值 | - |
| actions | 操作集合	 | Actions |

### Actions

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| toggle | 触发状态更改的函数,可以接受可选参数修改状态值	 | (state?: any) => void |
| action | 按照value顺序设置state为vulue | () => void |
| ... | 同上 | 同上 |

================================================
FILE: packages/useToggle/index.ts
================================================
import { ref, Ref, isRef, watch } from 'vue';


type State = string | number | boolean | undefined;
type RefState = Ref<State>

type Fn = (v?:any)=> void;

type Actions = Fn[];


function useToggle<T extends State, U extends RefState>(
    ...args: (T | U)[]
): [U,Actions]

/**
 * 用于在N个状态值间切换。
 * @param args 
 * @returns 
 */
function useToggle<T extends State, U extends RefState>(...args: (T | U)[]){
    
    const argsStateArr = args.map((variable)=> isRef(variable) ? variable : ref(variable));
    const initialValue = argsStateArr[0].value;
    const state = ref<State>(initialValue);
    let activeState = argsStateArr[0];


    // 1: 监听当前被激活的state异步
    // 2: 如果当前的异步发生改变则修改state
    watch([activeState],()=>{
        state.value = activeState.value
    },{
        deep:true
    })

    let currIndex = 0;
    const len = args.length;

    
    const toggle = (param?:T | U)=>{
        // 判定是否在参数里
        if( param !== undefined && args.includes(param) ){
            state.value = isRef(param) ? param.value : param;
            activeState = isRef(param) ? param : ref(param);
            return
        }
        // 顺序变化
        currIndex = currIndex + 1 > len - 1 ? 0 : currIndex + 1;
        state.value = argsStateArr[currIndex].value;
        activeState = argsStateArr[currIndex];
    };

    const createHandle = ()=>{
        return argsStateArr.map((active,index)=>{
            return ()=>{
                state.value = active.value;
                activeState = active;
                currIndex = index;
            }
        })
    };

    const actions: Actions = [ toggle, ...createHandle() ];
    
    return [ state, actions ]
}

export default useToggle

================================================
FILE: packages/useUnmount/index.ts
================================================
import { onScopeDispose, onUnmounted, version } from 'vue';

const useUnmount = (fn:any) => {
  const unmounted = onScopeDispose ?? onUnmounted;
  unmounted(() => {
    fn();
  });
};

export default useUnmount;


================================================
FILE: packages/useVirtualList/index.md
================================================
# useVirtualList

长列表虚拟化列表的 Hook,用于解决展示海量数据渲染时首屏渲染缓慢和滚动卡顿问题。


## 使用

```
<template>
  <div class="hello">
    <button
      style="margin-top: 30px"
      type="button"
      @click="handleVirtualScrollTo"
    >
      scroll to
    </button>
    <div
      :ref="containerProps.ref"
      @scroll="containerProps.onScroll"
      style="height: 300px; overflow: auto;border: 1px solid #cccccc"
    >
      <div :style="wrapperStyle">
        <div
          v-for="active in list"
          :key="active"
          style="
            height: 59px;
            border-bottom: 1px solid #cccccc;
            background-color: white;
          "
        >
          {{ active }}
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { useVirtualList } from "v3hooks";

export default {
  
  setup() {
    const { list, wrapperStyle, containerProps, scrollTo } = useVirtualList(
      Array.from(Array(99999).keys()),
      {
        itemHeight: 60,
        overscan: 10,
      }
    );

    const handleVirtualScrollTo = () => {
      scrollTo(22);
    };

    return {
      list,
      wrapperStyle,
      containerProps,
      handleVirtualScrollTo,
    };
  },
};
</script>
```

useVirtualList接受一个数组,导出一个虚拟化的list以元素配置,具体配置看Api


## Api

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| arr | 包含大量数据的列表	 | T[]	 | [] |
| options | 可选配置项,见 Options | Options | - |

### Options

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| itemHeight | 行高度,静态高度可以直接写入像素值,动态高度可传入函数| number | ((index: number) => number) | - |
| overscan | 视区上、下额外展示的 dom 节点数量 | number | 5 |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| list | 当前需要展示的列表内容	 | T[] |
| containerProps | 滚动容器的 props	 | object |
| wrapperStyle | children 外层包裹器 Style | object |
| scrollTo | 快速滚动到指定 index | (index: number) => void |



================================================
FILE: packages/useVirtualList/index.ts
================================================
import { reactive, ref, Ref } from 'vue';

export interface OptionType {
    itemHeight: number | ((index: number) => number);
    overscan?: number;
}

const useVirtualList = <T = any>(state: T[], options: OptionType) => {
    let start = 0;
    let end = 10;
    const list = ref(state.slice(start, end)) as Ref<T[]>;

    const { itemHeight, overscan = 5 } = options;
    const containerRef = ref<HTMLElement | null>();


    const totalHeight: number = (() => {
        if (typeof itemHeight === 'number') {
            return state.length * itemHeight;
        }
        return state.reduce((sum, _, index) => sum + itemHeight(index), 0);
    })();

    // 计算当前视图展示数量
    const getViewCapacity = (containerHeight: number) => {
        if (typeof itemHeight === 'number') {
            return Math.ceil(containerHeight / itemHeight);
        }
        let sum = 0;
        let capacity = 0;
        for (let i = start; i < state.length; i++) {
            const height = (itemHeight as (index: number) => number)(i);
            sum += height;
            if (sum >= containerHeight) {
                capacity = i;
                break;
            }
        }
        return capacity - start;
    };

    // 获取当前索引
    const getOffset = (scrollTop: number) => {
        if (typeof itemHeight === 'number') {
            return Math.floor(scrollTop / itemHeight) + 1;
        }
        let sum = 0;
        let offset = 0;
        for (let i = 0; i < state.length; i++) {
            const height = (itemHeight as (index: number) => number)(i);
            sum += height;
            if (sum >= scrollTop) {
                offset = i;
                break;
            }
        }
        return offset + 1;
    };

    // 获取当前索引向上高度
    const getDistanceTop = (index: number) => {
        if (typeof itemHeight === 'number') {
            const height = index * itemHeight;
            return height;
        }
        const height = state.slice(0, index).reduce((sum, _, i) => sum + itemHeight(i), 0);
        return height;
    };

    let offsetTop = getDistanceTop(start);


    // 计算展示指定位置
    const calculateRange = () => {
        const element = containerRef.value;
        if (element) {
            const offset = getOffset(element.scrollTop);
            const viewCapacity = getViewCapacity(element.clientHeight);

            const from = offset - overscan;
            const to = offset + viewCapacity + overscan;
            start = from < 0 ? 0 : from;
            end = to > state.length ? state.length : to;

            list.value = state.slice(start, end);

            // 实时计算
            offsetTop = getDistanceTop(start);
            wrapperStyle.marginTop = offsetTop + 'px';
            wrapperStyle.height = totalHeight - offsetTop + 'px';
        }
    };




    // 滚动容器的外层监听
    const containerProps = reactive({
        ref: (ele: any) => {
            containerRef.value = ele;
        },
        onScroll: (e: any) => {
            e.preventDefault();
            calculateRange()
        },
        style: { overflowY: 'auto' as const },
    });

    // children 外层包裹器 style
    const wrapperStyle = reactive({
        width: '100%',
        height: totalHeight - offsetTop + 'px',
        marginTop: offsetTop + 'px'
    });

    // 快速滚动到指定 index	
    const scrollTo = (index: number) => {
        if (containerRef.value) {
            containerRef.value.scrollTop = getDistanceTop(index);
            calculateRange();
        }
    };

    return {
        list,
        wrapperStyle,
        containerProps,
        scrollTo
    }
};

export default useVirtualList

================================================
FILE: packages/useWebSocket/index.md
================================================
# useWebSocket

用于处理 WebSocket 的 Hook。

## 基础使用

```vue
<template>
  <div class="hello">
    <div>webScoket状态: {{readyState}}</div>
    <button @click="connect">连接webScoket</button>
    <button @click="disconnect">关闭webScoket</button>
    <button @click="handleSendMessage">发送消息</button>
  </div>
</template>

<script lang="ts">
import { useWebSocket } from "v3hooks";
import { watchEffect } from 'vue';
export default {
  
  setup() {

    const { 
      readyState,
      latestMessage,
      disconnect,
      sendMessage,
    } = useWebSocket('ws://82.157.123.54:9010/ajaxchattest')

    const handleSendMessage = ()=>{
      sendMessage('hello v3hooks')
    }

    watchEffect(()=>{
      console.log(latestMessage.value)
    })

    return {
      readyState,
      disconnect,
      handleSendMessage
    };
  },
};
</script>
```

useWebSocket接受一个string的socket地址,返回一个集合对象。
上面例子中接受参数使用了latestMessage来获取最新一次通讯的event对象。

也可以使用onMessage来获取event对象,例子如下
```
useWebSocket('ws://82.157.123.54:9010/ajaxchattest',{
  onMessage(event){
    console.log(event)
  }
})
```


## Api
```
enum ReadyState {
    Connecting = 0,
    Open = 1,
    Closing = 2,
    Closed = 3
}
interface UseWebSocketOptions {
    manual?: boolean;
    reconnectLimit?: number;
    reconnectInterval?: number;
    onOpen?: (event: WebSocketEventMap['open']) => void;
    onClose?: (event: WebSocketEventMap['close']) => void;
    onMessage?: (event: WebSocketEventMap['message']) => void;
    onError?: (event: WebSocketEventMap['error']) => void;
}
interface Result {
    latestMessage: Ref<WebSocketEventMap['message'] | undefined>;
    sendMessage: WebSocket['send'];
    disconnect: () => void;
    connect: () => void;
    readyState: Ref<ReadyState>;
    webSocketIns: Ref<WebSocket | undefined>;
}
function useWebSocket(socketUrl: string, options?: UseWebSocketOptions): Result;
```

### Params

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| socketUrl |  必填,webSocket 地址	| string | - |
| options |  选填,连接配置项	| options | - |

### Options

| 参数 | 说明 | 类型 | 默认值 |
| :----| :---- | :---- | :---- |
| onOpen |  选填,webSocket 连接成功回调	| (event: WebSocketEventMap['open']) => void | - |
| onClose |  选填,webSocket 连接成功回调	| (event: WebSocketEventMap['close']) => void | - |
| onMessage |  选填,webSocket 收到消息回调	| (message: WebSocketEventMap['message']) => void | - |
| onError |  选填,webSocket 错误回调	| (event: WebSocketEventMap['error']) => void | - |
| reconnectLimit |  选填,重试次数	| number | 3 |
| reconnectInterval |  选填,重试时间间隔(ms)	| number | 3000 |
| manual |  选填,手动启动连接	| boolean | false |

### Result

| 参数 | 说明 | 类型 |
| :----| :---- | :---- |
| latestMessage	 | 最新消息	 | WebSocketEventMap['message'] |
| sendMessage	 | 发送消息函数	 | WebSocket['send'] |
| disconnect	 | 手动断开 webSocket 连接	 | () => void |
| connect	 | 手动连接 webSocket,如果当前已有连接,则关闭后重新连接	 | () => void |
| readyState	 | 当前 webSocket 连接状态	 | ReadyState |
| webSocketIns	| webSocket 实例	 | WebSocket |


================================================
FILE: packages/useWebSocket/index.ts
================================================
import { Ref, ref } from "vue";
import useTimeout from '../useTimeout'

interface UseWebSocketOptions {
    manual?: boolean,
    reconnectLimit?: number,
    reconnectInterval?: number,
    onOpen?: (event:WebSocketEventMap['open'])=> void,
    onClose?: (event:WebSocketEventMap['close'])=> void,
    onMessage?: (event:WebSocketEventMap['message'])=> void,
    onError?: (event:WebSocketEventMap['error'])=> void,
}
enum ReadyState {
    Connecting = 0,
    Open = 1,
    Closing = 2,
    Closed = 3,
}
interface Result{
    latestMessage: Ref<WebSocketEventMap['message'] | undefined>;
    sendMessage: WebSocket['send'];
    disconnect: () => void;
    connect: () => void;
    readyState: Ref<ReadyState>;
    webSocketIns: Ref<WebSocket | undefined>;
}

const defaultOptions = {
    manual: false,
    reconnectLimit: 3,
    reconnectInterval: 3000,
    onOpen:()=>{},
    onClose:()=>{},
    onMessage:()=>{},
    onError:()=>{},
};


function useWebSocket (
    socketUrl: string,
    options?: UseWebSocketOptions
):Result;

function useWebSocket(
    socketUrl: string,
    options?: UseWebSocketOptions
){
    const {
        manual,
        reconnectLimit,
        reconnectInterval,
        onOpen,
        onClose,
        onMessage,
        onError,
    } = {...defaultOptions,...options};

    if(!socketUrl || typeof(socketUrl)!== 'string'){
        throw new Error('useWebSocket require string socketUrl')
    }
    let readyState = ref<number>(ReadyState.Connecting);
    
    const reconnectCount = ref<number>(0);
    const socket = ref<WebSocket>();
    const latestMessage = ref<WebSocketEventMap['message']>();

    const run = ()=>{
        socket.value = new WebSocket(socketUrl);
        socket.value.addEventListener('open', function (event) {
            readyState.value = ReadyState.Open
            onOpen(event)
        });
        
        socket.value.addEventListener('message', function (event) {
            latestMessage.value = event;
            onMessage(event)
        });

        socket.value.addEventListener('error', function (event) {
            console.log('error ', event);
            reconnect();
            onError(event)
        });
        socket.value.addEventListener('close', function (event) {
            readyState.value = ReadyState.Closed
            onClose(event)
        });
    };

    const connect = ()=>{
        if( readyState.value !== ReadyState.Open){
            reconnectCount.value = 0;
            run()
        }
    };

    const reconnect = ()=>{
        if(reconnectCount.value >= reconnectLimit ) return
        useTimeout(()=>{
            reconnectCount.value++
            run()
        },ref(reconnectInterval))
    }

    const disconnect = ()=>{
        if(
            (   readyState.value === ReadyState.Connecting
                || readyState.value === ReadyState.Open 
            )
            && socket.value
        ){
            readyState.value = ReadyState.Closing
            socket.value.close()
        }
    };

    const sendMessage = (data: string | ArrayBufferLike | Blob | ArrayBufferView)=>{
        if(
            data 
            && socket.value
            && readyState.value === ReadyState.Open
        ) socket.value.send(data)
    };

    if( !manual ) connect()
    
    return {
        latestMessage,
        readyState,
        connect,
        disconnect,
        sendMessage,
        webSocketIns: socket
    }
};

export default useWebSocket

================================================
FILE: packages/utils/index.ts
================================================
type Fn = (...[]: any[])=> any; 

/**
 * 防抖
 * @param fn 
 * @param delay 
 * @returns 
 */
const debounce = (fn:Fn,delay:number)=>{
    let timer: NodeJS.Timeout| null = null;
    return function(...args:[]){
        if(timer) clearTimeout(timer)
        timer = setTimeout(()=>{
            // @ts-ignore
            fn.call(this,...args)
        },delay)
    }
}

/**
 * 节流
 * @param fn 
 * @param delay 
 * @returns 
 */
const throttle = (fn:Fn,delay:number)=>{
    let oldNow = Date.now();
    return function(...args:[]){
        const currNow = Date.now();
        if( currNow - oldNow < delay) return
        oldNow = currNow;
        // @ts-ignore
        fn.call(this,...args)
    }
}

/**
 * 防抖+节流
 * @param fn 
 * @param DBdelay 
 * @param TRdelay 
 * @returns 
 */
const throttleAndDeBounce = (fn:Fn,DBdelay:number,TRdelay:number)=>{
    let oldNow = Date.now();
    let timer: NodeJS.Timeout| null = null;
    return function(...args:[]){
        const currNow = Date.now();
        if( currNow - oldNow < TRdelay){
            if(timer) clearTimeout(timer);
            timer= setTimeout(()=>{
                oldNow = currNow;
                // @ts-ignore
                fn.call(this,...args)
            },DBdelay)
            return
        }
        oldNow = currNow;
        // @ts-ignore
        fn.call(this,...args)
    }
};

type Serializer<T> = {
    read(raw: string): T
    write(value: T): string
}

/**
 * 按照类型格式数据的常量Map
 */
const TypeSerializers: Record<'boolean' | 'object' | 'number' | 'any' | 'string', Serializer<any>> = {
    boolean: {
      read: (v: any) => v != null ? v === 'true' : null,
      write: (v: any) => String(v),
    },
    object: {
      read: (v: any) => v ? JSON.parse(v) : null,
      write: (v: any) => JSON.stringify(v),
    },
    number: {
      read: (v: any) => v != null ? Number.parseFloat(v) : null,
      write: (v: any) => String(v),
    },
    any: {
      read: (v: any) => (v != null && v !== 'null') ? v : null,
      write: (v: any) => String(v),
    },
    string: {
      read: (v: any) => v != null ? v : null,
      write: (v: any) => String(v),
    },
}

/**
 * 获取数据类型
 * @param defaultValue 
 * @returns 
 */
const getValueType = (defaultValue:unknown)=>{
    return defaultValue == null
        ? 'any'
        : typeof defaultValue === 'boolean'
        ? 'boolean'
        : typeof defaultValue === 'string'
            ? 'string'
            : typeof defaultValue === 'object'
            ? 'object'
            : Array.isArray(defaultValue)
                ? 'object'
                : !Number.isNaN(defaultValue)
                ? 'number'
                : 'any';
};



export {
    Fn,
    debounce,
    throttle,
    throttleAndDeBounce,
    TypeSerializers,
    getValueType
}

================================================
FILE: packages/utils/memoryCache.ts
================================================

interface Timer {
    [key:string]: NodeJS.Timeout
}
interface Options{
    maxCache?: number
}

class MemoryCache{
    memoryCache: Map<string,any>
    timer: Timer
    maxCache: number
    constructor(options?:Options){
        this.memoryCache = new Map();
        this.timer = {};
        this.maxCache = options?.maxCache || 1000;
    }

    /**
     * 增加缓存
     * @param key 
     * @param value 
     * @param time 
     * @param timeoutCallback 
     */
    put(
        key: string,
        value: any,
        time?: number,
        timeoutCallback?: ()=>{}
    ){
        if( !key || !value){
            throw new Error('key & value is required')
        }
        if( this.size() >= this.maxCache){
            this.del(
                [...this.memoryCache][0][0]
            )
        }
        
        this.memoryCache.set(key,value);

        if(
            time 
            && typeof time === 'number' 
            && time > 0
        ){
            this.timer[key] = setTimeout(()=>{
                this.del(key)
                delete this.timer[key]
                timeoutCallback && timeoutCallback()
            },time)
        }
    };

    /**
     * 获取缓存
     * @param key 
     * @returns 
     */
    get(
        key: string
    ){
        if( !this.has(key) ) return null
        return this.memoryCache.get(key);
    };

    /**
     * 判断是否有缓存
     * @param key 
     * @returns 
     */
    has(
        key: string
    ){
        return this.memoryCache.has(key);
    }

    /**
     * 删除缓存
     * @param key 
     * @returns 
     */
    del(
        key: string
    ){
        if( !this.has(key ) ) return
        if( this.timer[key] ){
            clearTimeout( this.timer[key] )
            delete this.timer[key]
        }
        this.memoryCache.delete(key)
    }

    /**
     * 清除缓存
     * @returns 
     */
    clear(){
        if( this.size() <= 0) return
        this.memoryCache.clear()
        for( let i in this.timer){
            clearTimeout( this.timer[i] )
            delete this.timer[i]
        }
    }

    /**
     * 获取缓存条数
     * @returns 
     */
    size(){
        return this.memoryCache.size
    }
}

export default MemoryCache

================================================
FILE: packages/utils/testingHelpers.ts
================================================
export function sleep(time:number) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(true);
    }, time);
  });
}


================================================
FILE: rollup.config.js
================================================
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import { babel } from '@rollup/plugin-babel';
import typescript from 'rollup-plugin-typescript2';
import dts from 'rollup-plugin-dts';

const input = 'packages/index.ts';
const isProd = process.env.NODE_ENV;
const fn = 'index';
const outputDir =  isProd? 'dist' : 'example/dist';


const basePlugins = [ // 打包插件
  nodeResolve(), // 查找和打包node_modules中的第三方模块
  commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理
  typescript({ // 解析TypeScript
    tsconfigOverride: {
      compilerOptions: {
        declaration: false,
      },
    },
  }), 
]

const devPlugins = [];
const prodPlugins = [
  babel({
    babelHelpers: 'bundled',
  })
];

const plugins = [...basePlugins].concat(isProd ? prodPlugins : devPlugins);


const configs = [
  {
    input,
    output: [
      {
        file: `${outputDir}/${fn}.es.js`, 
        format: 'esm',
        globals: {
          'vue': 'Vue'
        }
      },
      {
        file: `${outputDir}/${fn}.cjs.js`, 
        format: 'cjs',
        globals: {
          'vue': 'Vue'
        }
      },
      {
        name: 'v3hooks',
        file: `${outputDir}/${fn}.js`, 
        format: 'umd',
        globals: {
          'vue': 'Vue'
        },
        plugins: [terser()],
      },
    ],
    plugins: plugins,
    external: [
      'vue',
      'vue-router',
    ]
  },
  {
    input,
    output: {
      file: `${outputDir}/${fn}.d.ts`,
      format: 'esm',
    },
    plugins: [
      dts(),
    ],
    external: [
      'vue',
    ]
  }
]

// const configs = ['esm','cjs'].map((format)=>({
//     input,
//     output: {
//       file: `${outputDir}/${format}.js`, 
//       format,
//       globals: {
//         'vue': 'Vue'
//       }
//     },
//     plugins: plugins,
//     external: [
//       'vue',
//       'vue-router',
//     ]
//   })
// )
// configs.push({
//   input,
//   output: {
//     file: `${outputDir}/index.d.ts`,
//     format: 'esm',
//   },
//   plugins: [
//     dts(),
//   ],
//   external: [
//     'vue',
//   ]
// })

export default configs


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Basic Options */
    // "incremental": true,                         /* Enable incremental compilation */
    "target": "es5",                                /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
    "module": "ESNext",                           /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    // "lib": [],                                   /* Specify library files to be included in the compilation. */
    // "allowJs": true,                             /* Allow javascript files to be compiled. */
    // "checkJs": true,                             /* Report errors in .js files. */
    // "jsx": "preserve",                           /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
    "declaration": true,                         /* Generates corresponding '.d.ts' file. */
    "declarationDir": "./types",
    // "declarationMap": true,                      /* Generates a sourcemap for each corresponding '.d.ts' file. */
    // "sourceMap": true,                           /* Generates corresponding '.map' file. */
    // "outFile": "./",                             /* Concatenate and emit output to single file. */
    // "outDir": "./",                              /* Redirect output structure to the directory. */
    // "rootDir": "./",                             /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                           /* Enable project compilation */
    // "tsBuildInfoFile": "./",                     /* Specify file to store incremental compilation information */
    // "removeComments": true,                      /* Do not emit comments to output. */
    // "noEmit": true,                              /* Do not emit outputs. */
    // "importHelpers": true,                       /* Import emit helpers from 'tslib'. */
    "downlevelIteration": true,                  /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,                     /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true,                                 /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                       /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,                    /* Enable strict null checks. */
    // "strictFunctionTypes": true,                 /* Enable strict checking of function types. */
    // "strictBindCallApply": true,                 /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true,        /* Enable strict checking of property initialization in classes. */
    "noImplicitThis": true,                      /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                        /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                      /* Report errors on unused locals. */
    // "noUnusedParameters": true,                  /* Report errors on unused parameters. */
    // "noImplicitReturns": true,                   /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,          /* Report errors for fallthrough cases in switch statement. */
    // "noUncheckedIndexedAccess": true,            /* Include 'undefined' in index signature results */
    // "noImplicitOverride": true,                  /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
    // "noPropertyAccessFromIndexSignature": true,  /* Require undeclared properties from index signatures to use element accesses. */

    /* Module Resolution Options */
    "moduleResolution": "node",                  /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "baseUrl": "./",                             /* Base directory to resolve non-absolute module names. */
    // "paths": {},                                 /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                              /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                             /* List of folders to include type definitions from. */
    // "types": [],                                 /* Type declaration files to be included in compilation. */
    // "allowSyntheticDefaultImports": true,        /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true,                        /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,                    /* Do not resolve the real path of symlinks. */
    // "allowUmdGlobalAccess": true,                /* Allow accessing UMD globals from modules. */

    /* Source Map Options */
    // "sourceRoot": "",                            /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                               /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,                     /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                       /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    // "experimentalDecorators": true,              /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,               /* Enables experimental support for emitting type metadata for decorators. */

    /* Advanced Options */
    "skipLibCheck": true,                           /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true        /* Disallow inconsistently-cased references to the same file. */
  },
  "include": [
    "packages"
  ],
  "exclude": [
    "node_modules",
    "**/**/*.test.ts",
    "**/**/*.stories.tsx",
    "**/**/*.md",
    "**/dist",
    "packages/.test",
    "packages/_docs"
  ]
}
Download .txt
gitextract_yd9gghg3/

├── .gitignore
├── README.md
├── babel.config.js
├── docs/
│   └── question.md
├── example/
│   ├── .eslintignore
│   ├── README.md
│   ├── babel.config.js
│   ├── jsconfig.json
│   ├── package.json
│   ├── public/
│   │   └── index.html
│   ├── src/
│   │   ├── App.vue
│   │   ├── components/
│   │   │   └── HelloWorld.vue
│   │   ├── main.ts
│   │   ├── pages/
│   │   │   ├── home/
│   │   │   │   └── index.vue
│   │   │   ├── useBoolean/
│   │   │   │   └── index.vue
│   │   │   ├── useCookie/
│   │   │   │   └── index.vue
│   │   │   ├── useDate/
│   │   │   │   └── index.vue
│   │   │   ├── useDynamicList/
│   │   │   │   └── index.vue
│   │   │   ├── useExternal/
│   │   │   │   └── index.vue
│   │   │   ├── useFullscreen/
│   │   │   │   └── index.vue
│   │   │   ├── useInterval/
│   │   │   │   └── index.vue
│   │   │   ├── useLocalStorage/
│   │   │   │   └── index.vue
│   │   │   ├── useLockFn/
│   │   │   │   └── index.vue
│   │   │   ├── useMediaQuery/
│   │   │   │   └── index.vue
│   │   │   ├── useNetwork/
│   │   │   │   └── index.vue
│   │   │   ├── useQRCode/
│   │   │   │   └── index.vue
│   │   │   ├── useRouteQuery/
│   │   │   │   └── index.vue
│   │   │   ├── useSessionStorage/
│   │   │   │   └── index.vue
│   │   │   ├── useSetAndUseMap/
│   │   │   │   └── index.vue
│   │   │   ├── useTextSelection/
│   │   │   │   └── index.vue
│   │   │   ├── useToggle/
│   │   │   │   └── index.vue
│   │   │   ├── useVirtualList/
│   │   │   │   └── index.vue
│   │   │   └── useWebSocket/
│   │   │       └── index.vue
│   │   ├── router.ts
│   │   └── shims-vue.d.ts
│   ├── tsconfig.json
│   └── vue.config.js
├── jest.config.js
├── package.json
├── packages/
│   ├── index.ts
│   ├── useBoolean/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useCookie/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDate/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDebounce/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDebounceFn/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDocumentVisibility/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useDynamicList/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useExternal/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useFullscreen/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useInterval/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useLocalStorage/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useLockFn/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useMap/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useMediaQuery/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useNetwork/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useQRCode/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useRequest/
│   │   ├── __tests__/
│   │   │   └── index.test.ts
│   │   ├── index.md
│   │   ├── index.ts
│   │   ├── src/
│   │   │   ├── cache.ts
│   │   │   ├── fetch.ts
│   │   │   ├── loadingDelay.ts
│   │   │   ├── polling.ts
│   │   │   ├── service.ts
│   │   │   └── visibility.ts
│   │   └── types.d.ts
│   ├── useRouteQuery/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useSessionStorage/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useSet/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useTextSelection/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useThrottle/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useThrottleFn/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useTimeout/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useToggle/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useUnmount/
│   │   └── index.ts
│   ├── useVirtualList/
│   │   ├── index.md
│   │   └── index.ts
│   ├── useWebSocket/
│   │   ├── index.md
│   │   └── index.ts
│   └── utils/
│       ├── index.ts
│       ├── memoryCache.ts
│       └── testingHelpers.ts
├── rollup.config.js
└── tsconfig.json
Download .txt
SYMBOL INDEX (87 symbols across 25 files)

FILE: example/src/router.ts
  function createRouter (line 21) | function createRouter() {

FILE: packages/useBoolean/index.ts
  type Actions (line 7) | interface Actions{
  function useBoolean (line 21) | function useBoolean (value?: boolean){

FILE: packages/useCookie/index.ts
  type Options (line 4) | interface Options{

FILE: packages/useDate/index.ts
  type Value (line 10) | type Value =  string | number | Date;
  type Options (line 12) | interface Options{
  function useDate (line 26) | function useDate( initialValue?:Value, options?: Options ){

FILE: packages/useExternal/index.ts
  type Elements (line 3) | type Elements = HTMLScriptElement| HTMLLinkElement | HTMLImageElement;
  type Options (line 5) | interface Options{

FILE: packages/useFullscreen/index.ts
  type Options (line 3) | interface Options{
  type Actions (line 7) | interface Actions{
  type Target (line 13) | type Target = HTMLElement | ( () => HTMLElement ) | Ref<HTMLElement>;

FILE: packages/useInterval/index.ts
  type UseIntervalOptions (line 4) | interface UseIntervalOptions{

FILE: packages/useLocalStorage/index.ts
  type Options (line 4) | interface Options{

FILE: packages/useLockFn/index.ts
  type ArgsAny (line 3) | type ArgsAny = any[];
  type Fn (line 5) | type Fn = (...args: ArgsAny)=> Promise<any>;

FILE: packages/useMap/index.ts
  type MapValue (line 4) | type MapValue = readonly (readonly [any, any])[]
  type Actions (line 6) | interface Actions<T>{
  function useMap (line 21) | function useMap<T = any>(initialValue?:MapValue){

FILE: packages/useNetwork/index.ts
  type NetworkState (line 11) | interface NetworkState {

FILE: packages/useQRCode/index.ts
  type Text (line 4) | type Text = Ref<string> | string;
  type useQRCodeOptions (line 6) | interface useQRCodeOptions{
  method onRenderingEnd (line 30) | onRenderingEnd(qrCodeOptions:any, dataURL:string){

FILE: packages/useRequest/__tests__/index.test.ts
  method setup (line 59) | setup() {
  method setup (line 92) | setup() {
  method setup (line 112) | setup() {
  method setup (line 146) | setup() {
  method setup (line 185) | setup() {
  method setup (line 214) | setup() {
  method setup (line 229) | setup() {
  method setup (line 245) | setup() {
  method setup (line 263) | setup() {
  method setup (line 294) | setup() {
  method setup (line 311) | setup() {
  method setup (line 329) | setup() {
  method setup (line 356) | setup() {
  method setup (line 372) | setup() {
  method setup (line 389) | setup() {
  method setup (line 404) | setup() {
  method setup (line 421) | setup() {
  method setup (line 439) | setup() {
  method setup (line 455) | setup() {
  method setup (line 473) | setup() {
  method setup (line 492) | setup() {
  method setup (line 508) | setup() {

FILE: packages/useRequest/src/fetch.ts
  type FetchParams (line 1) | interface FetchParams extends RequestInit{
  function fetchData (line 23) | function fetchData( p:FetchParams ){

FILE: packages/useRequest/src/polling.ts
  class Polling (line 6) | class Polling{
    method constructor (line 10) | constructor() {
    method run (line 16) | run(
    method cancel (line 27) | cancel(){
    method task (line 31) | private task(

FILE: packages/useRequest/types.d.ts
  type Service (line 5) | type Service = (...args: any[]) => Promise<any>;
  type FetchService (line 7) | interface FetchService extends FetchParams{}
  type BaseOptions (line 11) | interface BaseOptions {
  type Run (line 33) | type Run = (...args: any[])=> void;
  type Refresh (line 36) | type Refresh = ()=> void;
  type Cancel (line 39) | type Cancel = (()=> void);
  type Mutate (line 42) | type Mutate = (state:any)=> void;
  type Result (line 46) | interface Result<T> {

FILE: packages/useSessionStorage/index.ts
  type Options (line 4) | interface Options{

FILE: packages/useSet/index.ts
  type Actions (line 3) | interface Actions<T>{
  function useSet (line 16) | function useSet<T = any>(initialValue?:T[]){

FILE: packages/useTextSelection/index.ts
  type Target (line 10) | type Target = HTMLElement | Ref<HTMLElement> | (() => HTMLElement ) | Do...

FILE: packages/useToggle/index.ts
  type State (line 4) | type State = string | number | boolean | undefined;
  type RefState (line 5) | type RefState = Ref<State>
  type Fn (line 7) | type Fn = (v?:any)=> void;
  type Actions (line 9) | type Actions = Fn[];
  function useToggle (line 21) | function useToggle<T extends State, U extends RefState>(...args: (T | U)...

FILE: packages/useVirtualList/index.ts
  type OptionType (line 3) | interface OptionType {

FILE: packages/useWebSocket/index.ts
  type UseWebSocketOptions (line 4) | interface UseWebSocketOptions {
  type ReadyState (line 13) | enum ReadyState {
  type Result (line 19) | interface Result{
  function useWebSocket (line 44) | function useWebSocket(

FILE: packages/utils/index.ts
  type Fn (line 1) | type Fn = (...[]: any[])=> any;
  type Serializer (line 64) | type Serializer<T> = {

FILE: packages/utils/memoryCache.ts
  type Timer (line 2) | interface Timer {
  type Options (line 5) | interface Options{
  class MemoryCache (line 9) | class MemoryCache{
    method constructor (line 13) | constructor(options?:Options){
    method put (line 26) | put(
    method get (line 61) | get(
    method has (line 73) | has(
    method del (line 84) | del(
    method clear (line 99) | clear(){
    method size (line 112) | size(){

FILE: packages/utils/testingHelpers.ts
  function sleep (line 1) | function sleep(time:number) {
Condensed preview — 108 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (186K chars).
[
  {
    "path": ".gitignore",
    "chars": 363,
    "preview": "# compiled output\n/node_modules\n/dist\nexample/node_modules\nexample/dist\n\n# Logs\nlogs\n*.log\n\n# OS\n.DS_Store\n\n# Tests\npack"
  },
  {
    "path": "README.md",
    "chars": 2081,
    "preview": "<p align=\"center\">\n  <img align=\"center\" style=\"width:200px\" src=\"https://img-steward-online.goodaa.com.cn/568d4fc225de4"
  },
  {
    "path": "babel.config.js",
    "chars": 148,
    "preview": "module.exports = {\n  presets: [\n    [\n      '@babel/preset-env',\n      {\n        modules: false,\n      },\n    ],\n    '@b"
  },
  {
    "path": "docs/question.md",
    "chars": 1425,
    "preview": "## 常见问题\n\n### setup中使用data为undefined\n因为data是被Ref嵌套的响应式, 直接return到Template中使用没问题,如果想要在Setup中使用需要嵌套一层watchEffect来获取异步数据。\n``"
  },
  {
    "path": "example/.eslintignore",
    "chars": 4,
    "preview": "dist"
  },
  {
    "path": "example/README.md",
    "chars": 319,
    "preview": "# example\n\n## Project setup\n```\nnpm install\n```\n\n### Compiles and hot-reloads for development\n```\nnpm run serve\n```\n\n###"
  },
  {
    "path": "example/babel.config.js",
    "chars": 73,
    "preview": "module.exports = {\n  presets: [\n    '@vue/cli-plugin-babel/preset'\n  ]\n}\n"
  },
  {
    "path": "example/jsconfig.json",
    "chars": 279,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"esnext\",\n    \"baseUrl\": \"./\",\n    \"moduleResolution\": \"node"
  },
  {
    "path": "example/package.json",
    "chars": 1288,
    "preview": "{\n  \"name\": \"example\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve\",\n   "
  },
  {
    "path": "example/public/index.html",
    "chars": 611,
    "preview": "<!DOCTYPE html>\n<html lang=\"\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=ed"
  },
  {
    "path": "example/src/App.vue",
    "chars": 367,
    "preview": "<template>\n  <router-view v-slot=\"{ Component }\">\n    <Suspense>\n      <component :is=\"Component\" />\n    </Suspense>\n  <"
  },
  {
    "path": "example/src/components/HelloWorld.vue",
    "chars": 5465,
    "preview": "<template>\n  <div class=\"hello\">\n    <p>{{ loading ? \"loading\" : data }}</p>\n    <button @click=\"run\">发起</button>\n    <b"
  },
  {
    "path": "example/src/main.ts",
    "chars": 196,
    "preview": "import { createApp } from 'vue'\nimport App from './App.vue'\nimport { createRouter } from './router'\n\nconst app = createA"
  },
  {
    "path": "example/src/pages/home/index.vue",
    "chars": 2462,
    "preview": "<template>\n  <div class=\"hello\">\n    <h4>第一测试</h4>\n    <p>{{ loading ? \"loading\" : data }}</p>\n    <button @click=\"run\">"
  },
  {
    "path": "example/src/pages/useBoolean/index.vue",
    "chars": 647,
    "preview": "<template>\n  <div class=\"hello\">\n    <div> {{useBooleanState}}</div>\n    <button @click=\"toggle\">toggle</button>\n    <bu"
  },
  {
    "path": "example/src/pages/useCookie/index.vue",
    "chars": 642,
    "preview": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <p> value:{{ state }}</p>\n    <button "
  },
  {
    "path": "example/src/pages/useDate/index.vue",
    "chars": 905,
    "preview": "<template>\n  <div class=\"hello\">\n    <div> value:{{ data }}</div>\n    <button @click=\"handleUpdateTime\">无参数刷新</button>\n "
  },
  {
    "path": "example/src/pages/useDynamicList/index.vue",
    "chars": 1637,
    "preview": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <div style=\"width: 60vw\">\n      <p \n  "
  },
  {
    "path": "example/src/pages/useExternal/index.vue",
    "chars": 1280,
    "preview": "<template>\n  <div class=\"hello\">\n    <div id=\"aa\" ref=\"aa\"></div>\n    <div class=\"bd-example\"><span class=\"badge badge-p"
  },
  {
    "path": "example/src/pages/useFullscreen/index.vue",
    "chars": 1214,
    "preview": "<template>\n  <div class=\"hello\">\n    <div ref=\"fullScreen\" style=\"background: white\">\n        <p>是否全屏: {{isFullscreen}}<"
  },
  {
    "path": "example/src/pages/useInterval/index.vue",
    "chars": 638,
    "preview": "<template>\n  <div class=\"hello\">\n    <div>value:{{ data }}</div>\n    <button @click=\"clear\">关闭</button>\n  </div>\n</templ"
  },
  {
    "path": "example/src/pages/useLocalStorage/index.vue",
    "chars": 758,
    "preview": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <div style=\"width: 60vw\">\n      <p> va"
  },
  {
    "path": "example/src/pages/useLockFn/index.vue",
    "chars": 737,
    "preview": "<template>\n  <div class=\"hello\">\n    <div>value:{{ data }}</div>\n    <button @click=\"submit\">submit</button>\n  </div>\n</"
  },
  {
    "path": "example/src/pages/useMediaQuery/index.vue",
    "chars": 479,
    "preview": "<template>\n  <div class=\"hello\">\n    <div>\n      <p> value:{{ state }}</p>\n    </div>\n  </div>\n</template>\n\n<script lang"
  },
  {
    "path": "example/src/pages/useNetwork/index.vue",
    "chars": 358,
    "preview": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <p> 网络状态:{{ state }}</p>\n  </div>\n</te"
  },
  {
    "path": "example/src/pages/useQRCode/index.vue",
    "chars": 719,
    "preview": "<template>\n  <div class=\"hello\">\n    <div> 二维码:</div>\n    <img :src=\"state\" alt=\"\">\n  </div>\n</template>\n\n<script lang=\""
  },
  {
    "path": "example/src/pages/useRouteQuery/index.vue",
    "chars": 574,
    "preview": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <p> value:{{ state }}</p>\n    <button "
  },
  {
    "path": "example/src/pages/useSessionStorage/index.vue",
    "chars": 770,
    "preview": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <div style=\"width: 60vw\">\n      <p> va"
  },
  {
    "path": "example/src/pages/useSetAndUseMap/index.vue",
    "chars": 828,
    "preview": "<template>\n  <div class=\"hello\">\n    <div>\n      <p> value:{{ state }}</p>\n      <button @click=\"()=> add(Math.random())"
  },
  {
    "path": "example/src/pages/useTextSelection/index.vue",
    "chars": 523,
    "preview": "<template>\n  <div style=\"text-align: center\">\n    <p ref=\"p\"> 可选择区域: 123111111111111aaaaaaaaaaabbbbbbbbbbb eeeeeeeeeeeee"
  },
  {
    "path": "example/src/pages/useToggle/index.vue",
    "chars": 842,
    "preview": "<template>\n  <div class=\"hello\">\n    <div> {{state}}</div>\n    <button @click=\"toggle\">toggle</button>\n    <button @clic"
  },
  {
    "path": "example/src/pages/useVirtualList/index.vue",
    "chars": 1567,
    "preview": "<template>\n  <div class=\"hello\">\n    <button\n      type=\"button\"\n      @click=\"handleVirtualScrollTo\"\n    >\n      跳转到第22"
  },
  {
    "path": "example/src/pages/useWebSocket/index.vue",
    "chars": 983,
    "preview": "<template>\n  <div class=\"hello\">\n    <div>webScoket状态: {{readyState}}</div>\n    <button @click=\"connect\">连接webScoket</bu"
  },
  {
    "path": "example/src/router.ts",
    "chars": 3668,
    "preview": "import {\n    createRouter as _createRouter,\n    createWebHashHistory\n} from 'vue-router'\n\n// Auto generates routes from "
  },
  {
    "path": "example/src/shims-vue.d.ts",
    "chars": 168,
    "preview": "/* eslint-disable */\ndeclare module '*.vue' {\n  import type { DefineComponent } from 'vue'\n  const component: DefineComp"
  },
  {
    "path": "example/tsconfig.json",
    "chars": 673,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    "
  },
  {
    "path": "example/vue.config.js",
    "chars": 62,
    "preview": "module.exports = {\n    devServer: {\n        port: 8081\n    }\n}"
  },
  {
    "path": "jest.config.js",
    "chars": 630,
    "preview": "module.exports = {\n  moduleFileExtensions: ['js', 'ts', 'jsx', 'tsx'],\n  transform: {\n    '^.+\\\\.(js|jsx)$': 'babel-jest"
  },
  {
    "path": "package.json",
    "chars": 1637,
    "preview": "{\n  \"name\": \"v3hooks\",\n  \"version\": \"1.5.0\",\n  \"author\": \"yanzhandong\",\n  \"description\": \"针对 Vue3 的实用Hooks集合\",\n  \"keywor"
  },
  {
    "path": "packages/index.ts",
    "chars": 1949,
    "preview": "import useRequest from './useRequest/index';\nimport useDate from './useDate/index';\nimport useDebounce from './useDeboun"
  },
  {
    "path": "packages/useBoolean/index.md",
    "chars": 1116,
    "preview": "# useBoolean\n\n优雅的管理 boolean 值的 Hook。\n\n\n## 使用\n\n```\n<template>\n    <div>\n      <p>{{ useBooleanState }}</p>\n      <button "
  },
  {
    "path": "packages/useBoolean/index.ts",
    "chars": 649,
    "preview": "import { Ref } from 'vue';\nimport useToggle from '../useToggle';\n\n// 默认值\nconst defaultValue = false;\n\ninterface Actions{"
  },
  {
    "path": "packages/useCookie/index.md",
    "chars": 1800,
    "preview": "# useCookie\n\n一个用来操作Cookie的 Hook 。\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"display:flex;align-items:fle"
  },
  {
    "path": "packages/useCookie/index.ts",
    "chars": 1091,
    "preview": "import { ref, watch as vueWatch } from 'vue';\nimport Cookies from 'js-cookie';\n\ninterface Options{\n    watch?: boolean,\n"
  },
  {
    "path": "packages/useDate/index.md",
    "chars": 1783,
    "preview": "# useDate\n\n一个用来操作时间的 Hook 。\n\n内部使用了 dayjs 作为format工具\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div> value:"
  },
  {
    "path": "packages/useDate/index.ts",
    "chars": 1572,
    "preview": "import { ref, readonly } from 'vue';\nimport dayjs from 'dayjs';\n\n\nconst defaultOptions ={\n    format: 'YYYY-MM-DD HH:mm:"
  },
  {
    "path": "packages/useDebounce/index.md",
    "chars": 840,
    "preview": "# useDebounce\n\n用来处理防抖值的 Hook。\n\n## 使用\n\n```\n<template>\n    <div>\n        <input\n            v-model=\"debounceCurrValue\"\n  "
  },
  {
    "path": "packages/useDebounce/index.ts",
    "chars": 565,
    "preview": "// import { debounce } from '../utils'\nimport useDebounceFn from '../useDebounceFn'\nimport { ref,Ref, watch } from 'vue'"
  },
  {
    "path": "packages/useDebounceFn/index.md",
    "chars": 803,
    "preview": "# useDebounceFn\n\n用来处理防抖函数的 Hook。\n\n\n## 使用\n\n```\n<template>\n    <div>\n      <p style=\"marginTop: 16\"> Clicked count: {{debo"
  },
  {
    "path": "packages/useDebounceFn/index.ts",
    "chars": 317,
    "preview": "import { Fn, debounce } from '../utils'\n\nconst defaultDelay = 1000;\n/**\n * 处理防抖函数\n * @param fn \n * @param delay \n * @ret"
  },
  {
    "path": "packages/useDocumentVisibility/index.md",
    "chars": 587,
    "preview": "# useDocumentVisibility\n\n可以获取页面可见状态的 Hook。\n\n## 使用Demo\n\n### 基础用法\n```vue\n<script lang=\"ts\">\nimport { watch } from 'vue';\ni"
  },
  {
    "path": "packages/useDocumentVisibility/index.ts",
    "chars": 361,
    "preview": "import { ref, Ref } from 'vue';\n\nconst useDocumentVisibility = (): Ref<boolean> =>{\n    const documentVisibility = ref<b"
  },
  {
    "path": "packages/useDynamicList/index.md",
    "chars": 2543,
    "preview": "# useDynamicList\n\n一个帮助你管理列表状态,并能生成唯一 key 的 Hook。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"display:flex"
  },
  {
    "path": "packages/useDynamicList/index.ts",
    "chars": 2428,
    "preview": "import { ref, Ref, isRef } from 'vue';\n\nconst useDynamicList = <T = any>(initialValue: Ref<T[]>)=>{\n\n    let uuid = <num"
  },
  {
    "path": "packages/useExternal/index.md",
    "chars": 3763,
    "preview": "# useExternal\n\n一个用于动态地向页面加载或卸载外部资源的 Hook。\n\n## 使用Demo\n### 基本使用\n```vue\n<script lang=\"ts\">\nimport { useExternal } from \"v3h"
  },
  {
    "path": "packages/useExternal/index.ts",
    "chars": 3118,
    "preview": "import { ref, Ref, isRef } from 'vue';\n\ntype Elements = HTMLScriptElement| HTMLLinkElement | HTMLImageElement;\n\ninterfac"
  },
  {
    "path": "packages/useFullscreen/index.md",
    "chars": 2401,
    "preview": "# useFullscreen\n\n一个用于处理 dom 全屏的 Hook。\n\n## 使用Demo\n\n### 基础用法\n```vue\n<template>\n  <div class=\"hello\">\n    <div ref=\"fullScr"
  },
  {
    "path": "packages/useFullscreen/index.ts",
    "chars": 1794,
    "preview": "import { ref, Ref, isRef, onMounted, onUnmounted } from 'vue';\n\ninterface Options{\n    onFull?: ()=> void,\n    onExitFul"
  },
  {
    "path": "packages/useInterval/index.md",
    "chars": 1491,
    "preview": "# useInterval\n\n一个可以处理 setInterval 的 Hook。\n\n\n## 基础使用\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>value:{{ data }}</"
  },
  {
    "path": "packages/useInterval/index.ts",
    "chars": 980,
    "preview": "import { ref, Ref, onMounted, onUnmounted, isRef } from 'vue';\nimport { Fn } from '../utils';\n\ninterface UseIntervalOpti"
  },
  {
    "path": "packages/useLocalStorage/index.md",
    "chars": 1550,
    "preview": "# useLocalStorage\n\n一个可以将状态持久化存储在 localStorage 中的 Hook 。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"displ"
  },
  {
    "path": "packages/useLocalStorage/index.ts",
    "chars": 1380,
    "preview": "import { ref, Ref, isRef, watch as vueWatch } from 'vue';\nimport { TypeSerializers,getValueType } from '../utils'\nconst "
  },
  {
    "path": "packages/useLockFn/index.md",
    "chars": 1119,
    "preview": "# useLockFn\n\n用于给一个异步函数增加竞态锁,防止并发执行。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>value:{{ data }}</div>"
  },
  {
    "path": "packages/useLockFn/index.ts",
    "chars": 494,
    "preview": "import { ref } from 'vue';\n\ntype ArgsAny = any[];\n\ntype Fn = (...args: ArgsAny)=> Promise<any>;\n\nconst useLockFn = (fn: "
  },
  {
    "path": "packages/useMap/index.md",
    "chars": 1636,
    "preview": "# useMap\n\n一个可以管理 Map 类型状态的 Hook。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>\n      <p> value:{{ state"
  },
  {
    "path": "packages/useMap/index.ts",
    "chars": 1232,
    "preview": "import { ref, Ref, markRaw } from 'vue';\n\n\ntype MapValue = readonly (readonly [any, any])[]\n\ninterface Actions<T>{\n    s"
  },
  {
    "path": "packages/useMediaQuery/index.md",
    "chars": 877,
    "preview": "# useMediaQuery\n\n一个监听 mediaQuery 状态的 Hook。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>\n      <p> valu"
  },
  {
    "path": "packages/useMediaQuery/index.ts",
    "chars": 475,
    "preview": "import { ref, onUnmounted } from 'vue';\n\nconst useMediaQuery = (query:string)=>{\n    const mediaQuery = window.matchMedi"
  },
  {
    "path": "packages/useNetwork/index.md",
    "chars": 1251,
    "preview": "# useNetwork\n\n一个用来获取网络状态的 Hook 。\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex"
  },
  {
    "path": "packages/useNetwork/index.ts",
    "chars": 1964,
    "preview": "import { reactive, onMounted, onUnmounted } from \"vue\";\n\n\nconst getConnection = ()=> {\n    const nav = navigator as any;"
  },
  {
    "path": "packages/useQRCode/index.md",
    "chars": 1452,
    "preview": "# useQRCode\n\n一个用来生成二维码的 Hook 。\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div> 二维码:</div>\n    <img :src=\"s"
  },
  {
    "path": "packages/useQRCode/index.ts",
    "chars": 1009,
    "preview": "import qrcode from \"easyqrcodejs\";\nimport { ref, Ref, watch, isRef } from \"vue\";\n\ntype Text = Ref<string> | string;\n\nint"
  },
  {
    "path": "packages/useRequest/__tests__/index.test.ts",
    "chars": 13182,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport { defineComponent, ref } from 'vue';\nimport MockDate from 'mockda"
  },
  {
    "path": "packages/useRequest/index.md",
    "chars": 6046,
    "preview": "# useRequest\n\n专注于管理异步请求的Hook,加速你的日常开发\n\n* 自动请求/手动请求\n* SWR(stale-while-revalidate)\n* 缓存/预加载\n* 屏幕聚焦重新请求\n* 轮询\n* 防抖\n* 节流\n* 依赖"
  },
  {
    "path": "packages/useRequest/index.ts",
    "chars": 5050,
    "preview": "import { \n    BaseOptions,\n    Result,\n    Run,\n    Service,\n    FetchService,\n    Cancel\n} from './types';\nimport { \n  "
  },
  {
    "path": "packages/useRequest/src/cache.ts",
    "chars": 382,
    "preview": "\nimport memoryCache from '../../utils/memoryCache'\n\nconst handleResCache = (\n    data: any,\n    resCache: memoryCache,\n "
  },
  {
    "path": "packages/useRequest/src/fetch.ts",
    "chars": 879,
    "preview": "export interface FetchParams extends RequestInit{\n    url: RequestInfo,\n}\n\nconst defaultParams = {\n    method: 'POST', /"
  },
  {
    "path": "packages/useRequest/src/loadingDelay.ts",
    "chars": 539,
    "preview": "/**\n * loading的延迟计算\n * @param startTime \n * @param loadingDelay \n * @param args \n * @returns \n */\nlet loadingDelayTimer:"
  },
  {
    "path": "packages/useRequest/src/polling.ts",
    "chars": 1005,
    "preview": "import { Run } from '../types'\n\n/**\n * 执行轮询\n */\nclass Polling{\n    isActive: boolean; // 是否是激活状态\n    private pollingInte"
  },
  {
    "path": "packages/useRequest/src/service.ts",
    "chars": 721,
    "preview": "\nimport fetchData from './fetch'\nimport MemoryCache from '../../utils/memoryCache'\n\nconst argsSymbolKey = 'argsKey';\n\nco"
  },
  {
    "path": "packages/useRequest/src/visibility.ts",
    "chars": 509,
    "preview": "import { Run } from '../types'\nimport { throttle } from '../../utils'\n\n\n/**\n * \b监听屏幕是否聚焦\n * @param run \n * @param focusT"
  },
  {
    "path": "packages/useRequest/types.d.ts",
    "chars": 1472,
    "preview": "import { Ref } from 'vue';\nimport { FetchParams } from './src/fetch'\n\n// Service请求实例\nexport type Service = (...args: any"
  },
  {
    "path": "packages/useRouteQuery/index.md",
    "chars": 976,
    "preview": "# useRouteQuery\n\n一个获取vueRouter query的 Hook 。\n\n请确保项目已安装Vue Router v4.x版本及以上,否则将不能使用此Hook.\n\n\n## 使用Demo\n\n```vue\n<template>\n"
  },
  {
    "path": "packages/useRouteQuery/index.ts",
    "chars": 419,
    "preview": "import { computed } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\n\n\nconst useRouteQuery = ( key: string)"
  },
  {
    "path": "packages/useSessionStorage/index.md",
    "chars": 1576,
    "preview": "# useSessionStorage\n\n一个可以将状态持久化存储在 sessionStorage 中的 Hook 。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"d"
  },
  {
    "path": "packages/useSessionStorage/index.ts",
    "chars": 1373,
    "preview": "import { ref, Ref, isRef, watch as vueWatch } from 'vue';\nimport { TypeSerializers, getValueType } from '../utils'\nconst"
  },
  {
    "path": "packages/useSet/index.md",
    "chars": 1123,
    "preview": "# useSet\n\n一个可以管理 Set 类型状态的 Hook。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>\n      <p> value:{{ state"
  },
  {
    "path": "packages/useSet/index.ts",
    "chars": 886,
    "preview": "import { ref, Ref, markRaw } from 'vue';\n\ninterface Actions<T>{\n    add: (value: T)=> void,\n    remove: (value: T)=> voi"
  },
  {
    "path": "packages/useTextSelection/index.md",
    "chars": 2014,
    "preview": "# useTextSelection\n\n实时获取用户当前选取的文本内容及位置的hook。\n\n\n## 使用Demo\n\n### 基础用法\n```vue\n<template>\n  <div style=\"text-align: center\">\n"
  },
  {
    "path": "packages/useTextSelection/index.ts",
    "chars": 2000,
    "preview": "import {\n    Ref,\n    isRef,\n    reactive,\n    onMounted,\n    onUnmounted,\n    toRefs\n} from 'vue';\n\ntype Target = HTMLE"
  },
  {
    "path": "packages/useThrottle/index.md",
    "chars": 818,
    "preview": "# useThrottle\n\n用来处理节流值的 Hook。\n\n\n## 使用\n\n```\n<template>\n    <div>\n        <input\n            v-model=\"throttleCurrValue\"\n "
  },
  {
    "path": "packages/useThrottle/index.ts",
    "chars": 561,
    "preview": "// import { debounce } from '../utils'\nimport useThrottleFn from '../useThrottleFn'\nimport { ref,Ref, watch } from 'vue'"
  },
  {
    "path": "packages/useThrottleFn/index.md",
    "chars": 747,
    "preview": "# useThrottleFn\n\n用来处理节流函数的 Hook。\n\n\n## 使用\n\n```\n<template>\n    <div>\n      <p style=\"marginTop: 16\"> Clicked count: {{thro"
  },
  {
    "path": "packages/useThrottleFn/index.ts",
    "chars": 318,
    "preview": "import { Fn, throttle } from '../utils'\n\nconst defaultDelay = 1000;\n\n/**\n * 处理节流函数\n * @param fn \n * @param delay \n * @re"
  },
  {
    "path": "packages/useTimeout/index.md",
    "chars": 1142,
    "preview": "# useTimeout\n\n一个可以处理 setTimeout 计时器函数的 Hook。\n\n\n## 基础使用\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>value:{{ data }"
  },
  {
    "path": "packages/useTimeout/index.ts",
    "chars": 706,
    "preview": "import { ref, isRef, Ref, onUnmounted } from 'vue';\nimport { Fn } from '../utils';\n\n\nconst useTimeout = (\n    fn: Fn,\n  "
  },
  {
    "path": "packages/useToggle/index.md",
    "chars": 2323,
    "preview": "# useToggle\n\n用于在多个状态值间切换的 Hook。\n(此处与 ahooks 略有不同,ahooks只能两个状态切换,本hook支持N个状态切换\b\b)\n\n\n## 基础使用\n\n```\n<template>\n    <div>\n   "
  },
  {
    "path": "packages/useToggle/index.ts",
    "chars": 1688,
    "preview": "import { ref, Ref, isRef, watch } from 'vue';\n\n\ntype State = string | number | boolean | undefined;\ntype RefState = Ref<"
  },
  {
    "path": "packages/useUnmount/index.ts",
    "chars": 212,
    "preview": "import { onScopeDispose, onUnmounted, version } from 'vue';\n\nconst useUnmount = (fn:any) => {\n  const unmounted = onScop"
  },
  {
    "path": "packages/useVirtualList/index.md",
    "chars": 1854,
    "preview": "# useVirtualList\n\n长列表虚拟化列表的 Hook,用于解决展示海量数据渲染时首屏渲染缓慢和滚动卡顿问题。\n\n\n## 使用\n\n```\n<template>\n  <div class=\"hello\">\n    <button\n "
  },
  {
    "path": "packages/useVirtualList/index.ts",
    "chars": 3610,
    "preview": "import { reactive, ref, Ref } from 'vue';\n\nexport interface OptionType {\n    itemHeight: number | ((index: number) => nu"
  },
  {
    "path": "packages/useWebSocket/index.md",
    "chars": 2933,
    "preview": "# useWebSocket\n\n用于处理 WebSocket 的 Hook。\n\n## 基础使用\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>webScoket状态: {{readySt"
  },
  {
    "path": "packages/useWebSocket/index.ts",
    "chars": 3470,
    "preview": "import { Ref, ref } from \"vue\";\nimport useTimeout from '../useTimeout'\n\ninterface UseWebSocketOptions {\n    manual?: boo"
  },
  {
    "path": "packages/utils/index.ts",
    "chars": 2767,
    "preview": "type Fn = (...[]: any[])=> any; \n\n/**\n * 防抖\n * @param fn \n * @param delay \n * @returns \n */\nconst debounce = (fn:Fn,dela"
  },
  {
    "path": "packages/utils/memoryCache.ts",
    "chars": 2197,
    "preview": "\ninterface Timer {\n    [key:string]: NodeJS.Timeout\n}\ninterface Options{\n    maxCache?: number\n}\n\nclass MemoryCache{\n   "
  },
  {
    "path": "packages/utils/testingHelpers.ts",
    "chars": 137,
    "preview": "export function sleep(time:number) {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve(true);\n    }"
  },
  {
    "path": "rollup.config.js",
    "chars": 2170,
    "preview": "import { nodeResolve } from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport { ters"
  },
  {
    "path": "tsconfig.json",
    "chars": 7012,
    "preview": "{\n  \"compilerOptions\": {\n    /* Visit https://aka.ms/tsconfig.json to read more about this file */\n\n    /* Basic Options"
  }
]

About this extraction

This page contains the full source code of the yanzhandong/v3hooks GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 108 files (155.5 KB), approximately 46.7k tokens, and a symbol index with 87 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!