古语云:“工欲善其事,必先利其器”。 我们后端领域也是如此。 想要做好一个项目,就必须有一个完整的项目架构作为支撑,才能够更好的Teamwork,为业务做好嫁衣。
创建新项目时,通常需要一些基本配置。 当我们没有基础模板时,我们需要从其他项目中复制过来,甚至需要自己重新绘制一些工具类。 这个时候我们就急需这个基础模板。 。
源地址
有新项目的,并且想尝试 Vue + TypeScript 开发的可用这套模板
基本功能列表
【红色部分尚未完成】
创建项目
选择 [Vue Cli](https://cli.vuejs.org/zh/guide/) 脚手架 快速创建
`vue create xxx`
目录结构
mac 下安装 brew
/bin/zsh -c "$(curl-fsSL)"
安装 tree
酿造安装树
树形列表目录结构
├── README.md 说明文件
├── babel.config.js bable 配置文件
├── jest.config.js 单元测试配置文件
├── package.json 项目信息文件
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── other.html
│ └── static 静态资源文件
│ ├── css
│ │ └── reset.css
│ └── worker Web Workers 文件夹(根据 Web Workers 的特殊性、需要放在在服务器)
│ └── test.worker.js
├── src
│ ├── api api 管理,按多页面分文件夹
│ │ ├── default-page
│ │ │ ├── index.ts 导出 api
│ │ │ └── testModule.api.ts 页面下的小模块 api
│ │ └── other-page
│ │ ├── index.ts
│ │ └── newsModule.api.ts
│ ├── assets 静态资源文件,但会经过 webpack 进行编译,不需要编译的可以放到 public 目录下
│ │ └── styles 公共样式(基础样式、其它公用样式)
│ │ ├── common.scss
│ │ └── pageAnimate.scss
│ ├── components
│ │ ├── business 业务组件
│ │ │ └── xw-list
│ │ │ ├── index.ts
│ │ │ ├── index.type.ts
│ │ │ └── index.vue
│ │ ├── common 基础组件
│ │ │ ├── xw-pagination
│ │ │ │ ├── index.type.ts
│ │ │ │ └── index.vue
│ │ │ ├── xw-search
│ │ │ │ ├── generateEl.vue
│ │ │ │ ├── index.type.ts
│ │ │ │ └── index.vue
│ │ │ └── xw-table
│ │ │ ├── coustomColumn.vue
│ │ │ ├── generateElTable.ts
│ │ │ ├── generateElTableColumn.ts
│ │ │ ├── index.type.ts
│ │ │ └── index.vue
│ │ └── example 例子文件
│ │ ├── langExample.vue
│ │ ├── requestExample.vue
│ │ ├── vuexExample.vue
│ │ ├── workerExample.vue
│ │ └── wsExample.vue
│ ├── directive 指令
│ │ ├── animate.directive.ts
│ │ ├── copy.directive.ts
│ │ ├── debounce.directive.ts
│ │ ├── draggable.directive.ts
│ │ ├── emoji.directive.ts
│ │ ├── index.ts
│ │ ├── longpress.directive.ts
│ │ └── permissions.directive.ts
│ ├── i18n 国际化
│ │ ├── index.ts
│ │ └── lang
│ │ ├── en.ts
│ │ └── zh.ts
│ ├── layout 项目布局
│ │ ├── base.layout.vue
│ │ └── other.layout.vue
│ ├── mock mock 数据
│ │ └── index.js
│ ├── plugins 项目插件
│ │ ├── config.ts
│ │ ├── index.ts
│ │ └── lazyLoad.plugin.ts
│ ├── router 路由管理,按多页面分文件夹
│ │ ├── config.ts
│ │ ├── default
│ │ │ └── module1.router.ts 页面下的小模块 api
│ │ ├── globalHook.ts 全局路由钩子
│ │ ├── index.ts 导出所有路由
│ │ └── other
│ │ └── module1.router.ts
│ ├── shims-tsx.d.ts
│ ├── shims-vue.d.ts
│ ├── store Vuex管理,按多页面分文件夹
│ │ ├── common 基础的 Vuex 模块
│ │ │ ├── permissions.vuex.ts
│ │ │ └── user.vuex.ts
│ │ ├── default
│ │ │ └── home.vuex.ts
│ │ └── index.ts 导出基础的 Vuex 模块
│ ├── theme 主题
│ │ ├── fonts
│ │ │ ├── element-icons.ttf
│ │ │ └── element-icons.woff
│ │ └── index.css
│ ├── types 类型控制文件(提供语法提示)
│ │ └── vue.d.ts
│ ├── utils 工具函数文件夹
│ │ ├── common.ts 通用的 js 函数
│ │ ├── dom.ts dom 操作相关的
│ │ ├── eventCenter.ts 发布订阅者模式(事件管理中心)
│ │ ├── progressBar.ts 页面进度条
│ │ ├── readyLocalStorage.ts 读取本地存储数据(用户信息、token、权限等)并存到 Vuex 中
│ │ ├── request Ajax 请求封装
│ │ │ ├── index.ts
│ │ │ ├── index.type.ts
│ │ │ └── request.ts
│ │ ├── requestInstance.ts Ajax 实例
│ │ ├── useElement.ts 按需使用 Element-ui
│ │ └── ws.ts WebSocket 通讯
│ └── views 按多页面分文件夹
│ ├── 404.vue
│ ├── default-page
│ │ ├── App.vue
│ │ ├── main.ts
│ │ └── test-module 小模块
│ │ ├── home 具体页面
│ │ │ └── index.vue
│ │ └── home2
│ │ └── index.vue
│ ├── login.vue
│ └── other-page
│ ├── App.vue
│ ├── main.ts
│ └── news-module 小模块
│ ├── news1 具体页面
│ │ ├── components 页面内组件
│ │ │ └── coustomColumnHeader.vue
│ │ └── index.vue
│ └── news2
│ ├── components
│ │ └── coustomColumnHeader.vue
│ └── index.vue
├── tests
│ └── unit
│ └── example.spec.ts
├── tsconfig.json
├── vue.config.js webpack 配置文件
├── yarn-error.log
└── yarn.lock
└── .env.development 本地环境配置
└── .env.production 生产环境配置
└── .env.staging 测试环境配置
环境分辨率
通过webpack提供的模式来实现不同的
# 指定模式
NODE_ENV = "development"
# Ajax 地址
VUE_APP_REQUEST_URL = 'http://localhost:8080'
.env.生产
NODE_ENV = "production"
VUE_APP_REQUEST_URL = 'http://prod.com'
.env.staging
NODE_ENV = "production"
VUE_APP_REQUEST_URL = 'http://staging.com'
包.json
"scripts": {
"serve": "vue-cli-service serve --mode development", // 开发
"build:stage": "vue-cli-service build --mode staging", // 测试
"build": "vue-cli-service build" // 生产
}
constApp=()=>import(/*webpackChunkName:app*/'./app.vue')
/*webpackChunkName:app*/组件分块
表单的异步组件
异步组件
constApp=resolve=>require(["./app.vue"],resolve)
手动路由
通过webpack的require.context引入递归模块
require.context(
directory: String,
includeSubdirs: Boolean /* 可选的,默认值是 true */,
filter: RegExp /* 可选的,默认值是 /^./.*$/,所有文件 */,
mode: String /* 可选的, 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once',默认值是 'sync' */
)
require.context 的参数不能接收变量
【好处:减少多人开发时的冲突,忘记引入新模块】
API管理
api统一管理。 项目中,我们其中一个页面的【增删改查】url都是统一的,只会改变请求的形式,所以url是集中管理的
Vuex/自定义状态管理 Vuex
自定义状态管理(发布-订阅者模式)
【思考:】不同类型的风暴函数存储在一个集合中,通过点击、取消、调度对集合进行处理。
其中,闭包用于处理单次监听,存储是否已经执行过
once(eventName: string, cb: CbType) {
const { eventStack } = this
const eventValue = eventStack[eventName]
const tempCb = () => {
let isOutOfDate = false
return (data: object) => {
if (isOutOfDate) return
cb(data)
isOutOfDate = true
}
}
eventValue ? eventValue.push(tempCb()) : eventStack[eventName] = [tempCb()]
}
axios 包
【功能列表】
/**
* token 处理
* @param config 配置项
* @param customConfig 自定义配置项
*/
private addToken(config: AxiosRequestConfig, customConfig: CustomConfigType) {
if (customConfig.isNeedToken) {
config.headers = {
token: store.getters['userStore/getToken'] || ''
};
} else {
config.headers = {};
}
}
/**
* 发起请求
* @param config 配置项
* @param customConfig 自定义配置
*/
private async transfromRquest(
config: AxiosRequestConfig,
customConfig: CustomConfigType = {}
): Promise<AxiosResponse> {
customConfig = { ...this.defaultCustomConfig, ...customConfig };
this.transformUrl(config.url);
this.handleLoading(customConfig, true);
this.addToken(config, customConfig);
this.requestCount++
try {
const result = await this.axios.request(config);
return result;
} catch (error) {
const { code, config } = error
if (code === 401) {
// 解决 token 失效的
// 方案一 跳转至登录页
// store.commit('userStore/setToken', '')
// store.commit('permissionsStore/setPermissions', {})
// router.replace({ path: '/login', query: {
// redirectUrl: router.currentRoute.fullPath
// } })
// 方式二 自动刷新 token 并重新发起失败的请求
const res = await this.transfromRquest({
method: 'post',
url: '/refresh-token'
})
console.log(res, '/refresh-token')
store.commit('userStore/setToken', res.data.token)
return this.transfromRquest(config)
// 方式三 在请求拦截里面先校验 token 是否过期 再发起请求
}
this.handleError(customConfig, error);
return Promise.reject(error);
} finally {
this.requestCount--
this.handleLoading(customConfig, false);
}
}
[缺点:]
与这些取消请求类似,服务器虽然收到了,但只是浏览器做了一层处理,无法等待响应。
当需要避免重复提交数据时typescript做界面,这些方法的实现是不准确的。 可以考虑聚焦、可变控制功能执行、可变控制按钮点击状态等。
WebWorkers简介
postMessage无法发送函数
WebSocket的嵌入
请参阅打包简单的 WebSocket 库
Element-ui列表组件的封装
[主意:]
<header class="list-header animate__animated animate__fadeIn">
<slot name="head" />
<xw-search
v-if="searchOption"
:searchOption="searchOption"
:searchParams="searchParams"
@onSearch="getList"
>
<slot name="search" />
</xw-search>
</header>
<main class="list-main">
<slot name="main" />
<xw-table :tableOption="tableOption" />
</main>
<footer class="list-footer">
<slot name="footer" />
<xw-pagination
v-if="paginationOption"
:paginationOption.sync="paginationOption"
@onPagination="getList"
/>
</footer>
import { Component } from 'vue'
export interface TableOption {
// element-ui 表格的配置属性
tableAttribute: TableAttribute
// 列的配置属性
tableColumn: TableColumn[]
}
export interface TableAttribute {
// 属性
props: {
data: object[]
[index: string]: any
}
// 事件
on: { [key: string]: Function | Function[] }
}
export interface TableColumn {
// 属性
props: {
label?: string
prop?: string
[index: string]: any
},
// 插槽
slots?: {
[index: string]: {
// 属性
options?: object
// 自定义组件
component: Component
}
}
// 多级表头
columnChild?: TableColumn[]
}
复杂的情况
编辑当前行,根据权限显示不同的按钮,加载和禁用按钮
gitcommit提交记录优化
使用commitizen代替你的gitcommit,commitizen还需要适配器的配合typescript做界面,官方推荐cz-conventional-changelog
"scripts": {
"commit": "git-cz"
},
"config": {
"commitizen": {
"path": "node_modules/cz-conventional-changelog"
}
}
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
}
}
同时在项目目录下创建.cz-config.js.commitlintrc.js文件
疗效如下:
指令封装
源地址
以上只是简单概述,详细内容请参考源码地址
博文推荐
【笔记不易,如果对您有帮助,请点赞,谢谢】