<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Al's blog]]></title><description><![CDATA[Al's blog]]></description><link>https://blog.lbj.moe</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 10:28:52 GMT</lastBuildDate><atom:link href="https://blog.lbj.moe/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[抛弃旁路由，使用 clash 作为透明网关]]></title><description><![CDATA[众所周知使用 clash 配置透明网关非常复杂，最近疯狂搜文终于找到了一个简便的工具：
https://github.com/mritd/tpclash
使用起来也非常简单。

首先先把它下载下来，重命名为 /usr/local/bin/clash

创建 clash 的工作目录，可以是任何目录，我放在了 /etc/clash

增加配置文件 config.yaml
 interface-name: ens18 # 请指定自己实际的接口名称(ip a 获取)

 port: 7890
 sock...]]></description><link>https://blog.lbj.moe/clash-tproxy</link><guid isPermaLink="true">https://blog.lbj.moe/clash-tproxy</guid><category><![CDATA[clash ]]></category><category><![CDATA[透明网关]]></category><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Sat, 08 Apr 2023 06:28:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/i7TvQLFWQAY/upload/4ada0459843e1b5091d79120d8ab2d19.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>众所周知使用 clash 配置透明网关非常复杂，最近疯狂搜文终于找到了一个简便的工具：</p>
<p><a target="_blank" href="https://github.com/mritd/tpclash">https://github.com/mritd/tpclash</a></p>
<p>使用起来也非常简单。</p>
<ol>
<li><p>首先<a target="_blank" href="https://github.com/mritd/tpclash/releases/latest">先把它下载下来</a>，重命名为 <code>/usr/local/bin/clash</code></p>
</li>
<li><p>创建 clash 的工作目录，可以是任何目录，我放在了 /etc/clash</p>
</li>
<li><p>增加配置文件 config.yaml</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">interface-name:</span> <span class="hljs-string">ens18</span> <span class="hljs-comment"># 请指定自己实际的接口名称(ip a 获取)</span>

 <span class="hljs-attr">port:</span> <span class="hljs-number">7890</span>
 <span class="hljs-attr">socks-port:</span> <span class="hljs-number">7891</span>
 <span class="hljs-attr">redir-port:</span> <span class="hljs-number">7892</span>
 <span class="hljs-attr">tproxy-port:</span> <span class="hljs-number">7893</span>
 <span class="hljs-attr">allow-lan:</span> <span class="hljs-literal">true</span>
 <span class="hljs-attr">bind-address:</span> <span class="hljs-string">"*"</span>
 <span class="hljs-attr">mode:</span> <span class="hljs-string">Rule</span>
 <span class="hljs-attr">log-level:</span> <span class="hljs-string">info</span>
 <span class="hljs-attr">external-controller:</span> <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">:9090</span>
 <span class="hljs-attr">secret:</span> <span class="hljs-string">""</span>
 <span class="hljs-attr">external-ui:</span> <span class="hljs-string">/opt/clash-dashboard</span>

 <span class="hljs-attr">dns:</span>
   <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span>
   <span class="hljs-attr">ipv6:</span> <span class="hljs-literal">false</span>
   <span class="hljs-attr">listen:</span> <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">:1053</span>
   <span class="hljs-attr">enhanced-mode:</span> <span class="hljs-string">fake-ip</span>
   <span class="hljs-attr">fake-ip-range:</span> <span class="hljs-number">198.18</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><span class="hljs-string">/16</span>
   <span class="hljs-attr">default-nameserver:</span>
     <span class="hljs-bullet">-</span> <span class="hljs-number">114.114</span><span class="hljs-number">.114</span><span class="hljs-number">.114</span>
     <span class="hljs-bullet">-</span> <span class="hljs-number">1.1</span><span class="hljs-number">.1</span><span class="hljs-number">.1</span>
   <span class="hljs-attr">nameserver:</span>
     <span class="hljs-bullet">-</span> <span class="hljs-number">114.114</span><span class="hljs-number">.114</span><span class="hljs-number">.114</span>
     <span class="hljs-bullet">-</span> <span class="hljs-number">223.6</span><span class="hljs-number">.6</span><span class="hljs-number">.6</span>

 <span class="hljs-attr">tun:</span>
   <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span>
   <span class="hljs-attr">stack:</span> <span class="hljs-string">system</span>
   <span class="hljs-attr">auto-route:</span> <span class="hljs-literal">true</span>
   <span class="hljs-attr">auto-detect-interface:</span> <span class="hljs-literal">true</span>
   <span class="hljs-attr">dns-hijack:</span>
     <span class="hljs-bullet">-</span> <span class="hljs-string">any:53</span>

 <span class="hljs-attr">proxies:</span>
   <span class="hljs-bullet">-</span> {
       <span class="hljs-attr">name:</span> <span class="hljs-string">hk</span>,
       <span class="hljs-attr">udp:</span> <span class="hljs-literal">true</span>,
     }

 <span class="hljs-attr">rule-providers:</span>
   <span class="hljs-attr">reject:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/reject.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/reject.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">icloud:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/icloud.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/icloud.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">apple:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/apple.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/apple.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">google:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/google.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/google.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">proxy:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/proxy.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/proxy.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">direct:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/direct.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/direct.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">private:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/private.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/private.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">gfw:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/gfw.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/gfw.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">greatfire:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/greatfire.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/greatfire.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">tld-not-cn:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">domain</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/tld-not-cn.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/tld-not-cn.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">telegramcidr:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">ipcidr</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/telegramcidr.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/telegramcidr.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">cncidr:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">ipcidr</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/cncidr.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/cncidr.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">lancidr:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">ipcidr</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/lancidr.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/lancidr.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

   <span class="hljs-attr">applications:</span>
     <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
     <span class="hljs-attr">behavior:</span> <span class="hljs-string">classical</span>
     <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/applications.txt"</span>
     <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/applications.yaml</span>
     <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

 <span class="hljs-attr">rules:</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,applications,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">DOMAIN-SUFFIX,hashnode.com,hk</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">DOMAIN-KEYWORD,taobao,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">DOMAIN-KEYWORD,github,hk</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">DOMAIN,clash.razord.top,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">DOMAIN,yacd.haishan.me,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,private,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,reject,REJECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,icloud,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,apple,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,google,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,proxy,hk</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,direct,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,lancidr,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,cncidr,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,telegramcidr,hk</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">GEOIP,LAN,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">GEOIP,CN,DIRECT</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">MATCH,DIRECT</span> <span class="hljs-comment"># 处理漏网之鱼，可按需调整为默认走 proxy 或像我一样默认 direct</span>
</code></pre>
</li>
<li><p>启动测试，看下配置是否有误</p>
<pre><code class="lang-bash"> clash --<span class="hljs-built_in">test</span>
</code></pre>
</li>
<li><p>注册为服务，方便使用 <code>systemctl</code> 控制</p>
<pre><code class="lang-plaintext"> # /etc/systemd/system/clash.service
 [Unit]
 Description=Clash TProxy
 After=network.target

 [Service]
 User=root
 Group=clash
 Restart=on-failure
 ExecStart=/usr/local/bin/clash -d /etc/clash

 [Install]
 WantedBy=multi-user.target
</code></pre>
</li>
<li><p>启动服务，并配置开机自启</p>
<pre><code class="lang-bash"> systemctl start clash
 systemctl <span class="hljs-built_in">enable</span> clash
</code></pre>
</li>
<li><p>将自己的设备网关设置为运行服务的机器 ip, Over</p>
</li>
</ol>
<hr />
<p>PS. tpclash 作者同样提供了 <a target="_blank" href="https://github.com/mritd/tpclash#26%E5%9C%A8docker%E5%AE%B9%E5%99%A8%E4%B8%AD%E4%BD%BF%E7%94%A8">docker 配置方案</a></p>
]]></content:encoded></item><item><title><![CDATA[使用 clash docker 做透明网关]]></title><description><![CDATA[之前一直使用 OpenWrt 旁路由的方式来翻墙，但 OpenWrt 本身不够稳定，并且我长期处于只使用 OpenWrt 其中的一个插件的状态，所以决定开一台虚拟机来体验下使用 Clash 作为透明网关
先给出(从同事那借来的)配置：
version: "3"

services:
  # Enable ip_forward - https://askubuntu.com/a/311054
  # For Ubuntu Server, you also need to disable syste...]]></description><link>https://blog.lbj.moe/clash-docker</link><guid isPermaLink="true">https://blog.lbj.moe/clash-docker</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Sun, 02 Apr 2023 13:55:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ZiQkhI7417A/upload/47a91e808e67195b205c665b6e4f5082.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>之前一直使用 OpenWrt 旁路由的方式来翻墙，但 OpenWrt 本身不够稳定，并且我长期处于只使用 OpenWrt 其中的一个插件的状态，所以决定开一台虚拟机来体验下使用 Clash 作为透明网关</p>
<p>先给出(从同事那借来的)配置：</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-comment"># Enable ip_forward - https://askubuntu.com/a/311054</span>
  <span class="hljs-comment"># For Ubuntu Server, you also need to disable systemd-resolved</span>
  <span class="hljs-comment"># 1. sudo systemctl stop systemd-resolved</span>
  <span class="hljs-comment"># 2. sudo systemctl disable systemd-resolved</span>
  <span class="hljs-attr">clash:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">dreamacro/clash-premium</span>
    <span class="hljs-attr">cap_add:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">NET_ADMIN</span>
    <span class="hljs-attr">devices:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/dev/net/tun</span>
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">"host"</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./clash:/root/.config/clash</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">"always"</span>
    <span class="hljs-attr">logging:</span>
      <span class="hljs-attr">driver:</span> <span class="hljs-string">"json-file"</span>
      <span class="hljs-attr">options:</span>
        <span class="hljs-attr">max-size:</span> <span class="hljs-string">"5m"</span>
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-attr">port:</span> <span class="hljs-number">7890</span>
<span class="hljs-attr">socks-port:</span> <span class="hljs-number">7891</span>
<span class="hljs-attr">redir-port:</span> <span class="hljs-number">7892</span>
<span class="hljs-attr">allow-lan:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">bind-address:</span> <span class="hljs-string">"*"</span>
<span class="hljs-attr">mode:</span> <span class="hljs-string">Rule</span>
<span class="hljs-attr">log-level:</span> <span class="hljs-string">info</span>
<span class="hljs-attr">external-controller:</span> <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">:9090</span>
<span class="hljs-attr">secret:</span> <span class="hljs-string">"secret"</span>
<span class="hljs-attr">dns:</span>
  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">listen:</span> <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">:1053</span>
  <span class="hljs-attr">ipv6:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">enhanced-mode:</span> <span class="hljs-string">fake-ip</span>
  <span class="hljs-attr">fake-ip-range:</span> <span class="hljs-number">198.18</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><span class="hljs-string">/16</span>
  <span class="hljs-attr">nameserver:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-number">223.5</span><span class="hljs-number">.5</span><span class="hljs-number">.5</span>
    <span class="hljs-bullet">-</span> <span class="hljs-number">223.6</span><span class="hljs-number">.6</span><span class="hljs-number">.6</span>

<span class="hljs-attr">tun:</span>
  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">stack:</span> <span class="hljs-string">system</span>
  <span class="hljs-attr">auto-route:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">auto-detect-interface:</span> <span class="hljs-literal">true</span>

<span class="hljs-attr">proxies:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"hk"</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">trojan</span>
    <span class="hljs-attr">server:</span> <span class="hljs-string">server</span>
    <span class="hljs-attr">port:</span> <span class="hljs-number">443</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">password</span>


<span class="hljs-attr">proxy-groups:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Proxy-Fallback</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">fallback</span>
    <span class="hljs-attr">proxies:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">hk</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">DIRECT</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">'http://www.gstatic.com/generate_204'</span>
    <span class="hljs-attr">interval:</span> <span class="hljs-number">3</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Proxy</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">select</span>
    <span class="hljs-attr">proxies:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">Proxy-Fallback</span>

<span class="hljs-attr">rule-providers:</span>
  <span class="hljs-attr">apple-proxy:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">behavior:</span> <span class="hljs-string">classical</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Hackl0us/SS-Rule-Snippet@master/Rulesets/Clash/Basic/Apple-proxy.yaml"</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/Apple-proxy.yaml</span>
    <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>
  <span class="hljs-attr">apple-direct:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">behavior:</span> <span class="hljs-string">classical</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Hackl0us/SS-Rule-Snippet@master/Rulesets/Clash/Basic/Apple-direct.yaml"</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/Apple-direct.yaml</span>
    <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>
  <span class="hljs-attr">china:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">behavior:</span> <span class="hljs-string">classical</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/DivineEngine/Profiles@master/Clash/RuleSet/China.yaml"</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/China.yaml</span>
    <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>
  <span class="hljs-attr">global:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">behavior:</span> <span class="hljs-string">classical</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/DivineEngine/Profiles@master/Clash/RuleSet/Global.yaml"</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/Global.yaml</span>
    <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>
  <span class="hljs-attr">netflix:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">behavior:</span> <span class="hljs-string">classical</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Hackl0us/SS-Rule-Snippet@main/Rulesets/Clash/App/stream/Netflix.yaml"</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/Netflix.yaml</span>
    <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>
  <span class="hljs-attr">disney:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">behavior:</span> <span class="hljs-string">classical</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Hackl0us/SS-Rule-Snippet@main/Rulesets/Clash/App/stream/DisneyPlus.yaml"</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/Disney.yaml</span>
    <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>
  <span class="hljs-attr">lan:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">behavior:</span> <span class="hljs-string">classical</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">"https://cdn.jsdelivr.net/gh/Hackl0us/SS-Rule-Snippet@master/Rulesets/Clash/Basic/LAN.yaml"</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">./ruleset/LAN.yaml</span>
    <span class="hljs-attr">interval:</span> <span class="hljs-number">86400</span>

<span class="hljs-attr">rules:</span>
  <span class="hljs-comment"># Rule sets</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,apple-proxy,Proxy</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,apple-direct,Proxy</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,china,DIRECT</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,global,Proxy</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">RULE-SET,lan,DIRECT</span>
  <span class="hljs-comment"># Final rules</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">GEOIP,CN,DIRECT</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">MATCH,Proxy</span>
</code></pre>
<p>然后 <code>docker compose up -d</code></p>
<p>当然以上只是把 Clash 跑起来，宿主机仍然需要设置转发规则来转发流量到 clash dns：</p>
<pre><code class="lang-bash"><span class="hljs-comment"># https://zhuanlan.zhihu.com/p/423684520</span>
REDIR_PORT=<span class="hljs-string">"7892"</span>
CLASH_DNS_PORT=<span class="hljs-string">"1053"</span>

<span class="hljs-comment">#在nat表中新建一个clash规则链</span>
iptables -t nat -N CLASH
<span class="hljs-comment">#排除环形地址与保留地址，匹配之后直接RETURN</span>
iptables -t nat -A CLASH -d 0.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 10.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 127.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 169.254.0.0/16 -j RETURN
iptables -t nat -A CLASH -d 172.16.0.0/12 -j RETURN
iptables -t nat -A CLASH -d 192.168.0.0/16 -j RETURN
iptables -t nat -A CLASH -d 224.0.0.0/4 -j RETURN
iptables -t nat -A CLASH -d 240.0.0.0/4 -j RETURN
<span class="hljs-comment">#重定向tcp流量到本机 redir_host</span>
iptables -t nat -A CLASH -p tcp -j REDIRECT --to-port <span class="hljs-string">"<span class="hljs-variable">$REDIR_PORT</span>"</span>
<span class="hljs-comment">#拦截外部tcp数据并交给clash规则链处理</span>
iptables -t nat -A PREROUTING -p tcp -j CLASH

<span class="hljs-comment">#在nat表中新建一个clash_dns规则链</span>
iptables -t nat -N CLASH_DNS
<span class="hljs-comment">#清空clash_dns规则链</span>
iptables -t nat -F CLASH_DNS
<span class="hljs-comment">#重定向udp流量到本机 clash dns</span>
iptables -t nat -A CLASH_DNS -p udp -j REDIRECT --to-port <span class="hljs-string">"<span class="hljs-variable">$CLASH_DNS_PORT</span>"</span>
<span class="hljs-comment">#抓取本机产生的53端口流量交给clash_dns规则链处理</span>
iptables -t nat -I OUTPUT -p udp --dport 53 -j CLASH_DNS
<span class="hljs-comment">#拦截外部upd的53端口流量交给clash_dns规则链处理</span>
iptables -t nat -I PREROUTING -p udp --dport 53 -j CLASH_DNS
</code></pre>
<p>之后把各种设置或主路由的 dns 网关设置为宿主机 ip 就可以翻墙了。</p>
<p>但是仍然有一个问题，clash 在跑的情况下宿主机不能联网了，应该是以上路由规则存在问题，<s>正在与 ChatGPT 激情讨论中</s></p>
]]></content:encoded></item><item><title><![CDATA[warning: Please check that your locale settings:   
        LANGUAGE = "en_US:en",   
        LC_ALL = (unset),   
        LC_MESSAGES = "en_US.UTF-8"]]></title><description><![CDATA[If you login to a vps through ssh and always get warning like me
warning: Please check that your locale settings:   
        LANGUAGE = "en_US:en",   
        LC_ALL = (unset),   
        LC_MESSAGES = "en_US.UTF-8",   
        LANG = "en_US.UTF-8"  ...]]></description><link>https://blog.lbj.moe/warning-please-check-that-your-locale-settings-language-enusen-lcall-unset-lcmessages-enusutf-8</link><guid isPermaLink="true">https://blog.lbj.moe/warning-please-check-that-your-locale-settings-language-enusen-lcall-unset-lcmessages-enusutf-8</guid><category><![CDATA[ssh]]></category><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Wed, 30 Jun 2021 06:03:57 GMT</pubDate><content:encoded><![CDATA[<p>If you login to a vps through ssh and always get warning like me</p>
<pre><code>warning: Please <span class="hljs-keyword">check</span> that your locale <span class="hljs-keyword">settings</span>:   
        <span class="hljs-keyword">LANGUAGE</span> = <span class="hljs-string">"en_US:en"</span>,   
        LC_ALL = (unset),   
        LC_MESSAGES = <span class="hljs-string">"en_US.UTF-8"</span>,   
        LANG = <span class="hljs-string">"en_US.UTF-8"</span>   
    <span class="hljs-keyword">are</span> supported <span class="hljs-keyword">and</span> installed <span class="hljs-keyword">on</span> your <span class="hljs-keyword">system</span>
</code></pre><p>That maybe not a env issue, probably you should edit ssh config:</p>
<pre><code class="lang-console">vim /etc/ssh/ssh_config
</code></pre>
<p>Then comment out this line</p>
<pre><code><span class="hljs-attribute">SendEnv</span> LANG LC_*

<span class="hljs-comment"># to</span>

<span class="hljs-comment"># SendEnv LANG LC_*</span>
</code></pre><p>Puff.</p>
]]></content:encoded></item><item><title><![CDATA[Compile openwrt apps]]></title><description><![CDATA[apt update

apt-get install software-properties-common
add-apt-repository ppa:deadsnakes/ppa

apt install -y build-essential asciidoc binutils bzip2 curl gawk gettext git libncurses5-dev libz-dev patch python3.5 python2.7 unzip zlib1g-dev lib32gcc1 l...]]></description><link>https://blog.lbj.moe/compile-openwrt-apps</link><guid isPermaLink="true">https://blog.lbj.moe/compile-openwrt-apps</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Mon, 19 Apr 2021 05:41:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1625034136986/n48JI49Q-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<pre><code class="lang-bash">apt update

apt-get install software-properties-common
add-apt-repository ppa:deadsnakes/ppa

apt install -y build-essential asciidoc binutils bzip2 curl gawk gettext git libncurses5-dev libz-dev patch python3.5 python2.7 unzip zlib1g-dev lib32gcc1 libc6-dev-i386 subversion flex uglifyjs git-core gcc-multilib p7zip p7zip-full msmtp libssl-dev texinfo libglib2.0-dev xmlto qemu-utils upx libelf-dev autoconf automake libtool autopoint device-tree-compiler g++-multilib antlr3 gperf wget

git <span class="hljs-built_in">clone</span> -b 19.07 --single-branch https://github.com/Lienol/openwrt openwrt19

<span class="hljs-built_in">cd</span> openwrt19

vim feeds.conf.default

<span class="hljs-comment"># add these lines to feed config</span>
src-git kenzo https://github.com/kenzok8/openwrt-packages
src-git small https://github.com/kenzok8/small


./scripts/feeds clean
./scripts/feeds update -a
./scripts/feeds install -a

make menuconfig

make -j8 download V=s
make -j1 V=s
</code></pre>
<hr />
<p>references:</p>
<ul>
<li>https://github.com/Lienol/openwrt</li>
<li>https://mianao.info/2020/05/05/%E7%BC%96%E8%AF%91%E6%9B%B4%E6%96%B0OpenWrt-PassWall%E5%92%8CSSR-plus%E6%8F%92%E4%BB%B6</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Run js until css transition completed]]></title><description><![CDATA[写代码的时候我们可能会经常碰到需要在某个元素的动画完成后再执行某些代码的场景，这时，我们就需要用到 transitionend 事件。
使用的方法也相当简单：
const listener = () => {

  // do something...

}


document.querySelector('#menu').addEventListener('transitionend', listener)
需要注意的两点：

如果 transition 是针对多个属性的，则该事件会被触发多...]]></description><link>https://blog.lbj.moe/run-js-until-css-transition-completed-2bd6368c2efd</link><guid isPermaLink="true">https://blog.lbj.moe/run-js-until-css-transition-completed-2bd6368c2efd</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Fri, 13 Dec 2019 07:23:11 GMT</pubDate><content:encoded><![CDATA[<p>写代码的时候我们可能会经常碰到需要在某个元素的动画完成后再执行某些代码的场景，这时，我们就需要用到 transitionend 事件。</p>
<p>使用的方法也相当简单：</p>
<pre><code><span class="hljs-keyword">const</span> listener = () =&gt; {

  <span class="hljs-comment">// do something...</span>

}


<span class="hljs-built_in">document</span>.<span class="hljs-built_in">querySelector</span>(<span class="hljs-string">'#menu'</span>).addEventListener(<span class="hljs-string">'transitionend'</span>, listener)
</code></pre><p>需要注意的两点：</p>
<ul>
<li><p>如果 transition 是针对<strong>多个属性</strong>的，则该事件会被触发多次</p>
</li>
<li><p>如果被监听元素的子元素样式也有 transition 属性，那么该监听也会被<strong>子元素</strong>的 transitionend 事件触发</p>
</li>
</ul>
<p>针对第 2 点，我们可以这样做：</p>
<pre><code><span class="hljs-keyword">const</span> listener = () =&gt; {

  <span class="hljs-keyword">if</span> (ev.currentTarget !== ev.target) <span class="hljs-keyword">return</span>

  <span class="hljs-comment">// do something...</span>

}

<span class="hljs-built_in">document</span>.<span class="hljs-built_in">querySelector</span>(<span class="hljs-string">'#menu'</span>).addEventListener(<span class="hljs-string">'transitionend'</span>, listener)
</code></pre><p>针对第 1 点，我们可以使用闭包解决问题：</p>
<pre><code><span class="hljs-comment">// only fired once</span>
<span class="hljs-keyword">const</span> listener = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">let</span> flag = <span class="hljs-literal">false</span>

  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">ev</span>) </span>{

    <span class="hljs-keyword">if</span> (flag) <span class="hljs-keyword">return</span>

    <span class="hljs-keyword">if</span> (ev.currentTarget !== <span class="hljs-built_in">this</span>) <span class="hljs-keyword">return</span>

    flag = <span class="hljs-literal">true</span>

    <span class="hljs-built_in">console</span>.log(ev.currentTarget)

  }

}

<span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#menu'</span>).addEventListener(<span class="hljs-string">'transitionend'</span>, listener())
</code></pre><p>refs:</p>
<ul>
<li><p>Transition events <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement#Transition_events">https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement#Transition_events</a></p>
</li>
<li><p>HTMLElement: transitionend event <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/transitionend_event">https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/transitionend_event</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[yarn、.npmrc 和私有 npm 仓库]]></title><description><![CDATA[如果你的项目中使用了 yarn 和私有 npm 仓库，却依赖 npmrc 指定 registry 的话，可能会和我下面碰到一样的问题：
uh-oh
更奇怪的一点是，普通的 http 请求是可以访问到的:

猜测和 npm 仓库的相应策略有关系，但是等对应部门给出解决方案不太现实，所以只好自己试试把 .npmrc 替换为 .yarnrc
好吧，没想到问题就这么解决了，所以在这里只能给大家提供一个建议：

⚠️ yarn 尽量使用 .yarnrc 来指定项目 config]]></description><link>https://blog.lbj.moe/yarn-and-npmrc</link><guid isPermaLink="true">https://blog.lbj.moe/yarn-and-npmrc</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Tue, 03 Dec 2019 06:25:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152164512/Fb2lBiQCI.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>如果你的项目中使用了 yarn 和私有 npm 仓库，却依赖 npmrc 指定 registry 的话，可能会和我下面碰到一样的问题：</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152160153/xJUrNivWm.png" alt="uh-oh" /><em>uh-oh</em></p>
<p>更奇怪的一点是，普通的 http 请求是可以访问到的:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152162270/bpzHuKtyA.png" alt /></p>
<p>猜测和 npm 仓库的相应策略有关系，但是等对应部门给出解决方案不太现实，所以只好自己试试把 .npmrc 替换为 .yarnrc</p>
<p>好吧，没想到问题就这么解决了，所以在这里只能给大家提供一个建议：</p>
<blockquote>
<p>⚠️ <strong>yarn 尽量使用 .yarnrc 来指定项目 config</strong></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[优化 webpack 4 配置加速 ci 构建过程]]></title><description><![CDATA[如果你的项目里也同时包含了 js 和 ts 的代码，相信构建速度会是项目中一个让人头疼的问题。下面我会尝试给出一些提高构建速度的配置并给出说明。
毫无疑问，配置大部分是针对 rules的。
以下给出的配置都假设你的 webpack.config.js位于项目根目录下。
## optimization 部分
// 使用 terser 代替 uglify-js
const TerserPlugin = require('terser-webpack-plugin');
const isWsl = r...]]></description><link>https://blog.lbj.moe/speed-up-webpack-4</link><guid isPermaLink="true">https://blog.lbj.moe/speed-up-webpack-4</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Fri, 25 Oct 2019 09:30:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152151239/JFM1t9xuz.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>如果你的项目里也同时包含了 js 和 ts 的代码，相信构建速度会是项目中一个让人头疼的问题。下面我会尝试给出一些提高构建速度的配置并给出说明。</p>
<p>毫无疑问，配置大部分是针对 <code>rules</code>的。</p>
<p>以下给出的配置都假设你的 <code>webpack.config.js</code>位于项目根目录下。</p>
<h2 id="optimization">## optimization 部分</h2>
<pre><code><span class="hljs-string">//</span> <span class="hljs-string">使用</span> <span class="hljs-string">terser</span> <span class="hljs-string">代替</span> <span class="hljs-string">uglify-js</span>
<span class="hljs-string">const</span> <span class="hljs-string">TerserPlugin</span> <span class="hljs-string">=</span> <span class="hljs-string">require('terser-webpack-plugin');</span>
<span class="hljs-string">const</span> <span class="hljs-string">isWsl</span> <span class="hljs-string">=</span> <span class="hljs-string">require('is-wsl');</span>

<span class="hljs-string">//</span> <span class="hljs-string">optimization</span>
{
  <span class="hljs-string">//</span> <span class="hljs-string">Keep</span> <span class="hljs-string">the</span> <span class="hljs-string">runtime</span> <span class="hljs-string">chunk</span> <span class="hljs-string">separated</span> <span class="hljs-string">to</span> <span class="hljs-string">enable</span> <span class="hljs-string">long</span> <span class="hljs-string">term</span> <span class="hljs-string">caching</span>
    <span class="hljs-string">//</span> <span class="hljs-string">https://webpack.js.org/configuration/optimization/#optimizationruntimechunk</span>
    <span class="hljs-attr">runtimeChunk:</span> <span class="hljs-literal">true</span>,

<span class="hljs-attr">minimizer:</span> [
    <span class="hljs-string">new</span> <span class="hljs-string">TerserPlugin(</span>{
           <span class="hljs-attr">terserOptions:</span> {
               <span class="hljs-attr">parse:</span> {
                   <span class="hljs-string">//</span> <span class="hljs-string">we</span> <span class="hljs-string">want</span> <span class="hljs-string">uglify-js</span> <span class="hljs-string">to</span> <span class="hljs-string">parse</span> <span class="hljs-string">ecma</span> <span class="hljs-number">8</span> <span class="hljs-string">code.</span> <span class="hljs-string">However</span>, <span class="hljs-string">we</span> <span class="hljs-string">don't</span> <span class="hljs-string">want</span> <span class="hljs-string">it</span>
                   <span class="hljs-string">//</span> <span class="hljs-string">to</span> <span class="hljs-string">apply</span> <span class="hljs-string">any</span> <span class="hljs-string">minfication</span> <span class="hljs-string">steps</span> <span class="hljs-string">that</span> <span class="hljs-string">turns</span> <span class="hljs-string">valid</span> <span class="hljs-string">ecma</span> <span class="hljs-number">5</span> <span class="hljs-string">code</span>
                   <span class="hljs-string">//</span> <span class="hljs-string">into</span> <span class="hljs-string">invalid</span> <span class="hljs-string">ecma</span> <span class="hljs-number">5</span> <span class="hljs-string">code.</span> <span class="hljs-string">This</span> <span class="hljs-string">is</span> <span class="hljs-string">why</span> <span class="hljs-string">the</span> <span class="hljs-string">'compress'</span> <span class="hljs-string">and</span> <span class="hljs-string">'output'</span>
                   <span class="hljs-string">//</span> <span class="hljs-string">sections</span> <span class="hljs-string">only</span> <span class="hljs-string">apply</span> <span class="hljs-string">transformations</span> <span class="hljs-string">that</span> <span class="hljs-string">are</span> <span class="hljs-string">ecma</span> <span class="hljs-number">5</span> <span class="hljs-string">safe</span>
                   <span class="hljs-string">//</span> <span class="hljs-string">https://github.com/facebook/create-react-app/pull/4234</span>
                   <span class="hljs-attr">ecma:</span> <span class="hljs-number">8</span>,
               },
               <span class="hljs-attr">compress:</span> {
                   <span class="hljs-attr">ecma:</span> <span class="hljs-number">5</span>,
                   <span class="hljs-attr">warnings:</span> <span class="hljs-literal">false</span>,
                   <span class="hljs-string">//</span> <span class="hljs-attr">Disabled because of an issue with Uglify breaking seemingly valid code:</span>
                   <span class="hljs-string">//</span> <span class="hljs-string">https://github.com/facebook/create-react-app/issues/2376</span>
                   <span class="hljs-string">//</span> <span class="hljs-attr">Pending further investigation:</span>
                   <span class="hljs-string">//</span> <span class="hljs-string">https://github.com/mishoo/UglifyJS2/issues/2011</span>
                   <span class="hljs-attr">comparisons:</span> <span class="hljs-literal">false</span>,
                   <span class="hljs-string">//</span> <span class="hljs-attr">Pending futher investigation:</span>
                   <span class="hljs-string">//</span> <span class="hljs-string">https://github.com/terser-js/terser/issues/120</span>
                   <span class="hljs-attr">inline:</span> <span class="hljs-number">2</span>,
               },
               <span class="hljs-attr">mangle:</span> {
                   <span class="hljs-attr">safari10:</span> <span class="hljs-literal">true</span>,
               },
               <span class="hljs-attr">output:</span> {
                   <span class="hljs-attr">ecma:</span> <span class="hljs-number">5</span>,
                   <span class="hljs-attr">comments:</span> <span class="hljs-literal">false</span>,
                   <span class="hljs-string">//</span> <span class="hljs-string">Turned</span> <span class="hljs-string">on</span> <span class="hljs-string">because</span> <span class="hljs-string">emoji</span> <span class="hljs-string">and</span> <span class="hljs-string">regex</span> <span class="hljs-string">is</span> <span class="hljs-string">not</span> <span class="hljs-string">minified</span> <span class="hljs-string">properly</span> <span class="hljs-string">using</span> <span class="hljs-string">default</span>
                   <span class="hljs-string">//</span> <span class="hljs-string">https://github.com/facebook/create-react-app/issues/2488</span>
                   <span class="hljs-attr">ascii_only:</span> <span class="hljs-literal">true</span>,
               },
           },

<span class="hljs-string">//</span> <span class="hljs-string">Use</span> <span class="hljs-string">multi-process</span> <span class="hljs-string">parallel</span> <span class="hljs-string">running</span> <span class="hljs-string">to</span> <span class="hljs-string">improve</span> <span class="hljs-string">the</span> <span class="hljs-string">build</span> <span class="hljs-string">speed</span>
           <span class="hljs-string">//</span> <span class="hljs-attr">Default number of concurrent runs:</span> <span class="hljs-string">os.cpus().length</span> <span class="hljs-bullet">-</span> <span class="hljs-number">1</span>
           <span class="hljs-string">//</span> <span class="hljs-string">https://github.com/webpack-contrib/terser-webpack-plugin/issues/21</span>
           <span class="hljs-attr">parallel:</span> <span class="hljs-type">!isWsl,</span>
           <span class="hljs-string">//</span> <span class="hljs-string">Enable</span> <span class="hljs-string">file</span> <span class="hljs-string">caching</span>
           <span class="hljs-attr">cache:</span> <span class="hljs-literal">true</span>,
           <span class="hljs-attr">sourceMap:</span> <span class="hljs-literal">false</span>,
           <span class="hljs-attr">exclude:</span> <span class="hljs-string">/\.min\.js$/</span>,
       }<span class="hljs-string">)</span>,
  ]
}
</code></pre><h2 id="jsx">## 针对 js(x) 文件的配置</h2>
<blockquote>
<p>rules</p>
</blockquote>
<pre><code>{
    <span class="hljs-attribute">test</span>: /\.(js|jsx|mjs)$/,
    exclude: [/[/\\\\]node_modules[/\\\\]/],
    use: [
        <span class="hljs-string">'thread-loader'</span>,
        {
            loader: require.<span class="hljs-built_in">resolve</span>(<span class="hljs-string">'babel-loader'</span>),
            options: {
                compact: true,
                configFile: <span class="hljs-string">'./.babelrc'</span>,
                // 对项目 js 代码 transpile 的结果进行持久化缓存
                cacheDirectory: <span class="hljs-string">'.cache/babel'</span>,
                // 缓存文件不做 gzip 压缩，
                //当你的代码文件数量比较多时建议配置此项为 false
                cacheCompression: false,
            },
        },
        {
            <span class="hljs-attribute">loader</span>: require.<span class="hljs-built_in">resolve</span>(<span class="hljs-string">'eslint-loader'</span>)，
            options: {
        // 对 lint 的结果进行缓存
                cache: <span class="hljs-string">'.cache/eslint'</span>,
            },
        },
    ],
},
</code></pre><blockquote>
<p>plugins</p>
</blockquote>
<pre><code><span class="hljs-comment">// 再另一个进程里进行对 ts 的类型检查</span>
<span class="hljs-keyword">new</span> ForkTsCheckerWebpackPlugin({
    tsconfig: <span class="hljs-string">'./tsconfig.json'</span>,
    <span class="hljs-comment">// block webpack's emit to wait for type checker/linter and to add errors to the webpack's compilation</span>
    <span class="hljs-comment">// also required for the the overlay functionality of webpack-dev-server</span>
    <span class="hljs-keyword">async</span>: <span class="hljs-keyword">false</span>,
    <span class="hljs-comment">// checkSyntacticErrors is required as we use happyPackMode and the thread-loader to parallelise the builds</span>
    checkSyntacticErrors: <span class="hljs-keyword">true</span>,
}),
</code></pre><h2 id="tsx">## 针对 ts(x) 文件的配置</h2>
<pre><code><span class="hljs-keyword">const</span> ForkTsCheckerWebpackPlugin = <span class="hljs-keyword">require</span>(‘fork-ts-checker-webpack-plugin’);

<span class="hljs-keyword">const</span> os = <span class="hljs-keyword">require</span>(‘os’)
<span class="hljs-keyword">const</span> cpus = os.cpus().length
<span class="hljs-comment">// 需要给 fork-ts-checker-webpack-plugin 分配 1 个</span>
<span class="hljs-keyword">const</span> tsLoaderWorkers = cpus &gt; <span class="hljs-number">2</span> ? cpus — <span class="hljs-number">1</span> : <span class="hljs-number">1</span>
</code></pre><blockquote>
<p>rules</p>
</blockquote>
<pre><code>{
    <span class="hljs-attribute">test</span>: /\.(tsx?|d.ts)$/,
    include: <span class="hljs-string">'src'</span>,
    use: [
        {
            // 启用基于文件的缓存
            loader: require.<span class="hljs-built_in">resolve</span>(<span class="hljs-string">'cache-loader'</span>),
            options: {
                cacheDirectory: <span class="hljs-string">'.cache/cache-loader'</span>,
            },
        },
        {
            <span class="hljs-attribute">loader</span>: require.<span class="hljs-built_in">resolve</span>(<span class="hljs-string">'thread-loader'</span>),
            options: {
                workers: tsLoaderWorkers,
            },
        },
        {
            <span class="hljs-attribute">loader</span>: require.<span class="hljs-built_in">resolve</span>(<span class="hljs-string">'ts-loader'</span>),
            options: {
       <span class="hljs-comment">/**
        * Increase build speed by disabling typechecking for the
        * main process and is required to be used with thread-loader
        * [@see](http://twitter.com/see) [https://github.com/TypeStrong/ts-loader/blob/master/examples/thread-loader/webpack.config.js](https://github.com/TypeStrong/ts-loader/blob/master/examples/thread-loader/webpack.config.js)
        * Requires to use the ForkTsCheckerWebpack Plugin
        */</span>
                happyPackMode: true,
                configFile: `./tsconfig.json`,
            },
        },
    ],
},
</code></pre><blockquote>
<p>plugins</p>
</blockquote>
<pre><code><span class="hljs-comment">// 在另一个进程里进行对 ts 的类型检查</span>
<span class="hljs-keyword">new</span> ForkTsCheckerWebpackPlugin({
    tsconfig: <span class="hljs-string">'./tsconfig.json'</span>,
    <span class="hljs-comment">// block webpack's emit to wait for type checker/linter and to add errors to the webpack's compilation</span>
    <span class="hljs-comment">// also required for the the overlay functionality of webpack-dev-server</span>
    <span class="hljs-keyword">async</span>: <span class="hljs-keyword">false</span>,
    <span class="hljs-comment">// checkSyntacticErrors is required as we use happyPackMode and the thread-loader to parallelise the builds</span>
    checkSyntacticErrors: <span class="hljs-keyword">true</span>,
}),
</code></pre><h2 id="iymg5pwi5p6c">## 效果</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152146832/qV5y_GbMS.png" alt="当前项目代码量" /><em>当前项目代码量</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152149101/k4fLhN4S-.png" alt /></p>
]]></content:encoded></item><item><title><![CDATA[[yarn] How to use multiple versions of one module in workspaces]]></title><description><![CDATA[在 yarn workspaces 同时使用同一个库的不同版本
https://yarnpkg.com
在项目中，我们的项目结构很有可能是下面的情况：
.

├── packages

│   ├── module-a

│   │      └── package.json

│   ├── module-b

│   │      └── package.json

│   └── share

│   │      └── package.json (react-dnd@3.0.0)

└...]]></description><link>https://blog.lbj.moe/yarn-how-to-use-multiple-versions-of-one-module-in-workspaces-e7962d7faa61</link><guid isPermaLink="true">https://blog.lbj.moe/yarn-how-to-use-multiple-versions-of-one-module-in-workspaces-e7962d7faa61</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Wed, 14 Aug 2019 07:07:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152174361/OQ3HhTrte.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>在 yarn workspaces 同时使用同一个库的不同版本</p>
<p><img src="https://cdn-images-1.medium.com/max/2000/0*1jwc9XiYkHEmUn5z.png" alt="[https://yarnpkg.com](https://cdn.hashnode.com/res/hashnode/image/upload/v1605152172533/T0Ht0g7H3.html)" /><em><a target="_blank" href="https://yarnpkg.com/en/">https://yarnpkg.com</a></em></p>
<p>在项目中，我们的项目结构很有可能是下面的情况：</p>
<pre><code>.

├── <span class="hljs-selector-tag">packages</span>

│   ├── <span class="hljs-selector-tag">module-a</span>

│   │      └── <span class="hljs-selector-tag">package</span><span class="hljs-selector-class">.json</span>

│   ├── <span class="hljs-selector-tag">module-b</span>

│   │      └── <span class="hljs-selector-tag">package</span><span class="hljs-selector-class">.json</span>

│   └── <span class="hljs-selector-tag">share</span>

│   │      └── <span class="hljs-selector-tag">package</span><span class="hljs-selector-class">.json</span> (react-dnd<span class="hljs-variable">@3</span>.<span class="hljs-number">0.0</span>)

└── <span class="hljs-selector-tag">package</span><span class="hljs-selector-class">.json</span> (react-dnd<span class="hljs-variable">@9</span>.<span class="hljs-number">3.0</span>)
</code></pre><p>我们有三个 worksapce，分别是 module-a module-b share，module-a/module-b 都依赖了根目录下的 react-dnd@9，而 share 则依赖了 react-dnd@3.</p>
<p>在 build 时，share 下的代码如果出现了 import { DndProvider } from ‘react-dnd’ 类似内容的话，被打包的会是根目录下的 node_modules/react-dnd@9，而不是 share/node_modules/react-dnd@3.</p>
<p>根据我的个人理解，因为 workspace 是把所有的依赖都放到了项目根目录下，即使你在 share 下的 package.json 声明了你需要的 react-dnd@3，在打包时得到的依然会是根目录下的 react-dnd@9</p>
<p>如何处理这种情况呢？聪明的同学可能已经想到了，我们使用 <a target="_blank" href="https://yarnpkg.com/lang/en/docs/cli/add/#toc-yarn-add-alias">yarn 的特性</a>同时安装两个版本的 react-dnd 就好了。</p>
<pre><code>yarn add react-dnd-v3@npm<span class="hljs-symbol">:react-dnd</span>@3.<span class="hljs-number">0</span>.<span class="hljs-number">0</span>
</code></pre><p>然后把引用旧版本的 <code>react-dnd</code> 都修改为 <code>react-dnd-v3</code></p>
<p>一般情况下到这里，上面的问题就应该解决了，可是我们还有问题。</p>
<p>react-dnd@3 和 react-dnd@9 都依赖了 hoist-non-react-statics，而前者需要的是 2.x，后者需要的是 3.x!</p>
<p>这种情况下我们就需要把 share/node_modules 与根目录下的 node_modules 隔离开了，让 share 下保留 react-dnd@3，根目录下保留 react-dnd@9，同时将它们各自的依赖安装在自己的 node_module 下</p>
<p>再查了一下文档，发现了 <code>[nohoist</code>](https://yarnpkg.com/blog/2018/02/15/nohoist/)</p>
<pre><code><span class="hljs-keyword">...</span>
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"app"</span>,
  <span class="hljs-string">"private"</span>: true,
  <span class="hljs-string">"workspaces"</span>: {
    <span class="hljs-string">"packages"</span>: [
                  <span class="hljs-string">"packages/share"</span>, 
                  <span class="hljs-string">"packages/module-a"</span>, 
                  <span class="hljs-string">"packages/module-b"</span>
    ],
    <span class="hljs-string">"nohoist"</span>: [<span class="hljs-string">"share/react-dnd"</span>, <span class="hljs-string">"share/react-dnd/**"</span>]
  }
  <span class="hljs-keyword">...</span>
</code></pre><p>完成上述步骤就 ok 啦</p>
]]></content:encoded></item><item><title><![CDATA[使用 CapsLock 切换输入法]]></title><description><![CDATA[为了让 mac/win 下的体验稍微一致些, 写了一个 ahk 脚本来做这件事情.
针对 CapsLock 键:

单击* 切换输入法(本质是调用 win + sapce*)

双击/长按 切换 CapsLock 状态


preview
以下是脚本内容:
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
;#Persistent
; #Warn  ; Enable w...]]></description><link>https://blog.lbj.moe/win-switch-im-with-caps</link><guid isPermaLink="true">https://blog.lbj.moe/win-switch-im-with-caps</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Sat, 29 Jun 2019 14:48:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152185911/GC62fClNz.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>为了让 mac/win 下的体验稍微一致些, 写了一个 ahk 脚本来做这件事情.</p>
<p>针对 <em>CapsLock </em>键:</p>
<ul>
<li><p><strong>单击*</strong> <em>切换输入法(本质是调用 </em>win + sapce*)</p>
</li>
<li><p><strong>双击/长按</strong> 切换 <em>CapsLock </em>状态</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152180896/-10BYSVsa.gif" alt="preview" /><em>preview</em></p>
<p>以下是脚本内容:</p>
<pre><code class="lang-autohotkey">#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
;#Persistent
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.

hideTrayTip() {
    TrayTip  ; Attempt to hide it the normal way.
    if SubStr(A_OSVersion,1,3) = "10." {
        Menu Tray, NoIcon
        Sleep 200  ; It may be necessary to adjust this sleep.
        Menu Tray, Icon
    }
}


switchCapsLockState() {
    state := GetKeyState("CapsLock", "T")
    nextState := !state
    SetCapsLockState % nextState

    return nextState
}

showTip(isOn) {
    title := isOn ? "CapsLock: ON" : "CapsLock: OFF"
    text := isOn ? "已打开" : "已关闭"

    TrayTip, %title%, %text%, 0, 16
    Sleep 1000
    hideTrayTip()

    return 
}

toggleAndShowTip() {
    nextState :=switchCapsLockState()
    showTip(nextState)

    return
}

CapsLock::
    KeyWait, CapsLock, T0.2

    if (ErrorLevel) {
        ; long click
        toggleAndShowTip()
    } else {
        KeyWait, CapsLock, D T0.1

        if (ErrorLevel) {
            ; single click
            Send #{Space}
        } else {
            ; double click
            toggleAndShowTip()
        }
    }

    KeyWait, CapsLock

return
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Upgrade to babel 7]]></title><description><![CDATA[babel/babel
🐠 Babel is a compiler for writing next generation JavaScript. - babel/babelgithub.com
{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ],
    "plugins": [
        "@babel/plugin-transform-runtime",
      ...]]></description><link>https://blog.lbj.moe/upgrade-to-babel-7-6641efea6e86</link><guid isPermaLink="true">https://blog.lbj.moe/upgrade-to-babel-7-6641efea6e86</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Wed, 17 Apr 2019 08:49:57 GMT</pubDate><content:encoded><![CDATA[<p>babel/babel
🐠 Babel is a compiler for writing next generation JavaScript. - babel/babelgithub.com</p>
<pre><code>{
    <span class="hljs-attr">"presets"</span>: [
        <span class="hljs-string">"@babel/preset-env"</span>,
        <span class="hljs-string">"@babel/preset-react"</span>
    ],
    <span class="hljs-attr">"plugins"</span>: [
        <span class="hljs-string">"@babel/plugin-transform-runtime"</span>,
        [
            <span class="hljs-string">"@babel/plugin-proposal-decorators"</span>,
            {
                <span class="hljs-attr">"legacy"</span>: *<span class="hljs-literal">true</span>
            *}
        ],
        <span class="hljs-string">"@babel/plugin-proposal-function-sent"</span>,
        <span class="hljs-string">"@babel/plugin-proposal-export-namespace-from"</span>,
        <span class="hljs-string">"@babel/plugin-proposal-numeric-separator"</span>,
        <span class="hljs-string">"@babel/plugin-proposal-throw-expressions"</span>,
        <span class="hljs-string">"@babel/plugin-syntax-dynamic-import"</span>,
        <span class="hljs-string">"@babel/plugin-syntax-import-meta"</span>,
        [
            <span class="hljs-string">"@babel/plugin-proposal-class-properties"</span>,
            {
                <span class="hljs-attr">"loose"</span>: *<span class="hljs-literal">false</span>
            *}
        ],
        <span class="hljs-string">"@babel/plugin-proposal-json-strings"</span>,
        <span class="hljs-string">"@babel/plugin-transform-modules-commonjs"</span>
    ],
    <span class="hljs-attr">"env"</span>: {
        <span class="hljs-attr">"development"</span>: {
            <span class="hljs-attr">"plugins"</span>: [
                <span class="hljs-string">"react-hot-loader/babel"</span>
            ]
        }
    }
}
</code></pre><p><a target="_blank" href="https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md">https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md</a></p>
]]></content:encoded></item><item><title><![CDATA[Use webpack-dev-middleware along with html-webpack-plugin in dev mode, HMR enabled]]></title><description><![CDATA[webapck
Webpack-dev-server is sweet, but when you have some routes in your back end and have to handle them with other logic instead just redirect them to your spa, webpack-dev-middleware may be one of your options.
We can use webpack-dev-middleware ...]]></description><link>https://blog.lbj.moe/use-webpack-dev-middleware-along-with-html-webpack-plugin-in-dev-mode-hmr-enabled-d7f527abc72f</link><guid isPermaLink="true">https://blog.lbj.moe/use-webpack-dev-middleware-along-with-html-webpack-plugin-in-dev-mode-hmr-enabled-d7f527abc72f</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Sat, 13 Oct 2018 04:29:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152193032/bQAzccskF.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>webapck</p>
<p>Webpack-dev-server is sweet, but when you have some routes in your back end and have to handle them with other logic instead just redirect them to your spa, webpack-dev-middleware may be one of your options.</p>
<p>We can use webpack-dev-middleware along with express, but here is one problem. Except routes mentioned above, you still need to respond remaining requests with your index page, especially when html-webpack-plugin is involved, because when we enabled HMR, the compiled index page only exists in memory, we don’t know it’s precise location.</p>
<p>So here is the solution:
<a target="_blank" href="https://github.com/jantimon/html-webpack-plugin/issues/145#issuecomment-312911903"><strong>HtmlWebpackPlugin + webpack-dev-middleware other pathname than / · Issue #145 ·…</strong>
<em>Hello, First, thank you for your wonderful work! Second ,) My issue, I tried to use HtmlWebpackPlugin with dev server…</em>github.com</a></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">var</span> app = express();
<span class="hljs-keyword">var</span> webpack = <span class="hljs-built_in">require</span>(<span class="hljs-string">'webpack'</span>);
<span class="hljs-keyword">var</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);

<span class="hljs-keyword">var</span> compiler = webpack(<span class="hljs-built_in">require</span>(<span class="hljs-string">'./webpack.config.js'</span>));

<span class="hljs-keyword">var</span> devMiddleware = <span class="hljs-built_in">require</span>(<span class="hljs-string">'webpack-dev-middleware'</span>);

app.use(devMiddleware(compiler, {
  <span class="hljs-attr">noInfo</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">publicPath</span>: <span class="hljs-string">'/'</span>
}));

app.use(<span class="hljs-string">'*'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">req, res, next</span>) </span>{
  <span class="hljs-keyword">var</span> filename = path.join(compiler.outputPath,<span class="hljs-string">'index.html'</span>);
  devMiddleware.waitUntilValid(<span class="hljs-function">() =&gt;</span> {
    compiler.outputFileSystem.readFile(filename, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, result</span>)</span>{
      <span class="hljs-keyword">if</span> (err) {
        <span class="hljs-keyword">return</span> next(err);
      }
      res.set(<span class="hljs-string">'content-type'</span>,<span class="hljs-string">'text/html'</span>);
      res.send(result);
      res.end();
    });
  });
});

app.listen(<span class="hljs-number">3000</span>);
</code></pre>
]]></content:encoded></item><item><title><![CDATA[树莓派搭建 Shadowsocks server，开启DDNS]]></title><description><![CDATA[👉如果你也有一些在国外的朋友需要使用国内的服务（网易云音乐、虾米或者国内视频网站），并且这些朋友很烦每天都会催你配置的话，你可能才会用到这篇教程
准备
> 树莓派 x1
> 公网 ip x1（打客服电话问运营商）
初始化
在拿到树莓派以后先安装系统（废话），找 SD卡槽找了半天，后来在发现原来在背面 😶
安装系统的时候还有一个大坑，按照官方说的使用 Etcher 写入 SD 卡根本进入不了系统…后面搜索了一番用 Win32 Disk Imager 才搞定。
进入系统之后首先打开树莓派的设置界...]]></description><link>https://blog.lbj.moe/raspberry-pi-ssr</link><guid isPermaLink="true">https://blog.lbj.moe/raspberry-pi-ssr</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Mon, 24 Sep 2018 09:39:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152199899/BZDd1WhLS.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>👉如果你也有一些在国外的朋友需要使用国内的服务（网易云音乐、虾米或者国内视频网站），并且这些朋友很烦每天都会催你配置的话，你可能才会用到这篇教程</p>
<h2 id="5yeg5ash">准备</h2>
<p>&gt; 树莓派 x1</p>
<p>&gt; 公网 ip x1（打客服电话问运营商）</p>
<h2 id="5yid5ael5yyw">初始化</h2>
<p>在拿到树莓派以后先安装系统（废话），找 SD卡槽找了半天，后来在发现原来在背面 😶</p>
<p>安装系统的时候还有一个大坑，按照官方说的使用 <a target="_blank" href="https://etcher.io/">Etcher</a> 写入 SD 卡根本进入不了系统…后面搜索了一番用 <a target="_blank" href="https://sourceforge.net/projects/win32diskimager/">Win32 Disk Imager</a> 才搞定。</p>
<p>进入系统之后首先打开树莓派的设置界面，启用 SSH、关闭图形界面（节省资源，毕竟是要长期跑着+没有一个多余的显示器给它插）。</p>
<h2 id="shadowsocks">Shadowsocks 配置</h2>
<p>安装 <em>Shadowsocks </em>的部分倒是没有太多好说的，执行 <em>apt install</em> 就可以了，配置可以参考下面：</p>
<blockquote>
<h1 id="ew">{</h1>
<h1 id="server0000">“server”:”0.0.0.0",</h1>
<h1 id="serverport8388">“server_port”:8388,</h1>
<h1 id="localport1080">“local_port”:1080,</h1>
<h1 id="passwordpwd">“password”:”pwd”,</h1>
<h1 id="timeout300">“timeout”:300,</h1>
<h1 id="methodrc4-md5">“method”:”rc4-md5",</h1>
<h1 id="fastopen-false">“fast_open”: false,</h1>
<h1 id="workers-1">“workers”: 1</h1>
<h1 id="fq">}</h1>
</blockquote>
<p>由于我们要在路由器里给它分配 ip，所以这里的 <em>server </em>部分就填 0.0.0.0 就可以了。</p>
<h2 id="6lev55sx5zmo6ywn572u">路由器配置</h2>
<p>这里我的路由器使用的是 <a target="_blank" href="http://www.right.com.cn/forum/thread-161324-1-1.html">Padavan</a>，别的系统的设置请自行查找。</p>
<p>首先我们要先给树莓派一个固定 ip，然后设置端口转发(高级设置 → 外部网络 (WAN) → 端口转发 (UPnP) )。设置内容如下图所示：</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152198313/L62saesVr.png" alt="端口转发设置" /><em>端口转发设置</em></p>
<p>下面就可以配置 DDNS 了。</p>
<h2 id="ddns">配置 DDNS</h2>
<p>这里我们使用的是 <em>ddclient </em>+ <em>namecheap </em>的域名。</p>
<p>ddclient 配置文件默认在 <em>/etc/ddclient.conf</em> ，内容如下：</p>
<blockquote>
<h1 id="useweb-webdynamicdnspark-your-domaincomgetip">use=web, web=dynamicdns.park-your-domain.com/getip</h1>
<h1 id="protocolnamecheap">protocol=namecheap</h1>
<h1 id="serverdynamicdnspark-your-domaincom">server=dynamicdns.park-your-domain.com</h1>
<h1 id="iydku6xkuivlhoxlrrnpnidopohkv67mllk"># 以下内容需要修改</h1>
<h1 id="loginexamplecom">login=example.com #你的域名</h1>
<h1 id="passwordxxxpasswordxxx">password=xxxpasswordxxx #你的密码</h1>
<h1 id="subdomain">subdomain #你的二级域名</h1>
</blockquote>
<p>设置完成以后，执行 <em>ping </em>测试一下吧，如果返回结果就是你的公网 ip 就大工搞成了！</p>
]]></content:encoded></item><item><title><![CDATA[redux-saga 中对按需打开/关闭对话框的一个实践]]></title><description><![CDATA[这里使用的组件是 ant design
我们在实践过程中很有可能会遇到执行完成某个异步操作之后需要弹出对话框进行确认/取消操作然后执行后续行为的情况. 但是在 redux-saga 中, 因为一系列行为都是使用 generator 实现的, 而主体逻辑又被包含在了一个又一个的 saga 中, 无法人为地去执行 yield, 使得实现这个需求有些困难.
因此，我们需要把 Modal.confirm() 转换为 Promise:
export const showConfirm = ({ title...]]></description><link>https://blog.lbj.moe/redux-saga-modal</link><guid isPermaLink="true">https://blog.lbj.moe/redux-saga-modal</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Mon, 14 May 2018 09:38:27 GMT</pubDate><content:encoded><![CDATA[<p>这里使用的组件是 ant design</p>
<p>我们在实践过程中很有可能会遇到执行完成某个异步操作之后需要弹出对话框进行确认/取消操作然后执行后续行为的情况. 但是在 redux-saga 中, 因为一系列行为都是使用 generator 实现的, 而主体逻辑又被包含在了一个又一个的 saga 中, 无法人为地去执行 yield, 使得实现这个需求有些困难.</p>
<p>因此，我们需要把 Modal.confirm() 转换为 Promise:</p>
<pre><code><span class="hljs-keyword">export</span> const showConfirm = <span class="hljs-function"><span class="hljs-params">({ title, content })</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">(resolve, reject)</span> =&gt;</span> {
    Modal.*confirm*({
      title,
      content,
      onOk() {
        <span class="hljs-keyword">return</span> resolve(<span class="hljs-literal">true</span>)
      },
      onCancel() {
        <span class="hljs-keyword">return</span> reject(<span class="hljs-literal">false</span>)
      },
    })
  })
}
</code></pre><p>之后就可以通过 yield call() 来调用它了，通过 catch 来处理 cancel 时的事件, 具体代码如下:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> showConfirm = <span class="hljs-function">(<span class="hljs-params">{ title, content }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    Modal.confirm({
      title,
      content,
      <span class="hljs-attr">okText</span>    : <span class="hljs-string">`确认`</span>,
      <span class="hljs-attr">cancelText</span>: <span class="hljs-string">'取消'</span>,
      onOk() {
        <span class="hljs-keyword">return</span> resolve(<span class="hljs-literal">true</span>)
      },
      onCancel() {
        <span class="hljs-keyword">return</span> reject(<span class="hljs-literal">false</span>)
      },
    })
  })
}

<span class="hljs-comment">// saga</span>
<span class="hljs-function"><span class="hljs-keyword">function</span>* <span class="hljs-title">show</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">yield</span> call(showConfirm)
    <span class="hljs-keyword">yield</span> put(resolveAction())
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
  } <span class="hljs-keyword">catch</span>(e) {
    <span class="hljs-keyword">yield</span> put(rejectAction())
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
  }
}
</code></pre>
<p>Tips: 在声明了一个 saga 之后，是可以通过 call 方法获得返回值的，但是 通过 fork 方法得到的是一个 task 对象</p>
]]></content:encoded></item><item><title><![CDATA[Use webpack to bundle backend code]]></title><description><![CDATA[Use webpack to bundle backend code]]></description><link>https://blog.lbj.moe/use-webpack-to-bundle-backend-code-aac23611f270</link><guid isPermaLink="true">https://blog.lbj.moe/use-webpack-to-bundle-backend-code-aac23611f270</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Thu, 10 May 2018 09:12:29 GMT</pubDate><content:encoded><![CDATA[<p>Use webpack to bundle backend code</p>
]]></content:encoded></item><item><title><![CDATA[React router: 使用 Prompt 在路由状态变化时进行交互]]></title><description><![CDATA[在使用 react router 时，我们可以使用 history.listen() 在路由变化时进行一些操作，比如 dispatch action 这样的操作，那如果是需要交互提醒是否要跳转的情况呢？
答案是 Prompt.
根据官方文档，我们可以用下面的方式使用 Prompt：
<Prompt
  when={formIsHalfFilledOut}
  message="Are you sure you want to leave?"
/>

when: bool
Instead of c...]]></description><link>https://blog.lbj.moe/react-router-prompt</link><guid isPermaLink="true">https://blog.lbj.moe/react-router-prompt</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Fri, 27 Apr 2018 09:39:42 GMT</pubDate><content:encoded><![CDATA[<p>在使用 react router 时，我们可以使用 history.listen() 在路由变化时进行一些操作，比如 dispatch action 这样的操作，那如果是需要交互提醒是否要跳转的情况呢？</p>
<p>答案是<em> <a target="_blank" href="https://reacttraining.com/react-router/core/api/Prompt">Prompt</a></em>.</p>
<p>根据<a target="_blank" href="https://reacttraining.com/react-router/core/api/Prompt">官方文档</a>，我们可以用下面的方式使用 <em>Prompt</em>：</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">Prompt</span>
  <span class="hljs-attr">when</span>=<span class="hljs-string">{formIsHalfFilledOut}</span>
  <span class="hljs-attr">message</span>=<span class="hljs-string">"Are you sure you want to leave?"</span>
/&gt;</span>
</code></pre><blockquote>
<p><a target="_blank" href="https://reacttraining.com/core/api/Prompt/when-bool">when: bool</a>
Instead of conditionally rendering a <code>&amp;lt;Prompt&amp;gt;</code> behind a guard, you can always render it but pass <code>when={true}</code> or <code>when={false}</code> to prevent or allow navigation accordingly.</p>
</blockquote>
<p>而 <em>message </em>可以接受<em> string | function</em>，那么我们就有了下面的用法:</p>
<pre><code>&lt;Prompt
  <span class="hljs-keyword">when</span>={formIsHalfFilledOut}
  message={ saved ? <span class="hljs-keyword">true</span> : <span class="hljs-string">'Please save before leaving.'</span> }
/&gt;
</code></pre><p>在实际应用中，我们会面临多个页面都需要 Prompt 的情况，这是把它独立成一个组件是最方便的做法。</p>
<p>但在 <em>React 16+</em>中，多个组件嵌套时周期调用发生<a target="_blank" href="https://reactjs.org/blog/2017/09/26/react-v16.0.html#breaking-changes">一些调整</a>，实际上我们在使用的过程中很可能会遇到<a target="_blank" href="https://github.com/ReactTraining/react-router/issues/5707">这个问题</a>：</p>
<blockquote>
<p>“Warning: A history supports only one prompt at a time”</p>
</blockquote>
<p>解决方案其实也很简单，在 <em>cDM </em>和 <em>cWU </em>时分别切换 <em>when </em>的状态就可以了</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { Prompt } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>

<span class="hljs-comment">// why we need *when* here?</span>
<span class="hljs-comment">// Prompt in React 16 "Warning: A history supports only one prompt at a time" [1]</span>
<span class="hljs-comment">// [1] https://github.com/ReactTraining/react-router/issues/5707</span>
<span class="hljs-keyword">const</span> initState = {
  <span class="hljs-attr">when</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">showPrompt</span>: <span class="hljs-literal">false</span>,
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PromptWrapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{
  state = initState

  componentDidMount() {
    <span class="hljs-built_in">this</span>.setState({
      <span class="hljs-attr">when</span>: <span class="hljs-literal">true</span>,
    })
  }

  componentWillReceiveProps({ showPrompt }) {
    <span class="hljs-built_in">window</span>.onbeforeunload = showPrompt
      ? <span class="hljs-function">() =&gt;</span> <span class="hljs-literal">true</span>
      : <span class="hljs-literal">null</span>

    <span class="hljs-built_in">this</span>.setState({
      title,
      showPrompt,
    })
  }

  componentWillUnmount() {
    <span class="hljs-built_in">this</span>.setState({
      ...initState,
    })
    <span class="hljs-built_in">window</span>.onbeforeunload = <span class="hljs-literal">null</span>
  }

  render() {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.Fragment</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Prompt</span>
          <span class="hljs-attr">when</span>=<span class="hljs-string">{this.state.when}</span>
          <span class="hljs-attr">message</span>=<span class="hljs-string">{()</span> =&gt;</span> this.state.showPrompt
            ? `Sure to leave?`
            : true
          } /&gt;
        {this.props.children}
      <span class="hljs-tag">&lt;/<span class="hljs-name">React.Fragment</span>&gt;</span></span>
    )
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> PromptWrapper
</code></pre>
]]></content:encoded></item><item><title><![CDATA[七牛云上传: 使用 Ant Design Upload 组件]]></title><description><![CDATA[后台的上传 scope 是 bucket:key 形式的，所以在上传时 formData 的应该是下面的结构：

因此，我们需要给 Upload 这个组件传入 额外的 token、key. 根据官方文档，传入额外的参数只需要给它加上 data 这个属性就可以了，对应的值是一个 Object.

<Upload {…otherProps} data={{ token: ‘xxxx:token’, key: ‘xxx:key’ }} />

具体的代码如下:
import { Upload } fr...]]></description><link>https://blog.lbj.moe/qiniu-antd-upload</link><guid isPermaLink="true">https://blog.lbj.moe/qiniu-antd-upload</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Sat, 21 Apr 2018 16:00:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152212267/YdXq2WRt7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>后台的上传 scope 是 bucket:key 形式的，所以在上传时 formData 的应该是下面的结构：</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152210734/jyM0UGEiJ.png" alt /></p>
<p>因此，我们需要给 <em>Upload </em>这个组件传入 额外的 <em>token、key. </em>根据<a target="_blank" href="https://ant.design/components/upload-cn/#API">官方文档</a>，传入额外的参数只需要给它加上 <em>data </em>这个属性就可以了，对应的值是一个 <em>Object.</em></p>
<blockquote>
<p>&lt;Upload {…otherProps} data={{ token: ‘xxxx:token’, key: ‘xxx:key’ }} /&gt;</p>
</blockquote>
<p>具体的代码如下:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Upload } <span class="hljs-keyword">from</span> <span class="hljs-string">'antd'</span>

<span class="hljs-keyword">const</span> uploadProps = {
  <span class="hljs-attr">action</span>: <span class="hljs-string">`https://up-z2.qiniu.com`</span>,
  <span class="hljs-attr">name</span>: <span class="hljs-string">'file'</span>,
  <span class="hljs-attr">listType</span>: <span class="hljs-string">'picture-card'</span>,
  <span class="hljs-attr">className</span>: <span class="hljs-string">'avatar-uploader'</span>
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UploadImg</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
  state = {
    <span class="hljs-attr">token</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">key</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">domain</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">loading</span>: <span class="hljs-literal">false</span>,
  }

  componentDidMount() {
    getImgUploadToken()
      .then(<span class="hljs-function">(<span class="hljs-params">{ token, key, domain }</span>) =&gt;</span> {
        <span class="hljs-built_in">this</span>.setState({
          token,
          key,
          domain
        })
      })
  }

  handleAvatarChange = <span class="hljs-function">(<span class="hljs-params">{ file }</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (file.status === <span class="hljs-string">'uploading'</span>) {
      <span class="hljs-built_in">this</span>.setState({ <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span> })
      <span class="hljs-keyword">return</span>
    }
    <span class="hljs-keyword">if</span> (file.status === <span class="hljs-string">'done'</span>) {
      <span class="hljs-keyword">const</span> { <span class="hljs-attr">response</span>: { hash } } = file
      <span class="hljs-keyword">const</span> imageSrc = <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">this</span>.state.domain}</span><span class="hljs-subst">${hash}</span>`</span>

      updateProfilePic(imageSrc)
        .then(<span class="hljs-function">() =&gt;</span> {
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'上传成功'</span>)
        })
    }
  }

  render() {
    <span class="hljs-keyword">const</span> { token, key } = <span class="hljs-built_in">this</span>.state
    <span class="hljs-keyword">const</span> {
      user,
      imageUrl,
      <span class="hljs-attr">form</span>: { getFieldDecorator }
    } = <span class="hljs-built_in">this</span>.props

    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Upload</span> {<span class="hljs-attr">...uploadProps</span>}
        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">profile-uploader</span>'}
        <span class="hljs-attr">accept</span>=<span class="hljs-string">"image/*"</span>
        <span class="hljs-attr">showUploadList</span>=<span class="hljs-string">{false}</span>
        <span class="hljs-attr">data</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">token</span>, <span class="hljs-attr">key</span> }}
        <span class="hljs-attr">beforeUpload</span>=<span class="hljs-string">{beforeUpload}</span>
        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.handleAvatarChange}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{imageUrl}</span>
          <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
            <span class="hljs-attr">width:</span> '<span class="hljs-attr">102px</span>',
            <span class="hljs-attr">height:</span> '<span class="hljs-attr">102px</span>'
          }}
          <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Upload</span>&gt;</span></span>
    )
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> UploadImg
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Dealing with integer number leading with 0(s) in JavaScript]]></title><description><![CDATA[parseInt(012, 10)
// 10
parseInt(05, 10)
// 5
Interesting, huh? That’s because js would treat number leading with zero(s) as octal numeral, so we need to convert it into decimal number and treat it as string. So here is my solution:
// num = 0120
var...]]></description><link>https://blog.lbj.moe/dealing-with-integer-number-leading-with-0-s-in-javascript-409024b0c842</link><guid isPermaLink="true">https://blog.lbj.moe/dealing-with-integer-number-leading-with-0-s-in-javascript-409024b0c842</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Fri, 16 Mar 2018 16:09:17 GMT</pubDate><content:encoded><![CDATA[<p>parseInt(012, 10)
// 10
parseInt(05, 10)
// 5</p>
<p>Interesting, huh? That’s because js would treat number leading with zero(s) as octal numeral, so we need to convert it into decimal number and treat it as string. So here is my solution:</p>
<pre><code><span class="hljs-comment">// num = 0120</span>
<span class="hljs-keyword">var</span> result = parseInt(<span class="hljs-built_in">num</span>, <span class="hljs-number">10</span>).toString(<span class="hljs-number">8</span>)
<span class="hljs-comment">// result = '120'</span>
</code></pre>]]></content:encoded></item><item><title><![CDATA[在 vue-cli 中引入 SemanticUI]]></title><description><![CDATA[首先，我们需要先安装 jQuery
npm install — save jquery
然后在 webpack.dev.config.js 文件中，添加
// plugins 区块内
 new webpack.ProvidePlugin({
   $ : “jquery”,
   jQuery : “jquery”,
   “window.jQuery”: “jquery”,
   “root.jQuery” : “jquery”
 })
随后安装 semantic-ui-css
npm ins...]]></description><link>https://blog.lbj.moe/vue-semantic-ui</link><guid isPermaLink="true">https://blog.lbj.moe/vue-semantic-ui</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Mon, 05 Mar 2018 14:45:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152224754/8c6q9tzH0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>首先，我们需要先安装 jQuery</p>
<p>npm install — save jquery</p>
<p>然后在 webpack.dev.config.js 文件中，添加</p>
<pre><code><span class="hljs-comment">// plugins 区块内</span>
 <span class="hljs-selector-tag">new</span> <span class="hljs-selector-tag">webpack</span><span class="hljs-selector-class">.ProvidePlugin</span>({
   $ : “<span class="hljs-selector-tag">jquery</span>”,
   <span class="hljs-selector-tag">jQuery</span> : “<span class="hljs-selector-tag">jquery</span>”,
   “<span class="hljs-selector-tag">window</span><span class="hljs-selector-class">.jQuery</span>”: “<span class="hljs-selector-tag">jquery</span>”,
   “<span class="hljs-selector-tag">root</span><span class="hljs-selector-class">.jQuery</span>” : “<span class="hljs-selector-tag">jquery</span>”
 })
</code></pre><p>随后安装 semantic-ui-css</p>
<pre><code>npm <span class="hljs-keyword">install</span> semantic-ui-css — <span class="hljs-keyword">save</span>
</code></pre><p>之后在 webpack.base.config.js 文件中添加，</p>
<pre><code>// resolve 区块
 resolve : {
 extensions: [‘’, ‘.js’, ‘.vue’],
 fallback : [<span class="hljs-type">path</span>.<span class="hljs-keyword">join</span>(__dirname, ‘../node_modules’)],
 <span class="hljs-keyword">alias</span> : {
   ‘vue’ : ‘vue/dist/vue.common.js’,
   ‘src’ : <span class="hljs-type">path</span>.resolve(__dirname, ‘../src’),
   ‘assets’ : <span class="hljs-type">path</span>.resolve(__dirname, ‘../src/assets’),
   ‘components’: <span class="hljs-type">path</span>.resolve(__dirname, ‘../src/components’),
   // Semantic-UI
   ‘semantic’ : <span class="hljs-type">path</span>.resolve(__dirname, ‘../node_modules/semantic-ui-css/semantic.min.js’)
 }
 }
</code></pre><p>随后在 webpack.dev.config.js 文件中，添加</p>
<pre><code><span class="hljs-attribute">plugins</span>: [
   new webpack.ProvidePlugin({
     $ : “jquery”,
     <span class="hljs-attribute">jQuery </span>: “jquery”,
     “window.jQuery”: “jquery”,
     “root.jQuery” : “jquery”,
     <span class="hljs-comment">// Semantic-UI</span>
     <span class="hljs-attribute">semantic </span>: ‘semantic-ui-css’,
     <span class="hljs-attribute">Semantic </span>: ‘semantic-ui-css’,
     ‘semantic-ui’: ‘semantic-ui-css’
 }),
</code></pre><p>接下来，引入 css 和 js 文件:</p>
<pre><code><span class="hljs-keyword">import</span> semantic from ‘semantic’
<span class="hljs-keyword">import</span> ‘../node_modules/semantic-ui-css/semantic.min.css’
</code></pre><p>接下来，我们要测试一下，添加一个 vue 文件：</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"semantic-component"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ui selection dropdown semanticDropDown"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"gender"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"selected"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown icon"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"default text"</span>&gt;</span>Gender<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"menu"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"item"</span> <span class="hljs-attr">:data-value</span>=<span class="hljs-string">"item.Value"</span>
             <span class="hljs-attr">v-for</span>=<span class="hljs-string">"item in items"</span>
             [@<span class="hljs-attr">click</span>](<span class="hljs-attr">http:</span>//<span class="hljs-attr">twitter.com</span>/<span class="hljs-attr">click</span>)=<span class="hljs-string">"changeSelection(item)"</span>&gt;</span>
          {{ item.Gender }}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">pre</span>&gt;</span>{{ JSON.stringify(selectedItem) }}<span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    data() {
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">items</span>       : [
          {<span class="hljs-attr">Gender</span>: <span class="hljs-string">'Male'</span>, <span class="hljs-attr">Value</span>: <span class="hljs-number">1</span>},
          {<span class="hljs-attr">Gender</span>: <span class="hljs-string">'Female'</span>, <span class="hljs-attr">Value</span>: <span class="hljs-number">0</span>}
        ],
        <span class="hljs-attr">selected</span>    : <span class="hljs-string">''</span>,
        <span class="hljs-attr">selecteditem</span>: {}
      }
    },
    <span class="hljs-attr">methods</span>: {
      changeSelection(item) {
        <span class="hljs-built_in">this</span>.selectedItem = item
        <span class="hljs-built_in">this</span>.selected = item.Value
      }
    },
    mounted() {
      <span class="hljs-built_in">this</span>.selecteditem = {}
      $(<span class="hljs-built_in">this</span>.$el).find(<span class="hljs-string">'.semanticDropDown'</span>).dropdown()
    }
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1605152223132/ZQnl7xUpT.jpeg" alt /></p>
<p>参考链接：<a target="_blank" href="http://stackoverflow.com/questions/36676215/using-vue-js-with-semantic-ui">Using vue.js with semantic UI</a></p>
]]></content:encoded></item><item><title><![CDATA[Ubuntu 16.04 配置 LAMP 环境（PHP7）]]></title><description><![CDATA[sudo apt-get update
Step 1. Install Apache2
 — — — — — — — — —
 sudo apt-get install apache2

 sudo ufw app list
 //Sample output:
 Available applications:
 Apache
 Apache Full
 Apache Secure
 OpenSSH

 sudo ufw allow in “Apache Full” 
 //Allow incom...]]></description><link>https://blog.lbj.moe/ubuntu-16-lamp</link><guid isPermaLink="true">https://blog.lbj.moe/ubuntu-16-lamp</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Mon, 05 Mar 2018 14:40:22 GMT</pubDate><content:encoded><![CDATA[<p>sudo apt-get update</p>
<p>Step 1. Install Apache2
 — — — — — — — — —</p>
<pre><code> sudo apt-get <span class="hljs-keyword">install</span> apache2

 sudo ufw app <span class="hljs-keyword">list</span>
 //<span class="hljs-keyword">Sample</span> <span class="hljs-keyword">output</span>:
 Available applications:
 Apache
 Apache <span class="hljs-keyword">Full</span>
 Apache Secure
 OpenSSH

 sudo ufw <span class="hljs-keyword">allow</span> <span class="hljs-keyword">in</span> “Apache <span class="hljs-keyword">Full</span>” 
 //<span class="hljs-keyword">Allow</span> incoming traffic <span class="hljs-keyword">for</span> this profile
</code></pre><p>Step 2. Install MySQL
 — — — — — — — —</p>
<pre><code>sudo apt-<span class="hljs-keyword">get</span> install mysql-<span class="hljs-keyword">server</span>

sudo mysql_secure_installation
</code></pre><p>Step 3. Install PHP
 — — — — — — — — — -</p>
<pre><code>sudo apt-<span class="hljs-keyword">get</span> install php7
</code></pre><p>Step 4. Install PHP Modules
 — — — — — — — — — — — — — -</p>
<pre><code> apt-<span class="hljs-keyword">cache</span> <span class="hljs-keyword">search</span> php- | <span class="hljs-keyword">less</span>

 apt-<span class="hljs-keyword">cache</span> <span class="hljs-keyword">show</span> package_name

 sudo apt-<span class="hljs-keyword">get</span> <span class="hljs-keyword">install</span> php7-*
</code></pre><p>遇到的问题：</p>
<p>— 重新设置 Mysql user/pwd.(解决方法见<a target="_blank" href="http://www.cnblogs.com/leolztang/p/5094930.html">链接2</a>)</p>
<p>— — — — — 
参考文章：
[1]. <a target="_blank" href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-ubuntu-16-04">How To Install Linux, Apache, MySQL, PHP (LAMP) stack on Ubuntu 16.04</a>
[2]. <a target="_blank" href="http://www.cnblogs.com/leolztang/p/5094930.html">MySQL ERROR 1698 (28000) 错误</a></p>
]]></content:encoded></item><item><title><![CDATA[AngularJS, Angular Material 与 Symfony 3 结合]]></title><description><![CDATA[首先需要在父级模版中引入 AngularJS 和 Angular Material.
<link rel="stylesheet" href="{{ asset('angular-material.css') }}">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:300,400,500,700,400italic">

<script src="{{ asset('angular...]]></description><link>https://blog.lbj.moe/symfony3-angular</link><guid isPermaLink="true">https://blog.lbj.moe/symfony3-angular</guid><dc:creator><![CDATA[Al Cheung]]></dc:creator><pubDate>Mon, 05 Mar 2018 14:37:12 GMT</pubDate><content:encoded><![CDATA[<p>首先需要在父级模版中引入 AngularJS 和 Angular Material.</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ asset('angular-material.css') }}"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css?family=RobotoDraft:300,400,500,700,400italic"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"{{ asset('angular.js') }}"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"{{ asset('angular-aria.js') }}"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"{{ asset('angular-animate.js') }}"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"{{ asset('angular-material.js') }}"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre><p>在js文件中,写入:</p>
<pre><code>angular.module(<span class="hljs-string">'myApp'</span>,[<span class="hljs-string">'ngMaterial'</span>])
    .config(**<span class="hljs-function"><span class="hljs-keyword">function</span>**(<span class="hljs-params">$interpolateProvider</span>)</span>{
        $interpolateProvider.startSymbol(<span class="hljs-string">'{[{'</span>).endSymbol(<span class="hljs-string">'}]}'</span>);
})
    .controller(<span class="hljs-string">'myController'</span>, myController);

myController.$inject = [<span class="hljs-string">'$scope'</span>];

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">myController</span>(<span class="hljs-params"> $scope </span>) </span>{

}
</code></pre><p>最后,在twig模版中写scope变量时,需要写成:</p>
<pre><code>{<span class="hljs-selector-attr">[{ scopeVariable }]</span>}
</code></pre><p>参考内容:</p>
<ul>
<li><a target="_blank" href="http://stackoverflow.com/questions/13671701/angularjs-twig-conflict-with-double-curly-braces">http://stackoverflow.com/questions/13671701/angularjs-twig-conflict-with-double-curly-braces</a></li>
</ul>
]]></content:encoded></item></channel></rss>