Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 442a5a9c73 | |||
| 8e364129f1 | |||
| e11b1a1f35 | |||
| e3681f2f77 | |||
| b29f8465da | |||
|
|
b46ea82104 | ||
| 643ee785d9 | |||
| e2a739fd0b | |||
|
|
1d990a3df5 | ||
| 60a02d6878 | |||
|
|
13af7b9bc2 | ||
| 87dc3221cf | |||
| e4190e2e91 | |||
| 062f292296 | |||
|
|
cec30cc040 | ||
| 3e2116803c | |||
| 7e27b2320c | |||
|
|
1c3ad4d69e | ||
|
|
3f0a3c2fcc | ||
| 66950e5aad | |||
| 26d89e7ed1 | |||
| f5d84965b6 | |||
| 81fc1c2c40 | |||
| 5ae33946d2 | |||
| b3b2d16ca5 | |||
| 3f43208997 | |||
| b48ba9dd97 | |||
| 780298648a | |||
| 17dcfc223a | |||
| b9751f6d6f | |||
| ad97d0ee80 | |||
| cd3b2634a4 | |||
| f4bd1a9bd4 | |||
| 05a3b66599 | |||
| 185a812d92 | |||
| 57cc1f64f5 | |||
| 41e7f1ea7c | |||
| 8f2afff210 | |||
| 29a1c15418 | |||
| bf6fb37dc5 | |||
| 6ddd284d96 | |||
|
|
59024a87f4 | ||
|
|
70627d22ee | ||
|
|
0866b382dc | ||
|
|
b40367c6f0 | ||
| 802bec24b5 | |||
|
|
405be8b671 | ||
| 4d01aa2f10 | |||
| d75c41ebae | |||
| 30904edfa1 | |||
| 913f9f6c62 | |||
| ab7b714461 | |||
| 061fde4eaf | |||
| 367b76c246 | |||
| 41e0e8317c | |||
| d94afaea47 | |||
| 4a0528c9b6 | |||
|
|
be7dc86d7e | ||
| 4e2f2d2484 | |||
| 1fc759392d | |||
|
|
668d2a7f01 | ||
|
|
96ce935dcd | ||
|
|
64757b48a0 | ||
| 425097a087 | |||
|
|
7750d81338 | ||
| c96a169b7c | |||
| 6e53faffc5 | |||
| 036268bb71 | |||
|
|
720e70defb | ||
| bbd55797e6 | |||
| 5b59ae3a93 | |||
| 74d577b82c | |||
| 0f0342f9cd | |||
| f084a92287 | |||
|
|
aae82af0c8 | ||
| 4e8d9615f0 | |||
|
|
9375705c6e | ||
|
|
09a0eddf03 | ||
|
|
344a8695b3 | ||
| 08730fc8f0 | |||
| 5da9aa3f72 | |||
|
|
e7656d7ddf | ||
| 37a743cd01 | |||
| 8db5f8207a | |||
|
|
878f01eb81 | ||
|
|
9cf7cb5811 | ||
|
|
c969c343a7 | ||
|
|
3c646d498d | ||
| 37d1c03678 | |||
| 7c16433182 | |||
| 643cd5bb8f | |||
|
|
df8fd44f72 | ||
| 921c90d09b | |||
| eb7978b586 | |||
| f30a81349a | |||
| 7737bbad7d | |||
| 1f2482bf4f | |||
|
|
5910ee3733 | ||
|
|
e94887687a | ||
|
|
306be25e48 | ||
|
|
da6a1e78ec | ||
|
|
887b74dbcf | ||
|
|
79bdef1e9d | ||
|
|
a1e7c3dc07 | ||
|
|
2f7d5a2c3c | ||
|
|
2272a37add | ||
|
|
415dc71da7 | ||
|
|
a41c1173a7 | ||
|
|
d3205166c2 | ||
|
|
ba3d655ccc | ||
|
|
7627681b1e | ||
|
|
6e0600c30c | ||
|
|
4a81506aec | ||
|
|
e90194314c | ||
|
|
065883b1cb | ||
|
|
32acd76e27 | ||
|
|
3bfac6eede | ||
|
|
89258be2db | ||
|
|
8665aa0ad4 | ||
|
|
deaa767902 | ||
|
|
dedd49116e | ||
|
|
74b5b07498 | ||
|
|
c448abc367 | ||
|
|
a8f51e2e00 | ||
|
|
5601c79dff | ||
|
|
476ae437b0 | ||
|
|
bb08f3d7c3 | ||
|
|
b273d66640 | ||
|
|
605d26782e | ||
|
|
b2ee25bdc7 | ||
|
|
d42e02e75d | ||
|
|
eb5625c3ae | ||
|
|
66a69b1100 | ||
|
|
62eb074a57 | ||
|
|
7c165816f4 | ||
|
|
48e5da6876 | ||
|
|
255a693ea6 | ||
|
|
638d19096a | ||
|
|
88cdf96334 | ||
|
|
34c8be0035 | ||
|
|
5394ba5657 | ||
|
|
95b2d33639 | ||
|
|
2d4a67120d | ||
|
|
ffdd1653d6 | ||
|
|
9766814e87 | ||
|
|
7930d0bb91 | ||
|
|
9d04847325 | ||
|
|
22aa1b322e | ||
|
|
3439b8d34b | ||
|
|
699e807bf1 | ||
|
|
0e94c2d863 | ||
|
|
0917a4c8ed | ||
|
|
78039c46d5 | ||
|
|
4d4d47cb09 | ||
|
|
0ef8644966 | ||
|
|
6689e4d5c1 | ||
|
|
d230b5a85d | ||
|
|
1c59f4fd29 | ||
|
|
da0530d37a | ||
|
|
2b635d6783 | ||
|
|
c287a3e94e | ||
|
|
40d2b564a3 | ||
|
|
7a08585b17 | ||
|
|
402ab0d495 | ||
|
|
d2987e3a14 | ||
|
|
dd9897df49 | ||
|
|
5a61993958 | ||
|
|
8f96698853 | ||
|
|
a8c44a4709 | ||
|
|
7eaf32df0a | ||
|
|
67430e3c19 | ||
|
|
44e79f3b55 | ||
|
|
4f5f3c2b2d | ||
|
|
46d2216d53 | ||
|
|
d92492c108 | ||
|
|
9c85d05c50 | ||
|
|
8b1e6645ee | ||
| 4356cce797 | |||
|
|
66c300abd8 | ||
|
|
6b782b5f73 | ||
|
|
ffc1454d04 | ||
|
|
91f0315240 | ||
|
|
c30aed06c7 | ||
| 46f5556257 | |||
|
|
94747c1072 | ||
|
|
195d807808 |
@ -3,8 +3,8 @@
|
||||
</p>
|
||||
|
||||
<p style="text-align:center">
|
||||
<img src="https://img.shields.io/github/repo-size/sookie2010/hexo_blog.svg" alt="Size" />
|
||||
<img src=https://img.shields.io/github/package-json/v/sookie2010/hexo_blog.svg alt="Version" />
|
||||
<img src="https://img.shields.io/github/repo-size/colorfulsweet/blog-web.svg" alt="Size" />
|
||||
<img src="https://img.shields.io/github/package-json/v/colorfulsweet/blog-web.svg" alt="Version" />
|
||||
<a target="_blank" href="https://www.colorfulsweet.site">
|
||||
<img src="https://img.shields.io/badge/blog-colorfulsweet-green.svg" alt="我的博客" />
|
||||
</a>
|
||||
|
||||
12
_config.yml
@ -36,7 +36,8 @@ skip_render:
|
||||
new_post_name: :title.md # File name of new posts
|
||||
default_layout: post
|
||||
titlecase: false # Transform title into titlecase
|
||||
external_link: true # Open external links in new tab
|
||||
external_link:
|
||||
enable: true # Open external links in new tab
|
||||
filename_case: 0
|
||||
render_drafts: false
|
||||
post_asset_folder: false
|
||||
@ -93,12 +94,15 @@ pagination_dir: page
|
||||
theme: yilia
|
||||
|
||||
# 图片存储仓库地址
|
||||
picture_cdn: https://blog-cdn.nos-eastchina1.126.net
|
||||
picture_cdn: https://nas.colorfulsweet.site:9003
|
||||
|
||||
# ICP备案号
|
||||
ICP: 鲁ICP备19028444号
|
||||
|
||||
# 站点地图
|
||||
sitemap:
|
||||
path: sitemap.xml
|
||||
template: ./sitemap_template.xml
|
||||
template: ./templates/sitemap.xml
|
||||
baidusitemap:
|
||||
path: baidusitemap.xml
|
||||
|
||||
@ -121,4 +125,4 @@ jsonContent:
|
||||
permalink: false
|
||||
excerpt: false
|
||||
categories: true
|
||||
tags: true
|
||||
tags: true
|
||||
|
||||
@ -1,22 +1,25 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
class Deploy {
|
||||
/**
|
||||
* 发布静态化的站点
|
||||
* @param {String} source 源位置
|
||||
* @param {String} target 目标位置
|
||||
* @param {Boolean} copyRoot 是否拷贝根目录
|
||||
* @param {Boolean} isRemove 是否先进行删除
|
||||
*/
|
||||
async exec(source, target, copyRoot) {
|
||||
await new Promise((resolve, reject) => {
|
||||
console.log(`删除${target}目录中的文件`)
|
||||
this._deleteFolderRecursive(target, true)
|
||||
resolve()
|
||||
})
|
||||
async exec(source, target, isRemove = false) {
|
||||
if(isRemove) {
|
||||
await new Promise((resolve, reject) => {
|
||||
console.log(`删除${target}目录中的文件`)
|
||||
this._deleteFolderRecursive(target, true)
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
console.log(`拷贝${source}所有文件 -> ${target}`)
|
||||
this._copyFolderRecursive(source, target, copyRoot)
|
||||
},
|
||||
this._checkDirectory(target)
|
||||
this._copyFolderRecursive(source, target)
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归删除目录以及子目录中的所有文件
|
||||
@ -35,7 +38,7 @@ module.exports = {
|
||||
if(!retainRoot) { // 根目录保留
|
||||
fs.rmdirSync(curPath)
|
||||
}
|
||||
},
|
||||
}
|
||||
/**
|
||||
* 递归拷贝目录
|
||||
* @param {String} source 源位置
|
||||
@ -53,24 +56,28 @@ module.exports = {
|
||||
let writable = fs.createWriteStream(_target) //创建写入流
|
||||
readable.pipe(writable);
|
||||
} else if (stats.isDirectory()) { //是目录则 递归
|
||||
this._checkDirectory(_src, _target, this._copyFolderRecursive)
|
||||
this._checkDirectory(_target, this._copyFolderRecursive, _src, _target)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验目标目录是否存在
|
||||
* @param {String} src 源目录
|
||||
* @param {String} target 目标目录
|
||||
* @param {Function} callback 回调函数
|
||||
* @param {Array} args 回调函数入参
|
||||
*/
|
||||
_checkDirectory (src,target,callback) {
|
||||
_checkDirectory (target, callback, ...args) {
|
||||
fs.access(target, fs.constants.F_OK, err => {
|
||||
if (err) {
|
||||
fs.mkdirSync(target)
|
||||
}
|
||||
callback.call(this, src, target)
|
||||
if (typeof callback === 'function') {
|
||||
callback.apply(this, args)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Deploy()
|
||||
@ -1,136 +0,0 @@
|
||||
const fs = require('fs'),
|
||||
path = require('path'),
|
||||
nos = require('@xgheaven/nos-node-sdk')
|
||||
|
||||
class ImageSynchronizer {
|
||||
/**
|
||||
* 构造方法
|
||||
* @param {Object} setting NosClient的设置项
|
||||
* @param {Array} imagesList 本地图片的列表
|
||||
* @param {String} rootPath 本地文件根路径
|
||||
*/
|
||||
constructor(setting, imagesList, rootPath) {
|
||||
// 网易云对象存储调用接口client
|
||||
this.client = new nos.NosClient(setting)
|
||||
this.imagesList = imagesList
|
||||
this.rootPath = rootPath
|
||||
}
|
||||
/**
|
||||
* 执行文件同步
|
||||
* @param {String} prefix 图片目录前缀
|
||||
*/
|
||||
synchronize(prefix) {
|
||||
return this._queryObjects({limit: this.imagesList.length+1, prefix}, function(pendingUploadFiles){
|
||||
this._uploadObject(pendingUploadFiles)
|
||||
}, function(pendingDeleteFiles){
|
||||
this._deleteObjects(pendingDeleteFiles)
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 查询所有对象存储库已存在的文件
|
||||
* @param {Object} params 查询的参数
|
||||
* @param {Function} uploadCallback 处理待上传文件的回调函数
|
||||
* @param {Function} deleteCallback 处理待删除文件的回调函数
|
||||
*/
|
||||
async _queryObjects(params, uploadCallback, deleteCallback) {
|
||||
// 列出所有已存储的对象
|
||||
const ret = await this.client.listObject(params)
|
||||
// ret 包括 items(元素),limit(请求的数量),nextMarker(下一个标记)
|
||||
let storageItems = ret.items.filter(item => {
|
||||
return /^images.+?\.(png|jpe?g|gif)$/.test(item.key)
|
||||
}).sort((item1, item2) => {
|
||||
if (item1.key > item2.key) {
|
||||
return 1
|
||||
}
|
||||
else if (item1.key < item2.key) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
});
|
||||
// 待上传的文件列表
|
||||
let pendingUploadFiles = this.imagesList.filter(item => {
|
||||
let index = this._binarySearch(storageItems, item.name, 'key', 0, storageItems.length - 1)
|
||||
if (index === -1) {
|
||||
// 文件名不存在, 代表是新文件
|
||||
item.type = 'new'
|
||||
return true
|
||||
}
|
||||
else if (storageItems[index].eTag !== item.md5) {
|
||||
// 文件名存在, 但是hash值不同, 代表有变化
|
||||
item.type = 'change'
|
||||
return true
|
||||
}
|
||||
return false
|
||||
});
|
||||
// 处理待上传的文件
|
||||
uploadCallback.call(this, pendingUploadFiles);
|
||||
// 待删除的文件列表( 仓库中存在, 本地不存在 )
|
||||
let pendingDeleteFiles = storageItems.filter(item => {
|
||||
return this._binarySearch(this.imagesList, item.key, 'name', 0, this.imagesList.length - 1) === -1;
|
||||
})
|
||||
// 处理待删除的文件
|
||||
deleteCallback.call(this, pendingDeleteFiles.map(item => item.key))
|
||||
}
|
||||
/**
|
||||
* 上传文件对象
|
||||
* @param {Array} filesList 待上传的文件列表
|
||||
* @param {Number} index 索引值
|
||||
*/
|
||||
_uploadObject(filesList, index=0) {
|
||||
if(index >= filesList.length) return
|
||||
|
||||
this.client.putObject({
|
||||
objectKey: filesList[index].name,
|
||||
body: fs.createReadStream(path.resolve(this.rootPath, filesList[index].name)), // 支持 Buffer/Readable/string
|
||||
}).then(result => {
|
||||
// eTag是上传后远端校验的md5值, 用于和本地进行比对
|
||||
let eTag = result.eTag.replace(/"/g,'')
|
||||
if(filesList[index].md5 === eTag) {
|
||||
console.log(`${filesList[index].name} 上传成功, md5:${eTag} 类型: ${filesList[index].type}`)
|
||||
} else {
|
||||
console.warn(`${filesList[index].name} 上传出错, md5值不一致`)
|
||||
console.warn(`===> 本地文件: ${filesList[index].md5}, 接口返回: ${eTag}`)
|
||||
}
|
||||
this._uploadObject(filesList, ++index)
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 批量删除文件
|
||||
* @param {Array} fileNamesList 文件名数组
|
||||
*/
|
||||
_deleteObjects(fileNamesList) {
|
||||
if(!Array.isArray(fileNamesList) || !fileNamesList.length) return
|
||||
|
||||
this.client.deleteMultiObject({
|
||||
objectKeys: fileNamesList
|
||||
}).then(err => {
|
||||
console.log('===> 文件删除成功')
|
||||
fileNamesList.forEach(item => console.log(item))
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 二分法查找
|
||||
* @param {Array} arr 执行查找的数组
|
||||
* @param {Object} target 要找到的目标元素
|
||||
* @param {String} key 数组元素上的键
|
||||
* @param {Number} start 查找的范围 起点
|
||||
* @param {Number} end 查找的范围 终点
|
||||
*/
|
||||
_binarySearch(arr, target, key, start, end) {
|
||||
if(!Array.isArray(arr) || !arr.length) {
|
||||
return -1
|
||||
}
|
||||
if(start >= end) {
|
||||
return arr[start][key] === target ? start : -1
|
||||
}
|
||||
let index = Math.ceil((start + end)/2)
|
||||
if(arr[index][key] === target) {
|
||||
return index
|
||||
} else if(arr[index][key] > target) {
|
||||
return this._binarySearch(arr, target, key, start, index-1)
|
||||
} else {
|
||||
return this._binarySearch(arr, target, key, index+1, end)
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = ImageSynchronizer
|
||||
@ -1,52 +0,0 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const crypto = require('crypto')
|
||||
|
||||
|
||||
function sortName(item1, item2) {
|
||||
if(item1.name > item2.name) {
|
||||
return 1
|
||||
} else if(item1.name < item2.name) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归遍历目录中的所有文件
|
||||
* @param {String} imageFolderPath 文件夹路径
|
||||
* @param {Array} images 图片列表
|
||||
* @param {String} rootPath 根路径
|
||||
*/
|
||||
function readDirSync(imageFolderPath, images, rootPath, callback, count={fileCount:0, finishCount:0}){
|
||||
var files = fs.readdirSync(imageFolderPath);
|
||||
files.forEach(item => {
|
||||
var fileInfo = fs.statSync(`${imageFolderPath}/${item}`)
|
||||
if(fileInfo.isDirectory()){
|
||||
// 该文件是一个目录, 则遍历该目录内容
|
||||
readDirSync(`${imageFolderPath}/${item}`, images, rootPath, callback, count)
|
||||
} else {
|
||||
count.fileCount ++
|
||||
var stream = fs.createReadStream(`${imageFolderPath}/${item}`)
|
||||
var fsHash = crypto.createHash('md5')
|
||||
|
||||
stream.on('data', data => {
|
||||
fsHash.update(data)
|
||||
})
|
||||
stream.on('end', () => {
|
||||
count.finishCount ++
|
||||
images.push({
|
||||
name: `${imageFolderPath}/${item}`.replace(rootPath, ''),
|
||||
md5: fsHash.digest('hex')
|
||||
})
|
||||
if(count.fileCount === count.finishCount && typeof callback === 'function') {
|
||||
callback(images.sort(sortName))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = function (rootPath, imageFloder, callback) {
|
||||
readDirSync(path.resolve(rootPath, imageFloder), [], rootPath, callback)
|
||||
}
|
||||
48
gulpfile.js
@ -1,14 +1,12 @@
|
||||
const gulp = require('gulp'),
|
||||
htmlmin = require('gulp-htmlmin'), //html压缩组件
|
||||
htmlclean = require('gulp-htmlclean'), //html清理组件
|
||||
plumber = require('gulp-plumber'), //容错组件(发生错误不跳出任务,并报出错误内容)
|
||||
Hexo = require('hexo')
|
||||
htmlmin = require('gulp-htmlmin'), // html压缩组件
|
||||
htmlclean = require('gulp-htmlclean'), // html清理组件
|
||||
plumber = require('gulp-plumber'), // 容错组件(发生错误不跳出任务,并报出错误内容)
|
||||
Hexo = require('hexo'),
|
||||
log = require('fancy-log') // gulp的日志输出
|
||||
|
||||
// 程序执行的传参
|
||||
const argv = require('optimist')
|
||||
// .demand(['accessKey', 'accessSecret', 'deployPath'])
|
||||
.describe('accessKey', '网易云对象存储key')
|
||||
.describe('accessSecret', '网易云对象存储secret')
|
||||
.describe('deployPath', '静态化后发布的目录')
|
||||
.argv
|
||||
|
||||
@ -33,7 +31,8 @@ gulp.task('compressHtml', () => {
|
||||
unprotect: /<script [^>]*\btype="text\/x-handlebars-template"[\s\S]+?<\/script>/ig //特殊处理
|
||||
}
|
||||
const minOption = {
|
||||
collapseWhitespace: true, //压缩HTML
|
||||
collapseWhitespace: true, //删除html中的空白
|
||||
conservativeCollapse: false, //将多个空白折叠为1空白(永远不要完全移除), 必须与 collapseWhitespace=true 一起使用
|
||||
collapseBooleanAttributes: true, //省略布尔属性的值 <input checked="true"/> ==> <input />
|
||||
removeEmptyAttributes: true, //删除所有空属性值 <input id="" /> ==> <input />
|
||||
removeScriptTypeAttributes: true, //删除<script>的type="text/javascript"
|
||||
@ -41,7 +40,7 @@ gulp.task('compressHtml', () => {
|
||||
removeComments: true, //清除HTML注释
|
||||
minifyJS: true, //压缩页面JS
|
||||
minifyCSS: true, //压缩页面CSS
|
||||
minifyURLs: true //替换页面URL
|
||||
minifyURLs: false //替换页面URL
|
||||
}
|
||||
return gulp.src('./public/**/*.html')
|
||||
.pipe(plumber())
|
||||
@ -50,37 +49,22 @@ gulp.task('compressHtml', () => {
|
||||
.pipe(gulp.dest('./public'))
|
||||
})
|
||||
|
||||
// 同步图片到对象存储仓库
|
||||
gulp.task('syncImages', () => {
|
||||
const listImages = require('./deploy_utils/list_images')
|
||||
if(!argv.accessKey || !argv.accessSecret) {
|
||||
return Promise.resolve('未获得accessKey以及accessSecret, 跳过图片同步').then(console.log)
|
||||
}
|
||||
// 同步当前本地存在的所有图片
|
||||
return new Promise(resolve => {
|
||||
listImages(`${process.cwd()}/source/`, 'images/', resolve)
|
||||
}).then(imagesList => {
|
||||
const ImageSynchronizer = require('./deploy_utils/image_synchronize')
|
||||
const nosSetting = {
|
||||
defaultBucket: 'blog-cdn',
|
||||
endpoint: 'http://nos-eastchina1.126.net',
|
||||
accessKey: argv.accessKey,
|
||||
accessSecret: argv.accessSecret
|
||||
}
|
||||
const imageSynchronizer = new ImageSynchronizer(nosSetting, imagesList, `${process.cwd()}/source/`)
|
||||
return imageSynchronizer.synchronize('images/')
|
||||
})
|
||||
// 拷贝图片
|
||||
gulp.task('copyImage', () => {
|
||||
const deploy = require('./deploy_utils/deploy')
|
||||
return deploy.exec('./images', './public/images')
|
||||
})
|
||||
|
||||
// 发布
|
||||
gulp.task('deploy', () => {
|
||||
if(!argv.deployPath) {
|
||||
return Promise.resolve('未获得deployPath, 跳过发布').then(console.log)
|
||||
return Promise.resolve('未获得deployPath, 跳过发布').then(log)
|
||||
}
|
||||
const deploy = require('./deploy_utils/deploy')
|
||||
return deploy.exec('./public', argv.deployPath, false)
|
||||
return deploy.exec('./public', argv.deployPath, true)
|
||||
})
|
||||
|
||||
// 默认任务
|
||||
gulp.task('default',
|
||||
gulp.series('generate', 'compressHtml', 'syncImages', 'deploy') // 串行执行任务
|
||||
gulp.series('generate', 'compressHtml', 'copyImage', 'deploy') // 串行执行任务
|
||||
)
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
BIN
images/JavaScript/ArrayBuffer.jpg
Normal file
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
BIN
images/JavaScript/Promise-all时间.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
BIN
images/JavaScript/await时间.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
images/JavaScript/btoa和atob.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
BIN
images/JavaScript/运行jest.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
images/MongoDB/article_keys集合.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
images/MongoDB/article集合.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
BIN
images/MongoDB/全文检索效果.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
images/MongoDB/全文检索数据量.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
images/flutter/GridView.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
images/flutter/ListView.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
images/flutter/android-studio_flutter插件.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
images/flutter/flutter_demo.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
images/flutter/flutter_doctor.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
images/flutter/创建flutter项目.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
BIN
images/linux/disown.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
BIN
images/linux/io/sort_help.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
images/linux/io/xargs循环执行.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
images/linux/io/默认标准输入.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 163 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |