Vue 折騰記 – (7) 寫一個挺不靠譜的Vue-Echarts元件

CRPER發表於2017-07-25

前言

上基友社群看了下,發現對echarts的封裝都是打包進去的…想想就還是算了..
圖表這貨.說實在的,若不是整個系統大量用到,打包進去沒必要…

CDN是個好東西,我們完全可以寫一個非同步載入JS然後封裝按需呼叫…

至於你能學到什麼,見仁見智了…不過有所收穫就是我這文章的意義所在了

廢話不多說,看效果圖;

  • 2017-10-19: 修正初次渲染及切換傳值渲染錯誤
  • 2017-08-09: 加入圖表資料更新監聽
  • 2017-08-31 : 防止多次append cdnjs

效果圖

實現思路及實現的功能

  • 非同步引入echarts,有三個版本,不是最大也不是最小那個,具體上官網看
  • 圖表的銷燬釋放記憶體
  • 圖表跟隨父包含塊自適應(事件監聽)
  • setOption的抽離,圖表例項化id隨機生成或者手動傳入
    • setOption可以一次性傳入整個圖表的引數,也可以拆分傳入(太多,只選了幾個複用率很高選項的)…都設定了預設值.
    • 隨機id是大寫字母的組合,外部有傳入則用外部的

程式碼

實現Vue.use(?)

  • index.js — 匯出元件的,內部實在亮瞎眼
import echarts from "./echarts";
// Vue.use必須有install這個函式..可能我這裡寫的比較粗糙..有興趣的可以去完善// 用是可以正常使用的export default {
install: function(Vue, Option) {
Vue.component("vue-echarts", echarts);

}
};
複製程式碼
  • echarts.vue
<
template>
<
div class="vue-echarts" :id="id" :style="style">
<
/div>
<
/template>
<
script>
export default {
name: 'vue-echarts', data: function () {
return {
loadJS: '', // 儲存非同步載入echart的promise狀態 chart: '', // 儲存地圖初始化的例項
}
}, props: {
id: {
type: String, default: function () {
// 生成一個隨機字串,純英文的,當不傳入ID的時候生成例項,加i確保不會隨機到一樣的 let temp = [];
for (let i = 0;
i <
6;
i++) {
let randomChar = String.fromCharCode(65 + Math.floor(Math.random() * 19) + i);
temp.push(randomChar);

} return temp.reduce((pre, next) =>
pre + next);

}
}, height: {
// 圖表高度 type: String, default: '300px'
}, width: {
// 圖表寬度 type: String, default: '100%'
}, legend: {
// 以下的配置都是echarts官方圖表的自定義部分,我拆分這麼幾大塊 type: Object, default: function () {
return {
data: ['郵件營銷', '聯盟廣告', '視訊廣告', '直接訪問', '搜尋引擎']
}
}
}, title: {
type: Object, default: function () {
return {
text: '堆疊區域圖'
}
}
}, tooltip: {
type: [Array, Object], default: function () {
return {
trigger: 'axis', axisPointer: {
type: 'cross', label: {
backgroundColor: '#6a7985'
}, animation: true
}
}
}
}, toolbox: {
type: Object, default: function () {
return {
show: true, feature: {
dataZoom: {
yAxisIndex: 'none'
}, dataView: {
readOnly: false
}, magicType: {
type: ['line', 'bar']
}, restore: {
}, saveAsImage: {
}
}
}
}
}, grid: {
type: Object, default: function () {
return {
left: '3%', right: '4%', bottom: '3%', containLabel: true
}
}
}, xAxis: {
type: [Array, Object], default: function () {
return [ {
type: 'category', boundaryGap: true, data: ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00', '24:00']
} ]
}
}, yAxis: {
type: [Array, Object], default: function () {
return [ {
type: 'value'
} ]
}
}, series: {
type: [Array, Object], default: function () {
return [ {
name: '曝光', type: 'line', smooth: true, lineStyle: {
normal: {
color: '#f00',
}
}, data: [120, 132, 101, 134, 90, 230, 210, 120, 132, 101, 134, 90, 120]
}, {
name: '點選', type: 'line', smooth: true, lineStyle: {
normal: {
color: '#20a0ff',
}
}, data: [220, 182, 191, 234, 290, 330, 310, 120, 132, 101, 134, 90, 120]
}, {
name: '點選率', type: 'line', lineStyle: {
normal: {
color: '#42b983',
}
}, smooth: true, data: [150, 232, 201, 154, 190, 330, 410, 120, 132, 101, 134, 90, 120]
} ]
}
}, setOption: {
type: Object
}, dispose: Boolean
}, computed: {
style () {
return {
height: this.height, width: this.width
}
}
}, created () {
this.loadJS = this.loadEchartsJS();

}, mounted () {
this.loadJS.then(() =>
{
let st = setTimeout(() =>
{
this.init();
// CDNJS載入後,元素渲染後,初始化圖表
}, 300);

}).catch(err =>
{
console.log('echarts js載入失敗');

});

}, beforeDestroy () {
window.removeEventListener('resize', this.chart.resize) if (this.chart) {
this.chart.dispose();
// 銷燬圖表例項
}
}, methods: {
init () {
// 初始化圖表 this.chart = new echarts.init(document.getElementById(this.id));
this.chart.setOption(this.setOption);
window.addEventListener('resize', this.chart.resize) // 圖表響應大小的關鍵,重繪
}, loadEchartsJS () {
// 非同步請求cdnjs const CDNURL = 'https://cdn.bootcss.com/echarts/3.7.1/echarts.min.js';
return new Promise((resolve, reject) =>
{
const script = document.createElement('script');
script.type = "text/javascript";
script.id = "cdnEchart";
if (script.readyState) {
//IE script.onreadystatechange = function () {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
resolve('success: ' + CDNURL);

}
};

} else {
//Others script.onload = function () {
resolve('success: ' + CDNURL);

};

} script.onerror = function () {
reject(Error(CDNURL + 'load error!'));

};
script.src = CDNURL;
if (!document.getElementById('cdnEchart')) {
document.head.appendChild(script);

}
});

}
}, watch: {
setOption: {
handler: function (newVal, oldVal) {
// 監聽外部傳入的值,渲染新的的圖表資料 if (this.chart) {
if (newVal) {
this.chart.setOption(newVal);

} else {
this.chart.setOption(oldVal);

} this.chart.resize();

} else {
setTimeout(() =>
{
this.init();

}, 300);

}
}, deep: true
}
}
}
<
/script>
複製程式碼

如何使用?

  • main.js — 主入口檔案,
// promise的polyfillrequire("es6-promise").polyfill();
// 百度圖表import echarts from './components/common/echarts';
Vue.use(echarts);
複製程式碼

總結

好吧,寫這個東西花了一下午的時間,從構思到去查echarts api到實現;

粗糙的版本已經誕生,可能還有可以優化的地方,只能待我以後發現了再做調整了.

有更好的實現方案和思路的可以往下面留言..

來源:https://juejin.im/post/597712ea51882548a13c196a

相關文章