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

Vue 發(fā)布新版腳手架工具,300 行代碼輕盈新生!

開發(fā) 前端
本文就是通過調(diào)試和大家一起學(xué)習(xí)這個300余行的源碼。一起來看看吧。

[[433823]]

 1. 前言

美國時間 2021 年 10 月 7 日早晨,Vue 團(tuán)隊等主要貢獻(xiàn)者舉辦了一個 Vue Contributor Days 在線會議,蔣豪群[1](知乎胖茶[2],Vue.js 官方團(tuán)隊成員,Vue-CLI 核心開發(fā)),在會上公開了create-vue[3],一個全新的腳手架工具。

create-vue使用npm init vue@next一行命令,就能快如閃電般初始化好基于vite的Vue3項目。

本文就是通過調(diào)試和大家一起學(xué)習(xí)這個300余行的源碼。

閱讀本文,你將學(xué)到:

1. 學(xué)會全新的官方腳手架工具 create-vue 的使用和原理

2. 學(xué)會使用 VSCode 直接打開 github 項目

3. 學(xué)會使用測試用例調(diào)試源碼

4. 學(xué)以致用,為公司初始化項目寫腳手架工具。

5. 等等

2. 使用 npm init vue@next 初始化 vue3 項目

create-vue github README[4]上寫著,An easy way to start a Vue project。一種簡單的初始化vue項目的方式。

  1. npm init vue@next 

估計大多數(shù)讀者,第一反應(yīng)是這樣竟然也可以,這么簡單快捷?

忍不住想動手在控制臺輸出命令,我在終端試過,見下圖。

npm init vue@next

最終cd vue3-project、npm install 、npm run dev打開頁面http://localhost:3000[5]

初始化頁面

2.1 npm init && npx

為啥 npm init 也可以直接初始化一個項目,帶著疑問,我們翻看 npm 文檔。

npm init[6]

npm init 用法: 

  1. npm init [--force|-f|--yes|-y|--scope]  
  2. npm init <@scope> (same as `npx <@scope>/create`)  
  3. npm init [<@scope>/]<name> (same as `npx [<@scope>/]create-<name>`) 

npm init <initializer> 時轉(zhuǎn)換成npx命令:

  •  npm init foo -> npx create-foo
  •  npm init @usr/foo -> npx @usr/create-foo
  •  npm init @usr -> npx @usr/create

看完文檔,我們也就理解了: 

  1. # 運(yùn)行  
  2. npm init vue@next  
  3. # 相當(dāng)于  
  4. npx create-vue@next 

我們可以在這里create-vue[7],找到一些信息。或者在npm create-vue[8]找到版本等信息。

其中@next是指定版本,通過npm dist-tag ls create-vue命令可以看出,next版本目前對應(yīng)的是3.0.0-beta.6。 

  1. npm dist-tag ls create-vue  
  2. - latest: 3.0.0-beta.6  
  3. - next: 3.0.0-beta.6 

發(fā)布時 npm publish --tag next 這種寫法指定 tag。默認(rèn)標(biāo)簽是latest。

可能有讀者對 npx 不熟悉,這時找到阮一峰老師博客 npx 介紹[9]、nodejs.cn npx[10]

npx 是一個非常強(qiáng)大的命令,從 npm 的 5.2 版本(發(fā)布于 2017 年 7 月)開始可用。

簡單說下容易忽略且常用的場景,npx有點(diǎn)類似小程序提出的隨用隨走。

輕松地運(yùn)行本地命令 

  1. node_modules/.bin/vite -v  
  2. # vite/2.6.5 linux-x64 node-v14.16.0  
  3. # 等同于  
  4. # package.json script: "vite -v"  
  5. # npm run vite  
  6. npx vite -v  
  7. # vite/2.6.5 linux-x64 node-v14.16.0 

使用不同的 Node.js 版本運(yùn)行代碼某些場景下可以臨時切換 node 版本,有時比 nvm 包管理方便些。 

  1. npx node@14 -v  
  2. # v14.18.0  
  3. npx -p node@14 node -v   
  4. # v14.18.0 

無需安裝的命令執(zhí)行 。

  1. # 啟動本地靜態(tài)服務(wù)  
  2. npx http-server  
  1. # 無需全局安裝  
  2. npx @vue/cli create vue-project  
  3. # @vue/cli 相比 npm init vue@next npx create-vue@next 很慢。  
  4. # 全局安裝  
  5. npm i -g @vue/cli  
  6. vue create vue-project 

npx vue-cli

npm init vue@next (npx create-vue@next) 快的原因,主要在于依賴少(能不依賴包就不依賴),源碼行數(shù)少,目前index.js只有300余行。

3. 配置環(huán)境調(diào)試源碼

3.1 克隆 create-vue 項目

本文倉庫地址 create-vue-analysis[11],求個star~ 

  1. # 可以直接克隆我的倉庫,我的倉庫保留的 create-vue 倉庫的 git 記錄  
  2. git clone https://github.com/lxchuan12/create-vue-analysis.git  
  3. cd create-vue-analysis/create-vue  
  4. npm i 

當(dāng)然不克隆也可以直接用 VSCode 打開我的倉庫。https://open.vscode.dev/lxchuan12/create-vue-analysis

順帶說下:我是怎么保留 create-vue 倉庫的 git 記錄的。 

  1. # 在 github 上新建一個倉庫 `create-vue-analysis` 克隆下來  
  2. git clone https://github.com/lxchuan12/create-vue-analysis.git  
  3. cd create-vue-analysis  
  4. git subtree add --prefix=create-vue https://github.com/vuejs/create-vue.git main  
  5. # 這樣就把 create-vue 文件夾克隆到自己的 git 倉庫了。且保留的 git 記錄 

關(guān)于更多 git subtree,可以看Git Subtree 簡明使用手冊[12]

3.2 package.json 分析 

  1. // create-vue/package.json  
  2.  
  3.   "name": "create-vue",  
  4.   "version": "3.0.0-beta.6",  
  5.   "description": "An easy way to start a Vue project",  
  6.   "type": "module",  
  7.   "bin": {  
  8.     "create-vue": "outfile.cjs"  
  9.   },  

bin指定可執(zhí)行腳本。也就是我們可以使用 npx create-vue 的原因。

outfile.cjs 是打包輸出的JS文件 

  1.  
  2.   "scripts": {  
  3.     "build": "esbuild --bundle index.js --format=cjs --platform=node --outfile=outfile.cjs",  
  4.     "snapshot": "node snapshot.js",  
  5.     "pretest": "run-s build snapshot",  
  6.     "test": "node test.js"  
  7.   },  

執(zhí)行 npm run test 時,會先執(zhí)行鉤子函數(shù) pretest。run-s 是 npm-run-all[13] 提供的命令。run-s build snapshot 命令相當(dāng)于 npm run build && npm run snapshot。

根據(jù)腳本提示,我們來看 snapshot.js 文件。

3.3 生成快照 snapshot.js

這個文件主要作用是根據(jù)const featureFlags = ['typescript', 'jsx', 'router', 'vuex', 'with-tests'] 組合生成31種加上 default 共計 32種 組合,生成快照在 playground目錄。

因為打包生成的 outfile.cjs 代碼有做一些處理,不方便調(diào)試,我們可以修改為index.js便于調(diào)試。 

  1. // 路徑 create-vue/snapshot.js  
  2. const bin = path.resolve(__dirname, './outfile.cjs')  
  3. // 改成 index.js 便于調(diào)試  
  4. const bin = path.resolve(__dirname, './index.js') 

我們可以在for和 createProjectWithFeatureFlags 打上斷點(diǎn)。

createProjectWithFeatureFlags其實類似在終端輸入如下執(zhí)行這樣的命令

  1. node ./index.js --xxx --xxx --force  
  1. function createProjectWithFeatureFlags(flags) {  
  2.   const projectName = flags.join('-')  
  3.   console.log(`Creating project ${projectName}`)  
  4.   const { status } = spawnSync(  
  5.     'node',  
  6.     [bin, projectName, ...flags.map((flag) => `--${flag}`), '--force'],  
  7.     {  
  8.       cwd: playgroundDir,  
  9.       stdio: ['pipe', 'pipe', 'inherit']  
  10.     }  
  11.   )  
  12.   if (status !== 0) {  
  13.     process.exit(status)  
  14.   }  
  15.  
  16. // 路徑 create-vue/snapshot.js  
  17. for (const flags of flagCombinations) {  
  18.   createProjectWithFeatureFlags(flags)  

調(diào)試:VSCode打開項目,VSCode高版本(1.50+)可以在 create-vue/package.json => scripts => "test": "node test.js"。鼠標(biāo)懸停在test上會有調(diào)試腳本提示,選擇調(diào)試腳本。如果對調(diào)試不熟悉,可以看我之前的文章koa-compose,寫的很詳細(xì)。

調(diào)試時,大概率你會遇到:create-vue/index.js 文件中,__dirname 報錯問題。可以按照如下方法解決。在 import 的語句后,添加如下語句,就能愉快的調(diào)試了。 

  1. // 路徑 create-vue/index.js  
  2. // 解決辦法和nodejs issues  
  3. // https://stackoverflow.com/questions/64383909/dirname-is-not-defined-in-node-14-version  
  4. // https://github.com/nodejs/help/issues/2907  
  5. import { fileURLToPath } from 'url';  
  6. import { dirname } from 'path';  
  7. const __filename = fileURLToPath(import.meta.url);  
  8. const __dirname = dirname(__filename); 

接著我們調(diào)試 index.js 文件,來學(xué)習(xí)。

4. 調(diào)試 index.js 主流程

回顧下上文 npm init vue@next 初始化項目的。

npm init vue@next

單從初始化項目輸出圖來看。主要是三個步驟。 

  1. 1. 輸入項目名稱,默認(rèn)值是 vue-project  
  2. 2. 詢問一些配置 渲染模板等  
  3. 3. 完成創(chuàng)建項目,輸出運(yùn)行提示  
  1. async function init() {  
  2.   // 省略放在后文詳細(xì)講述  
  3.  
  4. // async 函數(shù)返回的是Promise 可以用 catch 報錯  
  5. init().catch((e) => {  
  6.   console.error(e)  
  7. }) 

4.1 解析命令行參數(shù) 

  1. // 返回運(yùn)行當(dāng)前腳本的工作目錄的路徑。  
  2. const cwd = process.cwd()  
  3. // possible options: 
  4. // --default  
  5. // --typescript / --ts  
  6. // --jsx  
  7. // --router / --vue-router  
  8. // --vuex 
  9. // --with-tests / --tests / --cypress  
  10. // --force (for force overwriting)  
  11. const argv = minimist(process.argv.slice(2), {  
  12.     alias: {  
  13.         typescript: ['ts'],  
  14.         'with-tests': ['tests', 'cypress'],  
  15.         router: ['vue-router']  
  16.     },  
  17.     // all arguments are treated as booleans  
  18.     boolean: true  
  19. }) 

minimist[14]

簡單說,這個庫,就是解析命令行參數(shù)的。看例子,我們比較容易看懂傳參和解析結(jié)果。 

  1. $ node example/parse.js -a beep -b boop  
  2. { _: [], a: 'beep', b: 'boop' }  
  3. $ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz  
  4. { _: [ 'foo', 'bar', 'baz' ],  
  5.   x: 3,  
  6.   y: 4,  
  7.   n: 5,  
  8.   a: true,  
  9.   b: true,  
  10.   c: true,  
  11.   beep: 'boop' } 

比如 

  1. npm init vue@next --vuex --force 

4.2 如果設(shè)置了 feature flags 跳過 prompts 詢問

這種寫法方便代碼測試等。直接跳過交互式詢問,同時也可以省時間。 

  1. // if any of the feature flags is set, we would skip the feature prompts  
  2.   // use `??` instead of `||` once we drop Node.js 12 support  
  3.   const isFeatureFlagsUsed =  
  4.     typeof (argv.default || argv.ts || argv.jsx || argv.router || argv.vuex || argv.tests) ===  
  5.     'boolean'  
  6. // 生成目錄  
  7.   let targetDir = argv._[0]  
  8.   // 默認(rèn) vue-projects  
  9.   const defaultProjectName = !targetDir ? 'vue-project' : targetDir  
  10.   // 強(qiáng)制重寫文件夾,當(dāng)同名文件夾存在時  
  11.   const forceOverwrite = argv.force 

4.3 交互式詢問一些配置

如上文npm init vue@next 初始化的圖示

  •  輸入項目名稱
  •  還有是否刪除已經(jīng)存在的同名目錄
  •  詢問使用需要 JSX Router vuex cypress 等。 
  1. let result = {}  
  2.   try {  
  3.     // Prompts:  
  4.     // - Project name:  
  5.     //   - whether to overwrite the existing directory or not?  
  6.     //   - enter a valid package name for package.json  
  7.     // - Project language: JavaScript / TypeScript  
  8.     // - Add JSX Support?  
  9.     // - Install Vue Router for SPA development?  
  10.     // - Install Vuex for state management? (TODO)  
  11.     // - Add Cypress for testing?  
  12.     result = await prompts(  
  13.       [  
  14.         {  
  15.           name: 'projectName',  
  16.           type: targetDir ? null : 'text',  
  17.           message: 'Project name:',  
  18.           initial: defaultProjectName, 
  19.           onState: (state) => (targetDir = String(state.value).trim() || defaultProjectName)  
  20.         },  
  21.         // 省略若干配置  
  22.         {  
  23.           name: 'needsTests',  
  24.           type: () => (isFeatureFlagsUsed ? null : 'toggle'),  
  25.           message: 'Add Cypress for testing?',  
  26.           initial: false,  
  27.           active: 'Yes',  
  28.           inactive: 'No'  
  29.         }  
  30.       ],  
  31.       {  
  32.         onCancel: () => {  
  33.           throw new Error(red('✖') + ' Operation cancelled')  
  34.         }  
  35.       }  
  36.     ]  
  37.     )  
  38.   } catch (cancelled) {  
  39.     console.log(cancelled.message)  
  40.     // 退出當(dāng)前進(jìn)程。  
  41.     process.exit(1)  
  42.   } 

4.4 初始化詢問用戶給到的參數(shù),同時也會給到默認(rèn)值 

  1. // `initial` won't take effect if the prompt type is null  
  2.   // so we still have to assign the default values here  
  3.   const {  
  4.     packageName = toValidPackageName(defaultProjectName),  
  5.     shouldOverwrite,  
  6.     needsJsx = argv.jsx,  
  7.     needsTypeScript = argv.typescript,  
  8.     needsRouter = argv.router,  
  9.     needsVuex = argv.vuex,  
  10.     needsTests = argv.tests  
  11.   } = result  
  12.   const root = path.join(cwd, targetDir)  
  13.   // 如果需要強(qiáng)制重寫,清空文件夾  
  14.   if (shouldOverwrite) {  
  15.     emptyDir(root)  
  16.     // 如果不存在文件夾,則創(chuàng)建  
  17.   } else if (!fs.existsSync(root)) {  
  18.     fs.mkdirSync(root)  
  19.   }  
  20.   // 腳手架項目目錄  
  21.   console.log(`\nScaffolding project in ${root}...`)  
  22.  // 生成 package.json 文件  
  23.   const pkg = { name: packageName, version: '0.0.0' }  
  24.   fs.writeFileSync(path.resolve(root, 'package.json'), JSON.stringify(pkg, null, 2)) 

4.5 根據(jù)模板文件生成初始化項目所需文件 

  1. // todo:  
  2.  // work around the esbuild issue that `import.meta.url` cannot be correctly transpiled  
  3.  // when bundling for node and the format is cjs  
  4.  // const templateRoot = new URL('./template', import.meta.url).pathname  
  5.  const templateRoot = path.resolve(__dirname, 'template')  
  6.  const render = function render(templateName) {  
  7.    const templateDir = path.resolve(templateRoot, templateName)  
  8.    renderTemplate(templateDir, root)  
  9.  }  
  10.  // Render base template  
  11.  render('base')  
  12.   // 添加配置  
  13.  // Add configs.  
  14.  if (needsJsx) {  
  15.    render('config/jsx') 
  16.  }  
  17.  if (needsRouter) {  
  18.    render('config/router')  
  19.  }  
  20.  if (needsVuex) {  
  21.    render('config/vuex')  
  22.  }  
  23.  if (needsTests) {  
  24.    render('config/cypress')  
  25.  }  
  26.  if (needsTypeScript) {  
  27.    render('config/typescript')  
  28.  } 

4.6 渲染生成代碼模板 

  1. // Render code template.  
  2.   // prettier-ignore  
  3.   const codeTemplate =  
  4.     (needsTypeScript ? 'typescript-' : '') +  
  5.     (needsRouter ? 'router' : 'default')  
  6.   render(`code/${codeTemplate}`)  
  7.   // Render entry file (main.js/ts).  
  8.   if (needsVuex && needsRouter) {  
  9.     render('entry/vuex-and-router')  
  10.   } else if (needsVuex) {  
  11.     render('entry/vuex')  
  12.   } else if (needsRouter) {  
  13.     render('entry/router')  
  14.   } else {  
  15.     render('entry/default')  
  16.   } 

4.7 如果配置了需要 ts

重命名所有的 .js 文件改成 .ts。重命名 jsconfig.json 文件為 tsconfig.json 文件。

jsconfig.json[15] 是VSCode的配置文件,可用于配置跳轉(zhuǎn)等。

把index.html 文件里的 main.js 重命名為 main.ts。 

  1. // Cleanup.  
  2. if (needsTypeScript) { 
  3.     // rename all `.js` files to `.ts`  
  4.     // rename jsconfig.json to tsconfig.json  
  5.     preOrderDirectoryTraverse(  
  6.       root,  
  7.       () => {},  
  8.       (filepath) => {  
  9.         if (filepath.endsWith('.js')) {  
  10.           fs.renameSync(filepath, filepath.replace(/\.js$/, '.ts'))  
  11.         } else if (path.basename(filepath) === 'jsconfig.json') {  
  12.           fs.renameSync(filepath, filepath.replace(/jsconfig\.json$/, 'tsconfig.json'))  
  13.         }  
  14.       }  
  15.     )  
  16.     // Rename entry in `index.html`  
  17.     const indexHtmlPath = path.resolve(root, 'index.html')  
  18.     const indexHtmlContent = fs.readFileSync(indexHtmlPath, 'utf8')  
  19.     fs.writeFileSync(indexHtmlPath, indexHtmlContent.replace('src/main.js', 'src/main.ts'))  
  20.   } 

4.8 配置了不需要測試

因為所有的模板都有測試文件,所以不需要測試時,執(zhí)行刪除 cypress、/__tests__/ 文件夾 

  1. if (!needsTests) {  
  2.    // All templates assumes the need of tests.  
  3.    // If the user doesn't need it:  
  4.    // rm -rf cypress **/__tests__/  
  5.    preOrderDirectoryTraverse(  
  6.      root,  
  7.      (dirpath) => {  
  8.        const dirname = path.basename(dirpath)  
  9.        if (dirname === 'cypress' || dirname === '__tests__') {  
  10.          emptyDir(dirpath)  
  11.          fs.rmdirSync(dirpath)  
  12.        }  
  13.      },  
  14.      () => {}  
  15.    )  
  16.  } 

4.9 根據(jù)使用的 npm / yarn / pnpm 生成README.md 文件,給出運(yùn)行項目的提示 

  1. // Instructions:  
  2.   // Supported package managers: pnpm > yarn > npm  
  3.   // Note: until <https://github.com/pnpm/pnpm/issues/3505> is resolved,  
  4.   // it is not possible to tell if the command is called by `pnpm init`.  
  5.   const packageManager = /pnpm/.test(process.env.npm_execpath)  
  6.     ? 'pnpm'  
  7.     : /yarn/.test(process.env.npm_execpath)  
  8.     ? 'yarn'  
  9.     : 'npm'  
  10.   // README generation  
  11.   fs.writeFileSync(  
  12.     path.resolve(root, 'README.md'),  
  13.     generateReadme({  
  14.       projectName: result.projectName || defaultProjectName,  
  15.       packageManager,  
  16.       needsTypeScript,  
  17.       needsTests  
  18.     })  
  19.   )  
  20.   console.log(`\nDone. Now run:\n`)  
  21.   if (root !== cwd) {  
  22.     console.log(`  ${bold(green(`cd ${path.relative(cwd, root)}`))}`)  
  23.   }  
  24.   console.log(`  ${bold(green(getCommand(packageManager, 'install')))}`)  
  25.   console.log(`  ${bold(green(getCommand(packageManager, 'dev')))}`)  
  26.   console.log() 

5. npm run test => node test.js 測試 

  1. // create-vue/test.js  
  2. import fs from 'fs'  
  3. import path from 'path'  
  4. import { fileURLToPath } from 'url'  
  5. import { spawnSync } from 'child_process'  
  6. const __dirname = path.dirname(fileURLToPath(import.meta.url))  
  7. const playgroundDir = path.resolve(__dirname, './playground/')  
  8. for (const projectName of fs.readdirSync(playgroundDir)) {  
  9.   if (projectName.endsWith('with-tests')) {  
  10.     console.log(`Running unit tests in ${projectName}`)  
  11.     const unitTestResult = spawnSync('pnpm', ['test:unit:ci'], {  
  12.       cwd: path.resolve(playgroundDir, projectName),  
  13.       stdio: 'inherit',  
  14.       shell: true  
  15.     })  
  16.     if (unitTestResult.status !== 0) {  
  17.       throw new Error(`Unit tests failed in ${projectName}`)  
  18.     }  
  19.     console.log(`Running e2e tests in ${projectName}`)  
  20.     const e2eTestResult = spawnSync('pnpm', ['test:e2e:ci'], {  
  21.       cwd: path.resolve(playgroundDir, projectName),  
  22.       stdio: 'inherit',  
  23.       shell: true  
  24.     }) 
  25.     if (e2eTestResult.status !== 0) {  
  26.       throw new Error(`E2E tests failed in ${projectName}`)  
  27.     }  
  28.   }  

主要對生成快照時生成的在 playground 32個文件夾,進(jìn)行如下測試。 

  1. pnpm test:unit:ci  
  2. pnpm test:e2e:ci  

6. 總結(jié)

我們使用了快如閃電般的npm init vue@next,學(xué)習(xí)npx命令了。學(xué)會了其原理。 

  1. npm init vue@next => npx create-vue@next 

快如閃電的原因在于依賴的很少。很多都是自己來實現(xiàn)。如:Vue-CLI中 vue create vue-project 命令是用官方的npm包validate-npm-package-name[16],刪除文件夾一般都是使用 rimraf[17]。而 create-vue 是自己實現(xiàn)emptyDir和isValidPackageName。

非常建議讀者朋友按照文中方法使用VSCode調(diào)試 create-vue 源碼。源碼中還有很多細(xì)節(jié)文中由于篇幅有限,未全面展開講述。 

 

責(zé)任編輯:龐桂玉 來源: 前端大全
相關(guān)推薦

2016-09-07 15:35:06

VueReact腳手架

2020-06-29 11:35:02

Spring BootJava腳手架

2021-01-07 05:34:07

腳手架JDK緩存

2021-05-21 05:22:52

腳手架工具項目

2018-08-30 16:08:37

Node.js腳手架工具

2018-06-11 14:39:57

前端腳手架工具node.js

2022-01-14 14:09:11

腳手架代碼自定義

2022-12-12 08:56:45

Vite3Vite

2009-09-16 15:05:58

CakePHP腳手架

2017-07-21 09:56:46

Webpack3 Vue.js腳手架

2018-05-15 09:10:27

前端vue.jswebpack

2021-10-08 06:10:43

前端技術(shù)Vue

2016-08-10 14:59:41

前端Javascript工具

2025-05-26 08:45:00

AvueVue.js前端

2025-05-16 07:24:41

Springkafka腳手架

2021-12-23 10:35:32

SpringCloud腳手架架構(gòu)

2014-08-15 09:36:06

2020-03-20 08:32:41

物聯(lián)網(wǎng)腳手架傳感器

2022-04-24 11:33:47

代碼管理工程

2023-11-21 17:36:04

OpenFeignSentinel
點(diǎn)贊
收藏

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

av电影一区二区三区| 91久久精品一区| 午夜精产品一区二区在线观看的| 国产激情欧美| 亚洲欧美区自拍先锋| 国产综合av一区二区三区| 人人妻人人爽人人澡人人精品| 色135综合网| 亚洲精品福利视频| 视色视频在线观看| 国产在线观看www| 国产亚洲成aⅴ人片在线观看| 国产精品永久免费在线| 日本在线视频免费观看| 久久国产亚洲精品| 精品视频久久久久久| 少妇愉情理伦片bd| 成人做爰免费视频免费看| 亚洲一区中文在线| 亚洲v国产v| 日韩欧美在线观看一区二区| 国产中文字幕一区| 国产精品久久久久久久7电影| 免费视频一二三区| 99久久99视频只有精品| 亚洲欧美日韩另类| av av在线| 国产精久久一区二区| 色欧美日韩亚洲| 男女啪啪免费视频网站| 国内外激情在线| 国产欧美日韩不卡| 欧美精品成人一区二区在线观看| 国产成人av免费看| 久久精品国产网站| 国产精品吹潮在线观看| 五月婷婷中文字幕| 亚洲国产激情| 欧美高清在线观看| 草视频在线观看| 欧美残忍xxxx极端| 最近2019中文字幕一页二页| 99久久久久久久久久| 欧美顶级毛片在线播放| 精品国免费一区二区三区| 日本在线观看视频一区| 天堂久久一区| 欧美亚洲日本一区| 日韩av手机版| 欧美三区四区| 欧美专区日韩专区| 国产高清视频网站| 人人精品久久| 欧美三级日韩三级国产三级| 亚洲欧美在线精品| 久久国内精品| 欧美精品v国产精品v日韩精品| 污污的网站18| 一区二区三区日本视频| 51午夜精品国产| 欧美日韩理论片| 欧美视频三区| 日韩欧美高清一区| www.四虎在线| 婷婷成人综合| 亚洲人av在线影院| 粉嫩精品久久99综合一区| 大胆日韩av| 精品国产一区二区三区久久久 | 麻豆久久久久| 欧美日韩国产片| 午夜av中文字幕| 日本在线视频一区二区三区| 亚洲а∨天堂久久精品9966| 成人网站免费观看| 精品视频久久| 久久亚洲精品中文字幕冲田杏梨| 国产黄色片在线免费观看| 欧美日韩精品| 日本不卡免费高清视频| 国产女优在线播放| 国产精品99久| 久久一区免费| 日韩精品黄色| 午夜伦理一区二区| 久久午夜夜伦鲁鲁一区二区| 深夜福利亚洲| 亚洲成年人在线播放| 久久精品视频18| 在线中文一区| 国产91|九色| 在线观看毛片av| 丁香一区二区三区| 日韩黄色影视| 污污网站在线观看| 91久久精品网| 成人做爰www看视频软件| 教室别恋欧美无删减版| 萌白酱国产一区二区| 亚洲欧美自拍视频| 国产一区二区电影| 日本高清视频一区二区三区| 亚洲图区一区| 欧美性一二三区| 亚洲欧美高清在线| 色综合咪咪久久网| 26uuu另类亚洲欧美日本一| 亚洲最大成人在线视频| 99久久99久久免费精品蜜臀| 尤物国产精品| 亚洲成人看片| 亚洲国产精品999| www.av免费| 久久亚洲二区| 久久国产精品一区二区三区四区| 黄网站app在线观看| 色婷婷av久久久久久久| 在线播放第一页| 91免费精品| 日本精品一区二区三区在线| 亚洲国产日韩在线观看| 国产精品久久久久久久第一福利 | brazzers在线观看| 欧美情侣在线播放| 高清国产在线观看| 一区二区久久| 国产精品区一区| 国产传媒在线播放| 欧美日韩在线一区二区| 亚洲午夜福利在线观看| 夜久久久久久| 国产精品福利视频| 日本在线视频中文有码| 欧美一级午夜免费电影| 少妇高潮惨叫久久久久| 日韩电影一区二区三区四区| 免费在线一区二区| 欧美理论影院| 一本色道久久综合狠狠躁篇怎么玩 | 高清一区在线观看| 欧美精品一区二区久久| 国产a级全部精品| 你懂的在线免费观看| 精品久久久国产精品999| 白嫩情侣偷拍呻吟刺激| 狠狠爱www人成狠狠爱综合网| 147欧美人体大胆444| 国产欧美黑人| 91精品国产一区二区| xxxx日本少妇| 国产精品一二三区在线| 300部国产真实乱| 色悠久久久久综合先锋影音下载| 美女久久久久久久久久久| 97国产精品久久久| 亚洲免费观看高清完整版在线观看 | 在线免费观看日韩视频| 国产精品嫩草影院com| 超碰在线公开97| 一区二区三区午夜探花| 亚洲一区二区三区四区视频| a视频在线播放| 精品久久一区二区三区| 亚洲精品国产精品乱码| 久久综合九色综合97婷婷女人| 久久久久久久久久久福利| 蜜桃精品wwwmitaows| 国产精品青青在线观看爽香蕉 | 羞羞的视频在线| 五月开心六月丁香综合色啪| 3d动漫啪啪精品一区二区免费 | 免费欧美在线视频| 日韩免费在线观看视频| 成人在线免费视频| 欧美日韩国产高清一区二区三区 | 男女视频在线观看免费| 欧美主播一区二区三区美女| 成人高潮免费视频| 成人精品一区二区三区中文字幕| 播放灌醉水嫩大学生国内精品| 精品av一区二区| 成人性生交大片免费看视频直播| 欧美日韩经典丝袜| 亚洲欧美另类国产| 国产精品久久久久久69| 性做久久久久久| 精品人妻无码一区二区三区换脸| 国产一区二区三区四| 国产乱子伦农村叉叉叉| 欧美一区二区性| 国产高清在线一区二区| 欧美日韩大片| 欧美高清无遮挡| av在线收看| 亚洲国产精品一区二区久| 中文字幕免费在线看| 亚洲影视资源网| 欧美性受xxxx黑人| 成人少妇影院yyyy| 中文字幕免费高清在线| 亚洲理伦在线| 影音先锋成人资源网站| 国产亚洲一区| 国产视频精品网| 麻豆国产精品| 国产精品视频专区| 激情aⅴ欧美一区二区欲海潮| 久久精品影视伊人网| 神马午夜在线观看| 8x福利精品第一导航| 亚洲天堂男人av| 亚洲国产精品久久不卡毛片| 日本伦理一区二区三区| 久久久一区二区三区捆绑**| 亚洲天堂伊人网| 噜噜噜91成人网| 青青草精品视频在线| 无码一区二区三区视频| 日韩福利影院| 日本欧美高清| 国产精品日韩一区二区免费视频| 日韩一级特黄| 国产精品 欧美在线| 黄视频免费在线看| 高清在线视频日韩欧美| 菠萝菠萝蜜在线视频免费观看| 国产一区二区三区在线观看视频| 日韩一级在线播放| 欧美成人猛片aaaaaaa| 国产又粗又猛视频免费| 欧美综合一区二区三区| 国产又粗又猛又黄视频| 黄网动漫久久久| 日韩成人在线免费视频| 亚洲精品久久久久久国产精华液| 长河落日免费高清观看| 国产日韩在线不卡| 先锋影音av在线| 久久亚洲捆绑美女| 搡老熟女老女人一区二区| 不卡电影免费在线播放一区| 日本成人在线免费| 国产精品一区三区| 女同性αv亚洲女同志| 国产成人高清视频| 韩国三级在线看| 成人精品免费看| a级一a一级在线观看| 99精品视频中文字幕| 草草影院第一页| 国产视频一区不卡| 长河落日免费高清观看| 中文字幕一区二区三区不卡在线| 免费精品在线视频| 亚洲欧美日韩中文播放 | 五月激情丁香一区二区三区| 久艹视频在线观看| 亚洲1区2区3区4区| 国产成人免费看| 91久久一区二区| 亚洲天堂aaa| 日韩欧美一级精品久久| 刘亦菲久久免费一区二区| 亚洲国产欧美久久| 日本免费一区二区三区最新| 亚洲天堂网站在线观看视频| 999在线视频| 欧美精品一区三区| sm性调教片在线观看| 日韩av电影免费观看高清| 日韩毛片在线| 亚洲一区二区三区香蕉| 国产成人av毛片| 欧美一区观看| 97国产精品| 日b视频免费观看| 美女爽到呻吟久久久久| 国产性生活一级片| 成人av电影在线网| 懂色av粉嫩av浪潮av| 一区二区三区四区在线播放| 亚洲国产日韩欧美综合久久| 天干夜夜爽爽日日日日| 在线观看av不卡| 99国产精品久久久久久久成人| 精品噜噜噜噜久久久久久久久试看| 日本在线丨区| 久久精品国产99国产精品澳门| 丁香花电影在线观看完整版| 国产脚交av在线一区二区| 欧美电影院免费观看| 九九九九九精品| 亚洲一区色图| 成人在线免费观看av| 老汉av免费一区二区三区| 91视频在线免费| 中文字幕字幕中文在线中不卡视频| 久久精品国产亚洲av高清色欲| 欧洲人成人精品| 刘亦菲毛片一区二区三区| 亚洲一区二区福利| 日本孕妇大胆孕交无码| 国产精品久久久久久久久久小说| 97视频一区| 一区二区三区欧美成人| 中文亚洲免费| 亚洲精品无码久久久久久久| 久久精子c满五个校花| 久久网中文字幕| 欧美日韩一区二区三区高清| 丰满熟妇乱又伦| 久久手机免费视频| 丝袜美腿诱惑一区二区三区| wwwxx欧美| 久久精品久久久| 88av.com| 91免费观看国产| 国产在线一区视频| 日韩欧美专区在线| 欧美96在线| 国产精品视频一| 国产一区二区三区四区二区| www在线观看免费| 东方aⅴ免费观看久久av| 国产农村妇女精品一区| 岛国av一区二区在线在线观看| 亚洲第一视频在线| zzijzzij亚洲日本成熟少妇| 欧美aaa视频| 欧美日韩国产不卡在线看| 欧美视频成人| 精产国品一区二区三区| 1024成人网色www| 超碰在线免费97| 亚洲色图17p| 中文字幕在线免费观看视频| 国产麻豆一区二区三区在线观看| 亚洲一区 二区 三区| 一区二区三区国产好的精华液| 国产精品进线69影院| 中文天堂在线资源| 国产一区二区三区网站| 国产v综合v| 色噜噜色狠狠狠狠狠综合色一| 快she精品国产999| 黄色aaa视频| 色哟哟在线观看一区二区三区| 五月婷婷丁香六月| 欧美一区二区三区免费观看| 国产伦乱精品| 欧美在线一区视频| 91视频免费观看| 国产精品777777| 亚洲欧洲视频在线| 国产成人午夜性a一级毛片| 日本一区不卡| 久久99精品国产麻豆婷婷| 超碰97av在线| 欧美女孩性生活视频| 黄色网页在线观看| 91黄色精品| 亚洲深夜影院| 亚洲一区二区自偷自拍| 欧美午夜一区二区| 国产在线更新| 国产精品一区而去| 亚洲一区日韩在线| 人人人妻人人澡人人爽欧美一区| 欧美色图天堂网| 国产原创精品视频| 国产99视频精品免费视频36| 亚洲一区日本| 娇小11一12╳yⅹ╳毛片| 欧美一区二区三区白人| 123区在线| 任我爽在线视频精品一| 精品无人码麻豆乱码1区2区 | 色喇叭免费久久综合网| 九色91porny| 高跟丝袜欧美一区| 亚洲乱亚洲乱妇| 91情侣在线视频| 另类av一区二区| 91九色丨porny丨极品女神| 亚洲成人激情在线观看| 综合在线影院| av日韩在线看| 欧美极品少妇xxxxⅹ高跟鞋 | 最近免费中文字幕视频2019| 日本成人精品| 日韩一级在线免费观看| 亚洲欧洲综合另类| 青青色在线视频| 亚洲精品日韩激情在线电影| 国产视频久久| 男女做暖暖视频| 国产亚洲一级高清| 99亚洲乱人伦aⅴ精品| 色噜噜狠狠一区二区| 天天综合色天天|