距离上一次,脚手架文章《手把手Vue3项目(一)》已经过去了10个月,
这一次,随着Vue3正式普及,同时也能让更多的小伙伴,快速进入Vue3项目的开发,
这里我重新搭建了一个Vue3脚手架模板,并试图教会大家如何安装和配置一个Vue3的项目,
其中涉及几乎一个企业项目所包含的所有内容,希望可以帮到大家,
此项目可以从gitee上clone后直接使用,地址在文末
使用手册
1. cd vue3-scaffold-template
2. pnpm install 或 npm install
3. pnpm dev 或 npm run dev 启动项目
4. pnpm jsondb 或 npm run jsondb 启动json-server服务器
有两个案例,配置管理、时间管理App供大家参考。
安装和配置过程
授人以鱼不如授人以渔,
这个脚手架,大家可以clone下来,直接进入项目开发,希望大家用的开心。
创建项目
pnpm create vite
授人以鱼不如授人以渔,
这个脚手架,大家可以clone下来,直接进入项目开发,希望大家用的开心。
pnpm create vite
然后按照提示操作即可!
这里我选择的是:Vue、TypeScript,Vite 目前默认Vue3
配置src目录为默认目录
vite.config.ts 中配置
resolve: {
alias: [{ find: '@', replacement: path.resolve(__dirname, './src') }]
},
安装Element-Plus
参照 Element-Plus 快速上手
- 安装 Element-Plus
pnpm add element-plus
- 需要先安装按需导入的两款插件
pnpm add unplugin-vue-components unplugin-auto-import -D
- vite.config.ts 中配置
// vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
// ...
plugins: [
// ...
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
})
- 在实际代码中测试,是否生效
安装Element Plus 图标
- 安装Element Icon图标
pnpm install @element-plus/icons-vue
自动导入图标(可以使用,但考虑到项目开发,本人弃用此方案)
从 iconify(图标网站) 中自动导入任何图标集
自动导入必须遵循名称格式 {prefix:默认为i}-{collection:图标集合的名称}-{icon:图标名称}
<el-icon color="#000" size="22">
<i-ep-expand />
</el-icon>
- 需要先安装按需导入的两款插件
pnpm add unplugin-icons unplugin-auto-import -D
- vite.config.ts 中配置
// vite.config.ts
import path from 'path'
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// 给插件开发者调试用的插件,个人使用没必要安装和使用
import Inspect from 'vite-plugin-inspect'
const pathSrc = path.resolve(__dirname, 'src')
export default defineConfig({
// ...
plugins: [
// ...
AutoImport({
// Auto import functions from Vue, e.g. ref, reactive, toRef...
// 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
imports: ['vue'],
// Auto import functions from Element Plus, e.g. ElMessage, ElMessageBox... (with style)
// 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
resolvers: [
ElementPlusResolver(),
// Auto import icon components
// 自动导入图标组件
IconsResolver({
prefix: 'Icon',
}),
],
dts: path.resolve(pathSrc, 'auto-imports.d.ts'),
}),
Components({
resolvers: [
// Auto register icon components
// 自动注册图标组件
IconsResolver({
enabledCollections: ['ep'],
}),
// Auto register Element Plus components
// 自动导入 Element Plus 组件
ElementPlusResolver(),
],
dts: path.resolve(pathSrc, 'components.d.ts'),
}),
Icons({
autoInstall: true,
}),
Inspect(),
],
})
- 在实际代码中测试,是否生效
<el-icon color="#000" size="22">
<i-ep-expand />
</el-icon>
全局注册图标(本人选用方案)
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
Eslint、Prettier、 Husky、Githooks、commit-msg
- 需要安装的依赖
# eslint校验相关
pnpm add eslint eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin vue-eslint-parser -D
# prettier格式化相关
pnpm add prettier eslint-config-prettier eslint-plugin-prettier -D
# git提交相关
pnpm add husky lint-staged -D
# commit-msg相关
pnpm add @commitlint/config-conventional @commitlint/cli -D
- 相关配置
eslint
.eslint.js
// .eslint.js
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true
},
/* 指定如何解析语法。可以为空,但若不为空,只能配该值,原因见下文。*/
parser: 'vue-eslint-parser',
/* 优先级低于parse的语法解析配置 */
parserOptions: {
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features
sourceType: 'module', // Allows for the use of imports
ecmaFeatures: {
tsx: true,
jsx: true
},
/* 扩展配置,加一些插件 */
extends: [
'plugin:vue/vue3-recommended',
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended'
],
rules: {}
}
};
.eslintignore
*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
*.zip
/public
/docs
.husky
.local
/bin
Dockerfile
/src/utils/vendor
prettier
.prettierrc.js
module.exports = {
printWidth: 100, // 单行输出(不折行)的(最大)长度
tabWidth: 2, // 每个缩进级别的空格数
semi: false, // 是否在语句末尾打印分号
singleQuote: true, // 是否使用单引号
quoteProps: 'as-needed', // 仅在需要时在对象属性周围添加引号
bracketSpacing: true, // 是否在对象属性添加空格
jsxBracketSameLine: true, // 将 > 多行 JSX 元素放在最后一行的末尾,而不是单独放在下一行(不适用于自闭元素),默认false,这里选择>不另起一行
htmlWhitespaceSensitivity: 'ignore', // 指定 HTML 文件的全局空白区域敏感度, "ignore" - 空格被认为是不敏感的
trailingComma: 'none', // 去除对象最末尾元素跟随的逗号
useTabs: false, // 不使用缩进符,而使用空格
jsxSingleQuote: false, // jsx 不使用单引号,而使用双引号
arrowParens: 'always', // 箭头函数,只有一个参数的时候,也需要括号
rangeStart: 0, // 每个文件格式化的范围是文件的全部内容
proseWrap: 'always', // 当超出print width(上面有这个参数)时就折行
endOfLine: 'lf' // 换行符使用 lf
};
.prettierignore
package.json
/dist
.DS_Store
.eslintignore
*.png
.editorconfig
.gitignore
.prettierignore
.eslintcache
*.lock
yarn-error.log
.history
CNAME
yarn.lock
husky
参考链接:
- Husky
- commit-msg
pre-commit 代码提交前(代码格式化、修复)
- 配置husky
# 使用推荐的自动安装方式,这里我使用的pnpm的
npx husky-init && npm install # npm
npx husky-init && yarn # Yarn 1
yarn dlx husky-init --yarn2 && yarn # Yarn 2+
pnpm dlx husky-init && pnpm install # pnpm
它将设置 husky,修改package.json
并创建一个pre-commit
您可以编辑的示例挂钩。默认情况下,它将npm test
在您提交时运行,但是你需要自己找到.husky/pre-commim文件
把其中的npm test
改成npm run lint-staged
,并在package.json
中配置提交前所需要做的操作,配置内容如下:
{
"scripts": {
"lint-staged": "lint-staged", // ./husky/pre-commit执行的操作
},
"lint-staged": {
"*.{js,jsx,ts,tsx,less,scss}": [
"prettier --write", // prettier格式化代码
"eslint --cache --fix", // eslint修复代码
"git add"
]
}
}
- commit-msg配置
要使用husky add
添加另一个钩子commit-msg
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
它将在.husky目录
下生成一个commit-msg
文件,进行提交的commit-msg检测,如果不符合规范会给出提示;同时需要在.commitment.config.js
中添加配置,下面的配置可以直接使用,也可以自定义
commitment.config.js
module.exports = {
extends: ['@commitlint/config-conventional'], // 默认配置只有这一行,rules可以自己定义
// 也可以自定义规则
// ignores: [(commit) => commit.includes('init')],
rules: {
'body-leading-blank': [2, 'always'], //body上面要有换行
'footer-leading-blank': [1, 'always'], //footer上面要有换行
'header-max-length': [2, 'always', 108], //header最大108个字符
'subject-empty': [2, 'never'], //subject位不能为null
'type-empty': [2, 'never'], //type位不能为null
'type-enum': [
2,
'always',
[
'feat', // 新增功能
'fix', // bug修复
'perf', // 性能,体验优化
'style', // 不影响程序逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑)
'docs', // 文档更新
'refactor', // 重构代码(既没有新增功能,也没有修复 bug)
'build', // 主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交
'ci', // 主要目的是修改项目继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle等)的提交
'test', // 新增测试用例或是更新现有测试
'revert', // 回滚某个更早之前的提交
'merge', // 分支合并 Merge branch ?of ?
'chore' // 不属于以上类型的其他类型
// 'wip',
// 'workflow',
// 'types',
// 'release',
]
]
}
}
- 测试
# 检测上一次提交的commit是否符合提交规范
npx commitlint --from HEAD~1 --to HEAD --verbose
Vue-Router 4
- 安装依赖
pnpm add vue-router@4
- 配置:
// router/index.ts ----------------创建路由------------------
import {createRouter, createWebHistory} from "vue-router";
import {routes} from "@/router/config";
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
// router/config.ts ----------------路由路径配置文件------------
export const routes = [
// 路由重定向
{
path: '/',
redirect: '/home',
},
{
path: '/home',
// component: Home
component: () => import("@modules/home/Index.vue")
},
{
path: '/about',
component: () => import("@modules/about/Index.vue")
}
]
// main.ts -----------------使用router--------------------------
import {createApp} from 'vue'
import router from "@/router";
import App from './App.vue'
import "ant-design-vue/dist/antd.less"
// import '@ant-design/icons-vue'
const app = createApp(App)
app.use(router)
app.mount('#app')
// createApp(App).use(router).mount('#app')
// App.vue ----------------在页面中渲染路由加载的页面------------------------
<script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out <https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup>
</script>
<template>
<router-view></router-view>
</template>
<style>
</style>
- 路由权限、nprogress
// main.ts --------------------------------------------
import router from '@/router'
import './permission'
app.use(router)
// 挂载实例 app
app.mount('#app')
// permissson文件----------------------------------------
// 路由权限处理
import router from '@/router/index'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css'
import { RouteLocationNormalized } from 'vue-router'
NProgress.configure({ showSpinner: false })
router.beforeEach(async (to, from) => {
NProgress.start()
// if (to.name !== 'SsoRedirect') {
// await initRequest();
// }
if (to.path.indexOf('40') > -1) {
to.meta = from.meta
to.name = from.name
}
await handleRouterQuery(to, from)
})
router.afterEach(() => {
NProgress.done()
// 路由完成后做些事情
})
let currentPath: string
const Reg = /(^/user(?!/dashboard).*)/
// 处理路由上的公共参数 避免手动代入
async function handleRouterQuery(to: RouteLocationNormalized, from: RouteLocationNormalized) {
const { path, fullPath } = to
if (!Reg.test(path)) return
if (currentPath !== fullPath) {
currentPath = fullPath
}
return
}
Pinia
- 安装依赖
pnpm add pinia
- 配置
// main.ts--------------------------------------------------------
import { createPinia } from 'pinia'
import '@/store/index'
app.use(createPinia())
// 挂载实例 app
app.mount('#app')
// @/store/index.ts,批量导入modules中的所有store--------------------
import { acceptHMRUpdate } from 'pinia'
const modulesFiles = import.meta.globEager('./modules/*.ts')
Object.keys(modulesFiles).forEach((modulePath) => {
const store = modulesFiles[modulePath]
Object.keys(store).forEach((key) => {
if (key.startsWith('use')) {
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(store[key], import.meta.hot))
}
}
})
})
// @/store/modules/*.ts--------------------------------------------
// 一个使用例子
import { defineStore } from 'pinia'
interface IState {
topic: any
}
export const useMessageStore = defineStore<'message', IState>('message', {
state: () => ({
topic: {}
})
})
Scss、tailwindcss、postcss、autoprefixer
- 需要的依赖
pnpm add postcss sass tailwindcss @tailwindcss/line-clamp autoprefixer -D
posts.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
tailwind.config.js
module.exports = {
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
theme: {
extend: {
colors: {
normal: '#3693FF',
hover: '#0068E0',
active: '#004799',
background: '#f4f6fa',
title: '#333333',
content: '#666666',
username: '#303133',
operate: '#959EAD'
},
spacing: {
32: '32px',
4: '4px',
36: '36px',
16: '16px',
24: '24px',
8: '8px',
12: '12px',
10: '10px',
18: '18px',
20: '20px',
48: '48px'
},
borderRadius: {
none: '0',
small: '8px',
large: '16px'
},
fontSize: {
base: '14px',
lg: '16px',
'2lg': '19px',
m: '20px',
xl: '24px',
'2xl': '32px',
'3xl': '40px',
'4xl': '48px'
}
}
},
mode: 'jit',
plugins: [require('@tailwindcss/line-clamp')]
};
- vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
css: {
modules: {
scopeBehaviour: 'local',
generateScopedName: (name) => {
return name
}
}
}
})
axios封装和使用
# 这里请大家直接去看代码,2个文件
1. src/service/index.ts # axios封装的内容
2. src/api/index.ts # 封装后的axios如何使用
全局参数
// 导入全局参数(需要什么全局参数可以自己去定义)
import Global from './config/global'
// 注入全局参数
app.config.globalProperties.GLOBAL = Global
Element Plus 国际化
// main.ts
// Element Plus 国际化
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
// Element Plus 国际化
app.use(ElementPlus, { locale: zhCn, size: 'default' })
app.mount('#app')
Find me
Gitee:gitee.com/heyhaiyon
微信公众号:heyhaiyang
AdminJS:heyhaiyang
头条号:heyhaiyang
微信群:可以在公众号中获取,文章不让放群二维码