JS 脚本实战案例 0002

这个系列的第二篇来的比我预想的要早。

我挺喜欢这个系列的,不过类似的话似乎也说过很多次了。

那就直接开始正题吧。最近我写了一个很小项目,一个用纯前端技术来实现的短网址服务( https://r.izyx.xyz/#short )。我觉得这是一个很有趣的事情,因为它完全满足了我的需求,而且在技术上是十分简单的,并且它的实现成本极低,就特别适合个人用户。

这一篇我们就从原理的角度去讲解一下这个功能的实现。当然,并不是逐句的去讲解代码,而是讲解一些思路和主要的功能。你会发现我这里所讲的代码并不能够和项目中的代码完全对应,因为我们这个教程是面向初学者的,所以更多侧重理论层面的东西。

缘起:

我需要一个短网址服务,但并不是为了让网址变得更短。虽然在许多时候,它确实可以实现类似的效果。但想一想,我们多久没有正经的去输入网址,甚至多久没有正经的去观察过网址了呢?所以网址的长短真的不太重要了。

但是当我向大家分享一个项目的时候,我希望能够知道有多少人通过这个分享的渠道进行了访问,来方便我了解用户对这个项目的兴趣度,这是我以后做优化的数字依据。道理上,只要让用户经过一个我们可以控制的页面,并且在这个页面中加入任何一个第三方的网站统计代码就可以了。这样的服务遍地都是,每一个网站都会使用类似的功能。所以真的没有什么技术的门槛。

那么我们需要一个属于自己的页面,如果这是一个静态页面,我们可以很方便的把它托管在 github page 服务上,而且可以绑定自己的域名。

然后我们来说一说短网址的实现原理:

网站会记录一个对应关系——标记和目标网址。标记可能就是几个字,一般就是短网址后面那几个类似于乱码的字母,网站获得这个标记以后就查询自己这里记录的信息,找到对应的目标网址,然后跳转过去。

那么这些对应关系就是最核心的数据。正常情况下我们会用一个数据库来储存这些数据。当然这个数据库要放在服务器上,然后用一些动态语言去进行查询,这就是后端的一些服务。

但是我们前面说的 github page 并不支持数据库,也不支持动态语言。所以问题是我们能不能够用纯前端的方法去实现这个功能。

权衡:

纯前端实现跳转是完全没有问题的,这是最核心的了。

那么对应关系呢,当然也没有问题。但如果是使用上面所说的那种传统方法,用户凭着标记获得目标网址,其他的一概不知。但如果是纯前端,我们就要把所有的对应关系都放在前端页面上,也就是所有的数据都公开给了用户,虽然并不是直接的,但只要用户有心,必然可以获得。

这时候你就要反观自己的需求,究竟是安全更重要一些,还是简单的实现功能更重要一些。显然,像我这种只是希望分享几个项目。那么这些对应关系没有什么私密的,所以这个问题并不会对我造成阻碍。

然后还有一个性能的问题,为什么数据要放在数据库内,因为它足够专业,即便是数据量很大,它也能够稳定快速的去运行。但如果我们把所有的数据都写在代码之中,就变得不那么科学了。

但是我们不应该抛开剂量去谈毒性。在谈及性能的时候,也一定要考虑数量级。假设我们只有一条记录,就是只有一对对应关系。这时候当用户访问还去询问数据库,然后获得对应的网址进行跳转,就远不如直接把这条对应关系写在代码中来的快捷迅速。

所以如果只是很小数量的对应,那么写在代码中是完全可以接受的。我认为 1000 条以下的对应关系,写在代码中,对于性能并不会产生什么明显的影响。甚至以现在大家电脑的性能来看,3000 条记录也没有什么问题。这时候在反观我们的需求,只是为了日常分享一些项目,显然,需求远在这个数量级之下。

所以我觉得这个方法应该是可行的。

驻思:

上面所讲的东西似乎和技术关系不大,但是其实它是非常重要的。一个功能是可以被实现的,并不等于它是有用的。你可以做出一个轻飘飘的锤子来,那显然它是不能用来砸钉子。这时候你就要思考我为什么要做这样一个锤子,它有什么用,会不会有人有这个需求?如果你能够确切的解答这些问题,这可以保证这个锤子在做出来之后不会被直接丢进垃圾桶里。否则头脑一热,做一个毫无用途的东西,岂不是浪费生命?

虽然我们学习技术,但许多功夫其实在技术之外。技术只是工具,而用工具去做什么,这才是人与人之间的差异。

然后补充一下短网址的另外一个作用。前面我们说了对应关系,如果这个短网址是属于我们自己的,就意味着随时可以去修改这种对应关系。标记没有变化,但是对应的目标网址发生了变化。也就是说你以前分享出去的短地址,现在可以指向到完全不同的一个位置。

比如我建立了一个项目,然后到处分享,进行宣传。后来因为一些原因,我这个项目的地址发生了变化,这时候就会很尴尬。因为分享出去的网址是没有办法进行修改的。而如果分享的时候使用的是短地址,那么就可以让这个短地址重新指向到新的项目地址。

核心:

首先我们用一个对象去去储存对应关系:

const urls = {
    'appinn': 'https://www.appinn.com/',
    'meta': 'https://meta.appinn.net/'
}

这一点都不复杂,应该说是对于对象这种数据类型的最基本应用。前面是标记,后面是这个标记对应的网址。

现在假如我获取到了用户拿过来的标记 mark,我就可以去查询它对应的网址 urls[mark]。这个道理非常简单。

我们有几种方法在网址后面附加数据,比如锚点,就是网址中井号及其以后的部分;又比如查询参数,就是网址中问号及其以后的部分。

但是查询参数中,因为可以同时传递多个数据,所以需要给每一个数据命名,这样就比较长了。而我们这里只需要一个数据,所以采用锚点的方式会比较简短,而获取当前页面网址中的锚点可以使用如下方法:

const mark = window.location.hash

但这时候是带有井号的,所以我们要使用 replace 方法去删除掉最开始的井号。因为情况非常简单,所以即便不使用正则也是完全可以的。这里为了方便大家理解,我刻意的多增加了一对括号,其实可以不用加括号:

const mark = ( window.location.hash ).replace('#', '')

就是把前面这个值里面的井号替换成为没有任何内容,也就等于是删除掉了。当然如果本来就没有井号,那也就没有什么可以替换。所以保持原来的值不变。

这时候如果你是一个严谨的孩子,就应该测试一下。如果页面的网址中没有锚点,那么获取到的会是什么?(答案是获得的结果为一个空字符串)。

然后我们来认识一下页面网址对象:window.location,在它里面包含有当前页面网址的许多信息,比如上面所用到的属性就是获取其中的锚点部分。它也包含许多方法,能够让我们对网址进行一定的操作。

window.location.href

上面这个属性就是说完整的网址,我们可以通过它获取当前完整的网址,也可以通过给它赋值来修改当前的网址,也就实现了跳转。(这里说一下:前端网页进行网址跳转的方法有许多,这只是其中的一种)

组合:

差不多动手尝试一下,就可以把前面的知识组合起来了,然后就可以实现我们所需要的功能了。

const urls = {
    'appinn': 'https://www.appinn.com/',
    'meta': 'https://meta.appinn.net/'
}
const mark = ( window.location.hash ).replace('#', '')
window.location.href = urls[mark]

你看看,你看看,我算不算掰开揉碎的给你们讲呢?这里的每一句代码我在前面都已经讲了吧。

但是这样有一点点不严谨,如果使用的标记并不存在于我们所设定的数据之中,这时候我们会获得到一个怎样的值呢?——undefined

让网址变为未定义,你可以尝试一下,总之,和我们预期的不太一样。

又或者如果用户访问的时候,干脆就没有带锚点呢?当然也会像上面一样找不到对应的网址,结果得到 undefined

所以我们应该判断一下是否获得了一个有效的地址。如果没有,那么就不要做跳转了,而是用一定的方法去显示提示信息,所以一个相对完善的实现是下面这个样子:

const urls = {
    'appinn': 'https://www.appinn.com/',
    'meta': 'https://meta.appinn.net/'
}
const mark = ( window.location.hash ).replace('#', '')
const targetUrl = urls[mark]
if(targetUrl){
    window.location.href = targetUrl
}else{
    alert('哎呀,宝宝没有找到这个网址,不知道该怎么做了呢?')
}

总结:

一个非常简单的小功能,被小老鼠写了 3000 字,真的算是挺能水的了。

我们在实现一个功能之前,要对这个功能有充分的分析和思考,确定它的需求,确定它的优势和劣势。能够做到是一件很简单的事情,而只有在你对它充分了解之后,才有可能做好。而做好一件事情是很困难的。

在实现一个大的功能时,先把它拆分成许多小的功能,然后逐一实现。那么再复杂的事情也会变得简单。

要充分考虑到每一种可能性,用户并不会总是按着你的思路去使用这些产品。所以你必须把每一种可能性都认真的考虑到,否则就会有许多意料之外的情况发生。

技术是很简单的,但是用好技术是很困难的。需要很多的耐心,也需要非常的用心。


如果读完了记得请我喝杯咖啡:

老鼠爱发电

2 Likes