记录--“非主流” 的纯前端性能优化

news2025/1/15 17:24:42

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

性能优化一直是前端研究的主要课题之一,因为不仅直接影响用户体验,对于商业性公司,网页性能的优劣更关乎流量变现效率的高低。例如 DoubleClick by Google 发现:

  • 如果页面加载时间超过 3 秒,53% 的用户会选择终止当前操作并离开
  • 网站加载时间在 5 秒内的发布商比 19 秒内的广告收入至少多出一倍

同时,性能优化学习的不断深入,也同样是一个专业前端工程师的进阶之路。不过,随着 HTTP/2 和 SSR(服务端渲染)的不断普及,早期雅虎 35 条中的很多内容似乎已经显得有些过时,不少纯前端的细节优化方案也逐渐被认为微不足道。

但是,今天,我们依然想谈几个容易被很多前端工程师忽视,但却卓有成效的纯前端优化细节(技术框架以 Vue 为主)。

一、self

这里想说的 self 并不是  WindowOrWorkerGlobalScope 下的 self,或者说 window 的替身,而是 const self = this  中的 self,或者说对象缓存。

在几乎所有数据类型皆对象的 JavaScript 中,能有效降低属性访问深度的对象缓存是前端优化最基础的课程,即使在浏览器已经进化到即使没有明确地声明缓存对象,内核解析时也会自动缓存以增加解析效率的今天。

良好的对象缓存不仅仅只是为了避免写出下面的代码:

const obj = {
        human: {
                man: {}
        }
}
 
obj.human.man.age = 18
obj.human.man.name = 'Chen'
obj.human.man.career = 'programmer'

还有一个更加重要的原因:有效减少工程上线时压缩后的代码量!

首先,看一下上面代码压缩后的结果:

var ho={human:{man:{}}};ho.human.man.age=18,ho.human.man.name="Chen",ho.human.man.career="programmer";

然后,对属性对象 man 做一次变量缓存:

const obj = {
        human: {
                man: {}
        }
}
const man = obj.human.man
 
man.age = 18
man.name = 'Chen'
man.career = 'programmer'

再次压缩代码后的结果:

var ho={human:{man:{}}},yo=ho.human.man;yo.age=18,yo.name="Chen",yo.career="programmer";

可以看到,对象缓存使得代码容量有了明显的减少。

那么,对于实际的项目,变量缓存对总体代码又会带来多大容量的缩减呢?回到小节讨论的开始,我们一起感受一下不缓存的 this 对象带来的直观震撼吧。

vivo 某个项目的一个 js 文件:

整个文件存在 3836 个 this,保存到本地大概 375 KB。如果缓存 this,代码压缩时 4 个字符的 this 会被压缩成单字符变量。

整个文件的存储大小降低到 364 KB,一个 this 对象缓存即可让压缩后的代码容量下降超过 10 KB,注意,仅仅只是一个 this 对象!

二、Object.freeze ()

我们知道,在 Vue 组件或者 Vuex 的 state 中定义的数据是响应式的,当这些数据发生改变时,会通知 View 层更新界面。

首先,简单回忆一下 Vue 响应式数据的原理,如下图。

其中:

每一个组件 component 都拥有一个自己的观察者 watcher,内部封装了 Vue.prototype._render () 函数

每一个响应式数据属性都拥有一个自己的依赖 dep 收集器,用以收集依赖该数据的组件的 watcher

响应式数据的三个基本步骤:

(1)组件数据的响应化流程:component (options) -> observe (data) -> Reactive Data

  • component 的数据部分,所有的 options.data 属性通过 observe () 中的 Object.defineProperty () 函数转换成访问器属性
  • 在每一个数据属性被 Object.defineProperty () 转换时的函数闭包空间中,存在一个自己的 dep 收集器

(2)响应式数据的依赖收集流程:component (template) -> watcher (vm._render ())(get) -> Reactive Data

  • component 的模板字符串,通过 Vue compiler 后生成渲染函数 vm._render ()

  • 每一个 component 拥有一个自己的观察者 watcher,watcher 中封装了 vm._render (),组件初次渲染时:

    (a)watcher 实例暂存在 Dep.target 属性上

    (b)watcher 执行 vm._render () 函数,并进一步触发 vm._render () 所依赖数据属性的 getter

    (c)watcher 实例被收集到其所有依赖数据属性的 dep 收集器中

(3)响应式数据改变时的重新渲染流程:Reactive Data (set) -> dep 收集器 -> watcher (vm._render ()) -> 异步队列

  • 当响应式数据被修改时,触发数据属性的 setter 函数
  • 数据属性的 setter 函数会促使 dep 收集器将其收集的所有 watcher 实例推入异步队列 queueWatcher
  • 异步队列会被整体放入 nextTick () 中,即在下一个 tick 时被一次性全部执行;其实在 watcher () 中,渲染函数 vm._render () 是被封装到 vm._update () 中的,它在执行时,会首先通过 vnode 的 diff 算法比对找到修改的最少步骤,然后将最小的差异化渲染到页面
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
...
    // 如果没有旧的虚拟节点 prevVnode,表示是初次渲染,直接渲染到页面
    if (!prevVnode) {
        // initial render
        vm.$el = vm.__patch__(
            vm.$el, vnode, hydrating, false /* removeOnly */,
            vm.$options._parentElm,
            vm.$options._refElm
        )
         
    // 非初次渲染,数据修改导致需要更新页面时,进行 vnode diff 后将最小的差异化渲染到页面
    } else {
        // updates
        vm.$el = vm.__patch__(prevVnode, vnode)
    }
...
}

每一个响应式数据对象属性都一定会经历三个基本步骤中的 1 和 2,不过,很多属性在应用的整个生命周期中可能都不会经历步骤 3,因为它们始终没有改变。

但是,需要注意的是:之所以 Vue 会进行步骤 1 和 2 的操作,其实主要就是为了步骤 3 做准备,如果步骤 3 得不到执行,那么前两步的操作就是无意义的,或者说浪费。是否有方式避免这种浪费呢?有,就是 Object.freeze ()。

在将普通数据转变成响应式数据的核心函数 defineReactive(Vue 2.6.x src/core/observer

/index.js) 中,有一个判断,如果属性本身不是 configurable 的,则不会被转化成响应式数据,即不会执行上面的流程 1,与此同时,非响应式的数据也自然不会执行流程 2。

/**
 * Define a reactive property on an Object.
 */
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()
 
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }
   
  ...
}

对于整个应用生命周期中,不会改变的数据,可以使用 Object.freeze () 将其 configurable 属性置为 false;或者,将整个数据对象都 freeze 掉:

/**
 * 深度冻结对象
 */
function deepFreeze(obj) {
  Object.keys(obj).forEach(key => {
    const prop = obj[key]
 
    typeof prop === 'object' && prop !== null && deepFreeze(prop)
  })
 
  return Object.freeze(obj)
}

然后,“解冻” 部分需要改变的数据,并将其转换成响应式数据。

注意,如果解冻的属性值是对象,不能通过简单地赋值 “解冻” 该对象,因为对象的引用传递特性导致其 configurable 依然是 false。可以使用下面的简单深复制方法,让源对象丢失 configurable 属性:

/**
 * 简单对象深复制
 * -- 子对象引用关系丢失
 * -- 不适合循环引用数据
 */
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj))
}

对于 Object.freeze () 带来的性能提升,Vue 官方的一个 big table benchmark 里,做了一个 1000 x 10 的表格渲染对照实验,使用 Object.freeze () 的渲染速度比不使用时快了 4 倍。

三、Pre 机制

浏览器的 pre(预)机制。

由于可动态修改 DOM 的天然属性,JavaScript 不仅本身的执行是单线程的,而且其加载 / 解析执行时 HTML 的解析也是停止的,甚至在早期的浏览器中,其它资源的加载线程也会被同时阻止。

例如,在 IE7 中,页面的瀑布流:

其他资源的加载、解析、执行不能和 JavaScript 的加载执行并行,这导致了页面的加载时间很长。为了提高网络利用率,后来的主流浏览器都实现了预加载机制,即解析 HTML 页面的同时,启动一个轻量级解析器优先扫描 HTML 中的所有标记,寻找样式表、脚本、图像等静态资源,尽可能地并行加载它们。

IE8 中的页面瀑布流:

可以很明显地看到,静态资源被尽可能的并行加载了,即使在脚本加载解析的时候。

不过,随着 Web 应用的越加复杂化,CSS 和 JavaScript 资源容量也越来越大,很多资源并不是一开始就出现在 HTML 中,而是后期被 CSS 和 JavaScript 动态引入的。为了尽可能提前解析 / 加载这些资源,浏览器开始提供丰富的 pre 机制。

1、Preload

浏览器内核的预加载机制只适用于在 HTML 中显式声明的资源,对于 CSS 和 JavaScript 中定义的资源可能并不起作用。preload 很好地克服了这个问题,可以通过 preload 标识需要浏览器提前加载的重要资源,例如样式表、脚本、图片、字体甚至文档。

# 预加载 css
<link rel="preload" as="style" href="/assets/css/app.css">
 
# 预加载 js
<link rel="preload" as="script" href="/assets/js/app.js">
 
# 预加载图片
<link rel="preload" as="image" href="/assets/images/man.png">
 
# 预加载字体
<link rel="preload" as="font" href="/assets/font/rom9.ttf">

2、Prefetch

Prefetch 是一个低优先级的资源提示,允许浏览器在后台(空闲时)获取将来可能用得到的资源,并且将他们存储在浏览器的缓存中。有三种不同的 prefetch 类型:

(1)Link Prefetching:允许浏览器获取资源并将他们存储在缓存中。

  • HTML
<link rel="prefetch" href="/uploads/images/pic.png">
  • HTTP Header
Link: </uploads/images/pic.png>; rel=prefetch

(2)DNS Prefetching:允许浏览器在用户浏览页面时在后台运行 DNS 解析。

可以在一个 link 标签的属性中添加 rel="dns-prefetch' 来对指定进行 DNS prefetching 的 URL:

<!-- 域名 dns-prefetch -->
<link rel="dns-prefetch" href="//sthf.vivo.com.cn">
<link rel="dns-prefetch" href="//apph5wsdl.vivo.com.cn">
<link rel="dns-prefetch" href="//cfg-stsdk.vivo.com.cn">
<link rel="dns-prefetch" href="//trace-h5sdk.vivo.com.cn">
<link rel="dns-prefetch" href="//topicstatic.vivo.com.cn">

DNS 请求在带宽方面流量非常小,可是延迟会很高,尤其是在移动设备上。

(3)Prerendering:和 prefetching 非常相似,优化可能资源的加载,区别是 prerendering 在后台渲染整个未来可能加载的页面。

<link rel="prerender" href="https://www.vivo.com.cn">

这三种类型中,Link Prefetching 和前文的 preload 比较相似,但是优先级较低,而且更加专注于下一个页面;Prerendering 会预渲染一个用户不一定访问的完整页面,这会导致较高的带宽浪费和资源占用,应用的机会可能并不多;而 DNS Prefetching 是当前我们应用最多的。

在浏览一个网页时,DNS 解析总是发生在一个新域名初次被解析的时候,如果域名解析是独立串行的(如页面主域的解析),解析时间的长短(如下图中的 vivo 游戏大会员 supermember.vivo.com.cn)将直接影响页面的打开速度。得益于现代浏览器的预加载机制,除页面主域以外的其他资源域名的解析时间,一定程度上很好地掩蔽在了资源的并行加载过程中。

但是,dns 的解析并不一定是稳定可靠的,时间跨度从几十 ms 至过千 ms 都有可能,如果页面主要资源的 dns 解析时间过长,就会直接影响用户的使用体验,所以,恰当的 DNS Prefetching 依然很有必要。

3、Preconnect

相比于 DNS Prefetching,Preconnect 除了提前完成域名的 DNS 解析,还更近一步地完成 http 连接通道的建立,这包括 TCP 握手,TLS 协商等。

使用方法:

<!-- 域名 preconnect -->
<link rel="preconnect" href="//sthf.vivo.com.cn">
<link rel="preconnect" href="//apph5wsdl.vivo.com.cn">
<link rel="preconnect" href="//cfg-stsdk.vivo.com.cn">
<link rel="preconnect" href="//trace-h5sdk.vivo.com.cn">
<link rel="preconnect" href="//topicstatic.vivo.com.cn">

可以同时设置 Preconnect 和 DNS Prefetching,让浏览器优先进行 Preconnect,在不支持的前提下,优雅回退至 DNS Prefetching。

四、并行加载

随着 Web 应用的复杂化大型化,使用 MV* 类框架( Vue、React、Angular 等)进行快捷开发已经成为前端开发的主流模式。但是,这些框架都存在基础框架包较大,解析时间较长的问题。

首先,我们看一个标准的 Vue 项目 - vivo 游戏大会员 Chrome 开发者工具中的瀑布流:

可以看出资源的加载存在明显的层级结构:

  • 第 1 级:获取页面 HTML 文档并解析
  • 第 2 级:获取页面 CSS 和 JavaScript 文件并解析
  • 第 3 级:请求接口获取服务端数据
  • 第 4 级:页面渲染加载主页图片等资源

同时,可以发现由于 JavaScript 文件较大,解析时间较长,第 2 级与第 3 级,以及第 3 级和第 4 级之间的时间间隔较大。如果这种串行的逐级解析加载模式能够改变为并行的加载模式,势必将显著降低页面的加载时长。

注意,如果项目未开启 HTTP/2,可能需要增加资源域名以突破浏览器对单个域名并行下载数量的限制。当然,在下面实现并行加载的过程中,我们也使用了很明显的反模式 - 通过 window 全局变量传递数据。不过,在没有更好的实现方案前,通过有限可控的反模式实现更好的页面体验还是值得的。

下面,我们讨论如何将串行加载的资源变成并行加载。

1、接口

大多数时候,接口的请求并不需要等待 Vue.js 加载解析完成,完全可以在 HTML 中通过几行简单的 JavaScript 代码提前进行 Ajax 请求。

/**
 * 主接口请求前置
 */
var win = window
var xhr = new XMLHttpRequest()
 
xhr.open('get', '/api/member/masterpage?t=' + Date.now(), true)
xhr.onerror = function () { win._mainPageData = { msg: '请求出错', code: 10000 } }
xhr.timeout = 10000
xhr.ontimeout = function () { win._mainPageData = { msg: '请求超时', code: 10001 } }
xhr.onreadystatechange = function () {
  try {
    var status = xhr.status
 
    if (xhr.readyState == 4) {
      win._mainPageData = (status >= 200 && status < 300) || status == 304
      ? JSON.parse(xhr.responseText)
      : {
          msg: '',
          code: 10002
        }
    }
  } catch (e) { /* 请求超时时readyState可能也是4,但是访问status可能出错 */ }
}
xhr.send(null)

需要注意的是,直接插入到 HTML 中的 JavaScript 可能不会通过 babel 的编译,所以不要使用 ES6 语法,因为很可能一个简单的 const 就会让 Android 5/4.4.4 直接白屏。

2、图片

通常,Web 应用主页首屏会有几张装饰性且容量较大的图片,将图片写在 Vue 组件中,图片的加载会推迟到组件解析完成,我们同样可以在 HTML 中提前加载这些图片。

一种方式是使用前文 Pre 机制中提到的 Preload:

<link rel="preload" as="image" href="/assets/images/00.png">
<link rel="preload" as="image" href="/assets/images/01.png">
<link rel="preload" as="image" href="/assets/images/02.png">

尽管 Preload 拥有更简洁且不阻塞页面渲染的优点,但是这种方式当前依然存在两个明显的问题:

(1)低版本 Android 不支持 Preload

(2)如果项目需要判断环境是否支持 webp 格式,以便有区分地加载图片的 webp 格式和普通格式,Preload 就不好办了,除非你两种格式都加载,但很明显这样会造成严重的流量浪费。

所以,我们可以使用 JavaScript 代码在判断环境是否支持 webp 格式后,加载需要格式的图片:

/**
 * webp 探测
 */
var win = window
var doc = document
 
win._supportsWebP = (function () {
    var mime = 'image/webp'
  var canvas = typeof doc === 'object' ? doc.createElement('canvas') : {}
  canvas.width = canvas.height = 1
  return canvas.toDataURL ? canvas.toDataURL(mime).indexOf(mime) === 5 : false
}())
 
/**
 * 图片预加载
 */
var body = doc.body
var parentNode = document.createDocumentFragment()
var imgPostfix = '.png' + (win._supportsWebP ? '.webp' : '')
var linkPrefix = '//topicstatic.vivo.com.cn/f5ZUD0HxhQMn3J32/wukong/img/'
var imgPreLoad = win._imgPreLoad = [
  linkPrefix + '5f88483c-4d76-42d4-912d-35c8c92be8e6' + imgPostfix,
  linkPrefix + '5ee4c220-cd98-4d8c-9cdc-5fca3e103227' + imgPostfix,
  linkPrefix + '131008e1-9230-480c-934a-30f9f83e17ae' + imgPostfix,
  linkPrefix + 'cee41d4d-853d-4677-9a20-b9b5e1c4ffbenwebp' + imgPostfix,
  linkPrefix + 'ddf2cad0-d334-437a-8923-7b36a65544d1nwebp' + imgPostfix
]
 
imgPreLoad.forEach(function (link) {
  var img = doc.createElement('img')
 
  img.src = link
  img.style.left = '-9999px'
  img.style.position = 'absolute'
 
  parentNode.appendChild(img)
})
 
body.insertBefore(parentNode, body.firstChild)

此外,在合适的时候,可以尝试使用 svg 图片,除了永不失真的图片质量,更重要的是,svg 可以很好地打包到代码中,并始终保持比 base64 更好的可读性。

3、字体

有的时候,为了实现更好的视觉效果,并能应对动态变化的接口数据,我们会引入一些系统不支持的字体,比如数字字体 Rom9。

不过,我们可能只是用到字体中的某一部分,比如数字,此时除了使用字体编辑软件删除不需要的字符外,我们还可以将字体 base64 化后整合到 CSS 中以便更好地并行加载:

@font-face{
    src: url(data:font/truetype;charset=utf-8;base64,AA...省略...AK) format("truetype");
    font-style: normal;
    font-weight: normal;
    font-family: "Rom9";
    font-display: swap;
}

本文转载于:

https://my.oschina.net/vivotech/blog/4633382

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/162543.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MD5加密

MD5加密 md5加密 明文 加密变成 128位二进制 --> 32位16进制字符串的密文 MD5特征: 明文一样, 得到密文一样密文一样, 推出明文一样明文不一样, 得到密文不一样 缺点&#xff1a; 现在网上有很多暴力破解的网址&#xff0c;直接使用md5加密还是不太安全 为了提高安全性&am…

Linux常用命令——vmstat命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) vmstat 显示虚拟内存状态 补充说明 vmstat命令的含义为显示虚拟内存状态&#xff08;“Viryual Memor Statics”&#xff09;&#xff0c;但是它可以报告关于进程、内存、I/O等系统整体运行状态。 语法 vmst…

Java高并发编程实战,异步注解@Async自定义线程池

一、Async注解 Async的作用就是异步处理任务。 在方法上添加Async&#xff0c;表示此方法是异步方法&#xff1b; 在类上添加Async&#xff0c;表示类中的所有方法都是异步方法&#xff1b; 使用此注解的类&#xff0c;必须是Spring管理的类&#xff1b; 需要在启动类或配置类…

ELK日志(1)

Elasticsearch开源分布式搜索引擎&#xff0c;它的特点有&#xff1a;分布式&#xff0c;零配置&#xff0c;自动发现&#xff0c;索引自动分片&#xff0c;索引副本机制&#xff0c;restful 风格接口&#xff0c;多数据源&#xff0c;自动搜索负载等。RESTFUL特点包括&#xf…

MES系统之工控

MES系统之工控 要控制MES系统首先要对他有个了解。MES系统最早由1990年&#xff0c;由美国先进制造研究中心AMR提出的&#xff0c;当时中文意思叫制造执行系统概念。直到1997年&#xff0c;MESA(制造执行系统协会)提出了MES功能组件和继承模型&#xff0c;到20世纪90年代初期&a…

动态内存管”家“

&#x1f40b;动态内存管理&#x1f996;动态内存分配存在的意义&#x1f996;动态内存函数的介绍&#x1f424;malloc和free&#x1f424;calloc&#x1f424;realloc&#x1f996;常见动态内存错误&#x1f424;对空指针的解引用操作&#x1f424;对动态开辟空间的越界访问&a…

springMVC的响应

SpringMVC接收到请求和数据后&#xff0c;进行一些了的处理&#xff0c;当然这个处理可以是转发给Service&#xff0c;Service层再调用Dao层完成的&#xff0c;不管怎样&#xff0c;处理完以后&#xff0c;都需要将结果告知给用户。 对于响应&#xff0c;主要就包含两部分内容&…

关于 sensor hdr 模式下不出图/出图异常的排查方法

1、问题背景&#xff1a;有项目调试过 ov02k10&#xff08;1920*1080&#xff09;和 sc301IoT&#xff08;2048*1536&#xff09;两款 sensor, 都有出现 hdr 模式下出图异常或者不出图的问题&#xff0c;总结下排查过程及注意事项&#xff1b;2、问题现象&#xff1a;a、ov02k1…

Odoo 16 企业版手册 - 库存管理之寄售

寄售 使用“「设置」”菜单下提供的「寄售」选项&#xff0c;可以对库存中储存的产品设置所有者。产品将由零售商销售&#xff0c;但产品的实际所有权将由供应商持有&#xff0c;直到产品出售给客户。通过这种方法&#xff0c;您可以轻松地将未售出的产品退还给供应商。在寄售的…

java对接阿里云短信服务详解(验证码,推广短信,通知短信)

前言 小前提&#xff1a; - java&#xff1a;springboot框架&#xff0c;maven版本管理。 - 阿里云&#xff1a;有账号&#xff0c;已经进行实名认证。 java对接阿里云短信服务详解&#xff08;验证码&#xff0c;推广短信&#xff0c;通知短信&#xff09;前言1. 登录阿里云进…

基于servlet+mysql+jsp实现体育用品商城

基于servletmysqljsp实现体育用品商城一、系统介绍1、系统主要功能&#xff1a;2、环境配置二、功能展示1.主页(客户)2.登陆&#xff08;管理员&#xff09;3.主页&#xff08;管理员&#xff09;4.订单管理&#xff08;管理员&#xff09;5.客户管理&#xff08;管理员&#x…

linux系统结构

目录 0.前言 1.系统结构图 1.1.操作系统工作方式 1.2.高版本和低版本内核区别 0.前言 本专栏&#xff0c;是记录内核学习的&#xff0c;参考b站linux内核源码分析&#xff0c;以及linux内核艺术图解。后面的文章将记录个人的学习&#xff0c;源码注释&#xff0c;源码理解…

ANSYS Products 2020 R1 Linux64版本安装

fluent系列 占位 fluent2020R1版本安装fluent系列前言一、基础环境二、安装准备1.图形化环境准备2.路径准备3.挂载安装用iso4.拷贝安装文件三、开始安装1.进入图形化界面2.开始安装3.试运行fluent四、替换破解版的license总结前言 在centos7环境下安装使用fluent的部署记录。…

不用if else if 如何 解决文末尾问题

根据条件判断发送axios所携带的参数&#xff0c;这是搜索的2个条件&#xff0c;如果为空就按照空这个条件来搜索&#xff0c;所以为空携带参数就不能有他&#xff0c;导致if else if 的连续判断 开始来没有思路&#xff0c;随便尝试尝试&#xff0c;来打开自己的思路 期间尝…

【学习经验分享NO.20】代码报错(可帮助远程调试代码)

本博客会整理分享一些报错问题以及解决办法&#xff0c;本文会不断进行更新。有需求的朋友可以关注私信我&#x1f618;进行远程调试。&#x1f349;1.报错1问题nn.functional.sigmoid is deprecated. Use torch.sigmoid instead.解决办法将项目中的F.sigmoid修改为torch.sigmo…

【docker16】Docker-Compose容器编排

1.是什么 Docker-Compose是Docker官方的开源项目&#xff0c;负责实现对Docker容器集群的快速编排。 Compose是Docker公司推出的一个工具软件&#xff0c;可以管理多个Docker容器组成一个应用&#xff0c;你需要定义一个YAML格式的配置文件docker-compose.yml&#xff0c;写好…

JAVA导出Excel通用工具——第二篇:使用EasyExcel导出excel的多种情况的例子介绍

JAVA导出Excel通用工具——第二篇&#xff1a;使用EasyExcel导出excel的多种情况的例子介绍1. 前言2. 依赖3. 导出简单例子3.1 ① 基础入门例子3.1.1 核心代码3.1.2 效果展示3.2 ② 注解的简单使用3.2.1 ExcelIgnore3.2.2 ExcelProperty3.2.2.1 一般效果&#xff08;表头合并等…

MySQL高级【InnoDB引擎】

1&#xff1a;InnoDB引擎1.1&#xff1a;逻辑存储引擎 InnoDB的逻辑存储结构如下图所示: 1). 表空间 表空间是InnoDB存储引擎逻辑结构的最高层&#xff0c; 如果用户启用了参数 innodb_file_per_table(在 8.0版本中默认开启) &#xff0c;则每张表都会有一个表空间&#xff08…

【iOS】—— 初识block

block 文章目录block什么是block&#xff1f;block语法Block变量截获自动变量值__block说明符截获的自动变量block的三种存储类型NSGlobalBlockNSStackBlockNSMallocBlockblock的父类block循环引用未完待续什么是block&#xff1f; Blocks是带有自动变量&#xff08;局部变量&…

React--》初识React框架及其基本使用

目录 React React的安装与使用 JSX语法及使用 模块与组件 React开发者工具的安装 面向组件编程 React React是一个用于构建用户界面的JavaScript库。用户界面:HTML页面(前端)。React主要用来写HTML页面&#xff0c;或构建Web应用。 如果从 MVC的角度来看&#xff0c;…