react入门系列之使用create-react-app搭建一个todolist-dome。
安装create-react-app脚手架
- npm install -g create-react-app
创建你的todo-list项目
-
create-react-app todo-list
-
注意npm命名限制,项目名称不能含有大写字母。
清除项目中不必要的文件
-
src目录中的:App.css, App.test.js, logo.svg, serviceWorker.js文件
-
public目录中的: manifest.json文件
-
其中manifest.json,serviceWorker.js属于pwa的配置文件,有兴趣可以了解一下。
-
pwa 让网页写好以后,用户访问了一次之后,就可以把我们的网页当作app来用,可以不用联网。
-
在手机上或者电脑桌面上通过点击快捷方式来打开页面。
-
他的图标和快捷方式是通过public中manifest.json去定义的
之前删除的文件在剩下的文件有引入使用,现在将其删除
- App.js删除下列代码注释部分
import React, { Component }from 'react';
// import logo from './logo.svg';
// import './App.css';
class App extends Component {
render(){
return (
{
/*
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
*/
}
<div>
hello react
</div>
);
}
}
export default App;
- index.js 删除一下注释部分
import React from 'react';
import ReactDOM from 'react-dom';
// import './index.css';
import App from './App';
//import * as serviceWorker from './serviceWorker'; // pwa 引入
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
// serviceWorker.unregister(); // pwa 使用
- public中的index.html文件,删除以下注释部分代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<!--
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
-->
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>todo-list</title> <!-- 更改网页标题 -->
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
项目目录解析
-
yarn.lock -- 包锁文件
-
README.MD -- 项目介绍
-
package.json -- node的包文件
-
.gitignore -- 使用git的时候可以将不想上传到git的文件在此处标记
-
node_modules -- 项目依赖包
-
index.html 项目首页模版
-
favicon.ico 网页图标
-
index.js 入口js文件
-
app.js 万年老二组件
jsx语法
-
jsx语法规定,在return中的最外层只能含有一个标签。
-
为此你可以使用div也可以使用react提供的Fragment占位符,他其实也是一个组件。
-
在js中写html标签。就称为jsx语法
-
render()函数中return => html标签或者组件名加括号,组件必须以大写字母开头
-
jsx中使用变量,需要使用{}包裹
-
现在我们将App.js改造成如下代码。
-
通过npm run start 启动你的项目
import React, { Component, Fragment } from 'react';
class App extends Component {
render(){
return (
<Fragment>
<div >
<input/><button>提交</button>
</div>
<ul>
<li>react</li>
</ul>
</Fragment>
);
}
}
export default App;
react的响应式思想和事件绑定
定义数据
-
之前我们已经将todo-list的结构搭好了。现在需要实现功能。
-
不要直接操作dom
-
通过改变数据改变dom
-
此时我们需要准备两组数据,1组数据存储input里面的值,1组数据存储列表中的值。
-
如何定义数据?
-
一个类必定有一个构造函数,并且他是最先执行的函数,我们可以将数据存在这个构造函数中
-
constructor 即为App类的构造函数,构造函数中有state属性,他是用来存放这个类的变量的。
-
因此数据就定义在state中。
-
constructor 构造函数还有一个super()方法,他可以帮助App类调用他的父类(Component)的属性。
-
-
使用箭头函数不然this指向出错
-
只能通过setState方法去改变state中的变量
-
代码如下
import React, { Component, Fragment }from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
inputValue: '',// 用来存储 input框中的 value值。
list:[] // 用来存储 每一个li的 value值。
}
}
render(){
return (
<Fragment>
<div>
<input/>
<button>提交</button>
</div>
<ul>
<li>react</li>
</ul>
</Fragment>
);
}
}
export default App;
绑定数据
-
我们上面已经定义好了数据,现在将数据绑定到对应的dom上
-
这样方便我们通过数据改变dom
-
react jsx语法中,绑定数据到dom上,使用{} 包裹。 -- value =
-
列表数据绑定也是一样,在{}中写js表达式,我们可以通过es5的map函数遍历list数组获得item值,和他的下标
-
然后通过return返回一个li标签,返回之前,将item绑定到li的value值,将index作为li的key
-
注意的是,实际开发中将index作为key值是一个错误的做法。
import React, { Component, Fragment }from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
inputValue: '',// 用来存储 input框中的 value值。
list:['西瓜','苹果'] // 用来存储 每一个li的 value值。
}
}
render(){
return (
<Fragment>
<div>
<input value = {this.state.inputValue} />
<button>提交</button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return (
<li key={index}>
{item}
</li>
)
})
}
</ul>
</Fragment>
);
}
}
export default App;
react的事件绑定
-
我们现在需要绑定input的onChange事件,以此达到input的value值的变化和我们数据inputValue变化一致
-
react的事件绑定和原生的区别就是第二个单词首字母要大写,onChang
-
在onChange事件中绑定handleInputChange()方法,传入事件对象,获取target值,他的value就是当前input的value值
-
再通过this.setState()方法区改变组件中inputValue的值。
-
要注意的是,react中只能通过setState方法改变state中的属性。
import React, { Component, Fragment }from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
inputValue: '',// 用来存储 input框中的 value值。
list:['西瓜','苹果'] // 用来存储 每一个li的 value值。
}
}
handleInputChange = (e) => {
this.setState({
inputValue: e.target.value
})
console.log(e.target)
}
render(){
return (
<Fragment>
<div>
<input
onChange = {this.handleInputChange}
value = {this.state.inputValue} />
<button>提交</button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return (
<li key={index}>
{item}
</li>
)
})
}
</ul>
</Fragment>
);
}
}
export default App;
如何实现Todolis的增删功能
-
增加思路 在input框中输入要添加的字段,点击提交,提交绑定一个方法,将inputValue的值添加到list数组中
-
删除思路 在每一个li中绑定点击事件,在点击的时候,传入当前li的下标,在事件方法中通过下标删除list中的对应元素
-
要注意的时,在传入index的时候我尝试过直接在方法名后面传入,但是会报错,改在bind()方法中第二个参数传入,就没有问题了
-
state中的属性数据,只能通过this.setState({})方法修改,不能直接修改。
-
在使用splice的时候踩了一个坑,list = list.splice(index, 1)
-
这是一行错误代码,这样赋值,list等于是你删除的哪个元素组成的数组,splice会改变原数组,返回被删除的数组。无需再赋值
import React, { Component, Fragment } from 'react';
class App extends Component {
// 定义数据,一个类必定有一个构造函数,他是最先执行的函数
constructor(props){
super(props); // App继承Component类,所以要通过super(props)调用他的父类的属性
this.state = {
inputValue: '', // input框的value
list: [] // 列表的数据
}
}
handleInputChange = (e) => { // 使用箭头函数不然this指向出错
this.setState({ // 只能通过setState去改变state中的变量
inputValue : e.target.value
})
console.log(e.target)
}
// 增加方法
handleBtnClick = () => {
this.setState({
list : [...this.state.list, this.state.inputValue],
inputValue: ''
})
}
// 删除方法
handleItemDelet = (index) => {
console.log(index)
let list = [...this.state.list];
// list = list.splice(index, 1) // 这是一行错误代码,这样赋值,list等于是你删除的哪个元素组成的数组
list.splice(index, 1)
console.log(list)
this.setState({
list : [...list]
})
}
render(){
return (
<Fragment>
<div >
<input
value = { this.state.inputValue } // 数据相应绑定
onChange = { this.handleInputChange } // 事件绑定
/>
<button
onClick = { this.handleBtnClick }
>提交</button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return <li key={index} onClick = { this.handleItemDelet.bind(this, index) }>{item}</li> // 实际编程之中使用index做key值是一个非常不好的习惯
})
}
</ul>
</Fragment>
);
}
}
export default App;
jsx细节补充
-
注释用大括号包裹,单行注释要换行
-
添加样式使用 className ,class会被解析成类
-
dangerouslySetInnerHTML =
-
dangerouslySetInnerHTML = {{ __html: item }} 多一个花括号表示一个js对象
-
// 如果需要显示input框中内容中的标签效果 就这么写。但是这样写容易造成xss攻击 对应的标签中就不需要写item了
-
lable标签 使用htmlFor引入到指定标签中
render(){
return (
<Fragment>{/* 他其实是一个组件 */ }
<div >
<lable htmlFor = "insertArea">输入内容</lable>
<input
id = 'insertArea'
className = 'input'
value = { this.state.inputValue } // 数据相应绑定
onChange = { this.handleInputChange } // 事件绑定
/>
<button
onClick = { this.handleBtnClick }
>提交</button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return (
<li
key={index}
onClick = { this.handleItemDelet.bind(this, index) }
dangerouslySetInnerHTML = {{ __html: item }} // 如果需要显示input框中内容中的标签效果 就这么写。但是这样写容易造成xss攻击
>
</li>) // 实际编程之中使用index做key值是一个非常不好的习惯
})
}
</ul>
</Fragment>
);
}