# vue3-typescript-template **Repository Path**: diycp2015/vue3-typescript-template ## Basic Information - **Project Name**: vue3-typescript-template - **Description**: 新项目将正式使用`vue3.0`开发; 接下来总结(对自己技术掌握的稳固)介绍(分享有需要的猿友) 技术栈: `vue3 + vuex + typescript + webpack + vant-ui + axios + less + postcss-pxtorem(rem适配)` 涉及点:目前只贴出项目初期的几个功能 - `webpack require` 自动化注册路由、自动化注册异步组价 - `axios` 请求封装(请求拦截、响应拦截、取消请求、统一处理) - `vuex` 业务模块化、 接管请求统一处理 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-02-11 - **Last Updated**: 2021-08-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 一、开篇 - `vue3.0beta`版正式上线,作为新技术热爱者,新项目将正式使用`vue3.0`开发; 接下来总结(对自己技术掌握的稳固)介绍(分享有需要的猿友) - 上篇博客介绍了`vue3.0`常用语法及开发技巧;有需要的请点击 [Vue3.0 进阶、环境搭建、相关API的使用](https://juejin.im/post/6877832502590111757) - 觉得对您有用的点个 `star` 呗 ## 二、项目介绍(移动端) - 1)技术栈: `vue3 + vuex + typescript + webpack + vant-ui + axios + less + postcss-pxtorem(rem适配)` - 2)没用官方构建工具`vite`原因:`vite` 坑还真的不少,有时候正常写法`webpack`没问题, 在`vite`上就报错 - 3)不过还是很期待 `vite` 的, 等待成熟吧, 在正式使用; - 4)涉及点:目前只贴出项目初期的几个功能 - `webpack require` 自动化注册路由、自动化注册异步组价 - `axios` 请求封装(请求拦截、响应拦截、取消请求、统一处理) - `vuex` 业务模块化、 接管请求统一处理 ## 三、项目搭建 可参考上篇文章 [Vue3.0 进阶、环境搭建、相关API的使用](https://juejin.im/post/6877832502590111757) 1. `vue-cli、vue` 下载最新版本 2. 执行命令 `vue create my_app_name` 3. 执行完上面命令接下来选择手动配置(第三个),不要选择默认配置,有很多我们用不上,选择如下图: ![](https://imgkr2.cn-bj.ufileos.com/2fc4ce04-aceb-4ac6-8f70-1b5d40fca806.jpg?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=%252F8g2wk69CINkwmTa62rKIUjSLCk%253D&Expires=1603787843) ## 三、项目主要功能 **1. `webpack require` 自动化注册路由、自动化注册异步组价** ```js // 该文件在 utils 下的 global.ts // 区分文件是否自动注册为组件,vue文件定义 isComponents 字段; 区分是否自动注册为路由定义 isRouter 字段 // 使用方式分别在 main.ts 里方法asyncComponent() 以及路由文件router下的index.ts 方法 vueRouters() import { defineAsyncComponent } from 'vue' import { app } from '../main' // 获取所有vue文件 function getComponent() { return require.context('../views', true, /\.vue$/); } // 首字母转换大写 function letterToUpperCase(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); } // 首字母转换小写 function letterToLowerCase(str: string) { return str.charAt(0).toLowerCase() + str.slice(1); } export const asyncComponent = function () { // 获取文件全局对象 const requireComponents = getComponent(); requireComponents.keys().forEach((fileSrc: string) => { const viewSrc = requireComponents(fileSrc); const fileNameSrc = fileSrc.replace(/^\.\//, '') const file = viewSrc.default; if (viewSrc.default.isComponents) { // 异步注册组件 let componentRoot = defineAsyncComponent( () => import(`@/views/${fileNameSrc}`) ) app.component(letterToUpperCase(file.name), componentRoot) } }); }; // 获取路由文件 export const vueRouters = function () { const routerList: any = []; const requireRouters = getComponent(); requireRouters.keys().forEach((fileSrc: string) => { // 获取 components 文件下的文件名 const viewSrc = requireRouters(fileSrc); const file = viewSrc.default; // 首字母转大写 const routerName = letterToUpperCase(file.name); // 首字母转小写 const routerPath = letterToLowerCase(file.name); // 设置路由路径 const fileNameSrc = fileSrc.replace(/^\.\//, ''); if (file.isRouter) { routerList.push({ path: `/${routerPath}`, name: `${routerName}`, component: () => import(`@/views/${fileNameSrc}`) }); } }); return routerList; }; ``` **2. `axios` 请求封装(请求拦截、响应拦截、取消请求、统一处理)** ```js import axios, { AxiosRequestConfig, AxiosResponse, Canceler } from 'axios' import router from '@/router' import { Toast } from 'vant' if (process.env.NODE_ENV === 'development') { // 开发环境 axios.defaults.baseURL = `https://test-mobileapi.qinlinkeji.com/api/` } else { // 正式环境 axios.defaults.baseURL = `正式环境地址` } let sourceAjaxList: Canceler[] = [] export const axionInit = () => { axios.interceptors.request.use((config: AxiosRequestConfig) => { // 设置 cancel token 用于取消请求 (当一个接口出现401后,取消后续多有发起的请求,避免出现好几个错误提示) config.cancelToken = new axios.CancelToken(function executor(cancel: Canceler): void { sourceAjaxList.push(cancel) }) // 存在 sessionId 为所有请求加上 sessionId if (localStorage.getItem(`h5_sessionId`) && config.url!.indexOf('/user/login') < 0) config.url += ('sessionId=' + localStorage.getItem(`h5_sessionId`)) if (!config.data) config.data = {} return config }, function (error) { // 抛出错误 return Promise.reject(error) }) axios.interceptors.response.use((response: AxiosResponse) => { const { status, data } = response if (status === 200) { // 如果不出现错误,直接向回调函数内输出 data if (data.code === 0) { return data } else if (data.code === 401) { // 出现未登录或登录失效取消后面的请求 sourceAjaxList.length && sourceAjaxList.length > 0 && sourceAjaxList.forEach((ajaxCancel, index) => { ajaxCancel() // 取消请求 delete sourceAjaxList[index] }) Toast({ message: data.message, duration: 2000 }) return router.push('/login') } else { return data } } else { return response } }, error => { const { response } = error // 这里处理错误的 http code or 服务器或后台报错 if (!response || response.status === 404 || response.status === 500) { if (!response) { console.error(`404 error %o ${error}`) } else { if (response.data && response.data.message) { Toast.fail({ message: '请求异常,请稍后再试!', duration: 2000 }) } } } return Promise.reject(error.message) }) } ``` **3. `vuex` 业务模块化、 接管请求统一处理** ```js // 具体请看项目store目录 import { Module } from 'vuex' import { IGlobalState } from '../../index' import * as Types from './types' import { IHomeState, ICity, IAccessControl, ICommonlyUsedDoor } from './interface' import axios from 'axios' const state: IHomeState = { cityList: [], commonlyUsedDoor: { doorControlId: '', doorControlName: '' }, accessControlList: [] } const home: Module = { namespaced: true, state, actions: { // 获取小区列表 async [Types.GET_CITY_LIST]({ commit }) { const result = await axios.post(`auth/v2/getApplyListGroupByCommunityH5?`) commit(Types.GET_CITY_LIST, result.data) }, // 获取小区门禁列表 async [Types.GET_ACCESS_CONTROL_LIST]({ commit }, data) { const result = await axios.post(`doorcontrol/v2/queryUserDoor?`, { ...data }) commit(Types.GET_ACCESS_CONTROL_LIST, result.data.userDoorDTOS) commit(Types.SET_COMMONLY_USERDOOR, result.data.commonlyUsedDoor) }, }, mutations: { // 设置小区列表 [Types.GET_CITY_LIST](state, cityList: ICity[]) { if (cityList.length !== 0) state.cityList = cityList }, // 设置小区门禁列表 [Types.GET_ACCESS_CONTROL_LIST](state, accessControlList: IAccessControl[]) { if (accessControlList.length !== 0) return state.accessControlList = accessControlList }, // 设置当前小区 [Types.SET_COMMONLY_USERDOOR](state, commonlyUsedDoor: ICommonlyUsedDoor) { state.commonlyUsedDoor = commonlyUsedDoor } } } export default home ``` **4. `home` 文件代码** ```html ``` ## 四、项目ui ![](https://imgkr2.cn-bj.ufileos.com/9e203646-e937-4fc6-8602-6f07b28675f1.jpg?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=hOlNR%252FrpzGeXFqIIQS%252FybcPqvjc%253D&Expires=1603791394) ![](https://imgkr2.cn-bj.ufileos.com/6c90779e-486c-417c-a6ac-31dffae87019.jpg?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=zW8rYy5udR5Z9V5l1aoa58j77zU%253D&Expires=1603791397) ## 五、结语 以上为个人实际项目开发总结, 有不对之处欢迎留言指正