React从基础入门到高级实战:React 实战项目 - 项目一:在线待办事项应用

React 实战项目:在线待办事项应用

欢迎来到本 React 开发教程专栏的第 26 篇!在之前的 25 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件、状态、路由和性能优化等核心知识。这一次,我们将通过一个完整的实战项目——在线待办事项应用,将这些知识融会贯通,帮助您从理论走向实践。

本项目的目标是为初学者提供一个简单但全面的 React 开发体验。通过这个项目,您将学习如何分析需求、选择技术栈、实现功能并最终将应用部署到线上。无论您是刚刚接触 React 的新手,还是希望通过实践巩固基础的开发者,这篇文章都将为您提供清晰的指引和丰富的代码示例。


引言

React 是一个强大的前端框架,其声明式编程和组件化特性让开发者能够高效地构建用户界面。然而,仅仅理解理论是不够的——真正的学习发生在实践中。在本项目中,我们将构建一个在线待办事项应用,这是一个经典的入门案例,既简单又实用,能够帮助您掌握 React 的核心技能。

这个应用的目标非常明确:允许用户创建、编辑和删除待办事项,支持按状态过滤,并将数据保存在本地,确保刷新页面后不会丢失。我们将从需求分析开始,逐步完成技术选型、代码实现和部署上线,并在最后提供一个练习,帮助您进一步巩固所学内容。

通过这个项目,您将体验到:

  • 组件化思维:如何将复杂的界面拆分为可重用的模块。
  • 状态管理:如何在应用中高效地共享和更新数据。
  • 路由设计:如何实现多页面导航。
  • 数据持久化:如何使用本地存储保存用户数据。

准备好了吗?让我们开始吧!


需求分析

在动手写代码之前,我们需要明确这个待办事项应用的具体功能需求。一个清晰的需求清单不仅能指导开发过程,还能帮助我们理解每个功能的意义。以下是我们项目的核心需求:

  1. 创建待办事项
    用户可以输入任务描述并添加到待办列表中。
  2. 编辑待办事项
    用户可以修改已有任务的内容。
  3. 删除待办事项
    用户可以移除不再需要的任务。
  4. 过滤待办事项
    用户可以根据任务状态(全部、已完成、未完成)筛选列表。
  5. 数据持久化
    数据将保存在浏览器本地存储中,刷新页面后依然可用。

为什么选择这些功能?

这些功能覆盖了待办事项应用的核心场景,同时也为学习 React 提供了丰富的实践机会:

  • 创建和编辑涉及表单处理和事件监听。
  • 删除和过滤需要掌握状态更新和数组操作。
  • 本地存储引入了数据持久化的概念。

此外,这些功能简单直观,非常适合初学者上手,同时也为后续扩展(如添加分类、优先级等)留下了空间。


技术栈选择

在开始实现之前,我们需要选择合适的技术栈。以下是本项目使用的工具和技术,以及选择它们的理由:

  • React
    核心框架,用于构建用户界面。React 的组件化和声明式编程让开发过程更加直观。
  • Vite
    构建工具,提供快速的开发服务器和高效的打包能力。相比传统的 Create React App,Vite 的启动速度更快,热更新体验更优。
  • React Router
    用于实现页面导航。虽然待办事项应用可以是单页应用,但我们将通过多页面设计展示路由的用法。
  • Context API
    React 内置的状态管理工具,用于在组件间共享待办事项数据。相比 Redux,它更轻量,适合小型项目。

技术栈的优势

  • React:生态丰富,学习曲线平滑,是现代前端开发的标配。
  • Vite:2025 年的前端开发趋势偏向轻量化和高性能,Vite 代表了这一方向。
  • React Router:支持动态路由和参数传递,是多页面应用的理想选择。
  • Context API:无需引入外部依赖,简单易用,适合初学者理解状态管理。

这些工具的组合不仅易于上手,还能帮助您掌握现代 React 开发的精髓。


项目实现

现在,我们进入最核心的部分——代码实现。我们将从项目搭建开始,逐步完成组件拆分、路由设计、状态管理和本地存储的开发。

1. 项目搭建

首先,使用 Vite 创建一个新的 React 项目:

npm create vite@latest todo-app -- --template react
cd todo-app
npm install
npm run dev

安装必要的依赖:

npm install react-router-dom

这将启动一个基础的 React 项目,接下来我们将逐步实现功能。

2. 组件拆分

组件化是 React 的核心思想。通过将应用拆分为多个小组件,我们可以提高代码的可读性和复用性。

组件结构
  • App:根组件,负责路由配置和整体布局。
  • TodoList:显示待办事项列表,包含过滤功能。
  • TodoItem:展示单个待办事项,支持编辑和删除。
  • TodoForm:用于添加或编辑待办事项的表单。
  • FilterButtons:提供状态过滤选项。
文件结构
src/
├── components/
│   ├── TodoList.jsx
│   ├── TodoItem.jsx
│   ├── TodoForm.jsx
│   └── FilterButtons.jsx
├── context/
│   └── TodoContext.jsx
├── App.jsx
└── main.jsx

3. 路由设计

我们将应用设计为多页面结构,使用 React Router 实现导航。

路由配置
  • /:首页,显示待办事项列表。
  • /add:添加待办事项页面。
  • /edit/:id:编辑指定待办事项页面。

App.jsx 中配置路由:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import TodoList from './components/TodoList';
import TodoForm from './components/TodoForm';
import { TodoProvider } from './context/TodoContext';function App() {return (<TodoProvider><Router><div className="min-h-screen bg-gray-100 p-4"><Routes><Route path="/" element={<TodoList />} /><Route path="/add" element={<TodoForm />} /><Route path="/edit/:id" element={<TodoForm />} /></Routes></div></Router></TodoProvider>);
}export default App;
导航链接

TodoList 中添加导航到添加页面的按钮:

import { Link } from 'react-router-dom';function TodoList() {return (<div><h1 className="text-2xl font-bold mb-4">待办事项</h1><Link to="/add" className="bg-blue-500 text-white px-4 py-2 rounded">添加任务</Link>{/* 列表内容 */}</div>);
}export default TodoList;

4. 状态管理

我们使用 Context API 管理全局状态,包括待办事项列表和过滤条件。

创建 Context

src/context/TodoContext.jsx 中:

import { createContext, useState, useEffect } from 'react';export const TodoContext = createContext();export function TodoProvider({ children }) {const [todos, setTodos] = useState([]);const [filter, setFilter] = useState('all');// 加载本地存储数据useEffect(() => {const storedTodos = JSON.parse(localStorage.getItem('todos')) || [];setTodos(storedTodos);}, []);// 保存数据到本地存储useEffect(() => {localStorage.setItem('todos', JSON.stringify(todos));}, [todos]);return (<TodoContext.Provider value={{ todos, setTodos, filter, setFilter }}>{children}</TodoContext.Provider>);
}
使用 Context

TodoList 中访问和过滤数据:

import { useContext } from 'react';
import { TodoContext } from '../context/TodoContext';
import TodoItem from './TodoItem';
import FilterButtons from './FilterButtons';function TodoList() {const { todos, filter } = useContext(TodoContext);const filteredTodos = todos.filter((todo) => {if (filter === 'completed') return todo.completed;if (filter === 'incomplete') return !todo.completed;return true;});return (<div><h1 className="text-2xl font-bold mb-4">待办事项</h1><Link to="/add" className="bg-blue-500 text-white px-4 py-2 rounded mb-4 inline-block">添加任务</Link><FilterButtons /><ul className="space-y-2">{filteredTodos.map((todo) => (<TodoItem key={todo.id} todo={todo} />))}</ul></div>);
}export default TodoList;

5. 组件实现

TodoItem

TodoItem.jsx 中实现单个待办事项的展示和操作:

import { useContext } from 'react';
import { Link } from 'react-router-dom';
import { TodoContext } from '../context/TodoContext';function TodoItem({ todo }) {const { todos, setTodos } = useContext(TodoContext);const toggleComplete = () => {setTodos(todos.map((t) =>t.id === todo.id ? { ...t, completed: !t.completed } : t));};const deleteTodo = () => {setTodos(todos.filter((t) => t.id !== todo.id));};return (<li className="flex items-center justify-between p-2 bg-white rounded shadow"><div className="flex items-center"><inputtype="checkbox"checked={todo.completed}onChange={toggleComplete}className="mr-2"/><span className={todo.completed ? 'line-through text-gray-500' : ''}>{todo.text}</span></div><div><Linkto={`/edit/${todo.id}`}className="text-blue-500 mr-2">编辑</Link><button onClick={deleteTodo} className="text-red-500">删除</button></div></li>);
}export default TodoItem;
TodoForm

TodoForm.jsx 中实现添加和编辑表单:

import { useState, useContext, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { TodoContext } from '../context/TodoContext';function TodoForm() {const { todos, setTodos } = useContext(TodoContext);const { id } = useParams();const navigate = useNavigate();const [text, setText] = useState('');useEffect(() => {if (id) {const todo = todos.find((t) => t.id === id);if (todo) setText(todo.text);}}, [id, todos]);const handleSubmit = (e) => {e.preventDefault();if (!text.trim()) return;if (id) {// 编辑setTodos(todos.map((t) => (t.id === id ? { ...t, text } : t)));} else {// 添加const newTodo = {id: Date.now().toString(),text,completed: false,};setTodos([...todos, newTodo]);}navigate('/');};return (<div><h1 className="text-2xl font-bold mb-4">{id ? '编辑任务' : '添加任务'}</h1><form onSubmit={handleSubmit}><inputtype="text"value={text}onChange={(e) => setText(e.target.value)}className="w-full p-2 border rounded mb-4"placeholder="请输入任务描述"/><buttontype="submit"className="bg-blue-500 text-white px-4 py-2 rounded">保存</button></form></div>);
}export default TodoForm;
FilterButtons

FilterButtons.jsx 中实现过滤按钮:

import { useContext } from 'react';
import { TodoContext } from '../context/TodoContext';function FilterButtons() {const { filter, setFilter } = useContext(TodoContext);return (<div className="mb-4"><buttononClick={() => setFilter('all')}className={`mr-2 px-4 py-2 rounded ${filter === 'all' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>全部</button><buttononClick={() => setFilter('completed')}className={`mr-2 px-4 py-2 rounded ${filter === 'completed' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>已完成</button><buttononClick={() => setFilter('incomplete')}className={`px-4 py-2 rounded ${filter === 'incomplete' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>未完成</button></div>);
}export default FilterButtons;

6. 本地存储

本地存储已在 TodoContext 中实现,通过 localStorage 保存和加载数据,确保刷新页面后数据不会丢失。


部署

开发完成后,我们将应用部署到 Netlify,让它在线上运行。

1. 构建项目

运行以下命令生成静态文件:

npm run build

这会生成 dist 文件夹,包含应用的静态资源。

2. 部署到 Netlify

  1. 注册 Netlify:访问 Netlify 官网 并创建账号。
  2. 新建站点:在控制台选择“New site from Git”。
  3. 连接仓库:将项目推送至 GitHub 并连接。
  4. 配置构建
    • 构建命令:npm run build
    • 发布目录:dist
  5. 部署:点击“Deploy site”,等待部署完成。

部署成功后,您将获得一个唯一的 URL,可以通过它访问您的待办事项应用。


练习:添加分类功能

为了帮助您巩固所学,我们设计了一个练习:为待办事项添加分类功能。

需求

  • 用户可以为任务指定分类(如“工作”、“个人”、“学习”)。
  • 用户可以按分类过滤任务。

实现步骤

  1. 扩展数据结构
    todos 中为每个任务添加 category 字段。
  2. 更新 TodoForm
    添加分类选择下拉菜单。
  3. 更新过滤逻辑
    TodoListFilterButtons 中支持分类过滤。

示例代码

修改 TodoContext
export function TodoProvider({ children }) {const [todos, setTodos] = useState([]);const [filter, setFilter] = useState('all');useEffect(() => {const storedTodos = JSON.parse(localStorage.getItem('todos')) || [];setTodos(storedTodos);}, []);useEffect(() => {localStorage.setItem('todos', JSON.stringify(todos));}, [todos]);return (<TodoContext.Provider value={{ todos, setTodos, filter, setFilter }}>{children}</TodoContext.Provider>);
}
修改 TodoForm
function TodoForm() {const { todos, setTodos } = useContext(TodoContext);const { id } = useParams();const navigate = useNavigate();const [text, setText] = useState('');const [category, setCategory] = useState('工作');useEffect(() => {if (id) {const todo = todos.find((t) => t.id === id);if (todo) {setText(todo.text);setCategory(todo.category);}}}, [id, todos]);const handleSubmit = (e) => {e.preventDefault();if (!text.trim()) return;if (id) {setTodos(todos.map((t) =>t.id === id ? { ...t, text, category } : t));} else {const newTodo = {id: Date.now().toString(),text,category,completed: false,};setTodos([...todos, newTodo]);}navigate('/');};return (<div><h1 className="text-2xl font-bold mb-4">{id ? '编辑任务' : '添加任务'}</h1><form onSubmit={handleSubmit}><inputtype="text"value={text}onChange={(e) => setText(e.target.value)}className="w-full p-2 border rounded mb-4"placeholder="请输入任务描述"/><selectvalue={category}onChange={(e) => setCategory(e.target.value)}className="w-full p-2 border rounded mb-4"><option value="工作">工作</option><option value="个人">个人</option><option value="学习">学习</option></select><buttontype="submit"className="bg-blue-500 text-white px-4 py-2 rounded">保存</button></form></div>);
}
修改 TodoList 和 FilterButtons
function TodoList() {const { todos, filter } = useContext(TodoContext);const filteredTodos = todos.filter((todo) => {if (filter === 'completed') return todo.completed;if (filter === 'incomplete') return !todo.completed;if (['工作', '个人', '学习'].includes(filter)) return todo.category === filter;return true;});return (<div><h1 className="text-2xl font-bold mb-4">待办事项</h1><Link to="/add" className="bg-blue-500 text-white px-4 py-2 rounded mb-4 inline-block">添加任务</Link><FilterButtons /><ul className="space-y-2">{filteredTodos.map((todo) => (<TodoItem key={todo.id} todo={todo} />))}</ul></div>);
}function FilterButtons() {const { filter, setFilter } = useContext(TodoContext);return (<div className="mb-4 flex flex-wrap gap-2"><buttononClick={() => setFilter('all')}className={`px-4 py-2 rounded ${filter === 'all' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>全部</button><buttononClick={() => setFilter('completed')}className={`px-4 py-2 rounded ${filter === 'completed' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>已完成</button><buttononClick={() => setFilter('incomplete')}className={`px-4 py-2 rounded ${filter === 'incomplete' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>未完成</button><buttononClick={() => setFilter('工作')}className={`px-4 py-2 rounded ${filter === '工作' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>工作</button><buttononClick={() => setFilter('个人')}className={`px-4 py-2 rounded ${filter === '个人' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>个人</button><buttononClick={() => setFilter('学习')}className={`px-4 py-2 rounded ${filter === '学习' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>学习</button></div>);
}

练习目标

通过这个练习,您将学会如何扩展现有功能,提升对状态管理和组件通信的理解。


注意事项

  • 初学者友好:本文避免了复杂的概念,所有代码都尽量保持简单直观。
  • 学习建议:建议您边阅读边动手实现,遇到问题时查阅 React 官方文档和Vite 文档。
  • 扩展思路:完成项目后,可以尝试添加更多功能,如任务优先级、截止日期或提醒功能。

结语

通过这个在线待办事项应用项目,您从需求分析到部署上线,完整地走过了一个 React 项目的开发流程。您学习了组件拆分、路由设计、状态管理和数据持久化等核心技能,这些知识将成为您未来开发更复杂应用的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/84184.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1991-2024年上市公司个股换手率数据

1991-2024年上市公司个股换手率数据 1、时间&#xff1a;1991-2024年 2、来源&#xff1a;上海证券交易所和深圳证券交易所 3、指标&#xff1a;证券代码、交易年份、开始日期、截止日期、年换手率(流通股数)(%)、年换手率(总股数)(%)、日均换手率(流通股数)(%)、日均换手率…

RAID存储技术概述

1 数据存储架构 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划&#xff0c;涵盖存储系统的布局、数据存储策略等&#xff0c;它明确数据如何存储、管理与访问&#xff0c;为数据的安全、高效使用提供支撑。 1.1 存储系统 存储系统是计算机的重要组成部分之…

LRU 和 DiskLRU实现相册缓存器

我是写Linux后端的&#xff08;golang、c、py&#xff09;&#xff0c;后端缓存算法通常是指的是内存里面的lru、或diskqueue&#xff0c;都是独立使用。 很少有用内存lru与disklru结合的场景需求。近段时间研究android开发&#xff0c;里面有一些设计思想值得后端学习。 写这…

可视化预警:如何让生产风险预警更高效?

你有没有遇到过这种情况&#xff1f; 明明设备已经开始发热报警&#xff0c;但操作人员还在继续运行&#xff1b; 或者某个参数已经接近危险值&#xff0c;却没人注意到&#xff1b; 甚至问题早就埋下了隐患&#xff0c;只是当时没发现…… 这些情况的背后&#xff0c;其实都…

【MPC-C++】qpOASES 源码编译与链接,编译器设置细节

qpOASES 源码编译与链接 克隆源码 git clone https://github.com/coin-or/qpOASES.gitcd qpOASES mkdir build cd build接下来是构建&#xff0c;有一些细节。 查看 CMakeLists.txt&#xff0c;发现如果不显示指定 CMAKE_BUILD_TYPE 构建版本&#xff0c;会自动编译 Release…

【11408学习记录】考研数学攻坚:行列式本质、性质与计算全突破

行列式 数学线性代数一、对象&#xff08;元素&#xff09;&#xff1a;向量二、运算三、行列式3.1 第一种定义——行列式的本质定义3.2 行列式的性质性质1&#xff1a;行列互换&#xff0c;其值不变性质2&#xff1a;若行列式中某行&#xff08;列&#xff09;元素全为零&…

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…

小木的算法日记-线段树

&#x1f333; 线段树 &#xff08;Segment Tree&#xff09;&#xff1a;玩转区间作的终极利器 你好&#xff0c;未来的算法大师&#xff01; 想象一下&#xff0c;你正在处理一个巨大的数据集&#xff0c;比如某个电商网站一整天的用户点击流。老板突然问你&#xff1a;“下…

Day24 元组和OS模块

1、元组&#xff08;有序 不可变 可重复&#xff09; 管道工程中pipeline类接收的是一个包含多个小元组的列表作为输入。可以这样理解这个结构&#xff1a; &#xff08;1&#xff09; 列表 []: 定义了步骤执行的先后顺序。Pipeline 会按照列表中的顺序依次处理数据。之所以用列…

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…

crackme006

crackme006 名称值软件名称aLoNg3x.1.exe加壳方式无保护方式Serial编译语言Delphi调试环境Win10 64位使用工具x32dbg,ida pro,PEid,DarkDe4破解日期2025-06-05 脱壳 1. 先用PEid查壳 查到无壳 寻找Serial 查询到编程语言为Delphi 导出Delphi符号表信息到x32dbg&#xff0c…

Conda 创建新环境时报错 HTTP 502,如何解决?

Conda 创建新环境时报错 HTTP 502&#xff0c;如何解决&#xff1f; 最近在用 Conda 创建新环境时&#xff0c;突然遇到这样一个错误&#xff1a; CondaHTTPError: HTTP 502 BAD GATEWAY for url <https://mirrors.westlake.edu.cn/ANACONDA/cloud/conda-forge/linux-64/r…

2025最全TS手写题之partial/Omit/Pick/Exclude/Readonly/Required

随着 TS 在工作中使用的越来越广泛&#xff0c;面试的时候面试官也都会加上一两个 TS 的问题来了解候选人对于 TS 的熟悉程度&#xff0c;其中就有不少手写题目&#xff0c;比如笔者在字节的一次二面&#xff0c;面试官就问到了我如何实现一个 Pick&#xff0c;在小红书的一面&…

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)

引言 在嵌入式系统中&#xff0c;用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例&#xff0c;介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单&#xff0c;执行相应操作&#xff0c;并提供平滑的滚动动画效果。 本文设计了一个…

LLMs之StructuredOutput:大模型结构化输出的简介、常用方案、前沿框架之详细攻略

LLMs之StructuredOutput&#xff1a;大模型结构化输出的简介、常用方案、前沿框架之详细攻略 目录 大模型结构化输出的简介 1、特点与难点 大模型结构化输出的常用方案及对比 1、前沿框架&#xff1a;vLLM 与 XGrammar 大模型结构化输出的案例应用 大模型结构化输出的简介…

Linux中shell流程控制语句

一、if条件控制 1.1 语法解读 单路决策 - 单分支if语句样式&#xff1a;if [ 条件 ]then指令fi特点&#xff1a;单一条件&#xff0c;只有一个输出 双路决策 - 双分支if语句样式&#xff1a;if [ 条件 ]then指令1else指令2fi特点&#xff1a;单一条件&#xff0c;两个输出 …

Python学习(8) ----- Python的类与对象

Python 中的类&#xff08;Class&#xff09;与对象&#xff08;Object&#xff09;是面向对象编程&#xff08;OOP&#xff09;的核心。我们可以通过“类是模板&#xff0c;对象是实例”来理解它们的关系。 &#x1f9f1; 一句话理解&#xff1a; 类就像“图纸”&#xff0c;对…

数据结构-文件

文件是性质相同的记录的集合。 记录是文件中存取的基本单位&#xff0c;数据项是文件可使用的最小单位。 操作系统研究的文件是一维的无结构连续字符序列&#xff0c;数据库中研究的文件是带有结构的记录集合。 文件在外存上的4种基本组织方式&#xff1a;顺序、索引、散列、链…

前端开发面试题总结-CSS篇

文章目录 CSS面试高频问答1、CSS选择器的优先级2、CSS3新特性3、如何垂直水平居中盒子4、什么是重绘和重排5、px/em/rem/vw有什么区别6、rem布局的原理7、如何设置比12px还要小的字体8、CSS中隐藏元素的方式有哪些 CSS面试高频问答 1、CSS选择器的优先级 2、CSS3新特性 3、如何…