Polymer官方示例项目教程:深度解析

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本教程将深入分析Polymer 2014年7月5日版本的核心概念和实践技巧。Polymer是Google推出的Web组件库,通过封装自定义的HTML元素,实现模块化、可重用的网页应用构建。教程将详细探讨Web Components、Shadow DOM、HTML Imports等概念,解析项目结构,指导如何创建Polymer元素、实现数据绑定和属性定义,以及掌握生命周期方法。通过实践项目,学习者将能够熟练掌握Polymer开发流程,理解Web Components技术,为模块化Web开发打下坚实基础。

1. Polymer核心概念

在现代Web开发中,组件化已成为提高开发效率和维护效率的重要手段。 Polymer 是一个由Google发起的项目,旨在简化Web组件的使用,它建立在Web Components的原生API之上,为开发者提供了一套简洁的接口来创建和使用Web Components。

Polymer的发展与定位

Polymer自2013年问世以来,经过不断的迭代更新,已经成为Web组件开发中的一个重要工具。它不仅降低了Web Components的学习曲线,还通过一系列扩展功能,使得组件开发更加简单高效。Polymer 3.0版本更是与主流的构建工具如Webpack和Rollup等无缝集成,大大增强了其在现代前端工程中的应用前景。

与Web Components的关系

Polymer是Web Components技术的一种实现,提供了更加简化的API,使得开发者可以像使用传统JavaScript库那样方便地创建和使用Web Components。它不仅封装了Shadow DOM、Custom Elements和HTML Imports等原生Web Components技术,还提供了一套丰富的自定义元素库,让开发者能够快速构建复杂的UI界面。

通过阅读本章节,读者将对Polymer的核心概念有一个基本的了解,为进一步学习和实践Web组件化开发打下坚实的基础。

2. Web Components介绍

2.1 Web Components技术规范

Web Components 是一系列用于构建可复用的自定义元素的Web技术的总称。这一技术规范带来了真正的组件化开发,使得开发者可以在不担心影响到页面其他部分的情况下,创建独立且封装的组件。

2.1.1 组件化开发的必要性

在Web开发中,组件化是一种重要的设计理念,它能够提高代码的复用性、可维护性和可扩展性。传统开发模式常常面临样式冲突、依赖管理复杂以及难以重用代码等问题。组件化通过封装、隔离和抽象的方式,将界面和逻辑分成独立且可复用的部分,极大优化了这些问题。

2.1.2 Web Components的四大技术支柱

Web Components 技术规范由以下四个主要技术组成:

  • Custom Elements(自定义元素) :允许开发者创建新的HTML元素,并定义其行为。
  • Shadow DOM(影子DOM) :提供了一种将封装的样式和标记附加到元素上的方法,而不会影响到其他部分。
  • HTML Templates(HTML模板) :允许定义标记的模板,这些模板可以被复用而不直接渲染在页面上。
  • ES Modules(模块) :提供了一种原生的方式来组织和复用JavaScript代码。

2.2 组件化开发的优势

2.2.1 提升开发效率与代码复用

Web Components 的组件化方法可以显著提高开发效率,因为它允许开发者重用组件。这意味着可以避免重复的编码工作,加快开发流程。复用的代码也可以在多个项目间共享,从而节约资源和维护成本。

2.2.2 组件的封装与隔离

组件的封装和隔离是Web Components 提供的重要优势之一。开发者可以将相关的样式、脚本和模板封装在一个单一的单元内。这不仅避免了全局作用域的污染,还可以确保组件的内部实现细节与外部隔离,提高了代码的健壮性。

2.2.3 组件的自定义与扩展性

Web Components 的组件是高度可自定义的。开发者可以根据需要创建具有特定属性和行为的组件,以及扩展现有组件来创建更复杂的组件。这种自定义与扩展性是构建现代Web应用程序的关键需求。

总结

Web Components 提供了强大的组件化开发工具和方法,极大地推动了Web开发的现代化进程。它不仅简化了Web应用的开发流程,还提升了应用的可维护性和复用性。随着Web技术的不断演进,Web Components 作为其中的核心组件,其重要性将会持续增加。

3. Shadow DOM应用

在现代的Web开发中,Shadow DOM技术扮演了一个关键角色。它提供了一种将DOM树结构和样式封装起来的方法,使得开发者可以创建可重用且独立的组件。Shadow DOM是Web Components的核心之一,本章将深入探讨Shadow DOM的基本原理及其在实际开发中的应用。

3.1 Shadow DOM的基本原理

3.1.1 DOM结构与样式的封装

Shadow DOM是浏览器实现的一种封装技术,它允许开发者将一个独立的DOM树附加到主文档DOM上,同时将其样式和结构与主文档中的其他内容隔离开来。这为组件的封装提供了原生支持,使得开发者可以构建出独立于文档其他部分的组件。

// 创建一个Shadow DOM并附加到一个元素上
const host = document.getElementById('host-element');
const shadowRoot = host.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
  <style>
    /* 这里的样式只会应用于这个Shadow DOM内部 */
    h2 {
      color: blue;
    }
  </style>
  <h2>这是一个Shadow DOM的标题</h2>
`;

在上面的代码中,我们通过 attachShadow 方法为指定的宿主元素创建了一个开放的Shadow DOM。通过这个方法,我们能够向Shadow DOM内部添加内容,并通过 <style> 标签定义了只作用于此Shadow DOM内部的样式。由于这种封装性,外部样式和脚本无法访问或影响到Shadow DOM内部的元素。

3.1.2 Shadow DOM的版本区别

从技术演进的角度来看,Shadow DOM经历了两个主要的版本。Shadow DOM v0主要被用于Chrome浏览器,而Shadow DOM v1则成为了Web Components规范的一部分,得到了更广泛的浏览器支持。

// 检测浏览器对Shadow DOM v1的支持
if ('attachShadow' in document.createElement('div')) {
  console.log('Shadow DOM v1 is supported!');
} else {
  console.log('Your browser does not support Shadow DOM v1.');
}

上面的代码展示了如何检测浏览器是否支持Shadow DOM v1。由于Shadow DOM v1成为了标准,开发者在使用时通常不需要担心兼容性问题。不过,了解不同版本的差异性对于维护旧项目或使用某些框架的特定实现仍然是有益的。

3.2 Shadow DOM的实际应用

3.2.1 创建自定义元素的Shadow DOM

自定义元素(Custom Elements)是Web Components中的另一大支柱,Shadow DOM常与自定义元素一起使用以创建封装良好的组件。

class CustomElement extends HTMLElement {
  constructor() {
    super();
    // 创建Shadow DOM
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.innerHTML = `<p>这是一个自定义元素的Shadow DOM内容</p>`;
  }
}

customElements.define('custom-element', CustomElement);

在上面的代码示例中,我们定义了一个名为 CustomElement 的新类,它继承自 HTMLElement 。在这个类的构造函数中,我们创建了一个Shadow DOM,并添加了一些内容。最后,我们通过 customElements.define 方法注册了这个自定义元素,使得我们可以在HTML中直接使用 <custom-element> 标签。

3.2.2 Shadow DOM中的事件处理

在Shadow DOM中处理事件时,可以使用 ***posedPath() 方法来获取事件传播路径。这个方法返回一个事件目标列表,包括Shadow DOM内部和外部的元素。

// 假设有一个带有Shadow DOM的元素,当点击内部时
shadowRoot.querySelector('button').addEventListener('click', (event) => {
  const path = ***posedPath();
  console.log('事件路径:', path);
});

通过上述代码,当在Shadow DOM内部的按钮上触发点击事件时, composedPath 方法会返回事件传播的完整路径,包括Shadow DOM的内部和外部元素。

Shadow DOM对于隔离组件样式、封装组件行为等方面有着重要的作用。它能够让我们在Web应用中实现高内聚低耦合的组件设计,使得代码更加清晰、易于维护。

以上内容涉及了Shadow DOM的概念、原理、以及如何在实际应用中创建和使用Shadow DOM。接下来的章节将探讨HTML Imports和Polymer项目的结构解析,为创建和管理Polymer项目打下坚实的基础。

4. HTML Imports使用

HTML Imports是Web Components技术规范的一部分,它允许开发者导入和使用外部HTML文档中的元素。这种方法对于创建组件化的网页非常有用,因为它们可以用来封装和重用代码。HTML Imports为Web Components提供了一种机制,使得组件之间可以进行相互引用和依赖。

4.1 HTML Imports的基础

4.1.1 导入外部HTML文档

HTML Imports的核心功能是将一个HTML文档作为另一个HTML文档的一部分导入。这样做的好处是可以将相关代码封装在一个单独的文件中,使得代码更加模块化,易于维护和重用。导入的文档通常包含了自定义元素的定义,包括它们的样式、脚本和模板。

让我们来看一个简单的例子,展示如何导入一个外部HTML文档:

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>HTML Imports示例</title>
</head>
<body>
    <importer-element></importer-element>

    <script>
        // 代码逻辑
    </script>
</body>
</html>
<!-- importer-element.html -->
<link rel="import" href="path/to/your-component.html">
<dom-module id="importer-element">
    <template>
        <!-- 自定义元素的模板 -->
    </template>
</dom-module>
<script>
    document.addEventListener('WebComponentsReady', function() {
        var proto = Object.create HTMLElement.prototype;
        proto.createdCallback = function() {};
        proto.attachedCallback = function() {};
        proto.detachedCallback = function() {};
        proto.attributeChangedCallback = function() {};
        customElements.define('importer-element', proto);
    });
</script>

在上面的例子中, index.html 导入了 importer-element.html importer-element.html 包含了自定义元素的定义和相关脚本。

4.1.2 HTML Imports的加载机制

HTML Imports的加载机制遵循特定的规则。当浏览器遇到 <link rel="import"> 标签时,它会开始加载指定的外部文件,并等待该文件完全加载和解析之后,才会继续加载页面的其余部分。这种机制确保了依赖关系的正确加载顺序,避免了因资源未就绪而导致的脚本错误。

4.2 HTML Imports的高级应用

4.2.1 与Polymer的结合使用

HTML Imports是Polymer框架中不可或缺的一部分,因为Polymer使用它来封装和加载自定义元素。在Polymer中,每一个自定义元素都是通过HTML Import来定义和注册的。

<link rel="import" href="my-element.html">

<my-element></my-element>

上面的代码展示了如何使用HTML Imports来导入并使用一个名为 my-element 的自定义元素。

4.2.2 解决循环依赖问题

循环依赖问题是指两个或多个组件相互依赖对方,导致无法正确加载的问题。HTML Imports提供了一些策略来处理循环依赖问题,以确保在依赖关系复杂时仍能正确加载所有组件。

一个常见的解决方案是创建一个专门的文档来声明和加载所有依赖项,确保依赖关系的加载顺序是正确的。例如:

<!-- dependencies.html -->
<link rel="import" href="lib/dependency-1.html">
<link rel="import" href="lib/dependency-2.html">
<!-- 更多依赖项 -->

然后在主文档中导入这个依赖项声明文档:

<link rel="import" href="dependencies.html">
<link rel="import" href="my-custom-element.html">

通过这种方式,所有依赖项都会按照预定的顺序被加载,从而避免了循环依赖的问题。

通过本章节的介绍,我们已经深入了解了HTML Imports的基础知识和高级应用。这为使用Polymer框架创建和管理自定义元素提供了坚实的基础。接下来,我们将继续探讨Polymer项目的目录结构和配置,以及如何将这些知识点应用于具体的实例项目中。

5. 项目结构解析

5.1 Polymer项目的目录结构

5.1.1 标准的Polymer项目模板

Polymer项目的目录结构遵循一定的标准模板,这使得开发者可以快速上手,同时保持项目的一致性和可维护性。一个典型的Polymer项目模板通常包含以下几个核心目录和文件:

  • src/ :源代码目录,存放所有的 .html 文件。
  • index.html :项目的入口文件,通常包含基本的HTML结构和Polymer脚本。
  • elements/ :存放自定义元素的目录。
  • test/ :测试目录,用于存放单元测试或集成测试的相关文件。
  • demo/ :示例目录,用于展示项目中元素或组件的用法。
  • node_modules/ :项目依赖的Node.js模块。
  • polymer.json :Polymer项目的配置文件。
  • package.json :Node.js项目的配置文件。

5.1.2 文件与文件夹的组织方式

为了提高代码的可读性和可维护性,合理组织项目中的文件和文件夹是非常重要的。以下是文件与文件夹组织的推荐方式:

  • 按功能组织文件夹 :将相关的文件和文件夹组织在一起。例如,所有与用户界面相关的自定义元素可以放在 elements/ui/ 目录下,而数据模型相关的文件可以放在 elements/model/ 目录下。
  • 使用清晰的命名约定 :文件和文件夹的命名应该清晰地反映其内容和功能。例如,如果你正在开发一个轮播图元素,可以将其命名为 elements/ui/carousel.html
  • 配置文件集中管理 :将 polymer.json package.json 等配置文件放在项目的根目录下,便于管理和修改。
  • 文档和示例放在最外层 :创建 docs/ demo/ 目录,并放在项目根目录下,方便团队成员和用户快速找到项目的相关文档和示例代码。

代码块及解释

下面是一个典型的 polymer.json 配置文件的例子,它定义了项目的构建配置和开发服务器设置:

{
  "entrypoint": "index.html",
  "shell": "src/my-app.html",
  "sources": ["src/**/*"],
  "extraDependencies": [
    "webcomponents-loader.js"
  ],
  "builds": [
    {
      "name": "dev",
      "PolymerServer": {
        "entrypoint": "index.html",
        "port": 8080,
        "dev": true
      }
    },
    {
      "name": "prod",
      "PolymerServer": {
        "entrypoint": "index.html",
        "port": 8080,
        "inject": "builds/prod/my-app-{{buildstamp}}.html"
      }
    }
  ]
}

逻辑分析 :该配置文件定义了项目的入口点 entrypoint index.html ,表示应用的起始页面。 shell 属性指定了应用的shell文件,它通常包含Polymer应用的主要逻辑。 sources 属性定义了需要被构建系统处理的源代码文件的路径。 extraDependencies 列出了项目中依赖的额外文件。 builds 数组定义了构建配置,其中 dev 用于开发环境,提供热重载和源代码映射,而 prod 用于生产环境,它在构建时注入构建标记以优化缓存。

5.2 Polymer项目配置

5.2.1 polymer.json的作用与配置

polymer.json 是Polymer项目中非常关键的配置文件,它为构建系统提供了项目结构和构建指令。该文件支持多种构建配置,使开发者能够针对不同的环境设置不同的构建选项。

一个重要的配置项是 builds 属性,它允许开发者定义多个构建配置文件。例如,可以为不同的环境(开发环境和生产环境)创建不同的构建配置,或为同一环境创建不同的构建配置以满足不同的需求。

5.2.2 环境变量与构建工具集成

在进行项目配置时,环境变量的设置也是不可或缺的一部分。通过在 polymer.json 中设置 env 属性,可以为不同的构建环境指定环境变量。这些变量可以在构建过程中被Polymer CLI替换,以便在构建时设置不同的值。

例如,可以在开发环境中设置 NODE_ENV development ,在生产环境中设置为 production 。这样,可以在代码中根据 NODE_ENV 的值来调整资源的加载和日志记录等行为。

代码块及解释

下面的示例代码展示了如何在 polymer.json 中配置环境变量:

{
  "builds": [
    {
      "name": "dev",
      "PolymerServer": {
        "port": 8080,
        "dev": true
      },
      "env": {
        "NODE_ENV": "development"
      }
    },
    {
      "name": "prod",
      "PolymerServer": {
        "port": 8080,
        "inject": "builds/prod/my-app-{{buildstamp}}.html"
      },
      "env": {
        "NODE_ENV": "production"
      }
    }
  ]
}

逻辑分析 :在这个配置中,我们定义了两个构建配置: dev prod 。在 dev 配置中,我们设置了环境变量 NODE_ENV development ,这通常意味着在浏览器中会有更多的日志信息和较少的优化。而在 prod 配置中, NODE_ENV 被设置为 production ,这将导致代码被压缩和优化,以减少加载时间和提高性能。通过这种方式,开发者可以根据不同的环境需求来优化应用的行为。

表格展示

下面是一个 polymer.json 中可能使用的配置项表格:

| 配置项 | 类型 | 描述 | | --------------- | -------- | ------------------------------------------------------------ | | entrypoint | String | 应用程序的入口点文件路径。 | | shell | String | 应用程序的shell文件路径。 | | sources | Array | 包含所有需要构建的源文件路径的数组。 | | extraDependencies | Array | 一个额外的依赖项数组,当构建系统不知道如何处理这些依赖项时,它将被直接包含。 | | builds | Array | 构建配置的数组,每个构建配置可以有不同的 name PolymerServer env 设置。 | | env | Object | 为构建过程设置环境变量的对象。 |

使用这个表格可以帮助开发者快速理解 polymer.json 文件中每个配置项的作用,从而能够更加高效地配置和管理项目。

mermaid流程图展示

为了更直观地了解 polymer.json 的构建流程,下面是一个构建过程的mermaid流程图示例:

graph TD
    A[开始构建] --> B{是否dev模式?}
    B -- 是 --> C[加载开发环境配置]
    B -- 否 --> D[加载生产环境配置]
    C --> E[启动开发服务器]
    D --> F[构建生产版本]
    E --> G[监听文件更改]
    F --> H[完成构建]
    G --> B

逻辑分析 :这个流程图展示了构建过程中 polymer.json 根据 dev 配置与否的决策过程。如果处于开发模式,则加载相应的配置并启动开发服务器。在开发模式下,还会监听文件更改,以便在源文件更改时自动重新加载和构建。如果处于生产模式,则直接执行构建生产版本,并在完成后结束构建过程。通过这种流程, polymer.json 能够灵活地处理不同构建需求,从而优化开发和发布流程。

以上章节内容结合了代码块、表格和mermaid流程图等多种Markdown元素,展示了如何详细介绍和解析Polymer项目的目录结构和配置细节。这不仅有助于读者理解Polymer项目的结构,也为实际操作提供了指导。

6. 实例项目实践

6.1 创建一个Polymer项目

6.1.1 初始化项目与依赖安装

在创建Polymer项目之前,首先确保系统中已安装Node.js环境,因为Polymer CLI是基于Node.js开发的。打开终端,全局安装Polymer CLI:

npm install -g polymer-cli

安装完成后,通过Polymer CLI初始化一个新的项目。选择一个你喜欢的项目目录名,例如 my-polymer-project ,然后执行以下命令:

polymer init starter-kit

选择“Starter Kit”模板作为项目的基础结构。接下来,进入项目目录,并安装所需的依赖:

cd my-polymer-project
npm install

等待安装过程完成,这个过程会下载并安装项目所需的各种依赖包。

6.1.2 编写基础HTML模板

初始化项目后,你会得到一个基础的项目结构。在 index.html 文件中,你可以编写基础的HTML模板代码。以下是一个简单的示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Polymer Project</title>
    <!-- 引入Polymer库 -->
    <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
    <!-- 引入Polymer基础样式 -->
    <link rel="import" href="/src/my-app.html">
</head>
<body unresolved>
    <my-app>
        <!-- Polymer元素的内容 -->
    </my-app>
</body>
</html>

在上述模板中, <my-app> 标签是一个自定义元素,这个元素在后续的步骤中将会被定义。

6.2 自定义元素的开发与应用

6.2.1 定义与实现自定义元素

Polymer元素通常保存在 src 目录下的HTML文件中。创建一个名为 my-element.html 的新文件,并添加以下代码来定义你的自定义元素:

<link rel="import" href="../bower_components/polymer/polymer-element.html">

<dom-module id="my-element">
    <template>
        <style>
            /* 添加一些样式 */
            :host {
                display: block;
                padding: 10px;
            }
        </style>
        <h2>这是一个自定义元素</h2>
        <p>欢迎使用Polymer创建Web Components!</p>
    </template>
    <script>
        class MyElement extends Polymer.Element {
            static get is() { return 'my-element'; }
            static get properties() {
                return {
                    // 定义元素的属性
                };
            }
        }
        customElements.define(MyElement.is, MyElement);
    </script>
</dom-module>

上述代码中定义了一个简单的自定义元素 <my-element> ,并且包含了一些基本的样式和结构。

6.2.2 在项目中使用自定义元素

为了在你的项目中使用刚才创建的 <my-element> ,需要在 index.html 中引入你定义的自定义元素文件:

<link rel="import" href="/src/my-element.html">

然后,你就可以在 index.html 的body标签中使用 <my-element> 标签了。

6.3 项目打包与部署

6.3.1 打包工具的使用与配置

Polymer项目可以通过多种构建工具进行打包,如Webpack,但Polymer CLI提供了非常便捷的构建命令。在项目根目录下,运行以下命令来构建项目:

polymer build

这个命令会根据 polymer.json 中的配置来优化和打包你的应用。你可以通过修改 polymer.json 来调整构建配置,包括输出目录、JavaScript预处理器等。

6.3.2 项目部署与线上运行

打包完成后,你会在 build 目录下得到一个优化后适合线上部署的版本。你可以将这个目录中的内容上传到Web服务器。例如,如果你使用 firebase 进行部署,可以执行以下命令:

firebase deploy

这样,你的Polymer应用就成功部署到了线上环境。确保你已经安装了firebase命令行工具,并且已经登录到你的Firebase账户。

通过以上步骤,你不仅创建了一个Polymer项目,还实现了自定义元素的开发、打包、部署和运行。这不仅增强了你对Polymer框架的理解,也让你的开发更加灵活和强大。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本教程将深入分析Polymer 2014年7月5日版本的核心概念和实践技巧。Polymer是Google推出的Web组件库,通过封装自定义的HTML元素,实现模块化、可重用的网页应用构建。教程将详细探讨Web Components、Shadow DOM、HTML Imports等概念,解析项目结构,指导如何创建Polymer元素、实现数据绑定和属性定义,以及掌握生命周期方法。通过实践项目,学习者将能够熟练掌握Polymer开发流程,理解Web Components技术,为模块化Web开发打下坚实基础。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

在2013年的Google I/O大会上,Google发布了Polymer,它是一个使用Web组件构建Web应用的类库,同时也使用了为Web构建可重用组件的新的HTML 5标准。Polymer为大部分Web组件技术提供了polyfills功能,它能让开发者在所有的浏览器支持新特性前创建自己的可重用组件。此外,Polymer提供了一系列的部件的例子,其中包括天气、时钟、股票行情和线型图。 鉴于许多Web框架通过暴露JavaScript API来构建用户界面,而构建用户界面实际上就是生成一堆div和spans标记,Web 组件是原生浏览器的解决方案因此不依赖于一个完整的框架。因此,与现在的一般依赖某个Javscript框架的HTML组件相比,Web组件希望能减少碎片。尽管Web组件是一项有趣的新技术,但在浏览器们都支持它们(并且大部分用户都开始使用这些浏览器们)之前,其中的大部分功能还派不上用场。解决这个问题的一种方法是使用polyfills(译者注:polyfills原指一种墙面填料)。polyfill指的是一段代码,它能实现期望最终由浏览器自身实现的功能。如果浏览器本身已经实现了该功能,则polyfill什么都不做。否则,它模拟其实现而不依赖API。例如,webshims库实现了HTML5中各种功能,包括canvas、HTML5表格和在某些尚不支持地理位置定位的浏览器中实现地理位置定位功能。Polymer中的polyfills为需要使用Web组件成功构建应用提供了多种Web技术,包括:    HTML imports:种在其他HTML document中引入和重用HTML document的方法。    自定义元素:让开发者定义和使用自定义DOM元素。    Shadow DOM:在DOM中提供的封装。    模型驱动视图(Model Driven Views):提供象AngularJS的数据绑定。    ·Web动画:实现复杂动画的API。    ·Pointer事件:对鼠标触摸和手写笔事件的封装这些polyfills可以分开使用而不需要使用Polymer的其他部分。此外,Polymer提供:    polymer.js: Polymer的核心运行引擎,能轻易创建自定义属性和事件。    一系列可重用的可视和非可视元素。 标签:Polymer  Web框架
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值