# v3ts_cms_bms **Repository Path**: ouepacao/v3ts_cms_bms ## Basic Information - **Project Name**: v3ts_cms_bms - **Description**: v3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bmsv3ts_cms_bms - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-12-18 - **Last Updated**: 2023-02-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 1.创建项目 > pnpm create vue@latest # 2.为什么会有两个tsconfig.json文件,区别是什么 > 一个是为node环境服务,一个是web的服务 composite是合成的意思,最后会合成一个tsconfig.json # 3.pinia使用 ```ts import { defineStore } from 'pinia' const useCounter = defineStore('counter', { state: () => ({ counter: 100 }), actions: { increment(newCounter: number) { this.counter++ } }, getters: { double: (state) => state.counter * 2 } }) export default useCounter ``` ```vue ``` # 4.axios封装 ```ts import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' import axios from 'axios' // 扩展AxiosRequestConfig interface InterceptorOptions { requestOnFullfillCb?: (config: AxiosRequestConfig) => AxiosRequestConfig, requestOnRejectCb?: (error: any) => any, responseOnFullfillCb?: (res: T) => T, responseOnRejectCb?: (error: any) => any } interface OuepaRequestConfig extends AxiosRequestConfig { interceptors?: InterceptorOptions } class Request { // axios实例类型 instance: AxiosInstance // request实例对应一个axios实例 constructor(config: OuepaRequestConfig) { this.instance = axios.create(config) // 全局拦截器 this.instance.interceptors.request.use( (config: OuepaRequestConfig) => { console.log('[axios] 全局请求成功拦截') return config }, (error) => { // 全局失败拦截器 return error } ) this.instance.interceptors.response.use( (res: AxiosResponse) => { console.log('[axios] 全局响应成功拦截') return res.data }, error => {return error} ) // 自定义拦截器判断 this.instance.interceptors.request.use( config.interceptors?.requestOnFullfillCb, config.interceptors?.requestOnRejectCb ) this.instance.interceptors.response.use( config.interceptors?.responseOnFullfillCb, config.interceptors?.responseOnRejectCb ) } // 封装网络请求的方法 request(config: OuepaRequestConfig) { /* 单次请求的拦截处理 */ // 请求成功拦截 if (config.interceptors?.requestOnFullfillCb) { config = config.interceptors.requestOnFullfillCb(config) } return new Promise((resolve, reject) => { this.instance.request(config) .then(res => { // 响应成功拦截 if (config.interceptors?.responseOnFullfillCb) { res = config.interceptors.responseOnFullfillCb(res) } resolve(res) }) .catch(err => {reject(err)}) }) } /* 常用方法封装 */ get(config: OuepaRequestConfig) {return this.request({...config, method: 'get'})} post(config: OuepaRequestConfig) {return this.request({...config, method: 'post'})} delete(config: OuepaRequestConfig) {return this.request({...config, method: 'delete'})} put(config: OuepaRequestConfig) {return this.request({...config, method: 'put'})} patch(config: OuepaRequestConfig) {return this.request({...config, method: 'patch'})} } export default Request ``` # 5.vite区分 development 和 production 环境 > Vite当中提供了环境变量,在import对象的meta中 + import.meta.env.MODE(string) 当前模式 -> 'development'|'production' + import.meta.env.DEV(boolean) 当前模式是否为开发环境 + import.meta.env.PROD(boolean) 当前模式是否为生产环境 + import.meta.env.SSR(boolean) 当前模式是否为ssr环境 # 6.vite环境下创建 `dotenv` 文件,使用和创建环境变量 + .env 文件可以创建多个 + .env.production只有在生产环境才会被读 + .env.development只有开发环境才会被读 + .env.development.local 不会被git提交,不会被服务器共享,用于隐私数据 在根目录创建 ``.env`` 文件,里面可以声明变量,声明的变量最后会被vite暴露在 `import.meta.env`之中, 但是变量名称必须以`VITE_` 开头,例如: > VITE_BASE_URL: 'http://codercba.com:1888/airbnb/api' > 使用时`import.meta.env.VITE_BASE_URL` # 7.配置Element-plus自动按需打包 (vite.config.ts) ```ts import { defineConfig } from 'vite' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), vueJsx(), AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }),], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } }) ``` # 8.使用ref时给子组件设置类型 + 子组件自己声明 ```vue // 组件 child.vue ``` + 使用 `InstanceType` ```ts import Child from './child.vue' import { ElImage } from 'element-plus' type ElImageCtx = InstanceType type ChildCtx = InstanceType ``` # 9. vite-plugin-style-import自动导入css插件配置 > 还有 pnpm i -D consola ```ts import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import' plugins: [ //... createStyleImportPlugin({ resolves: [ElementPlusResolve()], libs: [ { libraryName: 'element-plus', esModule: true, resolveStyle: (name: string) => { return `element-plus/theme-chalk/${ name }.css` } } ] }) ] ``` # 10.axios@1.2.2版本config.headers.Authorization携带 ``Bearer``token问题 ```ts interceptors: { requestOnFullfill: (config) => { if (!!getToken()) { (config.headers as AxiosHeaders).set('Authorization', `Bearer ${ getToken() }`) } return config } } ``` # 11.pinia配置数据持久化 1. > `pnpm i pinia-plugin-persist` 2. pinia配置 ```ts import piniaPersist from 'pinia-plugin-persist' const pinia = createPinia() pinia.use(piniaPersist) ``` 3. 在store中配置 ```ts persist: { enabled: true, // 开启持久化 strategies: [ { key: 'collapsed', // 存储的key paths: ['isCollapsed'], // 指定需要存储的变量 storage: sessionStorage // 使用的存储方式,默认SessionStorage } ] } ``` # 12.vite配置全局less变量 ```ts export default defineConfig({ css: { preprocessorOptions: { less: { javascriptEnabled: true, additionalData: `@import "${ path.resolve(__dirname, 'src/asset/style/base.less') }";` } } } }) ``` # 13.unplugin-auto-import插件配置自动导入模块 ```ts plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()], imports: ['vue', 'vue-router', 'pinia'], dirs: [], // 配置自定义引入的模块 }) ``` # 14.使用全局组件自动导入 ```ts import type { App } from 'vue' const libraryInstallation = (app: App) => { const modules: Record = import.meta.glob('./*.vue', {eager: true}) const components: any = {} for (const m in modules) { components[m.split('./')[1].replace('.vue', '')] = modules[m].default } console.log(components) for (const cpn in components) { app.component(cpn, components[cpn]) } } export default libraryInstallation ``` # 15.nextTick原理 + 在vue过去的版本中(例如vue2),nextTick的宏微任务进行了多次的变更,最终在vue3中确认使用微任务来执行nextTick中的回调 > 在vue2的源码中,首先对于浏览器支持情况做了判断,而后再决定使用微任务或宏任务 > 在vue3中,首先判断当前是否还有任务队列 ```ts const resolvedPromise = /*#__PURE__*/ Promise.resolve() as Promise // 当前没有任务时 let currentFlushPromise: Promise | null = null // 当前任务 const RECURSION_LIMIT = 100 type CountMap = Map export function nextTick( this: T, fn?: (this: T) => void ): Promise { const p = currentFlushPromise || resolvedPromise // 判断当前是否还有任务在执行 return fn ?p.then(this ?fn.bind(this) :fn) :p } ``` # 16.style中使用 `v-bind` 的注意事项 > v-bind 绑定变量是记得要加 字符串包裹 `[笑哭]` # 17.countup.js 数字动画库 > pnpm i countup.js ```ts import { CountUp } from 'countup.js' const countup1Ref = ref() // 参数一:执行动画的元素 // 参数二:数字递增的值 // 参数三:options onMounted(() => { const countup1 = new CountUp(countup1Ref.value as HTMLElement, +props.number1, { startVal: 1 }) countup1.start() }) ```