Jak už asi víte, loni v září Evan You oznámila novou verzi Vue (Vue 3.0 nebo „One Piece“ pro přátele) během Vue.js Global Event – oficiální vydání zde.
Humbuk po upgradu kódu na nejnovější verzi Vue explodoval a všichni (včetně mě) byli dychtiví začít. Porušují však změny, zejména na globálním rozhraní API, a nutí autory knihoven/pluginů migrovat svůj kód na podporu nové verze a Composition API . Pokud chcete lépe pochopit, proč jsem napsal článek o tom, jak provést migraci z 2.x na 3.x zde - Jak migrovat svou knihovnu z Vue 2.x na Vue 3.x
Jako autor knihovny Vue musím říci, že migrace nebyla snadná práce, napodobování toho, co dělaly velké knihovny:oddělení podpory pro každou verzi cílení do samostatných branches
a tags
(main
pro vue 2.xa next
pro vue 3.x) nebo dokonce mít samostatné repo pro zajištění lepší izolace kódu.
Jak vysvětluje člen jádra Vue.js @antfu (Anthony Fu) v tomto příspěvku:
Je možné toho dosáhnout pomocí vývojového nástroje, který stejný @antfu vytvořil, s názvem Vue-demi.
Takže pokud máte zájem dozvědět se, jak vytvořit univerzální knihovnu/plugin pro obě verze Vue, tento článek je pro vás.
Vytvořit základní nastavení
Začněme vytvořením nového projektu pomocí vue-cli prompt.
vue create vue-universal-lib
Ujistěte se, že jste vybrali verzi 3.x pro Vue a zbytek nechám na vašich preferencích, ale důrazně doporučuji, abyste použili stejné možnosti, jaké zde popisuji, abyste byli na stejné stránce:
Vybrané možnosti:
- Babel
- Psopis
- Linter
- Použijte syntaxi komponenty ve stylu třídy Ne
- Použít Babel spolu s TypeScript Ano
- Vyberte si linter:ESLint + Prettier
Po několika sekundách budeme mít základní strukturu pro začátek. Pravděpodobně se budete muset zbavit některých věcí, jako je App.vue
a main.ts
protože budeme hlavně pracovat s index.ts
soubor.
Najděte účel
Zní to epicky, že? Samostatná zábava najděte nezbytnost, některé funkce často používané při vývoji webu, které chcete implementovat ve Vue a učinit je opakovaně použitelnými, něco, o čem si myslíte, že přinese hodnotu jako knihovna/plugin.
Pro tento výukový program vytvoříme jednoduchou knihovnu, která vám umožní animovat čísla jako počítadlo , podobný tomuto:
Tento typ komponenty se často používá na vstupních stránkách k zobrazení KPI.
Špinavé ruce
Nejprve si vytvořte counter-number
komponenta pod src/components/CounterNumber.ts
pomocí defineComponent
.
import { ref, defineComponent, h } from 'vue';
export const CounterNumber = defineComponent({
name: 'Awesome',
props,
setup(props, ctx) {
const value = ref(640);
return () =>
h(
'span',
{
class: 'counter-number',
},
value,
);
},
});
V tuto chvíli to nechme jako prezentační komponentu bez animace, později přidáme funkcionalitu prostřednictvím komposovatelné funkce, abychom využili Vue3's Composition API.
Můžete si také všimnout, že zde není žádná šablona pro komponentu, setup
funkce vrací vykreslovací funkci s <span>
prvek držící hodnotu čítače. To je zamýšleno a bude vysvětleno v sekci Caveates příspěvku.
Pro účely ukázky vynechejte main.ts
a App.vue
otestujte novou komponentu pomocí npm serve
.
Instalace pluginu
Pro vytvoření samotného pluginu vytvořte src/index.ts
:
import { App, inject, InjectionKey } from 'vue';
import { CounterNumber } from './components/CounterNumber';
export interface VueCounterOptions {
theme: string;
}
export interface VueCounterPlugin {
options?: VueCounterOptions;
install(app: App): void;
}
export const VueCounterPluginSymbol: InjectionKey<VueCounterPlugin> = Symbol();
export function VueCounterPlugin(): VueCounterPlugin {
const VueCounterPlugin = inject(VueCounterPluginSymbol);
if (!VueCounterPlugin) throw new Error('No VueCounterPlugin provided!!!');
return VueCounterPlugin;
}
export function createVueCounterPlugin(
options?: VueCounterOptions,
): VueCounterPlugin {
const plugin: VueCounterPlugin = {
options,
install(app: App) {
app.component('vue-counter', CounterNumber);
app.provide(VueCounterPluginSymbol, this);
},
};
return plugin;
}
Pojďme si to rozdělit na části, funkce createVueCounterPlugin
vám umožní nainstalovat plugin přes install
při použití createApp.use()
ve vaší aplikaci.
Tím se přidá k app
instance všech komponent, vlastností vaší knihovny, jak vidíte výše, s app.component('vue-counter', CounterNumber);
Chcete-li získat většinu rozhraní Composition API a být schopni vkládat do komponent knihovny věci jako options
nebo utilities
vytvoříme Symbol pluginu použít spolu s app.provide
v install
metoda, kde předáme samotný createVueCounterPlugin jako parametr. V tuto chvíli to může vypadat složitě, ale je to standardní způsob:
// index.ts
...
export const VueCounterPluginSymbol: InjectionKey<VueCounterPlugin> = Symbol();
export function VueCounterPlugin(): VueCounterPlugin {
const VueCounterPlugin = inject(VueCounterPluginSymbol);
if (!VueCounterPlugin) throw new Error('No VueCounterPlugin provided!!!');
return VueCounterPlugin;
}
...
Chcete-li plugin nainstalovat a otestovat, přejděte na src/main.ts
:
import { createApp } from 'vue';
import App from './App.vue';
import './assets/styles/main.css';
import { createVueCounterPlugin } from './';
const VueCounterPlugin = createVueCounterPlugin();
createApp(App).use(VueCounterPlugin).mount('#app');
Pokud chcete svému pluginu předat možnosti, můžete to udělat takto
const VueCounterPlugin = createVueCounterPlugin({ theme: 'light' });
Kouzlo za tím, co jsme udělali, je použití app.provide
v metodě instalace pluginu je, že můžeme později vložit možnosti pluginu jako závislost.
Nyní přidáme CounterNumber
komponentu do src/App.vue
.
// App.vue
<template>
<h2 class="font-bold text-2xl mb-8 text-gray-600">
Vue Counter animation
</h2>
<div
class="card bg-gray-100 rounded-xl p-8 auto shadow-lg mx-auto w-1/3 text-indigo-400 font-bold text-xl"
>
<vue-counter />
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: 'App',
});
</script>
Pokud jste zvědaví na pomocné třídy, které jsem zde použil, je to úžasný TailwindCSS, který miluji pro vytváření rychlých prototypů. Můžete jej nainstalovat také podle tohoto návodu. Jen se ujistěte, že jste tyto závislosti přidali jako devDependencies
na váš package.json
nebo budou součástí vaší knihovny.
Podívejme se, jak to vypadá v prohlížeči s npm run serve
Animace a kompozice
Vypadá krásně, ale potřebuje více magie. Přidejme animaci náběhu pro počítadlo. Abychom dosáhli hladké animace, budeme používat knihovnu s názvem anime.js, která je opravdu lehká a nabízí jednoduché a jednoduché API.
Mohli bychom přidat logiku přímo na CounterNumber
komponentu, ale protože jsme již mluvili o Composition API pojďme to použít pro tento účel.
Vytvořte useCounter.ts
soubor pod src/composables
a exportujte funkci nazvanou useCounter
takhle:
import { ref } from 'vue';
import anime from 'animejs/lib/anime.es.js';
export function useCounter() {
const count = ref(0);
const counter = {
value: 0,
};
anime({
targets: counter,
duration: 2000, // 2000ms
value: 640,
easing: 'easeOutQuad',
update: () => {
count.value = Math.round(counter.value);
},
});
return {
count,
};
}
Importujeme tovární funkci s názvem 'anime' z 'animejs/lib/anime.es.js' a předáváme cíl (v tomto případě obj obsahující ref
s hodnotou, která má být animována).
anime()
funkce přijímá mnoho parametrů pro přizpůsobení chování animace, jako je trvání , zpoždění , zmírnění a zpětná volání, jako je aktualizace který se spustí pokaždé, když animace aktualizuje cílový objekt. Zajímavé je, že jako vlastnost můžete předat stejnou vlastnost, kterou chcete animovat, v tomto případě value
, ve výše uvedeném příkladu bude hodnota od 0 do 640. Další informace o animejs API zkontrolujte dokumenty
Vraťte se ke svému CounterNumber.ts
a získejte použití count.value
uvnitř span
takhle:
export const CounterNumber = defineComponent({
name: 'Awesome',
props,
setup(props, ctx) {
const { count } = useCounter();
return () =>
h(
'span',
{
class: 'counter-number',
},
count.value,
);
},
});
Nyní se vraťte do prohlížeče a obnovte stránku, abyste viděli, jak se počítadlo pohybuje od 0 na 640 za 2 sekundy.
Udělejte jej přizpůsobitelný
V tuto chvíli jsou všechny hodnoty napevno zakódovány, ale protože děláme knihovnu, měly by být tyto parametry pro animaci přizpůsobitelné, a proto by měly být předány jako rekvizity komponentě a dolů do kompoziční funkce.
Nejprve přidáme několik rekvizit, které dávají smysl:
// src/components/Counternumber
const props = {
from: {
type: [Number, String],
default: 0,
},
to: {
type: [Number, String],
required: true,
default: 0,
},
duration: {
type: Number,
default: 1000, // Duration of animation in ms
},
easing: {
type: String,
default: 'easeInOutQuad',
},
delay: {
type: Number,
default: 0, // Delay the animation in ms
},
};
export const CounterNumber = defineComponent({
name: 'Awesome',
props,
setup(props, ctx) {
const { count } = useCounter(props);
...
},
});
Ujistěte se, že jste předali rekvizity na useCounter(props)
funkce;
Přejděte na App.vue
a vytvořte nějaké proměnné, které se předají komponentě jako rekvizity:
<template>
<h2 class="font-bold text-2xl mb-8 text-gray-600">Vue Counter animation</h2>
<div
class="card bg-gray-100 rounded-xl p-8 auto shadow-lg mx-auto w-1/3 text-indigo-400 font-bold text-xl"
>
<vue-counter :from="0" :to="640" :duration="3000" :delay="2000" />
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: 'App',,
});
</script>
Nakonec se vraťte na useCounter.ts
a předejte rekvizity anime
instance
import { ref } from 'vue';
import anime from 'animejs/lib/anime.es.js';
export function useCounter(props: any, emit: any) {
const count = ref(0);
const counter = {
value: props.from,
};
anime({
targets: counter,
duration: props.duration,
value: props.to,
delay: props.delay,
easing: props.easing || 'linear',
update: () => {
count.value = Math.round(counter.value);
},
});
return {
count,
};
}
Samozřejmě bychom museli přidat další kód, abychom vytvořili novou instanci anime objektu pokaždé, když se změní rekvizita, ale pro rozsah článku je více než dostačující.
Udělejte to univerzální
Tak skvělé, máme připravenou naši úžasnou knihovnu, v tuto chvíli je použitelná pouze na projektu s pro Vue 3 , jak můžeme dosáhnout izomorfní instalace?
To je místo vue-demi
přichází na pomoc.
npm i vue-demi
# or
yarn add vue-demi
Přidejte vue
a @vue/composition-api
k závislostem vašeho pluginu, abyste určili, které verze podporujete.
// package.json
{
"dependencies": {
"vue-demi": "latest"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-beta.12",
"vue": "^2.6.11 || >=3.0.5"
}
}
Nyní přichází ta důležitá část 📝, udělat si k tomu poznámky:nahradit všechny importy pocházející z vue
na vue-demi
, jako tak:
import { defineComponent, ref } from 'vue';
Bude:
import { defineComponent, ref } from 'vue-demi';
Knihovna se přesměruje na vue@2
+ @vue/composition-api
nebo vue@3
na základě uživatelského prostředí.
To je mocné.
Sestavení konfigurace
Svůj balíček pluginů si můžete sestavit mnoha různými způsoby, webpack, vue-cli (také webpack), parser, rollup atd. Je to na vás, ale opravdu doporučuji použít rollup.js, je to skvělý balíček modulů, opravdu snadné se do něj dostat a používá se ve většině hlavních pluginů Vue, jako je Vue Router.
yarn add rollup rollup-plugin-vue rollup-plugin-typescript2 rollup-plugin-terser @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-replace -D
Také budeme muset trochu upravit konfiguraci, aby externalizovala vue-demi
místo vue
a nastavit jej jako globální v okamžiku sestavení. Protože rollup.config.js
je poměrně velký, zde je odkaz na něj v ukázkovém repo.
V metodě createConfig
ujistěte se, že máte vue-demi
nastavte v globálních vlastnostech takto:
// rollup.config.js
...
output.globals = { 'vue-demi': 'VueDemi' };
...
const external = ['vue-demi'];
Nakonec přidáme script
v package.json
a cesty pro sestavení balíčku:
// package.json
"scripts": {
"build": "rollup -c rollup.config.js",
}
"main": "dist/vue-universal-lib.cjs.js",
"browser": "dist/vue-universal-lib.esm.js",
"unpkg": "dist/vue-universal-lib.global.js",
"jsdelivr": "dist/vue-universal-lib.global.js",
"module": "dist/vue-universal-lib.esm-bundler.js",
"types": "dist/vue-universal-lib.d.ts",
Upozornění
Samozřejmě, že nejsou všechny růže 🌹 a jednorožci 🦄, případ použití vue-demi
je spíše pro zásuvné moduly vue, které se příliš nespoléhají na komponenty vykreslování, protože funkce vykreslování Vue 2 a Vue 3 jsou zcela odlišné a dochází ke změnám mezi oběma, tj. v-model
na komponentě, která očekává jinak pojmenované události ve Vue 2 vs 3 (ìvstup vs update:modelValue
).
Proto jsme pro definici komponenty použili funkci renderingu a .ts
namísto .vue
soubor. U této ukázkové knihovny to neovlivní konečný výsledek, ale je to něco, co musíte vzít v úvahu.
Jedním ze způsobů, jak případně upravit změny ve vaší lib komponentě, by bylo použití dalších rozhraní API z Vue Demi
pomoci rozlišit uživatelská prostředí a provést určitou logiku specifickou pro verzi.
isVue2
isVue3
import { isVue2, isVue3 } from 'vue-demi';
if (isVue2) {
// Vue 2 only
} else {
// Vue 3 only
}
Jak již bylo řečeno, doufám, že tento článek byl dostatečně ilustrativní na cestě k vytvoření univerzálního pluginu pro Vue. Níže si poslechněte své myšlenky a otázky.
Šťastné kódování! 😎