【egg】egg.js基础(一)

前言

  • 我第一次了解egg.js时候,感觉非常nb。稍微学了一下,结果后来又忘差不多了。
  • 我现在养成了一种不写博客就感觉自己不会的习惯。就算当时会了,过段时间也忘了。
  • 官网
  • 插件
  • egg的构造有点像jekins里那个worker和master的感觉。node是单进程单线程,所以只使用一个cpu核,egg会做一个node集群,通过master来控制worker达到多核的利用。当客户端请求来时,会先发给master,master看哪个空闲,把请求发给哪个。如果哪个worker宕机了,就立即重启它。

安装

  • 先不用脚手架,熟悉下里面东西。
cnpm i egg --save
cnpm i egg-bin --save-dev
  • 这个egg-bin就相当于我们经常配的package.json里script命令。通过script去执行egg-bin,这个再去执行egg。
  • 所以package.json里加上:
  "scripts": {
    "dev": "egg-bin dev"
  },

编写controller

  • 如果你熟悉 Web 开发或 MVC,肯定猜到我们第一步需要编写的是 Controller 和 Router。
  • 新建个文件夹app,里面建个router.js
module.exports = (app) => {
    const { router, controller } = app
    router.get('/mypage', controller.mypage.index)
}
  • 这个会传入一个app,app上面挂了一堆东西,这里只需要router,和controller。controller是个对象,里面是文件夹controller下文件的实例,这里做了个文件叫mypage。
  • 在app下的controller文件夹下编写mypage.js文件:
const { Controller } = require('egg')
module.exports = class extends Controller {
    async index() {
        this.ctx.body = 'hello'
    }
}
  • 这个类继承egg的controller,就是可以拿到它的一些方法。我们自己写的这个函数名,就是index,对应着上面传给router的controller.mypage.index。
  • 另外需要建立个默认配置。在app文件夹同级下建个config文件夹,里面建个config.default.js文件。

config/config.default.js

exports.keys = 'xx'
  • 随便给个key,优点像加盐算法里面的盐值的意思。
  • 然后使用npm run dev 启动,就能看见页面渲染的hello了。
  • 查看cookie,发现cookie里面有个csrf_token,这些都帮你搞定了。

静态文件

  • app文件夹下面会有个public文件夹,没有这个文件夹会帮你建。
  • 在这个文件夹下随便建个文件,里面写上自己的内容,然后访问http://127.0.0.1:7001/public/你的文件名就可以访问了。

使用模板引擎

cnpm install egg-view-nunjucks --save
  • 先随意建个模板。需要在app下的view文件夹下。

app/view/aaa.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div>aaaaaaaaaaaaaaaaaaaaa</div>
    <div>
        {% for item in data %}
        <div> {{item.id}}</div>
        <div> {{item.title}}</div>
        {% endfor%}
    </div>
</body>
</html>
  • config.default.js需要改一下view的配置:

config/config.default.js

module.exports = app => {
    let config = {}
    config.keys = app.name + Date.now()
    config.view = {
        mapping: {
            defaultExtension: '.html',
            defaultViewEngine: 'nunjucks',
            '.html': 'nunjucks'
        }
    }
    return config
}
  • 这个defaultExtension就是查找文件没给扩展名会默认加上扩展名html。
  • defaultViewEngine就是默认模板引擎是nunjucks。
  • .html就是扩展名为这个的使用nunjucks渲染。如果想用别的比如ejs就是'.html':'ejs'
  • config文件夹下再建个文件plugin.js:

config/plugin.js

exports.nunjucks = {
    enable: true,
    package: 'egg-view-nunjucks'
}
  • 这个就是把插件开启。
  • 最后,还要修改controller,将前面写的mypage.js修改下,让其渲染页面。
const { Controller } = require('egg')
module.exports = class extends Controller {
    async index() {
        let { ctx } = this
        const data = [{ id: '111', title: '333' }, { id: '11221', title: '3341' }]

        await ctx.render('aaa', { data })
    }
}
  • 这里的aaa就没给扩展名,因为配置了默认的。
  • 打开页面全部渲染出来就ok了。
  • 总结下就是配插件,配默认设置,ctx.render。

调用远程接口

  • 在实际应用中,Controller 一般不会自己产出数据,也不会包含复杂的逻辑,复杂的过程应抽象为业务逻辑层 Service。
  • 先模拟个接口,新建个service.js,然后安装express和mockjs。
let Mock = require('mockjs')
let express = require('express')
let app = express()
app.get('/data', function (req, res) {
    let result = Mock.mock(
        {
            "code": 0,
            [`data|${req.query.limit}`]: [
                { "id": "@id" },
                { "title": "@csentence" }
            ]
        }
    )
    res.json(result)
})
app.listen(3000)
  • 访问 3000端口/data能有假数据,且使用查询?limit等于几就能显示几条就对了。
  • 然后,为了方便配置,我们可以在config.default.js里面的config对象上挂个属性,里面挂上url。
module.exports = app => {
    let config = {}
    config.keys = app.name + Date.now()
    config.view = {
        mapping: {
            defaultExtension: '.html',
            defaultViewEngine: 'nunjucks',
            '.html': 'nunjucks'
        }
    }
    config.mypage = { url: '127.0.0.1:3000/data' }
    return config
} 
  • 注意,config上不能直接挂字面量只能对象。上面配置的属性应该是被调用后传给真正的config对象的。另外使用他的curl方法会自己加http://,所以配置不用加。
  • 然后在app下建立service文件夹,写个mypageservice.js。文件夹名字是定死的,文件名没定死。
const { Service } = require('egg')
module.exports = class extends Service {
    async myback(limit) {
        let { ctx } = this
        let url = this.config.mypage.url
        let res = await ctx.curl(url, {
            method: 'GET',
            data: { limit },
            dataType: 'json'

        })
        return res.data.data
    }
}
  • 这个service用来处理复杂逻辑,控制器调用service来获取结果。
  • 我们取的第一个data是数据外层大括号里面的data,第二个data是前面定义的假数据的data那个键。因为我们controller需要获取到数组嘛,这样取就能拿到数组了。
  • 这里需要继承egg的类,后面会调它。把他导出就行了。
  • 最后编写controller,调用service。
const { Controller } = require('egg')
module.exports = class extends Controller {
    async index() {
        let { ctx } = this
        let limit = ctx.query  ? ctx.query.limit : 5;
        let data = await this.service.mypageservice.myback(limit)
        await ctx.render('aaa', { data })
    }
}
  • 这里是获取data是异步的,所以要用await。
  • this.service.xxx就是去找service文件夹下xxx文件实例。
  • 最后那个myback就是类里面的原型的方法名。
  • 刷新页面看是不是5条数据。修改查询参数,看是不是能返回对应条数的数据。

计划任务

  • 我感觉计划任务真的是一个很6的设计。有需求建议看一下文档对于计划任务的说明。
  • 一般指有计划的做的任务,常见的是定时任务。
  • 计划任务放在app下的schedule文件夹。
  • 比如我有个数据需要每天改一次。那么我那个数据就不需要每次要时都去请求,而是应该每天去请求一次,然后缓存下,下次就直接从缓存拿就可以了。
  • 先写个接口,可以获取到数据,为了方便查看取数据没有,加个时间戳:
app.get('/title', function (req, res) {
    res.json({ title: `每日更换标题${Date.now()}` })
})
  • 在app下建立schedule文件夹,写个文件updateTitle.js。文件名不是定死的,文件夹名是定死的。

app/schedule/updateTitle.js

const { Subscription } = require('egg')
module.exports = class extends Subscription {
    static get schedule() {
        return {
            interval: '5s',
            type: 'all'
        }
    }
    async subscribe() {
        let res = await this.ctx.curl(this.config.cache.url, {
            method: 'GET',
            dataType: 'json'
        })
        this.ctx.app.cache = res.data
    }
}
  • 这里要继承egg的subscription基类。里面静态方法schedule和subscribe方法是定死必须要有的,不然报错。
  • 其中,schedule相当于任务配置,返回个对象,里面interval代表执行间隔,为了看出效果,我们设定为5s执行一次。
  • 执行逻辑就是subscribe,名字定死的。就是请求数据,把数据放到this.ctx.app.cache上。
const { Controller } = require('egg')
module.exports = class extends Controller {
    async index() {
        let { ctx } = this
        let limit = ctx.query ? ctx.query.limit : 5;
        let data = await this.service.mypageservice.myback(limit)
        await ctx.render('aaa', { data, title: ctx.app.cache ? ctx.app.cache.title : '默认' })
    }
}
  • 在控制器中去获取缓存,如果有缓存,那么就拿缓存的,否则就走默认的。
  • 做到这里,实际计划任务第一次执行不会执行。所以需要让其在启动时拿到cache,这样渲染出的页面就一直不可能是默认了。
  • 第一次启动执行需要配置app.js。在app同目录下建立app.js。
module.exports = app => {
    app.beforeStart(async () => {
        await app.runSchedule('updateTitle')
    })
}
  • 这个updateTitle就是文件名,这样就会启动走一遍计划任务那个文件。
发布了163 篇原创文章 · 获赞 9 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/yehuozhili/article/details/104196088