原生开发移动web单页面(step by step)5——nodejs服务器的搭建

简介: 由于后面的开发要基于服务器提供的条件,因此这里很有必要从头到尾搭建一个本地服务器并且模拟提供相关的服务。首先去nodejs的官网下载安装包安装,然后创建一个文件夹,如下面的目录新建一个目录架构,如下图架构目录图lib文件夹主要放置后端自...

由于后面的开发要基于服务器提供的条件,因此这里很有必要从头到尾搭建一个本地服务器并且模拟提供相关的服务。首先去nodejs的官网下载安装包安装,然后创建一个文件夹,如下面的目录新建一个目录架构,如下图


架构目录图

lib文件夹主要放置后端自己写的js,主要提供后端服务
public文件夹主要放置前端的文件,也就是html,css和js代码
index.js是服务器的入口主程序, 可以处理前端的请求

调出命令行控制台将位置设置到该文件夹目录, 然后安装mime模块,输入命令如下

npm install mime

生成如下的目录架构,如图


生成架构目录图

基础环境已完成,现在开始写服务代码,打开index.js,写入下列代码

var http = require("http"),
    url = require("url"),
    root = __dirname,
    serverFile = require("./lib/serverFile.js");

var server = http.createServer(function (req, res) {
    var urlObj = url.parse(req.url, true);
    var path = urlObj.pathname.toLowerCase();
    if (req.method === "GET") {
        serverFile.getFile(req, res, path, root);
    }
}).listen(3000);

console.log("Server started on localhost: 3000");

然后再lib文件夹中创建serverFile.js, 编写下列代码

var mime = require("mime"),
    fs = require("fs");

function serveFile(req, res, pathname, root) {
    fs.stat(pathname, function (err, stat) {
        if (err) {
            if ("ENOENT" == err.code) {
                res.statusCode = 404;
                res.end("404");
            }
            else {
                res.statusCode = 500;
                res.end("Internal Server Error");
            }
        }
        else {
            res.setHeader("Content-Length", stat.size);
            res.writeHead(200, { "Content-Type": mime.getType(pathname) });
            var stream = fs.createReadStream(pathname);
            stream.pipe(res);
            stream.on("error", function (err) {
                res.statusCode = 500;
                res.end("Internal Server error");
            });
        }
    });
}

exports.getFile = function (req, res, key, root) {
    if (key.indexOf("/public/")  === 0) {
        serveFile(req, res, root + key, root);
    }
    else if (key === "/serve") {
        serveFile(req, res, root + "/public/serve/index.html", root);
    }
    else {
        res.statusCode = 404;
        res.end("404");
    }
}

在public文件夹中创建一个serve文件夹,把上一篇的html,css和js文件夹复制进去。接着在命令行控制台中输入

node index.js

接着在控制台上提示
Server started on localhost:3000
说明本地服务搭建成功,然后在浏览器中输入localhost:3000/serve或者在同一局域网上的手机端浏览器输入本地ip加端口号加文件名路径,比如本机ip为192.168.1.13,那么手机浏览器上输入 http://192.168.1.13:3000/serve

现在调整前端的目录结构,如下图


前端目录

将index.html放在serve根目录下,然后其中html放置html模板,将index.html上的html模板全部迁出到在html文件夹中, js文件夹放置js文件,css放置css文件,修改index.html的代码
index.html代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>首页</title>
    <link rel="stylesheet" href="/public/serve/css/index.css">
    <script src="/public/serve/js/page.js"></script>
    <script src="/public/serve/js/app.js"></script>
</head>
<body>
    <div class="pageContainer"></div>
    <script src="/public/serve/js/entry.js"></script>
    <script src="/public/serve/js/register.js"></script>
    <script src="/public/serve/js/goal.js"></script>
    <script src="/public/serve/js/index.js"></script>
</body>
</html>

这里使用绝对路径引用,基于服务器的入口index.js的位置为准。
page.js和app.js放在head元素里面,将之前的首页命名为entry, 这里每个js代表一个Page页面, index.js代表定义初始化程序。

引入服务器后,每个页面模板都是以异步的形式加载,因此Page的渲染过程需要改变,之前都直接加载在一个页面上,直接同步读取,现在异步读取,读取完后加载到页面上,才开始绑定事件。这个过程需要放在读取完毕后的回调函数中执行,因此要在Page的对象上加入新的一个方法,代码如下

render: function (fn) {
    console.error("must be override");
},

修改_initialize方法,代码如下

_initialize: function (container) {
    this.getDomObj(container);
    this._addEventListeners();
},

这个需要开发者自己手动调用设置模板html字符串,然后调用回调函数。
修改Page对象的构造函数, 代码如下

function Page() {
    this.domList = {};
    this.eventList = [];
}

接着修改App对象, 修改render的步骤,代码如下

render: function (page) {
    if (this.currentPage == page) return false;
    if (this.currentPage) this.currentPage._dispose();
    this.currentPage = page;

    var pageContainer = this.pageContainer;
    page.render(function (html) {
        pageContainer.innerHTML = html;
        page._initialize(pageContainer);
    });
}

在修改Page的实例对象之前, 首先先加入一个原型方法,它的目标是提供静态文件,如果静态文件已存在就直接调用,不存在则通过ajax去调取,然后缓存起来。以免多次请求,代码如下

fetch: function (url, bk) {
    var self = Page.prototype;
    var that = this;
    if (self.fetch[url]) {
        bk.call(this, self.fetch[url]);
    }
    else {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.onload = function () {
            var result = xhr.responseText;
            self.fetch[url] = result;
            bk.call(that, result);
        };
        xhr.send(null);
    }
}

然后增加各页面的render方法, 代码如下
entry.js的代码

var entryPage = App.createPage("index", {
    render: function (fn) {
        this.fetch("/public/serve/html/entry.html", function (text) {
            fn(text);
        });
    },
    getDomObj: function (dom) {
        this.attachDom(".btn-group", "btnGroup", dom)
            .attachDom(".index-container", "container", dom)
            .attachSlide("container", this.startFn, this.moveFn, this.endFn)
            .attachTap("btnGroup", this.tapHandler, false);
    },
    tapHandler: function (ev) {
        var target = ev.target;
        var action = target.dataset.action;
        switch (action) {
            case "register": 
                app.render(registerPage);
                break;
            case "login": 
                app.render(loginPage);
                break;
        }
    },
    startFn: function (ev) {},
    moveFn: function (ev) {},
    endFn: function (ev) {
        var speed = 1000 * ev.deltaX / ev.elapsed;
        if (speed > 200) {
            app.render(registerPage);
        }
        else if (speed < -200) {
            app.render(loginPage);
        }
    }
});

login.js的代码

var loginPage = App.createPage("login", {
    render: function (fn) {
        this.fetch("/public/serve/html/login.html", function (text) {
            fn(text);
        });
    },
    getDomObj: function (dom) {
        this.attachDom("[data-action='back']", "backBtn", dom)
            .attachDom(".login-form", "form", dom)
            .attachDom(".login-container", "container", dom)
            .attachSlide("container", this.startFn, this.moveFn, this.endFn)
            .attachTap("backBtn", this.tapBackHandler, false)
            .attachEvent("form", "submit", this.formSubmitHandler, false);
    },
    tapBackHandler: function (ev) {
        app.render(entryPage);
    },
    formSubmitHandler: function (ev) {
        ev.preventDefault();
        var form = ev.target;
        var name = form.name.value;
        var password = form.password.value;
        app.render(goalPage);
    },
    startFn: function (ev) {},
    moveFn: function (ev) {},
    endFn: function (ev) {
        var speed = 1000 * ev.deltaX / ev.elapsed;
        if (speed > 200) {
            app.render(entryPage);
        }
    }
});

register.js的代码

var registerPage = App.createPage("register", {
    render: function (fn) {
        this.fetch("/public/serve/html/register.html", function (text) {
            fn(text);
        });
    },
    getDomObj: function (dom) {
        this.attachDom("[data-action='back']", "backBtn", dom)
            .attachDom(".register-form", "form", dom)
            .attachDom(".register-container", "container", dom)
            .attachSlide("container", this.startFn, this.moveFn, this.endFn)
            .attachTap("backBtn", this.tapBackHandler, false)
            .attachEvent("form", "submit", this.submitHandler, false);
    },
    tapBackHandler: function (ev) {
        app.render(entryPage);
    },
    submitHandler: function (ev) {
        ev.preventDefault();
        var form = ev.target;
        var name = form.name.value;
        var password = form.password.value;
        var agree = form.agree.checked;
        if (agree) {
            app.render(goalPage);
        }
    },
    startFn: function (ev) {},
    moveFn: function (ev) {},
    endFn: function (ev) {
        var speed = 1000 * ev.deltaX / ev.elapsed;
        if (speed > 200) {
            app.render(entryPage);
        }
    }
});

goal.js的代码

var goalPage = App.createPage("goal", {
    render: function (fn) {
        this.fetch("/public/serve/html/goal.html", function (text) {
            fn(text);
        });
    },
    getDomObj: function () {}
});

index.js的代码

var app = new App();
app.render(entryPage);

总结: 这一篇建立一个最基础的本地服务器,后续还会根据上面的添加修改些小功能。不过不会引入太多的功能, 只会引入与前端展示有关的特性。 有了前后端,因此一个项目的结构就基本成型了,然后顺势的调整项目目录, 将页面结构重构, 减少第一次进入页面的数据加载量, 个人还是建议把页面结构彻底的分离开,每个js放置一个Page对象,真正等到项目上线的时候使用构建工具来合成及压缩。

后续更新:下一篇介绍history的api, 并将其引入App对象中,这样页面切换可以通过前进后退键了, 这样就有三个手段切换页面了。切换手段齐全了, 后续几篇接下来开始增加页面切换的效果, 让页面更加生动。

案例链接


原生开发移动web单页面(step by step)1——传统页面的开发
原生开发移动web单页面(step by step)2——Page对象
原生开发移动web单页面(step by step)3——App对象
原生开发移动web单页面(step by step)4——tap事件与slide事件
原生开发移动web单页面(step by step)6——history api应用

相关文章
|
8天前
|
JavaScript 前端开发
vue配合axios连接express搭建的node服务器接口_简单案例
文章介绍了如何使用Express框架搭建一个简单的Node服务器,并使用Vue结合Axios进行前端开发和接口调用,同时讨论了开发过程中遇到的跨域问题及其解决方案。
12 0
vue配合axios连接express搭建的node服务器接口_简单案例
|
20天前
|
存储
在 Web 中判断页面是不是刷新
【9月更文挑战第10天】在Web开发中,判断页面是否刷新有多种方法:1) 监听`popstate`事件,检测用户是否通过历史记录访问页面;2) 记录并比较页面加载时间戳,若相差极小,则可能为刷新;3) 利用本地存储设置特定值,若该值不存在或不符合预期,则页面可能被刷新。然而,这些方法并非绝对准确。
|
6天前
|
JavaScript Linux 开发工具
如何将nodejs项目程序部署到阿里云服务器上
该文章详细描述了将Node.js项目部署到阿里云服务器的步骤,包括服务器环境配置、项目上传及使用PM2进行服务管理的过程。
|
1月前
|
开发框架 JavaScript 前端开发
html,web页面朗读文字,朗读中文,朗读英文
html,web页面朗读文字,朗读中文,朗读英文
|
19天前
|
数据处理 Python
Django视图:构建动态Web页面的核心技术
Django视图:构建动态Web页面的核心技术
|
2月前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
67 0
|
2月前
|
Rust 安全 开发者
惊爆!Xamarin 携手机器学习,开启智能应用新纪元,个性化体验与跨平台优势完美融合大揭秘!
【8月更文挑战第31天】随着互联网的发展,Web应用对性能和安全性要求不断提高。Rust凭借卓越的性能、内存安全及丰富生态,成为构建高性能Web服务器的理想选择。本文通过一个简单示例,展示如何使用Rust和Actix-web框架搭建基本Web服务器,从创建项目到运行服务器全程指导,帮助读者领略Rust在Web后端开发中的强大能力。通过实践,读者可以体验到Rust在性能和安全性方面的优势,以及其在Web开发领域的巨大潜力。
33 0
|
2月前
|
前端开发 大数据 数据库
🔥大数据洪流下的决战:JSF 表格组件如何做到毫秒级响应?揭秘背后的性能魔法!💪
【8月更文挑战第31天】在 Web 应用中,表格组件常用于展示和操作数据,但在大数据量下性能会成瓶颈。本文介绍在 JavaServer Faces(JSF)中优化表格组件的方法,包括数据处理、分页及懒加载等技术。通过后端分页或懒加载按需加载数据,减少不必要的数据加载和优化数据库查询,并利用缓存机制减少数据库访问次数,从而提高表格组件的响应速度和整体性能。掌握这些最佳实践对开发高性能 JSF 应用至关重要。
45 0
|
2月前
|
前端开发 安全 开发者
JSF文件上传,让Web应用如虎添翼!一招实现文件上传,让用户爱不释手!
【8月更文挑战第31天】在现代Web应用开发中,文件上传是重要功能之一。JSF(JavaServer Faces)框架提供了强大的文件上传支持,简化了开发流程。本文将介绍JSF文件上传的基本步骤:创建前端表单、处理上传文件的后端Action类、将文件保存到服务器指定目录以及返回结果页面。通过示例代码,我们将展示如何利用JSF实现文件上传功能,包括使用`h:inputFile`控件和`ManagedBean`处理上传逻辑。此外,JSF文件上传还具备类型安全、解耦合和灵活性等优点,有助于提升程序的健壮性和可维护性。
27 0
下一篇
无影云桌面