Udostępnij za pomocą


Tworzenie aplikacji React w programie Visual Studio

W tym samouczku utworzysz interfejs użytkownika React dla aplikacji internetowej listy to-do za pomocą języka JavaScript i Visual Studio 2022. Kod tej aplikacji można znaleźć na stronie ToDoJSWebApp.

Warunki wstępne

Upewnij się, że zainstalowano następujące elementy:

Tworzenie aplikacji React ToDo List

  1. W programie Visual Studio wybierz pozycję Plik > Nowy > Projekt, aby otworzyć okno dialogowe Tworzenie nowego projektu, wybierz szablon React App JavaScript, a następnie wybierz Dalej.

    Zrzut ekranu przedstawiający wybieranie szablonu.

  2. Nadaj projektowi nazwę TodoWebApp i wybierz pozycję Utwórz.

    Spowoduje to utworzenie projektu JavaScript przy użyciu narzędzia wiersza polecenia vite.

  3. W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy folder src i wybierz pozycję Dodaj > Nowy folder. i utwórz nowy folder o nazwie components.

    Jest to wspólna konwencja umieszczania składników w folderze components, ale nie jest to wymagane.

  4. Kliknij prawym przyciskiem myszy nowy folder, wybierz pozycję Dodaj > nowy element, a następnie wybierz pozycję Plik składnika React JSX w oknie dialogowym, nadaj mu TodoListnazwę , a następnie kliknij przycisk Dodaj. Jeśli nie widzisz listy szablonów elementów, wybierz pozycję Pokaż wszystkie szablony.

    Zrzut ekranu przedstawiający dodawanie składnika JSX.

    Spowoduje to utworzenie nowego pliku JSX w folderze components.

  5. Otwórz składnik TodoList i zastąp zawartość domyślną następującym kodem:

    function TodoList() {
      return (
        <h2>TODO app contents</h2>
      );
    }
    export default TodoList;
    

    Ten składnik wyświetla nagłówek, który zostanie zastąpiony później.

    Następnie skonfiguruj ten składnik w aplikacji. App.jsx jest głównym składnikiem, który jest ładowany, który reprezentuje aplikację listy to-do. Ten składnik jest używany w pliku main.jsx.

  6. W Eksploratorze rozwiązań otwórz App.jsx, usuń wszystkie importy z góry i wyczyść zawartość instrukcji return. Plik powinien wyglądać podobnie do poniższego.

    function App() {
      return (
        <>
          <TodoList />
        </>
      );
    }
    export default App;
    
  7. Aby dodać składnik TodoList, umieść kursor wewnątrz fragmentu, a następnie wpisz <TodoL RETURN lub <TodoL TAB. Spowoduje to dodanie składnika. Może również automatycznie dodać instrukcję importu.

    Zrzut ekranu przedstawiający dodawanie składnika JSX do aplikacji.

    Jeśli instrukcja importu nie zostanie dodana automatycznie, dodaj ją na początku pliku, wpisując import TodoL i naciskając TAB .

    Następnie wyczyść pliki CSS.

  8. Otwórz App.css i usuń całą zawartość.

  9. Otwórz Index.css i usuń całą zawartość z wyjątkiem stylów :root:

    :root {
      font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
      line-height: 1.5;
      font-weight: 400;
      color-scheme: light dark;
      color: rgba(255, 255, 255, 0.87);
      background-color: #242424;
    }
    

Uruchamianie aplikacji

Wybierz przycisk Rozpocznij debugowanie na pasku narzędzi lub naciśnij skrót klawiaturowy F5.

Aplikacja zostanie otwarta w oknie przeglądarki.

Zrzut ekranu przedstawiający aplikację uruchomioną w przeglądarce.

Dodawanie funkcji listy to-do do aplikacji

Możesz pozostawić uruchomioną aplikację. Podczas wprowadzania zmian aplikacja jest automatycznie odświeżana z najnowszą zawartością dzięki obsłudze hot module replacement Vite. Niektóre akcje, takie jak dodawanie folderów lub zmienianie nazw plików, wymagają zatrzymania debugowania, a następnie ponownego uruchomienia aplikacji, ale ogólnie rzecz biorąc, możesz pozostawić ją uruchomioną w tle podczas opracowywania aplikacji. Otwórz składnik TodoList.jsx, aby można było go zdefiniować.

  1. W Eksploratorze rozwiązań otwórz TodoList.jsx i dodaj interfejs użytkownika wymagany do wyświetlania wpisów listy to-do i zarządzania nimi. Zastąp zawartość następującym kodem:

    function TodoList() {
      return (
        <div>
          <h1>TODO</h1>
          <div>
            <input type="text" placeholder="Enter a task" required aria-label="Task text" />
            <button className="add-button" aria-label="Add task">Add</button>
          </div>
          <ol id="todo-list">
            <p>existing tasks will be shown here</p>
          </ol>
        </div>
      );
    }
    export default TodoList;
    

    Powyższy kod dodaje pole wejściowe dla nowego zadania to-do i przycisk umożliwiający przesłanie danych wejściowych. Następnie podłączasz przycisk Dodaj. Użyj useState React hook, aby dodać dwie zmienne stanu, jedną dla zadania dodawanego, a drugą do przechowywania istniejących zadań. W tym samouczku zadania są przechowywane w pamięci, a nie w trwałej pamięci.

  2. Dodaj następującą instrukcję importu do TodoList.jsx, aby zaimportować useState.

    import { useState } from 'react'
    
  3. Następnie użyj tego haka, aby utworzyć zmienne stanu. Dodaj następujący kod w funkcji TodoList powyżej instrukcji return.

    const [tasks, setTasks] = useState(["Drink some coffee", "Create a TODO app", "Drink some more coffee"]);
    const [newTaskText, setNewTaskText] = useState("");
    

    Spowoduje to skonfigurowanie dwóch zmiennych, tasks i newTaskText, dla danych i dwóch funkcji, które można wywołać w celu zaktualizowania tych zmiennych, setTasks i setNewTasks. Gdy wartość zmiennej stanu zostanie zmieniona, platforma React automatycznie ponownie renderuje składnik.

    Jesteś prawie gotowy zaktualizować TodoList.jsx, aby pokazać to-do przedmioty jako listę, ale najpierw trzeba poznać ważną koncepcję React.

    W rozwiązaniu React po wyświetleniu listy elementów należy dodać klucz w celu unikatowego zidentyfikowania każdego elementu na liście. Ta funkcja została szczegółowo wyjaśniona w dokumentacji React w sekcji Rendering Lists, ale tutaj poznasz podstawy. Masz listę elementów to-do do wyświetlenia i musisz skojarzyć unikatowy klucz dla każdego elementu. Klucz dla każdego elementu nie powinien ulec zmianie i z tego powodu nie można użyć indeksu elementu w tablicy jako klucza. Potrzebujesz identyfikatora, który nie zmieni się przez cały okres istnienia tych wartości. Użyjesz randomUID(), aby utworzyć unikatowy identyfikator dla każdego elementu to-do.

  4. Utwórz TodoList.jsx przy użyciu identyfikatora UUID jako klucza dla każdego elementu to-do. Zaktualizuj todoList.jsx przy użyciu następującego kodu.

    import React, { useState } from 'react';
    
    const initialTasks = [
        { id: self.crypto.randomUUID(), text: 'Drink some coffee' },
        { id: self.crypto.randomUUID(), text: 'Create a TODO app' },
        { id: self.crypto.randomUUID(), text: 'Drink some more coffee' }
    ];
    
    function TodoList() {
        const [tasks, setTasks] = useState(initialTasks);
        const [newTaskText, setNewTaskText] = useState("");
    
        return (
            <article
                className="todo-list"
                aria-label="task list manager">
                <header>
                    <h1>TODO</h1>
                    <form className="todo-input" aria-controls="todo-list">
                        <input
                            type="text"
                            placeholder="Enter a task"
                            value={newTaskText} />
                        <button
                            className="add-button">
                            Add
                        </button>
                    </form>
                </header>
                <ol id="todo-list" aria-live="polite" aria-label="task list">
                    {tasks.map((task, index) =>
                        <li key={task.id}>
                            <span className="text">{task.text}</span>
                        </li>
                    )}
                </ol>
            </article>
        );
    }
    export default TodoList;
    

    Ponieważ wartości identyfikatorów są przypisywane poza funkcją TodoList, możesz mieć pewność, że wartości nie zmienią się, jeśli strona zostanie ponownie renderowana. Podczas próby użycia aplikacji w tym stanie zauważysz, że nie można wpisywać tekstu w pole wejściowe listy zadań. Jest to spowodowane tym, że element wejściowy jest powiązany z newTaskText, który został zainicjowany pustym ciągiem. Aby umożliwić użytkownikom dodawanie nowych zadań, musisz obsługiwać zdarzenie onChange w tej kontrolce. Należy również zaimplementować obsługę przycisku Dodaj.

  5. Dodaj wymagane funkcje bezpośrednio przed instrukcją return w funkcji TodoList.

    function handleInputChange(event) {
        setNewTaskText(event.target.value);
    }
    
    function addTask() {
        if (newTaskText.trim() !== "") {
            setTasks(t => [...t, { id: self.crypto.randomUUID(), text: newTaskText }]);
            setNewTaskText("");
        }
        event.preventDefault();
    }
    

    W funkcji handleInputChanged nowa wartość z pola wejściowego jest przekazywana za pośrednictwem event.target.value, a ta wartość jest używana do aktualizowania wartości zmiennej newTaskText przy użyciu setNewTaskText. W funkcji addTask dodaj nowe zadanie do listy istniejących zadań przy użyciu setTasks i ustaw identyfikator elementu jako nową wartość UUID. Zaktualizuj element wejściowy, aby uwzględnił onChange={handleInputChange} i zaktualizuj przycisk Dodaj, aby uwzględnić onClick={addTask}. Ten kod podłącza zdarzenie do funkcji obsługującej to zdarzenie. Po tym powinieneś móc dodać nowe zadanie do listy zadań. Nowe zadania są dodawane do dołu listy. Aby zwiększyć użyteczność tej aplikacji, należy dodać obsługę usuwania zadań i przenieść zadanie w górę lub w dół.

  6. Dodaj funkcje do obsługi usuwania, przenoszenia w górę i przenoszenia w dół, a następnie zaktualizuj znaczniki, aby wyświetlić przycisk odpowiadający każdej z tych akcji. Dodaj następujący kod w funkcji TodoList powyżej instrukcji return.

    function deleteTask(id) {
        const updatedTasks = tasks.filter(task => task.id != id);
        setTasks(updatedTasks);
    }
    
    function moveTaskUp(index) {
        if (index > 0) {
            const updatedTasks = [...tasks];
            [updatedTasks[index], updatedTasks[index - 1]] = [updatedTasks[index - 1], updatedTasks[index]];
            setTasks(updatedTasks);
        }
    }
    
    function moveTaskDown(index) {
        if (index < tasks.length) {
            const updatedTasks = [...tasks];
            [updatedTasks[index], updatedTasks[index + 1]] = [updatedTasks[index + 1], updatedTasks[index]];
            setTasks(updatedTasks);
        }
    }
    

    Funkcja delete przyjmuje identyfikator zadania i usuwa tę z listy i używa metody Array filter(), aby utworzyć nową tablicę z wyłączeniem wybranego elementu, a następnie wywołuje setTasks(). Pozostałe dwie funkcje przyjmują indeks elementu, ponieważ działanie to jest specyficzne dla kolejności elementów. Zarówno moveTaskUp(), jak i moveTaskDown() używają destruktowania przypisania, aby zamienić wybrane zadanie z jego sąsiadem.

  7. Następnie zaktualizuj widok, aby uwzględnić te trzy przyciski. Zaktualizuj instrukcję return, aby zawierała następujące elementy.

    return (
        <article
            className="todo-list"
            aria-label="task list manager">
            <header>
                <h1>TODO</h1>
                <form className="todo-input" onSubmit={addTask} aria-controls="todo-list">
                    <input
                        type="text"
                        required
                        autoFocus
                        placeholder="Enter a task"
                        value={newTaskText}
                        aria-label="Task text"
                        onChange={handleInputChange} />
                    <button
                        className="add-button"
                        aria-label="Add task">
                        Add
                    </button>
                </form>
            </header>
            <ol id="todo-list" aria-live="polite">
                {tasks.map((task, index) =>
                    <li key={task.id}>
                        <span className="text">{task.text}</span>
                        <button className="delete-button" onClick={() => deleteTask(task.id)}>
                            🗑️
                        </button>
                        <button className="up-button" onClick={() => moveTaskUp(index)}>
                            ⇧
                        </button>
                        <button className="down-button" onClick={() => moveTaskDown(index)}>
                            ⇩
                        </button>
                    </li>
                )}
            </ol>
        </article>
    );
    

    Dodano przyciski potrzebne do wykonania wcześniej omówionych zadań. Używasz znaków Unicode jako ikon na przyciskach. W znaczniku dodano niektóre atrybuty, aby umożliwić późniejsze dodanie niektórych arkuszy CSS. Możesz również zauważyć użycie atrybutów aria , aby poprawić dostępność; są one opcjonalne, ale zdecydowanie zalecane. Jeśli uruchomisz aplikację, powinna ona wyglądać podobnie do poniższej ilustracji.

    Zrzut ekranu przedstawiający uruchomioną aplikację i wyświetloną listę

    Teraz powinno być możliwe wykonanie następujących czynności w aplikacji internetowej TODO.

    • Dodaj zadanie
    • Usuń zadanie
    • Przenieś zadanie w górę
    • Przenieś zadanie w dół

    Te funkcje działają, ale możesz zrefaktoryzować kod, aby zbudować składnik wielokrotnego użycia do wyświetlania elementów to-do. Znacznik dla elementu to-do trafia do nowego komponentu TodoItem. Ponieważ zarządzanie listą pozostaje w składniku Todo, można przekazać wywołania zwrotne do przycisków Usuń i Przenieś.

  8. Aby rozpocząć, naciśnij prawym przyciskiem myszy folder składników w Eksploratorze rozwiązań i wybierz Dodaj > Nowy Element.

  9. W wyświetlonym oknie dialogowym wybierz plik komponentu React JSX, nazwij go TodoItem, a następnie kliknij Dodaj.

  10. Dodaj następujący kod do elementu TodoItem.

    W tym kodzie przekazujesz zadanie i wywołania zwrotne jako właściwości tego nowego składnika.

    import PropTypes from 'prop-types';
    
    function TodoItem({ task, deleteTaskCallback, moveTaskUpCallback, moveTaskDownCallback }) {
        return (
            <li aria-label="task" >
                <span className="text">{task}</span>
                <button
                    type="button"
                    aria-label="Delete task"
                    className="delete-button"
                    onClick={() => deleteTaskCallback()}>
                    🗑️
                </button>
                <button
                    type="button"
                    aria-label="Move task up"
                    className="up-button"
                    onClick={() => moveTaskUpCallback()}>
                    ⇧
                </button>
                <button
                    type="button"
                    aria-label="Move task down"
                    className="down-button"
                    onClick={() => moveTaskDownCallback()}>
                    ⇩
                </button>
            </li>
        );
    }
    
    TodoItem.propTypes = {
        task: PropTypes.string.isRequired,
        deleteTaskCallback: PropTypes.func.isRequired,
        moveTaskUpCallback: PropTypes.func.isRequired,
        moveTaskDownCallback: PropTypes.func.isRequired,
    };
    
    export default TodoItem;
    

    Powyższy kod zawiera kod znaczników składnika Todo, a na końcu którego deklarujesz PropTypes. Props są używane do przekazywania danych z komponentu nadrzędnego do komponentów podrzędnych. Aby uzyskać więcej informacji na temat props, zobacz Przekazywanie props do składnika — React. Ponieważ funkcje usuwania i przenoszenia są przekazywane jako wywołania zwrotne, program obsługi onClick musi zostać zaktualizowany w celu wywołania tych wywołań zwrotnych.

  11. Dodaj wymagany kod. Pełny kod todoList, który używa składnika TodoItem, jest pokazany tutaj.

    import React, { useState } from 'react'
    import TodoItem from './TodoItem'
    
    const initialTasks = [
        { id: self.crypto.randomUUID(), text: 'Drink some coffee' },
        { id: self.crypto.randomUUID(), text: 'Create a TODO app' },
        { id: self.crypto.randomUUID(), text: 'Drink some more coffee' }
    ];
    
    function TodoList() {
        const [tasks, setTasks] = useState(initialTasks);
        const [newTaskText, setNewTaskText] = useState("");
        function handleInputChange(event) {
            setNewTaskText(event.target.value);
        }
        function addTask() {
            if (newTaskText.trim() !== "") {
                setTasks(t => [...t, { id: self.crypto.randomUUID(), text: newTaskText }]);
                setNewTaskText("");
            }
            event.preventDefault();
        }
        function deleteTask(id) {
            const updatedTasks = tasks.filter(task => task.id !== id);
            setTasks(updatedTasks);
        }
        function moveTaskUp(index) {
            if (index > 0) {
                const updatedTasks = [...tasks];
                [updatedTasks[index], updatedTasks[index - 1]] = [updatedTasks[index - 1], updatedTasks[index]];
                setTasks(updatedTasks);
            }
        }
        function moveTaskDown(index) {
            if (index < tasks.length) {
                const updatedTasks = [...tasks];
                [updatedTasks[index], updatedTasks[index + 1]] = [updatedTasks[index + 1], updatedTasks[index]];
                setTasks(updatedTasks);
            }
        }
        return (
            <article
                className="todo-list"
                aria-label="task list manager">
                <header>
                    <h1>TODO</h1>
                    <form onSubmit={addTask} aria-controls="todo-list">
                        <input
                            type="text"
                            required
                            placeholder="Enter a task"
                            value={newTaskText}
                            aria-label="Task text"
                            onChange={handleInputChange} />
                        <button
                            className="add-button"
                            aria-label="Add task">
                            Add
                        </button>
                    </form>
                </header>
                <ol id="todo-list" aria-live="polite">
                    {tasks.map((task, index) =>
                        <TodoItem
                            key={task.id}
                            task={task.text}
                            deleteTaskCallback={() => deleteTask(task.id)}
                            moveTaskUpCallback={() => moveTaskUp(index)}
                            moveTaskDownCallback={() => moveTaskDown(index)}
                        />
                    )}
                </ol>
            </article>
        );
    }
    
    export default TodoList;
    

    Teraz składnik TodoItem jest używany do renderowania każdego elementu to-do. Zwróć uwagę, że klucz jest ustawiony na wartość task.id, która zawiera wartość UUID dla tego zadania. Po uruchomieniu aplikacji nie powinno być widocznych żadnych zmian w wyglądzie ani zachowaniu aplikacji, ponieważ została zrefaktoryzowana do korzystania z TodoItem.

    Teraz, gdy wszystkie podstawowe funkcje są już obsługiwane, nadszedł czas, aby dodać trochę stylizacji, aby wyglądało to atrakcyjnie. Zacznij od dodania linku w Index.html dla rodziny czcionek Inter, który będzie używany dla tej aplikacji. W Index.htmlistnieją inne elementy, które należy wyczyścić. W szczególności tytuł powinien zostać zaktualizowany i chcesz zastąpić plik vite.svg, który jest obecnie używany jako ikona.

  12. Zaktualizuj Index.html przy użyciu następującej zawartości.

    <!doctype html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <link rel="icon" type="image/svg+xml" href="/checkmark-square.svg" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>TODO app</title>
            <link href='https://fonts.googleapis.com/css?family=Inter' rel='stylesheet'>
            <script type="module" defer src="/src/main.jsx"></script>
        </head>
        <body>
        </body>
    </html>
    
  13. Edytuj plik main.jsx, aby podczas wywoływania rootzastąpić maincreateRoot.

    Pełny kod dla main.jsx jest pokazany tutaj.

    import { StrictMode } from 'react'
    import { createRoot } from 'react-dom/client'
    import App from './App.jsx'
    import './index.css'
    
    createRoot(document.querySelector('main')).render(
        <StrictMode>
            <App />
        </StrictMode>,
    )
    

    Oprócz tych zmian plik checkmark-square.svg został dodany do folderu publicznego. Jest to plik SVG z obrazu kwadratu znacznika wyboru FluentUI, który można pobrać bezpośrednio. (Istnieje pakiet, którego można użyć do bardziej zintegrowanego środowiska, ale wykracza poza zakres tego artykułu).

    Następnie zaktualizuj style składnika TodoList.

  14. W folderze components dodaj nowy plik CSS o nazwie TodoList.css. Możesz kliknąć prawym przyciskiem myszy projekt i wybrać pozycję Dodaj > Nowy Element, a następnie wybrać arkusz stylów. Nadaj plikowi nazwę TodoList.css.

  15. Dodaj następujący kod do TodoList.css.

    .todo-list {
        background-color: #1e1e1e;
        padding: 1.25rem;
        border-radius: 0.5rem;
        box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.3);
        width: 100%;
        max-width: 25rem;
    }
    
    .todo-list h1 {
        text-align: center;
        color: #e0e0e0;
    }
    
    .todo-input {
        display: flex;
        justify-content: space-between;
        margin-bottom: 1.25rem;
    }
    
    .todo-input input {
        flex: 1;
        padding: 0.625rem;
        border: 0.0625rem solid #333;
        border-radius: 0.25rem;
        margin-right: 0.625rem;
        background-color: #2c2c2c;
        color: #e0e0e0;
    }
    
    .todo-input .add-button {
        padding: 0.625rem 1.25rem;
        background-color: #007bff;
        color: #fff;
        border: none;
        border-radius: 0.25rem;
        cursor: pointer;
    }
    
    .todo-input .add-button:hover {
        background-color: #0056b3;
    }
    
    .todo-list ol {
        list-style-type: none;
        padding: 0;
    }
    
    .todo-list li {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0.625rem;
        border-bottom: 0.0625rem solid #333;
    }
    
    .todo-list li:last-child {
        border-bottom: none;
    }
    
    .todo-list .text {
        flex: 1;
    }
    
    .todo-list li button {
        background: none;
        border: none;
        cursor: pointer;
        font-size: 1rem;
        margin-left: 0.625rem;
        color: #e0e0e0;
    }
    
    .todo-list li button:hover {
        color: #007bff;
    }
    
    .todo-list li button.delete-button {
        color: #ff4d4d;
    }
    
    .todo-list li button.up-button,
    .todo-list li button.down-button {
        color: #4caf50;
    }
    
  16. Następnie zmodyfikuj TodoList.jsx, aby dodać następujący import w górnej części pliku.

    import './TodoList.css';
    
  17. Odśwież przeglądarkę po zapisaniu zmian. Powinno to poprawić styl aplikacji. Aplikacja powinna wyglądać podobnie do poniższej.

    Zrzut ekranu przedstawiający ostateczną wersję uruchomionej aplikacji.

    Teraz utworzono działającą aplikację listy to-do, która przechowuje elementy to-do w pamięci. Od tego momentu możesz zaktualizować aplikację, aby przechowywać elementy to-do w localStorage/IndexedDb, lub zintegrować ją z bazą danych po stronie serwera albo innym zapleczem dla bardziej trwałego przechowywania.

Streszczenie

W tym samouczku utworzyłeś nową aplikację React przy użyciu programu Visual Studio. Aplikacja składa się z listy to-do, która obejmuje obsługę dodawania zadań, usuwania zadań i zmieniania ich kolejności. Stworzyłeś dwa nowe komponenty React i używałeś ich przez cały ten samouczek.

Zasoby