# Jinmokai's blog - Full Content Document (llms-full.txt) > I am a web developer based in Hangzhou, China. I write about frontend development, daily life, and local history research. 我是一位在中国杭州的开发者,记录前端技术、日常生活和地方历史考究。 Author: Jin Mokai Website: https://www.jinmokai.tech Generated at: 2026-08-09T13:31:41.118Z Total posts: 29 This file contains the full content of all published posts for AI retrieval and analysis. ============================================================ ## 周末小记 **URL:** https://www.jinmokai.tech/blogs/weekly-note-08-09 **Published:** 2026-08-09 **Updated:** 2026-08-09 **Tags:** 随笔 **Summary:** 杭州刮台风了,写写最近近况 周六(2026-08-08) 最近两个周末都刮台风。周六去了西溪银泰,看了看自行车。 目前中意的是一辆迪卡侬 LD900。 你肯定会好奇,我为什么会选择迪卡侬?其实原因很简单:我想选择一个售后服务好的品牌,而且迪卡侬离我住的地方也不远,所以就选择了它。 我早期是做售后系统的,因此很喜欢售后服务好的品牌,无论是电子产品还是其他产品。 这是一辆非常漂亮的车,也符合我日常通勤和长途骑行的想法。 好了,书归正传。 周六逛完迪卡侬,发现蜜雪冰城总部大楼前有一个免费吃西瓜的活动。 很不错的公益行为,点赞👍 吃完西瓜,来到附近的商圈,发现这里有一个打麻将的活动,好像前三名可以获得 1—10g 黄金。 围观的人挺多,但我有点看不懂,只是简单跟风看了一会儿。 到了下午两三点,我还没吃饭,于是随便找了一家叫“兰湘子湘菜小炒”的湘菜馆。 味道不错。我还注意到一位很年轻的服务员,看上去大约 18 岁。她很像我早期初入社会时认识的一位学姐,无论是气质还是外貌,都给人一种内敛、低调、不张扬,但又充满活力的感觉。 最近半年,我发现自己好像得了一种“病”:每到周末都想出去走走,不能一直待在家里。最多待半天,如果待满一天,身体就会非常难受。 刚毕业的时候还好,我还能忍受;25 年也还行,到了 26 年就不太行了。待在家里会觉得难受,哪怕只是出去走一会儿,也比待在家里强。 而且,我在家学习的效率还不到在图书馆的一半,很难静下心来。 希望自行车到了之后,我能有更多时间提高自己的专注力吧! ------------------------------------------------------------ ## 千岛湖游玩 **URL:** https://www.jinmokai.tech/blogs/qian-dao-hu-play **Published:** 2026-07-25 **Updated:** 2026-07-25 **Tags:** 生活日记, 随笔 7月16日,第一天 公司团建,一大早从杭州出发,经过两个小时的车程抵达酒店。\ 到达千岛湖,午餐自然少不了当地特色鱼头。鱼肉鲜嫩,汤汁浓郁,味道很不错。午后天气炎热,我们没有外出,在酒店简单处理了一会儿工作。不知不觉间,天色渐晚。 \ 晚餐后,天空泛起一抹红晕,与眼前的湖光山色相映成趣,别有一番韵味。 认真拍照的女生 同事互拍三连奏 \ 饭后,我和同事相约夜骑,在周边随意逛了逛,途经千岛湖广场和骑龙巷。由于时间较晚,街上的活动不多,行人也比较少。简单转了一圈后,我们便骑车返回酒店,为第二天的环湖骑行养精蓄锐。 7月17日,第二天 吃过早饭 这一天的重头戏,是和同事一起挑战千岛湖著名的环湖骑行路线,全程约50公里。\ 我们一早便出发了。此时阳光还不算强烈,气温适宜,骑行在绿道上格外舒适。一路经过千岛湖大桥,湖水始终相伴左右,沿途风景宜人,令人流连忘返。\ 遇到值得记录的打卡点,我们便停下来拍照留念;看到喜欢的风景,也会放慢脚步,用镜头将它们一一珍藏。一路骑骑停停,虽然速度不快,却真正感受到了环湖骑行的乐趣。 \ 骑到大约25公里处时,随身携带的水已经见底。我们来到一处已经停用的游客服务区,幸运地从保安师傅的冰箱里买到了几瓶冰水。到了中午,天气逐渐炎热起来,体力也开始有些不支。 \ 好在途中发现一条小路可以骑到湖边。我们来到千岛湖岸边,用湖水简单洗了洗手臂和脸。湖水虽然有些温热,但对当时又累又热的我们来说,已经是难得的消暑神器。 \ 下半程经过一段隧道上坡时,我的两侧大腿内侧突然抽筋。只能咬牙坚持骑到隧道出口,再停到路边休整。我不断按压、放松腿部肌肉,大约过了十分钟,身体才逐渐缓过来。 最后一段路程,我和同事都已经又饿又渴。快到酒店时,我们在路边找到了一家颇有名气的饭店,名叫“淳鱼故事”。店里的甲鱼味道不错,其他菜品也可圈可点。下次再来千岛湖,这家店值得再次光顾。 \ 50公里的环湖骑行,有清晨的凉爽、沿途的湖光山色,也有正午的炎热、缺水和抽筋。虽然过程并不轻松,但那些骑过的路、吹过的风,以及镜头里留下的风景,都让这段旅程变得格外值得。 ------------------------------------------------------------ ## 《鄱阳记》辑佚 **URL:** https://www.jinmokai.tech/blogs/poyang-place-name-record **Published:** 2026-03-08 **Updated:** 2026-03-08 **Tags:** 鄱阳, 历史考究 研究鄱阳地方史过程中,发现了一本古籍《鄱阳记》 不过可惜的是,正文内容都已经缺失,只有在其他文献中又零零散散的记载,本文将列出辑佚的内容,以及《鄱阳记》相关背景。 《鄱阳记》目前已知有三个成书记录,分别是 1. 南齐时期(公元479年—502年)刘澄之《鄱阳记》 2. 隋唐之际(公元6世纪末至7世纪初)的徐湛《鄱阳记》 3. 唐代中后期(公元8世纪中叶以后)王德琏《鄱阳记》 下面我列举出关于《鄱阳记》辑佚内容,方便后续考究有文献支持。 刘澄之《鄱阳记》 背景: 三部书中最古老的一部,主要记载南北朝及以前的传说。 《常用典故辞典》第334页,记载“忘夫石” 《常用典故辞典》出自上海世纪出版股份有限公司 2013版 ISBN: 978-7-5326-3865-9/H.548 鄱阳西有望夫冈,相传为陈明妻梅氏望夫之处。 查阅《民俗文学与民俗文献研究》也有关于上述记载 下图为《民俗文学与民俗文献研究》第76页 上图说,《鄱阳记》有四位作者,当最新论文表示,实为三人。可以参考 "《鄱阳记》辑佚文献考" 《民俗文学与民俗文献研究》2008年12月出版 ISBN: 978-7-80752-272-0 《鄱阳记》辑佚文献考 该文来自“图书情报研究2021年第一期 《鄱阳记》辑佚文献考”论文 二、 徐湛《鄱阳记》(隋唐之际) 背景: 长期被误记为“徐谌”或“徐濯”,论文已定名为徐湛。 石藏山钟乳穴:“(石藏山)中有钟乳十数穴。” 考证:此条原被刘纬毅误置于王德琏名下,实为徐湛所记。位置在原浮梁与鄱阳交界。 尧山起源: “北有尧山,尝以尧为号。又以地饶衍,遂加‘食’为‘饶’。” 背景:这是“饶州”这一地名最早的文字来源之一。 三、王德琏《鄱阳记》 背景: 记录了唐代鼎盛时期的鄱阳,人文气息最重。 方言记录:“人语有吴楚之音。” 价值:这是关于鄱阳方言属于“吴头楚尾”交汇区最早的文献实录。 小雷岗:“(小雷岗)今小雷岗是也。” 独角山与奇兽:“(独角山)有独角兽居之。” 注:论文纠正了前人将“兽”误作“善”的字讹,这对研究地方生物志很有帮助。 “白云亭在县西南,旁对干越亭而峙焉。跨古城之危,瞰长江之深。隋州刺史刘长卿题诗曰:‘孤城上与白云齐’。因以白云为名。” - “清湾在县东南七里。隋开皇中,太守梁文谦莅官清洁,取此湾水以自供,人思其德,号为清湾。” * “怀蛟水,一名孝经潭,在县南二百步。江中流石际有潭,往往有蛟浮出时伤人焉……刺史张栖贞以人之行莫大于孝,悬《孝经》标杆上赏之,而人知劝。俗号为怀蛟水,或曰孝经潭。” 未知指定作者辑佚 《岁时民俗与古小说研究》第262页 《鄱阳记》云:“尧山不没,人居避水” 《岁时民俗与古小说研究》 2004年2月天津古籍出版社出版 ISBN: 7-80696-023-6 《辛弃疾评传》第80页也有记载 1998年12月出版的 《辛弃疾评传》 ISBN: 7-305-03274-3/B.206 ------------------------------------------------------------ ## 饶州游记 **URL:** https://www.jinmokai.tech/blogs/饶州游记 **Published:** 2025-11-25 **Updated:** 2025-11-25 **Tags:** 历史考究, 鄱阳 **Summary:** 该刊出自民国刊物《中学生文艺季刊》1936年2卷1期。 饶州游记: 出自1936年中学生文艺季刊第一期第二卷 作者:利贞 原文:饒州 暨南大學 利貞 一 提到江西我想誰都會聯想到鄱陽湖的;提到鄱陽湖更沒有一人不想到饒州的。饒州在江西的北部,鄱陽湖的東邊,地勢壯麗,風景幽美,無論拿那一方面來講,在江西都算是一個重要的城市。單就贛北而言,那更不消說是數一數二的州府。 饒屬七縣之一的鄱陽縣,就是在饒州。鄱陽在饒屬七縣中,面積最廣,人口最多,出產更非其他各縣所能比的。所以在過去都陽縣有「銀鄱陽」之雅號,一班官僚們個個都想鄱陽這個地方。雖然近年來世界鬧着經濟恐慌,國內高喊農村破產,而鄱陽比較上還算是好的 二 饒州只有正橫兩條街,正街與通景德鎮和鄱陽湖的饒河平行,橫街恰與正街垂直。正橫兩街成T字形,相較處在東門口,在饒州算是頂熱鬧的一端街。正街兩端比較冷靜,不過上邊比較下邊又要好些,因為郵政局、電報局、輪船碼頭都在上邊。 橫街無大商店,飲食雜貨舖居多。最有趣味的一件事,就是橫街被九條巷衕分為九段,而那九條卷所的命名,是從正街那邊後序數去叫一條巷,二條巷,...九條巷。每條巷都有一家麵館,這不多是饒州人歡喜喫湯麵餛飩的鐵證。饒州人待客亦如此,來了一位比較親近的客人,他老是叫一碗麵或餛飩來做點心。 饒州的糕餅南貸店特別多,最奇怪的就是店主和店員以安徽省的徽州人佔多數。饒州的糕餅做得很好,如酥糖、雞蛋糕、牛皮糖、葱酥,都是很有名而且富有鮮味的食品,不但在江西很出風頭,就是在長江各口岸,也是怪受人歡迎的。 銀魚烤肉是如何的鮮而有味呵!清蒸白蓮是多麼的甜而帶補呵!這兩樣名貴的國產,都是饒州的土產,產劃很豐富,價格也並不高。 三 饒州的重要教育機關和學校都在城內,而學梭更多數集中在府門口一帶。 饒州本來有三個中等學校,去年因為省立陶業學校搬到九江出去了,所以現在只有兩們了。 1.省立初級中學 這個中學的歷史很久,在民國初年開辦的。起初是叫饒州中學(x勘誤),後來改名爲省立第五中學。民十以前的第五中學,在江西是非常有名的,所出的人材也很多,那時正與現在的省立一二中並駕齊驅。民十以後,因屢經波折且校長一職常常易人,於是辦理方面慢慢地不行了。民十五年革命軍到江西,民十六便改名為省立鄱陽中學,還沒有到一年,教育廳改組,於是又隨之而改名為省立第十中學。自民十七到民廿二該校比較安定了一下,去年又改名為現在之省立鄱陽初級中學。一個學校在三十年之間,經過許多的變化,成績當然是不會好的,不過現在該校教員非常努力,且學生多爲十五歲以下之活潑青年,前途的偉大光明,並不是現在所能限量的。 2.饒屬共立農村師範學校 這個學校本來是叫芝陽師範學校,去年才改為農村師範的。該楼設備欠完全,辦理也不能算是完善,這一方面由於經費不充足,同時教員也聘請得太隨便了。 總州城內共有兩個完全小學,一男一女,兩校都辦得很有成績,在過去第一男小比第一女小要,好些這個由升學的學生方面可以觀察得出來。 其他初級義務小學很多,但是辦得很有生氣的沒有幾處。不是學生人數太少,便是教員老是不到校,平心而論饒州小學教員的薪金實是太不能使人滿意了,尤其是義務小學,每月連伙食費只有十二元,如果這班教員們要靠這筆薪金去供妻養兒,那他的妻兒是餓死無疑的。這樣難怪饒州的義務小學辦不好,教員不安心去教書,乃是一種必然的結果。 四 何處是饒州的風景線呢?毫無疑義的是鄱陽湖的遠景。然而要遠觀鄙陽溯的風景,我們不能不提到芝山。 芝山差不多是饒州唯一的名騰,凡是到過饒州的人,都得去芝山一遊。芝山本身是一座荒無的山,除了無數的大小坟墓,什麼也找不出。然而她為什麼這樣有名呢?原因是山頂上有一個石亭,坐在石亭裹,可以看見一片汪洋的鄱陽湖。每當夏秋之交,一片湖水,遠接着天邊,船舶往來,點綴成美景,尤其是那西岸的若影若現的廬山,更是令人愛慕。山的脚下近年來新建了一座廟宇,名芝山寺。寺內清潔幽靜,權宜閱書修學,炎夏天氣,好學者多往之。 東湖也是饒州的名勝,不過東湖在饒州,沒有西湖在杭州那樣受入愛戀,因爲她沒有什麼可愛和值得留戀的地方。僅僅在湖的中心有一個浮舟寺,由一條小提通過去,一路高木蔽天,湖水倒映,這到有幾分意味。東湖的水很淺,鲫魚和菱角很多,一般人所以還不忘卻東湖,恐怕就在這鯽魚和菱角身上吧! 風雨山和雙港都是饒州人常去遊玩的地方,尤其是學生們。兩個地方離饒州都只有十里路遠。風雨山森林很茂盛,竹林深處,有古廟一所,廟內甚大,和尚招待遊人非常客氣,不過有時候,他是要請你捐幾文的。雙港近鄱陽湖,為往來船舶必經之路。有一小山突出河中,山上有一九層之塔,是沒有頂的。 简体版: 饶州 暨南大学 利贞 一 提到江西我想谁都会联想到鄱阳湖的; 提到鄱阳湖更没有一人不想到饶州的。 饶州在江西的北部,鄱阳湖的东边,地势壮丽,风景幽美,无论拿那一方面来讲,在江西都算是一个重要的城市。 单就赣北而言,那更不消说是数一数二的州府。 饶属七县之一的鄱阳县,就是在饶州。 鄱阳在饶属七县中,面积最广,人口最多,出产更非其他各县所能比的。 所以在过去都阳县有「银鄱阳」之雅号,一班官僚们个个都想鄱阳这个地方。 虽然近年来世界闹着经济恐慌,国内高喊农村破产,而鄱阳比较上还算是好的 二 饶州只有正横两条街,正街与通景德镇和鄱阳湖的饶河平行,横街恰与正街垂直。 正横两街成T字形,相较处在东门口,在饶州算是顶热闹的一端街。 正街两端比较冷静,不过上边比较下边又要好些,因为邮政局、电报局、轮船码头都在上边。 横街无大商店,饮食杂货铺居多。 最有趣味的一件事,就是横街被九条巷同分为九段,而那九条卷所的命名,是从正街那边后序数去叫一条巷,二条巷,...九条巷。 每条巷都有一家面馆,这不多是饶州人欢喜喫汤面馄饨的铁证。 饶州人待客亦如此,来了一位比较亲近的客人,他老是叫一碗面或馄饨来做点心。 饶州的糕饼南贷店特别多,最奇怪的就是店主和店员以安徽省的徽州人占多数。 饶州的糕饼做得很好,如酥糖、鸡蛋糕、牛皮糖、葱酥,都是很有名而且富有鲜味的食品,不但在江西很出风头,就是在长江各口岸,也是怪受人欢迎的。 银鱼烤肉是如何的鲜而有味呵! 清蒸白莲是多么的甜而带补呵! 这两样名贵的国产,都是饶州的土产,产划很丰富,价格也并不高。 三 饶州的重要教育机关和学校都在城内,而学梭更多数集中在府门口一带。 饶州本来有三个中等学校,去年因为省立陶业学校搬到九江出去了,所以现在只有两们了。 1.省立初级中学 这个中学的历史很久,在民国初年开办的。 起初是叫饶州中学(x勘误),后来改名为省立第五中学。 民十以前的第五中学,在江西是非常有名的,所出的人材也很多,那时正与现在的省立一二中并驾齐驱。 民十以后,因屡经波折且校长一职常常易人,于是办理方面慢慢地不行了。 民十五年革命军到江西,民十六便改名为省立鄱阳中学,还没有到一年,教育厅改组,于是又随之而改名为省立第十中学。 自民十七到民廿二该校比较安定了一下,去年又改名为现在之省立鄱阳初级中学。 一个学校在三十年之间,经过许多的变化,成绩当然是不会好的,不过现在该校教员非常努力,且学生多为十五岁以下之活泼青年,前途的伟大光明,并不是现在所能限量的。 2.饶属共立农村师范学校 这个学校本来是叫芝阳师范学校,去年才改为农村师范的。 该楼设备欠完全,办理也不能算是完善,这一方面由于经费不充足,同时教员也聘请得太随便了。 饶州城内共有两个完全小学,一男一女,两校都办得很有成绩,在过去第一男小比第一女小要,好些这个由升学的学生方面可以观察得出来。 其他初级义务小学很多,但是办得很有生气的没有几处。 不是学生人数太少,便是教员老是不到校,平心而论饶州小学教员的薪金实是太不能使人满意了,尤其是义务小学,每月连伙食费只有十二元,如果这班教员们要靠这笔薪金去供妻养儿,那他的妻儿是饿死无疑的。 这样难怪饶州的义务小学办不好,教员不安心去教书,乃是一种必然的结果。 四 何处是饶州的风景线呢? 毫无疑义的是鄱阳湖的远景。 然而要远观鄙阳溯的风景,我们不能不提到芝山。 芝山差不多是饶州唯一的名腾,凡是到过饶州的人,都得去芝山一游。 芝山本身是一座荒无的山,除了无数的大小坟墓,什么也找不出。 然而她为什么这样有名呢? 原因是山顶上有一个石亭,坐在石亭裹,可以看见一片汪洋的鄱阳湖。 每当夏秋之交,一片湖水,远接着天边,船舶往来,点缀成美景,尤其是那西岸的若影若现的庐山,更是令人爱慕。 山的脚下近年来新建了一座庙宇,名芝山寺。 寺内清洁幽静,权宜阅书修学,炎夏天气,好学者多往之。 东湖也是饶州的名胜,不过东湖在饶州,没有西湖在杭州那样受入爱恋,因为她没有什么可爱和值得留恋的地方。 仅仅在湖的中心有一个浮舟寺,由一条小提通过去,一路高木蔽天,湖水倒映,这到有几分意味。 东湖的水很浅,鲫鱼和菱角很多,一般人所以还不忘却东湖,恐怕就在这鲫鱼和菱角身上吧! 风雨山和双港都是饶州人常去游玩的地方,尤其是学生们。 两个地方离饶州都只有十里路远。 风雨山森林很茂盛,竹林深处,有古庙一所,庙内甚大,和尚招待游人非常客气,不过有时候,他是要请你捐几文的。 双港近鄱阳湖,为往来船舶必经之路。 有一小山突出河中,山上有一九层之塔,是没有顶的。 刊载信息与发表时间 通过时间线溯源,该文确定刊载于民国时期的刊物《中学生文艺季刊》1936年第2卷第1期。 2. 撰稿时间推论(大概率:1933年) 通过对文章内部时间线索的细致分析,我们推断该文的撰稿时间极有可能是在 1933年。 🔑 关键推论一:鄱阳初级中学更名线索 - 原文引用: “自民十七到民廿二,该校比较安定了一下,去年又改名为现在之省立鄱阳初级中学。” - 历史查证: “省立鄱阳初级中学”这一校名实际使用时间为 1932年至1939年。 - 推论: 既然作者提及“去年”改名为此,结合校名启用年份,“去年”即指1932年。因此,该句话的撰写时间应为 1933年。 🔑 关键推论二:饶属共立农村师范学校更名线索 - 原文引用: “饶属共立农村师范学校,这个学校本来叫芝阳师范学校,去年才改为农村师范的。” - 历史查证: 芝阳师范学校变更为农村师范的大致时间在 1931年至1935年间。 - 推论: 虽然该信息具体年份难以单独确定,但若与前一条“鄱阳初级中学”的线索(推断为 1933年)相互印证,则此处的“去年”也应指 1932年,进一步强力支持文章撰写于 1933年 的判断。 综上所述,结合两处关于学校更名叙述及其准确的历史背景,我们得出结论:该文的撰写时间极有可能是在 1933年。 3. 历史校名勘误与更正 针对原文中对学校初名的描述,经查阅地方志资料,我们予以更正: - 原文描述: 在民国初年开办的。起初是叫饶州中学 - 官方更正: 经查阅地方志,该校的全称应为:饶州府中学堂 或 饶州府中学校。 ------------------------------------------------------------ ## 佛山出差记录 **URL:** https://www.jinmokai.tech/blogs/佛山出差记录 **Published:** 2025-07-06 **Updated:** 2025-07-06 **Tags:** 随笔 **Summary:** 由于项目需要,在佛山驻场一周,第一次坐飞机,第一次出差有感记录一下 出发佛山 由于项目需要,在佛山驻场一周,第一次坐飞机,第一次出差有感记录一下 六月二十九号,周末下午从杭州出发到佛上大约两小时 以前没有做过飞机,没有过学习需要和工作需要,这是第一次坐飞机难免有点紧张和兴奋,分享几张飞机照片 从高空看下面无不感叹祖国的山山水水,更加热爱自己生活的地方 刚落座,飞机平稳,空姐推出餐车,我要了一份牛肉面喝一杯果汁,味道不错,面相也还行! 牛马生活开始 一落地打车到酒店,有同事在接应,放下行李,聚了一餐,吃了一下这里特色叫什么:醉鹅 整个一周大部分都是吃吃吃,工作工作,比较规律,早上就在酒店吃早饭,中午和下午都在公司食堂吃饭, 14元满满一大碗,周三和周五我们出去聚餐,吃顿好的 酒店早餐 {/ 每天都加班到好晚 /} 每天喝咖啡续命 出来聚餐啦,甲方请客吃饭 餐厅拍摄一角 鱼生 早就听说过广东这边的鱼生,用的还是淡水鱼,怕有寄生虫,只是浅浅的吃了一片,我和同事都没在尝试 没怎么吃饱,回酒店点了这里的特产,双皮奶 芒果双皮奶 味道有点鸡蛋味,芒果还是很甜的,味道不错! 继续吃吃吃,食堂种类很少,吃的有点想吐,烧腊吃到吐!回杭州长时间不会再吃了 来广东喝了一下凉茶,一位好喝没想到味道和中药一样,不太好喝 周五聚餐,这家味道不错,人爆满,叫什么“虾丙”。 周五晚上吃完饭,第二天周六下午坐飞机回杭州 说点什么 最后总要说点什么,出差中很累但胜在第一次整体下来还是比较能接受,很新奇的体验! ------------------------------------------------------------ ## 鄱阳关帝庙一考 **URL:** https://www.jinmokai.tech/blogs/鄱阳关帝庙一考 **Published:** 2025-06-21 **Updated:** 2025-06-21 **Tags:** 历史考究, 鄱阳 **Summary:** 一张图片引发对鄱阳关帝庙的考究.... 鄱阳关帝庙一考 我为什么会考究这个,我其实不是很懂寺庙文化。我是在搜索鄱阳中学的时候发现一段话,有一所关帝庙在鄱阳中学旁 我就很惊奇,我在高中就读三年,怎么没听说过以及没看过呢? 仔细想想其实我好像对有一所寺庙有点印象,每次放学,走这条小巷都会看到一个小庙,但是不知道供奉的是谁,就没注意。 放在一个民房内还有人供奉香火之类,每次走过这里香火气息还是非常浓重的。 1947鄱阳县城厢图 那这里是关帝庙吗?我并不确定。 通过查阅《文化鄱阳 建筑篇》第200页说: 关帝庙有三座,一座在朝天门外的北关,一座在后山队的茅园里,还有一座在十八坊,不但规模小,而且时间不长。 哦,可能网上的信息大概率说的就是十八坊的,因为鄱阳中学距离十八坊还是很近的 但是鄱阳中学旁边的庙宇又是什么呢?没有信息,只能后续实地探访了。 后来在网上找到一张图片上面描述为 鄱阳关帝庙水灾难民收容所 这个关帝庙在哪里呢?我不确定. 翻开1947年鄱阳县城厢图,可以找到在茅园里。仁慈医院北面有一个关帝庙 茅园里关帝庙 翻开2003年版本鄱阳县志说 关帝庙 在北关外,祀关圣帝君。 元至正毁于兵。明洪武元年(1368),方丑仁自闽聚众数万攻鄱阳城,知府陶安率兵伏庙左榛莽中,适江西援兵至,庙中兵出关外合击,方丑仁败。后陶安以为关帝显圣,增建殿寝、门庑等,颇具规模。 清咸丰三年(1853)毁,光绪十二年(1862)重修。庙址后改为北关小学校址。 2003年庙址迁至北关小学北100米处。 现鄱阳县保育院 那么回到最初的问题,水灾难民的关帝庙是哪个地方呢?茅园里的关帝庙还是北关的呢又或是十八坊? 我来解答一下: 鄱阳水灾难民收容的地方网上倒是有一点资料,说是是1931年在茅园里的关帝庙 我通过查阅《鄱阳县志》找到了1931年确实发生过水灾,原文描述是: 民国20年(1931)4~5月,连续大于,江河洪水急涨。7月又连续大雨20余天。长江水倒灌。沿江滨湖一片汪洋。圩提尽倒,淹没农田面积15333.4公顷,灾民29.2万人,死亡1.1万人。 从上面这段话来说可见网上信息并非空穴来风,只是图片全是水印且无原件公开,导致考究起来有些困难。 以后有机会可以实地探访,去询问当地人了解其背后的历史! ------------------------------------------------------------ ## 定时器的技术方案 **URL:** https://www.jinmokai.tech/blogs/定时器的技术方案 **Published:** 2025-03-29 **Updated:** 2025-03-29 **Tags:** javascript 前言 项目需求中,如果遇到需要使用定时触发某个任务的时候,我们一般会使用 setTimeout 或者 setInterval 来实现,但是这两种方法都有一些问题,比如 setTimeout 在某些情况下可能会因为浏览器的渲染导致任务执行时间不准确,setInterval 在某些情况下可能会因为任务执行时间过长导致任务堆积,所以我们需要一种更可靠的方式来处理定时任务。 第一个版本 如果直接使用setInterval,这个方法是最笨的方案,会出现越来越不精准情况 我们可以想到最简便的优化方案就是每次执行完毕之后重新在执行一次写一个递归即可,这样能够缓解一点随着任务执行时间边长,任务堆积的问题 function loopIntervalHandler() { let interval; function execute() { console.log('...'); clearInterval(interval); interval = setInterval(execute, 1000); } interval = setInterval(execute, 1000); return () => { if (interval) { clearInterval(interval); } }; } const stopLoop = loopIntervalHandler() document.addEventListener('beforeunload', () => { stopLoop() }) 第二个版本 通过使用 requestAnimationFrame 来实现定时任务,其实 requestAnimationFrame 这个就单纯该问题解决方案和第一个优化版本一样,本质都是通过递归执行,本质还是并没有改变随着任务执行,任务堆积问题,只是缓解与第一版一样。如果一定要说一个特别的地方就是可以通过 requestAnimationFrame 来解决页面不可见的时候,定时任务不执行的问题, requestAnimationFrame 天然支持。 function loop(fn, interval = 1000) { let lastTime = performance.now(); let requestInstance function animate(currentTime) { if (currentTime - lastTime >= interval) { fn(); lastTime = currentTime; } requestInstance = requestAnimationFrame(animate); } requestInstance = requestAnimationFrame(animate); return () => { if (requestInstance) { cancelAnimationFrame(requestInstance); requestInstance = null } } } function animationHandler() { console.log("execute...") } const stop = loop(animationHandler, 1000); document.addEventListener('beforeunload', () => { stop() }) 第三个版本 js是单线程的,采用web Work技术方案来把定时任务放到web worker中执行,这样就不会影响到主线程的渲染,但是web worker中不能使用DOM操作,所以这个方案只适用于一些不涉及DOM操作的任务。 stop const worker = new Worker('./work.js'); let lastTime worker.onmessage = (event) => { let diffTime = event.data - lastTime lastTime = event.data console.log(diffTime) }; worker.onerror = (error) => { console.error('Worker 错误:', error); }; lastTime = performance.now() worker.postMessage('start'); // 如果需要停止 Worker const stopWorker = () => { worker.postMessage('stop') worker.terminate(); } document.addEventListener('DOMContentLoaded', () => { const btn = document.querySelector('button') btn?.addEventListener('click', () => { stopWorker() }) }) work.js脚本内容 let timer = null; self.onmessage = (event) => { if (event.data === 'start') { if (timer) { clearInterval(timer); } timer = setInterval(() => { self.postMessage(performance.now()); }, 1000); } else if (event.data === 'stop') { if (timer) { clearInterval(timer); timer = null; } } }; 结尾 以上都是我能想到的一些解决方案,如果还有更优的方案,欢迎补充交流。 ------------------------------------------------------------ ## 表恩村一考 **URL:** https://www.jinmokai.tech/blogs/表恩村一考 **Published:** 2025-03-19 **Updated:** 2025-03-19 **Tags:** 历史考究, 鄱阳 **Summary:** 鄱阳莲湖表恩村一考,主要参考来自《文化鄱阳》名胜篇,本人未做过深考究,如有错误,欢迎指正。 表恩村一考 表恩村在鄱阳莲湖南端,说到表恩之前不得不说一下“莲湖”, 莲湖古属立德乡,旧志转引古籍地理书————北宋乐史所著《大平寰宇记》“鄱阳县”条:在立德乡,去城西四十里彭蠡湖中,望之如荷叶浮水面,周翘百里。”这种称谓很形象,漂在鄱阳湖上的荷叶,后来改称莲湖。 莲湖开始有人居住,据说在唐之前。莲湖中部有一块高地,当地人称作海拔32米多,名日美女献羞,实际上只是一个大土丘。在它东南有一个土丘。海拔28米多,人们仍称作山,名曰样山。在两山之间,有一个村落,名宋家嘴,这是当年这片土地上的第一个村庄。虽然这个村子在唐代便无人居住,但留了人居的历史。 从古至今,一直人口稠密,而且居住历史比较悠久的村落,那当然非莲湖朱家莫属。据该村的《朱氏宗谱》介绍,唐文宗大和八年(834),因黄巢起义,兵扰攘,该村始祖朱禹二奉父朱介之命出外谋生。朱禹二从南京乌衣巷出发,入湖口、下鄱阳,到此见山川秀美,遂择地定居下来,距今已有1177年,繁衍子共四十二代。从此以后,这里人便越来越多,因此成为莲湖的第一大村。 莲湖有个地点叫表恩,是个湖岛,古称“表恩山”。 旧志曰:“表恩山在立德乡,去城西南四十里,滨彭蠡。相传,宋有徐姓者居此,献龙马,封池州太守,故名。” 它在莲湖的最南端,涨水时是个孤岛。志中所说的表恩徐氏来自奉新,奉新徐氏,为徐稚的后裔。 徐稚(97-168),字儒子今丰城市白土镇隐溪村人,东汉隐士,为江西历史上第一位著名贤士。他一生博学多才,淡泊自守,官多次征召,皆不出,有“南州高士”之美称。 相章太守陈普慕其才品面特为其专设一榻,去则悬之,于是便有了王勃《滕王阁序》中“人杰地灵,徐孺下陈蕃之榻”这个典故,千古流芳。今南昌名胜孺子亭原是徐孺子垂钓之处,为豫章十景之一。徐稚下传四十三世有徐复,字仲晦,又字冲晦,南宋初年从高安米岭徙居奉新,被奉新六溪徐氏尊为一世祖。 在表恩,有村民收藏明朝本村人徐晟的《寿藏志》(墓志铭)。志铭额题为“处士徐公寿藏志铭”。青石长约1.2米,宽约0.7米。 铭由壬子(明宣德七年即1432年)科乡贡进士、任湖广永州府东安县儒学司训的徐行撰文,前山南东道节度使巨容公裔孙、儒生刘中道书丹。 徐行在志中说及此事:“(徐晟)先世,本洪州丰城隐溪人也,家殷族盛,大有显宦,官至饶州。子孙遂家于番之表恩,由唐而至宋。” 《鄱阳县志》的记载:“在高宗南渡之后,家殷族盛,产龙马之瑞,尝闻子思云,国家兴有祯祥,徐氏亦有祯祥之兆也。鼻祖六二公贡献于朝得宠天恩是马也。能符国家之兴又挑徐氏之盛,赠封以中宪大夫,授池州守,以表恩敕兹地。”上面说法与《鄱阳县志》的记载不谋而合。 墓志铭的主人徐晟,为献马给皇上的徐氏六二公的后人。志铭中说:“承祖父之遗训,涉猎书史,闻见过人,乡党朋友,莫不各尽礼意以遇之。处士尝从容语人曰:吾家先世,读书尚义,累拜名爵,我为人后者,岂可不知所自哉。由是楝宇昴新,田园充拓,扬中溢外,启后光前。于天顺戊寅年(1458)出谷百石,施入官仓,以济郡之贫乏者。”徐晟生于明洪武二十年(1387),他63岁时,儿子徐顼不幸去世;天顺二年(1458)71岁时,捐谷百担于官仓济贫。这份墓志铭是他死之前,即筑好坟墓时提前写好的墓志铭。其时徐晟就已过了百岁。 ------------------------------------------------------------ ## 第一次参加开发者大会-D2 **URL:** https://www.jinmokai.tech/blogs/第一次参加开发者大会-d2 **Published:** 2025-03-08 **Updated:** 2025-03-08 **Tags:** 生活日记 早上九点二十开幕式 阿里巴巴大门 签到打卡合照 背景墙自拍打卡 与师兄合照 与师兄合照 签名墙 简单再展会外部走了走,有很多公司推销自己的产品 通义灵码产品 在主会场外,大屏幕列出来不同的会场,以及各个会场的时间安排 大屏幕列举了很多会场,以及各个会场的时间安排 开幕式主要聊一些此次会议的相关主题内容 上午第三场:midscene.js 作者在讲解产品ai自动化 上午第四场:玉伯老师讲解"Ai时代我们都有活" 上午会议结束,和师兄一起在阿里简单吃个饭,物价不是很贵 阿里巴巴餐厅物价表 一份烧鱼、一份蒸蛋、一份豆腐塞肉、一碗米饭 饭后师兄在阿里闲逛一下,这里师兄来过好几次,我第一次来,给我讲解了阿里的一些地方,颇具意思。 回到主会场,我下午主要听“工程研发专场”,师兄主要关注AI在别的专场,就和师兄暂时分开。 师兄请客买了一杯拿铁 一杯拿铁 主会场一点到一点半有“996乐队”演唱,最后小孩子的曲目让人影响深刻。 996乐队 996乐队-特邀嘉宾小孩在唱歌 996乐队-特邀嘉宾小孩在唱歌 下午上半场,工程研发还是在主会场,第一场主要是最近一段时间字节推出的trae编辑器架构原理,说编辑器和AI大模型之间的一些交互以及底层设计等。 trae编辑器 trae编辑器架构设计 trae编辑器ui设计层面采用web component让组件样式隔离,提高开发效率 最期待作者:vuejs作者尤雨溪,聊聊前端统一工具链,以及前端工具链的未来发展 vuejs作者尤雨溪聊前端工具链 vuejs作者尤雨溪聊前端工具链的问题 尤雨溪聊当下公司void 0 产品架构设计 尤雨溪答观众提问 简单听了听Rstack 下午场的上半场结束,茶歇一会,简单吃了蛋糕和小水果 下午场的下半场第一场,去了跨端的场次,主要聊饿了么团队的mist3.0框架 最后一场,去了另外一个场,ai场,主要聊的是ssr的一些方案,没怎么理解,对ssr知识点深入不够 结语 大会结束,感觉收获满满,希望国内能有更多优秀的开发者大会! ------------------------------------------------------------ ## 怪异的body和html **URL:** https://www.jinmokai.tech/blogs/怪异的body和html **Published:** 2025-02-26 **Updated:** 2025-02-26 **Tags:** HTML, css **Summary:** 晚上公司同事找我说这里溢出的div为什么没有被overflow隐藏,我看了看,有点印象说你试试在html设置overflow... 晚上公司同事找我说这里溢出的div为什么没有被overflow隐藏,我看了看,有点印象说你试试在html设置overflow,然后同事尝试了一下,发现效果可以了。 我之前在《HTML并不简单》一书中看过这方面知识点,特此写篇博客留作纪念。 布局的怪异性 html { overflow: auto; / overflow: hidden; overflow: scroll; / } body { height: 100px; border: 20px solid red; background-color: black; overflow: hidden; div { height: 500px; background-color: yellow } } 布局问题 当body设置overflow: hidden时候,如果此时html并未设置overflow属性时。body的hidden并未把div溢出的高度隐藏掉,但是如果给html设置了overflow属性且非visible值时,此时body的hidden属性就会把溢出的div隐藏掉。 解释一下:如果html元素没有设置overflow属性,那么body元素设置的overflow属性值可以看成设置在html元素上,html元素的overflow属性生效高度至少为一屏。 所以如果你将div的高度设置大于一屏(html元素并未设置overflow属性),那么溢出的div将会隐藏,因为此时溢出隐藏的容器是html 下面例子:div高度设置大于一屏,此时溢出隐藏的容器是html html { overflow: auto; / overflow: hidden; overflow: scroll; / } body { height: 100px; border: 20px solid red; background-color: black; overflow: hidden; div { height: 5000px; background-color: yellow } } 背景的怪异性 html { background: linear-gradient(to bottom, #333, #666); } body { background-color: yellow; } 渐变色以8px条纹铺满 如果html没设置颜色,只给body设置颜色,且body没有高度,此时颜色跟随body的,为什么? 如果html设置了渐变色,且body没设置颜色,此时html背景色为什么8px平铺? 解释:当仅当给body设置背景色时,渲染效果和html元素设置背景色一直,由于html元素的背景高度至少一屏,因此背景色是铺满整个屏幕的 当html元素设置了渐变背景后,body元素的背景色按照普通html元素的背景规则渲染,由于高度为0,因此黄色背景消失 body元素存在一个margin: 8px默认样式,在css中存在"margin合并的"现象,会合并最大的margin ------------------------------------------------------------ ## 原型继承那些事 **URL:** https://www.jinmokai.tech/blogs/原型继承那些事 **Published:** 2025-01-12 **Updated:** 2025-01-12 **Tags:** javascript **Summary:** 原型继承那些事,零散的总结 原型链 我的理解: 一个普通对象的创建 const a = {},其本质是通过 Object 构造函数创建的,即 const a = new Object(),所以 a实例 的隐式原型(即\\proto\\)指向 Object 的原型(即Object.prototype), obj 的隐式原型(\\proto\\)指向的 Object 的原型(Object.prototype),这里特殊所以 Object.prototype 的隐式原型(即\\proto\\)指向的是一个 null 话说回来,Object 的构造函数其实就是 new Function() 创建的,所以 Object(即Function new出来的实例) 的隐式原型(\\proto\\)指向的是 Function 的显示原型,因为 Function 的原型(Function.prototype)其实是一个对象也就是 new Object 创建的,所以 Function 的隐式原型(\\proto\\)指向 Object 的原型(即Object.prototype) 特殊最后 Function 的构造函数的隐式原型指向的是 Function 原型 注意点: + 误区 1:Function.prototype 是 new Object() 创建的 + 纠正:Function.prototype 是由引擎内部直接创建的,它是一个空函数对象(虽然 typeof Function.prototype 返回 "function"),但它的隐式原型指向 Object.prototype。 + 误区 2:Object 构造函数是 new Function() 创建的 + 纠正:Object 构造函数是引擎原生实现的,但它的行为等价于通过 Function 构造函数创建(即 Object.proto === Function.prototype)。 上面那段话是为了解释所以才说是由xx创建的,但是底层肯定不是简单的new Object或new Function创建,但是行为很类似。 原型对象和隐式原型(\\proto\\)区别 prose-td:text-center prose-td:align-middle | 概念 | 本质 | 作用 | 示例 | | --- | --- | --- | --- | | 原型对象 | 一个实际存在的对象 | 存储公共属性和方法供继承 | Object.prototype | | 隐式原型(proto)| 一个属性(指向原型对象的引用) | 建立对象与原型对象之间的链接(原型链) | a.\\proto\\ 指向 Object.prototype | 注意 + prototype 属性 vs \\proto\\ 属性 + prototype 是函数特有的属性,用于定义实例的原型。 + \\proto\\ 是所有对象的属性,指向其原型对象。 创建a对象原型链图全貌 创建a对象以及添加user实例 原型链图全貌 解释了控制台中\\proto\\和[[prototype]]的区别 [[prototype]]这只是 Chrome 控制台的开发者决定采取的行为。事实上,有一段时间,Chrome 控制台甚至没有 [[Prototype]] ,而是用 \\proto\\ 来表示现在所用的 [[Prototype]] 。这很令人困惑,因为它们并不相同。每个对象都有一个 [[Prototype]] (即使为 null),但并非所有对象都有 \\proto\\ 。 \\proto\\ 是从 Object.prototype 继承的 getter,如果你没有从那个继承,你就没有它。 基本上,这个想法是,至少就获取器而言,在控制台上下文中, [[Prototype]] 的层次结构更多的是一种分组,而不是一个层次路径。您在 [[Prototype]] 下看到的值仍然属于根对象;它们只是被隐藏在一个默认情况下隐藏它们的 [[Prototype]] 分组中。这种分组恰好也反映了实际的原型层次结构。所以看到 newPerson name [[Prototype]] constructor [[Prototype]] constructor proto newPerson name constructor proto 与组。所以访问 proto (通过所有 [[Prototype]] )就像访问 newPerson.proto https://www.reddit.com/r/learnjavascript/comments/z6z7mi/whydoestheconsoleshowprototypeandprotoin/ 出现[[prototype]]在 chrome 控制台是 chrome92 版本,2021 年 4 月份左右,以便更好的跟随 firefox 开发者工具 原型语法反映了它是一个内部槽位(也在这里),仅由调试器暴露,而不是某个正常属性。使用 proto 作为别名会引起一些混淆,因为对象实际上可以有那个名称的正常属性,而且还有一个旧的已弃用的 Object.prototype.proto getter/setter,它会访问[[原型]]。 https://stackoverflow.com/questions/75718019/display-of-proto-vs-prototype-in-chrome-developer-tools https://issues.chromium.org/issues/40565034 https://issues.chromium.org/issues/40759936 https://docs.google.com/document/d/1Xetnc9s6r0yy4LnPbqeCwsnsOtBlvJsV4OCdXMZ1wCM/edit?tab=t.0 javascript 一切皆为对象? 这句话其实不够准确,更准确的说法是: JavaScript 中的值可以分为原始类型和对象类型 原始类型包括:boolean、number、string、null、undefined、symbol、bigint 原始类型有对应的包装对象(除了 null 和 undefined) 当你调用原始类型的方法时,JavaScript 会临时创建一个包装对象 // 临时包装对象的过程 const str = "hello"; str.toUpperCase(); // JavaScript临时创建String对象,调用方法后销毁 // 等同于 new String("hello").toUpperCase(); 这就是为什么原始类型看起来"像对象",但实际上它们不是对象。 给数组添加属性可取吗? 数组本质其实就是一个特殊的对象,所以自然也能给数组添加属性,但是这种方式好不好? 不推荐在生产环境大量使用 - 可能导致代码难以理解 - 影响代码可维护性 - 可能造成性能问题 new Boolean new Boolean(true) === true 打印结果是 false 常用的解释式一个是包装类型一个是基本类型,所以两者不相等,我们可以通过 typeof 来查看 typeof new Boolean(true); // "object" typeof Boolean(true); // "boolean" typeof true; // "boolean" 但是值得注意的是 const b = new Boolean(true); if (b) { console.log("true"); } else { console.log("false"); } // 这里会打印true,也就是说在执行的时候Boolean对象会被自动转换为原始布尔值 在打印 new Boolean(true)时在控制台中,你可以看到[[prototype]]展开出现[[prototype]]和一个[[PrimitiveValue]] new Boolean()打印结果图 本次使用 chrome 浏览器 那[[PrimitiveValue]]这个是什么呢 ecma 官方解释: Internal state information associated with this object. Of the standard built-in ECMAScript objects, only Boolean, Date, Number, and String objects implement [[PrimitiveValue]]. 译文:与该对象关联的内部状态信息。在标准内置 ECMAScript 对象中,只有 Boolean、Date、Number 和 String 对象实现了[[PrimitiveValue]]。 参考 https://262.ecma-international.org/5.1/ 说到继承不得不提到es6的class 但从es6的class继承方式来说本质还是原型继承,只是写法上通过class以及extends关键字来实现。 但从其他角度来说的话,它们也存在一些差异 1. es5构造函数可以直接调用,但是es6的class必须使用new关键字 2. es6的class里的方法不可枚举,而es5的构造函数里的方法是可枚举的 class Example { method() {} } console.log(Object.getOwnPropertyDescriptor(Example.prototype, 'method').enumerable); // false function example2() { } example2.prototype.method = function() {} console.log(Object.getOwnPropertyDescriptor(example2.prototype, 'method').enumerable); // true 3. es5很难继承内置对象,而es6的class可以 + 如果想要继承内置对象,采用es5的方法将会很复杂,而es6就很简单 class MyArray extends Array { } function myArray2() { } Object.setPrototypeOf(myArray2, Array.prototype); console.log(Array.isArray(new MyArray())); // true console.log(Array.isArray(new myArray2())); // true + 特殊行为无法继承 function MyArray() {} MyArray.prototype = Object.create(Array.prototype); // class MyArray extends Array {} const myArr = new MyArray(); console.log(myArr.length); // 0 myArr[0] = 'a'; myArr[1] = 'b'; console.log(myArr.length); // 还是 0,而不是 2 如果一定要将es5继承内置对象方法,查看一下方法 function MyArray() { var arr = Array.prototype.constructor.apply(null, arguments); Object.setPrototypeOf(arr, MyArray.prototype); return arr; } MyArray.prototype = Object.create(Array.prototype); MyArray.prototype.constructor = MyArray; // 测试 var arr = new MyArray(1, 2, 3); console.log(Array.isArray(arr)); // true console.log(arr instanceof Array); // true console.log(arr instanceof MyArray); // true console.log(arr.length); // 3 arr.length = 0; console.log(arr[0]); // undefined ------------------------------------------------------------ ## 数据不可变 **URL:** https://www.jinmokai.tech/blogs/数据不可变 **Published:** 2024-12-31 **Updated:** 2024-12-31 **Tags:** javascript **Summary:** 数据不可变 数据不可变 最近写了很多公司代码,发现每次如果定义一个变量且这个变量后续不会进行的时候每次都会这样定义。 我这里就使用 vuejs 吧 const defineViews = ref(["hello", "world"]); 这样写也没错,但是不够语义,数据在执行过程中可能会存在被更改问题,但是这种情况很小但是我们的所要表达的意思就是该变量不能更改,书写逻辑更清晰的代码我想这是极好的! 原生 js Object.freeze()静态方法 数据不可变,首先想到的是 Object.freeze 这个方法,可以冻结对象或数组,但是值得注意的是该方式是一个浅冻结,对象下面的对象可以再次被更改,如果希望对象里面什么都不能更改的话,可以写一个递归表示该对象不能更改 function deepFreeze(object) { // 获取对象的属性名 const propNames = Reflect.ownKeys(object); // 冻结自身前先冻结属性 for (const name of propNames) { const value = object[name]; if ((value && typeof value === "object") || typeof value === "function") { deepFreeze(value); } } return Object.freeze(object); } // 冻结对象后: // - 不能添加新属性 // - 不能删除现有属性 // - 不能修改属性值 // - 不能修改属性特性 用例来自 MDN Object.defineProperty() 使用该语法进行配置 writable 为 false,configurable 也为 false const obj = {}; Object.defineProperty(obj, "key", { writable: false, // 不可写 configurable: false, // 不可配置 value: "value", enumerable: true, // 可枚举 }); proxy 使用 es6 新语法 proxy 来做拦截具体实现 function createImmutableObject(target) { const handler = { // 拦截设置属性操作 set(target, property, value) { console.warn(Cannot set property '${property}': object is immutable); return false; }, // 拦截删除属性操作 deleteProperty(target, property) { console.warn(Cannot delete property '${property}': object is immutable); return false; }, // 拦截 Object.defineProperty defineProperty(target, property, descriptor) { console.warn(Cannot define property '${property}': object is immutable); return false; }, // 拦截 Object.setPrototypeOf setPrototypeOf(target, prototype) { console.warn('Cannot set prototype: object is immutable'); return false; }, // 拦截属性描述符的修改 getOwnPropertyDescriptor(target, prop) { const descriptor = Object.getOwnPropertyDescriptor(target, prop); if (descriptor) { descriptor.configurable = false; descriptor.writable = false; } return descriptor; }, // 防止扩展对象 preventExtensions(target) { Object.preventExtensions(target); return true; }, // 检查对象是否可扩展 isExtensible(target) { return false; } }; // 如果目标对象包含嵌套对象,递归处理 for (let key in target) { if (typeof target[key] === 'object' && target[key] !== null) { target[key] = createImmutableObject(target[key]); } } // 创建代理 return new Proxy(target, handler); } // 使用示例 const obj = { name: 'John', age: 30, address: { city: 'New York', country: 'USA' } }; const immutableObj = createImmutableObject(obj); // 测试各种操作 try { // 尝试修改属性 immutableObj.name = 'Jane'; // 将被拦截 // 尝试删除属性 delete immutableObj.age; // 将被拦截 // 尝试添加新属性 immutableObj.newProp = 'test'; // 将被拦截 // 尝试修改嵌套对象 immutableObj.address.city = 'Boston'; // 将被拦截 // 尝试通过 Object.defineProperty 添加属性 Object.defineProperty(immutableObj, 'test', { value: 'test' }); // 将被拦截 // 尝试修改原型 Object.setPrototypeOf(immutableObj, {}); // 将被拦截 } catch (e) { console.error(e); } // 查看对象内容 console.log(immutableObj); 通过proxy可以模拟Object.freeze()功能,createImmutableObject函数实现的是一个深度冻结逻辑,但是proxy相比Object.freeze性能会稍差。 在 vue 中存在一个 readonly 的语法表示该数据不可变,核心源码是 function defineReadonlyProperty( proxy: any, target: any, key: string, shallow: boolean ) { Object.defineProperty(proxy, key, { enumerable: true, configurable: true, get() { const val = target[key] return shallow || !isPlainObject(val) ? val : readonly(val) }, set() { DEV && warn(Set operation on key "${key}" failed: target is readonly.) } }) } 需要注意的是 vue2 不支持数组,vue3 是支持的 if (!isPlainObject(target)) { if (DEV) { if (isArray(target)) { warn(Vue 2 does not support readonly arrays.); } else if (isCollectionType(target)) { warn(Vue 2 does not support readonly collection types such as Map or Set.); } else { warn(value cannot be made readonly: ${typeof target}); } } return target as any; } ------------------------------------------------------------ ## Astro5.0实验性功能responsive-images **URL:** https://www.jinmokai.tech/blogs/astro50实验性功能responsive-images **Published:** 2024-12-07 **Updated:** 2024-12-07 **Tags:** astro **Summary:** Astro5.0实验性功能responsive-images,2024年12月3号,astro5.0正式发布了!作为Astro爱好者,有一个新功能让我很喜爱,虽然这是实验性功能但是对于我来说这是一个非常值得兴奋的事!那就是responsive images! 背景历史 在 Astro 5.0 版本之前,Astro 团队在图像优化方面做了大量工作。例如,他们在内置的图像组件中添加了许多功能,以改善累积布局偏移(CLS)和最大内容绘制(LCP)。通过引入 decoding 和 densities 属性,实现了响应式图像加载效果,并通过 format 属性支持多种图像格式渲染。此外,quality 属性也被添加,以调整图像的加载优先级。 Astro 3.x 版本引入的 picture 组件,进一步增强了对多种格式图像加载的兼容性。尽管如此,Astro 团队在图像处理方面做了很多改进,但在高屏幕密度设备上自动加载高分辨率图像通常是不合理的。尤其是在移动设备上,高密度屏幕可能导致不必要的高分辨率图像下载,从而消耗更多的网络流量。 该功能最早版本发布于2024年11月21号,包版本为astro@5.0.0-beta.9 基本功能 以下操作都是生产开模式,图片路径会开发不一样 最初的\和\组件加载信息都是这样的(未使用densities属性): 通过最新的Astro5官网文档可以看到配置信息,添加实验性功能在配置文件中 { image: { experimentalLayout: 'responsive' }, experimental: { responsiveImages: true, }, } 然后看到的Image组件和Picture组件效果就是: 注意这里的sizes属性的min-width: 994px,这个994px的值是图片原始宽度,该值也会在srcset属性里面的控制最大值,下面有一个4096px的图片,渲染输出结果将会是: 默认的这些断点是astro默认配置信息,最大支持6K / @docs @name image.experimentalBreakpoints @type {number[]} @default [640, 750, 828, 1080, 1280, 1668, 2048, 2560] | [640, 750, 828, 960, 1080, 1280, 1668, 1920, 2048, 2560, 3200, 3840, 4480, 5120, 6016] @description The breakpoints used to generate responsive images. Requires the experimental.responsiveImages flag to be enabled. The full list is not normally used, but is filtered according to the source and output size. The defaults used depend on whether a local or remote image service is used. For remote services the more comprehensive list is used, because only the required sizes are generated. For local services, the list is shorter to reduce the number of images generated. / experimentalBreakpoints?: number[]; 源码解析 源码也是比较简单: 不管是\组件还是\组件内部实现方式都是基于getImage函数做处理,getImage函数也是已经暴露功能,不过官方建议推荐在服务端的时候使用,客户端不推荐! getImage() relies on server-only APIs and breaks the build when used on the client 在Image组件和picute组件内部更改的其实比较简单,无非就是判断当前模式是否开启了实验性responsive功能并通过applyResonsiveAttributes函数来输出实验性功能所需的属性方法。 DEFAULTRESOLUTIONS, LIMITEDRESOLUTIONS, getSizesAttribute, getWidths, } from './layout.js'; type GetImageResult, type ImageTransform, type SrcSetValue, type UnresolvedImageTransform, isImageMetadata, } from './types.js'; if (!globalThis?.astroAsset?.imageService) { const { default: service }: { default: ImageService } = await import( // @ts-expect-error 'virtual:image-service' ).catch((e) => { const error = new AstroError(AstroErrorData.InvalidImageService); error.cause = e; throw error; }); if (!globalThis.astroAsset) globalThis.astroAsset = {}; globalThis.astroAsset.imageService = service; return service; } return globalThis.astroAsset.imageService; } type ImageConfig = AstroConfig['image'] & { experimentalResponsiveImages: boolean; }; options: UnresolvedImageTransform, imageConfig: ImageConfig, ): Promise { // 这里参数作为如果不合法就return 简化 if (xxx) { return /// } // 2.执行服务器解析图片地址 const service = await getConfiguredImageService(); // If the user inlined an import, something fairly common especially in MDX, or passed a function that returns an Image, await it for them const resolvedOptions: ImageTransform = { ...options, src: await resolveSrc(options.src), }; let originalWidth: number | undefined; let originalHeight: number | undefined; let originalFormat: string | undefined; // Infer size for remote images if inferSize is true if ( options.inferSize && isRemoteImage(resolvedOptions.src) && isRemotePath(resolvedOptions.src) ) { const result = await inferRemoteSize(resolvedOptions.src); // Directly probe the image URL resolvedOptions.width ??= result.width; resolvedOptions.height ??= result.height; originalWidth = result.width; originalHeight = result.height; originalFormat = result.format; delete resolvedOptions.inferSize; // Delete so it doesn't end up in the attributes } const originalFilePath = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.fsPath : undefined; // Only set for ESM imports, where we do have a file path // Clone the src object if it's an ESM import so that we don't refer to any properties of the original object // Causing our generate step to think the image is used outside of the image optimization pipeline const clonedSrc = isESMImportedImage(resolvedOptions.src) ? // @ts-expect-error - clone is a private, hidden prop (resolvedOptions.src.clone ?? resolvedOptions.src) : resolvedOptions.src; if (isESMImportedImage(clonedSrc)) { originalWidth = clonedSrc.width; originalHeight = clonedSrc.height; originalFormat = clonedSrc.format; } if (originalWidth && originalHeight) { // Calculate any missing dimensions from the aspect ratio, if available const aspectRatio = originalWidth / originalHeight; if (resolvedOptions.height && !resolvedOptions.width) { resolvedOptions.width = Math.round(resolvedOptions.height * aspectRatio); } else if (resolvedOptions.width && !resolvedOptions.height) { resolvedOptions.height = Math.round(resolvedOptions.width / aspectRatio); } else if (!resolvedOptions.width && !resolvedOptions.height) { resolvedOptions.width = originalWidth; resolvedOptions.height = originalHeight; } } resolvedOptions.src = clonedSrc; const layout = options.layout ?? imageConfig.experimentalLayout; if (imageConfig.experimentalResponsiveImages && layout) { resolvedOptions.widths ||= getWidths({ width: resolvedOptions.width, layout, originalWidth, breakpoints: imageConfig.experimentalBreakpoints?.length ? imageConfig.experimentalBreakpoints : isLocalService(service) ? LIMITEDRESOLUTIONS : DEFAULTRESOLUTIONS, }); resolvedOptions.sizes ||= getSizesAttribute({ width: resolvedOptions.width, layout }); if (resolvedOptions.priority) { resolvedOptions.loading ??= 'eager'; resolvedOptions.decoding ??= 'sync'; resolvedOptions.fetchpriority ??= 'high'; } else { resolvedOptions.loading ??= 'lazy'; resolvedOptions.decoding ??= 'async'; resolvedOptions.fetchpriority ??= 'auto'; } delete resolvedOptions.priority; delete resolvedOptions.densities; } const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions; // Get all the options for the different srcSets const srcSetTransforms = service.getSrcSet ? await service.getSrcSet(validatedOptions, imageConfig) : []; let imageURL = await service.getURL(validatedOptions, imageConfig); const matchesOriginal = (transform: ImageTransform) => transform.width === originalWidth && transform.height === originalHeight && transform.format === originalFormat; let srcSets: SrcSetValue[] = await Promise.all( srcSetTransforms.map(async (srcSet) => { return { transform: srcSet.transform, url: matchesOriginal(srcSet.transform) ? imageURL : await service.getURL(srcSet.transform, imageConfig), descriptor: srcSet.descriptor, attributes: srcSet.attributes, }; }), ); if ( isLocalService(service) && globalThis.astroAsset.addStaticImage && !(isRemoteImage(validatedOptions.src) && imageURL === validatedOptions.src) ) { const propsToHash = service.propertiesToHash ?? DEFAULTHASHPROPS; imageURL = globalThis.astroAsset.addStaticImage( validatedOptions, propsToHash, originalFilePath, ); srcSets = srcSetTransforms.map((srcSet) => { return { transform: srcSet.transform, url: matchesOriginal(srcSet.transform) ? imageURL : globalThis.astroAsset.addStaticImage!(srcSet.transform, propsToHash, originalFilePath), descriptor: srcSet.descriptor, attributes: srcSet.attributes, }; }); } return { rawOptions: resolvedOptions, options: validatedOptions, src: imageURL, srcSet: { values: srcSets, attribute: srcSets.map((srcSet) => ${srcSet.url} ${srcSet.descriptor}).join(', '), }, attributes: service.getHTMLAttributes !== undefined ? await service.getHTMLAttributes(validatedOptions, imageConfig) : {}, }; } 首先,该函数会执行 getConfiguredImageService 函数,以判断用户是否配置了图像服务。如果没有配置,它会尝试动态导入一个默认服务,Astro 默认使用的图像服务是 sharp。 如果启用了实验性功能,系统将通过图像服务计算不同分辨率和尺寸的 srcset,然后进行校验,最终输出 URL 和属性。总体来说,代码结构简单明了。 总结 虽然早期的 Astro 已经具备了一定的处理逻辑,但效果并不理想,每个响应式属性都需要手动添加,十分繁琐。现在,通过简单的配置信息即可实现全面且出色的响应式图像处理。 此外,作者提到未来将考虑完善图像占位符功能,让人非常期待!👍👍 参考 + github discussions:https://github.com/withastro/roadmap/discussions/1031 + github issues::https://github.com/withastro/roadmap/issues/1042 + RFC:https://github.com/withastro/roadmap/blob/responsive-images/proposals/0053-responsive-images.md + astro.docs: https://docs.astro.build/zh-cn/reference/modules/astro-assets/#getimag ------------------------------------------------------------ ## 项目无用功的一些思考 **URL:** https://www.jinmokai.tech/blogs/项目无用功的一些思考 **Published:** 2024-12-06 **Updated:** 2024-12-06 **Tags:** 其他 **Summary:** 最近在做公司项目做了一些无用功,碎碎念一下😫 这段时间一直参与公司项目,一期开发较为顺利,二期开发其实也还好,不过有个点一直卡的很难受 先说难受的地方吧!!! 我们公司项目存在vue2以及vue3两种项且部分项目存在一些公用的表单管理器,由于公司内部的表单管理器模块我不是很熟悉走了一些弯路 因为很多模块有很多所以不是所有模块都存在表单统一管理,很多都是开发者个人写的表单,所以统一放到表单管理器里面是不能覆盖全面的 我当时的做法是:写了一个公共组件兼容表单管理器以及其他表单,其他表单只需要引入该组件,即可完成。当时为了方便后续开发者维护,我特地专门写了一个mixins以便需要用到的模块引入mixin即可使用,且该组件内部的参数传递都是和mixin进行传递,而不让模块出现很多冗余代码,方便代码清晰明了。 但是问题就是这样做会有几个问题,该组件内部发送时间会有两个emit事件,一个用于表单管理器,一个用于其他表单,所以需要开发者根据不同模块来接受不同的emit事件,代码和项目中的其他组件有些违和(该组件特殊业务上不属于表单管理器)还有一个问题就是如果使用mixin那么会导致vue3项目有一些影响,因为vue3不推荐mixin,虽然可以使用,但不是最佳实践。 我接了两三个模块之后发现问题变得似乎复杂了,我询问了高级开发关于表单管理器的使用规范,发现了问题:我的问题就是把传递的值分的过于细化了,该组件只需要暴露需要传递的值即可,不同模块可以自己去根据参数做处理 通过他们的建议,我目前的解决思路如下:保留现在存在的mixin以便后续还有需要用到的地方,重新设计一下新组件,不要暴露一个emit事件和表单管理器对齐即可,如果其他组件使用了该组件依然可以使用该组件(我之前当时是这样设计来的,但是当时遇到了一个小问题,迫使发送两个事件导致设计不是很合理) 说一说为什么当时为什么觉得不合理吧!因为该组件不像其他组件传递了值就能很好的接收,而是还需要通过将值通过组件内部调用接口再回显值给到父组件,这时会出现watch侦听器一直触发,我当时本想在watch里面来判断这个接受的参数是否是组件返回的值,因为返回的值和进来的值是有区别的可以通过属性判断。 但是当时考虑组件这样就不是很纯粹了,索性在定义一个data值来用于接受详情数据,这样就不会出现watch侦听器一直触发的问题了,但是一旦这样设计将会和表单管理器相违背,现在重新设计的话,只需要把值传递给组件即可,组件内部根据参数做处理,这样就不会出现watch侦听器一直触发的问题了,而且也不会出现和表单管理器相违背的问题了,然后组件返回的值都是模块需要用到的值,模块进行一些拼装即可完成接口对接 哎~~心累这里面我前前后后改了好几次,感觉还是不够好,希望这次改造好后能有所改进吧😭😭😭 后天加班,先把其他模块功能对接完成,表单这块需要重新设计一下 ------------------------------------------------------------ ## element主题色更换方案 **URL:** https://www.jinmokai.tech/blogs/element主题色更换方案 **Published:** 2024-11-27 **Updated:** 2024-11-27 **Tags:** element-ui, css **Summary:** element主题色更换方案,笔记迁移最早写于24年3月2日 最近再做一些企业特征项目,主要就是换肤,icon更换之类的,根据有主题色彩的项目! 我们公司项目组件库主要有是基于element二次开发的组件库,版本有element和element-plus两个版本。 先来谈谈最简单的element-plus吧,为什么最简单,因为它里面的换肤采用的是css变量的方式,我们开发者想要基于之前的主题开发就会显得更简单,直接替换掉css变量即可实现,我认为这是技术的最佳实践! element的对应的是vue2版本,element和element-plus里面用到的主题都是scss文件。 其中element中的没有采用css变量的方式,换肤变得不是技术最优解了! 我们的项目采用的方案是scss变量+css变量的方式进行更换,通过将css变量赋值给scss变量,然后项目中用到的颜色值通过scss变量进行替换。(:当然了,我们采用一种渐进式的颜色,让颜色渐变 当我们更改element主题时、将css变量赋值给scss变量的时候,会出现var(xx)不是一个颜色,为什么会这样? 其实原因很简单:就是var是运行时,scss变量是编译时,所以出现这种问题。 我只有三种方法 1. 将element所有涉及的mix函数全部修改!缺点很明显,会出现改不下去的情况,越改越多 2. 采用高兼容性版本css语法:color-mix来替换掉mix函数,并通过postcss来兼容低版本浏览器 3. 所以我们对于element组件库,我们只能通过覆盖样式的方式,方式虽然傻 ------------------------------------------------------------ ## Javascript五种observe **URL:** https://www.jinmokai.tech/blogs/javascript五种observe **Published:** 2024-11-27 **Updated:** 2024-11-27 **Tags:** javascript **Summary:** Javascript五种observe,笔记迁移最早写于23年12月23日 MutationObserver MutationObserver用于监听DOM对象的变更(包括子节点),当节点属性发生变化,或执行增删改操作时执行对应的callback。 MutationObserver为我们提供了一种十分方便的监听DOM变化的方式。 基本使用 // Observer需要一个用于监听的目标DOM const targetNode = document.getElementById("app"); //用于确定mutation监听变化的范围 const config = { attributes: true, // 监听目标节点的属性变化,例如id,class等属性 childList: true, // 除目标节点外还要监听目标节点的直接子节点 subtree: true, // subtree的范围大于childList,还包括子节点children characterData: true // 监听TextNode需要额外配置,默认TextNode变化不会触发callback }; // 当观察到变动时执行的回调函数,mutationsList包含本次变更的信息 const callback = function (mutationsList, observer) { console.log(mutationsList) }; const observer = new MutationObserver(callback); observer.observe(targetNode, config); API介绍 observe observe用于开启对某个DOM的监听,一个MutationObserver可以通过多次调用observe监听多个DOM的变化。 当变化发生时MutationObserver会将一个或多个mutation对象传给callback的第一个参数,mutation对象内包含本次变更的相关信息下面看一下mutation的结构 { addedNodes: [], //新增DOM时会包含被新增的DOM attributeName: "id", //本次变更的属性名 attributeNamespace: null, //命名空间URI,一般用不到 nextSibling: null, //当存在添加/删除节点的操作时会存在nextSibling/previousSibling, 引用上一个/下一个兄弟节点 previousSibling: null, oldValue: null, removedNodes: [], target: Text, type: "characterData" //变更类型,如characterData,childList等 } takeRecords takeRecords用于获取在事件队列中但还未传递给callback的mutation对象,通常使用在调用disconnect时又不想丢失之前的mutationRecords(如果mutation连续触发,可能出现mutation还在队列中但未传递给callback的情况)。 disconnect observer.disconnect() 调用observer.disconnect后Observer将不再监听target,如果不需要监听请及时调用该方法,以免产生预期之外的行为以及内存泄漏。 常见场景 对于需要监听DOM变化的场景可考虑使用MutationObserver,利于用于Tag group内元素的动态渲染,下面使用MutationObserver实现一个简单的Todo List MutationObserver To-Do List Demo #todo-list { list-style-type: none; } 待办事项列表 完成作业 购物 添加任务 移除任务 任务数量:2 const todoList = document.getElementById('todo-list'); const taskCount = document.getElementById('taskCount'); const observer = new MutationObserver((mutationsList, observer) => { mutationsList.forEach((mutation) => { if (mutation.type === 'childList') { updateTaskCount(); } }); }); const config = { childList: true }; observer.observe(todoList, config); document.getElementById('addTask').addEventListener('click', () => { const newTask = document.createElement('li'); newTask.textContent = '新任务'; todoList.appendChild(newTask); }); document.getElementById('removeTask').addEventListener('click', () => { const tasks = todoList.getElementsByTagName('li'); if (tasks.length > 0) { todoList.removeChild(tasks[0]); } }); function updateTaskCount() { const tasks = todoList.getElementsByTagName('li'); taskCount.textContent = 任务数量:${tasks.length}; } IntersectionObserver IntersectionObserver用于监听一个元素的可见比例(一个DOM元素被另一个DOM元素遮挡百分比)变化。 基本使用 const target = document.getElementById('app'); const options = { root: rootTarget, // 相对于某个元素进行遮挡计算 rootMargin: '0px', // 进行计算的边界范围,通过rootMargin可以实现提前计算或延迟计算(相对于root原本尺寸)的效果 threshold: 0.5 // 触发callback时的遮挡比例,0.5代表元素被遮挡50%时触发callback。由于浏览器事件循环机制的影响,callback触发时遮挡比例通常不会是精确的50%。 }; const intersectionObserver = new IntersectionObserver((entries, observer) => { //和MutationObserver相同,也是产生一个array entries.forEach(entry => { console.log(entry) }); }, options); intersectionObserver.observe(target); API介绍 observe & options observe方法用于启动一个Observer对DOM元素的监听。在创建IntersectionObserver时可以通过传入option改变监听的行为。 const options = { root: root, rootMargin: '100px', threshold: 0.7 }; 在上面的配置中,通过配置rootMargin为100px在target距离root元素100px时即可判定为被遮挡,通过threshold设置为0.7,当遮挡比例查过70%时执行callback。 entry callback第一个param是entry对象构成的array,entry包含了触发callback时DOM的位置信息 //被监听DOM元素的Rect信息 boundingClientRect: { bottom: 208 height: 200 left: 8 right: 208 top: 8 width: 200 x: 8 y: 8 } intersectionRatio: 1 //交叉比例 // 被监听元素与Root元素交叉部分矩形的Rect信息。 intersectionRect: { bottom: 208, height: 200, left: 8, right: 208, top: 8, width: 200, x: 8, y: 8 }, // 是否处于交叉状态 isIntersecting: true, isVisible: false, // Root元素的Rect信息 rootBounds: { bottom: 606, height: 606, left: 0, right: 476, top: 0, width: 476, x: 0, y: 0 }, // root元素 target: div#target, time: 49.09999990463257 常见场景 乍一看IntersectionObserver好像没啥用,单这个Api在某些场景下十分好用。 比如我们有一个通过sticky固定在屏幕顶部的header元素,我们希望在触发sticky时给header加一个shadow(很多table都有这样的功能) 一种很常见的做法是监听scroll,当滚动一定距离时加上shadow即可。但是监听scroll本身会早成一定的渲染压力(scroll触发非常频繁),同时如果使用React这样的框架又会造成额外的render,在用户的视角看来更卡了。 此时使用IntersectionObserver就很合适了,因为我们需要监听的只是触发sticky的一瞬间,其他的滚动都是无效的,没必要进行计算。 Sticky Header with Shadow on Intersection body { margin: 0; padding: 0; } header { height: 80px; background-color: #3498db; color: white; text-align: center; line-height: 80px; position: sticky; top: 0; z-index: 100; } .header-shadow { transition: box-shadow 0.3s ease; } .header-shadow.shadow { box-shadow: 0 2px 5px black; } section { height: 1000px; background-color: #ecf0f1; padding: 20px; } Sticky Header 向下滚动触发sticky时展示shadow const header = document.getElementById('sticky-header'); const section = document.querySelector('section'); const options = { threshold: 1 }; //guard滚动到可视区域以外时认为触发了shadow const intersectionObserver = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { header.classList.remove('shadow'); } else { header.classList.add('shadow'); } }); }, options); intersectionObserver.observe(document.getElementById('guard')); ResizeObserver ResizeObserver是用于监听DOM尺寸变化的observer,当DOM尺寸变化是执行callback 基本使用 和前面的api用法差不多,这里不过多介绍。 const box = document.getElementById('box'); const resizeObserver = new ResizeObserver(entries => { entries.forEach(entry => { console.log(entry) }); }); resizeObserver.observe(box); entry对象包含resize相关的信息,下面看一下entry的结构 { // 不同box-sizing下的尺寸 borderBoxSize: [{ blockSize: 200, inlineSize: 200, }], contentBoxSize: [{ blockSize: 200, inlineSize: 200, }], contentRect: { bottom: 200, height: 200, left: 0, right: 200, top: 0, width: 200, x: 0, y: 0 }, // 在物理设备像素上的大小, 在不同的屏幕上尺寸不同例如Retina devicePixelContentBoxSize: [{ blockSize: 300, inlineSize: 300 } ], target: div#resizable-box } 常见场景 可以基于ResizeObserver实现一个简单的resize-detector(参考react-resize-detector),在尺寸变化时返回尺寸信息。 实现一个简单的resize-detector 这个demo做简单一点,点击盒子就算拖拽有效。 ResizeObserver Demo with Resizable Box #resizable-box { width: 200px; height: 200px; background-color: #3498db; color: white; text-align: center; line-height: 200px; font-size: 24px; transition: background-color 0.5s ease; resize: both; overflow: auto; cursor: pointer; } Resize me! const resizableBox = document.getElementById('resizable-box'); let isResizing = false; let startX, startY, startWidth, startHeight; const resizeObserver = new ResizeObserver(entries => { for (const entry of entries) { const { width, height } = entry.contentRect; console.log('宽度:', width, '高度:', height); } }); resizeObserver.observe(resizableBox); resizableBox.addEventListener('mousedown', startResize); document.addEventListener('mousemove', handleResize); document.addEventListener('mouseup', stopResize); function startResize(e) { isResizing = true; startX = e.clientX; startY = e.clientY; startWidth = parseInt(document.defaultView.getComputedStyle(resizableBox).width, 10); startHeight = parseInt(document.defaultView.getComputedStyle(resizableBox).height, 10); } function handleResize(e) { if (!isResizing) return; const newWidth = startWidth + (e.clientX - startX); const newHeight = startHeight + (e.clientY - startY); resizableBox.style.width = newWidth + 'px'; resizableBox.style.height = newHeight + 'px'; } function stopResize() { isResizing = false; } PerformanceObserver PerformanceObserver用于监听浏览器的performance事件,方便在performance事件触发时作统一处理。 基本使用 // mdn demo function perfobserver(list, observer) { console.log(list) } var observer2 = new PerformanceObserver(perfobserver); // entryTypes用于指定要监听的事件类型 observer2.observe({ entryTypes: ["measure"] }); 下面列一下常见的entryTypes + mark 用于标记时间戳的事件 + measure performance.measure触发的事件 + frame 网页渲染的事件 + navigation 导航的事件,例如页面加载或重新加载 + resource 资源加载事件 + longtask 长任务事件 + paint:绘制事件,例如FP,FCP + layout-shift 用于监视布局变化的事件 对于对性能比较敏感的项目以及长期性能监控来说这个api还是比较方便的。 抖音应该使用到了该api,通过网页源代码搜索可以搜索到 ReportingObserver ReportingObserver用于监听浏览器报告的事件,例如废弃API,过时特性,网络错误。做监控SDK的同学应该经常能用到,日常业务代码用的比较少。 基本使用 这里就简单看一下使用方法吧, 比较简单 const observer = new ReportingObserver((reports, observer) => { reports.forEach(report => { console.log(report); }); }); // 监听过时特性 observer.observe({ types: ['deprecation'] }); ------------------------------------------------------------ ## 国庆游记 **URL:** https://www.jinmokai.tech/blogs/国庆游记 **Published:** 2024-10-03 **Updated:** 2024-10-03 **Tags:** 随笔 **Summary:** 国庆节,从沿河路到鄱阳中学再到文和里,再沿着土湖一路变骑变走... 今天回到高中逛了逛,看到很多学生从大门口出来,我很惊奇,今天是国庆学生不放假吗? 很是好奇,走进去看到学生有的抱着书从门口出来,又有的学生从大门口进去,不过出来的占大多数,我找到一对在交谈的女同学问了问:你们还上学吗?国庆不放假吗?她们有些害羞道:“放假,我们是高三学生没什么事情就在学校里面自习,所以可以自由出入” 我笑道:“原来这样,打扰了”笑着打完招呼就往里面走去,大门有个两个,第二个大门进去就是教学楼了,我就没进去了,在附近走了走,体育馆外墙变红了很好看,路面也很干净,宿舍楼也粉刷成红色。 我离开学校在附近走了走,发现有一些特色。例如,多了很多服装加工点,应该是学生陪读的母亲空闲时做些针线活打发一些时间、还有就是多了很多麻将馆,之前读书的时候附近看的挺少的、然后就是餐饮店大部分都换了,我记得有一家我经常吃,我第一吃来到高中在外面吃第一顿饭就是在这里吃的,后面也是经常吃,庆幸的是这家店还在味道依旧。。。 当我从学校走出后,心中感叹颇多,看着小好几届的学妹们,不禁想起当时在这里的点滴,多么青春多么活波的时光啊… 骑车来到文和里走了走,下午人不是很多,晚上会很热闹,好像有什么网红见面会的活动,我关注里面有一个博主叫:(瓜子,八十年代第一人)挺有趣的,可惜不是晚上无法找他合个影。 从文和里出来,沿着姜夔大道走了走,这里风景太美了,以后有机会在这里骑行我想将会是极好,在瓦屑坝博物馆停留了一下,这里的土湖太美了,远处的村落祠堂和这蓝天白云相得益彰。 湖边有少数民族在这里聚餐,也有躲在阴处拉二胡的大爷,也有情侣在这里闲逛,更多的是小孩在湖边玩耍。回老家的时间总是很平淡的,也可以说是无趣,但看到风景心中所困恼依旧的乡愁在这一刻都得以释放。 ------------------------------------------------------------ ## 青山湖骑行20240907 **URL:** https://www.jinmokai.tech/blogs/青山湖骑行20240907 **Published:** 2024-09-07 **Updated:** 2024-09-07 **Tags:** 生活日记 **Summary:** 周末去青山湖骑行,全程30多KM。沿途风景挺好,拍几张照片留个纪念 骑行 今天去青山湖骑行,全程30多KM,在起点拍个照片。 沿着湖边骑行风景很不错,拍了几张湖边照片,沿途也有很多打卡点,我兴趣不是很大就在开头和中途拍了几张留作纪念,下次来打算天气非常舒服的时候租个山地车,来这边逆时针骑骑! 中途到饭点,打算去临安市区吃点饭,这边的兰州拉面比市区便宜了3元,一份炒面才15RMB,价格很实惠。 吃完饭顺便去临安博物馆逛了逛,发现就两个场馆可以阅览,其他地方在装修就没进去,逛完后再续沿着后半程继续骑行。路过水上森林,没进去,身体已经很累了,就没有继续逛,继续沿着骑行路线骑行。 ------------------------------------------------------------ ## HTML标签语义 **URL:** https://www.jinmokai.tech/blogs/html标签语义 **Published:** 2024-09-01 **Updated:** 2024-09-01 **Tags:** HTML, 无障碍 **Summary:** HTML标签语义化使用场景-摘录《HTML并不简单》张鑫旭著 前言:本书主要摘录《HTML并不简单》一书,本文博客主要通过自己实践加深印象并方便自己日后查询 HTML语义标签 页面布局相关标签使用 HTML语义使用标准是会随着时间的变化而改变,而不是一成不变的 例如 `标签里面包含 这是不推荐的,现在这种方式却很常见,又例如元素和元素过去都是不推荐使用的,现在随着HTML5的推广,它有了自己独特的含义 header和footer标签 过去和标签使用场景往往都是在页面的头部或底部,现在使用场景可以放在文章头部或底部,也可以是组件头部或底部,例如: 文章标题 文章内容 文章底部 main和aside标签 标签表示页面主要内容,标签表示与主要内容相关的内容,结构如下所示: main aside 主要就是表示主内容区,表示次要内容区,不是侧边栏就一定使用标签,而是要有一定的关联性 nav标签 主要语义就是表示页面导航,通常包含一组链接,用于帮助用户在网站进行跳转,例如很多网站导航、标签菜单都是适合场景,例如: 首页 产品 关于 相反这不是多个导航链接场合都适合nav元素,例如网站底部的完全备案链接,都不适合元素 section元素 标签表示文档中的一个独立部分,通常包含一个标题,可以这么说只要带有标签独立部分(即带有h1-h6),都适合元素。 这种功能非常实用,只要是具有独立区域都可以使用,在很多非文档页面中也是非常常用的功能 终于不用写烦人的div标签了,在后台系统开发中中因为懒,常常使用 .xx > div 来解决样式问题🤯 这就会导致出现css渲染很慢的情况 文档结构信息标签使用 article标签 在文档结构信息对语义化要求非常高,例如,新闻,博客,咨询,论坛,社交网站信息流中,这些也是HTML语义化价值最大化的使用场景 比section元素更高一级的的article元素 article标签通常包裹整个信息块,结构如下: article h1 section h2 p section h2 p 如果出现article标签嵌套的情况,这是不符合语义的,内部的article请替换为section元素 总结一下基本结构就是 body main article h1 section h2 p section h2 p h1-h6标签 h1-h6标签表示标题,h1表示一级标题,h2表示二级标题,以此类推,h6表示六级标题,h1-h6标签通常用于文章标题,页面标题,组件标题等场景 其中,一个页面一定只有一个一级标签,即元素最多出现一次,且必须最先出现 对于工具类或应用网站,元素往往会用在logo上,同时设置网站的名称,以便增加网站seo识别权重;再某种程度上标签可以和有的一拼所以要慎用,如果发现网站已经添加了一个标签,就不要觉得无所谓,又额外添加标签,这会影响到seo, 此时请使用标签或添加role='heading'来保持标签语义 另外标签一定不能跳级,若标签是,则不能使用标签,只能使用标签,一定要一级一级的来 hrgroup元素 标签主要用来包裹标签和补充信息,而补充信息往往是一个或多个元素。 文章标题 副标题 效果如下 文章标题 副标题 引用元素区别blockquote,q,cite区别 表示引用元素有这三种,区别就是 - 标签表示长篇引用,通常是一段话或某个章节 这是一段引用内容 这是一段引用内容 效果如下 这是一段引用内容 这是一段引用内容 - 标签表示短篇引用,通常是一句话 q元素和blockquote元素语义本质一样,(q本质就是quote的缩写) 默认情况下blockquote是块级元素,同时在上下左右都会有默认margin距离。而q元素是行内元素,没有默认margin距离,但前后会增加引号 效果如下 这是一段引用内容 q标签还有一个属性叫cite,表示引用来源,此属性必须为url地址,例如: 景德镇浮梁有一个金公祠 金公祠 最后聊聊cite引用,虽然也是引用但是通常标签通常表示特殊特殊的名词,而不是言论引用 例如: 《HTML并不简单》是由张鑫旭编写 效果如下 《HTML并不简单》是由张鑫旭编写 默认cite标签会让字体斜体,因为英文字体不规则,体量小,所以专门设计倾斜字体样式很方便,特殊名称保持倾斜会很让人识别,不影响阅读,是很好的体验。 但是对于东南亚语言而言,这就不是很好的体验了,我们的中文斜体对于人力成本是巨大的,且中文字体倾斜都是采用算法强制倾斜,人力成本巨大,如果一定要使用的话,请采用font-style: normal来设置字体 倾斜字体i,b,em,strong i和em使用区别就是,这两个都可以让文字倾斜,且都表示当前文本和周围有所不同,他们区别是什么呢? 那就是屏幕阅读器的语气感情色彩 例如: 我们都羡慕人人都嫁给爱情,但是我们更重要的是爱自己 效果如下: 我们都羡慕人人都嫁给爱情,但是我们更重要的是爱自己 养成习惯的time标签 标签通常表示时间日期,如果涉及到时间相关的可以无脑使用 例如: 评论发表于刚刚 效果如下: 评论发表于刚刚 如果数据允许,精确时间值可以使用datetime属性标记,例如: 评论发表于刚刚 效果如下: 评论发表于刚刚 这里的datetime属性格式要求比较松散,只要是时间都可以,可以仅仅是年份,例如2024,也可以仅仅是小时和分钟,例如15:30,甚至可以是持续时间,例如PT4H18M3S 且使用time标签用css样式匹配也是非常安全 .desc time { color: red; ... } 内联元素 超过一半的span可以用data来替换 在一些工具,偏应用类的web产品中,一定会有大量来自数据库中的数据字段,这些数据往往采用元素呈现,现在,请使用`元素替代 年份 对于动态数据采用data标签,不仅对开发者友好,也对机器友好,其支持一个原生value的属性值,用来设置机器人识别信息;对于span元素,当我们需要挂在数据的时候需要在dom上挂载信息的时候 只能使用data-*自定义属性,例如: 年份 然后通过span.dataset.id来获取span上面的id数据,而使用data获取数据只需要data.value即可 ------------------------------------------------------------ ## 程运熙 **URL:** https://www.jinmokai.tech/blogs/程运熙 **Published:** 2024-08-11 **Updated:** 2024-08-11 **Tags:** 鄱阳中学 **Summary:** 鄱阳中学第三任校长考究 程运熙 在有简单介绍。 程运熙,生于1862年,卒年不详 光绪二十三年(1897 年考取举人)(江西科举第九十六名) 在1904-1905年担任江西省饶州府中学校学校校长(现鄱阳中学) 光绪二十三年丁酉科十八省乡试同年全录 于宣统二年1910年,成立景德镇商务总会,程运熙为书记 出自:《近代景德镇陶瓷史略》第59页 江西饶州府议员对张之洞的挽联 出自:《张之洞联语辑注》第256页 程运熙的孙子程道腴(生于1913年民国2年)3月17日 出自:《波阳文史资料 第8辑》第48页 ------------------------------------------------------------ ## 生活日记2024-7-28 **URL:** https://www.jinmokai.tech/blogs/生活日记20240728 **Published:** 2024-07-28 **Updated:** 2024-07-28 **Tags:** 生活日记 最近一直单曲听这首歌,很不错,骑行也听,写代码也听 (牡丹江-南拳妈妈) 你的浏览器不支持该音乐 前段时间一直加班,今天终于有双休了,昨天(周六)天气挺好骑单车随便逛了逛,中午又睡了一觉,基本没什么事情 今天周末,早上吃饭完,写了一会代码,最近在写一个Webcomponent组件,目前还在技术方案选型中,看能不能实现 可能是状态不好,写了一会就睡觉了,醒来被朋友叫醒 他说他到浙江省图书馆去了,我想了想,也打算过去,顺便将之前借的图书还了 我的朋友姓 ,赣州人,19年大学相识,目前从事游戏开发,主攻C#,虚拟引擎! 睡醒,在小区外随便找了一家店,午饭吃的是烧鸭饭,花费20元 我这里距离浙图并不是很远,骑单车比坐高铁还近,打算骑单车过去 最近一直骑单车发现,有点爱上了单车的感觉,骑行过程能让我享受短暂的自由,摆脱孤单! 到达浙图,找了一本vue2的设计书籍,公司项目中vue2,vue3各种各样,关于原理我都不是很懂,还是多看书多补充吧! 我和老表耐心读书 下午四点多,我们看的差不多了,老表借了一本关于游戏引擎的书籍,打算出去走走 我们打算坐地铁到城站,附近有一个五柳巷历史街区,打算从这走走,一路往南到鼓楼以及胡雪岩故居,不过傍晚了,景区关门,我们也就外围走走看看 我们无意中走到了一个小景区,因为傍晚了,没太阳,走走还是很不错的 晚上,我们有点饿了,在附近银泰找到一家日料店,主要做猪排饭,早期看过这类视频,很好吃,不过一直没找到这家店 等餐中 猪排饭也叫(猪排丼), 点了一个标准价格的花费49RMB,味道还可以,价格更贵的味道更丰富 吃完饭,我们随便走走,就回家了 ------------------------------------------------------------ ## 生活日记周末 **URL:** https://www.jinmokai.tech/blogs/生活日记 **Published:** 2024-07-20 **Updated:** 2024-07-20 **Tags:** 生活日记 周末生活日记 上周周末连续加班了两天,身心俱疲,今天周六,周末还要加班赶项目!趁着今天休息,骑单车逛逛西湖,顺便浏览一下,没去过的景点 早上吃过早饭,九点多骑单车,从家出发,经过西湖,最后到达浙江省美术馆 天气比较炎热,不过欣赏一些艺术作品让自己放松也是不错的! 中国青年 这张很有意思,驻足了很久 中国农民变化 这张艺术感挺强的! 相框 前段时间很火的一张照片,雨天,不同职业的人挤在同一个伞下! 雨中情 爸爸的礼物 爸爸的礼物 浙江省美术馆从二楼到四楼的展厅,分别逛了一下,时间尚早,骑单车去杭州人最多的地方,龙翔桥地铁站附近的银泰 很多美食,和娱乐产品,发现了一个任天堂体验馆,游玩一番 任天堂的游戏卡带,真贵,不过体验下来也确实好玩 任天堂游戏卡带 临近中午,买个奶茶喝喝,新加坡品牌,没喝过,尝尝味道 味道不错,冰块有点多 椰奶西瓜风味 bye~ 逛完回家了 tip:运动完之后,做什么事情,感觉整个人都轻松了! ------------------------------------------------------------ ## 西湖一日游 **URL:** https://www.jinmokai.tech/blogs/西湖一日游 **Published:** 2024-06-29 **Updated:** 2024-06-29 **Tags:** 生活日记 **Summary:** 周末闲来无事,西湖一日游! 杭州这段时间一直下雨,闲来无事,骑单车在西湖边走走逛逛 从浙江省图书馆出来,走几步路就到了黄龙洞 杭州西湖-黄龙洞 走进去就可以发现一个门 黄龙吐翠 之前走过这边,打算换个地方,我就骑着单车去了西湖内部的一个小景点-704工程 704工程入口 704工程通风装置 从704工程出来之后,沿着西湖继续走,发现一个地方很出片,多呆了一会 杭州风景还可以的,很多地方都没去逛,也是趁着天气好了一点,出去放松一下 bye~~ 👋👋 ------------------------------------------------------------ ## 饶州府中学堂最早课本 **URL:** https://www.jinmokai.tech/blogs/饶州府中学堂最早课本 **Published:** 2024-06-29 **Updated:** 2024-06-29 **Tags:** 鄱阳中学, 鄱阳 **Summary:** 1902年饶州府刚创立第一年的教学课本... 发现 我无意间在抖音搜索饶州府中学堂,因为我早起在抖音上分享过自己的文章,发现有人发了一个图文叫:饶州府中学堂课本文献 怀着满腔对历史考究的热爱,好奇点进去,发现作者ip和文献都很符合。 紧接着我就开始在孔夫子旧书网搜索相关信息,果然发现了蛛丝马迹! 孔夫子旧书网 搜索相关关键字找到了: 列举几张图片吧: 结束语 历史考究总是不是一番风顺的,有时候突然来了灵感找到了突破瓶颈的办法,有时候突然来了线索,一切都迎刃而解了! ------------------------------------------------------------ ## 纯css制作520爱心动画制作❤️ **URL:** https://www.jinmokai.tech/blogs/简单爱心 **Published:** 2023-05-20 **Updated:** 2023-05-20 **Tags:** css **Summary:** 一年一度的5月20号,闲来无事,写一写!点击爱心有动画! 效果 点击这个链接👉 第一步 * { margin: 0; padding: 0; } body { width: 100%; height: 100vh; display: grid; place-content: center; background: -webkit-linear-gradient(top, #ffa5a5 0%, #ffd3d3 100%); } 清除页面样式,并设置body背景图片! 第二步 ❤️ 5️⃣2️⃣0️⃣💌 设置所需的html页面 第三步 [type="checkbox"] { display: none; } [for="love"] { cursor: pointer; font-size: 5em; text-align: center; animation: 1s ease-in-out infinite love; } 将html骨架添加样式 第四步 @keyframes love { 0% { transform: scale(1); } 50% { transform: scale(2); } 100% { transform: scale(1); } } 让你的动画动起来吧!❤️ 第五步 p { display: none; margin-block-start: 3rem; width: min(30ch, 50%); } #love:checked ~ p { display: block; } 为点击后出现的字符设置样式(margin-block-start是逻辑属性) 通过:checked伪类实现点击效果,这样就是纯css啦! 第六步 .typing-demo { width: 11ch; animation: typing 2s steps(22), blink 0.5s step-end infinite alternate; white-space: nowrap; overflow: hidden; border-right: 3px solid; font-family: monospace; font-size: 3rem; } @keyframes typing { from { width: 0; } } @keyframes blink { 50% { border-color: transparent; } } 我们可以看到typing-demo为什么是11ch,因为emjoy字符一个占三个字符,所以应该要12ch,为了显示不突兀所以用11ch! 总结 css是个强大的工具,我们不能小瞧了! bye! 👋 ------------------------------------------------------------ ## 深拷贝,浅拷贝问题详解! **URL:** https://www.jinmokai.tech/blogs/深拷贝浅拷贝 **Published:** 2023-05-08 **Updated:** 2023-05-08 **Tags:** javascript **Summary:** 总结了深拷贝和浅拷贝一些流行方法!不过还是推荐使用Lodash库的深拷贝! | 第一次发布时间 | 最后更新时间 | 兼容性 | | ------------------- | ------------------- | -------- | | 2021-11-18 10:49:41 | 2023-7-10 13:59 | 下面详情 | 以下案例用到例子 let s = Symbol('foo'); let obj = { "name": "chkai", "date": new Date(), "value": 32, "arr": [1, 2, 3, 4, 5], [s]: "symbol value", regexp: /^\d/, fn: function() { console.log('this is fn') }, obj: { name: { age: 20 } }, map: new Map().set("a", 1), set: new Set().add("b") } 浅拷贝 Object.assign() 方法 function shallowCopy(obj) { return Object.assign({}, obj); } | | 兼容性 | 其他 | | ------------- | ---------- | ---- | | Object.assign | ie 不支持❌ | 引用类型改变 | 扩展运算符 function shallowCopy(obj) { return { ...obj }; } | | 兼容性 | 其他 | | ------------ | ---------- | ---- | | ... 扩展语法 | ie 不支持❌ | 引用类型改变 | 深拷贝 JSON.stringify()和 JSON.parse() function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); } | | 兼容性 | 其他 | | ---------------- | -------- | ------------------------------------------------------------ | | JSON.stringify() | ie 8以上 | node 18.14,chrome浏览器104:set,map,regexp获取不到。 function,symbol消失。日期Date变字符串 | 自定义 deepClone方法 function deepClone(source) { if (source === null) { return source; } var target = Array.isArray(source) ? [] : {}; for (var k in source) { var val = source[k]; var valueType = typeof val; if (valueType === "function") { target[k] = new Function("return " + val.toString())(); } else if (valueType === "object") { target[k] = deepClone(val); } else { target[k] = val; } } return target; } | | 兼容性 | 其他 | | ---------------------------- | -------- | ---------------- | | 自定义 | ie 8以上 | chrome104,node14:date日期、map集合、set集合、正则获取不到。 symbol消失。 | js原生structuredClone语法 const newCopy = structuredClone(obj) newCopy.arr.reverse() newCopy.obj.name.age = 323232 newCopy.set.clear() newCopy.map.clear() console.log(newCopy) console.log(obj) | | 兼容性 | 其他 | | ---------------------------- | -------- | ---------------- | | structuredClone() | 最低:chrome 98 firefox94 safari 15.4 nodejs 17 deno 1.14 | 如果有函数会报错:Uncaught DOMException: Failed to execute 'structuredClone' on 'Window' | node.js模块 const mapTag = "[object Map]"; const setTag = "[object Set]"; const arrayTag = "[object Array]"; const objectTag = "[object Object]"; const argsTag = "[object Arguments]"; const boolTag = "[object Boolean]"; const dateTag = "[object Date]"; const numberTag = "[object Number]"; const stringTag = "[object String]"; const symbolTag = "[object Symbol]"; const errorTag = "[object Error]"; const regexpTag = "[object RegExp]"; const funcTag = "[object Function]"; const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]; function forEach(array, iteratee) { let index = -1; const length = array.length; while (++index { cloneTarget.add(clone(value, map)); }); return cloneTarget; } // 克隆map if (type === mapTag) { target.forEach((value, key) => { cloneTarget.set(key, clone(value, map)); }); return cloneTarget; } // 克隆对象和数组 const keys = type === arrayTag ? undefined : Object.keys(target); forEach(keys || target, (value, key) => { if (keys) { key = value; } cloneTarget[key] = clone(target[key], map); }); return cloneTarget; } module.exports = { clone, }; // https://github.com/ConardLi/ConardLi.github.io/blob/master/demo/deepClone/src/clone6.js 掘金作品,很强。功能丰富! other + 可以使用lodash的拷贝。 ------------------------------------------------------------ ## 鄱阳中学历任校长 **URL:** https://www.jinmokai.tech/blogs/鄱阳中学历任校长 **Published:** 2023-04-10 **Updated:** 2023-04-10 **Tags:** 鄱阳中学, 鄱阳 **Summary:** 收集整理了鄱阳中学历任校长一些信息 鄱阳(波阳)中学历任校长 校名 姓名 籍贯 资历 任职时间 饶州府中学堂 萧鼎臣 波阳人 清代举人 1902 饶州府中学堂 李棻 浙江绍兴 举人 1903 江西省饶州府中学校 程运熙 波阳人 清代举人 1904-1905 江西省饶州府中学校 卢绍本 江苏 举人 1905-1906 江西省饶州府中学校 张佑闲 波阳人 举人 1906-1908 江西省饶州府中学校 徐凤钧 乐平 清代举人 1908-1909 江西省饶州府中学校 王善基 波阳 日本留学 1909-1911 江西省立饶州府中学校 周昱 余干人 日本留学 1912-1913 江西省立饶州府中学校 吴作新 余干人 1913-1914 江西省立饶州府中学校 张捄 余干人 清内阁中书 1914-1915 江西省立第五中学 钟祥鸾 兴国县人 江西优级师范学校毕业 1916-1924 江西省立第五中学 周日忠 抚州人 北京高等师范毕业 1925-1927 江西省立鄱阳中学 徐延展 新干县人 国立北平师范大学 1927(三个月) 江西省立第十中学 史世珍 波阳人 美国俄亥俄州大学 1927-1928 江西省立第十中学 吴大桢 临川人 江西优秀师范毕业 1928-1931 江西省立鄱阳初级中学 姜维翰 南昌人 北京师范大学 1932-1934 江西省立鄱阳初级中学 刘孝基 广丰人 北京师范大学 1934-1939 江西省立鄱阳中学 1939-1945 江西省立鄱阳中学 张国详 波阳人 北京大学 1945-1946 江西省立鄱阳中学 张重仪 萍乡人 武汉大学 1946-1949 江西省立鄱阳中学 程雪邨 波阳 上海华南大学(新民大学)? 1949-1952 江西省立鄱阳中学(分两部高门校址和东湖校址) 1952-1954 江西省立鄱阳中学(分两部高门校址和东湖校址) 孙守谦 东北 干部 1954-1955 鄱阳初级中学(东湖校址) 江西省立鄱阳中学(高门校址) 1955-1957 波阳中学(高门校址) 鄱阳初级中学(东湖校址) 1957-1960 鄱阳一中(高门校址) 鄱阳二中(东湖校址) 1960-1961 波阳一中(高门校址) 波阳二中(东湖校址) 姜道波 波阳 1961-1969 波阳中学 王佳喜 1969 波阳中学 曾和详 芝阳师范 1970 波阳中学 张华清 波中毕业 1979 波阳第一中学 程济庭 鄱阳人 波阳毕业 1982 波阳第一中学 曹功先 波阳 南京工学院 1985-1991 鄱阳中学 鄱阳中学 杨守仁 鄱阳人 江西师范大学 1991-1993 鄱阳中学 余孟灿 鄱阳人 江西行政学院 1993-2002 鄱阳中学 吴滋意 鄱阳人 江西师范大学 2002-2013 鄱阳中学 卓凡 鄱阳人 大学学历 2013-2022 鄱阳中学 孙晓平 鄱阳人 大学学历 2022-至今 部分校长信息: 萧鼎臣 生卒:(1863-不详) 光绪二十三年(1897 年考取举人)(江西科举第四名) 民国初,肖鼎臣举人身染沉疴逝世,临终前,口占一联以自挽: 父未安葬,母未终养,儿女未婚未嫁,遗憾皆由二弟补;…… 孰知病情突然恶化,未及续下联,即咽气长眠。其亲翁,又是挚友,十八坊程枚暄举人甚为悲坳,乃代其续下联: 名不出县,文不传世,身平不仕不耕,兴旺有待后人贤。 此续联不仅对仗工整,乃知交莫逆,代诉肺腑,’声泪俱下。 (出自波阳文史第九辑 126 页) 程运熙 生卒:(1862-不详) 光绪二十三年(1897 年考取举人)(江西科举第九十六名) 张佑闲 光绪二十八年(1902 年考取举人)(江西科举第三名) 张捄 生卒:(1872—1928) 光绪二十三年(1897 年考取举人) 张捄 (1866—1928),字匡予,号醰园居士,余干县人。光绪二十三年(1897),由县地方官推举赴京会试,适逢维新运动,参加“公车上书”,深得康有为、梁启超赏识。戊戌变法时期取消会试,对优秀举人量才点授官职。他因才华出众,点任为农工商部主事。变法失败,回到余干,杜门不出。民国初年,力辞为官,众多学校来请则欣然应聘。1914 年起,历任江西省立第五中学校长,江宁师范学院、江西省农业专门学校、省立第三师范学校、省立第一中学等校语文教员,讲课生动,深得师生敬仰。后返回故里,闭门著书,卖字为生,生活穷困潦倒,卧病不起。工书法,精说文。著《醰园文集》3 卷、《醰园诗集》1 卷。 钟祥鸾 钟是兴国县人,当时农亨的校长。解放前一直从事农业科学的实验研究,解放后仍在省农业厅或厅下属单位任工程师,后病故。 徐延展 生卒:(1897-不详) 1923-1928 年当过芝阳师范校长 1928 年在同仁中学当过教师 1941 年(民国三十年)在江西省樟树初级中学当校长 徐凤钧 生卒: (1866—1915) 徐凤钧(1866—1915)字晦吾,江西乐平人。光绪二十九年(1903 年)中举人,1904 年(光绪三十年),清政府要求各县将书院改办学堂。徐凤钧被乐平县令杜璘光聘请为翥山学堂堂长,1908-1909 年,徐凤钧出任江西省饶州府中学第六任学监,1910 年担任中国陶业学堂堂长,1911 年,民国政府任命徐凤钧守临江府、知清江县事。 周昱(周显?) 生卒: (1875-1951) 周昱(1875-1951)字微之,号西京,余干荫德门人。早年以学业精进而获得公费派往日本留学,1909 年在东京早稻田大学师范科毕业。回国时正逢废科举,兴学堂。他投身教育。1912-1913 年任江西省立饶州府中学校(鄱阳中学前身)学监(校长),临川第三师范及南昌匡庐中学教务主任等职。他担任玉婷中学董事长,并兼任校务主任主持校务。 史世珍 生卒: (1899-1990) 字号:聘珊籍贯:江西波阳县鄱阳镇西门村,生卒:1899 年 6 月 23 日-1990 年 史世珍出自世代书香门第,天资过人。自幼从父读书,深受教诲,很有长进。后毕业于九江同文书院。由于他勤奋颖悟,博览群书,过目成诵,很为师生欣赏。 1921 年他远赴美国留学,初入俄亥俄州立大学攻读文学。1925 年获文学学士学位。同年转往美国伊利诺斯州立大学研修经济学。1927 年,获经济学硕士学位。 他学成归国回到故乡,决心服务社会,报效祖国,从事教育事业。由于他深厚的学养,卓越的外语能力受聘为江西省立第十中学(现波阳中学)校长。教学两年,又改调上饶,任江西省立第六中学校长之职。 1930 年,史世珍被聘任为中央陆军军官学校第十一期外语主任教官。在黄埔军校执教 4 年后,便任职中央银行兼受聘于复旦大学教授经济学。并任国民政府行政院参事四年。 1942 年借调接任江西裕民银行总经理之职,此时正值抗日战争中期,为争取地方财力,发行纸币,以调节金融,增进全国抗战力量,及发展地方工商业,他无竭虑殚精,全力以赴,以努力完成使命为己任。在南昌期间,他先后兼任南昌保龄女子中学、豫章中学董事长。 1944 年,邻省广东适逢旱灾,事关人民生活疾苦,他当机立断,竭尽职能,为民请命,给予经济援救,以体恤和帮助缓解他们的危难。抗战胜利后,他逐辞卸裕民银行之职,转就中央银行顾问职务,兼任经济研究处专门委员、西北实业公司总经理等职。 1949 年随迁入台。 史世珍退休之后,仍不遗余力教育后人,受聘于铭传女子商业专科学校,担任教授。 史世珍有二女二男。长女史慧中毕业于金陵女子大学理学士。次女史惠珠,为美国麻州东北大学商学士,后又攻修医学,在美国行医多年。长子史怡中为美国南伊利诺大学哲学博士,先后任教于美、台、港等处之大学,并名列英国剑桥出版之名人录中。次子史台中先生为美国波士顿大学工商管理学系硕士,在美、台等地从事金融贸易。 1990 年,史世珍在台逝世,享年 91 岁。 (以下图片为任职时间) 1973 年拍摄 程雪邨 雪邨先生,一九 O 二年元月一日生于江西省鄱阳县新田垅,自号新田。一九七六年四月二十日病逝于南昌,享年七十有四。其父讳善孙,字则昌,又名步云,有诗作《隐志集》,育五子,雪邨先生为长子。先生览历丰富,坎坷一生。饶州中学毕业后,他考入上海华南大学攻读文学。一九二六年大学期间,他便加入中国共产党。一九二七年“四一二”事变后,他在党组织安排下离沪赴北平,负责文化宣传工作,出版首部诗集《汽笛颂》。一九三一年“九一八”事变后,为宣传抗日、反对内战,他被当局所注意,为避追捕,于一九三五年东渡日本留学。“卢沟桥事变”前夕回国,继续投身抗日救亡运动,发表不少诗作,如长诗《大雷雨》等。一九三八年回赣,入江西省抗敌后援会工作,主编《抗战月刊》。一九四 O 年转至浙江绍兴,主编抗战刊物《战旗》,因过度劳累,罹肺疾并被捕入狱,后被营救出狱,由绍兴回鄱阳,暂执教于芝阳师范。一九四一年二月,复因宣传抗日而被捕,被囚于鄱阳监狱。在狱中,得知四弟程翊(时任中共赣东北游击队司令)牺牲,五内俱裂,泣血草成长篇叙事诗《落花时节》。经担保出狱后,回芝阳师范执教,且一边养病。一九四九年五月鄱阳解放,被任命为江西省立鄱阳中学校长。一九五五年九月调入江西中等师资进修学校(后改名为南昌师范专科学校)任教,后辗转江西省文化局、江西省博物馆、江西省文艺学院工作。一九五六年,先生的《新田集》在《人民文学》(九月期)发表,影响很大,评价很高。一九五七年整风反右运动中,先生直言无忌,被打成右派分子。一九六六年后的“文化大革命”十年中,先生遭受很大冲击,历经磨难,被隔离、批斗、挂牌、游街、停薪、苦役。一九七三年三月,因砍柴而左腿骨摔成三截,肉体的摧残、精神的折磨,实难以言状,所赖尚有相濡以沫的妻子—耿松影老师的扶持,有儿女们的相伴,艰难地度过了古稀之年。不堪回首的岁月中,先生初衷不改,直面人生,吟啸从心,放歌依然,他曾慨言道:“我一生热爱诗,诗就是我的生命。一天我不吟诗、填词、度曲或写新诗,我就像丢了什么似的。”遗憾的是,“文化大革命”的风雨将要散去之时,而先生却未盼见拨乱反正的曙光,便永远地闭上了双目。 程济庭(1925-1996) 程济庭。中学高级教师。1925 年 8 月出生,波阳县鄱阳镇人。1956 年 6 月加入中国共产党。1944 年 7 月毕业于波阳中学高中部。1944 年 8 月任波阳中学职员,1946 年 3 月任波阳私立徽州小学教师,1946 年 9 月任波阳桃溪小学教师,1947 年 9 月任波阳中学教师。1954 年 9 月至 1955 年 8 月在江西省中等师资进修学校学习。1955 年 9 月任波阳一中教师、1958 年 4 月任教导主任。1969 年 9 月任波阳中学教师,1975 年 5 月任教导主任。1982 年 4 月任波阳一中校长,1983 年 4 月任波阳一中党总支副书记,1989 年 9 月退休。1996 年病逝。 程济庭在教育战线工作近 50 年,一贯忠于党的教育事业,为波阳教育事业的发展呕心呖血,付出了自己毕生的精力。能为人师表,处处表现了一个共产党员的高风亮节,为祖国培养了一大批栋梁之材,多次被评为教育战线的模范,出席波阳县教育先进代表大会,并于 1960 年 5 月出席江西省文教群英会,并获奖。 曹功先 曹功先 1935 年 9 月出生,波阳县团林乡人,1949 年 10 月参加工作,1984 年 6 月加人中国共产党。历任浮梁专区测绘学校学员、班长,青岛海校编辑、助教,江西省航运南京市建筑公司技术员、中学教员,波阳一中副校长、校长、波阳中学校长。1984 年 2 月任波阳县政协副主席,1984 年(1 月、1987 年 5 月和 1990 年 4 月分别当选为波阳县第八届、九届和十届人大常委会副主任。曾任江西省六届人民代表大会代表。 杨守仁 余孟灿 吴滋意 卓凡 孙晓平 参考 1. 陈元晖主编 璩鑫圭,童富勇,张守智编. 中国近代教育史资料汇编 实业教育 师范教育[M]. 2007 2. 《清代江西乡试研究》 3. 《光绪二十三年丁酉科十八省乡试同年全录》 4. 《光绪二十九年丁酉科十八省乡试同年全录》 5. 《鄱阳文史汇编》 6. 《鄱阳县志》 7. 《江西省地方人物志》 8. 《上饶地区综合年鉴_何细贵_主编_波阳中学》 9. 《余干县文史资料第 2 辑》 10. 《江西波阳中学校友史》 11. 《程雪邨诗集》 12. 黄埔军校军校网 13. 陈怀生主编;雷鸣鸿(博物馆),聂鹏云副主编;吴国林责任编辑;梅宁校对;《江西樟树风物系列丛书》编委会编. 转运纽 樟树盐事[M]. 2019.06,第 97 页 14. 徐南凯主编;中共波阳县委党史工作办公室编. 百年波阳人[M]. 2002 764 页 15. 景德镇陶瓷大学校史人物官网 16. 中国人民政治协商会议余干县委员会文史资料研究委员会. 余干县文史资料 第 7 辑 玉中专辑[M]. 1990 ------------------------------------------------------------ ## 鄱阳中学最早的照片及人物后记 **URL:** https://www.jinmokai.tech/blogs/鄱阳中学最早的照片 **Published:** 2023-04-08 **Updated:** 2023-04-08 **Tags:** 鄱阳中学, 鄱阳 **Summary:** 鄱阳中学历史目前已公开的最早的照片! 溯源 [//]: # (这张照片是目前关于鄱阳中学最早的照片,该照片出自赣州收藏家之手!根据收藏家描述,这张照片是民国时期流出的,当时鄱阳中学叫省立五中!这张照片三千一张后来公开在上饶市档案馆的百年映像二十年专栏。) 1924 年德兴旅饶同学会欢送朱(蕴华)李(锡畴)二君五中毕业摄影纪念 德兴学子在芝山合影(1924 年) 关于朱蕴华和李锡畴 我通过查阅《德兴县志》第 587 页找到了这张照片主人翁的历史 李锡畴,朱蕴华 - 1924 年江西省立五中毕业 - 毕业后回到县城,李锡畴担任银井小学校长,朱蕴华担任教导主任,并兼任德兴县党务特派员 - 1936 年李锡畴辞去小学校长当仁德兴党务干事 - 1948 年 3 月李锡畴病逝,职务由朱蕴华担任。 - 1949 年 5 月德兴县解放国民党垮台,朱蕴华后来不详 ------------------------------------------------------------ ## 鄱阳中学校址考究 **URL:** https://www.jinmokai.tech/blogs/鄱阳中学校址考究 **Published:** 2023-03-24 **Updated:** 2023-03-24 **Tags:** 鄱阳中学, 鄱阳 **Summary:** 鄱阳中学的校址考究很是烦恼,最短校址历史都过去几十年,地址模糊不清,花了大量时间考证才整理出来! 校址考究 + 1902-1934 饶州府中学堂 > 鄱阳茶条巷文昌宫-芝阳书院原址 > 校址:龙桥头(出自:《中国近代学制史料 第3辑 下》) > 具体位置:鄱阳茶条巷五一广场东侧(出自:《江西书院》286页) > 茶条巷芝阳书院,现:西门路与饶州大道交叉的东侧前有碑(出自:《文化鄱阳》198页) > > ~~查阅了鄱阳古城图与现实对比 (参考古图,和清代二级行政区治所城郭简图~~\n > ~~坐标是:五一中心学校东北角那块地方(纬度:28.997505 经度:116.683181)~~ + 1934 江西省立鄱阳初级中学 > 高门校舍 > 原因是原江西省立陶业学校由鄱阳迁至九江,高门校舍被鄱阳初级中学接收(第一次迁) > > 纬度:28.990307 经度:116.676135 + 1939-1945 江西省立鄱阳中学 > 迁至凰岗集镇 > 位置:凰岗河东(凰岗狮子山 经度117.005333 维度29.152696) > > 大礼堂:四院樟村 (待考证) > > 原因:抗日战争爆发,躲避灾难 + 1945 江西省立鄱阳中学 > 迁回高门 + 1952 江西省立鄱阳中学(分两部高门校址和东湖校址) > 一部:高门校址 > 二部:东湖校址 > 东湖校址具体位置:县城东湖荐福寺(出自:《鄱阳县志 下》960页) > 东湖校址也是原来的士行中学原址(出处:《江西学府志》486页) > 位置:现在的一中 纬度:28.990333 经度:116.699521 + 1968-1969 学校并入莲花山共产主义劳动大学 > 即 莲花山公社(莲华山包家垅 出自:鄱阳县志964页) > > 1958年公社化时称莲花山垦殖场;1961年分设横涌、新村、城墩、高沙公社和分场,隶属于侯家岗区;1968年扩社并队时称莲花山公社、莲花山垦殖场(场社合一);(莲华山包家垅其实是侯家岗乡) > > 具体位置:现在的侯家岗乡共大管委会 > > 原因:文革,教学停止。 + ~~1969 波阳一中~~ > ~~1969年,县城恢复波阳中学,因波阳一中校址高门被县化肥厂占用,故以原二中校址-县城东湖荐福寺~~ + 1985-至今 鄱阳中学 > 1982年波阳中学(东湖校址)改一中,原高门校址恢复波阳中学 侯家岗区 莲华山垦殖场公社 办公楼 ------------------------------------------------------------