vue3+webpack+eslint|prettier+elementplus+国际化+axios封装+pinia

作者分享了从Vue2转换到Vue3搭建项目时的踩坑经验。包括采用eslint + prettier进行代码规范,因webpack生态好等原因选用其创建项目,还介绍了element plus使用、国际化配置、axios封装,以及后续将vuex切换为pinia的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近菜鸟自己搭建一个项目,想着 vue3 都出来这么久了,再不用 vue3,怕是等我熟悉 vue3 的时候,vue4 都出来了,直接落后时代一个大版本那就难受了!

但是突然从 vue2 转变到用这个 vue3 确实是让人脑壳疼,接下来就把菜鸟踩坑的地方都分享给大家了!

菜鸟感觉这篇文章的eslint|prettier部分可能有点问题,但是还是有很多人收藏,所以就保留了,eslint|prettier部分建议看菜鸟重写的新文章:vue3+vite+eslint|prettier+elementplus+国际化+axios封装+pinia

创建项目 + eslint + prettier

首先要知道的就是,既然是公司,那么一般都有一套代码规范,但是菜鸟现在待的是一个生物公司,之前都没有前端这个岗位,所以一切就是靠菜鸟了,那既然是由我定规范,那一定要搞好点,所以就采用了eslint + prettier!

之所以不用 vite,其一就是感觉 webpack 生态比较好,其二 vue2 就是 webpack 的,其三 vite 的一些依赖要自己去下载,感觉是没有 webpack 方便,所以暂时就没转换(不过好像没法体验 Pinia,可以自己把 vuex 卸载并 npm Pinia,也可以创建的时候不选 vuex)!

注:这是菜鸟第一次使用vue3,后续还是转vite了,vite 确实yyds!

创建项目

vue create testpro

然后选择

在这里插入图片描述

之所以不用 ts,首先菜鸟不是很会 ts,第二菜鸟感觉 js 本来就是自由的语言,你给它加上限制不能说不好,只是菜鸟不喜欢而已!

然后路由模式一定要使用 hash 模式,history 模式菜鸟没用过,反正好像是要后端进行配合,不是很好使用

在这里插入图片描述

然后 css 菜鸟选择的是 scss,这个按照个人喜好就行!

eslint 菜鸟就是选择的 eslint + prettier

在这里插入图片描述

这个菜鸟选的第一个,不想让其提交的时候使用 fix, fix虽好,但是还是自己能全权掌握的更香,而不是交给 fix !

在这里插入图片描述

到这里项目就创建完了,但是配置还没有配置好!

eslint + prettier

因为这个时候,只要你新建一个 .vue 文件,eslint 就跑过来恶心你了,会报错

Component name "xxxxx” should always be multi-word

在这里插入图片描述

但是感觉这个规范确实不需要,所以只需要在 eslint.js 里面加上这一行就行

在这里插入图片描述

方便复制:

"vue/multi-word-component-names": 0,

这个时候你以为一切顺利,然后再敲了几行代码,eslint 就又来了,会报错

Delete eslint(prettier/prettier)

这是因为
在这里插入图片描述

这里就要创建 .prettierrc 文件,并输入以下

{
  "endOfLine": "auto"
}

注意

一定要重启vscode,不然还是会报错!

这里还有一篇文章说的是另一种方法,读者可以尝试:优雅解决:(linebreak-style) Expected linebreaks to be ‘LF‘ but found ‘CRLF‘. (eslint)

这个时候确实万事大吉,但是你如果写的代码最后一行没有换行,那么就会报错

Insert ␍⏎eslintprettier/prettier

但是这个无伤大雅,打个回车就行了!

完整 eslint 配置

module.exports = {
  root: true,
  extends: ["plugin:vue/vue3-essential", "eslint:recommended", "plugin:prettier/recommended"],
  parserOptions: {
    parser: "@babel/eslint-parser",
  },
  rules: {
    // 默认自带的,vite创建项目没有
    "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
    // 菜鸟加的
    "vue/multi-word-component-names": 0,
    "no-unused-vars": "warn", // 避免不使用的变量,一定要删除才能跑
  },
  // 解决一些 definexxx 编译宏总提示未引入问题
  env: {
    node: true,
    'vue/setup-compiler-macros': true
  }
};

自动格式化

要使用 eslint 需要一个插件:

在这里插入图片描述

在这里插入图片描述

但是如果想保存的时候自动格式化成为我们想要的格式,我们该怎么配置?

这个时候就要下载该插件:

在这里插入图片描述

并将 vscode 的 setting.json 设置成这样:

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "eslint.validate": ["javascript", "vue"],
  "eslint.run": "onType",
  "eslint.format.enable": true,
  "git.openRepositoryInParentFolders": "always",
  "path-intellisense.mappings": {
    "@": "${workspaceRoot}/src",
    "~@": "${workspaceRoot}/src"
  },

  // 配置 @/~@ 提示的配置,见:https://juejin.cn/post/7368757335802609702
  "path-intellisense.mappings": {
    "@": "${workspaceRoot}/src",
    "~@": "${workspaceRoot}/src"
  },
  
  "files.autoSave": "onFocusChange",
}

设置多少换行 – 》 .prettierrc

设置完这个,菜鸟以为万事大吉了,直到开发时,发现每次保存时,其实长度都没到 100 行就报错需要换行了,仔细研究才发现,.prettierrc 也需要设置:

{
  "endOfLine": "auto",
  "printWidth": 100
}

注意:

如果是已经创建好的项目,就要按照别人设置的 eslint 规则去新建并配置 .prettierrc,不然一保存,git 就全是改变!

更多插件见:工欲善其事必先利其器!你vscode配置了这些吗?

element plus

这里比较多,直接提出成一篇文章了:element plus使用问题

国际化

首先便是安装国际化库

npm i vue-i18n

在项目中,我们一般会新建一个文件夹专门存放 i18n 的配置

目录结构如下:

在这里插入图片描述

然后新建 i18n.js 文件,引入 vue-i18n,vue-i18n 使用 createI18n 创建实例:

import { createI18n } from "vue-i18n";
import EN from "./en/en";
import CN from "./cn/cn";

const message = {
  cn: {
    ...CN,
  },
  en: {
    ...EN,
  },
};

const i18n = createI18n({
  locale: "cn", // 设置语言类型
  legacy: false, // 如果要支持compositionAPI,此项必须设置为false;
  globalInjection: true, // 全局注册$t方法
  messages: message,
});

export default i18n;

然后 cn / en 里面的文件结构如下:

在这里插入图片描述

内容由自己定义,这里给个例子:

export default {
  username: "用户名",
  password: "密码",
  passwordtip: "请输入密码",
  signIn: "登录",
  signOut: "退出登录",
  signInerr: "登录失败",
};

英文的与之对应即可!

然后便是在 main.js 中引入国际化

在这里插入图片描述

注意

1、如果要在 script 中使用国际化方法,需要结构出 t 方法,html 中可以直接使用 $t()

import { useI18n } from "vue-i18n";

//  解构出t方法
const { t } = useI18n(); // 先调用此方法,然后再使用

// eslint-disable-next-line
ElMessage({
   message: t("signInerr"),
   type: "error",
 });

2、如果要获取值,必须这样使用:

js代码:

// 切换中英文
import { useI18n } from "vue-i18n";
const { locale } = useI18n(); // 先调用此方法,然后再使用
const changeLang = (msg) => {
  locale.value = msg;
  localStorage.setItem("LANG", msg);
};
const options = [
  {
    value: "cn",
    label: "中文",
  },
  {
    value: "en",
    label: "英文",
  },
];

html:

<el-select v-model="locale" @change="changeLang">
  <el-option
    v-for="item in options"
    :key="item.value"
    :label="item.label"
    :value="item.value"
  />
</el-select>

3、在非 setup 中使用

import i18n from "@/i18n/i18n";
const { t } = i18n.global;

其他就一样了,直接使用就行,这里的路径记得改成自己的!

这里有一个问题,望有大佬懂的可以见沸点:https://juejin.cn/pin/7436608519325548583

axios 封装

// 对你引用的第三方网络请求框架进行封装
import axios from "axios";
// 这里的 router 引入和 setup 中不一样
import router from "@/router";

// 退出登录
function signout() {
  // 清楚需要清除的
  localStorage.removeItem("token");
  localStorage.removeItem("Auth");
  router.replace({
    path: "/",
  });
}

// 设置baseURL
axios.defaults.baseURL = import.meta.env.VITE_APP_BASE_API;

export function request(config) {
  // 1. 创建axios的实例
  const instance = axios.create({
    timeout: 30000,
  });
  //2. 使用axios拦截器
  //2.1 请求拦截
  instance.interceptors.request.use(
    (conF) => {
      // 如果不返回,则真正的请求被拦截了,用户就会打印err
      // 一般进行的操作:
      // 1. config中的一些信息不符合服务器要求,就可以在这修改,在创建实例时也可以
      // 2. 每次发送网络请求时,都希望在界面中显示一个请求的图标
      // 3. 某些网络请求(登陆[token]),必须携带一些特殊的信息
      // console.log(conF);
      const token = localStorage.getItem("token"); // 也可能放在别的地方,按需修改,eg:token ? (conF.headers["Authorization"] = token) : null;
      token ? (conF.headers.token = token) : null;
      // 特殊界面无需token
      if (
        conF.url.includes("/form/information") ||
        conF.url.includes("/user/upload") ||
        conF.url.includes("/user/downloadExcel") ||
        conF.url.includes("/form/update")
      ) {
        conF.headers.token = null;
      }
      return conF;
    },
    (err) => {
      console.log(err);
      ElMessage({
        showClose: true,
        message: err,
      });
    }
  );
  //2.2 响应拦截
  instance.interceptors.response.use(
    (res) => {
      console.log(res);
      //更新token
      if (res.status == 200) {
        if (res.headers?.token) {
          localStorage.setItem("token", res.headers.token);
          // console.log(res.headers.token);
        }
      }
      return res.data;
    },
    (error) => {
      console.log(error);
      if (error.response.status) {
        switch (error.response.status) {
          // 401: 未登录
          case 401:
            ElMessage({
              showClose: true,
              message: "未登录",
            });
            signout();
            break;
          // 403 token过期
          // 登录过期对用户进行提示
          // 清除本地token对象
          // 跳转登录页面
          case 403:
            ElMessage({
              showClose: true,
              message: "登录过期,请重新登录",
            });
            signout();
            break;
          // 404请求不存在
          case 404:
            ElMessage({
              showClose: true,
              message: "网络请求不存在",
            });
            break;
          case 500:
            ElMessage({
              showClose: true,
              message: "服务器错误",
            });
            break;
          // 其他错误,直接抛出错误提示
          default:
            // console.log(error);
            ElMessage({
              showClose: true,
              message: error?.response?.data?.message || error?.response?.message,
            });
          }
        }
        return Promise.reject(error.response);
      } else {
        // console.log(error);
        ElMessage({
          showClose: true,
          message: "服务器异常",
        });
      }
    }
  );
  //3. 真正的发送请求
  return instance(config);
}

切换pinia(后补上)

菜鸟在写完博客后,马上尝试了一下把 vuex 换成 pinia ,当然仅限创建项目的时候换,要是你都开发完了,那就不要换了!!!

首先就是执行两个命令:

npm install pinia -S
npm uninstall vuex

然后把 main.js 的store换成 pinia

在这里插入图片描述

然后如果是 vuex 换过来的就不用自己创建 store目录和 index.js 文件,否则需要自己创建,将里面的内容改成:

// store/index.js
import { defineStore } from "pinia";
// 1. 定义容器、导出容器
// 参数1:容器的ID,必须是唯一的,后面Pinia会把所有的容器挂载到根容器
// 参数2:一些选项对象,也就是state、getter和action
// 返回值:一个函数,调用即可得到容器实例

export const useMainStore = defineStore("main", {
  // 类似于Vue2组件中的data,用于存储全局状态数据,但有两个要求
  // 1. 必须是函数,目的是为了在服务端渲染的时候避免交叉请求导致的数据状态污染
  // 2. 必须是箭头函数,这样是为了更好的 TS 类型推导
  state: () => {
    return {
      info: "pinia 可以使用",
    };
  },
  getters: {},
  actions: {},
});

// 2. 使用容器中的 state
// 3. 通过 getter 修改 state
// 4. 使用容器中的 action 同步和异步请求

项目中最简单的使用:

js代码

import { useMainStore } from "../../store";
const mainStore = useMainStore();

html代码

<div>{{ mainStore.info }}</div>

但是 vite 的 pinia 生成的和这里不一样。vite生成的如下(并没有state,getter等):

// pinia/counter.js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})

pinia确实比vuex好用多了,直接修改也能监听到变化,yyds!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PBitW

可以去掘金看更完善版本

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值