分类 唠嗑 下的文章
一个关于GUI面板软件的想法
前段时间,和群友偶然聊到了GUI话题,期望可以使用GUI应用来替代前端工具,比如代码规范/校验等,但是查找了相关资料,关于代码管理类的GUI程序非常少(甚至说是一个空白),总结了以下大概有以下几点:
- 安全性问题,用户不期望将代码上传到服务端
- GUI设计不贴合程序员,提升不了工作效率
- 极难扩展,大多数基于代码管理的SASS服务,程序员不可以开发插件去扩展已有功能
如果你是Mac用户,相信你一定知道RayCast,RayCast本质上是一个代替Mac启动器的应用程序,但是由于其强大的插件机制,可以让前端开发者方便地开发各种插件,比如翻译/词典/截图等等,如下图是我安装的一款颜色选择器插件:
RayCast的最佳设计不仅限于此,它还提供了‘快捷键’功能,使其通过预定义好的快捷键,轻松的唤起用户装的指定扩展,例如在Mac中有一款大名鼎鼎的剪贴板软件叫作Paste,而在RayCast中可以直接免费的使用类似的功能,装好指定的插件之后,只需要快捷键就可以唤出剪贴板软件:
另外,有曾接触过运维的同学们应该都知道一个国产的面板应用“宝塔”,虽然这款软件面向的是初级运维人员,但是其免费易用的特性让不少非专业的开发人员也能管理服务器。比如本博客就在使用宝塔软件,是我在上高中时使用宝塔搭建的,已经运行有5,6年了。抛开安全问题,作为一个在当时不懂运维的同学也能深深产生依赖,一直沿用至今,这或许就是GUI的魅力。
读者们对于GUI程序并不陌生,我们在使用的Mac,iphone等操作系统都在时时刻刻的服务用户;同样我们开发者领域也有一些出色的GUI程序,比如Clashx,SourceTree帮助着我们日常开发。
但是有没有一款工具可以改变开发习惯,真正的提高效率,并且兼顾安全。拿前端领域举例,诸如Eslint等工具层出不穷,包管理工具也是五花八门,而且作为一名优秀的开发者无时无刻都在使用工具去减少重复劳动,我们需要有大量的额外工具,例如JSON校验,Mock服务,Local Server,甚至是代码优化,日报/日志工具。
很遗憾,我们目前并没有这样的软件去集成这些五花八门的工具,但是读者们会心里想,我可以唤出浏览器打开一个工具网站就可以解决所有问题,为什么还需要一个GUI软件去做这样的事情呢?这也是本篇文章的重点。
依赖管理
NPM对于前端开发者来说并不陌生,我们想要安装一个库其实要做很多调研,大多需要查看其更新时间,下载量以及issue数量等等;但是当你有一天想要开发Rust程序时,你并不知道你要编写的功能需要安装什么库,你也不知道库和库是如何依赖的,这势必要浪费很多时间,所以如何统一各个语言的依赖管理呢?
这里实现技术也比较简单,无非就是将用户所期望的依赖底层通过命令行的形式进行安装;GUI的重点是如何查找到开发者心仪的依赖,下图是使用NPM搜索PDF
关键字的结果:
在默认搜索模式下,在搜索如此具体的关键字时,仍然找不到pdf.js的核心库,如果我是一个对前端领域比较陌生的开发者,我大概率会安装第一个依赖。虽然举得这个例子有点极端并且有一点杠精
,但是作为一款跨平台的代码项目管理工具,我们要做的是多个流行语言的包管理工具,对于经常跨语言开发的开发者,你不再需要打开多个标签页并且你也不需要熟悉你安装依赖的命令,只需要2步:
- 精确地搜索出依赖列表
- 右键单击鼠标,你的依赖会自动下载
如果在依赖列表中看到了你已安装的依赖,你完全可以双击右键去卸载它,这在我们频繁查找与安装依赖的场景非常有用,并且节约时间。
另外依赖检索列表中,会借鉴NPM的搜索模式,并且在之上结合搜索引擎和ChatGPT,会给你一个智能的依赖推荐,尽管你搜索的关键字和实际的依赖完全不同,你也能快速安装它!并且智能推荐是联网的,你无需GPT4,软件内部会将搜索引擎的结果提供给GPT,让GPT提取可用的依赖信息。
代码体检?咋有点像360?
代码体检的功能是一个仍然在探讨的feature,所以本章和最后完成的功能差距可能会非常大。在一些代码管理的SASS产品中,可能会提供这种功能,并且相对耗时以及昂贵。那么我们如何做这个功能呢?当代码项目被添加到软件中时,会经过一次极快的预处理,会在短时间内得出项目的技术栈和依赖关系,基础的模块数量。
在真正体检时,会得出一份报告,这个报告生成的过程不会产生联网的请求,一切都在本地完成,不依赖GPT。因为大额的代码段落让GPT分析会造成很多Token消耗,这一般人不太用得起(笑哭)。所以这块可能会使用开源的一些检测工具去扫描代码库中可能重复的函数,并且行数特别多的函数,并且注释的数量多少也会影响报告分数;
报告中的检查项可以手动关闭,因为开发者的习惯并不一样,有些人并不习惯编写注释,也有一些项目避免不了行数过多的函数。如果关闭对应的检查项,软件也会跳过这些步骤,势必会更快地生成报告。
工作流
软件会提供一个基础的工作流,将软件项目和快捷键绑定,甚至和其他软件绑定。你可以理解为迷你版的快捷指令,可以用代码编写工作流中的代码,比方说你有一个很重很大的项目,当你有一天重启了电脑或者应用崩溃闪退,导致你不得不重新打开这些应用。那么你就可以自定义一个工作流,当你使用快捷键唤出项目时,你的数据库,Docker,Redis就全部按照你的指示有顺序的跑起来了!
另外当你编写代码时,软件会每隔一段时间去检测你正在编写的模块,找到你经常更改的代码块,并且自动的给出你优化的建议(会有一个提示消息,和Copilot不同)。
利用到GPT的地方会有很多,比如当您把代码提到暂存区时,软件就已经分析其代码并且对代码做总结,生成好commit message,你只需要看到消息之后,点击确定,就可以应用这个提交。
除了显性的工作流之外,在软件内部,不管是体检的预处理分析文件还是你在编写代码时候产生的输出,都有可能会给软件其他功能提供支持。比如日志/日报插件,它会总结你一天的Commit message和具体的工作任务,你可以为其定义一个日报模板,只需要稍加修改,就可以一键发布;发布到哪儿呢?Github/Gitlab/钉钉,只要存在第三方接口的应用,自己编写逻辑,就可以直达!
插件
还记得一个成功的软件核心是什么吗?就是插件,软件可以允许用户使用HTML/JS定义界面,使用Rust/WASM去定义其实现,关于支持Nodejs?这个得看后续啦。
插件分为2类:
- 核心官方插件(上述的几个功能都属于核心插件)
- 第三方插件
这是借鉴了Obsidian,对第三方插件必须要提供源代码并且接受监管,如果对于非常流行的第三方插件会合并到官方插件中,但是会继续由社区开源爱好者(和官方开发者)一起推动维护。
有了第三方插件,你脑海中的各种工具,比如JSON校验,Mock服务,Format等等,把那些工具网站都删除了吧~,只需要一个快捷键就可以抵达的插件,无论是性能还是便捷性,本地的软件都会更胜一筹。
总结
对了,软件的名字叫作YourPanel,现已开源:
https://github.com/yourpanel/core (Next.js + Tauri)
文章中的GPT相关功能,都可以关闭,关闭这些GPT调用之后,任何有网络连接的插件在运行时,都会让你知晓;并且在安装插件时,你也可以清楚地看到插件是否基于网络连接!
作为一个本地的代码管理面板软件,我期望你的代码只允许你一个人访问,并且期望用较少的网络连接去完成任务。在初期使用中,我们没有计划引入用户管理,这意味着软件没有登录,我们也看不到你的代码和你的任务。
我们的初心就是:在一个安全环境下,提供开发者便捷服务。
时间紧迫,我们快速过一下最新的Vue3.3
久别重逢,最近换了新工作,三个月很忙碌,以后博客会定期更新;前几天Vue发布了3.3,我今年没写过Vue,但是也关注Vue的进展,总的来说Vue的发展方向在version 3这个版本中基本稳定了,反而DX的设计上会越来越好,如果你不使用Typescript,那么Vue3的许多新特性你可能并不适用,今天我们简单看一下Vue3.3的内容。
概览 (GPT总结)
- defineOptions:可以在 setup 中使用它来声明组件名称等组件属性,避免了需要再写一个script标签,使得新手和 Vue2 -> Vue3 的用户更容易理解。
- setup 中的 TypeScript 类型改进:支持从其他类型文件中导入类型,不再需要在 内使用 defineProps 等宏定义类型,但是目前还不支持复杂的类型操作。
- defineModel:简化了父组件和子组件双向通信(v-model)的代码,使得双向绑定的逻辑更加明了。
- defineSlots:可以自定义组件的 slot 类型,适用于在复杂场景下 Volar 不能准确推导组件类型的情况。
- 泛型组件:对于带有 slot 的复杂组件来说,可以自动推断传入的类型。
- setup 中的提升变量:支持将基础数据类型(除了 Symbol)的变量提升到顶部,解决了之前在 defineProps 时只能使用字面量的问题。
defineOptions
在之前的setup语法糖里面,如果要声明组件名称等组件属性,需要再写一个script标签,这会给新手和Vue2 -> Vue3的用户带来困惑,并且会给Eslint/Volar带来一定的难度,现在可以使用defineOptions宏来指定这样的信息,但是defineOptions不允许指定props/emits,因为这两者可以使用其他宏指定。
<script setup lang="ts">
defineOptions({
name: 'HelloWorld',
})
</script>
setup中的Typescript类型改进
这是一个很旧的问题,在使用defineProps时,是不能从其他地方引入类型使用的,可以具体查看RFC。在这个版本将支持从其他类型文件中导入,比如这样:
<script setup lang="ts">
import type { Props } from './foo'
defineProps<Props & { extraProp?: string }>()
</script>
只不过目前并不支持复杂的类型操作,在之前我们使用defineProps做类型声明时,如果传入了类型,编译器将会将类型转换为运行时代码;而现在如果要支持外部的类型要么就调用Typescript的龟速编译器,要么就自己实现一个基础的编译器,让其识别到导入的“是什么类型”。目前Vue采用了第二种方案,并且支持一个复杂类型的例子(如上代码所示)。
defineModel
简化了父组件和子组件双向通信(v-model)的代码,比如在以前我们编写v-model相关逻辑,需要写这么多代码:
<script setup lang="ts">
const props = defineProps<{
modelValue: number
}>()
const emit = defineEmits<{
(evt: 'update:modelValue', value: number): void
}>()
emit('update:modelValue', props.modelValue + 1)
</script>
现在使用defineModel:
<script setup>
const modelValue = defineModel<number>()
modelValue.value++
</script>
defineSlots
在复杂场景下,有时候Volar并不能准确推导组件类型,或者有时你想自定义组件的slot类型,可以使用defineSlots:
defineSlots<{
default(props: { item: T }): any
}>()
<HelloWorld :data="['foo', 'bar']">
<template #default="{ item }">{{ item }}</template>
</HelloWorld>
泛型组件
这对于普通组件来说意义不大,但是对于带有slot的复杂组件来说很有用,意味着可以自动推断传入的类型,在组件中使用,比如以下的场景:
<script setup lang="ts" generic="T extends string | number, U extends Item">
import type { Item } from './types'
defineProps<{
id: T
list: U[]
}>()
</script>
T和U都可以被用作emit/ts代码逻辑中,它和defineSlot在一起用最好
scripts中的提升变量
Vue3在之前在编译sfc的template时,会将静态内容提升,这样在大型项目中重复的静态内容会用这样的优化手段会得到提升,可以看一下之前的Vue文章。但是现在在setup中,也支持这样的优化了,如果在script中编写了基础数据类型(除了Symbol)的变量,都会被提升到顶部。这个改进主要解决了之前的问题,比如defineProps时,只能使用字面量:
<script setup>
const hello = 'world'
defineOptions({
hello,
})
</script>
这样的代码在之前是会有编译错误的,现在不会了,原因是defineProps宏编译之后,会变成下面的代码:
const __sfc__ = {
props: [propName],
setup(__props) {
},
}
显然这是错误的,无法访问到propName,经过提升优化之后,将会解决这个问题:
const __sfc__ = {
propName: "hello",
props: [this.propName],
setup(__props) {
},
}
(伪代码)
更符合人体工学 的 defineEmits
同样的,这次改进主要是少写一些代码:
const emit = defineEmits<{
(e: 'foo', id: number): void
(e: 'bar', name: string, ...rest: any[]): void
}>()
现在可以这么写:
const emit = defineEmits<{
foo: [id: number]
bar: [name: string, ...rest: any[]]
}>()
解构Props不失去其响应式(实验性)
如下代码所示,这更好的提供props的默认值:
const { msg = 'hello' } = defineProps(['msg'])
此时,msg仍然还是一个响应式变量,并且如下代码可能也不会再需要了:
export interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
})
在此前经常使用withDefaults宏去提供props默认值,现在有更好的趋近于es6的写法,所以建议大家之后可以使用这个功能,但是这个功能是实验性质的,需要自行斟酌。
时隔2年锈迹斑斑的Vue3知识点
这篇文章是一个小记, 因为涉及到的东西很简单, 所以只是做一个总结, 想学新东西的读者可以关掉网页了, 如果你还是一个vue3小白, 那么这篇简短的小记可能会帮助你.
渲染器
我们都知道vue中的sfc单文件模板是会被编译为一个渲染函数的, 这个渲染函数最终返回的就是VDOM, VDOM是虚拟dom的自定义实现, 因为虚拟dom不是一个规范, 而vue3对比以往版本也在VDOM上做了很多优化; 我们从宏观上来看vue3的渲染pipeline可以由下面的步骤表示
预编译/运行时编译出渲染函数 -> 进行渲染创建真实dom -> diff比对高效替换dom
具体的优化主要体现在预编译
上, 因为vue支持我们手写渲染函数, 但是官方推荐我们使用模板语法
, 最主要的原因就在于其更容易静态分析
, 也就更容易做编译时的优化, 就和vue的treeshaking优化一样(es规范是可被静态分析的, 从而实现死代码消除技术), 所以我们在后面的讨论都围绕模板语法来讲.
试想一下, 在vue2中的渲染原理, 都是静态编译我们的模板, 但是有一个致命的缺点: 静态内容也会被重新diff
什么意思呢?
如果我们在模板中写一段代码
<p>hello world</p>
事实上这段代码根本不可能有变更, 没有绑定, 也没有读取任何响应式变量, 所以在vue3中采用了PacthFlag
去给这段vdom子节点标注类型, 让其在diff期间主动跳过. 关于PacthFlag它在vue源码中是一个枚举, 定义了很多标识, 它们都有一个共同点就是都是位运算, 在diff运行时其实表现的性能消耗非常小
export const enum PatchFlags {
TEXT = 1,// 动态的文本节点
CLASS = 1 << 1, // 2 动态的 class
STYLE = 1 << 2, // 4 动态的 style
PROPS = 1 << 3, // 8 动态属性,不包括类名和样式
FULL_PROPS = 1 << 4, // 16 动态 key,当 key 变化时需要完整的 diff 算法做比较
HYDRATE_EVENTS = 1 << 5, // 32 表示带有事件监听器的节点
STABLE_FRAGMENT = 1 << 6, // 64 一个不会改变子节点顺序的 Fragment
KEYED_FRAGMENT = 1 << 7, // 128 带有 key 属性的 Fragment
UNKEYED_FRAGMENT = 1 << 8, // 256 子节点没有 key 的 Fragment
NEED_PATCH = 1 << 9, // 512
DYNAMIC_SLOTS = 1 << 10, // 动态 solt
HOISTED = -1, // 特殊标志是负整数表示永远不会用作 diff
BAIL = -2 // 一个特殊的标志,指代差异算法
}
除了PatchFlag, vue编译在其渲染过程中还提供了其他优化, 比如静态内容提升以及树结构打平,监听函数事件缓存, 下面我来一一介绍以下的渲染优化方案
我们在预编译之前已经知道了静态内容, 我们不仅可以让它跳过diff的判断, 也可以让它在生成vdom树时共享引用, 比如下面的伪代码就很好的阐述了vue2和vue3的渲染优化区别
// vue2
const output = () => {
const template = {text: "hello world"}
return {
children: [{template}]
}
}
// vue3
const template = {text: "hello world"}
const output = () => {
return {
children: [{template}]
}
}
这样的提升对于大型vue项目来说减少了很多内存占用.
而树结构打平也很容易理解, 和我们之前讲的PatchFlag有所关系, 我们刚知道静态内容都会被跳过, 那么如何跳过的呢? 就是通过打平, 我们在模板中编写的html代码可以称之为一个block, 在一个block中会存在很多很多节点, 那么vue编译器将会把所有后代节点的动态节点全部挑出来并且返回一个没有层级关系的vdom, 这样又大大提高了diff效率以及update效率.
而事件监听也很简单, vue在之前的版本会把下面的代码点击事件视作一个动态内容, 以至于每次更新都需要追踪它的变化
<div>
<button @click = 'onClick'>点我</button>
</div>
而现在不会了, 在内部提供了一个事件缓存, 解决了这个问题
响应式
这个部分是vue3的重中之重, 在vue2中其阉割版的响应式导致了很多恶心的API, 在vue2中vue都会将data中的属性进行深层次的递归, 以便每个对象都绑定了getter和setter方法; 并且在数组的表现上差强人意, 原因都是因为defineProperty, 这个API我相信已经存在了vue人的脑子里好几年了, 我就不展开讲了, 我们着重讲一下vue3的响应式.
首先vue3的响应式是基于proxy的, 而且这个API是不能被polyfill的, 但是由于我们的浏览器越来越先进, 所以使用先进的API提升开发体验也是值得的, 况且IE已死; proxy为对象提供了很强大的拦截功能, 包括数组的delete, push等等, 而且vue也在响应式核心中做了很多魔法, 比如在for循环时的读取和绑定新值...
vue3和vue2一样, 都提供了类观察者模式的方案, 但是我更愿意称之为发布订阅模式, 因为观察者模式是一对一有强关联, 但是发布订阅通常会有一个调度中心; 而在vue中, 一个又一个的副作用函数都订阅了响应式变量, 只要在代码中使用过响应式变量, vue都会追踪到, 并且将副作用追加到一个WeakMap
队列中, 当变量变更就会以此执行所属的所有副作用.
但是我们没有办法知道哪里读取了响应式变量, 这就要靠proxy了, proxy可以拦截属性的读, 那么同理我们使用ref函数创建一个基本类型
const foo = ref(1)
它也会被包装为一个对象, 如果给ref传递了一个对象, 那么其实和reactive这个函数无异, 它们的底层都是一样的.
另外在vue3中提供了很多编译器宏, 使得我们可以便捷的操作响应式变量, 比如我们想要操作ref的变量
const foo = $ref(1)
foo = 2
因为编译器宏会自动帮助我们加入.value, 还有我们对响应式变量解包时, 会让其失去响应式, 我们同样可以使用编译器宏$()
来简化toRef的操作
const { x, y } = $(useXXX())
Composition API
一些面试官也特别喜欢问组合API和Option API的区别, 又会有人问React Hook和Composition API的优劣势, 第一个问题很容易解答, 第二个问题需要我们结合各种周边环境回答 (哈哈哈哈哈)
首先是option和composition的区别
composition主要解决了一个代码复用和可维护性的问题, 尤其是代码复用, 我们曾经在一个组件中要想复用逻辑, 通常会选择mixin, 抱歉我真的没有用过mixin, 但是我见过别人用, 觉得很不理解...
首先mixin它可以编写多个, 并且可以同时混入到一个组件中, 说句难听的话, 这就是把你眼睛蒙上把粑粑拉你头上; 你并不知道程序错误是哪个mixin导致的, 而且也没有命名空间, 会导致逻辑冲突;
而composition就像一个函数一样, 正常导入完全不受限制.
当我们的vue文件变得越来越大时, 我们的关注度并不是methods, computed, 而是业务, 我们需要快速的找到业务代码, 如果在vue2中, 你会发现你的业务代码分散到不同地方, 你无法聚焦你的业务; 但是在vue3中, 我们没有options api的限制, 可以自由的将不同业务进行聚合, 并且实现快速定位.
那么和react hook区别是什么呢? 这个vue官方都说了compistion的设计是借鉴了react hook, 但是其内部实现和react大大不同. 原因还是我之前在知乎说的, react的历史包袱太重, 它本来就不是面向普通应用的, 一切设计都要以内部为先, 而且react hook变量是闭包实现的, 并且由于其核心机制导致react组件渲染一次就会跑一边所有的hook, 所以你可能经常会使用useMemo
和useCallback
来优化调用次数;
vue3不存在react的问题, 所有composition api都会仅执行一次, 并且你很容易捕捉到它最新值, 相比而言react你需要关心它的依赖值和副作用, 心智负担较大; 这里并不是说react不好, 因为react的种种缺点都是react大神承认的, 对于js开发者而言, vue确实负担更轻.
但是我们在面试中, 要揣测面试官是不是react吹, 如果是的话, 请尽可能的吹react
常见问题
- watch和watchEffect的应用场景和区别
我觉得应该不会问这么傻的问题, watch是懒执行, 依赖明确; watchEffect依赖不明确但是是同步执行, 我们需要在初始化时执行逻辑并且变更也执行逻辑, 就可以考虑watchEffect
- 为什么会用proxy设计响应式
同上, 文章前面已经解答过了
- treeshaking咋实现的
基于esmodule静态分析, vue2因为是单例, 无法按需加载
vue3对比vue2整体的提升有哪些
全方位的, 我们刚刚讲的响应式和渲染器都是补了vue2的坑, composition也补了option api的坑, 并且源码使用ts构建, 虽然vue3的sfc类型提示很牵强, 但是相比vue2的类型提示要熬掉鱿鱼丝几根头发写类型体操好得多. 而且源码是采用monorepo构建, 其实在阅读源码更方便了, 每个模块都是独立的, 尤其是响应式模块, 我们如果想要在其他平台单独使用响应式特性, 直接npm安装即可. 而且vue的核心模块也独立了, 支持很多平台, 甚至可以使用vue编写终端应用, 对渲染出口不作限制
2022年终总结
前言
这是一篇非常草率的2022总结文章, 我会更多的介绍关于技术方向, 以便同行们在明年有更多方向和想法. 如何形容2022年呢? 总的来说就是“进步的太慢, 或者说根本没有实质性的进步”; 导致这个问题的原因有很多, 简单的说就是被一些垃圾工作缠绕住了.
工作
2022年我仍然在西安, 2023年将是我工作的第四年, 作为一个软件从业者, 我可能在西安混的还算看得下去, 但是只有我自己知道, 我其实什么也不是. 因为西安这个城市造就了互联网行业注定没有氛围, 周围的同行技术不错的太少了. 我以前想这可能和我的圈子或者西安(地域)有关系. 事实上我们只要下班之后多学一点多看一会, 就可以超越大部分人了. 学习对于程序员来说是最简单的事情, 为什么大部分程序员都没有这样的习惯, 是因为大家都被结婚, 孩子, 父母, 傻逼领导等各种事情困住了, 我想等我年龄大一点可能也会变得不爱学习了.
我今年接触了很多一线城市的开发, 发现整个中国软件行业都太浮躁了, 换句话说优秀的团队太少了. 企业喜欢赚快钱, 大多数企业不会关心技术, 他们注重结果混KPI, 有时候甚至不需要展示demo演示程序, 一个ppt足以征服领导. 这种公司通常有以下几个特征:
- 设置了日报和周报
- 喜欢开会
- 没有加班费
- 每1,2周发一次版本, 美其名曰敏捷开发
- 每个公司都会有一个卷王, 半夜三更提交代码, 也不知道公司给他了多少钱
- 测试, 前端, 后端互相甩锅
- 裙带关系
- 地域歧视
如果你的公司碰巧都有以上症状, 请你立即下班开始学习, 你要努力提升自己, 摆脱这种工作环境. 我今年划水机会蛮多的, 在朋友圈和各个地方吐槽了很多岗位, 比如产品经理和前端, 这两个岗位是最浮躁的, 我甚至觉得这两个岗位让2个高中生过来都能完成, 如果我是老板, 我就会雇佣高中生来替代他们 (事实上我也是前端)
产品经理的jd我会写:
- 熟练使用微信 (例如转发消息)
- 熟练使用腾讯会议
- 熟悉互联网黑话 (能抽时间做一下么 | 我们能不能临时出一个给老板看的版本)
前端的jd我会写:
好吧, 我承认前端的门槛比产品高多了, 至少产品可以不用出高保真原型和文档, 只需要转发消息就可以把产品搞定, 我觉得是一个中国人都会;
我今年在工作中收获很多, 得到了很多人的帮助, 他们带我成长, 比如说在上一家公司的康凯哥, joe, 枨, 聪, 测试双煞和其他技术部同僚, 他们真诚, 负责, 为我解决和分担了不少麻烦; 其中凯哥和joe在技术上帮助我了很多, 到目前凯哥在我心目中仍然是榜样的存在, 因为他才华横溢而且不厌其烦的为我解答问题; 同样的还有joe, 和他一起共事, 是一段非常难忘的经历. 在新公司中, 我和我的同窗好友(基友)在一起工作的体验是我从业以来最爽的, 因为我们在一起会有当时一起学程序的美妙感觉, 以至于我和他在一起配合, 比去大厂还要更好. 所以我跳槽时, 我没有准备任何面试, 毅然决然和他一起战斗.
技术
在技术这一篇中, 我会聊的更多一些, 我们先从公司项目说起吧, 我今年接触了万恶的flutter来构建一个史一样的app, 尽管我们没有任何架构可言, 写出了和我便秘20天拉出的史一样的又臭又长的代码, 但是打包出来, 我们惊讶的发现, app居然如此流畅, 这让我认识到了四件事:
- flutter真吊
- 对js又爱又恨
- reactnative要完蛋
- 原生开发要被抢占一大部分份额
但是flutter的开发体验太糟糕了, dart的标准库设计对标es6, 简直是天大的差距; 而且最头疼的生态问题仍然很难解决, 比如地图, 图表仍然没有很好的支持. 而且对于前端而言上手难度颇大, 因为html和flutter ui抽象机制完全是思维的转变, 而且flutter/dart的编译器简直是太糟糕了, debug难度非常高, 如果离开了google和ai, 我很怀疑我能不能用flutter开发第二个app.
希望dart标准库能够多多迭代, 参照es6的设计好好改进, flutter是一个优秀的跨平台app开发框架, dart的唯一用武之地就是服务这个框架. 这相对于js不同, js的核心标准不变, 但是需要对不同设备和环境扩展api, 比如浏览器前端开发app时, 他会学习一些新的东西来挑战和质疑已经存在的知识, 这一定程度上会造成知识混乱. 但是开发app不同, 运行环境相对单一, 目的明确, 我们只需要知道开发网页需要使用js, 开发app需要使用flutter, 这就够了, 这就是最优解. 希望2023年flutter能够干掉reactnative一统跨平台app天下.
如果有人问我跨平台app推荐什么技术, 我会毫不犹豫的说flutter, 尽管它有太多缺点.
我们再来说说前端框架吧, 今年我是react黑粉, 同样的也是vue3黑粉, 我使用vue3已经快2年多了, 2020年我就写了vue3教程, 甚至在去年我写了一部分vue3的电子书. 但是后来放弃了, 因为我对浏览器的东西渐渐失去了兴趣, 在上一家公司做了不少网页的东西, 让我意识到这对职业发展很不利. react难么, react-hook我学了一个小时就能写一个完整的线上应用, vue3难么, 我学了几个小时就能写教程. 不是我有多厉害, 是因为框架的团队太强了, 它们把“开发者养懒了, 它们是技术罪犯”
当一个技术变得特别简单的时候, 就开始卷了, 它们试图让开发者写更少的代码能完成更多需求, 如果要找到一份心仪的工作, 你不得不去卷源码, 就和java一样, 你得在面试的时候准确说出jvm源码中的函数名称, 有时候函数名称有几十个单词组成, 你能用标准的英语读出来, 就能震慑傻逼面试官. 前端已经快要和java一样卷了, 现代前端才存在多少年, java已经存在几十年, 可见前端的发展速度令人震惊, 我们如果要想在这个圈子有更多机会, 我们需要逃避, 要去更换赛道.
每一个框架都有或多或少的缺点, react hook无处不在的编程陷阱, 我今年开发在线ide, 就被react各种陷阱逼疯了, 同样的功能使用vue3能节约60%的代码; 反观vue3, 它的ts支持也是非常糟糕的, 如果没有vite, volar等优秀的插件生态, 我的第一框架首选也不会是vue3. 在今年有很多框架值得我们去关注, next, nuxt, astro, qwik, svelte, solidjs, preact.
- 如果你喜欢react的api设计和虚拟dom框架, 我强烈推荐你使用preact.
- 如果你喜欢极简干净短小的js代码运行在你的网站上, 推荐svelte
- 如果你喜欢island架构, 推荐你使用astro
- 想玩好全栈框架, 推荐nuxt3和nextjs, nextjs有swc, nuxt3有我的代码和强大的api引擎
vite是今年前端宠儿, 也是我的心头爱, 鱿鱼丝是一个才华横溢的程序员, 他是一个天才, 有着独到的想法, 而且vite的团队技术都很顶尖, vite已经4.0了, 我有空一定要拜读一下vite的源码, vite在2023年的优化空间有限, 因为vite本质上的原理没有特别复杂, 它进一步的简化了开发模式和优化程序体验, 我们摆脱了webpack噩梦. 得益于插件系统, 理论上vite可以基于nodejs做出很多前端领域的扩展, 甚至很多nodejs框架也在集成vite.
在今年稍晚的时候, turbo团队发布了它们的构建工具turbopack, 我对于turbopack持乐观状态, 但是我不认为它能打败vite, vite的生态很庞大, 在前端界中生态就是王道, 就像npm一样, 再如何吐槽npm/nodemodules设计, 它仍然是最网红的依赖管理工具. turbopack我们应该关注它的核心技术turbo, turbo团队还有一个作品是turborepo, 也是解决monorepo的杀手锏框架, 它们很擅长利用缓存来优化一个流水线, 并且在turborepo中甚至可以将缓存共享到ci/cd中 (我记得是, 看到过相关功能), 并且它们推出了云服务, 对于企业用户而言, 在构建一些微应用/大型代码库和框架, 是非常适合使用turbo团队的作品的.
顺便再说一句, 不要学习webpack了, 我们不得不承认, webpack是一个优秀的构建系统, 理论上它能完成任意复杂度的产物构建工作, 但是它太重了, 而且大多数团队缺乏webpack技术沉淀, 而且大多数业务不需要庞大构建系统支持, 我们甚至只需要vite的默认配置进行打包, 性能可能会更好.
在今年我写了一个nodejs框架, 也就是sword.js, 我很喜欢nodejs, 我很喜欢用nodejs来构建后端应用, 简单易用, 但是swordjs也是我最后一个用nodejs写的后端开源项目, 我不得不抛弃nodejs. 是因为nodejs和我之后的发展方向存在一定冲突, 但是我仍然在前端领域频繁用到nodejs, 如果你是一个前端, 请务必学习nodejs, 它太简单了, 使用nodejs你可以做任何很酷的事情. 但是请允许我介绍一下deno, 和我为什么要使用deno?
deno是一个js运行时, 它原生支持js和ts和wasm, 请记住wasm这个技术. deno是使用rust编写的, 它也是构建在v8之上的. 我之后的发展方向是区块链, 大量的要和rust打交道, 那么deno和rust中的联系就由wasm构建, 我可以使用我熟悉的语言(typescript/rescript)开发api, 并且我可以直接通过deno运行rust的合约代码, 因为rust/ink编译出的合约代码就是wasm. 而wasm是一种类汇编语言, 是一个低级语言, 它是作为c++, rust等低级语言的编译目标而出现的.
wasm的性能非常优越, 它已经在前端音视频领域取得了巨大成功, 也在浏览器中作为js的补充出现, 会比同等功能的js代码更加高效, 更接近本地速度运行, 所以我认为wasm是未来, 也是区块链的未来. 你可能会说nodejs也可以写区块链应用呀? 使用deno仅仅是因为它能直接运行wasm吗?
答案不是, 如果是半年前, 我不会使用deno, 因为我说过生态是最重要的, 但是deno在下半年兼容了nodejs绝大部分api, nodejs作者也是deno的作者, 他曾经说不会兼容nodejs的api, 但是现在为了生态做了妥协. 此时此刻, deno不仅可以兼容nodejs代码, 还可以直接运行ts和wasm, 还具有无与伦比的安全性和速度. 我铁定站它!
我们继续聊一下typescript, 我曾经是ts的狂热粉丝, 但是今年我把类型挑战的中等题刷完之后, 我改变看法了, 我不需要类型体操, 它在业务中使用的地方很少, 尽管它为重构和构建应用带来了很大的好处, 但是我们不得不承认, 要深入ts不是那么容易的, 而且在工作中我们只需要学会低阶的ts语法就可以给我们带来很大的开发益处: “给开发者良好的类型提示, 消除隐藏错误”
但是我们有没有想过, 就算写出花一样的ts代码, 对应用性能和速度有任何益处吗? 答案是没有的, 编译出来的js仍然可能存在性能和类型错误, 因为超级多的人喜欢any, 和关闭类型错误提示; 我们需要一个语言在特殊时候代替typescript, 我认为是rescript, 它可以编译性能更好的js代码, 具体的好处我就不介绍了. deno + rescript + rust/ink + flutter将是我进入区块链的理想前后端技术栈
关于开源, 我今年大部分时间都在写我的swordjs框架, 目前可以跨nodejs server和unicloud nodejs端, 有着优秀的ts运行时校验等等功能, 结合vscode插件和编译器, 无论在开发还是生产环境, 都有畅快的开发体验, 实际上它拉高了nodejs后端api应用开发速度.
结束
就写这么多, 希望大家2023年顺利, 下面是我2023年的发展方向, 希望给大家一些启发:
- 进入区块链行业 (重要)
- 重新审视所有技术栈, 对技术栈进行换血 (重要)
- 学会rust和deno以及rescript相关技术栈 (重要)
- 学英语
- 对程序语言理论和编程基本功进行针对训练, 比如看一些相关书籍, 编译器知识, 程序语言, 算法等 (重要)
- 有空学习街舞
- 渐渐放弃前端, 尤其是常见的业务, 比如浏览器, 小程序方向
- 加强后端语言学习, 比如go, 我对go比较青睐, 它写api真的挺不错的
- 看情况适当了解docker, k8s
- 对设计和产品领域持续输出新的idea, 适当做一些练习和创业项目