Цель этого руководства - познакомить вас с vue.js ❤.

Этот контент изначально был написан для воркшопа для DAMDigital London.

Vue.js - это прогрессивный фреймворк для создания пользовательских интерфейсов (UI).

В этом руководстве предполагается, что у вас уже есть некоторые промежуточные знания о HTML, CSS и Javascript. Если у вас нет никаких знаний в этих областях, посетите freeCodeCamp, у них есть отличные ресурсы для изучения этих тем 😉.

Вот темы, которые будут рассмотрены в этом руководстве:

  • директива v-bind
  • Привязки классов и стилей
  • Обработка событий
  • Связывание данных в атрибутах
  • Отображение списка
  • Интерполяции - Усы
  • Привязки ввода формы - флажок
  • v-модель
  • Методы
  • Условный рендеринг
  • v-if
  • v-еще
  • Введение в PWA

Мы собираемся создать приложение для задач с возможностью иметь несколько списков задач.

Начиная

Клонируйте стартовые материалы с гитхаба. Он включает в себя базовый 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>

В content/js/app.js нам нужно isNavOpen в наших данных. Если вы измените значение последнего на true, появится навигационная система.

Свойство data в vue.js - это место, где мы храним данные нашего приложения, а также состояние нашего пользовательского интерфейса. Например, для isNavOpen по умолчанию установлено значение false, но, изменив его значение на true, мы можем привязать класс is-open к DOM.

В нашем app.js нам нужно добавить isNavOpen: false.

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

Теперь мы хотим изменить значение isNavOpen, когда мы нажимаем кнопку Меню.

Мы собираемся использовать обработчик событий «по клику». В vue.js мы можем использовать v-on: или @ (сокращенно) для прослушивания событий DOM. В нашем случае мы хотим прослушивать событие щелчка. Затем мы будем использовать _22 _ / _ 23_.

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

Как видите, мы можем передать встроенный оператор 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 }
        ]
      }
    ]
  }
});

Чтобы отображать наши списки в боковой навигации, нам нужно использовать директиву 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>

todoLists - исходные данные, а todoList - псевдоним, используемый для итерации в нашем массиве.

Мы используем синтаксис «усы» {{}} для привязки нашего текста к представлению. Тег усов заменяется целевым значением в todoLists.

Ссылки на документацию

Основной раздел

Заголовок

Мы хотим видеть наши задачи в основном разделе. Пока мы будем отображать только первый список todoLists (индекс 0).

В content/js/app.js = ›data добавьте currentListIndex: 0.

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

Свяжите заголовок списка, используя синтаксис усов в заголовке.

<h1>{{todoLists[currentListIndex].title}}</h1>

В заголовке есть фоновое изображение. Мы используем Unsplash Source для получения случайного изображения. Мы можем указать ключевое слово, чтобы получить релевантное изображение для нашего заголовка.

https://source.unsplash.com/featured/?{KEYWORD},{KEYWORD}

Когда мы связываем наше ключевое слово внутри атрибута, мы используем v-bind:

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

Todos

Чтобы отобразить наши задачи в основном разделе, нам нужно будет использовать v-for. Поскольку нам нужен индивидуальный идентификатор и имена для каждого ввода, мы передаем индекс в наш цикл for v-for="(value, index) in object".

Мы используем v-bind, чтобы проверять / отмечать входные данные наших задач, если они уже отмечены.

Мы используем v-model для обновления значения isCompleted из наших задач, когда мы устанавливаем флажок. Когда наш флажок установлен, isCompleted получит значение true, а родительский li автоматически получит класс .is-completed, поскольку isCompleted равен true.

Директива v-model создает двустороннюю привязку данных, то есть при обновлении значения обновляется и пользовательский интерфейс.

<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>

Ссылки на документацию

Изменить текущий список

Мы хотим иметь возможность изменять текущий отображаемый список. Этот параметр установлен currentListIndex в данных нашего приложения. Когда мы щелкаем по одному из элементов списка, мы хотим изменить currentListIndex на индекс этого элемента и закрыть боковую навигацию, если она открыта.

Нам также нужно, чтобы показать пользователю текущий отображаемый список, для этого мы добавляем класс .is-active, если 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>

Создать новый список

Переключить боковую панель

При нажатии на Создать новый список отображается .sidebar. Для этого мы хотим добавить к этому класс .is-open, а затем закрыть панель навигации, если она открыта на мобильном устройстве. Это очень похоже на то, что мы сделали с навигацией на мобильных устройствах.

В наши данные мы сначала добавим новую запись isSidebarOpen: false:

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

Теперь давайте свяжем наш класс .is-open с нашим .sidebar:

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

Нам нужно добавить обработчик событий при нажатии на Создать новый список, который откроет боковую панель и закроет навигацию на мобильном устройстве:

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

Отлично, теперь мы можем открыть нашу боковую панель 🎉.

Теперь закроем боковую панель, когда нажмем отменить:

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

Добавить новый список

Чтобы создать новый список, нам нужны значения полей ввода title и keyword. Когда пользователь нажимает Создать список, мы помещаем наши новые значения в todoLists в наших данных. Если один из наших входных данных пуст, мы покажем значение по умолчанию.

В наш app.js добавьте массив tempNewList, он будет хранить значения наших входных данных.

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

Теперь мы свяжем наши входы с помощью 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>

Хорошо, теперь давайте переместим наши новые tempNewList значения на todoLists.

Создайте метод с именем addNewList. Метод - это функция, хранящаяся как свойство объекта. Здесь объект - это экземпляр vue. В vue наш метод будет храниться в объекте methods.

addNewList будет следовать этому сценарию:

  1. Если title пуст, используйте строку по умолчанию "🕵️‍ 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;
    }
  }
});

Наконец, мы собираемся привязать наш метод к нашей кнопке Создать список.

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

Ссылки на документацию

Редактировать список

Хорошо, теперь, когда мы можем создать новый список, нам нужна возможность редактировать существующие. Мы сможем редактировать заголовок, ключевое слово и удалить список.

Переключить содержимое боковой панели

Создайте новый метод openSidebar. Этот будет:

  1. Откройте боковую панель
  2. Покажем форму, которую мы хотим использовать
  3. Закройте навигацию, если она открыта

В данных давайте добавим sidebarContentToShow: null. Это позволит нам узнать, какая форма должна отображаться.

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

У нас есть 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>

Теперь, когда мы нажимаем на Создать новый список, появляется боковая панель и мы видим… Ничего 😱. Это нормально, помните, sidebarContentToShow имеет значение null 😉.

Чтобы изменить значение sidebarContentToShow, мы собираемся создать openSidebar метод, который откроет боковую панель и изменит форму, которую мы хотим показать.

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

Теперь мы можем изменить Создать новый список, чтобы использовать openSidebar

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

И, та-да, мы сейчас визуализируем форму создания нового списка. Как вы, возможно, уже догадались, мы повторно воспользуемся нашим методом с кнопкой Изменить список.

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

Удалить список

Начнем с кнопки удалить список. Создайте новый метод с именем deleteList. Он удалит текущий отображаемый список и покажет первый.

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

Теперь мы можем удалять списки, но если мы попытаемся удалить все списки, мы получим ошибку, и наше приложение перестанет работать.

[Vue warn]: Error in render: "TypeError: todoLists[currentListIndex] is undefined"

Как вы, возможно, догадались, у нас есть эта ошибка, потому что наш todoLists пуст, и мы все еще пытаемся визуализировать некоторую часть нашего приложения, которая полагается на значения todoLists. Мы будем использовать условный рендеринг v-if и v-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>

Измените заголовок и значение ключевого слова

Вернемся к нашей форме 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>

Ссылки на документацию

Создать и отредактировать задачу

Пользовательский интерфейс нашего приложения почти готов, нам еще нужно:

  • Создать новую задачу в списке
  • Редактировать и удалять существующее задание

Звучит похоже на то, что мы сделали со списками, верно? Это будут почти те же шаги.

Создать задачу

В наших данных создайте новый элемент tempNewList:

tempNewTodo: [
  {
    name: null,
    isCompleted: false
  }
],

Нам нужен новый метод, поэтому мы можем добавить нашу новую задачу в список в 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;
}

Теперь давайте углубимся в наш HTML.

Нам нужно открыть боковую панель с формой createNewTodo.

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

Как и раньше, мы свяжем наши входные данные с помощью 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>

Поскольку сейчас мы привязываем данные для 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;
}

А теперь в нашем 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>

Редактировать задачу

Чтобы отредактировать задачу, во-первых, нам нужно знать индекс задачи, которая будет редактироваться, в наших данных create currentTodoIndex.

currentTodoIndex: 0,

Нам понадобится deleteTodo метод, который удалит текущее задание.

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

Теперь давайте посмотрим на наш HTML.

Во-первых, мы хотим открыть наш ползунок и изменить значение currentTodoIndex.

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

В нашей форме 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>

🎉🎉🎉🎉🎉 Пользовательский интерфейс нашего списка задач готов!

LocalStorage

Когда мы перезагружаем страницу, она возвращается к нашим фиктивным значениям. Насколько было бы здорово, если бы мы могли хранить наши списки и задачи локально?

Мы собираемся использовать window.localStorage. Это часть API веб-хранилища.

localStorage позволяет нам хранить данные без срока действия.

В нашем app.js создайте новый метод updateTodoLocalStorage

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

Мы используем метод setItem() из 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));
    }
  }
});

Теперь давайте проверим наше приложение и заглянем в Локальное хранилище. Если мы создадим / обновим список или задачу, мы увидим, что наше todoLocalStorage хранилище обновляется.

Теперь, когда мы загружаем нашу страницу, нам нужно установить наш todoLists как наш todoLocalStorage. Vue поставляется с крючками жизненного цикла. Мы будем использовать created: function(), чтобы установить наши значения. Мы также собираемся удалить наши фиктивные значения.

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

Теперь, если мы перезагрузим, закроем и снова откроем наше приложение, все наши задачи и список будут сохранены 🤟.

Ссылки на документацию

Бонус - автономное прогрессивное веб-приложение (PWA)

В этой бонусной части этого руководства мы настроим прогрессивное веб-приложение (PWA) и сервис-воркеров, чтобы мы могли использовать это веб-приложение в автономном режиме на смартфоне.

Настроить PWA

PWA - это:

Прогрессивные веб-приложения - это веб-приложения, которые представляют собой обычные веб-страницы или веб-сайты, но могут выглядеть для пользователя как традиционные или собственные мобильные приложения. Тип приложения пытается объединить функции, предлагаемые большинством современных браузеров, с преимуществами мобильного взаимодействия. википедия

По сути, это веб-приложение, которое использует преимущества новейших технологий для работы и ощущается как нативное приложение.

Чтобы настроить наш 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">

Манифест веб-приложения

Манифест веб-приложения (manifest.json) - это файл, который предоставляет информацию о нашем веб-приложении, такую ​​как значки, имя нашего приложения и т. Д. Он является частью технологий, используемых для PWA. Дополнительную информацию о манифесте веб-приложения можно найти в Веб-документации MDN.

Создайте этот файл в корне нашего проекта.

{
  "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/"
}

Мы хотим включить его в наш HTML-файл.

<link rel="manifest" href="manifest.json">

Сервисные работники

Что такое сервис-работники?

Сервис-воркеры - это новая функция браузера, которая предоставляет сценарии, управляемые событиями, которые выполняются независимо от веб-страниц. В отличие от других воркеров, сервис-воркеры могут быть отключены в конце событий, обратите внимание на отсутствие сохраненных ссылок из документов, и у них есть доступ к общедоменным событиям, таким как выборка из сети. У сервис-воркеров также есть кеш-память с поддержкой сценариев. Наряду с возможностью отвечать на сетевые запросы с определенных веб-страниц с помощью сценария, это позволяет приложениям переходить в автономный режим. w3c / ServiceWorker - Github

Это руководство не ставит своей целью углубленное изучение обслуживающего персонала, вы можете найти отличное руководство и ресурсы в Интернете:

Для наших сервисных работников мы используем эту суть от Omranic.

Создайте файл sw.js в корне нашего проекта.

В нашем index.html:

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

Затем в нашем 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"
];

Теперь нам нужно только следовать этой сути и добавить части Слушать установочное событие, Обновить кэш активов и Обслуживать оболочку приложения в автономном режиме из кеша.

Это руководство, наконец, готово. Теперь наш todo vue.js PWA доступен здесь https://vinceumo.github.io/todo