项目构建
第三方库集成
element-plus
全局引用
所有的组件全部集成
优点:集成比较简单 缺点:全部打包
按需引用
优点:包会小一点 缺点:引用起来稍微麻烦一点
- 使用 ele-plus
npm install -D unplugin-vue-components unplugin-auto-import
```js
// vue.config.js
const { defineConfig } = require('@vue/cli-service')
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
module.exports = defineConfig({
transpileDependencies: true,
outputDir: './build',
configureWebpack: {
plugins: [
// ...
AutoImport({
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
})
]
}
})
<template>
<div id="main">
<h1>main页面</h1>
<el-button type="primary">按钮</el-button>
</div>
</template>
axios集成
npm install axios
模拟请求
import axios, { AxiosResponse } from 'axios'
// axios的实例对象
axios
.get('http://httpbin.org/get', {
params: {
name: 'cvCoder'
}
})
.then((res: AxiosResponse) => {
console.log(res)
})
请求拦截
可以做哪些
- 设置请求需要携带的token:header[token]='token'
- 请求时间比较长时,显示loading
- 修改返回的响应数据
// axios的拦截器
// fn1: 请求发送成功会执行的函数
// fn2:请求发送失败会执行的函数
// axios.interceptors.request.use(fn1, fn2)
axios.interceptors.request.use(
(config) => {
// 1.给请求添加token
// 2,.loading动画
console.log('请求成功的拦截')
return config
},
(err) => {
console.log('请求发送错误')
return err
}
)
// axios.interceptors.response.use(fn1, fn2)
// fn1: 数据响应成功(服务器正常的返回了数据 20x[响应状态])
// fn2: 数据响应失败
axios.interceptors.response.use(
(res) => {
console.log('响应成功的拦截')
return res // 修改返回的响应数据
},
(err) => {
console.log('服务器响应失败')
return err
}
)
区分不同环境
-
在开发中,有时候我们需要根据不同的环境设置不同的环境变量,常见的有三种环境:
-
开发环境:development;
-
生产环境:production;
-
测试环境:test;
-
-
如何区分环境变量呢?常见有三种方式:
-
方式一:手动修改不同的变量;
-
方式二:根据process.env.NODE_ENV的值进行区分;
-
方式三:编写不同的环境变量配置文件;
-
/**
* 开发 http://httpbin.org/dev
* 生产 http://httpbin.org/prod
*
*/
// 根据process.env.NODE_ENV
/**
* 开发环境:development
* 成产环境:production
* 测试环境:test
*/
let BASE_URL = ''
let BASE_NAME = ''
console.log(process.env.NODE_ENV)
if (process.env.NODE_ENV === 'development') {
BASE_URL = 'http://httpbin.org/dev'
BASE_NAME = 'D'
} else if (process.env.NODE_ENV === 'production') {
BASE_URL = 'http://httpbin.org/prod'
BASE_NAME = 'P'
} else {
BASE_URL = 'http://httpbin.org/test'
BASE_NAME = 'T'
}
export { BASE_URL, BASE_NAME }
VueCLI下
- 名字不可以乱起
- 1.BASE_URL
- 2.NODE_ENV
- 3.VUE_APP_XXXX[以VUE_APP_开头]
只有
NODE_ENV,BASE_URL和以VUE_APP_开头的变量将通过webpack.DefinePlugin静态地嵌入到客户端侧的代码中
// .env.development文件
VUE_APP_BASE_URL = http://httpbin.org/development
VUE_APP_BASE_NAME = D
// .env.production文件
VUE_APP_BASE_URL = http://httpbin.org/prod
VUE_APP_BASE_NAME = p
// .env.test
VUE_APP_BASE_URL = http://httpbin.org/test
VUE_APP_BASE_NAME = T
// main.ts
console.log(process.env.VUE_APP_BASE_URL)
console.log(process.env.VUE_APP_BASE_NAME)
不同环境打印对应环境内容
axios封装
- axios第三方库不维护(浏览器升级、webpack更新等问题就会产生bug)
- 后续更换新的库时,项目中对axios都有依赖,那么很多地方都要修改,会产生很多冗余操作
- 共同的特性抽离,避免冗余
// 使用
import cvRequest from './service'
interface DataType {
data: any
returnCode: string
success: boolean
}
cvRequest
.request<DataType>({
showLoadings: true,
method: 'post',
url: '/post'
})
.then((res) => {
console.log('res----', res.data)
})
cvRequest
.post<DataType>({
showLoadings: true,
url: '/post'
})
.then((res) => {
console.log('res----post', res.data)
})
ts配置文件
ts源于JS,归于JS
tsconfig.json
编译选项:compilerOptions
- target: esnext
- 目标代码(ts -> js[es5|6|7])
- 配置项目的时候选择了babel编译,后面最终会用把babel对代码再做转换,babel不需要配置目标es,因为他会根据配置的.browserslistrc来自动适配浏览器
- module
- 目标代码需要使用的模块化方案([commonJS:require/module.exports][es-module:import/export]【umd: 多种模块化】)
- strict
- 严格模式的检查
- jsx
- 对JSX进行怎么样的处理(h函数、createElement,preserve[保留,不转化])
- importHelpers
- 辅助的导入功能
- moduleResolution
- 按照node的方式去解析模块
- skipLibCheck
- 跳过一些库的类型检测
- esModuleInterop、allowSyntheticDefaultImports
- export default 和 module.exports = {} 能不能混合一起使用
- es moudle和commonJS
- sourceMap
- 要不要生成映射文件
- baseUrl
- 文件路径在解析时,基本URL
- types
- 指定具体要解析使用的类型
- paths
- 路径解析
- lib
- 可以指定在项目中可以使用哪些库的类型(Proxy/window/Document) include
哪些代码需要进行编译解析 exclude 排除哪些内容
脚手架创建的配置一般不需要修改
说明
- vue文件中的defineComponent
- 意义:TS,类型推导
业务相关代码
样式相关
- 把脚手架默认的样式内容删除
- 添加初始化样式内容
初始化文件
方法一:github搜 方法二:npm i normalize.css
编写业务逻辑的相关内容
获取组件的实例类型
<login-account ref='accountRef' />
const accountRef = ref<InstanceType<typeof LoginAccount>>()
获取实例类型的原因,当通过ref去操作组件的内容时,可以有提示,使得编写逻辑更加的严谨
緩存
登录相关处理
- 1.登录的逻辑(网络请求,拿到数据后的处理)
- 2.数据保存到某一个位置
- 3.发送其他的请求(请求当前用户的信息)
- 4.拿到用户的菜单(动态路由菜单,根据角色获取【角色权限管理】)
- 5.跳到首页
获取到的数据在多处使用(共享数据),将其保存在VUEX。
文件夹介绍
- 多个项目公共的组件放在base-ui文件夹中
- 主要的组件放在components文件夹中
vuex对TS的支持较差
其中在useStore上面可以体现
解决
// store/index.ts
import { createStore, Store, useStore as useVuexStore }
import { IRootState, IStoreType } from './types'
export function useStore(): Store<IStoreType> {
return useVuexStore()
}
import { ILoginState } from './login/types'
export interface IRootState {
name: string
age: number
}
export interface IRootWithModule {
login: ILoginState
}
export type IStoreType = IRootState & IRootWithModule
element-plus使用
菜单权限控制
role based access control 基于角色的访问控制
-
方法一: 所有路由全部匹配
-
方法二:根据角色
-
方法三:菜单动态生成路由映射 菜单-路由映射 菜单-url-路由-path-component
自动构建
npm install coderwhy -g
新建
// add3page -> 3表示vue3
// user -> 表示组件
// -d -> d表示dest
// src/views/main/user -》 表示安装在哪个目录下
coderwhy add3page user -d src/views/main/user
自动构建组件以及相关的路由引用
动态路由配置
// 遍历获取指定目录下的所有ts文件
const routeFiles = require.context('../router/main', true, /\.ts/)
// true: 表示递归查找
动态路由下刷新,页面不存在
setupStore()
app.use(router)
app.use(store)
如上不能解决,考虑网友方法:解决
首次进入主页定位到第一个路由
直接在router页面的redirect到指定的路由,缺点: 当动态路由的数据发生变化,redirect的内容需要手动跟着调整
解决:
根据获取的菜单数据去拿到第一个路由firstRoute
router.beforeEach((to) => {
console.log('to', to, routes)
if (to.path !== '/login') {
const token = localCache.getCache('token')
if (!token) {
// router.push('/login')
return '/login'
}
if (to.path === '/main') {
return firstRoute?.path
}
}
})
vue3的TS
获取组件实例的类型
import loginAccount from './login-account.vue'
/**
* InstanceType<typeof loginAccount>> 获取组件实例
*/
const accountRef = ref<InstanceType<typeof loginAccount>>()
store中子模块
import { ILoginState } from './login/types' //
import { ISystemState } from './main/system/types'
import { IAnalysis } from './main/analysis/types'
/**
* IRootState 根
*/
export interface IRootState {
name: string
age: number
entireRoles: any[]
entireDepartments: any[]
entireMenus: any[]
}
/**
* 各个模块
* login system analysis
*/
export interface IRootWithModule {
login: ILoginState
system: ISystemState
analysis: IAnalysis
}
/**
* 由于vuex对TS的兼容性不好,在模块上的内不能直接提示(根类型取子模块),所以使用交叉类型
*/
export type IStoreType = IRootState & IRootWithModule
// store/index,ts
/**
* 使用自己定义的useStore的原因
* 由于vuex对TS的兼容不是很好
* 当模块进行引用获取相关内容的时候,可能存在没有提示的情况等
*/
export function useStore(): Store<IStoreType> {
return useVuexStore()
}