node.js - ההבדל בין "require(x)" ל"import x"


רק התחלתי לעבוד על פרויקט Node קטן שיתחבר ל-MongoDB. עם זאת, אני לא מצליח לייבא את המודולים הרלוונטיים של Node כמו שצריך, למרות שהתקנתי אותם כראוי דרך npm.

לדוגמה, הקוד הבא זורק שגיאה, ואומר לי ש"ל-express אין ייצוא ברירת מחדל":

import express from "express";

עם זאת, הקוד הזה עובד:

const express = require("express");

אז השאלה שלי היא, מה ההבדל בדרך שבה פונקציות import ופונקציות המשתנה/require עובדות? אני רוצה לתקן את מה שמפריע לי ב-imports בפרויקט, כי זה נראה סביר שעלול לגרום לבעיות נוספות בעתיד.

תשובה #1

התמונה הפשוטה הזו תעזור לך להבין את ההבדלים בין require ל-import.

חוץ מזה,

אתה לא יכול לטעון באופן סלקטיבי רק את החלקים שאתה צריך עם require, אבל עם import, אתה יכול לטעון באופן סלקטיבי רק את החלקים שאתה צריך, מה שיכול לחסוך בזיכרון.

הטענה היא סינכרונית (צעד אחרי צעד) עבור require מצד שני import יכול להיות אסינכרוני (בלי להמתין לייבוא קודם) כך שהוא יכול לבצע קצת טוב יותר מאשר require.

תשובה #2

ההבדל העיקרי בין require ל-import, הוא ש-import מובנה ב-JS, אבל require זמין רק ב-Node.js.


לצורך התייחסות, למטה מופיע דוגמה לאופן שבו Babel יכול להמיר את הסינטקס import של ES6 לסינטקס require של CommonJS. שימו לב שמאז Node.js גרסה 12, אין צורך לעשות זאת.

נניח כי הקובץ app_es6.js מכיל את הייבוא הבא:

import format from 'date-fns/format';

זוהי הוראה לייבא את הפונקציה format מהחבילה node בשם date-fns.

קובץ package.json הקשור יכול להכיל משהו כזה:

"scripts": {
    "start": "node app.js",
    "build-server-file": "babel app_es6.js --out-file app.js",
    "webpack": "webpack"
}

קובץ .babelrc הקשור יכול להיות משהו כזה:

{
    "presets": [
        [
            "env",
            {
                "targets":
                {
                    "node": "current"
                }
            }
        ]
    ]
}

סקריפט build-server-file המוגדר בקובץ package.json הוא הוראה ל-babel לנתח את הקובץ app_es6.js ולהפיק את הקובץ app.js.

לאחר הרצת הסקריפט build-server-file, אם תפתח את app.js ותחפש את היבוא של date-fns, תראה שהוא הומר ל:

var _format = require("date-fns/format");

var _format2 = _interopRequireDefault(_format);

רוב הקובץ הזה הוא ג'יבריש עבור רוב האנשים, אך מחשבים מבינים אותו.


גם כהפניה, כדוגמה לאיך אפשר ליצור מודול ולייבא אותו לפרויקט שלך, אם תתקין את date-fns ואז תפתח את node_modules/date-fns/get_year/index.js אתה יכול לראות שהוא מכיל:

var parse = require('../parse/index.js')

function getYear (dirtyDate) {
  var date = parse(dirtyDate)
  var year = date.getFullYear()
  return year
}

module.exports = getYear

באמצעות תהליך הבייבל שלמעלה, קובץ app_es6.js שלך יכול אז להכיל:

import getYear from 'date-fns/get_year';

// Which year is 2 July 2014?
var result = getYear(new Date(2014, 6, 2))
//=> 2014

ובבל יהפוך את הייבוא ל:

var _get_year = require("date-fns/get_year");

var _get_year2 = _interopRequireDefault(_get_year);

ולטפל בכל ההתייחסויות לפונקציה בהתאם.

תשובה #3

תן לי לתת דוגמה לכלילת מודול express עם require ו-import

-require

var express = require('express');

-import

import * as  express from 'express';

אז אחרי שימוש באחת מההצהרות שלמעלה, יהיה לנו משתנה שנקרא 'express'. עכשיו נוכל להגדיר את המשתנה 'app' כ

var app = express(); 

אז אנחנו משתמשים ב-'require' עם 'CommonJS' וב-'import' עם 'ES6'.

למידע נוסף על 'require' ו-'import', עיינו בקישורים למטה.

require - דרישת מודולים ב-Node.js: כל מה שאתה צריך לדעת

ייבוא - עדכון על מודולי ES6 ב-Node.js

תשובה #4

אני אעשה את זה פשוט,

  • ייבוא וייצוא הם תכונות של ES6 (JavaScript בדור הבא).
  • Require הוא שיטה ישנה לייבוא קוד מקבצים אחרים

ההבדל העיקרי הוא ב-require, הקובץ JS כולו נקרא או כלול. אפילו אם אינך צריך חלק ממנו.

var myObject = require('./otherFile.js'); //This JS file will be included fully.

בעוד שבייבוא ניתן לחלץ רק אובייקטים/פונקציות/משתנים הנדרשים.

import { getDate }from './utils.js'; 
//Here I am only pulling getDate method from the file instead of importing full file

הבדל משמעותי נוסף הוא שאתה יכול להשתמש ב-require בכל מקום בתוכנית, בעוד ש-import צריך תמיד להיות בראש הקובץ

עריכה: בגרסאות האחרונות של Node ניתן להשתמש בפירוק מבנה. זה ייראה כך

const { getDate } = require('./date.js');

תשובה #5

ES6 חדש:

'import' צריך להיות בשימוש עם מילות המפתח 'export' כדי לשתף משתנים/מערכים/אובייקטים בין קבצי js:

export default myObject;

//....in another file

import myObject from './otherFile.js';

ישן בית ספר:

יש להשתמש ב-'require' עם 'module.exports'

 module.exports = myObject;

//....in another file

var myObject = require('./otherFile.js');

תשובה #6

יש הבדל גדול בין זה:

import express from "express";

ועכשיו זה:

import * as express from "express";

התרגום הנכון מ-CommonJS ל-ES6 של

const express = require("express");

היא הייבוא השני.

בעצם, זה בגלל שבייבוא הראשון אתה מחפש ייצוא במודול express בשם express. בשני אתה מייבא את כל מודול express בשם express.

תשובה #7

import (תחביר מודולים של ES)require (תחביר של מודולים CommonJS)
סיכום

זו התקן העדכני ביותר לעבודה עם מודולים ב-JavaScript ומודרכת בדפדפנים מודרניים ובסביבות שמתרגמות או תומכות ב-ES6, כמו TypeScript או Babel.

זה לא היה במקור חלק מ-JavaScript, אבל זה אומץ כסטנדרט עבור Node.js, ששימש באופן שוטף בפיתוח בצד השרת ב-JavaScript.

בעוד ש-Node.js השתמש בעבר ב-CommonJS, הוא תומך כעת גם במודולי ES6.

ייצוא

סטטי (מוגדר מראש). מבנה הייצוא של המודול נקבע כאשר הקוד מנותח, לא בזמן הריצה.

הטבע הסטטי הזה מאפשר לכלים כמו באנדלרים ולינטרים לנתח את הקוד מבלי להפעילו, ובכך מאפשר תכונות כמו Tree-shaking טוב יותר וזמני טעינה מהירים יותר בדפדפנים.

// myModule.js
export function myFunc() { /*...*/ }
export const MY_CONST = 123;

נחושב בזמן ריצה. הייצוא במודול נקבע במהלך ביצוע הקוד.

// myModule.js
if (process.env.NODE_ENV === 'development') {
  module.exports.debug = function debug() {
    console.log('Debugging...');
  }
} else {
  module.exports.log = function log() {
    console.log('Logging...');
  }
}
טוען מודולים

יכול להיות אסינכרוני, מה שמאפשר טעינה יעילה ובחירה של חלקי המודול. זה יכול להוביל לזמני טעינה מהירים יותר וביצועים טובים יותר.

import { myFunc } from './myModule.js';

myFunc();

סינכרוני (טוען מודולים אחד אחד). תמיד טוען את כל המודול, מה שיכול להשפיע על הביצועים אם המודול גדול.

const { debug, log } = require('./myModule.js');

if(debug) debug();
if(log) log();
דוגמה מלאה
// somefile.js
export function sayHello() {
  console.log("Hello, world!");
}

console.log("somefile has been loaded!");
// main.js
import { sayHello } from './somefile.js';

sayHello();

// 👇 Outputs 👇
// "somefile has been loaded!"
// "Hello, world!"
// somefile.js
function sayHello() {
  console.log("Hello, world!");
}

module.exports = { sayHello };

console.log("somefile has been loaded!");
// main.js
const { sayHello } = require('./somefile.js');

sayHello();

// 👇 Outputs 👇
// "somefile has been loaded!"
// "Hello, world!"
היקף
// somefile.js
let count = 1;
export { count };

setTimeout(() => count = 2, 1000);
// main.js
import { count } from './somefile.js';

console.log(count); // 1
setTimeout(() => console.log(count), 1000); // 2

הייצוא מועתק בזמן דרישת המודול.
לכן גם אם ערך מיוצא משתנה במודול שבו הוגדר, שינוי זה לא נראה במודול שבו הוא נדרש.

// somefile.js
let count = 1;
module.exports.count = count;

setTimeout(() => count = 2, 1000);
// main.js
const mod = require('./somefile.js');

console.log(mod.count); // 1
setTimeout(() => console.log(mod.count), 1000); // 1

אם קשה לקרוא כאן, קרא עותק של הטבלה הזו בגיטהאב.