二、Vite
1. Vite 动态导入的使用问题
使用 webpack 的同学应该都知道,在 webpack 中可以通过 require.context
动态导入文件:
// https://webpack.js.org/guides/dependency-management/ require.context('./test', false, /\.test\.js$/);
在 Vite 中,我们可以使用这两个方法来动态导入文件:
import.meta.glob
该方法匹配到的文件默认是懒加载,通过动态导入实现,构建时会分离独立的 chunk,是异步导入,返回的是 Promise,需要做异步操作,使用方式如下:
const Components = import.meta.glob('../components/**/*.vue'); // 转译后: const Components = { './components/a.vue': () => import('./components/a.vue'), './components/b.vue': () => import('./components/b.vue') }
import.meta.globEager
该方法是直接导入所有模块,并且是同步导入,返回结果直接通过 for...in
循环就可以操作,使用方式如下:
const Components = import.meta.globEager('../components/**/*.vue'); // 转译后: import * as __glob__0_0 from './components/a.vue' import * as __glob__0_1 from './components/b.vue' const modules = { './components/a.vue': __glob__0_0, './components/b.vue': __glob__0_1 }
如果仅仅使用异步导入 Vue3 组件,也可以直接使用 Vue3 defineAsyncComponent API 来加载:
// https://v3.cn.vuejs.org/api/global-api.html#defineasynccomponent import { defineAsyncComponent } from 'vue' const AsyncComp = defineAsyncComponent(() => import('./components/AsyncComponent.vue') ) app.component('async-component', AsyncComp)
2. Vite 配置 alias 类型别名
当项目比较复杂的时候,经常需要配置 alias 路径别名来简化一些代码:
import Home from '@/views/Home.vue'
在 Vite 中配置也很简单,只需要在 vite.config.ts
的 resolve.alias
中配置即可:
// vite.config.ts export default defineConfig({ base: './', resolve: { alias: { "@": path.join(__dirname, "./src") }, } // 省略其他配置 })
如果使用的是 TypeScript 时,编辑器会提示路径不存在的警告⚠️,这时候可以在 tsconfig.json
中添加 compilerOptions.paths
的配置:
{ "compilerOptions": { "paths": { "@/*": ["./src/*"] } } }
3. Vite 配置全局 scss
当我们需要使用 scss 配置的主题变量(如 $primary
)、mixin方法(如 @mixin lines
)等时,如:
<script setup lang="ts"> </script> <template> <div class="container"></div> </template> <style scoped lang="scss"> .container{ color: $primary; @include lines; } </style>
我们可以将 scss 主题配置文件,配置在 vite.config.ts
的 css.preprocessorOptions.scss.additionalData
中:
// vite.config.ts export default defineConfig({ base: './', css: { preprocessorOptions: { // 添加公共样式 scss: { additionalData: '@import "./src/style/style.scss";' } } }, plugins: [vue()] // 省略其他配置 })
如果不想使用 scss 配置文件,也可以直接写成 scss 代码:
export default defineConfig({ css: { preprocessorOptions: { scss: { additionalData: '$primary: #993300' } } } })
三、VueRouter
1. script-setup 模式下获取路由参数
由于在 script-setup
模式下,没有 this
可以使用,就不能直接通过 this.$router
或 this.$route
来获取路由参数和跳转路由。
当我们需要获取路由参数时,就可以使用 vue-router
提供的 useRoute
方法来获取,使用如下:
// A.vue <script setup lang="ts"> import { ref, onMounted } from 'vue'; import router from "@/router"; import { useRoute } from 'vue-router' let detailId = ref<string>(''); onMounted(() => { const route = useRoute(); detailId.value = route.params.id as string; // 获取参数 }) </script>
如果要做路由跳转,就可以使用 useRouter
方法的返回值去跳转:
const router = useRouter(); router.push({ name: 'search', query: {/**/}, })
四、Pinia
1. store 解构的变量修改后没有更新
当我们解构出 store 的变量后,再修改 store 上该变量的值,视图没有更新:
// A.vue <script setup lang="ts"> import componentStore from "@/store/component"; const componentStoreObj = componentStore(); let { name } = componentStoreObj; const changeName = () => { componentStoreObj.name = 'hello pingan8787'; } </script> <template> <span @click="changeName">{{name}}</span> </template>
这时候点击按钮触发 changeName
事件后,视图上的 name
并没有变化。这是因为 store 是个 reactive 对象,当进行解构后,会破坏它的响应性。所以我们不能直接进行解构。
这种情况就可以使用 Pinia 提供 storeToRefs
工具方法,使用起来也很简单,只需要将需要解构的对象通过 storeToRefs
方法包裹,其他逻辑不变:
// A.vue <script setup lang="ts"> import componentStore from "@/store/component"; import { storeToRefs } from 'pinia'; const componentStoreObj = componentStore(); let { name } = storeToRefs(componentStoreObj); // 使用 storeToRefs 包裹 const changeName = () => { componentStoreObj.name = 'hello pingan8787'; } </script> <template> <span @click="changeName">{{name}}</span> </template>
这样再修改其值,变更马上更新视图了。
2. Pinia 修改数据状态的方式
按照官网给的方案,目前有三种方式修改:
- 通过
store.属性名
赋值修改单笔数据的状态;
这个方法就是前面一节使用的:
const changeName = () => { componentStoreObj.name = 'hello pingan8787'; }
- 通过
$patch
方法修改多笔数据的状态;
当我们需要同时修改多笔数据的状态时,如果还是按照上面方法,可能要这么写:
const changeName = () => { componentStoreObj.name = 'hello pingan8787' componentStoreObj.age = '18' componentStoreObj.addr = 'xiamen' }
上面这么写也没什么问题,但是 Pinia 官网已经说明,使用 $patch
的效率会更高,性能更好,所以在修改躲避数据时,更推荐使用 $patch
,使用方式也很简单:
const changeName = () => { // 参数类型1:对象 componentStoreObj.$patch({ name: 'hello pingan8787', age: '18', addr: 'xiamen', }) // 参数类型2:方法,该方法接收 store 中的 state 作为参数 componentStoreObj.$patch(state => { state.name = 'hello pingan8787'; state.age = '18'; state.addr = 'xiamen'; }) }
- 通过
action
方法修改多笔数据的状态;
也可以在 store 中定义 actions 的一个方法来更新:
// store.ts import { defineStore } from 'pinia'; export default defineStore({ id: 'testStore', state: () => { return { name: 'pingan8787', age: '10', addr: 'fujian' } }, actions: { updateState(){ this.name = 'hello pingan8787'; this.age = '18'; this.addr = 'xiamen'; } } })
使用时:
const changeName = () => { componentStoreObj.updateState(); }
这三种方式都能更新 Pinia 中 store 的数据状态。
五、Element Plus
1. element-plus 打包时 @charset 警告
项目新安装的 element-plus 在开发阶段都是正常,没有提示任何警告,但是在打包过程中,控制台输出下面警告内容:
在官方 issues 中查阅很久:github.com/element-plu…。
尝试在 vite.config.ts
中配置 charset: false
,结果也是无效:
// vite.config.ts export default defineConfig({ css: { preprocessorOptions: { scss: { charset: false // 无效 } } } })
最后在官方的 issues 中找到处理方法:
// vite.config.ts // https://blog.csdn.net/u010059669/article/details/121808645 css: { postcss: { plugins: [ // 移除打包element时的@charset警告 { postcssPlugin: 'internal:charset-removal', AtRule: { charset: (atRule) => { if (atRule.name === 'charset') { atRule.remove(); } } } } ], }, }
2. 中文语言包配置
默认 elemnt-plus 的组件是英文状态:
我们可以通过引入中文语言包,并添加到 ElementPlus 配置中来切换成中文:
// main.ts // ... 省略其他 import ElementPlus from 'element-plus'; import 'element-plus/dist/index.css'; import locale from 'element-plus/lib/locale/lang/zh-cn'; // element-plus 中文语言包 app.use(ElementPlus, { locale }); // 配置中文语言包
这时候就能看到 ElementPlus 里面组件的文本变成中文了。
总结
以上是我最近从入门到实战 Vue3 全家桶的 3 个项目后总结避坑经验,其实很多都是文档中有介绍的,只是刚开始不熟悉。也希望大伙多看看文档咯~
Vue3 script-setup 模式确实越写越香。
本文内容如果有问题,欢迎大家一起评论讨论。