React 从入门到入门

React 从入门到入门

这篇文章讲了 React 的基础知识,从 JSX 语法到面向组件编程:组件的属性 state,props,refs等属性
这是初学者写下的笔记,如有错误,欢迎指正!

  • React:JavaScript库 (和 jQuery 一样是个库)
  • 和 Vue 一样都尽量减少 DOM 的操作
  • Facebook 出品的前端框架

How would you React if I said I love Vue?

React HelloWorld

<div class="test"></div>
<script type="text/babel">
    // 创建虚拟 DOM
    var vdom = <h1>Hello React</h1>;
    // 将虚拟 DOM 渲染到页面
    ReactDOM.render(vdom, document.querySelector('.test'));
</script>
  • 刚上来的时候就给我来了一个报错,弄得我很懵,其实是内嵌 script 中 type 属性 应该为 type="text/babel" 否则会报一个出现意外的小于号的错(也就是无法解析 script 里面写的其他标签)

  • react.js:React 的核心库

  • react-dom.js:提供操作 DOM 的 react 扩展库

  • babel.min.js:解析 jsx => js代码 (将 ES6 语法转换成 ES5)

虚拟 DOM

  • React 创建虚拟 DOM 对象:
var myTitle = 'noobMing';
var element = React.createElement('h1',{id:myTitle},myTitle);
// 这种方法需要 babel 标签
const vDom = <h1 id={myTitle}>{myTitle}</h1>
  • 创建好的元素:
<h1 id="noobMing">noobMing</h1>

大写转换成小写:.toLowerCase()
小写转换成大写:.toUpperCase()

  • debugger 关键字:相当于在书写处添加了一个断点

JSX

  • 全称 JavaScript XML

  • 和 XML 一样都可以自定义标签 (之后叫组件标签)

  • babel.js 将 JSX 代码转换成纯 js 代码

  • 假如有可能出现嵌套的 HTML 结构那么最好用括号括起来

  • 如果我想用 li 渲染整个列表,那么我可以这么做:

let names = ['jquery', 'react', 'vue', 'angular'];
// 例如:
const ul = (<ul>
        {names.map((name,index)=>{return <li key={index}>{name}</li>})}
    </ul>)
// 渲染到页面
ReactDOM.render(ul, document.querySelector('.test'));

这就是虚拟 DOM 吗,我开始的时候想遍历整个列表,然后把他们边包裹成 li 边放到创建好的 ul 里面去,结果他不是在原来的 li 后面接着添加,而是直接覆盖掉前一个,又费事有没达到效果,看了老师的操作我想说还可以这么弄

  • 和 Vue 小程序 一样,React 强制要求循环需要一个 key 属性来确保这个属性是唯一的

面向组件编程

  • 组件标签首字母要大写
  • 分成两步:1.定义组件,2.渲染组件标签
  • 和 vue 的组件一样,最外层都要有一个父元素做包裹
  • 定义组件一共有三种方式:
// 1. 工厂函数组件 (简单组件)
function MyComponent() {
    return <h2></h2>;
}
// 渲染标签
ReactDOM.render(<MyComponent />,document.querySelector('div'));
  • 在真实 DOM 中,浏览器会将组件的最外层标签给去掉,只留下组件内部的标签
// ES6 的类组件 (复杂组件)
class MyComponent2 extends React.Component {
    render() {
        return <h2>ES6 的类组件 (复杂组件)</h2>
    }
}
  • 在这里输出的 this 指向组件对象

组件的状态:state

  • state 叫做状态机,值是对象,通过更新组件的 state 来更新组件的显示
  • 下面这段代码是你点击一下标签内文字就改变
class MyComponent2 extends React.Component {
    constructor(props) {
        // 调用父类型的构造函数
        super(props);
        // 初始化状态
        this.state = {
            isLikeMe: false
        }
        // 将新增方法中的 this 强制绑定为组件对象 (后期可以通过箭头函数来更改方法中 this 的指向)
        this.handleClick = this.handleClick.bind(this)
    }
    // 重写组件类的渲染方法
    render() {
        let { isLikeMe } = this.state;
        return <h2 onClick={this.handleClick}>{isLikeMe ? 'How would you React if I said I love Vue?' : "tip"}</h2>
    }
    // 新添加的方法:内部的 this 默认不是组件对象,而是 undefined
    handleClick() {
        // 得到原来的状态
        // this.state.isLikeMe
        // 更新状态
        this.setState({
            isLikeMe: !this.state.isLikeMe
        })
    }
}
  • 这里又和小程序有点像,想要改变 state 状态的话需要 this.setState() 方法
  • 只要你的组件有状态 (state),就不可以用工程模式 (只用函数创造组件的模式)

组件的传值:props

  • props 是只读的属性
  • 假如我想在不同的组件上渲染不同的值
function Person1(props) {
    return (<ul>
        <li>{props.name}</li>
        <li>{props.age}</li>
        <li>{props.gender}</li>
    </ul>)
}
// 假设这是从后台传过来的值
let p1 = {
    name: "noobMing",
    age: 18,
    gender: "man"
}
ReactDOM.render(<Person1 name={p1.name} age={p1.age} gender={p1.gender} />, document.querySelector('.person'));
  • 或者我想写成类组件:
class Person2 extends React.Component {
    render() {
        return (<ul>
            <li>{this.props.name}</li>
            <li>{this.props.age}</li>
            <li>{this.props.gender}</li>
        </ul>)
    }
}
// 注意传值的时候里面要加 this
  • 感觉这个 props 起到了一个传参的作用
  • props 还可以设置默认值:
// 例如我有一个 Person 组件
Person.defaultProps = {
    gender: "man"
}
  • 这里我们设置了 gender 的默认属性为 man
  • 我们想让某个值是必须的 (例如名字是必填项) 或者对名字进行判断
  • 需要引入 prop-types.min.js 这个文件
// 还是那个 Person 组件
Person.propTypes = {
    // 说明这个属性应该是 string 类型,并且是必须的
    name: propTypes.string.isRequired
}
  • 渲染部分的 js
ReactDOM.render(<Person name={p1.name} age={p1.age} gender={p1.gender} />, document.querySelector('.person'));
  • 里面这部分的写法下面两种是一样的:
// 定义一个对象 p1 把他的值传给组件
name={p1.name} age={p1.age} gender={p1.gender}
{...p1}
  • 但是我在写这里的时候发生了一个问题:刚开始的时候提示没有引入 propTypes ,当我引入 propTypes 后他会在引入的那行报错,说 require 未定义
  • 老师在视频中没有引入也可以使用 propTypes 我即使引入也会报错,但是我发现了问题所在,引入的语句属于 ES6 语法,所以 babel 会将其转化为 ES5 语法,而不知道为什么浏览器没有找到 require 函数的定义,所以才会报错
// 转换前 (ES6) 和转换后 (ES5) 的引入 propTypes 方法
import PropTypes from 'prop-types';
var PropTypes = require('prop-types');
  • 感觉 props 可以传递各种参数,需要传参的时候只用输出 props 然后在里面要找的参数就可以了

组件的属性:refs

  • ref 主要是标识组件内部的某一个元素
  • 感觉 ref 就是一个特殊的属性,它能把组件内的标签绑定到组件对象上,方便以后进行操作
// ref 的实例:绑定 input 里面的值
class Test extends React.Component {
    constructor(props) {
        super(props);
        // 修改 this
        this.showInput = this.showInput.bind(this);
        this.handleBlur = this.handleBlur.bind(this);
    }
    showInput() {
        console.log(this.input.value);
    }
    handleBlur(event) {
        // 这两种写法都能找到 input 里面的值
        alert(this.input2.value);
        alert(event.target.value);
    }
    render() {
        return (
            <div>
                // 官方建议的写法 (把当前的 input 元素绑定到组件对象上去了)
                <input type="text" ref={input => this.input = input} />
                <button onClick={this.showInput}>提示</button>
                <input type="text" onBlur={this.handleBlur} ref={input => this.input2 = input} />
            </div>
        )
    }
}
ReactDOM.render(<Test />, document.querySelector('.base'));
  • 还有一种废弃掉的写法:
// 组件的元素
<input type="text" ref="content" />
// 调用的方法
showInput() {
    // 在函数中找到组件标签
    const input = this.refs.content;
    alert(input.value);
}

组件的嵌套

注意:vscode 自动生成的 input 标签里面是没有终止符号 / 的,但是在 JSX 里面没有终止符号就会报错

  • 在使用的时候经常会出现大组件套小组件的情况,这时就需要组件的嵌套

  • 实例:要实现一个 todo list 页面,需要一个 App 大组件,里面嵌套两个小组件 MyInput 和 MyList 组件分别进行输入和显示

  • 这里面的一个问题就是 MyInput 组件获取到用户输入之后如何把内容给 MyList 组件

  • 这里我们把信息储存在他们的上一级组件之中 (App),然后把更新数据的函数当作参数传给 MyInput 组件,这样在子组件里就可以更新上一级组件的里面的信息,然后再把这个数据作为参数传给 MyList 组件,MyList 组件渲染这个列表就有了最简单的 todo 应用的效果

  • 在子组件中更新父组件里面元素的状态:状态在那个组件,更新状态的函数就应该在哪个组件

  • 代码:

// App 组件
class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            // 用 state 储存输入的内容
            todos: []
        }
        this.addtodos = this.addtodos.bind(this);
    }
    // 修改
    addtodos(todo) {
        // 获取整个列表
        let { todos } = this.state;
        // 在开头添加数据
        todos.unshift(todo);
        // 渲染回去
        this.setState({ todos });
        // 不可以这么做:this.state.todos.unshift(todo);
    }
    render() {
        return (<div>
            <h1>ToDoList</h1>
            // 把函数当作参数传到子组件上
            <MyInput addtodos={this.addtodos} />
            <MyList todos={this.state.todos} />
        </div>);
    }
}
  • MyInput 组件:
class MyInput extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            counter: 0,
        }
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
        // 设置 button 上的文字
        this.setState({
            counter: ++this.state.counter
        })
        this.button.innerHTML = "Add #" + this.state.counter;
        // 调用接收过来的函数
        this.props.addtodos(this.input.value);
        // 清空输入框
        this.input.value = null;
    }

    render() {
        return (<div>
            <input type="text" ref={input => this.input = input} />
            <button onClick={this.handleClick} ref={button => this.button = button}>Add #0</button>
        </div>)
    }
}
  • 注意:子组件接收参数是在 props 属性里面接收的

  • MyList 组件:

class MyList extends React.Component {
    render() {
        console.log(this);
        // 不得不说,React 渲染列表真的很方便
        return (<ul>{this.props.todos.map((todo, index) => {
            return <li key={index}>{todo}</li>
        })}</ul>)
    }
}
// 渲染 App 组件到页面上
ReactDOM.render(<App />, document.querySelector('.innerInput'));
  • 组件化编写功能的流程
    1. 拆分组件
    2. 实现静态组件(只有静态界面,没有动态数据和交互)
    3. 实现动态组件
      1). 实现初始化数据动态显示
      2). 实现交互功能

在组件中收集表单数据

  • 阻止默认行为:event.preventDefault()

  • 原生的 onchange 事件是在失去焦点的时候触发,而 React 中的 onChange 事件是在输入的时候就触发事件

  • 取出表单中数据有两种方法:使用 ref 在组件对象上绑定好元素;或者使用 state 属性让输入框内容的值 (value) 等于在 state 中定义的变量,然后监听输入框改变的事件,每在输入框中输入都会更新 state 中的数据 (推荐第二种)

  • 实例:两个输入框分别用 ref 和 state 绑定数据,点击 login 按钮时弹出输入的用户名和密码

class MyCompent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            pwd: ''
        }
        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleChange = this.handleChange.bind(this);
    }
    handleSubmit(event) {
        // 阻止默认行为
        event.preventDefault();
        console.log(this.nameInput);
        alert("用户名" + this.nameInput.value + "密码" + this.state.pwd);
    }
    handleChange(event) {
        // 让输入框和 state 中的数据同步
        let pwd = event.target.value;
        this.setState({ pwd });
    }
    render() {
        return (<form action="" onSubmit={this.handleSubmit}>
            <label for="name">name:</label>
            // 使用 ref 将整个元素绑定到组件对象上
            <input type="text" id="name" ref={input => this.nameInput = input} />
            <label for="password" >password:</label>
            // 在 state 中绑定数据,并且绑定监听输入框发生改变事件 handleChange
            <input type="password" id="password" value={this.state.pwd} onChange={this.handleChange} />
            <input type="submit" value="login" />
        </form>)
    }
}
ReactDOM.render(<MyCompent />, document.querySelector(".form"));
  • 受控组件:表单项输入数据能自动收集成状态 (每输入一个数,后台自动更新输入的数据:state 方法)
  • 非受控组件:需要时才手动读取表单输入框中的数据 (点击提交按钮的时候才更新数据:ref 方法)
  • 官网不推荐使用 refs 推荐使用 state
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值