免编译在JSP中直接写react代码

最近参与了一个历时4-5年的项目,项目是一个后台管理系统,访问量并不高,但经常根据业务方的一些特殊需求,在原有代码添加功能。项目所采用的技术架构还十分老旧,后台采用Struts + Spring + Hibernate, 前台直接使用JSP, 辅以struts与jstl的一些标签。

说实话,自从接受前端MVVM模式后,很久不再使用原始的JSP做前端了,实在是不习惯JSP这种杂乱无章的书写模式。

但项目目前还有线上跑着,维护工作还得继续,同时小组长还告诉我在未完全了解全部业务之前,千万不要尝试进行大面积重构。唉,说实话,我很怀疑这么乱的代码,我最终能完全理解业务。。。

想了下,最终还是想到办法使用原有的React技术栈完成前端工作,这里将方法写出来,供其它遇到这类问题的小伙伴参考一下。

struts的改造

struts的action方法仅完成两种用途,一是页面URL跳转,一是返回ajax数据。具体实现如下:

@Component("testAction")
@Scope("prototype")
public class TestAction {
    private static final String JSON_RESULT = "jsonResult";
    private Map<String, Object> jsonResultMap = Maps.newHashMap();

    ...

    //这类action方法主要负责页面的跳转
    public String gotoPage1(){
        return "page1";
    }

    //这类action方法主要负责以json的格式返回ajax数据
    public String loadTestData(){
        try {
            HttpServletRequest request = ServletActionContext.getRequest();
            String param1 = request.getParameter("param1");
            //做业务操作
            jsonResultMap.put("testData", testData);
            jsonResultMap.put("success", true);
        } catch (Exception e){
            jsonResultMap.put("errMsg", e.getMessage());
            jsonResultMap.put("success", false);
        }
        return JSON_RESULT;
    }

    ...
}

对应的struts配置

<package name="test" namespace="/test" extends="json-default">
    <action name="*" class="testAction" method="{1}">
        <result name="page1">/test/page1.jsp</result>
        <result name="jsonResult" type="json">
            <param name="root">jsonResultMap</param>
            <param name="contentType">text/html;charset=UTF-8</param>
        </result>
    </action>
</package>

前端jsp的改造

前端jsp页面引用一些常用CSS, JS资源,然后主要使用React来渲染页面,代码如下:

page1.jsp

<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<c:set var="ctx" value="${pageContext.request.contextPath}"/>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>title</title>
    <link href="${ctx}/css/bootstrap/bootstrap.min.css" rel="stylesheet" type="text/css"/>
    <link href="${ctx}/css/sb-admin-2/sb-admin-2.min.css" rel="stylesheet" type="text/css"/>
    <link href="${ctx}/css/antd/antd.min.css" rel="stylesheet" type="text/css"/>
    <link href="${ctx}/css/module_common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="reactHolder"></div>
<script type="text/javascript" src="${ctx}/script/lodash/lodash.min.js"></script>
<script type="text/javascript" src="${ctx}/script/react/react.min.js"></script>
<script type="text/javascript" src="${ctx}/script/react/react-dom.min.js"></script>
<script type="text/javascript" src="${ctx}/script/antd/antd.min.js"></script>
<script type="text/javascript" src="${ctx}/script/moment/moment.min.js"></script>
<script type="text/javascript" src="${ctx}/script/axios/axios.min.js"></script>
<script type="text/javascript" src="${ctx}/script/babel-core/browser.min.js"></script>
<script type="text/javascript">
    var __CTX_PATH__='${ctx}';
</script>
<script type="text/babel" src="${ctx}/script/TODO.jsx"></script>
</body>
</html>

这里在外部的jsx文件书写主要的页面渲染逻辑,jsx文件可使用ES6语法进行书写,将由babel5实时翻译为ES5代码(本项目为后台管理系统,可以忍受实时翻译的性能开销)。代码如下:

TODO.jsx代码

const React = window.React;
const ReactDOM = window.ReactDOM;
const _ = window._;
const axios = window.axios;
const antd = window.antd;

class TODO extends React.Component{
    render(){
        return <h1>TODO</h1>;
    }
}

ReactDOM.render(<TODO/>, document.getElementById('reactHolder'));

前端jsx文件的引用

开发中可能会将一些公共方法抽取出来放到一个单独的文件中,而js(x)文件的加载都是异步的,无法保证依赖性。这里可采用umd方式封闭JS模块的方案,如下代码:

;(function(f) {
    // CommonJS
    if (typeof exports === "object" && typeof module !== "undefined") {
        module.exports = f();

        // RequireJS
    } else if (typeof define === "function" && define.amd) {
        define([], f);

        // <script>
    } else {
        var g;
        if (typeof window !== "undefined") {
            g = window;
        } else if (typeof global !== "undefined") {
            g = global;
        } else if (typeof self !== "undefined") {
            g = self;
        } else {
            g = this;
        }
        g.Common = f();
    }
})(function(){
    const util1 = function(){
        ...
    };
    const util2 = function(){
        ...
    };
    return {
        util1,
        util2
    }
});

引用方可以这样写:

;(function(f) {
    // CommonJS
    if (typeof exports === "object" && typeof module !== "undefined") {
        module.exports = f(require('common'));

        // RequireJS
    } else if (typeof define === "function" && define.amd) {
        define(['common'], f);

        // <script>
    } else {
        var g;
        if (typeof window !== "undefined") {
            g = window;
        } else if (typeof global !== "undefined") {
            g = global;
        } else if (typeof self !== "undefined") {
            g = self;
        } else {
            g = this;
        }
        g.App1 = f(g.Common);
    }
})(function(Common){
    const app1Module1 = function(){
        ...
    };
    const app1Module2 = function(){
        ...
    };
    return {
        app1Module1,
        app1Module2
    }
});

当然这么写还是有很大缺点,由于没有引入cmd,amd等JS模块化方案,这里是污染全局变量了。对于老旧项目来说,没有上requirejs或browserify、webpack打包方案,目前也只能这么干了。

总结

虽然维护老旧项目很累,但能采用以前的技术栈写前端代码,这已经很幸福了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张善友的专栏

Web打印组件jatoolsPrinter

应用web化,不论对开发商,还是对用户来说,实在是一种很经济的选择,因为基于web的应用,客户端的规则很简单,容易学习,容易维护,容易发布。但对程序员来说,因为...

83790
来自专栏前端侠2.0

bootstrap-datetimepicker的功能优化

引用:bootstrap 和 bootstrap-datetimepicker CSS文件

45510
来自专栏DeveWork

WordPress删除头部wp_head()多余代码

如果你有查看过你的WordPress博客的“查看源代码”的话,你会发现头部的html代码非常多,而且是密密麻麻,有些像meta name="generator"...

91970
来自专栏张善友的专栏

Expression Web设计工具

从MS company store买了个Expression web软件,在Vista下安装了一个,今后可以用这个工具和Vistual studio 2005协...

201100
来自专栏大史住在大前端

【Recorder.js+百度语音识别】全栈方案技术细节

技术栈:React+recorder-tool.js +recorder.js + Express + Baidu语音识别API

54730
来自专栏walterlv - 吕毅的博客

为带有多种语言的 Jekyll 博客添加多语言选择

发布于 2018-03-06 06:47 更新于 2018-09...

19310
来自专栏自然语言处理

文本挖掘系列文章3

本文主要针对自然语言处理(NLP)过程中,重要基础部分抽取文本内容的预处理。首先我们要意识到预处理的重要性。在大数据的背景下,越来越多的非结构化半结构化文本。如...

14920
来自专栏技术博文

关于微信二次分享,标题变链接的解决方法(二)----代码部分

声明: 本篇博文只是个人工作中的分享总结,仅代表个人观点,虽然解决了不少网友的问题,但同时也引来了一些网友的不满,所以特此声明,当您遇到本博文解决不了的问题,可...

37160
来自专栏云瓣

通过一个demo了解Redux

TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象;使用数据流能帮我们明确了行为对应的响应,这和react的状态可预...

346100
来自专栏IT派

机器学习新手必看:Jupyter Notebook入门指南

【导读】Jupyter Notebook 是一个 Web 应用程序,便于创建和共享文学化程序文档,支持实时代码、数学方程、可视化和 Markdown,其用途包括...

46040

扫码关注云+社区

领取腾讯云代金券