vue3+ts первый опыт

Vue.js

задний план

Прошло более полугода с тех пор, как вы выпустили vue3.0 в прошлом году. Не так давно высокопроизводительный You Da выпустил vite2.0, из-за которого я весь день беспокоился о программе для переноса кода, поэтому я хотел изучить и узнать о vue3 и узнать, какие изменения были сделаны по сравнению с vue2.0, и были добавлены новые дополнения, какие особенности. Сначала я прочитал руководство по миграции vue2.0 в целом, а затем начал исследование и обучение с создания простейшего todo mvc.

Создать проект

Я напрямую использовал структуру vite, предоставленную в документе по миграции, для создания проекта, и теперь все больше и больше разработчиков переходят на машинописный текст, поэтому я решил разрабатывать на основе машинописного текста.

определить маршруты

писать маршруты

Домашняя страница используется для отображения списка задач. Щелкните элемент списка, чтобы перейти на страницу сведений, чтобы отобразить сведения о задачах. Создайте новый каталог маршрутизатора следующим образом:

router
├─index.ts

В vue-router4.0 предусмотрено два способа создания истории, а именноcreateWebHistoryа такжеcreateWebHashHistory. Из-за использования машинописного кодирования при определении константы маршрутов вам необходимо объявить тип какArray<RouteRecordRaw>.

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import { defineAsyncComponent } from 'vue'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'home',
    component: defineAsyncComponent(() => import('../pages/home.vue'))
  },
  {
    path: '/detail',
    name: 'detail',
    component: defineAsyncComponent({
      loader: () => import('../pages/detail.vue'),
      delay: 200,
      timeout: 3000
    })
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

В Vue3.0, черезdefineAsyncComponent

Маршрутизация приложений

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(router)

app.mount('#app')

О настройке

В vue2.0 при написании кода компонента он основан на API опций, черезdata,watch,computed,methodsи другие различные варианты организации логики кода. Однако, когда компонент становится очень большим,логические проблемыСписок будет расти, что приведет к фрагментации логики.Например, когда мы фокусируемся на одной логической точке, мы будем часто переходить к различным блокам опций кода, что затрудняет чтение и понимание логики кода.

Поэтому в vue3.0 появился составной API. Его основная идея состоит в том, чтобы объединить код с одной и той же логической задачей, чтобы иметь возможность его использовать, вы должны предоставить место для его использования, поэтому появляется функция настройки.

Функция настройки выполняется перед созданием компонента и может принимать параметр props и параметр контекста. Как только реквизиты разрешены, они служат точкой входа в составной API.

Поскольку экземпляр компонента не был создан при выполнении установки, его нельзя использовать, поэтому доступ к свойствам, объявленным в компоненте, возможен только через свойства.

Поскольку функция настройки создана вокруг BeForecreate и функция управления крючком, поэтому в Vue3.0 в этих двух функциях не нужно, любой код, написанный в этих двух функциях, должен быть размещен непосредственно в функцию настройки.

Создание компонентов

Создайте новую папку pages в каталоге src и создайте под ней новую папкуhome.vueа такжеdetail.vue.

pages
├─home.vue
├─detail.vue

Напишите домашний компонент

Для todomvc элемент todo содержит как минимум два свойства — todo и state, поэтому определите интерфейс Todo:

interface Todo {
  text: string,
  done: boolean
}

Затем вы можете определить тип тодолиста, который является сложным типом данных. В Vue 3.0, если вы хотите определить основной тип данных в качестве ответа, вам необходимо использовать Ref; если вы хотите определить сложные типы данных в качестве ответа, вам необходимо использовать реактивный, поэтому используйте реактивный характер для определения типа массива объекта :

import { defineComponent, ref, reactive } from 'vue'

export default defineComponent({
  setup (props, context) {
    let todoList = reactive<Array<Todo>>([
      {
        text: 'learn js',
        done: true
      },
      {
        text: 'learn java',
        done: false
      }
    ])
  }
})

Поскольку вам нужно динамически добавлять элементы списка дел, вам нужно определить другой строковый тип данных для получения пользовательского ввода:

let inputText = ref('')

Ограничения типа могут быть добавлены здесь без явного указания, потому что typescript автоматически выведет тип как строку на основе параметров, полученных ref.

Todomvc неотделим от добавления и удаления, поэтому мы определяем два метода: addTodo и removeTodo.

const addTodo = () => {
  // 记住这里是 `inputText.value`,这是因为基本数据类型被包装成了一个响应式对象,此时只能通过 value 属性来使用它的值
  if (inputText.value.trim().length) {
    const item = {
      text: inputText.value,
      done: false
    }
    todoList.push(item)
    inputText.value = ''
  }
}
const removeTodo = (index: number) => {
  todoList.splice(index, 1)
}

Последний важный шаг - вернуть данные и методы, которые мы определены ранее в функции настройки, поскольку они используются, когда шаблон отображается:

setup (props, context) {
  // 此处省略...
  return {
    todoList,
    inputText,
    addTodo,
    removeTodo
  }
}

Подготовка компонентов детали

Детальный компонент очень прост, ему нужно только отобразить объект задачи, переданный при переходе маршрута, включая задачу и статус. Но перед прыжком нужноhome.vueОпределяет способ перехода по маршруту.

Используйте Vue-маршрутизатор 4.0

vue-router4.0 предоставляет новый метод использования, то есть при использовании маршрутизатора или маршрута нам нужно самим импортировать предоставленныеuseRouterа такжеuseRouteфункция.为了使用 router 进行路由跳转,需要手动 import。

// home.vue
import { useRouter, RouteLocationOptions } from 'vue-router'

export default defineComponent({
  setup (props, context) {
    let router = useRouter()
    // ...
    const jumpToDetail = (item: Todo) => {
      router.push({
        name: 'detail',
        params: {
          text: item.text,
          done: item.done
        }
      } as RouteLocationOptions)
    }

    return {
      todoList,
      inputText,
      addTodo,
      removeTodo,
      jumpToDetail,
      router
    }
  }
})

Следует отметить, что наш маршрутный переход выполнен в виде объекта push, поэтому нам нужно указать, что этот объект имеет тип RouteLocationOptions, который предоставляется vue-router. Если push является строкой, вам нужно указать тип строки как RouteLocationRaw.

Получить параметры маршрута

Как упоминалось выше, в vue-router4.0, если вы хотите получить объект маршрута, вам нужно использовать useRoute, поэтому вам нужно импортировать вручную.

<template>
  <div id="detail">
    <h2>将来要做什么: {{ todo.text }}</h2>
    <h2>状态:<input type="checkbox" v-model="checked"/>{{ todo.done ? '已完成' : '未完成' }}</h2>
  </div>
</template>
import { useRoute } from 'vue-router'
import { ref, reactive, watch, defineComponent } from 'vue'

export default defineComponent({
  name: 'detail',
  setup (props, context) {
    const route = useRoute()
    // 使用 params,刷新页面参数会丢失
    const done = route.params.done === 'true' ? true : false
    let todo = reactive({
      text: route.params.text,
      done
    })
    let checked = ref(todo.done)

    watch(checked, (newV, oldV) => {
      if (newV) {
        todo.done = true
      } else {
        todo.done = false
      }
    }, {
      deep: true
    })

    return {
      todo,
      route,
      checked
    }
  }
})

Здесь нужно использовать функцию наблюдения, чтобы, когда мы ставим галочку, состояние можно было вовремя изменить. В отличие от опции watch в vue2.0, watch в vue3.0 — это чистая функция, которую можно использовать несколько раз.В число получаемых параметров входят: цель прослушивания, функция обратного вызова и дополнительные параметры, а третий параметр получает объект. содержит варианты глубокого прослушивания и немедленного исполнения.

О getCurrentInstance для получения текущего экземпляра компонента

В vue2 после того, как мы создадим корневой экземпляр и смонтируем маршрутизатор, мы можем передать его в любой компонент позже.this.$router.xxxиспользовать маршрутизацию. Но в Vue3, поскольку функция установки работает вокруг BeForecreate и создает крючки, экземпляр компонента не может быть получен через это. Рекомендуемая практика является импортvue-routerизuseRouterа такжеuseRouteПерейти к маршруту означает получить параметры, переданные маршрутом.

Но в vue3 при условииgetCurrentInstanceФункция, как следует из названия, функцией этой функции является получение текущего экземпляра компонента. Затем я вызываю функцию и печатаю текущий экземпляр:

Нажмите, чтобы просмотреть сведения о CTX:

vue2ctx.$router.xxxиспользовать маршрутизацию, поэтому я попробовал следующее:

const { ctx } = getCurrentInstance()
console.log('current instance:', ins)

Но typescript сразу выводит ошибку типа:

Здесь ctx будет типом объединения, либоComponentInternalInstanceтип, который также может бытьnullТипы. мы можем видетьComponentInternalInstanceКакие типы объявлены в интерфейсе:

Это очень странно, почему я могуgetCurrentInstanceкак насчет ктх? Выяснилось, что vite по умолчанию использует среду разработки, что удобно для получения инстанса на этапе разработки, поэтому я переключился на среду разработки:

Я вижу, что в ctx ничего нет.routerобъект, поэтому передайте в производственной средеgetCurrentInstanceполучить экземпляр для полученияобъект маршрутизатора, поэтому получите экземпляр через `getCurrentInstance` в производственной среде.