В непрекъснато развиващия се пейзаж на разработката на софтуер едно от ключовите предизвикателства е да гарантирате, че вашето приложение остава гъвкаво и адаптивно към променящите се изисквания. Превключването на функции, известно още като маркиране на функции, се очертава като мощна техника за справяне с това предизвикателство. Позволява на разработчиците да активират или деактивират специфични функции в приложение, без да променят кодовата база. Превключването на функции също така позволява на разработчиците да обединяват незавършени функции в основния клон, след което уеб разработчиците могат да създават нови функции стъпка по стъпка. В тази публикация в блога ще се потопим в внедряване на код за обработка на превключване на функции в приложения, изобразени от страна на клиента (CSR).

Механизмът за превключване на функции

Превключването на функции включва селективно активиране или деактивиране на определени функции в приложение въз основа на различни условия. Тези условия могат да варират от потребителски роли и разрешения до конкретни URL адреси или параметри на заявка. Кодът по-долу представя елегантен и ефективен начин за обработка на превключването на функции в CSR приложения.

featureToggleUtils.ts

Нека се потопим в кода, който управлява превключването на функциите.

// I have created my own typed interface to interact with local and session storage.
import { typedLocalStorage, typedSessionStorage } from '@utils/web-storage';

const featureFlagKey = 'featureFlags';
const persistFeatureKey = 'persistFeatureFlag';

// All the features that you want to be toggle on/off should be added here.
// To ensure that we type check the feature names across our application.
export type SupportedFeatureFlags = 'featureOne';

/*
 * Please add all the features that you want to be toggle on by default here.
 * Remember that all the features that are listed here will be available to the users in production,
 * since this is the default active features.
 */
const defaultActiveFeatures: SupportedFeatureFlags[] = [];

/**
 * @param featureFlag
 * @returns boolean
 * @description This function will check if the feature should be displayed 
 * or not. The feature can be toggled on by the url query, by local storage
 * or set as default active feature.
 * @example shouldDisplayFeature('myFeatureName') && <MyFeatureComponent />
 * @example The feature can be toggled and persisted by the url query,
 * (url)?featureFlags=[featureName]&persistFeatureFlag=true
 */
export const shouldDisplayFeature = (featureFlag: SupportedFeatureFlags): boolean => {
  // Check if feature should be persisted in session storage, (url)?persistFeatureFlag=true
  if (shouldPersistInSession()) {
    addFeatureFlagToSessionStorage(featureFlag);
  }

  return (
    isDefaultActivatedFeature(featureFlag) ||
    isFeatureActivatedByUrl(featureFlag) ||
    isFeatureActivatedBySessionStorage(featureFlag) ||
    isFeatureActivatedByLocalStorage(featureFlag)
  );
};

// Check if the feature is one of the default active features
const isDefaultActivatedFeature = (featureFlag: SupportedFeatureFlags): boolean => {
  return defaultActiveFeatures.includes(featureFlag);
};

// Check if feature includes in the url query, (url)?featureFlags=[featureName]
const isFeatureActivatedByUrl = (featureFlag: SupportedFeatureFlags): boolean => {
  const urlParams = new URLSearchParams(window.location.search);
  const featureParam = urlParams.get(featureFlagKey);

  if (featureParam) {
    const features = featureParam.split(',');
    return features.includes(featureFlag);
  }

  return false;
};

// Check if feature includes in local storage, featureFlags: ["featureName"]
const isFeatureActivatedByLocalStorage = (featureFlag: SupportedFeatureFlags): boolean => {
  const featureFlagsFromStorage = typedLocalStorage.getItem<string[]>(featureFlagKey) || [];
  return featureFlagsFromStorage.includes(featureFlag);
};

// Check if feature includes in session storage, featureFlags: ["featureName"]
const isFeatureActivatedBySessionStorage = (featureFlag: SupportedFeatureFlags): boolean => {
  const featureFlagsFromStorage = typedSessionStorage.getItem<string[]>(featureFlagKey) || [];
  return featureFlagsFromStorage.includes(featureFlag);
};

// Check if the feature should be persisted in session storage, (url)?persistFeatureFlag=true
const shouldPersistInSession = (): boolean => {
  const urlParams = new URLSearchParams(window.location.search);
  const shouldPersistInSession = urlParams.get(persistFeatureKey);
  return !!shouldPersistInSession;
};

// Add feature to session storage to persist the feature in the current session
const addFeatureFlagToSessionStorage = (featureFlag: SupportedFeatureFlags): void => {
  const featureFlagsFromStorage = typedSessionStorage.getItem<string[]>(featureFlagKey) || [];
  typedSessionStorage.setItem<string[]>(featureFlagKey, [...featureFlagsFromStorage, featureFlag]);
};

Разбиране на Кодекса

Дефиниции на характеристиките

В основата на внедряването са дефинициите на характеристиките. Типът SupportedFeatureFlags, който дефинира различните флагове на функции, които могат да се включват и изключват. Този тип гарантира, че само валидни имена на функции се използват в цялата кодова база.

Активни функции по подразбиране

Масивът defaultActiveFeatures съдържа функциите, които са активни по подразбиране в производствената среда. Това гарантира, че функцията е включена по подразбиране и е достъпна за крайните потребители без активиране на флаг за функция. Това е чудесно, когато искаме да внедрим нещо като бета версия и да можем лесно да го изключим, ако нещо лошо се случи в производството.

Функция shouldDisplayFeature

Основната функция, която се експортира от featureToggleUtils.ts, е отговорна за определянето дали дадена функция трябва да бъде показана, е shouldDisplayFeature. Тази функция разглежда поредица от условия, за да реши дали дадена функция трябва да бъде активна. Тези условия включват:

  1. Активиране по подразбиране: Ако функцията е в масива defaultActiveFeatures, тя се счита за активна по подразбиране и е налична за всички потребители в производството, без да е необходимо да се задава флаг на функцията. Това улеснява разработчиците да включат функция, но в същото време тя може лесно да бъде изключена, ако е необходимо връщане назад.
  2. Параметри на заявката на URL адреса: Ако функцията е включена в параметрите на заявката на URL адреса, тя се активира въз основа на параметъра на заявката. Следният URL е пример за това как можете да активирате myAwesomeFeature,yourdomain.com?featureFlag=myAwesomeFeature.
  3. Съхранение на сесията: Ако persistFeatureFlag параметърът на заявката е зададен, функцията се съхранява в хранилището на сесията, за да продължи на страниците в една и съща сесия. Това е чудесно, ако потребителите трябва да навигират между страници и искаме функцията да се активира при изгледи на страници. URL може да изглежда така yourdomain.com?featureFlag=myAwesomeFeature&persistFeatureFlag=true . Това улеснява споделянето на нови функции с нетехнически лица, за да им позволи да тестват и дават обратна връзка за функцията.
  4. Локално хранилище:За разработчиците може да е трудно винаги да манипулират или URL адрес за активиране на функция. След това разработчиците могат да добавят флага на функцията в локалното хранилище, за да гарантират, че функцията е във всяка сесия. Това може да стане чрез добавяне на двойка ключ-стойност, featureFlags: ["myAwesomeFeature"] към локално хранилище.

Проверки за активиране на функции

Както можете да видите, кодът по-горе има четири метода, за да разберете дали флагът на функцията е включен или не. Тези методи са:

  • isFeatureActivatedByDefault: Тази функция проверява дали функцията е активирана от набора от функции по подразбиране, defaultActiveFeatures.
  • isFeatureActivatedByUrl: Тази функция проверява дали функцията е активирана въз основа на параметрите на заявката в URL адреса.yourdomain.com?featureFlag=myAwesomeFeature
  • isFeatureActivatedByLocalStorage:Тази функция проверява дали функцията е активирана въз основа на стойности, съхранени в локално хранилище.
  • isFeatureActivatedBySessionStorage: Тази функция проверява дали функцията е активирана въз основа на стойности, съхранени в хранилището на сесията.

Устойчивост на съхранението на сесията

Кодът обработва постоянството на съхранение на сесията за функции, които се превключват с помощта на функцията shouldPersistInSession. Ако persistFeatureFlag параметърът на заявката присъства, addFeatureFlagToSessionStorage функцията се извиква, за да съхрани функцията в хранилището на сесията. Това е чудесна функция, ако искате да споделите една или повече функции с някого, които трябва да се запазят през цялата потребителска сесия.

Пример за използване в React

Кодът по-долу демонстрира колко лесно можем да скрием нещата зад флага на функцията. За да можете да изобразите ‹MyAwesomeFeatureComponent/›, флагът на функцията myAwesomeFeature трябва да бъде активиран в URL адреса, сесията/локалното хранилище или да бъде предоставен в activatedByDefaultArray.

import { shouldDisplayFeature } from '@utils/featureToggling';

const MyPage = () => {
  return (
   <>
    <h1>Hello</h1>
    {shouldDisplayFeature('myAwesomeFeature') && <MyAwesomeFeatureComponent/>}
   </> 
 )
}

Заключение

Превключването на функции е мощна техника, която дава възможност на разработчиците да въвеждат нови функции, да извършват A/B тестове и да отговарят на промените в изискванията на потребителите, без да прекъсват цялото приложение. Кодът по-горе осигурява солидна основа за внедряване на превключване на функции в CSR приложения. Като разберете и разширите този код, можете да създадете динамично и адаптивно приложение, което се грижи за развиващите се нужди на вашите потребители. Не забравяйте, че НЕ трябва да включвате превключващи тайни функции от страна на клиента. Всичко, което се случва от страна на клиента, е достъпно за потребителя. Това означава, че технически лица могат да бъдат на разположение, за да намерят вашите флагове за функции и да ги включат. Ако имате функции, които трябва да бъдат тайни до датата на пускане на пазара, те трябва да бъдат превключени на функцията в бекенда.

На обикновен английски

Благодарим ви, че сте част от нашата общност! Преди да тръгнете: