1. 快上車!從零開始搭建一個(gè)屬于自己的組件庫!

        共 10810字,需瀏覽 22分鐘

         ·

        2022-08-24 13:19


        組件在前端開發(fā)中越來越重要了,開發(fā)者更細(xì)分、聚焦于組件層面的開發(fā),然后像搭積木一樣完成應(yīng)用功能。組件庫可以統(tǒng)一管理組件,輸出文檔,能提升組件復(fù)用性、避免重復(fù)造輪子。趕快搭建自己的組件庫吧,這瓜保甜!

        需求背景

        為什么要搭建組件庫?

        雖然業(yè)界已經(jīng)有很多成熟優(yōu)秀的ui庫可以供我們使用,也為我們解決了很多問題。但是基礎(chǔ)的東西總是不能滿足所有業(yè)務(wù)場(chǎng)景,更多時(shí)候我們需要擴(kuò)展功能來滿足業(yè)務(wù)的需求,好比 table 需要自定列這樣的~相信這也是很多小伙伴開發(fā)時(shí)候的場(chǎng)景。

        1. 跨項(xiàng)目復(fù)用。很多時(shí)候?yàn)榱朔奖?,只是基于?dāng)前項(xiàng)目對(duì)組件進(jìn)行二次封裝(反正我是這樣干的哈哈),然后做其他項(xiàng)目遇到同樣場(chǎng)景時(shí),要么copy(經(jīng)常忘記之前封裝在哪個(gè)項(xiàng)目里了??)、要么重新干一個(gè)...總是缺少一個(gè)統(tǒng)籌的地方,復(fù)用很不方便。
        2. 組件使用文檔。文檔產(chǎn)出對(duì)于一線開發(fā)來說可能相對(duì)比較欠缺,因?yàn)榇蠹叶济τ跀]業(yè)務(wù),文檔這種奢侈品能省一點(diǎn)是一點(diǎn)。這樣導(dǎo)致一個(gè)問題就是自己封裝的組件別人不會(huì)用、不知道在哪里用,甚至不知道有這么個(gè)東西。
        3. 跨團(tuán)隊(duì)共建發(fā)展。大多B端系統(tǒng)都是以 elementantd 等ui框架為主,基于各種業(yè)務(wù)場(chǎng)景,基本都會(huì)有自己團(tuán)隊(duì)的二次封裝。其實(shí)類似的功能擴(kuò)展肯定會(huì)有的,如果有組件庫把組件都集中起來,就能減少很多重復(fù)造輪子的勞動(dòng)力了!

        筆者之前就經(jīng)常有這樣的痛點(diǎn),在某個(gè)項(xiàng)目里二次封裝了 el-select ,實(shí)現(xiàn) filterable 的時(shí)搜索輸入框移到下拉列表中,避免多選時(shí)多個(gè)tag擠壓了搜索框的空間。當(dāng)時(shí)是寫在一個(gè)項(xiàng)目里,然后其他項(xiàng)目也遇到了這樣的需求...我在十幾個(gè)項(xiàng)目里面尋找、回憶,找回當(dāng)年封裝的組件,人都麻了......

        正好最近在搞云產(chǎn)品,需要提供給各中后臺(tái)統(tǒng)一的樣式、布局規(guī)范以接入,還需要統(tǒng)一擴(kuò)展基礎(chǔ)組件的能力。于是組件庫的需求的就這么出來了!基本想做成的就是對(duì) element-ui/plus 、 antd 一些組件進(jìn)行二次封裝、擴(kuò)展,并集成到組件庫中,筆者當(dāng)仁不讓把需求從大佬手上搶過來做。

        目前初步搭建起來了一個(gè)簡易的組件庫了,可對(duì) element-plus 、 element-ui 的組件進(jìn)行開發(fā)調(diào)試,且目前已經(jīng)實(shí)現(xiàn)幾個(gè)組件的擴(kuò)展了~當(dāng)然,第一版還是有很多工作沒做完、做好,不過沒關(guān)系,畢竟不能一下吃成一個(gè)胖子。更多實(shí)現(xiàn)、優(yōu)化、還會(huì)慢慢迭代做,到時(shí)候有空會(huì)繼續(xù)分享相關(guān)的干貨~

        本文會(huì)從組件庫的工程架構(gòu)、文檔、組件開發(fā)環(huán)境準(zhǔn)備打包、發(fā)布進(jìn)行分享,組件開發(fā)環(huán)境主要是 element-ui/plus 的??,因?yàn)楸酒谛枨笾灰獫M足 vue2vue3 的中臺(tái),所以 antd 的還沒有投入,只好等下期了。與其說分享,其實(shí)更是做一個(gè)記錄沉淀一下,也是回顧總結(jié)~事不宜遲!開始進(jìn)入主題吧,從0-1搭建一個(gè)組件庫!

        一、項(xiàng)目架構(gòu)

        第一次搞組件庫,仿佛走進(jìn)一個(gè)新的空白領(lǐng)域了。作為一個(gè)沒經(jīng)驗(yàn)的小白,當(dāng)然是得抄作業(yè)啦,不不不,應(yīng)該是“借鑒”??。這時(shí)候搞個(gè)開源的項(xiàng)目來參照參照還是挺香的,于是筆者就去“學(xué)習(xí)了” element-plus項(xiàng)目[1]的架構(gòu)、代碼組織方式,再結(jié)合自己的需求場(chǎng)景就開始干了。

        1. Monorepo

        整個(gè)工程的代碼組織采用 Monorepo 的組織方式,使用工具 pnpm + workspace 來實(shí)現(xiàn)。所以全部項(xiàng)目都是放在一個(gè)倉庫里的,包括文檔、組件。

        工程具體分為以下幾塊,以文檔組件庫為兩大類進(jìn)行分塊:

        1. 文檔工程(docs)
          • 安裝指引
          • 組件使用文檔(elmelp、antd
          • 組件開發(fā)文檔
        2. 組件庫(packages)
          • element-plus
          • element-ui
          • ant-design
          • voice-components

        其中 voice-components 筆者是打算用來做 adapt層 用的,因?yàn)槲臋n工具用了 VitePress(后面會(huì)講),它只能支持 vue3 的組件,所以 vue2 、 react 的組件需要做一層適配,這一塊是預(yù)留的,暫時(shí)可以不關(guān)注。

        image.png

        第一版比較簡單,后續(xù)如果沉淀出一些工具、打包腳本等,也會(huì)再擴(kuò)展幾個(gè)項(xiàng)目放進(jìn)去 workspace 里。所以目前就先這樣吧,用著先~

        2. 文檔項(xiàng)目結(jié)構(gòu)

        抄作業(yè)抄作業(yè),這部分跟 element-plus 基本是一致的。

        • index.md。!!顧名思義,文檔首頁~
        • .vitepress目錄:文檔站點(diǎn)工具配置相關(guān),這個(gè)后面再展開~
        • zh-CN目錄:文檔md文件
          • components:組件使用文檔.md。組件的使用demo案例代碼,相關(guān)配置說明
          • guide:組件庫指引文檔.md。包括組件的安裝指南、開發(fā)指南
        • public目錄:相關(guān)靜態(tài)資源目錄。cssimage
        • build目錄:放點(diǎn)自己實(shí)現(xiàn)的構(gòu)建腳本、vite插件啥的
        image.png

        3. 組件庫結(jié)構(gòu)

        這部分跟 element-plus 也是基本一致的,具體大家可以參照他們的實(shí)現(xiàn),這里就記錄個(gè)大概,粗略帶過吧。

        每一個(gè)ui框架的結(jié)構(gòu)都一樣,以其中一個(gè)為例記錄:

        1. 組件項(xiàng)目入口——根index。導(dǎo)出當(dāng)前項(xiàng)目需要導(dǎo)出的所有模塊(可按需引入)。并導(dǎo)出全局安裝方法。(Vue.use(VcComponents))可全局注冊(cè)
          export * from './components'

          export default {

        install } 復(fù)制代碼2. components:- 入口文件:index。導(dǎo)出所有組件。js export * from '...' export * from '...' export * from '...' 復(fù)制代碼 ``` - 存放全部組件,以組件名作為文件夾名。3.  組件文件夾(以button為例):- 入口文件:index。導(dǎo)出當(dāng)前組件,并包裝 install 方法(主要用于Vue.use調(diào)用時(shí)進(jìn)行全局注冊(cè))。- 組件文件。實(shí)現(xiàn)組件擴(kuò)展的二次封裝。(這里建議擴(kuò)展組件時(shí)保留組件的原來用法,這樣可以降低使用時(shí)候的學(xué)習(xí)成本)

        image.png

        二、組件庫工具

        這里不會(huì)面面俱到,只記錄一些用到的核心工具以及核心的用法~就算不是特別細(xì)粒度,相信大家要自己動(dòng)手搞的時(shí)候也難不倒你們的?。」P者這么菜都一樣搞,你們肯定都行!

        1. 文檔站點(diǎn)工具——VitePress

        對(duì)于組件庫來說,文檔可以說是最關(guān)鍵的一環(huán)了,沒有文檔的組件庫不是真的組件庫~這里筆者用了幾分鐘去調(diào)研(根本就沒怎么調(diào)研),最終決定使用 VitePress[2] 作為文檔站點(diǎn)工具,目前用的版本是 1.0.0-alpha.4。(哈哈哈大家不要害怕alpha版,用著沒啥毛?。?/p>

        使用下來基本配置用法官方文檔[3]中都能找到,已經(jīng)滿足當(dāng)前的使用場(chǎng)景了~大家也要采用的話,花點(diǎn)時(shí)間去搓一搓就好,整個(gè)文檔站點(diǎn)搭建不算難,畢竟只要能跑起來就可以慢慢調(diào)整慢慢搞。

        核心配置(都放在 .vitepress 目錄下):

        1. 配置文件:.vitepress 根目錄的 config 文件。其實(shí)沒有特別多的配置,主要就是導(dǎo)航欄菜單欄而已。

          export default defineConfig({

        title: 'voice-ui', description: '', base, head: [ [ 'link', { rel: 'icon', href: '/images/favicon.ico' } ] ], themeConfig: { logo: '/images/favicon.ico', nav, // 配置導(dǎo)航欄 sidebar, // 配置側(cè)邊菜單欄 footer // 配置頁腳 } }) 復(fù)制代碼 ```

        1. nav 配置導(dǎo)航欄配置(文檔鏈接[4]

          export default [

        { text: '指南', link: '', activeMatch: '' }, { text: 'element-plus', link: '', activeMatch: '' }, { text: 'element-ui', link: '', activeMatch: '' }, { text: 'ant-design', link: '', activeMatch: '' } ] 復(fù)制代碼 ```

        1. sidebar 配置側(cè)邊菜單欄(文檔鏈接[5])。具體配置太多就不全貼出來了,這里的配置在文檔中都能找到。如下這樣配置就是一個(gè) nav 路由對(duì)應(yīng)一個(gè) sidebar 菜單。

          export default {

        '/zh-CN/guide/': [ { text: '安裝', items: [ { text: 'element-plus', link: '' }, ... ] }, { text: '開發(fā)者指南', items: ... } ] } 復(fù)制代碼 ```

        大概的效果如下,不同nav對(duì)應(yīng)各自的側(cè)邊欄菜單:![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/965ca66b0c364079b9efb9e5e9113f8d~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?)

        ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7723fd9b09ad4ac4b1e0507b0d3076ac~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?)
        1. /theme/index 中自定義主題 & 全局注冊(cè) vue3 組件
        • 具體配置參照文檔[6]。這里的僅是筆者的基本配置~
          import { App } from 'vue'
          import Theme from 'vitepress/theme'
          import '../../public/css/customStyle.css' // 自定義的主題色文件
          import 'element-plus/dist/index.css'
          import 'element-plus/theme-chalk/dark/css-vars.css'
          import VcComponent from '@voice-ui/voice-components' // 上文提到的adapt層,導(dǎo)出vue3的組件

          export default {
          ...Theme,
          enhanceApp ({ app }: {app: App}) {
            app.use(VcComponent) // 進(jìn)行組件注冊(cè),這樣我們可以直接在 markdown 中使用組件啦!
          }
          }
          復(fù)制代碼
        • customStyle.css 文件其實(shí)就是對(duì) VitePress 的一些 css變量 進(jìn)行自定義重寫??
          :root {
            --vc-primary-color#295dfa;
            ...
          }

          :root {
            --vp-c-brandvar(--vc-primary-color); /* 自定義 VitePress 的主題色 */
            ...
          }
          復(fù)制代碼

        2. 打包工具——Vite

        提到這個(gè)必須提一嘴:開發(fā)真絲滑!是的,包括各項(xiàng)目的 dev 、 build 都是使用 Vite 完成。其實(shí)這個(gè)沒什么好說的,大家可能用得比我都熟~所以這里只簡單帶一帶用了什么功能~

        1. 用到的 vue2、 vue3 插件(官方文檔戳[7]):
          • underfin/vite-plugin-vue2
          • @vitejs/plugin-vue
        2. 打包配置——庫模式?;镜亩加辛恕?code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">es、cjsumd、iife。(官方文檔戳[8],具體下文會(huì)講)
        3. 配置alias。各模塊在dev、prod環(huán)境中相互引用(官方文檔戳[9],具體下文會(huì)講)
        4. 配置external。(vite配置[10]rollup配置[11]

        第一版差不多就這些了,配置上還是比較簡單的?;究梢詽M足 dev開發(fā)、build打包 需求。

        三、開發(fā)環(huán)境

        因?yàn)槭褂玫?VitePress 支持在 markdown直接使用 vue3 組件,所以 vue2 、 vue3 、 react 相關(guān)的開發(fā)環(huán)境有所不同?;诖?, element-plus 的開發(fā)環(huán)境就沒有單獨(dú)搞了,直接在 docs 項(xiàng)目中進(jìn)行組件開發(fā)。

        1. vue3 + element-plus 開發(fā)環(huán)境

        這里也是直接抄作業(yè)的,模仿 element-plus 的實(shí)現(xiàn)。核心做法:

        • 包裝 element-plus組件 一層 install 方法
        • VitePress 中進(jìn)行全局注冊(cè)
        • md文件 中直接使用注冊(cè)好的組件,可以直接在文檔中進(jìn)行開發(fā)調(diào)試

        大概的代碼思路:

        1. 給組件對(duì)象添加 install 方法

          import { withInstall } from '../../utils'

          import Button from './button.vue'

          export const VcButton = withInstall(Button)

          export default VcButton

          export * from './'
          復(fù)制代碼
        2. install方法:接收一個(gè) Vue3 對(duì)象,用 Vue.component 進(jìn)行組件注冊(cè)

          export const withInstall = comp => {

        comp.install = app => { app.component(comp.name, comp) } return comp } 復(fù)制代碼 ```

        1. 文檔項(xiàng)目中,在 .vitepress/theme/index.ts 中進(jìn)行全局注冊(cè)(上文也有提到)

          export default {

        enhanceApp ({ app }: {app: App}) { // 這里能拿到 app ,也就是Vue3的app // VcButton在這里進(jìn)行全局注冊(cè) app.use(VcButton) // app.use 就會(huì)調(diào)用 VcButton的install方法 } } 復(fù)制代碼 ```

        1. md中直接使用注冊(cè)好的組件

          # Button 按鈕

          這是一個(gè)按鈕

          # Element-plus

          ##  Button

          <vc-button />
          復(fù)制代碼

        然后就能在頁面上看到了,并且是有熱更新的!這樣我們直接開發(fā)調(diào)試即可了。

        image.png

        2. vue2 + element-ui 開發(fā)環(huán)境

        vue2 、 react 的開發(fā)環(huán)境實(shí)現(xiàn)思路大致相同(react的這次還沒搞,以vue2為例就好),就是在當(dāng)前項(xiàng)目中用vite啟動(dòng)一個(gè)devServer進(jìn)行開發(fā),就跟普通的項(xiàng)目開發(fā)是一樣的。

        1. 根目錄建一個(gè) index.html ,指定入口

        2. 搞個(gè)demo目錄,其實(shí)就是Vue項(xiàng)目,newVue完后掛載到dom上。如下:demo-xxx 的vue文件中導(dǎo)入開發(fā)的組件進(jìn)行試用、調(diào)試。

          頁面效果如下:

        react的雖然還沒做,但是具體思路也是跟vue2一樣的,在react自己的項(xiàng)目中起服務(wù)進(jìn)行組件的開發(fā)調(diào)試~后續(xù)做了的話會(huì)補(bǔ)充進(jìn)這里~

        四、組件打包、發(fā)布

        目前的打包、發(fā)布實(shí)現(xiàn)得比較簡單。大概是統(tǒng)一打包,然后進(jìn)到每個(gè)目錄中去進(jìn)去npm發(fā)布,目前也是只發(fā)布在內(nèi)部的npm中。

        1. 統(tǒng)一打包

        為了打包方便,且契合當(dāng)前發(fā)布平臺(tái)的特性,在整個(gè)項(xiàng)目的根目錄中 package.json 的 scripts 中進(jìn)行了命令整合。這里后續(xù)可能會(huì)用腳本的方式去實(shí)現(xiàn),因?yàn)榭赡茉诖虬臅r(shí)候要處理一些其他的邏輯。目前第一版大概如下:

        {
            "scripts": {
              "build""pnpm run build:elp && pnpm run build:elu && pnpm run build:shared",
              "build:elp""pnpm run -C packages/element-plus build",
              "build:elu""pnpm run -C packages/element-ui build",
              "build:shared""pnpm run -C packages/shared build",
              "release""node scripts/release.ts"
            }
        }
        復(fù)制代碼

        2. 組件庫的打包配置

        上文打包工具哪里有提到過,目前是最簡單版的,打出 es、cjs、umd、iife 格式的包,而且要external掉第三方庫。大概配置:

        export default defineConfig(async ({ command, mode }) => {
          return {
            plugins: [ createVuePlugin() ],
            build: {
              rollupOptions: {
                external: ['element-ui''vue']
              },
              lib: {
                entry: path.resolve(__dirname, './components/index.js'),
                name'voiceUi',
                fileName'vc-element-ui',
                formats: ['es''cjs''umd''iife']
              }
            },
            resolve: {
              aliasawait alias()
            }
          }
        })
        復(fù)制代碼

        3. npm發(fā)布

        其實(shí) npm包 安裝只是其中一種方式,該組件庫后續(xù)還會(huì)新增模塊聯(lián)邦——MF的接入方式,這個(gè)會(huì)在后續(xù)進(jìn)行擴(kuò)展,到時(shí)候做了的話再補(bǔ)充一下或者再寫一篇文章吧~

        目前的npm發(fā)包時(shí)候用了個(gè)腳本,基本就是進(jìn)到每個(gè)目錄下去執(zhí)行以下更新版本好,然后執(zhí)行 npm publish。(這里的腳本會(huì)結(jié)合自己發(fā)布平臺(tái)的一些能力去寫的,所以就不貼出來了,大致思路就是這樣)

        五、開發(fā)時(shí)一些注意點(diǎn)

        1. Vue版本沖突導(dǎo)致啟動(dòng)服務(wù)、打包失敗

        因?yàn)槭鞘褂?Monorepo 代碼組織方式,所以整個(gè)項(xiàng)目難免會(huì)出現(xiàn)依賴包重合(版本不同)的問題。就好比這整個(gè)項(xiàng)目中既裝了 Vue3 、也裝了 Vue2 ,可能起項(xiàng)目時(shí)會(huì)報(bào)錯(cuò):

        Vue packages version mismatch:

        [email protected] 
        [email protected]
        復(fù)制代碼

        但是仔細(xì)檢查發(fā)現(xiàn)當(dāng)前項(xiàng)目的 node_modules 中的 vuevue-compiler 是同版本的,而且是在當(dāng)前工程中執(zhí)行的啟動(dòng)、打包。查了個(gè) issus ,在pnpm文檔中找到了相關(guān)的解決方案:

        image.png

        具體文檔地址:pnpm—shared-workspace-lockfile[12]

        配置了這個(gè)文件后問題就解決了~

        2. 配置alias解決入口問題

        組件庫打包后的入口開發(fā)時(shí)的入口其實(shí)是有點(diǎn)不一致的,所以如果我們開發(fā)中直接 import xxx from 組件庫名稱 這樣導(dǎo)入組件是會(huì)有問題的,畢竟一般情況下我們的入口是配置打包完之后的產(chǎn)物的入口的(一般是dist、lib目錄下的index)。

        image.png

        出于這點(diǎn),配置個(gè)alias就很好解決問題了,因?yàn)楹芏嗟胤接玫?,筆者直接就封成了個(gè)函數(shù):

        export async function alias (): Promise<Array<Alias>> {
          const projectPath = packagesPath()
          const dirArr = await fsPromises.readdir(projectPath)

          return dirArr.map(packagePath => {
            return {
              find:  new RegExp(`^@voice-ui\/${packagePath}(\/(dist))?$`),
              replacement: path.join(projectPath, `/${packagePath}/index`)
            }
          })
        }
        復(fù)制代碼

        大概作用就是把入口從 dist 下面換到當(dāng)前工程的對(duì)應(yīng)項(xiàng)目下的index入口。

        3. 樣式隔離

        docs項(xiàng)目中用的 VitePress ,他會(huì)有一些自己的樣式控制,可能會(huì)影響到我們需要在文檔中展示的組件。剛好筆者就遇到了這么一個(gè)情況:

        image.png

        如圖所示,table的樣式變得很奇怪。筆者并沒有對(duì)樣式有做什么處理,就是element-plus的table。用審查元素看了下,主要是 VitePress 也有自己的table的樣式,影響到 el-table 的表現(xiàn)了。

        我們可以通過重寫樣式解決,但這樣在其他的如 antdelement-ui 的組件放進(jìn)來的時(shí)候也會(huì)有問題。所以最好還是把樣式進(jìn)行隔離。哈哈哈,其實(shí)寫到這里的時(shí)候,筆者還沒有實(shí)現(xiàn)樣式隔離,畢竟給大伙寫文章更重要嘛?。?/p>

        筆者有個(gè)大概想法是用 webComponent 去隔離,這只好做完了再寫出來了??,讓大家留個(gè)念想。


        寫在最后

        其實(shí)組件庫這個(gè)東西真是早有早好,如果當(dāng)前團(tuán)隊(duì)還沒有的話,趕緊搞一個(gè)吧。筆者以前也沒意識(shí)去搞這個(gè)東西(還是太菜了),所以做了很多重復(fù)的勞動(dòng)。每做一些新項(xiàng)目,或者參與別人的項(xiàng)目,經(jīng)常想用一些自己之前封裝的組件,都很麻煩,有時(shí)候?yàn)榱瞬幌肭衼砬腥?,直接就?dòng)手寫了?,F(xiàn)在想想,要是那時(shí)候就做了個(gè)組件庫該多好啊~其實(shí)做個(gè)簡單版的也不是很難,如果是只需要關(guān)注一種前端框架的那就更簡單了,要考慮的東西更少。把整個(gè)組件庫搭出來之后,還能找其他小伙伴一起共建,一起維護(hù),不斷強(qiáng)大自己團(tuán)隊(duì)的組件庫,大家一起受益~

        真的!還沒有的趕緊搭!這瓜保甜。

        關(guān)于本文

        作者:井柏然

        https://juejin.cn/post/7120893568553582622


        最后


        歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
        回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會(huì)很認(rèn)真的解答喲!
        回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
        回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
        如果這篇文章對(duì)你有幫助,在看」是最大的支持
         》》面試官也在看的算法資料《《
        “在看和轉(zhuǎn)發(fā)”就是最大的支持


        瀏覽 53
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 亚洲AV无码成人精品区国产 | 小哥爆操小受 | 啊轻点灬大太粗太长了视频 | 欧美性猛交XXXXXX | 黄色草逼 |