PWA(渐进式网页应用)方式实现TodoList桌面应用

news2024/11/20 10:31:43

参考:
https://cloud.tencent.com/developer/article/2322236

todlist网页参考:
https://blog.csdn.net/weixin_42357472/article/details/140657576

实现在线网页当成app应用:
一个 PWA 应用首先是一个网页, 是通过 Web 技术编写出的一个网页应用,随后通过App Shell 架构添加上 Manifest 实现添加至设备主屏幕, 在通过 Service Worker 来实现离线缓存和消息推送等功能。

创建项目

目录结构
在这里插入图片描述

manifest.json

{
  "name": "TodoList",
  "short_name": "TodoList",
  "description": "A simple todo list application",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#4CAF50",
  "icons": [
    {
      "src": "icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

service-worker.js
本地测试修改
在这里插入图片描述

const CACHE_NAME = 'todolist-cache-v1';
const urlsToCache = [
    './',
    'index.html',
    'styles.css',
    'script.js',
    'icon-192x192.png',
    'icon-512x512.png'
  ];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(urlsToCache))
  );
});

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

index.html
引入
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TodoList</title>
    <link rel="stylesheet" href="styles.css">
    <link rel="manifest" href="/manifest.json">
	<meta name="theme-color" content="#4CAF50">
</head>
<body>
    <h1>TodoList</h1>
    <form id="todo-form">
        <input type="text" id="todo-input" placeholder="Enter a new task" required>
        <button type="submit" id="add-button">Add</button>
    </form>
    <ul id="todo-list"></ul>

    <script src="script.js"></script>
</body>
</html>

styles.css

body {
    font-family: Arial, sans-serif;
    max-width: 500px;
    margin: 0 auto;
    padding: 20px;
}
h1 {
    text-align: center;
}
#todo-form {
    display: flex;
    margin-bottom: 20px;
}
#todo-input {
    flex-grow: 1;
    padding: 10px;
    font-size: 16px;
    border: 1px solid #ddd;
    border-radius: 4px 0 0 4px;
}
#add-button {
    padding: 10px 20px;
    font-size: 16px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 0 4px 4px 0;
    cursor: pointer;
}
#todo-list {
    list-style-type: none;
    padding: 0;
}
.todo-item {
    display: flex;
    align-items: center;
    padding: 10px;
    background-color: #f9f9f9;
    border: 1px solid #ddd;
    margin-bottom: 10px;
    border-radius: 4px;
}
.todo-item.completed {
    text-decoration: line-through;
    opacity: 0.6;
}
.todo-item input[type="checkbox"] {
    margin-right: 10px;
}
.delete-button {
    margin-left: auto;
    background-color: #f44336;
    color: white;
    border: none;
    padding: 5px 10px;
    border-radius: 4px;
    cursor: pointer;
}

script.js

在这里插入图片描述

const todoForm = document.getElementById('todo-form');
const todoInput = document.getElementById('todo-input');
const todoList = document.getElementById('todo-list');

function loadTodos() {
    const todos = JSON.parse(localStorage.getItem('todos')) || [];
    todos.forEach(todo => {
        addTodoToDOM(todo.text, todo.completed);
    });
}

function saveTodos() {
    const todos = Array.from(todoList.children).map(li => ({
        text: li.querySelector('span').textContent,
        completed: li.classList.contains('completed')
    }));
    localStorage.setItem('todos', JSON.stringify(todos));
}
function addTodoToDOM(text, completed = false) {
    const li = document.createElement('li');
    li.className = 'todo-item' + (completed ? ' completed' : '');
    li.innerHTML = `
        <input type="checkbox" ${completed ? 'checked' : ''}>
        <span>${text}</span>
        <button class="delete-button">Delete</button>
    `;

    li.querySelector('input[type="checkbox"]').addEventListener('change', function() {
        li.classList.toggle('completed');
        if (li.classList.contains('completed')) {
            todoList.appendChild(li);
        } else {
            todoList.insertBefore(li, todoList.firstChild);
        }
        saveTodos();
    });

    li.querySelector('.delete-button').addEventListener('click', function() {
        li.remove();
        saveTodos();
    });

    if (completed) {
        todoList.appendChild(li);
    } else {
        todoList.insertBefore(li, todoList.firstChild);
    }
}

todoForm.addEventListener('submit', function(e) {
    e.preventDefault();
    if (todoInput.value.trim() === '') return;

    addTodoToDOM(todoInput.value);
    saveTodos();
    todoInput.value = '';
});

loadTodos();

if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
      navigator.serviceWorker.register('/service-worker.js')
        .then(registration => {
          console.log('Service Worker registered successfully:', registration.scope);
        })
        .catch(error => {
          console.log('Service Worker registration failed:', error);
        });
    });
  }

部署测试

PWA要求必须通过HTTPS提供服务。这里用python直接起个http服务

cd pwa-todolist
python -m http.server 8000

在这里插入图片描述
打开网页:
http://localhost:8000/index.html
在这里插入图片描述
点击右上角安装按钮可以弹出安装
在这里插入图片描述
安装好后弹出应用窗口,同时桌面也生成个快捷方式:
在这里插入图片描述
在这里插入图片描述
右上角可以再次返回浏览器打开和卸载应用:
在这里插入图片描述

然后现在关闭python -m http.server 8000服务或关闭整个浏览器
再次打开浏览器输入http://localhost:8000/,页面依旧存在
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

如何全面提升架构设计的质量?

当我们从可扩展、高可用、高性能等角度设计出来架构的时候&#xff0c;我们如何优化架构呢&#xff1f;就需要从成本、安全、测试等角度进行优化。 如何设计更好的架构 - 步骤 成本 低成本复杂度本质 低成本手段和应用 低成本的主要应用场景 安全 安全性复杂度本质 架构安全…

大语言模型系列-Transformer:深入探索与未来展望

大家好&#xff0c;我是一名测试开发工程师&#xff0c;已经开源一套【自动化测试框架】和【测试管理平台】&#xff0c;欢迎大家联系我&#xff0c;一起【分享测试知识&#xff0c;交流测试技术】 Transformer模型自其问世以来&#xff0c;便迅速在自然语言处理领域崭露头角&a…

2024年【危险化学品生产单位安全生产管理人员】最新解析及危险化学品生产单位安全生产管理人员考试总结

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品生产单位安全生产管理人员最新解析参考答案及危险化学品生产单位安全生产管理人员考试试题解析是安全生产模拟考试一点通题库老师及危险化学品生产单位安全生产管理人员操作证已考过的学员汇总&#xff0c;…

mysql基本数据类型(整型)

一、 常见面试题 整型都有哪些基础类型&#xff0c;各占几个字节 tinyint, smallint, mediumint, int, bigint: 1 2 3 4 8 int(n) 是什么意思&#xff0c;什么时候用到 指定显示位宽&#xff0c;需配合 zerofill 使用&#xff08;不够位宽则在前面补0&#xff09;&#xff0c;…

Could not find a version that satisfies the requirement

Could not find a version that satisfies the requirement 目录 Could not find a version that satisfies the requirement 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;2…

MATLAB被360误杀的解决方案

前面被误杀&#xff0c;今天又被误杀。 前面误杀结果是缺少文件&#xff0c;重装MATLAB也不行。 结果重装了操作系统。 这次&#xff0c;看到了提示额外小心。 当时备份了“病毒”文件&#xff0c;结果备份的也被杀了。 解铃还须系铃人 在360安全卫士里面恢复&#xff0c;步骤…

线性代数|机器学习-P27用于深度学习的神经网络结构

文章目录 1. 概述2. 参数定义3. CNN 网络 1. 概述 – 1. 卷积神经网络 CNNs – 2. 连续型线性分段函数 F – 3. 损失函数 – 4. 链式法则计算反向传播算法梯度 ∇ F g r a d F \nabla F \mathrm{grad}\; F ∇FgradF 2. 参数定义 我们定义每个样本有m维度特征&#xff0c;有…

java找不到符号解决办法

一、java找不到符号 如果你的代码里没有报错&#xff0c;明明是存在的。但是java报错找不到符号。如下所示&#xff0c; 二、解决步骤 1.清除编码工具缓存 本人用的idea&#xff0c; eclipse清除缓存方式有需要的可以百度一下&#xff01; 2.如果是mavne项目的 先clean 再…

流媒体服务器一:使用成熟的流媒体SRS 搭建 RTMP流媒体服务器

1 安装和测试srs流媒体服务器 服务器&#xff1a;SRS(Simple RTMP Server&#xff0c;⽀持RTMP、HTTP-FLV&#xff0c;HLS) 推流端&#xff1a;ffmpeg OBS 拉流端&#xff1a;ffplay VLC srs播放器 1.1 安装srs流媒体服务器 官网 SRS (Simple Realtime Server) | SRS 码…

大模型算法面试题(十四)

本系列收纳各种大模型面试题及答案。 1、微调后的模型出现能力劣化&#xff0c;灾难性遗忘是怎么回事 微调后的模型出现能力劣化&#xff0c;灾难性遗忘&#xff08;Catastrophic Forgetting&#xff09;是一个在机器学习领域&#xff0c;尤其是在深度学习和大模型应用中频繁出…

【SpringBoot】6 全局异常捕获

介绍 在项目开发中&#xff0c;如果每个 Controller 都增加 try catch 方法去捕获异常及处理&#xff0c;就会导致代码变得很繁琐、效率低下&#xff0c;而大部分异常是不能直接向外抛出&#xff0c;需要有个统一的显示处理方法&#xff0c;因此需要加上全局异常捕获统一获取并…

深度学习中常用损失函数介绍

选择正确的损失函数对于训练机器学习模型非常重要。不同的损失函数适用于不同类型的问题。本文将总结一些常见的损失函数&#xff0c;并附有易于理解的解释、用法和示例 均方误差损失(MSE) loss_fn nn.MSELoss()py均方误差&#xff08;Mean Squared Error&#xff0c;简称 MSE…

Navidrome音乐服务器 + 音流APP = 释放你的手机空间

20240727 By wdhuag 目录 前言&#xff1a; 参考&#xff1a; Navidrome音乐服务器 Demo试用&#xff1a; 支持多平台&#xff1a; 下载&#xff1a; 修改配置&#xff1a; 设置用NSSM成服务启动&#xff1a; 服务器本地访问网址&#xff1a; 音流 歌词封面API&am…

Golang | Leetcode Golang题解之第292题Nim游戏

题目&#xff1a; 题解&#xff1a; func canWinNim(n int) bool {return n%4 ! 0 }

网站打包封装成app,提高用户体验和商业价值

网站打包封装成app的优势 随着移动互联网的普及&#xff0c;用户对移动应用的需求越来越高。网站打包封装成app可以满足用户的需求&#xff0c;提高用户体验和商业价值。 我的朋友是一名电商平台的运营负责人&#xff0c;他曾经告诉我&#xff0c;他们的网站流量主要来自移动…

vite + xlsx + xlsx-style 导出 Excel

如下 npm i 依赖 npm i xlsxnpm i xlsx-style-vite1、简单的使用&#xff1a;.vue文件中使用 const dataSource ref([]) // 数据源const columns [{title: 用户名,key: userName,width: 120,},{title: 用户组,key: userGroup,width: 120,},{title: 状态,key: enable,width: …

MySQL 视图与事务

文章目录 视图事务事物的四大特性&#xff08;ACID)事务的开启和结束事物隔离级别现象脏读不可重复度幻读 隔离级别读未提交&#xff08;READ UNCOMMITTED)读提交 &#xff08;READ COMMITTED)可重复读 (REPECTABLE READ)串行化 (SERIALIZABLE) 查看与设置事务隔离级别重复读的…

【前端 13】Vue快速入门

Vue快速入门 在现代Web开发中&#xff0c;尽管通过HTML、CSS和JavaScript我们能够构建出美观且功能丰富的页面&#xff0c;但随着项目规模的增大&#xff0c;这种传统的开发方式在效率上逐渐显得力不从心。为了提高开发效率&#xff0c;前端开发者们引入了多种框架和库&#x…

Python酷库之旅-第三方库Pandas(050)

目录 一、用法精讲 181、pandas.Series.var方法 181-1、语法 181-2、参数 181-3、功能 181-4、返回值 181-5、说明 181-6、用法 181-6-1、数据准备 181-6-2、代码示例 181-6-3、结果输出 182、pandas.Series.kurtosis方法 182-1、语法 182-2、参数 182-3、功能 …

异步通信方式的两种消息传输模型

文章目录 一、点对点模型1.1 什么是点对点模型1.2 点对点模型的特点 二、发布订阅模型2.1 什么是发布订阅模型2.2 发布订阅模式的日常案例2.3 发布订阅模型的特点 三、总结参考资料 一、点对点模型 1.1 什么是点对点模型 点对点模型&#xff08;也叫队列模型&#xff09; 1.2…