• node.js入门学习(五)--Demo模块化改造


    1、node.js中模块的分类

      1)node.js内置模块(核心,原生)

        所有内置模块在安装node.js时就已经编译成二进制文件,可以直接加载运行(速度较快),部分内置模块,在node.exe这个进程启动时就已经默认加载了,可以直接使用。

      2)文件模块

        require(./common.js); // 去当前js文件的路径下找commom.js文件并导入执行。如果导入时没有指定文件后缀require(./commom),那么就按照commom.js,common.json,commom.node(c/c++编写)去找对应的文件。没有找到对应的文件,就去找common文件夹,如果找到了,看common文件夹里面有没有package.json文件,package.json文件有没有定义入口(main)。

      3)自定义模块(第三方模块)比如mime

        首先会在项目根目录下的node_modules文件夹里面找对应模块,没有找到去上一级目录找,直到盘符目录。

    2、require加载模块注意

      1)所有模块第一次加载完毕后都会有缓存,后面加载就直接读取缓存,避免了二次开销。因为有缓存,所以模块加载只会执行一次。

      2)每次加载优先从缓存中加载,没有才按照node.js加载模块的规则去查找

      3)核心模块在node.js源码编译的时候,都已经编译为二进制执行文件,所以加载速度较快(核心模块的优先级仅次于缓存)

      4)核心模块保存在lib目录下

      5)试图加载一个和核心模块同名的自定义模块是不会成功的,只能使用路径的方式加载。

      6)核心模块只能通过模块名称来加载,不可以通过路径加载

      7)require(./common.js)中"./"相对路径是相对当前模块(或当前js文件)

      8)建议加载文件模块时,文件后缀不要省略

    3、CommonJS规范

      CommonJS规范是为js语言制定的一种模块规范、编程API规范。

      node.js遵循了CommonJS规范

    4、module.exports介绍(两个模块进行通信,如何使用加载的模块)

      需求:如何在index.js加载a.js模块,并且使用a.js模块中的数据和方法 

      第一步:首先新建文件夹node-module-demo

      第二步:在node-module-demo目录新建index.js文件

      第三步:npm init -y

       第四步:a.js和index.js

      a.js

    var a = {
        name: '张三',
        id: 20,
        add: function(x, y) {
            return x + y;
        }
    };
    module.exports = a;

      index.js

    var a = require('./a.js');
    var result = a.add(10, 20);
    console.log(result); // 30
    console.log(a.name); // 张三
    console.log(a.id); // 20

       

      事实上,如果a.js不指定module.exports,默认返回一个对象。

    // a.js代码:
    module.exports.name = "李四";
    module.exports.id = 10;
    module.exports.sayHi = function () {
        console.log('hello您好');
    };
                
    // index.js代码:
    var a = require('./a.js');
    a.sayHi();
    console.log(a.name);
    console.log(a.id);

    5、exports和module.exports的区别
      1)exprots和module.exports指向的是同一个对象
      2)最终require()函数返回的是module.exports中的数据  

      const exports = module.exports;

      所以,不要重新给exports指定,比如像下面代码,就将exports的指向变了:

    exports = {
       a: 1 
    };

    6、将上一篇博客的Demo进行模块化改造
      1)模块化:便于多人合作、便于后期维护
      2)模块化思路:将Demo分成以下模块
              - 服务模块:负责启动服务  app.js
              - 扩展模块:扩展req和res对象  context.js
              - 路由模块:负责路由判断  router.js
              - 业务模块:负责处理具体路由的业务  handler.js
              - 数据操作模块:负责进行数据库操作
              - 配置模块:保存配置信息

      app.js

    // 加载http模块
    var http = require("http");
    
    var context = require("./context.js");
    var router = require("./router.js");
    var config = require('./config.js');
    
    // 创建一个http服务对象
    http.createServer(function(req, res) {
    
        context(req, res);
        router(req, res);
        
    }).listen(config.port, function() {
        console.log('服务器已经启动,请访问http://127.0.0.1:' + config.port);
    });

       context.js

    // context模块作用:扩展req和res对象
    var url = require("url");
    var fs = require("fs");
    var mime = require("mime");
    var _ = require("underscore");
    
    module.exports = function (req, res) {
        req.url = req.url.toLowerCase();
        req.method = req.method.toLowerCase();
    
        if (req.method === 'get') { 
            // 通过url模块的parse方法获取用户get请求提交的数据
            // 第二个参数为true时,urlObj的query属性就是所有的请求参数,类型是json对象
            var urlObj = url.parse(req.url, true);
            // console.log(urlObj);
            req.query = urlObj.query; // query属性就是所有的请求参数,类型是json对象
            req.pathname = urlObj.pathname;
        }
    
        // 封装render函数,将render函数挂在到res对象上;第二个参数是模板数据
        // render函数作用:读取html、css、image等静态资源并返回
        res.render = function (filename, templateData) {
            fs.readFile(filename, function(err, data) {
                if(err) {
                    res.writeHead(404, 'not found', {'Content-Type': 'text/html; charset=utf-8'});
                    res.end("404, not found.");
                    return;
                }
    
                if (templateData) {
                    var fn = _.template(data.toString("utf-8"));
                    data = fn(templateData);
                }
                
                res.setHeader('Content-Type', mime.getType(filename));
                res.end(data);
            });
        };
    }

      router.js

    // 路由模块
    var handler = require("./handler.js");
    
    module.exports = function(req, res) {
        if(req.url === '/' || req.url === '/index') {
            handler.index(req, res);
        }
        else if(req.url === '/list') {
            handler.list(req, res);
        }
        else if(req.url.startsWith('/add')) {
            handler.add(req, res);
        }
        else if(req.url.startsWith('/submit') && req.method === 'get') {
            handler.getSubmit(req, res);
        }
        else if(req.url.startsWith('/submit') && req.method === 'post') {
            handler.postSubmit(req, res);
        }
        else if(req.url.startsWith('/detail') && req.method === 'get') {
            handler.detail(req, res);
        }
        else if(req.url.startsWith('/delete') && req.method === 'get') {
            handler.deleteBook(req, res);
        }
        else if(req.url.startsWith('/edit') && req.method === 'post') {
            handler.editBook(req, res);
        }
        else if(req.url.includes('static')){
            handler.static(req, res);
        }
        else {
            handler.handler404(req, res);
        }
    }

      

      handler.js

    // 业务模块:负责处理具体路由的业务
    var path = require("path");
    var fs = require("fs");
    var querystring = require("querystring");
    
    var config = require('./config.js');
    
    module.exports = index; // 显示主页
    module.exports.list = list; // 显示list页面
    module.exports.add = add; // 显示add页面
    module.exports.getSubmit = getSubmit; // get请求提交数据:添加book
    module.exports.postSubmit = postSubmit; // post请求提交数据:添加book
    module.exports.detail = detail; // 显示detail页面
    module.exports.deleteBook = deleteBook; // 删除指定id的book
    module.exports.editBook = editBook; // 修改book
    module.exports.static = static; // 加载静态资源:css、image、js等
    module.exports.handler404 = handler404; // 处理404
    
    
    // 显示主页
    function index(req, res) {
        res.render(path.join(__dirname, 'pages/index.html'));
    }
    
    // 显示list页面
    function list(req, res) {
        // 第一步:读取data.json文件中的数据,转换为list数组
        readFile(function(bookList) {
            // 第二步:使用模版引擎,将bookList数据和list.html页面结合
            res.render(path.join(__dirname, 'pages/list.html'), {msg:'这是list页面', bookList:bookList});
        });
    }
    
    // 显示add页面
    function add(req, res) {
        res.render(path.join(__dirname, 'pages/add.html'));
    }
    
    // get请求提交数据:添加book
    function getSubmit(req, res) {
        // 用户表单提交get请求数据,保存到data/data.json文件中,新数据以追加的形式添加
        console.log(req.url); // /summit?name=xxx&price=xxx
    
        // 第一步:读取data.json文件中的数据,转换为list数组
        readFile(function(list) {
            // 第二步:获取get请求的参数,将用户提交的数据添加到json数组
            var book = req.query;
            book.id = list[list.length - 1].id + 1;
            list.push(book);
            console.log("添加一条记录后,data.json的内容:"  + JSON.stringify(list));
            
            // 第三步:将json数组写入data.json文件,并重定向到list页面
            writeFile(list, res);
        });
    }
    
    // post请求提交数据:添加book
    function postSubmit(req, res) {
        // 用户表单提交post请求数据,保存到data/data.json文件中,新数据以追加的形式添加
        getPostBodyData(req, function(book) { // post请求数据封装成book(json对象)
            // 读取data.json文件中的数据,转换为list数组
            readFile(function(list) {
                book.id = list[list.length - 1].id + 1;
                list.push(book);
                // console.log("添加一条记录后,data.json的内容:"  + JSON.stringify(list));
                
                // 将json数组写入data.json文件,并重定向到list页面
                writeFile(list, res);
            });
        });
    }
    
    // 显示detail页面
    function detail(req, res) {
        // 第一步:读取data.json文件中的数据,转换为list数组
        readFile(function(bookList) {
            // 第二步:查询指定id的记录
            var book = {};
            for (var i = 0; i < bookList.length; i++) {
                if (bookList[i].id == req.query.id) {
                    book = bookList[i];
                    break;
                }
            }
    
            // 第三步:使用模版引擎,将数据和detail.html页面结合
            res.render(path.join(__dirname, 'pages/detail.html'), {book:book});
        });
    }
    
    // 删除指定id的book
    function deleteBook(req, res) {
        // 第一步:读取data.json文件中的数据,转换为list数组
        readFile(function(bookList) {
            // 第二步:删除指定id的记录
            for (var i = 0; i < bookList.length; i++) {
                if (bookList[i].id == req.query.id) {
                    bookList.splice(i, 1); // 删除起始下标为queryId-1,长度为1的元素
                    break;
                }
            }
    
            // 第三步:将json数组重新写入data.json文件,并重定向到list页面
            writeFile(bookList, res);
        });
    }
    
    // 修改book
    function editBook(req, res) {
        getPostBodyData(req, function(book) { // post请求数据封装成book(json对象)
            console.log("=============book:" + book.id + book.name + book.price);
            // 读取data.json文件中的数据,转换为list数组
            readFile(function(bookList) {                
                for (var i = 0; i < bookList.length; i++) {
                    // console.log("bookList[i].id=" + bookList[i].id);
                    // console.log("book.id=" + book.id);
                    console.log(bookList[i].id == book.id);
                    if(bookList[i].id == book.id) {
                        bookList[i].name = book.name;
                        bookList[i].price = book.price;
                        break;
                    }
                }
            
                // 将json数组重新写入data.json文件,并重定向到list页面
                writeFile(bookList, res);
            });
        });
    }
    
    // 加载静态资源:css、image、js等
    function static(req, res) {
        res.render(path.join(__dirname, req.url));
    }
    
    // 处理404
    function handler404(req, res) {
        res.writeHead(404, 'not found', {'Content-Type': 'text/html; charset=utf-8'});
        res.end("404,not found!");
    }
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 封装读取data.json文件的方法
    function readFile(callback) {
        fs.readFile(config.dataPath, 'utf-8', function (err, data) {
            if(err && err.code !== 'ENOENT') {throw err;}
            console.log("data.json原来的内容:" + data); // [{"name":"xxx","price":"xxx"}]]
            var bookList = JSON.parse(data || '[]'); // json数组
            callback(bookList);
        });
    }
    
    // 封装写入data.json文件的方法
    function writeFile(list, res) { // 参数list的类型:json数组
        fs.writeFile(config.dataPath, JSON.stringify(list), function(err) {
            if(err) throw err;
            console.log('写入成功');
    
            // 重定向
            res.statusCode = 302;
            res.statusMessage = 'Found';
            res.setHeader('Location', '/list');
            res.end();
    
            // 使用模版引擎,将bookList数据和list.html页面结合
            // res.render(path.join(__dirname, 'pages/list.html'), {msg:'这是list页面', bookList:bookList});
        });    
    }
    
    // 封装写入data.json文件的方法
    function writeFile2(data, callback) { // 参数data的类型:字符串
        fs.writeFile(config.dataPath, data, function(err) {
            if(err) throw err;
            console.log('写入成功');
    
            // 调用callback()来执行当写入数据完毕后的操作
            callback();
        });    
    }
    
    // 封装获取用户post提交的数据的方法
    function getPostBodyData(req, callback) {
        var array = []; // 用来保存用户post请求提交的数据
        var postBody;
        req.on('data', function(chunk) {
            // chunk就是浏览器本次data事件提交的一部分数据;chunk是Buffer类型
            console.log("data事件...");
            array.push(chunk);
        });
    
        req.on('end', function() {
            console.log("end事件...");
            // 把array中多个Buffer对象汇总到一个Buffer对象
            var postBody = Buffer.concat(array); // 此处postBody类型:Buffer
            // console.log("postBody: " + postBody); // postBody: name=1&price=1
            // querystring对象可以把表单数据 name=xxx&price=xxx => {"name":"xxx","price":"xxx"}
            postBody = querystring.parse(postBody.toString("utf-8")); // 此处postBody类型:json对象
            callback(postBody);
        });
    }

      config.js

    // 配置模块:保存配置信息
    var path = require('path');
    
    module.exports = {
        port : 8081, // http端口
        dataPath : path.join(__dirname, 'data', 'data.json') // data.json文件的路径
    }
  • 相关阅读:
    vue中处理ie兼容性问题
    vue使用websocket
    vue-cli中使用sass的坑
    really_probe()
    ro.boot.bootreason property设置(androidboot.xxxx bootargs)
    kernel exception vector table
    compile/link misc
    user space syscall/library API misc
    LIUNX SHELL中-a 到-z的解释
    getenforce/setenforce
  • 原文地址:https://www.cnblogs.com/xy-ouyang/p/11145470.html
Copyright © 2020-2023  润新知