国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

零基礎(chǔ)理解 ESLint 核心原理

共 15755字,需瀏覽 32分鐘

 ·

2022-05-28 18:57

來自團隊 楊勁松 同學(xué)的分享

概述

本文將介紹 ESLint 的工作原理,內(nèi)容涉及 ESLint 如何讀取配置、加載配置,檢驗,修復(fù)的全流程。

為什么需要 ESLint

ESLint 相信大家都不陌生,如今前端工作越來越復(fù)雜,一個項目往往是多人參與開發(fā),雖然說每個人的代碼風(fēng)格都不一樣,但是如果我們完全不做任何約束,允許開發(fā)人員任意發(fā)揮,隨著項目規(guī)模慢慢變大,很快項目代碼將會成為不堪入目的??山,因此對于代碼的一些基本寫法還是需要有個約定,并且當(dāng)代碼中出現(xiàn)與約定相悖的寫法時需要給出提醒,對于一些簡單的約定最好還能幫我們自動修復(fù),而這正是 ESLint 要干的事情,下面引用一下 ESLint 官網(wǎng)的介紹。

  • 「Find Problems」:ESLint statically analyzes your code to quickly find problems. ESLint is built into most text editors and you can run ESLint as part of your continuous integration pipeline.
  • 「Fix Automatically」:Many problems ESLint finds can be automatically fixed. ESLint fixes are syntax-aware so you won't experience errors introduced by traditional find-and-replace algorithms.
  • 「Customize」:Preprocess code, use custom parsers, and write your own rules that work alongside ESLint's built-in rules. You can customize ESLint to work exactly the way you need it for your project.

也就是三部分:「找出代碼問題」,「自動修復(fù)」,「自定義規(guī)則」。ESLint 經(jīng)過許多年的發(fā)展已經(jīng)非常成熟,加上社區(qū)諸多開發(fā)者的不斷貢獻,目前社區(qū)也已經(jīng)積累了許多優(yōu)秀的代碼寫法約定,為了項目代碼的健康,也為了開發(fā)人員的身心健康,盡早地引入合適的 ESLint 規(guī)則是非常有必要的??。

ESLint 是如何工作的??

知其然更應(yīng)知其所以然,ESLint 是如何做到“讀懂”你的代碼甚至給你修復(fù)代碼的呢,沒錯,還是 AST(抽象語法樹),大學(xué)編譯原理課程里我們也學(xué)習(xí)過它,另外了解 Babel 或者 Webpack 的同學(xué)更應(yīng)該對 AST 很熟悉了。其中 ESLint 是使用 espree 來生成 AST 的。

概括來說就是,ESLint 會遍歷前面說到的 AST,然后在遍歷到「不同的節(jié)點」或者「特定的時機」的時候,觸發(fā)相應(yīng)的處理函數(shù),然后在函數(shù)中,可以拋出錯誤,給出提示。

讀取配置

ESLint 首先會從各種配置文件里讀取配置,例如 eslintrc 或者 package.json 中的 eslintConfig 字段中,也可以在使用命令行執(zhí)行 eslint 時指定任意一個配置文件。配置文件里的具體可配置項我們下面再詳細(xì)介紹,這里我們需要注意,

  • ESLint 會先讀取「給定目錄下最近的」一個配置文件。
  • 如果相同目錄下存在多個配置文件,那這層目錄里只有一個配置文件會被讀取,其中 .eslintrc 的優(yōu)先級會高于 package.json 配置。
  • 默認(rèn)會再繼續(xù)向外層文件夾「逐層讀取」配置文件,最終配置合并成一個。
  • 其中如果多個配置文件里都配置了重復(fù)的字段的話,那離給定目錄「最近的配置會生效,」 我們也可以在配置文件中添加 root: true 來阻止 ESLint 逐層讀取配置。

以下是讀取配置的核心代碼:

//?Load?the?config?on?this?directory.
????????try?{
????????????configArray?=?configArrayFactory.loadInDirectory(directoryPath);
????????}?catch?(error)?{
????????????throw?error;
????????}
????????
????????//?這里如果添加了?root?字段將會中斷向外層遍歷的操作
????????if?(configArray.length?>?0?&&?configArray.isRoot())?{
????????????configArray.unshift(...baseConfigArray);
????????????return?this._cacheConfig(directoryPath,?configArray);
????????}

????????//?Load?from?the?ancestors?and?merge?it.
????????const?parentPath?=?path.dirname(directoryPath);
????????const?parentConfigArray?=?parentPath?&&?parentPath?!==?directoryPath
??????????????this._loadConfigInAncestors()
????????????:?baseConfigArray;

????????if?(configArray.length?>?0)?{
????????????configArray.unshift(...parentConfigArray);
????????}?else?{
????????????configArray?=?parentConfigArray;
????????}


const?configFilenames?=?[
?????.eslintrc.js?,
?????.eslintrc.cjs?,
?????.eslintrc.yaml?,
?????.eslintrc.yml?,
?????.eslintrc.json?,
?????.eslintrc?,
?????package.json?
];

loadInDirectory(directoryPath,?{?basePath,?name?}?=?{})?{
????????const?slots?=?internalSlotsMap.get(this);
????????//?這里是以?configFilenames?數(shù)組中元素的順序決定優(yōu)先級的
????????for?(const?filename?of?configFilenames)?{
????????????const?ctx?=?createContext();
????????????if?(fs.existsSync(ctx.filePath)?&&?fs.statSync(ctx.filePath).isFile())?{
????????????????let?configData;
????????????????try?{
????????????????????configData?=?loadConfigFile(ctx.filePath);
????????????????}?catch?(error)?{
????????????????}
????????????????if?(configData)?{
????????????????????return?new?ConfigArray();
????????????????}
????????????}
????????}
????????return?new?ConfigArray();
????}

加載配置

在上述的 configArrayFactory.**loadInDirectory**方法中,ESLint 會依次加載配置里的extends, parser,plugin 等,其中

  • extends 是其他配置文件,秉著盡可能復(fù)用的原則,ESLint 允許我們使用插件中的配置或者是第三方模塊中的配置;
  • parser 用于解析 AST;
  • plugin 則是用戶自定義的插件,可以引入自己定義的規(guī)則,以及對非 js 文件的檢查和處理等。

extends 處理

ESLint 會遞歸地去讀取配置文件中的 extends。那問題來了,如果 extends 的層級很深的話,配置文件里的優(yōu)先級怎么辦????

_loadExtends(extendName,?ctx)?{
????????...
????????return?this._normalizeConfigData(loadConfigFile(ctx.filePath),?ctx);
}

_normalizeConfigData(configData,?ctx)?{
????????const?validator?=?new?ConfigValidator();

????????validator.validateConfigSchema(configData,?ctx.name?||?ctx.filePath);
????????return?this._normalizeObjectConfigData(configData,?ctx);
????}
????
*_normalizeObjectConfigData(configData,?ctx)?{
????????const?{?files,?excludedFiles,?...configBody?}?=?configData;
????????const?criteria?=?OverrideTester.create();
????????const?elements?=?this._normalizeObjectConfigDataBody(configBody,?ctx);
????}

*_normalizeObjectConfigDataBody({extends:?extend},?ctx)?{
????????const?extendList?=?Array.isArray(extend)???extend?:?[extend];
????????...
????????//?Flatten?`extends`.
????????for?(const?extendName?of?extendList.filter(Boolean))?{
????????????yield*?this._loadExtends(extendName,?ctx);
????????}
????????
????????yield?{

????????????//?Debug?information.
????????????type:?ctx.type,
????????????name:?ctx.name,
????????????filePath:?ctx.filePath,

????????????//?Config?data.
????????????criteria:?null,
????????????env,
????????????globals,
????????????ignorePattern,
????????????noInlineConfig,
????????????parser,
????????????parserOptions,
????????????plugins,
????????????processor,
????????????reportUnusedDisableDirectives,
????????????root,
????????????rules,
????????????settings
????????};
????????
}

可以看到,這里是先遞歸處理 extends,完了再返回自己的配置,所以最終得到的 ConfigArray 里的順序則是[配置中的extends,配置]。那這么看的話,自己本身的配置優(yōu)先級怎么還不如extends里的呢?別急,我們繼續(xù)往下看。ConfigArray 類里有一個extractConfig方法,當(dāng)所有配置都讀取完了,最終在使用的時候,都需要調(diào)用extractConfig把一個所有的配置對象合并成一個最終對象。

extractConfig(filePath)?{
????????const?{?cache?}?=?internalSlotsMap.get(this);
????????const?indices?=?getMatchedIndices(this,?filePath);
????????const?cacheKey?=?indices.join(?,?);

????????if?(!cache.has(cacheKey))?{
????????????cache.set(cacheKey,?createConfig(this,?indices));
????????}

????????return?cache.get(cacheKey);
}

function?getMatchedIndices(elements,?filePath)?{
????const?indices?=?[];

????for?(let?i?=?elements.length?-?1;?i?>=?0;?--i)?{
????????const?element?=?elements[i];

????????if?(!element.criteria?||?(filePath?&&?element.criteria.test(filePath)))?{
????????????indices.push(i);
????????}
????}

????return?indices;
}

剛剛我們說了,我們通過之前的操作得到的 ConfigArray 對象里,各個配置對象的順序其實是[{外層配置里的extends配置},{外層配置},{內(nèi)層配置里的extends配置},{內(nèi)層配置}],這看起來跟我們理解的優(yōu)先級是完全相反的,而這里的getMatchedIndices 方法則會把數(shù)組順序調(diào)轉(zhuǎn)過來,這樣一來,整個順序就正常了??。調(diào)整完ConfigArray的順序后,createConfig方法則具體執(zhí)行了合并操作。

function?createConfig(instance,?indices)?{
????const?config?=?new?ExtractedConfig();
????const?ignorePatterns?=?[];

????//?Merge?elements.
????for?(const?index?of?indices)?{
????????const?element?=?instance[index];

????????//?Adopt?the?parser?which?was?found?at?first.
????????if?(!config.parser?&&?element.parser)?{
????????????if?(element.parser.error)?{
????????????????throw?element.parser.error;
????????????}
????????????config.parser?=?element.parser;
????????}

????????//?Adopt?the?processor?which?was?found?at?first.
????????if?(!config.processor?&&?element.processor)?{
????????????config.processor?=?element.processor;
????????}

????????//?Adopt?the?noInlineConfig?which?was?found?at?first.
????????if?(config.noInlineConfig?===?void?0?&&?element.noInlineConfig?!==?void?0)?{
????????????config.noInlineConfig?=?element.noInlineConfig;
????????????config.configNameOfNoInlineConfig?=?element.name;
????????}

????????//?Adopt?the?reportUnusedDisableDirectives?which?was?found?at?first.
????????if?(config.reportUnusedDisableDirectives?===?void?0?&&?element.reportUnusedDisableDirectives?!==?void?0)?{
????????????config.reportUnusedDisableDirectives?=?element.reportUnusedDisableDirectives;
????????}

????????//?Collect?ignorePatterns
????????if?(element.ignorePattern)?{
????????????ignorePatterns.push(element.ignorePattern);
????????}

????????//?Merge?others.
????????mergeWithoutOverwrite(config.env,?element.env);
????????mergeWithoutOverwrite(config.globals,?element.globals);
????????mergeWithoutOverwrite(config.parserOptions,?element.parserOptions);
????????mergeWithoutOverwrite(config.settings,?element.settings);
????????mergePlugins(config.plugins,?element.plugins);
????????mergeRuleConfigs(config.rules,?element.rules);
????}

????//?Create?the?predicate?function?for?ignore?patterns.
????if?(ignorePatterns.length?>?0)?{
????????config.ignores?=?IgnorePattern.createIgnore(ignorePatterns.reverse());
????}

????return?config;
}

這里分析一下具體的合并邏輯

  • 對于 parser 和 processor 字段,后面的配置文件會覆蓋前面的配置文件。
  • 對于 env,globals,parserOptions,settings 字段則會合并在一起,但是這里注意,只有當(dāng)后面的配置里存在前面沒有的字段時,這個字段才會被合并進來,如果前面已經(jīng)有了這個字段,那后面的相同字段會被摒棄。
    • 例如 [{a: 1, b: 2}, {c: 3, b: 4}] 這個數(shù)組的合并結(jié)果則是 {a: 2, b: 2, c: 3}。
  • 對于 rules 字段,同樣是前面的配置優(yōu)先級高于后面的,但是如果某個已存在的 rule 里帶了參數(shù),那么 rule 的參數(shù)會被合并。

extends 處理完后會繼續(xù)處理 parserplugin 字段

parser 和 plugin 處理

這里 parserplugin 都是以第三方模塊的形式加載進來的,因此如果我們要自定義的話,需要先發(fā)包,然后再引用。對于 plugin,通常約定的包名格式是 eslint-plugin-${name} ,而在在配置中可以把包名中的 eslint-plugin 前綴省略。

_loadParser(nameOrPath,?ctx)?{
????????try?{
????????????const?filePath?=?resolver.resolve(nameOrPath,?relativeTo);
????????????return?new?ConfigDependency({
????????????????definition:?require(filePath),
????????????????...
????????????});
????????}?catch?(error)?{
????????????//?If?the?parser?name?is??espree?,?load?the?espree?of?ESLint.
????????????if?(nameOrPath?===??espree?)?{
????????????????debug(?Fallback?espree.?);
????????????????return?new?ConfigDependency({
????????????????????definition:?require(?espree?),
????????????????????...
????????????????});
????????????}
????????????return?new?ConfigDependency({
????????????????error,
????????????????id:?nameOrPath,
????????????????importerName:?ctx.name,
????????????????importerPath:?ctx.filePath
????????????});
????????}
????}
????
????_loadPlugin(name,?ctx)?{
????????const?request?=?naming.normalizePackageName(name,??eslint-plugin?);
????????const?id?=?naming.getShorthandName(request,??eslint-plugin?);
????????const?relativeTo?=?path.join(ctx.pluginBasePath,??__placeholder__.js?);

????????//?Check?for?additional?pool.
????????//?如果已有的?plugin?則復(fù)用
????????const?plugin?=
????????????additionalPluginPool.get(request)?||
????????????additionalPluginPool.get(id);

????????if?(plugin)?{
????????????return?new?ConfigDependency({
????????????????definition:?normalizePlugin(plugin),
????????????????filePath:???,?//?It's?unknown?where?the?plugin?came?from.
????????????????id,
????????????????importerName:?ctx.name,
????????????????importerPath:?ctx.filePath
????????????});
????????}

????????let?filePath;
????????let?error;
????????filePath?=?resolver.resolve(request,?relativeTo);

????????if?(filePath)?{
????????????try?{
????????????????const?startTime?=?Date.now();
????????????????const?pluginDefinition?=?require(filePath);
????????????????return?new?ConfigDependency({...});
????????????}?catch?(loadError)?{
????????????????error?=?loadError;
????????????}
????????}
????}

加載流程總結(jié)

整個加載配置涉及到多層文件夾的多個配置文件,甚至包括配置文件里的extends ,這里以一張流程圖來總結(jié)一下

檢驗

經(jīng)過前面的步驟之后,基本上我們已經(jīng)獲取了所有需要的配置,接下來就會進入檢驗流程,主要對應(yīng)源碼中的 Lint 類的 verify 方法。這個 verify 方法里主要也就是做一些判斷然后分流到其他處理方法里。

verify(textOrSourceCode,?config,?filenameOrOptions)?{
????????const?{?configType?}?=?internalSlotsMap.get(this);
????????if?(config)?{
????????????if?(configType?===??flat?)?{
????????????????let?configArray?=?config;
????????????????if?(!Array.isArray(config)?||?typeof?config.getConfig?!==??function?)?{
????????????????????configArray?=?new?FlatConfigArray(config);
????????????????????configArray.normalizeSync();
????????????????}
????????????????return?this._distinguishSuppressedMessages(this._verifyWithFlatConfigArray(textOrSourceCode,?configArray,?options,?true));
????????????}

????????????if?(typeof?config.extractConfig?===??function?)?{
????????????????return?this._distinguishSuppressedMessages(this._verifyWithConfigArray(textOrSourceCode,?config,?options));
????????????}
????????}
????????if?(options.preprocess?||?options.postprocess)?{
????????????return?this._distinguishSuppressedMessages(this._verifyWithProcessor(textOrSourceCode,?config,?options));
????????}
????????return?this._distinguishSuppressedMessages(this._verifyWithoutProcessors(textOrSourceCode,?config,?options));
????}

不管是哪個分支,他們大致都按照以下順序執(zhí)行:

  • ??先處理 processor。
  • ????解析代碼,獲取 AST 和節(jié)點數(shù)組。
  • ??????跑規(guī)則runRules。

下面我們對上面三個過程逐個介紹。

processor

processor 是在插件上定義的處理器,processor 能針對特定后綴的文件定義 preprocess 和 postprocess 兩個方法。其中 preprocess 方法能接受文件源碼和文件名作為參數(shù),并返回一個數(shù)組,且數(shù)組中的每一項就是需要被 ESLint 檢驗的代碼或者文件;通常我們使用 preprocess 從非 js 文件里提取出需要被檢驗的部分 js 代碼,使得非 js 文件也可以被 ESLint 檢驗。而 postprocess 則是可以在文件被檢驗完之后對所有的 lint problem 進行統(tǒng)一處理(過濾或者額外的處理)的。

獲取 AST

當(dāng)用戶沒有指定 parser 時,默認(rèn)使用 espree,若有指定 parser 則使用指定的 parser。

????????let?parser?=?espree;
????????if?(typeof?config.parser?===??object??&&?config.parser?!==?null)?{
????????????parserName?=?config.parser.filePath;
????????????parser?=?config.parser.definition;
????????}?else?if?(typeof?config.parser?===??string?)?{
????????????if?(!slots.parserMap.has(config.parser))?{
????????????????return?[{
????????????????????ruleId:?null,
????????????????????fatal:?true,
????????????????????severity:?2,
????????????????????message:?`Configured?parser?'${config.parser}'?was?not?found.`,
????????????????????line:?0,
????????????????????column:?0
????????????????}];
????????????}
????????????parserName?=?config.parser;
????????????parser?=?slots.parserMap.get(config.parser);
????????}
????????
????????const?parseResult?=?parse(
????????????????text,
????????????????languageOptions,
????????????????options.filename
????????????);

這里推薦一個網(wǎng)站https://astexplorer.net/,它能方便讓我們查看一段代碼轉(zhuǎn)化出來的 AST 長什么樣

runRules

正如我們前面說到的,規(guī)則是 ESLint 的核心,ESLint 的工作全是基于一條一條的規(guī)則,ESLint 是怎么處理規(guī)則的,核心就在 runRules 這個函數(shù)中。首先會定義nodeQueue數(shù)組,用于收集 AST 所有的節(jié)點。注意每個 AST 節(jié)點都會被推進數(shù)組中兩次(進一次出一次)。

Traverser.traverse(sourceCode.ast,?{
????????enter(node,?parent)?{
????????????node.parent?=?parent;
????????????nodeQueue.push({?isEntering:?true,?node?});
????????},
????????leave(node)?{
????????????nodeQueue.push({?isEntering:?false,?node?});
????????},
????????visitorKeys:?sourceCode.visitorKeys
????});

然后就會遍歷所有配置中的 rule,并通過 rule 的名稱找到對應(yīng)的 rule 對象,注意,這里的兩個 rule 不完全一樣?!概渲弥械?rule」指的是在 eslintrc 等配置文件中的 rules 字段下的每個 rule 名稱,例如下面這些??

「rule 對象」則指的是 rule 的具體定義,簡單來說就是定義了某個 rule 的基本信息以及它的檢查邏輯,甚至是修復(fù)邏輯,我們在之后的 ESLint 實戰(zhàn)介紹中會具體講解它??傊?,這里每個被遍歷到的 rule 對象,ESLint 會為 rule 對象里的「AST 節(jié)點」添加相應(yīng)的監(jiān)聽函數(shù)。以便在后面遍歷 AST 節(jié)點時可以觸發(fā)相應(yīng)的處理函數(shù)。

//?這里的?ruleListeners?就是{[AST節(jié)點]:?對應(yīng)的處理函數(shù)}鍵值對
Object.keys(ruleListeners).forEach(selector?=>?{
????????????const?ruleListener?=?timing.enabled
??????????????????timing.time(ruleId,?ruleListeners[selector])
????????????????:?ruleListeners[selector];

????????????emitter.on(
????????????????selector,
????????????????addRuleErrorHandler(ruleListener)
????????????);
????????});

為所有的 rule 對象添加好了監(jiān)聽之后,就開始遍歷前面收集好的nodeQueue,在遍歷到的不同節(jié)點時相應(yīng)觸發(fā)節(jié)點監(jiān)聽函數(shù),然后在監(jiān)聽函數(shù)中調(diào)用方法收集所有的的 eslint 問題。

nodeQueue.forEach(traversalInfo?=>?{
????????currentNode?=?traversalInfo.node;

????????try?{
????????????if?(traversalInfo.isEntering)?{
????????????????eventGenerator.enterNode(currentNode);
????????????}?else?{
????????????????eventGenerator.leaveNode(currentNode);
????????????}
????????}?catch?(err)?{
????????????err.currentNode?=?currentNode;
????????????throw?err;
????????}
????});

applyDisableDirectives

我們已經(jīng)獲取到所有的 lint 問題了,接下來會處理注釋里的命令,沒錯,相信大家都不陌生,就是 eslint-disable、eslint-disable-line 等,主要就是對前面的處理結(jié)果過濾一下,另外還要處理沒被用到的命令注釋等。

修復(fù)

接下來就是修復(fù)過程了,這里主要調(diào)用SourceCodeFixer類的applyFixes方法,而這個方法里,有調(diào)用了 attemptFix 來執(zhí)行修復(fù)操作。這里的 problem.fix實際上是一個對象,這個對象描述了修復(fù)的命令,類型是這樣的,{range: Number[]; text: string} 。這里我們只需要知道他是由規(guī)則的開發(fā)者定義的fix函數(shù)中返回的對象,所以這個對象描述的修復(fù)命令都由規(guī)則開發(fā)者決定。細(xì)節(jié)的我們將在之后的實戰(zhàn)篇里講解,這里不再展開。

    /**
* Try to use the 'fix' from a problem.
* @param {Message} problem The message object to apply fixes from
* @returns {boolean} Whether fix was successfully applied
*/
function attemptFix(problem) {
const fix = problem.fix;
const start = fix.range[0];
const end = fix.range[1];

// Remain it as a problem if it's overlapped or it's a negative range
if (lastPos >= start || start > end) {
remainingMessages.push(problem);
return false;
}

// Remove BOM.
if ((start < 0 && end >= 0) || (start === 0 && fix.text.startsWith(BOM))) {
output = ;
}

// Make output to this fix.
output += text.slice(Math.max(0, lastPos), Math.max(0, start));
output += fix.text;
lastPos = end;
return true;
}

至此,ESLint 工作的大致流程就已經(jīng)介紹完了,下面以一張圖來總結(jié)一下整個流程:


瀏覽 32
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報
評論
圖片
表情
推薦
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 国产三级网| 色色热| 久操av在线| 在线三级av| 女孩自慰在线观看| 99热网站| 日本性爱一区| caopeng97| 精品视频999| 欧美黄片无码| 在线观看AV91| 久久久久久网| 一级AA毛片| 激情综合网站| 国产一区二区不卡亚洲涩情| 91人妻无码精品| 亚洲在线| 黄色精品视频| 最好看的MV中文字幕国语| www.99国产| 大地av| 亚洲精品少妇| 香蕉网站操逼片| www.狠狠操| 久久99精品久久久久久| 无码区一区二区三区| 日韩色情在线| 全国男人的天堂网站| 亚洲激情视频网站| 蜜桃精品一区二区三区美女| 日本免费在线| 午夜精品18视频国产17c| 99成人免费视频| 俺来俺去www色官网| 99视频+国产日韩欧美| 中文无码人妻少妇| 久草在线| 无码国产精品一区二区免费式直播| 操少妇视频| 五月丁香六月婷婷综合| 国产成人精品久久| 免费的黄色视频在线观看| 日韩极品视频在线| 水果派成人播放无码| 在线观看高清无码| 小h片在线观看| 欧美97| 玖玖99视频| 一本无码中文字幕| 亚洲综合视频网| 亚洲三级视频在线播出| 国产伦子伦一级A片免费看老牛| 激情男人网| 水蜜桃视频网站| 影音先锋成人资源站| 午夜视频在线播放| 久久AV秘一区二区三区水生| 中文无码字幕在线| 熟女人妻在线| 99久久99久久兔费精桃| 亚欧精品久久久| 超碰色| 爱五月| 国产人妻人伦精品一区| 久久日韩操| 中文字幕免费在线观看| 国产成人精品AV在线观| 无码国产精品一区二区免费式直播| 人妻字幕| 少妇综合网| 日韩久久视频| 青青青草视频在线观看| AV小说在线观看| 欧美囗交大荫蒂免费| 一本加勒比HEZYO东京热无码| 老汉av| 中国12一13毛片| 青娱乐国产精品一区二区| 一级a片在线免费观看| 伊人天天色| 国产精品视频一区二区三| av资源在线| 国产v片| 东京热高清无码| 強姦婬片A片AAA毛片Mⅴ| 高清无码视频在线免费观看| 日韩中文字幕有码| 中文字幕第二页| 成人AV中文解说水果派| 狠狠干狠狠色| www男人的天堂| HEZ-502搭讪绝品人妻系列 | 婷婷五月精品中文字幕| 99精品在线免费观看| 天天日夜夜艹| www.国产精品| 99久久久久久| 无码视频观看| 夜夜骑天天操| 91AV在线看| 岛国无码在线| 成人无码区免费AV毛片| 六月婷婷综合| 99热精品在线播放| 日本黄色视频免费观看| 中文字幕人妻日韩在线| 成人无码区免费AV毛片| 日韩AV电影在线观看| www.sese| 九九大香蕉| 久久免费观看视频| 超碰AV在线| 高清无码在线看| 天天日综合| 麻豆乱婬一区二区三区| 色交视频| 色就是色欧美成人网| 成人福利影视| 免费看一级高潮毛片| 日产久久久| 国产人与禽zoz0性伦| 免费在线性爱视频| 日韩AV免费网站| 亚洲精品视频在线观看免费 | 日韩欧美在线中文字幕| 丰满人妻一区二区三区四区54| 成人小视频在线| 人人操超碰在线| 亚洲日韩精品在线观看| 一级黄色电影免费观看| 久久永久视频| 日韩高清无码不卡| 成人影音先锋| 国产福利网| 久久久久久亚洲| 日本黄色电影网站| 国产在线观看欧美| 久久久精品久久| 高清人妻无码| 69乱伦视频| 欧美在线视频a| 激情无码精品| 亚洲天堂网在线视频| 微熟女导航| 日韩一区二区免费视频| 99热66| 麻豆乱码国产一区二区三区| 国产av大全| 国产婷婷久久Av免费高清| 亚洲精品成人片在线观看精品字幕| 五月色视频| 日韩激情视频在线观看| www.豆花社区成人| 欧美国产综合在线| 欧美AⅤ在线| 中文字幕无码日韩| 欧美成人三级在线观看| 亚洲免费性爱视频| 欧美精品一区二区三区使用方法| 青榴视频免费观看| 久久久久久久久久久久久久久久久久免费精品分类视频 | 色色网欧美| 无码精品一区二区三区在线观看| 黄色搞逼视频| 91久久久久久久久久久久18| 粉嫩小泬BBBBBB免费看| 久久久久久婷婷| 九色国产在线| 免费在线黄色电影| 99视频精品| 尤物一区二区| 大香蕉大香蕉免费网| 黄色一级片在线| 亚洲色无码| 无码白浆| 日本中文字幕电影| 中文字幕av在线播放| 成人电影aaa| 欧美大胆视频| 亚洲天堂中文| 国产av一区二区三区四区| 国产亚洲精品码| 中文字幕第二页| 爱爱动态图| 成人电影aaa| 人妻懂色av粉嫩av浪潮av| 免费观看黄色AV| 天堂网亚洲| 91丨九色丨老农村| 日韩操操| 一区二区三区四区无码| 呦呦av| 亚洲人人18XXX—20HD| 乱伦小视频| 五月丁香性爱| 中文字幕乱码人妻二区三区| 国产高清AV| 一区二区高清无码视频| 免费一级网站| wwwa片| 国产逼| 亚洲AAA| 日本不卡在线观看| 精品免费在线| 亚洲无码1区| 亚洲欧美视频在线| 中文字字幕在线中文乱码更新时间| 五月丁香花婷婷| 国产精品国产三级国产AⅤ| 91香蕉视频| 佐山爱人妻无码蜜桃| 人妻黄色| 中文不卡在线| 中文字幕黄色电影| 婷婷色图| 毛片av在线| 亚洲无码中文字幕在线| 特级西西444www精品视频| 玩弄人妻少妇500系列视频| www.天天干| 成人黄网站免费观看| 嫩BBB槡BBBB槡BBBB百度| 亚洲一级免费视频| 免费看一级无码成人片| 99极品视频| 玖玖精品视频| 免费在线观看黄色片| 日韩有码第一页| 永井玛丽亚av无码中出流出| 日韩一级免费观看| 成人无码免费一区二区中文 | 日日摸日日添日日躁AV| 成人无码免费看| 亚洲成人无码AV| 懂色中文字幕| 国产对白视频| 欧美第二页| 五十路在线视频| aaa在线免费视频| 色五月电影| 中文字幕精品人妻| 搞搞视频| 在线成人视频网站大香蕉在线网站 | 婷婷99狠狠躁天天| 东北毛片| 国产精品久久久91| 国产嘿嘿| 欧美日韩日逼| 国产三级在线| 国产乱色精品成人免费视频| 久草毛片| 特级西西西西4444级酉西88wwww特| 欧美日韩精品一区二区三区| 无码9999| 翔田千里一区二区三区| 日韩无码网站| 精品无码9| 亚洲av毛片| 98色色| 91蝌蚪视频在线| 亚洲国产成人在线视频| 91操B| 亚洲熟女一区二区三区妖精 | 国产超碰在线| 色婷婷Av一区| 熟女人妻视频| 国产福利网| 鸭子av| aaaaaa在线观看免费高清| 欧美一级婬片A片免费软件| 中文字幕乱伦日本| 亚洲日韩精品无码| 亚洲熟女一区| 国产精品HongKong麻豆| 亚洲另类图片小说| 成人毛片| 好吊视频一区二区三区红桃视频you | A在线观看| 日本一区二区三区四区在线观看| 国产毛片毛片毛片毛片毛片| 无码网| 欧美三级性爱视频| 日本黄色电影在线| A在线观看| 另类激情网| 最近中文字幕在线| 高清一区二区| 2021天天夜日| 日韩无码动漫| 囯产精品久久久久久久久久久久久久 | 97国产在线| 亚洲福利在线免费观看| 热99视频| 国产精品一麻了麻了| 99爱爱视频| 靠逼久久| 日韩插插| 精品无码一区二区Av蜜桃| 国产做受91一片二片老头| 亚洲一区二区三区免费视频| 欧美亚洲动漫| 人人操超碰| 91插插插插| 豆花视频成人精品视频| 亚洲人妻免费视频| 中文无码熟妇人妻| 免费黄色| 2021天天夜日| 91成人一区二区三区| 18+免费网站| 亚洲免费观看高清完整版| 五月婷婷影院| av无码av天天av天天爽| 久久99久久99久久99人受| 成人黄色网址| 琪琪av| 香蕉成人网| 欧美成a| 无码福利电影| 亚洲欧美日韩在线| 午夜精东影业传媒在线观看| 东京热久久综合色五月老师| 91嫩操| 成人黄色电影在线观看| 国产一区二区不卡视频| 日本不卡视频在线| 夜色福利网| 亚洲性爱视频| 超碰免费在线| 久久久久久五月天| 日本www色| 黄色视频在线观看| 亚洲AV激情无码专区在线播放 | 日韩中文字幕av在线| 青青草成人网| 中文字幕乱在线| 北条麻妃在线观看| 99久久久国产精品免费蜜臀| 日韩AⅤ| 韩国中文字幕HD久久| 成人免费精品视频| 亚洲人妻av| 成人免费网站在线观看| 97久久精品国产熟妇高清网| 久久久国产AV| 永久免费视频| 欧美日韩国产成人在线观看| av资源网站| 国产AAA片| 福利三区| 99精品视频北条麻妃国产版 | 99综合网| AV资源在线免费观看| 欧美黄色小视频| 国产成人久久777777| 国产女人18毛片精品18水| 久久久久国产精品视频| 粉嫩小泬BBBBBB免费| 青草网在线观看| 久久久久久毛片| 亚洲日操| 国产精品高潮无套内谢| 人妻超碰在线| 中文字幕在线国产| 国产欧美二区综合中文字幕精品一 | 亚洲精品国产精品国自产在线| 91黄网站在线观看| 色婷婷视频在线| 日韩综合精品| 黄色免费大片| 蜜臀精品色无码蜜臀AV| 男女午夜福利| av性爱在线| 国产性色| 青青草免费在线观看| 蜜桃Av噜噜一区二区三区| 国产一级a毛一级a毛观看视频网站www.jn| 91精品电影18| 强伦轩一区二区三区在线观看| 伊人成人在线观看| 丁香五月中文| 亚洲免费网站| 丁香五月激情啪啪啪| 肉片无遮挡一区二区三区免费观看视频 | 迷奸91| 亚洲免费观看高清完整版在va线观看| 久久久久久久久久久久久自慰小片| AV网站免费在线观看| 日韩美毛片| 波多野结衣av中文字幕| 激情小视频在线| 97人妻在线视频| 午夜免费福利视频| 麻豆专区| 午夜亚洲无码| 欧美精产国品一二三区| 麻豆视频一区二区三区| 欧美激情网站| 先锋资源在线视频| 黄色一级爱爱| 噜噜噜av| 极品久久久久| 婷婷天堂站| 狠狠干狠狠艹| 亚洲福利视频电影精| 国产三级小视频| 亚洲精品三级在线观看| 成人三级片免费| 黑人在线播放| 97人妻精品一区二区三区| 免费视频99| 国产又爽又黄免费视频网站| 黄色在线免费观看网站| 亚洲AV无码日韩AV无码导航| 成人三级片免费| 婷婷五月综合激情| 特级西西444WWW大精品视频 | 六月丁香五月天| 深爱五月激情网| 97免费在线视频| 国产欧美日韩在线观看| 久久人妻精品| 久久久精品黄色网址| 东京热在线视频观看| 日日夜夜草| 久久亚洲成人| 激情无码精品| 人妻少妇偷人精品无码免费| 热99re69精品8在线播放| 免费无码婬片AAAAA片| 国产成人精品免费视频| 国产黄色小视频在线观看| 2016av天堂网| 亚洲综合天堂| 国精品无码人妻一区二区三区| 九九九免费视频| 伊人精品A片一区二区三区| 丁香五月天色婷婷| 日韩无码A片| 日韩精品一区二区亚洲AV观看| 国产精品国产精品国产| 91人妻人人澡人人澡人人精品| 日本欧美成人片AAAA| 国产123区| 亚洲无码蜜桃| 黄片视频链接| 人人操人人摸人人| 亚洲无码高清在线视频| 无码精品黄色片| 国产精品黄色视频| 国产精品久久久久无码AV| 日本操逼视频| 亚洲欧美日韩电影| 精品在线播放视频| 国产精品不卡| 国产熟妇毛多久久久久一区| 骚骚肥肥一区二区三区| 亚洲网站在线观看| 91视频人人| 艹在线观看| 91无码精品久久久一区第1集| 奇米影视狠狠干| 久草电影网站| 天天干夜夜爽| 国产精品乱子伦视频一区二区| 北条麻妃九九九在线视频| 乱伦五月天| 怡红院综合网| 久久久久久久久久久久高清毛片一级 | 久久久精品午夜人成欧洲亚洲韩国| 强开小嫩苞毛片一二三区| 日韩成人综合| 精品尤物| 国产精品免费一区二区三区四区视频| 日韩在线视频观看| 成人短视频在线观看| 免费国产乱伦| 嘿咻无码推油| 天美精东蜜桃91| 麻豆成人精品| 亚洲激情偷拍| 中文无码熟妇人妻AV在线| 91无码秘蜜桃一区二区三区-百度| 大香蕉亚洲网| 无码一区二区三区四区五区| 小泬BBBBBB免费看| 波多野结衣AV在线观看| 狼友视频第二页| 特黄aaaaaaaa真人毛片| 亚洲成人免费在线| 大香蕉伊人免费| 亚洲日韩中文字幕| 无码日韩视频| 亚洲第一成人久久网站| 四虎国产精品成人久久| 精品国产乱码久久久久久郑州公司| 天堂网亚洲| 国产精品一级A片| 中文字幕无码人妻| 中文无码一区二区三区| 屁屁影院CCYYCOM国产| 中文无码日韩| 亚洲高清在线播放| 黄色电影中文字幕| 丁香婷婷五月基地| 伊人三区| 欧美午夜福利视频| 亚洲综合影院| av在线三级| 国产成人精品无码片区在线观91| 婷婷日韩一区二区三区| 午夜福利1000| 波多野结衣AV在线观看| 偷拍九九热| 欧美你懂的| 台湾一区二区| 国产一区一区| 一级片三级片| 午夜成人福利| 无码一二三| 九九综合网| 免费观看黄色在线视频| www.a日逼| 久久综合久久鬼| 欧美精产国品一二三产品价格 | 亚洲免费观看A∨中文| 国产高清自拍视频| 欧美黄色精品| 高清毛片AAAAAAAAA郊外| 亚洲一区| 99久久99九九99九九九| 欧美一级久久| 99精品视频在线免费观看| 久久Av电影| 三级片中文字幕| 日韩精品久久久久久久酒店| 在线观看视频国产| 九九热日本| 色秘乱码一区二区三区| 亚洲国产婷婷| 最新国产视频| 中文字幕电影| 五月丁香色色| 在线综合国产欧美| 免费在线黄色电影| 东京热小视频| 天天干天天射天天爽| 免费v在线观看| 无码人妻日本| 免费无码高清视频| 亚洲成人AV在线观看| 久久91精品| 国产白嫩精品久久久久久| 91视频美女内射| 国产性色| 无码四区| 五月天婷婷色色| 伊人大香蕉视频在线观看| 狼友视频免费| 无码一区精品久久久成人| 亚洲精品免费视频| 乖我硬了让老子cao你小视频| 四虎操逼| www.色色网| 丰满人妻一区二区三区Av猛交| 婷婷五月天在线观看| 你懂的在线播放| 一级无码视频| 一本大道东京热AV| 艹逼视频| 国产AV一卡| 蜜臀激情| 一级av| 欧美亚洲成人在线| 国产日韩欧美在线播放| 欧美性猛交XXXX乱大交HD| 污视频网站免费观看| 国产成人三级在线| 在线观看日韩三级片av| 天天综合91| 天天人人精品| 国产AV无码成人精品区| 日日摸夜夜| 国产亚洲精品久久久波多野结衣| TokyoKot大交乱无码| 天天操天天操| 亚洲精品自拍| 中文字幕AV无码| 亚洲天堂在线观看免费| 国产精品一区二区三区在线| 九久热| 激情开心站| 日韩大屌操| 91成人情欲影视网| 露脸丨91丨九色露脸| 人善交精品一区二区三区| 黄色直播在线观看| 日本成人网址| 刘玥精品国产一区二区三区| 精品无码9| 无码做爰欢H肉动漫网站在线看 | 永久免费叼嘿| 九九色色| 久久精品国产亚洲AV麻豆痴男| 99视频精品| 中文字幕伊人| 国产精品成人免费精品自在线观看| 中文无码日韩欧美久久| 国产夫妻在线视频| 51AV在线| 竹菊传媒一区二区三区| 欧美操逼操| 黄色毛片在线播放| 在线天堂9| 久久久久国产一区二区三区四区| 操B网站| 伊人亚洲| 久久草在线观看| 久久久777| 亚洲精品成人视频| 色网站在线观看| 日韩精品一二区| 99色在线视频| 一区二区三区久久久久| 五月婷在线视频| AV天堂偷拍亂伦| 日逼老女人| 无码A级| 国产又黄又大又粗| 国产三级性爱| 国产无码AV大片| 国产精品黄色| 在线不卡视频| 中文字幕视频在线| 这里都是精品| 噜噜色色噜噜| 国产色无码网站www色视频| 中文字幕在线观看网址最新地址| 任我操在线视频| 9l农村站街老熟女| 91肏屄视频| 伊人综合大香蕉| 日本电影一区二区| 久久婷婷婬片A片AAA| 天天日天天色天天干| 美女裸身18禁| 蜜桃91精品| 精品无码AV一区二区三区| 蜜臀av网站| 国产91丝袜在线播放| 高清免费在线中文Av| 西西人体BBBBBB| 人人操人人爱人人摸| 成人国产AV网站| 91在线看18| 91中文| 抽插逼| 蜜芽人妻在线| 91蝌蚪视频在线| 亚洲成人a片| 欧美成人性色欲影院| 青青草手机视频在线| 一级做a视频| 污视频在线免费| 亚洲国产成人精品午夜| 亚洲人人妻| 水蜜桃视频网站在线观看| 亚洲午夜精品久久久久久APP| 亚洲国产视频在线观看| 亚洲精品天堂无码| 欧美精品一区二区三区蜜臀| 91丨豆花丨国产极品| 1024手机在线观看| 午夜AV福利影院| 欧美日韩国| 日本三级片中文字幕| 91精品国产综合久久久蜜臀粉嫩| 国产99久久久精品| 青娱乐A片| 亚洲福利社| 久久久久亚洲AV无码成人片 | 中文字幕偷拍| 精品无码一区二区三| 久久av电影| 久久精品人妻| 青青草原av| 偷窥美鲍| 欧美色欲| 天天噜天天操| 欧美后门菊门交4| 大香蕉尹人在线视频| 欧一美一婬一伦一区二区三区自慰国| 少妇高潮一区二区三区99| 美女高潮网站| 亚洲免费黄色片| 亚洲伊人大香蕉| 婷婷国产视频| 成人A片免费观看| 婷婷爱五月| 亚洲国产免费| 91av视频在线| 欧美日在线| 日本不卡在线| 中文字幕++中文字幕明步| 日本一节片在线播放| 欧美色一级| 欧美精品国产动漫| 亚洲香蕉视频网站| 亚洲男人av| 91一二区| 国产高潮白浆喷| 久久久久久综合| 亚洲AV秘无码苍井空| 残忍另类BBWBBWBBW| 久久久久成人片免费观看蜜芽| 国产精品色哟哟| 先锋影音资源站| 中文字幕午夜福利| jizz免费视频| 无码中文字| 国产激情无码免费| 影音先锋亚洲AV| 久久国产精品一区二区三区| 久久丝袜视频| 中文字幕免费在线视频| 黃色一级A片一級片| 麻豆成人91精品二区三区| 五丁香在线观看AV| 综合激情五月婷婷| 日韩AV手机在线观看| 91.xxxx| h片在线免费观看视频| 九九国产| av无码一区| 亚洲97| 一区二区三区四区在线| 三级理论片| 色久影院| 黄色A级视频| 无码入口| 欧美一区二区三区免费| 欧美97| 少妇免费视频| 亚洲第一中文字幕网| 天堂无码视频在线播放| 五月天久久综合| 天天日天天色天天干| 大香蕉96| 亚洲无码一级片| 国产精品无码永久免费不卡| 操逼A片| 欧美A视频在线观看| 日本AⅤ中文字幕| 4438成人网站| 嫩BBB| 亚洲欧美国产日韩字幕| 91在线无码精品秘入口动作| 亚洲精品乱码久久久久| 欧洲毛片基地c区| 一区二区国产视频| 91人人精品| 自拍偷拍av| 青春草在线观看视频| 黄片网站免费| 91视频爱爱| 国产一级AA片| 成人免费视频在线| 国产成人无码AⅤ片免费播放| 国产91福利| 日本不卡一区| 人人看人人搞人人摸| 日韩中文字幕一区二区| 超碰天堂| 天天日天天撸| 香蕉伊人在线| 色色天堂成人电影| 大香蕉在线99| 免费在线亚洲| AV在线一区二区三区| 国产一级片在线播放| 草逼com| 开心激情播播网| 欧美影院亚洲| 青青草精品在线视频| www.91在线| 亚洲无线观看| 国产成人精品a区在线观看| 免费观看黄片网站| 久草黄色| 日韩无码免费播放| 丁香激情五月天| 亚洲成人怡红院| 午夜传媒一区二区三区| 大香蕉在线75| 青青草亚洲| 免费操逼网站| 日韩无码123区| 亚洲成人在线观看视频| 色噜噜AV| 中文字幕久久无码| 亚洲无码视频在线观看高清 | 亚洲黄在线观看| 免费看一区二区三区| 中文字幕成人在线| 黄色视频网站日本| 成人视频A片| 依人综合网| 爱爱无码| 国产精品超碰| 息子交尾一区二区三区| 青娱乐在线视频精品| 亚洲有码在线观看| 免费中文资源在线观看| 噼里啪啦免费观看视频大全| 男人天堂新地址| 国产精品人妻AⅤ在线看| 97人人艹| 未满十八18禁止免费无码网站| 欧美日韩一区二区三区四区| 操日本逼| 国产AV影院| 老女人操屄| 免费成人一级片| 亚洲免费视频播放| 亚洲精品综合| 夜夜操狠狠操| 亚洲一区二区三区在线视频| 大鸡巴视频在线观看| 亚洲福利女神成人福利| 国产精品无| 国产学生妹| 日本无码视频在线观看| 国产午夜91人妻| 97超碰人人摸| 欧美一级黄色大片| www国产亚洲精品久久网站 | 亚洲一区中文字幕成人在线| 超碰97成人| 国产精品秘久久久久久1-~/\v7-/ 囯产精品一区二区三区线一牛影视1 | 国产成人精品在线| 天天噜噜色| 成人视频A片| 国产h视频| 成人网站在线看| 国产一区二区三区四区五区六区七区| 男女黄网站| 日韩免费一区| 手机毛片在线播放| 久久国产精品99久久人人澡| 无码人妻中文字幕| 五月天黄色电影| 国产在线接入| 国产99久久九九精品无码免费| 操中国老女人| 影音先锋女人aV鲁色资源网站| 嫩草久久99www亚洲红桃| 久久性爱网| 51XX嘿嘿午夜| 亚洲天堂国产| 综合夜夜| 女人特级毛片18| 欧美日韩日逼视频| 臭小子晚上让你爽个够视频| 欧美一级黄色片| 国产视频精品一区二区三区| 撸久久| 波多野结衣黄色| 囯产精品久久| 丁香欧美| 久久一区二区三区四区| 免费人成视频观看| 特级婬片AAAAAAA级| 亚洲人妻av| 久久国产一区二区| 亚洲秘一区二区三区-精品亚洲二区- | 亚洲成人一区二区在线观看| 四虎成人在线| 国产熟妇婬乱A片免费看牛牛| 天堂网址激情网址| 好逼天天有| h网站在线| 天天躁狠狠躁夜躁2024| 水蜜桃成人网| 波多野结衣无码电影| 日韩无码人妻一区二区三区| 亚洲A∨无码无在线观看| 麻豆国产精品一区| 亚洲va在线∨a天堂va欧美va| 久久久成人精品| 军人妓女院BD高清片在线播放| 国产免费一区二区三区四区| 亚洲无码专区视频| 国产美女精品| AA免费视频| 亚洲乱伦电影| 91嫩草欧美久久久九九九| 欧美色图自拍| 欧美日韩第一页| 国产无套在线观看| 91网站在线观看视频|