vue-compositionAPI + ts + vue-cli4 工程搭建
1. 更新安装最新vue-cli4
安装cli4.4 npm i -g @vue/cli
2.安装composition-api
1. 为什么不直接升级vue3
因为目前vue3生态还没完善,比如加入typescript,目前还有挺多模块没有支持typescript,旧的UI组件库还没能支持vue3,会出现报错情况,还不太清楚如何去配置。
2.安装composition-api
yarn add @vue/composition-api
3. 项目初始化配置
1 项目总体预览
-
统一的模块化路由
-
统一的
Ajax模块化,fetch、api、env -
统一的--> 改变成vue3支持的vuex模块化provide和inject -
统一的公共方法,
ulits -
统一的第三方UI组件
2 项目统一配置
1 router配置
-
vue-router版本是3.2,如何是最新版本会存在不同 -
require.context,来全局导入文件// 由于webpack-env不支持ts导入,RequireContext在项目中暂时不做处理 const requireFile: RequireContext = require.context( './', // path: string true, // deep?: boolean /\.ts/ // filter?: RegExp, mode?: "sync" | "eager" | "weak" | "lazy" | "lazy-once" ) requireFile.keys().forEach((file) => { // 将index.ts return,我们需要的是当前文件下的其他文件夹下面的文件 if(file === './index.ts') { return; } const config = requireFile(file); routes = [...routes, ...(config.default || config)]; // 合并routes })
2. axios配置
-
配置不同
env环境下的baseUrl.tsconst env = process.env.NODE_ENV interface Baseconfig = { [propName: string]: string } const api: Baseconfig = { development: 'http://localhost:8080/', production: 'https://xxx/' } export const baseUrl: string = api[env] -
配置
ajax.ts,导入baseUrl.ts这里只是提供一种方案,没有很详细
import axios from 'axios'; import Vue from 'vue'; import {baseUrl} from './baseURL' import {ResponseType} from './baseInterface' import qs from 'qs'; import thisVue from '../main'; // 设置统一的过期时间 20s axios.defaults.timeout = 20000; // 接口路径统一配置 axios.defaults.baseURL = baseUrl; axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'; axios.defaults.withCredentials = true; axios.defaults.xsrfCookieName = "CSRF-TOKEN"; axios.defaults.xsrfHeaderName = "X-CSRF-TOKEN"; // http request 拦截器 // 处理特殊错误errcode函数 function _handleError(errorCode: number, errorMessage: any){ switch (errorCode) { /* 用户未登录 */ case 401: thisVue.$message.warning({ msg: '您还未登录,即将跳转登录页面', }); // 重新登录 window.location.href = baseUrl + '/passport' break; //权限不足 case 403: thisVue.$message.warning({ msg: errorMessage, }); break; //资源不存在 case 404: thisVue.$message.error({ message: '资源不存在' }); break; // //参数格式不正确 // case 409: // Message.error({ // message: errorMessage // }); // break; // // 验证异常 // case 406: // Message.error({ // message: errorMessage // }); // break; default: thisVue.$message.error({ message: errorMessage }); break; } } // http response 拦截器 axios.interceptors.response.use( (response): any => { if(response.data.errcode !== 0) { _handleError(response.data.errcode, response.data.description_cn || response.data.description) return false } return response }, (error): void => { _handleError(error.response.data.errcode, error.response.data.description_cn || error.response.dara.description) } ) const fetch = { get(url: string, params: object | Array<any> | null): Promise<ResponseType> { return new Promise((resolve) => { axios.get(url, {params}).then(res => { resolve(res.data) }) }) }, // post(url, params={}) 默认POST的类型是json数据,但其实大部分是用from:data数据,只需要配置 axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'; // 其实是qs.stringify 将格式改变了 post(url: string, params: object | Array<any> | null): Promise<ResponseType> { params = qs.stringify(params) return new Promise((resolve) => { axios.post( url, params, // config={ // headers: {'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'} // } ).then(res => { resolve(res.data) }) }) } } Vue.prototype.$fetch = fetch; export { fetch }关于引入到的一些文件
baseInterface.ts// response 返回体 export interface ResponseType { errcode: number, data: object | null | Array<any>, code?: number, description_cn?: string, description?: string } -
api统一调用,导入ajax.ts某个子文件调用
permission.tsimport {fetch} from '../ulits/ajax' export const userList = { getList(params: ParmasType){ return fetch.get('ajax/inner/permission/list', params) }, }统一收集各个子文件的
api, 与router类似// 来个类似单例模式吧 const files = () => { let filesTypes: any; return () => { if(!filesTypes) { filesTypes = require.context( './', true, /\.ts$/ ) } return filesTypes } } const ff = files() const requireFile: any = ff() let apiList = {} requireFile.keys().forEach((fileName: string) => { if(fileName === './index.ts') { return; } const config = requireFile(fileName); apiList = {...apiList, ...(config.default || config)}; }) export default apiList;
3.provide和inject代替vuex
- 其中某个模块文件
// 由于暂时不支持vuex,所以用@vue/comoisition中的provide做全局状态管理
// 返回一个provide 和 reject,使用时请使用当前文件为前缀,避免冲突
import { provide, ref, computed, inject, Ref, reactive} from '@vue/composition-api'
interface Types {
count: Ref<number>;
[key: string]: Ref<any>;
}
// 定义symbol()来放provide
const APPROVECOUNT = Symbol()
export const useApproveProvide = () => {
// 常量
const count = ref<number>(12)
// 改变
const setCount = (value: number) => {
return count.value = value
}
// compute可以实时返回
const doubleCount = computed(() => {
return count.value * 2
})
provide(APPROVECOUNT, {
count,
setCount,
doubleCount
})
}
// inject
export const useApproveInject = () => {
const countContext = inject<Types>(APPROVECOUNT)
// inject必须在provide后面
if (!countContext) {
throw new Error('inject必须在provide后面')
}
return countContext
}
-
将所有模块整合,最后引入在
main.ts中的setup方法中// 所有全局状态的入口 import {useApproveProvide, useApproveInject} from "./approve/approve"; export { useApproveInject} // 将所有的模块函数都放在这里 export const stateList = () => { useApproveProvide() }
4.第三方组件库
导入第三方组件库的时候,通常会显示找不到该模块,是因为没有支持ts,需要在shims-vue.d.ts中去掉
// 我们在 typescript 的项目中安装一些包的话,可能会报错 Could not find a declaration file for module 'xxx' ,这是因为这个包可能不是.ts文件而是.js文件
declare module 'xxx'
4.简单demo文件
<template>
<div class="q">
<h1>{{ count }}</h1>
<h2>asd</h2>
<v-button @click="setCount(22)">点击变成22</v-button>
</div>
</template>
<script lang="ts">
import {defineComponent, ref, computed, inject} from '@vue/composition-api';
import {useApproveInject} from '@/provide/index';
export default defineComponent({
setup(props, {root}) {
// console.log(state)
console.log(root)
const {count, setCount} = useApproveInject();
return {
count,
setCount
}
}
})
</script>
<style>
</style>