React学习笔记二-组件创建

React中创建组件

第一种 - 创建组件的方式

使用构造函数来创建组件,如果要接收外界传递的数据,需要在构造函数的参数列表中使用props来接收;(这种方式props只是形参,可以是其他名称)

必须要向外return一个合法的JSX创建的虚拟DOM;

  • 创建组件:

    function Hello () { 
      //如果在一个组件种return null,则该组件是空的
          //return null;
      return <div>Hello 组件</div>
    }
    
  • 为组件传递数据:

    // 使用组件并为组件传递 props 数据,直接以属性的形式即可
    <Hello name={dog.name} age={dog.age} gender={dog.gender}></Hello>
    
    // 在构造函数中,使用 props 形参,接收外界传递过来的数据
    function Hello(props) {
      //在组件中,必须返回一个合法的JSX虚拟DOM元素
      console.log(props)
       // props.name = 'zs'
      //组件中的props只读,不可以被重新赋值。Vue中也是一样
      return <div>这是 Hello 组件 --- {props.name} --- {props.age} --- {props.gender}</div>
    }
    
  1. 父组件向子组件传递数据,可以使用{...obj}属性扩散传递的方式
    <Hello {...dog}></Hello>
  2. es6展开运算符的举例
var o2 ={
    age:22,
    address:'中国北京',
    phone:'110'
}    
var o1 ={
    name:'vivi',
    ...o2
}
console.log(o1);
  1. 将组件封装到单独的jsx文件中
import React from 'react'
// 在 function定义的组件中,如果想要使用 props,必须先定义形参,否则无法直接使用
// 但是,在class定义的组件中,可以直接使用 this.props 来直接访问,不需要预先接收 props
export default function Hello(props) {
  // console.log(props)
  return <p>haha --- {props.name}</p>
}
  1. 组件的名称首字母必须是大写,不然导入时会识别为html标签

  2. 在导入组件的时候,如何省略组件的.jsx后缀名:

   // 打开 webpack.config.js ,并在导出的配置对象中,新增 如下节点:
   resolve: {
       extensions: ['.js', '.jsx', '.json'], // 表示这几个文件的后缀名可以省略不写。按顺序尝试,如果找不到js后缀的该文件,会去找jsx格式的
       alias: {
           '@': path.join(__dirname, './src') //@就可以表示项目根目录的src这一层目录
       }
     }
  1. 在导入组件的时候,配置和使用@路径符号
    import Hello from '@/components/Hello'

第二种 - 创建组件的方式

使用 class 关键字来创建组件

ES6 中 class 关键字,是实现面向对象编程的新形式;

了解ES6中 class 关键字的使用

  1. class 中 constructor 的基本使用
  2. 实例属性和实例方法
  3. 静态属性和静态方法
  4. 使用 extends 关键字实现继承
用es5演示
//ES5中class关键字,是实现面向对象编程的新形式
function Person(name,age){
    this.name = name
    this.age = age
}
//实例方法
Person.prototype.say = function(){//挂载到构造函数原型对象上的方法,new出来的实例可以继承
    console.log('这是Person的实例方法')
}
//静态方法
Person.show = function(){//实例访问不到,构造函数把它挂载到构造器上了,因此不能通过原型链继承
    console.log('这是Person的静态方法')
}
const p1 =new Person('王多多',18);
console.log(p1);
//通过new出来的实例,访问到的属性,叫做【实例属性】
console.log(p1.name)
p1.say();
Person.show();
//【静态属性】:通过构造函数,直接访问到的属性
Person.info = 'aaa';//info属性直接挂载给了构造函数
console.log(p1.info)//undefined
用es6演示
//创建了一个类
//在class {} 内部,只能写构造器、静态方法、静态属性和实例方法
//class关键字内部,还是用原来的构造函数方法实现的。所以我们把class关键字称做语法糖
class Animal{
    //这是类中的构造器,每一个类中都有一个构造器,如果我们没有手动编写构造器,那么实际上类内部有个看不见的构造器,类似于constructor(){}
    //每当new这个类的时候,会立即执行构造器里的所有代码
    constructor(name,age){
        //实例属性
        this.name = name
        this.age = age
    }
    //在class内部,通过 static 修饰的属性,就是静态属性
    static info = 'eee'//(今后用得不多)
    run(){//挂载到类的原型对象上的方法,可以被实例继承,以后会经常用到
        console.log('这是Animal的实例方法')
    }
    //这是静态方法,只要加了static,都直接挂载到构造器上(今后用得不多)
    static show(){
        console.log('这是Animal的静态方法')
    }
}
const a1 =new Animal('二毛',5);
console.log(a1);
console.log(a1.age)//实例属性
console.log(a1.info)//undefined
console.log(Animal.info) //静态属性
a1.run();
Animal.show();
extends继承演示

父类中的任何属性和方法子类都能继承(包括静态的)

//父类,可以直接把父类理解成原型对象
class Person{
    constructor(name,age){
        this.name = name
        this.age = age
    }
    sayHello(){//实例方法,子类都可以访问到这个方法
        console.log('大家好')
    }
}
//子类,在class类中,可以使用extends关键字,实现子类继承弗雷
//语法: class 子类 extends 父类
class American extends Person{
   constructor(name,age){
       //如果一个子类通过extends关键字继承了父类,那么在子类的构造器中必须先调用super,放在第一行
       //super是一个函数,就是对父类构造器的一个引用
       super(name,age);
   }
}
const a1 =new American('Jack',25);
console.log(a1);
a1.sayHello();
//子类
class Chinese extends Person{
    constructor(name,age,idNumber){
        super(name,age);
        this.idNumber = idNumber;
    }
}
const c1 = new Chinese("张三",28,'330xxxxxxxxxxx0029')
console.log(c1);
c1.sayHello();

基于class关键字创建组件

最基本的组件结构:

   //如果要使用 class 定义组件,必须让该组件继承自 React.Component
   class 组件名称 extends React.Component {
       //this.props直接接收外界传来的参数
       console.log(this.props) //只读,不能重新赋值

       // 在组件内部,必须有 render 函数,作用是渲染当前组件对应的 虚拟DOM结构
       render(){
           // render 函数中,必须返回合法的 JSX 虚拟DOM结构
           return <div>这是 class 创建的组件{this.props}</div>
       }
   }

两种创建组件方式的对比

  • 共同点:使用方式都是</>用标签形式,并且直接在标签内传参

使用 class 关键字创建的组件,有自己的私有数据(this.state) 和 生命周期函数;

使用 function 创建的组件,只有props,没有自己的私有数据和 生命周期函数;

  1. 构造函数创建出来的组件:叫做“无状态组件”【今后用得不多,但是优点是因为剔除了生命周期函数,运行速度相对高一丢丢。电脑性能都较高,因此这个优势也不是很明显】
  2. class关键字创建出来的组件:叫做“有状态组件”【今后用得最多】
  3. 什么情况下使用有状态组件?什么情况下使用无状态组件?
    • 如果一个组件需要有自己的私有数据,则推荐使用:class创建的有状态组件;
    • 如果一个组件不需要有私有的数据,则推荐使用:无状态组件;
    • React官方说:无状态组件,由于没有自己的state和生命周期函数,所以运行效率会比有状态组件稍微高一些;

有状态组件和无状态组件之间的本质区别就是:有无state属性、和 有无生命周期函数;

  1. 组件中的 propsstate/data 之间的区别
    • props 中的数据都是外界传递过来的;
    • state/data 中的数据,都是组件私有的;(通过 Ajax 获取回来的数据,一般都是私有数据);
    • props 中的数据都是只读的;不能重新赋值;
    • state/data 中的数据,都是可读可写的;

渲染评论列表

cmtlist.png

通过for循环生成多个组件

  1. 数据:
CommentList: [
    { id: 1, user: '张三', content: '哈哈,沙发' },
    { id: 2, user: '李四', content: '哈哈,板凳' },
    { id: 3, user: '王五', content: '哈哈,凉席' },
    { id: 4, user: '赵六', content: '哈哈,砖头' },
    { id: 5, user: '田七', content: '哈哈,楼下山炮' }
]

4. 设置样式

  1. JSX语法创建的DOM,如果用 style写样式,不能用字符串赋值,需要用js对象来赋值
    外面的{}表示这是js代码,里面的{}表示是个对象
    如果属性值的单位是px,可以省略px光写数字

    <h1 style={ {color: 'red', fontWeight: 200, fontSize:16} }></h1>
    
  2. 启用 css-modules (css模块化)

    1. 先安装style-loader和css-loader npm i style-loader css-loader -D

    2. 修改 webpack.config.js这个配置文件,为 css-loader 添加参数:

modules为true表示为普通的CSS样式表启用模块化
{ 
    test: /\.css$/,
    use: [
        'style-loader',
        {
           loader: 'css-loader',//打包处理CSS样式表的第三方loader
           options:{modules:true}//开启CSS模块化
        }
    ]
} 

在做了这个处理后的css文件中,css 模块化只针对类选择器(.title)和Id选择器生效,不会将标签选择器(<h1>、<p>)模块化(仍然全局生效)

  1. 在需要的组件中,import导入样式表,并接收模块化的 CSS 样式对象:

    import cssObj from '../css/CmtList.css' 
    
  2. 在需要的HTML标签上,使用className指定模块化的样式:

    <h1 className={cssObj.title}>评论列表组件</h1>
    
  3. 使用localIdentName自定义生成的类名格式,可选的参数有:

    • [path] 表示样式表 相对于项目根目录 所在路径
    • [name] 表示 样式表文件名称
    • [local] 表示样式的类名定义名称
    • [hash:length] 表示32位的hash值
    • 例子:{ test: /\.css$/, use: [ {loader:'style-loader'}, { loader:'css-loader', options:{ modules: { localIdentName: '[path][name]-[local]-[hash:base64:5]', }, } } ] }
      页面上展现出来的样式名是src-css-cmtlist-title-12gry这样的形式
  4. 使用 :local():global()

    • :local()包裹的类名(默认可以不写,默认在样式表中定义的类名都是 :local()形式),是被模块化的类名,只能通过className={cssObj.类名}来使用

    • :global()包裹的类名,是全局生效的,不会被 css-modules 控制,定义的类名是什么,就是使用定义的类名className="类名"

/*被:global()包裹起来的类名,不会被模块化,而是全局生效*/
:global(.test) {
    font-style: italic
}
  1. 注意:只有.title这样的类样式选择器,才会被模块化控制,类似于body这样的标签选择器,不会被模块化控制;

在项目中启用模块化并同时使用bootstrap

  1. 把 自己的样式表,定义为 .scss 文件

  2. 第三方的 样式表,还是 以 .css 结尾

  3. 我们只需要为自己的 .scss 文件,启用模块化即可;

  4. 运行cnpm i sass-loader node-sass -D 安装能够解析scss文件的loader

  5. 添加loader规则:

    { test: /\.scss$/, use: ['style-loader', 'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]', 'sass-loader'] } // 打包处理 scss 文件的 loader
    

React 中绑定事件的注意点

  1. 事件的名称都是React的提供的,因此名称的首字母必须大写onClickonMouseOver

  2. 为事件提供的处理函数,必须是如下格式

    onClick= { function }

  3. 用的最多的事件绑定形式为:

    <button onClick={ () => this.show('传参') }>按钮</button>
    
    // 事件的处理函数,需要定义为 一个箭头函数,然后赋值给 函数名称
    show = (arg1) => {
        console.log('show方法' + arg1)
    }
    
  4. 在React中,如果想要修改 state 中的数据,推荐使用 this.setState({ msg:'xxx'})

绑定文本框与state中的值(单向数据流)

  1. 在 Vue 中,默认提供了v-model指令,可以很方便的实现 数据的双向绑定

  2. 但是,在 React 中,默认只是单向数据流,也就是只能把 state 上的数据绑定到页面,无法把页面中数据的变化,自动同步回 state ; 如果需要把 页面上数据的变化,保存到 state,则需要程序员手动监听onChange事件,拿到最新的数据,手动调用this.setState({ }) 更改回去;

  3. 案例:

    <input type="text" style={{ width: '100%' }} value={this.state.msg} onChange={() => this.textChanged()} ref="mytxt" />
    
     // 响应 文本框 内容改变的处理函数
      textChanged = () => {
        // console.log(this);
        // console.log(this.refs.mytxt.value);
        this.setState({
          msg: this.refs.mytxt.value
        })
      }
    

    React中数据同步的流程.png

使用ref获取DOM元素引用

和 Vue 中差不多,vue 为页面上的元素提供了 ref 的属性,如果想要获取 元素引用,则需要使用this.$refs.引用名称

在 React 中,也有 ref, 如果要获取元素的引用this.refs.引用名称

组件的生命周期

Vue的生命周期图
  • 生命周期的概念:每个组件的实例,从 创建、到运行、直到销毁,在这个过程中,会出发一些列 事件,这些事件就叫做组件的生命周期函数;

  • React组件生命周期分为三部分:

    • 组件创建阶段:特点:一辈子只执行一次

    componentWillMount:
    render:
    componentDidMount:

    • 组件运行阶段:按需,根据 props 属性 或 state 状态的改变,有选择性的 执行 0 到多次

    componentWillReceiveProps:
    shouldComponentUpdate:
    componentWillUpdate:
    render:
    componentDidUpdate:

    • 组件销毁阶段:一辈子只执行一次

    componentWillUnmount:

vue中的生命周期图
React Native 中组件的生命周期

React中组件的生命周期

defaultProps

在组件创建之前,会先初始化默认的props属性,这是全局调用一次,严格地来说,这不是组件的生命周期的一部分。在组件被创建并加载候,首先调用 constructor 构造器中的 this.state = {},来初始化组件的状态。

React生命周期的回调函数总结成表格如下:


React生命周期表格

组件生命周期的执行顺序:

  1. Mounting:
    • constructor()
    • componentWillMount()
    • render()
    • componentDidMount()
  2. Updating:
    • componentWillReceiveProps(nextProps)
    • shouldComponentUpdate(nextProps, nextState)
    • componentWillUpdate(nextProps, nextState)
    • render()
    • componentDidUpdate(prevProps, prevState)
  3. Unmounting:
    • componentWillUnmount()

通过Counter计数器的小案例 - 了解生命周期函数

  1. props 属性提供默认值 和 进行类型校验,需要先运行cnpm i prop-types --save

  2. 给组件的 props 提供默认值

      // 为组件提供 默认的 props 属性值
      static defaultProps = {
        initcount: 0 // 默认值为0    如果用户没有传递 ,则 默认就是0; 如果用户传递了,则 以用户传递的为准
      }
    
  3. 给组件的 props 进行类型校验

      // 3. 进行 props 属性的类型校验,   static propTypes = {}  是固定写法
      static propTypes = {
        initcount: PropTypes.number.isRequired // 规定 外界在传递 initcount 的时候,必须是 number 值类型,否则 ,会在终端报警告
        // isRequired 表示 这个 props 属性值 是必须要传递的
      }
    

发表评论案例

相关文章

类型校验
Animation Add-Ons

总结

掌握创建组件的两种方式
理解有状态组件和无状态组件的本质区别

理解props和state的区别
cmtlist.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容

  • 作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序...
    六个周阅读 8,433评论 1 33
  • 1).React核心概念 虚拟DOM(Virtual Document Object Model)假如存在如下的H...
    DoEmpty阅读 516评论 0 0
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 8,055评论 2 35
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,818评论 0 24
  • “阿清,你想去哪座城市读大学呀?”少女娇俏地问少年,仿佛只是随口一问,脸上明媚的笑容晃花人眼,放在身侧的手却紧握着...
    陈筱柒阅读 144评论 0 1