前端PWA:让你的网站变成App

前端PWA:让你的网站变成App

毒舌时刻

前端PWA?这不是噱头吗?

"PWA有什么用,用户直接用浏览器不就好了"——结果用户体验差,无法离线访问,
"我有原生App,不需要PWA"——结果开发成本高,维护困难,
"PWA就是加个manifest和service worker,多简单"——结果功能不完整,用户体验差。

醒醒吧,PWA不是简单的技术组合,而是一种现代化的Web应用模式!

为什么你需要这个?

  • 离线访问:即使没有网络也能访问应用
  • 安装到主屏幕:像原生App一样方便使用
  • 推送通知:及时向用户发送重要信息
  • 性能提升:缓存静态资源,加快加载速度
  • 跨平台:一次开发,多平台运行

反面教材

<!-- 反面教材:不完整的PWA配置 --> <!DOCTYPE html> <html> <head> <title>我的PWA应用</title> <!-- 缺少manifest.json --> <!-- 缺少service worker --> </head> <body> <h1>我的PWA应用</h1> <p>这是一个PWA应用</p> </body> </html> 
// 反面教材:简单的service worker // service-worker.js self.addEventListener('install', event => { console.log('Service Worker 安装'); }); self.addEventListener('activate', event => { console.log('Service Worker 激活'); }); self.addEventListener('fetch', event => { console.log('Service Worker 拦截请求'); }); 

正确的做法

<!-- 正确的做法:完整的PWA配置 --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>我的PWA应用</title> <!-- 添加manifest.json --> <link rel="manifest" href="/manifest.json"> <!-- 添加主题颜色 --> <meta name="theme-color" content="#317EFB"> <!-- 添加Apple Touch Icon --> <link rel="apple-touch-icon" href="/icons/icon-192x192.png"> <!-- 添加预加载 --> <link rel="preload" href="/style.css" as="style"> <link rel="preload" href="/script.js" as="script"> <link rel="stylesheet" href="/style.css"> </head> <body> <h1>我的PWA应用</h1> <p>这是一个完整的PWA应用</p> <button>发送通知</button> <script src="/script.js"></script> <script> // 注册service worker if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js') .then(registration => { console.log('Service Worker 注册成功:', registration.scope); }) .catch(error => { console.log('Service Worker 注册失败:', error); }); }); } </script> </body> </html> 
// 正确的做法:manifest.json { "name": "我的PWA应用", "short_name": "PWA应用", "description": "一个完整的PWA应用示例", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#317EFB", "icons": [ { "src": "/icons/icon-72x72.png", "sizes": "72x72", "type": "image/png", "purpose": "any" }, { "src": "/icons/icon-96x96.png", "sizes": "96x96", "type": "image/png", "purpose": "any" }, { "src": "/icons/icon-128x128.png", "sizes": "128x128", "type": "image/png", "purpose": "any" }, { "src": "/icons/icon-144x144.png", "sizes": "144x144", "type": "image/png", "purpose": "any" }, { "src": "/icons/icon-152x152.png", "sizes": "152x152", "type": "image/png", "purpose": "any" }, { "src": "/icons/icon-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { "src": "/icons/icon-384x384.png", "sizes": "384x384", "type": "image/png", "purpose": "any" }, { "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" } ] } 
// 正确的做法:完整的service worker // service-worker.js const CACHE_NAME = 'pwa-app-cache-v1'; const ASSETS_TO_CACHE = [ '/', '/index.html', '/style.css', '/script.js', '/manifest.json', '/icons/icon-72x72.png', '/icons/icon-96x96.png', '/icons/icon-128x128.png', '/icons/icon-144x144.png', '/icons/icon-152x152.png', '/icons/icon-192x192.png', '/icons/icon-384x384.png', '/icons/icon-512x512.png' ]; // 安装Service Worker self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => { console.log('Cache opened'); return cache.addAll(ASSETS_TO_CACHE); }) .then(() => self.skipWaiting()) ); }); // 激活Service Worker self.addEventListener('activate', event => { const cacheWhitelist = [CACHE_NAME]; event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) .then(() => self.clients.claim()) ); }); // 拦截网络请求 self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { // 如果缓存中有响应,直接返回 if (response) { return response; } // 否则发起网络请求 return fetch(event.request) .then(response => { // 如果响应有效,缓存一份 if (response && response.status === 200 && response.type === 'basic') { const responseToCache = response.clone(); caches.open(CACHE_NAME) .then(cache => { cache.put(event.request, responseToCache); }); } return response; }) .catch(error => { // 网络错误时返回离线页面 if (event.request.mode === 'navigate') { return caches.match('/offline.html'); } }); }) ); }); // 处理推送通知 self.addEventListener('push', event => { const data = event.data.json(); const options = { body: data.body, icon: '/icons/icon-192x192.png', badge: '/icons/icon-72x72.png', data: { url: data.url } }; event.waitUntil( self.registration.showNotification(data.title, options) ); }); // 处理通知点击 self.addEventListener('notificationclick', event => { event.notification.close(); event.waitUntil( clients.openWindow(event.notification.data.url) ); }); 
// 正确的做法:使用推送通知 // script.js // 请求通知权限 function requestNotificationPermission() { if ('Notification' in window) { Notification.requestPermission().then(permission => { if (permission === 'granted') { console.log('通知权限已授予'); } else { console.log('通知权限被拒绝'); } }); } } // 发送通知 function sendNotification() { if ('serviceWorker' in navigator && 'PushManager' in window) { navigator.serviceWorker.ready.then(registration => { registration.showNotification('测试通知', { body: '这是一条测试通知', icon: '/icons/icon-192x192.png', badge: '/icons/icon-72x72.png', data: { url: '/' } }); }); } } // 页面加载时请求通知权限 window.addEventListener('load', () => { requestNotificationPermission(); // 绑定按钮点击事件 const notificationBtn = document.getElementById('notification-btn'); if (notificationBtn) { notificationBtn.addEventListener('click', sendNotification); } }); // 检查是否是PWA模式 function checkPwaMode() { const isPwa = window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone || document.referrer.includes('android-app://'); if (isPwa) { console.log('应用以PWA模式运行'); } else { console.log('应用以浏览器模式运行'); } } checkPwaMode(); 

毒舌点评

看看,这才叫前端PWA!不是简单地添加manifest和service worker,而是构建一个完整的PWA应用,包括离线访问、推送通知、安装到主屏幕等功能。

记住,PWA不是替代原生App,而是一种补充。它可以让你的Web应用获得类似原生App的体验,同时保持Web的跨平台优势。

所以,别再觉得PWA是噱头了,它是现代Web开发的重要方向!

总结

  • manifest.json:配置应用名称、图标、显示模式等
  • Service Worker:实现离线缓存、推送通知等功能
  • 离线访问:缓存静态资源,确保无网络时也能访问
  • 安装到主屏幕:提供类似原生App的入口
  • 推送通知:及时向用户发送重要信息
  • 性能优化:缓存策略、预加载等
  • 可访问性:确保PWA对所有用户都友好
  • 跨平台:一次开发,多平台运行

PWA,让你的网站变成真正的应用!

Read more

10月实测:2025年最好用的12款ai写小说工具(含优劣对比)

10月实测:2025年最好用的12款ai写小说工具(含优劣对比)

最近,ai写小说的风刮得特别大,各种写小说软件层出不穷,很多人问我这些工具到底能不能用。 我花了不少时间,把市面上主流的12款工具都试用了一遍。今天就跟大家掏心窝子聊聊,这些工具哪些是真能帮上忙的“助手”,哪些又是纯粹浪费时间的“玩具”。 我会按照我自己的测评标准,从定位(适合谁)、核心功能(能干嘛)、使用场景(我怎么用),以及最重要的踩坑/建议(避坑指南)这几个方面来说,保证大家看完就能懂,不踩坑。 01、笔灵(一键生成整部小说的创作神器) 传送门:https://ibiling.cn/novel-editor?from=ZEEKLOGeditorgx711(建议复制链接到电脑浏览器打开体验更佳!) 定位: 面向小说作者的综合创作平台,偏重把设定变成实盘稿件并能保存续写。 核心功能: 它的 AI写全篇小说 能按模板一键拉出章节,支持站内保存和一键续写,另外有编辑大纲和素材板块。 使用场景: 我常在凌晨卡文时先让笔灵把整章拉出来当样稿,然后二次改;也会用它保存断更的稿子,续写挺稳。 踩坑/

Flutter 三方库 tiktoken 鸿蒙端侧 AI 重载计算环境适配指南:极尽压榨设备级 BPE 分词器吞吐量边界,打造工业级精控的大模型高昂运算成本阀门-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 tiktoken 鸿蒙端侧 AI 重载计算环境适配指南:极尽压榨设备级 BPE 分词器吞吐量边界,打造工业级精控的大模型高昂运算成本阀门-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 tiktoken 鸿蒙端侧 AI 重载计算环境适配指南:极尽压榨设备级 BPE 分词器吞吐量边界,打造工业级精控的大模型高昂运算成本阀门防线 在开发鸿蒙平台的生成式 AI 应用(如大模型助手、智能写作或 Rerank 逻辑)时,如何精确预估 Prompt 的消耗?如何实现窗口精度的截断?tiktoken 提供了一套完整的 OpenAI BPE(字节对编码)分词算法实现。本文将详解该库在 OpenHarmony 上的适配要点。 前言 什么是 tiktoken?它是 OpenAI 为其 GPT 系列模型推出的高性能 BPE 分词器。不同于常规的字符计数,Token 是模型处理文本的最小单位。在鸿蒙操作系统强调的“

斯坦福HAI官网完整版《2025 AI Index Report》全面解读

斯坦福HAI官网完整版《2025 AI Index Report》全面解读

一、这份报告真正想说什么 如果把整份《2025 AI Index Report》压缩成一句话,我会这样概括:AI 已经从“技术突破期”进入“系统扩散期”。它一边继续提升性能,一边迅速降本、普及、商业化、制度化;与此同时,风险事件、治理压力、数据约束、社会信任问题也同步上升。换句话说,2025年的AI不是“更神奇了”这么简单,而是开始变成一种会重塑产业结构、教育体系、监管逻辑和公众心理预期的基础能力。这个判断基本贯穿斯坦福官网总览页的 12 条结论与各章节摘要。(斯坦福人工智能研究所) 斯坦福自己对AI Index的定位也很明确:它不是某家公司的宣传册,也不是对未来的主观想象,而是一个收集、整理、浓缩并可视化 AI 数据趋势的观测框架,目的是为政策制定者、研究者、企业与公众提供更全面、客观的判断基础。也正因为如此,这份报告最重要的价值,

技术拆解:P2P组网如何一键远程AI

技术拆解:P2P组网如何一键远程AI

文章目录 * **远程访问AI服务的核心是什么?** * **从暴露服务到连接设备** * **核心组件与交互解析** * **安全架构深度剖析** * **一键安装脚本的技术实现** * **# Windows** * **#macOS** * **#Linux** * **与AI工作流的结合实践** 远程访问AI服务的核心是什么? 你自己在电脑或者服务器上装了AI服务,比如大语言模型、Stable Diffusion这些,但是有个头疼的事儿:外面的人或者你在别的地方,怎么既安全又方便地连上这些本地的服务?以前的办法要么得有公网IP,还得敲一堆命令行用SSH隧道,要么就是直接开端口映射,等于把服务直接晾在公网上,太不安全了。 今天咱们就好好说说一种靠P2P虚拟组网的办法,还拿个叫节点小宝的工具举例子,看看它怎么做到不用改啥东西,点一下就装好,还能建个加密的通道,实现那种“服务藏得好好的,想连就能直接连上”的安全远程访问方式。 从暴露服务到连接设备 核心思路转变在于:不再尝试将内网服务端口暴露到公网(一个危险的攻击面),而是将外部访问设