日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从零开始发布自己的NPM包

發布時間:2023/12/9 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从零开始发布自己的NPM包 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家好,我是若川。持續組織了6個月源碼共讀活動,感興趣的可以點此加我微信 ruochuan02?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。歷史面試系列

在Verdaccio搭建npm私有服務器中,我們介紹了如何搭建一個Npm私有服務器;服務器搭建完成后,我們本文來學習一下如何上傳我們自己的npm包。

前端模塊化作為前端必備的一個技能,已經在前端開發中不可或缺;而模塊化帶來項目的規模不斷變大,項目的依賴越來越多;隨著項目的增多,如果每個模塊都通過手動拷貝的方式無異于飲鴆止渴,我們可以把功能相似的模塊或組件抽取到一個npm包中;然后上傳到私有npm服務器,不斷迭代npm包來更新管理所有項目的依賴。

npm包的基本了解

首先我們來了解一下實現一個npm包需要包含哪些內容。

打包

通常,我們把打包好的一些模塊文件放在一個目錄下,便于統一進行加載;是的,npm包也是需要進行打包的,雖然也能直接寫npm包模塊的代碼(并不推薦),但我們經常會在項目中用到typescript、babel、eslint、代碼壓縮等等功能,因此我們也需要對npm包進行打包后再進行發布。

在深入對比Webpack、Parcel、Rollup打包工具中,我們總結了,rollup相比于webpack更適合打包一些第三方的類庫,因此本文主要通過rollup來進行打包。

npm域級包

隨著npm包越來越多,而且包名也只能是唯一的,如果一個名字被別人占了,那你就不能再使用這個名字;假設我想要開發一個utils包,但是張三已經發布了一個utils包,那我的包名就不能叫utils了;此時我們可以加一些連接符或者其他的字符進行區分,但是這樣就會讓包名不具備可讀性。

在npm的包管理系統中,有一種scoped packages機制,用于將一些npm包以@scope/package的命名形式集中在一個命名空間下面,實現域級的包管理。

域級包不僅不用擔心會和別人的包名重復,同時也能對功能類似的包進行統一的劃分和管理;比如我們用vue腳手架搭建的項目,里面就有@vue/cli-plugin-babel、@vue/cli-plugin-eslint等等域級包。

我們在初始化項目時可以使用命令行來添加scope:

npm?init?--scope=username

相同域級范圍內的包會被安裝在相同的文件路徑下,比如node_modules/@username/,可以包含任意數量的作用域包;安裝域級包也需要指明其作用域范圍:

npm?install?@username/package

在代碼中引入時同樣也需要作用域范圍:

require("@username/package")

加載規則

在npm包中的package.json文件,我們經常會看到main、jsnext:main、module、browser等字段,那么這些字段都代表了什么意思呢?其實這跟npm包的工作環境有關系,我們知道,npm包分為以下幾種類型的包:

  • 只能在瀏覽器端使用的

  • 只能在服務器端使用的

  • 瀏覽器/服務器端都可使用

假如我們現在開發一個npm包,既要支持瀏覽器端,也要支持服務器端(比如axios、lodash等),需要在不同的環境下加載npm包的不同入口文件,只通過一個字段已經不能滿足需求。

首先我們來看下main字段,它是nodejs默認文件入口, 支持最廣泛,主要使用在引用某個依賴包的時候需要此屬性的支持;如果不使用main字段的話,我們可能需要這樣來引用依賴:

import('some-module/dist/bundle.js')

所以它的作用是來告訴打包工具,npm包的入口文件是哪個,打包時讓打包工具引入哪個文件;這里的文件一般是commonjs(cjs)模塊化的。

有一些打包工具,例如webpack或rollup,本身就能直接處理import導入的esm模塊,那么我們可以將模塊文件打包成esm模塊,然后指定module字段;由包的使用者來決定如何引用。

jsnext:main和module字段的意義是一樣的,都可以指定esm模塊的文件;但是jsnext:main是社區約定的字段,并非官方,而module則是官方約定字段,因此我們經常將兩個字段同時使用。

在Webpack配置全解析中我們介紹到,mainFields就是webpack用來解析模塊的,默認會按照順序解析browser、module、main字段。

有時候我們還想要寫一個同時能夠跑在瀏覽器端和服務器端的npm包(比如axios),但是兩者在運行環境上還是有著細微的區別,比如瀏覽器請求數據用的是XMLHttpRequest,而服務器端則是http或者https;那么我們要怎樣來區分不同的環境呢?

除了我們可以在代碼中對環境參數進行判斷(比如判斷XMLHttpRequest是否為undefined),也可以使用browser字段,在瀏覽器環境來替換main字段。browser的用法有以下兩種,如果browser為單個的字符串,則替換main成為瀏覽器環境的入口文件,一般是umd模塊的:

{"browser":?"./dist/bundle.umd.js" }

browser還可以是一個對象,來聲明要替換或者忽略的文件;這種形式比較適合替換部分文件,不需要創建新的入口。key是要替換的module或者文件名,右側是替換的新的文件,比如在axios的packages.json中就用到了這種替換:

{"browser":?{"./lib/adapters/http.js":?"./lib/adapters/xhr.js"} }

打包工具在打包到瀏覽器環境時,會將引入來自./lib/adapters/http.js的文件內容替換成./lib/adapters/xhr.js的內容。

在有一些包中我們還會看到types字段,指向types/index.d.ts文件,這個字段是用來包含了這個npm包的變量和函數的類型信息;比如我們在使用lodash-es包的時候,有一些函數的名稱想不起來了,只記得大概的名字;比如輸入fi就能自動在編譯器中聯想出fill或者findIndex等函數名稱,這就為包的使用者提供了極大的便利,不需要去查看包的內容就能了解其導出的參數名稱,為用戶提供了更加好的IDE支持。

發布哪些文件

在npm包中,我們可以選擇哪些文件發布到服務器中,比如只發布壓縮后的代碼,而過濾源代碼;我們可以通過配置文件來進行指定,可以分為以下幾種情況:

  • 存在.npmignore文件,以.npmignore文件為準,在文件中的內容都會被忽略,不會上傳;即使有.gitignore文件,也不會生效。

  • 不存在.npmignore文件,以.gitignore文件為準,一般是無關內容,例如.vscode等環境配置相關的。

  • 不存在.npmignore也不存在.gitignore,所有文件都會上傳。

  • package.json中存在files字段,可以理解為files為白名單。

ignore相當于黑名單,files字段就是白名單,那么當兩者內容沖突時,以誰為準呢?答案是files為準,它的優先級最高。

我們可以通過npm pack命令進行本地模擬打包測試,在項目根目錄下就會生成一個tgz的壓縮包,這就是將要上傳的文件內容。

項目依賴

在package.json文件中,所有的依賴包都會在dependencies和devDependencies字段中進行配置管理:

  • dependencies:表示生產環境下的依賴管理,--save 簡寫 -S;

  • devDependencies:表示開發環境下的依賴管理,--save-dev 簡寫 -D;

dependencies字段指定了項目上線后運行所依賴的模塊,可以理解為我們的項目在生產環境運行中要用到的東西;比如vue、jquery、axios等,項目上線后還是要繼續使用的依賴。

devDependencies字段指定了項目開發所需要的模塊,開發環境會用到的東西;比如webpack、eslint等等,我們打包的時候會用到,但是項目上線運行時就不需要了,所以放到devDependencies中去就好了。

除了dependencies和devDependencies字段,我們在一些npm包中還會看到peerDependencies字段,沒有寫過npm插件的童鞋可能會對這個字段比較陌生,它和上面兩個依賴有什么區別呢?

假設我們的項目MyProject,有一個依賴PackageA,它的package.json中又指定了對PackageB的依賴,因此我們的項目結構是這樣的:

MyProject |-?node_modules|-?PackageA|-?node_modules|-?PackageB

那么我們在MyProject中是可以直接引用PackageA的依賴的,但如果我們想直接使用PackageB,那對不起,是不行的;即使PackageB已經被安裝了,但是node只會在MyProject/node_modules目錄下查找PackageB。

為了解決這樣問題,peerDependencies字段就被引入了,通俗的解釋就是:如果你安裝了我,你最好也安裝以下依賴。比如上面如果我們在PackageA的package.json中加入下面代碼:

{"peerDependencies":?{"PackageB":?"1.0.0"} }

這樣如果你安裝了PackageA,那會自動安裝PackageB,會形成如下的目錄結構:

MyProject |-?node_modules|-?PackageA|-?PackageB

我們在MyProject項目中就能愉快的使用PackageA和PackageB兩個依賴了。

比如,我們熟悉的element-plus組件庫,它本身不可能單獨運行,必須依賴于vue3環境才能運行;因此在它的package.json中我們看到它對宿主環境的要求:

{"peerDependencies":?{"vue":?"^3.2.0"}, }

這樣我們看到它在組件中引入的vue的依賴,其實都是宿主環境提供的vue3依賴:

import?{?ref,?watch,?nextTick?}?from?'vue'

許可證

license字段使我們可以定義適用于package.json所描述代碼的許可證。同樣,在將項目發布到npm注冊時,這非常重要,因為許可證可能會限制某些開發人員或組織對軟件的使用。擁有清晰的許可證有助于明確定義該軟件可以使用的術語。

借用知乎上Max Law的一張圖來解釋所有的許可證:

許可證

版本號

npm包的版本號也是有規范要求的,通用的就是遵循semver語義化版本規范,版本格式為:major.minor.patch,每個字母代表的含義如下:

  • 主版本號(major):當你做了不兼容的API修改

  • 次版本號(minor):當你做了向下兼容的功能性新增

  • 修訂號(patch):當你做了向下兼容的問題修正

  • 先行版本號是加到修訂號的后面,作為版本號的延伸;當要發行大版本或核心功能時,但不能保證這個版本完全正常,就要先發一個先行版本。

    先行版本號的格式是在修訂版本號后面加上一個連接號(-),再加上一連串以點(.)分割的標識符,標識符可以由英文、數字和連接號([0-9A-Za-z-])組成。例如:

    1.0.0-alpha 1.0.0-alpha.1 1.0.0-0.3.7

    常見的先行版本號有:

  • alpha:不穩定版本,一般而言,該版本的Bug較多,需要繼續修改,是測試版本

  • beta:基本穩定,相對于Alpha版已經有了很大的進步,消除了嚴重錯誤

  • rc:和正式版基本相同,基本上不存在導致錯誤的Bug

  • release:最終版本

  • 版本號

    每個npm包的版本號都是唯一的,我們每次更新npm包后,都是需要更新版本號,否則會報錯提醒:

    版本號報錯?

    當主版本號升級后,次版本號和修訂號需要重置為0,次版本號進行升級后,修訂版本需要重置為0。

    ?

    但是如果每次都要手動來更新版本號,那可就太麻煩了;那么是否有命令行能來自動更新版本號呢?由于版本號的確定依賴于內容決定的主觀性的動作,因此不能完全做到全自動化更新,誰知道你是改了大版本還是小版本,因此只能通過命令行實現半自動操作;命令的取值和語義化的版本是對應的,會在相應的版本上加1:

    命令行更新版本號

    在package.json的一些依賴的版本號中,我們還會看到^、~或者>=這樣的標識符,或者不帶標識符的,這都代表什么意思呢?

  • 沒有任何符號:完全百分百匹配,必須使用當前版本號

  • 對比符號類的:>(大于) ?>=(大于等于) <(小于) <=(小于等于)

  • 波浪符號~:固定主版本號和次版本號,修訂號可以隨意更改,例如~2.0.0,可以使用 2.0.0、2.0.2 、2.0.9 的版本。

  • 插入符號^:固定主版本號,次版本號和修訂號可以隨意更改,例如^2.0.0,可以使用 2.0.1、2.2.2 、2.9.9 的版本。

  • 任意版本*:對版本沒有限制,一般不用

  • 或符號:||可以用來設置多個版本號限制規則,例如 >= 3.0.0 || <= 1.0.0

  • npm包開發

    通過上面對package.json的介紹,相信各位小伙伴已經對npm包有了一定的了解,現在我們就進入代碼實操階段,開發并上傳一個npm包。

    工具類包

    相信不少童鞋在業務開發時都會遇到重復的功能,或者開發相同的工具函數,每次遇到時都要去其他項目中拷貝代碼;如果一個項目的代碼邏輯有優化的地方,需要同步到其他項目,則需要再次挨個項目的拷貝代碼,這樣不僅費時費力,而且還重復造輪子。

    我們可以整合各個項目的需求,開發一個適合自己項目的工具類的npm包,包的結構如下:

    hello-npm |--?lib/(存放打包后的文件) |--?src/(源碼) |--?package.json |--?rollup.config.base.js(rollup基礎配置) |--?rollup.config.dev.js(rollup開發配置) |--?rollup.config.js(rollup正式配置) |--?README.md |--?tsconfig.json

    首先看下package.json的配置,rollup根據開發環境區分不同的配置:

    {"name":?"hello-npm","version":?"1.0.0","description":?"我是npm包的描述","main":?"lib/bundle.cjs.js","jsnext:main":?"lib/bundle.esm.js","module":?"lib/bundle.esm.js","browser":?"lib/bundle.browser.js","types":?"types/index.d.ts","author":?"","scripts":?{"dev":?"npx?rollup?-wc?rollup.config.dev.js","build":?"npx?rollup?-c?rollup.config.js?&&?npm?run?build:types","build:types":?"npx?tsc",},"license":?"ISC" }

    然后配置rollup的base config文件:

    import?typescript?from?"@rollup/plugin-typescript"; import?pkg?from?"./package.json"; import?json?from?"rollup-plugin-json"; import?resolve?from?"rollup-plugin-node-resolve"; import?commonjs?from?"@rollup/plugin-commonjs"; import?eslint?from?"@rollup/plugin-eslint"; import?{?babel?}?from?'@rollup/plugin-babel' const?formatName?=?"hello"; export?default?{input:?"./src/index.ts",output:?[{file:?pkg.main,format:?"cjs",},{file:?pkg.module,format:?"esm",},{file:?pkg.browser,format:?"umd",name:?formatName,},],plugins:?[json(),commonjs({include:?/node_modules/,}),resolve({preferBuiltins:?true,jsnext:?true,main:?true,brower:?true,}),typescript(),eslint(),babel({?exclude:?"node_modules/**"?}),], };

    這里我們將打包成commonjs、esm和umd三種模塊規范的包,然后是生產環境的配置,加入terser和filesize分別進行壓縮和查看打包大小:

    import?{?terser?}?from?"rollup-plugin-terser"; import?filesize?from?"rollup-plugin-filesize";import?baseConfig?from?"./rollup.config.base";export?default?{...baseConfig,plugins:?[...baseConfig.plugins,?terser(),?filesize()], };

    然后是開發環境的配置:

    import?baseConfig?from?"./rollup.config.base"; import?serve?from?"rollup-plugin-serve"; import?livereload?from?"rollup-plugin-livereload";export?default?{...baseConfig,plugins:?[...baseConfig.plugins,serve({contentBase:?"",port:?8020,}),livereload("src"),], };

    環境配置好后,我們就可以打包了

    #?測試環境 npm?run?dev #?生產環境 npm?run?build

    全局包

    還有一類npm包比較特殊,是通過npm i -g [pkg]進行全局安裝的,比如常用的vue create、static-server、pm2等命令,都是通過全局命令安裝的;那么全局npm包如何開發呢?

    我們來實現一個全局命令的計算器功能,新建一個項目然后運行:

    cd?my-calc npm?init?-y

    在package.json中添加bin屬性,它是一個對象,鍵名是告訴node在全局定義一個全局的命令,值則是執行命令的腳本文件路徑,可以同時定義多個命令,這里我們定義一個calc命令:

    {"name":?"my-calc","version":?"1.0.0","description":?"","main":?"index.js","scripts":?{"test":?"echo?\"Error:?no?test?specified\"?&&?exit?1"},"bin":?{"calc":?"./src/calc.js",},"license":?"ISC", }

    命令定義好了,我們來實現calc.js中的內容:

    #!/usr/bin/env?nodeif?(process.argv.length?<=?2)?{console.log("請輸入運算的數字");return; }let?total?=?process.argv.slice(2).map((el)?=>?{let?parseEl?=?parseFloat(el);return?!isNaN(parseEl)???parseEl?:?0;}).reduce((total,?num)?=>?{total?+=?num;return?total;},?0);console.log(`運算結果:${total}`);

    需要注意的是,文件頭部的#!/usr/bin/env node是必須的,告訴node這是一個可執行的js文件,如果不寫會報錯;然后通過process.argv.slice(2)來獲取執行命令的參數,前兩個參數分別是node的運行路徑和可執行腳本的運行路徑,第三個參數開始才是命令行的參數,因此我們在命令行運行來看結果:

    calc?1?2?3?-4

    如果我們的腳本比較復雜,想調試一下腳本,那么每次都需要發布到npm服務器,然后全局安裝后才能測試,這樣比較費時費力,那么有沒有什么方法能夠直接運行腳本呢?這里就要用到npm link命令,它的作用是將調試的npm模塊鏈接到對應的運行項目中去,我們也可以通過這個命令把模塊鏈接到全局。

    在我們的項目中運行命令:

    npm?link

    可以看到全局npm目錄下新增了calc文件,calc命令就指向了本地項目下的calc.js文件,然后我們就可以盡情的運行調試;調試完成后,我們又不需要將命令指向本地項目了,這個時候就需要下面的命令進行解綁操作:

    npm?unlink

    解綁后npm會把全局的calc文件刪除,這時候我們就可以去發布npm包然后進行真正的全局安裝了。

    vue組件庫

    在Vue項目中,我們在很多項目中也會用到公共組件,可以將這些組件提取到組件庫,我們可以仿照element-ui來實現一個我們自己的ui組件庫;首先來構建我們的項目目錄:

    |-?lib |-?src|-?MyButton|-?index.js|-?index.vue|-?index.scss|-?MyInput|-?index.js|-?index.vue|-?index.scss|-?main.js |-?rollup.config.js

    我們構建MyButton和MyInput兩個組件,vue文件和scss不再贅述,我們來看下導出組件的index.js:

    import?MyButton?from?"./index.vue";MyButton.install?=?function?(Vue)?{Vue.component(MyButton.name,?MyButton); }; export?default?MyButton;

    組件導出后在main.js中統一組件注冊:

    import?MyButton?from?"./MyButton/index.js"; import?MyInput?from?"./MyInput/index"; import?{?version?}?from?"../package.json";import?"./MyButton/index.scss"; import?"./MyInput/index.scss";const?components?=?[MyButton,?MyInput];const?install?=?function?(Vue)?{components.forEach((component)?=>?{Vue.component(component.name,?component);}); }; if?(typeof?window?!==?"undefined"?&&?window.Vue)?{install(window.Vue); } export?{?MyButton,?MyInput,?install?}; export?default?{?version,?install?};

    然后配置rollup.config.js:

    import?resolve?from?"rollup-plugin-node-resolve"; import?vue?from?"rollup-plugin-vue"; import?babel?from?"@rollup/plugin-babel"; import?commonjs?from?"@rollup/plugin-commonjs"; import?scss?from?"rollup-plugin-scss"; import?json?from?"@rollup/plugin-json";const?formatName?=?"MyUI"; const?config?=?{input:?"./src/main.js",output:?[{file:?"./lib/bundle.cjs.js",format:?"cjs",name:?formatName,exports:?"auto",},{file:?"./lib/bundle.js",format:?"iife",name:?formatName,exports:?"auto",},],plugins:?[json(),resolve(),vue({css:?true,compileTemplate:?true,}),babel({exclude:?"**/node_modules/**",}),commonjs(),scss(),], }; export?default?config;

    這里我們打包出commonjs和iife兩個模塊規范,一個可以配合打包工具使用,另一個可以直接在瀏覽器中script引入。我們通過rollup-plugin-vue插件來解析vue文件,需要注意的是5.x版本解析vue2,最新的6.x版本解析vue3,默認安裝6.x版本;如果我們使用的是vue2,則需要切換老版本的插件,還需要安裝以下vue的編譯器:

    npm?install?--save-dev?vue-template-compiler

    打包成功后我們就能看到lib目錄下的文件了,我們就能像element-ui一樣,愉快的使用自己的ui組件了,在項目中引入我們的UI:

    /*?全局引入?main.js?*/ import?MyUI?from?"my-ui"; //?引入樣式 import?"my-ui/lib/bundle.cjs.css";Vue.use(MyUI);/*?在組件中按需引入?*/ import?{?MyButton?}?from?"my-ui"; export?default?{components:?{MyButton} }

    如果想要在本地進行調試,也可以使用link命令創建鏈接,首先在my-ui目錄下運行npm link將組件掛載到全局,然后在vue項目中運行下面命令來引入全局的my-ui:

    npm?link?my-ui

    我們會看到下面的輸出表示vue項目中my-ui模塊已經鏈接到my-ui項目了:

    D:\project\vue-demo\node_modules\my-ui? ->? C:\Users\XXXX\AppData\Roaming\npm\node_modules\my-ui ->? D:\project\my-ui

    npm包發布

    我們的npm包完成后就可以準備發布了,首先我們需要準備一個賬號,可以使用--registry來指定npm服務器,或者直接使用nrm來管理:

    npm?adduser npm?adduser?--registry=http://example.com

    然后進行登錄,輸入你注冊的賬號密碼郵箱:

    npm?login

    還可以用下面命令退出當前賬號

    npm?logout

    如果不知道當前登錄的賬號可以用who命令查看身份:

    npm?who?am?i

    登錄成功就可以將我們的包推送到服務器上去了,執行下面命令,會看到一堆的npm notice:

    npm?publish

    如果某版本的包有問題,我們還可以將其撤回

    npm?unpublish?[pkg]@[version]


    ·················?若川簡介?·················

    你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
    從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
    同時,最近組織了源碼共讀活動,幫助3000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。

    識別方二維碼加我微信、拉你進源碼共讀

    今日話題

    略。分享、收藏、點贊、在看我的文章就是對我最大的支持~

    總結

    以上是生活随笔為你收集整理的从零开始发布自己的NPM包的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 久草国产精品 | 午夜男人网 | 91国产在线播放 | 男女午夜视频在线观看 | 国产偷拍一区二区三区 | 国产一区二区视频免费 | 日韩二区在线 | 成 年 人 黄 色 大 片大 全 | 91一区二区三区在线 | 欧美91| 91精品国产色综合久久不卡98口 | 亚洲性自拍| 天堂在线一区二区 | 久久亚洲私人国产精品va | 成人看| 国产精品国产精品国产专区不卡 | 好吊操妞 | 婷婷色在线 | 日本黄网站色大片免费观看 | 午夜欧美在线 | 国产专区精品 | 亚洲激情一区二区 | 日本一区二区高清免费 | 亚洲最大黄色 | 国产亚洲色婷婷久久99精品91 | 2024av视频 | 中文在线a在线 | 国产精品一区二区三区免费看 | 少妇久久久久久被弄高潮 | 91观看视频 | 男插女视频网站 | 一道本不卡视频 | 老女人性生活视频 | 极品尤物一区二区三区 | 国产一区二区网站 | 一级大片免费看 | 黄色电影在线视频 | 久久国产视频网站 | 夜色导航 | 久久久久久国产精品免费播放 | 在线观看免费黄色小视频 | 国产成人在线视频 | 久久这里只有精品久久 | 九九热精品视频在线观看 | 一级黄色在线 | 欧美一区二区三区不卡视频 | 日本人视频69式jzzij | 99国产精品久久久 | 国产三级全黄裸体 | 网站黄在线观看 | 欧美整片sss | 欧美 日韩 国产 精品 | 日美毛片 | 5级黄色片 | www.男女| 午夜啪啪福利视频 | 欧美怡红院 | 欧美日韩成人 | 国产成人无码精品久久久性色 | 日韩欧美亚洲精品 | av最新版天堂资源在线 | 制服丝袜在线看 | 香蕉视频成人在线观看 | 最近的中文字幕 | 国产福利91精品一区二区三区 | 国产精品一品二区三区的使用体验 | 欧美天堂久久 | 日本性爱视频在线观看 | 国内精品视频在线播放 | 人妻丝袜一区二区三区 | 欧美不卡在线视频 | 亚洲成av人片一区二区 | 亚洲久久久| 神马久久香蕉 | 国产女教师一区二区三区 | 中文天堂在线资源 | 1024手机在线观看 | 中文字幕免费在线观看视频 | 精品国产三级片在线观看 | 国产乡下妇女三片 | 成人激情视频在线 | 一区二区三区四区精品视频 | 一个色综合久久 | 最新成人在线 | 国产亚洲综合av | 国产中文字幕在线免费观看 | 国产精品2019 | 午夜在线观看一区 | 久久久一区二区三区 | 国产在线观看a | 午夜视频国产 | 日本美女视频一区 | 欧美裸体xxxx极品少妇 | 最好看的mv中文字幕国语电影 | 久久国产劲爆∧v内射 | 国产免费啪啪 | 日本免费黄色 | 欧美色图亚洲色 | 日韩一区二区影院 |