Web Components とは?2025 年に知っておくべき基礎知識を徹底解説

Web標準の力によって、フロントエンド開発に革命をもたらすWeb Components。2025年の今、なぜこの技術が再び注目を集めているのでしょうか。
フレームワークの枠を超えて再利用可能なコンポーネントを作る。それは多くの開発者が長年夢見てきたことです。Web Componentsは、その夢を現実にする標準技術として、着実に成熟を続けています。
背景
Webフロントエンド開発の歴史と変遷
Webフロントエンド開発は、この20年間で劇的な変化を遂げました。かつてはjQueryでDOM操作を行っていた時代から、今ではReact、Vue.js、Angularといったフレームワークが主流となっています。
しかし、この進化の過程で一つの大きな課題が浮き彫りになりました。コンポーネントの再利用性です。
コンポーネント化の必要性
現代のWeb開発では、UIをコンポーネント単位で構築することが当たり前になりました。この背景には、以下のような開発上のメリットがあります。
- 保守性の向上:小さな単位での管理が可能
- 再利用性の確保:一度作ったコンポーネントを他の場所でも使用
- テストの容易さ:独立したユニットでのテスト実行
- チーム開発の効率化:役割分担とコードの分離
しかし、フレームワーク固有のコンポーネントシステムでは、その恩恵を十分に受けることができませんでした。
課題
フレームワーク依存という壁
従来のコンポーネント開発では、深刻な問題に直面することがあります。
javascript// React でのコンポーネント例
function MyButton({ onClick, children }) {
return (
<button className="my-button" onClick={onClick}>
{children}
</button>
);
}
上記のようなReactコンポーネントは、React環境でしか動作しません。Vue.jsプロジェクトで使いたくても、全く別の形で実装し直す必要があります。
実際の開発現場で起こる問題
多くの開発チームが以下のような課題に悩まされています。
# | 課題 | 具体的な問題 |
---|---|---|
1 | フレームワーク移行時の工数 | コンポーネントの全面的な書き直し |
2 | マルチブランド対応の困難 | 同じUIを異なる技術スタックで再実装 |
3 | デザインシステムの統一性 | フレームワークごとに異なる実装方法 |
4 | 学習コストの増大 | フレームワーク固有の記法を習得 |
レガシーシステムとの統合問題
特に企業の大規模システムでは、以下のようなエラーに遭遇することがよくあります。
javascriptError: Cannot resolve dependency 'react' in legacy system
at WebpackModuleFactory.create
これは、新しいReactコンポーネントを既存のjQueryベースのシステムに組み込もうとした際に発生する典型的なエラーです。フレームワーク依存のコンポーネントは、このような統合時に大きな障壁となります。
解決策
Web Componentsが提供する標準技術による解決
Web Componentsは、これらの課題を根本的に解決する標準技術です。W3Cによって策定されたこの仕様は、ブラウザ自体がコンポーネント化をサポートすることを可能にします。
最も重要なのは、Web Componentsで作られたコンポーネントはフレームワークに依存しないということです。
javascript// Web Components での同様の実装
class MyButton extends HTMLElement {
constructor() {
super();
this.addEventListener('click', this.handleClick);
}
handleClick(event) {
this.dispatchEvent(new CustomEvent('button-click', {
detail: { originalEvent: event }
}));
}
}
customElements.define('my-button', MyButton);
このコンポーネントは、React、Vue.js、Angular、さらには素のJavaScriptプロジェクトでも同じように動作します。
標準技術だからこその安心感
Web Componentsの最大の魅力は、ブラウザの標準機能であることです。フレームワークのバージョンアップや廃止に左右されることなく、長期的に安定して使用できます。
実際に、2025年現在、主要ブラウザでのサポート率は98%を超えており、production環境での使用も安心して行えます。
Web Components の基礎知識
Custom Elements とは
Custom Elementsは、独自のHTMLタグを作成できる仕組みです。<my-button>
のような、あなただけのHTMLタグを定義できます。
javascript// 基本的なCustom Elementの定義
class WelcomeMessage extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<div class="welcome">
<h2>ようこそ!</h2>
<p>Web Componentsの世界へ</p>
</div>
`;
}
}
// カスタムタグとして登録
customElements.define('welcome-message', WelcomeMessage);
上記のコードを実行すると、HTMLで<welcome-message></welcome-message>
と書くだけで、ウェルカムメッセージが表示されます。
ライフサイクルメソッドの活用
Custom Elementsには、コンポーネントの状態変化に応じて自動的に呼び出されるメソッドが用意されています。
javascriptclass LifecycleDemo extends HTMLElement {
// 要素がDOMに追加された時
connectedCallback() {
console.log('コンポーネントが接続されました');
this.render();
}
// 要素がDOMから削除された時
disconnectedCallback() {
console.log('コンポーネントが切断されました');
this.cleanup();
}
// 属性が変更された時
attributeChangedCallback(name, oldValue, newValue) {
console.log(`属性 ${name} が ${oldValue} から ${newValue} に変更`);
this.handleAttributeChange(name, newValue);
}
}
これらのライフサイクルメソッドを適切に使用することで、リソースの管理やイベントリスナーのクリーンアップなど、堅牢なコンポーネントを作成できます。
Shadow DOM の仕組み
Shadow DOMは、コンポーネント内部のスタイルとDOMを外部から隔離する技術です。これにより、真のカプセル化が実現できます。
javascriptclass EncapsulatedComponent extends HTMLElement {
constructor() {
super();
// Shadow DOM を作成
this.attachShadow({ mode: 'open' });
// 内部のスタイルとHTML
this.shadowRoot.innerHTML = `
<style>
.container {
background: #f0f0f0;
padding: 20px;
border-radius: 8px;
}
.title {
color: #333;
font-size: 18px;
}
</style>
<div class="container">
<h3 class="title">隔離されたコンポーネント</h3>
<slot></slot>
</div>
`;
}
}
Shadow DOM内のスタイルは、外部のCSSに影響を与えることも、外部から影響を受けることもありません。
Shadow DOM のメリット
Shadow DOMの導入により、以下のような問題を解決できます。
# | 従来の問題 | Shadow DOM での解決 |
---|---|---|
1 | CSS の競合 | 完全に隔離された空間 |
2 | グローバル汚染 | 内部実装の隠蔽 |
3 | スタイルの継承問題 | 明示的な継承制御 |
4 | セレクター衝突 | スコープ化されたCSS |
HTML Templates の活用
HTML Templatesは、再利用可能なHTMLの雛形を作成する仕組みです。<template>
タグで定義したHTMLは、実際に使用されるまでレンダリングされません。
html<!-- HTMLでテンプレートを定義 -->
<template id="user-card-template">
<style>
.user-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
margin: 8px 0;
}
.avatar {
width: 50px;
height: 50px;
border-radius: 50%;
}
</style>
<div class="user-card">
<img class="avatar" src="" alt="">
<h3 class="name"></h3>
<p class="email"></p>
</div>
</template>
JavaScriptでテンプレートを利用するコンポーネントは以下のようになります。
javascriptclass UserCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
// テンプレートを取得してクローン
const template = document.getElementById('user-card-template');
const clone = template.content.cloneNode(true);
this.shadowRoot.appendChild(clone);
}
connectedCallback() {
this.updateUserInfo();
}
updateUserInfo() {
const name = this.getAttribute('name') || 'Unknown';
const email = this.getAttribute('email') || '';
const avatar = this.getAttribute('avatar') || 'default-avatar.png';
this.shadowRoot.querySelector('.name').textContent = name;
this.shadowRoot.querySelector('.email').textContent = email;
this.shadowRoot.querySelector('.avatar').src = avatar;
}
}
HTML Imports の現状(廃止経緯含む)
HTML Importsは、当初Web Componentsの仕様に含まれていましたが、2019年に廃止されました。これは技術的な判断によるものです。
html<!-- 廃止されたHTML Imports の記法 -->
<link rel="import" href="my-component.html">
廃止の理由として、以下が挙げられます。
- ES Modules との重複:モジュールシステムとしてES Modulesが標準化
- ブラウザサポートの限定:Chrome以外での実装が進まない
- パフォーマンス問題:ネットワークリクエストの増加
ES Modules による代替手段
現在では、ES Modulesを使用してコンポーネントをインポートするのが一般的です。
javascript// modern approach with ES Modules
import './components/user-card.js';
import './components/welcome-message.js';
// コンポーネントの使用
document.body.innerHTML = `
<welcome-message></welcome-message>
<user-card name="田中太郎" email="tanaka@example.com"></user-card>
`;
この方式は、モジュールバンドラー(webpack、Rollup、Viteなど)との親和性も高く、現代的な開発ワークフローに適合します。
具体例
シンプルなカスタム要素の実装
実際に動作するシンプルなカウンターコンポーネントを作成してみましょう。このコンポーネントは、クリックするたびに数値が増加する機能を持ちます。
javascriptclass SimpleCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.attachShadow({ mode: 'open' });
this.render();
}
render() {
this.shadowRoot.innerHTML = `
<style>
.counter {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 16px;
border: 2px solid #007acc;
border-radius: 8px;
font-family: Arial, sans-serif;
}
.count {
font-size: 24px;
font-weight: bold;
color: #007acc;
min-width: 30px;
text-align: center;
}
</style>
<div class="counter">
<button id="increment">+</button>
<span class="count">${this.count}</span>
<button id="decrement">-</button>
</div>
`;
this.addEventListeners();
}
}
イベントリスナーの追加とカウント機能の実装を行います。
javascriptclass SimpleCounter extends HTMLElement {
// ... 前のコードに続いて
addEventListeners() {
const incrementBtn = this.shadowRoot.getElementById('increment');
const decrementBtn = this.shadowRoot.getElementById('decrement');
incrementBtn.addEventListener('click', () => {
this.count++;
this.updateCount();
this.dispatchCountChangeEvent();
});
decrementBtn.addEventListener('click', () => {
this.count--;
this.updateCount();
this.dispatchCountChangeEvent();
});
}
updateCount() {
const countElement = this.shadowRoot.querySelector('.count');
countElement.textContent = this.count;
}
dispatchCountChangeEvent() {
this.dispatchEvent(new CustomEvent('count-changed', {
detail: { count: this.count },
bubbles: true
}));
}
}
// カスタム要素として登録
customElements.define('simple-counter', SimpleCounter);
使用方法とイベントハンドリング
作成したコンポーネントは、HTMLで以下のように使用できます。
html<!DOCTYPE html>
<html>
<head>
<title>Counter Example</title>
</head>
<body>
<h1>Web Components カウンター</h1>
<simple-counter></simple-counter>
<script>
// カウント変更イベントをリスニング
document.addEventListener('count-changed', (event) => {
console.log('カウントが変更されました:', event.detail.count);
});
</script>
<script src="simple-counter.js"></script>
</body>
</html>
Shadow DOM を使ったスタイルカプセル化
より高度なスタイルカプセル化の例として、テーマ切り替え機能を持つカードコンポーネントを作成します。
javascriptclass ThemeCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.currentTheme = this.getAttribute('theme') || 'light';
this.render();
}
static get observedAttributes() {
return ['theme', 'title', 'content'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
if (name === 'theme') {
this.currentTheme = newValue;
}
this.render();
}
}
}
テーマに応じたスタイリングとレンダリングメソッドを実装します。
javascriptclass ThemeCard extends HTMLElement {
// ... 前のコードに続いて
getThemeStyles() {
const themes = {
light: {
background: '#ffffff',
color: '#333333',
border: '#e0e0e0',
shadow: '0 2px 4px rgba(0,0,0,0.1)'
},
dark: {
background: '#2d2d2d',
color: '#ffffff',
border: '#555555',
shadow: '0 2px 4px rgba(0,0,0,0.3)'
},
blue: {
background: '#e3f2fd',
color: '#1565c0',
border: '#90caf9',
shadow: '0 2px 4px rgba(21,101,192,0.2)'
}
};
return themes[this.currentTheme] || themes.light;
}
render() {
const styles = this.getThemeStyles();
const title = this.getAttribute('title') || 'タイトル';
const content = this.getAttribute('content') || 'コンテンツ';
this.shadowRoot.innerHTML = `
<style>
.card {
background: ${styles.background};
color: ${styles.color};
border: 1px solid ${styles.border};
box-shadow: ${styles.shadow};
border-radius: 12px;
padding: 24px;
margin: 16px 0;
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.title {
font-size: 20px;
font-weight: bold;
margin-bottom: 12px;
}
.content {
line-height: 1.6;
}
.theme-toggle {
margin-top: 16px;
display: flex;
gap: 8px;
}
button {
padding: 6px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
background: ${styles.border};
color: ${styles.color};
}
</style>
<div class="card">
<div class="title">${title}</div>
<div class="content">${content}</div>
<div class="theme-toggle">
<button onclick="this.getRootNode().host.setAttribute('theme', 'light')">Light</button>
<button onclick="this.getRootNode().host.setAttribute('theme', 'dark')">Dark</button>
<button onclick="this.getRootNode().host.setAttribute('theme', 'blue')">Blue</button>
</div>
</div>
`;
}
}
customElements.define('theme-card', ThemeCard);
Template 要素の実践的な使用例
複雑なレイアウトを持つコンポーネントでは、HTMLテンプレートを活用することで、保守性を大幅に向上させることができます。
html<!-- product-list-template.html -->
<template id="product-item-template">
<style>
.product-item {
display: flex;
align-items: center;
padding: 16px;
border-bottom: 1px solid #eee;
transition: background-color 0.2s;
}
.product-item:hover {
background-color: #f8f9fa;
}
.product-image {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 8px;
margin-right: 16px;
}
.product-info {
flex: 1;
}
.product-name {
font-size: 18px;
font-weight: bold;
margin-bottom: 4px;
}
.product-price {
font-size: 16px;
color: #e74c3c;
font-weight: bold;
}
.product-description {
color: #666;
font-size: 14px;
margin-bottom: 8px;
}
</style>
<div class="product-item">
<img class="product-image" src="" alt="">
<div class="product-info">
<div class="product-name"></div>
<div class="product-description"></div>
<div class="product-price"></div>
</div>
</div>
</template>
テンプレートを使用するコンポーネントの実装は以下のようになります。
javascriptclass ProductItem extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.loadTemplate();
}
loadTemplate() {
const template = document.getElementById('product-item-template');
if (!template) {
console.error('product-item-template not found');
return;
}
const clone = template.content.cloneNode(true);
this.shadowRoot.appendChild(clone);
}
connectedCallback() {
this.updateProductInfo();
}
static get observedAttributes() {
return ['name', 'price', 'image', 'description'];
}
attributeChangedCallback() {
this.updateProductInfo();
}
}
商品情報の更新とエラーハンドリングを含む完全な実装です。
javascriptclass ProductItem extends HTMLElement {
// ... 前のコードに続いて
updateProductInfo() {
if (!this.shadowRoot) return;
try {
const name = this.getAttribute('name') || '商品名未設定';
const price = this.getAttribute('price') || '0';
const image = this.getAttribute('image') || 'placeholder.jpg';
const description = this.getAttribute('description') || '';
const nameElement = this.shadowRoot.querySelector('.product-name');
const priceElement = this.shadowRoot.querySelector('.product-price');
const imageElement = this.shadowRoot.querySelector('.product-image');
const descElement = this.shadowRoot.querySelector('.product-description');
if (nameElement) nameElement.textContent = name;
if (priceElement) priceElement.textContent = `¥${parseInt(price).toLocaleString()}`;
if (descElement) descElement.textContent = description;
if (imageElement) {
imageElement.src = image;
imageElement.alt = name;
imageElement.onerror = () => {
imageElement.src = 'assets/placeholder.jpg';
};
}
} catch (error) {
console.error('Error updating product info:', error);
this.displayError();
}
}
displayError() {
this.shadowRoot.innerHTML = `
<div style="color: red; padding: 16px;">
商品情報の読み込みに失敗しました
</div>
`;
}
}
customElements.define('product-item', ProductItem);
2025年における Web Components
最新ブラウザサポート状況
2025年現在、Web Componentsの各仕様のブラウザサポート状況は非常に良好です。開発者にとって安心して使用できる環境が整っています。
仕様 | Chrome | Firefox | Safari | Edge | サポート率 |
---|---|---|---|---|---|
Custom Elements v1 | ✅ 54+ | ✅ 63+ | ✅ 10.1+ | ✅ 79+ | 98.2% |
Shadow DOM v1 | ✅ 53+ | ✅ 63+ | ✅ 10.1+ | ✅ 79+ | 97.8% |
HTML Templates | ✅ 26+ | ✅ 22+ | ✅ 8+ | ✅ 13+ | 99.1% |
ES Modules | ✅ 61+ | ✅ 60+ | ✅ 10.1+ | ✅ 16+ | 97.5% |
企業での実際の導入事例
多くの大手企業が、Web Componentsを本格的に導入し始めています。
Google: YouTube の UI コンポーネントの一部で使用 Microsoft: Office 365 の Web版で段階的に導入 Adobe: Creative Cloud の Web アプリケーションで活用 Netflix: 管理画面のコンポーネントライブラリとして採用
パフォーマンス面での優位性
Web Componentsは、フレームワークのオーバーヘッドがないため、軽量で高速です。
javascript// パフォーマンス測定の例
console.time('component-creation');
// 1000個のWebComponentsを作成
for (let i = 0; i < 1000; i++) {
const element = document.createElement('simple-counter');
document.body.appendChild(element);
}
console.timeEnd('component-creation');
// 通常、フレームワーク版より30-50%高速
フレームワークとの関係性
Web Componentsは、既存のフレームワークを置き換えるものではありません。むしろ、フレームワークと共存し、補完する関係にあります。
React との統合
ReactプロジェクトでWeb Componentsを使用する場合、いくつか注意点があります。
javascript// React でのWebComponents使用例
import React, { useRef, useEffect } from 'react';
function ReactApp() {
const counterRef = useRef(null);
useEffect(() => {
const handleCountChange = (event) => {
console.log('Count changed:', event.detail.count);
};
const element = counterRef.current;
if (element) {
element.addEventListener('count-changed', handleCountChange);
return () => {
element.removeEventListener('count-changed', handleCountChange);
};
}
}, []);
return (
<div>
<h1>React + Web Components</h1>
<simple-counter ref={counterRef}></simple-counter>
</div>
);
}
Vue.js との統合
Vue.js は Web Components との相性が非常に良く、設定なしで使用できます。
javascript// Vue.js でのWebComponents使用例
<template>
<div>
<h1>Vue + Web Components</h1>
<simple-counter @count-changed="handleCountChange"></simple-counter>
</div>
</template>
<script>
export default {
methods: {
handleCountChange(event) {
console.log('Count changed:', event.detail.count);
}
}
}
</script>
実用的な導入判断基準
Web Componentsの導入を検討する際の判断基準をまとめました。
導入を推奨するケース
-
デザインシステム構築
- 複数のプロジェクトで共通のコンポーネントを使用
- ブランド統一を重視する企業
-
レガシーシステムの段階的モダン化
- 既存のjQueryシステムに新しいUIを追加
- フレームワーク移行の橋渡し
-
マイクロフロントエンド構成
- 異なるチームが異なる技術で開発
- コンポーネント単位での独立性が重要
慎重な検討が必要なケース
-
小規模単一フレームワークプロジェクト
- ReactのみでWebアプリを構築
- チーム全体がReactに精通している
-
複雑な状態管理が必要
- Redux/Vuex等との連携が重要
- フレームワークのエコシステムに依存
-
開発速度を最優先する場合
- プロトタイプやMVP開発
- 短期間でのリリースが必要
ツールチェーンとの統合
現代的な開発ワークフローにWeb Componentsを組み込む方法を説明します。
Vite での設定例
javascript// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
build: {
lib: {
entry: 'src/components/index.js',
name: 'MyComponents',
fileName: 'my-components'
},
rollupOptions: {
external: [],
output: {
globals: {}
}
}
}
});
TypeScript サポート
Web ComponentsをTypeScriptで記述することで、型安全性を確保できます。
typescript// TypeScript でのWebComponents定義
interface CountChangedDetail {
count: number;
}
class TypedCounter extends HTMLElement {
private count: number = 0;
private shadowRoot: ShadowRoot;
constructor() {
super();
this.shadowRoot = this.attachShadow({ mode: 'open' });
this.render();
}
private dispatchCountChangeEvent(): void {
const event = new CustomEvent<CountChangedDetail>('count-changed', {
detail: { count: this.count },
bubbles: true
});
this.dispatchEvent(event);
}
private render(): void {
this.shadowRoot.innerHTML = `
<button>Count: ${this.count}</button>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'typed-counter': TypedCounter;
}
}
単体テストの書き方
Web Componentsは、標準的なDOMテストツールでテストできます。
javascript// Jest でのテスト例
describe('SimpleCounter', () => {
let element;
beforeEach(() => {
element = document.createElement('simple-counter');
document.body.appendChild(element);
});
afterEach(() => {
document.body.removeChild(element);
});
test('初期カウントは0である', () => {
const countElement = element.shadowRoot.querySelector('.count');
expect(countElement.textContent).toBe('0');
});
test('インクリメントボタンでカウントが増加する', () => {
const incrementBtn = element.shadowRoot.getElementById('increment');
incrementBtn.click();
const countElement = element.shadowRoot.querySelector('.count');
expect(countElement.textContent).toBe('1');
});
test('count-changed イベントが発火される', (done) => {
element.addEventListener('count-changed', (event) => {
expect(event.detail.count).toBe(1);
done();
});
const incrementBtn = element.shadowRoot.getElementById('increment');
incrementBtn.click();
});
});
よくあるエラーと対処法
実際の開発では、以下のようなエラーに遭遇することがあります。
カスタム要素名のエラー
vbnetDOMException: Failed to execute 'define' on 'CustomElementRegistry':
"mycomponent" is not a valid custom element name
解決策: カスタム要素名には必ずハイフンを含める
javascript// ❌ 間違い
customElements.define('mycomponent', MyComponent);
// ✅ 正しい
customElements.define('my-component', MyComponent);
Shadow DOM アクセスエラー
javascriptTypeError: Cannot read property 'querySelector' of null
解決策: Shadow DOM の初期化タイミングを確認
javascriptclass MyComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
// ここではまだ shadowRoot.innerHTML を設定していない
}
connectedCallback() {
// DOM接続後にレンダリング
this.render();
// ここでquerySelector等を実行
}
}
まとめ
Web Componentsは、2025年の今、フロントエンド開発において無視できない重要な技術となりました。フレームワークに依存しない標準技術として、長期的な視点での開発戦略に欠かせない存在です。
Web Components がもたらす価値
真の再利用性: フレームワークの枠を超えた、本当の意味でのコンポーネント再利用が可能になります。
持続可能な開発: ブラウザ標準だからこそ、技術的負債を減らし、長期的にメンテナンスしやすいコードベースを構築できます。
段階的な導入: 既存システムを大幅に変更することなく、必要な部分から段階的に導入できる柔軟性があります。
これからのフロントエンド開発で大切なこと
技術選択は、常に目的と手段を明確に分けることが重要です。Web Componentsは万能ではありませんが、適切な場面で使用することで、開発チームに大きな価値をもたらします。
特に、複数のプロジェクトやチームをまたいだコンポーネント共有、レガシーシステムとの共存、そして将来への投資という観点で、Web Componentsは強力な選択肢となるでしょう。
学習の次のステップ
Web Componentsを実際のプロジェクトで活用するために、以下のステップで学習を進めることをお勧めします。
- 小さなコンポーネントから始める: ボタンやカードなど、シンプルなUIから
- 既存プロジェクトに段階的に導入: 一部分から始めて効果を検証
- ツールチェーンとの統合: TypeScript、テスト、ビルドツールとの組み合わせ
- パフォーマンス測定: 実際の効果を数値で確認
Web Componentsは、あなたの開発体験を変える可能性を秘めています。標準技術という安心感と、フレームワークに縛られない自由度を手に入れて、より良いWeb開発の未来を一緒に築いていきましょう。
関連リンク
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来