react
前言:首先掌握 构造函数,原型,原型对象,class类,class类的继承的概念
一,概念
1. react 如何实现组件化
react中有组件化的概念,但是,并不像是vue这样的组件模板文件,react中,一切都是以js来表现的 es6和es7
2. react 核心概念
-
虚拟dom:用js对象的形式,来模拟页面上的嵌套关系
-
diff算法:对比dom和虚拟dom
(1)tree diff:dom树节点对比
(2)component diff: 在 tree diff的基础上, 组件级别的对比
(3)element diff:组件对比的同时,元素级别的对比
层层递进的关系
3.webpack
因为webpack是基于node构建的
和export defult 不同,这里是node里面的语法,common.js语法
module.exports = {}
4.node 和 chrome的关系
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时。
5.本地编译和打包编译
本地编译代码时, npm run dev 时,在内存中创建了文件,实质找不到文件,项目中右键检查源代码时,srcipt标签引入的是 src="app.js",并且app.js文件里并没有被压缩,假设本地运行的端口是8080,那么在localhost:8080/app.js下会看到你在内存中编译的js代码
但是生产环境引入了多个压缩的js文件
6.react脚手架
npm install -g create-react-app
npx create-react-app myapp
二,基础
1. react创建dom元素
react创建虚拟dom并渲染到页面,我们实际开发过程中用jsx语法写,其实也是利用了babel将jsx语法转成了这种形式,让浏览器解析
1 // 1. 创建dom元素 createElement( 2 // 参数1:创建的元素的类型,字符串,表示元素的名称 3 // 参数2: 是一个对象或是null,表示dom元素的属性, 4 // 参数3,子节点(包括其他虚拟dom,获取 文本子节点) 5 // 参数n: 其他子节点 6 // ) 7 // const myh2 = React.createElement('h2', null, '这是创建的子节点'); 8 9 //当设置属性class类名时,应写成className 10 const myh2 = React.createElement('h2', { className: 'myh2', title: 'this is dom'}, '这是创建的子节点'); 11 const mydiv = React.createElement('div', null, '给h1外面套一层div', myh2) // 给h2外面套一层div
12 //ReactDOM.render() 将虚拟dom渲染到页面 13 // 1. 要渲染的虚拟dom元素 14 // 2. 指定的容器 15 ReactDOM.render(mydiv, document.getElementById('app'))
2. jsx语法 https://reactjs.bootcss.com/docs/introducing-jsx.html
在js中混合写入html语法,叫做jsx,符合xml规范的js,jsx语法的本质在运行的时候被babel转换成 React.createElement()的形式
和Vue的插值表达式一样,循环的时候也要加key,由于虚拟dom的原因,防止dom元素被复用
创建元素时单行可以不用加(),多行必须要加()
1 let name = '小明' 2 let a = 10 3 let boo = true 4 let title = 'myh1' 5 const h1 = <h1>这里是h1</h1> 6 const myh1 = <div class={title}>{a + 2} {name} {boo ? '真' : '假'} {h1}</div> 7 8 ReactDOM.render(myh1, document.getElementById("app"))
jsx语法里写注释 /* */形式
1 {/* {a + 2} {name} {boo ? '真' : '假'} {h1} */}
jsx语法中给元素添加类名,使用className代替
引入图片,变量的形式或者使用node中的require
1 <img src={imageurl} alt="" /> 2 <img style={{height: '100px'}} src={require('../assets/image/2.jpg')} alt=""/>
3. react 创建组件
组件名称必须以大写字母开头。
React 会将以小写字母开头的组件视为原生 DOM 标签。例如,<div />
代表 HTML 的 div 标签,而 <Welcome />
则代表一个组件,并且需在作用域内使用 Welcome
。
3.1 第一种,构造函数创建组件,必须有return,否则报错,和vue一样,必须要有一个根元素,构造函数名即为组件名
如果不想让共同的根元素显示出来使用:fragment标签 或者是 <></> 空标签
1 function Hello() { 2 return <div>这里是组件</div> 3 } 4 5 ReactDOM.render(<div> 6 我要创建组件 7 <Hello></Hello> 8 </div>, 9 document.getElementById("app"))
react工具
https://github.com/facebook/react-devtools
https://github.com/facebook/react
react组件传值(props)
不管是vue,还是react,组件中props接受的参数永远都是只读的,react中props就是所谓的组件接受的参数,只要外界传值,那就直接使用props就行了,不管是函数是组件还是class类组件
1 //定义子组件 2 function Hello(props) { 3 // console.log(props) //形参接受外界传过来的参数,和vue一样,props接受的参数是只读的,不可修改 4 let { userinfo, user } = props 5 return <div>这里是组件 {userinfo.name}</div> 6 } 7 8 const userobj = { 9 name: 'shun', 10 age: 18 11 } 12 const userarr = [{ id: 0, gril: 'ppp' }, { id: 1, gril: 'uuu' }] 13 14 ReactDOM.render(<div> 15 我要创建组件 16 {/* 传递参数 */} 17 <Hello userinfo={userobj} user={userarr}></Hello> 18 </div>, 19 document.getElementById("app"))
自组件给父组件传值 (和vue一样,需要事件)
子组件:通过 this.props.自定义的事件名(传递的参数)
父组件:在调用子组件的地方,自定义的事件名 = { (参数)=> {this.父组件要执行的事件名(参数)}}
1 // 发送 2 <p onClick={() => { this.props.toFather(this.state.findlist)}}>点击给父组件传值</p> 3 4 //父组件调用子组件的地方接收 5 <Find contextobj={this.state.contextobj} toFather={(val) => {this.gettoFather(val)}}></Find>
react中兄弟组件之间传值
https://blog.csdn.net/qq_39290323/article/details/107166396,这个是有问题的,constructor里面是不能直接修改state里面的值的
默认props
3.2 第二种 class类创建组件 和 this.state
class创建组件的时候,首先掌握class和类的继承 extends
在class内部,this表示当前组件的实例对象,
1 import React from 'react' 2 // 组件 3 export default class Home extends React.Component { 4 constructor() { //同时创建this(子类本身没有this);所以super(props)的作用就是在父类的构造函数中给props赋值一个对象this.props=props这样就能在它的下面定义你要用到的属性了 5 // 继承必须调用super,才可以访问this 6 super() 7 this.state = { // this.state相当于vue中的data() { return {}}, 这个值是可读可写的 8 massage: "诶若为UR" 9 } 10 } 11 // render() 函数渲染当前组件对应的虚拟dom元素 12 render() { 13 return ( 14 //注意,在class内部,this表示当前组件的实例对象 15 <div> 16 <div>不知道{this.props.userobj.name}</div> 17 <h1>{this.state.massage}</h1> 18 </div> 19 ) 20 } 21 } 22 23 24 import Home from './Home' 25 26 const userobj = { 27 name: 'shun', 28 age: 18 29 } 30 31 32 ReactDOM.render( 33 <div> 34 组件 35 <Home userobj={userobj}></Home> 36 </div>, 37 document.getElementById("app"))
props和state的区别:
#### 函数式组件一般用于静态的渲染,不需要私有数据和声明周期函数
4. 列表渲染
1 export default class Listitem extends React.Component { 2 render() { 3 return ( 4 <ul> 5 {this.props.records.map((item) => ( 6 <li key={item.id}> 7 {item.name + "今年"} 8 {item.age} 9 </li> 10 ))} 11 </ul> 12 ); 13 } 14 }
innerHtml: "<p>渲染带标签的字符串</p>" <p dangerouslySetInnerHTML={{__html: this.state.innerHtml}}></p>
style样式
less的使用在另一篇随笔中有提到 https://www.cnblogs.com/shun1015/p/13520577.html
jsx语法中,style写成字符串的形式会报错,在 { } 里面写成对象的形式,对象的key是字符串
1 <p style={{ color: 'red', fontSize: '18px' }}>列表,下面是子组件</p> 2 3 const stylelist = { color: 'red', fontSize: '18px' } 4 <p style={stylelist}>列表,下面是子组件</p>
react中的css样式作用域
react中引入外部的样式文件,例如:import '@/style.css',因为没有作用域的概念,组件的所有子组件都会受到这个文件的影响,感觉react
的局部样式相比vue
要麻烦点,vue
可以在style
标签上添加scoped
,达到使样式作用于当前文件的效果,而react
没有,但是react
有其他的实现方式,比如css-Modules
、styled-components
方法一:css-Modules
自动为每一个类生成一个哈希值,产生惟一标志,因此避免了类的覆盖
这里需要配置wepack
文件,react
项目需要执行npm eject
命令暴露出config
文件,我这里增加配置如下:
关于sass和less配置方法是一样的
关键配置就是 modules: true
{ test: cssModuleRegex, use: getStyleLoaders({ importLoaders: 1, modules: true, // 加这一行代码,css模块化 sourceMap: isEnvProduction && shouldUseSourceMap, modules: { getLocalIdent: getCSSModuleLocalIdent, }, }), }
当配置好上述步骤后,组件内引入css文件就成了局部的形式,但是引入的css是一个对象
import cssobj from "@/style/index.scss"; console.log(cssobj);
通过打印这个对象我们发现,我们在css文件里面的只有类选择器和id选择器才会出现在对象里,也就是说,只有类选择器和id选择器才有模块化,只有配置css的模块化之后,才能打印出这个对象,没有配置的时候打印的是个空对象
所以说我们使用样式的时候 className只需要绑定被转成的哈希值就可以了
1 <div className={cssobj.listbox}> 2 <p className={cssobj.title}>子组件列表渲染</p> 3 <Listitem records={this.state.records}></Listitem> 4 </div> 5
注意
css模块化只针对类选择器和id选择器生效
css模块化不会将标签选择器模块化
还有 localIdentname等参数的配置项,自定义生成类名的格式,老版本的已经不适用于webpack4.x版本的项目了,我没有找到具体的配置方法。。。
:global和:local
通过:global包裹起来的类名不会被模块化,会变成一个全局的样式,classname直接绑定就好了
:local包裹起来的类名会被模块化,默认不写也是被模块化,一般不用
:global(.todolist) { color: blue; }
事件处理
react中事件名是小驼峰式命名, onClick={function}
利用箭头函数的this的指向问题,解决传参
render() { return ( <div> <p onClick={function () { alert('pppppppp') }}>点击</p> <p className={cssobj.click} onClick={this.handleClick}>点击事件,没办法传参啊</p> // 在构造函数中一个圆形对象的方法,调用另一个圆形对象的方法,使用this <button onClick={() => { this.clickMe() }}>利用箭头函数,的this指向问题,传参</button> // 这里利用这里在点击事件中执行一个箭头函数,箭头函数的内部执行事件处理函数 </div> ); } handleClick() { alert('react的点击事件') } clickMe() { console.log('点击了我') }
或者写成这样的形式(箭头函数) 匿名函数取了个名字,具名函数,前面调用一个有名字的函数没毛病吧
使用 this.setState()修改state里的数据,本身就是异步的
这里就很像小程序了 setData(),用法一毛一样
在使用this.setState(),修改数据的时候,是异步执行的,想拿到state里面更新是的值,用await或者是回调中拿
react中单向数据流,例如表单元素的value绑定data里的属性,仅仅是只读的,并不能修改输入框里的数据,想要修改输入框里的数据,需要绑定一个事件onChange,通过事件源或者ref属性获取到value并设置给data(手动实现双向绑定)
而在vue中有v-model这个语法糖,不仅是只读的,当我们修改输入框里的value时,对应绑定data里的数据也会改变,这就是vue的双向绑定(利用了vue的get和set方法)
例如:获取输入框value的两个方法:1,事件源e 2.ref属性
refs转发:(表示当前组件真正实例的引用,返回绑定当前属性的元素,不能再无状态组价中使用(函数式组件))
1 render() { 2 return ( 3 <input type="text" placeholder="请输入姓名" value={this.state.name} onChange={(e) => this.handlechange(e)} ref="mytext" /> 4 ) 5 } 6 handlechange = (e) => { 7 8 // console.log(e.target.value) 9 console.log(this.refs.mytext.value) 10 11 this.setState({ 12 // name: e.target.value, 13 name: this.refs.mytext.value 14 }) 15 }
过调用 React.createRef
创建了一个 React ref 并将其赋值给 ref
变量
constructor(props) {
super(props)
this.myref = React.createRef();
}
render() {
return (
<input type="text" ref={myref}/>
<button onClick={()=>{this.handleclick()}}>点击</button>
)
}
handleclick = () => {
console.log(this.myref.current.value) //通过变量ref获取到value
}
受控组件和非受控组件
主要针对于表单控件,一个表单元素仅仅设置value为只读,不设置onChange事件就是非受控组件,相反为受控组件
数据请求axios与 json-server(本地json服务器)
数据请求axios与 json-server(本地json服务器)
1.使用json-server 就是把本地的json文件,转成一个可访问的ip地址
npm i json-server -g
项目文件夹下新建db.json,然后运行json-server --watch db.json 或者 新建运行命令 npm run json:server,在本地项目的同一个端口下面会出现数据(会遇到问题,慢慢试就好了)
2. 使用代理请求服务器地址
和vue一毛一样,在mode_modules/ react-scripts/ config/webpagkDevServer.config.js里面找到proxy, 或者 npm run eject 暴露出config文件夹和script文件夹,在config.webpagkDevServer.config.js里
1 proxy: {
2 '/api': {
3 target: 'https://i.news.qq.com/trpc.qqnews_web.pc_base_srv.base_http_proxy',
4 changeOrigin: true, // 是否开启跨域
5 pathRewrite: { //重写接口
6 '^/api': ''
7 }
8 }
9 },
PrueComponent
pruecomponent 创建的组件 提供了一个具有浅比较的 shouldcomponentUpdate 方法,其他和component一致
在state中存在一个对象的话,记得更新的时候重新赋一个对象,因为还用同一个对象(地址,引用),不会去改变state的值
感觉 ES6 是为 react 而生的,又像是ES6创造了react......