整合營(yíng)銷(xiāo)服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          玻璃球里的花紋是怎么弄進(jìn)去的?看完童年之謎終于解開(kāi)了

          玻璃球里的花紋是怎么弄進(jìn)去的?看完童年之謎終于解開(kāi)了

          玩過(guò)玻璃球么?也就是彈珠,里面帶花紋的那種。

          每到過(guò)年,小編總能收拾出一些自己曾經(jīng)愛(ài)不釋手的玩具,而花花綠綠的彈珠正是其中之一。在我的記憶中,小時(shí)候?qū)Α笍椓锪铩沟某撩猿潭炔粊営诂F(xiàn)在玩《王者榮耀》開(kāi)黑,在地上畫(huà)個(gè)圈就能和小伙伴們戰(zhàn)上一個(gè)下午。

          不過(guò)在玩耍的同時(shí),你是否和小編思考過(guò)同樣的問(wèn)題:彈珠里面的花紋到底是怎么放進(jìn)去的?

          日本科普節(jié)目THE MAKING為我們很好的還原了制造流程。首先玻璃彈珠的材料通常來(lái)自廢舊玻璃瓶,這類(lèi)瓶子會(huì)根據(jù)顏色分類(lèi),然后碾成細(xì)碎狀作為材料使用:

          在這一過(guò)程中,流水線機(jī)器會(huì)去除掉玻璃上的紙制標(biāo)簽等物品,包括利用磁石去除各種金屬材料殘?jiān)?/p>

          經(jīng)過(guò)加工過(guò)濾后,廢品玻璃就變成了純色玻璃殘?jiān)?,它們?huì)作為原材料被運(yùn)往玻璃彈珠制作的流水線。

          讓這些材料變成彈珠的第一步,是軟化。彈珠原料也就是玻璃碎渣在進(jìn)入流水線后會(huì)被排放進(jìn)溶解爐,這里的溫度高達(dá)1200-1400攝氏度,可以將材料軟化的恰到好處,使其既可以流動(dòng)同時(shí)也可以進(jìn)行切割。

          說(shuō)起來(lái)溶解爐的構(gòu)造很像是飲水機(jī)+化糞池的結(jié)合體,經(jīng)過(guò)高溫軟化后,不需要的原料殘?jiān)鼤?huì)上浮或者下沉,真正可以使用的材料則會(huì)從中段流入到出口,向外排放。

          接著需要對(duì)材料進(jìn)行切割。排放出的玻璃材料需要機(jī)械幫助將其裁剪成合適、統(tǒng)一的大?。?/p>

          每一份都會(huì)剪切的非常均勻,切割過(guò)程看著異常舒適···甚至有點(diǎn)看饞了:

          切好后的彈珠材料需要進(jìn)行成型與冷卻。成型很好理解,剪切下來(lái)的彈珠材料并不是球形,需要進(jìn)一步加工處理。常見(jiàn)的機(jī)器是下面這臺(tái):

          這是一臺(tái)由兩個(gè)持續(xù)旋轉(zhuǎn)的槽筒組成輸送機(jī)。彈珠會(huì)在兩個(gè)槽筒的夾縫中不斷旋轉(zhuǎn)、打磨,與此同時(shí)機(jī)器會(huì)將它們運(yùn)往下一處流水線:

          當(dāng)然,彈珠在整個(gè)打磨與運(yùn)輸?shù)倪^(guò)程中,都在做降溫處理:

          在彈珠成型后,「賽車(chē)時(shí)間」就到了。彈珠會(huì)在各種彎道上快速飆車(chē),并將自己的體溫進(jìn)一步降低:

          最終到達(dá)賽道終點(diǎn),集體飛進(jìn)了貨箱中,透明的彈珠這就造好了。

          那么彈珠里面的花紋又是怎么出來(lái)的呢?這種彈珠又被稱(chēng)為貓眼彈珠,常因?yàn)橄∮械幕ㄉ屯獗肀恍』锇閭円暼粽鋵?,但制作過(guò)程其實(shí)并沒(méi)有我們想象的那么復(fù)雜。

          制作這類(lèi)彈珠,就要從制作彈珠的第一步開(kāi)始準(zhǔn)備,事實(shí)上里面的花紋同樣取材自玻璃,只不過(guò)是彩色玻璃。

          制作的秘訣就在于,兩種不同顏色的玻璃在軟化時(shí),放置的位置不同。通常來(lái)說(shuō),在熔爐中彩色彈珠的材料需要放置在透明彈珠的中間,也就是所謂的「夾心」:

          兩種材料在向外流出時(shí),一同倒出,此時(shí)透明玻璃的材料會(huì)正好包裹著有色材料。裁剪時(shí)你可以清晰的看到它們的分層:

          兩種材料會(huì)一同進(jìn)入成型與冷卻階段,此時(shí)彈珠在機(jī)器的影響下會(huì)不斷旋轉(zhuǎn),內(nèi)部的有色材料同時(shí)被不斷甩動(dòng),并在冷卻前逐漸被轉(zhuǎn)成了我們常見(jiàn)的貓眼狀。這樣,彈珠里的色彩就添加好了。

          最后無(wú)論是哪種彈珠,都要進(jìn)行檢查與包裝,然后送到我們手上。

          當(dāng)然這種流水線出來(lái)的彈珠很難控制花紋的樣式,想要彈珠擁有更加漂亮的花紋,則需要手工制作。

          簡(jiǎn)單來(lái)說(shuō),手工玻璃彈珠的制作可以理解為,一個(gè)不斷軟化與不斷塑形的過(guò)程。例如下面這位小哥的演示,首先也是將透明玻璃材料進(jìn)行高溫軟化,然后將其盤(pán)成圓柱狀:

          接著裁剪一些想要的有色玻璃芯,例如純白色,然后就像蘸醬一樣,間隔均勻的滾動(dòng)一番,讓玻璃芯粘在玻璃主體上:

          然后再度軟化,讓白色玻璃芯與透明材料成為一體:

          通過(guò)不斷的旋轉(zhuǎn)拉伸,把白色玻璃芯拉出網(wǎng)紋花紋:

          接著再次軟化并再粘上一層玻璃芯:

          接著繼續(xù)軟化,然后處理玻璃芯的形狀,使其變得扁平:

          再一次將它們軟化后,再來(lái)一層彩色玻璃芯,并重復(fù)之前的步驟,繼續(xù)盤(pán)它:

          這時(shí),彈珠材料已經(jīng)擁有多層花紋,而且被盤(pán)的又細(xì)又長(zhǎng),尤其是在前端,彈珠的雛形已經(jīng)被盤(pán)出來(lái)了:

          接著敲斷彈珠與材料的連接點(diǎn),獲得彈珠,但此刻彈珠上方還有裂痕:

          直接用高溫軟化,讓斷裂痕跡消失:

          最后放置冷卻,一顆多層花紋彈珠就做好了:

          當(dāng)然,手工制作彈珠時(shí)不同的花紋有不同的制作步驟與工序。下面的做法就更像是做漢堡,先把芯燒好,然后兩面夾上透明玻璃材料:

          最后無(wú)限拉伸,軟化、裁剪,滾上一層玻璃芯:

          接著開(kāi)始打磨,盤(pán)它:

          最后成型、冷卻,內(nèi)部的花紋同樣精美:

          另外一些能力超群的大觸還能夠直接在彈珠中雕刻。例如這位Vicki Schneider,他通過(guò)不斷軟化彈珠,然后將其它材料不斷與彈珠進(jìn)行融合:

          最終制作的效果簡(jiǎn)直讓這顆玻璃球變成了透明的精靈球:

          另一位大佬William Grout則在彈珠內(nèi)部制造了一片海底世界:

          看完以上大佬們的手藝,相信你已經(jīng)發(fā)現(xiàn)了,除了我們常見(jiàn)的透明和貓眼外,彈珠的種類(lèi)還有很多。例如剛才我們最初提過(guò)手工作品就屬于Latticino core,它的特點(diǎn)是彈珠中會(huì)有螺旋網(wǎng)狀花紋結(jié)構(gòu):

          下面這類(lèi)被稱(chēng)為Indian Swirls,也就是印度漩渦,不過(guò)這類(lèi)彈珠其實(shí)和印度沒(méi)有半毛錢(qián)關(guān)系:

          (素材來(lái)源自Mary Costello)

          另外就藝術(shù)感而言,Winlock Marbles系列也很出名。這些彈珠猶如注入了水彩一樣,顏色絢爛奪目。

          Fritts art glass同樣是備受好評(píng)的彈珠系列之一,它們的花紋更加細(xì)膩,線條也更加扭曲,給我一種蝴蝶翅膀的感覺(jué):

          當(dāng)然,近年來(lái)比較火的是宇宙彈珠。把宇宙握在手里是什么感覺(jué)?看看Satoshi Tomizu的作品就知道了:

          他利用如蛋白石等其他材料在彈珠中制造了各種行星,金、銀顆粒點(diǎn)綴出了璀璨銀河,燈光一開(kāi),效果不要太美:

          提到宇宙彈珠,就不得不提Gateson Recko,他創(chuàng)造的星空更加深邃神秘:

          這是動(dòng)態(tài)效果,小小的彈珠中簡(jiǎn)直包藏著星辰大海:

          另外小編還在Gateson Recko的官網(wǎng)發(fā)現(xiàn)另一個(gè)神仙作品,下面的彈珠中藏了一座島嶼:

          怎么樣,看完是不是很想來(lái)一顆?當(dāng)然,這些彈珠的售價(jià)也很「仙」,就拿Gateson Recko的系列作品來(lái)說(shuō),價(jià)格在400到1200美金,也就是最貴8000人民幣左右。

          感覺(jué)有點(diǎn)貴的離譜?但對(duì)于稀有彈珠來(lái)說(shuō),這些數(shù)字根本不算什么。例如下面這顆經(jīng)典lutzes彈珠。它由知名彈珠公司Christensen Agate打造,據(jù)了解因?yàn)槠漕伾珮O為罕見(jiàn),且有一定的年代價(jià)值,曾被拍賣(mài)至25000美元,是世界上最貴的彈珠之一。

          沒(méi)錯(cuò),一顆小球價(jià)值16萬(wàn)多人民幣。所以,手里有彈珠的別忘了互相轉(zhuǎn)告下,不要輕易拋棄它們,畢竟沒(méi)準(zhǔn)其中哪一顆就是絕世孤品,發(fā)家致富,就靠它了。

          參考資料:

          https://www.imarbles.com/makingmarbles.php

          https://en.wikipedia.org/wiki/Marble_(toy)

          https://www.youtube.com/watch?v=p8JKnubjnJo

          https://www.youtube.com/watch?v=PSjd1opk3i4

          https://gazettereview.com/2018/09/top-10-most-expensive-marbles/

          http://www.winlockmarbles.com/single_marbles.htm

          http://www.frittsartglass.com/gallery.html

          https://universemarbles.bigcartel.com/product/desert-island-marble-skull-island-4

          來(lái)源:狂丸科學(xué)

          編輯:重光

          近期熱門(mén)文章Top10

          ↓ 點(diǎn)擊標(biāo)題即可查看 ↓

          1. 大齡單身狗返鄉(xiāng)過(guò)年期間瞬時(shí)壓力激增現(xiàn)象及其應(yīng)對(duì)措施研究

          2. 12個(gè)革命性的公式

          3. 最小有多???最大有多大?

          4. 一幅圖讀懂量子力學(xué)(大神的戰(zhàn)爭(zhēng))

          5. WiFi穿墻完全指南

          6. 為什么你吃的食物跟廣告上的永遠(yuǎn)不一樣?

          7. 你知道愛(ài)因斯坦人生中發(fā)表的第一篇論文是什么嗎?

          8. 出生在顯赫世家是怎樣的體驗(yàn)?

          9. 理論物理學(xué)家費(fèi)紙,實(shí)驗(yàn)物理學(xué)家費(fèi)電,理論實(shí)驗(yàn)物理學(xué)家費(fèi)?

          10. 這些東西,看過(guò)的人都轉(zhuǎn)瘋了!

          利用完全二叉樹(shù)的結(jié)構(gòu)來(lái)維護(hù)一組數(shù)據(jù),然后進(jìn)行相關(guān)操作,一般的操作進(jìn)行一次的時(shí)間復(fù)雜度在

            O(1)~O(logn)之間。

                可謂是相當(dāng)?shù)囊I(lǐng)時(shí)尚潮流啊(我不信學(xué)信息學(xué)的你看到log和1的時(shí)間復(fù)雜度不會(huì)激動(dòng)一下下)!。

                什么是完全二叉樹(shù)呢?別急著去百度啊,要百度我?guī)湍惆俣龋?/p>

                若設(shè)二叉樹(shù)的深度為h,除第 h 層外,其它各層 (1~h-1) 的結(jié)點(diǎn)數(shù)都達(dá)到最大個(gè)數(shù),第 h 層所有的結(jié)點(diǎn)都連續(xù)集中

              在最左邊,這就是完全二叉樹(shù)。我們知道二叉樹(shù)可以用數(shù)組模擬,堆自然也可以。

                現(xiàn)在讓我們來(lái)畫(huà)一棵完全二叉樹(shù):


                從圖中可以看出,元素的父親節(jié)點(diǎn)數(shù)組下標(biāo)是本身的1/2(只取整數(shù)部分),所以我們很容易去模擬,也很

              容易證明其所有操作都為log級(jí)別~~

                堆還分為兩種類(lèi)型:大根堆、小根堆

                顧名思義,就是保證根節(jié)點(diǎn)是所有數(shù)據(jù)中最大/小,并且盡力讓小的節(jié)點(diǎn)在上方

                不過(guò)有一點(diǎn)需要注意:堆內(nèi)的元素并不一定數(shù)組下標(biāo)順序來(lái)排序的!!很多的初學(xué)者會(huì)錯(cuò)誤的認(rèn)為大/小根堆中

              下標(biāo)為1就是第一大/小,2是第二大/小……

                原因會(huì)在后面解釋?zhuān)F(xiàn)在你只需要深深地記住這一點(diǎn)!

                我們剛剛畫(huà)的完全二叉樹(shù)中并沒(méi)有任何元素,現(xiàn)在讓我們加入一組數(shù)據(jù)吧!

                下標(biāo)從1到9分別加入:{8,5,2,10,3,7,1,4,6}。

                如下圖所示


                (不要問(wèn)我怎么加,想想你是怎么讀入數(shù)組的。)

                我們可以發(fā)現(xiàn)這組數(shù)據(jù)是雜亂無(wú)章的,我們?cè)撊绾稳?strong>維護(hù)呢?

                現(xiàn)在我就來(lái)介紹一下堆的幾個(gè)基本操作:




                1. 上浮 shift_up;
                2. 下沉 shift_down
                3. 插入 push
                4. 彈出 pop
                5. 取頂 top
                6. 堆排序 heap_sort

                學(xué)習(xí)C/C++的同學(xué)有福利了,堆的代碼一般十分之長(zhǎng),而我們偉大的STL模板庫(kù)給我們提供了兩種簡(jiǎn)單方便堆操作的方式,

              想學(xué)習(xí)的可以看看這個(gè):http://www.cnblogs.com/helloworld-c/p/4854463.html 密碼: abcd111

                我個(gè)人建議吧,起碼知道一下實(shí)現(xiàn)的過(guò)程,STL只能是錦上添花,絕不可以雪中送炭!!

                萬(wàn)一哪天要你模擬堆的某一操作過(guò)程,而你只知道STL卻不知道原理,看不出這個(gè)題目是堆,事后和其他OIer

              討論出題解,那豈不是砍舌頭吃苦瓜,哭得笑哈哈。

                那么我們開(kāi)始講解操作過(guò)程吧,我們以小根堆為例

                剛剛那組未處理過(guò)的數(shù)據(jù)中我們很容易就能看出,根節(jié)點(diǎn)1元素8絕對(duì)不是最小的

                我們很容易發(fā)現(xiàn)它的一個(gè)兒子節(jié)點(diǎn)3(元素2)比它來(lái)的小,我們?cè)趺磳⑺?strong>放到最高點(diǎn)呢?很簡(jiǎn)單,直接交換嘛~~

                但是,我們又發(fā)現(xiàn)了,3的一個(gè)兒子節(jié)點(diǎn)7(元素1)似乎更適合在根節(jié)點(diǎn)。

                這時(shí)候我們是無(wú)法直接和根節(jié)點(diǎn)交換的,那我們就需要一個(gè)操作來(lái)實(shí)現(xiàn)這個(gè)交換過(guò)程,那就是上浮 shift_up。

                操作過(guò)程如下:

                從當(dāng)前結(jié)點(diǎn)開(kāi)始,和它的父親節(jié)點(diǎn)比較,若是比父親節(jié)點(diǎn)來(lái)的小,就交換,

              然后將當(dāng)前詢問(wèn)的節(jié)點(diǎn)下標(biāo)更新為原父親節(jié)點(diǎn)下標(biāo);否則退出?!?/strong>

                模擬操作圖示:


                偽代碼如下:

          Shift_up( i )
          {
              while( i / 2 >=1)
              {
                  if( 堆數(shù)組名[ i ] < 堆數(shù)組名[ i/2 ] )
                  {
                      swap( 堆數(shù)組名[ i ] , 堆數(shù)組名[ i/2 ]) ;
                      i=i / 2;
                  }
                  else break;
          }

                這一次上浮完畢之后呢,我們又發(fā)現(xiàn)了一個(gè)問(wèn)題,貌似節(jié)點(diǎn)3(元素8)不太合適放在那,而它的子節(jié)點(diǎn)7(元素2)

              好像才應(yīng)該在那個(gè)位置。

                此時(shí)的你應(yīng)該會(huì)說(shuō):“賜予我力量,讓節(jié)點(diǎn)7上浮吧,我是OIer!”

                然而,上帝(我很不要臉的說(shuō)是我)賜予你另外一種力量,讓節(jié)點(diǎn)3下沉!

                那么問(wèn)題來(lái)了:節(jié)點(diǎn)3應(yīng)該往哪下沉呢?

                我們知道,小根堆是盡力要讓小的元素在較上方的節(jié)點(diǎn),而下沉與上浮一樣要以交換來(lái)不斷操作,所以我們應(yīng)該

              讓節(jié)點(diǎn)7與其交換。     

                由此我們可以得出下沉的算法了:   

                讓當(dāng)前結(jié)點(diǎn)的左右兒子(如果有的話)作比較,哪個(gè)比較小就和它交換,

              并更新詢問(wèn)節(jié)點(diǎn)的下標(biāo)為被交換的兒子節(jié)點(diǎn)下標(biāo),否則退出。

                模擬操作圖示:


                偽代碼如下:

          Shift_down( i , n )    //n表示當(dāng)前有n個(gè)節(jié)點(diǎn)
          {
              while( i * 2 <=n)
              {
                  T=i * 2 ;
                  if( T + 1 <=n && 堆數(shù)組名[ T + 1 ] < 堆數(shù)組名[ T ])
                      T++;
                  if( 堆數(shù)組名[ i ] < 堆數(shù)組名[ T ] )
                  {
                     swap( 堆數(shù)組名[ i ] , 堆數(shù)組名[ T ] );
                      i=T;
                  }
                  else break;
          }

                講完了上浮和下沉,接下來(lái)就是插入操作了~~~~

                我們前面用的插入是直接插入,所以數(shù)據(jù)才會(huì)雜亂無(wú)章,那么我們如何在插入的時(shí)候邊維護(hù)堆呢?

              其實(shí)很簡(jiǎn)單,每次插入的時(shí)候呢,我們都往最后一個(gè)插入,讓后使它上浮。

                (這個(gè)不需要圖示了吧…)

                偽代碼如下:

          Push ( x )
              {
                  n++;
                  堆數(shù)組名[ n ]=x;
                  Shift_up( n );
              }

                咳咳,說(shuō)完了插入,我們總需要會(huì)彈出吧~~~~~

                彈出,顧名思義就是把頂元素彈掉,但是,彈掉以后不是群龍無(wú)首嗎??

                我們?nèi)绾稳ゾS護(hù)這堆數(shù)據(jù)呢?

                稍加思考,我們不難得出一個(gè)十分巧妙的算法:

              讓根節(jié)點(diǎn)元素和尾節(jié)點(diǎn)進(jìn)行交換,然后讓現(xiàn)在的根元素下沉就可以了!

                (這個(gè)也不需要圖示吧…)

                偽代碼如下:

          Pop ( x )
              {
                  swap( 堆數(shù)組名[1] , 堆數(shù)組名[ n ] );
                  n--;
                  Shift_down( 1 );
              }

                接下來(lái)是取頂…..我想不需要說(shuō)什么了吧,根節(jié)點(diǎn)數(shù)組下標(biāo)必定是1,返回堆[ 1 ]就OK了~~

              注意:每次取頂要判斷堆內(nèi)是否有元素,否則..你懂的

                圖示和偽代碼省略,如果你這都不會(huì)那你可以重新開(kāi)始學(xué)信息學(xué)了,當(dāng)然如果你是小白….這種稍微高級(jí)的數(shù)據(jù)

              結(jié)構(gòu)還是以后再說(shuō)吧。

                說(shuō)完這些,我們?cè)賮?lái)說(shuō)說(shuō)堆排序。之前說(shuō)過(guò)堆是無(wú)法以數(shù)組下標(biāo)的順序來(lái)來(lái)排序的對(duì)吧?

                所以我個(gè)人認(rèn)為呢,并不存在堆排序這樣的操作,即便網(wǎng)上有很多堆排序的算法,但是我這里有個(gè)更加方便的算法:

              開(kāi)一個(gè)新的數(shù)組,每次取堆頂元素放進(jìn)去,然后彈掉堆頂就OK了~


                偽代碼如下:

          Heap_sort( a[] )
          {
                  k=0;
                  while( size > 0 )
                  {
                      k++;
                      a[ k ]=top();
                      pop();    
                  }        
          }

                堆排序的時(shí)間復(fù)雜度是O(nlogn)理論上是十分穩(wěn)定的,但是對(duì)于我們來(lái)說(shuō)并沒(méi)有什么卵用。

                我們要排序的話,直接使用快排即可,時(shí)間更快,用堆排還需要O(2*n)空間。這也是為什么我說(shuō)堆的操作

              時(shí)間復(fù)雜度在O(1)~O(logn)。

                講完到這里,堆也基本介紹完了,那么它有什么用呢??

                舉個(gè)粒子,比如當(dāng)我們每次都要取某一些元素的最小值,而取出來(lái)操作后要再放回去,重復(fù)做這樣的事情。

                我們?nèi)羰怯?strong>快排的話,最壞的情況需要O(q*n^2),而若是堆,僅需要O(q*logn),時(shí)間復(fù)雜度瞬間低了不少。

                還有一種最短路算法——Dijkstra,需要用到堆來(lái)優(yōu)化,這個(gè)算法我后面會(huì)找個(gè)時(shí)間介紹給大家。

          文就某一個(gè)具體的類(lèi)型場(chǎng)景,著重介紹微前端架構(gòu)可以帶來(lái)什么價(jià)值以及具體實(shí)踐過(guò)程中需要關(guān)注的技術(shù)決策,并輔以具體代碼,從而幫助讀者構(gòu)建一個(gè)生產(chǎn)可用的微前端架構(gòu)系統(tǒng)。

          Techniques, strategies and recipes for building a modern web app with multiple teams using different JavaScript frameworks. — Micro Frontends

          前言

          目前社區(qū)有很多關(guān)于微前端架構(gòu)的介紹,但大多停留在概念介紹的階段。而本文會(huì)就某一個(gè)具體的類(lèi)型場(chǎng)景,著重介紹微前端架構(gòu)可以帶來(lái)什么價(jià)值以及具體實(shí)踐過(guò)程中需要關(guān)注的技術(shù)決策,并輔以具體代碼,從而能真正意義上幫助你構(gòu)建一個(gè)生產(chǎn)可用的微前端架構(gòu)系統(tǒng)。

          而對(duì)于微前端的概念感興趣或不熟悉的同學(xué),可以通過(guò)搜索引擎來(lái)獲取更多信息,如 知乎上的相關(guān)內(nèi)容, 本文不再做過(guò)多介紹。

          • 兩個(gè)月前 Twitter 曾爆發(fā)過(guò)關(guān)于微前端的“熱烈”討論,參與大佬眾多 (Dan、Larkin 等),對(duì)“事件”本身我們今天不做過(guò)多評(píng)論 (后面可能會(huì)寫(xiě)篇文章來(lái)回顧一下),有興趣的同學(xué)可以通過(guò)這篇文章了解一二:
          • https://zendev.com/2019/06/17/microfrontends-good-bad-ugly.html

          微前端的價(jià)值

          微前端架構(gòu)具備以下幾個(gè)核心價(jià)值:

          • 技術(shù)棧無(wú)關(guān)
          • 主框架不限制接入應(yīng)用的技術(shù)棧,子應(yīng)用具備完全自主權(quán)。
          • 獨(dú)立開(kāi)發(fā)、獨(dú)立部署
          • 子應(yīng)用倉(cāng)庫(kù)獨(dú)立,前后端可獨(dú)立開(kāi)發(fā),部署完成后主框架自動(dòng)完成同步更新。
          • 獨(dú)立運(yùn)行時(shí)
          • 每個(gè)子應(yīng)用之間狀態(tài)隔離,運(yùn)行時(shí)狀態(tài)不共享。

          微前端架構(gòu)旨在解決單體應(yīng)用在一個(gè)相對(duì)長(zhǎng)的時(shí)間跨度下,由于參與的人員、團(tuán)隊(duì)的增多、變遷,從一個(gè)普通應(yīng)用演變成一個(gè)巨石應(yīng)用 (Frontend Monolith) 后,隨之而來(lái)的應(yīng)用不可維護(hù)的問(wèn)題。這類(lèi)問(wèn)題在企業(yè)級(jí) Web 應(yīng)用中尤其常見(jiàn)。

          針對(duì)中后臺(tái)應(yīng)用的解決方案

          中后臺(tái)應(yīng)用由于其應(yīng)用生命周期長(zhǎng) (動(dòng)輒 3+ 年) 等特點(diǎn),最后演變成一個(gè)巨石應(yīng)用的概率往往高于其他類(lèi)型的 web 應(yīng)用。而從技術(shù)實(shí)現(xiàn)角度,微前端架構(gòu)解決方案大概分為兩類(lèi)場(chǎng)景:

          • 單實(shí)例:即同一時(shí)刻,只有一個(gè)子應(yīng)用被展示,子應(yīng)用具備一個(gè)完整的應(yīng)用生命周期。通?;?url 的變化來(lái)做子應(yīng)用的切換。
          • 多實(shí)例:同一時(shí)刻可展示多個(gè)子應(yīng)用。通常使用 Web Components 方案來(lái)做子應(yīng)用封裝,子應(yīng)用更像是一個(gè)業(yè)務(wù)組件而不是應(yīng)用。

          本文將著重介紹單實(shí)例場(chǎng)景下的微前端架構(gòu)實(shí)踐方案(基于 single-spa),因?yàn)檫@個(gè)場(chǎng)景更貼近大部分中后臺(tái)應(yīng)用。

          行業(yè)現(xiàn)狀

          傳統(tǒng)的云控制臺(tái)應(yīng)用,幾乎都會(huì)面臨業(yè)務(wù)快速發(fā)展之后,單體應(yīng)用進(jìn)化成巨石應(yīng)用的問(wèn)題。為了解決產(chǎn)品研發(fā)之間各種耦合的問(wèn)題,大部分企業(yè)也都會(huì)有自己的解決方案。筆者于 17 年底,針對(duì)國(guó)內(nèi)外幾個(gè)著名的云產(chǎn)品控制臺(tái),做過(guò)這樣一個(gè)技術(shù)調(diào)研:


          MPA 方案的優(yōu)點(diǎn)在于部署簡(jiǎn)單、各應(yīng)用之間硬隔離,天生具備技術(shù)棧無(wú)關(guān)、獨(dú)立開(kāi)發(fā)、獨(dú)立部署的特性。缺點(diǎn)則也很明顯,應(yīng)用之間切換會(huì)造成瀏覽器重刷,由于產(chǎn)品域名之間相互跳轉(zhuǎn),流程體驗(yàn)上會(huì)存在斷點(diǎn)。

          SPA 則天生具備體驗(yàn)上的優(yōu)勢(shì),應(yīng)用直接無(wú)刷新切換,能極大的保證多產(chǎn)品之間流程操作串聯(lián)時(shí)的流程性。缺點(diǎn)則在于各應(yīng)用技術(shù)棧之間是強(qiáng)耦合的。

          那我們有沒(méi)有可能將 MPA 和 SPA 兩者的優(yōu)勢(shì)結(jié)合起來(lái),構(gòu)建出一個(gè)相對(duì)完善的微前端架構(gòu)方案呢?

          JSConf China 2016 大會(huì)上,ucloud 的同學(xué)分享了他們的基于 angularjs 的方案(單頁(yè)應(yīng)用“聯(lián)邦制”實(shí)踐),里面提到的 "聯(lián)邦制" 概念很貼切,可以認(rèn)為是早期的基于耦合技術(shù)棧的微前端架構(gòu)實(shí)踐。

          微前端架構(gòu)實(shí)踐中的問(wèn)題

          可以發(fā)現(xiàn),微前端架構(gòu)的優(yōu)勢(shì),正是 MPA 與 SPA 架構(gòu)優(yōu)勢(shì)的合集。即保證應(yīng)用具備獨(dú)立開(kāi)發(fā)權(quán)的同時(shí),又有將它們整合到一起保證產(chǎn)品完整的流程體驗(yàn)的能力。

          這樣一套模式下,應(yīng)用的架構(gòu)就會(huì)變成:


          Stitching layer 作為主框架的核心成員,充當(dāng)調(diào)度者的角色,由它來(lái)決定在不同的條件下激活不同的子應(yīng)用。因此主框架的定位則僅僅是:導(dǎo)航路由 + 資源加載框架。

          而具體要實(shí)現(xiàn)這樣一套架構(gòu),我們需要解決以下幾個(gè)技術(shù)問(wèn)題:

          路由系統(tǒng)及 Future State

          我們?cè)谝粋€(gè)實(shí)現(xiàn)了微前端內(nèi)核的產(chǎn)品中,正常訪問(wèn)一個(gè)子應(yīng)用的頁(yè)面時(shí),可能會(huì)有這樣一個(gè)鏈路:


          由于我們的子應(yīng)用都是 lazy load 的,當(dāng)瀏覽器重新刷新時(shí),主框架的資源會(huì)被重新加載,同時(shí)異步 load 子應(yīng)用的靜態(tài)資源,由于此時(shí)主應(yīng)用的路由系統(tǒng)已經(jīng)激活,但子應(yīng)用的資源可能還沒(méi)有完全加載完畢,從而導(dǎo)致路由注冊(cè)表里發(fā)現(xiàn)沒(méi)有能匹配子應(yīng)用 /subApp/123/detail 的規(guī)則,這時(shí)候就會(huì)導(dǎo)致跳 NotFound 頁(yè)或者直接路由報(bào)錯(cuò)。

          這個(gè)問(wèn)題在所有 lazy load 方式加載子應(yīng)用的方案中都會(huì)碰到,早些年前 angularjs 社區(qū)把這個(gè)問(wèn)題統(tǒng)一稱(chēng)之為 Future State: https://ui-router.github.io/guide/lazyloading#future-states

          解決的思路也很簡(jiǎn)單,我們需要設(shè)計(jì)這樣一套路由機(jī)制:

          主框架配置子應(yīng)用的路由為 subApp: { url: '/subApp/**', entry: './subApp.js' },則當(dāng)瀏覽器的地址為 /subApp/abc 時(shí),框架需要先加載 entry 資源,待 entry 資源加載完畢,確保子應(yīng)用的路由系統(tǒng)注冊(cè)進(jìn)主框架之后后,再去由子應(yīng)用的路由系統(tǒng)接管 url change 事件。同時(shí)在子應(yīng)用路由切出時(shí),主框架需要觸發(fā)相應(yīng)的 destroy 事件,子應(yīng)用在監(jiān)聽(tīng)到該事件時(shí),調(diào)用自己的卸載方法卸載應(yīng)用,如 React 場(chǎng)景下 destroy=()=> ReactDOM.unmountAtNode(container)。

          要實(shí)現(xiàn)這樣一套機(jī)制,我們可以自己去劫持 url change 事件從而實(shí)現(xiàn)自己的路由系統(tǒng),也可以基于社區(qū)已有的 ui router library,尤其是 react-router 在 v4 之后實(shí)現(xiàn)了 Dynamic Routing 能力,我們只需要復(fù)寫(xiě)一部分路由發(fā)現(xiàn)的邏輯即可。這里我們推薦直接選擇社區(qū)比較完善的相關(guān)實(shí)踐 single-spa。

          App Entry

          解決了路由問(wèn)題后,主框架與子應(yīng)用集成的方式,也會(huì)成為一個(gè)需要重點(diǎn)關(guān)注的技術(shù)決策。

          構(gòu)建時(shí)組合 VS 運(yùn)行時(shí)組合

          微前端架構(gòu)模式下,子應(yīng)用打包的方式,基本分為兩種:


          兩者的優(yōu)缺點(diǎn)也很明顯:


          很顯然,要實(shí)現(xiàn)真正的技術(shù)棧無(wú)關(guān)跟獨(dú)立部署兩個(gè)核心目標(biāo),大部分場(chǎng)景下我們需要使用運(yùn)行時(shí)加載子應(yīng)用這種方案。

          JS Entry vs HTML Entry

          在確定了運(yùn)行時(shí)載入的方案后,另一個(gè)需要決策的點(diǎn)是,我們需要子應(yīng)用提供什么形式的資源作為渲染入口?

          JS Entry 的方式通常是子應(yīng)用將資源打成一個(gè) entry script,比如 single-spa 的 example 中的方式。但這個(gè)方案的限制也頗多,如要求子應(yīng)用的所有資源打包到一個(gè) js bundle 里,包括 css、圖片等資源。除了打出來(lái)的包可能體積龐大之外的問(wèn)題之外,資源的并行加載等特性也無(wú)法利用上。

          HTML Entry 則更加靈活,直接將子應(yīng)用打出來(lái) HTML 作為入口,主框架可以通過(guò) fetch html 的方式獲取子應(yīng)用的靜態(tài)資源,同時(shí)將 HTML document 作為子節(jié)點(diǎn)塞到主框架的容器中。這樣不僅可以極大的減少主應(yīng)用的接入成本,子應(yīng)用的開(kāi)發(fā)方式及打包方式基本上也不需要調(diào)整,而且可以天然的解決子應(yīng)用之間樣式隔離的問(wèn)題 (后面提到)。想象一下這樣一個(gè)場(chǎng)景:

          <!-- 子應(yīng)用 index.html -->
          <script src="http://unpkg/antd.min.js"></script>
          <body>
           <main id="root"></main>
          </body>
          // 子應(yīng)用入口
          ReactDOM.render(<App/>, document.getElementById('root'))
          

          如果是 JS Entry 方案,主框架需要在子應(yīng)用加載之前構(gòu)建好相應(yīng)的容器節(jié)點(diǎn) (比如這里的 "#root" 節(jié)點(diǎn)),不然子應(yīng)用加載時(shí)會(huì)因?yàn)檎也坏?container 報(bào)錯(cuò)。但問(wèn)題在于,主應(yīng)用并不能保證子應(yīng)用使用的容器節(jié)點(diǎn)為某一特定標(biāo)記元素。而 HTML Entry 的方案則天然能解決這一問(wèn)題,保留子應(yīng)用完整的環(huán)境上下文,從而確保子應(yīng)用有良好的開(kāi)發(fā)體驗(yàn)。

          HTML Entry 方案下,主框架注冊(cè)子應(yīng)用的方式則變成:

          framework.registerApp('subApp1', { entry: '//abc.alipay.com/index.html'})
          

          本質(zhì)上這里 HTML 充當(dāng)?shù)氖菓?yīng)用靜態(tài)資源表的角色,在某些場(chǎng)景下,我們也可以將 HTML Entry 的方案優(yōu)化成 Config Entry,從而減少一次請(qǐng)求,如:

          framework.registerApp('subApp1', { html: '', scripts: ['//abc.alipay.com/index.js'], css: ['//abc.alipay.com/index.css']})
          

          總結(jié)一下:


          模塊導(dǎo)入

          微前端架構(gòu)下,我們需要獲取到子應(yīng)用暴露出的一些鉤子引用,如 bootstrap、mount、unmout 等 (參考 single-spa),從而能對(duì)接入應(yīng)用有一個(gè)完整的生命周期控制。而由于子應(yīng)用通常又有集成部署、獨(dú)立部署兩種模式同時(shí)支持的需求,使得我們只能選擇 umd 這種兼容性的模塊格式打包我們的子應(yīng)用。如何在瀏覽器運(yùn)行時(shí)獲取遠(yuǎn)程腳本中導(dǎo)出的模塊引用也是一個(gè)需要解決的問(wèn)題。

          通常我們第一反應(yīng)的解法,也是最簡(jiǎn)單的解法就是與子應(yīng)用與主框架之間約定好一個(gè)全局變量,把導(dǎo)出的鉤子引用掛載到這個(gè)全局變量上,然后主應(yīng)用從這里面取生命周期函數(shù)。

          這個(gè)方案很好用,但是最大的問(wèn)題是,主應(yīng)用與子應(yīng)用之間存在一種強(qiáng)約定的打包協(xié)議。那我們是否能找出一種松耦合的解決方案呢?

          很簡(jiǎn)單,我們只需要走 umd 包格式中的 global export 方式獲取子應(yīng)用的導(dǎo)出即可,大體的思路是通過(guò)給 window 變量打標(biāo)記,記住每次最后添加的全局變量,這個(gè)變量一般就是應(yīng)用 export 后掛載到 global 上的變量。實(shí)現(xiàn)方式可以參考 systemjs global import,這里不再贅述,相關(guān)鏈接: https://github.com/systemjs/systemjs/blob/master/src/extras/global.js

          應(yīng)用隔離

          微前端架構(gòu)方案中有兩個(gè)非常關(guān)鍵的問(wèn)題,有沒(méi)有解決這兩個(gè)問(wèn)題將直接標(biāo)志你的方案是否真的生產(chǎn)可用。比較遺憾的是此前社區(qū)在這個(gè)問(wèn)題上的處理都會(huì)不約而同選擇”繞道“的方式,比如通過(guò)主子應(yīng)用之間的一些默認(rèn)約定去規(guī)避沖突。而今天我們會(huì)嘗試從純技術(shù)角度,更智能的解決應(yīng)用之間可能沖突的問(wèn)題。

          樣式隔離

          由于微前端場(chǎng)景下,不同技術(shù)棧的子應(yīng)用會(huì)被集成到同一個(gè)運(yùn)行時(shí)中,所以我們必須在框架層確保各個(gè)子應(yīng)用之間不會(huì)出現(xiàn)樣式互相干擾的問(wèn)題。

          • Shadow DOM?

          針對(duì) "Isolated Styles" 這個(gè)問(wèn)題,如果不考慮瀏覽器兼容性,通常第一個(gè)浮現(xiàn)到我們腦海里的方案會(huì)是 Web Components?;?Web Components 的 Shadow DOM 能力,我們可以將每個(gè)子應(yīng)用包裹到一個(gè) Shadow DOM 中,保證其運(yùn)行時(shí)的樣式的絕對(duì)隔離。

          但 Shadow DOM 方案在工程實(shí)踐中會(huì)碰到一個(gè)常見(jiàn)問(wèn)題,比如我們這樣去構(gòu)建了一個(gè)在 Shadow DOM 里渲染的子應(yīng)用:

          const shadow=document.querySelector('#hostElement').attachShadow({mode: 'open'});
          shadow.innerHTML='<sub-app>Here is some new text</sub-app><link rel="stylesheet" ;
          

          由于子應(yīng)用的樣式作用域僅在 shadow 元素下,那么一旦子應(yīng)用中出現(xiàn)運(yùn)行時(shí)越界跑到外面構(gòu)建 DOM 的場(chǎng)景,必定會(huì)導(dǎo)致構(gòu)建出來(lái)的 DOM 無(wú)法應(yīng)用子應(yīng)用的樣式的情況。

          比如 sub-app 里調(diào)用了 antd modal 組件,由于 modal 是動(dòng)態(tài)掛載到 document.body 的,而由于 Shadow DOM 的特性 antd 的樣式只會(huì)在 shadow 這個(gè)作用域下生效,結(jié)果就是彈出框無(wú)法應(yīng)用到 antd 的樣式。解決的辦法是把 antd 樣式上浮一層,丟到主文檔里,但這么做意味著子應(yīng)用的樣式直接泄露到主文檔了。

          • CSS Module? BEM?

          社區(qū)通常的實(shí)踐是通過(guò)約定 css 前綴的方式來(lái)避免樣式?jīng)_突,即各個(gè)子應(yīng)用使用特定的前綴來(lái)命名 class,或者直接基于 css module 方案寫(xiě)樣式。對(duì)于一個(gè)全新的項(xiàng)目,這樣當(dāng)然是可行,但是通常微前端架構(gòu)更多的目標(biāo)是解決存量 / 遺產(chǎn) 應(yīng)用的接入問(wèn)題。很顯然遺產(chǎn)應(yīng)用通常是很難有動(dòng)力做大幅改造的。

          最主要的是,約定的方式有一個(gè)無(wú)法解決的問(wèn)題,假如子應(yīng)用中使用了三方的組件庫(kù),三方庫(kù)在寫(xiě)入了大量的全局樣式的同時(shí)又不支持定制化前綴?比如 a 應(yīng)用引入了 antd 2.x,而 b 應(yīng)用引入了 antd 3.x,兩個(gè)版本的 antd 都寫(xiě)入了全局的 .menu class,但又彼此不兼容怎么辦?

          • Dynamic Stylesheet !

          解決方案其實(shí)很簡(jiǎn)單,我們只需要在應(yīng)用切出 / 卸載后,同時(shí)卸載掉其樣式表即可,原理是瀏覽器會(huì)對(duì)所有的樣式表的插入、移除做整個(gè) CSSOM 的重構(gòu),從而達(dá)到 插入、卸載 樣式的目的。這樣即能保證,在一個(gè)時(shí)間點(diǎn)里,只有一個(gè)應(yīng)用的樣式表是生效的。

          上文提到的 HTML Entry 方案則天生具備樣式隔離的特性,因?yàn)閼?yīng)用卸載后會(huì)直接移除去 HTML 結(jié)構(gòu),從而自動(dòng)移除了其樣式表。

          比如 HTML Entry 模式下,子應(yīng)用加載完成的后的 DOM 結(jié)構(gòu)可能長(zhǎng)這樣:

          <html>
           <body>
           <main id="subApp">
           // 子應(yīng)用完整的 html 結(jié)構(gòu)
           <link rel="stylesheet" >
           <div id="root">....</div>
           </main>
           </body>
          </html>
          

          當(dāng)子應(yīng)用被替換或卸載時(shí),subApp 節(jié)點(diǎn)的 innerHTML 也會(huì)被復(fù)寫(xiě),//alipay.com/subapp.css 也就自然被移除樣式也隨之卸載了。

          JS 隔離

          解決了樣式隔離的問(wèn)題后,有一個(gè)更關(guān)鍵的問(wèn)題我們還沒(méi)有解決:如何確保各個(gè)子應(yīng)用之間的全局變量不會(huì)互相干擾,從而保證每個(gè)子應(yīng)用之間的軟隔離?

          這個(gè)問(wèn)題比樣式隔離的問(wèn)題更棘手,社區(qū)的普遍玩法是給一些全局副作用加各種前綴從而避免沖突。但其實(shí)我們都明白,這種通過(guò)團(tuán)隊(duì)間的”口頭“約定的方式往往低效且易碎,所有依賴(lài)人為約束的方案都很難避免由于人的疏忽導(dǎo)致的線上 bug。那么我們是否有可能打造出一個(gè)好用的且完全無(wú)約束的 JS 隔離方案呢?

          針對(duì) JS 隔離的問(wèn)題,我們獨(dú)創(chuàng)了一個(gè)運(yùn)行時(shí)的 JS 沙箱。簡(jiǎn)單畫(huà)了個(gè)架構(gòu)圖:



          即在應(yīng)用的 bootstrap 及 mount 兩個(gè)生命周期開(kāi)始之前分別給全局狀態(tài)打下快照,然后當(dāng)應(yīng)用切出 / 卸載時(shí),將狀態(tài)回滾至 bootstrap 開(kāi)始之前的階段,確保應(yīng)用對(duì)全局狀態(tài)的污染全部清零。而當(dāng)應(yīng)用二次進(jìn)入時(shí)則再恢復(fù)至 mount 前的狀態(tài)的,從而確保應(yīng)用在 remount 時(shí)擁有跟第一次 mount 時(shí)一致的全局上下文。

          當(dāng)然沙箱里做的事情還遠(yuǎn)不止這些,其他的還包括一些對(duì)全局事件監(jiān)聽(tīng)的劫持等,以確保應(yīng)用在切出之后,對(duì)全局事件的監(jiān)聽(tīng)能得到完整的卸載,同時(shí)也會(huì)在 remount 時(shí)重新監(jiān)聽(tīng)這些全局事件,從而模擬出與應(yīng)用獨(dú)立運(yùn)行時(shí)一致的沙箱環(huán)境。

          螞蟻的微前端落地實(shí)踐

          自去年年底伊始,我們便嘗試基于微前端架構(gòu)模式,構(gòu)建出一套全鏈路的面向中后臺(tái)場(chǎng)景的產(chǎn)品接入平臺(tái),目的是解決不同產(chǎn)品之間集成困難、流程割裂的問(wèn)題,希望接入平臺(tái)后的應(yīng)用,不論使用哪種技術(shù)棧,在運(yùn)行時(shí)都可以通過(guò)自定義配置,實(shí)現(xiàn)不同應(yīng)用之間頁(yè)面級(jí)別的自由組合,從而生成一個(gè)千人千面的個(gè)性化控制臺(tái)。

          目前這套平臺(tái)已在螞蟻生產(chǎn)環(huán)境運(yùn)行半年多,同時(shí)接入了多個(gè)產(chǎn)品線的 40+ 應(yīng)用、4+ 不同類(lèi)型的技術(shù)棧。過(guò)程中針對(duì)大量微前端實(shí)踐中的問(wèn)題,我們總結(jié)出了一套完整的解決方案:


          在內(nèi)部得到充分的技術(shù)驗(yàn)證和線上考驗(yàn)之后,我們決定將這套解決方案開(kāi)源出來(lái)!

          qiankun - 一套完整的微前端解決方案

          取名 qiankun,意為統(tǒng)一。我們希望通過(guò) qiankun 這種技術(shù)手段,讓你能很方便的將一個(gè)巨石應(yīng)用改造成一個(gè)基于微前端架構(gòu)的系統(tǒng),并且不再需要去關(guān)注各種過(guò)程中的技術(shù)細(xì)節(jié),做到真正的開(kāi)箱即用和生產(chǎn)可用。

          對(duì)于 umi 用戶我們也提供了配套的 qiankun 插件 @umijs/plugin-qiankun ,以便于 umi 應(yīng)用能幾乎零成本的接入 qiankun。

          qiankun 開(kāi)源: https://github.com/umijs/qiankun

          @umijs/plugin-qiankun: https://github.com/umijs/umi-plugin-qiankun/


          這里有一份獨(dú)家晉升指南:

          部分VIP資料:

          最后說(shuō)一下的,也就是以上教程的獲取方式!

          領(lǐng)取方法:

          還是以往不變的老規(guī)矩!

          1.評(píng)論文章,沒(méi)字?jǐn)?shù)限制,一個(gè)字都行!然后轉(zhuǎn)發(fā)出去!

          2.關(guān)注小編,成為小編的粉絲!

          3.私信小編:“前端教程”即可!

          通過(guò)申請(qǐng)后會(huì)逐個(gè)開(kāi)通權(quán)限,小助手精力有限,手慢無(wú)哦

          視頻的價(jià)值取決于領(lǐng)取后的行動(dòng),大家千萬(wàn)別做收藏黨。和志同道合的人一起深入討論與學(xué)習(xí) Web前端技術(shù),也歡迎轉(zhuǎn)給需要的朋友!


          主站蜘蛛池模板: 国产一区二区三区美女| 亚洲乱码国产一区网址| 日韩成人无码一区二区三区| 无码人妻一区二区三区免费手机| 国产在线无码一区二区三区视频 | 国产一区二区三区小说| 精品国产一区在线观看| 大屁股熟女一区二区三区| 亚洲制服中文字幕第一区| 精品无人乱码一区二区三区| 日韩一区二区三区视频久久| 精品亚洲一区二区| 亚洲Av无码国产一区二区 | 精品3d动漫视频一区在线观看| 国产精品 视频一区 二区三区| 久久久精品人妻一区亚美研究所| 亚洲欧美日韩中文字幕一区二区三区 | 一区二区日韩国产精品| 亚洲日韩激情无码一区 | 无遮挡免费一区二区三区| 无码人妻精品一区二区三区66| 国产精品久久无码一区二区三区网 | 国产精品一区二区久久| 搜日本一区二区三区免费高清视频| 久久精品视频一区二区三区 | 免费无码AV一区二区| 精品性影院一区二区三区内射| 秋霞鲁丝片一区二区三区| 无码视频一区二区三区在线观看 | 97一区二区三区四区久久| 久久久久人妻精品一区 | 日韩精品无码一区二区三区不卡| 综合一区自拍亚洲综合图区| 99久久国产精品免费一区二区| 欧美日韩一区二区成人午夜电影| 一区二区三区影院| 国产一区二区三区亚洲综合| 老熟妇仑乱一区二区视頻| 国产精品 一区 在线| 国产suv精品一区二区6| 亚洲国产精品一区二区三区在线观看 |