【🚨Предупреждение 4D】 Удивительный Vue3 (Часть 2)

внешний интерфейс
【🚨Предупреждение 4D】 Удивительный Vue3 (Часть 2)

Продолжение предыдущей статьи:【🚨Предупреждение 4D】 Удивительный Vue3 (Часть 1)

Что - обновки?

Давайте посмотрим, на какие новые вещи Vue3 обратил наше внимание.

Composition API

Во-первых, это, конечно же, долгожданный Composition API.

С этой целью я носил однодневную анимацию дяди Рана~

Давайте сначала рассмотрим, как в Vue2OptionsAPIКак пишется:

С итерацией продукта продакт-менеджеры продолжают выдвигать новые требования:

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

использовалCompositionAPIКаким он будет?

Мы видим, что код, относящийся к функции, агрегируется, и код становитсяв шоколаде, и больше не прыгать вверх и вниз часто. Но это почти бессмысленно, на самом деле многие из наших операций, связанных с логикой, не нужно отражать, то, что нам действительно нужно использовать, может быть только некоторыми переменными и методами, а отличный API композиции приноситОрганизация и повторное использование кодаВозможность позволяет извлекать код, связанный с функцией, в повторно используемый файл функции JS и TS.В файле .vue возвращаемые значения этих функций объединяются посредством вызова функции.Наконец, шаблон возврата действительно должен быть привык к чему-либо:

Это так хорошо~

Почему Composition API работает так хорошо благодаря двум его основным компонентам:

  • Реактивность--Отзывчивая система
  • крючки жизненного цикла

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

Преимущество

В сравненииClass API:

  • лучше одинTypeScriptПоддержка вывода типов

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

  • код прощекомпрессия

    Например, при сжатии кода ключ объекта не будет сжат, это видно из пакета, который мы только что собрали для демонстрации «Три тряски»:

Для некоторых адаптивных переменных, объявленных API композиции, имена переменных можно безопасно сжимать.

  • Дружелюбный к деревьям

    То, как CompositionAPI вызывается по ссылке, позволяет легко использовать инструмент сборки.Tree shakingчтобы устранить «мертвый код», который мы на самом деле не используем

  • более гибкийлогическое мультиплексированиеспособность

    В Vue2 нам не хватало очень чистого и удобного способа повторного использования логики.

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

    1. смешивание-Mixins
    2. Компоненты высшего порядка -HOC
    3. слот с прицелом

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

Случай:прослушивание положения мыши:

Сначала посмотрим на способ Mixins:

Mixins

MouseMixin.js:

import {throttle} from "lodash"

let throttleUpdate;

export default {
    data:()=>({
        x:0,
        y:0
    }),
    
    methods:{
        update(e){
            console.log('still on listening')
            this.x = e.pageX
            this.y = e.pageY
        }
    },
    
    beforeMount() {
        throttleUpdate = throttle(this.update,200).bind(this)
    },
    mounted() {
        window.addEventListener('mousemove',throttleUpdate)
    },
    unmounted() {
        window.removeEventListener('mousemove',throttleUpdate)
    }
}

использовать:

<template>
  <header>
    <h1>获取鼠标位置——Mixins</h1>
  </header>

  <main>
    <span>(</span>
    <transition name="text" mode="out-in">
      <div :key="x" class="position">{{ x }}</div>
    </transition>
    <span>,</span>
    <transition name="text" mode="out-in">
      <div :key="y" class="position">{{ y }}</div>
    </transition>
    <span>)</span>
  </main>
</template>


<script>
import {defineComponent} from "vue"

import MouseMixin from "@/components/Mouse/MouseMixin.js";

export default defineComponent({
  mixins: [MouseMixin],
  components: {}
})
</script>

При интенсивном использовании миксинов:

  • конфликт пространств имен
  • Источник данных шаблона не ясен

HOC — Компоненты высшего порядка

HOC вReactОн используется больше, это решение для замены миксина. На самом деле Vue также может писать HOC.

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

Проверьте это с многократно используемой логикойMouseHOCкак писать:

import Mouse2 from "@/views/Mouse/Mouse2.vue";

import { defineComponent } from "vue";
import { throttle } from "lodash";

let throttleUpdate;

export default defineComponent({
  render() {
    return (
        <Mouse2 x={this.x} y={this.y}/>
    );
  },
  data: () => ({
    x: 0,
    y: 0,
  }),
  methods: {
    update(e) {
      this.x = e.pageX;
      this.y = e.pageY;
    },
  },
  beforeMount() {
    throttleUpdate = throttle(this.update, 200).bind(this);
  },
  mounted() {
    window.addEventListener("mousemove", throttleUpdate);
  },
  unmounted() {
    window.removeEventListener("mousemove", throttleUpdate);
  },
});

Подкомпонент внутри HOC — Mouse2.vue:

<template>
  <header>
    <h1>获取鼠标位置——HOC</h1>
  </header>

  <main>
    <span>(</span>
    <transition name="text" mode="out-in">
      <div :key="x" class="position">{{ x }}</div>
    </transition>
    <span>,</span>
    <transition name="text" mode="out-in">
      <div :key="y" class="position">{{ y }}</div>
    </transition>
    <span>)</span>
  </main>
</template>

<script lang="ts">
import {defineComponent} from "vue"
export default defineComponent({
  props:['x','y']
})
</script>

Точно так же проблема при интенсивном использовании HOC:

  • конфликт пространства имен props
  • Источник реквизита не ясен
  • Дополнительное потребление производительности экземпляра компонента

слот с прицелом

Принцип заключается в том, что черезНе нужно рендеритьs компонент--renderless component,пройти черезслот с прицеломспособ поместить содержимое повторно используемого логического выхода вslot-scopeсередина.

Посмотрите, как написан этот безрендеринговый компонент:

<template>
  <slot :x="x" :y="y"></slot>
</template>

<script>
import {throttle} from "lodash";

let throttleUpdate;

  export default {
    data:()=>({
      x:0,
      y:0
    }),
    methods:{
      update(e){
        console.log('still on listening')
        this.x = e.pageX
        this.y = e.pageY
      }
    },
    beforeMount() {
      throttleUpdate = throttle(this.update,200).bind(this)
    },
    mounted() {
      window.addEventListener('mousemove',throttleUpdate)
    },
    unmounted() {
      window.removeEventListener('mousemove',throttleUpdate)
    }
  }
</script>

компонент на страницеMouse3.vueиспользуется в:

<template>
  <header>
    <h1>获取鼠标位置——slot</h1>
  </header>
  <main>
    <span>(</span>

    <MouseSlot v-slot="{x,y}">
      <transition name="text" mode="out-in">
        <div :key="x" class="position">{{ x }}</div>
      </transition>
      <span>,</span>
      <transition name="text" mode="out-in">
        <div :key="y" class="position">{{ y }}</div>
      </transition>
    </MouseSlot>

    <span>)</span>
  </main>
</template>

<script lang="ts">
import {defineComponent} from "vue"
import MouseSlot from "@/components/Mouse/MouseSlot.vue"

export default defineComponent({
  components: {
    MouseSlot
  }
})
</script>

При интенсивном использовании:

  • отсутствие конфликтов пространств имен
  • Источники данных понятны
  • Дополнительное потребление производительности экземпляра компонента

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

Итак, как в Composition API добиться логического повторного использования?

Composition API

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

import {ref, onMounted, onUnmounted} from "vue"
import {throttle} from "lodash"

export default function useMousePosition() {

    const x = ref(0)
    const y = ref(0)

    const update = throttle((e: MouseEvent) => {
        x.value = e.pageX
        y.value = e.pageY
    }, 200)

    onMounted(() => {
        window.addEventListener('mousemove', update)
    })
    onUnmounted(() => {
        window.removeEventListener('mousemove', update)
    })

    return { x, y }
}

компонент страницыMouse4.vueиспользуется в:

<template>
  <header>
    <h1>获取鼠标位置——Composition API</h1>
  </header>

  <main>
    <span>(</span>
    <transition name="text" mode="out-in">
      <div :key="x" class="position">{{ x }}</div>
    </transition>
    <span>,</span>
    <transition name="text" mode="out-in">
      <div :key="y" class="position">{{ y }}</div>
    </transition>
    <span>)</span>
  </main>
</template>


<script lang="ts">
import {defineComponent} from "vue"
import useMousePosition from "@/components/Mouse/useMousePosition";

export default defineComponent({
  setup() {
    const { x, y } = useMousePosition()
    return { x, y }
  }
})
</script>

Даже при интенсивном использовании:

  • отсутствие конфликтов пространств имен
  • Источники данных понятны
  • Отсутствие дополнительной нагрузки на производительность экземпляра компонента

чистый,Чисто.

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

Чтобы продемонстрировать это, я поместил пример Vue2 вtodoMVCПроект перемещен с CompositionAPIрефакторингмомент.

todoMVCЭто небольшое приложение списка дел со следующими функциями:

  1. Локальный кеш и динамически сохраняется в LocalStorage
  2. Добавить список дел
  3. Нажмите, чтобы завершить дела, одним щелчком мыши все завершенные/незавершенные
  4. удалить задачу
  5. Удалить завершенные задачи
  6. Фильтровать список дел по статусу выполнения

(Хитрый друг может обнаружить, что я кастрировал функцию редактирования. Я тут реально ленился. Я торопился писать в то время, и из-за каких-то соображений совместимости статус редактирования не отображался, поэтому я кастрировал редактор в порыве ярости.... На самом деле это не особо влияет на то, что я хочу объяснить)

Вот код, весь компонент todo:TodoMVC.vue

import {defineComponent} from "vue"
import useTodoState from "@/views/TodoMVC/useTodoState";
import useFilterTodos from "@/views/TodoMVC/useFilterTodos";
import useHashChange from "@/views/TodoMVC/useHashChange";

export default defineComponent({
  setup() {

    /*响应式变量、新增和删除代办事项的方法*/
    const {
      todos,
      newTodo,
      visibility,
      addTodo,
      removeTodo
    } = useTodoState()

    // 筛选数据、一键全部完成/未完成、清空全部已完成事项
    const {
      filteredTodos,
      remaining,
      allDone,
      filters,
      removeCompleted
    } = useFilterTodos(todos, visibility)


    // 监听路由哈希变化
    useHashChange(filters, visibility)


    return {
      todos,
      newTodo,
      filteredTodos,
      remaining,
      allDone,
      visibility,
      removeCompleted,
      addTodo,
      removeTodo,
    }
  },

})

useTodoStateеще один звонок本地存储Функция композиции, связанная с логикой:useTodoStorage.ts

useTodoState.ts:

import { Todo, Visibility } from "@/Types/TodoMVC";
import { ref, watchEffect, } from "vue"
import useTodoStorage from "@/views/TodoMVC/useTodoStorage";

export default function useTodoState() {

    const { fetch, save, uid } = useTodoStorage()

    // 全部事项
    const todos = ref(fetch())
    
    // 即将新增事项的内容
    const newTodo = ref("")

    // 新增代办事项
    const addTodo = () => {
        const value = newTodo.value && newTodo.value.trim()
        if (!value) {
            return;
        }
        todos.value.push({
            id: uid.value,
            title: value,
            completed: false
        })
        uid.value += 1
        newTodo.value = ""
    }

    // 删除代办事项
    const removeTodo = (todo: Todo) => {
        todos.value.splice(todos.value.indexOf(todo), 1)
    }

    // 使用todos.value的副作用去动态保存代办事项到本地缓存中
    watchEffect(() => {
        save(todos.value)
    })

    // 当前筛选的类型(url的hash值与此值一致)
    const visibility = ref<Visibility>("all")
    
    return {
        todos,
        newTodo,
        visibility,
        addTodo,
        removeTodo
    }
}

для локального кешаuseTodoStorage.ts:

import {Todo} from "@/Types/TodoMVC";
import {ref, watchEffect} from "vue"


export default function useTodoStorage() {

    const STORAGE_KEY = 'TodoMVC——Vue3.0'


    // 获取LocalStorage中的数据
    const fetch = (): Todo[] => JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");

    // 数据存储到LocalStorage中
    const save = (todos: Todo[]) => {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
    }

    // 用于新增代办事项的id自动生成
    const uid = ref(~~(localStorage.getItem('uid') || 0));
    watchEffect(() => localStorage.setItem('uid', uid.value.toString()))

    return {
        fetch,
        save,
        uid
    }

}

Остальные по одному не показаны, код окончательно размещен в ссылке в конце статьиgithubОн на складе, и вы можете рассмотреть его поближе, если вам интересно. Поскольку эта демонстрация написана в спешке, я чувствую, что она написана не очень хорошо, и логическая организация все еще подлежит обсуждению.Это также показывает со стороны, что дает нам API композиции.Очень гибкая организация и возможность повторного использования, а насчет того, как красивее организовать код, это личное дело разработчика, и я тоже пытаюсь потихоньку выяснить, что удобнее писать.Лучшие практики.

По сравнению с React-хуками

  • Та же логическая комбинация и возможности повторного использования
  • позвонить только один раз
    • JS интуиция
    • Нет проблемы с переменной закрытия
    • Нет нагрузки на память/GC
    • Нет проблем со встроенными обратными вызовами, вызывающими вечное обновление подкомпонентов.

Нельзя отрицать, что рождение Composition API действительно было вдохновлено React Hooks, было бы слишком поверхностно называть это плагиатом, и я не хочу начинать здесь войну. Рамка хорошая, а передний круг должен бытьМир драгоценен, Не хорошо ли учиться друг у друга, не дерутся в берлогах.

На самом деле, Composition APIКак реализовать и использоватьобаОчень разныеДа я естественно понимаю.

Сравнение с React Hooks уже было описано в нескольких статьях, поэтому здесь я не буду вдаваться в подробности.

Проще говоря, это приносит пользуОтзывчивая система, используемый Composition APIумственное бремяЭто слишком мало в сравнении.

Fragment

Эта новая функция относительно проста, то есть ее можно прописать в шаблоненесколько корневых узлов. Что касается того, что это означает:

  • Сокращение бессмысленных элементов корневого узла
  • Могурекурсия уровнякомпоненты

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

QuickSort.vue:

<template>
  <quick-sort :list="left" v-if="left.length"></quick-sort>
  <span class="item">{{ flag }}</span>
  <quick-sort :list="right" v-if="right.length"></quick-sort>
</template>


<script lang="ts">
import {defineComponent, ref} from "vue"

export default defineComponent({
  name: 'quick-sort',
  props: ["list"],
  setup(props) {
    // eslint-disable-next-line vue/no-setup-props-destructure
    const flag: number = props.list[0]
    const left = ref<number[]>([])
    const right = ref<number[]>([])

    setTimeout(() => {
      props.list.slice(1).forEach((item: number) => {
        item > flag ? right.value.push(item) : left.value.push(item)
      })
    }, 100)

    return {
      flag,
      left,
      right
    }
  }
})
</script>

компонент на страницеFragment.vueиспользуется в:

<template>
  <h1>快速排序</h1>
  <h2>
    {{ list }}
  </h2>
  <div>
    <button @click="ok = !ok">SORT</button>
  </div>
  <hr>
  <template v-if="ok">
    <QuickSort :list="list"></QuickSort>
  </template>
</template>

<script lang="ts">
import QuickSort from "@/components/QuickSort.vue";
import {defineComponent, ref} from "vue"
import {shuffle} from "lodash"

export default defineComponent({
  components: {
    QuickSort
  },
  setup() {
    const ok = ref(false)
    const list = ref<number[]>([])
    for (let i = 1; i < 20; i++){
      list.value.push(i)
    }
    list.value = shuffle(list.value)
    return {list, ok}
  }
})
</script>

КQuickSortПройдите в длину 20, которая перетасовываетсямножество:

Можно видеть, что каждая рекурсивная компонентатот же уровеньиз:

И рекурсивные компоненты в Vue2, как правило,слои вложения, так как он может иметь только один корневой элемент, тот же метод записи сообщит об ошибке в Vue2.

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

Suspense

можно понимать какОтец асинхронных компонентов. Один для простого управления асинхронными компонентамивешатьа такжеЗаканчиватьусловие.

прямо в код,

Первый — это асинхронный компонент,AsyncComponent.vue:

<template>
  <h2>AsyncComponent</h2>
</template>
<script lang="ts">
import {defineComponent} from "vue"

export default defineComponent({
  props: {
    timeout:{
      type: Number,
      required: true
    }
  },
  async setup(props) {
    const sleep = (timeout: number) => {
      return new Promise(resolve => {
        setTimeout(resolve, timeout)
      })
    }
    await sleep(props.timeout)
  }
})
</script>

компонент на страницеSuspense.vueсередина:

<template>
  <h1>Suspense</h1>
  <Suspense>
    <template #default>
      <AsyncComponent :timeout="5000"/>
    </template>

    <template #fallback>
      <p class="loading">loading {{ loadingStr }}</p>
    </template>
  </Suspense>
</template>

<script lang="ts">
import {defineComponent} from "vue"
import AsyncComponent from "@/components/AsyncComponent.vue"
import useLoading from "@/composables/useLoading";

export default defineComponent({
  components: {
    AsyncComponent
  },
  setup() {
    const {loading: loadingStr} = useLoading()
    return {loadingStr}
  }
})
</script>

Проще говоря, это использование встроенных компонентов, предоставляемых Vue3:Suspenseоберните асинхронный компонент,template #defaultПоказывает загруженный асинхронный компонент вtemplate #fallbackПоказывает содержимое, которое необходимо отображать, когда асинхронный компонент находится в приостановленном состоянии.

Взгляните на эффект:

Teleport

понимается какКомплектация любой двери, так что ваши компоненты могут быть произвольно перенесены в любой DOM в html. В React также есть компоненты с такой же функциональностью —Portal, почему его переименовалиTeleportэто из-заhtmlОн также готов предоставить собственный тег портала, который называется Teleprot, чтобы избежать дублирования имен.

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

Здесь я написал демонстрацию модального всплывающего окна:

<template>
  <h1>Teleport——任意门</h1>
  <div class="customButton" @click="handleToggle">偷袭</div>
  <teleport to="body">
    <TeleportModal v-if="isOpen" @click="handleToggle"></TeleportModal>
  </teleport>
</template>

<script lang="ts">
import {defineComponent, ref} from "vue"
import TeleportModal from "@/components/TeleportModal.vue"

export default defineComponent({
  components: {
    TeleportModal
  },
  setup() {
    const isOpen = ref(false)
    const handleToggle = () => {
      isOpen.value = !isOpen.value
    }

    return {
      isOpen,
      handleToggle
    }
  }
})
</script>

Встроенный с Vue3TeleportКомпонент упаковывает модальный компонент, который необходимо отправить, и записывает селектор элемента, которому нужно отправить. (Немного какОтправить Экспресс, упакуйте в курьерскую коробку, напишите адрес доставки и забирайте)

Взгляните на эффект этой демонстрации:

Видно, что Ма Баого действительно ударили ногой под тело (🐶).

createRenderer API

Используя этот API, в Vue3 мы можем свободно и легко создаватьВеб-платформа (браузер)илине веб-платформаизпользовательский рендерер.

Принцип наверное такой:Virtual DOMа такжеСвязанные с платформойоказаниеотдельный,пройти черезcreateRendererAPIМы можем настроить все, когда виртуальный DOM отображается на платформе.действовать,Напримерновый,Исправлять,удалять«Элемент», который мы можем заменить или модифицировать в этих методах для нашегопользовательская логика, создавая тем самымпользовательский рендерер.

Конечно, ввеб-платформаСледующее относительно просто, потому что вы можете использовать Vueruntime-domпредоставить нам верхнююуровень абстракции, это помогает нам завершить сложный рендеринг Virtual DOM в Web DOM.Разные операции программирования интерфейса браузера, нам просто нужноcreateRendererпередать некоторые параметрыпользовательская логическая операцияИнтеграция может быть выполнена автоматически, например, вы можетеcreateElementДобавьте в метод часть собственной логики:так каждый разСоздать новый элементВсегда «здоровайся» с тобой.

передачаcreateRendererВозвращаемое значение позжеrenderer,createAppЭтот метод является методом атрибута этого рендерера, мы можем использовать наш собственный метод вместо нативного метода createApp.пользовательский рендерерсейчас~

Для этого я подготовилThree.jsи реализован пользовательский рендерер3D квадратзаблокировать демонстрацию и использоватьcomposition APIПовторно используйте логику, которую мы написали ранее, чтобы прослушивать положение мыши, и пусть этот трехмерный блок вращается с помощью нашей мыши.

Сначала напишитепользовательский рендерер:renderer.js:

import { createRenderer } from '@vue/runtime-dom'
import * as THREE from 'three'

let webGLRenderer

// Three.js相关
function draw(obj) {
    const {camera,cameraPos, scene, geometry,geometryArg,material,mesh,meshY,meshX} = obj
    if([camera,cameraPos, scene, geometry,geometryArg,material,mesh,meshY,meshX].filter(v=>v).length<9){
        return
    }
    let cameraObj = new THREE[camera]( 40, window.innerWidth / window.innerHeight, 0.1, 10 )
    Object.assign(cameraObj.position,cameraPos)

    let sceneObj = new THREE[scene]()

    let geometryObj = new THREE[geometry]( ...geometryArg)
    let materialObj = new THREE[material]()

    let meshObj = new THREE[mesh]( geometryObj, materialObj )
    meshObj.rotation.x = meshX
    meshObj.rotation.y = meshY
    sceneObj.add( meshObj )
    webGLRenderer.render( sceneObj, cameraObj );
}

const { createApp } = createRenderer({
      insert: (child, parent, anchor) => {
          if(parent.domElement){
              draw(child)
          }
      },
      createElement:(type, isSVG, isCustom) => {
          alert('hi Channing~')
          return {
              type
          }
      },
      setElementText(node, text) {},
      patchProp(el, key, prev, next) {
          el[key] = next
          draw(el)
      },
      parentNode: node => node,
      nextSibling: node => node,
      createText: text => text,
      remove:node=>node
});


// 封装一个自定义的createApp方法
export function customCreateApp(component) {
  const app = createApp(component)
  return {
    mount(selector) {
        webGLRenderer = new THREE.WebGLRenderer( { antialias: true } );
        webGLRenderer.setSize( window.innerWidth, window.innerHeight );
        const parentElement =  document.querySelector(selector) || document.body
        parentElement.appendChild( webGLRenderer.domElement );
        app.mount(webGLRenderer)
    }
  }
}


App.vue, здесь напишите какой-нибудь реальный DOMОперативная логика, я например ставлюmeshXа такжеmeshYУстанавливает мышь, возвращаемую этой функцией композиции, чтобы получить положение мышиxа такжеyВычисленное значение свойства (для уменьшения чувствительности к вращению).

<template>
  <div
      camera="PerspectiveCamera"
      :cameraPos={z:1}
      scene="Scene"
      geometry="BoxGeometry"
      :geometryArg="[0.2,0.2,0.2]"
      material="MeshNormalMaterial"
      mesh="Mesh"
      :meshY="y"
      :meshX="x"
  >
  </div>

</template>

<script>
import {computed} from 'vue'
import useMousePosition from "./useMousePosition";

export default {
  setup() {
    const {x: mouseX, y: mouseY} = useMousePosition()
    const x = computed(() => (mouseY.value)/200)
    const y = computed(() => (mouseX.value)/200)
    return {x,y}
  }
}
</script>
<style>

body {
  padding: 0;
  margin: 0;
  overflow: hidden;
}
</style>

Наконец, в main.js с помощьюrenderer.jsс пользовательским рендерером, упакованным вcustomCreateAppметод заменяет обычныйcreateAppметод, вы можете:

import { customCreateApp } from './renderer';
import App from "./App.vue";

customCreateApp(App).mount("#app")

Давайте посмотрим на окончательный эффект:

За отсутствием мышления зал!

Еще одна вещь - Вите

Наконец, так называемый перспективный инструмент сборкиVite.

yarn devОн всплывает с щелчком приложения, оченьбыстрокакие.

Его принцип — нативный браузерныйES importsсервер разработки. использоватьбраузеридтиРазобратьимпорт, на стороне серверакомпилировать по требованиюназад, пропущен полностьюПакетЭта концепция, сервер готов к использованию. служба поддержки.vue-файла такжеГорячее обновлениеи горячее обновлениескоростьНе тормозит при увеличении количества модулей.

Конечно,Производственная средаВ сборке по-прежнему используетсяrollupупаковать. его ароматсреда разработкискорость отладки.

Чтобы лучше понять, как это работает, я нашел картинку, нарисованную Snail Lao Wet:

Затем я создал демо-версию, чтобы увидетьViteкак обрабатываются наши файлы.

yarn create vite-app vite-demo
cd vite-demo && yarn && yarn dev

Открытьhttp://localhost:3000/

Видя результат запроса localhost, он все еще зарезервированES Moduleвведите код

Затем сервер Vite перехватывает ваш запрос наmain.jsзапрос, а затем вернуть вам содержимое main.js, которое все ещеES Moduleтип,

снова перехваченvue.js,App.vue, продолжать возвращать вам соответствующий контент и т. д.

Таким образом, процесс запуска приложения Vite полностью пропускается.упаковать и скомпилировать, запустите приложение за считанные секунды. То же самое верно для горячих обновлений файлов, например, когда вы изменяетеApp.vueконтент, который в свою очередь перехватывает и возвращает вам новыйПосле компиляцииФайл App.vue:

Для больших проектов такой миллисекундный отклик действительно удобен. Участвовал в разработке внутренней библиотеки компонентов в прошлом году, которая тогда была модифицированаwebpackПлагины, вы должны перезапускать проект каждый раз, когда вы его модифицируете, и каждый перезапускЧетыре или пять минутПоднимаясь вверх, кажется, что люди собираются разделиться.

Конечно, не так многозаменятьгипербола Webpack, потому чтоViteеще встадия развития,многоИнжинирингПо-прежнему трудно удовлетворить потребности Webpack, например, богатые периферийные устройства Webpack.плагини т.п.

напиши в конце

Спасибо за ваше терпение, чтобы прочитать это далеко.
Конечно, неточностей и ошибок в тексте может быть больше или меньше, любые предложения и замечания приветствуются.Комментарийобщаться с.
Все использовано в текстеDemoбыли размещены в:Портал GitHub.
Наконец, я надеюсь, что мои друзья смогутподобноКомментируйте и подписывайтесь на Sanlian, потому что это все, чем я делюсьвластьИсточник 🙏