TradingView + 웹 소켓 K 온라인 실시간 구덩이 가이드를 밀어

이 문서는 재현 원숭이 2048 웹 사이트 ➝ https://www.mk2048.com/blog/blog.php?id=hik0hihjaa

[일부터 내 개인 블로그 ]

0보다 알림 밝혀졌다

이틀 전 회사의 지도자들은 실제로 내가 최근 게으른, 업데이트되지 않습니다 내 블로그를 언급 한 ......

이 작업을 될 때, 등 휴일을 활용, 신속하게 업데이트 ...... 그리고?

1. TradingView는 게이샤입니다

- 오늘 우리는 특별한 기능을 말한다 TradingView 의 K 선 그래프, 선 그래프에 전념 전문 차트 라이브러리와 K는 주식, 펀드 및 기타 교환 필수적인 일이다. 이 프로젝트 자체는 무료이지만 공식 Github에서의 개인 라이브러리에서 호스팅되지 오픈 소스, 개발자는 단지 당신이에 액세스 할 수 있습니다, 공식에 필요한 정보를 제출해야합니다. 홈페이지 창고는 압축 된 라이브러리 파일과 간단한 데이터 액세스의 경우를 포함, 또한 시작하는 경우에 다른 창고를 제공하면서 위키는 개발 문서를 제공합니다.

몇 가지 일반적으로 사용되는 프런트 엔드 차트 라이브러리는 ECharts처럼 DataV 실제로는 막대 차트와 라인 차트, 기본적인 K-라인 다이어그램 (일부는 촛불, 그것을 다른 이름이라고합니다)을 지원 그릴뿐만 아니라, MA를 볼륨을 그릴 및 기타 지표. TradingView 전문 산업 제품으로, 앞서 언급 한 이러한 차트뿐만 아니라, 또한 전문 투자자를위한 전문적인 측정 도구를 제공하고, 분석가들은 필요한 모든이를 개발자가 구현 된 경우로 가야 사용 많은 에너지를 보내고,이 패키지 패키지 프로그램, 그것은 의심 할 여지없이 가장 매력적인 장소입니다.

그는 예외 동료가이 차트 라이브러리도 불 동전, 대형의 FCoin 업계 벤치 마크 수준을 선택했습니다 거의없는 것을 발견했을 때 최근 회사 진행중인 프로젝트는 디지털 자산의 교환은 제품을 연구 경쟁이다 공장은 업계에서 그 권위 사이에 볼이 차트 라이브러리뿐만 아니라 거의 독점에 대한 거부했다. 정확하게 이것 때문에, 우리는 또한 공부하기 시작했다.

2. 전문 === 문제

전문가가 전문 가야하지만, 전문적인 개념, 용어, 관행의 많은이 있습니다이 특정 산업 개발의 특정 요구에 뭔가, 우리가 알고하지 않았다, 그녀는 지금 학습했다. 공식 양식 문서에서 Github에서 위키에서 제공하지만, 문서의 품질이 매우 일반적이지만,이 매개 변수를 주석하는 것은 불완전 누락, 많은 작업이지만, 모호한 개념을 많이 가득 라인 사이, 모든 측면을 커버하는 것 세부 사항은 언급되지 않은, 독서 경험은 매우 나쁘다. 프로젝트의 공식 웹 사이트는 중국어 옵션을 제공하지만, 라이브러리를 차트 자체도 여러 언어를 지원하지만 저 개인적으로, 언어는 압력을 구성 그 자체로하지 않습니다 있지만 문서 (영어로만입니다,하지만 당신이 필요로하는 경우, 여기 중국 사람의 종류가있다 버전은 또한 UDF 프로그램을 기반으로 비디오 자습서를 포함, TradingView 프로젝트 팀에서 저자는, 베테랑 개발자이다. 설명을 용이하게하기 위해, 여기에 몇 가지 수치로, 감사를 사용합니다 저자 ).

비교 ECharts는 모든 준비가 DataV 것을, 그냥 데이터 소스의 자신의 세트를 달성하기 위해, 그 규칙에 따라 개발하는 개발자를 필요로 어려운 많은 높은 것으로 TradingView를 시작하려면 "상업용 등급"차트 라이브러리의 매개 변수를 사용하여 데이터를 입력 API는 각각의 역할에 대한 공식 API 있지만, 매개 변수가 지시 주어 지지만, 핵심 포인트 중 일부와는 명확하게 설명하지 않았다 (나를 포함한 일부 동료와 나는 가로 질러 온) 많은 개발자 여부를 문서를 읽은 후 잘 이해 "를 사용하는 방법 결국이 TM." 이 블로그를 작성, 나는 초보자가 쉽게 할 수 있도록이 문제를 해결하기 위해 어떤 기여를 기대하고있다.

시간을 절약하기 위해 3

나는이 블로그는 전체 일을 구축하기 위해 단계별로 가르쳐하지 않습니다 것입니다. 난 당신이 적어도 공식 문서가 검색 엔진에 다시 읽고, 예비 시도가, 문제가, 설정하는 것입니다 가정하고 여기에왔다.

이 블로그는 당신과 함께 공유하는 내 개인적인 이해에 따르면, 구덩이를 강화 것들을 이해하기 어려운 몇 가지를 넣어 더 내 자신의 경험에 기초한 자주 묻는 질문, 같다.

당신이 공식 문서로 이동하지 않도록 당신이 블로그에 믿을 수있는 경우에 따라서 완전히 TradingView을 파악 할 수있을 것이다, 쉽게 K 라인을 그려, 나는 미안 해요, 당신을 실망.

4. 나 개념에 대해 이야기하자

TradingView하지 이해하기 쉽게,하지만 매우 중요한, 더 전문화 된 개념의 일부가 여기에 간략하게 설명합니다.

4.1. 상징

기호 직역는 생각이었다 "기호, 상징"이며, "상품." K-라인 성능, 뭔가의 가격, 당신은 재고 수, 통화 할 수 있습니다, 어떤 상품, GM에 대한 TradingView처럼 될 수 있으며, 가격의 추세와 같은 추상적 인 개념을 제공합니다. 상징은 기호의 정의에 따라 라이브러리를 차트, 상품의 특성 (이름, 가격 소수점, 시간 해상도 지원, 거래 영업 시간. 공식 문서를 참조하십시오)의 일부를 설명하는 JS 개체입니다, 얻을 변경하기로 결정 어떤 데이터의 종류.

고정 형식은 상품명이다 "EXCHANGE : SYMBOL"이러한 주식, 거래와 같은 상품의 기호를 대신하여, 교환은 교환의 이름, 동일한 제품이 다른 교류에서 다른 가격을 가질 수있다, 구별 할 필요가있다.

4.2. 해결

당신은, 단어의 사용은 결의 직역가 호출 "해상도가"여기 두 개의 인접한 열 K 선 그래프 사이의 시간 간격을 의미한다, 나는 용어가되지 않는 공부하지 않은,하지만 개인적으로 느낌이 말하는 방법입니다 생각을 표현할 수있는 다른 단어를 사용하지만, TradingView은이 단어를 선택했다.

4.3. 연구

직역을 공부하는 것은 여기에 같은 볼륨으로 "목표", 이동 평균, 및 기타 다양한 분석 지표로 해석 "학습, 연구"입니다. 개발자는 제공되는 API의 TradingView을 통해 자신을 추가 할 수 있습니다.

4.4. 차트

표 본체, 특히 K 라인 도면 및 관련 지표는, 툴바를 포함하지 않는다. 예를 도시하는 그래프 표시 복수를 포함 할 수있다

4.5. 위젯

위젯 및 위젯 안드로이드의 개념. 차트 구성 요소 자체가 컨테이너로 주로하기 때문에 약간의 도구 모음을 볼 수, 실제 차트 예약 영역을 그릴 수있는 차트가 몸이 포함되어 있지 않습니다. 그것은 예를 나타내는 그래프 위젯 인스턴스의 복수를 포함 할 수있다

4.6. 된 형상

기능 세트, 일부 기능 (표시 여부, 스타일 포함) 차트 라이브러리를 정의하기위한 구성 옵션의 위젯 부분.

4.7. 오버라이드 (override)

커버, 차트 스타일 라이브러리 (그래프의 각 부분의 주로 색상) 사용자 정의 구성 옵션의 위젯 부분입니다. DOM 전체 차트 라이브러리 외층 캔버스 복수 이루어지는 내부 구조로 구성되어, 상기 스타일에 관한 설정도 캔버스의 부분이 외에도 제공되는 두 부분으로 분할되어 custom_css_url등록 커버 될 수있는 파일 CSS를 지정하는 데 사용 DOM 스타일 섹션. 특정는 찾을 공식 문서뿐만 아니라 크롬 DevTool와 결합 될 수있다.

4.8. 데이터 피드

데이터 소스, 즉, 다음 일에 대해 이야기한다. 그것은 TradingView 수집, 처리 된 데이터를 수집하고, 또한 달성하기 위해 사용자가 필요 TradingView의 핵심 데이터를 액세스. 이 클래스의 인스턴스는 간단한 오브젝트 것을 일 수있다.

자신의 데이터에 액세스하는 방법 (5)

데이터베이스 인스턴스 차트를 작성하는 문서를 읽고 이해 할 수 있어야한다 케이스를 사용하는 것이 어렵지 않습니다, 어려움은 데이터를 입력하는 방법에있다. 나는 TradingView 두통 친구의 대부분이 한 데이터가 켜져로, 여기에 갇혀있다 생각하고, 나머지는 사소한 문제입니다.

한 지정된 형식으로 구성 같은 라인에 기입하는, 아무리 당신이 어떤 종류의 데이터 자체가 성능의 일부만을 제공하는 라이브러리를 차트, 그것은했고, 성능 데이터를 분리하는 가장 일반적인를 TradingView 없습니다. , 분명 개발자가 자신의 어댑터를 구현하기위한 필요성을 넣어합니다.

TradingView 데이터, HTTP 방식을 얻기 위해 두 가지 방법을 제공 기반 웹 소켓 기반 프로그램 (JS의 API) (UDF는, 범용 데이터 피드, 프리젠 테이션의 경우는 이런 종류의 주요 저장소를 사용하는 것입니다).

udf_or_jsapi

새로운 세대 이후 날짜 역사적 데이터, 데이터 : 프로그램은 두 부분으로 나눌 수 있습니다 데이터의 관점에서 그것을 사용 상관없이.

5.1. UDF 프로그램

수행
UDF는 사용자 정의 프로토콜 세트 자신의 TradingView입니다. 기본적으로 실제로 JS API를했다. 프로토콜은 HTTP + 새로운 데이터를 확인하기 위해 역사적 HTTP를 통해 특정 조건 하에서 데이터 후 지속적으로 여론 조사를 조회하는 폴링 요청을 기반으로합니다.

이 프로그램은 매우 간단합니다, 앞 부분은 정의 된 단지 (데모 코드는 큰 문제가 약간의 여분의인지 비용이, 타이프 라이터를 쓴 것이 아니라) 그 위에 제공되는 데모 코드 액세스 인터페이스 경우에 따라 주요 업무는 요구 사항에 따라 적절한 쿼리 인터페이스를 제공하는 것이 필요하다, 백 엔드이며, 핵심은의, 지정된 상품을 얻으 해상도, 특정 시간의 데이터 범위가 특정 형식은 공식 문서를 참조 할 수 있습니다 지정하는 것입니다. 여기에서 우리는 수행하지 않았습니다.

폴링 - 우리는 매우 자주 새로운 데이터를 매우 낭비 성능을 얻을 수 실패 때문이 매우 유효하지만, 추천하지 연습 (환경이 웹 소켓을 지원하지 않습니다하지 않는 한, 그것은 단지 그것을 사용하는 것) 알고있다. 우리의 희망은 새로운 데이터가 도착할 때마다, 우리는 또한 다음과 같은 구조로 연결되는 알리기 위해 주도권을 쥐고 수 있다는 것입니다.

5.2. JS API를

JSAPI
이 핵심 TradingView 데이터 액세스, 당신은 가장 일반적인 또는 웹 소켓 물론,이 API 개발자를 통해 모든 유형의 데이터에 액세스 할 수 있습니다. UDF는 프로그램이 실제로 이러한 API를 호출하기 전에 말했다.

각 API의 공식 문서는 필수적있는, 설명 onReady(),, resolveSymbol(), getBars(), subscribeBars(), unsubscribeBars()나머지는 우리가 가장 기본적인 사용을 말할 경우, 달성하기 위해 자신의 필요에 따라. 처음 두 어려움, 우리는 몇 가지의 뒤쪽에 모습을 집중합니다. (여기서 우리는 단순히 이러한 기능을 포함 JS 객체를 생성 할 수 있습니다, 달성의 datafeed 클래스 메서드의 형태로 예를)

5.2.1. getBars ()

이 인터페이스는, 즉, 현재 시각 이전의 데이터 인, 기록 된 데이터를 획득하도록 설계된다. 해상도 TradingView에 따르면,이 시간 범위에서 얻으려고 노력, 시간을 앞으로 현재 시간에서 이동 지정하기 시작 기호 해상도 데이터를 지정된 지정합니다. 성능상의 이유로, TradingView 세그먼트 게으른 로딩을 확장, 차트를 끌어하므로 데이터의 가시 범위를 넘어, 단지 눈에 보이는 범위의 데이터를 획득.

코드의 이러한 부분은 우리가 단계적으로 데이터를 전송하는 기능 안에 구현되어,보다 달성 :

getBars (symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) {
  function _send (data) {
    // 按时间筛选
    const dataInRange = data.length
      ? data.filter(n => n.time >= ensureMilliseconds(from) && n.time <= ensureMilliseconds(to))
      : []

    // 没有数据就返回 noData
    const meta = {
      noData: !dataInRange.length
    }

    // 有数据,则整理成图表库要求的格式
    const bar = [...dataInRange]

    // 触发回调
    onHistoryCallback(bar, meta)
  }
}

우리의 함수로서이를 참조하여 getBars()상기 내부 함수, from, to, onHistoryCallbackAPI에 의해 제공되는 파라미터들은, data우리는 데이터를 획득 (bar, meta)고정 형태 TradingView 요구.

이 기능은 콜백 함수를 호출 할 책임이있다, 우리는 차트에 데이터를 인수했다. 다음으로, 우리는 (데모 코드, 비밀, 호환 코드의 일부는 가장 기본적인, 공개적으로 논리를 떠나, 생략 된) 데이터를 얻을 :

getBars (symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) {

  function _send (data) {
    // ...
  }

  // 出于数据共享的需要
  // 我们把获取到的数据放到 Redux 里
  // 先尝试从 Redux 获取现有数据
  const existingData = store.getState().kChartData || []

  // 如果已有数据,直接读取
  if (existingData.length) {
    _send(existingData)
    return
  }

  // 没数据则通过 WebSocket 加载
  // 我们的设计是历史数据和实时更新都走 WebSocket
  // 首次推送历史数据,后续推送更新
  // 所以同一交易对、分辨率,只会发起一个 WebSocket 请求

  // 先判断功能支持度
  // 这里我们用 WebWorker 把 WebSocket 的逻辑独立到主线程之外
  // 以达到性能优化的目的,这个后面再详述。
  if (!window.Worker) return

  // 限制 Worker 单例
  const hasWSInstance = !!window.kChartWorker
  window.kChartWorker = window.kChartWorker || new window.Worker('./worker-kchart.js')

  // WebWorker 数据推送回调
  window.kChartWorker.onmessage = e => {
    const { data = {} } = e

    // 当有数据推送时
    if (data.kChartData) {
      // 获取已有数据
      const kChartData = [...store.getState().kChartData]
      // 因为数据更新都在末端,所以倒序以加速搜索
      const kChartDataReversed = [...kChartData].reverse()

      // 将更新合并到已有数据
      for (const item of [...data.kChartData]) {
        const idx = kChartDataReversed.findIndex(n => n.time === item.time)
        idx < 0
          ? kChartData.push(item)
          : kChartData[kChartData.length - 1 - idx] = { ...kChartData[idx], ...item }
      }

      // 把新数据记录到 Redux
      const promise = new Promise((resolve, reject) => {
        store.dispatch(setKChartData(kChartData))
        resolve({
          total: kChartData,
          updates: [...data.kChartData]
        })
      })

      promise.then(res => {
        // dataInited 是我们自定义的一个变量
        // 用来区分首次推送和后续推送
        if (this.dataInited) {
          // 如非首次推送
          // 对全局 K 线订阅列表中的每个订阅者(后面详述)
          window.kChartSubscriberList = window.kChartSubscriberList || []
          for (const sub of window.kChartSubscriberList) {
            // 按交易对、分辨率筛选
            if (sub.symbol.id !== this.symbol.id) return
            if (sub.resolution !== resolution) return

            // 通过回调函数推送数据
            if (typeof sub.callback !== 'function') return
            // 图表库一次只能增加一条数据,或更新离现在时间最近的一条历史数据
            // 而我们的推送数据是个数组,可能会包含不止一条数据
            // 所以这里要逐个推送
            for (const update of res.updates) {
              sub.callback(update)
            }
          }
        } else {
          // 首次推送
          _send(res.total)
          this.dataInited = true
        }
      })
    }
  }

  // 准备 WebWorker 消息
  // 只有当没有现成数据的时候
  // 才会执行到这里
  // 所以只有在初始化、切换交易对/分辨率的时候
  // 才会发起 WebSocket 请求
  const msg = {
    action: hasWSInstance ? 'restart' : 'init',
    symbol: symbolInfo,
    resolution: resolution,
    url: WEBSOCKET_URL
  }

  // 发送 WebWorker 消息
  window.kChartWorker.postMessage(msg)
}

여기, 우리는 성공적으로 기록 데이터를 취득하고, 다양한 가입자를 밀어 실시간 업데이트를 보내 (이론적으로는 가능 항상 한 가입자 있지만,하지만 시스템 설계의 관점에서, 우리는 설계)했다.

getBars()다행히도, 사실, 사실 그 일만큼 명확한 메커니즘, 아무것도 특히 어려운,보다 최적화 된 데이터 구조 설계 및 성능을 제공합니다. 나는 많은 사람들이 다음과 같은 기능을 이해 생각한다.

5.2.2. subscribeBars ()

문서는이 기능 "과 함께 데이터 라인 K에 가입하는 데 사용됩니다 말한다 많은 사람들이 오해하는 콜백 하나 개의 통화 만"두 단어, ,, 한 번만 호출됩니다 완전한 기록 데이터가 끝난 얻을 필요한 실시간 푸시 얻을 에 달성 할 수 있습니다. 사실상, 이것은 단지 가입자 추가 외층에 저장된 데이터를 업데이트하는 콜백 함수를 추가하고, 콜백 함수는 앞에 실제로 내부 마쳤다. 이 기능은 기록 팀 전용 동등한 데이터 수집 및 배포되는 모든 내부 수행.getBars()onHistoryCallbackgetBars()subscribeBars()getBars()getBars()

subscribeBars (symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) {
  // 限制单例
  window.kChartSubscriberList = window.kChartSubscriberList || []

  const found = window.kChartSubscriberList.some(n => n.uid === subscriberUID)
  if (found) return

  // 添加订阅
  window.kChartSubscriberList.push({
    symbol: symbolInfo,
    resolution: resolution,
    uid: subscriberUID,
    callback: onRealtimeCallback
  })
}

조합의이 기능 + 해상도 각 기호에 대해 한 번 호출됩니다, 전달하고 콜백 함수에 해당하는 식별 정보 목록, 푸시 데이터가 도착하면, 자격을 갖춘 가입자를 찾기 위해 구독 목록을 통과한다 통과 콜백 함수를 호출에 가입 데이터입니다. 실제로 기본 "관찰자 모드"이다.

5.2.3. unsubscribeBars ()

마무리를 알아보기 subscribeBars()사실에 있음을, unsubscribeBars()매우 명확하고 단순 통과있다 :

unsubscribeBars (subscriberUID) {
  window.kChartSubscriberList = window.kChartSubscriberList || []

  const idx = window.kChartSubscriberList.findIndex(n => n.uid === subscriberUID)
  if (idx < 0) return

  window.kChartSubscriberList.splice(idx, 1)
}

6. 거래 / 해상도를 전환하는 방법

위젯 인스턴스를 생성 한 후, 특정 방법으로 차트 인스턴스를 얻을 수 있습니다, 그리고 특정 방법으로 기호와 해상도를 업데이트하고, 업데이트 된 몇 가지 기능은 이전 작업이 다시 트리거 새 매개 변수를 것이다 언급했다. 이러한 관점에서, 이러한 기능 라이프 사이클 기능과 같은 비트, 데이터 수집을 설명 동작의 타이밍이 개발자는 뭔가를 할 때 결정 발생을 업데이트 구독.

this.widget = new window.TradingView.widget(widgetOptions)
this.widget.onChartReady(() => {
  this.chart = this.widget.chart()

  // 设置图表类型(比如分时图和常规的蜡烛图的类型就不一样)
  this.chart.setChartType(chartType)

  // 切换 Symbol
  this.chart.setSymbol(symbol, callback)

  // 切换 Resolution
  this.chart.setResolution(resolution, callback)
})

다른 구덩이 7 TradingView

  • 의 JS API 함수가 자동으로 수동으로 전화를 얻기 위해 외부 층의 기능에 관계없이 인수를 전달, 적절한 시간에 호출됩니다.
  • JS API는에 onReady()그리고 resolveSymbol()이 두 가지 기능들은 비동기 적 이유를 묻는 사람들이 요구하지 않는, 콜백 함수를 호출해야합니다.
  • 같은 설정이 콜백이 트리거되지 않은 경우 기호 및 해상도 전환 함수는 콜백, 새의 가치와 기존의 현재 값을 가지고있다.

제 K-광고 성능 최적화

웹 소켓을 사용하는 과정에서, 우리는 WebWorker 성능 최적화를 사용했다. 거래 빈도가 일정 수준에 도달하면에 직접 로직의이 부분 조립 반응하는 경우, 웹 소켓은 자주 새로운 데이터가 이동, 클라이언트에 데이터를 밀어 버린다 setState()즉시 심하게 (고통스러운 스틱, 다음 페이지가 될 것입니다 교훈). 원리는 매우 간단, 매우 짧은 시간 간격이되고 setState(), 캐시, 업데이트 할 데이터가 자주 관개에 와서 계속하면, 불필요한 계산 및 렌더링을 줄이기 위해 하나로 병합, 그녀는 많은 업데이트 커밋되지 않습니다 저장 구성 요소가 항상 렌더링 다음 라운드로 갈 수 없어, 플러스 단위로 올 때마다 새로운 데이터가 통합 될 필요가 고주파 컴퓨팅 부하가 충분한 컴퓨팅 자원 페이지의 결과로, 기존의 데이터와 리소스의 메인 스레드를 차지합니다 렌더링, 페이지도 붙어.

다음 프로그램이 나올 것이 이해, 이러한 컴퓨팅 집약적 인 작업을하는 것입니다, 메인 스레드에서 수행하는, 즉 WebWorker이며, 동시 스레드에 나갔다. 그러나 단지 메인 쓰레드의 계산 다운로드하지만, 계산을 지불하기에 충분하지만, 업데이트하지 여전히 매우 자주있다. 과학 데이터는 약 0.1 초 인간의 시각 체류 시간, 즉 페이지의 숫자가 정말 수십 배 이상 더 시간을 보지하는 인간의 눈의 사용에서 1 초 변경할 경우에도 말을하는 것입니다 것을 보여 관점 1 초 우리는 완충 지대를 설정할 수 있습니다 4-5 회가 완전히 영향을주지 않습니다 0.5 초마다 업데이트, 그래서 우리는 페이지를 업데이트하는 주파수에 따라 웹 소켓 데이터를 밀어 필요가없는 경우에도 한계의 변화, 웹 소켓에서 데이터의 작은 배열에 캐시하지 그들이하지 말아야 할 것에 대한 메인 스레드를 통보하는 업데이트가, 컨텐츠의 배열이 있는지 여부를 확인하기 위해 고정 된 시간 간격으로, 이상 밀어 때문에 성능과 효율성 사이에 발견 균형.

어떤 사람들은 WebWorker은, 결국, 일반 거의이 페이지 H5, 요리하지를 사용하지 호환성 문제에 대해 우려됩니다. WebWorker 브라우저 호환성과 웹 소켓은 대략 우리가 우려하고 최소한의 범위에서, 동일, IE 10 동일하다 당신이하지 않는 한 골동품이 호환되어야하므로 위, 아이비 브라우저는 이미 말할 필요도없이 지원 요구, 나머지는 좋은으로 안심하시기 바랍니다.

9. 요약

이 프로젝트의 교환, 최근 몇 년 동안 상대적으로 커야한다, 그것은 이전에 구입하고 학습과 접촉하지 않은 중 많은 것들, 많은을 포함, 프로젝트를 인수했다. 피트 많이 발생했습니다, 또한 더 작은 성장이 없습니다. 후속 I가 발생 다른 구덩이를 공유합니다.


보다 전문적인 프런트 엔드 지식의 확인 [2048] 원숭이 www.mk2048.com을

추천

출처www.cnblogs.com/dssx/p/11774671.html