react从入门到入坑
React
React学习内容
React学习版本: 16.x
React老版本项目: 15.x
react官网说 17.x 会使用的一些技术
React应用级脚手架
- CRA
- dva
- umi
create-react-app 【 CRA 】
React官网提供的脚手架
- 脚手架: 作用: 快速构建一个项目
全局安装create-react-app
$ npm install -g create-react-app
如果不想全局安装,可以直接使用npx
$ npx create-react-app your-app 也可以实现相同的效果
创建一个项目
$ create-react-app your-app 注意命名方式Creating a new React app in /dir/your-app.Installing packages. This might take a couple of minutes. 安装过程较慢,可以推荐使用yarn
Installing react, react-dom, and react-scripts...
这需要等待一段时间,这个过程实际上会安装三个东西
- react: react的顶级库
- react-dom: 因为react有很多的运行环境,比如app端的react-native, 我们要在web上运行就使用react-dom
- react-scripts: 包含运行和打包react应用程序的所有脚本及配置
出现下面的界面,表示创建项目成功:
Success! Created your-app at /dir/your-app
Inside that directory, you can run several commands:npm start // 开发环境下运行Starts the development server.npm run build // 生产环境打包Bundles the app into static files for production.npm test // 测试环境下运行Starts the test runner.npm run eject // 配置文件抽离// webpack配置放在了node_modules/react-scripts里面 Removes this tool and copies build dependencies, configuration filesand scripts into the app directory. If you do this, you can’t go back!We suggest that you begin by typing:cd your-appnpm startHappy hacking!
根据上面的提示,通过cd your-app
命令进入目录并运行npm start
即可运行项目。
生成项目的目录结构如下:
├── README.md 使用方法的文档
├── node_modules 所有的依赖安装的目录
├── yarn-lock.json 锁定安装时的包的版本号,保证团队的依赖能保证一致。
├── package.json 项目依赖配置记录文件 、 记录项目脚本命令
├── public 静态公共目录( 生产环境 ) 不会被webpack编译
|-- config 项目webpack配置文件
|-- scripts 项目wepback脚本命令执行文件
└── src 开发用的源代码目录- index.js 项目入口文件- index.css 项目全局样式- App.js 构建了一个App组件,是项目最大的组件 【 类似根组件 】- App.css 是App组件的样式文件- App.test.js 是App组件测试文件 - logo.svg 初始项目的界面logo- serverWorker 内部文件,我们不操作
常见问题:
-
npm安装失败
-
切换为npm镜像为淘宝镜像
-
使用yarn,如果本来使用yarn还要失败,还得把yarn的源切换到国内
yarn config set registry https://registry.npm.taobao.org // 配置yarn镜像源yarn config list // 查看yarn 镜像列表
-
如果还没有办法解决,请删除node_modules及yarn-lock.json然后重新执行
cnpm install命令
-
再不能解决就删除node_modules及yarn-lock.json的同时清除npm缓存
npm cache clean --force
之后再执行npm install
命令 -
环境变量问题
-
react-scripts 版本过高问题 ( 降低版本 react-script@2.1.8)
-
-
以上全不行,咋整?
- 将傍边可以安装的人的目录文件拷贝过来,注意不要拷贝node_modules
- 拷贝过来之后,记得cnpm i
- 将傍边可以安装的人的目录文件拷贝过来,注意不要拷贝node_modules
关于React
React部分的内容包含了所有授课的思路
React的起源和发展
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。
react是团队项目
react是第一个使用虚拟DOM前端框架
React与传统MVC的关系
轻量级的视图层库!A JavaScript library for building user interfaces
React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式;React 构建页面 UI 的库。可以简单地理解为,React 将将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,就成了我们的页面。
React最为重要的一个部分就是: 组件
React这个框架最初的目的是为了: 实现文件上传
React高性能的体现:虚拟DOM
#####React高性能的原理:
在Web开发中我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。
React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别【 patch 补丁对象 】,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A-B,B-A,React会认为A变成B,然后又从B变成A UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。
尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,因而对实际DOM进行操作的仅仅是Diff算法,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。
面试题: 如果React在一个事件内,连续修改数据,会如何?
而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并
Vue这边没有合并的
#####React Fiber:
在react 16之后发布的一种react 核心算法,React Fiber是对核心算法的一次重新实现(官网说法)。之前用的是diff算法。
在之前React中,更新过程是同步的,这可能会导致性能问题。
当React决定要加载或者更新组件树时,会做很多事,比如调用各个组件的生命周期函数,计算和比对Virtual DOM,最后更新DOM树,这整个过程是同步进行的,也就是说只要一个加载或者更新过程开始,中途不会中断。因为JavaScript单线程的特点,如果组件树很大的时候,每个同步任务耗时太长,就会出现卡顿。
React Fiber的方法其实很简单——分片。把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。
React的特点和优势
- 虚拟DOM
我们以前操作dom的方式是通过document.getElementById()的方式,这样的过程实际上是先去读取html的dom结构,将结构转换成变量,再进行操作
而reactjs定义了一套变量形式的dom模型,一切操作和换算直接在变量中,这样减少了操作真实dom,性能真实相当的高,和主流MVC框架有本质的区别,并不和dom打交道
- 组件系统
react最核心的思想是将页面中任何一个区域或者元素都可以看做一个组件 component
那么什么是组件呢?
组件指的就是同时包含了html、css、js、image元素的聚合体
使用react开发的核心就是将页面拆分成若干个组件,并且react一个组件中同时耦合了css、js、image,这种模式整个颠覆了过去的传统的方式
- 单向数据流
其实reactjs的核心内容就是数据绑定,所谓数据绑定指的是只要将一些服务端的数据和前端页面绑定好,开发者只关注实现业务就行了
- JSX 语法
在vue中,我们使用render函数来构建组件的dom结构性能较高,因为省去了查找和编译模板的过程,但是在render中利用createElement创建结构的时候代码可读性较低,较为复杂,此时可以利用jsx语法来在render中创建dom,解决这个问题,但是前提是需要使用工具来编译jsx
编写第一个react应用程序
react开发需要引入多个依赖文件:react.js、react-dom.js,分别又有开发版本和生产版本,create-react-app里已经帮我们把这些东西都安装好了。把通过CRA创建的工程目录下的src目录清空,然后在里面重新创建一个index.js. 写入以下代码:
// 从 react 的包当中引入了 React。只要你要写 React.js 组件就必须引入React, 因为react里有一种语法叫JSX,稍后会讲到JSX,要写JSX,就必须引入React
import React from 'react'
// ReactDOM 可以帮助我们把 React 组件渲染到页面上去,没有其它的作用了。它是从 react-dom 中引入的,而不是从 react 引入。
import ReactDOM from 'react-dom'// ReactDOM里有一个render方法,功能就是把组件渲染并且构造 DOM 树,然后插入到页面上某个特定的元素上
ReactDOM.render(
// 这里就比较奇怪了,它并不是一个字符串,看起来像是纯 HTML 代码写在 JavaScript 代码里面。语法错误吗?这并不是合法的 JavaScript 代码, “在 JavaScript 写的标签的”语法叫 JSX- JavaScript XML。<h1>欢迎进入React的世界</h1>,
// 渲染到哪里document.getElementById('root')
)
元素与组件
如果代码多了之后,不可能一直在render方法里写,所以就需要把里面的代码提出来,定义一个变量,像这样:
import React from 'react'
import ReactDOM from 'react-dom'
// 这里感觉又不习惯了?这是在用JSX定义一下react元素
const app = '<h1>欢迎进入React的世界</h1>'
ReactDOM.render(app,document.getElementById('root')
)
函数式组件( 无状态组件 PureComponent)
由于元素没有办法传递参数,所以我们就需要把之前定义的变量改为一个方法,让这个方法去return一个元素:
import React from 'react'
import ReactDOM from 'react-dom'// 特别注意这里的写法,如果要在JSX里写js表达式(只能是表达式,不能流程控制),就需要加 {},包括注释也是一样,并且可以多层嵌套
const app = (props) => <h1>欢迎进入{props.name}的世界</h1>ReactDOM.render(app({name: 'react'}),document.getElementById('root')
)
这里我们定义的方法实际上也是react定义组件的第一种方式-定义函数式组件,这也是无状态组件。但是这种写法不符合react的jsx的风格,更好的方式是使用以下方式进行改造
import React from 'react'
import ReactDOM from 'react-dom'const App = (props) => <h1>欢迎进入{props.name}的世界</h1>ReactDOM.render(// React组件的调用方式<App name="react" />,document.getElementById('root')
)
这样一个完整的函数式组件就定义好了。但要注意!注意!注意!组件名必须大写,否则报错。
class组件
ES6的加入让JavaScript直接支持使用class来定义一个类,react的第二种创建组件的方式就是使用的类的继承,ES6 class
是目前官方推荐的使用方式,它使用了ES6标准语法来构建,看以下代码:
import React from 'react'
import ReactDOM from 'react-dom'class App extends React.Component {render () {return (// 注意这里得用this.props.name, 必须用this.props<h1>欢迎进入{this.props.name}的世界</h1>)}
}
ReactDOM.render(<App name="react" />,document.getElementById('root')
)
运行结果和之前完全一样,因为JS里没有真正的class,这个class只是一个语法糖, 但二者的运行机制底层运行机制不一样。
-
函数式组件是直接调用, 在前面的代码里已经有看到
-
es6 class
组件其实就是一个构造器,每次使用组件都相当于在实例化组件,像这样:import React from 'react' import ReactDOM from 'react-dom'class App extends React.Component {render () {return (<h1>欢迎进入{this.props.name}的世界</h1>)} }const app = new App({name: 'react' }).render()ReactDOM.render(app,document.getElementById('root') )
更老的一种方法
在16以前的版本还支持这样创建组件, 但现在的项目基本上不用
// react 15.x
React.createClass({render () {return (<div>{this.props.xxx}</div>)}
})
组件的组合、嵌套 【 vue slot 】
总结:
- 组件的嵌套式指: 将子组件在父组件的jsx中以标签的形式使用
- 组件的组合是指:将一个组件写在另一个组件的内容中,然后在外层组件中通过 this.props.children来接收内容中的组件
- 但是我们推荐大家后面写的话可以使用嵌套
将一个组件渲染到某一个节点里的时候,会将这个节点里原有内容覆盖
组件嵌套的方式就是将子组件写入到父组件的模板中去,且react没有Vue中的内容分发机制(slot),所以我们在一个组件的模板中只能看到父子关系
// 从 react 的包当中引入了 React 和 React.js 的组件父类 Component
// 还引入了一个React.js里的一种特殊的组件 Fragment
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'class Title extends Component {render () {return (<h1>欢迎进入React的世界</h1>)}
}
class Content extends Component {render () {return (<p>React.js是一个构建UI的库</p>)}
}
/** 由于每个React组件只能有一个根节点,所以要渲染多个组件的时候,需要在最外层包一个容器,如果使用div, 会生成多余的一层dom
class App extends Component {render () {return (<div><Title /><Content /></div>)}
}
**/
// 如果不想生成多余的一层dom可以使用React提供的Fragment组件在最外层进行包裹
class App extends Component {render () {return (<Fragment><Title /><Content /></Fragment>)}
}
ReactDOM.render(<App/>,document.getElementById('root')
)
#JSX 原理 [ 了解 ]
要明白JSX的原理,需要先明白如何用 JavaScript 对象来表现一个 DOM 元素的结构?
看下面的DOM结构
<div class='app' id='appRoot'><h1 class='title'>欢迎进入React的世界</h1><p>React.js 是一个帮助你构建页面 UI 的库</p>
</div>
上面这个 HTML 所有的信息我们都可以用 JavaScript 对象来表示:
{tag: 'div',attrs: { className: 'app', id: 'appRoot'},children: [{tag: 'h1',attrs: { className: 'title' },children: ['欢迎进入React的世界']},{tag: 'p',attrs: null,children: ['React.js 是一个构建页面 UI 的库']}]
}
但是用 JavaScript 写起来太长了,结构看起来又不清晰,用 HTML 的方式写起来就方便很多了。
于是 React.js 就把 JavaScript 的语法扩展了一下,让 JavaScript 语言能够支持这种直接在 JavaScript 代码里面编写类似 HTML 标签结构的语法,这样写起来就方便很多了。编译的过程会把类似 HTML 的 JSX 结构转换成 JavaScript 的对象结构。
下面代码:
import React from 'react'
import ReactDOM from 'react-dom'class App extends React.Component {render () {return (<div className='app' id='appRoot'><h1 className='title'>欢迎进入React的世界</h1><p>React.js 是一个构建页面 UI 的库</p></div>)}
}ReactDOM.render(<App />,document.getElementById('root')
)
编译之后将得到这样的代码:
import React from 'react'
import ReactDOM from 'react-dom'class App extends React.Component {render () {return (React.createElement("div",{className: 'app',id: 'appRoot'},React.createElement("h1",{ className: 'title' },"欢迎进入React的世界"),React.createElement("p",null,"React.js 是一个构建页面 UI 的库")))}
}ReactDOM.render(React.createElement(App),document.getElementById('root')
)
React.createElement
会构建一个 JavaScript 对象来描述你 HTML 结构的信息,包括标签名、属性、还有子元素等, 语法为
React.createElement(type,[props],[...children]
)
所谓的 JSX 其实就是 JavaScript 对象,所以使用 React 和 JSX 的时候一定要经过编译的过程:
JSX —使用react构造组件,babel进行编译—> JavaScript对象 —
ReactDOM.render()
—>DOM元素 —>插入页面
#组件中DOM样式
一共有四种
- 行内样式
想给虚拟dom添加行内样式,需要使用表达式传入样式对象的方式来实现:
// 注意这里的两个括号,第一个表示我们在要JSX里插入JS了,第二个是对象的括号<p style={{color:'red', fontSize:'14px'}}>Hello world</p>
行内样式需要写入一个样式对象,而这个样式对象的位置可以放在很多地方,例如render
函数里、组件原型上、外链js文件中
- 使用
class
React推荐我们使用行内样式,因为React觉得每一个组件都是一个独立的整体
其实我们大多数情况下还是大量的在为元素添加类名,但是需要注意的是,class
需要写成className
(因为毕竟是在写类js代码,会收到js规则的限制,而class
是关键字)
<p className="hello" style = {this.style}>Hello world</p>
- 不同的条件添加不同的样式
有时候需要根据不同的条件添加不同的样式,比如:完成状态,完成是绿色,未完成是红色。那么这种情况下,我们推荐使用classname/classnames这个包:
- css-in-js ( 在js中写css )
styled-components
是针对React写的一套css-in-js框架,简单来讲就是在js中写css。npm链接
styled-components是一个第三方包,要安装
React认为一切皆组件,那么样式也应该是一个组件
#todoList
作用:
增加一条数据
删除一条数据
修改一条数据
组件化开发React todolist, 项目开发中的组件的基本目录结构基本上是这样的:
/your-project
- src
- …
- components
- YourComponentOne
- index.js/YourComponentOne.js
- YourComponentTwo
- index.js/YourComponentTwo.js
- index.js 用于导出组件
注意:一个组件只干一件事情 ,所以TodoList和TodoItem要做成两个组件,这样也方便于后期理解shouldComponentUpdate
组件的数据挂载方式
React中数据分为两个部分
1. 属性
2. 状态
经验: 频繁变化的就写成状态
Vue中数据只有状态这一种类型
属性(props)
props
是正常从外部传入的,组件内部也可以通过一些方式来初始化的设置,属性不能被组件自己更改,但是你可以通过父组件主动重新渲染的方式来传入新的 props
内部设置的属性是不去更改它的
属性是描述性质、特点的,组件自己不能随意更改。
之前的组件代码里面有props
的简单使用,总的来说,在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为组件 props
对象的键值。通过箭头函数创建的组件,需要通过函数的参数来接收props
:
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'class Title extends Component {render () {return (<h1>欢迎进入{this.props.name}的世界</h1>)}
}const Content = (props) => {return (<p>{props.name}是一个构建UI的库</p>)
}class App extends Component {render () {return (<Fragment><Title name="React" /><Content name="React.js" /></Fragment>)}
}ReactDOM.render(<App/>,document.getElementById('root')
)
###设置组件的默认props
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'class Title extends Component {// 使用类创建的组件,直接在这里写static方法,创建defaultPropsstatic defaultProps = {name: 'React'}render () {return (<h1>欢迎进入{this.props.name}的世界</h1>)}
}const Content = (props) => {return (<p>{props.name}是一个构建UI的库</p>)
}// 使用箭头函数创建的组件,需要在这个组件上直接写defaultProps属性
Content.defaultProps = {name: 'React.js'
}class App extends Component {render () {return (<Fragment>{/* 由于设置了defaultProps, 不传props也能正常运行,如果传递了就会覆盖defaultProps的值 */}<Title /><Content /></Fragment>)}
}ReactDOM.render(<App/>,document.getElementById('root')
)
props.children
我们知道使用组件的时候,可以嵌套。要在自定义组件的使用嵌套结构,就需要使用 props.children
。在实际的工作当中,我们几乎每天都需要用这种方式来编写组件。
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'class Title extends Component {render () {return (<h1>欢迎进入{this.props.children}的世界</h1>)}
}const Content = (props) => {return (<p>{props.children}</p>)
}class App extends Component {render () {return (<Fragment><Title>React</Title><Content><i>React.js</i>是一个构建UI的库</Content></Fragment>)}
}ReactDOM.render(<App/>,document.getElementById('root')
)
使用prop-types检查props [ 白银 ]
React其实是为了构建大型应用程序而生, 在一个大型应用中,根本不知道别人使用你写的组件的时候会传入什么样的参数,有可能会造成应用程序运行不了,但是不报错。为了解决这个问题,React提供了一种机制,让写组件的人可以给组件的props
设定参数检查,需要安装和使用prop-types:
$ npm i prop-types -S
以上方案现在也不咋地流行了
React + Ts 【 王者 】
##状态(state)
**状态就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,**使用状态的目的就是为了在不同的状态下使组件的显示不同(自己管理)
经验: 组件自己的状态只能自己更改
###定义state
第一种方式
import React, { Component } from 'react'
import ReactDOM from 'react-dom'class App extends Component {state = {name: 'React',isLiked: false}render () {return (<div><h1>欢迎来到{this.state.name}的世界</h1><button>{this.state.isLiked ? '❤️取消' : '🖤收藏'}</button></div>)}
}
ReactDOM.render(<App/>,document.getElementById('root')
)
另一种方式(推荐)
import React, { Component } from 'react'
import ReactDOM from 'react-dom'class App extends Component {constructor() {super()this.state = {name: 'React',isLiked: false}}render () {return (<div><h1>欢迎来到{this.state.name}的世界</h1><button>{this.state.isLiked ? '❤️取消' : '🖤收藏'}</button></div>)}
}
ReactDOM.render(<App/>,document.getElementById('root')
)
this.props
和this.state
是纯js对象,在vue中,data属性是利用Object.defineProperty
处理过的,更改data的数据的时候会触发数据的getter
和setter
,但是React中没有做这样的处理,如果直接更改的话,react是无法得知的,所以,需要使用特殊的更改状态的方法setState
。
###setState
isLiked
存放在实例的 state
对象当中,组件的 render
函数内,会根据组件的 state
的中的isLiked
不同显示“取消”或“收藏”内容。下面给 button
加上了点击的事件监听。
import React, { Component } from 'react'
import ReactDOM from 'react-dom'class App extends Component {constructor() {super()this.state = {name: 'React',isLiked: false}}handleBtnClick = () => {this.setState({isLiked: !this.state.isLiked})}render () {return (<div><h1>欢迎来到{this.state.name}的世界</h1><button onClick={this.handleBtnClick}>{this.state.isLiked ? '❤️取消' : '🖤收藏'}</button></div>)}
}
ReactDOM.render(<App/>,document.getElementById('root')
)
setState有两个参数( 面试题 王者)
第一个参数可以是对象,也可以是方法 【return一个对象】,我们把这个参数叫做updater
-
参数是对象
this.setState({isLiked: !this.state.isLiked })
-
参数是方法
this.setState((prevState, props) => {return {isLiked: !prevState.isLiked} })
注意的是这个方法接收两个参数,第一个是上一次的state, 第二个是props
setState
是异步的,所以想要获取到最新的state,没有办法获取,就有了第二个参数,这是一个可选的回调函数
this.setState((prevState, props) => {return {isLiked: !prevState.isLiked}
}, () => {console.log('回调里的',this.state.isLiked)
})
console.log('setState外部的',this.state.isLiked)
属性vs状态
相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
不同点:
- 属性能从父组件获取,状态不能
- 属性可以由父组件修改,状态不能
- 属性能在内部设置默认值,状态也可以
- 属性不在组件内部修改,状态要改 【 属性只能外部修改,内部不允许修改】
- 属性能设置子组件初始值,状态不可以
- 属性可以修改子组件的值,状态不可以
state
的主要作用是用于组件保存、控制、修改自己的可变状态。state
在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state
是一个局部的、只能被组件自身控制的数据源。state
中状态可以通过 this.setState
方法进行更新,setState
会导致组件的重新渲染。
**props
的主要作用是让使用该组件的父组件可以传入参数来配置该组件**。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props
,否则组件的 props
永远保持不变。
如果搞不清 state
和 props
的使用场景,记住一个简单的规则:尽量少地用 state
,多用 props
。
没有 state
的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。
无状态组件也就是函数式组件
有状态组件就是类组件
经验:
功能复杂,我们使用类组件
功能单一,我们使用函数式组件
组件props要想变,那么久外部修改
组件state要想变,那么组件内部自身通过setState修改
react性能优化一个方案: 就是多使用无状态组件( 函数式组件 )
##状态提升
如果有多个组件共享一个数据,把这个数据放到共同的父级组件中来管理
受控组件与非受控组件
React组件的数据渲染是否被调用 是通过 传递过来的props
完全控制,控制则为受控组件,否则非受控组件。
渲染数据
-
条件渲染
{condition ? '❤️取消' : '🖤收藏' }{ condition && '取消'||'收藏' }
-
列表渲染
// 数据
const people = [{id: 1,name: 'Leo',age: 35
}, {id: 2,name: 'XiaoMing',age: 16
}]
// 渲染列表
{people.map(person => {return (<dl key={person.id}><dt>{person.name}</dt><dd>age: {person.age}</dd></dl>)})
}
React的高效依赖于所谓的 Virtual-DOM,尽量不碰 DOM。对于列表元素来说会有一个问题:元素可能会在一个列表中改变位置。要实现这个操作,只需要交换一下 DOM 位置就行了,但是React并不知道其实我们只是改变了元素的位置,所以它会重新渲染后面两个元素(再执行 Virtual-DOM ),这样会大大增加 DOM 操作。但如果给每个元素加上唯一的标识,React 就可以知道这两个元素只是交换了位置,这个标识就是key
,这个 key
必须是每个元素唯一的标识
- dangerouslySetHTML
对于富文本创建的内容,后台拿到的数据是这样的:
content = "<p>React.js是一个构建UI的库</p>"
处于安全的原因,React当中所有表达式的内容会被转义,如果直接输入,标签会被当成文本。这时候就需要使用dangerouslySetHTML
属性,它允许我们动态设置innerHTML
import React, { Component } from 'react'
import ReactDOM from 'react-dom'class App extends Component {constructor() {super()this.state = {content : "<p>React.js是一个构建UI的库</p>"}}render () {return (<div// 注意这里是两个下下划线 __htmldangerouslySetInnerHTML={{__html: this.state.content}}/>)}
}
ReactDOM.render(<App/>,document.getElementById('root')
)
事件处理
绑定事件
采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick
, React里的事件是驼峰onClick
,React的事件并不是原生事件,而是合成事件。
事件handler的写法 【 王牌 || 王者】
- 直接在render里写行内的箭头函数(不推荐)
- 在组件内使用箭头函数定义一个方法(推荐)
- 直接在组件内定义一个非箭头函数的方法,然后在render里直接使用
onClick={this.handleClick.bind(this)}
(不推荐) - 直接在组件内定义一个非箭头函数的方法,然后在constructor里bind(this)(推荐)\
- 注意: 事件不能定义在函数式组件中
Event 对象
和普通浏览器一样,事件handler会被自动传入一个 event
对象,这个对象和普通的浏览器 event
对象所包含的方法和属性都基本一致。不同的是 React中的 event
对象并不是浏览器提供的,而是它自己内部所构建的。它同样具有event.stopPropagation
、event.preventDefault
这种常用的方法
事件对象中的值很多都是null,但是可以正常使用
事件的参数传递
- 在
render
里调用方法的地方外面包一层箭头函数 - 在
render
里通过this.handleEvent.bind(this, 参数)
这样的方式来传递 - 通过
event
传递 - 比较推荐的是做一个子组件, 在父组件中定义方法,通过
props
传递到子组件中,然后在子组件件通过this.props.method
来调用
##处理用户输入
import React, { Component } from 'react'
import ReactDOM from 'react-dom'class App extends Component {constructor() {super()this.state = {xing: '',ming: ''}}handleInputChange = (e) => {this.setState({[e.target.name]: e.target.value})}render () {const {xing,ming} = this.statereturn (<div><label><span>姓:</span><inputtype="text"name="xing"value={xing}onChange={this.handleInputChange}/></label><label><span>名:</span><inputtype="text"name="ming"value={ming}onChange={this.handleInputChange}/></label><p>欢迎您: {xing}{ming}</p></div>)}
}
ReactDOM.render(<App/>,document.getElementById('root')
)
ref绑定:
- 建议不要过量使用ref , 会导致性能的浪费
普通绑定
函数形式 - 推荐
<input type=“text” ref = { el => this.user = el }/>
组件的生命周期
React中组件有生命周期,也就是说也有很多钩子函数供我们使用, 组件的生命周期,我们会分为四个阶段,初始化、运行中、销毁、错误处理(16.3之后)
生命周期钩子函数一定不要写成箭头函数
React生命周期
- React 15.x 10个钩子函数
- 初始化
- 运行中
- 销毁
- React 16.x 10个钩子函数
- 初始化
- 运行中
- 销毁
- 错误处理
- React 17.x 还没有发布 【 未来版本 】 9个钩子函数
- 初始化
- 运行中
- 销毁
- 错误处理
React生命周期对比
-
React15 和 React 16对比
- React15没有错误处理阶段
- React15有两个钩子函数,React16版本是没有的
- 定义自定义属性 getDefaultProps () {}
- 定义自定义状态的 getInitalState () {}
- React16版本
- 定义自定义属性; static defaultProps= {}
- 定义自定义状态: constructor ( props ) { supser( props ) this.state = {}}
-
React16 vs React 17
-
React17去除了React16的几个钩子函数,新增了另外几个
-
去除的钩子有:
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
- componentDidCatch
-
新增的钩子有:
- static getDeivedStateFromProps
- getSnapshotBeforeUpdate
- getDerivedStateFromError
-
初始化
在组件初始化阶段会执行
1. constructor
2. static getDerivedStateFromProps() ---- 将来会使用,
3. componentWillMount() / UNSAFE_componentWillMount() 带有UNSAFE属于过时的钩子函数
- componentWillMount会在17版本之后弃用( 使用static getDerivedStateFromProps)
4. render()
5. componentDidMount()
初始化阶段总结:
数据请求 + 数据修改 : componentDidMount 【 推荐 】
第三方库: 实例化
面试题:
1. 父组件渲染两次,子组件渲染几次? 两次2. 为什么子组件渲染列表式需要添加判断?- 因为父组件是渲染两次的,子组件也一样两次,而我们是第二次获取数据的3. 父子组件的渲染顺序- 先父组件- constructor- componentWillMount- render
- 在子组件- constructor- componentWillMount- render- componentDidMount
- 最后父组件- componentDidMount
##更新阶段
props
或state
的改变可能会引起组件的更新,组件重新渲染的过程中会调用以下方法:
1. componentWillReceiveProps() / UNSAFE_componentWillReceiveProps()
2. static getDerivedStateFromProps()
3. shouldComponentUpdate() // react性能优化第二方案
4. componentWillUpdate() / UNSAFE_componentWillUpdate()
5. render()
6. getSnapshotBeforeUpdate() ---- 将来会使用
7. componentDidUpdate()
真实DOM操作【 第三方库实例化 】 : componentDidUpdate
项目中路由、某一属性的检测( 监听 ) : componentWillReceiveProps
##卸载阶段
- componentWillUnmount()
##错误处理
-
componentDidCatch() — 16.3版本之后才有的
如果发生错误,你可以通过调用
setState
使用componentDidCatch()
渲染降级 UI,但在未来的版本中将不推荐这样做。 可以使用静态getDerivedStateFromError()
来处理降级渲染。
问题:
当子组件报错时候,我们希望项目中的组件树不要崩溃,而是显示回退UI,但是我们刚才处理之后发现,回退UI闪退了之后又出现了报错,不是我们所希望的
##各生命周期详解
#####1.constructor(props)
React组件的构造函数在挂载之前被调用。在实现React.Component
构造函数时,需要先在添加其他内容前,调用super(props)
,用来将父组件传来的props
绑定到这个类中,使用this.props
将会得到。
官方建议不要在`constructor`引入任何具有副作用和订阅功能的代码,这些应当使用`componentDidMount()`。 `constructor`中应当做些初始化的动作,如:初始化`state`,将事件处理函数绑定到类实例上,但也不要使用`setState()`。如果没有必要初始化state或绑定方法,则不需要构造`constructor`,或者把这个组件换成纯函数写法。白话: 继承父组件外部传递过来的属性,然后绑定到 当前 组件的 props属性身上
this.props = props
定义状态
方法绑定 this
this.fn = this.fn.bind( this )
当然也可以利用props
初始化state
,在之后修改state
不会对props
造成任何修改,但仍然建议大家提升状态到父组件中,或使用redux
统一进行状态管理。
constructor(props) {super(props);this.state = {isLiked: props.isLiked};
}
#####2.static getDerivedStateFromProps(nextProps, prevState)
getDerivedStateFromProps
是react16.3之后新增,在组件实例化后,和接受新的props
后被调用。他必须返回一个对象来更新状态,或者返回null表示新的props不需要任何state的更新。
如果是由于父组件的props
更改,所带来的重新渲染,也会触发此方法。
调用steState()
不会触发getDerivedStateFromProps()
。
之前这里都是使用constructor
+componentWillRecieveProps
完成相同的功能的
#####3. componentWillMount() / UNSAFE_componentWillMount()
UNSAFE前缀表示当前这个api已经过时了
componentWillMount()
将在React未来版本(官方说法 17.0)中被弃用。
为了避免副作用和其他的订阅,官方都建议使用componentDidMount()
代替。这个方法是用于在服务器渲染上的唯一方法。这个方法因为是在渲染之前被调用,也是惟一一个可以直接同步修改state的地方。
#####4.render()
render()方法是必需的。当他被调用时,他将计算this.props
和this.state
,并返回以下一种类型:
- React元素。通过jsx创建,既可以是dom元素,也可以是用户自定义的组件。
- 字符串或数字。他们将会以文本节点形式渲染到dom中。
- Portals【'portl】。react 16版本中提出的新的解决方案,可以使组件脱离父组件层级直接挂载在DOM树的任何位置。
4. null,什么也不渲染 - 布尔值。也是什么都不渲染。
当返回null
,false
,ReactDOM.findDOMNode(this)
将会返回null,什么都不会渲染。
render()
方法必须是一个纯函数,他不应该改变state
,也不能直接和浏览器进行交互,应该将事件放在其他生命周期函数中。
如果shouldComponentUpdate()
返回false
,render()
不会被调用。
#####5. componentDidMount
componentDidMount
在组件被装配后立即调用。初始化使得DOM节点应该进行到这里。
通常在这里进行ajax请求
如果要初始化第三方的dom库,也在这里进行初始化。只有到这里才能获取到真实的dom.
#####6.componentWillReceiveProps()/UNSAFE_componentWillReceiveProps( nextProps )
官方建议使用getDerivedStateFromProps
函数代替componentWillReceiveProps
。当组件挂载后,接收到新的props
后会被调用。如果需要更新state
来响应props
的更改,则可以进行this.props
和nextProps
的比较,并在此方法中使用this.setState()
。
如果父组件会让这个组件重新渲染,即使props
没有改变,也会调用这个方法。
React不会在组件初始化props时调用这个方法。调用this.setState
也不会触发。
#####7.shouldComponentUpdate(nextProps, nextState)
调用shouldComponentUpdate
使React知道,组件的输出是否受state
和props
的影响。默认每个状态的更改都会重新渲染,大多数情况下应该保持这个默认行为。
在渲染新的props
或state
前,shouldComponentUpdate
会被调用。默认为true
。这个方法不会在初始化时被调用,也不会在forceUpdate()
时被调用。返回false
不会阻止子组件在state
更改时重新渲染。
如果shouldComponentUpdate()
返回false
,componentWillUpdate
,render
和componentDidUpdate
不会被调用。
官方并不建议在
shouldComponentUpdate()
中进行深度查询或使用JSON.stringify()
,他效率非常低,并且损伤性能。
#####8.componentWillUpdate/UNSAFE_componentWillUpdate(nextProps, nextState)
在渲染新的state
或props
时,UNSAFE_componentWillUpdate
会被调用,将此作为在更新发生之前进行准备的机会。这个方法不会在初始化时被调用。
不能在这里使用this.setState(),也不能做会触发视图更新的操作。如果需要更新state
或props
,调用getDerivedStateFromProps
。
#####9.getSnapshotBeforeUpdate()
在react render()
后的输出被渲染到DOM之前被调用。它使您的组件能够在它们被潜在更改之前捕获当前值(如滚动位置)。这个生命周期返回的任何值都将作为参数传递给componentDidUpdate()。
#####10.componentDidUpdate(prevProps, prevState, snapshot)
在更新发生后立即调用componentDidUpdate()
。此方法不用于初始渲染。当组件更新时,将此作为一个机会来操作DOM。只要您将当前的props与以前的props进行比较(例如,如果props没有改变,则可能不需要网络请求),这也是做网络请求的好地方。
如果组件实现getSnapshotBeforeUpdate()
生命周期,则它返回的值将作为第三个“快照”参数传递给componentDidUpdate()
。否则,这个参数是undefined
。
#####11.componentWillUnmount()
在组件被卸载并销毁之前立即被调用。在此方法中执行任何必要的清理,例如使定时器无效,取消网络请求或清理在componentDidMount
中创建的任何监听。
#####12.componentDidCatch(error, info)
错误边界是React组件,可以在其子组件树中的任何位置捕获JavaScript错误,记录这些错误并显示回退UI,而不是崩溃的组件树。错误边界在渲染期间,生命周期方法以及整个树下的构造函数中捕获错误。
如果类组件定义了此生命周期方法,则它将成错误边界。在它中调用setState()
可以让你在下面的树中捕获未处理的JavaScript错误,并显示一个后备UI。只能使用错误边界从意外异常中恢复; 不要试图将它们用于控制流程。
错误边界只会捕获树中下面组件中的错误。错误边界本身不能捕获错误。
##PureComponent
PureComponnet
里如果接收到的新属性或者是更改后的状态和原属性、原状态相同的话,就不会去重新render了
在里面也可以使用shouldComponentUpdate
,而且。是否重新渲染以shouldComponentUpdate
的返回值为最终的决定因素。
import React, { PureComponent } from 'react'class YourComponent extends PureComponent {……
}
##ref
React提供的这个ref
属性,表示为对组件真正实例的引用,其实就是ReactDOM.render()
返回的组件实例,ref
可以挂载到组件上也可以是dom元素上。
- 挂到组件(
class
声明的组件)上的ref表示对组件实例的引用。不能在函数式组件上使用 ref 属性,因为它们没有实例: - 挂载到dom元素上时表示具体的dom元素节点。
在React 最新的版本中,要使用ref
, 需要使用React.createRef
方法先生成一个ref
。
import React, { Component, createRef } from 'react'
import ReactDOM from 'react-dom'class App extends Component {constructor() {super()// 创建inputRefthis.inputRef=createRef()}componentDidMount () {console.log(this.inputRef.current) // <input type="text">}render () {return (<div>{/* 关联ref和dom */}<input type="text" ref={this.inputRef} /></div>)}
}
ReactDOM.render(<App/>,document.getElementById('root')
)
React Hooks
React Hooks 是 React 16.7.0-alpha
版本推出的新特性, 有了React Hooks,在 react 函数组件中,也可以使用类组件(classes components)的 state 和 组件生命周期。通过下面几个例子来学习React Hooks。
解决;
解决函数式组件【 无状态组件 】 中无法使用 state 和生命周期问题
- State Hook
// useState是react包提供的一个方法
import React, { useState } from "react";
import ReactDOM from "react-dom";const Counter = () => {// useState 这个方法可以为我们的函数组件拥有自己的state,它接收一个用于初始 state 的值,返回一对变量。这里我们把计数器的初始值设置为0, 方法都是以set开始const [count, setCount] = useState(0);return (<div><p>你点击了{count}次</p><button onClick={() => setCount(count + 1)}>点击</button></div>);
};const rootElement = document.getElementById("root");ReactDOM.render(<Counter />, rootElement);
- Effect Hook
// useState是react包提供的一个方法
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";const Counter = () => {// useState 这个方法可以为我们的函数组件拥有自己的state,它接收一个用于初始 state 的值,返回一对变量。这里我们把计数器的初始值设置为0, 方法都是以set开始const [count, setCount] = useState(0);// 类似于componentDidMount或者componentDidUpdate:useEffect(() => {// 更改网页的标题,还可以做其它的监听document.title = `你点击了${count}次`;});return (<div><p>你点击了{count}次</p><button onClick={() => setCount(count + 1)}>点击</button></div>);
};const rootElement = document.getElementById("root");ReactDOM.render(<Counter />, rootElement);
- React Hooks 的规则
- 只能在顶层调用Hooks。不要在循环,条件或嵌套函数中调用Hook。
- 不要从常规JavaScript函数中调用Hook。只在React函数式组件调用Hooks。
- 自定义hooks可以选择讲解
- react 内置hooks api
- Basic Hooks
useState
useEffect
useContext
- Additional Hooks
useReducer
useCallback
useMemo
useRef
useImperativeHandle
useLayoutEffect
useDebugValue
- Basic Hooks
组件通信
分类:
父子组件通信
无论父组件传递是props还是state,子组件都是通过props接收
子父组件通信
父组件传递方法给子组件,子组件调用父组件传递过来的方法
注意: 自己的状态自己更改
非父子组件通信
ref链
1. ref = ‘xxx’ this.refs.xxx
2. ref = { el => this.xxx = el } this.xxx 【 推荐 】
跨组件通信
context
使用流程
- 创建上下文 React.createContext()
- 使用上下文包裹目标组件的父组件
<MoneyContext.Provider value = { money }><Father></Father></MoneyContext.Provider>
- 在目标组件中先定义一个静态属性 static contextType = MoneyContext
- 通过 this.context来使用数据
多组件状态共享
Flux
redux
mobx 【 阿里 】
父组件与子组件通信
-
父组件将自己的状态传递给子组件,子组件当做属性来接收,当父组件更改自己状态的时候,子组件接收到的属性就会发生改变
-
父组件利用
ref
对子组件做标记,通过调用子组件的方法以更改子组件的状态,也可以调用子组件的方法…
子组件与父组件通信
- 父组件将自己的某个方法传递给子组件,在方法里可以做任意操作,比如可以更改状态,子组件通过
this.props
接收到父组件的方法后调用。
跨组件通信
在react没有类似vue中的事件总线来解决这个问题,我们只能借助它们共同的父级组件来实现,将非父子关系装换成多维度的父子关系。react提供了context
api来实现跨组件通信, React 16.3之后的context
api较之前的好用。
实例,使用context
实现购物车中的加减功能
// counterContext.js
import React, { Component, createContext } from 'react'const {Provider,Consumer: CountConsumer
} = createContext()class CountProvider extends Component {constructor () {super()this.state = {count: 1}}increaseCount = () => {this.setState({count: this.state.count + 1})}decreaseCount = () => {this.setState({count: this.state.count - 1})}render() {return (<Provider value={{count: this.state.count,increaseCount: this.increaseCount,decreaseCount: this.decreaseCount}}>{this.props.children}</Provider>)}
}export {CountProvider,CountConsumer
}
// 定义CountButton组件
const CountButton = (props) => {return (<CountConsumer>// consumer的children必须是一个方法{({ increaseCount, decreaseCount }) => {const { type } = propsconst handleClick = type === 'increase' ? increaseCount : decreaseCountconst btnText = type === 'increase' ? '+' : '-'return <button onClick={handleClick}>{btnText}</button>}}</CountConsumer>)
}
// 定义count组件,用于显示数量
const Count = (prop) => {return (<CountConsumer>{({ count }) => {return <span>{count}</span>}}</CountConsumer>)
}
// 组合
class App extends Component {render () {return (<CountProvider><CountButton type='decrease' /><Count /><CountButton type='increase' /></CountProvider>)}
}
复杂的非父子组件通信在react中很难处理,多组件间的数据共享也不好处理,在实际的工作中我们会使用flux、redux、mobx来实现
HOC(高阶组件) - 必会
Higher-Order Components就是一个函数,传给它一个组件,它返回一个新的组件。
功能:
1. 进行某些方法或是属性的复用
2. 让外层的组件替我们完成任务,那么里层组件直接使用就可以了
const NewComponent = higherOrderComponent(YourComponent)
比如,我们想要我们的组件通过自动注入一个版权信息。
// withCopyright.js 定义一个高阶组件
import React, { Component, Fragment } from 'react'const withCopyright = (WrappedComponent) => {return class NewComponent extends Component {render() {return (<Fragment><WrappedComponent /><div>©版权所有</div></Fragment>)}}
}
export default withCopyright
// 使用方式
import withCopyright from './withCopyright'class App extends Component {render () {return (<div><h1>Awesome React</h1><p>React.js是一个构建用户界面的库</p></div>)}
}
const CopyrightApp = withCopyright(App)
这样只要我们有需要用到版权信息的组件,都可以直接使用withCopyright这个高阶组件包裹即可。
在这里要讲解在CRA 中配置装饰器模式的支持。
React引入组件库
- 组件库
- 蚂蚁金服开发的组件库,ant design
#状态管理
##传统MVC框架的缺陷
什么是MVC?
MVC
的全名是Model View Controller
,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。
V
即View视图是指用户看到并与之交互的界面。
M
即Model模型是管理数据 ,很多业务逻辑都在模型中完成。在MVC的三个部件中,模型拥有最多的处理任务。
C
即Controller控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
MVC只是看起来很美
MVC框架的数据流很理想,请求先到Controller, 由Controller调用Model中的数据交给View进行渲染,但是在实际的项目中,又是允许Model和View直接通信的。然后就出现了这样的结果:
MVC缺陷
1. controller view的任务过轻,而主要的业务处理都压在Model身上
2. Controller 和 view之间没有任何的联系
facebook 开发团队就看不上MVC ,所以导致React没有采用MVC架构思想,React单纯只能看做是MVC中V,
作为一个完整的软件架构,那么需要包含另外两个部分,所以React需要补全这两个部分,在React发布的同时,facebook团队发布了一个软件架构思维: Flux
这Flux软件架构思维中,React只是其中一部分,充当V
后期Flux发展改进,诞生了一个新的架构: Redux
Redux也存在不便利性,为了弥补Redux的缺陷,开发了mobx
前端几个核心框架出现的时间
1. Backbone.js MVP 2010.10
2. Angular.js ( 1.0 版本) MVC ->MVVM 2010.10
3. React 可以认为是MVC中的V 2013
4. Vue 1.0 MVVM 2014
5. Vue 2.0 MVVM 2016/9
6. Angular.ts( 2.0 版本) 2016年
7. 小程序 20.16
8. es6 2016
Flux Redux 都是和MVC MVP MVVM 一样是架构层级
错误的说法: Flux Redux是React的状态管理工具
正确说法: Flux Redux是一种架构思维,是可以进行状态管理的一个工具,也是一个集中式的存储仓库
##Flux
在2013年,Facebook让React
亮相的同时推出了Flux框架,React
的初衷实际上是用来替代jQuery
的,Flux
实际上就可以用来替代Backbone.js
,Ember.js
等一系列MVC
架构的前端JS框架。
其实Flux
在React
里的应用就类似于Vue
中的Vuex
的作用,但是在Vue
中,Vue
是完整的mvvm
框架,而Vuex
只是一个全局的插件。
React
只是一个MVC中的V(视图层),只管页面中的渲染,一旦有数据管理的时候,React
本身的能力就不足以支撑复杂组件结构的项目,在传统的MVC
中,就需要用到Model和Controller。Facebook对于当时世面上的MVC
框架并不满意,于是就有了Flux
, 但Flux
并不是一个MVC
框架,他是一种新的思想( 新的架构思想 )。
Flux 组成部分
- View: 视图层 ( 用React的组件来代替 )
- ActionCreators(动作创造者):视图层发出的消息(比如mouseClick)
- Dispatcher(派发器):用来接收Actions、执行回调函数
- Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面
Flux的流程:
- 组件获取到store中保存的数据挂载在自己的状态上
- 用户产生了操作,调用actions的方法
- actions接收到了用户的操作,进行一系列的逻辑代码、异步操作
- 然后actions会创建出对应的action,action带有标识性的属性
- actions调用dispatcher的dispatch方法将action传递给dispatcher
- dispatcher接收到action并根据标识信息判断之后,调用store的更改数据的方法
- store的方法被调用后,更改状态,并触发自己的某一个事件
- store更改状态后事件被触发,该事件的处理程序会通知view去获取最新的数据
Flux流程
- 要想使用FLux架构思维,需要通过一个工具进行使用, 这个工具就是flux
- 安装 flux
$ yarn add flux
- 在src目录下 新建store目录,里面新建index.js
- store有两个功能
- 存储数据
- 当数据发生改变时,视图要进行更新 ( 当前组件中的state发生了改变,从新从store中获取数据,要想重新复制,那么要通过事件的发布,订阅 )
- store有两个功能
// store/index.jsconst EventEmitter = require( 'events' ).EventEmitterconst store = {...EventEmitter.prototype,state: {count: 0},getState () {return this.state}}export default store
-
将store中的数据显示在组件(视图)中
import store from './store'class xxx extends React.Component{constructor () {super()this.state = {count: store.getState().count}}render () {return (<div><p> { this.state.count } </p></div>)}}
-
用户操作,用户点击按钮,执行当前组件中的方法,这个方法的逻辑实际上是actionCreators中的方法
-
创建actionCreators.js
- actions的发送要通过dispatcher来发送
import * as type from './type'import dispatcher from './dispatcher';const actionCreators = {increment () {// 创建动作let actions = {type: type.INCRMENT}// dispatcher来通过dispatch 发送actionsdispatcher.dispatch( actions )}}export default actionCreators
-
创建dispatcher.js
import { Dispatcher } from 'flux';import * as type from './type'import state from './state'const dispatcher = new Dispatcher()// dispatcher.register( callback )dispatcher.register( ( actions) => {switch ( actions.type ) {case type.INCRMENT:// 用户操作了state.count++break;default:break;}})export default dispatcher
-
通过store的事件的发布和订阅进行 当前组件中 state 的重新赋值
- 当我们点击按钮是,要通过store的事件的订阅给当前组件的state重新赋值,要想这样做,我们必须进行事件的发布
- 难点: 这个事件的发布往哪里写?
- 组件的生命周期中,数据可以进行一次修改的可以往 componentWillMount // componentDidMount
- 难点: 这个事件的订阅那里写?
- 当我们点击按钮的时候,就要修改当前组件的state,也就是要进行事件的订阅
import React from 'react';import logo from './logo.svg';import './App.css';import store from './store'import actionCreators from './store/actionCreators';class App extends React.Component {constructor () {super()this.state = {count: store.getState().count}}increment () {actionCreators.increment()store.emit('count')}componentDidMount () {store.on('count', () => {this.setState({count: store.getState().count})})}render () {return (<div><h3> flux </h3><button onClick = { this.increment }> + </button><p> count: { this.state.count } </p></div>)}}export default App;
##Redux
- 使用方式
- 数据不分块使用
- 数据分块使用 【 推荐 】
- 方便我们将来的维护和更新
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cGzAiPnd-1592989413788)(E:\1902\03-React.js\note\images\redux.jpg)]
核心组成部分
Store 数据的管理者和数据的存储者
actionCreators 动作的创建者,发送动作给 reducers
react Components 组件( 用来充当视图层 )
reducers 数据的修改者,返回一个新的 newstate 给store
React 只是 DOM 的一个抽象层,并不是 Web 应用的完整解决方案。有两个方面,它没涉及。
- 代码结构
- 组件之间的通信
2013年 Facebook 提出了 Flux 架构的思想,引发了很多的实现。2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。
如果你不知道是否需要 Redux,那就是不需要它
只有遇到 React 实在解决不了的问题,你才需要 Redux
简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。
- 用户的使用方式非常简单
- 用户之间没有协作
- 不需要与服务器大量交互,也没有使用 WebSocket
- 视图层(View)只从单一来源获取数据
需要使用Redux的项目:
- 用户的使用方式复杂 【 电商类型 后台管理系统 】
- 不同身份的用户有不同的使用方式(比如普通用户和管理员) 【 权限验证 】
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了WebSocket
- View要从多个来源获取数据
从组件层面考虑,什么样子的需要Redux:
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
Redux的设计思想:
- Web 应用是一个状态机,视图与状态是一一对应的。
- 所有的状态,保存在一个对象里面(唯一数据源)。
注意:flux、redux都不是必须和react搭配使用的,因为flux和redux是完整的架构,在学习react的时候,只是将react的组件作为redux中的视图层去使用了。
Redux的使用的三大原则:
- Single Source of Truth(唯一的数据源)
- State is read-only(状态是只读的)
- Changes are made with pure function(数据的改变必须通过纯函数完成)
自己实现Redux
-
redux是一个架构思维,我们实现需要一个工具,这个工具叫做redux
-
安装redux
$ yarn add redux
-
在src下新建一个store,store中新建index.js用来打造store
import { createStore } from 'redux'import reducer from './reducer'const store = createStore( reducer ) // 不加new createStore() 参数不是一个 Object 而是一个Functionexport default store
-
在store下新建一个state
const state = {todos: [{id: 1,task: '任务一'}]}export default state
-
在 store下新建一个 reducer
import state from './state'const reducer = ( previousState = state , action ) => {const newState = {...previousState // 解构的原因是为了做深拷贝,我们操作newState,不会影响state}return newState}export default reducer
- 在你想要使用的组件中直接引用 store
import React, { Component,Fragment } from 'react'import store from '../store'class Content extends Component{constructor () {super()this.state = {todos: store.getState()}}render () {return (<Fragment><div><ul><li> 1 </li></ul></div></Fragment>)}}export default Content
- 进行用户交互 React component — > actionCreators
- 在store下新建 actionCreators.js
import * as type from './type' import store from './index'const actionCreators = {add_todos_item ( val ) {//动作的创建const action = {type: type.ADD_TODOS_ITEM,payload: val // 负载数据}// 动作的发送store.dispatch( action )} }
export default actionCreators
10. 在Button组件中触发 actionCreators中 的方法```javascriptimport React, { Component,Fragment } from 'react'import actionCreators from './../store/actionCreators';class Button extends Component{add = () => {let val = this.input.valueactionCreators.add_todos_item( val )this.input.value = ''}render () {return (<Fragment><div><input type = "text" ref = { el => this.input = el } /><br/><button onClick = { this.add }> + </button></div></Fragment>)}}export default Button
- 在 reducer中修改数据
import state from './state'// const state = require( './state' )import * as type from './type'const reducer = ( previousState = state,action) => {let newState = {...previousState}//判断用户进行了那个用户交互 ,操作新状态switch ( action.type ) {case type.ADD_TODOS_ITEM://修改新状态newState.todos.push({id: newState.todos.length + 1,task: action.payload})break;default:break;}return newState}export default reducer
- 进行数据个更新,通过store的订阅功能进行更新,也就是组件需要重新赋值一次数据
- 在Content组件中进行订阅
componentDidMount () {store.subscribe( () => {this.setState({todos: store.getState().todos})})}
------------- redux 基础 – end ----------------------------------
------------- redux 进阶 – start ----------------------------------
redux – reducer – 划分
-
分析
一个项目:
- banenr
- home
- mine
- login
- register
- detail
- shopcar
- 会员
- 普通用户数据 -
解决: 希望的一个类型数据一个模块 ---- 》 reducer划分 combineReducers
-
分析: 我们希望我们的store下面每一个文件夹就是一个 类型 的数据包
-
解决: redux combineReducers
每一个数据包的目录结构- store
- home
- state.js
- type.js
- reducer.js
- actionCreator.js
- 我们需要一个统一的redcuer的管理者
- home
/* 这里的reducer才是真正的统一管理者*/import {combineReducers} from 'redux'import todolist from './todolist/reducer'const reducer = combineReducers({todolist})export default reducer
使用的时候
store.getState().todolist.todos
- store
redux - reducer 划分代码优化
- 问题: 我们在更新视图时,在组件中每次都要进行 store 的订阅,代码是冗余的
- 问题: 我们在触发actionCreators中的方法时,触发形式的书写也很相似
- 问题: 后端数据交互往哪里写? actionCreators中写,actionCreators不是用来创建动作的么?
所以,我们引入了 react-redux 工具来处理这些问题
综上: 我们通过 几个工具来解决
Provider bindActionCreators
react-redux只是一个辅助工具,只是将react和redux进行更好的连接的桥梁工具
核心概念
容器组件(智能组件)、UI组件(木偶组件)
react-redux觉得如果一个组件想要使用到store中的数据或者是actionCreator的方法,我们都应该将其变化为容器组件包裹UI组件的样子
其中,容器组件负责连接store,将状态、方法传递给UI组件,UI组件从属性上取得这些api后进行使用
而且,不需要担心是容器组件可以根据UI组件来生成
核心API
Provider 、 connect(mapStateToProps,mapDistpatchToProps)
Provider负责将store相关的api,传入到内部所有的容器组件中
connect负责根据UI组件来生成容器组件
使用方法与细节
-
需要安装react-redux工具
-
需要在组件的最外层套上Provider组件,并为其传入store
-
利用connect将需要使用store相关api的组件变成容器组件嵌套UI组件的模式
connect方法的返回值是一个函数,这个函数接收到UI组件之后会返回一个容器组件,容器内部已经嵌套了UI组件
Provider组件会利用context上下文将自己属性中store传递给自己的子级组件,而容器组件会取得context上面的store相关的api
我们可以在connect函数中传入mapStateToProps/mapDispatchToProps参数来掌控容器组件给UI组件传递属性的过程
mapStateToProps的作用:
将store中的state传递到UI组件的属性上
值为一个函数,接收到的就是store中的state
返回值是什么,UI组件的属性上就有什么
并且,因为容器组件中已经做好了store.subscribe的处理,所以一旦store中的状态变化,容器组件就马上能得知,就会重新给UI组件传入新的数据
问题: 坑 ? 我们的store中的数据在更新, 但是里面的容器组件中的 props 不更新分析: 先从组件上着手,查看我们书写的代码, 发现代码没有问题的, 这个时候我们就要去查看数据来源, 而数据来源来自于 reducer , 然后输出一下数据, 发现数据渲染了三次 , 有两次是一个组件在创建的时候, 数据从无到有, 还有一次是容器组件产生的 , 后面我们发送我们的new_state一直在重复初始化, 解决方案: let new_state = {...previousState}mapDispatchToProps的作用:可以将能使用到dispatch的一些方法传递到UI组件上
值为一个函数,接收到的就是store中的dispatch
返回什么,UI组件的属性上就有什么
这个时候actionCreator就变得很纯粹,只需要创建action,dispatch action的动作可以放到mapDispatchToProps的方法中,也就说,在mapDispatchToProps给UI组件传递的函数中将actionCreator创造好的action给dispatch走但是这样的写法优点不舒服,因为actionCreator里有一个方法,还需要在mapDispatchToProps里再写一个方法所以可以利用redux中的bindActionCreators将actionCreator中的方法直接放入到UI组件中并且将其返回的action给直接dispatch走
因为在组件中既要用到store中的状态,也要用到actionCreator的方法,导致这个组件引入了很多东西,其实我们可以将这些逻辑封装取出group-todolist.js:import actionCreator from '../store/todolist/actionCreator'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'let GroupTodolist = connect(state => state.todolist, dispatch => {return bindActionCreators(actionCreator, dispatch)
})export default GroupTodolist
TodoContent:import GroupTodolist from '../../modules/group-todolist'....export default GroupTodolist(TodoContent)
redux-thunk 工具
我们上面的使用方法很简单,也很舒服
但是有一点小问题,如果我们有了异步操作,比如我们会先执行一个ajax调用之后再去更改状态的话,这个异步动作,没有地方放了
- 我们不能把异步动作放到组件中,因为UI组件只负责使用数据,如果有其他的异步代码,让UI组件组件不纯粹
- 理论上来说放到actionCreator的方法中最合适,但是,因为actionCreator目前只是专注于创建action和返回action,无法放入异步操作
所以,我们需要用到redux的中间件工具:redux-thunk、redux-promise、redux-saga…
在这里我们研究一下redux-thunk
它的使用方法及其简单:
-
安装redux-thunk
-
在创建store的时候使用中间件
import { createStore, applyMiddleware } from ‘redux’
import thunk from ‘redux-thunk’
import reducer from ‘./reducer’
const store = createStore(reducer, applyMiddleware(thunk) )
-
这个时候,actionCreator的方法就可以返回一个能接收到dispatch的一个函数,我们可以在这个函数中进行异步操作之后,将actionCreator创建好的action给发送
------------- redux 进阶 – end ----------------------------------
###使用Redux框架
Redux的流程:
1.store通过reducer创建了初始状态
2.view通过store.getState()获取到了store中保存的state挂载在了自己的状态上
3.用户产生了操作,调用了actions 的方法
4.actions的方法被调用,创建了带有标示性信息的action
5.actions将action通过调用store.dispatch方法发送到了reducer中
6.reducer接收到action并根据标识信息判断之后返回了新的state
7.store的state被reducer更改为新state的时候,store.subscribe方法里的回调函数会执行,此时就可以通知view去重新获取state
Reducer必须是一个纯函数:
Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。Reducer不是只有Redux里才有,之前学的数组方法reduce
, 它的第一个参数就是一个reducer
纯函数是函数式编程的概念,必须遵守以下一些约束。
-
不得改写参数
-
不能调用系统 I/O 的API
-
不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
由于 Reducer 是纯函数,就可以保证同样的State,必定得到同样的 View。但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象,请参考下面的写法。
// State 是一个对象
function reducer(state = defaultState, action) {return Object.assign({}, state, { thingToChange });// 或者return { ...state, ...newState };
}// State 是一个数组
function reducer(state = defaultState, action) {return [...state, newItem];
}
最好把 State 对象设成只读。要得到新的 State,唯一办法就是生成一个新对象。这样的好处是,任何时候,与某个 View 对应的 State 总是一个不变(immutable)的对象。
我们可以通过在createStore中传入第二个参数来设置默认的state,但是这种形式只适合于只有一个reducer的时候。
划分reducer:
因为一个应用中只能有一个大的state,这样的话reducer中的代码将会特别特别的多,那么就可以使用combineReducers方法将已经分开的reducer合并到一起
注意:
- 分离reducer的时候,每一个reducer维护的状态都应该不同
- 通过store.getState获取到的数据也是会按照reducers去划分的
- 划分多个reducer的时候,默认状态只能创建在reducer中,因为划分reducer的目的,就是为了让每一个reducer都去独立管理一部分状态
最开始一般基于计数器的例子讲解redux的基本使用即可。
关于action/reducer/store的更多概念,请查看官网
Redux异步
通常情况下,action只是一个对象,不能包含异步操作,这导致了很多创建action的逻辑只能写在组件中,代码量较多也不便于复用,同时对该部分代码测试的时候也比较困难,组件的业务逻辑也不清晰,使用中间件了之后,可以通过actionCreator异步编写action,这样代码就会拆分到actionCreator中,可维护性大大提高,可以方便于测试、复用,同时actionCreator还集成了异步操作中不同的action派发机制,减少编码过程中的代码量
常见的异步库:
- Redux-thunk(就讲这个)
- Redux-saga
- Redux-effects
- Redux-side-effects
- Redux-loop
- Redux-observable
- …
基于Promise的异步库:
- Redux-promise
- Redux-promises
- Redux-simple-promise
- Redux-promise-middleware
- …
###容器组件(Smart/Container Components)和展示组件(Dumb/Presentational Components)
展示组件 | 容器组件 | |
---|---|---|
作用 | 描述如何展现(骨架、样式) | 描述如何运行(数据获取、状态更新) |
直接使用 Redux | 否 | 是 |
数据来源 | props | 监听 Redux state |
数据修改 | 从 props 调用回调函数 | 向 Redux 派发 actions |
调用方式 | 手动 | 通常由 React Redux 生成 |
使用react-redux
可以先结合context
来手动连接react和redux。
react-redux提供两个核心的api:
- Provider: 提供store
- connect: 用于连接容器组件和展示组件
-
Provider
根据单一store原则 ,一般只会出现在整个应用程序的最顶层。
-
connect
语法格式为
connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)(component)
一般来说只会用到前面两个,它的作用是:
- 把
store.getState()
的状态转化为展示组件的props
- 把
actionCreators
转化为展示组件props
上的方法
- 把
特别强调:
官网上的第二个参数为mapDispatchToProps, 实际上就是actionCreators
只要上层中有Provider
组件并且提供了store
, 那么,子孙级别的任何组件,要想使用store
里的状态,都可以通过connect
方法进行连接。如果只是想连接actionCreators
,可以第一个参数传递为null
Mobx
Mobx是一个功能强大,上手非常容易的状态管理工具。redux的作者也曾经向大家推荐过它,在不少情况下可以使用Mobx来替代掉redux。
这张图来自于官网,把这张图理解清楚了。基本上对于mobx的理解就算入门了。
官网有明确的核心概念使用方法,并配有egghead的视频教程。这里就不一一赘述了。
要特别注意当使用 mobx-react
时可以定义一个新的生命周期钩子函数 componentWillReact
。当组件因为它观察的数据发生了改变,它会安排重新渲染,这个时候 componentWillReact
会被触发。这使得它很容易追溯渲染并找到导致渲染的操作(action)。
componentWillReact
不接收参数componentWillReact
初始化渲染前不会触发 (使用componentWillMount
替代)componentWillReact
对于 mobx-react@4+, 当接收新的 props 时并在setState
调用后会触发此钩子- 要触发
componentWillReact
必须在render里面用到被观察的变量 - 使用Mobx之后不会触发
componentWillReceiveProps
react脚手架 - Mobx配置 ( 装饰器 )
-
创建项目
create-react-app app -
进入项目
cd app -
进行配置文件抽离
yarn eject -
安装mobx mobx-react
mobx 是状态管理工具
mobx-react 是做数据分片和数据获取
$ yarn add mobx mobx-react
注意: 如果git冲突
解决: 我们要原文件先放到本地暂存盘
git add .
git commit -m ’ 然后 : 安装mobx mobx-react’
注意不要git push -
配置装饰器( 修饰器 es6 ) babel
yarn add babel-plugin-transform-decorators-legacy -D
yarn add @babel/preset-env -D
yarn add babel-plugin-transform-class-properties -D
yarn add @babel/plugin-proposal-decorators -D -
配置package.json
“babel”: {
“plugins”: [
[
“@babel/plugin-proposal-decorators”,
{
“legacy”: true
}
],
“transform-class-properties”
],
“presets”: [
“react-app”,
“@babel/preset-env”
]
},
注意: 以下两个配置顺序不可更改
[
“@babel/plugin-proposal-decorators”,
{
“legacy”: true
}
],
“transform-class-properties”
项目中 mobx应该怎么用?
-
新建store目录
src
store
home
index.js
car
index.js
index.js -
在入口文件中 使用 Provider
import store from ‘./store’
import { Provider } from ‘mobx-react’ReactDOM.render(
, document.getElementById(‘root’)); -
哪个组件使用 , 就在哪个组件中 “注入” inject
import {inject} from ‘mobx-react’
@inject(‘store’)
-
打造mobx 数据包
import {observable, computed,action
} from 'mobx'
class Home {@observable //监听 ageage = 18@computed //当age发生改变时, 自动触发get doubleAge(){return this.age *= 2}@action // 用户操作 事件调用increment(){this.props.store.home.age ++ console.log( this.props.store.home.age )//数据请求fetch('/data/data.json').then(res => res.json()).then( result => console.log( result )).catch( error => console.log( error ))}}const home = new Home()export default home
-
打造store
store/index.jsimport home from ‘./home’
const store = {
//实例
home
}export default store
-
组件内使用数据
this.props.store.xxx 可以拿到数据
注意:
-
this.porps里面没有找到 @action 装饰器定义的方法, 但是可以直接使用,
-
this会丢失
this.props.store.home.increment.bind(this)
-
React Router
参考笔记: https://reacttraining.com/react-router/web/api/Route
React Router现在的版本是5, 于2019年3月21日搞笑的发布,搞笑的官网链接, 本来是要发布4.4的版本的,结果成了5。从4开始,使用方式相对于之前版本的思想有所不同。之前版本的思想是传统的思想:路由应该统一在一处渲染, Router 4之后是这样的思想:一切皆组件
面试题: React-router 3.x vs React-router 4.x / vue-router vs React-router
Router 4之后是这样的思想:一切皆组件
React Router包含了四个包:
包名 | Description |
---|---|
react-router | React Router核心api |
react-router-dom | React Router的DOM绑定,在浏览器中运行不需要额外安装react-router |
react-router-native | React Native 中使用,而实际的应用中,其实不会使用这个。 |
react-router-config | 静态路由的配置 |
主要使用react-router-dom
使用方式
正常情况下,直接按照官网的demo就理解 路由的使用方式,有几个点需要特别的强调:
- Route组件的exact属性
exact
属性标识是否为严格匹配, 为true
是表示严格匹配,为false
时为正常匹配。
- Route组件的render属性而不是component属性
怎么在渲染组件的时候,对组件传递属性呢?使用component
的方式是不能直接在组件上添加属性的。所以,React Router的Route
组件提供了另一种渲染组件的方式 render
, 这个常用于页面组件级别的权限管理。
-
路由的参数传递与获取
-
Switch组件
总是渲染第一个匹配到的组件
-
处理404与默认页
-
withRoute高阶组件的使用
-
管理一个项目路由的方法
-
code spliting
-
HashRouter和BrowserRouter的区别,前端路由和后端路由的区别。这个在Vue里应该有讲过了。
React Router基本原理
React Router甚至大部分的前端路由都是依赖于history.js
的,它是一个独立的第三方js库。可以用来兼容在不同浏览器、不同环境下对历史记录的管理,拥有统一的API。
- 老浏览器的history: 通过
hash
来存储在不同状态下的history
信息,对应createHashHistory
,通过检测location.hash
的值的变化,使用location.replace
方法来实现url跳转。通过注册监听window
对象上的hashChange
事件来监听路由的变化,实现历史记录的回退。 - 高版本浏览器: 利用HTML5里面的history,对应
createBrowserHistory
, 使用包括pushState
,replaceState
方法来进行跳转。通过注册监听window
对象上的popstate
事件来监听路由的变化,实现历史记录的回退。 - node环境下: 在内存中进行历史记录的存储,对应
createMemoryHistory
。直接在内存里push
和pop
状态。【 React 服务端渲染 】 - 总结:
- 老版本的浏览器是通过location.hash + hashchange事件 来监听url变化的 【 3.x 】
- 高版本浏览器是通过H5的history来进行url处理的 【 4.x 】
Immutable.js
前端拷贝问题 【 深拷贝 】
- 原生实现
- 递归
- 工具实现【 第三方封装库 】
- loadsh
- 缺点
- Immutable
JavaScript数据修改的问题
看一段大家熟悉的代码
const state = {str: 'string',obj: {y: 1},arr: [1, 2, 3]
}
const newState = stateconsole.log(newState === state) // true
由于js的对象和数组都是引用类型。所以newState的state实际上是指向于同一块内存地址的, 所以结果是newState和state是相等的。
尝试修改一下数据
const state = {str: 'string',obj: {y: 1},arr: [1, 2, 3]
}
const newState = statenewState.str = 'a cool boy'console.log(state.str, newState.str)
可以看到,newState的修改也会引起state的修改。要解决这个问题,js中提供了另一种修改数据的方式,要修改一个数据之前先制作一份数据的拷贝,像这样
const state = {str: 'string',obj: {y: 1},arr: [1, 2, 3]
}
const newState = Object.assign({}, state)newState.str = 'a cool boy'console.log(state.str, newState.str)
我们可以使用很多方式在js中复制数据,比如…
, Object.assign
, Object.freeze
, slice
, concat
, map
, filter
, reduce
等方式进行复制,但这些都是浅拷贝,就是只拷贝第一层数据,更深层的数据还是同一个引用,比如:
const state = {str: 'string',obj: {y: 1},arr: [1, 2, 3]
}
const newState = Object.assign({}, state)newState.obj.y = 2
newState.arr.push(4)console.log(state, newState)
可以看到,当在更改newState更深层次的数据的时候,还是会影响到state的值。如果要深层复制,就得一层一层的做递归拷贝,这是一个复杂的问题。虽然有些第三方的库已经帮我们做好了,比如lodash
的cloneDeep
方法。深拷贝是非常消耗性能的。
import { cloneDeep } from 'lodash'const state = {str: 'str',obj: {y: 1},arr: [1, 2, 3]
}
const newState = cloneDeep(state)newState.obj.y = 2
newState.arr.push(4)console.log(state, newState)
什么是不可变数据
不可变数据 (Immutable Data )就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是持久化数据结构( Persistent Data Structure),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的s性能损耗,Immutable 使用了 结构共享(Structural Sharing),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
immutable.js的优缺点
优点:
- 降低immutable带来的复杂度
- 节省内存
- 历史追溯性(时间旅行):时间旅行指的是,每时每刻的值都被保留了,想回退到哪一步只要简单的将数据取出就行,想一下如果现在页面有个撤销的操作,撤销前的数据被保留了,只需要取出就行,这个特性在redux或者flux中特别有用
- 拥抱函数式编程:immutable本来就是函数式编程的概念,纯函数式编程的特点就是,只要输入一致,输出必然一致,相比于面向对象,这样开发组件和调试更方便。推荐一本函数式编程的在线免费书《JS 函数式编程指南》, 此书可以推荐给学生做为课外补充阅读。
缺点:
- 需要重新学习api
- 资源包大小增加(源码5000行左右)
- 容易与原生对象混淆:由于api与原生不同,混用的话容易出错。
使用Immutable.js
参考官网重点讲解数据不可变数据的创建、更新及比较方式 。对于就业班来说,掌握以下知识点即可。
Map
import { Map } from 'immutable'const map = Map({a: 1,b: 2,c: 3
})const newMap = map.set('b', 20) // immutable数据每次都是生成新的再重新调用set进行修改,所以需要 重新赋值给一个新的变量console.log(map, newMap) // immutable.Map不是原生的对象
console.log(map.b, newMap.b) // immutable.Map不是原生的对象, 所以是undefined
console.log(map.get('b'), newMap.get('b')) // 要取值,需要调用get(key)方法,可以看到,两个值不一样const obj = {a: 1,b: 2,c: 3
}console.log(Map.isMap(map), Map.isMap(obj)) // true false, 使用Map.isMap来判断是否是一个immutable.Map类型
List
import { List } from 'immutable'const list = List([1, 2, 3, 4])
const newList = list.push(5)
console.log(list, newList)
console.log(list[4], newList[4]) // undefined undefined
console.log(list.get(4), newList.get(4)) // undefined 5
console.log(list.size, newList.size) // 4 5const arr = [1, 2, 3, 4]console.log(List.isList(list), List.isList(arr)) // true false
equals & is
import { Map, is } from 'immutable'const map = Map({a: 1,b: 2,c: 3
})const anotherMap = Map({a: 1,b: 2,c: 3
})console.log(map == anotherMap) // false
console.log(map === anotherMap) // false
console.log(map.equals(anotherMap)) // 使用equals进行比较 true
console.log(is(map, anotherMap)) // 使用is进行比较 true
List常用api
import { List } from 'immutable'const list = List([1, 2, 3, 4])
const list1 = list.push(5)
const list2 = list1.unshift(0)
const list3 = list.concat(list1, list2)
const list4 = list.map(v => v * 2)console.log(list.size, list1.size, list2.size, list3.size, list4.toJS()) // 4 5 6 15, [2, 4, 6, 8]
Map常用api
import { Map } from 'immutable'const alpha = Map({a: 1,b: 2,c: 3
})
const objKeys = alpha.map((v, k) => k)
console.log(objKeys.join()) // a, b, cconst map1 = Map({a: 1,b: 2
})
const map2 = Map({c: 3,d: 4
})
const obj = {d: 400,e: 50
}const mergedMap = map1.merge(map2, obj)console.log(mergedMap.toObject())
console.log(mergedMap.toJS())
嵌套数据结构
const { fromJS } = require('immutable');
const nested = fromJS({ a: { b: { c: [ 3, 4, 5 ] } } });const nested2 = nested.mergeDeep({ a: { b: { d: 6 } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }console.log(nested2.getIn([ 'a', 'b', 'd' ])); // 6const nested3 = nested2.updateIn([ 'a', 'b', 'd' ], value => value + 1);
console.log(nested3);
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }const nested4 = nested3.updateIn([ 'a', 'b', 'c' ], list => list.push(6));
// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }
##在redux中使用immutable.js
redux官网推荐使用redux-immutable进行redux和immutable的集成。几个注意点:
redux
中,利用combineReducers
来合并多个reduce
, redux
自带的combineReducers
只支持原生js形式的,所以需要使用redux-immutable
提供的combineReducers
来代替
// 使用redux-immutable提供的combineReducers方法替换redux里的combineReducers
import {combineReducers} from 'redux-immutable'
import reducerOne from './reducerOne'
import reducerTwo from './reducerTwo'const rootReducer = combineReducers({reducerOne,reducerTwo
});export default rootReducer;
reducer
中的initialState
也需要初始化成immutable
类型, 比如一个counter的reducer
import { Map } from 'immutable'import ActionTypes from '../actions'const initialState = Map({count: 0
})export default (state = initialState, action) => {switch (action.type) {case ActionTypes.INCREAMENT:return state.set('count', state.get('count') + 1) // 使用set或setIn来更改值, get或者getIn来取值case ActionTypes.DECREAMENT:return state.set('count', state.get('count') - 1)default:return state}
}
state
成为了immutable
类型,connect
的mapStateToProp
也需要相应的改变
const mapStateToProps = state => ({count: state.getIn(['counter', 'count']) // 永远不要在mapStateToProps里使用`toJS`方法,因为它永远返回一个新的对象
})
在shouldComponentUpdate
里就可以使用immutable.is
或者instance.equals
来进行数据的对比了。
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- Android中的数据绑定框架DataBinding(对比AngularJS双向数据绑定很好理解)
转自:http://blog.csdn.net/qibin0506/article/details/47393725 今天来了解一下Android最新给我们带来的数据绑定框架——Data Binding Library。数据绑定框架给我们带来了更大的方便性,以前我们可能需要在Activity里写很多的findViewById,烦…...
2024/5/9 21:12:02 - [OpenGL] Shadow Map 阴影
图:随着键盘控制点光源位置移动,阴影发生实时的变换 之前在 https://blog.csdn.net/ZJU_fish1996/article/details/51932954 一文中已经介绍了shadow map的基本原理,至今为止,它依旧是在游戏开发中运用较(最?)广的一种阴影技术。…...
2024/5/9 18:11:50 - 工作积累(九)——前后台传递类Map型参数
最近在工作中整合友盟消息推送服务时,遇到了用 Ajax 向 Java 后台传递自定义参数的需求,当时想要采取 java.util.Map ,但发现 Ajax 无法传递 java.util.Map 类型的参数,后来无奈采取的方式的是采用了这样的 Vo 对象: p…...
2024/5/10 9:03:57 - 割双眼皮价位SOU奇致治病
...
2024/5/10 13:55:50 - 双眼皮和填脂肪可以一起做吗
...
2024/5/9 15:33:59 - 双眼皮缝线位置可以填脂肪吗
...
2024/5/9 6:49:19 - 做完双眼皮怎么能维持的更久
...
2024/5/3 5:30:51 - 双眼皮割过维持多久
...
2024/5/3 9:37:02 - 割一个永久的双眼皮多少钱
...
2024/5/6 5:47:32 - 南通丽人整形美容医院做怎么样自然做出割了双眼皮能管多久
...
2024/5/9 6:27:29 - 做双眼皮价格VIP只搜奇致
...
2024/4/20 16:03:41 - AngularJs和ajax动态修改url地址而不刷新页面的方法
2019独角兽企业重金招聘Python工程师标准>>> 1、Angularjs: 原始地址为:http://xxx/#/a 添加或修改参数: $location.url(?a1&aa23); 执行后浏览器的url地址变为:http://xxx/#/a?a1&aa23,而页面也没有刷新。 …...
2024/4/20 16:03:39 - 割双眼皮整形to奇致勤奋
...
2024/4/20 16:03:38 - 双眼皮填脂肪太多了
...
2024/4/28 8:39:28 - 双眼皮 张春光
...
2024/4/21 13:14:49 - 全切双眼皮怎样避免疤痕增生
...
2024/5/9 5:55:55 - 双眼皮手术怎么做多少钱啊
...
2024/4/21 13:14:48 - Echarts 通过时间轴timeline改变xAxis.data数据进行不合并处理
写在前面: 使用时间轴 timeline 绘制图形的时候会有一种动态的效果,让图形看起来更加生动,也达到了交互式数据的展现。但是在使用 timeline 的时候我遇到了几个问题,其中最头疼的还是对 xAxis.data 数据进行不合并处理。本文章就是通过使用 TIMELINE_CHANGED 方法和…...
2024/5/6 6:02:48 - 双眼皮多少钱伍约伊思整形ok
...
2024/4/21 13:14:46 - 多点埋切双眼皮明眸大眼术做法
...
2024/4/21 13:14:45
最新文章
- Linux网络编程:TCP并发服务器实现
目录 1、前言 2、多进程代码实现 2.1 创建新的进程 2.2 客户端接收响应函数 2.3 僵尸进程处理 2.4 完整代码 2.5 代码测试 3、多线程代码实现 3.1 创建新的线程 3.2 线程函数定义 3.3 完整代码 3.4 代码测试 4、总结 1、前言 前面实现了基本的TCP编程…...
2024/5/10 15:11:17 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/5/9 21:23:04 - K8S容器空间不足问题分析和解决
如上图,今天测试环境的K8S平台出现了一个问题,其中的一个容器报错:Free disk space below threshold. Available: 3223552 bytes (threshold: 10485760B),意思服务器硬盘空间不够了。这个问题怎么产生的,又怎么解决的呢…...
2024/5/9 13:07:02 - 大模型重塑电商,淘宝、百度、京东讲出新故事
配图来自Canva可画 随着AI技术日渐成熟,大模型在各个领域的应用也越来越深入,国内互联网行业也随之进入了大模型竞赛的后半场,开始从“百模大战”转向了实际应用。大模型从通用到细分垂直领域的跨越,也让更多行业迎来了新的商机。…...
2024/5/9 9:37:34 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/5/10 12:36:12 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/5/9 15:10:32 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/5/4 23:54:56 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/5/9 4:20:59 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/5/4 23:54:56 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/5/4 23:55:05 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/5/4 23:54:56 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/5/7 11:36:39 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/5/4 23:54:56 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/5/6 1:40:42 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/5/4 23:54:56 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/5/8 20:48:49 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/5/7 9:26:26 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/5/4 23:54:56 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/5/8 19:33:07 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/5/5 8:13:33 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/5/8 20:38:49 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/5/4 23:54:58 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/5/10 10:22:18 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/5/9 17:11:10 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57