就业培训     下载中心     Wiki     联络
登录   注册

Log
  1. 首页
  2. 学习 Web 开发
  3. Tools and testing
  4. 理解客户端侧 JavaScript 框架
  5. React interactivity: Events and state

内容表

  • Handling events
  • Callback props
  • State and the useState hook
  • Putting it all together: Adding a task
  • Detour: counting tasks
  • Completing a task
  • Deleting a task
  • Deleting tasks from state and UI
  • 摘要
  • In this module

React interactivity: Events and state

  • 上一
  • Overview: Client-side JavaScript frameworks
  • 下一

With our component plan worked out, it's now time to start updating our app from a completely static UI to one that actually allows us to interact and change things. In this article we'll do this, digging into events and state along the way, and ending up with an app in which we can successfully add and delete tasks, and toggle tasks as completed.

Prerequisites: Familiarity with the core HTML , CSS ,和 JavaScript languages, knowledge of the terminal/command line .
Objective: To learn about handling events and state in React, and use those to start making the case study app interactive.

Handling events

If you've only written vanilla JavaScript before now, you might be used to having a separate JavaScript file, where you query for some DOM nodes and attach listeners to them. For example:

const btn = document.querySelector('button');
btn.addEventListener('click', () => {
  alert("hi!");
});

								

In React, we write event handlers directly on the elements in our JSX, like this:

<button
  type="button"
  onClick={() => alert("hi!")}
>
  Say hi!
</button>

								

注意: This may seem counter-intuitive regarding best-practice advice that tends to advise against use of inline event handlers on HTML, but remember that JSX is actually part of your JavaScript.

In the above example, we're adding an onClick 属性到 <button> element. The value of that attribute is a function that triggers a simple alert.

onClick attribute has special meaning here: it tells React to run a given function when the user clicks on the button. There are a couple of other things to note:

  • The camel-cased nature of onClick is important — JSX will not recognize onclick (again, it is already used in JavaScript for a specific purpose, which is related but different — standard onclick handler properties).
  • All browser events follow this format in JSX – on , followed by the name of the event.

Let's apply this to our app, starting in the Form.js 组件。

Handling form submission

At the top of the Form() component function, create a function named handleSubmit() . This function should prevent the default behavior of the submit event . After that, it should trigger an alert() , which can say whatever you’d like. It should end up looking something like this:

function handleSubmit(e) {
  e.preventDefault();
  alert('Hello, world!');
}

								

To use this function, add an onSubmit 属性到 <form> element, and set its value to the handleSubmit 函数:

<form onSubmit={handleSubmit}>

								

Now if you head back to your browser and click on the "Add" button, your browser will show you an alert dialog with the words "Hello, world!" — or whatever you chose to write there.

Callback props

In React applications, interactivity is rarely confined to just one component: events that happen in one component will affect other parts of the app. When we start giving ourselves the power to make new tasks, things that happen in the <Form /> component will affect the list rendered in <App /> .

We want our handleSubmit() function to ultimately help us create a new task, so we need a way to pass information from <Form /> to <App /> . We can't pass data from child to parent in the same way as we pass data from parent to child using standard props. Instead, we can write a function in <App /> that will expect some data from our form as an input, then pass that function to <Form /> as a prop. This function-as-a-prop is called a callback prop. Once we have our callback prop, we can call it inside <Form /> to send the right data to <App /> .

Handling form submission via callbacks

Inside the top of our App() component function, create a function named addTask() which has a single parameter of 名称 :

function addTask(name) {
  alert(name);
}

								

Next, we'll pass addTask() into <Form /> as a prop. The prop can have whatever name you want, but pick a name you’ll understand later. Something like addTask works, because it matches the name of the function as well as what the function will do. Your <Form /> component call should be updated as follows:

<Form addTask={addTask} />

								

Finally, you can use this prop inside the handleSubmit() function in your <Form /> component! Update it as follows:

function handleSubmit(e) {
  e.preventDefault();
  props.addTask("Say hello!");
}

								

Clicking on the "Add" button in your browser will prove that the addTask() callback function works, but it'd be nice if we could get the alert to show us what we're typing in our input field! This is what we'll do next.

注意: We decided to name our callback prop addTask to make it easy to understand what the prop will do. Another common convention you may well come across in React code is to prefix callback prop names with the word on , followed by the name of the event that will cause them to be run. For instance, we could have given our form a prop of onSubmit 采用值的 addTask .

State and the useState hook

So far, we've used props to pass data through our components and this has served us just fine. Now that we're dealing with user input and data updates, however, we need something more.

For one thing, props come from the parent of a component. Our <Form /> will not be inheriting a new name for our task; our <input />   element lives directly inside of <Form /> ,所以 <Form/> will be directly responsible for creating that new name. We can't ask <Form /> to spontaneously create its own props, but we can ask it to track some of its own data for us. Data such as this, which a component itself owns, is called state . State is another powerful tool for React because components not only own state, but can update it later. It's not possible to update the props a component receives; only to read them.

React provides a variety of special functions that allow us to provide new capabilities to components, like state. These functions are called hooks ,和 useState hook, as its name implies, is precisely the one we need in order to give our component some state.

To use a React hook, we need to import it from the react module. In Form.js , change your very first line so that it reads like this:

import React, { useState } from "react";

								

This allows us to import the useState() function by itself, and utilize it anywhere in this file.

useState() creates a piece of state for a component, and its only parameter determines the initial value of that state. It returns two things: the state, and a function that can be used to update the state later.

This is a lot to take in at once, so let's try it out. We're going to make ourselves a 名称 state, and a function for updating the 名称 状态。

Write the following above your handleSubmit() function, inside Form() :

const [name, setName] = useState('Use hooks!');

								

What's going on in this line of code?

  • We are setting the initial 名称 value as "Use hooks!".
  • We are defining a function whose job is to modify 名称 , called setName() .
  • useState() returns these two things, so we are using array destructuring to capture them both in separate variables.

Reading state

You can see the 名称 state in action right away. Add a value attribute to the form’s input, and set its value to 名称 . Your browser will render "Use hooks!" inside the input.

<input
  type="text"
  id="new-todo-input"
  className="input input__lg"
  name="text"
  autoComplete="off"
  value={name}
/>

								

Change "Use hooks!" to an empty string once you're done; this is what we want for our initial state.

const [name, setName] = useState('');

								

Reading user input

Before we can change the value of 名称 , we need to capture a user's input as they type. For this, we can listen to the onChange event. Let’s write a handleChange() function, and listen for it on the <input /> 标签。

// near the top of the `Form` component
function handleChange(e) {
  console.log("Typing!");
}
// Down in the return statement
<input
  type="text"
  id="new-todo-input"
  className="input input__lg"
  name="text"
  autoComplete="off"
  value={name}
  onChange={handleChange}
/>

								

Currently, your input’s value will not change as you type, but your browser will log the word "Typing!" to the JavaScript console, so we know our event listener is attached to the input. In order to change the input’s value, we have to use our handleChange() function to update our 名称 状态。

To read the contents of the input field as they change, you can access the input’s value property. We can do this inside handleChange() by reading e.target.value . e.target represents the element that fired the change event — that’s our input. So, value is the text inside it.

You can console.log() this value to see it in your browser’s console.

function handleChange(e) {
  console.log(e.target.value);
}

								

Updating state

Logging isn’t enough — we want to actually store the updated state of the name as the input value changes! Change the console.log() to setName() , as shown below:

function handleChange(e) {
  setName(e.target.value);
}

								

Now we need to change our handleSubmit() function so that it calls props.addTask with name as an argument — remember our callback prop? This will serve to send the task back to the App component, so we can add it to our list of tasks at some later date. As a matter of good practice, you should clear the input after your form submits, so we'll call setName() again with an empty string to do so:

function handleSubmit(e) {
  e.preventDefault();
  props.addTask(name);
  setName("");
}

								

At last, you can type something into the input field in your browser and click 添加 — whatever you typed will appear in an alert dialog.

您的 Form.js file should now read like this:

import React, { useState } from "react";
function Form(props) {
  const [name, setName] = useState("");
  function handleChange(e) {
    setName(e.target.value);
  }
  function handleSubmit(e) {
    e.preventDefault();
    props.addTask(name);
    setName("");
  }
  return (
    <form onSubmit={handleSubmit}>
      <h2 className="label-wrapper">
        <label htmlFor="new-todo-input" className="label__lg">
          What needs to be done?
        </label>
      </h2>
      <input
        type="text"
        id="new-todo-input"
        className="input input__lg"
        name="text"
        autoComplete="off"
        value={name}
        onChange={handleChange}
      />
      <button type="submit" className="btn btn__primary btn__lg">
        添加
      </button>
    </form>
  );
}
export default Form;

								

注意: One thing you'll notice is that you are able to submit empty tasks by just pressing the Add button without entering a task name. Can you think of a way to disallow empty tasks from being added? As a hint, you probably need to add some kind of check into the handleSubmit() 函数。

Putting it all together: Adding a task

Now that we've practiced with events, callback props, and hooks we're ready to write functionality that will allow a user to add a new task from their browser.

Tasks as state

Import useState into App.js , so that we can store our tasks in state — update your React import line to the following:

import React, { useState } from "react";

								

We want to pass props.tasks 到 useState() hook – this will preserve its initial state. Add the following right at the top of your App() function definition:

const [tasks, setTasks] = useState(props.tasks);

								

Now, we can change our taskList mapping so that it is the result of mapping tasks ,而不是 props.tasks . Your taskList constant declaration should now look like so:

const taskList = tasks.map(task => (
    <Todo
        id={task.id}
        name={task.name}
        completed={task.completed}
        key={task.id}
      />
    )
  );

								

Adding a task

We’ve now got a setTasks hook that we can use in our addTask() function to update our list of tasks. There’s one problem however: we can’t just pass the 名称 自变量 addTask() into setTasks ,因为 tasks is an array of objects and 名称 is a string. If we tried to do this, the array would be replaced with the string.

First of all, we need to put 名称 into an object that has the same structure as our existing tasks. Inside of the addTask() function, we will make a newTask object to add to the array.

We then need to make a new array with this new task added to it and then update the state of the tasks data to this new state. To do this, we can use spread syntax to copy the existing array , and add our object at the end. We then pass this array into setTasks() to update the state.

Putting that all together, your addTask() function should read like so:

function addTask(name) {
  const newTask = { id: "id", name: name, completed: false };
  setTasks([...tasks, newTask]);
}

								

Now you can use the browser to add a task to our data! Type anything into the form and click "Add" (or press the Enter key) and you'll see your new todo item appear in the UI!

However, we have another problem : our addTask() function is giving each task the same id . This is bad for accessibility, and makes it impossible for React to tell future tasks apart with the key prop. In fact, React will give you a warning in your DevTools console — "Warning: Encountered two children with the same key..."

We need to fix this. Making unique identifiers is a hard problem – one for which the JavaScript community has written some helpful libraries. We’ll use nanoid because it's tiny, and it works.

Make sure you’re in the root directory of your application and run the following terminal command:

npm install nanoid

								

注意: If you're using yarn, you'll need the following instead: yarn add nanoid

Now we can import nanoid into the top of App.js so we can use it to create unique IDs for our new tasks. First of all, include the following import line at the top of App.js :

import { nanoid } from "nanoid";

								

Now let's update addTask() so that each task ID becomes a prefix todo- plus a unique string generated by nanoid. Update your newTask constant declaration to this:

const newTask = { id: "todo-" + nanoid(), name: name, completed: false };

								

Save everything, and try your app again — now you can add tasks without getting that warning about duplicate IDs.

Detour: counting tasks

Now that we can add new tasks, you may notice a problem: our heading reads 3 tasks remaining, no matter how many tasks we have! We can fix this by counting the length of taskList and changing the text of our heading accordingly.

Add this inside your App() definition, before the return statement:

const headingText = `${taskList.length} tasks remaining`;

								

Hrm. This is almost right, except that if our list ever contains a single task, the heading will still use the word “tasks”. We can make this a variable, too. Update the code you just added as follows:

const tasksNoun = taskList.length !== 1 ? 'tasks' : 'task';
const headingText = `${taskList.length} ${tasksNoun} remaining`;

								

Now you can replace the list heading's text content with the headingText variable. Update your <h2> 像这样:

<h2 id="list-heading">{headingText}</h2>

								

Completing a task

You might notice that, when you click on a checkbox, it checks and unchecks appropriately. As a feature of HTML, the browser knows how to remember which checkbox inputs are checked or unchecked without our help. This feature hides a problem, however: toggling a checkbox doesn't change the state in our React application. This means that the browser and our app are now out-of-sync. We have to write our own code to put the browser back in sync with our app.

Proving the bug

Before we fix the problem, let's observe it happening.

We'll start by writing a toggleTaskCompleted() function in our App() component. This function will have an id parameter, but we're not going to use it yet. For now, we'll log the first task in the array to the console – we're going to inspect what happens when we check or uncheck it in our browser:

Add this just above your taskList constant declaration:

function toggleTaskCompleted(id) {
  console.log(tasks[0])
}

								

Next, we'll add toggleTaskCompleted to the props of each <Todo /> component rendered inside our taskList ; update it like so:

const taskList = tasks.map(task => (
  <Todo
      id={task.id}
      name={task.name}
      completed={task.completed}
      key={task.id}
      toggleTaskCompleted={toggleTaskCompleted}
  />
));

								

Next, go over to your Todo.js component and add an onChange handler to your <input /> element, which should use an anonymous function to call props.toggleTaskCompleted() with a parameter of props.id 。 <input /> should now look like this:

<input
  id={props.id}
  type="checkbox"
  defaultChecked={props.completed}
  onChange={() => props.toggleTaskCompleted(props.id)}
/>

								

Save everything and return to your browser and notice that our first task, Eat, is checked. Open your JavaScript console, then click on the checkbox next to Eat. It unchecks, as we expect. Your JavaScript console, however, will log something like this:

Object { id: "task-0", name: "Eat", completed: true }

								

The checkbox unchecks in the browser, but our console tells us that Eat is still completed. We will fix that next!

Synchronizing the browser with our data

Let’s revisit our toggleTaskCompleted() 函数在 App.js . We want it to change the completed property of only the task that was toggled, and leave all the others alone. To do this, we'll map() over the task list and just change the one we completed.

Update your toggleTaskCompleted() function to the following:

function toggleTaskCompleted(id) {
  const updatedTasks = tasks.map(task => {
    // if this task has the same ID as the edited task
    if (id === task.id) {
      // use object spread to make a new object
      // whose `completed` prop has been inverted
      return {...task, completed: !task.completed}
    }
    return task;
  });
  setTasks(updatedTasks);
}

								

Here, we define an updatedTasks constant that maps over the original tasks array.  If the task’s id property matches the id provided to the function, we use object spread syntax   to create a new object, and toggle the checked property of that object before returning it. If it doesn’t match, we return the original object.

Then we call setTasks() with this new array in order to update our state.

Deleting a task

Deleting a task will follow a similar pattern to toggling its completed state: We need to define a function for updating our state, then pass that function into <Todo /> as a prop and call it when the right event happens.

deleteTask callback prop

Here we'll start by writing a deleteTask() function in your App component. Like toggleTaskCompleted() , this function will take an id parameter, and we will log that id to the console to start with. Add the following below toggleTaskCompleted() :

function deleteTask(id) {
  console.log(id)
}

								

Next, add another callback prop to our array of <Todo /> components:

const taskList = tasks.map(task => (
  <Todo
    id={task.id}
    name={task.name}
    completed={task.completed}
    key={task.id}
    toggleTaskCompleted={toggleTaskCompleted}
    deleteTask={deleteTask}
  />
));

								

在 Todo.js , we want to call props.deleteTask() when the "Delete" button is pressed. deleteTask() needs to know the ID of the task that called it, so it can delete the correct task from the state.

Update the "Delete" button inside Todo.js, like so:

<button
  type="button"
  className="btn btn__danger"
  onClick={() => props.deleteTask(props.id)}
>
  Delete <span className="visually-hidden">{props.name}</span>
</button>

								

Now when you click on any of the "Delete" buttons in the app, your browser console should log the ID of the related task.

Deleting tasks from state and UI

Now that we know deleteTask() is invoked correctly, we can call our setTasks() hook in deleteTask() to actually delete that task from the app’s state as well as visually in the app UI. Since setTasks() expects an array as an argument, we should provide it with a new array that copies the existing tasks, excluding the task whose ID matches the one passed into deleteTask() .

This is a perfect opportunity to use Array.prototype.filter() . We can test each task, and exclude a task from the new array if its id prop matches the id parameter passed into deleteTask() .

更新 deleteTask() function inside your App.js file as follows:

function deleteTask(id) {
  const remainingTasks = tasks.filter(task => id !== task.id);
  setTasks(remainingTasks);
}

								

Try your app out again. Now you should be able to delete a task from your app!

摘要

That's enough for one article. Here we've given you the lowdown on how React deals with events and handles state, and implemented functionality to add tasks, delete tasks, and toggle tasks as completed. We are nearly there. In the next article we'll implement functionality to edit existing tasks and filter the list of tasks between all, completed, and incomplete tasks. We'll look at conditional UI rendering along the way.

  • 上一
  • Overview: Client-side JavaScript frameworks
  • 下一

In this module

  • Introduction to client-side frameworks
  • Framework main features
  • React
    • Getting started with React
    • Beginning our React todo list
    • Componentizing our React app
    • React interactivity: Events and state
    • React interactivity: Editing, filtering, conditional rendering
    • Accessibility in React
    • React resources
  • Ember
    • Getting started with Ember
    • Ember app structure and componentization
    • Ember interactivity: Events, classes and state
    • Ember Interactivity: Footer functionality, conditional rendering
    • Routing in Ember
    • Ember resources and troubleshooting
  • Vue
    • Getting started with Vue
    • Creating our first Vue component
    • Rendering a list of Vue components
    • Adding a new todo form: Vue events, methods, and models
    • Styling Vue components with CSS
    • Using Vue computed properties
    • Vue conditional rendering: editing existing todos
    • Focus management with Vue refs
    • Vue resources
  • Svelte
    • Getting started with Svelte
    • Starting our Svelte Todo list app
    • Dynamic behavior in Svelte: working with variables and props
    • Componentizing our Svelte app
    • Advanced Svelte: Reactivity, lifecycle, accessibility
    • Working with Svelte stores
    • TypeScript support in Svelte
    • Deployment and next steps
  • Angular
    • Getting started with Angular
    • Beginning our Angular todo list app
    • Styling our Angular app
    • Creating an item component
    • Filtering our to-do items
    • Building Angular applications and further resources

发现此页面有问题吗?

  • 编辑在 GitHub
  • 源在 GitHub
  • Report a problem with this content on GitHub
  • 想要自己修复问题吗?见 我们的贡献指南 .

最后修改: Oct 8, 2021 , 由 MDN 贡献者

相关话题

  1. Complete beginners start here!
  2. Web 快速入门
    1. Getting started with the Web overview
    2. 安装基本软件
    3. What will your website look like?
    4. 处理文件
    5. HTML 基础
    6. CSS 基础
    7. JavaScript 基础
    8. 发布您的网站
    9. How the Web works
  3. HTML — Structuring the Web
  4. HTML 介绍
    1. Introduction to HTML overview
    2. Getting started with HTML
    3. What's in the head? Metadata in HTML
    4. HTML text fundamentals
    5. Creating hyperlinks
    6. Advanced text formatting
    7. Document and website structure
    8. Debugging HTML
    9. Assessment: Marking up a letter
    10. Assessment: Structuring a page of content
  5. 多媒体和嵌入
    1. Multimedia and embedding overview
    2. Images in HTML
    3. Video and audio content
    4. From object to iframe — other embedding technologies
    5. Adding vector graphics to the Web
    6. Responsive images
    7. Assessment: Mozilla splash page
  6. HTML 表格
    1. HTML tables overview
    2. HTML table basics
    3. HTML Table advanced features and accessibility
    4. Assessment: Structuring planet data
  7. CSS — Styling the Web
  8. CSS 第一步
    1. CSS first steps overview
    2. What is CSS?
    3. Getting started with CSS
    4. How CSS is structured
    5. How CSS works
    6. Using your new knowledge
  9. CSS 构建块
    1. CSS building blocks overview
    2. Cascade and inheritance
    3. CSS 选择器
    4. The box model
    5. Backgrounds and borders
    6. Handling different text directions
    7. Overflowing content
    8. Values and units
    9. Sizing items in CSS
    10. Images, media, and form elements
    11. Styling tables
    12. Debugging CSS
    13. Organizing your CSS
  10. 样式化文本
    1. Styling text overview
    2. Fundamental text and font styling
    3. Styling lists
    4. Styling links
    5. Web fonts
    6. Assessment: Typesetting a community school homepage
  11. CSS 布局
    1. CSS layout overview
    2. Introduction to CSS layout
    3. Normal Flow
    4. Flexbox
    5. Grids
    6. Floats
    7. 位置
    8. Multiple-column Layout
    9. Responsive design
    10. Beginner's guide to media queries
    11. Legacy Layout Methods
    12. Supporting Older Browsers
    13. Fundamental Layout Comprehension
  12. JavaScript — Dynamic client-side scripting
  13. JavaScript 第一步
    1. JavaScript first steps overview
    2. What is JavaScript?
    3. A first splash into JavaScript
    4. What went wrong? Troubleshooting JavaScript
    5. Storing the information you need — Variables
    6. Basic math in JavaScript — Numbers and operators
    7. Handling text — Strings in JavaScript
    8. Useful string methods
    9. 数组
    10. Assessment: Silly story generator
  14. JavaScript 构建块
    1. JavaScript building blocks overview
    2. Making decisions in your code — Conditionals
    3. Looping code
    4. Functions — Reusable blocks of code
    5. Build your own function
    6. Function return values
    7. 事件介绍
    8. Assessment: Image gallery
  15. 引入 JavaScript 对象
    1. Introducing JavaScript objects overview
    2. Object basics
    3. 对象原型
    4. Object-oriented programming concepts
    5. Classes in JavaScript
    6. Working with JSON data
    7. Object building practice
    8. Assessment: Adding features to our bouncing balls demo
  16. 异步 JavaScript
    1. Asynchronous JavaScript overview
    2. General asynchronous programming concepts
    3. Introducing asynchronous JavaScript
    4. Cooperative asynchronous Java​Script: Timeouts and intervals
    5. Graceful asynchronous programming with Promises
    6. Making asynchronous programming easier with async and await
    7. Choosing the right approach
  17. 客户端侧 Web API
    1. 客户端侧 Web API
    2. Introduction to web APIs
    3. Manipulating documents
    4. Fetching data from the server
    5. Third party APIs
    6. Drawing graphics
    7. Video and audio APIs
    8. Client-side storage
  18. Web forms — Working with user data
  19. Core forms learning pathway
    1. Web forms overview
    2. Your first form
    3. How to structure a web form
    4. Basic native form controls
    5. The HTML5 input types
    6. Other form controls
    7. Styling web forms
    8. Advanced form styling
    9. UI pseudo-classes
    10. Client-side form validation
    11. Sending form data
  20. Advanced forms articles
    1. How to build custom form controls
    2. Sending forms through JavaScript
    3. CSS property compatibility table for form controls
  21. Accessibility — Make the web usable by everyone
  22. Accessibility guides
    1. Accessibility overview
    2. What is accessibility?
    3. HTML: A good basis for accessibility
    4. CSS and JavaScript accessibility best practices
    5. WAI-ARIA basics
    6. Accessible multimedia
    7. Mobile accessibility
  23. Accessibility assessment
    1. Assessment: Accessibility troubleshooting
  24. Tools and testing
  25. Client-side web development tools
    1. Client-side web development tools index
    2. Client-side tooling overview
    3. Command line crash course
    4. Package management basics
    5. Introducing a complete toolchain
    6. Deploying our app
  26. Introduction to client-side frameworks
    1. Client-side frameworks overview
    2. Framework main features
  27. React
    1. Getting started with React
    2. Beginning our React todo list
    3. Componentizing our React app
    4. React interactivity: Events and state
    5. React interactivity: Editing, filtering, conditional rendering
    6. Accessibility in React
    7. React resources
  28. Ember
    1. Getting started with Ember
    2. Ember app structure and componentization
    3. Ember interactivity: Events, classes and state
    4. Ember Interactivity: Footer functionality, conditional rendering
    5. Routing in Ember
    6. Ember resources and troubleshooting
  29. Vue
    1. Getting started with Vue
    2. Creating our first Vue component
    3. Rendering a list of Vue components
    4. Adding a new todo form: Vue events, methods, and models
    5. Styling Vue components with CSS
    6. Using Vue computed properties
    7. Vue conditional rendering: editing existing todos
    8. Focus management with Vue refs
    9. Vue resources
  30. Svelte
    1. Getting started with Svelte
    2. Starting our Svelte Todo list app
    3. Dynamic behavior in Svelte: working with variables and props
    4. Componentizing our Svelte app
    5. Advanced Svelte: Reactivity, lifecycle, accessibility
    6. Working with Svelte stores
    7. TypeScript support in Svelte
    8. Deployment and next steps
  31. Angular
    1. Getting started with Angular
    2. Beginning our Angular todo list app
    3. Styling our Angular app
    4. Creating an item component
    5. Filtering our to-do items
    6. Building Angular applications and further resources
  32. Git and GitHub
    1. Git and GitHub overview
    2. Hello World
    3. Git Handbook
    4. Forking Projects
    5. About pull requests
    6. Mastering Issues
  33. Cross browser testing
    1. Cross browser testing overview
    2. Introduction to cross browser testing
    3. Strategies for carrying out testing
    4. Handling common HTML and CSS problems
    5. Handling common JavaScript problems
    6. Handling common accessibility problems
    7. Implementing feature detection
    8. Introduction to automated testing
    9. Setting up your own test automation environment
  34. Server-side website programming
  35. 第一步
    1. First steps overview
    2. Introduction to the server-side
    3. Client-Server overview
    4. Server-side web frameworks
    5. Website security
  36. Django Web 框架 (Python)
    1. Django web framework (Python) overview
    2. 介绍
    3. 设置开发环境
    4. Tutorial: The Local Library website
    5. Tutorial Part 2: Creating a skeleton website
    6. Tutorial Part 3: Using models
    7. Tutorial Part 4: Django admin site
    8. Tutorial Part 5: Creating our home page
    9. Tutorial Part 6: Generic list and detail views
    10. Tutorial Part 7: Sessions framework
    11. Tutorial Part 8: User authentication and permissions
    12. Tutorial Part 9: Working with forms
    13. Tutorial Part 10: Testing a Django web application
    14. Tutorial Part 11: Deploying Django to production
    15. Web application security
    16. Assessment: DIY mini blog
  37. Express Web Framework (node.js/JavaScript)
    1. Express Web Framework (Node.js/JavaScript) overview
    2. Express/Node introduction
    3. Setting up a Node (Express) development environment
    4. Express tutorial: The Local Library website
    5. Express Tutorial Part 2: Creating a skeleton website
    6. Express Tutorial Part 3: Using a database (with Mongoose)
    7. Express Tutorial Part 4: Routes and controllers
    8. Express Tutorial Part 5: Displaying library data
    9. Express Tutorial Part 6: Working with forms
    10. Express Tutorial Part 7: Deploying to production
  38. Further resources
  39. Common questions
    1. HTML questions
    2. CSS questions
    3. JavaScript questions
    4. Web mechanics
    5. Tools and setup
    6. Design and accessibility
  • Web 技术
  • Learn Web Development
  • About MDN
  • Feedback
  • 关于
  • MDN Web Docs Store
  • 联络我们
  • Firefox

MDN

  • MDN on Twitter
  • MDN on Github

Mozilla

  • Mozilla on Twitter
  • Mozilla on Instagram

© 2005- 2022 Mozilla and individual contributors. Content is available under these licenses .

  • Terms
  • Privacy
  • Cookie