Hello,React
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- React 核心库(必须先于 react-dom.development.js 引入) -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 提供操作 DOM 的 react 扩展库 -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 解析 JSX 语法代码转为 JS 代码的库 -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 创建虚拟 DOM
const VDOM = <h1>Hello,React</h1>
// 渲染虚拟 DOM 到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
</body>
</html>
虚拟 DOM 的两种创建方式
虚拟 DOM 本质上是 Object 类型的对象。
虚拟 DOM 比较轻,真实 DOM 比较重,因为 虚拟 DOM 是在 React 内部使用,无需真实 DOM 上那么多属性。
虚拟 DOM 对象最终都会被 React 转换为真实的 DOM
JSX 方式
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- React 核心库(必须先于 react-dom.development.js 引入) -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 提供操作 DOM 的 react 扩展库 -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 解析 JSX 语法代码转为 JS 代码的库 -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 创建虚拟 DOM
const VDOM = (
<h1 id="title">
<span>Hello,React</span>
</h1>
)
// 渲染虚拟 DOM 到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
</body>
纯 JS 方式
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- React 核心库(必须先于 react-dom.development.js 引入) -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 提供操作 DOM 的 react 扩展库 -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 不需要 babel 库了 -->
<!-- 改回 text/javascript -->
<script type="text/javascript">
// 创建虚拟 DOM
// React.createElement(标签名, 标签属性, 标签内容)
const VDOM = React.createElement('h1', { id: 'title', class: 'myClass' }, 'Hello,React')
// 渲染虚拟 DOM 到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
</body>
jsx 语法规则
- 定义虚拟 DOM 时,不能写引号
- 标签中混入 JS 表达式时要用 {}
- 样式的类名指定用的是 className,而不是 class
- 内联样式,要用 style={{key:value, key:value}} 的形式写
- 虚拟 DOM 只能一个根标签
- 标签必须闭合
- 标签首字母的不同情况
- 若标签首字母小写,则将该标签转为 HTML 中的同名元素,若 HTML 中无同名元素,则报错。
- 若标签首字母大写,则渲染对应的组件,若组件没有定义,则报错。 ```html <!DOCTYPE html>
<a name="CycyE"></a>
# 面向组件编程
以下内容都是面向组件编程。
<a name="sgccr"></a>
# 模块化 & 组件化的理解
模块的理解:向外提供特定功能的 JS 程序,一般就是一个 JS 文件<br />拆成模块的原因:随着业务逻辑增加,代码越来越多且越来越复杂<br />模块的作用:复用 JS,简化 JS 的编写,提高 JS 的运行效率<br />模块化:当应用的 JS 都是以模块来编写的,这个应用就是一个模块化的应用
---
组件的理解:用来实现局部功能效果的代码和资源的集合 (HTML / CSS / JS / image 等)<br />用组件的原因:一个界面的功能太复杂<br />组件的作用:复用编码,简化项目编码,提高运行效率<br />组件化:当应用是以多组件的方式实现的,这个应用就是一个组件化的应用
<a name="sIWT7"></a>
# 组件
<a name="Wee9l"></a>
## 函数式组件
```html
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 创建函数式组件(首字母必须大写,不然会被 jsx 解析成标签)
function MyComponent() {
// 此处的 this 是 undefined
// 因为 babel 编译后开启了严格模式 (strict)
console.log(this);
return <h1>函数定义的组件适用于简单组件</h1>
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('test'))
</script>
</body>
执行了 ReactDOM.render(
执行了 ReactDOM.render(<MyComponent/> 之后,发生了什么
1. React 解析组件标签,找到了 MyComponent 组件。
1. 发现该组件是使用类定义的,随后 new 该类的实例,并通过该实例调用到原型上的 render 方法。
1. 将render() 返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中。
<a name="LjOel"></a>
# 组件实例的属性
<a name="ilTk9"></a>
## state
```javascript
<script type="text/babel">
// 创建类式组件(首字母必须大写,不然会被 jsx 解析成标签)
class MyComponent extends React.Component {
// 构造器方法只被调用一次,创建一个实例对象
constructor(props) {
super(props)
// 初始化状态
this.state = { isHot: true, wind: '微风' }
//解决 changeWeather() 中 this 指向问题
// 前面的 changeWeather 是实例的属性,后面的 changeWeather 是在原型上的方法
this.changeWeather = this.changeWeather.bind(this)
}
// render 调用 1 + n 次。
// 1 次是初始化的时候,n 次是状态更新的次数
render() {
// ES6 的写法
const { isHot, wind } = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind} </h1>
}
// changeWeather 调用 n 次,n 是点击的次数
changeWeather() {
// 这个 changeWeather() 放 Weather 的原型对象上,供实例使用
// 由于 changeWeather() 是作为 onClick 的回调,所以不是通过实例调用的,是直接调用
// 类中的方法默认开启了局部的严格模式,所以 changeWeather() 中的 this 为 undefined
console.log(this)
// 状态必须通过 setState 进行更新,且更新是一种合并,不是替换。
// 这里对象里没有写 wind,wind 保持原来的值不变
this.setState({ isHot: !this.state.isHot })
}
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('test'))
</script>
state 的简写方式
<script type="text/babel">
// 创建类式组件(首字母必须大写,不然会被 jsx 解析成标签)
class MyComponent extends React.Component {
// 初始化状态
state = { isHot: true, wind: '微风' }
render() {
// ES6 的写法
const { isHot, wind } = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind} </h1>
}
// 自定义方法, 要用 赋值语句的形式 + 箭头函数
changeWeather = () => {
console.log(this)
this.setState({ isHot: !this.state.isHot })
}
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('test'))
</script>
props
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<!-- 引入 react 核心库 -->
<!-- 相当于拥有了 React -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作 DOM -->
<!-- 相当于拥有了 ReactDOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入 babel,用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入 prop-types,用于对组件标签属性进行限制 -->
<!-- 相当于拥有了 propTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// 创建类式组件(首字母必须大写,不然会被 jsx 解析成标签)
class Person extends React.Component {
render() {
// ES6 的语法
const { name, sex, age } = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
}
// 对标签属性进行 类型 和 必要性 限制
Person.propTypes = {
// 限制 name 必须传值,且必须为字符串类型,否则控制台报错
name: PropTypes.string.isRequired,
// 限制 sex 为字符串
sex: PropTypes.string,
// 限制 age 为数值
age: PropTypes.number,
// 限制 speak 为函数,注意是:func
speak: PropTypes.func
}
// 指定默认标签属性值(如果不写的话)
Person.defaultProps = {
// sex 默认值为男
sex: '男',
// age 默认值为 18
age: 18
}
// 渲染组件到页面
ReactDOM.render(<Person name="Tom" sex="女" age={19} />, document.getElementById('test1'))
// 批量传递 props
const p = { name: "Merry", sex: "男", age: 30 }
// 这里 {...p} 是 React 的特殊用法,不是对象的复制
ReactDOM.render(<Person {...p} />, document.getElementById('test2'))
// ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>, document.getElementById('test2'))
// 由于对标签属性进行了限制,所有该写法在浏览器的 Console 报 warning
// ReactDOM.render(<Person sex="男" age="19" />, document.getElementById('test3'))
ReactDOM.render(<Person name="郝飞宇" speak={speak} />, document.getElementById('test3'))
function speak() {
console.log("我说话了")
}
</script>
</body>
</html>
props 的简写方式
<script type="text/babel">
// 创建类式组件(首字母必须大写,不然会被 jsx 解析成标签)
class Person extends React.Component {
constructor(props) {
//构造器是否接收 props,是否传递给 super,
// 取决于:我们是否希望在构造器中通过 this 访问 props
super(props)
// 如果没有将 props 传递给 super,那么下面的 this.props 将为 undefined
// 除此之外,和不写构造器相比,无其他差别
console.log('constructor', this.props);
}
// 对标签属性进行 类型 和 必要性 限制
static propTypes = {
// 限制 name 必须传值,且必须为字符串类型,否则控制台报错
name: PropTypes.string.isRequired,
// 限制 sex 为字符串
sex: PropTypes.string,
// 限制 age 为数值
age: PropTypes.number,
// 限制 speak 为函数,注意是:func
speak: PropTypes.func
}
// 指定默认标签属性值(如果不写的话)
static defaultProps = {
// sex 默认值为男
sex: '男',
// age 默认值为 18
age: 18
}
render() {
// ES6 的语法
const { name, sex, age } = this.props
// props 是只读的,不能修改。this.props.name = "h" 将会报错
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
}
// 渲染组件到页面
// age 用的是默认值 18,sex 用的是默认值 男
ReactDOM.render(<Person name="Tom" />, document.getElementById('test1'))
</script>
函数式组件使用 props
<script type="text/babel">
// 创建函数式组件(首字母必须大写,不然会被 jsx 解析成标签)
function Person(props) {
const { name, sex, age } = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
// 对标签属性进行 类型 和 必要性 限制
Person.propTypes = {
// 限制 name 必须传值,且必须为字符串类型,否则控制台报错
name: PropTypes.string.isRequired,
// 限制 sex 为字符串
sex: PropTypes.string,
// 限制 age 为数值
age: PropTypes.number,
// 限制 speak 为函数,注意是:func
speak: PropTypes.func
}
// 指定默认标签属性值(如果不写的话)
Person.defaultProps = {
// sex 默认值为男
sex: '男',
// age 默认值为 18
age: 18
}
// 渲染组件到页面
// age 用的是默认值 18,sex 用的是默认值 男
ReactDOM.render(<Person name="Tom" />, document.getElementById('test1'))
</script>
refs
应该尽可能减少 ref 的使用
字符串形式的 ref
<script type="text/babel">
// 创建类式组件(首字母必须大写,不然会被 jsx 解析成标签)
class Demo extends React.Component {
//展示左侧输入框的数据
showData1 = () => {
const { input1 } = this.refs
alert(input1.value)
}
render() {
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData1}>点我提示左侧的数据</button>
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById("test1"))
</script>
回调函数形式的 ref
<script type="text/babel">
// 创建类式组件(首字母必须大写,不然会被 jsx 解析成标签)
class Demo extends React.Component {
//展示左侧输入框的数据
showData1 = () => {
const { input1 } = this
alert(input1.value)
}
render() {
return (
<div>
{/* 在非首次调用 render 时,该回调函数会被调用两次
第一次的 currentNode 值为 null
第二次的 currentNode 值为 节点
但对我们几乎无影响
*/}
<input ref={(currentNode) => { this.input1 = currentNode }} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData1}>点我提示左侧的数据</button>
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById("test1"))
</script>
createRef 的使用
<script type="text/babel">
// 创建类式组件(首字母必须大写,不然会被 jsx 解析成标签)
class Demo extends React.Component {
// React.createRef 调用后可以返回一个容器, 该容器可以存储被 ref 所标识的节点,
// 该容器是“专人专用”的, 即一个容器只可以存储一个节点
myRef1 = React.createRef()
//展示左侧输入框的数据
showData1 = () => {
alert(this.myRef1.current.value)
}
render() {
return (
<div>
<input ref={this.myRef1} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData1}>点我提示左侧的数据</button>
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById("test1"))
</script>
React 中的事件处理
使用方法
- 通过 onXxx 属性指定事件处理函数(注意大小写 onClick、onBlur)
- React 使用的是自定义(合成)事件,而不是使用的原生 DOM 事件。为了更好的兼容性
- React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素)。为了更高效
通过 event.target 得到发生事件的 DOM 元素对象。不要过度使用 ref ```javascript