1. React基础知识
1.1注意事项:
在
jsx
中 写注释**:推荐使用{ /* 这是注释 */ }
**为
jsx
中的元素添加class类名
- 使用
className
来替代class
;htmlFor
替换label的for
属性在
JSX
创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹;在
jsx
语法中,标签必须 成对出现,如果是单标签,则必须自闭和!当 编译引擎,在编译
JSX代码
的时候,如果遇到了<
那么就把它当作 HTML代码去编译,如果遇到了{}
就把 花括号内部的代码当作 普通JS
代码去编译;React中创建组件(组件的名称首字母必须是大写)
不论是 class 还是 普通的function创建的组件,它们的props都是只读的
在 render 函数中不能使用
setState
setState
会触发 render 函数的执行,如果在 render 函数中,又调用了setState
,则会进入死循环!
1.2手动创建:基本的webpack4.x
项目
- 首先新建项目文件夹
webpack-base
- 在项目根目录:运行
npm init -y
快速初始化项目- 在项目根目录:创建
src
源代码目录和dist
产品目录和test目录
在
src
目录下:
- 创建
index.html
- 创建
index.js
- 使用
cnpm 安装 webpack ,
运行:cnpm i webpack webpack-cli -D ;安装 webpack 运行:cnpm i webpack-dev-server -D ;开启 实时打包编译 运行:cnpm i html-webpack-plugin -D 运行:cnpm i url-loader file-loader -D 解析图片 运行:cnpm i style-loader css-loader -D 处理第三方样式表 运行:cnpm i sass-loader node-sass -D 处理自己的样式表 运行:cnpm i @babel/core babel-loader @babel/plugin-transform-runtime -D 运行:cnpm i @babel/preset-env @babel/preset-stage-0 -D 运行:cnpm i @babel/preset-react -D 安装能够识别转换jsx语法的包 运行:cnpm i antd -S 安装 AntDesign 组件库 运行cnpm i babel-plugin-import -D 开发环境安装按需导入的babel插件
如何安装
cnpm
: 全局运行npm i cnpm -g
注意:
webpack 4.x
提供了 约定大于配置的概念;目的是为了尽量减少 配置文件的体积;
- 默认约定了:
- 打包的入口是
src
->index.js
- 打包的输出文件是
dist
->main.js
webpack4.x
中 新增了mode
选项(为必选项),可选的值为:development
和production
;
- 在项目根目录/新建
webpack.config.js
const path = require('path') const HtmlPlugin = require('html-webpack-plugin') const htmlPlugin = new HtmlPlugin({ template: path.join(__dirname, './src/index.html'), // 指定模板文件 filename: 'index.html' // 指定内存中生成的首页的名称 }) // 导入webpack的配置对象 module.exports = { // 必须要有mode属性:有两个可选值 development production mode: 'development', // 注意:默认4.x 的webpack中,约定了打包的入口是 src->index.js 出口是dist->main.js // 插件节点 plugins: [ htmlPlugin ], module: { rules: [ { test: /\.jpg|png|gif|bmp|webp$/, use: 'url-loader' }, // 处理图片 { test: /\.ttf|woff|woff2|eot|svg$/, use: 'url-loader' }, // 处理字体文件 { test: /\.css$/i, use: ['style-loader', 'css-loader'] }, // 处理普通css样式表 { test: /\.scss$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: { localIdentName: "[path][name]-[local]-[hash:5]" } } }], //打包处理css样式表的第三方loader }, { test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/ } // babel-loader ] }, resolve: { extensions: ['.js', '.jsx', '.json'], // 可省略的扩展名 alias: { '@': path.join(__dirname, './src') // 设置别名 } } }
- 在项目根目录/新建
.babelrc
文件{ "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ "@babel/plugin-transform-runtime", ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }] ] }
1.3设置样式
1.3.1多个className
怎么设置??
<p className={title,' test'}>内容1</p>
<p className={[title,'test'].join(' ')}>内容2</p>
1.3…2使用普通的 style
样式
注意:在
JSX
语法中,如果想写 行内样式了,不能 为style 设置 字符串的值而是应该
style={ {color: 'red', fontWeight: 200} }
在行内样式中,如果是 数值类型的样式;则可以不用引号包裹;
如果是字符串类型的样式值,必须用 引号包裹
<h1 style={ {color: 'red', fontWeight: 200} }></h1>
1.3.3启用css-modules
使用
css样式表
美化组件
- 在
src / 新建css文件夹 / cmtList.css文件
css 模块化
,只针对 类选择器 和 Id选择器 生效css 模块化
,不会将标签选择器模块化.title { color: red; }
- 运行
cnpm i style-loader css-loader -D
- 修改
webpack.config.js
这个配置文件,为.css
后缀名的样式表 启用CSS 模块化
module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader?modules'] } ] }
- 在需要的组件中,
import
导入样式表,并接收模块化的CSS
样式对象:import cssObj from '@/css/CmtList.css'
- 在需要的HTML标签上,使用
className
指定模块化的样式:<h1 className={cssObj.title}>评论列表组件</h1>
1.3.4使用localIdentName
自定义生成的类名格式
可选的参数有:
- [path] 表示样式表
相对于项目根目录
所在路径- [name] 表示 样式表文件名称
- [local] 表示样式的类名定义名称
[hash:length]
表示32位的hash值(只要 hash值 < 32,可以任意取)修改
webpack.config.js
这个配置文件(以此为准)module: { //所有第三方模块的配置规则 rules: [ //第三方匹配规则 { test: /\.css$/, use: [ {loader: "style-loader"}, {loader: "css-loader", options: { modules: { localIdentName: "[path][name]-[local]-[hash:5]" } } }], //打包处理css样式表的第三方loader } ] },
1.3.5使用 :local()
和 :global()
- :local()`包裹的类名,是被模块化的类名,只能通过
className={cssObj.类名}
来使用同时,
:local
默认可以不写,这样,默认在样式表中定义的类名,都是被模块化的类名;:local(.title) { color: red; }
:global()
包裹的类名,是全局生效的,不会被css-modules
控制,定义的类名是什么,就是使用定义的类名className="类名"
:global(.title) { color: red; }
1.3.6在项目中启用模块化并同时使用bootstrap
运行:
cnpm i bootstrap@3.3.7 -S
cnpm i url-loader -D
cnpm i file-loader -D
在需要的组件中,
import
导入样式表,并接收模块化的CSS
样式对象:如果在引用某个包的时候,这个包被安装到了node_modules目录中,
则可以省略 node_modules 这一层目录,直接以包名开始引入自己的模块或样式表
import 'bootstrap/dist/css/bootstrap.css'
修改
webpack.config.js
这个配置文件,打包处理字体文件的 loadermodule: { rules: [ { test: /\.ttf|woff|woff2|eot|svg$/, use: 'url-loader' } ] }
1.3.7在项目中为 scss
文件 或者 less文件启用模块化
自己规定:
- 把 自己的样式表,定义为
.scss
文件 或者.less
文件;启用模块化即可;- 第三方的样式表,还是 以
.css
结尾,这样我们不要为普通的.css
启用模块化运行
cnpm i sass-loader node-sass -D
安装能够解析scss
文件的loader修改
webpack.config.js
这个配置文件,打包处理scss
文件的 loadermodule: { rules: [ { test: /\.scss$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: { localIdentName: "[path][name]-[local]-[hash:5]" } } }], //打包处理css样式表的第三方loader } ] }
使用方法:
在需要的组件中,
import
导入样式表,并接收模块化的CSS
样式对象:import 'bootstrap/dist/css/bootstrap.css' import cssObj from '@/css/CmtList.scss'
<button className="btn btn-primary">按钮</button>
2.在项目中使用 react
-
运行
cnpm i react react-dom -S
安装包- react: 专门用于创建组件和虚拟DOM的,同时组件的生命周期都在这个包中
react-dom
: 专门进行DOM操作的,最主要的应用场景,就是ReactDOM.render()
运行:
npm run dev
启动项目 -
在
index.html
页面中,创建容器:<!-- 容器,将来,使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中 --> <div id="app"></div>
-
在
index.js
页面中-
导入包:
import React from 'react' import ReactDOM from 'react-dom'
-
创建虚拟DOM元素
这是 创建虚拟DOM元素的
API
你比四环多一环
第一个参数: 字符串类型的参数,表示要创建的标签的名称
第二个参数:对象类型的参数, 表示 创建的元素的属性节点第三个参数: 子节点
const myh1 = React.createElement('h1', { title: '啊,五环', id: 'myh1' }, '你比四环多一环')
-
渲染虚拟DOM元素
参数1: 表示要渲染的虚拟DOM对象
参数2: 指定容器,注意:这里不能直接放 容器元素的Id字符串,需要放一个容器的DOM对象ReactDOM.render(myh1, document.getElementById('app'))
index.js
页面(实现虚拟DOM嵌套:mydiv > myh1
)import React from 'react' import ReactDOM from 'react-dom' const myh1 = React.createElement('h1', { title: '啊,五环', id: 'myh1' }, '你比四环多一环') const mydiv = React.createElement('h1', { title: '啊,五环', id: 'myh1' }, '你比四环多一环',myh1) ReactDOM.render(mydiv, document.getElementById('app'))
-
3. JSX
语法
在
index.js
页面中(项目中的写法)
const mydiv = <div id="mydiv" title="div aaa">
这是一个div元素
<h1>这是一个大大的H1</h1>
</div>
什么是
JSX
语法:就是符合xml
规范的JS
语法;(语法格式相对来说,要比HTML严谨很多)
3.1如何启用jsx
语法?
-
安装
babel
插件- 运行
cnpm i @babel/core babel-loader @babel/plugin-transform-runtime -D
- 运行
cnpm i @babel/preset-env @babel/preset-stage-0 -D
- 运行
-
安装能够识别转换
jsx
语法的包- 运行
cnpm i @babel/preset-react -D
- 运行
-
与
src
目录平级,添加.babelrc
配置文件{ "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ "@babel/plugin-transform-runtime" ] }
-
在
webpack.config.js
页面中,添加babel-loader配置项:module: { //要打包的第三方模块 rules: [ { test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/ } ] }
jsx
语法的本质:并不是直接把jsx
渲染到页面上,而是 内部先转换成了createElement
形式,再渲染的
在jsx
中混合写入js 表达式
:在 jsx
语法中,要把JS代码
写到 { }
中
- 渲染数字
- 渲染字符串
- 渲染布尔值
- 为属性绑定值
- 渲染
jsx
元素 - 渲染
jsx
元素数组
let aa = 12
let title = "999"
let body = false
const h1 = <h1>的骄傲开发</h1>
const array = [
<h1>的骄傲开22发</h1>
<h1>的骄傲开11发</h1>
]
ReactDOM.render(<div>
{a + 2}
{body?'条件为真':'条件为假'}
<p title={title}>这是p标签</p>
{array}
{title}
</div>, document.getElementById('app'))
-
将普通字符串数组,转为
jsx
数组并渲染到页面上【两种方案】- 方法1:
const arrstr = ["11","22","33"] // 定义一个空数组,将来用来存放 名称 标签 const newArr = [] arrstr.forEach(item=>{ const temp = <h5 key={item}>{item}</h5> newArr.push(temp) } ReactDOM.render(<div> {newArr} </div>, document.getElementById('app'))
- 方法2
const arrstr = ["11","22","33"] ReactDOM.render(<div> {arrstr.map(item=>{ return <h3 key={item}>{item}</h3> })} </div>, document.getElementById('app'))
4.将组件封装到单独的文件components中
例如:新建
src/components/Hello.jsx
- 方法1:使用 function 创建组件
import React from 'react' export default function Hello () { // return null return <div>Hello 组件</div> }
- 方法2:使用 class 关键字 创建组件
import React from 'react' export default class Movie extends React.Component { constructor(){ super() // 组件私有数据 this.state = { } } render(){ return <div>内容</div> } }
然后在
index.js
页面中,引入组件import Hello from './components/Hello.jsx'
在导入组件的时候,如何省略组件的
.jsx
后缀名:
- 在导入组件的时候,配置和使用
@
路径符号// 打开 webpack.config.js ,并在导出的配置对象中,新增 如下节点: resolve: { extensions: ['.js', '.jsx', '.json'], // 表示,这几个文件的后缀名,可以省略不写 alias: { '@': path.join(__dirname, './src') } }
以上配置完成后,可以使用下面的引入方式:
在
index.js
页面中,引入组件import Hello from '@/components/Hello'
5.创建组件的方式:
第1种 - 使用构造函数来创建组件
使用构造函数来创建组件,然后直接把组件的名称 以标签的形式,丢到页面上即可。
如果要接收外界传递的数据,需要在构造函数的参数列表中使用
props
来接收;必须要向外return一个合法的
JSX
创建的虚拟DOM;如果在一个组件中 return 一个 null 则表示此组件是空的 什么都不会渲染
-
创建组件:
import React from 'react' import ReactDOM from 'react-dom' function Hello () { // return null return <div>Hello 组件</div> } ReactDOM.render(<div> <Hello></Hello> </div>, document.getElementById('app'))
-
为组件传递数据(2种方法)
注意:不论是
Vue
还是 React,组件中的 props 永远都是只读的;不能被重新赋值;import React from 'react' import ReactDOM from 'react-dom' const dog = { name: '大黄', age: 12, gender: '雄' } // 在构造函数中,使用 props 形参,接收外界 传递过来的数据 function Hello(props) { // props.name = 'zs' console.log(props) return <div>这是 Hello 组件 --- {props.name} --- {props.age} --- {props.gender}</div> } function Hello11(props) { // props.name = 'zs' console.log(props) return <div>这是 Hello 组件 --- {props.name} --- {props.age} --- {props.gender}</div> } ReactDOM.render(<div> <Hello name={dog.name} age={dog.age} gender={dog.gender}></Hello> <Hello11 {...dog}></Hello11> </div>, document.getElementById('app'))
第2种 - 使用 class 关键字来创建组件
index.js
页面基本结构:
import React from 'react'
import ReactDOM from 'react-dom'
// 导入 评论项 子组件
import CmtItem from '@/components/CmtItem'
class Movie extends React.Component {
constructor(){
super()
// 组件私有数据
this.state = {
}
}
render(){
return <div>内容</div>
}
}
ReactDOM.render(<div>
<Movie></Movie>
</div>, document.getElementById('app'))
如果要使用 class 定义组件,必须 让自己的组件,继承自
React.Component
在组件内部,必须有 render 函数,作用:渲染当前组件对应的 虚拟DOM结构
render 函数中,必须 返回合法的
JSX
虚拟DOM结构然后直接把组件的名称 以标签的形式,丢到页面上即可
这里的Movie标签 其实就是Movie类的一个实例
拓展:render 是 class 的实例方法
代码示例:
- 使用 class 关键字来创建组件
- 为class创建的组件传递props参数并直接使用
- 在 class 关键字创建的组件中,如果想使用外界传递过来的props参数,不需接收;
直接通过
this.props.属性
访问即可
- 在class组件内部,this表示当前组件的实例对象
- 不论是 class 还是 普通的function创建的组件,它们的props都是只读的
而 在 class 创建的组件中,
this.state.属性
都是可读可写的
- 语法规范:在子类中,this只能放在super之后使用
this.state = {};相当于vue中的data(){return { }}
import React from 'react'
import ReactDOM from 'react-dom'
class Movie extends React.Component {
constructor(name,age){
super(name,age)
// 组件私有数据
this.state = {
msg: '我是 class 创建的 Movie组件'
}
}
render(){
// this.props.name = 'lisi'(因为props是只读的,所以后台会报错)
this.state.msg = 'msg的值被我修改了'
return <div>这是 class 创建的组件--{this.props.name}--{this.state.msg}</div>
}
}
const user = {
name: 'zs',
age: 23,
gender: '男'
}
ReactDOM.render(<div>
<Movie name={user.name} age={user.age}></Movie>
<Movie {...user}></Movie>
</div>, document.getElementById('app'))
ES6
中 class 关键字,是实现面向对象编程的新形式;注意事项:
- 在 class 的 { } 区间内,只能写 构造器 静态属性和静态方法、实例方法
- class 关键字内部,还是用原来的配方实现的;所以说 我们把 class 关键字 称作 语法糖。
// 原来的配方 function Person(name,age) { this.name = name this.age = age } // 静态属性 Person.info = 'aaaa' Person.prototype.say = function() { console.log('这是Person的实例方法') } // 静态方法 Person.show = function() { console.log('这是Person的静态方法') } const p1 = new Person('王多多',18) console.log(p1) p1.say() Person.show()
6.了解ES6
中 class 关键字的使用
- class 中
constructor
的基本使用 - 实例属性和实例方法
- 通过new出来的实例,访问到的属性,叫做实例属性
- 静态属性和静态方法
- 通过构造函数直接访问到的属性,叫做静态属性。
- 在 class 内部 通过 static 修饰的属性,就是静态属性
代码示例:
在components/新建Animal.jsx
页面,然后在index.js
中引入
import Animal from '@/components/Animal
在
Animal.jsx
页面中
import React from 'react'
import ReactDOM from 'react-dom'
// 创建了一个动物类
class Animal {
// 这是类中的 构造器
// 每一个类中,都有一个构造器,如果我们程序员没有手动指定构造器,那么可以认为类内部有个隐形的、看不见的、空构造器,类似于 constructor(){}
// 构造器的作用:就是每当 new 这个类的时候,必然会优先执行构造器中的代码
constructor(name,age){
// 实例属性
this.name = name
this.age = age
}
// 在 class 内部 通过 static 修饰的属性,就是静态属性
static info = '寂寞中的旅途'
//实例方法:
jiao(){
console.log('这是实例方法')
}
// 静态方法
static show(){
console.log('这是静态方法')
}
}
const a1 = new Animal('大黄',13)
console.log(a1)
console.log(a1.name)
console.log(a1.age)
console.log(Animal.info)
a1.jiao()
Animal.show()
7.使用 extends
关键字实现继承
在 class 类中,可以使用 extends 关键字,实现 子类继承父类;语法:
class 子类 extends 父类 {}
可以把父类 理解成原型对象 prototype
- 使用
extends
关键字实现子类继承父类- 子类访问父类上的实例方法和实例属性
- constructor 构造器中 super函数的使用(如果子类中不写constructor 构造器,默认会传递数据)
- 为子类挂载独有的实例属性和方法
问题1:为什么一定要在 constructor 中 调用super()
答:因为 如果一个子类,通过 extends 关键字继承了父类,那么 在 子类的 constructor 构造函数中
必须优先调用一下super()
问题2:
super
是什么?答:super是一个函数,而且它是父类的构造器;子类中的super,
其实就是父类中 constructor 构造器的一个引用
问题3:为什么调用了super() 之后,
a1实例
的name和age变成了undefined ?答:因为没有传递name和age
代码示例:
import React from 'react' import ReactDOM from 'react-dom' class Person { constructor(name,age){ // 实例属性 this.name = name this.age = age } // 在 class 内部 通过 static 修饰的属性,就是静态属性 static info = '寂寞中的旅途' //实例方法: sayHello(){ console.log('这是实例方法') } // 静态方法 static show(){ console.log('这是静态方法') } } class American extends Person { constructor(name,age){ super(name,age) } } const a1 = new American('jack',13) console.log(a1) console.log(a1.name,a1.age) a1.sayHello() class Chinese extends Person { constructor(name,age,idCard){ super(name,age) // 语法规范:在子类中,this只能放在super之后使用 this.idCard = idCard } } const c1 = new Chinese('李四',13,122346546) console.log(c1) console.log(c1.name,c1.age) c1.sayHello()
8.两种创建组件方式的对比
注意:使用 class 关键字创建的组件,有自己的私有数据(
this.state
)和生命周期注意:使用 function 创建的组件,只有 props,没有自己的私有数据和生命周期
有状态组件和无状态组件之间的本质区别就是:有无state属性和 生命周期函数
- 用构造函数创建出来的组件:叫做“无状态组件”
- 用class关键字创建出来的组件:叫做“有状态组件”
- 什么情况下使用 有状态组件 或者 无状态组件 呢?
如果一个组件需要有自己的私有数据,推荐使用 有状态组件 如果一个组件不需要有自己的私有数据,推荐使用 无状态组件 React官方说:无状态组件,由于没有自己的state和生命周期,所以运行效率会比有状态组件稍微高一些
- 组件中的props 和 state/data 之间的区别
- props 中的数据都是外界传递过来的
- state/data 中的数据,都是组件私有的;(通过Ajax获取的数据,一般都是私有数据)
- props 中的数据都是只读的;不能重新赋值
- state/data 中的数据,都是可读可写的