Geeker-Admin项目跟做笔记(vue3+vite+pinia)

news2025/2/27 17:26:26

文章目录

  • 一、路由配置
    • (一) 静态路由
    • (二)动态路由
  • 二、axios的配置
      • 1. 创建axiosCancel.ts文件,用于有pending后直接取消
      • 2. axios封装
      • 3. 使用
  • 三、pinia仓库的使用
  • 四、Header 设计笔记
      • 1. 国际化(中英文切换)
  • 五、Menu 设计笔记
      • 1. Menu 组件结构
      • 2. 父级菜单与子菜单
  • 六、tabs标签页
      • 1. 利用pinia设计tabs仓库
      • 2. Tabs.vue
  • 七、数据大屏
      • 1. 数据大屏自适应屏幕大小
      • 2. 常用EChart资源

一、路由配置

(一) 静态路由

1.配置路由

const routes: RouteRecordRaw[] = [
    {
        path: '/login',
        name: 'login',
        component: () => import('../pages/login/index.vue'),
        meta: {
            requiresAuth: false,
            title: '登录',
            key: 'login'
        }
    }
]

RouteRecordRaw:为了规范ts的开发,增加对路由对象类型的限制

2.创建一个路由对象

const router = createRouter({
    history: createWebHashHistory(),
    routes,
    strict: false,
    // 切换页面,滚动到最顶部
    scrollBehavior: () => ({ left: 0, top: 0 })
})

history:hash模式(链接地址中有一个#)

3. 暴露路由对象

export default router

4. main.js中引入注册路由

import router from './routers/index'
app.use(router).mount('#app')

5. App.vue中将路由显示出来

<template>
	<router-view></router-view>
</template>

6. 子路由的设置与引入
6.1 将侧边菜单Layout组件设置为一级路由

将路由组件展示到页面

export const Layout=()=>import("../layout/index.vue")

6.2 设置modules文件夹,存放各路由组件路由
6.3 子路由的书写

path:路由路径
name:路由名称
redirect:路由重定向
meta:路由元信息
meta.requireAuth:是否需要权限验证
param meta.keepAlive:是否需要缓存该路由
param meta.title:路由标题
param meta.key:路由key,用来匹配权限按钮
children:二级路由

import { RouteRecordRaw } from "vue-router";
import { Layout } from "../constant";

// 常用组件模块
const dashboardRouter: Array<RouteRecordRaw> = [
    {
        path: '/dashboard',
        component: Layout,
        redirect: '/dashboard/dataVisualize',
        meta: {
            title: 'Dashboard'
        },
        children: [
            {
                path: '/dashboard/dataVisualize',
                name: 'dataVisualize',
                component: () => import('@/pages/dashboard/dataVisualize/index.vue'),
                meta: {
                    keepAlive: true,
                    requiresAuth: true,
                    title: '数据可视化',
                    key: 'dataVisualize'
                }
            },
            {
                path: '/dashboard/embedded',
                name: 'embedded',
                component: () => import('@/pages/dashboard/embedded/index.vue'),
                meta: {
                    keepAlive: true,
                    requiresAuth: true,
                    title: '内嵌页面',
                    key: 'embedded'
                }
            },
        ]
    }]

export default dashboardRouter

6.4 在router.ts中导入所有路由

const metaRouters = import.meta.glob("./modules/*.ts", { eager: true });

如果你倾向于直接引入所有的模块(例如依赖于这些模块中的副作用首先被应用),你可以传入 { eager: true } 作为第二个参数

import.meta.glob全局导入参考文档vite官网

6.5 处理路由表

export const routerArray: RouteRecordRaw[] = []
// Object.keys 返回一个所有元素为字符串的数组
Object.keys(metaRouters).forEach(item => {
    Object.keys(<Object>metaRouters[item]).forEach((key:any)=>{
        routerArray.push(...metaRouters[item][key])
    })
})

Object.keys方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。

6.6 注册路由

const routes: RouteRecordRaw[] = [
    ...routerArray
]

(二)动态路由

1. 动态路由配置
1.1 目录
在这里插入图片描述
1.2 配置参数

 * @description 动态路由参数配置简介
 * @param path ==> 菜单路径
 * @param name ==> 菜单别名
 * @param redirect ==> 重定向地址
 * @param component ==> 视图文件路径
 * @param meta ==> 菜单信息
 * @param meta.icon ==> 菜单图标
 * @param meta.title ==> 菜单标题
 * @param meta.activeMenu ==> 当前路由为详情页时,需要高亮的菜单
 * @param meta.isLink ==> 是否外链
 * @param meta.isHide ==> 是否隐藏
 * @param meta.isFull ==> 是否全屏(示例:数据大屏页面)
 * @param meta.isAffix ==> 是否固定在 tabs nav
 * @param meta.isKeepAlive ==> 是否缓存

2. 创建路由对象(index.ts)

const router = createRouter({
    history: createWebHashHistory(),
    routes: [...staticRouter, ...errorRouter],
    strict: false,
    scrollBehavior: () => ({ left: 0, top: 0 })
})

二、axios的配置

1. 创建axiosCancel.ts文件,用于有pending后直接取消

1.1 声明一个Map用于存储每个请求的标识和取消函数

Map对象的好处是可以快速判断是否有重复的请求

let pendingMap = new Map<string, Canceler>()

1.2 序列化参数

根据当前请求的信息生成请求的 Key

export const getPendingUrl = (config: AxiosRequestConfig) =>
    [
        config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)
    ].join('&')

1.3 创建AxiosCanceler类

  • 添加请求—用于把当前请求信息添加到 pendingRequest对象中
addPending(config: AxiosRequestConfig) {
    // 在请求开始之前,对之前的请求做检查取消操作
    this.removePending(config)
    const url = getPendingUrl(config);
    config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
        if (!pendingMap.has(url)) {
            // 如果pending中不存在当前请求,则添加进去
            pendingMap.set(url, cancel)
        }
    });
}
  • 移除请求–检查是否存在重复请求,若存在则需要取消已发出的请求
removePending(config: AxiosRequestConfig) {
    const url = getPendingUrl(config);
    if (pendingMap.has(url)) {
        // 如果pending中存在当前请求标识,需要取消当前请求,并且移除
        const cancel = pendingMap.get(url)
        cancel && cancel()
        pendingMap.delete(url)
    }
};
  • 清空所有pending
removeAllPending() {
    pendingMap.forEach(cancel => {
        cancel && isFunction(cancel) && cancel();
    })
    pendingMap.clear();
};

其中,isFunction()是自定义的一个类型

  • 重置
reset(): void {
   pendingMap = new Map<string, Canceler>()
}

2. axios封装

2.1 创建一个AxiosCanceler对象

import { AxiosCanceler } from "./helper/axiosCancel";
const axiosCanceler = new AxiosCanceler()

2.2 配置config对象

const config = {
    // 默认请求地址
    baseURL: import.meta.env.VITE_API_URL as string,
    // 设置超时时间:500
    timeout: ResultEnum.TIMEOUT as number
}

默认请求地址在.evn开头的文件中

2.3 创建RequestHttp类

  • 2.3.1 创建axios实例
service: AxiosInstance;//AXIOS实例

以下步骤在构造函数public-constructor中

  • 2.3.2 实例化axios
this.service = axios.create(config)
  • 2.3.3 配置请求拦截器

客户端发送请求 - 请求拦截器 - 服务器

this.service.interceptors.request.use(
   (config: AxiosRequestConfig) => {
       const globalStore = GlobalStore();
       // 将当前请求添加到pending中
       axiosCanceler.addPending(config);
       // 如果当前请求不需要显示Loading,在api服务站通过指定的第三个参数:{headers:{noLoading:true}}来控制不显示loading
       config.headers!.noLoading || showFullScreenLoading();
       //从GlobalStore仓库中获取token
       const token: string = globalStore.token;
       return { ...config, headers: { ...config.headers, "x-access-token": token } };
   },
   (error: AxiosError) => {
       return Promise.reject(error)
   }
);
  • 配置响应拦截器

服务器返回信息- 拦截统一处理 -客户端Js获取信息

this.service.interceptors.response.use(
   (response: AxiosResponse) => {
       const { data, config } = response;
       const globalStore = GlobalStore()
       // console.log('reaponse', data);
       // 在请求结束后,移除本次请求
       axiosCanceler.removePending(config)
       // 关闭Loading
       tryHideFullScreenLoading()
       //1. 登录失效(code==599)
       if (data.code == ResultEnum.OVERDUE) {
           ElMessage.error(data.msg)
           // setToken为仓库的action
           globalStore.setToken('')
           // 跳转至登录页面
           router.replace({
               path: "/login"
           })
           return Promise.reject(data);
       }
       // 2.全局错误信息拦截(防止下载文件的时候返回数据流,没有code,直接报错)
       //后面页面请求就不用判断data.code==200
       if (data.code && data.code !== ResultEnum.SUCCESS) {
           ElMessage.error(data.msg);
           return Promise.reject(data);
       }
       // 3.请求成功
       return data;
   },
   async (error: AxiosError) => {
       const { response } = error;
       tryHideFullScreenLoading();
       // 请求超时单独判断,因为请求超时没有reaponse
       if (error.message.indexOf("timeout") !== -1) ElMessage.error('请求超时,请稍后重试')
       // 根据响应的错误状态码,做不同的处理
       if (response) checkStatus(response.status);
       // 服务器结果都没有返回(可能服务器错误也可能服务端断网),断网处理:可以跳转到段网页面
       if (!window.navigator.onLine) router.replace({ path: "/errorPage/500" });
       return Promise.reject(error)
   }
)

2.4 常用请求方法封装

get<T>(url: string, params?: object, _object = {}):
    Promise<ResultData<T>> {
    return this.service.get(url, { params, ..._object });
}

post<T>(url: string, params?: object, _object = {}):
    Promise<ResultData<T>> {
    return this.service.post(url, params, _object);
}

2.5 暴露RequestHttp类

export default new RequestHttp(config)

3. 使用

以登录接口为例
3.1. 为组件的api标注类型(api–interface–index.ts)

  • 3.1.1 请求响应参数(不包含data)
export interface Result {
    code: string,
    msg: string
}
  • 3.1.2 请求响应参数(包含data)
export interface ResultData<T = any> extends Result {
    data?: T;
}
  • 3.1.3 登录模块
export namespace Login {
    export interface ReqLoginForm {        username: string;
        password: string;
    }
    export interface ResLogin {
        access_token: string
    }
}

3.2 后端为服务器端口名(api–config–servicePort.ts)

export const PORT1 = "/geeker";

3.3 登录模块(api–modules–login.ts)

import { Login } from "../interface";
import { PORT1 } from "@/api/config/servicePort";
import http from "../../api"

// 用户登录接口
export const loginApi=(params:Login.ReqLoginForm)=>{
    return http.post<Login.ResLogin>(PORT1+`/login`,params);
}

3.4 在登录组件中使用

formEl.validate(async valid => {
    if (!valid) return
    loading.value = true
    try {
      const requestLoginForm:Login.ReqLoginForm={
        username:loginFrom.username,
        password:md5(loginFrom.password)
      }
      const res=await loginApi(requestLoginForm)
      ElMessage.success('登录成功')
    } finally {
      loading.value = false
    }
})

3.5 请求结果
在这里插入图片描述

三、pinia仓库的使用

四、Header 设计笔记

1. 国际化(中英文切换)

参考文档:Mpx框架—国际化i18n
4.1.1安装vue-i18n

npm install vue-i18n --save

4.1.2 在vite.config.ts中对vue-i18n进行配置

alias:{
  'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',
}

4.1.3 配置i18n

  • index.ts引入createI18n
import { createI18n } from "vue-i18n";
  • 设置语言
    (1) 中文zh.ts
export default{
    home:{
        welcome:"欢迎使用"
    },
    tabs:{
        more:"更多",
        closeCurrent:"关闭当前",
        closeOther:"关闭其他",
        closeAll:"关闭所有"
    }
}

(2)英文en.ts

export default{
    home:{
        welcome:"Welcome"
    },
    tabs:{
        more:"More",
        closeCurrent:"Close current",
        closeOther:"Close other",
        closeAll:"Close All"
    }
}
  • 将上面两个文件引入index.ts
import zh from "./modules/zh";
import en from "./modules/en";
  • 配置createI18n
const i18n = createI18n({
    legacy: false,  // 如果要支持 compositionAPI,此项必须设置为 false
    locale: "zh", //设置语言类型
    globalInjection: true,//全局注册$t方法
    messages:{
        zh,
        en
    }
});
export default i18n;

4.1.4 main.js中引入并配置i18n

import i18n from '@/language/index'
const app = createApp(App)
app.use(i18n).mount('#app')

4.1.5 使用

<span>{{$t('tabs.more')}}</span>

五、Menu 设计笔记

1. Menu 组件结构

<template>
    <div class="menu">
    	//子组件
        <Logo :isCollapse="isCollapse" />
        <el-scrollbar>
            <el-menu>
                <el-menu>
                	//子组件
                    <SubItem :menuList="menuList" />
                </el-menu>
            </el-menu>
        </el-scrollbar>
    </div>
</template>

2. 父级菜单与子菜单

5.2.1 利用pinia设计菜单仓库MenuStore

  • 设计MenuStore的ts类型MenuState
export interface MenuState {
    menuList: Menu.MenuOptions[];
}
  • MenuStore仓库设计
import { defineStore } from "pinia";
import { MenuState } from "../interface";
import piniaPersistConfig from "@/config/piniaPersist";

export const MenuStore = defineStore({
    id: "MenuStore",
    state: (): MenuState => ({
        // menu list
        menuList: []
    }),
    getters: {},
    actions: {
        async setMenuList(menuList: Menu.MenuOptions[]) {
            this.menuList = menuList
        }
    },
    //开启该插件,开启数据存储
    persist: piniaPersistConfig("MenuState")
})

id : 作为store的第一个参数,是store唯一的名称(必须!!!)
state:相当于data
geters:相当于computed
actions:相当于methods
开启持久化:persist: piniaPersistConfig(“MenuState”)

5.2.2 从MenuStore中获取menuList,并将它传递给子组件SubItem

  • 在Menu组件中引入MenuStore
import { MenuStore } from '@/store/modules/menu';
  • 使用计算属性从MenuStore中获取menulist
const menuStore = MenuStore();
const menuList = computed((): Menu.MenuOptions[] => menuStore.menuList);
  • 将menuList传递给子组件
<SubItem :menuList="menuList" />
  • SubItem子组件接收列表
defineProps<{ menuList: Menu.MenuOptions[] }>();

tips :当前菜单列表为空

5.2.2 获取菜单列表

login.ts文件

  • 引入本地Json文件(mock.js)
import menu from '@/assets/json/menu.json'
  • 获取菜单列表
export const getMenuList = () => {
    return menu;
}
  • 使用递归处理路由菜单,生成一维数组(util.ts)
/**
 * @description: 使用递归处理路由菜单,生成一维数组
 * @param {Array} menuList 所有菜单列表
 * @param {Array} newArr 菜单的一维数组
 * @return array
 */
export function handleRouter(routerList: Menu.MenuOptions[], newArr: string[] = []) {
    // console.log(routerList);
    routerList.forEach((item: Menu.MenuOptions) => {
        typeof item === "object" && item.path && newArr.push(item.path);
        item.children && item.children.length && handleRouter(item.children, newArr)
    })
    // console.log(newArr);
    return newArr;
}

Menu.vue:获取菜单列表

onMounted(async () => {
    // 获取菜单列表
    try {
        const res = await getMenuList();
        console.log(res);
        if (!res.data) return;
        // 把路由菜单处理成一维数组(存储到pinia中)
        const dynamicRouter = handleRouter(res.data);
        menuStore.setMenuList(res.data);
    } finally {

    }
})

5.2.3 router-view将路由组件渲染至页面

<el-main>
  <router-view v-slot="{Component,route}">
    <transition appear name="fade-transform" mode="out-in">
      <keep-alive :include="cacheRouter">
        <component :is="Component" :key="route.path"></component>
      </keep-alive>
    </transition>
  </router-view>
</el-main>
  • v-slot = “{Component,route}”— 接收Props的默认插槽,并解构

  • 当使用 <component :is="..."> 来在多个组件间作切换时,被切换掉的组件会被卸载。我们可以通过 <KeepAlive> 组件强制被切换掉的组件仍然保持“存活”的状态。

  • cacheRouter — 使用递归,过滤需要缓存的路由

_route 所有路由表
_cache 缓存的路由表

import { RouteRecordRaw, RouteRecordName } from "vue-router";
import { routerArray } from "./router";

let cacheRouter: any[] = [];
const filterKeepAlive = (_route: RouteRecordRaw[], _cache: RouteRecordName[]): void => {
    _route.forEach(item => {
        item.meta?.keepAlive && item.name && _cache.push(item.name);
        item.children && item.children.length !== 0 && filterKeepAlive(item.children,_cache)
    })
};

filterKeepAlive(routerArray,cacheRouter);

export default cacheRouter;

cacheRouter:
在这里插入图片描述

六、tabs标签页

1. 利用pinia设计tabs仓库

6.1.1 点击左侧菜单栏添加tabs标签页

  • 不添加黑名单中的路径:如果路径包含在黑名单中,则退出方法
if (TABS_BLACK_LIST.includes(tabsItem.path)) return;
  • 6.1.2 定义标签信息

title:标签名(默认值:首页)
path:标签路径(默认值:HOME_URL)
close:标签状态(标签是否关闭,默认关闭)

 const tabInfo: TabsOptions = {
    title: tabsItem.title,
    path: tabsItem.path,
    close: tabsItem.close
};
  • 如果tabsMenuList中每一个元素的路径都等于 tabsItem.path ,则把该元素添加至tabsMenuList

Array.every:一个数组内的所有元素是否都能通过某个指定函数的测试,都通过则返回true,否则返回false

if (this.tabsMenuList.every(item => item.path !== tabsItem.path)) {
    this.tabsMenuList.push(tabInfo);
};
  • 将路径赋值给tabsMenuValue
this.setTabsMenuValue(tabsItem.path);

完整代码:

async addTabs(tabsItem: TabsOptions) {
    // 不添加黑名单中的路径
    if (TABS_BLACK_LIST.includes(tabsItem.path)) return;
    const tabInfo: TabsOptions = {
        title: tabsItem.title,
        path: tabsItem.path,
        close: tabsItem.close
    };
    if (this.tabsMenuList.every(item => item.path !== tabsItem.path)) {
        this.tabsMenuList.push(tabInfo);
    };
    this.setTabsMenuValue(tabsItem.path);
},

6.1.2 关闭标签页

  • 循环遍历tabsMenuList
if (tabsMenuValue === tabsPath) {
    tabsMenuList.forEach((item, index) => {
        if (item.path !== tabsPath) return;
        const nextTab = tabsMenuList[index + 1] || tabsMenuList[index - 1];
        if (!nextTab) return;
        tabsMenuValue = nextTab.path;
        router.push(nextTab.path);
    });
}

完整代码

async removeTabs(tabsPath: string) {
    let tabsMenuValue = this.tabsMenuValue;
    let tabsMenuList = this.tabsMenuList;
    if (tabsMenuValue === tabsPath) {
        tabsMenuList.forEach((item, index) => {
            if (item.path !== tabsPath) return;
            const nextTab = tabsMenuList[index + 1] || tabsMenuList[index - 1];
            if (!nextTab) return;
            tabsMenuValue = nextTab.path;
            router.push(nextTab.path);
        });
    }
    this.tabsMenuValue = tabsMenuValue;
    this.tabsMenuList = tabsMenuList.filter(item => item.path !== tabsPath);
},

2. Tabs.vue

6.2.1 监听路由变化

// getter函数形式
watch(
    () => route.path,
    () => {
        let params = {
            title: route.meta.title as string,
            path: route.path,
            close: true
        };
        tabStore.addTabs(params)
    },
    {
        immediate: true
    }
)

七、数据大屏

1. 数据大屏自适应屏幕大小

7.1.1 为外层盒子添加一个ref属性

<div class="dataScreen_container">
    <div class="dataScreen" ref="dataScreenRef">
    </div>
</div>

7.1.2 初始化ref

const dataScreenRef = ref<HTMLElement | null>(null);

7.1.3 初始化时为外层盒子加上缩放属性,防止界面刷新时就已经缩放

onMounted(() => {
  // 初始化时为外层盒子加上缩放属性,防止界面刷新时就已经缩放
  if (dataScreenRef.value) {
    dataScreenRef.value.style.transform = `scale(${getScale()}) translate(-50%,-50%)`
    dataScreenRef.value.style.width=`1920px`;
    dataScreenRef.value.style.height=`1080px`;

    // 为浏览器绑定事件
    window.addEventListener("resize",resize)
  }
})

7.1.4 根据浏览器大小推断缩放比例

const getScale = (width = 1920, height = 1080) => {
  let ww = window.innerWidth / width;
  let wh = window.innerWidth / height;
  return ww < wh ? ww : wh;
}

7.1.5 浏览器监听resize事件

const resize=()=>{
  if(dataScreenRef.value){
   dataScreenRef.value.style.transform=`scale(${getScale()})translate(-50%,-50%)`
  }
}

7.1.6 css设计

.dataScreen_container {
    width: 100%;
    height: 100%;
    background: url('./images/bg.png') no-repeat;
    background-repeat: no-repeat;
    background-attachment: fixed;
    background-position: center;
    background-size: 100% 100%;
    background-size: cover;
 .dataScreen{
    position: fixed;
    top: 50%;
    left: 50%;
    z-index: 999;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: all 0.3s;
    transform-origin: left top;
    }
}

2. 常用EChart资源

EChart官网、MCChart、PPChart、isqqw

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/405183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【小程序开发】—— 封装自定义弹窗组件

文章目录&#x1f34b;前言&#xff1a;&#x1f34d;正文1、探讨需求封装popup自定义弹窗组件2、实战开发弹窗组件2.1 子组件内容 popup.vue文件2.2 父组件引用子组件3、效果图预览3.1 不使用具名插槽的原有样式效果3.2 使用具名插槽之后样式效果&#x1f383;专栏分享&#x…

快速掌握Nginx部署前端项目(从Nginx安装配置及部署都非常详细哦!)

前言&#xff1a; 之前在Linux系统中部署了后端项目&#xff0c;今天继续来给大家分享如何部署前端项目。 涉及到了Nginx的简单介绍以及Nginx如何安装及配置并且能够部署前端项目 Nginx是一个轻量级的反向代理web服务器&#xff0c;在当今应用地非常广泛&#xff0c;特别是前…

vue的proxy代理详解

一、proxy常用参数说明 module.exports {publicPath: "/",devServer: {proxy: {"/api": {// 代理名称 凡是使用/api开头的地址都是用此代理target: "http://1.2.3.4:5000/", // 需要代理访问的api地址changeOrigin: true, // 允许跨域请求pa…

[Node.js]如何在IDEA中配置Node.js

【目录】 一、下载Node.js 二、安装Node.js 三、配置Node.js 四、测试Node.js 一、下载Node.js 官网下载链接&#xff1a;Node.js 点击下图红色标注区域&#xff0c;即可下载msi文件 二、安装Node.js 打开msi文件 一路点击“Next”&#xff08;建议自定义文件安装路径&#xf…

vue3的基本使用(超详细)

一、初识vue3 1.vue3简介 2020年9月18日&#xff0c;vue3发布3.0版本&#xff0c;代号大海贼时代来临&#xff0c;One Piece特点&#xff1a; 无需构建步骤&#xff0c;渐进式增强静态的 HTML在任何页面中作为 Web Components 嵌入单页应用 (SPA)全栈 / 服务端渲染 (SSR)Jams…

使用小程序制作一个电子木鱼,功德+1

此文主要通过小程序制作一个敲木鱼的小工具,在心烦意乱的时候缓解一下焦虑。 一、创建小程序二、设计页面三、代码块一、创建小程序 访问微信公众平台,点击账号注册。选择小程序,并在表单填写所需的各项信息进行注册。在开发管理选择开发设置,将AppID及AppSecret复制出来进…

Vue路由导航报错:NavigationDuplicated: Avoided redundant navigation to current location解决方法

点击vue路由跳转&#xff0c;控制台报错&#xff1a;Avoided redundant navigation to current location: “/xxxxxx“解决方法 一、描述问题 在使用this.$router.push跳转页面时候&#xff0c;重复点击菜单引起路由重复报错 比如当前页面显示区是路由组件‘/cats’&#xff…

关于js 中 try catch用法

try…catch语法&#xff0c;简单来说就是用来捕获异常的&#xff0c;我就简述一下我遇到的问题 当使用vuex在actions发请求时&#xff0c;这个接口不仅可以添加购物车数据&#xff0c;同时也可更新产品的数量&#xff0c;我就在更新产品数量的地方出现了问题&#xff0c; 先说说…

HTML生日快乐-生日祝福(烟花+粒子动画)

本人对前端不太了解&#xff0c;对网上代码进行简单修改&#xff0c;完成部署。 粒子动画&#xff1a;https://github.com/kennethcachia/shape-shifter 烟花&#xff1a;网上有许多相关代码&#xff0c;未找到原作者 效果&#xff1a;http://8.130.106.21/HappyBirthday/Happy…

原生HTML+CSS+JS制作自己的导航主页(前端大作业,源码+步骤详解)

文章目录前言一、插入背景二、头部1.导航栏2. 优化导航栏3 时间4. 搜索框三、主体四、底部五、背景泡沫球特效六、note小便签七、全部代码1. index.html2. style.css3. index.js八、总结链接&#xff1a;https://pan.baidu.com/s/1uaRCJXyIrY56NXabau4wjw?pwdLDL6 提取码&…

Vue3详细教程

一.Vue3的介绍 相信大家都已经学习过Vue2了&#xff0c;对Vue有了一定的了解。那么今天我们就一起来学习有关Vue3的知识点。那为什么要学习Vue3呢&#xff0c;主要是他有以下的特点。 &#xff08;1&#xff09;新增组合式api &#xff08;2&#xff09;更加接近原生 &#xf…

JavaWeb web.xml配置详解

参考&#xff1a; XML 教程Java web之web.xml配置详解 Javaweb三大组件是&#xff1a;Servlet&#xff0c;Filter&#xff0c;Listener。 1、Servlet Servlet作为中转处理的容器&#xff0c;连接了客户端和服务器端的信息交互和处理。2、Filter 拦截用户请求&#xff0c;在服…

tomcat下载安装及配置教程

之前选择的版本是tomcat 10.0按照下面流程走了一遍&#xff0c;发现一直是未发现&#xff08;Not Found&#xff09;的状态。 后来&#xff0c;我换成了tomcat 9版本就OK了 下面以tomcat 9.0版本为例讲述其过程 一、下载Tomcat及解压 官网&#xff1a; http://tomcat.apach…

Vue基础知识总结 9:vue webpack打包原理

&#x1f345; 作者简介&#xff1a;哪吒&#xff0c;CSDN2021博客之星亚军&#x1f3c6;、新星计划导师✌、博客专家&#x1f4aa; &#x1f345; 哪吒多年工作总结&#xff1a;Java学习路线总结&#xff0c;搬砖工逆袭Java架构师 &#x1f345; 关注公众号【哪吒编程】&#…

解决npm warn config global `--global`, `--local` are deprecated. use `--location=global` instead.

在安装配置node时&#xff0c;cmd检查node没问题 但是检查npm就出现npm warn config global --global, --local are deprecated. use --locationglobal instead.的报警 问题出现在&#xff0c;以前版本npm的命令一般时XXX -g 但是随着版本更替&#xff0c;这个老方法被弃用了&a…

详细vite创建vue3项目(vue3+vue-router+ts+vite+element-plus+pinia)

vite创建vue3ts项目 为何选择vite&#xff1a; vite 是一个基于 Vue3 单文件组件的非打包开发服务器&#xff0c;它做到了本地快速开发启动&#xff1a; 快速的冷启动&#xff0c;不需要等待打包操作&#xff1b; 即时的热模块更新&#xff0c;替换性能和模块数量的解耦让更新…

uniapp中自定义导航栏

相信很多小伙伴在使用uniapp进行多端开发的时候呢&#xff0c;在面对一些奇葩的业务需求的时候&#xff0c;uniapp给我们提供的默认导航栏已经不能满足我们的业务需求了&#xff0c;这个时候就需要我们自己自定义导航栏使用啦。 当然uniapp也给我们提供了很多的自定义导航栏的…

开箱即用,这些 Vue3 后台管理系统模板绝对让你爽歪歪!

原文链接&#xff1a;Vue3 后台管理系统模板推荐。 之前写了一篇关于 Vue2 的后台管理系统模板的推荐&#xff0c;详情请见 Vue后台管理系统模板推荐。 Vue3 在今年2月份已成为新的默认版本&#xff0c;本文收集了一些 Vue3 的后台管理系统模板&#xff0c;分享给在座的仌 &am…

web前端面试题(必背面试题)

必背面试题-手写题 前端面试&#xff08;手写题&#xff09;_Z_Xshan的博客-CSDN博客 css系列 面试官&#xff1a;说说你对盒子模型的理解 一、是什么 所有元素都可以有像盒子一样的平面空间和外形 一个盒子由四部分组成&#xff1a;context ,padding,margin,border con…

Django web开发(一) - 前端

文章目录前端开发1.快速开发网站2.标签2.1 编码2.2 title2.3 标题2.4 div和span2.5 超链接2.6 图片小结标签的嵌套2.7 列表2.8 表格2.9 input系列2.10 下拉框2.11 多行文本用户注册案例: 用户注册GET 方式POST 方式表单数据提交优化3.CSS样式3.1 快速上手3.2 CSS应用方式1. 在标…