ساخت یک صفحه وب و راه اندازی آنلاین با سرویس رایگان GitHub Pages

ساخت صفحه وب و آپلود آن

یکی از بهترین راه‌های یادگیری برنامه نویسی، تمرین کدنویسی با ساخت پروژه‌های واقعی است. در این مطلب ساخت و راه اندازی یک صفحه وب (بازی دوز) به صورت قدم به قدم آموزش داده شده است. در ادامه باهم یک صفحه وب استاتیک (فقط فرانت اند) درست کرده و با استفاده از سرویس github-pages که کاملا رایگان است، صفحه را آپلود و آنلاین خواهیم کرد.

نتیجه نهایی و راه اندازی شده در گیت هاب را از این لینک یا در کدباکس زیر ببینید:

برای شروع اول باید یک ویرایشگر کد مثل Visual Studio Code داشته باشیم. سپس با استفاده از HTML و CSS و JavaScript صفحه را کدنویسی کرده و در نهایت با استفاده از Git که برای نگهداری و آپلود/دانلود کد استفاده می شود، کد ها را روی گیت‌هاب قرار دهیم. این‌کار بسیار راحت‌تر از چیزی است که به نظر می‌رسد.

این مقاله بخش چهارم و پایانی از سری مقالات شروع کد‌نویسی وب است. مقالات دیگر در این سری شامل این موارد هستند:

  1. آشنایی با HTML زبان نشانه گذاری ابرمتن

  2. آشنایی با CSS برگه های استایل آبشاری

  3. آشنایی با JavaScript زبان برنامه نویسی وب

  4. ساخت یک صفحه وب و راه اندازی آنلاین آن (اینجایید)

ویرایشگر ویژوال استودیو کد یا VSCode

ویژوال استودیو کد یا VSCode بنظر من یکی بهترین ویرایشگر‌های کد است. این ادیتور بسیار سبک بوده و میلیون ها افزونه توسط افراد مختلف برای اضافه کردن قابلیت‌های بیشتر به آن نوشته شده است. این ویرایشگر رایگان را از طریق لینک زیر می‌توانید برای سیستم عامل‌های مختلف دانلود نمایید:

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

برای نصب live server بعد از باز کردن vscode وارد پنل extensions شده و اسم این افزونه را جستجو کنید:

سپس روی دکمه install کلیک کنید تا به ادیتور شما اضافه شود. در ادامه نحوه استفاده از آن را نیز بررسی خواهیم کرد.

گیت (Git)، سیستم کنترل نسخه

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

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

شروع پروژه و آماده سازی کد بیس

برای شروع پروژه اول یک فولدر جدید مثلا با اسم simple-web-page ایجاد می‌کنیم. سپس این فولدر را با vscode باز کرده (از طریق راست کلیک روی فولدر یا از قسمت File در خود ادیتور) و داخلش 3 فایل index.html و app.css و app.js را ایجاد می‌کنیم:

صفحه‌ای که خواهیم ساخت یک بازی دوز است. ساخت این بازی می‌تواند چالش خوبی برای تمرین سه بخش مهم یک صفحه وب، یعنی HTML و CSS و JavaScript باشد. ابتدا از HTML و ساختار صفحه شروع می‌کنیم، سپس CSS و ظاهر صفحه را درست می‌کنیم و در نهایت عملکرد بازی را با JavaScript کدنویسی می‌کنیم.

کدنویسی ساختار صفحه با HTML

درصورتی که با کد HTML آشنا نیستید ابتدا مقاله آشنایی با HTML را مطالعه کنید. یکی از بهترین ویژگی‌های ویرایشگر کد، تکمیل خودکار کد است. برای مثال داخل فایل HTML با نوشتن علامت تعجب (و زدن اینتر) کد‌های اولیه که در اکثر صفحات HTML استفاده می‌شوند، برای ما اضافه خواهند شد:

اگر برای شما این تکمیل خودکار انجام نشد، داخل تنظیمات VSCode مطمئن شوید که Emmet فعال است. Emmet مجموعه‌ای از مخفف‌های این چنینی است که برای کوتاه کردن تایپ کدهای طولانی استفاده می‌شود.

برای مثال با نوشتن عبارت ul*2>li*5 و زدن اینتر یا تب، این کد اضافه می‌شود:

همه مخفف‌های Emmet به صورت آنلاین قابل مشاهده هستند که پیشنهاد می‌کنم سر فرصت بررسی کنید.

<ul>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>
<ul>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

خب بریم سراغ صفحه، ابتدا عنوان صفحه رو داخل <head> تغییر بدیم به:

<head>
  ...
  <title>بازی دوز آنلاین</title>
</head>

و بعدش یه تیتر و یه عکس به <body> اضافه کنیم:

<body>
  <h1>بازی دوز آنلاین</h1>
  <img src="banner.png" alt="دوز">
</body>

یه عکس با اسم banner.png هم باید کنار بقیه فایل هامون داخل سورس پروژه قرار بدیم.

برای بازی دوز یه صفحه با 9 تا خونه هم لازم داریم. پس در نهایت کد HTML ما می تونه یه همچین چیزی باشه:

<!DOCTYPE html>
<html lang="fa">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>بازی دوز آنلاین</title>
</head>
<body>
  
  <h1>بازی دوز آنلاین</h1>
  <img src="banner.png" alt="دوز">

  <div class="board">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>

</body>
</html>

یه کلاس به اسم board دادم که بعدا با CSS چیدمان خونه‌های دوز رو درست کنم. تا اینجا ساختار صفحه رو با HTML تونستیم درست کنیم. حالا بیاین با افزونه Live Server که برای VSCode نصب کردیم، صفحه رو توی مرورگر باز کنیم. برای اینکار کافیه بخش دستورات یا Command Palette رو با کلید های ctrl + shift + p یا از طریق منوی View باز کرده و بنویسیم open with live server و اینتر بزنیم تا لایو سرور اجرا بشه. به طور خودکار صفحه رو با مرورگر باز می کنه ولی اگه نکرد هم به پورتش دقت کنید و به صورت دستی آدرسشو تو مرورگر بزنید:

http://127.0.0.1:5500/index.html ( اخرش اسم فایلمون هست index.html )

اینطوری ساید بای ساید مرورگر و VSCode رو قرار بدیم که راحتتر هر تغییری میدیم توی صفحه ببینیم:

همونطور که نمی بینید اون قسمت دوز هم که با 9 تا div خالی ایجاد کردیم هیچ اثری ازش نیست. به لحاظ فنی در صفحه وجود داره ولی خب خالی هست و محتوایی نداره و چون هیچ استایلی فعلا بهش ندادیم به صورت پیشفرض چیزی نشون داده نمیشه.

کدنویسی ظاهر صفحه با CSS

وقتشه با یکم کد CSS ظاهر همه چی رو درست کنیم. تو مقاله قبلی راجع به CSS یه توضیحات مختصر داده بودم. اگه css رو هم اولین باره میبینید بد نیست یکم راجع بهش مطالعه کنید. ظاهر صفحه رو قراره توی فایل app.css کدنویسی کنیم پس اول این فایل رو داخل <head> صفحه قرار میدیم:

<head>
  ...
  <link rel="stylesheet" href="app.css">
</head>

و داخل کد های css اول ویژگی های کلی صفحه مثل فونت و راست چین شدنش رو درست می کنیم:

(من از سرویس گوگل فونت استفاده کردم ولی اینطوری هم میشه که فایل فونت رو به فولدر پروژه اضافه کنیم و از طریق @font-face داخل کد css تعریفش کنیم.)

@import url("https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;700&display=swap");

body {
  direction: rtl;
  font-family: "Vazirmatn", sans-serif;
}

بعدش تیتر و تصویر رو درست میکنیم:

h1 {
  font-size: 1.5rem;
  text-align: center;
  margin-bottom: 0.5rem;
}
img {
  max-width: 100%;
  margin-bottom: 1rem;
}

بعدش هم بورد یا صفحه دوز رو درست می کنیم:

.board {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 0.5rem;
  max-width: 300px;
  margin: auto;
}
.board > * {
  aspect-ratio: 1;
  border: 1px solid #bbb;
}
.board > *:hover {
  background-color: #eee;
}

اینجا من از display: grid استفاده کردم و سه تا ستون با grid-template-columns تعریف کردم و در کل چون نه تا div داریم سه تا ستون و سه تا سطر ایجاد میشه. هر خونه داخل .board رو هم نوشتم aspect-ratio: 1 که ابعادش مربع باشه. تا اینجا همچین چیزی شد:

برای آشنایی بیشتر با Grid در CSS حتما آموزش CSS Grid رو ببینید.

کدنویسی بازی دوز با JavaScript

نوبت اضافه کردن فایل app.js و کد جاوا اسکریپت به صفحه هست. در آخر <body> میتونیم لینکش رو قرار بدیم:

<body>
  ...
  <script src="app.js"></script>
</body>

این بازی رو میتونیم اینطوری پیاده سازی کنیم که کاربر با کامپیوتر بازی کنه. برای اینکه یکم ساده تر بشه کامپیوتر فقط حرکت تصادفی یا رندم انجام میده. به این صورت در نظر میگیریم که اول کاربر که بازیکن X هست حرکت انجام میده و بعدش کامپیوتر که بازیکن O هست حرکت رندم انجام میده و دوباره از اول تا زمانی که یکی از از حالت های بردن این بازی اتفاق بیفته.

اگه خونه ها رو از صفر تا هشت شماره گذاری کنیم همه حالت های برنده شدن توی این بازی رو میشه به این صورت توی یه آرایه دو بعدی تعریف کرد:

const winingConditions = [
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8],

  [0, 3, 6],
  [1, 4, 7],
  [2, 5, 8],

  [0, 4, 8],
  [2, 4, 6],
];

یه سری متغیر دیگه هم برای پیاده سازی منطق بازی تعریف می کنیم:

const cells = document.querySelectorAll(".board > *");
const gameState = [];
  • همه خونه ها رو توی متغیر cells قرار دادیم.

  • وضعیت بازی رو داخل متغیر gameState به مرور قرار میدیم که هر خونه X هست O هست یا خالی هست.

اول بریم سراغ کلیک روی خونه ها. به این صورت میتونیم برای خونه ها کلیک تعریف کنیم و اگر خونه پر بود return کنیم و هیچ کاری نکنیم:

cells.forEach((cell, number) => {
  cell.addEventListener("click", () => {
    if (gameState[number]) {
      return;
    }
    // ...
  });
});

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

let isFinished = false;

و کدمون رو آپدیت می‌کنیم که موقع کلیک هم چک کنیم که بازی تموم شده یا نه:

cells.forEach((cell, number) => {
  cell.addEventListener("click", () => {
    if (isFinished || gameState[number]) {
      return;
    }
    // ...
  });
});

فعلا فرض کنیم یه سری تابع برای اینکار ها داریم و فقط استفادشو بنویسیم:

cell.addEventListener("click", () => {
  ...

  move("X", number);
  if (!isFinished) {
    const random = findRandom();
    move("O", random);
  }

});

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

cell.addEventListener("click", () => {
  ...

  move("X", number);
  if (!isFinished) {
    setTimeout(() => {
      const random = findRandom();
      move("O", random);
    }, 500);
  }

});

یه باگ داریم! باید حواسمون به باگ هایی که ناخواسته درست میکنیم باشه. اینجا الان کاربر می تونه قبل از اینکه 500 میلی ثانیه بگذره و کامپیوتر حرکتشو انجام بده، سریع خونه های دیگه رو هم کلیک کنه و بازی رو ببره!

برای حل این باگ میتونیم یه متغیر برای نوبت کاربر تعریف کنیم:

let isUserTurn = true;

و کدمون رو به این صورت آپدیت کنیم:

cells.forEach((cell, number) => {
  cell.addEventListener("click", () => {
    if (isFinished || !isUserTurn || gameState[number]) {
      return;
    }
    isUserTurn = false;
    move("X", number);
    if (!isFinished) {
      setTimeout(() => {
        const random = findRandom();
        move("O", random);
        isUserTurn = true;
      }, 500);
    }
  });
});

برای خوانایی و بهتر دیده شدن کد می تونیم تابع setTimeout رو جدا کنیم:

setTimeout(playComputer, 500);

در نهایت دوتا تابع move و findRandom رو هم به این صورت تعریف میکنیم:

const move = (player, number) => {
  // آپدیت وضعیت بازی و محتوای خونه
  gameState[number] = player;
  cells[number].textContent = player;

  // بررسی اینکه برنده داریم یا نه
  for (let i = 0; i < winingConditions.length; i++) {
    const condition = winingConditions[i];
    // اگه هر سه تا خونه توسط یه بازیکن پر شده باشه
    if (condition.every((n) => gameState[n] === player)) {
      const h1 = document.querySelector("h1");
      h1.textContent = player + " برنده شد!";
      h1.style.color = player === "X" ? "green" : "red";
      isFinished = true;
      break;
    }
  }
};

const findRandom = () => {
  // همه خونه های خالی رو پیدا میکنیم
  const emptyNums = [];
  for (let num = 0; num < 9; num++) {
    if (!gameState[num]) {
      emptyNums.push(num);
    }
  }
  // یدونه اش رو رندم انتخاب می کنیم
  return emptyNums[Math.floor(Math.random() * emptyNums.length)];
};

الان می تونیم بازی رو تست کنیم:

یکم ظاهر این X و O ها خوب نیست که می تونیم با css درستش کنیم:

حالا بهتر شد. و برای جذابیت بیشتر میتونیم با interval یکاری کنیم رنگ خونه های برنده هی آبی و قرمز بشه:

setInterval(() => {
  i++;
  condition.forEach((n) => {
    cells[n].style.color = i % 2 ? "blue" : "red";
  });
}, 200);

فقط یه نکته ی دیگه هم هست. الان تنها راه برای ریست کردن بازی اینه که کاربر خودش صفحه رو رفرش کنه. برای اینکار هم می تونیم خودمون یه دکمه قرار بدیم.

در HTML بعد از قسمت دوز اینو قرار میدیم:

<button>شروع مجدد</button>

در JavaScript هم در انتهای کد اینو اضافه می کنیم:

document
  .querySelector("button")
  .addEventListener("click", () => location.reload());

خب حالا ظاهر دکمه رو هم با CSS درست می کنیم. همچنین بنظرم اگه یه ذره دیگه هم تلاش کنیم بهتر از این می تونیم ظاهر صفحه رو طراحی کنیم. مثلا تم تیره استفاده کنیم و عکس و تیتر رو جابه جا کنیم ببینیم چی میشه. در کد CSS این تغییرات رو بدیم:


body {
  ...
  background-color: #282c34;
  color: #e3e6ed;
}

h1 {
  ...
  margin-bottom: 1rem;
}
img {
  ...
  filter: invert(1);
}

...
.board > *:hover {
  background-color: #1c1f26;
}

button {
  font-family: inherit;
  font-size: 1rem;
  color: inherit;
  background-color: #363d4b;
  border: none;
  padding: 0.5rem 1.2rem;
  margin: 2rem auto;
  display: block;
  cursor: pointer;
}
button:hover {
  background-color: #34435f;
}

بنظرم بهتر شد.

مرور نهایی کد های پروژه

کد کامل app.css به این صورت شد:

@import url("https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;700&display=swap");

body {
  direction: rtl;
  font-family: "Vazirmatn", sans-serif;
  background-color: #282c34;
  color: #e3e6ed;
}

h1 {
  font-size: 1.5rem;
  text-align: center;
  margin-bottom: 1rem;
}
img {
  max-width: 100%;
  margin-bottom: 1rem;
  filter: invert(1);
}

.board {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 0.5rem;
  max-width: 300px;
  margin: auto;
}
.board > * {
  aspect-ratio: 1;
  border: 1px solid #bbb;
  font-size: 2rem;
  display: grid;
  place-content: center;
}
.board > *:hover {
  background-color: #1c1f26;
}

button {
  font-family: inherit;
  font-size: 1rem;
  color: inherit;
  background-color: #363d4b;
  border: none;
  padding: 0.5rem 1.2rem;
  margin: 2rem auto;
  display: block;
  cursor: pointer;
}
button:hover {
  background-color: #34435f;
}

و کد کامل app.js هم به این صورت شد:

const winingConditions = [
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8],

  [0, 3, 6],
  [1, 4, 7],
  [2, 5, 8],

  [0, 4, 8],
  [2, 4, 6],
];

const cells = document.querySelectorAll(".board > *");
const gameState = [];
let isFinished = false;
let isUserTurn = true;

const move = (player, number) => {
  gameState[number] = player;
  cells[number].textContent = player;
  for (let i = 0; i < winingConditions.length; i++) {
    const condition = winingConditions[i];
    if (condition.every((n) => gameState[n] === player)) {
      const h1 = document.querySelector("h1");
      h1.textContent = player + " برنده شد!";
      h1.style.color = player === "X" ? "green" : "red";
      isFinished = true;
      setInterval(() => {
        i++;
        condition.forEach((n) => {
          cells[n].style.color = i % 2 ? "blue" : "red";
        });
      }, 200);
      break;
    }
  }
};

const findRandom = () => {
  const emptyNums = [];
  for (let num = 0; num < 9; num++) {
    if (!gameState[num]) {
      emptyNums.push(num);
    }
  }
  return emptyNums[Math.floor(Math.random() * emptyNums.length)];
};

const playComputer = () => {
  const random = findRandom();
  move("O", random);
  isUserTurn = true;
};

cells.forEach((cell, number) => {
  cell.addEventListener("click", () => {
    if (isFinished || !isUserTurn || gameState[number]) {
      return;
    }
    isUserTurn = false;
    move("X", number);
    if (!isFinished) {
      setTimeout(playComputer, 500);
    }
  });
});

document
  .querySelector("button")
  .addEventListener("click", () => location.reload());

کد کامل index.html هم به این صورت:

<!DOCTYPE html>
<html lang="fa">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>بازی دوز آنلاین</title>
  <link rel="stylesheet" href="app.css">
</head>
<body>
  
  <img src="banner.png" alt="دوز">
  <h1>بازی دوز آنلاین</h1>

  <div class="board">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>

  <button>شروع مجدد</button>

  <script src="app.js"></script>
</body>
</html>

آپلود پروژه روی GitHub

اگه توی گیت هاب اکانت ندارید اول باید از طریق https://github.com/signup ثبت نام کنید. بعدش این دوتا دستور رو داخل ترمینال VSCode وارد کنید (و اینتر بزنید) تا git شما رو بشناسه: (اطلاعات خودتونو جایگزین کنید)

git config --global user.email "you@example.com"
git config --global user.name "Your Name"

بعدش از منوی source control گزینه publish to github رو بزنید. باید یکی دوبار تایید کنید که لاگین بشه به حساب شما بعدش نوع انتشار رو هم public انتخاب کنید چون سرویس pages که فقط برای پروژه های عمومی رایگانه:

بعدش فایل های پروژه آپلود میشه و می تونید پروژه رو در بخش repository های خودتون در سایت گیت هاب مشاهده کنید.

اگه به هر دلیلی این کار انجام نشد می تونید فایل ها رو به صورت دستی هم آپلود کنید، در اینصورت مزایای git رو از دست میدید. برای اینکار اول یه repository جدید بسازید (https://github.com/new) بعدش میتونید آپلود کنید:

راه اندازی آنلاین در Github Pages

برای راه اندازی آنلاین وارد صفحه repository این پروژه میشیم و از منوی Settings هم وارد قسمت Pages میشیم. اونجا شاخه یا branch موردنظر رو انتخاب می کنیم که میخوایم از روی اون ساخته بشه. اگه خودتون تغییر نداده باشید معمولا master یا main رو باید انتخاب کنید. بعدش Save رو می زنیم و منتظر می مونیم که صفحه ساخته بشه:

ممکنه چند دقیقه طول بکشه بعدش اگه صفحه رو رفرش کنیم آدرس سایتی که ساخته شده رو میاره:

برای من این آدرس شد:
https://reza-akbari.github.io/simple-web-page

حالا هر موقع حوصله مون سر رفت می تونیم این صفحه رو باز کنیم و چند دور دوز بازی کنیم. به دوستاتونم می تونید بفرستید و پز بدید که برنامه نویس حرفه ای وب و گیم ساز پرو هستید 😉.

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

خیلی ممنونم که این مطلب رو مطالعه کردید و امیدوارم براتون مفید واقع شده باشه.


کامنت ها