一、创建 React 项目1、使用create-react-app全局安装npmi-gcreate-react-app创建项目create-react-app hello-react启动项目npmstart2、使用 Vite全局安装npmi-gpnpmpnpmi-gvite创建项目pnpmcreate vite启动项目pnpmrun dev二、React 项目结构文件 / 目录说明public静态资源目录包含 HTML 模板、图标等src源代码目录包含 React 组件、样式等.gitignoreGit 忽略文件配置package.json项目配置文件包含依赖、脚本等package-lock.json依赖版本锁定文件README.md项目说明文档public 目录文件 / 目录说明favicon.icon网站页签图标index.html主页面logo192.pnglogo 图logo512.pnglogo 图manifest.json应用加壳的配置文件robots.txt爬虫协议文件src 目录文件 / 目录说明App.cssApp 组件的样式App.jsApp 组件App.test.js用于给 App 做测试index.css样式index.js入口文件logo.svglogo 图reportWebVitals.js页面性能分析文件需要web-vitals库的支持setupTests.js组件单元测试的文件需要jest-dom库的支持三、React 简单案例1、具体实现Hello 组件importReact,{Component}fromreact;// 同时导入默认导出和命名导出importhellofrom./index.module.css;exportdefaultclassHelloextendsComponent{render(){returnh3 className{hello.title}Hello React/h3;}}.title{background-color:orange;}Welcome 组件importReact,{Component}fromreact;import./index.css;exportdefaultclassWelcomeextendsComponent{render(){returnh3 classNametitleWelcome/h3;}}.title{background-color:skyblue;}App 组件importReact,{Component}fromreact;importHellofrom./components/Hello;importWelcomefrom./components/Welcome;exportdefaultclassAppextendsComponent{render(){return(divHello/Welcome//div);}}index.jsimportReactfromreact;// 引入 React 核心库importReactDOMfromreact-dom;// 引入 ReactDOMimportAppfrom./App;// 引入 App 组件// 渲染 App 组件到页面ReactDOM.render(App/,document.getElementById(root));2、CSS Moduleindex.css是普通的、全局的样式表在里面定义的类名一旦被引入到项目中就会成为全局的import./index.css;index.module.css是 CSS Module 样式表当导入这个文件时会得到一个对象需要通过这个对象的属性来使用类名importhellofrom./index.module.css;四、TodoList 案例Header 组件importReact,{Component}fromreact;import{nanoid}fromnanoid;importPropTypesfromprop-types;import./index.css;exportdefaultclassHeaderextendsComponent{staticpropTypes{addTodo:PropTypes.func.isRequired,};handleKeyUp(e){if(e.keyCode!13)return;constvaluee.target.value.trim();if(!value)return;consttodoObj{id:nanoid(),name:value,done:false};this.props.addTodo(todoObj);e.target.value;};render(){return(div classNametodo-headerinput onKeyUp{this.handleKeyUp}typetextplaceholder请输入你的任务名称按回车键确认//div);}}.todo-header input{width:560px;height:28px;font-size:14px;border:1px solid #ccc;border-radius:4px;padding:4px 7px;}.todo-header input:focus{outline:none;border-color:rgba(82,168,236,0.8);box-shadow:inset 0 1px 1pxrgba(0,0,0,0.075),0 0 8pxrgba(82,168,236,0.6);}Footer 组件importReact,{Component}fromreact;importPropTypesfromprop-types;import./index.css;exportdefaultclassFooterextendsComponent{staticpropTypes{todos:PropTypes.array.isRequired,checkAllTodo:PropTypes.func.isRequired,clearAllDone:PropTypes.func.isRequired,};handleCheckAll(event){this.props.checkAllTodo(event.target.checked);};handleClearAllDone(){this.props.clearAllDone();};render(){constdoneCountthis.props.todos.reduce((pre,todo)pre(todo.done?1:0),0);// 已完成个数consttotalCountthis.props.todos.length;// 总个数return(div classNametodo-footerlabelinput typecheckboxonChange{this.handleCheckAll}checked{doneCounttotalCounttotalCount!0?true:false}//labelspanspan已完成{doneCount}/span/全部{totalCount}/spanbutton onClick{this.handleClearAllDone}classNamebtn btn-danger清除已完成任务/button/div);}}.todo-footer{height:40px;line-height:40px;padding-left:6px;margin-top:5px;}.todo-footer label{display:inline-block;margin-right:20px;cursor:pointer;}.todo-footer label input{position:relative;top:-1px;vertical-align:middle;margin-right:5px;}.todo-footer button{float:right;margin-top:5px;}List 组件importReact,{Component}fromreact;importPropTypesfromprop-types;importItemfrom../Item;import./index.css;exportdefaultclassListextendsComponent{staticpropTypes{todos:PropTypes.array.isRequired,updateTodo:PropTypes.func.isRequired,deleteTodo:PropTypes.func.isRequired,};render(){const{todos,updateTodo,deleteTodo}this.props;return(ul classNametodo-main{todos.map((todo){returnItem key{todo.id}{...todo}updateTodo{updateTodo}deleteTodo{deleteTodo}/;})}/ul);}}.todo-main{margin-left:0px;border:1px solid #ddd;border-radius:2px;padding:0px;}.todo-empty{height:40px;line-height:40px;border:1px solid #ddd;border-radius:2px;padding-left:5px;margin-top:10px;}Item 组件importReact,{Component}fromreact;importPropTypesfromprop-types;import./index.css;exportdefaultclassItemextendsComponent{state{mouse:false};// 标识鼠标移入、移出staticpropTypes{id:PropTypes.string.isRequired,name:PropTypes.string.isRequired,done:PropTypes.bool.isRequired,updateTodo:PropTypes.func.isRequired,deleteTodo:PropTypes.func.isRequired,};handleMouse(flag){return(){this.setState({mouse:flag});};};handleCheck(id){return(event){this.props.updateTodo(id,event.target.checked);};};handleDelete(id){if(window.confirm(确定删除吗)){this.props.deleteTodo(id);}};render(){const{id,name,done}this.props;const{mouse}this.state;return(li style{{backgroundColor:mouse?#ddd:white}}onMouseEnter{this.handleMouse(true)}onMouseLeave{this.handleMouse(false)}labelinput typecheckboxchecked{done}onChange{this.handleCheck(id)}/span{name}/span/labelbutton onClick{()this.handleDelete(id)}classNamebtn btn-dangerstyle{{display:mouse?block:none}}删除/button/li);}}li{list-style:none;height:36px;line-height:36px;padding:0 5px;border-bottom:1px solid #ddd;}li label{float:left;cursor:pointer;}li label li input{vertical-align:middle;margin-right:6px;position:relative;top:-1px;}li button{float:right;display:none;margin-top:3px;}li:before{content:initial;}li:last-child{border-bottom:none;}App 组件importReact,{Component}fromreact;importHeaderfrom./components/Header;importListfrom./components/List;importFooterfrom./components/Footer;import./App.css;exportdefaultclassAppextendsComponent{state{todos:[{id:001,name:吃饭,done:true},{id:002,name:睡觉,done:true},{id:003,name:打代码,done:false},{id:004,name:逛街,done:false},],};addTodo(todoObj){const{todos}this.state;constnewTodos[todoObj,...todos];this.setState({todos:newTodos});};updateTodo(id,done){const{todos}this.state;constnewTodostodos.map((todoObj){if(todoObj.idid)return{...todoObj,done};elsereturntodoObj;});this.setState({todos:newTodos});};deleteTodo(id){const{todos}this.state;constnewTodostodos.filter((todoObj){returntodoObj.id!id;});this.setState({todos:newTodos});};checkAllTodo(done){const{todos}this.state;constnewTodostodos.map((todoObj){return{...todoObj,done};});this.setState({todos:newTodos});};clearAllDone(){const{todos}this.state;constnewTodostodos.filter((todoObj){return!todoObj.done;});this.setState({todos:newTodos});};render(){const{todos}this.state;return(div classNametodo-containerdiv classNametodo-wrapHeader addTodo{this.addTodo}/List todos{todos}updateTodo{this.updateTodo}deleteTodo{this.deleteTodo}/Footer todos{todos}checkAllTodo{this.checkAllTodo}clearAllDone{this.clearAllDone}//div/div);}}body{background:#fff;}.btn{display:inline-block;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;box-shadow:inset 0 1px 0rgba(255,255,255,0.2),0 1px 2pxrgba(0,0,0,0.05);border-radius:4px;}.btn-danger{color:#fff;background-color:#da4f49;border:1px solid #bd362f;}.btn-danger:hover{color:#fff;background-color:#bd362f;}.btn:focus{outline:none;}.todo-container{width:600px;margin:0 auto;}.todo-container .todo-wrap{padding:10px;border:1px solid #ddd;border-radius:5px;}index.jsimportReactfromreact;// 引入 React 核心库importReactDOMfromreact-dom;// 引入 ReactDOMimportAppfrom./App;// 引入 App 组件// 渲染 App 组件到页面ReactDOM.render(App/,document.getElementById(root));
React - 创建 React 项目、React 项目结构、React 简单案例、TodoList 案例
一、创建 React 项目1、使用create-react-app全局安装npmi-gcreate-react-app创建项目create-react-app hello-react启动项目npmstart2、使用 Vite全局安装npmi-gpnpmpnpmi-gvite创建项目pnpmcreate vite启动项目pnpmrun dev二、React 项目结构文件 / 目录说明public静态资源目录包含 HTML 模板、图标等src源代码目录包含 React 组件、样式等.gitignoreGit 忽略文件配置package.json项目配置文件包含依赖、脚本等package-lock.json依赖版本锁定文件README.md项目说明文档public 目录文件 / 目录说明favicon.icon网站页签图标index.html主页面logo192.pnglogo 图logo512.pnglogo 图manifest.json应用加壳的配置文件robots.txt爬虫协议文件src 目录文件 / 目录说明App.cssApp 组件的样式App.jsApp 组件App.test.js用于给 App 做测试index.css样式index.js入口文件logo.svglogo 图reportWebVitals.js页面性能分析文件需要web-vitals库的支持setupTests.js组件单元测试的文件需要jest-dom库的支持三、React 简单案例1、具体实现Hello 组件importReact,{Component}fromreact;// 同时导入默认导出和命名导出importhellofrom./index.module.css;exportdefaultclassHelloextendsComponent{render(){returnh3 className{hello.title}Hello React/h3;}}.title{background-color:orange;}Welcome 组件importReact,{Component}fromreact;import./index.css;exportdefaultclassWelcomeextendsComponent{render(){returnh3 classNametitleWelcome/h3;}}.title{background-color:skyblue;}App 组件importReact,{Component}fromreact;importHellofrom./components/Hello;importWelcomefrom./components/Welcome;exportdefaultclassAppextendsComponent{render(){return(divHello/Welcome//div);}}index.jsimportReactfromreact;// 引入 React 核心库importReactDOMfromreact-dom;// 引入 ReactDOMimportAppfrom./App;// 引入 App 组件// 渲染 App 组件到页面ReactDOM.render(App/,document.getElementById(root));2、CSS Moduleindex.css是普通的、全局的样式表在里面定义的类名一旦被引入到项目中就会成为全局的import./index.css;index.module.css是 CSS Module 样式表当导入这个文件时会得到一个对象需要通过这个对象的属性来使用类名importhellofrom./index.module.css;四、TodoList 案例Header 组件importReact,{Component}fromreact;import{nanoid}fromnanoid;importPropTypesfromprop-types;import./index.css;exportdefaultclassHeaderextendsComponent{staticpropTypes{addTodo:PropTypes.func.isRequired,};handleKeyUp(e){if(e.keyCode!13)return;constvaluee.target.value.trim();if(!value)return;consttodoObj{id:nanoid(),name:value,done:false};this.props.addTodo(todoObj);e.target.value;};render(){return(div classNametodo-headerinput onKeyUp{this.handleKeyUp}typetextplaceholder请输入你的任务名称按回车键确认//div);}}.todo-header input{width:560px;height:28px;font-size:14px;border:1px solid #ccc;border-radius:4px;padding:4px 7px;}.todo-header input:focus{outline:none;border-color:rgba(82,168,236,0.8);box-shadow:inset 0 1px 1pxrgba(0,0,0,0.075),0 0 8pxrgba(82,168,236,0.6);}Footer 组件importReact,{Component}fromreact;importPropTypesfromprop-types;import./index.css;exportdefaultclassFooterextendsComponent{staticpropTypes{todos:PropTypes.array.isRequired,checkAllTodo:PropTypes.func.isRequired,clearAllDone:PropTypes.func.isRequired,};handleCheckAll(event){this.props.checkAllTodo(event.target.checked);};handleClearAllDone(){this.props.clearAllDone();};render(){constdoneCountthis.props.todos.reduce((pre,todo)pre(todo.done?1:0),0);// 已完成个数consttotalCountthis.props.todos.length;// 总个数return(div classNametodo-footerlabelinput typecheckboxonChange{this.handleCheckAll}checked{doneCounttotalCounttotalCount!0?true:false}//labelspanspan已完成{doneCount}/span/全部{totalCount}/spanbutton onClick{this.handleClearAllDone}classNamebtn btn-danger清除已完成任务/button/div);}}.todo-footer{height:40px;line-height:40px;padding-left:6px;margin-top:5px;}.todo-footer label{display:inline-block;margin-right:20px;cursor:pointer;}.todo-footer label input{position:relative;top:-1px;vertical-align:middle;margin-right:5px;}.todo-footer button{float:right;margin-top:5px;}List 组件importReact,{Component}fromreact;importPropTypesfromprop-types;importItemfrom../Item;import./index.css;exportdefaultclassListextendsComponent{staticpropTypes{todos:PropTypes.array.isRequired,updateTodo:PropTypes.func.isRequired,deleteTodo:PropTypes.func.isRequired,};render(){const{todos,updateTodo,deleteTodo}this.props;return(ul classNametodo-main{todos.map((todo){returnItem key{todo.id}{...todo}updateTodo{updateTodo}deleteTodo{deleteTodo}/;})}/ul);}}.todo-main{margin-left:0px;border:1px solid #ddd;border-radius:2px;padding:0px;}.todo-empty{height:40px;line-height:40px;border:1px solid #ddd;border-radius:2px;padding-left:5px;margin-top:10px;}Item 组件importReact,{Component}fromreact;importPropTypesfromprop-types;import./index.css;exportdefaultclassItemextendsComponent{state{mouse:false};// 标识鼠标移入、移出staticpropTypes{id:PropTypes.string.isRequired,name:PropTypes.string.isRequired,done:PropTypes.bool.isRequired,updateTodo:PropTypes.func.isRequired,deleteTodo:PropTypes.func.isRequired,};handleMouse(flag){return(){this.setState({mouse:flag});};};handleCheck(id){return(event){this.props.updateTodo(id,event.target.checked);};};handleDelete(id){if(window.confirm(确定删除吗)){this.props.deleteTodo(id);}};render(){const{id,name,done}this.props;const{mouse}this.state;return(li style{{backgroundColor:mouse?#ddd:white}}onMouseEnter{this.handleMouse(true)}onMouseLeave{this.handleMouse(false)}labelinput typecheckboxchecked{done}onChange{this.handleCheck(id)}/span{name}/span/labelbutton onClick{()this.handleDelete(id)}classNamebtn btn-dangerstyle{{display:mouse?block:none}}删除/button/li);}}li{list-style:none;height:36px;line-height:36px;padding:0 5px;border-bottom:1px solid #ddd;}li label{float:left;cursor:pointer;}li label li input{vertical-align:middle;margin-right:6px;position:relative;top:-1px;}li button{float:right;display:none;margin-top:3px;}li:before{content:initial;}li:last-child{border-bottom:none;}App 组件importReact,{Component}fromreact;importHeaderfrom./components/Header;importListfrom./components/List;importFooterfrom./components/Footer;import./App.css;exportdefaultclassAppextendsComponent{state{todos:[{id:001,name:吃饭,done:true},{id:002,name:睡觉,done:true},{id:003,name:打代码,done:false},{id:004,name:逛街,done:false},],};addTodo(todoObj){const{todos}this.state;constnewTodos[todoObj,...todos];this.setState({todos:newTodos});};updateTodo(id,done){const{todos}this.state;constnewTodostodos.map((todoObj){if(todoObj.idid)return{...todoObj,done};elsereturntodoObj;});this.setState({todos:newTodos});};deleteTodo(id){const{todos}this.state;constnewTodostodos.filter((todoObj){returntodoObj.id!id;});this.setState({todos:newTodos});};checkAllTodo(done){const{todos}this.state;constnewTodostodos.map((todoObj){return{...todoObj,done};});this.setState({todos:newTodos});};clearAllDone(){const{todos}this.state;constnewTodostodos.filter((todoObj){return!todoObj.done;});this.setState({todos:newTodos});};render(){const{todos}this.state;return(div classNametodo-containerdiv classNametodo-wrapHeader addTodo{this.addTodo}/List todos{todos}updateTodo{this.updateTodo}deleteTodo{this.deleteTodo}/Footer todos{todos}checkAllTodo{this.checkAllTodo}clearAllDone{this.clearAllDone}//div/div);}}body{background:#fff;}.btn{display:inline-block;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;box-shadow:inset 0 1px 0rgba(255,255,255,0.2),0 1px 2pxrgba(0,0,0,0.05);border-radius:4px;}.btn-danger{color:#fff;background-color:#da4f49;border:1px solid #bd362f;}.btn-danger:hover{color:#fff;background-color:#bd362f;}.btn:focus{outline:none;}.todo-container{width:600px;margin:0 auto;}.todo-container .todo-wrap{padding:10px;border:1px solid #ddd;border-radius:5px;}index.jsimportReactfromreact;// 引入 React 核心库importReactDOMfromreact-dom;// 引入 ReactDOMimportAppfrom./App;// 引入 App 组件// 渲染 App 组件到页面ReactDOM.render(App/,document.getElementById(root));