当前位置: 首页 > ds >正文

Vue3 + TypeScript + Pinia 搭建一套企业级的开发脚手架

从我最初接触vue3版本到现在已经有一年的时间。由于 vue3.2 版本的发布,<script setup&amp;amp;gt; 的实验性标志已经去掉,已经陆陆续续有不少公司开始使用 vue3.2开发项目了。这篇文章就来帮助大家如何快速使用 vue3.xtypeScriptvite 搭建一套企业级的开发脚手架 。废话不多说,直接上手开搞。文章最后有项目地址!!!

搭建前准备

  1. Vscode: 前端人必备写码神器
  2. Chrome:对开发者非常友好的浏览器(反正我是很依赖它的)
  3. Nodejs&npm:配置本地开发环境,安装 Node 后你会发现 npm 也会一起安装下来
  4. Vue.js devtools:浏览器调试插件
  5. Vue Language Features (Volar):Vscode 开发 vue3 必备插件,提供语法高亮提示,非常好用
  6. Vue 3 Snippets:vue3 快捷输入

Vue2 与 Vue3 的区别

Vue3由于完全由TS进行重写,在应用中对类型判断的定义和使用有很强的表现。同一对象的多个键返回值必须通过定义对应的接口(interface)来进行类型定义。要不然在 ESLint 时都会报错。

vue2 的双向数据绑定是利用 ES5 的一个 API Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。Vue3 中使用了 es6ProxyAPI对数据代理。

Vue3支持碎片(Fragments)

Vue2Vue3 最大的区别: Vue2使用Options APIVue3 使用的Composition API

生命周期钩子变化:

Vue2 ~~~~~~~~~~~ vue3
beforeCreate  -> setup()
created       -> setup()
beforeMount   -> onBeforeMount
mounted       -> onMounted
beforeUpdate  -> onBeforeUpdate
updated       -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed     -> onUnmounted
activated     -> onActivated
deactivated   -> onDeactivated

介绍 vite

Vite:下一代前端开发与构建工具

  • 极速的开发服务器启动
  • ⚡️ 轻量快速的热模块重载(HMR)
    ️ - 丰富的功能
  • 自带优化的构建
  • 通用的插件接口

Vite (法语意为 “迅速”,发音 /vit/)是一种全新的前端构建工具,它极大地改善了前端开发体验。

它主要由两部分组成

  • 一个开发服务器,它基于 原生 ES模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
    一套构建指令,它使用 Rollup打包你的代码,并且它是预配置的,可以输出用于生产环境的优化过的静态资源。
  • Vite 意在提供开箱即用的配置,同时它的 插件 API 和 JavaScript API 带来了高度的可扩展性,并有完整的类型支持。

使用 vite 快速创建脚手架

兼容性注意:Vite 需要 Node.js 版本 >= 12.0.0。

1. 第一步: 在需要创建项目文件目录下打开 cmd 运行以下命令
# npm 6.x
npm init @vitejs/app vite_vue3_ts --template# npm 7+, 需要额外的双横线:
npm init @vitejs/app vite_vue3_ts -- --template# yarn
yarn create @vitejs/app vite_vue3_ts --template
  1. 这里我采用 yarn来安装

    1638941494(1).png

  2. 选择 vue回车 => vue-ts回车

1638941564(1).png
  1. 打开项目文件夹,安装依赖,启动项目
# 进入项目文件夹
cd 到你的项目文件夹
# 安装依赖
yarn
# 启动
yarn dev
1638941893(1).png

约束代码风格

Eslint 支持
# eslint 安装
yarn add eslint --dev
# eslint 插件安装
yarn add eslint-plugin-vue --devyarn add @typescript-eslint/eslint-plugin --devyarn add eslint-plugin-prettier --dev# typescript parser
yarn add @typescript-eslint/parser --dev

注意: 如果 eslint安装报错:

image.png

可以尝试运行以下命令:

yarn config set ignore-engines true

运行成功后再次执行 eslint安装命令

项目下新建 .eslintrc.js

配置 eslint 校验规则:

module.exports = {root: true,env: {browser: true,node: true,es2021: true,},parser: 'vue-eslint-parser',extends: ['eslint:recommended','plugin:vue/vue3-recommended','plugin:@typescript-eslint/recommended','plugin:prettier/recommended',// eslint-config-prettier 的缩写'prettier',],parserOptions: {ecmaVersion: 12,parser: '@typescript-eslint/parser',sourceType: 'module',ecmaFeatures: {jsx: true,},},// eslint-plugin-vue @typescript-eslint/eslint-plugin eslint-plugin-prettier的缩写plugins: ['vue', '@typescript-eslint', 'prettier'],rules: {'@typescript-eslint/ban-ts-ignore': 'off','@typescript-eslint/no-unused-vars': 'off','@typescript-eslint/explicit-function-return-type': 'off','@typescript-eslint/no-explicit-any': 'off','@typescript-eslint/no-var-requires': 'off','@typescript-eslint/no-empty-function': 'off','@typescript-eslint/no-use-before-define': 'off','@typescript-eslint/ban-ts-comment': 'off','@typescript-eslint/ban-types': 'off','@typescript-eslint/no-non-null-assertion': 'off','@typescript-eslint/explicit-module-boundary-types': 'off','no-var': 'error','prettier/prettier': 'error',// 禁止出现console'no-console': 'warn',// 禁用debugger'no-debugger': 'warn',// 禁止出现重复的 case 标签'no-duplicate-case': 'warn',// 禁止出现空语句块'no-empty': 'warn',// 禁止不必要的括号'no-extra-parens': 'off',// 禁止对 function 声明重新赋值'no-func-assign': 'warn',// 禁止在 return、throw、continue 和 break 语句之后出现不可达代码'no-unreachable': 'warn',// 强制所有控制语句使用一致的括号风格curly: 'warn',// 要求 switch 语句中有 default 分支'default-case': 'warn',// 强制尽可能地使用点号'dot-notation': 'warn',// 要求使用 === 和 !==eqeqeq: 'warn',// 禁止 if 语句中 return 语句之后有 else 块'no-else-return': 'warn',// 禁止出现空函数'no-empty-function': 'warn',// 禁用不必要的嵌套块'no-lone-blocks': 'warn',// 禁止使用多个空格'no-multi-spaces': 'warn',// 禁止多次声明同一变量'no-redeclare': 'warn',// 禁止在 return 语句中使用赋值语句'no-return-assign': 'warn',// 禁用不必要的 return await'no-return-await': 'warn',// 禁止自我赋值'no-self-assign': 'warn',// 禁止自身比较'no-self-compare': 'warn',// 禁止不必要的 catch 子句'no-useless-catch': 'warn',// 禁止多余的 return 语句'no-useless-return': 'warn',// 禁止变量声明与外层作用域的变量同名'no-shadow': 'off',// 允许delete变量'no-delete-var': 'off',// 强制数组方括号中使用一致的空格'array-bracket-spacing': 'warn',// 强制在代码块中使用一致的大括号风格'brace-style': 'warn',// 强制使用骆驼拼写法命名约定camelcase: 'warn',// 强制使用一致的缩进indent: 'off',// 强制在 JSX 属性中一致地使用双引号或单引号// 'jsx-quotes': 'warn',// 强制可嵌套的块的最大深度4'max-depth': 'warn',// 强制最大行数 300// "max-lines": ["warn", { "max": 1200 }],// 强制函数最大代码行数 50// 'max-lines-per-function': ['warn', { max: 70 }],// 强制函数块最多允许的的语句数量20'max-statements': ['warn', 100],// 强制回调函数最大嵌套深度'max-nested-callbacks': ['warn', 3],// 强制函数定义中最多允许的参数数量'max-params': ['warn', 3],// 强制每一行中所允许的最大语句数量'max-statements-per-line': ['warn', { max: 1 }],// 要求方法链中每个调用都有一个换行符'newline-per-chained-call': ['warn', { ignoreChainWithDepth: 3 }],// 禁止 if 作为唯一的语句出现在 else 语句中'no-lonely-if': 'warn',// 禁止空格和 tab 的混合缩进'no-mixed-spaces-and-tabs': 'warn',// 禁止出现多行空行'no-multiple-empty-lines': 'warn',// 禁止出现;semi: ['warn', 'never'],// 强制在块之前使用一致的空格'space-before-blocks': 'warn',// 强制在 function的左括号之前使用一致的空格// 'space-before-function-paren': ['warn', 'never'],// 强制在圆括号内使用一致的空格'space-in-parens': 'warn',// 要求操作符周围有空格'space-infix-ops': 'warn',// 强制在一元操作符前后使用一致的空格'space-unary-ops': 'warn',// 强制在注释中 // 或 /* 使用一致的空格// "spaced-comment": "warn",// 强制在 switch 的冒号左右有空格'switch-colon-spacing': 'warn',// 强制箭头函数的箭头前后使用一致的空格'arrow-spacing': 'warn','no-var': 'warn','prefer-const': 'warn','prefer-rest-params': 'warn','no-useless-escape': 'warn','no-irregular-whitespace': 'warn','no-prototype-builtins': 'warn','no-fallthrough': 'warn','no-extra-boolean-cast': 'warn','no-case-declarations': 'warn','no-async-promise-executor': 'warn',},globals: {defineProps: 'readonly',defineEmits: 'readonly',defineExpose: 'readonly',withDefaults: 'readonly',},
}
项目下新建 .eslintignore
# eslint 忽略检查 (根据项目需要自行添加)
node_modules
dist
prettier 支持
# 安装 prettier
yarn add prettier --dev
解决 eslint 和 prettier 冲突

解决 ESLint中的样式规范和 prettier中样式规范的冲突,以 prettier的样式规范为准,使 ESLint 中的样式规范自动失效

# 安装插件 eslint-config-prettier
yarn add eslint-config-prettier --dev
项目下新建 .prettier.js

配置 prettier格式化规则:

module.exports = {tabWidth: 2,jsxSingleQuote: true,jsxBracketSameLine: true,printWidth: 100,singleQuote: true,semi: false,overrides: [{files: '*.json',options: {printWidth: 200,},},],arrowParens: 'always',
}
项目下新建 .prettierignore
# 忽略格式化文件 (根据项目需要自行添加)
node_modules
dist
package.json 配置:
{"script": {"lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx","prettier": "prettier --write ."}
}

上面配置完成后,可以运行以下命令测试下代码检查个格式化效果:

# eslint 检查
yarn lint
# prettier 自动格式化
yarn prettier
配置 husky + lint-staged

使用husky+ lint-staged助力团队编码规范, husky&lint-staged 安装推荐使用 mrm, 它将根据 package.json 依赖项中的代码质量工具来安装和配置 husky 和 lint-staged,因此请确保在此之前安装并配置所有代码质量工具,如 PrettierESlint

首先安装 mrm
npm i mrm -D --registry=https://registry.npm.taobao.org

husky是一个为 git 客户端增加 hook的工具。安装后,它会自动在仓库中的 .git/ 目录下增加相应的钩子;比如 pre-commit 钩子就会在你执行 git commit 的触发。

那么我们可以在 pre-commit 中实现一些比如 lint 检查单元测试代码美化等操作。当然,pre-commit阶段执行的命令当然要保证其速度不要太慢,每次 commit 都等很久也不是什么好的体验。

lint-staged,一个仅仅过滤出 Git 代码暂存区文件(被 git add 的文件)的工具;这个很实用,因为我们如果对整个项目的代码做一个检查,可能耗时很长,如果是老项目,要对之前的代码做一个代码规范检查并修改的话,这可能就麻烦了呀,可能导致项目改动很大。

所以这个lint-staged,对团队项目和开源项目来说,是一个很好的工具,它是对个人要提交的代码的一个规范和约束

安装 lint-staged

mrm安装 lint-staged 会自动把 husky一起安装下来

npx mrm lint-staged

安装成功后会发现 package.json 中多了一下几个配置:

1638942775(1).png

因为我们要结合 prettier代码格式化,所有修改一下配置:

"husky": {"hooks": {"pre-commit": "lint-staged"}},"lint-staged": {"*.{js,jsx,vue,ts,tsx}": ["yarn lint","prettier --write","git add"]}

好了,到这里代码格式化配置基本大功告成了!!!

配置文件引用别名 alias

直接修改 vite.config.ts 文件配置:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue()],resolve: {alias: {'@': path.resolve(__dirname, 'src'),},},
})

修改 tsconfig.json

{"compilerOptions": {"target": "esnext","module": "esnext","moduleResolution": "node","strict": true,"jsx": "preserve","sourceMap": true,"resolveJsonModule": true,"esModuleInterop": true,"lib": ["esnext", "dom"],"baseUrl": ".","paths": {"@/*":["src/*"]}},"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
配置 css 预处理器 scss

虽然 vite原生支持less/sass/scss/stylus,但是你必须手动安装他们的预处理器依赖

安装
yarn add dart-sass --dev
yarn add sass --dev
配置全局 scss 样式文件

src/assets 下新增 style 文件夹,用于存放全局样式文件

新建 main.scss, 设置一个用于测试的颜色变量 :

$test-color: red;

如何将这个全局样式文件全局注入到项目中呢?配置 Vite即可:

css:{preprocessorOptions:{scss:{additionalData:'@import "@/assets/style/mian.scss";'}}},
组件中使用

不需要任何引入可以直接使用全局scss定义的变量

.test{color: $test-color;
}
路由
# 安装路由
yarn add vue-router@4

src文件下新增 router文件夹 => router.ts 文件,内容如下:

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'const routes: RouteRecordRaw[] = [{path: '/',name: 'Login',component: () => import('@/pages/login/Login.vue'), // 注意这里要带上 文件后缀.vue},
]const router = createRouter({history: createWebHistory(),routes,
})export default router

修改入口文件 mian.ts :

import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'const app = createApp(App)app.use(router)app.mount('#app')

到这里路由的基础配置已经完成了,更多配置信息可以查看 vue-router 官方文档:

vue-router: https://next.router.vuejs.org/zh/guide/
vue-router4.x 支持 typescript,配置路由的类型是 RouteRecordRaw,这里 meta可以让我们有更多的发挥空间,这里提供一些参考:

  • title:string; 页面标题,通常必选。
  • icon?:string; 图标,一般配合菜单使用。
  • auth?:boolean; 是否需要登录权限。
  • ignoreAuth?:boolean; 是否忽略权限。
  • roles?:RoleEnum[]; 可以访问的角色
  • keepAlive?:boolean; 是否开启页面缓存
  • hideMenu?:boolean; 有些路由我们并不想在菜单中显示,比如某些编辑页面。
  • order?:number; 菜单排序。
  • frameUrl?:string; 嵌套外链。

这里只提供一些思路,每个项目涉及到的业务都会存在些差异,这里就不作详细讲解了,根据自己业务需求做配置即可。

统一请求封装

使用过 vue2.x 的同学应该对 axios 很熟悉了,这里我们直接使用 axios 做封装:

# 安装 axios
yarn add axios
# 安装 nprogress 用于请求 loading
# 也可以根据项目需求自定义其它 loading
yarn add nprogress
# 类型声明,或者添加一个包含 `declare module 'nprogress'
yarn add @types/nprogress --dev

实际使用中可以根据项目修改,比如RESTful api中可以自行添加put和delete请求,ResType也可以根据后端的通用返回值动态的去修改

新增 service 文件夹,service 下新增 http.ts 文件以及 api 文件夹:


1638944111(1).png

http.ts : 用于axios封装

//http.ts
import axios, { AxiosRequestConfig } from 'axios'
import NProgress from 'nprogress'// 设置请求头和请求路径
axios.defaults.baseURL = '/api'
axios.defaults.timeout = 10000
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
axios.interceptors.request.use((config): AxiosRequestConfig<any> => {const token = window.sessionStorage.getItem('token')if (token) {//@ts-ignoreconfig.headers.token = token}return config},(error) => {return error}
)
// 响应拦截
axios.interceptors.response.use((res) => {if (res.data.code === 111) {sessionStorage.setItem('token', '')// token过期操作}return res
})interface ResType<T> {code: numberdata?: Tmsg: stringerr?: string
}
interface Http {get<T>(url: string, params?: unknown): Promise<ResType<T>>post<T>(url: string, params?: unknown): Promise<ResType<T>>upload<T>(url: string, params: unknown): Promise<ResType<T>>download(url: string): void
}const http: Http = {get(url, params) {return new Promise((resolve, reject) => {NProgress.start()axios.get(url, { params }).then((res) => {NProgress.done()resolve(res.data)}).catch((err) => {NProgress.done()reject(err.data)})})},post(url, params) {return new Promise((resolve, reject) => {NProgress.start()axios.post(url, JSON.stringify(params)).then((res) => {NProgress.done()resolve(res.data)}).catch((err) => {NProgress.done()reject(err.data)})})},upload(url, file) {return new Promise((resolve, reject) => {NProgress.start()axios.post(url, file, {headers: { 'Content-Type': 'multipart/form-data' },}).then((res) => {NProgress.done()resolve(res.data)}).catch((err) => {NProgress.done()reject(err.data)})})},download(url) {const iframe = document.createElement('iframe')iframe.style.display = 'none'iframe.src = urliframe.onload = function () {document.body.removeChild(iframe)}document.body.appendChild(iframe)},
}
export default http

api: 项目中接口做统一管理,按照模块来划分

api文件下新增 login文件夹,用于存放登录模块的请求接口,login 文件夹下分别新增login.ts types.ts :

login.ts:

import http from '@/service/http'
import * as T from './types'const loginApi: T.ILoginApi = {login(params){return http.post('/login', params)}}
export default loginApi

types.ts:

export interface ILoginParams {userName: stringpassWord: string | number
}
export interface ILoginApi {login: (params: ILoginParams)=> Promise<any>
}

至此,一个简单地请求封装完成了!!!!

除了自己手动封装 axios ,这里还推荐一个 vue3 的请求库: VueRequest,非常好用,下面来看看 VueRequest有哪些比较好用的功能吧!!!

  • 所有数据都具有响应式
  • 轮询请求
  • 自动处理错误重试
  • 内置请求缓存
  • 节流请求与防抖请求
  • 聚焦页面时自动重新请求
  • ⚙️ 强大的分页扩展以及加载更多扩展
  • 完全使用 Typescript 编写,具有强大的类型提示
  • ⚡️ 兼容 Vite
  • 轻量化
  • 开箱即用

是不是很强大

官网链接: https://www.attojs.com/

状态管理 pinia

由于 vuex 4 对 typescript 的支持让人感到难过,所以状态管理弃用了 vuex 而采取了 pinia. pinia 的作者是 Vue 核心团队成员
尤大好像说 pinia可能会代替 vuex,所以请放心使用。

  • id是必要的,它将所使用 store 连接到 devtools。
  • 创建方式:new Vuex.Store(...)(vuex3)createStore(...)(vuex4)
  • 对比于 vuex3 ,state 现在是一个函数返回对象
  • 没有mutations,不用担心,state 的变化依然记录在 devtools 中。
# 安装
yarn add pinia@next

main.ts 中增加

# 引入
import { createPinia } from "pinia"
# 创建根存储库并将其传递给应用程序
app.use(createPinia())

src 文件夹下新增 store 文件夹,接在在 store 中新增 main.ts

创建 store, mian.ts :

import { defineStore } from 'pinia'export const useMainStore = defineStore({id: 'mian',state: () =>({name: '超级管理员'})
})

组建中获取 store :

<template><div>{{mainStore.name}}</div>
</template><script setup lang="ts">
import { useMainStore } from "@/store/mian"const mainStore = useMainStore()</script>
getters 用法介绍

Pinia 中的 getter 与 Vuex 中的 getter 、组件中的计算属性具有相同的功能
store=> mian.ts

import { defineStore } from 'pinia'export const useMainStore = defineStore({id: 'mian',state: () => ({name: '超级管理员',}),// gettersgetters: {nameLength: (state) => state.name.length,}
})

组件中使用:

<template><div>用户名:{{ mainStore.name }}<br />长度:{{ mainStore.nameLength }}</div><hr/><button @click="updateName">修改store中的name</button>
</template><script setup lang="ts">
import { useMainStore } from '@/store/mian'const mainStore = useMainStore()const updateName = ()=>{// $patch 修改 store 中的数据mainStore.$patch({name: '名称被修改了,nameLength也随之改变了'})
}
</script>
actions

这里与 Vuex有极大的不同,Pinia 仅提供了一种方法来定义如何更改状态的规则,放弃 mutations 只依靠 Actions,这是一项重大的改变。

PiniaActions 更加的灵活:

  • 可以通过组件或其他 action调用
  • 可以从其他 storeaction 中调用
  • 直接在 store 实例上调用
  • 支持同步或异步
  • 有任意数量的参数
  • 可以包含有关如何更改状态的逻辑(也就是 vuex 的 mutations 的作用)
  • 可以 $patch方法直接更改状态属性
import { defineStore } from 'pinia'export const useMainStore = defineStore({id: 'mian',state: () => ({name: '超级管理员',}),getters: {nameLength: (state) => state.name.length,},actions: {async insertPost(data:string){// 可以做异步// await doAjaxRequest(data);this.name = data;}},
})
环境变量配置

vite提供了两种模式:具有开发服务器的开发模式(development)和生产模式(production)

项目根目录新建:.env.development :

NODE_ENV=developmentVITE_APP_WEB_URL= 'YOUR WEB URL'

项目根目录新建:.env.production :

NODE_ENV=productionVITE_APP_WEB_URL= 'YOUR WEB URL'

组件中使用:
console.log(import.meta.env.VITE_APP_WEB_URL)

配置package.json:

打包区分开发环境和生产环境

"build:dev": "vite build --mode development",
"build:pro": "vite build --mode production",
使用组件库 Naive UI(也可以根据自己的技术栈选择)

组件库选择,这里我们选择 Naive UI 至于为什么选择它?我可以直接说尤大大推荐的吗?

# 安装 组件库
yarn add naive-ui
# 安装 字体
yarn add vfonts
如何使用
import { NButton } from "naive-ui"
<n-button>naive-ui</n-button>
全局配置 Config Provider

全局化配置设置内部组件的主题、语言和组件卸载于其他位置的 DOM 的类名。

<n-config-provider :locale="zhCN" :theme="theme"><!-- 容器 -->
</n-config-provider>
Vite 常用基础配置
基础配置

运行 代理打包 配置

server: {host: '0.0.0.0',port: 3000,open: true,https: false,proxy: {}
},

生产环境去除 console debugger

build:{...terserOptions: {compress: {drop_console: true,drop_debugger: true}}
}
生产环境生成 .gz 文件

开启 gzip可以极大的压缩静态资源,对页面加载的速度起到了显著的作用。

使用vite-plugin-compression可以 gzipbrotli 的方式来压缩资源,这一步需要服务器端的配合,vite 只能帮你打包出.gz 文件。此插件使用简单,你甚至无需配置参数,引入即可。

# 安装
yarn add --dev vite-plugin-compression

plugins 中添加:

import viteCompression from 'vite-plugin-compression'// gzip压缩 生产环境生成 .gz 文件
viteCompression({verbose: true,disable: false,threshold: 10240,algorithm: 'gzip',ext: '.gz',}),
最终 vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
//@ts-ignore
import viteCompression from 'vite-plugin-compression'// https://vitejs.dev/config/
export default defineConfig({base: './', //打包路径plugins: [vue(),// gzip压缩 生产环境生成 .gz 文件viteCompression({verbose: true,disable: false,threshold: 10240,algorithm: 'gzip',ext: '.gz',}),],// 配置别名resolve: {alias: {'@': path.resolve(__dirname, 'src'),},},css:{preprocessorOptions:{scss:{additionalData:'@import "@/assets/style/mian.scss";'}}},//启动服务配置server: {host: '0.0.0.0',port: 8000,open: true,https: false,proxy: {}},// 生产环境打包配置//去除 console debuggerbuild: {terserOptions: {compress: {drop_console: true,drop_debugger: true,},},},
})
常用插件

可以查看官方文档:https://vitejs.cn/plugins/

至此一个完整的项目框架已经初步成型,可以愉快的开始业务开发了!

非常推荐使用的 hooks 库:VueUse:https://vueuse.org/

感兴趣的可以了解一下

码云地址: https://gitee.com/fengzhongzhuifeng/type-script-vue3-vite.git

最后编辑于:2025-04-09 20:03:18


喜欢的朋友记得点赞、收藏、关注哦!!!

http://www.xdnf.cn/news/736.html

相关文章:

  • 数字化时代下的工业物联网智能体开发平台策略
  • Vue3中provide和inject数据修改规则
  • 代码随想录训练营第36天 ||1049. 最后一块石头的重量 II 494. 目标和 474. 一和零
  • C++——智能指针
  • 防抖与节流的理解与应用
  • 【C++】win 10 / win 11:Dev-C++ 下载与安装
  • 数据结构实验7.1:二叉树的遍历
  • C语言strlen和sizeof区分
  • Cadence学习笔记之---库元件制作、元件放置
  • TDengine 性能监控与调优实战指南(二)
  • 指针(2)
  • Linux 网络基础(二) (传输协议层:UDP、TCP)
  • cloudstudio学习笔记之openwebui
  • 嵌入式面试题解析:二维数组,内容与总线,存储格式
  • iwebsec靶场 文件包含关卡通关笔记11-ssh日志文件包含
  • Boost.Asio 确实属于 异步非阻塞模型
  • 多模态大语言模型arxiv论文略读(三十一)
  • 高并发场景下重试策略的演进设计
  • 【Linux】Rhcsa复习4
  • 亚马印象建材:推出“200×1200和300×1800数码釉木纹砖”新品
  • 树莓派超全系列教程文档--(36)树莓派条件过滤器设置
  • 奇异递归模板设计模式-CRTP
  • 32-工艺品商城小程序
  • 深入浅出讲解UDP检验中如何计算检验和
  • 标准的JNI (Java Native Interface) 加载函数 JNI_OnLoad
  • 4.凸包-Graham Scan
  • Spring Boot 版本与对应 JDK 版本兼容性
  • SpringCloud小白入门+项目搭建
  • `ImadcnIdentifierGenerator` 深度解析
  • 豆瓣图书数据采集与可视化分析(二)- 豆瓣图书数据清洗与处理