精品欧美一区二区三区在线观看 _久久久久国色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)

欧美精品一区三区| 成人免费视频视频| 中文字幕无线精品亚洲乱码一区 | 久久嫩草精品久久久精品| 日产精品99久久久久久| 欧美亚洲色综久久精品国产| 亚洲网站免费| 亚洲国产欧美在线| 欧美三级华人主播| 亚洲天堂手机在线| 欧美视频久久| 在线看日韩av| 亚洲av无一区二区三区久久| 麻豆国产在线| 亚洲欧美自拍偷拍色图| 国产一区免费在线| www.亚洲激情| 亚洲影视一区| 精品香蕉一区二区三区| www.色就是色.com| 91福利在线尤物| 欧美激情综合网| 99视频在线播放| 99精品人妻国产毛片| 亚州av一区| 制服丝袜成人动漫| 国产男女无遮挡| 成人在线免费观看| 99视频热这里只有精品免费| 国产精品极品美女在线观看免费 | 一区二区欧美在线| 亚洲熟妇一区二区| 韩日精品一区| 亚洲成av人片一区二区| 国产精品h视频| 完全免费av在线播放| 欧美性色综合网| 国产成人在线小视频| 国产精品免费观看| 99久久精品国产观看| 成人激情综合网| 欧美一区二区三区网站| 黑丝一区二区三区| 俺去啦;欧美日韩| 国产熟妇久久777777| 成人激情自拍| 日韩午夜av电影| 91小视频网站| 午夜av成人| 狠狠久久亚洲欧美专区| 波多野结衣av一区二区全免费观看| chinese偷拍一区二区三区| 波多野结衣视频一区| 91嫩草国产在线观看| 亚洲天堂网视频| 免费成人小视频| 国产精品久久久久久久久久久久久久| 欧美h在线观看| 亚洲免费一区二区| 2019日本中文字幕| 青青青视频在线免费观看| 欧美另类69xxxxx| 在线不卡国产精品| 日本精品久久久久中文| av一区二区高清| 国产一区二区三区在线观看视频 | 天天舔天天操天天干| 久久精品一级| 91精品久久久久久蜜臀| 精品综合久久久久| 97久久精品一区二区三区的观看方式| 欧美专区日韩专区| 一路向西2在线观看| 国产亚洲精彩久久| 51精品秘密在线观看| 一级做a爱视频| 国产精品免费精品自在线观看| 欧美精品在线视频| 鲁一鲁一鲁一鲁一av| 色综合视频一区二区三区44| 777欧美精品| 岛国大片在线免费观看| baoyu135国产精品免费| 亚洲电影在线看| 国产特黄级aaaaa片免| 精品五月天堂| 亚洲视频在线观看| 国产第一页精品| 欧美一区网站| 孩xxxx性bbbb欧美| 日韩精品一区二区三区国语自制| 99精品免费网| 热99在线视频| 一级α片免费看刺激高潮视频| 精品一区二区三区影院在线午夜 | 久久精品人人做人人爽| 免费无码毛片一区二区app| 日韩香蕉视频| 国产精品美女www| av老司机久久| ww久久中文字幕| 中文网丁香综合网| 1区2区3区在线| 在线观看欧美日本| av电影中文字幕| 亚洲午夜久久| 久久久精品在线观看| 久久精品视频9| 日韩av在线发布| 国产精品九九九| 亚洲精品网站在线| 欧美国产精品中文字幕| 黄色特一级视频| 成人免费网站www网站高清| 91精品国产高清一区二区三区| 中文字幕精品久久久| 日韩精品一区二区久久| 久久久视频在线| 中文字幕在线观看免费| www.亚洲色图.com| 中文字幕久久综合| 欧美大片免费高清观看| 日韩精品一区二区三区四区视频| 成人黄色a级片| 亚洲国产国产亚洲一二三| 91精品国产综合久久久久久久久| 亚洲av成人精品毛片| 亚洲欧美日韩国产手机在线| 国产在线青青草| 精品国产亚洲一区二区三区大结局| 亚洲国产成人精品久久久国产成人一区| 日b视频在线观看| 亚洲国产精品久久久天堂| 日韩av电影在线免费播放| 成人激情四射网| 亚洲欧洲在线观看av| 草草草在线视频| 日本福利一区| 久久久久成人精品| 国产精品一级二级| 国产精品视频免费| 成人亚洲视频在线观看| 青青草这里只有精品| 欧美黑人性视频| 国产三级小视频| 国产精品视频麻豆| 在线免费观看视频黄| 尤物tv在线精品| 18一19gay欧美视频网站| 日本wwwxxxx| 国产成人av资源| 在线精品日韩| 国产精品一区二区三区四区在线观看| 日韩中文字幕在线| 国产内射老熟女aaaa∵| 亚洲欧美日韩国产综合在线| 国产成人强伦免费视频网站| 欧美一区影院| 国产精品视频免费一区| 日韩伦理在线| 一区二区三区高清国产| 在线观看免费视频一区| 亚洲三级在线免费| 亚洲精品乱码久久久久久蜜桃欧美| 一级做a爱片性色毛片| 99精品黄色片免费大全| 无码aⅴ精品一区二区三区浪潮 | 日本女人高潮视频| 2023国产精华国产精品| 97视频在线看| 国内精品一区视频| 欧美久久一二区| 免费一级片在线观看| 91香蕉视频在线| 亚洲一级片网站| 欧美日韩亚洲国产精品| 精品在线观看一区二区| 国产一区二区三区影视| 欧美成人激情视频免费观看| 色欲久久久天天天综合网 | 少妇性饥渴无码a区免费| 欧美日韩在线网站| 99免费在线观看视频| 国产精品专区免费| 久久亚洲国产精品| 青青草免费在线视频| 欧美日韩国产一级| 日韩精品――中文字幕| 中日韩av电影| 男人网站在线观看| 美日韩一区二区| 欧美在线一区视频| 91久久夜色精品国产按摩| 国产一区福利视频| 91精品国产自产观看在线| 欧美一级淫片播放口| 免费在线午夜视频| 国产丝袜一区二区三区| a网站在线观看| 欧美专区在线观看一区| 1级黄色大片儿| 亚洲视频 欧洲视频| 久久久久久国产精品无码| 国产成人h网站| 日本黄色福利视频| 亚洲欧美不卡| 国产91视频一区| 欧美第一精品| 日本一区视频在线播放| 动漫视频在线一区| 亚洲精品欧美日韩专区| 日韩三区免费| 欧美亚洲激情在线| 久草免费在线色站| 日韩色av导航| 草草影院在线观看| 亚洲精品成人网| 囯产精品久久久久久| 欧美精品aⅴ在线视频| 日本免费在线观看视频| 亚洲h精品动漫在线观看| 一区二区国产精品精华液| 亚洲国产高清不卡| 六月婷婷七月丁香| www.66久久| av av在线| 国产99久久久国产精品潘金| 中日韩av在线播放| 麻豆国产精品官网| 欧美精品性生活| 日韩成人精品视频| 国产一二三四在线视频| 在线视频日韩| 成熟丰满熟妇高潮xxxxx视频| 韩国精品一区二区三区| 男人c女人视频| 亚洲性视频h| 草b视频在线观看| 亚洲激情综合| 久久综合色视频| av不卡免费看| 鲁一鲁一鲁一鲁一澡| 99国产精品视频免费观看一公开 | 日韩电影网1区2区| 午夜免费高清视频| 另类专区欧美蜜桃臀第一页| 亚洲36d大奶网| 久久99热狠狠色一区二区| 九九热精品国产| 国产成人一区在线| 成人免费看片载| 成a人片国产精品| 三级黄色片网站| 久久久久久久久久久久久女国产乱 | 色网站在线播放| 欧美性生活大片免费观看网址| 欧美日韩乱国产| 在线观看成人小视频| 夜夜嗨aⅴ一区二区三区| 91精品国产aⅴ一区二区| 午夜精品久久久久久久99热黄桃| 亚洲成人av在线| 免费在线超碰| 日韩中文字幕免费看| 国产黄色在线网站| 久久久久国产视频| 欧美激情喷水| 亚洲一区二区三区成人在线视频精品| 国产一区二区三区| 久久av一区二区三区漫画| 精品久久网站| 糖心vlog在线免费观看| 亚洲人成高清| 日本成人在线免费视频| 极品少妇xxxx精品少妇| 精品人妻伦一二三区久| 久久久久国产精品厨房| 国产喷水在线观看| 亚洲一区二区在线视频| 免费看污视频的网站| 欧美精品在线一区二区| 天天综合天天色| xxx成人少妇69| 在线观看网站免费入口在线观看国内| 国产精品男人的天堂| 91亚洲无吗| 热re99久久精品国99热蜜月| 亚洲二区三区不卡| 精品久久久久久无码国产| 国产美女精品在线| 国产精品亚洲无码| 亚洲蜜桃精久久久久久久| 久久精品视频7| 欧美一区二区三区免费视频 | 欧洲黄色一级视频| 久久99精品网久久| 成人免费毛片糖心| 亚洲福利视频一区| 亚洲无码久久久久| 精品视频久久久久久久| 国产cdts系列另类在线观看| 奇米一区二区三区四区久久| 欧美二区观看| 亚洲欧美日韩在线综合| 在线亚洲国产精品网站| 捷克做爰xxxⅹ性视频| 久久久久国产精品麻豆ai换脸 | 国产黄色小视频在线| 日产精品99久久久久久| 成人性生交大片免费看96| 一区二区三区在线观看www| 久久久水蜜桃av免费网站| 亚洲av午夜精品一区二区三区| 国产精品视频看| 人人妻人人爽人人澡人人精品| 亚洲精品一区二区三区在线观看| av网站无病毒在线| 日本久久久久久久久久久| 精品女人视频| 欧美高清中文字幕| 国产成人精品亚洲777人妖| 国产精品视频看看| 欧美日韩一区二区三区在线| 男人天堂亚洲二区| 欧美亚洲国产日本| 欧美三级午夜理伦三级小说| 福利视频免费在线观看| 国产精品综合二区| www.97视频| 欧美精品免费视频| 日本三级在线视频| 国产精品视频成人| 欧美精品一区二区三区中文字幕 | 精品一区二区三区亚洲| 亚洲免费视频一区| 免费一级欧美片在线观看| 成人免费无遮挡无码黄漫视频| 欧美日韩在线免费观看| 天天操天天插天天射| 欧美精品久久久久久久久久| jizz国产精品| 久久久久免费看黄a片app| www.日韩在线| 中文字幕在线字幕中文| 亚洲精美色品网站| 国产深夜视频在线观看| 成人自拍视频网站| 日韩视频一区| 久久精品成人av| 欧美中文字幕久久| 在线观看黄av| 亚洲www在线| 欧美黄色一区| 亚洲婷婷在线观看| 欧美日韩免费在线观看| 麻豆影视在线| 国产综合福利在线| 激情av一区| 国产交换配乱淫视频免费| 欧美伊人精品成人久久综合97| a中文在线播放| 亚洲综合小说区| 一区二区精品| 免费一级黄色录像| 日韩一本二本av| ****av在线网毛片| 欧美三级网色| 国内精品伊人久久久久av影院| 538精品在线观看| 国产午夜精品久久久| 国产精品99精品一区二区三区∴| 天天干天天色天天爽| 99视频国产精品| 一本到在线视频| 性日韩欧美在线视频| av伊人久久| 免费看91视频| 色婷婷国产精品久久包臀| 欧美被日视频| 精品国产电影| 久色婷婷小香蕉久久| 精品视频一区二区在线观看| 精品调教chinesegay| 韩国三级成人在线| 激情六月丁香婷婷| 最新中文字幕一区二区三区| 欧美一级一区二区三区| 国产精品天天狠天天看| 136国产福利精品导航网址| 一级肉体全黄裸片| 欧美大片日本大片免费观看| 天天综合网天天| 中文字幕日韩精品无码内射| 久久久久久久久一| 蜜桃av中文字幕| 国产精品一区二区电影| 亚洲精品乱码| 免费成人深夜夜行网站| 日韩精品极品在线观看| 日韩精品视频中文字幕|