1 Star 3 Fork 1

雨思 / jj.js

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

jj.js

jj.js

A super simple lightweight NodeJS MVC framework(一个超级简单轻量的NodeJS MVC框架)

项目介绍

无论什么语言什么框架的程序,类库的使用都离不开require、import、include等导入模块,不用时,还得删除。而本框架基于proxy实现了类库自动加载、懒加载和Class自动实例化及单例化技术,所有的类库,想用直接调用,系统会自动导入。

项目特性

  1. 系统架构为经典的MVC模式,模仿Thinkphp5,很容易上手
  2. 系统类库、用户类库都支持自动加载、懒加载、Class自动生成单实例
  3. 支持应用级、路由级、控制器级三级中间件,方便插件及二次开发
  4. 支持单应用和多应用两种运行模式
  5. 基于jsdoc,提供完整的代码提示,自动生成应用端types类型文件

项目地址

项目地址:https://github.com/yafoo/jj.js

码云镜像:https://gitee.com/yafu/jj.js

官网地址:https://me.i-i.me/special/jj.html

安装

npm i jj.js

运行环境要求:node.js >= v12.7.0

Hello world !

1、创建控制器文件 ./app/controller/index.js

const {Controller} = require('jj.js');

class Index extends Controller
{
    async index() {
        this.$show('Hello jj.js, hello world !');
    }
}

module.exports = Index;

2、创建应用入口文件 ./server.js

const {App, Logger} = require('jj.js');
const app = new App();

app.run(3000, '0.0.0.0', function(err){
    !err && Logger.log('app', 'http server is ready on 3000');
});

3、运行命令

node server.js

4、浏览器访问 http://127.0.0.1:3000,页面输出 Hello jj.js, hello world !

5、在Stackblitz中打开 Hello world !

开发手册

应用目录结构

├── app             //应用目录 (非必需,可改名)
│  ├── controller   //控制器目录 (非必需,可改名)
│  │  └── index.js  //控制器
│  ├── view         //模板目录 (非必需,可改名)
│  │  └── index     //index控制器模板目录 (非必需,可改名)
│  │     └── index.htm //模板
│  ├── middleware //中间件目录 (非必需,不可改名)
│  ├── model        //模型目录 (非必需,可改名)
│  ├── pagination   //分页目录 (非必需,可改名)
│  ├── logic        //逻辑目录 (非必需,可改名)
│  └── ****         //其他目录 (非必需,可改名)
├── app2            //应用2目录 (非必需,可改名)
├── common          //公共应用目录 (非必需,可改名)
├── config          //配置目录 (非必需,不可改名)
│  ├── app.js       //APP配置 (非必需,不可改名)
│  ├── db.js        //数据库配置 (非必需,不可改名)
│  ├── routes.js     //路由配置 (非必需,不可改名)
│  ├── view.js     //模版配置 (非必需,不可改名)
│  ├── cache.js     //缓存配置 (非必需,不可改名)
│  └── ****         //其他配置 (非必需,可改名)
├── public          //静态访问目录 (非必需,可改名)
│  └── static       //css image文件目录 (非必需,可改名)
├── node_modules    //nodejs模块目录
├── server.js       //应用入口文件 (必需,可改名)
└── package.json    //npm package.json

应用目录介绍:

  • config: 应用配置目录,系统的所有配置参数都放这里,也可以简化为一个config .js文件,所有配置参数会覆盖框架默认参数。其中config这个名字不允许修改。
  • app、app2、common: 为应用目录,common为公共应用目录,可以存放公共的model、logic或其他的文件,app为默认访问的应用。jj.js框架支持单应用和多应用运行模式,在./config/app.js中把app_multi设置为true即为多应用模式,此时可以创建app2、app3更多应用。框架默认为单应用模式。
  • public:静态资源目录,主要存放css文件、js文件、图片等静态资源。在./config/app.js中通过static_dir参数可以设置或更改静态目录名字,为空时,则关闭静态访问(系统不会加载静态资源访问的逻辑,最大节省内容,也即所谓的轻量)
  • server.js:应用入口文件,名字可以任意改。

系统类库

const {App, Controller, Db, Model, Pagination, View, Logger, Cookie, Response, Upload, Url, Middleware, Cache, Context, View} = require('jj.js');

jj.js类库继承关系图

系统类库都是Class类型,其中LoggerCache是静态类;开发时建议继承系统类库,这样可以在类内使用$开头的属性,实现自动加载功能,链式调用类方法时会自动实例化一个单例。例如,在控制器内使用 this.$logger 会返回系统Logger类,使用 this.$logger.info(),会自动生成一个logger单例,并调用info方法,其他系统类库及类库内可以以这种方法调用。

类库自动加载

类库自动加载、懒加载是整个框架的核心,这里以hello world!示例程序先简单做个介绍。

1、假如想在./app/controller/index.js控制器的user方法中读取user数据表中id为1的用户,我们直接上代码:

const {Controller} = require('jj.js');

class Index extends Controller
{
    async index() {
        this.$show('Hello jj.js, hello world !');
    }

    async user() {
        const user_info = await this.$db.table('user').find({id: 1});
        this.$show(user_info);
    }
}

module.exports = Index;
  • 访问url:http://127.0.0.1:3000/index/user,即可看到打印的json用户信息。

  • 其中async user() {}为异步方法,在控制器中,对外可以以url访问的方法必须设置为异步函数。使用this.$db调用框架db类,这里控制器必须继承框架Controller类,否则会调用失败。

  • 在控制器中,前缀为$字符的属性为特殊属性,框架首先会检测本类中即this实例中是否有$db属性,有的话,会直接调用。如果没有,会检测本类文件所在应用目录即./app/目录下,是否用db目录或文件,如果有,则$db即代表那个目录或文件,this.$db.table会继续在db目录下寻找table目录或文件。如果db目录或文件不存在,框架会在应用根目录./下找,是否有db目录或文件,如果有,和上面一样。如果还不存在,框架会在jj.js框架lib目录下找db文件或目录,而框架lib中有db.js文件,至此,this.$db成功访问到框架db类。

  • 如果将this.$db赋值为给一个变量,const db = this.$db;,得到的将是一个db Class类,可以进行new操作,const db = this.$db; const db1 = new db(this.ctx, options); const db2 = new this.$db(this.ctx, options);创建多个实例。

  • 但上面演示代码并没有new一个实例,而是直接调用db类的table()方法,这正是框架的智能之处。当调用this.$db.table('user')时,框架首先会检测db类是否有table静态属性,有的话会直接调用。没有,则会自动new一个db实例,而且这个实例是个单例,然后调用此db实例的table方法,table('user')设置数据表名后返回实例本身,然后紧接着调用find()方法,读取用户ID为1的数据。

注意:虽然系统加载很复杂, 但是基于node的常驻内存特性,上面的文件路径判断,处理一次就会被缓存下来,在下个生命周期不用重复判断,节省性能。

关于单例:通过this.$xxx调用的类实例,在单个生命周期内是单例,即不管调用几次,都是用的同一个实例,这样非常节省内存开销。如果有多个db实例需求,可以用上面的方法,自己new创建,自己创建时注意第一个参数需传入ctx,否则部分类库使用会出现异常。

生命周期:应用生命周期以url访问为单位,一次url访问到访问结束,是一个独立的生命周期,在这个周期内自动生成的单实例都是共用的。

2、假如自己创建了数据表模型./app/model/user.js,然后想在控制器中使用,模型文件代码如下:

const {Model} = require('jj.js');

class User extends Model
{
    async getUserInfo(condition) {
        const user_info = await this.db.find(condition);
        return user_info;
    }
}

module.exports = User;

这时在控制器的user方法中读取user数据表中id为1的用户,我们直接上代码:

const {Controller} = require('jj.js');

class Index extends Controller
{
    async index() {
        this.$show('Hello jj.js, hello world !');
    }

    async user() {
        const user_info = await this.$model.user.getUserInfo({id: 1});
        this.$show(user_info);
    }
}

module.exports = Index;
  • 访问url:http://127.0.0.1:3000/index/user,同样看到打印的json用户信息。

  • 根据示例1的加载检测机制,this.$model会自动定位到./app/model/目录,this.$model.user会自动加载./app/model/user.js类文件,调用方法getUserInfo,会自动生成一个user模型的单实例,并调此单实例的getUserInfo方法。

  • 可以看到,不管是系统类库,还是自定义类库,都可以实现自动加载、懒加载、自动生成单实例。

注意:在user模型内调用的是this.dbdb没有$前缀,这个db是专属于user模型的独立实例,与示例1生成的db实例不是同一个。当然也可以使用带$前缀的db,但这样,在一个生命周期同时调用多个不同的模型的话,容易造成混淆。

3、除了框架db类、自定义模型类,整个应用和框架的所有自定义类库、配置文件,都支持同过this.$xxxx调用。

config配置

应用的配置可以为./config/目录+./config/xxx.js文件的形式,也可以直接写到一个./config.js文件里。

应用配置不用每项都设置,只设置自己需要改的,默认会继承框架的默认配置,在控制器、中间件、模板类、模型类里都可以通过this.$config.xxx使用。

  • app配置./config/app.js
const app = {
    app_debug: true, // 调试模式
    app_multi: false, // 是否开启多应用

    default_app: 'app', // 默认应用
    default_controller: 'index', //默认控制器
    default_action: 'index', // 默认方法

    common_app: 'common', // 公共应用,存放公共模型及逻辑
    controller_folder: 'controller', //控制器目录名

    static_dir: '', // 静态文件目录,相对于应用根目录,为空或false时,关闭静态访问

    koa_body: null // koa-body配置参数,为''、null、false时,关闭koa-body,此时upload类将不可用,并且系统接收不到post参数。启用的话,建议配置:{multipart: true, formidable: {keepExtensions: true, maxFieldsSize: 10 * 1024 * 1024}}
}
module.exports = app;
  • 数据库配置./config/db.js:可以配置多个,方便程序里切换使用。
const db = {
    default: {
        type      : 'mysql', // 数据库类型
        host      : '127.0.0.1', // 服务器地址
        database  : 'jj', // 数据库名
        user      : 'root', // 数据库用户名
        password  : '', // 数据库密码
        port      : '', // 数据库连接端口
        charset   : 'utf8', // 数据库编码默认采用utf8
        prefix    : 'jj_' // 数据库表前缀
    }
}
module.exports = db;
  • 路由配置./config/routes.js: 路由功能基于@koa/router开发,关于url匹配规则可以参考官方文档:文档地址

本框架默认内置 应用/控制器/方法 的全局路由,即如果不需要定制url,可以直接访问,无需配置路由。

路由配置为一个数组,每一项为一条规则,项属性包含url规则,path访问路径,name规则名字,type路由类型。url一旦匹配,即会停止,如果需要继续向下匹配,需在程序内调用 await this.$next();,路由配置参考示例:

routes = [
    {url: '/', path: 'app/index/index2'}, // 访问'/',会定位到app应用的index控制的index2方法
    {url: '/article/:id.html', path: 'app/article/article', name: 'article'}, // 访问'/article/123.html',会定位到app应用的article控制的article方法;通过this.ctx.params.id可以获取到参数id;通过article可以反编译网址,执行this.$url.build(':article', {id: 123}); 生成'/article/123.html'
    {url: '/admin', path: 'app/admin/check', type: 'middleware'}, // 访问'/admin',会定位到app应用的admin中间件的check方法
    {url: '/about', path: 'app/about/index', type: 'view'}, // 访问'/about',会定位到app应用的view模板目录about目录下的index.htm文件,并直接输出文件内容
    {url: '/:cate/list_:page.html', path: 'cate/cate', name: 'cate_page'}, // 多参数分页示例
    {url: '/hello', path: async (ctx, next) => {ctx.body = 'hello world, hello jj.js!';}} // 直接路由到函数
];

module.exports = routes;

注意:本路由配置示例前两条规则为常规用法,后面的非常规用法,不建议使用。如果是单应用模式,可以去掉path参数里的app。

  • 自定义配置./config/self.js:自定义配置同样可以直接通过this.$config.self使用。
const self = {
    option1: ''
    option2: ''
    ...
}
module.exports = self;
  • 其他配置项,请参考系统配置文件jj.js/lib/config.js

编码命名规范

类名使用大驼峰,方法名使用小驼峰,私有方法使用下划线前缀。 控制器文件名使用小写下划线。

应用案例

Nginx代理

location / {
    proxy_pass       http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

License

MIT

MIT License Copyright (c) 2021 雨思 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

A super simple lightweight NodeJS MVC framework(一个超级简单轻量的NodeJS MVC框架) 展开 收起
JavaScript
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/yafu/jj.js.git
git@gitee.com:yafu/jj.js.git
yafu
jj.js
jj.js
master

搜索帮助