React简介虚拟DOMDiff算法创建React项目JSX语法组件组件声明方式组件传值props和state组件的生命周期
Posted 苦海123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React简介虚拟DOMDiff算法创建React项目JSX语法组件组件声明方式组件传值props和state组件的生命周期相关的知识,希望对你有一定的参考价值。
React简介:
前面只是简单介绍移动APP开发,后面还会继续深入介绍移动app开发;其中想要用ReactNative开发出更出色的应用,那么就得学好React,下面将介绍React:
React 是一个由 Facebook 开发用于构建用户界面的渐进式 javascript 库,其特点:声明式设计、高效、灵活、JSX、组件化、单向数据流。
React也是组件化的,与vue不同的是:React直接使用JS代码编写组件(结构、样式、逻辑混合在js代码中)。
React是前端三大框架中诞生最早的框架,社区庞大,技术团队实力雄厚。
虚拟DOM:
操作原生DOM是一件非常耗性能的事,操作虚拟DOM就不会那么耗性能了,操作虚拟DOM时采用Different算法,只更新变化的虚拟DOM部分;虚拟DOM指通过程序员手动模拟出来类似原生DOM的对象,如下面模拟一个带有链接的p元素:
var p = {//定义一个对象
tagName:'p',//标签名为:p
attrs:{//定义属性:
class:'font16'
},
children:[//定义内容,相当于innerText
'跳转到:',
{//在父标签的children中再定义以对象,其方法和定义p一样:
tagName:'a',//标签名为:a
attrs:{//属性:
href:'https://www.baidu.com',
},
children:[//定义内容:
'百度'
]
}
]
}
Diff算法:
Diff算法用于对比新旧虚拟DOM的算法,其中有三部分:tree diff、component diff、element diff,其区别:
tree diff :新旧DOM树逐层对比的方式,对比所有层节点来找到被更新的节点后修改旧DOM。
component diff:组件之间的对比,当对比组件的时候,如果两个组件的类型相同,则这个组件暂时不需要被更新,如果组件的类型不同,则立即移除旧组件,新建一个组件,替换到被移除的位置。
element diff:组件中每个元素之间的对比。
使用虚拟DOM创建React项目(导入资源型):
使用React开发项目时,必须安装两个包:react、react-dom;react是用来创建组件、组件生命周期等。react-dom用来操作DOM。创建react项目步骤:
//1.新建一个项目文件夹,并在文件夹打开终端键入:npm init -y 初始化一个package.json文件
//2.终端输入:cnpm install react react-dom --save 安装react和react-dom到运行里,ReactNative开发中不建议使用cnpm装包
//3.新建src文件夹并新建main.js文件并引入:react和react-dom:开始编写react中js主文件(并新建index.html文件,文件中留渲染的div)
import React from 'react';
import ReactDOM from 'react-dom';
//4.在main.js文件中使用React提供的API操作元素:
//4-1使用React.createElement()创建一个虚拟DOM,接收至少三个参数:参数1:字符串(标签类型),参数2:对象(标签属性),参数3开始:当前元素子节点,可放多个虚拟dom,如:
var mydiv = React.createElement('div',{class:'mydiv',id:'box'},'这是一个div元素');//<div class='mydiv' id='box'>这是一个div元素</div>
var spanp = React.createElement('span',{class:'spans'},'被span标签包裹的文本');//<span class='spans'>被span标签包裹的文本</span>
var textp = React.createElement('p',{class:'tp'},'p标签文本中',spanp);//<p class='tp'>p标签文本中<span class='spans'>被span标签包裹的文本</span></p>
// 4-2使用ReactDOM.render()将虚拟DOM渲染到页面中,参数1:渲染DOM内容,参数2:渲染的dom元素位置(获取DOM的方式),如:
ReactDOM.render(mydiv,document.getElementById('app'));//这里app表示index.htlm文件中一个id值为app的标签,如<div id='app'></div>
//4.webpack打包构建后,在dist目录下的文件是正常可以访问的。
JSX:
不难发现使用js创建元素的方式是非常繁琐的,因此这里介绍一款可以解决这个问题语法:JSX;
HTML 语言直接写在 JavaScript 语言中,不加任何引号,这就是 JSX 语法,它允许 HTML 与 JavaScript 的混写;
它是 一种 JavaScript 的语法扩展, 我们推荐在 React 中使用 JSX 来描述用户界面,JSX 是在 JavaScript 内部实现的;元素是构成 React 应用的最小单位,JSX 就是用来声明 React 当中的元素(底层实际就是通过上面js创建元素的)使用JSX语法时首先要安装:(cnpm install babel-preset-react -D)并配置在.babelrc文件中,babel-preset-react 用来转换JSX代码。(注意新版本:babel-preset-react-app,此环境应该基于上面环境)
与浏览器的 DOM 元素不同,React 当中的元素事实上是普通的对象,如:
// 注意:想要正常运行JSX语法:还需要以下两步:
//1.在项目目录下新建:.babelic文件,其配置代码如下:
{
"presets": ["env", "stage-0", "react"],
"plugins": ["transform-runtime"]
}
//2.cnpm i babel-preset-env babel-preset-stage-0 babel-plugin-transform-runtime --save ,下载上面配置依赖的包。
//3.配置完以上环境后,应该使用webpack打包后才可以以files的方式打正常访问react项目
import React from 'react';
import ReactDOM from 'react-dom';
var titles = '这是一个提示';
var elements =
<div>{/* JSX语法中允许有一个根节点,根节点中可以嵌套其它元素 */}
<p title={titles}>hello</p>{/*JSX中使用变量用{}包裹,实际指使用js语法时用{}包裹*/}
<a href="#">hello</a>
<p className='textp'>hello</p>{/*在JSX中使用className代替class属性,因为class在js中只一个关键字*/}
<label htmlFor="">hello</label>{/* 在JSX中for使用htmlFor代替 */}
</div>
const box = document.getElementById('box');
ReactDOM.render(elements,box);
//总结:
//1.当编译时遇到尖括号<>当JSX执行,当遇到大括号{}当js执行
//2.JSX语法中使用className代替class属性
//3.在JSX中for使用htmlFor代替
//4.jsx语法中只能使用一个根元素
//5.jsx中数组会自动展开
JSX 中注释:
写法一:
{
// 注释
// ...
}
写法二(单行推荐):
{/* 单行注释 */}
写法三(多行推荐):
{
/*
* 多行注释
*/
}
JSX 不同写法:
Babel 会把 JSX 通过React.createElement() 函数编译,每个 React 元素都是一个真实的 JavaScript 对象;下面几种方式是等价的,如:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};
const element = {
tagName:'h1',
attrs:{className: 'greeting'},
children:['Hello, world']
}
组件:
React 允许将代码封装成组件,然后像插入普通 HTML 标签一样,在网页中插入这个组件即可使用;组件规则注意事项:组件名的第一个首字母必须大写,class声明式组件必须有 render 方法,组件内必须有且只有一个根节点,组件的属性可以在组件内通过props 获取(函数需要传递参数:props;类直接通过: this.props),如:
函数式声明组件(无状态):
名字不能用小写,React 在解析的时候,是以标签的首字母来区分的,如果首字母是小写则当作 HTML 来解析,如果首字母是大写则当作组件来解析。
// 函数名大写,组件名为函数名
// 函数式声明组件,必须有return关键字,即使没内容也应该:return null
function Header(props){
return (//当返回多行代码时,建议使用小括号括起来
<div>
<p>这是header组件{props.name}</p>{/* 这里直接通过{属性名}的方式是不能拿到属性值的,需要给函数传递一个参数如:props等,在通过这个参数点出属性:{props.name},且这些属性只读 */}
</div>
)
}
const box = document.getElementById('box');
// 通过 <函数名/> 的方式定义组件名;组件中传递参数使用属性的方式,如:name={变量名},age={变量名};分开传递参数很不方便,可以使用es6中属性扩散语法传递一个对象达到同样效果:
var obj = {
name:'jack',
age:'28',
gender:'男'
}
var cont = <div>
<Header {...obj}/> {/* es6中属性扩散语法 */}
</div>
ReactDOM.render(cont,box);
抽离组件:
上面组件声明在同一个js文件中,没有达到减少主文件代码量的问题,想要减少主文件代码量,就得将组件抽离出来,如:
// 被抽离的组件header.js文件:(扩展:组件可以使用jsx后缀名,但是需要在webpack.config.js文件中配置解析jsx文件的loader:loader和js一样,可连写为:( js|jsx)$ 、jsx?$ )
import React from 'react';//导入React,注意:React首字母必须大写
function Header(props){
return (
<div>
<p>这是header组件{props.name}</p>
</div>
)
}
// 抽离组件需要暴露出:
export default Header;//或直接在函数或类前面加 export default;
// 在主文件(main.js)中使用这个组件:
// 导入组件:
import Header from './components/header.js';
// 在主文件中使用 <Header><Header/> (简写:<Header/>)使用组件即可
类方式声明组件(有状态):
// 通过class定义一个Header组件,extends 继承了React中Component
class Header extends React.Component {
render() { //使用render函数,函数中返回组件类容:
return (
<div>
<p>这是header组件{this.props.names}</p>{/*class声明的组件拿传递的值通过:this.props点属性名,也是只可读的*/}
</div>
)
}
};
ReactDOM.render(<Header names='jack'/>, document.getElementById('box'));
// 抽离组件的方式和函数声明组件的方式中一样
组件传值props和state:
state 和 props区别在于props 是不可变的,而 state 可以改变。函数式组件只能通过 props 来传递数据,不能通过state传值;class定义的组件都可以使用它们传值。
class Header extends React.Component {
constructor(props){//class定义的类中如果实现了继承,默认就有constructor函数,只是看不见;当写出这个构造函数时,要通过super调用:
super(props);//表示父类的构造函数
console.log(props);//在constructor中是不能直接使用props的,想要使用,就得在constructor中传递props
// constructor中的this.state表示私有数据对象,类似vue中data(){},this.state中定义的数据是可改变的,通过this.state点属性拿到,如:
this.state={
messages:'hello',
names:'jack'
}
}
render() {
return (
<div>
<p>这是header组件{this.props}</p>
<p>这是header组件{this.state.messages}</p>
<button onClick={this.change}>变更messages的值</button>{/* jsx中使用事件时,必须使用驼峰命名法,且处理函数名前面加this. */}
</div>
)
}
//React函数中this默认指向undefined,若想this指向class类,可以使用箭头函数:如:
// change(){
// this.state.messages = '修改hello为word';
// }
change=()=>{//这里是将箭头函数赋值给change,箭头函数中this依旧指向class类
// this.state.messages = '修改hello为word';//通过this.state方式修改数据只是单向的,内存中数据是修改了,但是页面没有自动刷新,因此也不推荐,推荐setState()方法异步修改,如
// this.setState({//setState()方法中传递一个对象,对象中传入要修改的属性,如:
// messages:'word',
// names:'lucky'
// })
// this.setState(function(prevState,props){//setState也支持传递一个函数,但是函数必须return一个对象;函数中第一个参数表示修改数据之前的数据,第二个参数是外界传递过来的数据属性
// return {
// messages:'word'
// }
// })
this.setState(function(prevState,props){//setState()因为setState是异步修改数据的,想要确保拿到最新的数据,那么,可以给setState继续传递一个参数为:回调函数,在回调函数里面拿到的数据是比较保险的,如:
return {
messages:'word'
}
},function(){
console.log(this.state.messages);
})
}
};
ReactDOM.render(<Header/>, document.getElementById('box'));
// 抽离组件的方式和函数声明组件的方式中一样
组件的生命周期:
组件从创建到运行再到销毁,这期间伴随着各种各样的事件,这些事件统称为组件的生命周期函数;
组件生命周期分为三部分:组件创建阶段(命周期函数一生只创建一次)、组件运行阶段(生命周期函数根据props和state的状态是否改变运行0-N次)、组件销毁阶段(生命周期函数一生只执行一次)。
import React from 'react';
import ReactDOM from 'react-dom';
import ReactTypes from 'prop-types';//导入数据校验模块
class Header extends React.Component {
constructor(props){
super(props);
//1.使用:this.state={}初始化组件私有状态,初始化组件私有数据
this.state={
messages:'hello',
numstate:props.num//因为props中的值时只读的,想要修改值,就得使用state存值,但是state存的值是固定的,因此这里可以传递一个动态的值,即props的值,此时页面的值应该为对应的state值。
}
}
//2.使用:static defaultProps = {}定义默认的值供程序正常运行,如:
static defaultProps = {
num:0
}
//3.使用:static propTypes = {}做数据校验,防止传过来的数据无效,如:
static propTypes = {//特别注意:做校验需要安装一个包prop-types: npm install prop-types ,并在开头导入
num:ReactTypes.number//如果需要检验其他数据类型,可阅读prop-types原文;当做完数据校验时,在组件中传递过来的值类型不符合时会报警告
}
// 4.使用:componentWillMount(){}组件即将被挂载到页面时触发函数,虚拟DOM也没创建(不能被拿到),因此此页面不能操作页面,可以操作数据,如:
componentWillMount(){
console.log(this.state.messages);//数据可被拿到
console.log(this.props.num);//数据可被拿到
this.myFunction();//函数可被调用
console.log(document.getElementById('testp'));//不能被获取
}
// 5.使用:render(){}即将渲染内存中的虚拟DOM,该函数执行完后内存中已经渲染好数据了,但是还没有被挂载到页面上,如:
render() { //render中不能使用this.setState方法,否则会进入死循环
return (
<div>
{/* <p id='testp'>num的值:{this.props.num}</p> */}
<p id='testp' onClick={this.addnumstate}>num的值自增为:{this.state.numstate}</p>{/*修改为可改变的state值*/}
<span ref='spans'>hello</span>{/* 利用refs属性可以快速获取到该节点 */}
</div>
)
}
// 6.使用:componentDidMount(){}页面已经有可见的DOM元素了,此时可以拿到render中的DOM,如:
componentDidMount(){
console.log(document.getElementById('testp'));//可以获取DOM元素,可对其进行操作(原生DOM型,但是需要注意this指向问题,可使用箭头函数,但是不推荐原生方式),如:
// document.getElementById('testp').οnclick=()=>{
// this.setState({
// numstate:this.state.numstate++
// })
// }//推荐React中事件绑定的方式,如testp标签中
}
//7.使用:conmponentWillReceiveProps(){}当子组件的属性props改变时触发此方法,但是第一次渲染时不会被触发,如:
conmponentWillReceiveProps(nextProps){//这里的数据是旧的,但是通过第一个参数nextProps.属性名,拿到的是最新的数据
console.log('当外界传递过来新的props时,才会触发该事件执行');
console.log(next.Props.num);
}
addnumstate=()=>{//使用箭头函数改变this指向为类的实例;注意:在dom中掉用次函数时在函数名前加this
this.setState({
numstate:this.state.numstate++
})
}
myFunction(){
console.log('测试componentWillMount是否可以调用外部函数');
}
// 以上是组件创建阶段生命周期,下面将介绍组件运行阶段生命周期:
// 1.使用:shouldComponentUpdate(){}里面return一个布尔值,判断是否要更新页面上数据,当为true时可以改变页面上数据,当为false时不能改变页面上的数据;无论是true或false,内存中数据都是改变重新渲染了
shouldComponentUpdate(nextProps,nextState){//此方法中可传入两个参数:第一个表示最新的props,第二个表示最新的state
console.log(nextState.numstate);
return true;//这里应该使用传入参数做出逻辑后return 布尔值
}
// 2.当shouldComponentUpdate中返回false时,会重新返回到shouldComponentUpdate执行,若果返回的是true,则执行下面的:componentWillUpdate(){}将要更新数据,此时页面和内存中数据都未被更新,如:
componentWillUpdate(){//此时页面上数据都是旧的
console.log(this.refs.spans.innerHTML);//使用refs获取ref设置的值
}
// 3.这里有一个运行时的render方法,但是里面数据h还是旧的。
// 4.使用:componentDidUpdate(){}更新了页面和内存中的数据,此时数据都是最新的,如:
componentDidUpdate(){//此时页面上数据都是新的
console.log(this.refs.spans.innerHTML);
}
};
ReactDOM.render(<Header num='校验时传入无效数据时,会报警告'/>, document.getElementById('box'));
对上面组件生命周期流程总结如下图:
提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:810665436@qq.com联系笔者删除。
笔者:苦海
以上是关于React简介虚拟DOMDiff算法创建React项目JSX语法组件组件声明方式组件传值props和state组件的生命周期的主要内容,如果未能解决你的问题,请参考以下文章