0.5.9 • Published 4 years ago

lovol v0.5.9

Weekly downloads
1
License
ISC
Repository
-
Last release
4 years ago

一、router介绍

这是一个简单的路由中间件,旨在帮助你快速搭建一个小型网站。将路由对象化Json的结构思维让你理解更加容易,支持常用的权限控制、模板、后端渲染、静态化等常用功能。 初期文档比较简陋,在后期会慢慢完善。

1.1开始

1.1.1引入并导出常用方法和变量

这些方法和变量都是常用的,可以先导出备用。

const lovol = require('lovol');

const {
    API, AUTH, STATIC,//方法描述
    GET, POST, PUT, DELETE,//请求方法
    initRouter, clearTemplate, clearStatic, clearCache//常用方法
} = lovol.router;

1.1.2初始化

第一个参数是路由的对象,第二个是配置参数。

const router = initRouter({
        [AUTH]() {//访问控制

            return true;
        },
        about() {//domain/about

            return {[STATIC]: true, qwe: 123}//可以静态化domain/about/1.html
        },
        admin: {
            async [AUTH]({request, response, ...data}) {

                // console.log(data);

                // return data.redirect('/about');//重定向

                return true;
            },
            [GET]({redirect, view}) {//默认的GET路由//domain/admin

                // redirect('/about');

                return view('/about');//使用别的页面
            },
            log: {
                async [GET]() {//domain/admin/log

                    return {qwe: 123};
                },
                api: {
                    [API]: true,//定义为接口,没有页面
                    [GET]() {

                    },
                    [POST]() {

                    }
                }
            },
        }
    }
    ,
    {
        max: 1024 * 1024 * 16,//请求的最大尺寸
        lang: 'zh-cn',//本地化
        charset: 'utf-8',
        defaultMethod: [POST],//表单请求会返回页面的方法
        // cacheTimeout: 10,//内存缓存的时间
        cacheTimeout: false,
        serverError: false,//是否显示服务器端错误
        bridge: {qwe: 123},//对象桥
        mime: {//mime-type支持格式
            json: 'application/json',
        },
    });

1.1.2使用

nodejs中有HTTP、HTTP/2、HTTPS,这里以HTTP为例。

http.createServer(async (request, response) => {
   
       let result = await router(request, response);//调用方法传入request,response
   
       // console.log(result);
   
       response.writeHead(
           result.head.statusCode,
           result.head.statusMessage,
           result.head.headers
       );
   
       if (typeof result.body.pipe === "function")
   
           result.body.pipe(response);//路由结果为流
   
       else
   
           response.end(result.body);//路由结果为值
   
   }).listen(80);

1.2目录结构

将会自动生成几个目录:shared(共享模板)、static(静态缓存)、views(页面模板)、wwwroot(资源目录)

┌ shared  ─ admin ┬ head.htm  //模板缓存文件
│                 ├ head.html //模板文件
│                 └ foot.html
├ static  ─ about ┬ 1.html    //静态缓存文件
│                 └ data.json //静态缓存文件
├ views   ┬ about.html
│         ├ admin.html
│         └ admin ─ log.html
└ wwwroot ┬ index.html
          ├ js
          ├ css
          └ img
  • 我不太会写文档这些,我就直接上例子吧。

1.3实例

1.3.1访问控制

[AUTH]() {
    ...
    return false;//false:浏览器会收到一个403错误,true:路由会继续向下进行。
    ...
}
        
//我比较懒直接贴代码
async [auth]({pathArray, getCookie, redirect, request}) {

    let cookie = getCookie();//校验部分cookie可以使用httponly来防止js直接篡改。
    
    let auth = cookie._token && cookie._token.length === 32 &&
        cookie._token === md5('my key 2019' + cookie._uid + cookie._menu);//验证cookie完整性
    
    if (pathArray[0] === 'login') {
    
        if (auth) {
    
            return redirect('/admin');//可以直接重定向
        }

    } else if (pathArray[0] === 'logout') {

        if (!auth) {

            return redirect('/admin');//可以直接重定向
        }

    } else if (!auth) {

        return redirect('/admin/login');

    } else if (pathArray.length === 0)

        return true;

    else if (cookie.menu)

        return cookie.menu.split(',').includes(pathArray[0]);//这是复杂一些的判断。

    else

        return false;//也可以返回403错误

    return true;
},
//这里面你还能做ip限制、流量控制、防恶意访问等你能想到的操作。

1.3.2目录默认文件

只要有defaultMethod里面的方法都会生成一个默认的文件根目录就是index.html,其他就是 目录名.html。如果defaultMethod加入了POST。

initRouter({
    [GET]() {}
    about: null,
    admin: {
        [POST]() {},
        log() {}
    }
});
//生成如下文件
├ views   ┬ index.html       // [GET]() {}
          ├ about.html       // about: null
          ├ admin.html       // admin: {[POST]() {}}
          └ admin ─ log.html // admin: {log() {}}

1.3.3动态转静态

return的对象里面加入STATIC: true值这样浏览器以访问静态文件的时候就会在static目录里面保存相应的静态文件,第二次访问同一个文件的时候后台就不会重新渲染,而是直接访问这个静态文件。 所有这个的使用要严格判断控制,不然会被而已生成静态文件消耗服务器资源,好处就是能自动缓存文件,比如产品页。

product({pathArray, view}) {//domain/about
    
    let [key, id] = pathArray;
    
    id = id.split('.')[0];
    
    if (key === 'id' && isValid(id))
   
        return {[STATIC]: true, qwe: 123}//可以静态化domain/about/1.html
        
    else
    
        return view('/404', {id});
},
//生成文件
├ static ─ product - id ┬ 1.html // domain/product/id/1.html
                        └ 9.html // domain/product/id/9.html

1.3.4后端渲染

后端渲染是将路由到的方法对应views下的html文件动态执行返回渲染后的html文件,一个url路由结果如下, 比如domain/notify/index.html(含html后缀),它路由的顺序如下,逐条去搜索,都没找到就返回404:

1、/wwwroot/notify/index.html

2、/static/notify/index.html

3、根据路由路劲去找相应的方法,如果方法属于API则直接返回返回值

4、如果方法不是API再找/views/notify/index.html,有则将返回值传入scope里面去渲染模板,没有则返回404。

再比如domain/notify/index(不含html后缀)就不再去/wwwroot和/static目录去匹配了。如果在notify下找到了index的默认方法(GET、POST)就去/views 目录下找相应的模板文件,将方法返回值传入scope里面去渲染,将结果返给浏览器。如果没有则使用notify的默认方法和对应模板文件去渲染将多余的路由值(index)传入scope(pathArray: 'index')中去渲染, 如果notify也没有就使用根路由的默认方法和模板文件去渲染,如果根目录也没有则返回404。

  • 注意,因为这个特性所以在用路由带参数的时候(domain/notify/参数1/参数2;domain/notify/qwe/123),最好是notify就是节点的末端这样效率是最高的, 不然它会先遍历节点在子节点都没匹配的时候再选中notify方法来渲染。

1.4关键字

1.4.1关键字1:#{}

//代码
...
scope.test.key = 123;
...
<div>#{scope.test.key}</div>
...
//结果
<div>123</div>

//可以等价的看作
`<div>${scope.test.key}</div>`

//所以这样用也是一样的
<div>#{1+1}</div>           //<div>${1+1}</div>           //<div>2</div>
<div>#{parseInt(1.5)}</div> //<div>${parseInt(1.5)}</div> //<div>1</div>

//这个也能转义
<div>\#{1+1}</div>  //<div>#{1+1}</div>
<div>\#{</div>      //<div>#{</div>

1.4.2关键字2:{/}和{~}

//共享模板
/shared/comm/head.html
<h1>{scope.title}</h1>

//代码
...
{/comm/head,{title:'这是标题'}}//参数可缺省
<p>这是内容</p>
...
//结果
<h1>这是标题</h1>
<p>这是内容</p>

//以上面的代码我前提,则:
...
{~comm/head,{title:'标题标题标题'}}
<p>这是内容</p>
...

//结果
<h1>这是标题</h1>
<p>这是内容</p>
因为'{~}'会在.html生成.htm,下次使用则直接使用.htm文件,不在运算.html文件里的。

1.4.3关键字3: <script server></script>

//代码
<div>
<script server>
    if (1) {
        write(`<h1>${1+1}</h1>`);
    } else {
        write(`<h2>我不会被执行</h2>`);
    }
</script>
</div>

//结果
<div>
<h1>2</h1>
</div>

1.4.4关键字混合使用

/shared/foot.html
<p>#{scope.world}</p>

//代码
<div>
<script server>
    scope.share = 'foot';
    scope.world = 'hello';
</script>
{/#{scope.share}}
</div>

//结果
<div>
<p>hello</p>
</div>

1.4.5错误提示

//代码
<div>
{/qwe}
<script server>
    write(qwe)
</script>
</div>

//结果
<div>
    <!-- 未加载源摸板 /shared/qwe.html  -->
    <!-- server script 代码有误:ReferenceError: qwe is not defined-->
</div>

1.5路由方法的参数

initRouter({
    [GET](scope) {
        console.log(Object.keys(scope));
        //[ 'view',//使用其它模板,可使API返回页面:view('/more/help')
            'query',//url参数:domain/test?qwe=123&asd=456 //query: {qwe: 123, asd: 456}
            'bridge',//桥对象:bridge: {qwe: 123, asd: 456}//第二个参数中传入
            'urlObj',//url解析后的对象:Url {port: '', href: '', query: {}, ...}
            'nonView',//不返回页面,直接返回数据:nonView({qwe: 123})
            'request',//原生对象request
            'response',//原生对象response
            'writeBody',//等价response.write();
            'redirect',//重定向:redirect('http://domain2/')
            'getCookie',//获取cookie对象
            'setCookie',//设置cookie:setCookie([{key: '', value: '', path: '', domain: '', secure: '', httponly: '', expires: ''}]);
            'delCookie',//删除cookie:delCookie([{key: '', path: ''}, {key: '', path: ''}]);
            'data'//表单提交的数据:data: {qwe: 123, asd: 456},如果有文件则:data.file1.save('tmp', 'f1.jpg');则保存为:/wwwroot/tmp/f1.jpg
             ]
    }
}, {bridge: {qwe: 123, asd: 456}});
0.5.9

4 years ago

0.5.8

5 years ago

0.5.7

5 years ago

0.5.6

5 years ago

0.5.5

5 years ago

0.5.4

5 years ago

0.5.3

5 years ago

0.5.2

5 years ago

0.5.1

5 years ago

0.5.0

5 years ago

0.4.9

5 years ago

0.4.8

5 years ago

0.4.7

5 years ago

0.4.6

5 years ago

0.4.5

5 years ago

0.4.4

5 years ago

0.4.3

5 years ago

0.4.2

5 years ago

0.4.1

5 years ago

0.3.6

5 years ago

0.3.5

5 years ago

0.3.4

5 years ago

0.3.3

5 years ago

0.3.2

5 years ago

0.3.1

5 years ago

0.3.0

5 years ago

0.2.10

5 years ago

0.2.9

5 years ago

0.2.8

5 years ago

0.2.7

5 years ago

0.2.6

5 years ago

0.2.5

5 years ago

0.2.4

5 years ago

0.2.2

5 years ago

0.2.1

5 years ago

0.1.10

5 years ago

0.1.9

5 years ago

0.1.8

5 years ago

0.1.7

5 years ago

0.1.6

5 years ago

0.1.5

5 years ago

0.1.4

5 years ago

0.1.3

5 years ago

0.1.2

5 years ago

0.1.1

5 years ago

0.1.0

5 years ago