ai

Web Components Scaling Strategy วิธี Scale

Web Components Scaling Strategy วิธี Scale

Web Components Scaling Strategy วิธี Scale

Web Components Scaling Strategy วิธี Scale

Web Components เป็น web standard ที่ช่วยสร้าง reusable custom HTML elements ด้วย Shadow DOM, Custom Elements, HTML Templates และ ES Modules ทำให้สร้าง UI components ที่ encapsulated ใช้ได้กับทุก framework (React, Vue, Angular) การ scale Web Components สำหรับ design systems ขนาดใหญ่ต้องวางแผน architecture, versioning, testing และ distribution อย่างเป็นระบบ บทความนี้อธิบาย scaling strategies ตั้งแต่ monorepo setup จนถึง CDN distribution พร้อม code examples

Web Components Fundamentals

// basic_component.js — Basic Web Component
class MyButton extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  static get observedAttributes() {
    return ['variant', 'size', 'disabled'];
  }

  connectedCallback() {
    this.render();
  }

  attributeChangedCallback(name, oldVal, newVal) {
    if (oldVal !== newVal) this.render();
  }

  get variant() { return this.getAttribute('variant') || 'primary'; }
  get size() { return this.getAttribute('size') || 'medium'; }

  render() {
    const styles = {
      primary: 'background: #3b82f6; color: white;',
      secondary: 'background: #6b7280; color: white;',
      outline: 'background: transparent; border: 2px solid #3b82f6; color: #3b82f6;',
    };
    const sizes = {
      small: 'padding: 4px 12px; font-size: 12px;',
      medium: 'padding: 8px 16px; font-size: 14px;',
      large: 'padding: 12px 24px; font-size: 16px;',
    };

    this.shadowRoot.innerHTML = `
      <style>
        button {

          border-radius: 6px;
          cursor: pointer;
          font-family: inherit;
          transition: opacity 0.2s;
        }
        button:hover { opacity: 0.9; }
        button:disabled { opacity: 0.5; cursor: not-allowed; }
      </style>
      <button >
        <slot></slot>
      </button>
    `;
  }
}

customElements.define('my-button', MyButton);
// Usage: <my-button variant="primary" size="large">Click Me</my-button>

Monorepo Architecture

# monorepo.py — Monorepo structure for Web Components
import json

class MonorepoStructure:
    STRUCTURE = """
# Design System Monorepo Structure
design-system/
├── packages/
│   ├── core/                   # Core utilities, tokens, mixins
│   │   ├── src/
│   │   │   ├── tokens.js       # Design tokens (colors, spacing)
│   │   │   ├── mixins.js       # Shared CSS mixins
│   │   │   └── utils.js        # Shared utilities
│   │   └── package.json
│   ├── button/                 # Individual component package
│   │   ├── src/
│   │   │   ├── button.js
│   │   │   ├── button.css
│   │   │   └── button.test.js
│   │   ├── stories/
│   │   │   └── button.stories.js
│   │   └── package.json
│   ├── input/
│   ├── modal/
│   ├── table/
│   └── ...
├── apps/
│   ├── storybook/              # Component documentation
│   ├── playground/             # Testing playground
│   └── docs/                   # Documentation site
├── tools/
│   ├── build/                  # Build scripts
│   ├── generators/             # Component generators
│   └── testing/                # Test utilities
├── lerna.json / pnpm-workspace.yaml
├── turbo.json                  # Turborepo config
└── package.json
"""

    TOOLS = {
        "pnpm": {"name": "pnpm Workspaces", "use": "Package management — fast, disk-efficient"},
        "turborepo": {"name": "Turborepo", "use": "Build orchestration — caching, parallel builds"},
        "changeset": {"name": "Changesets", "use": "Versioning + changelog — per-package semver"},
        "storybook": {"name": "Storybook", "use": "Component documentation + visual testing"},
    }

    def show_structure(self):
        print("=== Monorepo Structure ===")
        print(self.STRUCTURE[:600])

    def show_tools(self):
        print(f"\n=== Recommended Tools ===")
        for key, tool in self.TOOLS.items():
            print(f"  [{tool['name']}] {tool['use']}")

mono = MonorepoStructure()
mono.show_structure()
mono.show_tools()

Scaling Strategies

Web Components Scaling Strategy วิธี Scale
# scaling.py — Scaling strategies for Web Components
import json

class ScalingStrategies:
    STRATEGIES = {
        "lazy_loading": {
            "name": "1. Lazy Loading Components",
            "description": "โหลด component เมื่อต้องใช้จริง — ลด initial bundle size",
            "code": """
// lazy-loader.js
const lazyDefine = (tagName, importFn) => {
  if (customElements.get(tagName)) return;
  
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(async (entry) => {
      if (entry.isIntersecting) {
        const module = await importFn();
        if (!customElements.get(tagName)) {
          customElements.define(tagName, module.default);
        }
        observer.unobserve(entry.target);
      }
    });
  });
  
  document.querySelectorAll(tagName).forEach(el => observer.observe(el));
};

// Usage
lazyDefine('heavy-chart', () => import('./components/chart.js'));
lazyDefine('data-table', () => import('./components/table.js'));
""",
        },
        "tree_shaking": {
            "name": "2. Tree Shaking & Code Splitting",
            "description": "Import เฉพาะ components ที่ใช้ — Rollup/Webpack tree-shake ส่วนที่ไม่ใช้ออก",
            "code": "import { MyButton } from '@design-system/button'; // Only button code",
        },
        "design_tokens": {
            "name": "3. Design Tokens",
            "description": "แยก design values (colors, spacing) เป็น tokens — เปลี่ยน theme ง่าย",
            "code": "CSS Custom Properties: --ds-color-primary: #3b82f6;",
        },
        "versioning": {
            "name": "4. Independent Versioning",
            "description": "แต่ละ component มี version แยก — update component เดียวไม่กระทบอื่น",
            "code": "@design-system/button@2.1.0, @design-system/input@1.5.0",
        },
        "cdn_distribution": {
            "name": "5. CDN Distribution",
            "description": "Publish ไป CDN — ใช้ได้ทันทีด้วย script tag ไม่ต้อง build",
            "code": '',
        },
    }

    def show_strategies(self):
        print("=== Scaling Strategies ===\n")
        for key, strat in self.STRATEGIES.items():
            print(f"[{strat['name']}]")
            print(f"  {strat['description']}")
            print()

    def bundle_comparison(self):
        print("=== Bundle Size Comparison ===")
        print(f"  All components (no optimization): ~250KB")
        print(f"  Tree-shaken (import what you use):  ~30KB")
        print(f"  Lazy-loaded (on demand):             ~5KB initial")
        print(f"  CDN + HTTP/2:                        ~2KB per component (cached)")

scale = ScalingStrategies()
scale.show_strategies()
scale.bundle_comparison()

Testing Strategy

# testing.py — Testing strategy for Web Components
import json

class TestingStrategy:
    LAYERS = {
        "unit": {
            "name": "Unit Tests",
            "tool": "Web Test Runner + @open-wc/testing",
            "what": "Test component logic, attributes, events",
            "coverage": "80%+ per component",
        },
        "visual": {
            "name": "Visual Regression Tests",
            "tool": "Storybook + Chromatic / Percy",
            "what": "Screenshot comparison — catch visual changes",
            "coverage": "ทุก component + variants",
        },
        "a11y": {
            "name": "Accessibility Tests",
            "tool": "axe-core + Storybook a11y addon",
            "what": "WCAG compliance, keyboard navigation, screen reader",
            "coverage": "ทุก interactive component",
        },
        "integration": {
            "name": "Integration Tests",
            "tool": "Playwright / Cypress",
            "what": "Test components ในแต่ละ framework (React, Vue, Angular)",
            "coverage": "Critical components + framework wrappers",
        },
    }

    UNIT_TEST = """
// button.test.js — Unit test with @open-wc/testing
import { html, fixture, expect } from '@open-wc/testing';
import '../src/my-button.js';

describe('MyButton', () => {
  it('renders with default props', async () => {
    const el = await fixture(html`Click`);
    expect(el.variant).to.equal('primary');
    expect(el.size).to.equal('medium');
  });

  it('reflects variant attribute', async () => {
    const el = await fixture(html`Click`);
    const button = el.shadowRoot.querySelector('button');
    expect(button).to.exist;
    expect(el.variant).to.equal('secondary');
  });

  it('dispatches click event', async () => {
    const el = await fixture(html`Click`);
    let clicked = false;
    el.addEventListener('click', () => { clicked = true; });
    el.shadowRoot.querySelector('button').click();
    expect(clicked).to.be.true;
  });

  it('is accessible', async () => {
    const el = await fixture(html`Click Me`);
    await expect(el).to.be.accessible();
  });
});
"""

    def show_layers(self):
        print("=== Testing Layers ===\n")
        for key, layer in self.LAYERS.items():
            print(f"[{layer['name']}] Tool: {layer['tool']}")
            print(f"  What: {layer['what']}")
            print()

    def show_unit_test(self):
        print("=== Unit Test Example ===")
        print(self.UNIT_TEST[:500])

test = TestingStrategy()
test.show_layers()
test.show_unit_test()

CI/CD & Distribution

# cicd.py — CI/CD pipeline for Web Components
import json

class CICD:
    PIPELINE = """
# .github/workflows/release.yml
name: Release
on:
  push:
    branches: [main]

jobs:
  build-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - run: pnpm install
      - run: pnpm turbo build        # Build all packages
      - run: pnpm turbo test          # Run all tests
      - run: pnpm turbo lint          # Lint all packages

  release:
    needs: build-test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - run: pnpm install

      # Changesets — version + publish
      - uses: changesets/action@v1
        with:
          publish: pnpm changeset publish
        env:
          NPM_TOKEN: }
          GITHUB_TOKEN: }

  cdn-deploy:
    needs: release
    runs-on: ubuntu-latest
    steps:
      - run: pnpm turbo build --filter=@ds/*
      - run: aws s3 sync dist/ s3://cdn.example.com/ds/ --cache-control "public, max-age=31536000"
      - run: aws cloudfront create-invalidation --distribution-id $CDN_ID --paths "/ds/*"
"""

    DISTRIBUTION = {
        "npm": "npm publish — สำหรับ developers (import ใน build)",
        "cdn": "CDN (S3 + CloudFront / unpkg) — สำหรับ script tag",
        "storybook": "Storybook (Chromatic) — สำหรับ documentation + design review",
        "figma": "Figma plugin sync — สำหรับ designers",
    }

    def show_pipeline(self):
        print("=== CI/CD Pipeline ===")
        print(self.PIPELINE[:500])

    def show_distribution(self):
        print(f"\n=== Distribution Channels ===")
        for channel, desc in self.DISTRIBUTION.items():
            print(f"  [{channel}] {desc}")

cicd = CICD()
cicd.show_pipeline()
cicd.show_distribution()

FAQ - คำถามที่พบบ่อย

Q: Web Components กับ React Components ต่างกัน?

A: Web Components: web standard, ใช้ได้ทุก framework, Shadow DOM encapsulation React Components: React-specific, ใช้ได้เฉพาะ React, virtual DOM ข้อดี Web Components: framework-agnostic, future-proof, native browser support ข้อเสีย: DX ไม่ดีเท่า React, ecosystem เล็กกว่า ใช้ Web Components: design system ที่ต้องใช้ข้าม frameworks

เนื้อหาเกี่ยวข้อง — ดูเพิ่มเติมเรื่อง LocalAI Self-hosted Machine Learning Pipeline

Q: ใช้ library อะไรสร้าง Web Components ดี?

แนะนำเพิ่มเติม — ติดตาม XM Signal

A: Lit (Google): เบา (~5KB), reactive properties, templates — แนะนำมากที่สุด Stencil (Ionic): compiler-based, TypeScript, lazy-loading built-in Fast (Microsoft): enterprise-grade, accessibility built-in Vanilla: ไม่ใช้ library — เหมาะ component ง่ายๆ เริ่มต้น: Lit (ง่ายที่สุด, community ใหญ่, Google สนับสนุน)

เนื้อหาเกี่ยวข้อง — ดูเพิ่มเติมเรื่อง PlanetScale Vitess Domain Driven Design DDD

Q: Scale ยังไงให้รองรับ 100+ components?

A: 1) Monorepo (pnpm + Turborepo) — จัดการ packages 2) Independent versioning (Changesets) — update ทีละ component 3) Lazy loading — โหลดเมื่อใช้ 4) Tree shaking — bundle เฉพาะที่ import 5) CDN distribution — cache + parallel download 6) Visual regression testing — ป้องกัน breaking changes

แนะนำเพิ่มเติม — คู่มือเทรดจาก SiamCafeBook

เนื้อหาเกี่ยวข้อง — Network Security สำหรับ Home Office

Q: Shadow DOM จำเป็นไหม?

A: ขึ้นกับ use case: ใช้ Shadow DOM: ต้องการ style encapsulation ป้องกัน CSS leak ไม่ใช้ Shadow DOM: ต้องการ global CSS, ง่ายกว่า customize แนะนำ: ใช้ Shadow DOM สำหรับ design system — ป้องกัน style conflicts

เนื้อหาเกี่ยวข้อง — Crossplane Composition GitOps Workflow

XM Legend · เทรดเดอร์ & ผู้สอน Forex 13 ปี

ผู้ก่อตั้ง SiamCafe ตั้งแต่ปี 1997 · เทรดเดอร์สาย Forex มากกว่า 13 ปี ได้รับการยกย่องเป็น XM Legend · แบ่งปันความรู้ Forex, ไอที, AI และการเทรด จากประสบการณ์จริงในตลาดจริง