中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

做網(wǎng)站每頁面費用/bing搜索引擎國際版

做網(wǎng)站每頁面費用,bing搜索引擎國際版,網(wǎng)站建設(shè) 九艾,東莞兼職招聘網(wǎng)最新招聘有限狀態(tài)機(jī)是一種數(shù)學(xué)計算模型,它描述了在任何給定時間只能處于一種狀態(tài)的系統(tǒng)的行為。形式上,有限狀態(tài)機(jī)有五個部分: 初始狀態(tài)值 (initial state)有限的一組狀態(tài) (states)有限的一組事件 (events)由事件驅(qū)動的一組狀態(tài)轉(zhuǎn)移關(guān)系 (transition…

有限狀態(tài)機(jī)是一種數(shù)學(xué)計算模型,它描述了在任何給定時間只能處于一種狀態(tài)的系統(tǒng)的行為。形式上,有限狀態(tài)機(jī)有五個部分:

  • 初始狀態(tài)值 (initial state)
  • 有限的一組狀態(tài) (states)
  • 有限的一組事件 (events)
  • 由事件驅(qū)動的一組狀態(tài)轉(zhuǎn)移關(guān)系 (transitions)
  • 有限的一組最終狀態(tài) (final states)

狀態(tài)是指由狀態(tài)機(jī)建模的系統(tǒng)中某種有限的、定性的“模式”或“狀態(tài)”,并不描述與該系統(tǒng)相關(guān)的所有(可能是無限的)數(shù)據(jù)。例如,水可以處于以下 4 種狀態(tài)中的一種:冰、液體、氣體或等離子體。然而,水的溫度可以變化,所以其測量值是定量的和無限的。再比如管理TCP Socket連接時,其生命周期內(nèi)存在明顯的有限狀態(tài)轉(zhuǎn)換。

可能有相當(dāng)多的同學(xué)在開發(fā)中沒意識到有限狀態(tài)機(jī)的作用,但是實際上,我們幾乎無時不刻在有意無意間使用了有限狀態(tài)機(jī)。當(dāng)您在開發(fā)過程中能有意識地系統(tǒng)地進(jìn)行有限狀態(tài)分析并應(yīng)用有限狀態(tài)機(jī),往往代表著您達(dá)到了較高的水平。

目前開源的有限狀態(tài)機(jī)實現(xiàn)中比較知名的有:

  • xstate:堪稱狀態(tài)機(jī)航空母艦,功能太強大了,也太復(fù)雜了,學(xué)習(xí)成本非常高。
  • Javascript State Machine:功能較弱,在實際試用過程中發(fā)現(xiàn)在進(jìn)行異步切換時存在問題。
  • jssm:特點是引入自己的DSL語法來描述狀態(tài)機(jī),使用起來比較別扭。

事實上,從功能完整度上看xstate是第一選擇,但是其過于復(fù)雜了,在功能與易用平衡方面并不理想。

因此,我們開發(fā)了FlexState有限狀態(tài)機(jī),力求在功能性、易用性上達(dá)到平衡。

FlexState是一款簡單易用的有限狀態(tài)機(jī),具有以下特性:

  1. 支持基于Class構(gòu)建有限狀態(tài)機(jī)實例
  2. 支持狀態(tài)enter/leave/resume/done鉤子事件
  3. 狀態(tài)切換完全支持異步操作
  4. 支持定義異步狀態(tài)動作Action
  5. 支持狀態(tài)切換生命周期事件訂閱
  6. 支持錯誤處理和狀態(tài)切換中止
  7. 基于TypeScript開發(fā)
  8. 支持子狀態(tài)
  9. 核心代碼90%+單元測試覆蓋率

Github
官網(wǎng)

快速入門

下面我們以開發(fā)基于nodejs/net.socket的TCP客戶端為例來說明FlexStateMachine的使用。

作為例子,我們?yōu)?code>TCPClient設(shè)計以下幾種狀態(tài):

  • Initial:初始狀態(tài),構(gòu)建socket實例后處于該階段。
  • Connecting:連接中,當(dāng)調(diào)用Connect方法,觸發(fā)connect事件前。
  • Connected:已連接,當(dāng)觸發(fā)connect事件后。
  • Disconnecting:正在斷開,當(dāng)調(diào)用destory或end方法后,end/close事件觸發(fā)前。
  • Disconnected:被動斷開,當(dāng)觸發(fā)end/close事件后。
  • AlwaysDisconnected: 主動斷開狀態(tài)
  • IDLE: 自動添加的空閑狀態(tài),狀態(tài)機(jī)未啟動時
  • ERROR: 自動添加的錯誤狀態(tài),特殊的FINAL狀態(tài)

TCPClient的狀態(tài)圖如下:

第一步:構(gòu)建狀態(tài)機(jī)

推薦直接繼承FlexStateMachine來創(chuàng)建一個TCPClient實例,該種方式更加簡單易用。


import { state, FlexStateMachine } from "flexstate"class TcpClient extends FlexStateMachine{// 定義狀態(tài)static states = { Initial : { value:0, title:"已初始化", next:["Connecting","Connected","Disconnected"],initial:true},Connecting	: { value:1, title:"正在連接...", next:["Connected","Disconnected"] },Connected : { value:2, title:"已連接", next:["Disconnecting","Disconnected"] },Disconnecting : { value:3, title:"正在斷開連接...", next:["Disconnected"] },Disconnected : {value:4, title:"已斷開連接", next:["Connecting"]},AlwaysDisconnected	: {value:5, title:"已主動斷開連接", next:["Connecting"]}}                   constructor(options:FlexStateOptions){super(Object.assign({host:"",port:9000,autoStart:true,context           : null,                // 狀態(tài)上下文對象,當(dāng)執(zhí)行動作或狀態(tài)轉(zhuǎn)換事件時的this指向autoStart         : true,                // 自動啟動狀態(tài)機(jī)timeout           : 30 * 1000            // 當(dāng)執(zhí)行狀態(tài)切換回調(diào)時的超時,如enter、leave、done回調(diào)injectActionMethod: true,                // 將動作方法注入到當(dāng)前實例中  },options)) } @state{when:["Initial","Disconnected","Error"],   // 代表只能當(dāng)處于此三種狀態(tài)時才允許調(diào)用連接方法    pending:"Connecting",						// 執(zhí)行后進(jìn)入正在連接中的狀態(tài)}connect(){this._socket.connect(this.options)    } @state({when:["Connected"],     	// 代表只有在已連接狀態(tài)才允許執(zhí)行斷開方法pending:"Disconnecting"})  disconnect(){this._socket.destory()}// 當(dāng)狀態(tài)轉(zhuǎn)換成功后會調(diào)用此方法ontTransition({error,from,to,done,timeConsuming}){console.log(`從<${previous}>轉(zhuǎn)換到<current>,耗時:${timeConsuming}ms`)       // 例 ==> 從<Connecting>轉(zhuǎn)換到<Connected,耗時12ms>console.log(this.current)                          // {name,value,....}}onData(data){....}}

說明:

  • 以上我們創(chuàng)建了一個繼承自FlexStateMachine來創(chuàng)建一個TCPClient實例
  • 并且定義了Initial、Connecting、ConnectedDisconnecting、Disconnected、AlwaysDisconnected共六個狀態(tài)以及狀態(tài)之間的轉(zhuǎn)換約束。同時,狀態(tài)機(jī)還會自動添加一個ERRORIDLE狀態(tài)。
  • 定義了connectdisconnect兩個動作action,在這兩個方法前添加@state代表了當(dāng)執(zhí)行這兩個方法會導(dǎo)致狀態(tài)變化。

第二步:初始化TCPSocket

當(dāng)實例化TCPClient實例后,首先應(yīng)該創(chuàng)建Socket實例。由于TCPClient實例繼承自FlexStateMachine,并且我們指定了Initial為初始化狀態(tài)。
狀態(tài)機(jī)會在實例化并啟動后自動轉(zhuǎn)換到Initial狀態(tài)。因此,我們可以在進(jìn)入Initial狀態(tài)前進(jìn)行初始化操作。

class TcpClient extends FlexStateMachine{// 轉(zhuǎn)換至Initial狀態(tài)前會調(diào)用方法async onInitialEnter({retry,retryCount}){try{      this._socket = new net.Socket()// 當(dāng)連接成功時,切換到Connected事件; 每一個狀態(tài)均有一個大寫的狀態(tài)值實例成員// this.CONNECTED==this.states.Connected.valuethis._socket.on("connect",()=>this.transition(this.CONNECTED)) this._socket.on("close",()=>{//....     詳見后續(xù)重連說明}) 		// 套接字因不活動而超時則觸發(fā),這只是通知套接字已空閑,用戶必須手動關(guān)閉連接。// 通過事件觸發(fā)方式來執(zhí)行disconnect動作this._socket.on("timeout",()=>this.emit("disconnect"))this._socket.on("error",()=>this.transition(this.ERROR))this._socket.on("data",this.onData.bind(this))}catch(e){if(retryCount<3){retry(1000)                                      // 1000ms后重試執(zhí)行}else{				//throw e}      }
}

當(dāng)TCPClient實例化,狀態(tài)機(jī)處于IDLE狀態(tài)(<tcp實例>.current.name=='IDLE'),然后狀態(tài)機(jī)自動啟動(autoStart=true)將轉(zhuǎn)換至Initial狀態(tài)(initial狀態(tài))。

  • 狀態(tài)機(jī)轉(zhuǎn)換至Initial狀態(tài)前會調(diào)用onInitialEnter。我們可以在此方法中創(chuàng)建TCP Socket實例以及其他相關(guān)的初始化。
  • onInitialEnter成功執(zhí)行完畢后,狀態(tài)機(jī)的狀態(tài)將轉(zhuǎn)換至Initial。(IDLE->Initial
  • 如果在onInitialEnter函數(shù)初始化失敗或出錯,則應(yīng)該拋出錯誤。錯誤將導(dǎo)致狀態(tài)機(jī)將無法轉(zhuǎn)換至Initial狀態(tài),也就無法進(jìn)行后續(xù)的所有操作了。一般在初始化失敗時,會進(jìn)行如下操作:
    • 進(jìn)行重試操作,直至初始化成功(即成功創(chuàng)建好Socket并進(jìn)行相應(yīng)的事件綁定)。
    • 反復(fù)重試多次失敗后,也可能會放棄重試,TCP Client將無法切換到Initial狀態(tài),而是保持在IDLE狀態(tài)。
    • 當(dāng)條件具備時,狀態(tài)機(jī)需要重新運行(即調(diào)用tcp.start()來啟動狀態(tài)機(jī)),將重復(fù)上述過程。

第三步:連接服務(wù)器

當(dāng)TCPClient實例初始化完成后,就可以開始連接服務(wù)器。我們可以在類上創(chuàng)建狀態(tài)機(jī)動作connect,啟動連接操作。

import { state, FlexStateMachine } from "flexstate"class TcpClient extends FlexStateMachine{// 通過裝飾器來聲明這是一個狀態(tài)動作  @state({// 代表只能當(dāng)處于此三種狀態(tài)時才允許執(zhí)行動作,即調(diào)用連接方法when:["Initial","Disconnected","Error"],      // 執(zhí)行后進(jìn)入正在連接中的狀態(tài)pending:"Connecting"	})                async connect(){      this._socket.connect(this.options)          }
}
// 創(chuàng)建連接實例
let tcp = new TcpClient({...})
// 連接
tcp.connect()   
// 狀態(tài)機(jī)狀態(tài)將變化: Initial -> Connecting -> Connected
// 如果連接出錯狀態(tài)將變化:Initial -> Connecting -> Error

上述的@state({....})定義了一個狀態(tài)機(jī)動作,代表當(dāng)調(diào)用connect方法時會導(dǎo)致一系列的狀態(tài)轉(zhuǎn)換:

  • 動作名稱為connect,會創(chuàng)建一個同名的實例方法tcp.connect替換掉原始的connect方法。
  • when參數(shù)代表了只有當(dāng)前狀態(tài)為[Initial、DisconnectedError]其中一個時才允許執(zhí)行connect動作。
  • pending="Connecting"代表,執(zhí)行connect動作前,狀態(tài)機(jī)的狀態(tài)將暫時會切換至Connecting,也就是會顯示正在連接中。由于連接操作可能是耗時的,所有設(shè)計一個正在連接中是比較符合實際業(yè)務(wù)邏輯的。
  • 如果執(zhí)行socket.connect({...}) 出錯,可以通過@state({retry,retryCount})來啟用重試邏輯。需要注意的是 調(diào)用connect成功僅僅代表該方法在調(diào)用時沒有出錯,并不代表已經(jīng)連接成功。是否連接成功需要由socket/connect事件來觸發(fā)確認(rèn)。
  • 在上述中,并沒有顯式指定當(dāng)連接成功時的狀態(tài),原因是因為connect方法是一個異步方法,是否連接成功或失敗是通過事件回調(diào)的方式轉(zhuǎn)換狀態(tài)的。在初始化階段,我們訂閱了close、end等回調(diào)。
    • this._socket.on("close",()=>this.transition(this.DISCONNECTED))
    • this._socket.on("end",()=>this.transition(this.DISCONNECTED))
    • this._socket.on("error",()=>this.transition(this.ERROR))

當(dāng)執(zhí)行socket.connect方法后,如果接收到close/end/error則會轉(zhuǎn)換到對應(yīng)的DISCONNECTEDERROR狀態(tài)。

  • 至此,實現(xiàn)了當(dāng)tcp.connect方法,狀態(tài)轉(zhuǎn)換到Connecting狀態(tài),連接成功轉(zhuǎn)換至Connected狀態(tài),連接被斷開轉(zhuǎn)換至Disconnected狀態(tài),出現(xiàn)錯誤時轉(zhuǎn)換到ERROR狀態(tài)。并且在出錯時會進(jìn)行一定重試操作,更多關(guān)于重試的內(nèi)容詳見后續(xù)介紹。

第四步:偵聽連接狀態(tài)

在TCP連接生命周期內(nèi),狀態(tài)機(jī)會在最后Initial/Connecting/Connected/Disconnecting/Disconnected/AlwaysDisconnected狀態(tài)之間進(jìn)行轉(zhuǎn)換,我們希望可能偵聽狀態(tài)機(jī)的狀態(tài)轉(zhuǎn)換事件,以便在連接發(fā)生狀態(tài)轉(zhuǎn)換時進(jìn)行一些操作,此時就可以偵聽各種連接事件。

偵聽連接狀態(tài)有兩種方法:

  • FlexStateMachine本身就是一個EventEmitter,可以通過訂閱事方式進(jìn)行偵聽。
// *****偵聽某個狀態(tài)事件*****tcp.on("Connected/enter",({from,to})=>{// 當(dāng)準(zhǔn)備進(jìn)入連接前狀態(tài)時觸發(fā)此事件
})      tcp.on("Connected/leave",({from,to})=>{// 當(dāng)準(zhǔn)備要離開連接狀態(tài)時觸發(fā)此事件
})  tcp.on("Connected/done",({from,to})=>{// 當(dāng)切換至連接狀態(tài)后觸發(fā)此事件
})   
  • 在類中也可以直接定義on<狀態(tài)名>Enteron<狀態(tài)名>、on<狀態(tài)名>Doneon<狀態(tài)名>Leave類方法來偵聽事件。
class TcpClient extends FlexStateMachine{onInitialEnter({from,to}){...}			// 進(jìn)入Initial狀態(tài)前onInitial({from,to}){...}					// 已切換至Initial狀態(tài)onInitialDone({from,to}){...}				// ===onInitialonInitialLeave({from,to}){...}		  	// 離開Initial狀態(tài)時onConnectingEnter({from,to}){...}			// 進(jìn)入Connecting狀態(tài)前onConnecting({from,to}){...}				// 已切換至Connecting狀態(tài)onConnectingDone({from,to}){...}      	// === onConnectingonConnectingLeave({from,to}){...}		  	// 離開Connecting狀態(tài)時onConnectedEnter({from,to}){...}			// 進(jìn)入Connected狀態(tài)前onConnected({from,to}){...}				// 已切換至Connected狀態(tài)onConnectedDone({from,to}){...}			// ===onConnectedonConnectedLeave({from,to}){...}		  	// 離開Connected狀態(tài)時//...所有狀態(tài)均可以定義on<狀態(tài)名>Enter、on<狀態(tài)名>、on<狀態(tài)名>Leave事件 }

第五步:斷開重新連接

連接管理中的斷開重連是非常重要的功能,要處理此邏輯,首先分析一下什么情況下會斷開連接。

斷開連接一般包括主動被動兩種情況:

  • 服務(wù)器或網(wǎng)絡(luò)問題等導(dǎo)致的連接斷開

此種情況屬于客戶端被動斷開連接,一般會需要進(jìn)行自動重新連接。服務(wù)器主動斷開時,客戶端會偵聽到end事件,直接進(jìn)入斷開狀態(tài)。即狀態(tài)機(jī)不會切換到Disconnecting,而是直接至Disconnected

  • 客戶端主動斷開連接

此種情況屬性客戶主動斷開連接發(fā),就是客戶端主動調(diào)用disconnect方法,一般是不需要進(jìn)行自動重連的。
主動斷開時,需要調(diào)用socket.end方法,然后等待end事件的觸發(fā)。狀態(tài)機(jī)會經(jīng)歷從DisconnectingDisconnected的過程。

無論是主動斷開連接還是被動斷開連接,均會觸發(fā)close事件,因此需要在close事件觸發(fā)時區(qū)別是主動斷開還是被動斷開。
為了更好地區(qū)別主動斷開被動斷開,我們可以增加一個狀態(tài)AlwaysDisconnected來代表是客戶端主動斷開,AlwaysDisconnected被設(shè)計為FINAL狀態(tài)。
當(dāng)狀態(tài)機(jī)切換到Disconnected狀態(tài)時調(diào)用connect動作方法來重新連接。當(dāng)狀態(tài)機(jī)切換到AlwaysDisconnected時,則不進(jìn)行重新連接。
兩者差別在于,如果是主動斷開會經(jīng)歷Disconnecting狀態(tài),而被動斷開則不會經(jīng)過此狀態(tài),因此我們就可以在on("close")事件中處理將狀態(tài)轉(zhuǎn)換至AlwaysDisconnectedDISCONNECTED

class TcpClient extends FlexStateMachine{class TcpClient extends FlexStateMachine{...// 轉(zhuǎn)換至Initial狀態(tài)前會調(diào)用方法async onInitialEnter({retry,retryCount}){// 在此需要確認(rèn)該切換到Disconnected還是AlwaysDisconnected狀態(tài)this._socket.on("close",()=>{// 主動調(diào)用disconnect方法時,狀態(tài)機(jī)才會切換到Disconnectingif(this.current.name==="Disconnecting"){ this.transition(this.ALWAYSDISCONNECTED)}else{this.transition(this.DISCONNECTED)}})}// 當(dāng)切換至Disconnected狀態(tài)的回調(diào)async onDisconnected({from,to}){await delay(3000)this.connect()								// 重新執(zhí)行Connect動作}//async onConnectClosed({from,to}){}@state({when:"Connected",pending:"Disconnecting"// 由于調(diào)用end方法是異步操作,需要等待close事件觸發(fā)后,才是真正的斷開連接 // 因此,不能在調(diào)用disconnected返回后就將狀態(tài)設(shè)置為AlwaysDisconnected// 也就是說不要在此配置rejected參數(shù);// 假設(shè)執(zhí)行this._socket.end沒有出錯,則狀態(tài)將保持在Disconnecting狀態(tài),直至this._socket.on("close",callback)時才進(jìn)行狀態(tài)轉(zhuǎn)換// rejected:""  })async disconnect(){// 注意:此操作是異步狀態(tài)this._socket.end()   }
}

第六步:連接認(rèn)證子狀態(tài)

當(dāng)tcp連接成功后,一般服務(wù)器會要求對客戶連接進(jìn)行認(rèn)證才允許進(jìn)行使用,而認(rèn)證操作(login/logout)是一個耗時的異步操作,同樣需要進(jìn)行狀態(tài)管理。當(dāng)進(jìn)入Connected狀態(tài)后,狀態(tài)將在未認(rèn)證、正在認(rèn)證已認(rèn)證三個狀態(tài)間進(jìn)行轉(zhuǎn)換,并且在連接斷開或者出錯時馬上退出這三個狀態(tài)。因此,就有必要引入子狀態(tài)的概念。

引入子狀態(tài)后,對應(yīng)的狀態(tài)圖更新如下:

在這里插入圖片描述

class TcpClient extends FlexStateMachine{static states = { Connected		: { value:2, title:"已連接", next:["Disconnecting","Disconnected","Error"] // 定義一個獨立的狀態(tài)機(jī)域scope:{states:{Unauthenticated : {value:0,title:"未認(rèn)證",initial:true,next:["Authenticating"]},Authenticating  : {value:1,title:"正在認(rèn)證",next:["Authenticated"]}Authenticated   : {value:2,title:"已認(rèn)證",next:["Unauthenticated"]},}}},  }  ......// 當(dāng)狀態(tài)機(jī)進(jìn)入Connected后會啟動其子狀態(tài)機(jī)// 子狀態(tài)機(jī)會轉(zhuǎn)換到其初始狀態(tài)Unauthenticated,然后就可以在此執(zhí)行登錄動作async onUnauthenticatedEnter({from,to}){this.login()								// }onAuthenticated({from,to}){}@state({when:["Authenticating"],pending:["Authenticating"]})async login(){await this.send({// 認(rèn)證信息})}@state({when:["Authenticated"] })async logout(){await this.send({// 注銷信息})    }
}

推薦

以下是我的一大波開源項目推薦:

  • 全流程一健化React/Vue/Nodejs國際化方案 - VoerkaI18n
  • 無以倫比的React表單開發(fā)庫 - speedform
  • 終端界面開發(fā)增強庫 - Logsets
  • 簡單的日志輸出庫 - VoerkaLogger
  • 裝飾器開發(fā) - FlexDecorators
  • 有限狀態(tài)機(jī)庫 - FlexState
  • 通用函數(shù)工具庫 - FlexTools
  • 小巧優(yōu)雅的CSS-IN-JS庫 - Styledfc
  • 為JSON文件添加注釋的VSCODE插件 - json_comments_extension
  • 開發(fā)交互式命令行程序庫 - mixed-cli
  • 強大的字符串插值變量處理工具庫 - flexvars
  • 前端link調(diào)試輔助工具 - yald
  • 異步信號 - asyncsignal
http://www.risenshineclean.com/news/273.html

相關(guān)文章:

  • 企業(yè)網(wǎng)站建設(shè)話術(shù)/營銷網(wǎng)站
  • wordpress pdf生成/手機(jī)端網(wǎng)站優(yōu)化
  • 工信部外國網(wǎng)站備案/b站在線觀看人數(shù)在哪
  • wordpress 時尚主題/駐馬店百度seo
  • 嘉興網(wǎng)站排名優(yōu)化價格/北京網(wǎng)站
  • 網(wǎng)站建設(shè)部門/網(wǎng)站軟件下載
  • 建立收費網(wǎng)站/網(wǎng)絡(luò)銷售面試問題有哪些
  • 做搞基視頻網(wǎng)站/網(wǎng)絡(luò)營銷的案例有哪些
  • 網(wǎng)站怎么做下載連接/百度長尾關(guān)鍵詞挖掘
  • 網(wǎng)站彈窗客服代碼/刷推廣鏈接
  • 政府網(wǎng)站公眾號建設(shè)方案/谷歌瀏覽器 安卓下載2023版
  • wordpress tutorial/seo優(yōu)化排名服務(wù)
  • 網(wǎng)站被谷歌降權(quán)/廣州seo招聘網(wǎng)
  • 網(wǎng)站聊天怎么做/最新軍事報道
  • 成都微信端網(wǎng)站建/蘇州seo按天扣費
  • 網(wǎng)站沒有百度快照/全網(wǎng)絡(luò)品牌推廣
  • 制作企業(yè)網(wǎng)站需要注意的事項/地推是什么
  • 資料填寫網(wǎng)站類型怎么做/新聞發(fā)稿公司
  • 免費建網(wǎng)站撫順/win10優(yōu)化大師有用嗎
  • 萬盛網(wǎng)站建設(shè)公司/當(dāng)下最流行的營銷方式
  • 下載好看影視大全極速版/seo是什么工作內(nèi)容
  • 重慶響應(yīng)式網(wǎng)站建設(shè)公司/哪個軟件可以自動排名
  • python源碼分享網(wǎng)站/深度搜索
  • 龍華網(wǎng)站建設(shè)方案表/免費海報模板網(wǎng)站
  • 關(guān)鍵詞seo優(yōu)化/優(yōu)化大師官方免費下載
  • 百度指數(shù) 網(wǎng)站/杭州優(yōu)化公司哪家好
  • 哈爾濱市建設(shè)網(wǎng)站/寧波網(wǎng)絡(luò)推廣產(chǎn)品服務(wù)
  • 湛江網(wǎng)站建設(shè)哪家好/網(wǎng)絡(luò)營銷公司全網(wǎng)推廣公司
  • 個人可以做淘寶客網(wǎng)站嗎/網(wǎng)絡(luò)營銷首先要進(jìn)行
  • 免費制作單頁的網(wǎng)站/媒體推廣