1. 在项目中使用 react
-
运行
cnpm i react react-dom -S
安装包- react: 专门用于创建组件和虚拟DOM的,同时组件的生命周期都在这个包中
- react-dom: 专门进行DOM操作的,最主要的应用场景,就是
ReactDOM.render()
-
在
index.html
页面中,创建容器:<!-- 容器,将来,使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中 --> <div id="app"></div>
-
js文件中导入包:
import React from 'react' import ReactDOM from 'react-dom'
-
创建虚拟DOM元素:
// 这是 创建虚拟DOM元素的 API <h1 title="啊,五环" id="myh1">你比四环多一环</h1> // 第一个参数: 字符串类型的参数,表示要创建的标签的名称 // 第二个参数:对象类型的参数, 表示创建的元素的属性节点,可以用null // 第三个参数: 子节点 const myh1 = React.createElement('h1', { title: '啊,五环', id: 'myh1' }, '你比四环多一环')
-
渲染:
// 3. 渲染虚拟DOM元素 // 参数1: 表示要渲染的虚拟DOM对象 // 参数2: 指定容器,注意:这里不能直接放 容器元素的Id字符串,需要放一个容器的DOM对象 ReactDOM.render(myh1, document.getElementById('app'))
2. JSX语法
什么是JSX语法:就是符合 xml 规范的 JS 语法;(语法格式相对来说,要比HTML严谨很多)
-
如何启用 jsx 语法?
-
安装
babel
插件- 运行
cnpm i babel-core babel-loader babel-plugin-transform-runtime -D
- 运行
cnpm i babel-preset-env babel-preset-stage-0 -D
- 运行
-
安装能够识别转换jsx语法的包
babel-preset-react
- 运行
cnpm i babel-preset-react -D
- 运行
-
添加
.babelrc
配置文件{ "presets": ["env", "stage-0", "react"], "plugins": ["transform-runtime"] }
-
添加babel-loader配置项:
module: { //要打包的第三方模块 rules: [ { test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/ } ] }
-
-
jsx 语法的本质: 并不是直接把 jsx 渲染到页面上,而是内部先转换成了 createElement 形式,再渲染的;
-
在 jsx 中混合写入 js 表达式:在 jsx 语法中,要把 JS代码写到
{ }
中- 渲染数字
let a = 10; ReactDOM.render(<div>{a}</div>, document.getElementById('app'))
- 渲染字符串
let a = 'aaa'; ReactDOM.render(<div>{a}</div>, document.getElementById('app'))
- 渲染布尔值
let a = false; ReactDOM.render(<div>{a ? '条件为真' : '条件为假'}</div>,document.getElementById('app'))
- 为属性绑定值
let title = "sss"; ReactDOM.render(<div title={title}></div>, document.getElementById('app'))
- 渲染jsx元素
let aaa = <h1>111</h1>;\ ReactDOM.render(<div>{aaa}</div>, document.getElementById('app'))
- 渲染jsx元素数组
let arr = [ <h1>111</h1>, <h2>222</h2> ]; ReactDOM.render(<div>{arr}</div>, document.getElementById('app'))
- 将普通字符串数组,转为jsx数组并渲染到页面上【两种方案】
第一种
第二种const arrStr = ['aaa','bbb','ccc'] const nameArr = [] arrStr.forEach(item => { const temp = <h1>{item}</h1> nameArr.push(temp) }) ReactDOM.render(<div>{nameArr}</div>, document.getElementById('app'))
const arrStr = ['aaa','bbb','ccc'] ReactDOM.render(<div> {arrStr.map(item => <h1>{item}</h1>)} </div>, document.getElementById('app'))
- 渲染数字
-
在 jsx 中 写注释:推荐使用
{ /* 这是注释 */ }
-
为 jsx 中的元素添加class类名:需要使用
className
来替代class
;htmlFor
替换label的for
属性 -
在JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹,如果不想多一个没用的根元素包裹,可以用react提供的占位符Fragment,Fragment不会渲染到html结构中;
import React, { Component , Fragment} from 'react' class TodoList extends Component{ render() { return ( <Fragment> <input /><button>提交</button> <ul> <li>react</li> <li>vue</li> </ul> </Fragment> ) } } export default TodoList
-
在 jsx 语法中,标签必须 成对出现,如果是单标签,则必须自闭和!
当 编译引擎,在编译JSX代码的时候,如果遇到了
<
那么就把它当作 HTML代码去编译,如果遇到了{}
就把 花括号内部的代码当作 普通JS代码去编译;
3. React中创建组件
第1种 - 创建组件的方式
使用构造函数来创建组件,如果要接收外界传递的数据,需要在 构造函数的参数列表中使用
props
来接收;必须要向外return一个合法的JSX创建的虚拟DOM;
-
创建组件:
function Hello () { // return null return <div>Hello 组件</div> }
-
为组件传递数据:
const dog = { name : "大黄", age : 3 } // 在构造函数中,使用 props 形参,接收外界 传递过来的数据 function Hello(props) { // props.name = 'zs' console.log(props) // 结论:不论是 Vue 还是 React,组件中的 props 永远都是只读的;不能被重新赋值; return <div>这是 Hello 组件 --- {props.name} --- {props.age}</div> } // 使用组件并 为组件传递 props 数据 ReactDOM.render(<div> <Hello name={dog.name} age={dog.age}></Hello> //es6写法 //<Hello {...dog}></Hello> </div>, document.getElementById('app'))
-
父组件向子组件传递数据
-
使用{…obj}属性扩散传递数据
-
将组件封装到单独的文件中
-
注意:组件的名称首字母必须是大写
-
在导入组件的时候,如何省略组件的
.jsx
后缀名:// 打开 webpack.config.js ,并在导出的配置对象中,新增 如下节点: resolve: { extensions: ['.js', '.jsx', '.json'], // 表示,这几个文件的后缀名,可以省略不写 alias: { '@': path.join(__dirname, './src')// 这样@就表示项目根目录中src这一层目录 } }
-
在导入组件的时候,配置和使用
@
路径符号
第2种 - 创建组件的方式
使用 class 关键字来创建组件
ES6 中 class 关键字,是实现面向对象编程的新形式;
了解ES6中 class 关键字的使用
-
class 中
constructor
的基本使用 -
实例属性和实例方法
-
静态属性和静态方法
// 创建了一个动物类 // 注意:在 class 的 { } 区间内,只能写 构造器、静态方法和静态属性、实例方法 class Animal { // 这是类中的 构造器 // 每一个类中,都有一个构造器,如果我们程序员没有手动指定构造器,那么,可以认为类内部有个隐形的、看不见的 空构造器,类似于 constructor(){} // 构造器的作用,就是,每当 new 这个类的时候,必然会优先执行 构造器中的代码 constructor(name, age) { // 实例属性 this.name = name; this.age = age } // 在 class 内部,通过 static 修饰的属性,就是静态属性 static info = "eee" // (用的不多) // 这是动物的实例方法(会经常用到 实例方法) jiao() { console.log('动物的实例方法') } // 这是 动物 类的静态方法(用的不多) static show() { console.log('这是 Animal 的静态 show 方法') } } const a1 = new Animal('大黄', 3); console.log(a1); a1.jiao(); Animal.show();
-
使用
extends
关键字实现继承// 这是父类 【可以直接把 父类,理解成 原型对象 prototype】 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){ // 问题1:为什么一定要在 constructor 中调用 super // 答案: 因为,如果一个子类,通过 extends 关键字继承了父类,那么,在子类的 constructor 构造函数中,必须 优先调用一下 super(),规定 // 问题2:super 是个什么东西? // 答案: super 是一个函数,而且,它是 父类的 构造器;子类中的 super,其实就是父类中,constructor 构造器的一个引用; super(name, age) } } const a1 = new American('Jack', 20) console.log(a1) a1.sayHello() // 这是子类 中国人 class Chinese extends Person{ // IDNumber 身份证号 【中国人独有的】,既然是独有的,就不适合 挂载到 父类上; constructor(name, age, IDNumber){ super(name, age) // 语法规范:在子类中, this 只能放到 super 之后使用 this.IDNumber = IDNumber } } const c1 = new Chinese('张三', 22, '130428******') console.log(c1) c1.sayHello()
基于class关键字创建组件
-
最基本的组件结构:
// 如果要使用 class 定义组件,必须 让自己的组件,继承自 React.Component class 组件名称 extends React.Component { // 在 组件内部,必须有 render 函数,作用:渲染当前组件对应的 虚拟DOM结构 render(){ // render 函数中,必须 返回合法的 JSX 虚拟DOM结构 return <div>这是 class 创建的组件</div> } }
-
组件的props和state
// import React, {Component} from 'react' import React from 'react' import ReactDOM from 'react-dom' // class 关键字创建组件 class Movie extends React.Component { constructor() { // 由于 Movie 组件,继承了 React.Component 这个父类,所以,自定义的构造器中,必须 调用 super() super() // 只有调用了 super() 以后,才能使用 this 关键字 this.state = { msg: '大家好,我是 class 创建的 Movie组件' } } // render 函数的作用,是 渲染 当前组件所对应的 虚拟DOM元素 render() { // 在 class 关键字创建的组件中,如果想使用 外界传递过来的 props 参数,不需接收,直接通过 this.props.*** 访问即可 // 注意:不论是 class 还是普通 function 创建的组件,它们的 props 都是只读的; // 在 class 创建的组件中, this.state 上的数据,都是可读可写的! // this.state.msg = 'msg的值被我修改了!' return <div> {/* 注意:在 class 组件内部,this 表示 当前组件的实例对象 */} 这是 Movie 组件 -- {this.props.name} -- {this.props.age} -- {this.props.gender} <h3>{this.state.msg}</h3> </div> } } const user = { name: 'zs', age: 22, gender: '男' } // 3. 调用 render 函数渲染 ReactDOM.render(<div> {/* 这里的 Movie 标签,其实,就是 Movie 类的一个实例对象 */} {/* <Movie name={user.name} age={user.age}></Movie> */} <Movie {...user}></Movie> </div>, document.getElementById('app'))
4. 两种创建组件方式的对比
注意:使用 class 关键字创建的组件,有自己的私有数据(this.state) 和 生命周期函数;
注意:使用 function 创建的组件,只有props,没有自己的私有数据和 生命周期函数;
- 用构造函数创建出来的组件:叫做“无状态组件”【无状态组件今后用的不多】
- 用class关键字创建出来的组件:叫做“有状态组件”【今后用的最多】
- 什么情况下使用有状态组件?什么情况下使用无状态组件?
- 如果一个组件需要有自己的私有数据,则推荐使用:class创建的有状态组件;
- 如果一个组件不需要有私有的数据,则推荐使用:无状态组件;
- React官方说:无状态组件,由于没有自己的state和生命周期函数,所以运行效率会比 有状态组件稍微高一些;
有状态组件和无状态组件之间的本质区别就是:有无state属性、和 有无生命周期函数;
- 组件中的
props
和state/data
之间的区别- props 中的数据都是外界传递过来的;
- state/data 中的数据,都是组件私有的;(通过 Ajax 获取回来的数据,一般都是私有数据);
- props 中的数据都是只读的;不能重新赋值;
- state/data 中的数据,都是可读可写的;