blob: b1130021b89ca400bc488c278b8f44cacfdebb3d [file] [log] [blame]
<!doctype html>
<html data-n-head-ssr lang="zh-CN" data-n-head="%7B%22lang%22:%7B%22ssr%22:%22zh-CN%22%7D%7D">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>服务端渲染 - 跨平台方案 - 应用篇 - 使用手册 - Apache ECharts</title><meta data-n-head="ssr" data-hid="docsearch:language" name="docsearch:language" content="zh"><base href="/echarts-handbook/"><link data-n-head="ssr" rel="shortcut icon" type="image/png" href="https://echarts.apache.org/zh/images/favicon.png"><link data-n-head="ssr" rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css"><link data-n-head="ssr" rel="stylesheet" href="https://echarts.apache.org/zh/css/main.css"><link data-n-head="ssr" rel="stylesheet" href="https://lib.baomitu.com/docsearch.js/2.6.3/docsearch.min.css"><link data-n-head="ssr" rel="preconnect" href="https://14syrifeto-dsn.algolia.net" crossorigin=""><script data-n-head="ssr" src="https://cdn.staticfile.net/jquery/3.7.1/jquery.min.js"></script><script data-n-head="ssr" src="https://lib.baomitu.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script><script data-n-head="ssr" src="https://lib.baomitu.com/docsearch.js/2.6.3/docsearch.min.js"></script><link rel="preload" href="/echarts-handbook/_nuxt/0323e7b.js" as="script"><link rel="preload" href="/echarts-handbook/_nuxt/js/fa73816a85b29fbf7562.js" as="script"><link rel="preload" href="/echarts-handbook/_nuxt/css/8117eb7.css" as="style"><link rel="preload" href="/echarts-handbook/_nuxt/js/b964c20bb1562bc48da6.js" as="script"><link rel="preload" href="/echarts-handbook/_nuxt/css/093f748.css" as="style"><link rel="preload" href="/echarts-handbook/_nuxt/js/d28cad802cfdf9e4dbad.js" as="script"><link rel="preload" href="/echarts-handbook/_nuxt/css/ba9ec8a.css" as="style"><link rel="preload" href="/echarts-handbook/_nuxt/js/bbca8d7f75f3cc39fb47.js" as="script"><link rel="preload" href="/echarts-handbook/_nuxt/css/3556c50.css" as="style"><link rel="preload" href="/echarts-handbook/_nuxt/js/9d86a6602b3f4dd6c979.js" as="script"><link rel="stylesheet" href="/echarts-handbook/_nuxt/css/8117eb7.css"><link rel="stylesheet" href="/echarts-handbook/_nuxt/css/093f748.css"><link rel="stylesheet" href="/echarts-handbook/_nuxt/css/ba9ec8a.css"><link rel="stylesheet" href="/echarts-handbook/_nuxt/css/3556c50.css">
</head>
<body>
<div data-server-rendered="true" id="__nuxt"><!----><div id="__layout"><div><nav role="navigation" class="navbar navbar-default navbar-fixed-top"><div class="container-fluid"><div class="navbar-header"><button type="button" data-toggle="collapse" data-target="#navbar-collapse" aria-expanded="false" class="navbar-toggle collapsed"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a href="https://echarts.apache.org/zh/index.html" class="navbar-brand"><img src="https://echarts.apache.org/zh/images/logo.png?_v_=20240226" alt="echarts logo" class="navbar-logo"></a></div><div id="navbar-collapse" class="collapse navbar-collapse"><ul class="nav navbar-nav navbar-left"><li id="nav-index"><a href="https://echarts.apache.org/zh/index.html">首页</a></li><li id="nav-doc" class="dropdown"><a href="#" data-toggle="dropdown" class="dropdown-toggle">文档<b class="caret"></b></a><ul class="dropdown-menu"><li><a href="https://echarts.apache.org/zh/feature.html">特性</a></li><li><a href="https://echarts.apache.org/handbook/zh/get-started/">使用手册</a></li><li><a href="https://echarts.apache.org/zh/api.html">API</a></li><li><a href="https://echarts.apache.org/zh/option.html">配置项手册</a></li><li><a href="https://echarts.apache.org/zh/changelog.html">版本记录</a></li><li><a href="https://echarts.apache.org/zh/faq.html">常见问题</a></li><li><a href="https://echarts.apache.org/handbook/zh/basics/release-note/v5-upgrade-guide">v5 升级指南</a></li></ul></li><li id="nav-download" class="dropdown"><a href="#" data-toggle="dropdown" class="dropdown-toggle">下载<b class="caret"></b></a><ul class="dropdown-menu"><li><a href="https://echarts.apache.org/zh/download.html">下载</a></li><li><a href="https://echarts.apache.org/zh/download-theme.html">主题下载</a></li><li><a href="https://echarts.apache.org/zh/download-extension.html">扩展下载</a></li></ul></li><li id="nav-examples"><a href="https://echarts.apache.org/examples/zh/index.html">示例</a></li><li id="nav-resources"><a href="#" data-toggle="dropdown" class="dropdown-toggle">资源<b class="caret"></b></a><ul class="dropdown-menu"><li><a href="https://echarts.apache.org/zh/spreadsheet.html">表格工具</a></li><li><a href="https://echarts.apache.org/zh/theme-builder.html">主题构建工具</a></li><li><a href="https://echarts.apache.org/zh/cheat-sheet.html">术语速查手册</a></li><li><a href="https://echarts.apache.org/zh/resources.html">更多资源</a></li></ul></li><li id="nav-contribute"><a href="#" data-toggle="dropdown" class="dropdown-toggle">社区<b class="caret"></b></a><ul class="dropdown-menu"><li><a href="https://echarts.apache.org/zh/events.html">活动</a></li><li><a href="https://echarts.apache.org/zh/committers.html">贡献者列表</a></li><li><a href="https://echarts.apache.org/zh/maillist.html">邮件列表</a></li><li><a href="https://echarts.apache.org/zh/contributing.html">如何贡献</a></li><li><a href="https://echarts.apache.org/zh/dependencies.html">依赖项</a></li><li><a href="https://echarts.apache.org/zh/coding-standard.html">代码规范</a></li><li><a href="https://github.com/apache/echarts" target="_blank">源码(GitHub)<svg width="15px" height="15px" viewbox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="icon-external-link"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li><a href="https://github.com/apache/echarts/issues" target="_blank">Issues(GitHub)<svg width="15px" height="15px" viewbox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="icon-external-link"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li></ul></li><li id="nav-others"><a href="#" data-toggle="dropdown" class="dropdown-toggle">ASF<b class="caret"></b></a><ul class="dropdown-menu"><li><a href="https://www.apache.org/" target="_blank">Apache Software Foundation<svg width="15px" height="15px" viewbox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="icon-external-link"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li><a href="https://echarts.apache.org/zh/security.html">安全</a></li><li><a href="https://www.apache.org/licenses/" target="_blank">版权<svg width="15px" height="15px" viewbox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="icon-external-link"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li><a href="https://www.apache.org/events/current-event" target="_blank">活动<svg width="15px" height="15px" viewbox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="icon-external-link"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li><a href="https://www.apache.org/foundation/sponsorship.html" target="_blank">捐赠<svg width="15px" height="15px" viewbox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="icon-external-link"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li><a href="https://www.apache.org/foundation/thanks.html" target="_blank">鸣谢<svg width="15px" height="15px" viewbox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="icon-external-link"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li></ul></li></ul><ul class="nav navbar-nav navbar-right"><li id="nav-homeen"><a href="javascript:;" onclick='changeLang("en")'>EN</a></li></ul></div></div></nav> <div class="page-main"><div class="page-content handbook-content single-page container-fluid row flex-xl-nowrap"><div class="bd-sidebar col-sm-3 col-md-2 closed"><div class="sidebar-search"><input id="handbook-search-input" type="search" placeholder="搜索手册"> <div class="search-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg></div></div> <div class="bd-docs-nav"><ul class="nav bd-sidenav nav-root level0"><li class="nav-item"><a href="/echarts-handbook/zh/get-started" class="nav-link"><span class="title">快速上手</span></a> <ul class="nav bd-sidenav level1" style="display:none"></ul></li><li class="nav-item"><div class="nav-link"><span class="title">入门篇</span></div> <ul class="nav bd-sidenav level1"><li class="nav-item"><a href="/echarts-handbook/zh/basics/download" class="nav-link"><span class="title">获取 ECharts</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/basics/import" class="nav-link"><span class="title">在项目中引入 ECharts</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><!----><li class="nav-item"><a href="/echarts-handbook/zh/basics/help" class="nav-link"><span class="title">寻求帮助</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><li class="nav-item"><a class="nav-link"><span class="title">版本特性</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level2" style="display:none"><li class="nav-item"><a href="/echarts-handbook/zh/basics/release-note/v5-feature" class="nav-link"><span class="title">ECharts 5 特性介绍</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/basics/release-note/v5-upgrade-guide" class="nav-link"><span class="title">v4 升级 v5 指南</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/basics/release-note/5-2-0" class="nav-link"><span class="title">5.2</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/basics/release-note/5-3-0" class="nav-link"><span class="title">5.3</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/basics/release-note/5-4-0" class="nav-link"><span class="title">5.4</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/basics/release-note/5-5-0" class="nav-link"><span class="title">5.5</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li></ul></li></ul></li><li class="nav-item"><div class="nav-link"><span class="title">概念篇</span></div> <ul class="nav bd-sidenav level1"><li class="nav-item"><a href="/echarts-handbook/zh/concepts/chart-size" class="nav-link"><span class="title">图表容器及大小</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><!----><!----><li class="nav-item"><a href="/echarts-handbook/zh/concepts/style" class="nav-link"><span class="title">样式</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/concepts/dataset" class="nav-link"><span class="title">数据集</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/concepts/data-transform" class="nav-link"><span class="title">数据转换</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><!----><li class="nav-item"><a href="/echarts-handbook/zh/concepts/axis" class="nav-link"><span class="title">坐标轴</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/concepts/visual-map" class="nav-link"><span class="title">视觉映射</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/concepts/legend" class="nav-link"><span class="title">图例</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/concepts/event" class="nav-link"><span class="title">事件与行为</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li></ul></li><li class="nav-item"><div class="nav-link"><span class="title">应用篇</span></div> <ul class="nav bd-sidenav level1"><li class="nav-item"><a class="nav-link"><span class="title">常用图表类型</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level2" style="display:none"><li class="nav-item"><a class="nav-link"><span class="title">柱状图</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level3" style="display:none"><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/bar/basic-bar" class="nav-link"><span class="title">基础柱状图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/bar/stacked-bar" class="nav-link"><span class="title">堆叠柱状图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/bar/bar-race" class="nav-link"><span class="title">动态排序柱状图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><!----><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/bar/waterfall" class="nav-link"><span class="title">阶梯瀑布图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><!----></ul></li><li class="nav-item"><a class="nav-link"><span class="title">折线图</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level3" style="display:none"><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/line/basic-line" class="nav-link"><span class="title">基础折线图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/line/stacked-line" class="nav-link"><span class="title">堆叠折线图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/line/area-line" class="nav-link"><span class="title">区域面积图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/line/smooth-line" class="nav-link"><span class="title">平滑曲线图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/line/step-line" class="nav-link"><span class="title">阶梯线图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li></ul></li><li class="nav-item"><a class="nav-link"><span class="title">饼图</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level3" style="display:none"><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/pie/basic-pie" class="nav-link"><span class="title">基础饼图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/pie/doughnut" class="nav-link"><span class="title">圆环图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/pie/rose" class="nav-link"><span class="title">南丁格尔图(玫瑰图)</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li></ul></li><li class="nav-item"><a class="nav-link"><span class="title">散点图</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level3" style="display:none"><li class="nav-item"><a href="/echarts-handbook/zh/how-to/chart-types/scatter/basic-scatter" class="nav-link"><span class="title">基础散点图</span></a> <ul class="nav bd-sidenav level4" style="display:none"></ul></li></ul></li></ul></li><!----><li class="nav-item"><a class="nav-link"><span class="title">跨平台方案</span> <span class="glyphicon glyphicon-menu-up"></span></a> <ul class="nav bd-sidenav level2"><li class="nav-item"><a href="/echarts-handbook/zh/how-to/cross-platform/server" aria-current="page" class="nav-link nuxt-link-exact-active nuxt-link-active"><span class="title">服务端渲染</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/cross-platform/wechat-app" class="nav-link"><span class="title">微信小程序</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/how-to/cross-platform/baidu-app" class="nav-link"><span class="title">百度智能小程序</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li></ul></li><li class="nav-item"><a class="nav-link"><span class="title">数据处理</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level2" style="display:none"><li class="nav-item"><a href="/echarts-handbook/zh/how-to/data/dynamic-data" class="nav-link"><span class="title">动态的异步数据</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li><!----></ul></li><li class="nav-item"><a class="nav-link"><span class="title">标签</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level2" style="display:none"><li class="nav-item"><a href="/echarts-handbook/zh/how-to/label/rich-text" class="nav-link"><span class="title">富文本标签</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li></ul></li><li class="nav-item"><a class="nav-link"><span class="title">动画</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level2" style="display:none"><li class="nav-item"><a href="/echarts-handbook/zh/how-to/animation/transition" class="nav-link"><span class="title">数据过渡动画</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li></ul></li><li class="nav-item"><a class="nav-link"><span class="title">交互</span> <span class="glyphicon glyphicon-menu-down"></span></a> <ul class="nav bd-sidenav level2" style="display:none"><li class="nav-item"><a href="/echarts-handbook/zh/how-to/interaction/drag" class="nav-link"><span class="title">拖拽的实现</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li><!----><li class="nav-item"><a href="/echarts-handbook/zh/how-to/interaction/coarse-pointer" class="nav-link"><span class="title">智能指针吸附</span></a> <ul class="nav bd-sidenav level3" style="display:none"></ul></li></ul></li></ul></li><li class="nav-item"><div class="nav-link"><span class="title">最佳实践</span></div> <ul class="nav bd-sidenav level1"><!----><li class="nav-item"><a href="/echarts-handbook/zh/best-practices/canvas-vs-svg" class="nav-link"><span class="title">Canvas vs. SVG</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li><li class="nav-item"><a href="/echarts-handbook/zh/best-practices/aria" class="nav-link"><span class="title">无障碍访问</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li></ul></li><li class="nav-item"><div class="nav-link"><span class="title">编辑本文档</span></div> <ul class="nav bd-sidenav level1"><li class="nav-item"><a href="/echarts-handbook/zh/meta/edit-guide" class="nav-link"><span class="title">文档编辑指南</span></a> <ul class="nav bd-sidenav level2" style="display:none"></ul></li></ul></li></ul></div> <div class="open-sidebar"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg></div></div> <div class="bd-content col-sm-7 pl-sm-2 col-12"><div class="post-content content"><div><div class="post-inner"><!----> <div class="nuxt-content"><article><h1 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B8%B2%E6%9F%93-echarts-%E5%9B%BE%E8%A1%A8" tabindex="-1">服务端渲染 ECharts 图表</h1> <p>通常情况下,Apache ECharts<sup>TM</sup> 会在浏览器中动态的渲染图表,并且根据用户的交互来更新渲染。但是在下面这些比较特殊的场景,我们也需要在服务端中渲染图表并且输出到浏览器中:</p> <ul><li>需要缩短前端的渲染时间,保证第一时间显示图表</li> <li>需要在 Markdown, PDF 等不支持动态运行脚本的环境中嵌入图表</li></ul> <p>在这些场景下,ECharts 也提供了两种服务端渲染(server-side rendering,SSR)的方案:SVG 渲染或 Canvas 渲染。</p> <table><thead><tr><th>渲染方案</th> <th>渲染结果的形式</th> <th>优点</th></tr></thead> <tbody><tr><td>服务端 SVG 渲染</td> <td>SVG 字符串</td> <td>比 Canvas 图片体积更小;<br>矢量 SVG 图片不会模糊;<br>支持初始动画</td></tr> <tr><td>服务端 Canvas 渲染</td> <td>图片</td> <td>图片形式适用场景更广泛,对不支持 SVG 的场景可选择</td></tr></tbody></table> <p>通常情况下,应优先考虑使用服务端 SVG 渲染方案,如果 SVG 不适用,也可以考虑 Canvas 渲染方案。</p> <p>使用服务端渲染也有一定的局限性,尤其是和交互相关的一些操作无法支持。因此,如果有交互需求,可参考下文的“服务端渲染 Hydration”。</p> <h2 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B8%B2%E6%9F%93" tabindex="-1">服务端渲染</h2> <h3 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF-svg-%E6%B8%B2%E6%9F%93" tabindex="-1">服务端 SVG 渲染</h3> <blockquote><p>版本更新:</p> <ul><li>5.3.0 版本:使用零依赖的服务端 SVG 字符串渲染方案,并支持图表的初始动画</li> <li>5.5.0 版本:新增客户端轻量运行时,客户端无需加载完整 ECharts 即可实现部分交互</li></ul></blockquote> <p>5.3.0 里新引入了零依赖的服务端 SVG 字符串渲染方案:</p> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-ts line-numbers"><code><span class="token comment">// 服务端代码</span>
<span class="token keyword">const</span> echarts <span class="token operator">=</span> <span class="token keyword">require</span><span class="token punctuation">(</span><span class="token string">'echarts'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 在 SSR 模式下第一个参数不需要再传入 DOM 对象</span>
<span class="token keyword">let</span> chart <span class="token operator">=</span> echarts<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
renderer<span class="token operator">:</span> <span class="token string">'svg'</span><span class="token punctuation">,</span> <span class="token comment">// 必须使用 SVG 模式</span>
ssr<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 开启 SSR</span>
width<span class="token operator">:</span> <span class="token number">400</span><span class="token punctuation">,</span> <span class="token comment">// 需要指明高和宽</span>
height<span class="token operator">:</span> <span class="token number">300</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 像正常使用一样 setOption</span>
chart<span class="token punctuation">.</span><span class="token function">setOption</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token comment">//...</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 输出字符串</span>
<span class="token keyword">const</span> svgStr <span class="token operator">=</span> chart<span class="token punctuation">.</span><span class="token function">renderToSVGString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 如果不再需要图表,调用 dispose 以释放内存</span>
chart<span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
chart <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <p>整体使用的代码结构跟在浏览器中使用一样,首先是<code>init</code>初始化一个图表实例,然后通过<code>setOption</code>设置图表的配置项。但是<code>init</code>传入的参数会跟在跟浏览器中使用有所不同:</p> <ul><li>首先因为在服务端会采用字符串拼接的方式来渲染得到 SVG,我们并不需要容器来展示渲染的内容,所以我们可以在<code>init</code>的时候第一个<code>container</code>参数传入<code>null</code>或者<code>undefined</code>。</li> <li>然后我们在<code>init</code>的第三个参数中,我们需要通过显示指定<code>ssr: true</code>来告诉 ECharts 我们需要开启服务端渲染的模式,该模式下 ECharts 会关闭动画循环的模块以及事件交互的模块。</li> <li>在服务端渲染中我们也必须要通过<code>width</code>和<code>height</code>显示的指定图表的高和宽,因此如果你的图表是需要根据容器大小自适应的话,可能需要思考一下服务端渲染是否适合你的场景了。一种可能的解决方案是,首屏获取到图表容器大小后,请求服务端渲染图表,然后在客户端渲染图表;当用户交互改变容器大小时,重新请求服务端渲染。</li></ul> <p>在浏览器中我们在<code>setOption</code>完之后 ECharts 就会自动进行渲染将结果绘制到页面中,后续也会在每一帧判断是否有动画需要进行重绘。Node.js 中我们在设置了<code>ssr: true</code>后则没有这个过程。取而代之我们使用了<code>renderToSVGString</code>,将当前的图表渲染到 SVG 字符串,进一步得再通过 HTTP Response 返回给前端或者缓存到本地。</p> <p>HTTP Response 返回给前端(这里以 Express.js 为例):</p> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-ts line-numbers"><code>res<span class="token punctuation">.</span><span class="token function">writeHead</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token string-property property">'Content-Type'</span><span class="token operator">:</span> <span class="token string">'application/xml'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
res<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>svgStr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// svgStr 是上面 chart.renderToSVGString() 得到的字符串</span>
res<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <p>或者保存到本地:</p> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-ts line-numbers"><code>fs<span class="token punctuation">.</span><span class="token function">writeFile</span><span class="token punctuation">(</span><span class="token string">'bar.svg'</span><span class="token punctuation">,</span> svgStr<span class="token punctuation">,</span> <span class="token string">'utf-8'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <h4 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B8%B2%E6%9F%93%E4%B8%AD%E7%9A%84%E5%8A%A8%E7%94%BB%E6%95%88%E6%9E%9C" tabindex="-1">服务端渲染中的动画效果</h4> <p>上面的例子中可以看到,就算是服务端渲染 ECharts 也可以提供动画效果,这个动画效果是通过在输出的 SVG 字符串中嵌入 CSS 动画实现的。并不需要额外的 JavaScript 再去控制动画。</p> <p>但是,也因为 CSS 动画的局限性,我们没法在服务端渲染中实现一些更灵活的动画功能,诸如柱状图排序动画,标签动画,路径图的特效动画等。部分系列诸如饼图的动画效果也为服务端渲染做了特殊的优化。</p> <p>如果你不希望有这个动画效果,可以在<code>setOption</code>的时候通过<code>animation: false</code>关闭动画。</p> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-ts line-numbers"><code><span class="token function">setOption</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
animation<span class="token operator">:</span> <span class="token boolean">false</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <h3 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF-canvas-%E6%B8%B2%E6%9F%93" tabindex="-1">服务端 Canvas 渲染</h3> <p>如果你希望输出的是一张图片而非 SVG 字符串,或者你还在使用更老的版本,我们会推荐使用 <a href="https://github.com/Automattic/node-canvas">node-canvas</a> 来实现 ECharts 的服务渲染,<a href="https://github.com/Automattic/node-canvas">node-canvas</a> 是在 Node.js 上的一套 Canvas 实现,它提供了跟浏览器中 Canvas 几乎一致的接口。</p> <p>下面是一个简单的例子</p> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-ts line-numbers"><code><span class="token keyword">var</span> echarts <span class="token operator">=</span> <span class="token keyword">require</span><span class="token punctuation">(</span><span class="token string">'echarts'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> createCanvas <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">require</span><span class="token punctuation">(</span><span class="token string">'canvas'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 在 5.3.0 之前的版本中,你必须要通过该接口注册 canvas 实例创建方法。</span>
<span class="token comment">// 从 5.3.0 开始就不需要了</span>
echarts<span class="token punctuation">.</span><span class="token function">setCanvasCreator</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">createCanvas</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> canvas <span class="token operator">=</span> <span class="token function">createCanvas</span><span class="token punctuation">(</span><span class="token number">800</span><span class="token punctuation">,</span> <span class="token number">600</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// ECharts 可以直接使用 node-canvas 创建的 Canvas 实例作为容器</span>
<span class="token keyword">let</span> chart <span class="token operator">=</span> echarts<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span>canvas<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 像正常使用一样 setOption</span>
chart<span class="token punctuation">.</span><span class="token function">setOption</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token comment">//...</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> buffer <span class="token operator">=</span> <span class="token function">renderChart</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBuffer</span><span class="token punctuation">(</span><span class="token string">'image/png'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 如果不再需要图表,调用 dispose 以释放内存</span>
chart<span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
chart <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token comment">// 通过 Response 输出 PNG 图片</span>
res<span class="token punctuation">.</span><span class="token function">writeHead</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token string-property property">'Content-Type'</span><span class="token operator">:</span> <span class="token string">'image/png'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
res<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
res<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <h4 id="%E5%9B%BE%E7%89%87%E7%9A%84%E5%8A%A0%E8%BD%BD" tabindex="-1">图片的加载</h4> <p><a href="https://github.com/Automattic/node-canvas">node-canvas</a> 提供了图片加载的<code>Image</code>实现,如果你在图表中使用了到了图片,我们可以使用<code>5.3.0</code>新增的<code>setPlatformAPI</code>接口来适配。</p> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-ts line-numbers"><code>echarts<span class="token punctuation">.</span><span class="token function">setPlatformAPI</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token comment">// 同老版本的 setCanvasCreator</span>
<span class="token function">createCanvas</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">createCanvas</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function">loadImage</span><span class="token punctuation">(</span>src<span class="token punctuation">,</span> onload<span class="token punctuation">,</span> onerror<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> img <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Image</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 必须要绑定 this context.</span>
img<span class="token punctuation">.</span>onload <span class="token operator">=</span> <span class="token function">onload</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>img<span class="token punctuation">)</span><span class="token punctuation">;</span>
img<span class="token punctuation">.</span>onerror <span class="token operator">=</span> <span class="token function">onerror</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>img<span class="token punctuation">)</span><span class="token punctuation">;</span>
img<span class="token punctuation">.</span>src <span class="token operator">=</span> src<span class="token punctuation">;</span>
<span class="token keyword">return</span> img<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <p>如果你的图片是需要远程获取的,我们建议你通过 http 请求先预取该图片得到<code>base64</code>之后再作为图片的 URL 传入,这样可以保证在 Response 输出的时候图片是加载完成的。</p> <h2 id="%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BA%8C%E6%AC%A1%E6%B8%B2%E6%9F%93" tabindex="-1">客户端二次渲染</h2> <h3 id="%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%87%92%E5%8A%A0%E8%BD%BD%E5%AE%8C%E6%95%B4-echarts" tabindex="-1">客户端懒加载完整 ECharts</h3> <p>最新版本的 ECharts 服务端 SVG 渲染除了完成图表的渲染外,支持的功能包括:</p> <ul><li>图表初始动画(例如:柱状图初始化时的柱子上升动画)</li> <li>高亮样式(例如:鼠标移动到柱状图柱子上时的高亮效果)</li></ul> <p>但仅使用服务端渲染无法支持的功能包括:</p> <ul><li>动态改变数据</li> <li>点击图例切换系列是否显示</li> <li>移动鼠标显示提示框</li> <li>其他交互相关的功能</li></ul> <p>如果有相关需求,可以考虑先使用服务端渲染快速输出首屏图表,然后等待 <code>echarts.js</code> 加载完后,重新在客户端渲染同样的图表(称为 Hydration),这样就可以实现正常的交互效果和动态改变数据了。需要注意的是,在客户端渲染的时候,应开启 <code>tooltip: { show: true }</code> 之类的交互组件,并且用 <code>animation: 0</code> 关闭初始动画(初始动画应由服务端渲染结果的 SVG 动画完成)。</p> <p>从用户体验的角度,几乎感受不到二次渲染的过程,整个切换效果是非常无缝衔接的。你也可以像上面的例子中一样,在加载 <code>echarts.js</code> 的过程中使用 <a href="https://www.npmjs.com/package/pace-js">pace-js</a> 之类的库实现显示加载进度条的效果,来解决 ECharts 尚未完全加载完之前没有交互反馈的问题。</p> <p>使用服务端渲染 SVG 加上客户端 ECharts 懒加载的方式,其优点是,能够在首屏快速展示图表,而懒加载完成后可以实现所有 ECharts 的功能和交互;而缺点是,懒加载完整的 ECharts 需要一定时间,在加载完成前无法实现除高亮之外的用户交互(在这种情况下,开发者可以通过显示“加载中”来解决无交互反馈带来的困惑)。这个方案也是目前比较推荐的对首屏加载时间敏感,对功能交互完整性要求高的方案。</p> <h3 id="%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BD%BB%E9%87%8F%E8%BF%90%E8%A1%8C%E6%97%B6" tabindex="-1">客户端轻量运行时</h3> <p>方案一给出了实现完整交互的方案,但是有些场景下,我们并不需要很复杂的交互,只是希望在服务端渲染的基础上,能够在客户端进行一些简单的交互,例如:点击图例切换系列是否显示。这种情况下,我们能否不在客户端加载至少需要几百 KB 的 ECharts 代码呢?</p> <p>从 v5.5.0 版本起,如果图表只需要以下效果和交互,可以通过服务端 SVG 渲染 + 客户端轻量运行时来实现:</p> <ul><li>图表初始动画(实现原理:服务端渲染的 SVG 带有 CSS 动画)</li> <li>高亮样式(实现原理:服务端渲染的 SVG 带有 CSS 动画)</li> <li>动态改变数据(实现原理:轻量运行时请求服务器进行二次渲染)</li> <li>点击图例切换系列是否显示(实现原理:轻量运行时请求服务器进行二次渲染)</li></ul> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-html line-numbers"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>chart-container<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">width</span><span class="token punctuation">:</span>800px<span class="token punctuation">;</span><span class="token property">height</span><span class="token punctuation">:</span>600px</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://cdn.jsdelivr.net/npm/echarts/ssr/client/dist/index.min.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span>
const ssrClient = window['echarts-ssr-client'];
const isSeriesShown = {
a: true,
b: true
};
function updateChart(svgStr) {
const container = document.getElementById('chart-container');
container.innerHTML = svgStr;
// 使用轻量运行时赋予图表交互能力
ssrClient.hydrate(container, {
on: {
click: (params) => {
if (params.ssrType === 'legend') {
// 点击图例元素,请求服务器进行二次渲染
isSeriesShown[params.seriesName] = !isSeriesShown[params.seriesName];
fetch('...?series=' + JSON.stringify(isSeriesShown))
.then(res => res.text())
.then(svgStr => {
updateChart(svgStr);
});
}
}
}
});
}
// 通过 AJAX 请求获取服务端渲染的 SVG 字符串
fetch('...')
.then(res => res.text())
.then(svgStr => {
updateChart(svgStr);
});
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <p>服务器端根据客户端传来的每个系列是否显示的信息(<code>isSeriesShown</code>)进行二次渲染,返回新的 SVG 字符串。服务端代码<a href="#%E6%9C%8D%E5%8A%A1%E7%AB%AF-svg-%E6%B8%B2%E6%9F%93">同上文</a>,不再赘述。</p> <blockquote><p>关于状态记录:上述这种开发方式和纯客户端渲染的相比,开发者需要记录并维护一些额外的信息(例如这个例子中每个系列是否显示)。这是不可避免的,因为 HTTP 请求本身是无状态的,如果要实现有状态,要么像上面的例子这样由客户端记录状态并传递,要么服务器保留状态(例如通过 session,但需要耗费更多的服务器内存以及更复杂的销毁逻辑所以并不推荐)。</p></blockquote> <p>使用服务端 SVG 渲染加上客户端轻量运行时的方式,其优点是,客户端不再需要加载几百 KB 的 ECharts 代码,只需要加载一个不到 4KB 的轻量运行时代码;并且从用户体验的角度牺牲很少(支持初始动画、鼠标高亮)。而缺点是,需要一定的开发成本来维护额外的状态信息,并且无法支持实时性要求高的交互(例如移动鼠标显示提示框)。总体来说,<strong>推荐在对代码体积有非常严格要求的环境使用</strong>。</p> <h2 id="%E4%BD%BF%E7%94%A8%E8%BD%BB%E9%87%8F%E8%BF%90%E8%A1%8C%E6%97%B6" tabindex="-1">使用轻量运行时</h2> <p>客户端轻量运行时通过将服务端渲染的 SVG 图表进行理解,从而赋予图表一定的交互能力。</p> <p>可以通过以下方式引入客户端轻量运行时:</p> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-html line-numbers"><code><span class="token comment">&lt;!-- 方法一:使用 CDN --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://cdn.jsdelivr.net/npm/echarts/ssr/client/dist/index.min.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>
<span class="token comment">&lt;!-- 方法二:使用 NPM --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>node_modules/echarts/ssr/client/dist/index.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <h3 id="api" tabindex="-1">API</h3> <p>在全局变量 <code>window['echarts-ssr-client']</code> 中提供了以下 API:</p> <h4 id="hydrate(dom--htmlelement%2C-options--ecssrclientoptions)" tabindex="-1">hydrate(dom: HTMLElement, options: ECSSRClientOptions)</h4> <ul><li><code>dom</code>:图表容器,其内部的内容在调用本方法前应已设为服务端渲染的 SVG 图表</li> <li><code>options</code>:配置项</li></ul> <h5 id="ecssrclientoptions" tabindex="-1">ECSSRClientOptions</h5> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-ts line-numbers"><code>on<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">{</span>
mouseover<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">(</span>params<span class="token operator">:</span> ECSSRClientEventParams<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span><span class="token punctuation">,</span>
mouseout<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">(</span>params<span class="token operator">:</span> ECSSRClientEventParams<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span><span class="token punctuation">,</span>
click<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">(</span>params<span class="token operator">:</span> ECSSRClientEventParams<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span>
<span class="token punctuation">}</span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <p>和<a href="https://echarts.apache.org/api.html#events.%E9%BC%A0%E6%A0%87%E4%BA%8B%E4%BB%B6">图表鼠标事件</a>一样,这里的时间都是针对图表数据对象的(例如:柱状图的柱子、折线图的数据点等),而不是针对图表容器的。</p> <h5 id="ecssrclienteventparams" tabindex="-1">ECSSRClientEventParams</h5> <div class="md-code-block"><div class="nuxt-content-highlight"><pre class="language-ts line-numbers"><code><span class="token punctuation">{</span>
type<span class="token operator">:</span> <span class="token string">'mouseover'</span> <span class="token operator">|</span> <span class="token string">'mouseout'</span> <span class="token operator">|</span> <span class="token string">'click'</span><span class="token punctuation">;</span>
ssrType<span class="token operator">:</span> <span class="token string">'legend'</span> <span class="token operator">|</span> <span class="token string">'chart'</span><span class="token punctuation">;</span>
seriesIndex<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
dataIndex<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
event<span class="token operator">:</span> Event<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre></div> <!----> <div class="clipboard" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6" data-v-479657ca><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" data-v-479657ca></path></svg></svg></div></div> <ul><li><code>type</code>:事件类型</li> <li><code>ssrType</code>:事件对象类型,<code>legend</code> 表示图例数据,<code>chart</code> 表示图表数据对象</li> <li><code>seriesIndex</code>:系列索引</li> <li><code>dataIndex</code>:数据索引</li> <li><code>event</code>:原生事件对象</li></ul> <h3 id="%E7%A4%BA%E4%BE%8B" tabindex="-1">示例</h3> <p>参见上文「客户端轻量运行时」章节。</p> <h2 id="%E5%B0%8F%E7%BB%93" tabindex="-1">小结</h2> <p>上面,我们介绍了几种不同的渲染方案,包括:</p> <ul><li>客户端渲染</li> <li>服务端 SVG 渲染</li> <li>服务端 Canvas 渲染</li> <li>客户端轻量运行时渲染</li></ul> <p>这四种渲染方式可以结合使用,我们再来总结一下它们各自适用的场景:</p> <table><thead><tr><th>渲染方案</th> <th>加载体积</th> <th>功能及交互损失</th> <th>相对开发工作量</th> <th>推荐场景</th></tr></thead> <tbody><tr><td>客户端渲染</td> <td>最大</td> <td>无</td> <td>最小</td> <td>首屏加载时间不敏感,对功能交互完整性要求高</td></tr> <tr><td>客户端渲染(<a href="zh/basics/import#%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5-echarts-%E5%9B%BE%E8%A1%A8%E5%92%8C%E7%BB%84%E4%BB%B6">按需引用</a>部分包)</td> <td>大</td> <td>大:没有引入的包就无法使用对应功能</td> <td>小</td> <td>首屏加载时间不敏感,对代码体积没有严格要求但是希望尽可能小,仅使用 ECharts 的一小部分功能,没有服务器资源</td></tr> <tr><td>一次性服务端 SVG 渲染</td> <td>小</td> <td>大:无法动态改变数据、不支持图例切换系列是否显示、不支持提示框等实时性要求高的交互</td> <td>中</td> <td>首屏加载时间敏感,对功能交互完整性要求低</td></tr> <tr><td>一次性服务端 Canvas 渲染</td> <td>大</td> <td>最大:同上且不支持初始动画、图片体积更大、放大会模糊</td> <td>中</td> <td>首屏加载时间敏感,对功能交互完整性要求低,平台限制无法使用 SVG</td></tr> <tr><td>服务端 SVG 渲染加客户端懒加载 ECharts</td> <td>小,然后大</td> <td>中:懒加载完成前无法交互</td> <td>中</td> <td>首屏加载时间敏感,对功能交互完整性要求高,最好图表不会在加载后立刻需要交互</td></tr> <tr><td>服务端 SVG 渲染加客户端轻量运行时</td> <td>小</td> <td>中:无法实现实时性要求高的交互</td> <td>大(需要维护图表状态、定义客户端服务端接口协议)</td> <td>首屏加载时间敏感,对功能交互完整性要求低,对代码体积有非常严格要求,交互实时性要求不严格</td></tr> <tr><td>服务端 SVG 渲染加客户端懒加载 ECharts,懒加载完成前使用轻量运行时</td> <td>小,然后大</td> <td>小:在懒加载完成前无法进行复杂交互</td> <td>最大</td> <td>首屏加载时间敏感,对功能交互完整性要求高,有充分的开发时间</td></tr></tbody></table> <p>当然,还存在一些其他的组合可能性,但最常用的就是以上几种,相信如果你了解了这些渲染方案的特点,就可以根据自己的场景选择合适的方案了。</p></article></div></div> <div class="post-contributors"><h3><span class="inline-block align-middle">本文贡献者</span> <a target="_blank" href="https://github.com/apache/echarts-handbook/tree/master/contents/zh/how-to/cross-platform/server.md" title="编辑本文" class="inline-block align-middle text-sm"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-8 w-8 inline-block align-middle"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg> <span class="inline-block align-middle">在 GitHub 上编辑本页</span></a></h3> <div class="post-contributors-list"><a href="https://github.com/Ovilia" target="_blank" class="post-contributor"><img alt="Ovilia" src="https://avatars.githubusercontent.com/Ovilia?size=60" loading="lazy"> <span>Ovilia</span></a><a href="https://github.com/plainheart" target="_blank" class="post-contributor"><img alt="plainheart" src="https://avatars.githubusercontent.com/plainheart?size=60" loading="lazy"> <span>plainheart</span></a><a href="https://github.com/pissang" target="_blank" class="post-contributor"><img alt="pissang" src="https://avatars.githubusercontent.com/pissang?size=60" loading="lazy"> <span>pissang</span></a></div></div></div></div></div></div></div></div></div></div><script>window.__NUXT__=function(t,i,d,e,n,l,a,r,o,c,h,s,g,p,C,I,m,b,A,u,E,B,y,v,G,S,Z,V,W,L,Y,z,K,R,H,F,X,w,N,k,D,j,J,O,P,f,x,M,Q,T,U,q,_,$,tt,it,dt,et){return{layout:"default",data:[{html:'<h1 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B8%B2%E6%9F%93-echarts-%E5%9B%BE%E8%A1%A8" tabindex="-1">服务端渲染 ECharts 图表</h1>\n<p>通常情况下,Apache ECharts<sup>TM</sup> 会在浏览器中动态的渲染图表,并且根据用户的交互来更新渲染。但是在下面这些比较特殊的场景,我们也需要在服务端中渲染图表并且输出到浏览器中:</p>\n<ul>\n<li>需要缩短前端的渲染时间,保证第一时间显示图表</li>\n<li>需要在 Markdown, PDF 等不支持动态运行脚本的环境中嵌入图表</li>\n</ul>\n<p>在这些场景下,ECharts 也提供了两种服务端渲染(server-side rendering,SSR)的方案:SVG 渲染或 Canvas 渲染。</p>\n<table>\n<thead>\n<tr>\n<th>渲染方案</th>\n<th>渲染结果的形式</th>\n<th>优点</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>服务端 SVG 渲染</td>\n<td>SVG 字符串</td>\n<td>比 Canvas 图片体积更小;<br>矢量 SVG 图片不会模糊;<br>支持初始动画</td>\n</tr>\n<tr>\n<td>服务端 Canvas 渲染</td>\n<td>图片</td>\n<td>图片形式适用场景更广泛,对不支持 SVG 的场景可选择</td>\n</tr>\n</tbody>\n</table>\n<p>通常情况下,应优先考虑使用服务端 SVG 渲染方案,如果 SVG 不适用,也可以考虑 Canvas 渲染方案。</p>\n<p>使用服务端渲染也有一定的局限性,尤其是和交互相关的一些操作无法支持。因此,如果有交互需求,可参考下文的“服务端渲染 Hydration”。</p>\n<h2 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B8%B2%E6%9F%93" tabindex="-1">服务端渲染</h2>\n<h3 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF-svg-%E6%B8%B2%E6%9F%93" tabindex="-1">服务端 SVG 渲染</h3>\n<blockquote>\n<p>版本更新:</p>\n<ul>\n<li>5.3.0 版本:使用零依赖的服务端 SVG 字符串渲染方案,并支持图表的初始动画</li>\n<li>5.5.0 版本:新增客户端轻量运行时,客户端无需加载完整 ECharts 即可实现部分交互</li>\n</ul>\n</blockquote>\n<p>5.3.0 里新引入了零依赖的服务端 SVG 字符串渲染方案:</p>\n<md-code-block lang="ts" code="\'Ly8g5pyN5Yqh56uv5Luj56CBCmNvbnN0IGVjaGFydHMgPSByZXF1aXJlKCdlY2hhcnRzJyk7CgovLyDlnKggU1NSIOaooeW8j-S4i-esrOS4gOS4quWPguaVsOS4jemcgOimgeWGjeS8oOWFpSBET00g5a-56LGhCmxldCBjaGFydCA9IGVjaGFydHMuaW5pdChudWxsLCBudWxsLCB7CiAgcmVuZGVyZXI6ICdzdmcnLCAvLyDlv4Xpobvkvb_nlKggU1ZHIOaooeW8jwogIHNzcjogdHJ1ZSwgLy8g5byA5ZCvIFNTUgogIHdpZHRoOiA0MDAsIC8vIOmcgOimgeaMh-aYjumrmOWSjOWuvQogIGhlaWdodDogMzAwCn0pOwoKLy8g5YOP5q2j5bi45L2_55So5LiA5qC3IHNldE9wdGlvbgpjaGFydC5zZXRPcHRpb24oewogIC8vLi4uCn0pOwoKLy8g6L6T5Ye65a2X56ym5LiyCmNvbnN0IHN2Z1N0ciA9IGNoYXJ0LnJlbmRlclRvU1ZHU3RyaW5nKCk7CgovLyDlpoLmnpzkuI3lho3pnIDopoHlm77ooajvvIzosIPnlKggZGlzcG9zZSDku6Xph4rmlL7lhoXlrZgKY2hhcnQuZGlzcG9zZSgpOwpjaGFydCA9IG51bGw7\'" line-highlights="\'\'" />\n<p>整体使用的代码结构跟在浏览器中使用一样,首先是<code>init</code>初始化一个图表实例,然后通过<code>setOption</code>设置图表的配置项。但是<code>init</code>传入的参数会跟在跟浏览器中使用有所不同:</p>\n<ul>\n<li>首先因为在服务端会采用字符串拼接的方式来渲染得到 SVG,我们并不需要容器来展示渲染的内容,所以我们可以在<code>init</code>的时候第一个<code>container</code>参数传入<code>null</code>或者<code>undefined</code>。</li>\n<li>然后我们在<code>init</code>的第三个参数中,我们需要通过显示指定<code>ssr: true</code>来告诉 ECharts 我们需要开启服务端渲染的模式,该模式下 ECharts 会关闭动画循环的模块以及事件交互的模块。</li>\n<li>在服务端渲染中我们也必须要通过<code>width</code>和<code>height</code>显示的指定图表的高和宽,因此如果你的图表是需要根据容器大小自适应的话,可能需要思考一下服务端渲染是否适合你的场景了。一种可能的解决方案是,首屏获取到图表容器大小后,请求服务端渲染图表,然后在客户端渲染图表;当用户交互改变容器大小时,重新请求服务端渲染。</li>\n</ul>\n<p>在浏览器中我们在<code>setOption</code>完之后 ECharts 就会自动进行渲染将结果绘制到页面中,后续也会在每一帧判断是否有动画需要进行重绘。Node.js 中我们在设置了<code>ssr: true</code>后则没有这个过程。取而代之我们使用了<code>renderToSVGString</code>,将当前的图表渲染到 SVG 字符串,进一步得再通过 HTTP Response 返回给前端或者缓存到本地。</p>\n<p>HTTP Response 返回给前端(这里以 Express.js 为例):</p>\n<md-code-block lang="ts" code="\'cmVzLndyaXRlSGVhZCgyMDAsIHsKICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL3htbCcKfSk7CnJlcy53cml0ZShzdmdTdHIpOyAvLyBzdmdTdHIg5piv5LiK6Z2iIGNoYXJ0LnJlbmRlclRvU1ZHU3RyaW5nKCkg5b6X5Yiw55qE5a2X56ym5LiyCnJlcy5lbmQoKTs\'" line-highlights="\'\'" />\n<p>或者保存到本地:</p>\n<md-code-block lang="ts" code="\'ZnMud3JpdGVGaWxlKCdiYXIuc3ZnJywgc3ZnU3RyLCAndXRmLTgnKTs\'" line-highlights="\'\'" />\n<h4 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B8%B2%E6%9F%93%E4%B8%AD%E7%9A%84%E5%8A%A8%E7%94%BB%E6%95%88%E6%9E%9C" tabindex="-1">服务端渲染中的动画效果</h4>\n<p>上面的例子中可以看到,就算是服务端渲染 ECharts 也可以提供动画效果,这个动画效果是通过在输出的 SVG 字符串中嵌入 CSS 动画实现的。并不需要额外的 JavaScript 再去控制动画。</p>\n<p>但是,也因为 CSS 动画的局限性,我们没法在服务端渲染中实现一些更灵活的动画功能,诸如柱状图排序动画,标签动画,路径图的特效动画等。部分系列诸如饼图的动画效果也为服务端渲染做了特殊的优化。</p>\n<p>如果你不希望有这个动画效果,可以在<code>setOption</code>的时候通过<code>animation: false</code>关闭动画。</p>\n<md-code-block lang="ts" code="\'c2V0T3B0aW9uKHsKICBhbmltYXRpb246IGZhbHNlCn0pOw\'" line-highlights="\'\'" />\n<h3 id="%E6%9C%8D%E5%8A%A1%E7%AB%AF-canvas-%E6%B8%B2%E6%9F%93" tabindex="-1">服务端 Canvas 渲染</h3>\n<p>如果你希望输出的是一张图片而非 SVG 字符串,或者你还在使用更老的版本,我们会推荐使用 <a href="https://github.com/Automattic/node-canvas">node-canvas</a> 来实现 ECharts 的服务渲染,<a href="https://github.com/Automattic/node-canvas">node-canvas</a> 是在 Node.js 上的一套 Canvas 实现,它提供了跟浏览器中 Canvas 几乎一致的接口。</p>\n<p>下面是一个简单的例子</p>\n<md-code-block lang="ts" code="\'dmFyIGVjaGFydHMgPSByZXF1aXJlKCdlY2hhcnRzJyk7CmNvbnN0IHsgY3JlYXRlQ2FudmFzIH0gPSByZXF1aXJlKCdjYW52YXMnKTsKCi8vIOWcqCA1LjMuMCDkuYvliY3nmoTniYjmnKzkuK3vvIzkvaDlv4XpobvopoHpgJrov4for6XmjqXlj6Pms6jlhowgY2FudmFzIOWunuS-i-WIm-W7uuaWueazleOAggovLyDku44gNS4zLjAg5byA5aeL5bCx5LiN6ZyA6KaB5LqGCmVjaGFydHMuc2V0Q2FudmFzQ3JlYXRvcigoKSA9PiB7CiAgcmV0dXJuIGNyZWF0ZUNhbnZhcygpOwp9KTsKCmNvbnN0IGNhbnZhcyA9IGNyZWF0ZUNhbnZhcyg4MDAsIDYwMCk7Ci8vIEVDaGFydHMg5Y-v5Lul55u05o6l5L2_55SoIG5vZGUtY2FudmFzIOWIm-W7uueahCBDYW52YXMg5a6e5L6L5L2c5Li65a655ZmoCmxldCBjaGFydCA9IGVjaGFydHMuaW5pdChjYW52YXMpOwoKLy8g5YOP5q2j5bi45L2_55So5LiA5qC3IHNldE9wdGlvbgpjaGFydC5zZXRPcHRpb24oewogIC8vLi4uCn0pOwoKY29uc3QgYnVmZmVyID0gcmVuZGVyQ2hhcnQoKS50b0J1ZmZlcignaW1hZ2UvcG5nJyk7CgovLyDlpoLmnpzkuI3lho3pnIDopoHlm77ooajvvIzosIPnlKggZGlzcG9zZSDku6Xph4rmlL7lhoXlrZgKY2hhcnQuZGlzcG9zZSgpOwpjaGFydCA9IG51bGw7CgovLyDpgJrov4cgUmVzcG9uc2Ug6L6T5Ye6IFBORyDlm77niYcKcmVzLndyaXRlSGVhZCgyMDAsIHsKICAnQ29udGVudC1UeXBlJzogJ2ltYWdlL3BuZycKfSk7CnJlcy53cml0ZShidWZmZXIpOwpyZXMuZW5kKCk7\'" line-highlights="\'\'" />\n<h4 id="%E5%9B%BE%E7%89%87%E7%9A%84%E5%8A%A0%E8%BD%BD" tabindex="-1">图片的加载</h4>\n<p><a href="https://github.com/Automattic/node-canvas">node-canvas</a> 提供了图片加载的<code>Image</code>实现,如果你在图表中使用了到了图片,我们可以使用<code>5.3.0</code>新增的<code>setPlatformAPI</code>接口来适配。</p>\n<md-code-block lang="ts" code="\'ZWNoYXJ0cy5zZXRQbGF0Zm9ybUFQSSh7CiAgLy8g5ZCM6ICB54mI5pys55qEIHNldENhbnZhc0NyZWF0b3IKICBjcmVhdGVDYW52YXMoKSB7CiAgICByZXR1cm4gY3JlYXRlQ2FudmFzKCk7CiAgfSwKICBsb2FkSW1hZ2Uoc3JjLCBvbmxvYWQsIG9uZXJyb3IpIHsKICAgIGNvbnN0IGltZyA9IG5ldyBJbWFnZSgpOwogICAgLy8g5b-F6aG76KaB57uR5a6aIHRoaXMgY29udGV4dC4KICAgIGltZy5vbmxvYWQgPSBvbmxvYWQuYmluZChpbWcpOwogICAgaW1nLm9uZXJyb3IgPSBvbmVycm9yLmJpbmQoaW1nKTsKICAgIGltZy5zcmMgPSBzcmM7CiAgICByZXR1cm4gaW1nOwogIH0KfSk7\'" line-highlights="\'\'" />\n<p>如果你的图片是需要远程获取的,我们建议你通过 http 请求先预取该图片得到<code>base64</code>之后再作为图片的 URL 传入,这样可以保证在 Response 输出的时候图片是加载完成的。</p>\n<h2 id="%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BA%8C%E6%AC%A1%E6%B8%B2%E6%9F%93" tabindex="-1">客户端二次渲染</h2>\n<h3 id="%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%87%92%E5%8A%A0%E8%BD%BD%E5%AE%8C%E6%95%B4-echarts" tabindex="-1">客户端懒加载完整 ECharts</h3>\n<p>最新版本的 ECharts 服务端 SVG 渲染除了完成图表的渲染外,支持的功能包括:</p>\n<ul>\n<li>图表初始动画(例如:柱状图初始化时的柱子上升动画)</li>\n<li>高亮样式(例如:鼠标移动到柱状图柱子上时的高亮效果)</li>\n</ul>\n<p>但仅使用服务端渲染无法支持的功能包括:</p>\n<ul>\n<li>动态改变数据</li>\n<li>点击图例切换系列是否显示</li>\n<li>移动鼠标显示提示框</li>\n<li>其他交互相关的功能</li>\n</ul>\n<p>如果有相关需求,可以考虑先使用服务端渲染快速输出首屏图表,然后等待 <code>echarts.js</code> 加载完后,重新在客户端渲染同样的图表(称为 Hydration),这样就可以实现正常的交互效果和动态改变数据了。需要注意的是,在客户端渲染的时候,应开启 <code>tooltip: { show: true }</code> 之类的交互组件,并且用 <code>animation: 0</code> 关闭初始动画(初始动画应由服务端渲染结果的 SVG 动画完成)。</p>\n<p>从用户体验的角度,几乎感受不到二次渲染的过程,整个切换效果是非常无缝衔接的。你也可以像上面的例子中一样,在加载 <code>echarts.js</code> 的过程中使用 <a href="https://www.npmjs.com/package/pace-js">pace-js</a> 之类的库实现显示加载进度条的效果,来解决 ECharts 尚未完全加载完之前没有交互反馈的问题。</p>\n<p>使用服务端渲染 SVG 加上客户端 ECharts 懒加载的方式,其优点是,能够在首屏快速展示图表,而懒加载完成后可以实现所有 ECharts 的功能和交互;而缺点是,懒加载完整的 ECharts 需要一定时间,在加载完成前无法实现除高亮之外的用户交互(在这种情况下,开发者可以通过显示“加载中”来解决无交互反馈带来的困惑)。这个方案也是目前比较推荐的对首屏加载时间敏感,对功能交互完整性要求高的方案。</p>\n<h3 id="%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BD%BB%E9%87%8F%E8%BF%90%E8%A1%8C%E6%97%B6" tabindex="-1">客户端轻量运行时</h3>\n<p>方案一给出了实现完整交互的方案,但是有些场景下,我们并不需要很复杂的交互,只是希望在服务端渲染的基础上,能够在客户端进行一些简单的交互,例如:点击图例切换系列是否显示。这种情况下,我们能否不在客户端加载至少需要几百 KB 的 ECharts 代码呢?</p>\n<p>从 v5.5.0 版本起,如果图表只需要以下效果和交互,可以通过服务端 SVG 渲染 + 客户端轻量运行时来实现:</p>\n<ul>\n<li>图表初始动画(实现原理:服务端渲染的 SVG 带有 CSS 动画)</li>\n<li>高亮样式(实现原理:服务端渲染的 SVG 带有 CSS 动画)</li>\n<li>动态改变数据(实现原理:轻量运行时请求服务器进行二次渲染)</li>\n<li>点击图例切换系列是否显示(实现原理:轻量运行时请求服务器进行二次渲染)</li>\n</ul>\n<md-code-block lang="html" code="\'PGRpdiBpZD0iY2hhcnQtY29udGFpbmVyIiBzdHlsZT0id2lkdGg6ODAwcHg7aGVpZ2h0OjYwMHB4Ij48L2Rpdj4KCjxzY3JpcHQgc3JjPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2VjaGFydHMvc3NyL2NsaWVudC9kaXN0L2luZGV4Lm1pbi5qcyI-PC9zY3JpcHQ-CjxzY3JpcHQ-CmNvbnN0IHNzckNsaWVudCA9IHdpbmRvd1snZWNoYXJ0cy1zc3ItY2xpZW50J107Cgpjb25zdCBpc1Nlcmllc1Nob3duID0gewogIGE6IHRydWUsCiAgYjogdHJ1ZQp9OwoKZnVuY3Rpb24gdXBkYXRlQ2hhcnQoc3ZnU3RyKSB7CiAgY29uc3QgY29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NoYXJ0LWNvbnRhaW5lcicpOwogIGNvbnRhaW5lci5pbm5lckhUTUwgPSBzdmdTdHI7CgogIC8vIOS9v-eUqOi9u-mHj-i_kOihjOaXtui1i-S6iOWbvuihqOS6pOS6kuiDveWKmwogIHNzckNsaWVudC5oeWRyYXRlKGNvbnRhaW5lciwgewogICAgb246IHsKICAgICAgY2xpY2s6IChwYXJhbXMpID0-IHsKICAgICAgICBpZiAocGFyYW1zLnNzclR5cGUgPT09ICdsZWdlbmQnKSB7CiAgICAgICAgICAvLyDngrnlh7vlm77kvovlhYPntKDvvIzor7fmsYLmnI3liqHlmajov5vooYzkuozmrKHmuLLmn5MKICAgICAgICAgIGlzU2VyaWVzU2hvd25bcGFyYW1zLnNlcmllc05hbWVdID0gIWlzU2VyaWVzU2hvd25bcGFyYW1zLnNlcmllc05hbWVdOwogICAgICAgICAgZmV0Y2goJy4uLj9zZXJpZXM9JyArIEpTT04uc3RyaW5naWZ5KGlzU2VyaWVzU2hvd24pKQogICAgICAgICAgICAudGhlbihyZXMgPT4gcmVzLnRleHQoKSkKICAgICAgICAgICAgLnRoZW4oc3ZnU3RyID0-IHsKICAgICAgICAgICAgICB1cGRhdGVDaGFydChzdmdTdHIpOwogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9KTsKfQoKLy8g6YCa6L-HIEFKQVgg6K-35rGC6I635Y-W5pyN5Yqh56uv5riy5p-T55qEIFNWRyDlrZfnrKbkuLIKZmV0Y2goJy4uLicpCiAgLnRoZW4ocmVzID0-IHJlcy50ZXh0KCkpCiAgLnRoZW4oc3ZnU3RyID0-IHsKICAgIHVwZGF0ZUNoYXJ0KHN2Z1N0cik7CiAgfSk7Cjwvc2NyaXB0Pg\'" line-highlights="\'\'" />\n<p>服务器端根据客户端传来的每个系列是否显示的信息(<code>isSeriesShown</code>)进行二次渲染,返回新的 SVG 字符串。服务端代码<a href="#%E6%9C%8D%E5%8A%A1%E7%AB%AF-svg-%E6%B8%B2%E6%9F%93">同上文</a>,不再赘述。</p>\n<blockquote>\n<p>关于状态记录:上述这种开发方式和纯客户端渲染的相比,开发者需要记录并维护一些额外的信息(例如这个例子中每个系列是否显示)。这是不可避免的,因为 HTTP 请求本身是无状态的,如果要实现有状态,要么像上面的例子这样由客户端记录状态并传递,要么服务器保留状态(例如通过 session,但需要耗费更多的服务器内存以及更复杂的销毁逻辑所以并不推荐)。</p>\n</blockquote>\n<p>使用服务端 SVG 渲染加上客户端轻量运行时的方式,其优点是,客户端不再需要加载几百 KB 的 ECharts 代码,只需要加载一个不到 4KB 的轻量运行时代码;并且从用户体验的角度牺牲很少(支持初始动画、鼠标高亮)。而缺点是,需要一定的开发成本来维护额外的状态信息,并且无法支持实时性要求高的交互(例如移动鼠标显示提示框)。总体来说,<strong>推荐在对代码体积有非常严格要求的环境使用</strong>。</p>\n<h2 id="%E4%BD%BF%E7%94%A8%E8%BD%BB%E9%87%8F%E8%BF%90%E8%A1%8C%E6%97%B6" tabindex="-1">使用轻量运行时</h2>\n<p>客户端轻量运行时通过将服务端渲染的 SVG 图表进行理解,从而赋予图表一定的交互能力。</p>\n<p>可以通过以下方式引入客户端轻量运行时:</p>\n<md-code-block lang="html" code="\'PCEtLSDmlrnms5XkuIDvvJrkvb_nlKggQ0ROIC0tPgo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS9lY2hhcnRzL3Nzci9jbGllbnQvZGlzdC9pbmRleC5taW4uanMiPjwvc2NyaXB0Pgo8IS0tIOaWueazleS6jO-8muS9v-eUqCBOUE0gLS0-CjxzY3JpcHQgc3JjPSJub2RlX21vZHVsZXMvZWNoYXJ0cy9zc3IvY2xpZW50L2Rpc3QvaW5kZXguanMiPjwvc2NyaXB0Pg\'" line-highlights="\'\'" />\n<h3 id="api" tabindex="-1">API</h3>\n<p>在全局变量 <code>window[\'echarts-ssr-client\']</code> 中提供了以下 API:</p>\n<h4 id="hydrate(dom--htmlelement%2C-options--ecssrclientoptions)" tabindex="-1">hydrate(dom: HTMLElement, options: ECSSRClientOptions)</h4>\n<ul>\n<li><code>dom</code>:图表容器,其内部的内容在调用本方法前应已设为服务端渲染的 SVG 图表</li>\n<li><code>options</code>:配置项</li>\n</ul>\n<h5 id="ecssrclientoptions" tabindex="-1">ECSSRClientOptions</h5>\n<md-code-block lang="ts" code="\'b24_OiB7CiAgbW91c2VvdmVyPzogKHBhcmFtczogRUNTU1JDbGllbnRFdmVudFBhcmFtcykgPT4gdm9pZCwKICBtb3VzZW91dD86IChwYXJhbXM6IEVDU1NSQ2xpZW50RXZlbnRQYXJhbXMpID0-IHZvaWQsCiAgY2xpY2s_OiAocGFyYW1zOiBFQ1NTUkNsaWVudEV2ZW50UGFyYW1zKSA9PiB2b2lkCn0\'" line-highlights="\'\'" />\n<p>和<a href="https://echarts.apache.org/api.html#events.%E9%BC%A0%E6%A0%87%E4%BA%8B%E4%BB%B6">图表鼠标事件</a>一样,这里的时间都是针对图表数据对象的(例如:柱状图的柱子、折线图的数据点等),而不是针对图表容器的。</p>\n<h5 id="ecssrclienteventparams" tabindex="-1">ECSSRClientEventParams</h5>\n<md-code-block lang="ts" code="\'ewogIHR5cGU6ICdtb3VzZW92ZXInIHwgJ21vdXNlb3V0JyB8ICdjbGljayc7CiAgc3NyVHlwZTogJ2xlZ2VuZCcgfCAnY2hhcnQnOwogIHNlcmllc0luZGV4PzogbnVtYmVyOwogIGRhdGFJbmRleD86IG51bWJlcjsKICBldmVudDogRXZlbnQ7Cn0\'" line-highlights="\'\'" />\n<ul>\n<li><code>type</code>:事件类型</li>\n<li><code>ssrType</code>:事件对象类型,<code>legend</code> 表示图例数据,<code>chart</code> 表示图表数据对象</li>\n<li><code>seriesIndex</code>:系列索引</li>\n<li><code>dataIndex</code>:数据索引</li>\n<li><code>event</code>:原生事件对象</li>\n</ul>\n<h3 id="%E7%A4%BA%E4%BE%8B" tabindex="-1">示例</h3>\n<p>参见上文「客户端轻量运行时」章节。</p>\n<h2 id="%E5%B0%8F%E7%BB%93" tabindex="-1">小结</h2>\n<p>上面,我们介绍了几种不同的渲染方案,包括:</p>\n<ul>\n<li>客户端渲染</li>\n<li>服务端 SVG 渲染</li>\n<li>服务端 Canvas 渲染</li>\n<li>客户端轻量运行时渲染</li>\n</ul>\n<p>这四种渲染方式可以结合使用,我们再来总结一下它们各自适用的场景:</p>\n<table>\n<thead>\n<tr>\n<th>渲染方案</th>\n<th>加载体积</th>\n<th>功能及交互损失</th>\n<th>相对开发工作量</th>\n<th>推荐场景</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>客户端渲染</td>\n<td>最大</td>\n<td>无</td>\n<td>最小</td>\n<td>首屏加载时间不敏感,对功能交互完整性要求高</td>\n</tr>\n<tr>\n<td>客户端渲染(<a href="zh/basics/import#%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5-echarts-%E5%9B%BE%E8%A1%A8%E5%92%8C%E7%BB%84%E4%BB%B6">按需引用</a>部分包)</td>\n<td>大</td>\n<td>大:没有引入的包就无法使用对应功能</td>\n<td>小</td>\n<td>首屏加载时间不敏感,对代码体积没有严格要求但是希望尽可能小,仅使用 ECharts 的一小部分功能,没有服务器资源</td>\n</tr>\n<tr>\n<td>一次性服务端 SVG 渲染</td>\n<td>小</td>\n<td>大:无法动态改变数据、不支持图例切换系列是否显示、不支持提示框等实时性要求高的交互</td>\n<td>中</td>\n<td>首屏加载时间敏感,对功能交互完整性要求低</td>\n</tr>\n<tr>\n<td>一次性服务端 Canvas 渲染</td>\n<td>大</td>\n<td>最大:同上且不支持初始动画、图片体积更大、放大会模糊</td>\n<td>中</td>\n<td>首屏加载时间敏感,对功能交互完整性要求低,平台限制无法使用 SVG</td>\n</tr>\n<tr>\n<td>服务端 SVG 渲染加客户端懒加载 ECharts</td>\n<td>小,然后大</td>\n<td>中:懒加载完成前无法交互</td>\n<td>中</td>\n<td>首屏加载时间敏感,对功能交互完整性要求高,最好图表不会在加载后立刻需要交互</td>\n</tr>\n<tr>\n<td>服务端 SVG 渲染加客户端轻量运行时</td>\n<td>小</td>\n<td>中:无法实现实时性要求高的交互</td>\n<td>大(需要维护图表状态、定义客户端服务端接口协议)</td>\n<td>首屏加载时间敏感,对功能交互完整性要求低,对代码体积有非常严格要求,交互实时性要求不严格</td>\n</tr>\n<tr>\n<td>服务端 SVG 渲染加客户端懒加载 ECharts,懒加载完成前使用轻量运行时</td>\n<td>小,然后大</td>\n<td>小:在懒加载完成前无法进行复杂交互</td>\n<td>最大</td>\n<td>首屏加载时间敏感,对功能交互完整性要求高,有充分的开发时间</td>\n</tr>\n</tbody>\n</table>\n<p>当然,还存在一些其他的组合可能性,但最常用的就是以上几种,相信如果你了解了这些渲染方案的特点,就可以根据自己的场景选择合适的方案了。</p>\n',postPath:"zh/how-to/cross-platform/server",title:"服务端渲染 - 跨平台方案 - 应用篇"}],fetch:{},error:null,state:{filled:!1,docVersion:"",ghVersion:"",visibleAffix:!1,homepage:{},adBlocked:!1,focusMode:!1,posts:{zh:[{title:"快速上手",dir:e},{title:"入门篇",dir:n,children:[{title:"获取 ECharts",dir:l},{title:"在项目中引入 ECharts",dir:a},{title:"资源列表",dir:r,draft:t},{title:"寻求帮助",dir:o},{title:"版本特性",dir:c,children:[{title:"ECharts 5 特性介绍",dir:h},{title:"v4 升级 v5 指南",dir:s},{title:5.2,dir:g},{title:5.3,dir:p},{title:5.4,dir:C},{title:5.5,dir:I}]}]},{title:"概念篇",dir:m,children:[{title:"图表容器及大小",dir:b},{title:"配置项",dir:A,draft:t},{title:"系列",dir:u,draft:t},{title:"样式",dir:E},{title:"数据集",dir:B},{title:"数据转换",dir:y},{title:"坐标系",dir:v,draft:t},{title:"坐标轴",dir:G},{title:"视觉映射",dir:d},{title:"图例",dir:S},{title:"事件与行为",dir:Z}]},{title:"应用篇",dir:V,children:[{title:"常用图表类型",dir:W,children:[{title:"柱状图",dir:"bar",children:[{title:"基础柱状图",dir:L},{title:"堆叠柱状图",dir:Y},{title:"动态排序柱状图",dir:z},{title:"极坐标系柱状图",dir:K,draft:t},{title:"阶梯瀑布图",dir:R},{title:"视觉映射的柱状图",dir:d,draft:t}]},{title:"折线图",dir:H,children:[{title:"基础折线图",dir:F},{title:"堆叠折线图",dir:X},{title:"区域面积图",dir:w},{title:"平滑曲线图",dir:N},{title:"阶梯线图",dir:k}]},{title:"饼图",dir:"pie",children:[{title:"基础饼图",dir:D},{title:"圆环图",dir:j},{title:"南丁格尔图(玫瑰图)",dir:J}]},{title:"散点图",dir:O,children:[{title:"基础散点图",dir:P}]}]},{title:"移动端优化",dir:i,draft:t},{title:"跨平台方案",dir:f,children:[{title:"服务端渲染",dir:"server"},{title:"微信小程序",dir:"wechat-app"},{title:"百度智能小程序",dir:"baidu-app"}]},{title:"数据处理",dir:"data",children:[{title:"动态的异步数据",dir:x},{title:"数据下钻",dir:M,draft:t}]},{title:"标签",dir:"label",children:[{title:"富文本标签",dir:Q}]},{title:"动画",dir:T,children:[{title:"数据过渡动画",dir:U}]},{title:"交互",dir:q,children:[{title:"拖拽的实现",dir:"drag"},{title:"多图联动",dir:_,draft:t},{title:"智能指针吸附",dir:$}]}]},{title:"最佳实践",dir:tt,children:[{title:"移动端优化",dir:i,draft:t},{title:it,dir:dt},{title:"无障碍访问",dir:"aria"}]},{title:"编辑本文档",dir:"meta",children:[{title:"文档编辑指南",dir:et}]}],en:[{title:"Get Started",dir:e},{title:"Basics",dir:n,children:[{title:"Download ECharts",dir:l},{title:"Import ECharts",dir:a},{title:"Resources",dir:r,draft:t},{title:"Get Help",dir:o},{title:"What's New",dir:c,children:[{title:"ECharts 5 Features",dir:h},{title:"Migration from v4 to v5",dir:s},{title:5.2,dir:g},{title:5.3,dir:p},{title:5.4,dir:C},{title:5.5,dir:I}]}]},{title:"Concepts",dir:m,children:[{title:"Chart Container",dir:b},{title:"Chart Option",dir:A,draft:t},{title:"Series",dir:u,draft:t},{title:"Style",dir:E},{title:"Dataset",dir:B},{title:"Data Transform",dir:y},{title:"Coordinate",dir:v,draft:t},{title:"Axis",dir:G},{title:"Visual Mapping",dir:d},{title:"Legend",dir:S},{title:"Event and Action",dir:Z}]},{title:"How To Guides",dir:V,children:[{title:"Common Charts",dir:W,children:[{title:"Bar",dir:"bar",children:[{title:"Basic Bar",dir:L},{title:"Stacked Bar",dir:Y},{title:"Bar Racing",dir:z},{title:"Bar Polar",dir:K,draft:t},{title:"Waterfall",dir:R}]},{title:"Line",dir:H,children:[{title:"Basic Line",dir:F},{title:"Stacked Line",dir:X},{title:"Area Chart",dir:w},{title:"Smoothed Line",dir:N},{title:"Step Line",dir:k}]},{title:"Pie",dir:"pie",children:[{title:"Basic Pie",dir:D},{title:"Ring Style",dir:j},{title:"Rose Style",dir:J}]},{title:"Scatter",dir:O,children:[{title:"Basic Scatter",dir:P}]}]},{title:"Mobile",dir:i,draft:t},{title:"Cross Platform",dir:f,children:[{title:"Server Side Rendering",dir:"server"}]},{title:"Data",dir:"data",children:[{title:"Dynamic Data",dir:x},{title:"Drilldown",dir:M,draft:t}]},{title:"Label",dir:"label",children:[{title:"Rich Text",dir:Q}]},{title:"Animation",dir:T,children:[{title:"Data Transition",dir:U}]},{title:"Interaction",dir:q,children:[{title:"Drag",dir:"drag"},{title:"Connect",dir:_,draft:t},{title:"Intelligent Pointer Snapping",dir:$}]}]},{title:"Best Practices",dir:tt,children:[{title:"Mobile Optimization",dir:i,draft:t},{title:it,dir:dt},{title:"Aria",dir:"aria"}]},{title:"Edit Handbook",dir:"meta",children:[{title:"Edit Guide",dir:et}]}]},config:{routerBase:"/echarts-handbook",rootPath:"https://apache.github.io/echarts-handbook/",exampleViewPath:"https://echarts.apache.org/examples/${lang}/view.html?c=",exampleEditorPath:"https://echarts.apache.org/examples/${lang}/editor.html?c=",mainSitePath:"https://echarts.apache.org/",optionPath:"https://echarts.apache.org/option.html#",gitRepo:"apache/echarts-handbook"},i18n:{routeParams:{}}},serverRendered:t,routePath:"/zh/how-to/cross-platform/server",config:{_app:{basePath:"/echarts-handbook/",assetsPath:"/echarts-handbook/_nuxt/",cdnURL:null}}}}(!0,"mobile","visual-map","get-started","basics","download","import","resource","help","release-note","v5-feature","v5-upgrade-guide","5-2-0","5-3-0","5-4-0","5-5-0","concepts","chart-size","options","series","style","dataset","data-transform","coordinate","axis","legend","event","how-to","chart-types","basic-bar","stacked-bar","bar-race","polar-bar","waterfall","line","basic-line","stacked-line","area-line","smooth-line","step-line","basic-pie","doughnut","rose","scatter","basic-scatter","cross-platform","dynamic-data","drilldown","rich-text","animation","transition","interaction","connect","coarse-pointer","best-practices","Canvas vs. SVG","canvas-vs-svg","edit-guide")</script><script src="/echarts-handbook/_nuxt/0323e7b.js" defer></script><script src="/echarts-handbook/_nuxt/js/bbca8d7f75f3cc39fb47.js" defer></script><script src="/echarts-handbook/_nuxt/js/9d86a6602b3f4dd6c979.js" defer></script><script src="/echarts-handbook/_nuxt/js/fa73816a85b29fbf7562.js" defer></script><script src="/echarts-handbook/_nuxt/js/b964c20bb1562bc48da6.js" defer></script><script src="/echarts-handbook/_nuxt/js/d28cad802cfdf9e4dbad.js" defer></script><noscript data-n-head="ssr" data-body="true"><div class="no-script"><strong>很抱歉,Apache ECharts 网站需要启用 JavaScript 才能正常运行。</strong></div></noscript>
<script type="text/javascript">document.getElementById("nav-doc").className="active"</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-141228404-1"></script>
<script>var _hmt=_hmt||[];function gtag(){dataLayer.push(arguments)}!function(){var e=document.createElement("script"),t=(e.src="https://hm.baidu.com/hm.js?54b918eee37cb8a7045f0fd0f0b24395",document.getElementsByTagName("script")[0]);t.parentNode.insertBefore(e,t)}(),window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","UA-141228404-1")</script>
</body>
</html>