另一页

Hexo的Highlight问题

highlight不能在主题中配置

Aria2+NextCloud离线下载配置实践

用Aria2在我的vps上实现离线下载,用NextCloud做网盘。

前提条件

一台运行Ubuntu的VPS,默认你会基本命令和vim。此处不介绍如何安装Apache,PHP等需要的环境,可手动可面板安装。

1.安装Aria2

首先到aria2官网找文档,发现只提供源码给你编译,我不想那么麻烦,用关键字ubuntu+aria2搜索,发现软件源有编译好的aria2,直接安装。

sudo apt-get install aria2

aria2c -h可以查看命令行程序使用说明。

2.安装AriaNg(UI界面)

然后安装web界面,推荐AriaNg。界面不需要和后端位于同一服务器,因为他们用rpc而不是别的方式通信。只需要下载后解压打开index.html,或者直接访问在线服务如我的AriaNg

3.配置Aria2(RPC部分)

aria2c允许直接附加命令行参数作为配置,但为了方便,还是改配置文件。配置文件有多处,以~/.aria2/aria2/conf为例,用vim做以下配置(我们的目的是启动rpc服务以便UI调用):

enable-rpc=true
rpc-allow-origin-all=true
rpc-listen-all=true
rpc-secret=【自定义密钥】

以上选项允许我们从任何地方使用Aria2,第二行解决跨域问题,第三行允许响应外部请求。

然后启动aria2c。需要自定义下载路径可以设置dir=[PATH]

4.配置AriaNg

在Aria2基本设置中,填写服务器地址与密钥,端口默认是6800(如果安全策略不开放该端口需要到面板或防火墙改写规则)。

之后可以正常使用下载功能。

5.配置Aria2(后台启动)

Ubuntu的启动配置也有多处,这里以/etc/rc.local为例,在其exit 0之前添加想要运行的脚本。这里可以添加一行aria2c &

6.安装NextCloud

下载压缩包后解压到网站目录,根据提示安装。

根据官方manual,在/etc/apache2/sites-availabel/nextcloud.conf做以下修改(路径替换成自己的):

Alias /nextcloud "/var/www/nextcloud/"

<Directory /var/www/nextcloud/>
  Require all granted
  AllowOverride All
  Options FollowSymLinks MultiViews

  <IfModule mod_dav.c>
    Dav off
  </IfModule>

</Directory>

然后使配置文件生效a2ensite nextcloud.conf。这里的配置有些晦涩,除了运维应该都不清楚,简单表述一下,allowoverride是启用.htaccess的重写,multiviews是apache提供的自动补全/查找扩展名并返回文件的功能。可以想象index.php?abc能够通过重写地址转换成abc,同时访问home.php可以直接访问home。

之后启用External storage support,添加Aria2的下载路径,以便访问离线下载目录。

7.配置Aria2(解决权限问题)

systemd允许我们创建自定义service,需要在/lib/systemd/system下创建aria2.service(名字可以改)

[Unit]
Description=Aria2Service

[Service]
ExecStart=/usr/bin/aria2c --conf-path=/www/aria2.conf
User=www

[Install]
WantedBy=default.target

解释一下为什么这里指定了不同的配置文件,并且用www用户执行命令。终端中启动aria2下载的文件权限默认是644,属于root(或你登陆的用户),这导致nextcloud(php的user也是www)无法删除下载的文件。之前的配置文件在root目录下,为了使配置文件意义清晰,就搬到www目录。

8.替代方案:ES文件浏览器+FTP

如果不想配置NextCloud,也可以安装FTP Server,在移动端用ES文件浏览器访问服务器,它还支持在线播放视频。

Hooks与视图更新

用useState管理的状态,不建议使用对象类型,因为传入同一个对象不会触发更新。而在class组件中,setState默认一定会触发更新。

环境变量配置指南

因为安装grunt-cli后发现grunt命令不能使用,很明显没有配置环境变量。(grunt-cli也有点奇怪,yarn、vue-cli、webpack-cli还能正常用)

mac默认终端是zsh,其配置文件包括~/.bash_profile ~/.bash_rc ~/.profile 等。

编辑其中任何一个文件的内容,加上PATH=$PATH:/usr/bin/node/… 其中:作用是拼接,意味着path不会覆盖掉其他path的值。

Hexo博客主题开发指南

之前一直用Wordpress,从实用性角度讲,WP没有任何替代品。但我不想为了开发WP主题而去了解它的细节。因此选择了Hexo并期望从0开始完成一套主题。

知识储备

需要掌握的知识

HTML/CSS,部分需求涉及JS

可以快速学习的知识

Hexo安装及使用,EJS模板语法

开发准备

观察默认主题结构

可以看到,主题文件夹下有layout、scripts、source、languages、_config.yml,分别是模板、JS、静态资源、翻译、配置文件。
即使主题目录为空,Hexo server也不会报错,因此我们可以从空文件夹开始。

开发过程

第一张页面

在layout下新建layout.ejs,内容可以是任意字符串。此时访问主页可以看到刚才输入的内容。现在将内容替换为<%- body %>,我们可以看到文章。在此基础上修改页面。

主页和文章页

自定义页面

评论

Context使用的一系列问题

Context API是React16推出的一种简单的组件间状态共享方式。在外层创建Context并用Provider包裹内层,在内层可以通过useContext或this.context访问数据。不需要通过props层层传递数据和回调函数,也避免引入Redux和更多复杂度。

遇到的问题:

A. 如果使用函数式组件并且将对象作为context,有可能不会触发更新。这种情况可以考虑给Provider传拷贝的对象而不只是一个引用。也可以改用class组件,因为其可以调用setState与forceUpdate方法。

B. 用Provider组件包裹下层class组件后,下层组件获取到的context为空。发现是我的理解错了,this.context不能自动获取,而需要先指定contextType。(useContext不用)

C. ReferenceError: Cannot access 'MyContext' before initialization

我一定是先创建context再引用,那么为什么会报错呢?我想到了import的问题。我在index.ts里引用app.ts,那么应该是app.ts先加载。这个报错给我上了一课,我才会去想JS如何去执行import。React要求contextType必须是static的,所以在import的时候就会执行,但我初始化MyContext的语句却写在index.ts里面(也就是import后面),导致import的时候读不到。如果我只在constructor里使用MyContext,就不会报这个错,因为constructor执行的时机更晚。React.render()渲染组件时,constructor才会真正触发(对象才真正被创建),那个时候import已经结束了。

D.context做修改后,子组件只有下次渲染才会访问新的context。也就是说不能在js中做更改后立即访问到修改,setTimeout也不行,因为context的引用变了。

hexo不能使用ftpsync

已经有人给出了分析,jsftp有问题并且没有人修,我选择不用这种方式同步了。

我比较倾向的替代方式,手动上传、sftp同步、CI、Rsync,我都接受但是CI之前还没有尝试过,比较有趣。

安装flow.ci

Git意外提交大文件如何处理

Git作为版本管理系统,仓库区自然保存有历史提交与历史文件。但远程Git仓库默认不提供LFS服务,如果不慎提交了大文件(像我提交了electron打包后的应用),远程仓库会拒绝同步。

因为Git是版本管理系统,所以删除文件不会影响仓库中的历史提交,修改历史提交有两种方法:

filter-branch

branch-filter对历史记录commit重新应用指定操作,如git filter-branch --tree-filter 'rm -f xxx.yyy' HEAD,将对第一个commit到HEAD应用。

tree-filter和index-filter都可以实现相同的目的,区别在于tree-filter将每一个commit checkout到临时文件夹并允许自定义命令,index-filter将commit复制到index(暂存区)并应用自定义命令,因此index-filter更快。

rebase

最早听说rebase是用于合并冲突,它会将其中一分支的记录应用到rebase参数的分支上,两分支的commit记录一前一后,按顺序解决冲突。这里rebase有另一种用法,即rebase -i,它会提供一个可编辑的commit记录,支持修改commit记录(合并commit、对commit执行命令、编辑commit信息)。比如说提交大文件后,我们可以合并提交大文件与删除大文件的记录,合并后历史记录中不再留下该文件的痕迹

Promise限流

function* getPromise() {
    while (true) {
        let r = Math.random() * 1000
        yield new Promise((resolve) => {
            setTimeout(() => { resolve() }, r)
        }).then(() => { console.log('fin in ' + r, promiseCount, ++count, allPromise) })
    }
}

let getP = getPromise()
let promiseCount = 0
const maxCount = 10
let allPromise = 100
let count = 0

function sendNextPromise() {

    if (promiseCount < maxCount) {
        promiseCount++
        allPromise--
        getP.next().value.then(() => {
            promiseCount--

            if (allPromise > 0) { sendNextPromise() }

        })

    }

}

for (let i = 0; i < 100; i++) {

    sendNextPromise()
}

不方便的TS报错

场景1

我想访问document.activeElement的属性,但ts提示我该类型上不存在那个属性。这简直是在扯淡,Element类型的属性不应该包括any吗?我向dom元素上添加了标记去哪里修改类型定义?我只能let document:any=window.document再访问activeElement的属性,真的很不方便。

12