vue是单向流,双向绑定数据
vue /vuex/vue-router/axios(fetch) element-ui
react mvc单向,不是双向绑定
react react-dom (react-native) 组件、jsx、DOM DIDD 、事件、组件....
react-router-dom:hash/browser
redux:react-redux、dva、redux-sage、mobx....
axios / fetch
reactui库---antd
使用vue/react我们要告别操作dom的时代,都是基于数据驱动,由数据驱动视图的更新
react比vue少实现了v-model 具体的功能
react是mvc框架(model view controll)
react完成的是:
=》监听数据的更新,当数据更新后,帮助我们去渲染视图 DOM DIFF / 虚拟DOM变为真实DOM
相比较于vue来说
=》视图更新并没有改变数据(MVC是单向的),如果需要数据的更新,则需要自己在controll层中单独的处理
1,view ----视图层,在视图层进行一些操作
2,controll--在控制层里来修改数据
3,model---数据更新会通知视图渲染
react脚手架:create-react-app
全局环境安装脚手架
npm install -g create-react-app
基于脚手架快速构建工程化的项目
create-react-app 项目名称 (如果电脑上安装了yarn,默认基于yarn安装)
不想一步步的安装的话,可以基于npx直接一步到位
npx create-react-app 项目名称
脚手架项目目录
|- node_modules 所有安装的模块
|- public
|- index.html SPA单页面应用中,各组件最后合并渲染完成的结果都会放入到页面的#root盒子中呈现
|- xxx.html MPA/SPA这里存放的是最后编译页面的模板
|- 我们还可能会在此放一些公共资源,把这些资源直接基于SRC/LINK的方式调入到页面模板中,而不是
基于webpack最后合并在一起(不建议,但是项目中可能存在一些模块不支持CommonJs/Es6Module规范,此时我们只能在这里直接引入使用了)
|- src 整个项目的大部分源码都写在这个目录下
|- index.js 项目的入口,webpack从这个文件开始导入打包(MPA中需要创建多入口文件)
|- api 数据处理
|- store Redux公共状态管理的
|- assets 存储公共资源的
|- routes 路由管理的
|- components 公共的组件
。。。
|- package.json 依赖清单
默认的配置清单
- 生产依赖项
- react react框架的核心,提供了状态、属性、组件、生命周期等
- react-dom 把JSX语法渲染成为真实的DOM,最后显示在浏览器中
- react-scripts 包含了当前工程化项目中webpack配置的东西(嫌弃把webpack放到项目目录中看上去太丑,react脚手架把所有webpack的配置项和信赖都隐藏到node_modules中了),react-scripts这个react脚本执行命令,会通知webpack打包编译
- scripts 当前项目可执行的脚本命令(yarn xxx)
- yarn start =>开发环境下启动项目(默认会基于webpack-dev-server创建一个服务,用来随时编译和渲染开发的内容)
- yarn build =>生产环境下,把编写内容打包编译,放到build文件目录下(服务器部署)
- yarn eject =>把所有隐藏在node_modules中的webpack配置项都暴露出来(方便自己根据项目需求,二次更改webpack配置 )
yarn eject把webpack暴露出来
- babel-preset-react-app 解析JSX语法的
|- scripts
|- start.js => yarn start
|- build.js => yarn build
|- config
|- 这里存储的就是webpack的配置项
系统配置默认端口号
在package.json文件里
如果执行yarn start/build 提示少模块,我们则少了谁就安装谁
yarn add @babel/plugin-transform-react-jsx @babel/plugin-transform-react-jsx-source @babel/plugin-transform-react-jsx-selfcls
端口号,PORT
域名 HOST
协议 HTTPS
"scripts": {
//mac
"startMac":"PORT=8081 HOST=127.0.0.1 HTTPS=true node scripts/start.js"
//window
"startMac":"set PORT=8081&&set HOST=127.0.0.1&&set HTTPS=true&&node scripts/start.js"
"start": "node scripts/start.js", //默认的
"build": "node scripts/build.js",
"test": "node scripts/test.js"
}
修改less的处理配置
自己配置less https://blog.csdn.net/qq_25520603/article/details/90206399
yarn add less less-loader 安装
config/webpack.config.js文件里修改
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'less-loader'
),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
}
jsx语法 -可以理解为虚拟DOM
基础语法
1,每一个组件的视图只能有一个根元素节点
ReactDOM.render([jsx],document.getElementById('[id容器]'),[CALLBACK])
=> id容器不建议是HTML或者BODY,指定一个元素容器 #Root
=> CALLBACK把虚拟DOM渲染到浏览页面中触发的回调函数
2,jsx语法中基于{变量}绑定动态数据值或者js表达式
=》null和undefined代表空元素
=》在括号中不能直接使用对象或者函数(引用类型(除数组和虚拟DOM)外都不可以,不是合法jsx元素,会把数组变成字符串,数组当中也不可以包括不能识别的引用类型)
3,给jsx元素设置样式
=》设置样式类使用的是className,而不是class
=》设置行内样式style不能是字符串,必须是一个对象 style={{color:'red'}}
4,大括号中如果是js表达式,可以返回有效的数据值或者返回一个新的jsx元素
5,jsx要求循环绑定的元素都要设置一个属性key,存储的值是当前循环中的唯一值(key是dom diff时候的重要凭证,key值一般不要设置为循环的索引,而是设置为某一个具体不变的值)
import ReactDOM,{render} from 'react-dom' //把ReactDOM,和render同时导入出来
嵌套式
let sex=0
{sex===0?<p>内容</p>:null}
循环数据
data=[{
id:1,
name:'a'
},{
id=2,
name:'b'
}]
<ul>{data.map((item,index)=>{
return '<li key={item.id}>
<span>{item.id}</span>
<span>{item.name}</span>
</li>'
})}</ul>
虚拟DOM到真实DOM的渲染原理
执行顺序肯定是先把最里层的create-element执行,执行完依次往外层 ,create-elment
/*
https://www.babeljs.cn/repl
1,把jsx基于babel-preset-react-app语法解析包变为create-element格式 babeljs.cn
=>每当遇到一个元素标签都会create-element
=>react.createElement([标签名],[props|null],...)有几个子节点,从第三个实参开始分别是每一个
子节点的处理(文本节点直接就是文本内容,元素节点还需要再一次create-clement)
2,执行React.createElement
=》返回一个对象
{
$$typeof:Symbol(react.element),
key:
ref:
type:标签名/组件
props:{
//
xxx:xxx, 给元素标签上设置的属性(ref/key除外)
//没有子节点则没有children选项,有子节点才有children,只有一这个字节点它的值是单个值,如果有多个
子节点,它的值是一个数组
children:单个值(字符串/对象)或者 数组
}
}
3,基于reactDOM.render把生成的对象变为真实的DOM,最后渲染到浏览器页面指定容器中
*/
<div className="box" id="box" style={{color:'red'}}>你好世界
<span>aaa</span>
</div>
转成以下:
React.createElement("div", {
className: "box",
id: "box",
style: {
color: 'red'
}
}, "\u4F60\u597D\u4E16\u754C", React.createElement("span", null, "aaa"));
react中的组件
在react工程化项目中(vscode),我们会把需要写react组件的js命名为.jsx,这样只有一个目的,让创建的文件识别jsx语法,而在create-react-app脚手架创建的项目中,已经包含了对.jsx文件的处理
react中的组件:每一个组件都是一个单独的个体(数据私有、有自己完整的生命周期、有自己的视图...)
1.函数式组件
只要让函数返回一个jsx元素即可(掌握:props)
2.基于react.component类创建组件
3.react hook
函式组件-特点:简单(开发简单和渲染也快)但是一旦组件被调用,里面的内容渲染完成,当前组件中的信息基本上就不变了(除非:重新调用组件传递不同的属性信息)=》静态组件,没有state状态管控的随时变化,也没有生命周期函数
在reactDOM.render进行处理的时候,如果发现type不是标签字符串,而是一个函数(一个类),则会把函数执行(创建类的一个实例),与此同时会把调用组件时候,设置的属性传递给这个函数或者这个类
//只要在js中使用jsx,则必须引入react(因为需要用到create-element)
/*
简单:开发维护简单,渲染也简单(渲染速度快)
静态:只要把组件调取渲染完后,组件中的内容将不再修改(函数式组件中没有自己的状态管控,生命周期等)
组件特点:给组件传递进来的属性是只读的(只能获取不能修改)报错信息:Cannot assign to read only property 'title' of boject
1.不能直接的赋值默认值
=》props.title=props.title || '我是默认值' (错误的)
=》let title=props.title || '我是默认值' (可以的)
2.函数式组件不能像类组件一样,基于prop-types给属性设置默认的规则
*/
import React from 'react';
export default function Vote(){
return <div>
我是组件
</div>
}
/*
React.Children.map //固定的写法
*/
React.Children.map([需要遍历的数组对象],(item,index)=>{
})
类组件
类的创建
class Clock{
constructor(){
}
y=200; //=>新增的语法:给实例设置的私有属性和constructor中的this.y=200效果一样
AAA(){
//Clock.prototype.AAA
}
static CCC=300; //ES7新增语法
static BBB(){
//Clock.BBB 私有方法
}
}
/*
继承
1.原型继承
2.Call继承
3.寄生组合继承 Object.create
4.ES6中基于class实现继承
*/
class Clock extends Parent{
constructor(){
super(); //=>只要用到constructor,第一行就必须要用super,
}
}
创建类组件
对于第一个类组件来说,只需要学会:
1.数据管控 (model)
属性 props
状态 state(私有状态,redux公共状态管理)
/*
类组件:
当react-DOM.render渲染的时候,如果发现虚拟DOM中type是一个类组件,会创建这个类的一实例
并且把解析出来的props传递给这个类 new Clock(props)
=>执行 constructor (此时props并未挂载到实例上),基于this.props不能获取到值,但是可以直接使用形参中的props
解决方案:super(props)这样在constructor中也可以用this.props了
=>当constructor执行完,react会帮我们继续处理
=》把props /context...挂载到实例上(后期在其它的钩子函数中可以基于this.props获取传递的属性值)
=》react帮我们把render方法执行
*/
class Clock extends React.Component{
//第一种写法,写私有属性
constructor(props){ //props调取组件传递进来的属性
/*super执行,相当于把react.Component当做普通函数执行,让方法中的this是当前实例
this=>{props:xxx,context:xxx,refs:{},updater:{}}
*/
super(props); //把参数挂载到实例上
}
//第二种写法,写么有属性
/*x=100;
y=200; */
//必须要有render函数,它返回的内容是我们当前组件要渲染的视图
render(){ //到这里this.props是有的,因为到这步react帮我们把参数挂载到了实例上
return <div>
<h2>{}</h2>
</div>
}
}
给属性设置规则 prop-types
安装第三方库 github.com/facebook/prop-types
/*
基于第三方插件prop-typs设置属性的规则:默认值和其它规则
=》1,yarn add prop-types
设置默认值
=》PropTypes.isRequired 必须传
=》PropTypes.string/bool/number/func/object/symbol/node(元素对象)/element(jsx元素)/
instanceOf(xxx)必须是某个类的实例,/oneOf(['news','']),其中的某一个/oneOfTypes([PropTypes.string,PropTypes.number])多个类型中的一个
和VUE一样,我们设定的规则不会阻碍内容的渲染,不符合规则的在控制台报错
*/
class Clock extends React.Component{
static defaultProps={
title:'我是默认值'
}
static propTypes={ //给组件里设置默认规则
title:PropTypes.string.isRequired //设置规则 isRequired必须传递
}
constructor(props){
super(props);
}
render(){
return <div>
<h2>{this.props.title}</h2>
</div>
}
}
ReactDOM.render(<div>
<Clock></Clock>
</div>,document.getElementById('root'));