當前位置:
首頁 > 最新 > 來自後端的突襲?-開包即食的教程帶你淺嘗最新開源的C#Web引擎 Blazor

來自後端的突襲?-開包即食的教程帶你淺嘗最新開源的C#Web引擎 Blazor

在今年年初, 恰逢新春佳節臨近的時候. 微軟給全球的C#開發者們, 著實的送上了一分驚喜.微軟正式開源Blazor ,將.NET帶回到瀏覽器.

這個小驚喜, 迅速的在dotnet開發者中間傳開了. 而就在昨天(2018年3月22日) Blazor發布了它的第一次Release. Blazor到底是個什麼樣的東西呢?我們是否真的可以攜著C#語言進入前端的市場中? 不如現在就跟我一起體驗dotnet blazor吧.


首先

獲取最新版的dotnet core 並安裝Blazor模板:

安裝 最新的.Net Core(版本需要高於2.1.101)

對於簡單的嘗試來說, VS code 已經足夠. 所以筆者並沒有親自安裝Visual Studio.

使用命令行初始化項目:

dotnetnew-iMicrosoft.AspNetCore.Blazor.Templatesdotnetnewblazor-oBlazorApp1cdBlazorApp1dotnetrun

如果你需要使用Visual Studio,

安裝最新的Visual Studio 2017.

安裝 ASP.NET Core Blazor Language Services extension

在Visual Studio中創建新的測試項目:

選擇 File -> New Project -> Web -> ASP.NET Core Web Application

確定在Target Framework里選擇了 .NET Core and ASP.NET Core 2.0.

選擇 Blazor 模板


敵後根據地? 如何在前端渲染cshtml

當我們運行起項目之後, 就可以看到如下提示

個時候我們在瀏覽器里打開監聽的埠 http://localhost:17477. 就可以看到我們這個項目的網頁了.

這個簡單的示例項目帶了3個頁面

第一個頁面比較簡單, 但先別急,讓我們打開瀏覽器工具. 先看看頁面在載入頁面過程中都載入了什麼

在初次打開頁面的時候, 我們看到的是這樣一個Loading..的頁面. 這個頁面的代碼是這樣的.

BlazorDemo

Loading...

blazor.js 載入了mono.js, mono.js 載入了mono.wasm. 這個是個什麼文件?

asm代表的就是Web Assembly, 簡單地說它就是編譯好的二進位文件, 可以由瀏覽器直接運行, 源語言可以是C/C++或者任何可以編譯到Web Assembly的文件, 而這裡我們載入的就是mono 編譯好的Web Assembly文件, 它被載入之後, 相當於瀏覽器中啟動了一個mono 運行環境.

好傢夥 1.9MB. 當所有的Dll被下載完畢之後, 這個時候我們的瀏覽器就可以運行我們這個dotnet的網頁了. 於是就回到了我們最開始看到的那個應用程序.


柳暗花明又一村,Blazor的模板究竟是怎樣的.

我們已經知道,經過前面的步驟,瀏覽器里已經運行了一個.Net 運行時了. 而且載入了項目必須的dll. 那麼這樣一個簡單的程序,它的代碼究竟是怎麼樣的呢?

打開項目代碼,映入眼帘的是一個標準的.net Project

_ViewImports.cshtml包含了項目一些其他頁面中最常使用的namespace

Program.cs是程序的入口點

using Microsoft.AspNetCore.Blazor.Browser.Rendering;

using Microsoft.AspNetCore.Blazor.Browser.Services;

using System;

namespace BlazorDemo

{

class Program

{

static void Main(string[] args)

{

var serviceProvider = new BrowserServiceProvider(configure =>

{

// Add any custom services here

});

new BrowserRenderer(serviceProvider).AddComponent("app");

}

}

}

在入口點中, 我們註冊了一個瀏覽器渲染服務 BrowserRender,讓他渲染App

App.cshmtl是這樣的

這裡的Router對應的是Microsoft.AspNetCore.Blazor.Routing.Router. 當給它一個AppAssembly時, 他就會自動的把當前的Url 和 AppAssembly的其他Pages對應起來.

所以 當我們在瀏覽器里輸入 /Counter時,他就會載入Pages/Couter.cshtml.

Shared文件夾里分別是布局文件,導航欄, 還有一個我們自定義的控制項 SurveyPrompt.

熟悉Razor引擎的小夥伴們一定很輕車熟路了. 那麼當我們打開網站時, 默認顯示給我們的 就是Index, 這個時候我們會載入Pages/Index.cshtml

Index.cshtml的代碼是這個樣子的

@page"/"


Hello, world!

Welcome to yournewapp.

@page 可以告訴Router, 當前頁面註冊到 "/"

除了顯示hello world以外, 我們在這裡還看到了剛剛說到的第三方控制項. SurveyPrompt. 果然不簡單嘛, 一個看似簡單的頁面, 居然還告訴了我們如何使用自定義控制項.

從聲明上看, 我們知道 SunveyPrompt是一個控制項,並且有一個屬性Title. 現在我們打開它的代碼

@Title

Please take our

brief survey

and tell us what you think.

@functions

{

// This is to demonstrate how a parent component can supply parameters

public string Title { get; set; }

}

我們可以看到代碼分為兩部分, @functions上面是類似html的東西, 下面是類似C#的東西. 熟悉React或者Vue的夥伴們恐怕不會對這種混寫感到陌生. 這個就是Blazor的語法. Html部分很像使Razor的模板方式. 而最後整個頁面都會被編譯成一個類, 這個類派生自 Component. 如果你編譯過項目, 你會在Debug下面的Shared目錄找到一個叫SurveyPrompt.g.cs的東西

#pragma checksum "/Users/pzhi/SCM/gitHub/zhipu123/BlazorDemo/Shared/SurveyPrompt.cshtml" "" "a2a2ea88635b799343bc6d9647bbb818c8a20c9d"

//

#pragma warning disable 1591

namespace BlazorDemo.Shared

{

#line hidden

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using System.Net.Http;

using Microsoft.AspNetCore.Blazor;

using Microsoft.AspNetCore.Blazor.Components;

using Microsoft.AspNetCore.Blazor.Layouts;

using Microsoft.AspNetCore.Blazor.Routing;

using BlazorDemo;

using BlazorDemo.Shared;

public class SurveyPrompt : Microsoft.AspNetCore.Blazor.Components.BlazorComponent

{

#pragma warning disable 1998

protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)

{

base.BuildRenderTree(builder);

builder.OpenElement(0, "div");

builder.AddAttribute(1, "class", "alert alert-survey");

builder.AddAttribute(2, "role", "alert");

builder.AddContent(3, "
");

builder.OpenElement(4, "span");

builder.AddAttribute(5, "class", "glyphicon glyphicon-ok-circle");

builder.AddAttribute(6, "aria-hidden", "true");

builder.CloseElement();

builder.AddContent(7, "
");

builder.OpenElement(8, "strong");

builder.AddContent(9, Title);

builder.CloseElement();

builder.AddContent(10, "

Please take our
");

builder.OpenElement(11, "a");

builder.AddAttribute(12, "target", "_blank");

builder.AddAttribute(13, "class", "alert-link");

builder.AddAttribute(14, "href", "https://go.microsoft.com/fwlink/?linkid=870381");

builder.AddContent(15, "
brief survey
");

builder.CloseElement();

builder.AddContent(16, "
and tell us what you think.
");

builder.CloseElement();

builder.AddContent(17, "

");

}

#pragma warning restore 1998

// This is to demonstrate how a parent component can supply parameters

public string Title { get; set; }

}

}

#pragma warning restore 1591

我們發現@functions裡面的內容 會作為這個類的成員變數和 成員方法, 而上面的內容則被編譯到了BuildRenderTree方法中.

那麼到了這裡我們大概知道了這個簡單的HomePage都有什麼玄機了. 我們也大概知道了Blazor的語法, 也知道其實我們所有的頁面最終都會是一個Componet.

那麼什麼是Componet呢? 在這裡並不想過多的去筆墨介紹這個概念. 如果你是一個Vue或者React的開發, 你應該對這個模塊化開發不陌生. 一個Componet, 就是滿足一定的功能, 有自己的屬性, 狀態. 可以展示特定數據的元素.

就如同我們這裡的SurveyPrompt, 接受一個Title屬性,並且負責把他展示成這樣子


數據驅動? Blazor的刷新和綁定機制初探

現在我們知道了一個簡單的頁面是如何渲染出來的. 那麼讓我們打開Counter這個配置來看一看. 數據是如何交互的

我們第二個page張這樣子

有一個button, 大聲的叫我們點它. 當我們點擊的時候. 上面的current count 變成了 1

這一切是怎麼發生的呢? 以下是Counter.cshtml的代碼

@page "/counter"


Counter

Current count: @currentCount

Click me

@functions {

int currentCount = 0;

void IncrementCount()

{

currentCount++;

}

}

我們看到 這個頁面非常簡單, 我們定義了一個CurrentCount的Field, 然後在IncreaseCount方法里給它加一. 一個叫Click me的button標籤里 有一個@onclick方法, 將IncreaseCount作為參數

Counter.cshtml編譯後的代碼張這樣

#pragma checksum "/Users/pzhi/SCM/gitHub/zhipu123/BlazorDemo/Pages/Counter.cshtml" "" "05ad2dd449cbc9f09f8b759e1f06e7eb5e9583b4"

//

#pragma warning disable 1591

namespace BlazorDemo.Pages

{

#line hidden

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using System.Net.Http;

using Microsoft.AspNetCore.Blazor;

using Microsoft.AspNetCore.Blazor.Components;

using Microsoft.AspNetCore.Blazor.Layouts;

using Microsoft.AspNetCore.Blazor.Routing;

using BlazorDemo;

using BlazorDemo.Shared;

[Microsoft.AspNetCore.Blazor.Layouts.LayoutAttribute(typeof(MainLayout))]

[Microsoft.AspNetCore.Blazor.Components.RouteAttribute("/counter")]

public class Counter : Microsoft.AspNetCore.Blazor.Components.BlazorComponent

{

#pragma warning disable 1998

protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)

{

base.BuildRenderTree(builder);

builder.OpenElement(0, "h1");

builder.AddContent(1, "Counter");

builder.CloseElement();

builder.AddContent(2, "

");

builder.OpenElement(3, "p");

builder.AddContent(4, "Current count: ");

builder.AddContent(5, currentCount);

builder.CloseElement();

builder.AddContent(6, "

");

builder.OpenElement(7, "button");

builder.AddAttribute(8, onclick(IncrementCount));

builder.AddContent(9, "Click me");

builder.CloseElement();

builder.AddContent(10, "

");

}

#pragma warning restore 1998

int currentCount = 0;

void IncrementCount()

{

currentCount++;

}

}

}

#pragma warning restore 1591

我們看到 @onclick其實在這裡就是執行了一個Component的一個方法onclick, 顧名思義,當這個Component被點擊的時候就被調用. 我們的IncreaseCount被作為參數傳給了它, 可見onclick會在被點擊的時候執行IncreaseCount.

那麼問題來了,當我們執行了IncreaseCount方法時, 頁面怎麼會知道要不要刷新? 是刷新整個頁面還是刷新所有?

熟悉WPF的同學可能知道, 在WPF中如果我們需要讓一個ViewModel可以被監聽變化, 他就需要實現INotifyChanged事件. 那麼同樣道理, 我們的這個IncreaseCount可能也是類似的嗎?

然而基於編譯後的代碼我們可以發現 CurrentCount作為我們Counter這個類的Field, 並沒有任何機會高速Page自己變化了. 而且這個Field非常普通,也不是什麼WPF中的DP, 所以到目前為止變化是怎麼通知的.並沒有一個合理的解釋. 後面的時間裡我會嘗試閱讀Blazor的代碼搞清楚這件事情.

第一個問題畫個問號, 那麼第二個問題呢?

打開瀏覽器工具, 定位到button, 再次點擊button觀察dom的反應.

我們看到 在點擊Button的時候, button上面的

標籤閃動了, 說明它被刷新了, 而其他標籤並沒有. 所以局部刷新的功能是有的. 效率問題不用擔心了.

編輯Click me, 把他的內容變成 "點擊我", 再次點擊按鈕, 我們看到還是只有p變, 而且button也沒有變回原來的內容

所以我們知道, 這個局部刷新不是簡單的拿Dom作比較, 肯定是有Virtual Dom的機制在裡面.


星星之火,可燎原?

在簡單的嘗試了Blazor之後, 還是很興奮的. 可以看到Blazor是一個初具規模的產品. 我們C#開發可以用Blazor在今後寫前端渲染的網頁了!

我很期望這樣一個產品能夠持續的演進下去.

就目前版本看(0.1.0), Blazor尚不能應用到產品中. 主要還是有以下的原因

打包大小太大, 1.8M的大小對於網站簡直是致命的.

產品還不成熟, 現在Component還只能支持簡單的事件, 筆者測試的時候只有onclick,onchange.

兼容性差,使用了WebAssembly,就註定了兩年前的瀏覽器必定不能支持.

當然我們還是不能否認, Blazor為如何讓更多語言進入前端世界打開了一扇新的大門. 也許未來JavaScript將不僅僅是前端唯一可以使用的利器. 我們會看到C/C++, Python, Java寫的前端渲染頁面也不一定呢.

當然在後端語言打入前端世界的道路上, WebAssembly也未必是唯一的路勁, 比如Scala.js就完全使用了js重寫了Scala的庫函數, 類似的還有Kotlin.js. 可以看到雖然JavaScript已經非常Fancy了,但是後端程序員們進軍前端的熱情可謂從未停歇過啊.

祝dotnet的應用越來越廣, 祝廣大後端程序員們新年成就慢慢, 加薪升職.

原文: https://www.cnblogs.com/Gerryz/p/get-start-with-dotnet-blazor.html


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 dotNET跨平台 的精彩文章:

老衣的微服務實踐簡要指引2017版
從技術角度討論微服務

TAG:dotNET跨平台 |