<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Hexo</title>
  
  
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2025-10-11T09:39:21.406Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>John Doe</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>23年5月至24年5月总结</title>
    <link href="http://example.com/post/c5bccd64.html"/>
    <id>http://example.com/post/c5bccd64.html</id>
    <published>2024-05-07T08:42:25.000Z</published>
    <updated>2025-10-11T09:39:21.406Z</updated>
    
    <content type="html"><![CDATA[<p>今天失业算是一年了，写点总结想想这一年都干了啥。看了一下博客存档，这一年写的博客用手指头能数得过来。</p><span id="more"></span><h2 id="离职第一剑，先斩公司"><a href="#离职第一剑，先斩公司" class="headerlink" title="离职第一剑，先斩公司"></a>离职第一剑，先斩公司</h2><p>工人与老板存在天然矛盾，必须仲裁安排！<br>诉求：加班费 + 拖欠工资赔偿</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/laodongzhongcai.jpg"                        class="" title="劳动仲裁"                 ><h2 id="离开我工作的地方"><a href="#离开我工作的地方" class="headerlink" title="离开我工作的地方"></a>离开我工作的地方</h2><p>租房的街道和公司写字楼，准备骑单车蹬回家（半路家人来接我的）<br><img                           lazyload                       alt="image"                       data-src="/post/c5bccd64/gongzuojiedao-1.jpg"                         style="height: 300px"                 ></p><div style="display:flex"><img                           lazyload                       alt="image"                       data-src="/post/c5bccd64/gongzuojiedao-2.jpg"                         style="height: 300px"                 ><img                           lazyload                       alt="image"                       data-src="/post/c5bccd64/huijia.jpg"                         style="height: 300px"                 ></div><h2 id="回家种地"><a href="#回家种地" class="headerlink" title="回家种地"></a>回家种地</h2><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/fengjing-1.jpg"                        class="" title="风景-1"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/fengjing-2.jpg"                        class="" title="风景-2"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/fengjing-3.jpg"                        class="" title="风景-3"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/yumi.jpg"                        class="" title="风景-3"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/shanzha.jpg"                        class="" title="风景-3"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/lizi.jpg"                        class="" title="风景-3"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/ji.jpg"                        class="" title="风景-3"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/jidan.jpg"                        class="" title="风景-3"                 ><h2 id="分享学习资料被抓"><a href="#分享学习资料被抓" class="headerlink" title="分享学习资料被抓"></a>分享学习资料被抓</h2><p>在群里分享了点学习资料，被找上门来了<br>我猜是买到正版的同学气不过把我举报的</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/fenxiang.jpg"                        class="" title="分享"                 ><h2 id="找工作"><a href="#找工作" class="headerlink" title="找工作"></a>找工作</h2><p>总在家闲着不行，我得去找工作了<br>之前早走的同事入职新公司了，他说可以内推我，约好了面试<br>最终没谈拢，算了。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/tongshi.jpg"                        class="" title="同事内推"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/zhiweimiaoshu.jpg"                        class="" title="职位描述"                 ><h2 id="就业形势一片向好"><a href="#就业形势一片向好" class="headerlink" title="就业形势一片向好"></a>就业形势一片向好</h2><p>去济南找工作走了一圈，路过济南火车站，去图书馆补了一下山东火车的历史</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/jinan.jpg"                        class="" title="去济南火车站"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/boss.jpg"                        class="" title="boss直聘"                 ><p>找不到工作，看到群里群主都开始接单了，逐渐焦虑😥</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/qunzhu.jpg"                        class="" title="群主找不到工作"                 ><p>没工作在家喂喂猫，写点开源项目</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/zhaobudao.jpg"                        class="" title="喂猫"                 ><h2 id="在家闲鱼接单的日子"><a href="#在家闲鱼接单的日子" class="headerlink" title="在家闲鱼接单的日子"></a>在家闲鱼接单的日子</h2><p>没事干了，去闲鱼接点单，从最开始的帮配置开发环境开始做的，边做边在家闲玩<br>村里买的鸡腿带血，就这炸鸡腿水平还敢跟城里要一个价？</p><p>自己用微波炉做了，太简单了，叮3分钟就能吃了</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/jitui.jpg"                        class="" title="鸡腿"                 ><p>给家里添置一个热水器，这个闲鱼花200买的，原价1千多，苏州人真有钱😄</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/reshuiqi.jpg"                        class="" title="热水器"                 ><p>太阳能发电后鸣叫，说是赶鸟用的，娱乐一下</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/ganniao.jpg"                        class="" title="赶鸟"                 ><p>姨姥爷送的两只母兔子</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/tuzi.jpg"                        class="" title="兔子"                 ><p>给妹妹买点衣服，非要这个浅蓝色，也不管穿着舒不舒服，自己选的自己穿吧我不管了</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/maiyifu.jpg"                        class="" title="买衣服"                 ><p>家里的林地被推平了，说是有耕地指标，这里是耕地就得种粮食，不准栽树</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/tuilinhuangeng.jpg"                        class="" title="退林还耕"                 ><h2 id="接单稍有起色"><a href="#接单稍有起色" class="headerlink" title="接单稍有起色"></a>接单稍有起色</h2><p>有临沂的老板在闲鱼要找我驻场开发了，问了一下情况，说是前端那边遇到问题了让我去救场？自己解决吧，别让我跑空了</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/xianyu.jpg"                        class="" title="闲鱼接单"                 ><p>同时也在QQ技术群有兄弟拉我入伙，后来因为接单耽误了</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/qqwaibao.jpg"                        class="" title="QQ接单"                 ><p>内蒙古网友寄来特产，非常感谢，第一次吃到奶香味这么浓的千层酥和奶茶</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/neimeng.jpg"                        class="" title="内蒙网友"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/neimengtechan.jpg"                        class="" title="内蒙网友"                 ><p>后来有老板私聊让我做项目了，需求整理了一周，我们报价6w，老板同时找的人报价2-3w，直接把我吓傻了，后来还是找的我们。<br>跟之前合作过的兄弟一起接下来的，后面又找的之前公司同事做设计，做的不错</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/huiyijiedan.jpg"                        class="" title="会议接单"                 ><h2 id="项目做完出去溜溜"><a href="#项目做完出去溜溜" class="headerlink" title="项目做完出去溜溜"></a>项目做完出去溜溜</h2><p>找一起合作做项目的兄弟玩了，水平不错的<br>有本地人带着吃了顿真正的淄博烧烤，去古城逛了逛</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/zibo-1.jpg"                        class="" title="淄博"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/zibo-2.jpg"                        class="" title="淄博"                 ><p>路过潍坊，看到白浪河干了，说是被人工抽走的，潍坊要建地铁了？</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/bailanghe.jpg"                        class="" title="潍坊"                 ><h2 id="小宠物陆续生崽"><a href="#小宠物陆续生崽" class="headerlink" title="小宠物陆续生崽"></a>小宠物陆续生崽</h2><p>狗子，老猫，鸭子的小崽子们都出生了，日子越来越有盼头了</p><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/gouzai.jpg"                        class="" title="崽"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/maozai.jpg"                        class="" title="崽"                 ><img                         lazyload                       alt="image"                       data-src="/post/c5bccd64/yazai.jpg"                        class="" title="崽"                 ><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>今天写下总结，感觉1年时间也没多久，日子过得飞快。<br>总有人问我不工作么，实话讲工作没有意义，自己接单无非累点，倒也不至于饿死，留出来的闲空是很多的，目前感觉比上班划算（大概是我上班工资太低了）<br>今年年初就准备不找工作了，想做的事挺多的，最近准备抽出空来去深圳逛逛，因为机票300就能飞，还有其他原因…？还没想好😅<br>立志不到弹尽粮绝不找工作！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;今天失业算是一年了，写点总结想想这一年都干了啥。看了一下博客存档，这一年写的博客用手指头能数得过来。&lt;/p&gt;</summary>
    
    
    
    
    <category term="瞎扯" scheme="http://example.com/tags/%E7%9E%8E%E6%89%AF/"/>
    
  </entry>
  
  <entry>
    <title>小程序中实现自定义表单</title>
    <link href="http://example.com/post/2f44ad5.html"/>
    <id>http://example.com/post/2f44ad5.html</id>
    <published>2024-04-26T11:05:59.000Z</published>
    <updated>2025-10-11T09:39:21.798Z</updated>
    
    <content type="html"><![CDATA[<p>最近做的小程序关于表单的字段需要用户自定义编辑功能，这个表单编辑是整个项目的重点。</p><p>选择一个靠谱的表单编辑器，然后实现表单的数据处理是不得不深入研究的问题。</p><span id="more"></span><p>总的来说，自定义表单程序至少需要：设计器（支持拖拉拽修改各种参数）、数据管理（用户填写的表单数据进行处理）</p><h2 id="展示层面"><a href="#展示层面" class="headerlink" title="展示层面"></a>展示层面</h2><p>表单展示层面分为后台设计表单和用户填写表单页面。</p><h3 id="表单编辑器-form-create"><a href="#表单编辑器-form-create" class="headerlink" title="表单编辑器 form-create"></a>表单编辑器 form-create</h3><p>经过调研，我决定使用这款表单生成组件：form-create。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p>对，他是个组件，读取配置列表就可以生成不同类型的表单项，这种方式存储方便，展示也方便（没涉及复杂的功能）</p><blockquote><p><strong>form-create 是一个可以通过 JSON 生成具有动态渲染、数据收集、验证和提交功能的表单生成组件。支持5个UI框架，并且支持生成任何 Vue 组件。内置20种常用表单组件和自定义组件，再复杂的表单都可以轻松搞定。</strong></p></blockquote><p>form-create: <a class="link"   href="https://github.com/xaboy/form-create" >xaboy&#x2F;form-create: 强大的动态表单生成器|form-create is a form generation component that can generate dynamic rendering, data collection, verification and submission functions through JSON. (github.com)<i class="fas fa-external-link-alt"></i></a></p><img                         lazyload                       alt="image"                       data-src="/post/2f44ad5/demo-live3.gif"                        class="" title="demo1"                 ><p>项目简介中说到：支持5个UI框架，分别是：</p><ul><li><strong>element-plus</strong></li><li><strong>ant-design-vue</strong></li><li><strong>naive-ui</strong></li><li><strong>arco-design</strong></li><li><strong>tdesign</strong></li></ul><p>嗯，没有小程序版本，我们用的小程序是<code>Vue3 + uniapp</code>，项目作者虽说后续会支持小程序，但是我们等不了他了，自己手动实现好了。</p><p><strong>实现思路大概是：编辑出来表单JSON，前台去读取渲染</strong></p><p>其中作者实现了编辑器，通过form-create组件去渲染表单。</p><p>form-create-designer: <a class="link"   href="https://github.com/xaboy/form-create-designer" >xaboy&#x2F;form-create-designer: 好用的vue可视化表单设计器 (github.com)<i class="fas fa-external-link-alt"></i></a></p><img                         lazyload                       alt="image"                       data-src="/post/2f44ad5/687474703a2f2f666f726d2d6372656174652e636f6d2f696d672f64657369676e65722d7265766965772e706e67"                        class="" title="demo1"                 ><p>编辑器预览的样式是<code>element-ui</code>的，无所谓，我们用的是小程序，能生成配置，让我们小程序读到就可以了。</p><p>输入框JSON格式示例：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    type<span class="punctuation">:</span> <span class="string">&quot;input&quot;</span><span class="punctuation">,</span></span><br><span class="line">    title<span class="punctuation">:</span> <span class="string">&quot;商品名称&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="comment">//label名称</span></span><br><span class="line">    field<span class="punctuation">:</span> <span class="string">&quot;goods_name&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="comment">//字段名称</span></span><br><span class="line">    value<span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="comment">//input值,</span></span><br><span class="line">    props<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;text&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//输入框类型，可选值为 text、password、textarea、url、email、date</span></span><br><span class="line">      <span class="attr">&quot;clearable&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//是否显示清空按钮</span></span><br><span class="line">      <span class="attr">&quot;disabled&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//设置输入框为禁用状态</span></span><br><span class="line">      <span class="attr">&quot;readonly&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//设置输入框为只读</span></span><br><span class="line">      <span class="attr">&quot;rows&quot;</span><span class="punctuation">:</span> <span class="number">4</span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//文本域默认行数，仅在 textarea 类型下有效</span></span><br><span class="line">      <span class="attr">&quot;autosize&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//自适应内容高度，仅在 textarea 类型下有效，可传入对象，如 &#123; minRows: 2, maxRows: 6 &#125;</span></span><br><span class="line">      <span class="attr">&quot;number&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//将用户的输入转换为 Number 类型</span></span><br><span class="line">      <span class="attr">&quot;autofocus&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//自动获取焦点</span></span><br><span class="line">      <span class="attr">&quot;autocomplete&quot;</span><span class="punctuation">:</span> <span class="string">&quot;off&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//原生的自动完成功能，可选值为 off 和 on</span></span><br><span class="line">      <span class="attr">&quot;placeholder&quot;</span><span class="punctuation">:</span> <span class="string">&quot;请输入商品名称&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//占位文本</span></span><br><span class="line">      <span class="attr">&quot;size&quot;</span><span class="punctuation">:</span> <span class="string">&quot;default&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//输入框尺寸，可选值为large、small、default或者不设置,</span></span><br><span class="line">      <span class="attr">&quot;spellcheck&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="comment">//原生的 spellcheck 属性</span></span><br><span class="line">      <span class="attr">&quot;required&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    validate<span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">&#123;</span></span><br><span class="line">      required<span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">      message<span class="punctuation">:</span> &#x27;请输入商品名称&#x27;<span class="punctuation">,</span></span><br><span class="line">      trigger<span class="punctuation">:</span> &#x27;blur&#x27;</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>其中<code>props</code>在编辑器面板右侧可以编码处理，这个是我定义的姓名表单项：用户可以配置placeholder，是否可清空，是否禁用，是否只读……. 当然你可以加任何属性，业务上的也不耽误渲染。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> uniqueId <span class="keyword">from</span> <span class="string">&#x27;@form-create/utils/lib/unique&#x27;</span></span><br><span class="line"><span class="keyword">import</span> &#123; localeProps, makeRequiredRule, makeHiddenRule &#125; <span class="keyword">from</span> <span class="string">&#x27;@/utils&#x27;</span></span><br><span class="line"><span class="keyword">import</span> phoneCmp <span class="keyword">from</span> <span class="string">&#x27;@/components/form-item/phone.vue&#x27;</span></span><br><span class="line"><span class="keyword">const</span> label = <span class="string">&#x27;姓名&#x27;</span></span><br><span class="line"><span class="keyword">const</span> name = <span class="string">&#x27;name&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  <span class="attr">icon</span>: <span class="string">&#x27;icon-input&#x27;</span>,</span><br><span class="line">  label,</span><br><span class="line">  name,</span><br><span class="line">  <span class="title function_">rule</span>(<span class="params">&#123; t &#125;</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      <span class="attr">type</span>: name,</span><br><span class="line">      <span class="attr">field</span>: <span class="title function_">uniqueId</span>(),</span><br><span class="line">      <span class="attr">title</span>: label,</span><br><span class="line">      <span class="attr">info</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">      <span class="attr">$required</span>: <span class="literal">false</span>,</span><br><span class="line">      <span class="attr">props</span>: &#123;&#125;,</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">component</span>: phoneCmp,</span><br><span class="line">  <span class="title function_">props</span>(<span class="params">_, &#123; t &#125;</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">localeProps</span>(t, name + <span class="string">&#x27;.props&#x27;</span>, [</span><br><span class="line">      <span class="title function_">makeRequiredRule</span>(),</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&#x27;input&#x27;</span>,</span><br><span class="line">        <span class="attr">field</span>: <span class="string">&#x27;placeholder&#x27;</span>,</span><br><span class="line">        <span class="attr">title</span>: <span class="string">&#x27;输入框占位文本&#x27;</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123; <span class="attr">type</span>: <span class="string">&#x27;switch&#x27;</span>, <span class="attr">field</span>: <span class="string">&#x27;clearable&#x27;</span>, <span class="attr">title</span>: <span class="string">&#x27;是否可清空&#x27;</span> &#125;,</span><br><span class="line">      &#123; <span class="attr">type</span>: <span class="string">&#x27;switch&#x27;</span>, <span class="attr">field</span>: <span class="string">&#x27;disabled&#x27;</span>, <span class="attr">title</span>: <span class="string">&#x27;禁用&#x27;</span> &#125;,</span><br><span class="line">      &#123; <span class="attr">type</span>: <span class="string">&#x27;switch&#x27;</span>, <span class="attr">field</span>: <span class="string">&#x27;readonly&#x27;</span>, <span class="attr">title</span>: <span class="string">&#x27;是否只读&#x27;</span> &#125;,</span><br><span class="line">    ])</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="uniapp-小程序端"><a href="#uniapp-小程序端" class="headerlink" title="uniapp 小程序端"></a><code>uniapp</code> 小程序端</h3><p>小程序端因为设计器没有实现UI库适配，我使用相对稳定一些的<code>uni-ui</code>，相对比其他的功能少点，样式差点，不过这都不重要。</p><p>基本上，拿到了配置后走个<code>v-for</code>循环渲染一下表单项就可以了，大概是：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- diy-form --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">uni-forms</span> <span class="attr">ref</span>=<span class="string">&quot;baseFormRef&quot;</span> <span class="attr">label-position</span>=<span class="string">&quot;top&quot;</span> <span class="attr">:rules</span>=<span class="string">&quot;diyRules&quot;</span> <span class="attr">:modelValue</span>=<span class="string">&quot;diyFormPayload&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">formItem</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-for</span>=<span class="string">&quot;item in formList&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:key</span>=<span class="string">&quot;item.id&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:field</span>=<span class="string">&quot;item&quot;</span></span></span><br><span class="line"><span class="tag">      @<span class="attr">change-data</span>=<span class="string">&quot;changeOtherData&quot;</span></span></span><br><span class="line"><span class="tag">    /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">uni-forms</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br></pre></td></tr></table></figure><p><code>uni-forms</code>可以接收我自定义的<code>rule</code>，表单规则是根据生成的配置进行生成的，在尽量适配<code>uni-ui</code>的方式去实现，比如<code>required</code>，邮箱、身份证、手机号之类的验证，都是根据表单项中的参数来确定的。</p><p>对于表单项组件，为各种表单项添加<code>v-if</code>进行类型判断分别渲染。</p><p>重点说一下数据的流动：</p><p><code>uni-forms</code>动态传入的<code>rule</code>，需要在<code>onReady</code>之后触发<code>setRules()</code>方法（最早要晚于<code>ready</code>）。</p><p>所以在生成自定义rule后要调用一下<code>setRules()</code>，但是请求可能早于<code>onReady</code>，那就在<code>onReady</code>同时调用一次即可。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">onReady</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="title function_">initForm</span>()</span><br><span class="line">&#125;)</span><br><span class="line"><span class="title function_">watch</span>(formList, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="title function_">initForm</span>()</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>从<code>diy-form</code>传来的<code>field</code>，经过<code>computed</code>处理一层统一适配<code>form-item</code></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// form-item.vue</span></span><br><span class="line"><span class="keyword">const</span> _field = <span class="title function_">computed</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> field = props.<span class="property">field</span></span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    ...field,</span><br><span class="line">    ...(field?.<span class="property">props</span> || &#123;&#125;),</span><br><span class="line">    <span class="attr">required</span>: field.<span class="property">$required</span> != <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">readonly</span>: field?.<span class="property">props</span>?.<span class="property">readonly</span>,</span><br><span class="line">    <span class="attr">disabled</span>: field?.<span class="property">props</span>?.<span class="property">disabled</span> || field?.<span class="property">props</span>?.<span class="property">readonly</span> || <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">placeholder</span>: field?.<span class="property">props</span>?.<span class="property">placeholder</span> || <span class="string">`请输入<span class="subst">$&#123;field.title&#125;</span>`</span>,</span><br><span class="line">    <span class="attr">type</span>: field.<span class="property">type</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>这里的<code>formVal</code>是要绑定到表单项的，表单项的值要通过刚才说的<code>uni-forms</code>中绑定的<code>modelValue</code>中获取，所以为了不用统一传递，我直接从<code>uni-forms</code>中读取了。</p><p>修改也同样，直接改<code>uni-forms</code>中的<code>modelValue</code>即可。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> emit = <span class="title function_">defineEmits</span>([<span class="string">&#x27;changeData&#x27;</span>])</span><br><span class="line"><span class="keyword">const</span> formVal = <span class="title function_">computed</span>(&#123;</span><br><span class="line">  <span class="attr">get</span>: <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> form = inject&lt;<span class="built_in">any</span>&gt;(<span class="string">&#x27;uniForm&#x27;</span>)</span><br><span class="line">    <span class="keyword">let</span> formVal = form.<span class="title function_">_getDataValue</span>(_field.<span class="property">value</span>.<span class="property">field</span>, form.<span class="property">localData</span>)</span><br><span class="line">    <span class="keyword">return</span> formVal</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">set</span>: <span class="function">(<span class="params">val</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="title function_">emit</span>(<span class="string">&#x27;changeData&#x27;</span>, _field.<span class="property">value</span>.<span class="property">field</span>, val)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>最后，给<code>diy-form</code>组件暴露两个方法：验证表单和更新表单数据。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">defineExpose</span>(&#123;</span><br><span class="line">  <span class="attr">validate</span>: <span class="title function_">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="comment">// 验证表单规则，最终返回用户表单数据</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">updateData</span>: <span class="function">(<span class="params">data</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 更新表单中的值</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h2 id="数据管理"><a href="#数据管理" class="headerlink" title="数据管理"></a>数据管理</h2><p>关于结构，分为：Form(表单模型)，FormField(表单模型字段)，FormVal(表单数据主表)，FormFieldVal(表单字段数据)</p><img                         lazyload                       alt="image"                       data-src="/post/2f44ad5/graph.png"                        class="" title="ER图"                 ><blockquote><p>使用到的工具：<a class="link"   href="https://drawdb.vercel.app/" >drawDB<i class="fas fa-external-link-alt"></i></a></p></blockquote><p>其中<code>表单模型</code>存放表单主体，<code>表单模型字段</code>存储该表单中的所有字段<br><code>表单数据主表</code>存放每个用户的表单数据，<code>表单字段数据</code>存放用户实际填写的表单数据</p><h3 id="数据存储"><a href="#数据存储" class="headerlink" title="数据存储"></a>数据存储</h3><p>前台获取表单时，查询<code>表单模型</code>所关联的业务ID，对应根据<code>表单模型</code>查出相应字段。<br>提交表单时，组装表单字段和表单值，后端通过前台提交的<code>表单字段</code>中的key查询对应表单字段，该key是自定义表单随机生成的，具有唯一性。整理后一同放入<code>表单数据主表</code>和<code>字段数据表</code>中。</p><p>后台编辑表单时，同样的对<code>表单模型</code>和<code>表单模型字段联表</code>管理。<br>查看用户表单数据，对<code>表单数据主表</code>和<code>表单字段数据联表</code>查出即可。</p><p>另外，对于前端提交的表单，需要进行规则验证，这里采取的方法是按照表单字段表中的type进行分类，去映射thinkphp自带规则以及自定义的规则，比较简单不贴代码了。</p><h3 id="更新表单字段"><a href="#更新表单字段" class="headerlink" title="更新表单字段"></a>更新表单字段</h3><p>对于后台编辑表单字段，保存时，有几种情况：</p><ul><li>仅修改排序、字段属性</li><li>删掉表单某个字段</li><li>新增字段</li></ul><p>最开始思路是直接把旧字段全部删掉，然后把现有字段添加上去。<br>但这样会导致问题：之前填写过的数据将会全都查不出来。<br>我们希望删掉的字段查不出来无所谓，但是没删过的数据要能查到。<br>所以加个diff：新与旧比较</p><ul><li>现有字段则更新</li><li>新字段没有则删掉</li><li>新字段多出来则添加</li></ul><p>附上代码</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 更新表单字段</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">updateFormField</span>(<span class="params"><span class="variable">$payload</span></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="variable">$form_id</span> = <span class="variable">$payload</span>[<span class="string">&#x27;form_id&#x27;</span>];</span><br><span class="line">    <span class="variable">$form_list</span> = <span class="variable">$payload</span>[<span class="string">&#x27;form_list&#x27;</span>];</span><br><span class="line">    </span><br><span class="line">    <span class="variable">$formFieldDao</span> = <span class="keyword">new</span> <span class="title class_">FormField</span>();</span><br><span class="line">    <span class="comment">// 查询之前所有的字段，如果存在则更新，不存在则删除</span></span><br><span class="line">    <span class="variable">$lastFields</span> = <span class="variable">$formFieldDao</span>-&gt;<span class="title function_ invoke__">where</span>(<span class="string">&#x27;form_id&#x27;</span>, <span class="variable">$form_id</span>)-&gt;<span class="title function_ invoke__">select</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算新增字段，旧字段中不存在的新字段</span></span><br><span class="line">    <span class="variable">$newFields</span> = [];</span><br><span class="line">    <span class="keyword">foreach</span> (<span class="variable">$form_list</span> <span class="keyword">as</span> <span class="variable">$idx</span> =&gt; <span class="variable">$item</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">in_array</span>(<span class="variable">$item</span>[<span class="string">&#x27;field&#x27;</span>], <span class="title function_ invoke__">array_column</span>(<span class="variable">$lastFields</span>, <span class="string">&#x27;key&#x27;</span>))) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="variable">$newFields</span>[] = <span class="variable">$item</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// newFields一定是新增的</span></span><br><span class="line">    <span class="keyword">foreach</span> (<span class="variable">$newFields</span> <span class="keyword">as</span> <span class="variable">$field</span>) &#123;</span><br><span class="line">        <span class="variable">$formFieldDao</span></span><br><span class="line">          -&gt;<span class="title function_ invoke__">allowField</span>(<span class="literal">true</span>)</span><br><span class="line">          -&gt;<span class="title function_ invoke__">insert</span>([</span><br><span class="line">            <span class="string">&#x27;type&#x27;</span> =&gt; <span class="variable">$field</span>[<span class="string">&#x27;type&#x27;</span>],</span><br><span class="line">            <span class="string">&#x27;form_id&#x27;</span> =&gt; <span class="variable">$form_id</span>,</span><br><span class="line">            <span class="string">&#x27;sort&#x27;</span> =&gt; <span class="variable">$idx</span>,</span><br><span class="line">            <span class="string">&#x27;key&#x27;</span> =&gt; <span class="variable">$field</span>[<span class="string">&#x27;field&#x27;</span>],</span><br><span class="line">            <span class="string">&#x27;createtime&#x27;</span> =&gt; <span class="title function_ invoke__">time</span>(),</span><br><span class="line">            <span class="string">&#x27;payload&#x27;</span> =&gt; <span class="title function_ invoke__">json_encode</span>(<span class="variable">$field</span>, JSON_UNESCAPED_UNICODE),</span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">foreach</span> (<span class="variable">$form_list</span> <span class="keyword">as</span> <span class="variable">$idx</span> =&gt; <span class="variable">$item</span>) &#123;</span><br><span class="line">        <span class="comment">// 此处更新</span></span><br><span class="line">        <span class="variable">$formFieldDao</span></span><br><span class="line">          -&gt;<span class="title function_ invoke__">where</span>(<span class="string">&#x27;form_id&#x27;</span>, <span class="variable">$form_id</span>)</span><br><span class="line">          -&gt;<span class="title function_ invoke__">where</span>(<span class="string">&#x27;key&#x27;</span>, <span class="variable">$item</span>[<span class="string">&#x27;field&#x27;</span>])</span><br><span class="line">          -&gt;<span class="title function_ invoke__">update</span>([</span><br><span class="line">            <span class="string">&#x27;payload&#x27;</span> =&gt; <span class="title function_ invoke__">json_encode</span>(<span class="variable">$item</span>, JSON_UNESCAPED_UNICODE),</span><br><span class="line">            <span class="string">&#x27;sort&#x27;</span> =&gt; <span class="variable">$idx</span></span><br><span class="line">        ]);</span><br><span class="line">        <span class="comment">// 过滤掉更新过的</span></span><br><span class="line">        <span class="variable">$lastFields</span> = <span class="title function_ invoke__">array_filter</span>(<span class="variable">$lastFields</span>, </span><br><span class="line">          function (<span class="variable">$field</span>) <span class="keyword">use</span> ($<span class="title">item</span>) &#123;</span><br><span class="line">            <span class="title">return</span> $<span class="title">field</span>[&#x27;<span class="title">key</span>&#x27;] != $<span class="title">item</span>[&#x27;<span class="title">field</span>&#x27;];</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// lastFields一定是删除的</span></span><br><span class="line">    <span class="keyword">foreach</span> (<span class="variable">$lastFields</span> <span class="keyword">as</span> <span class="variable">$field</span>) &#123;</span><br><span class="line">        <span class="variable">$formFieldDao</span>-&gt;<span class="title function_ invoke__">where</span>(<span class="string">&#x27;id&#x27;</span>, <span class="variable">$field</span>[<span class="string">&#x27;id&#x27;</span>])-&gt;<span class="title function_ invoke__">delete</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 旧版直接删掉之前的数据，然后添加新数据（已废弃）</span></span><br><span class="line">    <span class="comment">// if (isset ($form_id) &amp;&amp; $form_id != &#x27;&#x27;) &#123;</span></span><br><span class="line">    <span class="comment">//     $formFieldDao-&gt;where(&#x27;form_id&#x27;, $form_id)-&gt;delete();</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">    <span class="comment">// $toInsertData = [];</span></span><br><span class="line">    <span class="comment">// foreach ($form_list as $idx =&gt; $item) &#123;</span></span><br><span class="line">    <span class="comment">//     $toInsertData[] = [</span></span><br><span class="line">    <span class="comment">//         &#x27;type&#x27; =&gt; $item[&#x27;type&#x27;],</span></span><br><span class="line">    <span class="comment">//         &#x27;form_id&#x27; =&gt; $form_id,</span></span><br><span class="line">    <span class="comment">//         &#x27;sort&#x27; =&gt; $idx,</span></span><br><span class="line">    <span class="comment">//         &#x27;key&#x27; =&gt; $item[&#x27;field&#x27;],</span></span><br><span class="line">    <span class="comment">//         &#x27;createtime&#x27; =&gt; time(),</span></span><br><span class="line">    <span class="comment">//         &#x27;payload&#x27; =&gt; json_encode($item, JSON_UNESCAPED_UNICODE),</span></span><br><span class="line">    <span class="comment">//     ];</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">    <span class="comment">// $formFieldDao-&gt;allowField(true)-&gt;insertAll($toInsertData);</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>关于后台编辑表单这里，最完美的解决方案应该是表单项删掉也不会导致查不出来数据，这让我想到直接隐藏即可，不是假删除，而是隐藏字段，后台也能看到被隐藏的字段，不过是前台不显示而已。<br>客户说存储空间有限，做成强制删除的，留一个隐藏按钮。作为两个方案的混合方案，这就结束了。</p><p>不足的地方：</p><ul><li>表单规则应该支持自定义</li><li>适配更多字段</li><li>表单页面性能优化</li><li>后端安全防范</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近做的小程序关于表单的字段需要用户自定义编辑功能，这个表单编辑是整个项目的重点。&lt;/p&gt;
&lt;p&gt;选择一个靠谱的表单编辑器，然后实现表单的数据处理是不得不深入研究的问题。&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
    <category term="vue" scheme="http://example.com/tags/vue/"/>
    
    <category term="web" scheme="http://example.com/tags/web/"/>
    
  </entry>
  
  <entry>
    <title>解决thinkphp自定义404页面无反应问题</title>
    <link href="http://example.com/post/8c748b50.html"/>
    <id>http://example.com/post/8c748b50.html</id>
    <published>2023-12-28T08:45:59.000Z</published>
    <updated>2025-10-11T09:39:21.862Z</updated>
    
    <content type="html"><![CDATA[<p>最近在做php，发现添加了自定义404页面模板后没反应，仍然使用默认异常模板，查了一下源码才知道问题所在，都是自己惯性思维惹的祸。</p><span id="more"></span><p>文档说明：</p><p><a class="link"   href="https://www.kancloud.cn/manual/thinkphp5_1/354092#HTTP__164" >异常处理 · ThinkPHP5.1完全开发手册 · 看云 (kancloud.cn)<i class="fas fa-external-link-alt"></i></a></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// \thinkphp\helper.php</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">function_exists</span>(<span class="string">&#x27;abort&#x27;</span>)) &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 抛出HTTP异常</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> integer|Response      $code 状态码 或者 Response对象实例</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> string                $message 错误信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> array                 $header 参数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">abort</span>(<span class="params"><span class="variable">$code</span>, <span class="variable">$message</span> = <span class="literal">null</span>, <span class="variable">$header</span> = []</span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$code</span> <span class="keyword">instanceof</span> Response) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpResponseException</span>(<span class="variable">$code</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpException</span>(<span class="variable">$code</span>, <span class="variable">$message</span>, <span class="literal">null</span>, <span class="variable">$header</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用<code>thinkphp</code>的<code>abort</code>函数可以方便地创建HTTP异常，从这里入手，看一下<code>thinkphp</code>是怎么处理http异常的。</p><p>这里通过<code>$code</code>区分了一下，相当于是实现了函数重载，<code>$code</code>参数是404，走<code>HttpException</code>。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HttpException</span> <span class="keyword">extends</span> \<span class="title">RuntimeException</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="variable">$statusCode</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="variable">$headers</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span>(<span class="params"><span class="variable">$statusCode</span>, <span class="variable">$message</span> = <span class="literal">null</span>, \<span class="built_in">Exception</span> <span class="variable">$previous</span> = <span class="literal">null</span>, <span class="keyword">array</span> <span class="variable">$headers</span> = [], <span class="variable">$code</span> = <span class="number">0</span></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="variable language_">$this</span>-&gt;statusCode = <span class="variable">$statusCode</span>;</span><br><span class="line">        <span class="variable language_">$this</span>-&gt;headers    = <span class="variable">$headers</span>;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">parent</span>::<span class="title function_ invoke__">__construct</span>(<span class="variable">$message</span>, <span class="variable">$code</span>, <span class="variable">$previous</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>初始化之后，会自动被<code>think\exception\Handle</code>处理，调用内部<code>render</code>方法，</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="keyword">namespace</span> <span class="title class_">think</span>\<span class="title class_">exception</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Handle</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Render an exception into an HTTP response.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span>  \Exception $e</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> Response</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">render</span>(<span class="params"><span class="built_in">Exception</span> <span class="variable">$e</span></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">$this</span>-&gt;render &amp;&amp; <span class="variable language_">$this</span>-&gt;render <span class="keyword">instanceof</span> \<span class="built_in">Closure</span>) &#123;</span><br><span class="line">            <span class="variable">$result</span> = <span class="title function_ invoke__">call_user_func_array</span>(<span class="variable">$this</span>-&gt;render, [<span class="variable">$e</span>]);</span><br><span class="line">            <span class="keyword">if</span> (<span class="variable">$result</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="variable">$result</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$e</span> <span class="keyword">instanceof</span> HttpException) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">$this</span>-&gt;<span class="title function_ invoke__">renderHttpException</span>(<span class="variable">$e</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">$this</span>-&gt;<span class="title function_ invoke__">convertExceptionToResponse</span>(<span class="variable">$e</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> HttpException $e</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> Response</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="function"><span class="keyword">function</span> <span class="title">renderHttpException</span>(<span class="params">HttpException <span class="variable">$e</span></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="variable">$status</span>   = <span class="variable">$e</span>-&gt;<span class="title function_ invoke__">getStatusCode</span>();</span><br><span class="line">        <span class="variable">$template</span> = <span class="title class_">Config</span>::<span class="title function_ invoke__">get</span>(<span class="string">&#x27;http_exception_template&#x27;</span>);</span><br><span class="line">        <span class="keyword">if</span> (!<span class="title class_">App</span>::<span class="variable">$debug</span> &amp;&amp; !<span class="keyword">empty</span>(<span class="variable">$template</span>[<span class="variable">$status</span>])) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="title class_">Response</span>::<span class="title function_ invoke__">create</span>(<span class="variable">$template</span>[<span class="variable">$status</span>], <span class="string">&#x27;view&#x27;</span>, <span class="variable">$status</span>)-&gt;<span class="title function_ invoke__">assign</span>([<span class="string">&#x27;e&#x27;</span> =&gt; <span class="variable">$e</span>]);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">$this</span>-&gt;<span class="title function_ invoke__">convertExceptionToResponse</span>(<span class="variable">$e</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时通过异常类型的判断，继续走<code>renderHttpException</code>，这个方法已经能够说明问题了，如果当前不是debug模式并且能够匹配到有对应模板，则会去找对应模板输出。显然我这里是走了else了，输出<code>App::$debug</code>值为<code>&#39;1&#39;</code>，但是我明明设置了<code>$debug=false</code>。继续查看<code>$debug</code>来源，发现初始化<code>$debug</code>是这样的：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">static</span> <span class="function"><span class="keyword">function</span> <span class="title">initCommon</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">// 初始化应用</span></span><br><span class="line">        <span class="variable">$config</span>       = <span class="built_in">self</span>::<span class="title function_ invoke__">init</span>();</span><br><span class="line">        <span class="built_in">self</span>::<span class="variable">$suffix</span> = <span class="variable">$config</span>[<span class="string">&#x27;class_suffix&#x27;</span>];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 应用调试模式</span></span><br><span class="line">        <span class="built_in">self</span>::<span class="variable">$debug</span> = <span class="title class_">Env</span>::<span class="title function_ invoke__">get</span>(<span class="string">&#x27;app_debug&#x27;</span>, <span class="title class_">Config</span>::<span class="title function_ invoke__">get</span>(<span class="string">&#x27;app_debug&#x27;</span>));</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>先读取<code>env</code>，再读取<code>config.php</code>，哦原来是我<code>config.php</code>写死之后，<code>.env</code>文件没有删掉。</p><p>在<code>thinkphp</code>官方文档其实有写：</p><blockquote><p>环境变量中设置的<code>APP_DEBUG</code>和<code>APP_TRACE</code>参数会自动生效（优先于应用的配置文件），其它参数则必须通过<code>Env::get</code>方法才能读取。</p></blockquote>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近在做php，发现添加了自定义404页面模板后没反应，仍然使用默认异常模板，查了一下源码才知道问题所在，都是自己惯性思维惹的祸。&lt;/p&gt;</summary>
    
    
    
    
    <category term="php" scheme="http://example.com/tags/php/"/>
    
  </entry>
  
  <entry>
    <title>使用nextjs编写个人博客</title>
    <link href="http://example.com/post/f2f67e66.html"/>
    <id>http://example.com/post/f2f67e66.html</id>
    <published>2023-07-24T07:06:32.000Z</published>
    <updated>2025-10-11T09:39:21.662Z</updated>
    
    <content type="html"><![CDATA[<p>MDX是一种将Markdown和JSX结合的语法，可以在Markdown中使用JSX，一直想独立编写一个博客平台，这次就用nextjs+MDX来写一个博客平台吧。</p><span id="more"></span><h2 id="使用Next-js开发博客平台的主要步骤"><a href="#使用Next-js开发博客平台的主要步骤" class="headerlink" title="使用Next.js开发博客平台的主要步骤"></a>使用Next.js开发博客平台的主要步骤</h2><ol><li><p><strong>使用Next.js创建项目</strong></p><p> 使用<code>pnpm create next-app</code>命令可以快速创建一个Next.js项目。</p></li><li><p><strong>设计目录结构</strong></p><p> 使用Next.js提供的app目录结构。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script></li><li><p><strong>实现Markdown解析</strong></p><p> 可以使用<code>next-mdx-remote</code>等MDX解析库,将MDX文件解析为ReactNode。</p></li><li><p><strong>静态页面生成</strong></p><p> 使用<code>generateStaticParams</code>实现文章静态页生成。</p></li><li><p><strong>添加样式</strong></p><p> 可以使用SASS或tailwindcss给博客及mdx元素添加样式。 </p></li><li><p><strong>部署上线</strong></p><p> 可以部署到Vercel、Netlify等平台上。</p></li></ol><p>Next.js的静态生成和路由功能可以方便实现一个简单的博客平台。</p><h2 id="项目初始化"><a href="#项目初始化" class="headerlink" title="项目初始化"></a>项目初始化</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pnpm create next-app</span><br></pre></td></tr></table></figure><p>默认创建的项目是nextjs 13.4版本(2023-7-20)，是最新使用<code>app router</code>的版本，相较于之前<code>page router</code>有些改动。</p><h2 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h2><p>由于我的文章文件名都是中文的，不利于网页的SEO，所以我为每一个文章通过文件名生成唯一id，使用id作为文章的路由。<br>目录结构大致是：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">app:</span><br><span class="line">├─(site)</span><br><span class="line">│  │  layout.tsx</span><br><span class="line">│  │  page.tsx</span><br><span class="line">│  │</span><br><span class="line">    # 文章列表页</span><br><span class="line">│  └─post</span><br><span class="line">│      │  header.tsx</span><br><span class="line">│      │  layout.tsx</span><br><span class="line">│      │  page.tsx</span><br><span class="line">│      │</span><br><span class="line">        # 文章详情页</span><br><span class="line">│      └─[slug]</span><br><span class="line">│              not-found.tsx</span><br><span class="line">│              page.tsx</span><br><span class="line">│</span><br><span class="line">└─data</span><br><span class="line">        # 解析mdx文件</span><br><span class="line">        post.ts</span><br></pre></td></tr></table></figure><p>src:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">├─assets</span><br><span class="line">│  │  globals.scss</span><br><span class="line">│  │  md-el.scss</span><br><span class="line">│  │</span><br><span class="line">│  └─icons</span><br><span class="line">│</span><br><span class="line">├─components</span><br><span class="line"></span><br><span class="line">├─lib</span><br><span class="line">│      mdx.ts</span><br><span class="line">│</span><br><span class="line">├─posts</span><br><span class="line">│      《函数式编程指北》练习题速通指南.mdx</span><br><span class="line">│      使用chatgpt编写小说.mdx</span><br><span class="line">│</span><br><span class="line">└─utils</span><br></pre></td></tr></table></figure><h2 id="实现Markdown解析"><a href="#实现Markdown解析" class="headerlink" title="实现Markdown解析"></a>实现Markdown解析</h2><p>实现了两个函数，获取所有meta数据和获取文章内容。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> rootDirectory = path.<span class="title function_">join</span>(process.<span class="title function_">cwd</span>(), <span class="string">&#x27;src&#x27;</span>, <span class="string">&#x27;posts&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过文件名获取文章</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">getPostBySlug</span> = <span class="keyword">async</span> (<span class="params"><span class="attr">slug</span>: <span class="built_in">string</span></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> realSlug = slug.<span class="title function_">replace</span>(<span class="regexp">/\.mdx$/</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> filePath = path.<span class="title function_">join</span>(rootDirectory, <span class="string">`<span class="subst">$&#123;realSlug&#125;</span>.mdx`</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> fileContent = fs.<span class="title function_">readFileSync</span>(filePath, &#123; <span class="attr">encoding</span>: <span class="string">&#x27;utf8&#x27;</span> &#125;)</span><br><span class="line">  <span class="keyword">const</span> &#123; data, content &#125; = <span class="title function_">matter</span>(fileContent)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> id = <span class="title function_">crc32</span>(realSlug).<span class="title function_">toString</span>(<span class="number">16</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> &#123; <span class="attr">meta</span>: &#123; ...data, <span class="attr">slug</span>: realSlug, id, <span class="attr">readingTime</span>: <span class="title function_">readingTime</span>(content) &#125;, <span class="attr">content</span>: fileContent &#125; <span class="keyword">as</span> <span class="title class_">PostDetail</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取所有文章meta数据</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">getAllPostsMeta</span> = <span class="keyword">async</span> (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> files = fs.<span class="title function_">readdirSync</span>(rootDirectory)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> metas = <span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>(files.<span class="title function_">map</span>(<span class="keyword">async</span> filename =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; meta &#125; = <span class="keyword">await</span> <span class="title function_">getPostBySlug</span>(filename)</span><br><span class="line">    <span class="keyword">return</span> meta</span><br><span class="line">  &#125;))</span><br><span class="line">  <span class="keyword">return</span> metas</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过id获取文章详情</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">getPost</span> = <span class="keyword">async</span> (<span class="params"><span class="attr">id</span>: <span class="built_in">string</span></span>) =&gt; &#123;</span><br><span class="line">  <span class="comment">// id to slug</span></span><br><span class="line">  <span class="keyword">const</span> slug = <span class="keyword">await</span> <span class="title function_">getPostName</span>(id);</span><br><span class="line">  <span class="keyword">if</span> (!slug) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&#x27;not found&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> post = <span class="keyword">await</span> <span class="title function_">getPostBySlug</span>(slug)</span><br><span class="line">  <span class="keyword">return</span> post</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>关于生成文章唯一id，我使用了crc32算法，这个算法的特点是速度快，生成的id短，但是会有冲突，不过我觉得这个冲突概率很小，而且我也不会写那么多文章，所以就这样吧。</p><ul><li>对于文章列表，直接使用<code>getAllPostsMeta</code>获取所有文章的meta数据，然后渲染列表即可。<figure class="highlight tsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> <span class="title class_">PostProps</span> &#123;</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">number</span> | <span class="built_in">string</span></span><br><span class="line">  <span class="attr">title</span>: <span class="built_in">string</span></span><br><span class="line">  <span class="attr">desc</span>: <span class="built_in">string</span></span><br><span class="line">  <span class="attr">tags</span>: <span class="built_in">string</span>[]</span><br><span class="line">  <span class="attr">date</span>: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Post</span> = (<span class="params">&#123; ...props &#125;: <span class="title class_">PostProps</span></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; title, <span class="attr">desc</span>: content, tags, <span class="attr">date</span>: time, id &#125; = props</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;post-card&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">Link</span> <span class="attr">href</span>=<span class="string">&#123;</span>`/<span class="attr">post</span>/$&#123;<span class="attr">id</span>&#125;`&#125;&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;post-title&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">          &#123;title&#125;</span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">Link</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;post-content&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        &#123;content&#125;</span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;post-footer&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">TimeBar</span> <span class="attr">time</span>=<span class="string">&#123;time&#125;</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;post-footer__tag&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">          &#123;</span></span><br><span class="line"><span class="language-xml">            tags.length &lt;= 0 &amp;&amp; <span class="tag">&lt;<span class="name">Tag</span> <span class="attr">val</span>=<span class="string">&#123;</span>&#x27;<span class="attr">未归档</span>&#x27;&#125; /&gt;</span></span></span><br><span class="line"><span class="language-xml">          &#125;</span></span><br><span class="line"><span class="language-xml">          &#123;</span></span><br><span class="line"><span class="language-xml">            tags.map((tag, idx) =&gt; &#123;</span></span><br><span class="line"><span class="language-xml">              if (idx &gt; 3) return null;</span></span><br><span class="line"><span class="language-xml">              return <span class="tag">&lt;<span class="name">Tag</span> <span class="attr">key</span>=<span class="string">&#123;tag&#125;</span> <span class="attr">val</span>=<span class="string">&#123;tag&#125;</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">            &#125;)</span></span><br><span class="line"><span class="language-xml">          &#125;</span></span><br><span class="line"><span class="language-xml">          &#123;</span></span><br><span class="line"><span class="language-xml">            tags.length &gt; 3 &amp;&amp; <span class="tag">&lt;<span class="name">Tag</span> <span class="attr">isMore</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">          &#125;</span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">PagePostList</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> posts = <span class="keyword">await</span> <span class="title function_">getPostList</span>()</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;post-list&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      &#123;</span></span><br><span class="line"><span class="language-xml">        posts.map(post =&gt; &#123;</span></span><br><span class="line"><span class="language-xml">          return <span class="tag">&lt;<span class="name">Post</span> <span class="attr">key</span>=<span class="string">&#123;post.id&#125;</span> &#123;<span class="attr">...post</span>&#125; /&gt;</span></span></span><br><span class="line"><span class="language-xml">        &#125;)</span></span><br><span class="line"><span class="language-xml">      &#125;</span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>对于文章详情页，通过id获取文章内容，将内容传入<code>MDXRemote</code>组件中，然后渲染即可。<figure class="highlight tsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">MDXRemote</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;next-mdx-remote/rsc&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">Post</span>(<span class="params">&#123;</span></span><br><span class="line"><span class="params">  params,</span></span><br><span class="line"><span class="params">&#125;: &#123;</span></span><br><span class="line"><span class="params">  params: &#123; slug: <span class="built_in">string</span> &#125;;</span></span><br><span class="line"><span class="params">&#125;</span>) &#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> postPayload = <span class="keyword">await</span> <span class="title function_">getPost</span>(params.<span class="property">slug</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;post-detail&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        &#123;/* ... */&#125;</span></span><br><span class="line"><span class="language-xml">        &#123;/* 文章内容 */&#125;</span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;markdown-body w-full break-words whitespace-normal &quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;<span class="name">Suspense</span> <span class="attr">fallback</span>=<span class="string">&#123;</span>&lt;&gt;</span>Loading...<span class="tag">&lt;/&gt;</span>&#125;&gt;</span></span><br><span class="line"><span class="language-xml">            &#123;/* @ts-ignore */&#125;</span></span><br><span class="line"><span class="language-xml">            <span class="tag">&lt;<span class="name">MDXRemote</span> <span class="attr">source</span>=<span class="string">&#123;postPayload.content&#125;</span> <span class="attr">options</span>=<span class="string">&#123;&#123;</span> <span class="attr">parseFrontmatter:</span> <span class="attr">true</span>, &#125;&#125; <span class="attr">components</span>=<span class="string">&#123;components&#125;</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;/<span class="name">Suspense</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">       &#123;/* ... */&#125; </span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/&gt;</span></span></span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">notFound</span>()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="静态页面生成"><a href="#静态页面生成" class="headerlink" title="静态页面生成"></a>静态页面生成</h2><p>在<code>post/[slug]/page.tsx</code>中，通过导出<code>generateStaticParams</code>函数，可以实现静态页面生成(app router)。<br>在app router中，生成静态页面路径的函数已经由原来的<code>getStaticPaths</code>改为<code>generateStaticParams</code></p><figure class="highlight tsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">generateStaticParams</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> metas = <span class="keyword">await</span> <span class="title function_">getAllPostsMeta</span>()</span><br><span class="line">  <span class="keyword">return</span> metas.<span class="title function_">map</span>(<span class="function"><span class="params">meta</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">slug</span>: meta.<span class="property">id</span> &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="添加样式"><a href="#添加样式" class="headerlink" title="添加样式"></a>添加样式</h2><p>mdx文件中的元素，可以通过<code>components</code>属性传入<code>&lt;MDXRemote /&gt;</code>组件，然后在<code>components</code>对象中添加样式。</p><figure class="highlight tsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> components = &#123;</span><br><span class="line">  <span class="comment">// 代码高亮</span></span><br><span class="line">  <span class="attr">code</span>: <span class="title class_">CodeBlock</span>,</span><br><span class="line">  <span class="attr">img</span>: <span class="title class_">Image</span>,</span><br><span class="line">  <span class="comment">// 为其元素添加自定义类名</span></span><br><span class="line">  <span class="attr">h1</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;h1&#x27;</span>),</span><br><span class="line">  <span class="attr">h2</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;h2&#x27;</span>),</span><br><span class="line">  <span class="attr">h3</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;h3&#x27;</span>),</span><br><span class="line">  <span class="attr">h4</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;h4&#x27;</span>),</span><br><span class="line">  <span class="attr">h5</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;h5&#x27;</span>),</span><br><span class="line">  <span class="attr">h6</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;h6&#x27;</span>),</span><br><span class="line">  <span class="attr">p</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;p&#x27;</span>),</span><br><span class="line">  <span class="attr">a</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;a&#x27;</span>),</span><br><span class="line">  <span class="attr">blockquote</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;blockquote&#x27;</span>),</span><br><span class="line">  <span class="attr">table</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;table&#x27;</span>),</span><br><span class="line">  <span class="attr">thead</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;thead&#x27;</span>),</span><br><span class="line">  <span class="attr">tbody</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;tbody&#x27;</span>),</span><br><span class="line">  <span class="attr">tr</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;tr&#x27;</span>),</span><br><span class="line">  <span class="attr">th</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;th&#x27;</span>),</span><br><span class="line">  <span class="attr">td</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;td&#x27;</span>),</span><br><span class="line">  <span class="attr">em</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;em&#x27;</span>),</span><br><span class="line">  <span class="attr">strong</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;strong&#x27;</span>),</span><br><span class="line">  <span class="attr">del</span>: <span class="title function_">tagRenderer</span>(<span class="string">&#x27;del&#x27;</span>),</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>app router用起来感觉别扭，比如文章生成那块，默认的MDXRemote组件并不是<code>RSC(react-server-component)</code>，导致外面引用该组件的组件必须使用’use client’，因为nextjs在服务端渲染不了useState函数，这样就导致了组件的复用性变差，不过这个问题可以通过自定义MDXRemote组件解决，官方示例有提供方法，这样就不如page router方便了。</p><p>另外，使用motion添加动画效果的时候，必须添加’use client’，这样就导致了动画效果在服务端渲染的时候不会生效，这个问题我还没想到解决方法。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;MDX是一种将Markdown和JSX结合的语法，可以在Markdown中使用JSX，一直想独立编写一个博客平台，这次就用nextjs+MDX来写一个博客平台吧。&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
  </entry>
  
  <entry>
    <title>给箱子添加开箱效果</title>
    <link href="http://example.com/post/6fb277ee.html"/>
    <id>http://example.com/post/6fb277ee.html</id>
    <published>2023-07-10T07:25:08.000Z</published>
    <updated>2025-10-11T09:39:21.826Z</updated>
    
    <content type="html"><![CDATA[<p>之前遇到开门效果，猜测只是旋转mesh的rotation而已，没想到和我想象的有些区别，这里记录一下。</p><span id="more"></span><p>先看一下目前情况，原项目是在网上看到的，发现箱顶的旋转有些诡异，决定练练手，看看能不能改进一下。</p><p>原项目链接：<a class="link"   href="https://github.com/wrongakram/React-Three-Minecraft-Chest" >wrongakram&#x2F;React-Three-Minecraft-Chest: Using React Three Fiber we will make a minecraft chest (github.com)<i class="fas fa-external-link-alt"></i></a></p><p>快速预览：<a class="link"   href="https://react-three-minecraft.netlify.app/" >React App (react-three-minecraft.netlify.app)<i class="fas fa-external-link-alt"></i></a></p><img                         lazyload                       alt="image"                       data-src="/post/6fb277ee/%E5%8A%A8%E7%94%BB.gif"                        class="" title="动画"                 ><p>目标：将箱顶旋转改为贴合箱子旋转</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p>网上找到两种方法，一种是通过计算旋转后的物体位置，然后设置对应position，这种比较麻烦，<br>[vue使用three.js实现盒子上下左右开关门的效果](<a class="link"   href="https://blog.csdn.net/qq_39165556/article/" >https://blog.csdn.net/qq_39165556/article/<i class="fas fa-external-link-alt"></i></a><br>details&#x2F;91870533)</p><p>另一种是将外部包一层group后，设置mesh的相对位置，然后只需要旋转外部的group就可以了，这个比较巧妙，我决定使用第二种。<br>首先将箱盖包裹起来，这里用group包裹，将group的position设置为0，0，0，然后将group的position设置为箱子的顶部位置，这样就可以通过旋转group来实现箱子的开合效果了。</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">a.group</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      <span class="attr">position</span>=<span class="string">&#123;[0,</span> <span class="attr">-0.99</span>, <span class="attr">0</span>]&#125;</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      <span class="attr">onClick</span>=<span class="string">&#123;handleAnimation&#125;</span> <span class="attr">ref</span>=<span class="string">&#123;group&#125;</span> &#123;<span class="attr">...props</span>&#125; <span class="attr">dispose</span>=<span class="string">&#123;null&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      &#123;/* 包裹原有mesh，将位置设置为原mesh位置 */&#125;</span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">a.group</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">position</span>=<span class="string">&#123;[</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">          <span class="attr">-1.4557723082564422e-11</span>,</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">          <span class="attr">1.7300409078598022</span>,</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">          <span class="attr">-0.9812345504760742</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        ]&#125;</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">rotation</span>=<span class="string">&#123;chestOpen.rotation&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      &gt;</span></span></span><br><span class="line"><span class="language-xml">        &#123;/* 仅仅用来展示group位置 */&#125;</span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">a.mesh</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">          <span class="attr">position</span>=<span class="string">&#123;[0,</span> <span class="attr">0</span>, <span class="attr">0</span>]&#125;</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        &gt;</span></span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;<span class="name">boxBufferGeometry</span> <span class="attr">attach</span>=<span class="string">&quot;geometry&quot;</span> <span class="attr">args</span>=<span class="string">&#123;[1,</span> <span class="attr">1</span>, <span class="attr">1</span>]&#125; /&gt;</span></span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;<span class="name">meshBasicMaterial</span> <span class="attr">color</span>=<span class="string">&#123;0x000000&#125;</span> <span class="attr">wireframe</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;/<span class="name">a.mesh</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        &#123;/* 箱盖 */&#125;</span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">a.primitive</span> <span class="attr">object</span>=<span class="string">&#123;nodes.Bone001&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">          <span class="attr">position</span>=<span class="string">&#123;[0,</span> <span class="attr">0</span>, <span class="attr">0</span>]&#125;</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        /&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">a.group</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      &#123;/* 箱子体 */&#125;</span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">primitive</span> <span class="attr">object</span>=<span class="string">&#123;nodes.Bone&#125;</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">skinnedMesh</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">castShadow</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">receiveShadow</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">material</span>=<span class="string">&#123;materials.Material&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">geometry</span>=<span class="string">&#123;nodes.Cube.geometry&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">skeleton</span>=<span class="string">&#123;nodes.Cube.skeleton&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      /&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">a.group</span>&gt;</span></span></span><br><span class="line">  );</span><br></pre></td></tr></table></figure><p>先看一下当前的效果</p><img                         lazyload                       alt="image"                       data-src="/post/6fb277ee/%E5%8A%A8%E7%94%BB-1688947597822.gif"                        class="" title="动画"                 ><p>线框代表group，现在只需要将group向左下方移动，然后将mesh复原回原来的位置就可以了。</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> (</span><br><span class="line">  <span class="language-xml"><span class="tag">&lt;<span class="name">a.group</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">    <span class="attr">position</span>=<span class="string">&#123;[0,</span> <span class="attr">-0.99</span>, <span class="attr">0</span>]&#125;</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">    <span class="attr">onClick</span>=<span class="string">&#123;handleAnimation&#125;</span> <span class="attr">ref</span>=<span class="string">&#123;group&#125;</span> &#123;<span class="attr">...props</span>&#125; <span class="attr">dispose</span>=<span class="string">&#123;null&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">a.group</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      <span class="attr">position</span>=<span class="string">&#123;[</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">-1.4557723082564422e-11</span> <span class="attr">-</span> <span class="attr">offset</span>,</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">1.7300409078598022</span> <span class="attr">-</span> <span class="attr">offset</span>,</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">-0.9812345504760742</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      ]&#125;</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      <span class="attr">rotation</span>=<span class="string">&#123;chestOpen.rotation&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">    &gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">a.mesh</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">position</span>=<span class="string">&#123;[0,</span> <span class="attr">0</span>, <span class="attr">0</span>]&#125;</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      &gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">boxBufferGeometry</span> <span class="attr">attach</span>=<span class="string">&quot;geometry&quot;</span> <span class="attr">args</span>=<span class="string">&#123;[1,</span> <span class="attr">1</span>, <span class="attr">1</span>]&#125; /&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">meshBasicMaterial</span> <span class="attr">color</span>=<span class="string">&#123;0x000000&#125;</span> <span class="attr">wireframe</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">a.mesh</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">a.primitive</span> <span class="attr">object</span>=<span class="string">&#123;nodes.Bone001&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">position</span>=<span class="string">&#123;[offset,</span> <span class="attr">offset</span>, <span class="attr">0</span>]&#125;</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      /&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">a.group</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">primitive</span> <span class="attr">object</span>=<span class="string">&#123;nodes.Bone&#125;</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">skinnedMesh</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      <span class="attr">castShadow</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      <span class="attr">receiveShadow</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      <span class="attr">material</span>=<span class="string">&#123;materials.Material&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      <span class="attr">geometry</span>=<span class="string">&#123;nodes.Cube.geometry&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      <span class="attr">skeleton</span>=<span class="string">&#123;nodes.Cube.skeleton&#125;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">    /&gt;</span></span></span><br><span class="line"><span class="language-xml">  <span class="tag">&lt;/<span class="name">a.group</span>&gt;</span></span></span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>最终效果：</p><img                         lazyload                       alt="image"                       data-src="/post/6fb277ee/%E5%8A%A8%E7%94%BB-1688947775987.gif"                        class="" title="动画"                 >]]></content>
    
    
    <summary type="html">&lt;p&gt;之前遇到开门效果，猜测只是旋转mesh的rotation而已，没想到和我想象的有些区别，这里记录一下。&lt;/p&gt;</summary>
    
    
    
    
    <category term="web" scheme="http://example.com/tags/web/"/>
    
    <category term="threejs" scheme="http://example.com/tags/threejs/"/>
    
  </entry>
  
  <entry>
    <title>使用vite-plugin-monkey优化油猴脚本</title>
    <link href="http://example.com/post/a1c86e47.html"/>
    <id>http://example.com/post/a1c86e47.html</id>
    <published>2023-07-03T15:09:42.000Z</published>
    <updated>2025-10-11T09:39:21.694Z</updated>
    
    <content type="html"><![CDATA[<p>之前写过的脚本全部混杂在一个文件里，非常原始，不利于维护，所以想着用vite来优化一下。</p><span id="more"></span><p>在v站看过有v友写的vite插件，用于编写油猴脚本的，一直想拿来用用，今天成功把<code>阿里云盘树状目录</code>跑起来了。</p><h2 id="创建项目"><a href="#创建项目" class="headerlink" title="创建项目"></a>创建项目</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pnpm create monkey</span><br></pre></td></tr></table></figure><p>我选择<code>vanilla-ts</code>模板，因为脚本很简单。</p><h2 id="安装依赖"><a href="#安装依赖" class="headerlink" title="安装依赖"></a>安装依赖</h2><p>用到的模块：</p><ul><li>jQuery 管理dom</li><li>jQuery.fancytree 展示列表</li></ul><p>重点：<br>这俩模块需要处理一下，因为咱用到了vite，配置vite进行正确地编译。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p>关于常见的模块格式</p><ul><li>CommonJS（通用模块规范）是一种用于服务器端JavaScript的模块系统。它的目标是使JavaScript能够在不同的环境中共享和重用代码。在CommonJS中，模块使用require函数导入，使用module.exports或exports对象导出。</li><li>UMD（通用模块定义）是一种兼容多种模块系统的模块格式。它可以在浏览器环境和服务器环境中使用。UMD模块首先检测是否支持AMD模块，如果不支持，则检测是否支持CommonJS模块，最后，如果都不支持，则将模块作为全局变量导出。</li><li>AMD（异步模块定义）是一种用于浏览器端JavaScript的模块系统。它的设计目标是在浏览器中异步加载模块，以提高性能。AMD模块使用define函数定义模块，并使用require函数异步加载依赖模块。</li><li>IIFE（立即调用函数表达式）是一种常见的JavaScript模块模式，它使用函数作用域来封装代码，并立即执行该函数。这种模式可以用于创建私有作用域，避免变量冲突，并将部分代码暴露给全局作用域。</li><li>ESM（ECMAScript模块）是一种现代的JavaScript模块系统，它是ECMAScript标准的一部分，从ES6（ES2015）开始引入。ESM模块使用import语句导入模块，使用export关键字导出模块。ESM模块可以在浏览器和服务器环境中使用，并且可以进行静态分析和优化。</li></ul><p>下面分别解释vite在dev模式和prod模式中的区别：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">在 Vite 中，开发模式（dev）和生产模式（prod）之间的区别主要体现在打包工具和模块规范上。</span><br><span class="line"></span><br><span class="line">开发模式（dev）：</span><br><span class="line">打包工具：在开发模式下，Vite 使用了一种称为「原生 ESM」的打包方式。它利用浏览器原生的 ES Modules 支持，不进行传统的打包和捆绑操作。这意味着每个模块都是作为一个独立的文件进行加载，而不是将所有模块合并为一个或多个捆绑文件。</span><br><span class="line">模块规范：在开发模式下，Vite 支持 ES Modules（ESM）规范，你可以使用 import 和 export 来导入和导出模块。此外，Vite 也支持 CommonJS 规范，因此你可以使用 require 和 module.exports。</span><br><span class="line"></span><br><span class="line">生产模式（prod）：</span><br><span class="line">打包工具：在生产模式下，Vite 使用 Rollup 作为打包工具。Rollup 是一个优化的 JavaScript 模块打包器，它会将应用程序的模块捆绑成更小、更高效的文件。</span><br><span class="line">模块规范：在生产模式下，Vite 会将所有模块转换为符合 ES Modules（ESM）规范的代码，并且会进行代码压缩、混淆和优化，以减小文件大小并提高加载性能。这意味着你只能使用 import 和 export 来导入和导出模块，而不支持 CommonJS 规范。</span><br><span class="line">总结起来，开发模式下的 Vite 使用原生 ESM 打包方式和支持多种模块规范，而生产模式下的 Vite 使用 Rollup 打包工具，并且只支持 ES Modules（ESM）规范。这种区别使得开发者可以在开发过程中享受到更快的冷启动和模块热替换等优势，并在生产环境中获得更高的性能和优化的代码。</span><br></pre></td></tr></table></figure><ul><li>在开发模式时，通过pnpm安装jquery和jquery.fancytree，两个模块都是支持esm导入的</li><li>在生产模式时，将jquery和jquery.fancytree都定义在vite-plugin-monkey的<code>externalGlobals</code>中，在此定义后，意味着该模块不经过处理而直接放到油猴脚本中的<code>@require</code>中，作为cdn依赖</li></ul><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// https://vitejs.dev/config/</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineConfig</span>(&#123;</span><br><span class="line">  <span class="attr">plugins</span>: [</span><br><span class="line">    <span class="comment">// 将 jQuery 注入到每个模块中</span></span><br><span class="line">    <span class="title function_">inject</span>(&#123;</span><br><span class="line">      <span class="attr">$</span>: <span class="string">&quot;jquery&quot;</span>,  <span class="comment">// 这里会自动载入 node_modules 中的 jquery</span></span><br><span class="line">      <span class="attr">jQuery</span>: <span class="string">&quot;jquery&quot;</span>,</span><br><span class="line">      <span class="string">&#x27;jquery.fancytree&#x27;</span>: <span class="string">&#x27;jquery.fancytree&#x27;</span>,</span><br><span class="line">    &#125;),</span><br><span class="line"></span><br><span class="line">    <span class="title function_">monkey</span>(&#123;</span><br><span class="line">      <span class="attr">entry</span>: <span class="string">&#x27;src/main.ts&#x27;</span>,</span><br><span class="line">      <span class="attr">userscript</span>: &#123;</span><br><span class="line">        <span class="attr">icon</span>: <span class="string">&#x27;https://vitejs.dev/logo.svg&#x27;</span>,</span><br><span class="line">        <span class="attr">namespace</span>: <span class="string">&#x27;npm/vite-plugin-monkey&#x27;</span>,</span><br><span class="line">        <span class="attr">match</span>: [<span class="string">&#x27;https://www.aliyundrive.com/s/*&#x27;</span>],</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="attr">build</span>: &#123;</span><br><span class="line">        <span class="comment">// 替换外部依赖为头部@require</span></span><br><span class="line">        <span class="attr">externalGlobals</span>:&#123;</span><br><span class="line">          <span class="attr">jquery</span>: $.<span class="title function_">concat</span>(</span><br><span class="line">            <span class="keyword">await</span> util.<span class="title function_">fn2dataUrl</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">              <span class="comment">// @ts-ignore</span></span><br><span class="line">              <span class="variable language_">window</span>.<span class="property">jQuery</span> = <span class="variable language_">window</span>.<span class="property">jquery</span> = $;</span><br><span class="line">            &#125;)</span><br><span class="line">          ),</span><br><span class="line">          <span class="string">&#x27;jquery.fancytree&#x27;</span>: fancytree,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">externalResource</span>: &#123;</span><br><span class="line">          <span class="string">&#x27;jquery.fancytree/dist/skin-lion/ui.fancytree.css&#x27;</span>: cdn.<span class="title function_">bootcdn</span>()</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">build</span>: &#123;</span><br><span class="line">    <span class="attr">commonjsOptions</span>: &#123;</span><br><span class="line">      <span class="comment">// 支持 commonjs 的 require</span></span><br><span class="line">      <span class="attr">transformMixedEsModules</span>: <span class="literal">true</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="模块中css引用相对路径资源问题"><a href="#模块中css引用相对路径资源问题" class="headerlink" title="模块中css引用相对路径资源问题"></a>模块中css引用相对路径资源问题</h2><p>其中遇到了些问题，需要先了解dev模式下，<code>vite-plugin-monkey</code>是如何做到热更新的？<br><a href="%E5%85%B3%E4%BA%8Evite-plugin-monkey%E7%9A%84%E5%90%AF%E5%8A%A8%E5%AE%9E%E6%97%B6%E6%9B%B4%E6%96%B0%E7%9A%84%E6%A8%A1%E5%BC%8F%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E7%9A%84%E8%AE%A8%E8%AE%BA">https://github.com/lisonge/vite-plugin-monkey/discussions/72</a></p><p>作者说到：脚本dev模式下生成的代码，实际更新只在脚本服务器中更新，插件页面上用到的环境是脚本服务器的</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">;(<span class="function">(<span class="params">&#123; entrySrc = <span class="string">``</span> &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">window</span>.<span class="property">GM</span>;</span><br><span class="line">  <span class="variable language_">document</span>.<span class="property">__monkeyWindow</span> = <span class="variable language_">window</span>;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`[vite-plugin-monkey] mount monkeyWindow to document`</span>);</span><br><span class="line">  <span class="keyword">const</span> entryScript = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;script&quot;</span>);</span><br><span class="line">  entryScript.<span class="property">type</span> = <span class="string">&quot;module&quot;</span>;</span><br><span class="line">  entryScript.<span class="property">src</span> = entrySrc;</span><br><span class="line">  <span class="variable language_">document</span>.<span class="property">head</span>.<span class="title function_">insertBefore</span>(entryScript, <span class="variable language_">document</span>.<span class="property">head</span>.<span class="property">firstChild</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`[vite-plugin-monkey] mount entry module to document.head`</span>);</span><br><span class="line">&#125;)(...[</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="string">&quot;entrySrc&quot;</span>: <span class="string">&quot;http://127.0.0.1:5173/__vite-plugin-monkey.entry.js&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">]);</span><br></pre></td></tr></table></figure><p>同时这也造成一个问题，在模块中引入的图片路径是相对<code>/</code>目录时，相对的是浏览器中的根目录，而不是脚本服务器的。<br>从而导致我的图片加载失败。</p><img                         lazyload                       alt="image"                       data-src="/post/a1c86e47/image-20230703153946523.png"                        class="" title="image-20230703153946523"                 ><p>解决方案就是写一个链接替换插件，将原来指向根目录的链接替换为脚本服务器的链接。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">modifyCssUrlsPlugin</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> rootUrl = <span class="string">&#x27;/&#x27;</span></span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    <span class="attr">name</span>: <span class="string">&#x27;modify-css-urls&#x27;</span>,</span><br><span class="line">    <span class="title function_">configureServer</span>(<span class="params">_server</span>) &#123;</span><br><span class="line">      rootUrl = <span class="string">&#x27;http://&#x27;</span> + server.<span class="property">config</span>.<span class="property">server</span>.<span class="property">host</span> + <span class="string">&#x27;:&#x27;</span> + server.<span class="property">config</span>.<span class="property">server</span>.<span class="property">port</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="title function_">transform</span>(<span class="params">code, id</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (id.<span class="title function_">endsWith</span>(<span class="string">&#x27;.css&#x27;</span>)) &#123;</span><br><span class="line">        <span class="comment">// 读取 CSS 文件内容</span></span><br><span class="line">        <span class="keyword">const</span> cssContent = code;</span><br><span class="line">        <span class="comment">// 修改 CSS 文件中的 URL 相对路径</span></span><br><span class="line">        <span class="keyword">const</span> modifiedCssContent = cssContent.<span class="title function_">replace</span>(<span class="regexp">/url\((.*?)\)/g</span>, <span class="function">(<span class="params">match, sourceUrl</span>) =&gt;</span> &#123;</span><br><span class="line">          <span class="comment">// 处理 URL 相对路径</span></span><br><span class="line">          <span class="keyword">const</span> modifiedUrl = <span class="title function_">resolveRelativeUrl</span>(rootUrl, sourceUrl);</span><br><span class="line">          <span class="keyword">return</span> <span class="string">`url(<span class="subst">$&#123;modifiedUrl&#125;</span>)`</span>;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回修改后的 CSS 代码</span></span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">          <span class="attr">code</span>: modifiedCssContent,</span><br><span class="line">          <span class="attr">map</span>: <span class="literal">null</span>, <span class="comment">// 如果需要生成 Source Map，请配置正确的 Source Map</span></span><br><span class="line">        &#125;;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// vite.config.ts</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineConfig</span>(&#123;</span><br><span class="line">  <span class="attr">plugins</span>: [</span><br><span class="line">    <span class="title function_">modifyCssUrlsPlugin</span>(),</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  ]</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><img                         lazyload                       alt="image"                       data-src="/post/a1c86e47/image-20230703154420746.png"                        class="" title="image-20230703154420746"                 ><p>问题成功解决！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;之前写过的脚本全部混杂在一个文件里，非常原始，不利于维护，所以想着用vite来优化一下。&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
  </entry>
  
  <entry>
    <title>使用AI模仿孙燕姿唱歌</title>
    <link href="http://example.com/post/20c76524.html"/>
    <id>http://example.com/post/20c76524.html</id>
    <published>2023-06-30T07:59:38.000Z</published>
    <updated>2025-10-11T09:39:21.618Z</updated>
    
    <content type="html"><![CDATA[<p>偶然看到孙燕姿唱最近流行的歌，音色很符合她，听完才发现竟然不是本人唱的，而是AI依据原歌曲和孙女士声音模型混合的结果。</p><span id="more"></span><h2 id="效果预览"><a href="#效果预览" class="headerlink" title="效果预览"></a>效果预览</h2><p><a class="link"   href="https://www.bilibili.com/video/BV1o14y1Z7EK/?vd_source=1ec8998e58f8cdf9b1492f4de118be1a" >【AI孙燕姿】《遥远的歌》Cover 刘惜君_哔哩哔哩_bilibili<i class="fas fa-external-link-alt"></i></a><br>这位up主训练的不错，我的耳朵听不出来是AI唱的，而且还有孙燕姿的风格。</p><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><ul><li>安装<a class="link"   href="https://www.python.org/downloads/" >Python<i class="fas fa-external-link-alt"></i></a>，并配置环境变量 (本人使用python3.10)</li><li>人声提取：Ultimate Vocal Remover - ultimatevocalremover.com&#x2F;<br><a class="link"   href="https://github.com/Anjok07/ultimatevocalremovergui" >https://github.com/Anjok07/ultimatevocalremovergui<i class="fas fa-external-link-alt"></i></a></li><li>so-vits-svg GUI：<a class="link"   href="https://github.com/voicepaw/so-vits-svc-fork/blob/main/README_zh_CN.md" >so-vits-svc-fork&#x2F;README_zh_CN.md at main · voicepaw&#x2F;so-vits-svc-fork · GitHub<i class="fas fa-external-link-alt"></i></a>，直接点击.bat下载<br>打开bat可能出现访问不了huggingface，需要给终端配置代理<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># 编辑终端配置文件</span><br><span class="line">code $PROFILE</span><br><span class="line"># 将以下内容添加到文件末尾，端口需要根据你的代理软件设置，我这是clash</span><br><span class="line">$env:http_proxy=&quot;http://127.0.0.1:7890&quot;</span><br><span class="line">$env:https_proxy=&quot;http://127.0.0.1:7890&quot;</span><br></pre></td></tr></table></figure></li><li>孙女士声音模型可在该博客下载：<a class="link"   href="https://v3u.cn/a_id_310" >AI天后,在线飙歌,人工智能AI孙燕姿模型应用实践，复刻《遥远的歌》，原唱晴子(Python3.10)-刘悦 (v3u.cn)<i class="fas fa-external-link-alt"></i></a>，下载后解压，得到<code>config.json</code>和<code>G_27200.pth</code></li></ul><h2 id="开始制作"><a href="#开始制作" class="headerlink" title="开始制作"></a>开始制作</h2><ol><li><p>首先下载你想让AI唱的歌，然后使用人声提取软件ltimate Vocal Remover提取人声<br><img                         lazyload                       alt="image"                       data-src="/%E4%BD%BF%E7%94%A8AI%E6%A8%A1%E4%BB%BF%E5%AD%99%E7%87%95%E5%A7%BF%E5%94%B1%E6%AD%8C%5Cimage-20230630082155166.png"                        alt="image-20230630082155166"                 ><br>如图，选择输入音频和输出位置，使用默认模型，点击Start，等待提取完成。</p></li><li><p>双击bat文件打开so-vits-svg GUI<br><img                         lazyload                       alt="image"                       data-src="/%E4%BD%BF%E7%94%A8AI%E6%A8%A1%E4%BB%BF%E5%AD%99%E7%87%95%E5%A7%BF%E5%94%B1%E6%AD%8C%5Cimage-20230630082028007.png"                        alt="image-20230630082028007"                 ><br>选择模型和配置文件，选择刚才提取的人声，点击infer，等待生成完毕。</p></li></ol><p>推理结束后就可以在输出目录那里看到孙女士唱的歌了。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script>]]></content>
    
    
    <summary type="html">&lt;p&gt;偶然看到孙燕姿唱最近流行的歌，音色很符合她，听完才发现竟然不是本人唱的，而是AI依据原歌曲和孙女士声音模型混合的结果。&lt;/p&gt;</summary>
    
    
    
    
    <category term="AI" scheme="http://example.com/tags/AI/"/>
    
  </entry>
  
  <entry>
    <title>使用chatgpt编写小说</title>
    <link href="http://example.com/post/c63cf213.html"/>
    <id>http://example.com/post/c63cf213.html</id>
    <published>2023-06-26T06:42:05.000Z</published>
    <updated>2025-10-11T09:39:21.642Z</updated>
    
    <content type="html"><![CDATA[<p>自己喂提示词给chatgpt，总是感觉效果不理想，没有逻辑，今天看到一个项目，可以使用chatgpt编写小说，效果还不错，可以参考一下。</p><span id="more"></span><h2 id="项目地址"><a href="#项目地址" class="headerlink" title="项目地址"></a>项目地址</h2><p><a class="link"   href="https://github.com/mshumer/gpt-author" >https://github.com/mshumer/gpt-author<i class="fas fa-external-link-alt"></i></a></p><h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><p>由于作者提供的代码是直接使用gpt4.0的，没有适配gpt3.5，所以在issue里有人提供了适配gpt3.5的代码，另外添加了多语言翻译。<br>issue: <a class="link"   href="https://github.com/mshumer/gpt-author/issues/1#issuecomment-1602105017" >https://github.com/mshumer/gpt-author/issues/1#issuecomment-1602105017<i class="fas fa-external-link-alt"></i></a><br>进入colab后，点击标题栏<code>复制到云端硬盘</code>，将代码保存一份避免丢失。</p><p>在第一段代码中填写openai.api_key和stability_api_key，openai用于生成小说内容，stability用于生成封面。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">!pip install openai --quiet</span><br><span class="line">!pip install EbookLib --quiet</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> openai</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> ebooklib <span class="keyword">import</span> epub</span><br><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">openai.api_key = <span class="string">&quot;ENTER OPENAI KEY HERE&quot;</span>       <span class="comment"># get it at https://platform.openai.com/</span></span><br><span class="line">stability_api_key = <span class="string">&quot;ENTER STABILITY KEY HERE&quot;</span> <span class="comment"># get it at https://beta.dreamstudio.ai/</span></span><br></pre></td></tr></table></figure><p>代码下方有一个<code>setting</code>面板，修改model（默认gpt-3.5-turbo-16k），num_chapters（想生成多少章的小说）…<br>其中destLanguage是翻译语言，可以设置为<code>zh-cn</code>，这样生成的小说就是中文的了。<br>更多语言请执行第3段代码，查看支持的语言。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># To find the language code to use in the cell form above.</span></span><br><span class="line"><span class="keyword">for</span> key, value <span class="keyword">in</span> LANGUAGES.items():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Code: <span class="subst">&#123;key&#125;</span>\t Language: <span class="subst">&#123;value&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p>我这里测试gpt3.5，生成18章小说就会报错：<code>InvalidRequestError: This model&#39;s maximum context length is 8192 tokens</code><br>这是我的设置：</p><p>myModel:<br>gpt-3.5-turbo-16k<br>num_chapters:<br>18<br>novelStyle:<br>写实主义和现实主义的结合<br>author:<br>GPT-Author<br>prompt:<br>一个普通的上班族，因为一次意外事件而失去了工作，他决定改变自己的生活，追求自己真正的梦想，并在困境中找到了新的机会和意义。<br>writing_style:<br>简洁明快，语言流畅自然。运用恰如其分的描写和对话，使得故事情节紧凑而引人入胜。叙述方式富有情感，能够深入人心，让读者对故事中的人物和事件产生共鸣，在小说中巧妙地融入一些哲学思考和社会观察。通过人物的内心独白和对社会现象的揭示，探讨了个人命运与社会环境的关系，引发读者对生活意义和社会问题的思考<br>destLanguage:<br>zh-cn</p><h2 id="问题解决"><a href="#问题解决" class="headerlink" title="问题解决"></a>问题解决</h2><p>执行中可能会出现以下问题：</p><h3 id="1-报错SyntaxError-invalid-character-‘：’-U-FF1A"><a href="#1-报错SyntaxError-invalid-character-‘：’-U-FF1A" class="headerlink" title="1. 报错SyntaxError: invalid character ‘：’ (U+FF1A)"></a>1. 报错SyntaxError: invalid character ‘：’ (U+FF1A)</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">step cost: 0.004595</span><br><span class="line">step cost: 0.00587</span><br><span class="line">step cost: 0.006504</span><br><span class="line">step cost: 0.0024980000000000002</span><br><span class="line">Generating storyline with chapters and high-level details...</span><br><span class="line">step cost: 0.005292</span><br><span class="line">step cost: 0.009691</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"></span><br><span class="line">  File &quot;/usr/local/lib/python3.10/dist-packages/IPython/core/interactiveshell.py&quot;, line 3553, in run_code</span><br><span class="line">    exec(code_obj, self.user_global_ns, self.user_ns)</span><br><span class="line"></span><br><span class="line">  File &quot;&lt;ipython-input-19-ce40d1d73907&gt;&quot;, line 1, in &lt;cell line: 1&gt;</span><br><span class="line">    novel, title, chapters, chapter_titles = write_fantasy_novel(prompt, num_chapters, writing_style)</span><br><span class="line"></span><br><span class="line">  File &quot;&lt;ipython-input-18-0c9dea3b0c8c&gt;&quot;, line 168, in write_fantasy_novel</span><br><span class="line">    chapter_titles = ast.literal_eval(storyline)</span><br><span class="line"></span><br><span class="line">  File &quot;/usr/lib/python3.10/ast.py&quot;, line 64, in literal_eval</span><br><span class="line">    node_or_string = parse(node_or_string.lstrip(&quot; \t&quot;), mode=&#x27;eval&#x27;)</span><br><span class="line"></span><br><span class="line">  File &quot;/usr/lib/python3.10/ast.py&quot;, line 50, in parse</span><br><span class="line">    return compile(source, filename, mode, flags,</span><br><span class="line"></span><br><span class="line">  File &quot;&lt;unknown&gt;&quot;, line 1</span><br><span class="line">    这是我重写后的故事情节：</span><br><span class="line">               ^</span><br><span class="line">SyntaxError: invalid character &#x27;：&#x27; (U+FF1A)</span><br><span class="line">在generate_storyline的prompt内添加`Just provide the code without replying`</span><br></pre></td></tr></table></figure><h3 id="2-This-model’s-maximum-context-length-is-16385-tokens-However-your-messages-resulted-in-16407-tokens-Please-reduce-the-length-of-the-messages"><a href="#2-This-model’s-maximum-context-length-is-16385-tokens-However-your-messages-resulted-in-16407-tokens-Please-reduce-the-length-of-the-messages" class="headerlink" title="2. This model’s maximum context length is 16385 tokens. However, your messages resulted in 16407 tokens. Please reduce the length of the messages"></a>2. This model’s maximum context length is 16385 tokens. However, your messages resulted in 16407 tokens. Please reduce the length of the messages</h3><p>调小num_chapters，或者换个model</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;自己喂提示词给chatgpt，总是感觉效果不理想，没有逻辑，今天看到一个项目，可以使用chatgpt编写小说，效果还不错，可以参考一下。&lt;/p&gt;</summary>
    
    
    
    
    <category term="chatgpt" scheme="http://example.com/tags/chatgpt/"/>
    
  </entry>
  
  <entry>
    <title>解决标签栏无限转圈问题(图片容错最佳实践)</title>
    <link href="http://example.com/post/62754896.html"/>
    <id>http://example.com/post/62754896.html</id>
    <published>2023-05-23T08:31:41.000Z</published>
    <updated>2025-10-11T09:39:21.990Z</updated>
    
    <content type="html"><![CDATA[<p>之前公司项目PHP渲染的，莫名其妙标题栏一直转圈，最终发现是备用图片加载失败导致死循环，总结一下问题与解决方案。</p><span id="more"></span><p>通常，图片加载失败会有一个默认的图片，但是如果默认图片也加载失败，就会导致死循环。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;http://www.abc.com/1.jpg&quot;</span> <span class="attr">onerror</span>=<span class="string">&quot;this.src=&#x27;http://www.abc.com/2.jpg&#x27;&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><p>上述代码，如果2.jpg加载失败，则会走图片的onerror回调，再次加载2.jpg，如果2.jpg还是加载失败，就会再次走onerror回调，再次加载2.jpg，如此循环，浏览器就会卡死转圈。</p><p>解决方案：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span>   <span class="attr">src</span>=<span class="string">&quot;test.png&quot;</span>  <span class="attr">alt</span>=<span class="string">&quot;Alt Info&quot;</span> <span class="attr">onerror</span>=<span class="string">&quot;this.classList.add(&#x27;error&#x27;);&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span><span class="selector-class">.error</span> &#123;</span><br><span class="line">    <span class="attribute">position</span>: relative;</span><br><span class="line">    <span class="attribute">display</span>: inline-block;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">img</span><span class="selector-class">.error</span><span class="selector-pseudo">::before</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="number">#f5f5f5</span> <span class="built_in">url</span>(<span class="string">&quot;data:image/svg+xml,%3Csvg class=&#x27;icon&#x27; viewBox=&#x27;0 0 1024 1024&#x27; xmlns=&#x27;http://www.w3.org/2000/svg&#x27; width=&#x27;200&#x27; height=&#x27;200&#x27;%3E%3Cpath d=&#x27;M304.128 456.192c48.64 0 88.064-39.424 88.064-88.064s-39.424-88.064-88.064-88.064-88.064 39.424-88.064 88.064 39.424 88.064 88.064 88.064zm0-116.224c15.36 0 28.16 12.288 28.16 28.16s-12.288 28.16-28.16 28.16-28.16-12.288-28.16-28.16 12.288-28.16 28.16-28.16z&#x27; fill=&#x27;%23e6e6e6&#x27;/%3E%3Cpath d=&#x27;M887.296 159.744H136.704C96.768 159.744 64 192 64 232.448v559.104c0 39.936 32.256 72.704 72.704 72.704h198.144L500.224 688.64l-36.352-222.72 162.304-130.56-61.44 143.872 92.672 214.016-105.472 171.008h335.36C927.232 864.256 960 832 960 791.552V232.448c0-39.936-32.256-72.704-72.704-72.704zm-138.752 71.68v.512H857.6c16.384 0 30.208 13.312 30.208 30.208v399.872L673.28 408.064l75.264-176.64zM304.64 792.064H165.888c-16.384 0-30.208-13.312-30.208-30.208v-9.728l138.752-164.352 104.96 124.416-74.752 79.872zm81.92-355.84l37.376 228.864-.512.512-142.848-169.984c-3.072-3.584-9.216-3.584-12.288 0L135.68 652.8V262.144c0-16.384 13.312-30.208 30.208-30.208h474.624L386.56 436.224zm501.248 325.632c0 16.896-13.312 30.208-29.696 30.208H680.96l57.344-93.184-87.552-202.24 7.168-7.68 229.888 272.896z&#x27; fill=&#x27;%23e6e6e6&#x27;/%3E%3C/svg%3E&quot;</span>) no-repeat center / <span class="number">50%</span> <span class="number">50%</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-tag">img</span><span class="selector-class">.error</span><span class="selector-pseudo">::after</span> &#123;</span><br><span class="line">    <span class="attribute">content</span>: <span class="built_in">attr</span>(alt);</span><br><span class="line">    <span class="comment">/** 定位代码 **/</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>来源：</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p><a class="link"   href="https://www.cnblogs.com/coco1s/p/17202234.html" >现代图片性能优化及体验优化指南 - 图片资源的容错及可访问性处理 - ChokCoco - 博客园 (cnblogs.com)<i class="fas fa-external-link-alt"></i></a></p><p><a class="link"   href="https://www.zhangxinxu.com/wordpress/2020/10/css-style-image-load-fail/" >图片加载失败后CSS样式处理最佳实践 « 张鑫旭-鑫空间-鑫生活 (zhangxinxu.com)<i class="fas fa-external-link-alt"></i></a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;之前公司项目PHP渲染的，莫名其妙标题栏一直转圈，最终发现是备用图片加载失败导致死循环，总结一下问题与解决方案。&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
  </entry>
  
  <entry>
    <title>读魔改路由模块源码</title>
    <link href="http://example.com/post/7b386f88.html"/>
    <id>http://example.com/post/7b386f88.html</id>
    <published>2023-05-17T19:12:12.000Z</published>
    <updated>2025-10-11T09:39:22.066Z</updated>
    
    <content type="html"><![CDATA[<p>做列表+详情页的时候总感觉体验不太如意，想要实现“进入列表时刷新，从列表进入详情时保存列表滚动位置，并在返回时还原位置”，今天偶然看到群友写的模块，让我思维打开了。</p><span id="more"></span><h2 id="问题背景"><a href="#问题背景" class="headerlink" title="问题背景"></a>问题背景</h2><p>之前遇到的问题：</p><p>列表页 跳到 编辑页，编辑完了返回，发现列表页数据没变化，是因为列表页加了keepalive</p><p>后来在 列表页 添加了<code>onActivated</code>时刷新数据，现在确实返回列表后能更新了，但是这同时把整个列表清空重新加载了。这时记录列表页进入详情页之前的scroll位置的插件（vue-scroll-behavior）就失效了，因为数据加载需要时间，而滚动是返回后立即滚动的。</p><p>或者更简单的，将列表页 keepalive 关掉，实际上和上面情况一样，也是重新刷新页面。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><video src="/post/7b386f88/3C8E5B08D6AE0E20694C56AEE5ED8C5B.mp4" controls="controls" style="max-width: 100%; display: block; margin-left: auto; margin-right: auto;">your browser does not support the video tag</video><p>这个就是最终效果：前进刷新后退不刷新</p><video src="/post/7b386f88/a1684322398063.mp4" controls="controls" style="max-width: 100%; display: block; margin-left: auto; margin-right: auto;">your browser does not support the video tag</video><h2 id="实现原理"><a href="#实现原理" class="headerlink" title="实现原理"></a>实现原理</h2><p>仓库地址：<a class="link"   href="https://github.com/JoeshuTT/vue-page-stack-router" >JoeshuTT&#x2F;vue-page-stack-router: 一个 Vue.js 的页面栈路由管理器 (github.com)<i class="fas fa-external-link-alt"></i></a></p><p>闲话不多讲，看看代码</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.js</span></span><br><span class="line"><span class="keyword">export</span> * <span class="keyword">from</span> <span class="string">&quot;./injectionSymbols&quot;</span>;</span><br><span class="line"><span class="keyword">export</span> * <span class="keyword">from</span> <span class="string">&quot;./useApi&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> &#123; createPageStackRouter &#125; <span class="keyword">from</span> <span class="string">&quot;./pageStackRouter&quot;</span>;</span><br></pre></td></tr></table></figure><p><code>index.js</code>中导出<code>createPageStackRouter</code>函数，看看里面咋写的</p><p>代码不贴了，讲讲思路算了</p><ul><li>install 中，在afterEach中注册跳转逻辑</li><li>如果该路由是keep-alive(默认都是)，计算当前路由和上一个路由的position差值，position代表位于页面栈的位置，越靠前越大。</li><li>根据差值判断pop还是push，如果push则记录当前滚动位置，如果pop则还原滚动位置，并根据路由操作维护一个<code>pageStackList</code></li></ul><p>其中路由<code>router-view</code>组件被替换成了插件中的<code>PageStackRouterView</code>，该组件包装了一层<code>router-view</code>，并根据模块维护的<code>pageStackList</code>来区分是否缓存组件</p><p>其实现后退还原位置，而进入刷新页面的方法是：</p><p>直接判断当前路由是否属于<code>pageStackList</code>中，如果是，则说明该操作是返回页面，则缓存并还原滚动位置；如果不属于，则说明是进入新页面，不缓存直接刷新走<code>onMounted</code>。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;router-view v-slot=&quot;&#123; Component, route &#125;&quot;&gt;</span><br><span class="line">    &lt;keep-alive :include=&quot;cachedViews&quot;&gt;</span><br><span class="line">      &lt;component :is=&quot;Component&quot; :key=&quot;route.fullPath&quot; /&gt;</span><br><span class="line">    &lt;/keep-alive&gt;</span><br><span class="line">  &lt;/router-view&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line">import &#123; computed &#125; from &quot;vue&quot;;</span><br><span class="line">import &#123; usePageStackList &#125; from &quot;../useApi&quot;;</span><br><span class="line"></span><br><span class="line">export default &#123;</span><br><span class="line">  name: &quot;PageStackRouterView&quot;,</span><br><span class="line">  setup() &#123;</span><br><span class="line">    // 取出当前维护的路由栈</span><br><span class="line">    const pageStackList = usePageStackList(); </span><br><span class="line">    const cachedViews = computed(() =&gt; pageStackList.map((v) =&gt; v.name));</span><br><span class="line"></span><br><span class="line">    return &#123;</span><br><span class="line">      pageStackList,</span><br><span class="line">      cachedViews,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;做列表+详情页的时候总感觉体验不太如意，想要实现“进入列表时刷新，从列表进入详情时保存列表滚动位置，并在返回时还原位置”，今天偶然看到群友写的模块，让我思维打开了。&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
  </entry>
  
  <entry>
    <title>解决在ios上以视频作为背景弹出原生播放器问题</title>
    <link href="http://example.com/post/eacd074b.html"/>
    <id>http://example.com/post/eacd074b.html</id>
    <published>2023-05-08T13:55:44.000Z</published>
    <updated>2025-10-11T09:39:21.986Z</updated>
    
    <content type="html"><![CDATA[<p>RT</p><span id="more"></span><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> videoEl = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&quot;#video&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> ua = navigator.<span class="property">userAgent</span>.<span class="title function_">toLocaleLowerCase</span>();</span><br><span class="line"><span class="comment">// x5内核</span></span><br><span class="line"><span class="keyword">if</span> (ua.<span class="title function_">match</span>(<span class="regexp">/tencenttraveler/</span>) != <span class="literal">null</span> || ua.<span class="title function_">match</span>(<span class="regexp">/qqbrowse/</span>) != <span class="literal">null</span>) &#123;</span><br><span class="line">    videoEl.<span class="title function_">setAttribute</span>(<span class="string">&quot;x-webkit-airplay&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">    videoEl.<span class="title function_">setAttribute</span>(<span class="string">&quot;x5-playsinline&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">    videoEl.<span class="title function_">setAttribute</span>(<span class="string">&quot;webkit-playsinline&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">    videoEl.<span class="title function_">setAttribute</span>(<span class="string">&quot;playsinline&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// other</span></span><br><span class="line">    videoEl.<span class="title function_">setAttribute</span>(<span class="string">&quot;webkit-playsinline&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line">    videoEl.<span class="title function_">setAttribute</span>(<span class="string">&quot;playsinline&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;RT&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
  </entry>
  
  <entry>
    <title>解决echarts在高分屏下高度异常问题</title>
    <link href="http://example.com/post/e58ad079.html"/>
    <id>http://example.com/post/e58ad079.html</id>
    <published>2023-04-28T17:14:07.000Z</published>
    <updated>2025-10-11T09:39:21.858Z</updated>
    
    <content type="html"><![CDATA[<p>一直没在乎过window系统下调整分辨率缩放，今天同事笔记本缩放调到175%，直接把我页面整废了，抓紧修一下！</p><span id="more"></span><p>正常看着是没什么毛病的</p><img                         lazyload                       alt="image"                       data-src="/post/e58ad079/image-20230428171907202.png"                        class="" title="image-20230428171907202"                 ><p>然而调一下系统缩放，到125%</p><img                         lazyload                       alt="image"                       data-src="/post/e58ad079/image-20230428171950804.png"                        class="" title="image-20230428171950804"                 ><p>其中图表的height算法是这样的</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> clientHeight= (<span class="variable language_">window</span>.<span class="property">innerHeight</span> || <span class="variable language_">document</span>.<span class="property">documentElement</span>.<span class="property">clientHeight</span> || <span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">clientHeight</span>)</span><br><span class="line"><span class="keyword">const</span> lineChartHeight = clientHeight - <span class="number">475</span></span><br></pre></td></tr></table></figure><p>lineChart:height &#x3D; 浏览器高度 - header高度</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p>然而实际缩放的时候，header高度并不是写死的475px，所以我的做法是</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> clientHeight= (<span class="variable language_">window</span>.<span class="property">innerHeight</span> || <span class="variable language_">document</span>.<span class="property">documentElement</span>.<span class="property">clientHeight</span> || <span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">clientHeight</span>)</span><br><span class="line"><span class="keyword">const</span> lineChartHeight = clientHeight - <span class="number">475</span> / ratio</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;一直没在乎过window系统下调整分辨率缩放，今天同事笔记本缩放调到175%，直接把我页面整废了，抓紧修一下！&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
  </entry>
  
  <entry>
    <title>解决vue-scroll-behavior不生效问题</title>
    <link href="http://example.com/post/b0379d51.html"/>
    <id>http://example.com/post/b0379d51.html</id>
    <published>2023-04-28T16:44:54.000Z</published>
    <updated>2025-10-11T09:39:21.958Z</updated>
    
    <content type="html"><![CDATA[<p>之前研究过vue-scroll-behavior源码，操作也都清楚，奈何配置好了，切换页面后无效，也不滚动到指定位置。。</p><span id="more"></span><h2 id="页面嵌套问题"><a href="#页面嵌套问题" class="headerlink" title="页面嵌套问题"></a>页面嵌套问题</h2><p>正常来说，想要实现返回列表后保留原来位置，需要给列表页面添加keepAlive，保证数据切换页面不会丢失。</p><p>因为我在App.vue里面加了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">// App.vue</span><br><span class="line">&lt;transition :name=&quot;$route.meta.transition&quot; duration=&quot;300&quot;&gt;</span><br><span class="line">    &lt;template v-if=&quot;noKeepAliveRoute&quot;&gt;</span><br><span class="line">&lt;router-view v-slot=&quot;&#123; Component, route &#125;&quot;&gt;</span><br><span class="line">    &lt;component :is=&quot;Component&quot; :key=&quot;route.path&quot; /&gt;</span><br><span class="line">        &lt;/router-view&gt;</span><br><span class="line">    &lt;/template&gt;</span><br><span class="line">    &lt;template v-else&gt;</span><br><span class="line">        &lt;KeepAlive :max=&quot;10&quot;&gt;</span><br><span class="line">            &lt;router-view v-slot=&quot;&#123; Component, route &#125;&quot;&gt;</span><br><span class="line">            &lt;component :is=&quot;Component&quot; :key=&quot;route.path&quot; /&gt;</span><br><span class="line">            &lt;/router-view&gt;</span><br><span class="line">        &lt;/KeepAlive&gt;</span><br><span class="line">    &lt;/template&gt;</span><br><span class="line">&lt;/transition&gt;</span><br></pre></td></tr></table></figure><p>并且我确定我的列表页不在noKeepAliveRoute之列，所以是能够缓存列表然后滚动的。</p><p>实际却是切换回来修改scrollTop不管改什么值都是0。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p>随后我在生命周期created中打印个字符串’inited’，切换页面后发现它打印了两遍：第一次一遍，再切页面一遍。</p><img                         lazyload                       alt="image"                       data-src="/post/b0379d51/g1.gif"                        class="" title="动画"                 ><p>说明该页面没有缓存上。</p><p>遂检查路由，发现这个页面嵌了不止一层，所以说，在最近的一层上面加上keep-alive就好了。</p><h2 id="动画问题"><a href="#动画问题" class="headerlink" title="动画问题"></a>动画问题</h2><p>解决了吗，仍然没有，虽然上面的可以了，但是有些页面还是如以前一样。</p><p>我的方法是把源码copy到本地，引入后打断点。</p><p>果然，在设置返回列表页面的scrollTop的代码断点中，我发现列表页还没加载出来，还停留在详情页时就执行了。</p><img                         lazyload                       alt="image"                       data-src="/post/b0379d51/image-20230428170149228.png"                        class="" title="image-20230428170149228"                 ><p>碍于本人对加载时机极其敏感（写原生写的），这时候列表页面还没加载出来，设置scrollTop一定是无效的，所以应该等页面加载出来再设置scrollTop。</p><p>尝试setTimeout发现需要指定一个猜测出来的毫秒数，不过有些页面加载速度不一样，不能用这种土方法。</p><p>比较和之前修复的页面的区别，发现该页面添加了页面切换动画，记得动画是先生成了下一组件dom后添加css动画的</p><img                         lazyload                       alt="image"                       data-src="/post/b0379d51/image-20230428170749677.png"                        class="" title="image-20230428170749677"                 ><p>果然，现在#app上有两个页面，vueScrollBehavior插件用的querySelector选中元素默认选中了第一个，而第一个是旧的页面，设置旧的页面是没有意义的，我们要设置新的页面。</p><p>为了省事，干脆把querySeletor改成querySeletorAll即可。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">vue-scroll-behavior\src\utils\helpers.<span class="property">js</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Setting Scroll Position</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">setScrollPosition</span>(<span class="params">Vue, position = &#123; x: <span class="number">0</span>, y: <span class="number">0</span> &#125;</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> <span class="title function_">scrollTo</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">    <span class="title class_">Vue</span>.<span class="title function_">nextTick</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">+      <span class="keyword">const</span> el = <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(vueScrollBehavior.<span class="property">_el</span>)</span><br><span class="line">+      <span class="keyword">if</span> (el.<span class="property">length</span>) &#123;</span><br><span class="line">+        el.<span class="title function_">forEach</span>(<span class="function">(<span class="params">el</span>) =&gt;</span> &#123;</span><br><span class="line">+          el.<span class="property">scrollLeft</span> = position.<span class="property">x</span></span><br><span class="line">+          el.<span class="property">scrollTop</span> = position.<span class="property">y</span></span><br><span class="line">+          el.<span class="title function_">scrollTo</span>(position.<span class="property">x</span>, position.<span class="property">y</span>)</span><br><span class="line">+        &#125;)</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable language_">window</span>.<span class="title function_">scrollTo</span>(position.<span class="property">x</span>, position.<span class="property">y</span>)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (vueScrollBehavior.<span class="property">_delay</span> &gt; <span class="number">0</span>)</span><br><span class="line">    <span class="built_in">setTimeout</span>(scrollTo, vueScrollBehavior.<span class="property">_delay</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="title function_">scrollTo</span>()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>问题成功解决了！</p><img                         lazyload                       alt="image"                       data-src="/post/b0379d51/%E5%8A%A8%E7%94%BB-1682673109569.gif"                        class="" title="动画"                 >]]></content>
    
    
    <summary type="html">&lt;p&gt;之前研究过vue-scroll-behavior源码，操作也都清楚，奈何配置好了，切换页面后无效，也不滚动到指定位置。。&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
  </entry>
  
  <entry>
    <title>面试提问经验技巧</title>
    <link href="http://example.com/post/6e1cc159.html"/>
    <id>http://example.com/post/6e1cc159.html</id>
    <published>2023-04-17T21:59:52.000Z</published>
    <updated>2025-10-11T09:39:22.110Z</updated>
    
    <content type="html"><![CDATA[<p>最近公司又要招人，一般我面试别人都是随口出题，想到什么问什么，感觉这样不太尊重面试者，所以今天总结一下最佳面试实践，追求在最短的时间衡量面试者水平。</p><span id="more"></span><p><a class="link"   href="https://www.bilibili.com/video/BV1Jt411J7Ey/" >程序员面试官揭秘：我是如何刁难候选人的？_哔哩哔哩_bilibili<i class="fas fa-external-link-alt"></i></a></p><p>根据UP主“欲三更”所说，好问题分为下面5类</p><h2 id="高区分、多维度、防准备"><a href="#高区分、多维度、防准备" class="headerlink" title="高区分、多维度、防准备"></a>高区分、多维度、防准备</h2><p>题目要区分度高，任何人都能答上来两句，但水平不一样说的话也不一样</p><p>多维度：既考察思维，又考察经验，问题很宽泛</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p>防准备：答案在网上搜不到，或者说答案不唯一</p><p>例如：谈谈你对XX的理解</p><p>听面试者回答不仅仅只看答案结果本身，还要看面试者语言组织能力，思维表达是否顺畅，回答的深度在项目中的占位</p><h2 id="大体量"><a href="#大体量" class="headerlink" title="大体量"></a>大体量</h2><p>答案的体量非常大，甚至能写一本书。看面试者描述答案的系统性、全面性，对答案的前因后果的理解。</p><p>例如：我的网页加载很慢，如何优化？</p><p>好的回答应该从DNS，缓存，首屏加载优化，优化技巧（懒加载骨架屏预加载），代码裁剪，素材优化，渐进增强开始展开。</p><p>而差一点的，明显没有实际研究过的，只会背背网上答案。</p><h2 id="面向切面提问"><a href="#面向切面提问" class="headerlink" title="面向切面提问"></a>面向切面提问</h2><p>主要看知识的广度和深度。</p><p>比如问：react与vue的异同？为什么react需要各种性能优化手段，而vue不需要优化便能有很高的性能？</p><p>这通常是不同框架实现的不同，若是对框架有深入思考，回答这类问题就很简单了。</p><h2 id="灵魂追问之认知层次"><a href="#灵魂追问之认知层次" class="headerlink" title="灵魂追问之认知层次"></a>灵魂追问之认知层次</h2><p>认知层次分为：</p><p>记忆-&gt;理解-&gt;应用-&gt;分析-&gt;评价-&gt;创新</p><p>一般问到分析，就可以了解面试者对一类知识的思考深度。</p><p>比如：前端路由是什么？是用来解决什么问题？是用什么方式解决问题的？</p><p>没有思考过的只知道使用，而不知道其存在的意义，其原理所在。</p><p>又如：前端vue-router配置路由表是通过统一定义到route里面，而像nestjs或者springboot是通过装饰器&#x2F;注解定义到方法上面，比较分散。这两种方式的区别？优缺点？你会选择哪一种实现？理由是？</p><p>这类问题没有标准答案，需要言之有理，属于分析层次，类似问市面上竞品优缺点。</p><h2 id="STAR分析法"><a href="#STAR分析法" class="headerlink" title="STAR分析法"></a>STAR分析法</h2><p>有时候怀疑面试者项目经验造假，只需要问几个问题便知道深浅了。</p><p>S：situation情景，你当时为什么做这个项目，当时遇到了什么问题</p><p>T：task任务，这个项目的目标是什么，对团队有什么效果</p><p>A：action行动，为了实现目标，做了哪些事</p><p>R：result结果，项目做完后结果是什么，最好是量化数据</p><p>比如：</p><p>FAQ项目当时几个客服？每天订单量多少，是否是客服不够用了？这是S</p><p>当时做FAQ的目标是什么？这是T</p><p>你在FAQ项目中做了哪些事？这是A</p><p>FAQ为客服省了多少时间？这是R</p><p>提问时不会直接问题目，而是先猜测面试者处于项目中的位置，然后提问问题去验证。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近公司又要招人，一般我面试别人都是随口出题，想到什么问什么，感觉这样不太尊重面试者，所以今天总结一下最佳面试实践，追求在最短的时间衡量面试者水平。&lt;/p&gt;</summary>
    
    
    
    
    <category term="瞎扯" scheme="http://example.com/tags/%E7%9E%8E%E6%89%AF/"/>
    
  </entry>
  
  <entry>
    <title>聊聊近况</title>
    <link href="http://example.com/post/549a398b.html"/>
    <id>http://example.com/post/549a398b.html</id>
    <published>2023-04-14T06:00:11.000Z</published>
    <updated>2025-10-11T09:39:21.858Z</updated>
    
    <content type="html"><![CDATA[<p>复盘最近线上bug以及所有人对此的态度。</p><span id="more"></span><h2 id="事情经过"><a href="#事情经过" class="headerlink" title="事情经过"></a>事情经过</h2><p>暂且将最近做的项目分为系统级项目A，小网页B</p><p>A项目在年前（2022年末）就做完了，一直没上线，后面有些新添的需求陆续在加，直到前天拿去青岛交付，主要就是向目标群体展示系统功能，教后台怎么操作。</p><p>如你所见，交付时翻车了，比如用户名不管怎么写都会提示重复、打开页面404、打开页面报php500的那种代码堆栈信息、没删干净的图形码（当时登录的获取验证码需要先填图形码，后来给删了，但是没考虑到其他依赖发送验证码的函数，导致发送验证码异常）</p><p>不止翻车，从青岛回来还带了一堆新需求回来，一些操作逻辑我们没想到的，或者说他们临时变卦，或者说沟通问题，需求不明确的地方是按照我们想法做的，这次都要给他们加上，或者说改动。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p>这事老板们知道了，请全技术部去办公室开会，直接就问负责人是谁：当然有我，还有一后端。</p><p>然后拿出客户反馈的截图，图上是一个登录界面（宽度特别宽，高度特别扁，这分明就是在电脑端打开的，我们项目是H5的，当时vw适配的）然后他就开始输出了：这是正常人能做出来的吗，我平时用的可不是这样，人家客户心里怎么想……（此处省略一堆唾沫星子）。这就属于胡搅蛮缠了</p><p>另外一个是登录界面正常在手机端打开的，说是登录按钮写的“登录&#x2F;注册”，他找不到注册的地方。实际上我们做的是：如果有账号就登录，没有账号就自动注册。找不到情有可原，我们没有给标注，我背锅好吧。</p><p>再有，拿出之前做的项目B，其中关于我们页面留的电话是错的，立即试用跳的链接是错的。同样是一顿输出，再拿出别人网站和我们对比，结果可想而知。后来又扯到招我们进来的时候没走老板面试，直接让技术面的就过来了，以后要改这个。你们不要以为转正了就高枕无忧了，这都做不好，咱自研的项目更做不好。</p><p>其他的就是后端问题了，线上bug确实不应该，希望他接受批评认真改正吧，但我同样认为人身攻击不可取，即使是发生这种情况。</p><h2 id="我们的态度"><a href="#我们的态度" class="headerlink" title="我们的态度"></a>我们的态度</h2><p>老板们叨叨完之后，问我们有什么想说的，我自然是指出客户是用电脑打开的，我们做的是手机端的项目，另外一个想说没插上嘴，算了你强你有理。</p><p>后端直接愣住了，不说话。</p><p>项目经理：（以下是长话短说版）我觉得沟通有问题，管理有问题，给技术们分配时间不合理。老板：哦哦还有要说的吗？</p><p>设计：确实，老板说的有道理，我之前公司要求更高。</p><p>回到工位，后端老老实实改bug，别的啥都不干了。</p><p>到了下午下班，之前他第一个拔腿就跑，这次说要加班，让我先走吧。我回去也没啥事，在公司找点学习资料看看陪他加班，直到9点，我们回去了。</p><h2 id="我的看法"><a href="#我的看法" class="headerlink" title="我的看法"></a>我的看法</h2><p>这感觉似曾相识，查了一下和PUA完全对应，与批评不同，PUA会打压弱者，挑弱者毛病，并人身攻击，利用你对他的信任，将黑的说成白的，让你怀疑自己黑的究竟是不是黑，或许真的就是他说的白。目的就是让你接受他的意识，完全按照他的想法来。</p><p>我和产品经理是不服的，我们谈论事实，为自己辩解，而不是全盘听老板怎么说，这属于第二阶段了。</p><p>后端不知道怎么回事，一句话不说，还加班到9点。</p><p>人难免犯错，若是一犯错就被打压、人身攻击，那我们只能谨小慎微，不敢轻易尝试新鲜东西。相反，若是犯了错没有人站出来指正，这个错误便会一直存在。如果一个事物不受到外力作用，那么它的运动轨迹是无法改变的。</p><p>为了免于陷入困境，我决定抛弃老板们的看法，就事论事，错了就是错了，没错就是没错，人身攻击什么的我不在乎，我也不是刻意要讨好你。对于错误的我认真改正，正确的我坚决维护</p><p><a class="link"   href="https://www.bilibili.com/video/BV1a44y177Em/?spm_id_from=..top_right_bar_window_history.content.click&vd_source=1ec8998e58f8cdf9b1492f4de118be1a" >远比PUA恐怖的精神控制，可能就在你身边！3种煤气灯人如何一步步让你沦陷？如何摆脱煤气灯操控！_哔哩哔哩_bilibili<i class="fas fa-external-link-alt"></i></a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;复盘最近线上bug以及所有人对此的态度。&lt;/p&gt;</summary>
    
    
    
    
    <category term="瞎扯" scheme="http://example.com/tags/%E7%9E%8E%E6%89%AF/"/>
    
  </entry>
  
  <entry>
    <title>优化图片上传预览框代码</title>
    <link href="http://example.com/post/29e06f9d.html"/>
    <id>http://example.com/post/29e06f9d.html</id>
    <published>2023-04-11T18:23:44.000Z</published>
    <updated>2025-10-11T09:39:21.618Z</updated>
    
    <content type="html"><![CDATA[<p>项目用jQuery写的，一些操作dom逻辑混杂在一起，不够清晰，于是重构了一下，把逻辑分离出来，方便以后维护。</p><span id="more"></span><h2 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h2><p>类似pubsub，监听attr变化，更新dom<br>而数据处理部分仅仅需要往attr上写数据，不需要关心dom如何渲染</p><h2 id="实现代码"><a href="#实现代码" class="headerlink" title="实现代码"></a>实现代码</h2><p>稍微展示一下重点代码</p><p>看看界面长什么样吧！</p><p>未选中状态</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><img                         lazyload                       alt="image"                       data-src="/post/29e06f9d/image-20230411184805092.png"                        class="" title="image-20230411184805092"                 ><p>选中状态</p><img                         lazyload                       alt="image"                       data-src="/post/29e06f9d/image-20230411184840406.png"                        class="" title="image-20230411184840406"                 ><p>可以看到，未选中状态需要展示文本“添加自定义图片”，选中状态需要展示图片和删除按钮<br>若是直接编写代码，采用命令式的方式，需要判断当前状态，然后根据状态来决定如何渲染dom<br>这会导致状态切换逻辑混杂DOM操作，使代码不够清晰，不利于维护</p><p>而采用数据驱动的方式，只需要往attr上写数据，然后监听attr变化，根据attr的值来渲染dom，这样就可以把状态切换逻辑和DOM操作分离开来，使代码更加清晰</p><p>这是DOM结构</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;editor-image-choice&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;editor-image--view rounded&quot;</span> <span class="attr">style</span>=<span class="string">&quot;display: none;&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;&quot;</span> <span class="attr">class</span>=<span class="string">&quot;image-view__img&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot; init-text text-gray text-xs image-view__text&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">p</span>&gt;</span>请选择图片<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">p</span>&gt;</span>支持JPG，PNG，GIF<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;del-icon image-view__del&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">img</span>   <span class="attr">src</span>=<span class="string">&quot;/edit3d/assets/image/icon/del.png&quot;</span>  <span class="attr">alt</span>=<span class="string">&quot;&quot;</span> &gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;editor-image--content&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;editor-image--text&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;text text-xs text-gray&quot;</span>&gt;</span>建议尺寸：50 x 50<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">class</span>=<span class="string">&quot;btn btn-primary editor-image--btn choice-logo-image-btn&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;text-sm&quot;</span>&gt;</span>添加图片<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这里是配合该组件的脚本</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@typedef</span> <span class="variable">Config</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@property</span> &#123;<span class="type">string</span>&#125; url 初始图片链接</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@property</span> &#123;<span class="type">Function</span>&#125; onChange 图片链接变更回调</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> 实现图片上传选择组件</span></span><br><span class="line"><span class="comment"> * 思路：初始化为容器设置当前选中的链接属性，删除后清空链接属性，上传后设置链接属性</span></span><br><span class="line"><span class="comment"> * 元素属性变更监听器：</span></span><br><span class="line"><span class="comment"> *  判断当前元素属性值</span></span><br><span class="line"><span class="comment"> *    1. 有值：显示图片，隐藏文字</span></span><br><span class="line"><span class="comment"> *    2. 无值：显示文字，隐藏图片</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &#123;<span class="type">string</span>&#125; selector 图片容器元素选择器</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &#123;<span class="type">Config</span>&#125; cfg </span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">useImagePicker</span> = (<span class="params">selector, cfg = &#123;&#125;</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> $container = $(selector)</span><br><span class="line">  <span class="keyword">const</span> $viewContainer = $container.<span class="title function_">find</span>(<span class="string">&#x27;.editor-image--view&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> $viewImg = $container.<span class="title function_">find</span>(<span class="string">&#x27;.image-view__img&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> $viewText = $container.<span class="title function_">find</span>(<span class="string">&#x27;.image-view__text&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> $delIcon = $container.<span class="title function_">find</span>(<span class="string">&#x27;.image-view__del&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> $uploadBtn = $container.<span class="title function_">find</span>(<span class="string">&#x27;.editor-image--content .editor-image--btn&#x27;</span>)</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">setImageUrl</span>(<span class="params">url</span>) &#123;</span><br><span class="line">    $container.<span class="title function_">attr</span>(<span class="string">&#x27;image-url&#x27;</span>, url)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">isImgSetted</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> $container.<span class="title function_">attr</span>(<span class="string">&#x27;image-url&#x27;</span>) !== <span class="string">&#x27;&#x27;</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">setError</span>(<span class="params"></span>) &#123;</span><br><span class="line">    $viewContainer.<span class="title function_">addClass</span>(<span class="string">&#x27;error&#x27;</span>)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">removeError</span>(<span class="params"></span>) &#123;</span><br><span class="line">    $viewContainer.<span class="title function_">removeClass</span>(<span class="string">&#x27;error&#x27;</span>)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 监听attr变更</span></span><br><span class="line">  tools.<span class="title function_">observeDomAttribute</span>(selector, <span class="string">&#x27;image-url&#x27;</span>, <span class="keyword">function</span> (<span class="params">url</span>) &#123;</span><br><span class="line">    $viewImg.<span class="title function_">attr</span>(<span class="string">&#x27;src&#x27;</span>, tools.<span class="title function_">urlResolve</span>(url))</span><br><span class="line">    <span class="keyword">if</span> (url === <span class="string">&#x27;&#x27;</span>) &#123;</span><br><span class="line">      $viewImg.<span class="title function_">hide</span>()</span><br><span class="line">      $viewText.<span class="title function_">show</span>()</span><br><span class="line">      $delIcon.<span class="title function_">hide</span>()</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      $viewImg.<span class="title function_">show</span>()</span><br><span class="line">      $viewText.<span class="title function_">hide</span>()</span><br><span class="line">      $delIcon.<span class="title function_">show</span>()</span><br><span class="line">      <span class="comment">// 设置了图片后要清除错误状态</span></span><br><span class="line">      <span class="title function_">removeError</span>()</span><br><span class="line">    &#125;</span><br><span class="line">    $viewContainer.<span class="title function_">show</span>()</span><br><span class="line">    <span class="comment">// 执行change回调</span></span><br><span class="line">    cfg.<span class="property">onChange</span> &amp;&amp; cfg.<span class="title function_">onChange</span>(url)</span><br><span class="line">  &#125;);</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 初始化</span></span><br><span class="line">  <span class="title function_">setImageUrl</span>(cfg.<span class="property">url</span> || <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">  <span class="comment">// 上传按钮点击后打开媒体库选择图片，选择后设置当前图片链接状态</span></span><br><span class="line">  $uploadBtn.<span class="title function_">click</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="title function_">openMediaSourceDialog</span>(<span class="string">&#x27;image&#x27;</span>, <span class="keyword">function</span> (<span class="params">url</span>) &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;selected&#x27;</span>, url)</span><br><span class="line">      <span class="title function_">setImageUrl</span>(url)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="comment">// 删除按钮点击后清空当前图片链接状态</span></span><br><span class="line">  $delIcon.<span class="title function_">on</span>(<span class="string">&#x27;click&#x27;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="title function_">setImageUrl</span>(<span class="string">&#x27;&#x27;</span>)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    setImageUrl,</span><br><span class="line">    setError,</span><br><span class="line">    isImgSetted</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 使用时，只需传入选择器，并且在图片变更时做外部的操作即可</span></span><br><span class="line"><span class="keyword">let</span> imgPicker = <span class="literal">null</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">initImageView</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> imgUrl = <span class="keyword">await</span> <span class="title function_">getImgUrl</span>() </span><br><span class="line">  imgPicker = <span class="title function_">useImagePicker</span>(<span class="string">&quot;.editor-image-choice&quot;</span>, &#123;</span><br><span class="line">    <span class="attr">url</span>: imgUrl,</span><br><span class="line">    <span class="title function_">onChange</span>(<span class="params">url</span>) &#123;</span><br><span class="line">      <span class="comment">// dosomething(url) ... </span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 初始化图片预览框</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="title function_">initImageView</span>()</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 提交时判断是否设置了图片</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">onSubmit</span>(<span class="params"></span>)&#123;</span><br><span class="line">  <span class="keyword">const</span> isImgSet = imgPicker?.<span class="title function_">isImgSetted</span>()</span><br><span class="line">  <span class="keyword">if</span> (!isImgSet) &#123;</span><br><span class="line">    imgPicker.<span class="title function_">setError</span>()</span><br><span class="line">    layer.<span class="title function_">msg</span>(<span class="string">&quot;请上传图片&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// submit...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>基本就是这样，感觉很简单有没有？这就是数据驱动的魅力！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;项目用jQuery写的，一些操作dom逻辑混杂在一起，不够清晰，于是重构了一下，把逻辑分离出来，方便以后维护。&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
  </entry>
  
  <entry>
    <title>讨论前端模块间通信5种方式</title>
    <link href="http://example.com/post/bea61df6.html"/>
    <id>http://example.com/post/bea61df6.html</id>
    <published>2023-04-06T20:58:08.000Z</published>
    <updated>2025-10-11T09:39:22.058Z</updated>
    
    <content type="html"><![CDATA[<p>前端划分组件后，想要实现代码耦合度低并且井然有序，就必须认识常见模块间通信方式的实现及其优缺点，准确把握应用场景，在复杂与简单之间寻找平衡。</p><span id="more"></span><p>推荐了解：<a class="link"   href="https://dev.to/luistak/cross-micro-frontends-communication-30m3" >⚔️ Cross micro frontends communication 📦 - DEV Community<i class="fas fa-external-link-alt"></i></a></p><p>前端模块间通信方式大致5种：</p><ul><li>web workers</li><li>props &amp; callbacks</li><li>custom events</li><li>pub&#x2F;sub library</li><li>结合场景自行实现通信</li></ul><p>其中web workers我不经常使用，后面4个皆有在最近做的项目中使用到，简单讲讲用到的场景以及使用感受。</p><h2 id="前端模块间通信方式"><a href="#前端模块间通信方式" class="headerlink" title="前端模块间通信方式"></a>前端模块间通信方式</h2><h3 id="props-callback"><a href="#props-callback" class="headerlink" title="props &amp; callback"></a>props &amp; callback</h3><p>比如父页面弹出iframe子窗口</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p>父传子：父页面打开子窗口之前可以通过设置一个全局变量，子窗口加载时去读全局变量，此时就是最新的数据</p><p>子传父：子窗口可以调用父页面的全局函数，将自己内部的数据传回父页面。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.html 父页面</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="property">props</span> = &#123;</span><br><span class="line">    <span class="attr">message</span>: <span class="string">&#x27;hello&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="variable language_">window</span>.<span class="property">setProps</span> = <span class="function"><span class="params">val</span> =&gt;</span> <span class="title class_">Object</span>.<span class="title function_">assign</span>(&#123;&#125;, <span class="variable language_">window</span>.<span class="property">props</span>, val)</span><br><span class="line">layer.<span class="title function_">open</span>(<span class="string">&#x27;./dialog.html&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// dialog.html 子窗口</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(parent.<span class="property">props</span>) <span class="comment">// hello</span></span><br><span class="line">parent.<span class="title function_">setProps</span>(&#123;</span><br><span class="line">    <span class="attr">message</span>: <span class="string">&#x27;world&#x27;</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>优点：这种方式最直接，最容易接受，也是最常见的手段。</p><p>缺点：没有工程化框架时（如Vue, React…）只能依赖全局变量共享，全局变量本身带有局限性。</p><h3 id="custom-event"><a href="#custom-event" class="headerlink" title="custom event"></a>custom event</h3><p>js原生支持自定义事件，你可以在任何时机发布自己的事件并携带数据。因此，可以使用其进行通信。</p><p>父页面：加载API数据到达，向全局发送’data-loaded’事件。</p><p>依赖API数据的页面：监听‘data-loaded’，对数据处理。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="title function_">request</span>().<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> customEvent = <span class="keyword">new</span> <span class="title class_">CustomEvent</span>(<span class="string">&#x27;data-loaded&#x27;</span>, &#123; result &#125;);</span><br><span class="line">    <span class="variable language_">window</span>.<span class="title function_">dispatchEvent</span>(customEvent)</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// card.js</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">addEvevntListener</span>(<span class="string">&#x27;data-loaded&#x27;</span>, <span class="function"><span class="params">result</span> =&gt;</span> &#123;</span><br><span class="line">    result.<span class="title function_">map</span>( <span class="comment">/* do something... */</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>优点：不依赖全局变量，不限于框架&#x2F;原生，因为自定义事件原生支持</p><p>缺点：如果监听事件比触发事件晚了（触发事件时还没来得及监听），会导致收不到事件；编码难度较高，需要有代码设计能力。</p><h3 id="pub-sub-library"><a href="#pub-sub-library" class="headerlink" title="pub&#x2F;sub library"></a>pub&#x2F;sub library</h3><p>依靠基于pub&#x2F;sub模式的代码库可以轻松实现页面间通信，用的比较顺手的rxjs足够应付大多数场景，最近也用到了其中的subject。</p><p>父页面：创建可观察对象，管理数据增删改查，而不关心视图结构。</p><p>子页面：订阅可观察对象，依据父页面数据渲染页面。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="property">subject</span> = <span class="keyword">new</span> rxjs.<span class="title class_">BehaviorSubject</span>([]);</span><br><span class="line"><span class="title function_">request</span>().<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =&gt;</span> <span class="variable language_">window</span>.<span class="property">subject</span>.<span class="title function_">next</span>(result))</span><br><span class="line"></span><br><span class="line"><span class="comment">// card.js</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="property">subject</span>.<span class="title function_">subscribe</span>(</span><br><span class="line"><span class="function"><span class="params">val</span> =&gt;</span> <span class="title function_">render</span>(val)</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>优点：简单直接，代码轻量；rxjs本身很强大，可处理复杂场景；</p><p>缺点：依赖rxjs增加页面负担；同自定义事件类似</p><h2 id="触发事件晚于监听事件解决办法"><a href="#触发事件晚于监听事件解决办法" class="headerlink" title="触发事件晚于监听事件解决办法"></a>触发事件晚于监听事件解决办法</h2><p>在RxJS中，可以使用Subject来实现监听者和监听的功能。如果要监听监听者触发监听之前的更新，可以将监听者的更新先存放在一个数组中，然后在Subject进行订阅时，先将存放的更新发送出去，再将Subject本身加入到监听者中，以便后续的更新可以被Subject监听到。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Subject</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;rxjs&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Listener</span> &#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="attr">updates</span>: <span class="built_in">any</span>[] = [];</span><br><span class="line">  <span class="keyword">private</span> subject = <span class="keyword">new</span> <span class="title class_">Subject</span>();</span><br><span class="line"></span><br><span class="line">  <span class="title function_">addUpdate</span>(<span class="params"><span class="attr">update</span>: <span class="built_in">any</span></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">updates</span>.<span class="title function_">push</span>(update);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">listen</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 发送之前的更新</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">updates</span>.<span class="title function_">forEach</span>(<span class="function">(<span class="params">update</span>) =&gt;</span> <span class="variable language_">this</span>.<span class="property">subject</span>.<span class="title function_">next</span>(update));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 加入Subject进行后续的监听</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">subject</span>.<span class="title function_">subscribe</span>(<span class="function">(<span class="params">update</span>) =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;update:&#x27;</span>, update));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">update</span>(<span class="params"><span class="attr">update</span>: <span class="built_in">any</span></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">addUpdate</span>(update);</span><br><span class="line">    <span class="comment">// 触发监听</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">listen</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> listener = <span class="keyword">new</span> <span class="title class_">Listener</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更新之前，先监听</span></span><br><span class="line">listener.<span class="title function_">listen</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更新</span></span><br><span class="line">listener.<span class="title function_">update</span>(<span class="string">&#x27;update 1&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 再次监听</span></span><br><span class="line">listener.<span class="title function_">listen</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更新</span></span><br><span class="line">listener.<span class="title function_">update</span>(<span class="string">&#x27;update 2&#x27;</span>);</span><br></pre></td></tr></table></figure><p>以上代码来源：ChatGPT 3.5</p><p>但是直接使用<code>ReplaySubject</code>即可解决问题，这是<code>ReplaySubject</code>的描述：</p><img                         lazyload                       alt="image"                       data-src="/post/bea61df6/ReplaySubject.png"                        class="" title="img"                 ><blockquote><p><strong>ReplaySubject</strong> 将对观察者发送全部的元素，无论观察者是何时进行订阅的。</p><p>这里存在多个版本的 <strong>ReplaySubject</strong>，有的只会将最新的 n 个元素发送给观察者，有的只会将限制时间段内最新的元素发送给观察者。  ——<a class="link"   href="https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/rxswift_core/observable_and_observer/replay_subject.html" >ReplaySubject · RxSwift 中文文档 (beeth0ven.github.io)<i class="fas fa-external-link-alt"></i></a></p></blockquote>]]></content>
    
    
    <summary type="html">&lt;p&gt;前端划分组件后，想要实现代码耦合度低并且井然有序，就必须认识常见模块间通信方式的实现及其优缺点，准确把握应用场景，在复杂与简单之间寻找平衡。&lt;/p&gt;</summary>
    
    
    
    
    <category term="coding" scheme="http://example.com/tags/coding/"/>
    
  </entry>
  
  <entry>
    <title>谈生命与生活</title>
    <link href="http://example.com/post/7f64cd66.html"/>
    <id>http://example.com/post/7f64cd66.html</id>
    <published>2023-04-01T20:31:36.000Z</published>
    <updated>2025-10-11T09:39:22.070Z</updated>
    
    <content type="html"><![CDATA[<p>今天百感交集，晚上睡不着觉爬起来总结一下自己最近的想法，关于生命的意义方面的，仍在迷茫，仍在思考。</p><span id="more"></span><h2 id="关于生命"><a href="#关于生命" class="headerlink" title="关于生命"></a>关于生命</h2><p>刚刚收到家里消息，年过80的姥爷与世长辞了，这早已是我们预料之中的事情，并没有让我猝不及防或是怎么样。</p><p>年前，姥爷因肠胃不舒服住院一直住，后来病情恶化去ICU了，母亲一直陪在身边打理吃喝拉撒，听父亲说ICU的医疗费每天一万，很贵。</p><p>奶奶在家里守着我们说“老头早点走了吧，别拖累小孩”，我听到是十分震惊的，我一直想的是尽快治好。</p><p>后来才知道，姥爷因为胃不好，导致肾也不好，肝也不好，全身上下都是毛病，由于身体状况恶化，正常的饮食已经无法为他提供足够的营养，让他的状况更加绝望。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><p>姥爷其实年轻的时候胃就不太好，他曾是我爷爷的工友，姥爷中午吃饭只吃钙奶饼干，煮鸡蛋汤喝了都会吐。</p><p>我印象里姥爷总是佝偻着背，身体极其瘦弱，不过姥爷之前当过兵。</p><p>上周就已经说快不行了，让我去看看姥爷，算是最后见一面。姥爷胳膊和腿已经和擀面杖差不多粗了，脸上也没什么肉，眼皮耷拉着，能感受到他已经用力去睁开眼睛，我只能朝他微笑，说不出话，我不知道说什么，他大概也知道我为什么过来。母亲跟我说姥爷得休息了，我便走了，那一面竟是最后一面。</p><p>生命很脆弱，家里人可能随时离开，我也可能先于他们，这种准备一定要提前打好，活在当下，希望我们都不留遗憾。</p><p>关于姥爷治病那一块，ICU呆了一个月吧，这个钱花就花了，我一想到这些就有些喘不开气，我舅一直没出去干活吧，可能花了几年挣来的钱都付给了医院，如果明知道治不好，明知道钱白花为什么还要去送钱呢？我恨这种医院，那最后一根稻草倘若不能支撑我上岸，我宁愿撒手走的潇洒一些，而不是将所有希望寄托在上面，最终换来一场空。</p><p>明天是周日，父亲让我回去送送姥爷，不敢看母亲的脸，想必她一定是最痛的。</p><h2 id="关于生活"><a href="#关于生活" class="headerlink" title="关于生活"></a>关于生活</h2><p>昨天，公司的另一位前端离职了，他刚来公司一个月，因为欠了一个月工资，公司没有按时发放</p><p>当时是我和组长一起面的，小伙技术扎实，他对公司安排的任务都能游刃有余，有时我也和他讨论问题，他总能给我带来一些新的技术分享。</p><p>他之前在上海呆过，来临沂了，正好命中老板心思：想找个大厂（北上广深）回来的，因为看新闻说大厂裁员，“向社会输出人才”。</p><p>他来面试那天其实就已经拖欠1个多月工资了，我那时候没关心这些事，毕竟临沂物价低，钱够花就没想别的。</p><p>我以为这都没啥，但是他告诉我：拖欠一个月就要准备找下家了，工资现金流都不稳定，小心老板当老赖一分钱工资没有。</p><p>他真的在拖欠1个月整之后离职了，HR告诉他工资会在月底发放，但是到昨天（31日）下午下班时还没有收到。</p><p>他离职后，我才后悔没把情况讲清楚告诉他，他貌似问过：工资准时发吗？没多想直接点头了。因为我之前就被坑过，老员工急着走，花式忽悠我入职替他位置，好让他走人（老板说交接完再走）。我是十分恨他的，更恨老板，在我走的时候同样的方法对我用了，我没理他直接拉黑。</p><p>我也无法控制这种事情的发生，之前一直是正常发工资，无非晚一天早一天的（发上上个月工资）。谁知道年后公司收不来款，导致大家工资都拖延。总之，他走了很可惜，我们好不容易招聘到了他。</p><p>大概是他有工作经验不愁找工作吧，反正我没有那个勇气了，我之前的实习离职也是因为不满意老板，所以没有考虑后果，看老板不爽就走，但是现在不一样了，现在招聘上三年经验以下的很少公司要了，我若是离职了，那就等于离开程序员行业了（臆想中的）</p><p>他和公司协商后还是没有收到工资，所以打算离职后去仲裁，就像他说的：既然公司违反了合同，我就会采取罢工行动。最终公司答应周一给他结清工资，看看吧，希望周一给他结清工资吧。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;今天百感交集，晚上睡不着觉爬起来总结一下自己最近的想法，关于生命的意义方面的，仍在迷茫，仍在思考。&lt;/p&gt;</summary>
    
    
    
    
    <category term="胡扯" scheme="http://example.com/tags/%E8%83%A1%E6%89%AF/"/>
    
  </entry>
  
  <entry>
    <title>聊新生代农民工</title>
    <link href="http://example.com/post/ab1ac1e2.html"/>
    <id>http://example.com/post/ab1ac1e2.html</id>
    <published>2023-04-01T06:49:02.000Z</published>
    <updated>2025-10-11T09:39:21.854Z</updated>
    
    <content type="html"><![CDATA[<p>最近刚了解到新生代农民工的概念（村刚通网），瞬间认清自己的定位，一切迷茫仿佛都消失了。</p><span id="more"></span><p>其实这个词”新生代农民工“早就有所耳闻，是在一位程序员UP主发的动态看到的，私以为特指00后码农，实则含义更广。</p><img                         lazyload                       alt="image"                       data-src="/post/ab1ac1e2/qqgroup.jpg"                        class="" title="img"                 ><p>如百度百科所说：</p><blockquote><p>新生代乡籍工指的是在1980年及之后出生的，进城从事非农业生产6个月及以上的，常住地在城市，户籍地在乡村的劳动力，是新时代的产业工人。他们很早进入社会，游离于城市和乡村之间，基本没有从事过农业生产，不像父辈那般依恋乡村。他们努力想变得和城市里的同龄人一样，但受到经济收入、文化程度等种种因素制约，城市对于他们来说依然没有归属感。<br>中国社科院研究员王春光用更为“学术”的语言对他们进行了描述：新生代乡籍工有76%未婚；基本上没有务农的经历，很多是从学校毕业后就直接外出的，甚至连基本的农业常识都缺乏；从动机上看，他们基本上不是基于“生存理性”外出，而是更多地将流动视为改变生活方式和寻求更好发展的契机。</p></blockquote><p>对于我等农村青年，确实没下过几次地，家人总是以“小孩学习去，别下地”为由（或者说我以此为由），拒绝我务农。<br>（实际学也没上好，混的也不咋地，稍微划划水我就得进厂了）<br>是的，目前我就是属于“游离于城市和乡村之间”的“两栖人”，周末偶尔回家，工作日正常在市里上班。你可以说我农村人也可以说我城里人（没买房的城里人），我对于这些扣帽子打标签不在乎。</p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script><blockquote><p>“新生代乡籍工”不再老老实实地待在最脏、最累、最“没出息”的工作岗位上，不再省吃俭用攒钱往家里寄，更不会挣够钱回家盖房娶媳妇。在受到歧视时，他们会摔门离去。</p></blockquote><p>教育发达了，大家都想要更好的生活，更好的发展，更好的自己。做苦力做久了就一辈子都是苦力，这样的生活一眼望到头，我觉得不是我想要的。<br>省吃俭用往家里寄钱，除非是家里很困难揭不开锅了要我去支持。<br>挣够钱回家盖房子这个感觉不现实，不是说挣不挣够钱，在农村盖房子还好，而是说就算盖了房子，我也不会回去住，没有我发展的空间，或者说盖了房子在家务农？我不想这样。”家是避风港，同样也是枷锁”</p><blockquote><p>第一代乡籍工普遍将谋生作为第一目标，建筑工地是他们的聚集地，新二代乡籍工的目光则投向三产服务业、制造业、纺织业、电子业等行业，赚钱只是他们外出打工的部分目的。他们希望在城里买房，能像城里人一样生活。<br>因为有着更多的企盼,他们找工作时也往往更“挑剔”。“虽然我来自农村，但这并不代表我对生活质量没有要求。我同样需要娱乐，需要交友，需要上网来了解全国各地的信息。”王玉明今年23岁，初中毕业，一家招工单位对他有意向，但因为单位地处偏僻，上网、外出都不方便，他一口回绝。<br>“我有技术，不愁工作，我要进大企业工作。我原来那家单位管吃管住，但早晨馒头、咸菜加白开水；中午一年到头都是炒白菜，这样的生活，我不能忍受。”这是一个第二代乡籍工对找工作一直持观望态度。<br>许多用工单位很需要这些人,但也对此表现出了相当的担忧。“现在不少年轻人，在工厂有点不高兴，什么也不想，马上走人。”一家企业招工负责人曾说，用人单位有一部分认为，第二代乡籍工存在的最大问题，就是敬业精神差。</p></blockquote><p>时代进步了，人们的生活水平提高了，人们的生活观念也发生了变化，但社会并没有在这方面给予更多的关照。关于待遇问题，公司起码保证把工人当人看，不会像以前那样，工人是什么都不是，只是个工具，只要能干活就行，不干活就赶走。但面临一个死结：工人人多，你不干有的是人干。总有缺钱的兄弟，我们不能要求他们不去干，来抵制这种现象。只能内卷吧，卷到自己不和这群人呆在一个水平上，待遇自然就上来了，这是我目前的看法。不要跟我扯什么“拒绝内卷”之类的，大家都是第一次当人，我若是能控制得了（实际谁都控制不了，就怨人太多，活太少。。。少生优生幸福一生）</p><blockquote><p>个人消费支出快速增加，不再为攒钱而使自己变成“苦行僧”——这是不同于父辈的显著特点。<br>据广东省统计局抽样调查显示，09年第二季度，新生代乡籍工平均每月挣钱873元，只交回老家232元，即将近八成的工资收入被自己花掉了，或者自己留着了。他们把超过一成的收入花在了文化娱乐和电话费上。新生代乡籍工在接受城市文化和生活方式的同时，也受到了一些不良风气和固有陋习的影响，出现了“娇子乡籍工”、“月光族”等现象。新生代乡籍工大多是刚刚从学校毕业就直接进入城市打工，既没有吃苦耐劳的思想准备，也没有吃苦耐劳的能力和精神，过分追求较好的生活，勤俭节约的品格逐渐淡化。调查显示，有68.5%的人收入主要用于自己的吃穿住行，工资很少寄回家，70%的人拥有手机或小灵通。一些人崇尚享受，注重攀比，非理性的消费观念让收入较低的乡籍工生活更加陷入困境。</p></blockquote><p>诚然，本人对奢侈品的追求同他们一样，经常月底没钱，不过目前有在控制。<br>最近眼看钱包没钱了，仔细一想也没花多少钱，无非是年初提了一辆喜德盛自行车、买了个2T的三星固态硬盘、16G的3200内存条而已。最后一看工资单，哦！原来是工资只发到了12月，截止目前4月1号，还没发工资，这下明白了，实际攒了万把块钱了，才明白自己不是“月光族”。月光族只能说赚的钱少了，而不要说攒不下钱，抛开上面大头消费，剩下的无非吃吃喝喝，交个房租水电了，能有什么花费。</p><img                         lazyload                       alt="image"                       data-src="/post/ab1ac1e2/mmexport1680306935920.jpg"                        class="" title="mmexport1680306935920"                 ><img                         lazyload                       alt="image"                       data-src="/post/ab1ac1e2/D5DAFFADD69976565EE72AF86161A1AD.jpg"                        class="" title="img"                 ><blockquote><p>随着中国城市化进程，从“卖苦力”到“拼智力”，从城乡两栖到定居城市，从单向涌入城市到开始回乡创业，中国乡籍工群体正在发生着脱胎换骨般的变化。“村妹不甘种稻麻，满怀志智闯天涯。绣衣出口显身手，四海街头飘彩霞。欧美澳，亚非拉，品牌名起客商夸。时时领创推新品，岁岁登台戴大花。”这是诗人左河水以《鹧鸪天·打工妹》一词讲述的家乡真人故事。</p></blockquote><p>必须指出，新时代的女青年不甘愿只做家庭主妇，不愿做”男人的附属品”，而是有更高的追求，更有活力，更有主见，积极为女性平权推波助澜。我见过很多即使家境优渥的女生，仍然努力劳动学习，甚至比男生付出的更多，她们清楚自己的方向，并为之艰苦奋斗，努力摒弃大家眼中的女生的消极看法。</p><p>我想起来《平凡的世界》中的孙少平：</p><blockquote><p>如果从小你就在这个天地里日出而作，日落而息，那你现在就会和众乡亲抱同一理想。经过几年的辛苦，像大哥一样娶个满意的媳妇儿，生个大胖儿子，加上你的体魄，会成为一名出色的庄稼人。不幸的是，你知道的太多了，思考的太多了，因此才有了这种不能为周围人所理解的苦恼。 ——路遥·《平凡的世界》</p></blockquote><p>愿新生代农民工们都能在城市里打拼出一片天地，无论出身，不甘平凡。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近刚了解到新生代农民工的概念（村刚通网），瞬间认清自己的定位，一切迷茫仿佛都消失了。&lt;/p&gt;</summary>
    
    
    
    
    <category term="瞎扯" scheme="http://example.com/tags/%E7%9E%8E%E6%89%AF/"/>
    
  </entry>
  
  <entry>
    <title>关于按钮不同状态颜色设计的看法</title>
    <link href="http://example.com/post/c8a91104.html"/>
    <id>http://example.com/post/c8a91104.html</id>
    <published>2023-03-24T20:14:34.000Z</published>
    <updated>2025-10-11T09:39:21.702Z</updated>
    
    <content type="html"><![CDATA[<p>按钮一般有三种状态：正常、悬停、点击。本来为了方便想做一个小工具生成各种状态的颜色来着，后来还是放弃了，因为这实在是太简单了。</p><span id="more"></span><p>工具大体上是这样的：<br>分为border和background两种颜色，然后根据不同的状态生成不同的颜色。<br>相当于笛卡尔积出2*3&#x3D;6种结果，通过css变量一一对应上预览的按钮。</p><p>但我将想法同网友分享后，网友直接甩我less代码，我就不得不放弃了，因为这实在是太简单了。</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@color-master:</span> <span class="number">#f51669</span>; <span class="comment">// ← change this color</span></span><br><span class="line"></span><br><span class="line"><span class="variable">@background-color:</span> <span class="variable">@color-master</span>;</span><br><span class="line"><span class="variable">@text-color:</span> <span class="built_in">lighten</span>(<span class="variable">@color-master</span>, <span class="number">45%</span>);</span><br><span class="line"><span class="variable">@hover-color:</span> <span class="built_in">lighten</span>(<span class="variable">@color-master</span>, <span class="number">10%</span>);</span><br><span class="line"><span class="variable">@body-color:</span> <span class="built_in">darken</span>(<span class="variable">@color-master</span>, <span class="number">30%</span>);</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.btn-primary</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="variable">@text-color</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="variable">@background-color</span>;</span><br><span class="line">  <span class="attribute">border</span>: none;</span><br><span class="line">  <span class="attribute">max-width</span>: <span class="number">50%</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.btn-primary</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="variable">@hover-color</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="variable">@body-color</span>;</span><br><span class="line">  <span class="attribute">display</span>: flex;</span><br><span class="line">  <span class="attribute">align-items</span>: center;</span><br><span class="line">  <span class="attribute">justify-content</span>: center;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">100vh</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最终经过编译后就是计算后的颜色值，没有什么兼容性问题（之前考虑filter能用但是兼容性不好，这个直接预编译了）。<br>less中有lighten和darken两个函数，分别是变亮和变暗，这样就可以很方便的生成不同状态的颜色了。<br>一般的做法也就同上面一样，hover提亮，active变暗，通过一个小函数就做出来了。<br>同样的方法在scss中也有。<br>关于lighten的计算方法：</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">lighten</span>(<span class="variable">@color</span>, <span class="variable">@amount</span>) &#123;</span><br><span class="line">  <span class="comment">// 将颜色转换为HSL格式</span></span><br><span class="line">  <span class="variable">@hue:</span> <span class="built_in">hue</span>(<span class="variable">@color</span>);</span><br><span class="line">  <span class="variable">@saturation:</span> <span class="built_in">saturation</span>(<span class="variable">@color</span>);</span><br><span class="line">  <span class="variable">@lightness:</span> <span class="built_in">lightness</span>(<span class="variable">@color</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 计算新的亮度值</span></span><br><span class="line">  <span class="variable">@new-lightness:</span> <span class="variable">@lightness</span> + (<span class="variable">@amount</span> % <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 限制亮度值在0~100之间</span></span><br><span class="line">  <span class="variable">@new-lightness:</span> <span class="built_in">max</span>(<span class="number">0</span>, <span class="built_in">min</span>(<span class="variable">@new-lightness</span>, <span class="number">100</span>));</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 将HSL值转换为RGB格式</span></span><br><span class="line">  <span class="variable">@rgb:</span> <span class="built_in">hsl2rgb</span>(<span class="variable">@hue</span>, <span class="variable">@saturation</span>, <span class="variable">@new-lightness</span>);</span><br><span class="line">  <span class="variable">@red:</span> <span class="built_in">extract</span>(<span class="variable">@rgb</span>, <span class="number">1</span>);</span><br><span class="line">  <span class="variable">@green:</span> <span class="built_in">extract</span>(<span class="variable">@rgb</span>, <span class="number">2</span>);</span><br><span class="line">  <span class="variable">@blue:</span> <span class="built_in">extract</span>(<span class="variable">@rgb</span>, <span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 构造新的颜色值</span></span><br><span class="line">  <span class="variable">@new-color:</span> <span class="built_in">rgb</span>(<span class="variable">@red</span>, <span class="variable">@green</span>, <span class="variable">@blue</span>);</span><br><span class="line">  <span class="variable">@new-color:</span> <span class="string">~&quot;#@&#123;colorstr(@new-color)&#125;&quot;</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 返回新的颜色值</span></span><br><span class="line">  <span class="variable">@return</span> <span class="variable">@new-color</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>附codepen:<br><a class="link"   href="https://codepen.io/timkl/pen/PqWxMz" >https://codepen.io/timkl/pen/PqWxMz<i class="fas fa-external-link-alt"></i></a></p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9469686835170190"     crossorigin="anonymous"></script><ins class="adsbygoogle"     style="display:block; text-align:center;"     data-ad-layout="in-article"     data-ad-format="fluid"     data-ad-client="ca-pub-9469686835170190"     data-ad-slot="7263645837"></ins><script>     (adsbygoogle = window.adsbygoogle || []).push({});</script>]]></content>
    
    
    <summary type="html">&lt;p&gt;按钮一般有三种状态：正常、悬停、点击。本来为了方便想做一个小工具生成各种状态的颜色来着，后来还是放弃了，因为这实在是太简单了。&lt;/p&gt;</summary>
    
    
    
    
    <category term="胡扯" scheme="http://example.com/tags/%E8%83%A1%E6%89%AF/"/>
    
  </entry>
  
</feed>
