回归CSS标准之Float

2015/10/27 · CSS ·
float

原文出处:
百度EFE   

最近因为遇到一个float相关的bug,又跑去撸了一遍css标准。然后发现,它确实比其他属性复杂好多,既不像inline-block那样单纯的完成并排显示,又不像绝对定位那样彻底的脱离文档流而不影响别的元素。它唯一单纯的就是,真的是唯一可以实现文字环绕显示的属性。

但是唯一单纯的特点却并不是很招人待见,相反,大家更习惯使用float去完成其他的功能,比如横排展示和自适应分栏布局。

很多人这样用着,只是因为一堆现成的文章告诉他们可以这样用,但是到底为什么可以,以及用的时候要注意什么问题却并不是所有人都知道,结果就是,一时的“便利”,为日后的维护埋了一堆的坑。

这篇文章打算通过将目前一些成文的浮动元素的特点与CSS规范中的具体描述对应,来加深大家对float属性原理的理解。并在后面通过一个bug实例,说明使用这个属性时要注意的问题。

手摸手,带你用合理的姿势使用webpack4(上)

2018/08/26 · 基础技术 ·
webpack

原文出处: 华尔街见闻技术团队 –
花裤衩   

前几天 webpack 作者 Tobias Koppers
发布了一篇新的文章 webpack 4.0 to 4.16: Did you
know?(需翻墙),总结了一下webpack 4发布以来,做了哪些调整和优化。

并且说自己正在着手开发 webpack 5

Oh you are still on webpack 3. I’m sorry, what is blocking you? We
already working on webpack 5, so your stack might be outdated soon…

翻译成中文就是:

图片 1

正好我也在使用一个文档生成工具
docz(安利一波)
也最低需要webpack 4+,新版webpack性能提高了不少,而且webpack 4
都已经发布五个多月了,想必应该已经没什么坑了,应该可以安心的按照别人写的升级攻略升级了。之前一直迟迟不升级完全是被去年被
webpack 3 坑怕了。它在 code splitting 的情况下
CommonsChunkPlugin会完全失效。过了好一段时间才修复,欲哭无泪。

所以这次我等了快大半年才准备升级到webpack 4
但万万没想到还是遇到了不少的问题!
有很多之前遗留的问题还是没有很好地解决。但最主要的问题还是它的文档有所欠缺,已经废除了的东西如commonsChunkPlugin还在官方文档中到处出现,很多重要的东西却一笔带过,甚至没写,需要用户自己去看源码才能解决。

还比如在v4.16.0版本中废除了 optimization.occurrenceOrderoptimization.namedChunksoptimization.hashedModuleIdsoptimization.namedModules
这几个配置项,替换成了optimization.moduleIds
optimization.chunkIds,但文档完中全没有任何体现,所以你在新版本中还按照文档那样配置其实是没有任何效果的。

最新最完整的文档还是看他项目的配置WebpackOptions.json,强烈建议遇到不清楚的配置项可以看这个,因为它一定保证是和最新代码同步的。

吐槽了这么多,我们言归正传。由于本次手摸手篇幅有些长,所以拆解成了上下两篇文章:

  • 上篇 —
    就是普通的在webpack 3的基础上升级,要做哪些操作和遇到了哪些坑
  • 下篇 — 是在webpack 4下怎么合理的打包和拆包,并且如何最大化利用
    long term caching

本文章不是手摸手从零教你 webpack
配置,所以并不会讲太多很基础的配置问题
。比如如何处理 css
文件,如何配置 webpack-dev-server,讲述 file-loader 和 url-loader
之间的区别等等,有需求的推荐看
官方文档 或者
survivejs
出的一个系列教程。或者推荐看我司的另一篇 wbepack 入门文章,已同步到
webpack4
传送门。

Vue 项目架构设计与工程化实践

2018/07/25 · JavaScript
· Vue

原文出处: Berwin   

文中会讲述我从0~1搭建一个前后端分离的vue项目详细过程

Feature:

  • 一套很实用的架构设计
  • 通过 cli 工具生成新项目
  • 通过 cli 工具初始化配置文件
  • 编译源码与自动上传CDN
  • Mock 数据
  • 反向检测server api接口是否符合预期

前段时间我们导航在开发一款新的产品,名叫
快言,是一个主题词社区,具体这个产品是干什么的就不展开讲了,有兴趣的小伙伴可以点进去玩一玩~

这个项目的1.0乞丐版上线后,需要一个管理系统来管理这个产品,这个时候我手里快言项目的功能已经上线,暂时没有其他需要开发的功能,所以我跑去找我老大把后台这个项目给拿下了。

浮动元素的业界公认特点

float属性被设置为非none的元素:

  1. 元素被视作块级元素,相当于display设置为“block”;
  2. 元素具备包裹性,会根据它所包含的元素实现宽度、高度自适应;
  3. 浮动元素前后的块级兄弟元素忽视浮动元素的而占据它的位置,并且元素会处在浮动元素的下层(并且无法通过z-index属性改变他们的层叠位置),但它的内部文字和其他行内元素都会环绕浮动元素;
  4. 浮动元素前后的行内元素环绕浮动元素排列;
  5. 浮动元素之前的元素如果也是浮动元素,且方向相同,它会紧跟在它们后面;父元素宽度不够,换行展示;
  6. 浮动元素之间的水平间距不会重叠;
  7. 当包含元素中只有浮动元素时,包含元素将会高度塌陷;
  8. 浮动元素的父元素的非浮动兄弟元素,忽视浮动元素存在,覆盖浮动元素;
  9. 浮动元素的父元素的浮动兄弟元素,会跟随浮动元素布局,仿佛处在同一父元素中。

目前实现的很多应用都是直接对应上述特点实现的。但是很多人在看过这些描述以后,并不知道它的结论从何而来,无据可循,怎会安心?为了解决大家的疑虑,下面我会将上面的九条与CSS规范做一一的对应。

升级篇

技术选型

接到这个任务后,我首先考虑这个项目日后会变得非常复杂,功能会非常多。所以需要精心设计项目架构和开发流程,保证项目后期复杂度越来越高的时候,代码可维护性依然保持最初的状态

后台项目需要频繁的发送请求,操作dom,以及维护各种状态,所以我需要先为项目选择一款合适的mvvm框架,综合考虑最后项目框架选择使用
Vue,原因是:

  • 上手简单,团队新人可以很容易就参与到这个项目中进行开发,对开发者水平要求较低(毕竟是团队项目,门槛低我觉得非常重要)
  • 我个人本身对Vue还算比较熟悉,一年前2.0还没发布的时候阅读过vue
    1.x的源码,对vue的原理有了解,项目开发中遇到的所有问题我都有信心能解决掉
  • 调研了我们团队的成员,大部分都使用过vue,对vue多少都有过开发经验,并且之前团队内也用vue开发过一些项目

所以最终选择了Vue

CSS规范映射

前言

我一直认为模仿和借鉴是学习一个新东西最高效的方法。所以我建议还是通过借鉴一些成熟的
webpack 配置比较好。比如你项目是基于 react 生态圈的话可以借鉴
create-react-app
,下载之后npm run eject 就可以看到它详细的 webpack 配置了。vue
的话由于新版vue cli不支持 eject了,而且改用
webpack-chain来配置,所以借鉴起来可能会不太方便,主要配置
地址。觉得麻烦的话你可以直接借鉴
vue-element-admin
配置。或者你想自己发挥,你可以借鉴
webpack 官方的各种
examples,来组合你的配置。

选择vue周边依赖(全家桶)

框架定了Vue 后,接下来我需要挑选一些vue套餐来帮助开发,我挑选的套餐有:

  • vuex – 项目复杂后,使用vuex来管理状态必不可少
  • element-ui – 基于vue2.0
    的组件库,饿了么的这套组件库还挺好用的,功能也全
  • vue-router –
    单页应用必不可少需要使用前端路由(这种管理系统非常适合单页应用,系统经常需要频繁的切换页面,使用单页应用可以很快速的切换页面而且数据也是按需加载,不会重复加载依赖)
  • axios – vue 官方推荐的http客户端
  • vue-cli 的 webpack 模板,这套模板是功能最全的,有hot
    reload,linting,testing,css extraction 等功能

第一条和第二条可以总体归结为“浮动对于自身的影响”。

1.元素被视作块级元素,相当于display设置为“block”;

2.元素具备包裹性,会根据它所包含的元素实现宽度、高度自适应;

标准中有关第一条是有明确指明的:

if ‘float’ has a value other than ‘none’, the box is floated and
‘display’ is set according to the table below.

Specified value Computed value
inline-table table
inline, table-row-group, table-column, table-column-group, table-header-group, table-footer-group, table-row, table-cell, table-caption, inline-block block
others same as specified

基本上说的就是第一条。

对于第二条,这个标准中也有明确的说明

for Floating, non-replaced elements If ‘width’ is computed as ‘auto’,
the used value is the “shrink-to-fit” width. Calculation of the
shrink-to-fit width is similar to calculating the width of a table
cell using the automatic table layout algorithm. Roughly: calculate
the preferred width by formatting the content without breaking lines
other than where explicit line breaks occur, and also calculate the
preferred minimum width, e.g., by trying all possible line breaks. CSS
2.1 does not define the exact algorithm. Thirdly, find the available
width: in this case, this is the width of the containing block minus
the used values of ‘margin-left’, ‘border-left-width’, ‘padding-left’,
‘padding-right’, ‘border-right-width’, ‘margin-right’, and the widths
of any relevant scroll bars.

Then the shrink-to-fit width is: min(max(preferred minimum width,
available width), preferred width).

其实这段话看的时候挺绕的,主要是几个width的含义不容易理解:

首选宽度(preferred width):完全不允许折行展示情况下的内容宽度

最小首选宽度(preferred minimum
width)
:所有折行展示可能下的最小内容宽度

可用宽度(available
width)
:包含块宽度减去margin,padding,border,滚动条宽等所有这些以后的差值

在通常情况下,按照上面的公式,这个自适应宽度(shrink-to-fit
width)就是首选宽度,而首选宽度呈现出来的感觉就是“包裹”。

但是,看到这里有没有发现一个问题?就是所谓的首选宽度到底是如何计算的,如果一个浮动元素里包含另外一个浮动元素,它是如何计算的?是否要把子孙浮动元素的宽度考虑进去?标准似乎并没有更多的考虑这种情况。而由于这点”模糊“造成的问题,后面也会提及。

而关于高度

‘Auto’ heights for block formatting context roots

In certain cases (see, e.g., sections 10.6.4 and 10.6.6 above), the
height of an element that establishes a block formatting context is
computed as follows:

If it only has inline-level children, the height is the distance
between the top of the topmost line box and the bottom of the
bottommost line box.

If it has block-level children, the height is the distance between the
top margin-edge of the topmost block-level child box and the bottom
margin-edge of the bottommost block-level child box.

这个比width好理解也简单些,就是实在的“包裹”。

升级 webpack

首先将 webpack 升级到 4
之后,直接运行webpack --xxx是不行的,因为新版本将命令行相关的东西单独拆了出去封装成了webpack-cli。会报如下错误:

The CLI moved into a separate package: webpack-cli.
Please install webpack-cli in addition to webpack itself to use the
CLI.

所有你需要安装npm install webpack-cli -D -S。你也可将它安装在全局。

同时新版 webpack
需要Node.js 的最低支持版本为 6.11.5不要忘了升级。如果还需要维护老项目可以使用
nvm 来做一下 node 版本管理。

升级所有依赖

因为webpack4改了 它的hook api
,所以所有的loadersplugins都需要升级才能够适配。

可以使用命令行
npm outdated,列出所以可以更新的包。免得再一个个去npm找相对于的可用版本了。

图片 2

反正把devDependencies的依赖都升级一下,总归不会有错。

架构设计

在开发这个项目前,我去参加了北京的首届 vueconf
大会,其中有一个主题是阴明讲的《掘金 Vue.js 2.0
后端渲染及重构实践》,讲了掘金重构后的架构设计,我觉得他们的架构设计的挺不错,所以参考掘金的架构,设计了一个更适合我们自己业务场景的架构

第三、四、五、六条可以总体归结为“浮动对于兄弟元素的影响”。

3.浮动元素前后的块级兄弟元素忽视浮动元素的而占据它的位置,并且元素会处在浮动元素的下层(并且无法通过z-index属性改变他们的层叠位置),但它的内部文字和其他行内元素都会环绕浮动元素;

4.浮动元素前后的行内元素环绕浮动元素排列;

5.浮动元素之前的元素如果也是浮动元素,且方向相同,它会紧跟在它们后面;父元素宽度不够,换行展示;

6.浮动元素之间的水平间距不会重叠;

标准里对float的定义是

Floats. In the float model, a box is first laid out according to the
normal flow, then taken out of the flow and shifted to the left or
right as far as possible. Content may flow along the side of a float.

上面这句核心思想就是说,浮动元素最大的特点就是脱离了文档流。

标准中又对“脱离文档流”的结果做了描述:

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.

我想这句整个证明了第三条和第四条的合法性。浮动元素对于块级兄弟元素以及行内兄弟元素的处理是有区别的。如果兄弟块盒没有生成新的BFC,那它其中的行内盒也会受到浮动元素的影响,为浮动元素让出位置,缩进显示。至于对齐的位置,标准中也有描述:

A floated box is shifted to the left or right until its outer edge
touches the containing block edge or the outer edge of another float.
If there is a line box, the outer top of the floated box is aligned
with the top of the current line box.

这两条说明,float虽然使元素脱离的文档流,但是它却依然占据着位置,这其实也是影响外部元素宽度计算的一个原因之一,也是它跟绝对定位最大的不同。

至于其中提及的,会放置在块级元素之上,这个也有考据:

The contents of floats are stacked as if floats generated new stacking
contexts, except that any positioned elements and elements that
actually create new stacking contexts take part in the float’s parent
stacking context. A float can overlap other boxes in the normal flow
(e.g., when a normal flow box next to a float has negative margins).
When this happens, floats are rendered in front of non-positioned
in-flow blocks, but behind in-flow inlines.

第五条,这个是浮动元素行为九准则中规定的。这里列举一下:

  1. The left outer edge of a left-floating box may not be to the left
    of the left edge of its containing block. An analogous rule holds
    for right-floating elements.
  2. If the current box is left-floating, and there are any
    left-floating boxes generated by elements earlier in the source
    document, then for each such earlier box, either the left outer
    edge of the current box must be to the right of the right outer
    edge of the earlier box, or its top must be lower than the bottom
    of the earlier box. Analogous rules hold for right-floating boxes.
  3. The right outer edge of a left-floating box may not be to the
    right of the left outer edge of any right-floating box that is
    next to it. Analogous rules hold for right-floating elements.
  4. A floating box’s outer top may not be higher than the top of its
    containing block. When the float occurs between two collapsing
    margins, the float is positioned as if it had an otherwise empty
    anonymous block parent taking part in the flow. The position of
    such a parent is defined by the rules in the section on margin
    collapsing.
  5. The outer top of a floating box may not be higher than the outer
    top of any block or floated box generated by an element earlier in
    the source document.
  6. The outer top of an element’s floating box may not be higher than
    the top of any line-box containing a box generated by an element
    earlier in the source document.
  7. A left-floating box that has another left-floating box to its left
    may not have its right outer edge to the right of its containing
    block’s right edge. (Loosely: a left float may not stick out at
    the right edge, unless it is already as far to the left as
    possible.) An analogous rule holds for right-floating elements.
  8. A floating box must be placed as high as possible.
  9. A left-floating box must be put as far to the left as possible, a
    right-floating box as far to the right as possible. A higher
    position is preferred over one that is further to the left/right.

九准则其实已经基本上把浮动元素自身的行为方式定义的比较全面了,主要的原则就是:浮动元素之间不重叠;尽可能像边缘漂浮,但不越界。

第六条,在CSS标准描述margin的时候有提及:

Margins between a floated box and any other box do not collapse (not
even between a float and its in-flow children). Margins of elements
that establish new block formatting contexts (such as floats and
elements with ‘overflow’ other than ‘visible’) do not collapse with
their in-flow children.

因此,也可证明合理。

带来的变化

其实这次升级带来了不少改变,但大部分其实对于普通用户来说是不需要关注的,比如这次升级带来的功能SideEffectsModule Type’s IntroducedWebAssembly Support,基本平时是用不到的。我们主要关注那些对我们影响比较大的改动如:optimization.splitChunks代替原有的CommonsChunkPlugin(下篇文章会着重介绍),和Better Defaults-mode更好的默认配置,这是大家稍微需要关注一下的。

图片 3

如果想进一步了解 Tree ShakingSideEffects的可见文末拓展阅读。
上图参考 Webpack 4 进阶

整体架构图

图片 4

第七、八、九条可以总体归结为“浮动对于包含元素的影响”。浮动使用时的另一批潜在坑就出现在对几个特点的应用上。

7.当包含元素中只有浮动元素时,包含元素将会高度塌陷;

8.浮动元素的父元素的非浮动兄弟元素,忽视浮动元素存在,在浮动元素之下展示;

9.浮动元素的父元素的浮动兄弟元素,会跟随浮动元素布局,仿佛处在同一父元素中。

首先,以上三条拥有一个共同的原因:浮动元素脱离文档流。

接着去读一下标准中有关高度计算的描述:

For block-level non-replaced elements in normal flow when ‘overflow’
computes to ‘visible’

If ‘margin-top’, or ‘margin-bottom’ are ‘auto’, their used value is 0.
If ‘height’ is ‘auto’, the height depends on whether the element has
any block-level children and whether it has padding or borders … Only
children in the normal flow are taken into account (i.e., floating
boxes and absolutely positioned boxes are ignored, and relatively
positioned boxes are considered without their offset). Note that the
child box may be an anonymous block box.

关键看最后一段,浮动元素的高度会被忽略的,因此一旦包含块中只包含浮动元素,那么包含块就不再有参考的计算高度,自然就塌陷了。当然,如果包含元素里还包含其他元素,那么它的高度会参考非浮动元素按标准中描述的规则计算。

第七条也就成立了。

那么第八条、第九条为什么?看CSS标准中的下面的描述:

References to other elements in these rules refer only to other
elements in the same block formatting context as the float.

也就是说,float对同一个BFC内的元素有效。如果父元素没有触发生成新的BFC,那么父元素的兄弟元素都算是跟父元素中的元素处于同一BFC,也就会受浮动的影响,并且行为规则与同处于同一个父元素之中的元素的规则相同:块级元素重叠;行内元素环绕;浮动元素跟随。

正是因为浮动元素的这三条特点,因此,在使用了浮动元素以后,通常都要做“清除浮动“或”闭合浮动“的操作,来避免浮动元素对其他元素的影响。

到这里,浮动元素的九个特点基本上都在标准中找到了对应,但是我说的是基本,上面提及的有一个问题我们还没有完美解决,就是浮动元素的auto宽度计算规则。我们这里先举一个实际的例子来解答这个疑惑。

默认配置

webpack 4 引入了零配置的概念,被
parcel 刺激到了。
不管效果怎样,这改变还是值得称赞的。

最近又新出了 Fastpack 可以关注一下。

言归正题,我们来看看 webpack 默认帮我们做了些什么?

development 模式下,默认开启了NamedChunksPlugin
NamedModulesPlugin方便调试,提供了更完整的错误信息,更快的重新编译的速度。

module.exports = { + mode: ‘development’ – devtool: ‘eval’, – plugins:
[ – new webpack.NamedModulesPlugin(), – new
webpack.NamedChunksPlugin(), – new webpack.DefinePlugin({
“process.env.NODE_ENV”: JSON.stringify(“development”) }), – ] }

1
2
3
4
5
6
7
8
9
module.exports = {
+ mode: ‘development’
– devtool: ‘eval’,
– plugins: [
–   new webpack.NamedModulesPlugin(),
–   new webpack.NamedChunksPlugin(),
–   new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
– ]
}

production
模式下,由于提供了splitChunksminimize,所以基本零配置,代码就会自动分割、压缩、优化,同时
webpack 也会自动帮你 Scope hoistingTree-shaking

module.exports = { + mode: ‘production’, – plugins: [ – new
UglifyJsPlugin(/* … */), – new webpack.DefinePlugin({
“process.env.NODE_ENV”: JSON.stringify(“production”) }), – new
webpack.optimize.ModuleConcatenationPlugin(), – new
webpack.NoEmitOnErrorsPlugin() – ] }

1
2
3
4
5
6
7
8
9
module.exports = {
+  mode: ‘production’,
–  plugins: [
–    new UglifyJsPlugin(/* … */),
–    new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
–    new webpack.optimize.ModuleConcatenationPlugin(),
–    new webpack.NoEmitOnErrorsPlugin()
–  ]
}

webpack
一直以来最饱受诟病的就是其配置门槛极高,配置内容极其复杂和繁琐,容易让人从入门到放弃,而它的后起之秀如
rollup、parcel
等均在配置流程上做了极大的优化,做到开箱即用,所以webpack 4
也从中借鉴了不少经验来提升自身的配置效率。愿世间再也不需要 webpack
配置工程师

目录结构

. ├── README.md ├── build # build 脚本 ├── config # prod/dev build
config 文件 ├── hera # 代码发布上线 ├── index.html # 最基础的网页 ├──
package.json ├── src # Vue.js 核心业务 │ ├── App.vue # App Root
Component │ ├── api # 接入后端服务的基础 API │ ├── assets # 静态文件 │
├── components # 组件 │ ├── event-bus # Event Bus 事件总线,类似
EventEmitter │ ├── main.js # Vue 入口文件 │ ├── router # 路由 │ ├──
service # 服务 │ ├── store # Vuex 状态管理 │ ├── util # 通用
utility,directive, mixin 还有绑定到 Vue.prototype 的函数 │ └── view #
各个页面 ├── static # DevServer 静态文件 └── test # 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.
├── README.md
├── build                   # build 脚本
├── config                  # prod/dev build config 文件
├── hera                    # 代码发布上线
├── index.html              # 最基础的网页
├── package.json
├── src                     # Vue.js 核心业务
│   ├── App.vue             # App Root Component
│   ├── api                 # 接入后端服务的基础 API
│   ├── assets              # 静态文件
│   ├── components          # 组件
│   ├── event-bus           # Event Bus 事件总线,类似 EventEmitter
│   ├── main.js             # Vue 入口文件
│   ├── router              # 路由
│   ├── service             # 服务
│   ├── store               # Vuex 状态管理
│   ├── util                # 通用 utility,directive, mixin 还有绑定到 Vue.prototype 的函数
│   └── view                # 各个页面
├── static                  # DevServer 静态文件
└── test                    # 测试
 

从目录结构上,可以发现我们的项目中没有后端代码,因为我们是纯前端工程,整个git仓库都是前端代码,包括后期发布上线都是前端项目独立上线,不依赖后端~

代码发布上线的时候会先进行编译,编译的结果是一个无任何依赖的html文件
index.html,然后把这个 index.html
发布到服务器上,在编译阶段所有的依赖,包括css,js,图片,字体等都会自动上传到cdn上,最后生成一个无任何依赖的纯html,大概是下面的样子:

<!DOCTYPE html><html><head><meta
charset=utf-8><title>快言管理后台</title><link
rel=icon href=
href=
rel=stylesheet></head><body><div
id=app></div><script type=text/javascript
src=
type=text/javascript
src=
type=text/javascript
src=;

1
<!DOCTYPE html><html><head><meta charset=utf-8><title>快言管理后台</title><link rel=icon href=https://www.360.cn/favicon.ico><link href=http://s3.qhres.com/static/***.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=http://s2.qhres.com/static/***.js></script><script type=text/javascript src=http://s8.qhres.com/static/***.js></script><script type=text/javascript src=http://s2.qhres.com/static/***.js></script></body></html>

一个栗子

先看一下代码:

CSS

.ui-label { display: inline; } .form-section { width: 700px; margin: 0 0
60px; min-width: 960px; margin-left: 168px; margin-top: 60px; }
.form-field-required { font-size: 14px; margin: 30px 0; }
.form-field-required:before, .form-field-required:after { display:
table; content: ”; } .form-field-required:after { clear: both; }
.form-field-label { float: left; zoom: 1; width: 104px; line-height:
30px; text-align: left; vertical-align: top; } .form-field-value {
line-height: 30px; padding-left: 12px; float: left; }
.form-field-value-required-star { float: left; color: red; width: 12px;
text-align: left; } .ui-textbox { position: relative; display:
inline-block; } .ui-textbox input { color: #333333; background:
#ffffff; border: 1px solid #dddddd; width: 240px; height: 24px;
line-height: 24px; vertical-align: middle; box-sizing: content-box; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
.ui-label {
    display: inline;
}
 
.form-section {
    width: 700px;
    margin: 0 0 60px;
    min-width: 960px;
    margin-left: 168px;
    margin-top: 60px;
}
 
.form-field-required {
    font-size: 14px;
    margin: 30px 0;
}
 
.form-field-required:before,
.form-field-required:after {
    display: table;
    content: ”;
}
 
.form-field-required:after {
    clear: both;
}
 
.form-field-label {
    float: left;
    zoom: 1;
    width: 104px;
    line-height: 30px;
    text-align: left;
    vertical-align: top;
}
 
.form-field-value {
    line-height: 30px;
    padding-left: 12px;
    float: left;
}
 
.form-field-value-required-star {
    float: left;
    color: red;
    width: 12px;
    text-align: left;
}
 
.ui-textbox {
    position: relative;
    display: inline-block;
}
 
.ui-textbox input {
    color: #333333;
    background: #ffffff;
    border: 1px solid #dddddd;
    width: 240px;
    height: 24px;
    line-height: 24px;
    vertical-align: middle;
    box-sizing: content-box;
}

XHTML

<section class=”form-section”> <div
class=”form-field-required”> <esui-label class=”form-field-label
ui-label” title=””>姓名:</esui-label> <div
class=”form-field-value”> <div
class=”form-field-value-required-star”>*</div> <div
id=”name” class=”ui-textbox”> <input type=”text” title=”金额”
style=”width: 191px;” /> </div> </div> </div>
</section>

1
2
3
4
5
6
7
8
9
10
11
<section class="form-section">
    <div class="form-field-required">
        <esui-label class="form-field-label ui-label" title="">姓名:</esui-label>
        <div class="form-field-value">
            <div class="form-field-value-required-star">*</div>
            <div id="name" class="ui-textbox">
                <input type="text" title="金额" style="width: 191px;" />
            </div>
        </div>
    </div>
</section>

这段代码算是使用float实现元素横排展示的一个比较复杂的例子(我并没有说这个实现方案是推荐的,后面我会解释为什么其实不推荐)。也最大程度的利用float的特点,并且能够解答我上面提出的那个疑惑。为了清楚的说明,我们可以从裸样式入手,一步一步随着样式的增加,跟踪展示效果:

第一步:去掉所有结构相关的代码(为了清晰展示结构,加上背景样式),展示是这样的:

图片 5第一步

“form-field-label”原来的display属性是inline,因此虽然设定了宽高,却并没有作用;“form-field-value”是块级盒,包括里面的“星号”、“输入框”、“文字描述”也都是,因此垂直展示。

第二步,为“form-field-label”和“form-field-value”增加float属性,展示效果如下:

图片 6

第二步

这个效果的出现,利用了上述浮动特点的第一条、第二条、第五条和第七条。而关于’包裹性’也有了最简单情况的示例展示:即容器的长方框恰好包住无折行条件下的容器内的元素。

第三步,为“form-field-value”中的“form-field-value-required-star”增加float属性,此时展示效果如下:

图片 7第三步

这个效果的出现,利用了上述浮动特点的第一条、第二条、第三条和第四条。
需要着重关注的,一个是兄弟元素’ui-textbox’在占据了星号位置的同时,’ui-textbox’中的行内元素input缩进环绕星号展示,也就是第四条的完美体现;另一个则是星号浮动属性的设置对于父元素宽度计算的影响。我们发现,虽然input行内元素缩进展示,但是父元素的宽度却并没有因此而随之增加,也就是,它的宽度仍然是未缩进前包含块的“首选宽度”,即input宽;但是如果把星号的宽度提高到超过input宽,那么你会发现,包含块的宽度变成了星号的宽度。这就解答了我之前的问题:如果一个浮动元素里包含另外一个浮动元素,它的auto宽度计算是会考虑进来浮动元素的,计算规则是包含块去掉所有后代浮动元素后的“首选宽度”与所有后代浮动元素宽度和的最大值。

第四步,为“ui-textbox”设置display属性值为“inline-block”,此时展示效果如下:

图片 8第四步

为什么包含块的宽度突然可以足够星号和输入框同时并排了?原因是inline-block的设置改变了原本块级元素的行为,CSS标准里有如下描述:

This value causes an element to generate an inline-level block
container. The inside of an inline-block is formatted as a block box,
and the element itself is formatted as an atomic inline-level box.

所以此时,“ui-textbox”就是作为一个行内元素整体缩进展示,而不是像前面的,本身并没有缩进,只是内部的input缩进。那么此时包含块去掉所有后代浮动元素后的“首选宽度”就是“缩进距离”与“ui-textbox”宽度的和。所以就足够星号和输入框并排展示了。

但是你觉着这样就没问题了?我们来改变一下源码:

  1. 去掉ui-textbox的静态class赋值
  2. 使用js动态分配class:
JavaScript

var nameInput = document.getElementById('name'); setTimeout(
function () { nameInput.setAttribute('class', 'ui-textbox'); }, 0 );

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6a303ef91946227217-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6a303ef91946227217-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6a303ef91946227217-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6a303ef91946227217-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6a303ef91946227217-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6a303ef91946227217-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f6a303ef91946227217-7">
7
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6a303ef91946227217-1" class="crayon-line">
var nameInput = document.getElementById('name');
</div>
<div id="crayon-5b8f6a303ef91946227217-2" class="crayon-line crayon-striped-line">
setTimeout(
</div>
<div id="crayon-5b8f6a303ef91946227217-3" class="crayon-line">
    function () {
</div>
<div id="crayon-5b8f6a303ef91946227217-4" class="crayon-line crayon-striped-line">
        nameInput.setAttribute('class', 'ui-textbox');
</div>
<div id="crayon-5b8f6a303ef91946227217-5" class="crayon-line">
    },
</div>
<div id="crayon-5b8f6a303ef91946227217-6" class="crayon-line crayon-striped-line">
    0
</div>
<div id="crayon-5b8f6a303ef91946227217-7" class="crayon-line">
);
</div>
</div></td>
</tr>
</tbody>
</table>

再运行一下,发现了什么:在几乎所有的浏览器(包括IE)效果都没有变化,但是在Chrome下却坑了,效果是酱紫滴:

包含块的宽度又不够并排了,变成了输入框的宽度。DOM样式和结构不可能变化,但是有了这样的区别,是为什么?我们看到上面代码里最诡异的也就是延迟class的赋值,从结果看,在Chrome下,这个延迟赋值显然没有生效,也就是并没有触发包含块宽度的重计算。再深层的原因还没有研究,因为Safari下也有同样的问题,所以我只当它是Webkit的bug:浮动元素中后代元素,动态设置display为inline-block,改变元素的盒属性,外部浮动元素无法感知。

那么怎么办?放弃Chrome?显然不行……
使用其他方式,在设置完display以后强制触发宽度变化?目前还没有找到哪个属性可以,甚至设置为float,也都无效。

其实根本也不必费力寻找方式去触发宽度变化,我举这个例子,想表达的是,使用float实现并排展示,并在其中掺杂inline-block实现并排并不是明智之举,在未来会大大增加理解和维护的难度。

那么,在实际开发中,到底是用float实现并排更推荐一些还是inline-block更推荐一些,关于这个的讨论,网上也都不少。我个人的观点,两者各有利弊:

html-webpack-plugin

用最新版本的的 html-webpack-plugin你可能还会遇到如下的错误:

throw new Error('Cyclic dependency' + nodeRep)

产生这个 bug 的原因是循环引用依赖,如果你没有这个问题可以忽略。

目前解决方案可以使用 Alpha
版本,npm i --save-dev html-webpack-plugin@next

或者加入chunksSortMode: 'none'就可以了。

但仔细查看文档发现设置成chunksSortMode: 'none'这样是会有问题的。

Allows to control how chunks should be sorted before they are included
to the HTML.

这属性会决定你 chunks 的加载顺序,如果设置为none,你的 chunk
加载在页面中加载的顺序就不能够保证了,可能会出现样式被覆盖的情况。比如我在app.css里面修改了一个第三方库element-ui的样式,通过加载顺序的先后来覆盖它,但由于设置为了none,打包出来的结果变成了这样:

<link href=”/app.8945fbfc.css” rel=”stylesheet”> <link
href=”/chunk-elementUI.2db88087.css” rel=”stylesheet”>

1
2
<link href="/app.8945fbfc.css" rel="stylesheet">
<link href="/chunk-elementUI.2db88087.css" rel="stylesheet">

app.css被先加载了,之前写的样式覆盖就失效了,除非你使用important或者其它
css 权重的方式覆盖它,但这明显是不太合理的。
vue-cli正好也有这个相关
issue,尤雨溪也在不使用@next版本的基础上
hack
了它,有兴趣的可以自己研究一下,本人在项目中直接使用了@next版本,也没遇到其它什么问题(除了不兼容
webpack 的 prefetch/preload 相关
issue)。两种方案都可以,自行选择。

其它 html-webpack-plugin 的配置和之前使用没有什么区别。

表现层

  • store/ – Vuex 状态管理
  • router/ – 前端路由
  • view/ – 各个业务页面
  • component/ – 通用组件

发表评论

电子邮件地址不会被公开。 必填项已用*标注