成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

tradingView--最專業(yè)的走勢圖表,收下吧,也許你會用到

canopus4u / 3032人閱讀

摘要:進階二進制傳輸數(shù)據(jù)在傳輸數(shù)據(jù)的時候是明文傳輸,而且像線上的歷史數(shù)據(jù),一般數(shù)據(jù)量比較大。為了安全性以及更快的加載出圖表,我們決定使用二進制的方式傳輸數(shù)據(jù)。

前言

最近在做交易所項目里的K線圖,得些經(jīng)驗,與大家分享。

代碼居多,流量預(yù)警!?。?!

點贊 收藏 不迷路。

技術(shù)選型

echrats

基于canvas繪制,種類齊全的可視化圖表庫。

官方地址: https://echarts.baidu.com/

highcharts

基于svg繪制,可定制化內(nèi)容很多,操縱dom更方便。

官方地址: https://www.highcharts.com.cn/

tradingview

基于canvas的專業(yè)全球化走勢圖表。

官方地址: https://cn.tradingview.com/

優(yōu)缺點

hightcharts: 前些日子有仔細研究過 hightcharts https://www.fota.com/option。 發(fā)現(xiàn)svg中的dom操作,以及定制化內(nèi)容更好實現(xiàn),但幾乎都需要手動實現(xiàn)的這個特性在開發(fā)周期短的壓迫下屈服了。上面的這個項目在慢慢摸索下也做了小三個月的樣子,但還是很有成就感的。

echrats: echarts的官方案例很多,經(jīng)常在做一些后臺管理系統(tǒng),展現(xiàn)數(shù)據(jù)時候會用到,方便,易用,使用者也足夠多,搜索引擎雞本能夠解決你的任何問題。但對一些在圖上劃線,等操作,就顯得略微疲軟。不夠能滿足需求。

tradingview: 只要進入官網(wǎng),就可見其專業(yè)性,他完全就是為了專業(yè)交易兒打造的,您只需要想里面填充數(shù)據(jù)就可以了,甚至在一些常用的交易內(nèi)容上,可以使用tradingview自己的數(shù)據(jù)推送。

小記

所以,專業(yè)的交易圖表,就交給專業(yè)的庫來做吧

手動狗頭~~~~(∩_∩)

準(zhǔn)備工作

申請賬號(key)

在官網(wǎng)注冊后會有郵件提示的,一步一步跟著做就可以了,這里就不做贅述了。

環(huán)境搭建

我使用的是自己搭建的React+webpack4腳手架,你也可以使用原生JS,或者你喜歡的任何框架(后面貼出來的代碼都是在React環(huán)境下的)。

從官方下載代碼庫

了解websocket通訊協(xié)議

發(fā)送請求

接收數(shù)據(jù)

大綱

這里附上tradingview中文開發(fā)文檔 https://b.aitrade.ga/books/tr...

以及api示例 http://tradingview.github.io/... (此處需要自備梯子)

一位大神的Demo https://github.com/tenggouwa/...

準(zhǔn)備開始吧

創(chuàng)建

  page
  |--kLine // k線內(nèi)容文件夾
  |--|--api // 需要使用的方法
  |--|--|--datafees.js // 定義了一些公用方法
  |--|--|--dataUpdater.js // 更新時調(diào)用的內(nèi)容
  |--|--|--socket.js // websocket方法
  |--|--index.js // 自己代碼開發(fā)
  |--|--index.scss // 樣式開發(fā)

datafees.js加入如下代碼

    import React from "react"
    import DataUpdater from "./dataUpdater"
    
    class datafeeds extends React.Component {
        constructor(self) {
            super(self)
            this.self = self
            this.barsUpdater = new DataUpdater(this)
            this.defaultConfiguration = this.defaultConfiguration.bind(this)
        }
        onReady(callback) {
            // console.log("=============onReady running")
            return new Promise((resolve) => {
                let configuration = this.defaultConfiguration()
                if (this.self.getConfig) {
                    configuration = Object.assign(this.defaultConfiguration(), this.self.getConfig())
                }
                resolve(configuration)
            }).then(data => callback(data))
        }
        getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onDataCallback) {
            const onLoadedCallback = (data) => {
                data && data.length ? onDataCallback(data, { noData: false }) : onDataCallback([], { noData: true })
            }
            this.self.getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback)

        }
        resolveSymbol(symbolName, onSymbolResolvedCallback, onResolveErrorCallback) {
            return new Promise((resolve) => {
                // reject
                let symbolInfoName
                if (this.self.symbolName) {
                    symbolInfoName = this.self.symbolName
                }
                let symbolInfo = {
                    name: symbolInfoName,
                    ticker: symbolInfoName,
                    pricescale: 10000,
                }
                const { points } = this.props.props
                const array = points.filter(item => item.name === symbolInfoName)
                if (array) {
                    symbolInfo.pricescale = 10 ** array[0].pricePrecision
                }
                symbolInfo = Object.assign(this.defaultConfiguration(), symbolInfo)
                resolve(symbolInfo)
            }).then(data => onSymbolResolvedCallback(data)).catch(err => onResolveErrorCallback(err))
        }
        subscribeBars(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) {
            this.barsUpdater.subscribeBars(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback)
        }
        unsubscribeBars(subscriberUID) {
            this.barsUpdater.unsubscribeBars(subscriberUID)
        }
        defaultConfiguration = () => {
            const object = {
                session: "24x7",
                timezone: "Asia/Shanghai",
                minmov: 1,
                minmov2: 0,
                description: "www.coinoak.com",
                pointvalue: 1,
                volume_precision: 4,
                hide_side_toolbar: false,
                fractional: false,
                supports_search: false,
                supports_group_request: false,
                supported_resolutions: ["1", "15", "60", "1D"],
                supports_marks: false,
                supports_timescale_marks: false,
                supports_time: true,
                has_intraday: true,
                intraday_multipliers: ["1", "15", "60", "1D"],
            }
            return object
        }
    }
    
    export default datafeeds

dataUpdater加入如下代碼

class dataUpdater {
    constructor(datafeeds) {
        this.subscribers = {}
        this.requestsPending = 0
        this.historyProvider = datafeeds
    }
    subscribeBars(symbolInfonwq, resolutionInfo, newDataCallback, listenerGuid) {
        this.subscribers[listenerGuid] = {
            lastBarTime: null,
            listener: newDataCallback,
            resolution: resolutionInfo,
            symbolInfo: symbolInfonwq
        }
    }
    unsubscribeBars(listenerGuid) {
        delete this.subscribers[listenerGuid]
    }
    updateData() {
        if (this.requestsPending) return
        this.requestsPending = 0
        for (let listenerGuid in this.subscribers) {
            this.requestsPending++
            this.updateDataForSubscriber(listenerGuid).then(() => this.requestsPending--).catch(() => this.requestsPending--)
        }
    }
    updateDataForSubscriber(listenerGuid) {
        return new Promise(function (resolve, reject) {
          var subscriptionRecord = this.subscribers[listenerGuid];
          var rangeEndTime = parseInt((Date.now() / 1000).toString());
          var rangeStartTime = rangeEndTime - this.periodLengthSeconds(subscriptionRecord.resolution, 10);
          this.historyProvider.getBars(subscriptionRecord.symbolInfo, subscriptionRecord.resolution, rangeStartTime, rangeEndTime, function (bars) {
            this.onSubscriberDataReceived(listenerGuid, bars);
            resolve();
          }, function () {
            reject();
          });
        });
    }
    onSubscriberDataReceived(listenerGuid, bars) {
        if (!this.subscribers.hasOwnProperty(listenerGuid)) return
        if (!bars.length) return
        const lastBar = bars[bars.length - 1]
        const subscriptionRecord = this.subscribers[listenerGuid]
        if (subscriptionRecord.lastBarTime !== null && lastBar.time < subscriptionRecord.lastBarTime) return
        const isNewBar = subscriptionRecord.lastBarTime !== null && lastBar.time > subscriptionRecord.lastBarTime
        if (isNewBar) {
            if (bars.length < 2) {
                throw new Error("Not enough bars in history for proper pulse update. Need at least 2.");
            }
            const previousBar = bars[bars.length - 2]
            subscriptionRecord.listener(previousBar)
        }
        subscriptionRecord.lastBarTime = lastBar.time
        console.log(lastBar)
        subscriptionRecord.listener(lastBar)
    }
    periodLengthSeconds =(resolution, requiredPeriodsCount) => {
        let daysCount = 0
        if (resolution === "D" || resolution === "1D") {
            daysCount = requiredPeriodsCount
        } else if (resolution === "M" || resolution === "1M") {
            daysCount = 31 * requiredPeriodsCount
        } else if (resolution === "W" || resolution === "1W") {
            daysCount = 7 * requiredPeriodsCount
        } else {
            daysCount = requiredPeriodsCount * parseInt(resolution) / (24 * 60)
        }
        return daysCount * 24 * 60 * 60
    }
}
export default dataUpdater

socket.js加入如下代碼(也可以使用自己的websocket模塊)

    class socket {
        constructor(options) {
            this.heartBeatTimer = null
            this.options = options
            this.messageMap = {}
            this.connState = 0
            this.socket = null
        }
        doOpen() {
            if (this.connState) return
            this.connState = 1
            this.afterOpenEmit = []
            const BrowserWebSocket = window.WebSocket || window.MozWebSocket
            const socketArg = new BrowserWebSocket(this.url)
            socketArg.binaryType = "arraybuffer"
            socketArg.onopen = evt => this.onOpen(evt)
            socketArg.onclose = evt => this.onClose(evt)
            socketArg.onmessage = evt => this.onMessage(evt.data)
            // socketArg.onerror = err => this.onError(err)
            this.socket = socketArg
        }
        onOpen() {
            this.connState = 2
            this.heartBeatTimer = setInterval(this.checkHeartbeat.bind(this), 20000)
            this.onReceiver({ Event: "open" })
        }
        checkOpen() {
            return this.connState === 2
        }
        onClose() {
            this.connState = 0
            if (this.connState) {
                this.onReceiver({ Event: "close" })
            }
        }
        send(data) {
            this.socket.send(JSON.stringify(data))
        }
        emit(data) {
            return new Promise((resolve) => {
                this.socket.send(JSON.stringify(data))
                this.on("message", (dataArray) => {
                    resolve(dataArray)
                })
            })
        }
        onMessage(message) {
            try {
                const data = JSON.parse(message)
                this.onReceiver({ Event: "message", Data: data })
            } catch (err) {
                // console.error(" >> Data parsing error:", err)
            }
        }
        checkHeartbeat() {
            const data = {
                cmd: "ping",
                args: [Date.parse(new Date())]
            }
            this.send(data)
        }
        onReceiver(data) {
            const callback = this.messageMap[data.Event]
            if (callback) callback(data.Data)
        }
        on(name, handler) {
            this.messageMap[name] = handler
        }
        doClose() {
            this.socket.close()
        }
        destroy() {
            if (this.heartBeatTimer) {
                clearInterval(this.heartBeatTimer)
                this.heartBeatTimer = null
            }
            this.doClose()
            this.messageMap = {}
            this.connState = 0
            this.socket = null
        }
    }
    export default socket

初始化圖表

可以同時請求websocket數(shù)據(jù)。

新建init函數(shù),并在onready/mounted/mounted等時候去調(diào)用(代碼的含義在注釋里,我盡量寫的詳細一點)

+

init = () => {
    var resolution = this.interval; // interval/resolution 當(dāng)前時間維度
    var chartType = (localStorage.getItem("tradingview.chartType") || "1")*1;
    var locale = this.props.lang; // 當(dāng)前語言
    var skin = this.props.theme; // 當(dāng)前皮膚(黑/白)
    if (!this.widgets) {
        this.widgets = new TradingView.widget({ // 創(chuàng)建圖表
            autosize: true, // 自動大小(適配,寬高百分百)
            symbol:this.symbolName, // 商品名稱
            interval: resolution,
            container_id: "tv_chart_container", // 容器ID
            datafeed: this.datafeeds, // 配置,即api文件夾下的datafees.js文件
            library_path: "/static/TradingView/charting_library/", // 圖表庫的位置,我這邊放在了static,因為已經(jīng)壓縮過
            enabled_features: ["left_toolbar"],
            timezone: "Asia/Shanghai", // 圖表的內(nèi)置時區(qū)(常用UTC+8)
            // timezone: "Etc/UTC", // 時區(qū)為(UTC+0)
            custom_css_url: "./css/tradingview_"+skin+".css", //樣式位置
            locale, // 語言
            debug: false,
            disabled_features: [ // 在默認情況下禁用的功能
                "edit_buttons_in_legend",
                "timeframes_toolbar",
                "go_to_date",
                "volume_force_overlay",
                "header_symbol_search",
                "header_undo_redo",
                "caption_button_text_if_possible",
                "header_resolutions",
                "header_interval_dialog_button",
                "show_interval_dialog_on_key_press",
                "header_compare",
                "header_screenshot",
                "header_saveload"
            ],
            overrides: this.getOverrides(skin), // 定制皮膚,默認無蓋默認皮膚
            studies_overrides: this.getStudiesOverrides(skin) // 定制皮膚,默認無蓋默認皮膚
        })
        var thats = this.widgets;
        // 當(dāng)圖表內(nèi)容準(zhǔn)備就緒時觸發(fā)
        thats.onChartReady(function() {
            createButton(buttons);
        })
        var buttons = [
            {title:"1m",resolution:"1",chartType:1},
            {title:"15m",resolution:"15",chartType:1},
            {title:"1h",resolution:"60",chartType:1},
            {title:"1D",resolution:"1D",chartType:1},
        ];
        // 創(chuàng)建按鈕(這里是時間維度),并對選中的按鈕加上樣式
        function createButton(buttons){
            for(var i = 0; i < buttons.length; i++){
                (function(button){
                    let defaultClass =
                    thats.createButton()
                    .attr("title", button.title).addClass(`mydate ${button.resolution === "15" ? "active" : ""}`)
                    .text(button.title)
                    .on("click", function(e) {
                        if (this.className.indexOf("active")> -1){// 已經(jīng)選中
                            return false
                        }
                        let curent =e.currentTarget.parentNode.parentElement.childNodes
                        for(let index of curent) {
                            if (index.className.indexOf("my-group")> -1 && index.childNodes[0].className.indexOf("active")> -1) {
                                index.childNodes[0].className = index.childNodes[0].className.replace("active", "")
                            }
                        }
                        this.className = `${this.className} active`
                        thats.chart().setResolution(button.resolution, function onReadyCallback() {})
                    }).parent().addClass("my-group"+(button.resolution == paramary.resolution ? " active":""))
                })(buttons[i])
            }
        }
    }
}
請求數(shù)據(jù)

新建initMessage函數(shù)---在需要去獲取數(shù)據(jù)的時候,調(diào)取initMessage。

    initMessage = (symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback) => {
        let that = this
        //保留當(dāng)前回調(diào)
        that.cacheData["onLoadedCallback"] = onLoadedCallback;
        //獲取需要請求的數(shù)據(jù)數(shù)目
        let limit = that.initLimit(resolution, rangeStartDate, rangeEndDate)
        //如果當(dāng)前時間節(jié)點已經(jīng)改變,停止上一個時間節(jié)點的訂閱,修改時間節(jié)點值
        if(that.interval !== resolution){
            that.interval = resolution
            paramary.endTime = parseInt((Date.now() / 1000), 10)
        } else {
            paramary.endTime = rangeEndDate
        }
        //獲取當(dāng)前時間段的數(shù)據(jù),在onMessage中執(zhí)行回調(diào)onLoadedCallback
        paramary.limit = limit
        paramary.resolution = resolution
        let param
        // 分批次獲取歷史(這邊區(qū)分了歷史記錄分批加載的請求)
        if (isHistory.isRequestHistory) {
            param = {
                // 獲取歷史記錄時的參數(shù)(與全部主要區(qū)別是時間戳)
            }
        } else {
            param = {
                // 獲取全部記錄時的參數(shù)
            }
        }
        this.getklinelist(param)
    }

在請求歷史數(shù)據(jù)時,由于條件不滿足,會一直請求后臺接口,所以需要加上 函數(shù)節(jié)流

在lodash這個庫里面是有節(jié)流的方法的

首先引入節(jié)流函數(shù)----import throttle from "lodash/throttle"

使用非常簡單,只要在函數(shù)前面套一層-----this.initMessage = throttle(this.initMessage, 1000);

throttle()函數(shù)里面,第一個參數(shù)是需要截留的函數(shù),第二個為節(jié)流時間。

收到數(shù)據(jù),渲染圖表

可以在接收數(shù)據(jù)的地方調(diào)用socket.on("message", this.onMessage(res.data))

onMessage函數(shù),是為渲染數(shù)據(jù)進入圖表內(nèi)容

// 渲染數(shù)據(jù)
onMessage = (data) => { // 通過參數(shù)將數(shù)據(jù)傳遞進來
    let thats = this
    if (data === []) {
        return
    }
    // 引入新數(shù)據(jù)的原因,是我想要加入緩存,這樣在大數(shù)據(jù)量的時候,切換時間維度可以大大的優(yōu)化請求時間
    let newdata = []
    if(data && data.data) {
        newdata = data.data
    }
    const ticker = `${thats.symbolName}-${thats.interval}`
    // 第一次全部更新(增量數(shù)據(jù)是一條一條推送,等待全部數(shù)據(jù)拿到后再請求)
    if (newdata && newdata.length >= 1 && !thats.cacheData[ticker] && data.firstHisFlag === "true") {
        // websocket返回的值,數(shù)組代表時間段歷史數(shù)據(jù),不是增量
        var tickerstate = `${ticker}state`
        // 如果沒有緩存數(shù)據(jù),則直接填充,發(fā)起訂閱
        if(!thats.cacheData[ticker]){
            thats.cacheData[ticker] = newdata
            thats.subscribe()   // 這里去訂閱增量數(shù)據(jù)?。。。。。。?        }
        // 新數(shù)據(jù)即當(dāng)前時間段需要的數(shù)據(jù),直接喂給圖表插件
        // 如果出現(xiàn)歷史數(shù)據(jù)不見的時候,就說明 onLoadedCallback 是undefined
        if(thats.cacheData["onLoadedCallback"]){ // ToDo
            thats.cacheData["onLoadedCallback"](newdata)
        }
        //請求完成,設(shè)置狀態(tài)為false
        thats.cacheData[tickerstate] = false
        //記錄當(dāng)前緩存時間,即數(shù)組最后一位的時間
        thats.lastTime = thats.cacheData[ticker][thats.cacheData[ticker].length - 1].time
    }
    // 更新歷史數(shù)據(jù) (這邊是添加了滑動按需加載,后面我會說明)
    if(newdata && newdata.length > 1 && data.firstHisFlag === "true" && paramary.klineId === data.klineId && paramary.resolution === data.resolution && thats.cacheData[ticker] && isHistory.isRequestHistory) {
        thats.cacheData[ticker] = newdata.concat(thats.cacheData[ticker])
        isHistory.isRequestHistory = false
    }
    // 單條數(shù)據(jù)()
    if (newdata && newdata.length === 1 && data.hasOwnProperty("firstHisFlag") === false && data.klineId === paramary.klineId  && paramary.resolution === data.resolution) {
        //構(gòu)造增量更新數(shù)據(jù)
        let barsData = newdata[0]
        //如果增量更新數(shù)據(jù)的時間大于緩存時間,而且緩存有數(shù)據(jù),數(shù)據(jù)長度大于0
        if (barsData.time > thats.lastTime && thats.cacheData[ticker] && thats.cacheData[ticker].length) {
            //增量更新的數(shù)據(jù)直接加入緩存數(shù)組
            thats.cacheData[ticker].push(barsData)
            //修改緩存時間
            thats.lastTime = barsData.time
        } else if(barsData.time == thats.lastTime && thats.cacheData[ticker] && thats.cacheData[ticker].length){
            //如果增量更新的時間等于緩存時間,即在當(dāng)前時間顆粒內(nèi)產(chǎn)生了新數(shù)據(jù),更新當(dāng)前數(shù)據(jù)
            thats.cacheData[ticker][thats.cacheData[ticker].length - 1] = barsData
        }
        // 通知圖表插件,可以開始增量更新的渲染了
        thats.datafeeds.barsUpdater.updateData()
    }
}

邏輯中心===>getbars

新建getbars函數(shù)(該函數(shù)會在圖表有變化時自動調(diào)用)

    getBars = (symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback) => {
        const timeInterval = resolution // 當(dāng)前時間維度
        this.interval = resolution
        let ticker = `${this.symbolName}-${resolution}`
        let tickerload = `${ticker}load`
        var tickerstate = `${ticker}state`
        this.cacheData[tickerload] = rangeStartDate
        //如果緩存沒有數(shù)據(jù),而且未發(fā)出請求,記錄當(dāng)前節(jié)點開始時間
        // 切換時間或幣種
        if(!this.cacheData[ticker] && !this.cacheData[tickerstate]){
            this.cacheData[tickerload] = rangeStartDate
            //發(fā)起請求,從websocket獲取當(dāng)前時間段的數(shù)據(jù)
            this.initMessage(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback)
            //設(shè)置狀態(tài)為true
            this.cacheData[tickerstate] = true
        }
        if(!this.cacheData[tickerload] || this.cacheData[tickerload] > rangeStartDate){
            //如果緩存有數(shù)據(jù),但是沒有當(dāng)前時間段的數(shù)據(jù),更新當(dāng)前節(jié)點時間
            this.cacheData[tickerload] = rangeStartDate;
            //發(fā)起請求,從websocket獲取當(dāng)前時間段的數(shù)據(jù)
            this.initMessage(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback);
            //設(shè)置狀態(tài)為true
            this.cacheData[tickerstate] = !0;
        }
        //正在從websocket獲取數(shù)據(jù),禁止一切操作
        if(this.cacheData[tickerstate]){
            return false
        }
        // 拿到歷史數(shù)據(jù),更新圖表
        if (this.cacheData[ticker] && this.cacheData[ticker].length > 1) {
            this.isLoading = false
            onLoadedCallback(this.cacheData[ticker])
        } else {
            let self = this
            this.getBarTimer = setTimeout(function() {
                self.getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback)
            }, 10)
        }
        // 這里很重要,畫圈圈----實現(xiàn)了往前滑動,分次請求歷史數(shù)據(jù),減小壓力
        // 根據(jù)可視窗口區(qū)域最左側(cè)的時間節(jié)點與歷史數(shù)據(jù)第一個點的時間比較判斷,是否需要請求歷史數(shù)據(jù)
        if (this.cacheData[ticker] && this.cacheData[ticker].length > 1 && this.widgets && this.widgets._ready && !isHistory.isRequestHistory && timeInterval !== "1D") {
            const rangeTime = this.widgets.chart().getVisibleRange()  // 可視區(qū)域時間值(秒) {from, to}
            const dataTime = this.cacheData[ticker][0].time // 返回數(shù)據(jù)第一條時間
            if (rangeTime.from * 1000 <= dataTime + 28800000) { // true 不用請求 false 需要請求后續(xù)
                isHistory.endTime = dataTime / 1000
                isHistory.isRequestHistory = true
                // 發(fā)起歷史數(shù)據(jù)的請求
                this.initMessage(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback)
            }
        }
    }

小記

tradingview主要就是這幾個函數(shù)之間的搭配。

使用onLoadedCallback(this.cacheData[ticker])或者this.datafeeds.barsUpdater.updateData()去更新數(shù)據(jù)。

滑動加載時,可以先加載200條,后面每次150條,這樣大大縮小了數(shù)據(jù)量,增加了渲染時間。

滑動加載時的節(jié)流會經(jīng)常用到。

進階websocket

二進制傳輸數(shù)據(jù)

websocket在傳輸數(shù)據(jù)的時候是明文傳輸,而且像K線上的歷史數(shù)據(jù),一般數(shù)據(jù)量比較大。為了安全性以及更快的加載出圖表,我們決定使用二進制的方式傳輸數(shù)據(jù)。

可以通過使用pako.js解壓二進制數(shù)據(jù)

引入pako.jsyarn add pako -S

使用方法

if (res.data instanceof Blob) { // 看下收到的數(shù)據(jù)是不是Blob對象
    const blob = res.data
    // 讀取二進制文件
    const reader = new FileReader()
    reader.readAsBinaryString(blob)
    reader.onload = () => {
        // 首先對結(jié)果進行pako解壓縮,類型是string,再轉(zhuǎn)換成對象
        data = JSON.parse(pako.inflate(reader.result, { to: "string" }))
    }
}

轉(zhuǎn)換后,數(shù)據(jù)大小大概減少了20%。

差不多了

寫在最后

這里只分享些簡單的內(nèi)容,細節(jié)可以參照原生js版本的Demo https://github.com/tenggouwa/...

關(guān)于滾動加載,以及二進制的內(nèi)容有問題的可以評論留言。

如果這篇文章對你有幫助,或者是讓您對tradingview有些了解,歡迎留言或點贊,我會一一回復(fù)。

筆者最大的希望就是您能從我的文章里獲得點什么,我就很開心啦。。。

后面,至少每個月更新一篇文章。點贊關(guān)注不迷路啊,老鐵。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/105473.html

相關(guān)文章

  • tradingView--專業(yè)走勢圖表,收下也許你會用到

    摘要:進階二進制傳輸數(shù)據(jù)在傳輸數(shù)據(jù)的時候是明文傳輸,而且像線上的歷史數(shù)據(jù),一般數(shù)據(jù)量比較大。為了安全性以及更快的加載出圖表,我們決定使用二進制的方式傳輸數(shù)據(jù)。 前言 最近在做交易所項目里的K線圖,得些經(jīng)驗,與大家分享。 代碼居多,流量預(yù)警?。。。?點贊 收藏 不迷路。 技術(shù)選型 echrats showImg(https://segmentfault.com/img/remote/14...

    tianyu 評論0 收藏0
  • Vue中引入TradingView制作K線圖

    摘要:圖表庫希望通過僅一次調(diào)用,接收所有的請求歷史。更新后臺返回線最新的數(shù)據(jù)網(wǎng)上比較少關(guān)于引入的文章小弟不才粗略的分享一下我的實現(xiàn)方法 **前言: 本文使用的是1.10版本 , 可通過TradingView.version()查看當(dāng)前版本. 附上開發(fā)文檔地址:https://zlq4863947.gitbooks.i...** 一、修改datafeed.js為export導(dǎo)出,并在vue文件...

    codergarden 評論0 收藏0
  • tradingview入門

    摘要:今年七月入坑,中間斷斷續(xù)續(xù)做了別的項目,因為沒有完全掌握這個插件,所以一直沒有嵌入項目。引入圖表上有可以下載,支持多種語言,但是缺少關(guān)鍵的核心庫,這個需要到官網(wǎng)申請獲得。官方使用的數(shù)據(jù)獲取方式為獲取,數(shù)據(jù)接口是官方提供的。 今年七月入坑,中間斷斷續(xù)續(xù)做了別的項目,因為沒有完全掌握這個插件,所以一直沒有嵌入項目?,F(xiàn)在已經(jīng)四個月過去了,迭代工作沒那么忙,是時候整合tradingview到項...

    douzifly 評論0 收藏0
  • tradingview-websocket進階

    摘要:用戶量量大,數(shù)據(jù)量大,而且要求實時更新數(shù)據(jù)的時候,需要使用。該方法接收的有兩種,一種是數(shù)組。是歷史數(shù)據(jù),時間段的數(shù)據(jù),根據(jù)時間顆粒來劃分。 1、websocket 用戶量量大,數(shù)據(jù)量大,而且要求實時更新數(shù)據(jù)的時候,需要使用websocket。tradingview正好就是這樣的應(yīng)用場景。 2、tradingview和websocket結(jié)合 getBars方法。tradingview圖...

    Cc_2011 評論0 收藏0
  • tradingView-webSocket進階——Vue.js

    摘要:無奈,還是需要對這份代碼進行加工。功能缺少,主要指業(yè)務(wù)邏輯實現(xiàn)上的功能缺少。缺少的功能主要是歷史記錄獲取展示的功能。查詢緩存是否為空,如果為空,表示數(shù)據(jù)還沒有下發(fā),后再查詢一次。如果有數(shù)據(jù),取到當(dāng)前數(shù)據(jù),執(zhí)行回調(diào)。 前幾天寫了一篇關(guān)于tradingView和webSocket的文章傳送門,因為代碼本身還在整合中,所以比較混亂,而且也沒有demo可以運行。這兩天在GitHub上面看到了一...

    levius 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<