hexo已经几年没用过了,临时想用一下发现依赖版本已经落后太多,无法运行了,这篇博客主要是记录一下hexo的升级过程。

hexo升级

  1. 全局升级hexo-cli,先hexo version查看当前版本,然后npm install -g hexo-cli,再次hexo version查看是否升级成功。

    1
    2
    npm install -g hexo-cli
    hexo version
  2. 全局安装npm-check,用于检测是否有需要升级的依赖。安装成功后,可以使用npm-check -u命令,按照提示选择要升级的依赖。

    1
    2
    npm install -g npm-check
    npm-check -u

    image-20230703172612063

  3. 依赖成功升级后,发现项目并不能正常启动,提示_comfig.yml 中存在无效配置。原因是版本跨度太大,有些配置规范已经被修改了,需要按照官网最新的配置规则进行修改。

  4. _comfig.yml 修改后,hexo终于可以正常启动了,但是预览发现网页不能被正常解析(如下图),因为我之前使用的主题next也需要升级到最新版本才能适配。

    WX20230630-094239@2x

    主题升级文档地址:https://theme-next.js.org/docs/getting-started/upgrade.html

    第一步:将原来themes目录下的老主题目录进行备份;

    第二步:用npm install hexo-theme-next安装新的主题;

    第三步:对主题进行配置修改,将node_modules/hexo-theme-next/_config.yml文件拷贝到根目录,然后重命名为_config.next.yml,再将之前备份的主题配置还原到新的配置文件中。

    主题升级完成,启动后预览,发现主题的语言包不是中文,我检查了一下配置文件,之前设置的默认语言是language: zh-Hans,在新版的hexo中简体中文已经改成了zh-CN,与i18n标准保持一致了。

hexo部署

当我以为hexo已经全部升级完成时,部署又遇到了一些坑

  1. 首先就是运行 hexo d 抛出异常,提示Error: EACCES: permission denied, unlink ,一般Mac遇到这个问题,就是文件权限的问题,解决方案是删除.deploy_git目录,然后重新部署

    1
    2
    3
    4
    5
    6
    #删除项目根目录下的 .deploy_git
    sudo rm -rf .deploy_git

    hexo clean
    hexo g
    hexo d
  2. 之后运行hexo d 部署发现又失败了,原因是_config.yml 中的repo最好使用ssh地址,使用http地址就会有一定的失败概率;

    image-20230703192514771

    1
    2
    3
    4
    5
    6
    # Deployment
    ## Docs: https://hexo.io/docs/deployment.html
    deploy:
    type: git
    repo: git@github.com:rossroma/rossroma.github.io.git
    branch: master
  3. 最后hexo终于被推送到github了,发现里面的图片全部无法显示了,因为现在大部分网站都已经升级了https,已经无法加载http协议的图片了。由于我的博客搭建的比较早,那时候都还在用http协议的图片,无奈,还要把图片升级一下。

    我的图片都存储在七牛,如果有备案域名的话,可以免费申请一个https证书,过程比较简单,就不赘述了。

    我顺便还给Typora配置了图床,如图:

    image-20230703192942773

    用的是PicGo,配置比较简单,有中文的说明文档,感兴趣的可以去看一下:https://picgo.github.io/PicGo-Doc/。配置好之后,在Typora中插入图片会自动上传到七牛并将URL替换,用起来非常丝滑。

总结

至此,整个升级过程终于结束了,本来我以为只是简单的升级几个依赖,后面却发现耗费了我半天的时间。因此值得写篇文章,记录一下这个过程。

上一篇我们谈到了Object和Function的混乱关系,那么这一篇就来理一理。

在JavaScript中,所有对象都是由函数创建的,请看下面代码:

1
var obj = { key: "value"}

在如上代码中,obj 虽然是由字面量方式创建的,但它只是new Object()构建方式的语法糖,而Object是一个函数:typeof(object) // function。所以,对象是由函数创建的,下面我们一步步来解释。

每个函数function都有一个prototype属性,即原型。prototype本身是一个对象,有一个默认的属性constructor,属性值指向函数本身。而每个对象,都有一个隐式原型__proto__,值为创造该对象的function的prototype。为了证实这个理论,我们将用代码来验证一下:

1
2
3
4
5
6
7
> let obj = new Object()
> obj.__proto__ === Object.prototype
< true

> let obj2 = {}
> obj2.__proto__ === Object.prototype
< true

根据上面的代码,我们可以看出,不管是由哪种方式创建的对象,其__proto__都指向了Object的原型,所以,它们都是通过Object函数来创建的。

img

上面是一张大而全的关系图,我们试着捋一捋:首先,f1和f2是通过new Foo()的方式创建的,他们的__proto__指向了构造函数Foo.prototype,这个Foo.prototype也是一个对象,它的__proto__指向了Object.prototype,这说明了函数本身也是一个对象,而Object.prototype的__proto__的值为null,感觉像是到达了一个终点。

接着再捋一捋另一条路线,因为函数本身也是对象,所以函数也有自己的__proto__,如图,Object的__proto__指向了Function.prototype,说明Object是由Function创建的。而Function的__proto__指向了自己的prototype,这说明Function也是由Function创建的。

至此,大致理清了Object与Function的奇妙关系,所有对象都是由函数创建的,而函数本身,也是一种对象。

JavaScript中有很多让人迷惑的地方,比如有人说JavaScript中一切皆对象,但又有一个Object类型,还有下面的代码:

1
2
3
4
> null.prototype
< TypeError: null is not an object (evaluating 'null.prototype')
> typeof null
< "object"

当我们试图去访问null的原型时,被提示null不是一个对象,但我们使用typeof判断其类型时,得到的却是object。

我觉得可以将JavaScript中的所有类型划分为3类:

  • 第一类是null和undefined,这是两个比较特殊的类型,这里不做展开介绍;
  • 第二类是String、Number、Boolean和Symbol,它们可以通过对象的形式被创建,如Boolean(true), Number(3), String(9)等,这时它们为对象类型。但通常我们不会也不推荐这么做,我们会使用字面量的方式创建,如let isShow = true; let num = 45,这是它们属于基本类型,而非对象类型;
  • 第三类是我们通常所定义的Object类型,它们都是引用类型,原型都指向Object,有Object,Array,Function,Set,Map,RegExp,Date等。其中Object可以算作纯Object类型,根正苗红的那种,具体表现形式为一个大括号,里面包含了0或多个键值对,其他的可以算作是Object的子集。

接下来我们主要讲讲通常所定义的Object类型,即第三类,先看下面代码:

1
2
3
4
5
6
> typeof Object
< "function"
> typeof Array
< "function"
> typeof Date
< "function"

我们又惊奇的发现,这些所谓的Object,竟然是个function,那么它俩究竟是什么关系呢?其实,所有的引用类型都是通过构造函数来创建的,而构造函数的原型是Object。所以,如果你理解了下面的代码,你就能明白Object和Function的关系了:

1
2
3
4
5
6
> Object instanceof Function
< true
> Function instanceof Object
< true
> Object instanceof Object
< true

什么?还是感觉很混乱吗?那就要了解一下prototype了,请看下篇。

写代码时,通常要耗费大量的时间去排查bug产生的原因,如何快速的定位bug,在很大程度上决定了你的开发效率。下面是我的一些经验分享。

提高编码质量

在一个项目足够简单时,往往不会意识到编码规范和代码质量的重要性。当项目大到需要多人协作或是接收别人的代码时,你就会意识到,编码规范的统一、项目结构的设计、代码的易读性和规范的注释是多么重要。

提高编码质量不仅仅可以帮你快速的定位到bug,更能有效的减少bug数量。糟糕的代码就像堆在一起的杂物,而高质量的代码就像是分门别类的收纳箱,在找东西时 ,哪个效率更高是显而易见的。

当然,提高编码质量不是一蹴而就的,需要有扎实的基础知识,良好的编码规范,以及日积月累的犯错经验。之后可以专门写一篇文章,来介绍如何编写高质量的代码。

阅读报错信息

如果浏览器有输出错误信息,那么根据错误信息,我们就能大致判断出错误类型以及可能出错的原因。

比如:

  • 根据如下报错信息,我们看到,代码在运行时,试图去访问某一对象的attr属性,而该对象并没有被定义。这时候,我们只要在出错的页面去检索关键词attr,输出attr所依赖的对象,就知道错误出在哪儿了
1
Uncaught TypeError: Cannot read property 'attr' of undefined
  • map是数组才有的方法,这里可以预见的是,a并不是一个数组,结果就是 a 没有达到我们预期的格式
1
Uncaught TypeError: a.map is not a function
  • js代码格式错误,通常ESLint会输出报错,检查一下引号、逗号、小括号、大括号是否写完整
1
Uncaught SyntaxError: Invalid or unexpected token
  • 有时候,即便是看懂了报错信息,也不清楚自己的代码到底出了什么问题。你可以把报错信息复制下来,去stackoverflow,segmentfault等专业问答网站去检索一下,如果找到不到合理的答案,还可以去google或百度去搜。要相信,总有人会和你遇到过类似的问题。

善用console

有时候,代码不能以期望的方式运行,但又没有输出任何有效的错误信息,我们可以使用console方法,将必要的信息打印出来。

如何使用?

比如,但我们点击提交按钮时,并没有发起提交请求。这时,我们要查看源码,找出从点击提交按钮一直到发起请求中间,都执行了哪些步骤。并将关键步骤处的关键信息输出,比如if else的判断条件、被遍历的数组和对象,以及可能中断执行的return语句。

注意事项

在使用console输出时,建议带上关键词,且不建议使用无意义的数字、字母等:

1
2
3
4
5
6
7
// good
console.log('attrValue', this.attrValue)
console.log('getModule方法的判断条件', isShow)

// bad
console.log(666, this.attrValue)
console.log(isShow)

另外,在错误排除后,建议删除不再使用的console调试代码,避免在查找其他错误时,造成不必要的干扰。

如果输出的是对象或数组,有可能得到不准确的结果

1
2
3
4
5
let obj = {
key: 1234
}
console.log(obj) // {key: 555}
obj.key = 555

打断点

Chrome的开发者工具有一个很好用的代码调试工具,就是Breakpoints(断点),与console不同的是,它会在断点处终止程序的执行,可以方便的看到代码在哪一步产生了问题。

相比在代码里写debugger打断点的方式,Chrome的调试工具即便捷,又不产生副作用,所以不推荐在代码里写debugger的方式来调试。

更多关于Chrome开发者工具的介绍与使用信息,可移步:chrome开发者工具的使用

排除法

这个方法之前明明是正常的,我并没有改动它,只是在其他地方加了一些代码,它就不正常了。

怎么办?假使页面中一共有abcd四个方法,我们逐次注释掉a, b , c , d,每注释掉一个就运行一次代码,如果在注释掉d以后,代码运行正常了,那么问题就出在d身上。

剥离法

如果页面的代码量很大,用排除法效率可能会很低,这时候我们可以试试剥离法。

同样一个方法(组件),在A页面运行没有问题,放到B页面就不行。这时候,可以建一个新的页面,里面只放这一个组件,然后运行,如果没有异常,首先可以排除,不是这个组件的问题。接下来,把B页面可能对该方法(组件)造成影响的条件,逐次添加进来,看看到底是哪个条件造成了异常。

线上故障排除

有时候线上代码出了故障,而本地又无法重现,由于线上代码不能使用console,并且上线代码都经过了压缩混淆,导致很难调试。

这时候可以试试sourceMap,在webpack的配置文件中开启sourceMap,在打包的时候会生成对应的.map文件。然后将打包过的文件整体上传到服务器,这样调试线上代码时,除了压缩混淆过的代码,还会有一份原始代码,方便打断点和阅读代码。

不过这样做也会有一些缺点,一是打包时间会变长,二是将原生代码暴露出去。所以,平时建议关闭sourceMap功能,只有在修复无法在本地重现的线上bug时再使用。而且,在修复完毕后,重新打一个没有sourceMap的包,上传到服务器,避免本地代码泄露。

关于sourceMap的更多介绍可以移步以下几个链接:

JavaScript Source Map 详解

打破砂锅问到底:详解Webpack中的sourcemap

在平时写代码时,经常会对两个值做是否相等的比较,这看上去是非常简单,不值一提的事情,但有一些小细节,一定要注意。

等于(宽松相等) ==

相等操作符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。在转换后(等式的一边或两边都可能被转换),最终的比较方式等同于全等操作符 === 的比较方式。

转换规则如下:

  • 如果其中一个操作数的类型为 Boolean ,那么,首先将它转换为数字类型,false 转换为 0, true 将转换为 1。
  • 如果其中一个操作数的类型是字符串,另外一个为数字类型,那么,将字符串转换为数字进行比较。
  • 如果其中一个操作数的类型是字符串,另外一个为 object 类型,那么,将调用对象的 toString 方法之后,比较字符串。
  • 如果其中一个操作数的类型是数字类型,另外一个为 object 类型,那么,将对象转换为数值后进行数字比较。

以下规定了一些特殊的比较:

  • null 和 undefined 是相等的。
  • null 和 undefined 不会转换为任何其他类型
  • 如果任何一个操作的结果为 NaN,那么相等比较返回 false,不相等比较返回 true。注意,即使两个操作数都为 NaN,返回结果一样是 false,也就是说,NaN 不等于 NaN。
  • 如果两个操作数都是对象,那么比较它们引用的值,如果引用同一个对象,那么,返回真,否则,返回假。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// DEMO

null == undefined
=> true
false == 0
=> true
true == 1
=> true
0 == new String("0")
=> true
undefined == false
=> false
null == false
=> false
NaN == NaN
=> false

全等(严格相等) ===

全等操作符比较两个值是否相等,两个被比较的值在比较前都不进行隐式转换。如果两个被比较的值具有不同的类型,这两个值是不全等的。

  • 两个被比较的值类型相同,值也相同,并且都不是 number 类型时,两个值全等
  • 两个值都是 number 类型,当两个都不是 NaN,并且数值相同,或是两个值分别为 +0 和 -0 时,两个值被认为是全等的
1
2
3
4
5
6
7
8
9
10
11
12
// DEMO

0 === "0"
=> false
false === "false"
=> false
null === undefined
=> false
+0 === -0
=> true
1 === 1.0
=> true

看完上面的介绍,是否对 “等于” 和 “全等” 有了更深入的理解呢?如果还是担心自己掌握的不够牢固,那建议你彻底抛弃“等于”,因为“全等”操作符的结果更容易预测,并且因为没有隐式转换,全等比较的操作会更快。

参考链接

我有一部kindle,如何才能快速把书导入,轻松愉快的阅读呢?嗯… 一般有三种方法,接下来我就为你一一介绍。

直接在kindle上购买

在kindle首页的左上方,有一个商店按钮,点进去就是Amazon的电子书商城。你可以在其中找到任何你想要的电子书,直接购买。kindle支持信用卡和支付宝两种支付方式。支付宝需要借助手机或电脑来完成支付。假如你之前有绑定过信用卡,这无疑是最便捷的支付方式,直接选择你绑定的信用卡然后点击下单就完成支付了。

从电脑中拷贝

假如Amazon上并没有你想要的电子书,而恰好你的电脑中有,那么你可以用数据线把你的kindle和电脑连接,这时电脑中就会多了一个kindle盘符,我们打开这个盘符会发现有很多的文件夹,其中有一个Documents文件夹是用来存放电子书的,只要把电子书拷贝到这个目录就行了。不过要注意你拷贝进来的电子书,必须是kindle所支持的格式,比如.awz3/.mobi/.pdf等。

通过Email,远程发送到kindle中

很多时候,我们想把一本书存到kindle中,却发现kindle并不在身边,怎么办呢?别担心,一样可以搞定。

打开Amazon网站,登入和kindle所绑定相同的账户,进入到我的账户——内容和设备——设置页面。拉到页面的最下方,会发现有一个“已认可的发件人电子邮箱列表”,然后点击“添加认可的电子邮箱”,将我们用来发送邮件的邮箱账号填入。只有被正确填入的邮箱账号,才能给kindle发送内容。之后在“〖发送至Kindle〗电子邮箱”中找到kindle被分配的邮箱地址,一般以@kindle.cn结尾,复制这个邮箱地址作为收件人。将要发送的电子书以附件的形式添加到邮件中发送出去,很快,我们的kindle中就会同步这本电子书了。

kindle虽然支持不少文档格式,但自家的.awz3无疑是体验最好的,如何将其他格式转为.awz3呢?只要在邮件的主题中输入Convert,附件中的.pdf/.mobi,甚至是Word文档,通通会自动转为.awz3再存入到kindle中。唯一的一个缺憾是,.epub格式不能被支持,这里推荐一个在线工具,可以将多种文档格式转为.awz3,包括.epub。https://convertio.co

批量删除

有时候,我想批量删除kindle中的电子书或订阅内容,在kindle中逐个操作实在是太繁琐了,有没有便捷的方法呢?

当然有,还是打开Amazon网站,进入到我的账户——内容和设备——我的内容页面,在这里批量选中你要删除的内容,然后点击删除就可以了。kindle会自动同步这些操作,一并删除。

错误的方法:将kindle用数据线链接至电脑,直接删除Documents中的文件,这样的操作是无效的,kindle会自动同步Amazon云盘中的内容,将被删除文件同步回来。

怎么样?这些方法你都掌握了吗?

门前的池塘里,有一只大白鹅。长长的脖颈,雪白的羽毛,一双船桨一样的脚掌自如地拨动着水面。它是那样的美,就像天鹅一般。它能像天鹅一样飞翔吗?

黄昏,池塘里洒满了金色。

大白鹅游上了岸,它抖了抖翅膀,高傲的扬起它的脖颈,大摇大摆的走在路上。

它能像天鹅一样飞翔吗?

我向它追过去,它察觉到我,开始挥动着翅膀,向前奔跑。我跑得愈发快了,它也加快了挥动翅膀的频率,那双巨大而有力的翅膀,将地上厚厚的尘土掀起。两只脚掌如蜻蜓点水一般,刚刚落地就迅速弹起。它越来越快,突然,它的双脚离地了,就像助跑了一段的飞机,倏的起飞了。它用力煽动着翅膀,跨过一段长长的矮篱笆,落在了另一头。

我高兴极了!

终有一日,它会飞过那口池塘、飞过一座座屋顶、飞过那片绿油油的麦田,然后消失在橘色夕阳里。

它能向天鹅一样飞翔。

突然心血来潮想写博客了。

记得第一次写博客,大概是2010年,用的也是这个域名。那时候还是蛮兴奋的,自己买了虚拟主机,注册了域名,用了WordPress博客程序,选了自己喜欢的主题,信誓旦旦的想要把它好好经营下去。大概半年光景,我发现我的兴趣在于折腾博客程序和主题,并不在写作上,在那之后,这个博客就没再更新了。两年后,域名到期,我没有续租,我的第一个博客站点就此消亡了。

又过了一年,我可能又有些空了,注册了新域名,又搞起了WordPress了,这次和前一次一样,两年后关站。

去年双十一,阿里云也搞起了五折活动,我就购入了一台入门级云服务器,打算用来跑一些自己做的小应用,顺便把rossroma.com这个域名,重新注册了回来。那段时间,我正打算换个工作,于是就想做一个小应用来练练手,顺便也可以用作面试作品。于是我想到了我多年前的一个想法——“看图猜电影”。

在开发这个小应用的期间,我接触到了不少新的知识和技能,这其中就有HEXO。它基于nodejs,没有传统的图形管理界面,用Markdown来书写,用Git来发布,这对我来说很新鲜,也很酷。于是,我的第三个博客站点就此诞生了。

这一次呢,我也不给自己订什么写作目标了,想写就写,反正也不会有人来看,哈哈哈。就当做一个公开的记事本吧。

写作,是一件很有意义的事情。记得小学的时候,老师经常跟我们讲“好记性不然烂笔头”,我当时是持反对意见的。我觉得老师讲的知识都太简单,我听一遍就会了,根本就不需要写下来,而且知识重要的是理解而不是死记硬背。一晃20年过去了,我又想起了这句话,我觉得这句话说对了一半。“写”下来,固然会加深记忆,但更为重要的是,在写之前,你要先组织好语言。我们在讲话聊天的时候,往往不需要过多的思考,张口就来。而写作就不一样了,你要思考,要有一个编译的过程,即便是写出来了,还要再反复推敲,修修改改。这样一个思考总结的过程,我想就是写作最大的意义所在吧。

0%