以freemarker和vue为例深入理解后端渲染、前端渲染

目录

 

一、springboot集成freemaker demo

二、echarts图表,echarts已由百度迁入apache,官网。

三、vue.js 后端使用----非node环境vue-cli构建的vue使用方式  官网

四、freemarker后端渲染,vue前端渲染

五、案例:html转pdf的几个方案


一、springboot集成freemaker demo

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

配置

spring:
  freemarker:
    suffix: .ftl
    content-type: text/html
    enabled: true
    cache: false #开发阶段不缓存
    charset: UTF-8
    settings:
      number_format: '0.##' #全局数字格式化
    template-loader-path: 'classpath:/templates/'
server:
  port: 8080

控制器这里不返回json数据,而是视图"demo"或modelAndView,即前后端不分离开发模式,这里返回的视图用的模板引擎可以是thymelyfe,freemarker,velocity,beetle等

package com.example.demo.controller;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author haodongdong
 * @since 2021-03-17
 */
@Controller
@RequestMapping("/test")
public class TestController {

    @GetMapping("/t1")
    public ModelAndView backendRender(){
        Map map= new HashMap<String,String>();
        map.put("test1","value1");
        map.put("test2",null);
        map.put("test3",123.456);
        map.put("test4", Arrays.asList(1,2,3));
        map.put("test5","{\"num\":\"123\"}");
        map.put("test6",map);
        Map map2= new HashMap<>();
        map2.put("mymap","mymapvlue");
        map.put("test7",map2);
        return new ModelAndView("demo",map);

    }

}

模板页面demo.ftl,默认放置在/resources/templates/下面的。该demo.ftl可以使用freemarker的一些语法及少量内建函数进行数据展示,如果需要进行js计算,则需要一些dom操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <#--如果是非基本类型,不要直接取,否则报错-->
    <div>${test1}</div>
    <#--html非空判断-->
    <div>${test2!}</div>
    <#--标签上的非空判断-->
    <#if !test2??>
        <div>非空判断<span>
    </#if>
    <#--会适用配置文件的全局设置-->
    <div>${test3}</div>
    <#--freemarker中有些字符串,数字处理内建函数可用-->
    <div>${test3?string.percent}</div>
    <#--html中遍历集合,-->
    <#list test4 as item>
        <span>${item}<span>
    </#list>
    <#--遍历map,拿到keys集合,keys为内置参数-->
    <#list test6?keys as mykey>
      <#if mykey=="test3">
         <div>${mykey}---${test6["${mykey}"]}</div>
      </#if>
    </#list>
    <#--遍历map,拿到values集合,values为内置参数-->
    <#list test7?values as myvalue>
        <div>${myvalue}</div>
    </#list>

</div>
</body>
<script type="text/javascript">
    /*script也可以取值,使用各种freemarker语法*/
    console.log("${test1}")
    console.log(${test5})
    /*取json字符串的value*/
    console.log(${test5}.num)
    console.log(JSON.stringify(${test5}))
    <#list test4 as item>
        console.log(${item})
    </#list>
</script>

</html>

效果图

二、echarts图表,echarts已由百度迁入apache,官网

这里引入CDN的echarts.js资源,也可以下载echats.comom.min.js(比较小)到本地引入使用。

js部分增加 var myChart = echarts.init(document.getElementById('main'));并对该对象设置各种options属性值即可。这里用freemarker语法进行取值,更多属性设置参考官网。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <#--如果是非基本类型,不要直接取,否则报错-->
    <div>${test1}</div>
    <#--html非空判断-->
    <div>${test2!}</div>
    <#--标签上的非空判断-->
    <#if !test2??>
        <div>非空判断<span>
    </#if>
    <#--会适用配置文件的全局设置-->
    <div>${test3}</div>
    <#--freemarker中有些字符串,数字处理内建函数可用-->
    <div>${test3?string.percent}</div>
    <#--html中遍历集合,-->
    <#list test4 as item>
        <span>${item}<span>
    </#list>
    <#--遍历map,拿到keys集合,keys为内置参数-->
    <#list test6?keys as mykey>
      <#if mykey=="test3">
         <div>${mykey}---${test6["${mykey}"]}</div>
      </#if>
    </#list>
    <#--遍历map,拿到values集合,values为内置参数-->
    <#list test7?values as myvalue>
        <div>${myvalue}</div>
    </#list>
    <#--echarts图表-->
     <!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
     <div id="main" style="width: 600px;height:400px;float: right;margin-left: 100px" ></div>


</div>
</body>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@5.1.1/dist/echarts.min.js"></script>

<script type="text/javascript">
    /*script也可以取值,使用各种freemarker语法*/
    console.log("${test1}")
    console.log(${test5})
    /*取json字符串的value*/
    console.log(${test5}.num)
    console.log(JSON.stringify(${test5}))
    <#list test4 as item>
        console.log(${item})
    </#list>
    /*echarts图表js*/
    var myChart = echarts.init(document.getElementById('main'));
    // 指定图表的配置项和数据
    var option = {
        title:{
            text:'分组统计'
        },
        tooltip: {
            trigger: 'axis',
            axisPointer: {
                type: 'cross',
                crossStyle: {
                    color: '#999'
                }
            }
        },
        toolbox: {
            feature: {
                dataView: {show: true, readOnly: false},
                magicType: {show: true, type: ['line', 'bar']},
                restore: {show: true},
                saveAsImage: {show: true}
            }
        },
        legend: {
            data: ['总订单数', '总消费金额', '总优惠金额','总实收金额']
        },
        xAxis: [
            {
                type: 'category',
                data: [<#list test4 as item>${item},</#list>],
                axisPointer: {
                    type: 'shadow'
                }
            }
        ],
        yAxis: [
            {
                type: 'value',
                name: '金额(元)',
                min: 0,
                // max: 250,
                // interval: 50,
                axisLabel: {
                    formatter: '{value}'
                }
            },
            {
                type: 'value',
                name: '数量',
                min: 0,
                // max: 25,
                // interval: 5,
                axisLabel: {
                    formatter: '{value}'
                }
            }
        ],
        series: [
            {
                name: '总订单数',
                type: 'line',
                data: 666,
            },
            {
                name: '总消费金额',
                type: 'bar',
                data: 999,
                markPoint: {
                    data: [
                        {type: 'max', name: '最大值'},
                        {type: 'min', name: '最小值'}
                    ]
                },
                markLine: {
                    data: [
                        {type: 'average', name: '平均值'}
                    ]
                }
            },
            {
                name: '总实收金额',
                type: 'bar',
                data: [11,22,33]
            },
            {
                name: '总优惠金额',
                type: 'bar',
                yAxisIndex: 1,
                data: [111,222,333]
            }
        ]
    };


    // 使用刚指定的配置项和数据显示图表。
    myChart.setOption(option);


</script>

</html>

效果图

三、vue.js 后端使用----非node环境vue-cli构建的vue使用方式  官网

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <#--如果是非基本类型,不要直接取,否则报错-->
    <div>${test1}</div>
    <#--html非空判断-->
    <div>${test2!}</div>
    <#--标签上的非空判断-->
    <#if !test2??>
        <div>非空判断<span>
    </#if>
    <#--会适用配置文件的全局设置-->
    <div>${test3}</div>
    <#--freemarker中有些字符串,数字处理内建函数可用-->
    <div>${test3?string.percent}</div>
    <#--html中遍历集合,-->
    <#list test4 as item>
        <span>${item}<span>
    </#list>
    <#--遍历map,拿到keys集合,keys为内置参数-->
    <#list test6?keys as mykey>
      <#if mykey=="test3">
         <div>${mykey}---${test6["${mykey}"]}</div>
      </#if>
    </#list>
    <#--遍历map,拿到values集合,values为内置参数-->
    <#list test7?values as myvalue>
        <div>${myvalue}</div>
    </#list>
    <#--echarts图表-->
     <!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
     <div id="main" style="width: 600px;height:400px;float: right;margin-left: 100px" ></div>
    <#--vue数据绑定-->
    <div id="myvue">{{aa}}--{{bb}}</div>

</div>
</body>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@5.1.1/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>


<script type="text/javascript">
    /*script也可以取值,使用各种freemarker语法*/
    console.log("${test1}")
    console.log(${test5})
    /*取json字符串的value*/
    console.log(${test5}.num)
    console.log(JSON.stringify(${test5}))
    <#list test4 as item>
        console.log(${item})
    </#list>
    /*echarts图表js*/
    var myChart = echarts.init(document.getElementById('main'));
    // 指定图表的配置项和数据
    var option = {
        title:{
            text:'分组统计'
        },
        tooltip: {
            trigger: 'axis',
            axisPointer: {
                type: 'cross',
                crossStyle: {
                    color: '#999'
                }
            }
        },
        toolbox: {
            feature: {
                dataView: {show: true, readOnly: false},
                magicType: {show: true, type: ['line', 'bar']},
                restore: {show: true},
                saveAsImage: {show: true}
            }
        },
        legend: {
            data: ['总订单数', '总消费金额', '总优惠金额','总实收金额']
        },
        xAxis: [
            {
                type: 'category',
                data: [<#list test4 as item>${item},</#list>],
                axisPointer: {
                    type: 'shadow'
                }
            }
        ],
        yAxis: [
            {
                type: 'value',
                name: '金额(元)',
                min: 0,
                // max: 250,
                // interval: 50,
                axisLabel: {
                    formatter: '{value}'
                }
            },
            {
                type: 'value',
                name: '数量',
                min: 0,
                // max: 25,
                // interval: 5,
                axisLabel: {
                    formatter: '{value}'
                }
            }
        ],
        series: [
            {
                name: '总订单数',
                type: 'line',
                data: 666,
            },
            {
                name: '总消费金额',
                type: 'bar',
                data: 999,
                markPoint: {
                    data: [
                        {type: 'max', name: '最大值'},
                        {type: 'min', name: '最小值'}
                    ]
                },
                markLine: {
                    data: [
                        {type: 'average', name: '平均值'}
                    ]
                }
            },
            {
                name: '总实收金额',
                type: 'bar',
                data: [11,22,33]
            },
            {
                name: '总优惠金额',
                type: 'bar',
                yAxisIndex: 1,
                data: [111,222,333]
            }
        ]
    };


    // 使用刚指定的配置项和数据显示图表。
    myChart.setOption(option);
    //vue相关
    var vm = new Vue({
        el: '#myvue',
        data: {
            aa:'',
            bb:'',
        },
        mounted(){
           let data1=${test5};
           this.aa=data1.num;
           let data2="${test1}";
           this.bb=data2
        }
    })
    console.log(vm)




</script>

</html>

效果图

四、freemarker后端渲染,vue前端渲染

有后端返回的数据可以看到,freemarker的${xxx}语法已替换为数值,而vue的{{}}语法并没有被替换,这个是浏览器根据vue.js语法在前端完成的替换,固freemarker后端渲染,vue前端渲染。

五、案例:html转pdf的几个方案参考,参考

方案1:html转pdf,使用vue js工具库html2canvas,效果比较差,该pdf为canvas画得图片,可能会有图表截断现象;

方案2:网页另存为再使用其他三方工具转换,如果网页中有其它js如vue.js引用,则这样另存为会保存一份xx.html及一个存放三方js,css的文件夹,当然这个可以将外部引入的js,css内容全量内嵌到html页面

方案3:使用vue或freemarker生成html,使用vue.js生成的html{{}}是没有替换的,vue是前端渲染,故只能使用后端渲染技术freemarker等。如本例可以使用restemplate请求http://localhost:8080/test/t1接口获取到html字符串,下载到前端,使用其他三方工具转换为pdf,同方案二,外部js,css需要内嵌到html,比较麻烦但较方案2灵活,可以定义要下载的页面内容

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值