Окончательный опыт разработки!Vite + Vue 3 + tsx полное руководство

Vue.js

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

Демонстрация связанных функций, которые были реализованы

  • typescript
  • Vue3 большинство примеров синтаксиса
  • конфигурация строительных лесов vite
  • режим разработки tx
  • less
  • router
  • vuex
  • Element-plus

Комбинируя вышеуказанные функции с axios, я думаю, что для большинства потребностей небольшого проекта достаточно.Если вы заинтересованы, вы можете разветвить код, чтобы испытать его, и вы также можете отправить вопрос для общения.

Почему проект vue отказывается от метода написания SFC и пытается кодировать метод jsx/tsx?

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

Почему я рекомендую использовать JSX для разработки Vue3

Конечно, это тоже дело вкуса, ведь у каждого свои предпочтения и предубеждения — это нормально, но еще одна идея и еще один план могут также дать еще один выбор для последующей реализации бизнеса.

Зачем писать это демо?

В нынешнем основном интерфейсном фреймворке мне нравится двусторонний механизм привязки данных vue и гибкость синтаксиса jsx, но в эпоху vue2 не удалось добиться идеального сочетания, поэтому я всегда был сожаление.

До появления vue3 все больше и больше библиотек компонентов пользовательского интерфейса с открытым исходным кодом используют tsx для реализации связанных бизнес-компонентов, и экология становится все более и более зрелой, поэтому я быстро испытал это, пока бизнес компании не был тяжелым, и закончил опыт После этого мне стало очень комфортно, и я, кажется, нашел идеальный метод кодирования кадров (конечно, это совершенство только для меня, ведь всеми любимый фреймворк, методы кодирования разные).

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

Далее я объясню порядок создания проекта и каждый функциональный модуль один за другим.

Демонстрационный анализ

Основные зависимости:

  1. vue@^3.0.5
  2. vite@^2.3.5
  3. vue-router@4.0.8
  4. vuex@4.0.1
  5. typescript@^4.1.3
  6. less@^4.1.1
  7. @vitejs/plugin-vue-jsx@^1.1.5
  8. element-plus@^1.0.2-beta.46

создание проекта

Готов к работе

  1. обязательно установитеyarn
$ npm install yarn -g
  1. обязательно установитеviteстроительные леса
$ npm install -g create-vite-app
# or
$ yarn add -g create-vite-app

Создайте

$ npm init @vitejs/app
# or
$ yarn create @vitejs/app

Затем вы хотите ввести название проекта, и после нажатия Enter вам будут предложены варианты выбора пресета шаблона:

1.png

Видно, что пресетов много, на официальном сайте приведены поддерживаемые на данный момент шаблоны пресетов:

  • vanilla
  • vanilla-ts
  • vue
  • vue-ts
  • react
  • react-ts
  • preact
  • preact-ts
  • lit-element
  • lit-element-ts
  • svelte
  • svelte-ts

Здесь мы выбираемvue

Тогда давайте тогда выберемvue-ts, после возврата вагона строительные леса помогут нам построить проект.

2.png

Вот структура каталогов после завершения сборки:

│  ├─public # 静态资源目录
│  │      favicon.ico 
│  │
│  ├─src
│  │  │  App.vue # 入口vue文件
│  │  │  main.ts # 入口文件
│  │  │  shims-vue.d.ts # vue文件模块声明文件
│  │  │  vite-env.d.ts # vite环境变量声明文件
│  │  │
│  │  ├─assets # 资源文件目录
│  │  │      logo.png
│  │  │
│  │  └─components # 组件文件目录
│  │         HelloWorld.vue
│  │
│  │ .gitignore
│  │ index.html # Vite项目的入口文件 
│  │ package.json
│  │ README.md
│  │ tsconfig.json # tsconfig配置文件
│  │ vite.config.ts # vite配置文件

Из приведенной выше структуры каталогов вы можете видеть, что структура каталогов проекта, созданная с помощью шаблонов vite, по-прежнему очень понятна.После установки зависимостей запуститеyarn devЗатем вы можете увидеть, на что похож запущенный проект. Конечно, такая конфигурация далека от того, что нам нужно, поэтому нам нужно внести изменения отсюда.

Реновация проекта

настроить eslint

Добавьте eslint для стандартизацииTypescriptтак же какvueКод, сначала установите соответствующие зависимости:

yarn add eslint eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin -D

Роли этих трех зависимостей:

  • eslint: основной код ESLint
  • eslint-plugin-vue: плагин ESLint для определения спецификаций кода vue.
  • @typescript-eslint/parser: синтаксический анализатор ESLint для анализа машинописного текста для проверки и нормализации кода машинописного текста.
  • @typescript-eslint/eslint-plugin: это плагин ESLint, который содержит различные определенные спецификации для обнаружения кода Typescript.

Сочетание Prettier и Eslint

yarn add prettier eslint-config-prettier eslint-plugin-prettier -D

в:

  • красивее: основной код более красивого плагина
  • eslint-config-prettier: Разрешить конфликт между спецификацией стиля в ESLint и спецификацией стиля в prettier. Спецификация стиля в prettier будет преобладать, а спецификация стиля в ESLint будет автоматически признана недействительной.
  • eslint-plugin-prettier: использовать красивее в качестве спецификации ESLint

После установки зависимостей можно приступить к настройке связанных файлов.Сначала добавьте новый в корневую директорию проекта..eslintrc.jsдокумент

Это место встретило яму, еслиeslint-config-prettierЕсли номер версии выше 8.0.0, то.eslintrc.jsБольше не нужно добавлять в конфигурацию расширения.'prettier/@typescript-eslint'Эта конфигурация, иначе выполнение eslint сообщит об ошибке

module.exports = {
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser', // Specifies the ESLint parser
    ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
    sourceType: 'module', // Allows for the use of imports
    ecmaFeatures: {
      // Allows for the parsing of JSX
      jsx: true
    }
  },
  extends: [
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended', 
    'plugin:prettier/recommended'
  ],
  rules: {}
}

с последующим увеличениемprettierКонфигурация, а затем добавить в корневую директорию проекта.prettierrc.jsдокумент

// 具体配置可以参考 https://prettier.io/docs/en/options.html
module.exports = {
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: false, // 未尾逗号
  vueIndentScriptAndStyle: true,
  singleQuote: true, // 单引号
  quoteProps: 'as-needed',
  bracketSpacing: true,
  trailingComma: 'none', // 未尾分号
  jsxBracketSameLine: false,
  jsxSingleQuote: false,
  arrowParens: 'always',
  insertPragma: false,
  requirePragma: false,
  proseWrap: 'never',
  htmlWhitespaceSensitivity: 'strict',
  endOfLine: 'lf'
};

Для этого в дополнение к конфигурации скрипта был настроен eslint, и теперь вам нужно толькоpackage.jsonПосле настройки команды скрипта в , настройка всего eslint завершена.

{
  ...
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc --noEmit --skipLibCheck && vite build", // 增加skipLibCheck可以跳过引入库的ts检查
    "serve": "vite preview",
    "lint": "eslint src",
    "lint:fix": "eslint src --fix --ext .ts,.tsx"
  },
}

Теперь давайте попробуем результаты обнаружения eslint:

5.png

Вы видите, что eslint успешно выполнил проверку кода, после чего мы автоматически исправляем это:

6.png

Автоматическое восстановление eslint также проходит успешно, пока настройка eslint завершена.

поддержка тсх

Во-первых, вам нужно установить официально поддерживаемый плагин vite.@vitejs/plugin-vue-jsx, ядро ​​этого плагина на самом деле@vue/babel-plugin-jsx, просто инкапсулирует слой в этом плагине для вызова плагина vite. Итак, что касается спецификации синтаксиса jsx для vue, вы можете напрямую обратиться к ней.@vue/babel-plugin-jsx, ссылка на документ выглядит следующим образом, рекомендуется сначала прочитать спецификацию грамматики. Официальное письмо более подробное, и я также объясню использование большинства спецификаций в сочетании с реальной ситуацией.спецификация синтаксиса vue jsx.

$ npm install @vitejs/plugin-vue-jsx -D
# or
$ yarn add @vitejs/plugin-vue-jsx -D

После установки вvite.config.tsЧтобы использовать плагин, код выглядит следующим образом:

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";

export default defineConfig({
  plugins: [
    vue(),
    vueJsx() //插件使用
  ],
});

Затем вы можете поместить каталог вapp.vue,HelloWorld.vueтак же какshims.vue.d.tsЭти три файла удаляются, потому что нам нужно только позже написать файл tsx.

Затем добавьте файл App.tsx в каталог src и напишите следующий код:

import { defineComponent } from 'vue'

export default defineComponent({
    setup() {
        return () => <div>hello world</div> //写一个 hello world祭天
    }
})

затем повторитеyarn devВы можете увидеть знакомый привет мир на странице. Да, вы правильно прочитали, это так просто.

Вот столкнулся с небольшой проблемой на этот раз, то есть занят порт 3000. Как настроить конфигурацию портов vite в это время, собственно на официальном сайте vite написано более понятно, т.к.vite.config.tsТакже есть подсказки связанных типов, поэтому проблема решается быстро, вvite.config.tsДобавьте объект serve на сервер и установите порт.Конфигурация выглядит следующим образом:

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";

export default defineConfig({
  plugins: [
    vue(),
    vueJsx() //插件使用
  ],
  server: {
    port: 8888
  }
});

маленький наконечник

Также хлопотно каждый раз писать шаблон tsx для режима vue3.Здесь я предлагаю вам добавить собственный фрагмент кода, если вы используете vscode.Это шаблон, который я использую каждый день:

{
	"Print to console": {
      "prefix": "vuetsx",
      "body": [
			"import { defineComponent } from 'vue'\n",
      "export default defineComponent({",
      "    props: {},",
			"    emits: [],",
			"    components: {},",
			"    setup(props, ctx) {",
			"        return () => <div></div>",
			"    }",
      "})",
      ],
      "description": "Create vue template"
    }
}

Настройка псевдонимов путей

Псевдонимы также должны быть вvite.config.tsВ конфигурации конкретная конфигурация выглядит следующим образом:

import { resolve } from "path"; // 此处如果报错则安装 node/path依赖
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";

export default defineConfig({
  plugins: [vue(), vueJsx()],
  server: {
    port: 8888
  },
  resolve: {
    alias: {
      "@": resolve(__dirname, "/src"),
    },
  },
});

На этом этапе новый псевдоним пути можно использовать непосредственно в проекте, используяvscodeМожет не быть подсказки пути, на этот раз нужно толькоjsconfig.json/tsconfig.jsonнастроитьpathsа такжеbaseUrlПоявится приглашение пути, как показано ниже:

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

меньше конфигурации

Vite предоставляет.scss, .sass, .less, .stylа также.stylusВстроенная поддержка файлов. Следовательно, для них не нужно устанавливать специальные плагины Vite, но должны быть установлены соответствующие зависимости препроцессора, а файлы меньшего размера можно парсить непосредственно после установки зависимостей.

$ npm install less less-loader -D
# or
$ yarn add less less-loader -D

Учтите, что здесь есть яма, в devDependencies нужно писать меньше и меньше загрузчика, иначе операция сообщит об ошибке.

конфигурация маршрутизатора

Установить

Обратите внимание, что рут должен быть установлен 4.0.0 или выше, лучше всего установить последнюю версию прямого тока.

Проверьте версию Vue-маршрутизатора:

$ npm info vue-router versions

Установите последнюю версию vue-router напрямую:

$ npm install vue-router@4.0.8
# or
$ yarn add vue-router@4.0.8

Создайте следующую структуру каталогов в каталоге src:

- src
  |- router
  |   index.ts
  |- views
  |   404.tsx
  |   login.tsx
  |   home.tsx
настроить

Конфигурация маршрутизации в новой версии очень похожа на предыдущую версию с небольшими отличиями. Все API-интерфейсы новой версии маршрутизации представлены функциональным образом, а с подсказками типа ts мы можем завершить настройку без документации.

import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";

// 路由配置 和以前一样
const routes: RouteRecordRaw[] = [
  {
    path: "/",
    redirect: "/login",
  },
  {
    path: "/home",
    name: "home",
    meta: {
      type: "home",
    },
    component: () => import("@/views/home"),
  },
  {
    path: "/login",
    name: "login",
    meta: {
      type: "login",
    },
    component: () => import("@/views/login"),
  },
  {
    path: "/:pathMatch(.*)*", // 注意此处 404页面匹配规则和以前不相同,得采用这种配置方式才行
    name: "404",
    component: () => import("@/views/404"),
  },
];

// 此处由【new VueRouter】的方式修改为【createRouter】的方式 其余无变化
const router = createRouter({
  history: createWebHashHistory(), //路由模式的配置采用API调用的方式 不再是之前的字符串 此处采用的hash路由
  routes,
});

export default router;
добавить защиту маршрутизации
// 路由守卫和之前的实现方式一致 此处只是做了一个demo仅供演示
router.beforeEach(
  (
    to: RouteLocationNormalized,
    from: RouteLocationNormalized,
    next: NavigationGuardNext
  ) => {
  // 获取userToken,根据业务场景可由localStorage也可由cookie中获取
  const user = localStorage.getItem("user");
  // 路由守卫判断
  if (to.meta.type === "login" && user) {
    next({ name: "home" });
    return;
  }

  if (to.meta.type === "home" && !user) {
    next({ name: "login" });
    return;
  }

  next();
});

Теперь настроен базовый маршрут vue3, а затем вmain.tsПлагин в этом файле записи можно импортировать через Vue.

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

createApp(App).use(router).mount("#app");

На этом этапе, когда вы запускаете проект, вы можете видеть, что адресная строка уже является ссылкой с использованием хэш-маршрутизации, но в это время еще есть последний шаг для достижения перехода маршрутизации, который необходимо использовать.router-viewДа, эта часть аналогична реализации vue 2. Здесь я использую метод импорта, чтобы реализовать его единообразно.

# App.tsx
import "@/assets/base.less"
import { defineComponent } from "vue";
import { RouterView } from "vue-router"; //从vue router中引入RouterView组件 实际上也可以不用引入直接使用

export default defineComponent({
  setup() {
    return () => <RouterView />;
  },
});

конфигурация vuex

Установить

Обратите внимание, что vuex также должен установить версию 4.0.0 и выше, лучше всего установить последнюю версию напрямую.

Проверьте версию vuex:

$ npm info vuex versions

Установите последнюю версию vue-router напрямую:

$ npm install vuex@4.0.1
# or
$ yarn add vuex@4.0.1

Создайте следующую структуру каталогов в каталоге src:

- src
  |- store
  |  | index.ts 
  |  |- home
  |  |  | index.ts
  |  |  | actionType.ts
  |  |- login
  |  |  | index.ts
  |  |  | actionType.ts
настроить

Конфигурация vuex в vue3 в основном такая же, как и в vue2.Здесь я создам ее модульным способом в качестве демонстрации.

Сначала настройте основную запись

# store/index.ts
import { createStore } from "vuex"
import home from "./home";
import login from "./login"

// 此处和router类似
const store = createStore({
    state: {},
    getters: {},
    mutations: {},
    actions: {},
    modules: {
        home,
        login
    }
})

export default store

Позже я буду использовать репозиторий модуля входа в систему только в качестве связанной демонстрации кода.

# store/login/index.ts
import { Module } from "vuex";
import { SET_USER } from "./actionType";

export type IUser = Record<"name" | "password", string>;

export interface ILoginState {
  user: IUser;
}

// Module这个类型可以传两个范型变量 第一个是当前模块state的对象接口类型 第二个是主仓库state的对象接口类型 
const LoginStore: Module<ILoginState, {}> = {
  namespaced: true,
  state: {
    user: {
      name: "",
      password: "",
    },
  },
  getters: {},
  mutations: {
    [SET_USER](state, payload: IUser) {
      state.user = payload;
    },
  },
  actions: {
    [SET_USER]({ commit }, payload: IUser) {
      commit(SET_USER, payload);
    },
  },
};

export default LoginStore;

На самом деле, из кода видно, что разницы с vue2 в принципе нет. Что лишает меня дара речи, так это то, что в обновленном vuex поддержка ts все еще не очень сильна. В файле объявления много любых типов, что в принципе делает нельзя использовать подсказки типа ts.Преимущества,это зависит от того,может ли официал оптимизировать потом.Конечно в сообществе тоже есть сопутствующие решения.Если интересно,можете переместиться сюда.Пусть vuex лучше поддерживает машинописные решения

Теперь, когда государственный склад создан, просто импортируйте магазин так же, как импортируете маршрут.

import App from './App'
import store from "@/store"
import router from "@/router"
import { createApp } from 'vue'

createApp(App).use(router).use(store).mount("#app");

Позже я продемонстрирую использование vuex в компоненте в сочетании с кодом компонента.

Элемент-плюс введение

На самом деле введение element-plus на официальном сайте было представлено очень подробно, здесь мы продемонстрируем введение по запросу.

Установить
$ npm install element-plus
# or
$ yarn add element-plus

Затем установите плагин импорта стиля vite.

$ npm install vite-plugin-style-import -D
# or
$ yarn add vite-plugin-style-import -D
настроить

затем вvite.config.tsв следующей конфигурации

import { resolve } from "path";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import styleImport from "vite-plugin-style-import";

export default ({ mode }) => // vite配置文件中环境变量可以以如下方式取到
  defineConfig({
    plugins: [
      vue(),
      vueJsx(),
      styleImport({
        libs: [
          {
            libraryName: "element-plus",
            esModule: true,
            ensureStyleFile: true,
            resolveStyle: (name) => {
              return `element-plus/lib/theme-chalk/${name}.css`;
            },
            resolveComponent: (name) => {
              return `element-plus/lib/${name}`;
            },
          },
        ],
      }),
    ],
    base: mode === "development" ? "/" : "./", //此时把环境打包路径也配置好,避免生产环境打包出现白屏
    server: {
      port: 8888,
    },
    resolve: {
      alias: {
        "@": resolve(__dirname, "/src"),
      },
    },
  });

потомmain.tsВставьте файл стиля в , а затем используйте его непосредственно в компоненте по мере необходимости.element-plusохватывать

// main.ts
import App from "./App";
import store from "./store";
import router from "./router";
import { createApp } from "vue";
import "element-plus/lib/theme-chalk/index.css";

createApp(App).use(router).use(store).mount("#app");

Анализ проекта

Спецификация синтаксиса JSX / TSX

Если у вас есть опыт разработки React, то помимо уникального vue мы можем найти еще несколько новых концепций:slot,directive,emitКроме этого, большинство спецификаций синтаксиса jsx, которые поддерживают vue, такие же, как и у react.Я не буду много говорить об одних и тех же частях.Если вы не понимаете, вы можете прочитать документ и быстро понять его. пример комбинированного кода:

Fragment

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

<template>
  <div></div>
  <div></div>
  <div></div>
</template>

Но способ использования jsx не поддерживает такой способ записи, или должен быть только один корневой узел.В настоящее время мы можем добавить виртуальный узел, чтобы выполнить те же требования, что и реагировать:

const App = () => (
  <>
    <span>I'm</span>
    <span>Fragment</span>
  </>
);
инструкция

@vue/babel-plugin-jsxПомог нам разобрать несколько распространенных инструкций vue, таких какv-show,v-model, Использование и функции этих двух точно такие же, как и в Vue, поэтому я не буду вдаваться в подробности.Далее давайте поговорим о нескольких общих командных функциях, которые необходимо реализовать сами по себе:

  • v-bind
import { defineComponent, ref } from "vue";
const App = defineComponent({
  setup(){
    const size = ref<"large" | "medium" | "small" | "mini">("mini")
    return () => 
      <Button size={size.value}></Button> //此处直接换成jsx的模版语法 效果和v-bind是一致的
  }
});
  • v-if

Используйте операторы условного суждения для реализации функции v-if, которая совместима с реакцией.

const App = () => (
  <>
   {
     condition ?  <span>A</span> : <span>B</span>
   }
  </>
);
  • v-for

Как и в реакции, используется метод цикла карты

import { defineComponent, ref } from "vue";
const App = defineComponent({
  setup(){
    const list = ref<string[]>([])
    return () => {
      list.value.map((data,index) => <p key={index}>{data}</p>)
    }
  }
});
  • пользовательская директива

Сначала создайте пользовательскую директиву

import { ObjectDirective } from "vue";

const foucsDirective: ObjectDirective<HTMLElement, any> = {
  mounted(el) {
    switch (el.tagName) {
      case "INPUT":
        el.focus();
        break;
      default:
        const input = el.querySelector("input");
        input?.focus();
        break;
    }
  },
};

export default foucsDirective;

глобальный импорт

import App from "./App";
import store from "./store";
import router from "./router";
import { createApp } from "vue";
import foucsDirective from "@/directive/focus";
import "element-plus/lib/theme-chalk/index.css";

const app = createApp(App);

// 全局挂载指令
app.directive("focus",foucsDirective);

app.use(router).use(store).mount("#app");

местное знакомство

import { defineComponent, ref } from "vue";
import foucsDirective from "@/directive/focus";

const App = defineComponent({
  directives: { focus: foucsDirective },
  setup(){
    const value = ref<string>("")
    return () => <input type="text" v-focus v-model={value.value}/>
  }
});
слот

В отличие от реакции, компонент поставляется с пропсами детей, а вложенность пользовательских компонентов vue зависит от слотов, поэтому способ написания слотов в vue в jsx также сильно отличается.

import { defineComponent } from "vue";

// 子组件
const Child = defineComponent({
  setup(props, { slots }) {
    return () => (
      <>
        默认插槽: {slots.default && slots.default()}
        <br />
        具名插槽: {slots.prefix && slots.prefix()}
        <br />
        作用域插槽:{slots.suffix && slots.suffix({ name: "这是作用域插槽的示范" })}
      </>
    );
  },
});

// 父组件
const Father = defineComponent({
  setup() {
    return () => (
      <Child
        v-slots={{
          prefix: <i class="el-icon-star-on"></i>, // 具名插槽
          suffix: (props: Record<"name", string>) => <span>{props.name}</span>, // props可作插槽作用域的作用
        }}
      >
        这是默认插槽的示范
      </Child>
    );
  },
});

export default Father

Из приведенного выше простого примера легко обобщить использование слотов по умолчанию, именованных слотов и слотов с областью действия в Vue, Результаты его рендеринга следующие:

3.png

Здесь есть яма, компоненты, которые напрямую передаются в defineComponent в v-slots, не будут рендериться

const Test1 = defineComponent({
  setup() {
    return <i class="el-icon-star-on"></i>;
  },
}); // 错误 此组件作为slot传入子组件不会被成功渲染

const Test2 = () => <i class="el-icon-star-on"></i> // 正确 此组件作为slot传入子组件会被成功渲染
emit

Передача значений из нейтронов Vue в родительские — это, как правило, способ испускания.Это примерно похоже на vue3, но есть еще один шаг для определения испускания, который также заключается в подготовке к последующему свертыванию типа.

import { defineComponent } from "vue";

// 子组件
const Child = defineComponent({
  emits: ["click"],
  setup(props ,{ emit }) {
    return () => (
      <button onClick={() => {emit("click")}}>点我触发emit</button>
    );
  },
});

// 父组件
const Father = defineComponent({
  setup() {
    return () => (
      <Child onClick={() => {
          console.log("emit 触发了")
      }}/>
    );
  },
});

В этом случае проблем нет, но в tsx из-за отсутствия объявления типа соответствующего события emit в реквизитах подкомпонента будет сообщено об ошибке

4.jpg

Но фактическая функция может быть запущена, здесь просто исключение в определении типа. Иногда я сталкиваюсь с библиотекой, которая несовместима с формой записи tsx (например, element-plus ==), и я не хочу, чтобы у меня была красная ошибка.В настоящее время я могу обрабатывать это так:

import { defineComponent } from "vue";

// 子组件
const Child = defineComponent({
  emits: ["click"],
  setup(props ,{ emit }) {
    return () => (
      <button onClick={() => {emit("click")}}>点我触发emit</button>
    );
  },
});

// 父组件
const Father = defineComponent({
  setup() {
    return () => (
      <Child {...{  // 避免出现因为类型检测导致的报错 此方法可适用于任何不存在props类型声明的参数
        onClick:() => {
          console.log("emit 触发了")
        }
      }}/>
    );
  },
});

Но на самом деле это план спасения страны через кривую, поэтому, если вы планируете разрабатывать библиотеку или обычно планируете использовать vue3 для написания проекта с tsx, для совместимости лучше всего использовать следующий метод:

import { defineComponent, PropType } from "vue";

// 子组件
const Child = defineComponent({
  emits: ["click"], // 传统template写法
  props: {
    onClick: Function as PropType<(event:MouseEvent) => void> // 兼容tsx写法,让事件有类型声明
  },
  setup(props ,{ emit }) {
    return () => (
      <button onClick={(event:MouseEvent) => {emit("click",event)}}>点我触发emit</button>
    );
  },
});

// 父组件
const Father = defineComponent({
  setup() {
    return () => (
      <Child onClick={(event:MouseEvent) => { // 此处便不会出现类型报错 并且有好的类型提示
          console.log("emit 触发了")
      }}/>
    );
  },
});
tsx Метод рендеринга

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

import { ref, renderSlot, onUnmounted, defineComponent } from "vue";

// 带render函数的组件 优点:可将逻辑区与模版区分开
export const RenderComponent = defineComponent({
  props: {
    title: String,
  },
  // 逻辑层
  setup() {
    const count = ref<number>(1);

    const timer = setInterval(() => {
      count.value++;
    }, 2000);

    onUnmounted(() => {
      clearInterval(timer);
    });

    return {
      count,
    };
  },
  // 渲染层
  render() {
    // render函数在响应式数据发生更改时会自动触发(与react类似)
    const { count, $slots, title } = this;
    return (
      <div class="render-component">
        {renderSlot($slots, "prefix")} {count}
        <br />
        这是props:{title}
        <br />
        {renderSlot($slots, "default")}
      </div>
    );
  },
});

Используется в проектах маршрутизатора и vuex.

Использование в проектах маршрутизатора
import { defineComponent } from "vue";
import { useRouter, useRoute, RouterView } from "vue-router";

const App = defineComponent({
  setup(){
    const router = useRouter();
    const route = useRoute();
    
    function go(pathName:string){
      // 跳转路由
      router.push({
        name: pathName,
        query: {
          value: "路由传参"
        }
      })
      
      // 取路由传递的参数 params的同理
      const { query } = route;
      console.log(query)
    }
    
    return () => <>
      <button onClick={() => {go('home')}}>跳转home</button>
      <button onClick={() => {go('login')}}>跳转login</button>
      <RouterView />
    </>
  }
});
Использование в проекте vuex
import { useStore } from "vuex";
import { SET_USER } from "@/store/login/actionType";
import { defineComponent, computed, readonly } from "vue";

const App = defineComponent({
  setup(){
    // 暴露state以及dispatch
    const { state, dispatch } = useStore();
    // 此处最好用readonly包裹暴露出的state,让其成为只读属性 避免直接修改
    const loginState = computed(() => readonly(store.state.login));
    
    function modifyUserInfo(){
      // 直接调用dispatch 用法和vue2中一致
      dispatch(`login/${SET_USER}`,{})
    }
    
    return () => <>
      <button onClick={modifyUserInfo}>修改state</button>
      <div>{loginState.user} {loginState.password}</div>
    </>
  }
});

Эпилог

На этом анализ соответствующего примера завершен.Исходный код примера этой статьи находится в этом репозитории.vite-vue3-tsxЕсли вы заинтересованы, вы можете разветвить и запустить, и вы также можете быть более знакомы с синтаксисом, связанным с vue3.

Я написал эту статью только для того, чтобы помочь студентам, которые плохо знакомы с vue3 и tsx, создать более полную структуру.В статье должны быть некоторые места, которые я не очень хорошо знаю.Добро пожаловать, чтобы больше общаться и добиваться прогресса друг с другом!