اشتراک‌ها
ورک فلو نسخه راست چین شده بوت استرپ 4

نسخه 4 بوت استرپ راست چین شده با پلاگین gulp-rtlcss .

برای استفاده سریع :

ابتدا مخزن را clone کنید. سپس با رفتن به دایرکتوری آن دستور gulp watch را بزنید. یا فایل index.html را با مرور گر خود باز کنید.

ورک فلو نسخه راست چین شده بوت استرپ 4
مطالب
معرفی فریم ورک Blueprint CSS
احتمالا با عباراتی مانند طراحی table less و مزیت‌های طراحی با CSS، همانند سرعت بالاتر بارگذاری سایت در مقایسه با نمایش یک جدول که نیازمند دریافت تمام جزئیات آن و سپس رندر نهایی اطلاعات آن توسط مرورگر است، بارها برخورد داشته‌اید. اما ... آیا یکبار سعی کرده‌اید که به صورت دستی همان کارهایی را که پیشتر با HTML table انجام می‌دادید، اینبار توسط CSS پیاده سازی کنید؟
در اکثر اوقات نتیجه کار مایوس کننده، بسیار سخت و نگهداری آن در طول زمان بسیار مشکل خواهد بود؛ به علاوه سازگاری با مرورگرهای مختلف و نکات ریز هر کدام را نیز لحاظ کنید. به همین جهت تعدادی فریم ورک CSS برای شبیه سازی گرید و جدول تهیه شده‌اند که کار طراحی table less را بسیار ساده و لذت بخش کرده‌اند. یکی از این موارد، فریم ورک Blueprint CSS نام دارد و در ادامه نحوه استفاده از آن‌را مرور خواهیم کرد. این مرور هم مستقل است از فناوری سمت سرور مورد استفاده و صرفا مباحث html و CSS آن بررسی خواهند شد.


دریافت Blueprint CSS

این فریم ورک سورس باز را از مخزن کدهای آن در GitHub می‌توانید دریافت کنید: (^)
البته نگران حجم نزدیک به 4 مگابایتی بسته دریافتی آن نباشید؛ زیرا نهایتا با سه فایل CSS از آن بیشتر کاری نداریم و مابقی مثال‌های آن هستند.
پس از دریافت آن، یک پوشه را به نام blueprint ایجاد کرده و سه فایل ie.css ،print.css و screen.css را در آن قرار دهید.
به علاوه داخل این پوشه، یک پوشه جدید دیگر را به نام src ایجاد کرده و فایل grid.png موجود در این بسته را نیز در آن کپی کنید.


ساختار ابتدایی یک صفحه مبتنی بر Blueprint CSS

پس از ایجاد پوشه blueprint و src به نحوی که توضیح داده شد، ابتدایی‌ترین ساختار یک صفحه تشکیل شده با blueprint css به نحو زیر است:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Blueprint test page</title>

    <!-- Framework CSS -->
    <link rel="stylesheet" href="blueprint/screen.css" type="text/css" media="screen, projection">
    <link rel="stylesheet" href="blueprint/print.css" type="text/css" media="print">
    <!--[if lt IE 8]><link rel="stylesheet" href="blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
  </head>
  <body>
    <div class="container showgrid">
  test
  <hr class="space" />
  <hr class="space" />
  <hr class="space" />
  <hr class="space" />
  <hr class="space" />
  test
    </div>
  </body>
</html>
توضیحات:
پس از مشخص سازی DocType (مهم)، سه فایل CSS یاد شده به header صفحه اضافه خواهند شد. همانطور که ملاحظه می‌کنید، سازگاری با IE نیز مدنظر آن بوده است.
کار با blueprint css همواره داخل div زیر انجام می‌شود:
<div class="container">
  page
</div>
توسط کلاس container یک گرید به عرض 950px در میانه صفحه برای شما تشکیل خواهد شد.
اگر علاقمند باشید که این گرید را مشاهده نمائید و همچنین بتوانید ستون‌های آن‌را نیز شمارش کنید، تنها کافی است showgrid را به این class تعریف شده اضافه نمائید (همانند ساختار صفحه فوق). به این ترتیب شکل زیر نمایان خواهد شد:

مطابق شکل فوق، در این عرض مشخص، 24 ستون آن در اختیار ما خواهند بود.
به علاوه ذکر hr با class=space سبب خواهد شد تا مطابق تنظیمات و فاصله بندی منظم این فریم ورک، یک سطر خالی برای ما ایجاد شود.


طراحی بدون جدول با Blueprint CSS

در ادامه قصد داریم در این صفحه ابتدایی، یک جدول با دو ستون و دو ردیف را ایجاد کنیم:
  <body>
    <div class="container showgrid">
      <div class="span-12">
        row1-col1
      </div>
      <div class="span-12 last">
        row1-col2
      </div>
      <div class="span-12">
        row2-col1
      </div>
      <div class="span-12 last">
        row2-col2
      </div>
    </div>
  </body>
که شکل زیر را برای ما ایجاد خواهد کرد:

توضیحات:
ستون‌های گرید نهایی با رنگ آبی مشخص هستند (class=container showgrid). اگر نیاز به 12 ستون داریم، می‌نویسیم span-12 و ... همین! به این ترتیب یک سلول جدول، با 12 ستون در اختیار ما خواهد بود. سلول بعدی هم در اینجا 12 ستونه است. اما یک last را اضافه‌تر دارد. در span-12 last این last به معنای انتهای ردیف جاری است و ذکر آن الزامی است.
تا اینجا یک ردیف تمام شد. اکنون در ادامه ردیف دوم را نیز به همین ترتیب با دو div و class‌هایی که ملاحظه می‌کنید، مشخص خواهیم کرد.
نحوه کار کلی با Blueprint css به همین سادگی است که ملاحظه می‌کنید. تعداد ستون‌های مورد نیاز را با ذکر container showgrid به سادگی می‌توان شمارش کرد. سپس این اعداد شمارش شده و مد نظر را پس از span ذکر کنید. مثلا اگر یک طرح سه ستونه نیاز دارید به صورت زیر خواهد بود:
  <body>
    <div class="container showgrid">
      <div class="span-8">
        row1-col1
      </div>
      <div class="span-8">
        row1-col2
      </div>
      <div class="span-8 last">
        row1-col3
      </div>
    </div>
  </body>

طراحی سلول‌های تو در تو


سؤال: ما پیشتر در یک html table به سادگی می‌توانستیم داخل یک سلول آن حتی یک جدول جدید نیز قرار دهیم، اینجا چطور؟
پاسخ: در اینجا هم بجای td و tr و table، از divهای تو در تو استفاده کنید. بستن ستون آخر را با last یاد شده فراموش نکنید. مثلا:
  <body>
    <div class="container showgrid">
      <div class="span-8">
                <div class="span-4">
                    row-1, col1 : cell-1
                </div>
                <div class="span-4 last">
                    row-1, col1 : cell-2
                </div>
      </div>
      <div class="span-8">
        row1-col2
      </div>
      <div class="span-8 last">
        row1-col3
      </div>
    </div>
  </body>
در اینجا در اولین div تعریف شده دو div تو در تو اضافه شده‌اند. البته با توجه به اینکه div والد 8 ستونی است، جمع عرض divهای فرزند باید 8 باشد که در اینجا به دو div چهارستونی تقسیم شده است.


سایر امکانات Blueprint CSS

تا اینجا با کلیات نحوه طراحی یک جدول به کمک CSS و فریم ورک Blueprint CSS آشنا شدیم (به کمک container و span-n آن). در ادامه مرور سریعی خواهیم داشت بر سایر امکانات این فریم ورک CSS و منظور از این امکانات، کلمات و عبارات مجازی است که می‌توانید داخل classهای divهای تعریف شده اضافه نمائید (CSS selectors تعریف شده در آن):
prepend-n و border:
فرض کنید در divهای تو در توی قسمت قبل، قصد داریم عرض ستون اول را بجای 4 ستون به 3 ستون تبدیل کنیم، اما این div را یک ستون به سمت راست حرکت دهیم:
  <body>
    <div class="container showgrid">
      <div class="span-8">
        <div class="prepend-1 span-3 border">
               row-1, col1 : cell-1
        </div>
        <div class="span-4 last">
              row-1, col1 : cell-2
        </div>
      </div>
      <div class="span-8">
           row1-col2
      </div>
      <div class="span-8 last">
           row1-col3
      </div>
    </div>
  </body>
برای این منظور همانطور که ملاحظه می‌کنید از prepend-1 استفاده شده است. border در اینجا سبب خواهد شد تا در سمت راست div یک خط عمودی رسم شود. در مقابل آن colborder هم وجود دارد که سبب ترسیم حاشیه با فاصله بیشتری نسبت به border می‌شود.
شبیه به همین قابلیت، با append-x (افزودن تعدادی ستون به سمت راست)، prepend-top (فاصله‌ای به اندازه 1.5em را به بالای div اضافه می‌کند) و append-bottom (فاصله‌ای به اندازه 1.5em را به پایین div اضافه می‌کند) نیز وجود دارد.
در مقابل این‌ها، push-n و pull-n هم وجود دارند. کار append و prepend اضافه کردن چند ستون به بعد و قبل از یک div است. push یک div را به تعداد واحدی که مشخص می‌کنیم به سمت راست حرکت می‌دهد. pull یک div را n ستون به سمت چپ حرکت خواهد داد (بدون تغییری در تعداد ستون‌ها).


دریافت مرجع سریع Blueprint CSS
مطالب
استفاده از دیتابیس Sqlite در الکترون (قسمت اول)
یکی از مهمترین بخش‌های هر برنامه، بخش ذخیره و بازیابی دیتا است. برای ذخیره سازی از طریق وب و مرورگر، راه‌های مختلف زیادی چون webStorage ,Indexed DB ,Sqlite ,NeDB, و ... وجود دارند.

Sqlite دیتابیس مناسبی برای برنامه‌های چندسکویی است و عموما به عنوان اولین گزینه استفاده می‌شود. برای کار با این دیتابیس، ما از ماژول sql.js که یکی از ماژول‌های معروف در جاوااسکریپت است، استفاده می‌کنیم. برای نصب آن از طریق npm، به شکل زیر اقدام می‌کنیم:
npm install sql.js --save
سپس کد همیشگی زیر را برای آغاز، در فایل index.js وارد می‌کنیم:
const{app,BrowserWindow}=require("electron");

let win;
function onLoad()
{
  win=new BrowserWindow({
    width:800,
    height:600
  });
  win.loadURL(`file://${__dirname}/index.html`);
}

app.on("ready",onLoad());
  ابتدا باید بررسی کنیم که آیا دیتابیس از قبل موجود است یا خیر و اگر موجود نبود، برای اولین بار آن را بسازیم. برای بررسی وجود یک فایل نیز می‌توانیم از چند دستور مختلف استفاده کنیم. path.exists و fs.exists، دو عدد از آن‌ها می‌باشند که هر دوی آنان به صورت غیرهمزمان هستند و پارمتر اولشان نام فایل، به همراه مسیر (یا تنها مسیر) است و پارامتر دوم هم یک تابع callback است که به عنوان پارامتر، جواب را بر می‌گرداند. برای استفاده از حالت همزمان، عبارت Sync را به انتهای نام متدها اضافه کنید. نحوه استفاده از آن به شکل زیر است:
var path=require("path");
path.exists('filepath",(status)=>
{
....
});
var status=path.existsSync("file");
//===============================
var fs=require("fs");
fs.exists('filepath",(status)=>
{
....
});
var status=path.existsSync("file");
ولی بهتر است بدانید که تاریخ انقضای دو دستور بالا سر آمده است و الان از دستور fs.stat استفاده می‌شود. این متد هم به دو شکل همزمان و غیرهمزمان وجود دارد:
fs.stat('foo.txt', function(err, stat) {
    if(err == null) {
        console.log('فایل موجوده');
    } else if(err.code == 'ENOENT') {
        // فایل وجود نداره
        fs.writeFile('log.txt', 'Some log\n');
    } else {
        //خطای دیگری رخ داده است
    }
});
برای استفاده از حالت همزمان هم کد را به شکل زیر بنویسید:
try {
  stats = fs.statSync(path);
  console.log("File exists.");
}
catch (e) {
  console.log("File does not exist.");
}
سپس کد زیر را در فرآیند اصلی وارد می‌کنیم:
const fs = require('fs');
const sql = require('sql.js');

  dbPath = './mydb.sqlite';
  dbExists=false;

try {
  dbExists = fs.statSync(dbPath);
}
catch (e) {
}

if(!dbExists)
{
  //create Database
var sqlStr=fs.readFileSync("./sql.txt");
var db = new sql.Database();
db.run(String(sqlStr));

//write to disk
var data=db.export();
var buffer=new Buffer(data);
fs.writeFileSync(dbPath,buffer);
}
else{
  var buffer = fs.readFileSync(dbPath);
  var db = new sql.Database(buffer);
}
ابتدا بررسی می‌کنیم که آیا فایلی با نام test.sqlite در مسیر جاری است یا خیر. در صورتی که نباشد، کد داخل شرط اجرا می‌شود. در اولین خط شرط، فایلی را با نام sql.txt که شامل محتوای زیر است، می‌خوانیم:
CREATE TABLE numbers (
    id     INT          PRIMARY KEY
                        UNIQUE
                        NOT NULL,
    fname  VARCHAR (20) NOT NULL,
    lname  VARCHAR (30) NOT NULL,
    number VARCHAR (15) NOT NULL
);
insert into numbers values(1,'ali','yeganeh','03111223344');
insert into numbers values(2,'xxx','yyy','45454555');
سپس در خطوط بعدی دیتابیس جدیدی را ایجاد می‌کنیم و دستور sql را با متد run اجرا می‌کنیم. دستوراتی که متد run اجرا می‌کند، شامل خروجی نیستند. پس دستوراتی را که نیاز به خروجی ندارند، به این متد بسپارید. سپس از این دیتابیس، یک شیء خروجی را دریافت میکنم و با بافر کردن، آن را در یک فایل ذخیره می‌کنیم. در صورتی هم که از قبل دیتابیس وجود داشته باشد، بافر خوانده شده را مستقیما به سازنده Database می‌دهیم.
بعد از آن نیاز است تا دیتابیس در دسترس Render Process‌ها قرار بگیرد که در مقاله "شیوه کدنویسی در الکترون " در مورد global صحبت کرده‌ایم و نحوه استفاده از آن را فرا گرفتیم:
global.db=db;

در پایان اجرای برنامه لازم است که دیتابیس توسط دستور close بسته شود. سپس کد زیر را در رویداد windows-all-closed می‌نویسیم:
app.on('window-all-closed', () => {
  db.close();
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
در این کد گفته‌ایم که موقعی که تمام پنجره‌های برنامه بسته شدند، دیتابیس را نیز ببند.
(چند مورد خارج از بحث): کد بعدی که مورد استفاده قرار گرفته است و در مقالات قبلی در مورد آن صحبت نکرده‌ایم این است که در سیستم‌های مک، وضعیت به این قرار است که اگر شما برنامه را ببیندید، آن برنامه بسته نشده و در پس زمینه فعال است و می‌توانید آن را از طریق dock اطراف صفحه، مجددا فعال کنید. ولی با نوشتن کد بالا، ما این وضعیت را اعلام کرده‌ایم که اگر تمامی پنجره‌ها بسته شدند، کل برنامه را ببند.

همچنین بسیار خوب است که کد زیر را هم همیشه اضافه کنید:
win.on('closed', () => {
    win = null;
  });
موقعی که پنجره مربوطه بسته شود، متغیری که به پنجره اشاره می‌کند، در حافظه می‌ماند. پس بهتر است که این مقدار حافظه را رها کنید تا Garbage Collector اقدام به حذف آن در حافظه کند.
پس اگر این کد را نوشتید، وضعیت سیستم عامل مک را به خاطر داشته باشید و مجبور هستید کد زیر را نیز اضافه کنید:
app.on('activate', () => {
  if (win === null) {
    createWindow();
  }
});
در این صورت اگر کاربر پنجره را از طریق Dock مجددا فعال کرد، پنجره برنامه شما نیز مجددا نمایش داده خواهدشد.

بعد از اینکه دیتابیس را به شیء global دادیم، در صفحه html کد زیر را وارد می‌کنیم:
<html>
  <head>
    <script src="./jquery.min.js"></script>
    <link href="./bootstrap-3.3.6-dist/css/bootstrap.min.css" rel="stylesheet"></link>
    <meta charset="utf-8">
    <title></title>
    <script>
    const {remote}=require("electron");
    let db=remote.getGlobal("db");
    </script>
  </head>
  <body>

<table id="people" class="table table-hover table-striped">
<th>
  <tr>
    <td>First Name</td>
    <td>last Name</td>
    <td>Phone Number</td>
  </tr>
</th>
<tbody>

</tbody>
</table>
  </body>
</html>
در این کد یک جدول داریم و قصد ما این است که آن دو سطر را که در ابتدا اضافه کردیم، در آن نمایش دهیم. چیزی که در این کدها قابل ملاحظه است، این است که ما از بسته‌های بوت استرپ و جی کوئری استفاده می‌کنیم. در ادامه کدهای زیر را در تگ اسکریپت  وارد می‌کنیم:
$(document).ready(()=>
{
  //show data
var tableBody=$("#people");

db.each("select * from numbers",(row)=>{
  let rowTemplate=`<tr><td>${row.fname}</td><td>${row.lname}</td><td>${row.number}</td></tr>`;
  tableBody.append(rowTemplate);
});
متد each برای مواقعی مناسب است که شما تعدادی سطر را برای بازگشت دارید و callback آن، به ازای هر سطر اجرا می‌شود و با استفاده از یک template string سطر سازی را انجام داده و آن را با استفاده از توابع جی کوئری به انتهای جدول اضافه می‌کنیم.
حال وقت آن رسیده است که خروجی کار را ببینیم. پس کد npm start را اجرا می‌کنیم. همانطور که می‌بینید خروجی به راحتی نمایش داده می‌شود. در مقاله بعدی بیشتر در این مورد صحبت می‌کنیم.
مطالب
شروع کار با webpack - قسمت سوم
در مطلب قبلی با فایل‌های پیکربندی وبپک، وب سرور وبپک، لودر‌ها و ... آشنا شدیم .


استفاده از preLoader‌ها در وبپک 
پیش‌تر با Loader‌ها آشنا شدیم و دلیل استفاده‌ی از آنها نیز ذکر و Loader تایپ اسکریپت را نیز نصب کرده و با استفاده از آن فایل‌های پروژه را ترنسپایل کردیم. اما ممکن است که همه‌ی کارها در استفاده از یک Loader خلاصه نشوند. ممکن است بخواهید از یک ابزار Linting مانند jsHint  قبل از اجرای Loader ها بهره ببرید و این دقیقا کاری است که به preLoader‌ها سپرده می‌شود. به عنوان مثال پیش لودر jsHint را نصب خواهیم کرد. اضافه کردن preLoader‌ها در فایل پیکربندی وبپک تفاوتی با Loader‌ها نخواهد داشت و دارای همان قسمت‌هایی است که برای Loader‌ها تعریف کردیم.
پیش از هر کاری ابتدا jsHint را نصب کرده و سپس loader آن را نیز نصب می‌کنیم. دستورات مورد نیاز، در ادامه آورده شده اند:
npm install -D jsHint jsHint-loader
سپس در ادامه به فایل پیکربندی وبپک مراجعه کرده و قسمت preLoader را به آن اضافه می‌کنیم:
module.exports = {
    entry:['./shared.js','./main.ts']
    ,output:{
        filename:'bundle.js'
    }
    ,watch :true
    ,module:{
        preLoaders:[
            {
                test:/\.js$/
                ,exclude:/node_modules/
                ,loader:'jshint-loader'
            }
        ],
        loaders:[
            {
                test:/\.ts$/
                ,exclude:/node_modules/
                ,loader:'ts-loader'
            }
        ]
    }
    
}

حال وب سرور وبپک را اجرا می‌کنیم. در خط فرمان نتیجه‌ی اجرا شدن jsHint در تصویر قابل مشاهده است که از دو فایل پروژه ایراد گرفته است:


همچنین برای تکمیل قابل ذکر است که وبپک دارای postLoaders نیز می‌باشد که پس از Loader‌های اصلی اجرا می‌شوند.
لیست کاملی از Loader‌ها را می‌توانید در اینجا مشاهده کنید .تمامی لودرهای وبپک

Minify کردن باندل‌ها با استفاده از وبپک

در صورتی که باندل ساخته شده تا به اینجای کار را باز کرده باشید، مشاهده کرده‌اید که باندل ساخته شده Minify شده نیست. ساده‌ترین روش جهت Minify کردن باندل ساخته شده در هنگام فراخوانی وبپک با استفاده از یک پرچم در خط فرمان می‌باشد. دستور مورد نیاز در ادامه آورده شده است.
// در حالتی که به صورت محلی وبپک نصب شده است
npm run webpack -- -p
// درحالتی که وبپک به صورت سراسری اجرا می‌شود
webpack -p
حال در صورتی که به باندل ساخته شده مراجعه کنید، باندل در حالت Minify شده قرار دارد.

اضافه کردن فایل پیکربندی مخصوص بیلد‌های اصلی پروژه

قطعا شما نیز در حین توسعه‌ی پروژه از دستورات لاگ یا اندازه گیری زمان اجرای یک قطعه کد و ... استفاده می‌کنید و حضور این کد‌ها در باندل نهایی دلیلی ندارد. یک راهکار این است که کدهایی را که فقط جهت توسعه‌ی پروژه و دیباگ بودند و سودی در نتیجه‌ی نهایی ندارند، به صورت دستی پاک کنیم و در صورتی که حجم این طور دستورات بالا باشند، قطعا کار جالبی نخواهد بود و همچنین حذف کلی این دستورات نیز در ادامه برای برگشت به پروژه ممکن است مشکل زا باشد.
راهکاری که با وبپک می‌توان پیش گرفت این است که از یک Loader جهت بیلد‌های اصلی استفاده کرده و مثلن تمامی کامنت‌ها و دستورات لاگ کننده و ... را از بیلد نهایی حذف کنیم. جهت اینکار یک فایل پیکربندی مخصوص را به بیلدهای اصلی به پروژه اضافه می‌کنیم و اسم فایل را webpack.prod.config.js می‌گذاریم.
قدم بعدی نصب یک loader می‌باشد که وظیفه‌ی حذف مواردی را دارد که برای آن مشخص خواهیم کرد. این لودر strip-loader نام دارد و با دستور زیر آن را در پروژه اضافه می‌کنیم.
npm install -D strip-loader
سپس وارد فایل پیکربندی که فقط جهت تولید باندل‌های اصلی پروژه ایجاد کرده‌ایم (webpack.prod.config.js) می‌شویم و کد‌های زیر را وارد می‌کنیم.
// webpack.prod.config.js
 //تنظیمات قبلی را می‌خوانیم
var devConfig = require("./webpack.config.js");
// لودری که وظیفه‌ی حذف کردن دارد را وارد می‌کنیم
var stripLoader = require("strip-loader");

// مانند قبل یک آبجکت با موارد مورد نظر برای لودر می‌سازیم
var stripLoaderConfig = {
    test:[/\.js$/,/\.ts$/],
    exclude :/node_modules/
    ,loader:stripLoader.loader("console.log")
}

// اضافه کردن به لیست لودرهای قبلی
devConfig.module.loaders.push(stripLoaderConfig);

// و در آخر اکسپورت کردن تنظیمات جدید وقبلی
module.exports = devConfig;
در توضیح کد‌های بالا در خط اول ابتدا فایل پیکربندی را که در توسعه‌ی عادی پروژه استفاده می‌شد، می‌خوانیم و در آبجکتی با نام devConfig ذخیره می‌کنیم. مزیت اینکار این می‌باشد که از تعریف تنظیمات تکراری و مورد نیاز جلوگیری می‌کند (نکته : اگر بخاطر داشته باشید در مطلب قبلی ذکر شد که فایل‌های پیکربندی در فرمت commonjs می‌باشند و در نتیجه امکان وارد کردن آنها با استفاده از تابع require امکان پذیر است).
خط بعدی نیز loader ی که وظیفه‌ی حذف موارد مورد نظر ما را دارد وارد می‌کنیم و در ادامه یک آبجکت را با تعاریفی که قبلن از لودر‌ها داشتیم می‌سازیم. تنها نکته در قسمت تعریف اسم لودر می‌باشد.
loader:stripLoader.loader("console.log")
در کد بالا مواردی را که مورد نیاز است از سورس اصلی در سورس نهایی حذف شود، به لودر معرفی می‌کنیم. در اینجا تمامی دستوراتی که شامل console.log می‌شوند از بیلد نهایی باندل حذف خواهند شد.
تنها مرحله‌ی باقی مانده، فراخوانی وبپک با استفاده از فایل پیکربندی جدید می‌باشد. برای اینکار با استفاده از پرچم config محل فایل جدید پیکربندی را به وبپک معرفی می‌کنیم تا از فایل پیکربندی پیش فرض قبلی استفاده نکند.
// در حالتی که وبپک به صورت محلی نصب شده است
npm run webpack -- --config webpack.prod.config.js -p
// در حالتی که وبپک به صورت سراسری نصب شده باشد
webpack --config webpack.prod.config.js -p
پس از اجرای این دستور و باز کردن صفحه‌ی index.html خواهید دید که پیغامی در کنسول مرورگر ظاهر نخواهد شد و دستورات console.log همگی حذف شده‌اند.
توجه داشته باشید که اگر برای ساخت باندل از وبپک استفاده کرده‌اید، برای میزبانی فایل‌های پروژه از وب سرور وبپک استفاده نکنید و از وب سروری دیگر (مانند http-server یا IIS و ...) استفاده کنید؛ چرا که باندل توسط وب سرور وبپک دوباره ساخته می‌شود و تغییرات از بین می‌روند. در صورتی که می‌خواهید از وب سرور وبپک برای میزبانی بیلد نهایی پروژه نیز استفاده کنید، فایل پیکربندی بیلد نهایی را نیز به وب سرور وبپک با استفاده از دستور زیر معرفی کنید:
// زمانی که وبپک به صورت محلی در پروژه نصب شده است
npm run webpackserver -- --config webpack.prod.config.js  -p
//در حالتی که وبپک به صورت گلوبال ( سراسری ) نصب می‌باشد
webpack-dev-server --config webpack.prod.config.js  -p

مدیریت فایل و فولدرها با استفاده وبپک
تا به اینجای کار اگر به ساختار چینش فایل‌ها در پروژه دقت کنید خواهید دید که همگی فایل‌ها در مسیر اصلی پروژه قرار دارند و این روش مناسبی برای مدیریت فایل‌ها و فولدرها در پروژه‌های واقعی و بزرگ نیست. در ادامه قصد داریم این مسئله را حل کرده و ساختار مشخصی را برای محل قرارگیری فایل‌های پروژه با کمک وبپک ایجاد کنیم.
در اولین قدم فولدری را برای اسکریپت‌ها با نام js ایجاد کرده و اسکریپت‌ها را به این فولدر انتقال می‌دهیم.
در قدم دوم برای فایل‌های استاتیک پروژه مانند صفحات html و ... فولدر دیگری را با نام assets ایجاد می‌کنیم و این گونه فایل‌ها را در آن قرار خواهیم داد.
تا اینجای کار در صورتی که وبپک را اجرا کنید، مسیرهای جدید را پیدا نخواهد کرد و دچار خطا خواهد شد. پس به فایل پیکربندی ( webpack.config.js ) مراجعه کرده و وبپک را از ساختار جدید پروژه خبردار می‌کنیم.
// new webpack.config.js file
//ماژول توکار نود جی اس
var path = require("path");

module.exports = {
    // مشخص کردن زمینه برای فایل‌های ورودی
    context:path.resolve("js"),
    entry:['./shared.js','./main.ts']
    ,output:{
       // مشخص کردن محل قرارگیری باندل ساخته شده
        path:path.resolve("build/js"),
      // درخواست از سمت چه مسیری برای باندل خواهد آمد ؟
        publicPath:"assets/js",
        filename:'bundle.js'
    }
    ,
    devServer:{
        //راهنمایی برای وب سرور جهت اینکه فایل‌ها را از چه محلی سرو کند
        contentBase:"assets"
    }

    ,watch :true
    ,module:{
        
        loaders:[
            {
                test:/\.ts$/
                ,exclude:/node_modules/
                ,loader:'ts-loader'
            }
        ]
    }
    
}
در خط اول فایل پیکربندی جدید، ماژول توکار path از نود جی اس را وارد می‌کنیم و سپس کلید جدیدی را به تنظیمات وبپک با نام context اضافه می‌کنیم که زمینه‌ی فایل‌های ورودی را مشخص خواهد کرد. قبلا ذکر شد که فولدری با نام js را ساخته و اسکریپت‌ها را در آن قرار می‌دهیم. پس با کمک ماژول path این مسیر را به وبپک معرفی می‌کنیم.
context:path.resolve("js")
تغییر بعدی را در تنظیمات برای ساخت باندل داریم که مشخص کردن مسیر قرارگیری جدید باندل می‌باشد که با کلید جدیدی با نام path، مسیر قرارگیری باندل پس از ساخته شدن را به وبپک اطلاع می‌دهیم و در ادامه کلید دیگری با نام publicPath اضافه شده که راهنمایی برای وب سرور وبپک می‌باشد تا با استفاده از آن درخواست‌هایی که به مسیر مشخص شده می‌آیند، از مسیری که در کلید path نامیده شده سرو شوند.
// تغییرات در شی output
// این کلید جدید مسیر قرار گیری جدید باندل را به وبپک اطلاع می‌دهد
path:path.resolve("build/js"),
//  راهنما برای وب سرور وبپک جهت میزبانی مسیر زیر از کلید بالا
publicPath:"assets/js",
آخرین تغییر در فایل پیکربندی، مربوط به اضافه شدن آبجکت جدید devServer می‌باشد که در آن کلیدی اضافه شده که مسیر اصلی فایل‌های میزبانی شده را اعلام می‌کند. به طور مثال فایل html اصلی پروژه را بالاتر اشاره کردیم که در این مسیر قرار می‌دهیم.
در نهایت وارد فایل index.html می‌شویم و مسیر جدید باندل را به آن معرفی میکنیم.
//index.html

<html>
    <head>
        first part of webpack tut!
    </head>
    <body>
        <h1>webpack is awesome !</h1>
        <script src="assets/js/bundle.js"></script>
    </body>
</html>
قابل مشاهده است که مسیر باندل ذکر شده در اینجا وجود خارجی ندارد و وبپک آن را با کمک تنظیماتش، به صورت پویا پیدا خواهد کرد. در تصویر زیر سعی بر روشن‌تر شدن این مسئله شده است.


حال با اجرا کردن وب سرور وبپک می‌توان مشاهده کرد که مسیرهای جدید، بدون مشکل توسط وبپک پیدا شده و فایل‌ها سرو می‌شوند. قابل توجه است که این نوع چینش پروژه قابل تغییر و شخصی سازی برای پروژه‌های گوناگون می‌باشد.


ساخت فایل‌های سورس مپ  (source map)


ساده‌ترین راه جهت ساخت فایل‌های سورس مپ با استفاده از یک پرچم در هنگام فراخوانی وبپک به صورت زیر می‌باشد.

// فعال کردن ساخت سورس مپ‌ها 
npm run webpack -- -d 
// یا  در هنگام نصب گلوبال
webpack -d
// جهت استفاده به همراه وب سرور
npm run webpackserver -- -d
// یا به صورت نصب گلوبال
webpack-dev-server -d
راه دوم با استفاده از انجام تغییرات در فایل پیکربندی وبپک می‌باشد؛ به این صورت که کلیدی را به این فایل اضافه می‌کنیم.
// webpack.config.js
// کلید جدید اضافه شده در فایل پیکربندی
devtool:"#source-map"
حال در هنگام فراخوانی وبپک فایل سورس مپ نیز ساخته خواهد شد و احتیاجی به استفاده از پرچم خط فرمان در هنگام فراخوانی نیست.
با اجرای وب سرور وبپک خواهید دید که سورس مپ‌ها در منوی توسعه دهنده‌ی مرورگر قابل دستیابی می‌باشند.

ساخت چندین باندل گوناگون

قصد داریم به پروژه، دو صفحه‌ی دیگر را نیز با نام‌های aboutme و contact اضافه کنیم. هر یک از این صفحات اسکریپت مخصوص به خود را خواهد داشت و باندل نهایی نیز شامل تمامی آنها خواهد شد. در صورتی که این خروجی مطلوب ما نباشد و به طور مثال بخواهیم مکانیزمی شبیه به lazy loading اسکریپت‌ها را داشته باشیم و فقط زمانی اسکریپت‌ها بارگذاری شوند که به آنها احتیاج باشد، برای انجام این کار با وبپک به صورت زیر عمل خواهیم کرد.
دو صفحه‌ی html جدید را با عناوین ذکر شده‌ی بالا به پوشه‌ی assets اضافه می‌کنیم و برای هریک نیز اسکریپتی با همان نام خواهیم ساخت و در پوشه‌ی js قرار می‌دهیم.
محتوای صفحات بدین شکل می‌باشد.
// index.html file
<html>
<head>

    <title>
        third part of webpack tut!
    </title>
</head>

<body>
    <nav>
        <a href="aboutme.html">about me</a>
        <a href="contact.html">contact</a>
    </nav>
    <h1>webpack is awesome !</h1>
    <script src="assets/js/shared.js"></script>
    <script src="assets/js/index.js"></script>
</body>

</html>

// aboutme.html file
<html>

<head>

    <title>
        about me page !
    </title>
</head>

<body>
    <nav>
        <a href="index.html">index</a>
        <a href="contact.html">contact</a>
    </nav>
    <h1>webpack is awesome !</h1>
    <script src="assets/js/shared.js"></script>
    <script src="assets/js/aboutme.js"></script>
</body>

</html>

// contact.html file

<html>

<head>

    <title>
        contact me page !
    </title>
</head>

<body>
    <nav>
        <a href="index.html">index</a>
        <a href="aboutme.html">about me</a>
    </nav>
    <h1>webpack is awesome !</h1>
    <script src="assets/js/shared.js"></script>
    <script src="assets/js/contact.js"></script>
</body>

</html>
در هر یک از صفحات یک اسکریپت مخصوص آن صفحه و همچنین یک اسکریپت با نام shared.js که قبلا فرض کردیم نقش ماژولی را دارد که در سرتاسر پروژه از آن استفاده می‌شود، اضافه شده است. حال این تغییرات را در فایل پیکربندی وبپک به آن معرفی می‌کنیم.
var path = require("path");
var webpack = require("webpack");
// وارد کردن پلاگینی از وب پک برای ساخت تکه‌های مختلف اسکریپت‌ها 
// معرفی اسکریپت shared.js
var commonChunkPlugin = new webpack.optimize.CommonsChunkPlugin("shared.js");
module.exports = {
    context:path.resolve("js"),
    //entry:['./shared.js','./main.ts']
   // معرفی اسکریپت‌های جدید به وبپک
    entry:{
        index:"./main.js",
        aboutme:"./aboutme.js",
        contact:"./contact.js"
    }
    ,output:{
        path:path.resolve("build/js"),
        publicPath:"assets/js",
     //   filename:'bundle.js'
     // به جای یک باندل کلی از وبپک میخاهیم برای هر ورودی باندلی جدید بسازد
        filename:"[name].js"
    }
    // رجیستر کردن پلاگین 
    ,plugins:[commonChunkPlugin]
    ,
    devServer:{
        contentBase:"assets"
    }
    //,devtool:"#source-map"
    ,watch :true
    ,module:{...
    }
    
}
جهت انجام این کار از یک پلاگین وبپک با نام CommonsChunkPlugin کمک گرفته‌ایم که به ما کمک می‌کند اسکریپت shared.js را به عنوان یک وابستگی در تمامی صفحاتمان داشته باشیم. نحوه‌ی کار این پلاگین نیز از نامش مشخص است و نقاط Common را می‌توان با آن مشخص کرد. تغییر بعدی نیز در کلید entry می‌باشد که اسکریپت‌های جدید را با استفاده از اسمشان به وبپک معرفی کرده‌ایم و سپس در کلید output نیز به وبپک خبر داده‌ایم که برای هر ورودی، باندل جداگانه‌ی خود را بسازد. تکه کد  filename:[name].js به وبپک می‌گوید که باندل‌های جداگانه، با نام خود اسکریپت ساخته شوند و در نهایت کلید جدید plugins به وبپک پلاگین CommonsChunkPlugin را اضافه می‌کند.
حال با اجرای وبپک می‌توان دید که سه باندل ساخته شده که همگی به اسکریپت shared.js وابستگی دارند و اگر این اسکریپت را از صفحات HTML حذف کنید، با خطا رو به رو خواهید شد. این پلاگین قدرت ساخت باندل‌هایی با خاصیت مشخص کردن وابستگی‌ها و همچنین تو در تویی‌ها خاص را نیز دارد. برای مطالعه‌ی بیشتر می‌توانید به اینجا مراجعه کنید: پلاگین commonsChunk

در قسمت بعدی با استفاده از وبپک فایل‌های css، فونت‌ها و تصاویر را نیز باندل خواهیم کرد.

فایل‌های مطلب:
سورس تا قبل از قسمت ایجاد تغییرات در ساختار فایل‌های پروژه :dntwebpack-part3-beforeFileAndFolderManagment.zip
سورس برای بعد از ایجاد تغییرات در ساختار فایل‌های پروژه : dntwebpack-part3AfterFileOrganization.zip

مطالب
Blazor 5x - قسمت دوم - بررسی ساختار اولیه‌ی پروژه‌های Blazor
پس از آشنایی با دو مدل هاستینگ برنامه‌های Blazor در قسمت قبل، اکنون می‌خواهیم ساختار ابتدایی قالب‌های این دو پروژه را بررسی کنیم.


ایجاد پروژه‌های خالی Blazor

در انتهای قسمت قبل، با روش ایجاد پروژه‌های خالی Blazor به کمک NET SDK 5x. آشنا شدیم. به همین جهت دو پوشه‌ی جدید BlazorWasmSample و BlazorServerSample را ایجاد کرده و از طریق خط فرمان و با کمک NET CLI.، در پوشه‌ی اولی دستور dotnet new blazorwasm و در پوشه‌ی دومی دستور dotnet new blazorserver را اجرا می‌کنیم.
البته اجرای این دو دستور، نیاز به اتصال به اینترنت را هم برای بار اول دارند؛ تا فایل‌های مورد نیاز و بسته‌های مرتبط را دریافت و restore کنند. بسته به سرعت اینترنت، حداقل یک ربعی را باید صبر کنید تا دریافت ابتدایی بسته‌های مرتبط به پایان برسد. برای دفعات بعدی، از کش محلی NuGet، برای restore بسته‌های blazor استفاده می‌شود که بسیار سریع است.


بررسی ساختار پروژه‌ی Blazor Server و اجرای آن

پس از اجرای دستور dotnet new blazorserver در یک پوشه‌ی خالی و ایجاد پروژه‌ی ابتدایی آن:


همانطور که مشاهده می‌کنید، ساختار این پروژه، بسیار شبیه به یک پروژه‌ی استاندارد ASP.NET Core از نوع Razor pages است.
- در پوشه‌ی properties آن، فایل launchSettings.json قرار دارد که برای نمونه، شماره پورت اجرایی برنامه را در حالت اجرای توسط دستور dotnet run و یا توسط IIS Express مشخص می‌کند.

- پوشه‌ی wwwroot آن، مخصوص ارائه‌ی فایل‌های ایستا مانند wwwroot\css\bootstrap است. در ابتدای کار، این پوشه به همراه فایل‌های CSS بوت استرپ است. در ادامه اگر نیاز باشد، فایل‌های جاوا اسکریپتی را نیز می‌توان به این قسمت اضافه کرد.

- در پوشه‌ی Data آن، سرویس تامین اطلاعاتی اتفاقی قرار دارد؛ به نام WeatherForecastService که هدف آن، تامین اطلاعات یک جدول نمایشی است که در ادامه در آدرس https://localhost:5001/fetchdata قابل مشاهده است.

- در پوشه‌ی Pages، تمام کامپوننت‌های Razor برنامه قرار می‌گیرند. یکی از مهم‌ترین صفحات آن، فایل Pages\_Host.cshtml است. کار این صفحه‌ی ریشه، افزودن تمام فایل‌های CSS و JS، به برنامه‌است. بنابراین در آینده نیز از همین صفحه برای افزودن فایل‌های CSS و JS استفاده خواهیم کرد. اگر به قسمت body این صفحه دقت کنیم، تگ جدید کامپوننت قابل مشاهده‌است:
<body>
   <component type="typeof(App)" render-mode="ServerPrerendered" />
کار آن، رندر کامپوننت App.razor واقع در ریشه‌ی پروژه‌است.
همچنین در همینجا، تگ‌های دیگری نیز قابل مشاهده هستند:
<body>
    <component type="typeof(App)" render-mode="ServerPrerendered" />

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_framework/blazor.server.js"></script>
</body>
همانطور که در قسمت قبل نیز عنوان شد، اتصال برنامه‌ی در حال اجرای در مرورگر blazor server با backend، از طریق یک اتصال دائم SignalR صورت می‌گیرد. اگر در این بین خطای اتصالی رخ دهد، div با id مساوی blazor-error-ui فوق، یکی از پیام‌‌هایی را که مشاهده می‌کنید، بسته به اینکه برنامه در حالت توسعه در حال اجرا است و یا در حالت ارائه‌ی نهایی، نمایش می‌دهد. افزودن مدخل blazor.server.js نیز به همین منظور است. کار آن مدیریت اتصال دائم SignalR، به صورت خودکار است و از این لحاظ نیازی به کدنویسی خاصی از سمت ما ندارد. این اتصال، کار به روز رسانی UI و هدایت رخ‌دادها را به سمت سرور، انجام می‌دهد.

- در پوشه‌ی Shared، یکسری فایل‌های اشتراکی قرار دارند که قرار است در کامپوننت‌های واقع در پوشه‌ی Pages مورد استفاه قرار گیرند. برای نمونه فایل Shared\MainLayout.razor، شبیه به master page برنامه‌های web forms است که قالب کلی سایت را مشخص می‌کند. داخل آن Body@ را مشاهده می‌کنید که به معنای نمایش صفحات دیگر، دقیقا در همین محل است. همچنین در این پوشه فایل Shared\NavMenu.razor نیز قرار دارد که ارجاعی به آن در MainLayout.razor ذکر شده و کار آن نمایش منوی آبی‌رنگ سمت چپ صفحه‌است.

- در پوشه‌ی ریشه‌ی برنامه، فایل Imports.razor_ قابل مشاهده‌است. مزیت تعریف usingها در اینجا این است که از تکرار آن‌ها در کامپوننت‌های razor ای که در ادامه تهیه خواهیم کرد، جلوگیری می‌کند. هر using تعریف شده‌ی در اینجا، در تمام کامپوننت‌ها، قابل دسترسی است؛ به آن global imports هم گفته می‌شود.

- در پوشه‌ی ریشه‌ی برنامه، فایل App.razor نیز قابل مشاهده‌است. کار آن تعریف قالب پیش‌فرض برنامه‌است که برای مثال به Shared\MainLayout.razor اشاره می‌کند. همچنین کامپوننت مسیریابی نیز در اینجا ذکر شده‌است:
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>
اگر شخصی مسیر از پیش تعریف شده‌ای را وارد کند، به قسمت Found وارد می‌شود و در غیر اینصورت به قسمت NotFound. در قسمت Found است که از قالب MainLayout برای رندر یک کامپوننت توسط کامپوننت RouteView، استفاده خواهد شد و در قسمت NotFound، فقط پیام «Sorry, there's nothing at this address» به کاربر نمایش داده می‌شود. قالب‌های هر دو نیز قابل تغییر و سفارشی سازی هستند.

- فایل appsettings.json نیز همانند برنامه‌های استاندارد ASP.NET Core در اینجا مشاهده می‌شود.

- فایل Program.cs آن که نقطه‌ی آغازین برنامه‌است و کار فراخوانی Startup.cs را انجام می‌دهد، دقیقا با یک فایل Program.cs برنامه‌ی استاندارد ASP.NET Core یکی است.

- در فایل Startup.cs آن، همانند قبل دو متد تنظیم سرویس‌ها و تنظیم میان‌افزارها قابل مشاهده‌است.
public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton<WeatherForecastService>();
}
در ConfigureServices آن، سرویس‌های صفحات razor و ServerSideBlazor اضافه شده‌اند. همچنین سرویس نمونه‌ی WeatherForecastService نیز در اینجا ثبت شده‌است.
قسمت‌های جدید متد Configure آن، ثبت مسیریابی توکار BlazorHub است که مرتبط است با اتصال دائم SignalR برنامه و اگر مسیری پیدا نشد، به Host_ هدایت می‌شود:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...

    app.UseEndpoints(endpoints =>
    {
       endpoints.MapBlazorHub();
       endpoints.MapFallbackToPage("/_Host");
    });
}
در ادامه در همان پوشه، دستور dotnet run را اجرا می‌کنیم تا پروژه، کامپایل و همچنین وب سرور آن نیز اجرا شده و برنامه در آدرس https://localhost:5001 قابل دسترسی شود.


که به همراه 13 درخواست و نزدیک به 600 KB دریافت اطلاعات از سمت سرور است.

این برنامه‌ی نمونه، به همراه سه صفحه‌ی نمایش Home، نمایش یک شمارشگر و نمایش اطلاعاتی از پیش آماده شده‌است. اگر صفحه‌ی شمارشگر آن‌را باز کنیم، با کلیک بر روی دکمه‌ی آن، هرچند مقدار current count افزایش می‌یابد، اما شاهد post-back متداولی به سمت سرور نیستیم و این صفحه بسیار شبیه به صفحات برنامه‌های SPA (تک صفحه‌ای وب) به نظر می‌رسد:


همانطور که عنوان شد، مدخل blazor.server.js فایل Pages\_Host.cshtml، کار به روز رسانی UI و هدایت رخ‌دادها را به سمت سرور به صورت خودکار انجام می‌دهد. به همین جهت است که post-back ای را مشاهده نمی‌کنیم و برنامه، شبیه به یک برنامه‌ی SPA به نظر می‌رسد؛ هر چند تمام رندرهای آن سمت سرور انجام می‌شوند و توسط SignalR به سمت کلاینت بازگشت داده خواهند شد.
برای نمونه اگر بر روی دکمه‌ی شمارشگر کلیک کنیم، در برگه‌ی network مرورگر، هیچ اثری از آن مشاهده نمی‌شود (هیچ رفت و برگشتی را مشاهده نمی‌کنیم). علت اینجا است که اطلاعات متناظر با این کلیک، از طریق web socket باز شده‌ی توسط SignalR، به سمت سرور ارسال شده و نتیجه‌ی واکنش به این کلیک‌ها و رندر HTML نهایی سمت سرور آن، از همین طریق به سمت کلاینت بازگشت داده می‌شود.


بررسی ساختار پروژه‌ی Blazor WASM و اجرای آن

پس از اجرای دستور dotnet new blazorwasm در یک پوشه‌ی خالی و ایجاد پروژه‌ی ابتدایی آن:


همان صفحات پروژه‌ی خالی Blazor Server در اینجا نیز قابل مشاهده هستند. این برنامه‌ی نمونه، به همراه سه صفحه‌ی نمایش Home، نمایش یک شمارشگر و نمایش اطلاعاتی از پیش آماده شده‌است. صفحات و کامپوننت‌های پوشه‌های Pages و Shared نیز دقیقا همانند پروژه‌ی Blazor Server قابل مشاهده هستند. مفاهیمی مانند فایل‌های Imports.razor_ و App.razor نیز مانند قبل هستند.

البته در اینجا فایل Startup ای مشاهده نمی‌شود و تمام تنظیمات آغازین برنامه، داخل فایل Program.cs انجام خواهند شد:
namespace BlazorWasmSample
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");

            builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

            await builder.Build().RunAsync();
        }
    }
}
در اینجا ابتدا کامپوننت App.razor را به برنامه معرفی می‌کند که ساختار آن با نمونه‌ی مشابه Blazor Server دقیقا یکی است. سپس سرویسی را برای دسترسی به HttpClient، به سیستم تزریق وابستگی‌های برنامه معرفی می‌کند. هدف از آن، دسترسی ساده‌تر به endpoint‌های یک ASP.NET Core Web API است. از این جهت که در یک برنامه‌ی سمت کلاینت، دیگر دسترسی مستقیمی به سرویس‌های سمت سرور را نداریم و برای کار با آن‌ها همانند سایر برنامه‌های SPA که از Ajax استفاده می‌کنند، در اینجا از HttpClient برای کار با Web API‌های مختلف استفاده می‌شود.

تفاوت ساختاری دیگر این پروژه‌ی WASM، با نمونه‌ی Blazor Server، ساختار پوشه‌ی wwwroot آن است:


که به همراه فایل جدید نمونه‌ی wwwroot\sample-data\weather.json است؛ بجای سرویس متناظر سمت سرور آن در برنامه‌ی blazor server. همچنین فایل جدید wwwroot\index.html نیز قابل مشاهده‌است و محتوای تگ body آن به صورت زیر است:
<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
</body>
- چون این برنامه، یک برنامه‌ی سمت کلاینت است، اینبار بجای فایل Host_ سمت سرور، فایل index.html سمت کلاینت را برای ارائه‌ی آغازین برنامه داریم که وابستگی به دات نت ندارد و توسط مرورگر قابل درک است.
- در ابتدای بارگذاری برنامه، یک loading نمایش داده می‌شود که در اینجا نحوه‌ی تعریف آن مشخص است. همچنین اگر خطایی رخ دهد نیز توسط div ای با id مساوی blazor-error-ui اطلاع رسانی می‌شود.
- مدخل blazor.webassembly.js در اینجا، کار دریافت وب اسمبلی و فایل‌های NET runtime. را انجام می‌دهد؛ برخلاف برنامه‌های Blazor Server که توسط فایل blazor.server.js، یک ارتباط دائم SignalR را با سرور برقرار می‌کردند تا کدهای رندر شده‌ی سمت سرور را دریافت و نمایش دهند و یا اطلاعاتی را به سمت سرور ارسال کنند: برای مثال بر روی دکمه‌ای کلیک شده‌است، اطلاعات مربوطه را در سمت سرور پردازش کن و نتیجه‌ی نهایی رندر شده را بازگشت بده. اما در اینجا همه چیز داخل مرورگر اجرا می‌شود و برای این نوع اعمال، رفت و برگشتی به سمت سرور صورت نمی‌گیرد. به همین جهت تمام کدهای #C ما به سمت کلاینت ارسال شده و داخل مرورگر به کمک فناوری وب اسمبلی، اجرا می‌شوند. در اینجا از لحاظ ارسال تمام کدهای مرتبط با UI برنامه‌ی سمت کلاینت به مرورگر کاربر، تفاوتی با فریم‌ورک‌هایی مانند Angular و یا React نیست و آن‌ها هم تمام کدهای UI برنامه را کامپایل کرده و یکجا ارسال می‌کنند.

در ادامه در همان پوشه، دستور dotnet run را اجرا می‌کنیم تا پروژه کامپایل و همچنین وب سرور آن نیز اجرا شده و برنامه در آدرس https://localhost:5001 قابل دسترسی شود.


که به همراه 205 درخواست و نزدیک به 9.6 MB دریافت اطلاعات از سمت سرور است. البته اگر همین صفحه را refresh کنیم، دیگر شاهد دریافت مجدد فایل‌های DLL مربوط به NET Runtime. نخواهیم بود و اینبار از کش مرورگر خوانده می‌شوند:


در این برنامه‌ی سمت کلاینت، ابتدا تمام فایل‌های NET Runtime. و وب اسمبلی دریافت شده و سپس اجرای تغییرات UI، در همین سمت و بدون نیاز به اتصال دائم SignalR ای به سمت سرور، پردازش و نمایش داده می‌شوند. به همین جهت زمانیکه بر روی دکمه‌ی شمارشگر آن کلیک می‌کنیم، اتفاقی در برگه‌ی network مرورگر ثبت نمی‌شود و رفت و برگشتی به سمت سرور صورت نمی‌گیرد.

عدم وجود اتصال SignalR، مزیت امکان اجرای آفلاین برنامه‌ی WASM را نیز میسر می‌کند. برای مثال یکبار دیگر همان برنامه‌ی Blazor Server را به کمک دستور dotnet run اجرا کنید. سپس آن‌را در مرورگر در آدرس https://localhost:5001 باز کنید. اکنون پنجره‌ی کنسولی که dotnet run را اجرا کرده، خاتمه دهید (قسمت اجرای سمت سرور آن‌را ببندید).


بلافاصله تصویر «سعی در اتصال مجدد» فوق را مشاهده خواهیم کرد که به دلیل قطع اتصال SignalR رخ داده‌است. یعنی یک برنامه‌ی Blazor Server، بدون این اتصال دائم، قادر به ادامه‌ی فعالیت نیست. اما چنین محدودیتی با برنامه‌های Blazor WASM وجود ندارد.
البته بدیهی است اگر یک Web API سمت سرور برای ارائه‌ی اطلاعاتی به برنامه‌ی WASM نیاز باشد، API سمت سرور برنامه نیز باید جهت کار و نمایش اطلاعات در سمت کلاینت در دسترس باشد؛ وگرنه قسمت‌های متناظر با آن، قادر به نمایش و یا ثبت اطلاعات نخواهند بود.
مطالب
چرا TypeScript؟
زبان TypeScript به عنوان superset زبان JavaScript ارائه شده‌است و هدف آن، strong typing و ارائه‌ی قابلیت‌های پیشرفته‌ی زبان‌های شیءگرا، جهت نوشتن برنامه‌های کلاینت و سرور، با کمترین میزان خطاها است. زبان TypeScript چندسکویی و سورس باز است و در نهایت به نگارشی از JavaScript کامپایل می‌شود که با تمام مرورگرهای فعلی سازگاری دارد و یا در سمت سرور بدون مشکلی توسط NodeJS قابل درک است.
- TypeScript زبان توصیه شده‌ی توسعه‌ی برنامه‌های AngularJS 2 است و همچنین با سایر کتابخانه‌های معروف جاوا اسکریپتی مانند ReactJS و jQuery نیز سازگاری دارد. بنابراین اگر قصد دارید به AngularJS 2 مهاجرت کنید، اکنون فرصت خوبی است تا زبان TypeScript را نیز بیاموزید. همچنین WinJS نیز با TypeScript نوشته شده‌است.
- superset زبان JavaScript بودن به این معنا است که تمام کدهای جاوا اسکریپتی موجود، به عنوان کد معتبر TypeScript نیز شناخته می‌شوند و همین مساله مهاجرت به آن‌را ساده‌تر می‌کند. زبان‌های دیگری مانند Dart و یا CoffeeScript ، نسبت به JavaScript بسیار متفاوت به نظر می‌رسند؛ اما Syntax زبان TypeScript شباهت بسیار زیادی به جاوا اسکریپت و خصوصا ES 6 دارد. در اینجا تنها کافی است پسوند فایل‌های js را به ts تغییر دهید و از آن‌ها به عنوان کدهای معتبر TypeScript استفاده کنید.
- strong typing و معرفی نوع‌ها، کدهای نهایی نوشته شده را امن‌تر می‌کنند. به این ترتیب کامپایلر، پیش از اینکه کدهای شما در زمان اجرا به خطا بر بخورند، در زمان کامپایل، مشکلات موجود را گوشزد می‌کند. همچنین وجود نوع‌ها، سرعت توسعه را با بهبود ابزارهای مرتبط با برنامه نویسی، افزایش می‌دهند؛ از این جهت که مفهوم مهمی مانند Intellisense، با وجود نوع‌ها، پیشنهادهای بهتر و دقیق‌تری را ارائه می‌دهد. همچنین ابزارهای Refactoring نیز در صورت وجود نوع‌ها بهتر و دقیق‌تر عمل می‌کنند. این موارد مهم‌ترین دلایل طراحی TypeScript جهت توسعه و نگهداری برنامه‌های بزرگ نوشته شده‌ی با JavaScript هستند.
- Syntax زبان TypeScript به شدت الهام گرفته شده از زبان سی‌شارپ است. به همین جهت اگر با این زبان آشنایی دارید، درک مفاهیم TypeScript برایتان بسیار ساده خواهد بود.
- بهترین قسمت TypeScript، کامپایل شدن آن به ES 5 است (به این عملیات Transpile هم می‌گویند). در زبان TypeScript به تمام امکانات پیشرفته‌ی ES 6 مانند کلاس‌ها و ماژول‌ها دسترسی دارید، اما کد نهایی را که تولید می‌کند، می‌تواند ES 5 ایی باشد که هم اکنون تمام مرورگرهای عمده آن‌را پشتیبانی می‌کنند. با تنظیمات کامپایلر TypeScript، امکان تولید کدهای ES 3 تا ES 5 و همچنین ES 6 نیز وجود دارد. نمونه‌ی آنلاین این ترجمه را در TypeScript playground می‌توانید مشاهده کنید.
- TypeScript چندسکویی است. امکانات و کامپایلر این زبان، برای ویندوز، مک و لینوکس طراحی شده‌اند.
- TypeScript سورس باز است. طراحان اصلی آن، همان طراحان زبان سی‌شارپ در مایکروسافت هستند و هم اکنون این زبان به صورت سورس باز توسط این شرکت توسعه داده شده و در GitHub نگهداری می‌شود.


آماده سازی محیط‌های کار با TypeScript

برای کار با TypeScript، یک ادیتور متنی ساده، به همراه کامپایلر آن کفایت می‌کند. اما همانطور که عنوان شد، یکی از مهم‌ترین دلایل وجودی TypeScript، بهبود ابزارهای برنامه نویسی مرتبط با JavaScript است و اگر قرار باشد صرفا از یک ادیتور متنی ساده استفاده شود، فلسفه‌ی وجودی آن زیر سؤال می‌رود.

نصب TypeScript در ویژوال استودیو

در نگارش‌های جدید ویژوال استودیو، از VS 2013 Update 2 به بعد، قسمت ویژه‌ی TypeScript نیز قابل مشاهده‌است. البته این قسمت با به روز رسانی‌های TypeScript، نیاز به به روز رسانی دارد. به همین جهت به سایت رسمی آن مراجعه کرده و بسته‌های جدید مخصوص VS 2013 و یا 2015 آن‌را دریافت و نصب کنید.


همچنین افزونه‌ی Web Essentials نیز امکانات بیشتری را جهت کار با TypeScript به همراه دارد و امکان مشاهده‌ی خروجی جاوا اسکریپت تولیدی را در حین کار با فایل TypeScript فعلی میسر می‌کند. در سمت چپ صفحه TypeScript را خواهید نوشت و در سمت راست، خروجی JavaScript نهایی را بلافاصله مشاهده می‌کنید.


تصویر فوق مربوط به VS 2015 است. همچنین گزینه‌ی افزودن یک فایل و آیتم جدید نیز امکان افزودن فایل‌های TS را به همراه دارد.


نصب و تنظیم TypeScript در ویژوال استودیو کد

ویژوال استودیو کد، نگارش رایگان، سورس باز و چندسکویی ویژوال استودیو است که بر روی ویندوز، مک و لینوکس قابل اجرا است. ویژوال استودیو کد نیز به همراه پشتیبانی بسیار خوبی از TypeScript است، تا حدی که تمام ارائه‌های معرفی Anugular 2 توسط تیم مربوطه‌ی آن از گوگل، توسط ویژوال استودیو کد و یکپارچگی آن با TypeScript انجام شدند.


ویژوال استودیو کد بر مبنای فولدرها کار می‌کند و با گشودن یک پوشه در آن (با کلیک بر روی دکمه‌ی open folder آن)، امکان کار کردن با آن پوشه و فایل‌های موجود در آن را خواهیم یافت.
نکته‌ی مهم اینجا است که پس از نصب VS Code، برای فایل‌های با پسوند ts بلافاصله Intellisense مرتبط نیز مهیا است و نیاز به هیچگونه تنظیم اضافه‌تری ندارد. همچنین قابلیت‌های type safety این زبان نیز در این ادیتور به نحو واضحی مشخص هستند:


در ادامه ابتدا یک پوشه‌ی جدید خالی را ایجاد کنید و سپس این پوشه را در VS Code باز نمائید (از طریق منوی فایل، گزینه‌ی گشودن پوشه). سپس ماوس را بر روی نام این پوشه حرکت دهید:


همانطور که مشاهده می‌کنید، دکمه‌ی new file ظاهر می‌شود. در اینجا می‌توانید فایل جدیدی را به نام test.ts اضافه کنید.
در ادامه با فشردن دکمه‌های ctrl+shift+p، امکان انتخاب یک task runner را جهت کامپایل فایل‌های ts خواهیم داشت:


در اینجا ابتدا عبارت task< را وارد کنید و سپس از منوی باز شده، گزینه‌ی rub build task را انتخاب کنید:


پس از آن، در بالای صفحه مشاهده خواهید کرد که عنوان شده: «هنوز هیچ task runner ایی برای اینکار تنظیم نشده‌است»


برای این منظور بر روی دکمه‌ی configure task runner تصویر فوق که با رنگ آبی مشخص شده‌است، کلیک کنید. به این ترتیب یک فایل جدید به نام task.json ایجاد می‌شود که در پوشه‌ای به نام vscode. در ریشه‌ی پروژه (یا همان پوشه‌ی جاری) قرار می‌گیرد:


فایل task.json دارای تعاریفی است که کامپایلر TypeScript یا همان tsc را فعال می‌کند:
{
"version": "0.1.0",

// The command is tsc. Assumes that tsc has been installed using npm install -g typescript
"command": "tsc",

// The command is a shell script
"isShellCommand": true,

// Show the output window only if unrecognized errors occur.
"showOutput": "silent",

// args is the HelloWorld program to compile.
"args": ["HelloWorld.ts"],

// use the standard tsc problem matcher to find compile problems
// in the output.
"problemMatcher": "$tsc"
}
محتوای پیش فرض و ابتدایی این فایل را در قطعه کد فوق مشاهده می‌کنید. این فایل json را جهت تنظیمات کامپایلر TypeScript پروژه‌ی جاری، ویرایش خواهیم کرد. در این فایل دکمه‌ی ctrl+space را بفشارید. بلافاصله منوی تکمیل کننده‌ی این فایل ظاهر می‌شود. از ترکیب ctrl+space در قسمت‌های مختلف این فایل جهت دریافت توصیه‌های بیشتری نیز می‌توان استفاده کرد.
در اینجا قسمتی که نیاز به تنظیم دارد، خاصیت args است. مقادیر آن، پارامترهایی هستند که به کامپایلر typescript ارسال می‌شوند. برای نمونه آن‌را به صورت ذیل تغییر دهید:
"args": [
         "--target", "ES5",
         "--outdir", "js",
         "--sourceMap",
         "--watch",
         "test.ts"
    ],
پارامتر و سوئیچ target مشخص می‌کند که خروجی تولیدی باید با فرمت ES 5 باشد. همچنین فایل‌های js تولیدی را در پوشه‌ی js در ریشه‌ی پروژه یا پوشه‌ی جاری قرار دهد. پارامتر sourceMap مشخص می‌کند که علاوه بر فایل‌های js، فایل‌های map که بیانگر نگاشت بین فایل‌های ts و js هستند نیز تولید شوند. این فایل‌ها برای دیباگ برنامه بسیار مفید هستند. پارامتر watch، کلیه‌ی تغییرات پوشه‌ی جاری را تحت نظر قرار داده و به صورت خودکار کار کامپایل را انجام می‌دهد. در آخر نیز فایل و یا فایل‌های ts مدنظر ذکر می‌شوند.
برای اجرای کامپایلر، ابتدا از منوی view گزینه‌ی toggle output را انتخاب کنید تا بتوان خروجی نهایی کامپایلر را مشاهده کرد. سپس گزینه‌ی view->command pallet و اجرا tasks< را انتخاب کنید. در ادامه همانند مرحله‌ی قبل، یعنی گزینه‌ی run build task را اجرا کنید (که خلاصه‌ی این عملیات ctrl+shift+B است).

به این ترتیب پوشه‌ی js که در خاصیت args مشخص کردیم، تولید می‌شود:


البته این خطا هم در قسمت output نمایش داده می‌شود:
 error TS5023: Unknown option 'watch'
Use the '--help' flag to see options.

علت اینجا است که در تنظیمات فوق، خاصیت command به tsc تنظیم شده‌است و همانطور که در کامنت آن عنوان شده‌است، کامپایلر typescript را از طریق دستور npm install -g typescript دریافت می‌کند و نیازی به ذکر مسیر آن در اینجا نیست. بنابراین لازم است تا با npm و نصب typescript از طریق آن آشنا شد و به این ترتیب کامپایلر آن‌را به روز کرد تا دستور watch را شناسایی کند.


نصب TypeScript از طریق npm

همانطور که عنوان شد، TypeScript چندسکویی است و این مورد را از طریق npm یا NodeJS package manager انجام می‌دهد. برای این منظور به آدرس https://nodejs.org/en   مراجعه کرده و فایل نصاب آن‌را مخصوص سیستم عامل خود دریافت و سپس نصب کنید. Node.js یک runtime سمت سرور اجرای برنامه‌های جاوا اسکریپتی است. از آنجائیکه TypeScript در نهایت به JavaScript تبدیل می‌شود، استفاده از node.js انتخاب مناسبی جهت اجرا و توزیع آن در تمام سیستم عامل‌ها بوده‌است.
پس از نصب node.js، از package manager آن که npm نام دارد، جهت نصب TypeScript استفاده می‌شود. چون node.js به Path و مسیرهای اصلی ویندوز اضافه می‌شود، تنها کافی است دستور npm install -g typescript را در خط فرمان صادر کنید. در اینجا سوئیچ g به معنای global و دسترسی عمومی است.


همانطور که در این تصویر مشخص است، پس از صدور دستور نصب TypeScript، نگارش 1.8.9 آن نصب شده‌است. اما زمانیکه کامپایلر tsc را با پارامتر version اجرا می‌کنیم، شماره نگارش قدیمی 1.0.3.0 را نمایش می‌دهد. برای رفع این مشکل به مسیر C:\Program Files (x86)\Microsoft SDKs\TypeScript مراجعه کرده و پوشه‌ی 1.0 را به 1.0-old تغییر نام دهید.


اکنون اگر مجددا بررسی کنیم، نگارش صحیح قابل مشاهده است:


پس از این تغییرات اگر مجددا به VS Code باز گردیم و ctrl+shift+B را صادر کنیم (جهت اجرای مجدد task runner و اجرای tsc تنظیم شده) ، پیام ذیل مشاهده می‌شود:
 15:33:52 - Compilation complete. Watching for file changes.
به این معنا که اینبار پارامتر watch را شناسایی کرده‌است و دیگر از کامپایلر قدیمی tsc استفاده نمی‌کند. برای آزمایش آن، از منوی view گزینه‌ی split editor را انتخاب کنید و سپس در سمت چپ فایل test.ts و در سمت راست، فایل test.js کامپایل شده را باز کنید:


در اینجا چون پارامتر watch فعال شده‌است، هر تغییری که در فایل ts داده شود، بلافاصله کامپایل شده و در فایل js منعکس خواهد شد.


تنظیم VS Code جهت دیباگ کدهای TypeScript

در نوار ابزار کنار صفحه‌ی VS Code، بر روی دکمه‌ی دیباگ کلیک کنید:


سپس بر روی دکمه‌ی چرخ‌دنده‌ی موجود که کار انجام تنظیمات را توسط آن می‌توان ادامه داد، کلیک کنید. بلافاصله منویی ظاهر می‌شود که درخواست انتخاب محیط دیباگ را دارد:


در اینجا node.js را انتخاب کنید. با اینکار فایل جدیدی دیگری به نام launch.json به پوشه‌ی vscode. اضافه می‌شود. اگر به این فایل دقت کنید دو خاصیت name به نام‌های Launch و Attach در آن موجود هستند. این نام‌ها در یک دراپ داون، در کنار دکمه‌ی start دیباگ نیز ظاهر می‌شوند:


- در فایل launch.json، باید خاصیت "program": "${workspaceRoot}/app.js" را ویرایش کرد و app.js آن‌را به test.ts مثال جاری تغییر داد.
- سپس خاصیت "sourceMaps" آن نیز باید تغییر کرده و جهت استفاده‌ی از source mapهای تولیدی به true تنظیم شود.
- در آخر باید مسیر پوشه‌ی خروجی js را نیز تنظیم کرد: "outDir": "${workspaceRoot}/js"
همچنین باید دقت داشت چون externalConsole به false تنظیم شده‌است، خروجی این کنسول به output ویژوال استودیوکد منتقل می‌شود.

اکنون اگر بر روی دکمه‌ی سبز رنگ start کلیک کنید (دکمه‌ی F5)، امکان دیباگ سطر به سطر کد TypeScript را خواهید یافت:



فایل‌های نهایی json یاد شده‌ی در متن را از اینجا می‌توانید دریافت کنید:
 VSCodeTypeScript.zip
اشتراک‌ها
پروژه TypeC

A Simple .NET Type Container 

پروژه TypeC