ToCode
Відкрити в Telegram
1 420
Підписники
Немає даних24 години
+27 днів
-230 день
Архів дописів
1 420
# איתחול רידאקס ממידע שמגיע מהשרת
אנחנו אוהבים ברידאקס שכל המידע שמור באוביקט אחד וקל מאוד לעדכן במכה אחת את כל האפליקציה. כשיש לי מידע ששמור בצד השרת, אני יכול תמיד לשלוף את ה JSON של כל המידע, להוציא dispatch שמחליף את הסטייט באפליקציה במה שקיבלתי מהשרת והכל מתעדכן.
החיסרון בגישה כזאת הוא שאחרי "התקנת" JSON חדש מהשרת, כל הקומפוננטות ירונדרו מחדש, גם אם החלק שלהן במידע לא השתנה. ב JavaScript כשאני מחליף את כל הסטייט באוביקט חדש, גם אם זהה, עדיין החלפתי את ה References ולכן ריאקט לא יכול לזהות שלא היה שינוי.
בפוסט זה אני רוצה להראות גישה קצת יותר מתוחכמת לשילוב שינויים מהשרת, תוך שימוש ב JSON Patch, כדי לעדכן רק את החלקים באוביקט ה State שבאמת השתנו, כדי להיות מסוגל לשמור על עקביות ברמת ה References בין עדכונים מהשרת.
## הקוד עם הבעיה
התוכנית שלנו היום מכילה קוד רידאקס שיודע לעדכן את הסטייט שלו דרך action:
export const slice = createSlice({
name: 'items',
initialState,
reducers: {
reset: (state, action) => {
return action.payload;
},
},
})
הסטייט הוא בסך הכל מערך של 4 פריטים, ובשביל שיהיה קל לשחק עם הקוד גם בלי שרת יצרתי פונקציה שמייצרת גירסה חדשה של הסטייט על ידי שינוי הטקסט של אחד הפריטים:
const initialItems = [
{ id: 1, text: 'first item' },
{ id: 2, text: 'second item' },
{ id: 3, text: 'third item' },
{ id: 4, text: 'fourth item' },
];
function randomizeItems() {
const item = _.sample(initialItems);
return initialItems.map(i => (
i.id === item.id ? { ...i, text: `${i.text} ${_.random(10)}`} : {...i }
));
}
יש קומפוננטה שיודעת להציג פריט מהרשימה, ועוד קומפוננטה שמציגה את כל ה-4:
const Item = React.memo(({ id }) => {
console.count(`Item ${id}`);
const me = useSelector(state => state.items.find(i => i.id === id));
if (!me) {
return <p>Missing item {JSON.stringify(id)}</p>
}
return (<p>{me.id} - {me.text}</p>);
});
function Main() {
const items = useSelector(state => state.items);
const dispatch = useDispatch();
return (
<>
<button onClick={() => dispatch(slice.actions.reset({ items: randomizeItems() }))}>Randomize</button>
{items.map(i => <Item id={i.id} key={i.id} />)}
</>
)
}
עכשיו בואו נראה מה קורה כאן:
1. כשלוחצים על כפתור Randomize אנחנו מגרילים אוביקט סטייט חדש שמורכב מ-4 פריטים חדשים לגמרי. אותו דבר היה קורה אם הייתי מפעיל JSON.parse על אוביקט שמגיע מהשרת.
2. בגלל שהפריטים חדשים, כל useSelector בתוך Item לוקח את המידע החדש ומרנדר את עצמו מחדש. ריאקט מפעיל את render כי הוא מזהה שהתוצאה של useSelector שונה ממה שהיתה קודם - אפילו כשמדובר על פריטים שלא השתנו במערך.
אתם יכולים לשחק עם הקוד והכפתור בקודסנדבוקס בקישור:
https://codesandbox.io/s/damp-rain-q71w55?file=/src/App.js
## איך לצמצם רנדרים
ברגע שאני מבין שה render-ים המיותרים הגיעו בגלל שרידאקס החליף את כל האוביקטים בחדשים, אני יכול לתקן את הבעיה ברמה של Redux. הספריה rfc6902 יודעת לזהות הבדלים בין שני אוביקטי JSON ולשנות אוביקט אחד כדי "להגיע" לשני, בצורה שהיא Mutable. כשאני שם את הקוד הזה בתוך immer אז יוחלפו רק החלקים שבאמת השתנו באוביקט הסטייט. אני מוסיף לסלייס את הפונקציה:
resetWithDiff: (state, action) =>{
applyPatch(state, createPatch(state, action.payload));
},
ומייבא את שתי הפונקציות applyPatch ו createPatch מ rfc6902:
import {applyPatch, createPatch} from 'rfc6902'
ומוסיף כפתור לאפליקציה שישלח dispatch עם הפעולה החדשה:
function Main() {
const items = useSelector(state => state.items);
const dispatch = useDispatch();
return (
<>
<button onClick={() => dispatch(slice.actions.reset({ items: randomizeItems() }))}>Randomize</button>
<button onClick={() => dispatch(slice.actions.resetWithDiff({ items: randomizeItems() }))}>Randomize With Diff</button>
{items.map(i => <Item id={i.id} key={i.id} />)}
</>
)
}1 420
# סיקור באג - הארכת מנוי שלא עבדה
חשבתי שאני בונה את הפיצ'ר הכי קל בעולם, אבל כמו שקורה הרבה פעמים במערכות תוכנה, כשפיצ'ר חדש פוגש קוד קיים הוא עלול לדחוף את הקוד בכיוונים לא צפויים. כך קרה עם פיצ'ר הארכת מנוי בתשלום מראש.
המנגנון של מנוי בתשלום מראש אומר שאתם יכולים לקנות מנוי לתקופה לבחירתכם, פשוט משלמים מראש על כל החודשים ואז לא צריך לשלם פעם בחודש. אבל אנשים יכולים לטעות ולפעמים אנחנו קונים מנוי לחודשיים ואז אחרי חודש מגלים שייקח לנו יותר זמן לסיים את הקורס ורוצים להאריך. במצב של הארכה אם אני משלם מראש על חודשיים אני רוצה שהחודשיים האלה יצטרפו לתקופת המנוי הקיים שלי, כלומר בן אדם שקנה מנוי לשלושה חודשים, ואז אחרי חודש קנה עוד חודשיים מצפה שהחודשיים הנוספים יתחילו מהרגע שהשלושה חודשים הראשונים נגמרו (ולא מרגע שהוא קונה אותם). זה מצב חדש שקודם לא נדרשנו להתמודד איתו.
הפונקציה שטיפלה בעדכון פרטי משתמש אחרי רכישה נראתה במקור כך:
def subscribe(duration)
self.subscription_end_date = (Time.zone.now + duration)
end
אבל ברגע שאנחנו מוסיפים אפשרות למשתמש "להאריך" מנוי יש לשנות את הפונקציה. צריך לבדוק אם לאותו משתמש יש כבר מנוי קיים, ואם כן להאריך את המנוי. קל? לא לגמרי. זה היה הניסיון הראשון עם הטעות:
def subscribe(duration)
self.subscription_end_date = ((subscription_end_date || Time.zone.now) + duration)
end
כל בדיקות היחידה ובדיקות המערכת שהרצתי לא עלו על הבעיה, ואולי זה הלקח כאן. בדיקות עוזרות בבדיקת מצבים שאפשר לחשוב עליהם, אבל באגים נוטים להופיע באזורים שאנחנו לא חושבים עליהם. לפחות עד שהם מגיעים.
במקרה שלנו הבאג מתגלה כשמשתמש שכבר היה לו מנוי בעבר מנסה לרכוש מנוי חדש. במצב כזה ה subscription_end_date הוא תאריך בעבר, ו"תוספת" הזמן למנוי מצטרפת למועד המקורי בו המנוי פג תוקף, כך שגם אחרי תשלום לאותו משתמש עדיין אין מנוי בתוקף.
וכמו הרבה באגים, גם כאן אחרי שרואים את הבעיה הפיתרון הוא פשוט טיפול במקרה הנוסף. הקוד ב Ruby לתיקון היה:
def subscribe(duration)
existing_subscription_period = if subscription_end_date.present? && Time.zone.now < subscription_end_date
subscription_end_date
else
Time.zone.now
end
self.subscription_end_date = (existing_subscription_period + duration)
end
וכמו תמיד בפוסטים כאלה אני מזכיר שאם אתם נתקלים בדברים באתר שלא עובדים כמו שאתם חושבים שצריכים לעבוד, בבקשה אל תקחו ללב ועדכנו בטופס צור קשר. אנחנו עדיין בריקושטים מה Rewrite אבל לאט לאט נסדר את כל הבעיות.1 420
# היום למדתי: אירועי mousedown ו mouseup ב JavaScript לא תמיד מתואמים
אני מאוד אוהב לפרסם פה בעיות שנתקלתי בהן שעבורן מצאתי יופי של פיתרון. לצערי לפעמים אנחנו נתקלים בבאגים שאין להם פיתרונות יפים, ושפשוט צריך להכיר ולאלתר. ככה גיליתי היום עם אירועי mousedown ו mouseup ב JavaScript.
התוכנית אחרי ניקוי עושה דבר מאוד פשוט - כל פעם שמישהו לוחץ על כפתור של העכבר היא מציגה את הטקסט down, וכשעוזבים את הכפתור היא משנה את הטקסט ל up. אפשר לשחק עם הקוד בקודפן בקישור הזה:
https://codepen.io/ynonp/pen/vYrrRPz?editors=1010
או לראות אותו מוטמע כאן:
<iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/ynonp/embed/vYrrRPz?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/ynonp/pen/vYrrRPz">
Untitled</a> by Ynon Perek (<a href="https://codepen.io/ynonp">@ynonp</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
אבל מסתבר שמאוד קל לשבור את המנגנון: פשוט לוחצים על כפתור העכבר השמאלי ואז על הימני, כשנפתח התפריט-כפתור-ימני לוחצים על Escape כדי לסגור אותו, מזיזים קצת את העכבר או עוברים לחלון אחר ואז עוזבים את הכפתורים. אם תשחקו עם זה בטוח תמצאו אפילו שיטות יותר קלות משלי.
קוד דומה ייכשל גם אם נעבור להשתמש ב Pointer Events.
הנקודה החשובה - קשה לזהות כשמשתמש מפסיק ללחוץ על העכבר. לפי הפיצ'ר שאתם בונים תצטרכו למצוא פיתרון טוב יותר, לדוגמה אני נתקלתי בזה כשתיקנתי מנגנון של Resize בדפדפן, שם אפשר לזהות כשסמן העכבר "יוצא" מאזור ה Resize ולהפסיק את הפעולה, או לתפוס אירועי Mouse Move ולבדוק בכל אירוע כזה איזה כפתורים עדיין לחוצים. עבור משימות אחרות תצטרכו למצוא מעקפים אחרים, אבל באופן כללי אל תבנו על התאמה בין אירועי mousedown ו mouseup.
1 420
# הזמנה לוובינר: טייפסקריפט בקטנה
בשבוע שעבר הגעתי ללמד קורס ריאקט באיזו חברה, ושוב כולם שאלו "הדוגמאות יהיו בטייפסקריפט נכון?". טייפסקריפט כבר מזמן הפך לעובדה מוגמרת, לשפה שכולם רוצים לבחור בה ויש כמות הולכת וגדלה של מתכנתי ווב ופרויקטים שעבורם JavaScript נקי כבר אינה אופציה.
היתרונות של טייפסקריפט רבים, ביניהם השלמה אוטומטית שעובדת טוב יותר, קל יותר למצוא את כל ההתיחסויות לפונקציה או מחלקה מסוימת כי החיפוש הוא לא רק לפי שם, ולכן קל יותר לשנות שמות, להזיז דברים ממקום למקום ולשנות ממשקים. את רוב היתרונות אנחנו מקבלים ישר מהקופסה, בלי שנצטרך להתאמץ יותר מדי. כן צריך להגדיר טיפוס לכל דבר, אבל בזכות ה Type Inference של טייפסקריפט יחסית לא צריך להגדיר יותר מדי טיפוסים ולכן זו לא תוספת משמעותית לקוד.
ובכל זאת יש דרכים טובות יותר ופחות להשתמש בטייפסקריפט ולפעמים בגלל היכרות לא מספיק טובה עם השפה אנחנו מוצאים את עצמנו מתאמצים יותר ממה שצריך או כותבים הגדרות טיפוסים עם חזרות או לא גנריות מספיק. הודעות השגיאה של טייפסקריפט יכולות גם הן לבלבל ואנחנו נמצא את עצמנו מבלים זמן רב ב"תיקון" קוד למרות שבלי טייפסקריפט הוא היה תקין לגמרי.
ביום חמישי הקרוב בשעה עשר בבוקר אעביר וובינר של שעה על טייפסקריפט במטרה להאיר את הצדדים הטובים יותר של השפה, ולעזור לכם למצוא דרכים טובות לפתור בעיות וכך לחסוך זמן פיתוח. הוובינר יהיה מעניין גם אם לא עבדתם מעולם בטייפסקריפט, וגם אם עבדתם בה ורוצים לקבל טיפים לשימוש טוב יותר. במהלך השעה נדבר על:
1. למה כדאי לבחור בטייפסקריפט, קצת על ההיסטוריה שלה ומה טייפסקריפט נותנת לנו כמפתחים.
2. טכניקות לכתיבת טיפוסים בצורה גנרית יותר, כמובן עם דוגמאות לשימוש באותן טכניקות.
3. איך לשלב טייפסקריפט בפרויקטים ומהו קובץ ההגדרות tsconfig
תרגישו חופשי לבוא עם שאלות על טייפסקריפט או דוגמאות של דברים שאתם לא בטוחים איך לכתוב ואני אשמח לראות ולעזור.
פרטים נוספים והרשמה בדף הוובינר בקישור: https://www.tocode.co.il/workshops/122.
נתראה בחמישי בבוקר,
ינון
1 420
# ככה מצאתי את זה
בשיטוט מקרי בקוד של axios נתקלתי בקוד הבא עבור אחת הבדיקות:
// from: https://github.com/axios/axios/blob/v1.x/test/specs/promise.spec.js#L10
it('should provide succinct object to then', function (done) {
let response;
axios('/foo').then(function (r) {
response = r;
});
getAjaxRequest().then(function (request) {
request.respondWith({
status: 200,
responseText: '{"hello":"world"}'
});
setTimeout(function () {
expect(typeof response).toEqual('object');
expect(response.data.hello).toEqual('world');
expect(response.status).toEqual(200);
expect(response.headers['content-type']).toEqual('application/json');
expect(response.config.url).toEqual('/foo');
done();
}, 100);
});
});
הם מגדירים שרת פיקטיבי ומאתחלים את פרטי התשובה הפיקטיבית שהוא ישלח, שולחים בקשת רשת עם axios ואז מחכים 100 מילי שניות ובודקים את אוביקט התשובה. אבל למה לחכות דווקא 100 מילי שניות? לפי גיט בליים האשמה היא בקומיט 46a9639. כל הבדיקות עד אותו קומיט השתמשו ב setTimeout עם זמן המתנה של 0, אבל חלקן כנראה נכשלו מדי פעם ובאותו קומיט כל הבדיקות שכללו setTimeout עלו לזמן המתנה 100 מילי שניות.
אבל שאלה יותר מעניינת היא למה בכלל להשתמש ב setTimeout שם. הרי את אותה בדיקה אפשר לתקן בצורה יותר יעילה באמצעות הזזת הקוד למקום המתאים (כלומר אחרי ה then). בקיצור ובקוד:
it('should provide succinct object to then', function (done) {
axios('/foo').then(function (response) {
expect(typeof response).toEqual('object');
expect(response.data.hello).toEqual('world');
expect(response.status).toEqual(200);
expect(response.headers['content-type']).toEqual('application/json');
expect(response.config.url).toEqual('/foo');
done();
});
getAjaxRequest().then(function (request) {
request.respondWith({
status: 200,
responseText: '{"hello":"world"}'
});
});
});
התשובה לדעתי היא ששינוי מבני כזה הוא יותר מפחיד - אולי הפקודות לא עושות בדיוק את מה שאני חושב שהן עושות, אולי ה setTimeout היה שם מסיבה מסוימת, אולי המבנה המקורי עם ה setTimeout מטפל במקרי קצה שאני לא רואה עכשיו. יותר קל לסדר את הבעיה באותו מבנה בו מצאתי את הקוד.
אבל יש שתי בעיות בגישה כזאת:
1. גם אם למבנה שבו מצאתי את הבדיקה יש סיבה מקורית (או היתה סיבה), אם אני לא יודע מה היא ולא מצליח למצוא אותה אז לשמור עליה רק מוסיף "קסם אפל" לקוד. עכשיו כל מתכנתת חדשה שתגיע לצוות תראה שה setTimeout הזה נמצא בכל הבדיקות ותעתיק אותו גם, ואז לאורך זמן כולנו נהיה בטוחים שזאת הדרך הנכונה לעשות דברים למרות שאולי היא כבר מזמן לא.
2. כשאני נשאר באותם מבנים של קוד אני לא נותן לעצמי את האופציה לנסות רעיונות חדשים וללמוד מהם.
כשאתם רואים קוד לא הגיוני אין טעם להישאר באותו מבנה כי "ככה מצאתי אותו". כן צריך לשבת ולנסות למצוא את ההגיון והסיבות לדברים, אבל בסוף אחרי שמוצאים (או אם לא מוצאים), מותר ורצוי לארגן קוד מחדש כדי שיהיה טוב יותר.1 420
# ואיך זה נשבר?
רוב הזמן בכתיבת קוד אנחנו מחפשים איך לגרום לדברים לעבוד: איזה ספריית קוד תעזור לי להתקדם יותר מהר, איך לתקן את הבאג, איך לכתוב קוד "נקי" שיחזיק מעמד לאורך זמן.
טכניקה קלה שאנשים לפעמים שוכחים היא לשבור דברים בכוונה, רק בשביל לראות איך זה נשבר: איך אני גורם לקוד הזה לא לעבוד? איך אני "מנקה" את הבאג שמצאתי? איזה פיצ'ר אני צריך להוסיף למערכת כדי שכל הבאגים יבואו?
לאורך זמן ככל שנהיה יותר טובים בלשבור קוד, כך יהיה לנו גם יותר קל לכתוב קוד יציב ולהימנע מ Code Smells שמובילים לבאגים המוזרים באמת.
1 420
# סיכום כל הארגומנטים עם shift
הפקודה shift ב bash "מזיזה" את הארגומנטים מקום אחד שמאלה, כך ש
$1 נמחק, $2 הופך ל $1, $3 הופך ל $2 וכך הלאה. הסקריפט הבא מציג דוגמת שימוש פשוטה ב shift שפשוט מדפיסה את שני הארגומנטים הראשונים שקיבלה:
#!/bin/bash
echo $1
shift
echo $1
טריק פשוט עם shift הוא מימוש לולאה בצורה רקורסיבית בסקריפט bash. לדוגמה אם אני רוצה להדפיס את כל הארגומנטים שהסקריפט קיבל אני יכול להדפיס אחד, להפעיל shift ואז להפעיל את אותו הסקריפט בצורה רקורסיבית:
#!/bin/bash
[[ $# == 0 ]] && exit 0
echo $1
shift
exec $0 "$@"
או בחזרה לכותרת - אם אני רוצה לסכום את כל הארגומנטים שהסקריפט קיבל אני יכול לנסות שוב ברקורסיה, והפעם אני מעביר ל shift את המספר 2, מה שגורם לו להזיז את הארגומנטים בשני מקומות:
#!/bin/bash
if [[ $# == 1 ]]
then
echo $1
exit 0
fi
(( sum = $1 + $2 ))
shift 2
exec $0 $sum "$@"
ולמרות שרקורסיות בסקריפטים יכולות להיות רעיון מעניין, אחד השימושים האמיתיים והמועילים ב shift הוא בפיענוח פרמטרים. נתבונן בתוכנית הבאה:
while getopts "c:o:l" opt; do
case $opt in
c)
COUNT=$OPTARG;;
o)
OUTFILE=$OPTARG;;
l)
PADDING=1;;
esac
done
shift $((OPTIND-1))
התוכנית השתמשה ב getopts כדי לפענח את כל הארגומנטים שמתחילים במינוסים, כולל את הפרמטרים ש"תפסו" ערכים אחריהם (במקרה שלנו c ו o), ואז "הזיזה" את כל הארגומנטים כדי להיפטר מכל אלה שפוענחו על ידי getopts כך שהמשך הקוד יכול לעבוד עם $1, $2 וכן הלאה.
נ.ב. למרות שרקורסיות זה כיף, שווה לזכור שב bash אפשר לרוץ על כל הארגומנטים עם לולאת for פשוטה - רק צריך לוותר על ה in. לדוגמה חישוב סכום של כל הארגומנטים יהיה פשוט:
#!/bin/bash
for arg
do
(( sum += arg ))
done
echo $sum1 420
# קריאה עם read מ Pipe ב Bash
הפקודה read קוראת שורה מהקלט הסטנדרטי, שוברת אותה למילים (עם תו ההפרדה במשתנה
IFS) ושומרת את התוצאה למשתנה אחד או יותר שאת שמו היא מקבלת בהפעלה.
לכן הקוד הבא קורא את השורה הראשונה מהקובץ /etc/shells ושומר אותו למשתנה בשם first_line:
read first_line < /etc/shells
והקוד הבא קורא רק את המילה הראשונה מאותו קובץ ושומר אותה למשתנה first_word:
read first_word rest < /etc/shells
קל לשכוח שההתנהגות של read בשילוב | אינה אינטואיטיבית, וזה לא בגלל read. כל פעם שאנחנו מפעילים פקודות בתוך Pipeline, באש פותח תת-מעטפת (Sub Shell) ומבצע את הפקודות בתוכה. הבעיה היא שמשתנים שמוגדרים באותו Sub Shell יימחקו כשה Sub Shell יסתיים, כלומר ביציאה מה Pipeline. הפקודה הזאת לדוגמה לא לוקחת את המילה הראשונה מהפלט של wc /etc/shells לתוך המשתנה lines:
wc /etc/shells | read lines
כלומר היא כן, אבל המשתנה נשמר בתוך Sub Shell ונמחק איך שהשורה נגמרת. בשביל ש read תוכל לשמור משתנה ל Shell הנוכחי שלי היא חייבת להיות מופעלת ממנה, כלומר לא מצד ימין של סימן Pipe. במצב כזה אני צריך להשתמש בפקודת <() כדי להריץ פקודה ולשמור את הפלט שלה לתוך File Handle, ואז בעזרת הפניית קלט רגילה אני יכול להעביר את השורה הראשונה מאותו File Handle ל read:
read lines words chars rest < <(wc /etc/passwd)1 420
# ללמוד מהטובים ביותר
האמונה שכדאי ללמוד מהטובים ביותר משותפת לרוב האנשים וברוב תחומי הלימוד. כיף לחשוב שאם רק מסי היה מאמן אותך בכדורגל היית הופך מחר לאלוף העולם, שאם וורן באפט היה המאמן העסקי שלך העסקים היו ממריאים או שקורס דיבור מול קהל תיאורטי שהיה מעביר טוני רובינס היה נותן לך את הכלים הטובים ביותר לעמוד על במה.
האמונה הזאת מסבכת אותנו בשני מקומות:
1. בתחילת הלימוד, כשאין לנו עדיין את הכלים להבין על מה מדובר, הלימוד מהכי טובים הוא רק מבלבל. הם לוקחים כמובן מאליו דברים שלך אין מושג בהם, ולכן רוב הטיפים שיתנו פשוט לא יהיו רלוונטיים למתחילים.
2. בהמשך הלימוד כשאנחנו כבר כן מתחילים להבין על מה מדובר, פערי הרמות גורמים לאיבוד חוש הביקורתיות, וקשה לראות את הטעויות של אותם מאסטרים.
זה קצת כמו טיפוס במדרגות - תמיד כדאי להניח את הרגל על מדרגה קצת יותר גבוהה מאיפה שאתה נמצא. כלל אצבע טוב הוא להקשיב לאנשים שאנחנו מבינים אותם ויכולים להיות ביקורתיים כלפיהם, כי חוש ביקורת טוב יאפשר לנו בהמשך לזהות גם את הטעויות שלנו וללמוד מהן. וכן לחפש חומרי לימוד טובים, לחפש עוד מקורות ללמוד מהם, ולשלב את הדברים הטובים מכל מקור זה חלק מהתהליך.
1 420
הי חברים לכל מי שפספסו הקלטה של הוובינר מיום חמישי על טעויות נפוצות בריאקט:
https://www.tocode.co.il/past_workshops/121
1 420
# דינו מאפשר הרצת חבילות גם מ npm
כתבתי פה בעבר על דינו, שלדעתי בנוי בצורה הרבה יותר נכונה מ node ואני מקווה שבעתיד נראה יותר פרויקטים משתמשים בו. אחד המכשולים המרכזיים לאימוץ של דינו היה חוסר התמיכה בחבילות מ npm. בניגוד ל node, בדינו אנחנו לא צריכים לנהל קובץ package.json או להפעיל
npm install, במקום זה כל החבילות והגירסאות שלהן רשומות ממש בתוך הקוד. התקנת המודולים היא לתיקיית cache גלובאלית ובהפעלה של תוכנית דינו מארגן את כל המודולים שהיא צריכה.
לא מזמן יצאה גירסה 1.28 של דינו שהוסיפה תמיכה בחבילות מ npm. בכך נשבר חסם משמעותי לאימוץ דינו, למרות שהוא עדיין כנראה פחות יציב מ node. בכל מקרה למשחקים זה עובד מצוין והתמיכה ב TypeScript מהקופסה היא כמו הדובדבן על הקצפת. הנה דוגמה שלי המבוססת על הדוגמה באתר שלהם לעבודה עם אקספרס:
import express from "npm:express@4.18.2";
function twice(x: number) {
return x * 2;
}
const app = express();
app.get("/", (req, res) => {
res.send("Welcome to the Dinosaur API!");
});
app.get("/twice", (req, res) => {
const n = Number(req.query.n || 0);
res.send(`${n} * 2 = ${twice(n)}`);
});
app.listen(8000);
ובשביל להריץ, אם יש לכם כבר דינו 1.28 ומעלה מותקן מפעילים:
$ deno run --allow-net --allow-read --allow-env main.ts
שימו לב לציון ההרשאות בהפעלת דינו - בניגוד ל node, תוכניות דינו רצות ב Sandbox ויכולות לגשת רק ל APIs אליהם נתנו הרשאה מפורשת. מנגנון כזה יכול להפוך את המערכת לקצת יותר מאובטחת, למרות שבפרויקטים גדולים קשה לי לראות איך זה עוזר (כי ממילא נצטרך לתת יחסית הרבה הרשאות בשביל כל מה שהפרויקט עושה).
Вже доступно! Дослідження Telegram за 2025 — головні інсайти року 
