Недавно я начал Vue3 и завершил 3 проекта.Я столкнулся с большим количеством проблем.Сегодня я потрачу немного времени, чтобы разобраться и поделиться с вами еще 15 общими проблемами.В основном, соответствующие адреса документов размещены.Пожалуйста, ознакомьтесь с другими документами ~
Завершено. Эти три проекта в основном разрабатываются с использованием Vue3 (режим сценария установки), поэтому они в основном сводятся к нескольким аспектам:
-
vue3 -
Вите -
VueRouter -
Пиния -
ЭлементПлюс
1. Вью3
1. Изменения в методах жизненного цикла Vue2.x и Vue3.x
Адрес документа: https://v3.cn.vuejs.org/guide/composition-api-lifecycle-hooks.html
Методы жизненного цикла Vue2.x и Vue3.x сильно изменились.Давайте сначала посмотрим:
2.x Жизненный цикл | 3.x Жизненный цикл | Описание времени выполнения |
---|---|---|
перед созданием | настраивать | Выполнить перед созданием компонента |
созданный | настраивать | Выполнить после создания компонента |
перед креплением | onBeforeMount | Выполняется до того, как компонент будет смонтирован на узле |
смонтированный | onMounted | Выполнить после монтирования компонента |
перед обновлением | onBeforeUpdate | Выполнить перед обновлением компонента |
обновлен | onОбновлено | Выполняется после завершения обновления компонента |
перед уничтожением | onBeforeUnmount | Выполнить перед удалением компонента |
уничтожен | onUnmounted | Выполнить после завершения удаления компонента |
ошибка захвачена | onErrorCaptured | Активирует функцию ловушки при перехвате исключения из компонента-потомка |
В настоящее время Vue3.x все еще поддерживает жизненный цикл Vue2.x, но не рекомендуется смешивать и сочетать.Вы можете использовать жизненный цикл 2.x на ранней стадии и попытаться использовать жизненный цикл 3 .x позже.
Поскольку я использую script-srtup
режим , я напрямую использую функции жизненного цикла Vue3.x:
// A.vue
<script setup lang="ts">
import { ref, onMounted } from "vue";
let count = ref<number>(0);
onMounted(() => {
count.value = 1;
})
</script>
Время выполнения каждого хука вы также можете посмотреть в документации: https://v3.cn.vuejs.org/guide/instance.html#Lifecycle Diagram.
2. В режиме настройки скрипта родительский компонент получает данные дочернего компонента
Адрес документа: https://v3.cn.vuejs.org/api/sfc-script-setup.html#defineexpose
Здесь мы в основном показываем, как родительский компонент получает переменные, определенные внутри дочернего компонента.Для связи между родительским и дочерним компонентами вы можете обратиться к документации для получения более подробной информации: https://v3.cn.vuejs.org/guide/ компонент-basics.html
Мы можем использовать макрос глобального макроса компилятораdefineExpose
, чтобы предоставить параметры в дочернем компоненте родительскому компоненту и использовать {key: vlaue}
метод экземпляр дочернего компонента через метод ref шаблона, а затем соответствующее значение может быть получен:
// 子组件
<script setup>
let name = ref("pingan8787")
defineExpose({ name }); // 显式暴露的数据,父组件才可以获取
</script>
// 父组件
<Chlid ref="child"></Chlid>
<script setup>
let child = ref(null)
child.value.name //获取子组件中 name 的值为 pingan8787
</script>
Примечание :
-
Глобальные макросы компилятора можно использовать только в режиме настройки скрипта; -
В режиме настройки сценария макросы import
можно их использования; -
Режим настройки скрипта предоставляет всего 4 макроса, в том числе: defineProps, defineEmits, defineExpose, withDefaults.
3. Укажите значения по умолчанию для реквизита
Документация по определенным свойствам: https://v3.cn.vuejs.org/api/sfc-script-setup.html#defineprops-%E5%92%8C-defineemitswithDefaults документация: https://v3.cn.vuejs.org/api /sfc-script-setup.html#%E4%BB%85%E9%99%90-typescript-%E7%9A%84%E5%8A%9F%E8%83%BD
Четыре глобальных макроса компилятора , предоставляемые режимом настройки сценариев, были представлены ранее , но подробно о них не defineProps
рассказывалось withDefaults
. Использование defineProps
макросов можно использовать для определения входных параметров компонентов следующим образом:
<script setup lang="ts">
let props = defineProps<{
schema: AttrsValueObject;
modelValue: any;
}>();
</script>
Недостатком этого объявления является то, что оно определяет только типы иprops
два свойства в свойстве и не дает возможности установить значение реквизита по умолчанию.schema
modelValue
defineProps
На самом деле мы можем добиться этого с помощью макроса withDefaults:
<script setup lang="ts">
let props = withDefaults(
defineProps<{
schema: AttrsValueObject;
modelValue: any;
}>(),
{
schema: [],
modelValue: ''
}
);
</script>
Вспомогательная функция withDefaults обеспечивает проверку типов для значений по умолчанию и гарантирует, что тип возвращаемых реквизитов удаляет необязательные флаги для свойств, для которых объявлены значения по умолчанию.
4. Настройте глобальные пользовательские параметры
Адрес документа: https://v3.cn.vuejs.org/guide/migration/global-api.html#vue-prototype-%E6%9B%BF%E6%8D%A2%E4%B8%BA-config- глобальные свойства
В Vue2.x мы можем Vue.prototype
добавить свойство global property. Но в Vue3.x нужно Vue.prototype
заменить config.globalProperties
:
// Vue2.x
Vue.prototype.$api = axios;
Vue.prototype.$eventBus = eventBus;
// Vue3.x
const app = createApp({})
app.config.globalProperties.$api = axios;
app.config.globalProperties.$eventBus = eventBus;
При его использовании вам необходимо сначала получить объект экземпляра с помощью getCurrentInstance
метода :
// A.vue
<script setup lang="ts">
import { ref, onMounted, getCurrentInstance } from "vue";
onMounted(() => {
const instance = <any>getCurrentInstance();
const { $api, $eventBus } = instance.appContext.config.globalProperties;
// do something
})
</script>
instance
Вывод содержимого выглядит следующим образом:
5. изменения v-модели
Адрес документа: https://v3.cn.vuejs.org/guide/migration/v-model.html
Когда мы используем v-model
директивы , фактически v-bind
и v-on
комбинированное сокращение, существуют различия между Vue2.x и Vue3.x.
-
Vue2.x
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
В дочернем компоненте, если вы хотите выполнить двустороннюю привязку данных к свойству, вы this.$emit('update:myPropName', newValue)
можете обновить v-model
связанное значение, передав .
-
Vue3.x
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event"/>
script-setup
В режиме вы не можете использовать this.$emit
для отправки событий обновления, в конце концов, this
такого нет.В это время вам нужно использовать два макроса defineProps и defineEmits, представленные ранее, чтобы добиться:
// 子组件 child.vue
// 文档:https://v3.cn.vuejs.org/api/sfc-script-setup.html#defineprops-%E5%92%8C-defineemits
<script setup lang="ts">
import { ref, onMounted, watch } from "vue";
const emit = defineEmits(['update:modelValue']); // 定义需要派发的事件名称
let curValue = ref('');
let props = withDefaults(defineProps<{
modelValue: string;
}>(), {
modelValue: '',
})
onMounted(() => {
// 先将 v-model 传入的 modelValue 保存
curValue.value = props.modelValue;
})
watch(curValue, (newVal, oldVal) => {
// 当 curValue 变化,则通过 emit 派发更新
emit('update:modelValue', newVal)
})
</script>
<template>
<div></div>
</template>
<style lang="scss" scoped></style>
При использовании родительского компонента это очень просто:
// 父组件 father.vue
<script setup lang="ts">
import { ref, onMounted, watch } from "vue";
let curValue = ref('');
watch(curValue, (newVal, oldVal) => {
console.log('[curValue 发生变化]', newVal)
})
</script>
<template>
<Child v-model='curValue'></Child>
</template>
<style lang="scss" scoped></style>
6. Ошибки среды разработки нелегко устранить
Адрес документа: https://v3.cn.vuejs.org/api/application-config.html#errorhandler
Vue3.x делает более понятные подсказки для некоторых исключений в процессе разработки, например следующее приглашение:
Таким образом, источник исключения может быть более четко информирован, и видно, что проблема, вероятно, <ElInput 0=......
здесь , но недостаточно ясна. В настоящее время вы можете добавить глобальный обработчик исключений , предоставляемый Vue3.x, для более четкого вывода содержимого ошибки и информации о стеке вызовов.Код выглядит следующим образом :
// main.ts
app.config.errorHandler = (err, vm, info) => {
console.log('[全局异常]', err, vm, info)
}
На этом этапе вы можете увидеть вывод следующим образом:
Сразу стало понятнее. Конечно, этот элемент конфигурации также можно использовать для интеграции сервисов отслеживания ошибок Sentry и Bugsnag. Рекомендуемая литература: как Vue3 реализует глобальную обработку исключений?
7. Наблюдать за данными рефери не интуитивно и неудобно
Когда мы выводим ref
объявленную .
const count = ref<numer>(0);
console.log('[测试 ref]', count)
Вы увидите, что консоль выводит RefImpl
объект :
Это кажется очень неинтуитивным. Мы все знаем, что для получения и изменения значения ref
объявленной переменной вам нужно .value
ее получить, поэтому вы также можете:
console.log('[测试 ref]', count.value);
Здесь есть еще один способ, который заключается в том, чтобы включить опцию « Включить пользовательские средства форматирования » на панели настроек консоли.


В это время вы обнаружите, ref
что изменился:
Более понятный и интуитивно понятный.
Я нашел этот метод в "Проектирование и реализация Vue.js", но не нашел в документе подходящего введения. Если друг найдет его, дайте мне знать~
2. Вите
1. Использование динамического импорта Vite
Адрес документа: https://cn.vitejs.dev/guide/features.html#glob-import
Учащиеся, использующие webpack, должны знать, что файлы можно импортировать в webpack require.context
динамически :
// https://webpack.js.org/guides/dependency-management/
require.context('./test', false, /\.test\.js$/);
В Vite мы можем динамически импортировать файлы, используя эти два метода:
-
import.meta.glob
Файл, соответствующий этому методу, по умолчанию загружается отложенно , что реализовано с помощью динамического импорта . Независимый фрагмент будет отделен во время построения , что является асинхронным импортом , а возврат будет 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 настроить псевдоним типа псевдонима
Адрес документа: https://cn.vitejs.dev/config/#resolve-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
Адрес документа: https://cn.vitejs.dev/config/#css-preprocessoroptions
Когда нам нужно использовать переменные темы, настроенные с помощью scss (например $primary
), методы примесей (например @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'
}
}
}
})
3. ВьюРоутер
1. Получить параметры маршрутизации в режиме настройки скрипта
Адрес документа: https://router.vuejs.org/zh/guide/advanced/composition-api.html
Так как в 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: {/**/},
})
4. Пиния
1. Переменные, уничтоженные хранилищем, не обновляются после модификации
文档地址:https://pinia.vuejs.org/core-concepts/#using-the-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
方法修改多笔数据的状态;
文档地址:https://pinia.vuejs.org/api/interfaces/pinia._StoreWithState.html#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 在开发阶段都是正常,没有提示任何警告,但是在打包过程中,控制台输出下面警告内容:
Загляните в официальные выпуски давно: https://github.com/element-plus/element-plus/issues/3219.
vite.config.ts
Пытаюсь настроить charset: false
в , тоже неверный результат:
// vite.config.ts
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
charset: false // 无效
}
}
}
})
Наконец нашел решение в официальных вопросах:
// 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. Конфигурация китайского языкового пакета
Адрес документа: https://element-plus.gitee.io/zh-CN/guide/i18n.html#%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AE
По умолчанию компоненты 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 становится китайским.
Подвести итог
Вышеизложенное — мой краткий обзор опыта обхода ямы после 3-х проектов от входа до реального боевого ведра семейства Vue3, На самом деле многие из них представлены в документе, но я не знаком с ним в начале. Я также надеюсь, что вы прочитаете больше документов~
Режим настройки сценария Vue3 действительно становится все более и более ароматным.