[Коллекция будет] Руководство по использованию Browser WebStorage Cache

внешний интерфейс JavaScript браузер
[Коллекция будет] Руководство по использованию Browser WebStorage Cache

⚠️Эта статья является первой подписанной статьей сообщества Nuggets, и её перепечатка без разрешения запрещена.

задний план

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

image.png

Эта серия требований может быть достигнута с помощью технологии хранения браузера. В этой статье давайте узнаем о технологии хранения в браузере.WebStorage, всестороннее понимание его базового и расширенного использования, а также как использовать эти методы для отработки двух распространенных сценариев. Представляя метод использования, я также инкапсулирую工具类Способ унифицировать все методы вызова, изучив это, может сделать его более удобным для вас в развитии бизнеса.

Почему стоит выбрать WebStorage?

Мы знаем, что распространенные методы облегченного хранения браузера включаютCookieа такжеWebStorage. Итак, почему мы выбираемWebStorageвместоCookieШерстяная ткань?

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

Второй,chrome(80+)Браузеры блокируют все по умолчанию三方CookieЭто больше не шокирует. С этим изменениемCookieБез сомнения, была отрезана еще одна мощная рука. Для тех из вас, кто не знает, эта статья сильно написана Amway, которая анализирует ее очень подробно.Когда браузер полностью отключает сторонние файлы cookie.

Кроме того, используйтеCookieТакже необходимо решить следующие проблемы:

  • Сохраненные данные слишком малы,CookieРазмер хранилища составляет всего4k, если вам нужно хранить много данных, то очевидно, что это не может удовлетворить ваши потребности, и вообще никто этим не занимается.
  • всегда носить с собойHTTP请求头, будет некоторое взаимодействие с сервером, когда я просто сохраняю некоторые локальные данные, это, очевидно, приведет к потере производительности.

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

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

Основное использование

В этой главе я познакомлю вас с некоторыми базовыми навыками использования webStorage с точки зрения класса пакетного инструмента. Здесь также поделитесь ссылкой на адрес онлайн-источника:Кодекс практики упаковки при хранении

Затем в инструменте отладки браузераApplicationВ меню вы можете увидеть слеваStorageОтладочная версия, в которой мы проходимAPIЗдесь можно отладить значение, сохраненное в хранилище.

image.png

Поддержка среды и инициализация

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

image.png

В наш код мы также должны добавить слой отказоустойчивого суждения.Если нам нужно сделать его совместимым, мы можем выполнить деградацию обработки. Такие какCookieилиIE6中изuserData持久化用户数据.

Ниже приведено относительно простое суждение, которое также можно инкапсулировать в простую вызываемую функцию. Выдает какую-то ошибку в консоль, если браузер ее не поддерживает.

class CustomStorage {
  private readStorage: Storage

  constructor () {
    if (!window) {
        throw new Error('当前环境非浏览器,无法消费全局window实例。')
    }
    if (!window.localStorage) {
        throw new Error('当前环境非无法使用localStorage')
    }
    if (!window.sessionStorage) {
        throw new Error('当前环境非无法使用sessionStorage')
    }
  }
}

Используйте, когда среда поддерживаетWebStorageПри условии , вы можете инициализировать некоторые данные по умолчанию, выберите, какие из них использовать здесьStorageи сохраните конфигурацию.

interface StorageBootStrapConfig {
  /** 当前环境 */
  mode: 'session' | 'local',
  
  /** 超时时间 */
  timeout: number
}

/**
   * 初始化Storage的数据
   * @param config StorageBootStrapConfig
   */
 bootStrap (config: StorageBootStrapConfig): void {
  switch (config.mode) {
    case 'session':
      this.readStorage = window.sessionStorage
      break;

    case 'local':
      this.readStorage = window.localStorage
      break;
  
    default:
      throwErrorMessage('当前配置的mode未再配置区内,可以检查传入配置。')
      break;
  }
  this.config = config
}

Затем, поbootstrapПосле инициализации некоторых текущих конфигураций вы можете передать текущий экземпляр на страницеcustomStorageиспользовать некоторые функциональные методы.

import CustomStorage from 'web-storage-db'

const customStorage = new CustomStorage()

customStorage.bootStrap({
  mode: 'local',
  timeout: 3000,
})

export default customStorage

Сериализация JSON

дляWebStorageС точки зрения хранения стоимости очень зависит отJSON的序列化. Как показано ниже:

image.png

при сдаче на хранениеObject类型, сохраненные данные становятся строкой своего типа, потому чтоWebStorageХранение может быть только字符串существует в виде , поэтому, если мы хотим хранить данные ссылочного типа, нам нужно полагаться наJSON序列化способность. пройти черезstringifyа такжеparseПосле того, как некоторые методы обработают значение, вы можете очень хорошо хранить некоторые ссылочные типы.

Но есть некоторыеJSON.stringifyДанные недружественного типа, старайтесь не хранить, напримерundefined, Function, SymbolПодождите, я также написал здесь простую функцию для проверки сохраненного значения.

/**
 * 判断当前值是否能够呗JSON.stringify识别
 * @param data 需要判断的值
 * @returns 前参数是否可以string化
 */
export function hasStringify (data: any): boolean {
  if (data === undefined) {
    return false
  }

  if (data instanceof Function) {
    return false
  }

  if (isSymbol(data)) {
    return false
  }

  return true
}

вisSymbolметод делаетSymbolТиповое оценочное суждение.

/**
 * 判断当前类型是否是Symbol
 * @param val 需要判断的值
 * @returns 当前参数是否是symbol
 */
export function isSymbol(val: any): boolean {
  return typeof val === 'symbol'
}

сохранить данные

Если вам нужно сохранить данные вWebStorageкоторый сам обеспечиваетsetItemAPI для этого здесьlocalStorageНапример, значение может быть сохранено в виде:

// # 原生
window.localStorage.setItem('key', 'value')

// # attribute形式存储
window.localStorage['key1'] = 'value'
window.localStorage.name = 'wangly19'

image.png

А мы в использовании, явно не будем пользоваться原生APIметод, большинство из них будет упаковано в工具方法, чтобы справиться с некоторыми повторяющимися задачами. Например, в следующем пакете я сделал слой упаковки для содержимого хранимых данных и добавилJSON序列化数据а также过期时间.

/**
   * 设置当前
   * @param key 设置当前存储key
   * @param value 设置当前存储value
   */
 setItem(key: string, value) {
  if (hasStringify(value)) {
    const saveData: StorageSaveFormat = {
      timestamp: new Date().getTime(),
      data: value
    }
    console.log(saveData, 'saveData')
    this.readStorage.setItem(key, JSON.stringify(saveData))
  } else {
    throwErrorMessage('需要存储的data不支持JSON.stringify方法,请检查当前数据')
  }
}


// 使用
customStorage.setItem('setItem', [1])

image.png

читать данные

Раз есть задаток, значит и чтение должно быть, можно пройтиgetItemилиObjectчтобы прочитать значение в виде . Теперь давайте рассмотрим три примера.

image.png

window.localStorage.setItem('person', JSON.stringify({ 
    name: 'wangly19', 
    age: 22 
}))

const person = window.localStorage.getItem('person')


JSON.parse(person)

// { name: "wangly19", age: 22 }

image.png

выше普通的使用方式, в то время как мы封装Когда сохраненные данные сохраняются, некоторые判断, сделать сохраненные данные JSON解析化的处理, вернуться напрямую解析后的数据, более удобный и простой в использовании.

/**
  * 获取数据
  * @param key 获取当前数据key
  * @returns 存储数据
*/
getItem<T = any>(key: string): T | null {
  const content: StorageSaveFormat | null = JSON.parse(this.readStorage.getItem(key))
  return content?.data || null
}

// # 使用
customStorage.getItem('setItem') // [1]

Удалить

Для удаления хранилища можно использовать не толькоremoveItem,deleteи т. д. для выполнения операций над сохраненным значением移除.

// # removeItem
window.localStorage.removeItem('person')

// # delete
delete window.localStorage.person
delete window.localStorage['peson']

Вы также можете использовать clear, чтобы очистить все данные в хранилище.

window.localStorage.clear()

Кроме того, если часть данных удаляетсяStorageнет текущего хранилищаkeyданные, то нам не нужно выполнять текущий移除数据операция. Давайте посмотрим на пакет нижеremoveItemметод, я добавил слой值是否存在чтобы решить, действительно ли необходимо выполнить этот шаг удаления.

/**
   * 移除一条数据
   * @param key 移除key
   */
removeItem(key: string) {
  if (this.hasItem(key)) {
      this.readStorage.removeItem(key)
  }
}

/**
 * 清除存储中所有数据
 */
clearAll() {
  this.readStorage.clear()
}

длина

WebStorageПринести свои собственныеlengthсвойство, вы можете получить текущийStorageдлина.

window.localStorage.length

/**
   * 返回当前存储库大小
   * @returns number
*/
size(): number {
    return this.readStorage.length
}

image.png

ключи и значения

Увидев это, многие друзья должны знать, как этого добиться? да, черезObject.keysа такжеObject.valuesможно получить текущийStorageвсе вkeyа такжеvalue. часть埋点SDKбудет сообщеноStorageдля фильтрации данных.

Object.keys(localStorage)
// (4) ["wwwPassLogout", "BIDUPSID", "BDSUGSTORED", "safeIconHis"]
Object.values(localStorage)
// (4) ["0", "30B3EE0AF6EE9F4F89EF16486C288502", "[{\"q\":\"localstorage%20%E8%BF%87%E6%9C%9F%E6%97%B6%…:\"new%20dateshijianchuo\",\"s\":4,\"t\":206989341223}]", ""]

Второй черезkey(index)метод, вы можете напрямую получить значение определенной позиции.

window.localStorage.key(0)
window.localStorage.key(1)
window.localStorage.key(2)

существует工具类Среди них я также провел封装,можно использоватьgetKeys, getValuesчтобы получить все пространство для храненияKeyа такжеValueколлекция.

/**
   * 获取所有key
   * @returns 回storage当中所有key集合
   */
getKeys(): Array < string > {
  return Object.keys(this.readStorage)
}

/**
 * 获取所有value
 * @returns 所有数据集合
 */
getValues() {
  return Object.values(this.readStorage)
}

// # 使用
customStorage.getKeys()
customStorage.getValues()

image.png

Наличие собственности?

судить о текущемStorageНаличие определенного атрибута вgetItemЧтобы получить значение, а затем решить, существует ли значение, чтобы вынести суждение.

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

localStorage.key(2)
// "BDSUGSTORED"
localStorage.hasOwnProperty('BDSUGSTORED')
// true
localStorage.hasOwnProperty('1111')
// false

image.png

Исходя из этого, я также инкапсулировал метод оценки наличия значения в хранилище.hasItemСпособ сделать что-либоkeyЕсть ли какое-то суждение в хранилище.

/**
 * 判断是否存在该属性
 * @param key 需要判断的key
 */
hasItem(key: string): boolean {
  return this.readStorage.hasOwnProperty(key)
}

Расширенное использование

В расширенном использовании я представлю некоторые работы, которые могут быть碰到的问题, и дайте немного解决方案.

Срок действия

WebStorageсерединаSessionStorageЦикл - текущая сессия. а такжеlocalStorageЕсли не очистить вручную, сохраненные данные не будут активно очищаться.

Ключевые слова, которые зададут в интервью: Если localStorage активно не очищается, срок хранения хранимых данных не истечет.

Поэтому во многих случаях, если требуется время истечения, разработчику необходимо разобраться с этим самому, и метод обработки также очень прост и жесток. Это займет время при передаче сохраненного значения. Обратитесь к приведенному ниже коду черезnew Date().getTime()чтобы получить текущее время, а затем установить его в хранилище.

const person = {
    // 存储数据
    data: {
        name: 'wangly19',
        age: 22
    },
    // 过期时间
    timestamp: new Date().getTime()
}
window.localStorage.setItem('person', JSON.stringify(person))

При получении времени будет простая суждение,当前时间 - 存储时间 >= 过期时间Это может выполнить некоторую обработку суждения при управлении значением.

// # 原生

let person = localStorage.getItem('person')
person = JSON.parse(val)

// 这里可以使用一些库在做处理,如`dayjs`
if(new Date().getTime() - person.timestamp > [过期时间]) {
    // 数据已经过期的一些操作
} else {
    // 正常处理
}

Поэтому необходимоgetItemметод, добавляю оценку времени истечения срока действия, и я также напрямую инкапсулирую эту логику в функцию.

/**
  * 获取数据
  * @param key 获取当前数据key
  * @returns 存储数据
*/
getItem<T = any>(key: string): T | null {
  const content: StorageSaveFormat | null = JSON.parse(this.readStorage.getItem(key))
  if (content?.timestamp && new Date().getTime() - content.timestamp >= this.config.timeout) {
    this.removeItem(key)
    return null
  }
  return content?.data || null
}

функция слушателя

WebStorageПри изменении браузер будет запущенstorageмероприятие.

А в приложении можно использоватьaddEventListenerдобавить одинstorageСобытие обязывает его.

Спусковой механизм можно увидеть на рисунке ниже. в разных окнахstorageПри срабатывании выдает токeventИнформация. существуетeventСреди них мы можем получить триггерurl,新值, 旧值, 触发的keyВ ожидании информации мы можем использовать этот API для мониторинга URL-адресов в браузере.

<script>
 document.body.innerHTML = '初始化数据'
 window.addEventListener("storage", function (event) {
 const values = {
 url: event.url,
 key: event.key,
 old: event.oldValue,
 new: event.newValue,
 }
 document.body.innerHTML = JSON.stringify(values)
 });
 </script>

изменить данные

Так как нет родногоchangeItemЭто своего рода метод, поэтому нам нужно сделать некоторые методы самостоятельно封装Для облегчения нашей частой необходимости модифицировать данные в хранилище.

Например, следующий, похожий наuseStateВ форме обратного вызова для изменения значения.

changeItem('name', (oldValue) => {
    const name = `update: ${oldValue} update`
    return name
})

Метод реализации также относительно прост для понимания, благодаряgetItemЧтобы получить данные, а затемsetItemнастраиватьonChangeЗначение функции обратного вызова, которая объединяет согласованную операцию.

/**
   * 修改当前存储内容数据
   * @param key 当前存储key
   * @param onChange 修改函数
   * @param baseValue 基础数据
   */
 changeItem<S = any>(
  key: string, 
  onChange: (oldValue: S) => S | null, baseValue?: any
) {
  const data = this.getItem<S>(key)
  this.setItem(key, onChange(data || baseValue))
}

// # 使用
customStorage.changeItem('key', (oldValue) => {
    retutn oldValue + 'newUpadte'
})

Пробел и переполнение

Если это активный пользователь, например, некоторые проекты по созданию документов, многие из них часто переходят кlocalStorageВ нем хранится очень много данных, и многие разработчики беспокоятся о том, будет ли он напрямую溢出.

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

Статус хранения и оценка хранилища

В безопасном контексте и поддерживаемом браузере черезStorageEstimateВы можете получить состояние кеша текущего браузера, например: сколько используется и сколько всего.

Следующий код сначала определяет, существует ли браузерnavigator, а потом продолжайте судитьnavigatorЗдесьstorage, и, наконец, выполнитьestimateПолучить информацию о нашем магазине асинхронно.

if (navigator && navigator.storage) {
    navigator.storage.estimate().then(estimate => {
        console.log(estimate)
    });
}

image.png

Этот веб-API требует, чтобы текущий проект находился под https. Полученная квота (общая память) составляет около 3 МБ, что, безусловно, является безопасным диапазоном памяти в сценариях разработки.

Очистка переполненного кэша

если в内存濒临溢出В сценарии нам нужно освободить место для модификации данных после обработки. Во-первых, мы собираем и сортируем данные по времени.Следующий метод заключается вstorageвсе сtimestampДанные поля агрегируются и сортируются.

/**
 * 获取当前清除存储空间,并且进行排序
 */
getClearStorage() {
  const keys: string[] = Object.keys(this.readStorage)
  const db: Array<{
    key: string,
    data: StorageSaveFormat
  }> = []
  keys.forEach(name => {
    const item = this.getItem(name)
    if (item.timestamp) {
      db.push({
        key: name,
        data: item
      })
    }
  })
  return db.sort((a, b) => {
    return a.data.timestamp - b.data.timestamp
  })
}

Когда у вас есть отсортированный список данных, вам необходимо подумать об очистке данных и очистить более длительное время от текущего в соответствии с временной шкалой. В это время вам нужно понять условие:总大小(quota) - (使用大小)usage > [当前存入大小currentSize]

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

initCacheSize просто сначала обновляет данные емкости. Получите новые данные о емкости.

/**
 * 容量清理,直到满足存储大小为止
 */
detectionStorageContext(currentSize: number) {
  if (this.usage + currentSize >= this.quota) {
      const storage = this.getClearStorage()
      for (let { key, data } of storage) {
          // 如果满足要求就跳出,还不够就继续清除。
          if (this.usage + currentSize < this.quota) break
          // 刷新容量大小
          this.removeItem(key)
          initCacheSize()
      }
  }
}

Последний шаг -setItemвыполнить вdetectionStorageContext, каждый раз, когда сохраненный контент обновляется, он будет оцениваться первым是否要溢出, если бы добавленные или измененные данные переполнились бы, я бы сделал空间清理.

Практическая сцена

В этой главе основное внимание уделяется некоторым простымWebStorageсценарии использования.

история поиска

сюда, один из наших工具类Он в основном сформирован. Наконец, вернемся к一开始的案例, мы можем передатьchangItemБыстро реализуйте эту функцию истории поиска, не беспокоясь о некоторых проблемах с совместимостью данных. Все, на чем нам нужно сосредоточиться, это настройка для сохранения значения.

image.png

Код дела выглядит следующим образом:

export default function Search() {
  const [searchList, setSearchList] = useState([]);

  useEffect(() => {
    const data = localStore.getItem('search')
    setSearchList(data || [])
  }, [])

  const onSearch = (value) => {
    if (value) {
      localStore.changeItem(
        'search',
        (oldValue) => {
          if (oldValue.includes(value)) {
            return oldValue;
          }

          if (oldValue) {
            const newValue = [...oldValue, value];
            setSearchList(newValue);
            console.log(newValue, 'value');
            return newValue;
          }

          if (value) {
            setSearchList([value]);
            return [value];
          }

          return [];
        },
        [],
      );
    }
  };

  return (
    <div className="demo-app">
      <Search
        placeholder="请输入搜索内容"
        enterButton="Search"
        size="large"
        suffix={suffix}
        onSearch={onSearch}
      />

      <div className="tag-wrapper">
        {searchList.map((e) => {
          return (
            <Tag
              key={e}
              style={{
                margin: 10,
              }}
              color="#108ee9"
            >
              {e}
            </Tag>
          );
        })}
      </div>
    </div>
  );
}

данные изображения

Браузер имеет ограничения на запросы, и большинство картинок в нашем проекте фактически возвращаются через back-end интерфейс.emojiЭмодзи является примером.

Мы использовали данные эмодзи Zhihu для проведения моделирования и обнаружили, что всего существует 73 части данных.Очень неудобно запрашивать внутренние данные каждый раз при обновлении веб-страницы, и эти данные, очевидно, не нужно Хранится вStoreСреди них, в течение определенного периода времени, вероятность изменения очень маленькая, то мы помещаем его в本地存储Очевидно, хороший выбор.

image.png

Когда страница загружается, я добавляю уровень суждения к запросу данных интерфейса, только数据为空Только когда запрашивается список данных значка бэкэнда. Если это время истечения срока действия, данные локального значка будут очищены при получении данных, а затем данные внутреннего значка будут повторно запрошены, снова помещены в кеш, и будет обновлено новое время истечения срока действия.

const emojiRef = useRef(localStore.getItem('emoji'));

useEffect(() => {
    if (!emojiRef.current) {
      fetchEmojiIcon()
    }
  })

Если в вашем проекте много путей к ресурсам, вы можете поместить их вlocalStorageОн хранится в нем для удобного использования при необходимости.

image.png

Ресурсы и материалы

Суммировать

Эта статья оWebStorageБольшинство навыков использования в этой книге сделали некоторые краткие сведения об использовании, инкапсулировали некоторые часто используемые операции и методы хранения, а также рассмотрели некоторые сложные сценарии, которые часто встречаются в работе, такие как время истечения срока действия, изменение данных, переполнение буфера и другие функции. Сделаны некоторые описания, и, наконец, он инкапсулирован в класс инструмента, который удобно использовать в повседневной разработке.

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

последние хорошие статьи

концевые сноски

Если эта статья оказалась для вас полезной, я надеюсь, что вы поставите лайк и поддержите меня.

Эта статья была впервые опубликована в: Nuggets Technology Community
Тип: подписанная статья
Добавить Автора
Фаворит в колонке: javaScript базовый продвинутый
Публичный номер: Жизнь программы ItCodes