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

也許這才是你想要的微前端方案

開發(fā) 前端
微前端是當(dāng)下的前端熱詞,稍具規(guī)模的團(tuán)隊(duì)都會(huì)去做技術(shù)探索,作為一個(gè)不甘落后的團(tuán)隊(duì),我們也去做了。

前言

微前端是當(dāng)下的前端熱詞,稍具規(guī)模的團(tuán)隊(duì)都會(huì)去做技術(shù)探索,作為一個(gè)不甘落后的團(tuán)隊(duì),我們也去做了。也許你看過了Single-Spa,qiankun這些業(yè)界成熟方案,非常強(qiáng)大:JS沙箱隔離、多棧支持、子應(yīng)用并行、子應(yīng)用嵌套,但仔細(xì)想想它真的適合你嗎?

對(duì)于我來說,太重了,概念太多,理解困難。先說一下背景,我們之所以要對(duì)我司的小貸管理后臺(tái)做微前端改造,主要基于以下幾個(gè)述求:

  •  系統(tǒng)從接手時(shí)差不多30個(gè)頁面,一年多時(shí)間,發(fā)展到目前150多個(gè)頁面,并還在持續(xù)增長;
  •  項(xiàng)目體積變大,帶來開發(fā)體驗(yàn)很差,打包構(gòu)建速度很慢(初次構(gòu)建,1分鐘以上);
  •  小貸系統(tǒng)開發(fā)量占整個(gè)web組50%的人力,每個(gè)迭代都有兩三個(gè)需求在這一個(gè)系統(tǒng)上開發(fā),代碼合并沖突,上線時(shí)間交叉。帶來的是開發(fā)流程管理復(fù)雜;
  •  業(yè)務(wù)人員是分類的,沒有誰會(huì)用到所有的功能,每個(gè)業(yè)務(wù)人員只擁有其中30%甚至更少的功能。但不得不加載所有業(yè)務(wù)代碼,才能看到自己想要的頁面;

所以和市面上很多前端團(tuán)隊(duì)引入微前端的目的不同的是,我們是拆,而更多的團(tuán)隊(duì)是合。所以本方案適合和我目的一致的前端團(tuán)隊(duì),將自己維護(hù)的巨嬰系統(tǒng)瓦解,然后通過微前端"框架"來聚合,降低項(xiàng)目管理難度,提升開發(fā)體驗(yàn)與業(yè)務(wù)使用體驗(yàn)。

巨嬰系統(tǒng)技術(shù)棧: Dva + Antd

方案參考美團(tuán)一篇文章:微前端在美團(tuán)外賣的實(shí)踐

在做這個(gè)項(xiàng)目的按需提前加載設(shè)計(jì)時(shí),自己去深究過webpack構(gòu)建出的項(xiàng)目代碼運(yùn)行邏輯,收獲比較多:webpack 打包的代碼怎么在瀏覽器跑起來的?, 不了解的可以看看

方案設(shè)計(jì)

基于業(yè)務(wù)角色,我們將巨嬰系統(tǒng)拆成了一個(gè)基座系統(tǒng)和四個(gè)子系統(tǒng)(可以按需擴(kuò)展子系統(tǒng)),如下圖所示:

基座系統(tǒng)除了提供基座功能,即系統(tǒng)的登錄、權(quán)限獲取、子系統(tǒng)的加載、公共組件共享、公共庫的共享,還提供了一個(gè)基本所有業(yè)務(wù)人員都會(huì)使用的業(yè)務(wù)功能:用戶授(guan)信(li)。

子系統(tǒng)以靜態(tài)資源的方式,提供一個(gè)注冊函數(shù),函數(shù)返回值是一個(gè)Switch包裹的組件與子系統(tǒng)所有的models。

路由設(shè)計(jì)

子系統(tǒng)以組件的形式加載到基座系統(tǒng)中,所以路由是入口,也是整個(gè)設(shè)計(jì)的第一步,為了區(qū)分基座系統(tǒng)頁面和子系統(tǒng)頁面,在路由上約定了下面這種形式: 

  1. // 子系統(tǒng)路由匹配,偽代碼  
  2. function Layout(layoutProps) {  
  3.   useEffect(() => {  
  4.       const apps = getIncludeSubAppMap();  
  5.       // 按需加載子項(xiàng)目;  
  6.       apps.forEach(subKey => startAsyncSubapp(subKey));  
  7.   }, []); 
  8.   return (  
  9.     <HLayout {...props}>  
  10.       <Switch>  
  11.           {/* 企業(yè)用戶管理 */}  
  12.           <Route exact path={Paths.PRODUCT_WHITEBAR} component={pages.ProductManage} breadcrumbName="企業(yè)用戶管理" />  
  13.           {/* ...省略一百行 */}  
  14.           <Route path="/subPage/" component={pages.AsyncComponent} />  
  15.       </Switch>  
  16.     </HLayout>  

即只要以subPage路徑開頭,就默認(rèn)這個(gè)路由對(duì)應(yīng)的組件為子項(xiàng)目,從而通過AsyncComponent組件去異步獲取子項(xiàng)目組件。

異步加載組件設(shè)計(jì)

路由設(shè)計(jì)完了,然后異步加載組件就是這個(gè)方案的靈魂了,流程是這樣的:

  •  通過路由,匹配到要訪問的具體是那個(gè)子項(xiàng)目;
  •  通過子項(xiàng)目id,獲取對(duì)應(yīng)的manifest.json文件;
  •  通過獲取manifest.json,識(shí)別到對(duì)應(yīng)的靜態(tài)資源(js,css)
  •  加載靜態(tài)資源,加載完,子項(xiàng)目執(zhí)行注冊
  •  動(dòng)態(tài)加載model,更新子項(xiàng)目組件

直接上代碼吧,簡單明了,資源加載的邏輯后面再詳講,需要注意的是model和component的加載順序: 

  1. export default function AsyncComponent({ location }) {  
  2.   // 子工程資源是否加載完成  
  3.   const [ayncLoading, setAyncLoaded] = useState(true);  
  4.   // 子工程組件加載存取  
  5.   const [ayncComponent, setAyncComponent] = useState(null);  
  6.   const { pathname } = location;  
  7.   // 取路徑中標(biāo)識(shí)子工程前綴的部分, 例如 '/subPage/xxx/home' 其中xxx即子系統(tǒng)路由標(biāo)識(shí)  
  8.   const id = pathname.split('/')[2];  
  9.   useEffect(() => {  
  10.     if (!subAppMapInfo[id]) {  
  11.       // 不存在這個(gè)子系統(tǒng),直接重定向到首頁去  
  12.       goBackToIndex();  
  13.     }  
  14.     const status = subAppRegisterStatus[id];  
  15.     if (status !== 'finish') {  
  16.       // 加載子項(xiàng)目  
  17.       loadAsyncSubapp(id).then(({ routes, models }) => {  
  18.         loadModule(id, models);  
  19.         setAyncComponent(routes);  
  20.         setAyncLoaded(false);  
  21.         // 已經(jīng)加載過的,做個(gè)標(biāo)記  
  22.         subAppRegisterStatus[id] = 'finish';  
  23.       }).catch((error = {}) => {  
  24.         // 如果加載失敗,顯示錯(cuò)誤信息  
  25.         setAyncLoaded(false);  
  26.         setAyncComponent(  
  27.           <div style={{  
  28.             margin: '100px auto',  
  29.             textAlign: 'center',  
  30.             color: 'red',  
  31.             fontSize: '20px'  
  32.           }}  
  33.           >  
  34.             {error.message || '加載失敗'}  
  35.           </div>);  
  36.       });  
  37.     } else {  
  38.       const models = subappModels[id];  
  39.       loadModule(id, models);  
  40.       // 如果能匹配上前綴則加載相應(yīng)子工程模塊  
  41.       setAyncLoaded(false);  
  42.       setAyncComponent(subappRoutes[id]);  
  43.     }  
  44.   }, [id]);  
  45.   return (  
  46.     <Spin spinning={ayncLoading} style={{ width: '100%', minHeight: '100%' }}>  
  47.       {ayncComponent}  
  48.     </Spin>  
  49.   );  

子項(xiàng)目設(shè)計(jì)

子項(xiàng)目以靜態(tài)資源的形式在基座項(xiàng)目中加載,需要暴露出子系統(tǒng)自己的全部頁面組件和數(shù)據(jù)model;然后在打包構(gòu)建上和以前也稍許不同,需要多生成一個(gè)manifest.json來搜集子項(xiàng)目的靜態(tài)資源信息。

子項(xiàng)目暴露出自己自愿的代碼長這樣: 

  1. // 子項(xiàng)目資源輸出代碼  
  2. import routes from './layouts';  
  3. const models = {};  
  4. function importAll(r) {  
  5.   r.keys().forEach(key => models[key] = r(key).default);  
  6.  
  7. // 搜集所有頁面的model  
  8. importAll(require.context('./pages', true, /model\.js$/));  
  9. function registerApp(dep) {  
  10.   return {  
  11.     routes, // 子工程路由組件  
  12.     models, // 子工程數(shù)據(jù)模型集合  
  13.   };  
  14.  
  15. // 數(shù)組第一個(gè)參數(shù)為子項(xiàng)目id,第二個(gè)參數(shù)為子項(xiàng)目模塊獲取函數(shù)  
  16. (window["registerApp"] = window["registerApp"] || []).push(['collection', registerApp]); 

子項(xiàng)目頁面組件搜集: 

  1. import menus from 'configs/menus';  
  2. import { Switch, Redirect, Route } from 'react-router-dom';  
  3. import pages from 'pages';  
  4. function flattenMenu(menus) {  
  5.   const result = [];  
  6.   menus.forEach((menu) => {  
  7.     if (menu.children) {  
  8.       result.push(...flattenMenu(menu.children));  
  9.     } else {  
  10.       menu.Component = pages[menu.component];  
  11.       result.push(menu);  
  12.     }  
  13.   });  
  14.   return result;  
  15.  
  16. // 子項(xiàng)目自己路徑分別 + /subpage/xxx   
  17. const prefixRoutes = flattenMenu(menus);  
  18. export default (  
  19.   <Switch>  
  20.     {prefixRoutes.map(child =>  
  21.       <Route  
  22.         exact  
  23.         key={child.key}  
  24.         path={child.path}  
  25.         component={child.Component}  
  26.         breadcrumbName={child.title}  
  27.       />  
  28.     )}  
  29.     <Redirect to="/home" />  
  30.   </Switch>); 

靜態(tài)資源加載邏輯設(shè)計(jì)

開始做方案時(shí),只是設(shè)計(jì)出按需加載的交互體驗(yàn):即當(dāng)業(yè)務(wù)切換到子項(xiàng)目路徑時(shí),開始加載子項(xiàng)目的資源,然后渲染頁面。但后面感覺這種改動(dòng)影響了業(yè)務(wù)體驗(yàn),他們以前只需要加載數(shù)據(jù)時(shí)loading,現(xiàn)在還需要承受子項(xiàng)目加載loading。所以為了讓業(yè)務(wù)盡量小的感知系統(tǒng)的重構(gòu),將按需加載換成了按需提前加載。簡單點(diǎn)說,就是當(dāng)業(yè)務(wù)登錄時(shí),我們會(huì)去遍歷他的所有權(quán)限菜單,獲取他擁有那些子項(xiàng)目的訪問權(quán)限,然后提前加載這些資源。

遍歷菜單,提前加載子項(xiàng)目資源: 

  1. // 本地開發(fā)環(huán)境不提前按需加載  
  2. if (getDeployEnv() !== 'local') {  
  3.   const apps = getIncludeAppMap();  
  4.   // 按需提前加載子項(xiàng)目資源;  
  5.   apps.forEach(subKey => startAsyncSubapp(subKey));  

然后就是show代碼的時(shí)候了,思路參考webpackJsonp,就是通過攔截一個(gè)全局?jǐn)?shù)組的push操作,得知子項(xiàng)目已加載完成: 

  1. import { subAppMapInfo } from './menus';  
  2. // 子項(xiàng)目靜態(tài)資源映射表存放:  
  3. /**  
  4.  * 狀態(tài)定義:  
  5.  * '': 還未加載  
  6.  * ‘start’:靜態(tài)資源映射表已存在;  
  7.  * ‘map’:靜態(tài)資源映射表已存在;  
  8.  * 'init': 靜態(tài)資源已加載;  
  9.  * 'wait': 資源加載已完成, 待注入;  
  10.  * 'finish': 模塊已注入;  
  11. */  
  12. export const subAppRegisterStatus = {};  
  13. export const subappSourceInfo = {};  
  14. // 項(xiàng)目加載待處理的Promise hash 表  
  15. const defferPromiseMap = {};  
  16. // 項(xiàng)目加載待處理的錯(cuò)誤 hash 表  
  17. const errorInfoMap = {};  
  18. // 加載css,js 資源  
  19. function loadSingleSource(url) {  
  20.   // 此處省略了一寫代碼  
  21.   return new Promise((resolove, reject) => {  
  22.     link.onload = () => {  
  23.       resolove(true);  
  24.     };  
  25.     link.onerror = () => {  
  26.       reject(false);  
  27.     };  
  28.   });  
  29.  
  30. // 加載json中包含的所有靜態(tài)資源  
  31. async function loadSource(json) {  
  32.   const keys = Object.keys(json);  
  33.   const isOk = await Promise.all(keys.map(key => loadSingleSource(json[key])));  
  34.   if (!isOk || isOk.filter(res => res === true) < keys.length) {  
  35.     return false;  
  36.   }  
  37.   return true;  
  38.  
  39. // 獲取子項(xiàng)目的json 資源信息  
  40. async function getManifestJson(subKey) {  
  41.   const url = subAppMapInfo[subKey];  
  42.   if (subappSourceInfo[subKey]) {  
  43.     return subappSourceInfo[subKey];  
  44.   }  
  45.   const json = await fetch(url).then(response => response.json())  
  46.     .catch(() => false);  
  47.   subAppRegisterStatus[subKey] = 'map';  
  48.   return json;  
  49.  
  50. // 子項(xiàng)目提前按需加載入口  
  51. export async function startAsyncSubapp(moduleName) {  
  52.   subAppRegisterStatus[moduleName] = 'start'; // 開始加載  
  53.   const json = await getManifestJson(moduleName);  
  54.   const [, reject] = defferPromiseMap[moduleName] || [];  
  55.   if (json === false) {  
  56.     subAppRegisterStatus[moduleName] = 'error';  
  57.     errorInfoMap[moduleName] = new Error(`模塊:${moduleName}, manifest.json 加載錯(cuò)誤`);  
  58.     reject && reject(errorInfoMap[moduleName]);  
  59.     return;  
  60.   }  
  61.   subAppRegisterStatus[moduleName] = 'map'; // json加載完畢  
  62.   const isOk = await loadSource(json);  
  63.   if (isOk) {  
  64.     subAppRegisterStatus[moduleName] = 'init';  
  65.     return;  
  66.   }  
  67.   errorInfoMap[moduleName] = new Error(`模塊:${moduleName}, 靜態(tài)資源加載錯(cuò)誤`);  
  68.   reject && reject(errorInfoMap[moduleName]);  
  69.   subAppRegisterStatus[moduleName] = 'error';  
  70.  
  71. // 回調(diào)處理  
  72. function checkDeps(moduleName) {  
  73.   if (!defferPromiseMap[moduleName]) {  
  74.     return;  
  75.   }  
  76.   // 存在待處理的,開始處理;  
  77.   const [resolove, reject] = defferPromiseMap[moduleName];  
  78.   const registerApp = subappSourceInfo[moduleName];  
  79.   try {  
  80.     const moduleExport = registerApp();  
  81.     resolove(moduleExport);  
  82.   } catch (e) {  
  83.     reject(e);  
  84.   } finally {  
  85.     // 從待處理中清理掉  
  86.     defferPromiseMap[moduleName] = null;  
  87.     subAppRegisterStatus[moduleName] = 'finish';  
  88.   }  
  89.  
  90. // window.registerApp.push(['collection', registerApp])  
  91. // 這是子項(xiàng)目注冊的核心,靈感來源于webpack,即對(duì)window.registerApp的push操作進(jìn)行攔截  
  92. export function initSubAppLoader() {  
  93.   window.registerApp = [];  
  94.   const originPush = window.registerApp.push.bind(window.registerApp);  
  95.   // eslint-disable-next-line no-use-before-define  
  96.   window.registerApp.push = registerPushCallback 
  97.   function registerPushCallback(module = []) {  
  98.     const [moduleName, register] = module;  
  99.     subappSourceInfo[moduleName] = register;  
  100.     originPush(module);  
  101.     checkDeps(moduleName);  
  102.   }  
  103.  
  104. // 按需提前加載入口  
  105. export function loadAsyncSubapp(moduleName) {  
  106.   const subAppInfo = subAppRegisterStatus[moduleName];  
  107.   // 錯(cuò)誤處理優(yōu)先  
  108.   if (subAppInfo === 'error') {  
  109.     const error = errorInfoMap[moduleName] || new Error(`模塊:${moduleName}, 資源加載錯(cuò)誤`);  
  110.     return Promise.reject(error);  
  111.   }  
  112.   // 已經(jīng)提前加載,等待注入  
  113.   if (typeof subappSourceInfo[moduleName] === 'function') {  
  114.     return Promise.resolve(subappSourceInfo[moduleName]());  
  115.   }  
  116.   // 還未加載的,就開始加載,已經(jīng)開始加載的,直接返回  
  117.   if (!subAppInfo) {  
  118.     startAsyncSubapp(moduleName);  
  119.   }  
  120.   return new Promise((resolve, reject = (error) => { throw error; }) => {  
  121.     // 加入待處理map中;  
  122.     defferPromiseMap[moduleName] = [resolve, reject];  
  123.   });  

這里需要強(qiáng)調(diào)一下子項(xiàng)目有兩種加載場景:

  •  從基座頁面路徑進(jìn)入系統(tǒng), 那么就是按需提前加載的場景, 那么startAsyncSubapp先執(zhí)行,提前緩存資源;
  •  從子項(xiàng)目頁面路徑進(jìn)入系統(tǒng), 那就是按需加載的場景,就存在loadAsyncSubapp先執(zhí)行,利用Promise完成發(fā)布訂閱。至于為什么startAsyncSubapp在前但后執(zhí)行,是因?yàn)閡seEffect是組件掛載完成才執(zhí)行;

至此,框架的大致邏輯就交代清楚了,剩下的就是優(yōu)化了。

其他難點(diǎn)

其實(shí)不難,只是怪我太菜,但這些點(diǎn)確實(shí)值得記錄,分享出來共勉。

公共依賴共享

我們由于基座項(xiàng)目與子項(xiàng)目技術(shù)棧一致,另外又是拆分系統(tǒng),所以共享公共庫依賴,優(yōu)化打包是一個(gè)特別重要的點(diǎn),以為就是webpack配個(gè)external就完事,但其實(shí)要復(fù)雜的多。

antd 構(gòu)建

antd 3.x就支持了esm,即按需引入,但由于我們構(gòu)建工具沒有做相應(yīng)升級(jí),用了babel-plugin-import這個(gè)插件,所以導(dǎo)致了兩個(gè)問題,打包冗余與無法全量導(dǎo)出antd Modules。分開來講:

  •  打包冗余,就是通過BundleAnalyzer插件發(fā)現(xiàn),一個(gè)模塊即打了commonJs代碼,也打了Esm代碼;
  •  無法全量導(dǎo)出,因?yàn)榛?xiàng)目不知道子項(xiàng)目會(huì)具體用哪個(gè)模塊,所以只能暴力的導(dǎo)出Antd所有模塊,但babel-plugin-import這個(gè)插件有個(gè)優(yōu)化,會(huì)分析引入,然后刪除沒用的依賴,但我們的需求和它的目的是沖突的;

結(jié)論:使用babel-plugin-import這個(gè)插件打包c(diǎn)ommonJs代碼已經(jīng)過時(shí), 其存在的唯一價(jià)值就是還可以幫我們按需引入css 代碼;

項(xiàng)目公共組件共享

項(xiàng)目中公共組件的共享,我們開始嘗試將常用的組件加入公司組件庫來解決,但發(fā)現(xiàn)這個(gè)方案并不是最理想的,第一:很多組件和業(yè)務(wù)場景強(qiáng)相關(guān),加入公共組件庫,會(huì)造成組件庫臃腫;第二:沒有必要。所以我們最后還是采用了基座項(xiàng)目收集組件,并統(tǒng)一暴露: 

  1. function combineCommonComponent() {  
  2.  const contexts = require.context('./components/common', true, /\.js$/);  
  3.  return contexts.keys().reduce((next, key) => {  
  4.    // 合并components/common下的組件  
  5.    const compName = key.match(/\w+(?=\/index\.js)/)[0];  
  6.    next[compName] = contexts(key).default;  
  7.    return next;  
  8.  }, {});  

webpackJsonp 全局變量污染

如果對(duì)webpack構(gòu)建后的代碼不熟悉,可以先看看開篇提到的那篇文章。

webpack構(gòu)建時(shí),在開發(fā)環(huán)境modules是一個(gè)對(duì)象,采用文件path作為module的key; 而正式環(huán)境,modules是一個(gè)數(shù)組,會(huì)采用index作為module的key。

由于我基座項(xiàng)目和子項(xiàng)目沒有做沙箱隔離,即window被公用,所以存在webpackJsonp全局變量污染的情況,在開發(fā)環(huán)境,這個(gè)污染沒有被暴露,因?yàn)槲募﨣ey是唯一的,但在打正式包時(shí),發(fā)現(xiàn)qa 環(huán)境子項(xiàng)目無法加載,最后一分析,發(fā)現(xiàn)了window.webpackJsonp 環(huán)境變量污染的bug。

最后解決的方案就是子項(xiàng)目打包都擁有自己獨(dú)立的webpackJsonp變量,即將webpackJsonp重命名,寫了一個(gè)簡單的webpack插件搞定: 

  1. // 將webpackJsonp 重命名為 webpackJsonpCollect  
  2. config.plugins.push(new RenameWebpack({ replace: 'webpackJsonpCollect' })); 

子項(xiàng)目開發(fā)熱加載

基座項(xiàng)目為什么會(huì)成為基座,就因?yàn)樗偾曳€(wěn)定的特殊性。但開發(fā)時(shí),由于子項(xiàng)目無法獨(dú)立運(yùn)行,所以需要依賴基座項(xiàng)目聯(lián)調(diào)。但做一個(gè)需求,要打開兩個(gè)vscode,同時(shí)運(yùn)行兩個(gè)項(xiàng)目,對(duì)于那個(gè)開發(fā),這都是一個(gè)不好的開發(fā)體驗(yàn),所以我們希望將dev環(huán)境作為基座,來支持本地的開發(fā)聯(lián)調(diào),這才是最好的體驗(yàn)。

將dev環(huán)境的構(gòu)建參數(shù)改成開發(fā)環(huán)境后,發(fā)現(xiàn)子項(xiàng)目能在線上基座項(xiàng)目運(yùn)行,但webSocket通信一直失敗,最后找到原因是webpack-dev-sever有個(gè)host check邏輯,稱為主機(jī)檢查,是一個(gè)安全選項(xiàng),我們這里是可以確認(rèn)的,所以直接注釋就行。 

 

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

2016-12-16 19:06:02

擴(kuò)展數(shù)據(jù)庫架構(gòu)

2022-10-09 14:05:24

前端single-spa

2021-12-15 07:24:56

SocketTCPUDP

2013-11-28 14:34:30

微軟WP

2022-01-26 00:05:00

AOPRPC遠(yuǎn)程調(diào)用

2021-06-21 09:36:44

微信語音轉(zhuǎn)發(fā)

2024-09-25 08:22:06

2020-05-28 10:45:31

Git分支合并

2012-05-17 11:04:18

匈牙利命名法

2021-10-28 18:58:57

動(dòng)態(tài)規(guī)劃數(shù)據(jù)結(jié)構(gòu)算法

2024-06-03 09:52:08

2021-11-03 05:37:22

Windows 11操作系統(tǒng)微軟

2013-02-22 09:49:43

大數(shù)據(jù)谷歌大數(shù)據(jù)全球技術(shù)峰會(huì)

2021-02-28 13:52:46

程序員編碼技術(shù)

2022-04-26 18:08:21

C語言代碼編程規(guī)范

2017-03-08 13:12:44

編程學(xué)習(xí)

2020-03-05 16:47:51

Git內(nèi)部儲(chǔ)存

2019-01-02 10:49:54

Tomcat內(nèi)存HotSpot VM

2015-02-11 09:35:09

iPhone6

2017-04-13 10:15:18

機(jī)器人工作
點(diǎn)贊
收藏

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

中文字幕有码在线观看| 亚洲一区欧美在线| 熟妇高潮一区二区| 红桃视频成人在线观看| 国产日韩一区欧美| 久久精品视频7| 久久在线视频| 日韩午夜中文字幕| 欧美牲交a欧美牲交| 91精品大全| 成人免费高清视频在线观看| 青青精品视频播放| 日韩国产第一页| 日韩精品福利一区二区三区| 欧美综合一区二区| 免费极品av一视觉盛宴| 国产在线视频网| 国产91精品免费| 国产精品永久免费观看| 日本中文字幕免费| 无码一区二区三区视频| 日韩高清不卡av| 国产性生活一级片| 在线日韩影院| 亚洲午夜电影网| 中文字幕不卡每日更新1区2区| 丝袜+亚洲+另类+欧美+变态| 成人激情综合| 亚洲欧美日韩久久精品| 欧美日韩一区综合| 亚洲AV无码国产精品午夜字幕| 日日摸夜夜添夜夜添精品视频| 欧美另类在线播放| 天堂网av2018| 精品不卡一区| 日韩精品在线看| 国偷自产av一区二区三区麻豆| 成人一区视频| 日本高清不卡一区| 欧美a v在线播放| 制服丝袜中文字幕在线| 亚洲欧洲日韩女同| 亚洲精品一品区二品区三品区 | 狠狠干狠狠久久| 天天做天天躁天天躁| 色网站在线看| 国产精品天美传媒沈樵| 欧洲一区二区在线| 天堂av网在线| 99在线精品一区二区三区| 国产精品18久久久久久久久| 欧美极度另类性三渗透| 性欧美疯狂猛交69hd| 日韩精品第一区| 一本色道久久综合狠狠躁篇的优点| 中文文字幕文字幕高清| 亚洲专区**| 欧美videofree性高清杂交| 精品国产鲁一鲁一区二区三区| 激情小说亚洲| 欧美日韩在线亚洲一区蜜芽| 我看黄色一级片| 欧洲av一区二区| 91豆麻精品91久久久久久| 欧美三级午夜理伦三级| 校园春色亚洲色图| 欧美亚洲动漫制服丝袜| 亚欧激情乱码久久久久久久久| 精品女同一区二区三区在线观看| 欧美日韩午夜在线| 97超碰人人爽| 日韩一区二区三区精品视频第3页| 日韩视频在线一区二区| 黄色av电影网站| 老司机成人在线| 亚洲人成毛片在线播放| 91资源在线播放| 午夜片欧美伦| 久久久在线视频| 亚洲天堂一区在线观看| 日韩福利电影在线| 91视频国产高清| 欧美一级在线免费观看| 久久久一区二区三区捆绑**| 久久久久久久免费| 95在线视频| 一级女性全黄久久生活片免费| 国产中文字幕在线免费观看| 欧美成人app| 日韩精品一区二区三区视频在线观看 | 国产66精品久久久久999小说| 少妇又色又爽又黄的视频| 国产日韩欧美精品电影三级在线| 自拍偷拍视频在线| 黄色视屏在线免费观看| 欧美日韩一区二区三区在线看| 国产精品久久久久久久99| 欧美1区二区| 色偷偷888欧美精品久久久| 欧美日韩大片在线观看| 天堂在线亚洲视频| 97人人澡人人爽| 国产视频网站在线| 亚洲永久精品国产| 成人免费在线观看视频网站| 超碰在线一区| 色偷偷88888欧美精品久久久| 日本少妇全体裸体洗澡| 另类调教123区| 久久久久久久久久码影片| 国产剧情在线| 欧美综合久久久| 久久午夜夜伦鲁鲁片| 99精品一区| 国产成人精品综合久久久| www夜片内射视频日韩精品成人| 久久麻豆一区二区| 国产在线一区二区三区| 婷婷在线免费观看| 中文字幕日本乱码精品影院| 熟女性饥渴一区二区三区| 精品一区二区三区免费看| 国产午夜精品免费一区二区三区| 久久精品国产亚洲av无码娇色 | 国产日韩在线一区| 日本在线视频1区| 亚洲永久免费视频| 91人妻一区二区三区| 四虎成人av| 国产精品丝袜视频| 国产在线91| 欧美日韩在线影院| 日韩综合第一页| 激情成人亚洲| av在线不卡一区| 成人午夜在线影视| 4438亚洲最大| 波多野结衣在线网址| 日韩 欧美一区二区三区| 免费精品视频一区| 天天爽夜夜爽夜夜爽| 亚洲美女视频一区| av噜噜在线观看| 国产精品久久久久久| 国产乱肥老妇国产一区二| 国产综合在线观看| 在线亚洲一区观看| 夜夜春很很躁夜夜躁| 爽爽淫人综合网网站| 欧美一区亚洲二区| 香蕉视频亚洲一级| 一区二区三区高清国产| 中文字幕黄色av| 国产精品久久久久久久第一福利| 五月婷婷丁香色| 婷婷精品进入| 99re资源| 国产三级伦理在线| 亚洲国产精久久久久久| 少妇一级淫片免费放中国 | 日韩高清不卡av| 国产精品一区二区三区四| 久久亚洲综合色| 午夜激情福利在线| 天天超碰亚洲| 福利视频久久| 亚洲天堂av在线| 在线视频日本亚洲性| 92久久精品一区二区| 国产一区二区三区在线看麻豆| 亚洲日本japanese丝袜| 日本在线视频中文有码| 精品国产第一区二区三区观看体验| 久久久香蕉视频| 91丨porny丨国产| 国产精品69页| 欧美a级片视频| 不卡日韩av| 国产精品粉嫩| 久久精品99国产精品酒店日本| aaa级黄色片| 岛国av一区二区在线在线观看| 日本成人免费视频| 国产精品一区专区| a√天堂在线观看| 四季av在线一区二区三区 | 五月婷婷开心中文字幕| 日本精品视频一区二区| 日韩影院一区二区| 91尤物视频在线观看| 视频在线观看免费高清| 亚洲国产日本| 亚洲五月六月| 91嫩草精品| 国产精品久久久久久久久男| 日本片在线观看| 一本大道久久加勒比香蕉| 国产chinasex对白videos麻豆| 狠狠色狠狠色综合日日小说| 天天色影综合网| www一区二区| 国产一级片中文字幕| 羞羞答答国产精品www一本| 免费久久久久久| 深爱激情久久| 国产伦精品一区二区三区视频免费| 成人免费毛片嘿嘿连载视频…| 久久国产色av| www视频在线观看免费| 337p日本欧洲亚洲大胆色噜噜| 中文字幕一区二区在线视频| 精品国产老师黑色丝袜高跟鞋| 欧美大片xxxx| 日本一区二区三区国色天香| 伊人网综合视频| 国产高清不卡一区二区| 黄大色黄女片18第一次| 免费在线亚洲欧美| 成人在线国产视频| 亚洲破处大片| 特级毛片在线免费观看| 欧美男gay| 精品欧美日韩| 日韩精品一区国产| 91精品久久久久久| 成人久久网站| 国产成人精品av| 中日韩脚交footjobhd| 久久免费国产精品1| 色呦呦在线播放| 久久综合电影一区| 蜜桃av在线免费观看| 中文字幕日韩欧美| 高h视频在线| 亚洲色无码播放| 美国成人毛片| 亚洲人成电影网站色…| 久久经典视频| 亚洲男人天天操| 久久免费看视频| 亚洲片在线观看| eeuss影院www在线播放| 一区二区在线视频播放| 国产youjizz在线| 在线精品视频视频中文字幕| 国产私拍精品| 中文日韩在线观看| 日本三级在线视频| 久久精品国产亚洲7777| 嫩草香蕉在线91一二三区| 色婷婷久久av| 黄色av免费在线| 欧美区二区三区| 超免费在线视频| 97碰在线观看| 免费电影日韩网站| 国产精品久久久久久久久久ktv | 国产视频不卡在线| 国产精品你懂的| 在线观看黄网址| 一区二区三区中文免费| 久久久久无码国产精品| 午夜伦欧美伦电影理论片| 黄色片免费观看视频| 色一情一乱一乱一91av| 中文字幕在线视频第一页| 在线不卡欧美精品一区二区三区| 国产同性人妖ts口直男| 精品福利av导航| 你懂的视频在线免费| 中文字幕亚洲第一| 羞羞视频在线观看不卡| 97在线视频一区| 亚洲天堂一区二区| 91在线高清免费观看| 盗摄系列偷拍视频精品tp| 蜜桃传媒一区二区| 91青青国产在线观看精品| 免费观看中文字幕| 亚洲免费精品| 污污视频网站在线| 91亚洲男人天堂| 国产中文av在线| 天天综合网天天综合色| 一区二区视频网| 精品久久久久久综合日本欧美| 青青国产在线| 欧美大奶子在线| 亚洲成人激情社区| 999久久久| 狠狠色丁香婷婷综合影院| 欧美a级黄色大片| 久久亚洲国产精品一区二区| 制服丝袜中文字幕第一页 | 亚洲午夜精品久久久久久高潮 | 欧美成人精品免费| 日本一区中文字幕 | 豆花视频一区二区| 亚洲国产日韩美| 亚洲免费黄色| 亚洲国产欧美91| 国产三级精品三级| 日本少妇做爰全过程毛片| 欧美日韩www| 欧美成人综合在线| 久久男人的天堂| 国产麻豆精品| 亚洲国产一区二区在线| 在线一区免费观看| 精品无码av一区二区三区不卡| 国产欧美精品一区| 韩国av中文字幕| 精品久久久久久最新网址| 婷婷视频在线| 日韩免费精品视频| 日韩成人一级| 成人黄色大片网站| 国产精品综合一区二区三区| 一级黄色片网址| 色天使色偷偷av一区二区 | 中文字幕在线精品| 亚洲伊人av| 狠狠色综合色区| 韩日在线一区| 日本成人xxx| 国产精品国产三级国产普通话99 | 日韩电影免费在线看| a视频免费观看| 午夜精品一区二区三区电影天堂 | 日韩av在线网| 538在线精品| 国产精品一级久久久| 欧美久久视频| 伊人av在线播放| 亚洲精品免费一二三区| 中文字幕av资源| 中文日韩在线观看| 成人国产精品| 亚洲欧美一区二区原创| 日韩经典中文字幕一区| 3d动漫精品啪啪一区二区下载 | 青青草激情视频| 日韩小视频在线观看专区| av网站网址在线观看| 91在线网站视频| 欧美一区精品| 性生活在线视频| 一区二区三区四区乱视频| 精品二区在线观看| 久久久久国产精品www| 国产精品tv| 欧美日韩二三区| 国产日韩视频一区二区三区| 国产情侣免费视频| 日韩中文字幕在线播放| 国产精品美女久久久久人| 蜜臀在线免费观看| 成人在线视频首页| 97久久久久久久| 伊人成人开心激情综合网| 欧美高清xxx| 台湾无码一区二区| 99久久综合国产精品| 最新中文字幕在线观看视频| 最近中文字幕日韩精品| 久久精品一级| 国产3p露脸普通话对白| 久久精品在线免费观看| 亚洲一区二区天堂| 欧美寡妇偷汉性猛交| 国产精品一区二区中文字幕| 国产又黄又大又粗视频| 国产农村妇女毛片精品久久麻豆| 91久久久久久久久久久久| 欧美人与物videos| 亚洲最好看的视频| 污污视频网站在线| 欧美日韩国产精品专区| 99riav在线| 俄罗斯精品一区二区| 久久中文在线| 亚洲色婷婷一区二区三区| 亚洲精品二三区| 久久久久久久性潮| 人妻少妇精品无码专区二区| 国产欧美日韩久久| 亚洲欧美另类一区| 国产精品久久久| 亚洲小说欧美另类婷婷| 精品国产aaa| 亚洲精品一区二区三区99| 韩国成人在线| 超碰成人免费在线| 中文在线资源观看网站视频免费不卡| 精品人妻少妇AV无码专区| 青青草原成人在线视频| 欧美日韩一区自拍| 一级片久久久久| 亚洲精品久久久久| 国产午夜精品一区在线观看|