整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          WordPress Body Class 101:主

          WordPress Body Class 101:主題設計師的提示和技巧

          過使用WordPress的經驗,我們發現主題設計師經常過度使用某種功能。當他們需要的只是一些簡單的CSS時,他們正在尋找瘋狂的WordPress過濾器和鉤子來完成任務。WordPress默認生成很多CSS類。(WordPress CSS備忘單)。其中一個CSS類區域是body類樣式。在本文中,我們將解釋WordPress體類101以及為初學主題設計者分享一些有用的提示和技巧。

          什么是WordPress Body Class?

          Body Class(body_class)是一個WordPress函數,它為body元素提供不同的類,因此主題作者可以使用CSS有效地設置其網站的樣式。HTML body標簽主要存在于header.php文件中,該文件在每個頁面上加載。編碼主題時,可以將body_class函數附加到body元素,如下所示:

          <body <?php body_class($class); ?>>

          通過添加這個小元素,您可以靈活地使用CSS輕松修改每個頁面的外觀。根據所加載頁面的類型,WordPress會自動添加相應的類。例如,如果您在存檔頁面上,如果您選擇使用它,WordPress將自動將存檔類添加到body元素。幾乎每一頁都能做到這一點。以下是WordPress可能添加的常見類的一些示例:

          .rtl {}

          .home {}

          .blog {}

          .archive {}

          .date {}

          .search {}

          .paged {}

          .attachment {}

          .error404 {}

          .single postid-(id) {}

          .attachmentid-(id) {}

          .attachment-(mime-type) {}

          .author {}

          .author-(user_nicename) {}

          .category {}

          .category-(slug) {}

          .tag {}

          .tag-(slug) {}

          .page-parent {}

          .page-child parent-pageid-(id) {}

          .page-template page-template-(template file name) {}

          .search-results {}

          .search-no-results {}

          .logged-in {}

          .paged-(page number) {}

          .single-paged-(page number) {}

          .page-paged-(page number) {}

          .category-paged-(page number) {}

          .tag-paged-(page number) {}

          .date-paged-(page number) {}

          .author-paged-(page number) {}

          .search-paged-(page number) {}

          如何使用WordPress Body Class

          在大多數情況下,WordPress body類是主題設計者經常忽略的簡單解決方案。大約一年前,我需要設置僅在特定頁面上設置WordPress菜單項的樣式。我決定深入挖掘以找到一個過濾器,為菜單項添加一個特殊的導航類,這些菜單項將使用條件語句添加。例如,如果它是單個頁面,則將“Blog”類添加到菜單項。我花了很多時間搞清楚所有這些并寫一篇關于它的帖子,有用戶指出可以很容易地用現有的體類來完成它,如下所示

          .single #navigation .leftmenublog div{display: inline-block !important;}

          注意:它如何使用在所有單個帖子頁面中添加的.single body類。

          給你另一個例子。一旦用戶想要一種基于類別頁面更改其網站上的徽標圖像的方法。包括他在內的大多數主題設計都是使用CSS(#header .logo)加載徽標。我建議如何使用.category-slug body類來改變他的徽標:

          .category-advice #header .logo{background: url(images/logo-advice.png) no-repeat !important;}

          .category-health #header .logo{background: url(images/logo-health.png) no-repeat !important;}

          希望現在您已經掌握了如何在主題設計中利用類。但是等等,這會變得更加強大,因為有一種方法可以使用過濾器添加自定義主體類。

          如何添加自定義正文類

          WordPress有一個過濾器,您可以使用它在需要時添加自定義正文類。向您展示如何使用過濾器添加一個body類,然后再向您展示特定的用例場景,以便每個人都可以在同一頁面上。

          因為body類是特定于主題的,所以你需要在functions.php文件中編寫一個自定義函數,如下所示:

          function

          my_class_names(

          $classes

          ) {

          // add 'class-name' to the $classes array

          $classes

          []=

          'test-class-name'

          ;

          // return the $classes array

          return

          $classes

          ;

          }

          //Now add test class to the filter

          add_filter(

          'body_class'

          ,

          'my_class_names'

          );

          上面的代碼將在您網站的每個頁面上的body標簽中添加一個“test-class-name”類。該函數的真正強大之處在于條件標記。您可以說只在單頁等上添加XYZ類。

          將類別類添加到單個帖子頁面

          假設您要自定義每個類別的單個帖子頁面。根據自定義的類型,您可以為其使用正文類。為了簡單起見,我們假設您要根據單個帖子的類別更改頁面的背景顏色。您可以通過向單個帖子頁面視圖添加類別類來實現。將以下函數粘貼到主題的functions.php文件中:

          // add category nicenames in body class

          function category_id_class($classes) {

          global $post;

          foreach((get_the_category($post->ID)) as $category)

          $classes[]=$category->category_nicename;

          return $classes;

          }

          add_filter('body_class', 'category_id_class');

          上面的代碼將在您的正文類中為單個帖子頁面添加類別類。然后,您可以根據需要使用css類來設置樣式。

          在WordPress主題的Body Class中添加Page Slug

          將以下代碼粘貼到主題的functions.php文件中:

          //Page Slug Body Class

          function add_slug_body_class( $classes ) {

          global $post;

          if ( isset( $post ) ) {

          $classes[]=$post->post_type . '-' . $post->post_name;

          }

          return $classes;

          }

          add_filter( 'body_class', 'add_slug_body_class' );

          瀏覽器檢測和瀏覽器特定的正文類

          有時您可能需要特殊的CSS樣式才能在某些瀏覽器中正常工作(HINT:Internet Explorer)。好吧彌敦道賴斯提出了一個非常酷的解決方案,基于用戶正在訪問從網站瀏覽器上增加了瀏覽器的具體類體類。

          您所要做的就是將以下代碼粘貼到functions.php文件中:

          <?php

          add_filter('body_class','browser_body_class');

          function browser_body_class($classes) {

          global $is_lynx, $is_gecko, $is_IE, $is_opera, $is_NS4, $is_safari, $is_chrome, $is_iphone;

          if($is_lynx) $classes[]='lynx';

          elseif($is_gecko) $classes[]='gecko';

          elseif($is_opera) $classes[]='opera';

          elseif($is_NS4) $classes[]='ns4';

          elseif($is_safari) $classes[]='safari';

          elseif($is_chrome) $classes[]='chrome';

          elseif($is_IE) $classes[]='ie';

          else $classes[]='unknown';

          if($is_iphone) $classes[]='iphone';

          return $classes;

          }

          ?>

          然后,您可以使用以下類:

          .ie .navigation {some item goes here}

          更多用例:

          還有更多用例,這篇文章它的重點是幫助新主題開發人員理解WordPress體類的基礎知識及其優點。用例僅限于您的想象力。已經看到商業主題開發人員(Genesis)使用body類來提供各種布局選項。例如,全頁面布局,補充工具欄內容,內容邊欄等。這樣,用戶可以輕松切換頁面布局而不必擔心外觀問題,因為已經使用CSS.0進行了處理

          還可以看到一些開發人員添加HTTP請求來加載他們提供的各種配色方案的CSS文件。在很多情況下,變化也很小。當一個人可以完成工作時,沒有必要加載3個樣式表。只需使用body類。

          傳統的網頁開發中,JavaScript通常被放置在HTML文檔的<script>標簽內,而在大多數情況下,建議將這些<script>標簽放置在</body>標簽之前,即在<body>標簽的尾部引入JavaScript代碼。這種做法有以下幾個原因:

          加載順序

          瀏覽器在解析HTML文檔時是按順序執行的,當遇到<script>標簽時,瀏覽器會停止解析HTML,去加載并執行JavaScript代碼。如果將<script>標簽放在<head>標簽中,那么在JavaScript加載和執行期間,HTML解析會暫停,頁面呈現會延遲,給用戶帶來不好的體驗。而將<script>標簽放在</body>標簽之前,可以確保頁面的HTML結構已經完全加載和解析,然后再去加載和執行JavaScript代碼,不會影響頁面的呈現速度和用戶體驗。

          優化加載時間

          將JavaScript代碼放在</body>標簽之前,可以確保頁面的其他內容(如HTML結構、CSS樣式、圖片等)都已加載完畢,這樣可以最大程度地減少JavaScript加載和執行的時間。因為JavaScript通常會操作和修改頁面的DOM結構,如果在DOM還未完全加載的情況下執行JavaScript代碼,可能會導致JavaScript無法找到或操作相關的DOM元素,造成錯誤或異常。通過將JavaScript代碼放在</body>標簽之前,可以確保DOM已經完全加載,并且用戶可以盡早地看到頁面內容,提高了網頁的整體加載速度。

          兼容性考慮

          在舊版的Internet Explorer(IE)瀏覽器中,將<script>標簽放在<head>標簽中時,可能會導致JavaScript無法正常工作。這是因為舊版IE瀏覽器在解析HTML文檔時,會在遇到<script>標簽時立即執行其中的代碼,并且在繼續解析HTML文檔之前必須等待JavaScript代碼的加載和執行完成。如果JavaScript代碼比較大或執行時間較長,用戶在這段時間內將看不到頁面內容,給用戶造成不好的體驗。通過將JavaScript代碼放在</body>標簽之前,可以規避這個問題,確保頁面內容能夠盡快呈現給用戶。

          綜上所述,將JavaScript代碼放在</body>標簽之前是一種較好的習慣,可以優化頁面加載時間,提高用戶體驗,并確保腳本在正確的上下文中執行,避免兼容性問題。然而,隨著Web開發技術的不斷發展,我們可以根據具體的需求和使用的工具選擇合適的方式來引入JavaScript代碼,以達到更好的性能和開發體驗。

          vent Loop(事件循環)是前端工程師經常討論到的話題。

          作者:lzaneli,騰訊 CDC 前端開發工程師,博客:Lzane.com

          Microtasks(微任務)是事件循環中一類優先級比較高的任務,本文通過一個有趣的例子探索其運行時機。從兩年前被動接受知識 "當瀏覽器JS引擎調用棧彈空的時候,才會執行 Microtasks 隊列",到兩年后主動深入探索源碼后了解到的 "當 V8 執行完調用要返回 Blink 時,由于 MicrotasksScope 作用域失效,在其析構函數中檢查 JS 調用棧是否為空,如果為空就會運行 Microtasks。"。同時文章中介紹了用于探索瀏覽器運行原理的一些工具。

          一個有趣的例子

          剛學前端那會學習事件循環,說事件循環存在的意義是由于 JavaScript 是單線程的,所以需要事件循環來防止JS阻塞,讓網絡請求等I/O操作不阻塞主線程。

          而 Microtasks 是一類優先級比較高的任務,我們不能像 Macrotasks(宏任務) 一樣插入 Macrotasks 隊列末端,等待多個事件循環后才執行,而需要插入到 Microtasks 的隊列里面,在本輪事件循環中執行。

          比如下面這個有趣的例子:

          document.body.innerHTML=` 
              <button id="btn" type="button">btn</button> 
          `; 
          
          const button=document.getElementById('btn')
           
          button.addEventListener('click',()=>{
            Promise.resolve().then(()=>console.log('promise resolved 1'))
            console.log('listener 1')
          })
           
          button.addEventListener('click',()=>{
            Promise.resolve().then(()=>console.log('promise resolved 2'))
            console.log('listener 2')
          })
           
          // 1. 手動點擊按鈕 
          // button.click() // 2. 解開這句注釋,用JS觸發點擊行為
          

          當我手動點擊按鈕的時候,大家覺得瀏覽器的輸出是下面的A還是B?

          • A. listener1 -> promise resolved 1 -> listener2 -> promise resolved 2
          • B. listener1 -> listener2 -> promise resolved 1 -> promise resolved 2

          大家可以在這里試一下:

          https://codesandbox.io/static/img/play-codesandbox.svg

          當我將上面代碼中的最后一行注釋打開,使用JS觸發點擊行為的時候,瀏覽器的輸出是A還是B?

          大家覺得上面1、2兩種情況的輸出順序是否一樣?

          答案非常有意思

          • 當我們使用1. 手動點擊按鈕時,瀏覽器的輸出是A
          • 當我們使用2. 用JS觸發點擊行為時,瀏覽器的輸出是B

          被動接受知識

          為什么會出現這種情況呢? 這個 Microtasks 的運行時機有關。兩年前當我帶著這個問題搜索資料并詢問大佬的時,大佬告訴我:

          當瀏覽器JS引擎調用棧彈空的時候,才會執行Microtasks隊列

          按照這個結論,我使用 Chrome Devtool 中的 Performance 做了一次探索

          人工點擊按鈕

          人工點擊的時候輸出為 listener1 -> promise resolved 1 -> listener2 -> promise resolved 2 。

          • 從上圖中我們可以看到,一次點擊事件之后,瀏覽器會調用 Function Call 進入JS引擎,執行 listener1,輸出listener1
          • 彈棧時發現JS調用棧為空,這時候就會執行 Microtasks 隊列中的所有 Microtask,輸出promise resolved 1
          • 接著瀏覽器調用 Function Call 進入JS引擎,執行 listener2,輸出listener 2
          • 彈棧時發現JS調用棧為空,這時候就會執行 Microtasks 隊列中的所有Microtask,輸出promise resolved 2

          JS觸發點擊事件

          在JS代碼中觸發點擊時輸出為 listener1 -> listener2 -> promise resolved 1 -> promise resolved 2

          • 從上圖中我們可以看到,瀏覽器運行JS代碼時,調用了 button.click 這個函數
          • 進入事件處理,執行 listener1,輸出listener1
          • 彈棧時發現JS調用棧非空(button.click函數還在運行)
          • 執行 listener2,輸出listener 2
          • 彈棧時發現JS調用棧為空,這時候就會執行 Microtasks 隊列中的所有 Microtask,輸出promise resolved 1promise resolved 2

          探索工具

          Chrome Devtool 中的 Performance 是一個 sample profiler (采樣分析儀),即它的運行機制是每1ms暫停一下vm,將當前的調用棧記錄下來,最后利用這部分信息做出可視化。

          由于它是一種 sample 的機制,所以在兩個 sample 之間的運行狀態可能會被丟失,所以我們在使用這個工具的時候可以

          1. 使CPU變慢:在 Devtool 中打開 "CPU 6x slowdown"
          2. 在要探索的函數中執行一段比較長的for循環占用CPU時間(如上面的 heavy)

          強烈建議大家學會使用這個工具,本文例子的 profile 結果文件也會文章最后給到大家,大家有興趣可以導入試一試。

          主動探索V8源碼

          兩年的時間過去了,在上周整理筆記的時候,我開始質疑這一個知識,"當瀏覽器 JS 引擎調用棧彈空的時候,才會執行 Microtasks 隊列"。

          因為它其實是個表現,我想知道瀏覽器和 JS 引擎到底是怎么實現這樣的機制的。

          因此我使用chrome://tracing進行探索,

          下面探索基于 Chrome Version 88.0.4324.192 (Official Build) (x86_64),不同瀏覽器的實現有不同

          人工點擊按鈕

          • 從上圖中我們可以看到,一次點擊事件之后,Blink(Blink是一個渲染引擎,Chrome 的 Renderer 進程中的主線程大部分時間會在 Blink 和 V8 兩者切換)會調用 v8.callFunction 進入 V8 引擎,執行 listener1,輸出listener1
          • 返回 Blink 時發現 V8 調用棧為空,這時候就會執行 V8.RunMicrotasks 執行 Microtasks 隊列中的所有 Microtask,輸出promise resolved 1
          • Blink 調用 v8.callFunction 進入 V8 引擎,執行 listener2,輸出listener 2
          • 返回 Blink 時發現 V8 調用棧為空,這時候就會執行 Microtasks 隊列中的所有 Microtask,輸出promise resolved 2

          注意,chrome://tracing 中的v8.xxx小寫v開頭的為 Blink 的調用,V8.xxx大寫的V才是真正的V8引擎。

          詳細源碼

          tracing 工具還有一個非常好用的功能,點擊下圖中的放大鏡,就可以直接打開 Chromium Code Search 查看 Chromium 的源碼。這個工具也自帶搜索功能,可以查看函數的聲明、定義以及調用。

          下面源碼的探索基于commit e8b6574c 的Chromium,并且為了簡化隱藏了無關的代碼,用...替代

          比如我們在上面的 tracing 里面看到有v8.callFunction的調用,我們點擊可以找到這個這個函數調用,是在 Blink 中調用 V8 的入口。

          third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc

          v8::MaybeLocal<v8::Value> V8ScriptRunner::CallFunction(
             v8::Local<v8::Function> function,
             ExecutionContext* context,
             v8::Local<v8::Value> receiver,
             int argc,
             v8::Local<v8::Value> args[],
             v8::Isolate* isolate 
          ){
            ... 
            TRACE_EVENT0("v8", "v8.callFunction"); // 這就是我們在 tracing 中看到的 v8.callFunction
            ...
            v8::MicrotaskQueue* microtask_queue=ToMicrotaskQueue(context); // 拿到 microtask 隊列
            ...
            v8::MicrotasksScope microtasks_scope(isolate, microtask_queue,
                                                 v8::MicrotasksScope::kRunMicrotasks); // 這個 scope 很可疑,這里構造之后在這個函數后面并沒有使用
            ... 
            probe::CallFunction probe(context, function, depth);
            v8::MaybeLocal<v8::Value> result=function->Call(isolate->GetCurrentContext(), receiver, argc, args); // 函數調用
            CHECK(!isolate->IsDead());
            ...
          }
          

          這里類型為v8::MicrotasksScope的變量很可疑,在創建之后并沒有在后續的函數里面使用,所以我們來看一下他的聲明和定義

          v8/include/v8.h

          /**
           * This scope is used to control microtasks when MicrotasksPolicy::kScoped
           * is used on Isolate. In this mode every non-primitive call to V8 should be
           * done inside some MicrotasksScope.
           * Microtasks are executed when topmost MicrotasksScope marked as kRunMicrotasks
           * exits.
           * kDoNotRunMicrotasks should be used to annotate calls not intended to trigger
           * microtasks.
           */
          class V8_EXPORT V8_NODISCARD MicrotasksScope {
           public:
            enum Type { kRunMicrotasks, kDoNotRunMicrotasks };
          
            MicrotasksScope(Isolate* isolate, Type type);
            MicrotasksScope(Isolate* isolate, MicrotaskQueue* microtask_queue, Type type);
            ~MicrotasksScope(); // 注意這個析構函數
            ...
          

          上面這段注釋告訴我們,這個類是用來控制 Microtasks 的(當 MicrotasksPolicy::kScoped這個策略被使用的時候,我們在后面會拎出來講,這里大家先默認 Blink 是設置了這個策略)。

          這里的析構函數非常的可疑,因為我們在前面一步發現變量microtasks_scope創建之后并沒有在后續的函數里面使用,而析構函數會在變量被銷毀時執行。我們繼續來看 v8::MicrotasksScope 的定義

          v8/src/api/api.cc

          MicrotasksScope::~MicrotasksScope() {
            if (run_) {
              microtask_queue_->DecrementMicrotasksScopeDepth(); // 這里將函數調用棧減少一層
              if (MicrotasksPolicy::kScoped==microtask_queue_->microtasks_policy() && // 這里檢查策略是否是 MicrotasksPolicy::kScoped
                  !isolate_->has_scheduled_exception()) {
                DCHECK_IMPLIES(isolate_->has_scheduled_exception(),
                               isolate_->scheduled_exception()==                 i::ReadOnlyRoots(isolate_).termination_exception());
                microtask_queue_->PerformCheckpoint(reinterpret_cast<Isolate*>(isolate_)); // 這一步嘗試執行 Microtasks 隊列
              }
            }
            ...
          }
          

          v8/src/execution/microtask-queue.cc

          void MicrotaskQueue::PerformCheckpoint(v8::Isolate* v8_isolate) {
            if (!IsRunningMicrotasks() && !GetMicrotasksScopeDepth() && // 注意,這一步檢查了調用棧是否為空
                !HasMicrotasksSuppressions()) {
              Isolate* isolate=reinterpret_cast<Isolate*>(v8_isolate);
              RunMicrotasks(isolate); // 執行隊列中的Microtasks
              isolate->ClearKeptObjects();
            }
          }
          
          ...
          
          int MicrotaskQueue::RunMicrotasks(Isolate* isolate) {
            ...
              {
                TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.RunMicrotasks"); // 我們在 tracing 里面也可以看到這個輸出
                maybe_result=Execution::TryRunMicrotasks(isolate, this,
                                                           &maybe_exception);
                processed_microtask_count=  static_cast<int>(finished_microtask_count_ - base_count);
              } 
            ...
          }
          

          到這里我們就知道了 Microtasks 的運行時機了,當 V8 執行完調用要返回 Blink 時,由于 MicrotasksScope 作用域失效,在其析構函數中檢查 JS 調用棧是否為空,如果為空的話就會運行 Microtasks

          下圖為完整的調用路徑

          觀察到的現象即是 "當瀏覽器 JS 引擎調用棧彈空的時候,才會執行 Microtasks 隊列"

          所以現在我如果問你,是不是 Macrotasks(宏任務)執行完才會執行 Microtasks 呢? 答案顯然是否定的,如同這個例子,我們的 Macrotask 是處理點擊輸入,而 Microtasks 在其中被執行了兩次。

          JS觸發點擊事件

          用JS觸發點擊事件其實也是同理的,同樣是使用V8::MicrotasksScope的析構函數來進行調用,只是前面幾次都因為調用棧非空(GetMicrotasksScopeDepth),所以等到最后面才執行。

          V8::MicrotasksPolicy

          那是不是所有使用V8引擎的應用 Microtasks 的運行時機都是一樣的呢?答案是否定的,Microtasks 的運行時機是由V8::MicrotasksPolicy來決定的。

          v8/include/v8.h

          /**
           * Policy for running microtasks:
           *   - explicit: microtasks are invoked with the
           *               Isolate::PerformMicrotaskCheckpoint() method;
           *   - scoped: microtasks invocation is controlled by MicrotasksScope objects;
           *   - auto: microtasks are invoked when the script call depth decrements
           *           to zero.
           */
          enum class MicrotasksPolicy { kExplicit, kScoped, kAuto };
          

          由上面的源碼注釋我們可以知道

          • explicit模式下,由應用自己主動調用才會運行 Microtasks。目前 Node 是使用了這種策略。
          • scoped模式下,由MicrotasksScope控制,但作用域失效時,在其析構函數中運行 Microtasks。目前 Blink 是使用這種策略,如下面的代碼段為 Blink 設置 MicrotasksPolicy。
          • auto模式為 V8 的默認值,當調用棧為空的時候就會執行 Microtasks

          third_party/blink/renderer/bindings/core/v8/v8_initializer.cc

          static void InitializeV8Common(v8::Isolate* isolate) {
            ...
            isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kScoped);
            ...
          }
          

          探索工具

          chrome://tracing/ 是一個 structural profiler 或叫 CPU profiler,與 Chrome Devtool performance 的 sample profiler 不同,他是由代碼中主動的去埋點打印出來的,所以每一次函數調用都會被記錄下來,不會像sample profiler一樣漏掉采樣時刻之間的狀態。

          使用方法如下,首先進入 chrome://tracing 點擊右上角的 Record,勾選住你想 profile 的組件。

          然后去到你的 demo 頁執行你想要探索的操作,回到 tracing 頁面點 Stop,接著在 Processes 里面篩選掉其他 Tab(標簽頁)的信息。

          最后使用鍵盤 w(放大)s(縮小)a(左移)d(右移)探索

          強烈建議大家學會使用這個工具,本文例子的 profile 結果文件也會文章最后給到大家,大家有興趣可以導入試一試。

          總結

          Event Loop(事件循環)是前端工程師經常討論到的話題,往深挖可以挖出 JS 如何實現異步、requestAnimationFrame、瀏覽器渲染機制、Macrotasks、Microtasks等等問題。

          本文主要探索了Microtasks的運行時機,我從兩年前被動接受知識 "當瀏覽器JS引擎調用棧彈空的時候,才會執行 Microtasks 隊列"

          到兩年后主動使用工具深入探索源碼后了解到的 "當 V8 執行完調用要返回 Blink 時,由于 MicrotasksScope 作用域失效,在其析構函數中檢查 JS 調用棧是否為空,如果為空就會運行 Microtasks。"

          這也是計算機最吸引我的地方,當你每隔一段時間回來看一個東西的時候,都能夠更往深一步,發現到更神奇的原理,也可以夠感受到自己的進步

          在探索的過程中還使用了一些工具,如 Chrome Devtool Performance、Chrome tracing、Chromium Code Search 等,希望感興趣的同學,也可以使用這些工具,更深入的探索瀏覽器內部原理。


          主站蜘蛛池模板: 国产成人一区二区三区在线观看| 无码人妻精品一区二区三区9厂| 麻豆一区二区99久久久久| 在线精品动漫一区二区无广告| 精品欧洲AV无码一区二区男男| 免费无码一区二区三区蜜桃| 一区二区三区在线观看| 日韩免费无码一区二区三区| 伦理一区二区三区| 少妇一夜三次一区二区| 久久无码人妻精品一区二区三区| 精品爆乳一区二区三区无码av| 中文字幕不卡一区| 久久久久国产一区二区| 成人区人妻精品一区二区不卡网站| 日韩免费无码一区二区视频| 亚洲熟妇AV一区二区三区宅男| 3d动漫精品啪啪一区二区免费| 久久精品免费一区二区三区| 成人无码一区二区三区| 一区二区三区在线观看视频| 国产在线一区二区视频| 国产一区二区精品久久岳√ | 国产一区二区三区高清在线观看| 国产伦精品一区二区三区四区| 亚洲av乱码一区二区三区按摩 | 久久99国产一区二区三区| 国产精品免费大片一区二区| 国产精品毛片a∨一区二区三区| 日本强伦姧人妻一区二区| 亚洲毛片不卡av在线播放一区| 国产无码一区二区在线| 国产午夜精品一区二区三区不卡| 一区二区三区四区无限乱码| 一区精品麻豆入口| 亚洲AV综合色区无码一区爱AV| 精品一区二区三区免费毛片爱 | 色综合视频一区二区三区44| 在线观看一区二区精品视频| 日韩经典精品无码一区| 本免费AV无码专区一区|