个人主页:Silence Lamb
本章内容:【搭建VUE基础项目】
npm i -g @vue/cli
vue ui
- webpack进行打包的时候会原封不动打包到dist文件夹中
- 作用是生成项目的入口文件
- webpack打包的js,css也会自动注入到该页面中
- 我们浏览器访问项目的时候就会默认打开生成好的index.html
- assets: 存放公用的静态资源
- components: 非路由组件(全局组件),其他组件放在views或者pages文件夹中
- App.vue: 唯一的根组件
- main.js: 程序入口文件,最先执行的文件
{
"生成 vue 模板": {
"prefix": "vue",
"body": [
"<template>",
"<div>div>",
"template>",
"",
"<script>",
"//这里可以导入其他文件(比如:组件,工具 js,第三方插件 js,json文件,图片文件等等)",
"//例如:import 《组件名称》 from '《组件路径》';",
"",
"export default {",
"//import 引入的组件需要注入到对象中才能使用",
"components: {},",
"props: {},",
"data() {",
"//这里存放数据",
"return {",
"",
"};",
"},",
"//计算属性 类似于 data 概念",
"computed: {},",
"//监控 data 中的数据变化",
"watch: {},",
"//方法集合",
"methods: {",
"",
"},",
"//生命周期 - 创建完成(可以访问当前 this 实例)",
"created() {",
"",
"},",
"//生命周期 - 挂载完成(可以访问 DOM 元素)",
"mounted() {",
"",
"},",
"beforeCreate() {}, //生命周期 - 创建之前",
"beforeMount() {}, //生命周期 - 挂载之前",
"beforeUpdate() {}, //生命周期 - 更新之前",
"updated() {}, //生命周期 - 更新之后",
"beforeDestroy() {}, //生命周期 - 销毁之前",
"destroyed() {}, //生命周期 - 销毁完成",
"activated() {}, //如果页面有 keep-alive 缓存功能,这个函数会触发",
"}",
"script>",
"<style lang='scss' scoped>",
"//@import url($3); 引入公共 css 类",
"$4",
"style>"
],
"description": "生成 vue 模板"
}
}
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.10.0",
/*
* @Description : Eslint校验
* @Author : SilenceLamb
* @Version : V1.0.0
*/
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
},
},
extends: ['prettier', 'plugin:vue/vue3-recommended', 'plugin:prettier/recommended'],
rules: {
'no-var': 'error',
'prettier/prettier': ['error'],
// 禁止出现console
'no-console': 'warn',
// 禁用debugger
'no-debugger': 'warn',
// 禁止出现重复的 case 标签
'no-duplicate-case': 'warn',
// 禁止出现空语句块
'no-empty': 'warn',
// 禁止不必要的括号
'no-extra-parens': 'off',
// 禁止对 function 声明重新赋值
'no-func-assign': 'warn',
// 禁止在 return、throw、continue 和 break 语句之后出现不可达代码
'no-unreachable': 'warn',
// 强制所有控制语句使用一致的括号风格
curly: 'warn',
// 要求 switch 语句中有 default 分支
'default-case': 'warn',
// 强制尽可能地使用点号
'dot-notation': 'warn',
// 要求使用 === 和 !==
eqeqeq: 'warn',
// 禁止 if 语句中 return 语句之后有 else 块
'no-else-return': 'warn',
// 禁止出现空函数
'no-empty-function': 'warn',
// 禁用不必要的嵌套块
'no-lone-blocks': 'warn',
// 禁止使用多个空格
'no-multi-spaces': 'warn',
// 禁止多次声明同一变量
'no-redeclare': 'warn',
// 禁止在 return 语句中使用赋值语句
'no-return-assign': 'warn',
// 禁用不必要的 return await
'no-return-await': 'warn',
// 禁止自我赋值
'no-self-assign': 'warn',
// 禁止自身比较
'no-self-compare': 'warn',
// 禁止不必要的 catch 子句
'no-useless-catch': 'warn',
// 禁止多余的 return 语句
'no-useless-return': 'warn',
// 禁止变量声明与外层作用域的变量同名
'no-shadow': 'off',
// 允许delete变量
'no-delete-var': 'off',
// 强制数组方括号中使用一致的空格
'array-bracket-spacing': 'warn',
// 强制在代码块中使用一致的大括号风格
'brace-style': 'warn',
// 强制使用骆驼拼写法命名约定
camelcase: 'warn',
// 强制使用一致的缩进
indent: 'off',
// 强制在 JSX 属性中一致地使用双引号或单引号
// 'jsx-quotes': 'warn',
// 强制可嵌套的块的最大深度4
'max-depth': 'warn',
// 强制最大行数 300
// "max-lines": ["warn", { "max": 1200 }],
// 强制函数最大代码行数 50
// 'max-lines-per-function': ['warn', { max: 70 }],
// 强制函数块最多允许的的语句数量20
'max-statements': ['warn', 100],
// 强制回调函数最大嵌套深度
'max-nested-callbacks': ['warn', 3],
// 强制函数定义中最多允许的参数数量
'max-params': ['warn', 3],
// 强制每一行中所允许的最大语句数量
'max-statements-per-line': ['warn', { max: 1 }],
// 要求方法链中每个调用都有一个换行符
'newline-per-chained-call': ['warn', { ignoreChainWithDepth: 3 }],
// 禁止 if 作为唯一的语句出现在 else 语句中
'no-lonely-if': 'warn',
// 禁止空格和 tab 的混合缩进
'no-mixed-spaces-and-tabs': 'warn',
// 禁止出现多行空行
'no-multiple-empty-lines': 'warn',
// 禁止出现;
semi: ['warn', 'never'],
// 强制在块之前使用一致的空格
'space-before-blocks': 'warn',
// 强制在 function的左括号之前使用一致的空格
// 'space-before-function-paren': ['warn', 'never'],
// 强制在圆括号内使用一致的空格
'space-in-parens': 'warn',
// 要求操作符周围有空格
'space-infix-ops': 'warn',
// 强制在一元操作符前后使用一致的空格
'space-unary-ops': 'warn',
// 强制在注释中 // 或 /* 使用一致的空格
// "spaced-comment": "warn",
// 强制在 switch 的冒号左右有空格
'switch-colon-spacing': 'warn',
// 强制箭头函数的箭头前后使用一致的空格
'arrow-spacing': 'warn',
'prefer-const': 'warn',
'prefer-rest-params': 'warn',
'no-useless-escape': 'warn',
'no-irregular-whitespace': 'warn',
'no-prototype-builtins': 'warn',
'no-fallthrough': 'warn',
'no-extra-boolean-cast': 'warn',
'no-case-declarations': 'warn',
'no-async-promise-executor': 'warn',
},
}
Tips:echo {}> .eslintignore
# eslint 忽略检查 (根据项目需要自行添加)
node_modules
dist
Tips:安装 prettier
# 安装 prettier
yarn add prettier -D
# 安装插件 eslint-config-prettier
yarn add eslint-config-prettier -D
Tips:echo {} > .prettierrc.js
/*
* @Description :
* @Author : SilenceLamb
* @Version : V1.0.0
*/
module.exports = {
printWidth: 150,
// 指定每个缩进级别的空格数
tabWidth: 4,
// 使用制表符而不是空格缩进行
useTabs: false,
// 在语句末尾打印分号
semi: false,
// 使用单引号而不是双引号
singleQuote: true,
// 更改引用对象属性的时间 可选值""
quoteProps: 'as-needed',
// 在JSX中使用单引号而不是双引号
jsxSingleQuote: false,
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none
trailingComma: 'es5',
// 在对象文字中的括号之间打印空格
bracketSpacing: true,
// 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
arrowParens: 'always',
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
rangeStart: 0,
rangeEnd: Infinity,
// 指定要使用的解析器,不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准 always\never\preserve
proseWrap: 'preserve',
// 指定HTML文件的全局空格敏感度 css\strict\ignore
htmlWhitespaceSensitivity: 'css',
// Vue文件脚本和样式标签缩进
vueIndentScriptAndStyle: false,
// 换行符使用 lf 结尾是 可选值""
endOfLine: 'lf',
}
Tips:echo {}> .prettierignore
# 忽略格式化文件 (根据项目需要自行添加)
node_modules
dist
Tips:package.json
"scripts": {
"lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx",
"prettier": "prettier --write ."
},
# 页面标题
VUE_APP_TITLE=开发环境
# 开发环境配置
VUE_MODE_ENV='development'
# 开发环境
VUE_APP_BASE_API='/dev-api'
# 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES=true
# 页面标题
VUE_APP_TITLE=线上环境
# 线上环境
VUE_MODE_ENV='production'
# 管理系统/生产环境
VUE_APP_BASE_API='/prod-api'
# 页面标题
VUE_APP_TITLE=测试环境
# 测试环境
VUE_MODE_ENV='staging'
# 测试环境
VUE_APP_BASE_API='/stage-api'
/**
* @Description : Vue环境工具
* @Author : SilenceLamb
* @Version : V1.0.0
*/
export default {
/**
* @Description : 是否是开发环境
* @param mode 模式
*/
isDev(mode) {
return mode === 'development'
},
/**
* @Description : 是否是线上环境
* @param mode 模式
*/
isProd(mode) {
return mode === 'production'
},
/**
* @Description : 是否是构建
* @param common 启动环境
*/
isBuild(common) {
return common === 'build'
},
/**
* @Description : 是否是启动服务器
* @param common 启动环境
*/
isServer(common) {
return common === 'server'
},
}
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"#/*": ["config/*"],
"@api/*": ["src/api/*"],
"@assets/*": ["src/assets/*"],
"@components/*": ["src/components/*"],
"@store/*": ["src/store/*"],
"@utils/*": ["src/utils/*"],
"@views/*": ["src/views/*"],
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
/*
* @Description : 配置别名
* @Author : SilenceLamb
* @Version : V1.0.0
*/
import { resolve } from 'path';
function pathResolve(dir) {
return resolve(process.cwd(), '.', dir);
}
export default {
// 导入时想要省略的扩展名列表
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
fallback: { path: require.resolve('path-browserify') },
alias: {
'@': pathResolve('src'),
'@api': pathResolve('src/api'),
'@assets': pathResolve('src/assets'),
'@components': pathResolve('src/components'),
'@store': pathResolve('src/store'),
'@utils': pathResolve('src/utils'),
'@views': pathResolve('src/views'),
'#': pathResolve('config'),
},
};
/**
* @Description : Vue相关配置
* @Author : SilenceLamb
* @Version : V1.0.0
*/
import resolve from './resolve/index'
export default function VueConfig(isBuild, VITE_APP_BASE_API) {
return {
['resolve']: resolve,
}
}
const VueConfig = require('#/index')
const WebpackName = process.env.VUE_APP_TITLE // 网页标题
module.exports = {
configureWebpack: {
resolve: VueConfig['resolve'],
},
}
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
const port = 80
const url = "http://localhost:88"
const name = process.env.VUE_APP_TITLE || "Silence-VUE"// 网页标题
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
// 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
outputDir: 'dist',
// 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
assetsDir: 'static',
// 是否开启eslint保存检测,有效值:ture | false | 'error'
lintOnSave: process.env.NODE_ENV === 'development',
// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
productionSourceMap: false,
// 跨域相关配置
devServer: {
host: '127.0.0.1',
port: port,
proxy: {
[process.env.VUE_APP_BASE_API]: {
target: url,
open: true,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
}
}
Tips:在src文件下创建utils,文件夹下新建index.ts文件夹:用于扫描ts文件
/**
* 动态引入方法
*/
export default {
install(app) {
const methods = require.context('./modules/', true, /\.js$/)
// 加载在某个目录中动态注册的所有全局方法
methods.keys().forEach((filePath) => {
const method = methods(filePath)
const fileName = getFileName(filePath)
//export default{}类型
if (method.default !== undefined && method.default !== null) {
// 将该方法注册为 $fileName 的全局方法
Object.keys(method).forEach(() => {
app.config.globalProperties['$' + fileName] = method.default
})
} else {
// 将该方法注册为 $fileName 的全局方法
app.config.globalProperties['$' + fileName] = method
}
})
},
}
/**
* @Description 获取最后一个文件夹
* @param {string} filePath
* @returns {string} fileName 子文件夹名
*/
export function getLastFileName(filePath) {
return filePath.substring(0, filePath.lastIndexOf('/')).split('/').pop()
}
Tips:main.ts全局引入
import { createApp } from 'vue'
import App from './App.vue'
import utils from '/@/utils';
const app = createApp(App)
app.use(utils,true)
app.use(router)
app.mount('#app')
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
/**
* @Description : 用于消息提示、确认消息和提交内容
* @Version : V1.0.0
* @Author : SilenceLamb
*/
let loadingInstance
export default {
// 消息提示
message(content, type) {
ElMessage[type](content)
},
// 弹出提示 -(弹框)
alert(title, content, type) {
ElMessageBox.alert(content, '系统提示', { type: type }).then()
},
// 通知提示 -(通知)
notify(content, type) {
ElNotification[type](content)
},
// 确认窗体 -(窗体)
confirm(title, content, type) {
return ElMessageBox.confirm(content, content, {
title: title,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: type,
})
},
// 提交内容 -(窗体)
prompt(title, content, type) {
return ElMessageBox.prompt(content, content, {
title: title,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: type,
})
},
// 打开遮罩层 -(遮罩)
loading(content) {
loadingInstance = ElLoading.service({
lock: true,
text: content,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)',
})
},
// 关闭遮罩层 -(遮罩)
closeLoading() {
loadingInstance.close()
},
}
/**
* @Description : 参数处理工具
* @Author : SilenceLamb
* @Version : V1.0.0
*/
export default {
/**
* @Description :请求参数处理
* @param params
* @return {string}
*/
tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName]
const part = `${encodeURIComponent(propName)}=`
if (value !== null && value !== '' && typeof value !== 'undefined') {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
// eslint-disable-next-line max-depth
if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
const params = `${propName}[${key}]`
const subPart = `${encodeURIComponent(params)}=`
result += `${subPart + encodeURIComponent(value[key])}&`
}
}
} else {
result += `${part + encodeURIComponent(value)}&`
}
}
}
return result
},
}
<template >
<div class="app-container">
<a-button @click="message">message</a-button>
</div>
</template>
<script lang="ts">
export default{
methods:{
message(){
this.$model.message('消息提示', 'success');
}
}
}
</script>
/*
* @Description : 项目设置
* @Author : SilenceLamb
* @Version : V1.0.0
*/
export default {
/**
* 前端端口
*/
vuePort: 80,
/**
* 后端接口
*/
adminUrl: 'http://localhost:88',
};
/*
* @Description : 通用设置
* @Author : SilenceLamb
* @Version : V1.0.0
*/
export default {
/**
* @Description : 在npm run build 或 yarn build 时 ,生成文件的目录名称
* @param {String} param -(默认dist)
*/
outputDir: 'dist',
/**
* @Description : 用于放置生成的静态资源 (js、css、img、fonts) 的
* @param {String} param -(默认static)
*/
assetsDir: 'static',
};
/*
* @Description : 整合常量
* @Author : SilenceLamb
* @Version : V1.0.0
*/
import silence from './modules/silence';
import common from './modules/common';
const constants = {
['silence']: silence,
['common']: common,
};
export default constants;
/*
* @Description : 代理目标列表
* @Author : SilenceLamb
* @Version : V1.0.0
*/
const constant = require('../../constant/index')
module.exports = function () {
return {
[process.env.VUE_APP_BASE_API]: {
target: constant['silence'].adminUrl,
changeOrigin: true,
pathRewrite: { [`^${process.env.VUE_APP_BASE_API}`]: '' },
},
}
}
不配置pathRewrite 请求就被转发到 XX.XX.XX.XX:80 并把相应uri带上
比如:localhost:80/api/xxx 会被转发到XX.XX.XX.XX:80/api/xxx
跨域配置:config/devServer/index.js
/**
* @Description : 跨域相关配置
* @Author : SilenceLamb
* @Version : V1.0.0
*/
const constant = require('../constant/index')
const getProxy = require('./modules')
module.exports = {
host: '0.0.0.0',
open: true,
port: constant['silence'].vuePort,
proxy: getProxy(),
}
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
const { VueConfig } = require('./config/index')
const constant = require('./config/constant/index')
const vueConfig = VueConfig(process.env.VUE_MODE_ENV, process.env.VUE_APP_BASE_API)
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '/' : '/',
// 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
outputDir: constant['common'].outputDir,
// 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
assetsDir: constant['common'].assetsDir,
// 是否开启eslint保存检测,有效值:ture | false | 'error'
lintOnSave: constant['common'].lintOnSave,
// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
productionSourceMap: constant['common'].productionSourceMap,
devServer: vueConfig['server'],
css: {
loaderOptions: { sass: { sassOptions: { outputStyle: 'expanded' } } },
},
configureWebpack: {
resolve: vueConfig['resolve'],
plugins: vueConfig['plugins'],
}
}
/**
* @Description : 依赖自动按需导入
* @Author : SilenceLamb
* @Version : V1.0.0
*/
const ImportDeps = require('unplugin-auto-import/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
exports.AutoImportDeps = function () {
return ImportDeps({
resolvers: [ElementPlusResolver()],
imports: ['vue', 'vue-router', 'vuex', '@vueuse/head'],
// 可以选择auto-import.d.ts生成的位置,建议设置为 'src/auto-import.d.ts
dts: 'src/auto/auto-import.d.ts',
})
}
/**
* @Description : 组件按需自动导入
* @Author : SilenceLamb
* @Version : V1.0.0
*/
const ImportComp = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
exports.ImportComponents = function () {
/* 组件按需自动导入*/
return ImportComp({
//引入自己的组件
dirs: ['src/modules/components'],
//引入ElementPlus官方组件
resolvers: [ElementPlusResolver()],
dts: 'src/auto/components.d.ts',
// 组件的有效文件扩展名
extensions: ['vue'],
// 允许子目录作为组件的命名空间前缀
directoryAsNamespace: false,
// 用于转换目标的筛选器
include: [/\.vue$/, /\.vue\?vue/],
exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/],
// 自定义规则
libs: [
{
libraryName: 'element-plus',
esModule: true,
resolveStyle: (name) => {
return `element-plus/lib/theme-chalk/${name}.scss`
},
},
],
})
}
import { createStore } from 'vuex'
import getters from './getter'
const modulesFiles = require.context('./modules', true, /\.js$/)
/*
* 如果你需要 `import app from './modules/app'`
* 它将自动要求模块文件中的所有 Vuex 模块
*/
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './index.scss' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
export default createStore({
modules,
getters,
})
import defaultSettings from '@/config/settings'
const { title, logo, fixedHeader, sidebarLogo } = defaultSettings
/**
* 可能是在一个 Vuex store 中作为状态的初始值进行定义
* @type {lang} 语言
* @type {title} 网站标题
* @type {logo} 网站logo
* @type {fixedHeader} 头部固定
* @type {sidebarLogo} 是否显示Logo
*/
const state = {
lang: 'zh',
title,
logo,
fixedHeader,
sidebarLogo,
}
/**
* 用于存储更改应用程序状态的函数
* 这些函数通常在 actions 中被调用
*/
const mutations = {
/**
* 更改设置
* @param state state对象
* @param key state属性
* @param value 给定的值
* @constructor
*/
CHANGE_SETTING: (state, { key, value }) => {
if (Object.hasOwnProperty.call(state, key)) {
state[key] = value
}
},
}
/**
* 用于存储异步操作的函数
* 通常用于执行一些异步操作
*/
const actions = {
/**
* 更改设置
* @param commit
* @param data { key:'', value:''}
*/
changeSetting({ commit }, data) {
commit('CHANGE_SETTING', data)
},
}
export default {
namespaced: true,
state,
mutations,
actions,
}
this.$store.state.count
mapState({
// 箭头函数可使代码更简练
count: state => state.count
})
this.$store.getters.doneTodosCount
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
store.commit('increment', {
amount: 10
})
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
/**
* @Description : 动态加载模块
* @Author : SilenceLamb
* @Version : V1.0.0
*/
import { createStore } from 'vuex'
import createPersistedState from 'vuex-persistedstate'
import Token from '@/utils/modules/token'
const modulesFiles = require.context('./modules', true, /\.js$/)
/*
* 它将自动要求模块文件中的所有 Vuex 模块
*/
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './mixin.scss' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
export default createStore({
modules,
// 持久化操作 当修改state的内容自动持久化存储
plugins: [
createPersistedState({
storage: window.localStorage,
key: 'State',
setItem: function (key, value) {
//将token信息存储到Cookies中
if (key === 'token') {
Token.setToken(value)
}
},
}),
],
})
yarn add vue-router@4
Tips
:src文件夹下新建router文件夹,router文件夹下新建static.ts
/**
* @Description : 路由配置
* @Author : SilenceLamb
* @Version : V1.0.0
*/
import { RouteRecordRaw } from 'vue-router';
export const constantRoutes: RouteRecordRaw [] = [
{
path: '/',
name: 'Index',
component: () => import('/@/views/index/index.vue'),
},
];
export default {constantRoutes};
Tips
:在main.ts中,引入router并注册
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
const app = createApp(App)
app.use(router)
app.mount('#app')
Tips
:在App.vue中设置路由展现出口
<template>
<router-view />
template>
Tips
:src文件夹下新建router文件夹,router文件夹下新建index.ts
/**
* @Description : 路由守卫
* @Author : SilenceLamb
* @Version : V1.0.0
*/
import {createRouter, createWebHistory} from 'vue-router';
import {constantRoutes} from './static';
import {useAdminInfoState} from '/@/store/modules/adminInfo';
//引入进度条样式
import 'nprogress/nprogress.css';
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,
});
/**
* @Description 全局前置守卫
* 当一个导航触发时,全局前置守卫按照创建顺序调用
* @param to: 即将要进入的目标 用一种标准化的方式
* @param from: 当前导航正要离开的路由 用一种标准化的方式
* @return {boolean} false: 取消当前的导航 |true 通过一个路由地址跳转到一个不同的地址
*/
/* 添加白名单*/
const whiteList = ['/login']; // no redirect whitelist
// @ts-ignore
router.beforeEach((to, from, next) => {
console.log(useAdminInfoState().getToken())
if (useAdminInfoState().getToken()) {
/* has token*/
if (to.path === '/login') {
next({path: '/'})
} else if (useAdminInfoState().userName === null) {
//获取用户信息
} else {
next()
}
} else {
// 没有token
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
}
}
})
/**
* @Description 全局后置钩子
* 钩子不会接受 next 函数也不会改变导航本身
*/
router.afterEach(() => {
});
export default router;
setup
我们不能再直接访问 this. r o u t e r 或 t h i s . router 或 this. router或this.routeimport { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
function pushWithQuery(query) {
router.push({
name: 'search',
query: {
...route.query,
},
})
}
},
}
<script>
export default {
name: 'RedirectVue',
created() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({
path: `/${path}`,
query,
})
},
render(h) {
return h() // avoid warning message
},
}
</script>
Tips
:组件内路由守卫
Tips
你可以在路由组件内直接定义路由导航守卫(传递给路由配置的)
beforeRouteEnter(to, from) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !
// 因为当守卫执行时,组件实例还没被创建!
},
beforeRouteUpdate(to, from) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
// 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
},
beforeRouteLeave(to, from) {
// 在导航离开渲染该组件的对应路由时调用
// 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
},
import { useRouter,useRoute } from 'vue-router';
const go=()=>{
const Router=useRouter()
const Route=useRoute()
Router.push({
name:'/login',
query:{
id:'123456'
}
})
}
<router-view v-slot="{ Component }">
<transition name="fade">
<component :is="Component" />
</transition>
</router-view>
const routes = [
{
path: '/custom-transition',
component: PanelLeft,
meta: { transition: 'slide-left' },
},
{
path: '/other-transition',
component: PanelRight,
meta: { transition: 'slide-right' },
},
]
<router-view v-slot="{ Component, route }">
<!-- 使用任何自定义过渡和回退到 `fade` -->
<transition :name="route.meta.transition || 'fade'">
<component :is="Component" />
</transition>
</router-view>
✨
Tips
:简单Layout布局:src/layout/index.vue
<template>
<div class="app-wrapper">
<app-main />
<back-to-top />
</div>
</template>
<style lang="scss" scoped>
@import 'src/styles/index';
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
}
</style>
✨
Tips
:主要区域组件:src/layout/components/appMain/index.vue
<template>
<section class="app-main">
<router-view v-slot="{ Component }">
<transition name="fade-transform" mode="out-in">
<component :is="Component" :key="$route.path" />
</transition>
</router-view>
</section>
</template>
<script>
export default { name: 'AppMain' };
</script>
<style lang="scss" scoped>
.app-main {
/*50 = navbar */
min-height: 100vh;
width: 100%;
position: relative;
overflow: hidden;
}
</style>
<style lang="scss">
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
.fixed-header {
padding-right: 15px;
}
}
</style>
✨
Tips
:路由引入Layout布局:
import Layout from '/@/layout/index.vue';
{
path: '/',
redirect: '/index',
component: Layout,
children: [{ name: 'index', path: '/index', component: () => import('/@/views/index/index.vue') }],
},
npm install axios --save
const app = createApp(App)
app.config.globalProperties.$axios = axios
getMenu() {
this.$axios({
url: "",
method: "get",
params: {}
}).then(res => {
console.log(res)
})
}
import axios from "axios";
//1、对axios二次封装
const requests = axios.create({
//基础路径,requests发出的请求在端口号后面会跟改baseURl
baseURL:'/api',
timeout: 5000,
})
//2、配置请求拦截器
requests.interceptors.request.use(config => {
//config内主要是对请求头Header配置
//比如添加token
return config;
})
//3、配置相应拦截器
requests.interceptors.response.use((res) => {
//成功的回调函数
return res.data;
},(error) => {
//失败的回调函数
console.log("响应失败"+error)
return Promise.reject(new Error('fail'))
})
//4、对外暴露
export default requests;
//当前模块,API进行统一管理,即对请求接口统一管理
import requests from "@/utils/request";
//获取验证码
export function getCodeImage() {
return requests({
url: '/captchaImage',
method: 'get'
})
}
import {reqCateGoryList} from './api'
//发起请求
reqCateGoryList();
import Cookies from 'js-cookie'
const app = createApp(App)
app.use(Cookies)
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
//2、配置请求拦截器
requests.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === true
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
//添加token
if (isToken) {
//config内主要是对请求头Header配置
config.headers['Authorization'] = 'Bearer ' +token.getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
return config
}, error => {
Promise.reject(error).then(r => r)
})
//3、配置相应拦截器
requests.interceptors.response.use((res) => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
} else if (code === 500) {
ElMessage({
message: msg || 'Error',
type: 'error',
duration: 5 * 1000
})
return Promise.reject(new Error(msg))
} else {
//成功的回调函数
return res.data;
}
}, (error) => {
//失败的回调函数
let { message } = error;
if (message === "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
ElMessage({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
})
//当前模块,API进行统一管理,即对请求接口统一管理
import requests from "@/utils/requests"
//获取验证码
export function getCodeImage() {
return requests({
url: '/captchaImage',
method: 'get',
headers: {
isToken: false
},
})
}
️如果文章对你有帮助【关注点赞❤️收藏⭐】