Content Table

Vue :key

我们都知道使用 v-for 渲染数组时都要给每个元素绑定一个 key,官方文档也说的很清 (挠) 楚 (头):

当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。

key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

相同父元素的子元素必须有独特的 key,重复的 key 会造成渲染错误。

需要记住一点: 当用来创建 DOM 元素的数据变化后,就会更新 DOM (更新了才能在界面上看到),为了提高效率,vue 使用了 key 来判断是复用已有的 DOM 还是创建新的 DOM (在同一个 parent DOM element 下进行比较):

  • 如果能找到相同 key 的 DOM 元素,则更新它的内容
  • 如果找不到相同 key 的 DOM 元素,则删除旧的 DOM 元素,新创建一个

上面的内容,懂的人一看就懂,不懂的人仍然是一头雾水。下面我们以一个很简单的修改用户名的例子演示一下应该就能明白什么时候复用,什么时候创建了。

JS 的正则表达式

JS 中可以用下面 3 种方式使用正则表达式获取数据:

  • RegExp.exec()
  • String.match()
  • String.replace(): 这个用法比较奇葩,但是我喜欢

下面就简单介绍下每一个的用法。

常用 SQL

创建数据库

1
CREATE DATABASE ebag DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci

增加新列

1
ALTER TABLE table_name ADD COLUMN column_name varchar(64)

分页查询

MySQL 中使用 LIMIT 进行分页,第一个参数是起始位置 offset,从 0 开始,第二个参数是要取多少条记录

1
SELECT * FROM question WHERE subject_code='XXX' LIMIT 0, 30

数据库规范

标准化和规范化

数据的标准化有助于消除数据库中的数据冗余。标准化有好几种形式,但 Third Normal Form (3NF) 通常被认为在性能、扩展性和数据完整性方面达到了最好平衡。简单来说,遵守 3NF 标准的数据库的表设计原则是:

  • 第一范式 (1NF) 无重复的列

    所谓第一范式 (1NF) 是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式 (1NF) 中表的每一行只包含一个实例的信息。简而言之,第一范式就是无重复的列。说明:在任何一个关系数据库中,第一范式 (1NF) 是对关系模式的基本要求,不满足第一范式 (1NF) 的数据库就不是关系数据库。

  • 第二范式 (2NF) 属性完全依赖于主键 (每个表要定义主键,如无意义自增长 id)

    第二范式 (2NF) 是在第一范式 (1NF) 的基础上建立起来的,即满足第二范式 (2NF) 必须先满足第一范式 (1NF)。第二范式 (2NF) 要求数据库表中的每个实例或行必须可以被唯一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。例如员工信息表中加上了员工编号 (emp_id) 列,因为每个员工的员工编号是惟一的,因此每个员工可以被惟一区分。这个惟一属性列被称为主关键字或主键、主码。

    第二范式 (2NF) 要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是属性完全依赖于主键。

  • 第三范式(3NF)属性不依赖于其它非主属性 (表中最多包含其他表中的主键,即外键)

    满足第三范式 (3NF) 必须先满足第二范式 (2NF)。简而言之,第三范式 (3NF) 要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。例如,存在一个部门信息表,其中每个部门有部门编号 (dept_id)、部门名称、部门简介等信息。那么在的员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式 (3NF) 也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性。

拓扑排序在并行任务调度中的运用

如下图所示的一个任务设计:

  • 任务 1 和任务 2 可以同时执行,等他们都结束后才能执行任务 3
  • 任务 4 执行结束后可以同时执行任务 5 和任务 6
  • 任务 8 执行结束后则任务执行完成

如果用程序来实现这个任务的调度,触发任务的执行,应该怎么做呢?

通过观察,这是一个有向无环图,要访问某个节点 (执行任务),需要先访问它的所有前驱节点,而且每个节点只能访问一次,这是一个典型的可以使用图的拓扑排序解决的问题。

Lambda and Stream 笔记

创建 Stream:

1
2
3
4
Stream<String>  stringStream1  = list.stream();
Stream<String> stringStream2 = Stream.of("taobao");
Stream<Integer> integerStream1 = Stream.of(1, 2, 3, 5);
Stream<Integer> integerStream1 = Stream.iterate(1, item -> item + 1).limit(10);

Fabric 笔记

Fabric 是基于 HTML5 canvas 的图形库,通过操作对象的属性来操作 canvas (叫 object model),已经提供了一些基本的图元,如 Rect, Circle, Ellipse, Line, Polygon, Image, SVG, Text, Textbox, 阴影等,支持动画、拖动放大缩小旋转等,还支持自由绘制,能够序列化为字符串方便保存,需要的时候再把字符串反序列化为 fabric.Canvas 对象。

Fabric.js is a powerful and simple Javascript HTML5 canvas library. Fabric provides interactive object model on top of canvas element. Fabric also has SVG-to-canvas (and canvas-to-SVG) parser.

下面列举一些可能会常用到的知识点:

  • 全局设置默认值: fabric.Object.prototype.transparentCorners = false (cornerSize: 7)
  • 导出时包含指定 / 自定义属性: canvas.toJSON(['name', 'lockScalingY'],默认很多属性都没有到处,根据自己的需要在这里指定
  • 设置选中 item 的属性: canvas.getActiveObject().set("fontFamily", 'Monaco')
  • 修改整个 canvas 的鼠标: canvas.defaultCursor = 'move'
  • <canvas width="100" height="100"> 的大小被 Canvas 中指定的值覆盖: new fabric.Canvas('c', { width: 600, height: 600 })
  • 控制点 controls 指的是选中图形后在图形边框上出现的 8 个用来控制图形大小的小方框,名字为 tl, tr, br, bl, ml, mt, mr, mb
  • canvas.renderAll() 会立即重绘,canvas.requestRenderAll() 则是把重绘的命令放到事件队列里,如果准备放入时已经存在则丢弃,不重复放入队列中
  • 常用属性: Well, as you would expect there are those related to
    • positioning: left, top
    • dimension: width, height
    • rendering: fill, opacity, stroke, strokeWidth
    • scaling and rotation: scaleX, scaleY, angle
    • flipping: flipX, flipY
    • skewing: skewX, skewY

Fabric Demos 里有很多例子,对学习 Fabric 非常有帮助。

微信网页中精确定位

支持 H5 的浏览器提供了获取 GPS 的接口 navigator.geolocation.getCurrentPosition(success, error),由于各种原因,国内得到的定位都是不准确的,百度地图 API 也提供了定位接口 new BMap.Geolocation().getCurrentPosition(callback),不过也不准确,有时候会偏离几十公里,为了能够在浏览器里精确的定位,可以在微信浏览器中打开网页,使用微信的 JS SDK 中的 wx.getLocation() 进行定位得到大地坐标系 WGS84 的坐标,然后转换为百度 BD09 坐标,使用百度 API 获取此坐标对应的中文地址。

动态切换数据源

动态切换数据源的原理为使用 AbstractRoutingDataSource,根据数据源的名字查找数据源:

  1. 在 Spring IoC 容器中创建一个 AbstractRoutingDataSource 实现的对象 routingDataSource (Spring 默认没有使用此数据源)
  2. 创建一个数据源的 Map dataSourceMap,key 为 DataSource 的名字,value 为 DataSource 的对象
  3. 调用 routingDataSource.setTargetDataSources(dataSourceMap) 设置可供使用的数据源
  4. 在需要使用数据源的时候,Spring JDBC 会调用 routingDataSource.determineTargetDataSource() 获取数据源,而要返回哪个数据源,由 routingDataSource.determineCurrentLookupKey() 返回的数据源的名字决定

下面我们从最简单的方式实现数据源切换,然后一步一步的深入优化基于 AbstractRoutingDataSource 的数据源。

Promise 知识点

可以访问 Promise 对象基础 对 Promise 进行了解,这里就不再赘述了。

Promises 承诺,你将会得到延期或长期运行任务的未来结果。承诺有两个渠道:第一个为结果,第二个为潜在的错误。要获取结果,您将回调函数作为 then 函数参数,要处理错误,您将回调函数提供为 catch 函数参数。Promise 的状态的改变是单向的,一次性的,一旦改变,状态就会凝固了。

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
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
</head>

<body>
<script type="text/javascript">
let p = new Promise((resolve, reject) => {
setTimeout(function() {
const result = Math.random();
result > 0.5 ? resolve(result) : reject('Lower than 0.5');
}, 2000);
});

p.then(result => {
console.log('success', result);
}).catch(result => {
console.log('fail', result);
});
</script>
</body>

</html>