Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

USB供电能力简介

发表于 2019-08-20

最近买了个行车记录仪,它通过USB接口供电,要求电源输入为5V2A,即要求电源的输出功率要达到10W。不想直接安装到车上,想插在电脑的USB接口上,测试行车记录仪的功能。那么电脑USB接口的供电能力足够吗?

USB标准供电能力

USB有多个标准,供电能力各不相同:

  • USB1.1 传输速率最高12Mbps; 电源供应 5V 500mA
  • USB2.0 传输速率最高480Mbps; 电源供应 5V 500mA
  • USB3.0 传输速率最高5Gbps; 电源供应 5V 900mA

最新的USB3.0标准输出电压为5V,输出电流最大为900mA,如果超过可能会因发热造成线路损坏。,因此电脑上的标准USB接口无法支持行车记录仪的正常使用。

注意: 输出电压由电源输出决定,但输出电流大小由负载决定。

USB快充技术

目前的快充技术基本有三种:USB-IF组织的USB PD、高通QC、联发科PEP/PE,其他手机厂商的快充方案大多来自这三个。

  • USB PD(快充标准)目前基本绑定在USB3.0(数据传输标准)和USB Type-C(接头物理标准)上了,也得到谷歌和苹果两大巨头的支持,非常有希望成为未来的统一标准。

  • 高通Quick Charge技术则是实际上被中高端智能手机采用最多的,小米、华硕、LG、乐视、中兴、HTC等最近两年的旗舰都是这个标准,三星FastCharge是QC的马甲,索尼也用QC。 由于硬件端和软件端都被强硬规定, 高通最新的QC 4.0已经使用了USB PD协议,成为了USB PD的一个分支。

  • 联发科PEP技术推广成本最低,广泛用于中低端手机,魅族mCharge也是基于PEP。 不过联发科的PE3.0有可能被苹果采用。

参考链接

  1. 关于USB快充你不得不知的几点问题,by EDN.
  2. 手机充电是用电脑 USB 快还是用插座快?,by zhihu.
  3. 瓦特,by wikipedia.
  4. 手机快充是跟数据线有关还是跟充电器有关?,by zhihu.

MSSQL2008中分页查询的实现

发表于 2019-08-17

在后台管理中,涉及分页操作,需要数据库支持limit和offset选项,但在MSSQL2008中这两个选项不支持,不过可以用ROW_NUMBER函数在SQL语句中实现数据分页效果。SQL代码示例如下:

1
2
3
4
5
6
SELECT * FROM  
(SELECT Users.id as id,name,sign,account,audit,roleName,RoleId, Users.deleteFlag as deleteFlag,
ROW_NUMBER() OVER (ORDER BY Users.createdAt) AS Seq
FROM Users LEFT JOIN Roles ON Users.RoleId=Roles.id)Users
Where deleteFlag=0 and
Seq > :offset_num and Seq<= :offset_num+:limit_num

参考链接

  1. How to use Offset . . .Fetch Next in SQL SERVER 2008 R2,by microsoft msdn.

关系数据库中存储树结构及前端还原的方法

发表于 2019-08-17

树形结构很常见,例如组织结构图、线性化讨论等。树形结构中,实例被称为节点(node),每个节点有多个子节点和一个父节点。最上层的节点叫根(root)节点,它没有父节点。最底层的节点叫叶(leaf)节点,它没有子节点。本文主要介绍如何在关系数据库中存储树结构,以及如何从关系数据库中恢复树结构。

关系数据库中存储树结构的方法

层级数据设计方案

在关系数据库中存储树结构主要有以下几种方案:

  • 邻接表模型

最常见的解决方案,在数据库表中添加parent_id字段,再引用同一张表中的其他记录的id。可通过一个外键约束维护这种关系。

优点:增加叶节点、修改节点或子树位置很方便。

缺点:查询节点的所有后代、删除子树很困难。

  • 路径枚举

路径枚举是一个由连续的直接层级关系组成的完整路径。例如/usr/local/lib。

优点:查询节点所有后代、插入新节点很方便。
缺点:依赖应用程序逻辑代码维护路径字符串,验证字符串正确性开销大。受限于字符串长度,树结构无法无限扩展。

  • 嵌套集

嵌套集解决方案是存储子孙节点的相关信息,而不是节点的直接祖先。通常使用两个数字nsleft和nsright编码每个节点,以表示子孙节点信息。

nsleft的数值小于该节点所有后代的ID,同时nsright的值大于该节点所有后代的ID。

确定这三个值(nsleft,ID,nsright)的简单方法是对树进行一次深度优先遍历,在逐层深入过程中依次递增地分配nsleft的值,并在返回时依次递增地分配nsright的值。

优点:查询给定节点祖先和后代很容易,删除非叶子节点,其后代会自动代替被删除节点成为其祖先节点的直接后代。

缺点:不易理解,插入和移动节点复杂。

  • 闭包表

闭包表通过额外的数据库表记录树中节点间父子的关系,包括直接的父子关系、间接的父子关系和指向自己的关系。

层级数据设计比较

层级数据设计比较
设计 表 查询子 查询树 插入 删除 引用完整性
邻接表 1 简单 困难 简单 简单 是
邻接表+递归查询 1 简单 简单 简单 简单 否
路径枚举 1 简单 简单 简单 简单 否
嵌套集 1 困难 简单 困难 困难 否
闭包集 2 简单 简单 简单 简单 是
  • 邻接表设计最简单,如果使用的数据库支持WITH或CONNECT BY PRIOR的递归查询,将使得邻接表查询更为高效。

前端从关系数据库中还原树结构

表结构,使用sequelize的对象表示:

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
'use strict';
module.exports = (sequelize, DataTypes) => {
const Role = sequelize.define('Role', {
id: {
type: DataTypes.STRING(50),
primaryKey: true
},
roleName: {
type: DataTypes.STRING(128),
allowNull: false
},
path: {
type: DataTypes.STRING(512),
allowNull: false,
unique: true,
comment: '角色路径唯一!'
},
deleteFlag:{
type: DataTypes.BOOLEAN,
defaultValue: false
}
}, {});
Role.associate = function(models) {
// associations can be defined here
Role.hasMany(models.User)
};
return Role;
};

递归生成角色树:

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
// 获取第一层节点
function getFirstLevel(recordList){
var firstLevelList=[];
for(let i=0;i< recordList.length;i++){
let record=recordList[i]
if (record.path.length === 2 && record.path.split('')[1] === '/' && parseInt(record.path.split('')[0])>=0){
record.label=record.roleName
firstLevelList.push(record)
}
}
return firstLevelList
}

// 指定根节点,递归生成角色树
function recursiveTree(parentRecord,recordList){
for (let i = 0; i < recordList.length; i++){
let record = recordList[i]
if(parentRecord.path==record.path.slice(0,-2)){
record=recursiveTree(record,recordList)
if (parentRecord.children==undefined){
parentRecord.children=[]
}
parentRecord.children.push(record)
}
}
parentRecord.label = parentRecord.roleName
return parentRecord
}

// 生成角色树
export function parseRoleTree(recordList){
var result = getFirstLevel(recordList);

for (let i=0;i<result.length;i++){
parent = result[i]
parent = recursiveTree(parent, recordList)
}

return result;
}

参考链接

  1. java递归生成树形结构菜单,by 伊宇紫.
  2. Bill Karwin著,谭振林,Push Chen译. SQL反模式[M].人民邮电出版社.

网页布局方法入门

发表于 2019-08-10 | 更新于 2023-01-13

使用CSS对网页布局进行设计是前端工程师的重要工作。下面即介绍各种CSS网页布局的方法,以及相应的技术。

居中方法

居中在布局中很常见,假设DOM文档结构如下,子元素要在父元素中居中:

1
2
3
<div class="parent">
<div class="child"></div>
</div>

水平居中

子元素为行内元素还是块状元素,宽度一定还是宽度未定,采取的布局方案不同。以下即根据子元素的类型给出对应水平居中方法:

  • 行内元素:对父元素设置text-align:center;
  • 定宽块状元素: 设置左右margin值为auto;
  • 不定宽块状元素: 设置子元素为display:inline,然后在父元素上设置text-align:center;
  • 通用方案: flex布局,对父元素设置display:flex;justify-content:center;

垂直居中

垂直居中对于子元素是单行内联文本、多行内联文本以及块状元素采用的方案是不同的。具体方法如下:

  • 父元素一定,子元素为单行内联文本:设置父元素的height等于行高line-height
  • 父元素一定,子元素为多行内联文本:设置父元素的display:table-cell或inline-block,再设置vertical-align:middle;
  • 块状元素:设置子元素position:absolute 并设置top、bottom为0,父元素要设置定位为static以外的值,margin:auto;

单列布局

单列布局如图1所示,可分成两种类型,有定宽、水平居中的特点。

  • 一种是header、content、footer宽度都相同,其一般不会占满浏览器的最宽宽度,但当浏览器宽度缩小低于其最大宽度时,宽度会自适应。
  • 一种是header、footer宽度为浏览器宽度,但content以及header和footer里的内容却不会占满浏览器宽度。

单列布局的两种形式

图1 单列布局的两种形式

对于第一种,对header、content、footer统一设置width或max-width,并通过margin:auto实现居中。

1
2
3
4
5
<div class="layout">
<div id="header">头部</div>
<div id="content">内容</div>
<div id="footer">尾部</div>
</div>
1
2
3
4
5
.layout{
/* width: 960px; *//*设置width当浏览器窗口宽度小于960px时,单列布局不会自适应。*/
max-width: 960px;
margin: 0 auto;
}

对于第二种,header、footer的内容宽度为100%,但header、footer的内容区以及content统一设置width 或 max-width,并通过margin:auto实现居中。

1
2
3
4
5
6
7
<div id="header">
<div class="layout">头部</div>
</div>
<div id="content" class="layout">内容</div>
<div id="footer">
<div class="layout">尾部</div>
</div>
1
2
3
4
5
.layout{
/* width: 960px; *//*设置width当浏览器窗口宽度小于960px时,单列布局不会自适应。*/
max-width: 960px;
margin: 0 auto;
}

二列&三列布局

二列布局的特征是侧栏固定宽度,主栏自适应宽度。三列布局的特征是两侧两列固定宽度,中间列自适应宽度。 之所以将二列布局和三列布局写在一起,是因为二列布局可以看做去掉一个侧栏的三列布局,其布局的思想有异曲同工之妙。

对于传统的实现方法,主要讨论上图2中前三种布局,经典的带有侧栏的二栏布局以及带有左右侧栏的三栏布局。使用flex技术,可实现图2的五种布局。

二列&三列布局

图2 二列&三列布局

实现二列&三列布局的CSS技术主要有:

float+margin

原理说明:设置两个侧栏分别向左向右浮动,中间列通过外边距给两个侧栏腾出空间,中间列的宽度根据浏览器窗口自适应。

1
2
3
4
5
<div id="content">
<div class="sub">sub</div>
<div class="extra">extra</div>
<div class="main">main</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
.sub{
width: 100px;
float: left;
}
.extra{
width: 200px;
float: right;
}
.main{
margin-left: 100px;
margin-right: 200px;
}

注意:

  • DOM文档的书写顺序,先写两侧栏,再写主面板,更换后则侧栏会被挤到下一列(圣杯布局和双飞翼布局都会用到)。  
  • 这种布局方式比较简单明了,但缺点是渲染时先渲染了侧边栏,而不是比较重要的主面板。

position+margin

原理说明:通过绝对定位将两个侧栏固定,同样通过外边距给两个侧栏腾出空间,中间列自适应。

1
2
3
<div class="sub">left</div>
<div class="main">main</div>
<div class="extra">right</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.sub, .extra {
position: absolute;
top: 0;
width: 200px;
}
.sub {
left: 0;
}
.extra {
right: 0;
}
.main {
margin: 0 200px;
}

注意:

  • 本方法不限制DOM书写顺序,先写主面板会使主面板部分优先渲染(一般主面板会比侧栏内容重要)。
  • 与上一种方法相比,本种方法是通过定位来实现侧栏的位置固定。
  • 如果中间栏含有最小宽度限制,或是含有宽度的内部元素,则浏览器窗口小到一定程度,主面板与侧栏会发生重叠。

圣杯布局(float + 负margin)

原理说明:

主面板设置宽度为100%,主面板与两个侧栏都设置浮动,常见为左浮动,这时两个侧栏会被主面板挤下去。通过负边距将浮动的侧栏拉上来,左侧栏的负边距为100%,刚好是窗口的宽度,因此会从主面板下面的左边跑到与主面板对齐的左边,右侧栏此时浮动在主面板下面的左边,设置负边距为负的自身宽度刚好浮动到主面板对齐的右边。为了避免侧栏遮挡主面板内容,在外层设置左右padding值为左右侧栏的宽度,给侧栏腾出空间,此时主面板的宽度减小。由于侧栏的负margin都是相对主面板的,两个侧栏并不会像我们理想中的停靠在左右两边,而是跟着缩小的主面板一起向中间靠拢。此时使用相对布局,调整两个侧栏到相应的位置。

双飞翼布局(float + 负margin )

原理说明:

双飞翼布局和圣杯布局的思想有些相似,都利用了浮动和负边距,但双飞翼布局在圣杯布局上做了改进,在main元素上加了一层div, 并设置margin,由于两侧栏的负边距都是相对于main-wrap而言,main的margin值变化便不会影响两个侧栏,因此省掉了对两侧栏设置相对布局的步骤。

flex布局

与之前所讲的几种传统布局方案相比,flex布局的代码可谓异常简洁,而且非常通用,利用简单的三行CSS即实现了常见的五种布局。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="layout">
<aside class="aside">侧边栏宽度固定</aside>
<div class="main">主内容栏宽度自适应</div>
</div>
<div class="layout">
<div class="main">主内容栏宽度自适应</div>
<aside class="aside">侧边栏宽度固定</aside>
</div>
<div class="layout">
<aside class="aside">左侧边栏宽度固定</aside>
<div class="main">主内容栏宽度自适应</div>
<aside class="aside">右侧边栏宽度固定</aside>
</div>
<div class="layout">
<aside class="aside">第1个侧边栏宽度固定</aside>
<aside class="aside">第2个侧边栏宽度固定</aside>
<div class="main">主内容栏宽度自适应</div>
</div>
<div class="layout">
<div class="main">主内容栏宽度自适应</div>
<aside class="aside">第1个侧边栏宽度固定</aside>
<aside class="aside">第2个侧边栏宽度固定</aside>
</div>
1
2
3
4
5
6
7
8
9
.layout {
display: flex;
}
.main {
flex: 1;
}
.aside {
width: 200px;
}

自适应与响应式布局

自适应是为了解决如何才能在不同大小的设备上呈现同样的网页,实现一次设计,普遍适用而提出的技术,以避免维护多个版本的针对不同分辨率设备的网页。

但自适应存在一个问题,如果屏幕太小,即使网页能够根据屏幕大小进行适配,但是会感觉在小屏幕上查看,内容过于拥挤。响应式正是为了解决这个问题而衍生出来的概念。它可以自动识别屏幕宽度、并做出相应调整的网页设计,布局和展示的内容可能会有所变动。

目前网页的响应式布局解决方案成为当前主流,并因 twitter 开源的 bootstrap 而被大家熟知。响应式布局的实现原理如下:

  • 允许网页宽度自动调整

网页代码的头部,加入一行viewport元标签。

1
<meta name="viewport" content="width=device-width, initial-scale=1" />

viewport是网页默认的宽度和高度,上面这行代码的意思是,网页宽度默认等于屏幕宽度(width=device-width),原始缩放比例(initial-scale=1)为1.0,即网页初始大小占屏幕面积的100%。

  • 尽量少使用绝对宽度

由于网页会根据屏幕宽度调整布局,所以不能使用绝对宽度的布局,也不能使用具有绝对宽度的元素。这一条非常重要。具体说,CSS代码不能指定像素宽度:

1
width:xxx px;

通过指定百分比宽度来替代:同时还可以配合css的cal,进行计算宽度。

1
2
width: xx%;
width:auto;
  • 相对大小的字体

字体也不能使用绝对大小(px),而只能使用相对大小(em)或者高清方案(rem),rem不局限于字体大小,前面的宽度width也可以使用,代替百分比。

1
2
3
body {
font: normal 100% Helvetica, Arial, sans-serif;
}

上面的代码指定,字体大小是页面默认大小的100%,即16像素。

1
2
3
h1 {
  font-size: 1.5em;
}

然后,h1的大小是默认大小的1.5倍,即24像素(24/16=1.5)。

  • 流动布局(fluid grid)

“流动布局”的含义是,各个区块的位置都是浮动的,不是固定不变的。

1
2
3
4
5
6
7
8
9
.main {
  float: right;
  width: 70%;
}

.leftBar {
  float: left;
  width: 25%;
}

float的好处是,如果宽度太小,放不下两个元素,后面的元素会自动滚动到前面元素的下方,不会在水平方向overflow(溢出),避免了水平滚动条的出现。

  • 选择加载CSS

“自适应网页设计”的核心,就是CSS3引入的Media Query模块。

它的意思就是,自动探测屏幕宽度,然后加载相应的CSS文件。

1
2
3
4
5
6
7
<link rel="stylesheet" type="text/css"
    media="screen and (max-device-width: 400px)"
    href="tinyScreen.css" />

<link rel="stylesheet" type="text/css"
    media="screen and (min-width: 400px) and (max-device-width: 600px)"
    href="smallScreen.css" />

除了用html标签加载CSS文件,还可以在现有CSS文件中加载。

1
@import  url("tinyScreen.css") screen and (max-device-width: 400px);
  • CSS的@media规则

同一个CSS文件中,也可以根据不同的屏幕分辨率,选择应用不同的CSS规则。

1
2
3
4
5
6
7
8
9
10
@media  screen and (max-device-width: 400px) {
  .column {
    float: none;
    width:auto;
  }

  #sidebar {
    display:none;
  }
}
  • 图片的自适应(fluid image)

除了布局和文本,”自适应网页设计”还必须实现图片的自动缩放。这只要一行CSS代码:

1
img { max-width: 100%;}

布局相关CSS

display

display CSS 属性指定了元素的显示类型,它包含两类基础特征,用于指定元素怎样生成盒模型——外部显示类型定义了元素怎样参与流式布局的处理,内部显示类型定义了元素内子元素的布局方式。

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
/* <display-outside> values */
display: block;
display: inline;
display: run-in;

/* <display-inside> values */
display: flow;
display: flow-root;
display: table;
display: flex;
display: grid;
display: ruby;

/* <display-outside> plus <display-inside> values */
display: block flow;
display: inline table;
display: flex run-in;

/* <display-listitem> values */
display: list-item;
display: list-item block;
display: list-item inline;
display: list-item flow;
display: list-item flow-root;
display: list-item block flow;
display: list-item block flow-root;
display: flow list-item block;

/* <display-internal> values */
display: table-row-group;
display: table-header-group;
display: table-footer-group;
display: table-row;
display: table-cell;
display: table-column-group;
display: table-column;
display: table-caption;
display: ruby-base;
display: ruby-text;
display: ruby-base-container;
display: ruby-text-container;

/* <display-box> values */
display: contents;
display: none;

/* <display-legacy> values */
display: inline-block;
display: inline-table;
display: inline-flex;
display: inline-grid;

/* Global values */
display: inherit;
display: initial;
display: unset;

position

CSS position属性用于指定一个元素在文档中的定位方式。top,right,bottom 和 left 属性则决定了该元素的最终位置。position 属性被指定为从下面的值列表中选择的单个关键字:

  • static

该关键字指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置。此时 top, right, bottom, left 和 z-index 属性无效。

  • relative

该关键字下,元素先放置在未添加定位时的位置,再在不改变页面布局的前提下调整元素位置(因此会在此元素未添加定位时所在位置留下空白)。position:relative 对 table-*-group, table-row, table-column, table-cell, table-caption 元素无效。

  • absolute

不为元素预留空间,通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置。绝对定位的元素可以设置外边距(margins),且不会与其他边距合并。

  • fixed

不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先。

  • sticky

盒位置根据正常流计算(这称为正常流动中的位置),然后相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位。在所有情况下(即便被定位元素为 table 时),该元素定位均不对后续元素造成影响。当元素 B 被粘性定位时,后续元素的位置仍按照 B 未定位时的位置来确定。position: sticky 对 table 元素的效果与 position: relative 相同。

float

float CSS属性指定一个元素应沿其容器的左侧或右侧放置,允许文本和内联元素环绕它。该元素从网页的正常流动(文档流)中移除,尽管仍然保持部分的流动性(与绝对定位相反)。float 属性的值被指定为单一的关键字,值从下面的值列表中选择:

  • left

表明元素必须浮动在其所在的块容器左侧的关键字。

  • right

表明元素必须浮动在其所在的块容器右侧的关键字。

  • none

表明元素不进行浮动的关键字。

  • inline-start

关键字,表明元素必须浮动在其所在块容器的开始一侧,在ltr脚本中是左侧,在rtl脚本中是右侧。

  • inline-end

关键字,表明元素必须浮动在其所在块容器的结束一侧,在ltr脚本中是右侧,在rtl脚本中是左侧。

float属性具有包裹性和高度欺骗两个特性。

包裹性

block元素不指定width的话,默认是100%,一旦让该div浮动起来,立刻会像inline元素一样产生包裹性,宽度会跟随内容自适应。(这也是通常float元素需要手动指定width的原因)。

float的包裹性

图3 float的包裹性

效果非常近似于display:inline-block。但相比之下,浮动能设定为左浮和右浮,但display:inline-block都是从左到右排列的。(还有些细微差别,两个display:inline-block间会有空隙,但两个float间没有。)

高度欺骗性

float的高度欺骗性

图4 float的高度欺骗性

外层div在没有手动设定height的前提下,其高度是由内部content的最大高度决定的,由于img的float使得img具有高度塌陷的欺骗性,让div误以为img的line-height为0,因此div的高度就是文字的匿名inline-box的inline-height。

闭合浮动

闭合浮动的实现方法很多,最合理的方法是用:after伪元素,思路是用:after元素在div后面插入一个隐藏文本”.”,隐藏文本用clear来实现闭合浮动。

1
2
3
4
5
6
7
8
9
10
.clearfix:after {
clear: both;
content: "."; //你头可以改成其他任意文本如“abc”
display: block;
height: 0; //高度为0且hidden让该文本彻底隐藏
visibility: hidden;
}
.clearfix {
*zoom: 1;
}
1
2
3
4
5
6
7
8
9
<div style="border:4px solid blue;" class="clearfix">
<div style="width:200px; border:4px solid red; float:left;">
我是浮动元素1
</div>
<div style="width:200px; border:4px solid yellow; float:left;">
我是浮动元素2
</div>
</div>
<div style="border:4px solid gray;">我是页脚</div>

参考链接

  1. CSS布局十八般武艺都在这里了,by Shelley Lee.
  2. Flex 布局教程:语法篇,by 阮一峰.
  3. Flex 布局教程:实例篇,by 阮一峰.
  4. Flexbox 布局的最简单表单,by 阮一峰.
  5. 自适应网页设计(Responsive Web Design),by 阮一峰.
  6. CSS Grid 网格布局教程,by 阮一峰.
  7. 响应式和自适应的区别,by LorinLuo.
  8. display,by mozilla.
  9. position,by mozilla.
  10. CSS浮动float详解,by 张歆琳.
  11. float,by mozilla.
  12. Scrolling a flexbox with overflowing content,by stackoverflow.
  13. How to Fix Overflow Issues in CSS Flex Layouts,by moduscreate.
  14. An Interactive Guide to Flexbox,by joshwcomeau.

Mockjs入门简介

发表于 2019-08-09

Mock.js可生成随机数据,拦截Ajax请求,实现前后端分离的目的。Mockjs支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜色等数据类型,增加了单元测试的真实性,具有用法简单,方便扩展和开发无侵入的特点。下面简单介绍Mockjs的用法。

开始 & 安装

Node (CommonJS)

1
2
# 安装
npm install mockjs
1
2
3
4
5
6
7
8
9
10
11
// 使用 Mock
var Mock = require('mockjs')
var data = Mock.mock({
// 属性 list 的值是一个数组,其中含有 1 到 10 个元素
'list|1-10': [{
// 属性 id 是一个自增数,起始值为 1,每次增 1
'id|+1': 1
}]
})
// 输出结果
console.log(JSON.stringify(data, null, 4))

语法规范

Mock.js 的语法规范包括两部分:

  • 数据模板定义规范(Data Template Definition,DTD)
  • 数据占位符定义规范(Data Placeholder Definition,DPD)

数据模板定义规范 DTD

数据模板中的每个属性由 3 部分构成:属性名、生成规则、属性值:

1
2
3
4
// 属性名   name
// 生成规则 rule
// 属性值 value
'name|rule': value

注意:

  • 属性名 和 生成规则 之间用竖线 | 分隔。
  • 生成规则 是可选的。
  • 生成规则 有 7 种格式:
    • ‘name|min-max’: value
    • ‘name|count’: value
    • ‘name|min-max.dmin-dmax’: value
    • ‘name|min-max.dcount’: value
    • ‘name|count.dmin-dmax’: value
    • ‘name|count.dcount’: value
    • ‘name|+step’: value
  • 生成规则 的 含义 需要依赖 属性值的类型 才能确定。
  • 属性值 中可以含有 @占位符。
  • 属性值 还指定了最终值的初始值和类型。

数据占位符定义规范 DPD

占位符 只是在属性值字符串中占个位置,并不出现在最终的属性值中。

占位符 的格式为:

1
2
@占位符
@占位符(参数 [, 参数])

注意:

  • 用 @ 来标识其后的字符串是 占位符。
  • 占位符 引用的是 Mock.Random 中的方法。
  • 通过 Mock.Random.extend() 来扩展自定义占位符。
  • 占位符 也可以引用 数据模板 中的属性。
  • 占位符 会优先引用 数据模板 中的属性。
  • 占位符 支持 相对路径 和 绝对路径。

Mock.Random

Mock.Random 是一个工具类,用于生成各种随机数据。

Mock.Random 的方法在数据模板中称为『占位符』,书写格式为 @占位符(参数 [, 参数]) 。

1
2
3
4
5
6
7
var Random = Mock.Random
Random.email()
// => "n.clark@miller.io"
Mock.mock('@email')
// => "y.lee@lewis.org"
Mock.mock( { email: '@email' } )
// => { email: "v.lewis@hall.gov" }

参考链接

  1. Mockjs,by nuysoft.
  2. Mockjs语法规范,by nuysoft.
  3. Mock.Random,by nuysoft.

软件设计模式简介

发表于 2019-08-06 | 更新于 2022-12-07

软件模式是将模式的一般概念应用于软件开发领域,即软件开发的 总体指导思路或参照样板。软件模式并非仅限于设计模式,还包括 架构模式、分析模式和过程模式等,实际上,在软件生存期的每一个阶段都存在着一些被认同的模式。下面重点介绍各种经典的设计模式。

创建型模式

简单工厂模式( Simple Factory Pattern )

工厂方法模式(Factory Method Pattern)

抽象工厂模式(Abstract Factory)

建造者模式

单例模式

单例是指在程序运行周期内有且只有一个实例化的对象。

根据实例化的时机,主要分为饿汉和懒汉两种。

饿汉

第一次获取实例前就已经创建好。

1
2
3
4
5
6
7
8
9
10
11
class Singleton {
public:
static Singleton* GetInstance();
private:
static Singleton* instance_;
};
Singleton* instance_ = new Singleton(); // 已经创建好

Singleton* Singleton::GetInstance() {
return instance_;
}

懒汉

第一次获取实例时才创建。懒汉方式下会存在线程安全问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Singleton {
public:
static Singleton* GetInstance();
private:
static Singleton* instance_;
};
Singleton* instance_ = nullptr; // 静态变量的初始化,默认为nullptr

Singleton* Singleton::GetInstance() {
if (instance_ == nullptr) {
instance_ = new Singleton(); // instance_为空时才创建
}
return instance_;
}

结构型模式

适配器模式

桥接模式

装饰模式

外观模式

享元模式

代理模式

行为型模式

命令模式

中介者模式

观察者模式

状态模式

策略模式

参考链接

  1. 图说设计模式,by me115.
  2. 【置顶】探究osg中的程序设计模式【目录】,by 3wwang.
  3. 解释器模式(详解版),by c.biancheng.
  4. C++各类设计模式及实现详解,by linux.
  5. The Catalog of Design Patterns,by refactoringguru.
  6. C++ 静态单例,by sinat_31135199.
  7. 单例模式对象的删除,by hahacaidao.

UML类图中类之间关系

发表于 2019-08-06 | 更新于 2020-03-31

UML类图中类之间的关系主要有六种:泛化、实现、聚合、组合、依赖、关联。如图1所示。

UML类图

  • 车的类图结构为<>,表示车是一个抽象类;
  • 它有两个继承类:小汽车和自行车;它们之间的关系为实现关系,使用带空心箭头的虚线表示;
  • 小汽车为与SUV之间也是继承关系,它们之间的关系为泛化关系,使用带空心箭头的实线表示;
  • 小汽车与发动机之间是组合关系,使用带实心箭头的实线表示;
  • 学生与班级之间是聚合关系,使用带空心箭头的实线表示;
  • 学生与身份证之间为关联关系,使用一根实线表示;
  • 学生上学需要用到自行车,与自行车是一种依赖关系,使用带箭头的虚线表示;

熟悉这六种关系及其符号表示,有利于我们设计类图和同行之间交流。下面介绍这六种关系。

泛化关系(generalization)

类的继承结构表现在UML中为:泛化(generalize)与实现(realize):

继承关系为 is-a的关系;两个对象之间如果可以用 is-a 来表示,就是继承关系:(..是..)

例如:自行车是车、猫是动物。

泛化关系用一条带空心箭头的直接表示;如下图表示;

泛化关系

汽车在现实中有实现,可用汽车定义具体的对象;汽车与SUV之间为泛化关系;最终代码中,泛化关系表现为继承非抽象类;

实现关系(realize)

实现关系用一条带空心箭头的虚线表示;

例如:”车”为一个抽象概念,在现实中并无法直接用来定义对象;只有指明具体的子类(汽车还是自行车),才可以用来定义对象(”车”这个类在C++中用抽象类表示,在JAVA中有接口这个概念,更容易理解)

实现关系

最终代码中,实现关系表现为继承抽象类;

聚合关系(aggregation)

聚合关系用一条带空心菱形箭头的直线表示,如下图表示A聚合到B上,或者说B由A组成;

聚合关系

聚合关系用于表示实体对象之间的关系,表示整体由部分构成的语义;例如一个部门由多个员工组成;

与组合关系不同的是,整体和部分不是强依赖的,即使整体不存在了,部分仍然存在;例如, 部门撤销了,人员不会消失,他们依然存在;

组合关系(composition)

组合关系用一条带实心菱形箭头直线表示,如下图表示A组成B,或者B由A组成;

组合关系

与聚合关系一样,组合关系同样表示整体由部分构成的语义;比如公司由多个部门组成;

但组合关系是一种强依赖的特殊聚合关系,如果整体不存在了,则部分也不存在了;例如, 公司不存在了,部门也将不存在了;

关联关系(association)

关联关系是用一条直线表示的;它描述不同类的对象之间的结构关系;它是一种静态关系, 通常与运行状态无关,一般由常识等因素决定的;它一般用来定义对象之间静态的、天然的结构; 所以,关联关系是一种“强关联”的关系;

比如,乘车人和车票之间就是一种关联关系;学生和学校就是一种关联关系;

关联关系默认不强调方向,表示对象间相互知道;如果特别强调方向,如下图,表示A
知道B,但 B不知道A;

关联关系

在最终代码中,关联对象通常是以成员变量的形式实现的;

依赖关系(dependency)

依赖关系是用一套带箭头的虚线表示的;如下图表示A依赖于B;他描述一个对象在运行期间会用到另一个对象的关系;

依赖关系

与关联关系不同的是,它是一种临时性的关系,通常在运行期间产生,并且随着运行时的变化; 依赖关系也可能发生变化;

显然,依赖也有方向,双向依赖是一种非常糟糕的结构,我们总是应该保持单向依赖,杜绝双向依赖的产生;

在最终代码中,依赖关系体现为类构造方法及类方法的传入参数,箭头的指向为调用关系;依赖关系除了临时知道对方外,还是“使用”对方的方法和属性;

参考链接

  1. 看懂UML类图和时序图,by me115.

提高效率的在线工具

发表于 2019-08-06

在此记录一些常用在线工具,提高工作效率。

百度文库在线下载

在某个文档页面的网址域名后面加上 vvv 三个字母,访问后就可以快捷下载。

比如:

https://wenku.baidu.com/view/3e6d30b2fd0a79563c1e72bd.html

加上 vvv 为:

https://wenku.baiduvvv.com/view/3e6d30b2fd0a79563c1e72bd.html

商品历史价格查询

在电商网站商品详情页的网址域名后面加入vvv三个字母访问后即可查询该商品的历史价格。

例如:

https://detail.tmall.com/item.htm?id=527080261251

添加vvv后变为:

https://detail.tmallvvv.com/item.htm?id=527080261251

参考链接

  1. VVV文档在线导出工具,by vvv.
  2. 商品历史价格查询,by hisprice.

Nodejs调试入门

发表于 2019-07-31 | 更新于 2024-01-21

调试是每个程序员必备的技能,因此选择合适的调试工具能极大地方便我们调试代码。Node.js 的调试方式也有很多,常见的有:

  1. 万能的 console.log
  2. debugger
  3. node –inspect + chrome devtools

本文重点介绍最有用的node –inspect方法。具体步骤如下:

  1. node –inspect app.js
  2. 打开 Chrome 浏览器,访问 chrome://inspect,选择第一步的调试目标。
  3. 在chrome访问源代码,设置断点调试。

参考链接

  1. 《Node.js 调试指南》,by nswbmw.
  2. Node.js Power Tools: 13 Libraries Every Developer Should Know,by Pinjari Rehan.

Mock数据

发表于 2019-07-28 | 更新于 2019-08-08

在实际开发中,最理想的前后端交互方式当然是后端先帮前端 mock 数据,然后前端开发,但现实很骨感,总会因为种种原因,前端需要自己来 mock 假数据。常用的伪造假数据的库有:Mockjs和easy-mock。这两个库有各自问题,因此需要一种新的 mock 假数据方案。

Mockjs

原理是: 拦截了所有的请求并代理到本地,然后进行数据模拟,所以你会发现 network 中没有发出任何的请求。

但它的最大的问题是就是它的实现机制。它会重写浏览器的XMLHttpRequest对象,从而才能拦截所有请求,代理到本地。

大部分情况下用起来还是蛮方便的,但就因为它重写了XMLHttpRequest对象,所以比如progress方法,或者一些底层依赖XMLHttpRequest的库都会和它发生不兼容。

另外,因为是它是本地模拟数据,实际上不会走任何网络请求。所以本地调试起来很蛋疼,只能通过console.log来调试。

easy-mock

天然支持跨域,还是支持MockJs的所有语法。但因为用的人多了,它的免费服务会经常的挂,可以说天天挂。官方的建议是自己搭建服务。如果你的公司整体搭建一个这样的 mock 服务的话也是一个不错的选择。但大部分人可能还是没有这个技术条件的。

新的Mock假数据方案

本地会启动一个mock-server来模拟数据,线上环境还是继续使用mockjs来进行模拟(因为本项目是一个纯前端项目,你也可以自己搭建一个线上 server 来提供数据)。不管是本地还是线上所以的数据模拟都是基于mockjs生成的,所以只要写一套 mock 数据,就可以在多环境中使用。

该方案的好处是,在保留 mockjs 的优势的同时,解决之前的痛点。由于我们的 mock 是完全基于webpack-dev-serve来实现的,所以在你启动前端服务的同时,mock-server就会自动启动,这里还通过 chokidar 来观察 mock 文件夹内容的变化。在发生变化时会清除之前注册的mock-api接口,重新动态挂载新的接口,从而支持热更新。有兴趣的可以自己看一下代码 mock-server.js。由于是一个真正的server,所以你可以通过控制台中的network,清楚的知道接口返回的数据结构。并且同时解决了之前mockjs会重写 XMLHttpRequest对象,导致很多第三方库失效的问题。

vue.config.js中配置本地mock-server服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
devServer: {
port: port,
open: true,
overlay: {
warnings: false,
errors: true
},
proxy: {
// change xxx-api/login => mock/login
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: `http://127.0.0.1:${port}/mock`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
// 输入app为exress实例,即var app = express();
// after: function(app, server) {
// do fancy stuff
// }
after: require('./mock/mock-server.js')
}

参考链接

  1. 手摸手,带你用vue撸后台 系列五(v4.0新版本),by 花裤衩.
  2. Getting Started,by mockjs.
上一页1…363738…53下一页

Jack Huang

528 日志
68 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse