前端路由,是指随着浏览器地址栏的变化,展示给用户的页面也不相同。
传统的网页根据用户访问的不同地址,浏览器从服务器获取对应页面内容展示给用户。这样造成服务器压力比较大,而且用户访问速度也会比较慢。在这种场景下,单页面应用出现了。
单页面应用,就是只有一个页面,用户访问一个网址,服务器返回的页面也只有一个。地址栏内容改变显示不同页面由前端路由来实现。
法一:hash+hashchange
我们通过读取location.hash和监听hashchange事件来实现路由。
首先定义一个Router对象:
function Router() {
this.currentUrl = '';
this.routes = {};
}
Router.prototype.route = function (path, callback) {
this.routes[path] = callback || function () {};
}
Router.prototype.refresh = function () {
this.currentUrl = location.hash.slice(1) || '/';
this.routes[this.currentUrl]();
}
Router.prototype.init = function () {
window.addEventListener('load', this.refresh.bind(this), false);
window.addEventListener('hashchange', this.refresh.bind(this), false);
}
然后创建一个实例:
对应的html为:
上面就实现了不同路由展示不同的内容。支持浏览器的前进和后退,很好地解决了前后端分离的问题。
法二:history.pushState()+popstate
跟之前的方法一样,pushState()修改url的地址,popstate监听地址的改变,不同的是,手动的进行pushState()并不会触发popstate事件。
2.原生js实现对象属性监听器
可以使用defineProperty来实现对象属性的监听。defineProperty是ES5属性,大部分的使用场景都没有问题。现在首先来看看它的用法:
各参数的作用分别是:
obj是需要定义属性的那个对象。
prop是需要被定义或者修改的属性名。
descriptor是定义属性prop的描述。
下面来详细看看descriptor的各属性可以做什么。
value 设置属性的值
在代码中,我们定义了bob的一个属性name,value为bob。然后试着给name属性赋值Bob,却发现属性没有发生变化。这是为什么?原来只有把writable修饰符设置为true时,这个属性才能被修改。
writable 当该属性为true时,该属性的才会被赋值运算符改变。默认为false。
依然是上面的例子,这次成功修改了bob的name属性。
enumerable 设置该属性是否是可枚举类型。默认为false。
只有enumerable属性设置为true时,这个属性才可以用for(prop in obj)和Object.keys()枚举出来。
上面的代码为bob添加了两个属性,一个是可枚举属性age,一个是不可枚举属性math。
configurable 该属性决定了对象的属性是否可以被删除,以及除writable外的其他特性能不能被修改;并且修改writable时只能修改为false。
get 给属性提供的getter方法,如果没有getter则返回undefined,该方法返回值被用作属性值。
set 给属性提供的setter方法,该方法接受唯一参数,并将该参数的新值分配给该属性。
get和set属性不能和value和writable属性一起出现,否则会报错。
在上面的代码中,我们为bob的weight属性添加了set和get方法,现在读取和修改属性时都会打印出提示文字。注意,为什么需要一个中间变量weight来保存weight属性值。因为如果我们直接在get或set方法里访问bob.weight,则又会触发wegiht的get方法,会导致程序陷入死循环中。
介绍完defineProperty的用法,属性对象监听器的写法就呼之欲出啦。我们只需要重写对象属性的set方法,就可以在对象的属性发生变化时调用我们设置的回调函数了。我们来完成一个函数,实现对对象属性的监听。
我们的监听器已经实现好了,其实,defineProperty非常强大,vuejs的数据绑定也是基于该方法实现的。
原生js实现前端路由
理解JavaScript的Object.defineProperty()函数