Normal view

There are new articles available, click to refresh the page.
Today — 8 September 2024Main stream

Package Easy(基于 NSIS 的打包工具)V1.0.6

7 September 2024 at 23:15
tangmanger:

前言

好久没有更新了,最近比较忙,平时使用也积累了一些需求和遇到了一些 bug 。主要分布于新功能的构思与旧功能重构,最近遇到了一个棘手问题,不修改就满足不了使用,所以对部分功能进行重构和修改,决定更新一版,满足后续使用

更新内容

  • 支持用户自定义目标路径,且支持本机保存可以跨项目使用
  • 支持安装到所有用户(解决管理员下安装非管理员下运行问题)
  • 修复了虚拟化问题导致文件列表展示不正确
  • 修复保存校验个别字段不准确
  • 修复工作目录和源目录拷贝文件错误
  • 补充黑色主题部分颜色值
  • 修复切换语言时导致主题列表显示错误

功能说明

  1. 支持用户自定义目标路径,且支持本机保存可以跨项目使用 在日常使用中常常遇到一个问题,比如我要将 A 文件写到 B 目录去,但是 B 目录又不在系统默认的目录列表内,这样就会有一个很尴尬问题,需要手动去改程序增加目录。最近给外国佬做一个程序,他们严格规定目录的写入位置,然后我在打包的时候就发现无法做到这点,新增了自定义目标目录的功能。

目标路径库 1. 这个是软件提供的目标路径管理的一个功能,可以提供目标路径检索删除新增等功能。打开方式:数据->路径管理 注意:当在项目页面打开时,默认编辑为项目的目标路径库,此时添加可以选择同步到本地,这样所有项目都可以使用,如果在首页进行添加操作则是存储到本地。

  1. 在程序集页面和多语言界面可以选择我们自定义路径支持用户选择自定义文件夹,就可以将指定文 件释放到指定文件夹下。 图片
  2. 支持安装到所有用户 现阶段安装用于管理员身份安装,这样对于国内用户没有问题,但是由于国外用户有的有完整的权限校验,比如管理员下安装,各自用户下使用,导致程序无法找到,现在可以支持一键设置安装到所有用户。

图片

  1. 修复了虚拟化问题导致文件列表展示不正确 修复了一个文件列表虚拟化 策略,导致修改后未保存的话,滚动到可视区域外会失效,需要重新设置的 bug 。(现在存在一个多选的问题,但是不影响使用就是体验稍差点)

  2. 修复工作目录和源目录拷贝文件错误

    修复当工作目录和源目录一致的时候,选择文件出现拷贝无权限操作,导致程序异常。

  3. 修复切换语言时导致主题列表显示错误&补充黑色主题部分颜色值 修复切换语言时,导致主题列表刷新不及时,无法切换语言列表。并补充了部分主题颜色值

一点小感悟

最近工作比较忙,各种乱七八糟的事儿,也是用到哪就更新到哪,遇到问题就去解决。怕自己闲下来,又怕自己停下来没饭吃。人入中年,好多事情和时间都不是自己的了,不过我会努力更新完善的让它更易用,让它帮助到更多的人。

github:https://github.com/tangmanger/PackageEasy

release:https://github.com/tangmanger/PackageEasy/releases

Yesterday — 7 September 2024Main stream

在做个 AI 员工的系统,想真正实现 AI 自举从头到尾自己干活加优化自己绩效

7 September 2024 at 17:33
redchamber:

我正在做个 AI 员工的系统,想要真正实现 AI 自举的从头到尾自己干活,自己 review 自己的工作表现,自己优化自己的绩效,这么循环着来。

在 AI 的帮助下,判断了下,准备先从对逻辑严密性要求比较低,并且整个业务链闭环比较好打通的社交媒体营销的市场营销员工开始。

后面逐步往客户、销售、行政这些岗位扩展,最后再做我们的牧言低代码平台的开发员工的工种(因为开发对逻辑严密性要求最高,所以我理解是最难的)

以推特为例子,我想象的一个流程大概像下面的流程图一样,其中人类介入的审核等等流程,都准备做成可选的,换句话说理想情况就是 AI 从头到尾把所有活全干了,还能按照 tweet API 返回的触达和互动的数据,定期优化自己的绩效。真正实现躺着把活干了

流程图

对了我现在的 landing page 网站是 www.getAiAlly.com (这个域名也是 AI 帮忙选的)

大家觉得实现难度上怎么样呢,然后你觉得这个点子能赚钱不~~

我现在是整天让 AI 帮着干活,写网页,做 SEO ,写代码,评估早饭健不健康,用的工具从 cursor 到 claude.ai 到 monica ,感觉整个人已经木了……

请问,如果需要 MySQL 存储几百个二进制位的数据,该用什么类型

7 September 2024 at 16:49
awesomePower:

举个例子,有一类数据,它大概有几百多种属性,属性的正好也只有 0 和 1 这两种可能。

在这种情况下,应该挑选哪个类型的字段来存储这个数据呢?需要满足以下条件:

  1. 空间占用尽量小
  2. 需要方便查询,比如我需要筛选出第 5 个、第 77 个、第 100 个属性...是 1 的数据(同时是 1 或者其中有一个是 1 这两种情况都需要查询)
  3. 用多个 BIGINT 存储可行么

分享一个空间利用率超高的 Base36 算法

By: iqoo
7 September 2024 at 15:16
iqoo:

很久以前学习 C 语言时写的一个练手项目,在常规的进制转换上做了些改进。

https://github.com/EtherDream/base36_914

进制转换一般分两种。一种将整个输入数据当做一个大数,然后不断模 N 和除 N ,将大数分解成一堆 N 以内的数据,从而实现 BaseN 。这种方案空间效率最高,借助大数库实现也不难,只是大数运算非常耗性能。

另一种方案则是每次取 4 或 8 字节到寄存器中,然后不断模 N 和除 N 进行分解。这种方案性能高,实现简单,但空间利用率可能不高。

本方案 Base36 编码时每次读取 9 字节到一个 u8 和 u64 中,利用除法和取模上小技巧,只需少数计算即可将其分解成 14 个字符。空间利用率为 64.2%,相比 Base36 的理论效率 64.6% 只差 0.5%。(不考虑尾块填充的情况下)

在线演示: https://etherdream.com/base36/

如果存在 BUG 或者有更好的改进方案,欢迎提出建议。

写 RUST 写的好爽

7 September 2024 at 08:32
DollarKiller:

感觉 rust 配合 ai 写代码超级爽 效率超高的:


我使用 RUST 搭配 Codeium 和 ChatGPT 写代码的技巧

感觉 codeium 搞歧视 rust 提示很全,golang 的提示就相比少了很多

开发步骤:

  1. 确定功能和方法: 首先,先想好你需要实现的功能。然后,根据功能列出需要的方法以及每个方法的用途。

  2. 定义输入输出: 接着,明确每个方法的输入参数和返回值。如果涉及到结构体等复杂数据类型,直接将这些结构体交给 ChatGPT 来生成代码。

  3. 无需过多思考细节: 在这个过程中,你几乎不需要过多思考代码细节。只要考虑好功能模块、数据来源以及数据加工的逻辑,剩下的就可以交给 Codeium 和 ChatGPT 来完成。

示例代码:

fn example_function() {
    // 定义好方法名、功能、输入参数、返回值等,如果涉及数据库操作告诉他怎么取数据 orm 是什么 然后直接扔给 chatgpt
}

后端接口一定要保持单一职责吗

7 September 2024 at 00:17
chenxiaolani:

之前干前端的时候遇到一个需求就是需要用表格展示一些订单或者商品信息,但是跟我对接的后端哥们让我从两个接口拿。但是我想让他再单独出一个接口直接返回我所有的信息,这样我就不用请求两次接口再拼装展示到表格中; 我的想法就是: 单独一次的请求肯定比两次性能好,万一有一个请求嘎了或者慢了, 表格数据就加载慢或者不出来了。

后端那哥们的想法是:接口需要保持单一职责,再单独出一个接口就重复了。

当时刚到那家公司,也没劲跟这哥们扯犊子,不过他给的理由我也无办法反驳,但是我感觉我的想法也是对的。

大佬们遇到这种情况,最佳的做法应该是咋样的啊。

===嵌入式软件开发新手入门请指点===

By: FreeWong
7 September 2024 at 09:50
FreeWong: 仅会上位机软件开发,C 语言也才看了二个星期。
有什么你认为对于入门新手来说,非常值得推荐的书籍,教程有哪些,感激不尽。
另外,野火开发板的教程,以及开发板是否对于新手入门有帮助?
https://doc.embedfire.com
任何建议,请分享给新手入门,感激。

写 RUST 写的好爽

7 September 2024 at 08:32
DollarKiller:

感觉 rust 配合 ai 写代码超级爽 效率超高的:


我使用 RUST 搭配 Codeium 和 ChatGPT 写代码的技巧

感觉 codeium 搞歧视 rust 提示很全,golang 的提示就相比少了很多

开发步骤:

  1. 确定功能和方法: 首先,先想好你需要实现的功能。然后,根据功能列出需要的方法以及每个方法的用途。

  2. 定义输入输出: 接着,明确每个方法的输入参数和返回值。如果涉及到结构体等复杂数据类型,直接将这些结构体交给 ChatGPT 来生成代码。

  3. 无需过多思考细节: 在这个过程中,你几乎不需要过多思考代码细节。只要考虑好功能模块、数据来源以及数据加工的逻辑,剩下的就可以交给 Codeium 和 ChatGPT 来完成。

示例代码:

fn example_function() {
    // 定义好方法名、功能、输入参数、返回值等,如果涉及数据库操作告诉他怎么取数据 orm 是什么 然后直接扔给 chatgpt
}

写 Go 真的好爽

By: jlak
6 September 2024 at 15:56
jlak: 写 Go 一个月了,越写越爽,最近开始用上了 Ai 辅助后真倍爽
自己也没搞清楚为什么写 Go 就会特别爽
有可能是规范和提示,这点动态语言不太完善
(但是不知道为什么 TS 就不爱用)
即使给程序来个大改也比较轻松不容易出错
还有代码是真的容易看懂
自己写的也好别人写的也好从来没想过能这么容易懂

后端接口一定要保持单一职责吗

7 September 2024 at 00:17
chenxiaolani:

之前干前端的时候遇到一个需求就是需要用表格展示一些订单或者商品信息,但是跟我对接的后端哥们让我从两个接口拿。但是我想让他再单独出一个接口直接返回我所有的信息,这样我就不用请求两次接口再拼装展示到表格中; 我的想法就是: 单独一次的请求肯定比两次性能好,万一有一个请求嘎了或者慢了, 表格数据就加载慢或者不出来了。

后端那哥们的想法是:接口需要保持单一职责,再单独出一个接口就重复了。

当时刚到那家公司,也没劲跟这哥们扯犊子,不过他给的理由我也无办法反驳,但是我感觉我的想法也是对的。

大佬们遇到这种情况,最佳的做法应该是咋样的啊。

Before yesterdayMain stream

All In Boom 带来的思考

By: qizheng22
4 September 2024 at 15:03
qizheng22:

ESXi 7*24 小时稳定运行两年多,前几天拉闸换灯,没提前关电脑。重新开机时发现有台虚拟机找不到了,无法重新注册。目录里只有-flat.vmdk 文件。后来通过其他虚拟机挂载 vmdk 找回。

没过几天,ESXi 的 ssl 证书到期。替换证书重启无法进入系统。提示:

Shutting down firmware services... 
Using 'simple offset' UEFI RTS mapping policy
Relocating the modules and starting the kernel...

即使指定 ignoreHeadless=TRUE 也无法跳过。

虽然重装 ESXi 解决。但是在故障期间,我试着把硬盘拿出来备份数据,在读取 ESXi 的 VMFS 文件系统遇到不少阻碍。一般的磁盘工具无法识别,还有的只能识别很老版本的 VMFS 文件系统。还有一个数据恢复软件是收费的。

使用 ESXi 的初衷是为了隔离不同系统和运行环境。而 ESXi 稳定可靠,自身占用开销小,接近物理机性能。但是这几年使用中发现不少痛点。

  1. 显卡只能直通某一台虚拟机。如果有多台虚拟机,无法共享 GPU 算力。
  2. 未直通显卡的虚拟机无法 GPU 加速。浏览器/远程桌面等应用只能 CPU 软解码,导致 CPU 使用率占用高。
  3. VMFS 文件系统没有预想的通用,不能方便地通过其他系统或磁盘管理工具拿回文件。

必需基于 ESXi 的 All In One ?

在回答这个问题前,先看 ESXi 带来的优点。

  1. 可以 7*24 在线,只能手动更新。ESXi 本身只提供虚拟平台,只要没有功能 bug 和驱动问题。基本不用更新。
  2. 因为 ESXi 只是一个虚拟平台,在使用过程中非常纯净,不会担心因为安装其他乱七八糟的软件弄坏系统。而且支持 U 盘运行。(我用 U 盘稳定运行过一年,系统是加载到内存里里启动的)
  3. 系统自己占用 1G 多内存,不会有其他内存开销。基于 WEB 管理,非常方便。

如果换其他平台,上面三点如何解决?

  1. 对 7*24 小时的机器来说,影响最大的是重启。windows 现在的更新每月一次,可以关闭自动更新和自动重启。而 linux 大多数更新都不用重启。
  2. 无论是 Windows 还是 Linux ,用户文件基本只在用户目录里。只是一般 Windows 都会给最大权限,其他软件又不规矩,相对于 Linux 确实会产生更多垃圾文件。
  3. 在默认启动图形化系统后,Linux 轻量级桌面环境 xfce ,lxqt 占用内存 1G 以下。即使是 gnome ,kde ,占用 2G 以内。Windows 10 LTSC 是 2G 以下。都是可以接受的。

Windows+Hyper-V 还是 Linux+VMware ?

Windows 平台软件丰富,驱动支持好。Windows 下使用 Linux 子系统或 Docker ,需要开启 Hyper-V 。Hyper-V 虚拟化方案是 Type1 ,对标 ESXi ,性能比 VMware ,VirualBox 这类 Type2 好。能用 GPU 加速,但不支持 USB 。启用 Hyper-V ,会和其他虚拟类软件冲突。虽然最新的 VMWare 和 VirtualBox 虽然已兼容,但性能会有损耗。还有很多如安卓模拟器等依然会冲突。

Linux 下对于程序员来说开发环境搭建方便,大多数开发环境只需一条命令安装,玩 AI 也适合。VSCode ,JetBrains Gateway 等远程开发方案都只支持 Linux 。Linux 下原生支持 Docker ,大多数非图形化程序在 Linux 下运行效率高( Windows 的 Microsoft Defender 扫描,对有很多小文件的项目磁盘 IO 有较大影响)。VMWare Workstation 个人已完全免费,性能好,支持 USB 。

如果选择 Windows ,一定会安装 Linux ,因为 NAS 和 Docker 是刚需。而且现在两块数据机械硬盘都是 EXT4 格式,迁移成本大。

如果选择 Linux ,不一定会安装 Windows 。因为有笔记本电脑,Windows 下的电源管理比 Linux 强多了,笔记本电脑还是首选 Windows 。

ai 写代码: cursor 有没有平替, 20 刀有点太贵了

4 September 2024 at 13:39
tdb11039gg:

下面是 ai 推荐的工具:

  • Kite:Kite 是一个编程助手,它提供了代码补全功能。它有免费版本,同时也提供了更高级的付费功能。
  • TabNine:TabNine 是一个使用深度学习进行代码补全的工具,它也有免费版本,并且性能不错。
  • GitHub Copilot:虽然 GitHub Copilot 目前是付费的,但它为学生和开源项目维护者提供了免费使用的机会。
  • CodeSuggester:这是一个基于 AI 的代码建议工具,目前是免费的。
  • AI Code:这是一个基于 AI 的代码生成器,它提供了一定的免费额度。
  • IntelliCode:这是微软为 Visual Studio 和 Visual Studio Code 提供的代码补全工具,它是免费的。
  • Codota:Codota 提供了代码补全和示例代码的功能,它有免费版本,也有更高级的付费版本。

大家常用哪个工具可以推荐下。cursor 确实挺好用的,太贵了。

我晕了 论坛社区 ICP 怎么搞

By: manami
4 September 2024 at 10:36
manami: 《关于第五批取消和下放管理层级行政审批项目的决定》第七条为取消互联网电子公告服务专项审批(备案)的通知

只看这个通知标题,我以为论坛社区 ICP 备案变简单了,不再需要专项审批

在阿里云咨询,得到的却是更严格了?不能备案

https://imgse.com/i/pAVLX1e

https://imgse.com/i/pAVLj6H

有点晕了 有没有了解的老哥说说 如何解读这个通知

只想知道独立开发者注册个小微企业,能不能把论坛社区的 ICP 备案办下来

app 初始化时需要通过接口获取上千个独立的配置项,如何优化?

By: aboutboy
2 September 2024 at 10:33
aboutboy:

正在开发一个 app ,用户在使用 app 访问服务时,需要根据对应的服务从后端获取对应的配置文件( json 格式)。

一共有上千个独立的配置项。

目前是当 app 第一次启动时,会首先通过接口查询配置项清单,然后再依次对各配置项进行请求获取。

这样的问题是,一个 app 就会向后端发起上千个请求。而且可能需要十来分钟甚至更长时间才能把全部配置拉下来。

这样一方面对后端服务器造成压力,另一方面影响用户体验。

如果把配置全部打包在一起的话,大概40-50MB左右。

有些配置项还会更新,这就需要app 在后续的运行过程中对有更新的配置项进行更新

请问大佬们有什么好的思路?

5 个前端 JS 函数,只为了优雅解决 a.b.c.d = 1 问题

By: sdjl
1 September 2024 at 22:40
sdjl:

前言

最近更新了我的系列文章,其中有一部分是关于 JavaScript 语言中“点表示法”的使用

文章中有 5 个工具函数是纯JavaScript的,我觉得不仅仅是小程序项目用得上,其他前端 JS 项目也应该用得上

我把文章中的这一部分单独整理出来,给需要写前端代码的朋友参考参考

你可以直接在 Github 项目 中的前端 utils.js 文件中找到这 5 个工具函数。

下面是文章节选,完整的文章列表可以在 Github 项目 中查看。

点表示法a.b.c

用点表示法给对象赋值:putValue函数

想象一下,如果a是一个对象,你要给a.b.c.d赋值为 1 ,你会这样写?

let a // 通过某种方式获得的对象
if (!a.b) {
  a.b = {}
}
if (!a.b.c) {
  a.b.c = {}
}
a.b.c.d = 1

拜托,大学生才这样写。为此,我们引入了putValue函数:

utils.putValue(a, 'b.c.d', 1)

putValue函数会自动创建a.ba.b.c中间对象,它的声明如下:

/**
* 向对象中按照路径赋值,如果路径上的中间对象不存在,则自动创建。
* @param {Object} obj - 目标对象。
* @param {string} key - 属性路径,支持'a.b.c'形式。
* @param {*} value - 要设置的值。
* @param {Object} [options={}] - 可选参数。
*   - {boolean} remove_undefined - 如果为 true 且 value 为 undefined ,则删除该属性。
* @throws {Error} 如果 obj 为 null 或 undefined ,或路径不合法(如中间非对象)则抛出异常。
*/
putValue(obj, key, value, {remove_undefined = true} = {}){
  // ...
}

使用这个函数有两个地方要注意,一个是如果路径上的中间对象不是对象,会抛出异常。例如a.b=2,这里b不是对象,此时会抛出异常。

另一个是如果valueundefined,则会删除该属性。例如下面的代码:

utils.putValue(a, 'b.c.d', undefined)
console.log(a) // {b: {c: {}}},putValue 会自动创建中间对象,但不会自动删除空对象

但若你真想赋值为undefined,可设置remove_undefined参数为false

读取对象属性值:pickValue函数

对应的,如果要获取a.b.c.d的值,可以使用pickValue函数:

utils.putValue(a, 'b.c.d', 1)
const value = utils.pickValue(a, 'b.c.d')
console.log(value) // 1

当然你也可以直接使用javascript的原生语法:

const value = a.b?.c?.d

这两种写法,当中间路径不存在时,均会返回undefined

a.b?.c?.d这种写法是硬编码,而在实际开发中路径可能是动态的。例如用户要修改一个配置项,这个配置可能是user_config.font.size也可能是user_config.page.color.background,如果使用硬编码的方式,可能会写出这样的代码:

if (key === 'font.size') {
  user_config.font.size = value
} else if (key === 'page.color.background') {
  user_config.page.color.background = value
}
// 更多的 if 语句...

这样写显然不够优雅,看看putValuepickValue的组合用法。

// 写入用户配置:
utils.putValue(user_config, key, value)

// 读取用户配置
const value = utils.pickValue(user_config, key)

不管key怎么变,一句话搞定,感觉一下子和大学生拉开差距了是吧?

向数组末尾添加元素:pushValue函数

在实际开发中,常有向数组末尾添加数据的需求。例如记录用户最近的评论,此时你可以使用pushValue函数:

const user_data = {} // 用户数据
let comment = {content: '顶'} // 用户的评论

utils.pushValue(user_data, `articles.recent_comments`, comment)

console.log(user_data) // {articles: {recent_comments: [{content: '顶'}]}}

在上面代码中,pushValue函数先是自动创建了user_data.articles.recent_comments数组,然后把comment添加到数组末尾。pushValue函数的声明如下:

/**
 * 将值推入对象指定路径的数组中,若路径或数组不存在则自动创建。
 * @param {Object} obj - 目标对象。
 * @param {string} key - 数组属性的路径,支持'a.b.c'形式。
 * @param {*} value - 要推入的值。
 * @throws {Error} 如果路径不是数组,则抛出异常。
 */
pushValue(obj, key, value){
  // ...
}

再次调用此函数,数组中就会有两个评论:

comment = {content: '再顶'}

utils.pushValue(user_data, `articles.recent_comments`, comment)

console.log(user_data) // {articles: {recent_comments: [{content: '顶'}, {content: '再顶'}]}}

向对象中添加多个属性:putObj函数

前面我们使用putValue函数向obj对象写入了一个属性值,但如果你要写入很多个(例如 100 个)属性值,你可能会使用for循环:

let user_config = {}
let new_config_keys // 100 个新的配置项(数组)
let new_config_values // 对应的 100 个值(数组)

for (let i = 0; i < new_config_keys.length; i++) {
  utils.putValue(user_config, new_config_keys[i], new_config_values[i])
}

这样写没问题,但在实战中,你拿到的用户配置往往不是数组的形式,而很可能是一个对象,例如:

let new_config = {
  font: {
    size: 16,
    'family.first': 'Arial',
    'family.second': 'sans-serif',
  },
  'page.color.background': '#fff',
  // ...
}

// 你对拿到的配置数据又进一步处理
new_config.update_time = new Date()

这种情况下你可以使用putObj函数一次性写入多个属性值:

utils.putObj(user_config, new_config)

console.log(user_config)

/* 输出如下:
{
  font: {
    size: 16,
    family: {
      first: 'Arial',
      second: 'sans-serif'
    }
  },
  page: {
    color: {
      background: '#fff'
    }
  },
  update_time: '...',
}
*/

注意putObj会自动处理上面new_config变量中各种路径的写法putObj函数的声明如下:

/**
 * 将一个对象的所有属性按路径添加到另一个对象中。
 * @param {Object} obj - 目标对象。
 * @param {Object} obj_value - 要添加的属性对象,键支持'a.b.c'形式的路径。
 * @param {Object} [options={}] - 可选参数。
 *   - {boolean} remove_undefined - 如果为 true 且 value 为 undefined ,则删除该属性。
 * @throws {Error} 如果 obj 为 null 或 undefined ,或路径不合法(如中间非对象)则抛出异常。
 *
 * 注意
 *   若 obj_value 中出现重复路径,则后者会覆盖前者。
 *   如 obj_value = {a: {b: 1}, 'a.b': 2},则结果为 {a: {b: 2}}
 */
putObj(obj, obj_value, { remove_undefined = true} = {}) {
  // ...
}

从对象中获取多个属性:pickObj函数

同样的,我们可以一次性读取多个对象的属性值。例如虽然小程序中的用户配置非常复杂,但当前页面仅关注背景颜色、字体大小等少量配置项,你可以这样使用pickObj函数:

let user_config // 某个用户的所有配置

// 本页面需要关注的配置
const keys = ['page.color.background', 'font.size', 'font.family']

// 获取当前页面需要的配置
const curr_config = utils.pickObj(user_config, keys)

console.log(curr_config)

/* 输出如下:
{
  'page.color.background': '#fff',
  'font.size': 16,
  'font.family': {
    first: 'Arial',
    second: 'sans-serif'
  }
}
*/

console.log(curr_config.font) // undefined

注意,传给pickObj函数的第二个参数是一个字符串数组,而不是对象。并且,pickObj返回的对象中,属性值不是以curr_config.font.size这样的形式返回,而是返回curr_config['font.size']

当然,如果你想要curr_config.font.size这样的形式,可用putObj转换一下:

let obj_config = utils.putObj({}, curr_config)

console.log(obj_config.font.size) // 16

点表示法在微信小程序中实战演示

为什么要设计这几个函数?为什么要支持config.a.b.cconfig['a.b.c']两种写法混用?为什么传给putObj的第二个参数是对象,而传给pickObj的第二个参数是字符串数组?为什么pickObj返回的对象属性值不是config.a.b.c这样的形式,而是config['a.b.c']

因为这样设计符合实战需求,一句话解释就是:“这样好用”

下面我们通过几个案例来演示这些函数在实战中的应用。

在 js 中设置用户配置

假设用户首次打开小程序,你需要设置用户默认字体大小为 16 ,背景颜色为白色。可以这样写:

let user_config = {}
utils.putValue(user_config, 'font.size', 16)
utils.putValue(user_config, 'page.color.background', '#fff')

使用putValue设置后,你想修改字体大小和背景颜色?可以这样写:

user_config.font.size = 18
user_config.page.color.background = '#000'

在 wxml 中实现修改用户配置

你可能会在 wxml 页面中实现多个配置项的修改,并且使用同一个函数来处理。这时你可以这样写:

<button bind:tap="changeConfig" data-key="font.size" value="16" >
<button bind:tap="changeConfig" data-key="page.color.background" value="#fff">
changeConfig(e){
  const { user_config } = this.data
  const { key, value } = e.currentTarget.dataset

  // 从 wxml 中获得点表示法的 key 字符串,直接调用 putValue 函数
  utils.putValue(user_config, key, value)

  // 修改背景色时顺便改一下字体颜色(两种写法混用)
  if (key === 'page.color.background' && value === '#fff') {
    user_config.page.color.font_color = '#000'
  }

  // 记录最近修改时间
  user_config.update_time = new Date()
}

在 wxml 中使用用户配置

要在页面中使用page.color.backgroundpage.color.font_color的值,实现根据用户配置显示不同的颜色,可以这样写:

<view style="background-color: {{color.background}}">
  <text style="color: {{color.font_color}}">
      Hello, WxMpCloudBooster!
  </text>
</view>
onLoad(){
  const { user_config } = this.data
  const color = utils.pickValue(user_config, 'page.color')
  this.setData({color})
}

你看,我们传递给pickValuekey根据实际需求可长可短。

初始化默认的用户配置

你希望为每个用户设置一个默认的用户配置,并且你想用常规方式写(不使用点表示法)。可以这样:

// 默认配置
const DEFAULT_CONFIG = {
  font: {
    size: 16,
  },
  page: {
    color: {
      background: '#fff',
      font_color: '#000'
    }
  },
  // 这里也可以使用点表示法 a.b.c ,但你不想这样写...
}

App({
  initConfig(){
    let { user_config } = this.data
    utils.putObj(user_config, DEFAULT_CONFIG) // user_config 的其他值会被保留
    // 保存用户配置...
  }
})

注意,上面代码中user_config可能会有其他没有出现在DEFAULT_CONFIG中的配置项,这些配置项会被保留。

记录用户最近发表的内容

假如你已经实现了“记录用户最近发布的评论”功能,代码如下:

<button bind:tap="append" data-key="articles.recent_comments" data-prop="comment" >

当用户点击这个按钮时,假设this.data中已经有一个comment对象,你可以这样添加评论:

append(e){
  const { user_data } = this.data
  const { key, prop } = e.currentTarget.dataset
  const value = this.data[prop] // prop === "comment"

  utils.pushValue(user_data, key, value) // 注意这里用的是 push
}

上面这个append函数会把this.data.comment对象添加到user_data.articles.recent_comments数组的末尾。

然后,此时你希望再增加一个按钮,可以把最近的点赞数据this.data.like添加到user_data.articles.recent_likes数组的末尾,那么只需一句:

<button bind:tap="append" data-key="articles.recent_likes" data-prop="like" >

完成了,你不需要修改append函数,只需要给data-keydata-prop属性设置不同的值即可。

可见,点表示法很大的目的是为了在wxml中可以方便地指定路径,并在js中方便地处理这些路径。

在页面中修改多个配置项

假设你有一个修改用户配置项的页面,wxml代码如下:

<!-- 注意这里有一个 for 循环 -->
<view wx:for="{{configs}}">
  配置名称:{{item.title}}
  当前值:{{item.value}}
  输入新值:<input type="text" />
  点击修改:<button bind:tap="changeConfig"/>
</view>

上面代码使用了for循环,configs变量中有多少个值,就会显示多少个配置项。

为了实现在用户打开页面时显示的是用户的当前值(而不是默认值),你还需要从user_config中读取当前用户的配置值。

代码样例如下:

// 代码中写死了需要修改的配置项以及默认值
configs = [
  {title: '字体大小', key: 'font.size', value: 16},
  {title: '背景颜色', key: 'page.color.background', value: '#fff'},
  {title: '字体颜色', key: 'page.color.font_color', value: '#000'},
]

// 读取当前用户的配置值
const uc_obj = utils.pickObj(user_config, configs.map(item => item.key))

// 注意,这里的 uc_obj 是 uc_obj['font.size'] 这样的形式,而不是 uc_obj.font.size

// 用户当前值覆盖默认值
configs.forEach(item => {
  if (uc_obj[item.key] !== undefined) {
    item.value = uc_obj[item.key]
  }
})

this.setData({configs}) // 传给 wxml 页面显示

这样你就实现了修改多个配置项的页面,用户打开页面时显示的是用户当前的配置值。

提问:假设我们坚决不使用点表示法,且要实现上面这些功能,你要如何设计才能如此简单、高效?

让你的函数也支持点表示法

好了,目前我们花了不少篇幅介绍点表示法,这是因为后面我们会介绍更多的工具函数,而这些工具函数都支持点表示法的调用方式

当你编写自己的工具函数时,你可以调用putValuepickValuepushValueputObjpickObj这 5 个函数,轻松地让你的工具函数也支持点表示法。如果你不知道如何实现,可以参考utils.js中其他函数的代码。

(文章节选完,如果你感兴趣的话可以看看 Github 项目

关于 Java 和 Spring boot 开发体验的一些疑惑,请高手解答

By: woodytang
30 August 2024 at 19:11
woodytang:

之前写前端比较多,最近开始用 Spring boot 开发 api 使用的是 JPA Hibernate orm+jdk21 方案,我不知道为什么感觉国内很多人不喜欢这个 Hibernate ,但是我自己用下来感觉非常好,它最牛逼的是可以按照 Entity 生成数据库结构,非常智能,所以你只要把实体定义好,一切都 ok 了,很容易维护。 有个插件 JPA buddy ,非常好用,可以很好规范代码。 JPA 也可以自定义 sql ,很灵活,除了在转成 json 的时候,会遇到无限循环的问题,其他感觉都很好。 感觉 Spring boot 写后端,除了 JAVA 本身有点啰嗦,还有编译很浪费时间,其他都还蛮不错的。

但是 JPA 通过 Entity 生成数据库的时候,为什么字段顺序和 Entity 是不一样的,是打乱的,,id 的位置会随机出现~~我槽,这就像要喝汤的时候飞进了一个苍蝇,我看网上有很多人说要 hack 一下,官方无解,但这个太离谱了。

然后就是 CommandLineRunner ,我本来的需求是需要给项目 seed 一点初始化数据,这个 CommandLineRunner 只在启动的时候自动执行,不能通过 cli 命令执行,,泥马这个 CommandLineRunner 和 Command 一点关系都没有啊,我内个去,所以 seed 数据只能在 CommandLineRunner 加 if 条件。你去看人家 PHP laravel 都有 artisan cil 工具啊,这个可是天天都要用的,为啥你没有啊~~~你 Tomcat 都可以监听指令,为啥就不做一个监听 命令行指令的。 后来我看了一个 spring boot cli 的插件,结果完全不是这个概念。。

为什么这么成熟的框架连这些基本都做不好啊谁可以告诉我一下

然后我现在在看 liquidbase 和 flyway ,还是 flyway 吧,直接 sql 多优雅,liquidbase 那个语法不能忍受~~

谁能告诉我怎么才能优雅的搞后端呢?

❌
❌