前端PWA应用的相关知识和基础Demo

news2024/9/30 5:35:39

一、什么是PWA应用?

1、PWA简介

​ 渐进式Web应用(Progressive Web App),简称PWA,是 Google 在 2015 年提出的一种使用web平台技术构建的应用程序,官方认为其核心在于Reliable(可靠的)、Fast(快速的)、Engaging(可参与的),结合了web网站程序和原生应用程序两者的优点,可以带给用户更佳的使用体验。

​ PWA既能像网站一样,通过一套代码在多个平台运行,而且可以通过浏览器进行访问,并通过Url链接进行分享。又能像原生应用一样,通过应用商店或网页安装在设备上,安装之后可以通过图标访问,作为一个独立的应用程序被启动;而且即使脱离网络,也可以通过应用缓存访问到部分页面和数据。

​ 但需要注意的是,当PWA应用通过安装在设备上的图标打开时,虽然从外观上看来像是一个原生的应用程序,但从技术角度来看,其仍属于网站范畴,所以仍需要一个浏览器引擎来解析和运行,为其提供正常运行的环境。因此其原理类似于打开了一个单独的、自定义窗口内容的浏览器窗口。

​ PWA不仅是一种技术,更代表了一种Web网站的开发理念,如果一个网站程序实现了可安装、可离线等多种特定功能,我们就可以将其视为一个PWA应用。目前国内支持PWA的网站有:微博、语雀等等。

在这里插入图片描述

2、PWA特点

​ 原生应用程序代表了最佳的功能,因为其与操作系统深入结合,拥有易于访问、可离线、操作系统集成等优点。 Web 网站程序则代表了最广的范围,因为其以浏览器为基础,拥有跨平台、无需下载、易于更新部署等优点。而PWA 则处于原生应用程序功能和 Web 网站程序范围的交叉点,是两者的结合体,主要拥有以下几种特点:

① 跨平台: PWA应用只需开发者书写一套代码,就可以在不同操作平台上运行,而且PWA应用采取渐进式增强的理念,其核心功能可以在任何浏览器上正常运行,其余强大的功能则需要依赖于浏览器对PWA特性的支持,根据浏览器的支持性,逐步升级体验。

② 可安装: PWA应用可以添加到主屏幕或应用程序菜单中,实现类似原生应用的图标入口,点击图标,作为一个独立应用被启动,用户可以更方便地访问应用。也可以将程序打包并上传各个应用商店,让用户通过应用商店安装网站应用。

③ 离线访问: PWA应用具备离线访问的能力,它们可以缓存应用的核心资源,使得用户可以在没有网络连接的情况下继续访问应用,查看到部分页面和数据,提供基本的功能,并在网络恢复时更新缓存。

④ 推送通知: PWA应用可以主动发送推送通知给用户,使得应用可以及时通知用户有关重要更新、新消息或其他关键信息,类似于原生应用的通知功能。

⑤ 快速加载: PWA应用使用Service Workers来缓存资源并提供离线体验,这也使得应用可以更快地加载和响应用户操作。

⑥ 可搜索: PWA应用可以通过搜索引擎被发现,而且可以通过url链接进行分享。

⑦ 热更新: PWA应用中的部分内容发生更新时,可在联网后自动进行局部热更新,确保用户能用到最新的应用程序,而无需像原生应用一样,重新下载安装客户端。

​ 结合官方提出的Reliable(可靠的)、Fast(快速的)、Engaging(可参与的)三个核心,我认为跨平台、离线访问体现了Reliable(可靠的),无论是在低版本浏览器还是无网络的情况下,PWA都可以展示基本功能;快速加载、热更新则体现了Fast(快速的),利用缓存和自动更新,减少重复数据加载,提升响应速度;可安装和推送通知则体现了Engaging(可参与的),可安装在设备上,并向用户推送通知。

3、适用场景

​ 地图导航、资料文档、博客笔记等等。

二、PWA的核心技术是什么?

​ PWA的实现依赖于多种技术实现,其中最核心的技术为Service WorkerWeb App ManifestPush Notification

1、Service Worker

​ Service Worker是一个独立于网页线程的脚本,无权访问页面的DOM结构,充当了网站和浏览器之间的代理服务器,每个PWA应用都只能注册一个Service Worker,其在PWA中主要用来实现离线访问、缓存资源、推送通知等功能,当然除此之外,它还具有很多其他功能,在这我们就不展开讲述了。

​ 在网络正常时,当PWA应用请求Service Worker范围内的资源时,Service Worker会拦截该请求,并充当网络代理,然后它可以决定是从缓存中获取数据还是从服务器中获取数据。如果是从服务器中获取数据,Service Worker会缓存请求的数据,等到离线访问时,返回缓存的数据,使得PWA应用可以在离线状态下运行,并且可以利用缓存提升应用的加载速度。

​ 由于Service Worker权利太大,能够直接截取并返回用户的请求,处于安全性考虑,目前仅支持在HTTPS或本地环境的安全环境下使用。

​ Service Worker的浏览器兼容性如下图:
在这里插入图片描述

如何为PWA注册Service Worker?

​ 在Service Worker控制页面之前,必须在PWA应用中注册Service Worker服务。这意味着,在用户第一次访问PWA应用时,页面还并未受到Service Worker的控制,也就无法实现离线访问等功能。

​ 注册Service Worker时,我们只需先判断浏览器是是否支持相关的API,如果支持则直接通过navigator.serviceWorker.register(url)进行注册即可,参数url表示具体Service Worker逻辑代码文件的路径。

// 这是页面中唯一与Service Worker有关的代码
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
   .then(registration => {
     console.log('Service Worker 注册成功!', registration);
   })
   .catch(error => {
     console.log('Service Worker 注册失败:', error);
  });
}

​ 如果想要查看Service Worker是否已经注册并正常运行,以Chrome浏览器为例,我们可以通过F12开发者工具中的Application,然后选中左侧的Service Workers ,如果右侧展示的信息中的Status中显示activity则表示已经注册并正常运行。

在这里插入图片描述

​ 如果想要在移动端页面检查是否已经注册并正常运行,也只能通过连接电脑调试的方法来查看,具体可查看该文档:tools-and-debug。

Service Worker的作用范围怎么确定?

​ Service Worker在注册时引入的具体逻辑文件所在文件夹决定了其作用范围,例如:

navigator.serviceWorker.register("example.com/my-pwa/serviceworker.js");

​ 则该Service Worker的作用范围在my-pwa文件夹下的任何文件,如: example.com/my-pwa/index.html等等。

​ 为了实现Service Worker在PWA应用中的作用最大化,推荐将具体逻辑文件设置在PWA应用程序的根目录下,因为这样可以拦截到PWA应用中的所有请求。

Service Worker的生命周期分为哪些阶段?

​ Service Worker 的生命周期从注册 Service Worker 开始,也就是前文所说的register()方法,调用该方法时,就会发生注册行为。该生命周期阶段并没有对应的事件,然后我们可以通过register()方法的.then()来判断是否注册成功。

① Registration(注册)

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
   .then(registration => {
     console.log('Service Worker 注册成功!', registration);
   })
   .catch(error => {
     console.log('Service Worker 注册失败:', error);
  });
}

​ 然后浏览器开始下载并安装 Service Worker 文件,安装成功后,则会触发install事件,在整个生命周期中,install事件仅会触发这一次。开发者通常会在此事件中进行初始化,缓存一些静态资源,以备离线时访问。

② Installation(安装)

// 安装阶段
self.addEventListener('install', function(event) {
  event.waitUntil(
    // 向缓存中存储基本数据
    caches.open('cache-name').then(function(cache) {
      return cache.addAll([
        '/path/to/resource1',
        '/path/to/resource2',
        // ...
      ]);
    })
  );
});

​ 在Service Worker中,我们需要通过全局对象self才能监听各个生命周期事件。在waitUntil()方法执行结束之前,Service Worker不会结束安装状态,必须等待其内部代码执行结束之后,才会进入到下一个生命周期。caches对象是限制在Service Worker 生命周期内使用的特殊对象,用于实现数据的缓存。

③ Activation(激活)

​ 当Service Worker安装完成后,并不会立即进入激活状态,为了不影响当前正在访问的页面,此时Service Worker 并没有控制当前页面。所以要等到当前页面关闭,且再次加载该页面时,Service Worker才会进入激活状态,触发activate事件,开始控制网页的请求和缓存。在此阶段,开发者通常会进行清理旧的缓存、处理更新逻辑等操作,因为浏览器的缓存空间是有限的。

// Service Worker激活成功后 
self.addEventListener('activate', function(event) {
  event.waitUntil(
    // 对缓存中的数据进行处理
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        // 只保留符合要求的数据 删除不需要的旧数据
        cacheNames.filter(function(cacheName) {
          return cacheName !== 'cache-name';
        }).map(function(cacheName) {
          return caches.delete(cacheName);
        })
      );
    })
  );
});

​ 在waitUntil()方法执行结束之前,Service Worker不会进入下个状态,然后可以通过caches对象,对缓存的数据进行操作。

​ 还有要注意的一点是,Service Worker 进入激活状态后,它会一直保持激活状态,除非被手动注销或者被新的 Service Worker 脚本取代。

④ Update(更新)

​ 浏览器会周期性的检测当前应用的Service Worker是否有更新,当检测到Server Worker 脚本文件发生更新时,会在后台下载新的脚本,并触发更新流程。更新流程与安装流程类似,需要经历下载、安装、激活三个阶段。下载完成之后,会立即进行安装,但是安装完成之后,默认并不会立即激活,而且进入等待状态。因为同一时间只能有一个版本的 Service Worker处于Activation状态。只有当旧版本的Service Worker控制的所有页面都被关闭,然后用户再重新访问这些页面时,新的Service Worker才会被激活并接管旧版本所有页面的控制权。

​ 我们也可以通过skipWaiting()方法来强制激活等待中 Service Worker,使其取代旧版 Service Worker,获得页面的控制权。该方法只有在存在等待状态的 Service Worker时,调用才会有意义,所以通常都在install事件中执行调用。

// 新版Service Worker的install事件
self.addEventListener("install", (event) => {
  // 安装好后 调用skipWaiting() 使其立即激活
  // skipWaiting() 返回一个 promise,但完全可以忽略它
  self.skipWaiting();
  // 然后执行 service worker 安装所需的缓存数据等其他操作
  e.waitUntil(
    (async () => {
      const cache = await caches.open(cacheName);
      await cache.addAll(contentToCache);
    })(),
  );
});

⑤ Termination(终止)

​ 当Service Worker被手动注销,或被新版本Service Worker取代后,就会进入终止阶段,它将不再控制页面的请求,并释放相应的资源。即使不被注销或者取代,Service Worker也不会无限期的存活,各大浏览器的处理逻辑不同,但在激活一段时间后,Service Worker就会被终止。终止之后,需要重新注册,才能继续运行。

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
   .then(registration => {
     console.log('Service Worker 注册成功!', registration);
     // 手动注销Service Worker
     registration.unregister().then(function (boolean) {
        if(boolean) {
          console.log('Service Worker 注销成功!')
        }
      });
   })
   .catch(error => {
     console.log('Service Worker 注册失败:', error);
  });
}

⑥ Fetch(请求)

​ Service Worker 还提供了一个fetch事件,每当Service Worker控制的页面中,发出fetch请求或者html、css、js等资源请求时,都会触发该事件,我们可以在此阶段拦截请求并结合缓存使用自定义响应来响应请求。注意:ajax请求不会触发该事件。

​ 通常当请求的资源存在缓存时,我们都会从缓存中获取资源而不是从服务器获取。如果缓存中没有,那我们会使用另一个请求从服务器获取资源,并将资源存储在缓存中,以便下次请求或离线请求时使用。

self.addEventListener("fetch", (e) => {
  e.respondWith(
    (async () => {
      // 从缓存中获取资源
      const r = await caches.match(e.request);
      console.log(`Service Worker正在请求资源: ${e.request.url}`);
      if (r) {
        // 如果缓存中存在资源 则直接返回缓存中的资源
        return r;
      }
      // 如果缓存中没有 则去服务器请求资源
      const response = await fetch(e.request);
      const cache = await caches.open(cacheName);
      console.log(`Service Worker 缓存新资源: ${e.request.url}`);
      // 将请求的资源存储到缓存中 
      cache.put(e.request, response.clone());
      // 将请求结果缓存
      return response;
    })(),
  );
});

​ 该fetch事件的事件对象event中包含了一个respondWith()方法,该方法可以阻止浏览器默认的fetch请求操作,并允许自定义请求的response,更多信息请查看:FetchEvent.respondWith()。

2、Web App Manifest

​ Web App Manifest(Web应用清单),是一个遵守W3C规范的JSON文件,用来定义PWA安装的客户端在设备上应该如何显示和运行,例如应用的名称、图标、启动方式等等,该文件是实现PWA所必需的。通过该文件,用户可将PWA应用安装到用户的主屏幕上,使其更像一个原生应用的客户端。

​ 该文件中可定义的应用信息很多,其中比较常用的有以下几条:

① name

​ 该字段定义PWA应用的全名,是Web App Manifes中必须的一个基本字段。该名称一般会显示为应用商店的应用名称,也会在应用启动时显示在标题栏中。

"name": "学科网PWA示例"
② short_name

​ 该字段定义PWA应用的简称,尽量控制在12个字符以内,当应用程序被安装在桌面上时,由于空间有限,通常就会显示该简称,但具体展示name还是short_name可能因设备、浏览器或操作系统而有所不同,例如:在macos系统中,统一展示name字段。

"short_name": "PWA示例"
③ icons

​ 该字段定义了应用程序安装在桌面上的图标,属性值为一个数组,数组元素为一个对象,对象中包含srcsizestype三个属性,分别代表图标地址、图标的尺寸和图标的MIME类型。

  • src:指定了图标文件的位置,字段值可以是相对于manifest文件的相对URL,或者是一个绝对的网络URL。
  • sizes:指明了图标的尺寸,以宽×高的形式指定了图标的宽高,单位默认为px,目前设备适配性最好的图标尺寸为512×512
  • type:指明了图标的MIME媒体类型,帮助浏览器在选择合适的图标文件,例如:image/pngimage/jpeg等等。

​ 该字段属性值数组至少需要定义一个图标元素,也可以定义多个不同格式的图标元素,从而为用户提供最佳的图标效果。每个浏览器都会根据其需要和所安装的操作系统选择其中最接近其所需的规范的某个图标。图标选择规则很多,主要有尺寸匹配、类型匹配、设备类型匹配等规则。

"icons": [
   {
      "src": "icons/512.png",
      "type": "image/png",
      "sizes": "512x512"
   },
   {
      "src": "icons/1024.png",
      "type": "image/png",
      "sizes": "1024x1024"
   }
]
④ start_url

​ 该字段定义PWA应用的起始URL,用户点击图标打开程序时,将会加载这个URL所对应的页面,可以是相对于manifest文件的相对路径,也可以是一个绝对路径。推荐使用绝对路径,如果PWA应用的主页是网站的根目录,那么将该字段设置为/即可。如果没有设置该字段,则默认将安装PWA应用时的URL作为该字段的值。

"start_url": "./index.html"
⑤ display

​ 该字段定义了PWA应用的打开方式,字段值有以下四种:

  • standalone(推荐):应用将以独立窗口打开,类似于原生应用程序,没有导航栏等浏览器功能。

在这里插入图片描述

  • fullscreen:应用将以全屏模式打开,隐藏浏览器的地址栏和工具栏。由于电脑操作系统的限制,该字段值表现效果与standalone一致。

  • minimal-ui:应用将独立窗口打开,但保留了一部分浏览器的导航功能,如后退、刷新功能等。

在这里插入图片描述

  • browser:应用将以常规浏览器网页的形式打开,类似于设置了一个网页的快捷方式。但是由于电脑操作系统的限制,该字段值表现效果与standalone一致。

    "display": "standalone"
    
⑥ id

​ 该字段用于作为PWA应用的唯一标识,如果未设置,则默认以start_url的值为字段值。

"id": "xkw-pwa"
⑦ background_color

​ 该字段定义了PWA应用窗口打开后且样式表加载完成之前的窗口背景色,字段值支持关键字(red、green等)、十六进制色值(#FFFFFF、#CCCCC等)和RGB色值(rgb(255,255,255)等),但不建议使用rgba()等带有透明度的颜色,因为各个浏览器的展示效果可能大相径庭。但是目前iOS 和 iPadOS 上的 Safari 以及部分桌面浏览器目前会忽略此字段。

"background_color": "#000000",
⑧ theme_color

​ 该字段定义了PWA应用的窗口主题色,将会影响窗口工具栏、头部标题栏等区域的颜色,段值支持关键字(red、green等)、十六进制色值(#FFFFFF、#CCCCC等)和RGB色值(255,255,255等)。但是该属性会被<meta name="theme-color" content="#ccc">标签设置的主题色所覆盖。

"theme_color": "#3880FF"
⑨ 其他属性

​ 。。。

3、Push Notification

PushNotification是两个独立的API,Push用来接收服务器推送的信息,Notification 用来向用户推送信息。两者都需要在 Service Worker 内调用运行。

​ 具体可查看:Push Notification

三、如何开发一个PWA应用Demo?

1、创建一个demo文件夹,用来存储相关文件

在这里插入图片描述

2、创建manifest.json文件,设置PWA应用信息
{
  "name": "猪猪侠的PWA示例", 
  "short_name": "PWA示例",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "red",
  "theme_color": "#ccc",
  "icons": [
    {
      "src": "/icons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

3、创建icons文件夹,存储PWA应用图标文件

​ 存储以下两个图标文件:

在这里插入图片描述
在这里插入图片描述

4、创建main.css文件,设置页面样式
h3 {
  color: red;
}
5、创建sw.js文件,设置Service Worker相关逻辑

​ 这里我们只需要直接书写Service Worker的处理逻辑即可:

// 缓存的key值,用于区别新旧版本缓存
var cacheStorageKey = 'minimal-pwa-2'
// 设置初始需要缓存的文件
var cacheList = [
  '/',
  'index.html',
  'main.css',
  '/icons/android-chrome-512x512.png'
]
// 监听安装事件 并在此阶段 缓存基本资源
self.addEventListener('install', e => {
  e.waitUntil(
    caches.open(cacheStorageKey)
      .then(
        // 缓存基本资源
        cache => cache.addAll(cacheList)
      )
      .then(() =>
        // 当脚本更新时 使新版Service Worker强制进入activate状态
        self.skipWaiting()
      )
  )
})
// 监听fetch请求事件
self.addEventListener('fetch', function (e) {
  // 拦截相关请求
  e.respondWith(
    // 如果缓存中已经有请求的数据就终止请求 直接返回缓存数据
    caches.match(e.request).then(async function (response) {
      if (response != null) {
        return response
      }
      // 否则就重新向服务端请求
      const res = await fetch(e.request)
      // 这块需要结合具体业务具体分析 我这里的示例逻辑是无脑全部缓存
      // 请求成功后将请求的资源缓存起来 后续请求直接走缓存
      const cache = await caches.open(cacheStorageKey)
      cache.put(e.request, res.clone())
      // 将请求的资源返回给页面。
      return res;
    })
  )
})
// 监听激活事件
self.addEventListener('activate', function (e) {
  e.waitUntil(
    //获取所有cache名称
    caches.keys().then(cacheNames => {
      return Promise.all(
        // 获取缓存中所有不属于当前版本cachekey下的内容
        cacheNames.filter(cacheNames => {
          return cacheNames !== cacheStorageKey
        }).map(cacheNames => {
          // 删除不属于当前版本的cache缓存数据
          return caches.delete(cacheNames)
        })
      )
    }).then(() => {
      // 无须刷新页面 即可使新版server worker接管当前页面
      return self.clients.claim()
    })
  )
})
6、创建主文件index.html,设置页面DOM,并引用各类资源
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Hello PWA</title>
  <meta name="viewport"
    content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <link rel="stylesheet" href="main.css">
  <link rel="manifest" href="manifest.json">
</head>

<body>
  <h3>Hello 猪猪侠的PWA</h3>
</body>
<script>
  // 检测浏览器是否支持SW
  if ('serviceWorker' in navigator) {
    // 为当前页面注册Service Worker
    navigator.serviceWorker.register('./sw.js')
      .then(function (registartion) {
        console.log('当前浏览器支持sw:', registartion.scope);
        console.log('Service Worker注册成功', registartion);
      })
  }
</script>
</html>
7、部署到服务器上(https) 或在本地环境使用

以本地环境为例,使用VSCode作为辅助工具:

① 在VSCode中,右键选中index.html文件,选中Open with live Server选项,运行页面:

在这里插入图片描述

② F12控制台,查看Service Worker是否注册成功:
在这里插入图片描述

③ 然后点击Application,选中左侧Service Workers,查看sw脚本是否正常运行:
在这里插入图片描述

④ 点击左侧Cache Storage,选中我们定义的cacheStorageKey-当前域名地址,查看初始资源(sw.js文件中定义的cacheList数组中的资源)是否被缓存:
在这里插入图片描述

⑤ 点击Network,选中All,刷新页面,查看请求资源情况:
在这里插入图片描述

⑥ 经过上次刷新,所有相关资源已被缓存,再次刷新页面,所有资源都将经过Service Worker之后,从缓存中获取:
在这里插入图片描述

⑦ 通过选中NetWork中的Offline选项切断网络,查看在无网络时,页面是否能利用缓存正常显示:
在这里插入图片描述

⑧ 点击右上角的下载按钮,安装PWA应用到设备上:

在这里插入图片描述

⑨ 以macos系统为例,PWA应用安装后,会自动打开,并桌面端入口安装到应用启动台中,查看打开后的页面:

在这里插入图片描述

在这里插入图片描述

⑩ F12控制台,重复②~⑦的操作,验证在桌面端打开时,Service Worker 能否正常运行。

⑪ 下载安装的PWA应用,需要通过右上角的功能区,进行卸载:

在这里插入图片描述

⑫ 如果我们本地修改了项目文件的内容,直接刷新或者关闭页面再打开,是看不到更新的内容的,因为都是直接从缓存中获取相关数据,而且关闭页面或浏览器后Service Worker脚本依旧在后台执行,再次打开访问时,依旧是从缓存中获取数据。想要页面显示最新的内容需要先注销掉旧的的脚本,然后让新脚本取代旧脚本即可,在浏览器开发工具的Application中我们可以直接注销Service Worker脚本,然后再刷新页面即可:
在这里插入图片描述

⑬ 其他操作。。。

四、相关资料

PWA谷歌文档

PWA的MDN文档

Service Worker

Service Worker 生命周期

Web App Manifes

参考博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/906062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

修改窗口类的属性将影响所有该类的窗口

正如标题所指出的&#xff1a;窗口类的属性将影响所有由该窗口类创建的窗口。这就是它为什么称之为窗口类的原因。 我用这么多话来强调这一点&#xff0c;似乎是一件显而易见的事情&#xff0c;但我看到许多”解决方案”忽视了这个简单的事实。 在 WNDCLASS&#xff08;或 WND…

Autoware.universe与Carla(二进制版)联调

文章目录 一、Carla的使用1.1 二进制Carla的安装1.2 Carla API的使用1.2.1 增加行人流和车流1.2.3 改变服务器的时间&#xff0c;光照&#xff0c;天气1.2.3 手动控制车辆1.1.4 车辆的自动驾驶 二、Universe与Carla联调2.1 设置OpenPlanner2.2 重新编译并运行 本文介绍了 Autow…

Nginx反向代理配置流式响应

Nginx 是通过缓存响应内容来处理请求的。也就是说&#xff0c;当 Nginx 接收到完整的响应后&#xff0c;才会将其发送给客户端&#xff0c;所以默认不支持流式响应&#xff0c;这里讲讲 Nginx 反向代理中怎么配置流式响应&#xff1f; 一、使用背景 最近使用 Egg.js 搭建自动化…

冠达管理:非银金融是什么?

非银金融&#xff08;Non-banking Financial Institutions&#xff0c;简称非银&#xff09;是指除了传统的银行以外的其他金融机构。与银行不同的是&#xff0c;非银金融机构没有颁发钱银的权利&#xff0c;但在金融市场中发挥着重要的效果。在全球范围内&#xff0c;非银金融…

每日刷题(翻转+二分+BFS)

食用指南&#xff1a;本文为作者刷题中认为有必要记录的题目 ♈️今日夜电波&#xff1a;凄美地—郭顶 1:10 ━━━━━━️&#x1f49f;──────── 4:10 &#x1f504; ◀️ ⏸ ▶️ ☰…

Android 标准语音识别框架:SpeechRecognizer 的封装和调用

前言 此前&#xff0c;笔者梳理了语音相关的两篇文章&#xff1a; 如何打造车载语音交互&#xff1a;Google Voice Interaction 给你答案&#xff1a;介绍的是 3rd Party App 如何通过 Voice Interaction API 快速调用系统的语音交互服务快速完成确认、选择的基础语音对话直面…

创建 github 项目,并自动化配置

一 新建项目 github 创建新项目&#xff0c;并自动化部署 二 github 到本地 三 自动化部署

苹果iOS17引入新功能:实时显示充电设施信息,续航焦虑不再

据外媒9to5mac报道&#xff0c;苹果公司计划在iOS 17中引入一项非常方便电动汽车车主的功能&#xff0c;即iPhone内置的地图应用将实时显示充电设施的可用性信息。在最新发布的iOS 17 Beta 1版本中&#xff0c;这一功能仍在开发阶段&#xff0c;尚缺少一些必要的数据。 据称&am…

【健康医疗】Axure用药提醒小程序原型图,健康管理用药助手原型模板

作品概况 页面数量&#xff1a;共 10 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;健康管理领域&#xff0c;用药助手 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本作品为「用药提醒」小程序原…

基于Java+SpringBoot+Vue的养老服务管理系统【源码+论文+演示视频+包运行成功】

博主介绍&#xff1a;✌csdn特邀作者、博客专家、java领域优质创作者、博客之星&#xff0c;擅长Java、微信小程序、Python、Android等技术&#xff0c;专注于Java技术领域和毕业项目实战战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏…

Linux面试笔试题(3)

54、统计磁盘空间或文件系统使用情况的命令是&#xff1a;(  A  ) A. df        B. dd   C. du        D. fdisk 在Linux系统中&#xff0c;df命令用于统计磁盘空间或文件系统使用情况。例如&#xff0c;要查看当前目录及其子目录下所有文件和文件夹…

linux 安装 kibana

首先下载 kibana https://www.elastic.co/cn/downloads/kibana 然后上传到linux /usr/local 目录下解压安装 修改config/kibana.yml 配置文件&#xff0c;将elasticsearch.hosts

攻防世界-supersqli

原题 解题思路 直接查找看不到明显的回显变化 先找回显变化数量 -1 order by 2 #如果是3列就报错&#xff0c;说明只有两列。接下来找数据库名称&#xff1a; -1 union select 1,databases # 结果是后端做了一些简单的过滤&#xff0c;需要更换查找语句。 -1; show …

python打包的exe文件运行时出现DLL load failed找不到DLL

一、问题&#xff0c;python开发的py代码文件通过"pyinstaller -c -F .\test.py"命令打包后在windows控制台(cmd.exe)运行报如下错误 D:\python\test>test.exe Traceback (most recent call last):File "test.py", line 7, in <module>File &qu…

轮胎侧偏刚度拟合估计

参考博客&#xff1a; carsim如何获得轮胎侧偏刚度_SSW.hani的博客-CSDN博客

面试算法变成题-必须掌握

一 多线程 1.1 死锁 1.1.1 死锁的案例 package com.ljf.day0814;/*** ClassName: DeadLock* Description: TODO* Author: admin* Date: 2023/08/21 09:31:16 * Version: V1.0**/ public class DeadLock {public static void main(String[] args) {Object objAnew Object(…

Delphi 安卓App自动升级

Androidapi.JNI.Support引用这个单元 procedure _InstallApk(Apk: string); varLFile: JFile;LIntent: JIntent; beginLFile : TJFile.JavaClass.init(StringToJString(ExtractFilePath(Apk)), StringToJstring(ExtractFileName(Apk)));LIntent : TJIntent.Create;LIntent.set…

【Vue】全家桶之Axios

文章目录 概述axios特点安装关于 ajax、fetch、axiosAxios APIAxios 实例请求配置响应结构默认配置拦截器错误处理取消请求取消请求请求体编码 概述 Axios是基于promise的网络请求库&#xff0c;可以在nodejs和浏览器中运行&#xff0c;是isomorphic的&#xff0c;意思就是同一…

fastgpt构建镜像

1.把client目录复制到服务器 .next和node_modules文件夹不用上传到服务器 在服务器目录运行 docker build -t fastgpt:1.0.3 . 构建服务 再运行 docker ps 就可以看到容器了

【从零学习python 】58.Python中的自定义异常及引发异常的方法

文章目录 自定义异常补充文件备份制作文件的备份 进阶案例 自定义异常 你可以用 raise 语句来引发一个异常。异常/错误对象必须有一个名字&#xff0c;且它们应是 Error 或 Exception 类的子类。 下面是一个引发异常的例子: class ShortInputException(Exception):自定义的异…