<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>hooopo (Hooopo)</title>
    <link>http://beta.w2solo.com/hooopo</link>
    <description>https://hackershare.dev</description>
    <language>en-us</language>
    <item>
      <title>我的 side project 成为昨日 Hackernews Top 10</title>
      <description>&lt;p&gt;昨天晚上本来想在 &lt;a href="https://news.ycombinator.com/item?id=24579206" rel="nofollow" target="_blank" title=""&gt;hackernews&lt;/a&gt;上面推广一下新做的一个项目 &lt;a href="https://hackershare.dev" rel="nofollow" target="_blank"&gt;https://hackershare.dev&lt;/a&gt; ，顺便把之前做的一个&lt;a href="https://drawerd.com/" rel="nofollow" target="_blank" title=""&gt;DrawERD&lt;/a&gt;也提交了上去，没想到 hackershare 石沉大海，DrawERD 这个项目被顶了上来，真的是无心插柳...&lt;/p&gt;

&lt;p&gt;目前为止，获得了 51 upvote 和 37 comments，最好的时候在首页排到了 top 3 。hackernews 首页的流量确实可以，通过 GA 观察，最高峰差不多 30+ 同时在线人数，然后获得新注册用户几百个，比之前总数还要多...&lt;/p&gt;

&lt;p&gt;&lt;img src="https://img.way2solo.com/uploads/photo/2020/086d32cc-1575-40d4-81c7-4abf49e419a8.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;不过由于英文不是很好，没有很好的和老外在评论里沟通，这点很遗憾，通过他们的回复，总结一些 hackernews 推广经验：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在 hackernews 上发帖的时间很重要，最好选在国内的凌晨左右，因为他们的算法是按时间重力下沉的，时间因素占了很大的比重，时间选不好帖子很快就沉底了&lt;/li&gt;
&lt;li&gt;英文语法最好让专业一点的朋友 review 一下，有时候老外读起来真的会感到 confusing&lt;/li&gt;
&lt;li&gt;付费业务的话，隐私说明、价格说明这些也是非常重要，老外对这个很苛刻&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>hooopo</author>
      <pubDate>Fri, 25 Sep 2020 19:48:51 +0800</pubDate>
      <link>http://beta.w2solo.com/topics/602</link>
      <guid>http://beta.w2solo.com/topics/602</guid>
    </item>
    <item>
      <title>hackershare: Social bookmarking reinvented!</title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/f30cbd33-7093-4091-8850-45d9c9527f3a.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hackershare.dev" rel="nofollow" target="_blank" title=""&gt;hackershare&lt;/a&gt; 是一个书签分享平台，你可以通过 chrome extension 一键分享你正在浏览的网页。与 pocket 之类书签管理工具不同的是，hackershare 鼓励分享你的书签，而不是私藏，独乐乐不如众乐乐。&lt;/p&gt;

&lt;p&gt;大众标签是书签工具必备的一个特性，可以便于管理你的书签。成为一个强大的信息组织工具，是 hackershare 的目标，未来在标签上会做很多优化，比如同义词环、优选术语、上位术语等。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/9f00c460-f7d5-4264-896c-cfac7589709c.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;hackershare 是一个发现平台，系统通过用户的点击、浏览、收藏、评论等行为对内容进行热度排序，算出每日、每周、每月和总热度排序。目前 hackershare 的内容主要面向编程开发、产品运营、UI 设计、创业思考等。&lt;img src="https://l.ruby-china.com/photo/2020/c487ecf1-9507-4f21-b125-3bc281389f47.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;hackershare 是一个 RSS 订阅平台；系统会定期抓取技术相关的热门 RSS 源，你只需要订阅就可以收到 RSS 更新的通知，效果和 RSS 阅读器一样。同时，你还可以按其他维度来订阅你感兴趣的内容，比如关注用户、关注标签，这样用户和标签相关的内容就会推送给你，一键掌握技术咨询。&lt;/p&gt;

&lt;p&gt;未来会支持用户自己提交 RSS 源，你可以用来推广你的博客和产品。
&lt;img src="https://l.ruby-china.com/photo/2020/e2ea87f2-a7c7-4380-9ee2-75c0c0cd2310.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;hackershare 是一个开源项目，应用的范围很广，比如：如果拿来放商品链接，就可以成为什么值得买之类的导流站；&lt;/p&gt;

&lt;p&gt;如果拿来放新闻咨询就可以成为一个新闻站；&lt;/p&gt;

&lt;p&gt;如果团队内部共享资料，就可以成为一个内部知识库。&lt;/p&gt;

&lt;p&gt;和普通论坛或 CMS 比，hackershare 更轻量级，SEO 友好，具有更好的信息架构，以及自动化特性，做一些导流站的话，你甚至可以省去写爬虫的时间。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;项目地址：&lt;a href="https://hackershare.dev" rel="nofollow" target="_blank"&gt;https://hackershare.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;github：&lt;a href="https://github.com/hooopo/hackershare" rel="nofollow" target="_blank"&gt;https://github.com/hooopo/hackershare&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;chrome extension：&lt;a href="https://github.com/hooopo/hackershare-ext" rel="nofollow" target="_blank"&gt;https://github.com/hooopo/hackershare-ext&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>hooopo</author>
      <pubDate>Wed, 23 Sep 2020 07:59:30 +0800</pubDate>
      <link>http://beta.w2solo.com/topics/596</link>
      <guid>http://beta.w2solo.com/topics/596</guid>
    </item>
    <item>
      <title>Rails UJS + Stimulusjs + Turbolinks 5 = ❤️</title>
      <description>&lt;h2 id="del.icio.us复活？"&gt;del.icio.us 复活？&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/63877/90985133-721f2200-e5ac-11ea-8bb0-0b288d4f4e47.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;最近看到被雅虎收购后关掉的社会化书签网站&lt;a href="http://del.icio.us/" rel="nofollow" target="_blank" title=""&gt;del.icio.us&lt;/a&gt;又更新了内容，一个叫 Maciej Ceglowski 的家伙拥有了 del.icio.us 的域名，估计又要重新搞起？&lt;/p&gt;
&lt;h2 id="造个轮子"&gt;造个轮子&lt;/h2&gt;
&lt;p&gt;自从 google reader 被关掉、del.icio.us 被关掉，获取信息的渠道少了，我主要通过 github 和 twitter，用 pocket 来保存书签，但 pocket 更偏向收藏。&lt;/p&gt;

&lt;p&gt;由于很久没有写 Web，HTML CSS JavaScript 这些快忘的差不多了，抱着重新学习一下前端和做一个社会化标签网站的想法，注册了一个域名 hackershare.dev.&lt;/p&gt;

&lt;p&gt;产品的设计功能点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;可以通过 chrome extension 方便保存网页 URL，类似 pocket 的 chrome extension.&lt;/li&gt;
&lt;li&gt;Web 端可以根据用户收藏、分享等对 URL 的热度进行排序，类似 hackernews 和 reddit 的机制&lt;/li&gt;
&lt;li&gt;灵活的类目组织，可以对 URL 进行打标签，整理类目&lt;/li&gt;
&lt;li&gt;对 URL 进行评论讨论&lt;/li&gt;
&lt;li&gt;一点社区化功能，关注用户和类目之类&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Rails UJS + Stimulusjs + Turbolinks 5"&gt;Rails UJS + Stimulusjs + Turbolinks 5&lt;/h2&gt;
&lt;p&gt;下面写一下重新使用 Rails 来做 Web 的一些体验，技术栈主要是 Postgresql 12+，Rails6， Turbolinks 5，Rails UJS 和 Stimulusjs&lt;/p&gt;

&lt;p&gt;拿收藏功能来举例，使用 stimulusjs+rails-ujs 来演示一下 Rails 的新三板斧。页面效果：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/63877/90985775-b3193580-e5b0-11ea-8ed2-af8f6db90124.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;ERB 片段：&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;data-controller=&lt;/span&gt;&lt;span class="s"&gt;"likes"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"relative z-0 inline-flex shadow-sm rounded-md"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;link_to&lt;/span&gt; &lt;span class="na"&gt;toggle_liking_bookmark_path&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;bookmark&lt;/span&gt;&lt;span class="err"&gt;),&lt;/span&gt; &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:post&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="err"&gt;',&lt;/span&gt; &lt;span class="na"&gt;remote:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:json&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;action:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;ajax:success-&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;likes#toggle"}, class: "relative inline-flex items-center px-4 py-2 rounded-l-md border border-gray-300 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150" do %&amp;gt;
    &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;data-target=&lt;/span&gt;&lt;span class="s"&gt;"likes.svg"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-5 w-5 &amp;lt;%= bookmark.liked_by?(current_user) ? "&lt;/span&gt;&lt;span class="na"&gt;text-yellow-300&lt;/span&gt; &lt;span class="na"&gt;hover:text-yellow-400&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;text-gray-300&lt;/span&gt; &lt;span class="na"&gt;hover:text-gray-400&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;" viewBox="0 0 20 20" fill="currentColor"&amp;gt;
      &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-target=&lt;/span&gt;&lt;span class="s"&gt;"likes.data"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"-ml-px relative inline-flex items-center px-3 py-2 rounded-r-md border border-gray-300 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;bookmark.likes_count&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先是 UJS，&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;%= link_to toggle_liking_bookmark_path(bookmark), method: :post, type: 'button', remote: true, data: {type: :json, action: "ajax:success-&amp;gt;likes#toggle"}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;link_to 带 remote: true 之后 ujs 发起一个 ajax 请求，type 是 json，和 jquery-ujs 那时候的写法一致，只不过之前一般都是服务端返回 JavaScript 操作 DOM。现在有了 stimulusjs 层，返回 JavaScript 和 HTML 还有 JSON 都很方便处理。下面说 stimulusjs：&lt;/p&gt;

&lt;p&gt;HTML 里的&lt;code&gt;data-controller&lt;/code&gt;和&lt;code&gt;data-action&lt;/code&gt;还有&lt;code&gt;data-target&lt;/code&gt;作用是事件绑定。收藏这个功能就是 ujs 发起 ajax 请求成功之后，我们要更新 button 颜色，更新 likes.data 区域的数量。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// likes_controller.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stimulus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;targets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 这里如果返回的是JavaScript或html，说明是未登录情况，turbolinks直接给跳转了&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;like&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svgTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-gray-300&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svgTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hover:text-gray-400&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svgTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-yellow-300&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svgTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hover:text-yellow-400&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svgTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-yellow-300&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svgTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hover:text-yellow-400&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svgTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-gray-300&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svgTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hover:text-gray-400&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;likes_count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面就是 stimulusjs 的全部代码，&lt;code&gt;data: {action: "ajax:success-&amp;gt;likes#toggle"}&lt;/code&gt; 让 ajax 成功之后调用 likes_controller.js 的 toggle 方法，并且可以取得到 event 对象，这里来处理 ajax 请求之后的页面效果。无论是后端返回 HTML 还是 JSON 还是 JavaScript，相对旧的 jquery-ujs 的方式，在 stimulusjs 这层里处理都非常容易。并且 JavaScript 有了新的组织，统一在 app/javascrips/controllers 目录，每个功能单独一个 controller。&lt;/p&gt;

&lt;p&gt;当然，上面的功能还可以用另外的方式来实现，抛弃 rails-ujs，直接 stimulusjs 绑定 link 的 click 事件，在 likes_controller.js 里去做 ajax 请求 + 页面处理的工作，但我觉得 ujs+stimulusjs 这种更简单一些。&lt;/p&gt;

&lt;p&gt;下面说一下 turbolinks 5 和 ujs 的一些坑，我们知道使用 turbolinks 之后，内部链接之间切换不会刷新页面，体验要比每次重新刷新页面好很多，但对于服务器端来说，整个页面还是要渲染一遍，相关的查询也要执行一遍，想有更好的体验也需要一些专门的 ajax 的请求，返回局部内容，比如分页和过滤场景。&lt;/p&gt;

&lt;p&gt;但目前版本的 rails-ujs 和 turbolinks 5 还不是兼容的很好，拿分页举例，下一页按钮由 rails-ujs 触发，使用 pushState 接口同步 URL 历史，便于收藏，但由于这个请求并不是 turbolinks 参与的，turbolinks 维护的快照会缺失，当浏览器点后退或前进时，rails-ujs 渲染的部分会丢掉，体验很差。一个 hack 的解法是，把浏览器 restoration visit 屏蔽掉，统一 reload，由于浏览器后退的操作其实并不是高频，并不会代理什么影响：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.onpopstate = function(event) {
  document.location.reload();
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不知道 Turblinks 6 会不会把这个统一做好，或者抛弃 rails-ujs，让 turbolinks 来完成 ajax 的局部刷新功能。总体来说 rails-ujs+Stimulusjs+turbolinks 5 这套组合拳还是非常棒的，并且是真的渐进增强体验，比如分页和过滤这种，关闭了 JS，页面渲染还是正常，一点不影响 SEO. 只需要一行额外代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;js&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s2"&gt;"bookmarks/bookmarks_with_pagination"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;content_type: &lt;/span&gt;&lt;span class="s2"&gt;"text/html"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Links"&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hackershare.dev" rel="nofollow" target="_blank"&gt;https://hackershare.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hooopo/hackershare" rel="nofollow" target="_blank"&gt;https://github.com/hooopo/hackershare&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hooopo/hackershare-ext" rel="nofollow" target="_blank"&gt;https://github.com/hooopo/hackershare-ext&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/hackershare/pinmchdpdbjbhijbagmealcojjpeebmhv" rel="nofollow" target="_blank"&gt;https://chrome.google.com/webstore/detail/hackershare/pinmchdpdbjbhijbagmealcojjpeebmhv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stimulusjs.org/" rel="nofollow" target="_blank"&gt;https://stimulusjs.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/turbolinks/turbolinks" rel="nofollow" target="_blank"&gt;https://github.com/turbolinks/turbolinks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>hooopo</author>
      <pubDate>Mon, 24 Aug 2020 14:57:06 +0800</pubDate>
      <link>http://beta.w2solo.com/topics/527</link>
      <guid>http://beta.w2solo.com/topics/527</guid>
    </item>
    <item>
      <title>DrawERD-在线 ERD 和 DB 文档工具</title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/fefeafc4-32f1-4db9-a649-2951f207ffaa.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;最近做了一个在线 ERD 工具，本来的想法就是 “可以在线协同编辑 ERD 的工具”，写完之后，发现 “导出 DDL”、“生成 DB Doc” 这些真实需求。&lt;/p&gt;

&lt;p&gt;但我个人其实主要用来建模，开始想做一个项目的时候，把表结构和实体关系在 DrawERD 上画下来，在界面上迭代，觉得差不多了就导出 DDL，放到 Rails migration 里。&lt;/p&gt;

&lt;p&gt;自己这种 workflow 用下来基本上还是挺满意。不过推广上遇到一些问题，这东西有点小众，使用频率也不是很高，所以基本上只有自己用... 不过前段时间论坛上分享了一下，最近看数据陆陆续续还有一些用户注册。&lt;/p&gt;

&lt;p&gt;目前的 feature list：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;可以导出 SVG&lt;/li&gt;
&lt;li&gt;可以生成 SVG 外链&lt;/li&gt;
&lt;li&gt;导出 DDL SQL&lt;/li&gt;
&lt;li&gt;导出 Markdown 文档：&lt;a href="https://gist.github.com/hooopo/f9353f6e10cf31dc4e7593d6b56c1617" rel="nofollow" target="_blank"&gt;https://gist.github.com/hooopo/f9353f6e10cf31dc4e7593d6b56c1617&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;生成在线 HTML 文档&lt;/li&gt;
&lt;li&gt;实体关系自动检测&lt;/li&gt;
&lt;li&gt;分组功能&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://drawerd.com/landing_assets/img/brand/full.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;地址： &lt;a href="https://drawerd.com" rel="nofollow" target="_blank"&gt;https://drawerd.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;相关话题：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ruby-china.org/topics/39732" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/39732&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://v2ex.com/t/681894" rel="nofollow" target="_blank"&gt;https://v2ex.com/t/681894&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>hooopo</author>
      <pubDate>Tue, 07 Jul 2020 19:18:45 +0800</pubDate>
      <link>http://beta.w2solo.com/topics/473</link>
      <guid>http://beta.w2solo.com/topics/473</guid>
    </item>
  </channel>
</rss>
