React به همراه Typescript و Webpack

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

در حال حاضر محبوبیت React به ارتفاعات صعود کرده است، محبوبیت Typescript در حال افزایش است و به جرئت می‌تواند گفت که Webpack مورد پسندترین module bundler برای برنامه‌های است. گرچه، هنوز هم کمبود مثالی برای بهترین راه در شروع یک پروژه React به همراه Typescript و Webpack‌ حس می‌شود. با این وجود، اگر می‌خواهید از create-react-app یا هر بسته شروع React دیگری استفاده کنید، منابعی خوبی برای شما وجود دارند. اما اگر می‌خواهید کنترل خوبی بر روی پیکربندی برنامه خود داشته باشید، موارد کمی در دسترس هستند.

نکته منفی در استفاده از یک cli مانند create-react-app این است که باید به مانند create-react-app، به این ابزار تکیه کنید:

  • Webpack
  • Babel
  • PostCSS / Autoprefixer
  • Jest
  • Flow (اختیاری)

اگر با هر کدام از موارد بالا مشکل دارید، به سختی آن را قبول خواهید کرد. create-react-app بر پایه قول خود، یعنی «عدم نیاز به هیچ‌گونه پیکربندی» کار می‌کند؛ زیرا شما نمی‌توانید این ابزار را پیکربندی کنید. create-react-app به صورتی کار می‌کند که وقتی از آن گذشتید، می‌توانید پیکربندی را شروع کنید. اما آیا ساده‌تر نمی‌شد اگر از اول پیکربندی‌ها را مدیریت می‌کردید؟ به علاوه، وقتی که از آن می‌گذرید، تعداد dependencyها در پروژه شما افزایش خواهد یافت، که حتی ممکن است به نیمی از آن‌ها نیاز نداشته باشید. با این وجود، احتمالا create-react-app بهترین جعبه‌ابزار برای شروع پروژه به عنوان یک تازه‌کار، با یک پروژه سطح پایین یا متوسط است.

اگر می‌خواهید یک پروژه را با این موارد راه‌اندازی کنید، این مقاله برای شما کمک کننده خواهد بود:

  • React - فریم‌وورک frontend‌ اولیه.
  • Typescript - زبان اولیه که به JavaScript کمپایل می‌شود.
  • Webpack - bundle کننده برنامه.
  • Express - سروری که برنامه را میزبانی خواهد کرد.
  • بارگذاری ماژول سریع - ابزاری که تمام تغییرات کد شما را در هنگام اجرای سرور بر روی مرورگر، بدون نیاز به توقف و اجرای مجدد برنامه بارگذاری می‌کند.

کار با react، اسکریپت نویسی و pack کردن را شروع می‌کنیم

قبل از شروع، یک پوشه خالی در حافظه خود بسازید تا از ابتدا شروع کنیم. اگر از یک دستگاه با سیستم عامل ویندوز استفاده می‌کنید،  CMD خود را باز کده و این دستورات را بنویسید:

mkdir react-ts-webpack
cd react-ts-webpack
code .

آخرین دستور، پوشه‌ای که اخیرا ساخته شده است را به عنوان یک پروژه در Visual Studio Code باز می‌کند؛ یک ویرایشگر کد محبوب که احتمالا شما نیز آن را دارید. البته در صورت تمایل می‌توانید از ویرایشگر مورد علاقه خود استفاده کنید.

حال طبق رسوم وقتی که یک پروژه جدید می‌سازید، با نوشتن این دستور، npm را در پروژه خود راه‌اندازی کنید:

npm init -y

حال می‌خواهیم فایل‌ها و پوشه‌هایی بسازیم که برای راه‌اندازی پروژه ضروری هستند. پس در شاخه ریشه خود، این پوشه‌ها / فایل‌ها را بسازید:

  • فایل جدیدی به نام webpack.config.js بسازید: این فایل، برای پیکربندی‌های Webpack است.
  • فایل جدیدی به نام tsconfig.json بسازید: این فایل، برای پیکربندی‌های JavaScript‌ است.
  • فایل جدیدی به نام server.js بسازید: این فایل، سرور شما برای شروع برنامه است.
  • پوشه جدیدی به نام src بسازید.
  • پوشه جدیدی به نام app داخل پوشه src بسازید.
  • فایل جدیدی به نام index.html داخل پوشه app، و پوشه components بسازید.
  • فایل جدیدی به نام App.tsx داخل پوشه app و فایل دیگری به نام Hello.tsx داخل پوشه components بسازید.

پس ساختار پوشه برنامه چنین چیزی است:

|-src
  |-app
    |-components
      |-Hello.tsx
    |-App.tsx
    |-index.html
|-package.json
|-package-lock.json
|-server.js
|-tsconfig.json
|-webpack.config.js

شروع دانلودها

حال می‌خواهیم Dependencyهای تولید و توسعه خود را دانلود کنیم.

دانلود Dependencyهای تولید

npm i react react-dom express typescript --save

دانلود Dependencyهای توسعه

npm i @types/react @types/react-dom webpack webpack-cli ts-loader webpack-dev-middleware webpack-hot-middleware html-webpack-plugin source-map-loader -D

تبریک! حال بدون نوشتن تقریبا هیچ کدی، نصف راه را گذرانده‌ایم.

کدنویسی

در اینجا، محتویات فایل‌هایی که ساختید را می‌بینید. می‌توانید آن‌ها را از اینجا به داخل فایل‌های خود کپی کنید.

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>React with Typescript and Webpack</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>

tsconfig.json:

{
    "compilerOptions": {
      "allowSyntheticDefaultImports": true,
      "jsx": "react",
      "module": "commonjs",
      "noImplicitAny": true,
      "outDir": "./build/",
      "preserveConstEnums": true,
      "removeComments": true,
      "sourceMap": true,
      "target": "es5"
    },
    "include": [
      "./src/**/**/*"
    ]
  }

webpack.config.js:

const path = require('path'),
    webpack = require('webpack'),
    HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: {
        app: ['./src/app/App.tsx', 'webpack-hot-middleware/client'],
        vendor: ['react', 'react-dom']
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'js/[name].bundle.js'
    },
    devtool: 'source-map',
    resolve: {
        extensions: ['.js', '.jsx', '.json', '.ts', '.tsx']
    },
    module: {
        rules: [
            {
                test: /\.(ts|tsx)$/,
                loader: 'ts-loader'
            },
            { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({ template: path.resolve(__dirname, 'src', 'app', 'index.html') }),
        new webpack.HotModuleReplacementPlugin()
    ]
}

به کار با React می‌رسیم

بیایید یک کامپوننت React معمولی بنویسیم؛ زیرا این کدی است که باید بعد از راه‌اندازی بر روی آن تمرکز کنید.

Hello.tsx:

import * as React from 'react';
interface IProps {
   compiler: string,
   framework: string,
   bundler: string
}
export class Hello extends React.Component<IProps, {}> {
   render() {
   return <h1>This is a {this.props.framework} application using    {this.props.compiler} with {this.props.bundler}</h1>
   }
}

App.tsx:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Hello } from './components/Hello';
ReactDOM.render(<Hello compiler="Typescript" framework="React" bundler="Webpack" />,
document.getElementById('root'));

Pack کردن

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

  • entry: نحوه‌ای که Webpack کار می‌کند، به این صورت است که شما برای آن یک نقطه ورود فراهم می‌کنید و خودش با توجه به چیزی که ورودی شما وارد می‌کند، راهش را در ساخت برنامه‌ شما پیدا می‌کند، و یک نمودار Dependency می‌سازد. هر چه قدر که ویژگی برای این آبجکت فراهم کنید، همان مقدار bundle ساخته می‌شوند؛ پس Webpack با نگاه کردن به پیکربندی‌های entry و output، فایل‌های app.bundles.js و vendor.bundles.js را می‌سازد. علت داشتن bundleهای جداگانه، این است که احتمال تغییر کدهای react و react-dom شما کم است؛ پس داشتن آن‌ها در bundleهای جدا، باعث می‌شود که مرورگر شما بتواند برای کارایی بهتر آن‌ها را cache‌ کند.
  • output: وقتی که Webpack باید کد bundle شده از برنامه شما را به دیسک محدود کند، به این پیکربندی نگاه می‌کند. Path، شاخه خروجی برای نوشته شدن کد شما است و filename همانطور که از نامش پیداست، نام فایلی است که به کد خروجی داده خواهد شد.
  • devtool: Webpack برای اضافه کردن ابزاری خاص برای توسعه، به این پیکربندی نگاه می‌کند. در اینجا، source-map اضافه شده است تا کد شما در مروگر source-map شود و خطایابی در زمان توسعه آسان‌تر شود.
  • resolve: Weback‌ برای تصمیم‌گیری در جهت در نظر گرفتن یا رد کردن یک فایل هنگام bundle کردن، به اینجا نگاه می‌کند.
  • module: این پیکربندی Webpack را قادر می‌سازد تا یک فایل خاص که توسط برنامه درخواست شده است را با کمک loaderها بارگذاری کند. ما در برنامه خود از ts-loader و source-map-loader استفاده می‌کنیم. source-map-loader در بالا توضیح داده شد. حال بدون ts-loader، Webpack‌ نمی‌تواند این import را در فایل App.tsx درک کند؛ زیرا کامپوننت Hello یک فایل tsx است که توسط ویرایشگر شما درک می‌شود، اما وقتی که فرایند import صورت می‌گیرد، Webpack آن را درک نمی‌کند.
import { Hello } from './components/Hello';
  • pluginها: Webpack نمی‌تواند هر کاری را انجام دهد؛ و انتظار داشتن این مسئله از آن، به نوعی اشتباه است. پس Webpack با استفاده از pluginها به محدودیت‌های خود غلبه می‌کند، تا بتواند فراتر از ظرفیت‌های خود گسترش داده شود. مانند پلاگین html-webpack-plugin که یک فایل الگو برای مرورگر از فایل index.html ما در شاخه src می‌سازد. یا مثلا پلاگین HotModuleReplacement که کد ما را قادر می‌سازد تا به طور خودکار مجددا بارگذاری شود و نیازی به توقف و اجرای مجدد سرور، هر زمانی که تغییری در برنامه ایجاد می‌شود نداشته باشیم.

نوبت به ساخت می‌رسد

حال می‌توانید به صورت دستی برنامه خود را بسازید. قبل از اجرای سرور خود، به طور خودکار به بخش ساخت می‌رویم. فعلا یک اسکریپت build به فایل package.json خود اضافه کرده، و دستور Webpack را اجرا کنید.

package.json:

...
scripts: {
...
"build": "./node_modules/.bin/webpack",
...
}
...

اگر به ترمینال رفته و این دستور را اجرا کنید، می‌توانید برنامه bundle شده خود را در پوشه dist ببینید.

این دستور را در ترمینال خود اجرا کنید:

npm run build

خروجی موجود در ترمینال:

حال وقت استفاده از Express است

اگر نمی‌خواهید از Express برای اجرای سرور برای برنامه React خود استفاده کنید، می‌توانید از webpack-dev-server استفاده کنید که آن را مدیریت می‌کند و مجبور نیستید خودتان یک سرور بنویسید. اما اگر می‌خواهید در سرور سفارشی خود انعطاف داشته باشید، routeها را مدیریت کرده، و درخواست‌ها و پاسخ‌ها را ویرایش کنید. پیشنهاد می‌شود که سرور خود را خودتان بنویسید.

server.js:

const path = require('path'),
   express = require('express'),
   webpack = require('webpack'),
   webpackConfig = require('./webpack.config.js'),
   app = express(),
   port = process.env.PORT || 3000;
app.listen(port, () => { console.log(`App is listening on port ${port}`) });
app.get('/', (req, res) => {
   res.sendFile(path.resolve(__dirname, 'dist', 'index.html'));
});
let compiler = webpack(webpackConfig);
app.use(require('webpack-dev-middleware')(compiler, {
   noInfo: true, publicPath: webpackConfig.output.publicPath, stats:    { colors: true }
}));
app.use(require('webpack-hot-middleware')(compiler));
app.use(express.static(path.resolve(__dirname, 'dist')));

حال می‌توانید به فایل package.json خود رفته، و اسکریپت شروع را اضافه کنید:

"start": "npm run build && node server.js"

بارگذاری مجدد

وقتی که سرور را با اجرای دستور npm start در ترمینال اجرا کنید، webpack-dev-middleware و webpack-hot-middleware باید به جایگزینی ماژول‌ لحظه‌ای رسیدگی کنند، اما مسئله آن نیست.

شاید متوجه شوید که بعد از اعمال یک تغییر، Webpack به درستی کد تغییر یافته را کمپایل کرده، و انتشار می‌دهد؛ اما این فقط وقتی که مرورگر خود را مجددا بارگذاری می‌کنید، تاثیر خود را می‌گذارد. ما به دنبال این اتفاق نبودیم. مرورگر بعد از ساخت مجدد کامپوننت تغییر یافته، باید مجددا بارگذاری می‌شد.

برای حل این مشکل، باید کمی فایل App.tsx را تغییر دهیم:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Hello } from './components/Hello';
declare let module: any

ReactDOM.render(<Hello compiler="Typescript" framework="React..." bundler="Webpack" />,
document.getElementById('root'));

if (module.hot) {
   module.hot.accept();
}

پس ما اساسا به برنامه خود می‌گوییم که تغییرات لحظه‌ای که توسط Webpack اعمال شده‌اند را بپذیرد. بخش تعریف، به این علت ضروری است که ویژگی «در لحظه» به طور پیشفرض بر روی آبجکت module وجود ندارد، و ما باید از Typescript درخواست کنیم که اجازه این کار را بدهد.

پس از این که بعد از اعمال این تغییر سرور خود را ری‌استارت کنید، سرور شما همانطور که انتظار می‌رود کار خواهد کرد.

در اینجا، کار ما به پایان می‌رسد و با موفقیت توانستیم از React، Typescript و Webpack در کنار هم استفاده کنیم. امیدوارم این مقاله برای شما پرکاربرد بوده باشد.

منبع

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

چگونه از Bootstrap به همراه React استفاده کنیم؟

با محبوبیت روز افزون برنامه‌های تک صفحه‌ای در سال‌های اخیر، تعداد زیادی فریم‌وورک جاوااسکریپت frontend مانند Angular، React، VueJS و Ember پدید آمده‌ا...

استفاده از Create React App نسخه 2 و TypeScript

حال که Create React App نسخه ۲ منتشر شده است، پشتیبانی TypeScript رسمی هم به همراه آن می‌آید. این یک مسئله هیجان انگیز برای کاربران JavaScript است که...

راهنمای سریع برای کمک به شما در درک و ساخت برنامه‌های ReactJS

اولین بخش، نحوه ساخت یک برنامه React ساده با استفاده از create-react-app را نشان داده و ساختار پروژه را شرح می‌دهد.بخش دوم یک قطعه کد که از قبل بر روی...

چرا به Context API جدید React نیاز داریم ؟

چندین سال پیش، Context API به عنوان یک ویژگی آزمایشی با هشدار «این API در آینده می‌تواند بشکند» معرفی شد. با توجه به این که Context API آزمایشی بود، ا...