最近菜鸟自己搭建一个项目,想着 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!!!