å ç¢ã§ã¹ã±ãŒã©ãã«ãªWebã³ã³ããŒãã³ãã€ã³ãã©ãæ§ç¯ããŸãããã®ã¬ã€ãã§ã¯ãã°ããŒãã«ãªWebéçºã®ããã®èšèšååãããŒã«ããã¹ããã©ã¯ãã£ã¹ãé«åºŠãªæè¡ã解説ããŸãã
Webã³ã³ããŒãã³ãã€ã³ãã©ã¹ãã©ã¯ãã£ïŒå æ¬çãªå®è£ ã¬ã€ã
Webã³ã³ããŒãã³ãã¯ãçŸä»£ã®Webã¢ããªã±ãŒã·ã§ã³åãã«åå©çšå¯èœãªUIèŠçŽ ãäœæãã匷åãªæ¹æ³ãæäŸããŸããç¹ã«äžçäžã«åæ£ããå€§èŠæš¡ããŒã ã§äœæ¥ããå Žåãã¹ã±ãŒã©ããªãã£ãä¿å®æ§ãäžè²«æ§ã確ä¿ããããã«ã¯ãWebã³ã³ããŒãã³ããäžå¿ãšããå åºãªã€ã³ãã©ãæ§ç¯ããããšãäžå¯æ¬ ã§ãããã®ã¬ã€ãã§ã¯ãå ç¢ãªWebã³ã³ããŒãã³ãã€ã³ãã©ãèšèšãå®è£ ãå±éããæ¹æ³ã«ã€ããŠå æ¬çãªæŠèŠã説æããŸãã
ã³ã¢ã³ã³ã»ããã®çè§£
å®è£ ã«å ¥ãåã«ãWebã³ã³ããŒãã³ãã®åºæ¬çãªæ§æèŠçŽ ãçè§£ããããšãäžå¯æ¬ ã§ãïŒ
- ã«ã¹ã¿ã èŠçŽ (Custom Elements): ç¬èªã®HTMLã¿ã°ãé¢é£ããJavaScriptã®æ¯ãèããšå ±ã«å®çŸ©ã§ããŸãã
- Shadow DOM: ã«ãã»ã«åãæäŸããã³ã³ããŒãã³ãå å€ãžã®ã¹ã¿ã€ã«ãã¹ã¯ãªããã®æŒæŽ©ãé²ããŸãã
- HTMLãã³ãã¬ãŒã (HTML Templates): åå©çšå¯èœãªHTMLæ§é ãå®çŸ©ããæ¹æ³ãæäŸããŸãã
- ESã¢ãžã¥ãŒã« (ES Modules): ã¢ãžã¥ãŒã«åãããJavaScriptéçºãšäŸåé¢ä¿ç®¡çãå¯èœã«ããŸãã
Webã³ã³ããŒãã³ãã€ã³ãã©ã®èšèšåå
é©åã«èšèšãããWebã³ã³ããŒãã³ãã€ã³ãã©ã¯ã以äžã®ååã«åŸãã¹ãã§ãïŒ
- åå©çšæ§: ã³ã³ããŒãã³ãã¯ãç°ãªããããžã§ã¯ããã³ã³ããã¹ãã§åå©çšã§ããããã«èšèšãããã¹ãã§ãã
- ã«ãã»ã«å: Shadow DOMã䜿çšããŠãã³ã³ããŒãã³ããäºãã«å¹²æžããªãããã«åé¢ãããããšãä¿èšŒãã¹ãã§ãã
- æ§æå¯èœæ§: ã³ã³ããŒãã³ãã¯ãããè€éãªUIèŠçŽ ãäœæããããã«ç°¡åã«çµã¿åãããããããã«èšèšãããã¹ãã§ãã
- ã¢ã¯ã»ã·ããªãã£: ã³ã³ããŒãã³ãã¯ãWCAGã¬ã€ãã©ã€ã³ã«åŸããé害ãæã€ãŠãŒã¶ãŒãã¢ã¯ã»ã¹ã§ããããã«ãã¹ãã§ãã
- ä¿å®æ§: ã€ã³ãã©ã¹ãã©ã¯ãã£ã¯ãç¶æã»æŽæ°ã容æã§ããã¹ãã§ãã
- ãã¹ãå®¹ææ§: ã³ã³ããŒãã³ãã¯ãèªåãã¹ãããŒã«ã䜿çšããŠç°¡åã«ãã¹ãã§ããã¹ãã§ãã
- ããã©ãŒãã³ã¹: ã³ã³ããŒãã³ãã¯ãããã©ãŒãã³ã¹ãé«ããã¢ããªã±ãŒã·ã§ã³å šäœã®ããã©ãŒãã³ã¹ã«åœ±é¿ãäžããªãããã«èšèšãããã¹ãã§ãã
- åœéåãšå°åå (i18n/l10n): ã³ã³ããŒãã³ãã¯ãè€æ°ã®èšèªãšå°åããµããŒãããããã«èšèšãããã¹ãã§ããåœéåã«ã¯
i18nextã®ãããªã©ã€ãã©ãªããã©ãŠã¶APIã®äœ¿çšãæ€èšããŠãã ãããäŸãã°ãæ¥ä»ã®æžåŒèšå®ã¯ãŠãŒã¶ãŒã®ãã±ãŒã«ãå°éãã¹ãã§ãïŒ
const dateFormatter = new Intl.DateTimeFormat(userLocale, options);
const formattedDate = dateFormatter.format(date);
éçºç°å¢ã®ã»ããã¢ãã
å ç¢ãªéçºç°å¢ã¯ãWebã³ã³ããŒãã³ãã®æ§ç¯ãšä¿å®ã«äžå¯æ¬ ã§ãã以äžã«æšå¥šãããã»ããã¢ããã瀺ããŸãïŒ
- Node.jsãšnpm (ãŸãã¯yarn/pnpm): äŸåé¢ä¿ã®ç®¡çãšãã«ãã¹ã¯ãªããã®å®è¡ã®ããã
- ã³ãŒããšãã£ã¿ (VS Code, Sublime Textãªã©): JavaScriptãHTMLãCSSããµããŒããããã®ã
- ãã«ãããŒã« (Webpack, Rollup, Parcel): ã³ãŒãã®ãã³ãã«ãšæé©åã®ããã
- ãã¹ããã¬ãŒã ã¯ãŒã¯ (Jest, Mocha, Chai): åäœãã¹ãã®äœæãšå®è¡ã®ããã
- ãªã³ã¿ãŒãšãã©ãŒããã¿ãŒ (ESLint, Prettier): ã³ãŒãã¹ã¿ã€ã«ãšãã¹ããã©ã¯ãã£ã¹ã®åŸ¹åºã®ããã
create-web-componentãopen-wcã®ãžã§ãã¬ãŒã¿ãŒã®ãããªãããžã§ã¯ãã¹ãã£ãã©ãŒã«ãã£ã³ã°ããŒã«ã䜿çšãããšãå¿
èŠãªããŒã«ããã¹ãŠèšå®ãããæ°ããWebã³ã³ããŒãã³ããããžã§ã¯ããè¿
éã«ã»ããã¢ããã§ããŸãã
åºæ¬çãªWebã³ã³ããŒãã³ãã®å®è£
æšæ¶ã¡ãã»ãŒãžã衚瀺ããåçŽãªWebã³ã³ããŒãã³ãã®äŸããå§ããŸãããïŒ
// greeting-component.js
class GreetingComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
}
static get observedAttributes() {
return ['name'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'name' && oldValue !== newValue) {
this.render();
}
}
render() {
this.shadowRoot.innerHTML = `
Hello, ${this.name || 'World'}!
`;
}
get name() {
return this.getAttribute('name');
}
set name(value) {
this.setAttribute('name', value);
}
}
customElements.define('greeting-component', GreetingComponent);
ãã®ã³ãŒãã¯greeting-componentãšããã«ã¹ã¿ã èŠçŽ ãå®çŸ©ããŸããShadow DOMã䜿çšããŠå
éšæ§é ãšã¹ã¿ã€ã«ãã«ãã»ã«åããŸããname屿§ã«ãããæšæ¶ã¡ãã»ãŒãžãã«ã¹ã¿ãã€ãºã§ããŸãããã®ã³ã³ããŒãã³ããHTMLã§äœ¿çšããã«ã¯ãJavaScriptãã¡ã€ã«ãã€ã³ã¯ã«ãŒãããæ¬¡ã®ã¿ã°ã远å ããã ãã§ãïŒ
ã³ã³ããŒãã³ãã©ã€ãã©ãªã®æ§ç¯
å€§èŠæš¡ãªãããžã§ã¯ãã§ã¯ãWebã³ã³ããŒãã³ããåå©çšå¯èœãªã³ã³ããŒãã³ãã©ã€ãã©ãªã«ãŸãšããããšãæçã§ããããã«ãããäžè²«æ§ãä¿é²ãããã³ãŒãã®éè€ãåæžãããŸããã³ã³ããŒãã³ãã©ã€ãã©ãªãæ§ç¯ããæ¹æ³ã¯æ¬¡ã®ãšããã§ãïŒ
- ãã£ã¬ã¯ããªæ§é : ã³ã³ããŒãã³ããæ©èœãã«ããŽãªã«åºã¥ããŠè«ççãªãã©ã«ãã«æŽçããŸãã
- åœåèŠå: ã³ã³ããŒãã³ããšãã®ãã¡ã€ã«ã«äžè²«ããåœåèŠåã䜿çšããŸãã
- ããã¥ã¡ã³ããŒã·ã§ã³: åã³ã³ããŒãã³ãã«ã€ããŠã䜿çšäŸã屿§ãã€ãã³ããå«ãæç¢ºã§å æ¬çãªããã¥ã¡ã³ããŒã·ã§ã³ãæäŸããŸããStorybookã®ãããªããŒã«ãéåžžã«åœ¹ç«ã¡ãŸãã
- ããŒãžã§ãã³ã°: ã»ãã³ãã£ãã¯ããŒãžã§ãã³ã°ã䜿çšããŠå€æŽã远跡ããåŸæ¹äºææ§ã確ä¿ããŸãã
- å ¬é: ã³ã³ããŒãã³ãã©ã€ãã©ãªãnpmãGitHub Packagesã®ãããªããã±ãŒãžã¬ãžã¹ããªã«å ¬éããä»ã®éçºè ãç°¡åã«ã³ã³ããŒãã³ããã€ã³ã¹ããŒã«ããŠäœ¿çšã§ããããã«ããŸãã
ããŒã«ãšèªåå
Webã³ã³ããŒãã³ãã®ãã«ãããã¹ããå ¬éãªã©ã®ã¿ã¹ã¯ãèªååããããšã§ãéçºã¯ãŒã¯ãããŒãå€§å¹ ã«æ¹åã§ããŸããæ€èšãã¹ãããŒã«ãšãã¯ããã¯ã¯æ¬¡ã®ãšããã§ãïŒ
- ãã«ãããŒã« (Webpack, Rollup, Parcel): ãã«ãããŒã«ãèšå®ããŠãã³ã³ããŒãã³ããæé©åãããJavaScriptãã¡ã€ã«ã«ãã³ãã«ããŸãã
- ãã¹ããã¬ãŒã ã¯ãŒã¯ (Jest, Mocha, Chai): åäœãã¹ããäœæããŠãã³ã³ããŒãã³ããæ£ããåäœããããšã確èªããŸãã
- ç¶ç¶çã€ã³ãã°ã¬ãŒã·ã§ã³/ç¶ç¶çããªããªãŒ (CI/CD): CI/CDãã€ãã©ã€ã³ãã»ããã¢ããããŠãã³ãŒãããŒã¹ã«å€æŽãå ãããããã³ã«ã³ã³ããŒãã³ããèªåçã«ãã«ãããã¹ãããããã€ããŸãã人æ°ã®CI/CDãã©ãããã©ãŒã ã«ã¯GitHub ActionsãGitLab CIãJenkinsãªã©ããããŸãã
- éçè§£æ (ESLint, Prettier): éçè§£æããŒã«ã䜿çšããŠãã³ãŒãã¹ã¿ã€ã«ãšãã¹ããã©ã¯ãã£ã¹ã培åºããŸãããããã®ããŒã«ãCI/CDãã€ãã©ã€ã³ã«çµ±åããŠãã³ãŒãã®ãšã©ãŒãäžæŽåãèªåçã«ãã§ãã¯ããŸãã
- ããã¥ã¡ã³ããŒã·ã§ã³ãžã§ãã¬ãŒã¿ãŒ (Storybook, JSDoc): ããã¥ã¡ã³ããŒã·ã§ã³ãžã§ãã¬ãŒã¿ãŒã䜿çšããŠãã³ãŒããã³ã¡ã³ãã«åºã¥ããŠã³ã³ããŒãã³ãã®ããã¥ã¡ã³ããŒã·ã§ã³ãèªåçã«çæããŸãã
é«åºŠãªãã¯ããã¯
匷åºãªåºç€ãã§ãããªããWebã³ã³ããŒãã³ãã€ã³ãã©ãããã«åŒ·åããããã®é«åºŠãªãã¯ããã¯ãæ¢æ±ã§ããŸãïŒ
- ç¶æ 管ç: ReduxãMobXã®ãããªç¶æ 管çã©ã€ãã©ãªã䜿çšããŠãè€éãªã³ã³ããŒãã³ãã®ç¶æ ã管çããŸãã
- ããŒã¿ãã€ã³ãã£ã³ã°: ããŒã¿ã倿Žããããšãã«ã³ã³ããŒãã³ãã®ããããã£ãèªåçã«æŽæ°ããããã®ããŒã¿ãã€ã³ãã£ã³ã°ãå®è£ ããŸããlit-htmlã®ãããªã©ã€ãã©ãªã¯å¹ççãªããŒã¿ãã€ã³ãã£ã³ã°ã¡ã«ããºã ãæäŸããŸãã
- ãµãŒããŒãµã€ãã¬ã³ããªã³ã° (SSR): ãµãŒããŒäžã§Webã³ã³ããŒãã³ããã¬ã³ããªã³ã°ããŠãSEOãšåæããŒãžèªã¿èŸŒã¿æéãæ¹åããŸãã
- ãã€ã¯ãããã³ããšã³ã: Webã³ã³ããŒãã³ãã䜿çšããŠãã€ã¯ãããã³ããšã³ããæ§ç¯ããå€§èŠæš¡ãªã¢ããªã±ãŒã·ã§ã³ãããå°ãããç¬ç«ããŠãããã€å¯èœãªåäœã«åå²ã§ããŸãã
- ã¢ã¯ã»ã·ããªã㣠(ARIA): ARIA屿§ãå®è£ ããŠãé害ãæã€ãŠãŒã¶ãŒã®ããã®ã³ã³ããŒãã³ãã®ã¢ã¯ã»ã·ããªãã£ãåäžãããŸãã
ã¯ãã¹ãã©ãŠã¶äºææ§
Webã³ã³ããŒãã³ãã¯çŸä»£ã®ãã©ãŠã¶ã§åºããµããŒããããŠããŸããããããå€ããã©ãŠã¶ã§ã¯å¿
èŠãªæ©èœãæäŸããããã«ããªãã£ã«ãå¿
èŠã«ãªãå ŽåããããŸãã@webcomponents/webcomponentsjsã®ãããªããªãã£ã«ã©ã€ãã©ãªã䜿çšããŠãã¯ãã¹ãã©ãŠã¶äºææ§ã確ä¿ããŠãã ãããPolyfill.ioã®ãããªãµãŒãã¹ãå©çšããŠãå¿
èŠãªãã©ãŠã¶ã«ã®ã¿ããªãã£ã«ãæäŸããçŸä»£ã®ãã©ãŠã¶ã®ããã©ãŒãã³ã¹ãæé©åããããšãæ€èšããŠãã ããã
ã»ãã¥ãªãã£ã«é¢ããèæ ®äºé
Webã³ã³ããŒãã³ããæ§ç¯ããéã«ã¯ãæœåšçãªã»ãã¥ãªãã£è匱æ§ã«æ³šæããããšãéèŠã§ãïŒ
- ã¯ãã¹ãµã€ãã¹ã¯ãªããã£ã³ã° (XSS): ãŠãŒã¶ãŒå ¥åããµãã¿ã€ãºããŠãXSSæ»æãé²ããŸãããã³ãã¬ãŒããªãã©ã«ã¯ãé©åã«ãšã¹ã±ãŒãããªããšè匱æ§ãçãå¯èœæ§ããããããæ³šæããŠäœ¿çšããŠãã ããã
- äŸåé¢ä¿ã®è匱æ§: ã»ãã¥ãªãã£è匱æ§ãä¿®æ£ããããã«ãäŸåé¢ä¿ã宿çã«æŽæ°ããŠãã ãããnpm auditãSnykã®ãããªããŒã«ã䜿çšããŠãäŸåé¢ä¿ã®è匱æ§ãç¹å®ããä¿®æ£ããŸãã
- Shadow DOMã®åé¢: Shadow DOMã¯ã«ãã»ã«åãæäŸããŸããã絶察çãªã»ãã¥ãªãã£å¯Ÿçã§ã¯ãããŸãããã³ã³ããŒãã³ãå ã§å€éšã®ã³ãŒããããŒã¿ãæ±ãéã«ã¯æ³šæãå¿ èŠã§ãã
ã³ã©ãã¬ãŒã·ã§ã³ãšã¬ããã³ã¹
å€§èŠæš¡ãªããŒã ã§ã¯ãäžè²«æ§ãšå質ãç¶æããããã«ãæç¢ºãªã¬ã€ãã©ã€ã³ãšã¬ããã³ã¹ã確ç«ããããšãäžå¯æ¬ ã§ãã以äžãæ€èšããŠãã ããïŒ
- ã³ãŒãã¹ã¿ã€ã«ã¬ã€ã: æç¢ºãªã³ãŒãã¹ã¿ã€ã«ã¬ã€ãã©ã€ã³ãå®çŸ©ãããªã³ã¿ãŒããã©ãŒããã¿ãŒã䜿çšããŠåŸ¹åºããŸãã
- ã³ã³ããŒãã³ãã®åœåèŠå: ã³ã³ããŒãã³ããšãã®å±æ§ã«ã€ããŠäžè²«ããåœåèŠåã確ç«ããŸãã
- ã³ã³ããŒãã³ãã®ã¬ãã¥ãŒããã»ã¹: ãã¹ãŠã®ã³ã³ããŒãã³ããå¿ èŠãªåºæºãæºãããŠããããšã確èªããããã®ã³ãŒãã¬ãã¥ãŒããã»ã¹ãå°å ¥ããŸãã
- ããã¥ã¡ã³ããŒã·ã§ã³æšæº: æç¢ºãªããã¥ã¡ã³ããŒã·ã§ã³æšæºãå®çŸ©ãããã¹ãŠã®ã³ã³ããŒãã³ããé©åã«ææžåãããŠããããšã確èªããŸãã
- äžå åãããã³ã³ããŒãã³ãã©ã€ãã©ãª: åå©çšãšäžè²«æ§ãä¿é²ããããã«ãäžå åãããã³ã³ããŒãã³ãã©ã€ãã©ãªãç¶æããŸãã
Bitã®ãããªããŒã«ã¯ãç°ãªããããžã§ã¯ããããŒã éã§Webã³ã³ããŒãã³ãã管çã»å ±æããã®ã«åœ¹ç«ã¡ãŸãã
äŸïŒå€èšèªå¯Ÿå¿Webã³ã³ããŒãã³ãã®æ§ç¯
ç°ãªãèšèªã§ããã¹ãã衚瀺ããåçŽãªWebã³ã³ããŒãã³ããäœæããŠã¿ãŸãããããã®äŸã§ã¯ãåœéåã®ããã«i18nextã©ã€ãã©ãªã䜿çšããŸãã
// i18n-component.js
import i18next from 'i18next';
class I18nComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
async connectedCallback() {
await i18next.init({
lng: 'en',
resources: {
en: {
translation: {
greeting: 'Hello, World!'
}
},
fr: {
translation: {
greeting: 'Bonjour le monde !'
}
},
es: {
translation: {
greeting: '¡Hola Mundo!'
}
}
}
});
this.render();
}
static get observedAttributes() {
return ['language'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'language' && oldValue !== newValue) {
i18next.changeLanguage(newValue);
this.render();
}
}
render() {
this.shadowRoot.innerHTML = `
${i18next.t('greeting')}
`;
}
get language() {
return this.getAttribute('language');
}
set language(value) {
this.setAttribute('language', value);
}
}
customElements.define('i18n-component', I18nComponent);
ãã®ã³ã³ããŒãã³ãã䜿çšããã«ã¯ãJavaScriptãã¡ã€ã«ãã€ã³ã¯ã«ãŒãããæ¬¡ã®ã¿ã°ã远å ããŸãïŒ
çµè«
å ç¢ãªWebã³ã³ããŒãã³ãã€ã³ãã©ã®æ§ç¯ã«ã¯ãæ éãªèšç»ãèšèšãå®è£ ãå¿ èŠã§ãããã®ã¬ã€ãã§æŠèª¬ããååãšãã¹ããã©ã¯ãã£ã¹ã«åŸãããšã§ãçµç¹ã®ããã«ã¹ã±ãŒã©ãã«ã§ä¿å®æ§ãé«ããäžè²«æ§ã®ããWebã³ã³ããŒãã³ããšã³ã·ã¹ãã ãäœæã§ããŸããåå©çšæ§ãã«ãã»ã«åãã¢ã¯ã»ã·ããªãã£ãããã©ãŒãã³ã¹ãåªå ããããšãå¿ããªãã§ãã ãããããŒã«ãšèªååãæŽ»çšããŠéçºã¯ãŒã¯ãããŒãåçåããå€åããããŒãºã«åºã¥ããŠã€ã³ãã©ãç¶ç¶çã«æ¹åããŠãã ãããWebéçºã®ç¶æ³ãé²åãç¶ããäžã§ãææ°ã®Webã³ã³ããŒãã³ãæšæºãšãã¹ããã©ã¯ãã£ã¹ã«åžžã«ç²ŸéããŠããããšã¯ãã°ããŒãã«ãªèŠèŽè ã«å¯Ÿå¿ããçŸä»£çã§é«å質ãªWebã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã«äžå¯æ¬ ã§ãã