Недавно я начал Vue3 и завершил 3 проекта.Я столкнулся с большим количеством проблем.Сегодня я потрачу немного времени, чтобы разобраться и поделиться с вами еще 15 общими проблемами.В основном, соответствующие адреса документов размещены.Пожалуйста, ознакомьтесь с другими документами ~
Завершено. Эти три проекта в основном разрабатываются с использованием Vue3 (режим сценария установки), поэтому они в основном сводятся к нескольким аспектам:

  • vue3
  • Вите
  • VueRouter
  • Пиния
  • ЭлементПлюс

Бао Гэ рассказывает о технологиях

, вроде 77

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 два свойства в свойстве и не дает возможности установить значение реквизита по умолчанию.schemamodelValuedefineProps

На самом деле мы можем добиться этого с помощью макроса 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);

Здесь есть еще один способ, который заключается в том, чтобы включить опцию « Включить пользовательские средства форматирования » на панели настроек консоли.

картина
изображение.png
картина
изображение.png

В это время вы обнаружите, 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 修改数据状态的方式

按照官网给的方案,目前有三种方式修改:

  1. 通过 store.属性名赋值修改单笔数据的状态;

这个方法就是前面一节使用的:

const changeName = () => {
  componentStoreObj.name = 'hello pingan8787';
}
  1. 通过 $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';
  })
}
  1. 通过 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 становится китайским.картина

Бао Гэ рассказывает о технологиях

, вроде 19

Подвести итог

Вышеизложенное — мой краткий обзор опыта обхода ямы после 3-х проектов от входа до реального боевого ведра семейства Vue3, На самом деле многие из них представлены в документе, но я не знаком с ним в начале. Я также надеюсь, что вы прочитаете больше документов~

Режим настройки сценария Vue3 действительно становится все более и более ароматным.