قابلیت Module Federation در Vite و استفاده از کامپوننت های ریکت در Vue
توی این آموزش میخوایم با استفاده از قابلیت Module Federation در Vite یک کامپوننت ساده در ریکت بسازیم و از اون توی پروژه ی vue استفاده کنیم. در این آموزش از نسخه ی 18 ریکت و نسخه ی 3 vue و همچنین از نسخه ی 3 ویت استفاده شده است.
برای راحتی کار من یک پروژه ی React و Vue با استفاده از Vite ساخته ام که از لینک زیر میتونید clone بگیرید:
https://github.com/modos/Vite-Module-Federation-Using-React-in-Vue-Tutorial
من کانفیگ پروژه رو خیلی ساده انجام دادم، برای این که کامپوننت های ریکت رو با پسوند js به جای jsx بسازید باید از babel استفاده کنید و... که این موارد رو به عنوان تمرین به خودتون میدم:) پکیجی برای Vite ساخته شده به نام originjs/vite-plugin-federation که از لینک زیر میتونید داکیومنتش رو بخونید، پروژه های مختلفی هم به عنوان example گذاشتن که میتونید کامل طرز کار باهاش رو یاد بگیرید.
https://github.com/originjs/vite-plugin-federation
ما هم قراره از این پکیج استفاده کنیم.
فایل package.json مربوط به پروژه ریکت:
{ "name": "react-test", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@originjs/vite-plugin-federation": "^1.1.11", "@types/react": "^18.0.24", "@types/react-dom": "^18.0.8", "@vitejs/plugin-react": "^2.2.0", "vite": "^3.2.3" }}
فایل package.json مربوط به پروژه vue:
{ "name": "vue-test", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": { "vue": "^3.2.41" }, "devDependencies": { "@originjs/vite-plugin-federation": "^1.1.11", "@vitejs/plugin-vue": "^3.2.0", "vite": "^3.2.3" }}
بعد از تغییرات فایل های بالا با استفاده از yarn یا npm توی هر پروژه بصورت جداگانه پکیج ها رو نصب می کنیم.
توی پروژه ی ریکت یک کامپوننت به نام Button.jsx میسازیم:
import React from "react"import ReactDOM from "react-dom"const buttonStyle = { backgroundColor: 'green', color: 'white', padding: '8px', fontWeight: 'bold', boxShadow: 'none', border: 'none', borderRadius: '5px', cursor: 'Pointer'}const buttonClicked = () => { alert("Vite React !")}const Button = (el, props) => { ReactDOM.render(<button style={buttonStyle} onClick={buttonClicked}>{props.value}</button>, el)}export {Button};
توی این کامپوننت یک دکمه ی ساده ساخته ایم با یه سری استایل و event که وقتی روی اون کلیک می کنیم یک alert باز بشه. محتوای دکمه رو هم از props می گیریم. نکته ای که هست این button رو توی ReactDom.render باید بزاریم. موقع استفاده از این دکمه باید props.value و همچنین parent ای که باید توی اون inject بشه رو بهش پاس بدیم.
محتوای فایل های App.jsx و main.jsx اهمیتی ندارند و حتی میتونیم اون ها رو پاک کنیم.
یک فایل در روت پروژه به نام vite.config.js میسازیم:
import {defineConfig} from 'vite'import federation from "@originjs/vite-plugin-federation"import react from '@vitejs/plugin-react'// https://vitejs.dev/config/export default defineConfig({ plugins: [ react(), federation({ name: 'react', filename: "remoteEntry.js", exposes: { './Button': './src/Button.jsx' }, }) ], build: { target: 'esnext', minify: true, cssCodeSplit: false }})
کانفیگ های مختلف vite رو توی این فایل قرار میدیم. ابتدا پکیج vite-plugin-federation رو ایمپورت می کنیم. در فسمت defineConfig و توی plugins ابتدا پلاگین react رو کال میکنیم. برای بیلد کامپوننت های react این کار الزامیه، بعد توی تابع federation یک آبجکت میسازیم. داخل این آبجکت ابتدا یک نام انتخاب می کنیم، از این نام برای ایمپورت فایل مون توی یک پروژه ی دیگه استفاده میشه. سپس توی قسمت filename اسم فایل خروجی ای که میخوایم تولید کنیم و کامپوننت هامون اونجا اضافه بشن رو انتخاب می کنیم که من اینجا remoteEntry.js گذاشتم. در قسمت exposes یک key/value اضافه می کنیم. key میشه اسم کامپوننت مون و value هم میشه آدرس فایل کامپوننت مون توی پروژه الان مون یعنی ریکت. توی قسمت build تارگت رو میزاریم esnext تا از نسخه ی 2021 جاوا اسکریپت استفاده کنه. minify رو true میزاریم تا فایل خروجی مون کم حجم بشه و نمیخوایم فایل های css مون جدا باشن پس cssCodeSplit رو false میزاریم.
حالا با استفاده از دستور vite build از پروژه ی ریکت بیلد می گیریم. بعد از اون در فولدر dist/assets یک فایل به نام remoteEntry.js ساخته شده است. این فایل کامپوننت Button ای که ساختیم رو توی خودش داره و میتونیم از اون توی پروژه های دیگه استفاده کنیم.
حالا برین توی فولدر dist و اون رو روی سرور لوکال (یا هر جای دیگه) بیارین بالا تا با یه آدرس در دسترس باشه.
توی پروژه ی vue فایل vite.config.js مون به این شکله:
import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import federation from '@originjs/vite-plugin-federation'// https://vitejs.dev/config/export default defineConfig({ plugins: [ vue(), federation({ name: 'vue', remotes: { react: 'http://127.0.0.1:5500/react-test/dist/assets/remoteEntry.js' } }) ]})
توی کانفیگ این بار به جای ()react از ()vue استفاده می کنیم. توی بخش remotes میگیم که چه فایل هایی رو باید خارج از پروژه مون بگیره و بتونیم توی پروژه ی خودمون import کنیم. اسم کامپوننتی که سمت ریکت ساختیم رو مشخص می کنیم . آدرس فایل remoteEntry.js رو بهش میدیم.
حالا میخوایم توی فایل App.Vue از کامپوننت Button ریکت ای که ساختیم استفاده کنیم:
<script setup>import {Button} from 'react/Button'import {onMounted} from "vue";onMounted(() => { Button(document.getElementById('react'), {value: 'Hello From React'});})</script><template> <div id="react"> </div></template>
Button رو از react/Button ایمپورت می کنیم. توی تابع onMounted میگیم که موقعی که فایل مون mount شد به کامپوننت ریکت یعنی Button یه value بده که توی alert نشون داده بشه. به همره المنتی که میخوایم اون کامپوننت توی اون inject بشه که اینجا من یه div با آیدی react ساختم و گفتم باید button توی این div بیاد.
خب تموم شد. با استفاده از دستور vite dev پروژه ی vue رو میاریم بالا و می بینیم که button ریکت مون وسط صفحه قرار داره با همون استایلی که تعریف کردیم واسش با متن Hello From React که توی vue بهش پاس دادیم.
در آخر اینم بگم که Webpack هم قابلیت Module Federation رو داره، دیدن این ویدیو رو هم بهتون پیشنهاد میدم: