Vite2 + Vue3 + TypeScript + Pinia +ElementPlus搭建项目

2022年05月10日 阅读数:8
这篇文章主要向大家介绍Vite2 + Vue3 + TypeScript + Pinia +ElementPlus搭建项目,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

1、vite2建立项目

兼容性注意:Vite 须要 Node.js 版本 >= 12.0.0。javascript

在建立项目文件目录下打开 cmd 运行如下命令css

# npm 6.x
npm init @vitejs/app projectName --template

# npm 7+, 须要额外的双横线:
npm init @vitejs/app projectName -- --template

# yarn
yarn create @vitejs/app projectName --template

![image.png](https://img-blog.csdnimg.cn/img_convert/3fdcd50f85a804bd3269078d84618c4c.png#clientId=uf30abecb-412b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=462&id=u60e33100&margin=[object Object]&name=image.png&originHeight=808&originWidth=1653&originalType=binary&ratio=1&rotation=0&showTitle=false&size=121990&status=done&style=none&taskId=ub2a8537f-3a3b-48e3-95f7-161add8ae64&title=&width=944.5714285714286)html

2、安装sass插件

//yarn
yarn add sass --dev

//npm
npm i sass -D

就直接使用了,不用像webpack 还要安装sass-loadervue

3、安装路由

//npm
npm install vue-router

//yarn
yarn add vue-router
  1. 在 src 文件下新增 router 文件夹 => index.ts 文件
import {
   
    createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
  {
   
   
    path: '/',
    name: 'Login',
    component: () => import('xxxx'), // 注意这里要带上 文件后缀.vue
  },
]

const router = createRouter({
   
   
  history: createWebHistory(),
  routes,
})

export default router


  1. mian.ts 引入
import {
   
    createApp } from 'vue'
import App from './App.vue'
import router from './router/index'

const app = createApp(App)

//链式
app.use(router).mount('#app')

4、安装axios

yarn add axios

npm i axios -S

在 src 文件下新增 utils 文件夹,service.ts 和 request.ts
![image.png](https://img-blog.csdnimg.cn/img_convert/03eb23b3bde02518e69bcc5bbf970e4f.png#clientId=u1e499d95-2955-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=387&id=u75ec5e02&margin=[object Object]&name=image.png&originHeight=678&originWidth=490&originalType=binary&ratio=1&rotation=0&showTitle=false&size=39460&status=done&style=none&taskId=u6643d583-8108-4fd6-a658-2927af6bc04&title=&width=280)java

  • service.ts:用与建立axios实例,统一配置请求拦截器、响应拦截…
/**
 * axios 请求配置
 */
import axios, {
   
    AxiosRequestConfig, AxiosResponse } from 'axios'
import {
   
    ElNotification } from 'element-plus'

/** 
 * 请求失败后的错误统一处理 
 * @param status 请求失败的状态码
 */
const errorHandle = (status: number) => {
   
   
    // 状态码判断
    switch (status) {
   
   
        case 302: ElNotification.error('接口重定向了!');
            break;
        case 400:
            ElNotification.error("发出的请求有错误,服务器没有进行新建或修改数据的操做==>" + status)
            break;
        // 401: 未登陆
        // 未登陆则跳转登陆页面,并携带当前页面的路径
        // 在登陆成功后返回当前页面,这一步须要在登陆页操做。                
        case 401: //重定向
            ElNotification.error("token:登陆失效==>" + status)
            break;
        // 403 token过时
        // 清除token并跳转登陆页
        case 403:
            ElNotification.error("登陆过时,用户获得受权,可是访问是被禁止的==>" + status)
            break;
        case 404:
            ElNotification.error("网络请求不存在==>" + status)
            break;
        case 406:
            ElNotification.error("请求的格式不可得==>" + status)
            break;
        case 408: ElNotification.error(" 请求超时!")
            break;
        case 410:
            ElNotification.error("请求的资源被永久删除,且不会再获得的==>" + status)
            break;
        case 422:
            ElNotification.error("当建立一个对象时,发生一个验证错误==>" + status)
            break;
        case 500:
            ElNotification.error("服务器发生错误,请检查服务器==>" + status)
            break;
        case 502:
            ElNotification.error("网关错误==>" + status)
            break;
        case 503:
            ElNotification.error("服务不可用,服务器暂时过载或维护==>" + status)
            break;
        case 504:
            ElNotification.error("网关超时==>" + status)
            break;
        default:
            ElNotification.error("其余错误错误==>" + status)
    }
}

//axios建立实例
const service = axios.create({
   
   
    //默认地址
    baseURL: "",
    // baseURL: "",
    //请求超时时间
    timeout: 3000,
    //配置请求头
    headers: {
   
    'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;' }
});

// 请求拦截器
service.interceptors.request.use((config:AxiosRequestConfig) => {
   
   
    //处理业务代码 ex:是否存在token
    return config
}, (error) => {
   
   
    // 错误抛到业务代码
    error.data = {
   
   }
    error.data.msg = '服务器异常,请联系管理员!'
    return Promise.resolve(error)
})

//响应拦截器
service.interceptors.response.use((response: AxiosResponse) => {
   
   
    const status = response.status

    if (status < 200 || status >= 300) {
   
   
        // 处理http错误,抛到业务代码
        errorHandle(status)
    }
    return response
}, (error) => {
   
   
    // 错误抛到业务代码
    error.data = {
   
   }
    error.data.msg = '请求超时或服务器异常,请检查网络或联系管理员!'
    return Promise.resolve(error)
})

export default service






  • request.ts:用于封装get、post、delete、put方法
/**
 * request 请求封装
 */

import service from "./service"

export default {
   
   
    get(url: string, data = {
   
   }) {
   
   
        return new Promise((resolve, reject) => {
   
   
            service
                .get(url, {
   
    params: data })
                .then(response => {
   
   
                    resolve(response.data);
                })
                .catch(err => {
   
   
                    reject(err);
                });
        });
    },
    delete(url: string, data = {
   
   }) {
   
   
        return new Promise((resolve, reject) => {
   
   
            service
                .delete(url, {
   
    params: data })
                .then(response => {
   
   
                    resolve(response.data);
                })
                .catch(err => {
   
   
                    reject(err);
                });
        });
    },
    post(url: string, data = {
    
    }) {
   
   
        return new Promise((resolve, reject) => {
   
   
            service
                .post(url, data)
                .then(response => {
   
   
                    resolve(response.data);
                })
                .catch(err => {
   
   
                    reject(err);
                });
        });
    },
    put(url: string, data = {
    
    }) {
   
   
        return new Promise((resolve, reject) => {
   
   
            service
                .put(url, data)
                .then(response => {
   
   
                    resolve(response.data);
                })
                .catch(err => {
   
   
                    reject(err);
                });
        });
    }
}


之后发送请求就直接使用,request.tsnode

5、安装ElementPlus

//NPM
$ npm install element-plus --save
// Yarn
$ yarn add element-plus
  1. mian.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).mount('#app')

6、安装pinia

npm install pinia --save

yarn add pinia
  1. 在 src 文件下新增 store文件夹 => index.ts 文件

/**
 * pinia 建立
 */
import {
   
    createPinia } from 'pinia'
const store = createPinia()
export default store
  1. mian.ts 引入
import {
   
    createApp } from 'vue'
import App from './App.vue'
//引入pinia
import store from './store'
const app = createApp(App)
app.use(store).mount('#app')

7、配置vite.config.ts

能够直接复制使用,额外的配置能够自定义。
备注能够信息用不到的,能够删除。webpack

//须要分别安装这两个插件
npm install @types/node --save-dev
//使用 vite-plugin-compression 能够 gzip 或 brotli 的方式来压缩资源,这一步须要服务器端的配合,vite 只能帮你打包出 .gz 文件。此插件使用简单,你甚至无需配置参数,引入便可。
//开启 gzip 能够极大的压缩静态资源,对页面加载的速度起到了显著的做用。
npm install vite-plugin-compression --save-dev

import {
   
    defineConfig } from 'vite' // 帮手函数,这样不用 jsdoc 注解也能够获取类型提示
import vue from '@vitejs/plugin-vue'  //识别.vue文件
import viteCompression from "vite-plugin-compression"; //gzip必备插件,开启gzip、br压缩

//path 
const path = require('path');
/**
 * 
 * 此时 TS 可能有这个错误提示:找不到模块“path”或其相应的类型声明。
 * 解决方法:npm install @types/node --save-dev
 */

const resolve = (dir: string) => path.join(__dirname, dir)//__dirname 老是指向被执行 js 文件的绝对路径 EX:/d1/d2/myscript.js 文件中写了 __dirname, 它的值就是 /d1/d2

// https://vitejs.dev/config/
export default defineConfig({
   
   
  //开发或生产环境服务的公共基础路径
  base: './',
  //做为静态资源服务的文件夹。并在构建期间复制到 outDir 的根目录,而且始终按原样提供或复制而无需进行转换。
  publicDir: "public",
  //用于加载 .env 文件的目录。
  // envDir:"root",
  //控制台输出的级别 info 、warn、error、silent
  logLevel: "info",
  // 设为false 能够避免 vite 清屏而错过在终端中打印某些关键信息
  clearScreen: true,
  //plugins配置须要使用的插件列表
  plugins: [vue(), viteCompression({
   
   
    verbose: true,
    disable: false,
    threshold: 10240,
    algorithm: "gzip",
    ext: ".gz",
  })],

  // 路径相关规则
  resolve: {
   
   
    //配置别名
    alias: {
   
   
      '@': resolve('src'),// @表示当前的src目录路径
      comps: resolve('src/components'),// comps表示当前的src目录路径下components
      apis: resolve('src/apis'),// apis表示当前的src目录路径下apis
      views: resolve('src/views'),
      utils: resolve('src/utils'),
      routes: resolve('src/routes'),
    }
  },
  //配置全局css变量
	// css: {
   
   
	// 	preprocessorOptions: {
   
   
	// 		scss: {
   
   
	// 			additionalData: '@import "@/assets/styles/mixin.scss";',//多个全局样式直接分号引用就好了
	// 		},
	// 	},
	// },
  
  //本地运行配置,以及反向代理配置
  // server: {
   
   
  //   host: "0.0.0.0",//指定服务器应该监听哪一个 IP 地址。 若是将此设置为 0.0.0.0 或者 true 将监听全部地址,包括局域网和公网地址。
  //   port: 12138,//指定开发服务器端口
  //   strictPort: false, //设为true时端口被占用则直接退出,不会尝试下一个可用端口
  //   https: false,//是否启用 http 2
  //   open: true,//服务启动时自动在浏览器中打开应用
  //   cors: true,//为开发服务器配置 CORS , 默认启用并容许任何源
  //   force: true,//是否强制依赖预构建
  //   hmr: false,//禁用或配置 HMR 链接
  //   // 传递给 chockidar 的文件系统监视器选项
  //   watch: {
   
   
  //     ignored: ["!**/node_modules/your-package-name/**"]
  //   },
  //   // 反向代理配置
  //   proxy: {
   
   
  //     '/api': {
   
   
  //       target: "https://xxxx.com/",// 所要代理的目标地址
  //       changeOrigin: true,
  //       rewrite: (path) => path.replace("/^/api /", '') //重写地址
  //     }
  //   }
  // },
  //打包配置
  build: {
   
   
     //传递给 Terser 的更多 minify 选项。 生产环境去除 console debugger
    terserOptions: {
   
   
      compress: {
   
   
        drop_console: true,
        drop_debugger: true,
      },
    },
    
  //   //浏览器兼容性  "esnext"|"modules"
  //   target: "modules",
  //   //指定输出路径
  //   outDir: "dist",
  //   //生成静态资源的存放路径
  //   assetsDir: "assets",
  //   //小于此阈值的导入或引用资源将内联为 base64 编码,以免额外的 http 请求。设置为 0 能够彻底禁用此项
  //   assetsInlineLimit: 4096,
  //   //启用/禁用 CSS 代码拆分
  //   cssCodeSplit: true,
  //   //构建后是否生成 source map 文件
  //   sourcemap: false,
  //   //自定义底层的 Rollup 打包配置
  //   rollupOptions: {
   
   
  //   },
  //   //@rollup/plugin-commonjs 插件的选项
  //   commonjsOptions: {
   
   
  //   },
  //   //构建的库
  //   lib: {
   
   
  //   },
  //   //当设置为 true,构建后将会生成 manifest.json 文件
  //   manifest: false,
  //   // 设置为 false 能够禁用最小化混淆,
  //   // 或是用来指定使用哪一种混淆器
  //   // boolean | 'terser' | 'esbuild'
  //   minify: "terser", //terser 构建后文件体积更小
  //   //设置为 false 来禁用将构建后的文件写入磁盘
  //   write: true,
  //   //默认状况下,若 outDir 在 root 目录下,则 Vite 会在构建时清空该目录。
  //   emptyOutDir: true,
  //   //启用/禁用 brotli 压缩大小报告
  //   brotliSize: true,
  //   //chunk 大小警告的限制
  //   chunkSizeWarningLimit: 500
  },
})

/***
 * 注意事项:
 */
//  ● 假设不配置 base 时,打包以后,访问时出现白屏。
//  ● alias 不配置的时候,每次引入文件须要找根目录,比较麻烦。

8、环境变量配置

项目根目录分别新建:.env 、.env.development 、.env.production
![image.png](https://img-blog.csdnimg.cn/img_convert/a31b12abd9e72a0c1a6f5425128f2f01.png#clientId=u6dd3eca0-f03e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=338&id=u2cae385e&margin=[object Object]&name=image.png&originHeight=592&originWidth=487&originalType=binary&ratio=1&rotation=0&showTitle=false&size=41425&status=done&style=none&taskId=u4ebc535f-6edf-4b35-8447-211b5879f84&title=&width=278.2857142857143)ios

  • .env
# port 端口号
VITE_PORT = 8888

# open 运行 npm run dev 时自动打开浏览器
VITE_OPEN = false

# public path 配置线上环境路径(打包)、本地经过 http-server 访问时,请置空便可
VITE_PUBLIC_PATH = ''
  • .env.developmen
# 本地环境
ENV = 'development'

# 本地环境接口地址
VITE_API_URL = 'http://localhost:8888/'

  • .env.production
# 线上环境
ENV = 'production'

# 线上环境接口地址
VITE_API_URL = 'URL'

使用:web

console.log(import.meta.env.VITE_API_URL)
//输入命令 npm run dev 系统会识别.env.developmen文件下的VITE_API_URL地址
//输入命令 npm run build 系统会识别.env.production文件下的VITE_API_URL地址
 

9、约束代码风格

  1. Eslint 支持
  • 下载依赖
# eslint 安装
yarn add eslint --dev
# eslint 插件安装
yarn add eslint-plugin-vue --dev

yarn add @typescript-eslint/eslint-plugin --dev

yarn add eslint-plugin-prettier --dev

# typescript parser
yarn add @typescript-eslint/parser --dev

注意: 若是 eslint 安装报错:vue-router

能够尝试运行如下命令:

yarn config set ignore-engines true

  • 在根目录下新建 .eslintrc.js
//配置 eslint 校验规则:
module.exports = {
   
   
	root: true,
	env: {
   
   
		browser: true,
		node: true,
		es2021: true,
	},
	parser: 'vue-eslint-parser',
	extends: [
		'eslint:recommended',
		'plugin:vue/vue3-recommended',
		'plugin:@typescript-eslint/recommended',
		'plugin:prettier/recommended',
		// eslint-config-prettier 的缩写
		'prettier',
	],
	parserOptions: {
   
   
		ecmaVersion: 12,
		parser: '@typescript-eslint/parser',
		sourceType: 'module',
		ecmaFeatures: {
   
   
			jsx: true,
		},
	},
	// eslint-plugin-vue @typescript-eslint/eslint-plugin eslint-plugin-prettier的缩写
	plugins: ['vue', '@typescript-eslint', 'prettier'],
	rules: {
   
   
		'@typescript-eslint/ban-ts-ignore': 'off',
		'@typescript-eslint/no-unused-vars': 'off',
		'@typescript-eslint/explicit-function-return-type': 'off',
		'@typescript-eslint/no-explicit-any': 'off',
		'@typescript-eslint/no-var-requires': 'off',
		'@typescript-eslint/no-empty-function': 'off',
		'@typescript-eslint/no-use-before-define': 'off',
		'@typescript-eslint/ban-ts-comment': 'off',
		'@typescript-eslint/ban-types': 'off',
		'@typescript-eslint/no-non-null-assertion': 'off',
		'@typescript-eslint/explicit-module-boundary-types': 'off',
		'vue/multi-word-component-names': 'off',
		'no-var': 'error',
		'prettier/prettier': 'error',
		// 禁止出现console
		'no-console': 'warn',
		// 禁用debugger
		'no-debugger': 'warn',
		// 禁止出现重复的 case 标签
		'no-duplicate-case': 'warn',
		// 禁止出现空语句块
		'no-empty': 'warn',
		// 禁止没必要要的括号
		'no-extra-parens': 'off',
		// 禁止对 function 声明从新赋值
		'no-func-assign': 'warn',
		// 禁止在 return、throw、continue 和 break 语句以后出现不可达代码
		'no-unreachable': 'warn',
		// 强制全部控制语句使用一致的括号风格
		curly: 'warn',
		// 要求 switch 语句中有 default 分支
		'default-case': 'warn',
		// 强制尽量地使用点号
		'dot-notation': 'warn',
		// 要求使用 === 和 !==
		eqeqeq: 'warn',
		// 禁止 if 语句中 return 语句以后有 else 块
		'no-else-return': 'warn',
		// 禁止出现空函数
		'no-empty-function': 'warn',
		// 禁用没必要要的嵌套块
		'no-lone-blocks': 'warn',
		// 禁止使用多个空格
		'no-multi-spaces': 'warn',
		// 禁止屡次声明同一变量
		'no-redeclare': 'warn',
		// 禁止在 return 语句中使用赋值语句
		'no-return-assign': 'warn',
		// 禁用没必要要的 return await
		'no-return-await': 'warn',
		// 禁止自我赋值
		'no-self-assign': 'warn',
		// 禁止自身比较
		'no-self-compare': 'warn',
		// 禁止没必要要的 catch 子句
		'no-useless-catch': 'warn',
		// 禁止多余的 return 语句
		'no-useless-return': 'warn',
		// 禁止变量声明与外层做用域的变量同名
		'no-shadow': 'off',
		// 容许delete变量
		'no-delete-var': 'off',
		// 强制数组方括号中使用一致的空格
		'array-bracket-spacing': 'warn',
		// 强制在代码块中使用一致的大括号风格
		'brace-style': 'warn',
		// 强制使用骆驼拼写法命名约定
		camelcase: 'warn',
		// 强制使用一致的缩进
		indent: 'off',
		// 强制在 JSX 属性中一致地使用双引号或单引号
		// 'jsx-quotes': 'warn',
		// 强制可嵌套的块的最大深度4
		'max-depth': 'warn',
		// 强制最大行数 300
		// "max-lines": ["warn", { "max": 1200 }],
		// 强制函数最大代码行数 50
		// 'max-lines-per-function': ['warn', { max: 70 }],
		// 强制函数块最多容许的的语句数量20
		'max-statements': ['warn', 100],
		// 强制回调函数最大嵌套深度
		'max-nested-callbacks': ['warn', 3],
		// 强制函数定义中最多容许的参数数量
		'max-params': ['warn', 3],
		// 强制每一行中所容许的最大语句数量
		'max-statements-per-line': ['warn', {
   
    max: 1 }],
		// 要求方法链中每一个调用都有一个换行符
		'newline-per-chained-call': ['warn', {
   
    ignoreChainWithDepth: 3 }],
		// 禁止 if 做为惟一的语句出如今 else 语句中
		'no-lonely-if': 'warn',
		// 禁止空格和 tab 的混合缩进
		'no-mixed-spaces-and-tabs': 'warn',
		// 禁止出现多行空行
		'no-multiple-empty-lines': 'warn',
		// 禁止出现;
		semi: ['warn', 'never'],
		// 强制在块以前使用一致的空格
		'space-before-blocks': 'warn',
		// 强制在 function的左括号以前使用一致的空格
		// 'space-before-function-paren': ['warn', 'never'],
		// 强制在圆括号内使用一致的空格
		'space-in-parens': 'warn',
		// 要求操做符周围有空格
		'space-infix-ops': 'warn',
		// 强制在一元操做符先后使用一致的空格
		'space-unary-ops': 'warn',
		// 强制在注释中 // 或 /* 使用一致的空格
		// "spaced-comment": "warn",
		// 强制在 switch 的冒号左右有空格
		'switch-colon-spacing': 'warn',
		// 强制箭头函数的箭头先后使用一致的空格
		'arrow-spacing': 'warn',
		'prefer-const': 'warn',
		'prefer-rest-params': 'warn',
		'no-useless-escape': 'warn',
		'no-irregular-whitespace': 'warn',
		'no-prototype-builtins': 'warn',
		'no-fallthrough': 'warn',
		'no-extra-boolean-cast': 'warn',
		'no-case-declarations': 'warn',
		'no-async-promise-executor': 'warn',
	},
	globals: {
   
   
		defineProps: 'readonly',
		defineEmits: 'readonly',
		defineExpose: 'readonly',
		withDefaults: 'readonly',
	},
}

  • 在根目录下新建 .eslintignore
# eslint 忽略检查 (根据项目须要自行添加)
*.sh
node_modules
lib
*.md
*.scss
*.woff
*.ttf
.vscode
.idea
dist
mock
public
bin
build
config
index.html
src/assets

  1. prettier
  • 下载依赖
# 安装 prettier
yarn add prettier --dev

  • 在根目录下新建 .prettierrc.js
module.exports = {
   
   
  tabWidth: 2,
  jsxSingleQuote: true,
  jsxBracketSameLine: true,
  printWidth: 100,
  singleQuote: true,
  semi: false,
  overrides: [
    {
   
   
      files: '*.json',
      options: {
   
   
        printWidth: 200,
      },
    },
  ],
  arrowParens: 'always',
}

  • 在根目录下新建 .prettierignore
# 忽略格式化文件 (根据项目须要自行添加)
node_modules
dist

在package.json 配置:

{
   
   
  "script": {
   
   
    //添加如下这两个
    "lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx",
    "prettier": "prettier --write ."
  }
}

解决 eslint 和 prettier 冲突

解决 ESLint 中的样式规范和 prettier 中样式规范的冲突,以 prettier 的样式规范为准,使 ESLint 中的样式规范自动失效

yarn add eslint-config-prettier --dev

上面配置完成后,能够运行如下命令测试下代码检查个格式化效果:

# eslint 检查
npm run lint   or -yarn lint
# prettier 自动格式化
npm run prettier  or -yarn prettier