React学习-React初识
一、前言
为什么要去学习React呢,关于前端三大框架Angular,Vue,React其实都得去学吧,因为大家都在用啊,大家都再谈论啊,面试什么的都要求,没办法,曾几何时,大家都说求求大佬们别坑新了,别出框架了,老子孩子孙子都学不动了。其实我jiao的吧,技术,不就是一直更新的,新的技术代替老的技术,说不定有一天,三大框架也会被其他东西代替,就想以前的jquery一样,没什么的,我觉得大佬们出一个框架的时间,比起我们去学习的时间来说真的是太多了,这东西思想都是一样的,都是通的,如果真的去认真学习一下,也就几天的时间。技多不压身,就这样吧。
关于为什么要学习React,就说以下几点吧
1.和Angular1相比,React设计很优秀,一切基于JS并且实现了组件化开发的思想;
2.开发团队实力强悍,不必担心断更的情况;
3.社区强大,很多问题都能找到对应的解决方案;
4.提供了无缝转到 ReactNative 上的开发体验,让我们技术能力得到了拓展;增强了我们的核心竞争力;
5.很多企业中,前端项目的技术选型采用的是React.js;
其实让我总结起来就三点:1.用的人多 2.用的人多 3.用的人多
二、React中的几个核心概念
2.1 虚拟DOM(Virtual Document Object Model)
DOM的本质是什么:浏览器中的概念,用JS对象来表示 页面上的元素,并提供了操作 DOM 对象的API;
什么是React中的虚拟DOM:是框架中的概念,是程序员 用JS对象来模拟 页面上的 DOM 和 DOM嵌套;
为什么要实现虚拟DOM(虚拟DOM的目的):为了实现页面中, DOM 元素的高效更新
比如我们的页面上有一个dom元素,输出这个dom元素(暂且叫真实的dom元素)
<body> <div id="box" title="这是一个dom元素" data-value="div"> 这是一个文本节点 <p>p标签</p> </div> <script> var div = document.getElementById('box'); console.log(div); </script> </body>
那么接下来我们来用虚拟DOM的方法来描述这个dom元素,也就是用一个js对象来表示这个dom元素的所有东西,将来我们也能用这个虚拟的dom来渲染出来真实的dom
<script> var div = { tagName:'div',//html标签名 attrs:{ id:'box', title:'这是一个dom元素', 'data-value':'div' }, childrens:[ '这是一个文本节点', { tagName:'p', attrs:null, childrens:[ 'p标签' ] } ] } </script>
就这样,我们就用js对象描述了一个完整的dom元素。
2.2 Diff算法
所谓Diff,也就是Different,就是差异的意思,React是比较数据变换前后虚拟dom的差异来更新界面的上dom元素的,并且只是更新改变的dom节点的各项属性值,不改变的就不去更新。
在我们传统的操作dom的方式中,比如一个集合,当里边的某条数据变化,我们一般会重新赋值集合,并且使用模板引擎重新调用一下渲染方法,这样一来是更新了全部的dom节点,相比而言就不那么高效了。
至于算法究竟是怎么来的我们也不必深究了。也就是对比上边的两个js对象,有没有差异啥的,然后做标记,需要更新那些东西,然后再反馈到界面上。
三、第一个HelloWorld程序
看了不少教程,上来就是脚手架什么的 ,一下子怼一大堆东西,都不知道什么玩意。也有用webpack的,直接开始模块化开发,初学者不建议这样,弄的都是一头雾水的。其实react,vue,angular这些框架都能和使用jquery一样,页面引入相关js就行了,这篇文章最主要的还是写给初学者和后端,前端可能就不需要了。
3.1 首先要引入相关js,博主已经给准备好了,点击下载lib.zip,页面代码如下,也是非常的简单大气上档次:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="lib/babel.js"></script> <script src="lib/react.development.js"></script> <script src="lib/react-dom.development.js"></script> </head> <body> <div id="app"></div> <script> console.log(React, ReactDOM); </script> </body> </html>
很简单,和你刚学jquery是一样的,引入之后输出一下React的两个对象,没问题就可以开始了。
3.2 创建虚拟dom元素,渲染到页面上
<body> <div id="app"></div> <script> // 这是 创建虚拟DOM元素的 API <h1 title="h1标签" id="myh1">hello world</h1> // 第一个参数: 字符串类型的参数,表示要创建的标签的名称 // 第二个参数:对象类型的参数, 表示 创建的元素的属性节点 // 第三个参数: 子节点 var h1 = React.createElement('h1', { title: 'h1标签', id: 'myh1' }, 'hello world') // 3. 渲染虚拟DOM元素 // 参数1: 表示要渲染的虚拟DOM对象 // 参数2: 指定容器,注意:这里不能直接放 容器元素的Id字符串,需要放一个容器的DOM对象 ReactDOM.render(h1, document.getElementById('app')) </script> </body>
运行结果如下:
四、JSX语法
4.1 html代码和js代码混编
创建虚拟dom着实麻烦,如果有嵌套情况就更麻烦了,要写好半天,于是React提供了一套新的创建虚拟dom的方法,这是一新语法,意思就是js的扩展语法,可以让我们直接在js代码中写html标签和js代码,意思就是js和html代码混写。引用李纳斯的一句名言:talk is cheap,show me your code,不懂的话,翻译成汉语就是:废话少说,放码过来。
<body> <div id="app"></div> <script> var h1 = <h1 title="h1标签" id="myh1">hello world</h1>; //没错,就是这样直接写标签,不加'',不是字符串 ReactDOM.render(h1, document.getElementById('app')) </script> </body>
运行结果如下:
没错,报错了,因为 var h1 = <h1 title="h1标签" id="myh1">hello world</h1>; 是新语法,现在好多浏览器根本不认识,所以肯定报错。别忘了,我们之前还引入了一文件就是babel.js,这货就是用来帮浏览器来认这样的代码的,另外还有一些很新的es6的语法,浏览器也不认识,也需要它的帮助。
接下来我们就来使用它,就是把<script></script>标签 改成 <script type="text/babel"></script>,text/babel就是 babel.js来识别的,它会把里边的高级代码转换成浏览器能认识的代码,本身这段script 浏览器看见他的type属性是不认识的,所以不会管它。
修改之后如下:
4.2 加入js代码(数据处理)
接下来我们写点js代码,渲染一些数据,为了区别在html标签中的js代码,需要用{} 把js代码包起来,这个语法好多模板框架都有,vue,angular里边是{{}} ,也都类似,都差不多。我们来渲染一个列表
<body> <div id="app"></div> <script type="text/babel"> // jsx语法下的数组 var fruits = [ <li key="1">苹果</li>, <li key="2">香蕉</li>, <li key="3">橘子</li> ] // js语法下的数组 var todos = [ { id: 1, title: '吃饭' }, { id: 2, title: 'sleep' }, { id: 3, title: '大' } ] // 调用map方法 生成一个jsx数组 var todoLis = todos.map(item => { return <li key={item.id}>{item.title}</li> }) // 推荐jsx语法中元素用括号包起来 var element = ( <div> {/* 在 jsx 中,绑定数组成员会直接把成员渲染到这里 (jsx中的注释要写在花括号) */} <ul>{fruits}</ul> <ul> {/* 直接绑定一个数据 */} {todoLis} </ul> {/* 直接在标签中动态生成一个数组 */} <ul> { todos.map(item => { return <li key={item.id}>{item.title}</li> }) } </ul> </div> ) ReactDOM.render(element, document.getElementById('app')) </script> </body>
运行结果如下:
4.3 加入js代码(事件处理)
事件名称和原生事件都一样,只不过写法上注意一点大小写,因为后边是js代码,所有用{}包起来
<body> <div id="app"></div> <script type="text/babel"> function handleClick () { window.alert('hello') } // 1. 使用驼峰命名 // 2. 必须绑定一个函数 // 3. 不能使用字符串的方式,一定要使用 {函数} 来绑定 var element = ( <div> <button onClick={handleClick}>点我</button> {/* 直接绑定一个匿名函数 */} <button onClick={() => alert('hello world')}>行内处理</button> </div> ) ReactDOM.render(element, document.getElementById('app')) </script> </body>
五、组件化编程
React最大的特点就是组件化编程,也就是模块化编程,但是模块化是对于业务代码来说的,比如js代码中一个功能块,java中的一个utils包,c#中的一个dll类库,组件化其实也是一个块,只不是是对于界面上ui来说,比如说一个日历组件,在界面上展现出来就是一个日历,无论是模块化还是组件化都有一个特点,那就是拿过来到哪里都能用。组件化编程就是要求我们把页面分成一块一块的,这样一来页面结构清晰,每一个组件控制每一块页面,互不影响,当然你有时候也可以把一个页面当做一个组件,当这个页面实在是没多少东西很简单的时候就可以这样做。
5.1 函数式组件
函数式组件使用时,直接定义一个函数,函数名称大写,之于为什么大写,这里内部React把 一个组件看成一个类了,之后我们会介绍以类的方式来创建组件,使用的时候在render函数里边放一个以函数名 命名的标签就行了。但标签和双标签都行。
<body> <div id="app"></div> <script type="text/babel"> // 组件的名字首字母必须大写 function AppHeader () { return ( {/* jsx中如果要给标签加类名的话,用className属性,因为class在js代码中为关键字*/} <div className="header"> <h1>头部</h1> </div> ) } //使用方式如下 用一个闭合的标签 单标签和双标签都行 //ReactDOM.render(<AppHeader></AppHeader>, document.getElementById('app')) ReactDOM.render(<AppHeader/>, document.getElementById('app')) </script> </body>
当然我们也可以为组件传递数据,传递数据的方式是在标签上加属性值,这里可以写js代码,在组件的函数中会以形参的方式传递过来,传递过来的数据是只读的,我们并不能作修改
<body> <div id="app"></div> <script type="text/babel"> var dog = { name:'haha', age:5, sex:'公' } // 组件的名字首字母必须大写 function AppHeader (props) { //组件上传递的数据会作为形参传递过来,形参名字什么都无所谓,一般我们props 意思就是属性 //但是这个属性是只读的,我们不能修改 props.name = '1' console.log(props); //jsx中如果要给标签加类名的话,用className属性,因为class在js代码中为关键字 return ( <div className="header"> <h1>头部,{props.name},{props.age}</h1> </div> ) } //使用方式如下 用一个闭合的标签 单标签和双标签都行 //ReactDOM.render(<AppHeader></AppHeader>, document.getElementById('app')) //传递给AppHeader组件的数据 ReactDOM.render(<AppHeader name={dog.name} age={dog.age}/>, document.getElementById('app')) </script> </body>
5.2 class方式创建的组件
React推荐我们用class方式创建组件,创建的组件用class关键字,并且继承React.Component,这才会把这个类标记为一个组件,在组件中,必须声明一个render方法,返回jsx元素,内部react会调用这个方法帮我们渲染组件。
<body> <div id="app"></div> <script type="text/babel"> // 1. class 组件类,必须继承自 React.Component 才是一个组件类,否则就是一个普通的类 // 2. 在组件类中,必须通过 render 渲染函数返回组件模板 // 3. 接下来就可以在其它能访问到这个组件类的作用域中去使用这个组件了 class MyComponent extends React.Component { render () { return ( <div> <h1>My Component</h1> </div> ) } } var element = <MyComponent /> ReactDOM.render(element, document.getElementById('app')) </script> </body>
既然是class,就会有自己的实例数据,接下来我们创建一个具有自己私有数据的组件,我们在class内部定义一个state变量来存储数据
<body> <div id="app"></div> <script type="text/babel"> // EcmaScript 6 Class class AppFooter extends React.Component { constructor () { super() // state 就是组件的状态,也就是把数据驱动视图的数据初始化到 state 中 // state 类似于 Vue 中的 data this.state = { foo: 'bar' } } render () { return ( <div className="footer"> <p>底部 {this.state.foo}</p> </div> ) } } ReactDOM.render(<AppFooter/>, document.getElementById('app')) </script> </body>
5.3 两种方式的对比
其实我们上边以class方式创建的带有state变量的组件 就叫做有状态的组件,这中组件有自己的私有数据,不和外边有接触,并且数据还可以更改,这样我们就真正实现了组件化,想用的时候随时随地拿一个实例就行了。根据实例不同的数据渲染成不同状态的组件。
其实这个state就类似vue中的data了。第一种函数式创建的方法,没有自己数据,且创建方式过于分离,适合那种简单且不牵扯到数据变化的静态纯展现的组件。
六、真正的使用组件
接下来我们创建一个功能比较完成的商品列表组件,react在渲染列表数据的时候 要给每个项目添加一个key属性来保证唯一,这样的话React更新dom的时候确保每项的状态一直保证正确,如果不加的话可能会有一些问题。
<body> <div id="app"></div> <script type="text/babel"> class ProductList extends React.Component { constructor () { // 如果子类加入了 constructor 构造函数,则一定要手动调用父类的构造函数 super super() // React 组件需要通过手动为组件类添加 state 成员来初始化:ViewModel // state 等价于 Vue 中的 data // 接下来就可以在该组件管理的模板中通过 {} 来访问绑定数据了 this.state = { products:[ { id:1, name:'iphone x', price:8999 }, { id:2, name:'xiao mi 8', price:2999 } ] } } render () { return ( <div> <ul> { this.state.products.map(item => { return (<li key={item.id}> <span>名称:{item.name}</span> <span>价格:{item.price}</span> <button onClick={this.show.bind(this,item)}>获取商品</button> <button onClick={this.add.bind(this)}>添加商品</button> </li>) }) } </ul> </div> ) } show (item) { console.log(item) // console.log(this) // 默认是 window 这里我们采用this.show.bind(this) 把this指针改成当前组件 //这样我们就能能用this 调用 当前组件里的 数据(state)和方法了。 } add(){ // React 不是使用的类似于 Vue 中的 Object.defineProperty() 方式 get、set // this.state.message = 'hello world' // React中没有双向绑定的功能,(吐槽一下,不知道为什么没设计,感觉这是和vue比较时的一大劣势) // 现在只需要知道,如果要修改 state 中的数据并且希望得到视图更新,则一定要使用 // this.setState 方法 // this.setState({ // message: 'hello world' // }) var products = this.state.products; products.push({ id:products.length+1, name:'华为 p30', price:4999 }) this.setState({ products:products }) console.log(this.state) } } ReactDOM.render(<ProductList/>, document.getElementById('app')) </script> </body>
运行结果如下,点击相关按钮可看到结果
七、总结
本篇React教程只是为了新手和一些后端人员学习,学完之后知道React是什么就可以了。其实如果要用React也完全可以这么来用,对于后端人员来说,引入相关js就行了。不过前端肯定都用webpack工程化开发,各种配置各种打包相当麻烦。有人说脚手架了,其实脚手架也就是把常用的配置给你生成好了。这些都是工具吧,你想用就去学一点,关键还是技术吧。就像前端开发,能用的编辑器不得和你亲戚一样多。
一直有同学说,脚手架是什么东西。脚手架就是框架啊,你自己慢慢一行代码一行代码的配也行啊,就比如你搭建ssm框架,mvc框架,三层框架,代码生成器生成一坨坨的东西。都一个意思。其实我一直很讨厌官方定义的一些东西,就比如至今我都不知道socket 为什么叫套接字编程,难道是中国人硬怼过来的。
感觉后端学习这些框架其实很简单的,因为思想在里边,而且前端好多东西其实都在有意无意的往后端转。我真的觉得React的jsx语法 其实和jsp 或者 aspx ,甚至和php也差不多吧 都是在html代码中混写逻辑代码。
至于其他框架,Angular感觉实在太重了,其实我更爱vue,各有优缺点吧。反正天下框架一大抄吧,不对,文化人读书人的事怎能说抄呢,是吸取精华,去其糟粕,吸收外来文化,为我所有,不要说三大框架,就是各种编程语言里的框架都能看见里边有相同的影子,因为毕竟编程这个思想是一样的啊,你不满意,就重新造一个,我不满意,我也重新写一个,百度不是还出了一个san,没什么好不好吧,用起来舒服就行,能提高效率提高生产力就行。大家都是混口饭吃,何必纠结来纠结去呢,好了就去学就去使用,不好了就换一个。
算了不扯淡了,不过这是我一贯的风格,就算是学习技术的文章,也不想去那么很严肃,很正式,毕竟,学东西都够累了,还不能吐槽几句。