Vite
是一种新型前端构建工具,能够显著提升前端开发体验。7 月 13 日,Vite 3.0 正式发布。
去年 2 月,Vite 2 正式发布。从那时起,它的使用率不断增长,每周 npm
下载量超过 100 万次。发布后迅速形成了庞大的生态系统。Vite
正在推动 Web 框架的新一轮创新竞赛。
Vite
组成它主要由两部分组成:
一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)
。
一套构建指令,它使用 Rollup
打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
Vite
意在提供开箱即用的配置,同时它的 插件 API
和 JavaScript API
带来了高度的可扩展性,并有完整的类型支持。
开发环境中:Vite 需要在支持原生 ES 模块动态导入的浏览器中使用。
生产环境中:默认支持的浏览器需要支持 通过脚本标签来引入原生 ES 模块 。可以通过官方插件 @vitejs/plugin-legacy
支持旧浏览器。
根据 Vite
官网介绍,可以使用 npm
或 yarn
或 pnmp
来初始化 Vite
项目。
兼容性注意
Vite 需要 Node.js 版本 >= 12.0.0。
本项目使用 pnpm
构建。
# pnpm
pnpm create vite
然后按照提示操作即可!
还可以通过附加的命令行选项直接指定项目名称和想要使用的模板。例如,要构建一个 Vite + Vue
项目,运行:
# pnpm
pnpm create vite vite3-vue3 -- --template vue
终端执行后:
Vue-Router
Vue-Router
作为大多数项目必不可少的路由工具,已经被大多数的前端项目所使用,Vue-Router
可以让构建单页面应用变得更加的容易。
pnpm i vue-router@4
在 src 目录下创建一个文件夹 router/index.js,然后配置:
import { createRouter, createWebHashHistory } from 'vue-router'
import Layout from '@/layout'
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/Login.vue'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index.vue'),
//using el svg icon, the elSvgIcon first when at the same time using elSvgIcon and icon
meta: { title: 'Dashboard', elSvgIcon: 'Fold' }
}
]
},
]
const router = createRouter({
history: createWebHashHistory(),
scrollBehavior: () => ({ top: 0 }),
routes: constantRoutes
})
export default router
main.js
中引入import { createApp } from 'vue'
import App from './App.vue'
import router from '@/router'
const app = createApp(App)
app.use(router)
app.mount('#app')
Pinia
Pinia
是 Vue
的存储库,它允许跨组件/页面共享状态。
集成操作可参考之前的文章 Pinia上手——Vue 存储库
。
pnpm i pinia
实现一个登录(login
)请求。
创建一个 store/user.js 文件,添加如下代码:
import { loginAction } from '@/api/user'
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => {
return {
username: '',
avatar: ''
}
},
actions: {
update_username(username) {
this.$patch((state) => {
state.username = username
})
},
login(data) {
return new Promise((resolve, reject) => {
loginAction(data).then(res => {
if (res.code === 200) {
resolve(null)
} else {
reject(res)
}
}).catch((error) => {
reject(error)
})
})
}
}
})
在 main.js
中引入
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
element-plus
pnpm i element-plus
1、完整引入
如果对打包后的文件大小不是很在乎,那么使用完整导入会更方便。
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')
2、按需导入
自动导入
首先需要安装 unplugin-vue-components
和 unplugin-auto-import
这两款插件。
pnpm i -D unplugin-vue-components unplugin-auto-import
然后在 vite.config.js
中配置,代码如下:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
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: [
// ...
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
})
在 main.js
中引入如下代码:
import { createApp } from 'vue'
import App from './App.vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
alias
在过去使用vue-cli的时候,一般使用 @
去引入某些文件,由于 Vite 没有提供类似的配置,所以需要手动对其进行相关配置。
在 vite.config.js
中配置,代码如下:
import { defineConfig } from 'vite'
import path from 'path'
export default defineConfig({
// ...
define: {
// path库报错修复
'process.platform': null,
'process.version': null
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
})
pnpm i axios
axios
请求封装新建src/utils/request.js文件,代码如下:
import axios from 'axios'
import router from '@/router'
import { ElLoading, ElMessage, ElMessageBox } from 'element-plus'
import { getToken } from '@/utils/auth'
import { useUserStore } from '@/store/user'
let reqConfig
let loadingE
const service = axios.create()
// 请求拦截
service.interceptors.request.use(
(request) => {
// token setting
request.headers['AUTHORIZE_TOKEN'] = getToken()
/* download file*/
if (request.isDownLoadFile) {
request.responseType = 'blob'
}
/* upload file*/
if (request.isUploadFile) {
request.headers['Content-Type'] = 'multipart/form-data'
}
reqConfig = request
if (request.bfLoading) {
loadingE = ElLoading.service({
lock: true,
text: '数据载入中',
background: 'rgba(0, 0, 0, 0.1)'
})
}
/*
*params会拼接到url上
* */
if (request.isParams) {
request.params = request.data
request.data = {}
}
return request
},
(err) => {
Promise.reject(err)
}
)
// 响应拦截
service.interceptors.response.use(
(res) => {
if (reqConfig.afHLoading && loadingE) {
loadingE.close()
}
// 如果是下载文件直接返回
if (reqConfig.isDownLoadFile) {
return res
}
const { code } = res.data
const successCode = '0,200,20000'
if (successCode.includes(code)) {
return res.data
} else {
//返回错误信息
//注:如果没有return 则,会放回到请求方法中.then ,返回的res为 undefined
return Promise.reject(res.data)
}
},
(err) => {
/*http错误处理*/
if (loadingE) loadingE.close()
ElMessage({
message: err,
type: 'error',
duration: 2 * 1000
})
const errObj = {
msg: err.toString(),
reqUrl: reqConfig.baseURL + reqConfig.url,
params: reqConfig.isParams ? reqConfig.params : reqConfig.data
}
return Promise.reject(JSON.stringify(errObj))
}
)
export function axiosReq({
url,
data,
method,
isParams,
bfLoading,
afHLoading,
isUploadFile,
isDownLoadFile,
baseURL
}) {
return service({
url: url,
method: method ?? 'get',
data: data ?? {},
isParams: isParams ?? false,
bfLoading: bfLoading ?? false,
afHLoading: afHLoading ?? true,
isUploadFile: isUploadFile ?? false,
isDownLoadFile: isDownLoadFile ?? false,
baseURL: baseURL ?? import.meta.env.VITE_APP_BASE_URL
})
}
export default request
然后,在新建src/api/user.js,用来处理具体的网络请求。
import request from '@/utils/request'
export function loginAction(data) {
return request({
url: 'xxx', // 请求地址
data,
method: 'post'
})
}