第二节:三方库集成和配置(vue cli-webpack、element-plus、axios、vscode、区分不同环境)
一. vue cli-webpack配置
1. 说明
在vue cli创建的项目中,配置文件为:vue.config.js
vue.config.js
是一个可选的配置文件,如果项目的 (和 package.json
同级的) 根目录中存在这个文件,那么它会被 @vue/cli-service
自动加载。
2. vue cli 基本配置
(详见:https://cli.vuejs.org/zh/config/#全局-cli-配置 )
(1). outputDir
(2). publicPath
剖析:
默认值为‘/’,生成的打包文件的引用的路径都是以/开头,没法直接在vscode中运行,需要部署后才能运行,
如果改为'./',生成的打包文件的引用路径如下图,可以直接在vscode中运行
3. webpack配置
(详见:https://cli.vuejs.org/zh/guide/webpack.html#简单的配置方式 )
在vue cli创建的项目中,不能配置webpack.config.js文件,需要在vue.config.js中的configureWebpack 或 chainWebpack 属性中配置, configureWebpack 属性中的配置和webpack中的配置一样,自动就合并了。
补充:cli中,@符号已经默认配置了,对应的就是src目录,当然可以重写。
下面是几种写法:
const path = require('path'); module.exports = { //CLI提供的属性 outputDir: './build', // publicPath: './', //打包后的可以直接使用,不用发布,默认是 '/' // webpack写法1(json格式) // 和webpack属性完全一致, 最后会进行合并 configureWebpack: { resolve: { alias: { components: '@/components', }, }, }, // webpack写法2(函数形式) // configureWebpack: (config) => { // config.resolve.alias = { // '@': path.resolve(__dirname, 'src'), // components: '@/components' // } // } // webpack写法3(链式编程) // chainWebpack: (config) => { // config.resolve.alias // .set('@', path.resolve(__dirname, 'src')) // .set('components', '@/components'); // }, };
二. element-plus配置
1. 全局引用
(1). 说明
如果你对打包后的文件大小不是很在乎,那么使用完整导入会更方便。
(2). 步骤
A. 安装生产依赖 【npm install element-plus】
B. 全局引用,在main.ts文件中导入
import { createApp } from 'vue'; import App from './App.vue'; // 全局引入element-plus import ElementPlus from 'element-plus'; import 'element-plus/dist/index.css'; const app = createApp(App); app.use(ElementPlus); //全局引用element-plus app.use(golbalRegister); // 按需引入element-plus app.mount('#app');
C. 在页面中直接使用任意组件即可
<template> <div> <el-button>默认按钮</el-button> <el-button type="primary">主要按钮</el-button> <el-button type="success">成功按钮</el-button> <el-button type="info">信息按钮</el-button> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ setup() { }, }); </script> <style lang="less"></style>
2. 按需引用
(1).说明
根据需要导入相应的组件和样式
(2).步骤
直接在对应页面导入组件和组件的样式,使用即可
<template> <div> <el-button>默认按钮</el-button> <el-button type="primary">主要按钮</el-button> <el-button type="success">成功按钮</el-button> <el-button type="info">信息按钮</el-button> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; import { ElButton } from 'element-plus'; import 'element-plus/theme-chalk/el-button.css'; export default defineComponent({ components: { ElButton, }, setup() { }, }); </script> <style lang="less"></style>
3. 按需引用-全局封装
(1). 说明
上述在每个页面导入组件和组件的样式过于繁琐,所以这里将element-plus的组件封装成全局,然后每个页面直接使用即可,采用插件的形式进行封装 (这里有个问题,高版本的按需导入样式存在问题,这里的封装仅仅按需导入插件,样式使用的总样式)
(2). 步骤
A. 封装register-element.ts文件(插件形式)
import { App } from 'vue'; import 'element-plus/dist/index.css'; //全局样式 import { ElButton, ElCheckbox } from 'element-plus'; const components = [ElButton, ElCheckbox]; export default function (app: App): void { // 注册全局组件 for (const cItem of components) { app.component(cItem.name, cItem); } }
B. 封装global文件夹下的index.ts文件,统一出口
import { App } from 'vue'; import registerElement from './register-element'; // 插件的形式对外导出(函数模式) export function golbalRegister(app: App): void { app.use(registerElement); }
C. 在main.ts中进行导入
import { createApp } from 'vue'; import App from './App.vue'; // 按需引入element-plus import { golbalRegister } from './global'; const app = createApp(App); app.use(golbalRegister); // 按需引入element-plus app.mount('#app');
D. 页面中直接使用即可
<template> <div> <el-button>默认按钮</el-button> <el-button type="primary">主要按钮</el-button> <el-button type="success">成功按钮</el-button> <el-button type="info">信息按钮</el-button> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ setup() { }, }); </script> <style lang="less"></style>
三. axios配置
1. 说明
axios的详细用法参考之前的文章:https://www.cnblogs.com/yaopengfei/p/12347199.html
这里补充一个通过创建实例的方式进行请求Post表单提交(实际上axios对象也是一个实例)
import axios from 'axios'; const myAxiosInstance = axios.create({ baseURL: 'http://xxxx:8002', timeout: 1000, headers: { 'X-Custom-Header': 'foobar' }, }); const params = new URLSearchParams(); params.append('userAccount', 'admin'); params.append('passWord', '122222'); myAxiosInstance .post('/Api/AdminApi_Areas/SysMainApi/CheckLogin', params) .then((res) => { console.log(res.data); }) .catch((err) => { console.log(err); });
2. 封装思路
封装1个类,然后创建一个实例,将实例进行对外导出,里面结合Element-plus的组件,进行请求加载效果的配置
import type { AxiosRequestConfig, AxiosResponse } from 'axios'; /* eslint-disable */ export interface HYRequestInterceptors<T = AxiosResponse> { requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig; requestInterceptorCatch?: (error: any) => any; responseInterceptor?: (res: T) => T; responseInterceptorCatch?: (error: any) => any; } export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig { interceptors?: HYRequestInterceptors<T>; showLoading?: boolean; }
// 以类的方式来封装axios import axios, { AxiosInstance } from 'axios'; import type { HYRequestInterceptors, HYRequestConfig } from './type'; import { ElLoading, ILoadingInstance } from 'element-plus'; const DEAFULT_LOADING = true; class HYRequest { instance: AxiosInstance; interceptors?: HYRequestInterceptors; showLoading: boolean; loading?: ILoadingInstance; constructor(config: HYRequestConfig) { // 创建axios实例 this.instance = axios.create(config); // 保存基本信息 this.showLoading = config.showLoading ?? DEAFULT_LOADING; this.interceptors = config.interceptors; // 使用拦截器 // 1.从config中取出的拦截器是对应的实例的拦截器 this.instance.interceptors.request.use( this.interceptors?.requestInterceptor, this.interceptors?.requestInterceptorCatch, ); this.instance.interceptors.response.use( this.interceptors?.responseInterceptor, this.interceptors?.responseInterceptorCatch, ); // 2.添加所有的实例都有的拦截器 this.instance.interceptors.request.use( (config) => { if (this.showLoading) { this.loading = ElLoading.service({ lock: true, text: '正在请求数据....', background: 'rgba(0, 0, 0, 0.5)', }); } return config; }, (err) => { return err; }, ); this.instance.interceptors.response.use( (res) => { // 将loading移除 this.loading?.close(); const data = res.data; if (data.returnCode === '-1001') { console.log('请求失败~, 错误信息'); } else { return data; } }, (err) => { // 将loading移除 this.loading?.close(); // 例子: 判断不同的HttpErrorCode显示不同的错误信息 if (err.response.status === 404) { console.log('404的错误~'); } return err; }, ); } request<T>(config: HYRequestConfig<T>): Promise<T> { return new Promise((resolve, reject) => { // 1.单个请求对请求config的处理 if (config.interceptors?.requestInterceptor) { config = config.interceptors.requestInterceptor(config); } // 2.判断是否需要显示loading if (config.showLoading === false) { this.showLoading = config.showLoading; } this.instance // eslint-disable-next-line @typescript-eslint/no-explicit-any .request<any, T>(config) .then((res) => { // 1.单个请求对数据的处理 if (config.interceptors?.responseInterceptor) { res = config.interceptors.responseInterceptor(res); } // 2.将showLoading设置true, 这样不会影响下一个请求 this.showLoading = DEAFULT_LOADING; // 3.将结果resolve返回出去 resolve(res); }) .catch((err) => { // 将showLoading设置true, 这样不会影响下一个请求 this.showLoading = DEAFULT_LOADING; reject(err); return err; }); }); } get<T>(config: HYRequestConfig<T>): Promise<T> { return this.request<T>({ ...config, method: 'GET' }); } post<T>(config: HYRequestConfig<T>): Promise<T> { return this.request<T>({ ...config, method: 'POST' }); } delete<T>(config: HYRequestConfig<T>): Promise<T> { return this.request<T>({ ...config, method: 'DELETE' }); } patch<T>(config: HYRequestConfig<T>): Promise<T> { return this.request<T>({ ...config, method: 'PATCH' }); } } export default HYRequest;
实例的封装
// service统一出口 import HYRequest from './request'; import { BASE_URL, TIME_OUT } from './request/config'; const hyRequest = new HYRequest({ baseURL: BASE_URL, timeout: TIME_OUT, // 实例级别的拦截器 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; }, }, }); export default hyRequest;
//测试自己封装的axios import hyRequest from './index'; // 下面是表单提交到参数封装 const params = new URLSearchParams(); params.append('userAccount', 'admin'); params.append('passWord', '123456'); // 封装返回值类型 interface DataType { status: string; msg: string; data: unknown; } hyRequest .post<DataType>({ url: '/Api/AdminApi_Areas/SysMainApi/CheckLogin', data: params, // showLoading: false, // headers: {}, // baseURL: '', }) .then((res) => { console.log(res); console.log(res.status, res.msg, res.data); }) .catch((err) => { console.log(err); });
四. vscode配置
1. 在vscode中设置选项中,输入配置,然后选择settings.json,进行配置
2. 分享一个vscode的配置
{ "workbench.iconTheme": "vscode-great-icons", "editor.fontSize": 17, "eslint.migration.2_x": "off", "[javascript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "files.autoSave": "afterDelay", "editor.tabSize": 2, "terminal.integrated.fontSize": 16, "editor.renderWhitespace": "all", "editor.quickSuggestions": { "strings": true }, "debug.console.fontSize": 15, "window.zoomLevel": 1, "emmet.includeLanguages": { "javascript": "javascriptreact" }, "explorer.confirmDragAndDrop": false, "workbench.tree.indent": 16, "javascript.updateImportsOnFileMove.enabled": "always", "editor.wordWrap": "on", "path-intellisense.mappings": { "@": "${workspaceRoot}/src" }, "hediet.vscode-drawio.local-storage": "eyIuZHJhd2lvLWNvbmZpZyI6IntcImxhbmd1YWdlXCI6XCJcIixcImN1c3RvbUZvbnRzXCI6W10sXCJsaWJyYXJpZXNcIjpcImdlbmVyYWw7YmFzaWM7YXJyb3dzMjtmbG93Y2hhcnQ7ZXI7c2l0ZW1hcDt1bWw7YnBtbjt3ZWJpY29uc1wiLFwiY3VzdG9tTGlicmFyaWVzXCI6W1wiTC5zY3JhdGNocGFkXCJdLFwicGx1Z2luc1wiOltdLFwicmVjZW50Q29sb3JzXCI6W1wiRkYwMDAwXCIsXCIwMENDNjZcIixcIm5vbmVcIixcIkNDRTVGRlwiLFwiNTI1MjUyXCIsXCJGRjMzMzNcIixcIjMzMzMzM1wiLFwiMzMwMDAwXCIsXCIwMENDQ0NcIixcIkZGNjZCM1wiLFwiRkZGRkZGMDBcIl0sXCJmb3JtYXRXaWR0aFwiOjI0MCxcImNyZWF0ZVRhcmdldFwiOmZhbHNlLFwicGFnZUZvcm1hdFwiOntcInhcIjowLFwieVwiOjAsXCJ3aWR0aFwiOjExNjksXCJoZWlnaHRcIjoxNjU0fSxcInNlYXJjaFwiOnRydWUsXCJzaG93U3RhcnRTY3JlZW5cIjp0cnVlLFwiZ3JpZENvbG9yXCI6XCIjZDBkMGQwXCIsXCJkYXJrR3JpZENvbG9yXCI6XCIjNmU2ZTZlXCIsXCJhdXRvc2F2ZVwiOnRydWUsXCJyZXNpemVJbWFnZXNcIjpudWxsLFwib3BlbkNvdW50ZXJcIjowLFwidmVyc2lvblwiOjE4LFwidW5pdFwiOjEsXCJpc1J1bGVyT25cIjpmYWxzZSxcInVpXCI6XCJcIn0ifQ==", "hediet.vscode-drawio.theme": "Kennedy", "editor.fontFamily": "Source Code Pro, 'Courier New', monospace", "editor.smoothScrolling": true, "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "workbench.colorTheme": "Atom One Dark", "vetur.completion.autoImport": false, "security.workspace.trust.untrustedFiles": "open", "eslint.lintTask.enable": true, "eslint.alwaysShowStatus": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true } }
五. 区分不同环境
1. 方案1-手动修改
(不可取)
//写法1:每次手动改 const BASE_URL = 'https://www.ypf.org/test'; const BASE_NAME = 'ypf'; export { BASE_URL, BASE_NAME };
2. 方案2-根据process.env.NODE_ENV进行区分
(推荐)
原理:根据cli内置的一个值, process.env.NODE_ENV ,开发环境下为development,生产环境下为production,测试环境下为test,编写一个config.ts文件,if判断,从而对外导出。
分享config.ts代码
//写法2:根据process.env.NODE_ENV进行区分 // 开发环境: development // 生成环境: production // 测试环境: test let BASE_URL = ''; if (process.env.NODE_ENV === 'development') { BASE_URL = 'http://127.0.0.1:8000/'; } else if (process.env.NODE_ENV === 'production') { BASE_URL = 'http://ypf.org/prod'; } else { BASE_URL = 'http://ypf.org/test'; } export { BASE_URL };
3. 方案3-编写不同环境变量配置文件
(推荐)
详见:https://cli.vuejs.org/zh/guide/mode-and-env.html#模式
(1). 定义3个不同的文件,分别如下,这三个名称是固定的,分别在里面定义变量。
.env (各种环境都可以获取)
.env.development (开发环境获取)
.env.production (生产环境获取)
.env代码如下:
/* 开发和生产环境都能获取 */ VUE_APP_NAME=ypf0
.env.development 代码如下:
VUE_APP_BASE_URL=https://ypf.org/dev VUE_APP_BASE_NAME=ypf2
.env.production代码如下:
VUE_APP_BASE_URL=https://ypf.org/prod VUE_APP_BASE_NAME=ypf1
特别注意:只有 NODE_ENV,BASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中。
(2). 测试代码
需要用process.env.xxx进行调用
export default defineComponent({ setup() {// 测试写法3 console.log('测试写法3'); console.log(process.env.VUE_APP_BASE_URL); console.log(process.env.VUE_APP_BASE_NAME); console.log(process.env.VUE_APP_NAME); }, });
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。