< script src = " https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.development.js" > script>
< script src = " https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.development.js" > script>
< script src = " https://unpkg.com/@babel/standalone/babel.min.js" > script>
react需要引入 3个库 vue是全在vue.js里
拆分为3个库主要是为了react-native 移动端RN做准备
react
和
react-native
使用同一个 react.min.js 核心
但是
不使用同一个 dom
移动端 支持安卓和ios原生的控件
web端 渲染成真实的dom
js远程如果有错体现出来 crossorigin
作用影响不大
react.min.js 核心
react-dom.min.js DOM 相关的功能 且支持react跨平台能力
babel.min.js 将 ES6 代码转为 ES5 代码,且支持jsx写法
(react的一种写法 会编译成 HTML片段)
< script type = " text/babel" >
script>
---------------------------------------------
通过 npm 运用 React
//第一种纯自动方式
安装 react 脚手架
npm install -g create-react-app//脚手架相当于cli
create-react-app reactapp
//通过脚手架创建文件夹含(初始项目)
cd reactapp//进入项目
// react 好像默认是 yarn 但是npm也好像支持
yarn start//运行项目 yarn install
yarn add axios//安装依赖
npm start//也可以运行项目 npm install
npm install axios//安装依赖
//目录结构
public->robots.txt 是否可被爬虫定义
public->manifest.json 移动端或桌面端
安装web app的
基本用不上可以直接删除
src->App.css //当前App的css文件
src->App.js //函数式组件 在.js里写.jsx的
src->index.js //.jsx转换器
src->logo.svg//图直接放外面?
src->setupTests.js//做测试时的初始化设置
src->serviceWorker.js
//手机端网页成单独app离线功能
//不需要 pwa此功能可删除
yarn eject//执行一种不可逆的暴露操作
//根目录中多出 config 文件夹
//根目录中多出 scritp 文件夹
这两个东西是webpack的相关配置
他的配置信息是非常的多
package.json 的相关配置也是新增了
许多 webpack 原先隐藏的包
//第二种拉取现有框架(自动)
//第三种纯手动方式
npm install babel -g 全局安装 语法转换工具
npm install webpack -g 全局安装 打包工具
npm install webpack-dev-server -g 全局安装 开发时运行工具
mkdir reactApp 创建文件目录
cd reactApp/ 进入目录
npm init 初始化node.js的基本包 生成 package.json 文件
//添加依赖包及插件
npm install react --save 安装react核心
npm install react-dom --save 安装react-dom相关的功能
//同时我们也要安装一些 babel 插件
npm install babel-core
npm install babel-loader
npm install babel-preset-react
npm install babel-preset-es2015
//新建一些必要文件:
touch index.html
touch App.jsx
touch main.js
touch webpack.config.js
// webpack.config.js 基础必要配置
var config = {
entry: './main.js',
output: {//载入器
path:'./',
filename: 'index.js',
},
devServer: {//服务器
inline: true,
port: 7777
},
module: {//编译器
loaders: [ {
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'react']
}
}]
}
}
module.exports = config;
// package.json 修改启动脚本名称
"scripts" 中的 "test" "echo \"Error: no test specified\" && exit 1"
替换为
"start": "webpack-dev-server --hot"
//index.html 必要配置
DOCTYPE html >
< html>
< head>
< meta charset = " UTF-8" >
< title> React App - W3Cschool教程(w3cschool.cn) title>
head>
< body>
< div id = " app" > div>
< script src = " index.js" > script>
body>
html>
//App.jsx必要配置 (组件)
import React from 'react';
class App extends React.Component {
render() {
return (
< div>
Hello World!!!< br />
欢迎来到W3Cschool教程学习!!!
div>
);
}
}
export default App;
//main.js必要配置
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(< App /> , document.getElementById('app'))
//npm start 即可启动运行
从今天开始
我们正式学习react的一个课程啊
今天是我们第一次上课
第一次尚课
上课 对吧
之前老有人这个吐槽我这个上字啊
今天是我们第一次上课
第一次上课的话
我个人会每接触一个新的东西
会比较喜欢给他起一个名字
叫邂逅
那第一次接触 react 的话
我也希望大家可以和 react 之间
来一场美丽的邂逅
来真正爱上 react 的技术
react 如果你用熟练了的话
你真的会非常喜欢他的一个开发模式的
不过不用着急
今天是我们第一次上课
我们和 react 的第一次接触
大家有一个美好的过程
那我们下面的话
就开始正式学习
下面相关的一些东西
那我叫做邂逅 react 开发
这个里面要讲什么东西呢
我们一点一点来看
但是
在我们正式学习一些新内容之前
我在这里要做一些上课提醒
课前的一个提醒
提醒一个什么东西呢
首先第一个
提醒我们这个课程需要具备什么基础
如果你之前不具备这样一个基础的话
你课下一定要把相关的内容做一个补充
那我这里要求的基础的话也不是特别的高
我需要大家具备的基础就是我们
前端的一个技术html,css,javascript
那这个 javascript 的话
包括 es5 也包括es6
以及以上的一些相关的语法
但是有一些同学之前用过es6的语法
但是es6的某些语法用的可能并不是特别熟练
这个的话
没有太大的关系
一方面
有一些es6语法
我在课堂上用到的话
我会再次提起
另一方面的话
如果
我有些es6语法在用到的时候
上课提醒了
你感觉自己掌握的还不是特别好
这个时候大家在课下的话
一定要花一定的时间
再去做一些学习
再去做一些练习
我们课堂上不是为了讲这些es相关的语法的
我会提到
但是有些语法不会讲的非常详细
所以需要大家具备
html,css,以及javascript
包括JavaScript里面es6一些相关的基础
有一些我会再次提及的
没有关系啊
那有一些比较常用的话
用的多了,慢慢就可以熟练掌握了
好
那这个是我们具备的基础
我相信很多同学都是有问过
需要什么样的基础
之前都已经回答过了
大家大概的话都是了解的
那我们下面的话
再来说另外一个东西
另外一个东西就是我们上课时间
大家记住这个上课时间
默认情况下
我们上课时间是周 一 三 五 晚20:00~22:00
当然
有时候准备的知识点比较多
晚上十点有可能讲不完
不一定讲完的话有可能会拖堂
可能会拖半个小时啊
拖一个小时啊
那这个的话我觉得大家都是没什么意见的
因为我们来这里学习
就是为了学更多的一个东西
那我们讲的时间越长
对大家来说肯定是更好一点
唉
肯定没有同学说
少讲一点,少讲一点
我们尽可能的话
会给大家多讲一些东西
所以有时候我会拖堂
那这个的话
一般情况下是这样一个时间
但是一般情况下可能会拖堂
这个大家也做一个了解
另外的话最好是来听直播
如果没有听直播
找时间回看一下录播
为什么要这样呢
因为你来听直播的话
我们所有的同学
我们整个班的所有的同学
学习进度是保持一致的
那保持一致的情况下
如果你有什么问题
你可以立即在群里面提出
看到的时候我也会回复
那如果其他同学看到的话
其他同学也会帮助你回复
因为大家都学到这个知识点了
有可能你遇到的这个bug他刚刚解决过
刚刚讲过
这个时候我就会在群里回你
或者其他同学就会在群里回你
那这样大家的节奏都是保持一致的
如果
比如说你落后了十节课
那大家学到后面了
你问前面的问题
那这个时候可能还需要回顾
需要去翻以前的笔记
翻以前的代码来给你回复这个问题
那这个成本可能会比较高
有可能
对吧有可能
当然我们也会
我看到的话肯定会回复你
你私聊我的话
我也会给你回复
但是我们最好是
所有人都保持一样的一个进度
这样相当于我带着大家我们一起来学习
这个东西
那一起来学习这个东西
你有任何的问题就可以快速的解答
并且
如果你听完今天的课
对吧
然后你有一个地方疑惑非常大
那我发现很多同学都有这样一个疑惑的话
我可能会在下一次课程里面换另外一种方式
把这个知识点再给大家讲一下
可能会出现这样
所以呢大家尽量来听直播
我们每个同学的话
进度保持一致
这样会比较好一点
那还有一个东西的话
就是当前的课程目录
目录的话只是暂时的
我们并不一定完全按照目录来将
因为在讲课的过程中
有一些知识点有可能放到前面来讲
会更合适一点
有一些知识点放到后面来讲
会更合适一点
那我会进行一些调整
进行一些调整的时候
大家也不用特别惊讶
说
唉
今天不是准备讲组件化吗
怎么今天讲另外一个东西啦
大家不要有这样的疑惑啊
我尽可能是按照每个同学比较好理解的方式
会调整一下我们相关的目录的
可能会这样
也有可能是
我在中间会穿插一些知识点
我觉得呢
虽然有些东西
没有在目录里面列出来
但是我觉得讲这个东西的话
更有利于大家去学习这个 react
那我也会在中间穿插其它一些知识点
那么目录也会做一些调整
当然我们最终的话还是会形成一个非常完整的目录的
并且的话
如果我上课的时候
把每个视频录成一个小视频
到时候我们也会对课程进行一个升级
目录结构做一个升级
就是每个小知识点可能都是一个视频
这样的话方便大家进行一个回顾
OK
这个是关于我们的上课时间和我们的课程目录
大家一定要也做一个了解
那还有一个提醒
就是什么提醒呢
就是大家要相互尊重
共同来进步
因为我们每一个同学
每一个人他们的基础可能是不一样的
之前学习的精力和工作的方向
可能是不一样的
有些同学可能是做 ios 的
有些同学可能是做 安卓 的
有些同学可能是做 前端 的
还有些同学可能是做 后端 的
所以每个同学之前的精力和基础
可能都不太一样
对吧
那我不希望在课堂上面的话
出现一些情况
就是说
老师
唉
这个东西不要讲啦
这个es6语法你就不要提了
不用提了
这个东西我们都会了
不要有这样一个现象
我所讲的所有内容都是为了
大部分同学考虑的
所以我希望每个同学的话
都可以相互尊重
共同进步
一起把课程里面所有的东西
全部给它掌握了
那这个的话
才是我们最终的一个目的
所以这个的话
是我们的一个课前提醒
下面的话
我们再来说一个东西
就是我们课程的一个内容
我们这个内容是
全面讲解 react 技术栈
相信很多同学之前已经看到
我列的 思维导图的大纲表
后面的话我也会把这个大纲发给大家
大家可以在课下的时候
再来看一下这个大纲
也没有任何的问题
整个的学习过程的话
基本上是围绕着这个大纲来进行学习的
但是中间的话我也会穿插其他的一些东西
并不仅仅局限于这个大纲
也会讲解一些其他的相关的知识的
那之后我们是做这样一个项目
网易云音乐 PC端这样一个项目
那这个项目的话它内容其实是非常非常多的
而且据我私下了解的话
这个网易云音乐这个前端团队
应该是有 30多 个人的
35还是36个人
也就是说
他们的人数是非常多的
三十五六个人来一起开发网易云相关一个东西的
那我们不可能靠课堂上几节课
把里面所有的功能都把它给做出来
但是大家也不用担心
我会挑出来几个非常关键的模块
把他给做一下
而且我只要做到的模块
我基本上可以做到一个 1:1 的还原
那即使有些模块我们没有讲到
大家课下的话可也以根据我们所讲的东西
一定是没有任何的问题的
而且
只要是我讲到的这些模块
一定会给大家讲的非常清楚
为什么要这样做
如何设计整个组件的一个划分
和他的一个边界
等等...
这些东西都会讲的非常详细清晰
一定是可以保证大家以后
做其它的模块的话
没有任何的问题的
所以我们之后的话
会做这个项目
并且的话
我们会挑出几个比较关键的模块
把这个项目做一下
OK
那这个话就是关于我们课程的一个内容
也是我们课前提醒的一些相关的东西
这个的话应该是没有什么问题
我把这部分保存一个视频吧
每个小知识点
就是安照现在这种上课模式
我都会保存一个小的视频
这样方便以后我们课程目录升级
也方便大家找到某一个知识点
进行相关的回顾啊
好现在我先不看大家聊天啊
我到中间的时候
或者讲完一个稍微难一点的知识点的时候
再看大家这个聊天框
那这个的话是我们的课前提醒
我这里的话再来说一个东西
刚才有一个同学问到的一个问题
就是老师什么时候把这个ppt发给我们
那是这样的
我每天上完课之后
都会把当天的代码和ppt
或者其他的一些资料
然后放到哪里呢
给他上传到腾讯课堂里
然后你就可以下载之类的
视频首页->课程概述->最下面可下载相关资料
下面我们就开始正式学习我们的一个内容了
学习今天内容的话
我们今天最主要的是
邂逅一下 react 开发
那邂逅 react 开发的话
我们还是先对 react 开发
来做一个介绍
当然我之前的话在公开课
里面也做过一些介绍
但是
我这里介绍的
有一些东西是一样的
还是有很多东西是不一样的
所以即使听过公开课的同学
也要好好来听一下这个东西
既然学习个新的东西我们就
先对这个东西做一个认识
react 到底是个什么样的东西
那react 是什么呢
我相信每个做过开发的同学
特别是前端开发大前端开发的人
对 react 有或多或少的印象
那 react 到底是什么
那这里的话还是用官方的
对他的一个解释
来对他做一个说明
他说 react 的话
是用来构建用户界面的
javascript的一个库
那构建用户界面 JavaScript 库的话
又是一个什么样的东西
我们知道对于前段来说
不管是ios还是安卓
还是web端
其实都属于前端
那我们这里的话
讲 react 最主要是针对于 web端的
这里的话我就这样来说
对于我们前端开发人员
最主要指web端开发人员
最主要的任务就是构建用户界面
其实构建用户界面我们都知道
做我们的前端开发
有三个非常重要的技术
第一个的话就是 HTML
另外一个的话就是 CSS
还有一个就是JavaScript
HTML 用来构建我们页面的一个结构
CSS 构建我们页面的样式
而 JavaScript 的话
我们页面的动态内容
和我们页面交互的一些相关东西
最主要就是这三个东西
那html和css他是我们最基础的
而我们 JavaScript 这个东西
其实我们在之前
在之前很多网站也会使用
原生的 JavaScript 来开发我们整个页面
但如果你用原生的 JavaScript 来开发页面的话
会存在一些问题
会存在什么问题呢
比如说操作 dom 的话
可能会存在一些兼容性问题
比如说你为了写出来更具兼容性的代码
可能我们的代码会存在冗余的问题
而且也包括我们的代码
如何组织代码规范的相关的问题
也就是说用原生的JavaScript
来开发我们的这个网站的时候
开发我们前端页面的时候
它存在很多的问题
所以呢在很长一段时间
之内的话
我们都在寻找比较好的
js的一个库
来帮助我们前端人员进行开发
那在过去的很长一段时间之内
这个jQuery是被使用最多的js库
其实之前有一份调查
全球 1万非访问最高的网站中
有65%使用了jQuery(也就是js)
这是在之前的调查里面
这个数据的话
来自于维基百科
那也就是说在之前的时候
这个 jQuery 是当时最受欢迎的一个js库
但是其实现在来说的话
越来越多的公司开始不再使用这个jQuery 了
包括程序员使用最多的网站
也就是github
他也放弃了这个jQuery
那如果现在大家去公司做开发的话
其实用jQuery 的也是非常少的
除了一些老的项目的话
基本上很少有新的项目再使用jQuery
现在前端领域最流行的框架是哪些呢
就是我们的三大框架
三大框架就是Vue ,React 和Angular
也就是我们的这三个框架
但是呢这个Angular 在国内
并不是特别受欢迎
尤其是Angular 目前的版本的话
他对typescript还有一定的要求
也就是说他门槛会更高一点
所以这个Angular 的话
目前在国内并不是特别流行
而比较流行的就是vue和react
是国内最流行的两个框架
之前的话在公开课里面做了些对比
但是我这里的话就不再对比他们的东西了
之后的话打算专门写一篇文章
是对VUE和react来做一些对比的
一个文章
大家可以关注我的公众号
我后面的话肯定会写到这个公众号里面
我们这里的话暂时就不做一个很多的对比了
但是之后技术点我可能会拿出来分享
比如vue是怎么做的
但是这里我们就不做一个很多的分析
react 是 js库
但是 react 本身是框架
国内使用率非常高
那我们了解了这一个东西后
我们下面再来说一个东西
那就是关于react 的一个起源
react 的起源的话
这个东西大家做一个了解
我个人学习技术
比较喜欢了解他的一个起源
不管是javascript 还是pyth
我都喜欢了解一下他的一个起源
什么情况下
产生了这样一个库
为什么当时要设计这样一个库
我比较喜欢了解
这些东西啊
大家也可以了解一下
知道他在什么背景下面产生的
那我们简单了解一下react
他的起源啊
我们知道 react是 2013年 Facebook
开源的一款框架
vue 大概是 2014年
那么当年Facebook 为什么要推出这样的一款框架呢
其实是有原因的
什么原因呢
原因来自于一个需求
以及这个需求所产生的bug
在facebook 当时有一个需求
就是要展示
类似于 消息 +99
现在看这个东西很多同学
可能是感觉非常的简单的
当时facebook 就把传统的方式把他做出来了
做出来它发现
光这么个小东西
它经常产生bug
产生什么bug呢
比如我现在来了条消息
来了条消息我就需要知道
你之前有多少条新消息
是没有读的
就必须 产生个 +99的数字
就必须维护当前这里这个数字的一个状态
维护完这个状态之后的话
它需要通过dom操作来改变 +99这个数字
就是某一个工程师在修改
某一个状态的时候容易产生bug
比如说 有可能修改错了 位置
有可能多个 +99 在一起的时候
类名是相似的
而且极有可能是用了一个for循环
总之在 当时facebook很容易产生这样的 bug
...
类名 加上循环的 下标不就好了
唉!
总之.该功能上线之后
过多的修改 3个+99 容易产生bug
当然可以修复了
那那那
很快确实有修复这个bug
facebook 工程师并不满足于此
也就是说
他不满足于仅仅把这个bug给修复
他开始思考为什么会产生这样一个问题
为什么这一个简单的需求
总是会产生这样一个类似的问题呢
最主要的原因是在传统的开发模式当中
我们过多的去操作页面的细节了啦
其实这个问题的话不管是在早期的前端
还是在ios端
还是在安卓端
都会出现这样一个问题
比如说现在你要操作某一个 +99
那么你就要掌握和使用dom相关的API
当然你也可以通过jQuery 来 操作dom
总之
不仅你要维护这个状态
还要进行dom 操作
去操作dom 的一些细节
另外的话我们这里的状态
我们这里的话其实是有
三个状态的
就是这三个状态
有可能是分布在
不同的地方的
你管理起来的话也不是特别的方便
不方便管理和进行维护
其实之前你学习过vue
学习过react 的话
看到 +99 就应该想到
可以把这个东西封装成一个组件
但是在早期的不管是原生态
开发还是jQuery 开发
没有组件这样一个概念的
没有组件这样一个概念的话
你就很难将组件当成一个整体
单独拿出来给他维护相关的一个状态
并且当状态发生改变的时候
单独来更新这么一个东西
他就不是特别的方便
当然我聊到这个东西的话
可能有的同学压根就不知道什么是组件
没有关系
总之就是在传统开发里存在这么些问题
第一个
操作界面过多的细节
第二个
就是他的状态不方便维护
就是facebook在发现这个问题的时候
他就不仅仅满足于修复这么一个bug
他开始思考为什么产生这样一个问题
产生这样的一个问题的话
他们就会去想
有没有一种新的模式可以解决上面的问题
那么他们当时就可以提出来
就是以组件的方式来划分一个个模块
划分一个个的组件
但是如果按照我划分的话
所有 +99 可能是同一个组件
然后之后的话给我创建组件对象
总之以组件的方式去划分一个一个功能模块
来描述UI 界面应该涨什么样子
然后用 state
比如说 我现在把 +99 划分成组件了
划分成这个组件之后
这个界面要长什么样子呢
我是用jsx 来进行描述的
使用jsx
这个是什么东西
这个我后面会在搞一个东西叫state
这个jsx 的话是依赖这个state 的
如果有一天我想改变这里的+99 数字的话
我就直接改变这里的state
改变这里的state 的话
他这里是通过统一的setState 方式进行改变的
我们后面会讲到
现在只是提一下它当时是怎么设计这个东西的
因为你jsx 依赖这个state
这样的话一旦state 受到改变它就会重新渲染dom
这样的话就不会产生这个问题了
所以这个的话
就是react 的起源
源于这样一个小的bug
之后经过了很多的一个思考
最后发明了一种新的模式来解决这种问题
而解决这种问题
这个的话就是react 最初的原型
就是为了来解决这样一个问题的
好这个的话就关于react 的起源
那我们这个东西做一个了解就可以了啊
了解完react 的起源之后
我们再来看一下react 的特点
react 的话官方最主要说了他有三个特点
哪三个特点呢
第一个特点就是
声明式编程
其实声明式编程时整个大前端
很多地方的一个开发模式
不管是vue react flutter swiftUI
其实都是声明式编程
我接下来会给大家提到
声明式编程长什么样子
命令式编程长什么样子
后面的话我们再来具体说这个东西
声明式编程的话他就允许我们
只维护自己的状态
当react 发生改变的时候
让react 重新去渲染界面就可以了
也就是说我们最主要是维护这个状态+99的
UI=f(state)
开发的UI 界面是依赖于我们的状态的
他可以通过一个f 函数方法
这个函数在react 里面指的就是 render函数
后面的话我会讲到
就是render函数了
而在Flutter 里面就是build 函数了
那也就是说我们现在有一个状态
我们根据这一个状态
通过render 函数
最终构建出来我们的界面
你会发现
就等于我们的UI了
这个state 状态发生了改变
他只需要通过某种方式改变这个状态
之后重新执行f 这个render函数
他会根据最新的状态重新构建这个UI
那么UI自动就会发生改变了
在整个的这个过程中
我们是不需要操作这个dom 的
在不管原生的js还是jq里面
现在数据发生改变了
你必然要进行dom 操作
当然我们这里不需要
我们只需要告诉他
状态上的改变
他就可以根据
我们当前 f 的render函数方法
以最新的状态重新构建出来一个UI
这个过程就是声明式编程
后面我们会通过代码演练这个东西的
大家做一个了解就可以了
这个是react 的一大特点
那还有另外一个特点
这个特点的话
其实我刚才已经说过了
就是组件化开发
我们可以把它整个界面的话
划分成一个一个的组件
而组件化开发
也是前端目前流行的一个趋势
我们可以把一个个复杂的界面
划分成一个个组件
但如何合理的进行组件化的划分
和设计的话
是我们讲课的一个重点
我会经常对某个组件进行抽取
并且把我们这个组件设计的尽可能的合理
上课的时候可以认真来听这一部分
这一部分的话
是我们的一个重点
我从vue 截了个图
它其实都是组件化的思想
这个截图非常好
指vue 官方文档组件化的示意图
比如说现在我们要开发这么一个界面
那这个界面怎么做呢
首先整体上整个这个大的页面
它是一个组件
然后我们要开发某一块功能的时候
比如说我们要开发这一块功能
那么这个的话到时候就是个组件
同级其它剩余地方
也是划分成一个组件
相当于我们大的 app的话
现在有 共3个组件
组件里面又包含小组件
最终我们的react 程序
包括vue 也是一样的
包括组件化开发思想
都是这样的
其实包括flutter也是一样的
就是把我们的应用程序
划分成一个个小的组件
再把我们的组件进行一个组装
最后形成了我们的应用程序
而我们在设计组件的时候
要考虑很多的问题
不管是他的复用性问题
还是如何划分会更加合理
的一个问题
这个东西我都会在之后做一个讲解的
现在大家做一个了解就可以了
这个 组件化 也是react 的一个特点
至于react 的第三个特点
就是多平台适配
react 开发之初最主要是开发我们web界面的
也就是说最主要学习的
也是react 当前应用场景最广的
就是开发web
但是 2015年 facebook还推出了一个东西
叫reactNative 简称RN
他可以用来开发移动端跨平台(类似于vue的uni-app?)
目前的话Flutter也非常火爆
但是还是有很多公司在用 RN
Flutter 非常火爆是因为
其性能是远远大于 reactNative 的
这个跟他底层的渲染原理有关系
大家有兴趣的话可以课下做一些了解
我这里就不再展开说这个问题了
总之
我们的react 是可以开发移动端的
还有一个2017年的时候
facebook 推出reactVR
vr的话大家知道就是虚拟现实
他可以用来开发虚拟现实的web应用程序
那我个人也是觉得5g 即将普及
我个人还是非常看好5g 普及的
而且我们国家的话也在大力发展这个5g
我感觉5g真正推广起来的话
会带动一系列的产业啊
到时候我们有可能就需要通过react
来开发vue相关的一个程序
这个我觉得绝对不是一个虚幻的幻像
肯定会
我觉得肯定会实现的
react 一定是一个非常火爆的5g应用场景
react 其实可以开发很多东西
不管是 VR Web 还是 RN(移动端)
都是可以的
这个的话
都是关于react 的一个特点
react 刚才我们介绍的最主要是抽象的一个层面
来说了他的一些特点
这里我来总结了它一些东西
我之前在公开课讲到的一些东西
可能之前有些同学没有来听公开课
在这里我还是提一下
如果你一直学习react
真的学习react
真的是掌握最先进的思想和技术
为什么这样说呢首先
react 是有facebook 更新和维护的
他是大量优秀程序员的思想结晶
而且react 的流行不局限于普通的开发工程师
也就是说我们在公司做这个应用层面
开发工程师对他的认可
其实有大量的流行框架
他也借鉴了react 的思想
比如 vue设计之初,
有很多灵感来自于angular和react
之前很多人问过我
什么时候开vue3 的课程
vue3 出来之后
有很多人担心
我好不容易学会vue2.6
那这个vue3 我是不是又要重新进行学习啦
也有很多同学感觉非常兴奋
因为vue3 里面增加了非常多的好用的新特性
现在我会开设vue3 的课程大家不用着急
现在发布的只是vue3 的beta 版本
beta 版本他有很多的api 还是不稳定的
你这个时候去学习它
可不可以学呢
可以
就是你写一些demo 还行
真正要应用到线上
就是你公司真正用vue3 来开发
我估计还是有一段时间的
首先vue3 真正发布正式版
我估计还得3-5个月
那发布完正式版之后
她可能还需要半年的时间来
修复它正式版里相关的一个bug
才有很多的公司敢在线上的环境来使用这个vue3
所以大家去学习这个vue3
现在去学习一下
没有关系
但是真正应用到公司的线上环境
还是需要比较长的时间的
所以大家不用着急
vue3 里面的话其实增加了很多特性
比如vue3 有被大家最期待的一个特性
vue function based API 有这么一个特性
这个特性的话借鉴了
react hooks的思想
这是我们的一个重点
后面我们会非常详细的讲我们的hooks
而且我们做项目的话也是使用react hooks
来进行开发的
vue里面这个vue function based API
就是借鉴了react hooks 的思想
如果你之前写过 vue3 demo
然后你又写过 react hooks
你会发现他们的写法非常相似
所以说学会react 再去学习vue3的时候
你会感觉很多东西
用起来跟react 是非常相似的
而且用起来上手也是非常快的
所以vue 的话有很多思想借用了这个react
当然 vue 里面也有它自己非常好用的一些特性啊
其实我个人也是非常喜欢这个vue
vue里面的一些东西用起来也是特别方便的
但是各有优势
我们不要陷入到论坛或者群里面
大家喜欢争论
到底vue 更好还是react 更好呢
这个的话
没有必要争论啊
在国内
这两个框架
都是我们前端开发人员
必须掌握的
必须掌握的两个框架啊
不是说你掌握的一个框架
就可以一直高薪
一直在某个公司一直这样混下去
我想是不太可能的
作为一个前端开发人员
你想走的更深
走的更远
这两个框架都是必须学习的
所以不要去争论哪一个框架更好
没有任何的必要
那另外一个就是这个flutter
flutter 他有很多灵感是来自于react 的
这里的话从flutter 官方截了一幅图
这里面他有说到
flutter 里的 widgets 被构建
用了一个现代的框架
他有很多的灵感是来自react 的
也就是说flutter 有特别多的灵感是来自于react 的
flutter 有几个概念
叫 widget-element-renderOnject
对应 jsx 虚拟dom 真实dom
但是这个东西大家做个了解
就可以了
知道确确实实有很多很多的框架
都借鉴了react的思想
所以说我认为这个react
可以认为是前端的先驱者
他的技术总是可以引领前端的潮流
确确实实是这样
react确实有很多先进的思想
所以我们学习react
是真正可以掌握最先进的思想和技术的
这个是没有任何的问题的
这个是关于react 的一个介绍
我们了解就可以了
我们今天做一个邂逅嘛
邂逅的话就是可能有很多
理论上的东西
可能有些同学之前就了解过了
但是有的同学没有了解过
所以这些东西我们多多少少还是要
做一些介绍
这是2020年的
HackerRank的
(国外程序员非常喜欢上的一个网站)
这个网站会经常做一些调研
一份关于你更想学习哪个框架的调查
你会发现
react 排在第一名
紧接着是 angularjs
紧接着是 Django
vue
ruby on rails
spark
spring
expressjs
.netcore
backbone.js
asp
enbar
cocoa
struts
meteor
pyramid
jsf
padrino
总结:2020年国外最受欢迎的框架是 react
vue排第四
angularjs排第二感觉不可信啊
但是国内
vue的话可能会更受欢迎点
我刚才以及说过了
在国内这两个框架
其实都是需要掌握的
总之 react在国内外都是非常受欢迎的
也有很多知名网站是使用react进行开发的
1.medium 国外程序员技术文章
2.twitter 推特
3.facebook 脸书
4.网易云音乐
5.斗鱼/虎牙
6.知乎
7.阿里云
8.优酷网
他们都是使用react 进行开发的
使用 vue的
1.b站
编程范式:
1.命令式编程:操纵dom?
每做一步都是给我们计算机(浏览器)
一步一步的命令
2.声明式编程:操纵?
react 在标签里写 onClick={函数名}
vue click='函数名'
使用 react dom 渲染jsx jsx中{展示变量} onClick={函数名}
改变 展示变量后需要重新 上一行中的所有步骤
进阶 将.jsx 封装成 一个组件来使用
解决 逻辑混乱的问题
class App extends React.Component{
//继承 react父类对象 创建 .jsx 组件
//生函数 自动执行
constructor(){
super()//初始化父类 以使用this
//this.message='hello'//不妥
this.state={//数据绑定专用
message:'hello'
}
}
render(){
return (
//内定要渲染的东西
< div>
< h2> {this.message} h2>
< button onClick = {this.btnClick.bind(this)} > button>
//.bind(this) 手动绑定this 指向当前App类
//否则 this指向的是react 自动调用内部onClick
//的某个东西
div>
)
}
btnClick(){
console.log('按钮发生了点击')
//this.message='hello2'//这里的this 是unfind
//this指向的 是 .jsx 已被react修改为指向一个可修改的地方
//this.reader()//重新渲染 失败
this.setState({//重新渲染 成功
message:'hello2'
})
}
}
ReactDOM.render(< App /> , document.getElementById('app'));
//替换 id 为app 的元素中的内容
js中 类对象 基础知识的补充
...
在es5 中定义一个类
是 new 一个函数
例如:
function 函数名(形参1,形参2){
this.变量1=形参1
this.变量2=形参2
}
//es5 添加 函数方法
// 下载类对象里
//通过原型写在类对象函数里
函数名.prototype.running=function(){
console.log(this.name,this.age,"running")
//this 指向一个类对象 '变量'
}
var 变量=new 函数名(实参1,实参2)
console.log(变量.变量1,变量.变量2)
// 实参1 实参2
变量.running(); //实参1,实参2,"running"
但是我们一般在react 里面
一般不使用es5 语法 定义类
因为 函数定义类看起来很怪
因此我们使用es6 的语法
例如
class 函数名 {
constructor(形参1,形参2){//生函数
this.变量1=形参1
this.变量2=形参2
}
running(){
console.log(this.name,this.age,"running")
//this 指向一个类对象 '变量'
}
}
//es5 添加 函数方法
var 变量=new 函数名(实参1,实参2)
console.log(变量.变量1,变量.变量2)
// 实参1 实参2
变量.running(); //实参1,实参2,"running"
//操作this 指向的方法
//显式绑定
fun.apply(obj)//主动调用fun函数
fun.call(obj)//主动调用fun函数
fun.bind(obj)//主动调用fun函数 优先级大于call
//同时存在时 bind生效
fun 函数方法里面
this 的指向就是obj 对象了
.apply(this)
.call(this)
.bind(this)//在onClick={函数.bind(this)} 只有这个有效
//主动给函数传入外部this 并使函数内this指向它
// 一个对象 并使函数内this指向它
关于js 中es6 类的继承
首先
为什么要使用继承
继承有很多的优势
是
面向对象3大特性的其中一大特性
面向对象有3大特性
什么特性
1.封装
2.继承
优势1.减少重复的代码(被抽到父类里)
优势2.
3.多态(子类重写父类的方法)的前提
重载 不知是否支持
super(a,b)//代表 父类对象 的生函数
//即使无参数可传也必须立即调用
//以初始化父类中的内容
//以供调用
//子类中必须初始化父类对象的
//这与php中不同
//应该是 js没有静态检测吧
接下来打算将两个案例
来对前面讲的东西进行回顾
案例一
渲染一个父级菜单(不可收缩)
里面有4个子选项
快速生成代码片段网站生成器
https://snippet-generator.app/
名称 | 触发词
---------------------
实际代码黏贴在这
文件->首选项->用户片段->选择 js/HTML
文件内容为
{
'名称':{
"prefix":"触发词",
"body":[
json数据格式的代码片段
]
}
}
直接将网站生成的代码
覆盖即可
认识 jsx 语法
通过在script中
增加type='text/babel'
以开启 jsx 语法
可以
将一个
完整的html标签
当做一种值
存入一个变量里
可被遍历
相当于php的
";
?>
的一种类似简写形式
看起来与 HTML元素无差别
其本质并不是 html 元素
这是react 的一种思想
甚至 css 也可以写进js 里
为什么react 选择了jsx
因为它认为 js 与 html/css
并不能以一种灵活的方式来相互配合
js经常需要修改 html 或css
单标签必须以 /结尾
{这里可以写this.变量}//相当于php的< ?echo $变量 >
{/*这是一段.jsx注释*/}
{这里可以写js表达式(高阶函数)}
{this.函数名()}//这里不需要bind.(this)
那 在this.state={
中
}
字符串 √
number √
数组 √
是可以正常显示的
但是以下不行(会被忽略)
null ×
undefined ×
true/false×
对象 {} ×
这一点比vue好很多
在vue的插值表达式中
光是处理 null undefined 就写了不少的代码
true && '哈哈'
只会展示哈哈 true 会被忽略
.jsx 如何绑定属性
class
css
src
等
src="./xxx.jpg"
src={this.state.imgUrl}
.jpg?param=140*140
服务器上的较小图片地址
className="box title"
className={"box title " + (this.state.classname)}
与 class 类对象区分
此举再次验证了 .jsx 不是 html
只是长得像而已
呃... 非常灵活的.jsx?
style={{fontSize:"10px",color:"red"}}
style={{fontSize:"10px",color:red}}
/"red" 不写引号为变量 red
.jsx 的事件绑定
1.
onClick={this.函数名.bind(this)}
与 onclick html点击事件区分
如果不传this
那么函数内this指向的就是 react绑定的undefined
函数名(){
console.log(this)
//手动找到传进来的外层APP类对象 的this
}
2.
onClick={this.函数名}
//this 指向react 的 onClick
//不是 this指向的是 App 类对象
函数名=()=>{
//箭头函数没有 this
//所以this 还是指向 App 类对象
console.log(this)
//直接就找到了外层APP类对象 的this
}
3.推荐
onClick={()=>{ this.函数名() }}
// this依旧指向的是 App 类对象
函数名(){
// 普通函数有this
//但是
//这个this 没有被react 绑定undefind
//当函数体内找不到 this 变量时
//就会往上一层找
//就找到了 App 类对像的this
console.log(this)
//直接就找到了外层APP类对象 的this
//原理类似2.
//改变了 初始调用环境 为 外层APP类对象 的this
}
1. 在 onClick={this.函数名()} 中
//react 可以修改了普通函数内部的
//this为undefind
//也因此
//普通函数内部就是undefind
但是在3.中
//匿名箭头函数做了个小小的手脚
//顶替了原先要被修改this的普通函数
//而 箭头函数 无this 无法被修改
//所以 普通函数内部的this 依旧是变量this
//当普通函数题内 this 找不到时
//就会找到App类对象的this
//主要原因 :
//用了箭头函数 这块不会死的特性
//挡了 react的枪
//至于react 为何要开枪
//(绑定普通函数的this为 undefind)
//这就不得而知了
函数名(){
console.log(this)
}
.jsx 的事件传参
关于onClick 事件
当你点击按钮是会触发事件
事件由浏览器封装成一个事件对象
我们有时会用到这个事件对象
我们有时想拿到这个事件对象
这就意味着我必须在回调函数里面拿到 event对象
这个 event对象是有
react 调用onClick 默认传入的
不需要我们来传
event对象 是一个 class类对象
这并不是原生的浏览器事件对象
这是react 内部合成的事件对象
我们需要的东西
里面都是有的
react 的条件渲染
类似vue的 v-if,v-show
react 没有这些指令
估计同php 一样
完全使用 语法(js)来控制
.jsx中的某个标签是否渲染
非常灵活
react 的列表渲染
没有 v-for
依旧是
完全使用 语法(js)来控制(.map高阶函数)
不能用foreach (没有返回值。 无法返回.jsx标签)
(应该是foreach(非普通函数) 封装成了不能接受返回值 )
非常灵活
filter((item,index,arr)=>{
//必须返回布尔值 为true的 item
},this)
//绑定this
arr.filter(()=>{}).map(()=>{}).slice(0,4)//前3个
//数组的双重处理
写代码
主要考虑可读性
不要为了使用高阶函数
而写难以理解的代码
.jsx
仅仅是一种简单的写法(语法糖)
所有 .jsx
最终都会 通过 balel.js 和type="balel"
把
< h2> hello React h2>
转换成
React.createElement("h2",null,"hello React")
如果直接写
React.createElement("h2", null, "hello React")
// type config children
// 类型 对象形式储存 数组形式储存
1.
type:
当前ReactElement的类型
如果是标签元素,那么就使用字符串表示"div"
如果是组件元素 那么就直接使用组件的名称
2.
config:
所有jsx中的属性都在config中以对象的
键值对的形式储存
3.
children(代表很多形参 )
//从arguments-2 获取后面所有参数
存放在标签中的内容,
以children数组的方式进行储存
如果有多个元素,react内部有代码
对它们进行特殊处理
就不需要
在 script标签中加 type="balel"
也不需要引入React 的 balel.js
https://babeljs.io/repl/#?presets=react
//用 babel语法
//将 jsx 还原至 其本来的面目(照妖镜)
React.createElement("h2",null,"hello React")
//创建的是一个对象
//这个对象 是虚拟dom
//是合成的虚拟dom
每个 对象(虚拟dom)
可以聚集成为虚拟dom
虚拟dom
为一种 二叉树结构
所有的树结构都能抽像为二叉树
在内存中对 js对象(虚拟dom操作效率是非常高的)
React.createElement("h2",null,"hello React")
//ReactElement 对象
//这个对象就是我们想要的树结构
虚拟dom 怎么转成 真实dom呢
非常简单
这时候有一个函数就起了关键性的作用
reactDom.render(虚拟dom,要渲染的位置)
//会将虚拟dom 在该位置上 渲染成真实dom
为什么要采用虚拟dom
而不是在真实dom 上修改删除
1.虚拟dom 是在内存 快
2.观察追踪真实dom的变化 比 虚拟dom 更困难
3.修改真实dom 每次修改 浏览器会触发多次
整个回流和重绘
修改虚拟dom 每多次修改 浏览器会触发一次
整个回流和重绘
react 的生命周期
componentDidMount组件渲染完毕后触发
componentDidUpdate(propos,state,shot)
组件更新完毕后触发
propos 前一次 propos
state 前一次 state
shot react的 生命周期函数之一
getSnapshotBeforeUpdate
手动 return的一个 {}
极少使用
componentWillUnmount 组件被销毁之前触发
hooks 可以为普通函数组件模仿声明周期
认识组件的嵌套
函数名=< 函数名/>
class名=< class名/>
函数3
return (
div
< 组件1/>
< 组件2/>
div
)
函数4
return 组件3
组件之间数据的传递
父传子以及属性验证类似于vue的
子组件 >上传入 父组件的函数
子组件内部通过props 拿到
父类
return(
div
< 子组件 name = " why" age = " 18" height = " 1.88" />
< 子组件 name = " why2" age = " 182" height = {变量}/>
div
)
子组件上的所有 自定义属性通通会
以对象键值对的形式
放入 子组件生函数的形参中
props
情况一:
class 子类 extends Component
constructor(props){
super(props);
//this.props=props
//父类已有这个方法
}
render(){
const {why,age,height}=this.props
//获取从父类继承的props
return(
div
{why+age+height}
div
)
}
情况二:
函数 子函数(props){
const {why,age,height}=this.props
//获取从父类继承的props
return(
div
{why+age+height}
div
)
}
}
-------------------
import PropTypes from 'prop-types'
//父传子数据类型 检测
传参的类型限制
(子函数/子类名).propTypes={
name:PropTypes.string.isRequired,
age:PropTypes.number,
height:PropTypes.number,
height:PropTypes.array
}
isRequired //表示必传
(子函数/子类名).defaultProps={
name:'why',
age:30,
height:22,
height:[],
}
defaultProps//表示未得到父组件传入值时的默认值
以下子类写法等效于 上面 子类写法
class 子类名 (){
static propTypes={
name:PropTypes.string.isRequired,
age:PropTypes.number,
height:PropTypes.number,
height:PropTypes.array
}
static defaultProps={
name:'why',
age:30,
height:22,
height:[],
}
}
刚才验证了一个 不传 props
子组件也能获取 到this.props
因为react 从父组件的组件标签上做了一些骚操作
使得子组件 能够自动获得 父组件的props
接下来回到讲组件通讯
上节课
前面讲到了父子组件传递
设置默认值和传递数据的类型验证
某些情况需要子组件传递父组件
在vue中
是通过子组件emit 发送自定义事件
在子组件标签里的
然后 父组件函数放在 子组件标签里
那在react 中是怎么做的呢
class 子类名 extends Component{
render(){
const {父组件函数}=this.props
return < button onClick = {父组件函数}> button>
}
}
class 父类名 extends Component{
render(){
return
(
<子类名 fun={e=>{this.父组件函数()} } name="why"/>
)
}
}
这是一种将父组件的函数
通过props
传入子组件
绑定到子组件元素中的 onClick 上
由于子组件在 父组件中
因此 子组件 能调用到父组件的函数
this 需要手动绑定传入父组件的this
否则this 既不指向父组件
也不指向子组件 全部为undefind
同样的下面这种绑定this的方法也可以
fun={this.父组件函数.bind(this)}
引入css
import "./tabcontrol.css";
React 实现slot
React 没有 slot
因为.jsx可以当做变量
直接.jsx传入就可以了
父子组件通信 方式1
-------------------------------
class 父类
< 子类>
< span> 我是子类中的元素1 span>
< span> 我是子类中的元素2 span>
< span> 我是子类中的元素3 span>
子类>
-------------------------------------
class 子类
< div> {this.props.children[0]} div>
< div> {this.props.children[1]} div>
< div> {this.props.children[2]} div>
--------------------------------------
父类的 span 会逐一插入 进子类的div中
因为在父类中
子类 标签所包含的 .jsx 区域 中
每个 .jsx 元素都逐一
被当react做 子类的 props
存在children 数组 中了
但是这种索引值
顺序不能乱
我们要采用另外一种方案了
父子组件通信 方式2
-------------------------------
class 父类
< 子类
name1 = {> 我是子类中的元素1 span> }
name2={< span> 我是子类中的元素2 span> }
name3={< span> 我是子类中的元素3 span> }
/>
-------------------------------------
class 子类
< div> {this.props.name1} div>
< div> {this.props.name2} div>
< div> {this.props.name3} div>
--------------------------------------
精准的拿到每一个属性
href="/#"
在react 中可阻止 语法检测发出警告
//父传孙的其它 写法
< App>
< ProfileHeader {...{name1: '123',name2= ' 456' }} >
// < div>
// {name1+name2}
// div>
ProfileHeader>
App>
{...{name1:'123',name2='456'}}
{...this.props}
//.jsx 语法 非 对象展开符
// 在.jsx 中重新定义为
// 对象展开为 .jsx标签的属性
祖孙 组件通信(Context)
Context 一共有 4个 API
1.React.createContext
创建一个 可供 子孙调用的 Context 对象
2.Context.Provider
向子孙推送 Context 对象
3.Class.contextType
class类中 接受长辈推送的 Context 对象
4.Context.Consumer
普通函数中 接受长辈推送的 Context 对象
祖孙 组件通信 示例
--------------------------------------
let obj=React.createContext({//开启祖孙传递
name1:'123',// 给一个 默认属性和值
name2:'456'// 给一个 默认属性和值
})
class App类
< div>
< obj.Provider value = " 789" >
< 父类 /> //向父类推送 '789'
obj.Provider>
// < 父类 /> //向父类推送 默认数据(在外面的话)
div>
-------------------------------
class 父类
< div>
< 子类 />
div>
-------------------------------------
class 子类
< div>
< 孙类 />
div>
------------------------
class 孙类
< div>
{this.context}//是 '789'
div>
孙类.contextType=obj
//不支持 父/子/app 同时传入 会覆盖的
如果是函数 孙类
function 孙类
< div>
< obj.Consumer>
{
value=>{
return < div> {value} div> //是 '789
}
}
obj.Consumer>
div>
孙类.contextType=obj
//不支持 父/子/app 同时传入 会覆盖的
------------------------
如果有 第二个需要传递的数据
需要 再在 第一个value=>{
中写< obj.Consumer> 嵌套
}
今后 使用的都是redux
EventBus 类似 vue的事件总线
接下来我们深入理解下 setState
我说过我会在后面
讲这个东西
而且会讲的非常清楚的
大家暂时不要着急
有些东西
我说了先用着
原理先放一放
后期都会给大家解开这些迷惑的
现在我们正式来讲一下
所有有关 setState的东西
这个东西的话
非常的重要
而且
他的这个
如果去
我看到
很多同学之前在群里面也讨论过
有些同学说唉
setState 的话
这个东西是异步的
有些同学说
这个setState 的话
有些情况是同步的
有些同学说
这个setState 的话
后面跟上一个对象
有些同学说
后面的话
可以跟上一个函数
那
感觉这个东西的话
使用起来
非常的灵活
非常的灵活
另外有时候
也感觉非常奇怪
有时候是同步
有时候又是异步
感觉这个东西的话
是吧
既灵活
又感觉它迷惑性非常强
我们今天的话
就会把这个东西
给他讲清楚
大家不用着急
还有一个
我之前说过
这个setState 里面的话
setState 里面
我们要保证数据的不可变性
为什么要保证他的不可变性
我也会
在今天
给大家讲清楚这些东西
这个东西他的使用的话
其实不会特别难
但是涉及到
很多很多相关原理性的东西
我们今天的话
都会一一给他解开
所以我们今天啊
先将这个 setState
关于 events这个东西的话
等到我们
今天讲完还有时间
我就把这个东西讲一下
如果没有时间的话
我会放到某一次课程里面
再给他 events 时间总线这样一个东西
再来给他
做一个讲解就可以了
这个的话没有太大的关系啊
好
拿今天我们来学习一下
我们的 setState
那学习 setState 的话
我们来先看第一个东西
就是为什么要使用这个 setState
为什么要使用这样的一个
setState 呢
好
setState 他是一个异步更新的
那什么叫做异步更新呢
现在
this.state={
aa:0
}
this.setState({
aa:this.state.aa+1
})
//aa 重新渲染为 1
//但是
console.log(this.state.aa)
//依旧为 0
因为
this.setState({})
的更新是异步的更新
如果是同步的更新
log 必定等你这个函数完全
执行完之后的话
我再执行下一行代码 log(this.state.aa)
所以有可能 更改在打印之后
异步属于宏任务
在 同步 与微任务(.then)之后
并在 执行时不断判断是否有 微任务
redux
用于管理第三方状态非常非常有用
redux的作者(亦是react 的核心成员)
对
this.setState({})
的更新为什么是异步的更新
的提问
做出了回复
简单总结就是两点
1.异步(宏任务)总是在(同步和微任务.then之后)
最后统一更新数据
并重新渲染相关组件的所有 .jsx
形成浏览器的页面
2.render 会等待所有 setState修改结束后
调用
以统一批量重新渲染页面
当 setState 为同步时
其本身组件class 类没什么问题
但是 子类的props
无法统一批量重新渲染页面
而是调用一次
渲染一次
因此 子类的 render中的.jsx
(不会被渲染?)
没太搞懂
因为 子类的 setState(没有被调用?)
this.setState({
aa:this.state.aa+1
},()=>{console.log(this.state.aa)})
在 setState 自带回调函数中
打印就是 更新之后的数据了
componentDidUpdate(){
console.log(this.state.aa)
}
生命周期也能
我在有一些地方看到
setState 是同步更新的
有一些东西确确实实是同步更新的
1.在定时器里
setTimeout(()=>{
this.setState({
aa:this.setState.aa+1
})
console.log(this.setState.aa)
//此时 aa 必然为同步
//是 +1之后的值
})
---------------------------------
2.在生命周期里
componentDidMount(){
通过dom绑定点击事件(){
this.setState({
aa:this.setState.aa+1
})
console.log(this.state.aa)
//此时 aa 必然为同步
//是 +1之后的值
}
}
所以不能明确的说 setState
究竟是同步还是异步
一般而言
1.在组件生命周期或React合成事件(比如 onClick)
setState 是异步的
2.在setTimeout或原生dom事件(比如 click)
setState 是同步的
在react 源码中
看的最多的就是
react
react-dom
react-reconciler 渲染到真实dom
源码中 setState 的同步异步取决于
react 的判断
setState 数据的合并
react内部使用了这个函数
Object.assign({对象:1},{对象:2},{对象:3},...)
将所有对象依次叠加到 对象1中
如有重复直接覆盖
可以有返回值 返回值为对象1
多次调用
setState({aa:this.state.aa+1})
setState({aa:this.state.aa+1})
setState({aa:this.state.aa+1})
是无效的
因为 setState 会 由 react 最后统一用
Object.assign 一次性更新所有 setState预设的对象
只有 最新一次会生效 其余的全被覆盖了
这样的话岂不是不能实时控制css 的宽度动画等操作?
有时 我们并不希望它合并
//prevState 是所有由 react
//的 setState 对象统一更新队列中的
//上一个要更新对象中保存的值
//prevState 保存着上一次的值
setState((prevState,props)=>{
return {
aa:prevState.aa+1
}
})
setState((prevState,props)=>{
return {
aa:prevState.aa+1
}
})
setState((prevState,props)=>{
return {
aa:prevState.aa+1
}
})
结果是加 3次
但是依旧无法实时控制 css的宽啊
react 的 渲染机制
将 .jsx 组合成虚拟dom
再将虚拟dom 渲染成真实dom
react 的更新机制
1.props/state 改变
2.render 函数重新执行
3.产生新的虚拟dom树(对象)
4.新旧虚拟dom树(对象)进行diff
5.计算出差异进行更新
6.更新到真实的dom
在setState 时尽量别改变原数组
react 中添加key 的
意义在于
即使key的item 项顺序变化了
也能够精确对比找到变化的 key之一
而精准更新数据
而不是全部更新
index 对key 是没有优化的
因为增加item 时 key 就不对应了
随机数同样如此
item 如果相同的话也是如此
那???
用啥做key 呢
数据库有唯一的 id 标识
用这个当key
可以做到 完美性能优化
render
当app的 render
被调用时
所有 子组件的render函数都会被
重新调用
以生成新的虚拟dom树(对象)
与旧的进行 diff对比
我们应该使用 shouldComponentUpdate
来阻止 app 内子组件
重新render 未被修改过的.jsx
render(){
return < div> div>
}
shouldComponentUpdate( nextProps , nextState ){
//nextProps 当前组件接受的最新的 Props
//nextProps 当前组件的 State变化后最新的State 值
// 虽是最新但是 只是排在队列里 还未实际改变
if(this.state.aa!==nextState.aa){
//如果 当 aa 发生变化时
return true;//允许多次更新当前组件的render .jsx
}
//return true;
//默认返回true(支持多次更新当前组件的render .jsx)
return false;
//其它不是aa 的情况return false
//不对当前组件的render .jsx进行二次更新
}
shouldComponentUpdate
是一个控制当前组件
是否允许更新render.jsx
的生命周期函数
import React,{PureComponent} from 'react'
class 类 extends PureComponent{
//继承自
//PureComponent 类
//的组件
//react 自动帮你
//做了 shouldComponentUpdate
//的所有有关事情
//不用再手写
}
因此之后的类
基本上就是继承自PureComponent
而不是
前面一直写的
Component
接下来要讲memo 的使用
import React,{ memo } from 'react'
//memo 高阶函数式组件
//其功能在于在函数式组件中替代
class 的 PureComponent 使用
//用于自动优化是否渲染render .jsx 的
let bb=memo (function cc(){
return < div> div>
})
< bb /> 使用时已经拥有了优化之后的性能
接下来继续补充完善组件化相关知识
组件 类 和 函数的定义方式
组件的生命周期
组件的 祖孙/父子/子父 通信
子子没讲
组件的slot 就是没有slot 就是把.jsx当变量传入并渲染
setState 被忽视的细节逻辑理解
关于子子
在react 中被称为全局事件传递
需要用到 events
给setState 设置值时尽量不要
用旧值当变量赋值
这可能会影响react 的性能或导致新的错误
正确示范
setState({
aa :aa+1
})
错误示范
aa=aa+1
setState({
aa :aa
})
关于
setState不可变的力量
如果你使用 this.state.aa
来储蓄新的值
并更新
那么在性能函数shouldComponentUpdate
或
继承了PureComponent组件
而非 Component组件的类上
上
以上这些情况都会阻止渲染render .jsx
以节省性能
只有
继承自
Component组件的类上
会没事
能正常重新渲染render .jsx
arr2=[...arr]
arr2
与
arr
指向的不是同一个地方
那岂不是相当于深克隆
如果展开后里面
还有数组/对象的话
该数组/对象是浅拷贝
[0,1,2,3,[4]]
[...[0,1,2,3,[4]]]
1,1,2,3是深拷贝
[4]是浅拷贝
arr=[0,1,2,3,[4]]//源
arr2=[...[0,1,2,3,[4]]]// 生/浅 拷贝源
arr2[4][0]=arr2[4][0]+1
//此时应该 是浅拷贝
但是由于 arr2 是深拷贝
所以 react的 purcomment
性能优化组件
会认为 arr和 arr2 不是同一个内存地址
还是会更新 .jsx
因为 diff 不进行深层比较
所以
当 [...[内部]]外部深拷贝时
内部深层的引用 数组/对象
进不进行深拷贝结果都是一样的
只要外部数组(单个0,1,2,3深拷贝/浅拷贝的引用值[4]) 不同
不管内部 深/浅 拷贝
直接全部更新
arr=[[0],[1],[2],[3],[4]]//源
//arr 的引用地址为 0xff
arr2=[...[[0],[1],[2],[3],[4]]]// 生/浅 拷贝源
//arr2 即使全为浅拷贝
// 地址也会为 新的引用地址
因此 地址 不同
所以 会触发 react .jsx 的 更新(重新渲染)
关于子子(全局事件传递)
在react 中被称为全局事件传递
需要用到 events
快捷键 rpc
创建 继承自 PureComponent 的 组件
子子(全局/通用 事件传递)
-----------------------------
安装事件总线库
yarn add events
或
npm i events
至于react 究竟是依靠 npm 还是 yarn
还是两者都有尚不可知
react 有的包 只支持 yarn react 还是yarn为主吧
vue npm 为主
------------------------------
import { EventEmitter } from 'events'
const eventBus= new EventEmitter();
class App
render(){
return {this.kk()}}>
div>
}
kk(){
eventBus.emit("函数名","参数1",参数2,参数3,参数...)
}
----------------------------------
class 任意类
//给回调函数命名
let hh =(形参1,形参2,形参3)=>{
// 或 (...arr) 接受所有形参至数组
}
componentDidMount(){//生命周期 组件加载完毕
//在此挂载监听
eventBus.addListener("函数名",hh )
}
componentWillUnmount(){//生命周期 组件销毁
//在此卸载所有组件监听
//eventBus.addListener("函数名")
//在此卸载当前组件监听
eventBus.addListener("函数名",hh)
}
如何使用 ref
react 前面的坡度比较陡峭
但是一旦你把它掌握的话
他的运用并不是特别难的
前面讲的源码/原理
可能理解起来比较困难
我讲的真正的目的是为了
把基础打好
这样学习后面的东西反而是比较快的
特别是你把react 核心的东西掌握的话
那些第三方的 ui 库
那些东西用起来就更简单了
就没有什么难度了
用一下别人的UI库就更简单了
所以一定要把
前面的基础打扎实
后面的话再在react 开发里面再
补充一些知识
到时候讲到 补充的知识
就不会特别复杂了
1.ref 受控和非受控组件
----------------------------
import react,{PureComponent ,createRef } from 'react'
--------------------------
class App extends PureComponent {
constructor(props){
super(props);
this.titleRef = createRef();
this.titleRef2 = null
}
render(){
return (
< div>
< h1 ref = ' kk' > 哈哈1
h1>
< h2 ref = {this.titleRef}> 哈哈2
h2>
{this.titleRef2=e}}'>哈哈3 h3>
div>
)
}
componentDidMount(){//组件加载完毕 生命周期
//domcument.getElementById
//方式1 字符串
console.log(this.refs.kk)//不推荐
// 打印结果: < h1> 哈哈1 h1>
//方式2 对象
console.log(this.titleRef.current)//推荐
// 打印结果: < h2> 哈哈2 h2>
//方式3 函数
console.log(this.titleRef2)//一般
// 打印结果: < h3> 哈哈3 h3>
this.titleRef2.innerHTML='以上三种方式拿到元素后都可可修改元素内容'
//如果放在 < 组件类 ref = ?/> 上
//打印结果将会是一个 class组件类对象
// (函数式组件则没有组件类对象)
//可以有其它方式 hooks/React.forwardRef
//来通过 ref 获取函数式组件(后面讲)
//可以用来 调用组件内函数 或 改变子组件内数据
//这点 vue 也可这样调用子组件函数
}
}
认识受控组件
在react中
可以获取到 html使用原生from 提交的信息
并对他们进行 修改 或干些其他事
甚至 react
可以对
这类受控组件(这里指from 或 类似from 的其它dom元素 )
select
进行完全控制
label 标签的 for 属性 ='kk'
input 标签 id='kk'
那么点击 label会 将光标聚焦到 input
在 .jsx 中 for属性写为 htmlfor
防止与.js的 for 关键字冲突
.jsx中
{this.gg(e)}}>
{this.hh(e)}}
value={this.state.value}
>
< input type = submit>
gg(e){
e.preventDefault()
//取消from的 submit默认 提交行为
}
hh(e){
//获取input 更新的 value 即时保存至 state
this.setState({
value:e.target.value
})
}
我感觉有
antd 的react 组件
根本用不到这些受控组件
而且还丑
真要自己手写
还不如全用div
可控组件 select 的写法 不是写在 option里
{this.handleSubmit(e)}}
value={this.state.value}
>
< option value = apple >
< option value = apple2 >
< option value = apple3 >
handleSubmit(e){
this.setState({
value:e.target.value
})
}
升级版 标签中加了 name 属性
handleSubmit(e){
this.setState({
[e.target.name] :e.target.value
})
}
//对象键为变量时需要 []括起来
var cc=1
console.log({[cc]:1})
// { 1 :1}
//这种方式被称为计算属性名
非受控组件
不建议使用
1.受控组件中, 表单数据是由React 组件来管理的
2.另一种是在非受控组件中,使用dom 来处理
大家了解即可
老师不是用的 dom.
而是ref.
拿到 input 元素 获取其value
this.ref='xx'.current.value
//不推荐
而且使用 ref 需要引入 ref 函数
详情请看前面的 ref 相关记录
接下来学习 高阶组件
可能学到这
很多同学感觉到
学习量特别的大
但是
这些东西都是
比较核心的东西
比较重要的东西
并且很多东西再讲的时候
我们会深入到原理
甚至是源码里面
来讲这些东西
所以我们花了大块的事件
把组件这块给他学好
这些东西是非常重要的
组件化
是很重要的基础
高阶函数的定义
-接受一个或多个函数作为输入
-或
-输出一个函数
高阶组件的定义(hoc)
-高阶组件是一个函数
-参数是一个组件
-输出也是一个组件
let 输出返回组件 = 高阶组件函数(参数组件)
编写代码时
快捷键 rpc 回车
出现基本react vscode代码
function gg(cc){
return class newComponent extends PureComponent{
render(){
return(
< div>
< cc /> //这方式有点像vue的插槽
div>
)
}
}
}
gg就是高阶组件
因为它
接受了一个组件
和
返回了一个组件
类命可以像函数名一样
赋值到变量上时可以省略 名字
示例
let aa=function(){}
let bb=class { }
class/函数式 组件
都可以
App(类/函数 名).displayName='App2'
为 类/函数 名 设置展示
(在react的detv调试工具中)
的默认名字
这功能 ... 毫无用处吧
高阶组件
并不属于 react的api
而是一种设计模式
在后面的 redux中的 connect
和 react-router 中的 withRouter
react路由
还有其他第三方组件库
中会运用到
上面的是返回 class 组件
返回函数式组件也是没有问题的
function gg(cc){
return function newComponent {
return(
< div>
< cc /> //这方式有点像vue的插槽
div>
)
}
}
高阶组件 可以劫持
然后做许多事情
比如在 组件上追加 新的属性和值
< cc {...this.props} 新属性: '新值'/>
但是自从 hooks 的出现
高阶组件就用的比较少了
hooks 真的解决了非常多的问题
但是 路由 redux 第三方组件库api
还是使用了非常多的 高级组件
.jsx 语法中
{支持字符串模板写法}
{`名字: ${this.state.name}`}
增强普通 props
增强祖孙 props
总之运用非常广泛
还可以通过高阶组件对渲染进行鉴权(判断)
也就是某些页面必须是登录成功
才能访问
类似于vue的路由守卫的功能
登录鉴权操作
也可以通过 高阶组件来做到
相当于 对 .jsx 进行加工
运用全凭你的想像空间
上面劫持了 props 进行操作
还可以
劫持 .jsx 进行操作
劫持 生命周期函数 进行操作
比如 检测 一个组件渲染花了多长时间
拦截 生命周期
直接重写了生命周期 ?
function withRenderTime(WrappedComponent) {
return class extends PureComponent {
// 组件即将渲染获取一个时间 beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// 组件渲染完成再获取一个时间 endTime
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(`${WrappedComponent.name}渲染时间: ${interval}`)
//可以拿到当前组件的 name
}
render() {
return < WrappedComponent {...this.props} />
}
}
}
官方建议 不要继承自己的组件
而是去组合
高阶组件(函数)的意义
写出更加优雅的代码处理
react 不建议使用 mixin 混入
mixin 可能会相互依赖 相互耦合 不利于代码维护
不同的mixin 中的方法可能会冲突
Mixin非常多时 , 组件是可以感知到的
甚至还要为其做相关处理
这样会给代码造成滚雪球式的复杂性
函数式组件 ref 的转发
自带高阶组件(函数)
import { forwardRef } from 'react';
可以使
函数式组件
拥有class类对象实例
从而 使用 ref
选取
函数式组件
里面的某个具体元素
const Profile = forwardRef(function(props, ref) {
//增强多出一个参数 ref
return < p ref = {ref}> Profile p>
})
hooks 可以改变这种方式
使其变得更简单
(模态框的)独立挂载
Portals 的使用 ReactDOM.createPortal
某些时候
我们希望
(组件)渲染的内容
独立于父组件
甚至
独立于
当前挂载到的
DOM元素(指 id为root 的react选定的那个div)
比如全局弹窗组件
就可能需要这个功能
有两种方案
1.弹窗直接放在body上
2.提前写好一个弹窗 div 在屏幕居中显示
ReactDOM.createPortal(任意一个想要渲染的子元素,选中的dom元素)
// 参数1 参数2
import ReactDOM from 'react-dom';
class Modal extends PureComponent {
render() {
return ReactDOM.createPortal(
//需要用到这个函数
this.props.children,//指父组件的所有子组件
document.getElementById("modal")
)
}
}
在js里引入 css
就是普通的
import '.css'
在style中引入 css
@import url('a.css');
Fragment
代替 .jsx return 返回时的根元素
呈现的状态为 空
有效节省了 dom 节点的渲染
加强了性能
import { Fragment } from 'react';
--以前写为
render() {
return (
< div>
< p> 1 p>
< p> 2 p>
< p> 3 p>
div>
)}
--现在可写为
render() {
return (
< Fragment >
< p> 1 p>
< p> 2 p>
< p> 3 p>
Fragment >
)}
--简写为
render() {
return (
<>
< p> 1 p>
< p> 2 p>
< p> 3 p>
>
)}
如果要往标签里加属性
则不能简写
< Fragment key = {item.name}>
< p> 1 p>
< p> 2 p>
< p> 3 p>
Fragment>
StrictMode 严格模式
//初始创建 就带有这么个东西
严格模式 仅在开发时生效
提供警告 和
全面检查
1.检查即将弃用的生命周期 componentWillMount
2.过时的ref API 字符串方式
3.检查意外的副作用
会调用2次你写的某些 生命周期 或者 生函数
只是在开发时
4.检查即将弃用的其它API 比如findDOMNode 获取dom元素方法
5.检测过时的context API 比如 通过getChildContext返回的Context对象
在 任意组件.js 中
export default class App extends PureComponent {
render() {
return (
< div>
< React.StrictMode>
< Home/>
React.StrictMode> ,
< Profile/>
div>
)
}
}
或
在index.js中
ReactDOM.render(
< React.StrictMode>
< App />
React.StrictMode> ,
document.getElementById('root')
);
react 的样式写法
内联样式
------------------------------------
在 .jsx中
< h2
style = {}
>
我是标题
h2>
------------------或------------------
this.state = {
color: "red"
}
< h2
style = {{fontSize: "50px", color: this.state.color}}
>
我是标题
h2>
------------------或------------------
let aa={fontSize: "50px", color: "red"}
< h2
style = {aa}
>
我是标题
h2>
------------------------------------
后面学了
css in js 组件库
就没有必要写行内样式了
css 在任意组件引入
import './xx.css'
都对 所有组件的类名生效
css modules
这个是由 webpack提供的 css 模块化工具
需要自己在
webpack.cinfig.js中
配置 modules:true等
react 中内置 .css/.less/.sass 的模块化 配置
我们只需 xx.modules.css
.less
.sass
就可以了
引用时就成了
import appStyle from './style.module.css';
只有引入 该css
并使用 appStyle
render(){
return{
< div className = {appStyle.title}> div>
//表示 使用该模块内的指定某个类名样式
}
}
组件才能生效
样式类名需要使用驼峰法命名
{内不支持-连字符写法}
css in js 方案
全面的 动态/静态 css/less/sass 方案
使用最广泛的库是 styled-components
yarn add styled-components
用前须知
---------------------------------
text() 调用 函数名为text的函数
text`` 同上
let name='小明'
let age=18
text`my name is ${name},age is ${age}`
结果为
[
['my name is ',',age is',''],
'why',
18
]
text`
font-size:15px;
color:red;//静态样式写法
color:${props=>{reops.color}};//动态样式写法
color:${this.state.color};
color:${color};
`
styled-components 这个框架
会解析传过来的样式
使用方式
----------------------------
vscode 语法高亮插件: vscode-styled-components
作者 Julien Poissonnier
-------------------------------------------
yarn add styled-components
---------------------------------------------
import style from 'styled-components'
let HomeWrapper =styled.div`
font-size:15px;
color:red;//静态样式写法 支持sass 语法
&span{
}
`
div 是函数
HomeWrapper 是函数返回的组件
既然是组件 就能在 .jsx 中使用
render(){
return(
< HomeWrapper>
就是个div 并且带有div的样式
HomeWrapper>
)
}
如果是 input 元素
可以直接在返回的 组件上写 type='text'
props 会穿透进去的
------------------------------------------------------
const HYInput = styled.input.attrs({//attrs 元素属性
placeholder: "coderwhy",
bColor: "red"//相当于在返回的组件上自定义属性名
})`//继续样式 不受影响
background-color: lightblue;
border-color: ${props => props.bColor};//相当于拿到组件上传入的属性名
color: ${props => props.color};//拿到组件上传入的属性名
`
----------------------------------------------------
this.state = {
color: "purple"
}
---------------------------------------------------
< HYInput type = " password" color = {this.state.color}/>
根据 styled-components
通过 元素(比如div) 函数
创造的组件可以被继承(较少使用)
----------------------------------
//初代样式
const HYButton = styled.button`
padding: 10px 20px;
border-color: red;
color: red;
`
-------------------------------------------
//继承该组件的主要样式并稍作修改(重写部分样式)
const HYPrimaryButton = styled(HYButton)`
color: #fff;
background-color: green;
`
------------------------------------
关于主题的使用方法
---------------------------------
import styled, { ThemeProvider } from 'styled-components';
------------------------------------------------------------
render() {
return (
< ThemeProvider theme = {{ themeColor: "red", fontSize: "30px" }} >
ThemeProvider>
)
}
------------------------------------------------------------
theme={{ themeColor: "red", fontSize: "30px" }}
这个样式此时被共享到了 全局
任意一处都可使用 theme中的样式
---------------------------------------------
const TitleWrapper = styled.h2`
text-decoration: underline;
color: ${props => props.theme.themeColor};
font-size: ${props => props.theme.fontSize};
`
-------------------------------------------------
接下来学习AntDesign-react 组件库
react 的 className 还有个专门的动态库
-----------------------
yarn add classnames
-----------------------
import classNames from 'classnames';
-----------------------
.jsx 中
div>
---------------------------------------------------
isActive 变量为true 则class中有类名 active
否则没有
"kk" 长存class的类名写这里
gg 长存class的类名 的变量 写这里
gg如果是null/undefind
只要判断为 false
则不会显示class的类名
classNames({"active":isActive,"active2":isActive2},"kk",gg)
classNames([{"active":isActive,"active2":isActive2},"kk",gg],"hh")
传入数组对象都可以
而且规律看起来比较宽松
AntDesign 的安装
yarn add antd --安装组件库
import 'antd/dist/antd.less' --引用CSS库
import { Button, DatePicker } from 'antd'; --引用组件库
< Button type = " primary" loading > Loading
Button> --使用组件库
yarn add @ant-design/icons --安装图标库
import { PoweroffOutlined } from '@ant-design/icons' --引用组件库
< div icon = { /> } loading /> --使用组件库
yarn add moment --安装日期格式化库
import moment from 'moment'; --引用组件库
--使用组件库
为什么 uni-app 的日期选择是 字符串
而这非要用 日期格式化
因为电脑卡所以代码才没有提示
@ant-design 可能是某一个项目有很多其他库
/icons 是项目中具体的某一个库
rollup 非UI打包工具
开发 vue react
无ui界面等是 rollup 轻量级打包工具
打包的
不是webpack
该打包工具 采用了 按需引入