如何使用原生 JavaScript 构建路由器
很多时候,我们可能想把 JavaScript 代码写在一个文件中,但希望只有匹配特定路由时才执行这段代码。你可以借助路由器来实现这一点,下载路由器库或自己编写代码即可。
今天,我将指导您使用原生 JavaScript 构建一个非常基本的路由器功能。我将使用一些ES6 特性和JavaScript 正则表达式来构建此路由器,因此您必须熟悉这些特性才能更好地理解。
概念
编程的好处在于,你可以用任何你喜欢的方法或风格来解决问题,但你必须避免一些不好的做法。
以下是我们将采用的构建路由器的方法。
- 创建路由器类
- 创建一个方法,将路由逻辑及其相应的回调函数存储在数组中。
- 创建一个方法来处理这些逻辑,如果逻辑为真,则返回相应的回调函数。
这是我们想要的图片。
const router = new RouterClass();
// the get() method would store the '/' logic and callback in an array;
router.get('/', function(){
// code to be executed if '/' is matched
});
// here get() method would push '/another-page' and the callback to the existing array
router.get('/another-page', function(){
// code to be executed if '/another-page' is matched
);
router.init(); // this method will process the logics
构建我们的路由器
步骤 1 - 创建路由器类
我们将创建一个名为 Router 的类,它将使用new
关键字来调用。
class Router {
}
第 2 步 - 添加构造函数
构造函数是使用 new 关键字实例化 Router 类时执行的方法。在构造函数方法中,我们将创建一个名为 的属性routes
,并为其分配一个空数组。
routes 属性会将所有路由及其回调函数存储在一个数组中。
class Router {
constructor(){
this.routes = [];
}
}
您还可以将options
参数传递给构造函数方法并为路由器类设置一些选项,但为了简单起见,我们将跳过这一步。
步骤 3 - 创建存储路线的方法
我们将创建一个名为“get”的方法,get()
用于存储路由及其回调。get 方法应该包含两个参数uri
:callback
class Router {
constructor(){
this.routes = [];
}
get(uri, callback){
}
}
我命名这个方法是get
为了方便阅读。因此,router.get(uri, callback);
它应该表示:获取一个特定的 URI 并返回一个回调。你可以用你自己的方式命名它。或许,router.if(uri, callback);
步骤 4 - 验证 get 方法的参数
在这种方法中,我们将验证我们的参数,以确保在使用路由器时不会错误地传递错误类型的变量作为参数。
class Router {
constructor(){
this.routes = [];
}
get(uri, callback){
// ensure that the parameters are not empty
if(!uri || !callback) throw new Error('uri or callback must be given');
// ensure that the parameters have the correct types
if(typeof uri !== "string") throw new TypeError('typeof uri must be a string');
if(typeof callback !== "function") throw new TypeError('typeof callback must be a function');
// throw an error if the route uri already exists to avoid confilicting routes
this.routes.forEach(route=>{
if(route.uri === uri) throw new Error(`the uri ${route.uri} already exists`);
});
}
}
步骤 5-将路线添加到路线数组
验证方法的参数后get()
,我们将创建一个名为的对象route
并将该对象推送到我们现有的路由数组中。
class Router {
constructor(){
this.routes = [];
}
get(uri, callback){
// ensure that the parameters are not empty
if(!uri || !callback) throw new Error('uri or callback must be given');
// ensure that the parameters have the correct types
if(typeof uri !== "string") throw new TypeError('typeof uri must be a string');
if(typeof callback !== "function") throw new TypeError('typeof callback must be a function');
// throw an error if the route uri already exists to avoid confilicting routes
this.routes.forEach(route=>{
if(route.uri === uri) throw new Error(`the uri ${route.uri} already exists`);
})
// Step 5 - add route to the array of routes
const route = {
uri, // in javascript, this is the same as uri: uri, callback: callback, avoids repition
callback
}
this.routes.push(route);
}
}
init()
步骤 6 - 使用方法处理路由
快完成了!让我们使用 方法来处理路由init()
。当调用此方法时,我们希望它循环遍历路由数组,并将 和 进行匹配route.uri
。window.request.pathname
如果找到匹配项,我们将通过返回 函数来跳出循环route.callback
。为了更轻松地跳出循环,我们将使用Array.some()
方法来代替 ,Array.forEach()
因为Array.some()
会在循环返回真值时结束循环。
class Router {
constructor(){
this.routes = [];
}
get(uri, callback){
// ensure that the parameters are not empty
if(!uri || !callback) throw new Error('uri or callback must be given');
// ensure that the parameters have the correct types
if(typeof uri !== "string") throw new TypeError('typeof uri must be a string');
if(typeof callback !== "function") throw new TypeError('typeof callback must be a function');
// throw an error if the route uri already exists to avoid confilicting routes
this.routes.forEach(route=>{
if(route.uri === uri) throw new Error(`the uri ${route.uri} already exists`);
})
// Step 5 - add route to the array of routes
const route = {
uri, // in javascript, this is the same as uri: uri, callback: callback, avoids repition
callback
}
this.routes.push(route);
}
init(){
this.routes.some(route=>{
let regEx = new RegExp(`^${route.uri}$`); // i'll explain this conversion to regular expression below
let path = window.location.pathname;
if(path.match(regEx)){
// our route logic is true, return the corresponding callback
let req = { path } // i'll also explain this code below
return route.callback.call(this, req);
}
})
}
}
代码很少,但其中有些奇怪的地方,对吧?我先从转换成正则表达式开始。
我将我们的代码转换route.uri
成正则表达式,因为我们想要匹配 route.uri 的精确值,否则window.location.pathname
会router.get('/about', callback)
匹配“/about-us”、“/about-me”,因此我引入了 regExp 关键字^
和$
。
你也注意到了let req = { path }
, 也意味着let req = { path: path }
。这只是传递一个可以通过回调参数访问的对象。实际上,这意味着:
const router = new Router();
router.get('/about-me', function(req){
console.log(req.path); // outputs /about-me to the console
}
router.init();
结论
这些步骤是构建一个基本的JavaScript 路由器时可以参考的。为了进一步提升,你应该关注以下功能:
- 具有路由参数
- 能够评估查询参数
- 有命名路线
- 分组路线
如果您不知道如何实现这些功能,可以查看我构建的路由器库的源代码,了解我是如何实现这些功能的。更好的是,您可以通过 npm 安装该库并在脚本中使用它。请查看npmnpm i @kodnificent/sparouter
上的安装指南。
注意:
这主要用于前端路由。如果你想构建后端路由器,可以遵循类似的流程,但获取请求 URI 的过程取决于服务器。
这是我在 dev.to 上的第一篇文章,所以点个爱心鼓励一下吧。非常欢迎评论、贡献和批评。欢迎查看我的dev.to 个人资料并关注我,让我们一起开发。
文章来源:https://dev.to/kodnificent/how-to-build-a-router-with-vanilla-javascript-2a18