浏览代码

computed, watch

Евгений Колесников 1 年之前
父节点
当前提交
5fc3fa58a7
共有 6 个文件被更改,包括 203 次插入77 次删除
  1. 6 0
      articles/web_06.md
  2. 186 27
      articles/web_09.md
  3. 10 7
      articles/web_10.md
  4. 0 21
      articles/web_18.md
  5. 0 8
      articles/web_19.md
  6. 1 14
      readme.md

+ 6 - 0
articles/web_06.md

@@ -30,6 +30,12 @@
 
 1. Устанавливаем плагин **tailwind**
 
+    >У меня на linux пакет `@vue/cli` подтянулся автоматически, но на винде может выдасть ошибку (может дело и не в винде, а в старом npm...). В таком случае нужно принудительно установить `@vue/cli`
+    >
+    >```
+    >npm install @vue/cli
+    >```
+
     ```
     npx @vue/cli add tailwind
     ```

+ 186 - 27
articles/web_09.md

@@ -1,49 +1,208 @@
 [К содержанию](../readme.md#введение-в-web-разработку)
 
-# Введение во Vue
+# Vue.js практика: computed, watch
 
-1. [Vue 3: Введение](https://v3.ru.vuejs.org/ru/guide/introduction.html)
-1. Создание проекта
+## #17 Криптономикон: рефакторинг
 
-    >Для новых проектов теперь рекомендуется использовать `create-view` для создания проектов на основе Vite. 
+<!-- 1 час 24 мин -->
 
-    >Для работы с проектами на **Vue** необходим **NPM** (англ. Node Package Manager) — менеджер пакетов, входящий в состав Node.js.
+* [YouTube](https://www.youtube.com/watch?v=_esgbWGiP3c)
+* [RuTube](https://rutube.ru/video/84b87c1a5285bb57d7771e67e9436fc7/?r=wd&t=613)
 
-    Создание **Vue** проекта:
 
-    ```
-    npm init vite@latest
+**Содержание урока:**
+
+* [формирование списка проблем для рефакторинга](#список-проблем-существующего-кода)
+* [наличие в состоянии зависимых данных (computed)](https://www.youtube.com/live/_esgbWGiP3c?t=1980)
+
+**Материалы к изучению:**
+
+- [вычисляемые свойства](https://ru.vuejs.org/guide/essentials/computed.html)
+
+## Расшифровка скринкаста
+
+### Список проблем существующего кода
+
+* [x] одинаковый код в наблюдателях
+* [ ] при удалении остается подписка (таймер)
+* [ ] количество запросов (в АПИ есть возможность получить данные по списку тикеров)
+* [ ] запросы к АПИ внутри компонента
+* [ ] обработка ошибок АПИ
+* [x] наличие состояния зависимых данных
+* [ ] график переполняется
+* [x] при удалении тикера не меняется LocalStorage
+* [ ] LocalStorage и анонимные вкладки (LocalStorage может быть недоступен)
+* [ ] магические строки и числа (url, 5000 мсек, ключ localstorage, размер страницы)
+
+**Параллельно**
+
+* [x] график сломан, если везде одинаковые значения
+* [x] при удалении тикера остается выбор
+
+### Наличие состояния зависимых данных
+
+Метод **filteredTickers** зависит от страниц и массива тикеров, в таком случае нужно использовать [вычисляемые свойства](https://ru.vuejs.org/guide/essentials/computed.html)
+
+В _Composition API_ у вычисляемых свойств другой синтаксис:
+
+```js
+const calculatedProperty = computed(() => {
+    // тут какие-то вычисления на основе состояний
+    return result
+})
+```
+
+Вычисляемые свойства кешируются, т.е. будут вычисляться только при изменнии используемых реактивных переменных (состояний). В нашем случае это "номер страницы", "строка фильтра" и "список тикеров"
+
+При использовании в шаблоне надо помнить, что **computed** это свойство, а не функция. Он не может принимать аргументы (не нужно указывать круглые скобки)
+
+Если установлен линтер, то он может показать ошибку, что присваиванию _hasNextPage_ не место в **computed**
+
+Т.к. это тоже вычисляемое свойство, то его тоже можно завернуть в **computed**
+
+>Попутно решили, что start и end тоже можно вынести в **computed** для единообразия
+
+В итоге получились такие вычисляемые свойства:
+
+```js
+const startIndex = computed(() => {
+  return (page.value - 1) * 6 
+})
+
+const endIndex = computed(() => {
+  return page.value * 6
+})
+
+// это объясняется чуть позже
+const filteredTickers = computed(() => {
+  return tickers.value.filter(
+    t => t.name.includes(filter.value.toUpperCase()))
+})
+
+// не забудьте поменять в шаблоне filteredTickers на paginatedTickers
+const paginatedTickers = computed(() => {
+  return filteredTickers.value.slice(startIndex.value, endIndex.value)
+})
+
+// не забудьте удалить переменную hasNextPage
+const hasNextPage = computed(() => {
+  // я уже упоминал, что computed возвращает реактивный объект, т.е. к его значению надо обращаться через свойство .value  
+  return filteredTickers.value.length > endIndex.value
+})
+```
+
+Как понять что нужно использовать **computed**? Очень просто. Если нет прямого изменения состояния (присваивания), а только вычисление по другим реактивным переменным (в лекции сказали ещё "если результат используется в шаблоне", но это не так, в этом же примере _startIndex_ и _endIndex_ сделаны вычисляемыми, но они используются только в коде).
+
+Смотрим, что ещё можно поменять на **computed**?
+
+* **normalizeGraph** - переделайте в **computed** самостоятельно, заодно переименуйте в _normalizedGraph_ (прилагательное вместо глагола, т.е. у нас не действие, а свойство)
+
+* разбить **filteredTickers** на два вычисляемых свойства (выше уже есть код)
+
+### Починка графика
+
+Если минимальное значение равно максимальному, то возвращать 50%, чтобы было ровное плато посередине.   
+
+```js
+const normalizedGraph = computed(() => {
+  const maxValue = Math.max(...graph.value)
+  const minValue = Math.min(...graph.value)
+
+  if (maxValue == minValue) {
+    return graph.value.map(() => 50)
+  }
+
+  return graph.value.map(
+    price => 5 + ((price - minValue) * 95) / (maxValue - minValue)
+  )
+})
+```
+
+### Применение "наблюдателей"
+
+В коде часто встречаются условия "если изменилось что-то, то сделай то-то". Это значит, что можно использовать **watch**
+
+Рассматривается пример с удалением тикеров на второй странице. В результате мы остаемся на пустой странице.
+
+Вариантов решения два:
+
+* в методе удаления тикеров проверить длину массива _paginatedTickers_ и если он пустой, то уменьшить номер страницы
+* использовать наблюдатели  
+
+    >Обратите внимание, я использую синтаксис с параметрами. Первым параметром в функции приходит новое значение переменной.
+
+    ```js
+    watch(paginatedTickers, (value) => {
+        if (value.length === 0 && page.value > 1) page.value--
+    })
     ```
 
-    Процесс инициализации проекта интерактивный. Установщик предлагает ввести название проекта и выбрать нужные пакеты (практически все можно оставить по-умолчанию):
+### при удалении тикера остается выбор
 
-    * `Project name:` - название проекта (по-умолчанию предлагает `vue-project` - можете оставить его)
-    * `Add TypeScript?` - оставляем `No`
-    * `Add JSX Support?`
-    * `Add Vue Router...`
-    * `Add Pinia...`
-    * `Add Vitest...`
-    * `Add Ent-to-End Testing Solution`
-    * `Add ESLint` - меняем на `Yes`
-    * `Add Prettier` - меняем на `Yes`
+В метод **handleDelete** добавляем логику проверки и очистки выделенного тикера
 
-    После инициализации проекта в текущем каталоге появится папка с именем проекта, который вы ввели
+```js
+if (sel.value == tickerToRemove) {
+    sel.value = null
+}
+```  
 
-    >Пример выше был расписан под **Linux**, но, как оказалось, под **Windows** поведение скрипта отличается. 
+Заодно поменяйте _sel_ на человеко-понятное наименование (названия переменных должны быть самоочевидны)
 
-    **Важно!!!** При инициализации проекта не загружаются зависимости. После установки необходимо выполнить команду `npm install`
+### Вынос сброса графика в методе **select** в watch 
 
-1. Продолжаем изучать [JavaScript](https://learn.javascript.ru/property-accessors)
+Напоминаю текущий код:
 
----
+```js
+function select (ticker) {
+  sel.value = ticker
+  graph.value = []
+}
+```
 
-**Задание**
+Он соответствует правилу "если изменилось что-то, то сделай то-то", соответственно можно его перенести очистку графика в наблюдатель (сделайте самостоятельно)
+
+### Вынос логики сохранения тикеров в LocalStorage в наблюдатель (заодно исправится бага, когда список не сохранялся при удалении тикера)
+
+Наблюдатель реализуйте сами. И не забудьте изменить добавление тикеров в массив (иначе на сработает **watch** на массив)
+
+```js
+tickers.value = [...tickers.value, newTicker]
+```
 
-1. Перечитать и осмыслить [Vue 3: Введение](https://v3.ru.vuejs.org/ru/guide/introduction.html)
+Что делает этот код?
 
-1. Создать проект на **Vue**, изучить структуру, запустить и "поиграться" с вёрсткой и кодом
+Выражение `...array` делает декомпозицию массива, т.е. извлекает все его элементы и вставляет в новый массив. В итоге в массив тикеров записывается новый массив, содержащий все старые элементы и новый.
 
-1. Перечитать и осмыслить первую часть [Учебника](https://learn.javascript.ru/property-accessors) до раздела "Промисы: async/await"
+### Исправление дублирования логики в наблюдателях **page** и **filter**
+
+1. Создаем вычисляемое свойство _pageStateOptions_, которое будет хранить и фильтр и страницу:
+
+    ```js
+    const pageStateOptions = computed(() => {
+        return {
+            filter: filter.value,
+            page: page.value
+        }
+    })
+    ```
+
+1. И вешаем на него наблюдателя
+
+    ```js
+    watch(pageStateOptions, (value) => {
+        window.history.pushState(
+            null,
+            document.title,
+            `${window.location.pathname}?filter=${value.filter}&page=${value.page}`
+        )
+    })
+    ```
+
+--- 
+
+**Задание**
 
+* 
 
 [Назад](./web_08.md) | [Дальше](./web_10.md)

+ 10 - 7
articles/web_10.md

@@ -1,17 +1,20 @@
 [К содержанию](../readme.md#введение-в-web-разработку)
 
-# Учимся учиться - Vue.js: практика
+# Vue.js:
 
-<!-- 5 мин -->
+## #18 Криптономикон: рефакторинг
 
-1. [#9 Учимся учиться - Vue.js: практика](https://www.youtube.com/watch?v=ekrzshM4AHI)
+<!-- 2ч -->
 
-1. Продолжаем изучать [JavaScript](https://learn.javascript.ru/async)
+* [YouTube](https://www.youtube.com/live/AzsO67rloQw?feature=share&t=248)
+* [RuTube](https://rutube.ru/video/979b2cce76478860252ca7f6ee63b4b1/?r=wd&t=250)
 
----
+**Содержание урока:**
+
+- Презентация задачи для шаблона письма
+- разделение бизнес логики и реализации (вынос запросов АПИ в отдельный файл, вебсокеты, подписка на обновления)
 
-[Курс Learning to learn (бесплатно, русские субтитры):](coursera.org/learn/learning-how-to-learn)
+---
 
-1. Перечитать и осмыслить первую часть [Учебника](https://learn.javascript.ru/async) до раздела "Модули"
 
 [Назад](./web_09.md) | [Дальше](./web_11.md)

+ 0 - 21
articles/web_18.md

@@ -1,28 +1,7 @@
 [К содержанию](../readme.md#введение-в-web-разработку)
 
-# Криптономикон: рефакторинг - Vue.js: практика
-
-<!-- 1 час 24 мин -->
-
-1. [#17 Криптономикон: рефакторинг - Vue.js: практика (computed)](https://www.youtube.com/watch?v=_esgbWGiP3c)
-
-    **Содержание урока:**
-
-    - формирование списка проблем для рефакторинга
-    - [наличие в состоянии зависимых данных (computed)](https://www.youtube.com/live/_esgbWGiP3c?t=1980)
-
-    **Материалы к изучению:**
-
-    - [вычисляемые свойства](https://v3.ru.vuejs.org/ru/guide/computed.html#%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D1%8F%D0%B5%D0%BC%D1%8B%D0%B5-%D1%81%D0%B2%D0%BE%D0%B8%D1%81%D1%82%D0%B2%D0%B0)
-
-    - [исходный код урока](https://gitlab.com/vuejs-club/youtube-course/cryptonomicon/-/tree/lesson5)
-
-1. Продолжаем изучать учебник [JavaScript (CSS для JavaScript-разработчика)](https://learn.javascript.ru/css-for-js) 
 
 ---
 
-## Задание
-
-1. Пересмотреть видео [#17 Криптономикон: рефакторинг - Vue.js: практика](https://www.youtube.com/watch?v=_esgbWGiP3c) и доработать свой код. 
 
 [Назад](./web_17.md) | [Дальше](./web_19.md)

+ 0 - 8
articles/web_19.md

@@ -2,17 +2,9 @@
 
 # Криптономикон: рефакторинг - Vue.js: практика (часть 2)
 
-<!-- 2ч -->
 
-1. [#18 Криптономикон: рефакторинг - Vue.js: практика](https://www.youtube.com/live/AzsO67rloQw?feature=share&t=248)
 
-    **Содержание урока:**
 
-    - Презентация задачи для шаблона письма
-    - разделение бизнес логики и реализации (вынос запросов АПИ в отдельный файл, вебсокеты, подписка на обновления)
 
-    **Материалы к изучению:**
-
-    [исходный код урока](https://gitlab.com/vuejs-club/youtube-course/cryptonomicon/-/tree/lesson6)
 
 [Назад](./web_18.md) | [Дальше](./web_20.md)

+ 1 - 14
readme.md

@@ -344,23 +344,10 @@ https://office-menu.ru/uroki-sql Уроки SQL
 1. [Vue.js: Реализация реактивности (нюансы), Компоненты, tailwind](./articles/web_06.md)
 1. [Криптономикон-4 - Самостоятельная работа. Promise, async/await.](./articles/web_07.md)
 1. [Vue.js: LocalStorage, фильтрация, пагинация, наблюдатели, history](./articles/web_08.md)
+1. [Vue.js практика: computed, watch](./articles/web_09.md)
 
 <!-- 
 
-1. [Двустороннее связывание - Vue.js: концепции](./articles/web_04.md)
-1. [Как во Vue.js использовать Х - Vue.js: нюансы](./articles/web_05.md)
-1. [Знакомство - Vue.js: практика](./articles/web_06.md)
-1. [Декларативность - Vue.js: концепции](./articles/web_07.md)
-1. [Бизнес логика или детали реализации? - Vue.js: концепции](./articles/web_08.md)
-1. [Введение во Vue](./articles/web_09.md)
-1. [Учимся учиться - Vue.js: практика](./articles/web_10.md)
-1. [Криптономикон-1 - Vue.js: практика](./articles/web_11.md)
-1. [Криптономикон-2 - Vue.js: практика](./articles/web_12.md)
-1. [Реализация реактивности - Vue.js: нюансы](./articles/web_13.md)
-1. [Компоненты - Vue.js: концепции](./articles/web_14.md)
-1. [Криптономикон-3: vue-cli и tailwind](./articles/web_15.md)
-1. [Криптономикон-4 - Самостоятельная работа (валидации)](./articles/web_16.md)
-1. [Криптономикон-5: Работа со списком - Vue.js: практика](./articles/web_17.md)
 1. [Криптономикон: рефакторинг - Vue.js: практика (computed)](./articles/web_18.md)
 1. [Криптономикон: рефакторинг - Vue.js: практика (часть 2)](./articles/web_19.md)
 1. [Криптономикон: ещё раз о рефакторинге - Vue.js: практика. #20 YAGNI - Vue.js: практика.](./articles/web_20.md)