Ένας περιεκτικός οδηγός για το React reconciliation, που εξηγεί πώς λειτουργεί το virtual DOM, οι αλγόριθμοι diffing και οι βασικές στρατηγικές για τη βελτιστοποίηση της απόδοσης σε σύνθετες εφαρμογές React.
React Reconciliation: Κατάκτηση του Diffing του Virtual DOM και Βασικές Στρατηγικές για Απόδοση
Το React είναι μια ισχυρή βιβλιοθήκη JavaScript για τη δημιουργία διεπαφών χρήστη. Στον πυρήνα του βρίσκεται ένας μηχανισμός που ονομάζεται reconciliation, ο οποίος είναι υπεύθυνος για την αποτελεσματική ενημέρωση του πραγματικού DOM (Document Object Model) όταν αλλάζει η κατάσταση ενός component. Η κατανόηση του reconciliation είναι ζωτικής σημασίας για τη δημιουργία αποδοτικών και επεκτάσιμων εφαρμογών React. Αυτό το άρθρο εμβαθύνει στην εσωτερική λειτουργία της διαδικασίας reconciliation του React, εστιάζοντας στο virtual DOM, τους αλγόριθμους diffing και τις στρατηγικές για τη βελτιστοποίηση της απόδοσης.
Τι είναι το React Reconciliation;
Το Reconciliation είναι η διαδικασία που χρησιμοποιεί το React για να ενημερώσει το DOM. Αντί να χειρίζεται απευθείας το DOM (κάτι που μπορεί να είναι αργό), το React χρησιμοποιεί ένα virtual DOM. Το virtual DOM είναι μια ελαφριά, ενσωματωμένη στη μνήμη αναπαράσταση του πραγματικού DOM. Όταν αλλάζει η κατάσταση ενός component, το React ενημερώνει το virtual DOM, υπολογίζει το ελάχιστο σύνολο αλλαγών που απαιτούνται για την ενημέρωση του πραγματικού DOM και, στη συνέχεια, εφαρμόζει αυτές τις αλλαγές. Αυτή η διαδικασία είναι σημαντικά πιο αποτελεσματική από τον απευθείας χειρισμό του πραγματικού DOM σε κάθε αλλαγή κατάστασης.
Σκεφτείτε το σαν να ετοιμάζετε ένα λεπτομερές σχέδιο (virtual DOM) ενός κτιρίου (πραγματικό DOM). Αντί να γκρεμίζετε και να ξαναχτίζετε ολόκληρο το κτίριο κάθε φορά που χρειάζεται μια μικρή αλλαγή, συγκρίνετε το σχέδιο με την υπάρχουσα δομή και κάνετε μόνο τις απαραίτητες τροποποιήσεις. Αυτό ελαχιστοποιεί τις διαταραχές και κάνει τη διαδικασία πολύ πιο γρήγορη.
Το Virtual DOM: Το Μυστικό Όπλο του React
Το virtual DOM είναι ένα αντικείμενο JavaScript που αντιπροσωπεύει τη δομή και το περιεχόμενο του UI. Είναι ουσιαστικά ένα ελαφρύ αντίγραφο του πραγματικού DOM. Το React χρησιμοποιεί το virtual DOM για να:
- Παρακολουθεί Αλλαγές: Το React παρακολουθεί τις αλλαγές στο virtual DOM όταν ενημερώνεται η κατάσταση ενός component.
- Diffing: Στη συνέχεια, συγκρίνει το προηγούμενο virtual DOM με το νέο virtual DOM για να καθορίσει τον ελάχιστο αριθμό αλλαγών που απαιτούνται για την ενημέρωση του πραγματικού DOM. Αυτή η σύγκριση ονομάζεται diffing.
- Μαζικές Ενημερώσεις: Το React ομαδοποιεί αυτές τις αλλαγές και τις εφαρμόζει στο πραγματικό DOM σε μία μόνο λειτουργία, ελαχιστοποιώντας τον αριθμό των χειρισμών DOM και βελτιώνοντας την απόδοση.
Το virtual DOM επιτρέπει στο React να εκτελεί πολύπλοκες ενημερώσεις UI αποτελεσματικά χωρίς να αγγίζει απευθείας το πραγματικό DOM για κάθε μικρή αλλαγή. Αυτός είναι ένας βασικός λόγος για τον οποίο οι εφαρμογές React είναι συχνά πιο γρήγορες και πιο ανταποκρινόμενες από τις εφαρμογές που βασίζονται σε απευθείας χειρισμό DOM.
Ο Αλγόριθμος Diffing: Εύρεση των Ελάχιστων Αλλαγών
Ο αλγόριθμος diffing είναι η καρδιά της διαδικασίας reconciliation του React. Καθορίζει τον ελάχιστο αριθμό λειτουργιών που απαιτούνται για να μετατραπεί το προηγούμενο virtual DOM στο νέο virtual DOM. Ο αλγόριθμος diffing του React βασίζεται σε δύο κύριες υποθέσεις:
- Δύο στοιχεία διαφορετικών τύπων θα παράγουν διαφορετικά δέντρα. Όταν το React συναντά δύο στοιχεία με διαφορετικούς τύπους (π.χ., ένα
<div>και ένα<span>), θα αποσυνδέσει εντελώς το παλιό δέντρο και θα συνδέσει το νέο δέντρο. - Ο προγραμματιστής μπορεί να υποδείξει ποια θυγατρικά στοιχεία μπορεί να είναι σταθερά σε διαφορετικές αποδόσεις με ένα
keyprop. Η χρήση τουkeyprop βοηθά το React να προσδιορίσει αποτελεσματικά ποια στοιχεία έχουν αλλάξει, προστεθεί ή αφαιρεθεί.
Πώς Λειτουργεί ο Αλγόριθμος Diffing:
- Σύγκριση Τύπου Στοιχείου: Το React συγκρίνει πρώτα τα ριζικά στοιχεία. Εάν έχουν διαφορετικούς τύπους, το React καταστρέφει το παλιό δέντρο και δημιουργεί ένα νέο δέντρο από την αρχή. Ακόμα κι αν οι τύποι των στοιχείων είναι οι ίδιοι, αλλά τα χαρακτηριστικά τους έχουν αλλάξει, το React ενημερώνει μόνο τα αλλαγμένα χαρακτηριστικά.
- Ενημέρωση Component: Εάν τα ριζικά στοιχεία είναι το ίδιο component, το React ενημερώνει τα props του component και καλεί τη μέθοδο
render()του. Η διαδικασία diffing συνεχίζεται στη συνέχεια αναδρομικά στα θυγατρικά στοιχεία του component. - Reconciliation Λίστας: Κατά την επανάληψη σε μια λίστα θυγατρικών στοιχείων, το React χρησιμοποιεί το
keyprop για να καθορίσει αποτελεσματικά ποια στοιχεία έχουν προστεθεί, αφαιρεθεί ή μετακινηθεί. Χωρίς κλειδιά, το React θα έπρεπε να αποδώσει ξανά όλα τα θυγατρικά στοιχεία, κάτι που μπορεί να είναι αναποτελεσματικό, ειδικά για μεγάλες λίστες.
Παράδειγμα (Χωρίς Κλειδιά):
Φανταστείτε μια λίστα στοιχείων που αποδίδονται χωρίς κλειδιά:
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
Εάν εισαγάγετε ένα νέο στοιχείο στην αρχή της λίστας, το React θα πρέπει να αποδώσει ξανά και τα τρία υπάρχοντα στοιχεία, επειδή δεν μπορεί να καταλάβει ποια στοιχεία είναι τα ίδια και ποια είναι νέα. Βλέπει ότι το πρώτο στοιχείο της λίστας έχει αλλάξει και υποθέτει ότι *όλα* τα στοιχεία της λίστας μετά από αυτό έχουν επίσης αλλάξει. Αυτό συμβαίνει επειδή χωρίς κλειδιά, το React χρησιμοποιεί reconciliation που βασίζεται σε δείκτες. Το virtual DOM θα "σκεφτόταν" ότι το 'Item 1' έγινε 'New Item' και πρέπει να ενημερωθεί, όταν στην πραγματικότητα απλώς προσθέσαμε το 'New Item' στην αρχή της λίστας. Το DOM πρέπει στη συνέχεια να ενημερωθεί για τα 'Item 1', 'Item 2' και 'Item 3'.
Παράδειγμα (Με Κλειδιά):
Τώρα, εξετάστε την ίδια λίστα με κλειδιά:
<ul>
<li key="item1">Item 1</li>
<li key="item2">Item 2</li>
<li key="item3">Item 3</li>
</ul>
Εάν εισαγάγετε ένα νέο στοιχείο στην αρχή της λίστας, το React μπορεί να καθορίσει αποτελεσματικά ότι έχει προστεθεί μόνο ένα νέο στοιχείο και τα υπάρχοντα στοιχεία έχουν απλώς μετακινηθεί προς τα κάτω. Χρησιμοποιεί το key prop για να αναγνωρίσει τα υπάρχοντα στοιχεία και να αποφύγει περιττές επανα-αποδόσεις. Η χρήση κλειδιών με αυτόν τον τρόπο επιτρέπει στο virtual DOM να κατανοήσει ότι τα παλιά στοιχεία DOM για τα 'Item 1', 'Item 2' και 'Item 3' δεν έχουν αλλάξει πραγματικά, επομένως δεν χρειάζεται να ενημερωθούν στο πραγματικό DOM. Το νέο στοιχείο μπορεί απλώς να εισαχθεί στο πραγματικό DOM.
Το key prop πρέπει να είναι μοναδικό μεταξύ των αδελφών στοιχείων. Ένα κοινό μοτίβο είναι να χρησιμοποιείτε ένα μοναδικό ID από τα δεδομένα σας:
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
Βασικές Στρατηγικές για τη Βελτιστοποίηση της Απόδοσης του React
Η κατανόηση του React reconciliation είναι μόνο το πρώτο βήμα. Για να δημιουργήσετε πραγματικά αποδοτικές εφαρμογές React, πρέπει να εφαρμόσετε στρατηγικές που βοηθούν το React να βελτιστοποιήσει τη διαδικασία diffing. Ακολουθούν μερικές βασικές στρατηγικές:
1. Χρησιμοποιήστε Αποτελεσματικά τα Κλειδιά
Όπως καταδείχθηκε παραπάνω, η χρήση του key prop είναι ζωτικής σημασίας για τη βελτιστοποίηση της απόδοσης λίστας. Βεβαιωθείτε ότι χρησιμοποιείτε μοναδικά και σταθερά κλειδιά που αντικατοπτρίζουν με ακρίβεια την ταυτότητα κάθε στοιχείου στη λίστα. Αποφύγετε τη χρήση δεικτών πίνακα ως κλειδιά εάν η σειρά των στοιχείων μπορεί να αλλάξει, καθώς αυτό μπορεί να οδηγήσει σε περιττές επανα-αποδόσεις και απροσδόκητη συμπεριφορά. Μια καλή στρατηγική είναι να χρησιμοποιήσετε ένα μοναδικό αναγνωριστικό από το σύνολο δεδομένων σας για το κλειδί.
Παράδειγμα: Εσφαλμένη Χρήση Κλειδιού (Δείκτης ως Κλειδί)
<ul>
{items.map((item, index) => (
<li key={index}>{item.name}</li>
))}
</ul>
Γιατί είναι κακό: Εάν η σειρά των items αλλάξει, ο index θα αλλάξει για κάθε στοιχείο, αναγκάζοντας το React να αποδώσει ξανά όλα τα στοιχεία της λίστας, ακόμη και αν το περιεχόμενό τους δεν έχει αλλάξει.
Παράδειγμα: Σωστή Χρήση Κλειδιού (Μοναδικό ID)
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
Γιατί είναι καλό: Το item.id είναι ένα σταθερό και μοναδικό αναγνωριστικό για κάθε στοιχείο. Ακόμα κι αν η σειρά των items αλλάξει, το React μπορεί ακόμα να αναγνωρίσει αποτελεσματικά κάθε στοιχείο και να αποδώσει ξανά μόνο τα στοιχεία που έχουν πραγματικά αλλάξει.
2. Αποφύγετε τις Περιττές Επανα-αποδόσεις
Τα Components αποδίδονται ξανά κάθε φορά που αλλάζουν τα props ή η κατάστασή τους. Ωστόσο, μερικές φορές ένα component μπορεί να αποδοθεί ξανά ακόμα και όταν τα props και η κατάστασή του δεν έχουν αλλάξει πραγματικά. Αυτό μπορεί να οδηγήσει σε προβλήματα απόδοσης, ειδικά σε σύνθετες εφαρμογές. Ακολουθούν ορισμένες τεχνικές για την αποτροπή περιττών επανα-αποδόσεων:
- Pure Components: Το React παρέχει την κλάση
React.PureComponent, η οποία εφαρμόζει μια ρηχή σύγκριση props και κατάστασης στοshouldComponentUpdate(). Εάν τα props και η κατάσταση δεν έχουν αλλάξει ρηχά, το component δεν θα αποδοθεί ξανά. Η ρηχή σύγκριση ελέγχει εάν οι αναφορές των props και των αντικειμένων κατάστασης έχουν αλλάξει. React.memo: Για λειτουργικά components, μπορείτε να χρησιμοποιήσετε τοReact.memoγια να απομνημονεύσετε το component. ΤοReact.memoείναι ένα component υψηλότερης τάξης που απομνημονεύει το αποτέλεσμα ενός λειτουργικού component. Από προεπιλογή, θα συγκρίνει ρηχά τα props.shouldComponentUpdate(): Για class components, μπορείτε να εφαρμόσετε τη μέθοδο κύκλου ζωήςshouldComponentUpdate()για να ελέγξετε πότε ένα component θα πρέπει να αποδοθεί ξανά. Αυτό σας επιτρέπει να εφαρμόσετε προσαρμοσμένη λογική για να καθορίσετε εάν είναι απαραίτητη μια επανα-απόδοση. Ωστόσο, να είστε προσεκτικοί όταν χρησιμοποιείτε αυτήν τη μέθοδο, καθώς μπορεί να είναι εύκολο να εισαγάγετε σφάλματα εάν δεν εφαρμοστεί σωστά.
Παράδειγμα: Χρήση React.memo
const MyComponent = React.memo(function MyComponent(props) {
// Render logic here
return <div>{props.data}</div>;
});
Σε αυτό το παράδειγμα, το MyComponent θα αποδοθεί ξανά μόνο εάν τα props που του μεταβιβάζονται αλλάξουν ρηχά.
3. Αμεταβλητότητα
Η αμεταβλητότητα είναι μια βασική αρχή στην ανάπτυξη React. Όταν ασχολείστε με σύνθετες δομές δεδομένων, είναι σημαντικό να αποφεύγετε την απευθείας μεταλλαγή των δεδομένων. Αντ 'αυτού, δημιουργήστε νέα αντίγραφα των δεδομένων με τις επιθυμητές αλλαγές. Αυτό διευκολύνει το React να ανιχνεύσει αλλαγές και να βελτιστοποιήσει τις επανα-αποδόσεις. Βοηθά επίσης στην αποτροπή απροσδόκητων παρενεργειών και κάνει τον κώδικά σας πιο προβλέψιμο.
Παράδειγμα: Μεταλλαγή Δεδομένων (Εσφαλμένο)
const items = this.state.items;
items.push({ id: 'new-item', name: 'New Item' }); // Μεταλλάσσει τον αρχικό πίνακα
this.setState({ items });
Παράδειγμα: Αμετάβλητη Ενημέρωση (Σωστό)
this.setState(prevState => ({
items: [...prevState.items, { id: 'new-item', name: 'New Item' }]
}));
Στο σωστό παράδειγμα, ο τελεστής διασποράς (...) δημιουργεί έναν νέο πίνακα με τα υπάρχοντα στοιχεία και το νέο στοιχείο. Αυτό αποφεύγει τη μεταλλαγή του αρχικού πίνακα items, διευκολύνοντας το React να ανιχνεύσει την αλλαγή.
4. Βελτιστοποιήστε τη Χρήση Context
Το React Context παρέχει έναν τρόπο μεταβίβασης δεδομένων μέσω του δέντρου component χωρίς να χρειάζεται να μεταβιβάζετε props χειροκίνητα σε κάθε επίπεδο. Ενώ το Context είναι ισχυρό, μπορεί επίσης να οδηγήσει σε προβλήματα απόδοσης εάν χρησιμοποιηθεί εσφαλμένα. Οποιοδήποτε component που καταναλώνει ένα Context θα αποδοθεί ξανά κάθε φορά που αλλάζει η τιμή του Context. Εάν η τιμή του Context αλλάζει συχνά, μπορεί να προκαλέσει περιττές επανα-αποδόσεις σε πολλά components.
Στρατηγικές για τη βελτιστοποίηση της χρήσης Context:
- Χρησιμοποιήστε Πολλαπλά Contexts: Αναλύστε μεγάλα Contexts σε μικρότερα, πιο συγκεκριμένα Contexts. Αυτό μειώνει τον αριθμό των components που πρέπει να αποδοθούν ξανά όταν αλλάζει μια συγκεκριμένη τιμή Context.
- Απομνημονεύστε τους Παρόχους Context: Χρησιμοποιήστε το
React.memoγια να απομνημονεύσετε τον πάροχο Context. Αυτό αποτρέπει την περιττή αλλαγή της τιμής Context, μειώνοντας τον αριθμό των επανα-αποδόσεων. - Χρησιμοποιήστε Επιλογείς: Δημιουργήστε συναρτήσεις επιλογής που εξάγουν μόνο τα δεδομένα που χρειάζεται ένα component από το Context. Αυτό επιτρέπει στα components να αποδοθούν ξανά μόνο όταν αλλάζουν τα συγκεκριμένα δεδομένα που χρειάζονται, αντί να αποδίδονται ξανά σε κάθε αλλαγή Context.
5. Code Splitting
Το Code splitting είναι μια τεχνική για την ανάλυση της εφαρμογής σας σε μικρότερα bundles που μπορούν να φορτωθούν κατ 'απαίτηση. Αυτό μπορεί να βελτιώσει σημαντικά τον αρχικό χρόνο φόρτωσης της εφαρμογής σας και να μειώσει την ποσότητα JavaScript που πρέπει να αναλύσει και να εκτελέσει το πρόγραμμα περιήγησης. Το React παρέχει διάφορους τρόπους για να εφαρμόσετε το code splitting:
React.lazyκαιSuspense: Αυτές οι δυνατότητες σάς επιτρέπουν να εισάγετε δυναμικά components και να τα αποδίδετε μόνο όταν είναι απαραίτητα. ΤοReact.lazyφορτώνει το component τεμπέλικα και τοSuspenseπαρέχει ένα εφεδρικό UI ενώ φορτώνεται το component.- Δυναμικές Εισαγωγές: Μπορείτε να χρησιμοποιήσετε δυναμικές εισαγωγές (
import()) για να φορτώσετε modules κατ' απαίτηση. Αυτό σας επιτρέπει να φορτώνετε κώδικα μόνο όταν είναι απαραίτητος, μειώνοντας τον αρχικό χρόνο φόρτωσης.
Παράδειγμα: Χρήση React.lazy και Suspense
const MyComponent = React.lazy(() => import('./MyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
);
}
6. Debouncing και Throttling
Το Debouncing και το throttling είναι τεχνικές για τον περιορισμό του ρυθμού με τον οποίο εκτελείται μια συνάρτηση. Αυτό μπορεί να είναι χρήσιμο για το χειρισμό συμβάντων που ενεργοποιούνται συχνά, όπως τα συμβάντα scroll, resize και input. Με το debouncing ή το throttling αυτών των συμβάντων, μπορείτε να αποτρέψετε την εφαρμογή σας να γίνει μη ανταποκρινόμενη.
- Debouncing: Το Debouncing καθυστερεί την εκτέλεση μιας συνάρτησης μέχρι να περάσει ένα ορισμένο χρονικό διάστημα από την τελευταία φορά που κλήθηκε η συνάρτηση. Αυτό είναι χρήσιμο για την αποτροπή της υπερβολικά συχνής κλήσης μιας συνάρτησης όταν ο χρήστης πληκτρολογεί ή κάνει κύλιση.
- Throttling: Το Throttling περιορίζει τον ρυθμό με τον οποίο μπορεί να κληθεί μια συνάρτηση. Αυτό διασφαλίζει ότι η συνάρτηση καλείται το πολύ μία φορά μέσα σε ένα δεδομένο χρονικό διάστημα. Αυτό είναι χρήσιμο για την αποτροπή της υπερβολικά συχνής κλήσης μιας συνάρτησης όταν ο χρήστης αλλάζει το μέγεθος του παραθύρου ή κάνει κύλιση.
7. Χρησιμοποιήστε ένα Profiler
Το React παρέχει ένα ισχυρό εργαλείο Profiler που μπορεί να σας βοηθήσει να εντοπίσετε σημεία συμφόρησης απόδοσης στην εφαρμογή σας. Το Profiler σας επιτρέπει να καταγράψετε την απόδοση των components σας και να οπτικοποιήσετε τον τρόπο με τον οποίο αποδίδονται. Αυτό μπορεί να σας βοηθήσει να εντοπίσετε components που αποδίδονται ξανά περιττά ή χρειάζονται πολύ χρόνο για να αποδοθούν. Το profiler είναι διαθέσιμο ως επέκταση Chrome ή Firefox.
Διεθνείς Παράγοντες
Κατά την ανάπτυξη εφαρμογών React για ένα παγκόσμιο κοινό, είναι απαραίτητο να λάβετε υπόψη τη διεθνοποίηση (i18n) και την τοπική προσαρμογή (l10n). Αυτό διασφαλίζει ότι η εφαρμογή σας είναι προσβάσιμη και φιλική προς το χρήστη για χρήστες από διαφορετικές χώρες και πολιτισμούς.
- Κατεύθυνση Κειμένου (RTL): Ορισμένες γλώσσες, όπως η αραβική και η εβραϊκή, γράφονται από δεξιά προς τα αριστερά (RTL). Βεβαιωθείτε ότι η εφαρμογή σας υποστηρίζει διατάξεις RTL.
- Μορφοποίηση Ημερομηνίας και Αριθμού: Χρησιμοποιήστε κατάλληλες μορφές ημερομηνίας και αριθμού για διαφορετικές τοπικές ρυθμίσεις.
- Μορφοποίηση Νομίσματος: Εμφανίστε τις νομισματικές αξίες στη σωστή μορφή για την τοπική ρύθμιση του χρήστη.
- Μετάφραση: Παρέχετε μεταφράσεις για όλο το κείμενο στην εφαρμογή σας. Χρησιμοποιήστε ένα σύστημα διαχείρισης μεταφράσεων για να διαχειριστείτε αποτελεσματικά τις μεταφράσεις. Υπάρχουν πολλές βιβλιοθήκες που μπορούν να βοηθήσουν, όπως το i18next ή το react-intl.
Για παράδειγμα, μια απλή μορφή ημερομηνίας:
- ΗΠΑ: MM/DD/YYYY
- Ευρώπη: DD/MM/YYYY
- Ιαπωνία: YYYY/MM/DD
Η αποτυχία να ληφθούν υπόψη αυτές οι διαφορές θα προσφέρει μια κακή εμπειρία χρήστη για το παγκόσμιο κοινό σας.
Συμπέρασμα
Το React reconciliation είναι ένας ισχυρός μηχανισμός που επιτρέπει αποτελεσματικές ενημερώσεις UI. Κατανοώντας το virtual DOM, τον αλγόριθμο diffing και τις βασικές στρατηγικές για βελτιστοποίηση, μπορείτε να δημιουργήσετε αποδοτικές και επεκτάσιμες εφαρμογές React. Θυμηθείτε να χρησιμοποιείτε αποτελεσματικά τα κλειδιά, να αποφεύγετε τις περιττές επανα-αποδόσεις, να χρησιμοποιείτε αμεταβλητότητα, να βελτιστοποιείτε τη χρήση context, να εφαρμόζετε code splitting και να αξιοποιείτε το React Profiler για να εντοπίσετε και να αντιμετωπίσετε σημεία συμφόρησης απόδοσης. Επιπλέον, λάβετε υπόψη τη διεθνοποίηση και την τοπική προσαρμογή για να δημιουργήσετε πραγματικά παγκόσμιες εφαρμογές React. Τηρώντας αυτές τις βέλτιστες πρακτικές, μπορείτε να προσφέρετε εξαιρετικές εμπειρίες χρήστη σε ένα ευρύ φάσμα συσκευών και πλατφορμών, υποστηρίζοντας παράλληλα ένα ποικίλο, διεθνές κοινό.