本项目将帮助你搭建 vue3 面向企业级项目开发环境,用于提升前端开发速度及效率。已经做成脚手架上传至 npm,可自行下载qian-cli
脚手架初始化项目
qian-cli 仓库地址: qian-cli
安装 qian-cli 脚手架
npm install qian-cli -g
使用脚手架初始化项目
qian-cli init v3-test
名字 | 说明 |
---|---|
Vue3 | 构建用户界面的渐进式框架 |
Tsx Typescript | JavaScript 类型提示、语法扩展 |
Prettierrc | 格式化代码 |
Vite2 | 前端开发与构建工具 |
Scss | css 扩展框架 |
Vue-router4 | 路由 |
Axios | HTTP 请求 |
Pinia | 全局状态管理 |
# 2022/8/27
pinia ^2.0.18 → ^2.0.21
sass ^1.54.4 → ^1.54.5
vue-router ^4.1.3 → ^4.1.5
@types/node ^18.7.3 → ^18.7.13
typescript ^4.7.4 → ^4.8.2
unplugin-auto-import ^0.11.1 → ^0.11.2
vite ^3.0.7 → ^3.0.9
以下步骤均为仓库第一次创建的时候使用的步骤,后续仓库变化下方步骤不会变化
npm init vite@latest
npm i @vitejs/plugin-vue-jsx --dev
vite.config.ts 文件添加
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Jsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
plugins: [vue(), Jsx()]
})
npm i eslint-config-prettier --dev
.prettierrc 文件添加
{
"useTabs": false,
"tabWidth": 2,
"printWidth": 300,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"semi": false
}
vite.config.ts 文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Jsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path' // 如果报错则安装一下 npm i @types/node vscode报错需要关闭软件重启
export default defineConfig({
plugins: [vue(), Jsx()],
server: {
open: false, //自动打开浏览器
base: './ ', //生产环境路径
proxy: {
'^/api': {
target: 'http://localhost', // 后端服务实际地址
changeOrigin: true, //开启代理
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
resolve: {
alias: {
'@': resolve(__dirname, './src') // 例: @/components/HelloWorld.vue
}
}
})
tsconfig.json 文件
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"baseUrl": ".", // 配置一
"paths": {
// 配置二
"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
跟项目下新建.env.development
文件和.env.production
文件
# .env.development
NODE_ENV = development
VITE_APP_BASEURL = ''
VITE_APP_BASE_API = /api
# .env.production
NODE_ENV = production
VITE_APP_BASEURL = ''
VITE_APP_BASE_API = www.xuanxiaoqian.com
// 使用 import.meta.env.VITE_APP_BASE_API
Ts 联想提示,在src
文件下新增dev.d.ts
文件(如有则修改就行)
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
interface ImportMetaEnv extends Readonly<Record<string, string>> {
// 更多环境变量...
readonly VITE_APP_BASE_API: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
配置 package.json
, 打包区分开发环境和生产环境
"build:dev": "vite build --mode development",
"build:pro": "vite build --mode production",
npm i sass --dev
Vite 设置全局 scss 变量
在style
文件下新增variable.scss
文件
// src/style/variable.scss
$vua-pre: vua-;
$white-color: #fff;
$border-color: #dcdee2;
$dash-border-color: #f0f0f0;
$primary-color: #2d8cf0;
$assist-color: #bae7ff;
$disable-color: #c5c8ce;
$text-color: #333;
$error-color: #e44233;
$bg-prev-color: #f5f7fa;
$font-size-base: 14px;
$font-size-mid: $font-size-base + 2;
$font-size-large: $font-size-base + 4;
$font-size-huge: $font-size-base + 10;
新增vite.config.ts
文件内容
export default defineConfig({
plugins: ...
server: ...
resolve: ...
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "/@/style/variable.scss";` // scss全局变量
}
}
}
})
使用全局变量
.home {
color: $assist-color;
}
npm i vue-router@4
src
文件下新增router
文件夹和views
文件夹
// router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('/@/views/Home/Home.vue'), // 需要创建这个文件,不然启动会报错
meta: {
title: '404not found'
}
},
{
path: '/',
name: 'Home',
component: () => import('/@/views/Home/Home.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes: routes,
strict: true,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
}
})
// 路由白名单
const whiteList = ['/', '/index', '/home', '/login', '/register']
router.beforeEach((to, from, next) => {
// if (whiteList.indexOf(to.path) === -1) { // 如果有需要就打开
// next({ path: '/' })
// } else {
// next()
// }
})
router.afterEach(() => {})
export default router
修改入口文件main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
const app = createApp(App)
app.use(router)
app.mount('#app')
修改主页面App.vue
<script setup lang="ts"></script>
<template>
<router-view></router-view>
</template>
<style></style>
npm i axios
src
文件下新增utils
文件夹并在下面再建一个http
文件夹 src/utils/http
// src/utils/http/defaultConfig.ts
import axios, { AxiosRequestConfig } from 'axios'
export const defaultConfig: AxiosRequestConfig = {
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 1000,
headers: {
token: localStorage.getItem('token') ?? '',
'Content-Type': 'application/json;charset=UTF-8'
}
}
export default defaultConfig
// src/utils/http/interceptors.ts
import Axios, { AxiosInstance } from 'axios'
import defaultConfig from './defaultConfig'
export class Interceptors {
public instance: AxiosInstance
constructor() {
this.instance = Axios.create(defaultConfig)
this.init()
}
init() {
// 数据请求之前
this.instance.interceptors.request.use(
(config: any) => {
// console.log("请求了");
return config
},
(err) => {
console.log(err)
}
)
// 数据返回之前
this.instance.interceptors.response.use(
(response) => {
// console.log("响应了");
return Promise.resolve(response)
},
(err) => {
console.log(err)
}
)
}
getInterceptors() {
return this.instance
}
}
const instance = new Interceptors().getInterceptors()
export default instance
// src/utils/http/index.ts
import { AxiosInstance } from 'axios'
import instance from './interceptors'
const http: AxiosInstance = instance
export default http
src
文件下新增api
文件夹并在下面再建一个login
文件夹 src/api/login
// src/api/login/types.ts
export interface LoginParams {
userName: string
passWord: string | number
}
export interface LoginApi {
login: (params: LoginParams) => Promise<any>
}
// src/api/login/login.ts
import http from '/@/utils/http'
import * as T from './types'
const loginApi: T.LoginApi = {
login(params) {
return http.post('/login', params)
}
}
export default loginApi
使用
import loginApi from '/@/api/login/login'
loginApi.login({ userName: 'root', passWord: '123456' }).then((res) => {
console.log(res.data)
})
npm i pinia@next
src
文件下新增store
文件夹
// src/store/main.ts
import { defineStore } from 'pinia'
export const useMainStore = defineStore({
id: 'mian',
state: () => ({
name: '超级管理员'
})
})
修改入口文件main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import { createPinia } from 'pinia'
const pinia = createPinia()
const app = createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')
使用
<script setup lang="ts">
import { useMainStore } from '/@/store/main'
const mainStore = useMainStore()
</script>
<template>
<div>{{ mainStore.name }}</div>
</template>
<style></style>
src
文件下新增style
文件夹
// src/style/reset.css
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
修改入口文件main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import { createPinia } from 'pinia'
// reset.css
import './style/reset.css'
const pinia = createPinia()
const app = createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')
src
文件下新增hooks
文件夹
// src/hooks/usePoint.ts
import { reactive, onMounted, onBeforeUnmount } from 'vue'
export default function () {
//展示的数据 可以通过App.vue 界面去隐藏
let point = reactive({
x: 0,
y: 0
})
//获取鼠标点击事件
function savePonint(event: MouseEvent) {
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX, event.pageY)
}
//现实之后调用 挂载完毕
onMounted(() => {
window.addEventListener('click', savePonint)
})
//在隐藏之前调用 卸载之前
onBeforeUnmount(() => {
window.removeEventListener('click', savePonint)
})
return point
}
使用
import usePoint from '/@/hooks/usePoint'
let point = usePoint()
console.log(point)
npm i unplugin-auto-import -D
新增vite.config.ts
文件内容
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({
imports: ['vue', 'vue-router'],
// 可以选择auto-import.d.ts生成的位置,使用ts建议设置为'src/auto-import.d.ts'
dts: 'src/auto-import.d.ts'
})
]
})
重启项目src
目录下会新增auto-import.d.ts
文件,我们就可以愉快的自动导入啦
以下的内容均不在仓库中,只是为了方便有需要的人能快速使用
npm i vite-plugin-vue-setup-extend -D
配置vite.config.ts
import { defineConfig } from 'vite'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
plugins: [VueSetupExtend()]
})
使用
<script lang="ts" setup name="demo"></script>
npm install element-plus --save
修改入口文件main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
使用
<el-button type="primary">看见我没问题就成功了!!!</el-button>
npm install --save nprogress
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
Nprogress.configure({
// 动画方式
easing: 'ease',
// 递增进度条的速度
speed: 500,
// 是否显示加载ico
showSpinner: false,
// 自动递增间隔
trickleSpeed: 200,
// 初始化时的最小百分比
minimum: 0.3
})
export default Nprogress
使用
import NProgress from '../utils/nprogress'
NProgress.start() // 控制开始加载
NProgress.done() // 控制结束加载
npm install js-cookie --save
使用
import Cookies from 'js-cookie'
// Create a cookie, valid across the entire site:
Cookies.set('name', 'value')
// 创建一个从现在起7天内过期的cookie,在整个站点有效:
Cookies.set('name', 'value', { expires: 7 })
// Create an expiring cookie, valid to the path of the current page:
Cookies.set('name', 'value', { expires: 7, path: '' })
// name value
Cookies.set('TokenKey', token, { expires: 1000, path: '/', domain: 'xx.com' })
//不写过期时间,默认为1天过期
this.$cookies.set('user_session', '25j_7Sl6xDq2Kc3ym0fmrSSk2xV2XkUkX')
this.$cookies.set('token', 'GH1.1.1689020474.1484362313', '60s') // 60秒后过去
this.$cookies.set('token', 'GH1.1.1689020474.1484362313', '30MIN') // 30分钟后过去
// Read cookie:
Cookies.get('name') // => 'value'
Cookies.get('nothing') // => undefined
// Read all visible cookies:
Cookies.get() // => { name: 'value' }
// Delete cookie:
Cookies.remove('name')
// Delete a cookie valid to the path of the current page:
Cookies.set('name', 'value', { path: '' })
Cookies.remove('name') // fail!
Cookies.remove('name', { path: '' }) // removed!
vscode 插件 CSScomb,用于保存自动修改 css 属性顺序
下载CSScomb
插件
在项目根目录添加新文件.csscomb.json
{
"exclude": [".git/**", "node_modules/**", "bower_components/**"],
"always-semicolon": true,
"block-indent": " ",
"color-case": "lower",
"color-shorthand": true,
"element-case": "lower",
"eof-newline": true,
"leading-zero": false,
"quotes": "single",
"remove-empty-rulesets": true,
"space-after-colon": " ",
"lines-between-rulesets": 1,
"space-after-combinator": " ",
"space-after-opening-brace": "\n",
"space-after-selector-delimiter": "\n",
"space-before-closing-brace": "\n",
"space-before-colon": "",
"space-before-combinator": " ",
"space-before-opening-brace": " ",
"space-before-selector-delimiter": "",
"space-between-declarations": "\n",
"strip-spaces": true,
"unitless-zero": true,
"vendor-prefix-align": true,
"sort-order": [
[
"font",
"font-family",
"font-size",
"font-weight",
"font-style",
"font-variant",
"font-size-adjust",
"font-stretch",
"font-effect",
"font-emphasize",
"font-emphasize-position",
"font-emphasize-style",
"font-smooth",
"line-height",
"position",
"z-index",
"top",
"right",
"bottom",
"left",
"display",
"visibility",
"float",
"clear",
"overflow",
"overflow-x",
"overflow-y",
"-ms-overflow-x",
"-ms-overflow-y",
"clip",
"zoom",
"-webkit-align-content",
"-ms-flex-line-pack",
"align-content",
"-webkit-box-align",
"-moz-box-align",
"-webkit-align-items",
"align-items",
"-ms-flex-align",
"-webkit-align-self",
"-ms-flex-item-align",
"-ms-grid-row-align",
"align-self",
"-webkit-box-flex",
"-webkit-flex",
"-moz-box-flex",
"-ms-flex",
"flex",
"-webkit-flex-flow",
"-ms-flex-flow",
"flex-flow",
"-webkit-flex-basis",
"-ms-flex-preferred-size",
"flex-basis",
"-webkit-box-orient",
"-webkit-box-direction",
"-webkit-flex-direction",
"-moz-box-orient",
"-moz-box-direction",
"-ms-flex-direction",
"flex-direction",
"-webkit-flex-grow",
"-ms-flex-positive",
"flex-grow",
"-webkit-flex-shrink",
"-ms-flex-negative",
"flex-shrink",
"-webkit-flex-wrap",
"-ms-flex-wrap",
"flex-wrap",
"-webkit-box-pack",
"-moz-box-pack",
"-ms-flex-pack",
"-webkit-justify-content",
"justify-content",
"-webkit-box-ordinal-group",
"-webkit-order",
"-moz-box-ordinal-group",
"-ms-flex-order",
"order",
"-webkit-box-sizing",
"-moz-box-sizing",
"box-sizing",
"width",
"min-width",
"max-width",
"height",
"min-height",
"max-height",
"margin",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"padding",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left",
"table-layout",
"empty-cells",
"caption-side",
"border-spacing",
"border-collapse",
"list-style",
"list-style-position",
"list-style-type",
"list-style-image",
"content",
"quotes",
"counter-reset",
"counter-increment",
"resize",
"cursor",
"-webkit-user-select",
"-moz-user-select",
"-ms-user-select",
"user-select",
"nav-index",
"nav-up",
"nav-right",
"nav-down",
"nav-left",
"-webkit-transition",
"-moz-transition",
"-ms-transition",
"-o-transition",
"transition",
"-webkit-transition-delay",
"-moz-transition-delay",
"-ms-transition-delay",
"-o-transition-delay",
"transition-delay",
"-webkit-transition-timing-function",
"-moz-transition-timing-function",
"-ms-transition-timing-function",
"-o-transition-timing-function",
"transition-timing-function",
"-webkit-transition-duration",
"-moz-transition-duration",
"-ms-transition-duration",
"-o-transition-duration",
"transition-duration",
"-webkit-transition-property",
"-moz-transition-property",
"-ms-transition-property",
"-o-transition-property",
"transition-property",
"-webkit-transform",
"-moz-transform",
"-ms-transform",
"-o-transform",
"transform",
"-webkit-transform-origin",
"-moz-transform-origin",
"-ms-transform-origin",
"-o-transform-origin",
"transform-origin",
"-webkit-animation",
"-moz-animation",
"-ms-animation",
"-o-animation",
"animation",
"-webkit-animation-name",
"-moz-animation-name",
"-ms-animation-name",
"-o-animation-name",
"animation-name",
"-webkit-animation-duration",
"-moz-animation-duration",
"-ms-animation-duration",
"-o-animation-duration",
"animation-duration",
"-webkit-animation-play-state",
"-moz-animation-play-state",
"-ms-animation-play-state",
"-o-animation-play-state",
"animation-play-state",
"-webkit-animation-timing-function",
"-moz-animation-timing-function",
"-ms-animation-timing-function",
"-o-animation-timing-function",
"animation-timing-function",
"-webkit-animation-delay",
"-moz-animation-delay",
"-ms-animation-delay",
"-o-animation-delay",
"animation-delay",
"-webkit-animation-iteration-count",
"-moz-animation-iteration-count",
"-ms-animation-iteration-count",
"-o-animation-iteration-count",
"animation-iteration-count",
"-webkit-animation-direction",
"-moz-animation-direction",
"-ms-animation-direction",
"-o-animation-direction",
"animation-direction",
"text-align",
"-webkit-text-align-last",
"-moz-text-align-last",
"-ms-text-align-last",
"text-align-last",
"vertical-align",
"white-space",
"text-decoration",
"text-emphasis",
"text-emphasis-color",
"text-emphasis-style",
"text-emphasis-position",
"text-indent",
"-ms-text-justify",
"text-justify",
"letter-spacing",
"word-spacing",
"-ms-writing-mode",
"text-outline",
"text-transform",
"text-wrap",
"text-overflow",
"-ms-text-overflow",
"text-overflow-ellipsis",
"text-overflow-mode",
"-ms-word-wrap",
"word-wrap",
"word-break",
"-ms-word-break",
"-moz-tab-size",
"-o-tab-size",
"tab-size",
"-webkit-hyphens",
"-moz-hyphens",
"hyphens",
"pointer-events",
"opacity",
"filter:progid:DXImageTransform.Microsoft.Alpha(Opacity",
"-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha",
"-ms-interpolation-mode",
"color",
"border",
"border-width",
"border-style",
"border-color",
"border-top",
"border-top-width",
"border-top-style",
"border-top-color",
"border-right",
"border-right-width",
"border-right-style",
"border-right-color",
"border-bottom",
"border-bottom-width",
"border-bottom-style",
"border-bottom-color",
"border-left",
"border-left-width",
"border-left-style",
"border-left-color",
"-webkit-border-radius",
"-moz-border-radius",
"border-radius",
"-webkit-border-top-left-radius",
"-moz-border-radius-topleft",
"border-top-left-radius",
"-webkit-border-top-right-radius",
"-moz-border-radius-topright",
"border-top-right-radius",
"-webkit-border-bottom-right-radius",
"-moz-border-radius-bottomright",
"border-bottom-right-radius",
"-webkit-border-bottom-left-radius",
"-moz-border-radius-bottomleft",
"border-bottom-left-radius",
"-webkit-border-image",
"-moz-border-image",
"-o-border-image",
"border-image",
"-webkit-border-image-source",
"-moz-border-image-source",
"-o-border-image-source",
"border-image-source",
"-webkit-border-image-slice",
"-moz-border-image-slice",
"-o-border-image-slice",
"border-image-slice",
"-webkit-border-image-width",
"-moz-border-image-width",
"-o-border-image-width",
"border-image-width",
"-webkit-border-image-outset",
"-moz-border-image-outset",
"-o-border-image-outset",
"border-image-outset",
"-webkit-border-image-repeat",
"-moz-border-image-repeat",
"-o-border-image-repeat",
"border-image-repeat",
"outline",
"outline-width",
"outline-style",
"outline-color",
"outline-offset",
"background",
"filter:progid:DXImageTransform.Microsoft.AlphaImageLoader",
"background-color",
"background-image",
"background-repeat",
"background-attachment",
"background-position",
"background-position-x",
"-ms-background-position-x",
"background-position-y",
"-ms-background-position-y",
"-webkit-background-clip",
"-moz-background-clip",
"background-clip",
"background-origin",
"-webkit-background-size",
"-moz-background-size",
"-o-background-size",
"background-size",
"box-decoration-break",
"-webkit-box-shadow",
"-moz-box-shadow",
"box-shadow",
"filter:progid:DXImageTransform.Microsoft.gradient",
"-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient",
"text-shadow"
]
]
}
在 文件-> 首选项-> 设置-> settings.json 添加
"csscomb.formatOnSave": true,
"csscomb.preset": {},
"csscomb.ignoreFilesOnSave": [
],
在 vscode 下 CTRL+Shift+P 唤出快捷命令,输入CSSComb:Format styles
回车 (不能缺少)
修改 css 代码保存,就可以看到 css 属性顺序自动改变了
用于检查项目package.json
的依赖是否是最新版本
npm install -g npm-check-updates
使用
ncu # 检查项目依赖最新版本
ncu -u # 更新package.json的版本号
npm install # 更新项目依赖
此功能不建议使用,属于一个实验性功能,在此列出此功能是为了让不知道的人知道vue还有这个东西
具体使用请点击上面链接前往 vue 官方文档学习使用
vite 默认添加 postcss,所以不需要额外安装
npm i autoprefixer postcss-plugin-px2rem -D # 第一个是浏览器前缀 第二个是rem移动端适配
在根目录添加postcss.config.cjs
module.exports = {
plugins: [
require('autoprefixer'),
require('postcss-plugin-px2rem')({
rootValue: 37.5, //换算基数,1rem相当于10px,值为37.5时,1rem为20px,淘宝的flex默认为1rem为10px
// unitPrecision: 5, //允许REM单位增长到的十进制数字。
//propWhiteList: [], //默认值是一个空数组,这意味着禁用白名单并启用所有属性。
propBlackList: ['border'], //黑名单
// exclude: /(node_module)/, //默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如/(node_module)\/如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值
// selectorBlackList: [], //要忽略并保留为px的选择器
// ignoreIdentifier: false, //(boolean/string)忽略单个属性的方法,启用ignoreidentifier后,replace将自动设置为true。
// replace: true, // (布尔值)替换包含REM的规则,而不是添加回退。
mediaQuery: false, //(布尔值)允许在媒体查询中转换px。
minPixelValue: 3 //设置要替换的最小像素值(3px会被转rem)。 默认 0
})
]
}
情况:我使用 vite 的 proxy 去代理我的请求,但是我发现好像并没有帮我代理
事件重现
:
我在 vite.config 里面写了代理配置
server: {
...
proxy: {
'^/api': {
target: 'http://localhost:8000', // 后端服务实际地址
changeOrigin: true, //开启代理
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
src/utils/http/defaultConfig
import { AxiosRequestConfig } from 'axios'
export const defaultConfig: AxiosRequestConfig = {
baseURL: '/api' // 已经添加上了
}
export default defaultConfig
src/api/login/user/user.ts
import http from '/@/utils/http'
export const allVideo = () => http.post('/user/login')
然后我开心的去请求了,却发现浏览器报错
POST http://192.168.31.153:3000/api/user/login 404 (Not Found)
Error: Request failed with status code 404
...
我一看,地址为什么是这样的,不应该是 http://localhost:8000/user/login
吗?我以为是代理失效,找了半天才发现是我请求方式错了
src/api/login/user/user.ts
import http from '/@/utils/http'
export const allVideo = () => http.get('/user/login') // 改成get
我们看成功的真实请求地址
请求网址: http://192.168.31.153:3000/api/user/login
请求方法: GET
是我 nginx 知识薄弱了,他的请求地址不会变。
但是为什么会现在才发现这个问题呢?可能是之前创建项目第一次请求都成功了,当后面的 api 写错了浏览器报 404 我就会潜意识认为是我 api 地址或请求方式写错了,但是这次是第一次请求就报错,让我以为是没有代理上。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。