# vue3-ts-cms **Repository Path**: China-Htf/vue3-ts-cms ## Basic Information - **Project Name**: vue3-ts-cms - **Description**: No description available - **Primary Language**: TypeScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-01-07 - **Last Updated**: 2022-01-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # vue3-ts-cms ## 一、创建项目 ### webpack #### 创建 ```node vue create vue3-ts-cms ``` #### 配置 ``` Default ([Vue 2] babel, eslint) Default (Vue 3) ([Vue 3] babel, eslint) Manually select features 自己选择配置 ``` ``` (*) Choose Vue version 可选择 vue 版本(√) (*) Babel 是否选择 babel(例如: es6 转换 es5)(√) (*) TypeScript 是否使用 TypeScript(√) ( ) Progressive Web App (PWA) Support 项目是否支持 PWA(X) ( ) Router 是否默认添加 router 路由(后续自行配置路由) ( ) Vuex 是否默认添加 Vuex 状态管理(后续自行配置) (*) CSS Pre-processors 是否选择 CSS 预处理器(√) (*) Linter / Formatter 是否选择 ESLint 对代码进行格式化限制(√) ( ) Unit Testing 是否添加单元测试(X) ( ) E2E Testing 是否添加 E2E 测试(X) ``` #### 版本 ``` 2.x > 3.x 选择 vue3 ``` #### 风格 ``` Use class-style component syntax? (y/N) 是否使用 class 风格组件(N)不使用 ``` #### Babel ``` Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n) 是否使用 Babel 处理 TS (Y)使用 ``` #### 预处理器 ``` Sass/SCSS (with dart-sass) Sass/SCSS (with node-sass) > Less 此时我们使用 LESS Stylus ``` #### ESLint ``` ESLint with error prevention only ESLint + Airbnb config ESLint + Standard config > ESLint + Prettier Prettier 使代码看起来更优雅 使用这个 TSLint (deprecated) ``` ``` >(*) Lint on save 保存的时候就使用 Lint ( ) Lint and fix on commit ``` #### 储放 ``` > In dedicated config files 配置信息单独储放一个文件 使用该项 In package.json ``` #### 保存 ``` Save this as a preset for future projects? (y/N) 保存配置信息,方便下次使用 (N)不需要 ``` ## 二、项目规范 ### 代码规范 #### editorconfig > EditorConfig 有助于为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格。 > > VSCode需要安装一个插件:EditorConfig for VS Code ```yaml # http://editorconfig.org root = true [*] # 表示所有文件适用 charset = utf-8 # 设置文件字符集为 utf-8 indent_style = space # 缩进风格(tab | space) indent_size = 2 # 缩进大小 end_of_line = lf # 控制换行类型(lf | cr | crlf) trim_trailing_whitespace = true # 去除行首的任意空白字符 insert_final_newline = true # 始终在文件末尾插入一个新行 [*.md] # 表示仅 md 文件适用以下规则 max_line_length = off trim_trailing_whitespace = false ``` #### prettier > Prettier 是一款强大的代码格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等语言,基本上前端能用到的文件格式它都可以搞定,是当下最流行的代码格式化工具。 > > VSCode需要安装prettier的插件 ##### 安装 ```shell npm install prettier -D ``` ##### 配置.prettierrc文件 * useTabs:使用tab缩进还是空格缩进,选择false; * tabWidth:tab是空格的情况下,是几个空格,选择2个; * printWidth:当行字符的长度,推荐80,也有人喜欢100或者120; * singleQuote:使用单引号还是双引号,选择true,使用单引号; * trailingComma:在多行输入的尾逗号是否添加,设置为 `none`; * semi:语句末尾是否要加分号,默认值true,选择false表示不加; ```json { "useTabs": false, "tabWidth": 2, "printWidth": 80, "singleQuote": true, "trailingComma": "none", "semi": false } ``` ##### 创建.prettierignore忽略文件 ``` /dist/* .local .output.js /node_modules/** **/*.svg **/*.sh /public/* ``` ##### package.json配置scripts ```json "prettier": "prettier --write ." ``` ## 三、第三方库集成 ### vue-router #### 安装 ```shell npm install vue-router@next ``` #### 创建 ```typescript /** * createRouter:创建路由 * createWebHashHistory:路由模式 */ import { createRouter, createWebHashHistory } from 'vue-router' /** * type:代表引入的是一个类型 * RouteRecordRaw:router提供的路由类型 */ import type { RouteRecordRaw } from 'vue-router' const routes: RouteRecordRaw[] = [ { // 默认路由 path: '/', redirect: '/login' }, { path: '/login', component: () => import('@/views/login/login.vue') }, { path: '/main', component: () => import('@/views/main/main.vue') }, ] const router = createRouter({ // 引入路由 routes, // 设置路由模式 history: createWebHashHistory() }) export default router ``` #### 配置 ```typescript import router from './router' createApp(App).use(router).mount('#app') ``` #### 使用 ```typescript ``` ### vuex #### 安装 ```shell npm install vuex@next ``` #### 创建 ```typescript /** * createStore:创建 vuex */ import { createStore } from 'vuex' const store = createStore({ state: () => { return { name: 'coderhtf' } } }) export default store ``` #### 配置 ```typescript createApp(App).use(router).use(store).mount('#app') ``` #### 使用 ```typescript

{{ $store.state.name }}

``` ### element-Plus #### 安装 ```shell npm install element-plus ``` #### 全局 ```typescript // 引入组件库 import ElementPlus from 'element-plus' // 引入样式 import 'element-plus/lib/theme-chalk/index.css' createApp(App).use(router).use(store).use(ElementPlus).mount('#app') ``` #### 局部 > 安装babel的插件: ```shell npm install babel-plugin-import -D ``` > 配置babel.config.js > > 详见官方文档,有介绍 ```json module.exports = { plugins: [ [ 'import', { // 从该库引入东西时 libraryName: 'element-plus', customStyleName: name => { // 引入相应的 css 文件 return `element-plus/lib/theme-chalk/${name}.css` } } ] ], presets: ['@vue/cli-plugin-babel/preset'] } ``` > 局部引入组件进行封装 > > element-plus 的组件进行全局注册,各组件使用的时候,不需要引入 > > main.js:组件进行全局注册 ```typescript // 引入自定义组件 import { registerApp } from './global' /** * 注册 element-plus * app 对象传参过去 */ app.use(registerApp) ``` > global文件夹的index ```typescript /** * App:vue内部给 app 添加的类型 */ import type { App } from 'vue' // 自定义组件:element-plus 的使用 import registerElement from './register-element' // 导出注册的element组件 export function registerApp(app: App) { // 自定义组件进行使用,并且 app 传参过去 app.use(registerElement) } ``` > register-element:该文件就是储存全局注册的element组件 ```typescript /** * App:vue内部给 app 添加的类型 */ import { App } from 'vue' // 样式文件 import 'element-plus/lib/theme-chalk/base.css' // 导出 element-plus 组件 import { ElButton } from 'element-plus' // 需要注册的组件 const components = [ ElButton ] // 进行导出 export default function(app: App) { // 循环出需要注册的组件 for (const cpn of components) { // 进行全局注册 app.component(cpn.name, cpn) } } ``` ### axios #### 安装 ```shell npm install axios ``` #### 封装 > service/index.ts > > 总出口 ```typescript // TFRequest 封装的 axios 请求组件 import TFRequest from './request' const tfRequest = new TFRequest({ /** * 环境变量中设置的 * VUE_APP_BASE_URL:请求前缀路径 * VUE_APP_TIME_OUT:请求超时时间 */ baseURL: process.env.VUE_APP_BASE_URL, timeout: process.env.VUE_APP_TIME_OUT }) export default tfRequest ``` > service/request/index.ts > > 封装的axios组件 ```typescript // 引入 axios import axios from 'axios' // 引入类型 import type { AxiosInstance, AxiosRequestConfig } from 'axios' class TFRquest { // 创建一个实例对象 instance: AxiosInstance // 构造器:通过new关键字创建一个实例时,构造函数会被调用 // 构造函数不需要返回任何值,默认返回当前创建出来的实例 // config:请求的网络地址 constructor(config: AxiosRequestConfig) { this.instance = axios.create(config) } // 开始请求 // config:请求后缀以及方式 request(config: AxiosRequestConfig): void { this.instance.request(config).then(res => { console.log(res) }) } } export default TFRquest ``` #### 使用 ```typescript import tfRequest from './service' tfRequest.request({ url: '/home/multidata', method: 'GET' }) ``` #### 拦截与响应 > TFRequestInterceptors:设置请求接口以及类型 > > TFRequestConfig:axios内置类型继承过去以及自定义接口赋值进去 > > interceptors:设置我们的实例对象 > > ?. 都设置成为可选类型 可使用拦截器可不使用 ```typescript // 定义请求拦截器与响应器接口 interface TFRequestInterceptors { // 请求拦截器成功 requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig // 请求拦截器失败 requestInterceptorCatch?: (error: any) => any // 请求响应器成功 responseInterceptor?: (res: AxiosResponse) => AxiosResponse // 请求响应器失败 responseInterceptorCatch?: (error: any) => any } // TFRequestConfig 继承 AxiosRequestConfig 的类型 // TFRequestInterceptors 定义的类型赋值进去 interface TFRequestConfig extends AxiosRequestConfig { interceptors?: TFRequestInterceptors } class TFRquest { // 创建一个实例对象 instance: AxiosInstance interceptors?: TFRequestInterceptors // 构造器:通过new关键字创建一个实例时,构造函数会被调用 // 构造函数不需要返回任何值,默认返回当前创建出来的实例 // config:请求的网络地址 constructor(config: TFRequestConfig) { this.instance = axios.create(config) this.interceptors = config.interceptors // 请求拦截器 从 config 取出对应实例的拦截器 this.instance.interceptors.request.use( // interceptors 则是其他组件传过来的参数 // ps:requestInterceptor 传来这个参数 则使用该方法 this.interceptors?.requestInterceptor, this.interceptors?.requestInterceptorCatch ) // 请求响应器 this.instance.interceptors.response.use( this.interceptors?.responseInterceptor, this.interceptors?.responseInterceptorCatch ) // 添加所有实例的拦截器 this.instance.interceptors.request.use( (config) => { console.log('所有请求成功拦截') return config }, (err) => { console.log('所有请求拦截失败') return err }) this.instance.interceptors.response.use( (res) => { console.log('所有请求响应成功'); return res }, (err) => { console.log('所有请求响应失败'); return err }) } ``` > service/index.ts > > 使用 ```typescript interceptors: { requestInterceptor: (config) => { console.log('请求拦截器成功'); return config }, requestInterceptorCatch: (err) => { console.log('请求拦截器失败'); return err }, responseInterceptor: (res) => { console.log('请求响应器成功'); return res }, responseInterceptorCatch: (err) => { console.log('请求响应器失败'); return err } } ``` #### 接口类型封装 > type.ts ```typescript import { AxiosRequestConfig, AxiosResponse } from 'axios' // 定义请求拦截器与响应器接口 export interface TFRequestInterceptors { // 请求拦截器成功 requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig // 请求拦截器失败 requestInterceptorCatch?: (error: any) => any // 请求响应器成功 responseInterceptor?: (res: AxiosResponse) => AxiosResponse // 请求响应器失败 responseInterceptorCatch?: (error: any) => any } // TFRequestConfig 继承 AxiosRequestConfig 的类型 // TFRequestInterceptors 定义的类型赋值进去 export interface TFRequestConfig extends AxiosRequestConfig { interceptors?: TFRequestInterceptors } ``` > index.ts ```typescript import type {TFRequestInterceptors,TFRequestConfig} from './type' ``` #### 全局请求拦截 ```typescript this.instance.interceptors.request.use( (config) => { console.log('所有请求成功拦截') return config }, (err) => { console.log('所有请求拦截失败') return err }) this.instance.interceptors.response.use( (res) => { console.log('所有请求响应成功'); return res }, (err) => { console.log('所有请求响应失败'); return err }) ``` #### 单独请求拦截 ```typescript request(config: TFRequestConfig): void { // 单独请求拦截 if(config.interceptors?.requestInterceptor) { config = config.interceptors.requestInterceptor(config) } this.instance.request(config).then(res => { if(config.interceptors?.responseInterceptor) { res = config.interceptors.responseInterceptor(res) } console.log(res); }) } ``` > 使用 > > main.ts ```typescript tfRequest.request({ url: '/home/multidata', method: 'GET', // 每个请求单独的拦截 interceptors: { requestInterceptor: (config) => { console.log('单独请求'); return config } } }) ``` ## 四、基本搭建 ### token > 在我们每个实例的请求中,都需要携带 token > > service/index.ts ```typescript // 携带 token 的拦截 const token = '' f (token) { config.headers.Authorization = `Bearer ${token}` } console.log('请求拦截器成功'); ``` ### 错误信息 ```typescript // 服务器返回错误信息 const data = res.data if (data.returnCode === '-1001') { console.log('错误信息 请求失败'); } else { return data } ``` ```typescript // 响应失败 if (err.response.status === 404) { console.log('404的错误'); } return err ``` ### loading > service/request/index.ts ```typescript import { ElLoading } from 'element-plus'; import { ILoadingInstance } from 'element-plus/lib/el-loading/src/loading.type' class TFRquest { loading?: ILoadingInstance // 添加所有实例的拦截器 this.instance.interceptors.request.use( (config) => { console.log('所有请求成功拦截') // 开启 loading this.loading = ElLoading.service({ // 开启蒙版效果 lock: true, // 显示文字 text: '请求加载中....', // 背景颜色 background: 'rgba(0, 0, 0, 0.5)' }); return config }, this.instance.interceptors.response.use( (res) => { console.log('所有请求响应成功'); // 将loading移除 this.loading?.close() }, (err) => { // 将 loading 移除 this.loading?.close() } ) } ``` ## 五、首页