这个文章写的比较详细一些,是关于react项目的初学从基础创建到项目的实战,望业界大佬不要嘲笑才好。本人呢!在学习中如有什么错点,可以指出来呀,我才能吸取教训多多改正啊,望共进步!如果有辛给你们带来帮助,我也会很高兴滴!文章会实时更新
下载一个react项目
React的优点:提升开发效率、代码可维护性和可阅读性增强-
需提前检测npm 和node 因为本人配置了,所以就也不演示和解释了
npm install -g create-react-app 下载脚手架工具
create-react-app my-app 下载项目 目录
cd my-app 进行到当前文件夹下
npm start 开启项目
项目目录讲解
1.yarn.lock文件 主要是项目的安装包做的一些版本号限制 无效动它(理解是什么即可)
2.README.md 这个文件是项目的说明文件 关于项目的说明 可以通过markdown语法自己写一些对项目的说明
3.package.json 属于node的包文件,关于配置,包,指令调用
4.gitignore 用git上传管理包文件的时候,做一些不想上传的包文件,即可定义在这里面
5.node_modules 环境的配置文件第三方的 node的包文件
6.public
(1) favicon.ico 打开文件,网页浏览器左上方的文件图标
(2)ndex.html 首页的html模板 manifest title网页浏览器的标题 noscript 这个是容错标签增加健壮性
(3) manifest.json
PWA serviceWorker 一起连用 定义和它是配套的
定义快捷方式的图标,网址,主题颜色
7.src 放置所有项目的源代码
(1)index.js 整个程序的 源代码的入口文件
import * as serviceWorker from ‘./serviceWorker’
serviceWorker 通过写网页的形式,做一些移动端的app应用
// 效果是:例如上线到一台具备https协议的服务器上,用户的第一次访问是必须联网的,但是一旦用户访问的页面断网了。当第二次访问的时候,会加载到第一次访问数据的那个时候
(2)App.text.js 自动化测试文件
(3)App.js 内容的编译
react组件
1.组件化 自行看对比图理解组件划分
1.index.js
ReactDOM.render(
//jsx
<App />,
// App 挂载到index.html主文件内ID名为root的节点下 就是index.html的div内展示组件内容
document.getElementById('root')
);
2.App .js 组件
// 因为是个组件所以要引入react compontent
//用jsx是会解析当前代码的
import React,{ Component,Fragment } from 'react';
// es6
// import { Component } from 'react'
// 等价于
// const Component = React.Component
class App extends Component {
render(){
//jsx
return (
<Fragment>
<div className="App">hello</div>
<p>hello</p>
</Fragment>
);
}
}
export default App; //导出
最基础的jsx语法
- JSX语法中,如果我们要使用自己创建的组件 直接通过标签形式来直接使用即可(附加解释和vue相类似)
- JSX语法中,使用自己的组件,组件名的开头必须是大写英文字母 小写的一般都是原始标签
- 需要在组件内容内有一个最大的div包裹元素 Fragment占位符 替换div
- 在JSX内写注释需要用花括号进行一个包裹
- dangerouslySetInnerHTML={{__html: item}} 标签内容进行的转义
- 循环时 标签上的key={ index }
- import ‘./style.css’ css的引用 className 进行定义class类名
- label 区域聚光标的for 在react中for 就为htmlFor input引用,那么input的ID就为htmlFor的命名
- 事件为on然后字母大写
项目的最基本就已经完成了
接下来可以做一个TodoList的简单案例试一下
import React, { Component, Fragment } from 'react'
import './style.css'
// Fragment 替换div 占位符
class TodoList extends Component {
// constructor最优先执行的一个函数
constructor (props) {
super(props)
// this.state 组件的状态
this.state = {
inputValue: '',
list: []
}
}
render () {
return (
<Fragment>
<div>
{/* label 一定范围内聚焦 在react中for 就为htmlFor 事件为on然后字母大写 */}
<label htmlFor='inserAea'>
输入内容
</label>
<input
id='inserAea'
className='input'
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)} />
<button onClick={this.handleBtnClick.bind(this)}>
提交
</button>
</div>
<ul>
{/* dangerouslySetInnerHTML 输入框内的标签不显示,防XSS攻击的可能 第一个花括号意为花括号内一定跟着一个js的表达式 第二个花括号意为着js的对像 */}
{this.state.list.map((item, index) => {
return (
<li key={index} onClick={this.handleItemDelete.bind(this, index)} dangerouslySetInnerHTML={{__html: item}}>
</li>
)
})}
</ul>
</Fragment>
)
}
handleInputChange (e) {
// 获取state内的值 需要用setState进行获取
// this指向问题可以用bind来进行改变即可
this.setState({
inputValue: e.target.value
})
}
handleBtnClick () {
// ...this.state.list 展开运算符 把以前数据的内容展开,然后产生一个全新的数组
this.setState({
list: [...this.state.list, this.state.inputValue],
// 并且清空inputValue
inputValue: ''
})
}
handleItemDelete (index) {
// state 不允许直接修改任何内容,可以拷贝出来进行修改后附值
const list = [...this.state.list]
list.splice(index, 1)
this.setState({
list: list
})
}
}
export default TodoList
进行TodoList的拆分
- 父组件向子组件 传值属于运用属性的形式进行传值 既可以传递数据,又可以传递方法 例如content={item} deleteItem={this.handleItemDelete.bind(this)} bind(改变this指向的)
- 子组件接受通过this.props.(传递的值)来接收 当要改变父组件方法的this指向做一次绑定
父组件 TodoList
import React, { Component, Fragment } from 'react'
import TodoItem from './TodoItem'
import './style.css'
class TodoList extends Component {
constructor (props) {
super(props)
this.state = {
inputValue: '',
list: []
}
}
render () {
return (
<Fragment>
<div>
<label htmlFor='inserAea'>
输入内容
</label>
<input
id='inserAea'
className='input'
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)} />
<button onClick={this.handleBtnClick.bind(this)}>
提交
</button>
</div>
<ul>
{this.state.list.map((item, index) => {
return (
<div>
<TodoItem content={item} index={index} deleteItem={this.handleItemDelete.bind(this)} />
</div>
)
})
}
</ul>
</Fragment>
)
};
handleInputChange (e) {
this.setState({
inputValue: e.target.value
})
};
handleBtnClick () {
this.setState({
list: [...this.state.list, this.state.inputValue],
inputValue: ''
})
};
handleItemDelete(index){
const list=[...this.state.list];
list.splice(index,1);
this.setState({
list:list
})
}
}
export default TodoList
子组件 接收父组件传值TodoItem
import React , { Component } from 'react'
class TodoItem extends Component {
constructor(props){
super(props);
this.handleClick=this.handleClick.bind(this);
}
render () {
return (
<div onClick={this.handleClick}>
{this.props.content}
</div>
)
}
handleClick () {
this.props.deleteItem(this.props.index)
}
}
export default TodoItem
优化代码
父组件
import React, { Component, Fragment } from 'react'
import TodoItem from './TodoItem'
import './style.css'
class TodoList extends Component {
constructor (props) {
super(props)
this.state = {
inputValue: '',
list: []
}
// this的指向放在顶部即可
this.handleInputChange = this.handleInputChange.bind(this)
this.handleBtnClick = this.handleBtnClick.bind(this)
this.handleItemDelete = this.handleItemDelete.bind(this)
}
render () {
return (
<Fragment>
<div>
<label htmlFor='inserAea'>
输入内容
</label>
<input
id='inserAea'
className='input'
value={this.state.inputValue}
onChange={this.handleInputChange} />
<button onClick={this.handleBtnClick}>
提交
</button>
</div>
<ul>
{this.getTodoItem()}
</ul>
</Fragment>
)
}
// 简洁代码化
getTodoItem () {
return this.state.list.map((item, index) => {
return (
<div key={index}>
<TodoItem content={item} index={index} deleteItem={this.handleItemDelete} />
</div>
)
})
}
handleInputChange (e) {
// 使用函数形成异步,就对value值进行一个保存附值
const value = e.target.value
this.setState(() => ({
inputValue: value
}))
}
handleBtnClick () {
// prevState 等价于之前接收的原数据
this.setState((prevState) => ({
list: [...prevState.list,prevState.inputValue],
inputValue: ''
}))
// this.setState({
// list: [...this.state.list, this.state.inputValue],
// inputValue: ''
// })
}
handleItemDelete (index) {
// 可以形成一个函数体在函数体内做改变
this.setState((prevState) => {
const list = [...prevState.list]
list.splice(index, 1)
return {list}
})
// this.setState({
// list: list
// })
}
}
export default TodoList
子组件
import React , { Component } from 'react'
class TodoItem extends Component {
constructor (props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
render () {
const { content } = this.props
return (
<div onClick={this.handleClick}>
{content}
{/* {this.props.content} */}
</div>
)
}
handleClick () {
const { deleteItem, index } = this.props
deleteItem(index)
// this.props.deleteItem(this.props.index)
}
}
export default TodoItem
围绕react衍生出的思考
- 声明式开发 减少了代码量
- 可以与其它框架并存
- 组件化
- 单项数据流 父组件可以向子组件传数据,但子组件绝对不能更改父组件的数据
- 视图层框架 大型项目时,就需要引入redux数据层框架
- 函数式编程
安装React 开发调试工具
1.打开浏览器
2.浏览器右侧的三个点点开
3.点击 更多工具
4.点击 扩展程序
5.点击左上角扩展程序
6.点击最下面的Chrome网上应用店
7.需要打开翻墙软件
8.搜索react 出现React Developer Tools 点击安装
PropTypes与DefaultProps
也可自行观看文档进行理解
需要引入propTypes
import PropTypes from 'prop-types'
// 意思就是对TodoItem这个组件对父组件传递过来的值做的一个属性的检验 isRequired表示检测父组件向子组件传值也必须传递
TodoItem.propTypes = {
test: PropTypes.string.isRequired,
// 可以是字符串类型也可以是数字类型
// 多种验证时 PropTypes.arrayOf(PropTypes.string, PropTypes.number) 表示数组的组成或者怎么怎么样
// oneOfType 表示以下类型中的一个 PropTypes.arrayOf([PropTypes.string, PropTypes.number])
content:PropTypes.string,
deleteItem: PropTypes.func,
index: PropTypes.number
}
const { content,test } = this.props
// 组件内部定义的默认值
TodoItem.defaultProps = {
test: 'hello world'
}
Props,state与render函数的关系
// 当组件的state或者props发生改变的时候,render函数就会重新的执行
//1. 当父组件的render函数被运行时,它的子组件的render都将被重新运行一次
//2.子组件的props接收也发生了改变
什么是虚拟DOM?
虚拟DOM中的Diff算法
React中ref的使用
之前用e.target来获取DOM元素可以使用ref来获取
ref={(input)=>{this.input=input}} 添加在需要获取值的标签上
例子:
<ul ref={(ul) => { this.ul = ul}}>
{this.getTodoItem()}
</ul>
handleBtnClick () {
// this.setState是异步的,但是它提供了一个第二参数的函数内
this.setState((prevState) => ({
list: [...prevState.list, prevState.inputValue],
inputValue: ''
}), () => {
// querySelectorAll('div') 查找所有的div元素
console.log(this.ul.querySelectorAll('div').length)
})
}
Reac的生命周期函数
- 生命周期函数指在某一个时刻组件会自动调用执行的函数
- render
- construtor 不是react 独有的,是es6附带的 不归类到react生命周期内
// 在组件即将挂载到页面的时刻自动执行
componentWillMount () {
console.log('componentWillMount')
}
// 在组件被挂载到页面之后,自动被执行
componentDidMount () {
console.log('componentDidMount')
}
// 在组件被更新之前,自动被执行 要求返回结果 是一个布尔值 true或者false
shouldComponentUpdate () {
console.log('shouldComponentUpdate')
return true
}
// 组件被更新之前它会自动执行,但是他在shouldComponentUpdate之后被执行
// 如果 shouldComponentUpdate返回true它才执行 返回false就不会被执行
componentWillUpdate () {
console.log('componentWillUpdate')
}
// 在有props参数的时候才会被执行
// 一个组件要从父组件接受参数
// 如果这个组件第一次存在于父组件中,不会执行
// 如果这个组件之前已经存在于父组件中,才会执行
componentWillReceiveProps () {
console.log('componentWillReceiveProps')
}
// 当这个组件即将被从页面中剔除的时候,才会被执行
componentWillUnmount () {
console.log('componentWillUnmount')
}
React生命周期函数的使用场景
当数据提交时才会改变值 性能的优化
shouldComponentUpdate (nextProps, nextState) {
if (nextProps.content !== this.props.content) {
return true
}else {
return false
}
}
接口的调用 可以下载axios组件
npm install axios
componentDidMount(){
axios.get('/api/todolist').then(()=>{
alert('12')
}).catch(()=>{
alert('235')
})
}