pin_drop当前位置:知识文库 ❯ 图文

框架集成:25. 框架选型与HTML:项 - 详细教程与实战指南

一、教程简介

在前端开发中,选择合适的技术框架是项目成功的关键因素之一。不同的项目需求、团队背景、性能要求和维护预期,都影响着框架选型的决策。本教程将从项目需求分析出发,系统对比React、Vue、Angular、Svelte等主流框架在HTML集成方面的差异,讲解框架迁移策略、渐进式增强方法、Web Components与框架的关系,以及前端框架的未来发展趋势,帮助你在实际项目中做出明智的技术选型决策。


二、核心概念

框架选型的核心维度

框架选型需要从多个维度综合评估,包括性能需求生态系统学习曲线可扩展性SEO需求团队因素六大核心维度。

  • 性能需求:首屏加载时间(FCP/LCP)、交互响应速度(FID/INP)、包体积大小、运行时性能、内存占用

  • 生态系统:社区活跃度、第三方库丰富度、工具链完善度、学习资源、企业支持

  • 学习曲线:入门难度、概念复杂度、文档质量、从零到产出的时间、TypeScript支持

  • 可扩展性:大型项目支持、代码组织方式、状态管理方案、微前端兼容、团队协作

  • SEO需求:SSR/SSG支持、Meta标签管理、爬虫友好度、社交分享支持、动态渲染能力

  • 团队因素:团队技术栈经验、招聘市场、长期维护成本、培训成本、技术债务

以下是项目类型与框架推荐的决策逻辑:

代码示例

// 项目类型分析决策树
function selectFramework(project) {
    // 1. 内容型网站(博客、新闻、文档)
    if (project.type === 'content') {
        if (project.needInteractivity === 'low') {
            return 'Astro + 任意UI框架'; // 零JS默认
        }
        if (project.needInteractivity === 'medium') {
            return 'Next.js (SSG) 或 Nuxt.js (SSG)';
        }
    }

    // 2. 电商网站
    if (project.type === 'ecommerce') {
        if (project.scale === 'large') {
            return 'Next.js (SSR + ISR)'; // 大规模电商
        }
        return 'Nuxt.js 或 Next.js'; // 中小型电商
    }

    // 3. 管理后台
    if (project.type === 'admin') {
        if (project.needSEO === false) {
            return 'React + Vite 或 Vue + Vite'; // CSR即可
        }
        return 'Next.js 或 Nuxt.js';
    }

    // 4. 实时应用(聊天、协作工具)
    if (project.type === 'realtime') {
        return 'React + Socket.io 或 Vue + Socket.io';
    }

    // 5. 移动端优先
    if (project.type === 'mobile-first') {
        if (project.needNative === true) {
            return 'React Native 或 Ionic + Angular';
        }
        return 'Svelte (最小包体积) 或 PWA方案';
    }

    // 6. 企业级应用
    if (project.type === 'enterprise') {
        return 'Angular (完整方案) 或 React (灵活组合)';
    }

    // 7. 渐进式增强
    if (project.type === 'progressive') {
        return 'Vue (渐进式) 或 Svelte (轻量)';
    }

    return '根据团队经验选择';
}

框架全面对比

以下是React、Vue、Angular、Svelte四大主流框架的核心特性对比:

特性 React Vue Angular Svelte
发布年份 2013 2014 2016 2016
维护方 Meta 尤雨溪/社区 Google Svelte团队/社区
架构模式 库(UI层) 渐进式框架 完整框架 编译器
渲染方式 Virtual DOM Virtual DOM (响应式) Incremental DOM 编译时优化(无VDOM)
包体积(gzip) ~42KB ~33KB ~130KB ~2KB (运行时)
学习曲线 中等 较低 较陡 最低
TypeScript 良好支持 良好支持 原生支持 良好支持
SSR方案 Next.js Nuxt.js Angular Universal SvelteKit
状态管理 Zustand/Jotai/Redux Pinia RxJS/NgRx 内置stores
移动端方案 React Native uni-app/NativeScript Ionic/NativeScript Capacitor
社区规模 最大 增长中
NPM周下载量 ~25M ~5M ~3M ~500K
GitHub Stars ~225K ~207K ~95K ~78K

HTML集成方式对比

不同框架在HTML集成方面有不同的方式和特点:

代码示例

// ==================== React ====================
// JSX - JavaScript中写HTML
function App() {
    return (
        <div className="container">
            <h1>Hello React</h1>
            <button onClick={handleClick}>Click</button>
        </div>
    );
}
// 特点:JSX是语法糖,编译为React.createElement()
// className替代class,事件使用驼峰命名,需要构建工具编译

// ==================== Vue ====================
// SFC - 单文件组件
// <template>
//     <div class="container">
//         <h1>Hello Vue</h1>
//         <button @click="handleClick">Click</button>
//     </div>
// </template>
// 特点:Template更接近原生HTML,v-bind/v-on指令,Scoped样式隔离

// ==================== Angular ====================
// 组件模板
// @Component({
//     selector: 'app-root',
//     template: `
//         <div class="container">
//             <h1>Hello Angular</h1>
//             <button (click)="handleClick()">Click</button>
//         </div>
//     `
// })
// 特点:模板语法扩展HTML,[()]双向绑定语法,依赖注入系统

// ==================== Svelte ====================
// Svelte组件
// <script>
// function handleClick() { /* ... */ }
// </script>
// <div class="container">
//     <h1>Hello Svelte</h1>
//     <button on:click={handleClick}>Click</button>
// </div>
// 特点:最接近原生HTML,编译时优化,无运行时,最小包体积

性能基准对比

性能指标 React Vue Angular Svelte
创建1000行列表(ms) ~35 ~25 ~45 ~15
更新1行(ms) ~30 ~20 ~40 ~5
交换行(ms) ~25 ~18 ~35 ~8
内存占用(MB) ~40 ~30 ~55 ~20
启动时间(ms) ~80 ~50 ~120 ~30
包体积(KB gzip) ~42 ~33 ~130 ~2

提示:以上数据基于js-framework-benchmark,实际性能因实现方式而异。Svelte在各项指标中表现最优,但框架选型不能仅看性能数据。


三、迁移策略

策略1:渐进式迁移(推荐)

将新框架应用作为微前端嵌入旧应用,逐步替换旧模块。使用single-spa可以实现多种框架共存。

代码示例

// 使用single-spa实现渐进式迁移
// 1. 安装single-spa: npm install single-spa

// 2. 主应用配置
import { registerApplication, start } from 'single-spa';

// 注册旧应用(jQuery/原生JS)
registerApplication({
    name: 'legacy-app',
    app: () => import('./legacy-app/bootstrap'),
    activeWhen: ['/legacy'],
});

// 注册新React应用
registerApplication({
    name: 'react-app',
    app: () => import('./react-app/bootstrap'),
    activeWhen: ['/dashboard', '/settings'],
});

// 注册新Vue应用
registerApplication({
    name: 'vue-app',
    app: () => import('./vue-app/bootstrap'),
    activeWhen: ['/products', '/orders'],
});

start();

// 3. React子应用引导
import singleSpaReact from 'single-spa-react';
const lifecycles = singleSpaReact({
    React,
    ReactDOMClient: ReactDOM,
    rootComponent: App,
});
export const { bootstrap, mount, unmount } = lifecycles;

策略2:路由级迁移

按路由逐页迁移,新旧应用共存于不同路由。可以使用Nginx反向代理或框架自带的Rewrites功能。

代码示例

// 使用Nginx反向代理实现路由级迁移
// nginx.conf
// server {
//     listen 80;
//     server_name example.com;
//     location /legacy/ { proxy_pass http://old-app:3000/; }
//     location / { proxy_pass http://new-app:3001/; }
//     location /static/ { proxy_pass http://cdn:4000/; }
// }

// 或者使用Next.js Rewrites
// next.config.js
module.exports = {
    async rewrites() {
        return [
            {
                source: '/legacy/:path*',
                destination: 'http://old-app:3000/:path*',
            },
        ];
    },
};

策略3:组件级迁移(Web Components桥接)

将旧组件封装为Web Components,在新框架中使用,实现跨框架复用。

代码示例

// 将jQuery组件封装为Web Components
class LegacyDatePicker extends HTMLElement {
    connectedCallback() {
        this.innerHTML = '<div class="legacy-datepicker"></div>';
        $(this.querySelector('.legacy-datepicker')).datepicker({
            format: this.getAttribute('format') || 'yyyy-mm-dd',
            onSelect: (date) => {
                this.dispatchEvent(new CustomEvent('date-select', {
                    detail: { date },
                    bubbles: true,
                }));
            },
        });
    }

    disconnectedCallback() {
        $(this.querySelector('.legacy-datepicker')).datepicker('destroy');
    }
}
customElements.define('legacy-datepicker', LegacyDatePicker);

// 在React中使用
// <legacy-datepicker
//     format="yyyy-mm-dd"
//     onDateSelect={(e) => setDate(e.detail.date)}
// />

jQuery到React的迁移示例

代码示例

// ==================== 迁移前:jQuery ====================
$(document).ready(function() {
    $.get('/api/products', function(products) {
        var $list = $('#product-list');
        $list.empty();
        products.forEach(function(product) {
            var $item = $('<div class="product-item">')
                .append($('<h3>').text(product.name))
                .append($('<p>').text(product.description))
                .append($('<span class="price">').text('¥' + product.price))
                .append($('<button>').text('加入购物车')
                    .data('id', product.id)
                    .on('click', function() { addToCart($(this).data('id')); })
                );
            $list.append($item);
        });
    });
});

// ==================== 迁移后:React ====================
function ProductList() {
    const [products, setProducts] = useState([]);
    const [keyword, setKeyword] = useState('');

    useEffect(() => {
        fetch('/api/products')
            .then(res => res.json())
            .then(data => setProducts(data));
    }, []);

    const filteredProducts = products.filter(p =>
        p.name.toLowerCase().includes(keyword.toLowerCase())
    );

    return (
        <div>
            <input value={keyword}
                onChange={(e) => setKeyword(e.target.value)} />
            {filteredProducts.map(product => (
                <div key={product.id} className="product-item">
                    <h3>{product.name}</h3>
                    <p>{product.description}</p>
                    <span className="price">¥{product.price}</span>
                </div>
            ))}
        </div>
    );
}

四、渐进式增强

渐进式增强四层模型

  • 第一层:语义化HTML — 所有浏览器都能访问内容

  • 第二层:CSS样式 — 增强视觉体验

  • 第三层:JavaScript交互 — 增强交互体验

  • 第四层:框架增强 — 复杂交互和状态管理

渐进式增强实践:表单验证

代码示例

// ==================== 第一层:HTML ====================
// 纯HTML表单 - 浏览器原生验证
// <form action="/api/register" method="POST">
//     <input type="text" name="username" required minlength="3" />
//     <input type="email" name="email" required />
//     <input type="password" name="password" required minlength="8" />
//     <button type="submit">注册</button>
// </form>

// ==================== 第二层:CSS ====================
// input:valid { border-color: #4caf50; }
// input:invalid { border-color: #f44336; }
// input:focus { outline: 2px solid #2196f3; }

// ==================== 第三层:JavaScript ====================
class FormValidator {
    constructor(form) {
        this.form = form;
        this.inputs = form.querySelectorAll('input');
        this.init();
    }

    init() {
        this.inputs.forEach(input => {
            input.addEventListener('input', () => this.validateField(input));
            input.addEventListener('blur', () => this.validateField(input));
        });
        this.form.addEventListener('submit', (e) => {
            if (!this.validateAll()) e.preventDefault();
        });
    }

    validateField(input) {
        if (input.validity.valueMissing) return false;
        if (input.validity.typeMismatch) return false;
        if (input.validity.tooShort) return false;
        return true;
    }

    validateAll() {
        let isValid = true;
        this.inputs.forEach(input => {
            if (!this.validateField(input)) isValid = false;
        });
        return isValid;
    }
}

// ==================== 第四层:框架增强 ====================
// React组件 - 最丰富的交互体验
function RegistrationForm() {
    const [formData, setFormData] = useState({
        username: '', email: '', password: '',
    });
    const [errors, setErrors] = useState({});

    // 实时用户名可用性检查
    const checkUsername = debounce(async (username) => {
        const available = await fetch(`/api/check-username?u=${username}`)
            .then(r => r.json());
        setErrors(prev => ({
            ...prev,
            username: available ? '' : '用户名已被占用',
        }));
    }, 300);

    return (
        <form onSubmit={handleSubmit}>
            <input value={formData.username}
                onChange={(e) => {
                    setFormData(prev => ({ ...prev, username: e.target.value }));
                    checkUsername(e.target.value);
                }} />
            {errors.username && <span className="error">{errors.username}</span>}
        </form>
    );
}

Vue渐进式增强:从CDN引入到完整项目

代码示例

// ==================== 阶段1:CDN引入 ====================
// 在现有HTML中引入Vue,增强交互
// <div id="app">
//     <form @submit.prevent="submitForm">
//         <input v-model="username" placeholder="用户名" />
//         <button type="submit">提交</button>
//     </form>
// </div>
const { createApp, ref } = Vue;
createApp({
    setup() {
        const username = ref('');
        const submitForm = () => { /* ... */ };
        return { username, submitForm };
    },
}).mount('#app');

// ==================== 阶段2:组件化 ====================
// npm create vite@latest my-app -- --template vue
// 使用SFC单文件组件

// ==================== 阶段3:路由和状态管理 ====================
// npm install vue-router pinia
// stores/user.js
export const useUserStore = defineStore('user', {
    state: () => ({ user: null, token: '' }),
    actions: {
        async login(credentials) {
            const res = await fetch('/api/login', {
                method: 'POST',
                body: JSON.stringify(credentials),
            });
            const data = await res.json();
            this.user = data.user;
        },
    },
});

// ==================== 阶段4:SSR/SSG ====================
// 迁移到Nuxt.js: npx nuxi@latest init my-nuxt-app

五、Web Components与框架

创建框架无关的Web Component

Web Components是浏览器原生的组件标准,可以在任何框架中使用。以下创建一个评分组件示例:

代码示例

class StarRating extends HTMLElement {
    static get observedAttributes() {
        return ['value', 'max', 'readonly'];
    }

    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this._value = 0;
        this._max = 5;
        this._readonly = false;
    }

    connectedCallback() {
        this.render();
        this.setupEvents();
    }

    attributeChangedCallback(name, oldVal, newVal) {
        switch (name) {
            case 'value': this._value = Number(newVal) || 0; break;
            case 'max': this._max = Number(newVal) || 5; break;
            case 'readonly': this._readonly = newVal !== null; break;
        }
        if (this.shadowRoot) this.render();
    }

    render() {
        this.shadowRoot.innerHTML = `
            <style>
                .star { font-size: 24px; cursor: pointer; }
                .star.active { color: #ffc107; }
                .star.inactive { color: #e0e0e0; }
            </style>
            <div class="stars">
                ${Array.from({ length: this._max }, (_, i) => `
                    <span class="star ${i < this._value ? 'active' : 'inactive'}"
                          data-value="${i + 1}">
                        ${i < this._value ? '★' : '☆'}
                    </span>
                `).join('')}
            </div>
        `;
        this.setupEvents();
    }

    setupEvents() {
        if (this._readonly) return;
        const stars = this.shadowRoot.querySelectorAll('.star');
        stars.forEach(star => {
            star.addEventListener('click', () => {
                this._value = Number(star.dataset.value);
                this.setAttribute('value', String(this._value));
                this.dispatchEvent(new CustomEvent('rating-change', {
                    detail: { value: this._value },
                    bubbles: true, composed: true,
                }));
                this.render();
            });
        });
    }
}
customElements.define('star-rating', StarRating);

在各框架中使用Web Component

代码示例

// ==================== 在React中使用 ====================
function ProductReview() {
    const [rating, setRating] = useState(0);
    const ratingRef = useRef();

    useEffect(() => {
        const el = ratingRef.current;
        if (el) {
            el.value = rating;
            el.addEventListener('rating-change', (e) => {
                setRating(e.detail.value);
            });
        }
    }, [rating]);

    return <star-rating ref={ratingRef} />;
}

// ==================== 在Vue中使用 ====================
// Vue对Web Components支持更好
// <template>
//     <star-rating :value="String(rating)" @rating-change="onRatingChange" />
// </template>
// Vue配置:
// app.config.compilerOptions.isCustomElement = (tag) => tag.includes('-');

// ==================== 在Angular中使用 ====================
// import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
// @NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })

// ==================== 在Svelte中使用 ====================
// <star-rating value={String(rating)} on:rating-change={onRatingChange}>
// </star-rating>

将框架组件输出为Web Component

代码示例

// React组件输出为Web Component
// npm install @github/react-to-webcomponent
import reactToWebComponent from '@github/react-to-webcomponent';

function MyButton({ label, onClick }) {
    return <button onClick={onClick} className="my-btn">{label}</button>;
}
customElements.define(
    'my-button',
    reactToWebComponent(MyButton, React, ReactDOM)
);

// Vue组件输出为Web Component
import { defineCustomElement } from 'vue';
const MyButtonCe = defineCustomElement({
    props: ['label'],
    emits: ['click'],
    template: `<button @click="$emit('click')" class="my-btn">{{ label }}</button>`,
});
customElements.define('my-button-vue', MyButtonCe);

六、未来趋势

  • 服务端组件 (RSC):React Server Components引领将渲染逻辑移回服务端的趋势,减少客户端JavaScript体积

  • 编译时优化:Svelte和SolidJS证明了编译时优化的价值,框架将更多工作从运行时移到构建时

  • 岛屿架构:Astro的岛屿架构允许页面中只有交互部分加载JavaScript,其余为纯HTML

  • AI辅助开发:AI工具正在改变前端开发方式,从代码生成到智能调试

  • Web Components标准化:浏览器原生组件标准不断完善,框架无关的组件生态正在形成

  • 边缘渲染:将渲染逻辑部署到CDN边缘节点,实现更快的首屏加载和个性化内容

新兴框架和工具

代码示例

// ==================== Astro ====================
// 岛屿架构 - 默认零JS
// ---
// import ReactCounter from '../components/ReactCounter';
// import VueNewsletter from '../components/VueNewsletter.vue';
// ---
// <ReactCounter client:load />        // 仅这个组件加载React运行时
// <VueNewsletter client:visible />   // 仅在可见时加载Vue运行时
// <HeavyWidget client:idle />        // 仅在空闲时加载

// ==================== Solid.js ====================
// 细粒度响应式 - 无Virtual DOM
import { createSignal, For } from 'solid-js';
function TodoList() {
    const [todos, setTodos] = createSignal([]);
    const addTodo = () => {
        setTodos(prev => [...prev, { id: Date.now(), text: input() }]);
    };
    return (
        <ul>
            <For each={todos()}>
                {(todo) => <li>{todo.text}</li>}
            </For>
        </ul>
    );
}

// ==================== Qwik ====================
// 可恢复性 - 零初始JS
import { component$, useSignal } from '@builder.io/qwik';
export default component$(() => {
    const count = useSignal(0);
    return (
        <div>
            <p>Count: {count.value}</p>
            {/* 点击时才加载事件处理代码 */}
            <button onClick$={() => count.value++}>Increment</button>
        </div>
    );
});

// ==================== HTMX ====================
// 回归HTML - 用属性声明交互
// <button hx-post="/api/like" hx-target="#like-count" hx-swap="innerHTML">
//     点赞
// </button>

框架选型决策矩阵

代码示例

// 根据项目特征选择框架的决策工具
const frameworkDecisionMatrix = {
    'content-site': {
        small: { recommended: 'Astro', reason: '零JS默认,SEO最优' },
        medium: { recommended: 'Next.js (SSG)', reason: 'React生态丰富' },
        large: { recommended: 'Next.js (ISR)', reason: '增量静态再生成' },
    },
    'web-app': {
        small: { recommended: 'Vue + Vite', reason: '上手快,体积小' },
        medium: { recommended: 'React + Next.js', reason: '生态最大' },
        large: { recommended: 'Angular', reason: '完整方案,适合大团队' },
    },
    'ecommerce': {
        small: { recommended: 'Nuxt.js', reason: 'SEO友好,开发效率高' },
        large: { recommended: 'Next.js (SSR + ISR)', reason: '性能最优' },
    },
    'admin-dashboard': {
        small: { recommended: 'Vue + Element Plus', reason: '组件库丰富' },
        large: { recommended: 'React + Ant Design Pro', reason: '企业级方案' },
    },
    'mobile': {
        hybrid: { recommended: 'React Native', reason: '原生性能,生态成熟' },
        pwa: { recommended: 'Svelte + SvelteKit', reason: '最小包体积' },
    },
};

七、浏览器兼容性

特性 Chrome Firefox Safari Edge 说明
Web Components 67+ 63+ 10.1+ 79+ Custom Elements + Shadow DOM
ES Modules 61+ 60+ 11+ 16+ 原生模块支持
CSS Custom Properties 49+ 31+ 9.1+ 15+ CSS变量
CSS :host / ::part 73+ 72+ 13.1+ 79+ Shadow DOM样式穿透
框架运行时兼容性 最低浏览器版本 说明
React 18 IE不支持 需ES6+环境
Vue 3 IE不支持 需Proxy等ES6+特性
Angular 17 IE不支持 需ES2020+环境
Svelte 4 IE不支持 编译后代码需ES6+
Astro IE不支持 输出为静态HTML

八、注意事项与最佳实践

框架选型注意事项

  • 不要盲目追新:选择成熟稳定的技术栈,新框架需要评估长期维护风险

  • 团队经验优先:团队熟悉的框架通常比"更好"的框架更高效

  • 考虑招聘市场:React开发者最多,Angular和Vue次之,Svelte较少

  • 评估长期维护:框架的维护方、社区活跃度、版本更新策略

  • 性能不是唯一标准:开发效率、可维护性同样重要

  • 避免过度工程:简单项目不要选择重量级框架

小贴士

建议为每个项目创建一份框架选型决策文档,记录项目概述、需求分析、候选方案评估和最终决策,方便团队回顾和后续维护参考。


九、常见问题与解决方案

常见问题

团队对框架选择意见不统一怎么办?

建立客观评估框架,让团队成员一起按性能、开发效率、生态、学习成本、团队经验、长期维护、招聘难度等维度打分,用数据说话。同时建议做POC(概念验证)对比,用每个框架实现同一个功能模块,对比开发体验和产出质量。

旧项目迁移成本太高怎么办?

采用渐进式迁移策略:先用Web Components封装旧组件,新功能使用新框架开发,使用微前端(single-spa/qiankun)让新旧共存,然后逐步替换旧模块。避免一次性重写带来的风险。

框架版本升级困难怎么办?

可以使用兼容层(如Vue的@vue/compat),采用渐进式升级策略:先升级工具链,使用兼容模式运行,逐个修复废弃API警告,最后移除兼容层。也可以使用codemod工具自动迁移代码。

React和Vue应该选哪个?

React生态最大、灵活性高、社区资源最丰富,适合大型项目和需要高度定制的场景。Vue学习曲线更低、开发效率高、渐进式设计,适合中小型项目和快速迭代。选择时优先考虑团队经验和项目需求。

什么时候应该选择Svelte?

Svelte适合对性能要求极高、包体积敏感的项目(如PWA、移动端),以及追求最佳开发体验的小团队。由于社区相对较小,大型项目需要评估生态支持和招聘难度。

Web Components能替代框架吗?

Web Components适合创建可复用的UI组件,但缺少框架提供的状态管理、路由、生命周期管理等高级功能。建议将Web Components作为框架的补充,用于创建跨框架共享的组件,而非完全替代框架。


十、总结

本教程系统讲解了前端框架选型的各个方面,核心要点如下:

  • 项目需求分析:从性能、生态、学习曲线、可扩展性、SEO、团队因素六个维度评估项目需求,使用决策树匹配框架

  • 框架全面对比:React(生态最大)、Vue(渐进式)、Angular(完整方案)、Svelte(编译优化)各有优劣,需根据场景选择

  • 迁移策略:渐进式迁移(微前端)、路由级迁移(Nginx代理)、组件级迁移(Web Components桥接)三种策略

  • 渐进式增强:从语义化HTML到CSS增强、JavaScript交互、框架增强四层模型

  • Web Components:框架无关的组件标准,可用于桥接不同框架、封装旧组件

  • 未来趋势:服务端组件(RSC)、编译时优化、岛屿架构、AI辅助开发、边缘渲染

  • 选型原则:没有最好的框架,只有最适合的框架。根据项目需求、团队经验、长期维护成本综合评估

总结:框架选型是一个需要综合考虑技术、团队、业务因素的决策过程,建议通过POC验证、团队讨论、数据驱动的方式做出最终选择。

标签: 框架选型 React Vue Angular Svelte 迁移策略 渐进式增强 Web Components

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: 框架集成:24. 框架SEO与HTML: - 详细教程与实战指南 下一篇: 游戏开发:HTML游戏开发概述 - 从入门到实践详解

poll相关推荐