減塑全球風潮 2020年起泰國商場禁用塑膠袋

摘錄自2019年11月18日NOWnews報導

為了達到環保的目的,2020年起泰國許多連鎖超商或大賣場將不再供應塑膠袋。根據路透社的報導,泰國國家資源和環境部部長瓦拉伍特(Varawut Silpa-archa)在臉書宣佈,許多商店與政府合作,暫停提供一次用塑膠袋,措施將於2020年1月起生效。

「方便性會下降,不過這是為了延續環境的生態循環。」瓦拉伍特受訪時說,他建議民眾可以使用可重複利用的大布袋取代塑膠袋。

不再提供塑膠袋的商店包括在泰國管理上千家7-11的CP All Plc公司和泰國購物商場集團。其實,這並不是泰國第一次嘗試縮減塑膠袋使用的政策。早在2018年,CAN便報導過泰國旅遊局曾發佈「國家淨化聲明」,希望在2021年時,能減少一半以上現行使用的塑膠袋總用量。

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

【其他文章推薦】

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

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

因為我的一個低級錯誤,生產數據庫崩潰了將近半個小時

前言

halo,相信大家一定過了一個很開心的端午節吧,我看朋友圈裡各種曬旅遊,曬美食的,真是羡慕啊,不像我,感冒了只能在家擼文章。
當然,玩的多開心,節後上班就有多鬱悶,假日綜合征可不是說說而已。對此我想表達的是,沒事,不用鬱悶,來看我如何自爆家醜來讓你們開心下。

反常的sql語句

上周四午休時分,我正在工位上小憩,睡夢中彷彿看到了自己拿着李白在榮耀峽谷里大殺四方的情景,就在我剛拿完五殺準備帶領隊友推對面水晶的時候,一句慌亂急促的“糟了”把我從睡夢中驚醒。我眯開朦朧的雙眼,才發現剛才的發聲來源於我的組長庄哥,看到他在緊張的點開日誌系統查看日誌,我預感到有什麼不妙的事情發生,仔細一問才知道,原來就在我眯眼的期間,線上數據庫服務器的CPU被打滿,同時觸發了生產數據庫只讀延遲的限定時間並且發出告警,而且告警的過程持續了半個小時。

這讓我倒吸了一口涼氣,因為我們組做的系統很多都用的是同一個數據庫服務器,日用戶活躍量有好幾十萬,如果服務器崩潰了將會使所有的系統服務都不可用,於是我們趕緊通過sql日誌進行問題查找,最後排查出來是因為一張sql的高量查詢沒有走索引導致,日誌列表显示,這條sql語句的掃描行數達到了上百萬,基本就是全表掃描的情況,而且半個小時的時間查詢了達上萬次,每條sql查詢的耗時都在3000ms以上。我的天啊,難怪服務器會CPU打滿,這麼一條耗時的sql語句查詢量這麼大,數據庫的資源當然是直接就崩潰了,這是當時那條sql的查詢情況:

臨時處理

看了這條語句,我又倒吸一口涼氣,這不就是我寫的系統調用的sql語句嗎?完了,這回逃不掉了,真是人在睡夢裡,鍋從天上來。

當然,因為是我自己寫的sql,所以我一看就知道這條語句是有問題的。

根據我的代碼處理,這條sql的調用還少了個重要的參數user_fruit_id,這個參數沒有傳的話是不應該走這條sql查詢的,在我的設計里,該參數是數據表裡一個聯合索引的最左側字段,如果該字段沒有傳值的話,那麼索引就不會生效了。

KEY `idx_userfruitid_type` (`user_fruit_id`,`task_type`,`receive_start_time`,`receive_end_time`) USING BTREE

雖然定位到了sql語句,但是線上的問題刻不容緩,總不可能找出bug改完再上線吧,所以,我們只能做了一個臨時處理,就是在原來的表上多加了一個聯合索引,其實就是去掉了user_fruit_id 字段,讓這些高量的查詢都能走新的索引,就像下面這樣

KEY `idx_task_type_receive_start_time` (`task_type`,`receive_start_time`,`receive_end_time`,`created_time`) USING BTREE

加上索引后,sql的掃描行數就大幅度的降低了,重啟實例后就又能正常運行了。

最左匹配原則

那麼為什麼最左側的字段沒傳索引就不生效了,這是因為MySQL的聯合索引是基於“最左匹配原則”匹配的。

我們都知道,索引的底層是B+樹結構,聯合索引的結構也是B+樹,只不過鍵值數量不是一個,而是多個,構建一顆B+樹只能根據一個值來構建,因此數據庫依據聯合索引最左的字段來構建B+樹。

例如我們用兩個字段(name,age)這個聯合索引來分析,

圖片來源於林曉斌老師的《MySQL實戰45講》課程,

當我們在where條件中查找name為“張三”的所有記錄的時候,可以快速定位到ID4,並且查出所有包含“張三”的記錄,而如果要查找“張三,10”這一條特定的數據,就可以用 name = “張三” and age = 10 獲取,因為聯合索引的鍵值對是兩個,所以只要前面的name確定的情況下就可以進一步定位到具體的age記錄,但是如果你的查詢條件只有age的話,那麼索引就不會生效,因為沒有匹配最左邊的字段,後面所有的索引字段都不會生效,所以我之前寫的sql語句才會因為少了最左邊的user_fruit_id字段而走了全表掃描的查詢方式。

正常來說,假設一個聯合索引設計成(a,b)這樣的結構的話,那麼用a and b作為條件,或者a單獨作為查詢條件都會走索引,這種情況下我們就不要再為a字段單獨設計索引了。

但如果查詢條件裏面只有b的語句,是無法使用(a,b)這個聯合索引的,這時候你不得不維護另外一個索引,也就是說你需要同時維護(a,b)、(b) 這兩個索引。

找出Bug

雖然臨時做了處理,但問題並不算解決,很明顯是系統出現了bug才會有走這樣的查詢條件。因為是我自己寫的代碼,所以知道是哪條sql后我就馬上定位到了代碼里的具體方法,後來才發現是因為我對user_fruit_id字段的判空處理不生效所致。

因為該字段是從調用方傳過來的,所以我在方法參數里對該字段做了非空限制的註解,也就是javax包下的@NotNull,

public class GardenUserTaskListReq implements Serializable {

    private static final long serialVersionUID = -9161295541482297498L;

    @ApiModelProperty(notes = "水果id")
    @NotNull(message = "水果id不能為空")
    private Long userFruitId;
    /**以下省略*/
    .....................
}

雖然加上該註解來做非空校驗,但我卻沒有在參數加上另一個註解@Validated,該註解如果沒加上的話,那麼調用javax包下的校驗規則就都不生效,正確的寫法是在controller層方法的參數前面加上註解,

除此之外,因為user_fruit_id這個字段是另一張表的主鍵,我在代碼里也沒有對這張表是否存在這個id做查詢判斷,這樣一來,無論調用方傳什麼值過來都會直接觸發sql查詢,並且在不跑索引的情況下直接走全表掃描。

不得不說,這真是個低級錯誤,說真的,我對這個原因真是感到嘀笑皆非,再怎麼說也工作幾年了,怎麼還犯一些新手級別的錯誤呢,這臉打得真是讓我相當慚愧。

總結

雖然是低級錯誤,但造成的後果也算挺嚴重了,這次事件也讓我更加的警醒,在以後的開發工作中必須要遵守該有的原則,大概有這麼幾點:

1、不能相信調用端。重要的參數都要先做驗證,即使是非空值也需要做驗證,不符合條件的就要直接返回或拋異常,不能參与業務sql的查詢,否則頻繁的訪問也會對服務造成負擔。

2、sql語句要先做性能查詢。對於數據量大的表,建好索引后,所有的sql查詢語句要用explain檢測性能,並且根據結果來進一步優化索引。

3、代碼必須要review。之前我沒有放太大的精力在代碼的review上,雖說跟迭代排期的緊湊也有關係,但不管怎麼說,bug確實是我的疏忽造成的,尤其是像空值這種細小的錯誤在Java里可以說家常便飯。千里之堤毀於蟻穴,有時一個小bug很容易就引發整個系統的崩盤,這一次的問題也讓我更加深刻的認識到了review代碼的重要性,不管業務開發的工作量有多麻煩,這一步操作絕對不能忽視。

後續

知道了bug的原因,改完代碼當天就重新發布了,後來,庄哥告訴我說,為了以後讓組裡的其他人對此次問題有所警戒,讓我寫一篇問題記錄總結一下,我想了一下,這不是我的強項啊,但怎麼說也確實是自己的問題,還是老老實實的寫一下記錄好了。我本以為這樣就可以松一口氣了,可平哥 (組裡的一位大佬) 卻突然用詭異的眼神看着我,語重心長的說,上次xxx也因為線上出現問題寫了報告,你這一次估計也不能例外了,可能要一萬字以上。我瞬間就感覺一個雷劈到了我頭上,蒼天啊。。。。。。

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

※別再煩惱如何寫文案,掌握八大原則!

Java 從入門到進階之路(二十五)

在之前的文章我們介紹了一下 Java 中的  集合框架中的Collection 的子接口 List的 增刪改查和與數組間相互轉換的方法,本章我們來看一下 Java 集合框架中的Collection 的子接口 List 的另外一些方法。

我們在使用集合的時候難免會對其中的元素進行排序,因為 Set 集合本身是無序的,所以本章將着重講解 List 集合,如下:

 1 import java.util.ArrayList;
 2 import java.util.Collections;
 3 import java.util.List;
 4 import java.util.Random;
 5 
 6 /**
 7  * 排序集合元素
 8  * 排序集合使用的是集合的工具類 Collections 的靜態方法 sort
 9  * 排序僅能對 List 集合進行,因為 Set 部分實現類是無序的
10  * */
11 public class Main {
12     public static void main(String[] args) {
13         List<Integer> list = new ArrayList<Integer>();
14         Random random = new Random();
15         for(int i=0;i<10;i++){
16             list.add(random.nextInt(100));
17         }
18         System.out.println(list); // [49, 24, 29, 59, 56, 1, 1, 5, 49, 60]
19 
20         Collections.sort(list);
21         System.out.println(list); // [1, 1, 5, 24, 29, 49, 49, 56, 59, 60]
22     }
23 }

在上面的代碼中,我們隨機生成了一些正數並添加到 List 集合中,我們通過 sort 方法,系統變自動按照自然數的排列方法為我們做好了排序,很是方便,那如果 List 中使我們自己定義的內容,比如說一個類,那該如何排序呢,如下:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 排序集合元素
 * 排序集合使用的是集合的工具類 Collections 的靜態方法 sort
 * 排序僅能對 List 集合進行,因為 Set 部分實現類是無序的
 */
public class Main {
    public static void main(String[] args) {
        List<Point> list = new ArrayList<Point>();
        list.add(new Point(1, 2));
        list.add(new Point(2, 3));
        list.add(new Point(4, 2));
        list.add(new Point(2, 5));
        list.add(new Point(9, 3));
        list.add(new Point(7, 1));
        System.out.println(list); // [(1, 2), (2, 3), (4, 2), (2, 5), (9, 3), (7, 1)]
        // Collections.sort(list); // 編譯錯誤,不知道 Point 排序規則
        /**
         * sort 方法要求集合必須實現 Comparable 接口
         * 該接口用於規定實現類事可以比較的
         * 其中一個 compareTo 方法時用來定義比較大小的規則
         * */
        Collections.sort(list);
        System.out.println(list); // [(1, 2), (2, 3), (4, 2), (2, 5), (7, 1), (9, 3)]

    }
}

class Point implements Comparable<Point> { // 定義為泛型 T 類型
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }

    /**
     * 當實現了 Comparable 接口后,需要重寫下面的方法
     * 該方法的作用是定義當前對象與給定參數對象比較大小的規則
     * 返回值為一個 int 值,該值表示大小關係
     * 它不關注具體的取值是多少,而關注的是取值範圍
     * 當返回值 >0 時:當前對象不參數對象大
     * 當返回值 <0 時:當前對象比參數對象小
     * 當返回值 =0 時:兩個對象相等
     */
    @Override
    public int compareTo(Point o) {
        /**
         * 比較規則,點到原點的距離長的打
         * */
        int len = this.x * this.x + this.y * this.y;
        int olen = o.x * o.x + o.y * o.y;
        return len - olen;
    }
}

在上面的代碼中,我們跟之前一樣定義了一個 Point 類,然後實例化后存入 list 集合中,如果我們相對 Point 進行排序,如果直接調用 sort 方法會報錯,這是由於編譯器不知道我們定義的 Point 類的排序規則,所以我們需要通過接口 Comparable 接口來自定義排序規則,如下圖:

 

我們可以通過坐標系中點到原點的距離來判斷 Point 的大小,即 x*x+y*y 的大小,然後通過重寫 compareTo 方法來實現 sort 。

實際上雖然能實現,但是並不理想,為了實現一個 sort 方法,要求我們的集合元素必須實現 Comparable 接口並且定義比較規則,這種我們想使用某個功能,而它要求我們修改程序的現象稱為“侵入性”。修改的代碼越多,侵入性越強,越不利於程序的擴展。

我們再來看一下下面的排序:

 1 import java.util.ArrayList;
 2 import java.util.Collections;
 3 import java.util.List;
 4 
 5 public class Main {
 6     public static void main(String[] args) {
 7         List<String> list1 = new ArrayList<String>();
 8         list1.add("Java");
 9         list1.add("c++");
10         list1.add("Python");
11         list1.add("PHP");
12         Collections.sort(list1);
13         System.out.println(list1); // [Java, PHP, Python, c++]
14 
15         List<String> list2 = new ArrayList<String>();
16         list2.add("孫悟空");
17         list2.add("豬八戒");
18         list2.add("唐僧");
19         list2.add("六小齡童");
20         Collections.sort(list2);
21         System.out.println(list2); // [六小齡童, 唐僧, 孫悟空, 豬八戒]
22 
23     }
24 }

在上面的排序中,當我們對字母或漢字進行排序時,其實是比的第一個字的 Unicode 碼,那如果我們不使用 Comparable 的接口該如何自定義我們想要的實現規則呢?比如根據字符串的長度來進行排序,其實 sort 有一個額外的重載方法來實現我們想要的結果,如下:

 1 import java.util.ArrayList;
 2 import java.util.Collections;
 3 import java.util.Comparator;
 4 import java.util.List;
 5 
 6 public class Main {
 7     public static void main(String[] args) {
 8         /**
 9          * 重載的 sort 方法要求傳入一個額外的比較器
10          * 該方法不再要求集合元素必須實現 Comparable 接口
11          * 並且不再使用集合元素資深的比較規則排序了
12          * 而是根據給定的這個額外的比較器的比較規則對集合元素進行排序
13          * 實際開發中也推薦使用這種方式進行集合元素排序
14          * 若集合元素是自定義的
15          * 創建比較器時推薦使用匿名內部類的形式
16          */
17         List<String> list1 = new ArrayList<String>();
18         list1.add("Java");
19         list1.add("c++");
20         list1.add("Python");
21         list1.add("PHP");
22         MyComparator myComparator1 = new MyComparator();
23         Collections.sort(list1, myComparator1); // 重載 sort
24         System.out.println(list1); // [c++, PHP, Java, Python]
25 
26         List<String> list2 = new ArrayList<String>();
27         list2.add("孫悟空");
28         list2.add("豬八戒");
29         list2.add("唐僧");
30         list2.add("六小齡童");
31         // 匿名內部類形式創建
32         Comparator<String> myComparator2 = new Comparator<String>() {
33             @Override
34             public int compare(String o1, String o2) {
35                 return o1.length() - o2.length();
36             }
37         };
38         Collections.sort(list2, myComparator2); // 重載 sort
39         System.out.println(list2); // [唐僧, 孫悟空, 豬八戒, 六小齡童]
40 
41     }
42 }
43 
44 /**
45  * 定義一個額外的比較器
46  * 該方法用來定義 o1 和 o2 的比較
47  * 若返回值 >0:o1>o2
48  * 若返回值 <0:o1<o2
49  * 若返回值 =0:兩個對象相等
50  */
51 class MyComparator implements Comparator<String> {
52     @Override
53     public int compare(String o1, String o2) {
54         /**
55          * 字符串中字符多的大
56          * */
57         return o1.length() - o2.length();
58     }
59 }

    

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

【其他文章推薦】

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

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

鋰電池再生利用,新創事業 FreeWire 推行動充電車

  特斯拉(Tesla)高調宣布鋰電池儲能系統,其中針對企業用戶的「Powerpack」,100 度電售價 25,000 美元,已將平均儲能成本拉低到相當驚人的每度電 250 美元,一般認為若平均儲能成本降到每度電 100 美元,將引爆能源儲存革命,特斯拉挾 GigaFactory 的威力,正全速逼近這個目標,不過,卻有一個小型新創事業,認為現在就可以做到。   這家新創事業 FreeWire 初期募資金額不過 42.5 萬美元,加上美國海軍資助 50 萬美元,如何能超越家大業大的特斯拉?其實原理也不難,就是採用二手電池。   FreeWire 、日產(Nissan)與 NEC 合資的汽車能源供應公司(Automotive Energy Supply Corporation,AESC)合作,AESC 是僅次於 Panasonic,全球第二大車用鋰電池供應商,市佔率達 21%,2014 年銷售最佳的電動車是 Nissan Leaf,其電池正是由 AESC 供應,Nissan 每個月都會測試電池,每六個月就汰換效能已經衰退的鋰電池,數量以千計,對 Nissan 來說庫存這些二線電池是個麻煩,但這些電池的效能或許已經不不敷電動車所需,拿來做為能源儲存用途卻還很有用。   於是,FreeWire 以新電池六分之一的價格取得這些二手電池,第一步,是取之於電動車用之於電動車,將這些二手電池組裝成總容量 48 度電的行動充電車,專供電動車在本來沒有設置充電座的停車場、機場、企業廠區等處充電,行動充電車可以推到停車場任何一個停車格,幫電動車充電,如此一來,停車場可以省下設置大量充電座的成本,卻能提供身為少數的電動車主充電服務,而這些二手電池在每天充放電循環 2 次的情況下,還可以有 5 年的壽命。  
 

    由於使用二手電池,FreeWire 裝置的每度電成本只有 100 美元,執行長阿卡迪‧索西諾夫(Arcady Sosinov)表示,電池產業界要到 2030 年才能達到的成本目標,他們用二手電池壓低成本,所以現在就已經做到。   阿卡迪‧索西諾夫的野心不只是為電動車行動充電,他認為二手電池的商機相當廣大,同樣的系統也可用來取代柴油發電機,為沒有電力供應之處供電,或是作為備援電力,而鋰電池供電比起柴油發電機有更大的優勢,那就是不會產生噪音與廢氣。如果要在戶外辦一場蚊子電影院,使用鋰電池系統顯然更好,而許多開發中國家的手機基地台因電網不穩定都有備用電源需求,以往這些取代柴油發電機的系統生意由奇異的獨拉松獨佔,如今以二手電池成本之低,也可與之競爭。   更進一步,二手電池系統也可進軍特斯拉正大張旗鼓進入的家庭與企業能源儲存領域,不過,二手電池的發展,也不是只會和特斯拉打對台,因為特斯拉發展電動車也一樣會面臨汰換大量二手電池,這些二手電池若都可再利用製成能源儲存裝置,對特斯拉也是一項利多。   FreeWire 利用二手電池的想法也給能源儲存業界一個啟發,過去鋰電池效能會衰退是產業界的一大問題,但換個方向思考,開啟電池的分級使用,反而可開創許多價格彈性商機。     本文全文授權轉載自《科技新報》─〈〉

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

※別再煩惱如何寫文案,掌握八大原則!

刺激銷量?北京6月起收取電動車充電服務費

北京市發改委於5月7日發佈了《關於本市電動汽車充電服務收費有關問題的通知》,從2015年6月1日起,提供電動汽車充電服務的充電設施經營單位在收取電費的同時,可按充電電量額外收取充電服務費,每千瓦時收費上限標準為當日本市92號汽油每升最高零售價的15%。各經營單位可在不超過上限標準情況下,制定具體收費標準。   其充電服務費上限標準隨油價變動自行動態調整。如2015年4月29日本市92號汽油最高零售價為6.46元人民幣(下同)/升,則充電服務收費上限標準為6.46元的15%,即0.97元/度。油價上升,充電服務費相應上升油價下降,充電服務費相應下降。   北京市發改委副主任高朋指出,根據測算,此標準可以確保電動汽車動力成本低於燃油汽車。以本市銷售相對較好的北汽E150EV電動汽車為例(100公里平均耗電16度),當油價在6-10元/升區間變動時,充電服務費為每度電0.9元-1.5元。按國家發展改革委檔,對向電力公司直接報裝的充電服務設施,按大工業用電價格標準執行,按此測算,加上電價費用,電動汽車動力成本約為同款燃油汽車的50%-60%左右。   對此有觀點認為,發改委此舉會影響電動汽車銷量,降低消費者對電動車的購買慾。汽車產業分析師張志勇指出,目前,電動車推廣最大的障礙是充電問題,《通知》中規定了電動車充電服務費,可以鼓勵一些民營資本來加入電動車充電網路的建設,此舉將加快北京地區的充電網路建設。   張志勇還指出,即使充電設施經營單位額外收取充電服務費,這個成本也是遠遠低於燃油車的用車成本,這對消費者來看並不能產生多大影響。因此《通知》發布後不僅不會影響電動車的銷量,反而會增加消費者購買電動車的信心。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

Salesforce LWC學習(十八) datatable展示 image

本篇參看:

https://developer.salesforce.com/docs/component-library/bundle/lightning-datatable/documentation

https://www.lightningdesignsystem.com/components/data-tables/

我們在salesforce中,經常會使用 listview,標準的 lightning listview會有排序,filter展示chart等等標準功能。當然,某些情況標準搞定不了情況下,我們便需要使用自定義的開發。自定義開發我們就可以使用 lightning-datatable組件。一個 lightning-datatable組件用來展示表格數據,它可以根據數據類型展示每一列。比如一個 email類型的字段會展示一個點開以後是一個 mail:to的超鏈接。默認展示的類型是text類型。那 lightning-datatable有哪些特性呢?

  • 展示和格式化焗油適當類型的列
  • 具有無限滾動行功能
  • 指定的數據類型允許 inline編輯
  • 可以定義header的action
  • 可以定義行級別的action
  • 重置每一列展示的大小
  • 選擇行操作
  • 指定的列通過升序或者降序排列
  • 文字換行和剪切
  • 對行進行編號
  • 設置單元格對齊方式

lightning-datatable有很多屬性,其他的請自行查看文檔,這裏只例舉幾個核心屬性或者方法。比較核心的如下:

  • columns:一組列的object用來定義數據類型,這個object有很多的屬性,其中有幾個必填的配置: label & fieldName & type。分別對應着字段的展示名稱,字段的API name以及字段的類型,不同的字段類型將會自動的轉換成不同的展示形式,默認是text。其他的屬性我們後期會有簡單介紹。
  • data:一組數據用來展示,通常 comulns設置表單的頭以及每個單元列的類型,data設置內容;
  • hide-checkbox-column:標籤用來設定是否展示左側的checkbox,值為true/false,true即隱藏checkbox;
  • selected-rows:用來初始化時展示哪些行是被選中的;
  • key-field:用來指定每行的unique id;
  • getSelectedRows:這個是lightning-datatable提供的方法,用來獲取哪些行用戶選中的。

簡單介紹完畢,下面通過幾點來慢慢帶入datatable的功能。

一. 基礎 Datatable展示數據

demo中展示,我們來看官方提供的一個demo。

dataTableExample1.html:用來展示一個 datatable,我們看到屬性中的data / columns / key-field都是上面描述過的, onrowselection方法為當有行選擇的時候的調用

<template>
    <lightning-datatable
            data={data}
            columns={columns}
            key-field="id"
            onrowselection={getSelectedName}>
    </lightning-datatable>
</template>

 dataTableExample1.js:有兩種方式可以獲取選擇的行信息,demo中有兩個。datatable有一個標準事件rowselection,調用以後可以通過event.detail.

selectedRows獲取選中的行的信息,另外一種就是找到datatable然後調用其getSelectedRows方法。

除此之外,我們看到columns的設置方式,除了 label / fieldName / type這三個基礎信息以外,還可以看到有 typeAttributes 以及 cellAttributes兩種設置。

  • typeAttributes:用於對指定的列的類型格式化dataCell,不同的type可能擁有不同的設置。salesforce針對不同的type可以設置不同的 typeAttributes,這裏不再詳細展開,具體查看文檔;
  • cellAttributes:typeAttributes用於設置針對 type的格式化,cellAttributes通常用於列中附加圖標信息,設置圖標位置等信息。
import { LightningElement } from 'lwc';

const columns = [
     {label: 'Opportunity name', fieldName: 'opportunityName', type: 'text'},
     {label: 'Confidence', fieldName: 'confidence', type: 'percent', cellAttributes:{ iconName: { fieldName: 'trendIcon' }, iconPosition: 'right' }},
     {label: 'Amount', fieldName: 'amount', type: 'currency', typeAttributes: { currencyCode: 'EUR'}},
     {label: 'Contact Email', fieldName: 'contact', type: 'email'},
     {label: 'Contact Phone', fieldName: 'phone', type: 'phone'},
];

const data = [{
                    id: 'a',
                    opportunityName: 'Cloudhub',
                    confidence: 0.2,
                    amount: 25000,
                    contact: 'jrogers@cloudhub.com',
                    phone: '2352235235',
                    trendIcon: 'utility:down'
                },
                {
                    id: 'b',
                    opportunityName: 'Quip',
                    confidence: 0.78,
                    amount: 740000,
                    contact: 'quipy@quip.com',
                    phone: '2352235235',
                    trendIcon: 'utility:up'
                }];

export default class DatatableExample1 extends LightningElement {
    data = data;
    columns = columns;

    getSelectedName(event) {
        //兩種方式獲取選中的行,第一種是使用 event.detail.selectedRows,另外一種是使用querySelector找到 lightning-datatable,然後使用datatable封裝的方法
        //const selectedRows = event.detail.selectedRows;
        const dataTable = this.template.querySelector('lightning-datatable');
        const selectedRows = dataTable.getSelectedRows();
        // Display that fieldName of the selected rows
        for (let i = 0; i < selectedRows.length; i++){
            console.log("You selected: " + selectedRows[i].opportunityName);
        }
    }
}

 簡單展示效果如下:

 二. datatable中展示父表中的字段值

上個demo中簡單介紹了datatable的使用,數據全是寫死的demo,接下來通過一個包含後台的邏輯進行datatable展示數據。

DataTableExampleController:用於獲取 Case表中的相關字段以及其父表的字段

public with sharing class DataTableExampleController {
    @AuraEnabled(cacheable=true)
    public static List<Case> getCaseList(){
        List<Case> caseList = [SELECT Id,CaseNumber,Account.Name,
                                    Priority,Status
                                FROM Case
                                LIMIT 100];
        return caseList;
    }
}

datatableExample2.js:用於展示相關的字段,通過wire adapter獲取後台數據放在data中

import { LightningElement, track, wire } from 'lwc';
import getCaseList from '@salesforce/apex/DataTableExampleController.getCaseList';
const COLUMNS = [
    {label: 'Case Number', fieldName: 'CaseNumber', type: 'text'},
    {label: 'Account Name', fieldName: 'Account.Name', type: 'text'},
    {label: 'Priority', fieldName: 'Priority', type: 'text'},
    {label: 'Status', fieldName: 'Status', type: 'text'}
];
export default class DataTableExample2 extends LightningElement {
    columns = COLUMNS;
    @track datas;

    @wire(getCaseList)
    wiredCaseList({ error, data }) {
        if(data) {
            this.datas = data;
        } else if(error) {
            //TODO
            console.log(JSON.stringify(error));
        }
    }
}

dataTableExample.html:展示datatable

<template>
    <lightning-datatable
            data={datas}
            columns={columns}
            key-field="id"
            >
    </lightning-datatable>
</template>

結果展示:通過下圖我們可以看到,其他的字段可以正常展示,但是父表的數據獲取確實失敗的。

 來剖析一下原因,datatable針對columns的fieldName只支持一級結構的獲取,針對獲取父的方式沒法通過 各種點的方式獲取到,那麼如何獲取呢,查看下面的方式,我們對js的代碼進行一下改動。按照下面的步驟兩步走的修改。

 結果展示:我們可以看到 Account Name列的值也順利的展示出來了,按照這種操作做一個拓展,其實我們也可以去做一些字段間的邏輯操作拼出一個符合我們邏輯的列,不僅僅局限於獲取父的對象值這種簡單操作。

 三. 實現datatable展示 formula類型是圖片的功能

自己看文檔的小夥伴可能發現datatable的columns的type沒有類似 image類型,但是在object field創建時我們很容易創建一些IMAGE的字段,那樣的話使用標準的datatable便無法展示,那要如何操作呢?還好lwc提供了一個自定義類型的操作。首先我們在Case表裡面新增一個formula 字段,展示圖片。

 接下來一步接着一步搞定。

dataTableWithImage.js:這裏需要繼承 LightningDatatable,並且引入 imageRow.html,其用於做template

import LightningDatatable from 'lightning/datatable';
import imageRow from './imageRow.html';
export default class DataTableWithImage extends LightningDatatable {
    static customTypes = {
        image: {
            template: imageRow
        }
    };
}

imageRow.html:默認要求 imageRow需要和 dataTableWithImage在同一個目錄下,如果不在一個目錄,需要改上面的文件目錄信息。

<template>
    <c-data-table-image-template
        url={value}
        >
    </c-data-table-image-template>
</template>

dataTableImageTemplate.html:用來通過 img標籤展示圖片

<template>
    <img src={url} class="image"/>
</template>

dataTableImageTemplate.css:用來設置圖片大小的初始值,這種是一種偷懶的方式,正常應該通過 attribute注入進來指定大小的寬和高。

.image {
  height: 30px;
  width: 30px;
}

dataTableImageTemplate.js

import { LightningElement,api } from 'lwc';

export default class DataTableImageTemplate extends LightningElement {
    @api url;
}

OK,通過上面的這些步驟我們實現和繼承了自定義的datatable組件。接下來是調用操作。我們在class裏面搜索一下Priority_Image__c這個字段,apexClass這裏不再寫。

dataTableExample3.js:這裡有幾個關鍵點。

  • 設置 type為 image類型,image類型是我們上文自定義創建的
  • 對 formula類型進行解析,找到他的src部分。比如針對<img src=”/resource/priority/priority/medium.png” alt=”Priority Flag” border=”0″/>這個字符串,我們需要的是/resource/priority/priority/medium.png這個字符串
import { LightningElement, track, wire } from 'lwc';
import getCaseList from '@salesforce/apex/DataTableExampleController.getCaseList';

const COLUMNS = [
    {label: 'Case Number', fieldName: 'CaseNumber', type: 'text'},
    {label: 'Account Name', fieldName: 'AccountName', type: 'text'},
    {label: 'Priority', fieldName: 'Priority', type: 'text'},
    { label: 'Priority Image',fieldName:'Priority_Image__c',type: 'image' },
    {label: 'Status', fieldName: 'Status', type: 'text'}
];
export default class DataTableExample3 extends LightningElement {
    columns = COLUMNS;
    @track datas;

    @wire(getCaseList)
    wiredCaseList({ error, data }) {
        if(data) {
            //this.datas = data;
            let resultList = [];
            let objectTemp;
            data.forEach(item => {
                objectTemp = Object.assign({},item);
                if(item.Account) {
                    objectTemp.AccountName = item.Account.Name;
                }

                const srcIndex = objectTemp.Priority_Image__c.indexOf('src=');
                if(srcIndex !== -1) {
                    const imgSrcSubstring = objectTemp.Priority_Image__c.substring(srcIndex + 5);
                    objectTemp.Priority_Image__c = imgSrcSubstring.substring(0,imgSrcSubstring.indexOf('"'))
                }
                console.log(JSON.stringify(objectTemp));
                resultList.push(objectTemp);
            });
            this.datas = resultList;
        } else if(error) {
            //TODO
            console.log(JSON.stringify(error));
        }
    }
}

dataTableExample3.html

<template>
    <c-data-table-with-image
                    key-field="id"
                    data={datas}
                    columns={columns}
                    >
                </c-data-table-with-image>
</template>

結果展示:針對不同的Priority展示其對應的formula的圖片。

總結:篇中講了datatable的簡單的應用以及相應的擴展,datatable很強悍,這裏不逐點講述,感興趣的可以自行查看 inline edit,action等操作。篇中有錯誤地方歡迎指出,有不懂的歡迎留言。

後記:工作原因這篇博客準備了好久才成型,不知不覺學了半年的lwc的開發,也整理總結了一些博客供自己和他人學習。前半年的主旋律大部分時間都在學習lwc,後半年的時間大部分會放在考證和學習其他技術,不出意外應該是今年的最後一篇lwc的博文。有其他lwc的問題歡迎留言探討,希望各位salesforce開發者可以養成獨立思考的能力,先思考,在查,最後再問。加油!

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

※別再煩惱如何寫文案,掌握八大原則!

Spring Cloud Alibaba系列(五)sentinel實現服務限流降級

一、sentinel是什麼

sentinel的官方名稱叫分佈式系統的流量防衛兵。Sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。在Spring Cloud項目中最開始我們使用的是Hystrix,目前已停止更新了。現在Spring Cloud官方推薦的是rensilience4j。當然還有我們今天學習的sentinel。

Sentinel 具有以下特徵:

  • 豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、消息削峰填谷、集群流量控制、實時熔斷下游不可用應用等。
  • 完備的實時監控:Sentinel 同時提供實時的監控功能。您可以在控制台中看到接入應用的單台機器秒級數據,甚至 500 台以下規模的集群的匯總運 行情況。
  • 廣泛的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。
  • 完善的 SPI 擴展點:Sentinel 提供簡單易用、完善的 SPI 擴展接口。您可以通過實現擴展接口來快速地定製邏輯。例如定製規則管理、適配動態數據源等。

二、sentinel實現限流

2.1 安裝sentinel控制台

  • 下載地址:https://github.com/alibaba/Sentinel/releases

這裏我們直接下載jar包即可,下載后通過命令行啟動:

java -jar sentinel-dashboard-1.7.2.jar
  • 默認端口:8080
  • 默認用戶名:sentinel
  • 默認密碼:sentinel

啟動成功后,我們瀏覽器訪問http://localhost:8080,出現如下界面。

2.2 微服務繼承sentinel

  • 引入sentinel依賴
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  • 添加sentinel的相關配置
server:
  port: 7003
spring:
  application:
    name: sentinel-provider
  cloud:
	nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
  • 提供個接口用來測試限流
@SpringBootApplication
public class SentinelApplication {

    public static void main(String[] args) {
        SpringApplication.run(SentinelApplication.class, args);
    }
}

@RestController
class TestController{
    @GetMapping("/test")
    public String test(){
        return "hello! sentinel!";
    }
}

我們請求幾次這個接口后,打開sentinel控制台,就可以實時監控到這個sentinel-provider服務接口調用情況了。

2.3 配置限流規則

我們這裏做一個簡單的規則配置:

  • 閥值類型:QPS

  • 單機閥值:2

意思就是:該接口每秒最多允許進入兩個請求。

點擊新增后,在流控規則里發現了一條規則:

現在,我們繼續請求3次這個接口。第三次響應的內容如下:

Blocked by Sentinel (flow limiting)

我們打開控制台發現拒絕了一條請求。

三、Sentinel規則介紹

不管是限流還是降級,它都是按照某種規則進行的,下面具體介紹一下sentinel支持的幾種規則。

3.1 流控規則

流量控制,其原理是監控應用流量的QPS(每秒查詢率) 或併發線程數等指標,當達到指定的閾值時

對流量進行控制,以避免被瞬時的流量高峰衝垮,從而保障應用的高可用性。

資源名:唯一名稱,默認是請求路徑,可自定義

針對來源:指定對哪個微服務進行限流,默認指default,意思是不區分來源,全部限制

閾值類型/單機閾值

  • QPS(每秒請求數量): 當調用該接口的QPS達到閾值的時候,進行限流

  • 線程數:當調用該接口的線程數達到閾值的時候,進行限流

3.2 降級規則

降級規則就是當滿足什麼條件時,對服務降級——即將請求轉發到另外接口上,這個接口與業務無關,只是為了保證系統的完整性。

  • RT(平均響應時間) :當資源的平均響應時間超過閾值(以 ms 為單位)之後,資源進入准降級狀態。如果接下來 1s 內持續進入 5 個請求,它們的 RT都持續超過這個閾值,那麼在接下的時間窗口(以 s 為單位)之內,就會對這個方法進行服務降級。

    注意 Sentinel 默認統計的 RT 上限是 4900 ms,超出此閾值的都會算作 4900 ms,若需要變更此上限可以通過啟動配置項 -Dcsp.sentinel.statistic.max.rt=xxx 來配置。

  • 異常比例:當資源的每秒異常總數占通過量的比值超過閾值之後,資源進入降級狀態,即在接下的時間窗口(以 s 為單位)之內,對這個方法的調用都會自動地返回。異常比率的閾值範圍是 [0.0,1.0]。

  • 異常數 :當資源近 1 分鐘的異常數目超過閾值之後會進行服務降級。注意由於統計時間窗口是分鐘級別的,若時間窗口小於 60s,則結束熔斷狀態后仍可能再進入熔斷狀態。

3.3 熱點規則

熱點規則允許將規則具體到參數上。

我們用個例子來看看效果。

  • 編寫接口
@GetMapping("/myTest")
@SentinelResource("test3")
public String test123(String name,String age){
    return  name + "----"+ age;
}
  • 添加規則
  • 運行效果

結果显示,第一個參數被限流了,而第二個參數正常。

3.4 系統規則

系統保護規則是從應用級別的入口流量進行控制,從單台機器的總體 Load、RT、入口 QPS 、CPU使用率和線程數五個維度監控應用數據,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。

系統保護規則是應用整體維度的,而不是資源維度的,並且僅對入口流量 (進入應用的流量) 生效。

  • Load(僅對 Linux/Unix-like 機器生效):當系統 load1 超過閾值,且系統當前的併發線程數超過系統容量時才會觸發系統保護。系統容量由系統的 maxQps * minRt 計算得出。設定參考值一般是 CPU cores * 2.5。

  • RT:當單台機器上所有入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。

  • 線程數:當單台機器上所有入口流量的併發線程數達到閾值即觸發系統保護。

  • 入口 QPS:當單台機器上所有入口流量的 QPS 達到閾值即觸發系統保護。

  • CPU使用率:當單台機器上所有入口流量的 CPU使用率達到閾值即觸發系統保護。

3.5 授權規則

很多時候,我們需要根據調用來源來判斷該次請求是否允許放行,這時候可以使用 Sentinel 的來源問控制的功能。來源訪問控制根據資源的請求來源(origin)限制資源是否通過:

  • 若配置白名單,則只有請求來源位於白名單內時才可通過;

  • 若配置黑名單,則請求來源位於黑名單時不通過,其餘的請求通過。

流控應用:sentinel提供了RequestOriginParser來處理接口來源。

我們運行abc來源的請求訪問/test接口。

@Component
class requestOrigin implements RequestOriginParser{

    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        String server = httpServletRequest.getParameter("server");
        return server;
    }
}

我們請求http://localhost:7003/test?server=abc 和 http://localhost:7003/test?server=ab來分別看看效果。

@SentinelResource的使用

@SentinelResource 用於定義資源,並提供可選的異常處理和 fallback 配置項。

主要參數有以下幾個

屬性 作用
value 資源名稱
entryType entry類型,標記流量的方向,取值IN/OUT,默認是OUT
blockHandler 處理BlockException的函數名稱,函數要求:1. 必須是 public;2.返回類型 參數與原方法一致;3. 默認需和原方法在同一個類中。若希望使用其他類的函數,可配置blockHandlerClass ,並指定blockHandlerClass裏面的方法。
blockHandlerClass 存放blockHandler的類,對應的處理函數必須static修飾。
fallback 1. 返回類型與原方法一致;2. 參數類型需要和原方法相匹配;3. 默認需和原方法在同一個類中。若希望使用其他類的函數,可配置fallbackClass
fallbackClass 存放fallback的類。對應的處理函數必須static修飾。
defaultFallback 若同時配置了 fallback 和 defaultFallback,以fallback為準。
exceptionsToIgnore 指定排除掉哪些異常。排除的異常不會計入異常統計,也不會進入fallback邏輯,而是原樣拋出。
exceptionsToTrace 需要trace的異常

@sentinelResource可結合blockHandler用於限流處理,結合fallback用於降級處理。具體規則可通過sentinel控制台配置,具體我就不演示了,在下一章內容中,我會分別演示限流和降級的應用。

public class MySentinelResource {

    @SentinelResource(value="message",blockHandler="blockHandler",fallback="fallback")
    public String message(String str){
        if(StringUtils.isBlank(str)){
            throw new RuntimeException();
        }
        return str;
    }
    /**
     * 限流處理
     * @param str
     * @param ex
     * @return
     */
    public String blockHandler(String str, BlockedException ex){
        return str + "--"+ ex;
    }
    /**
     * 降級處理
     * @param str
     * @return
     */
    public String fallback(String str){
        return null;
    }
}

代碼示例

gitee:https://gitee.com/zhixie/spring-cloud-alibaba-learning/tree/master/sentinel-server

github:https://github.com/binzh303/spring-cloud-alibaba-learning/tree/master/sentinel-server

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

【其他文章推薦】

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

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

特斯拉:將調整以符合中國充電標準

美國電動車大廠特斯拉(Tesla)表示,在中國發布全國電動車充電標準後,該公司將修改新生產的車輛,以便用戶可在符合新標準的充電設施,享受充電服務。    特斯拉在中文網站發布公告:「特斯拉將全面支持新國標,修改新生產車輛以符合新國標的規定… 隨著未來幾年中國充電基礎設施的飛速發展,特斯拉用戶將徹底消除對於充電的顧慮。」    特斯拉的充電規格和中國不相容,是消費者不敢購買電動車的一大阻力。不過,中國當局對此尚未提出明確的時間表,電動車充電的新標準何時出爐仍不得而知。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

※別再煩惱如何寫文案,掌握八大原則!

深圳電動車熱:東部公交今年將投放565輛純電動車

5月13日,深圳市坪山新區召開新區公交業務座談會,東部公交公司計畫在2015年投放565輛純電動車輛,並將於近期採取公開招標的方式選定車輛更新解決方案。   東部公交公司在坪山新區內運營的公交線路有61條,運力1043台。坪山新區的500米公交網站覆蓋率為92%,公交線路基本覆蓋至坪山新區每個社區。   而目前,東部公交公司在坪山新區運營的新能源線路共16條,運力309輛,占東部公交新能源總運力的35%,其中純電動大巴150輛,混合動力大巴159輛。   2014年東部公交公司在坪山新區投放了134輛純電動大巴。根據坪山新區內運營的線路現狀,東部公交將繼續加大在坪山新區投放新能源車輛的力度,計畫2015年內在坪山新區投放70輛新能源車輛。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

氣候變遷衝擊 2050年前全球經濟損失恐達241兆元

摘錄自2019年11月20日中央通訊社綜合報導

最新分析報告顯示,隨著越來越多的旱澇災情和農作物歉收,危及經濟成長,威脅基礎建設,氣候變遷恐直接造成2050年前全球經濟損失7.9兆美元(約新台幣241兆元)。

英國經濟學人資訊社(EIU)今天(20日)公布氣候變遷韌性指數(Climate Change Resilience Index),發現以當前趨勢來看,氣溫暖化帶來的衝擊在2050年以前,將讓全球經濟成長下滑3%。這項指數用來衡量全球82大經濟體的準備程度。

氣候變遷引發的極端氣候事件頻傳,這項報告評估各國暴露在氣候變遷帶來的損失,結果發現非洲地區風險最高,區域國內生產毛額(GDP)恐因此萎縮4.7%。

世界最大經濟體美國被認為是受到衝擊程度最小的國家之一。但經濟學人資訊社提到,美國總統川普的政策代表在對抗氣候變遷戰爭中「短暫地倒退」。

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

※別再煩惱如何寫文案,掌握八大原則!