آشنایی با Web Component ها

Web Components

02 اسفند 1399
آشنایی با Web Component ها

آیا تابه‌حال آرزو کرده‌اید که کاش می‌توانستید کامپوننت‌های Vue و React و Angular را باهم به اشتراک بگذارید؟ یکی از قابلیت‌های جدید حوزه‌ی وب، Web Component ها هستند که این اجازه را به ما می‌دهند! ما در این مقاله یک Web Component را خواهیم ساخت و روش استفاده از آن‌ها را به شما نشان خواهم داد.

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

برای مطالعه‌ی این مقاله آشنایی با یکی از فریم‌ورک‌های جاوا اسکریپتی رایج (React یا Vue یا Agular) به شما کمک خواهد کرد چراکه ما وارد جزئیات نخواهیم شد و فقط کلیت کار با وب کامپوننت‌ها را بررسی خواهیم کرد.

Web Component چیست؟

وب کامپوننت‌ها (Web Components) درواقع API های خاصی هستند که به ما اجازه می‌دهند کدهای HTML خود را در محیطی ایزوله بسازیم تا در برنامه‌های دیگر قابل‌استفاده باشند. اگر از فریم‌ورک‌های Vue یا React یا Angular استفاده کرده باشید حتما با کامپوننت‌ها آشنا هستید. Web Component ها چنین قابلیتی را به حوزه‌ی وب می‌آورند بدون اینکه نیازی به فریم‌ورک خاصی باشد. به‌طور مثال شما می‌توانید کامپوننتی مثل <my-web-component> را بسازید و از آن در هر کتابخانه یا فریم‌ورک جاوا اسکریپتی استفاده نمایید! در ضمن بر اساس اعلام سایت caniuse، وب کامپوننت‌ها در اکثر مرورگر های محبوب و مدرن پشتیبانی می شوند.

این کامپوننت‌ها framework agnostic هستند یعنی به هیچ فریم‌ورکی نیاز نداشته و با تمام فریم‌ورک‌ها سازگار هستند بنابراین یکی از تکنولوژی‌های بسیار محبوب در آینده خواهند بود. تصور کنید که می‌خواهیم یک کامپوننت را برای فرم‌های HTML بسازیم. اگر این کار را با web component ها انجام بدهیم، کافی است یک‌بار کدها را بنویسیم و دیگر نیازی به نوشتن نسخه‌های مختلف برای فریم‌ورک‌های مختلف نیست.

حالا نوبت کار عملی است. من یک پروژه را در نظر گرفته‌ام و پس‌ازآنکه آن را با جاوا اسکریپت ساده کدنویسی کردم، از آن در Vue و React و Angular نیز استفاده خواهیم کرد تا به شما نشان بدهم که این کامپوننت‌ها با تمام فریم‌ورک‌ها سازگار خواهند بود.

ساخت اولین Web Component با جاوا اسکریپت ساده

ما می‌خواهیم برنامه‌ای بسازیم که یک نام را گرفته و آن را در گوگل جست‌وجو کند. در این برنامه کامپوننتی خواهیم داشت که نتایج جست‌وجو را به ما نشان می‌دهد. توجه داشته باشید که مقاله کدنویسی web component ها را به شما آموزش نمی‌دهد بلکه مثالی از استفاده از آن‌ها را ارائه می‌کند.

در قدم اول یک فایل جاوا اسکریپتی جدید به نام search-result.js را ایجاد می‌کنیم که محتوای زیر را دارد. من ابتدا یک‌بار تمام کدها را برایتان قرار می‌دهم و سپس قسمت به قسمت آن را بررسی می‌کنیم.

//search-result.js




const template = document.createElement('template');

template.innerHTML = `

  <style>

    div {

      margin-top: 20px;

      color: green;

    }

  </style>

  <div>

    <p>The Google search result of your name is <a target="_blank" rel="noopener">here</a></p>

  </div>

`;




class SearchResult extends HTMLElement {

  constructor() {

    super();




    this.attachShadow({ mode: 'open' });




    this.shadowRoot.appendChild(template.content.cloneNode(true));




    this.shadowRoot.querySelector('a').href = '';

  }




  static get observedAttributes() {

    return ['name-attribute'];

  }




  attributeChangedCallback(name, oldValue, newValue) {

    if (name == 'name-attribute') {

      this.shadowRoot.querySelector(

        'a'

      ).href = `https://www.google.com/search?q=${newValue}`;

    }

  }

}




window.customElements.define('search-result', SearchResult);

در بخش اول باید عنصر HTML ای به نام <template> (به معنی قالب) را بسازیم بنابراین از دستور document.createElement استفاده کرده‌ایم و آن را در متغیری به نام template ذخیره کرده‌ایم. عنصر <tamplate> در HTML یک عنصر خاص است که به‌محض بارگذاری صفحه نشان داده نمی‌شود بلکه بعدا و با استفاده از جاوا اسکریپت بارگذاری خواهد شد.

در مرحله‌ی بعدی باید محتوای این template را بنویسیم و همان‌طور که در کد بالا می‌بینید من فقط یک div ساده را به همراه چند استایل CSS نوشته‌ام. در مرحله‌ی بعدی باید با استفاده از جاوا اسکریپت، template ساخته‌شده را فعال کنیم. احتمالا بدانید که برای ساخت عناصر شخصی در HTML می‌توانیم یک interface به نام HTMLElement را extend کنیم:

class SearchResult extends HTMLElement {}

اگر با react کار کرده باشید می‌دانید که یکی از روش‌های ساخت کامپوننت extend کردن React.Component است. HTMLElement نیز مانند React.Component است با این تفاوت که HTMLElement جزئی از API مرورگرها است و به فریم‌ورک خاصی نیاز ندارد. تمام کاری که در کد بالا کرده‌ایم، ساخت یک عنصر جدید است.

حالا که کلاس جدید خود را داریم باید به سراغ constructor آن برویم. در قدم اول باید super را صدا بزنیم (این مسئله جزء بحث وراثت در جاوا اسکریپت است و ربطی به web component ها ندارد). ما تا این قسمت کامپوننت خود را با استفاده از HTMLElement ساخته‌ایم بنابراین حالا می‌خواهیم از چیزی به نام shadow DOM استفاده کنیم تا کپسوله‌سازی (encapsulation) انجام بدهیم. کپسوله‌سازی یعنی ایزوله کردن کامپوننت خودمان تا بعدا بتوانیم در هرجایی از آن استفاده کنیم. کپسوله‌سازی باعث می‌شود کامپوننت ما و استایل‌های آن ایزوله شود بنابراین کامپوننت ساخته‌شده از کدهای CSS سراسری تاثیر نمی‌پذیرد و همیشه شکل خود را حفظ می‌کند.

برای متصل کردن shadow DOM به یک عنصر خاص باید از متد attachShadow استفاده کنیم که یک شیء را دریافت می‌کند. ما در کد بالا mode را روی حالت open گذاشته‌ایم که یعنی کدهای جاوا اسکریپت خارج از این کامپوننت می‌توانند به این کامپوننت دسترسی داشته باشند (با استفاده از element.shadowRoot). با این حساب باید متوجه شده باشید که دسترسی به عناصر HTML در وب کامپوننت‌ها با عناصر HTML در حالت عادی یکی نیست. در حالت عادی برای دسترسی به عنصری که id=title است باید به شکل زیر عمل کنیم:

document.querySelector('#title')

اما زمانی که چنین عنصری در وب کامپوننت‌ها وجود داشته باشد دیگر جزئی از DOM اصلی نیست بنابراین به جای اینکه از document استفاده کنیم از ShadowRoot استفاده می کنیم. با توجه به توضیحاتی که دادم باید متوجه کد زیر شده باشید:

this.shadowRoot.appendChild(template.content.cloneNode(true));

این کد کجا قرار دارد؟ درون constructor:

  constructor() {

    super();




    this.attachShadow({ mode: 'open' });




    this.shadowRoot.appendChild(template.content.cloneNode(true));




    this.shadowRoot.querySelector('a').href = '';

  }

با این حساب this به همین شیء یا عنصر اشاره می‌کند. ما ازاینجا به shadowRoot دسترسی پیدا می‌کنیم که همان شیء document برای وب کامپوننت‌ها است. در مرحله‌ی بعدی از دستور appendChild استفاده کرده‌ایم که برای قرار دادن یک عنصر درون یک عنصر دیگر (به‌عنوان فرزند) است. چه چیزی را به‌عنوان فرزند اضافه می‌کنیم؟ کل محتوای موجود در template را با استفاده از دستور cloneNode کپی کرده‌ام و سپس آن را appendChild داده‌ایم تا به‌عنوان فرزند shadowRoot اضافه کند. درنهایت نیز با استفاده از دستور querySelector می‌توانیم تگ <a> را گرفته و خصوصیت href آن را روی یک رشته‌ی خالی بگذاریم.

در مرحله‌ی بعدی متد استاتیک observedAttributes را داریم. چرا چنین متدی را تعریف کرده‌ایم؟

static get observedAttributes() {

   return ['name-attribute'];

}

برنامه‌ی ما نام خود را به‌صورت پویا و با استفاده از attribute خاصی به نام name-attribute دریافت خواهد کرد و مشکل اصلی نیز همین «پویا» بودن دریافت اطلاعات است. وب کامپوننت‌ها به‌صورت پیش‌فرض به تغییر attribute ها واکنش نشان نمی‌دهند بنابراین باید به‌صورت دستی این کار را انجام بدهیم. فلسفه‌ی تعریف متد  observedAttributes نیز همین است. این متد آرایه‌ای را برمی‌گرداند که در آن نامه‌ای attribute های موردنظر ما قرار دارد؛ attribute هایی که باید تحت نظرشان داشته باشیم و با تغییرشان کاری را انجام بدهیم. من فقط name-attribute را در نظر گرفته‌ام.

حالا که مقادیر تحت نظر خودمان برای تغییرات را تعریف کرده‌ایم باید به این تغییرات واکنش نشان بدهیم. برای انجام این کار متد attributeChangedCallback را تعریف کرده‌ایم:

attributeChangedCallback(name, oldValue, newValue) {

    if (name == 'name-attribute'){

      this.shadowRoot.querySelector('a').href = `https://www.google.com/search?q=${newValue}`;

    }

}

این متد سه آرگومان می گیرد:

  • name: نام attribute ای که با تغییر کردن باعث اجرای متد شده است.
  • oldValue: مقدار قبلی آن attribute
  • newValue: مقدار جدید آن attribute

من در این کد با یک شرط if ساده گفته‌ام اگر name برابر با name-attribute باشد باید تگ <a> را با querySelector گرفته و href آن را به مقدار جدید تغییر بدهیم. متد ما برای جست‌وجو در گوگل استفاده از یک query string ساده است بنابراین کافی است که newValue را به پارامتر q در URL اضافه کنیم. ما می‌توانیم در این کد شرط if را حذف کنیم چراکه فقط یک attribute ممکن برای تغییر داریم اما من آن را نوشته‌ام تا اگر شما چندین attribute داشتید بدانید چه‌کار کنید. البته در آن حالت می‌توانید به‌جای if های مختلف از یک دستور switch نیز استفاده نمایید.

در مرحله‌ی آخر باید کاری کنیم که مرورگر بداند یک وب کامپوننت جدید نوشته‌ایم. یعنی چه؟ یعنی در حال حاضر وب کامپوننت ما ساخته شده است اما هنوز مرورگر آن را شناسایی نمی‌کند. چرا؟ به دلیل اینکه مرورگر به‌صورت پیش‌فرض تنها تگ‌های پیش‌فرض HTML مانند <a> و <p> را می‌شناسد درحالی‌که وب کامپوننت‌ها نام خاصی مانند <search-result> را دارند. برای حل این مشکل باید از CustomElementRegistry کمک بگیریم. برای دسترسی به آن باید از window.customElements اقدام کنیم و متد define آن را صدا بزنیم. این همان کاری است که در انتهای کد بالا مشاهده کرده بودید:

window.customElements.define('search-result', SearchResult);

آرگومان اولی که به آن پاس داده‌ایم (رشته‌ی search-result) نام دلخواه شما برای وب کامپوننت است. طبیعتا شما می‌توانید هر نام دیگری را برای آن انتخاب کنید و تنها قانونی که وجود دارد این است که نام کامپوننت شما باید حتما دارای علامت خط فاصله باشد بنابراین نامی مانند search-result صحیح و نامی مانند searchresult یا searchResult غلط است. آرگومان دومی که به define پاس داده شده است نیز نام constructor ای است که وظیفه‌ی ساختن وب کامپوننت شما را دارد. طبیعتا constructor ها نام ندارند بنابراین منظور من از نام constructor درواقع همان نام کلاسی است که تعریف کرده بویم (SearchResult).

متدهای Lifecycle

به شما تبریک می‌گویم! شما اولین وب کامپوننت خود را نوشته‌اید و می‌توانید به‌راحتی از آن در مرورگر خود استفاده کنید. به‌عنوان نکته‌ی تکمیلی این را به شما می‌گویم که وب کامپوننت‌ها (دقیقا مانند کامپوننت‌های Vue یا React یا Angular) دارای lifecycle method (متدهای چرخه‌ی زندگی) هستند. متدهای lifecycle متدهای خاصی هستند که در لحظه‌ی خاصی از چرخه‌ی زندگی یک وب کامپوننت اجرا می‌شوند. مثال:

  • متد connectedCallback: زمانی که کامپوننت شما برای اولین بار به DOM متصل می شود، این متد به صورت خودکار اجرا خواهد شد.
  • متد disconnectedCallback: زمانی که کامپوننت شما از DOM جدا شود، این متد به صورت خودکار اجرا خواهد شد.
  • متد adoptedCallback: زمانی که کامپوننت شما به سند (document) جدیدی منتقل شود، این متد به صورت خودکار اجرا خواهد شد.

درصورتی‌که می‌خواهید با متدهای lifecycle بیشتر آشنا شوید می‌توانید این مقاله از وب‌سایت توسعه‌دهندگان موزیلا را مطالعه نمایید.

من درنهایت یک مثال ساده از lifecycle method ها را برایتان آماده کرده‌ام. ابتدا یک فایل HTML به نام index.html بسازید و محتوای زیر را درون آن کپی کنید:

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title>Life cycle callbacks test</title>

    <style>

      custom-square {

        margin: 20px;

      }

    </style>

    <script defer src="main.js"></script>

  </head>

  <body>

    <h1>Life cycle callbacks test</h1>




    <div>

      <button class="add">Add custom-square to DOM</button>

      <button class="update">Update attributes</button>

      <button class="remove">Remove custom-square from DOM</button>

    </div>




  </body>

</html>

حالا فایلی به نام main.js را ایجاد می‌کنیم و محتوای زیر را در آن کپی می‌کنیم:

// Create a class for the element

class Square extends HTMLElement {

  // Specify observed attributes so that

  // attributeChangedCallback will work

  static get observedAttributes() {

    return ['c', 'l'];

  }




  constructor() {

    // Always call super first in constructor

    super();




    const shadow = this.attachShadow({mode: 'open'});




    const div = document.createElement('div');

    const style = document.createElement('style');

    shadow.appendChild(style);

    shadow.appendChild(div);

  }




  connectedCallback() {

    console.log('Custom square element added to page.');

    updateStyle(this);

  }




  disconnectedCallback() {

    console.log('Custom square element removed from page.');

  }




  adoptedCallback() {

    console.log('Custom square element moved to new page.');

  }




  attributeChangedCallback(name, oldValue, newValue) {

    console.log('Custom square element attributes changed.');

    updateStyle(this);

  }

}




customElements.define('custom-square', Square);




function updateStyle(elem) {

  const shadow = elem.shadowRoot;

  shadow.querySelector('style').textContent = `

    div {

      width: ${elem.getAttribute('l')}px;

      height: ${elem.getAttribute('l')}px;

      background-color: ${elem.getAttribute('c')};

    }

  `;

}




const add = document.querySelector('.add');

const update = document.querySelector('.update');

const remove = document.querySelector('.remove');

let square;




update.disabled = true;

remove.disabled = true;




function random(min, max) {

  return Math.floor(Math.random() * (max - min + 1) + min);

}




add.onclick = function() {

  // Create a custom square element

  square = document.createElement('custom-square');

  square.setAttribute('l', '100');

  square.setAttribute('c', 'red');

  document.body.appendChild(square);




  update.disabled = false;

  remove.disabled = false;

  add.disabled = true;

};




update.onclick = function() {

  // Randomly update square's attributes

  square.setAttribute('l', random(50, 200));

  square.setAttribute('c', `rgb(${random(0, 255)}, ${random(0, 255)}, ${random(0, 255)})`);

};




remove.onclick = function() {

  // Remove the square

  document.body.removeChild(square);




  update.disabled = true;

  remove.disabled = true;

  add.disabled = false;

};

مثال اجرایی این کد در آدرس mdn.github.io/web-components-examples/life-cycle-callbacks قرار داده شده است و می‌توانید اجرای آن را به صورت عملی مشاهده کنید.

استفاده از وب کامپوننت در React

همان‌طور که قبلا برایتان توضیح داده بودم وب کامپوننت‌ها framework agnostic هستند که یعنی به هیچ فریم‌ورک خاصی وابسته نبوده و با همه‌ی آن‌ها کار می‌کنند. ما در این قسمت می‌خواهیم پروژه‌ی ساده‌ای به نام react-web-components را بسازیم و از وب کامپوننت ساخته‌شده‌ی خودمان در آن استفاده کنیم. من از پروژه‌ی create-react-app برای انجام این کار استفاده می‌کنم:

npx create-react-app react-web-components

با اجرای دستور بالا پروژه‌ی ما به نام react-web-components ساخته می‌شود. در مرحله‌ی بعدی باید وارد این پروژه شده و آن را اجرا کنید بنابراین به ترتیب دستورات زیر را در ترمینال خود اجرا کنید:

cd react-web-components

npm start

طبیعتا با اجرای ستور npm start، پروژه در مسیر localhost:3000 برایتان باز خواهد شد. اولین کاری که باید انجام بدهید رفتن به پوشه‌ی src پروژه است. در این پوشه یک پوشه‌ی جدید به نام web-components را ایجاد کنید و سپس فایل search-result.js را در آن کپی کنید. اگر یادتان باشد ما فایلی به نام search-result.js را ساخته بودیم که تمام کدهای جاوا اسکریپت کامپوننت ما را در خود نگه‌داشته است. زمانی که این کار را انجام بدهید، آدرس فایل در پروژه‌ی شما به شکل زیر خواهد بود:

/src/web-components/search-result.js

حالا وارد فایل App.js از پوشه‌ی src شوید و کد موجود در آن را به شکل زیر تغییر بدهید:

//App.js




import { useState } from 'react';




import './App.css';

//import our Web Component

import './web-components/search-result';




function App() {

  const [name, setName] = useState('');




  return (

    <div className="App">

      <input

        placeholder="Enter your name"

        onChange={(event) => setName(event.target.value)}

        value={name}

      ></input>




      <div class="greeting">Hello {name}!</div>




      <search-result name-attribute={name}></search-result>

    </div>

  );

}




export default App;

اگر با کدهای React سروکار داشته باشید درک کد بالا برایتان بسیار ساده است. ما یک input داریم که اسم کاربر را از او می‌گیرد و آن را درون state قرار می‌دهد. در مرحله‌ی بعدی نیز این اسم را از طریق attribute ای به نام name-attribute به کامپوننت ما (یعنی <search-result>) پاس می‌دهد.

در مرحله‌ی بعدی فایل App.css از پوشه‌ی src را باز می‌کنیم و استایل های زیر را به آن اضافه می‌کنیم تا برنامه‌ی ما ظاهر بهتری بگیرد:

//App.css




.App {

  text-align: center;

  margin-top: 30px;

}




.greeting {

  margin-top: 20px;

}

در حالت عادی کدهای ما در react به ES5 تبدیل می‌شوند بنابراین باید از polyfill ها استفاده کنیم تا قابلیت استفاده از وب کامپوننت در ES5 را داشته باشیم. ما می‌توانیم کدهای خودمان را به نسخه‌های جدیدتری از ES5 تبدیل کنیم اما ازآنجایی‌که وب کامپوننت‌ها در مرورگرهای قدیمی‌تر پشتیبانی نمی‌شوند احتمال اینکه وب‌سایت شما در بعضی از مرورگرها باز نشود بالا می‌رود بنابراین polyfill ها گزینه‌ی بهتری هستند. ما به دو polyfill بهنام‌های webcomponentsjs و vendor-copy نیاز داریم بنابراین باید دستور زیر را برای نصب آن‌ها در ترمینال خود اجرا کنید:

npm install --save @webcomponents/webcomponentsjs vendor-copy

با این‌کار دو polyfill ما نصب می‌شوند اما هنوز آن‌ها را فعال نکرده‌ایم. برای انجام این کار فایل package.json را باز کرده و در قسمت scripts، اسکریپت‌هایی را برای این دو polyfill نیز تعریف کنید تا بعد از npm install های ما نیز اجرا شوند:

//package.json




"scripts": {

  "start": "react-scripts start",

  "build": "react-scripts build",

  "test": "react-scripts test",

  "eject": "react-scripts eject",

  "postinstall": "vendor-copy"

},

حالا در انتهای همین فایل package.json باید به vendor-copy بگوییم که پس از اجرای install چه فایل‌هایی را کپی کند. ما در این پروژه به دو فایل webcomponents-bundle.js و custom-elements-es5-adapter.js نیاز داریم. برای انجام این کار می‌توانیم به شکل زیر عمل کنیم:

//package.json




"vendorCopy": [

  {

    "from": "node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js",

    "to": "public/vendor/custom-elements-es5-adapter.js"

  },

  {

    "from": "node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js",

    "to": "public/vendor/webcomponents-bundle.js"

  }

]

درنهایت دستور npm install را اجرا می‌کنیم تا فرآیند install یک‌بار دیگر انجام شود. پس از پایان این فرآیند یک پوشه‌ی جدید به نام public/vendor را در مسیر پروژه‌ی خود مشاهده خواهید کرد که دو فایل custom-elements-es5-adapter.js و webcomponents-bundle.js را درون خود دارد. تنها کاری که باقی‌مانده است اضافه کردن polyfill های ساخته شده به فایل HTML خودمان است تا برای کاربران بارگذاری شوند. فایل HTML ما در آدرس public/index.html قرار دارد بنابراین به آن رفته و در انتهای تگ <body> خطوط زیر را اضافه می‌کنیم:

//index.js




<script src="%PUBLIC_URL%/vendor/webcomponents-bundle.js"></script>

<script>if (!window.customElements) { document.write("<!--"); }</script>

<script src="%PUBLIC_URL%/vendor/custom-elements-es5-adapter.js"></script>

<!--! DO NOT REMOVE THIS COMMENT, WE NEED ITS CLOSING MARKER -->

توجه داشته باشید که نباید کامنت انتهایی را حذف کنید چرا که polyfill هایمان به آن نیاز دارند. حالا با اجرای npm start می‌توانیم برنامه را در مرورگر (آدرس localhost:3000) تست کنیم.

استفاده از وب کامپوننت در Vue

Vue یکی دیگر از فریم‌ورک‌های بسیار مجبوب در دنیای جاوا اسکریپت است. ما در قدم اول باید آن را نصب کنیم:

npm i -g @vue/cli

کاربران لینوکس و مک باید sudo را نیز به ابتدای دستور بالا اضافه کنند. در مرحله‌ی بعدی باید یک پروژه ی Vue بسازیم. من می‌خواهم نام پروژه را vue-web-components بگذارم. برای این کار دستور زیر را در ترمینال خود اجرا کنید:

vue create vue-web-components

سپس با دو دستور زیر وارد پوشه‌ی پروژه شده و آن را اجرا می‌کنیم:

cd vue-web-components

npm run start

حالا برنامه روی آدرس localhost:8080 در مرورگر باز می‌شود. اگر این‌طور شد یعنی همه‌چیز را به‌درستی انجام داده‌اید. من دقیقا مانند چیزی که در دستورالعمل React توضیح دادم، بازهم پوشه‌ای به نام web-components را در src ایجاد می‌کنیم و سپس فایل search-result.js را در آن قرار می‌دهیم. این فایل باید آدرس /src/web-components/search-result.js را داشته باشد.

در مرحله‌ی بعدی وارد فایل src/App.vue می‌شویم و محتویاتش را به شکل زیر تغییر می‌دهیم:

//App.vue




<template>

  <div class="App">

    <input placeholder="Enter your name" v-model="name" />




    <div class="greeting">Hello {{ name }}!</div>




    <search-result v-bind:name-attribute="name"></search-result>

  </div>

</template>




<script>

//import our Web Component

import './web-components/search-result.js';




export default {

  data() {

    return {

      name: '',

    };

  },

};

</script>




<style>

.App {

  margin-top: 30px;

  text-align: center;

}

.greeting {

  margin-top: 20px;

}

</style>

این کد معادل همان کدی است که در React نوشتیم؛ یک فیلد input برای دریافت نام کاربر و سپس استفاده از وب کامپوننت search-result برای نمایش لینک جست‌وجو در گوگل. در این قسمت هم باید از polyfill ها استفاده کنیم. من از webcomponents.js استفاده می‌کنم چراکه تنها polyfill هایی را load می‌کند که مرورگر به آن احتیاج دارد بنابراین نمی‌توانیم آن را به‌صورت مستقیم به‌عنوان یک وابستگی (dependency) به webpack بدهیم بلکه باید وابستگی‌ها را به فایل index خود اضافه کنید. در ابتدا نیاز به نصب این polyfill داریم:

npm install --save-dev copy-webpack-plugin @webcomponents/webcomponentsjs

حالا باید polyfill هایمان را کپی کنیم. برای این کار یک فایل جدید به نام vue.config.js را در مسیر اصلی پروژه‌ی خود ایجاد کنید تا تنظیمات webpack به دست ما باشد. حالا این تنظیمات را به آن اضافه کنید:

//vue.config.js




const CopyWebpackPlugin = require('copy-webpack-plugin');




module.exports = {

  configureWebpack: {

    plugins: [

      new CopyWebpackPlugin({

        patterns: [

          {

            context: 'node_modules/@webcomponents/webcomponentsjs',

            from: '**/*.js',

            to: 'webcomponents',

          },

        ],

      }),

    ],

  },

};

پلاگین CopyWebpackPlugin تمام فایل‌های جاوا اسکریپتی موردنیاز را به پوشه‌ی webcomponents (درون dist)‌ کپی خواهد کرد. آخرین مرحله بارگذاری polyfill های کپی‌شده است بنابراین به فایل public/index.html در پروژه‌ی خود رفته و در قسمت <head> آن کدهای زیر را اضافه می کنیم:

//index.html




<script src="webcomponents/webcomponents-loader.js"></script>

<script>

  if (!window.customElements) {

    document.write('<!--');

  }

</script>

<script src="webcomponents/custom-elements-es5-adapter.js"></script>

<!-- ! DO NOT REMOVE THIS COMMENT, WE NEED ITS CLOSING MARKER -->

مثل همیشه نباید کامنت انتهایی این دستور را حذف کنید. حالا اگر دستور npm run serve را در ترمینال اجرا کنید، پروژه در مسیر localhost:8080 باز می‌شود.

استفاده از وب کامپوننت در Angular

همانطور که می‌دانید Angular پیچیده‌تر از Vue و React است بنابراین فرآیند استفاده از وب کامپوننت در آن کمی پیچیده‌تر است. برای انجام این کار ابتدا angular را نصب می‌کنیم:

npm i -g @angular/cli

کاربران لینوکس و مک باید sudo را به ابتدای دستور بالا اضافه کنند. ساخت یک پروژه به نام angular-web-components هدف بعدی ما است بنابراین سه دستور زیر را به ترتیب در ترمینال خود اجرا کنید:

ng new angular-web-components

cd angular-web-components

ng serve

با انجام این کار پروژه در مسیر localhost:4200 برایتان باز می‌شود. این بار به پوشه‌ی src/app در پروژه‌ی خود رفته و پوشه‌ی جدیدی به نام web-components را ایجاد کنید که درون خود فایل search-result.js را داشته باشد. با این حساب مسیر این فایل به شکل src/app/web-components/search-result.js خواهد بود.

اگر با انگولار کار کرده باشید می‌دانید که برای ایجاد two way binding در Angular باید ماژول FormsModule را در اسکریپت خود import کنید. از طرفی انگولار به‌صورت پیش‌فرض وب کامپوننت ما را تشخیص نمی‌دهد بنابراین باید خصوصیت schemas را به NgModule اضافه کنیم و اعلام کنیم که می‌خواهیم از یک عنصر خاص (وب کامپوننت) استفاده کنیم. این کار با CUSTOM_ELEMENTS_SCHEMA انجام می‌شود. چطور می‌توانیم این کار ها را انجام بدهیم؟ شما باید به فایل src/app/app.module.ts بروید و کدهای آن را به شکل زیر ویرایش کنید:

//app.module.ts




import { BrowserModule } from '@angular/platform-browser';

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

import { FormsModule } from '@angular/forms';




import { AppComponent } from './app.component';




@NgModule({

  declarations: [AppComponent],

  imports: [BrowserModule, FormsModule],

  providers: [],

  schemas: [CUSTOM_ELEMENTS_SCHEMA],

  bootstrap: [AppComponent],

})

export class AppModule {}

در مرحله‌ی بعدی به فایل src/app/app.component.html می‌رویم و کدهای درون آن را حذف کرده و به جایش کدهای زیر را در آن قرار می‌دهیم:

//app.component.html




<div class="App">

  <input placeholder="Enter your name" [(ngModel)]="name" />




  <div class="greeting">Hello {{name}}!</div>




  <search-result [attr.name-attribute]="name"></search-result>

</div>

در نهایت تنها کاری که باقی می‌ماند اضافه کردن چند استایل CSS به فایل src/app/app.component.css است، بنابراین:

//app.component.css




.App {

  text-align: center;

  margin-top: 30px;

}




.greeting {

  margin-top: 20px;

}

البته باید کامپوننت src/app/app.component.ts را نیز ویرایش کنیم تا تمام این ویرایش‌ها را قبول کند:

//app.component.ts




import { Component } from '@angular/core';

//import our Web Component

import './web-components/search-result.js';




@Component({

  selector: 'app-root',

  templateUrl: './app.component.html',

  styleUrls: ['./app.component.css'],

})

export class AppComponent {

  name;

}

حالا مثل همیشه باید polyfill هایمان را نصب کنیم:

npm install --save-dev @webcomponents/webcomponentsjs

حالا باید polyfill های خودمان را بارگذاری کنیم. شما در مسیر اصلی پروژه یک فایل به نام angular.json را دارید. آن را باز کرده و آرایه‌ی assets را به آن اضافه کنید. این آرایه باید به شکل زیر باشد:

//angular.json




"assets": [

"src/favicon.ico",

"src/assets",

{

  "glob": "**/*.js",

  "input": "node_modules/@webcomponents/webcomponentsjs",

  "output": "webcomponents/"

}

در نهایت وارد فایل src/index.html شده و این loader را در آن قرار می‌دهیم. همچنین قسمتی را برای ES5 قرار می دهیم. هر دو کار در قسمت <head> انجام می‌شود:

//index.html




<script src="webcomponents/webcomponents-loader.js"></script>

<script>

  if (!window.customElements) {

    document.write('<!--');

  }

</script>

<script src="webcomponents/custom-elements-es5-adapter.js"></script>

<!-- ! DO NOT REMOVE THIS COMMENT, WE NEED ITS CLOSING MARKER -->

همانطور که در دو بخش قبلی گفتم نباید کامنت خط آخر را حذف کنید. نهایتا با اجرای دستور ng serve پروژه‌ی ما روی آدرس localhost:4200 باز خواهد شد.


منبع: وب‌سایت academind

نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.