React框架
React的起源和发展
-
起初facebook在建设instagram(图片分享)的时候,因为牵扯到一个东西叫数据流,那为了处理数据流并且还要考虑好性能方面的问题,Facebook开始对市场上的各种前端MVC框架去进行一个研究,然而并没有看上眼的,于是Facebook觉得,还是自己开发一个才是最棒的,那么他们决定抛开很多所谓的“最佳实践”,重新思考前端界面的构建方式,他们就自己开发了一套,果然大牛创造力还是很强大的。
-
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC
框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。
React的出发点
- 基于HTML的前端界面开发正变得越来越复杂,其本质问题基本都可以归结于如何将来自于服务器端或者用户输入的动态数据高效的反映到复杂的用户界面上。而来自Facebook的React框架正是完全面向此问题的一个解决方案,按官网描述,其出发点为:用于开发数据不断变化的大型应用程序(Building
large applications with data that changes over
time)。相比传统型的前端开发,React开辟了一个相当另类的途径,实现了前端界面的高性能高效率开发。
React与传统MVC的关系
-
轻量级的视图层库!A JavaScript library for building user interfaces
-
React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式;React 构建页面
UI 的库。可以简单地理解为,React 将将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,就成了我们的页面。
React高性能的体现:虚拟DOM
React高性能的原理:
-
在Web开发中我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。
-
React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM
API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event
Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A-B,B-A,React会认为A变成B,然后又从B变成A
UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。 -
尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,部而对实际DOM进行操作的仅仅是Diff分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。
React Fiber:
- 在react 16之后发布的一种react 核心算法,React
Fiber是对核心算法的一次重新实现(官网说法)。之前用的是diff算法。
在之前React中,更新过程是同步的,这可能会导致性能问题。
- 当React决定要加载或者更新组件树时,会做很多事,比如调用各个组件的生命周期函数,计算和比对Virtual
DOM,最后更新DOM树,这整个过程是同步进行的,也就是说只要一个加载或者更新过程开始,中途不会中断。因为JavaScript单线程的特点,如果组件树很大的时候,每个同步任务耗时太长,就会出现卡顿。 - React
Fiber的方法其实很简单——分片。把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。
React的特点和优势
1.虚拟DOM ==> 高性能
- 我们以前操作dom的方式是通过document.getElementById()的方式,这样的过程实际上是先去读取html的dom结构,将结构转换成变量,再进行操作
- 而reactjs定义了一套变量形式的dom模型,一切操作和换算直接在变量中,这样减少了操作真实dom,性能真实相当的高,和主流MVC框架有本质的区别,并不和dom打交道
2.组件系统 ===> 高效率
-
react最核心的思想是将页面中任何一个区域或者元素都可以看做一个组件 component
那么什么是组件呢?
组件指的就是同时包含了html、css、js、image元素的聚合体
- 使用react开发的核心就是将页面拆分成若干个组件,并且react一个组件中同时耦合了css、js、image,这种模式整个颠覆了过去的传统的方式
3.单向数据流
- 其实reactjs的核心内容就是数据绑定,所谓数据绑定指的是只要将一些服务端的数据和前端页面绑定好,开发者只关注实现业务就行了
4.JSX 语法
- 在vue中,我们使用render函数来构建组件的dom结构性能较高,因为省去了查找和编译模板的过程,但是在render中利用createElement创建结构的时候代码可读性较低,较为复杂,此时可以利用jsx语法来在render中创建dom,解决这个问题,但是前提是需要使用工具来编译jsx
构建React简易环境
react开发需要引入多个依赖文件:react.js、react-dom.js,分别又有开发版本和生产版本
react.js中有React对象,帮助我们创建组件等功能
react-dom.js中有ReactDOM对象,渲染组件的虚拟dom为真实dom的爆发功能
在编写react代码的时候会大量的使用到jsx代码,但是需要编译:
- 浏览器端编译,通过引入browser、babel等对引入的script内的代码做编译 – 浏览器
- 利用webpack等开发环境进行编译,将编译好的文件引入到应用中
<!--引入react的核心包-->
<script src="./js/react.development.js"></script>
<!--引入开发web的包-->
<script src="./js/react-dom.development.js"></script>
<!--引入解析jsx的包-->
<script src="./js/babel.js"></script>
<script type="text/babel">
ReactDOM.render(<h2>hello world</h2>,
document.getElementById("box"))
</script>
元素与组件
如果代码多了之后,不可能一直在render方法里写,所以就需要把里面的代码提出来,定义一个变量,像这样:
import React from 'react'
import ReactDOM from 'react-dom'
// 这里感觉又不习惯了?这是在用JSX定义一下react元素
const app = <h1>欢迎进入React的世界</h1>
ReactDOM.render(
app,
document.getElementById('root')
)
函数式组件
这里我们定义的方法实际上也是react定义组件的第一种方式-定义函数式组件,这也是无状态组件。
import React from 'react'
import ReactDOM from 'react-dom'
/*
const 组件名(首字母大写)=(props)=>{
return jsx表达式
}
*/
const App = (props) => {
return (
<h1>欢迎进入{props.name}的世界</h1>
)
}
ReactDOM.render(
// React组件的调用方式
<App name={"react"} />,
document.getElementById('root')
)
这样一个完整的函数式组件就定义好了。但要注意!注意!注意!组件名必须大写,否则报错。
通过函数式组件实现切换显示隐藏
var arr = ["a","b","c"]
var visible = true;//默认List是显示的
const List = props => { //通过props参数就可以获取到父组件App传递过来的arr属性
return (
//<ul style={{display:visible?"block":"none"}}>
//<ul className={visible?'show':'hide'}>
<ul>
{
props.arr.map((item,index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
)
}
function App(){ //父组件
return (
<div>
<h2 onClick={()=>{visible=!visible;render()}}>商品列表</h2>
{ !visible || <List arr={arr} />}
</div>
)
}
function render(){
ReactDOM.render(<App/>,document.getElementById("box"))
}
render()
class组件
ES6的加入让JavaScript直接支持使用class来定义一个类,react的第二种创建组件的方式就是使用的类的继承ES6 class
是目前官方推荐的使用方式,它使用了ES6标准语法来构建,看以下代码:
import React from 'react'
import ReactDOM from 'react-dom'
/*
class 组件名 extends React.Component {
render(){ //render是必不可少的钩子函数
return jsx表达式
}
}
*/
class App extends React.Component {
render () {
return (
// 注意这里得用this.props.name, 必须用this.props
<h1>欢迎进入{this.props.name}的世界</h1>
)
}
}
ReactDOM.render(
<App name="react" />,
document.getElementById('root')
)
更老的一种方法:
在15.x的版本只能这样创建组件, 但现在的项目基本上不用
React.createClass({
getInitialState(){ //定义组件初始化状态
},
getDefaultProps(){ //定义组件的默认属性
},
render () {
return (
<div>hello world</div>
)
}
})
实现对象的遍历 ===> 不会的转成会的
//Object.keys 返回对象的所有的key值形成的数组
var obj = {a:1,b:2}
class App extends React.Component{
render(){
return (
<div>
{
Object.keys(obj).map(item=>{ //item=["a","b"]
return <p key={item}>{item} : {obj[item]}</p>
})
}
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById("box"))
组件的组合、嵌套
将一个组件渲染到某一个节点里的时候,会将这个节点里原有内容覆盖
组件嵌套的方式就是将子组件写入到父组件的模板中去,且react没有Vue中的内容分发机制(slot),所以我们在一个组件的模板中只能看到父子关系
// 从 react 的包当中引入了 React 和 React.js 的组件父类 Component
// 还引入了一个React.js里的一种特殊的组件 Fragment
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'
class Title extends Component {
render () {
return (
<h1>欢迎进入React的世界</h1>
)
}
}
class Content extends Component {
render () {
return (
<p>React.js是一个构建UI的库</p>
)
}
}
/** 由于每个React组件只能有一个根节点,所以要渲染多个组件的时候,需要在最外层包一个容器,如果使用div, 会生成多余的一层dom
class App extends Component {
render () {
return (
<div>
<Title />
<Content />
</div>
)
}
}
**/
// 如果不想生成多余的一层dom可以使用React提供的Fragment组件在最外层进行包裹
class App extends Component {
render () {
return (
<Fragment>
<Title />
<Content />
</Fragment>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)