Vite + Vue3 项目基操
2021年8月 · 预计阅读时间: 6 分钟
#
前言到现在 Vue3.0 正式发布快有一个年头了,同时后续也发布的更快的打包工具 Vite 也更新到了 2.x 版本,相信有很多小伙伴也已经在公司的新项目中应用起来,体验这一快感,下面这篇文章总结下项目中基于 Vite2.x + Vue3.0 常用小基操。
#
注册组件在开发项目多数情况少不了使用 UI 框架组件来快速开发搭建我们的页面,一般使用组件库时,为了减小包体积(tree-shshaking 技术),都采用按需加载的方式。
以 Vant 组件使用为例,通常我们会:
import { createApp } from "vue";import App from "./App.vue";
const app = createApp(App);
// 引入组件import { Progress, Picker, Popup, ....} from "vant";
// app.use() 注册app.use(Progress) .use(Picker) .use(Popup) ... .mount("#app");
简单的引用几个组件还好,看起来没有拉面条,想想就这样引入算了,如果有二十三十个就显得很长也会让 main.ts
越来越大,基于模块化开发思想,最好单独封装到一个配置文件中。
创建一个 config 文件夹下 vant.config.ts 文件名中
import { Progress, Picker, Popup, ....} from "vant";
const components = { Progress, Picker, Popup, ....}
// .use 内部会自动寻找 install 方法进行调用注册挂载const componentsInstallHandler = { install(app) { Object.keys(components).forEach(key => app.use(components[key])) }}
export default componentsInstallHandler
main.ts 中
// ...省略 createApp 引入
// 导入import vantComponentsInstall from "./config/vant.config";
app.use(vantComponentsInstall).mount("#app");
这样操作完是不是舒服多了~
#
组件全局注册自己编写了一个比如 <Header/>
组件想要全局使用,这时候可以调用 createApp() 返回的 component 函数进行全局注册
// ...省略 createApp 引入
import Header from "@/components/Header/Index.vue";
app.component("Header", Header);
#
过滤器在 3.x 中,过滤器已删除,不再支持。相反地,可以用方法调用或计算属性代替它。
<template> <div> {{ computedFunc("computed") }} </div></template><script lang="ts">import { computed, defineComponent } from "vue";export default defineComponent({ setup() { // 使用 闭包 接受传递参数 const computedFunc = () => { return (val) => val; }; return { computedFunc, }; },});</script>
#
全局过滤器比如多个组件中数据渲染值都需要解密后才能正确显示,不可能每一个组件里面都写 computed,这时候可以通过 全局属性 在所有组件中使用它:
// main.tsconst app = createApp(App)
app.config.globalProperties.$filters = { decrypt(value) { return mdDecrypt(value)},
然后,就可以通过 $filters
对象修改所有的模板:
<template> <p>{{ $filters.decrypt(encryptVal) }}</p></template>
#
自定义 UI 框架主题色每个设计师采用的主题色不一样,UI 框架默认的主题色不是我们想要的,想必我们需要自定义配置一下。下面以 Vant 为例:
import { defineConfig } from 'vite'
export default defineConfig({ css: { preprocessorOptions: { less: { javascriptEnabled: true, // 需要自定义样式变量名称 modifyVars: { 'blue': '#00C6B8', 'gray-7': '#333333', 'gray-4': '#00C6B8', ... }, }, }, },})
#
服务代理 server在开发测试调用后端服务地址,少不了需要 server 代理,Vue2.x 配合 Vue-CLI 可以在 config/index.js 添加 proxyTable
修改添加, Vite 中又是怎么添加的呢,Vite 官网也有说明,下面看看如何配置:
import { defineConfig } from 'vite'
export default defineConfig({ server: { port: 3006, // 设置端口,默认 3000 open: true, // 设置启动时,自动打开浏览器 cors: true, // 允许跨域 host:'10.x.x.x', // 本地 IP 访问,如何移动端手机调试就需要填写本地 IP 了 proxy: { '/api': { target: 'http://10.x.x.x.x', changeOrigin: true, secure: false, rewrite: path => path.replace('/api/', '/') } // 配置多个继续添加 '/api2': { ... } }, },})
#
配置别名写法有很多种,可以是一个对象,或一个 { find, replacement }
具体可以看 options
import vue from '@vitejs/plugin-vue'import { defineConfig } from 'vite'
export default defineConfig({ resolve: { alias: [ { find: "@", replacement: resolve(__dirname, "src"), }, { find: "@ASS", replacement: resolve(__dirname, "src/assets"), }, ] },
server: { .... },})
#
移动端 rem 自适应结合使用 amfe-flexible
和 postcss-pxtorem
实现移动端适配。
amfe-flexible
amfe-flexible
是配置克伸缩布局方案,主要将 1rem 设为 viewWidth/10。
postcss-pxtorem
postcss-pxtorem 是 postcss 的插件,用于将像素单位生成 rem 单位。
npm 安装
npm i amfe-flexible -Dnpm i postcss-pxtorem -D
安装完毕后,在 main.ts 中导入 amfe-flexible 插件
// ...import "amfe-flexible/index.js";
postcss-pxtorem 配置
配置 postcss-pxtorem 方式有三种,可以在 vite.config.ts、.postcssrc.ts、postcss.copnfig.ts 其中之一配置即可。
vite.config.ts 中配置如下:
import { defineConfig } from 'vite'
export default defineConfig({ css: { ... , loaderOptions: { postcss: { plugins: [ require('postcss-pxtorem')({ rootValue: 37.5, // 根据设计稿宽度除以10进行设置,这边假设设计稿为375,即rootValue设为37.5,我这里设置 propList: ['*'] // // propList是设置需要转换的属性,这边*为所有都进行转换。 }) ] } } },})
根目录创建 .postcssrc.ts 或者 postcss.copnfig.ts 配置如下:
module.exports = { plugins: { autoprefixer: { overrideBrowserslist: [ 'Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8', 'last 10 versions', // 所有主流浏览器最近10版本用 ], }, postcss-pxtorem: { rootValue: 37.5, propList: ['*'], }, },}
配置完毕重启看看效果:
还可以 引入 rem.js 方式,不过需要编写样式的时候自己 px 转 rem ,好在 vscode 扩展商店中有 px to rem 插件一件转换,这里展开了,可以自行 google 一下就有了。
#
环境变量有时候项目开发中需要部署有三种运行环境中比如 测试环境(test)、开发环境(dev)、生成环境(prod),请求的接口地址也需要更换,如何动态的更换呢? Vite 在一个特殊的 import.meta.env
对象上暴露环境变量,可以通过暴露的环境变量来判断是运行在哪一个环境中。
npm run dev 运行 import.meta.env
暴露的变量值为 development
npm run build import.meta.env
暴露的变量值为 production
还有一种 测试环境(test)如何拿到呢?
可以通过 package.json 配置 scripts run 命令
"scripts": { "dev": "vite", "build": "vite build", "test:build": "vite build --mode test", },
运行 npm run test:build import.meta.env
暴露变量值为 test 了 --mode xxx 名称可以自定义
请求 URL 统一管理 config.ts:
export const DEV_BASE_URL = "https://xxx";export const TEST_BASE_URL = "https://xxx";export const PRO_BASE_URL = "https://xxx";
以封装 axios
设置 baseURL 为例:
import { DEV_BASE_URL, PRO_BASE_URL, TEST_BASE_URL, TIMEOUT } from "./config";
const envMap = { development: DEV_BASE_URL, production: PRO_BASE_URL, test: TEST_BASE_URL,};
const base_URL = envMap[import.meta.env.MODE];
// axiosconst instance = axios.create({ baseURL: base_URL, timeout: 30000,});
#
去除 console.log 和 vconsole 调试在 vue-cli 中我们可以配置 configureWebpack
去除,来看 vite 如何根据在生成环境去除的:
//...importimport { defineConfig, loadEnv, ConfigEnv, UserConfigExport } from "vite";
export default ({ command, mode }): UserConfigExport => { const isEnvProduction = mode === "production" return defineConfig({ //...其他配置 build: { terserOptions: { compress: { drop_console: isEnvProduction, // true 为关闭 drop_debugger: isEnvProduction, }, }, }, });};
在配置文件中我们无法通过 import.meta.env
获取到运行环境变量,因为它是暴露再客户端源码中的,所以我们可以通过返回一个函数的方式,在函数接收的 mode
参数中获取到环境变量。
#
hooks 使用升级 Vue3 后,让人最脑壳痛应该不是 Compostion API 语法,而是提供了给我们全新的组织代码的思维方式。
刚开始 Vue3.0 正式发布学习完之后,代码也是遵循 Compostion API 正确用法,可是当组件庞大,逻辑复杂,代码量也是很多,发现比 Options API 写法维护性更差,也是慢慢写成了 拉面条...
下面结合真实业务需求讲解一下
比如有一个获取验证码方法,点击获取完毕 60s 倒计时后才能重新获取,正常霹雳巴拉我们会这么写:
example.vue
<template> <button @click="onGetCode">{{ codeText }}</button></template><script lang="ts">import { defineComponent, reactive, toRefs } from "vue";export default defineComponent({ setup(props, ctx) { const state = reactive({ isGetCode: false, codeText: "获取验证码", countDown: 60, phone: "", }); // 点击后倒计时 const countTime = () => { state.isGetCode = true; let timer; timer = setInterval( (function setIntervalFunc() { if (countDown !== 0) { state.codeText = `重新发送${countDown.value--}`; } else { state.isGetCode = false; state.codeText = "获取验证码"; state.countDown = 60; clearInterval(timer); } return setIntervalFunc; })(), 1000 ); }; // 获取验证码请求 const onGetCode = async () => { const { success, message } = await getPhoneCode(encrypt(state.phone)); if (success) { countTime(); Toast.success(message); } }; return { ...toRefs(state), onGetCode, }; },});</script>
从新上面代码看没有问题啊,Compostion API 不就是这么用啊,梭哈完毕,可是再想想,如何这个页面只是其中一个逻辑,还有十个逻辑,都内聚在一起,依次继续写在后面,写到后面不就是我们所说的拉面条了吗?
所以可以单独将逻辑拆分到一个文件中
// useVerificationCode.jsimport { reactive, toRefs } from "vue";
export default const useVerificationCode = () => { const state = reactive({ isGetCode: false, codeText: '获取验证码', countDown: 60, phone: '' }) // 点击后倒计时 const countTime = () => { state.isGetCode = true let timer timer = setInterval( (function setIntervalFunc() { if (countDown !== 0) { state.codeText = `重新发送${countDown.value--}` } else { state.isGetCode = false state.codeText = '获取验证码' state.countDown = 60 clearInterval(timer) } return setIntervalFunc })(), 1000 ) } // 获取验证码请求 const onGetCode = async () => { const { success, message } = await getPhoneCode(encrypt(state.phone)) if (success) { countTime() Toast.success(message) } } return { ...toRefs(state) onGetCode, }}
再看看改造后:
// example.vue<template> <button @click="onGetCode">{{ codeText }}</button></template><script lang="ts">import { defineComponent } from "vue";// 引入 useVerificationCode.jsimport useVerificationCode from "./useVerificationCode.js";export default defineComponent({ setup(props, ctx) { // 获取验证码 const { onGetCode, isGetCode, codeText, countDown, phone } = useVerificationCode();
// ... // ... return { onGetCode, isGetCode, codeText, countDown, phone, }; },});</script>
这样是不是清爽许多了,以前错误的观点,就是只有复用的逻辑才应该封装到 hook 中,在 官方的给例子 中,并不是强调逻辑复用,而是逻辑关注点分离,到这里相信大家都明白该怎么写了。但是在简单逻辑的组件中,过度抽离就没必要了。
总结
目前自己应用 Vue3.0 开发中的小项目中还没有遇到过大坑,面向百度、文档都能轻松解决,赶紧用起来吧,两个字 好使,Vite 编译是真的快~,尤大给力( ̄ ▽  ̄)。