npm create vite@latest
兼容性注意:
Vite v3+ 需要 Node.js 版本 14.18+,16+
Vite v2+ 需要 Node.js 版本 12.2.0+
使用Git Bash Here
创建项目时,会出现无法上下切换选项的问题,所以一般是在当前文件夹下使用cmd
创建项目
当前目录下打开cmd:
创建项目:
- 使用
npm install
下载基本的依赖- 使用
npm run dev
运行项目(这两步在创建完项目之后都会提示你做的)
这是一个基本的目录结构
node_modules
存放(npm install
之后)项目的依赖public
存放一些基本的公共文件,如 icon 等src
文件,开发最常用的文件夹,所有资源都放在这个文件夹下面
src
文件下几个文件的解读
App.vue
vue项目的主组件,createApp
时 需要传入的根组件,页面入口文件 ,所有页面都是在App.vue下进行切换的。是整个项目的关键,app.vue负责构建定义及页面组件归集。
一般情况下,这个页面里面什么都不做,只放一个router-view
路由视图组件就完事了main.js
项目的入口文件,主要作用是初始化vue实例,并引入所需要的插件
- 实例化vue,上面提到的
createApp
- 引入各类插件,
axios,elementUI
等,引入css
样式- 存储用户的全局变量等
package.json
这个文件要说的东西要多了 可以点击链接直接去看看这位师兄的,很详细
另外package.json
最好和package-lock.json
一起学习使用vite.config.js
vite的官方配置文档,东西多的很,不是一时半会就能学完的,只能说用到的时候去看就行
使用vite需要注意的几个点
- vite中没法使用@来指代src目录,需要手动配置
import path from "path";
resolve: {
// 配置路径别名,解决不能使用@来指代src目录的问题
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
- vite中
无法使用 require
引入文件(CommonJS
模块化规范),vite只支持ESModules
模块化规范
最好也别想着用什么插件强制使用require啥的,没必要
vite
和webpack
类似的环境变量配置方案,使用.env文件创建不同的环境配置文件,再在文件中进行相关的环境字段配置
注意,要想在全局中使用,首字母开头必须是VITE
这样我们就可以在其他文件中使用import.meta.env
访问到当前的环境变量了
package.json
还要进行配置,告诉项目,我们当前使用的是什么环境运行命令、打包命令
使用--mode < env >
的形式
最后在
vite.config.js
也需要一点环境变量的配置
这里我这要配置了【静态资源 CDN】加速的配置,其他跟环境变量没有太大关系,主要是认识loadEnv
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
export default defineConfig(({ command, mode }) => {
// 静态文件路径
/*
loadEnv() 三个参数【https://cn.vitejs.dev/guide/api-javascript.html#loadenv】
@mode:package.json中启动的时候,设置的 --mode 会读取到 中的变量
@envDir:.env.xxx文件的路径
@prefixes:默认情况下只有前缀为 VITE_ 会被加载,除非更改了 prefixes 配置
现在将前缀更改为BASE_URL,这样我们就可以获取运维在部署时,就可以获取到CDN加载静态资源的前缀了,
这个BASE_URL是跟运维约定好的字段,实际要根据自己的项目进行判断
*/
// 如果没有找到 BASE_URL ,就使用 ./ 的目录
const staticPath = loadEnv(mode, "./", "BASE_URL").BASE_URL || "./";
return {
// 静态资源cdn加速,如果是开发环境的话,就是用本地加载不使用cdn
base: mode == "dev" ? "./" : staticPath,
build: {
assetsDir: "static", // 静态资源导出的文件名
},
plugins: [vue()],
resolve: {
// 配置路径别名,解决不能使用@来指代src目录的问题
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
};
});
npm install axios
下载 axios
下载之后不需要在main.js
中进行引入,直接在需要的地方引入就行了
request.js文件
封装了【请求】、【响应】拦截器,三个常用的请求方法,全局loading 这样最基础的一个axios
请求就封装好了
import axios from "axios";
import { getToken, removeToken } from "@/utils/auth";
// 引入全局loading的【开始loading】和【结束loading】
import { startLoading, endLoading } from "./requestLoading";
// 这里需要注意一点,我们的router不能使用 useRouter的形式,那个是vue文件中使用的,这里我们要自己引入
import myRouter from "../router/index";
import { ElMessage } from "element-plus";
// 引入环境变量
const myProcess = import.meta.env;
// 创建一个axios实例
const service = axios.create({
baseURL: myProcess.VITE_APP_API,
timeout: 1000 * 30, // 请求超时时间
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
// request请求拦截器
service.interceptors.request.use(
(config) => {
if (config.headers.showLoading) {
// 开始请求的时候,调用startLoading
startLoading();
}
if (getToken() && getToken() !== undefined) {
config.headers["token"] = getToken(); // 让每个请求携带自定义请求头
}
return config;
},
// 咋说呢,这块其实要不要都无所谓
(error) => {
return Promise.reject(error);
}
);
// response响应拦截器
service.interceptors.response.use(
(response) => {
endLoading();
// 未登录或者登陆超时的时候,返回登录页面
if ([401, 403].includes(response.data.code)) {
removeToken();
myRouter.push({ name: "login" });
}
return response;
},
(error) => {
endLoading();
ElMessage.error(error.message);
return Promise.reject(error);
}
);
/*
@description:get方法,对应get请求
@url:请求地址
@params:请求参数
@showLoading:是否在等待接口返回时,启动全局loading
*/
export const get = (url, params, showLoading) => {
return service({
url: url,
method: "get",
params,
headers: { showLoading },
});
};
/*
@description:postFormData 适用于老接口,很多老接口使用的是formData进行传值
@url:请求地址
@params:请求参数
@showLoading:是否在等待接口返回时,启动全局loading
*/
export function postFormData(url, data, showLoading) {
return service({
url,
method: "post",
headers: { "Content-Type": "application/x-www-form-urlencoded", showLoading },
transformRequest: [
function (data) {
// 对 data 进行任意转换处理
let ret = "";
for (const it in data) {
ret += encodeURIComponent(it) + "=" + encodeURIComponent(data[it]) + "&";
}
return ret;
},
],
data,
});
}
// postJson
export function postJson(url, data, showLoading) {
return service({
url,
method: "post",
data,
headers: { "Content-Type": "application/json", showLoading },
});
}
// 这个导出可有可无,只能说导出去需要用到的时候再用吧
export default service;
requestLoading.js
文件
import { ElLoading } from "element-plus";
// 设置请求的数量
let requestNum = 0;
// 设置loading变量
let loading;
// 开始loading
export function startLoading() {
if (requestNum === 0) {
loading = ElLoading.service({
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)",
});
}
requestNum++;
}
// 结束loading
export function endLoading() {
// 延迟 300ms 再调用 closeLoading 方法, 合并300ms内的请求
// 当有请求中嵌套请求的情况也也可开启延时来解决
setTimeout(closeLoading, 300);
// closeLoading();
}
function closeLoading() {
if (requestNum <= 0) return;
requestNum--;
if (requestNum === 0) {
loading.close();
}
}
在创建
vite
项目的时候,vue-router
并不是自带的,因为vue
的本质是一个渐进式框架,我们需要手动引入并配置路由
npm install vue-router@4 点击查看vue-router官方文档
下载完成后,我们在src目录下新建一个router
文件夹,文件夹中新建一个index.js
,初始并实例化我们的router
index.js文件
// 引入创建路由的函数,和配置路由模式的函数
import { createRouter, createWebHashHistory } from "vue-router";
const routes = [
{
path: "/",
name: "login",
component: () => import(/* webpackChunkName: "登录页面" */ "../views/login/index.vue"),
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
最后引入到我们的
main.js
文件中,并使用
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
// 引入路由文件
import router from "./router/index";
const app = createApp(App);
// 使用路由
app.use(router)
app.mount("#app");
less
为例没有必要为它们安装特定的
Vite
插件,但必须安装相应的预处理器依赖
直接执行完命令就可以使用less
了
npm add -D less
vite 官方文档CSS预处理器
首先我们下载
element-ui-plus
,使用element
的container
布局容器进行页面结构布局
npm install element-plus --save
官方文档
下载
element icon
这个路由左侧的icon
会用到
npm install @element-plus/icons-vue
在mian.js
中引入
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
// 引入路由文件
import router from "./router/index";
// 引入ElementPlus
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
// 引入element icon
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
const app = createApp(App);
// 引入element icon
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
// 使用路由
app.use(router);
// 使用ElementPlus
app.use(ElementPlus);
app.mount("#app");
<template>
<div class="common-layout">
<el-container>
<el-aside width="200px" class="aside_wrap">
<el-scrollbar>
<el-menu
active-text-color="#ffd04b"
background-color="#545c64"
class="el-menu-vertical-demo"
text-color="#fff"
:default-active="defaultActive"
>
<myAside :mainRouterList="mainRouterList">myAside>
el-menu>
el-scrollbar>
el-aside>
<el-container>
<el-header class="header_wrap">
<myHeader>myHeader>
el-header>
<el-main class="main_wrap">
<myMain>myMain>
el-main>
<el-footer>Footerel-footer>
el-container>
el-container>
div>
template>
<script setup>
import myAside from "../layout/components/myAside/myAside.vue";
import myHeader from "../layout/components/myHeader/myHeader.vue";
import myMain from "../layout/components/myMain/myMain.vue";
import { useRoute, useRouter } from "vue-router";
import { ref } from "vue";
const myRouter = useRouter();
const myRoute = useRoute();
// 获取路由里的所有路由数组
const routerList = myRouter.options.routes;
// 获取layout里面的所有数组
const mainRouterList = routerList.find((v) => v.name == "home").children;
// 当前路由地址,用作左边侧边栏的高亮
const defaultActive = ref("");
defaultActive.value = myRoute.path;
script>
<style lang="less" scoped>
.aside_wrap {
background-color: rgb(136, 136, 136);
height: 100vh;
}
.header_wrap {
background-color: rgb(220, 243, 255);
}
.main_wrap {
background-color: #c7c7c7;
height: 100%;
}
style>
使用递归的形式,循环调用el-menu
里面的el-sub-menu和el-menu-item
myAside
文件里代码
<template>
<template v-for="item in myMainRouterList" :key="item.name">
<!-- 多个 -->
<el-sub-menu :index="item.path" v-if="item?.children?.length > 0">
<template #title>
<el-icon><component :is="item?.meta?.icon"></component></el-icon>
<span>Navigator One</span>
</template>
<!-- 递归调用自己 -->
<myAside :mainRouterList="item?.children"></myAside>
</el-sub-menu>
<!-- 单个 -->
<el-menu-item :index="item.path" v-else @click="handleClickMenu(item)">
<el-icon><component :is="item?.meta?.icon"></component></el-icon>
<span>Navigator Two</span>
</el-menu-item>
</template>
</template>
<script setup>
import { defineProps } from "vue";
import { useRouter } from "vue-router";
const myRouter = useRouter()
// 获取父组件传递过来的参数
const myProps = defineProps(["mainRouterList"]);
const myMainRouterList = myProps.mainRouterList;
// 点击左侧侧边栏,进行路由跳转
const handleClickMenu = (subItem) => {
myRouter.push(subItem?.path);
};
</script>
然后再随便撸一个路由
// 引入创建路由的函数,和配置路由模式的函数
import { createRouter, createWebHashHistory } from "vue-router";
const routes = [
{
path: "/",
name: "login",
component: () => import(/* webpackChunkName: "登录页面" */ "../views/login/index.vue"),
},
{
path: "/home",
name: "home",
redirect: { name: "page1" },
component: () => import("../views/layout/index.vue"),
children: [
{
path: "/page1",
name: "page1",
meta: { icon: "CirclePlus", title: "页面11" },
redirect: { name: "page1-1" },
children: [
{
path: "/page1-1",
name: "page1-1",
meta: { icon: "Link", title: "页面1-1" },
component: () => import("../views/pages/components/page1-1.vue"),
},
{
path: "/page1-2",
name: "page1-2",
meta: { icon: "Position", title: "页面1-2" },
component: () => import("../views/pages/components/page1-2.vue"),
},
],
},
{
path: "/page2",
name: "page2",
meta: { icon: "Female", title: "页面22" },
component: () => import("../views/pages/page2.vue"),
},
{
path: "/page3",
name: "page3",
meta: { icon: "Male", title: "页面33" },
component: () => import("../views/pages/page3.vue"),
},
],
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;