最簡單的 v-show
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="importmap">
{
"imports": {
"echarts": "http://localhost:8080/FrontEnd/assets/js/echarts/echarts.esm.js",
"vue": "http://localhost:8080/FrontEnd/assets/js/vue2/vue.esm.browser.js"
}
}
</script>
<style>
.example {
width: 50%;
height: 500px;
border: 1px solid red;
}
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<button @click="showEchart">{{show}}</button>
<div ref="chart-container" class="example" id="main" v-show="show"></div>
</div>
<script type="module">
import * as echarts from 'echarts';
import Vue from 'vue';
new Vue({
el: '#app',
data() {
return {
show: false,
myChart: null,
options: {
title: {
text: 'ECharts 入門示例',
},
tooltip: {},
xAxis: {
data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子'],
},
yAxis: {},
series: [
{
name: '銷量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
},
],
},
};
},
methods: {
showEchart() {
this.show = !this.show;
// 等待容器掛載完成
this.$nextTick(() => {
// 容器為百分比大小,顯示的時候重新計算獲取具體畫素值
this.myChart.resize();
});
},
},
mounted() {
// 由於容器已經在 dom tree 中,可以直接獲的容器
this.myChart = echarts.init(this.$refs['chart-container']);
this.myChart.setOption(this.options);
},
});
</script>
</body>
</html>
來個騷操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="importmap">
{
"imports": {
"echarts": "http://localhost:8080/FrontEnd/assets/js/echarts/echarts.esm.js",
"vue": "http://localhost:8080/FrontEnd/assets/js/vue2/vue.esm.browser.js"
}
}
</script>
<style>
.example {
width: 50%;
height: 500px;
border: 1px solid red;
}
</style>
</head>
<body>
<div id="app">
<div ref="chart-container" class="example" id="main"></div>
</div>
<script type="module">
import * as echarts from 'echarts';
import Vue from 'vue';
let options = {
title: {
text: 'ECharts 入門示例',
},
tooltip: {},
xAxis: {
data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子'],
},
yAxis: {},
series: [
{
name: '銷量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
},
],
};
echarts.init(document.getElementById('main')).setOption(options);
setTimeout(() => {
new Vue({
el: '#app',
data() {
return {
options: {
title: {
text: 'ECharts 入門示例',
},
tooltip: {},
xAxis: {
data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子'],
},
yAxis: {},
series: [
{
name: '銷量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
},
],
},
};
},
});
}, 1000);
</script>
</body>
</html>
一開始有 echarts 渲染,1s 後畫布被清空,這個和 Vue 的渲染原理有關
<div id="app">
<div ref="chart-container" class="example" id="main"></div>
</div>
Vue 在處理這段 html 時,將 html 字串作為模版在 Vue 內部解析,將{{msg}},v-on:click 等處理,然後在渲染時,不是使用原來的元素,而是建立同名元素,將原來元素的所有特性複製到新元素上,掛載的是新元素。
首先 echarts 處理完成後,dom tree 為
<div id="app">
<div
ref="chart-container"
class="example"
id="main"
style="user-select: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); position: relative;"
_echarts_instance_="ec_1719372515943"
>
<div
style="position: relative; width: 727px; height: 500px; padding: 0px; margin: 0px; border-width: 0px; cursor: default;"
>
<!-- canvas 畫布有內容 -->
<canvas
data-zr-dom-id="zr_0"
width="1454"
height="1000"
style="position: absolute; left: 0px; top: 0px; width: 727px; height: 500px; user-select: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); padding: 0px; margin: 0px; border-width: 0px;"
></canvas>
</div>
<div class=""></div>
</div>
</div>
交給 Vue 處理,Vue 只是建立同名元素,比如 canvas,但並未在新的 canvas 上畫東西,所以 Vue 處理完成,掛載到 DOM 上時,畫布是空白的。
既然是新的 canvas 替換了已經畫過東西的 canvas,那麼我可以在新的 canvas 上重新畫嗎?
new Vue({
mounted() {
echarts.init(document.getElementById('main')).setOption(options);
},
});
不行。因為 echarts 對容器有快取
檔案:echarts/src/core/echarts.ts
const DOM_ATTRIBUTE_KEY = '_echarts_instance_';
export function getInstanceByDom(dom: HTMLElement): EChartsType | undefined {
return instances[modelUtil.getAttribute(dom, DOM_ATTRIBUTE_KEY)];
}
export function init(
dom?: HTMLElement | null,
theme?: string | object | null,
opts?: EChartsInitOpts
): EChartsType {
const existInstance = getInstanceByDom(dom);
if (existInstance) {
if (__DEV__) {
warn('There is a chart instance already initialized on the dom.');
}
return existInstance;
}
}
class ECharts {
dispose() {
const dom = this.getDom();
if (dom) {
// 透過屬性值清快取
modelUtil.setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
}
}
}
那就清了 echarts 快取,重新畫
new Vue({
mounted() {
echarts.dispose(document.getElementById('main'));
echarts.init(document.getElementById('main')).setOption(options);
},
});
稍微複雜點的 v-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="importmap">
{
"imports": {
"echarts": "http://localhost:8080/FrontEnd/assets/js/echarts/echarts.esm.js",
"vue": "http://localhost:8080/FrontEnd/assets/js/vue2/vue.esm.browser.js"
}
}
</script>
<style>
.example {
width: 50%;
height: 500px;
border: 1px solid red;
}
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<button @click="showEchart">{{show}}</button>
<div ref="chart-container" class="example" id="main" v-if="show"></div>
</div>
<script type="module">
import * as echarts from 'echarts';
import Vue from 'vue';
new Vue({
el: '#app',
data() {
return {
show: false,
myChart: null,
options: {
title: {
text: 'ECharts 入門示例',
},
tooltip: {},
xAxis: {
data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子'],
},
yAxis: {},
series: [
{
name: '銷量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
},
],
},
};
},
methods: {
showEchart() {
this.show = !this.show;
// 導致重複渲染,建議用 v-show
this.$nextTick(() => {
if (this.show) {
// 每次都是一個新的 div.chart-container 元素
this.myChart = echarts.init(this.$refs['chart-container']);
this.myChart.setOption(this.options);
} else {
echarts.dispose(this.myChart);
this.myChart = null;
}
});
},
},
mounted() {
if (this.$refs['chart-container']) {
this.myChart = echarts.init(this.$refs['chart-container']);
this.myChart.setOption(this.options);
}
},
});
</script>
</body>
</html>