koa 项目中的知识点 - 详情页

过程描述

  • 使用 RPC 通信从后台服务器获取数据
  • 模板渲染

    include 子模版
    xss 过滤、模板 helper 函数


将 ES6 模板字符串改造成模板引擎

  • 基础使用
    const vm = require('vm');
    
    const user = {
        name: 'haha'
    }
    
    console.log(vm.runInNewContext(`

    ${user.name}

    `
    , { user })); //

    haha

  • XSS 过滤
    console.log(vm.runInNewContext(`

    ${_(user.name)}

    `
    , { // 模板字符串里可以直接写函数调用 user, _: function(markup) { // XSS 过滤规则 if (!markup) return ''; return String(markup) .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/'/g, ''') .replace(/"/g, '"') } }));
  • 子模版
    const vm = require('vm');
    
    const templateMap = {
        templateA: '`

    ${include("templateB")}

    `'
    , templateB: '`

    hahahaha

    `'
    } const context = { include(name) { return templateMap[name]() }, _: function(markup) { if (!markup) return ''; return String(markup) .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/'/g, ''') .replace(/"/g, '"') } } Object.keys(templateMap).forEach(key=> { // 将templateMap中的value值为渲染之后的 const temp = templateMap[key]; templateMap[key] = vm.runInNewContext(` (function() {return ${temp}}) `, context); }) console.log(templateMap['templateA']()); //

    hahahaha

  • 封装
    const fs = require('fs');
    const vm = require('vm');
    
    // 缓存
    const templateCache = {};
    
    // 创建模板渲染context
    const templateContext = vm.createContext({
        include: function (name, data) {
            const template = templateCache[name] || createTemplate(name)
            return template(data);
        }
    });
    
    /**
     * @param {*} templatePath 模板路径
     * @returns 渲染好的html字符串
     */
    function createTemplate(templatePath) {
    
        templateCache[templatePath] = vm.runInContext(
            // data 是在这个页面中可以使用的数据
            `(function (data) {
                with (data) {
                    return \`${fs.readFileSync(templatePath, 'utf-8')}\`
                }
            })`,
            templateContext
        );
    
        return templateCache[templatePath]
    }
    
    module.exports = createTemplate
    

代码结构

koa 项目中的知识点 - 详情页_第1张图片

detail-server

  • 目录结构
    koa 项目中的知识点 - 详情页_第2张图片
  • lib/geeknode-rpc-server.js
    const RPC = require('./rpc-server');
    
    /**
     * 因为所有服务用的包头格式都一样,不一样的只有protobuf协议,所以这里可以将这段逻辑封成一个模块
     * 
     * 日常做项目的时候一定要注意把重复代码做封装
     */
    module.exports = function (protobufRequestSchema, protobufResponseSchema) {
        return new RPC({
            // 解码请求包
            decodeRequest(buffer) {
                const seq = buffer.readUInt32BE();
    
                return {
                    seq: seq,
                    result: protobufRequestSchema.decode(buffer.slice(8))
                }
            },
            // 判断请求包是不是接收完成
            isCompleteRequest(buffer) {
                const bodyLength = buffer.readUInt32BE(4);
    
                return 8 + bodyLength
            },
            // 编码返回包
            encodeResponse(data, seq) {
                const body = protobufResponseSchema.encode(data);
    
                const head = Buffer.alloc(8);
                head.writeUInt32BE(seq);
                head.writeUInt32BE(body.length, 4);
    
                return Buffer.concat([head, body]);
            }
        })
    }
    
  • lib/rpc-server.js
    const net = require("net");
    
    module.exports = class RPC {
        constructor({ encodeResponse, decodeRequest, isCompleteRequest }) {
            this.encodeResponse = encodeResponse;
            this.decodeRequest = decodeRequest;
            this.isCompleteRequest = isCompleteRequest;
        }
    
        createServer(callback) {
            let buffer = null;
    
            const tcpServer = net.createServer((socket) => {
                socket.on('data', (data) => {
                    buffer = (buffer && buffer.length > 0) ?
                        Buffer.concat([buffer, data]) : // 有遗留数据才做拼接操作
                        data;
    
                    let checkLength = null;
                    while (buffer && (checkLength = this.isCompleteRequest(buffer))) {
                        let requestBuffer = null;
                        if (checkLength == buffer.length) {
                            requestBuffer = buffer;
                            buffer = null;
    
                        } else {
                            requestBuffer = buffer.slice(0, checkLength);
                            buffer = buffer.slice(checkLength);
                        }
    
                        const request = this.decodeRequest(requestBuffer);
                        callback(
                            {   // request
                                body: request.result,
                                socket
                            },
                            {   // response
                                end: (data) => {
                                    const buffer = this.encodeResponse(data, request.seq)
                                    socket.write(buffer);
                                }
                            }
                        );
                    }
                })
            });
    
            return {
                listen() {
                    tcpServer.listen.apply(tcpServer, arguments)
                }
            }
        }
    }
    
  • mockdata/column.js
    module.exports = [{
        id: 232,
        type: 2,
        column_cover: "https://static001.geekbang.org/resource/image/42/78/42db8ef7b28bcdc26410141dd97b8178.jpg",
        column_title: "Node.js开发实战",
        column_subtitle: "带你开发一个完整的Node.js项目",
        author_name: "杨浩",
        author_intro: "腾讯高级工程师",
        column_intro: `

    课程背景

    Node.js 拥有广大的 JavaScript 程序员基础并且完全开源,所以它拥有一个强大的开发者社区。依靠繁荣的社区力量,现在已经发展出成熟的技术体系与庞大的生态。它被广泛地用在 Web 服务、开发工作流、客户端应用等诸多领域。其中,在 Web 服务开发这个领域,业界对 Node.js 的接受程度最高。

    对于很多前端开发者来说,掌握 Node.js 的基础知识并不难,难点在于如何按照后端工程师的思维,基于 Node.js 来一步步构建项目,其中涉及诸如 RPC 调用、系统运维以及进程管理等前端工程师较少涉及到的领域。

    因此,本课程站在一个前端工程师的角度,讲解如何基于 Node.js 开发一个完整的项目,从一开始的技术预研再到实际开发、性能优化以及最终的框架架构搭建和工程化建设,带你完整体验一遍前端工程师使用 Node.js进行项目开发会碰到的各种常见场景和技术难点,学完课程之后,你将能够熟练运用 Node.js 进行大型项目的设计和开发。

    讲师简介

    杨浩,腾讯高级工程师。之前曾在腾讯视频负责 Web 端的相关工作,设计并完成了腾讯视频整站大部分页面的 Node.js 化。

    腾讯视频是从 2015 年开始使用 Node.js 对整站进行改造的,杨浩与同事一起从零开始一步一步把整个 Node.js 的开发运维步骤打通,搭建了一个运行于后台服务和浏览器前端之间的 Node.js 中间层用作 SSR(Server Side Rendering),以提高搜索引擎抓取的效果以及首屏展现的速度。

    在 2018 年由 InfoQ 举办的 ArchSummit 全球架构师峰会深圳站上,杨浩也对在腾讯视频进行 Node.js 改造这一经历做了公开分享:

    https://time.geekbang.org/dailylesson/detail/100016617

    课程收获

    1. Node.js 开发必备基础知识;
    2. 使用 Node.js 构建 BFF 层;
    3. 一个完整项目的开发重构实战;
    4. 性能优化和工程化建设核心方法。

    更新频率

    1. 9月18日上线 1-8 讲,9月25日开始固定为每周三更新。
    2. 全部课程预计将于 12 月 11 日前更新完毕。

    如何在电脑端观看视频

    1. 用浏览器访问 https://time.geekbang.org ,登录极客时间账号;
    2. 然后在“讲堂”板块选择“视频课程”标签,点击相应的视频课程即可观看。

    订阅须知

    1. 本课程为视频课程,共55讲,订阅成功后即可通过“极客时间”App端、小程序端、Web端永久观看;
    2. 由于视频内容为虚拟商品,一经订阅,概不退款;
    3. 企业批量购买请点击“企业充值”了解详情,可支持员工选课,企业支付;
    4. 在课程学习过程中,如有任何问题,请邮件联系 [email protected]
    5. 戳此申请学生认证,订阅课程一律 5 折。
    `
    , column_unit: "55讲", sub_count: 2915, update_frequency: "约550分钟", column_price: 12900, column_price_market: 12900, articles: [{ id: 0, is_video_preview: true, article_title: '第一课' }, { id: 1, is_video_preview: false, article_title: '第二课' }, { id: 2, is_video_preview: false, article_title: '第三课' }, { id: 3, is_video_preview: false, article_title: '第四课' }] }, { id: 278, type: 2, column_cover: "https://static001.geekbang.org/resource/image/a0/19/a0ad97e899aa22023951bde5813e0819.jpg", column_title: "移动端自动化测试实战", column_subtitle: "一步一步带你成为测试高手", author_name: "思寒", author_intro: "TesterHome 社区测试专家、霍格沃兹测试学院创始人", column_intro: `

    课程背景

    随着行业的竞争加剧,互联网产品迭代的速度越来越快,QA 与测试工程师都需要在越来越短的测试周期内充分保证质量。 App 测试则面临着更多的挑战,比如多端发布、多版本发布、多机型发布等等,这导致了手工测试很难完全胜任,自动化测试、持续测试就成为了团队的必要需求。作为传统的测试工程师与研发工程师,需要适应行业的变化,并积极做出应对。

    学习自动化测试有两个难点,除了其本身拥有一定的技术门槛之外,实战经验才是学习自动化测试的最大难点。部分初中级测试工程师往往在掌握了基本的编程与自动化测试框架 API 后,会认为自己已经掌握了自动化测试,便开始投入到实际开发中,却又掉进各种各样的技术债和经验债里,痛苦不堪。

    因此,整个课程将分为基础篇、框架篇和实战篇三个阶段,十个章节,由浅入深地带大家掌握 app 自动化测试技能。通过学习这门课程,你将能够掌握正确的自动化测试方法、理解自动化测试的技术体系,可以解决工作中遇到的自动化测试难题,并能把这项技术熟练应用在自己公司的测试体系中。

    讲师简介

    思寒,TesterHome 社区测试专家,霍格沃兹测试学院创始人。

    先后任职于阿里巴巴、百度、雪球等公司,承担测试工程师、测试开发工程师、测试架构师等工作。

    2013年,移动互联网开始迅猛发展,催生了对 app 测试的需求,思寒老师与其团队决定物色一个优秀的移动测试框架,在进行大量调研之后,Appium 以其优秀的特性脱颖而出。思寒老师也开始投身于这个开源框架的建设中,积极参与 Appium 框架的中文翻译、向 Appium 提交测试用例与 pull request,同时也开始在历任的公司中将 Appium 用于公司内的移动测试,积累了非常多的落地经验。

    课程收获

    1. 系统掌握 Appium 自动化测试框架;
    2. 全面提升移动端自动化测试实战能力;
    3. 掌握自动遍历测试及兼容性测试技巧;
    4. 构建多设备管理平台与持续集成体系。

    更新频率

    1. 10月23日上线 1-9 讲,10月30日开始固定为每周三更新。
    2. 全部课程预计将于 2020 年 1 月 22 日前更新完毕。

    如何在电脑端观看视频

    1. 用浏览器访问 https://time.geekbang.org ,登录极客时间账号;
    2. 然后在“讲堂”板块选择“视频课程”标签,点击相应的视频课程即可观看。

    订阅须知

    1. 本课程为视频课程,共58讲,订阅成功后即可通过“极客时间”App端、小程序端、Web端永久观看;
    2. 由于视频内容为虚拟商品,一经订阅,概不退款;
    3. 企业批量购买请点击“企业充值”了解详情,可支持员工选课,企业支付;
    4. 在课程学习过程中,如有任何问题,请邮件联系 [email protected]
    5. 戳此申请学生认证,订阅课程一律 5 折。
    `
    , column_unit: "55讲", sub_count: 1471, update_frequency: "约550分钟", column_price: 9900, column_price_market: 9900, articles: [{ id: 0, is_video_preview: true, article_title: '第一课' }, { id: 1, is_video_preview: false, article_title: '第二课' }, { id: 2, is_video_preview: false, article_title: '第三课' }, { id: 3, is_video_preview: false, article_title: '第四课' }] }, { id: 211, type: 1, column_cover: "https://static001.geekbang.org/resource/image/b6/1a/b683240befccbdcaa86da8f382d3a11a.jpg", column_subtitle: "Facebook研发效率工作法", column_title: "研发效率破局之道", author_intro: "前Facebook内部工具团队Tech Lead", author_name: "葛俊", column_intro: `

    如果你问中国和美国互联网公司都有什么差别,很多人会回答:低效加班文化。最近爆出的996大讨论,正反映出国内很多公司拼工时的做法,以及程序员群体对这种做法的反感情绪。

    “拼工时”说到底是为提高产出,但国内互联网产业已经步入从野蛮生长到精耕细作的过渡期,人力成本逐渐提高,通过糙快猛打拼和996加班去抢占市场获得机会的成功案例越来越少。至此,只有提高效能才是出路。

    事实上,越来越多的公司意识到研发效能的重要性,很多百人研发规模的公司开始组建专门的效能团队,着手提高公司的整体效能。

    然而,因为软件开发的灵活性,导致研发效能的提高需要关注的点太多、可以使用的方法也很多,结果就是不知道从何处着手,或者是花了精力、加大了投入却看不到效果,甚至产出抵不上投入。

    • 整个研发过程环节很多,到底该从哪里下手?
    • 通过设置的度量标准来看,某个指标明显提高了,但却看不到总体效果,而且引发了团队抱怨以及团队间的矛盾。这到底是怎么回事?
    • 团队成员虽然执行力强,但是不愿意思考,应该创建怎样的公司文化来激发创新?

    在这个专栏中,葛俊将基于硅谷和国内多年的从业经验,从研发流程、工程方法、个人效能、管理和文化这四个方向入手,系统介绍研发效能的理论和实践,探讨协同、开发、测试、运维等关键研发步骤中高效的工程方法。

    葛俊,前 Facebook内部工具团队Tech Lead,开源项目Phabricator的主要作者之一。在互联网行业奋斗的15年里,他曾任职于微软、Facebook、华为,以及硅谷和国内的两家创业公司。

    在此期间,他在研发效能团队工作过,也在使用效能流程和工具的产品团队工作过,也有过主导推进研发效能的丰富经历。可以说,他目睹了硅谷以及国内的大型企业、创业公司推进研发效能的成功经验与失败经历,同时总结了一套适用的高效能引入方法,希望在这个专栏里和你一起学习、进步。

    专栏模块

    专栏共35讲,分为5个模块。

    1. 研发效能综述

    讲解研发效能的定义、模型,并着重介绍什么是度量,以及度量的正确使用方法。借此,希望你能够梳理出研发效能的主脉络,构建出一幅清晰的知识图谱。

    2. 研发流程

    与你分享敏捷、持续集成、持续交付、DevOps、团队协同等话题。通过这个模块,希望帮助你深入理解研发过程中的关键流程,以及流程优化的基本原则,从而能够针对实际情况找到最合适自己的工程实践,让软件开发的整个流程更加顺畅、高效。

    3. 工程方法

    与你讨论研发流程(包括开发、测试、运维等)中各关键步骤的高效工程方法,并分析软件开发的趋势和热点,比如智能化、云原生等。通过这个模块,希望能够加深你对这些工程方法的了解,帮助你找到针对具体环节提高效能的方法,以及学会如何正确地使用这些方法。

    4. 个人效能

    聚集探讨如何提高个人效能,具体涉及深度工作、Git、命令行、VIM、工具环境集成等内容,旨在帮助你提高技术专精程度,持续成长。每个开发人员都应该提高自己的效能,只有这样才能持续学习持续提高,避免被业务拖着跑的现象。

    5. 管理和文化

    系统分析硅谷尤其是Facebook的工程师文化,并根据国内公司的具体落地经验,给出推荐的文化引入和建设方法。

    限时福利

    1. 订阅后,分享专属海报,每邀请一位好友订阅有奖励。
    2. 戳此添加社群管理员,进入技术交流&福利群
    3. 戳此申请学生认证,订阅课程一律 5 折。

    订阅须知

    1. 本专栏为订阅专栏,更新时间为2019年8月23日至2019年11月13日。订阅成功后,即可通过“极客时间”App端、小程序端、Web端永久阅读。
    2. 本专栏更新时间为每周一、三、五,形式为图文 + 音频,共计35期,定价99元。
    3. 企业批量购买请点击“企业充值”了解详情,可支持员工选课,企业支付。
    4. 本专栏为虚拟商品,一经订阅,概不退款。
    5. 在专栏阅读过程中,如有任何问题,请邮件联系[email protected]
    `
    , column_unit: "35讲", sub_count: 3034, update_frequency: "每周一 / 三 / 五更新", column_price: 9900, column_price_market: 9900, articles: [{ id: 0, is_video_preview: true, article_title: '第一课' }, { id: 1, is_video_preview: false, article_title: '第二课' }, { id: 2, is_video_preview: false, article_title: '第三课' }, { id: 3, is_video_preview: false, article_title: '第四课' }] }, { id: 180, type: 3, column_cover: "https://static001.geekbang.org/resource/image/cc/fb/ccb3acf6fcfab959aee1d800b882f7fb.png", column_subtitle: "程序员都应该学学怎么表达", column_title: "如何做好一场技术演讲", author_intro: "极客时间编辑部", author_name: "极客时间", column_intro: `

    内容简介

    程序员都应该学学怎么表达。

    据调查,75% 的人在公众场合演讲时会感到非常恐慌。但是,公众演讲是每个人都需要面对的“刁难”,无论是学生时期的课题总结、毕业答辩,还是工作之后的公司内部项目汇报、外部技术交流,这些场合都需要我们具备优秀的演讲能力,才能更好地向别人展示我们的工作成果,让优秀的技术方案广为传播。

    公众场合的演讲能力成为职场中越来越重要的一项基础技能。 谁知道,未来的某一天,会不会是你,站在新闻发布会的台上,向下面黑压压的人群宣布一款你公司的技术产品呢? 又或者,你作为一位技术大咖,在一场技术大会上向大家讲述你的技术棋局?

    就像编程一样,演讲能力也是可以习得的。正如天生口吃的德摩斯梯尼通过自己的不断苦练也能成为古希腊著名的演说家一样,成为一个优秀的技术演讲者,也是有径可循的。

    这里收录的 6 篇文章,包含多位技术大牛的演讲经验总结,从技术人员的角度来剖析做好一场技术演讲的术与道。

    内容目录

    本系列共 6 篇文章,前 5 篇文章沿着“内容准备—心态调节—陈述技巧—形象管理”依次展开,每篇文章的主题各有侧重点。最后一篇是综合性的经验阐述,和前五篇提到的几个关键点既相互对照,又加入了节奏掌控、回答问题的技巧等独创性内容。

    限时福利

    1. 订阅后,分享专属海报,每邀请一位好友订阅有奖励。
    2. 戳此添加社群管理员,进入技术交流&福利群
    ↵↵

    适宜人群

    想提升演讲能力,向公众有效传播自己的技术方案、理念和观点的人;想学习更多的内容布局、组织方法,让自己的演讲更生动、更形象的人;想了解如何在公众场合管理好自己的身体语言,打造良好的个人形象的人。

    购买须知

    1. 本系列共六篇文章,现已全部上线,购买成功后,即可通过“极客时间”App端、小程序端、Web端永久观看;
    2. 由于文字内容为虚拟商品,一经购买,概不退款;
    3. 在阅读过程中,如有任何问题,请邮件联系 [email protected]
    `
    , column_unit: "6讲", sub_count: 17209, update_frequency: "约150分钟", column_price: 100, column_price_market: 100, articles: [{ id: 0, is_video_preview: true, article_title: '第一课' }, { id: 1, is_video_preview: false, article_title: '第二课' }, { id: 2, is_video_preview: false, article_title: '第三课' }, { id: 3, is_video_preview: false, article_title: '第四课' }] }, { id: 198, type: 1, column_cover: "https://static001.geekbang.org/resource/image/7f/6d/7f9edc0620b7083074b21cc9036ffa6d.jpg", column_subtitle: "基于DDD的微服务拆分与设计", column_title: "DDD实战课", author_intro: "人保高级架构师", author_name: "欧创新", column_intro: `

    随着分布式技术的快速兴起,我们已经进入到了微服务架构时代。微服务架构的出现,很好地实现了应用之间的解耦,解决了单体应用扩展性和弹性伸缩能力不足的问题。随着业务的复杂度升级,其好处自然不言而喻。

    那微服务到底怎么拆分和设计才算合理,拆多小才叫微服务?这个尖锐的问题,在业内一直被热议。紧接着,继阿里巴巴成功转型中台战略之后,微服务设计和拆分再至风口浪尖,对于众多公司来说,都是一个不小的挑战。

    那有没有好的方法来指导微服务和中台的设计呢?当然有,你也可能耳闻过,那就是领域驱动设计(DDD)。

    • 你可能会疑惑,早在2003年就诞生的DDD,怎么来指导“迟到”近20年才大热的微服务设计?
    • 怎么认识和学习这个抽象又庞大的知识体系?
    • 怎么应用,怎么实践?
    • 落地后它究竟能解决旧系统的哪些问题?

    本专栏将重点解决以上问题,力求用浅显易懂的案例,深入DDD的核心知识体系与设计思想,带你掌握一套完整而系统的基于DDD的微服务拆分与设计方法,明确从战略设计到战术设计的微服务标准设计过程,助力落地边界清晰、可持续演进的微服务架构。

    你将获得

    • 洞悉DDD必知必会10大核心概念
    • 掌握事件风暴与领域建模
    • 上手中台业务建模与设计
    • 实战设计清晰的微服务架构

    作者简介

    欧创新,人保高级架构师,拥有十多年的软件架构经验。他热衷于采用DDD的设计方法实现中台业务建模,专注基于DDD的微服务设计和开发。目前,他正在深入探索传统企业中台数字化转型的技术和方法体系。

    课程设计

    专栏共21讲,分为基础篇、进阶篇和实战篇三部分。

    基础篇主要讲解DDD的核心知识体系,具体包括:领域、子域、限界上下文、实体、值对象、聚合和聚合根等概念。借助浅显易懂的案例,带你理解它们以及它们之间的合作、依赖关系。

    进阶篇主要讲解领域事件、DDD分层架构、几种常见的微服务架构模型以及中台设计思想等内容。带你深入微服务分层设计的底层原理与具体实现。

    实战篇作为本课程的重点,包含多个实战小项目,例如中台和领域建模的实战、微服务设计实战等。最后作者会用一个典型的案例将DDD所有的知识点串联,带你走一遍基于DDD的微服务设计全流程。

    另外,实战篇还补充有“如何借鉴微服务的设计思想来设计前端应用”“微服务设计原则”以及“分布式架构设计的关键注意事项”,以完善实战体系。

    限时福利

    1. 订阅后,分享专属海报,每邀请一位好友订阅有奖励。
    2. 戳此添加社群管理员,进入技术交流&福利群
    3. 戳此申请学生认证,订阅课程一律 5 折。
    ↵↵

    订阅须知

    1. 本专栏为订阅专栏,更新时间为2019年10月14日至2019年12月02日。订阅成功后,即可通过“极客时间”App端、小程序端、Web端永久阅读。
    2. 本专栏更新时间为每周一、三、五,形式为图文+音频,共计21期,定价68元。
    3. 企业批量购买请点击“企业充值”了解详情,可支持员工选课,企业支付。
    4. 本专栏为虚拟商品,一经订阅,概不退款。
    5. 在专栏阅读过程中,如有任何问题,请邮件联系[email protected]
    `
    , column_unit: "21讲", sub_count: 3878, update_frequency: "每周一 / 三 / 五更新", column_price: 5900, column_price_market: 5900, articles: [{ id: 0, is_video_preview: false, article_title: '第一课' }] }]
  • mockdata/comment.js
    module.exports = [
        {
            id: 1,
            avatar: 'https://static001.geekbang.org/account/avatar/00/0f/52/62/1b3ebed5.jpg',
            name: '僵尸浩',
            isTop: true,
            content: '哈哈哈哈',
            publishDate: '今天',
            commentNum: 10,
            praiseNum: 5
        },
        {
            id: 2,
            avatar: 'https://static001.geekbang.org/account/avatar/00/0f/52/62/1b3ebed5.jpg',
            name: '极客主编',
            isTop: true,
            content: '我来送大礼了!!',
            publishDate: '上周',
            commentNum: 10,
            praiseNum: 2
        },
        {
            id: 3,
            avatar: 'https://static001.geekbang.org/account/avatar/00/0f/52/62/1b3ebed5.jpg',
            name: '极客老板',
            isTop: true,
            content: '我来发股票了!!!',
            publishDate: '十年前',
            commentNum: 10,
            praiseNum: 0
        }
    ]
    
  • detail.proto
    message Column {
        required int32 id = 1;
        required string column_cover = 2;
        required string column_title = 3;
        required string column_subtitle = 4;
        required string author_name = 5;
        required string author_intro = 6;
        required string column_intro = 7;
        required string column_unit = 8;
        required uint32 sub_count = 9;
        required string update_frequency = 10;
        required uint32 column_price = 11;
        optional uint32 column_price_market = 12;
        repeated Article articles = 13;
    }
    message Article {
        required uint32 id = 1;
        required bool is_video_preview = 2;
        required string article_title = 3;
    }
    
    message ColumnResponse {
        required Column column = 1;
        repeated Column recommendColumns = 2;
    }
    message ColumnRequest {
        required int32 columnid = 1;
    }
    
  • start.js
    const fs = require('fs')
    const protobuf = require('protocol-buffers');
    const schemas = protobuf(
        fs.readFileSync(`${__dirname}/detail.proto`)
    );
    
    // 假数据
    const columnData = require('./mockdata/column')
    
    /**
     * 服务端的编解包逻辑
     */
    const server = require('./lib/geeknode-rpc-server')(schemas.ColumnRequest, schemas.ColumnResponse);
    
    server
        .createServer((request, response) => {
            // 因为都是假数据,这里就没有使用栏目id。真实项目会拿这个columnid去请求数据库
            const columnid = request.body;
    
            // 直接返回假数据
            response.end({
                column: columnData[0],
                recommendColumns: [columnData[1], columnData[2]]
            });
        })
        .listen(4000, ()=> {
            console.log('rpc server listened: 4000')
        });
    

node-client

  • 目录结构
    koa 项目中的知识点 - 详情页_第3张图片
  • template/index.js
    const fs = require('fs');
    const vm = require('vm');
    
    const templateCache = {};
    
    const templateContext = vm.createContext({
        include: function (name, data) {
            const template = templateCache[name] || createTemplate(name)
            return template(data);
        }
    });
    
    function createTemplate(templatePath) {
    
        templateCache[templatePath] = vm.runInContext(
            `(function (data) {
                with (data) {
                    return \`${fs.readFileSync(templatePath, 'utf-8')}\`
                }
            })`,
            templateContext
        );
    
        return templateCache[templatePath]
    }
    
    module.exports = createTemplate
    
  • client.js
    const EasySock = require('easy_sock');
    
    const protobuf = require('protocol-buffers')
    const fs = require('fs');
    const schemas = protobuf(fs.readFileSync(`${__dirname}/detail.proto`));
    
    const easySock = new EasySock({ 
        ip: '127.0.0.1',
        port: 4000,
        timeout: 500,
        keepAlive: true
    })
    
    easySock.encode = function(data, seq) {
        const body = schemas.ColumnRequest.encode(data);
    
        const head = Buffer.alloc(8);
        head.writeInt32BE(seq);
        head.writeInt32BE(body.length, 4);
    
        return Buffer.concat([head, body])
    }
    
    easySock.decode = function(buffer) {
        const seq = buffer.readInt32BE();
        const body = schemas.ColumnResponse.decode(buffer.slice(8));
        
        return {
            result: body,
            seq
        }
    }
    
    easySock.isReceiveComplete = function(buffer) {
        if (buffer.length < 8) {
            return 0
        }
        const bodyLength = buffer.readInt32BE(4);
    
        if (buffer.length >= bodyLength + 8) {
            return bodyLength + 8
            
        } else {
            return 0
        }
    }
    
    module.exports = easySock;
    
  • detail.proto
    message Column {
        required int32 id = 1;
        required string column_cover = 2;
        required string column_title = 3;
        required string column_subtitle = 4;
        required string author_name = 5;
        required string author_intro = 6;
        required string column_intro = 7;
        required string column_unit = 8;
        required uint32 sub_count = 9;
        required string update_frequency = 10;
        required uint32 column_price = 11;
        optional uint32 column_price_market = 12;
        repeated Article articles = 13;
    }
    message Article {
        required uint32 id = 1;
        required bool is_video_preview = 2;
        required string article_title = 3;
    }
    
    message ColumnResponse {
        required Column column = 1;
        repeated Column recommendColumns = 2;
    }
    message ColumnRequest {
        required int32 columnid = 1;
    }
    
  • index.js
    const mount = require('koa-mount');
    const static = require('koa-static')
    const app = new (require('koa'));
    const rpcClient = require('./client');
    const template = require('./template');
    
    const detailTemplate = template(__dirname + '/template/index.html');
    
    app.use(mount('/static', static(`${__dirname}/source/static/`)))
    
    app.use(async (ctx) => {
        if (!ctx.query.columnid) {
            ctx.status = 400;
            ctx.body = 'invalid columnid';
            return 
        }
    
        const result = await new Promise((resolve, reject) => {
            rpcClient.write({
                columnid: ctx.query.columnid
            }, function (err, data) {
                err ? reject(err) : resolve(data)
            })
        })
    
        ctx.status = 200;
        
        ctx.body = detailTemplate(result);
    })
    
    app.listen(3000)
    
    // module.exports = app;
    

你可能感兴趣的:(Node.js,javascript,vue.js,前端)