Vue.js 101 todo PWA 教程 Bonus - 离线渐进式 Web 应用程序 (PWA)

2025-06-07

Vue.js 101 todo PWA 教程

奖励 - 离线渐进式 Web 应用程序 (PWA)

本教程的目标是介绍 vue.js ❤。

该内容最初是为DAM Digital London的一个研讨会撰写的。

Vue.js 是一个用于构建用户界面(UI)的渐进式框架。

本教程假设您已经具备一些关于 HTML、CSS 和 JavaScript 的中级知识。如果您对这些领域没有任何了解,请查看freeCodeCamp,他们提供了丰富的资源来学习这些主题😉。

本教程将涵盖以下主题:

  • v-bind 指令
  • 类和样式绑定
  • 事件处理
  • 属性中的数据绑定
  • 列表渲染
  • 插值 - 胡子
  • 表单输入绑定 - 复选框
  • v-模型
  • 方法
  • 条件渲染
  • v-if
  • v-否则
  • PWA 简介

我们将创建一个能够拥有多个待办事项列表的待办事项应用程序。

待办事项应用程序截图

入门

从 github克隆入门材料。它包含基础 HTML、CSS 和 JS。

您可以在github上找到本教程的最终代码,如果您点击此链接,还可以找到演示

Vue CDN 已经包含在我们的 index.html 以及 CSS 和 content/js/app.js 中😃。

在移动设备上切换导航

首先,移动设备(📱 < 850px)上的侧边导航应该显示和隐藏。

切换导航

当我们点击菜单时,我们想要<nav>切换类别.is-open

在 Vue.js 中,v-bind允许我们在 HTML 属性内绑定数据。例如v-bind:id=""v-bind:style=""v-bind:data-target=""等。 的简写v-bind:

在 中index.html,我们将动态传递.is-open使用v-bind:class。如果isNavOpen为真,则我们将添加我们的类。

<nav v-bind:class="{'is-open': isNavOpen}">
<!-- ... -->
</nav>
Enter fullscreen mode Exit fullscreen mode

在 中content/js/app.js,我们需要isNavOpen数据。如果将最后一个值更改为true,导航就会显示出来。

vue.js 中的data属性用于存储应用程序的数据以及 UI 的状态。例如,默认isNavOpen设置为 false,但通过将其值更改为 true,我们可以将is-open类绑定到 DOM。

在我们的 app.js 中,我们需要添加isNavOpen: false

var app = new Vue({
  el: "#app",
  data: {
    isNavOpen: false
  }
});
Enter fullscreen mode Exit fullscreen mode

isNavOpen现在我们想要改变单击菜单按钮时的值

我们将使用“on click”事件处理程序。在 Vue.js 中,我们可以使用v-on:@(简写)来监听 DOM 事件。在本例中,我们想要监听点击事件。因此,我们将使用v-on:click/ @click

<button v-on:click="isNavOpen = !isNavOpen" class="menu">Menu</button>
Enter fullscreen mode Exit fullscreen mode

如您所见,我们可以传递内联 javascript 语句,我们也可以使用方法(函数),我们将在本教程的后面看到如何使用最后一个方法。

文档参考

将待办事项列表绑定到侧边导航

在中content/js/app.js,让我们添加一些虚拟列表,以便我们可以开始整合我们的侧面导航。

var app = new Vue({
  el: "#app",
  data: {
    isNavOpen: false,
    todoLists: [
      {
        title: "✈️ Trip to japan",
        keyword: "japan",
        items: [
          { name: "Eat ramen", isCompleted: true },
          { name: "Visit mt Fuji", isCompleted: false },
          { name: "Learn japanese", isCompleted: false }
        ]
      },
      {
        title: "🏂 Ski trip to the Alps",
        keyword: "Alps",
        items: [
          { name: "Find a chalet", isCompleted: true },
          { name: "Learn how to ski", isCompleted: false }
        ]
      },
      {
        title: "🍉 Groceries",
        keyword: "Food",
        items: [
          { name: "Apples", isCompleted: false },
          { name: "Banana", isCompleted: true },
          { name: "Tomatoes", isCompleted: false },
          { name: "Bread", isCompleted: true }
        ]
      }
    ]
  }
});
Enter fullscreen mode Exit fullscreen mode

为了在侧面导航中呈现我们的列表,我们需要使用v-for指令。

<nav v-bind:class="{'is-open': isNavOpen}">
  <ul>
    <li v-for="todoList in todoLists">
      <button>
        {{todoList.title}}
        <span>
          {{todoList.items.length}}
        </span>
      </button>
    </li>
    <li>
      <button class="is-add">Create a new list</button>
    </li>
  </ul>
</nav>
Enter fullscreen mode Exit fullscreen mode

todoLists是源数据,todoList是用于在数组中迭代的别名。

我们使用“moustache”语法{{}}将文本绑定到视图。moustache 标签将被 中的目标值替换todoLists

文档参考

主要部分

标题

我们希望能够在主页面中看到待办事项。目前,我们只渲染第一个todoLists列表(索引 0)。

content/js/app.js=>data添加currentListIndex: 0

var app = new Vue({
  el: "#app",
  data: {
    isNavOpen: false,
    currentListIndex: 0,
    todoLists: [
      //...
    ]
  }
});
Enter fullscreen mode Exit fullscreen mode

使用标题中的胡须语法绑定列表的标题。

<h1>{{todoLists[currentListIndex].title}}</h1>
Enter fullscreen mode Exit fullscreen mode

标题有一张背景图片。我们使用Unsplash Source获取随机图片。我们可以指定关键词来获取与标题相关的图片。

https://source.unsplash.com/featured/?{KEYWORD},{KEYWORD}
Enter fullscreen mode Exit fullscreen mode

当我们将关键字绑定到属性中时,我们使用v-bind

<header v-bind:style="'background-image: url(https://source.unsplash.com/featured/?' + todoLists[currentListIndex].keyword + ')'">
  <!-- ... -->
</header>
Enter fullscreen mode Exit fullscreen mode

待办事项

要在主部分中呈现待办事项,我们需要使用v-for。由于我们希望每个输入都有单独的 ID 和名称,因此我们在 for 循环中传递了索引v-for="(value, index) in object"

如果这些已经被检查过,我们就会用它v-bind检查/勾选我们的待办事项输入。

当我们点击复选框时,我们使用v-model来更新 todos 的值。当复选框被选中时, isCompleted 将获得 true 的值,并且父级将自动获得isCompleted 的类名isCompletedli.is-completedtrue

v-model指令创建双向数据绑定,这意味着当值更新时,UI 也会更新。

<ul>
  <li v-for="(todo, index) in todoLists[currentListIndex].items" v-bind:class="{'is-completed': todo.isCompleted}">
    <label v-bind:for="'todo' + index">
      <input
      type="checkbox"
      v-bind:name="'todo' + index"
      v-bind:id="'todo' + index"
      v-bind:checked="todo.isCompleted"
      v-model="todo.isCompleted">
      {{todo.name}}
    </label>
    <button class="is-danger">Edit todo</button>
  </li>
  <li>
    <button class="is-add">New Todo</button>
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

文档参考

更改当前列表

我们希望能够更改当前显示的列表。这个功能由currentListIndex我们应用的数据设置。当我们点击某个列表项时,我们希望切换currentListIndex到该列表项的索引,并关闭侧边导航(如果打开)。

我们还需要向用户显示当前正在显示的列表,为此,我们添加了类.is-activeif currentListIndex === index

<li v-for="(todoList, index) in todoLists"  v-bind:class="{'is-active' : currentListIndex === index}">
    <button v-on:click="currentListIndex = index; isNavOpen = false">
        {{todoList.title}}
        <span>
            {{todoList.items.length}}
        </span>
    </button>
</li>
Enter fullscreen mode Exit fullscreen mode

创建新列表

切换侧边栏

点击“创建新列表”时,我们会显示.sidebar。为此,我们需要将 类添加.is-open到此列表,然后如果此列表在移动设备上打开,则关闭导航栏。此操作方法与我们在移动设备上对导航栏的操作非常相似。

在我们的数据中,我们首先添加一个新条目isSidebarOpen: false

var app = new Vue({
  el: "#app",
  data: {
    isNavOpen: false,
    isSidebarOpen: false,
    currentListIndex: 0
    //...
  }
});
Enter fullscreen mode Exit fullscreen mode

现在让我们将我们的类绑定.is-open到我们的.sidebar

<div class="sidebar" v-bind:class="{'is-open' : isSidebarOpen}">
 <!-- ... -->
</div>
Enter fullscreen mode Exit fullscreen mode

我们需要在单击“创建新列表”时添加一个事件处理程序,该事件处理程序将打开侧边栏并关闭移动设备上的导航:

<button class="is-add" v-on:click="isSidebarOpen = true; isNavOpen = false;">Create a new list</button>
Enter fullscreen mode Exit fullscreen mode

很好,现在我们可以打开侧边栏🎉。

现在,当我们单击“取消”时,让我们关闭侧边栏:

<button type="button" class="is-danger" v-on:click="isSidebarOpen = false">Cancel</button>
Enter fullscreen mode Exit fullscreen mode

添加新列表

要创建新列表,我们需要输入标题关键字的值。当用户点击“创建列表”时,我们会将新值推送到todoLists数据中。如果其中一个输入为空,则会显示默认值。

在我们的app.js中,添加一个tempNewList数组,它将存储我们的输入值。

var app = new Vue({
  el: "#app",
  data: {
    isNavOpen: false,
    isSidebarOpen: false,
    currentListIndex: 0,
    tempNewList: [
      {
        title: null,
        keyword: null
      }
    ]
    //...
  }
});
Enter fullscreen mode Exit fullscreen mode

现在我们将使用绑定我们的输入v-model

<form>
  <h3>Create a new list</h3>
  <label for="listTitle">Title:</label>
  <input id="listTitle" name="listTitle" type="text" placeholder="My amazing next trip to south america" v-model="tempNewList.title">
  <label for="listKeyword">Keyword:</label>
  <input id="listKeyword" name="listKeyword" type="text" placeholder="Colombia" v-model="tempNewList.keyword">
  <div class="buttons">
      <button type="button" class="is-danger" v-on:click="isSidebarOpen = false">Cancel</button>
      <button type="button" class="is-confirm">Create List</button>
  </div>
</form>
Enter fullscreen mode Exit fullscreen mode

好的,现在让我们将新tempNewList值推送到todoLists

创建一个名为 的方法addNewList方法是存储为对象属性的函数。这里的对象是 Vue 实例。在 Vue 中,我们的方法将存储在一个methods对象中。

addNewList方法将遵循以下场景:

  1. 如果标题为空,则使用默认字符串"🕵️‍ List with no name"
  2. 如果关键字为空,则使用默认字符串"earth"
  3. 推动我们的价值观todoLists
  4. 将当前列表更改为新列表
  5. 关闭侧边栏
  6. 重置输入的值
var app = new Vue({
  el: "#app",
  data: {
    //...
  },
  methods: {
    addNewList: function() {
      var listTitle = this.tempNewList.title;
      var listKeyword = this.tempNewList.keyword;
      if (listTitle == null) {
        listTitle = "🕵️‍ List with no name";
      }
      if (listKeyword == null) {
        listKeyword = "earth";
      }
      this.todoLists.push({
        title: listTitle,
        keyword: listKeyword,
        items: []
      });
      this.currentListIndex = this.todoLists.length - 1;
      this.isSidebarOpen = false;
      this.tempNewList.title = null;
      this.tempNewList.keyword = null;
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

最后,我们将把我们的方法绑定到我们的创建列表按钮。

<button type="button" class="is-confirm" v-on:click="addNewList">Create List</button>
Enter fullscreen mode Exit fullscreen mode

文档参考

编辑列表

好的,现在我们可以创建新列表了,我们希望能够编辑现有列表。我们可以编辑标题、关键字,还可以删除列表。

切换侧边栏内容

创建一个新方法openSidebar。该方法将:

  1. 打开侧边栏
  2. 显示我们想要使用的表单
  3. 如果此导航处于打开状态,请关闭它

在数据中,让我们添加sidebarContentToShow: null,这将使我们知道应该显示什么形式。

var app = new Vue({
  el: "#app",
  data: {
    isNavOpen: false,
    isSidebarOpen: false,
    sidebarContentToShow: null,
    currentListIndex: 0
    //...
  },
  methods: {
    //...
  }
});
Enter fullscreen mode Exit fullscreen mode

我们的侧边栏中有 4 种表单可供切换:

  1. "createNewList"
  2. "editList"
  3. "createNewTodo"
  4. "editTodo"

在 HTML 中,我们将根据 的值有条件地渲染表单sidebarContentToShow。为此,我们使用了v-if指令。它允许我们在条件成立时渲染代码块。我们需要取消注释表单,并添加一个v-if指令。

<div class="sidebar" v-bind:class="{'is-open' : isSidebarOpen}">
  <div class="sidebar-content">
      <form v-if="sidebarContentToShow === 'createNewList'">
          <h3>Create a new list</h3>
          <!-- ... -->
      </form>
      <form v-if="sidebarContentToShow === 'editList'">
        <h3>Edit list</h3>
          <!-- ... -->
      </form>
      <form v-if="sidebarContentToShow === 'createNewTodo'">
        <h3>Create a new todo</h3>
          <!-- ... -->
      </form>
      <form v-if="sidebarContentToShow === 'editTodo'">
        <h3>Edit todo</h3>
          <!-- ... -->
      </form>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

现在,当我们点击“创建新列表”时,侧边栏出现了,但我们看不到……什么都没有😱。记得,那个正常的值sidebarContentToShow被设置为了 null 😉。

为了改变的值,sidebarContentToShow我们将创建一种openSidebar方法来打开侧边栏并改变我们想要显示的形式。

var app = new Vue({
  el: "#app",
  data: {
    //...
  },
  methods: {
    openSidebar: function(contentToShow) {
      this.isSidebarOpen = true;
      this.isNavOpen = false;
      this.sidebarContentToShow = contentToShow;
    }
    //...
  }
});
Enter fullscreen mode Exit fullscreen mode

现在我们可以更改创建一个新列表,以便我们可以使用openSidebar

<button class="is-add" v-on:click="openSidebar('createNewList')">Create a new list</button>
Enter fullscreen mode Exit fullscreen mode

好了,我们现在渲染“创建新列表”表单。你可能已经猜到了,我们将在“编辑列表”按钮中重用我们的方法。

<button class="is-primary" v-on:click="openSidebar('editList')">Edit list</button>
Enter fullscreen mode Exit fullscreen mode

编辑列表表单

删除列表

我们从删除列表按钮开始。创建一个名为 的新方法deleteList。它将删除当前显示的列表并显示第一个列表。

//...
deleteList: function() {
  this.todoLists.splice(this.currentListIndex, 1);
  this.currentListIndex = 0;
  this.isSidebarOpen = false;
}
//...
Enter fullscreen mode Exit fullscreen mode
<button type="button" class="is-danger" v-on:click="deleteList">Delete list</button>
Enter fullscreen mode Exit fullscreen mode

现在我们可以删除列表,但如果我们尝试删除所有列表,我们会收到错误并且我们的应用程序停止工作。

[Vue warn]: Error in render: "TypeError: todoLists[currentListIndex] is undefined"
Enter fullscreen mode Exit fullscreen mode

你可能已经猜到了,出现这个错误是因为我们的todoLists为空,而我们仍然尝试渲染依赖于 值的应用程序部分todoLists。我们将使用条件渲染v-ifv-else,为了解决这个问题,我们将仅在 的情况下渲染主要内容todoLists.length > 0。此外,我们希望用户能够创建新列表,我们将使用v-else来显示一个可帮助用户创建新列表的替代主要内容。

<main v-if="todoLists.length > 0">
  <!-- ... -->
</main>
<main v-else>
  <header style="background-image: url(https://source.unsplash.com/featured/?cat">
      <div class="header-content">
          <h1>Please create a new list</h1>
          <button class="is-add" v-on:click="openSidebar('createNewList')">Create a new list</button>
      </div>
  </header>
</main>
Enter fullscreen mode Exit fullscreen mode

更改标题和关键字值

让我们回到editList表单。我们想要:

  • todoLists使用将我们的输入与正确的元素绑定v-model
  • 当我们点击完成时,我们想要关闭滑块。
  • 仅在以下情况下呈现此表单todoLists.length > 0
<form v-if="sidebarContentToShow === 'editList' && todoLists.length > 0">
    <h3>Edit list</h3>
    <label for="listTitle">Title:</label>
    <input id="listTitle" name="listTitle" type="text" placeholder="My amazing next trip to south america" v-model="todoLists[currentListIndex].title">
    <label for="listKeyword">Keyword:</label>
    <input id="listKeyword" name="listKeyword" type="text" placeholder="Colombia" v-model="todoLists[currentListIndex].keyword">
    <div class="buttons">
        <button type="button" class="is-danger" v-on:click="deleteList">Delete list</button>
        <button type="button" class="is-confirm" v-on:click="isSidebarOpen = false">Done</button>
    </div>
</form>
Enter fullscreen mode Exit fullscreen mode

文档参考

创建和编辑待办事项

我们的应用程序的 UI 几乎完成了,我们还需要:

  • 在列表中创建新的待办事项
  • 编辑和删除现有的待办事项

听起来和我们对列表的操作很像吧?步骤几乎一样。

创建待办事项

在我们的数据中创建一个新的元素tempNewList

tempNewTodo: [
  {
    name: null,
    isCompleted: false
  }
],
Enter fullscreen mode Exit fullscreen mode

我们需要一种新方法,以便我们可以将新的待办事项添加到列表中todoLists

addNewTodo: function() {
  var todoName= this.tempNewTodo.name;
  var todoCompleted = this.tempNewTodo.isCompleted;
  if (todoName == null) {
    todoName = "🕵️‍ unnamed todo";
  }
  this.todoLists[this.currentListIndex].items.push({
    name: todoName,
    isCompleted: todoCompleted
  });
  this.isSidebarOpen = false;
  this.tempNewTodo.name = null;
  this.tempNewTodo.isCompleted = false;
}
Enter fullscreen mode Exit fullscreen mode

现在让我们深入了解 HTML。

我们需要使用createNewTodo表单打开侧边栏

<button class="is-add" v-on:click="openSidebar('createNewTodo')">New Todo</button>
Enter fullscreen mode Exit fullscreen mode

正如我们之前所做的那样,我们将使用绑定我们的输入v-model并使用该addNewTodo方法来推送我们的新值。

<form v-if="sidebarContentToShow === 'createNewTodo'">
    <h3>Create a new todo</h3>
    <label for="todoName">Name:</label>
    <input id="todoName" name="todoName" type="text" placeholder="Do things..." v-model="tempNewTodo.name">
    <label for="todoCompleted"><input name="todoCompleted" id="todoCompleted" type="checkbox" v-bind:checked="tempNewTodo.isCompleted" v-model="tempNewTodo.isCompleted"> Is completed</label>
    <div class="buttons">
        <button type="button" class="is-danger" v-on:click="isSidebarOpen = false">Cancel</button>
        <button type="button" class="is-confirm" v-on:click="addNewTodo">Create todo</button>
    </div>
</form>
Enter fullscreen mode Exit fullscreen mode

由于我们现在正在绑定待办事项中的数据isCompleted,因此我们将在导航中显示已完成待办事项的数量。

在我们的app.js中,创建一个totalTodosCompleted传递当前 todoList 索引的方法。

totalTodosCompleted: function(i){
  var total = 0;
  for (var j = 0; j < this.todoLists[i].items.length; j++) {
    if(this.todoLists[i].items[j].isCompleted){
      total++;
    }
  }
  return total;
}
Enter fullscreen mode Exit fullscreen mode

现在navigation,我们将使用新方法返回已完成待办事项的总数。

<li v-for="(todoList, index) in todoLists" v-bind:class="{'is-active' : currentListIndex === index}">
    <button v-on:click="currentListIndex = index; isNavOpen = false">
        {{todoList.title}}
        <span>
            {{totalTodosCompleted(index)}} / {{todoList.items.length}}
        </span>
    </button>
</li>
Enter fullscreen mode Exit fullscreen mode

编辑待办事项

要编辑待办事项,首先,我们需要知道将要编辑的待办事项的索引,在我们的数据中创建currentTodoIndex

currentTodoIndex: 0,
Enter fullscreen mode Exit fullscreen mode

我们需要一种deleteTodo方法来删除当前的待办事项。

deleteTodo: function() {
  this.todoLists[this.currentListIndex].items.splice(this.currentTodoIndex, 1);
  this.isSidebarOpen = false;
  this.currentTodoIndex = 0;
}
Enter fullscreen mode Exit fullscreen mode

现在让我们看一下 HTML。

首先,我们要打开滑块并改变的值currentTodoIndex

<button class="is-primary" v-on:click="openSidebar('editTodo'); currentTodoIndex = index">Edit todo</button>
Enter fullscreen mode Exit fullscreen mode

在我们的editTodo表单中,我们将:

  • 仅在以下情况下显示我们的表格todoLists[currentListIndex].items.length > 0
  • 绑定待办事项名称,如果完成,使用v-model
  • 当我们点击删除待办事项时,触发该方法deleteTodo
  • 当我们点击完成时,关闭侧边栏
<form v-if="sidebarContentToShow === 'editTodo' && todoLists[currentListIndex].items.length > 0">
  <h3>Edit todo</h3>
  <label for="todoName">Todo:</label>
  <input id="todoName" name="todoName" type="text" placeholder="Do things..." v-model="todoLists[currentListIndex].items[currentTodoIndex].name">
  <label for="todoCompleted"><input name="todoCompleted" id="todoCompleted" type="checkbox" v-bind:checked="todoLists[currentListIndex].items[currentTodoIndex].isCompleted" v-model="todoLists[currentListIndex].items[currentTodoIndex].isCompleted"> Is completed</label>
  <div class="buttons">
      <button type="button" class="is-danger" v-on:click="deleteTodo">Delete todo</button>
      <button type="button" class="is-confirm" v-on:click="isSidebarOpen = false">Done</button>
  </div>
</form>
Enter fullscreen mode Exit fullscreen mode

🎉🎉🎉🎉🎉 我们的待办事项的 UI 现已完成!

本地存储

当我们重新加载页面时,它会恢复到我们的虚拟值。如果我们能将列表和待办事项存储在本地,那该有多好啊?

我们将使用window.localStorage 。它是Web Storage API的一部分

localStorage允许我们存储没有到期日期的数据。

在我们的app.js中,创建一个新方法updateTodoLocalStorage

//...
updateTodoLocalStorage: function () {
  localStorage.setItem('todoLocalStorage', JSON.stringify(this.todoLists));
}
//...
Enter fullscreen mode Exit fullscreen mode

我们使用setItem()Web Storage API 中的方法。我们传递以下参数:

  • setItem(keyName, keyValue);
    • keyName:我们要创建/更新的键的名称('todoLocalStorage')。
    • keyValue:我们想要赋予您正在创建/更新的密钥的值(JSON.stringify(this.todoLists))。

我们现在希望每次更新待办事项或列表的值时都使用此方法。Vue 允许我们使用选项来响应数据变化watch。每当我们的值发生变化时todoLists,我们都会调用我们的updateTodoLocalStorage方法。由于我们的对象具有嵌套值,我们希望检测这些值内部的变化。我们可以通过传递deep: true来实现这一点。

var app = new Vue({
  el: "#app",
  data: {
    //...
  },
  watch: {
    todoLists: {
      handler() {
        this.updateTodoLocalStorage();
      },
      deep: true
    }
  },
  methods: {
    //...
    updateTodoLocalStorage: function() {
      localStorage.setItem("todoLocalStorage", JSON.stringify(this.todoLists));
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

现在让我们检查一下我们的应用,并查看一下本地存储。如果我们创建/更新一个列表或待办事项,我们可以看到todoLocalStorage存储正在更新。

现在,当我们加载页面时,我们需要将 our 设置todoLists为 our todoLocalStorage。Vue 自带了生命周期钩子。我们将使用它created: function()来设置值。此外,我们还要删除虚拟值。

var app = new Vue({
  el: "#app",
  data: {
    //...
    todoLists: []
  },
  created: function() {
    this.todoLists = JSON.parse(
      localStorage.getItem("todoLocalStorage") || "[]"
    );
  },
  watch: {
    //...
  },
  methods: {
    //...
  }
});
Enter fullscreen mode Exit fullscreen mode

现在,如果我们重新加载、关闭并重新打开我们的应用程序,我们所有的待办事项和列表都已保存。

文档参考

奖励 - 离线渐进式 Web 应用程序 (PWA)

在本教程的奖励部分中,我们将设置一个渐进式 Web 应用程序 (PWA) 和服务工作者,以便我们可以在智能手机上离线使用此 Web 应用程序。

设置 PWA

PWA 是:

渐进式 Web 应用是指 Web 应用,其内容类似于常规网页或网站,但用户在浏览时可以像传统应用或原生移动应用一样。这类应用试图将大多数现代浏览器提供的功能与移动体验的优势相结合。维基百科

它本质上是一个利用最新技术运行的网络应用程序,感觉就像一个原生应用程序。

要设置我们的 PWA,我们需要创建一个manifest.json文件并设置我们的服务人员。

PWA 必须从安全来源(HTTPS)提供服务。

生成图标资产

首先,将所有图标资源添加到我们的项目中。这些图标已通过https://realfavicongenerator.net/生成。它们包含在 中content/img/

在我们的 HTML 头部,我们要包含:

<link rel="apple-touch-icon" sizes="180x180" href="content/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="content/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="content/img/favicon-16x16.png">
<link rel="mask-icon" href="content/img/safari-pinned-tab.svg" color="#5bbad5">
<link rel="shortcut icon" href="content/img/favicon.ico">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-config" content="content/img/browserconfig.xml">
<meta name="theme-color" content="#77c4d3">
Enter fullscreen mode Exit fullscreen mode

Web 应用清单

Web 应用清单 (manifest.json) 是一个提供 Web 应用相关信息的文件,例如图标、应用程序名称等。它是 PWA 技术的一部分。您可以在MDN Web 文档中获取更多关于 Web 应用清单的信息。

在我们的项目根目录下创建此文件。

{
  "name": "todo",
  "short_name": "todo",
  "author": "Vincent Humeau",
  "lang": "en-GB",
  "icons": [
    {
      "src": "content/img/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "content/img/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#77c4d3",
  "background_color": "#77c4d3",
  "display": "standalone",
  "orientation": "portrait",
  "scope": "/todo/",
  "start_url": "/todo/"
}
Enter fullscreen mode Exit fullscreen mode

在我们的 HTML 文件中,我们想要包含它。

<link rel="manifest" href="manifest.json">
Enter fullscreen mode Exit fullscreen mode

服务人员

什么是服务人员?

Service Worker 是一项新的浏览器功能,它提供独立于网页运行的事件驱动脚本。与其他 Worker 不同,Service Worker 可以在事件结束时关闭,请注意,它不会保留文档的引用,并且可以访问全域事件,例如网络抓取。Service Worker 还具有可脚本化的缓存。除了能够通过脚本响应来自特定网页的网络请求之外,它还为应用程序提供了一种“离线”的方式。w3c /ServiceWorker - Github

本教程并不旨在深入介绍服务工作者,您可以在网上找到一些很棒的教程和资源:

对于我们的服务人员,我们使用来自 Omranic 的这个要点

sw.js在我们的项目根目录创建一个文件。

在我们的index.html中:

<script>
    if ('serviceWorker' in navigator) {
        window.addEventListener('load', () => {
            navigator.serviceWorker.register('sw.js');
        });
    }
</script>
Enter fullscreen mode Exit fullscreen mode

然后,在我们的sw.js中,我们将缓存所有允许我们的应用程序离线使用的资产:

var shellCacheName = "pwa-todo-v1";
var filesToCache = [
  "./",
  "./index.html",
  "./content/css/screen.min.css",
  "./content/js/app.js",
  "https://cdn.jsdelivr.net/npm/vue",
  "https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css"
];
Enter fullscreen mode Exit fullscreen mode

现在,我们只需要遵循这个要点并添加部分“监听安装事件”“更新资产缓存”“从缓存中离线提供应用程序外壳”


本教程终于完成了。我们的待办事项 vue.js PWA现在可以在这里访问了:https://vinceumo.github.io/todo


文章来源:https://dev.to/vinceumo/vuejs-101-todo-pwa-tutorial-4bne
PREV
YouTube 实时订阅者数量
NEXT
CSS 3D - 沿 z 轴滚动