第八章 vue.js-自定義指令(基礎(chǔ)篇)

? ? ? ? 在第五章中我們已經(jīng)介紹了需要Vue內(nèi)置的指令内贮,比如v-if宣赔、v-show等,這些豐富的內(nèi)置指令能滿足我們的絕大部分的業(yè)務(wù)需求侣姆,不過在需要一些特殊功能時,我們?nèi)匀幌M麑OM進行底層的操作沉噩,這個時候就需要用到自定義指令捺宗。

8.1基本用法

? ? ? ? 自定義指令的注冊方法和組件很像,也分全局注冊和局部注冊川蒙,比如注冊一個v-focus的指令蚜厉,用于<input>、<textarea>元素初始化時自動獲得焦點畜眨,兩種寫法分別是:

//全局注冊

Vue.directive('focus',{

? ? //指令選項

});

//局部注冊

var app= new Vue({

? ? ? ? el:'#app',

? ? ? ? directives:{

? ? ? ? focus:{

? ? ? ? ? ? //指令選項

? ? ? ? }

? ? }

})

? ? ? ? 寫法與組件基本類似昼牛,只是方法名由component改為了directive。上例值是注冊了自定義指令v-focus,還沒有實現(xiàn)具體功能康聂,下面具體介紹自定義指令的各個選項贰健。

? ? ? ? 自定義指令的選項是由幾個鉤子函數(shù)組成的,每個都是可選的恬汁。

? ? ? bind:只調(diào)用一次伶椿,指令第一次綁定到元素時調(diào)用,用這個鉤子函數(shù)可以定義一個在綁定時執(zhí)行一次的初始化動作氓侧。

? ? ? ? inserted:被綁定元素插入父節(jié)點時調(diào)用(父節(jié)點存在即可調(diào)用脊另,不必存在與document中).

? ? ? ? update:被綁定元素所在的模板更新時調(diào)用,而不論綁定值是否變化约巷。通過比較更新前后的綁定值偎痛,可以忽略不必要的模板更新。

? ? ? ? componentUpdated:被綁定元素所在模板完成一次更新周期時調(diào)用独郎。

? ? ? ? unbind:指令只調(diào)用一次踩麦,指令與元素解綁時調(diào)用。

? ? ? ? 可以根據(jù)需求在不同的鉤子函數(shù)內(nèi)完成邏輯代碼囚聚,例如上面的v-focus靖榕,我們希望在元素插入父節(jié)點時就調(diào)用,那用到的最好是inserted顽铸,示例代碼如下:

<div id="app">

? ? ? ? <input type="text" v-focus>

</div>

<script>

? ? Vue.directive('v-focus',{

? ? ? ? inserted:function(el){

? ? ? ? ? ? el.focus();//聚焦元素

? ? }

? });

? ? var app = new Vue({

? ? ? ? el:'#app'

? ? })

? ? ? ? 每個鉤子函數(shù)都有幾個參數(shù)可用茁计,比如上面我們用到了el。它們的含義如下:

? ? ? ? el:指令所綁定的元素,可以用來直接操作DOM星压。

? ? ? ? binding:一個對象践剂,包含以下屬性:

? ? ? ? ? ? name 指令名,不包括v-前綴娜膘。

? ? ? ? ? ? value 指令的綁定值逊脯,例如v-my-directive="1+1“,value的值是2.

? ? ? ? ? ? oldValue 指令綁定的前一個值竣贪,僅在update和componentUpdated的鉤子中可用军洼,無論值是否改變都可用。

? ? ? ? ? ? expression 綁定值的字符串形式演怎,例如v-my-directive="1+1",expression的值是”1“匕争。

? ? ? ? ? ? arg 傳給指令的參數(shù),例如v-my-directive:foo,arg的值是foo爷耀。

? ? ? ? ? ? modifiers 一個包含修飾符的對象甘桑,例如v-my-directive.foo.bar。修飾符對象modifiers的值是{foo:true,bar:true}.

? ? ? ? vnode :Vue編譯生成的虛擬節(jié)點歹叮,在進階篇中介紹跑杭。

? ? ? ? oldVnode:上一個虛擬節(jié)點,僅在update和componentUpdated鉤子中使用咆耿。

? ? 下面是結(jié)合了以上參數(shù)的一個具體示例德谅,代碼如下:

<div id="app">

? ? <div v-test:msg.a.b="message"></div>

</div>

<script>

? ? Vue.directive('test',{

? ? ? ? bind:function(){

? ? ? ? ? ? var key = {};

? ? ? ? ? ? for (var i in vnode){

? ? ? ? ? ? ? ? key.push(i);

? ? ? ? ? ? }

? ? ? ? ? ? el.innerHTML =

? ? ? ? ? ? 'name' +biding.name +'<br>'+

? ? ? ? ? ? 'value' +biding.value+'<br>'+

? ? ? ? ? ? 'expression' +biding.expression+'<br>'+

? ? ? ? ? ? 'argument' +biding.arg+'<br>'+

? ? ? ? ? ? 'modifiers' +JSON.stringify(biding.modifiers) +'<br>'+

? ? ? ? ? ? 'vnode' +keys.join(',')

? ? ? ? }

? ? });

? ? var app = new Vue({

? ? el:'#app',

? ? data:{

? ? ? ? message:'some text'

? ? }

})

</script>

執(zhí)行后,<div>的內(nèi)容會使用inner HTML重置票灰,結(jié)果為:

name:test

value:some text

expression:message

argument:msg

modifiers:{"a":true,"b":true}

vnode keys:

tag,data,children,text.elm,ns,context,functionalContext,key,componentOptions,componentInstance,parent,raw,isStatic,isRootInsert,isComment,isCloned,isOnce

? ? ? ? 在大多數(shù)場景女阀,我們會在bind鉤子里綁定一些事件,比如在document上用addEventListener綁定屑迂,在unbind里用removeEventListener解綁浸策,比較典型的示例就是讓這個元素隨著鼠標拖曳。在后面的8.2章節(jié)中惹盼,我們會詳細介紹到庸汗。

? ? ? ? 如果需要多個值,自定義指令也可以傳入一個JavaScript對象字面量手报,只要是合法類型的JavaScript表達式都是可以的蚯舱。示例代碼如下:

<div id="app">

? ? <div v-test="{msg:'hello',name:'Lmz'}"></div>

</div>

<script>

? ? Vue.directive('test',{

? ? ? ? bind:function(el,binding,vnode){

? ? ? ? console.log(binding.value.msg);

? ? ? ? console.log(binding.value.name);

? ? }

});

? ? var app = new Vue({

? ? ? ? el:'app'

? ? })

Vue2.x移除了大量Vue1.x自定義指令的配置。在使用自定義指令時掩蛤,應(yīng)該充分理解業(yè)務(wù)需求枉昏,因為很多時候你需要的可能并不是自定義指令,而是組件揍鸟。在下一節(jié)中兄裂,我們結(jié)合兩個經(jīng)典的示例在進一步了解自定義指令的使用場景和用法。


8.2實戰(zhàn)

8.2.1開發(fā)一個可從外部關(guān)閉的下拉菜單

? ? ? ? 網(wǎng)頁中有很多常見的下拉菜單。點擊某個按鈕會彈出一個下拉菜單晰奖,然后點擊頁面中其它空白區(qū)域(除了菜單本身外)谈撒,菜單就關(guān)閉了。本示例就用自定義指令來實現(xiàn)這樣的需求匾南。

? ? ? ? 先來分析一下如何實現(xiàn)啃匿。

? ? ? ? 該示例有兩個特點,一是下拉菜單本身是不會關(guān)閉的蛆楞,二是點擊下拉菜單以外的所以區(qū)域都要關(guān)閉溯乒。點擊所有區(qū)域可以在document上綁定click事件來實現(xiàn),同時只要過濾出是否點擊的是目標元素內(nèi)部的元素即可臊岸。

? ? ? ? 首先初始化各個文件:

index.html

<!DOCTYPE html>

<html>

<head>

? ? <meta charset="utf-8">

? ? <title>可從外部關(guān)閉的下拉菜單</title>

? ? <lin rel="stylesheet" type="text/css" href="style.css">/

</head>

<body>

? ? <div id="app" v-cloak></div>

? ? <script src="https:unpkg.com/vue/dist/vue.min.js"></script>

? ? <script src="clickoutside.js></script>

? ? <script src="index.js></script>

</body>

</html>

index.js

var app = new Vue({

? ? el:'#app'

});

clickoutside.js

? ? Vue.directive('clickoutside',{

});

利用組件的基本知識很容易完成index.html和index.js的邏輯:

<div id="app" v-cloak">

? ? <div class="main" v-clickoutside="handleClose">

? ? ? ? <button @click="show =!show">點擊顯示下拉菜單</button>

? ? ? ? <div class="dropdown" v-show="show">

? ? ? ? ? ? ? ? <p>下拉框的內(nèi)容橙数,點擊外面區(qū)域可以關(guān)閉</p>

? ? ? ? </div>

? ? </div>

</div>

var app = new Vue({

? ? el:'#app',

? ? data:{

? ? ? ? show:false

? ? },

? ? methods:{

? ? handleClose:{

? ? ? ? this.show=false;

? ? ? ? }

? ? }

});

? ? ? ? 邏輯很簡單,點擊按鈕時顯示class為dropdown的div元素帅戒。

? ? ? ? 自定義指令v-clickoutside綁定了一個函數(shù)handleClose,原來關(guān)閉菜單崖技。先來看一下clickoutside.js中的內(nèi)容:

Vue.directive('clickoutside',{

? ? bind:function(el,binding,vnode){

? ? ? ? function doucumentHandler(e){

? ? ? ? ? ? if(el.contains(e.target)){

? ? ? ? ? ? ? ? return false;

? ? ? ? ? ? }

? ? ? ? ? ? if(binding.expression){

? ? ? ? ? ? ? ? binding.value(e);

? ? ? ? ? ? }

? ? ? ? el._vueClickOutside_ = documentHandler;

? ? ? ? document.addEventListener('click',documentHandler);

? ? ? ? },

? ? unbind:function(el,binding){

? ? ? ? document.removeEventListener('click',el._vueClickOutside_);

? ? ? ? delete el._vueClickOutside_;

? ? ? ? }

? ? }

});

? ? ? ? 之前分析過逻住,要在document上綁定click事件,所以在bind鉤子內(nèi)聲明了一個函數(shù)documentHandler迎献,并將它作為句柄綁定在document的click事件上瞎访。documentHandler函數(shù)做了兩個判斷,第一個是判斷點擊的區(qū)域是否是指令所在的元素內(nèi)部吁恍,如果是扒秸,就跳出函數(shù),不往下繼續(xù)執(zhí)行冀瓦。

TIPS:contains方法是用來判斷元素A是否包含了元素B伴奥,包含返回true,不包含返回false翼闽,示例代碼如下:

<body>

? ? <div id="parent">

? ? ? ? ? ? 父元素

? ? ? ? ? ? <div id="children">子元素</div>

? ? </div>

? ? <script type="text/javascript">

? ? var A =document.getElementById('parent');

? ? var B =document.getElementById('children');

? ? console.log(A.contains(B));//true

? ? console.log(B.contains(A));//false

? ? </script>

</body>

? ? ? ? 第二個判斷的是當(dāng)前的指令v-clickoutside有沒有寫表達式拾徙,在該自定義指令中,表達式應(yīng)該是一個函數(shù)感局,在過濾了內(nèi)部元素后尼啡,點擊外面任何區(qū)域應(yīng)該執(zhí)行用戶表達式中的函數(shù),所以binding.value()就是用來執(zhí)行當(dāng)前上下文methods中指定的函數(shù)的询微。

? ? ? ? 與Vue1.x不同的是崖瞭,在自定義指令中,不能再用this.xxx的形式在上下文中聲明一個變量撑毛。所以用el._vueClickOutside_引用了doucumentHandler书聚,這樣就可以在unbind鉤子里移除對document的click事件監(jiān)聽。如果不移除,當(dāng)組件或元素銷毀時寺惫,它仍然存在于內(nèi)存中疹吃。

? ? ? ? 以上代碼分解完整代碼基本一致,不再重復(fù)提供西雀。下面是style.css的代碼:

[v-cloak]{

display:none;

}

.main{

width:125px;

}

button{

display:block;

width:100%;

color:#fff;

background-color:#39f;

border:0;

padding:6px;

text-align:center;

font-size:12px;

border-radius:4px;

cussor:pointer;

outline:none;

position:relative;

}

button:active{

top:1px;

left:1px;

}

.dropdown{

width:100%;

height:150px;

margin:5px 0;

}


8.2.2開發(fā)一個實時事件轉(zhuǎn)換指令v-time

? ? ? ? 在一些社區(qū)萨驶,比如微博、朋友圈等艇肴,發(fā)布的動態(tài)會有一個相對本機時間轉(zhuǎn)換后的相對時間腔呜。(2小時前,11天前等).

? ? ? ? 一般在服務(wù)器的存儲事件格式是Unix時間戳再悼,比如2017-01-01 00:00:00的時間戳是1483200000.前端在拿到數(shù)據(jù)后核畴,將它轉(zhuǎn)換為可讀的時間格式再顯示出來。為了顯出實時性冲九,在一些社交類產(chǎn)品中谤草,甚至?xí)崟r轉(zhuǎn)換為幾秒鐘前、幾分鐘前莺奸、幾小時前等不同的格式丑孩,這樣比直接轉(zhuǎn)換為年、月灭贷、日温学、時、分甚疟、秒更友好仗岖。本示例就來實現(xiàn)這樣一個自定義指令v-time,將表達式傳入的時間戳實時轉(zhuǎn)換為相對時間览妖。

? ? ? ? 便于演示效果轧拄,我們初始化時定義了兩個時間。

index.html

<!DOCTYPE html>

<html>

<head>

? ? <meta charset="utf-8">

? ? <title>時間轉(zhuǎn)換指令</title>

</head>

<body>

? ? <div id="app" v-cloak>

? ? ? ? <div v-time="timeNow"></div>

? ? ? ? <div v-time="timeBefore"></div>

? ? </div>

? ? <script src = "https://unpkg.com/vue/dist/vue.min.js"></script>

? ? <srript src="time.js"></script>

? ? <script src="index.js"></script>

</body>

</html>

index.js

var app = new Vue({

? ? el:'#app',

? ? data:{

? ? ? ? timeNow:(new Date().getTime(),

? ? ? ? timeBefore:1488930695721

? ? }

})

timeNow是目前的時間黄痪,timeBefore是一個寫死的時間:2017-03-08.

TIP:本示例所用的時間戳都是毫秒級紧帕,如服務(wù)端返回秒級時間戳需要乘以1000后再使用。

? ? ? ? 分析一下時間轉(zhuǎn)換邏輯:

? ? ? ? 1分鐘以前桅打,顯示“剛剛”

? ? ? ? 1分鐘-1小時之間是嗜,顯示“xx分鐘前”。

? ? ? ? 1小時-24小時之間挺尾,顯示:"xx小時前“鹅搪。

? ? ? ? 1天-一個月(31天)間,顯示:"xx天前”遭铺。

? ? ? ? 大于1個月丽柿,顯示“xx年xx月xx日”恢准。

? ? ? ? 為了使判斷邏輯更簡單,統(tǒng)一使用時間戳進行時間大小判斷甫题。在寫指令v-time之前馁筐,需要先寫一系列與時間相關(guān)的函數(shù),我們聲明一個對象Time坠非,把它們都封裝在里面敏沉。

time.js

? ? var time = {//獲取當(dāng)前時間戳

? ? ? ? getUnix:function(){

? ? ? ? ? ? var date = new Date();

? ? ? ? ? ? return date.getTime();

? ? ? ? },

? ? //獲取今天0點0分0秒的時間戳

? ? ? ? getTodayUnix:function(){

? ? ? ? ? ? var date = new Date();

? ? ? ? ? ? date.setHours(0);

? ? ? ? ? ? date.setMinutes(0);

? ? ? ? ? ? date.setSeconds(0);

? ? ? ? ? ? date.setMilliSeconds(0);

? ? ? ? ? ? return .date.getTime();

? ? }炎码,

? ? //獲取今年1月1日0點0分0秒的時間戳

? ? ? ? getYearUnix:function(){

? ? ? ? ? ? var date = new Date();

? ? ? ? ? ? date.setMonth(0);

? ? ? ? ? ? date.setDate(0);

? ? ? ? ? ? date.setHours(0);

? ? ? ? ? ? date.setMinutes(0);

? ? ? ? ? ? date.setSeconds(0);

? ? ? ? ? ? date.setMilliSeconds(0);

? ? ? ? ? ? return .date.getTime();

? ? ? ? }盟迟,

? ? //? 獲取標準年月日

? ? ? ? getYearUnix:function(){

? ? ? ? ? ? var date = new Date(time);

? ? ? ? ? ? var month =date.getMonth()+1<10?'0'+(date.getMonth()+1):date.getMonth()+1;

? ? ? ? ? ? var day = date.getDate()<10?'0'+date.getDate():date.getDate();

? ? ? ? ? ? return .date.getFullYear()+'-'+month +'-'+day;

? ? ? ? },

? ? //轉(zhuǎn)換時間

? ? getFormatTime:function(){

? ? ? ? var now = this.getUnix();//當(dāng)前時間戳

? ? ? ? var today=this.getTodayUnix();//今天0點時間戳

? ? ? ? var year = this.getYearUnix();//今年0點時間戳

? ? ? ? var timer = (now -timestamp)/1000;//轉(zhuǎn)換為秒級時間戳

? ? ? ? var tip='';

? ? ? ? if(timer < =0){

? ? ? ? ? ? tip='剛剛';

? ? ? ? }else if(Math.floor(timer/60)<=0){

? ? ? ? ? ? tip='剛剛';

? ? ? ? }else if(timer<3600){

? ? ? ? ? ? tip=Math.floor(timer/60)+'分鐘前';

? ? ? ? }else if(timer >=3600 &&(timestamp - today > =0)){

? ? ? ? ? ? tip=Math.floor(timer/3600)+'小時前';

? ? ? ? }else if(timer /86400<=31)){

? ? ? ? ? ? tip=Math.floor(timer/86400)+'天前';

? ? ? ? }else{

? ? ? ? ? ? tip =this.getLastDate(timetamp);

? ? ? ? }

? ? ? ? return tip;

? ? }

};

? ? ? ? Time.getFormatTime()方法就是自定義指令v-time所需要的潦闲,入?yún)楹撩爰墪r間戳攒菠,返回已經(jīng)整理號事件格式的字符串。

? ? ? ? 最后在time.js里補全生于代碼:

? ? ? ? Vue.directive('time',{

? ? ? ? ? ? bind:function(el,binding){

? ? ? ? ? ? el.innerHTML=Time.getFomatTime(binding.value);

? ? ? ? ? ? el._timeout_=setInterval(function(){

? ? ? ? ? ? ? ? el.innerHTML=Time.getFomatTime(binding.value);

? ? ? ? ? ? ? ? },60000);

? ? ? ? },

? ? ? ? unbind:function(el){

? ? ? ? ? ? clearInterval(el._timeout_);

? ? ? ? ? ? delete el._timeout_;

? ? ? ? }

? ? });

? ? ? ? 在bind鉤子里歉闰,將指令v-time表達式的值binding.value作為參數(shù)傳入Time.getFormatTime()方法得到格式化時間辖众,再通過el/innerHTML寫入指令坐在元素。定時器el._timeout_每分鐘出發(fā)一次和敬,更新時間赵辕,并且在unbind鉤子里清除掉。

? ? ? ? 總結(jié):在編寫自定義指令時概龄,給DOM綁定一次性事件等初始條件,建議在bind鉤子內(nèi)完成饲握。同時要在unbind鉤子內(nèi)解除相關(guān)綁定私杜。在自定義指令里,理論上可以任意操作DOM救欧,但這又違背了Vue.js的初衷衰粹,所以對于大幅度 DOM變動,應(yīng)該使用組件笆怠。


上一章:vue.js組件詳解(基礎(chǔ)篇)

下一章:Render函數(shù)(進階篇)-未更新

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铝耻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蹬刷,更是在濱河造成了極大的恐慌瓢捉,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件办成,死亡現(xiàn)場離奇詭異泡态,居然都是意外死亡,警方通過查閱死者的電腦和手機迂卢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門某弦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桐汤,“玉大人,你說我怎么就攤上這事靶壮≌” “怎么了漏麦?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵椒惨,是天一觀的道長。 經(jīng)常有香客問我硼砰,道長蜂莉,這世上最難降的妖魔是什么蜡娶? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮映穗,結(jié)果婚禮上窖张,老公的妹妹穿的比我還像新娘。我一直安慰自己蚁滋,他們只是感情好宿接,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辕录,像睡著了一般睦霎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上走诞,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天副女,我揣著相機與錄音,去河邊找鬼蚣旱。 笑死碑幅,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的塞绿。 我是一名探鬼主播沟涨,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼异吻!你這毒婦竟也來了裹赴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤诀浪,失蹤者是張志新(化名)和其女友劉穎棋返,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笋妥,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡懊昨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了春宣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酵颁。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嫉你,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出躏惋,到底是詐尸還是另有隱情幽污,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布簿姨,位于F島的核電站距误,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扁位。R本人自食惡果不足惜准潭,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望域仇。 院中可真熱鬧刑然,春花似錦、人聲如沸暇务。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垦细。三九已至择镇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間括改,已是汗流浹背腻豌。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘱能,地道東北人饲梭。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像焰檩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子订框,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容