StencilJS Web Components
StencilJS Compiler สร้าง Web Components มาตรฐาน TypeScript JSX Ionic Team Custom Elements ทำงานทุก Framework React Angular Vue Vanilla JS Lazy Loading Virtual DOM SSR
| Feature | StencilJS | Lit | Svelte |
|---|---|---|---|
| Language | TypeScript + JSX | TypeScript | Svelte Syntax |
| Output | Web Components | Web Components | Vanilla JS |
| Bundle Size | เล็ก (Tree-shaking) | เล็กมาก (5KB) | เล็กมาก |
| SSR | รองรับ | รองรับ | SvelteKit |
| Lazy Loading | Built-in | ไม่มี | ไม่มี |
StencilJS Component
# === StencilJS Setup & Component ===
# npm init stencil
# Choose: component (for a component library)
# cd my-components
# npm install
# npm start
# src/components/my-counter/my-counter.tsx
# import { Component, State, h } from '@stencil/core';
#
# @Component({
# tag: 'my-counter',
# styleUrl: 'my-counter.css',
# shadow: true,
# })
# export class MyCounter {
# @State() count: number = 0;
#
# private increment = () => { this.count++; };
# private decrement = () => { this.count--; };
#
# render() {
# return (
# <div class="counter">
# <button onClick={this.decrement}>-</button>
# <span>{this.count}</span>
# <button onClick={this.increment}>+</button>
# </div>
# );
# }
# }
# src/components/my-counter/my-counter.css
# .counter {
# display: flex;
# align-items: center;
# gap: 12px;
# font-family: sans-serif;
# }
# button {
# padding: 8px 16px;
# border: none;
# border-radius: 4px;
# background: #3880ff;
# color: white;
# cursor: pointer;
# }
# span {
# font-size: 24px;
# min-width: 40px;
# text-align: center;
# }
# stencil.config.ts
# import { Config } from '@stencil/core';
#
# export const config: Config = {
# namespace: 'my-components',
# outputTargets: [
# { type: 'dist', esmLoaderPath: '../loader' },
# { type: 'dist-custom-elements' },
# { type: 'www', serviceWorker: null },
# ],
# };
# Build & Publish
# npm run build
# npm publish
# Usage in HTML
# <script type="module" src="my-components/dist/my-components.esm.js"></script>
# <my-counter></my-counter>
# Usage in React
# import { MyCounter } from 'my-components-react';
# function App() {
# return <MyCounter />;
# }
from dataclasses import dataclass
from typing import List
@dataclass
class WebComponent:
tag: str
props: List[str]
events: List[str]
slots: List[str]
shadow: bool
components = [
WebComponent("my-counter", ["initial-count"], ["countChanged"], [], True),
WebComponent("my-button", ["variant", "size", "disabled"], ["click"], ["default"], True),
WebComponent("my-modal", ["open", "title"], ["close", "confirm"], ["default", "footer"], True),
WebComponent("my-toast", ["message", "type", "duration"], ["dismiss"], [], True),
]
print("StencilJS Component Library:")
for c in components:
print(f"\n <{c.tag}>")
print(f" Props: {', '.join(c.props)}")
print(f" Events: {', '.join(c.events)}")
print(f" Shadow DOM: {c.shadow}")
Kubernetes Pod Scheduling
# === Kubernetes Pod Scheduling ===
# 1. nodeSelector — วิธีง่ายสุด
# apiVersion: v1
# kind: Pod
# metadata:
# name: frontend-pod
# spec:
# nodeSelector:
# disk: ssd
# env: production
# containers:
# - name: app
# image: my-stencil-app:latest
# 2. Node Affinity — ยืดหยุ่นกว่า
# apiVersion: v1
# kind: Pod
# metadata:
# name: frontend-pod
# spec:
# affinity:
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: kubernetes.io/arch
# operator: In
# values: [amd64]
# - key: node-type
# operator: In
# values: [frontend, general]
# preferredDuringSchedulingIgnoredDuringExecution:
# - weight: 80
# preference:
# matchExpressions:
# - key: zone
# operator: In
# values: [ap-southeast-1a]
# containers:
# - name: app
# image: my-stencil-app:latest
# 3. Taints & Tolerations
# kubectl taint nodes node1 dedicated=frontend:NoSchedule
# kubectl taint nodes gpu-node nvidia.com/gpu=true:NoSchedule
#
# apiVersion: v1
# kind: Pod
# spec:
# tolerations:
# - key: "dedicated"
# operator: "Equal"
# value: "frontend"
# effect: "NoSchedule"
# containers:
# - name: app
# image: my-stencil-app:latest
# 4. Pod Anti-Affinity — กระจาย Pod
# apiVersion: apps/v1
# kind: Deployment
# spec:
# template:
# spec:
# affinity:
# podAntiAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# - labelSelector:
# matchExpressions:
# - key: app
# operator: In
# values: [frontend]
# topologyKey: kubernetes.io/hostname
# 5. Topology Spread Constraints
# spec:
# topologySpreadConstraints:
# - maxSkew: 1
# topologyKey: topology.kubernetes.io/zone
# whenUnsatisfiable: DoNotSchedule
# labelSelector:
# matchLabels:
# app: frontend
# 6. Priority Classes
# apiVersion: scheduling.k8s.io/v1
# kind: PriorityClass
# metadata:
# name: high-priority
# value: 1000000
# globalDefault: false
# description: "High priority for critical services"
scheduling_strategies = {
"nodeSelector": {
"use_case": "เลือก Node ตาม Label แบบง่าย",
"flexibility": "Low — Hard Match เท่านั้น",
"example": "nodeSelector: {disk: ssd}",
},
"Node Affinity": {
"use_case": "เลือก Node ด้วย Expression ซับซ้อน",
"flexibility": "High — Hard + Soft Match",
"example": "requiredDuring + preferredDuring",
},
"Taints/Tolerations": {
"use_case": "ไล่ Pod ออกจาก Node ที่ไม่ต้องการ",
"flexibility": "Medium — Node-centric",
"example": "dedicated=gpu:NoSchedule",
},
"Pod Anti-Affinity": {
"use_case": "กระจาย Pod ไม่ให้อยู่ Node เดียวกัน",
"flexibility": "High — HA Setup",
"example": "topologyKey: hostname",
},
"Topology Spread": {
"use_case": "กระจาย Pod ตาม Zone/Region",
"flexibility": "High — Multi-zone",
"example": "maxSkew: 1, zone",
},
}
print("Kubernetes Pod Scheduling Strategies:")
for strategy, info in scheduling_strategies.items():
print(f"\n [{strategy}]")
for k, v in info.items():
print(f" {k}: {v}")
CI/CD Deploy
# === StencilJS CI/CD with K8s Scheduling ===
# .github/workflows/deploy.yml
# name: Deploy StencilJS App
# on:
# push:
# branches: [main]
# jobs:
# build-deploy:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: actions/setup-node@v4
# with:
# node-version: 20
# - run: npm ci
# - run: npm run build
# - run: npm test
# - name: Build Docker Image
# run: |
# docker build -t my-registry/stencil-app:} .
# docker push my-registry/stencil-app:}
# - name: Deploy to K8s
# run: |
# kubectl set image deployment/frontend \
# app=my-registry/stencil-app:}
# kubectl rollout status deployment/frontend
# Dockerfile
# FROM node:20-alpine AS build
# WORKDIR /app
# COPY package*.json ./
# RUN npm ci
# COPY . .
# RUN npm run build
#
# FROM nginx:alpine
# COPY --from=build /app/www /usr/share/nginx/html
# COPY nginx.conf /etc/nginx/conf.d/default.conf
# EXPOSE 80
# K8s Deployment with Scheduling
# apiVersion: apps/v1
# kind: Deployment
# metadata:
# name: frontend
# spec:
# replicas: 3
# selector:
# matchLabels:
# app: frontend
# template:
# metadata:
# labels:
# app: frontend
# spec:
# affinity:
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: node-type
# operator: In
# values: [frontend, general]
# podAntiAffinity:
# preferredDuringSchedulingIgnoredDuringExecution:
# - weight: 100
# podAffinityTerm:
# labelSelector:
# matchLabels:
# app: frontend
# topologyKey: kubernetes.io/hostname
# topologySpreadConstraints:
# - maxSkew: 1
# topologyKey: topology.kubernetes.io/zone
# whenUnsatisfiable: ScheduleAnyway
# labelSelector:
# matchLabels:
# app: frontend
# containers:
# - name: app
# image: my-registry/stencil-app:latest
# ports:
# - containerPort: 80
# resources:
# requests:
# cpu: 100m
# memory: 128Mi
# limits:
# cpu: 500m
# memory: 256Mi
deploy_checklist = {
"Build": ["npm ci", "npm run build", "npm test", "docker build"],
"Push": ["docker push to registry", "tag with git SHA"],
"Deploy": ["kubectl set image", "rollout status", "health check"],
"Scheduling": ["Node Affinity", "Pod Anti-Affinity", "Topology Spread"],
"Monitor": ["kubectl top pods", "Prometheus metrics", "Grafana dashboard"],
}
print("Deployment Checklist:")
for phase, steps in deploy_checklist.items():
print(f"\n [{phase}]")
for step in steps:
print(f" - {step}")
เคล็ดลับ
- StencilJS: ใช้สร้าง Design System ที่ทำงานทุก Framework
- Node Affinity: ใช้แทน nodeSelector ยืดหยุ่นกว่า
- Pod Anti-Affinity: กระจาย Pod ไม่ให้อยู่ Node เดียวกันสำหรับ HA
- Topology Spread: กระจาย Pod ตาม Zone สำหรับ Multi-AZ
- Priority: ใช้ PriorityClass สำหรับ Critical Services
StencilJS คืออะไร
Compiler สร้าง Web Components TypeScript JSX Ionic Custom Elements ทำงานทุก Framework React Angular Vue Lazy Loading SSR
Pod Scheduling คืออะไร
Kubernetes Scheduler เลือก Node สำหรับ Pod Resource Requests Node Affinity Taints Tolerations Pod Anti-Affinity Topology Spread
Node Affinity ต่างจาก nodeSelector อย่างไร
nodeSelector Hard Match Label เท่านั้น Node Affinity required preferred Operator In NotIn Exists Gt Lt ยืดหยุ่นกว่า
Taints และ Tolerations คืออะไร
Taints ติด Node ไล่ Pod Tolerations ติด Pod ทนต่อ Taint NoSchedule PreferNoSchedule NoExecute GPU dedicated Node
สรุป
StencilJS Web Components Compiler TypeScript JSX ทุก Framework Kubernetes Pod Scheduling Node Affinity Taints Tolerations Pod Anti-Affinity Topology Spread Priority Classes CI/CD Deploy
