Recientemente, inicié Vue3 y completé 3 proyectos. Encontré muchos problemas. Hoy, me tomaré un tiempo para resolverlo y compartiré 15 problemas más comunes con usted. Básicamente, se publican las direcciones de los documentos correspondientes. Lea más documentos ~
Completado Los tres proyectos están desarrollados básicamente en Vue3 (modo setup-script), por lo que se resumen principalmente en varios aspectos:

  • vue3
  • Vite
  • VueRouter
  • Pinia
  • Elemento Plus

A Bao Ge charla sobre tecnología

, como 77

1. Vue3

1. Cambios en los métodos de ciclo de vida de Vue2.x y Vue3.x

Dirección del documento: https://v3.cn.vuejs.org/guide/composition-api-lifecycle-hooks.html

Los métodos del ciclo de vida de Vue2.x y Vue3.x han cambiado mucho. Echemos un vistazo primero:

2.x Ciclo de vida 3.x Ciclo de vida Descripción del tiempo de ejecución
antes de crear configuración Ejecutar antes de la creación del componente
creado configuración Ejecutar después de crear el componente
antes del monte onBeforeMount Ejecutado antes de que el componente se monte en el nodo
montado en Montado Ejecutar después de montar el componente
antes de Actualizar onBeforeUpdate Ejecutar antes de la actualización del componente
actualizado onActualizado Ejecutado después de que se completa la actualización del componente
antes de destruir onBeforeUnmount Ejecutar antes de que se desinstale el componente
destruido en Desmontado Ejecutar después de que se complete la desinstalación del componente
error capturado onErrorCaptured Activa la función de enlace al capturar una excepción de un componente descendiente

En la actualidad, Vue3.x todavía es compatible con el ciclo de vida de Vue2.x, pero no se recomienda mezclar y combinar. Puede usar el ciclo de vida de 2.x en la etapa inicial e intentar usar el ciclo de vida de 3 .x más tarde.

Como uso el script-srtupmodo , uso las funciones del ciclo de vida de Vue3.x directamente:

// A.vue
<script setup lang="ts">
import { ref, onMounted } from "vue";
let count = ref<number>(0);

onMounted(() => {
count.value = 1;
})
</script>

Para conocer el tiempo de ejecución de cada enlace, también puede consultar la documentación: https://v3.cn.vuejs.org/guide/instance.html#Lifecycle Diagram

2. En el modo de configuración de secuencias de comandos, el componente principal obtiene los datos del componente secundario

Dirección del documento: https://v3.cn.vuejs.org/api/sfc-script-setup.html#defineexpose

Aquí presentamos principalmente cómo el componente principal obtiene las variables definidas dentro del componente secundario. Para la comunicación entre los componentes principal y secundario, puede consultar la documentación para obtener más detalles: https://v3.cn.vuejs.org/guide/ componentes-básicos.html

Podemos usar la macro de la macro del compilador globaldefineExpose para exponer los parámetros en el componente secundario al componente principal y usar el {key: vlaue}método instancia del componente secundario a través del método ref de la plantilla y luego el valor correspondiente Puede ser obtenido:

// 子组件
<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>

Nota :

  • Las macros del compilador global solo se pueden usar en el modo de configuración de secuencias de comandos;
  • En el modo de configuración de secuencias de comandos, las macros importse de usarlas;
  • El modo de configuración de secuencias de comandos proporciona un total de 4 macros, que incluyen: defineProps, defineEmits, defineExpose, withDefaults.

3. Proporcione valores predeterminados para accesorios

documentación dedefinedProps: https://v3.cn.vuejs.org/api/sfc-script-setup.html#defineprops-%E5%92%8C-defineemitswithDefaults documentación: 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

Las cuatro macros globales del compilador proporcionadas por el modo de configuración de secuencias de comandos se han presentado anteriormente , pero no se han presentado en detalle.Esta sección presenta definePropsy withDefaults. El uso de definePropsmacros se puede utilizar para definir los parámetros de entrada de los componentes, de la siguiente manera:

<script setup lang="ts">
let props = defineProps<{
schema: AttrsValueObject;
modelValue: any;
}>();
</script>

La desventaja de esta declaración es que solo define los tipos propsde schemay modelValuedos propiedades en la propiedad definePropsNo proporciona una forma de establecer el valor predeterminado de props.

De hecho, podemos lograr esto a través de la macro withDefaults:

<script setup lang="ts">
let props = withDefaults(
defineProps<{
schema: AttrsValueObject;
modelValue: any;
}>(),
{
schema: [],
modelValue: ''
}
);
</script>

La función de ayuda withDefaults proporciona verificación de tipos para valores predeterminados y garantiza que el tipo de los accesorios devueltos elimine las marcas opcionales para las propiedades que han declarado valores predeterminados.

4. Configurar parámetros personalizados globales

Dirección del documento: https://v3.cn.vuejs.org/guide/migration/global-api.html#vue-prototype-%E6%9B%BF%E6%8D%A2%E4%B8%BA-config- propiedades globales

En Vue2.x podemos Vue.prototypeagregar propiedad de propiedad global. Pero en Vue3.x necesitas Vue.prototypereemplazar con config.globalPropertiesconfig:

// 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;

Al usarlo, primero debe obtener el objeto de instancia a través del getCurrentInstancemétodo :

// 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>

La salida de instancecontenido es la siguiente:imagen

5. Cambios en el modelo v

Dirección del documento: https://v3.cn.vuejs.org/guide/migration/v-model.html

Cuando estamos usando v-modeldirectivas , de hecho v-bindy taquigrafía v-oncombinada , existen diferencias entre Vue2.x y Vue3.x.

  • Vue2.x
<ChildComponent v-model="pageTitle" />

<!-- 是以下的简写: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />

En el componente secundario, si desea realizar un enlace de datos bidireccional en una propiedad, this.$emit('update:myPropName', newValue)puede actualizar el v-modelvalor enlazado pasando .

  • Vue3.x
<ChildComponent v-model="pageTitle" />

<!-- 是以下的简写: -->

<ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event"/>

script-setupEn modo, no puede usar this.$emitpara enviar eventos de actualización, después de todo, no existe thistal cosa. En este momento, necesita usar las dos macros de defineProps y defineEmits presentadas anteriormente para lograr:

// 子组件 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>

Cuando se usa el componente principal, es muy simple:

// 父组件 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. Los errores del entorno de desarrollo no son fáciles de solucionar

Dirección del documento: https://v3.cn.vuejs.org/api/application-config.html#errorhandler

Vue3.x genera avisos de aviso más amigables para algunas excepciones en el proceso de desarrollo, como el siguiente aviso:imagen

De esta forma, la fuente de la anormalidad puede ser informada con mayor claridad, y puede verse que el problema probablemente esté <ElInput 0=......aquí , pero no es lo suficientemente claro. En este momento, puede agregar el controlador de excepciones global proporcionado por Vue3.x para mostrar el contenido del error y la información de la pila de llamadas con mayor claridad. El código es el siguiente :

// main.ts
app.config.errorHandler = (err, vm, info) => {
    console.log('[全局异常]', err, vm, info)
}

En este punto, puede ver la salida de la siguiente manera:imagen

Se hizo más claro de inmediato. Por supuesto, este elemento de configuración también se puede utilizar para integrar los servicios de seguimiento de errores Sentry y Bugsnag. Lectura recomendada: ¿Cómo implementa Vue3 el manejo global de excepciones?

7. Observar los datos de ref no es intuitivo e inconveniente

Cuando mostramos la variable refdeclarada .

const count = ref<numer>(0);

console.log('[测试 ref]', count)

Verá que la consola emite un RefImplobjeto :imagen

Parece muy poco intuitivo. Todos sabemos que para obtener y modificar el valor de la variable refdeclarada , debe .valueobtenerla, por lo que también puede:

console.log('[测试 ref]', count.value);

Aquí hay otra forma, que es activar la opción " Habilitar formateadores personalizados " en el panel de configuración de la consola.

imagen
imagen.png
imagen
imagen.png

En este momento, encontrará que refel ha cambiado:

imagenMás claro e intuitivo.

Encontré este método en "Diseño e implementación de Vue.js", pero no encontré ninguna introducción relevante en el documento. Si un amigo lo encuentra, hágamelo saber ~

2. Invitar

1. El uso de la importación dinámica de Vite

Dirección del documento: https://cn.vitejs.dev/guide/features.html#glob-import

Los estudiantes que usan webpack deben saber que los archivos se pueden importar require.contextdinámicamente :

// https://webpack.js.org/guides/dependency-management/
require.context('./test'false, /\.test\.js$/);

En Vite, podemos importar archivos dinámicamente usando estos dos métodos:

  • import.meta.glob

El archivo que coincide con este método se carga de forma diferida de forma predeterminada , lo que se implementa a través de la importación dinámica . El fragmento independiente se separará durante la construcción , que es una importación asíncrona , y el retorno es Promesa. Necesita hacer una operación asíncrona. El uso es como sigue:

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

Este método es para importar directamente todos los módulos , y se importa sincrónicamente . El resultado devuelto se puede operar directamente a través del for...inciclo . El método de uso es el siguiente:

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
}

Si solo importa componentes de Vue3 de forma asíncrona, también puede usar la API de Vue3 defineAsyncComponent para cargarlos directamente:

// 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. Visite configurar alias tipo alias

Dirección del documento: https://cn.vitejs.dev/config/#resolve-alias

Cuando el proyecto es más complejo, a menudo es necesario configurar alias de ruta de alias para simplificar algo de código:

import Home from '@/views/Home.vue'

También es muy sencillo de configurar en Vite, solo configúralo vite.config.tsen resolve.alias:

// vite.config.ts
export default defineConfig({
  base: './',
  resolve: {
    alias: {
      "@": path.join(__dirname, "./src")
    },
  }
  // 省略其他配置
})

Si está utilizando TypeScript, el editor le mostrará una advertencia de que la ruta no existe⚠️, tsconfig.jsonpuede agregar compilerOptions.pathsla configuración en:

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
     }
  }
}

3. Visite configurar global scss

Dirección del documento: https://cn.vitejs.dev/config/#css-preprocessoroptions

Cuando necesitamos usar variables de tema configuradas con scss (p. ej $primary.), métodos de mezcla (p. ej @mixin lines.), etc., como:

<script setup lang="ts">
</script>
<template>
<div class="container"></div>
</template>

<style scoped lang="scss">
.container{
color: $primary;
@include lines;
}
</style>

Podemos configurar el archivo de configuración del tema scss en vite.config.tsel css.preprocessorOptions.scss.additionalData:

// vite.config.ts
export default defineConfig({
  base: './',
  css: {
    preprocessorOptions: {
      // 添加公共样式
      scss: {
        additionalData: '@import "./src/style/style.scss";'
      }

    }
  },
  plugins: [vue()]
  // 省略其他配置
})

Si no desea utilizar el archivo de configuración scss, también puede escribir el código scss directamente:

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '$primary: #993300'
      }
    }
  }
})

3. VueRouter

1. Obtener parámetros de enrutamiento en el modo de configuración de secuencias de comandos

Dirección del documento: https://router.vuejs.org/zh/guide/advanced/composition-api.html

Dado que en el script-setupmodo , no se thispuede usar, no puede usar directamente this.$routero this.$routepara obtener parámetros de enrutamiento y rutas de salto. Cuando necesitamos obtener parámetros de enrutamiento, podemos usar vue-routerel useRoutemétodo provisto para obtenerlo, de la siguiente manera:

// 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>

Si desea realizar saltos de enrutamiento, puede usar el valor de retorno del useRoutermétodo para saltar:

const router = useRouter();
router.push({
  name'search',
  query: {/**/},
})

4. Pinia

1. Las variables destruidas por la tienda no se actualizan después de la modificación

文档地址: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 在开发阶段都是正常,没有提示任何警告,但是在打包过程中,控制台输出下面警告内容:imagen

Consulte los problemas oficiales durante mucho tiempo: https://github.com/element-plus/element-plus/issues/3219.

vite.config.tsIntentando configurar charset: falseen , el resultado tampoco es válido:

// vite.config.ts
export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        charset: false // 无效
      }
    }
  }
})

Finalmente encontré la solución en los temas oficiales:

// 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. Configuración del paquete de idioma chino

Dirección del documento: https://element-plus.gitee.io/zh-CN/guide/i18n.html#%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AE

Por defecto, los componentes de elemnt-plus están en inglés:imagen

Podemos cambiar a chino introduciendo el paquete de idioma chino y agregándolo a la configuración de 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 }); // 配置中文语言包

En este momento, puede ver que el texto de los componentes en ElementPlus pasa a ser chino.imagen

A Bao Ge charla sobre tecnología

, como 19

Resumir

Lo anterior es mi resumen de la experiencia de evitar boxes después de 3 proyectos desde la entrada hasta el cubo de la familia Vue 3 de combate real De hecho, muchos de ellos se presentan en el documento, pero no estoy familiarizado con él al principio. También espero que lea más documentos ~

El modo de configuración del script Vue3 es, de hecho, cada vez más fragante.