jsx语法规则:
1. 定义虚拟dom, 不要使用引号
2. 标签中混入JS表达式要用{}
3. 样式的类名不要使用class, 要使用className
4. 内联样式, 要用style = {{key: value}}的形式
5. 只有一个根标签
6. 标签必须闭合
7. 标签的字母
1. 若小写字母开头, 规则将标签转为html中同名元素, 若没有则报错
2. 若大写字母开头, react就会去渲染对应的组件, 若组件没有则报错
模块:
理解: 向外部提供特定功能的js程序, 一般就是一个js文件
作用: 复用js, 简化js的编写
模块化:
当应用的js都以模块的方式来编写, 就可以成为模块化应用
组件:
理解: 用来实现局部功能效果的代码和资源的集合(html/js/image等等)
作用: 复用代码, 简化项目编码
组件化:
当项目多以组件的方式来实现, 就可以成为组件化
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<!-- react 核心库 必须先导入这个-->
<script src="https://fb.me/react-15.1.0.js"></script>
<!-- react 操作dom的库 -->
<script src="https://fb.me/react-dom-15.1.0.js"></script>
<!-- babel 将jsx转为js的库 -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<div id="app"> </div>
<script type="text/babel">
// 1. 准备数据
const data = ['Angular', 'React', 'Vue']
// 2. 创建虚拟dom
const vDom = (
<div>
<h1>前端技术框架</h1>
<ul>
{
// 这里只允许放js表达式(有返回值), 不允许放js语句
data.map((str, index) => {
return <li key={index}> { str } </li>
})
}
</ul>
</div>
)
// 3. 渲染
ReactDOM.render(vDom, document.getElementById('app'))
</script>
</body>
</html>
函数式组件
<script type="text/babel">
// 1. 创建函数式组件
// 组件首字母必须大写, 参照jsx规则
function Dom() {
console.log(this) // 这里的this 是undefine 因为babel 转换成js之后, 开启了严格模式, 导致自定义函数this 不会指向window
// babel 官网试一试, 可以查看babel翻译后的js
return <h1> hello react</h1>
}
// 这里渲染的时候需要注意, 不能直接写 Dom , 必须< /> 必须要闭合 参照jsx规则
ReactDOM.render(<Dom />, document.getElementById('app'))
/*
1. React解析组件标签, 找到Dom
2. 发现组件是使用函数㐉的, 随后调用函数, 将返回的虚拟dom转为真实的dom
*/
</script>
class
<script>
class Person {
// 构造器函数, 不是必须要写的, 赋值才有必要
constructor(name, age) {
// 这里的this 是类的实例对象
this.name = name
this.age = age
}
a = 1 // 直接被挂到实例对象上
// 这个函数也会被放到实例对象上
// 函数如果被直接调用, speak里的this为null,
// 赋值函数里的this 会自动向外层找, 找到类实例对象
eat = () => {
}
// 普通的函数 方法被放到了原型对象上
speak() {
// 这里的this 看是谁调用的,
// [注意] 调用函数 不仅仅有 实例.函数 还有 实例.call/apply/bind 都能更改this的指向
console.log(`my name is ${this.name}, age is ${this.age}`)
}
}
const p1 = new Person('jak', 20)
console.log(p1)
</script>
class组件
1. class 组件 必须继承 React.Component
2. 构造函数必须实现render() {} render里 必须有一个返回
3. 构造函数不是必须实现的, 只有两种情况需要实现构造函数
. 在构造函数里为state 赋值
. 在构造函数里绑定函数
<script type="text/babel">
// 1. 创建class组件
class Dom extends React.Component {
render() {
return (
<h1>hello class component</h1>
)
}
}
ReactDOM.render(<Dom />, document.getElementById('app'))
</script>
state(数据驱动UI), props(传递参数), refs(绑定事件) 三剑客
state的使用
<script type="text/babel">
// 1. 创建class组件
class Dom extends React.Component {
// 如果使用构造函数, 必须调用super
constructor(props) {
super(props)
this.state = {isHot: false}
}
render() {
// 解构赋值
const { isHot} = this.state
return (
<h1 onClick={this.changeWeather}>hello class component {isHot ? "hot" : 'cool'}</h1>
)
}
changeWeather() {
// 这个时候 的this是空的
// changeWeather 是被直接调用的, 被调用的时候内部使用了 严格模式, 导致this指向为空
console.log(this)
}
}
ReactDOM.render(<Dom />, document.getElementById('app'))
</script>
上面的示例里, changeWeather中this 为空的, 为空就拿不到state, 拿不到state就无法修改值, 所以要改动一下
// 构造器里拿到的this 就是实例本身, 使用bind函数进行绑定就可以了
constructor(props) {
super(props)
this.state = {isHot: false}
this.changeWeather = this.changeWeather.bind(this)
}
如果想要修改state里的值, 那么必须调用setState方法, 不然无法改变
changeWeather() {
const {isHot} = this.state
this.setState({isHot: !isHot}) // 必须调用setState修改才可以驱动UI的更改
}
也可以修改成下面这种样式
<script type="text/babel">
// 1. 创建class组件
class Dom extends React.Component {
// constructor(props) {
// super(props)
// this.state = {isHot: false}
// this.changeWeather = this.changeWeather.bind(this)
// }
// 根据类的特性, 直接赋值会被挂到实例对象上, 初始化可以这么写
state = {isHot: false}
render() {
const { isHot} = this.state
return (
<h1 onClick={this.changeWeather}>hello class component {isHot ? "hot" : 'cool'}</h1>
)
}
// 根据类的特性, 赋值函数可以被挂到实例对象上, 并且箭头函数里的this会自动向上寻找
changeWeather = () => {
const {isHot} = this.state
this.setState({isHot: !isHot})
}
}
ReactDOM.render(<Dom />, document.getElementById('app'))
</script>
使用props进行传值
<script type="text/babel">
// 1. 创建class组件
class Dom extends React.Component {
render() {
const { name, sex, age } = this.props
return (
<ul>
<li>姓名: { name }</li>
<li>性别: { sex }</li>
<li>年龄: { age }</li>
</ul>
)
}
}
// 可以使用这种展开的形式进行传递
// 注意: 原生是不允许展开对象的, 但是这里因为有babel 的转换{...p}
const p = {name:'kk', age:10, sex:'男'}
ReactDOM.render(<Dom {...p} />, document.getElementById('app'))
// 可以直接用参数单个传递
ReactDOM.render(<Dom name='cc' sex='女' age='18'/>, document.getElementById('app1'))
</script>
可以对传递参数的类型进行限制
<script type="text/babel">
// 1. 创建class组件
class Dom extends React.Component {
constructor(props) {
super(props)
// 使用这种方式, 这里调用的方式 类.proTypes 所以下面的 protypes 和 defaultProps 需要加上static
// 表示是在类上
PropTypes.checkPropTypes(Dom.propTypes, props, 'prop', 'Dom');
}
render() {
const { name, sex, age } = this.props
return (
<ul>
<li>姓名: { name }</li>
<li>性别: { sex }</li>
<li>年龄: { age + 1 }</li>
</ul>
)
}
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
sex: PropTypes.string,
speak: PropTypes.func // 注意, 这里的函数为func
}
static defaultProps = {
sex: '男',
age: 18
}
}
ReactDOM.render(<Dom name='kk' age={18} speak={ speak}/>, document.getElementById('app1'))
function speak() {
console.log('1111')
}
</script>
refs 事件绑定
字符串形式的ref
<script type="text/babel">
// 1. 创建class组件
class Dom extends React.Component {
showAlert = () => {
const input = this.refs.input
console.log(input.value)
}
render() {
const { name, sex, age } = this.props
return (
<div>
// 这里使用ref定义, 类似id定义的值
<input ref="input"/>
<button onClick={this.showAlert}>点击显示</button>
</div>
)
}
}
ReactDOM.render(<Dom />, document.getElementById('app1'))
</script>
回调形式的ref, 字符串形式的ref不被推荐
<script type="text/babel">
// 1. 创建class组件
class Dom extends React.Component {
showAlert = () => {
console.log(this.input.value)
// 也可以这么去取
const { input } = this
}
render() {
const { name, sex, age } = this.props
return (
<div>
// 回调形式的ref
<input ref={ (currentNode) => this.input = currentNode }/>
<button onClick={this.showAlert}>点击显示</button>
</div>
)
}
}
ReactDOM.render(<Dom />, document.getElementById('app1'))
</script>
回调形式还是会有一个问题, 更新UI的时候会调用两次(重新渲染的时候会先释放掉, 然后渲染新的), 可以使用class的绑定函数, 来解决这个问题
<script type="text/babel">
// 1. 创建class组件
class Dom extends React.Component {
showAlert = () => {
const {input} = this
console.log(input.value)
}
saveInput = (e) => {
this.input = e
}
render() {
const { name, sex, age } = this.props
return (
<div>
{/*
加{}表示括号里面的是要使用js表达式, 然后才能使用注释
<input ref={ (currentNode) => this.input = currentNode }/>
*/}
<input ref={ this.saveInput }/>
<button onClick={this.showAlert}>点击显示</button>
</div>
)
}
}
ReactDOM.render(<Dom />, document.getElementById('app1'))
</script>
createRef() 应该是低于16都没有这个功能, 新的是使用useRef
<script type="text/babel">
// 1. 创建class组件
class Dom extends React.Component {
// 会返回一个容器, 就已经挂到了实例自身上
// 这里面同时只能存在一份
myRef = React.createRef()
myRef2 = React.createRef()
showAlert = () => {
const {input} = this
console.log(input.value)
}
showAert2 = () => {
const {input2} = this
console.log(input2.value)
}
render() {
const { name, sex, age } = this.props
return (
<div>
<input ref={ this.myRef }/>
<button onClick={this.showAlert}>点击显示</button>
<input onBlur={this.showAert2} ref={ this.myRef2 }/>
</div>
)
}
}
ReactDOM.render(<Dom />, document.getElementById('app1'))
</script>
注意: 不要过度使用ref, event.target可以获取到DOM控件