Расширения Chrome - отличный способ улучшить рабочий процесс пользователя. Они сокращают переключение контекста и упрощают работу разработчика. Вот как сделать их в React.

- Создание приложения React
- Создание расширения
- Добавление функциональности
- Добавление в Интернет-магазин Google Chrome

Создание приложения React

Первое, что вам нужно сделать, это создать приложение React. Чтобы упростить этот процесс, мы будем использовать create-react-app в сочетании с шаблоном Typescript. В этом руководстве мы создадим простое расширение для игры в кости. Не стесняйтесь создавать свои собственные, просто обратите внимание на имена файлов, имена переменных и другие именованные параметры.

npx create-react-app roll-dice-extension --template typescript

Делаем расширение

После создания приложения вы увидите знакомую структуру папок. Перейдите в свою папку public и обновите config.yaml. Это файл, в котором указываются имя приложения, описание, значки и другие параметры, используемые Chrome.

{
  "short_name": "Roll Dice",
  "name": "A simple extension for rolling dice",
  "icons": {
    "16": "/logo16.png",
    "32": "/logo32.png",
    "48": "/logo48.png",
    "128": "/logo128.png"
  },
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "index.html"
  }
}

Перед сборкой и загрузкой расширения в Chrome вам необходимо обновить package.json скрипт сборки. Замените его следующим.

"build": "set \"INLINE_RUNTIME_CHUNK=false\" && react-scripts build"

Это просто удаляет небезопасный встроенный код Javascript из вашего приложения React, что запрещено в расширениях Chrome. Теперь, наконец, расширение можно создать, запустив npm run build и перейдя в Chrome, где вы должны ввести chrome://extensions в строке URL. Это приведет вас на страницу расширений, где можно загрузить расширение. Для этого вы должны сначала включить режим разработчика с помощью переключателя в правом верхнем углу, а затем нажать кнопку с надписью «Загрузить распакованный» и загрузить расширение, выбрав папку build в вашем проекте.

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

Если вы хотите проверить расширение в производстве, не стесняйтесь проверить Math Embed - мое расширение для простого встраивания математических уравнений в Medium. В противном случае следуйте инструкциям, чтобы увидеть, как добавить базовую функциональность, или создайте свою собственную. (Github для встраивания математики)

Добавление функциональности

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

Запустите npm run start, чтобы запустить сервер разработки. Вы должны увидеть то же приложение React, что и в расширении, только большего размера. Нам ничего из этого не нужно, поэтому просто удалите весь код tsx в App.tsx.

//App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
  return (
    
  );
}
export default App;

Чтобы облегчить себе жизнь, мы установим Material UI npm install @material-ui/core и создадим базовую тему и макет в нашем App.tsx файле.

//App.tsx
import {
  Box,
  Button,
  createMuiTheme,
  ThemeProvider,
  Typography,
} from "@material-ui/core";
import { useState } from "react";
import "./App.css";
import RollDiceButton from "./RollDiceButton";
import RollDisplay from "./RollDisplay";
const theme = createMuiTheme({
  typography: {
    allVariants: {
      color: "#FFFFFF",
    },
    fontFamily: [
      "Poppins",
      "sans-serif",
      '"Apple Color Emoji"',
      '"Segoe UI Emoji"',
      '"Segoe UI Symbol"',
    ].join(","),
    fontSize: 14,
    h1: {
      fontSize: 24,
      fontWeight: 700,
    },
  },
});
function App() {
return (
    <ThemeProvider theme={theme}>
      <Box
        height="100%"
        flex={1}
        display="flex"
        justifyContent="center"
        alignItems="center"
        flexDirection="column"
        bgcolor="#023047"
      >
      </Box>
    </ThemeProvider>
  );
}
export default App;

Разобравшись с этим, мы можем взглянуть на остальные функции нашего приложения. Мы будем разрабатывать:

  • Базовое отображение всех выполненных нами бросков
  • Сумма всех бросков
  • Кнопки для увеличения и уменьшения количества брошенных кубиков
  • И кнопка для бросания кубиков.

Для реализации этих функций мы разработаем следующее:

  • Государства, в которых проводится подсчет кубиков и отдельные броски
  • RollDiceButton: кнопка, которая принимает количество кубиков в качестве входных данных и возвращает отдельные броски.
  • RollDisplay: отображение отдельных рулонов и их суммы
  • Кнопки для увеличения или уменьшения количества кубиков

Мы начинаем с определения двух состояний в App.tsx: одно, в котором хранятся отдельные значения броска игральных костей, и другое, в котором хранится текущее количество костей, которые мы бросаем.

const [diceRolls, setDiceRolls] = useState<number[]>([]);
const [count, setCount] = useState(2);

Затем мы создадим новый файл для кнопки бросания кубиков RollDiceButton.tsx. Кнопка будет принимать count как счетчик кубиков и setDiceRolls, который установит состояние в родительском компоненте.

Мы также добавим простые функции для генерации бросков кубиков getDiceRolls() и getDiceRolls(count: number), которые будут запускаться при нажатии кнопки.

import { Box, Button } from "@material-ui/core";
import React, { useEffect } from "react";
interface RollDiceButtonProps {
  count: number;
  setDiceRolls: React.Dispatch<React.SetStateAction<number[]>>;
}
const getDiceRoll = () => {
  return Math.ceil(Math.random() * 6);
};
const getDiceRolls = (count: number) => {
  var rolls: number[] = [];
  for (var i = 0; i < count; i++) {
    rolls.push(getDiceRoll());
  }
  return rolls;
};
const RollDiceButton: React.FC<RollDiceButtonProps> = (props) => {
  useEffect(() => {
    props.setDiceRolls(getDiceRolls(props.count));
  }, [props.count]);
  return (
    <Box>
      <Button
        variant="contained"
        color="primary"
        onClick={() => props.setDiceRolls(getDiceRolls(props.count))}
      >
        Roll Dice
      </Button>
    </Box>
  );
};
export default RollDiceButton;

Во-вторых, мы создадим компонент отображения RollDisplay.tsx. Он будет принимать в качестве входных данных отдельные кубики diceRolls бросков и отображать их в структурированном виде, включая сумму бросков.

import { Box, Typography } from "@material-ui/core";
import React from "react";
interface RollDisplayProps {
  diceRolls: number[];
}
const RollDisplay: React.FC<RollDisplayProps> = (props) => {
  return (
    <Box
      display="flex"
      justifyContent="center"
      alignItems="center"
      flexDirection="column"
    >
      <Box display="flex">
        {props.diceRolls.map((roll) => (
          <Box padding={1}>
            <Typography>{roll}</Typography>
          </Box>
        ))}
      </Box>
      <Box padding={1}>
        <Typography variant="h1">
          {props.diceRolls.reduce((acc, value) => acc + value)}
        </Typography>
      </Box>
    </Box>
  );
};
export default RollDisplay;

Наконец, мы добавляем их к App.tsx. Мы импортируем необходимые компоненты и включаем их в ThemeProvider.

import {
  Box,
  Button,
  createMuiTheme,
  ThemeProvider,
  Typography,
} from "@material-ui/core";
import { useState } from "react";
import "./App.css";
import RollDiceButton from "./RollDiceButton";
import RollDisplay from "./RollDisplay";
const theme = createMuiTheme({
  typography: {
    allVariants: {
      color: "#FFFFFF",
    },
    fontFamily: [
      "Poppins",
      "sans-serif",
      '"Apple Color Emoji"',
      '"Segoe UI Emoji"',
      '"Segoe UI Symbol"',
    ].join(","),
    fontSize: 14,
    h1: {
      fontSize: 24,
      fontWeight: 700,
    },
  },
});
function App() {
  const [diceRolls, setDiceRolls] = useState<number[]>([0, 0]);
  const [count, setCount] = useState(2);
return (
    <ThemeProvider theme={theme}>
      <Box
        height="100%"
        flex={1}
        display="flex"
        justifyContent="center"
        alignItems="center"
        flexDirection="column"
        bgcolor="#006d77"
        padding="16px"
      >
        <Box
          display="flex"
          justifyContent="center"
          alignItems="center"
          flexDirection="row"
        >
          <Button onClick={() => setCount(count > 1 ? count - 1 : 1)}>
            <Typography>-</Typography>
          </Button>
          <RollDisplay diceRolls={diceRolls}></RollDisplay>{" "}
          <Button onClick={() => setCount(count + 1)}>
            <Typography>+</Typography>
          </Button>
        </Box>
<RollDiceButton
          setDiceRolls={setDiceRolls}
          count={count}
        ></RollDiceButton>
      </Box>
    </ThemeProvider>
  );
}
export default App;

Вот и все, что касается простого расширения. Если вы где-то заблудились, не стесняйтесь заглянуть в репозиторий Github для этого проекта. Обновите его в chrome://extensions и проверьте.

Добавление расширения в Интернет-магазин Google Chrome

Перед публикацией вы можете дважды проверить правильность работы расширения. Если все в порядке, вы готовы опубликовать его через Консоль разработчика Chrome Webstore.

Зарегистрируйтесь или войдите в консоль, нажмите + Новый элемент в правом верхнем углу, и вам будет предложено ввести файл .zip. Просто заархивируйте buildпапку вашего проекта и поместите ее в свой браузер.

Некоторая информация будет предварительно заполнена из вашего manifest.json файла. Остальное зависит от тебя. После того, как все будет заполнено, загорится кнопка «Отправить на рассмотрение», и вы сможете отправить свое расширение на рассмотрение. Если он пройдет, вы увидите, что он добавлен в Интернет-магазин Chrome.

Поздравляю. Вы добрались до конца, и ваше расширение скоро появится в Интернет-магазине.

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