引言

如今,主流的前端框架React,Vue和Angular在前端领域已成三足鼎立之势,基于前端技术栈的发展现状,大大小小的公司或多或少也会使用其中某一项或者多项技术栈,那么掌握并熟练使用其中至少一种也成为了前端人员必不可少的技能饭碗。当然,框架的部分实现细节也常成为面试中的考察要点,因此,一方面为了应付面试官的连番追问,另一方面为了提升自己的技能水平,还是有必要对框架的底层实现原理有一定的涉猎。

当然对于主攻哪门技术栈没有严格的要求,挑选你自己喜欢的就好,在面试中面试官一般会先问你最熟悉的是哪门技术栈,对于你不熟悉的领域,面试官可能也不会做太多的追问。笔者在项目中一直是使用的Vue框架,其上手门槛低,也提供了比较全面和友好的官方文档可供参考。但是可能因人而异,感觉自己还是比较喜欢React,也说不出什么好坏,可能是自己最早接触的前端框架吧,不过很遗憾,在之前的工作中一直派不上用场,但即便如此,也阻挡不了自己对底层原理的好奇心。所以最近也是开始研究React的源码,并对源码的解读过程做一下记录,方便加深记忆。如果你的技术栈刚好是React,并且也对源码感兴趣,那么我们可以一起互相探讨技术难点,让整个阅读源码的过程变得更加容易和有趣。源码中如果有理解错误的地方,还希望能够指出。

1、准备阶段

在facebook的github上,目前React的最新版本为v16.12.0,我们知道在React的v16版本之后引入了新的Fiber架构,这种架构使得任务拥有了暂停恢复机制,将一个大的更新任务拆分为一个一个执行单元,充分利用浏览器在每一帧的空闲时间执行任务,无空闲时间则延迟执行,从而避免了任务的长时间运行导致阻塞主线程同步任务的执行。为了了解这种Fiber架构,这里选择了一个比较适中的v16.10.2的版本,没有选择最新的版本是因为在最新版本中移除了一些旧的兼容处理方案,虽说这些方案只是为了兼容,但是其思想还是比较先进的,值得我们推敲学习,所以先将其保留下来,这里选择v16.10.2版本的另外一个原因是React在v16.10.0的版本中涉及到两个比较重要的优化点:


在上图中指出,在任务调度(Scheduler)阶段有两个性能的优化点,解释如下:

  • 将任务队列的内部数据结构转换成最小二叉堆的形式以提升队列的性能(在最小堆中我们能够以最快的速度找到最小的那个值,因为那个值一定在堆的顶部,有效减少整个数据结构的查找时间)。
  • 使用周期更短的postMessage循环的方式而不是使用requestAnimationFrame这种与帧边界对齐的方式(这种优化方案指得是在将任务进行延迟后恢复执行的阶段,前后两种方案都是宏任务,但是宏任务也有顺序之分,postMessage的优先级比requestAnimationFrame高,这也就意味着延迟任务能够更快速地恢复并执行)。

当然现在不太理解的话没关系,后续会有单独的文章来介绍任务调度这一块内容,遇到上述两个优化点的时候会进行详细说明,在开始阅读源码之前,我们可以使用create-react-app来快速搭建一个React项目,后续的示例代码可以在此项目上进行编写:

// 项目搭建完成后React默认为最新版v16.12.0
create-react-app react-learning// 为了保证版本一致,手动将其修改为v16.10.2
npm install --save react@16.10.2 react-dom@16.10.2// 运行项目
npm start

执行以上步骤后,不出意外的话,浏览器中会正常显示出项目的默认界面。得益于在Reactv16.8版本之后推出的React Hooks功能,让我们在原来的无状态函数组件中也能进行状态管理,以及使用相应的生命周期钩子,甚至在新版的create-react-app脚手架中,根组件App已经由原来的类组件的写法升级为了推荐的函数定义组件的方式,但是原来的类组件的写法并没有被废弃掉,事实上我们项目中还是会大量充斥着类组件的写法,因此为了了解这种类组件的实现原理,我们暂且将App根组件的函数定义的写法回退到类组件的形式,并对其内容进行简单修改:

// src -> App.js
import React, {Component} from 'react';function List({data}) {return (<ul className="data-list">{data.map(item => {return <li className="data-item" key={item}>{item}</li>})}</ul>);
}export default class App extends Component {constructor(props) {super(props);this.state = {data: [1, 2, 3]};}render() {return (<div className="container"><h1 className="title">React learning</h1><List data={this.state.data} /></div>);}
}

经过以上简单修改后,然后我们通过调用

// src -> index.js
ReactDOM.render(<App />, document.getElementById('root'));

来将组件挂载到DOM容器中,最终得到App组件的DOM结构如下所示:

<div class="container"><h1 class="title">React learning</h1><ul class="data-list"><li class="data-item">1</li><li class="data-item">2</li><li class="data-item">3</li></ul>
</div>

因此我们分析React源码的入口也将会是从ReactDOM.render方法开始一步一步分析组件渲染的整个流程,但是在此之前,我们有必要先了解几个重要的前置知识点,这几个知识点将会更好地帮助我们理解源码的函数调用栈中的参数意义和其他的一些细节。

2、前置知识

首先我们需要明确的是,在上述示例中,App组件的render方法返回的是一段HTML结构,在普通的函数中这种写法是不支持的,所以我们一般需要相应的插件来在背后支撑,在React中为了支持这种jsx语法提供了一个Babel预置工具包@babel/preset-react,其中这个preset又包含了两个比较核心的插件:

  • @babel/plugin-syntax-jsx:这个插件的作用就是为了让Babel编译器能够正确解析出jsx语法。
  • @babel/plugin-transform-react-jsx:在解析完jsx语法后,因为其本质上是一段HTML结构,因此为了让JS引擎能够正确识别,我们就需要通过该插件将jsx语法编译转换为另外一种形式。在默认情况下,会使用React.createElement来进行转换,当然我们也可以在.babelrc文件中来进行手动设置。
// .babelrc
{"plugins": [["@babel/plugin-transform-react-jsx", {"pragma": "Preact.h", // default pragma is React.createElement"pragmaFrag": "Preact.Fragment", // default is React.Fragment"throwIfNamespace": false // defaults to true}]]
}

这里为了方便起见,我们可以直接使用Babel官方实验室来查看转换后的结果,对应上述示例,转换后的结果如下所示:

// 转换前
render() {return (<div className="container"><h1 className="title">React learning</h1><List data={this.state.data} /></div>);
}// 转换后
render() {return React.createElement("div", {className: "content"}, React.createElement("header", null, "React learning"), React.createElement(List, { data: this.state.data }));
}

可以看到jsx语法最终被转换成由React.createElement方法组成的嵌套调用链,可能你之前已经了解过这个API,或者接触过一些伪代码实现,这里我们就基于源码,深入源码内部来看看其背后为我们做了哪些事情。

2.1 createElement & ReactElement

为了保证源码的一致性,也建议你将React版本和笔者保持一致,采用v16.10.2版本,可以通过facebook的github官方渠道进行获取,下载下来之后我们通过如下路径来打开我们需要查看的文件:

// react-16.10.2 -> packages -> react -> src -> React.js 

React.js文件中,我们直接跳转到第63行,可以看到React变量作为一个对象字面量,包含了很多我们所熟知的方法,包括在v16.8版本之后推出的React Hooks方法:

const React = {Children: {map,forEach,count,toArray,only,},createRef,Component,PureComponent,createContext,forwardRef,lazy,memo,// 一些有用的React Hooks方法useCallback,useContext,useEffect,useImperativeHandle,useDebugValue,useLayoutEffect,useMemo,useReducer,useRef,useState,Fragment: REACT_FRAGMENT_TYPE,Profiler: REACT_PROFILER_TYPE,StrictMode: REACT_STRICT_MODE_TYPE,Suspense: REACT_SUSPENSE_TYPE,unstable_SuspenseList: REACT_SUSPENSE_LIST_TYPE,// 重点先关注这里,生产模式下使用后者createElement: __DEV__ ? createElementWithValidation : createElement,cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,createFactory: __DEV__ ? createFactoryWithValidation : createFactory,isValidElement: isValidElement,version: ReactVersion,unstable_withSuspenseConfig: withSuspenseConfig,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,

这里我们暂且先关注createElement方法,在生产模式下它来自于与React.js同级别的ReactElement.js文件,我们打开该文件,并直接跳转到第312行,可以看到createElement方法的函数定义(去除了一些__DEV__环境才会执行的代码):

/*** 该方法接收包括但不限于三个参数,与上述示例中的jsx语法经过转换之后的实参进行对应* @param type 表示当前节点的类型,可以是原生的DOM标签字符串,也可以是函数定义组件或者其它类型* @param config 表示当前节点的属性配置信息* @param children 表示当前节点的子节点,可以不传,也可以传入原始的字符串文本,甚至可以传入多个子节点* @returns 返回的是一个ReactElement对象*/
export function createElement(type, config, children) {let propName;// Reserved names are extracted// 用于存放config中的属性,但是过滤了一些内部受保护的属性名const props = {};// 将config中的key和ref属性使用变量进行单独保存let key = null;let ref = null;let self = null;let source = null;// config为null表示节点没有设置任何相关属性if (config != null) {// 有效性判断,判断 config.ref !== undefinedif (hasValidRef(config)) {ref = config.ref;}// 有效性判断,判断 config.key !== undefinedif (hasValidKey(config)) {key = '' + config.key;}self = config.__self === undefined ? null : config.__self;source = config.__source === undefined ? null : config.__source;// Remaining properties are added to a new props object// 用于将config中的所有属性在过滤掉内部受保护的属性名后,将剩余的属性全部拷贝到props对象中存储// const RESERVED_PROPS = {//   key: true,//   ref: true,//   __self: true,//   __source: true,// };for (propName in config) {if (hasOwnProperty.call(config, propName) &&!RESERVED_PROPS.hasOwnProperty(propName)) {props[propName] = config[propName];}}}// Children can be more than one argument, and those are transferred onto// the newly allocated props object.// 由于子节点的数量不限,因此从第三个参数开始,判断剩余参数的长度// 具有多个子节点则props.children属性存储为一个数组const childrenLength = arguments.length - 2;if (childrenLength === 1) {// 单节点的情况下props.children属性直接存储对应的节点props.children = children;} else if (childrenLength > 1) {// 多节点的情况下则根据子节点数量创建一个数组const childArray = Array(childrenLength);for (let i = 0; i < childrenLength; i++) {childArray[i] = arguments[i + 2];}props.children = childArray;}// Resolve default props// 此处用于解析静态属性defaultProps// 针对于类组件或函数定义组件的情况,可以单独设置静态属性defaultProps// 如果有设置defaultProps,则遍历每个属性并将其赋值到props对象中(前提是该属性在props对象中对应的值为undefined)if (type && type.defaultProps) {const defaultProps = type.defaultProps;for (propName in defaultProps) {if (props[propName] === undefined) {props[propName] = defaultProps[propName];}}}// 最终返回一个ReactElement对象return ReactElement(type,key,ref,self,source,ReactCurrentOwner.current,props,);
}

经过上述分析我们可以得出,在类组件的render方法中最终返回的是由多个ReactElement对象组成的多层嵌套结构,所有的子节点信息均存放在父节点的props.children属性中。我们将源码定位到ReactElement.js的第111行,可以看到ReactElement函数的完整实现:

/*** 为一个工厂函数,每次执行都会创建并返回一个ReactElement对象* @param type 表示节点所对应的类型,与React.createElement方法的第一个参数保持一致* @param key 表示节点所对应的唯一标识,一般在列表渲染中我们需要为每个节点设置key属性* @param ref 表示对节点的引用,可以通过React.createRef()或者useRef()来创建引用* @param self 该属性只有在开发环境才存在* @param source 该属性只有在开发环境才存在* @param owner 一个内部属性,指向ReactCurrentOwner.current,表示一个Fiber节点* @param props 表示该节点的属性信息,在React.createElement中通过config,children参数和defaultProps静态属性得到* @returns 返回一个ReactElement对象*/
const ReactElement = function(type, key, ref, self, source, owner, props) {const element = {// This tag allows us to uniquely identify this as a React Element// 这里仅仅加了一个$$typeof属性,用于标识这是一个React Element$$typeof: REACT_ELEMENT_TYPE,// Built-in properties that belong on the elementtype: type,key: key,ref: ref,props: props,// Record the component responsible for creating this element._owner: owner,};...return element;
};

一个ReactElement对象的结构相对而言还是比较简单,主要是增加了一个$$typeof属性用于标识该对象是一个React Element类型。REACT_ELEMENT_TYPE在支持Symbol类型的环境中为symbol类型,否则为number类型的数值。与REACT_ELEMENT_TYPE对应的还有很多其他的类型,均存放在shared/ReactSymbols目录中,这里我们可以暂且只关心这一种,后面遇到其他类型再来细看。

2.2 Component & PureComponent

了解完ReactElement对象的结构之后,我们再回到之前的示例,通过继承React.Component我们将App组件修改为了一个类组件,我们不妨先来研究下React.Component的底层实现。React.Component的源码存放在packages/react/src/ReactBaseClasses.js文件中,我们将源码定位到第21行,可以看到Component构造函数的完整实现:

/*** 构造函数,用于创建一个类组件的实例* @param props 表示所拥有的属性信息* @param context 表示所处的上下文信息* @param updater 表示一个updater对象,这个对象非常重要,用于处理后续的更新调度任务*/
function Component(props, context, updater) {this.props = props;this.context = context;// If a component has string refs, we will assign a different object later.// 该属性用于存储类组件实例的引用信息// 在React中我们可以有多种方式来创建引用// 通过字符串的方式,如:<input type="text" ref="inputRef" />// 通过回调函数的方式,如:<input type="text" ref={(input) => this.inputRef = input;} />// 通过React.createRef()的方式,如:this.inputRef = React.createRef(null); <input type="text" ref={this.inputRef} />// 通过useRef()的方式,如:this.inputRef = useRef(null); <input type="text" ref={this.inputRef} />this.refs = emptyObject;// We initialize the default updater but the real one gets injected by the// renderer.// 当state发生变化的时候,需要updater对象去处理后续的更新调度任务// 这部分涉及到任务调度的内容,在后续分析到任务调度阶段的时候再来细看this.updater = updater || ReactNoopUpdateQueue;
}// 在原型上新增了一个isReactComponent属性用于标识该实例是一个类组件的实例
// 这个地方曾经有面试官考过,问如何区分函数定义组件和类组件
// 函数定义组件是没有这个属性的,所以可以通过判断原型上是否拥有这个属性来进行区分
Component.prototype.isReactComponent = {};/*** 用于更新状态* @param partialState 表示下次需要更新的状态* @param callback 在组件更新之后需要执行的回调*/
Component.prototype.setState = function(partialState, callback) {...this.updater.enqueueSetState(this, partialState, callback, 'setState');
};/*** 用于强制重新渲染* @param callback 在组件重新渲染之后需要执行的回调*/
Component.prototype.forceUpdate = function(callback) {this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

上述内容中涉及到任务调度的会在后续讲解到调度阶段的时候再来细讲,现在我们知道可以通过原型上的isReactComponent属性来区分函数定义组件和类组件。事实上,在源码中就是通过这个属性来区分Class ComponentFunction Component的,可以找到以下方法:

// 返回true则表示类组件,否则表示函数定义组件
function shouldConstruct(Component) {return !!(Component.prototype && Component.prototype.isReactComponent);
}

Component构造函数对应的,还有一个PureComponent构造函数,这个我们应该还是比较熟悉的,通过浅比较判断组件前后传递的属性是否发生修改来决定是否需要重新渲染组件,在一定程度上避免组件重渲染导致的性能问题。同样的,在ReactBaseClasses.js文件中,我们来看看PureComponent的底层实现:

// 通过借用构造函数,实现典型的寄生组合式继承,避免原型污染
function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;function PureComponent(props, context, updater) {this.props = props;this.context = context;// If a component has string refs, we will assign a different object later.this.refs = emptyObject;this.updater = updater || ReactNoopUpdateQueue;
}// 将PureComponent的原型指向借用构造函数的实例
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());// 重新设置构造函数的指向
pureComponentPrototype.constructor = PureComponent;// Avoid an extra prototype jump for these methods.
// 将Component.prototype和PureComponent.prototype进行合并,减少原型链查找所浪费的时间(原型链越长所耗费的时间越久)
Object.assign(pureComponentPrototype, Component.prototype);// 这里是与Component的区别之处,PureComponent的原型上拥有一个isPureReactComponent属性
pureComponentPrototype.isPureReactComponent = true;

通过以上分析,我们就可以初步得出ComponentPureComponent之间的差异,可以通过判断原型上是否拥有isPureReactComponent属性来进行区分,当然更细粒度的区分,还需要在阅读后续的源码内容之后才能见分晓。

3、面试考点

看完以上内容,按道理来说以下几个可能的面试考点应该就不成问题了,或者说至少也不会遇到一个字也回答不了的尴尬局面,试试看吧:

  • 在React中为何能够支持jsx语法
  • 类组件的render方法执行后最终返回的结果是什么
  • 手写代码实现一个createElement方法
  • 如何判断一个对象是不是React Element
  • 如何区分类组件和函数定义组件
  • ComponentPureComponent之间的关系
  • 如何区分ComponentPureComponent

4、总结

本文作为React16源码解读的开篇,先讲解了几个比较基础的前置知识点,这些知识点有助于我们在后续分析组件的任务调度和渲染过程时能够更好地去理解源码。阅读源码的过程是痛苦的,一个原因是源码量巨大,文件依赖关系复杂容易让人产生恐惧退缩心理,另一个是阅读源码是个漫长的过程,期间可能会占用你学习其他新技术的时间,让你无法完全静下心来。但是其实我们要明白的是,学习源码不只是为了应付面试,源码中其实有很多我们可以借鉴的设计模式或者使用技巧,如果我们可以学习并应用到我们正在做的项目中,也不失为一件有意义的事情。后续文章就从ReactDOM.render方法开始,一步一步分析组件渲染的整个流程,我们也不需要去搞懂每一行代码,毕竟每个人的思路不太一样,但是关键步骤我们还是需要去多花时间理解的。

5、交流

如果你觉得这篇文章的内容对你有帮助,能否帮个忙关注一下笔者的公众号[前端之境],每周都会努力原创一些前端技术干货,关注公众号后可以邀你加入前端技术交流群,我们可以一起互相交流,共同进步。

文章已同步更新至Github博客,若觉文章尚可,欢迎前往star!

你的一个点赞,值得让我付出更多的努力!

逆境中成长,只有不断地学习,才能成为更好的自己,与君共勉!

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Python算法学习day5:剑指offer:二进制中1的个数

    剑指offer:二进制中1的个数 (1) 运用Python函数 代码详细: def numberOf1(n):if n >= 0:return bin(n).count(1)else:return bin(n & 0xffffffff).count(1)或者: def numberOf1(n):return bin(n & 0xffffffff).count(1)这里bin函数将数字转换为二进制,count为Pytho…...

    2024/3/29 7:38:25
  2. 【汇智学堂】docker布署springboot项目入门之二

    Ubuntu: Ifconfig -a,make sure which ip.makedir dockspace in “/home” modify, read and write for everybody sudo chmod 777 /home/dockerspace next, putty client , version:ssh-server. if you don not have it ,study from this: cmd,pscp(study from https://docs.d…...

    2024/4/27 17:55:59
  3. 抽象类和接口

    抽象类类与类之间的关系是继承,一个类可以继承另一个类,而有一种类存在的意义就是为了继承 抽象类包含抽象方法(abstract method)的类就是抽象类(abstract class)抽象方法被 abstract 修饰的方法称为抽象方法,抽象方法没有方法体( 没有 {} ,不能执行代码) abstract class S…...

    2024/4/27 14:14:54
  4. 搭建驱动(内核)开发环境_vs2017+WDK1809

    Install Visual Studio 下载链接: https://docs.microsoft.com/en-us/windows-hardware/drivers/other-wdk-downloads#step-2-install-the-wdk 这个链接也包含WDK的下载,可以根据系统版本下载对应版本的WDK. 安装选项:如果需要兼容XP等操作系统并且需要编写MFC应用程序,还需要…...

    2024/4/27 15:01:54
  5. D. Zero Quantity Maximization

    链接:https://codeforces.com/problemset/problem/1133/DYou are given two arrays aa and bb, each contains nn integers.You want to create a new array cc as follows: choose some real (i.e. not necessarily integer) number dd, and then for every i∈[1,n]i∈[1,n]…...

    2024/4/27 16:57:31
  6. git应用

    gitgit的简介 Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持。Git 与 SVN 区别 Git 不仅仅是个版本控制系统,它也是个内容管理系统(CMS),…...

    2024/4/27 14:31:25
  7. ADO.NET基础

    ADO.NET类集合主要由五大核心组件类组成: Connection(数据库连接) Command(数据库的命令) DataReader(数据库的读取器) DataSet(数据集) DataAdaper(数据库的适配器)对象 说明Connection 用于提供与数据库的连接功能Command 返回数据、运行存储过程及发送或检索参数…...

    2024/4/27 15:26:32
  8. JavaSE

    JavaSEjava基础Java语言概述 java基础 Java语言概述...

    2024/4/27 14:41:08
  9. [LeetCode] 12、整数转罗马数字

    题目描述 给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。(还是那13种组合) 输入: 58 输出: "LVIII" 解释: L = 50, V = 5, III = 3.解题思路 这道题能想到用贪心算法,是来源于生活中的经验。对于这道问题,类似于用最少的纸币凑成一个整数,贪…...

    2024/4/27 15:50:01
  10. CIO40:你以为你是CIO?你应该是销售

    有数据显示,到2020年全球数字化转型相关行业增加值将达到10万亿美元,全球1000强企业中的67%、中国1000强企业中的50%都会把数字化转型做为企业的战略核心。目前国内80%的企业仍处于数字化转型的探索期,而60%以上的企业数字化进程仍受阻于IT实施的不足。针对未来企业的发展趋…...

    2024/4/27 13:39:27
  11. KMP

    前缀表 + kmp求前缀表的核心就是 知道在 字符串‘’A B … C D‘’中求 最长公共前后缀长度 已知 A B互相相同,C D 互相相同, (A + B)和(C+D)互相相同 得到A和D互相相同prefix_table右移一下,扫描文本串时,pattern串,根据prefix_table的下标位置来改变指向pattern串的指针#incl…...

    2024/4/27 17:58:58
  12. 剑指Offer-03——用两个栈实现队列

    问题描述: 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。在完成本题之前,首先需要明确栈和队列的机制。栈:LIFO(last in first out)先入后出。 队列:FIFO(first in first out)先入先出。解题思路描述: 一个先进先出,一个先进后出。顺序…...

    2024/4/27 16:31:07
  13. 【C++】强制类型转换

    C++为了加强类型转换的可视性,引入了四种强制类型转换操作符: static_cast、reinterpret_cast、const_cast、dynamic_cast 最好不使用强制类型转换!!!!!一.static_caststatic_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但…...

    2024/4/13 1:08:45
  14. docker-宿主机器与docker容器文件复制

    docker-宿主机器与docker容器文件复制 从宿主机器复制文件到docker容器 # 将宿主机器中的demo.zip文件复制到窗口的`/opt/soft`路径下 docker cp demo.zip container_id:/opt/soft从docker容器复制文件到宿主机器 # 将docker窗口中的demo.zip复制到宿主机器`/usr/local/`路径下…...

    2024/4/27 18:30:50
  15. Perforce与Unreal Engine4的连接、集成

    Perforce与Unreal Engine 4的集成 链接1描述了关于Epic Games Launcher的安装,链接2 描述了关于Perforce的安装 1 https://blog.csdn.net/weixin_42182599/article/details/103883033 2 https://blog.csdn.net/weixin_42182599/article/details/104009997Step1:打开cmd.exe,输…...

    2024/4/27 16:10:35
  16. Docker下安装部署Tomcat

    文章目录获取Tomcat镜像创建部署目录运行Tomcat容器部署项目 使用Docker来安装部署Tomcat可以随时使用最新的Tomcat服务器版本,不需要在本机安装Tomcat服务器,尤其是当使用不同版本的Tomcat服务器时,更是可以随时切换。 获取Tomcat镜像 通常,我使用官方发布的镜像,可使用如…...

    2024/4/27 19:05:16
  17. 修改 Flask 中 request.args 不可变字典类型

    Flask 里面的 request.args 请求回来的参数是 ImmutableMultiDict 类型,ImmutableMultiDict 是不可变字典,这是为了保证传参的数据安全性。但是某些时候我们需要对其进行操作,例如:将参数中为空串的参数去掉,这时候我们可以使用 dict 将其转变为普通的字典 # data type is…...

    2024/4/27 16:30:15
  18. 贝塞尔曲线路径规划在线绘制工具

    在做游戏开发的时候经常会用到贝塞尔曲线来规划路径,并且在网上也没找到合适的demo,要么就是不支持高阶贝塞尔,要么就是不能匀速运动。所以决定趁着闲余时间自己写一个工具,方便以后用。 并且我已经把源码放在GitHub上了,有兴趣的可以看看,希望能帮到有所需要的朋友。 在…...

    2024/4/27 14:29:05
  19. Loadrunner录制脚本的两种方式

    一、两种录制方式比较HTML录制方式:URL录制方式:Loadrunner默认是HTML方式录制脚本二、两种录制方式优点对比:(一)HTML 录制优点:减少了捕获动态值的需要。(1)资源从内存中取出且在回放时下载。因此,脚本比其他的录制方式更小且更容易阅读。(2)由于只有较少的硬编码…...

    2024/4/1 18:23:58
  20. 动手学深度学习(Pytorch版)学习笔记(三):DL元素之一:激活函数

    这一系列的学习笔记是基于李沐等人的开源书《动手学深度学习》总结和扩展而来。感谢Pytorch版项目原作者:项目地址。个人梳理了一下学习路线,并在知识体系中扩展了一些其他的东西,例如:讨论损失函数; 图卷积网络 ; 对抗生成网络; 注意力机制;模型压缩; 轻量化网络等等…...

    2024/4/1 11:49:07

最新文章

  1. 如何用Python实现智能客服问答系统

    随着人工智能技术的不断发展&#xff0c;机器人客服与聊天系统成为了热门话题。Python作为一种简单易学、功能强大的编程语言&#xff0c;在机器人客服与聊天系统的开发中具有广泛应用。 本文将介绍如何使用Python实现机器人客服与聊天系统&#xff0c;包括实现方式、代码示例和…...

    2024/4/27 19:14:08
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 零基础 HTML 入门(详细)

    目录 1.简介 1.1 HTML是什么? 1.2 HTML 版本 1.3 通用声明 2.HTML 编辑器 3.标签的语法 4.HTML属性 5.常用标签 5.1 head 元素 5.1.1 title 标签 5.1.2 base 标签 5.1.3 link 标签 5.1.4 style 标签 5.1.5 meta 标签 5.1.6 script 5.2 HTML 注释 5.3 段落标签…...

    2024/4/22 16:14:13
  4. 图像处理相关知识 —— 椒盐噪声

    椒盐噪声是一种常见的图像噪声类型&#xff0c;它会在图像中随机地添加黑色&#xff08;椒&#xff09;和白色&#xff08;盐&#xff09;的像素点&#xff0c;使图像的质量降低。这种噪声模拟了在图像传感器中可能遇到的问题&#xff0c;例如损坏的像素或传输过程中的干扰。 椒…...

    2024/4/23 15:25:06
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/26 18:09:39
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/26 20:12:18
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/27 4:00:35
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/26 21:56:58
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/27 9:01:45
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/25 18:39:16
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/26 22:01:59
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/25 2:10:52
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/25 18:39:00
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; 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
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在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