Jekyll 主题 Minimal Mistakes 使用总结
虽然我使用 Jekyll 已经有一段时间了,但一直没注意到 Minimal Mistakes 这个主题的存在。
用它改版个人技术博客后,感觉效果还不错。我打算用这个主题把森罗社官网也重构一下。
用这篇博文简单总结一下改版期间遇到的一些问题,留作后续参考。
关于 Minimal Mistakes
它的官网使用的便是这个主题,视觉效果还行,适合 startup 用,简单省事。
网页吊顶(Masthead)
在 _data
文件夹下新建一个 navigation.yml
文件即可。
支持中文
在 _config.yml
中添加一条配置:
locale: "zh-CN"
拷贝ui-text.yml 文件至 _data
目录下。
(可以删除其他不需要的语言)
我本来以为主题的 data
文件也是能够被复用的,事实证明并非如此,需要手动拷贝到项目中去。
GA
老的 GA 即将线下,现在要使用 GTM。
免费开通 GTM 之后,会得到一个 tracking_id
,然后在 _config.yml
文件中增加如下配置:
analytics:
provider : "google-gtag"
google:
tracking_id : "YOUR-TRACKING-ID"
anonymize_ip : false # default
评论区
评论区的作用是增加 customer engagement
我一直使用 Disqus,这次也不例外。
- 官网入口
Site Admin
=>Settings
=>Shortname
然后继续在 _config.yml
文件中添加配置:
comments:
provider : "disqus"
disqus:
shortname : 'feelang-github-io'
作者信息(Author)
- 直接查看官方文档
Page 文件统一管理
Jekyll 官方文档的示例代码中,通常将 Page
文件放在根目录下,但是这么做会使得文件目录结构看起来比较乱,尤其是当页面变多之后。
比较好的办法是将这些 Page
文件放在一个文件夹下进行统一管理,实现步骤如下:
- 在根目录下新建一个
_pages
文件夹 -
在
_config.yml
文件中新增一条配置:include: - _pages
这样 Page 文件就可以放到 _pages
文件夹中进行管理。
include
是 Jekyll 的一个配置命令:
Include: Force inclusion of directories and/or files in the conversion.
用法:include: [DIR, FILE, ...]
布局
这个主题提供了很多布局方式,这里只介绍几个常用的。
splash
splash
适用于 landing page,它继承自 default
。
这个主题的官网首页便是使用了这个布局。
下面简单分析下源码。
docs/_pages/home.md
的 Front Matter 定义了一个 header
变量:
header:
overlay_color: "#5e616c"
overlay_image: /assets/images/mm-home-page-feature.jpg
actions:
- label: "<i class='fas fa-download'></i> Install now"
url: "/docs/quick-start-guide/"
这个变量在 _layouts/splash.html
中会被用到:
以上代码可以看出,这里既可以展示图片,也可以展示视频。
- 展示图片的
include
文件是page_hero.html
。
它用到的参数有:
- page.header.overlay_image => overlay_img_path
- page.header.overlay_color
- page.header.overlay_filter => overlay_filter
- gradient
- rgba
- page.header.image_description => image_description
- page.header.show_overlay_excerpt
- page.header.cta_url
- page.header.actions
- label
- url
- page.header.image
- page.header.caption
展示视频的 include
文件是 _includes/page__hero-video.html
:
<!-- Courtesy of embedresponsively.com -->
这里进一步引用了 _include/video
这个模块:
<!-- Courtesy of embedresponsively.com -->
代码很简单,四个视频平台:
- vimeo
- youtube
- google-drive
- bilibili
继续分析 _layouts/splash.html
的剩余代码:
<div id="main" role="main">
<article class="splash" itemscope itemtype="https://schema.org/CreativeWork">
<meta itemprop="headline" content="Jekyll 主题 Minimal Mistakes 使用总结">
<meta itemprop="description" content="虽然我使用 Jekyll 已经有一段时间了,但一直没注意到 Minimal Mistakes 这个主题的存在。">
<meta itemprop="datePublished" content="2023-05-27T00:00:00+08:00">
<section class="page__content" itemprop="text">
<div id="main" role="main">
<div class="sidebar sticky">
<div itemscope itemtype="https://schema.org/Person" class="h-card">
<div class="author__avatar">
<a href="https://feelang.xyz/">
<img src="/assets/images/site-logo.png" alt="feelang" itemprop="image" class="u-photo">
</a>
</div>
<div class="author__content">
<h3 class="author__name p-name" itemprop="name">
<a class="u-url" rel="me" href="https://feelang.xyz/" itemprop="url">feelang</a>
</h3>
<div class="author__bio p-note" itemprop="description">
<p>一个喜欢做知识类产品的自由职业者</p>
</div>
</div>
<div class="author__urls-wrapper">
<button class="btn btn--inverse">关注</button>
<ul class="author__urls social-icons">
<li itemprop="homeLocation" itemscope itemtype="https://schema.org/Place">
<i class="fas fa-fw fa-map-marker-alt" aria-hidden="true"></i> <span itemprop="name" class="p-locality">杭州</span>
</li>
<li><a href="https://github.com/feelang" rel="nofollow noopener noreferrer me" itemprop="sameAs"><i class="fab fa-fw fa-github" aria-hidden="true"></i><span class="label">Github</span></a></li>
<li><a href="https://weibo.com/u/1670598115" rel="nofollow noopener noreferrer me" itemprop="sameAs"><i class="fab fa-fw fa-weibo" aria-hidden="true"></i><span class="label">微博</span></a></li>
<li><a href="https://blog.csdn.net/feelang" rel="nofollow noopener noreferrer me" itemprop="sameAs"><i class="fas fa-link" aria-hidden="true"></i><span class="label">CSDN</span></a></li>
<li><a href="https://juejin.cn/user/2805609405883607" rel="nofollow noopener noreferrer me" itemprop="sameAs"><i class="fas fa-link" aria-hidden="true"></i><span class="label">掘金</span></a></li>
<li>
<a href="mailto:feelangcpp@gmail.com" rel="me" class="u-email">
<meta itemprop="email" content="feelangcpp@gmail.com" />
<i class="fas fa-fw fa-envelope-square" aria-hidden="true"></i><span class="label">电子邮箱</span>
</a>
</li>
<!--
<li>
<a href="http://link-to-whatever-social-network.com/user/" itemprop="sameAs" rel="nofollow noopener noreferrer me">
<i class="fas fa-fw" aria-hidden="true"></i> Custom Social Profile Link
</a>
</li>
-->
</ul>
</div>
</div>
</div>
<article class="page" itemscope itemtype="https://schema.org/CreativeWork">
<meta itemprop="headline" content="Health 项目技术总结(Next.js + AntD)">
<meta itemprop="description" content="好长一段时间没写前端,很多知识点都忘掉了,为了防止遗忘,趁着项目刚结束,赶紧总结一下。">
<meta itemprop="datePublished" content="2023-05-18T00:00:00+08:00">
<div class="page__inner-wrap">
<header>
<h1 id="page-title" class="page__title" itemprop="headline">
<a href="https://feelang.xyz/programming/2023/05/18/summary-of-the-health-project.html" itemprop="url">Health 项目技术总结(Next.js + AntD)
</a>
</h1>
</header>
<section class="page__content" itemprop="text">
<aside class="sidebar__right sticky">
<nav class="toc">
<header><h4 class="nav__title"><i class="fas fa-file-alt"></i> 目录</h4></header>
<ul class="toc__menu"><li><a href="#邂逅-nextjs">邂逅 Next.js</a></li><li><a href="#老朋友-antd">老朋友 AntD</a><ul><li><a href="#使用-form">使用 Form</a></li><li><a href="#formitem-的联动校验">Form.Item 的联动校验</a></li></ul></li><li><a href="#typescript-tips">TypeScript tips</a><ul><li><a href="#string--number">string => number</a></li><li><a href="#interface">interface</a></li></ul></li></ul>
</nav>
</aside>
<p>好长一段时间没写前端,很多知识点都忘掉了,为了防止遗忘,趁着项目刚结束,赶紧总结一下。</p>
<h2 id="邂逅-nextjs">邂逅 <code class="language-plaintext highlighter-rouge">Next.js</code></h2>
<p>这个项目用的是 <code class="language-plaintext highlighter-rouge">next.js</code>,也是 <code class="language-plaintext highlighter-rouge">react</code> 官方推荐的。今天才知道原来 react 已经升级改版,我之前掌握的技能早已过时。</p>
<ul>
<li>React 官方推荐入口 => <a href="https://react.dev/learn/start-a-new-react-project">Start a New React Project</a></li>
<li>Next.js 官网入口 => <a href="https://nextjs.org/">Next.js</a></li>
</ul>
<p>第一次用 <code class="language-plaintext highlighter-rouge">next.js</code>,不了解技术细节,不过从使用感受来说,还不错,用起来很方便。</p>
<p>从官网简介可以看出,这是一个全栈框架:</p>
<blockquote>
<p>Next.js enables you to create full-stack Web applications by extending the latest React features, and integrating powerful Rust-based JavaScript tooling for the fastest builds.</p>
</blockquote>
<p>根据已经掌握的 <a href="https://ant.design/components">antd</a> 知识,很快就把第一个页面搞定了。</p>
<p>开始搞第二个页面时,我开始网上寻找 Router 解决方案,找到了 <a href="https://reactrouter.com/en/main">React Router</a>。</p>
<p>发现这个框架也已经升级改版,而我只掌握了上一个版本的用法,所以就开始翻文档,学习新用法,但是内容实在太长了,读不下去。</p>
<p>顺便发现了 <code class="language-plaintext highlighter-rouge">next.js</code> 自身内置了路由模块——<a href="https://nextjs.org/docs/pages/building-your-application/routing/api-routes">API Routes</a>。</p>
<p>继续翻文档,结果云里雾里,根本看不懂。</p>
<p>灵机一动,不如去看看视频教程,找到了 <a href="https://www.youtube.com/watch?v=gSSsZReIFRk">Next.js App Router: Routing, Data Fetching, Caching</a>。</p>
<!-- Courtesy of embedresponsively.com -->
<div class="responsive-video-container">
<iframe src="https://www.youtube-nocookie.com/embed/gSSsZReIFRk" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
</div>
<p>原来在 <code class="language-plaintext highlighter-rouge">next.js</code> 项目中,只需要在 <code class="language-plaintext highlighter-rouge">app</code> 目录下新建一个文件夹就可以自动生成路由,实在方便。</p>
<p>就这样新建了第二个页面。</p>
<p>然后开始使用 <code class="language-plaintext highlighter-rouge">antd</code> 的 <code class="language-plaintext highlighter-rouge">Form</code> 组件写页面。</p>
<h2 id="老朋友-antd">老朋友 AntD</h2>
<h3 id="使用-form">使用 <code class="language-plaintext highlighter-rouge">Form</code></h3>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">Form</span>
<span class="na">form</span><span class="p">=</span><span class="si">{</span><span class="nx">form</span><span class="si">}</span>
<span class="na">layout</span><span class="p">=</span><span class="s">"vertical"</span>
<span class="na">onFinish</span><span class="p">=</span><span class="si">{</span><span class="nx">onFinish</span><span class="si">}</span>
<span class="na">onFinishFailed</span><span class="p">=</span><span class="si">{</span><span class="nx">onFinishFailed</span><span class="si">}</span>
<span class="p">></span>
</code></pre></div></div>
<p>其中:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">form={form}</code> 的 <code class="language-plaintext highlighter-rouge">form</code> 来自:<code class="language-plaintext highlighter-rouge">const [form] = Form.useForm()</code></li>
<li><code class="language-plaintext highlighter-rouge">onFinish</code> 和 <code class="language-plaintext highlighter-rouge">onFinishFailed</code> 是两个函数</li>
</ul>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">const</span> <span class="nx">onFinish</span> <span class="o">=</span> <span class="p">(</span><span class="nx">values</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">values</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">onFinishFailed</span> <span class="o">=</span> <span class="p">(</span><span class="nx">errorInfo</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">errorInfo</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>页面底部添加两个按钮:计算 & 重置。</p>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span><span class="p">></span>
<span class="p"><</span><span class="nc">Space</span><span class="p">></span>
<span class="p"><</span><span class="nc">Button</span> <span class="na">type</span><span class="p">=</span><span class="s">"primary"</span> <span class="na">htmlType</span><span class="p">=</span><span class="s">"submit"</span><span class="p">></span>计算<span class="p"></</span><span class="nc">Button</span><span class="p">></span>
<span class="p"><</span><span class="nc">Button</span> <span class="na">htmlType</span><span class="p">=</span><span class="s">"reset"</span><span class="p">></span>重置<span class="p"></</span><span class="nc">Button</span><span class="p">></span>
<span class="p"></</span><span class="nc">Space</span><span class="p">></span>
<span class="p"></</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span><span class="p">></span>
</code></pre></div></div>
<p>其中 <code class="language-plaintext highlighter-rouge"><Space></code> 一用,就不用单独设置边距了,非常方便。</p>
<p>这两个按钮的功能通过设置 <code class="language-plaintext highlighter-rouge">htmlType</code> 属性来实现,其他任何代码不需要。</p>
<h3 id="formitem-的联动校验"><code class="language-plaintext highlighter-rouge">Form.Item</code> 的联动校验</h3>
<p>其中一个输入框要填写年龄,且是必填:</p>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span> <span class="na">label</span><span class="p">=</span><span class="s">"1 年龄(age)"</span> <span class="na">name</span><span class="p">=</span><span class="s">"age"</span>
<span class="na">rules</span><span class="p">=</span><span class="si">{</span><span class="p">[{</span> <span class="na">required</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">请输入年龄</span><span class="dl">'</span> <span class="p">}]</span><span class="si">}</span>
<span class="p">></span>
<span class="p"><</span><span class="nc">InputNumber</span> <span class="na">placeholder</span><span class="p">=</span><span class="s">"请输入年龄"</span> <span class="na">min</span><span class="p">=</span><span class="si">{</span><span class="mi">18</span><span class="si">}</span> <span class="na">max</span><span class="p">=</span><span class="si">{</span><span class="mi">100</span><span class="si">}</span>
<span class="na">onChange</span><span class="p">=</span><span class="si">{</span><span class="nx">setAge</span><span class="si">}</span>
<span class="p">/></span>
<span class="p"></</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span><span class="p">></span>
</code></pre></div></div>
<p>利用 <code class="language-plaintext highlighter-rouge">setAge</code> 接收输入值:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">[</span><span class="nx">age</span><span class="p">,</span> <span class="nx">setAge</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="o"><</span><span class="kr">string</span> <span class="o">|</span> <span class="kr">number</span> <span class="o">|</span> <span class="kc">null</span><span class="o">></span><span class="p">(</span><span class="kc">null</span><span class="p">)</span>
</code></pre></div></div>
<blockquote>
<p>需要留意的是,<code class="language-plaintext highlighter-rouge">InputNumber</code> 的 <code class="language-plaintext highlighter-rouge">onChange</code> 属性的变量类型为 <code class="language-plaintext highlighter-rouge">string | number | null</code>。</p>
</blockquote>
<p>第二个输入框也是年龄,但它的输入值必须小于 <code class="language-plaintext highlighter-rouge">age</code>:</p>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span> <span class="na">label</span><span class="p">=</span><span class="s">"3.1 绝经年龄(age at menopause)"</span>
<span class="na">name</span><span class="p">=</span><span class="s">"menopauseAge"</span>
<span class="na">dependencies</span><span class="p">=</span><span class="si">{</span><span class="p">[</span><span class="dl">'</span><span class="s1">age</span><span class="dl">'</span><span class="p">]</span><span class="si">}</span>
<span class="na">rules</span><span class="p">=</span><span class="si">{</span><span class="p">[</span>
<span class="p">{</span>
<span class="na">required</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">请输入年龄</span><span class="dl">'</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">({</span> <span class="nx">getFieldValue</span> <span class="p">})</span> <span class="o">=></span> <span class="p">({</span>
<span class="nx">validator</span><span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">value</span> <span class="o">||</span> <span class="nx">getFieldValue</span><span class="p">(</span><span class="dl">'</span><span class="s1">age</span><span class="dl">'</span><span class="p">)</span> <span class="o">>=</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">reject</span><span class="p">(</span><span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">`绝经年龄不得大于当前年龄(</span><span class="p">${</span><span class="nx">age</span><span class="p">}</span><span class="s2">)`</span><span class="p">));</span>
<span class="p">},</span>
<span class="p">}),</span>
<span class="p">]</span><span class="si">}</span>
<span class="p">></span>
</code></pre></div></div>
<p>这个用法值得仔细记录一下。</p>
<p>先看 <code class="language-plaintext highlighter-rouge">dependencies</code> 的用法,官网介绍如下,简单明了。</p>
<blockquote>
<p>当字段间存在依赖关系时使用。如果一个字段设置了 dependencies 属性。那么它所依赖的字段更新时,该字段将自动触发更新与校验。
一种常见的场景,就是注册用户表单的“密码”与“确认密码”字段。“确认密码”校验依赖于“密码”字段,设置 dependencies 后,“密码”字段更新会重新触发“校验密码”的校验逻辑。</p>
</blockquote>
<p>再看 <code class="language-plaintext highlighter-rouge">rules</code> 用法:</p>
<blockquote>
<p>校验规则,设置字段的校验逻辑。</p>
</blockquote>
<p>类型为 <code class="language-plaintext highlighter-rouge">Rule[]</code>,是个数组。</p>
<p><code class="language-plaintext highlighter-rouge">Rule</code> 定义如下:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">type</span> <span class="nx">Rule</span> <span class="o">=</span> <span class="nx">RuleConfig</span> <span class="o">|</span> <span class="p">((</span><span class="nx">form</span><span class="p">:</span> <span class="nx">FormInstance</span><span class="p">)</span> <span class="o">=></span> <span class="nx">RuleConfig</span><span class="p">);</span>
</code></pre></div></div>
<p>可以看出,<code class="language-plaintext highlighter-rouge">Rule</code> 支持两种类型,所以上面代码中 <code class="language-plaintext highlighter-rouge">rules</code> 的第一个元素类型为 <code class="language-plaintext highlighter-rouge">RuleConfig</code>:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="nl">required</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nx">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">请输入年龄</span><span class="dl">'</span><span class="p">,</span>
<span class="p">},</span>
</code></pre></div></div>
<p>第二个元素类型为 <code class="language-plaintext highlighter-rouge">((form: FormInstance) => RuleConfig)</code>,是个高阶函数。</p>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">({</span> <span class="nx">getFieldValue</span> <span class="p">})</span> <span class="o">=></span> <span class="p">({</span>
<span class="nx">validator</span><span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">value</span> <span class="o">||</span> <span class="nx">getFieldValue</span><span class="p">(</span><span class="dl">'</span><span class="s1">age</span><span class="dl">'</span><span class="p">)</span> <span class="o">>=</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">reject</span><span class="p">(</span><span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">`绝经年龄不得大于当前年龄(</span><span class="p">${</span><span class="nx">age</span><span class="p">}</span><span class="s2">)`</span><span class="p">));</span>
<span class="p">},</span>
<span class="p">}),</span>
</code></pre></div></div>
<p>由此可以推测出 <code class="language-plaintext highlighter-rouge">getFieldValue</code> 是 <code class="language-plaintext highlighter-rouge">FormInstance</code> 的一个属性,通过看源码可以证实:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kr">interface</span> <span class="nx">FormInstance</span><span class="o"><</span><span class="nx">Values</span> <span class="o">=</span> <span class="kr">any</span><span class="o">></span> <span class="p">{</span>
<span class="na">getFieldValue</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="nx">StoreValue</span><span class="p">;</span>
<span class="nl">getFieldsValue</span><span class="p">:</span> <span class="p">(()</span> <span class="o">=></span> <span class="nx">Values</span><span class="p">)</span> <span class="o">&</span> <span class="p">((</span><span class="na">nameList</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">[]</span> <span class="o">|</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">filterFunc</span><span class="p">?:</span> <span class="p">(</span><span class="na">meta</span><span class="p">:</span> <span class="nx">Meta</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">)</span> <span class="o">=></span> <span class="kr">any</span><span class="p">);</span>
<span class="nl">getFieldError</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="kr">string</span><span class="p">[];</span>
<span class="nl">getFieldsError</span><span class="p">:</span> <span class="p">(</span><span class="nx">nameList</span><span class="p">?:</span> <span class="nx">NamePath</span><span class="p">[])</span> <span class="o">=></span> <span class="nx">FieldError</span><span class="p">[];</span>
<span class="nl">getFieldWarning</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="kr">string</span><span class="p">[];</span>
<span class="nl">isFieldsTouched</span><span class="p">:</span> <span class="p">((</span><span class="nx">nameList</span><span class="p">?:</span> <span class="nx">NamePath</span><span class="p">[],</span> <span class="nx">allFieldsTouched</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">)</span> <span class="o">&</span> <span class="p">((</span><span class="nx">allFieldsTouched</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">);</span>
<span class="nl">isFieldTouched</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">;</span>
<span class="nl">isFieldValidating</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">;</span>
<span class="nl">isFieldsValidating</span><span class="p">:</span> <span class="p">(</span><span class="na">nameList</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">[])</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">;</span>
<span class="nl">resetFields</span><span class="p">:</span> <span class="p">(</span><span class="nx">fields</span><span class="p">?:</span> <span class="nx">NamePath</span><span class="p">[])</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="nl">setFields</span><span class="p">:</span> <span class="p">(</span><span class="na">fields</span><span class="p">:</span> <span class="nx">FieldData</span><span class="p">[])</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="nl">setFieldValue</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="nl">setFieldsValue</span><span class="p">:</span> <span class="p">(</span><span class="na">values</span><span class="p">:</span> <span class="nx">RecursivePartial</span><span class="o"><</span><span class="nx">Values</span><span class="o">></span><span class="p">)</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="nl">validateFields</span><span class="p">:</span> <span class="nx">ValidateFields</span><span class="o"><</span><span class="nx">Values</span><span class="o">></span><span class="p">;</span>
<span class="nl">submit</span><span class="p">:</span> <span class="p">()</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">validator</code> 也是一个高阶函数:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">type</span> <span class="nx">Validator</span> <span class="o">=</span> <span class="p">(</span><span class="nx">rule</span><span class="p">:</span> <span class="nx">RuleObject</span><span class="p">,</span> <span class="nx">value</span><span class="p">:</span> <span class="nx">StoreValue</span><span class="p">,</span> <span class="nx">callback</span><span class="p">:</span> <span class="p">(</span><span class="nx">error</span><span class="p">?:</span> <span class="kr">string</span><span class="p">)</span> <span class="o">=></span> <span class="k">void</span><span class="p">)</span> <span class="o">=></span> <span class="nb">Promise</span><span class="o"><</span><span class="k">void</span> <span class="o">|</span> <span class="kr">any</span><span class="o">></span> <span class="o">|</span> <span class="k">void</span><span class="p">;</span>
</code></pre></div></div>
<p>返回值是一个 <code class="language-plaintext highlighter-rouge">Promise</code> 或 <code class="language-plaintext highlighter-rouge">void</code>。</p>
<p>通过以上分析,可以总结出:当两个 <code class="language-plaintext highlighter-rouge">Form.Item</code> 之间存在依赖时,可通过 <code class="language-plaintext highlighter-rouge">dependencies</code> 和 <code class="language-plaintext highlighter-rouge">rules</code> 属性设定依赖逻辑。</p>
<h2 id="typescript-tips">TypeScript tips</h2>
<h3 id="string--number">string => number</h3>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// menopauseAge 是个 `string`</span>
<span class="kd">const</span> <span class="nx">menopauseAge</span> <span class="o">=</span> <span class="nx">values</span><span class="p">.</span><span class="nx">menopauseAge</span> <span class="p">?</span> <span class="o">+</span><span class="nx">values</span><span class="p">.</span><span class="nx">menopauseAge</span> <span class="p">:</span> <span class="mi">0</span>
</code></pre></div></div>
<h3 id="interface">interface</h3>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">interface</span> <span class="nx">RiskResult</span> <span class="p">{</span>
<span class="nl">message</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
<span class="nx">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
<span class="kd">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">success</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">warning</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">error</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">info</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>用法:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">results</span><span class="p">:</span> <span class="nx">RiskResult</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span>
<span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">乳腺癌低风险</span><span class="dl">'</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">请继续保持健康生活方式</span><span class="dl">'</span><span class="p">,</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">success</span><span class="dl">'</span>
<span class="p">},</span>
<span class="p">]</span>
</code></pre></div></div>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">[</span><span class="nx">result</span><span class="p">,</span> <span class="nx">setResult</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="o"><</span><span class="nx">RiskResult</span><span class="o">></span><span class="p">()</span>
</code></pre></div></div>
</section>
<footer class="page__meta">
<p class="page__date"><strong><i class="fas fa-fw fa-calendar-alt" aria-hidden="true"></i> 更新时间:</strong> <time class="dt-published" datetime="2023-05-18T00:00:00+08:00">May 18, 2023</time></p>
</footer>
<nav class="pagination">
<a href="/programming/2022/10/14/difference-between-gulp-and-webpack.html" class="pagination--pager" title="Gulp 和 Webpack 有什么区别?
">上一页</a>
<a href="/tools/2023/05/27/jekyll-theme-minimal-mistakes.html" class="pagination--pager" title="Jekyll 主题 Minimal Mistakes 使用总结
">下一页</a>
</nav>
</div>
<div class="page__comments">
<h4 class="page__comments-title">留下评论</h4>
<section id="disqus_thread"></section>
</div>
</article>
</div>
</section>
</article>
</div>
这部分代码只是给 article
元素中添加了四个 meta
:
Page Variable | Item Property |
---|---|
page.title | headline |
page.excerpt | description |
page.date | datePublished |
page.last_modified_at | dateModified |
对页面视觉没有作用,应该只是为了 SEO。
default
_layouts/default.html
是所有布局文件的基类,它定了 html 文件的基本框架。
通过源码可以看出,<html>
标签中有 lang
的属性:
<html lang="zh" class="no-js">
然后是 <head>
标签:
<head>
<meta charset="utf-8">
<!-- begin _includes/seo.html --><title>Jekyll 主题 Minimal Mistakes 使用总结 - feelang的技术博客</title>
<meta name="description" content="虽然我使用 Jekyll 已经有一段时间了,但一直没注意到 Minimal Mistakes 这个主题的存在。">
<meta name="author" content="feelang">
<meta property="article:author" content="feelang">
<meta property="og:type" content="article">
<meta property="og:locale" content="zh_CN">
<meta property="og:site_name" content="feelang的技术博客">
<meta property="og:title" content="Jekyll 主题 Minimal Mistakes 使用总结">
<meta property="og:url" content="https://feelang.xyz/tools/2023/05/27/jekyll-theme-minimal-mistakes.html">
<meta property="og:description" content="虽然我使用 Jekyll 已经有一段时间了,但一直没注意到 Minimal Mistakes 这个主题的存在。">
<meta property="og:image" content="https://feelang.xyz/assets/images/site-logo.png">
<meta property="article:published_time" content="2023-05-27T00:00:00+08:00">
<link rel="canonical" href="https://feelang.xyz/tools/2023/05/27/jekyll-theme-minimal-mistakes.html">
<!-- end _includes/seo.html -->
<link href="/feed.xml" type="application/atom+xml" rel="alternate" title="feelang的技术博客 Feed">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript">
document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/g, '') + ' js ';
</script>
<!-- For all browsers -->
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="preload" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@latest/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@latest/css/all.min.css"></noscript>
<!-- start custom head snippets -->
<!-- insert favicons. use https://realfavicongenerator.net/ -->
<!-- end custom head snippets -->
</head>
head.html
中做了以下几件事:
- seo
- atom feed
- viewport
- stylesheet =>
/assets/css/main.css
- fontawesome
还支持自定义 js:
head/custom.html
是留给我们自定义用的。
继续看 <body>
标签:
<nav class="skip-links">
<ul>
<li><a href="#site-nav" class="screen-reader-shortcut">转到主导航栏</a></li>
<li><a href="#main" class="screen-reader-shortcut">转到内容</a></li>
<li><a href="#footer" class="screen-reader-shortcut">转到底部</a></li>
</ul>
</nav>
<div class="masthead">
<div class="masthead__inner-wrap">
<div class="masthead__menu">
<nav id="site-nav" class="greedy-nav">
<a class="site-title" href="/">
feelang的技术博客
<span class="site-subtitle">分享全栈开发知识</span>
</a>
<ul class="visible-links"><li class="masthead__menu-item">
<a
href="/weapps/"
>小程序</a>
</li><li class="masthead__menu-item">
<a
href="/apps/"
>APP</a>
</li><li class="masthead__menu-item">
<a
href="/web/"
>Web</a>
</li><li class="masthead__menu-item">
<a
href="/tutorials/"
>教程</a>
</li><li class="masthead__menu-item">
<a
href="/about/"
>关于</a>
</li></ul>
<button class="search__toggle" type="button">
<span class="visually-hidden">切换搜索</span>
<i class="fas fa-search"></i>
</button>
<button class="greedy-nav__toggle hidden" type="button">
<span class="visually-hidden">切换菜单</span>
<div class="navicon"></div>
</button>
<ul class="hidden-links hidden"></ul>
</nav>
</div>
</div>
</div>
先看 _includes/skip-links.html
:
<nav class="skip-links">
<ul>
<li><a href="#site-nav" class="screen-reader-shortcut">转到主导航栏</a></li>
<li><a href="#main" class="screen-reader-shortcut">转到内容</a></li>
<li><a href="#footer" class="screen-reader-shortcut">转到底部</a></li>
</ul>
</nav>
页面内快速跳转用的。
再来看 _includes/masthead.html
,页面吊顶:
获取 _config.yml
中配置的 site.logo
,并将其赋值给 logo_path
变量。
用一个 <a>
标签做展示:
标题支持 title
和 subtitle
:
<a class="site-title" href="/">
feelang的技术博客
<span class="site-subtitle">分享全栈开发知识</span>
</a>
然后展示导航条:
<ul class="visible-links"><li class="masthead__menu-item">
<a href="/weapps/">小程序</a>
</li><li class="masthead__menu-item">
<a href="/apps/">APP</a>
</li><li class="masthead__menu-item">
<a href="/web/">Web</a>
</li><li class="masthead__menu-item">
<a href="/tutorials/">教程</a>
</li><li class="masthead__menu-item">
<a href="/about/">关于</a>
</li></ul>
接下来是搜索按钮:
<button class="search__toggle" type="button">
<span class="visually-hidden">切换搜索</span>
<i class="fas fa-search"></i>
</button>
最后是自适应布局中的 Toggle menu
:
<button class="greedy-nav__toggle hidden" type="button">
<span class="visually-hidden">切换菜单</span>
<div class="navicon"></div>
</button>
以上便是 _includes/masthead.html
的源码,回到 _layouts/default.html
继续往下看:
子类布局占位:
<div class="initial-content">
<div id="main" role="main">
<div class="sidebar sticky">
<div itemscope itemtype="https://schema.org/Person" class="h-card">
<div class="author__avatar">
<a href="https://feelang.xyz/">
<img src="/assets/images/site-logo.png" alt="feelang" itemprop="image" class="u-photo">
</a>
</div>
<div class="author__content">
<h3 class="author__name p-name" itemprop="name">
<a class="u-url" rel="me" href="https://feelang.xyz/" itemprop="url">feelang</a>
</h3>
<div class="author__bio p-note" itemprop="description">
<p>一个喜欢做知识类产品的自由职业者</p>
</div>
</div>
<div class="author__urls-wrapper">
<button class="btn btn--inverse">关注</button>
<ul class="author__urls social-icons">
<li itemprop="homeLocation" itemscope itemtype="https://schema.org/Place">
<i class="fas fa-fw fa-map-marker-alt" aria-hidden="true"></i> <span itemprop="name" class="p-locality">杭州</span>
</li>
<li><a href="https://github.com/feelang" rel="nofollow noopener noreferrer me" itemprop="sameAs"><i class="fab fa-fw fa-github" aria-hidden="true"></i><span class="label">Github</span></a></li>
<li><a href="https://weibo.com/u/1670598115" rel="nofollow noopener noreferrer me" itemprop="sameAs"><i class="fab fa-fw fa-weibo" aria-hidden="true"></i><span class="label">微博</span></a></li>
<li><a href="https://blog.csdn.net/feelang" rel="nofollow noopener noreferrer me" itemprop="sameAs"><i class="fas fa-link" aria-hidden="true"></i><span class="label">CSDN</span></a></li>
<li><a href="https://juejin.cn/user/2805609405883607" rel="nofollow noopener noreferrer me" itemprop="sameAs"><i class="fas fa-link" aria-hidden="true"></i><span class="label">掘金</span></a></li>
<li>
<a href="mailto:feelangcpp@gmail.com" rel="me" class="u-email">
<meta itemprop="email" content="feelangcpp@gmail.com" />
<i class="fas fa-fw fa-envelope-square" aria-hidden="true"></i><span class="label">电子邮箱</span>
</a>
</li>
<!--
<li>
<a href="http://link-to-whatever-social-network.com/user/" itemprop="sameAs" rel="nofollow noopener noreferrer me">
<i class="fas fa-fw" aria-hidden="true"></i> Custom Social Profile Link
</a>
</li>
-->
</ul>
</div>
</div>
</div>
<article class="page" itemscope itemtype="https://schema.org/CreativeWork">
<meta itemprop="headline" content="Health 项目技术总结(Next.js + AntD)">
<meta itemprop="description" content="好长一段时间没写前端,很多知识点都忘掉了,为了防止遗忘,趁着项目刚结束,赶紧总结一下。">
<meta itemprop="datePublished" content="2023-05-18T00:00:00+08:00">
<div class="page__inner-wrap">
<header>
<h1 id="page-title" class="page__title" itemprop="headline">
<a href="https://feelang.xyz/programming/2023/05/18/summary-of-the-health-project.html" itemprop="url">Health 项目技术总结(Next.js + AntD)
</a>
</h1>
</header>
<section class="page__content" itemprop="text">
<aside class="sidebar__right sticky">
<nav class="toc">
<header><h4 class="nav__title"><i class="fas fa-file-alt"></i> 目录</h4></header>
<ul class="toc__menu"><li><a href="#邂逅-nextjs">邂逅 Next.js</a></li><li><a href="#老朋友-antd">老朋友 AntD</a><ul><li><a href="#使用-form">使用 Form</a></li><li><a href="#formitem-的联动校验">Form.Item 的联动校验</a></li></ul></li><li><a href="#typescript-tips">TypeScript tips</a><ul><li><a href="#string--number">string => number</a></li><li><a href="#interface">interface</a></li></ul></li></ul>
</nav>
</aside>
<p>好长一段时间没写前端,很多知识点都忘掉了,为了防止遗忘,趁着项目刚结束,赶紧总结一下。</p>
<h2 id="邂逅-nextjs">邂逅 <code class="language-plaintext highlighter-rouge">Next.js</code></h2>
<p>这个项目用的是 <code class="language-plaintext highlighter-rouge">next.js</code>,也是 <code class="language-plaintext highlighter-rouge">react</code> 官方推荐的。今天才知道原来 react 已经升级改版,我之前掌握的技能早已过时。</p>
<ul>
<li>React 官方推荐入口 => <a href="https://react.dev/learn/start-a-new-react-project">Start a New React Project</a></li>
<li>Next.js 官网入口 => <a href="https://nextjs.org/">Next.js</a></li>
</ul>
<p>第一次用 <code class="language-plaintext highlighter-rouge">next.js</code>,不了解技术细节,不过从使用感受来说,还不错,用起来很方便。</p>
<p>从官网简介可以看出,这是一个全栈框架:</p>
<blockquote>
<p>Next.js enables you to create full-stack Web applications by extending the latest React features, and integrating powerful Rust-based JavaScript tooling for the fastest builds.</p>
</blockquote>
<p>根据已经掌握的 <a href="https://ant.design/components">antd</a> 知识,很快就把第一个页面搞定了。</p>
<p>开始搞第二个页面时,我开始网上寻找 Router 解决方案,找到了 <a href="https://reactrouter.com/en/main">React Router</a>。</p>
<p>发现这个框架也已经升级改版,而我只掌握了上一个版本的用法,所以就开始翻文档,学习新用法,但是内容实在太长了,读不下去。</p>
<p>顺便发现了 <code class="language-plaintext highlighter-rouge">next.js</code> 自身内置了路由模块——<a href="https://nextjs.org/docs/pages/building-your-application/routing/api-routes">API Routes</a>。</p>
<p>继续翻文档,结果云里雾里,根本看不懂。</p>
<p>灵机一动,不如去看看视频教程,找到了 <a href="https://www.youtube.com/watch?v=gSSsZReIFRk">Next.js App Router: Routing, Data Fetching, Caching</a>。</p>
<!-- Courtesy of embedresponsively.com -->
<div class="responsive-video-container">
<iframe src="https://www.youtube-nocookie.com/embed/gSSsZReIFRk" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
</div>
<p>原来在 <code class="language-plaintext highlighter-rouge">next.js</code> 项目中,只需要在 <code class="language-plaintext highlighter-rouge">app</code> 目录下新建一个文件夹就可以自动生成路由,实在方便。</p>
<p>就这样新建了第二个页面。</p>
<p>然后开始使用 <code class="language-plaintext highlighter-rouge">antd</code> 的 <code class="language-plaintext highlighter-rouge">Form</code> 组件写页面。</p>
<h2 id="老朋友-antd">老朋友 AntD</h2>
<h3 id="使用-form">使用 <code class="language-plaintext highlighter-rouge">Form</code></h3>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">Form</span>
<span class="na">form</span><span class="p">=</span><span class="si">{</span><span class="nx">form</span><span class="si">}</span>
<span class="na">layout</span><span class="p">=</span><span class="s">"vertical"</span>
<span class="na">onFinish</span><span class="p">=</span><span class="si">{</span><span class="nx">onFinish</span><span class="si">}</span>
<span class="na">onFinishFailed</span><span class="p">=</span><span class="si">{</span><span class="nx">onFinishFailed</span><span class="si">}</span>
<span class="p">></span>
</code></pre></div></div>
<p>其中:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">form={form}</code> 的 <code class="language-plaintext highlighter-rouge">form</code> 来自:<code class="language-plaintext highlighter-rouge">const [form] = Form.useForm()</code></li>
<li><code class="language-plaintext highlighter-rouge">onFinish</code> 和 <code class="language-plaintext highlighter-rouge">onFinishFailed</code> 是两个函数</li>
</ul>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">const</span> <span class="nx">onFinish</span> <span class="o">=</span> <span class="p">(</span><span class="nx">values</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">values</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">onFinishFailed</span> <span class="o">=</span> <span class="p">(</span><span class="nx">errorInfo</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">errorInfo</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>页面底部添加两个按钮:计算 & 重置。</p>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span><span class="p">></span>
<span class="p"><</span><span class="nc">Space</span><span class="p">></span>
<span class="p"><</span><span class="nc">Button</span> <span class="na">type</span><span class="p">=</span><span class="s">"primary"</span> <span class="na">htmlType</span><span class="p">=</span><span class="s">"submit"</span><span class="p">></span>计算<span class="p"></</span><span class="nc">Button</span><span class="p">></span>
<span class="p"><</span><span class="nc">Button</span> <span class="na">htmlType</span><span class="p">=</span><span class="s">"reset"</span><span class="p">></span>重置<span class="p"></</span><span class="nc">Button</span><span class="p">></span>
<span class="p"></</span><span class="nc">Space</span><span class="p">></span>
<span class="p"></</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span><span class="p">></span>
</code></pre></div></div>
<p>其中 <code class="language-plaintext highlighter-rouge"><Space></code> 一用,就不用单独设置边距了,非常方便。</p>
<p>这两个按钮的功能通过设置 <code class="language-plaintext highlighter-rouge">htmlType</code> 属性来实现,其他任何代码不需要。</p>
<h3 id="formitem-的联动校验"><code class="language-plaintext highlighter-rouge">Form.Item</code> 的联动校验</h3>
<p>其中一个输入框要填写年龄,且是必填:</p>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span> <span class="na">label</span><span class="p">=</span><span class="s">"1 年龄(age)"</span> <span class="na">name</span><span class="p">=</span><span class="s">"age"</span>
<span class="na">rules</span><span class="p">=</span><span class="si">{</span><span class="p">[{</span> <span class="na">required</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">请输入年龄</span><span class="dl">'</span> <span class="p">}]</span><span class="si">}</span>
<span class="p">></span>
<span class="p"><</span><span class="nc">InputNumber</span> <span class="na">placeholder</span><span class="p">=</span><span class="s">"请输入年龄"</span> <span class="na">min</span><span class="p">=</span><span class="si">{</span><span class="mi">18</span><span class="si">}</span> <span class="na">max</span><span class="p">=</span><span class="si">{</span><span class="mi">100</span><span class="si">}</span>
<span class="na">onChange</span><span class="p">=</span><span class="si">{</span><span class="nx">setAge</span><span class="si">}</span>
<span class="p">/></span>
<span class="p"></</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span><span class="p">></span>
</code></pre></div></div>
<p>利用 <code class="language-plaintext highlighter-rouge">setAge</code> 接收输入值:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">[</span><span class="nx">age</span><span class="p">,</span> <span class="nx">setAge</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="o"><</span><span class="kr">string</span> <span class="o">|</span> <span class="kr">number</span> <span class="o">|</span> <span class="kc">null</span><span class="o">></span><span class="p">(</span><span class="kc">null</span><span class="p">)</span>
</code></pre></div></div>
<blockquote>
<p>需要留意的是,<code class="language-plaintext highlighter-rouge">InputNumber</code> 的 <code class="language-plaintext highlighter-rouge">onChange</code> 属性的变量类型为 <code class="language-plaintext highlighter-rouge">string | number | null</code>。</p>
</blockquote>
<p>第二个输入框也是年龄,但它的输入值必须小于 <code class="language-plaintext highlighter-rouge">age</code>:</p>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">Form</span><span class="p">.</span><span class="nc">Item</span> <span class="na">label</span><span class="p">=</span><span class="s">"3.1 绝经年龄(age at menopause)"</span>
<span class="na">name</span><span class="p">=</span><span class="s">"menopauseAge"</span>
<span class="na">dependencies</span><span class="p">=</span><span class="si">{</span><span class="p">[</span><span class="dl">'</span><span class="s1">age</span><span class="dl">'</span><span class="p">]</span><span class="si">}</span>
<span class="na">rules</span><span class="p">=</span><span class="si">{</span><span class="p">[</span>
<span class="p">{</span>
<span class="na">required</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">请输入年龄</span><span class="dl">'</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">({</span> <span class="nx">getFieldValue</span> <span class="p">})</span> <span class="o">=></span> <span class="p">({</span>
<span class="nx">validator</span><span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">value</span> <span class="o">||</span> <span class="nx">getFieldValue</span><span class="p">(</span><span class="dl">'</span><span class="s1">age</span><span class="dl">'</span><span class="p">)</span> <span class="o">>=</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">reject</span><span class="p">(</span><span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">`绝经年龄不得大于当前年龄(</span><span class="p">${</span><span class="nx">age</span><span class="p">}</span><span class="s2">)`</span><span class="p">));</span>
<span class="p">},</span>
<span class="p">}),</span>
<span class="p">]</span><span class="si">}</span>
<span class="p">></span>
</code></pre></div></div>
<p>这个用法值得仔细记录一下。</p>
<p>先看 <code class="language-plaintext highlighter-rouge">dependencies</code> 的用法,官网介绍如下,简单明了。</p>
<blockquote>
<p>当字段间存在依赖关系时使用。如果一个字段设置了 dependencies 属性。那么它所依赖的字段更新时,该字段将自动触发更新与校验。
一种常见的场景,就是注册用户表单的“密码”与“确认密码”字段。“确认密码”校验依赖于“密码”字段,设置 dependencies 后,“密码”字段更新会重新触发“校验密码”的校验逻辑。</p>
</blockquote>
<p>再看 <code class="language-plaintext highlighter-rouge">rules</code> 用法:</p>
<blockquote>
<p>校验规则,设置字段的校验逻辑。</p>
</blockquote>
<p>类型为 <code class="language-plaintext highlighter-rouge">Rule[]</code>,是个数组。</p>
<p><code class="language-plaintext highlighter-rouge">Rule</code> 定义如下:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">type</span> <span class="nx">Rule</span> <span class="o">=</span> <span class="nx">RuleConfig</span> <span class="o">|</span> <span class="p">((</span><span class="nx">form</span><span class="p">:</span> <span class="nx">FormInstance</span><span class="p">)</span> <span class="o">=></span> <span class="nx">RuleConfig</span><span class="p">);</span>
</code></pre></div></div>
<p>可以看出,<code class="language-plaintext highlighter-rouge">Rule</code> 支持两种类型,所以上面代码中 <code class="language-plaintext highlighter-rouge">rules</code> 的第一个元素类型为 <code class="language-plaintext highlighter-rouge">RuleConfig</code>:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="nl">required</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nx">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">请输入年龄</span><span class="dl">'</span><span class="p">,</span>
<span class="p">},</span>
</code></pre></div></div>
<p>第二个元素类型为 <code class="language-plaintext highlighter-rouge">((form: FormInstance) => RuleConfig)</code>,是个高阶函数。</p>
<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">({</span> <span class="nx">getFieldValue</span> <span class="p">})</span> <span class="o">=></span> <span class="p">({</span>
<span class="nx">validator</span><span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">value</span> <span class="o">||</span> <span class="nx">getFieldValue</span><span class="p">(</span><span class="dl">'</span><span class="s1">age</span><span class="dl">'</span><span class="p">)</span> <span class="o">>=</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">reject</span><span class="p">(</span><span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">`绝经年龄不得大于当前年龄(</span><span class="p">${</span><span class="nx">age</span><span class="p">}</span><span class="s2">)`</span><span class="p">));</span>
<span class="p">},</span>
<span class="p">}),</span>
</code></pre></div></div>
<p>由此可以推测出 <code class="language-plaintext highlighter-rouge">getFieldValue</code> 是 <code class="language-plaintext highlighter-rouge">FormInstance</code> 的一个属性,通过看源码可以证实:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kr">interface</span> <span class="nx">FormInstance</span><span class="o"><</span><span class="nx">Values</span> <span class="o">=</span> <span class="kr">any</span><span class="o">></span> <span class="p">{</span>
<span class="na">getFieldValue</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="nx">StoreValue</span><span class="p">;</span>
<span class="nl">getFieldsValue</span><span class="p">:</span> <span class="p">(()</span> <span class="o">=></span> <span class="nx">Values</span><span class="p">)</span> <span class="o">&</span> <span class="p">((</span><span class="na">nameList</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">[]</span> <span class="o">|</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">filterFunc</span><span class="p">?:</span> <span class="p">(</span><span class="na">meta</span><span class="p">:</span> <span class="nx">Meta</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">)</span> <span class="o">=></span> <span class="kr">any</span><span class="p">);</span>
<span class="nl">getFieldError</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="kr">string</span><span class="p">[];</span>
<span class="nl">getFieldsError</span><span class="p">:</span> <span class="p">(</span><span class="nx">nameList</span><span class="p">?:</span> <span class="nx">NamePath</span><span class="p">[])</span> <span class="o">=></span> <span class="nx">FieldError</span><span class="p">[];</span>
<span class="nl">getFieldWarning</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="kr">string</span><span class="p">[];</span>
<span class="nl">isFieldsTouched</span><span class="p">:</span> <span class="p">((</span><span class="nx">nameList</span><span class="p">?:</span> <span class="nx">NamePath</span><span class="p">[],</span> <span class="nx">allFieldsTouched</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">)</span> <span class="o">&</span> <span class="p">((</span><span class="nx">allFieldsTouched</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">);</span>
<span class="nl">isFieldTouched</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">;</span>
<span class="nl">isFieldValidating</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">)</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">;</span>
<span class="nl">isFieldsValidating</span><span class="p">:</span> <span class="p">(</span><span class="na">nameList</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">[])</span> <span class="o">=></span> <span class="nx">boolean</span><span class="p">;</span>
<span class="nl">resetFields</span><span class="p">:</span> <span class="p">(</span><span class="nx">fields</span><span class="p">?:</span> <span class="nx">NamePath</span><span class="p">[])</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="nl">setFields</span><span class="p">:</span> <span class="p">(</span><span class="na">fields</span><span class="p">:</span> <span class="nx">FieldData</span><span class="p">[])</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="nl">setFieldValue</span><span class="p">:</span> <span class="p">(</span><span class="na">name</span><span class="p">:</span> <span class="nx">NamePath</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="nl">setFieldsValue</span><span class="p">:</span> <span class="p">(</span><span class="na">values</span><span class="p">:</span> <span class="nx">RecursivePartial</span><span class="o"><</span><span class="nx">Values</span><span class="o">></span><span class="p">)</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="nl">validateFields</span><span class="p">:</span> <span class="nx">ValidateFields</span><span class="o"><</span><span class="nx">Values</span><span class="o">></span><span class="p">;</span>
<span class="nl">submit</span><span class="p">:</span> <span class="p">()</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">validator</code> 也是一个高阶函数:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">type</span> <span class="nx">Validator</span> <span class="o">=</span> <span class="p">(</span><span class="nx">rule</span><span class="p">:</span> <span class="nx">RuleObject</span><span class="p">,</span> <span class="nx">value</span><span class="p">:</span> <span class="nx">StoreValue</span><span class="p">,</span> <span class="nx">callback</span><span class="p">:</span> <span class="p">(</span><span class="nx">error</span><span class="p">?:</span> <span class="kr">string</span><span class="p">)</span> <span class="o">=></span> <span class="k">void</span><span class="p">)</span> <span class="o">=></span> <span class="nb">Promise</span><span class="o"><</span><span class="k">void</span> <span class="o">|</span> <span class="kr">any</span><span class="o">></span> <span class="o">|</span> <span class="k">void</span><span class="p">;</span>
</code></pre></div></div>
<p>返回值是一个 <code class="language-plaintext highlighter-rouge">Promise</code> 或 <code class="language-plaintext highlighter-rouge">void</code>。</p>
<p>通过以上分析,可以总结出:当两个 <code class="language-plaintext highlighter-rouge">Form.Item</code> 之间存在依赖时,可通过 <code class="language-plaintext highlighter-rouge">dependencies</code> 和 <code class="language-plaintext highlighter-rouge">rules</code> 属性设定依赖逻辑。</p>
<h2 id="typescript-tips">TypeScript tips</h2>
<h3 id="string--number">string => number</h3>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// menopauseAge 是个 `string`</span>
<span class="kd">const</span> <span class="nx">menopauseAge</span> <span class="o">=</span> <span class="nx">values</span><span class="p">.</span><span class="nx">menopauseAge</span> <span class="p">?</span> <span class="o">+</span><span class="nx">values</span><span class="p">.</span><span class="nx">menopauseAge</span> <span class="p">:</span> <span class="mi">0</span>
</code></pre></div></div>
<h3 id="interface">interface</h3>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">interface</span> <span class="nx">RiskResult</span> <span class="p">{</span>
<span class="nl">message</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
<span class="nx">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
<span class="kd">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">success</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">warning</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">error</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">info</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>用法:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">results</span><span class="p">:</span> <span class="nx">RiskResult</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span>
<span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">乳腺癌低风险</span><span class="dl">'</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">请继续保持健康生活方式</span><span class="dl">'</span><span class="p">,</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">success</span><span class="dl">'</span>
<span class="p">},</span>
<span class="p">]</span>
</code></pre></div></div>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">[</span><span class="nx">result</span><span class="p">,</span> <span class="nx">setResult</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="o"><</span><span class="nx">RiskResult</span><span class="o">></span><span class="p">()</span>
</code></pre></div></div>
</section>
<footer class="page__meta">
<p class="page__date"><strong><i class="fas fa-fw fa-calendar-alt" aria-hidden="true"></i> 更新时间:</strong> <time class="dt-published" datetime="2023-05-18T00:00:00+08:00">May 18, 2023</time></p>
</footer>
<nav class="pagination">
<a href="/programming/2022/10/14/difference-between-gulp-and-webpack.html" class="pagination--pager" title="Gulp 和 Webpack 有什么区别?
">上一页</a>
<a href="/tools/2023/05/27/jekyll-theme-minimal-mistakes.html" class="pagination--pager" title="Jekyll 主题 Minimal Mistakes 使用总结
">下一页</a>
</nav>
</div>
<div class="page__comments">
<h4 class="page__comments-title">留下评论</h4>
<section id="disqus_thread"></section>
</div>
</article>
</div>
</div>
搜索:
<div class="search-content">
<div class="search-content__inner-wrap"><form class="search-content__form" onkeydown="return event.key != 'Enter';" role="search">
<label class="sr-only" for="search">
输入您要搜索的关键词...
</label>
<input type="search" id="search" class="search-input" tabindex="-1" placeholder="输入您要搜索的关键词..." />
</form>
<div id="results" class="results"></div></div>
</div>
footer:
<div id="footer" class="page__footer">
<footer>
<!-- start custom footer snippets -->
<!-- end custom footer snippets -->
<div class="page__footer-follow">
<ul class="social-icons">
<li><strong>关注:</strong></li>
<li><a href="https://github.com/feelang" rel="nofollow noopener noreferrer"><i class="fab fa-fw fa-github" aria-hidden="true"></i> Github</a></li>
<li><a href="https://weibo.com/u/1670598115" rel="nofollow noopener noreferrer"><i class="fab fa-fw fa-weibo" aria-hidden="true"></i> 微博</a></li>
<li><a href="/feed.xml"><i class="fas fa-fw fa-rss-square" aria-hidden="true"></i> Feed</a></li>
</ul>
</div>
<div class="page__footer-copyright">© 2024 <a href="https://feelang.xyz">feelang的技术博客</a>. 技术来自于 <a href="https://jekyllrb.com" rel="nofollow">Jekyll</a> & <a href="https://mademistakes.com/work/minimal-mistakes-jekyll-theme/" rel="nofollow">Minimal Mistakes</a>.</div>
</footer>
</div>
最后是 scripts:
<script src="/assets/js/main.min.js"></script>
<script src="/assets/js/lunr/lunr.min.js"></script>
<script src="/assets/js/lunr/lunr-store.js"></script>
<script src="/assets/js/lunr/lunr-en.js"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-EMB5R38212"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-EMB5R38212', { 'anonymize_ip': false});
</script>
<script>
var disqus_config = function () {
this.page.url = "https://feelang.xyz/tools/2023/05/27/jekyll-theme-minimal-mistakes.html"; /* Replace PAGE_URL with your page's canonical URL variable */
this.page.identifier = "/tools/2023/05/27/jekyll-theme-minimal-mistakes"; /* Replace PAGE_IDENTIFIER with your page's unique identifier variable */
};
(function() { /* DON'T EDIT BELOW THIS LINE */
var d = document, s = d.createElement('script');
s.src = 'https://feelang-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
_includes/scripts.html
中包含了以下内容:
1)自定义 js,默认使用 /assets/js/main.min.js
:
<script src="/assets/js/main.min.js"></script>
2)搜索
<script src="/assets/js/lunr/lunr.min.js"></script>
<script src="/assets/js/lunr/lunr-store.js"></script>
<script src="/assets/js/lunr/lunr-en.js"></script>
3)埋点
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-EMB5R38212"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-EMB5R38212', { 'anonymize_ip': false});
</script>
4)评论
<script>
var disqus_config = function () {
this.page.url = "https://feelang.xyz/tools/2023/05/27/jekyll-theme-minimal-mistakes.html"; /* Replace PAGE_URL with your page's canonical URL variable */
this.page.identifier = "/tools/2023/05/27/jekyll-theme-minimal-mistakes"; /* Replace PAGE_IDENTIFIER with your page's unique identifier variable */
};
(function() { /* DON'T EDIT BELOW THIS LINE */
var d = document, s = d.createElement('script');
s.src = 'https://feelang-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
5)js 脚本
最后总结一下 _layouts/default.html
的代码结构:
- html
- head
- include
head.html
- include
seo.html
- site.atom_feed
- viewport
/assets/css/main.css
- fontawesome
- site.head_scripts
- include
- include
head/custom.html
- include
- body
- include_cached
skip-links.html
#site-nav
#main
#footer
- include_cached
masthead.html
- site.logo
-
site.masthead site.title - site.subtitle
- site.data.navigation.main
- site.search
- Toggle menu
- `
- include_cached
- head
Health 项目技术总结(Next.js + AntD)
好长一段时间没写前端,很多知识点都忘掉了,为了防止遗忘,趁着项目刚结束,赶紧总结一下。
邂逅 Next.js
这个项目用的是 next.js
,也是 react
官方推荐的。今天才知道原来 react 已经升级改版,我之前掌握的技能早已过时。
- React 官方推荐入口 => Start a New React Project
- Next.js 官网入口 => Next.js
第一次用 next.js
,不了解技术细节,不过从使用感受来说,还不错,用起来很方便。
从官网简介可以看出,这是一个全栈框架:
Next.js enables you to create full-stack Web applications by extending the latest React features, and integrating powerful Rust-based JavaScript tooling for the fastest builds.
根据已经掌握的 antd 知识,很快就把第一个页面搞定了。
开始搞第二个页面时,我开始网上寻找 Router 解决方案,找到了 React Router。
发现这个框架也已经升级改版,而我只掌握了上一个版本的用法,所以就开始翻文档,学习新用法,但是内容实在太长了,读不下去。
顺便发现了 next.js
自身内置了路由模块——API Routes。
继续翻文档,结果云里雾里,根本看不懂。
灵机一动,不如去看看视频教程,找到了 Next.js App Router: Routing, Data Fetching, Caching。
原来在 next.js
项目中,只需要在 app
目录下新建一个文件夹就可以自动生成路由,实在方便。
就这样新建了第二个页面。
然后开始使用 antd
的 Form
组件写页面。
老朋友 AntD
使用 Form
<Form
form={form}
layout="vertical"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
其中:
form={form}
的form
来自:const [form] = Form.useForm()
onFinish
和onFinishFailed
是两个函数
const onFinish = (values: any) => {
console.log(values)
}
const onFinishFailed = (errorInfo: any) => {
console.log(errorInfo)
}
页面底部添加两个按钮:计算 & 重置。
<Form.Item>
<Space>
<Button type="primary" htmlType="submit">计算</Button>
<Button htmlType="reset">重置</Button>
</Space>
</Form.Item>
其中 <Space>
一用,就不用单独设置边距了,非常方便。
这两个按钮的功能通过设置 htmlType
属性来实现,其他任何代码不需要。
Form.Item
的联动校验
其中一个输入框要填写年龄,且是必填:
<Form.Item label="1 年龄(age)" name="age"
rules={[{ required: true, message: '请输入年龄' }]}
>
<InputNumber placeholder="请输入年龄" min={18} max={100}
onChange={setAge}
/>
</Form.Item>
利用 setAge
接收输入值:
const [age, setAge] = useState<string | number | null>(null)
需要留意的是,
InputNumber
的onChange
属性的变量类型为string | number | null
。
第二个输入框也是年龄,但它的输入值必须小于 age
:
<Form.Item label="3.1 绝经年龄(age at menopause)"
name="menopauseAge"
dependencies={['age']}
rules={[
{
required: true,
message: '请输入年龄',
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('age') >= value) {
return Promise.resolve();
}
return Promise.reject(new Error(`绝经年龄不得大于当前年龄(${age})`));
},
}),
]}
>
这个用法值得仔细记录一下。
先看 dependencies
的用法,官网介绍如下,简单明了。
当字段间存在依赖关系时使用。如果一个字段设置了 dependencies 属性。那么它所依赖的字段更新时,该字段将自动触发更新与校验。 一种常见的场景,就是注册用户表单的“密码”与“确认密码”字段。“确认密码”校验依赖于“密码”字段,设置 dependencies 后,“密码”字段更新会重新触发“校验密码”的校验逻辑。
再看 rules
用法:
校验规则,设置字段的校验逻辑。
类型为 Rule[]
,是个数组。
Rule
定义如下:
type Rule = RuleConfig | ((form: FormInstance) => RuleConfig);
可以看出,Rule
支持两种类型,所以上面代码中 rules
的第一个元素类型为 RuleConfig
:
{
required: true,
message: '请输入年龄',
},
第二个元素类型为 ((form: FormInstance) => RuleConfig)
,是个高阶函数。
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('age') >= value) {
return Promise.resolve();
}
return Promise.reject(new Error(`绝经年龄不得大于当前年龄(${age})`));
},
}),
由此可以推测出 getFieldValue
是 FormInstance
的一个属性,通过看源码可以证实:
export interface FormInstance<Values = any> {
getFieldValue: (name: NamePath) => StoreValue;
getFieldsValue: (() => Values) & ((nameList: NamePath[] | true, filterFunc?: (meta: Meta) => boolean) => any);
getFieldError: (name: NamePath) => string[];
getFieldsError: (nameList?: NamePath[]) => FieldError[];
getFieldWarning: (name: NamePath) => string[];
isFieldsTouched: ((nameList?: NamePath[], allFieldsTouched?: boolean) => boolean) & ((allFieldsTouched?: boolean) => boolean);
isFieldTouched: (name: NamePath) => boolean;
isFieldValidating: (name: NamePath) => boolean;
isFieldsValidating: (nameList: NamePath[]) => boolean;
resetFields: (fields?: NamePath[]) => void;
setFields: (fields: FieldData[]) => void;
setFieldValue: (name: NamePath, value: any) => void;
setFieldsValue: (values: RecursivePartial<Values>) => void;
validateFields: ValidateFields<Values>;
submit: () => void;
}
validator
也是一个高阶函数:
type Validator = (rule: RuleObject, value: StoreValue, callback: (error?: string) => void) => Promise<void | any> | void;
返回值是一个 Promise
或 void
。
通过以上分析,可以总结出:当两个 Form.Item
之间存在依赖时,可通过 dependencies
和 rules
属性设定依赖逻辑。
TypeScript tips
string => number
// menopauseAge 是个 `string`
const menopauseAge = values.menopauseAge ? +values.menopauseAge : 0
interface
interface RiskResult {
message: string,
description: string,
type: "success" | "warning" | "error" | "info"
}
用法:
const results: RiskResult[] = [
{
message: '乳腺癌低风险',
description: '请继续保持健康生活方式',
type: 'success'
},
]
const [result, setResult] = useState<RiskResult>()
`
- include_cached search/search_form.html
- lunr
- google
- algolia
- footer
- include footer/custom.html
- site.data.ui-text[site.locale].follow_label
- site.footer.links
- site.atom_feed
- copyright
- include_cached footer.html
- include scripts.html
- site.footer_scripts
- site.search
- include analytics.html
- google
- google-universal
- google-gtag
- custom
- include /comments-providers/scripts.html
- diques
- discourse
- facebook
- staticman
- staticman_v2
- utterances
- giscus
- custom
- site.after_footer_scripts
留下评论