Chrome推送通知实战:演示项目解析

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

简介:Chrome推送通知利用Web Push API和服务工作线程(Service Worker)实现跨平台桌面和移动设备通知。本文通过"push-notification-chrome"项目展示如何在Chrome浏览器上实现这一功能,涵盖权限请求、事件处理、通知对象创建等关键步骤。文章还强调了兼容性、安全性和测试的重要性,以及服务器端集成的必要性。通过分析该项目,读者能够掌握Chrome推送通知的实现原理和关键操作。 push-notification-chrome:Chrome 推送通知演示

1. Chrome推送通知概述

随着移动互联网的普及,用户对即时信息的需求越来越高。在这种背景下,浏览器推送通知(Web Push Notifications)作为一种有效的用户通知形式,成为了开发者与用户沟通的新桥梁。Chrome作为市面上使用率最高的浏览器之一,对推送通知提供了原生支持,通过Web Push API为网页应用带来了一种全新的主动式用户体验方式。

推送通知允许网站在不需要打开浏览器标签的情况下,直接与用户进行互动。这对于提升用户参与度、增强用户忠诚度都具有重要的意义。例如,新闻网站可以实时向用户推送最新消息,电商平台则可以发布促销活动的即时通知。然而,推送通知并非浏览器原生支持的功能,开发者需要通过使用Web Push API来实现这一功能。

在本章中,我们将对Chrome推送通知的概念、机制、以及实现方式做全面的介绍,为读者提供一个清晰的基础知识框架。下一章我们将深入探讨Web Push API的核心作用及其在实际开发中的应用。

2. Web Push API核心作用

2.1 API概述与基本原理

2.1.1 推送通知的工作流程

Web Push API 是现代Web应用中用于实现服务器端向用户浏览器发送通知的接口。其工作流程大致分为以下几个步骤:

  1. 用户订阅 :用户访问网站并同意接收通知后,浏览器会生成一个订阅详情,包括了订阅的端点(endpoint)、认证秘钥等信息。
  2. 服务器端处理 :将订阅详情发送给应用服务器,服务器保存这些信息,并使用这些订阅端点来发送通知。
  3. 通知发送 :服务器通过Web Push API发送通知,通知内容被加密后通过HTTP请求发送至用户浏览器。
  4. 用户设备接收 :浏览器接收到通知后,根据是否配置了服务工作线程(Service Worker),对通知进行接收或处理。
  5. 用户交互 :用户可以点击通知,触发浏览器打开网页或者执行JavaScript代码进行进一步的交互。

2.1.2 Web Push API的角色与优势

Web Push API 在整个推送通知流程中扮演了核心角色:

  • 跨浏览器 :作为标准化的API,它提供了一种方式让开发者可以在不同的浏览器中实现推送通知功能。
  • 减少服务器负载 :通过向用户的设备推送通知而不是轮询服务器,极大地减少了不必要的服务器请求,节约资源。
  • 即时更新 :推送通知能够在信息更新的第一时间通知用户,避免了用户需要手动刷新页面来获取最新信息。
  • 用户体验提升 :即时、相关且不侵入式的推送通知能提升用户体验,增强用户与应用的互动。

2.2 API的实际应用场景

2.2.1 实时新闻更新提醒

新闻网站或实时信息平台利用Web Push API向用户推送最新的新闻更新。用户无需刷新页面,就可以获得最新的新闻动态。

// 示例代码:发送新闻推送通知
function sendNewsPush() {
    const pushSubscription = getSubscription(); // 获取用户订阅信息的函数
    const payload = {
        title: "Breaking News",
        body: "New update on the election results.",
        icon: "/path/to/icon.png",
        tag: "news",
        data: {
            url: "https://example.com/news-updates"
        }
    };

    const options = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'TTL': 2419200 // 保存通知的时间
        },
        body: JSON.stringify(payload)
    };

    fetch(pushSubscription.endpoint, options);
}

在上述代码示例中,我们构建了一个通知负载(payload),指定了通知的标题、内容、图标等,并通过 fetch 函数向用户订阅的端点发送请求,实现了通知的推送。

2.2.2 电商促销通知

电子商务网站可利用推送通知向用户推送促销活动信息,吸引用户回归网站购物。比如,针对用户曾浏览过但未购买的商品进行特别优惠通知。

// 示例代码:发送促销活动推送通知
function sendPromotionPush() {
    // 获取订阅信息和构建推送通知的逻辑与sendNewsPush相同
    const payload = {
        title: "Special Discount Alert",
        body: "50% off on all tech products.",
        icon: "/path/to/discount-icon.png",
        actions: [
            { action: "view-discount", title: "View Discount" },
            { action: "dismiss", title: "Dismiss" }
        ]
    };

    // 发送通知的逻辑与sendNewsPush相同
    fetch(pushSubscription.endpoint, options);
}

此段代码展示了一个促销活动的推送通知,用户不仅可以查看折扣详情,还可以选择忽略通知。这样,商家可以更精确地向潜在购买者推送个性化内容,增加用户对促销信息的响应率。

3. 服务工作线程(Service Worker)功能

3.1 Service Worker基础概念

3.1.1 Service Worker的作用

Service Worker是浏览器的一个高级特性,它允许在Web应用背后运行JavaScript代码,即使没有打开该页面或者浏览器被关闭。Service Worker的主要作用在于拦截和处理网络请求,以及接收服务器端的推送消息,从而实现离线应用和后台同步等功能。

Service Worker充当着客户端与服务器之间的代理,它能控制和管理网络请求,甚至可以进行缓存操作,使得Web应用在没有网络连接时仍能访问到之前缓存的资源。这一特性使得Service Worker在开发渐进式Web应用(PWA)中扮演着核心角色。

3.1.2 Service Worker生命周期

Service Worker拥有自己独特的生命周期,主要包括以下三个阶段:

  • 注册(Registration):通过 navigator.serviceWorker.register() 函数注册Service Worker。
  • 安装(Installation):当注册成功后,浏览器会尝试安装Service Worker,安装完成后进入等待状态。
  • 激活(Activation):一旦安装完成,并且没有页面正在使用旧的Service Worker,新的Service Worker就会被激活,并开始接收事件。

理解Service Worker的生命周期对于开发和调试Service Worker至关重要,开发者需要合理管理这些阶段,确保应用能够顺畅地更新和运行。

3.2 Service Worker的生命周期管理

3.2.1 安装与激活

Service Worker的安装通常发生在首次访问应用时,浏览器会检查Service Worker文件是否有变化,如果有,则触发安装事件。在安装事件的处理函数中,开发者通常会初始化缓存,比如:

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('v1').then(cache => {
      return cache.addAll([
        '/app-shell',
        '/styles/main.css',
        '/scripts/main.js'
      ]);
    })
  );
});

在上述代码块中, event.waitUntil 确保了Service Worker只在缓存操作成功后才会完成安装。

Service Worker的激活发生在安装之后,此时它会监听 activate 事件。激活事件是清理旧缓存的好时机:

self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.filter(cacheName => {
          return cacheName !== 'v1';
        }).map(cacheName => caches.delete(cacheName))
      );
    })
  );
});

3.2.2 事件监听与处理

一旦Service Worker处于激活状态,它就会开始监听各种事件,例如 fetch 事件用于拦截网络请求, push 事件用于处理服务器端推送的的通知等。

处理 fetch 事件的代码示例如下:

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(response => {
      return response || fetch(event.request);
    })
  );
});

在这段代码中, event.respondWith 方法允许开发者拦截请求并返回一个响应。如果缓存中有请求的资源,则直接返回缓存的资源;如果没有,则发起新的网络请求。

Service Worker的事件监听与处理是提供离线支持和实现后台数据同步的关键,开发者必须精心设计以确保用户体验的连贯性和应用的稳定性。

4. 订阅管理与密钥对生成

4.1 订阅流程详解

4.1.1 订阅过程中的步骤

推送通知的订阅流程是Web Push API中的关键一环,它涉及到用户的授权和服务器端的配置。以下是订阅流程中的关键步骤:

  1. 权限请求 :首先,网站必须向用户请求发送推送通知的权限。这通常在用户与网站交互后,例如点击一个按钮或访问某个页面时触发。
  2. 创建订阅 :一旦用户授予权限,浏览器会与服务器协调,创建一个订阅。这个订阅包含了将消息发送到服务器所需的所有信息,包括终点(endpoint)、公钥和认证密钥。

  3. 保存订阅信息 :订阅信息会被发送到服务器,通常保存在数据库中,以便日后向该用户发送通知。

  4. 发送通知 :当有消息需要发送给用户时,服务器使用保存的订阅信息来构造并发送一个推送消息到用户的浏览器。

4.1.2 订阅参数的配置与使用

订阅参数的配置主要涉及在客户端和服务端设置正确的推送服务信息。以下是一个配置订阅参数的示例代码:

async function subscribeUser() {
  const applicationServerKey = urlB64ToUint8Array(
    'BEl62iUYgUivxIkv69yViEgsrogjVzT_105Yov94Dl666j5N6fS_jDCaS6UvTq91D0f03S_nqGyL'
  );
  const options = {
    userVisibleOnly: true,
    applicationServerKey: applicationServerKey
  };

  const subscription = await navigator.serviceWorker.register('sw.js')
    .then(registration => registration.pushManager.subscribe(options));

  // 将订阅信息发送到服务器
  // 这里使用fetch API发送POST请求,服务器地址和数据需要自己配置
  fetch('https://yourserver.com/save-subscription', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(subscription),
  });
}

在这个例子中, applicationServerKey 是从你的推送服务提供商那里获取的公钥,它用于订阅流程以确保端到端的安全性。 userVisibleOnly 选项确保通知总是可见的,这是当前Web Push API要求的。

4.2 VAPID密钥对的作用与生成

4.2.1 VAPID密钥对的安全性

VAPID(Voluntary Application Server Identification for Web Push)是Web Push API中的一个安全特性,它允许服务器与推送服务提供商之间建立一个额外的安全层。VAPID密钥对由服务器端生成,包括一个公钥和一个私钥,用来在推送服务与应用服务器之间建立身份验证。

使用VAPID可以使得推送消息不依赖于第三方的推送服务提供商,即使推送服务提供商被更换,已有的订阅依然可以使用。此外,VAPID增加了服务器的身份验证,这意味着即使有人控制了推送服务,他们也无法冒充你的服务器发送消息。

4.2.2 密钥对的生成方法

VAPID密钥对可以通过多种方式生成,这里我们使用Node.js中的 elliptic 库来生成它们:

const EC = require('elliptic').ec;
const ec = new EC('p256');

const keyPair = ec.genKeyPair();
const publicKey = keyPair.getPublic('hex');
const privateKey = keyPair.getPrivate('hex');

console.log(`Public key: ${publicKey}`);
console.log(`Private key: ${privateKey}`);

执行这段代码将会输出一对公钥和私钥。公钥需要发送给推送服务提供商进行注册,而私钥需要安全地存储在你的服务器上,以便在发送推送通知时使用。

生成密钥后,需要将公钥以JSON Web Token (JWT) 格式与推送服务提供商进行交换。这是一个JWT的示例:

{
  "aud": "https://fcm.googleapis.com",
  "exp": 1469618798,
  "sub": "mailto@example.org"
}

在实际使用中,你需要将公钥和一些额外的声明(如应用程序的邮箱)嵌入到JWT中,并使用你的VAPID私钥进行签名。然后,将这个JWT连同订阅的公钥一起发送到推送服务提供商进行验证。

5. 用户权限请求处理

5.1 权限请求的触发时机

5.1.1 用户权限与推送通知关系

推送通知是一种需要获得用户明确授权的功能,因此在用户与Web应用的交互中,何时触发权限请求成为了一个关键点。用户权限与推送通知的关联遵循以下原则:

  • 用户必须先与Web应用交互,以表明他们对在浏览器中接收通知感兴趣。
  • 一旦用户符合特定的交互行为,浏览器会自动显示权限请求对话框,向用户索要通知权限。
  • 用户的选择会被浏览器记住,允许用户控制他们想要接收通知的程度。

权限请求机制是建立在浏览器安全模型之上的。这意味着用户必须已经在网站上进行了一定的交互(例如点击页面、滚动或输入),否则浏览器可能会阻止权限请求,以防止滥用并保护用户免受未请求的通知打扰。

5.1.2 如何触发权限请求

具体触发权限请求的步骤如下:

  1. 确保用户已与网页进行了交互。
  2. 检查用户是否已经获得相应的权限,如果未获得,使用 Notification.requestPermission() 方法。
  3. 浏览器将弹出一个权限请求对话框。
  4. 用户可以选择允许或拒绝权限请求。
  5. 根据用户的选择,应用需要进行相应的处理。

代码示例如下:

// 检查浏览器是否支持通知
if ("Notification" in window) {
  // 判断是否已经获得权限
  if (Notification.permission !== "granted") {
    // 请求权限
    Notification.requestPermission().then(function(permission) {
      if (permission === "granted") {
        console.log("权限已获得");
      } else if (permission === "denied") {
        console.log("权限已拒绝");
      }
    });
  }
}

上述代码首先检查浏览器是否支持通知功能,然后检查是否已经获得权限。如果没有获得权限,就会调用 Notification.requestPermission() 方法并等待用户的响应。用户的选择会赋值给 permission 变量。

5.2 权限请求的用户体验优化

5.2.1 设计用户友好的请求界面

用户对权限请求界面的体验好坏会影响他们的决策,因此必须认真设计这个界面。设计用户友好的请求界面应考虑以下因素:

  • 界面简洁:请求权限时,应直接明了地告诉用户你正在请求发送通知的权限。
  • 说明具体:清晰地说明为什么需要发送通知以及通知的内容会是什么。
  • 自定义选项:提供选择不同通知方式(如声音、振动、图标标记等)的选项,增加用户的控制感。

5.2.2 权限请求拒绝后的处理策略

即使用户拒绝了权限请求,应用也可以采取一些策略来增强用户体验:

  • 弹出提示信息,引导用户了解通知的重要性以及如何手动开启通知。
  • 提供相关的帮助链接,解释如何在浏览器设置中更改通知权限。
  • 在适当的时候重新触发权限请求,但要小心不要过多打扰用户。

处理拒绝权限请求的代码示例如下:

// 用户拒绝权限请求后的处理
if (Notification.permission === "denied") {
  console.log("通知权限已拒绝");
  // 提供重新请求权限的选项
  setTimeout(function() {
    // 弹出提示信息
    alert("若想接收通知,请在浏览器设置中更改通知权限。");
  }, 5000); // 5秒后显示提示
}

该代码段在用户拒绝权限请求后,会在5秒后弹出一个提示信息,引导用户理解通知的重要性,并提供更改设置的方法。

6. push 事件与事件监听器

6.1 push 事件的触发机制

6.1.1 事件触发条件

push 事件是Web Push API中最为核心的事件之一,它的触发标志着从服务器向客户端发送通知的开始。此事件只有在用户已经授予了应用推送通知权限,并且服务工作线程(Service Worker)处于激活状态时才会被触发。以下是一些关键的触发条件:

  • 用户订阅了推送通知
  • Service Worker成功注册并处于激活状态
  • 应用在浏览器后台运行时,或者从后台切换到前台时
  • 服务器端向应用发送了推送通知请求

6.1.2 事件处理的步骤

处理 push 事件通常涉及以下几个步骤:

  1. 监听 push 事件:在Service Worker的JavaScript文件中,使用 self.addEventListener('push', function(event) {...}) 来监听 push 事件。
  2. 从事件中获取数据:通过 event.data.json() event.data.text() 方法从 push 事件中提取服务器发送的数据。
  3. 显示通知:使用提取的数据构建一个通知对象,并使用 self.registration.showNotification(title, options) 方法将通知显示给用户。

6.1.3 示例代码

下面是一个简单的示例代码,展示了如何在Service Worker中设置 push 事件监听器:

self.addEventListener('push', function(event) {
    console.log('Push event received');
    // 解析推送通知的数据
    event.waitUntil(
        self.registration.showNotification('新消息', {
            body: '您有一条新消息!',
            icon: 'images/new-message.png',
        })
    );
});

6.1.4 参数说明与执行逻辑

  • event.waitUntil() : 这是一个确保Service Worker在处理事件期间保持活跃的机制。在这个方法内,可以进行如显示通知等操作。
  • self.registration.showNotification() : 此方法用于显示通知。它的第一个参数是通知的标题,第二个参数是一个配置对象,用来定义通知的外观和行为。

6.2 事件监听器的实现与管理

6.2.1 监听器的编码实现

在实现 push 事件监听器时,我们需要确保能够处理来自服务器的数据,并将其转化为用户友好的通知。下面是一个处理推送通知数据并显示通知的示例:

self.addEventListener('push', function(event) {
    // 检查通知数据是否存在
    if (event.data) {
        // 获取并处理数据
        var data = event.data.json();
        console.log('Received push message:', data);
        // 显示通知
        var notificationOptions = {
            body: data.message,
            icon: data.icon || 'default-icon.png',
        };
        event.waitUntil(
            self.registration.showNotification(data.title, notificationOptions)
        );
    } else {
        console.log('No data available');
    }
});

6.2.2 错误处理与日志记录

在实际应用中,错误处理非常重要,以确保应用在遇到问题时能够恢复。示例中通过 console.log 进行了日志记录,但在生产环境中,推荐使用更成熟的日志系统进行错误追踪和监控。

self.addEventListener('push', function(event) {
    event.waitUntil(
        self.registration.showNotification('更新可用', {
            body: '点击以更新您的应用程序',
        })
        .catch(function(error) {
            // 记录错误信息
            console.error('Failed to show notification:', error);
        })
    );
});

通过结合错误处理和日志记录,我们可以更好地理解事件监听器的工作情况,确保在出现问题时快速响应。

6.3 push 事件的实践案例分析

6.3.1 实际业务场景下的应用

在实际的业务场景中, push 事件通常用于更新提醒、系统消息或其他重要通知。例如,一个在线协作工具可能会使用 push 事件提醒用户有关新评论或文件上传的通知。

self.addEventListener('push', function(event) {
    // 实际业务场景下的事件处理
});

6.3.2 性能优化与用户体验

push 事件的处理也需要考虑到性能和用户体验。例如,避免在事件处理中执行复杂的操作或长时间的计算。通知应该快速呈现给用户,确保响应及时。

self.addEventListener('push', async function(event) {
    // 快速处理,确保用户体验
});

6.3.3 与用户交互的增强

push 事件还可以与用户进行交互,例如在通知上添加点击事件的回调,或者根据用户的点击动作进行不同的操作。

self.addEventListener('push', function(event) {
    // 与用户交互的逻辑
});

6.3.4 安全性考量

当处理来自服务器的数据时,需要确保数据的安全性和验证。避免将用户暴露在潜在的安全风险中,如XSS攻击。

self.addEventListener('push', function(event) {
    // 安全性考虑
});

6.3.5 跨平台实现

push 事件在不同的浏览器和设备上有不同的支持情况。因此,在实现时需要注意兼容性和平台差异。

self.addEventListener('push', function(event) {
    // 跨平台考虑
});

6.3.6 代码版本控制与迭代

随着应用的发展, push 事件的处理逻辑可能需要更新。因此,合理的代码版本控制和迭代规划是必要的。

self.addEventListener('push', function(event) {
    // 代码迭代考虑
});

本章节通过代码示例、逻辑分析和参数说明,深入探讨了 push 事件的触发机制和事件监听器的实现方法。这些知识不仅有助于理解Web Push API的核心功能,而且还为创建高效、可靠的通知系统提供了实践指导。

7. 构建通知对象

通知对象是Web Push API中最直接的用户交互方式。它决定了用户接收到的推送通知的具体内容和行为。为了确保通知对象既能够传递必要的信息,又不会对用户体验造成干扰,开发者需要精心设计通知内容,并利用通知对象提供的高级特性。

7.1 通知内容的设计原则

通知内容的设计旨在向用户传达关键信息,并在不干扰用户当前任务的情况下提供必要的交互入口。开发者需要考虑通知的简洁性、相关性以及用户交互的设计。

7.1.1 标题与消息体的设计

通知的标题和消息体是用户首先注意到的部分。标题应简洁明了,能够快速传达通知的核心内容。消息体则提供更多的细节,但也要注意不可过长,避免用户阅读负担。

const options = {
  body: '这是一条来自网站的推送通知。点击这里回到网站!',
  icon: 'https://example.com/icon.png',
  vibrate: [200, 100, 200], // 调整振动频率
};

在上面的示例中,我们定义了一个通知对象,其中包含了通知的标题、图标和振动模式。振动模式可以根据需要调整以吸引用户注意。

7.1.2 通知动作的添加与配置

通知动作允许用户直接从通知面板对通知进行响应,提高用户操作的便捷性。动作的添加需要在通知对象中指定,并且每一个动作都必须是事先在代码中定义好的。

const options = {
  actions: [
    { action: 'open-site', title: '打开网站' },
    { action: 'mark-as-read', title: '标记为已读' }
  ],
};

self.registration.showNotification('新消息', options);

这段代码展示了如何为通知添加两个动作:打开网站和标记为已读。用户可以通过点击这些动作直接执行预定义的JavaScript函数。

7.2 通知对象的高级特性

除了基本的内容和动作,通知对象还提供了其他高级特性,比如多媒体内容的嵌入和通知超时的设置,以增强用户体验。

7.2.1 多媒体内容的嵌入

为了使通知更加吸引人,可以将图片或视频嵌入到通知中。这可以提高用户对通知内容的兴趣,并且促进用户与通知的互动。

const options = {
  body: '这是一条包含图片的推送通知',
  image: 'https://example.com/image.jpg',
  // 其他选项...
};

self.registration.showNotification('多媒体通知', options);

在本示例中,我们添加了一个图片到通知中。在支持的浏览器中,用户将能看到这张图片作为通知的一部分。

7.2.2 通知的超时设置与管理

开发者可以设置通知在一定时间后自动消失,以避免用户被过多的通知所困扰。这有助于提升用户的整体体验。

const options = {
  body: '这是一条会自动消失的推送通知',
  // 其他选项...
};

// 设置通知显示5秒后自动关闭
setTimeout(() => {
  self.registration.getNotifications().then(notifications => {
    notifications.forEach(notification => notification.close());
  });
}, 5000);

self.registration.showNotification('自定义超时通知', options);

上述代码中,我们首先通过 showNotification 显示了一个通知,然后使用 setTimeout 函数在5秒后关闭了通知。这样,用户不会因为通知长时间保持打开状态而感到烦躁。

通知对象的构建是Web Push API中实现良好用户体验的关键一步。通过对通知内容的仔细设计以及对高级特性的合理应用,开发者可以显著提高通知的吸引力和功能性,从而提升用户的互动率和满意度。

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

简介:Chrome推送通知利用Web Push API和服务工作线程(Service Worker)实现跨平台桌面和移动设备通知。本文通过"push-notification-chrome"项目展示如何在Chrome浏览器上实现这一功能,涵盖权限请求、事件处理、通知对象创建等关键步骤。文章还强调了兼容性、安全性和测试的重要性,以及服务器端集成的必要性。通过分析该项目,读者能够掌握Chrome推送通知的实现原理和关键操作。

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

基于gcc的stm32环境搭建源码+文档说明.zip,个人经导师指导并认可通过的高分设计项目,评审分99分,代码完整确保可以运行,小白也可以亲自搞定,主要针对计算机相关专业的正在做毕业设计的学生和需要项目实战练习的学习者,可作为毕业设计、课程设计、期末大作业,代码资料完整,下载可用。 基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的stm32环境搭建源码+文档说明.zip基于gcc的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值