前言
今天把整个博客都整理了一下主要是把 webpack3 升级到了 webpack5,可能是年纪大了,一些花里胡哨的东西都不太喜欢了。就把页面上以前一些花里胡哨的东西都移除了,留下了一个干干净净的,纯粹的文章页面。
在升级的过程中,发现了 webpack5 和 webpack3 & webpack4 比较大的一个差异点 —— 资源处理。
more >>懒惰的后端程序员,不务正业学习前端中
今天把整个博客都整理了一下主要是把 webpack3 升级到了 webpack5,可能是年纪大了,一些花里胡哨的东西都不太喜欢了。就把页面上以前一些花里胡哨的东西都移除了,留下了一个干干净净的,纯粹的文章页面。
在升级的过程中,发现了 webpack5 和 webpack3 & webpack4 比较大的一个差异点 —— 资源处理。
more >>说起表单验证,这可是一个经久不衰的话题了,不论是新型的 low code 方案还是老旧的手写方式。
表单验证已经在过去长久的实践里面获得通用的认识,所以大体上,绝大多数的表单验证,在观感上、体验上基本都是一致的。
通常而言,我们常见的都可以发现大致有这么几种表单验证方式:
而我们目前使用的 ant-design-vue(1.7.8) 的表单验证器,至少在现阶段说不上好用,勉强算是灵活有效,适应面广泛。
而 ant-design-vue 的表单验证基本是基于 async-validator 的这带来的比较大的问题就是写起来其实蛮繁琐的。
随手举个官网的例子:
ruleForm: {
pass: '',
checkPass: '',
age: '',
},
rules: {
pass: [{ validator: validatePass, trigger: 'change' }],
checkPass: [{ validator: validatePass2, trigger: 'change' }],
age: [{ validator: checkAge, trigger: 'change' }],
},
最大的特点就是 rules 的声明与 form 分离,且极其不直观。
当然这也是 js 配置方式的一贯特点,很灵活,没有什么太大的劣势。
那么对于我来说,我觉得代码这个东西,主要还是人阅读的,从这个基本点出发,我还是希望稍稍的改善一下阅读体验,但是呢,并不想太过改动原本的验证逻辑。
那么实际上用 class 方式的验证就可以做到。
more >>最早用 ubuntu 的时候,那个时候还是 atom & vscode 针锋相对相对的时候,基于两者生态还没起来的原因,不免使用上有所不便,索性也就都不用了,直接把目光投向了最老牌的两个编辑器 —— emacs & vim。
出于对 编辑器 没有什么太高要求,故而最终用上的是 vim。
但是想把 vim 调教成一个合适的,功能齐全的编辑器并不方便,核心在于 vim 本身受制于 vimscript 功能局限,加之 vim 在本身是同步编程时代的产物,一旦在成规模的应用上,使用 vim 来作为主力 ide 是一件不怎么好用的事情。
more >>2021 年即将结束了,那么是时候总结一下今年我做了哪些工作和事情了。
此前 20 年的时候,我还在 xd 继续做着页面特效,后来工作发生了调整,去了 TapTap ,在 TapTap 这边做回了 2B 业务,算是重回了“安逸圈”(毕竟对兼容性的要求没这么高了)
一开始负责的是开发者中心的任务。总体来讲,由于时间的问题,开发者中心项目上其实没有非常好的规划,不过从整个技术架构上,已经开始使用比较先进的理念和架构。
开发者中心主要用的还是 vue2 。当然这是由于开工的时候, vue3 尚未 release 。显然 vue3 在当时那个阶段 是不适合直接使用的。
为此我们又想再使用 vue2 的基础上去使用 vue3 composition 的复用能力,这最好的方案就是直接用 @vue/composition-api
,它带来了巨大的提升,事实上我们的许多基础建设都依赖于 composition api 提供的巨大支持。
从实践的结果来看,开发者中心总体上结果很不错,里面许多的基础建设也逐渐的演变为通用的前端基础设施。
后来主要去负责了星云平台,由于星云主要是一个对内的平台,基于此,这里许多相对激进的方案得以施展。
more >>随着上班越来越忙,单独跑 blog 过来写 md 的时间其实是越来越短的,而且我的 cms 管理,也从原来的印象笔记切换成了 notion,是事实上 notion 本身就有一个比较好的展示页面:
除了速度比较慢一点以外,没有什么太大的缺点,所以后面的文章就全部挂到 notion 上了。
当然不排除以后,我用 react-notion-x 搭建一个基于 notion cms 管理的页面,当然这是后话了。
来自 2022年09月04日的更新
由于 notion 在国内访问速度实在太慢了,所以最终还是继续发布在这里了,并且 目前主要使用 Obsidian 代替作为管理器了。
随着浏览器的功能逐渐增强,我们不可避免的需要在前端处理越来越多的数据,除了数据,我们甚至可能需要对 cache 作出更加丰富的操作。
那么问题来了,用什么工具更加合适呢?
cookie 是我们常见数据持久化方案,但是他的大小仅仅只有 4k,并且随着 cookie 的增大,你的请求携带的cookie 也随之增加,这显然是不划算的。
localstorage 是一个不错的方案,他的大小也是勉强够用的,约 2.5M 随着各个浏览器大小的不同而发生改变,比如 chrome[1] 他的大小就有 10M
当然咯有一些成熟的库对 localstorage 作出了一定程度上的封装来减少存取的难度,但是实际上,它在某些痛点上并不能很好的解决这些问题。(这会在后文指出)
所以 IndexDB 应运而生,用于处理这些复杂而麻烦的任务。
IndexDB 顾名思义,是一个 db。
各位前端的小伙伴相比都是用 node 的,对于 node 我们最喜欢也最常用的是 mongodb。
IndexDB 和 mongo 类似 是一个 nosql 数据库,并且非常像 redis,是一个 键值数据库,所以这货所存储的聚合不一定非要是领域对象,任何数据结构都可以,比如list、set、hash
等数据结构,而且支持某些高级的集合操作,比如求 交集/并集/差集 (当然用起来不是一般的麻烦,我觉得 LINQ 才是最好的数据处理方式)
此文承接上一篇文章,主要把后续传输图片的部分介绍一下。
其实传输图片除了直接注入 js 还有一个方案是通过 拦截 url 实现。
但是这既不灵活也不优雅,具体怎么实现在此就不多赘述了。
惯例,我不太喜欢写 java 代码,还是继续 kt 把,反正语法就一丢丢差别
由于 安卓 q 的权限变更,如果你是老老实实使用正常的权限的话,理论上不需要额外申请相册的写入权限,此外相册的写入权限只是为了方便图片是否写入成功,写 cache 目录是同理的。
首先先非常简单的把写相册功能实现一下:
// 这里不使用复杂的方案了,直接 replace ,理论上是需要通过正则提取的
val buffer = Base64.decode(base64.replace("data:image/png;base64", ""), Base64.DEFAULT)
val bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.size)
// 构建 query
val contentValue = ContentValues().apply {
// 这里可以改名字的,我就用时间代替了
put(MediaStore.MediaColumns.DISPLAY_NAME, System.currentTimeMillis().toString())
put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
}
val resolver = this.contentResolver
val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
// 这两是为了出错的时候会正常关闭流
var stream: OutputStream? = null
var uri: Uri? = null
try {
uri = resolver.insert(contentUri, contentValue)
?: throw IOException("fail to create record")
stream = resolver.openOutputStream(uri) ?: throw IOException("fail to get output stream");
if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)) {
throw IOException("fail to save bitmap image");
}
} catch (e: IOException) {
// 如果 uri 生成了记得一定要删除
if (uri != null) {
resolver.delete(uri, null, null);
}
throw e;
} finally {
// 如果 stream 存在一定要记得关闭流
stream?.close()
}
然后没什么难度的传递参数:
this@MainActivity.saveFileIntoStorage(base64)
val alertDialog = AlertDialog.Builder(this@MainActivity)
alertDialog.setTitle("系统提示")
.setMessage("js传图成功")
.create()
alertDialog.show()
我们把代码整体修改一下,如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="btn">click</button>
<script>
function download(url) {
// 这里用了 fetch api 其他的可能需要做一个转换
// 也很简单通过 xhr 获取后 设置 xhr.responseType = "blob" 即可
// content 直接是 blob 的
// 如果实在不行可以用 canvas toBlob 解决,都不难
return fetch(url).then(function (response) {
return response.blob();
});
}
document.getElementById("btn").addEventListener("click", () => {
let url = "./test.png";
download(url)
.then(
(blob) =>
new Promise( (resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
// 转换成 base64
reader.readAsDataURL(blob);
})
)
.then( (base64) => {
window.webviewTest.onJsCallback(base64);
});
});
</script>
</body>
</html>
然后看一下效果, 也 ok ,没啥问题。
我记得之前听我一个做安卓的同学说 安卓 那边是可以直接用 blob 的,但是我找了一圈,委实并未找到相关的 api ,即使相似的也只有 sql.blob 这东西感觉也不像,那还是老老实实用 base 64折腾把。
相比较 拦截 url ,这里传输的图片要大很多。
实测是 大约1.5M以下的图片通过地址栏传输是没啥问题的,而函数传输在 10M 以内实测是没有什么问题。
至于再大的图片,您就老老实实挂一个 http 本地服务,用 tcp 上传得了(大家都省事儿),要知道前端这个速度转换一个 10M 的图片少说4秒起步,在走各种管线,跑安卓在转换 走 fs,着实慢得无法接受。
在虚拟机上 10M 的图片需要花费月 8 秒的时间来传输,如果使用 jpg 压缩后(85%质量)直接走 http可比这快多了。
最近我同事接了一个项目,就是需要传一个图片给游戏的 webview,考虑到服务端传输又没有账号登陆的原因,所以还是本地的好(喂喂喂!本来就该本地传输啊!!)
所以这里写了一个简单的 demo 来完成上述代码。
more >>此处空空如也~
输入点什么来搜索吧!