资讯专栏INFORMATION COLUMN

从零开始搭建React同构应用(四):搭建Koa Server & 完善SSR

fizz / 1834人阅读

摘要:从零开始搭建同构应用四搭建完善上一篇我们使用了的方式测试了,这篇文章来讲如何在前文的基础上搭建一个,实现真正意义上的。至此,一个简单的框架已经搭建完成,剩下的工作就是结合工作需要,在里面添砖加瓦啦。

从零开始搭建React同构应用(四):搭建Koa Server & 完善SSR

上一篇我们使用了CLI的方式测试了SSR,这篇文章来讲如何在前文的基础上搭建一个Koa Server,实现真正意义上的SSR。

demo在这

主要内容

Koa搭建

完善SSR逻辑

Koa搭建

新建server/index.js:

我们使用Koa v2.0的版本;

npm i koa@next -S;

先搭建一个最简单的服务器

const Koa = require("koa");
const app = new Koa();

app.use(ctx => {
  ctx.body = "Hello Koa";
});

app.listen(8088, _ => {
    console.log("server started")
});

添加一个npm script

  "scripts": {
    "start": "node --harmony server/index", //启动HTTP服务器
    "watch": "webpack -d -w --progress --colors --bs",
    "test-server": "anywhere -p 18341 -d ./build",
    "dist": "cross-env NODE_ENV="production" webpack -p",
    "test-ssr": "node --harmony test/cli.js"
  },

执行

npm run start

这样一个最简单的Koa框架就搭建起来,下面就可以往里面填充东西了。

配置router

在添加router之前,我们需要加载webpack编译生成的HTML模板,这里我们没有使用EJS,HBS等Nodejs渲染引擎,我们而是使用cheerio来帮助我们操作HTML,cheerio可以让我们在Node环境下像使用jQuery一样来操作HTML,非常容易上手,这里是它的API,基本和jQuery无差别。

在server/index.js增加:

const cheerio = require("cheerio");
const fs = require("fs");
const path = require("path");
const Promise = require("bluebird");
const serve = require("koa-static-server");
const readFileAsync = Promise.promisify(fs.readFile);

/**
 * 读取HTML模版,返回cheerio实例
 * @param path
 * @return {Promise.<*>}
 */
async function loadHTMLTemplate(path) {
    try {
        let content = await readFileAsync(path);
        return cheerio.load(content);

    } catch (e) {
        console.error(e);
        return false;
    }
}

我们使用koa-better-router中间件作为路由模块。我们添加一个router,在server/index.js增加:

const router = require("koa-better-router")().loadMethods();

router.get("/", async(ctx, next) => {

    let $ = await loadHTMLTemplate(path.resolve(__dirname, "../build/index.html"));

    if (!$) {
        return ctx.body = null;
    }

    return ctx.body = $.html();


});

app.use(router.middleware());

执行

npm run start

我们会发现CSS,JS等文件没有被加载进来,因为没有对应的路由,下面我们配置静态文件服务。

配置静态文件服务

我们不可能为所有的资源都写router,因此我们需要配置一个静态文件服务。这里我使用了koa-static-server中间件。

我们以build目录作为资源文件根目录,在server/index.js增加:

const serve = require("koa-static-server");
const readFileAsync = Promise.promisify(fs.readFile);
const RES_PATH = path.resolve(__dirname, "../build/");

//hfs
app.use(serve({rootDir: RES_PATH}));

执行

npm run start

资源可以被正确载入了。

完善SSR逻辑

我们先添加一个API接口,方便模拟Node端的接口调用,在server/index.js增加:

//API接口
router.get("/api/todo_list", async(ctx, next) => {

    return ctx.body = ["11", "222"];

});

我们还是以Index.jsx为例:

test/cli.js中的代码copy过来。修改/路由

const Koa = require("koa");
const app = new Koa();
const router = require("koa-better-router")().loadMethods();
const cheerio = require("cheerio");
const fs = require("fs");
const path = require("path");
const Promise = require("bluebird");
const serve = require("koa-static-server");
const readFileAsync = Promise.promisify(fs.readFile);
const RES_PATH = path.resolve(__dirname, "../build/");
const fetch = require("isomorphic-fetch");

router.get("/", async(ctx, next) => {

    let $ = await loadHTMLTemplate(path.resolve(__dirname, "../build/index.html"));

    if (!$) {
        return ctx.body = null;
    }

    let IndexBundle = require("../build_server/index.bundle.js");

    //fetch接口数据
    let todoList = await(await fetch("http://localhost:8088/api/todo_list")).json();

    let initialData = {todoList};

    let instance = React.createElement(IndexBundle.default, initialData);

    let str = renderToString(instance);


    $("#wrap").html(str);

    //前后端数据要同步
    let syncScript = ``;

    $("head").append(syncScript);

    return ctx.body = $.html();


});

这里要注意前后端数据要同步,我把Node端获取的数据放在window._SERVER_DATA中了,前端渲染的时候会优先使用window._SERVER_DATA来渲染。

if (process.browser) {

    //初始数据,用于和server render数据同步
    let initialData = window._SERVER_DATA || {};

    let store = createStore(reducers, initialData, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

    let App = connect(_ => _)(Layout);//用connect包装一下,这里只用到mapStateToProps,而且不对state加以过滤

    ReactDOM.render(
        
            
        ,
        document.getElementById("wrap"));
}

执行

npm run start

访问http://127.0.0.1:8088/

可以看到#wrap中已经被填充渲染好的HTML文本了,Node端和前端的数据也同步了。 ^_^

至此,一个简单的SSR框架已经搭建完成,剩下的工作就是结合工作需要,在里面添砖加瓦啦。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81407.html

相关文章

  • 从零开始搭建React同构应用(三):配置SSR

    摘要:从零开始搭建同构应用三配置这篇文章来讲解来配置,我们先从最简单的方法开始,用的方式模拟实现。影响生产环境下执行效率。最后权衡下,还是决定使用现在多一套编译配置的方案。新建,写入以下内容以为例,注意不能少。 从零开始搭建React同构应用(三):配置SSR 这篇文章来讲解来配置server side render,我们先从最简单的方法开始,用cli的方式模拟实现SSR。 demo在这里 ...

    jzzlee 评论0 收藏0
  • 基于 Webpack 4 多入口生成模板用于服务端渲染的方案及实战

    摘要:原作者原链接基于多入口生成模板用于服务端渲染的方案及实战法律声明警告本作品遵循署名非商业性使用禁止演绎未本地化版本协议发布。这是什么背景现代化的前端项目中很多都使用了客户端渲染的单页面应用。 原作者:@LinuxerPHL原链接:基于 Webpack 4 多入口生成模板用于服务端渲染的方案及实战 法律声明 警告:本作品遵循 署名-非商业性使用-禁止演绎3.0 未本地化版本(CC BY-...

    big_cat 评论0 收藏0
  • 基于 Webpack 4 多入口生成模板用于服务端渲染的方案及实战

    摘要:原作者原博文地址基于多入口生成模板用于服务端渲染的方案及实战法律声明警告本作品遵循署名非商业性使用禁止演绎未本地化版本协议发布。这是什么背景现代化的前端项目中很多都使用了客户端渲染的单页面应用。 原作者:@LinuxerPHL原博文地址: 基于 Webpack 4 多入口生成模板用于服务端渲染的方案及实战 法律声明 警告:本作品遵循 署名-非商业性使用-禁止演绎3.0 未本地化版本(...

    Lavender 评论0 收藏0
  • 从零开始最小实现 react 服务器渲染

    摘要:从零开始最小实现服务器渲染前言最近在写的时候想到,如果我部分代码提供,部分代码支持,那我应该如何写呢不想拆成个服务的情况下而且最近写的项目里面也用过一些服务端渲染,如,自己也搭过的项目,确实开发体验都非常友好,但是友好归友好,具体又是如何实 showImg(https://segmentfault.com/img/bVMbjB?w=1794&h=648); 从零开始最小实现 react...

    cheukyin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<