想我第一次接触做网页的时候,还是我初中时候看到信息课教材上用dw做一些简单的静态网页。随后因为许许多多的原因,逐步加入的PHP和js两个大块的内容。网站用的是类似以前MVC的分层结构(但是并不完全相同),核心逻辑都是在后台完成的。为了保证SEO,原则上,页面能后台生成的部分都用后台生成。此外,尽管我们会采用一些新的技术(例如css3画圆角,当然现在来看也已经很老套了),但是因为我早期使用的就是IE,尽管之后用了火狐(FireFox)再到谷歌浏览器(Chrome),现在已经很少用IE了,因此我还是一直是保持了IE兼容性的,尽管可能是需要高版本IE才能正常体验全部功能。就在前段时间,我甚至还把MarkDown语法解析在后端做了一份,目的就是为了搜索引擎能够识别出这些内容来。
相比起我刚开始做这个网站来说,已经这么多年过去了,时代可能变了,我也不能一直采用这种结构下去了。这事情的起因大致就是我这段时间需要做一些新功能,本身就是要大量使用js计算的工程,加上现在工作也是用的vue,所以萌生了自己实现部分vue功能的想法,尽管现在核心的功能可以实现,但是还有很多其他问题。我这里从以前的用户登录问题说起吧。
先来谈谈用户的问题
之后因为要保持模块独立并解决跨域cookie的问题,我把除用户中心以外的获取的所有搬到了前端,通过跨域ajax获取到用户token及用户名等信息。这给我们造成了不小的困难,因为我们无法在用户打开页面之前获取到用户的token。当然,这也不是完全没有办法的,我这里说两种方案:
一种是用户中心登录时,通过某些方式让浏览器访问各个子站点,然后子站点通过访问的地址写token。但是,实际上如果我有100个子站点(尽管实际上我没有),但是用户只需要访问其中的一到两个,这个100个站点的访问需要占用大量资源不说,利用率还低。同时这100个站点可能会有访问失败的,然后就会是各站点之间账号不同步。这种做法是我初期同步主站和DZ论坛登录的做法(但是效果并不好,甚至经常失败)。
另一种比较可行的方式用户中心要生成两个token,一个AccountToken(即绑定账号的token)和ClientToken(即客户端token),同时存储这两个token的关系。其工作原理是但用户访问用户中心时,如果ClientToken不存在就给他生成一个ClientToken,用一个有效期足够长的时间的cookie来存储,同时数据库的token关系表中记录这个token(当然也可以不急着记录),当用户登录时,向之前一样生成一个token,这个就是AccountToken,但是,这个token不下发到cookie中,而是写入数据库和当前ClientToken绑定。当访问其他站点时,子站点也是获取ClientToken(不存在则先向用户中心请求,然后通过回调页面写入cookie再重定向回原来的页面,cookie的有效期设置为session,这样,如果这个ClientToken不同步了,只需要重启浏览器就能自动重新从用户中心获取),这种操作会有一个特征就是第一次打开子站点的时候是会有一次跳转(或ajax请求)然后回之前页面(或刷新)的,可以参考渣浪微博。这种办法建立在唯一且不变的ClientToken的基础上,用户信息始终存储在服务器,根据客户端的token从服务器中查出用户信息,因此,各子站点只要能访问这个记录关系的数据库就可以获取到用户登录状态。尽管这个ClientToken并不能做到真正的唯一且不变,但是,只要用户不人为删除或修改cookie的值,并且用户中心的cookie不过期就不会出现这种问题。即使发生这种情况,只需重启浏览器就能重新同步了,除非有人故意搞事(但也就给自己添麻烦)就不会出现这种问题。
我这里暂时还没多少变动(因为改的话影响会有点大,不过可能会要改的了)。
页面无刷新切换url技术
我一直使用的是php通过后台查询数据,在后台组装成一个生成好的网页返回。可是并不是所有情况下这个做法都能获得最好的结果。首先,如果你们有注意到我博客电脑版网页的页面切换的话,你会发现他是一个ajax请求,而不是整页刷新,这么做的目的是为了点击站内链接之后bgm控件不会退出而刷新。这个过程js监控了a标签的点击并取消事件阻止跳转,然后通过ajax向后台请求网页内容,并附带isajax之类的字段。此时后台PHP程序我的框架会在调用show_head
函数处保存标题但不输出头部html,当网页主体生成完毕后会调用show_foot
函数,此时该函数不再输出网站脚步,而是读取ob缓存的网页主题,然后和保存的标题一并打包成json格式给客户端。客户端收到,这个json数据之后,取出标题并更新标题,然后内容通过innerHTML
字段暴力写入网页文档中,然后找到里面所有的scrip标签并通过evel重新执行脚本,最后重新绑定所有a标签的事件。这样就可以在几乎不修改原有代码的情况下实现无刷新跳转功能。但是这时候还有个问题没有解决,就是我们的url是没有改变的,此时,用户刷新页面会回到他打开时的那个页面,同时,他也不方便将当前看到的网页分享给其他人。
由于直接修改loction会导致网页跳转,因此,传统做法是用“锚点”,也就是在url结尾加上"#xxx"这种格式的内容,由于这些锚点原本是在页面之内定位用的,所以改变锚点并不会引起网页跳转刷新,但是这个锚点的内容可以通过js获取,同时,请求的时候浏览器也会作为url的一部分发送给服务器,所以要在后端判断是访问哪个页面也不难。不过这样做会有两个问题,一个是SEO,因为在搜索引擎看来这是一个页面(也就是说这时候其实搜索引起就已经技术跟不上了),所以它无法理解锚点变化之后会发生什么。其次,这回影响到原有锚点的功能,毕竟他只能存放一个值,存放了实际url就不能存放页面内的锚点地址了。
正是由于这样的原因,有了一个新的方案——history API
,我以前发布过一个关于这个的文章,我的博客也正是用的这种方式。简单来说就是浏览器提供一个API,通过这个API,我们可以向浏览器中写入历史记录从而实现跳转的效果,通过这个API,我们就能完美的解决我们的问题,同时还能修改好网站标题了。当然,这个API需要当时非常“先进”的IE10才能执行,但是,终究,他还是一个IE兼容的东西。
但是,这里我仍然有一个坑,就是内容并不完全独立,如果有301或者302重定向,这时候isajax字段会在第二次请求时丢失,于是又会返回一个完整的网页。此外通过innerHTML
写入也存在诸多问题。因此这种更新页面内容的方式并不完美(但是更新url的还是完美的),可能会要修改成更合理的要啥更新啥。
最后所js的双向绑定
其实吧,我一开始想,后台生成内容,如果需要js频繁修改的话,我可以给他生成一个带id
的span
之类的元素,js需要修改的时候,我js通过id
获取dom节点然后innerHTML
写入就行了,这样,后台能给他写入一个初始值给搜索引擎看,同时也能通过js修改。一开始我感觉vue也就那么一回事,我那样操作也不是太麻烦。但还是,随着时间变化,特别是现在写的倾向前端的东西多了,感觉这个还是挺香的。首先,直接绑定就是多一事不如少一事。其次有个大问题就是如果一个控件的内容如果要绑定,多个相同控件的id就会冲突。我现在做的小项目的做法是改成class
识别,每个控件生成的时候直接用他外面生成的div框去找。但是首先,就是多了这个div
框,其次,这时候后端也管不到这个控件了。因此这样一来双向绑定也不存在问题了。
既然那个项目本来就是前端js计算为主,那我可以整双向绑定了吧。但是似乎为此引进个vue好像也没必要,并且也和我现在的模式不完全兼容。自己实现个双向绑定其实也不难,其实也就是defineProperty
,这个我几年前就知道了,然后去读DOM中的各个节点来绑定。不过以前一个是没想到这个defineProperty
能这样用,而且那时候IE兼用不好。不过现在ie也多对defineProperty
有良好的兼容了。所以我就开干了。
绑定的功能也差不多就绪了,可是,回头一看,还有新问题。我后端取模板是直接到模板目录取,直接include
就行,可是前端呢?直接后端一次全部输出到HTML中?我都不知道是否能用上,这自然就不是一个好主意了,毕竟,我能不能用上又是一回事,这些数据又不会缓存的。专门放个前端模板?首先感觉有点别扭,其次我可能要等多个子模块,顺序请求肯定不是个好主意,但是,并发请求如何保证所有请求都能回来呢?Promise是有一些封装的,虽然感觉如果只是回调的话,它算不上多香,但是它有个all方法,就很方便了。但是这个办法不支持IE,并且ajax请求不能跨域,所以想在其他域放基础模块时有点问题的,要加上跨域的响应头才行。还有就是模块自身的脚本怎么引入执行evel
?恐怕仍然不是一个好主意。还有一种方式就是用import之类的,这样ie还是不支持,然后模板又成问题了。
Say goodbye to IE
当我忽然觉得IE这也不行那也不行,就是连currentScript
也不能支持的时候,还有之前写的箭头函数,因为ie不支持,我又改回去了。我忽然发现,时间是真的过的快,这些年已经发生了翻天覆地的变化,转眼就是好几年过去了。以前我啦IE兼容,兼容的是win7的IE,那时候是要淘汰xp。而现在,微软极力推行win10,win7的IE自然也不在支持的行列,甚至,IE也已经凉了,微软在推Edge了,IE已经退出历史舞台不再更新了,这些都不支持也正常的。当然是用webpack打包带上一些插件去处理,可以解决这些问题,但是,这中间还有许许多多的问题。仔细想来,我这小破站子也许是该做一次升级了,尽管具体采用何种模式我暂时还没确定,抽空想想。至少以后新做的东西,会采用一些新的模式。IE的包袱,大概是改丢了,但是向博客这边,SEO怎么整我还不知道。并且,如果改的话,大概是涉及到网站的基础了。