通过全程参与,可以加深对VUE项目的理解。
近期做的一个项目,前端除了UI外,没有使用什么框架。不使用现成的框架是无奈之举,因为找不到合适的。之前用的框架,比较老旧,还是vue2的;新的吧,有学习成本,怕耽误时间,也不知道效果怎么样,存在一定的风险。利用最基本的“空白”项目,按需添加基础功能,代码可控,进度也较有保证,同时还能够消除无框架不会工作恐惧症。现在记录一下心得,以后可以反复使用。
记录重点有:
0、整体结构 1、路由 2、导航条及子菜单 3、ajax请求封装 4、vue.config.js及系统配置 5、登录及退出
一、整体结构
1、创建项目 首先是新创建一个vue3项目。方法是
vue create 项目名称
然后选择合适的选项。具体可参考拙作 vue3多个项目共享开发和单个项目独立打包的解决方案
1)按默认方案创建 项目结构: 2)创建时增加路由及store支持 多了router、store以及一些页面。
3)我们项目的整体结构 实际项目中,当然还会夹带一些私货,额外增加一些东东。
2、项目入口main.js 注意项目的入口不是App.vue,而是main.js。
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App).use(store).use(router).mount('#app')
main.js是系统约定好的名称,正如一般程序的入口是函数void main()一样。整个项目的入口是main.js,然后每个模块的入口可以是index.js。都是js。本质上,vue是一个大的js语法糖。它有这样那样的结构,让人只把杭州作汴州,但归根到底,它最终是要编译成原始的js,才能被浏览器识别、运行。
从上述代码可以知道,main.js的作用是加载App.vue,引入store、路由,然后绑定到页面id="app"的div。这样该div就是整个项目的活动区域,即展示页面内容的区域了: vue项目是单页面应用,只有一张html页面。我们看到的所有内容,都展示在id="app"的这个div上!完全由js控制。
3、我们项目里的main.js main.js这里的引用,都是全局性的。除了路由,store,还可以引入ui框架,css,全局性组件等等。比如我们项目里的main.js是这样写:
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import PerfectScrollbar from "vue3-perfect-scrollbar";
import "vue3-perfect-scrollbar/dist/vue3-perfect-scrollbar.css";
import Antd from "ant-design-vue";
import "ant-design-vue/dist/antd.css";
import "@/assets/css/default.css";
createApp(App).use(Antd).use(PerfectScrollbar).use(router).mount("#app");
二、路由
简单来说,路由就是菜单配置,将菜单项的id,路径,都集中写在了一个配置文件里。
1、系统自动生成的路由
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import( '../views/AboutView.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
以上完全是系统自动生成的代码。看上去也不难理解。其中路由表routes对应的是导航条 2、实际项目中的路由 见本文第六章第一条
3、代码中使用路由 如果想多加几个导航条,可以依葫芦画瓢,很容易就能实现。每个路由,name是唯一的ID,在代码里控制跳转的话,引用name就可以,可以不再重复写这个path。比如在某个vue里,可以这样使用路由:
import { defineComponent } from "vue";
import { useRouter } from "vue-router";
export default defineComponent({
setup() {
const router = useRouter();
const browseIt = (fd) => {
const to = router.resolve({
name: "about",
params: { id: fd.id },
});
window.open(to.href, "_blank");
};
return {
browseIt,
};
},
});
三、导航条及子菜单
1、导航条 系统生成的代码,已经做了很好的示范:
<template>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav>
<router-view/>
</template>
通常,我们的菜单可以从服务器端返回,然后利用循环语句输出。
2、子菜单 导航条是一级菜单,二级或以下是子菜单。子菜单,可以利用UI框架来完成。比如我们利用ant design vue的menu组件来完成
四、ajax请求封装
我们做的项目,总免不了要从服务器端请求数据。前后端分离,理论上前端的数据都来自于服务器端。
目前一般是结合第三方组件axios对ajax请求进行封装。理由主要有2个:
1)ajax一般有超时、返回代码区别对待等共性操作,封装一个ajax处理方法,统一调用,方便维护和修改;除此之外,axios还可以对ajax请求进行拦截,使得请求前、请求后、结果返回做相应处理。
2)解决跨域问题。axios本身似乎并不解决跨域问题,但由于第一点,它对ajax请求进行了封装,我们可以利用一个统一方法,结合api路径前缀做转发,使得浏览器以为api所在路径与前端是同一台服务器,因而不存在跨域。注意所谓跨域问题,是浏览器的一个安全设置,是一种保护措施。只要它不觉得跨域,那跨域就不存在。
1、统一的ajax封装方法
1)ajax封装代码(src/request/index.js)
import axios from "axios";
const service = axios.create({
baseURL: "/api",
timeout: 60000,
withCredentials: true,
headers: {
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest",
},
});
service.interceptors.request.use(
function (config) {
return config;
},
function (error) {
console.log(error);
return Promise.reject(error);
}
);
service.interceptors.response.use(
function (response) {
console.log(response);
const dataAxios = response.data;
return dataAxios;
},
function (error) {
console.log(error);
return Promise.reject(error);
}
);
export default service;
2)使用ajax统一封装方法
import request from "@/request";
export const userLogin = (params) => {
return request({
url: "/sys/login",
params,
method: 'post'
})
}
2、开发环境中,利用vue.config.js做api路径转发 如上所属,ajax的封装方法request,会在api请求路径前加上前缀“/api”,可以利用这一点来设置转发。转发是为了避免跨域。其原理,表面上,我们请求的是当前服务器的路径“/api/a/b/c”,但我们设置凡“/api”开头的路径,都转发到另一台服务器(即后端所在服务器)。浏览器蒙在鼓里,并没有察觉,因此不会触发所谓跨域警告。
devServer: {
proxy: {
"/api": {
target: "192.168.0.22",
pathRewrite: {
"^/api": "",
},
},
},
},
3、生产环境中,利用nginx做api路径转发
location /api/ {
proxy_pass http://192.168.0.22:8090/;#必须斜杠/结尾
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
五、vue.config.js及系统配置
有关vue.config.js里面的配置,上面约略提到了一些,这里给出完整代码:
1、系统生成的vue.config.js代码
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})
2、按项目需要修改后的代码
const { defineConfig } = require("@vue/cli-service");
const path = require("path");
const appConfig = require("./public/config");
const resolve = (dir) => {
return path.join(__dirname, dir);
};
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
proxy: {
"/api": {
target: appConfig.server,
pathRewrite: {
"^/api": "",
},
},
},
},
publicPath: "/",
chainWebpack: (config) => {
config.resolve.alias
.set("@", resolve("src"))
.set("_c", resolve("src/components"));
config.plugin("html").tap((args) => {
args[0].title = appConfig.app.name;
return args;
});
},
});
值得一提的是,里面的devServer元素设置,针对的是开发环境。详见拙作:vue.config.js中的devServer。
3、真正的项目配置 按我的理解,vue.config.js只在开发阶段和发布时有用,之后就像被消费了的耗材,没啥用处了。同一项目中,真正的项目配置,是/public/config/index.js,即使是发布、部署到生产环境,仍然可以修改,是真正意义上的配置文件。
/public/config/index.js
exports.app = {
name: "订餐拿饭抓阄系统",
owner: "蓬蓬养猪场",
developer: "一群饭桶",
};
详见拙作:vue项目读取全局配置
六、登录及退出
主要是路由的应用。原理是: 将页面分为无须登录可浏览和必须登录方可浏览2种。在路由表中做过滤。当转向必须登录页面时,检查登录状态,如果已登录,放行;未登录,转向登录页。注意登录页要设为无须登录可浏览。流程很简单,大家都明白,就不画流程图了。
1、路由表(/src/router/index.js) 完整的路由表。有关登录控制部分,见所谓“路由守卫”。
import { createRouter, createWebHashHistory } from "vue-router";
import Home from "../views/home/PageIndex.vue";
const routes = [
{
path: "/login",
name: "login",
component: () => import("../views/login/PageIndex.vue"),
meta: {
noLogin: true,
},
},
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/map",
name: "Map",
component: () => import("../views/map/PageIndex.vue"),
},
{
path: "/resource",
name: "Resource",
component: () => import("../views/resource/PageIndex.vue"),
},
{
path: "/resource/detail/:id",
name: "ResourceDetail",
component: () => import("../views/resource/PageDetail.vue"),
},
{
path: "/sys",
name: "Sys",
component: () => import("../views/sys/PageIndex.vue"),
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
router.beforeEach((to, from, next) => {
const isLogin = localStorage.isLogin ? true : false;
if (isLogin) {
to.path === "/login" ? next("home") : next();
} else {
to.meta.noLogin || to.path === "/login" ? next() : next("/login");
}
});
export default router;
2、登录及登出 注意要使用router本身的方法,不可用原始的js,如window.location.href = *** 这种方法,否则部署到nginx会报错。 1)登录
const router = useRouter();
localStorage.setItem("isLogin", true);
document.cookie = "token=" + res.token;
router.replace({ path: "/" });
2)登出
const router = useRouter();
localStorage.removeItem("isLogin");
router.replace({ path: "/login" });
七、store
vuex store。
其作用,主要是提供数据共享,使得各组件之间方便传递数据。vue基于组件开发,但组件之间传递参数甚为繁琐。有了store,那么所有组件都能直接从store读取,十分方便。其次,store还可以用来实现数据缓存。比如获取数据时,先看看store里有没有,有的话,直接读取返回;没有的话,从数据库读取,然后存入store再返回。缓存的话,前端存入localStorage也可以,但有个数据过期问题,需要手动清除,或者使用时间戳进行比较。而store,刷新页面即消失,或者关掉浏览器也消失,一般没有数据老化的问题。
1、引入store 2、定义store 3、使用store
|