原型链是实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。每个构造函数都有一个prototype属性,指向原型对象,原型对象的包含一个指向构造函数的指针(constructor)
每个对象都可以有一个原型__proto__,这个原型还可以有他自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里面找,如果没有的话就去他的原型对象里面去找,以此类推…………这个操作被委托在整个原型链上,这个就是我们说的原型链了
prototype属性,是函数所独有的,他是从一个函数指向一个对象,他的含义是函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象,这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法----原型对象
__proto__属性,是原型链查询中实际用到的,他总是指向prototype,换句话说就是指向构造函数的原型对象,他是对象独有的
constructor属性,每个函数都有一个原型对象,该原型对象有一个constructor属性,指向对象的函数本身。
不同点:
typeof
是一个操作符而不是函数,用来检测给定变量的数据类型
typeof
原理:不同的对象在地底层都表示为二进制,在javascript
中二进制前三位存储其类型信息。
typeof
null为object原因是因为不同的对象在底层都表示为二进制,在javascript中二进制前三位都为0会被判断为object类型,null的二进制表示全为0,自然前三位也是0。
Array: 1000100010001000
null: 0000000000000000
//那么为什么数组也是object呢
那是因为二进制中的前一般代表地位 所以array的前三位不是100而是000
instanceof
用来比较一个对象是否为某一个构造函数的实例。注意instanceof运算只能用于对象,不适用原始类型的值,也可以判断某个对象的原型是不是某个值
instanceof
原理:检测constructor.prototype
是否存在与参数object
的原型链上。instanceof
查找的过程中会遍历object
的原型链直到找到constructor
的prototype
,如果查找失败测绘返回false
相同点:常用来判断变量是否为空或者是什么类型的。
**es5:**Number、String、Boolean、null、undefined,Object
**es6:**新增了Symbol、给Object新增了Set、Map、WeakSet、WeakMap、TypedArray等
对象中的一种类型,时值得集合,可以按照插入顺序迭代他的元素。Set中的元素只会出现一次,所以set中的元素时唯一的
保存键值对,并且能够记住键的原始插入顺序,任何值都可以作为一个键或一个值
WeakSet
对象是一些对象值的集合, 并且其中的每个对象值都只能出现一次。在WeakSet
的集合中是唯一的
Set
相比,WeakSet
只能是对象的集合,而不能是任何类型的任意值。WeakSet
持弱引用:集合中对象的引用为弱引用。 如果没有其他的对WeakSet
中对象的引用,那么这些对象会被当成垃圾回收掉。 这也意味着WeakSet中没有存储当前对象的列表。 正因为这样,WeakSet
是不可枚举的。WeakMap
对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
Map | Object | |
---|---|---|
意外的键 | Map 默认情况不包含任何键。只包含显式插入的键。 |
一个 |
键的类型 | 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。 |
一个Object 的键必须是一个 String 或是Symbol 。 |
键的顺序 |
|
一个 |
Size | Map 的键值对个数可以轻易地通过size 属性获取 |
Object 的键值对个数只能手动计算 |
迭代 | Map 是 iterable 的,所以可以直接被迭代。 |
迭代一个Object 需要以某种方式获取它的键然后才能迭代。 |
call,apply,bind相同点:三者都可以改变函数的this指向。三者第一个参数都是this要指向的对象,如果没有这个参数或者参数为undefined||null则会默认指向全局window。
不同点:apply是数组,而call是参数列表,而apply和call是一次性传入参数,而bind可以分多次传入。bind传入的参数和call一样
bind是返回绑定this之后的函数,便于稍后调用;apply,call则是立即执行
函数内部呢能够沿着作用域链访问外部的变量,叫做闭包
[[]]里包着的属性叫做运行时属性
[[Scopes]]—作用域链
//a.js
const a=1;
module.exports=a
const a=require('./a')
/**
*
*
*/
console.log(a)
Promise.all() 方法接收一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个Promise
实例, 那个输入的所有promise的resolve回调的结果是一个数组。这个Promise
的resolve回调执行是在所有输入的promise的resolve回调都结束,或者输入的iterable里没有promise了的时候。它的reject回调执行是,只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。
跨域:值得是浏览器不能执行其他网站的脚本。它是由浏览器同源策略造成的,是浏览器对javascript施加的安全限制
可以围绕一下五点来进行区别
1、变量提升
var声明的变量存在变量提升,即变量可以在声明之前调用值为undefined
let和const声明的变量不存在变量提升,即他们所声明的变量一定要在声明后使用,否则会报错
2、暂时性死区
var不存在暂时型死区,也就是可以变量提升
let和const存在暂时性死区,只有等到声明的那一行代码出现才可以获取和使用该变量
3、块级作用域
var不存在块级作用域
let和const存在块级作用域
4、重复声明
var可以重复声明
let和const在同一作用域下不允许重复声明
5、修改声明的变量
var和let可以修改已经声明过的变量
const声明一个只读的常量。一旦声明常量的值就不能改变,const实际上保证的并不是变量的值不能改动,而是变量指向的那个内存地址所保存的值不能改动,对于简单类型的数据,值就保存在变量指向的那个内存地址,因此等同于常量。对于复杂类型,变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的,并不能确保该变量的结构不变
作用域是一套规则,用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识名称进行变量查找
词法作用域
词法作用域也就是在词法阶段定义的作用域。
换句话说,词法作用域就是你在写代码的时候就已经决定了变量的作用域。
动态作用域
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
(3) 执行构造函数中的代码(为这个新对象添加属性) ;
(4) 返回新对象。
"EventLoop是一个程序结构,用于等待和发送消息和事件。
简单说,就是在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"EventLoop线程"(可以译为"消息线程")。
只能捕获同步错误,异步捕获不到
try()catch(err){}
都能捕获到
window.addEventListener("error",(err)=>{
console.log(err)
})
Generator是一个特殊的函数–返回一个对象,调用对象里next()方法来继续往下执行
//每当执行到yield就会暂停使用next继续往下执行
function *fo(){
const a=yield Promise.resolve().then(()=>{return 1})
return a;
}
fo().next().next()
每个vue实例在被创建时都要经过一系列的初始化
vue的生命周期由四个阶段组成
比对render前后生成的两颗虚拟dom树,从上到下从左到右基于key值一次遍历比对。
具体道一个vDom节点的比对过程:有key就先比对key,key不一样的话,就直接把原来的销毁掉,然后创建一个新的。
然后比对type,type不一样的话,就直接把原来的销毁掉,然后创建一个新的-销毁阶段-创建阶段。
最后比对所有属性,属性更新的话只需要更新节点的属性-update阶段。
react是一个声明式,高效且灵活的用于构建用户界面的javaScript库,使用react可以将一些简短、独立的代码片段组合成复杂的UI界面,这些代码片段被称作“组件”
因为react在设计之初就有跨平台的的野望,所以react本身只关注vDom,具体到客户端的实现转换,有其他包来处理
因为要使用JSX也就式createElement的语法糖
jsx在react中是createElement的语法糖
React Dom在渲染所有输入内容之前,默认会进行转义。
元素是构成React应用的最小砖块,描述了想在屏幕上想看到的内容----组件是由元素构成的
元素是不可变对象,一旦被创建,你就无法更改它的子元素或者睡醒,一个元素就像电影的单帧:他代表了某个特定时刻的UI
首字母必须大写
函数式组件
定义组件最简单的方式就是编写JavaScript函数------函数组件的render:每次函数组件的Render意味着这个函数每次再次执行
//组件名称必须要以大驼峰来表示
const Demo=function(props){
//props是从 中传过来的参数
//props默认就是对象类型,在react中props是只读的,不允许修改
//普通函数和箭头函数中的this都指向undefined
return (
111
//渲染组件
)
//在组件中直接使用Child就表示是父子组件,通过props传参
//在react中并不会通过props修改而重新渲染
//v16.8以前函数式组件都没有这种状态
}
const Child=(props)=>{
console.log(props)//{a:"aaa"}
}
ReactDom.render(
,//数据
document.getElementById(root)//目标
)
类组件
//react组件--原型机-React.Component
//大驼峰命名、使用class声明
class Demo extends React.Component{
//必须要使用render方法,而且必须要有返回值
constructor(){
super()
}
state={
a:0
}
handlerClick(){
//react并没有进行mvvm绑定并不会更新视图
this.state.a++;
//接收两个参数
//第一个参数时可以和state进行合并,更新state是异步的,值得注意的是如果写在了定时器或者延时器内那么他就是同步的
//第二个参数是一个回调函数,是this.setState&&render()执行完之后执行的函数,这就有了mvvm绑定
this.setState({
a:1
},()=>{})
}
render(){
//onClick事件,并不是原生的而是react给我们封装好的,必须要传入一个函数
//当使用this.handlerClick不能给参数,因为会直接执行此函数
//当需要参数时应该这么写()=>this.handlerClick("1")
return(
{this.state.a}
+
)
}
}
组合组件
组件可以在其输出中引入其他组件。
function Welcome(props) {
return Hello, {props.name}
;
}
function App() {
return (
);
}
ReactDOM.render(
,
document.getElementById('root')
);
组件无论是使用函数声明还是通过class声明都绝不能修改自身的props。
所有的react组件都得象纯函数一样保护自己的props不被更改
不要直接修改State因为不会重新渲染而是使用setState()
构造函数是唯一可以给this.state赋值的地方
出于性能考虑React可能会把多个setState()调用合并成一个调用所以是异步的
多个setState()一块更改一个状态时不会同时进行更改,因为setState会等待其他setState一块执行
state的更新可能是异步的因为
当调用setState()的时候,react会把提供的对象浅合并到当前state
setState更改值的两种更改方案
直接在setState中放入值
第一个参数放回调函数
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
```
setState()
将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式
将 setState()
视为请求而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。React 并不会保证 state 的变更会立即生效。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-voZXP6AK-1644484439771)(D:\react生命周期钩子16.0.0.png)]
数据请求一般写在ComponentDidMount里
执行顺序
挂载阶段钩子
constructor—>ComponentWillMount–>render–>ComponentDidMount–>运行中
更新阶段钩子–顺序
compoentWillReceiveProps–只有父组件更新才会执行这个生命周期钩子
(只有父组件reRender子组件就会reRender)
shouldComponentUpdate–根据返回值决定是中断reRander(true||false)
componentWillUpdate–reRander之前
Render–渲染视图diff
ComponentDidUpdate–reRander之后
卸载阶段
怎么卸载:条件渲染、key值改变—
必须是在在别的组件卸载本组件才可已触发本组件的钩子
componentWillUnmount
shouldComponentUpdate比较少用—性能优化
//这个钩子提供两个参数合会一个布尔值
/*
return true:继续执行下一个钩子
return false:不会往下执行钩子
*/
/*
nextProps:下一个属性值
nextState:下一个状态值
*/
//可以用来做优化
shouldComponentUpdate(nextProps,nextState){
//比较当前状态与下一个状态值是否相等如果相等则可以返回false也就是不会再次render了
}
componentDidCatch
//只能捕获子组件或者插槽中的错误--异步捕获不到
class Demo extends Component{
componentDidCatch(error){
console.log(error)
}
}
//可以用来做403,404错误页面
static getDeriveStateFromError
//捕获错误同上
class Demo extends Component{
static getDeriveStateFromError(error){
return {
hasError:error
}
}
}
getChildContext
//老版和context一块使用的钩子----现在不在使用
import PropType from "prop-types"
class Demo extends React.Component{
static childContextTypes={
count:PropType.number
}
getChildContext(){
//在这里使用的时候必须在ContextTypes中声明类型
return this.data
}
data={count:1}
}
static getDerivedStateFromProps()
会在render方法之前调用,并且初始挂在及后续更新时都会被调用。它返回一个对象来更新state,如果返回null则不更新任何内容
class Demo1 extends React.Component{
state={
count:1
}
static getDerivedStateFromProps(){
//在渲染之前调用
console.log("getDerivedStateFromProps");
return null
}
handlerClick=()=>{
console.log(this);
this.setState({
count:this.state.count+1
})
}
render(){
console.log("render啦");
return(
{this.state.count}
)
}
}
getSnapshotBeforeUpdate()
在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate()
。
getSnapshotBeforeUpdate(prevProps, prevState)
这里默认做了shouldComponentUpdate这个钩子需要做的事情也就是对state和nextState做了一个浅比较—性能优化
class Demo extends React.PureComponent{
//....
}
react元素的事件处理和DOM元素事件处理很相似
React事件的命名采用小驼峰式,而不是纯小写
render(){
return (
)
}
使用JSX语法式需要传入一个函数作为事件处理
向事件处理程序传递参数
//在这两种情况下,React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。
可以通过{}在JSX内构建一个元素集合。当渲染的时候会将元素集合一个一个的进行渲染。
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
{number}
);
ReactDOM.render(
{listItems}
,
document.getElementById('root')
);
/**
1
2
3
4
5
*/
key帮助React识别哪写元素改变了,比如被添加或者删除。因此你应当给数组中的每一个元素赋予一个确定的标识。一个元素的key最好是这个元素在列表中拥有的一个独一无二的字符串。
元素的key只有放在就近的数组上下文中才有意义也就是在map方法中的元素需要设置key属性
key只是在兄弟节点之间应该是独一无二的。并不需要全局唯一。当我们生成两个不同的数组是,我们可以使用相同的key值
将数据绑定视图中,视图的展示受控于数据。与用户交互时,回调里更新数据,从而驱动视图更新。比如一个input框,vlaue绑定为一个状态,交互回调里调用this.setState
class Demo extends React.Component{
state={
value:"111"
}
handleChange=(e)=>{
this.setState({
value:e.target.value
})
}
handleClick=(e)=>{
cosole.log(this.state.value)
}
render(){
const {value}=this.state
return(
)
}
}
有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个React组件传递所有的输入state。当你将之前的代码库转换为React或将React应用程序域非React库集成时,这可能会令人厌烦
ref-name—v15(快被弃用)
this.refs.inputRef//获取dom实例
//或
{console.log(target)}}/>
ref-callback—v16.0
{this.inputRef=target}}/>//target表示此DOM实例
this.inputRef
createRef—v16.7
//createRef是一个闭包返回了一个{current:null}当进行绑定时就可以将DOM保存到current属性然后通过this.inputRef.current来操作DOM
import {createRef} from "react"
class Demo extends React.Component{
constructor(props){
super(props)
this.inputRef=createRef()//这里没有默认值在Hook中可以给useRef默认值如果这里需要给默认值时可以直接使用this.createRef.current=null
}
componentDidMount(){
console.log(this.inputRef);
}
render(){
return (
)
}
}
//或者直接给current值
class Demo extends React.Component{
constructor(props){
super(props)
this.inputRef={current:null}
}
componentDidMount(){
console.log(this.inputRef);
}
render(){
return (
)
}
}
视图不受数据控制。一般对应表单元素不受控的话,我们也可以使用ref来获取表单元素中的值
class Demo extends React.component{
inputRef = React.createRef();
handleClick = () => {
console.log(this.inputref.current?.value)
}
render(){
return (
)
}
}
class Demo extends React.component{
inputValue = '';
handleChange = (event) => {
this.inputValue = event.target.value;
}
handleClick = () => {
console.log(this.inputref.current?.value)
}
render(){
return (
点我获取input的value
)
}
}
当vDom非常庞大时,每次进行diff时就会卡顿,因为diff是不可中断的,一整个树为单位
解决,以节点为单位,可以中断
在React中,将多个组件中需要共享的state向上移动到他们的最近共同父组件中,便可实现state,这就是所谓的‘状态提升’
作用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrmEtUGL-1644484439772)(D:\上课截图\Snipaste_2022-01-10_09-25-14.png)]
class EventStore {
store = {
// eventName:[]
};
/**
* 声明对应的事件名称和应该传入的函数,这个函数用来对数据进行更新例如fn(data){this.setState({count:data})}
* @param {*} eventName
* @param {*} fn
*/
on = (eventName, fn) => {
this.store[eventName]
? this.store[eventName].push(fn)
: (this.store[eventName] = [fn])
}
/**
* 执行对应的事件名eventName和要传入的数据params让他在子组件里进行更新数据
* @param {*用来执行对应的函数*} eventName
* @param {*给其传入的值*} params
*/
emit=(eventName,params)=>{
this.store[eventName]?.forEach(fn=>{
fn(params)
})
}
}
export default new EventStore();
context
在子(孙…)(内部)组件中可以共享数据
不能同级通信
import PropTypes from 'prop-type'
class Child extends React.Component{
//传入到context中的内容必须要进行声明
static contextTypes={
a:PropTypes.number
}
render(){
console.log("this.context",this.context);
return(
)
}
}
class App extends React.Component{
//对应着子组件里的属性
static childContextTypes={
a:PropTypes.number
}
getChildContext(){
//return出去的对会直接打到子组件的this.context里--前提是子组件必须要有对应的属性
return {
a:1
}
}
render(){
console.log("APP THIS",this);
return (
)
}
}
import React, { createContext } from 'react';
import './App.css';
export const myContext = createContext({
a:2
});
class Child extends React.Component{
//这里是将父组件中的value打进子组件(本组件)中的this.context
static contextType = myContext
render(){
console.log(this,'Child')
return {this.context?.a}
}
}
class App extends React.Component{
render(){
return (
// 如果打开下面的注释,那么Child中的this.context取值为 {a:4},即Provider的value值
//通过value向子组件传值
// 现在取的是默认值,即 {a: 2}
//
//
)
}
}
export default App;
- 第二种使用方式
//Child
import React from "react";
import PropTypes from "prop-types";
// import eventStore from "../../../utils/eventStore";
import testContext from "./../../../utils/context";
const { Consumer } = testContext;
console.log("PropTypes.number", PropTypes.number);
const connect = (Target) => {
return function NewComponent(){
return (
{(value) => {
console.log("value", value);
return (
);
}}
)
}
}
class Child extends React.Component {
state = {
count: null,
};
componentDidMount() {}
render() {
console.log('child', this);
return (
{/* 我是Child,我要显示来自最外层组件Test的数据count: {this.props} */}
);
}
}
export default connect(Child);
import React from "react";
import Child from "../Child";
const Parent = (props) => (
parent
);
export default Parent;
import React, { Component, createContext } from "react";
import PropTypes from 'prop-types';
import Parent from "./Parent";
import textContext from './../../utils/context';
console.log('textContext', textContext);
const { Provider } = textContext;
const store = {
state: {
count: 0,
a: 1
},
setStore : (assignState, callback) => {
Object.assign(store.state, assignState);
callback(store.state);
}
}
export default class Test extends Component {
componentDidMount(){
console.log(this)
}
render() {
return (
{/* */}
test
{/* */}
);
}
}
FLUX思想—架构思想
Flux是一种架构思想,简单来讲:当项目足够大时,将数据提升到外部仓库里—状态提升到全局Store + Store内容分发给组件+组件交互updataStore然后更新组件就是这样一种,通过全局store统一管理数据(从而实现组件数据共享的架构思想)
数据共享:状态提升到全局然后分发下去
更新数据:updateStore
并发模式图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LsFv5G9k-1644484439773)(D:\上课截图\Snipaste_2022-01-13_16-36-20.png)]
Redux工具包
react+react-Redux+Redux
为了解决远端组件进行数据共享、全局状态管理和全局状态通信 的工具包
React-Redux的核心:Provider与connect
-
provider:容器组件,底层:把store打入context
-
connect :高阶组件,底层从context中拿到store,然后打入参数组件的props
function Child(props){
console.log(props)
return(
Child
)
}
export default connect(state=>state)(Child)
redux核心:createStore与disPatch
-
createStore:创建store
-
dispatch:更新store的方法
//store
import { createStore,combineReducers} from "redux"
import taskArrReducer from "./reducers/taskArr"
const reducers = combineReducers({
taskArr:taskArrReducer
})
const store=createStore(reducers)
export default store;
//./reducers/taskArr
const allTaskList=[]
const taskArrReducer=(state=[],action={})=>{
const {type,newTaskArr,value,bool,index}=action
const getCurrentShowList=(index)=>{
switch (index){
case 0:
return [...allTaskList]
case 1:
return allTaskList.filter(item=>item.bool===false)
case 2:
return allTaskList.filter(item=>item.bool===true)
default:return []
}
}
switch (type){
case "ALL_TASK_LIST":
state=getCurrentShowList(0)
return state;
case "ADD_ALLTASKLIST":
allTaskList.push({
value,
bool
})
return state;
case "UNFINISHED_TASK":
return getCurrentShowList(1)
case "FILTER_TASK_LIST":
return getCurrentShowList(index)
default:
return state
}
}
export default taskArrReducer
dispatch的规范/更新store的流程
dispatch是一个action(action就是一个对象,形如(type:“XXXX”,…args)), 基于action.type触发对应的reducer(reducer是具体更新store的方法,纯函数)逻辑,从而更新store
DVA图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lYG6fTzl-1644484439773)(D:\1608495359297-7ed29641-0793-4f74-8490-626ac7779413.png)]
Redux中间件
Hook简介
什么是Hook?
Hook是一些可以让你在函数组件里“钩入”React state及生命周期等特性的函数,Hook不能再class组件中使用——这使得你不使用class也能使用React。
它可以让咱们不在编写class的情况下使用state以及其他的react特性
-
完全可选
-
100%向后兼容
-
现在可用。hook已发布于v16.8.0
为什么添加Hook
- 在组件之间复用状态逻辑很难
- 复杂组件变得难以理解
- 难以理解的class
useState
会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他方法中调用这个函数
可以在一个组件中多次使用State Hook
相当于class里的特殊实例属性state,与this.setState
function ExampleWithManyStates() {
// 声明多个 state 变量!
// 数组解构的语法让我们在调用 useState 时可以给 state 变量取不同的名字。
const [age, setAge] = useState(42);//useState(初始值)
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
- 注意使用useState维护复杂(复合类型)的状态时,每次更新的值不会与原来的值合并
function Demo(params) {
const [state,setState]=useState({
count:0,
oterState:0
})
console.log("此组件render啦 此时的state为:",state);
return(
setState({count:state.count+1})}>{state.count}
)
}
- 可以发现的是原来的值oterState不见了,他被覆盖掉了,此时我们可以使用结构运算符使用
()=>setCount({...state,count:state.count+1})
useEffect(副作用)
概念:什么是副作用(effect)?我们暂时理解为UI无关的业务逻辑(数据获取、订阅或者手动修改过 DOM)—第二个参数设置为空数组[]就能实现
componentDidMount()
function SetNumber(number) {
/**
* []表示解构赋值
* count:读取数据,相当于state里的属性
* setCount: 设置数据的方法,类似this.setState()
* useState(data):data是count的默认值
*/
const [count,setCount]=useState(0)
/**
* useEffect
* @prop callback 执行副作用---可以返回一个函数用来消除副作用
* callback在渲染之后执行,他返回一个函数在卸载之后执行
* @prop deps 监听器---当监听器发生改变时会触发副作用否则只有每次reRender之后才会执行,目的是为了不阻塞reRender
*/
useEffect(()=>{console.log("你已经使用了");document.title=`你已经点了${count}次`})
return(
你已经点了{count}次
setCount(count+1)}>点我变数
)
}
作用域链与值存储
function Count(props){
//触发函数组件的render
//在学习hooks之前我们智能考虑到通过外层组件来触发函数内部组件的render
let count=0;
console.log("函数组件render了,当前count的值为:",count);
return(
count++}>
点击使count++
是我使父组件更新,从而引发本组件更新
)
}
/**
* 问题:每次render后整个函数组件重新执行,count值会被重置,那么如何维护count
* 闭包:函数特性——————函数可以沿着作用域链访问外部变量
* 实践1:变量提升到外部作用域,通过外币作用域来存储域维护变量值
*/
const insObj={
current:0
}
const addCount=()=>{
insObj.current++;
}
function Counter(props) {
const [state,setState]=useState(0);
console.log("函数组件render了,当前count值为",insObj.current);
return (
+
setState(state+1)}/>
)
}
/**
* 实践2:外层包裹一层函数作用域,通过外部函数作用域来存储与维护变量值
*/
function counterCreator(props){
const insObj={
current:0
}
const addCount=()=>{
console.log(insObj.current);
insObj.current++;
}
return function Counter(props) {
console.log('函数组件render了,当前count值为',insObj.current);
return +
}
}
function Count(props) {
const Counter=counterCreator(props)
return (
)
}
useRef
返回一个可变的ref对象,其.current
属性被初始化为传入的参数。返回的ref对象在组件的整个生命周期内持续存在。
它可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式。
function TextInputWithFocusButton() {
const inputEl=useRef(null);
const handleClick=()=>{
//这时候的current指向已经挂在DOM上的文本元素
inputEl.current.focus()
}
const handleFocus=()=>{
inputEl.current.value="你好牛逼"
}
return (
<>
在input上聚焦
>
)
}
其功能类似class组件的实例属性
class Demo extends React.Component{
count = 0;
render(){
return (
{this.count}
)
}
}
forwardRef
useRef获取子组件会产生warning因此需要在子组件里面使用forwardRef
const ChildRefScreen = () => {
const _a = useRef(null)
return (
)
}
const A = forwardRef((props, ref) => {
return (
<>
>
)
})
memo-useRef/useCollBack/useMemo
什么是memo?:函数组件每次更新都会重新执行函数本身,组件内部定义的变量与函数都会再次初始化定义。
useRef:变量记忆
useCallback:函数记忆
useMemo:计算结果记忆,什么UI记忆
useLayoutEffect
const Demo =props=>{
//使用场景,例如在render前执行一段动画
//和渲染有关的业务逻辑
useLayoutEffect(fn,[])//消除副作用>>副作用>>render
}
useReducer
useCallback
----useCallback会占用空间—在某些时段提倡使用例如父组件更新useCallback会使用缓存
const Demo =props=>{
const [count,setCount]=useState(0)
const handlerClick=useCallback((count)=>{
//当监听器内容不变时,这个函数就会一直被缓存
//当监听器内容发生变化时,这个函数就会被重新定义
//也可以通过形参传进来来进行变化---count
setCount(count+1)
},[])
return(
{count}
)
}
useMemo
const Demo =props=>{
//同上,需要配合第二个参数使用---[]监听器
const memoUI=useMemo(()=>{
return count + 1
},[])
}
自定义Hooks
export const useFilter={
//自定义Hook,将业务逻辑从UI视图里面抽离出来然后返回出去UI页面需要使用的东西
}
封装Card卡片
import React,{useEffect,useRef} from "react";
import styles from "./index.module.css"
function Card(props){
const {title,extra,style}=props
return(
{title}
{extra}
{props.children}
)
}
function Demo() {
return(
More} style={{width:1000}}>
正文
正文
正文
)
}
export default Demo
Fiber简介
截止到当前版本(react17.0.2),react尚存在一个明显的缺陷——这是大多数vDom框架都需要面对的——“diff”。 要知道,render前后的两颗vDom tree进行diff,这个过程是不可中断的(以tree为单位,不可中断),这将造成当diff的两颗tree足够庞大的时候,js线程会被diff阻塞,无法对并发事件作出回应。 为了解决这个问题,react将vDom节点上添加了链表节点的特性,将其改造成了fiber节点(其实就是vdom节点结合了链表节点的特性),目的是为了后面的Fiber架构的实现,以实现应对并发事件的“并发模式”。 事实上,原计划是17版本上的,但最终定期在了18版本。
fiber(细化)可以把一个耗时长的任务分成许多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。React中的Fiber就把整个更新过程碎片化。
如果在更新过程中有大量的节点需要更新就会出现长时间占用JS主线程的情况,并且整个递归过程无法被打断,由于JS线程和GUI线程事互斥的,所以可能会看到UI卡顿。
所以要实现fiber架构,必须要解决两个问题
-
保证任务在浏览器空闲的时候执行—requestIdleCallback
-
将任务进行碎片化
-
requestIdelCallback
requestIdleCallback(callback)是实验性API,可以传入一个回调函数,回调函数能够收到一个deadline对象,通过该对象的timeRemaining()方法可以获取到当前浏览器的空闲时间,如果有空闲时间,那么就执行一小段任务,如果时间不足了,则继续requestIdleCallback,等到浏览器又有空闲时间的时候再接着执行。
requestIdleCallback(callback[,option])
/*
callback
一个在事件循环空闲时即将被调用的函数的引用。函数会接收到一个名为 IdleDeadline 的参数,这个参数可以获取当前空闲时间以及回调是否在超时时间前已经执行的状态。
options 可选
包括可选的配置参数。具有如下属性:
timeout: 如果指定了timeout,并且有一个正值,而回调在timeout毫秒过后还没有被调用,那么回调任务将放入事件循环中排队,即使这样做有可能对性能产生负面影响。*/
-
链表结构
目前的虚拟DOM是树结构,当任务被打断后,树结构无法恢复之前的任务将继续执行,所以需要一种新的数据结构,即链表,链表可以包含多个指针,可以轻易找到下一个节点,继而恢复任务的执行,Fiber采用的链表中包含三个指针,parent、child、sibling。parent指向父Fiber节点、child指向其子Fiber节点、sibling指向其兄弟节点。一个节点对应一个任务节点
实现Fiber
React-router-dom
//终端
//安装
yarn add react-router-dom
//引入
import {BrowserRouter} from "react-router-dom"
跳转姿势
-
跳转
import {USENavigate}
-
静态跳转
import {Link} from 'react-router-dom'
点我
import {Link} from 'react-router-dom'
点我
配置---"/root/:id"
useParams只能拿到动态路由中的参数
React Native
view、text、scrollview
React Native工作原理
CDN
小程序
一定要严格遵守开发规范要不然会出现兼容问题
为什么会出现小程序?
- 公众号,h5无法更便捷的调用native功能。
- 增加用户对微信的依赖/黏性。
小程序学习步骤
- 安装开发者工具
- 创建小程序
- 打开小程序IDE创建小程序
- 小程序的开发应该注意的点
- 目录结构即配置
- 目录结构即路由
小程序由每个页面作为基础
小程序的mvvm绑定
M=》V:定义data,直接打入到视图中
V=》M:定义回调函数,给视图绑定事件回调,在回调里使用this.setData更新数据
this.setData
像是react中类比this.setSatae
获取状态是使用this.data…
this.setData({},callback)
第一个参数是同步的
WXML进阶
小程序中没有h5的dom,小程序的模板视图由小程序提供的元组件构成。所以可用不使用根元素
以下组件具体使用要看官方文档
view
<view>view>
text
<text>123text>//文本组件--span
cover-view
WXSS进阶
-自带css模块化功能,其他组件的样式不会对当前页面的样式影响
-rpx作为单位,小程序默认给我们做了rem适配
rpx---->px
特殊属性
使用了vdom+diff的方式,因此列表渲染也要加key
使用wx:开头
wx:for="{{list}}",他已经帮我们定义好了item,index
组件通信
组件通信:
- 父传子:
- 父通过props给子组件传递数据, 子组件通过this.properties进行接受;
- 事件回调要通过给子组件绑定自定义事件,子组件视图绑定事件触发的回调中通过this. triggerEvent来触发该自定义事件。也可以直接给子组件绑定小程序原生事件,会被转发到子组件的容器根节点。
- 子传父:
- 父传子自定义事件,子的回调里通过this. triggerEvent的第二个参数来进行数据的传递,父通过event.detail进行接受。
- 状态提升, 子A传父, 父传子B。
- 远端通信
通过app.js中定义实例属性globalData,
在任意模块中通过getApp这个方法获取app实例,从而通过实例属性完成数据共享。
web3
其他
SPA(单页面应用)
路由:基于url来进行的条件渲染
路由原理
location.hash
使用hashChange事件监听函数里进行条件渲染
history
使用popstate事件,以及规定了push和replace方法
不能在文件协议中打开
不能刷新因为会像服务器请求这个路由所以会发生404 解决:在web服务器做一个重定向
MPA(多页面应用)
混合开发
用前端的语法写其他工作叫做混合开发
多端
即h5端、ios/andriod、小程序等
跨域
同源策略
终端碎片化
可视化建站
底带码、零代码,来创建网站,项目等
源
华为 --reigstry=https://mirrors.huaweicloud.com/repository/npm/
终端命令
pwd //查看绝对路径
cat 文件名 //查看文件详细
小程序
一定要严格遵守开发规范要不然会出现兼容问题
为什么会出现小程序?
- 公众号,h5无法更便捷的调用native功能。
- 增加用户对微信的依赖/黏性。
小程序学习步骤
- 安装开发者工具
- 创建小程序
- 打开小程序IDE创建小程序
- 小程序的开发应该注意的点
- 目录结构即配置
- 目录结构即路由
小程序由每个页面作为基础
小程序的mvvm绑定
M=》V:定义data,直接打入到视图中
V=》M:定义回调函数,给视图绑定事件回调,在回调里使用this.setData更新数据
this.setData
像是react中类比this.setSatae
获取状态是使用this.data…
this.setData({},callback)
第一个参数是同步的
WXML进阶
小程序中没有h5的dom,小程序的模板视图由小程序提供的元组件构成。所以可用不使用根元素
以下组件具体使用要看官方文档
view
<view>view>
text
<text>123text>//文本组件--span
cover-view
WXSS进阶
-自带css模块化功能,其他组件的样式不会对当前页面的样式影响
-rpx作为单位,小程序默认给我们做了rem适配
rpx---->px
特殊属性
使用了vdom+diff的方式,因此列表渲染也要加key
使用wx:开头
wx:for="{{list}}",他已经帮我们定义好了item,index
组件通信
组件通信:
- 父传子:
- 父通过props给子组件传递数据, 子组件通过this.properties进行接受;
- 事件回调要通过给子组件绑定自定义事件,子组件视图绑定事件触发的回调中通过this. triggerEvent来触发该自定义事件。也可以直接给子组件绑定小程序原生事件,会被转发到子组件的容器根节点。
- 子传父:
- 父传子自定义事件,子的回调里通过this. triggerEvent的第二个参数来进行数据的传递,父通过event.detail进行接受。
- 状态提升, 子A传父, 父传子B。
- 远端通信
通过app.js中定义实例属性globalData,
在任意模块中通过getApp这个方法获取app实例,从而通过实例属性完成数据共享。
web3
其他
SPA(单页面应用)
路由:基于url来进行的条件渲染
路由原理
location.hash
使用hashChange事件监听函数里进行条件渲染
history
使用popstate事件,以及规定了push和replace方法
不能在文件协议中打开
不能刷新因为会像服务器请求这个路由所以会发生404 解决:在web服务器做一个重定向
MPA(多页面应用)
混合开发
用前端的语法写其他工作叫做混合开发
多端
即h5端、ios/andriod、小程序等
跨域
同源策略
终端碎片化
可视化建站
底带码、零代码,来创建网站,项目等
源
华为 --reigstry=https://mirrors.huaweicloud.com/repository/npm/
终端命令
pwd //查看绝对路径
cat 文件名 //查看文件详细