2024年2月

正交化

这是一张老式电视图片,有很多旋钮可以用来调整图像的各种性质,所以对于这些旧式电视,可能有一个旋钮用来调图像垂直方向的高度,另外有一个旋钮用来调图像宽度,也许还有一个旋钮用来调梯形角度,还有一个旋钮用来调整图像左右偏移,还有一个旋钮用来调图像旋转角度之类的。电视设计师花了大量时间设计电路,那时通常都是模拟电路来确保每个旋钮都有相对明确的功能。如一个旋钮来调整这个(高度),一个旋钮调整这个(宽度),一个旋钮调整这个(梯形角度),以此类推。

相比之下,想象一下,如果有一个旋钮调的是
\(0.1x\)
表示图像高度,
\(+0.3x\)
表示图像宽度,
\(-1.7x\)
表示梯形角度,
\(+0.8x\)
表示图像在水平轴上的坐标之类的。如果调整这个(其中一个)旋钮,那么图像的高度、宽度、梯形角度、平移位置全部都会同时改变,如果有这样的旋钮,那几乎不可能把电视调好,让图像显示在区域正中。

所以在这种情况下,正交化指的是电视设计师设计这样的旋钮,使得每个旋钮都只调整一个性质,这样调整电视图像就容易得多,就可以把图像调到正中。

接下来是另一个正交化例子,想想学车的时候,一辆车有三个主要控制,第一是方向盘,方向盘决定往左右偏多少,还有油门和刹车。就是这三个控制,其中一个控制方向,另外两个控制的速度,这样就比较容易解读。知道不同控制的不同动作会对车子运动有什么影响。

想象一下,如果有人这么造车,造了个游戏手柄,手柄的一个轴控制的是
\(0.3×\)
转向角-速度,然后还有一个轴控制的是
\(2×\)
转向角
\(+0.9×\)
车速,理论上来说,通过调整这两个旋钮是可以将车子调整到希望得到的角度和速度,但这样比单独控制转向角度,分开独立的速度控制要难得多。

所以正交化的概念是指,可以想出一个维度,这个维度想做的是控制转向角,还有另一个维度来控制的速度,那么就需要一个旋钮尽量只控制转向角,另一个旋钮,在这个开车的例子里其实是油门和刹车控制了的速度。但如果有一个控制旋钮将两者混在一起,比如说这样一个控制装置同时影响的转向角和速度,同时改变了两个性质,那么就很难令的车子以想要的速度和角度前进。然而正交化之后,正交意味着互成90度。设计出正交化的控制装置,最理想的情况是和实际想控制的性质一致,这样调整参数时就容易得多。可以单独调整转向角,还有的油门和刹车,令车子以想要的方式运动。

那么这与机器学习有什么关系呢?要弄好一个监督学习系统,通常需要调的系统的旋钮。

确保四件事情,首先,通常必须确保至少系统在训练集上得到的结果不错,所以训练集上的表现必须通过某种评估,达到能接受的程度,对于某些应用,这可能意味着达到人类水平的表现,但这取决于的应用,将在后面更多地谈谈如何与人类水平的表现进行比较。但是,在训练集上表现不错之后,就希望系统也能在开发集上有好的表现,然后希望系统在测试集上也有好的表现。在最后,希望系统在测试集上系统的成本函数在实际使用中表现令人满意,比如说,希望这些猫图片应用的用户满意。

回到电视调节的例子,如果的电视图像太宽或太窄,想要一个旋钮去调整,可不想要仔细调节五个不同的旋钮,它们也会影响别的图像性质,只需要一个旋钮去改变电视图像的宽度。

所以类似地,如果的算法在成本函数上不能很好地拟合训练集,想要一个旋钮,是的画这东西表示旋钮,或者一组特定的旋钮,这样可以用来确保的可以调整的算法,让它很好地拟合训练集,所以用来调试的旋钮是可能可以训练更大的网络,或者可以切换到更好的优化算法,比如
Adam
优化算法,等等。

相比之下,如果发现算法对开发集的拟合很差,那么应该有独立的一组旋钮,是的,这就是画得毛毛躁躁的另一个旋钮,希望有一组独立的旋钮去调试。比如说,的算法在开发集上做的不好,它在训练集上做得很好,但开发集不行,然后有一组正则化的旋钮可以调节,尝试让系统满足第二个条件。类比到电视,就是现在调好了电视的宽度,如果图像的高度不太对,就需要一个不同的旋钮来调节电视图像的高度,然后希望这个旋钮尽量不会影响到电视的宽度。增大训练集可以是另一个可用的旋钮,它可以帮助的学习算法更好地归纳开发集的规律,现在调好了电视图像的高度和宽度。

如果它不符合第三个标准呢?如果系统在开发集上做的很好,但测试集上做得不好呢?如果是这样,那么需要调的旋钮,可能是更大的开发集。因为如果它在开发集上做的不错,但测试集不行这可能意味着对开发集过拟合了,需要往回退一步,使用更大的开发集。

最后,如果它在测试集上做得很好,但无法给的猫图片应用用户提供良好的体验,这意味着需要回去,改变开发集或成本函数。因为如果根据某个成本函数,系统在测试集上做的很好,但它无法反映的算法在现实世界中的表现,这意味着要么的开发集分布设置不正确,要么的成本函数测量的指标不对。

如果现在无法理解全部细节,别担心,但希望对这种正交化过程有个概念。要非常清楚,到底是四个问题中的哪一个,知道可以调节哪些不同的东西尝试解决那个问题。

当训练神经网络时,一般不用
early stopping
,这个技巧也还不错,很多人都这么干。但个人而言,觉得用
early stopping
有点难以分析,因为这个旋钮会同时影响对训练集的拟合,因为如果早期停止,那么对训练集的拟合就不太好,但它同时也用来改善开发集的表现,所以这个旋钮没那么正交化。因为它同时影响两件事情,就像一个旋钮同时影响电视图像的宽度和高度。不是说这样就不要用,如果想用也是可以的。但如果有更多的正交化控制,比如这里写出的其他手段,用这些手段调网络会简单不少。

所以希望对正交化的意义有点概念,就像看电视图像一样。如果说,的电视图像太宽,所以要调整这个旋钮(宽度旋钮)。或者它太高了,所以要调整那个旋钮(高度旋钮)。或者它太梯形了,所以要调整这个旋钮(梯形角度旋钮),这就很好。

在机器学习中,如果可以观察的系统,然后说这一部分是错的,它在训练集上做的不好、在开发集上做的不好、它在测试集上做的不好,或者它在测试集上做的不错,但在现实世界中不好,这就很好。必须弄清楚到底是什么地方出问题了,然后刚好有对应的旋钮,或者一组对应的旋钮,刚好可以解决那个问题,那个限制了机器学习系统性能的问题。

前言:

之前有网友说 Mvc系列的教程对新手不友好,因此补充新手入门系列教程。

在开始使用 Taurus.Mvc 进行 Web应用开发之前,建议可以观摩一下之前的文章:
WebAPI 系列教程

因为两者的教程,有相通的部分,唯一的差别部分,在于Web应用涉及到UI界面。

本系列的目录大纲为:

Taurus.MVC WebMVC 入门开发教程1:框架下载环境配置与运行

Taurus.MVC WebMVC 入门开发教程2:一个简单的页面呈现

Taurus.MVC WebMVC 入门开发教程3:数据绑定Model

Taurus.MVC WebMVC 入门开发教程4:数据列表绑定List
<Model>Taurus.MVC WebMVC 入门开发教程5:表单提交与数据验证

Taurus.MVC WebMVC 入门开发教程6:路由配置与路由映射

Taurus.MVC WebMVC 入门开发教程7:部分视图和页面片段

下面开始本系列教程第一篇:

项目的工程引入方式有两种:

1、引用源码(自带工程项目)

2、引用Nuget(纯dll引用)

支持 .Net 和.Net Core 双系列,而且是全量版本:

1、.Net Framework. 支持:.Net 2.0 及以上全系列

2、.Net Core. 支持: .Net Core 2.1 及以上全系列

1、引用源码方式一:下载框架源码:

开源地址:
https://github.com/cyq1162/Taurus.MVC

1.1、引用源码方式一: .Net 项目工程的使用:

进入目录: /demo/default/ 项目路径。

Taurus.MVC.sln :
.Net Framework版本,一般部署在 Window 平台,建议:VS2012 以上,当前最新VS2022。

框架的运行注意点:.Net 版本通过配置 HttpModules 引用 Taurus。

 <httpModules>
   <!--Taurus IIS应用程序池:经典模式(下运行,开启此配置,反之,注释掉此行)-->
 <add name="Taurus.Core" type="Taurus.Core.UrlRewrite,Taurus.Core"/>
 </httpModules>

查看:web.config 经典模式注册打开,保持和下图一致。

1.2、引用源码方式一: .Net Core 项目工程的使用:

Taurus.MVC_NetCore :
.NET Core 版本,一般部署在 Linux 平台,建议:VS2017 以上,当前最新VS2022。

框架的运行注意点:.Net Core 版本通过配置服务添加和使用引用 Taurus。

 public voidConfigureServices(IServiceCollection services)
{
services.AddTaurusMvc();
}
//This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)//把 IHostingEnvironment IWebHostEnvironment {
app.UseTaurusMvc();
}

如下图:

1.3、引用源码方式一:F5运行工程项目

正常成功运行时界面:(新版本新增:
Taurus Admin Center
,管理后台中心)


2.1、引用Nuget方式二:.Net 项目新建:

2.2、引用Nuget方式二:.Net Core 项目新建:

2.3、引用Nuget方式二:搜索并引入Nuget 包:

nuget管理中,搜索taurus,根据工程版本引用版本:

.NET Framework:Taurus.MVC

.NET Core :Taurus.MVC.Core

2.4、引用Nuget方式二:配置Mvc引入工程

1、.Net 工程项目,在 Web.Config 配置引用:

复制代码
<configuration>
  
  <system.web>
   
    <httpModules>
      <!--Taurus IIS应用程序池:经典模式(下运行,开启此配置,反之,注释掉此行)-->
    <add name="Taurus.Core" type="Taurus.Core.UrlRewrite,Taurus.Core"/>
    </httpModules>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules>
      <!--Taurus IIS应用程序池:集成模式(下运行,开启此配置,反之,注释掉此行)-->
      <add name="Taurus.Core" type="Taurus.Core.UrlRewrite,Taurus.Core"/>

    </modules>
  </system.webServer>
</configuration>
复制代码

2、.NET Core 项目,通过Startup.cs 配置入口:

复制代码
 public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTaurusMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseTaurusMvc();
        }
    }
复制代码

3、NET5、NET6、NET7、NET8...系列,通过Program.cs 配置入口:

复制代码
var builder = WebApplication.CreateBuilder(args);

//补上Starpup的ConfigureServices:
builder.Services.AddTaurusMvc();
var app = builder.Build();

app.UseTaurusMvc();

app.Run();
复制代码

2.5、引用Nuget方式二:F5运行工程项目

旧版本:运行成功:提示需要控制器进行编码,或输出 404 - Not found.

新版本(
>=V3.3.1
):采用共存模式,没有可处理的控制器项,默认不处理,权限交给下一个模块、或下一个中间件。

默认框架无处理事件,需要显示内容,需要添加控制器、界面、和数据,详见下一篇。

总结:

在本篇教程中,我们学习了如何将 Taurus.MVC  框架引入工程项目中,并最终成功运行了应用程序。

在下一篇教程中,我们将继续探索 Taurus.MVC WebMVC 框架的更多功能和用法!

见下一篇,一个简单的Mvc页面显示。

大家好,我是 陶朱公Boy ,一个认真生活,总想超越自己的程序员。

前言

知乎上有一个提问:在做程序员的道路上,你掌握了什么概念或技术使你感觉自我提升突飞猛进?
↓↓↓

今天,我们就这个话题一起来做个讨论。

我的回答

春节期间,我写过一篇与今天主题较相近的一篇文章: 你的编程能力从什么时候开始突飞猛进的? 真诚的与大家分享了我多年从小公司到一线互联网公司积淀的编程经验。(目前该文阅读量已突破1W+,得到了不少小伙伴的认可,感兴趣的小伙伴可以进去看看。)

今天,我也是一如既往结合自己过去多年积淀的编程经验,来谈下我对于这个问题的观点,希望大家喜欢。

我将从如下三个方面来展开细说:

一、
理论结合实践

我的观点认为,程序员要想提升自我的编程能力,几乎没有任何捷径可走,还是那句老生常谈的老话:多做项目多看书。

当然这里顺序一定不能搞错,不然学习效果可能会差强人意。这也是为什么我们捧着那一本本厚厚的计算机书籍经常犯困的原因。

如果你在之前没有写过一定数量的业务代码、没有使用过相应框架或中间件解决你实践场景中遇到的问题,而一味投进知识的海洋去遨游,过一会你就会发现,根本就坚持不下去,里面的内容实在太过枯燥,找不到感觉。

但当你做过一系列大大小小的编程项目,解决过一系列线上问题(无论上简单的业务问题亦或是框架乃至各种性能问题),一段时间后,你再回过头去看那些理论书籍,你就会发现,之前看不进去的一些书籍,现在完全不会觉得无聊,也不会觉得难懂,甚至看到某一处,你会有一种醍醐灌顶的感觉,这个时候看书的效果就出来。

所以我的观点认为,要想学好编程,我们最好先有一定的项目实践经验,然后再用书籍巩固我们的基础知识,这样学习的效果会事倍功半。

关于计算机相关的经典计算机书籍,如果你还不知道该阅读哪些或知道但苦于不知从何处找寻,这里我把我积累多年的本地藏书文件夹分享给你,希望能帮你节省找书籍的时间,资料不贵,你的时间最宝贵!

书籍详情请点击 :
那些年,我书架上的几本经典计算机书籍!

二、
做复杂度高的项目

在这里我想述说的是,我们程序员小伙伴如果有条件,尽量多做些业务复杂度高的项目。
复杂度低的项目包含一些企业内XX管理系统或一些流量极低的网站。这类项目的特点是业务场景单一且功能简单,用户规模小,流量几乎没有。
由于业务场景单一且功能简单,你只要负责写完一些简单的CRUD功能就算完事。压根不存在由于后期业务功能的不断升级与复杂度上升,代码需要做各种重构。也接触不到因为多线程、高并发场景下而触发的各种性能问题,比如慢SQL、服务gc频繁甚至OOM,CPU100%等各种性能问题。
所以常年待在这样的环境下,你的成长注定受挫,时间一长,就会失去核心竞争力。

三、多给自己设目标,多去挑战自己

在我的职业生涯中,对我成长最快、帮助最大的其中一个窍门就数“准备面试、出去面试了”。
可能有小伙伴会有疑惑,我对我目前的公司还算满意,没有跳槽的打算,出去面试干嘛?
我的观点认为面试的目的不光服务于跳槽,它还能成就你更多。
在准备面试的过程中,通过你自己不断的梳理、构建对应的“知识树”(结合面试过程中碰到的一个又一个你当时答不上来的问题),一定程度能帮你查漏补缺知识盲区。
当然更大的作用我认为它能帮你检阅你目前的水平处于什么位置(外面的技术栈你是否都会,还是一问三不知,原因是公司技术太老套,10年不更新),也能帮你review在这家公司你领到的这份薪水相对于行业平均水平是高了还是低了等等。
所以我是比较主张我们程序员小伙伴没事多出去面试,不一定为了跳槽,而是帮助你补齐知识盲区的同时适当的检阅一下自己目前知识的掌握情况和了解清楚自己的薪资水位是否健康,绝对对你百利而无一害。

小结

OK,今天的分享已接近尾声,让我简单做个小结:
我认为关于“在做程序员的道路上,你掌握了什么概念或技术使你感觉自我提升突飞猛进?”这个问题的观点上可谓是人则见仁智者见智了。
我结合我自己过往的编程经验跟大家分享了三则我自己深度总结的而且对我自我提升突飞猛进的三个认知,希望一定程度对你有所帮助与启发。
如果觉得不错,帮忙点个赞加个关注、收藏一下。一方面能鼓励一下我,另一方面避免下次想看却找不到这个号。

写到最后


感谢您一路陪伴着我,探索编程的奇妙世界。如果您对
程序员编程技巧、计算机原理、职场进阶、认知成长
等充满兴趣,那么不要错过未来我为大家奉上的精彩内容!点击
关注
,让您的程序员之旅更加丰富多彩,我们一同成长,一同前行!

前言

提到浮动,前端的小伙伴肯定都不陌生,但是随着弹性布局等等一些更好用的标准出来后,用在布局方面少了很多,当初我刚开始接触前端的时候,很习惯用浮动来给元素改变定位,当时还并不是很流行flexbox布局,很多布局会通过浮动来实现,但是使用浮动来布局会产生一些副作用,比如虽然使用浮动可以使元素向左或向右靠齐,但会造成高度坍塌,当时的我并不太了解其中的缘由,只是机械地从网上搜索到一些解决高度坍塌的代码,到现在也不能算是了解的很透彻,只能算是比刚开始做前端的时候多了解了一点。

脱离文档流

在说到浮动的时候,很多地方都会说,它们脱离了文档流,那么正常情况下文档流是怎么样的呢?

写过HTML的小伙伴应该都了解,HTML中的元素应用的是默认的流式布局;假设页面上有一个
div
,然后还有一个
span
,如果我们不编写额外的样式改变它们的布局方式,它们会按照默认的规则布局,
div
是块状元素,默认占满行,即使给div设置了宽度,也是占着一行,如果有多个div会纵向一个个排布,而
span
是行内元素,通常行内元素会放置在块容器内部,如果有多个span会默认横向从左向右一个挨一个排布。

应用了浮动的元素就不受流式布局的控制了。比如应用了
float: left;

div
,它不再会占满一整行,应用了
float: right;

span
,可以从右向左排布。

那么浮动有什么特点,清除浮动又是什么意思呢?

浮动

前端有几个较为有名的布局方式,比如圣杯布局、双飞翼布局等等,使用
float
是实现这些布局的方式之一,当然现在我们可以使用更便捷的方式来实现,比如flex弹性布局、grid网格布局等等,在这方面浮动的应用应该少了很多,但其实浮动它原本设计之初应该就不是为了这些布局(个人观点,暂未考证),这从它的名称上就可以窥见。

我们可以把文档想象成一个水面,而浮动元素就好比是浮在水面上的船。水波环绕船体,就像浮动元素被其他内容环绕一样。比如下面这张图:

image

设计
float
属性主要就是实现这种效果,这从规范文档中的描述就能看出:

The most interesting characteristic of a float (or "floated" or "floating" box) is that content may flow along its side (or be prohibited from doing so by the
'clear'
property). Content flows down the right side of a left-floated box and down the left side of a right-floated box.

浮动框(或 "浮动 "或 "浮动 "框)最有趣的特点是,内容可以沿其侧面流动(或通过 "清除 "属性禁止流动)。内容可以沿着左浮动框的右侧流动,也可以沿着右浮动框的左侧流动。

这里流动的内容就可以比作流动的水,所以我们经常能看到使用
float
实现文字环绕效果的例子。比如规范文档中给出的例子:

image

只能说早些时候的CSS还不够完善,不能满足一些特殊的布局需求,而
float
恰好误打误撞可以满足,这算是一种css hack吧。

当使用
float:left;
或者
float: right;
声明某个节点后,这个节点就变成了水面上的船,一个自身重量为0的船,完全浮在水面上,当船上没有任何东西时,它不会影响到水面,而当我们往其中增加内容之后,船就会向水面下沉一点,从而影响到水面。产生类似于规范文档中的以下描述:

Since a float is not in the flow, non-positioned block boxes created before and after the float box flow vertically as if the float did not exist. However, the current and subsequent line boxes created next to the float are shortened as necessary to make room for the margin box of the float.

由于浮动框不在流中,在浮点框之前和之后创建的非定位块框会垂直流动,就像浮动框不存在一样。不过,在浮动框旁边创建的当前和后续行方框会根据需要缩短,以便为浮动框的margin box留出空间。

float会影响所在行的行方框,也就是
line boxes
。就类似于影响到水面的面积。

清除浮动

在使用浮动用于布局之后,我们常常需要清除浮动,那么清除浮动是什么意思呢?这里可以继续用前面浮在水面上的船来举例子,虽然可能不算很贴切。

船在水面上是浮动着的,随着水流的作用会飘移,那么就有可能飘移到其他的水域,清除浮动就类似于阻止这艘船飘移,比如安装一道闸门,防止船飘进来或飘出去。通过设置特定的css属性来清除浮动,就类似于这里的安装闸门。

image

创建BFC容器

之前在
《对BFC的理解》
中我们有提到过,可以通过创建BFC容器的方式来清除浮动,比如设置
display: flow-root;
,这就类似于在一个水域的下游处安装闸门,防止这个水域内的船飘往下游。

假设有以下一段HTML:

<div style="width: 100px; height: 100px; border: 1px solid orange; float: left;">
    <span>文本文本666666文本文本666666文本文本666666</span>
</div>
<div style="width: 100px; height: 100px; background: pink; ">
    <span>文本文本222</span>
</div>

在未清除浮动前是这样的:

image

可以看到,
文本666666
占用了
文本222
所在div的容器,就像是船飘到了下游的水域,占了
文本222
的位置。

将第一个div用BFC容器包裹后(类似于闸门的作用):

<div style="display: flow-root;">
    <div style="width: 100px; height: 100px; border: 1px solid orange; float: left;">
        <span>文本文本666666文本文本666666文本文本666666</span>
    </div>
</div>
<div style="width: 100px; height: 100px; background: pink;">
    <span>文本文本222</span>
</div>

页面就变成了下面这个样子:

image

使用clear属性

可以看到在规范文档中,还直接提供了一个
clear
属性

用于清除浮动。

This property indicates which sides of an element's box(es) may
not
be adjacent to an earlier floating box. The 'clear' property does not consider floats inside the element itself or in other
block formatting contexts.

该属性表示元素方框的哪些边不得与先前的浮动方框相邻。"清除"属性不考虑元素本身内部或其他块格式上下文中的浮动。

创建BFC的方式可以类比为上游水域在下游口安装了闸门,使用
clear
属性就可以类比为下游水域在上游口安装闸门,防止上游的船飘进来。所以前面的例子中,我们也可以使用以下代码来清除浮动:

<div style="width: 100px; height: 100px; border: 1px solid orange; float: left;">
    <span>文本文本666666文本文本666666文本文本666666</span>
</div>
<div style="width: 100px; height: 100px; background: pink; clear: left;">
    <span>文本文本222</span>
</div>

可以看到,最终效果和上面创建BFC的效果是一样的。但实际我之前看到的解决浮动的方案中,比较推荐的做法是由浮动的元素这边来处理清除,比如创建BFC容器,或者加在伪元素
before
或者
after
上。比如下面这段代码:

<div id="float-box">
    <div style="width: 100px; height: 100px; border: 1px solid orange; float: left;">
        <span>文本文本666666文本文本666666文本文本666666</span>
    </div>
</div>
<style>
    #float-box::after {
        content: '';
        display: block;
        clear: left;
    }
</style>
<div style="width: 100px; height: 100px; background: pink;">
    <span>文本文本222</span>
</div>

上面的代码中使用
::after
元素创建了一个看不见的块来清除浮动。

clear
属性可以设置多种值。

Name: clear
Value: none | left | right | both |
inherit
Initial: none
Applies to: block-level elements
Inherited: no
Percentages: N/A
Media: visual
Computed value: as specified

根据文档里的描述,
left
值表示当前块不被相邻的左浮动框影响,
right
值表示当前块不被相邻的右浮动框影响,
both
表示同时不受两类浮动框的影响。

但是规范文档中也提示我们,使用
clear
属性会产生副作用,使用
none
以外的值可能会引入间隙(clearance)。

Values other than 'none' potentially introduce clearance. Clearance inhibits margin collapsing and acts as spacing above the margin-top of an element. It is used to push the element vertically past the float.

“none”以外的值可能会引入间隙。 间隙会抑制边距折叠,并充当元素顶部边距上方的间距。 它用于将元素垂直推过浮动。

在《对BFC的理解》中,我们提到过,在BFC容器中,相邻块级盒子之间的垂直'margin'会折叠。这里的意思应该就是指,设置了
clear
属性为非
none
之后,会影响BFC容器里的垂直
margin
折叠。

文档中给出了间隙的值是怎么计算得出的:

Then the amount of clearance is set to the greater of:

  1. The amount necessary to place
    the border edge of the block
    even with the bottom outer edge of the lowest float that is to be cleared.
  2. The amount necessary to place
    the top border edge of the block
    at its hypothetical position.

然后将间隙量设置为以下两者中的较大值:

1.将块的边界边缘与要清除的最低浮动的底部外边缘对齐所需的量。
2.将块的顶部边框边缘放置在其假设位置所需的量。

光从描述上看,有点抽象,尤其是第二种计算,这里所谓的假设位置是哪里呢?第一种似乎好理解一些,就是让两个边缘对齐的一个间隔的量。所以文档中也举了例子来配合解说。

  1. 示例1

image

从名称就可以看出,F是个浮动块高度为H,此时B1有个bottom margin值为M1,B2有个top margin值为M2,在B2未设置clear属性之前,B1和B2之间的间距为M1和M2中的较大值,也就是产生了垂直
margin
的折叠。

假设B1的底部边框在y=0这个位置,就如上图所示,此时浮动框F的顶部位置就在y=M1的位置,而B2的顶部边框就在y=max(M1,M2)的位置,浮动框F的底部位置在y=M1+H的位置。

在这个例子中,B2不在F下面,这个例子所描述的就是需要添加间隙的场景。也就是说:

max(M1,M2) < M1 + H

根据文档中描述的计算方式,这里需要计算两次间隙量,C1和C2,然后取两者中的较大值:C =
max(C1, C2)

第一种方法是使 B2 的顶部与 F 的底部齐平,即 y(top of B2) = M1 + H。这意味着margin不再折叠(B1和B2的间距肯定大于M1和M2),它们之间有了间隙:

此时它们的等式关系为:

F的底部 = B2的顶部边框
M1 + H = M1 + C1 + M2
    C1 = M1 + H - M1 - M2
       = H - M2

第二中计算是保持 B2 的顶部位置,即 y(top of B2) = max(M1,M2)。也就是B2边框在其假设位置,按照预期保持垂直
margin
折叠的效果。此时的等式关系就是编程下面这样了:

max(M1, M2) = M1 + C2 + M2
         C2 = max(M1, M2) - M1 - M2

因为假设了
max(M1,M2) < M1 + H
,因此可以得出以下不等式:

C2 = max(M1, M2) - M1 - M2 < M1 + H - M1 - M2 = H - M2
C2 < H1 - M2

又因为
C1 = H1 - M2
,所以在这个场景中
C2 < C1
。因此间隙量C=max(C1, C2)=C1。

  1. 示例2,负值间隙
<p style="margin-bottom: 4em">
            First paragraph.</p>

<p style="float: left; height: 2em; margin: 0">
    Floating paragraph.</p>

<p style="clear: left; margin-top: 3em">
    Last paragraph.</p>

在最后一个
p
元素未设置
clear
属性之前,第一个和最后一个
p
元素之间的
margin
会折叠,最后一个
p
元素的顶部边框边缘(top border edge)会与浮动
p
元素的顶部齐平。

当我们设置
clear
属性用于清除浮动时,需要让最后一个
p
元素的top border edge位于浮动框的下面,也就是说需要往下挪动
2em
。此时必须引入间隙,相应地,
margin
不再折叠,此时我们来计算间隙量:

c + m-t = 2em
c = 2em - m-t = 2em - 3em = -1em

第一种方式计算C1 = H - M2 = 2em - 3em = -1em

第二种计算方式,也就是保持 最后一个
P
元素 的顶部位置,C2 = max(M1, M2) - M1 - M2 = 4em - 4em - 3em = -3em

所以最后C=max(C1,C2)=-1em。

从上面两个例子可以看出,间隙量的两种计算方式的区别就在于,是否改变浮动框后续元素的顶部位置。

总结

随着弹性布局、网格布局等一系列新的布局方式引入后,浮动的使用少了很多,但它仍然能实现特殊的网页效果,因此我们还是需要对它进行必要的了解。