Inertia.js: The Bridge Between Vue.js and Laravel
সার্ভার সাইড রেন্ডারিং এবং ক্লায়েন্ট সাইড রেন্ডারিং এর তূলনামূলক আলোচনা
মডার্ন ওয়েব এপ্লিকেশন ডেভেলপ করার জন্য Server-side rendering (SSR) and client-side rendering (CSR) এই দুইটা টার্ম বিবেচনা করা হয়। সহজ কথায়, SSR এ HTML কোড সার্ভার থেকে রেন্ডার হয়ে ব্রাউজার এর কাছে পাঠানো হয়, এবং CSR এ HTML কোড ক্লায়েন্ট সাইডেই জাভাস্ক্রিপ্টের সাহায্যে জেনারেট করা হয়।
সার্ভার সাইড রেন্ডারিং এর সুবিধা-
- প্রথম বার দ্রুত পেজ লোড হয়। কারন HTML কোড তো সব সার্ভার থেকেই জেনারেট হয়ে আসছে। ব্রাউজারকে কোড ডাউনলোড করে রেন্ডার করতে হয়না।
- সার্চ ইঞ্জিন অপটিমাইজেশন(SEO) এর জন্য SSR খুবই ভালো। কারন সার্চ ইঞ্জিন বট যখন ক্রল করতে আসবে তখন সে ফুললি রেন্ডার্ড HTML কোড পাবে এবং এর দ্বারা সার্চ ইঞ্জিনের ইনডেক্সিং সহজ হবে।
- ব্যাকওয়ার্ড কম্প্যাটিবিলিটি- অনেক ওল্ড ভার্সন ব্রাউজার জাভাস্ক্রিপ্ট এর অনেক ফিচার সাপোর্ট করেনা। এক্ষেত্রে পুরো পেজ যখন সার্ভার থেকে রেন্ডার হয়ে আসবে তখন ব্রাউজারের কম্প্যাটিবিলিটি ইস্যু আর থাকেনা।
সার্ভার সাইড রেন্ডারিং এর অসুবিধা-
- সার্ভারের উপর লোড বেশি পড়ে। কারন সকল কাজ সার্ভার থেকেই হয়।
- মডার্ন ওয়েব এপ্লিকেশনে ইউজার ইন্ট্র্যারিক্টিভিটি কমে যায়। কারন কোন কিছুই ডাউনামিক্যালি চেঞ্জ হয়না। যেকোন কাজের জন্য সার্ভার থেকে রেন্ডার হয়ে আসতে হয় তারপর আউটপুট দেখা যায়।
ক্লায়েন্ট সাইড রেন্ডারিং এর সুবিধা-
- ক্লায়েন্ট সাইড রেন্ডারিং এ অবশ্যই ইউজার ইন্টারএক্টিভিটি বাড়বে। কারন এখানে অলমোস্ট সকল কাজ ফ্রন্টেন্ডেই সমাধা হয়। শুধু ডাটা নেওয়ার জন্য সার্ভারে রিকোয়েস্ট করা হয়। সুতরাং ওয়েব পেজের সকল কিছুই ডায়নামিক্যালি আপডেট হয়।
- সার্ভারের উপর লোড কমে যায়। ফলশ্রুতিতে সার্ভার খরচ ও কমে যায়।
- প্রোগ্রেসিভ ওয়েব এপ (PWA) বিল্ড করার মাধ্যমে অফলাইন সাপোর্ট পাওয়া যায়। অর্থাৎ ইন্টারনেট না থাকলেও ব্রাউজার ক্যাশ থেকে এপ্লিকেশন এক্সেস করা যায়।
ক্লায়েন্ট সাইড রেন্ডারিং এর অসুবিধা-
- ইনিশিয়াল লোডিং টাইম বেশি লাগে।
- SEO ভালভাবে করা যায়না।
- ওল্ড ভার্সনের ব্রাউজার অথবা যে ব্রাউজারে জাভাস্ক্রিপ্ট ডিস্যাবল করা আছে সেখানে সব ফাংশনালিটি কাজ করেনা।
সার্ভার-সাইড-রেন্ডারিং নাকি ক্লায়েন্ট সাইড রেন্ডারিং, কোনটা উত্তম?
এইটা আসলে এক কথায় বলা মুশকিল। কারন একেক এপ্লিকেশনের রিকোয়ারমেন্ট একেক রকম। এবং এটা মেইনলি এপ্লিকেশন রিকোয়ারমেন্টের উপর ভিত্তি করে নির্ধারন করা হয়।
ধরেন, আপনি একটা ওয়েবসাইট বানাতে চাচ্ছেন ইনিশিয়াল লোডিং টাইম মিনিমাম রাখা দরকার, ভাল সার্চ ইঞ্জিন অপ্টিমাইজেশন দরকার, এবং সব ব্রাউজারে সাপোর্ট দরকার তাহলে আপনার জন্য অবশ্যই সার্ভার-সাইড-রেন্ডারিং ভাল স্যুট করবে। আবার আপনার যদি প্রোগ্রেসিভ ওয়েব এপের সাথে হাই ইন্ট্যার্যাকশন দরকার হয় তাহলে নিশ্চিন্তে ক্লায়েন্ট-সাইড-রেন্ডারিং করতে হবে।
তবে, বর্তমানে হাইব্রিড এপ্রোচের জনপ্রিয়তা বাড়ছে। হাইব্রিড হল, এপ্লিকেশনের ইনিশিয়াল পেজ লোডিং সার্ভার-সাইড-রেন্ডারিং করে হবে, এরপর পেজ কম্পোনেন্ট আপডেটের বাকি সব কাজ ক্লায়েন্ট-সাইড-রেন্ডারিং করে হবে। এই হাইব্রিড এপ্রোচের জন্য ইনারশিয়ার উৎপত্তি।
Inertia.js কি?
একটি ওয়েব এপ্লিকেশ ডেভেলপ করার সময় দুইটা লেয়ারের কথা মাথায় রেখে ডেভেলপ করা হয়। একটা ফ্রন্টেন্ড লেয়ার, অপরটা ব্যাকেন্ড লেয়ার। ব্যাকেন্ডের জন্য আমরা অনেক ধরনের ফ্রেমওয়ার্ক যেমনঃ লারাভেল, জ্যাঙ্গো ইত্যাদি ব্যবহার করে থাকি। এসকল ফ্রেমওয়ার্কের নিজস্ব স্ট্রাকচার আছে যেমনঃ রাউটার, ভিউ, কন্ট্রোলার, মডেল ইত্যাদি। সাধারনত সব ধরনের ব্যাকেন্ড ফ্রেমওয়ার্কেই ফ্রন্টেন্ড ভিউ এর জন্য বেশ কিছু লিমিটেশন থাকে। বিশেষ করে রাউটিং করার অথবা ক্লায়েন্ট-সার্ভার ডাটা ট্রান্সফারের সময় ফুল পেজ রিলোড হয় যা ইউজার এক্সপেরিয়েন্সকে ক্ষতি করে। কিন্ত মডার্ন ওয়েব এপ্লিকেশনের একটি বড় চাহিদা হল সিংগেল পেজ এপ্লিকেশন (SPA)। ট্রাডিশনাল ব্যাকেন্ড ফ্রেমওয়ার্ক দিয়ে SPA বানানো সম্ভব না। এক্ষেত্রে একমাত্র ভরসা হল কোন একটি ফ্রন্টেন্ড ফ্রেমওয়ার্ক যেমনঃ React, Vue, Angular ইত্যাদি।
এ পর্যায়ে এসে এপ্লিকেশন আর ইউনিফাইড থাকেনা। ফ্রন্টেন্ড এবং ব্যাকেন্ড আলাদা হয়ে যায় এবং এদের নিজেদের মধ্যে যোগাযোগ হয় API এর মাধ্যমে। ফ্রন্টেন্ড কোন ডাটা নিতে চাইলে ব্যাকেন্ডের API কল করে নেয় আবার ডাটা দিতে চাইলেও API কল করে দেয়। এক্ষেত্রে ডেভেলপার কে ফ্রন্টেন্ডের জন্য API ডেভেলপ করতে হয়।
ঠিক এই যায়গাতে এসে Inertia এর ম্যাজিক। Inertia মুলত একটি রাউটিং লাইব্রেরী। যে কিনা ব্যাকেন্ড ফ্রেমওয়ার্কের ভিতর বসে ফ্রন্টেন্ড এর সকল কাজ সমাধা করে। ফ্রন্টেন্ড-ব্যাকেন্ড ডাটা ট্রান্সফার, রাউটিং ইত্যাদি কাজ গুলো সে নিজে দায়িত্বে করে থেকে এবং এমনভাবে করে যেন পুরো এপ্লিকেশনটি একটি SPA এর মত আচরন করে। এবং সবথেকে গুরুত্বপূর্ন হল, Inertia এর জন্য ডেভেলপার কে আলাদা করে API ডেভেলপ করতে হয়না। ব্যাকেন্ড ফ্রেমওয়ার্কের ডিফল্ট রাউটিং ই সে ব্যবহার করতে পারে।
মনে রাখতে হবে, ইনারশিয়া কোন আলাদা ফ্রেমওয়ার্ক না কিংবা অন্য কোন ব্যাকেন্ড বা ফ্রন্টেন্ড ফ্রেমওয়ার্কের রিপ্লেসমেন্ট ও না। বরং ইনারশিয়া ডিজাইন ই করা হইছে অন্য ফ্রেমওয়ার্কের সাথে মিলে কাজ করার জন্য। এটাকে আমরা ক্লায়েন্ট সাইড এবং সার্ভার সাইডের মধ্যে একটি ব্রিজের মত ভাবতে পারি। যে ব্রিজ উভয় পাশকে একটি এডাপ্টারের মাধ্যমে সংযোগ করে। এখন পর্যন্ত ইনারশিয়া এডাপ্টার হিসেবে অফিসিয়ালি Laravel, Rails, React,, Vue, এবং Svelt সাপোর্ট করে।
Inertia.js কাদের জন্য?
Inertia.js মুলত সেইসব ব্যাকেন্ড ডেভেলপারদের জন্য যারা মূলত বিভিন্ন ফ্রেমওয়ার্ক যেমনঃ লারাভেল, জ্যাংগো ইত্যাদি দিয়ে সার্ভার সাইড রেন্ডার্ড এপ্লিকেশন ডেভেলপ করেন কিন্তু Vue, React অথবা svelt দিয়ে ফ্রন্টেন্ড বানাতে চান। তবে এক্ষেত্রে লারাভেল ডেভেলপাররা বিশেষ সুবিধা পাবেন কারন Inertia.js তৈরিই হইছে লারাভেল ফ্রেমওয়ার্ক কে মাথায় রেখে।
যখন একজন লারাভেল ডেভেলপার কে বলা হয় আপনার সার্ভার সাইড রেন্ডার্ড এপ্লিকেশন কে ক্লায়েন্ট সাইড রেন্ডার্ড বানাতে হবে, তখন সর্বপ্রথম যে চিন্তাটা মাথায় আসে তা হল, ও ক্লায়েন্ট সাইড? তাহলে তো REST API অথবা GraphQL API ডেভেলপ করতে হবে। অথেনটিকেশন করতে হবে, রোল ম্যানেজ করতে, স্টেট ম্যানেজ করতে হবে ব্লা ব্লা। এটা সম্পুর্নরূপে একটা প্যারাডাইম শিফট।
একজন ফুলস্ট্যাক ডেভেলপারকে সম্পুর্ন আলাদা দুইটা এপ্লিকেশন কোডবেজ ম্যানেজ করতে হয় অথবা দুইজন ডেভেলপার কে দুইটা এপ্লিকেশন ডেভেলপ করতে হয়। এক্ষেত্রে কোম্পানির ও ডেভেলপমেন্ট খরচ বেড়ে যায়। কারন যদি দুইটা আলাদা এপ্লিকেশন ডেভেলপ করতে হয় সেক্ষেত্রে দুইজন ডেভেলপার কে মেইন্টেইন করতে হবে। আবার একজন ফুলস্ট্যাক ডেভেলপার ম্যানেজ করলে ডেভেলপমেন্ট টাইম ও বেশি লাগছে যা খরচ বাড়ারই নামান্তর।
Inertia.js কিভাবে কাজ করে?
Inertia কাজ করার জন্য আলাদা তেমন কিছু করা লাগেনা। ব্যাকেন্ডে ফ্রেমওয়ার্কে যেসব ফাংশনালিটিগুলো আছে, যেমন, রাউট, ভিউ, মডেল, কন্ট্রোলার, মিডলওয়্যার, অথেনটিকেশন, ডাটা ফেচিং ইত্যাদি আগের মতই ব্যবহার করা যাবে।
Inertia শুধুমাত্র এপ্লিকেশনের ভিউ লেয়ারটাকে রিপ্লেস করে। লারাভেলে যে ব্লেড টেমপ্লেটকে ভিউ হিসেবে সার্ভার সাইডে রেন্ডার করা হয় তার বদলে জাভাস্ক্রিপ্ট পেজ কম্পোনেন্ট হিসেবে রেন্ডার হবে। কিন্তু এখানে শুধু মাত্র জাভাস্ক্রিপ্ট পেজ রিটার্ন করলেই সেটা সিংগেল পেজ এপ্লিকেশন হয়ে যাবেনা। পেজের মধ্যে যেকোন লিংক ক্লিক করলেই ফুল পেজ রিলোড হয়ে ইউজার এক্সপেরিয়েন্স কে ব্যাহত করবে। এখানেই ইনারশিয়ার আসল কাজ।
ইনারশিয়া যেটা করে, সে ইউজারকে এক পেজ থেকে আরেক পেজে নিয়ে যাবে কোন প্রকার রিলোড ছাড়াই। ইনারশিয়ার যে বিল্ট ইন Link কম্পোনেন্ট আছে সে এটা করে থাকে। আমাদের বহুল পরিচিত <a> ট্যাগের রিপ্লেসমেন্ট এই Link কম্পোনেন্ট।যখন কোন লিঙ্কে ক্লিক করা হবে তখন Link কম্পোনেন্ট সেই ক্লিক ইভেন্টকে ধরে সেটাকে XHR এর মাধ্যমে সার্ভারে রিকোয়েস্ট পাঠায় এবং পেজ কম্পোনেন্ট রিসিভ করে শো করে। ক্লিক করা ছাড়াও জাভাস্ক্রিপ্টের router.visit() মেথডের মাধ্যমেও পেজ ভিজিট করা যায়।
ইনারশিয়া যখন ফ্রন্টেন্ড থেকে XHR রিকোয়েস্ট পাঠায় তখন সার্ভারে থাকা InertiaMiddleware বুঝে ফেলে এই রিকোয়েস্ট ইনারশিয়া পাঠাইছে, তখন সে ট্রাডিশনাল HTML Response পাঠানোর বদলে জাভাস্ক্রিপ্ট পেজ কম্পোনেন্ট পাঠায় এবং ডাটাকে এই কম্পোনেন্টের ভিতর JSON আকারে পুশ করে। ফ্রন্টেন্ডে থাকা ইনারশিয়া তখন এটা রিসিভ করে এবং ব্রাউজার হিস্ট্রি স্টেট আপডেট করে এবং রিকোয়েস্টেড পেজে নিয়ে যায়।
পুরো কাজটা হয় কোন প্রকার পেজ রিলোড ছাড়াই, যার কারনে মনে হয় এটা একটা সিংগেল পেজ এপ্লিকেশন।
The Protocol
একটা ইনারশিয়া এপ্লিকেশনের প্রথম লোডিং এর সময় সে রেগুলার ফুলপেজ ব্রাউজার রিকোয়েস্ট পাঠায় সার্ভার এ। এই রিকোয়েস্টের সাথে কোন স্পেশাল ইনারশিয়া হেডার ডাটা থাকেনা। ফলে সার্ভার ও এটাকে ইনারশিয়া রিকোয়েস্ট হিসেবে গণ্য করেনা এবং রেসপন্স হিসেবে সেও একটা রেগুলার HTML Response পাঠায়। এই রেসপন্সের সাথে অন্যান্য রেগুলার রেস্পন্সের মতই HTML, CSS , JavaScript কোড থাকে যেগুলো এসে পেজের রুট div এর মধ্যে বসে যায়। তবে এর সাথে একটা জিনিষ থাকে, সেটা হল data-page attribute যার মধ্যে JSON data থাকে।
<body>
<div id="app" data-page='{"component":"Event","props":{"event":{"id":80,"title":"Birthday party","start_date":"2019-06-02","description":"Come out and celebrate Jonathan's 36th birthday party!"}},"url":"/events/80","version":"c32b8e4965f418ad16eaebba1d4e960f"}'></div>
</body>
একবার সাইট লোড হয়ে গেলে এরপর থেকে প্রত্যেকটা রিকোয়েস্ট XHR এর মাধ্যমে যায় যেখানে প্রত্যেকটা রিকোয়েস্টের সাথে X-Inertial:true হেডার থাকে।
GET: <http://example.com/events/80>
Accept: text/html, application/xhtml+xml
X-Requested-With: XMLHttpRequest
X-Inertia: true
X-Inertia-Version: 6b16b94d7c51cbe5b1fa42aac98241d5
লারাভেলে থাকা InertiaMiddleware এই X-Inertia: true
attribute দেখে চিনতে পারে এটা Inertia Request.
The Page Object
Inertia তে সার্ভার এবং ক্লায়েন্ট এর মধ্যে ডাটা আদান-প্রদান হয় Page Object এর মাধ্যমে। Page Object এর মধ্যে পেজ কম্পোনেন্ট রেন্ডার করার জন্য, ব্রাউজার হিস্ট্রি ম্যানেজ করার জন্য, এবং এসেট ভার্সনিং করার জন্য প্রয়োজনীয় ডাটা থাকে। অব্জেক্ট এর ৪ টা প্রোপার্টি থাকে। যেমনঃ
- component : যার মধ্যে জাভাস্ক্রিপ্ট পেজ কম্পোনেন্ট এর নাম থাকে।
- props: যার মধ্যে সকল ডাটা থাকে। যেটা আমরা লারাভেলে ব্লেড টেমপ্লেটে compact() এর মাধ্যমে পাঠাতাম। এটা ইনারশিয়াতে props হিসেবে আসবে।
- url: পেজের URL থাকে এর মধ্যে।
- version: Asset যেমন css, img ইত্যাদির ভার্সন থাকে।
যখন ফুলপেজ ভিজিট হয় তখন উপরের প্রোপার্টিগুলো data-page মধ্যে JSON হিসেবে আসে যখন Inertia ভিজিট হয় তখন এটা JSON Payload হিসেবে আসে।
Events : Life Cycle Hooks
Inertia তে অনেক গুলো ইভেন্ট ড্রাইভেন লাইফ সাইকেল হুক আছে। ইনারশিয়া ক্লায়েন্ট থেকে যখন লারাভেল সার্ভারে রাউটার এর মাধ্যমে রিকোয়েস্ট পাঠায় তখন একটা রিকোয়েস্ট শুরু থেকে শেষ অবধি বেশ কয়েকটা ইভেন্ট আছে যেগুলো হুকের সাহায্যে ধরে কাস্টম কোডব্লক রান করা যায়। এসকল ইনভেন্ট কে লিসেন করা হয় router.on()
মেথডের মাধ্যমে।
Start:
import { router } from '@inertiajs/vue3'
router.on('start', (event) => {
console.log(`Starting a visit to ${event.detail.visit.url}`)
})
Before:
import { router } from '@inertiajs/vue3'
router.on('before', (event) => {
if (!confirm('Are you sure you want to navigate away?')) {
event.preventDefault()
}
})
Progress:
import { router } from '@inertiajs/vue3'
router.on('progress', (event) => {
this.form.progress = event.detail.progress.percentage
})
Success:
import { router } from '@inertiajs/vue3'
router.on('success', (event) => {
console.log(`Successfully made a visit to ${event.detail.page.url}`)
})
Error:
import { router } from '@inertiajs/vue3'
router.on('error', (errors) => {
console.log(errors)
})
Invalid:
import { router } from '@inertiajs/vue3'
router.on('invalid', (event) => {
console.log(`An invalid Inertia response was received.`)
console.log(event.detail.response)
})
Exception:
import { router } from '@inertiajs/vue3'
router.on('exception', (event) => {
console.log(`An unexpected error occurred during an Inertia visit.`)
console.log(event.detail.error)
})
Finish:
import { router } from '@inertiajs/vue3'
router.on('finish', (event) => {
NProgress.done()
})
Navigate:
import { router } from '@inertiajs/vue3'
router.on('navigate', (event) => {
console.log(`Navigated to ${event.detail.page.url}`)
})
Scroll Management
Inertia এর চমৎকার ফিচারগুলোর মধ্যে একটি হল স্ক্রল ম্যানেজমেন্ট। ট্রাডিশনাল ওয়েব এপ্লিকেশনে আমরা যখন এক পেজ থেকে অন্য পেজে যাই তখন পেজের টপ থেকে দেখায়। ইনারশিয়া এক্ষেত্রে স্ক্রল পজিশন মনে রাখে। পেজের ঠিক যেখানে রেখে আমি অন্য পেজে গিয়েছিলাম, ফিরে আসলে ঠিক সেইখান থেকেই দেখায়। Link কম্পোনেন্টের ভিতর preserve-scroll attribute সেট করে দিলেই এটা কাজ করা শুরু করবে।
import { Link } from '@inertiajs/vue3'
<Link href="/" preserve-scroll>Home</Link>
Installation of Inertia & Vue inside Laravel
Inertia ইনস্টল করার আগে যেকোন একটি সার্ভার সাইড ফ্রেমওয়ার্ক সেটাপ করে নিতে হবে। ইনারশিয়াকে লারাভেলের জন্য স্পেশালাইজড হিসেবে বানানো হইচে। এ কারনে লারাভেলের সাথে ইনারশিয়া সব থেকে বেস্ট আউটপুট দেবে।
লারাভেলের Breeze প্যাকেজ ইনস্টল করলে অটোমেটিক ভিউ এবং ইনারশিয়া ইনস্টল এবং কনফিগার হয়ে যায়।
Breeze প্যাকেজ ছাড়াও চাইলে ম্যানুয়ালি লারাভেলের ভিতর ভিউ এবং ইনারশিয়া ইনস্টল ও কনফিগার করা যায়। এজন্য আমাদের inertia-laravel প্যাকেজ টা প্রথমে ইনস্টল করে নিতে হবে।
composer require inertiajs/inertia-laravel
resources/view/app.blade.php
তৈরি করতে হবে যেটা এপ্লিকেশন ইনিশিয়াল লোডিং এর সময় ভিউ হবে।
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
@vite('resources/js/app.js')
@inertiaHead
</head>
<body>
@inertia
</body>
</html>
@inertiaHead
এবং @inertia
এই দুইটা ইনারশিয়া ডিরেক্টিভের মধ্যে পেজের জন্য প্রয়োজনীয় সকল এসেট (html, css, js etc ) লোড হবে। এবং বাই ডিফল্ট এই app.blade.php ফাইল ই রুট ভিউ হিসেবে ব্যবহৃত হয়। তবে চাইলে এটাকে Inertia::setRootView()
মেথডের মাধ্যমে পরিবর্তন করা যাবে।
পরবর্তি ধাপে একটি লারাভেল Middleware তৈরি করতে হবে।
php artisan inertia:middleware
Middleware তৈরি হয়ে গেলে এটাকে কার্নেলে রেজিস্টার করতে হবে। তার জন্য App\Http\Kernel এ web মিডলওয়্যার গ্রুপের শেষের দিকে নিচে মত করে এড করে দিতে হবে।
'web' => [
// ...
\\App\\Http\\Middleware\\HandleInertiaRequests::class,
],
এখন লারাভেলের কন্ট্রোলার থেকে রেস্পন্স রিটার্ন করা যাবে।
use Inertia\\Inertia;
class EventsController extends Controller
{
public function show(Event $event)
{
return Inertia::render('Event/Show', [
'event' => $event->only(
'id',
'title',
'start_date',
'description'
),
]);
}
}
এতক্ষন আমরা ইনারশিয়ার সার্ভার সাইড কনফিগার করলাম। এবার ক্লায়েন্ট সাইড কনফিগার করতে হবে।
তার জন্য @inertiajs/vue3 নামে জাভাস্ক্রিপ্টের একটি প্যাকেজ ইনস্টল করতে হবে।
npm install @inertiajs/vue3
এরপর main.js ফাইলকে নিচের কোডের মত মডিফাই করে দিতে হবে।
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
createInertiaApp({
resolve: name => {
const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
return pages[`./Pages/${name}.vue`]
},
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.mount(el)
},
})