vue學習筆記(五)條件渲染和列表渲染

前言

在眾多的編程語言中,我們的基礎語法總是少不了一些專業語法,比如像定義變量,條件語句,for循環,數組,函數等等,vue.js這個優秀的前端框架中也有同樣的語法,我們換一個名詞,將條件語句改成專業詞彙叫做條件渲染,循環語句改成專業詞彙叫做列表渲染,這樣比較舒服一點。

本章目標

  • 學會條件渲染的使用

  • 學會可復用的key的使用

  • 學會列表渲染的使用

條件渲染

1.v-if的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <span v-if="type==='A'">成績為A</span>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',
        data:{
            type:'A'
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:成績為A

v-if判斷條件是否相等,就像if一樣,如果相等,那麼值就會true,與之對應的還有v-else,v-else-if

2.v-else的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <span v-if="type==='A'">成績為A</span>
    <span v-else>成績為B</span>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',    
        data:{
            type:'B'
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:成績為B

小練習

我們做一個小練習,鞏固一下v-if和v-else的使用,需求如下:點擊一個按鈕時,按鈕上的文字變為显示,再次點擊時按鈕上的文字變為隱藏,當按鈕上的文字显示隱藏時,显示紅色,按鈕上的文字變為显示時显示藍色

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <button @click="handleClick">{{text}}</button>
            <div v-if="show" class="box red"></div>
            <div v-else class="blue box"></div>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                    text:'隱藏'
                },
                methods:{
                    handleClick(){
                        this.show=!this.show;
                        this.text=this.show?'隱藏':'显示'
                    }
                }
            })
            
        </script>
    </body>
</html>

結果

 

3.v-else-if的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <input type="text" v-model="type"/>
    <div v-if="type==='A'">成績為A</div>
    <div v-else-if="type==='B'">成績為B</div>
    <div v-else-if="type==='C'">成績為C</div>
    <div v-else>不及格</div>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',    
        data:{
            type:''
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:

4.v-show

說起這個v-show,其實和v-if有與曲同工的妙處,但是又有不同的地方,我們來看下示例你就秒懂了

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div v-show="show" class="box red"></div>
            <button @click="handleClick()">{{text}}</button>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                    text:'隱藏',
                },
                methods:{
                    handleClick(){
                        this.show=!this.show;
                        this.text=this.show?'隱藏':'显示'
                    }
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

當按鈕變為显示的時候,背景顏色消失,這裏就不截圖了,有興趣的小夥伴可以自己去嘗試,既然v-if可以幫我們實現元素的显示和隱藏,那我們還需要v-show干什麼呢?不妨看下接下來的實例。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div v-show="show" class="box red"></div>
            <div class="box blue" v-if="show"></div>
            <button @click="handleClick()">{{text}}</button>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                    text:'隱藏',
                },
                methods:{
                    handleClick(){
                        this.show=!this.show;
                        this.text=this.show?'隱藏':'显示'
                    }
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

 當我們點擊按鈕的時候

 

現在結果已經出來了,使用v-show的dom元素,dom元素只是簡單的切換display屬性,而v-if會將dom元素移除,當我們再次點擊時,v-if又會重新渲染元素,可想而知如果頻繁的切換的話,那麼有多麼的耗費性能,因此我總結了如下幾點

  • 頻繁的切換显示/隱藏要使用v-show

  • 只判斷一次時,使用v-if

5.減少dom的生成

我們都知道js操作dom元素是非常消耗性能的,但是我們需要盡量的避免這個問題,vue中為我們提供了一個template標籤,這個標籤叫做模板(至於什麼叫做模板,後期的博客會講到),我們先看一個示例

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div v-if="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </div>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

 我們想讓圖上的那個div消失,不想為了管理同一組元素而多生成一個節點,這樣是非常消耗性能的,我們將div標籤變成template標籤

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div v-if="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </div>
            <template v-if="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </template>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

View Code

 現在我有心中萌生了一個想法,v-if可以使用template,那麼v-show是否可以使用呢?

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <template v-if="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </template>
            <template v-show="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </template>
            <button @click="handleClick()">{{text}}</button>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                    text:'隱藏',
                },
                methods:{
                    handleClick(){
                        this.show=!this.show;
                        this.text=this.show?'隱藏':'显示'
                    }
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

View Code

 

 答案是v-if可以使用template,而v-show不能使用template

vue中用key管理可復用的元素

Vue 會盡可能高效地渲染元素,通常會復用已有元素而不是從頭開始渲染。這麼做除了使 Vue 變得非常快之外,還有其它一些好處。例如,如果你允許用戶在不同的登錄方式之間切換。

示例一:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <template v-if="type==='username'">
        <label>用戶名</label>
        <input type="text" placeholder="請輸入您的賬號" />
    </template>
    <template v-else>
        <label>郵箱</label>
        <input type="text" placeholder="請輸入您的郵箱" />
    </template>
    <p>
        <a href=""@click.prevent="type='username'">用戶名登錄</a>|
        <a href=""@click.prevent="type='email'">郵箱登錄</a>
    </p>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',    
        data:{
            isShow:true,
            type:'username'
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:

 

 

當我們在用戶名登錄和郵箱切換的時候,我們發現我們輸入的內容始終保持,為什麼呢?總的來說,因為兩個模板使用了相同的元素,input不會被替換掉——僅僅是替換了它的 placeholder屬性,這樣也不總是符合實際需求,所以 Vue 為你提供了一種方式來表達這兩個元素是完全獨立的,不要復用它們,只需添加一個具有唯一值的key屬性即可。

示例二:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <template v-if="type==='username'">
        <label>用戶名:</label>
        <input type="text" placeholder="請輸入您的用戶名"  key='usename'/>
    </template>
    <template v-else>
        <label>郵箱:</label>
        <input type="text" placeholder="請輸入您的郵箱"  key='email'/>
    </template>
    <p>
        <a href=""@click.prevent="type='username'">用戶名登錄</a>|
        <a href=""@click.prevent="type='email'">郵箱登錄</a>
    </p>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',    
        data:{
            isShow:true,
            type:'username'
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:

 

現在我們點擊切換的時候,輸入框都會重新渲染,當然我們的<label>標籤依舊的高效的復用,因為它沒有添加key。

列表渲染

我們用v-for指令根據一組數組的選項列表進行渲染,v-for指令需要以item in items的形式的特殊語法,items是原數據數組並且item是元素迭代的別名

1.v-for的基本使用

語法:(item,index) in|of items

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for的基本使用</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(item) in arr">{{item}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    arr:['apple','banana','pear']
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

結果:

當然v-for中也可以帶第二個參數index

2.v-for中帶索引

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for的基本使用</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(item,index) in arr">{{item}}--{{index}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    arr:['apple','banana','pear']
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

View Code

3.v-for迭代字符串

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for的基本使用</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(item,index) in arr">{{item}}--{{index}}</li>
            </ul>
            <ul>
                <li v-for="item in 'helloworld'">{{item}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    arr:['apple','banana','pear']
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

View Code

4.v-for迭代對象

語法:(value,key,index) of | in items

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for迭代對象</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(value,key,index) of obj">{{value}}-{{key}}-{{index}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    obj:{
                        name:'kk',
                        age:18,
                        sex:'male'
                    }
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

結果:

5.v-for迭代整數

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for迭代對象</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(value,key,index) of obj">{{value}}-{{key}}-{{index}}</li>
            </ul>
            <ul>
                <li v-for="item in 10">{{item}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    obj:{
                        name:'kk',
                        age:18,
                        sex:'male'
                    }
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

結果:

注意:但我們迭代整數的時候,item從1開始而不是從0開始

總結

在本章內容中,我們一共學習了三個知識點,分別是條件渲染的使用(v-if,v-else,v-else-if),管理可復用的key,列表渲染(v-for的基本使用等等),本章的內容也多但是在實際應用上非常廣泛,畢竟這些是非常基礎的語法,基礎不牢,地動山搖,學習任何東西都需要自己一步一個腳印走出來。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

美生物學家夫婦深入拉美記錄野生動物

摘錄自2020年1月25日公視報導

氣候變遷讓全世界許多珍貴的物種面臨可能消失的危機,有一對美國的生物學家夫婦在哥斯大黎加執行拍攝動物的計畫,希望能將這些影像分享給世人。

八年前,來自美國賓州的生物學家洛沙索以及太太琳熙,搬到哥斯大黎加西北部的瓜拿卡斯特省,去年底,他們成立瓜拿卡斯特監控計畫,在當地超過40個森林裡架設隱藏式攝影機,收錄超過100個不同的野生動物物種、約1萬個錄影的片段。這些鏡頭防水防震,還有動態捕捉以及夜間攝影功能。他們希望將這些拍攝到的珍貴畫面,分享給在地的孩子。

根據世界自然基金會的報告,哥斯大黎加面積不到全世界的1%,但其生物多樣性卻佔全球的5%,而這其中有超過25%,是國家指定為特別保護的森林及保育區。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

老虎「從後追上」一把壓地上!他「裝死不動」倖存

摘錄自2020年1月30日ETtoday報導

位於印度中部的馬哈拉施特拉邦(Maharashtra)25日發生老虎攻擊人的事件,一名當地村民被老虎追趕後壓制在地上,為了脫困,他先是假裝自己已經死亡,一動也不動動的躺在地上,旁邊的村民則是不斷地朝著老虎丟擲石頭以示恐嚇;最後老虎似乎受到驚嚇轉身逃跑,而該名村民也平安無事的躲過了一劫。

報導中指出,這起混亂一共造成至少三人受傷,老虎最後則是似乎受到驚嚇,頭也不回地轉身跑走,被壓在地上的男子幸運地存活下來,沒有受到太大的傷害。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

【自然語言處理】利用LDA對希拉里郵件進行主題分析

首先是讀取數據集,並將csv中ExtractedBodyText為空的給去除掉

import pandas as pd
import re
import os

dir_path=os.path.dirname(os.path.abspath(__file__))
data_path=dir_path+"/Database/HillaryEmails.csv"
df=pd.read_csv(data_path)
df=df[['Id','ExtractedBodyText']].dropna()

對於這些郵件信息,並不是所有的詞都是有意義的,也就是先要去除掉一些噪聲數據:

def clean_email_text(text):
    text = text.replace('\n'," ") #新行,我們是不需要的
    text = re.sub(r"-", " ", text) #把 "-" 的兩個單詞,分開。(比如:july-edu ==> july edu)
    text = re.sub(r"\d+/\d+/\d+", "", text) #日期,對主體模型沒什麼意義
    text = re.sub(r"[0-2]?[0-9]:[0-6][0-9]", "", text) #時間,沒意義
    text = re.sub(r"[\w]+@[\.\w]+", "", text) #郵件地址,沒意義
    text = re.sub(r"/[a-zA-Z]*[:\//\]*[A-Za-z0-9\-_]+\.+[A-Za-z0-9\.\/%&=\?\-_]+/i", "", text) #網址,沒意義
    pure_text = ''
    # 以防還有其他特殊字符(数字)等等,我們直接把他們loop一遍,過濾掉
    for letter in text:
        # 只留下字母和空格
        if letter.isalpha() or letter==' ':
            pure_text += letter
    # 再把那些去除特殊字符后落單的單詞,直接排除。
    # 我們就只剩下有意義的單詞了。
    text = ' '.join(word for word in pure_text.split() if len(word)>1)
    return text

然後取出ExtractedBodyText的那一列,對每一行email進行噪聲過濾,並返回一個對象:

docs = df['ExtractedBodyText']
docs = docs.apply(lambda s: clean_email_text(s))  

然後我們呢把裏面的email提取出來:

doclist=docs.values

接下來,我們使用gensim庫來進行LDA模型的構建,gensim可用指令pip install -U gensim安裝。但是,要注意輸入到模型中的數據的格式。例如:[[一條郵件字符串],[另一條郵件字符串], ...]轉換成[[一,條,郵件,在,這裏],[第,二,條,郵件,在,這裏],[今天,天氣,腫么,樣],...]。對於英文的分詞,只需要對空白處分割即可。同時,有些詞語(不同於噪聲)是沒有意義的,我們要過濾掉那些沒有意義的詞語,這裏簡單的寫一個停止詞列表:

stoplist = ['very', 'ourselves', 'am', 'doesn', 'through', 'me', 'against', 'up', 'just', 'her', 'ours',
            'couldn', 'because', 'is', 'isn', 'it', 'only', 'in', 'such', 'too', 'mustn', 'under', 'their',
            'if', 'to', 'my', 'himself', 'after', 'why', 'while', 'can', 'each', 'itself', 'his', 'all', 'once',
            'herself', 'more', 'our', 'they', 'hasn', 'on', 'ma', 'them', 'its', 'where', 'did', 'll', 'you',
            'didn', 'nor', 'as', 'now', 'before', 'those', 'yours', 'from', 'who', 'was', 'm', 'been', 'will',
            'into', 'same', 'how', 'some', 'of', 'out', 'with', 's', 'being', 't', 'mightn', 'she', 'again', 'be',
            'by', 'shan', 'have', 'yourselves', 'needn', 'and', 'are', 'o', 'these', 'further', 'most', 'yourself',
            'having', 'aren', 'here', 'he', 'were', 'but', 'this', 'myself', 'own', 'we', 'so', 'i', 'does', 'both',
            'when', 'between', 'd', 'had', 'the', 'y', 'has', 'down', 'off', 'than', 'haven', 'whom', 'wouldn',
            'should', 've', 'over', 'themselves', 'few', 'then', 'hadn', 'what', 'until', 'won', 'no', 'about',
            'any', 'that', 'for', 'shouldn', 'don', 'do', 'there', 'doing', 'an', 'or', 'ain', 'hers', 'wasn',
            'weren', 'above', 'a', 'at', 'your', 'theirs', 'below', 'other', 'not', 're', 'him', 'during', 'which']

然後我們將輸入轉換成gensim所需的格式,並過濾掉停用詞:

texts = [[word for word in doc.lower().split() if word not in stoplist] for doc in doclist]

再將這所有的單詞放入到一個詞袋中,把每個單詞用一個数字index指代:

from gensim import corpora, models, similarities
import gensim
dictionary = corpora.Dictionary(texts)

再分別統計每一篇email中每個詞語在這個詞袋中出現的次數,並返回一個列表:

corpus = [dictionary.doc2bow(text) for text in texts]

 這個列表告訴我們,第14(從0開始是第一)個郵件中,一共6個有意義的單詞(經過我們的文本預處理,並去除了停止詞后)其中,51號單詞出現1次,505號單詞出現1次,以此類推。。。

最後,就可以開始構建我們的模型了:

lda = gensim.models.ldamodel.LdaModel(corpus=corpus, id2word=dictionary, num_topics=20)
print(lda.print_topic(10, topn=5))

 可以看到,第11個主題最常用的單詞,接下來,我們看下所有的主題:

for i in lda.print_topics(num_topics=20, num_words=5):
    print(i)

 我們再看下第一篇email屬於哪一個主題:

print(lda.get_document_topics(corpus[0]))

 屬於第四個主題的概率是0.95

相關代碼和數據:鏈接: https://pan.baidu.com/s/1sl1I5IeQFDHjVwf2a0C89g 提取碼: xqqf 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

從演奏家的新年大膽願望 一窺日本都市蜂害問題

文:宋瑞文

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

原來JS是這樣的 – 原型鏈

提到屬性描述符 [[Get]][[Put]] 以及提到了訪問描述符 [[Prototype]],看它們的特性就會很容易的讓人想到經典的面向對象風格體系中對類操作要做的事情,但帶一些 introspector 的味道。回想到之前所寫來自用的辣雞應用中所附帶了一個簡易的類似 jQuery 的簡易常用功能實現,就用到了簡單的 [[Prototype]] 特性。但我們前幾篇都沒有詳細的提及 js 的原型鏈相關的內容,本篇就將討論 js 的 [[Prototype]] 屬性和相關的內容。

注:ES6 的 Proxy 和 class 的概念不在本篇討論範圍內。

[[Prototype]]

JavaScript 中的特殊對象屬性除了 [[Get]][[Put]] 外,還有一個很重要的特殊內置屬性就是 [[Prototype]] 了。

[[Prototype]] 是一個幾乎所有對象在創建時都會被賦予一個非空值的屬性,還記得在之前提到 new 操作符的行為嗎?其中的行為之一就是把其 [[Prototype]] 關聯指向到對應的內置對象上。通常 [[Prototype]] 所指向的即為創建此對象時所使用的對象了。

來看下面一個例子

var macat = { a: 1 };

var codingcat = macat; // 和 macat 指向的內容相同
codingcat.b = 2;
console.log(macat.b); // 2

var pineapple = Object.create( macat ); // 新對象,但其 [[Prototype]] 鏈向 macat
pineapple.c = 3; // 新對象的屬性
console.log(macat.c); // undefined
codingcat.d = 4;
console.log(pineapple.d) // 4;

上例中, 變量 codingcat 顯然是指向和 macat 相同的內容,實質完全一致,而 pineapple 則是通過 創建的變量。顯然 pineapplemacat 是不同的兩個對象。不過我們會發現我們依然可以通過 pineapple.d 訪問 macat.d 的值,這就是因為在 Object.create() 中,會把 pineapple[[Prototype]] 指向我們的原型對象 macat 了。

[[Prototype]] 引用的作用是什麼呢?看上去這是一個確定這種像 fallback 一樣的取值操作應該 fallback 到誰的屬性標記,而準確的說,這種 pineapple.d 形式的屬性引用會觸發 [[Get]] 操作(上篇的內容),而默認的 [[Get]] 則會在對象本身沒有此屬性時會去查找 [[Prototype]] 引用的變量了。這樣的引用成為了鏈狀,故被稱作原型鏈。

當然,這個行為其實我們已經“用過”很多次了,比如 .toString().valueOf()hasOwnProperty(),我們 Object.create() 等形式構建的新對象顯然並沒有附帶一份這些函數的副本,而是因為普通的 [[Prototype]] 鏈最終都會指向內置的 Object.prototype,而它提供了這些功能。

屬性設置和屏蔽

不過上例中有個有趣的坑,我們考慮在上例的基礎上做如下操作:

...
pineapple.a++; // 交互式終端會輸出 1
console.log(pineapple.a); // 2
console.log(macat.a); // 1

pineapple.a++ 看上去是進行了變量自增的操作,但這一行后,我們發現 pineapple.a 不再等於 macat.a 了,這是因為實際上 pineapple.a 本來並不存在,但可以通過原型鏈找到 macat.a,而 pineapple.a++ (相當於 pineapple.a = pineapple.a + 1)最終進行的賦值操作創建了 pineapple.a ,故最終這兩個變量的值自然不再相等。

這個例子來看,如果本身即通過對 pineapple 的屬性(a)進行訪問操作,那麼不同情況下訪問得到的結果可能是不同的甚至是出人意料的。無意中創建的屬性“阻止”了原型鏈上查找這個屬性的行為,我們稱之為屬性屏蔽

屬性屏蔽根據變量本身情況的不同會有很多不同的狀態表現,例如原型鏈上層變量的數據訪問屬性標記為只讀的情況,(如果不是嚴格模式下)嘗試進行的賦值操作會被忽略等。

類 (迫真)

我們早已知道 JavaScript 中不存在“類”的概念,而為了能夠“寫着爽”,很多開發者都在想盡辦法在 JavaScript 中模仿其它 OO 語言中“類”的行為。其中很常見的做法類似下面這樣:

function Person(name) {
    console.log("I'm " + name + "!");
    this.name = name;
}

Person.prototype.getName = function() {
    return this.name;
}

var chris = new Person("Chris"); // I'm Chris
var sophie = new Person("Sophie"); // I'm Sophie
chris.getName(); // "Chris"

看上去我們的 Person 像極了一個包含 name 成員變量和 getName() 方法的類,並且在其“構造函數”中會輸出 “I’m xxx”。不過在之前的文章中我們已經講過了,並不存在所謂的構造函數,new 只是把 Person() 函數作為構造對象所需調用的函數進行了一次調用而已。不過你可能還會比較奇怪為什麼 .getName() 是可以使用的,既然我們在原型鏈這一章提起這件事,顯然是因為原型鏈,於是回顧之前第二章我們含糊提到的一句話是(之一是)“對這個新對象執行 [[Prototype]] 鏈接”,實際上,這裏我們被 new 出來的對象的 [[Prototype]] 被關聯到了 Person.prototype 上,於是當我們嘗試進行屬性訪問的時候,自然就可以訪問到 Person.prototype.getName() 上了。

不過這個過程還是可能會引起一些蛋疼的誤會,比如假設我們在上面例子的基礎上:

...
sophie.constructor === Person; // true
sophie.constructor === Person.prototype.constructor; // true
Person.prototype = {};
var koishi = new Person("Koishi");  // I'm Koishi
koishi.constructor === Person; // false
koishi.constructor === Object; // true
sophie.constructor === Person; // true
sophie.constructor === Person.prototype.constructor; // false

由於“構造函數”這種表現形式的理解,我們有時候會認為 變量名.constructor 實際就總是構造調用時指向的函數,甚至 sophie.constructor === Person 返回也是 true ,但實際並不是這樣,這裏返回為真,僅僅是因為 Person.prototype.constructor 默認指向的就是 Person 罷了。於是我們嘗試替換 Person.prototype 之後創建了變量 koishi,再檢查 koishi.constructor === Person 就不再為真了,在原型鏈的查找過程最終找到了 Object.prototype,然後 Object.prototype.constructor 其實指向了 Object

不過,後面我們接着嘗試檢查了 sophie.constructor 卻發現似乎它並未受到影響,這個就不要往原型鏈方面想了,這裏的原因僅僅是 sophie 的原型鏈指向的是曾經 Person.prototype 所指向的東西上,而我們 Person.prototype = {} 的操作只是讓 Person.prototype 指向了新的東西,舊的東西並沒有改變,所以 sophie 自然看上去“沒有受到影響”了。當然,koishi 這個變量被構造時所被調用的函數仍然是 Person(),這和 koishi.constructor 或者 Person.prototype.constructor 的指向沒有什麼關係。

對象實例關係

當然我們還有一點需要重新強調的是,[[Prototype]].prototype 不是一回事,[[Prototype]] 是描述對象實例關係的屬性描述符,而 .prototype 只是 Function 對象的一個屬性而已。new 操作符會把新建的對象的 [[Prototype]] 指向原對象的 .prototype 屬性上,僅此而已。

既然 [[Prototype]] 實際描述了對象之間的實例關係,那麼我們自然就可以想到 instanceof 的實際作用了,其所做的事情就是告訴你在 a instanceof Foo 中, a 的整個原型鏈中是否有指向 Foo.prototype 的對象。

絕大多數瀏覽器支持一個 .__proto__ 屬性(實際位於 Object.__proto__)指向了 [[Prototype]] ,這對於我們調試時希望直接訪問內部的 [[Prototype]] 提供了便利,不過它並不是標準,所以除了調試便利之外還是不要使用它比較好。

最後

於是關於原型鏈相關的簡單討論就到此結束了。和上篇一樣,如果你對這些內容仍然感興趣,不妨去讀一讀《You don’t know JS – this & object prototypes》一書。這是一本開源書,你可以在這裡在線閱讀這本書,或者購買這本書的电子版或實體版。這本書的中文譯本涵蓋在《你所不知道的 JavaScript 上卷》中,你也可以考慮看中文版。

由於近期工作過於繁忙的精力佔用緣故,“原來JS是這樣的”系列可能就暫時告一段落了。最後,儘管我會盡可能仔細的檢查文章內容是否有問題,但也不保證這篇文章中一定不會有錯誤,如果您發現文章哪裡有問題,請在下面留言指正,或通過任何你找得到的方式聯繫我指正。感激不盡~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

02-MyBatis執行Sql的流程分析

目錄

本博客着重介紹MyBatis執行Sql的流程,關於在執行過程中緩存、動態SQl生成等細節不在本博客中體現,相應內容後面再單獨寫博客分析吧。

還是以之前的查詢作為列子:

public class UserDaoTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws Exception{
        ClassPathResource resource = new ClassPathResource("mybatis-config.xml");
        InputStream inputStream = resource.getInputStream();
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void selectUserTest(){
        String id = "{0003CCCA-AEA9-4A1E-A3CC-06D884BA3906}";
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CbondissuerMapper cbondissuerMapper = sqlSession.getMapper(CbondissuerMapper.class);
        Cbondissuer cbondissuer = cbondissuerMapper.selectByPrimaryKey(id);
        System.out.println(cbondissuer);
        sqlSession.close();
    }

}

之前提到拿到sqlSession之後就能進行各種CRUD操作了,所以我們就從sqlSession.getMapper這個方法開始分析,看下整個Sql的執行流程是怎麼樣的。

獲取Mapper

進入sqlSession.getMapper方法,會發現調的是Configration對象的getMapper方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //mapperRegistry實質上是一個Map,裏面註冊了啟動過程中解析的各種Mapper.xml
    //mapperRegistry的key是接口的全限定名,比如com.csx.demo.spring.boot.dao.SysUserMapper
    //mapperRegistry的Value是MapperProxyFactory,用於生成對應的MapperProxy(動態代理類)
    return mapperRegistry.getMapper(type, sqlSession);
}

進入getMapper方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    //如果配置文件中沒有配置相關Mapper,直接拋異常
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //關鍵方法
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

進入MapperProxyFactory的newInstance方法:

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  //生成Mapper接口的動態代理類MapperProxy
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
  
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

下面是動態代理類MapperProxy,調用Mapper接口的所有方法都會先調用到這個代理類的invoke方法(注意由於Mybatis中的Mapper接口沒有實現類,所以MapperProxy這個代理對象中沒有委託類,也就是說MapperProxy幹了代理類和委託類的事情)。好了下面重點看下invoke方法。

//MapperProxy代理類
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //獲取MapperMethod,並調用MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

  @UsesJava7
  private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
      throws Throwable {
    final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
        .getDeclaredConstructor(Class.class, int.class);
    if (!constructor.isAccessible()) {
      constructor.setAccessible(true);
    }
    final Class<?> declaringClass = method.getDeclaringClass();
    return constructor
        .newInstance(declaringClass,
            MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
        .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
  }

  /**
   * Backport of java.lang.reflect.Method#isDefault()
   */
  private boolean isDefaultMethod(Method method) {
    return ((method.getModifiers()
        & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
        && method.getDeclaringClass().isInterface();
  }
}

所以這邊需要進入MapperMethod的execute方法:

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //判斷是CRUD那種方法
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

然後,通過一層一層的調用,最終會來到doQuery方法, 這兒咱們就隨便找個Excutor看看doQuery方法的實現吧,我這兒選擇了SimpleExecutor:

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //內部封裝了ParameterHandler和ResultSetHandler
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      //StatementHandler封裝了Statement, 讓 StatementHandler 去處理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

接下來,咱們看看StatementHandler 的一個實現類 PreparedStatementHandler(這也是我們最常用的,封裝的是PreparedStatement), 看看它使怎麼去處理的:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
     //到此,原形畢露, PreparedStatement, 這個大家都已經滾瓜爛熟了吧
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    //結果交給了ResultSetHandler 去處理,處理完之後返回給客戶端
    return resultSetHandler.<E> handleResultSets(ps);
  }

到此,整個調用流程結束。

簡單總結

這邊結合獲取SqlSession的流程,做下簡單的總結:

  • SqlSessionFactoryBuilder解析配置文件,包括屬性配置、別名配置、攔截器配置、環境(數據源和事務管理器)、Mapper配置等;解析完這些配置後會生成一個Configration對象,這個對象中包含了MyBatis需要的所有配置,然後會用這個Configration對象創建一個SqlSessionFactory對象,這個對象中包含了Configration對象;
  • 拿到SqlSessionFactory對象后,會調用SqlSessionFactory的openSesison方法,這個方法會創建一個Sql執行器(Executor組件中包含了Transaction對象),這個Sql執行器會代理你配置的攔截器方法
  • 獲得上面的Sql執行器后,會創建一個SqlSession(默認使用DefaultSqlSession),這個SqlSession中也包含了Configration對象和上面創建的Executor對象,所以通過SqlSession也能拿到全局配置;
  • 獲得SqlSession對象后就能執行各種CRUD方法了。

以上是獲得SqlSession的流程,下面總結下本博客中介紹的Sql的執行流程:

  • 調用SqlSession的getMapper方法,獲得Mapper接口的動態代理對象MapperProxy,調用Mapper接口的所有方法都會調用到MapperProxy的invoke方法(動態代理機制);
  • MapperProxy的invoke方法中唯一做的就是創建一個MapperMethod對象,然後調用這個對象的execute方法,sqlSession會作為execute方法的入參;
  • 往下,層層調下來會進入Executor組件(如果配置插件會對Executor進行動態代理)的query方法,這個方法中會創建一個StatementHandler對象,這個對象中同時會封裝ParameterHandler和ResultSetHandler對象。調用StatementHandler預編譯參數以及設置參數值,使用ParameterHandler來給sql設置參數。

Executor組件有兩個直接實現類,分別是BaseExecutor和CachingExecutor。CachingExecutor靜態代理了BaseExecutor。Executor組件封裝了Transction組件,Transction組件中又分裝了Datasource組件。

  • 調用StatementHandler的增刪改查方法獲得結果,ResultSetHandler對結果進行封裝轉換,請求結束。

Executor、StatementHandler 、ParameterHandler、ResultSetHandler,Mybatis的插件會對上面的四個組件進行動態代理。

重要類

  • MapperProxyFactory

  • MapperProxy

  • MapperMethod

  • SqlSession:作為MyBatis工作的主要頂層API,表示和數據庫交互的會話,完成必要數據庫增刪改查功能;

  • Executor:MyBatis執行器,是MyBatis 調度的核心,負責SQL語句的生成和查詢緩存的維護;

    StatementHandler 封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設置參數、將Statement結果集轉換成List集合。
    ParameterHandler 負責對用戶傳遞的參數轉換成JDBC Statement 所需要的參數,
    ResultSetHandler 負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合;
    TypeHandler 負責java數據類型和jdbc數據類型之間的映射和轉換
    MappedStatement MappedStatement維護了一條<select|update|delete|insert>節點的封裝,
    SqlSource 負責根據用戶傳遞的parameterObject,動態地生成SQL語句,將信息封裝到BoundSql對象中,並返回
    BoundSql 表示動態生成的SQL語句以及相應的參數信息

    Configuration MyBatis所有的配置信息都維持在Configuration對象之中。

參考

  • https://www.cnblogs.com/dongying/p/4031382.html
  • https://blog.csdn.net/qq_38409944/article/details/82494187

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

印度研究:森林大火致黑碳更濃 加速冰河融化

摘錄自2020年2月4日中央通訊社報導

印度一項最新研究顯示,森林大火引發的黑碳(black carbon)濃度增加,加速冰河融化,更引發海平面升高及氣候變遷惡化。印度斯坦時報(Hindustan Times)今天(4日)引述WIHG科學家內基(PS Negi)指出,經過研究團隊過去幾年在北阿坎德省根戈催里冰河(Gangotri Glacier)鄰近地區的觀察與研究,森林大火導致黑碳濃度增加近一倍,加速了這條冰河的融化速度。

黑碳是一種懸浮粒子,源於石油、煤、木炭、樹木、柴草、塑膠垃圾、動物糞便等含碳物質燃燒不完全及氧化後所形成的產物。內基說,黑碳已公認是造成氣候變遷的第二重要人為因素。由於黑碳吸收更多光源且釋放紅外線輻射,從而提高當地溫度,導致冰河融化。當喜瑪拉雅山區高處的黑碳濃度增加時,使喜瑪拉雅山區的冰河融化更快。他認為,由於源自全球、區域和本地的開發及污染不斷累積,導致喜瑪拉雅山區的黑碳濃度增加,進而加速冰河融化,連帶影響全球氣候變遷。

至於森林大火的起因,根據印度森林調查報告(Forest Survey of India),北阿坎德省通常在2到6月通報森林火災,火災起因包括人為因素及閃電。根據官方統計,從2000年以來,北阿坎德省森林大火導致4萬4554公頃的森林遭到破壞。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

紐西蘭南島大淹水 宣布進入緊急狀態

摘錄自2020年02月05日公視報導

紐西蘭南島最南端的南地大區,過去60個小時,當地已經降下超過1000毫米的大雨,造成多處河川暴漲或潰堤、交通要道爆發土石流,一度造成200名旅客受困,兩人受傷。迫使南地大區宣布進入緊急狀態,紐西蘭氣象局也首次發布天氣紅色警戒。

為了大家生命安全著想,當局5日上午通知6000名住在地勢低窪地區的民眾緊急撤離家園。同時採取預防性停電等措施,避免災情擴大。目前預估未來幾天仍會持續降雨,所幸雨勢將會減小。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

用吃的就解決! 外來種海膽破壞海藻林 新創公司投入壽司商機

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益