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

「Create-?」每個(gè)前端開發(fā)者都可以擁有屬于自己的命令行腳手架

開發(fā) 前端
最近一直在搞Strve.js生態(tài),在自己搗鼓框架的同時(shí)也學(xué)到了很多東西。所以就本篇文章給大家介紹一種更加方便靈活的命令行腳手架工具,以及如何發(fā)布到NPM上。

[[441093]]

前言

為什么要寫這篇文章呢?是因?yàn)樽罱恢痹诟鉙trve.js生態(tài),在自己搗鼓框架的同時(shí)也學(xué)到了很多東西。所以就本篇文章給大家介紹一種更加方便靈活的命令行腳手架工具,以及如何發(fā)布到NPM上。

之前,我也寫過類似的開發(fā)命令行工具的文章,但是核心思想都是通過代碼遠(yuǎn)程拉取Git倉庫中的項(xiàng)目模板代碼。有時(shí)候會(huì)因?yàn)榫W(wǎng)速的原因?qū)е吕∈。M(jìn)而會(huì)初始化項(xiàng)目失敗。

那么,有沒有比這個(gè)更好的方案呢?那么本篇就來了。

最近,使用Vite工具開發(fā)了很多項(xiàng)目。不得不佩服尤老師驚人的代碼能力,創(chuàng)建了這么好的開發(fā)工具,開發(fā)體驗(yàn)非常絲滑。尤其是你剛初始化項(xiàng)目時(shí),只需要執(zhí)行一行命令,也不用全局安裝什么工具。然后,自定義選擇需要的模板進(jìn)行初始化項(xiàng)目,就大功告成了!這種操作著實(shí)把我驚到了!我在想,如果我把create-vite的這種思路應(yīng)用到我自己的腳手架工具中是不是很Nice!

實(shí)戰(zhàn)

所以,二話不說,就抓緊打開ViteGitHub地址。

https://github.com/vitejs

找了大半天,終于找到了命令行工具核心代碼。

https://github.com/vitejs/vite/tree/main/packages/create-vite

映入眼簾的是很多以template-開頭的文件夾,打開幾個(gè)都看了一下,都是框架項(xiàng)目模板。那么,可以先放在一邊。

下一步,我們就打開index.js文件看下什么內(nèi)容。我列下代碼,大家可以簡(jiǎn)單看一下,不用深究。

  1. #!/usr/bin/env node 
  2.  
  3. // @ts-check 
  4. const fs = require('fs'
  5. const path = require('path'
  6. // Avoids autoconversion to number of the project name by defining that the args 
  7. // non associated with an option ( _ ) needs to be parsed as a string. See #4606 
  8. const argv = require('minimist')(process.argv.slice(2), { string: ['_'] }) 
  9. // eslint-disable-next-line node/no-restricted-require 
  10. const prompts = require('prompts'
  11. const { 
  12.   yellow, 
  13.   green, 
  14.   cyan, 
  15.   blue, 
  16.   magenta, 
  17.   lightRed, 
  18.   red 
  19. } = require('kolorist'
  20.  
  21. const cwd = process.cwd() 
  22.  
  23. const FRAMEWORKS = [ 
  24.   { 
  25.     name'vanilla'
  26.     color: yellow, 
  27.     variants: [ 
  28.       { 
  29.         name'vanilla'
  30.         display: 'JavaScript'
  31.         color: yellow 
  32.       }, 
  33.       { 
  34.         name'vanilla-ts'
  35.         display: 'TypeScript'
  36.         color: blue 
  37.       } 
  38.     ] 
  39.   }, 
  40.   { 
  41.     name'vue'
  42.     color: green, 
  43.     variants: [ 
  44.       { 
  45.         name'vue'
  46.         display: 'JavaScript'
  47.         color: yellow 
  48.       }, 
  49.       { 
  50.         name'vue-ts'
  51.         display: 'TypeScript'
  52.         color: blue 
  53.       } 
  54.     ] 
  55.   }, 
  56.   { 
  57.     name'react'
  58.     color: cyan, 
  59.     variants: [ 
  60.       { 
  61.         name'react'
  62.         display: 'JavaScript'
  63.         color: yellow 
  64.       }, 
  65.       { 
  66.         name'react-ts'
  67.         display: 'TypeScript'
  68.         color: blue 
  69.       } 
  70.     ] 
  71.   }, 
  72.   { 
  73.     name'preact'
  74.     color: magenta, 
  75.     variants: [ 
  76.       { 
  77.         name'preact'
  78.         display: 'JavaScript'
  79.         color: yellow 
  80.       }, 
  81.       { 
  82.         name'preact-ts'
  83.         display: 'TypeScript'
  84.         color: blue 
  85.       } 
  86.     ] 
  87.   }, 
  88.   { 
  89.     name'lit'
  90.     color: lightRed, 
  91.     variants: [ 
  92.       { 
  93.         name'lit'
  94.         display: 'JavaScript'
  95.         color: yellow 
  96.       }, 
  97.       { 
  98.         name'lit-ts'
  99.         display: 'TypeScript'
  100.         color: blue 
  101.       } 
  102.     ] 
  103.   }, 
  104.   { 
  105.     name'svelte'
  106.     color: red, 
  107.     variants: [ 
  108.       { 
  109.         name'svelte'
  110.         display: 'JavaScript'
  111.         color: yellow 
  112.       }, 
  113.       { 
  114.         name'svelte-ts'
  115.         display: 'TypeScript'
  116.         color: blue 
  117.       } 
  118.     ] 
  119.   } 
  120.  
  121. const TEMPLATES = FRAMEWORKS.map( 
  122.   (f) => (f.variants && f.variants.map((v) => v.name)) || [f.name
  123. ).reduce((a, b) => a.concat(b), []) 
  124.  
  125. const renameFiles = { 
  126.   _gitignore: '.gitignore' 
  127.  
  128. async function init() { 
  129.   let targetDir = argv._[0] 
  130.   let template = argv.template || argv.t 
  131.  
  132.   const defaultProjectName = !targetDir ? 'vite-project' : targetDir 
  133.  
  134.   let result = {} 
  135.  
  136.   try { 
  137.     result = await prompts( 
  138.       [ 
  139.         { 
  140.           type: targetDir ? null : 'text'
  141.           name'projectName'
  142.           message: 'Project name:'
  143.           initial: defaultProjectName, 
  144.           onState: (state) => 
  145.             (targetDir = state.value.trim() || defaultProjectName) 
  146.         }, 
  147.         { 
  148.           type: () => 
  149.             !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : 'confirm'
  150.           name'overwrite'
  151.           message: () => 
  152.             (targetDir === '.' 
  153.               ? 'Current directory' 
  154.               : `Target directory "${targetDir}"`) + 
  155.             ` is not empty. Remove existing files and continue?` 
  156.         }, 
  157.         { 
  158.           type: (_, { overwrite } = {}) => { 
  159.             if (overwrite === false) { 
  160.               throw new Error(red('✖') + ' Operation cancelled'
  161.             } 
  162.             return null 
  163.           }, 
  164.           name'overwriteChecker' 
  165.         }, 
  166.         { 
  167.           type: () => (isValidPackageName(targetDir) ? null : 'text'), 
  168.           name'packageName'
  169.           message: 'Package name:'
  170.           initial: () => toValidPackageName(targetDir), 
  171.           validate: (dir) => 
  172.             isValidPackageName(dir) || 'Invalid package.json name' 
  173.         }, 
  174.         { 
  175.           type: template && TEMPLATES.includes(template) ? null : 'select'
  176.           name'framework'
  177.           message: 
  178.             typeof template === 'string' && !TEMPLATES.includes(template) 
  179.               ? `"${template}" isn't a valid template. Please choose from below: ` 
  180.               : 'Select a framework:'
  181.           initial: 0, 
  182.           choices: FRAMEWORKS.map((framework) => { 
  183.             const frameworkColor = framework.color 
  184.             return { 
  185.               title: frameworkColor(framework.name), 
  186.               value: framework 
  187.             } 
  188.           }) 
  189.         }, 
  190.         { 
  191.           type: (framework) => 
  192.             framework && framework.variants ? 'select' : null
  193.           name'variant'
  194.           message: 'Select a variant:'
  195.           // @ts-ignore 
  196.           choices: (framework) => 
  197.             framework.variants.map((variant) => { 
  198.               const variantColor = variant.color 
  199.               return { 
  200.                 title: variantColor(variant.name), 
  201.                 value: variant.name 
  202.               } 
  203.             }) 
  204.         } 
  205.       ], 
  206.       { 
  207.         onCancel: () => { 
  208.           throw new Error(red('✖') + ' Operation cancelled'
  209.         } 
  210.       } 
  211.     ) 
  212.   } catch (cancelled) { 
  213.     console.log(cancelled.message) 
  214.     return 
  215.   } 
  216.  
  217.   // user choice associated with prompts 
  218.   const { framework, overwrite, packageName, variant } = result 
  219.  
  220.   const root = path.join(cwd, targetDir) 
  221.  
  222.   if (overwrite) { 
  223.     emptyDir(root) 
  224.   } else if (!fs.existsSync(root)) { 
  225.     fs.mkdirSync(root) 
  226.   } 
  227.  
  228.   // determine template 
  229.   template = variant || framework || template 
  230.  
  231.   console.log(`\nScaffolding project in ${root}...`) 
  232.  
  233.   const templateDir = path.join(__dirname, `template-${template}`) 
  234.  
  235.   const write = (file, content) => { 
  236.     const targetPath = renameFiles[file] 
  237.       ? path.join(root, renameFiles[file]) 
  238.       : path.join(root, file) 
  239.     if (content) { 
  240.       fs.writeFileSync(targetPath, content) 
  241.     } else { 
  242.       copy(path.join(templateDir, file), targetPath) 
  243.     } 
  244.   } 
  245.  
  246.   const files = fs.readdirSync(templateDir) 
  247.   for (const file of files.filter((f) => f !== 'package.json')) { 
  248.     write(file) 
  249.   } 
  250.  
  251.   const pkg = require(path.join(templateDir, `package.json`)) 
  252.  
  253.   pkg.name = packageName || targetDir 
  254.  
  255.   write('package.json', JSON.stringify(pkg, null, 2)) 
  256.  
  257.   const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent) 
  258.   const pkgManager = pkgInfo ? pkgInfo.name : 'npm' 
  259.  
  260.   console.log(`\nDone. Now run:\n`) 
  261.   if (root !== cwd) { 
  262.     console.log(`  cd ${path.relative(cwd, root)}`) 
  263.   } 
  264.   switch (pkgManager) { 
  265.     case 'yarn'
  266.       console.log('  yarn'
  267.       console.log('  yarn dev'
  268.       break 
  269.     default
  270.       console.log(`  ${pkgManager} install`) 
  271.       console.log(`  ${pkgManager} run dev`) 
  272.       break 
  273.   } 
  274.   console.log() 
  275.  
  276. function copy(src, dest) { 
  277.   const stat = fs.statSync(src) 
  278.   if (stat.isDirectory()) { 
  279.     copyDir(src, dest) 
  280.   } else { 
  281.     fs.copyFileSync(src, dest) 
  282.   } 
  283.  
  284. function isValidPackageName(projectName) { 
  285.   return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test( 
  286.     projectName 
  287.   ) 
  288.  
  289. function toValidPackageName(projectName) { 
  290.   return projectName 
  291.     .trim() 
  292.     .toLowerCase() 
  293.     .replace(/\s+/g, '-'
  294.     .replace(/^[._]/, ''
  295.     .replace(/[^a-z0-9-~]+/g, '-'
  296.  
  297. function copyDir(srcDir, destDir) { 
  298.   fs.mkdirSync(destDir, { recursive: true }) 
  299.   for (const file of fs.readdirSync(srcDir)) { 
  300.     const srcFile = path.resolve(srcDir, file) 
  301.     const destFile = path.resolve(destDir, file) 
  302.     copy(srcFile, destFile) 
  303.   } 
  304.  
  305. function isEmpty(path) { 
  306.   return fs.readdirSync(path).length === 0 
  307.  
  308. function emptyDir(dir) { 
  309.   if (!fs.existsSync(dir)) { 
  310.     return 
  311.   } 
  312.   for (const file of fs.readdirSync(dir)) { 
  313.     const abs = path.resolve(dir, file) 
  314.     // baseline is Node 12 so can't use rmSync :( 
  315.     if (fs.lstatSync(abs).isDirectory()) { 
  316.       emptyDir(abs
  317.       fs.rmdirSync(abs
  318.     } else { 
  319.       fs.unlinkSync(abs
  320.     } 
  321.   } 
  322.  
  323. /** 
  324.  * @param {string | undefined} userAgent process.env.npm_config_user_agent 
  325.  * @returns object | undefined 
  326.  */ 
  327. function pkgFromUserAgent(userAgent) { 
  328.   if (!userAgent) return undefined 
  329.   const pkgSpec = userAgent.split(' ')[0] 
  330.   const pkgSpecArr = pkgSpec.split('/'
  331.   return { 
  332.     name: pkgSpecArr[0], 
  333.     version: pkgSpecArr[1] 
  334.   } 
  335.  
  336. init().catch((e) => { 
  337.   console.error(e) 
  338. }) 

看到上面這么多代碼是不是不想繼續(xù)閱讀下去了?不要慌!我們其實(shí)就用到里面幾個(gè)地方,可以放心的繼續(xù)閱讀下去。

這些代碼算是Create Vite核心代碼了,我們會(huì)看到常量FRAMEWORKS定義了一個(gè)數(shù)組對(duì)象,另外數(shù)組對(duì)象中都是一些我們初始化項(xiàng)目時(shí)需要選擇安裝的框架。所以,我們可以先ViteGithub項(xiàng)目Clone下來,試試效果。

然后,將項(xiàng)目Clone下來之后,我們找到/packages/create-vite這個(gè)文件夾,我們現(xiàn)在就只關(guān)注這個(gè)文件夾。

我用的Yarn依賴管理工具,所以我首先使用命令初始化依賴。

  1. yarn  

然后,我們可以先打開根目錄下的package.json文件,會(huì)發(fā)現(xiàn)有如下命令。

  1.   "bin": { 
  2.     "create-vite""index.js"
  3.     "cva""index.js" 
  4.   } 

我們可以在這里起一個(gè)自己模板的名字,比如我們就叫demo,

  1.   "bin": { 
  2.     "create-demo""index.js"
  3.     "cvd""index.js" 
  4.   } 

然后,我們先在這里使用yarn link命令來將此命令在本地可以運(yùn)行。

然后再運(yùn)行create-demo命令·。

 會(huì)顯示一些交互文本,會(huì)發(fā)現(xiàn)非常熟悉,這正是我們創(chuàng)建Vite項(xiàng)目時(shí)所看到的。我們?cè)谇懊嬲f到我們想實(shí)現(xiàn)一個(gè)屬于自己的項(xiàng)目模板,現(xiàn)在我們也找到了核心。所以就開始干起來吧!

我們會(huì)看到在根目錄下有很多template-開頭的文件夾,我們打開一個(gè)看一下。比如template-vue。

原來模板都在這!但是這些模板文件都是以template-開頭,是不是有什么約定?所以,我們打算回頭再去看下index.js文件。

  1. // determine template 
  2. template = variant || framework || template 
  3.  
  4. console.log(`\nScaffolding project in ${root}...`) 
  5.  
  6. const templateDir = path.join(__dirname, `template-${template}`) 

果真,所以模板都必須以template-開頭。

那么,我們就在根目錄下面建一個(gè)template-demo文件夾,里面再放一個(gè)index.js文件,作為示例模板。

我們?cè)趫?zhí)行初始化項(xiàng)目時(shí)發(fā)現(xiàn),需要選擇對(duì)應(yīng)的模板,那么這些選項(xiàng)是從哪里來的呢?我們決定再回去看下根目錄下的index.js文件。

會(huì)發(fā)現(xiàn)有這么一個(gè)數(shù)組,里面正是我們要選擇的框架模板。

  1. const FRAMEWORKS = [ 
  2.   { 
  3.     name'vanilla'
  4.     color: yellow, 
  5.     variants: [ 
  6.       { 
  7.         name'vanilla'
  8.         display: 'JavaScript'
  9.         color: yellow 
  10.       }, 
  11.       { 
  12.         name'vanilla-ts'
  13.         display: 'TypeScript'
  14.         color: blue 
  15.       } 
  16.     ] 
  17.   }, 
  18.   { 
  19.     name'vue'
  20.     color: green, 
  21.     variants: [ 
  22.       { 
  23.         name'vue'
  24.         display: 'JavaScript'
  25.         color: yellow 
  26.       }, 
  27.       { 
  28.         name'vue-ts'
  29.         display: 'TypeScript'
  30.         color: blue 
  31.       } 
  32.     ] 
  33.   }, 
  34.   { 
  35.     name'react'
  36.     color: cyan, 
  37.     variants: [ 
  38.       { 
  39.         name'react'
  40.         display: 'JavaScript'
  41.         color: yellow 
  42.       }, 
  43.       { 
  44.         name'react-ts'
  45.         display: 'TypeScript'
  46.         color: blue 
  47.       } 
  48.     ] 
  49.   }, 
  50.   { 
  51.     name'preact'
  52.     color: magenta, 
  53.     variants: [ 
  54.       { 
  55.         name'preact'
  56.         display: 'JavaScript'
  57.         color: yellow 
  58.       }, 
  59.       { 
  60.         name'preact-ts'
  61.         display: 'TypeScript'
  62.         color: blue 
  63.       } 
  64.     ] 
  65.   }, 
  66.   { 
  67.     name'lit'
  68.     color: lightRed, 
  69.     variants: [ 
  70.       { 
  71.         name'lit'
  72.         display: 'JavaScript'
  73.         color: yellow 
  74.       }, 
  75.       { 
  76.         name'lit-ts'
  77.         display: 'TypeScript'
  78.         color: blue 
  79.       } 
  80.     ] 
  81.   }, 
  82.   { 
  83.     name'svelte'
  84.     color: red, 
  85.     variants: [ 
  86.       { 
  87.         name'svelte'
  88.         display: 'JavaScript'
  89.         color: yellow 
  90.       }, 
  91.       { 
  92.         name'svelte-ts'
  93.         display: 'TypeScript'
  94.         color: blue 
  95.       } 
  96.     ] 
  97.   } 

所以,可以在后面數(shù)組后面再添加一個(gè)對(duì)象。

  1.     name'demo'
  2.     color: red, 
  3.     variants: [ 
  4.       { 
  5.         name'demo'
  6.         display: 'JavaScript'
  7.         color: yellow 
  8.       } 
  9.     ] 

好,你會(huì)發(fā)現(xiàn)我這里會(huì)有個(gè)color屬性,并且有類似顏色值的屬性值,這是依賴kolorist導(dǎo)出的常量。kolorist是一個(gè)將顏色放入標(biāo)準(zhǔn)輸入/標(biāo)準(zhǔn)輸出的小庫。我們?cè)谥澳切┠0褰换ノ谋緯?huì)看到它們顯示不同顏色,這正是它的功勞。

  1. const { 
  2.   yellow, 
  3.   green, 
  4.   cyan, 
  5.   blue, 
  6.   magenta, 
  7.   lightRed, 
  8.   red 
  9. } = require('kolorist'

我們,也將模板對(duì)象添加到數(shù)組里了,那么下一步我們執(zhí)行命令看下效果。

 

會(huì)發(fā)現(xiàn)多了一個(gè)demo模板,這正是我們想要的。

我們繼續(xù)執(zhí)行下去。

我們會(huì)看到根目錄下已經(jīng)成功創(chuàng)建了demo1文件夾,并且里面正是我們想要的demo模板。

上圖顯示的Error,是因?yàn)槲覜]有在demo模板上創(chuàng)建package.json文件,所以這里可以忽略。你可以在自己的模板里創(chuàng)建一個(gè)package.json文件。

雖然,我們成功在本地創(chuàng)建了自己的一個(gè)模板,但是,我們只能本地創(chuàng)建。也就是說你換臺(tái)電腦,就沒有辦法執(zhí)行這個(gè)創(chuàng)建模板的命令。

所以,我們要想辦法去發(fā)布到云端,這里我們發(fā)布到NPM上。

首先,我們重新新建一個(gè)項(xiàng)目目錄,將其他模板刪除,只保留我們自己的模板。另外,將數(shù)組中的其他模板對(duì)象刪除,保留一個(gè)自己的模板。

我以自己的模板create-strve-app為例。

然后,我們打開package.json文件,需要修改一些信息。

以create-strve-app為例:

  1.   "name""create-strve-app"
  2.   "version""1.3.3"
  3.   "license""MIT"
  4.   "author""maomincoding"
  5.   "bin": { 
  6.     "create-strve-app""index.js"
  7.     "cs-app""index.js" 
  8.   }, 
  9.   "files": [ 
  10.     "index.js"
  11.     "template-*" 
  12.   ], 
  13.   "main""index.js"
  14.   "private"false
  15.   "keywords": ["strve","strvejs","dom","mvvm","virtual dom","html","template","string","create-strve","create-strve-app"], 
  16.   "engines": { 
  17.     "node"">=12.0.0" 
  18.   }, 
  19.   "repository": { 
  20.     "type""git"
  21.     "url""git+https://github.com/maomincoding/create-strve-app.git" 
  22.   }, 
  23.   "bugs": { 
  24.     "url""https://github.com/maomincoding/create-strve-app/issues" 
  25.   }, 
  26.   "homepage""https://github.com/maomincoding/create-strve-app#readme"
  27.   "dependencies": { 
  28.     "kolorist""^1.5.0"
  29.     "minimist""^1.2.5"
  30.     "prompts""^2.4.2" 
  31.   } 

注意,每次發(fā)布前,version字段必須與之前不同,否則發(fā)布失敗。

最后,我們依次運(yùn)行如下命令。

切換到npm源

  1. npm config set registry=https://registry.npmjs.org 

登錄NPM(如果已登錄,可忽略此步)

  1. npm login 

發(fā)布NPM

  1. npm publish 

我們可以登錄到NPM(https://www.npmjs.com/)

查看已經(jīng)發(fā)布成功!

 

以后,我們就可以直接運(yùn)行命令下載自定義模板。這在我們重復(fù)使用模板時(shí)非常有用,不僅可以提升效率,而且還可以避免犯很多不必要的錯(cuò)誤。

結(jié)語

另外,此篇舉例的 Create Strve App 是一套快速搭建Strve.js項(xiàng)目的命令行工具。如果你對(duì)此感興趣,可以訪問以下地址查看源碼:

https://github.com/maomincoding/create-strve-app

熬夜奮戰(zhàn)二個(gè)多月,Strve.js生態(tài)初步已經(jīng)建成,以下是Strve.js 最新文檔地址,歡迎瀏覽。

https://maomincoding.github.io/strvejs-doc/

 

責(zé)任編輯:姜華 來源: 前端歷劫之路
相關(guān)推薦

2019-09-19 09:02:52

開發(fā)者技能工具

2021-12-23 06:07:21

前端技術(shù)編程

2023-11-21 17:36:04

OpenFeignSentinel

2022-04-20 06:56:33

Strve.js前端

2021-12-16 23:40:33

部署ReactTypeScript

2021-08-17 21:52:04

工具

2018-01-29 20:12:11

python翻譯命令行

2021-05-21 05:22:52

腳手架工具項(xiàng)目

2022-03-07 10:27:03

Linux開源社區(qū)

2021-01-07 05:34:07

腳手架JDK緩存

2019-12-25 15:20:48

前端腳手架命令

2016-07-05 16:30:10

碳云智能數(shù)字化生命

2020-04-09 10:25:18

Java 開發(fā)者神器

2018-08-30 16:08:37

Node.js腳手架工具

2018-06-11 14:39:57

前端腳手架工具node.js

2014-08-15 09:36:06

2010-05-21 18:30:02

2019-08-09 10:52:58

Linux內(nèi)核Spark

2016-06-30 11:25:52

VisualNET開發(fā)

2025-05-26 08:45:00

AvueVue.js前端
點(diǎn)贊
收藏

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

中文字幕视频观看| 色之综合天天综合色天天棕色| 久久久久久久麻豆| 榴莲视频成人app| 亚洲国产欧美另类丝袜| 免费一区二区三区在在线视频| 日韩不卡高清视频| 欧美在线精品一区| 日韩国产精品一区| 污污网站免费看| 欧美xxxx做受欧美88bbw| 不卡高清视频专区| 国产在线观看一区二区三区| 国产真实乱人偷精品视频| 亚洲人成亚洲精品| 日韩西西人体444www| 亚洲 高清 成人 动漫| 1024国产在线| av色综合久久天堂av综合| 国产精品一区二区三区成人| 日本一区二区不卡在线| 99精品网站| 亚洲精品一区二三区不卡| 日韩av.com| 在线日韩影院| 亚洲成在人线在线播放| 中文字幕在线观看一区二区三区| 五月婷婷丁香花| 国产乱码一区二区三区| 国产精品美女www| 69成人免费视频| 狠狠色丁香久久综合频道| 中文字幕一区日韩电影| 免费污网站在线观看| 成午夜精品一区二区三区软件| 欧美日韩免费观看一区三区| 久久国产乱子伦免费精品| 欧美性受ⅹ╳╳╳黑人a性爽| 国产精品大尺度| 日本精品一区二区三区高清 久久 日本精品一区二区三区不卡无字幕 | 久久久精品一区二区| 国产性生活毛片| 天堂精品久久久久| 欧美一区二区三区小说| 色婷婷成人在线| 小黄鸭精品aⅴ导航网站入口| 亚洲福利视频一区二区| 青青草视频国产| 国产黄色在线免费观看| 中文字幕色av一区二区三区| 三级三级久久三级久久18| 人操人视频在线观看| 国产成人免费网站| av电影成人| 亚洲h视频在线观看| 国产成人综合在线播放| 1区1区3区4区产品乱码芒果精品| 国产一区二区小视频| 免费人成在线不卡| 国产在线精品成人一区二区三区| 懂色av蜜臀av粉嫩av分享吧最新章节| 男女av一区三区二区色多| 97视频在线观看播放| 日韩精品在线免费看| 99精品福利视频| 欧美在线欧美在线| 中文字幕精品无| 日本三级亚洲精品| 国产综合香蕉五月婷在线| 国产精品毛片一区二区在线看舒淇 | 天天躁日日躁狠狠躁欧美巨大小说| 精品久久久久久亚洲综合网| 99免费观看视频| 特黄特色欧美大片| 亚洲天堂第一页| 中国1级黄色片| 亚洲午夜精品一区 二区 三区| 超薄丝袜一区二区| 日本一区二区三区免费视频| 久久精品动漫| 成人写真视频福利网| jizz中国少妇| 97se亚洲国产综合自在线不卡| 欧美日韩一区二区三区在线视频 | 久久av资源| 色诱女教师一区二区三区| 欧美成人一二三区| 亚洲欧美日韩视频二区| 国产精品美女免费| 人妻中文字幕一区| 中文字幕不卡在线播放| 91国在线高清视频| 成人午夜视屏| 欧美一卡2卡三卡4卡5免费| 男人网站在线观看| 日韩欧美精品一区| 国外成人在线视频| 中文字幕人妻一区二区在线视频| 国产精品一区三区| 欧美二级三级| av电影免费在线观看| 日韩欧美中文在线| 黄色一级片免费播放| 欧美天堂社区| 久久综合久久八八| 福利网址在线观看| 国产白丝网站精品污在线入口| 欧美久久综合性欧美| 3d玉蒲团在线观看| 在线观看91视频| 精品国产免费久久久久久婷婷| 欧美精美视频| 午夜精品久久久久久久久久久久久 | 中日韩高清电影网| 欧美亚洲国产一区在线观看网站 | 国产日韩欧美精品在线| 日韩精品免费一区| 激情久久一区二区| 亚洲精品资源美女情侣酒店| 久草国产在线视频| 麻豆国产精品官网| 你懂的视频在线一区二区| 亚洲色图美国十次| 欧美另类高清zo欧美| 成人精品999| 一区二区高清| 成人综合电影| 日本网站在线免费观看视频| 色哟哟精品一区| 五月天激情小说| 综合久久99| 成人网页在线免费观看| 91网页在线观看| 色又黄又爽网站www久久| 亚洲精品乱码久久久久久蜜桃图片| 国产精品久久久久久久免费观看| 国产成人在线一区| 色中色在线视频| 亚洲最大色网站| 亚洲视频在线不卡| 亚州av乱码久久精品蜜桃| 国产美女精品视频免费观看| 二区三区在线| 91国偷自产一区二区三区观看| 中文字幕影片免费在线观看| 亚洲国产专区校园欧美| 国产伦精品一区二区三区免费视频 | 国产成人澳门| 欧美激情奇米色| 亚洲va久久久噜噜噜无码久久| 亚洲欧美日韩一区二区三区在线观看| 午夜剧场在线免费观看| 成人影视亚洲图片在线| 国产日本欧美一区二区三区在线| a视频网址在线观看| 欧美三级电影精品| 国精产品视频一二二区| 久久国产精品99久久久久久老狼| 一本久道久久综合| 北岛玲精品视频在线观看| 久久精品亚洲精品| 99久久免费国产精精品| 一区二区在线观看视频在线观看| 国模大尺度视频| 狠狠久久婷婷| 女同一区二区| 99久久婷婷国产综合精品首页| 中文字幕在线成人| 国产男女裸体做爰爽爽| 亚洲自拍偷拍综合| www.88av| 日产国产高清一区二区三区| 一区二区三区在线视频111| 亚洲一区二区av| 日韩中文字幕在线| wwwav在线播放| 精品久久久久久久久久国产| 国产jk精品白丝av在线观看| 美洲天堂一区二卡三卡四卡视频| 男同互操gay射视频在线看| 给我免费播放日韩视频| 国产精品久久久久7777婷婷| 国精产品一区| 亚洲国产精品久久久久秋霞不卡| www.五月婷婷.com| 亚洲人成网站影音先锋播放| 亚洲高清无码久久| 免费看欧美女人艹b| 成人在线免费观看网址| 欧美偷窥清纯综合图区| 国产精品久久久久久一区二区| 国产成人午夜| 亚洲欧美日韩精品久久| 国产在成人精品线拍偷自揄拍| 亚洲一二三四久久| www..com.cn蕾丝视频在线观看免费版 | 97精品久久久| 在线免费观看黄色av| 日韩欧美激情四射| 亚洲中文字幕无码爆乳av| 亚洲人成网站影音先锋播放| 性欧美13一14内谢| 国产成人免费av在线| 欧美黑人又粗又大又爽免费| 欧美日韩国产探花| 日本一区二区三区四区高清视频 | 欧美国产成人精品一区二区三区| 国产精品国产三级国产aⅴ入口 | 一二三区精品视频| 先锋影音av在线| 99久久国产综合精品麻豆| 爱豆国产剧免费观看大全剧苏畅| 亚洲一区日韩| 99久久免费观看| 国产精品久久占久久| 欧美日韩系列| 牛牛精品成人免费视频| 97碰碰视频| 国产精品毛片无码| 国产精品免费久久久| 免费成人在线电影| 欧美黑人极品猛少妇色xxxxx| 欧美性天天影视| 亚洲色图色老头| 日韩中文字幕免费观看| 9191国产精品| 亚洲一区二区三区高清视频| 一本高清dvd不卡在线观看| 麻豆91精品91久久久| 亚洲免费在线观看视频| www.涩涩爱| 国产欧美精品一区二区三区四区| 性色av蜜臀av色欲av| 成人高清伦理免费影院在线观看| 99999精品| 精品一区二区免费在线观看| www.亚洲高清| 日韩成人伦理电影在线观看| 丝袜老师办公室里做好紧好爽 | 孩xxxx性bbbb欧美| 久久香蕉一区| 久久久久久久久爱| 久草在线视频网站| 欧美极品少妇xxxxⅹ喷水| 日韩电影免费观看| 久久久久中文字幕| www.51av欧美视频| 91国自产精品中文字幕亚洲| 成人影院在线视频| 97成人在线视频| 日韩av影片| 国产91久久婷婷一区二区| 日本欧美日韩| 国产精品视频免费在线观看| 日本免费一区二区三区等视频| 国产欧美一区二区三区久久| 婷婷精品久久久久久久久久不卡| 国产在线精品成人一区二区三区| www.久久99| 成人免费看片网站| 久久国产精品免费精品3p| 精品一区二区三区国产| 伊人春色之综合网| 亚洲高清视频一区二区| 国产精品99久久久久久动医院| 亚洲国产精品影视| 国语精品一区| 波多野结衣家庭教师在线播放| 香蕉av777xxx色综合一区| 嫩草av久久伊人妇女超级a| 麻豆国产一区二区| 中文字幕人妻熟女在线| 91片在线免费观看| 特级西西人体高清大胆| 亚洲男帅同性gay1069| 日韩精品一区二区三区国语自制| 日本乱人伦aⅴ精品| 亚洲视频一区在线播放| 欧美成人综合网站| 日本护士...精品国| 日韩在线视频免费观看| 黄色影院在线看| 日本三级久久久| 国产精品视频一区视频二区| 国内视频一区| 久久国产电影| 131美女爱做视频| 精品在线播放午夜| 天堂www中文在线资源| 欧美高清一级片在线观看| 欧美黑人精品一区二区不卡| 欧美日韩另类在线| 国产绿帽刺激高潮对白| 日韩激情av在线播放| 欧美69xxxx| 欧美在线激情网| 一区二区三区四区高清视频| 欧美日韩亚洲免费| 黄色成人精品网站| 国产色视频在线播放| 成人精品在线视频观看| 午夜黄色福利视频| 午夜精品久久久| 国产人妻精品一区二区三区| 日韩精品在线视频观看| 在线播放免费av| 国产精品爽爽ⅴa在线观看| 盗摄牛牛av影视一区二区| 亚洲日本精品国产第一区| 中文日韩欧美| 好吊操视频这里只有精品| 国产三级三级三级精品8ⅰ区| 欧美日韩免费一区二区| 欧美日韩综合色| 欧美白人做受xxxx视频| 欧美激情小视频| 91精品麻豆| 亚洲国产另类久久久精品极度| 一本色道88久久加勒比精品| 俄罗斯女人裸体性做爰| 国产精品久久777777| 久久久精品毛片| 日韩av网站导航| 国产乱码在线| 5566av亚洲| 亚洲成av人电影| 五月婷婷激情久久| 国产婷婷色一区二区三区| 91国产丝袜播放在线| 精品国产免费人成电影在线观看四季| 9色在线视频网站| 国产精品福利小视频| 你懂的视频欧美| 337p粉嫩大胆噜噜噜鲁| av在线不卡电影| 国产精品第九页| 精品国产网站在线观看| 里番在线播放| 国产一区二区三区高清| 激情丁香综合| 韩国av中国字幕| 亚洲成人av在线电影| 黄色av网站免费在线观看| 欧美大片在线免费观看| 影音先锋欧美激情| av在线观看地址| 成年人午夜久久久| 成年人免费看毛片| 亚洲美女av在线| 偷拍视频一区二区三区| 日本一区二区久久精品| 欧美96一区二区免费视频| 国产精品久久久久久成人| 欧美精品一二三区| 最新黄网在线观看| 高清av免费一区中文字幕| 亚洲午夜伦理| 中文字幕丰满孑伦无码专区| 欧美午夜电影在线| 懂色av中文在线| 91在线国产电影| 亚洲国产高清视频| 极品人妻一区二区三区| 在线看日本不卡| 精品麻豆一区二区三区| 99在线视频播放| 午夜影院日韩| 女教师淫辱の教室蜜臀av软件| 欧美美女一区二区在线观看| а√中文在线8| 国产青春久久久国产毛片 | 亚洲天堂国产视频| 18成人在线视频| 亚洲精品一级片| 日本高清久久天堂| 91久久国产| 中国一级特黄录像播放| 91福利精品第一导航| 毛片激情在线观看| 国产中文一区二区| 奇米888四色在线精品| 日本精品人妻无码77777| 亚洲精品电影在线观看| 欧美高清你懂的| 青青草精品视频在线| 国产午夜精品福利| 国产高清视频免费| 清纯唯美日韩制服另类| 香蕉av一区二区| 久久久无码人妻精品一区| 欧美日韩国产经典色站一区二区三区 | 国产偷国产偷亚洲高清人白洁| 国产深喉视频一区二区| 18一19gay欧美视频网站| 久久影视一区| 亚洲色图14p| 欧美一级黄色录像| 第四色男人最爱上成人网| 久久亚洲a v| 中文字幕精品一区二区精品绿巨人|