1. 样式标准
1) 缩进
使用soft tab(4个空格)
.element {
position: absolute;
top: 10px;
left: 10px;
}
复制代码
2)分号
每个属性声明末尾都要加分号。
.element {
width: 20px;
}
复制代码
3)空格
以下几种情况不需要空格:
- 属性名后
- 多个规则的分隔符','前
!important'!'后- 属性值中'('后和')'前
- 行末不要有多余的空格
以下几种情况需要空格:
- 属性值前
- 选择器'>', '+', '~'前后
- '{'前
!important'!'前@else前后- 属性值中的','后
- 注释'/ '后和' /'前
/* not good */
.element {
color :red! important;
}
/* good */
.element {
color: red !important;
}
/* not good */
.element ,
.dialog{
...
}
/* good */
.element,
.dialog {
}
/* not good */
.element>.dialog{
...
}
/* good */
.element > .dialog{
...
}
/* not good */
.element{
...
}
/* good */
.element {
...
}
/* not good */
@debug: true;
header {
background-color: (yellow)when(@debug = true);
}
/* good */
header {
background-color: (yellow) when (@debug = true);
}
复制代码
4) 空行
以下几种情况需要空行:
- 文件最后保留一个空行
- '}'后最好跟一个空行,包括scss中嵌套的规则
- 属性之间需要适当的空行
/* not good */
.element {
...
}
.dialog {
color: red;
&:after {
...
}
}
/* good */
.element {
...
}
.dialog {
color: red;
&:after {
...
}
}
复制代码
5) 注释
注释统一用'/* */'(scss 和 less中也不要用'//'),具体参照右边的写法;
缩进与下一行代码保持一致;
可位于一个代码行的末尾,与代码间隔一个空格。
/* Modal header */
.modal-header {
...
}
/*
* Modal header
*/
.modal-header {
...
}
.modal-header {
/* 50px */
width: 50px;
color: red; /* color red */
}
复制代码
6) 引号
最外层统一使用双引号;
url的内容要用引号;
属性选择器中的属性值需要引号。
.element:after {
content: "";
background-image: url("logo.png");
}
li[data-type="single"] {
...
}
复制代码
7)命名
- 类名使用
小写字母,以中划线分隔 - id采用
驼峰式命名 less中的变量、函数以中划线分隔命名
/* class */
.element-content {
...
}
/* id */
#myDialog {
...
}
/* 变量 */
@color-black: #000;
/* mixins */
.my-mixin() {
color: black;
}
复制代码
8)属性声明顺序
相关的属性声明按右边的顺序做分组处理,组之间需要有一个空行。
.declaration-order {
display: block;
float: right;
position: absolute;
z-index: 100;
border: 1px solid #e5e5e5;
border-radius: 3px;
font: normal 13px "Helvetica Neue", sans-serif;
line-height: 1.5;
color: #333;
background-color: #f5f5f5;
opacity: 1;
}
复制代码
书写顺序前后为:
(1)定位属性:position display float left top right bottom overflow clear z-index
(2)自身属性:width height padding border margin background
(3)文字样式:font-family font-size font-style font-weight font-varient color
(4)文本属性text-align vertical-align text-wrap text-transform text-indent text-decoration letter-spacing word-spacing white-space text-overflow
目的:减少浏览器reflow(回流),提升浏览器渲染dom的性能
原理:浏览器的渲染流程为:
1、解析html构建dom树,解析css构建css树:将html解析成树形的数据结构,将css解析成树形的数据结构
2、构建render树:DOM树和CSS树合并之后形成的render树。
3、布局render树:有了render树,浏览器已经知道那些网页中有哪些节点,各个节点的css定义和以及它们的从属关系,从而计算出每个节点在屏幕中的位置。
4、绘制render树:按照计算出来的规则,通过显卡把内容画在屏幕上。
css样式解析到显示至浏览器屏幕上就发生在234步骤,可见浏览器并不是一获取到css样式就立马开始解析而是根据css样式的书写顺序将之按照dom树的结构分布render样式,完成第2步,然后开始遍历每个树结点的css样式进行解析,此时的css样式的遍历顺序完全是按照之前的书写顺序。在解析过程中,一旦浏览器发现某个元素的定位变化影响DOM,则需要重新渲染。
正确的书写顺序:
.demo{
width: 100px;
height: 100px;
background-color: red ;
position: absolute;
}
复制代码
当浏览器解析到position的时候突然发现该元素是绝对定位元素需要脱离文档流,而之前却是按照普通元素进行解析的,所以不得不重新渲染,解除该元素在文档中所占位置,然而由于该元素的占位发生变化,其他元素也可能会受到它回流的影响而重新排位。最终导致③步骤花费的时间太久而影响到④步骤的显示,影响了用户体验。
// 下面是推荐的属性的顺序
[ [ "display", "visibility", "float", "clear", "overflow", "overflow-x", "overflow-y", "clip", "zoom" ],
[ "table-layout", "empty-cells", "caption-side", "border-spacing", "border-collapse", "list-style", "list-style-position", "list-style-type", "list-style-image" ],
[ "-webkit-box-orient", "-webkit-box-direction", "-webkit-box-decoration-break", "-webkit-box-pack", "-webkit-box-align", "-webkit-box-flex" ],
[ "position", "top", "right", "bottom", "left", "z-index" ],
[ "margin", "margin-top", "margin-right", "margin-bottom", "margin-left", "-webkit-box-sizing", "-moz-box-sizing", "box-sizing", "border", "border-width", "border-style", "border-color", "border-top", "border-top-width", "border-top-style", "border-top-color", "border-right", "border-right-width", "border-right-style", "border-right-color", "border-bottom", "border-bottom-width", "border-bottom-style", "border-bottom-color", "border-left", "border-left-width", "border-left-style", "border-left-color", "-webkit-border-radius", "-moz-border-radius", "border-radius", "-webkit-border-top-left-radius", "-moz-border-radius-topleft", "border-top-left-radius", "-webkit-border-top-right-radius", "-moz-border-radius-topright", "border-top-right-radius", "-webkit-border-bottom-right-radius", "-moz-border-radius-bottomright", "border-bottom-right-radius", "-webkit-border-bottom-left-radius", "-moz-border-radius-bottomleft", "border-bottom-left-radius", "-webkit-border-image", "-moz-border-image", "-o-border-image", "border-image", "-webkit-border-image-source", "-moz-border-image-source", "-o-border-image-source", "border-image-source", "-webkit-border-image-slice", "-moz-border-image-slice", "-o-border-image-slice", "border-image-slice", "-webkit-border-image-width", "-moz-border-image-width", "-o-border-image-width", "border-image-width", "-webkit-border-image-outset", "-moz-border-image-outset", "-o-border-image-outset", "border-image-outset", "-webkit-border-image-repeat", "-moz-border-image-repeat", "-o-border-image-repeat", "border-image-repeat", "padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "width", "min-width", "max-width", "height", "min-height", "max-height" ],
[ "font", "font-family", "font-size", "font-weight", "font-style", "font-variant", "font-size-adjust", "font-stretch", "font-effect", "font-emphasize", "font-emphasize-position", "font-emphasize-style", "font-smooth", "line-height", "text-align", "-webkit-text-align-last", "-moz-text-align-last", "-ms-text-align-last", "text-align-last", "vertical-align", "white-space", "text-decoration", "text-emphasis", "text-emphasis-color", "text-emphasis-style", "text-emphasis-position", "text-indent", "-ms-text-justify", "text-justify", "letter-spacing", "word-spacing", "-ms-writing-mode", "text-outline", "text-transform", "text-wrap", "-ms-text-overflow", "text-overflow", "text-overflow-ellipsis", "text-overflow-mode", "-ms-word-wrap", "word-wrap", "-ms-word-break", "word-break" ],
[ "color", "background", "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", "background-color", "background-image", "background-repeat", "background-attachment", "background-position", "-ms-background-position-x", "background-position-x", "-ms-background-position-y", "background-position-y", "-webkit-background-clip", "-moz-background-clip", "background-clip", "background-origin", "-webkit-background-size", "-moz-background-size", "-o-background-size", "background-size" ],
[ "outline", "outline-width", "outline-style", "outline-color", "outline-offset", "opacity", "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", "-ms-filter:\'progid:DXImageTransform.Microsoft.Alpha", "-ms-interpolation-mode", "-webkit-box-shadow", "-moz-box-shadow", "box-shadow", "filter:progid:DXImageTransform.Microsoft.gradient", "-ms-filter:\'progid:DXImageTransform.Microsoft.gradient", "text-shadow" ],
[ "-webkit-transition", "-moz-transition", "-ms-transition", "-o-transition", "transition", "-webkit-transition-delay", "-moz-transition-delay", "-ms-transition-delay", "-o-transition-delay", "transition-delay", "-webkit-transition-timing-function", "-moz-transition-timing-function", "-ms-transition-timing-function", "-o-transition-timing-function", "transition-timing-function", "-webkit-transition-duration", "-moz-transition-duration", "-ms-transition-duration", "-o-transition-duration", "transition-duration", "-webkit-transition-property", "-moz-transition-property", "-ms-transition-property", "-o-transition-property", "transition-property", "-webkit-transform", "-moz-transform", "-ms-transform", "-o-transform", "transform", "-webkit-transform-origin", "-moz-transform-origin", "-ms-transform-origin", "-o-transform-origin", "transform-origin", "-webkit-animation", "-moz-animation", "-ms-animation", "-o-animation", "animation", "-webkit-animation-name", "-moz-animation-name", "-ms-animation-name", "-o-animation-name", "animation-name", "-webkit-animation-duration", "-moz-animation-duration", "-ms-animation-duration", "-o-animation-duration", "animation-duration", "-webkit-animation-play-state", "-moz-animation-play-state", "-ms-animation-play-state", "-o-animation-play-state", "animation-play-state", "-webkit-animation-timing-function", "-moz-animation-timing-function", "-ms-animation-timing-function", "-o-animation-timing-function", "animation-timing-function", "-webkit-animation-delay", "-moz-animation-delay", "-ms-animation-delay", "-o-animation-delay", "animation-delay", "-webkit-animation-iteration-count", "-moz-animation-iteration-count", "-ms-animation-iteration-count", "-o-animation-iteration-count", "animation-iteration-count", "-webkit-animation-direction", "-moz-animation-direction", "-ms-animation-direction", "-o-animation-direction", "animation-direction" ],
[ "content", "quotes", "counter-reset", "counter-increment", "resize", "cursor", "-webkit-user-select", "-moz-user-select", "-ms-user-select", "user-select", "nav-index", "nav-up", "nav-right", "nav-down", "nav-left", "-moz-tab-size", "-o-tab-size", "tab-size", "-webkit-hyphens", "-moz-hyphens", "hyphens", "pointer-events" ]
]
复制代码
9)颜色
颜色16进制用小写字母;
颜色16进制尽量用简写。
/* not good */
.element {
color: #ABCDEF;
background-color: #001122;
}
/* good */
.element {
color: #abcdef;
background-color: #012;
}
复制代码
10)属性简写
属性简写需要你非常清楚属性值的正确顺序,而且在大多数情况下并不需要设置属性简写中包含的所有值,所以建议尽量分开声明会更加清晰;
margin 和 padding 相反,需要使用简写;
常见的属性简写包括:
fontbackgroundtransitionanimation
/* not good */
.element {
transition: opacity 1s linear 2s;
}
/* good */
.element {
transition-delay: 2s;
transition-timing-function: linear;
transition-duration: 1s;
transition-property: opacity;
}
复制代码
2. 公共组件
你列举讲下
3. 菜单配置
你列举讲下
4. tabs页面权限处理
5. 特定页面处理(抽屉, tabs等)
6. 三种常用压面布局处理
一个页面, 通常是先画好布局, 再处理内容, 布局中大概分为以下几类
1. 上部分输入查询下部分使用内容
首先写好页面布局,空出内容部分,同时写好布局样式(边距, 变宽, 长宽, 阴影, 背景色等)
布局用块级元素, 普通流布局就可以
其次内容使用组件插槽处理, 内容布局,样式在组件中处理
2. 左侧查询条件, 右侧内容页面
1. 写好页面左右布局, 设定好长宽, 该布局可使用浮动, 或者flex完成, 样式处理同上,
2. 内容, 可直接在布局中写, 或者使用插槽, 一般一个vue文件内不宜太多, 最少使用插槽或这个组件形式, 利于维护修改,同时增加阅读性
3. 上下布局,信息加表格
普通流布局 留出信息模块长宽, 读取数据渲染即可, 上部分内容比较简单的情况下, 可直接在一个页面中画下部分内容。
7. 常用技巧
vue常用技巧
1. 使用 $attrs 和 $listeners 进行多层级的数据和事件传递
先聊聊如何传递 Prop,可以分为静态和动态的 Prop:
<!-- 静态的prop -->
<blog-post title="My journey with Vue"></blog-post>
<!-- 动态的prop -->
<blog-post v-bind:title="post.title"></blog-post>
<!-- 动态的prop传递可以简写成 -->
<blog-post :title="post.title"></blog-post>
<!-- 需要传递多个prop的时候,可以一起写在v-bind上 -->
<blog-post v-bind="{ editable, title: post.title}"></blog-post>
复制代码
了解了 Props 的传递方式,在看看官方文档是怎么定义 $attrs 的, 在尤大大的文档中这样介绍了 $attrs:
$attrs: 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过v-bind="$attrs"传入内部组件
$attrs 包含了传入到父作用域中没有在 props 声明的其他 props,因此我们可以用 $attrs 去代替那些父组件中不需要的而子组件需要的 props, 通过 v-bind="$attrs" 统一传递给后代。这样就避免了一个个声明然后再一个个传递。
<blog-post v-bind="$attrs"></blog-post>
复制代码
上面这一行代码就通过 v-bind="$attrs" 的方式将本作用域中不作为 prop 的其他属性传递给了 blog-post 组件。
父组件通过 $attrs 传递给后代组件后,后代组件如果想通过触发事件来更新父组件状态该如何处理?如果一级一级地往上 emit 事件,会不会弄得代码太繁琐复杂了?在 Vue 中可以通过 $listeners 解决这个问题,先看看官方文档关于 $listeners 的说明:
包含了父作用域中的 (不含
.native修饰器的)v-on事件监听器。它可以通过v-on="$listeners"传入内部组件——在创建更高层次的组件时非常有用。
文档中说了 $listeners 包含了父作用域中的事件监听器。意思就是 $listeners 表示了父组件中的事件监听器集合,只要是触发父组件的事件,而不是自己的,就可以用一个 v-on="$listeners"表示。
<!-- 父组件(第一层组件) -->
<componentA @on-change="handleChange" v-bind="{ editable, title: post.title}" />
<!-- 中间层的组件 -->
<Child v-bind="$attrs" v-on="$listeners"/>
<!-- 数据传递的目标组件,事件触发的组件 -->
<div @click="handleClick">{{ title }} </div>
<script>
export default {
props: {
title: String
}
handleClick() {
this.$emit('on-change', 'New Title');
}
}
</script>
复制代码
上面的代码示例中,中间层的组件内通过 v-bind="$attrs" 将其余的 Prop 传递给了 Child 组件,再通过 v-on="$listeners" 绑定父作用域中的事件监听器,一旦 emit 就会传给了父组件。
2. 实现数据的双向绑定,方便维护数据
有很多这样的场景,父组件需要传递数据给子组件,且在子组件触发数据更新的时候,马上反馈给父组件,父组件数据更新,单向数据流向子组件,最后子组件更新。通常情况用 props + $emit 的方式去更新状态,但是这种处理方式有点笨拙,且不易维护,所以可以通过实现数据的“双向绑定”来提高代码的可维护性。可以通过这以下方式去实现:
使用 .sync 实现 Prop 的“双向绑定”
在 v-bind prop的时候添加 .sync 修饰符,赋新值的时候用 this.$emit('update:propName', newValue)
<!-- .sync是 v-on:update这种模式的一种缩写 -->
<Child v-on:update:title="title" />
<!-- 相当于 -->
<Child :title.sync="title" />
复制代码
如果要更新上述代码中的 title 值,只需要 this.$emit('update:title', '新标题'),完成数据更新。
使用 model 选项
model 是2.2.0+ 新增的选项,一个组件上的 v-model 默认会利用名为 value 的 Prop 和名为 input 的事件, 而 model 选项可以规定 Prop 名称和事件名称来实现 v-model,好处是在实现 v-model 的同时也避免了 Prop 和事件名的冲突。
<!-- 父组件 -->
<Model v-model="checked"/>
<!-- Model组件 -->
<div @click="handleClick">
<p>自定义组件的 v-model</p>
checked {{checked}}
</div>
<script lang="ts">
export default {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
methods: {
handleClick() {
this.$emit('change', !this.checked);
}
}
复制代码
在上述代码中,只需要在 model 选项中添加 prop 和 event,就可以实现了 v-model。而在 Vue + TS 项目中 vue-property-decorator 中提供了 Model 的装饰器,需要这么写:
@Model('change', { type: Boolean }) readonly checked!: boolean
handleClick() {
this.$emit('change', !this.checked);
}
复制代码
只需要通过 .sync 和 model 就可以实现数据的“双向绑定”,这样书写代码可以一定程度上减少我们的代码,而且另代码变得更优雅且可维护。
3. 使用 Mixins
Mixins 可以用于两种场景:
- 利用它去抽取成组件内的公共代码加强代码复用,不要在全局内套来套去,最好在组件内或者页面中使用。
- 利用它去分离功能点,有时候会遇到一种情况,就是业务功能很多导致写起来的 Vue 文件行数很多,导致代码很难以维护,功能点代码不好追踪。可以通过抽取功能代码的方式,让这个庞大的 Vue 文件更好维护。
首先写一个公共的 mixin 文件, 把高复用的状态和函数写进去。
export default class CommonMixins extends Vue{
public paginations = {
pageSize: 20,
total: 0,
currentPage: 1,
}
handleChangePageSize (pageSize: number, cb: Function) {
this.paginations.pageSize = pageSize;
cb();
}
handleChangePageNum (currentPage: number, cb: Function) {
this.paginations.currentPage = currentPage;
cb();
}
}
复制代码
vue-property-decorator 提供了 Mixins 的装饰器,在业务页面中引入 Mixin 只需要往里 Mixins 传入 , 可以传多个,表示混入多个 Mixin。
<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator';
import CommonMixins from "./common-mixin";
import PermissionMixins from "./permission-mixin";
@Component({})
export default class Parent extends Mixins(CommonMixins, PermissionMixins) {
}
</script>
复制代码
如果只需要一个的话,也可以直接继承
<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator';
import CommonMixins from "./common-mixin";
@Component({})
export default class Parent extends CommonMixins {
}
</script>
复制代码
在遇到功能点多,代码量大的页面时,我们可以利用 Mixin 抽离一些功能点,通过文件去管理这些功能,这样会比较方便去管理代码。
4. 使用动态组件去懒加载组件
组件在加载都是同步的,但当页面内容很多,有些组件并不需要一开始就加载出来的比如弹窗类的组件,这些就可以用动态组件,当用户执行了某些操作后再加载出来,这样可以提高主模块加载的性能, 在 Vue 中可以使用 component 动态组件, 依 is 的值,来决定哪个组件被渲染。
<template>
<div>
主页面 <br/>
<button @click="handleClick1">点击记载组件1</button><br/>
<button @click="handleClick2">点击记载组件2</button><br/>
<component :is="child1"></component>
<component :is="child2"></component>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component({})
export default class AsyncComponent extends Vue {
public child1:Component = null;
public child2:Component = null;
handleClick1() {
this.child1 = require('./child1').default;
}
handleClick2() {
this.child2 = require('./child2').default;
}
}
</script>
复制代码
示例代码中,只有当点击的时候才会去加载组件。component 还可以配合 v-show 去控制显示和隐藏,这样这个component 只会 mounted 一次,优化性能。
5. 在组件作用域内的 CSS 中使用 ::v-deep 修改组件样式
有很多场景想更改 UI 组件样式,然后怕影响别人的使用,加上 scoped 后又不能生效,可以使用 ::v-deep 深度作用选择器去修改组件作用域内的 CSS 的样式。在 CSS 中我们可以使用 >>> 操作符,但在预处理器中的写法就要用 /deep/ 或 ::v-deep。
<style scoped>
>>> .ivu-tabs-tabpane {
background: #f1f1f1;
}
</style>
<style lang="scss" scoped>
/deep/ .ivu-tabs-tabpane {
background: #f1f1f1;
}
</style>
<style lang="scss" scoped>
::v-deep .ivu-tabs-tabpane {
background: #f1f1f1;
}
</style>
复制代码
::v-deep 和 /deep/ 作用是一样的,但不推荐使用 /deep/, 在 Vue3.0 中将不支持 /deep/ 这种写法。
6. 使用装饰器优化代码
装饰器增加了代码的可读性,清晰地表达了意图,而且提供一种方便的手段,增加或修改类的功能,比如给类其中的方法提供防抖的功能。
import debounce from 'lodash.debounce';
export function Debounce(delay: number, config: object = {}) {
return (target: any, prop: string) => {
return {
value: debounce(target[prop], delay, config),
};
};
}
复制代码
这样的好处是使用起来非常方便,另外增加了代码的可读性。
@Debounce(300)
onIdChange(val: string) {
this.$emit('idchange', val);
}
复制代码
7. 利用 require.context 去获取项目目录信息
关于 require.context,webpack 文档是这么描述的:
可以给这个函数传入三个参数:一个要搜索的目录,一个标记表示是否还搜索其子目录, 以及一个匹配文件的正则表达式。
webpack 会在构建中解析代码中的
require.context()。如果想引入一个文件夹下面的所有文件,或者引入能匹配一个正则表达式的所有文件,这个功能就会很有帮助
根据这个提示,我们可以引用到一个文件夹下面的所有文件,由此可以利用获取的文件信息去做一些操作,比如在注册组件的时候,原本我们注册组件的时候需要一个个引入并且一个个注册,而且后面想加新的,又要再写上,现在可以通过 require.context 去优化这一段代码。
// import WmsTable from './wms-table/table/index';
import Table from './table/index.vue';
import CustomHooks from './custom-hooks/custom-hooks-actions/index';
import SFilter from './s-filter/filter-form';
import WButton from './button/index';
import CreateForm from './createForm/create-form/CreateForm.vue';
import Action from './table/action-table-column.vue';
import DetailItem from './detail-item.vue';
Vue.component('w-filter', SFilter);
Vue.component('w-button', WButton);
Vue.component('custom-hooks', CustomHooks);
Vue.component('create-form', CreateForm);
Vue.component('w-table', Table);
Vue.component('w-table-action', Action);
Vue.component('zonetime-date-picker', ZonetimeDatePicker);
Vue.component('detail', DetailItem);
复制代码
注册全局组件的时候,不需要一个一个 import,和一个个去注册,使用 require.context 可以自动导入模块,这样的好处在于,当我们新建一个组件,不用自己再去手写注册,而在一开始就帮我们自动完成。
const contexts = require.context('./', true, /.(vue|ts)$/);
export default {
install (vm) {
contexts.keys().forEach(component => {
const componentEntity = contexts(component).default;
if (componentEntity.name) {
vm.component(componentEntity.name, componentEntity);
}
});
}
};
复制代码
全局常用技巧
1. 建议使用 padding 代替 margin
我们在设计稿还原的时候,padding 和 margin 两个是常用的属性,但我们知道属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠,所以如果 margin 使用的过于频繁的时候,Box 的垂直距离可能就会发生重叠。
还有一个问题就是第一个子元素的 margin-top 值会加在父元素上的 bug(最后一个子元素的 margin-bottom 也存在类似的问题)。这里是不是有人问为什么呢?
原因就在于:
the expression collapsing margins means that adjoining margins (no non-empty content, padding or border areas or clearance separate them) of two or more boxes (which may be next to one another or nested) combine to form a single margin.
翻译过来就是:
所有毗邻的两个或多个盒元素的
margin将会合并为一个margin共享。 毗邻的定义为:同级或者嵌套的盒元素,并且它们之间没有非空内容、Padding或Border分隔。
至于为什么合并我个人觉得这和排队的安全距离有点类似,人与人之间的安全距离是 1m,如果安全距离不合并,那么我们在排队的时候是不是人与人的距离就变成 2m 了。当然很可能不是这个原因。
所以我们可以在首位元素使用 padding 来替代 margin。当然有的时候使用 padding 不能满足需求,这时你也可以在“非空内容”这个条件做文章。即在父元素添加一个伪元素。
所以我们在使用
margin的时候一定要注意 collapsing margins 问题。
2. position:fixed 降级问题
不知道曾经的你是不是遇到吸顶效果,就是使用 position:fixed 这个属性。其实如果其父元素中有使用 transform,fixed 的效果会降级为 absolute。
解决方案:
既然会降级为 absolute 效果,我们该怎么解决这个问题呢?我们就改考虑什么情况下 fixed 和 absolute 的表现效果会是一样的。
即当使用 fixed 的直接父元素的高度和屏幕的高度相同时 fixed 和 absolute 的表现效果会是一样的。
如果这个直接父级内的元素存在滚动的情况,那就加上
overflow-y: auto。
3. 合理使用 px | em | rem | % 等单位
在 CSS 中有许多距离单位,比如 px | em | rem | %,还有 CSS3 中的 vh | vw 等单位。
那么我们在项目中应该如何使用呢?我们在 pc 端不需要考虑的这么复杂,所以这里我们主要讲讲这些单位在移动端中的使用。
基础单位 px
px 是我们最早接触到的单位了,不过我们在移动端自适应的要求下,使用的频率不是很高;我总结了以下使用的情况:
比较小的图案
比如需要我们画一个 r 为 5px 的圆,如果我们使用 rem 作为单位,我们很快会发现在一些机型上的图案不圆,会呈现椭圆形。这是由于 rem 转 px 会存在精度丢失问题。
所以这个时候我们就需要使用 px 配合 dpr 来实现:
// less
/*@size 建议取双数*/
.circle(@size, @backgroundColor) {
width: @size;
height: @size;
background-color: @backgroundColor;
[data-dpr="1"] & {
width: @size * 0.5;
height: @size * 0.5;
}
[data-dpr="3"] & {
width: @size * 1.5;
height: @size * 1.5;
}
}
复制代码
1px 细线问题
这个问题下面我会单独做一小节讲,在这里就不累述。
字体大小(基本都是用 rem 作为单位)
一般情况字体的大小我也会使用 rem 作为单位,因为精度丢失我认为在可以接受的范围之内。
相对单位 rem
rem 是 CSS3 新增的一个相对单位(root em),即相对 HTML 根元素的字体大小的值。
rem 应该是自适应使用的最广泛的单位了。
相对单位 em
em 也是一个相对单位,却是相对于当前对象内文本的字体大小。
line-height
一般建议在 line-height 使用 em。因为在需要调整字体大小的时候,只需修改 font-size 的值,而 line-height 已经设置成了相对行高了。
首行缩进两个字符
在存在首行缩进的需求,我也会使用这个单位。
text-indent: 2em
复制代码
视口单位 vw | vh
vw: 1vw = 视口宽度的 1%
vh: 1vh = 视口高度的 1%
我们知道以 rem 单位设计的弹性布局,是需要在头部加载一段脚本来进行监听分辨率的变化来动态改变根元素字体大小,使得 CSS 与 JS 耦合了在一起。
那么有没有方案解决这个耦合的问题呢?
答案就是视口单位 vw | vh。
以下就是前人给出的使用方案:
$vm_fontsize: 75;
@function rem($px) {
@return ($px / $vm_fontsize ) * 1rem;
}
$vm_design: 750;
html {
font-size: ($vm_fontsize / ($vm_design / 2)) * 100vw;
@media screen and (max-width: 320px) {
font-size: 64px;
}
@media screen and (min-width: 540px) {
font-size: 108px;
}
}
// body 也增加最大最小宽度限制,避免默认100%宽度的 block 元素跟随 body 而过大过小
body {
max-width: 540px;
min-width: 320px;
}
复制代码
4. 合理使用变量
一般设计稿中的某一类的文字(元素)都是用相同的字体大小、颜色、行高等样式属性,所以这些值我们不必每次都重复写,因为当 UI 更新设计方案,你需要改的地方就很多了。这些重复使用的值我们完全可以存放在变量里面。
Sass 和 Less 稍微有点区别:
// sass
$direction: left;
// less
@direction: left;
复制代码
当然 CSS 原生也是存在变量的,使用规则如下:
变量定义的语法是: --*; // *为变量名称。
变量使用的语法是:var(*);
- 无论是变量的定义和使用只能在声明块 {} 里面
- CSS 变量字符限制为: [0-9]、[a-zA-Z]、_、-、中文和韩文等。
:root {
--blue_color: #3388ff;
--main_bgcolor: #fafafa;
--font_size_12: 12px;
--font_size_14: 14px;
--color: 20px;
}
.div1{
background-color: var(--main_bgcolor);
font-size: var(--font_size_12);
}
复制代码
5. 使用 Mixin 归类重复样式
和重复变量一样,重复的样式也可以归类。我觉得优秀的代码其中有一条肯定是代码的复用性强。
之前我们写 CSS 的时候,也会将一些重复使用的代码放在一个 class 中,这样的确达到了一定的复用性,不过最后的效果可能就是在一个元素里面放了很多 class,如下图:
这样下一个接手得人难免会有点迷糊,而且这样会造成样式越来越难修改。
这个时候,mixin( 可以理解成 class 中的 class )就能发挥它的作用了。
这是一个描述性文字的样式:
.font-description {
.font-des-style(24px,#fff,1.5em);
.line-camp(2);
}
// less
/* 多行显示 */
.line-camp( @clamp:2 ) {
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: @clamp;
-webkit-box-orient: vertical;
}
.font-des-style( @fontSize, @color, @lineHeight, @textAlign:left ) {
font-size: @fontSize;
color: @color;
line-height: @lineHeight;
text-align: @textAlign;
}
复制代码
这只是一个简单的例子,我们可以把可复用的样式放在 mixin 中,这样接手项目的人只需要熟悉你写的 mixin.less 就可以开始迭代需求了。
6. 1px 方案
做过移动端需求的前端肯定是避免不了处理 1px 细线问题,这个问题的原因就是 UI 对页面美观度的要求越来越高(不要和我说这是 retina 屏的问题)。
据小生所知好像没有什么兼容性特别好的方案,这里我只是提供两种种相对较好的方案。
使用伪类 + transform
.border_bottom {
overflow: hidden;
position: relative;
border: none!important;
}
.border_bottom:after {
content: "";
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background-color: #d4d6d7;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
复制代码
当然这个方案在一些版本较低的机型也是会出现粗细不均、细线消失断裂的兼容性问题。不过现在已经 2019 年了,版本较低的机型也淘汰的差不多了。
使用 box-shadow 模拟
.border_bottom {
box-shadow: inset 0px -1px 1px -1px #d4d6d7;
}
复制代码
这个方案基本可以满足所有场景,不过有个缺点也就是颜色会变浅。
多谢 D文斌 分享的另一种方案: 这种方案对 dpr 做了不同的处理,可谓更加精细。
.min-device-pixel-ratio(@scale2, @scale3) {
@media screen and (min-device-pixel-ratio: 2), (-webkit-min-device-pixel-ratio: 2) {
transform: @scale2;
}
@media screen and (min-device-pixel-ratio: 3), (-webkit-min-device-pixel-ratio: 3) {
transform: @scale3;
}
}
.border-1px(@color: #DDD, @radius: 2PX, @style: solid) {
&::before {
content: "";
pointer-events: none;
display: block;
position: absolute;
left: 0;
top: 0;
transform-origin: 0 0;
border: 1PX @style @color;
border-radius: @radius;
box-sizing: border-box;
width: 100%;
height: 100%;
@media screen and (min-device-pixel-ratio: 2), (-webkit-min-device-pixel-ratio: 2) {
width: 200%;
height: 200%;
border-radius: @radius * 2;
transform: scale(.5);
}
@media screen and (min-device-pixel-ratio: 3), (-webkit-min-device-pixel-ratio: 3) {
width: 300%;
height: 300%;
border-radius: @radius * 3;
transform: scale(.33);
}
}
}
.border-top-1px(@color: #DDD, @style: solid) {
&::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
border-top: 1Px @style @color;
transform-origin: 0 0;
.min-device-pixel-ratio(scaleY(.5), scaleY(.33));
}
}
复制代码
7. 从 html 元素继承 box-sizing
在大多数情况下我们在设置元素的 border 和 padding 并不希望改变元素的 width,height 值,这个时候我们就可以为该元素设置 box-sizing:border-box;。
我不希望每次都重写一遍,而是希望他是继承而来的,那么我们可以使用如下代码:
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
复制代码
这样的好处在于他不会覆盖其他组件的 box-sizing 值,又无需为每一个元素重复设置 box-sizing: border-box;。
8. 内联首屏关键 CSS
性能优化中有一个重要的指标 —— 首次有效绘制(FMP),即指页面的首要内容(primary content)出现在屏幕上的时间。这一指标影响用户看到页面前所需等待的时间,而 内联首屏关键 CSS(即 Critical CSS,可以称之为首屏关键 CSS) 能给用户一个更好的心理预期。
如图:
我们知道内联 CSS 能够使浏览器开始页面渲染的时间提前,即在 HTML 下载完成之后就能渲染了。
既然是内联关键 CSS,也就说明我们只会将少部分的 CSS 代码直接写入 HTML 中。至于内联哪些 CSS 你可以使用 Critical。
9. 文字超出省略、文字两端对齐
需求中我们也经常遇到这样的需求,这里直接提供方案。
超出省略
.line-camp( @clamp:2 ) {
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: @clamp;
-webkit-box-orient: vertical;
}
复制代码
所遇到的问题:
-webkit-box-orient: vertical在使用 webpack 打包的时候这段代码会被删除掉,原因是optimize-css-assets-webpack-plugin这个插件的问题。
解决方案:
可以使用如下的写法:
.line-camp( @clamp:2 ) {
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: @clamp;
/*! autoprefixer: off */
-webkit-box-orient: vertical;
/* autoprefixer: on */
}
复制代码
两端对齐
// html
<div>姓名</div>
<div>手机号码</div>
<div>账号</div>
<div>密码</div>
// css
div {
margin: 10px 0;
width: 100px;
border: 1px solid red;
text-align: justify;
text-align-last:justify
}
div:after{
content: '';
display: inline-block;
width: 100%;
}
复制代码
8. 工具推荐
函数库
Lodash(推荐):github.com/lodash/loda…
Underscore:underscorejs.org/
Ramda:github.com/ramda/ramda
outils:github.com/proYang/out…
30-seconds-of-code:github.com/Chalarangel…
动画库
Animate.css
- CSS3 动画库,也是目前最通用的动画库。
- daneden.github.io/animate.css…
Anime.js:
- 一个强大的、轻量级的用来制作动画的javascript库
- animejs.com/
Hover.css:
- CSS hover 悬停效果,可以应用于链接、按钮、图片等等。
- github.com/IanLunn/Hov…
wow.js:
- 滚动展示动画
- WOW.js 依赖 animate.css,所以它支持 animate.css 多达 60 多种的动画效果。
- github.com/matthieua/W…
scrollReveal.js:
- 类似 wow.js
- github.com/jlmakes/scr…
Magic.css:
- css3 animation动画库
- github.com/miniMAC/mag…
Waves:
- 点击波纹效果
- github.com/fians/Waves
move.js:
- 一个小型的JavaScript库,通过JS来控制一系列的CSS动画顺序执行,使CSS3动画变得非常简单和优雅。
- github.com/visionmedia…
Velocity.js:
- 一个功能齐全的 JavaScript 动画套件,包括诸如淡入淡出、滚动、滚动、停止、结束、翻转等动画效果。
- github.com/julianshapi…
滚动库
iscroll - 平滑滚动插件:github.com/cubiq/iscro…
BetterScroll:
- github.com/ustbhuangyi…
- iscroll 的优化版,使移动端滑动体验更加流畅
mescroll:
- 移动端上拉刷新下拉加载
- www.mescroll.com/api.html
animatescroll:
- jQuery滚动到指定位置
- plugins.compzets.com/animatescro…
crossfade.js:
- jQuery图片模糊插件crossfade.js
- www.dowebok.com/104.html
jQuery Scrollbox:
- 图片文字滚动插件
- www.jq22.com/jquery-info…
liMarquee:
- jQuery无缝滚动插件
- www.dowebok.com/188.html
轮播图
Swiper:
- 常用于移动端网站的内容触摸滑动
- www.swiper.com.cn/
iSlider:
- 移动端滑动组件
- eux.baidu.com/iSlider/dem…
slip.js:
- 移动端跟随手指滑动组件,零依赖。
- github.com/binnng/slip…
OwlCarousel2:owlcarousel2.github.io/OwlCarousel…
slick:www.jq22.com/jquery-info…
myFocus:www.cosmissy.com/myfocus/tut…
WebSlides:github.com/webslides/w…
jQuery轮播插件slider:www.jq22.com/jquery-info…
滚屏
fullpage:www.jq22.com/jquery-info…
弹出框
layer:
- layer.layui.com/
- layui 独立维护的三大组件之一(layDate、layer、layim)
- 尽管 layui 中包含了这些组件,但因为它们受众群体广泛或应用广泛,特从 layui 中抽取出来个副本,可独立引用。我们也会对它们进行同步维护。
Bootbox.js:bootboxjs.com/
dialogBox:
- 基于 jQuery
- www.jq22.com/jquery-info…
easyDialog:www.h-ui.net/easydialog-…
消息通知
Notyf:
- 简单的响应式纯js消息通知插件
- www.htmleaf.com/jQuery/Ligh…
PNotify:
- 页面右上角的提示信息(非弹框提示)
- sciactive.com/pnotify/
- sciactive.github.io/pnotify/
- github.com/sciactive/p…
overhang.js:
- www.jq22.com/jquery-info…
- overhang.js 是一个JQuery插件显示即时通知、 确认或给定元素中的提示。
下拉框
select2:select2.org/
级联选择器
ustbhuangyi/picker
- github.com/ustbhuangyi…
- 移动端最好用的的筛选器组件、联动筛选
jQueryDistpicker:
颜色选择器
Bootstrap Colorpicker 2:github.com/farbelous/b…
时间选择器
layDate:www.layui.com/laydate/
wdatepicker
时间日期处理
Moment.js:
- Moment.js 是一个解析,验证,操作和显示日期和时间的 JavaScript 类库。
- momentjs.com
- github.com/moment/mome…
timeago.js:
- 轻量级的时间转换 Javascript 库
- github.com/hustcc/time…
表单验证
validator.js:github.com/chriso/vali…
jQuery Validation
- jQuery 表单校验
- github.com/jquery-vali…
Validation Plugin:
- jQuery插件
- www.imooc.com/learn/385
Validform:
- 一行代码搞定整站的表单验证!- Jquery表单验证插件
- validform.rjboy.cn/
分页插件
pagination:github.com/superRaytin…
富文本编辑器
wangEditor:www.wangeditor.com/
百度UEditor:ueditor.baidu.com/website/
CKEditor:ckeditor.com/
KindEditor:kindeditor.net/demo.php
MediumEditor:yabwe.github.io/medium-edit…
Simditor:simditor.tower.im/
Summernote:summernote.org/
Quill:quilljs.com/
Slate:github.com/ianstormtay…
Markdown 编辑器
Editor.md:pandao.github.io/editor.md/
树插件
图片懒加载
lazyload:github.com/tuupola/jqu…
瀑布流
Masonry:www.cnblogs.com/cjc917/p/74…
Metro风兼瀑布流布局效果www.lanrenzhijia.com/jquery/1985…
相册(图片滑动切换展示效果)
PC端:Viewer.js:fengyuanchen.github.io/viewerjs/
PC端:photoviewer
移动端:PhotoSwipe:www.helloweba.net/javascript/…
导航插件
okayNav:www.dowebok.com/204.html
视频播放器
Chimee
- 组件化H5播放器框架
- chimee.org/
- juejin.im/entry/68449…
flv.js
- Bilibili开源纯JavaScript编写的FLV播放器
- Flash 视频(FLV)播放器
- github.com/Bilibili/fl…
- www.toutiao.com/a6478550535…
ckplayer:www.ckplayer.com/
jplayer:www.jplayer.cn/
html5player:juejin.im/post/684490…
Video.js
- 开源、免费的HTML5和Flash视频播放器
- Video.js - 开源、免费的HTML5和Flash视频播放器
多青:dogeek.net/
弹幕视频播放器
DanmuPlayer - Html5弹幕视频播放器插件:github.com/chiruom/Dan…
jquery.danmu.js - jQuery弹幕插件:github.com/chiruom/jqu…
scroxt - 字幕字体滚动插件:github.com/chenjianfan…
复制粘贴插件
clipboard.js:github.com/zenorocha/c…
ZeroClipboard:github.com/zeroclipboa…
条形码插件
barcode
二维码插件
jquery.qrcode.js:github.com/jeromeetien…
拖拽
Draggabilly:www.jq22.com/jquery-info…
dragula:www.toutiao.com/a6491847196…
手机端事件
touch.js
文件上传
uploader
WebUploader:
- github.com/fex-team/we…
- HTML5 & FLASH 文件上传
代码高亮
hightlight:highlightjs.org/
前端国际化 i18n
i18next:github.com/i18next/i18…
vue-i18n:github.com/kazupon/vue…
前端系列——jquery.i18n.properties前端国际化解决方案“填坑日记”:www.cnblogs.com/landeanfen/…
基于jQuery.i18n.properties 实现前端页面的资源国际化:m.blog.csdn.net/aixiaoyang1…
地图
百度地图:lbsyun.baidu.com/
谷歌地图:www.runoob.com/googleapi/g…
高德地图:lbs.amap.com/
腾讯地图:lbs.qq.com/
网页即时通讯
LayIM:layim.layui.com/
闲聊么:www.xianliao.me/
数据可视化
ECharts :echarts.baidu.com/index.html
阿里云 DataV
PDF 阅读器
主题色提取
RGBaster:github.com/briangonzal…
Color Thief
vibrant.js
前端存储
ustbhuangyi/storage:
- 封装了sessionStorage和localStorage
- github.com/ustbhuangyi…
store.js
- 本地存储localstorage的封装
- github.com/jaywcjlove/…
localForage:github.com/localForage…
数据 Mock
Easy Mock:github.com/easy-mock/e…
Mockjs:
- mockjs.com
- 生成任意随机数据,拦截 Ajax 请求