前言
2月7日,Vue3 正式成为了新的默认版本, 可以预见,接下来 Vue3 相关的生态会迅速的发展,大伙们也可以开始学习 Vue3 准备新一轮的卷了。而项目的开始,少了一个开箱即用的模板怎么行呢?我结合者 Vue3 新的生态,提前踩了踩坑撸了一个开箱即用的模板分享出来。
-
这篇文章上半文为该模板
开箱即用的功能概述
-
下半文则为一些想自己动手配置的小伙伴讲解一下
配置的过程
本文篇幅较长,难免有遗漏或者写错的地方,如有找到,欢迎在评论区提出讨论或者在 Github 上提出 issue ,有想补充贡献的,也欢迎提出PR
已配置好的开箱即用功能:
-
编程语言
:TypeScript 4.x 、JavaScript -
前端框架
:Vue 3.x -
构建工具
:Vite 2.x -
UI 框架
:Element Plus -
图标工具
:icones -
CSS预编译
:Sass -
CSS框架
:Windi CSS -
HTTP工具
:Axios -
路由管理
:Vue Router 4.x -
状态管理
:Pinia -
代码规范
:EditorConifg、Prettier、ESLint、Airbnb JavaScript Style Guide -
提交规范
:husky、Commitlint 、lint-staged
还有一些其余的各种功能插件:
-
实现自动按需加载
(墙裂推荐
):unplugin-auto-import、unplugin-vue-components、unplugin-icons -
实现 SVG图标 的组件化
:vite-svg-loader -
让各种 API 支持响应式
:VueUse -
让加载页面时有所反馈
:NProgress -
支持 markdown
:vite-plugin-md
上述所有的功能都已经过配置和验证,使用模板之后就可以愉快的撸码了!
这个模板没有配置
GitHub Actions、变更文档、单元测试 等的内容,如果有需要可以查看我的另一篇文档自行配置 # 从零开始搭建规范的 TypeScript SDK 项目工程环境
Github
使用方法
食用模板前,建议大家先
fork本仓库
,以获得最新的变更和补充,如果本模板对你有帮助,顺手点一个 star 呀
下载
- 点击 Use this template 按钮,就可以跳转到创建项目的地址
- 或者直接使用 git clone
git clone https://github.com/nekobc1998923/vitecamp.git
安装
npm install
运行
npm run dev
打包
npm run build
vetur -> volar
额外提一句,对于 vue3 的支持,vetur
很明显的不如 volar
,这边建议小伙伴们禁用vuetur而使用volor
如果同时还维护着 vue2 的项目,可以使用 vscode 的
工作区
功能进行单独使用
配置流程:
下面是整个模板的一个配置流程,如果有需要 自己动手配置一个 的可以往下接着阅读~
代码目录结构
整个模板代码目录的结构如图所示:
.husky
:用来放husky 钩子的配置文件夹.vscode
:用来放项目中的 vscode 配置presets
:用来放 vite 插件的 plugin 配置public
:用来放一些诸如 页头icon 之类的公共文件,会被打包到dist根目录下src
:用来放项目代码文件api
:用来放http的一些接口配置assets
:用来放一些 CSS 之类的静态资源components
:用来放 Vue 组件layout
:用来放项目的布局router
:用来放项目的路由配置store
:用来放状态管理Pinia的配置utils
:用来放项目中的工具方法类views
:用来放项目的.vue视图
Vite
vite 初始化
vite 创建一个 vue3 + typescript
基本项目,只需要一条短短的指令,这里的 my-vue-app
需要改成你的项目名:
npm init vite@latest my-vue-app --template vue-ts
使用 alias 别名
别忘了先安装
@types/node
哦~
我们配置 vite.config.ts
resolve: {
alias: {
'@': resolve(__dirname, './src'), // 把 @ 指向到 src 目录去
},
},
配置完后别忘了 tsconfig.json 也需要配置哦
配置 vite 服务设置
我们配置 vite.config.ts
,加入以下配置
值得注意的是:我们只有配置了 host 之后,我们本地才可以通过 ip 进行访问项目
server: {
host: true, // host设置为true才可以使用network的形式,以ip访问项目
port: 8080, // 端口号
open: true, // 自动打开浏览器
cors: true, // 跨域设置允许
strictPort: true, // 如果端口已占用直接退出
// 接口代理
proxy: {
'/api': {
// 本地 8000 前端代码的接口 代理到 8888 的服务端口
target: 'http://localhost:8888/',
changeOrigin: true, // 允许跨域
rewrite: (path) => path.replace('/api/', '/'),
},
},
},
配置 build 配置
我们可以设置打包时移除代码中的 console ,以及配置打包后的静态资源到 dist 下不同的目录
build: {
brotliSize: false,
// 消除打包大小超过500kb警告
chunkSizeWarningLimit: 2000,
// 在生产环境移除console.log
terserOptions: {
compress: {
drop_console: false,
pure_funcs: ['console.log', 'console.info'],
drop_debugger: true,
},
},
assetsDir: 'static/assets',
// 静态资源打包到dist下的不同目录
rollupOptions: {
output: {
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
},
},
},
配置 vite 环境变量
经常,我们需要去区分生产环境和开发环境,我们在根目录下新建三个文件用来放环境变量:
.env
.env.development
.env.production
其中 .env 配置如下:
VITE_API_BASEURL = /api
VITE_BASE = /base-vue3/
VITE_APP_TITLE = base-vue3
VITE_API_BASEURL
是项目的api基础前缀
VITE_BASE
是项目的基础路径前缀
VITE_APP_TITLE
是项目的网页标题
自动按需加载 api & 组件 & 样式
- 你是否厌烦了每次使用
vue
时,需要不断的 import vue 的 api - 你是否厌烦了每次使用
组件库
时,需要不断的 按需引入 组件 - 你是否厌烦了有时使用
第三方组件库
时,需要额外引入 css 样式
现在有两个插件可以帮我们解决这些问题,我们在调用时可以不需要import而直接使用,且最终打包时,只有实际使用过的api和组件才会被build进最终产物,它们就是 unplugin-auto-import
和 unplugin-vue-components
unplugin-auto-import:自动按需引入 vue\vue-router\pinia 等的api
unplugin-vue-components:自动按需引入 第三方的组件库组件 和 我们自定义的组件
更加详细的内容
推荐阅读
:juejin.cn/post/706264…
安装配置
我们先安装依赖:
npm install -D unplugin-vue-components unplugin-auto-import
然后我们配置 vite.config.ts
加入以下内容;
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
export default {
plugins: [
// ...
AutoImport({
dts: './src/auto-imports.d.ts',
imports: ['vue', 'pinia', 'vue-router', '@vueuse/core'],
// Generate corresponding .eslintrc-auto-import.json file.
// eslint globals Docs - https://eslint.org/docs/user-guide/configuring/language-options#specifying-globals
eslintrc: {
enabled: true, // Default `false`
filepath: './.eslintrc-auto-import.json', // Default `./.eslintrc-auto-import.json`
globalsPropValue: true, // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable')
},
}),
Components({
dts: './src/components.d.ts',
// imports 指定组件所在位置,默认为 src/components
dirs: ['src/components/'],
}),
],
}
因为我们使用了 ESLint
我们还需要去 .eslintrc.js
中配置:
// .eslintrc.js
module.exports = {
/* ... */
extends: [
// ...
'./.eslintrc-auto-import.json',
],
}
Element Plus
Element UI 不必我多说了大家都认识把,在使用 Vue2 搭建项目时使用的人数就已经非常多了,而 Element Plus 就是他们基于 Vue3 的组件库;Element Plus 于 2月7日,同 Vue3 一起成为正式版本,我试用了一下,正式版本还是比较好用的,值得推荐
安装配置
我们先安装 element-plus
npm install element-plus --save
然后我们在 vite.config.ts
中配置上文配置了的 AutoImport
和 Components
中的 resolvers
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default {
plugins: [
// ...
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
})
],
}
这样配置完之后,它就会去自动按需引入我们在代码中使用到的组件
和对应的样式
,是不是很便利呢
设置中文主题
Element Plus
默认的是英文,如果我们要使用中文主题,就可以在 APP.vue
中设置
<script setup lang="ts">
import zhCn from 'element-plus/lib/locale/lang/zh-cn';
const locale = zhCn;
</script>
<template>
<el-config-provider :locale="locale">
<router-view></router-view>
</el-config-provider>
</template>
其余组件库
如果你想使用其余诸如 ant-design-vue
或者 Naive UI
等的组件库,也可以自行参考配置,值得一提的是,它们都支持配置成自动按需引入的形式
自动按需引入的,可以自行在这里参考配置:github.com/antfu/unplu…
@vitejs/plugin-vue-jsx
这个插件可以让我们支持 jsx 写法
npm i @vitejs/plugin-vue-jsx -D
然后配置 vite.config.ts
import vueJsx from '@vitejs/plugin-vue-jsx';
export default defineConfig({
plugins: [...,vueJsx()]
})
@vitejs/plugin-legacy
npm i @vitejs/plugin-legacy -D
然后配置 vite.config.ts
import legacy from '@vitejs/plugin-legacy';
export default defineConfig({
plugins: [...,
legacy({
targets: ['defaults', 'not IE 11'],
}),
]
})
vite-svg-loader
这个插件可以让我们直接引入 svg 文件来使用,就像使用 Vue组件 一样
安装流程
npm i vite-svg-loader -D
然后配置 vite.config.ts
import svgLoader from 'vite-svg-loader'
export default defineConfig({
plugins: [...,svgLoader()]
})
使用示例
<script setup lang="ts">
import MyIcon from '@/assets/example.svg?component';
</script>
<template>
<MyIcon />
</template>
vite-plugin-md
这个插件可以帮助我们将 markdown
引入为 Vue组件
进行使用
值得注意的是,因为上面我们配置了 自动按需引入组件 ,安装完后我们只需要把
.md
文件放在components
目录下就可以直接使用啦
安装配置
npm i vite-plugin-md -D
然后配置 vite.config.ts
// vite.config.ts
import Markdown from 'vite-plugin-md';
export default {
plugins: [
Markdown(),
]
}
TypeScript 配置
这里直接贴上完整的 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来告诉编译器到哪里去查找模块,所有非相对模块导入都会被当做相对于 baseUrl。
"baseUrl": ".",
// 非相对模块导入的路径映射配置
"paths": {
"@/*": ["src/*"],
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
// 编译器默认排除的编译文件
"exclude": ["node_modules"]
}
代码风格规范
代码风格规范这一块的配置,我在我的另一篇文章中有很详细的配置流程,小伙伴们可以看过去: 从零开始搭建规范的 TypeScript SDK 项目工程环境
我们只需要额外安装一个依赖即可,在 eslint
初始化时选择 Vue
环境的话,它会帮我们自动装上
npm i -D eslint-plugin-vue
然后最后配置成的 .eslintrc.js
会有一些不一样的配置,我这里贴出完整配置:
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
globals: {
defineEmits: true,
document: true,
localStorage: true,
GLOBAL_VAR: true,
window: true,
defineProps: true,
defineExpose: true,
},
extends: [
'./.eslintrc-auto-import.json',
'airbnb-base',
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended', // 添加 prettier 插件
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: ['vue', '@typescript-eslint', 'import'],
rules: {
'no-console': 'off',
'import/no-unresolved': 'off',
'import/extensions': 'off',
'import/no-extraneous-dependencies': 'off',
},
};
Vue Router 4.x
安装配置
我们需要安装 vue-router4.x
的版本
npm install vue-router@4 --save
我们在 src/router 目录下新建 index.ts 文件存放我们的 router 配置
我们再把配置好的 router
挂载到 vue
上:修改入口 main.ts
文件
unplugin-icons
我们经常都要为找各种各样的图标而发愁,而现在有一个极好的插件和一个极好的库,可以直接解决我们找图标、用图标的后顾之忧
icones:是一个非常优秀的图标库,里面集成了很多的图标
unplugin-icons:可以自动按需引入我们所要使用的图标,而不用手动 import
安装配置
npm i -D unplugin-icons @iconify/json
安装好后,我们配置 vite.config.ts
加入以下内容
// vite.config.ts
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import Components from 'unplugin-vue-components/vite'
export default {
plugins: [
Components({
resolvers: IconsResolver(),
}),
Icons({
compiler: 'vue3',
autoInstall: true,
}),
],
}
使用示例
我们先打开网址:icones.netlify.app/ 随便选择一个图标
然后点击复制
回到我们的代码中,只需要短短的一句就可以使用了:
<template>
<i-mdi-account-reactivate style="font-size: 2em; color: red" />
</template>
Pinia
Pinia.js 是新一代的状态管理工具,可以认为它是下一代的 Vuex,即 Vuex5.x;它相比于 Vuex 有几个比较明显的变化:
- 更好的
typescript
支持 - 移除了
mutations
只留下actions
;actions
支持同步以及异步 - 无需手动添加
store
安装配置
npm install pinia --save
在src/store 文件夹下新建index.ts,配置如下:
import { createPinia } from 'pinia';
const store = createPinia();
export default store;
然后我们在main.ts下use导出的store
pinia使用方式
我们可以在store目录下新建theme.ts文件,编写如下:
import { defineStore } from 'pinia';
const theme = defineStore({
// 这里的id必须为唯一ID
id: 'theme',
state: () => {
return {
themeType: '亮蓝色',
themeColor: '#2080F0FF',
};
},
// 等同于vuex的getter
getters: {
getThemeType: (state) => state.themeType,
getThemeColor: (state) => state.themeColor,
},
// pinia 放弃了 mutations 只使用 actions
actions: {
// actions可以用async做成异步形式
setThemeType(type: string) {
this.themeType = type;
},
},
});
export default theme;
调用方式如下:
<script setup lang="ts">
import theme from '@/store/theme'
const myTheme = theme()
myTheme.setThemeType('暗淡色')
</script>
<template>
{{myTheme.themeColor}}
</template>
SCSS
这个不用我过多介绍,不会还有人没有用过 sass
或者 less
吧?我们直接进入配置流程:
安装配置
npm i sass -D
我们在 src/assets
目录下新建 variables.scss
文件,用来存放我们全局的 css 变量,我们先只配置一个主题色:
同时我们也在 src/assets
目录下新建 index.scss
,在它里面引入我们未来将要新建的 scss 文件;并在 main.ts
中引入它
$theme-color: #2080F0FF
然后打开 vite.config.ts 文件,加上 css 配置的引入
css: {
preprocessorOptions: {
scss: {
additionalData: `
@import "@/assets/styles/variables.scss";
`,
javascriptEnabled: true,
},
},
},
组件中的使用方式
按上述配置完后,我们在组件中不需要任何的引入,就可以直接进行使用我们在 variables.scss
中定义的变量:
<style lang="scss">
.myclass {
color: $theme-color;
}
</style>
Windi CSS
Windi CSS
可以视为 Tailwind CSS
的上位替代品,它提供更快的加载时间、与 Tailwind v2.0 的完全兼容性以及一系列额外的酷功能。
我们可以进行 原子化
的css编程,并且框架自动帮我们做好了按需引入的工作;
安装配置
npm i -D vite-plugin-windicss windicss
安装完依赖后,我们在 vite.config.ts
中安装此插件
import WindiCSS from 'vite-plugin-windicss'
export default {
plugins: [
WindiCSS(),
],
}
然后,我们在 main.ts
中引入它
import 'virtual:windi.css'
Axios
安装配置
npm i axios -S
基本封装
安装完后我们再在 src/api 目录下新建 http.ts 配置文件,用来封装 axios 方法,简单配置如下:
// src/api/http.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { ElMessage } from 'element-plus';
import showCodeMessage from '@/api/code';
const BASE_PREFIX = '/api';
// 创建实例
const service: AxiosInstance = axios.create({
// 前缀
baseURL: BASE_PREFIX,
// 超时
timeout: 1000 * 60 * 30,
// 请求头
headers: {
'Content-Type': 'application/json',
},
});
// 请求拦截器
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
// TODO 在这里可以加上想要在请求发送前处理的逻辑
// TODO 比如 loading 等
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
},
);
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
if (response.status === 200) {
return response;
}
ElMessage.info(JSON.stringify(response.status));
return response;
},
(error: AxiosError) => {
const { response } = error;
if (response) {
ElMessage.error(showCodeMessage(response.status));
return Promise.reject(response.data);
}
ElMessage.warning('网络连接异常,请稍后再试!');
return Promise.reject(error);
},
);
export default service;
我们再新建一个 code.ts 用来存放我们 http 请求后接收到状态码的处理逻辑
// src/api/code.ts
declare interface codeMessageMapTypes {
400: string;
401: string;
403: string;
404: string;
405: string;
500: string;
[key: string]: string;
}
const codeMessageMap: codeMessageMapTypes = {
400: '[400]:请求参数错误',
401: '[401]:账户未登录',
403: '[403]:拒绝访问',
404: '[404]:请求路径错误',
405: '[405]:请求方法错误',
500: '[500]:服务器错误',
};
const showCodeMessage = (code: number | string): string => {
return codeMessageMap[JSON.stringify(code)] || '网络连接异常,请稍后再试!';
};
export default showCodeMessage;
VueUse
VueUse
是一个响应式的 Vue
实用程序的合集,使用它,我们可以把各种各样的东西变成响应式而不用我们手动编写 hook
安装配置
npm i npm i @vueuse/core -D
安装完依赖后,我们还需要在 vite.config.ts
中配置 自动按需引入
import { VueUseComponentsResolver } from 'unplugin-vue-components/resolvers';
export default defineConfig({
plugins: [
AutoImport({
imports: ['@vueuse/core'],
}),
Components({
resolvers: [VueUseComponentsResolver()],
}),
],
});
这样配置完之后,我们就不需要再自己去引入方法,可以直接使用啦
NProgress
这个插件相信大部分人都有用过,它可以帮助我们在顶部加上页面加载提示:一个加载进度条和一个转的圈
安装配置
npm i --save nprogress
我们因为用着 typescript 所以还需要安装它的 types
npm i @types/nprogress -D
安装完之后,我门回到 src/router/index.ts
配置我们的路由守卫
import NProgress from 'nprogress';
router.beforeEach((to, from) => {
if (!NProgress.isStarted()) {
NProgress.start();
}
});
router.afterEach((to, from) => {
NProgress.done();
});
我们再在 src/assets/styles
目录下新建 nprogress.scss
文件配置如下:
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: $theme-color;
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
/* Fancy blur effect */
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px $theme-color, 0 0 5px $theme-color;
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
}
/* Remove these to get rid of the spinner */
#nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: $theme-color;
border-left-color: $theme-color;
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@-webkit-keyframes nprogress-spinner {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes nprogress-spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}