MySQL 获取随机条数据

场景

有一个需要从数据库随机获取指定数量的数据的需求,然而这个问题却是意外的挺麻烦。

假设有一个数据表

1
2
3
4
5
6
7
create table topic (
id int primary key not null
comment '编号',
content varchar(20) not null
comment '内容'
)
comment '主题表';

这里的 topic 表有两个关键性的特点

  • 主键可以进行比较(int
  • 主键整体存在趋势(自增/自减)

解决方案 1:直接使用 order by rand()

直接使用 order by rand() 就可以获取到随机的数据了,而且能够获取到全部的数据(顺序仍然是随机的)。

  1. 按照 rand() 产生的结果

    这一步相当于为每条数据加上一列由 rand() 函数产生的数据,然后对这一列进行排序

  2. 限制查询条数
1
2
3
4
select *
from topic
order by rand()
limit 50000;

但缺点很明显,速度是个问题,因为 rand() 的数据没有索引,所以会造成排序速度极慢。

在 10w 条数据中随机获取 5w 条数据,花费时常 6 s 378 ms,这个时间真的太长了点。

其实 order by rand() 看起来很奇怪,实际上等效于:

1
2
3
4
5
6
7
8
9
select *
from (
select
topic.*,
rand() as order_column
from topic
) as temp
order by order_column
limit 50000;

解决方案 2:使用 where subquery 取中间的随机值

因为 order by rand() 没有索引导致的排序太耗时,我们可以尝试绕过这个问题。

下面的这种解决方案便是如此

  1. 取最小值和最大值之间的随机值
  2. 判断 id 是否大于(或者小于)这个随机值
  3. 限制查询条数
1
2
3
4
5
6
7
8
9
10
select *
from topic
where id >= ((select max(id)
from topic)
- (select min(id)
from topic))
* rand()
+ (select min(id)
from topic)
limit 50000;

这种方法查询速度虽然极快(150 ms),但却会受到数据分布密度的影响。如果数据不是平均的,那么查询到的总数据条数就会受限。

那么,下面来说该方法的缺陷

  • 获取到的数据受分布密度影响

    例如数据分布呈以下情况

    1,100002,100003,100004...199999,200000

    那么使用上述代码就只能获取到很少一部分数据(大约在 2.5w 条左右)。然而如果将符号稍微下改一下,将 >= 修改为 <=,那么能够获取到的平均数量将大大增加(7.5w 条左右)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    select *
    from topic
    # 注意:这里的符号修改了
    where id >= ((select max(id)
    from topic)
    - (select min(id)
    from topic))
    * rand()
    + (select min(id)
    from topic)
    limit 50000;
  • 每一条数据获取到的概率不是完全相同的
    虽然获取到的全部数据是随机的,但每一个的概率却并不相同。例如在 <= 时会出现永远都为第一条的现象,究其原因就是因为 第一条 的概率实在是太大了,因为查询数据表时数据的检索规则是从第一条开始的呢!即便修改成 >=,所得到的第一条数据也普遍偏小。
    使用 >= 的结果

    • 数据越是在前面,那么获取到的概率就越低
    • 但即便是很低概率,在最前面总有机会,所以第一条一般偏小
    • 数据密度前面偏大时,获取到的数量会非常小

密度越是趋于平均,获取到的最大随机数据条数的平均值愈接近 1/2,否则则会愈加偏离(不一定偏大还是偏小)。

解决方案 3:使用临时表 temporary table

解决方案 2 着眼于避免使用没有索引的 rand() 进行排序,但这里思考另一个解决方案,使用加了索引之后的 rand() 进行排序。创建临时表,仅包含主键 id 和需要进行排序的索引列 randomId,然后排序完成过后获取到乱序数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
drop temporary table if exists temp_topic;
create temporary table temp_topic (
id bigint primary key not null,
randomId double not null,
index (randomId)
)
as
select
id,
rand() as randomId
from topic;
select t.*
from topic t
join (
select id
from (
select id
from temp_topic
order by randomId
) as temp
limit 50000
) as temp
on t.id = temp.id;

这种方法的查询速度不算很快(878 ms,相比于第二种),而且仍然是与数据量呈正相关的(因为要复制数据)。但和第一种,也是真正的随机获取。

解决方案 4:使用 join order by rand()

吾辈在 StackOverflow 上面看到了一个 最优解 by 2016,一切表现的都很好,速度不算慢(261 ms),也可以获取到全部数据,也是真正的随机获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
select g.*
from
topic g
join
(select id
from
topic
where
rand() < (select ((50000 / count(0)) * 10)
from
topic)
order by rand()
limit 50000) as z on z.id = g.id;

这里的 where rand() 是想要在获取的数据与总数据量差距过大(10 倍以上)时过滤数据,提高排序效率。

吾辈这里不清楚上面发生了什么,感觉 rand() 只要在 join 里面之后的排序就会变得很快!
事实上,只要使用 join 内连接之后,就算是 order by rand() 的效率也很高,甚至在获取少量数据时比上面使用 where rand 过滤数据后再排序的的速度还要快

1
2
3
4
5
6
7
SELECT t.*
FROM topic t
JOIN
(SELECT id
FROM `topic`
ORDER BY RAND()
LIMIT 50000) AS z ON z.id = t.id;

注:在获取的数据量很大时,使用上面那种 where 过滤一次的效率上会更有优势一点!

总结

这里有一篇不错的英文文章对随机获取数据进行了分析:http://jan.kneschke.de/projects/mysql/order-by-rand/,也有人在 StackOverflow 上进行了讨论 https://stackoverflow.com/questions/1823306/

不同点 order by rand() where subquery temporary table join order by rand()
可以随机获取全部 可以 几乎不可能 可以 可以
速度 极快 较快 极快
需要可比较的主键类型
受数据分布密度影响
速度受表数据复杂度影响 很大 极小 较小 极小

那么,看完上面的不同点对比,我们也可以得出它们的使用场景了

  • 强烈推荐首选 join order by rand() 作为随机获取数据的解决方案
  • 唯一不推荐的就是 order by rand(),这是新手才会写出来 sql。当然,如果你的数据量很小(1000 条以下)时,直接使用 order by rand() 以现代机器的性能 sql 也不会很慢呢

注:如果仅仅只是需要打乱数据顺序的话还是更推荐将数据读取到内存中在进行操作更好!

Windows 上 Maven 安装与使用

官网, GitHub

介绍

Maven 已经是 Java 事实上的依赖管理标准工具了,所以学习使用 maven 有益无害。

前置要求

下载

在官网 下载页面 找到 Binary zip archive 下载二进制数据。

Maven 下载页面

这里不使用二进制安装包的原因是绿色版更容易迁移,而且是跨平台的。

设置环境变量

将文件夹解压到某个位置,然后在环境变量 Path 中添加 /bin/ 目录

Maven 设置环境变量

验证安装

使用 cmd 打开命令行,输入 mvn --version,你应该得到了类似于下面的输出

查看 Maven 的版本

基本使用

  1. 创建一个普通的项目

    直接使用命令行根据模板创建项目在实际中极为罕见,这里只是演示一下 maven 可以使用命令行创建项目而已

    1
    2
    mvn archetype:generate -DgroupId=com.rxliuli.maven.example -DartifactId=HelloWorld -DarchetypeArtifactId
    =maven-archetype-quickstart -DinteractiveMode=false

    应该会得到如下输出

    Maven 使用命令行创建模板项目

  2. 进入项目

    项目目录结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    HelloWorld
    └─src
    ├─main
    │ └─java
    │ └─com
    │ └─rxliuli
    │ └─maven
    │ └─example
    └─test
    └─java
    └─com
    └─rxliuli
    └─maven
    └─example

    maven 项目的配置文件是 pom.xml,而 源码测试 代码则分离到了两个单独的文件夹

  3. maven 基本命令

    • package(mvn package): 打包项目
    • clean(mvn clean): 清理打包目录
    • test(mvn test): 执行 test 目录下的测试
    • install(mvn install): 打包项目并安装到本地

其实原生 maven 了解多少并没有什么,因为 IDE 基本都集成了这些开源的工具,并不需要我们手动输入 maven 命令了。嘛,多少了解一些也是挺好的啦ヽ(=^・ω・^=)丿

使用 vue-cli 创建模板项目

场景

吾辈曾经只是个 Java 后端开发人员,原本对前端的了解大致只有 HTML/CSS/JavaScript/JQuery 级别,后来接触到了 nodejs。不仅是工作之需,吾辈个人而言也非常想要了解现代前端技术。然而天可怜见,吾辈刚入门 nodejs 并没有发现什么,但发现想要构建一个项目,需要用到前端工具链实在太多了。配置文件的数量甚至远远超过后端。
所以为了快速开发,入门之后遇到问题再去解决,吾辈选择了使用 nodejs + npm + vuejs + webpack + vscode 组合,使用 vue-cli 快速搭建一个基于现代前端工具链前端项目。

致那些想要了解前端但又不得其门的后端开发者,第一步的入门是最重要/最困难的。

步骤

前置要求

想要继续向下阅读的话请务必确认你的 PC 上已经正确安装了 nodejs/npm,如果还未曾安装,请参考 nodejs 官网 进行安装

npm 已经默认包含在 nodejs 中了

第一步:全局安装 vue-cli

打开命令行,安装 vue-cli

1
npm install -g @vue/cli

安装完成后在命令行输入 vue 应该会有类似于以下的输出

vue-cli 安装完成验证

第二步:使用模板初始化一个项目

命令格式

1
2
# option 是选项,template 是使用的模板,app-name 是要初始化项目的名字
vue init [option] <template> <app-name>

例如我们使用 vue init webpack vue-webpack-example 初始化一个 webpack 模板的项目,大部分组件我们暂时还不需要,所以选择 vue-router 以及使用 npm 进行构建。

vue-cli 初始化 webpack 模板项目

初始化完成后我们在命令行进入文件夹 vue-webpack-example 中,现在我们可以通过 npm run dev 启动开发服务器模式和 npm run build 打包项目为静态文件

尝试使用 npm run dev 运行项目,最后应该会得到如下输出

vue 项目运行输出

在浏览器打开链接 http://localhost:8080

vue 项目模板

第三步:初始化模板的一些坑

当你使用 npm run build 打包好静态文件在 dist 目录后,从文件管理器直接运行,却发现浏览器只有一片空白。

vue 打包出来的文件在浏览器显示的空白页面

这是 vue-cli 默认模板的问题,具体原因与解决方案请参考 Vue 打包的静态文件不能直接运行

那么,这篇使用 vue-cli 简单的建立项目就到这里啦,希望各位后端开发者都能尝试有趣的现代前端呢 -(๑☆‿ ☆#)ᕗ

Vue 实现一个滚动到顶部的悬浮图标组件

场景

吾辈在写 vuejs 前端项目的时候,需要实现一个下拉文章列表时,出现一个悬浮按钮,用于一键回到文章顶部。

实现

实现源码放到了 GitHubDemo 演示 想直接看源码/效果的人可以直接去看,但最好看一下 注意点

思路

  1. 定义一个 vuejs 组件,抽取出最需要的几个属性(位置,组件的样子)
  2. 监听窗口滚动,当滚动到第二屏的时候显示组件
  3. 监听组件点击,点击即逐渐减少与顶端的距离
  4. 当在滚动中用户手动下拉时终止滚动
  5. 引用组件并传递一个 vue 模板

代码

定义模板 VxScrollToTop

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
一个 Vue 的滚动到顶部的容器组件(不提供 UI 显示)
使用:
1. 引入自定义文件上传组件: import VxScrollToTop from '@/components/common/VxScrollToTop'
2. 声明它
export default {
components: {
VxScrollToTop: VxScrollToTop
}
}
3. 在 template 中使用
<vx-scroll-to-top>
<!-- 这里面的内容随你定义,是上拉按钮要显示的样子 -->
<v-btn color="primary"
fab>
<v-icon>vertical_align_top</v-icon>
</v-btn>
</vx-scroll-to-top>
*/
<template>
<div :style="scrollToTopStyle"
v-show="showScrollToTop"
@click="scrollToTop">
<slot></slot>
</div>
</template>
<script>
export default {
props: {
// 定义上拉按钮容器的位置
top: {
type: [Number, String],
default: undefined
},
bottom: {
type: [Number, String],
default: undefined
},
left: {
type: [Number, String],
default: undefined
},
right: {
type: [Number, String],
default: undefined
}
},
data: () => ({
// 是否显示,初始默认不显示
showScrollToTop: false,
// 定时器
timer: null,
scrollToTopStyle: {
position: 'fixed',
top: '',
bottom: '',
left: '',
right: ''
}
}),
methods: {
isNumber (str) {
if (!new RegExp('^[0-9]+([.]{1}[0-9]+)?$').test(str)) {
return false
}
return true
},
watchPosition () {
if (![this.top, this.bottom, this.left, this.right].find(i => i !== undefined)) {
this.scrollToTopStyle.bottom = this.scrollToTopStyle.right = '14px'
}
},
watchTop () {
if (this.top !== undefined) {
this.scrollToTopStyle.top = this.isNumber(this.top) ? parseFloat(this.top) + 'px' : this.top
}
},
watchBottom () {
if (this.bottom !== undefined) {
this.scrollToTopStyle.bottom = this.isNumber(this.bottom) ? parseFloat(this.bottom) + 'px' : this.bottom
}
},
watchLeft () {
if (this.left !== undefined) {
this.scrollToTopStyle.left = this.isNumber(this.left) ? parseFloat(this.left) + 'px' : this.left
}
},
watchRigth () {
if (this.right !== undefined) {
this.scrollToTopStyle.right = this.isNumber(this.right) ? parseFloat(this.right) + 'px' : this.right
}
},
/**
* 初始化按钮的位置
*/
initBtnPosition () {
this.watchTop()
this.watchBottom()
this.watchLeft()
this.watchRigth()
this.watchPosition()
},
initBindScroll () {
// 监听窗口滚动
document.onscroll = ((oldScrollTopLength) => {
const clientHeight = document.documentElement.clientHeight
return () => {
const scrollTopLength = document.documentElement.scrollTop || document.body.scrollTop
// 如果定时器不存在的话就正常计算滚动到顶部的图标是否存在
if (!this.timer) {
// 滚动到第二屏就显示
this.showScrollToTop = scrollTopLength > clientHeight
}
// 向下滚动时判断判断是否正在向上滚动,是的话就清除定时器,停在当前位置
if (scrollTopLength > oldScrollTopLength && this.timer) {
// 设置这个是因为有时候 clearInterval() 并不能清除这个属性,或许是 vuejs 组件中的属性特殊一点?
this.timer = clearInterval(this.timer)
}
oldScrollTopLength = scrollTopLength
}
})(0)
},
/**
* 回到顶部
*/
scrollToTop () {
this.timer = setInterval(() => {
const scrollTopLength = document.documentElement.scrollTop || document.body.scrollTop
if (scrollTopLength <= 0) {
this.timer = clearInterval(this.timer)
this.showScrollToTop = false
}
const spend = scrollTopLength / 5
document.documentElement.scrollTop = document.body.scrollTop = scrollTopLength - spend
}, 30)
}
},
mounted () {
this.initBtnPosition()
this.initBindScroll()
}
}
</script>
<style scoped>
#vx-scroll-to-top-btn {
position: fixed;
}
</style>

使用起来就很简单了

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
<template>
<div>
<h2 v-for="i in 100"
:key="i">
第 {{i}} 行文字
</h2>
<vx-scroll-to-top>
<!-- 这里面的内容随你定义,是上拉按钮要显示的样子 -->
<v-btn color="primary"
fab>
<v-icon>vertical_align_top</v-icon>
</v-btn>
</vx-scroll-to-top>
</div>
</template>

<script>
import VxScrollToTop from '@/components/common/VxScrollToTop'

export default {
components: {
VxScrollToTop
}
}
</script>

目前这里只能供了最简单的功能,如果有特别的需求可以在上面继续修改一下就好啦

注意点

  • 组件方法内部必须使用 箭头函数,因为使用 function 会导致 this 发生变化。详情参考 MDN
  • 必须要手动置空 timer,例如上文所用的 this.timer = clearInterval(this.timer),关于为什么 timer 没有被 clearInterval 清空目前吾辈还真不太清楚,但如果把 timer 放到组件外部就正常使用,估计是 vuejs 的属性有什么特殊的地方也说不定!

Windows 为当前用户添加开机自启项

场景

不知道你是否遇到过,每次打开电脑,开机之后总是要启动 Chrome 浏览器等一些常用应用,而常用应用中有的有开机自启的选项,有些则没有。每次打开都是重复性的操作,真是不厌其烦。。。

解决方案

下面是 Windows 当前用户的自启动目录的路径

1
C:\Users\{你的用户名}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

我们可以为想要自启动的程序创建一个快捷方式,然后将快捷方式放到这个目录下就可以啦

然而,直接放快捷方式太多的话管理起来可能会是个麻烦,所以我们可以使用脚本,下面给出两种脚本示例

  • ahk 脚本

    1
    2
    ; 开机自启 Chrome 浏览器
    Run Chrome
  • cmd 脚本

    1
    2
    rem 开机自启 Chrome 浏览器
    start chrome

那么,关于在 Windows 下添加自启项到这里边结束啦,很简单的吧!

Vue 自定义标签的 src 属性不能使用相对路径

场景

吾辈在使用 Vuetify 时突然遇到的,明明 img 标签就可以使用相对路径获取到图片,而 Veutify 的组件 v-img 却不能使用。

如下面 3 种加载图片的方式

1
2
3
4
5
6
<!-- 正常加载 -->
<v-img :src="require('../../assets/logo.png')" />
<!-- 无法加载 -->
<v-img src="../../assets/logo.png" />
<!-- 正常加载 -->
<img src="../../assets/logo.png" />

吾辈在 segmentfault 上的提问

原因

是的,居然必须用 require() 引入图片才能生效,那为什么 img 标签可以直接使用相对路径呢?这和 vue-loader 资源路径处理 有关系。

官方资源路径处理

官方明确指出会将所有资源路径作为模块依赖,也就是后台 vue-loader 帮我们转换成 require() 的形式了。

解决方案

vue cli 3

vue cli 3 的配置项 API 发生了改变,由 transformToRequire 改为 transformAssetUrls,而且配置方式也不再是直接修改 webpack 配置文件,而是修改 vue.config.js 这个经过包装后的文件。现在,最新的配置方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
return {
...options,
//修复静态资源引用的问题 vue cli 2 => vue cli 3 升级之后配置项由 transformToRequire 改为 transformAssetUrls
transformAssetUrls: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href',
// 在这里添加需要使用静态资源的自定义元素
'a-avatar': 'src',
},
}
})
},
}

具体参考
Vue Loader => 从 v14 迁移 => 废弃的选项
Vue Cli 3 => webpack 相关 => 链式操作 (高级) => 修改 Loader 选项

vue cli 2

那么,Veutify 组件中的 src 不能使用相对路径的原因就很明确了。因为 vue-loader 并不知道我们要把 v-imgsrc 属性转换成 require() 依赖。我们找到 vue-loader 配置处,在 options.transformToRequire 中加上 v-img 即可

1
2
// vuetify 框架的 src 标签也需要自动转换为 require
'v-img': 'src'

吾辈的配置文件在 build > vue-loader.conf.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap

module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction,
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href',
'v-img': 'src',
},
}

vue-loader 官方文档参考

然后重启 npm run dev 刷新一下就行啦

工具类网站

编程

netlify:静态网站托管平台

可以简单方便的托管自己的静态网站,能够连接到主流的代码托管平台(GitHub, GitLab, Bitbucket),支持自动在线部署网站,可以免费同时部署多个网站(同一域名下)。相比于 GitHub Page 而言有着更加强大的功能

下面是 GitHub 和 Netlify 的对比

对比 GitHub Netlify
价格 免费 免费
构建限制 每小时 10 次 每分钟 3 次
使用 HTTPS 的自定义域
单击 回滚 没有
资产优化 没有
表格处理 没有
部署预览 没有
持续部署 没有
自定义重写和重定向 没有
兼容所有静态站点生成器 没有
预呈现 没有
拆分测试 没有
Lambda 函数集成 没有

cloudflare:加速并保护网站

配合上面的 netlify 一起使用,对于吾辈而言主要用到了下面的功能:

  • 快速刷新域名 DNS 缓存(几分钟内在网络上完成更新)
  • 为网站加上 HTTPS 前缀
  • 自动将 HTTP 请求转发到 HTTPS
  • 使用 CDN 加速网站

heroku:云端程序托管平台

上面说过的 netlify 可以托管静态网站,但 heroku 能托管一切!不仅仅是前端,Node.js, Ruby, Python, Java, PHP, Go, Scala, Clojure 都不在话下。那么,为什么吾辈没有第一个就推荐它呢?因为 heroku 免费帐户的限制比较大。。。

  • 未认证信用卡的账户每个月只有 500 个小时可用时间
  • 未认证信用卡的账户无法自定义域名
  • 虽然能够托管多语言的程序,然而并非指程序依赖的服务也存在,例如 MySQL,Redis 等等

但即便有上面的这些缺点,对于开源的后端程序用于展示的话还是相当不错的。而且,我们可以利用它免费搭建 Shadowsocks 服务,而且速度也不算慢!

draw:画图工具

开源免费的 Web 版的在线画图工具,方便而快捷,支持多种格式导出/保存/导入。即时保存,即使浏览器崩溃也没有问题。

功能列表

  • 基础功能:支持任意拖动图形,自带对其/微调,可以进行样式编辑,在任意处插入文本
  • 便签:将一个或多个可复用的内容保存为便签,下次直接拖动出来就好了
  • 第三方保存:Google Drive, OneDrive, Dropbox, GitHub, Trello, 电脑或手机设备, 浏览器
  • 多格式导出:PNG, JPEG, SVG, PDF, VSDX, HTML, XML, URL 地址

Serveo:内网穿透

用于在微信开发时,微信公众号需要公网 ip + 80 端口的问题,可以实现远程访问到本地部署的项目。

namesilo:域名服务

如果你需要建立个人网站,或者搭建个人博客的话。那域名几乎是必不可少的。毕竟,谁也不想记住一个 IP 地址,而有一个更有标识性的域名岂非更好呢?

注:之所以不使用国内的 万网 之类的是因为国内的域名有可能被封/被屏蔽,原因很多请参考 警告——不要在国内注册和使用 CN 域名,本质上在国内注册的域名所有权不属于你!

coursera:在线教育

国外非常流行的在线教育网站,有着非常多的在线课程可以学习,而且学完之后还有证书可拿(免费结业证书/付费认证证书)。

附:大部分课程都是英语,当初没学好英语真是一生的痛呀!

flaticon:矢量图标库

免费的在线图标库,目前大约有 1494000 个图标了。只所以不推荐阿里的矢量图标库 iconfont 是因为。。。嗯,你懂的,国人总喜欢 抄袭 而非 原创(某企鹅以此成家),所以设计/创新相关的还是使用国外的更好一点呢!

非编程

Google Translate:谷歌翻译

Google 出品的翻译网站,支持的语言非常多,对于中文/英文的支持也很不错呢(信仰)

inoreader:RSS 阅读器

全平台的 RSS 阅读器,与之相似的只有 feedly 了吧。用来订阅/管理 RSS 源,阅读起来也很方便,基本上免费的套餐便是足够个人使用了。

itellyou:微软相关资源下载

方便下载各种微软的相关资源,提供了直接的下载链接(_ed2k_),例如 Windows 系统_,_VS IDE_,.NET Framework_。

Gmail:Google 家的邮箱服务

Google 出品,必属精品(#大雾)目前而言最好的邮箱。Web 版本提供的功能与体验是其他国内邮箱根本无法相提并论的(QQ/163 之流),配合 Chrome 插件Gmail For Android 风味更佳。

附:Gmail Web 版必须使用梯子,但服务本身并未受到影响,与其他邮箱互通邮件一切正常。

smms:免费稳定的图床

一个简单好用的图床,不需要注册/登录,直接就可以上传图片获取外链了,提供 API 可供开发/嵌入到第三方客户端。

Firefox send:免费安全的文件共享

说到 Mozilla 泥萌可能很多人不太清楚,但 Firefox 却是大名鼎鼎,而 Mozilla 正是 Firefox 的母公司!

Mozilla 家推出的所有现代浏览器都可以使用的文件共享服务。
主要特性

  • 最大支持 1G
  • 24 小时后自动删除
  • 加密的共享链接
  • 支持限制下载次数
  • 支持手动失效

VSCode 自定义模板代码片段

场景

VSCode 时常需要输入一些重复的代码片段,例如创建 vue 文件后总是要写一些类似于下面这样的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>

</template>

<script>
export default {
data: () => ({

}),
methods: {

},
mounted () {

}
}
</script>

能不能有什么办法使用快捷键,或者提示将之快捷打出来呢?VSCode 已经想到了这些并提供了一个名为 Snippet 的功能。

使用

打开命令面板(_查看 > 命令面板_,快捷键是 _CS-P_),输入 Snippet 找到 Preferences: Configure User Snippets(配置用户代码片段),选择你所需要配置的代码片段生效的语言。

这里以 Vue 为例,回车之后会打开一个名为 vue.json 的文件,初始配置大致如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
// Place your snippets for vue here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// "Print to console": {
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
}

上面那些注释其实就给出了一个配置的例子。放开注释我们研究一些每一行的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
// Place your snippets for vue here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// 代码片段的名字
"Print to console": {
"prefix": "log", // 前缀,在 vue 文件中输入这个前缀就会有代码提示
// 输入前缀回车之后在文件中生成的内容,单行直接用字符串,否则使用数组
"body": [
//下面是每一行的内容,$1 指的是第一个变量(光标会首先停在这里)
"console.log('$1');",
// $2 是第二个变量,在输入完第一个变量后按 Tab 就能跳转到第二个变量所在的位置
"$2"
],
// 关于这个代码片段的描述
"description": "Log output to console"
}
}

好了,我们也来模仿写一个自己代码片段吧

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
{
// 在最下方追加
"vue teamplte": {
"prefix": "vueTemplate",
"body": [
"<template>",
" $1",
"</template>",
"",
"<script>",
"export default {",
" data: () => ({",
"",
" }),",
" methods: {",
"",
" },",
" mounted () {",
"",
" }",
"}",
"</script>"
],
"description": "vue 的模板代码"
}
}

然后,在 .vue 文件中输入 vueTemplate 试试吧

更多的详细信息(英文)可以参考 Microsoft 官方的 VSCode 创建代码片段

Vue 打包的静态文件不能直接运行

问题

吾辈使用 vue-cli 直接生成的 vue 模板项目,在模板之上继续开发的。然而在使用 npm run build 打包项目时,却发现打包好的项目在浏览器中直接打开好像什么都没有?

原因

查看了一下打包后的 index.html 源码,终于发现了一个重要的点:

vue-cli 打包的项目 index.html 源码

所有涉及到路径的地方全都是以 / 开头的

下面是吾辈打包后生成的 dist 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dist:.
│ index.html

└─static
├─css
│ app.b7bce283257fbd427fb1dc3fea80cee1.css
│ app.b7bce283257fbd427fb1dc3fea80cee1.css.map

├─fonts
│ MaterialIcons-Regular.012cf6a.woff
│ MaterialIcons-Regular.570eb83.woff2
│ MaterialIcons-Regular.a37b0c0.ttf
│ MaterialIcons-Regular.e79bfd8.eot

└─js
app.58cce746b2fe4ac2f2b9.js
app.58cce746b2fe4ac2f2b9.js.map
manifest.2ae2e69a05c33dfc65f8.js
manifest.2ae2e69a05c33dfc65f8.js.map
vendor.a32972498ed8de656202.js
vendor.a32972498ed8de656202.js.map

这下很清楚了,vue-cli 生成的模板项目打包后的文件默认需要放到静态资源服务器上,而且还必须是根目录!这很不好,很糟糕,所以需要修改配置。

解决方案

  1. 修改文件 _/config/index.js_,将 build.assetsPublicPath 属性的值由 / 改为 ./
    /config/index.js
  2. 修改文件 _/build/utils.js_,在插件 ExtractTextPlugin 中添加 publicPath: '../../'
    /build/utils.js

那么,使用 npm run build 重新打包后的静态文件应该就可以直接打开啦

VSCode 集成 ESLint 和 Prettier

情景

ESLint 是一个前端代码质量检测工具,然而配置非常非常非常的麻烦(前端的配置真的是超多呢),所以在吾辈的 VueJS 项目中直接使用了 standard 规则。然而问题在于有了代码规则,还需要自动的格式化,否则为了手动格式化是一件极其痛苦的事情。。。

步骤

VSCode 安装下面三个插件

然后在 User Settings,添加以下配置

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
// 配置 ESlint 和 Prettier
"eslint.autoFixOnSave": true, // 每次保存的时候将代码按 eslint 格式进行修复
"prettier.eslintIntegration": true, // 让 prettier 使用 eslint 的代码格式进行校验
"prettier.semi": false, // 去掉代码结尾的分号
"prettier.singleQuote": true, // 使用单引号替代双引号
"javascript.format.insertSpaceBeforeFunctionParenthesis": true, // 让函数 (名) 和后面的括号之间加个空格
"vetur.format.defaultFormatter.html": "js-beautify-html", // 格式化. vue 中 html
"vetur.format.defaultFormatter.js": "vscode-typescript", // 让 vue 中的 js 按编辑器自带的 ts 格式进行格式化
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
"wrap_attributes": "force-aligned" // 属性强制折行对齐
}
},
"eslint.validate": [
// 开启对. vue 文件中错误的检查
"javascript",
"javascriptreact",
{
"language": "html",
"autoFix": true
},
{
"language": "vue",
"autoFix": true
}
]

好了,现在可以正常使用 VSCode 的格式化功能了,保存时也会进行格式化同时修复 ESLint 发现的错误。