[
  {
    "path": ".gitignore",
    "content": "# 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\npackages/*/coverage\npackages/*/.nyc_output\n\n# IDEs and editors\n.idea\n.project\n.classpath\n.c9\n*.launch\n.settings/\n*.sublime-workspace\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img align=\"center\" style=\"width:200px\" src=\"https://img-steward-online.goodaa.com.cn/568d4fc225de4b7e8067f9505ce97acb.png\"/>\n</p><br/>\n<h1 align=\"center\"><b>V3Hooks</b></h1>\n<h4 align=\"center\">针对 Vue3 的实用Hooks集合</h4>\n\n\n<h4  align=\"center\">\n  V3Hooks也可以说是<a href=\"https://github.com/alibaba/hooks\">ahooks</a>的Vue实现，绝大部分Api是保持一致的\n</h4>\n<br>\n\n## 🔨安装\n<h4 align=\"center\">\n  <pre>npm i <a href=\"https://www.npmjs.com/package/v3hooks\">v3hooks</a> --save</pre>\n  <i>or</i>\n  <pre>yarn add <a href=\"https://www.npmjs.com/package/v3hooks\">v3hooks</a></pre>\n</h4>\n\n## 🏃文档\n<h4 align=\"center\">\n  <a href=\"https://yanzhandong868.gitbook.io/v3hooks/\">使用文档</a>\n</h4>\n\n<br>\n\n## ⚡使用\n\n- **Async**\n  - `useRequest` — 一个完整的管理异步数据请求的Hook,<a href=\"https://ahooks.js.org/zh-CN/hooks/async\">aHook useRequest</a>的Vue3实现，Api完全一致，如果你使用过aHook这将无缝衔接到Vue3.\n- **Side**\n  - `useDebounce` — 用于处理防抖值的 Hook.\n  - `useDebounceFn` — 用于处理防抖函数的 Hook.\n  - `useThrottle` — 用于处理节流值的 Hook.\n  - `useThrottleFn` — 用于处理节流函数的 Hook.\n  - `useInterval` — 用于处理interval的 Hook.\n  - `useTimeout` — 用于处理timeout的 Hook.\n<!-- - **Browser** -->\n- **State**\n  - `useToggle` — 用于在两个状态值间切换的 Hook.\n  - `useBoolean` — 优雅的管理 boolean 值的 Hook.\n  - `useDate` — 用于处理时间格式化 Hook.\n  - `useLocalStorage` — 简单高效管理localStorage的 Hook.\n  - `useSessionStorage` — 简单高效管理SessionStorage的 Hook.\n  - `useCookie` — 用于管理本地Cookie Hook.\n  - `useNetwork` — 用于获取网络状态 Hook.\n  - `useSet` — 用于管理Set的 Hook.\n  - `useMap` — 用于管理Map的 Hook.\n  - `useWebSocket` — 用于处理 WebSocket 的 Hook。\n  <!-- - `useRouteQuery` — 用于获取url query值的 Hook. -->\n- **UI**\n  - `useVirtualList` — 用于长列表虚拟化列表的 Hook.\n  - `useDynamicList` — 用于管理列表状态 Hook.\n  - `useMediaQuery` — 用于监听 mediaQuery 状态的 Hook。\n  - `useExternal` — 用于加载异步资源的 Hook.\n  - `useFullscreen` — 一个用于处理 dom 全屏的 Hook.\n  - `useDocumentVisibility` — 可以获取页面可见状态的 Hook.\n  - `useTextSelection` — 实时获取用户当前选取的文本内容及位置Hook.\n  - `useQRCode` — 用来生成二维码的Hook.\n- **Advanced**\n  - `useLockFn` — 用于增加异步函数增加竞态锁，防并发 Hook.\n\n\n## 常见问题\n常见问题请见 [文档](https://github.com/yanzhandong/v3hooks/blob/master/docs/question.md)\n\n\n## 🤝 感谢\n如果这个项目对您有帮助，欢迎Star"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = {\n  presets: [\n    [\n      '@babel/preset-env',\n      {\n        modules: false,\n      },\n    ],\n    '@babel/preset-react',\n  ],\n};\n"
  },
  {
    "path": "docs/question.md",
    "content": "## 常见问题\n\n### setup中使用data为undefined\n因为data是被Ref嵌套的响应式, 直接return到Template中使用没问题，如果想要在Setup中使用需要嵌套一层watchEffect来获取异步数据。\n```\nconst { data, run, cancel } = useRequest(\n    () => {\n      return axios.get(\n          `https://xxx.com/application/list`\n      );\n    }\n);\n\nwatchEffect(()=>{\n  console.log( data?.value );\n})\n```\n可以看到下面demo中React中Ahooks也是需要这么使用的\nhttps://codesandbox.io/s/determined-glitter-m77g6?file=/src/App.js\n### 在tsx中使用useRequest\n  在一个tsx项目中使用useRequest，不能直接使用data返回值到html中，因为data是一个被Ref嵌套的响应式数据，在html中使用便利需要使用.value来获取真实数据，可以参考以下例子做法\n```\nimport { defineComponent, watchEffect, ref } from 'vue';\nimport { useRequest,useTimeout } from 'v3hooks';\n\n// 模拟列表请求\nconst mockRequest = ()=>{\n  return new Promise((resolve,reject)=>{\n    useTimeout(()=>{\n      resolve({code:200,data:[{name:'aaa'},{name:'bbbbb'},{name:'ccccc'}]})\n    },ref(500))\n  })\n};\n\ninterface MockDataItem{\n  name:string\n}\n\nexport default defineComponent({\n  name: 'App',\n  setup() {\n    const content = ref<MockDataItem[]>([]);\n    const { data } = useRequest<any>(() => mockRequest() );\n\n    watchEffect(()=>{\n      if(data?.value && data.value.code === 200){\n        content.value = data.value.data;\n      }\n    })\n  \n    return () => (\n      <>\n        <h1>姓名表</h1>\n        {\n          content.value.map((item:MockDataItem)=>{\n            return (\n              <h3>{item.name}</h3>\n            )\n          })\n        }\n      </>\n    );\n  }\n});\n```"
  },
  {
    "path": "example/.eslintignore",
    "content": "dist"
  },
  {
    "path": "example/README.md",
    "content": "# example\n\n## Project setup\n```\nnpm install\n```\n\n### Compiles and hot-reloads for development\n```\nnpm run serve\n```\n\n### Compiles and minifies for production\n```\nnpm run build\n```\n\n### Lints and fixes files\n```\nnpm run lint\n```\n\n### Customize configuration\nSee [Configuration Reference](https://cli.vuejs.org/config/).\n"
  },
  {
    "path": "example/babel.config.js",
    "content": "module.exports = {\n  presets: [\n    '@vue/cli-plugin-babel/preset'\n  ]\n}\n"
  },
  {
    "path": "example/jsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"esnext\",\n    \"baseUrl\": \"./\",\n    \"moduleResolution\": \"node\",\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ]\n    },\n    \"lib\": [\n      \"esnext\",\n      \"dom\",\n      \"dom.iterable\",\n      \"scripthost\"\n    ]\n  }\n}\n"
  },
  {
    "path": "example/package.json",
    "content": "{\n  \"name\": \"example\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve\",\n    \"build\": \"vue-cli-service build\",\n    \"lint\": \"vue-cli-service lint\"\n  },\n  \"dependencies\": {\n    \"@vueuse/core\": \"^5.0.3\",\n    \"axios\": \"^0.21.1\",\n    \"core-js\": \"^3.8.3\",\n    \"v3hooks\": \"^1.5.0\",\n    \"vue\": \"^3.0.4\",\n    \"vue-router\": \"4.0.3\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.12.16\",\n    \"@babel/eslint-parser\": \"^7.12.16\",\n    \"@typescript-eslint/eslint-plugin\": \"^4.15.1\",\n    \"@typescript-eslint/parser\": \"^4.15.1\",\n    \"@vue/cli-plugin-babel\": \"~5.0.0-beta.2\",\n    \"@vue/cli-plugin-eslint\": \"~5.0.0-beta.2\",\n    \"@vue/cli-plugin-typescript\": \"~4.5.0\",\n    \"@vue/cli-service\": \"~5.0.0-beta.2\",\n    \"@vue/compiler-sfc\": \"^3.0.4\",\n    \"@vue/eslint-config-typescript\": \"^7.0.0\",\n    \"eslint\": \"^7.20.0\",\n    \"eslint-plugin-vue\": \"^7.2.0\",\n    \"typescript\": \"~4.1.5\"\n  },\n  \"eslintConfig\": {\n    \"root\": true,\n    \"env\": {\n      \"node\": true\n    },\n    \"extends\": [\n      \"plugin:vue/vue3-essential\",\n      \"eslint:recommended\",\n      \"@vue/typescript\"\n    ],\n    \"parserOptions\": {\n      \"parser\": \"@typescript-eslint/parser\"\n    },\n    \"rules\": {}\n  },\n  \"browserslist\": [\n    \"> 1%\",\n    \"last 2 versions\",\n    \"not dead\",\n    \"not ie 11\"\n  ]\n}\n"
  },
  {
    "path": "example/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n    <link rel=\"icon\" href=\"<%= BASE_URL %>favicon.ico\">\n    <title><%= htmlWebpackPlugin.options.title %></title>\n  </head>\n  <body>\n    <noscript>\n      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>\n    </noscript>\n    <div id=\"app\"></div>\n    <!-- built files will be auto injected -->\n  </body>\n</html>\n"
  },
  {
    "path": "example/src/App.vue",
    "content": "<template>\n  <router-view v-slot=\"{ Component }\">\n    <Suspense>\n      <component :is=\"Component\" />\n    </Suspense>\n  </router-view>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n  name: 'App'\n});\n</script>\n\n<style>\n*{\n  margin: 0;\n  padding: 0;\n}\nbutton{\n  display: block;\n  padding: 10px 20px;\n}\n</style>\n"
  },
  {
    "path": "example/src/components/HelloWorld.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <p>{{ loading ? \"loading\" : data }}</p>\n    <button @click=\"run\">发起</button>\n    <button @click=\"cancel\">取消</button>\n    <button @click=\"handleMutate\">突变测试</button>\n    <button @click=\"handleRefreshDeps\">refreshDeps测试</button>\n\n\n    <!-- useDebounceFn demo测试 -->\n    <p style=\"marginTop: 16\"> Clicked count: {{debounceFnValue}} </p>\n    <button type=\"button\" @click=\"debounceFnRun\">\n      useDebounceFn测试\n    </button>\n\n    <!-- useDebounce demo测试 -->\n    <br/>\n    <input\n      v-model=\"debounceCurrValue\"\n      placeholder=\"Typed value\"\n      style=\" width: 280 \"\n    />\n    <p style=\"marginTop: 16\">DebouncedValue: {{debounceValue}}</p>\n\n    <!-- useThrottleFn demo测试 -->\n    <p style=\"marginTop: 16\"> Clicked count: {{throttleFnValue}} </p>\n    <button type=\"button\" @click=\"throttleFnRun\">\n      useThrottleFn测试\n    </button>\n\n    <!-- useThrottle demo测试 -->\n    <br/>\n    <input\n      v-model=\"throttleCurrValue\"\n      placeholder=\"Typed value\"\n      style=\" width: 280 \"\n    />\n    <p style=\"marginTop: 16\">throttleValue: {{throttleValue}}</p>\n\n    <!-- useToggle测试 -->\n    <p>useToggleDemoState: {{useToggleDemoState}}</p>\n    <button @click=\"handleUseTToggle\">直接设置</button>\n    <button @click=\"useTToggle\">useTToggle</button>\n    <button @click=\"useTSetLeft\">useTSetLeft</button>\n    <button @click=\"useTSetCenter\">useTSetCenter</button>\n    <button @click=\"useTSetRight\">useTSetRight</button>\n\n    <p>{{ useBooleanState }}</p>\n    <button @click=\"useBooleanToggle\">toggle</button>\n    <button @click=\"setTrue\">setTrue</button>\n    <button @click=\"setFalse\">setFalse</button>\n\n\n    <!-- useVirtualList测试 -->\n    <br/>\n    <button\n      style=\"margin-top:30px;\"\n      type=\"button\"\n      @click=\"handleVirtualScrollTo\"\n    >\n      scroll to\n    </button>\n    <div\n      :ref=\"virtualContainerProps.ref\" \n      @scroll=\"virtualContainerProps.onScroll\"\n      style=\"height: 300px; overflow: auto\"\n    >\n      <div :style=\"virtualWrapperStyle\">\n        <div \n          v-for=\"active in virtualList\" \n          :key=\"active\"\n          style=\"height:59px;border-bottom: 1px solid #cccccc;background-color: white\"\n        >\n          {{ active}}\n        </div>\n      </div>\n    </div>\n    \n  </div>\n</template>\n\n<script lang=\"ts\">\n\nimport { \n  useRequest, \n  useDebounce, \n  useDebounceFn,\n  useThrottleFn,\n  useThrottle,\n  useToggle,\n  useBoolean,\n  useVirtualList\n} from \"../../dist/index.js\";\n\nimport { ref } from 'vue';\nimport axios from \"axios\";\n\nexport default {\n  setup() {\n\n    // useRequest demo测试\n    const refreshTest = ref(11);\n    // const refreshTest2 = ref(11);\n\n    const { data, run, loading, cancel, mutate } = useRequest(\n      (mobile:number, code:string) => {\n        return axios.post(\n          `http://localhost:8080?mobile=${mobile}&code=${code}`\n        );\n      },\n      {\n        // manual: true,\n        defaultParams:[\n          15652922446,\n          '0000'\n        ],\n        refreshDeps: [ refreshTest ],\n        refreshOnWindowFocus: true\n      }\n    );\n    // run(1562922446,'0000');\n    \n    const handleRefreshDeps = ()=>{\n      refreshTest.value = Math.random();\n      console.log(refreshTest);\n    }\n\n\n    const handleMutate = ()=>{\n      mutate({\n        code: Math.random(),\n        data: {\n          id: +new Date()\n        },\n      });\n    };\n\n\n    // useDebounceFn demo测试\n    const debounceFnValue = ref<number>(1);\n    const { run:debounceFnRun } = useDebounceFn(()=>{\n      debounceFnValue.value++\n    },1000)\n\n    // useDebounce demo测试\n    const debounceCurrValue = ref(1);\n    const debounceValue = useDebounce(debounceCurrValue);\n\n    // useThrottleFn demo测试\n    const throttleFnValue = ref(1);\n    const { run:throttleFnRun } = useThrottleFn(()=>{\n      throttleFnValue.value++\n    })\n\n    // useThrottle demo测试\n    const throttleCurrValue = ref(1);\n    const throttleValue = useThrottle(throttleCurrValue);\n\n    //useToggle 测试\n    const [ useToggleDemoState, [ useTToggle, useTSetLeft, useTSetCenter, useTSetRight]] = useToggle('left','center','right');\n    const handleUseTToggle = ()=>{\n      useTToggle('center')\n    };\n\n    //useBoolean 测试\n    const [ useBooleanState,{ toggle: useBooleanToggle, setTrue, setFalse}] = useBoolean();\n\n\n    // useVirtualList测试\n    \n    const {\n      list: virtualList,\n      wrapperStyle: virtualWrapperStyle,\n      containerProps: virtualContainerProps,\n      scrollTo: virtualScrollTo\n    } =useVirtualList(Array.from(Array(99999).keys()),{\n      itemHeight: 60,\n      overscan: 10\n    })\n\n    const handleVirtualScrollTo = ()=>{\n      virtualScrollTo(22);  \n    }\n\n\n    return {\n      loading,\n      data,\n      run,\n      cancel,\n      handleMutate,\n      handleRefreshDeps,\n\n      debounceFnValue,\n      debounceFnRun,\n\n      debounceCurrValue,\n      debounceValue,\n\n      throttleFnValue,\n      throttleFnRun,\n\n      throttleCurrValue,\n      throttleValue,\n\n      useToggleDemoState,\n      handleUseTToggle,\n      useTToggle,\n      useTSetLeft,\n      useTSetCenter,\n      useTSetRight,\n\n      useBooleanState,\n      useBooleanToggle,\n      setTrue,\n      setFalse,\n\n      virtualContainerProps,\n      virtualWrapperStyle,\n      virtualList,\n      handleVirtualScrollTo\n    };\n  },\n};\n</script>\n\n<!-- Add \"scoped\" attribute to limit CSS to this component only -->\n<style scoped>\nh3 {\n  margin: 40px 0 0;\n}\nul {\n  list-style-type: none;\n  padding: 0;\n}\nli {\n  display: inline-block;\n  margin: 0 10px;\n}\na {\n  color: #42b983;\n}\n</style>\n"
  },
  {
    "path": "example/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport App from './App.vue'\nimport { createRouter } from './router'\n\nconst app = createApp(App);\nconst router = createRouter(); \napp.use(router);\napp.mount('#app')\n"
  },
  {
    "path": "example/src/pages/home/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <h4>第一测试</h4>\n    <p>{{ loading ? \"loading\" : data }}</p>\n    <button @click=\"run\">发起</button>\n    <button @click=\"cancel\">取消</button>\n    <button @click=\"handleMutate\">突变测试</button>\n    <button @click=\"handleRefreshDeps\">refreshDeps测试</button>\n    \n    <h4>第二Cache测试</h4>\n    <p>{{ loading2 ? \"loading\" : data2 }}</p>\n    <button @click=\"run2\">发起</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\n\nimport { \n  useRequest, \n  useTimeout\n} from \"v3hooks\";\n\nimport { ref } from 'vue';\n// import axios from \"axios\";\n\n\n// 模拟列表请求\nconst mockRequest = ()=>{\n  return new Promise((resolve)=>{\n    console.log(1231)\n    useTimeout(()=>{\n      resolve({code:200,time:+(new Date()),data:[{name:'aaa'},{name:'bbbbb'},{name:'ccccc'}]})\n    },500)\n  })\n};\n\n\nexport default {\n  \n  \n  setup() {\n\n    // useRequest demo测试\n    const refreshTest = ref(11);\n    // const refreshTest2 = ref(11);\n    const isReady = ref(false);\n\n    const { data, run, loading, cancel, mutate } = useRequest(\n      () => {\n        // return axios.post(\n        //   `https://xxxx/auth/login`\n        // );\n        // 暂时使用mock\n        return mockRequest();\n      },\n      {\n        manual: true,\n        pollingInterval: 1000,\n        ready:isReady,\n        // loadingDelay: 1000,\n        refreshDeps: [ refreshTest ],\n        onSuccess(data){\n          console.log(data,'success')\n        },\n        refreshOnWindowFocus: true,\n      }\n    );\n\n    setTimeout(()=>{\n      console.log('isReady');\n      isReady.value = true\n    },1000)\n\n    const { data:data2, run:run2, loading:loading2 } = useRequest(\n      () => {\n        return mockRequest();\n      },\n      {\n        // manual: true,\n        refreshDeps: [ refreshTest ],\n        refreshOnWindowFocus: true,\n        cacheKey: 'mock1',\n        cacheTime: 6000,\n        staleTime: 3000\n      }\n    );\n    \n    const handleRefreshDeps = ()=>{\n      refreshTest.value = Math.random();\n      console.log(refreshTest);\n    }\n\n\n    const handleMutate = ()=>{\n      mutate({\n        code: Math.random(),\n        data: {\n          id: +new Date()\n        },\n      });\n    };\n\n\n\n\n    return {\n      loading,\n      data,\n      run,\n      cancel,\n      handleMutate,\n      handleRefreshDeps,\n\n\n      data2,\n      run2,\n      loading2\n    };\n  },\n};\n</script>\n\n<!-- Add \"scoped\" attribute to limit CSS to this component only -->\n<style scoped>\n* {\n  margin: 0 auto;\n  text-align: center;\n  margin-bottom: 10px;\n}\n</style>\n"
  },
  {
    "path": "example/src/pages/useBoolean/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div> {{useBooleanState}}</div>\n    <button @click=\"toggle\">toggle</button>\n    <button @click=\"setTrue\">setTrue</button>\n    <button @click=\"setFalse\">setFalse</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport {useBoolean } from \"../../../dist/index.js\";\nexport default {\n  \n  setup() {\n\n    //useBoolean 测试\n    const [ useBooleanState,{ toggle, setTrue, setFalse}] = useBoolean();\n    \n    return {\n      useBooleanState,\n      toggle,\n      setTrue,\n      setFalse\n    };\n  },\n};\n</script>\n\n<style scoped>\n  .hello{\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n  }\n</style>"
  },
  {
    "path": "example/src/pages/useCookie/index.vue",
    "content": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <p> value:{{ state }}</p>\n    <button @click=\"handlerUpdateState\">修改Cookie</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useCookie } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    // 获取query中的a\n    const state = useCookie('a',{\n        defaultValue: '1111',\n        watch: true,\n        path: '/useCookie',\n        expires: 1,\n\n    });\n\n    const handlerUpdateState = ()=>{\n      state.value = String( Math.random() );\n    };\n\n    // useVirtualList测试\n    return {\n      state,\n      handlerUpdateState\n    };\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useDate/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div> value:{{ data }}</div>\n    <button @click=\"handleUpdateTime\">无参数刷新</button>\n    <button @click=\"handleUpdateTimeParam\">有参数刷新</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useDate } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    const { data, refresh } = useDate(+new Date(),{\n        format: 'YYYY-MM-DD HH:mm:ss',\n        method: 'hour',\n        methodParam: 3\n    });\n\n    const handleUpdateTime = ()=>{\n        refresh();\n    }\n\n    const handleUpdateTimeParam = ()=>{\n        refresh('2021-7-16 12:17:00');\n    }\n\n    return {\n      data,\n      refresh,\n      handleUpdateTime,\n      handleUpdateTimeParam\n    };\n  },\n};\n</script>\n\n<style scoped>\n    .hello{\n        display:flex;\n        flex-direction: column;\n        justify-content: flex-start;\n        align-items: center;\n        margin-top: 30px;\n    }\n    \n</style>"
  },
  {
    "path": "example/src/pages/useDynamicList/index.vue",
    "content": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <div style=\"width: 60vw\">\n      <p \n        v-for=\" (active,index) in list\" \n        :key=\"getKey(index)\"\n        style=\"width: 100%;height:60px;border:1px solid #cccccc;line-height:60px;\"\n      > value:{{ active }} uuid:{{getKey(index)}}</p>\n    </div>\n    <div style=\"width:39vw\">\n      <button @click=\"()=> insert(0,Math.random())\">insert头部插入</button>\n      <button @click=\"()=> resetList(['a','b','c'])\">重置</button>\n      <button @click=\"()=> merge(0, [Math.random(),Math.random()])\">头部插入多个</button>\n      <button @click=\"()=> replace(1, Math.random())\">第二个替换</button>\n      <button @click=\"()=> remove(1)\">删除第二个</button>\n      <button @click=\"()=> move(0,2)\">一三互换位置</button>\n      <button @click=\"()=> push(Math.random())\">尾部插入</button>\n      <button @click=\"()=> pop()\">尾部删除</button>\n      <button @click=\"()=> unshift(Math.random())\">头部插入</button>\n      <button @click=\"()=> shift()\">头部删除</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useDynamicList } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    const defalutValue = ref(['a','b','c'])\n    const { \n      list,\n      insert,\n      resetList,\n      merge,\n      replace,\n      remove,\n      move,\n      getKey,\n      push,\n      pop,\n      unshift,\n      shift,\n    } = useDynamicList(defalutValue);\n\n    // useVirtualList测试\n    return {\n      list,\n      insert,\n      resetList,\n      merge,\n      replace,\n      remove,\n      move,\n      getKey,\n      push,\n      pop,\n      unshift,\n      shift,\n    };\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useExternal/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div id=\"aa\" ref=\"aa\"></div>\n    <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>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useExternal } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    const aa = ref();\n    const { load, unload } = useExternal(\n        // 'http://img-steward-online.goodaa.com.cn/449c2e5dd6a948e3af67b69c4ece907a.js',\n        // 'https://ahooks.js.org/useExternal/bootstrap-badge.css',\n        'https://img-steward-test.goodaa.com.cn/99b2b706a5b942c2bcbfe211107c7b80.jpeg',\n        (el)=>{ console.log(el) },\n        {\n            manual: true,\n            target: aa\n        }\n    );\n\n    setTimeout(()=>{\n        load()\n    },1000)\n\n    setTimeout(()=>{\n        unload()\n    },8000)\n    return {\n      aa\n    }\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useFullscreen/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div ref=\"fullScreen\" style=\"background: white\">\n        <p>是否全屏: {{isFullscreen}}</p>\n        <button @click=\"setFull\" id=\"a\">全屏</button>\n        <button @click=\"exitFull\">退出全屏</button>\n        <button @click=\"toggle\">toggle切换</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref, watch } from 'vue';\nimport { useFullscreen, useDocumentVisibility } from \"../../../dist/index.js\";\n\nexport default {\n  setup() {\n    const fullScreen = ref();\n    const documentVisibility = useDocumentVisibility();\n    const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen(fullScreen, {\n        onFull: ()=>{\n            console.log('全屏')\n        },\n        onExitFull: ()=>{\n            console.log('非全屏')\n        }\n    });\n    // const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen();\n    // const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen(document.body);\n\n    watch(\n      documentVisibility,\n      (value)=>{\n        if(value) console.log('重新触发active')\n      }\n    )\n    \n    // useVirtualList测试\n    return {\n      isFullscreen,\n      setFull,\n      exitFull,\n      toggle,\n      fullScreen\n    };\n  }\n};\n</script>"
  },
  {
    "path": "example/src/pages/useInterval/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div>value:{{ data }}</div>\n    <button @click=\"clear\">关闭</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useInterval,useTimeout } from \"../../../dist/index.js\";\n\n\nexport default {\n  \n  setup() {\n    const data = ref(1);\n    let delay = ref<null | number>(3000);\n\n    useInterval(()=>{\n      data.value++\n    },delay);\n\n    const clear = ()=>{ \n      delay.value = null;\n    };\n\n    useTimeout(()=>{\n      console.log('fuck')\n    },delay)\n\n    useTimeout(()=>{\n      console.log('fuck2')\n    },1000)\n\n    return {\n      data,\n      clear\n    };\n  },\n};\n</script>\n"
  },
  {
    "path": "example/src/pages/useLocalStorage/index.vue",
    "content": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <div style=\"width: 60vw\">\n      <p> value:{{ state }}</p>\n    </div>\n    <div style=\"width:39vw\">\n      <button @click=\"handleUpdate\">更新storage</button>\n      <button @click=\"handleDelete\">删除storage</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useLocalStorage } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    const state = useLocalStorage('useLocalStorage');\n\n    const handleUpdate = ()=>{\n        state.value = { 'a': Math.random() };\n    };\n\n    const handleDelete = ()=>{\n        state.value = undefined;\n    };\n\n    // useVirtualList测试\n    return {\n      state,\n      handleUpdate,\n      handleDelete\n    };\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useLockFn/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div>value:{{ data }}</div>\n    <button @click=\"submit\">submit</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useLockFn } from \"../../../dist/index.js\";\n\nfunction mockApiRequest() {\n  return new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(1);\n    }, 2000);\n  });\n}\n\nexport default {\n  \n  setup() {\n    const data = ref(1);\n    const submit = useLockFn(async ()=> {\n        await mockApiRequest();\n        data.value++\n    });\n\n    return {\n        data,\n      submit,\n    };\n  },\n};\n</script>\n\n<style scoped>\n.hello {\n  display: flex;\n  flex-direction: column;\n  justify-content: flex-start;\n  align-items: center;\n  margin-top: 30px;\n}\n</style>"
  },
  {
    "path": "example/src/pages/useMediaQuery/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div>\n      <p> value:{{ state }}</p>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { watch } from 'vue'\nimport { useMediaQuery } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    const state = useMediaQuery('(min-width: 480px)');\n\n    watch(\n      state,\n      (value)=>{\n        if(!value) console.log('已经是480px以下了')\n      }\n    )\n    \n    // useVirtualList测试\n    return {\n      state\n    };\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useNetwork/index.vue",
    "content": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <p> 网络状态:{{ state }}</p>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useNetwork } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    // 获取query中的a\n    const state = useNetwork();\n\n    // useVirtualList测试\n    return {\n      state\n    };\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useQRCode/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div> 二维码:</div>\n    <img :src=\"state\" alt=\"\">\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useQRCode } from \"../../../dist/index.js\";\n\nconst logo = require('../../assets/logo.png')\n\nexport default {\n  \n  setup() {\n    const text = ref<string>('https://www.baidu.com/')\n    // let text2 = 'https://www.baidu.com/';\n\n    const state = useQRCode(text,{\n      logo: logo.default,\n      colorDark: '#000000'\n    });\n\n    setTimeout(()=>{\n      text.value = 'https://www.acfun.cn/'\n    },2000)\n\n    return {\n      state\n    };\n  },\n};\n</script>\n\n<style scoped>\n  .hello{\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n  }\n</style>"
  },
  {
    "path": "example/src/pages/useRouteQuery/index.vue",
    "content": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <p> value:{{ state }}</p>\n    <button @click=\"handlerUpdateState\">修改query</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useRouteQuery } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    // 获取query中的a\n    const state = useRouteQuery('a');\n\n    // console.log(state.value)\n\n    const handlerUpdateState = ()=>{\n      state.value = String( Math.random() );\n    };\n\n    // useVirtualList测试\n    return {\n      state,\n      handlerUpdateState\n    };\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useSessionStorage/index.vue",
    "content": "<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <div style=\"width: 60vw\">\n      <p> value:{{ state }}</p>\n    </div>\n    <div style=\"width:39vw\">\n      <button @click=\"handleUpdate\">更新storage</button>\n      <button @click=\"handleDelete\">删除storage</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useSessionStorage } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    const state = useSessionStorage('useLocalStorage',{a:111});\n\n    const handleUpdate = ()=>{\n        state.value = { 'a': Math.random() };\n    };\n\n    const handleDelete = ()=>{\n        state.value = undefined;\n    };\n\n    // useVirtualList测试\n    return {\n      state,\n      handleUpdate,\n      handleDelete\n    };\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useSetAndUseMap/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div>\n      <p> value:{{ state }}</p>\n      <button @click=\"()=> add(Math.random())\">add</button>\n    </div>\n\n    <div>\n      <p> value:{{ state2 }}</p>\n      <button @click=\"()=> set('1',Math.random())\">set</button>\n      <button @click=\"()=> remove('1')\">remove</button>\n      <button @click=\"()=> setAll([ ['1','111'], ['2','2222'] ])\">setAll</button>\n    </div>\n    \n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useSet, useMap } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    const [state , { add } ] = useSet();\n    const [state2, { set, setAll, remove }] = useMap([\n      ['1','321']\n    ]);\n\n    // const add = ()=>{};\n\n    // useVirtualList测试\n    return {\n      state,\n      add,\n\n      state2,\n      set,\n      setAll,\n      remove\n    };\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useTextSelection/index.vue",
    "content": "<template>\n  <div style=\"text-align: center\">\n    <p ref=\"p\"> 可选择区域: 123111111111111aaaaaaaaaaabbbbbbbbbbb eeeeeeeeeeeeeeee</p>\n    <p>已选择的值：{{ text }}</p>\n    <p>位置信息：rect: {{ rect }}</p>\n    <p>left: {{ rect.left }}</p>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useTextSelection } from \"../../../dist/index.js\";\n\nexport default {\n  setup() {\n    const p = ref();\n    const { text, rect } = useTextSelection();\n\n    return {\n        text,\n        p,\n        rect\n    };\n  },\n};\n</script>"
  },
  {
    "path": "example/src/pages/useToggle/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div> {{state}}</div>\n    <button @click=\"toggle\">toggle</button>\n    <button @click=\"setToggle\">setToggle</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useToggle,useTimeout } from \"../../../dist/index.js\";\nexport default {\n  \n  setup() {\n\n    const platform = ref<string>('安装 App');\n    const platform2 = ref<string>('安装中...');\n    const [state, [toggle]] = useToggle(platform, platform2,'不安装');\n\n    useTimeout(() => {\n      platform.value = `安装 ios App`\n      platform2.value = '安装中2....'\n    }, ref(3000));\n\n    const setToggle = ()=>{\n      toggle(platform)\n    }\n    \n    return {\n      state,\n      toggle,\n      setToggle\n    };\n  },\n};\n</script>\n\n<style scoped>\n  .hello{\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n  }\n</style>"
  },
  {
    "path": "example/src/pages/useVirtualList/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <button\n      type=\"button\"\n      @click=\"handleVirtualScrollTo\"\n    >\n      跳转到第222排\n    </button>\n    <div\n      :ref=\"containerProps.ref\"\n      @scroll=\"containerProps.onScroll\"\n      class=\"container\"\n    >\n      <div :style=\"wrapperStyle\" class=\"wrapper\">\n        <div\n          v-for=\"active in list\"\n          :key=\"active\"\n          class=\"item\"\n        >\n          {{active}}\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useVirtualList } from \"../../../dist/index.js\";\n\nexport default {\n  \n  data(){\n    return {\n    }\n  },\n  setup() {\n    // useVirtualList测试\n\n    const { list, wrapperStyle, containerProps, scrollTo } = useVirtualList(\n      Array.from(Array(9999).keys()),\n      {\n        itemHeight: 60,\n        overscan: 10,\n      }\n    );\n\n    const handleVirtualScrollTo = () => {\n      scrollTo(222);\n    };\n\n    return {\n      list,\n      wrapperStyle,\n      containerProps,\n      handleVirtualScrollTo,\n    };\n  },\n};\n</script>\n\n\n<style scoped>\n::-webkit-scrollbar {display:none}\n.hello{\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n.container{\n  height: calc(100vh - 130px); \n  overflow-y: auto;\n  margin-top: 30px; \n}\n.wrapper{\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n.item{\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  width: 800px;\n  height: 52px;\n  border: 1px solid #cccccc;\n  margin-bottom: 8px;\n  background-color: white;\n  position: relative;\n}\nbutton{\n  margin-top: 30px;\n}\n</style>"
  },
  {
    "path": "example/src/pages/useWebSocket/index.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <div>webScoket状态： {{readyState}}</div>\n    <button @click=\"connect\">连接webScoket</button>\n    <button @click=\"disconnect\">关闭webScoket</button>\n    <button @click=\"handleSendMessage\">发送消息</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useWebSocket } from \"../../../dist/index.js\";\nimport { watchEffect } from '@vue/runtime-core';\nexport default {\n  \n  setup() {\n\n    const { \n      readyState,\n      latestMessage,\n      disconnect,\n      sendMessage,\n    } = useWebSocket('ws://82.157.123.54:9010/ajaxchattest',{\n      onMessage(event){\n        console.log(event)\n      }\n    })\n\n    const handleSendMessage = ()=>{\n      sendMessage('hello v3hooks')\n    }\n\n    watchEffect(()=>{\n      console.log(latestMessage.value)\n    })\n\n    return {\n      readyState,\n      disconnect,\n      handleSendMessage\n    };\n  },\n};\n</script>\n\n<style scoped>\n  .hello{\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n  }\n</style>"
  },
  {
    "path": "example/src/router.ts",
    "content": "import {\n    createRouter as _createRouter,\n    createWebHashHistory\n} from 'vue-router'\n\n// Auto generates routes from vue files under ./pages\n// https://vitejs.dev/guide/features.html#glob-import\n// @ts-ignore\n// const pages = import.meta.glob('./pages/*/index.vue')\n\n// const routes = Object.keys(pages).map((path) => {\n//     // @ts-ignore\n//     const name = path.match(/\\.\\/pages(.*)\\/index\\.vue$/)[1].toLowerCase();\n//     console.log(name,'name');\n//     return {\n//         path: name === '/home' ? '/' : name,\n//         component: pages[path] // () => import('./pages/*.vue')\n//     }\n// })\n\nexport function createRouter() {\n    return _createRouter({\n        // use appropriate history implementation for server/client\n        // import.meta.env.SSR is injected by Vite.\n        history: createWebHashHistory(),\n        routes:[\n            {\n                path:'/',\n                component: () => import('./pages/home/index.vue')\n            },\n            {\n                path:'/useVirtualList',\n                component: () => import('./pages/useVirtualList/index.vue')\n            },\n            {\n                path:'/useDynamicList',\n                component: () => import('./pages/useDynamicList/index.vue')\n            },\n            {\n                path:'/useLocalStorage',\n                component: () => import('./pages/useLocalStorage/index.vue')\n            },\n            {\n                path:'/useSessionStorage',\n                component: () => import('./pages/useSessionStorage/index.vue')\n            },\n            // {\n            //     path:'/useRouteQuery',\n            //     component: () => import('./pages/useRouteQuery/index.vue')\n            // },\n            {\n                path:'/useCookie',\n                component: () => import('./pages/useCookie/index.vue')\n            },\n            {\n                path:'/useDate',\n                component: () => import('./pages/useDate/index.vue')\n            },\n            {\n                path:'/useNetwork',\n                component: () => import('./pages/useNetwork/index.vue')\n            },\n            {\n                path:'/useLockFn',\n                component: () => import('./pages/useLockFn/index.vue')\n            },\n            {\n                path:'/useSetAndUseMap',\n                component: () => import('./pages/useSetAndUseMap/index.vue')\n            },\n            {\n                path:'/useMediaQuery',\n                component: () => import('./pages/useMediaQuery/index.vue')\n            },\n            {\n                path:'/useExternal',\n                component: () => import('./pages/useExternal/index.vue')\n            },\n            {\n                path:'/useFullscreen',\n                component: () => import('./pages/useFullscreen/index.vue')\n            },\n            {\n                path:'/useTextSelection',\n                component: () => import('./pages/useTextSelection/index.vue')\n            },\n            {\n                path:'/useInterval',\n                component: () => import('./pages/useInterval/index.vue')\n            },\n            {\n                path:'/useQRCode',\n                component: () => import('./pages/useQRCode/index.vue')\n            },\n            {\n                path:'/useToggle',\n                component: () => import('./pages/useToggle/index.vue')\n            },\n            {\n                path:'/useBoolean',\n                component: () => import('./pages/useBoolean/index.vue')\n            },\n            {\n                path:'/useWebSocket',\n                component: () => import('./pages/useWebSocket/index.vue')\n            }\n        ]\n    })\n}"
  },
  {
    "path": "example/src/shims-vue.d.ts",
    "content": "/* eslint-disable */\ndeclare module '*.vue' {\n  import type { DefineComponent } from 'vue'\n  const component: DefineComponent<{}, {}, any>\n  export default component\n}\n"
  },
  {
    "path": "example/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"importHelpers\": true,\n    \"moduleResolution\": \"node\",\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"sourceMap\": true,\n    \"baseUrl\": \".\",\n    \"types\": [\n      \"webpack-env\"\n    ],\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ]\n    },\n    \"lib\": [\n      \"esnext\",\n      \"dom\",\n      \"dom.iterable\",\n      \"scripthost\"\n    ]\n  },\n  \"include\": [\n    \"src/**/*.ts\",\n    \"src/**/*.tsx\",\n    \"src/**/*.vue\",\n    \"tests/**/*.ts\",\n    \"tests/**/*.tsx\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  },
  {
    "path": "example/vue.config.js",
    "content": "module.exports = {\n    devServer: {\n        port: 8081\n    }\n}"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  moduleFileExtensions: ['js', 'ts', 'jsx', 'tsx'],\n  transform: {\n    '^.+\\\\.(js|jsx)$': 'babel-jest',\n    '^.+\\\\.(ts|tsx)$': 'ts-jest',\n  },\n  testEnvironment: 'jsdom',\n  collectCoverageFrom: [\n    '<rootDir>/packages/**/*.{ts,tsx}',\n    '!**/node_modules/**',\n    '!<rootDir>/packages/__tests__/**/*',\n    '!<rootDir>/dist/**/*',\n  ],\n  coveragePathIgnorePatterns: ['/node_modules/', '/__fixtures__/', '/fixtures/', '/__tests__/helpers/', '/__tests__/utils/', '__mocks__'],\n  testMatch: ['**/__tests__/**/*.test.[jt]s?(x)'],\n  globals: {\n    'ts-jest': {\n      babelConfig: './babel.config.js',\n    },\n  }\n};"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"v3hooks\",\n  \"version\": \"1.5.0\",\n  \"author\": \"yanzhandong\",\n  \"description\": \"针对 Vue3 的实用Hooks集合\",\n  \"keywords\": [\n    \"vuehook\",\n    \"hook\",\n    \"vue3\",\n    \"vue3hook\",\n    \"v3hooks\",\n    \"vueHook\"\n  ],\n  \"homepage\": \"https://yanzhandong868.gitbook.io/v3hooks/\",\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.es.js\",\n  \"unpkg\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/yanzhandong/v3hooks.git\"\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"test\": \"jest\",\n    \"watch\": \"rollup -c --watch\",\n    \"build\": \"cross-env NODE_ENV=production rollup -c\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.16.12\",\n    \"@babel/plugin-transform-runtime\": \"^7.16.10\",\n    \"@babel/preset-env\": \"^7.16.11\",\n    \"@babel/preset-react\": \"^7.16.7\",\n    \"@rollup/plugin-babel\": \"^5.3.0\",\n    \"@rollup/plugin-commonjs\": \"^21.0.1\",\n    \"@rollup/plugin-node-resolve\": \"^13.1.3\",\n    \"@types/jest\": \"^27.4.0\",\n    \"@types/js-cookie\": \"^2.2.7\",\n    \"@vue/test-utils\": \"^2.0.0-rc.17\",\n    \"babel-jest\": \"^27.5.1\",\n    \"cross-env\": \"^7.0.3\",\n    \"jest\": \"^27.5.1\",\n    \"mockdate\": \"^3.0.5\",\n    \"rollup\": \"^2.52.6\",\n    \"rollup-plugin-dts\": \"^3.0.2\",\n    \"rollup-plugin-terser\": \"^7.0.2\",\n    \"rollup-plugin-typescript2\": \"^0.30.0\",\n    \"rollup-plugin-vue\": \"^6.0.0\",\n    \"ts-jest\": \"^27.1.3\",\n    \"tslib\": \"^2.3.0\",\n    \"typescript\": \"^4.3.5\",\n    \"vue\": \"^3.0.11\",\n    \"vue-router\": \"4.0.3\"\n  },\n  \"dependencies\": {\n    \"@babel/runtime\": \"^7.16.7\",\n    \"dayjs\": \"^1.10.5\",\n    \"easyqrcodejs\": \"^4.4.5\",\n    \"js-cookie\": \"^2.2.1\"\n  }\n}\n"
  },
  {
    "path": "packages/index.ts",
    "content": "import useRequest from './useRequest/index';\nimport useDate from './useDate/index';\nimport useDebounce from './useDebounce/index';\nimport useDebounceFn from './useDebounceFn/index';\nimport useThrottle from './useThrottle/index';\nimport useThrottleFn from './useThrottleFn/index';\nimport useBoolean from './useBoolean/index';\nimport useToggle from './useToggle/index';\nimport useVirtualList from './useVirtualList/index';\nimport useDynamicList from './useDynamicList/index';\nimport useLocalStorage from './useLocalStorage/index';\nimport useSessionStorage from './useSessionStorage/index';\nimport useNetwork from './useNetwork/index';\nimport useCookie from './useCookie/index';\nimport useLockFn from './useLockFn/index';\nimport useSet from './useSet/index';\nimport useMap from './useMap/index';\nimport useMediaQuery from './useMediaQuery/index';\nimport useExternal from './useExternal/index';\nimport useFullscreen from './useFullscreen/index';\nimport useDocumentVisibility from './useDocumentVisibility/index'\nimport useTextSelection from './useTextSelection/index'\nimport useInterval from './useInterval/index'\nimport useTimeout from './useTimeout/index'\nimport useQRCode from './useQRCode/index'\nimport useWebSocket from './useWebSocket/index'\nimport useUnmount from './useUnmount/index'\n\n// 暂时无法@/分包 未引入vue-router会导致都不能用\n// import useRouteQuery from './useRouteQuery/index';\n\n\nexport {\n    // Async\n    useRequest,\n\n    // Side\n    useDebounce,\n    useDebounceFn,\n    useThrottle,\n    useThrottleFn,\n    useInterval,\n    useTimeout,\n\n    // State\n    useBoolean,\n    useToggle,\n    useDate,\n    useCookie,\n    useLocalStorage,\n    useSessionStorage,\n    // useRouteQuery,\n    useNetwork,\n    useSet,\n    useMap,\n    useWebSocket,\n\n    // UI\n    useVirtualList,\n    useDynamicList,\n    useMediaQuery,\n    useExternal,\n    useFullscreen,\n    useDocumentVisibility,\n    useTextSelection,\n    useQRCode,\n\n    //Advanced\n    useLockFn,\n    useUnmount\n\n}"
  },
  {
    "path": "packages/useBoolean/index.md",
    "content": "# useBoolean\n\n优雅的管理 boolean 值的 Hook。\n\n\n## 使用\n\n```\n<template>\n    <div>\n      <p>{{ useBooleanState }}</p>\n      <button @click=\"useBooleanToggle\">toggle</button>\n      <button @click=\"setTrue\">setTrue</button>\n      <button @click=\"setFalse\">setFalse</button>\n    </div>\n</template>\n\n<script lang=\"ts\">\n\nimport { ref } from 'vue';\nimport { useBoolean } from 'v3hooks';\n\n\nexport default {\n  \n  \n\n  setup() {\n    //useToggle 测试\n    const [ useBooleanState,{ toggle: useBooleanToggle, setTrue, setFalse}] = useBoolean();\n\n\n    return {\n      useBooleanState,\n      useBooleanToggle,\n      setTrue,\n      setFalse\n    }\n  }\n}\n</script>\n```\n\nuseBoolean默认切换布尔值状态，也可以接收一个参数作为新的值。\n\n\n\n## Api\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| defaultValue | 可选项，传入默认的状态值\t | Ref<boolean> | false |\n\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state | 状态值 | - |\n| actions | 操作集合\t | Actions |\n\n### Actions\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| toggle | 触发状态更改的函数,可以接受一个可选参数修改状态值 | (value?: boolean) => void |\n| setTrue | 设置状态值为 true | () => void |\n| setFalse | 设置状态值为 false | () => void |"
  },
  {
    "path": "packages/useBoolean/index.ts",
    "content": "import { Ref } from 'vue';\nimport useToggle from '../useToggle';\n\n// 默认值\nconst defaultValue = false;\n\ninterface Actions{\n    toggle: ()=> void;\n    setTrue: ()=> void;\n    setFalse: ()=> void;\n}\n\nfunction useBoolean(\n    value?: boolean\n): [Ref<boolean>,Actions]\n/**\n * \n * @param defaultValue\n * @returns \n */\nfunction useBoolean (value?: boolean){\n\n    value = value || defaultValue;\n\n    const [ state, [ toggle ]] = useToggle(value,!value);\n    \n    const setTrue = () => toggle(true);\n    const setFalse = () => toggle(false);\n\n    const actions: Actions = { toggle, setTrue, setFalse }\n    return [state, actions ]\n}\n\nexport default useBoolean"
  },
  {
    "path": "packages/useCookie/index.md",
    "content": "# useCookie\n\n一个用来操作Cookie的 Hook 。\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <p> value:{{ state }}</p>\n    <button @click=\"handlerUpdateState\">修改Cookie</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useCookie } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    // 获取cookie中的a\n    const state = useCookie('a',{\n        defaultValue: '1111',\n        watch: true,\n        path: '/useCookie',\n        expires: 1,\n\n    });\n\n    const handlerUpdateState = ()=>{\n      state.value = String( Math.random() );\n    };\n\n    // useVirtualList测试\n    return {\n      state,\n      handlerUpdateState\n    };\n  },\n};\n</script>\n```\n\nuseCookie接受一个key是cookie中的键名。 修改返回的state可直接修改cookie.\n\n## 注意点\n* state等于undefined或者null可用于删除本地Cookie  例：`state.value = undefined;`\n\n## Api\n```\n\ninterface Options{\n    watch?: boolean,\n    defaultValue?: string | undefined,\n    expires?: number | Date,\n    path?: string,\n    domain?: string,\n    secure?: boolean,\n    sameSite?: 'strict' | 'lax' | 'none'\n}\n\nconst state = useCookieState(\n  key: string,\n  options?: Options,\n)\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| key | 存储在本地 cookie 的 key 值 | string\t | - |\n| options |可选项，配置 cookie 属性，详见 Options | Options\t | - |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state | 本地 cookie 值 | Ref<any> |\n\n\n### Options\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| defaultValue | 可选，定义 cookie 默认值，但不同步到本地 cookie | string\t | undefined |\n| expires |可选，定义 cookie 存储有效时间 | number | Date\t | - |\n| path | 可选，定义 cookie 可用的路径 | string \t| / |\n| domain | 可选，定义 cookie 可用的域，默认为 cookie 创建的域名 | string | - |\n| secure | 可选，cookie 传输是否需要 https 安全协议 | boolean | - |\n| sameSite | 可选，cookie 不能与跨域请求一起发送 | `strict` | `lax` | `none`\t | - |"
  },
  {
    "path": "packages/useCookie/index.ts",
    "content": "import { ref, watch as vueWatch } from 'vue';\nimport Cookies from 'js-cookie';\n\ninterface Options{\n    watch?: boolean,\n    defaultValue?: string | undefined,\n    expires?: number | Date,\n    path?: string,\n    domain?: string,\n    secure?: boolean,\n    sameSite?: 'strict' | 'lax' | 'none'\n}\n\nconst defaultOptions = {\n    watch: false,\n    defaultValue: undefined\n};\nconst useCookie = (key: string, options?:Options)=>{\n\n    const {\n        watch, \n        defaultValue\n    } = { ...defaultOptions, ...options };\n\n    const state = ref( Cookies.get(key) || defaultValue );\n\n    const setCookie = ( value: any )=>{\n        Cookies.set(key, value, { ...options });\n        state.value = value;\n    };\n\n    if( watch ){\n        vueWatch(\n            state,\n            (value)=>{\n                if( value === null || value === undefined ){\n                    Cookies.remove(key);\n                    return\n                }\n                setCookie(value);\n            },\n            {\n                deep: true\n            }\n        )\n    }\n    return state\n};\n\n\nexport default useCookie"
  },
  {
    "path": "packages/useDate/index.md",
    "content": "# useDate\n\n一个用来操作时间的 Hook 。\n\n内部使用了 dayjs 作为format工具\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div> value:{{ data }}</div>\n    <button @click=\"handleUpdateTime\">无参数刷新</button>\n    <button @click=\"handleUpdateTimeParam\">有参数刷新</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useDate } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    const { data, refresh } = useDate(+new Date(),{\n        format: 'YYYY-MM-DD HH:mm:ss',\n        method: 'hour',\n        methodParam: 3\n    });\n\n    const handleUpdateTime = ()=>{\n        refresh();\n    }\n\n    const handleUpdateTimeParam = ()=>{\n        refresh('2021-7-16 12:17:00');\n    }\n\n    return {\n      data,\n      refresh,\n      handleUpdateTime,\n      handleUpdateTimeParam\n    };\n  },\n};\n</script>\n```\n\nuseDate接受一个时间并根据options来返回格式化后的数据。 可根据返回的refresh来进行更新调用.\n\n其中method的参数具体使用可以参考dayjs的 [取值/赋值](https://dayjs.gitee.io/docs/zh-CN/get-set/millisecond)\n\n## Api\n```\n\ninterface Options{\n    format?: string\n    method?: 'format' | 'millisecond' | 'second' | 'minute' | 'hour' | 'date' |'day'  | 'month' | 'year',\n    methodParam?: number \n}\n\nfunction useDate(\n    value?: Value | undefined, \n    options?: Options\n): {\n    readonly data: any,\n    refresh: () => void;\n}\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| initialValue | 初始化的时间值 |  string - number - Date | Date |\n| options | 可选项，配置时间属性，详见 Options | Options | - |\n\n\n### Options\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| format | 针对日期格式化 | string | `YYYY-MM-DD HH:mm:ss` |\n| method | 获取时间的操作方法 | 见Api Options.method | `format` |\n| methodParam | 针对日期格式化的操作方法的参数 | number  | - |\n\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| data | 格式化后的时间值 | Ref<string> |\n| refresh | 格式化后的时间值 | (refreshValue)=> void  |"
  },
  {
    "path": "packages/useDate/index.ts",
    "content": "import { ref, readonly } from 'vue';\nimport dayjs from 'dayjs';\n\n\nconst defaultOptions ={\n    format: 'YYYY-MM-DD HH:mm:ss',\n    method: 'format'\n};\n\ntype Value =  string | number | Date;\n\ninterface Options{\n    format?: string\n    method?: 'format' | 'millisecond' | 'second' | 'minute' | 'hour' | 'date' |'day'  | 'month' | 'year',\n    methodParam?: number \n}\n\nfunction useDate(\n    value?: Value | undefined, \n    options?: Options\n): {\n    readonly data: any,\n    refresh: (refreshValue?: Value) => void;\n}\n\nfunction useDate( initialValue?:Value, options?: Options ){\n    \n    const state = ref<string>();\n    \n    let value = initialValue || +new Date();\n    const {\n        format,\n        method,\n        methodParam\n    } = { ...defaultOptions, ...options }\n\n\n    const refresh = ( refreshValue?:Value )=>{\n        console.log(refreshValue);\n        value = refreshValue || +new Date();\n        switch( method ){\n            case 'format':\n                state.value = dayjs(value).format(format);\n                break;\n            case undefined :\n                break;\n            default:\n                let data: any = dayjs(value);\n                if( methodParam ){\n                    data = data[method](methodParam);\n                    if( options && options.format ){\n                        data = data.format(format);\n                    }\n                }\n                state.value = data\n        }\n       \n    };\n\n    refresh();\n\n    const data = readonly( state);\n\n    return {\n        data,\n        refresh\n    }\n};\n\n\nexport default useDate"
  },
  {
    "path": "packages/useDebounce/index.md",
    "content": "# useDebounce\n\n用来处理防抖值的 Hook。\n\n## 使用\n\n```\n<template>\n    <div>\n        <input\n            v-model=\"debounceCurrValue\"\n            placeholder=\"Typed value\"\n            style=\"width: 280\"\n        />\n        <p style=\"marginTop: 16\">DebouncedValue: {{debounceValue}}</p>\n    </div>\n</template>\n\n<script lang=\"ts\">\n\nimport { ref } from 'vue';\nimport { useDebounce } from 'v3hooks';\n\n\nexport default {\n  \n  \n\n  setup() {\n    const debounceCurrValue = ref(1);\n    const debounceValue = useDebounce(debounceCurrValue,500);\n\n    return {\n        debounceCurrValue,\n        debounceValue,\n    }\n  }\n}\n</script>\n```\n\n使用useDebounce后，频繁设置debounceCurrValue不会立刻改变debounceValue， debounceValue只会在输入结束 500ms 后变化。\n\n## Api\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| value | 需要防抖的值 | any | - |\n| wait | 超时时间，单位为毫秒 | number | 1000 |"
  },
  {
    "path": "packages/useDebounce/index.ts",
    "content": "// import { debounce } from '../utils'\nimport useDebounceFn from '../useDebounceFn'\nimport { ref,Ref, watch } from 'vue';\n\n// 默认值\nconst defaultDelay = 1000;\n\n/**\n * 处理防抖值\n * @param value \n * @param delay \n * @returns \n */\nconst useDebounce = <T>( value: Ref<T> , delay?:number )=>{\n    \n    delay = delay || defaultDelay;\n\n    const res = ref<T>(value.value) as Ref<T>;\n\n    // 利用useDebounceFn来简化处理值\n    const { run } = useDebounceFn(()=> res.value = value.value ,delay);\n\n    watch(value,()=> run(),{ deep: true });\n\n    return res;\n};\n\n\nexport default useDebounce"
  },
  {
    "path": "packages/useDebounceFn/index.md",
    "content": "# useDebounceFn\n\n用来处理防抖函数的 Hook。\n\n\n## 使用\n\n```\n<template>\n    <div>\n      <p style=\"marginTop: 16\"> Clicked count: {{debounceFnValue}} </p>\n      <button type=\"button\" @click=\"debounceFnRun\">\n        useDebounceFn测试\n      </button>\n    </div>\n</template>\n\n<script lang=\"ts\">\n\nimport { ref } from 'vue';\nimport { useDebounceFn } from 'v3hooks';\n\n\nexport default {\n  \n  \n\n  setup() {\n    const debounceFnValue = ref<number>(1);\n    const { run:debounceFnRun } = useDebounceFn(()=>{\n      debounceFnValue.value++\n    },500)\n\n    return {\n      debounceFnValue,\n      debounceFnRun,\n    }\n  }\n}\n</script>\n```\n\n频繁调用 debounceFnRun，但只会在所有点击完成 500ms 后执行一次相关函数\n\n\n\n## Api\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| fn | 需要防抖执行的函数 | () => void | - |\n| wait | 超时时间，单位为毫秒 | number | 1000 |"
  },
  {
    "path": "packages/useDebounceFn/index.ts",
    "content": "import { Fn, debounce } from '../utils'\n\nconst defaultDelay = 1000;\n/**\n * 处理防抖函数\n * @param fn \n * @param delay \n * @returns \n */\nconst useDebounceFn = ( fn:Fn, delay?: number )=>{\n    const run = debounce( fn, typeof( delay ) === 'number'? delay : defaultDelay );\n    return { run };\n}\n\n\nexport default useDebounceFn"
  },
  {
    "path": "packages/useDocumentVisibility/index.md",
    "content": "# useDocumentVisibility\n\n可以获取页面可见状态的 Hook。\n\n## 使用Demo\n\n### 基础用法\n```vue\n<script lang=\"ts\">\nimport { watch } from 'vue';\nimport { useDocumentVisibility } from \"v3hooks\";\n\nexport default {\n  setup() {\n    const documentVisibility = useDocumentVisibility();\n    watch(\n      documentVisibility,\n      (value)=>{\n        if(value) console.log('重新触发active')\n      }\n    )\n  }\n};\n</script>\n```\n监听 document 的可见状态\n\n\n## Api\n```\nconst useDocumentVisibility: () => Ref<boolean>;\n```\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| documentVisibility |  判断 document 是否在是否处于可见状态\t | boolean |\n"
  },
  {
    "path": "packages/useDocumentVisibility/index.ts",
    "content": "import { ref, Ref } from 'vue';\n\nconst useDocumentVisibility = (): Ref<boolean> =>{\n    const documentVisibility = ref<boolean>( document.hidden );\n    let handler = ()=> {\n        documentVisibility.value = document.hidden;\n    };\n    document.addEventListener('visibilitychange',handler );\n    return documentVisibility\n}\n\nexport default useDocumentVisibility"
  },
  {
    "path": "packages/useDynamicList/index.md",
    "content": "# useDynamicList\n\n一个帮助你管理列表状态，并能生成唯一 key 的 Hook。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <div style=\"width: 60vw\">\n      <p \n        v-for=\" (active,index) in list\" \n        :key=\"getKey(index)\"\n        style=\"width: 100%;height:60px;border:1px solid #cccccc;line-height:60px;\"\n      > value:{{ active }} uuid:{{getKey(index)}}</p>\n    </div>\n    <div style=\"width:39vw\">\n      <button @click=\"()=> insert(0,Math.random())\">insert头部插入</button>\n      <button @click=\"()=> resetList(['a','b','c'])\">重置</button>\n      <button @click=\"()=> merge(0, [Math.random(),Math.random()])\">头部插入多个</button>\n      <button @click=\"()=> replace(1, Math.random())\">第二个替换</button>\n      <button @click=\"()=> remove(1)\">删除第二个</button>\n      <button @click=\"()=> move(0,2)\">一三互换位置</button>\n      <button @click=\"()=> push(Math.random())\">尾部插入</button>\n      <button @click=\"()=> pop()\">尾部删除</button>\n      <button @click=\"()=> unshift(Math.random())\">头部插入</button>\n      <button @click=\"()=> shift()\">头部删除</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useDynamicList } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    const defalutValue = ref(['a','b','c'])\n    const { \n      list,\n      insert,\n      resetList,\n      merge,\n      replace,\n      remove,\n      move,\n      getKey,\n      push,\n      pop,\n      unshift,\n      shift,\n    } = useDynamicList(defalutValue);\n\n    // useVirtualList测试\n    return {\n      list,\n      insert,\n      resetList,\n      merge,\n      replace,\n      remove,\n      move,\n      getKey,\n      push,\n      pop,\n      unshift,\n      shift\n    };\n  },\n};\n</script>\n```\n\nuseDynamicList接受一个数组，导出一个list及一系列操作数组的方法。\n\n\n## Api\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| initialValue | 列表的初始值\t\t | T[]\t | [] |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| list | 当前的列表\t | T[] |\n| resetList | 重新设置 list 的值\t\t | (list: T[]) => void |\n| insert | 在指定位置插入元素\t | (index: number, obj: T) => void\t |\n| merge | 在指定位置插入多个元素 | (index: number, obj: T[]) => void\t |\n| replace | 替换指定元素 | (index: number, obj: T) => void\t |\n| remove | 删除指定元素\t | (index: number) => void\t |\n| move | 移动元素\t | (oldIndex: number, newIndex: number) => void\t\t |\n| getKey | 获得某个元素的 uuid | (index: number) => number |\n| getIndex | 获得某个key的 index\t | (key: number) => number\t |\n| push | 在列表末尾添加元素 | (obj: T) => void\t|\n| pop | 移除末尾元素 | () => void\t |\n| unshift | 在列表起始位置添加元素 | (obj: T) => void\t |\n| shift | 移除起始位置元素 | () => void\t |\n\n"
  },
  {
    "path": "packages/useDynamicList/index.ts",
    "content": "import { ref, Ref, isRef } from 'vue';\n\nconst useDynamicList = <T = any>(initialValue: Ref<T[]>)=>{\n\n    let uuid = <number>(0);\n    const uuidKeys = ref<number[]>([]);\n\n    const setUUID = (index?:number)=>{\n        index = index === undefined ? uuidKeys.value.length : index;\n        uuidKeys.value.splice( index, 0 ,uuid++ );\n    };\n\n    (()=>{\n        initialValue.value.forEach(()=> setUUID() );\n    })()\n\n    const resetList = (resetList: Ref<T[]> | T[])=>{\n        uuidKeys.value = [];\n        if( isRef(resetList) ){\n            resetList.value.forEach(()=> setUUID() );\n            initialValue = resetList;\n            return\n        }\n        resetList.forEach(()=> setUUID() );\n        initialValue.value = resetList;\n    };\n\n    const insert = (index: number, obj: T)=>{\n        initialValue.value.splice( index, 0, obj);\n        setUUID(index);\n    };\n\n    const merge = (index: number, obj: T[])=>{\n        obj.forEach((active,i) => setUUID(index + i) );\n        initialValue.value.splice( index, 0, ...obj);\n    };\n\n    const replace = (index: number, obj: T)=>{\n        initialValue.value.splice( index, 1, obj);\n    };\n\n    const remove = (index: number)=>{\n        uuidKeys.value.splice( index, 1);\n        initialValue.value.splice( index, 1);\n    };\n\n    const move = (oldIndex: number, newIndex: number)=>{\n        if (oldIndex === newIndex)  return;\n        [ initialValue.value[oldIndex], initialValue.value[newIndex] ] = [ initialValue.value[newIndex], initialValue.value[oldIndex] ];\n        [ uuidKeys.value[oldIndex], uuidKeys.value[newIndex] ] = [ uuidKeys.value[newIndex], uuidKeys.value[oldIndex] ];\n    };\n\n    const getKey = (index: number)=> uuidKeys.value[index];\n    const getIndex = (key: number)=> uuidKeys.value.indexOf(key);\n\n    const push = (obj: T)=>{\n        initialValue.value.push( obj );\n        setUUID();\n    };\n\n    const pop = ()=>{\n        initialValue.value.pop();\n        uuidKeys.value.pop();\n    };\n\n    const unshift = (obj: T)=>{\n        initialValue.value.unshift( obj );\n        setUUID(0);\n    };\n\n    const shift = ()=>{\n        initialValue.value.shift();\n        uuidKeys.value.shift();\n    };\n\n    return {\n        list: initialValue,\n        resetList,\n        insert,\n        merge,\n        replace,\n        remove,\n        move,\n        getKey,\n        getIndex,\n        push,\n        pop,\n        unshift,\n        shift\n    }\n}\n\n\nexport default useDynamicList"
  },
  {
    "path": "packages/useExternal/index.md",
    "content": "# useExternal\n\n一个用于动态地向页面加载或卸载外部资源的 Hook。\n\n## 使用Demo\n### 基本使用\n```vue\n<script lang=\"ts\">\nimport { useExternal } from \"v3hooks\";\n\nexport default {\n  setup() {\n    const { resources } = useExternal(\n        'http://img-steward-online.goodaa.com.cn/449c2e5dd6a948e3af67b69c4ece907a.js',\n        (el)=>{ console.log(el) }\n    );\n  },\n};\n</script>\n```\n页面上加载外部 javascript 文件，例如引入 449c2e5dd6a948e3af67b69c4ece907a.js\n\n### 引入css\n```vue\n<template>\n  <div class=\"hello\">\n    <div class=\"bd-example\">\n      <span class=\"badge badge-pill badge-primary\">Primary</span>\n      <span class=\"badge badge-pill badge-secondary\">Secondary</span>\n      <span class=\"badge badge-pill badge-success\">Success</span>\n      <span class=\"badge badge-pill badge-danger\">Danger</span>\n      <span class=\"badge badge-pill badge-warning\">Warning</span>\n      <span class=\"badge badge-pill badge-info\">Info</span>\n      <span class=\"badge badge-pill badge-light\">Light</span>\n      <span class=\"badge badge-pill badge-dark\">Dark</span>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useExternal } from \"v3hooks\";\n\nexport default {\n  setup() {\n    const { load, unload } = useExternal(\n        'https://ahooks.js.org/useExternal/bootstrap-badge.css',\n        (el)=>{ console.log(el) },\n        {\n            manual: true\n        }\n    );\n\n    setTimeout(()=>{\n        load()\n    },1000)\n\n    setTimeout(()=>{\n        unload()\n    },8000)\n  },\n};\n</script>\n```\n页面上加载外部 css 文件，例如引入 bootstrap-badge.css\n\n### 引入图片\n```vue\n<template>\n  <div class=\"hello\">\n    <div ref=\"parent\"></div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useExternal } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    const parent = ref();\n    const { load, unload } = useExternal(\n        'https://img-steward-test.goodaa.com.cn/99b2b706a5b942c2bcbfe211107c7b80.jpeg',\n        (el)=>{ console.log(el) },\n        {\n            manual: true,\n            target: parent\n        }\n    );\n\n    setTimeout(()=>{\n        load()\n    },1000)\n\n    setTimeout(()=>{\n        unload()\n    },8000)\n    return {\n      parent\n    }\n  },\n};\n</script>\n```\n加载一个静态图片作为外部资源引入页面\n\n\n\nuseExternal接受一个src路径, 导出资源本身和操作方法.\n\n## Api\n```\ninterface Options {\n    manual?: boolean;\n    async?: boolean;\n    crossOrigin?: 'anonymous' | 'use-credentials';\n    referrerPolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';\n    noModule?: boolean;\n    defer?: boolean;\n    media?: string;\n    target?: HTMLElement | Ref<HTMLElement>;\n}\nconst useExternal: (src: string, onLoaded?: ((el: Elements) => void) | undefined, options?: Options) => {\n    resources: Ref<Elements | null>;\n    load: () => Promise<unknown>;\n    unload: () => void;\n};\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| src |  外部资源 url 地址\t| string | - |\n| onLoaded |  资源加载成功回调  | (el: Elements) => void) | - |\n| options |  参数  | Options | - |\n\n### Options\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| manual |  是否手动执行  | boolean | false |\n| async |  script标签是否增加async属性  | boolean | - |\n| crossOrigin |  script标签是否增加crossOrigin属性  | `'anonymous' - 'use-credentials'` | - |\n| referrerPolicy |  script标签是否增加referrerPolicy属性  | 原生referrerPolicy |  | - |\n| noModule |  script标签是否增加noModule属性  | boolean | - |\n| defer |  script标签是否增加defer属性  | boolean | - |\n| media |  引入外链样式表 <link> 的 media 属性, 如 all/screen/print/handheld  | string | all |\n| target |  需插入外部图片资源 <img> 的父容器 DOM 节点或者 Ref  |  HTMLElement | Ref<HTMLElement> | - |\n\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| resources\t | 加载后的资源\t | any |\n| load\t | 开始加载外部资源\t | () => void |\n| unload\t | 卸载外部资源\t | () => void |"
  },
  {
    "path": "packages/useExternal/index.ts",
    "content": "import { ref, Ref, isRef } from 'vue';\n\ntype Elements = HTMLScriptElement| HTMLLinkElement | HTMLImageElement;\n\ninterface Options{\n    manual?: boolean,\n    async?: boolean,\n    crossOrigin?: 'anonymous' | 'use-credentials'\n    referrerPolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url'\n    noModule?: boolean\n    defer?: boolean,\n    media?: string,\n    target?: HTMLElement | Ref<HTMLElement>\n}\n\n\nconst useExternal = (\n    src: string,\n    onLoaded?: (el: Elements) => void,\n    options: Options = {},\n)=>{\n    const resources = ref<Elements | null>( null );\n\n    const {\n        manual = false,\n        async,\n        crossOrigin,\n        referrerPolicy,\n        noModule,\n        defer,\n        media = 'all',\n        target = document.body\n    } = options;\n\n    let el: Elements = document.createElement('script');\n\n    let parentEl: Element | HTMLElement  = document.head;\n\n    const loadScript = ()=> new Promise((resolve)=>{\n        const isExist = document.querySelector(`script[src=\"${src}\"]`);\n        if( isExist ) return\n\n        el = document.createElement('script');\n        el.src = src;\n        el.type = 'text/javascript';\n        if( async ) el.async = async;\n        if( defer ) el.defer = defer;\n        if( noModule ) el.noModule = noModule;\n        if( crossOrigin ) el.crossOrigin = crossOrigin;\n        if( referrerPolicy ) el.referrerPolicy = referrerPolicy;\n\n        el = parentEl.appendChild(el);\n        resolve(el);\n    });\n\n    const loadCss = ()=> new Promise((resolve)=>{\n        const isExist = document.querySelector(`link[href=\"${src}\"]`);\n        if( isExist ) return\n\n        el = document.createElement('link');\n        el.href = src;\n        el.rel = 'stylesheet'\n        el.type='text/css';\n        el.media = media;\n\n        el = parentEl.appendChild(el);\n        resolve(el);\n    });\n\n    const loadImage = ()=> new Promise((resolve)=>{\n        const isExist = document.querySelector(`img[src=\"${src}\"]`);\n        if( isExist ) return\n\n        el = document.createElement('img');\n        el.src = src;\n        parentEl = isRef( target) ? target.value :  target;\n        parentEl.appendChild(el);\n        resolve(el);\n    });\n\n    const load = ()=> new Promise(async (resolve, reject)=>{\n        if( /\\.js$/.test(src) ){\n            await loadScript();\n        }\n        if( /\\.css$/.test(src)){\n            await loadCss();\n        }\n        if( /\\.(gif|jpg|jpeg|png|svg|GIF|JPG|PNG|)$/.test(src)){\n            await loadImage()\n        }\n        resources.value = el;\n        el.addEventListener('error', (event: any) => reject(event));\n        el.addEventListener('abort', (event: any) => reject(event));\n        el.addEventListener('load', () => {\n            onLoaded && onLoaded(el)\n        });\n        resolve(el)\n    });\n\n    const unload = ()=>{\n        if( resources.value ){\n            parentEl.removeChild(resources.value);\n        }\n    }\n\n    if( !manual ) load()\n\n    return { resources, load, unload }\n}\n\nexport default useExternal"
  },
  {
    "path": "packages/useFullscreen/index.md",
    "content": "# useFullscreen\n\n一个用于处理 dom 全屏的 Hook。\n\n## 使用Demo\n\n### 基础用法\n```vue\n<template>\n  <div class=\"hello\">\n    <div ref=\"fullScreen\" style=\"background: white\">\n        <p>是否全屏: {{isFullscreen}}</p>\n        <button @click=\"setFull\" id=\"a\">全屏</button>\n        <button @click=\"exitFull\">退出全屏</button>\n        <button @click=\"toggle\">toggle切换</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useFullscreen } from \"v3hooks\";\n\nexport default {\n  setup() {\n    const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen();\n    \n    // useVirtualList测试\n    return {\n      isFullscreen,\n      setFull,\n      exitFull,\n      toggle\n    };\n  }\n};\n</script>\n```\n没有传值默认为document.body\n\n### 局部全屏\n```vue\n<template>\n  <div class=\"hello\">\n    <div ref=\"fullScreen\" style=\"background: white\">\n        <p>是否全屏: {{isFullscreen}}</p>\n        <button @click=\"setFull\" id=\"a\">全屏</button>\n        <button @click=\"exitFull\">退出全屏</button>\n        <button @click=\"toggle\">toggle切换</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useFullscreen } from \"../../../dist/index.js\";\n\nexport default {\n  setup() {\n    const fullScreen = ref();\n    const [ isFullscreen, { setFull, exitFull, toggle } ] = useFullscreen(fullScreen, {\n        onFull: ()=>{\n            console.log('全屏')\n        },\n        onExitFull: ()=>{\n            console.log('非全屏')\n        }\n    });\n    \n    return {\n      isFullscreen,\n      setFull,\n      exitFull,\n      toggle,\n      fullScreen\n    };\n  }\n};\n</script>\n```\nRef传值为fullScreen的div标签，则只会此区域全屏\n\n## 介绍\nuseFullscreen接受一个HTMLElement, 导出操作方法.\n\n## Api\n```\ninterface Actions {\n    setFull: () => void;\n    exitFull: () => void;\n    toggle: () => void;\n}\nconst useFullscreen: (\n  target?: Target$1 | undefined, \n  options?: Options | undefined\n) => [\n  isFullscreen: Ref<boolean>,\n  actions: Actions\n];\n```\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| target |  原生Dom或者被Ref嵌套的Dom\t|  `HTMLElement or Ref<HTMLElement> or (() => HTMLElement)` | document.body |\n| options |  设置(可选)\t| Options  | - |\n\n### Options\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| onExitFull | \t监听退出全屏\t | ()=>void |\n| onFull\t | 监听全屏\t\t | ()=>void |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| isFullscreen\t | 是否全屏\t\t | boolean |\n| setFull\t | 设置全屏\t\t | ()=>void |\n| exitFull | 退出全屏 | ()=>void |\n| toggleFull | 切换全屏 | ()=>void |\n"
  },
  {
    "path": "packages/useFullscreen/index.ts",
    "content": "import { ref, Ref, isRef, onMounted, onUnmounted } from 'vue';\n\ninterface Options{\n    onFull?: ()=> void,\n    onExitFull?: ()=> void\n}\ninterface Actions{\n    setFull: ()=> void,\n    exitFull: ()=> void,\n    toggle: ()=> void\n}\n\ntype Target = HTMLElement | ( () => HTMLElement ) | Ref<HTMLElement>;\n\nconst defaultOptions = {\n    onFull: function(){},\n    onExitFull: function(){},\n};\n\nconst useFullscreen = (\n    target?: Target, \n    options?: Options\n):[\n    isFullscreen: Ref<boolean>,\n    actions: Actions\n]=>{\n    const fullScreenElement = !!document.fullscreenElement;\n    const isFullscreen = ref(fullScreenElement);\n\n    const {\n        onFull,\n        onExitFull\n    } = { ...defaultOptions, ...options};\n\n    let el:HTMLElement = document.body;\n\n    const getEl = ()=>{\n        if( typeof target === 'function'){\n            return target()\n        }\n        return isRef( target ) ? target.value : target;\n    };\n\n    const handler = ()=>{\n        if(isFullscreen.value){\n            onFull()\n        }else{\n            onExitFull()\n        }\n    };\n\n    onMounted(()=>{\n        el = getEl() || el;\n        el.addEventListener('fullscreenchange',handler) \n    });\n\n    onUnmounted(()=>{\n        el.removeEventListener('fullscreenchange',handler) \n    });\n\n    const actions:Actions = {\n        setFull: ()=>{\n            if( isFullscreen.value ) return\n            el.requestFullscreen();\n            isFullscreen.value = true;\n        },\n        exitFull: ()=>{\n            if( !isFullscreen.value ) return\n            document.exitFullscreen();\n            isFullscreen.value = false;\n        },\n        toggle: ()=>{\n            isFullscreen.value ? actions.exitFull(): actions.setFull()\n        }\n    };\n    \n\n    return [ isFullscreen, actions ]\n};\n\nexport default useFullscreen"
  },
  {
    "path": "packages/useInterval/index.md",
    "content": "# useInterval\n\n一个可以处理 setInterval 的 Hook。\n\n\n## 基础使用\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>value:{{ data }}</div>\n    <button @click=\"clear\">关闭</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useInterval } from \"../../../dist/index.js\";\n\n\nexport default {\n  \n  setup() {\n    const data = ref(1);\n    let delay = ref<null | number>(1000);\n\n    useInterval(()=>{\n      data.value++\n    },delay);\n\n    const clear = ()=>{ \n      delay.value = null;\n    };\n\n    return {\n      data,\n      clear\n    };\n  },\n};\n</script>\n\n```\n\n每1000ms，执行一次，设置delay为null则立即中断\n\n\n## 非中断式调用\n```vue\n<script lang=\"ts\">\nimport { useInterval } from \"v3hooks\";\nexport default {\n  \n  setup() {\n    useInterval(()=>{\n      console.log('每 3s 执行一次')\n    },3000)\n  },\n};\n</script>\n\n```\n\nuseInterval可以接受一个普通number参数,这样的useInterval可以接受一个普通number参数，不会被中断会一直被执行，谨慎使用！！\n\n\n## Api\n```\ninterface UseIntervalOptions {\n    immediate?: boolean;\n}\nconst useInterval: (\n  fn: Fn, \n  delay: number | Ref<number | undefined | null>, \n  options?: UseIntervalOptions | undefined\n) => void;\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| fn\t | 要重复调用的函数\t | (...args: any[]) => void | - |\n| delay\t | 间隔时间，当取值为 null 或 undefined 时会停止计时器\t | number - Ref<number> - Ref<undefined> - Ref<null> | - |\n| options\t | 配置计时器的行为，详见下面的 Options\t | Options | - |\n\n### Options\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| immediate\t | 参数可以用来控制是否在首次渲染时立即执行 | boolean| false |"
  },
  {
    "path": "packages/useInterval/index.ts",
    "content": "import { ref, Ref, onMounted, onUnmounted, isRef } from 'vue';\nimport { Fn } from '../utils';\n\ninterface UseIntervalOptions{\n    immediate?: boolean\n}\n\nconst defaultOptions = {\n    immediate: false,\n};\n\nconst useInterval = (\n    fn: Fn,\n    delay: number | Ref<number | undefined | null>,\n    options?: UseIntervalOptions\n)=>{\n\n    const{\n        immediate\n    } = {...defaultOptions,...options};\n    \n    const state = isRef(delay) ? delay : ref(delay);\n\n    if( immediate ) fn()\n\n    let timer: null | NodeJS.Timeout = null;\n    \n    const clear = ()=> timer && clearTimeout(timer)\n\n    const handler = ()=>{\n        if( state.value === undefined || state.value === null ) return\n        fn();\n        run();\n    };\n\n    const run = ()=>{\n        if( state.value === undefined || state.value === null ){\n            clear();\n            return\n        }\n        setTimeout(handler,state.value)\n    }\n    \n    run();\n\n    onUnmounted(()=> clear() )\n};\n\nexport default useInterval"
  },
  {
    "path": "packages/useLocalStorage/index.md",
    "content": "# useLocalStorage\n\n一个可以将状态持久化存储在 localStorage 中的 Hook 。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <div style=\"width: 60vw\">\n      <p> value:{{ state }}</p>\n    </div>\n    <div style=\"width:39vw\">\n      <button @click=\"handleUpdate\">更新storage</button>\n      <button @click=\"handleDelete\">删除storage</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useLocalStorage } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    const state = useLocalStorage('useLocalStorage',{a:231});\n\n    const handleUpdate = ()=>{\n        state.value = { a: Math.random()};\n    };\n\n    const handleDelete = ()=>{\n        state.value = undefined;\n    };\n\n    // useVirtualList测试\n    return {\n      state,\n      handleUpdate,\n      handleDelete\n    };\n  },\n};\n</script>\n```\n\nuseLocalStorage接受一个key和一个value，导出一个响应式的state, 用户直接赋值state.value可自动修改本地localStorage。\n\n## 注意点\n* 不设置value可用于获取本地LocalStorage  例：`useLocalStorage('useLocalStorage')`\n* value等于undefined或者null可用于删除本地Storage  例：`state.value = undefined;`\n\n\n## Api\n```\nconst state = useLocalStorage(\n  key: string,\n  initialValue?: any,\n  options?: Options\n);\n\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| key | LocalStorage存储键名 | any\t | - |\n| initialValue | 初始值 | any\t | {} |\n| options | 配置 | Options\t | - |\n\n### Options\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| watch | 是否实时修改LocalStorage | boolean | true |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state | 可以被修改的数据源 | Ref<any> |\n\n"
  },
  {
    "path": "packages/useLocalStorage/index.ts",
    "content": "import { ref, Ref, isRef, watch as vueWatch } from 'vue';\nimport { TypeSerializers,getValueType } from '../utils'\nconst storage = localStorage;\ninterface Options{\n    watch: boolean\n}\n\nconst defaultOptions = {\n    watch: true\n}\n\n\n\nconst useLocalStorage = <T = any>( key: string, initialValue?: T | Ref<T>, options?:Options)=>{\n    \n    const { watch } = { ...defaultOptions, ...options };\n    \n    const data = ref() as Ref<T | undefined | null>;\n    try{\n        if( initialValue !== undefined ){\n            data.value = isRef( initialValue ) ? initialValue.value : initialValue;\n        }else{\n            data.value = JSON.parse( storage.getItem(key) || '{}' );\n        }\n    }catch(error){\n        console.log(error,'useLocalStorage初始化失败')\n    }\n\n    const type = getValueType(data.value);\n\n    // 判断类型取格式化方法\n    let serializer = TypeSerializers[type];\n\n\n    const setStorage = ()=> storage.setItem( key, serializer.write(data.value) );;\n\n    // 状态监听\n    if( watch ){\n        vueWatch(\n            data,\n            (newValue)=>{\n                if( newValue === undefined || newValue === null ){\n                    storage.removeItem(key);\n                    return\n                }\n                setStorage();\n            },\n            {\n                deep:true\n            }\n        )\n    }\n    \n    setStorage()\n\n    return data\n};\n\nexport default useLocalStorage"
  },
  {
    "path": "packages/useLockFn/index.md",
    "content": "# useLockFn\n\n用于给一个异步函数增加竞态锁，防止并发执行。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>value:{{ data }}</div>\n    <button @click=\"submit\">submit</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useLockFn } from \"v3hooks\";\n\nfunction mockApiRequest() {\n  return new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(1);\n    }, 2000);\n  });\n}\n\nexport default {\n  \n  setup() {\n    const data = ref(1);\n    const submit = useLockFn(async ()=> {\n        await mockApiRequest();\n        data.value++\n    });\n\n    return {\n        data,\n      submit,\n    };\n  },\n};\n</script>\n\n<style scoped>\n.hello {\n  display: flex;\n  flex-direction: column;\n  justify-content: flex-start;\n  align-items: center;\n  margin-top: 30px;\n}\n</style>\n```\n\nuseLockFn接受一个异步的函数， 并返回一个具有执行锁的函数\n\n## Api\n```\nconst useLockFn: (fn: Fn) => (...args: ArgsAny) => Promise<any>;\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| fn\t | 需要增加竞态锁的函数\t | (...args: any[]) => any | - |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| fn\t | 增加了竞态锁的函数 | (...args: any[]) => any |"
  },
  {
    "path": "packages/useLockFn/index.ts",
    "content": "import { ref } from 'vue';\n\ntype ArgsAny = any[];\n\ntype Fn = (...args: ArgsAny)=> Promise<any>;\n\nconst useLockFn = (fn: Fn)=>{\n    const lock = ref(false);\n    return async( ...args: ArgsAny )=>{\n        if( lock.value ) return\n        lock.value = true;\n        try{\n            const ret = await fn(...args);\n            lock.value = false;\n            return ret;\n        }catch ( error ){\n            lock.value = false;\n            throw error;\n        }\n    }\n};\n\nexport default useLockFn"
  },
  {
    "path": "packages/useMap/index.md",
    "content": "# useMap\n\n一个可以管理 Map 类型状态的 Hook。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>\n      <p> value:{{ state }}</p>\n      <button @click=\"()=> set('1',Math.random())\">set</button>\n      <button @click=\"()=> remove('1')\">remove</button>\n      <button @click=\"()=> setAll([ ['1','111'], ['2','2222'] ])\">setAll</button>\n    </div>\n    \n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useSet, useMap } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    const [state, { set, setAll, remove }] = useMap([\n      ['1','321']\n    ]);\n    return {\n      state,\n      set,\n      setAll,\n      remove\n    };\n  },\n};\n</script>\n```\n\nuseMap接受一个 Map 可接受的参数, 并导出以下方法.\n\n## Api\n```\ntype MapValue = readonly (readonly [any, any])[]\n\ninterface Actions<T>{\n    set: ( key: string, value: T)=> void,\n    get: ( key: string )=> T,\n    remove: ( key: string )=> void,\n    has: ( key: string )=> boolean,\n    clear: ()=> void,\n    setAll: (newMap: MapValue)=> void;\n    reset: ()=> void,\n}\n\nfunction useMap <T = any>(initialValue?:MapValue) : [\n    state: Ref<Map<any,any>>,\n    actions: Actions<T>\n]\n\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| initialValue | 可选项，传入默认的 Map 参数\t | readonly[any,any] | - |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state\t | Map 对象\t | Map |\n| set\t | 添加元素\t | ( key: string, value: T)=> void |\n| get\t| 移除元素\t | ( key: string )=> T |\n| remove\t| 移除元素 | ( key: string )=> void |\n| has\t| 判断是否存在元素 |( key: string )=> boolean |\n| clear\t| 清空Map\t | ()=> void |\n| setAll\t| 添加并生成一个新的 Map 对象 | (newMap: (readonly [any, any])[])=> void |\n| reset\t| 重置为默认值\t | ()=> void |"
  },
  {
    "path": "packages/useMap/index.ts",
    "content": "import { ref, Ref, markRaw } from 'vue';\n\n\ntype MapValue = readonly (readonly [any, any])[]\n\ninterface Actions<T>{\n    set: ( key: string, value: T)=> void,\n    get: ( key: string )=> T,\n    remove: ( key: string )=> void,\n    has: ( key: string )=> boolean,\n    clear: ()=> void,\n    setAll: (newMap: MapValue)=> void;\n    reset: ()=> void,\n}\n\nfunction useMap <T = any>(initialValue?:MapValue) : [\n    state: Ref<Map<any,any>>,\n    actions: Actions<T>\n]\n\nfunction useMap<T = any>(initialValue?:MapValue){\n    \n    const initialMap = initialValue ? new Map(initialValue) :  new Map();\n    const state = ref(initialMap) as Ref<Map<any,any>>;\n\n    const actions:Actions<T> = {\n        set:( key: any, value: T )=>{\n            state.value.set(key,value);\n        },\n        get:( key: any )=>{\n            return state.value.get(key);\n        },\n        remove: ( key: any )=>{\n            state.value.delete( key );\n        },\n        has: ( key: any )=> state.value.has(key),\n        clear: ()=> state.value.clear(),\n        setAll: ( newMap: MapValue )=>{\n            state.value = new Map(newMap);    \n        },\n        reset: ()=> state.value = initialMap\n    };\n\n    return [ state, markRaw(actions) ]\n};\n\nexport default useMap"
  },
  {
    "path": "packages/useMediaQuery/index.md",
    "content": "# useMediaQuery\n\n一个监听 mediaQuery 状态的 Hook。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>\n      <p> value:{{ state }}</p>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { watch } from 'vue'\nimport { useMediaQuery } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    const state = useMediaQuery('(min-width: 480px)');\n\n    watch(\n      state,\n      (value)=>{\n        if(!value) console.log('已经是480px以下了')\n      }\n    )\n    \n    // useVirtualList测试\n    return {\n      state\n    };\n  },\n};\n</script>\n```\n\nuseMediaQuery接受一个MediaQuery条件, 导出一个state boolean值来判断是否满足MediaQuery条件\n\n## Api\n```\nconst useMediaQuery: (query: string) => vue.Ref<boolean>;\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| query |  MediaQuery条件\t| string | - |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state\t | 否满足MediaQuery条件\t | boolean |"
  },
  {
    "path": "packages/useMediaQuery/index.ts",
    "content": "import { ref, onUnmounted } from 'vue';\n\nconst useMediaQuery = (query:string)=>{\n    const mediaQuery = window.matchMedia(query);\n    const stata = ref<boolean>( mediaQuery.matches );\n    \n    const handleChange = ( event: MediaQueryListEvent )=> stata.value = event.matches;\n\n    mediaQuery.addEventListener('change',handleChange)\n\n    onUnmounted(()=>{\n        mediaQuery.removeEventListener('change',handleChange)\n    });\n\n    return stata\n};\n\nexport default useMediaQuery"
  },
  {
    "path": "packages/useNetwork/index.md",
    "content": "# useNetwork\n\n一个用来获取网络状态的 Hook 。\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <p> 网络状态:{{ state }}</p>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useNetwork } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    // 获取query中的a\n    const state = useNetwork();\n\n    // useVirtualList测试\n    return {\n      state\n    };\n  },\n};\n</script>\n```\n\nuseNetwork返回网络状态信息\n\n\n## Api\n```\nconst useNetwork: () => {\n    since?: number | Date,\n    online?: boolean,\n    rtt?: number,\n    type?: string,\n    downlink?: number,\n    saveData?: boolean,\n    downlinkMax?: number,\n    effectiveType?: string,\n};\n```\n### Result\n\n| 属性 | 描述                                         | 类型                 |\n|----------|--------------------------------------|----------------------|\n| online  | 网络是否为在线 | `boolean` |\n| since  | 在线与不在线最后改变时间 | `Date` |\n| rtt  | 当前连接下评估的往返时延 | `number` |\n| type  | 设备使用与所述网络进行通信的连接的类型 | `bluetooth` \\| `cellular` \\| `ethernet` \\| `none` \\| `wifi` \\| `wimax` \\| `other` \\| `unknown` |\n| downlink  | 有效带宽估算（单位：兆比特/秒） | `number` |\n| downlinkMax  | 最大下行速度（单位：兆比特/秒） | `number` |\n| saveData  | 用户代理是否设置了减少数据使用的选项 | `boolean`  |\n| effectiveType  | 网络连接的类型 | `slow-2g` \\| `2g` \\| `3g` \\| `4g`  |\n"
  },
  {
    "path": "packages/useNetwork/index.ts",
    "content": "import { reactive, onMounted, onUnmounted } from \"vue\";\n\n\nconst getConnection = ()=> {\n    const nav = navigator as any;\n    if (typeof nav !== 'object') return null;\n    return nav.connection || nav.mozConnection || nav.webkitConnection;\n}\n\n\nexport interface NetworkState {\n    since?: number | Date;\n    online?: boolean;\n    rtt?: number;\n    type?: string;\n    downlink?: number;\n    saveData?: boolean;\n    downlinkMax?: number;\n    effectiveType?: string;\n  }\n  \n\nconst handlerSetConnection = ()=>{\n    const connection = getConnection();\n    return {\n        rtt: connection.rtt,\n        type: connection.type,\n        saveData: connection.saveData,\n        downlink: connection.downlink,\n        downlinkMax: connection.downlinkMax,\n        effectiveType: connection.effectiveType,\n    } as NetworkState;\n};\n\nconst useNetwork = ()=>{\n    const state = reactive<NetworkState>({\n        online: navigator.onLine,\n        since: +new Date(),\n        ...handlerSetConnection()\n    });\n\n    const onOnline = ()=>{\n        state.online = true;\n        state.since = +new Date();\n    };\n\n    const onOffline = ()=>{\n        state.online = false;\n        state.since = +new Date();\n    };\n\n    const onConnectionChange = <T extends keyof NetworkState>() => {\n        const connectionData = handlerSetConnection();\n        Object.keys(connectionData).forEach((key)=>{\n            let propertyKey = key as T;\n            state[propertyKey] = connectionData[propertyKey];\n        })\n    };\n\n    onMounted(()=>{\n        window.addEventListener('online', onOnline);\n        window.addEventListener('offline', onOffline);\n        getConnection()?.addEventListener('change', onConnectionChange);\n    })\n\n    onUnmounted(()=>{\n        window.removeEventListener('online',onOnline);\n        window.removeEventListener('offline',onOffline);\n        getConnection()?.removeEventListener('change', onConnectionChange);\n    })\n    \n    return state\n}\n\nexport default useNetwork"
  },
  {
    "path": "packages/useQRCode/index.md",
    "content": "# useQRCode\n\n一个用来生成二维码的 Hook 。\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div> 二维码:</div>\n    <img :src=\"state\" alt=\"\">\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useQRCode } from \"../../../dist/index.js\";\n\nconst logo = require('../../assets/logo.png')\n\nexport default {\n  \n  setup() {\n    const text = ref<string>('https://www.baidu.com/')\n    // let text2 = 'https://www.baidu.com/';\n\n    const state = useQRCode(text,{\n      logo: logo.default,\n      colorDark: '#000000'\n    });\n\n    setTimeout(()=>{\n      text.value = 'https://www.acfun.cn/'\n    },2000)\n\n    return {\n      state\n    };\n  },\n};\n</script>\n```\n\nusQRCide接受一个静态url，也可以是一个被Ref包裹的url，当Ref值发生变化时，二维码会跟随内容进行变化。\n\n\n## Api\n```\ntype Text = Ref<string> | string;\ninterface useQRCodeOptions {\n    onRenderingStart?: (qrCodeOptions: any) => void;\n    onRenderingEnd?: (qrCodeOptions: any, dataURL: string) => void;\n    [key: string]: any;\n}\nconst useQRCode: (text: Text, options?: useQRCodeOptions | undefined) => Ref<string | undefined>;\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| text |  \b需要生成二维码的url或text\t| `string` \\| `Ref<string>` | - |\n| options |  \b二维码配置项\t| Options | - |\n\n### Options\n\nOptions配置项可以参考<a href=\"https://github.com/ushelp/EasyQRCodeJS#qrcode-api\">EasyQRCodeJS</a>useQRCode的底层是使用了EasyQRCodeJS来作为二维码的实现。\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state\t | base64格式的二维码图片\t | string |\n"
  },
  {
    "path": "packages/useQRCode/index.ts",
    "content": "import qrcode from \"easyqrcodejs\";\nimport { ref, Ref, watch, isRef } from \"vue\";\n\ntype Text = Ref<string> | string;\n\ninterface useQRCodeOptions{\n    onRenderingStart?: (qrCodeOptions:any)=> void;\n    onRenderingEnd?: (qrCodeOptions:any,dataURL:string)=>void;\n    [key:string]: any;\n}\n\nconst defaultUseQRCodeOptions = {\n    onRenderingEnd:()=>{}\n};\n\nconst useQRCode = (\n    text:Text,\n    options?: useQRCodeOptions\n)=>{\n    const state = ref<string>();\n\n    const {\n        onRenderingEnd,\n        ...otherOptions\n    } = {...defaultUseQRCodeOptions,...options};\n\n    const Qrcode = new qrcode(document.createElement('div'),{\n        text: isRef(text) ? text.value : text,\n        ...otherOptions,\n        onRenderingEnd(qrCodeOptions:any, dataURL:string){\n            state.value = dataURL;\n            onRenderingEnd(qrCodeOptions, dataURL)\n        }\n    })\n    if(isRef(text)){\n        watch(text,()=>{\n            Qrcode.makeCode(text.value)\n        })\n    }\n\n    return state\n}\n\n\nexport default useQRCode"
  },
  {
    "path": "packages/useRequest/__tests__/index.test.ts",
    "content": "import { shallowMount } from '@vue/test-utils';\nimport { defineComponent, ref } from 'vue';\nimport MockDate from 'mockdate';\nimport { sleep } from '../../utils/testingHelpers';\nimport useRequest from '../index';\nimport { Service, BaseOptions, Result } from '../types'\n\ndescribe('useRequest', () => {\n  const originalError = console.error;\n  const originalWarn = console.warn;\n  const requestDelayTime = 500;\n\n  beforeAll(() => {\n    console.error = (...args) => {\n      if (/Vue warn/.test(args[0])) {\n        return;\n      }\n      originalError.call(console, ...args);\n    };\n    console.warn = (...args) => {\n      if (/Vue warn/.test(args[0])) {\n        return;\n      }\n      originalWarn.call(console, ...args);\n    };\n  });\n  afterAll(() => {\n    console.error = originalError;\n    console.warn = originalWarn;\n  });\n\n  const request: Service = (req:any) =>\n    new Promise((resolve, reject) => {\n      setTimeout(() => {\n        if (req === 0) {\n          reject(new Error('fail'));\n        } else {\n          resolve('success');\n        }\n      }, requestDelayTime);\n    });\n\n  it('should be defined', () => {\n    expect(useRequest).toBeDefined();\n  });\n\n  const setUp = (service:Service, options?:BaseOptions) => useRequest(service, options);\n\n  let hook:Result<any>;\n  it('useRequest should auto run', async () => {\n    let successValue:string = '';\n    const successCallback = (text:string) => {\n      successValue = text;\n    };\n    const errorCallback = jest.fn();\n\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            onSuccess: successCallback,\n            onError: errorCallback,\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(true);\n    await sleep(requestDelayTime);\n    expect(hook.loading.value).toEqual(false);\n    expect(hook.data.value).toEqual('success');\n    expect(successValue).toEqual('success');\n    expect(errorCallback).not.toHaveBeenCalled();\n\n    hook.run(0);\n    expect(hook.loading.value).toEqual(true);\n    await sleep(requestDelayTime);\n    expect(hook.error.value).toEqual(new Error('fail'));\n    expect(hook.loading.value).toEqual(false);\n    expect(errorCallback).toHaveBeenCalled();\n\n    hook.run(1);\n    await sleep(requestDelayTime);\n    expect(hook.data.value).toEqual('success');\n    expect(hook.loading.value).toEqual(false);\n    expect(errorCallback).toHaveBeenCalled();\n    wrapper.unmount();\n  });\n\n  it('useRequest should be manually triggered', async () => {\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            manual: true,\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(false);\n    hook.run(1);\n    expect(hook.loading.value).toEqual(true);\n    await sleep(requestDelayTime);\n    expect(hook.loading.value).toEqual(false);\n    expect(hook.data.value).toEqual('success');\n    wrapper.unmount();\n  });\n\n  it('useRequest polling should work', async () => {\n    const callback = jest.fn();\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(\n            () => {\n              callback();\n              return request();\n            },\n            {\n              pollingInterval: 100,\n              pollingWhenHidden: true,\n            },\n          );\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(true);\n    expect(callback).toHaveBeenCalled();\n    expect(callback).toHaveBeenCalledTimes(1);\n    await sleep(100);\n    expect(callback).toHaveBeenCalledTimes(2);\n    await sleep(100);\n    expect(callback).toHaveBeenCalledTimes(3);\n    hook.cancel();\n    expect(callback).toHaveBeenCalledTimes(3);\n\n    hook.run();\n    expect(callback).toHaveBeenCalledTimes(4);\n    wrapper.unmount();\n  });\n\n  it('useRequest debounceInterval should work', async () => {\n    const callback = jest.fn();\n\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(\n            () => {\n              callback();\n              return request();\n            },\n            {\n              manual: true,\n              debounceInterval: 100,\n            },\n          );\n        },\n      }),\n    );\n\n    hook.run(1);\n    hook.run(2);\n    hook.run(3);\n    hook.run(4);\n    await sleep(100);\n    expect(callback).toHaveBeenCalled();\n    expect(callback).toHaveBeenCalledTimes(1);\n\n    hook.run(1);\n    hook.run(2);\n    hook.run(3);\n    hook.run(4);\n    await sleep(100);\n    expect(callback).toHaveBeenCalled();\n    expect(callback).toHaveBeenCalledTimes(2);\n\n    wrapper.unmount();\n  });\n\n  it('useRequest throttleInterval should work', async () => {\n    const callback = jest.fn();\n\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(\n            () => {\n              callback();\n              return request();\n            },\n            {\n              manual: true,\n              throttleInterval: 100,\n            },\n          );\n        },\n      }),\n    );\n    await sleep(100);\n    hook.run(1);\n    hook.run(2);\n    await sleep(100);\n    hook.run(3);\n    hook.run(4);\n\n    expect(callback).toHaveBeenCalledTimes(2);\n\n    wrapper.unmount();\n  });\n\n  it('useRequest mutate should work', async () => {\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request);\n        },\n      }),\n    );\n    await sleep(requestDelayTime);\n    expect(hook.data.value).toEqual('success');\n    hook.mutate('hello');\n    expect(hook.data.value).toEqual('hello');\n    wrapper.unmount();\n  });\n\n  it('useRequest loadingDelay should work', async () => {\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            loadingDelay: 2000,\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(false);\n    await sleep(1000);\n    expect(hook.loading.value).toEqual(false);\n    wrapper.unmount();\n  });\n\n  it('useRequest loadingDelay should delay', async () => {\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            loadingDelay: 300,\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(false);\n    await sleep(requestDelayTime);\n    expect(hook.loading.value).toEqual(false);\n    wrapper.unmount();\n  });\n\n  it('useRequest refreshDeps should work', async () => {\n    const refreshValue = ref(1);\n    const callback = jest.fn();\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(\n            () => {\n              callback();\n              return request();\n            },\n            {\n              refreshDeps: [refreshValue],\n            },\n          );\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(true);\n    expect(callback).toHaveBeenCalledTimes(1);\n    await sleep(requestDelayTime);\n    expect(hook.loading.value).toEqual(false);\n\n    refreshValue.value = 2;\n    await sleep(0);\n    expect(hook.loading.value).toEqual(true);\n    expect(callback).toHaveBeenCalledTimes(2);\n    await sleep(requestDelayTime);\n    expect(hook.loading.value).toEqual(false);\n    wrapper.unmount();\n  });\n\n  it('useRequest ready should work', async () => {\n    const ready = ref(false);\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            ready,\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(false);\n    ready.value = true;\n    await sleep(0);\n    expect(hook.loading.value).toEqual(true);\n    wrapper.unmount();\n  });\n\n  it('useRequest initialData should work', async () => {\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            initialData: 'hello',\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(true);\n    expect(hook.data.value).toEqual('hello');\n    await sleep(requestDelayTime);\n    expect(hook.data.value).toEqual('success');\n    wrapper.unmount();\n  });\n\n  it('useRequest formatResult should work', async () => {\n    let formarParams = '';\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            formatResult: p => {\n              formarParams = p;\n              return 'hello';\n            },\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(true);\n    await sleep(requestDelayTime);\n    expect(hook.loading.value).toEqual(false);\n    expect(formarParams).toEqual('success');\n    expect(hook.data.value).toEqual('hello');\n    wrapper.unmount();\n  });\n\n  it('useRequest defaultParams should work', async () => {\n    const defaultParamsRequest = (...req:any[]) =>\n      new Promise(resolve => {\n        setTimeout(() => {\n          resolve(req);\n        }, 500);\n      });\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(defaultParamsRequest, {\n            defaultParams: [1, 2, 3],\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(true);\n    await sleep(requestDelayTime);\n    expect(hook.data.value).toEqual([1, 2, 3]);\n    wrapper.unmount();\n  });\n\n  it('useRequest throwOnError to be false should work', async () => {\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            manual: true,\n          });\n        },\n      }),\n    );\n    hook.run(0);\n    await sleep(requestDelayTime);\n    expect(hook.data.value).toEqual(undefined);\n    expect(hook.error.value).toEqual(new Error('fail'));\n    wrapper.unmount();\n  });\n\n  it('useRequest cacheKey should work', async () => {\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            cacheKey: 'testCacheKey',\n          });\n        },\n      }),\n    );\n    await sleep(requestDelayTime);\n    expect(hook.loading.value).toEqual(false);\n    expect(hook.data.value).toEqual('success');\n    wrapper.unmount();\n\n    let hook2:any;\n    const wrapper2 = shallowMount(\n      defineComponent({\n        setup() {\n          hook2 = setUp(request, {\n            cacheKey: 'testCacheKey',\n          });\n        },\n      }),\n    );\n    expect(hook2.data.value).toEqual('success');\n    await sleep(requestDelayTime);\n    expect(hook2.loading.value).toEqual(false);\n    wrapper2.unmount();\n  });\n\n  it('useRequest staleTime should work', async () => {\n    MockDate.set(0);\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            cacheKey: 'testStaleTime',\n            staleTime: 3000,\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(true);\n    await sleep(requestDelayTime);\n    expect(hook.loading.value).toEqual(false);\n    expect(hook.data.value).toEqual('success');\n    wrapper.unmount();\n    MockDate.set(1000);\n\n    let hook2:any;\n    const wrapper2 = shallowMount(\n      defineComponent({\n        setup() {\n          hook2 = setUp(request, {\n            cacheKey: 'testStaleTime',\n            staleTime: 3000,\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(false);\n    expect(hook2.data.value).toEqual('success');\n    wrapper2.unmount();\n\n    MockDate.set(3001);\n    let hook3:any;\n    const wrapper3 = shallowMount(\n      defineComponent({\n        setup() {\n          hook3 = setUp(request, {\n            cacheKey: 'testStaleTime',\n            staleTime: 3000,\n          });\n        },\n      }),\n    );\n    expect(hook3.data.value).toEqual('success');\n    await sleep(requestDelayTime);\n    expect(hook3.loading.value).toEqual(false);\n    wrapper3.unmount();\n  });\n\n  it('useRequest cacheTime should work', async () => {\n    MockDate.set(0);\n    const wrapper = shallowMount(\n      defineComponent({\n        setup() {\n          hook = setUp(request, {\n            cacheKey: 'testCacheTime',\n            cacheTime: 2000,\n          });\n        },\n      }),\n    );\n    expect(hook.loading.value).toEqual(true);\n    await sleep(requestDelayTime);\n    expect(hook.loading.value).toEqual(false);\n    expect(hook.data.value).toEqual('success');\n    wrapper.unmount();\n    MockDate.set(500);\n    await sleep(500);\n\n    let hook2:any;\n    const wrapper2 = shallowMount(\n      defineComponent({\n        setup() {\n          hook2 = setUp(request, {\n            cacheKey: 'testCacheTime',\n            cacheTime: 2000,\n          });\n        },\n      }),\n    );\n    expect(hook2.data.value).toEqual('success');\n    wrapper2.unmount();\n    MockDate.set(3001);\n    await sleep(3001);\n\n    let hook3:any;\n    const wrapper3 = shallowMount(\n      defineComponent({\n        setup() {\n          hook3 = setUp(request, {\n            cacheKey: 'testCacheTime',\n            cacheTime: 2000,\n          });\n        },\n      }),\n    );\n    expect(hook3.data.value).toEqual(undefined);\n    await sleep(requestDelayTime);\n    expect(hook3.loading.value).toEqual(false);\n    expect(hook3.data.value).toEqual('success');\n    wrapper3.unmount();\n  });\n});\n"
  },
  {
    "path": "packages/useRequest/index.md",
    "content": "# useRequest\n\n专注于管理异步请求的Hook，加速你的日常开发\n\n* 自动请求/手动请求\n* SWR(stale-while-revalidate)\n* 缓存/预加载\n* 屏幕聚焦重新请求\n* 轮询\n* 防抖\n* 节流\n* 依赖请求\n* 突变\n* loading delay\n\n## 请求方式\n\b如果service是object,useRequest会使用 Fetch 来发送网络请求\n```\nconst { data } = useRequest(\n    {\n    url: 'http://xxx.xx.com/api/userInfo',\n    method: 'POST'\n    }\n);\n```\n\b如果service是async函数,useRequest会调用此函数来发送网络请求\n```\nconst { data } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n);\n```\n\n## 使用\n### 默认请求\n```\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n);\nwatchEffect(()=>{\n    console.log( data?.value );\n})\n```\n在这个例子中， useRequest 接收了一个异步函数 ，在组件初次加载时， 自动触发该函数执行。同时 useRequest 会自动管理异步请求的 loading , data 状态。你可以通过data和loading来实现你的需求\n\n\n因为返回的data为响应式，js中获取data.value需要在watchEffect中使用，而在template中使用是不需要的。\n\n### 手动触发\n\n```\nconst { data, run, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        manual: true\n    }\n);\n```\n通过设置 options.manual = true , 则需要手动调用 run 时才会触发执行异步函数。\n\n### 轮询\n```\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        pollingInterval: 1000\n    }\n);\n```\n\n通过设置 options.pollingInterval ，进入轮询模式，定时触发函数执行。\n\n### 依赖请求\n```\nimport { onMounted, ref } from 'vue'\n\nconst isReady = ref(false);\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        ready: isReady\n    }\n);\nonMounted(() => {\n    isReady.value = true;\n})\n```\n只有当 options.ready 变为 true 时, 才会发起请求，基于该特性可以实现串行请求，依赖请求等。\n\n### 防抖\n\n```\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        debounceInterval: 1000\n    }\n);\n```\n通过设置 options.debounceInterval ，则进入防抖模式。此时如果频繁触发 run ，则会以防抖策略进行请求。\n\n\n### 节流\n\n```\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        throttleInterval: 1000\n    }\n);\n```\n通过设置 options.throttleInterval ，则进入节流模式。此时如果频繁触发 run ，则会以节流策略进行请求。\n\n\n\n### 缓存 & SWR\n\n```\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        caccacheKey: 'mock1'\n    }\n);\n```\n如果设置了 options.cacheKey ， useRequest 会将当前请求结束数据缓存起来。下次组件初始化时，如果有缓存数据，我们会优先返回缓存数据，然后在背后发送新请求，也就是 SWR 的能力。你可以通过 cacheTime 设置缓存数据回收时间，也可以通过 staleTime 设置数据保持新鲜时间。\n\n\n### 预加载\n\n```\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        caccacheKey: 'mock1'\n    }\n);\n```\n同一个 cacheKey 的请求，是全局共享的，也就是你可以提前请求数据。后续请求会提前返回之前预加载的数据，利用该特性，可以很方便的实现预加载。\n\n\n### 屏幕聚焦重新请求\n\n```\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        refreshOnWindowFocus: true,\n        focusTimespan: 2000\n    }\n);\n```\n\n如果你设置了 options.refreshOnWindowFocus = true ，则在浏览器窗口 refocus 和 revisible 时，会重新发起请求。你可以通过设置 options.focusTimespan 来设置请求间隔，默认无 。\n\n### 突变\n\n```\nconst { data, mutate } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    }\n);\n\n<button type=\"button\" @click={() => mutate({code:'1',msg:'test'})}>\n    突变测试\n</button>\n```\n你可以通过 mutate ，直接修改 data.\n\n### Loading Delay\n\n```\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        loadingDelay: 300\n    }\n);\n```\n通过设置 options.loadingDelay ，可以延迟 loading 变成 true 的时间，有效防止闪烁。\n\n### refreshDeps\n\n开发中会经常碰到这个需求，当某些 state 变化时，我们需要重新执行异步请求，使用useRequest将很方便的实现此功能。\n\n```\nimport { onMounted, ref } from 'vue'\n\nconst random = ref(1);\nconst { data, loading } = useRequest(\n    () => {\n        return axios.post(\n            `http://xxx.xx.com/api/userInfo`\n        );\n    },\n    {\n        refreshDeps: [ random ]\n    }\n);\n\nsetInterval(()=>{\n    random.value = Math.random()\n},1000)\n```\n\n当例子中 random 发生变化时,会重新执行 service。\n\n## Api\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| data | service 返回的数据，默认为 undefined | undefined / any |\n| error | service 抛出的异常，默认为 undefined | undefined / Error |\n| loading | service 是否正在执行 | boolean |\n| run | 手动触发 service 执行，参数会传递给 service | (...args: any[]) => void |\n| refresh | 使用上一次的 params，重新执行 service| () => void |\n| cancel | 如果有轮询，停止 | () => void |\n| mutate | 突变直接修改 data\t | (newData) => void |\n| loading | service 是否正在执行 | boolean |\n\n### Params\n所有的 Options 均是可选的。\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| manual | 是否需要手动执行 | boolean | false |\n| defaultParams | 如果 manual=false ，自动执行 run 的时候，默认带上的参数 | any[] | - |\n| refreshDeps | 在 manual = false 时，refreshDeps 变化，会触发 service 重新执行 | any[] | [] |\n| loadingDelay | 设置显示 loading 的延迟时间，避免闪烁 | number | - |\n| pollingInterval | 轮询间隔，单位为毫秒。设置后，将进入轮询模式，定时触发 run | number | - |\n| pollingWhenHidden | 在页面隐藏时，是否继续轮询。默认为 true，即不会停止轮询 | boolean | true |\n| refreshOnWindowFocus | 屏幕重新聚焦时间间隔，在当前时间间隔内，不会重新发起请求 | number | - |\n| focusTimespan | 在页面隐藏时，是否继续轮询。默认为 true，即不会停止轮询 | boolean | true |\n| debounceInterval | 防抖间隔, 单位为毫秒，设置后，请求进入防抖模式。 | number | - |\n| throttleInterval | 节流间隔, 单位为毫秒，设置后，请求进入节流模式。 | number | - |\n| ready | 只有当 ready 为 true 时，才会发起请求 | boolean | - |\n| cacheKey | * 请求唯一标识。如果设置了 cacheKey，我们会启用缓存机制 * 我们会缓存每次请求的 data , error , params , loading在缓存机制下 * 同样的请求我们会先返回缓存中的数据，同时会在背后发送新的请求，待新数据返回后，重新触发数据更新 | string | - |\n| cacheTime | 设置缓存数据回收时间。默认缓存数据 5 分钟后回收，需要配和 cacheKey 使用 | number | 300000 |\n| staleTime | 缓存数据保持新鲜时间。在该时间间隔内，认为数据是新鲜的，不会重新发请求，需要配和 cacheKey 使用 | number | 0 |\n\n\n## 致敬\n\n因为本人之前React中一直使用 umi ，在 umi 中使用了useRequest, Api使用非常的顺手。到了vue3中没有此hook，所以实现一份Vue的简版 useRequest来供Vue3项目使用。\n\nReact Ahooks [原版链接](https://ahooks.js.org/zh-CN/hooks/async)\n"
  },
  {
    "path": "packages/useRequest/index.ts",
    "content": "import { \n    BaseOptions,\n    Result,\n    Run,\n    Service,\n    FetchService,\n    Cancel\n} from './types';\nimport { \n    shallowRef, \n    ref,\n    watch\n} from 'vue';\nimport { \n    debounce, \n    throttle, \n    throttleAndDeBounce \n} from '../utils';\nimport { loadingDelayAsync, clearLoadingDelayTimer } from './src/loadingDelay';\nimport { serveiceProxy, argsSymbolKey } from './src/service';\nimport Polling from './src/polling';\nimport { visibility } from './src/visibility';\nimport { handleResCache } from './src/cache'\nimport memoryCache from '../utils/memoryCache'\n\n// service请求参数缓存\nconst reqCache = new memoryCache();\n\n// 请求参数缓存\nconst resCache = new memoryCache();\n\n\n// 默认参数\nconst defaultOptions = {\n    manual: false,\n    initialData: undefined,\n    onSuccess: ()=>{},\n    onError: ()=>{},\n    formatResult: (data:any)=> data,\n    defaultParams: [], \n    pollingInterval: 0,\n    pollingWhenHidden: true,\n    ready: undefined,\n    debounceInterval: undefined,\n    throttleInterval: undefined,\n    refreshOnWindowFocus: false,\n    focusTimespan: undefined,\n    loadingDelay: 0,\n    refreshDeps: [],\n    cacheTime: 300000,\n    staleTime: 0,\n};\n\n\nconst useRequest = <T>(service: Service | FetchService,options?:BaseOptions )=>{\n     \n    const { \n        manual,\n        initialData,\n        onSuccess,\n        onError,\n        formatResult,\n        defaultParams,\n        pollingInterval,\n        pollingWhenHidden,\n        ready,\n        debounceInterval,\n        throttleInterval,\n        refreshOnWindowFocus,\n        focusTimespan,\n        loadingDelay,\n        refreshDeps,\n        cacheKey,\n        cacheTime,\n        staleTime\n    } = { ...defaultOptions, ...options };\n\n    const data = shallowRef<T | undefined>(initialData);\n    const error = ref<Error | undefined >(undefined);\n    const loading = ref(false);\n    const latestTime = ref<number>(0);\n\n    // 取消轮询\n    const cancel:Cancel = () => Polling.cancel();\n\n    // 执行轮询\n    const pollingRun = ()=>{\n        if( pollingInterval < 4 || Polling.isActive){\n            return\n        }\n        Polling.run( run, pollingInterval, pollingWhenHidden );\n    };\n\n    // 执行网络请求\n    let run:Run = (...args: any[])=>{\n        \n        // 请求开始时间\n        const reqTime = +new Date();\n\n        // 判断开启缓存 && 有缓存，先返回缓存\n        // 缓存修改并不会阻止顺序执行，service请求会继续发出\n        // 也就是所谓SWR能力\n        if( cacheKey && resCache.has(cacheKey)){\n            data.value = resCache.get(cacheKey)\n            \n            if( latestTime.value + staleTime > reqTime ){\n                return\n            }\n        }else if(loadingDelay > 0){\n            loadingDelayAsync( loadingDelay ).then(()=> loading.value = true)\n        }else {\n            loading.value = true;\n        }\n\n        \n        // 更新最新一次请求开始时间\n        latestTime.value = reqTime;\n\n        serveiceProxy( service, args, reqCache ).then(( responseData )=>{\n            clearLoadingDelayTimer();\n\n            responseData = formatResult(responseData)\n            data.value = responseData as any;\n\n            loading.value = false;\n\n            onSuccess(responseData,args)\n\n            // 处理缓存\n            handleResCache(\n                responseData,\n                resCache,\n                cacheKey,\n                cacheTime\n            )\n        }).catch(( e:Error )=>{\n            loading.value = false;\n\n            error.value = e;\n\n            onError(error.value,args)\n        });\n        // 非激活状态执行轮询\n        pollingRun()\n    };\n\n    const refresh = ()=>{\n        const args = reqCache.get(argsSymbolKey) || [];\n        run(...args)\n    };\n\n\n    // 是否自动执行\n    if( !manual && ready === undefined){\n\n        // 是否携带默认参数\n        defaultParams.length > 0 ? run(...defaultParams) : run()\n\n        // 是否执行轮询\n        pollingRun()\n    }\n\n\n    //监听依赖请求是否执行\n    watch(()=> ready,(curr)=>{\n        if( curr?.value === true){\n            refresh()\n        }\n    },{ deep: true })\n\n    //多个监听依赖请求是否执行\n    watch(refreshDeps,()=> refresh() , { deep: true });\n\n    \n    // 防抖+节流\n    if(\n        debounceInterval !== undefined \n        && throttleInterval !== undefined\n        && typeof(debounceInterval) === 'number'\n        && typeof(throttleInterval) === 'number'\n    ){\n        run = throttleAndDeBounce(run,debounceInterval,throttleInterval);\n    }else{\n        // 防抖\n        if( \n            debounceInterval !== undefined \n            && typeof(debounceInterval) === 'number'\n        ){\n            run = debounce(run,debounceInterval)\n        }\n\n        // 节流\n        if(\n            throttleInterval !== undefined \n            && typeof(throttleInterval) === 'number'\n        ){\n            run = throttle(run,throttleInterval)\n        }\n    }\n\n    // 屏幕聚焦重新请求\n    if(refreshOnWindowFocus === true){\n        visibility( refresh, focusTimespan );\n    }\n\n\n    // 突变改变data值\n    const mutate = (state:any)=>{\n        data.value = state;\n    };\n\n\n    // 返回值\n    const res: Result<T> = {\n        data,\n        error,\n        run,\n        refresh,\n        loading,\n        cancel,\n        mutate\n    };\n\n    return res\n};\n\n\n\nexport default useRequest"
  },
  {
    "path": "packages/useRequest/src/cache.ts",
    "content": "\nimport memoryCache from '../../utils/memoryCache'\n\nconst handleResCache = (\n    data: any,\n    resCache: memoryCache,\n    cacheKey?: string,\n    cacheTime?: number,\n)=>{\n    if( cacheKey ){\n        if( resCache.has(cacheKey)){\n            resCache.put(cacheKey,data)\n            return\n        }\n        resCache.put(cacheKey,data,cacheTime)\n    }\n} \n\nexport {\n    handleResCache\n}"
  },
  {
    "path": "packages/useRequest/src/fetch.ts",
    "content": "export interface FetchParams extends RequestInit{\n    url: RequestInfo,\n}\n\nconst defaultParams = {\n    method: 'POST', // *GET, POST, PUT, DELETE, etc.\n    mode: 'cors', // no-cors, *cors, same-origin\n    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached\n    credentials: 'same-origin', // include, *same-origin, omit\n    headers: {\n      'Content-Type': 'application/json'\n    },\n    redirect: 'follow', // manual, *follow, error\n    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\n};\n\n/**\n * 使用fetch发起请求\n * @param Params \n * @returns FetchData\n */\n\nfunction fetchData( p:FetchParams ){\n    const params = { ...defaultParams, ...p } as FetchParams;\n    return fetch( params.url, params);\n}\n\n\nexport default fetchData"
  },
  {
    "path": "packages/useRequest/src/loadingDelay.ts",
    "content": "/**\n * loading的延迟计算\n * @param startTime \n * @param loadingDelay \n * @param args \n * @returns \n */\nlet loadingDelayTimer:NodeJS.Timeout | null = null;\nexport const loadingDelayAsync = ( loadingDelay:number)=>{\n    clearLoadingDelayTimer();\n    return new Promise((resolve)=>{\n        loadingDelayTimer = setTimeout(()=> resolve(loadingDelayTimer),Math.max( loadingDelay, 0 ) );\n    })\n};\n\n/**\n * 取消loading延迟计算Timer\n */\nexport const clearLoadingDelayTimer = ()=>{\n    if( loadingDelayTimer ){\n        clearTimeout(loadingDelayTimer)\n    }\n} "
  },
  {
    "path": "packages/useRequest/src/polling.ts",
    "content": "import { Run } from '../types'\n\n/**\n * 执行轮询\n */\nclass Polling{\n    isActive: boolean; // 是否是激活状态\n    private pollingInterval: number;\n    private pollingWhenHidden: boolean;\n    constructor() {\n        this.isActive = false;\n        this.pollingInterval = 0;\n        this.pollingWhenHidden = true;\n    }\n\n    run(\n        run:Run,\n        pollingInterval: number, \n        pollingWhenHidden: boolean\n    ){\n        this.isActive = true;\n        this.pollingInterval = pollingInterval;\n        this.pollingWhenHidden = pollingWhenHidden;\n        this.task(run);\n    }\n\n    cancel(){\n        this.isActive = false\n    }\n\n    private task(\n        run:Run\n    ){\n        setTimeout(()=>{\n            if( !this.isActive ) return\n            if( this.pollingWhenHidden ){\n                run();\n            }else{\n                if(!document.hidden){\n                    run();\n                }\n            }\n            this.task(run);\n        },this.pollingInterval)\n    }\n}\n\n\nexport default new Polling()\n"
  },
  {
    "path": "packages/useRequest/src/service.ts",
    "content": "\nimport fetchData from './fetch'\nimport MemoryCache from '../../utils/memoryCache'\n\nconst argsSymbolKey = 'argsKey';\n\nconst serveiceProxy = async (service: any, args: any[], reqParamsCache: MemoryCache)=>{\n    try{\n        if( args.length > 0 ){\n            reqParamsCache.put(argsSymbolKey,args);\n        }\n        if( Object.prototype.toString.call(service) === \"[object Function]\"){\n            return await service(...args);\n        }\n        if( Object.prototype.toString.call(service) === \"[object Object]\" ){\n            const response = await fetchData(service);\n            return response.json()\n        }\n    }catch(error){\n        return Promise.reject(error)\n    }\n}\n\n\nexport { serveiceProxy, argsSymbolKey }"
  },
  {
    "path": "packages/useRequest/src/visibility.ts",
    "content": "import { Run } from '../types'\nimport { throttle } from '../../utils'\n\n\n/**\n * \b监听屏幕是否聚焦\n * @param run \n * @param focusTimespan \n */\nconst visibility = (run:Run ,focusTimespan?: undefined | number)=>{\n    let handler = ()=> {\n        if (!document.hidden) {\n            run()\n        }\n    };\n    if(focusTimespan !== undefined && typeof(focusTimespan) === 'number'){\n        handler = throttle(handler,focusTimespan);\n    }\n    document.addEventListener('visibilitychange',handler );\n}\n\nexport { visibility }"
  },
  {
    "path": "packages/useRequest/types.d.ts",
    "content": "import { Ref } from 'vue';\nimport { FetchParams } from './src/fetch'\n\n// Service请求实例\nexport type Service = (...args: any[]) => Promise<any>;\n\nexport interface FetchService extends FetchParams{};\n\n\n// 请求参数\nexport interface BaseOptions {\n    manual?: boolean; // 是否需要手动触发\n    initialData?: any; //默认的 data\n    formatResult?: (response: any) => any; // 格式化请求结果\t\n    onSuccess?: (data: any, params: any[]) => void; // 成功回调\n    onError?: (error: Error, params: any[]) => void; // 失败回调\n    defaultParams?: any[]; // 如果 manual=false ，自动执行 run 的时候，默认带上的参数\n    pollingInterval?: number; // 轮询请求时间\n    pollingWhenHidden?: boolean; // 在屏幕不可见时，暂时暂停定时任务。\n    ready?: undefined | Ref<boolean>; // 依赖请求\n    debounceInterval?: undefined | number; // 防抖\n    throttleInterval?: undefined | number; // 节流\n    refreshOnWindowFocus?: boolean, // 屏幕聚焦重新请求\n    focusTimespan?: undefined | number, // 屏幕聚焦重新间隔\n    loadingDelay?: number, //loading延迟时间\n    refreshDeps?: any[], //依赖刷新监听\n    cacheKey?: string, //缓存key\n    cacheTime?: number, //缓存数据回收时间\n    staleTime?: number //缓存数据新鲜时间\n};\n\n// 执行请求\nexport type Run = (...args: any[])=> void;\n\n// 重新执行\nexport type Refresh = ()=> void;\n\n//取消请求\nexport type Cancel = (()=> void);\n\n// 突变\nexport type Mutate = (state:any)=> void;\n\n\n// hook返回值\nexport interface Result<T> {\n    data: Ref<T | undefined>; // 是否需要手动触发\n    loading: Ref<boolean>;\n    run: Run;\n    refresh: Refresh;\n    cancel: Cancel;\n    mutate: Mutate;\n    error: Ref<Error | undefined>;\n};"
  },
  {
    "path": "packages/useRouteQuery/index.md",
    "content": "# useRouteQuery\n\n一个获取vueRouter query的 Hook 。\n\n请确保项目已安装Vue Router v4.x版本及以上，否则将不能使用此Hook.\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <p> value:{{ state }}</p>\n    <button @click=\"handlerUpdateState\">修改query</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useRouteQuery } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    // 获取query中的a\n    const state = useRouteQuery('a');\n\n    const handlerUpdateState = ()=>{\n      state.value = String( Math.random() );\n    };\n\n    // useVirtualList测试\n    return {\n      state,\n      handlerUpdateState\n    };\n  },\n};\n</script>\n```\n\nuseRouteQuery接受一个key是query中的参数key。 修改返回的state可直接修改url中的query。\n\n## Api\n```\nconst state = useRouteQuery(\n  key: string,\n);\n\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| key | query中的键名 | string\t | - |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state | query值也可以直接被修改，这样将同步修改query | Ref<any> |\n\n"
  },
  {
    "path": "packages/useRouteQuery/index.ts",
    "content": "import { computed } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\n\n\nconst useRouteQuery = ( key: string)=>{\n  \n    const route = useRoute();\n    const router = useRouter();\n    return computed({\n        get:()=>{\n            return route.query[key];\n        },\n        set: val => {\n            router.replace({ query: { ...route.query ,[key]: val } });\n        }\n    })\n};\n\nexport default useRouteQuery"
  },
  {
    "path": "packages/useSessionStorage/index.md",
    "content": "# useSessionStorage\n\n一个可以将状态持久化存储在 sessionStorage 中的 Hook 。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\" style=\"display:flex;align-items:flex-start;\">\n    <div style=\"width: 60vw\">\n      <p> value:{{ state }}</p>\n    </div>\n    <div style=\"width:39vw\">\n      <button @click=\"handleUpdate\">更新storage</button>\n      <button @click=\"handleDelete\">删除storage</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useSessionStorage } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    const state = useSessionStorage('useSessionStorage',{a:231});\n\n    const handleUpdate = ()=>{\n        state.value = { a: Math.random()};\n    };\n\n    const handleDelete = ()=>{\n        state.value = undefined;\n    };\n\n    // useVirtualList测试\n    return {\n      state,\n      handleUpdate,\n      handleDelete\n    };\n  },\n};\n</script>\n```\n\nuseSessionStorage接受一个key和一个value，导出一个响应式的state, 用户直接赋值state.value可自动修改本地sessionStorage。\n\n## 注意点\n* 不设置value可用于获取本地sessionStorage  例：`useSessionStorage('useSessionStorage')`\n* value等于undefined或者null可用于删除本地Storage  例：`state.value = undefined;`\n\n\n## Api\n```\nconst state = useSessionStorage(\n  key: string,\n  initialValue?: any,\n  options?: Options\n);\n\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| key | sessionStorage存储键名 | any\t | - |\n| initialValue | 初始值 | any\t | {} |\n| options | 配置 | Options\t | - |\n\n### Options\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| watch | 是否实时修改sessionStorage | boolean | true |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state | 可以被修改的数据源 | Ref<any> |\n\n"
  },
  {
    "path": "packages/useSessionStorage/index.ts",
    "content": "import { ref, Ref, isRef, watch as vueWatch } from 'vue';\nimport { TypeSerializers, getValueType } from '../utils'\nconst storage = sessionStorage;\ninterface Options{\n    watch: boolean\n}\n\nconst defaultOptions = {\n    watch: true\n}\n\nconst useSessionStorage = <T = any>( key: string, initialValue?: T | Ref<T>, options?:Options)=>{\n\n    const { watch } = { ...defaultOptions, ...options };\n\n    const data = ref() as Ref<T | undefined | null>;\n    try{\n        if( initialValue !== undefined ){\n            data.value = isRef( initialValue ) ? initialValue.value : initialValue;\n        }else{\n            data.value = JSON.parse( storage.getItem(key) || '{}' );\n        }\n    }catch(error){\n        console.log(error,'useLocalStorage初始化失败')\n    }\n\n    const type = getValueType(data.value);\n\n    // 判断类型取格式化方法\n    let serializer = TypeSerializers[type];\n\n\n    const setStorage = ()=> storage.setItem( key, serializer.write(data.value) );\n\n    // 状态监听\n    if( watch ){\n        vueWatch(\n            data,\n            (newValue)=>{\n                if( newValue === undefined || newValue === null ){\n                    storage.removeItem(key);\n                    return\n                }\n                setStorage();\n            },\n            {\n                deep:true\n            }\n        )\n    }\n\n    setStorage()\n\n    return data\n};\n\nexport default useSessionStorage\n"
  },
  {
    "path": "packages/useSet/index.md",
    "content": "# useSet\n\n一个可以管理 Set 类型状态的 Hook。\n\n\n## 使用Demo\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>\n      <p> value:{{ state }}</p>\n      <button @click=\"()=> add(Math.random())\">add</button>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useSet, useMap } from \"../../../dist/index.js\";\n\nexport default {\n  \n  setup() {\n    const [state , { add } ] = useSet([1]);\n    return {\n      state,\n      add\n    };\n  },\n};\n</script>\n```\n\nuseSet接受一个 Set 可接受的参数, 并导出以下方法.\n\n## Api\n```\ninterface Actions<T>{\n    add: (value: T)=> void,\n    remove: (value: T)=> void,\n    has: (value: T)=> boolean,\n    clear: ()=> void,\n    reset: ()=> void,\n}\n\nfunction useSet <T = any>(initialValue?:T[]) : [\n    state: Ref<Set<any>>,\n    actions: Actions<T>\n]\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| initialValue | 可选项，传入默认的 Set 参数\t | T[] | - |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state\t | Set 对象\t | Set |\n| add\t | 添加元素\t | (value: T)=> void |\n| remove\t| 移除元素\t | (value: T)=> void |\n| has\t| 判断是否存在元素\t | (value: T)=> void |\n| clear\t| 清空set\t | ()=> void |\n| reset\t| 重置为默认值\t | ()=> void |"
  },
  {
    "path": "packages/useSet/index.ts",
    "content": "import { ref, Ref, markRaw } from 'vue';\n\ninterface Actions<T>{\n    add: (value: T)=> void,\n    remove: (value: T)=> void,\n    has: (value: T)=> boolean,\n    clear: ()=> void,\n    reset: ()=> void,\n}\n\nfunction useSet <T = any>(initialValue?:T[]) : [\n    state: Ref<Set<any>>,\n    actions: Actions<T>\n]\n\nfunction useSet<T = any>(initialValue?:T[]){\n    \n    const initialSet = initialValue ? new Set(initialValue) : new Set();\n    const state = ref(initialSet);\n\n    const actions:Actions<T> = {\n        add: ( value: T )=>{\n            state.value.add(value);\n        },\n        remove: ( value: T )=>{\n            state.value.delete(value);\n        },\n        has: ( value: T )=> state.value.has(value),\n        clear: ()=> state.value.clear(),\n        reset: ()=>{\n            state.value = new Set();\n        }\n    };\n\n    return [ state, markRaw(actions) ]\n};\n\nexport default useSet"
  },
  {
    "path": "packages/useTextSelection/index.md",
    "content": "# useTextSelection\n\n实时获取用户当前选取的文本内容及位置的hook。\n\n\n## 使用Demo\n\n### 基础用法\n```vue\n<template>\n  <div style=\"text-align: center\">\n    <p ref=\"p\"> 可选择区域: 123111111111111aaaaaaaaaaabbbbbbbbbbb eeeeeeeeeeeeeeee</p>\n    <p>已选择的值：{{ text }}</p>\n    <p>位置信息：rect: {{ rect }}</p>\n    <p>left: {{ rect.left }}</p>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useTextSelection } from \"v3hooks\";\nexport default {\n  setup() {\n    const { text, rect } = useTextSelection();\n    return {\n        text,\n        rect\n    };\n  },\n};\n</script>\n```\n没有传值默认为document， 页面上可选择的文本都会被计算\n\n### 监听特定区域文本选择\n```vue\n<template>\n  <div style=\"text-align: center\">\n    <p ref=\"p\"> 可选择区域: 123111111111111aaaaaaaaaaabbbbbbbbbbb eeeeeeeeeeeeeeee</p>\n    <p>已选择的值：{{ text }}</p>\n    <p>位置信息：rect: {{ rect }}</p>\n    <p>left: {{ rect.left }}</p>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useTextSelection } from \"v3hooks\";\n\nexport default {\n  setup() {\n    const p = ref();\n    const { text, rect } = useTextSelection(p);\n\n    return {\n        text,\n        p,\n        rect\n    };\n  },\n};\n</script>\n```\n传值为Ref的P标签，只会监听特定区域。\n\n\n## 介绍\nuseTextSelection接受一个HTMLElement, 导出已选择的文本和位置信息。\n\n## Api\n```\nconst useTextSelection: (\n  target?: HTMLElement | Ref<HTMLElement> | (() => HTMLElement) | Document\n) => vue.ToRefs<{\n    text: string;\n    rect: {\n        left: number;\n        right: number;\n        top: number;\n        bottom: number;\n        height: number;\n        width: number;\n    };\n}>;\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| target |  原生Dom或者被Ref嵌套的Dom\t|  `HTMLElement or Ref<HTMLElement> or (() => HTMLElement) or Document` | document |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| text\t | 用户选取的文本值\t | string |\n| rect\t | 位置信息\t | Rect |\n\n\n### Rect\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| left\t | 文本的左坐标\t | number |\n| right\t | 文本的右坐标\t\t | number |\n| top\t | 文本的顶坐标\t\t | number |\n| bottom\t | 文本的底坐标\t\t | number |\n| height\t | 文本的高度\t\t | number |\n| width\t | 文本的宽度\t\t | number |"
  },
  {
    "path": "packages/useTextSelection/index.ts",
    "content": "import {\n    Ref,\n    isRef,\n    reactive,\n    onMounted,\n    onUnmounted,\n    toRefs\n} from 'vue';\n\ntype Target = HTMLElement | Ref<HTMLElement> | (() => HTMLElement ) | Document;\n\nconst defaultReact = {\n    left: NaN,\n    right: NaN,\n    top: NaN,\n    bottom: NaN,\n    height: NaN,\n    width: NaN\n};\n\nconst useTextSelection = (\n    target:Target = document\n)=>{\n    let state = reactive({\n        text: '',\n        rect: defaultReact\n    })\n\n    let el:HTMLElement | Document = document;\n\n    const getEl = ()=>{\n        if( typeof target === 'function'){\n            return target()\n        }\n        return isRef( target ) ? target.value : target;\n    };\n\n    const getRect = (selection: Selection | null)=>{\n        if( !selection ) return defaultReact\n        const range = selection.getRangeAt(0);\n        const {  height, width, top, left, right, bottom } = range.getBoundingClientRect();\n        return {\n            height,\n            width,\n            top,\n            left,\n            right,\n            bottom,\n        };\n    };\n\n    const handleMouseup = ()=>{\n        let currSelection: Selection | null = window.getSelection();\n        let text = currSelection ? currSelection.toString() : '';\n        let rect = getRect( currSelection );\n\n        state.text = text;\n        state.rect = rect;\n    };\n\n    const handleMousedown = ()=>{\n        if( state.text ){\n            state.text = ''\n            state.rect = defaultReact;\n        }\n        const currSelection:Selection | null = window.getSelection();\n        if (!currSelection) return;\n        currSelection.removeAllRanges();\n    };\n\n    onMounted(()=>{\n        el = getEl();\n        el.addEventListener('mouseup',handleMouseup);\n        document.addEventListener('mousedown', handleMousedown);\n    })\n\n    onUnmounted(()=>{\n        el.removeEventListener('mouseup',handleMouseup);\n        document.removeEventListener('mousedown', handleMousedown);\n    })\n\n    return toRefs( state )\n};\n\nexport default useTextSelection\n"
  },
  {
    "path": "packages/useThrottle/index.md",
    "content": "# useThrottle\n\n用来处理节流值的 Hook。\n\n\n## 使用\n\n```\n<template>\n    <div>\n        <input\n            v-model=\"throttleCurrValue\"\n            placeholder=\"Typed value\"\n            style=\"width: 280\"\n        />\n        <p style=\"marginTop: 16\">throttleValue: {{throttleValue}}</p>\n    </div>\n</template>\n\n<script lang=\"ts\">\n\nimport { ref } from 'vue';\nimport { useThrottle } from 'v3hooks';\n\n\nexport default {\n  \n  \n\n  setup() {\n    const throttleCurrValue = ref(1);\n    const throttleValue = useThrottle(throttleCurrValue,500);\n\n    return {\n        throttleCurrValue,\n        throttleValue,\n    }\n  }\n}\n</script>\n```\n\n使用useThrottle后，频繁设置throttleCurrValue， throttleValue每隔 500ms 变化一次。。\n\n## Api\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| value | 需要防抖的值 | any | - |\n| wait | 超时时间，单位为毫秒 | number | 1000 |"
  },
  {
    "path": "packages/useThrottle/index.ts",
    "content": "// import { debounce } from '../utils'\nimport useThrottleFn from '../useThrottleFn'\nimport { ref,Ref, watch } from 'vue';\n\n// 默认值\nconst defaultDelay = 1000;\n\n/**\n * 处理防抖值\n * @param value \n * @param delay \n * @returns \n */\nconst useThrottle = <T>( value: Ref<T> , delay?:number )=>{\n\n    delay = delay || defaultDelay;\n\n    const res = ref<T>(value.value) as Ref<T>;\n\n    // 利用useDebounceFn来简化处理值\n    const { run } = useThrottleFn(()=> res.value = value.value ,delay);\n\n    watch(value,()=> run(),{ deep: true });\n\n    return res;\n};\n\n\nexport default useThrottle"
  },
  {
    "path": "packages/useThrottleFn/index.md",
    "content": "# useThrottleFn\n\n用来处理节流函数的 Hook。\n\n\n## 使用\n\n```\n<template>\n    <div>\n      <p style=\"marginTop: 16\"> Clicked count: {{throttleFnValue}} </p>\n      <button type=\"button\" @click=\"run\">\n        useThrottleFn测试\n      </button>\n    </div>\n</template>\n\n<script lang=\"ts\">\n\nimport { ref } from 'vue';\nimport { useThrottleFn } from 'v3hooks';\n\n\nexport default {\n  \n  \n\n  setup() {\n    const throttleFnValue = ref(1);\n    const { run } = useThrottleFn(()=>{\n      throttleFnValue.value++\n    },500)\n\n    return {\n      throttleFnValue,\n      run,\n    }\n  }\n}\n</script>\n```\n\n频繁调用 run，但只会每隔 500ms 执行一次相关函数。\n\n\n\n## Api\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| fn | 需要节流执行的函数\t | () => void | - |\n| wait | 超时时间，单位为毫秒 | number | 1000 |"
  },
  {
    "path": "packages/useThrottleFn/index.ts",
    "content": "import { Fn, throttle } from '../utils'\n\nconst defaultDelay = 1000;\n\n/**\n * 处理节流函数\n * @param fn \n * @param delay \n * @returns \n */\nconst useThrottleFn = ( fn:Fn, delay?: number )=>{\n    const run = throttle( fn, typeof( delay ) === 'number'? delay : defaultDelay );\n    return { run };\n}\n\n\nexport default useThrottleFn"
  },
  {
    "path": "packages/useTimeout/index.md",
    "content": "# useTimeout\n\n一个可以处理 setTimeout 计时器函数的 Hook。\n\n\n## 基础使用\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>value:{{ data }}</div>\n    <button @click=\"clear\">关闭</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useTimeout } from \"v3hooks\";\n\n\nexport default {\n  \n  setup() {\n    const data = ref(1);\n    let delay = ref<null | number>(1000);\n\n\n    useTimeout(()=>{\n      data.value++\n    },delay)\n\n    const clear = ()=>{ \n      delay.value = null;\n    };\n\n    return {\n      data,\n      clear\n    };\n  },\n};\n</script>\n\n```\n\n1000ms执行一次，设置delay为null则立即中断\n\n## 非中断式调用\n```vue\n<script lang=\"ts\">\nimport { useTimeout } from \"v3hooks\";\nexport default {\n  \n  setup() {\n    useTimeout(()=>{\n      console.log(' 3s 后执行')\n    },3000)\n  },\n};\n</script>\n\n```\n\nuseTimeout可以接受一个普通number参数,这样的timeout不能被中断\n\n## Api\n```\nconst useTimeout: (\n  fn: Fn,\n  delay: number | Ref<number | undefined | null>\n) => void;\n```\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| fn\t | 要重复调用的函数\t | (...args: any[]) => void | - |\n| delay\t | 间隔时间，当取值为 null 或 undefined 时会停止计时器\t | Ref<number> | Ref<undefined> | Ref<null> | - |"
  },
  {
    "path": "packages/useTimeout/index.ts",
    "content": "import { ref, isRef, Ref, onUnmounted } from 'vue';\nimport { Fn } from '../utils';\n\n\nconst useTimeout = (\n    fn: Fn,\n    delay: number | Ref<number | undefined | null>\n)=>{\n    const state = isRef(delay) ? delay : ref(delay);\n\n    let timer: null | NodeJS.Timeout = null;\n    \n    const clear = ()=> timer && clearTimeout(timer)\n\n    const handler = ()=>{\n        if(state.value === undefined || state.value === null ) return\n        fn();\n    };\n\n    const run = ()=>{\n        if( state.value === undefined || state.value === null ){\n            clear();\n            return\n        }\n        setTimeout(handler,state.value)\n    }\n    \n    run()\n    onUnmounted(()=> clear())\n};\n\nexport default useTimeout"
  },
  {
    "path": "packages/useToggle/index.md",
    "content": "# useToggle\n\n用于在多个状态值间切换的 Hook。\n(此处与 ahooks 略有不同，ahooks只能两个状态切换，本hook支持N个状态切换\b\b)\n\n\n## 基础使用\n\n```\n<template>\n    <div>\n      <p>useToggleDemoState: {{useToggleDemoState}}</p>\n      <button @click=\"handleUseTToggle\">设置指定值</button>\n      <button @click=\"useTToggle\">useTToggle</button>\n      <button @click=\"useTSetLeft\">useTSetLeft</button>\n      <button @click=\"useTSetCenter\">useTSetCenter</button>\n      <button @click=\"useTSetRight\">useTSetRight</button>\n    </div>\n</template>\n\n<script lang=\"ts\">\n\nimport { ref } from 'vue';\nimport { useToggle } from 'v3hooks';\n\n\nexport default {\n  \n  \n\n  setup() {\n    //useToggle 测试\n    const [ useToggleDemoState, [ useTToggle, useTSetLeft, useTSetCenter, useTSetRight]] = useToggle('left','center','right');\n\n    const handleUseTToggle = ()=>{\n      useTToggle('center')\n    };\n\n    return {\n      useToggleDemoState,\n      handleUseTToggle,\n      useTToggle,\n      useTSetLeft,\n      useTSetCenter,\n      useTSetRight,\n    }\n  }\n}\n</script>\n```\n\nuseToggle接受多个参数，且在actions中进行同等数量导出。Actions中第一个为toggle切换，其余为设置对应参数。\n\n\n## 异步值Toggle\n\n```\n<template>\n  <div class=\"hello\">\n    <div> {{state}}</div>\n    <button @click=\"toggle\">toggle</button>\n    <button @click=\"setToggle\">setToggle</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { ref } from 'vue';\nimport { useToggle,useTimeout } from \"../../../dist/index.js\";\nexport default {\n  \n  setup() {\n\n    const platform = ref<string>('安装 App');\n    const platform2 = ref<string>('安装中...');\n    const [state, [toggle]] = useToggle(platform, platform2,'不安装');\n\n    useTimeout(() => {\n      platform.value = `安装 ios App`\n      platform2.value = '安装中2....'\n    }, ref(3000));\n\n    const setToggle = ()=>{\n      toggle(platform)\n    }\n    \n    return {\n      state,\n      toggle,\n      setToggle\n    };\n  },\n};\n</script>\n```\n\nuseToggle可以接受ref值的切换，内部支持了响应式，如果ref值发生变化,state会监听其变化同步修改。\n\n\n\n## Api\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| value | 需要切换的值 | string - number - boolean - undefined | - |\n| ... | 同上 | 同上 | - |\n\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| state | 状态值 | - |\n| actions | 操作集合\t | Actions |\n\n### Actions\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| toggle | 触发状态更改的函数，可以接受可选参数修改状态值\t | (state?: any) => void |\n| action | 按照value顺序设置state为vulue | () => void |\n| ... | 同上 | 同上 |"
  },
  {
    "path": "packages/useToggle/index.ts",
    "content": "import { ref, Ref, isRef, watch } from 'vue';\n\n\ntype State = string | number | boolean | undefined;\ntype RefState = Ref<State>\n\ntype Fn = (v?:any)=> void;\n\ntype Actions = Fn[];\n\n\nfunction useToggle<T extends State, U extends RefState>(\n    ...args: (T | U)[]\n): [U,Actions]\n\n/**\n * 用于在N个状态值间切换。\n * @param args \n * @returns \n */\nfunction useToggle<T extends State, U extends RefState>(...args: (T | U)[]){\n    \n    const argsStateArr = args.map((variable)=> isRef(variable) ? variable : ref(variable));\n    const initialValue = argsStateArr[0].value;\n    const state = ref<State>(initialValue);\n    let activeState = argsStateArr[0];\n\n\n    // 1: 监听当前被激活的state异步\n    // 2: 如果当前的异步发生改变则修改state\n    watch([activeState],()=>{\n        state.value = activeState.value\n    },{\n        deep:true\n    })\n\n    let currIndex = 0;\n    const len = args.length;\n\n    \n    const toggle = (param?:T | U)=>{\n        // 判定是否在参数里\n        if( param !== undefined && args.includes(param) ){\n            state.value = isRef(param) ? param.value : param;\n            activeState = isRef(param) ? param : ref(param);\n            return\n        }\n        // 顺序变化\n        currIndex = currIndex + 1 > len - 1 ? 0 : currIndex + 1;\n        state.value = argsStateArr[currIndex].value;\n        activeState = argsStateArr[currIndex];\n    };\n\n    const createHandle = ()=>{\n        return argsStateArr.map((active,index)=>{\n            return ()=>{\n                state.value = active.value;\n                activeState = active;\n                currIndex = index;\n            }\n        })\n    };\n\n    const actions: Actions = [ toggle, ...createHandle() ];\n    \n    return [ state, actions ]\n}\n\nexport default useToggle"
  },
  {
    "path": "packages/useUnmount/index.ts",
    "content": "import { onScopeDispose, onUnmounted, version } from 'vue';\n\nconst useUnmount = (fn:any) => {\n  const unmounted = onScopeDispose ?? onUnmounted;\n  unmounted(() => {\n    fn();\n  });\n};\n\nexport default useUnmount;\n"
  },
  {
    "path": "packages/useVirtualList/index.md",
    "content": "# useVirtualList\n\n长列表虚拟化列表的 Hook，用于解决展示海量数据渲染时首屏渲染缓慢和滚动卡顿问题。\n\n\n## 使用\n\n```\n<template>\n  <div class=\"hello\">\n    <button\n      style=\"margin-top: 30px\"\n      type=\"button\"\n      @click=\"handleVirtualScrollTo\"\n    >\n      scroll to\n    </button>\n    <div\n      :ref=\"containerProps.ref\"\n      @scroll=\"containerProps.onScroll\"\n      style=\"height: 300px; overflow: auto;border: 1px solid #cccccc\"\n    >\n      <div :style=\"wrapperStyle\">\n        <div\n          v-for=\"active in list\"\n          :key=\"active\"\n          style=\"\n            height: 59px;\n            border-bottom: 1px solid #cccccc;\n            background-color: white;\n          \"\n        >\n          {{ active }}\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useVirtualList } from \"v3hooks\";\n\nexport default {\n  \n  setup() {\n    const { list, wrapperStyle, containerProps, scrollTo } = useVirtualList(\n      Array.from(Array(99999).keys()),\n      {\n        itemHeight: 60,\n        overscan: 10,\n      }\n    );\n\n    const handleVirtualScrollTo = () => {\n      scrollTo(22);\n    };\n\n    return {\n      list,\n      wrapperStyle,\n      containerProps,\n      handleVirtualScrollTo,\n    };\n  },\n};\n</script>\n```\n\nuseVirtualList接受一个数组，导出一个虚拟化的list以元素配置，具体配置看Api\n\n\n## Api\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| arr | 包含大量数据的列表\t | T[]\t | [] |\n| options | 可选配置项，见 Options | Options | - |\n\n### Options\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| itemHeight | 行高度，静态高度可以直接写入像素值，动态高度可传入函数| number | ((index: number) => number) | - |\n| overscan | 视区上、下额外展示的 dom 节点数量 | number | 5 |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| list | 当前需要展示的列表内容\t | T[] |\n| containerProps | 滚动容器的 props\t | object |\n| wrapperStyle | children 外层包裹器 Style | object |\n| scrollTo | 快速滚动到指定 index | (index: number) => void |\n\n"
  },
  {
    "path": "packages/useVirtualList/index.ts",
    "content": "import { reactive, ref, Ref } from 'vue';\n\nexport interface OptionType {\n    itemHeight: number | ((index: number) => number);\n    overscan?: number;\n}\n\nconst useVirtualList = <T = any>(state: T[], options: OptionType) => {\n    let start = 0;\n    let end = 10;\n    const list = ref(state.slice(start, end)) as Ref<T[]>;\n\n    const { itemHeight, overscan = 5 } = options;\n    const containerRef = ref<HTMLElement | null>();\n\n\n    const totalHeight: number = (() => {\n        if (typeof itemHeight === 'number') {\n            return state.length * itemHeight;\n        }\n        return state.reduce((sum, _, index) => sum + itemHeight(index), 0);\n    })();\n\n    // 计算当前视图展示数量\n    const getViewCapacity = (containerHeight: number) => {\n        if (typeof itemHeight === 'number') {\n            return Math.ceil(containerHeight / itemHeight);\n        }\n        let sum = 0;\n        let capacity = 0;\n        for (let i = start; i < state.length; i++) {\n            const height = (itemHeight as (index: number) => number)(i);\n            sum += height;\n            if (sum >= containerHeight) {\n                capacity = i;\n                break;\n            }\n        }\n        return capacity - start;\n    };\n\n    // 获取当前索引\n    const getOffset = (scrollTop: number) => {\n        if (typeof itemHeight === 'number') {\n            return Math.floor(scrollTop / itemHeight) + 1;\n        }\n        let sum = 0;\n        let offset = 0;\n        for (let i = 0; i < state.length; i++) {\n            const height = (itemHeight as (index: number) => number)(i);\n            sum += height;\n            if (sum >= scrollTop) {\n                offset = i;\n                break;\n            }\n        }\n        return offset + 1;\n    };\n\n    // 获取当前索引向上高度\n    const getDistanceTop = (index: number) => {\n        if (typeof itemHeight === 'number') {\n            const height = index * itemHeight;\n            return height;\n        }\n        const height = state.slice(0, index).reduce((sum, _, i) => sum + itemHeight(i), 0);\n        return height;\n    };\n\n    let offsetTop = getDistanceTop(start);\n\n\n    // 计算展示指定位置\n    const calculateRange = () => {\n        const element = containerRef.value;\n        if (element) {\n            const offset = getOffset(element.scrollTop);\n            const viewCapacity = getViewCapacity(element.clientHeight);\n\n            const from = offset - overscan;\n            const to = offset + viewCapacity + overscan;\n            start = from < 0 ? 0 : from;\n            end = to > state.length ? state.length : to;\n\n            list.value = state.slice(start, end);\n\n            // 实时计算\n            offsetTop = getDistanceTop(start);\n            wrapperStyle.marginTop = offsetTop + 'px';\n            wrapperStyle.height = totalHeight - offsetTop + 'px';\n        }\n    };\n\n\n\n\n    // 滚动容器的外层监听\n    const containerProps = reactive({\n        ref: (ele: any) => {\n            containerRef.value = ele;\n        },\n        onScroll: (e: any) => {\n            e.preventDefault();\n            calculateRange()\n        },\n        style: { overflowY: 'auto' as const },\n    });\n\n    // children 外层包裹器 style\n    const wrapperStyle = reactive({\n        width: '100%',\n        height: totalHeight - offsetTop + 'px',\n        marginTop: offsetTop + 'px'\n    });\n\n    // 快速滚动到指定 index\t\n    const scrollTo = (index: number) => {\n        if (containerRef.value) {\n            containerRef.value.scrollTop = getDistanceTop(index);\n            calculateRange();\n        }\n    };\n\n    return {\n        list,\n        wrapperStyle,\n        containerProps,\n        scrollTo\n    }\n};\n\nexport default useVirtualList"
  },
  {
    "path": "packages/useWebSocket/index.md",
    "content": "# useWebSocket\n\n用于处理 WebSocket 的 Hook。\n\n## 基础使用\n\n```vue\n<template>\n  <div class=\"hello\">\n    <div>webScoket状态： {{readyState}}</div>\n    <button @click=\"connect\">连接webScoket</button>\n    <button @click=\"disconnect\">关闭webScoket</button>\n    <button @click=\"handleSendMessage\">发送消息</button>\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport { useWebSocket } from \"v3hooks\";\nimport { watchEffect } from 'vue';\nexport default {\n  \n  setup() {\n\n    const { \n      readyState,\n      latestMessage,\n      disconnect,\n      sendMessage,\n    } = useWebSocket('ws://82.157.123.54:9010/ajaxchattest')\n\n    const handleSendMessage = ()=>{\n      sendMessage('hello v3hooks')\n    }\n\n    watchEffect(()=>{\n      console.log(latestMessage.value)\n    })\n\n    return {\n      readyState,\n      disconnect,\n      handleSendMessage\n    };\n  },\n};\n</script>\n```\n\nuseWebSocket接受一个string的socket地址，返回一个集合对象。\n上面例子中接受参数使用了latestMessage来获取最新一次通讯的event对象。\n\n也可以使用onMessage来获取event对象，例子如下\n```\nuseWebSocket('ws://82.157.123.54:9010/ajaxchattest',{\n  onMessage(event){\n    console.log(event)\n  }\n})\n```\n\n\n## Api\n```\nenum ReadyState {\n    Connecting = 0,\n    Open = 1,\n    Closing = 2,\n    Closed = 3\n}\ninterface UseWebSocketOptions {\n    manual?: boolean;\n    reconnectLimit?: number;\n    reconnectInterval?: number;\n    onOpen?: (event: WebSocketEventMap['open']) => void;\n    onClose?: (event: WebSocketEventMap['close']) => void;\n    onMessage?: (event: WebSocketEventMap['message']) => void;\n    onError?: (event: WebSocketEventMap['error']) => void;\n}\ninterface Result {\n    latestMessage: Ref<WebSocketEventMap['message'] | undefined>;\n    sendMessage: WebSocket['send'];\n    disconnect: () => void;\n    connect: () => void;\n    readyState: Ref<ReadyState>;\n    webSocketIns: Ref<WebSocket | undefined>;\n}\nfunction useWebSocket(socketUrl: string, options?: UseWebSocketOptions): Result;\n```\n\n### Params\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| socketUrl |  必填，webSocket 地址\t| string | - |\n| options |  选填，连接配置项\t| options | - |\n\n### Options\n\n| 参数 | 说明 | 类型 | 默认值 |\n| :----| :---- | :---- | :---- |\n| onOpen |  选填，webSocket 连接成功回调\t| (event: WebSocketEventMap['open']) => void | - |\n| onClose |  选填，webSocket 连接成功回调\t| (event: WebSocketEventMap['close']) => void | - |\n| onMessage |  选填，webSocket 收到消息回调\t| (message: WebSocketEventMap['message']) => void | - |\n| onError |  选填，webSocket 错误回调\t| (event: WebSocketEventMap['error']) => void | - |\n| reconnectLimit |  选填，重试次数\t| number | 3 |\n| reconnectInterval |  选填，重试时间间隔（ms）\t| number | 3000 |\n| manual |  选填，手动启动连接\t| boolean | false |\n\n### Result\n\n| 参数 | 说明 | 类型 |\n| :----| :---- | :---- |\n| latestMessage\t | 最新消息\t | WebSocketEventMap['message'] |\n| sendMessage\t | 发送消息函数\t | WebSocket['send'] |\n| disconnect\t | 手动断开 webSocket 连接\t | () => void |\n| connect\t | 手动连接 webSocket，如果当前已有连接，则关闭后重新连接\t | () => void |\n| readyState\t | 当前 webSocket 连接状态\t | ReadyState |\n| webSocketIns\t| webSocket 实例\t | WebSocket |\n"
  },
  {
    "path": "packages/useWebSocket/index.ts",
    "content": "import { Ref, ref } from \"vue\";\nimport useTimeout from '../useTimeout'\n\ninterface UseWebSocketOptions {\n    manual?: boolean,\n    reconnectLimit?: number,\n    reconnectInterval?: number,\n    onOpen?: (event:WebSocketEventMap['open'])=> void,\n    onClose?: (event:WebSocketEventMap['close'])=> void,\n    onMessage?: (event:WebSocketEventMap['message'])=> void,\n    onError?: (event:WebSocketEventMap['error'])=> void,\n}\nenum ReadyState {\n    Connecting = 0,\n    Open = 1,\n    Closing = 2,\n    Closed = 3,\n}\ninterface Result{\n    latestMessage: Ref<WebSocketEventMap['message'] | undefined>;\n    sendMessage: WebSocket['send'];\n    disconnect: () => void;\n    connect: () => void;\n    readyState: Ref<ReadyState>;\n    webSocketIns: Ref<WebSocket | undefined>;\n}\n\nconst defaultOptions = {\n    manual: false,\n    reconnectLimit: 3,\n    reconnectInterval: 3000,\n    onOpen:()=>{},\n    onClose:()=>{},\n    onMessage:()=>{},\n    onError:()=>{},\n};\n\n\nfunction useWebSocket (\n    socketUrl: string,\n    options?: UseWebSocketOptions\n):Result;\n\nfunction useWebSocket(\n    socketUrl: string,\n    options?: UseWebSocketOptions\n){\n    const {\n        manual,\n        reconnectLimit,\n        reconnectInterval,\n        onOpen,\n        onClose,\n        onMessage,\n        onError,\n    } = {...defaultOptions,...options};\n\n    if(!socketUrl || typeof(socketUrl)!== 'string'){\n        throw new Error('useWebSocket require string socketUrl')\n    }\n    let readyState = ref<number>(ReadyState.Connecting);\n    \n    const reconnectCount = ref<number>(0);\n    const socket = ref<WebSocket>();\n    const latestMessage = ref<WebSocketEventMap['message']>();\n\n    const run = ()=>{\n        socket.value = new WebSocket(socketUrl);\n        socket.value.addEventListener('open', function (event) {\n            readyState.value = ReadyState.Open\n            onOpen(event)\n        });\n        \n        socket.value.addEventListener('message', function (event) {\n            latestMessage.value = event;\n            onMessage(event)\n        });\n\n        socket.value.addEventListener('error', function (event) {\n            console.log('error ', event);\n            reconnect();\n            onError(event)\n        });\n        socket.value.addEventListener('close', function (event) {\n            readyState.value = ReadyState.Closed\n            onClose(event)\n        });\n    };\n\n    const connect = ()=>{\n        if( readyState.value !== ReadyState.Open){\n            reconnectCount.value = 0;\n            run()\n        }\n    };\n\n    const reconnect = ()=>{\n        if(reconnectCount.value >= reconnectLimit ) return\n        useTimeout(()=>{\n            reconnectCount.value++\n            run()\n        },ref(reconnectInterval))\n    }\n\n    const disconnect = ()=>{\n        if(\n            (   readyState.value === ReadyState.Connecting\n                || readyState.value === ReadyState.Open \n            )\n            && socket.value\n        ){\n            readyState.value = ReadyState.Closing\n            socket.value.close()\n        }\n    };\n\n    const sendMessage = (data: string | ArrayBufferLike | Blob | ArrayBufferView)=>{\n        if(\n            data \n            && socket.value\n            && readyState.value === ReadyState.Open\n        ) socket.value.send(data)\n    };\n\n    if( !manual ) connect()\n    \n    return {\n        latestMessage,\n        readyState,\n        connect,\n        disconnect,\n        sendMessage,\n        webSocketIns: socket\n    }\n};\n\nexport default useWebSocket"
  },
  {
    "path": "packages/utils/index.ts",
    "content": "type Fn = (...[]: any[])=> any; \n\n/**\n * 防抖\n * @param fn \n * @param delay \n * @returns \n */\nconst debounce = (fn:Fn,delay:number)=>{\n    let timer: NodeJS.Timeout| null = null;\n    return function(...args:[]){\n        if(timer) clearTimeout(timer)\n        timer = setTimeout(()=>{\n            // @ts-ignore\n            fn.call(this,...args)\n        },delay)\n    }\n}\n\n/**\n * 节流\n * @param fn \n * @param delay \n * @returns \n */\nconst throttle = (fn:Fn,delay:number)=>{\n    let oldNow = Date.now();\n    return function(...args:[]){\n        const currNow = Date.now();\n        if( currNow - oldNow < delay) return\n        oldNow = currNow;\n        // @ts-ignore\n        fn.call(this,...args)\n    }\n}\n\n/**\n * 防抖+节流\n * @param fn \n * @param DBdelay \n * @param TRdelay \n * @returns \n */\nconst throttleAndDeBounce = (fn:Fn,DBdelay:number,TRdelay:number)=>{\n    let oldNow = Date.now();\n    let timer: NodeJS.Timeout| null = null;\n    return function(...args:[]){\n        const currNow = Date.now();\n        if( currNow - oldNow < TRdelay){\n            if(timer) clearTimeout(timer);\n            timer= setTimeout(()=>{\n                oldNow = currNow;\n                // @ts-ignore\n                fn.call(this,...args)\n            },DBdelay)\n            return\n        }\n        oldNow = currNow;\n        // @ts-ignore\n        fn.call(this,...args)\n    }\n};\n\ntype Serializer<T> = {\n    read(raw: string): T\n    write(value: T): string\n}\n\n/**\n * 按照类型格式数据的常量Map\n */\nconst TypeSerializers: Record<'boolean' | 'object' | 'number' | 'any' | 'string', Serializer<any>> = {\n    boolean: {\n      read: (v: any) => v != null ? v === 'true' : null,\n      write: (v: any) => String(v),\n    },\n    object: {\n      read: (v: any) => v ? JSON.parse(v) : null,\n      write: (v: any) => JSON.stringify(v),\n    },\n    number: {\n      read: (v: any) => v != null ? Number.parseFloat(v) : null,\n      write: (v: any) => String(v),\n    },\n    any: {\n      read: (v: any) => (v != null && v !== 'null') ? v : null,\n      write: (v: any) => String(v),\n    },\n    string: {\n      read: (v: any) => v != null ? v : null,\n      write: (v: any) => String(v),\n    },\n}\n\n/**\n * 获取数据类型\n * @param defaultValue \n * @returns \n */\nconst getValueType = (defaultValue:unknown)=>{\n    return defaultValue == null\n        ? 'any'\n        : typeof defaultValue === 'boolean'\n        ? 'boolean'\n        : typeof defaultValue === 'string'\n            ? 'string'\n            : typeof defaultValue === 'object'\n            ? 'object'\n            : Array.isArray(defaultValue)\n                ? 'object'\n                : !Number.isNaN(defaultValue)\n                ? 'number'\n                : 'any';\n};\n\n\n\nexport {\n    Fn,\n    debounce,\n    throttle,\n    throttleAndDeBounce,\n    TypeSerializers,\n    getValueType\n}"
  },
  {
    "path": "packages/utils/memoryCache.ts",
    "content": "\ninterface Timer {\n    [key:string]: NodeJS.Timeout\n}\ninterface Options{\n    maxCache?: number\n}\n\nclass MemoryCache{\n    memoryCache: Map<string,any>\n    timer: Timer\n    maxCache: number\n    constructor(options?:Options){\n        this.memoryCache = new Map();\n        this.timer = {};\n        this.maxCache = options?.maxCache || 1000;\n    }\n\n    /**\n     * 增加缓存\n     * @param key \n     * @param value \n     * @param time \n     * @param timeoutCallback \n     */\n    put(\n        key: string,\n        value: any,\n        time?: number,\n        timeoutCallback?: ()=>{}\n    ){\n        if( !key || !value){\n            throw new Error('key & value is required')\n        }\n        if( this.size() >= this.maxCache){\n            this.del(\n                [...this.memoryCache][0][0]\n            )\n        }\n        \n        this.memoryCache.set(key,value);\n\n        if(\n            time \n            && typeof time === 'number' \n            && time > 0\n        ){\n            this.timer[key] = setTimeout(()=>{\n                this.del(key)\n                delete this.timer[key]\n                timeoutCallback && timeoutCallback()\n            },time)\n        }\n    };\n\n    /**\n     * 获取缓存\n     * @param key \n     * @returns \n     */\n    get(\n        key: string\n    ){\n        if( !this.has(key) ) return null\n        return this.memoryCache.get(key);\n    };\n\n    /**\n     * 判断是否有缓存\n     * @param key \n     * @returns \n     */\n    has(\n        key: string\n    ){\n        return this.memoryCache.has(key);\n    }\n\n    /**\n     * 删除缓存\n     * @param key \n     * @returns \n     */\n    del(\n        key: string\n    ){\n        if( !this.has(key ) ) return\n        if( this.timer[key] ){\n            clearTimeout( this.timer[key] )\n            delete this.timer[key]\n        }\n        this.memoryCache.delete(key)\n    }\n\n    /**\n     * 清除缓存\n     * @returns \n     */\n    clear(){\n        if( this.size() <= 0) return\n        this.memoryCache.clear()\n        for( let i in this.timer){\n            clearTimeout( this.timer[i] )\n            delete this.timer[i]\n        }\n    }\n\n    /**\n     * 获取缓存条数\n     * @returns \n     */\n    size(){\n        return this.memoryCache.size\n    }\n}\n\nexport default MemoryCache"
  },
  {
    "path": "packages/utils/testingHelpers.ts",
    "content": "export function sleep(time:number) {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve(true);\n    }, time);\n  });\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import { nodeResolve } from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport { terser } from 'rollup-plugin-terser';\nimport { babel } from '@rollup/plugin-babel';\nimport typescript from 'rollup-plugin-typescript2';\nimport dts from 'rollup-plugin-dts';\n\nconst input = 'packages/index.ts';\nconst isProd = process.env.NODE_ENV;\nconst fn = 'index';\nconst outputDir =  isProd? 'dist' : 'example/dist';\n\n\nconst basePlugins = [ // 打包插件\n  nodeResolve(), // 查找和打包node_modules中的第三方模块\n  commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理\n  typescript({ // 解析TypeScript\n    tsconfigOverride: {\n      compilerOptions: {\n        declaration: false,\n      },\n    },\n  }), \n]\n\nconst devPlugins = [];\nconst prodPlugins = [\n  babel({\n    babelHelpers: 'bundled',\n  })\n];\n\nconst plugins = [...basePlugins].concat(isProd ? prodPlugins : devPlugins);\n\n\nconst configs = [\n  {\n    input,\n    output: [\n      {\n        file: `${outputDir}/${fn}.es.js`, \n        format: 'esm',\n        globals: {\n          'vue': 'Vue'\n        }\n      },\n      {\n        file: `${outputDir}/${fn}.cjs.js`, \n        format: 'cjs',\n        globals: {\n          'vue': 'Vue'\n        }\n      },\n      {\n        name: 'v3hooks',\n        file: `${outputDir}/${fn}.js`, \n        format: 'umd',\n        globals: {\n          'vue': 'Vue'\n        },\n        plugins: [terser()],\n      },\n    ],\n    plugins: plugins,\n    external: [\n      'vue',\n      'vue-router',\n    ]\n  },\n  {\n    input,\n    output: {\n      file: `${outputDir}/${fn}.d.ts`,\n      format: 'esm',\n    },\n    plugins: [\n      dts(),\n    ],\n    external: [\n      'vue',\n    ]\n  }\n]\n\n// const configs = ['esm','cjs'].map((format)=>({\n//     input,\n//     output: {\n//       file: `${outputDir}/${format}.js`, \n//       format,\n//       globals: {\n//         'vue': 'Vue'\n//       }\n//     },\n//     plugins: plugins,\n//     external: [\n//       'vue',\n//       'vue-router',\n//     ]\n//   })\n// )\n// configs.push({\n//   input,\n//   output: {\n//     file: `${outputDir}/index.d.ts`,\n//     format: 'esm',\n//   },\n//   plugins: [\n//     dts(),\n//   ],\n//   external: [\n//     'vue',\n//   ]\n// })\n\nexport default configs\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    /* Visit https://aka.ms/tsconfig.json to read more about this file */\n\n    /* Basic Options */\n    // \"incremental\": true,                         /* Enable incremental compilation */\n    \"target\": \"es5\",                                /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */\n    \"module\": \"ESNext\",                           /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */\n    // \"lib\": [],                                   /* Specify library files to be included in the compilation. */\n    // \"allowJs\": true,                             /* Allow javascript files to be compiled. */\n    // \"checkJs\": true,                             /* Report errors in .js files. */\n    // \"jsx\": \"preserve\",                           /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */\n    \"declaration\": true,                         /* Generates corresponding '.d.ts' file. */\n    \"declarationDir\": \"./types\",\n    // \"declarationMap\": true,                      /* Generates a sourcemap for each corresponding '.d.ts' file. */\n    // \"sourceMap\": true,                           /* Generates corresponding '.map' file. */\n    // \"outFile\": \"./\",                             /* Concatenate and emit output to single file. */\n    // \"outDir\": \"./\",                              /* Redirect output structure to the directory. */\n    // \"rootDir\": \"./\",                             /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */\n    // \"composite\": true,                           /* Enable project compilation */\n    // \"tsBuildInfoFile\": \"./\",                     /* Specify file to store incremental compilation information */\n    // \"removeComments\": true,                      /* Do not emit comments to output. */\n    // \"noEmit\": true,                              /* Do not emit outputs. */\n    // \"importHelpers\": true,                       /* Import emit helpers from 'tslib'. */\n    \"downlevelIteration\": true,                  /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */\n    // \"isolatedModules\": true,                     /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */\n\n    /* Strict Type-Checking Options */\n    \"strict\": true,                                 /* Enable all strict type-checking options. */\n    // \"noImplicitAny\": true,                       /* Raise error on expressions and declarations with an implied 'any' type. */\n    // \"strictNullChecks\": true,                    /* Enable strict null checks. */\n    // \"strictFunctionTypes\": true,                 /* Enable strict checking of function types. */\n    // \"strictBindCallApply\": true,                 /* Enable strict 'bind', 'call', and 'apply' methods on functions. */\n    // \"strictPropertyInitialization\": true,        /* Enable strict checking of property initialization in classes. */\n    \"noImplicitThis\": true,                      /* Raise error on 'this' expressions with an implied 'any' type. */\n    // \"alwaysStrict\": true,                        /* Parse in strict mode and emit \"use strict\" for each source file. */\n\n    /* Additional Checks */\n    // \"noUnusedLocals\": true,                      /* Report errors on unused locals. */\n    // \"noUnusedParameters\": true,                  /* Report errors on unused parameters. */\n    // \"noImplicitReturns\": true,                   /* Report error when not all code paths in function return a value. */\n    // \"noFallthroughCasesInSwitch\": true,          /* Report errors for fallthrough cases in switch statement. */\n    // \"noUncheckedIndexedAccess\": true,            /* Include 'undefined' in index signature results */\n    // \"noImplicitOverride\": true,                  /* Ensure overriding members in derived classes are marked with an 'override' modifier. */\n    // \"noPropertyAccessFromIndexSignature\": true,  /* Require undeclared properties from index signatures to use element accesses. */\n\n    /* Module Resolution Options */\n    \"moduleResolution\": \"node\",                  /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */\n    // \"baseUrl\": \"./\",                             /* Base directory to resolve non-absolute module names. */\n    // \"paths\": {},                                 /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */\n    // \"rootDirs\": [],                              /* List of root folders whose combined content represents the structure of the project at runtime. */\n    // \"typeRoots\": [],                             /* List of folders to include type definitions from. */\n    // \"types\": [],                                 /* Type declaration files to be included in compilation. */\n    // \"allowSyntheticDefaultImports\": true,        /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */\n    \"esModuleInterop\": true,                        /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */\n    // \"preserveSymlinks\": true,                    /* Do not resolve the real path of symlinks. */\n    // \"allowUmdGlobalAccess\": true,                /* Allow accessing UMD globals from modules. */\n\n    /* Source Map Options */\n    // \"sourceRoot\": \"\",                            /* Specify the location where debugger should locate TypeScript files instead of source locations. */\n    // \"mapRoot\": \"\",                               /* Specify the location where debugger should locate map files instead of generated locations. */\n    // \"inlineSourceMap\": true,                     /* Emit a single file with source maps instead of having a separate file. */\n    // \"inlineSources\": true,                       /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */\n\n    /* Experimental Options */\n    // \"experimentalDecorators\": true,              /* Enables experimental support for ES7 decorators. */\n    // \"emitDecoratorMetadata\": true,               /* Enables experimental support for emitting type metadata for decorators. */\n\n    /* Advanced Options */\n    \"skipLibCheck\": true,                           /* Skip type checking of declaration files. */\n    \"forceConsistentCasingInFileNames\": true        /* Disallow inconsistently-cased references to the same file. */\n  },\n  \"include\": [\n    \"packages\"\n  ],\n  \"exclude\": [\n    \"node_modules\",\n    \"**/**/*.test.ts\",\n    \"**/**/*.stories.tsx\",\n    \"**/**/*.md\",\n    \"**/dist\",\n    \"packages/.test\",\n    \"packages/_docs\"\n  ]\n}\n"
  }
]