我的技术和生活

  • 迎接 Vue 3.0

    迎接 Vue 3.0

    思否(SegmentFault)去年引入了新的运营合伙人,大张旗鼓,计划重新梳理视频教程体系。第一个举措就是推出新系列:思否编程公开课。这个系列的优势在于结合社区运营的知识与一线讲师的实战能力:即社区寻找大家关心的话题,请相关领域的讲师来讲解,达到受众明确、内容扎实的目标。

    很高兴能够得到邀请,做了一期《迎接 Vue 3.0》,我也趁机深入了解了一把 Vue 3.0。

    Vue 3.0 原计划 2019 年发布,但是开发过程有些波折,一些早期的设计被放弃了,因为不够好;引入了新的更好的设计,于是延期至今,仍然是 alpha 版。在新的设计下,Vue 3.0 更快,快到不需要时间切片;而且还能基本保持与 2.x 版本的兼容。

    至于新增的 Compisition API,其实是选修课,类似 Async 之于 Promise。有了它,我们能更好的复用代码、维护代码;不用它,也不耽误我们享受其它升级带来的便利。

    其实看过很多 Vue 3.0 的东西之后,我越发感激开发团队为这个框架做得一切,越发感激开源文化为我们带来的新世界。

    我备课一向认真充分,所以讲课的内容应该是现在最新最全面的,欢迎大家围观:【思否编程公开课】迎接Vue 3.0

    (更多…)
  • 给 Markdown 里的图片增加样式

    给 Markdown 里的图片增加样式

    在 markdown 里添加图片很容易,用这个语法即可:

    ![alt](/path/to/image "title")

    但是如果图片需要一些特殊样式,就不太好搞。比如前两天,老板觉得博文中的二维码太大,不好看,让我改小点。我一开始只知道可以用 HTML 来做,因为 Markdown 内建支持所有 HTML,但总觉得不够优雅,尤其是,不知道怎么要求将来写博客的人都用 HTML 来写。

    于是 Google 之,找到这篇文章:How to Style Images With Markdown,写得非常好,列举了很多添加样式的方法,尤其是使用 #hash + CSS 选择器的方法,很有想象力,推荐给大家。

    首先,在 Markdown 里,给图片的 URL 添加 hash。这个动作并不会造成任何实质性的影响。

    ![alt](/path/to/image#thumbnail)

    然后,在 CSS 里,定义“src 里包含字符串 #thumbnail”的规则即可:

    img[src*="#thumbnail"] {
      max-width: 10rem;
    }

    [attr*=val] 选择器的意思是 attr 属性里包含 val 字符串。这里用 [src$="#thumbnail"] 效果是一样的。如果你想了解所有 CSS 属性选择器,还是推荐看 MDN 文档

  • 2020 年计划

    2020 年计划

    今年真的是一言难尽……2019年年底的时候,大家常半开玩笑的说,虽然2019年可能不太好,但有可能是接下来十年里最好的一年。没想到一语成谶,2020 年从各方面来说,都在脱离大家期望的轨道,向着混乱的深渊奔去。

    突入其来的冠状病毒穷凶极恶,它不在乎你是谁,也不在乎你在哪儿,更不在乎你想干什么。它就是快快的、稳稳的传播,把疾病和恐慌快速散播到全世界。

    然后,就过了可能是几十年来最寡淡的春节。能够不出问题,大家就满足了。希望疫病能够尽快了结,大家的生活能够尽快恢复正常。无论如何,新年计划该做还得做。

    (更多…)
  • 瞎猜疫情结束后

    瞎猜疫情结束后

    我的几个预测,如果蒙对,纯属巧合。

    (更多…)
  • 解决 Raspberry Pi 4 安装 php-mbstring/php-curl 的问题

    解决 Raspberry Pi 4 安装 php-mbstring/php-curl 的问题

    最近要在 flarum 上做二次开发,尝试直接用 php -S localhost:8080 未果,于是打算在树莓派上搭个开发环境,省得它整日落灰。

    因为在本地创建过仓库,所以这次直接从 GitHub clone 项目下来,然后打算执行 composer install 安装依赖。结果提示差了 php-mbstring(解决汉字等多字节字符)和 php-curl(用于远程请求)两个模块。然后我就打算用 apt install php-curl 安装模块,没想到失败了,仔细看错误信息,因为这个模块依赖 libcurl3,但是系统里是 libcurl4,所以不行。

    那就安装 libcurl3 呗,结果系统认为明显 libcurl4 更新,不给装 3,哪怕删了重装都不行。

    后来查了半天,找到答案。原来我添加的源是 stretch 的,也就是面向 Debian 9 的;而 Raspberry Pi 的系统是基于 Debian 10,也就是 buster 的,所以依赖处理上,两方面就冲突了。

    这个时候,需要用 sh -c 'echo "deb https://packages.sury.org/php/ buster main" > /etc/apt/sources.list.d/php.list' 把属于“buster”的源添加进系统,接着删掉之前 stretch 的源,然后 apt update 之后,就可以正常安装了。


    参考链接:https://github.com/oerdnj/deb.sury.org/issues/1193

  • 笔记:七牛云续费免费证书

    笔记:七牛云续费免费证书

    这是一篇笔记,没啥技术含量。

    七牛云有一定的免费额度,对于我这种技术博客来说,可用量充足,所以我很早就开始使用他们家的服务。我启用了二级域名 qiniu.meathill.com,用来存放所有静态资源。

    主站 HTTPS 之后,如果加载非 HTTPS 资源,也会报错。所以需要在七牛也开启 HTTPS。这就需要 SSL 证书。七牛提供免费的 TrustAsia 证书,但是一次只能买一年份,不能自动续期,每次换证书都要折腾好久,所以简单写个笔记记一下。

    (更多…)
  • 利用 Web Speech API 实现语音阅读试题(TTS)

    利用 Web Speech API 实现语音阅读试题(TTS)

    孩子上小学一年级,寒假作业里有一项:每天做20个20以内的加减法。这个作业老师不直接布置,而是让家长负责,方式任意。那么,显而易见,这个工作就由我负责。然后我就顺手抄起 Vue 做了一个 Web App:http://mui.evereditor.com,源代码在:https://github.com/meathill/mui-teacher

    然后老婆说,别的教学应用都会把题目念出来,这样比较有上课的感觉,问我能不能也把题目念出来。我之前大略有些印象,可以直接调用浏览器的原生 API 实现 TTS(text to speech),于是就想试试看吧。

    使用“Web speech API”作为关键词,很容易找到 MDN 上的这个页面,继而了解到 SpeechSynthesisUtterance 这个类,接下来就简单了,直接参考 Using the Web Speech API 里的 Demo,就完成了下面代码:

    doRead(index) {
      const content = this.$refs.line[index].textContent;
      const msg = new SpeechSynthesisUtterance(content.replace('-', '减'));
      speechSynthesis.speak(msg);
    },

    这里有三个注意事项:

    1. 系统会把 - 读成“至”,但我这里是加减的“减”,所以我要手动把它替换一下
    2. 从某个版本开始,发音必须由用户主动触发,即放在交互性事件里,不能在页面打开时自动读
    3. 发音效果跟浏览器有关,目前 Chrome 和 Safari 效果比较好

    这样的 TTS,对于整段文字来说,效果一般,但是读一般的算式足够了,小朋友也挺喜欢。这个 API 还可以用作语音输入,不过考虑到模型的效果,没有尝试,想真正可用的话,还是用那些高级的在线版本吧。


    图文无关,想出去玩……

  • PHP built-in web server 支持自动查找入口

    PHP built-in web server 支持自动查找入口

    使用 php -s localhost:8080 可以快速启动一个开发服务器,非常方便,是我现在需要简单服务器支持时的首选。

    不过我最初了解到这个功能的时候,它(可能)还不支持请求重写,也就是说,我们访问 /foo/bar,它就会去当前目录里查找 /foo/bar,找不到就 404。如果想要实现 index.php 重定向,必须手动编写路由文件,比较麻烦。我宁可用 nginx 实现,因为部署上线的时候早晚要用。

    最近偶然发现,PHP built-in web server 已经支持请求重写了,如果命中,就会直接返回目标文件;如果没有命中,就会沿着目录往上找,直到找到 index.php 或者 index.html,或者到启动服务器的根目录,然后把请求地址放在 $_SERVER['PATH_INFO'] 里,留待 php 处理。

    这样一来,无论是 WordPress,还是 Laravel,还是其它基于路由的单一入口项目,都可以直接使用 PHP built-in web server 开发了,简单方便快捷。甚至连纯前端项目,如果你不熟悉服务器端的配置,也可以简单的安装一个 PHP 来实现。

    比如,在本地开发 WordPress,可以这样:

    # 安装 php 和 mysql
    brew install php
    brew install mysql
    
    # 配置 mysql root 用户密码,替换下面的 `NEWPASS` 
    $(brew --prefix mysql)/bin/mysqladmin -u root password NEWPASS
    
    # 下载并解压 wordpress.zip,进入目录,启动服务器
    php -S localhost:8080
    
    # 完成!

    参考文档:

    PHP manual: Built-in web server

  • 再见科比

    再见科比

    小时候,受大我4岁的表哥影响,开始看 NBA。当时应该是乔丹第一次退役复出,我印象最深的是他的后仰式跳投和各种总决赛。

    乔丹是公认的王者。乔丹二次退役之后,大家开始为他寻找接班人,当时呼声最高的是希尔、哈达威和科比。前两位看起来都比科比合适,更有风度,更儒雅随和。

    结果大家都知道了,希尔、哈达威都只有灵光一现,只有科比所在的湖人成就三连冠霸业——但是这里面有多少应该归功奥尼尔,到现在也争不出个定论。

    我当时也不喜欢科比,主要是觉得他目无尊长,让乔丹在最后一次全明星抱憾而归。接下来,乔丹彻底退役,我对 NBA 的兴趣也下降了,进入云球迷阶段。不过当时 NBA 也真不好看,原因有三:

    1. 没有统治级球队,没有传奇
    2. 马刺和活塞携手带来泥潭摔跤模式……
    3. 姚明加入 NBA,火箭队成为中国球迷的大主队,常规赛几乎只播火箭队;但是火箭队季后赛都是一轮游,对于云球迷来说,往后面看都不知道谁在打谁……

    这种情况持续到 2008 年奥运会,科比来到中国,显示出巨大的影响力,让我感到震惊。同年,加索尔加盟湖人,帮助科比三进总决赛,并且夺下两冠。

    时隔多年,我惊讶的发现,自己从一代科黑,生生的被改造成科粉。

    以前觉得科比喜欢浪投,喜欢打英雄球;现在觉得,那是科比自信和负责任的表现。

    以前觉得科比妨碍了乔丹最后一次表演;现在觉得,职业赛场上,全力以赴也是一种尊重。(感谢德国 8:0 沙特,7:1 巴西)

    以前崇拜超级英雄,梦想成为奥尼尔、詹姆斯这种身体素质惊人的超人;现在工作了,发现自己更可能是芸芸众生中的一员,开始喜欢科比这样兢兢业业全力以赴的斗争精神。

    另外,互联网的发展让信息传播得更快更多更好,我们也可以看到更多“非”比赛的信息。比如科比手指骨折简单处理一下就上场,跟腱断裂后单脚罚球,等等。让人不得不佩服这位硬汉。

    然而我怎么也没想到,他的生命会在今天早上戛然而止,以这样一个无厘头的方式告别世界——乔丹、奥尼尔、甚至詹姆斯,你都可以想象他们无厘头的样子,唯独科比,我脑海里只有他咬碎钢牙战战战战到底的样子……还带着他最会打篮球的女儿——那个前几个月才以 115-27 狂胜对手而被大家熟知的女儿,我都不敢想象在人生的最后时刻,他们爷儿俩是怎么过的……

    世事无常,且行且珍惜。今天起开始复工,以纪念科比。

    https://www.youtube.com/watch?v=15y5O3W8SGw
  • 配置 nginx 支持目录别名

    配置 nginx 支持目录别名

    我有一个小项目,我们假定它叫 up,是用 PHP 写的私人图床,部署在服务器上,域名是 up.meathill.com。因为是 PHP,不需要编译,直接 `git clone` 仓库然后配置 nginx 指过去就好。

    后来想做一个在线预览的功能,是个单页应用,也放在这个仓库里,目录是 `up/fe`,项目用 Vue + Webpack 来做,源码放在 `up/fe/src`,生成的文件放在 `up/fe/dist`。

    这样一来,我就需要在原本的 nginx 里配置一个虚拟目录 `/admin`,指向生成的文件。预览的文件路径就是 `/admin/${file}`,所以我还要把未命中的文件重定向到 `index.html

    经过反复摸索,最终的配置如下:

    {
            location / {
                    # 未直接命中的请求都交给根目录里的 index.php 处理
                    try_files $uri $uri/ /index.php$args;
            }
    
            # 默认的 nginx 1.14 + PHP 7.1 + php-fpm 配置
            location ~ .php$ {
                    include snippets/fastcgi-php.conf;
                    fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
            }
    
            # /admin 的访问指向编译后的前端资源
            location /admin {
                    alias /mnt/www/uploader/fe/dist;
            }
    
            # 直接访问的地址解析,正则其实不必要
            location ~ "/admin/(?<page>d{8}-d{6})$" {
                    alias /mnt/www/uploader/fe/dist/index.html;
                    # 这里的定义很重要,默认是 application/octet-stream 会启动下载
                    default_type text/html;
            }
            
            # 把不希望看到的请求屏蔽掉
            location /fe {
                    return 404;
            }
    }