درک JavaScript مدرن برای دایناسورها - بخش اول

گردآوری و تالیف : عرفان کاکایی
تاریخ انتشار : 08 شهریور 1397
دسته بندی ها : جاوا اسکریپت

اگر از هنگام پدید آمدن JavaScript تا به حال مدتی باشد که در صحنه حضور ندارید، یادگیری آن می‌تواند سخت باشد. این اکوسیستم به قدری با سرعت در حال تغییر است که درک مشکلاتی که ابزار مختلف در حال تلاش برای بر طرف کردن آن‌ها هستند، سخت است. من در سال 1377 برنامه‌نویسی را شروع کردم، اما در سال 1393 به طور جدی یادگیری JavaScript را شروع کردم. به یاد دارم که وقتی برای اولین بار با تیتر Browserify مواجه شدم، این جمله را دیدم:

Browserify شما را قادر می‌سازد که ماژول‌ها را با bundle کردن تمام Dependencyهای خود، در مرورگر require کنید. (require(‘modules’))

تقریبا هیچ کلمه‌ای از این جمله را درک نمی‌کردم، و سعی می‌کردم بفهمم که این چه کاربردی می‌تواند برای یک توسعه دهنده داشته باشد.

هدف این مقاله این است که یک مفاد تاریخی درباره تکامل ابزار JavaScript و این که امروزه و در سال 2017 چه چیزی هستند را فراهم کنیم. ما از ابتدا شروع کرده، و یک وبسایت به روش قدیمی برای مثال می‌سازیم؛ یعنی بدون هیچ گونه ابزار و فقط با استفاده از HTML و JavaScript. سپس به تدریج ابزار مختلف و مشکلاتی که حل می‌کنند را معرفی خواهیم کرد. با این مفاد تاریخی، می‌توانید JavaScript همیشه در حال تغییر را یاد گرفته، و با آن وقف پیدا کنید. بیایید شروع کنیم!

استفاده از JavaScript به روش قدیمی

بیایید با یک وبسایت قدیمی با استفاده از HTML و JavaScript شروع کنیم، که شامل دانلود و لینک کردن دستی فایل‌ها است. در اینجا، یک مثال فایل index.html می‌بینید که به یک فایل JavaScript لینک می‌شود:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JavaScript Example</title>
  <script src="index.js"></script>
</head>
<body>
  <h1>Hello from HTML!</h1>
</body>
</html>

خط <script src=”index.js”></script> به یک فایل JavaScript جدا در شاخه مشابه، به نام index.js اشاره دارد:

// index.js
console.log("Hello from JavaScript!");

این تمام چیزی است که برای ساخت یک وبسایت نیاز دارید. حال فرض کنید که می‌خواستید یک کتابخانه نوشته شده توسط یک شخص دیگر، مثل moment.js (یک کتابخانه که شما را قادر می‌سازد تا تاریخ‌ها را به قالب خوانا برای انسان تبدیل کنید‌) را اضافه کنید. برای مثال، به این صورت می‌توانید یک تابع moment را در JavaScript استفاده کنید:

moment().startOf('day').fromNow();        // 20 hours ago

اما این زمانی قابل اجراست که بر فرض moment.js را بر روی وبسایت خود include کرده باشید. در صفحه خانه moment.js، می‌توانید این دستورالعمل را ببینید:

در بخش install در سمت راست، کارهای زیادی انجام شده است، اما بیایید فعلا آن‌ها را نادیده بگیریم. ما می‌توانیم moment.js را با دانلود فایل moment.min.js در همین شاخه و include کردن آن در فایل index.html، به وبسایت خود اضافه کنیم.

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example</title>
  <link rel="stylesheet" href="index.css">
  <script src="moment.min.js"></script>
  <script src="index.js"></script>
</head>
<body>
  <h1>Hello from HTML!</h1>
</body>
</html>

دقت کنید که moment.min.js قبل از index.js بارگذاری می‌شود، که یعنی می‌توانید به این صورت از تابع moment در فایل index.js استفاده کنید:

// index.js
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());

و ما قبلا به این صورت با استفاده از کتابخانه‌های JavaScript، وبسایت می‌ساختیم. نکته مثبت این بود که درک آن ساده بود. نکته منفی این بود که یافتن و دانلود نسخه‌های جدید کتابخانه هر زمان که بروزرسانی می‌شدند، سخت بود.

استفاده از یک ابزار مدیریت پکیج JavaScript (npm)

از سال 2010 به بعد، چندین ابزار مدیریت پکیج JavaScript پدید آمدند تا به خودکارسازی روند دانلود و بروزرسانی کتابخانه‌ها از یک مخزن مرکزی کمک کنند. به راحتی می‌توان گفت که Bower در سال 2013 معروف‌ترین آن‌ها بود، اما در نهایت در سال 2015 npm جای آن را گرفت. (جای دارد اشاره کنیم که از سال 2016، Yarn هم مقدار زیادی از توجهات را به عنوان یک جایگزین برای رابط npm به خود جذب کرده است، اما همچنان از پکیج‌های npm استفاده می‌کند.)

دقت کنید که npm‌ در اصل یک ابزار مدیریت پکیج بود که به خصوص برای node.js ساخته شده بود. یک runtime جاوااسکریپت که برای اجرا بر روی سرور طراحی شده بود، نه frontend. پس این مسئله یک پکیج frontend جاوااسکریپت برای کتابخانه‌هایی که برای اجرا در مرورگر ساخته شده‌اند را تبدیل به یک انتخاب عجیب می‌کند.

نکته: استفاده از ابزار مدیریت پکیج شامل استفاده از یک خط دستوری می‌شود، که در گذشته به عنوان یک توسعه دهنده frontend به آن نیازی نبود. چه خوب یا چه بد، دانستن نحوه استفاده از خط دستوری، یک بخش مهم از JavaScript مدرن است. (و همچنین در را به زمینه‌های دیگر توسعه باز می‌کند.)

بیایید به استفاده از npm برای نصب خودکار پکیج moment.js، به جای دانلود دستی آن نگاهی داشته باشیم. اگر node.js را نصب دارید، در نتیجه npm را نیز نصب دارید که یعنی می‌توانید خط دستوری خود را به پوشه‌ای که فایل index.js خود را در آن قرار داده‌اید هدایت کنید:

$ npm init

این مسئله، سوال‌های مختلفی را در مقابل شما قرار خواهد داد و پس یک فایل جدید به نام package.json خواهد ساخت. (تنظیمات پیشفرض موجود، مناسب هستند و می‌توانید برای هر سوال کلید Enter را بفشارید.) این یک فایل پیکربندی است که npm برای ذخیره تمام اطلاعات پروژه استفاده می‌کند. فایل package.json با تنظیمات پیشفرض، باید چنین ظاهری داشته باشد:

{
  "name": "your-project-name",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

برای نصب پکیج moment.js، می‌توانیم از دستورالعمل‌های npm در صفحه اصلی آن‌ها با نوشتن این دستور پیروی کنیم:

$ npm install moment --save

این دستور دو کار انجام می‌دهد. در ابتدا، تمام کدها را از پکیج moment.js داخل پوشه node_modules دانلود می‌کند. سپس، به طور خودکار فایل package.json را تغییر می‌دهد تا از moment.js به عنوان یک Dependency برای پروژه پیروی کند.

{
  "name": "modern-javascript-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "moment": "^2.22.2"
  }
}

این مسئله، بعدا در هنگام به اشتراک گذاری یک پروژه کارآمد است. به جای به اشتراک گذاری پوشه node_modules، (که می‌تواند بسیار حجیم شود) فقط باید فایل package.json را به اشتراک بگذارید و توسعه دهندگان دیگر می‌توانند با دستور npm install، تمام پکیج‌های مورد نیاز را به صورت خودکار نصب کنند.

پس دیگر مجبور نیستیم به صورت دستی moment.js را از وبسایت آن دانلود کنیم، بلکه می‌توانیم با استفاده از npm آن را به صورت خودکار دانلود کرده، و بروزرسانی کنیم. با نگاه به داخل پوشه node_modules، می‌توانیم فایل moment.min.js را در شاخه node_modules/moment/min ببینیم. این یعنی ما می‌توانیم نسخه دانلود شده توسط npm فایل moment.min.js را به این صورت در فایل index.html لینک کنیم:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JavaScript Example</title>
  <script src="node_modules/moment/min/moment.min.js"></script>
  <script src="index.js"></script>
</head>
<body>
  <h1>Hello from HTML!</h1>
</body>
</html>

پس نکته خوب این است که حال ما می‌توانیم از npm برای دانلود و بروزرسانی پکیج‌های خود از طریق خط دستوری استفاده کنیم. نکته بد این است که ما پوشه node_modules را می‌گردیم تا مکان هر پکیج را پیدا کرده، و به صورت دستی در HTML خود include کنیم. این کاملا نامناسب است، پس حال به نحوه خودکارسازی این روند نیز نگاهی خواهیم داشت.

استفاده از یک Module Bundler یا اتصال دهنده ماژول JavaScript (Webpack)

اکثر زبان‌های برنامه‌نویسی، راهی برای وارد کردن کد از یک فایل به فایل دیگر فراهم می‌کنند. JavaScript در اصل با این ویژگی طراحی نشده بود، زیرا فقط برای اجرا در مرورگر، بدون هیچ گونه دسترسی به سیستم فایل کامپیوتر کاربر طراحی شده بود. (به دلایل امنیتی) پس برای مدتی طولانی، سازمان‌دهی کد JavaScript در چندین فایل، نیازمند این بود که هر فایل را با متغیرهای به اشتراک گذاری شده به صورت global بارگذاری کنید.

این کاری است که ما با مثال moment.js بالا انجام می‌دهیم. کل فایل moment.min.js باید در HTML بارگذاری شود، که یک متغیر global به نام moment را تعریف می‌کند و این متغیر بعدا برای هر فایلی که بعد از moment.min.js بارگذاری شده است،‌ در دسترس است. (بدون توجه به این که نیازمند دسترسی به آن است یا خیر)

در سال 2009، پروژه‌ای به نام CommonJS با هدف اختصاص‌دهی یک اکوسیستم برای JavaScript خارج از مرورگر شروع شده بود. یک بخش بزرگ از CommonJS، مشخصات آن برای ماژول‌ها بود که در نهایت JavaScript را قادر می‌ساخت تا کد را مانند اکثر زبان‌های برنامه‌نویسی، در میان فایل‌های مختلف بدون متوسل شدن به  متغیرهای Global وارد کرده و خروجی دهد. شناخته‌شده‌ترین پیاده‌سازی برای ماژول‌های CommonJS، در واقع node.js است.

همانطور که پیش‌تر اشاره شد، node.js یک runtime جاوااسکریپت، طراحی شده برای اجرا بر روی سرور است. در زیر، نمونه مثال قبلی با استفاده از ماژول‌های node.js را می‌بینید. به جای بارگذاری تمام moment.min.js با یک تگ اسکریپت HTML، می‌توانید به این صورت آن را مستقیما در فایل JavaScript بارگذاری کنید:

// index.js
var moment = require('moment');
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());

دوباره، بارگذاری ماژول در JavaScript به این صورت کار می‌کند، که با توجه به این که node.js یک زبان سمت سرور با دسترسی به سیستم فایل‌ کامپیوتر است، کاملا هم به خوبی کار می‌کند. Node.js همچنین مکان مسیر هر ماژول npm را می‌داند. پس به جای این که مجبور باشید دستور require(‘./node_modules/moment/min/moment.min.js) را بنویسید، می‌توانید به سادگی بنویسید: require(‘moment’).

این مسئله به کلی برای node.js‌ خوب است، اما اگر تلاش کنید که از کد بالا در مرورگر استفاده کنید، با خطایی که می‌گوید require تعریف نشده است، مواجه خواهید شد. مرورگر به سیستم فایل دسترسی ندارد، که یعنی بارگذاری ماژول‌ها به این صورت بسیار پیچیده است. بارگذاری فایل‌ها باید به صورت دستی انجام شود؛ چه به صورت همگام، (که روند اجرا را کند می‌کند) یا چه به صورت ناهمگام. (که ممکن است مشکلات زمان‌بندی داشته باشد.)

در اینجا، Module Bunlder به میان می‌آید. یک Module Bundler جاوااسکریپت، ابزاری است که با یک قدم ساخت، (که به سیستم فایل دسترسی دارد) به مشکل رسیدگی می‌کند تا یک خروجی نهایی بسازد که با مرورگر سازگار است. (و نیازی به دسترسی به سیستم فایل ندارد) در این مورد، ما به یک Module Bundler نیاز داریم تا تمام بیانیه‌های require (که یک سینتکس مرورگر JavaScript نامعتبر است) را پیدا کند و آن‌ها را با محتویات واقعی هر فایل جایگزین کند. نتیجه نهایی یک فایل جاوااسکریپت bundle شده است. (که به هیچ گونه بیانیه require نیاز ندارد.)

معروف‌ترین اتصال دهنده ماژول، Browserify بود که در سال 2011 منتشر شده بود و در زمینه استفاده از بیانیه‌های require به استایل node.js در سمت frontend، پیشگام بود. (که همین باعث شد تا npm‌ تبدیل به ابزار مدیریت پکیج frontend منتخب شود.) در حدود سال 2015، Webpack در نهایت تبدیل به Module Bundler محبوب‌تر شد.

بیایید به نحوه استفاده از Webpack برای به کار در آوردن مثال require(‘moment’) در مرورگر داشته باشیم. در ابتدا باید  Webpack‌ را در پروژه خود نصب کنیم. خود Webpack یک پکیج npm است، پس باید آن را از طریق خط دستوری نصب کنیم:

$ npm install webpack --save-dev

به آرگومان --save-dev دقت کنید. این آرگومان، آن را به عنوان یک Dependency توسعه ذخیره می‌کند، که یعنی این یک پکیج است که شما در محیط توسعه خود نیاز خواهید داشت، اما نه در سرور خود. می‌توانید این مسئله را منعکس شده در فایل package.json ببینید، که به صورت خودکار بروزرسانی شده بود:

{
  "name": "modern-javascript-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "moment": "^2.19.1"
  },
  "devDependencies": {
    "webpack": "^3.7.1"
  }
}

حال ما Webpack را به صورت نصب شده به عنوان یک پکیج در شاخه node_modules داریم. می‌توانید با استفاده از این خط دستوری، از Webpack استفاده کنید:

$ ./node_modules/.bin/webpack index.js bundle.js

این خط دستوری، ابزار Webpack را که در پوشه node_modules نصب شده بود را اجرا می‌کند، با فایل index.js شروع می‌کند، تمام بیانیه‌های require را پیدا می‌کند و آن‌ها را با کد مناسب جایگزین می‌کند تا یک خروجی تکی به نام index.js بسازد. این یعنی از آنجایی که این فایل شامل بیانیه‌های نامعتبر است، ما دیگر قرار نیست که از آن در مروگر خود استفاده کنیم. ما در عوض از خروجی bundle.js در مروگر استفاده می‌کنیم، که باید در فایل index.htnl منعکس شود:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JavaScript Example</title>
  <script src="bundle.js"></script>
</head>
<body>
  <h1>Hello from HTML!</h1>
</body>
</html>

حال اگر مرورگر را مجددا بارگذاری کنید، باید ببینید که همه چیز به مانند قبل کار می‌کند.

دقت کنید که هر زمان فایل index.js را تغییر می‌دهیم، باید دستور Webpack‌ را اجرا کنیم. این خسته کننده است، و همینطور که از امکانات پیشرفته‌تر Webpack استفاده می‌کنیم، حتی خسته کننده‌تر هم خواهد شد. Webpack می‌تواند تنظیمات را از یک فایل پیکربندی در شاخه ریشه پروژه به نام webpack.config.js بخواند، که در این مورد چنین چیزی خواهد بود:

// webpack.config.js
module.exports = {
  entry: './index.js',
  output: {
    filename: 'bundle.js'
  }
};

حال هر زمان که index.js را تغییر می‌دهیم، می‌توانیم Webpack را با استفاده از این دستور اجرا کنیم:

$ ./node_modules/.bin/webpack

از آنجایی که Webpack این تنظیمات را از فایل webpack.config.js بارگذاری می‌کند، دیگر نیازی نیست که گزینه‌های index.js و bundle.js را مشخص کنیم. این بهتر است، اما هنوز هم وارد کردن این دستور برای هر تغییر کد، خسته کننده است. کمی جلوتر، این فرایند را روان‌تر خواهیم کرد.

در کل، این شاید چیز زیادی به نظر نیاید، اما برتری‌های زیادی در این جریان کاری وجود دارند. ما دیگر در حال وارد کردن اسکریپت‌های خارجی با استفاده از متغیرهای global نیستیم. تمام کتابخانه‌های JavaScript‌ جدید بر خلاف اضافه کردن تگ‌های <script> در HTML، با استفاده از بیانیه require اضافه خواهند شد. و حال که یک قدم ساخت (Build Step) اضافه کرده‌ایم، امکانات قدتمند دیگری هم هستند که می‌توانیم به جریان توسعه خود اضافه کنیم.

در اینجا، بخش اول این مقاله به پایان می‌رسد. در بخش بعدی، با transpile کردن کد شروع خواهیم کرد.

منبع

مقالات پیشنهادی

درک JavaScript مدرن برای دایناسورها - بخش دوم

Transpile کردن کد، یعنی تبدیل کردن کد نوشته شده در یک زبان، به کدی در یک زبان مشابه. این یک بخش مهم از توسعه frontend است. از آنجایی که مرورگرها در اض...

وب سایت های الهام بخش برای طراحی - سری ششم

امروز قصد داریم یک سری وبسایت های خارجی که بطور کاربردی ، زیبا و قدرتمند طراحی شدن رو براتون قرار بدیم تا شما بتونین با طریقه طراحی اونها آشنا بشین یا...

وب سایت های الهام بخش برای طراحی - سری هفتم

امروز قصد داریم یک سری وبسایت های خارجی که بطور کاربردی ، زیبا و قدرتمند طراحی شدن رو براتون قرار بدیم تا شما بتونین با طریقه طراحی اونها آشنا بشین یا...

وب سایت های الهام بخش برای طراحی - سری 8

امروز قصد داریم یک سری وبسایت های خارجی که بطور کاربردی ، زیبا و قدرتمند طراحی شدن رو براتون قرار بدیم تا شما بتونین با طریقه طراحی اونها آشنا بشین یا...