常州网站制作企业,熊掌号 西安网站建设,wordpress related posts,网站的查询系统怎么做组件与模块
前期准备
安装React官方浏览器调试工具#xff0c;浏览器扩展搜索即可 比如红色的React就是本地开发模式
开启一个用React写的网站#xff0c;比如美团 此时开发状态就变成了蓝色 组件也能解析出来
何为组件模块
模块#xff0c;简单来说就是JS代…组件与模块
前期准备
安装React官方浏览器调试工具浏览器扩展搜索即可 比如红色的React就是本地开发模式
开启一个用React写的网站比如美团 此时开发状态就变成了蓝色 组件也能解析出来
何为组件模块
模块简单来说就是JS代码分块了 组件简单来说就是把H5CSSJS图像一些杂七杂八的整合到一个模块里 模块化 当应用的JS都以模块来编写这个应用就是一个模块化的应用 组件化 当应用是以多组件的方式实现这个应用就是一个组件化的应用
函数式组件
函数式组件那就先写一个函数放在script标签里备着 写完就需要渲染光一个函数在那放着肯定不行所以用ReactDOM.render渲染到真实DOM上 打开页面很明显不行因为React不会直接认识函数 之前我们说过React自定义的组件首字母一定大写
我们的函数式组件函数名就是组件的名字所以组件名要大写那么函数名也要大写
如果小写了就会出现这种错误因为小写的标签会先去h5原生的标签找所以会找不到 改一下注意标签闭合
执行ReactDom.render发生了什么 1.React解析组件标签然后找到了目标组件比如上面的Demo/组件 2.发现组件是使用函数定义的随后调用该函数将返回的虚拟DOM转为真实DOM随后渲染到页面上
类的基本知识复习
原型链
原型链当在实例化的对象中访问一个属性时首先会在该对象内部(自身属性)寻找如找不到则会向其__proto__指向的原型中寻找如仍找不到则继续向原型中__proto__指向的上级原型中寻找直至找到或Object.prototype.__proto__为止值为null这种链状过程即为原型链。如下图所 原型链的作用查找对象的属性方法
子类找不到就去父类去找
代码 !DOCTYPE html
html langenheadtitleHello React!/titlemeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1/headbodyscript typetext/javascript// 用class创建类类中无需定义变量构造器只有在需要赋值的时候才需要定义class Person{constructor(name,age){//this指向当前实例对象this.namename;this.ageage;//当然可以在构造器里固定写值this.school尚硅谷}// 类中定义方法不要用functionfunction就变成函数了speak(){console.log(名字${this.name},年龄${this.age})}talk(){console.log(people talk)}}//继承class Student extends Person{constructor(name,age,grande){// 继承用super关键字复用父类的构造// surper(父类构造参数)super(name,age);// 子类独有的构造要单独写this.grandegrande;}speak(){//直接调用构造的值即可console.log(名字${this.name},年龄${this.age},年级${this.grande})}study(){console.log(努力学习)}}const pnew Person(张三,23);const studentnew Student(李四,25,大三);console.log(p);p.speak();student.speak();student.study();//调用父类方法先去原型链找找到了就调用找不到继续往下面的原型链找student.talk();/script/body
/html类的创建
类构造器
子类
子类构造器
总结
1.类中的构造器不是必须写的只有要对实例做一些初始化的时候比如添加指定属性时才写2.如果A类继承了B类且A类写了构造器那么A构造器中的super是必须调用的3.类中定义的方法都是放在了类的原型对象上的
类组件
创建类组件
类组件类名就是组件名
// 类组件必须继承于React的Component
// 创建类组件
class MyComponet extends React.Component{// render是放在MyComponet的原型对象上面供实例对象是用// render中的this是谁// 实际上是指向MyComponet的实例对象同时也是MyComponent组件实例对象render(){console.log(render中的this,this)return divaaa/div}
}
// ReactDOM的render和类组件里的render没有关系只是单纯的名字一样
ReactDOM.render(MyComponet/,document.getElementById(test))打印的this对象 执行代码成功渲染
ReactDOM.render如何渲染类组件
ReactDOM.render(MyComponet/,document.getElementById(真实DOM的id))执行时发生了什么
1.React解析对象通过找到同名 MyComponet class组件2.发现组件是类组件随后自动将MyComponet类new出来对象并且自动调用render方法3.将render返回的虚拟DOM转换为真实DOM随后页面显示
类组件的实例核心属性
注意我们这里说的是类组件实例的核心属性
也就是说只有实例对象才有属性
所以这里直接pass掉函数组件函数组件的是无法创建对象的只有类组件才能创建实例对象
给Java程序员一个类比
比如定义一个方法 public void test(){}方法是没办法创建对象的。类比函数组件
但是定义一个类public class Test{}类是可以创建对象的。类比类组件核心属性1state
组件的状态里面存着数据数据的改变驱动页面展示
state例子
通过state对画面进行动态控制
script typetext/babel// 借助构造器对state设置值class MyComponet extends React.Component{constructor(props){// 由于继承于React.Component// 所以用构造器设置state值时必须调用父类构造器类组件语法子类使用构造器时必须先用super调用父类构造器// 参数固定为属性propssuper(props);// 对当前对象state进行修改注意是state而不是states这两个是不同的东西// state在设置时需要用{key1:value1,key2:value2,...}这样来setthis.state{isHot:true}}render() {console.log(this);return (div今天天气{this.state.isHot?炎热:凉爽}/div);}}// ReactDOM的render和类组件里的render没有关系只是单纯的名字一样ReactDOM.render(MyComponet/,document.getElementById(test))
/script
如图state已经成功set上值 利用React工具看一下组成
将组件绑定事件
先回顾一下原生的几种绑定方式
注意H5中一些原生的属性在React中已经完成了重写
比如
onclick在React中变成了onClick
onblur变成了onBlur
class变成了className如果写错了在控制台就会有提示
最后引入React的推荐写法
先分享一个错误写法 script typetext/babel// 借助构造器对state设置值class MyComponet extends React.Component{constructor(props){super(props);this.state{isHot:false}}render() { return (// div onClicktest()这种函数用字符串的形式肯定是不可以的div onClick{test()} //换用表达式写法今天天气{this.state.isHot?炎热:凉爽}/div);}}// ReactDOM的render和类组件里的render没有关系只是单纯的名字一样ReactDOM.render(MyComponet/,document.getElementById(test))function test(){console.log(OK)}这里会造成一个问题就是把 表达式传给了onClick()事件 也就是onClick传了个函数表达式进去而不是函数名
而这个函数表达式是个undefined的返回值就会导致onClick事件触发函数后接收了undefined如果是有小括号这样传值会触发一次回调onClick在接收到undefined之后就会不动了导致后续事件都失效
div onClick{test()}/div //表达式
div onClick{test}/div //函数名function test(){console.log(OK)}所以主要是记住**如果要绑定事件传一个函数名就行**
有的小伙伴可能会问那这样怎么传参
答案是箭头函数用箭头函数传参最后改成正确答案
script typetext/babel// 借助构造器对state设置值class MyComponet extends React.Component{constructor(props){super(props);this.state{isHot:false}}render() { return (div onClick{test}今天天气{this.state.isHot?炎热:凉爽}/div);}}// ReactDOM的render和类组件里的render没有关系只是单纯的名字一样ReactDOM.render(MyComponet/,document.getElementById(test))function test(){console.log(OK)}
/script将函数优化进组件里
理论上函数和组件应该是融为一体的不应该相互独立
所以将function函数放进类组件只保留函数名函数体即可
运行代码会报错因为onClick没有指定this的指向
如果放入类组件内部则意味着属于这个类所以onClick回调时需要用this指向指向当前实例
修改指向不再报错
解决类中的this指向问题
script typetext/babel// 借助构造器对state设置值class MyComponet extends React.Component{constructor(props){// 由于继承于React.Component// 所以用构造器设置state值时必须调用父类构造器类组件语法子类使用构造器时必须先用super调用父类构造器// 参数固定为属性propssuper(props);// 对当前对象state进行修改注意是state而不是states这两个是不同的东西this.state{isHot:false}// 解决test中的指向问题这时如果再在test中输出就不再是undefinedthis.testthis.test.bind(this)}test(){console.log(OK)// test 函数放在哪 放在MyComponet的原型对象上供实例使用// 由于test方法是onClick的回调,所以就不能用对象实例调用,直接调用即可// 类方法中默认开启了局部的严格模式,所以此时如果输出this对象,即为undefinedconsole.log(this)}render() { return (div onClick{this.test}今天天气{this.state.isHot?炎热:凉爽}/div);}}// ReactDOM的render和类组件里的render没有关系只是单纯的名字一样ReactDOM.render(MyComponet/,document.getElementById(test))/script状态不可直接更改要用setState
反例直接操作状态(state)来实现boolean值切换 由此可见state是不可以直接修改的所以要借助对象内部的API来修改
打印一下对象看看原型链找到最顶层的ReactComponent的原型对象类似于Object的性质 所以我们如果想修改就只能用setState({key:value}) 调用时
this.setState({key:value})setState是一个合并操作
比如我定义了一个state
this.state({key1:value1},{key2:value2})如果此时我对key1进行修改操作此时仅仅只针对key1进行覆盖操作不会影响key2以及key2的值。
this.setState({key1:test})那么此时的state就会变成
this.state({key1:test},{key2:value2})如果再来一个key3就再合并进去这就是所谓的合并操作
组件各部分渲染次数
构造器 constructor 有几个ReactDOM.render提及了这个组件就构造几次一般来说是一次 ReactDOM.render(MyComponet/,document.getElementById(test))
普通函数 test 有几次事件触发或者被调用这个函数就调用几次
渲染 render 1n次1是初始化时的渲染n是state的修改次数
script typetext/babelclass MyComponet extends React.Component{constructor(props){// 构造器 }test(){// 普通函数console.log(this)this.setState({key1:value1},{key2:value2})}render() { // render函数负责渲染 return (div onClick{this.test}今天天气{this.state.isHot?炎热:凉爽}/div);}}ReactDOM.render(MyComponet/,document.getElementById(test))
/scriptsetState的简写方式
首先看看类中的一个知识就是定义一个固定的值是不需要去像函数一样定义变量类型变量名一连串操作… 直接赋值即可
构造器中定义固定值和构造器外类内去定义效果一样
class MyComponet extends React.Component{constructor(props){this.namenamethis.state{isHot:false}//构造器定义固定值this.age25}// 直接定义值是一样的类中不需要定义变量直接赋值即可age25// 就连state也不用在构造器去写了直接定义即可state{isHot:false}render() { return (div onClick{this.test}今天天气{this.state.isHot?炎热:凉爽}/div);}
}自定义方法改写
为了解决类中方法的this指向问题而采用了赋值语句接收箭头函数如果是箭头函数那么里面的this就会自动向外寻找寻找其再外面一层的this所以函数的再外一层的指向就是类随即解决this指向问题。
test作为变量接收箭头函数的返回值
这样就相当于把箭头函数赋值给了对象的属性上
在属性调用的时候相当于通过属性调用函数
属性一定有this所以就解决了this的指向问题实际开发过程中自定义方法要用箭头函数而不是这种写法test(){console.log(this)this.state({key1:value1},{key2:value2})}
箭头函数test作为变量接收箭头函数的返回值这样就相当于把箭头函数赋值给了对象的属性上这样在属性调用的时候相当于通过属性调用函数这样就解决了this指向问题test (){console.log(this)this.state({key1:value1},{key2:value2})}注意类里面不支持这种箭头函数写法必须去用值接收 test (){console.log(this)this.state({key1:value1},{key2:value2})}state总结 理解
1.state是组件对象最重要的属性值是对象({key1:value1},{key2:value2})
2.组件被称为状态机通过更新组件的state来更新对应的页面显示(重新渲染组件) 强烈注意
1.组件中render方法中的this为组件实例对象
2.组件自定义的方法中this为undefined如何解决
a. 强制绑定this通过函数对象的bind()方法来创建一个新函数b. 用属性接收一个箭头函数
3.状态数据不能直接修改或更新需要通过特定API来处理
核心属性2props
前面看state只是类组件内部的东西类组件内部可以提前准备一些数据用
回顾js展开运算符
首先回顾一下js的展开符号 ...
首先以打印一个数组为例 script typetext/javascriptlet a[1,2,3]console.log(a)/script这种的输出就是结果就是这样是叠在一起的 但如果是用了展开符号再打印数组内部结构就会被展开铺开 script typetext/javascriptlet a[1,2,3]console.log(...a)/script展开运算符的其他用法
可以用作两个数组连接
反例 用展开符连接
注意展开符无法直接展开对象 比如直接展开a对象会报错 但是利用展开符复制对象是可以的 点击这里查看MDN官方文档对于展开符的解释 展开复制过程中直接修改某个属性
如果对于某个属性不满意当然可以直接在复制过程中直接修改
let 新对象 {...展开源对象key:valuekey2:value2......}展开符就复习到这开始进入React部分 React Props传参
如果总是用对象中的state就好像只能内部操作属性无法获取外部的值此时就得用Props属性来实现从外部对类标签进行传值的操作
直接在标签上以keyvalue的形式传值注意这里value先按照字符串来填写写数字会报错后面会解释为什么 但是不难发现如果写了n多个属性就会很繁琐所以引入展开符效果一样更加方便
注意传入对象和被使用的对象key之间key要匹配上不然key都对不上了肯定找不到value了 这里有一个非常有意思的点之前上面刚刚才说过对象不可以用展开符直接展开这里就用到了展开符去传参。
因为这个是在React中的特殊语法标签上可以这么去搞。有React依赖babel就可以这么用
标签传值数字问题
直接传一个数字是不行的因为React默认value是有数据类型的双引号默认是String。 所谓数据类型只有在JS中才有既然是这样我们传一个JS代码就可以了即传入参数用{}包裹这样数字就会被识别为Number类型自然不会有问题
对象.propTypes和PropTypes的区别
就和Java一样React传参编译没有明显提示
问题1如果我希望传一个String但是方法调用者传了一个Number就会有问题问题2某个属性没有值那我希望给一个默认值这个默认值该如何设置
可以看下面两个
属性类型约束
React16以前的类型指定 对象.propTypes来写规则代码
React.PropTypes.类型来指定类型 React16及以后因为React对象过于臃肿因此对象类型不再集成在React中所以就引入了prop-types.js来做类型指定 直接用PropTypes.类型即可测试可以正常渲染 甚至可以传函数进去注意函数的关键字是func而不是function这么写是避免重名
属性默认值
简单指定一下即可不是很难 MyComponet.defaultProps{// 如果不传值则默认张三name:张三,// 如果不传值则默认18age:18,}将默认属性值和属性约束集成在类中
前面我们看到默认属性值和属性约束都写在类的外面这就违反了我们类组件的初衷所以我们要把这个写在类内部
注意是类的内部render的外部 直接用static来修饰即可同时去掉 类名称调用 props是只读的
直接修改props是会报错的因为其是只读的 补充一下
函数式组件使用Props
首先函数式组件是没有实例对象的 所以对象的三大属性就有两个废掉了用不了唯有props可以留下因为比较特殊在函数组件需要传参使用。 除此以外像static这种类才会有的关键字就用不了了
以下是例子 核心属性3ref
可以理解为标签用来标识自己用的类似于id
组件内的标签可以定义ref属性来标识自己
每个标签上可以定义ref对象会将这些ref收集起来到对象身上对象身上有refs的属性代表多个标签的ref被收集起来我们可以通过这些ref来获取真实DOM
传统获取DOM的方式 class MyComponet extends React.Component{showDate(){
//这种传统形式获取太麻烦因为React是操作DOM的所以我们借助React的ref来获取真实DOMconst inputDivdocument.getElementById(input1)console.log(inputDiv.value)}render(){return (divinput idinput1 typetext/input/div)}}}下面介绍React中的refref有很多形式具体可以看下面的介绍
字符串形式的ref弃用
React已弃用一些问题指的是效率问题
字符串形式的ref指的是标签里的ref字符串 接受了一个字符串所以是字符串形式的ref
注意写在标签里的是ref在标签外收集的时候是用this.refs来进行收集最后通过解构的方式拿到key
!DOCTYPE html
html langenheadtitleHello React!/titlemeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1/headbodydiv idtest/div/bodyscript typetext/javascript src../js/react.development.js/script !--React核心的库--script typetext/javascript src../js/react-dom.development.js/script!--React操作Dom相关库--script typetext/javascript src../js/babel.min.js/script!--将JSX转换成JS用的依赖因为浏览器不认识JSX只认识JS--script typetext/babelclass MyComponet extends React.Component{showData(){//这种传统形式获取太麻烦因为React是操作DOM的所以我们借助React的ref来获取真实DOMconst inputDivdocument.getElementById(input1)alert(inputDiv.value)}showData2(){// 通过React对象来操作通过refs属性获取完之后解构// 这里解构是依靠input2的ref名字来进行解构的const {input2}this.refs;alert(input2.value)}render(){return (divinput idinput1 typetext placeholder点击按钮弹出左侧数据/inputbutton onClick{this.showData2}/buttoninput refinput2 onBlur{this.showData2} typetext placeholder获得焦点提示数据/input/div)}}ReactDOM.render(MyComponet/,document.getElementById(test))/script/html回调函数形式的ref
主要是标签上的ref参数通过回调函数的形式将当前节点通过箭头函数传传递到当前对象上
其中current代表传入的DOM即当前组件this.testcurrent代表了在对象上创建一个test属性并且将当前传入的current DOM给挂载到对象上
这里的this指向是谁 因为这个标签是在render里面的所以ref里的回调函数会向外扩散寻找对象即找到类组件的对象
input ref{(current){this.testcurrent}}input/提示ES6语法const {test}this
这两个写法并不等价const {test}this.test在JavaScript ES6中你可以使用解构赋值destructuring assignment来从对象中提取属性。
当你写 const {test}this 时你是在从 this 对象中提取 test 属性并将其赋值给 test 变量。然而当你尝试 const {test}this.test 时你实际上是在尝试从 this.test 这个值而不是对象中提取属性。
如果 this.test 是一个对象那么这个表达式就会尝试从该对象中提取 test 属性。
如果 this.test 不是一个对象那么这个表达式就会失败因为它无法从非对象值中提取属性。属性自己是不能再拿出一个属性的只有对象才能拿出属性代码示例
script typetext/babelclass MyComponet extends React.Component{showData(){// 直接解构对象属性获取DOMconst {test}this// 获取属性alert(test.value)}render(){return (divinput ref{(current)this.testcurrent} typetext placeholder点击按钮弹出左侧数据/inputbutton onClick{this.showData}点击触发/button/div)}}ReactDOM.render(MyComponet/,document.getElementById(test))
/script
回调ref的执行次数问题
回调ref的次数在组件被更新的时候会触发两次在组件第一次创建的时候只触发一次
为什么更新的时候是触发两次
第一次是为了在更新之前把上一次的处理后的属性内容都清空掉传一个null进去。清空的动作第二次就是正常的触发该正常传DOM就传DOM。更新的动作
多的代码就不放了这个不必纠结直接正常用回调函数即可不必纠结两次回调
createRef 官方推荐
这个是React官方大力推荐的ref的创建方式主要指的是React.createRef()函数
React.createRef()调用后可以返回一个容器该容器可以存储被ref标识的节点该容器是“调用ref的标签专属的”如果后续还有组件用了同名的ref属性就会覆盖之前的ref
// 直接就可以挂载到对象属性上
对象属性React.createRef()除此以外React官方是不推荐去多用ref尽量多用state来管理属性ref主要是与DOM绑定在一起 JSX的注释问题
JSX的注释需要用花括号括起来
括起来之后就可以在内部写JS的注释了因为被识别为JS语句了
比如