vue大型電商項目尚品匯(前臺篇)day03

来源:https://www.cnblogs.com/heymar/archive/2022/05/20/16294088.html
-Advertisement-
Play Games

堆積了兩天一起發的,先祝大家節日快樂 後面任務很繁重,還有登錄註冊組件還有後臺管理頁面,真的繁重,我現在感覺每天全天時間都在學都不一定學得完,主要想在六月一號之前把整個項目過一遍。看看能不能創造奇跡 一.防抖和節流 拋出一個問題,就是我們的三級聯動,正常情況你慢慢的去滑動是沒有bug的,但是當你快速 ...


堆積了兩天一起發的,先祝大家節日快樂
後面任務很繁重,還有登錄註冊組件還有後臺管理頁面,真的繁重,我現在感覺每天全天時間都在學都不一定學得完,主要想在六月一號之前把整個項目過一遍。看看能不能創造奇跡

一.防抖和節流

拋出一個問題,就是我們的三級聯動,正常情況你慢慢的去滑動是沒有bug的,但是當你快速的從上往下滑一圈,你會發現只會觸發幾個標題,如果這個時候我們的回調也就是滑動的事件函數裡面有大量的業務邏輯,他會執行完一個再去執行另一個,但是你已經滑完很久了,所以這個時候頁面就會有卡頓現象

1.防抖

什麼是防抖?防抖就是一個事件觸發多次,最終只會有觸發一次的結果。

我們這裡防抖和節流採用一個 Lodash的插件來完成,但是自己也要懂防抖和節流的一個原理,首先防抖大概封裝是這樣的,定義一個函數,接受的參數為我們事件的回調和定時器的等待時間,裡面先清除一個定時器,然後定義一個定時器,定時器裡面調用我們的事件回調,當我們事件觸發就去執行這個防抖函數,意思就是比如我們有一個鍵盤輸入事件,當我們一輸入就會清除定時器,在執行定時器,定時器裡面的回調才會列印輸出我們輸入的內容,要等個500ms,那麼在這500ms之內你再次輸入,又不會列印輸出了因為定時器被清除了,又會重新計時500ms,這就是防抖的一個思想。

image-20220519104839618

註意這個clear只能寫在函數的外面

2.節流

防止觸發頻率過快,在一個時間段內只執行一次。

一定要把防抖和節流做一個區別:防抖是可以觸發n次,但是只會以你不觸發後的最後一次為準,而節流是可以觸發n次,但是限制了你的觸發頻率,所以n次內其實只觸發了不到n次

自己封裝就像這樣,需要一個節流閥flag,當我點擊一次去判斷節流閥,判斷成功進來先把節流閥關閉,在執行定時器,單位時間內執行完節流閥才會打開,所以這段時間,你怎麼點擊都執行不了回調,這裡不需要清除定時器,因為定時器會自動到時間就結束

image-20220519111724587

3.三級聯動節流

首先要註意一點,vue腳手架預設是給你安裝好了lodash

image-20220519141334043

怎麼來用具體可以參照他的中文文檔,它有兩種引入方式,以節流為例,在文檔裡面找到_.throttle,最主要的是引入如果是全部引入,因為他還有很多的方法,數組求和等等,直接以_命名他

image-20220519141942293

然後用的時候就像官網文檔一樣用

image-20220519142003430

但是我們這裡如果就想用一下節流就沒必要全部導入了,直接導入我們想要的節流,註意怎麼導入的,在lodash找到throttle

image-20220519142139353

然後用的時候也是直接用,還要註意一下這裡要回歸原始的es5的鍵值對的形式才可以使用這個節流

image-20220519142235795

二.三級聯動組件路由跳轉

當我們呢點擊三級聯動的一二三級標題的時候,可以實現跳轉到search頁面,並且會把自己的id和標題當成參數傳過去,所以這裡就涉及到,路由的跳轉,我們的這些標題又剛好是a標簽用一個router-link就可以跳轉過去,但是這樣做會有一點不完善,因為我們一級標題還好後面二級三級那麼多,每一個都轉換為router-link,這個組件標簽又是vue內部提供的,去點擊一次還要進入vue內部處理一次,就會造成頁面卡頓

那麼就只有採用編程式跳轉了,其實這裡編程式跳轉也不太好,為什麼?我們每一個a標簽都要來一個點擊事件,是不是跟上面這種情況差不多了

這裡最好的解決辦法是,用編程式路由導航➕事件委托來做,我們給他們共同的父元素來一個點擊事件,他們的父元素只有一個不就可以解決了嗎

image-20220519144438643

現在就要去解決一些問題了

  • 第一個問題:我們下麵這麼多元素,怎麼就能確保點擊的是a標簽?

    我們可以把每一級標題添加一個自定義屬性data-categoryName,當我們點擊的時候再通過e.target.dataset.categoryName來判斷點擊的元素是否是a標簽

    image-20220519151556136

  • 第二個問題:怎麼來區分點擊的是一級、二級還是三級標題,因為我們傳過去的參數有id1、id2、id3,這裡做一下改造,將我們前面獲取到的自定義屬性採用解構賦值,註意一下,我們雖然自定義屬性是駝峰命名,但是最終到頁面會給你轉化為小數 ,為一個變數

    image-20220519152926258

    我們給每個標題同樣通過自定義屬性給他們的id給到,同時以解構賦值接受給變數

    image-20220519153158831

    得到參數之後,因為我們是編程式導航嘛,所以最終肯定是要通過push方法傳出去值的,判斷為a標簽後定義一個對象裡面放的是要跳轉的地址也就是search,然後依次判斷是否是id1,是就來個query的對象裡面正常寫一般寫在push裡面query裡面的參數形式,最後都判斷完了,把query對象放到跳轉地址這個對象裡面,直接push即可

    image-20220519154134089

    image-20220519154147119

三.search模塊商品分類

將三級聯動全局組件直接拿過來,然後給三級聯動來一個條件渲染,預設為true

image-20220519160930007

這裡要考慮一點,當我們進入home那麼在search裡面的組件就會被銷毀,當我們進入search,在home裡面的組件就會被銷毀,所以當我們切換到search的時候,typenav的mounted又會被執行一次,那麼我們就可以把邏輯寫在這裡,就是當$route.path!=home這個時候就把條件改為false

image-20220519161356937

image-20220519161407935

然後滑鼠移入分類,又改為true

image-20220519161718491

移出可以接著上次在共同父級設置的移出這裡做,同樣還是要判斷一下是不是home頁面,因為home不能隱藏

image-20220519162006615

1.過渡動畫

回顧一下過渡動畫,要寫vue的過渡動畫必須有一個前提 那就是必須要有v-for或者v-show,由transition標簽包裹,然後可以寫自己的name,css部分name-enter,name-enter-to,離開是leave,最後時間以及要變化哪些都寫在active裡面

image-20220519164022245

image-20220519164221157

四.三級聯動列表優化

現在還有什麼優化的地方,有一個地方,我們剛纔說過,切換一次路由,裡面的所有組件都會被銷毀,包括三級聯動組件,所以頻繁切換,三級聯動也在頻繁銷毀頻繁執行mounted,而我們的mounted裡面寫了一個東西,dispatch派發到vuex裡面的actions,並且這裡還有邏輯處理,就是發送了ajax請求,所以問題就出現在這裡,我們切換一次路由組件,就會發送一次ajax請求,但針對於這裡來說,這裡三級聯動的請求,只需要一次就夠了,也沒有改,所以就需要換位置,他的統計都不只是只執行一次,繼續往父級走,那就是App組件,就寫在這裡,因為app組件只會執行一次,在這裡派發到actons,typenav需要的時候就state來取就是

image-20220519165722507

五.合併參數

就是將我們的query和我們的params參數合併起來。

這裡主要是考慮兩種場景,一個是點擊搜索會有params的參數,這個時候我點擊分類標題會將他的query參數合併上去。因為我們是後點的三級聯動列表,所以邏輯應該在後點這裡合併上去

image-20220519173441444

image-20220519173913604

然後還有一種場景,我已進入主頁就由三級聯動標題進入搜索頁,然後我再根據搜索頁去搜索更加詳細的查找內容,這個時候後點的是header裡面的搜索按鈕,所以邏輯在header組件去做

image-20220519174303785

六.mockjs模擬數據

因為我們現在首頁組件當中除了三級聯動後面的業務沒有介面,包括輪播圖等,這個時候就需要一個模擬數據,mockjs是一個插件,專門用來生成數據,攔截ajax請求

1.創建數據

使用方法:

  • 首先src創建一個mock文件夾

  • 第二步開始創建假數據,我們的數據都是json格式,所以直接在mock裡面創建json文件,放入數據,註意格式化一下,不能留有空格,不然跑不起來

    image-20220519203248533

    這是輪播圖的模擬數據,註意點mock數據需要的圖片要放到public文件夾下,因為只有public下麵的文件打包的時候才會原封不動輸送出去

  • 第三步創建mockServer.js通過mockjs來模擬出數據

    image-20220519203710987

    註意引入Mock名字必須是大寫首字母,它是一個對象,後面引入兩個json文件,為什麼沒有export,這裡可以直接導入?因為webpack是預設對圖片、json文件導出的,這些文件是預設導出的,不用export

  • 然後就可以調用Mock裡面的mock方法,兩個參數,第一個是請求路徑,第二個是請求數據

    image-20220519204007784

  • 然後我們的mockServer.js文件需要在入口文件引入,表示至少需要執行一次,我們的模擬數據才會出現,這裡不需要暴露,直接在入口文件引入這個js文件,表示直接執行

    image-20220519204159152

2.應用數據

這樣一來我們的模擬數據就創建好了,接下來就是怎麼來用他的問題了。

他雖然不會發起真正的ajax請求,會被瀏覽器攔截,但是可以把他當做真正的請求看,所以第一步我們先去介面統一管理裡面,給他來一個請求和響應的攔截器,並且用axios創建一個他的實例,而且baseurl要為/mock

image-20220519205518793

然後去介面統一管理處,創建一個獲取banner的介面函數

image-20220519205936553

這個時候回到我們需要輪播圖的組件,給他的mounted派發actions,把我們的數據用vuex管理起來,同時在actions獲取ajax請求

image-20220519210138301

去actions發起ajax請求

image-20220519210448598

可以在列印台看到,我們確實得到了數據,最重要的一點沒有網路請求,這就是ajax的攔截

image-20220519210551214

這個時候mutations將我們的數據給上,同時state定義一個數組裝這個數據

image-20220519211602139

在我們輪播圖組件拿到這個數據,通過計算屬性

image-20220519211756813

3.輪播圖(方案一)

我們用swiper實現輪播圖功能

  • 第一步下載導入,swiper需要導入兩個,一個是css樣式,由於我們下麵的組件也會用到這個樣式,所以直接導入在入口文件,還有一個是js哪裡要用就導入在哪

    image-20220520093725129

  • 實現swiper輪播圖第二步就是要定義好結構,結構這裡最重要的是用我們的mock模擬出來的數據,列表渲染出來

    image-20220520094226560

  • 這個時候可以去用她的js邏輯了,但是要想一下這個定義在哪裡,有一個很重要的前提,new Swiper首先必須頁面上有這些結構,如果我們把new Swiper寫在mounted,這個時候頁面上確實有結構了,但是我們的列表迴圈裡面的bannerList是通過ajax得來的,要知道ajax是典型的的非同步函數,所以當我們開始new的時候,圖片這些數據還沒有開始遍歷的,這個時候結構就不完整,輪播圖的實現就有問題

    這裡有一個比較笨的解決方法,就是開一個定時器,將new swiper放在裡面

    image-20220520103855570

4.輪播圖(方案二)

這也是最佳的完美解決方案,我們首先可以通過watch來做,去觀察bannerList的值,當他發生變化就已經代表了ajax請求回來了,所以這個時候再去new swiper

image-20220520112621313

當然只是這樣還是不夠,為什麼?這個時候我們的bannerList確實有值了,非同步操作也已經完成了,但是這個時候要把這些數據拿去v-for列表迴圈又需要一定的等待時間,這個時候你直接去new了,其實頁面上的結構還是沒有的,這裡真正完美的做法是watch+nextTick來做

nextTick:官網的解釋在下次 DOM 更新迴圈結束之後執行延遲回調。在修改數據之後立即使用這個方法,獲取更新後的 DOM。

其意思就是當DOM完全呈現之後包括迴圈結束之後才會去執行nextTick的回調函數

image-20220520113401392

七.floor組件模擬數據

又要給一個組件動態渲染數據了,由於前面已經通過mock模擬出來了數據,這個時候需要先將數據的介面寫進介面統一管理

image-20220520115442914

然後vuex三部曲走起,因為我們的介面請求都是在actions裡面來做的

image-20220520115814755

這個時候我們應該去去派發actions了,但是我們先觀察一下這個數據的結構,它是一個數組裡面有兩個對象,而我們的floor這個組件當時也復用了兩次,說明是不同的兩個對象,如果我在floor組件裡面去派發actions到時候來的數據就是一個數組有兩個對象,你用這個也不行用那個也不行,都體現不了復用性,這裡應該在他們父級也就是放他們組件標簽處來接收數據,並且通過v-for遍歷組件標簽

image-20220520120044567

image-20220520120814646

接下來遍歷我們的floor組件標簽,同時父傳子,通過props把屬於他們自己的那個對象傳過去

image-20220520120935575

image-20220520121019340

1.floor數據渲染

註意點:

  • swiper5及以前的版本最大的容器叫做swiper-container不是現在官網上的swiper

image-20220520141823868

  • 還有一個點我這裡直接在floor組件的mounted函數裡面new swiper是可以實現的,為什麼?

    image-20220520144317153

    要知道當時不能實現是因為在mounted裡面還發了ajax這個非同步操作請求,所以那個時候接著來一個new swiper是沒有完整結構的,但是在這裡我們的ajax是在父級home這個組件完成的,並且都已經把數據通過props發過來了,你在floor裡面也沒有影響結構的非同步了,所以直接在這裡的mounted new swiper就沒問題

    image-20220520144614907

八.共用組件carousel

當我們開發項目時,如果看到某一個組件在很多地方都使用,而且結構包括邏輯這些都一樣的話,就可以拆分為全局組件,大家直接復用,就比如這裡的上下兩個輪播圖。

當然大的輪播圖是用watch來做的,我們也可以把小輪播圖用watch來做,你會發現監視不了這個數據

image-20220520151728083

因為我們這個數據是父組件派發然後傳過來的,可以說過來的時候就已經是那個樣子了,從沒有變過,沒有變動還怎麼去監視,這裡可以添加immediate讓監視屬性一開始就監視一次。

image-20220520151912712

既然是一開始就執行,那時候肯定floor也為空,不然怎麼會監視到數據變化,所以還是要配合nextTick讓數據都有了到DOM上了再來new

image-20220520152341267

這樣我們就開始拆分組件了,全局組件放在components下麵,關鍵步驟在於我們需要接受一個從外部傳進來的輪播圖數據,並且全局組件監聽的就是他

image-20220520152904531

然後在我們模板上遍歷的也是他

image-20220520152931032

註冊全局組件

image-20220520153445819

將我們的組件標簽運用上,同時將數據傳過來

image-20220520154938162

九.Search模塊

1.靜態組件

完成一個組件開發,還是那個套路顯示靜態頁面完成,然後拆分組件,發請求,vuex三部曲,組件獲取數據並渲染出來

2.Search模塊vuex操作

我們已經完成了靜態組件以及拆分,下一步就是ajax的api請求,因為是api開頭的所以可以直接用封裝好的axios來獲取請求,但是還是要去介面統一管理封裝一下函數,因為這裡我們search搜索商品的參數很多,所以是用一個對象傳進來,而且正常的寫法data後面本身就是一個對象,所以這裡params傳進來的參數至少都是一個空對象

image-20220520163925204

然後就可以去vuex三部曲,通過介面文檔可以看到,我們返回的結果是一個對象,所以這裡直接數據定義為對象形式,但是這裡還測試不了,用到了我們之前還沒怎麼用過的dispatch的第二個參數就是我們要傳進來的值

image-20220520164611319

3.數據動態展示

我們可以看到獲取過來的數據有很多是數組形式的,這個時候就要用到 getters了,項目中的getters主要作用就是簡化倉庫中的數據,就是當我們state定義的數據裡面還嵌套多層數據,這個時候就可以用gettes來簡化,然後我們到時候用的話,就會簡便的多

image-20220520173340596

所以把上面這個數組通過gettes來獲取,註意的是gettes可以接受一個參數,就是當前倉庫中的state

image-20220520173600739

同樣也是寫在計算屬性裡面來獲取

image-20220520173735788

然後我們就可以開始先把下麵商品詳情頁的數據動態展示了,先獲取getters

image-20220520174957860

在我們getters裡面有一點要註意,雖然一般不會出現這種情況,就是怕別人沒有網了,這個時候我們沒有goodsList等這些數組,那麼用一個亦或語法讓其成為空數組以防網頁崩潰

image-20220520175139917

4.根據不同參數獲取數據展示

要根據不同參數獲取數據展示,那我們就需要將mounted裡面發起ajax請求的派發封裝為一個函數

image-20220520203953003

但是這裡發起一個空對象去搜索有點扯,我們需要把發給伺服器的參數整起來,看介面文檔可知,要發給伺服器的參數為一個對象,所以我們可以把這個參數定義在data裡面,然後再mounted之前傳進去參數,自然而然mounted掛載之後就會是我們從主頁通過點三級聯動及搜索頁面的詳情頁或者是從主頁通過搜索得來的詳情頁

image-20220520204845268

當然我們不能就以這個數據發過去,所以在mounted,也就是dispatch之前將真正的參數傳進來,註意這裡用到一個es6新增對象的方法Object.assign,他可以合併多個對象,如果有相同的就以後面的值為準,沒有的就添加上最後形成一個新對象

image-20220520210004186

5.search模塊子組件動態開發

既然我們都做了serach的搜索了,search裡面還有一個子組件,來展示品牌和一些標簽選擇的,這些數據也是順帶返回回來的所以順便就做了,因為這兩個數據是數組返回,所以直接getters,然後組件接受getters

image-20220520212629613

6.再次請求獲取數據

為什麼要再次請求,因為我們剛纔做的版本只是在進入search這個組件由於重新掛載了,所以只能請求mounted那一次,如果我要繼續搜索呢?

這個時候,有一個關鍵的技術點,當我們每一次搜索或者點擊三級聯動列表的時候,我們的url是不是在變

image-20220520214601112

而且以前我們都知道我們的響應數據一般是data裡面或者計算屬性裡面,通過vue管理者工具可以看到,我們的$route其實也是響應式數據

image-20220520214658847

所以關鍵點就在於這裡,我們可以監聽$route,然後把參數來一次assign方法合併,再次dispatch即可做到重覆搜索的功能

image-20220520221209548


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、概述 Hue是一個開源的Apache Hadoop UI系統,最早是由Cloudera Desktop演化而來,由Cloudera貢獻給開源社區,它是基於Python Web框架Django實現的。通過使用Hue我們可以在瀏覽器端的Web控制臺上與Hadoop集群進行交互來分析處理數據,例如操作 ...
  • 一、安裝mysql8.0 ##下載mysql安裝包 http://mirrors.sohu.com/mysql/MySQL-8.0/ wget http://mirrors.sohu.com/mysql/MySQL-8.0/mysql-community-client-8.0.18-1.el7.x8 ...
  • 分享嘉賓:王懷遠 阿裡雲 表格存儲架構師 編輯整理:李瑤 DataFun 出品平臺:DataFunTalk 導讀: 大家好,我是王懷遠,我2015年加入阿裡雲,一直從事表格存儲的研發和架構相關工作,目前擔任表格存儲的架構師。我在存儲和資料庫領域有一些研發和架構方面的經驗。 本次分享的主題是一站式物聯 ...
  • 一、概述 Impala 直接針對存儲在 HDFS、HBase或 Amazon Simple Storage Service (S3)中的 Apache Hadoop 數據提供快速的互動式 SQL 查詢。Impala是一個基於Hive、分散式、大規模並行處理(MPP:Massively Paralle ...
  • 今天我們來認識一位接觸 OpenHarmony 不到一年,便帶領團隊成功開發出一款“啟航 KP“智能開發套件的開發者——軟通動力資深項目經理許北林。 ...
  • 這次更新的設計規範不僅新增了更多應用場景案例,幫助大家高效設計不同類型的業務應用,還通過清晰直觀的案例對比圖,幫助大家有效避坑。 ...
  • 在音視頻應用中我們經常涉及到耳機麥克風和設備麥克風的切換。不同聲道的配置。在遇到這種情況的時候,我們如何配置呢? 耳返即耳機採集監聽,在設備上插入耳機(普通耳機或藍牙耳機)後,能從本機耳機側聽到本設備麥克風采集的聲音。 雙聲道即兩個聲音通道,聽到聲音時可以根據左耳和右耳對聲音相位差來判斷聲源的具體... ...
  • 對於運營者來說,消息推送一直是提升用戶活躍與轉化的重要工具,如何在提升轉化的情況下,同時不降低用戶的接受程度,這一直是運營不斷追求的目標。 好的推送不只在於優質的推送內容,還需要把握合適的時機。在合適時機把用戶喜歡的內容推送給他們,才能有效促進推送轉化。作為一名用戶,大家每天都會在各個時間點收到不同 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...