为脚手架(按需)添加开发常用的插件/配置
一、css相关配置
1. 添加全局样式变量
定义一些全局的主题样式变量,统一各页面样式时十分有用
ts
// vite.config.ts
export default defineConfig({
// ...其他配置
css: {
preprocessorOptions: {
scss: {
// ...其他配置
// 添加额外的代码变量,可以全局引用。如果在这里添加了样式,那么样式在最终编译会重复生成
additionalData: "$color: red;",
// 变量名可以直接替换为文件导入,比如:
// additionalData: @import '@/styles/theme.scss';
}
}
},
// ..其他配置
})
2. 自动填加浏览器前缀
可以避免手动处理浏览器兼容问题,专注在样式开发工作上
-
安装
autoprefixer
:autoprefixer是添加前缀(vite已经内置postcss,无需再单独下载)tspnpm add autoprefixer -D
-
在
vite.config.ts
中配置ts// vite.config.ts import autoprefixer from 'autoprefixer'; export default defineConfig({ // ...其他配置 css: { // ..其他配置 postcss: { plugins: [ autoprefixer({ overrideBrowserslist: [ 'Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8' ], grid: true }) ] } }, // ..其他配置 })
3. 将类名处理成哈希(可选)
避免类名重复导致全局样式冲突
vue的
scoped
也能一定程度上解决这个问题。简单项目建议直接使用scoped
,复杂样式管理可以使用 CSS modules 文件或者通过postcss-modules自定义将类名处理成哈希值。
CSS modules
也是基于postcss-modules
实现的,配置 CSS modules 的行为,选项将被传递给postcss-modules
。任何以.module.scss
为后缀名的文件都被认为是一个导入CSS Module
这样的文件会返回一个相应的模块对象
- 下面简单写一下
CSS Module
的使用,postcss-modules
可以自行了解,也差不多。
vue
<script setup lang="ts">
import { useCssModule } from 'vue';
const style = useCssModule();
</script>
<style module lang="scss">
/** 也可以把样式写在以 `.module.scss` 为后缀名的文件中再导入使用 **/
// @import '**/**.module.scss';
.example {
text-decoration: dashed;
display: flex;
flex-flow: row nowrap;
justify-content: center;
}
</style>
- 可以在
vite.config.ts
中配置生成规则:
ts
// vite.config.ts
export default defineConfig({
// ...其他配置
css: {
// ..其他配置
modules: {
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
},
// ..其他配置
})
4. 将px转rem(可选)
自适应不同屏幕
在自适应不同屏幕时会发挥作用,尤其是在还原ui设计图时。如果你在项目中使用了UI框架,请确认框架组件是否支持
pxtorem
- 下载
postcss-pxtorem
插件
shell
pnpm add postcss-pxtorem -D
vite.config.ts
中配置
ts
// vite.config.ts
// @ts-expect-error postcss-pxtorem还没有官方的ts包
import pxtorem from 'postcss-pxtorem';
export default defineConfig({
// ...其他配置
css: {
// ..其他配置
postcss: {
plugins: [
pxtorem({
// 1rem = 14px
rootValue: 14,
// 需要转换的属性,除 border 外所有px 转 rem
propList: ['*', '!border'],
// 排除node_modules
exclude: '/node_modules/*',
// 是否要在媒体查询中转换px
mediaQuery: false,
// 设置要转换的最小像素值
minPixelValue: 2
})
]
}
},
// ..其他配置
})
- 添加 remUnit 适配代码
ts
// remUnit.ts
function setRem(): void {
const designWidth: number = 1920 // 设计稿宽度
const html: HTMLElement = document.documentElement
const width: number = html.clientWidth
html.style.fontSize = `${(width / designWidth * 14)}px`
}
// 初始化和窗口变化时调用
setRem()
window.addEventListener('resize', setRem)
export default setRem
// 这里用gpt将上面的代码改写成了一段更严谨的代码,随便用哪个都可以
// remUnit.ts
class RemAdapter {
private designWidth: number
private rootValue: number
constructor(designWidth: number = 1920, rootValue: number = 14) {
this.designWidth = designWidth
this.rootValue = rootValue
this.init()
}
private calculateRem(): void {
const html: HTMLElement = document.documentElement
const width: number = html.clientWidth
html.style.fontSize = `${(width / this.designWidth * this.rootValue)}px`
}
private init(): void {
this.calculateRem()
window.addEventListener('resize', this.calculateRem.bind(this))
}
}
// 创建实例
new RemAdapter()
export default RemAdapter
- 在
main.ts
中引入:
ts
import './remUnit'
5. tailwindcss v3.x
(可选)
可以当做postcss的插件使用。
- 下载和初始化
shell
pnpm add -D tailwindcss@3
// 下载完成后初始化
npx tailwindcss init
- 修改配置文件
ts
// `tailwind.config.ts`
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}"
],
theme: {
extend: {},
},
plugins: [],
}
// vite.config.ts
import tailwindcss from 'tailwindcss';
export default defineConfig({
// ...其他配置
css: {
// ..其他配置
postcss: {
plugins: [
// ...其他配置
tailwindcss()
]
}
},
// ..其他配置
})
- 添加 Tailwind 的基础指令,并在main.ts中引入
ts
/* src/tailwind.css */*
@tailwind base;
@tailwind components;
@tailwind utilities;
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import './tailwind.css'; // 包含 Tailwind 指令的 CSS 文件
import './style.scss'
createApp(App).mount('#app')
6. tailwindcss v4.0
(可选)
可以当做 vite 的插件使用。版本4集成了
autoprefixer
,所以如果使用这个版本,需要删除autoprefixer
插件的使用
- 下载和初始化
shell
pnpm add tailwindcss @tailwindcss/vite
- 修改配置文件
js
// vite.config.ts
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
tailwindcss(),
],
})
- 添加 Tailwind 的基础指令,并在main.ts中引入
css
/* src/tailwind.css */
@import "tailwindcss";
/* main.ts */
import { createApp } from 'vue'
import App from './App.vue'
import './tailwind.css'; // 包含 Tailwind 指令的 CSS 文件
import './style.scss'
createApp(App).mount('#app')
二、静态资源处理
静态资源?
静态资源本身并不是标准意义上的模块,因此对它们的处理和普通的代码是需要区别对待的。一方面我们需要解决资源加载 的问题,对 Vite 来说就是如何将静态资源解析并加载为一个 ES 模块的问题;另一方面在生产环境下我们还需要考虑静态资源的部署问题、体积问题、网络性能问题,并采取相应的方案来进行优化。
1. 图片无损压缩
图片资源的体积往往是项目产物体积的大头,如果能尽可能精简图片的体积,那么对项目整体打包产物体积的优化将会是非常明显的
- 安装
shell
pnpm add vite-plugin-imagemin -D
vite.config.ts
中配置
ts
import viteImagemin from 'vite-plugin-imagemin';
export defaul defineConfig({
plugins:[
// 其他配置
viteImagemin({
// 是否在控制台输出压缩结果
verbose: false,
// 无损压缩配置,无损压缩下图片质量不会变差
optipng: {
// 优化级别:
optimizationLevel: 7
},
gifsicle: {
optimizationLevel: 7,
interlaced: false
},
mozjpeg: {
// 压缩质量,范围为`0` (最差)到`100` (完美)。
quality: 20,
},
pngquant: {
quality: [0.8, 0.9],
speed: 4,
},
// svg 优化
svgo: {
plugins: [
{
// 删除与文档宽度和高度匹配的[`viewBox`]
name: 'removeViewBox'
},
{
// 从文档中的元素中删除空属性
name: 'removeEmptyAttrs',
active: false
}
]
}
})
]
})
2. 以组件的形式加载Svg
svg可以作为静态图片加载,但是如果作为一个组件引用的话,可以很方便的修改svg的各种属性,且比img标签等引入方式更加优雅(如果你想在项目中使用雪碧图优化svg,可以先跳过这一小节)
- 安装
shell
pnpm add vite-svg-loader -D
- 在
vite.config.ts
中配置
ts
import svgLoader from 'vite-svg-loader'
export default defineConfig({
plugins: [
// 其他配置
svgLoader()
]
})
-
使用
这个插件提供几种显示导入:
url、raw、component
import svg from './my-icon.svg?url'
import svg from './my-icon.svg?raw'
import svg from './my-icon.svg?component'
url
: 可以使用?url
后缀将 SVG 作为 URL 导入
raw
: 可以使用?raw
后缀将 SVG 作为 字符串 导入
component
: 可以使用?component
后缀将 SVG 作为 vue组件 导入**当未提供显示参数时,SVG 将默认作为 Vue 组件导入。**可以使用
defaultImport
配置设置来更改此设置,具体设置可参考官方说明
3. 动态引入静态资源
使用动态路径访问
assets
资源在前面,我们已经为
./src/assets
路径下的静态资源设置了别名,在加载图片时就不用手动寻址。比如:<img src="@assets/images/code-icon.png" />
。 但是在使用动态路径访问assets
资源时会导致打包后路径错误,找不到图片文件。所以我这里在src/utils/tools.ts
中添加了一个getAssetsFile
方法,用于使用动态路径获取assets
下的静态资源,代码如下:
ts
/**
* @description 获取assets静态资源路径,在使用动态路径时很有用
* */
export const getAssetsFile = (url: string) => {
return new URL(`../assets/${url}`, import.meta.url).href;
};
4. 雪碧图优化(可选)
svg 文件一般体积不大,但 Vite 中对于 svg 文件会始终打包成单文件,当项目中使用了较多的svg图标时,会导致网络请求增加,大量的 HTTP 请求会导致网络解析耗时变长,页面加载性能直接受到影响。
(不过这个问题只在http1.1中受影响,因为http2的多路复用设计可以解决大量http请求导致的网络加载性能问题。如果项目依旧使用的是http1.1并且存在较多svg,建议使用雪碧图技术进行优化)
- 安装
shell
pnpm add -D @spiriit/vite-plugin-svg-spritemap
- 在
vite.config.ts
中配置
ts
// vite.config.js / vite.config.ts
import VitePluginSvgSpritemap from '@spiriit/vite-plugin-svg-spritemap'
export default {
plugins: [VitePluginSvgSpritemap('./src/icons/*.svg', {
prefix: 'sprites_icon-', // 前缀
output: {
view: true,
use: true
}
})]
}
- 使用
这个插件也支持把svg导入为vue组件使用,如果项目中需要雪碧图优化,那么上面第2点提到的
以组件的形式加载Svg
的插件就不用下了,用这一个插件就可以了。
vue
// 该插件支持两种显示引入后缀用于转化组件,也可以不适用后缀,但样式会不太一样:
<script setup lang="ts">
import ViteView from '@assets/icons/vite.svg?view'; // 转化为vue组件,适配大小
import ViteUse from '@assets/icons/vite.svg?use'; // 直接使用,原始大小
</script>
<template>
<ViteView />
<ViteUse />
</template>
- 解决import无法找到模块的报错
ts
官方给的解决方案是在 vite-env.d.ts中添加下面这句代码,如果你添加了之后import语句还在报错,请重启编辑器
/// <reference types="@spiriit/vite-plugin-svg-spritemap/client" />
5. 单文件 or 内联?
在 Vite 中,所有的静态资源都有两种构建方式,一种是打包成一个单文件,另一种是通过 base64 编码的格式内嵌到代码中。
这两种方案到底应该如何来选择呢?
对于比较小的资源,适合内联到代码中,一方面对代码体积
的影响很小,另一方面可以减少不必要的网络请求,优化网络性能
。而对于比较大的资源,就推荐单独打包成一个文件,而不是内联了,否则可能导致上 MB 的 base64 字符串内嵌到代码中,导致代码体积瞬间庞大,页面加载性能直线下降。
Vite 中内置的优化方案是下面这样的:
- 如果静态资源体积 >= 4KB,则提取成单独的文件
- 如果静态资源体积 < 4KB,则作为 base64 格式的字符串内联
上述的4 KB
即为提取成单文件的临界值,当然,这个临界值你可以通过build.assetsInlineLimit
自行配置,如下代码所示:
typescript
// vite.config.ts
{
build: {
// 8 KB
assetsInlineLimit: 8 * 1024
}
}
svg 格式的文件不受这个临时值的影响,始终会打包成单独的文件,因为它和普通格式的图片不一样,需要动态设置一些属性,所以采用了上面的一些优化措施
三、安装pinia
应该在什么时候使用 Store?
一个 Store 应该包含可以在整个应用中访问的数据。这包括在许多地方使用的数据,例如显示在导航栏中的用户信息,以及需要通过页面保存的数据,例如一个非常复杂的多步骤表单。
另一方面,你应该避免在 Store 中引入那些原本可以在组件中保存的本地数据,例如,一个元素在页面中的可见性。
并非所有的应用都需要访问全局状态,但如果你的应用确实需要一个全局状态,那 Pinia 将使你的开发过程更轻松。 (不要) 滥用 store
1. 安装
shell
// 安装pinia和pinia持久化插件
pnpm add pinia pinia-plugin-persistedstate
2. 配置
在/src
下新建文件夹stores
,创建index.ts
文件如下:
ts
import { createPersistedState } from 'pinia-plugin-persistedstate';
const pinia = createPinia();
pinia.use(
createPersistedState({
// 全局 key 配置接受传入 Store key 的函数,并返回一个新的 storage 密钥。
key: (id: string) => `__persist__${id}`,
// 该配置将会使所有 Store 持久化存储,且必须配置 persist: false 显式禁用持久化。
auto: true
})
);
export default pinia;
3. vs code配置
- 为编辑器添加快速生成
Pinia store
模板代码,下面定义了两种Pinia store的模板:
json
1. 打开vs code
2. 按下 Ctrl+Shift+P (Windows) 或 Cmd+Shift+P (Mac) 打开命令面板
3. 输入 "Configure User Snippets"
4. 选择 "New Global Snippets file..." 或者选择特定的语言(如 "typescript.json")
5. 如果选择新建全局片段,输入文件名(如 "pinia")
在自动创建好的文件中粘贴一下代码:
{
"Pinia Options Store Boilerplate": {
"scope": "javascript,typescript",
"prefix": "pinia-options",
"body": [
"import { defineStore, acceptHMRUpdate } from 'pinia'",
"",
"export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', {",
" state: () => ({",
" $0",
" }),",
" getters: {},",
" actions: {},",
"})",
"",
"if (import.meta.hot) {",
" import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))",
"}",
""
],
"description": "Bootstrap the code needed for a Vue.js Pinia Options Store file"
},
"Pinia Setup Store Boilerplate": {
"scope": "javascript,typescript",
"prefix": "pinia-setup",
"body": [
"import { defineStore, acceptHMRUpdate } from 'pinia'",
"",
"export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', () => {",
" $0",
" return {}",
"})",
"",
"if (import.meta.hot) {",
" import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))",
"}",
""
],
"description": "Bootstrap the code needed for a Vue.js Pinia Setup Store file"
}
}
配置完成后使用方法:
1. 创建一个新的 .ts 文件
2. 在文件中输入 `pinia-options` 或 `pinia-setup`
3. 按下 Tab 键,代码片段将自动展开
下面的代码是我使用`pinia-setup`生成的:
import { defineStore, acceptHMRUpdate } from 'pinia'
export const useTestStore = defineStore('test', () => {
return {}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useTestStore, import.meta.hot))
}
- 这里附带贴一下vue的快速生成代码模板:
json
1. 打开vs code
2. 按下 Ctrl+Shift+P (Windows) 或 Cmd+Shift+P (Mac) 打开命令面板
3. 输入 "Configure User Snippets"
4. 选择'vue'
{
"Vue3 Template": {
"prefix": "v3",
"body": [
"<template>",
"\t<div>",
"\t\t$1",
"\t</div>",
"</template>",
"",
"<script setup>",
"import { ref } from 'vue'",
"",
"$2",
"</script>",
"",
"<style scoped>",
"$3",
"</style>"
],
"description": "Vue 3 基础模板",
},
"Vue3-ts-scss Template": {
"prefix": "v3ts",
"body": [
"<template>",
"\t<div>",
"\t\t$1",
"\t</div>",
"</template>",
"",
"<script lang='ts' setup>",
"",
"$2",
"</script>",
"",
"<style lang='scss' scoped>",
"$3",
"</style>"
],
"description": "Vue 3 ts scss模板"
},
"Vue3 Watch": {
"prefix": "v3watch",
"body": [
"watch(${1:source}, (newValue, oldValue) => {",
"\t${2:// 处理逻辑}",
"})"
],
"description": "Vue 3 侦听器"
},
"Vue3 Props": {
"prefix": "v3props",
"body": [
"const props = defineProps({",
"\t${1:propName}: {",
"\t\ttype: ${2:String},",
"\t\trequired: ${3:true},",
"\t\tdefault: ${4:null}",
"\t}",
"})"
],
"description": "Vue 3 组件 props 定义"
},
"Vue3 Emits": {
"prefix": "v3emits",
"body": [
"const emit = defineEmits(['${1:eventName}'])"
],
"description": "Vue 3 组件 emits 定义"
}
}
四、安装router
1. 安装
shell
pnpm add vue-router@4
2. 添加路由
这一步可以按自己的习惯创建文件,下面是代码示例:
ts
// src/router/modules/home/index.ts
import { RouteRecordRaw } from 'vue-router';
const homeRoutes: RouteRecordRaw[] = [
{
path: '/',
name: 'HelloWorld',
component: () => import('@/components/HelloWorld.vue'),
meta: {
title: 'Hello World',
keepAlive: true
}
}
];
export default homeRoutes;
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import homeRoutes from './modules/home';
const router = createRouter({
history: createWebHistory(),
routes: [...homeRoutes]
});
export default router;
// src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');
vue
// src/App.vue
<template>
<router-view></router-view>
</template>
五、自动生成路由和类型(可选)
默认情况下,该插件检查
src/pages
文件夹中是否有任何.vue
文件,并根据文件名生成相应的路由结构。这样,您在向应用程序添加路由时不再需要维护routes
数组,**只需将新的.vue
组件添加到路由文件夹中,然后让这个插件完成剩下的工作!**对于不同复杂度的项目,自动生成路由有不同的优缺点,按需使用。
1. 安装
shell
pnpm add -D unplugin-vue-router
2. 配置
ts
// vite.config.ts
import VueRouter from 'unplugin-vue-router/vite'
export default defineConfig({
plugins: [
VueRouter({
routesFolder: 'src/views',
dts: 'types/typed-router.d.ts'
}),
// ⚠️ Vue must be placed after VueRouter()
Vue(),
],
})
json
// tsconfig.json
{
"include":[
// other files...
"types/typed-router.d.ts"
],
"compilerOptions":{
// ...
"moduleResolution": "Bundler",
// ...
}
}
- 如果用了自动导入,那么
import
中的'vue-router'
需要替换为VueRouterAutoImports
:
ts
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite';
import { VueRouterAutoImports } from 'unplugin-vue-router';
export default defineConfig(({ mode }) => {
return {
pulgins: [
AutoImport({
// imports: ['vue', 'vue-router', 'pinia']修改为:
imports: ['vue', VueRouterAutoImports, 'pinia']
// ...其他配置
});
]
}
});
3. 创建
使用这个插件后页面组件需要按照指定规则创建
-
src/views
文件夹中添加一个index.vue
组件。这将在/
呈现为首页。 -
如果页面由动态路由链接,那么文件名应该写为
[xx].vue
,更多规则可以参看文档,举例:/
: ->index.vue
/about
: ->about.vue
/users
: ->users/index.vue
/users/:id
: ->users/[id].vue
,id
就是路由参数/users/[[id]].vue
: ->/users/:id?
,id
是可选参数/users/[slugs]+.vue
: ->/users/:slugs+
,slugs
是可重复参数/pages/[...path].vue
: ->/:path(.*)
,可捕获所有路由,可用于添加404/pages/[email protected]
: -> 将定义为命名视图- 注意:
index.vue
文件名必须全部小写
-
/src/main.ts
或者router.ts
中导入插件
ts
import { routes } from 'vue-router/auto-routes'
const router = createRouter({ history: createWebHistory(), routes, })
4. 初始化
- 添加插件后,启动开发服务器,会在
typed-router.d.ts
生成类型的第一个版本 - 在
vite-env.d.ts
中添加类型,避免ts报错
ts
/// <reference types="unplugin-vue-router/client" />
unplugin-vue-router
将添加一个虚拟vue-router/auto
模块,该模块从vue-router
导出所有内容,并从unplugin-vue-router/runtime
导出一些额外功能。建议避免在新项目中使用
vue-router/auto
。保留它是为了与使用它的现有项目兼容,并且将来可能会被删除。您可以通过将此设置添加到
.vscode/settings.json
来从 VSCode 导入建议中排除vue-router/auto
:
json
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.autoImportFileExcludePatterns": ["vue-router/auto$"]
}
六、添加页面跳转进度条
1. 安装
shell
pnpm add nprogress
pnpm add @types/nprogress -D
2. 封装
ts
// 新建文件:/src/utils/nporgress.ts
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
//全局进度条的配置
NProgress.configure({
easing: 'ease', // 动画方式
speed: 1000, // 递增进度条的速度
showSpinner: false, // 是否显示加载ico
trickleSpeed: 200, // 自动递增间隔
minimum: 0.3, // 更改启动时使用的最小百分比
parent: 'body', //指定进度条的父容器
})
// 打开进度条
export const start = () => {
NProgress.start()
}
// 关闭进度条
export const close = () => {
NProgress.done()
}