精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Babel 插件:30分鐘從入門到實(shí)戰(zhàn)

精選
開發(fā)
本文不會(huì)深入討論它們的詳細(xì)用法,當(dāng)你在編寫插件的時(shí)候,可以根據(jù)功能需求找到它們,我們后文也會(huì)涉及到部分用法。

Babel 是一個(gè) source to source(源碼到源碼)的 JavaScript 編譯器,簡(jiǎn)單來說,你為 Babel 提供一些 JavaScript 代碼,Babel 可以更改這些代碼,然后返回給你新生成的代碼。Babel 主要用于將 ECMAScript 2015+ 代碼轉(zhuǎn)換為能夠向后兼容的 JavaScript 版本。Babel 使用插件系統(tǒng)進(jìn)行代碼轉(zhuǎn)換,因此任何人都可以為 babel 編寫自己的轉(zhuǎn)換插件,以支持實(shí)現(xiàn)廣泛的功能。

Babel 編譯流程

Babel 的編譯流程主要分為三個(gè)部分:解析(parse),轉(zhuǎn)換(transform),生成(generate)。

code -> AST -> transformed AST -> transformed code

解析 Parse

將源碼轉(zhuǎn)換成抽象語法樹(AST, Abstract Syntax Tree)。

比如:

function square(n) {
return n * n;
}

以上的程序可以被轉(zhuǎn)換成類似這樣的抽象語法樹:

- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n

轉(zhuǎn)換 Transform

轉(zhuǎn)換階段接受一個(gè) AST 并遍歷它,在遍歷的過程中對(duì)樹的節(jié)點(diǎn)進(jìn)行增刪改。這也是運(yùn)行 Babel 插件的階段。

生成 Generate

將經(jīng)過一系列轉(zhuǎn)換之后的 AST 轉(zhuǎn)換成字符串形式的代碼,同時(shí)還會(huì)創(chuàng)建 sourcemap。

你會(huì)用到的一些工具庫

對(duì)于每一個(gè)階段,Babel 都提供了一些工具庫:

  • Parse 階段可以使用@babel/parser 將源碼轉(zhuǎn)換成 AST。?
  • Transform 階段可以使用@babel/traverse 調(diào)用 visitor 函數(shù)遍歷 AST,期間可以使用 @babel/types 創(chuàng)建 AST 和檢查 AST 節(jié)點(diǎn)的類型,批量創(chuàng)建 AST 的場(chǎng)景下可以使用 @babel/template 中途還可以使用 @babel/code-frame 打印報(bào)錯(cuò)信息。
  • Generate 階段可以使用@babel/generator 根據(jù) AST 生成代碼字符串和 sourcemap。

以上提及的包都是 @babel/core 的 dependencies,所以只需要安裝 @babel/core 就能訪問到它們。

除了上面提到的工具庫,以下工具庫也比較常用:

  • @babel/helper-plugin-utils:如果插件使用者的 Babel 版本沒有您的插件所需的 API,它能給用戶提供明確的錯(cuò)誤信息。
  • babel-plugin-tester:用于幫助測(cè)試 Babel 插件的實(shí)用工具,通常配合 jest 使用。

本文不會(huì)深入討論它們的詳細(xì)用法,當(dāng)你在編寫插件的時(shí)候,可以根據(jù)功能需求找到它們,我們后文也會(huì)涉及到部分用法。

認(rèn)識(shí) Babel 插件

接下來讓我們開始認(rèn)識(shí) Babel 插件吧。

babel 插件是一個(gè)簡(jiǎn)單的函數(shù),它必須返回一個(gè)匹配以下接口的對(duì)象。如果 Babel 發(fā)現(xiàn)未知屬性,它將拋出錯(cuò)誤。

圖片

以下是一個(gè)簡(jiǎn)單的插件示例:

export default function(api, options, dirname) {
return {
visitor: {
StringLiteral(path, state) {},
}
};
};

Babel 插件接受 3 個(gè)參數(shù):

  • api:一個(gè)對(duì)象,包含了 types (@babel/types)、traverse (@babel/traverse)、template(@babel/template) 等實(shí)用方法,我們能從這個(gè)對(duì)象中訪問到 @babel/core dependecies 中包含的方法。
  • options:插件參數(shù)。
  • dirname:目錄名。

返回的對(duì)象有 name、manipulateOptions、pre、visitor、post、inherits 等屬性:

  • name:插件名字。
  • inherits:指定繼承某個(gè)插件,通過 Object.assign 的方式,和當(dāng)前插件的 options 合并。
  • visitor:指定 traverse 時(shí)調(diào)用的函數(shù)。
  • pre 和 post 分別在遍歷前后調(diào)用,可以做一些插件調(diào)用前后的邏輯,比如可以往 file(表示文件的對(duì)象,在插件里面通過 state.file 拿到)中放一些東西,在遍歷的過程中取出來。
  • manipulateOptions:用于修改 options,是在插件里面修改配置的方式。

我們上面提到了一些陌生的概念:visitor、path、state,現(xiàn)在讓我們一起來認(rèn)識(shí)它們:

visitor 訪問者

這個(gè)名字來源于設(shè)計(jì)模式中的訪問者模式(https://en.wikipedia.org/wiki/Visitor_pattern)。簡(jiǎn)單的說它就是一個(gè)對(duì)象,指定了在遍歷 AST 過程中,訪問指定節(jié)點(diǎn)時(shí)應(yīng)該被調(diào)用的方法。

假如我們有這樣一段程序:

function foo() {
return 'string'
}

這段代碼對(duì)應(yīng)的 AST 如下:

- Program
- FunctionDeclaration (body[0])
- Identifier (id)
- BlockStatement (body)
- ReturnStatement (body[0])
- StringLiteral (arugument)

當(dāng)我們對(duì)這顆 AST 進(jìn)行深度優(yōu)先遍歷時(shí),每次訪問 StringLiteral 都會(huì)調(diào)用 visitor.StringLiteral。

當(dāng) visitor.StringLiteral 是一個(gè)函數(shù)時(shí),它將在向下遍歷的過程中被調(diào)用(即進(jìn)入階段)。當(dāng) visitor.StringLiteral 是一個(gè)對(duì)象時(shí)({ enter(path, state) {}, exit(path, state) {} }),visitor.StringLiteral.enter 將在向下遍歷的過程中被調(diào)用(進(jìn)入階段),visitor.StringLiteral.exit 將在向上遍歷的過程中被調(diào)用(退出階段)。

Path 路徑

Path 用于表示兩個(gè)節(jié)點(diǎn)之間連接的對(duì)象,這是一個(gè)可操作和訪問的巨大可變對(duì)象。

Path 之間的關(guān)系如圖所示:

除了能在 Path 對(duì)象上訪問到當(dāng)前 AST 節(jié)點(diǎn)、父級(jí) AST 節(jié)點(diǎn)、父級(jí) Path 對(duì)象,還能訪問到添加、更新、移動(dòng)和刪除節(jié)點(diǎn)等其他方法,這些方法提高了我們對(duì) AST 增刪改的效率。

State 狀態(tài)

在實(shí)際編寫插件的過程中,某一類型節(jié)點(diǎn)的處理可能需要依賴其他類型節(jié)點(diǎn)的處理結(jié)果,但由于 visitor 屬性之間互不關(guān)聯(lián),因此需要 state 幫助我們?cè)诓煌?visitor 之間傳遞狀態(tài)。

一種處理方式是使用遞歸,并將狀態(tài)往下層傳遞:

const anotherVisitor = {
Identifier(path) {
console.log(this.someParam) // => 'xxx'
}
};

const MyVisitor = {
FunctionDeclaration(path, state) {
// state.cwd: 當(dāng)前執(zhí)行目錄
// state.opts: 插件 options
// state.filename: 當(dāng)前文件名(絕對(duì)路徑)
// state.file: BabelFile 對(duì)象,包含當(dāng)前整個(gè) ast,當(dāng)前文件內(nèi)容 code,etc.
// state.key: 當(dāng)前插件名字
path.traverse(anotherVisitor, { someParam: 'xxx' });
}
};

另外一種傳遞狀態(tài)的辦法是將狀態(tài)直接設(shè)置到 this 上,Babel 會(huì)給 visitor 上的每個(gè)方法綁定 this。在 Babel 插件中,this 通常會(huì)被用于傳遞狀態(tài):從 pre 到 visitor 再到 post。

export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}

常用的 API

Babel 沒有完整的文檔講解所有的 api,因此下面會(huì)列舉一些可能還算常用的 api(并不是所有,主要是 path 和 types 上的方法或?qū)傩裕覀儾⒉恍枰勘诚聛恚谀阈枰玫臅r(shí)候,能找到對(duì)應(yīng)的方法即可。

你可以通過 babel 的 typescript 類型定義找到以下列舉的屬性和方法,還可以通過 Babel Handbook 找到它們的具體使用方法。

Babel Handbook:https://astexplorer.net/

查詢

  • path.node:訪問當(dāng)前節(jié)點(diǎn)
  • path.get():獲取屬性內(nèi)部的 path
  • path.inList:判斷路徑是否有同級(jí)節(jié)點(diǎn)
  • path.key:獲取路徑所在容器的索引
  • path.container:獲取路徑的容器(包含所有同級(jí)節(jié)點(diǎn)的數(shù)組)
  • path.listKey:獲取容器的key
  • path.getSibling():獲得同級(jí)路徑
  • path.findParent():對(duì)于每一個(gè)父路徑調(diào)用 callback 并將其 NodePath 當(dāng)作參數(shù),當(dāng) callback 返回真值時(shí),則將其 NodePath 返回
  • path.find():與 path.findParent 的區(qū)別是,該方法會(huì)遍歷當(dāng)前節(jié)點(diǎn)

遍歷

  • path.stop():跳過遍歷當(dāng)前路徑的子路徑

  • path.skip():完全停止遍歷

判斷


  • types.isXxx():檢查節(jié)點(diǎn)的類型,如 types.isStringLiteral(path.node)

  • path.isReferencedIdentifier():檢查標(biāo)識(shí)符(Identifier)是否被引用

增刪改


  • path.replaceWith():替換單個(gè)節(jié)點(diǎn)

  • path.replaceWithMultiple():用多節(jié)點(diǎn)替換單節(jié)點(diǎn)

  • path.replaceWithSourceString():用字符串源碼替換節(jié)點(diǎn)

  • path.insertBefore() / path.insertAfter():插入兄弟節(jié)點(diǎn)

  • path.get('listKey').unshiftContainer() / path.get('listKey').pushContainer():插入一個(gè)節(jié)點(diǎn)到數(shù)組中,如 body

  • path.remove():刪除一個(gè)節(jié)點(diǎn)

作用域


  • path.scope.hasBinding(): 從當(dāng)前作用域開始向上查找變量

  • path.scope.hasOwnBinding():僅在當(dāng)前作用域中查找變量

  • path.scope.generateUidIdentifier():生成一個(gè)唯一的標(biāo)識(shí)符,不會(huì)與任何本地定義的變量相沖突

  • path.scope.generateUidIdentifierBasedOnNode():基于某個(gè)節(jié)點(diǎn)創(chuàng)建唯一的標(biāo)識(shí)符

  • path.scope.rename():重命名綁定及其引用

AST Explorer

在 @babel/types 的類型定義中,可以找到所有 AST 節(jié)點(diǎn)類型。我們不需要記住所有節(jié)點(diǎn)類型,社區(qū)內(nèi)有一個(gè) AST 可視化工具能夠幫助我們分析 AST:axtexplorer.net。

在這個(gè)網(wǎng)站的左側(cè),可以輸入我們想要分析的代碼,在右側(cè)會(huì)自動(dòng)生成對(duì)應(yīng)的 AST。當(dāng)我們?cè)谧髠?cè)代碼區(qū)域點(diǎn)擊某一個(gè)節(jié)點(diǎn),比如函數(shù)名 foo,右側(cè) AST 會(huì)自動(dòng)跳轉(zhuǎn)到對(duì)應(yīng)的 Identifier AST 節(jié)點(diǎn),并高亮展示。

我們還可以修改要 parse 的語言、使用的 parser、parser 參數(shù)等。

自己實(shí)現(xiàn)一個(gè)插件吧

現(xiàn)在讓我們來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的插件吧!以下是插件需要實(shí)現(xiàn)的功能:

  1. 將代碼里重復(fù)的字符串字面量(StringLiteral)提升到頂層作用域。
  2. 接受一個(gè)參數(shù) minCount,它是 number 類型,如果某個(gè)字符串字面量重復(fù)次數(shù)大于等于 minCount 的值,則將它提升到頂層作用域,否則不做任何處理。

因此,對(duì)于以下輸入:

const s1 = "foo";
const s2 = "foo";

const s3 = "bar";

function f1() {
const s4 = "baz";
if (true) {
const s5 = "baz";
}
}

應(yīng)該輸出以下代碼:

var _foo = "foo",
_baz = "baz";
const s1 = _foo;
const s2 = _foo;
const s3 = "bar";

function f1() {
const s4 = _baz;

if (true) {
const s5 = _baz;
}
}

通過 https://astexplorer.net/,我們發(fā)現(xiàn)代碼里的字符串在 AST 上對(duì)應(yīng)的節(jié)點(diǎn)叫做 StringLiteral,如果想要拿到代碼里所有的字符串并且統(tǒng)計(jì)每種字符串的數(shù)量,就需要遍歷 StringLiteral 節(jié)點(diǎn)。

我們需要一個(gè)對(duì)象用于存儲(chǔ)所有 StringLiteral,key 是 StringLiteral 節(jié)點(diǎn)的 value 屬性值,value 是一個(gè)數(shù)組,用于存儲(chǔ)擁有相同 path.node.value 的所有 path 對(duì)象,最后把這個(gè)對(duì)象存到 state 對(duì)象上,以便于在遍歷結(jié)束時(shí)能統(tǒng)計(jì)相同字符串的重復(fù)次數(shù),從而可以判斷哪些節(jié)點(diǎn)需要被替換為一個(gè)標(biāo)識(shí)符。

export default function() {
return {
visitor: {
StringLiteral(path, state) {
state.stringPathMap = state.stringPathMap || {};
const nodes = state.stringPathMap[path.node.value] || [];
nodes.push(path);
state.stringPathMap[path.node.value] = nodes;
}
}
};
}

通過 https://astexplorer.net/,我們發(fā)現(xiàn)如果想要往頂層作用域中插入一個(gè)變量,其實(shí)就是往 Program 節(jié)點(diǎn)的 body 上插入 AST 節(jié)點(diǎn)。Program 節(jié)點(diǎn)也是 AST 的頂層節(jié)點(diǎn),在遍歷過程的退出階段,Program 節(jié)點(diǎn)是最后一個(gè)被處理的,因此我們需要做的事情是:根據(jù)收集到的字符串字面量,分別創(chuàng)建一個(gè)位于頂層作用域的變量,并將它們統(tǒng)一插入到 Program 的 body 中,同時(shí)將代碼中的字符串替換為對(duì)應(yīng)的變量。

圖片

export default function() {
return {
visitor: {
StringLiteral(path, state) { /** ... */ },
Program: {
exit(path, state) {
const { minCount = 2 } = state.opts || {};

for (const [string, paths] of Object.entries(state.stringPathMap || {})) {
if (paths.length < minCount) {
continue;
}

const id = path.scope.generateUidIdentifier(string);

paths.forEach(p => {
p.replaceWith(id);
});

path.scope.push({ id, init: types.stringLiteral(string) });
}
},
},
}
};
}

完整代碼

import { PluginPass, NodePath } from '@babel/core';
import { declare } from '@babel/helper-plugin-utils';

interface Options {
/**
* 當(dāng)字符串字面量的重復(fù)次數(shù)大于或小于 minCount,將會(huì)被提升到頂層作用域
*/
minCount?: number;
}

type State = PluginPass & {
// 以 StringLiteral 節(jié)點(diǎn)的 value 屬性值為 key,存放所有 StringLiteral 的 Path 對(duì)象
stringPathMap?: Record<string, NodePath[]>;
};

const HoistCommonString = declare<Options>(({ assertVersion, types }, options) => {
// 判斷當(dāng)前 Babel 版本是否為 7
assertVersion(7);

return {
// 插件名字
name: 'hoist-common-string',

visitor: {
StringLiteral(path, state: State) {
// 將所有 StringLiteral 節(jié)點(diǎn)對(duì)應(yīng)的 path 對(duì)象收集起來,存到 state 對(duì)象里,
// 以便于在遍歷結(jié)束時(shí)能統(tǒng)計(jì)相同字符串的重復(fù)次數(shù)
state.stringPathMap = state.stringPathMap || {};

const nodes = state.stringPathMap[path.node.value] || [];
nodes.push(path);

state.stringPathMap[path.node.value] = nodes;
},

Program: {
// 將在遍歷過程的退出階段被調(diào)用
// Program 節(jié)點(diǎn)是頂層 AST 節(jié)點(diǎn),可以認(rèn)為 Program.exit 是最后一個(gè)執(zhí)行的 visitor 函數(shù)
exit(path, state: State) {
// 插件參數(shù)。還可以通過 state.opts 拿到插件參數(shù)
const { minCount = 2 } = options || {};

for (const [string, paths] of Object.entries(state.stringPathMap || {})) {
// 對(duì)于重復(fù)次數(shù)少于 minCount 的 Path,不做處理
if (paths.length < minCount) {
continue;
}

// 基于給定的字符串創(chuàng)建一個(gè)唯一的標(biāo)識(shí)符
const id = path.scope.generateUidIdentifier(string);

// 將所有相同的字符串字面量替換為上面生成的標(biāo)識(shí)符
paths.forEach(p => {
p.replaceWith(id);
});

// 將標(biāo)識(shí)符添加到頂層作用域中
path.scope.push({ id, init: types.stringLiteral(string) });
}
},
},
},
};
});

測(cè)試插件

測(cè)試 Babel 插件有三種常用的方法:

  • 測(cè)試轉(zhuǎn)換后的 AST 結(jié)果,檢查是否符合預(yù)期
  • 測(cè)試轉(zhuǎn)換后的代碼字符串,檢查是否符合預(yù)期(通常使用快照測(cè)試)
  • 執(zhí)行轉(zhuǎn)換后的代碼,檢查執(zhí)行結(jié)果是否符合預(yù)期

我們一般使用第二種方法,配合 babel-plugin-tester 可以很好地幫助我們完成測(cè)試工作。配合 babel-plugin-tester,我們可以對(duì)比輸入輸出的字符串、文件、快照。

import pluginTester from 'babel-plugin-tester';
import xxxPlugin from './xxxPlugin';

pluginTester({
plugin: xxxPlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
// 1. 對(duì)比轉(zhuǎn)換前后的字符串
// 1.1 輸入輸出完全一致時(shí),可以簡(jiǎn)寫
'does not change code with no identifiers': '"hello";',
// 1.2 輸入輸出不一致
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
// 2. 對(duì)比轉(zhuǎn)換前后的文件
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
// 3. 與上一次生成的快照做對(duì)比
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});

本文將以快照測(cè)試為例,以下是測(cè)試我們插件的示例代碼:

import pluginTester from 'babel-plugin-tester';
import HoistCommonString from '../index';

pluginTester({
// 插件
plugin: HoistCommonString,
// 插件名,可選
pluginName: 'hoist-common-string',
// 插件參數(shù),可選
pluginOptions: {
minCount: 2,
},
tests: {
'using jest snapshots': {
// 輸入
code: `const s1 = "foo";
const s2 = "foo";

const s3 = "bar";

function f1() {
const s4 = "baz";
if (true) {
const s5 = "baz";
}
}`,
// 使用快照測(cè)試
snapshot: true,
},
},
});

當(dāng)我們運(yùn)行 jest 后(更多關(guān)于 jest 的介紹,可以查看 jest 官方文檔https://jestjs.io/docs/getting-started),會(huì)生成一個(gè) snapshots 目錄:

圖片

有了快照以后,每次迭代插件都可以跑一下單測(cè)以快速檢查功能是否正常。快照的更新也很簡(jiǎn)單,只需要執(zhí)行 jest --updateSnapshot。

使用插件

如果想要使用 Babel 插件,需要在配置文件里添加 plugins 選項(xiàng),plugins 選項(xiàng)接受一個(gè)數(shù)組,值為字符串或者數(shù)組。以下是一些例子:

// .babelrc
{
"plugins": [
"babel-plugin-myPlugin1",
["babel-plugin-myPlugin2"],
["babel-plugin-myPlugin3", { /** 插件 options */ }],
"./node_modules/asdf/plugin"
]
}

Babel 對(duì)插件名字的格式有一定的要求,比如最好包含 babel-plugin,如果不包含的話也會(huì)自動(dòng)補(bǔ)充。以下是 Babel 插件名字的自動(dòng)補(bǔ)全規(guī)則:

圖片

到這里,Babel 插件的學(xué)習(xí)就告一段落了,如果大家想繼續(xù)深入學(xué)習(xí) Babel 插件,可以訪問 Babel 的倉庫(https://github.com/babel/babel/tree/main/packages)這是一個(gè) monorepo,里面包含了很多真實(shí)的插件,通過閱讀這些插件,相信你一定能對(duì) Babel 插件有更深入的理解!

參考文檔

Babel plugin handbook:https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md

Babel 官方文檔:https://babeljs.io/docs/en/

Babel 插件通關(guān)秘籍:https://juejin.cn/book/6946117847848321055

責(zé)任編輯:未麗燕 來源: 字節(jié)跳動(dòng)技術(shù)團(tuán)隊(duì)
相關(guān)推薦

2016-08-03 16:01:47

GitLinux開源

2017-01-10 09:07:53

tcpdumpGET請(qǐng)求

2023-09-27 08:09:22

2014-12-02 10:07:58

華為云VDC

2018-02-02 10:24:37

Nginx入門指南

2013-05-03 10:57:09

泛型泛型教程

2020-05-22 10:20:27

Shiro架構(gòu)字符串

2020-06-15 14:40:30

架構(gòu)運(yùn)維技術(shù)

2022-06-17 08:05:28

Grafana監(jiān)控儀表盤系統(tǒng)

2016-09-13 20:58:41

MySQ基礎(chǔ)入門Sql

2012-06-28 10:26:51

Silverlight

2025-10-30 09:09:25

2017-07-18 11:10:45

2021-03-03 11:36:57

Java 8Java 15Java

2013-06-27 09:41:19

LuaLua語言Lua語言快速入門

2025-04-07 05:00:00

2015-11-23 17:34:33

秒借

2024-08-27 13:43:38

Spring系統(tǒng)業(yè)務(wù)

2017-06-07 18:40:33

PromiseJavascript前端

2013-12-11 10:00:14

C++新特性C
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

俺要去色综合狠狠| 中文国产字幕在线观看| 老司机午夜精品视频| 精品产国自在拍| 国产成人免费av在线| 欧美精品激情在线观看| 人妻 日韩 欧美 综合 制服| 亚洲欧美韩国| 中文字幕制服丝袜成人av| 91久久大香伊蕉在人线| 少妇太紧太爽又黄又硬又爽| 成人女性视频| 日韩欧美第一区| 久久久免费视频网站| 欧美一区二区三区| 99久久精品99国产精品 | caoporm免费视频在线| heyzo一本久久综合| 国产精品一区二区久久| 国产精品19乱码一区二区三区| 国产乱码精品一区二区亚洲| 日韩三级在线免费观看| www.xxx亚洲| 久久99亚洲网美利坚合众国| 国产人久久人人人人爽| 国产精品久久久久久免费观看| 樱花视频在线免费观看| 国产综合色产| 日韩亚洲在线观看| 亚洲av综合一区二区| 亚洲国产视频二区| 精品视频一区二区三区免费| 日本在线xxx| 免费在线视频欧美| 欧美激情自拍偷拍| 麻豆精品传媒视频| 国产综合无码一区二区色蜜蜜| 青青草伊人久久| 欧美一级在线亚洲天堂| 久久亚洲成人av| 久久久久久久久久久妇女| 亚洲欧美中文字幕| 免费中文字幕av| 国产劲爆久久| 欧美大胆一级视频| xxww在线观看| 视频一区在线播放| 日韩欧美在线看| www.xxx麻豆| 免费**毛片在线| 国产三级一区二区三区| 黄色99视频| 国产 日韩 欧美 综合| 国产伦精一区二区三区| 91久久精品日日躁夜夜躁国产| 中文字幕av影视| 日韩成人精品在线| 国产精品jvid在线观看蜜臀| 日本视频免费观看| 久久福利一区| 日韩av手机在线| 精品国产一区二区三区四| 亚洲一区视频| 欧美在线视频在线播放完整版免费观看 | 国外成人在线视频网站| 欧美一级视频免费| 97精品国产97久久久久久久久久久久| 国产麻豆乱码精品一区二区三区| 国产精品扒开腿做爽爽爽的视频| 95视频在线观看| 日韩精品久久久久久久软件91| 8x8x8国产精品| 中文字幕avav| 成人在线tv视频| 亚洲成人av在线播放| 这里只有精品在线观看视频 | 在线观看亚洲成人| 国产区二区三区| 四虎视频在线精品免费网址| 7777精品伊人久久久大香线蕉最新版| 色婷婷.com| 精品一区视频| 亚洲成av人乱码色午夜| 黄色性生活一级片| 精品99在线| 久久久精品久久| 国产中文字幕免费| 久久男女视频| 成人国产精品久久久久久亚洲| av中文字幕播放| 99久久亚洲一区二区三区青草| 欧美日韩在线一二三| 一广人看www在线观看免费视频| 国产精品国产精品国产专区不蜜 | 国产精品免费电影| 国产1区2区3区中文字幕| 亚洲国产成人在线观看| 91啪亚洲精品| 亚洲高清在线播放| xxxx视频在线| 欧美丝袜第三区| 日本天堂在线播放| 精品久久一区| 欧美刺激性大交免费视频| 国产高清中文字幕| 寂寞少妇一区二区三区| 国产在线播放一区二区| 亚洲s色大片| 亚洲不卡在线观看| 国产成年人视频网站| 极品国产人妖chinesets亚洲人妖| 国产亚洲美女久久| 日本a在线观看| 男人操女人的视频在线观看欧美| 国产精品区一区二区三含羞草| 成人高清免费在线播放| 亚洲国产精品久久久男人的天堂| 韩国中文字幕av| 精品自拍偷拍| 久久亚洲电影天堂| 国产成人无码av| 国产91丝袜在线播放| 先锋影音日韩| 色网在线免费观看| 日韩精品一区二区三区三区免费 | 欧美激情在线一区二区三区| 免费人成自慰网站| 日韩av黄色| 亚洲日本成人网| 国产精华一区| 夜夜爽妓女8888视频免费观看| 国产乱妇无码大片在线观看| 神马影院一区二区| 在线女人免费视频| 精品国产精品网麻豆系列| 亚洲欧美综合7777色婷婷| 奶水喷射视频一区| 国产九色91| 亚洲图区一区| 7777精品伊人久久久大香线蕉超级流畅 | 一区二区三区在线看| 九热视频在线观看| 伊人久久大香线蕉av不卡| 久久久在线视频| av在线免费在线观看| 成人免费一区二区三区视频| 日本三区在线观看| 色棕色天天综合网| 欧美最猛性xxxxx亚洲精品| 成人午夜视频一区二区播放| 亚洲精品一二三四区| 91精品999| 日韩在线视屏| 国产精品视频内| 国产中文字幕在线视频| 欧美性猛交xxxx富婆| 久久偷拍免费视频| 国产精品综合| 久久精品一二三区| 欧美13videosex性极品| 亚洲福利精品在线| 国产成人在线观看网站| 成人高清在线视频| 少妇人妻大乳在线视频| 国产厕拍一区| 欧亚精品中文字幕| 三级在线视频| 色国产精品一区在线观看| 国产毛片毛片毛片毛片毛片毛片| 影音先锋亚洲精品| 韩日午夜在线资源一区二区| 亚洲电影观看| 亚洲品质视频自拍网| 欧美在线视频精品| 一区二区中文字幕在线| 欧美一区二区三区影院| 亚洲国产精品一区| 国内成+人亚洲| 波多野结衣亚洲| 亚洲午夜精品久久久久久久久久久久| 天天综合久久综合| 亚洲视频一二三区| 北京富婆泄欲对白| 日韩黄色一级片| 在线视频福利一区| 中文字幕亚洲在线观看| 2019精品视频| 成人亚洲性情网站www在线观看| 在线一区二区三区四区五区| 国产一级淫片久久久片a级| 狠狠久久亚洲欧美| 欧美日韩一区二区三区四区五区| 91视频免费入口| 亚洲视频福利| 日韩少妇中文字幕| 精品精品视频| 91高清免费视频| 麻豆网站在线看| 337p日本欧洲亚洲大胆精品| 亚洲中文无码av在线| 亚洲最大成人网4388xx| 波多野结衣 在线| 久久99国产精品尤物| 日本免费成人网| 色综合中文网| 高清国产在线一区| 无人区在线高清完整免费版 一区二| 超碰精品一区二区三区乱码| 无码国精品一区二区免费蜜桃| 欧美日韩在线播放三区| 亚洲精品午夜久久久久久久| 欧美激情一区二区三区蜜桃视频 | 婷婷六月国产精品久久不卡| 久久久999国产精品| 婷婷久久久久久| 欧美日本免费一区二区三区| 国产乡下妇女做爰视频| 中文字幕中文在线不卡住| 97伦伦午夜电影理伦片| 国产伦精品一区二区三区免费迷| 久久国产乱子伦免费精品| 欧美一区二区三区免费看| 日本在线免费观看一区| 哺乳一区二区三区中文视频| 国产免费亚洲高清| 外国成人直播| 国内精品一区二区三区四区| 精产国品自在线www| 亚洲女人天堂成人av在线| 亚洲爱爱综合网| 欧美色综合影院| 亚洲欧美综合另类| 亚洲一区二区三区不卡国产欧美| 亚洲视频重口味| 欧美国产日韩亚洲一区| 538国产视频| 成人性视频免费网站| 中文字幕在线视频精品| 视频一区二区三区中文字幕| 成人性免费视频| 欧美天堂亚洲电影院在线观看 | 免费福利在线视频| 亚洲国产精品成人一区二区| 国产偷拍一区二区| 宅男在线国产精品| 亚洲网站免费观看| 欧美日韩在线不卡| 最近中文字幕在线观看| 日本电影亚洲天堂一区| 永久免费无码av网站在线观看| 精品久久久一区二区| 日韩欧美大片在线观看| 亚洲成人av免费| xxxx 国产| 亚洲午夜影视影院在线观看| 久草免费在线观看视频| 亚洲靠逼com| 亚洲 欧美 综合 另类 中字| 秋霞影院午夜丰满少妇在线视频| 亚洲人成在线播放| 激情福利在线| 亚洲视频在线播放| 东热在线免费视频| 一本色道久久88综合日韩精品 | 一区二区日本伦理| 水蜜桃久久夜色精品一区| 亚洲国产日韩综合一区| 日韩精品久久| 国产又黄又爽免费视频| 一区二区三区在线电影| av动漫在线播放| 亚洲精品视频啊美女在线直播| 成人一对一视频| 久久精品一区二区国产| 国产成人av影视| 裸体在线国模精品偷拍| 亚洲一二区在线观看| 国产成人免费在线| 欧美熟妇精品黑人巨大一二三区| 91美女片黄在线观看| 国产精品麻豆入口| 国产亚洲综合av| 女人裸体性做爰全过| 亚洲免费观看高清| 精品在线播放视频| 欧美三级电影在线观看| 国产视频在线一区| 亚洲国产精品va在线看黑人 | 99久久婷婷国产综合精品| 久久av无码精品人妻系列试探| 中文字幕不卡一区| 欧美日韩大片在线观看| 欧美日韩日本国产| 一区二区的视频| 精品毛片乱码1区2区3区| 男同在线观看| 欧美成人精品影院| 亚洲美女尤物影院| 成人欧美在线观看| 亲子伦视频一区二区三区| 亚洲欧洲日夜超级视频| 国内精品美女在线观看| 九色porny91| 国产福利一区在线| 精品人伦一区二区| 性欧美超级视频| 欧美激情欧美激情在线五月| 久久r热视频| 91嫩草在线| 国产欧美日韩精品一区二区三区| 浴室偷拍美女洗澡456在线| 久久久久久久欧美精品| 99国产精品免费视频| 久久精品一级爱片| 国产性猛交普通话对白| 欧美丝袜丝nylons| 天天干天天操av| 欧美成人性生活| 日韩欧美精品电影| 好看的日韩精品| 午夜精品偷拍| jizzzz日本| 久久久久久夜精品精品免费| 欧美成人综合色| 欧美色综合影院| 毛片在线播放网站| 国内成人精品一区| 91精品福利观看| 日韩三级在线播放| 久久久久99| 亚洲国产精品无码久久久久高潮 | 国产在线一卡二卡| 欧美中文字幕亚洲一区二区va在线| 黄色片一区二区三区| 久久精品人人做人人爽| 免费在线观看一区| 欧美精品v日韩精品v国产精品| 黄色亚洲精品| 中文字幕久久久久久久| 一区二区三区在线观看视频| 这里只有精品9| 国产亚洲精品美女久久久| 日韩脚交footjobhd| 国产日韩欧美二区| 亚洲天堂久久| 男人添女人荫蒂国产| 亚洲柠檬福利资源导航| 国产精品午夜福利| 久久精品电影网| 国产美女视频一区二区| 中文字幕一区二区三区乱码| 麻豆国产精品视频| 3d蒂法精品啪啪一区二区免费| 日本高清成人vr专区| 亚洲精品欧美极品| 91精品国产91久久久久久密臀| 污视频网址在线观看| 国产精品伦理在线| 伊人网免费视频| 中文字幕日韩欧美| 看片一区二区| 欧美 另类 交| 国产精品一品视频| 免费在线黄色片| 精品国产一区二区三区久久影院| 黄色成人在线网| 国产伦视频一区二区三区| 99亚洲一区二区| 一本加勒比波多野结衣| 欧美日韩国产一区在线| 你懂的在线网址| 国产欧美在线播放| 一本一本久久a久久综合精品| 深夜做爰性大片蜜桃| 亚洲国产日韩一级| 天天干天天操av| 国产精品久久久久77777| 欧美丰满日韩| 亚洲高清av一区二区三区| 亚洲一区av在线| 欧美美乳在线| 91精品久久久久久久久久另类| 欧美一区91| 国产真实乱人偷精品| 91福利国产成人精品照片| 青青影院在线观看| 国产成人精品日本亚洲11| 亚洲一区激情| 国产农村妇女精品一区| 欧美大片拔萝卜| 中文字幕系列一区| 91视频成人免费| 久久综合一区二区| 伊人成人在线观看| 久久理论片午夜琪琪电影网| 精品国产一区探花在线观看| 一本之道在线视频| 欧美视频裸体精品| 2024最新电影免费在线观看| 欧美精品一区二区视频|