React两大核心:
(1)虚拟Dom:用js对象来模拟页面中Dom元素及嵌套关系
(2)Diff算法:tree diff、component diff、element diff
webpack初始化项目:
创建文件夹
在文件夹中 npm init -y
根目录新建文件夹 src (源代码目录)
根目录新建文件夹 dist (产品目录)
在src中新建index.html main.js
安装webpack npm i webpack -D
npm i webpack-cli -D
根目录创建配置文件 webpack.config.js
webpack.config.js
module.exports={
mode:'development', //必选项 development production 区别:是否压缩代码
//在webpack 4.x中,有一个很大的特性,就是约定大于配置,约定默认的打包入口路径是:src->index.js
//所以将main.js更改为index.js
//默认打包的输出文件路径是dist->main.js
}
解决更改实时更新打包编译
1、npm i webpack-dev-server -D
2、在package.json->scripts中加上"dev":"webpack-dev-server --open --port 3000 --hot --host 127.0.0.1"
将首页放到内存中
1、npm i html-webpack-plugin -D
2、在webpack.config.js中导入插件:
const path = require('path')
//导入在内存中自动生成index页面的插件
const HtmlWebPackPlugin = require('html-webpack-plugin')
3、创建一个插件的实例对象: //可以将打包的js文件追加到内存的html文件中,不用手动添加script标签
const htmlPlugin = new HtmlWebPackPlugin({
template:path.join(_dirname,'./src/index.html'), //源文件
filename:'index.html'
})
4、将插件放置exports中
module.exports={
mode:'development', //必选项 development production 区别:是否压缩代码
//在webpack 4.x中,有一个很大的特性,就是约定大于配置,约定默认的打包入口路径是:src->index.js
//所以将main.js更改为index.js
//默认打包的输出文件路径是dist->main.js
plugins:[
htmlPlugin
],
module:{ //所有的第三方模块的配置规则
rules:[
{test:/\.js|jsx$/,use:'babel-loader',exclude:/node_modules/}
]
}
}
项目中使用React
//1、npm i react react-dom -S
//react:专门用于创建组件,同时组件的生命周期都在这个包里
//react-dom:专门进行DOM操作,最主要的应用场景就是,ReactDOM.render()
//2、在index.html页面中,创建容器
<div id="app"></div>
//3、在index.js中导入包:
import React from 'react'
import ReactDOM from 'react-dom'
//4、创建虚拟DOM元素
//参数1:创建的元素的类型,字符串,表示元素的名称
//参数2:一个对象或者null,表示这个DOM元素的属性
//参数3:子节点(包括其他虚拟DOM获取文本子节点)
//参数n:其他子节点
//<h1 id="myH1" title="this is a h1">这是一个H1</h1>
const myh1 = React.createElement('h1',{id:'myH1',title:'this is a h1'},'这是一个H1')
//5、使用ReactDOM吧虚拟DOM渲染到页面上
//参数1:要渲染的那个虚拟DOM元素
//参数2:指定页面上一个容器(一个DOM元素)
ReactDOM.render(myh1,document.getElemntById('app'))
//采用上面的渲染方式太麻烦,而html是最优秀的标记语言
//在js文件中默认不能写类似于HTML的标记,否则打包失败
//可以用babel来转换js中的标签
//这种在js中混合写入类似于HTML的语法,叫做jsx语法
const myDiv= <div id="myDiv" title="aaa"><div>
安装babel插件
// 安装babel插件
npm i babel-core babel-loader babel-plugin-transform-runtime -D
npm i babel-preset-env babel-preset-stage-0 -D
// 安装能够识别转换jsx语法的包babel-preset-react-D
npm i babel-preset-react -D
//在webpack.config.js配置文件中增加module
// 添加配置文件.babelrc
{
"presets":["env","stage-0","react"],
"plugins":["transform-runtime"]
}
//添加配置项
module.exports={
mode:'development', //必选项 development production 区别:是否压缩代码
//在webpack 4.x中,有一个很大的特性,就是约定大于配置,约定默认的打包入口路径是:src->index.js
//所以将main.js更改为index.js
//默认打包的输出文件路径是dist->main.js
plugins:[
htmlPlugin
],
module:{ //所有的第三方模块的配置规则
rules:[
{test:/\.js|jsx$/,use:'babel-loader',exclude:/node_modules/}
]
},
resolve:{
extensions:['.js', '.jsx', '.json'], //表示这几个文件的后缀名可以省略不写
alias:{
'@':path.join(__dirname, './src') //这样,@就表示项目根目录中src这层路径
}
}
}
jsx语法
let a = 10
//使用{}占位符
ReactDOM.render(<div>{a}</div>, document.getElementById('app'))
创建组件,组件的名称首字母必须大写
//第一种创建组件的方式
//使用构造函数来创建组件,如果要接收外界传递的数据,
//需要在构造函数的参数列表中使用props来接收,必须向外return一个合法的JSX创建的虚拟DOM
function Hello(){
//在组件中,必须返回一个合法的JSX虚拟DOM元素
return ...
}
//直接把组件名称,以标签形式,放到页面上
<Hello></Hello>
//为组件传递数据
<Hello name={dog.name} age={dog.age} gender={dog.gender}></Hello>
//使用展开运算符
<Hello {...dog}></Hello>
const dog = {
name:'大黄',
age:'3',
gender:'雄'
}
//使用props形参,在构造函数接受外界传过来的属性值
function Hello(props){
//props.name='zs' 会报错:不论是Vue还是React,组件中的props永远都是只读的,不能被重新赋值
//return null
return <div>这是Hello组件--{props.name}--{props.age}--{props.gender}</div>
}
//第二种创建组件的方式,使用class关键字来创建组件
//通过new出来的实例访问到的属性,叫实例属性
//通过构造函数直接访问到的属性,叫静态属性
//在class{}中,只能写构造器、静态方法、静态属性和实例方法
class Animal{
//constructor(){} 每个类中都有一个默认的无参构造器
constructor(name,age){
this.name=name //实例属性
this.age=age //实例属性
}
static info='aaa' //静态属性
say(){} //Animal的实例方法,挂载到原型对象上prototype
static show(){} //Animal的静态方法
}
const a1=new Animal('大黄',3)
a1.name //实例属性
Animal.info //静态属性
//class继承,使用extends实现子类继承父类,拥有父类的构造函数
//子类构造函数中需要调用super()
//子类独有的,可以在自己的构造器中体现
class Dog extends Animal{
constructor(name,age,id){
super()
this.id=id //在子类中,this只能放到super之后使用
}
}
//基于class创建组件的方式
class 组件名称 extends React.Component{
constructor(){
super()
this.state={
msg:'我是私有数据'
} //等价于Vue中的data(){return {}}
}
//组件内部必须要有render函数,用于渲染当前组件所对应的虚拟DOM元素
render(){
//必须返回合法的DOM元素
//在class关键字创建的组件中,如果想使用外界传递过来的props参数
//不需要接受,直接通过this.props.***访问
return <div>这是class创建的组件--{this.props.name}--{this.props.age}</div>
}
}
//构造函数和class关键字创建的组件的区别
//用构造函数创建的组件:叫做无状态组件,无自己的私有数据,只有props,无生命周期函数
//用class创建的组件:叫做有状态组件,有自己的私有数据(this.state)和生命周期函数
//有状态组件和无状态组件的本质区别:有无state属性
//this.state中的数据时可读可写的
将组件抽离:创建.jsx文件,将创建组件的构造函数放到jsx文件中
Hello.jsx
//使用props形参,在构造函数接受外界传过来的属性值
export default function Hello(props){
//props.name='zs' 会报错:不论是Vue还是React,组件中的props永远都是只读的,不能被重新赋值
//return null
return <div>这是Hello组件--{props.name}--{props.age}--{props.gender}</div>
}
//export default Hello 将组件暴露出去
//使用组件的时候,直接导入
import Hello from './../Hello.jsx'
解决导入组件时,统一.jsx后缀
//在webpack.config.js配置文件的导出对象中增加resolve
resolve:{
extensions:['.js', '.jsx', '.json'] //表示这几个文件的后缀名可以省略不写
}
解决文件路径问题
import Hello form './../Hello'
//在配置文件的导出对象中,extension的同级配置alias
alias:{
'@':path.join(__dirname, './src') //这样,@就表示项目根目录中src这层路径
}
JSX中CSS等样式的写法
//以样式对象的形式
//在样式中,数值类型不需单引号包裹,字符串须单引号包裹
//如果关键字有‘-’,则也须要单引号包裹
<h1 style={{color:'red',fontSize:'35px'}}>这是评论列表组件</h1>
//样式表的形式,className=''
//1、安装加载器
npm i style-loader css-loader -D
//2、在webpack.config.js配置文件中配置加载器,打包处理css样式表的第三方loader
{test:/\.css$/, use:['style-loader','css-loader?modules']}
//3、导入样式表
import cssObj from '@/../css/xxx.css'
//直接导入的样式表默认在全局,没有作用域,整个项目上都生效
//解决css样式表全局有效,导致样式冲突的问题
//css-loader启用模块化,在配置文件的css-loader加载器中追加参数
{test:/\.css$/, use:['style-loader','css-loader']}
//开启之后,CSS样式表导入之后就是对象了,样式需要通过样式对象去访问
//只对类选择器和Id选择器生效
import cssObj from '@/../css/xxx.css'
<div className={cssObj.xx}></div>
//取消模块化,使用全局样式,全局生效,模块化:local()(默认)
//在样式表中使用:global(.title){}
:global(.title){
fontSize:'25px'
}
//使用复合样式
<div className={cssObj.xx + ' 样式2'}></div>
<div className={[cssObj.xx,'样式2'].join(' ')}></div>
//安装字体loader
npm i url-loader -D
npm i file-loader -D
{test:/\.ttf|woff|woff2|eot|svg$/, use:'url-loader'}
//安装.scss .less的loader
npm i sass-loader node-sass -D
{test:/\.scss$/, use:['style-loader','css-loader?modules','sass-loader']}
解决第三方样式表的模块化问题
//导入第三方样式表
//如果在引用某个包的时候,这个包被安装到了node_modules目录中,则可以省略node_modules这一层目录
//直接以包名开始引入自己的模块或者样式表
import 'bootstrap/dist/css/bootstrap.css'
//取消了第三方样式表的模块化,就不用按照 import xx from 'path'的形式导入
//导入方式对比自己的样式表
import cssObj from '@/../css/xxx.scss'
//一般第三方的样式表都是以.css文件结尾,所以可以将自己的样式文件定义为.scss或者.less文件
//只对.scss .less文件启用模块化
{test:/\.scss$/, use:['style-loader','css-loader?modules','sass-loader']}
{test:/\.css$/, use:['style-loader','css-loader']}
React中绑定事件
//为事件提供的处理函数,注意:函数后不带'()',以下均为class内部
onClick={function}
<button onClick={this.show}>按钮</button>
show(){
console.log('show方法')
}
//用的最多的事件绑定形式
//匿名函数this指向外层作用域的调用者
<button onClick={()=>this.show('传参')}>按钮</button>
//事件的处理函数,把匿名函数的引用赋给show
show = (arg1)=>{
console.log('show方法'+arg1)
}
//React中,如果想为state中的数据重新赋值,不要使用this.state.msg='123'
//应该使用this.setState({msg:'123'})
//推荐使用this.setState({}),只会把对应的state状态更新,而不会覆盖其他的state状态
//注意:this.setState()方法是异步的
//如果想在更新完state状态的时候拿到最新的值,可以使用this.setState({},callback)
this.setState({},function(){ //回调函数
console.log()
})
//绑定文本框和state中的值
//改变的state状态值能自动同步到页面上-> 单向数据流
//页面的值同步到state中 -> react不提供,所以需要程序员手动监听文本框的onChange事件,
//调用this.setState({})手动同步
//当为文本框绑定value值以后,要么同时提供一个readonly,要么提供一个onChange()处理函数
<input type="text" value={this.state.msg} readonly/>
<input type="text" value={this.state.msg} onChange={()=>this.txtChanged()}/>
txtChange = ()=>{
console.log()
}
//onChange事件中,获取文本框的值,有两种方案
//1.通过事件参数e来获取
e.target.value
//2.this.refs
<input type="text" value={this.state.msg} onChange={()=>this.txtChanged()} ref="txt"/>
this.refs.txt.value
React组件生命周期
React路由:react-router
npm install react-router-dom
//导入路由模块
//HashRouter表示一个路由的根容器,将来所有的路由相关的东西,都要包裹在HashRouter中,
//且一个网站中只需要使用一次HashRouter
//Route表示一个路由规则,在Route上,有两个比较重要的属性,path component
//Link表示路由的链接,类似于Vue中的<router-link>
import {HashRouter,Route,Link} from 'react-router-dom'
//当使用HashRouter把App根组件的元素包裹起来之后,网站就已经启用路由了,访问地址后会加上'#'
//在一个HashRouter中只能有一个唯一的根元素
import Home from '../Home.jsx'
import Moviefrom '../Movie.jsx'
import Aboutfrom '../About.jsx'
<link to='/home'>首页</link>
<link to='/movie'>电影</link>
<link to='/about'>关于</link>
//路由规则 path表示要匹配的路由,component表示要展示的组件
//route除了是路由规则,还是一个占位符
//默认是模糊匹配,如果需要精确匹配,加上exact属性
//如果需要加参数,可以在匹配规则中使用':+参数',表示i这个位置匹配到是的参数
//如果想要获取参数,可使用this.props.mathch.params.xx
//<Route path='/movie/:type/:id' component={Movie} exact>
<Route path='/home' component={Home}>
<Route path='/movie' component={Movie} exact>
<Route path='/about' component={About}>
//使用路由中的switch包裹,能够指定,如果前面的路由优先匹配到,则放弃匹配后续路由
<Switch></Switch>
react UI框架:antd
//使用fetch API获取数据
//第一个.then中获取不到数据,拿到的是一个response对象
//通过response.json()获取数据
fetch('url')
.then(response=>{
return response.json()
})
.then(data=>{
console.log(data)
})
//使用第三方工具解决fetch无法跨域请求问题
npm i fetch-jsonp
import fetchJSONP from'fetch-jsonp'
fetchJSONP('url')
.then(response=>{
return response.json()
})
.then(data=>{
console.log(data)
})
//url
const url='../${}'
钩子函数
//在组件render之前立即调用
componentWillMount()
//所有的组件(包括子组件)在render执行完之后立即调用,并且只会被调用一次。
componentDidMount()
//在props被改变时被触发,初始化render时不调用。
componentWillReceiveProps(nextProps)
//发生重渲染时,在render()函数调用前被调用的函数,当函数返回false时候,
//阻止接下来的render()函数的调用,阻止组件重渲染,而返回true时,组件照常重渲染。
shouldComponentUpdate(nextProps, nextState)