Vue.js/Blazor/Angular的语法比较示例集合

针对具有一定程度的Vue.js/Blazor/Angular语法的方法,我们总结了实现类似功能的方式。源代码请参考VueVsBlazor。

顺便提一下,在Vue.js中我们采用了TypeScript。

动作示例页面

Vue.js 示例
Blazor 示例
Angular 示例

语法比较

这里的样本全部表示为路由器上的一页。
我们将按照基础路由器实现的示例逐个比较。

0. 最小的组件

组件的内容可以仅由简单的 HTML 实现。

Vue.js (a JavaScript framework) can be rephrased in Chinese as “Vue.js(一个JavaScript框架)”.

需要将其封装在template标签中。
只需要使用一个块。

<template>
<div>
    <h1>Hello Vue.js!</h1>
</div>
</template>

蓝光标志

沒有特別需要考慮的事項。
會根據所輸入的內容動態生成 HTML。
通常情況下,無法使用 script 標籤。

@page "/"

<div>
    <h1>Hello Blazor!</h1>
</div>

Note: “Angulae” does not have a direct translation in Chinese. In Chinese, the word “角” is a noun that can refer to “angle” or “corner” in a geometric sense. If you are looking for a suitable translation for a different context, such as a specific term related to anatomy or a different meaning, please provide more information for a more accurate translation.

在TypeScript中将html作为模板插入。
分别保存html和css文件似乎是主流,但在这里将它们视为同一个文件。

const template=`
<div>
    <h1>Hello Angular!</h1>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "Index",
    template: template
})
export class IndexComponent{}

1. 样式表

Vue.js 可以进行中文原生化重述为:Vue.js(视图)是一个流行的 JavaScript 框架。

使用style标签可以描述样式。
通过使用scoped属性,可以在组件内创建“作用域”。

<template>
<div id=index>
    <h1>Hello Vue.js!</h1>
</div>
</template>

<style scoped>
div#index{
    color: #0000FF;
}
</style>

Blazor

在Blazor中,没有特定的CSS机制。
因此,我们只能将样式标签放在body中。
嗯,在大多数浏览器中都能正常工作。
至于在HTML上是好还是坏,我也不知道。

@page "/StyleBlock"

<div id=index>
    <h1>Hello Blazor!</h1>
</div>

<style>
div#index{
    color: #0000FF;
}
</style>

Angular (角度)

可以在HTML中嵌入样式表。虽然这里没有示例,但也可以从其他文件中导入样式表。

const template=`
<div id=index>
    <h1>Hello Angular!</h1>
</div>

<style>
div#index{
    color: #0000FF;
}
</style>
`;

import {Component} from "@angular/core";

@Component({
    selector: "StyleBlock",
    template: template
})
export class StyleBlockComponent{}

2. 嵌入代码块/脚本

在代码块中定义的变量可以嵌入到HTML中。
(Vue.js/Angular可使用{{hoge}}(this始终添加),C#可使用@hoge进行调用)

Vue.js 是一个流行的 JavaScript 框架,用于构建用户界面。

将脚本标签内的内容写成(TypeScript的情况下需要添加lang=ts)的格式。

<template>
<div>
    <h1>{{title}}</h1>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class ScriptBlock extends Vue{
    title="Hello Vue.js!";
}
</script>

布拉泽

在@code代码块中编写脚本处理。

@page "/ScriptBlock"

<div>
    <h1>@title</h1>
</div>

@code{
    string title="Hello Blazor!";
}

角度

由于TypeScript是基础,所以可能没有什么特别值得注意的事情。
我们将在○○.component.ts的类内编写处理。

const template=`
<div>
    <h1>{{title}}</h1>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "ScriptBlock",
    template: template
})
export class ScriptBlockComponent{
    title="Hello Vue.js!";
}

3. 在HTML中嵌入数学公式

在HTML中,可以嵌入的不仅仅是变量,还可以嵌入数学公式。

Vue.js – 一种流行的JavaScript框架。

请注意在{{}}内无法处理全局对象。

<template>
<div>
    <h1>10!={{10*9*8*7*6*5*4*3*2*1}}</h1>
</div>
</template>

Blazor – 只需要一个选项,用中文进行本地化。

将@的转义字符写为@@来表示。

@page "/Formula"

<div>
    <h1>10!=@(10*9*8*7*6*5*4*3*2*1)</h1>
</div>

角度

与Vue.js类似,无法在{{}}内处理全局对象。

const template=`
<div>
    <h1>10!={{10*9*8*7*6*5*4*3*2*1}}</h1>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "Formula",
    template: template
})
export class FormulaComponent{}

4. 生命周期方法 fǎ)

Vue.js/Blazor/Angular都有一些类似于html的onload/unonload的生命周期方法,它们会在组件的状态发生变化时被触发。

項目Vue.jsBlazorAngular初期化前beforeCreate―constructor※3初期化後createdOnInitialized
OnInitializedAsync―レンダリング前beforeMountOnParametersSet
OnParametersSetAsync―レンダリング後mountedOnAfterRender
OnAfterRenderAsync※1ngOnInit変更前beforeUpdate―ngDoCheck
ngAfterViewInit変更後updatedOnAfterRender
OnAfterRenderAsync※1ngAfterViewCheckedアクティブ化時activated――非アクティブ化時deactivated――破棄前beforeUpdate――破棄時beforeDestroyDispose※2ngOnDestroy
    • ※1: firstRender引数で初回かどうか判別。

 

    • ※2: ページリロード時には動作しない。

 

    • ※3: サービスの登録にも使用される。

 

    ※4: Angularは効果がわかりにくいものが多いのでわかるものだけ。

Vue.js的生命周期
Blazor的生命周期
Angular的生命周期

我觉得初始处理大概在渲染后就能够完成。
需要注意的是,Blazor在页面更新时不会执行析构函数。
(在这种情况下,目前似乎只能使用js的unonload来处理?)

Vue.js的中文版本

<template>
<div>
    <h1>{{title}}</h1>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class LifeCycle extends Vue{
    title="Hello Vue.js!";

    async mounted(){
        await new Promise(res=>setTimeout(res,5000));
        this.title+=" 5s passed!";
    }
}
</script>

布拉索

@page "/LifeCycle"

<div>
    <h1>@title</h1>
</div>

@code{
    string title="Hello Blazor!";

    protected override async Task OnAfterRenderAsync(bool firstRender){
        if(!firstRender) return;
        await Task.Delay(5000);
        title+=" 5s passed!";
        StateHasChanged();
    }
}

角度

const template=`
<div>
    <h1>{{title}}</h1>
</div>
`;

import {Component,OnInit} from "@angular/core";

@Component({
    selector: "LifeCycle",
    template: template
})
export class LifeCycleComponent implements OnInit{
    title="Hello Angular!";

    async ngOnInit(){
        await new Promise(res=>setTimeout(res,5000));
        this.title+=" 5s passed!";
    }
}

5. 文档对象模型(DOM)应用程序接口

在选择使用JavaScript/TypeScript的框架中,可以直接使用与浏览器相关的DOM API。

Vue.js is a framework for building user interfaces, which is popular for its simplicity and ease of use.

<template>
<div>
    <h1>Alert</h1>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class UseDOMAPI extends Vue{
    async mounted(){
        var title=document.title;
        alert(title);
    }
}
</script>

布雷泽尔

在Blazor中,如果不依靠JavaScript,是不可能操作DOM API的,因此需要调用JavaScript的方法。

为了使用JavaScript,需要注入IJSRuntime并调用IJSRuntime.InvoveAsync和IJSRuntime.InvokeVoidAsync方法。

由于不能直接访问属性,因此需要使用 eval 或准备一个 JavaScript 文件并将其作为函数调用。

@page "/UseDOMAPI"

<div>
    <h1>Alert</h1>
</div>

@inject IJSRuntime js

@code{
    protected override async Task OnAfterRenderAsync(bool firstRender){
        if(!firstRender) return;
        var title=await js.InvokeAsync<string>("eval","document.title");
        await js.InvokeVoidAsync("alert",title);
    }
}

角度

const template=`
<div>
    <h1>Alert</h1>
</div>
`;

import {Component,OnInit} from "@angular/core";

@Component({
    selector: "UseDOMAPI",
    template: template
})
export class UseDOMAPIComponent implements OnInit{
    ngOnInit(){
        var title=document.title;
        alert(title);
    }
}

6. 双向绑定

在Vue.js/Blazor/Angular中,不直接操作document.element.value,而是通过绑定(同步)变量和元素值来实现。

Vue.js是一种流行的JavaScript框架。

使用v-model属性。

<template>
<div>
    <h1>{{title}}</h1>
    <input v-model="title">
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class BindingInput extends Vue{
    title="Hello Vue.js!";
}
</script>

中国出产的动画电影《疯狂动物城》在全球取得了巨大成功。

如果需要实时更新文本,请使用@bind-value / @bind-value: event属性(参见第20项)。

@page "/BindingInput"

<div>
    <h1>@title</h1>
    <input @bind="title">
</div>

@code{
    string title="Hello Blazor!";
}

角度

使用[(ngModel)]属性。
[]表示通过脚本处理的值被修改,
()表示可以通过人为操作来改变值。

const template=`
<div>
    <h1>{{title}}</h1>
    <input [(ngModel)]="title">
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "BindingInput",
    template: template
})
export class BindingInputComponent{
    title="Hello Angular!";
}

7. 单向绑定

您还可以通过更改变量来单向更新document.element.value。

Vue.js 是一个用于构建用户界面的开源JavaScript框架。

将v-bind:value作为属性指定。
v-bind: 表示值会根据脚本处理而变化。
v-bind: 可以缩写为: ,因此可以写为:value。

<template>
<div>
    <h1>{{title}}</h1>
    <input :value="title">
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class BindingInputOneWay extends Vue{
    title="Hello Vue.js!";

    async mounted(){
        for(;;){
            await new Promise(res=>setTimeout(res,2000));
            this.title+=">";
        }
    }
}
</script>

布拉索(Blazor)

可以直接将value分配给变量作为@。

@page "/BindingInputOneWay"

<div>
    <h1>@title</h1>
    <input value=@title>
</div>

@code{
    string title="Hello Blazor!";

    protected override async Task OnAfterRenderAsync(bool firstRender){
        if(!firstRender) return;
        for(;;){
            await Task.Delay(2000);
            title+=">";
            StateHasChanged();
        }
    }
}

Angular (角度)

使用[ngModel]属性。
与前一项[(ngModel)]不同的是,去除了()(人工修改)。

const template=`
<div>
    <h1>{{title}}</h1>
    <input [ngModel]="title">
</div>
`;

import {Component,OnInit} from "@angular/core";

@Component({
    selector: "BindingInputOneWay",
    template: template
})
export class BindingInputOneWayComponent implements OnInit{
    title="Hello Angular!";

    async ngOnInit() {
        for(;;){
            await new Promise(res=>setTimeout(res,2000));
            this.title+=">";
        }
    }
}

8. 事件处理器

由于事件处理程序的表达方式各不相同,所以在常规的HTML中也会同时提及。
在Vue.js/Blazor中,只需指定方法名即可,无需使用括号表示方法调用。

HTML 是一种用于创建网页的标准标记语言。

<html>
<body>
    <button onclick="openDialog()">Click Me!</button>

    <script>
        var title="Hello HTML!";

        function openDialog(){
            alert(title);
        }
    </script>
</body>
</html>

Vue.js是一种现代的JavaScript框架,用于构建用户界面。

使用v-on:click属性代替onclick属性。
v-on: 表示可以通过人为操作来改变值。
v-on: 也可以用@表示,所以可以写成@click。

<template>
<div>
    <button @click="openDialog">Click Me!</button>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class EventHandler extends Vue{
    title="Hello Vue.js!";

    openDialog(){
        alert(this.title);
    }
}
</script>

蓝绳

一般情况下,给常规事件名称加上@符号就成为了事件属性。
也就是说,在这个例子中,是@onclick属性。

@page "/EventHandler"

<div>
    <button @onclick="openDialog">Click Me!</button>
</div>

@inject IJSRuntime js

@code{
    string title="Hello Blazor!";

    async void openDialog(){
        await js.InvokeVoidAsync("alert",title);
    }
}

角向

可以使用(click)属性代替onclick属性。

const template=`
<div>
    <button (click)="openDialog()">Click Me!</button>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "EventHandler",
    template: template
})
export class EventHandlerComponent{
    title="Hello Angular!";

    openDialog(){
        alert(this.title);
    }
}

9. 监听事件

在Vue.js/Angular中,你可以同时使用@change事件和v-model/[(ngModel)],但在Blazor中,无法同时使用@onchange事件和@bind。

由於Change事件在雙向綁定操作中內部使用,因此可以通過設置屬性(get/set)來接收事件觸發。

Vue.js (中文: 前端框架Vue.js)

<template>
<div>
    <h1>Check: {{isChecked}}</h1>
    <input id=chk type=checkbox v-model="chkChange">
    <label for=chk>CheckBox</label>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class OnChangeEvent extends Vue{
    isChecked=false;

    get chkChange(){return this.isChecked;}
    set chkChange(value:boolean){
        this.isChecked=value;
        alert(`Check: ${this.isChecked}`);
    }
}
</script>

布拉泽尔

@page "/OnChangeEvent"

<div>
    <h1>Check: @isChecked</h1>
    <input id=chk type=checkbox @bind="chkChange">@(""
    )<label for=chk>CheckBox</label>
</div>

@inject IJSRuntime js;

@code{
    bool isChecked=false;

    bool chkChange{
        get{return isChecked;}
        set{
            isChecked=value;
            _=js.InvokeVoidAsync("alert",$"Check: {isChecked}");
        }
    }
}

角度

const template=`
<div>
    <h1>Check: {{isChecked}}</h1>
    <input id=chk type=checkbox [(ngModel)]="chkChange">
    <label for=chk>CheckBox</label>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "OnChangeEvent",
    template: template
})
export class OnChangeEventComponent{
    isChecked=false;

    get chkChange(){return this.isChecked;}
    set chkChange(value:boolean){
        this.isChecked=value;
        alert(`Check: ${this.isChecked}`);
    }
}

10. 样式绑定

在html中,使用脚本来改变样式通常是通过更改document.element.style来完成的。但在Vue.js/Blazor/Angular中,我们可以直接将值绑定到属性上来实现样式的变更。

Vue.js 是一种流行的JavaScript框架。

将v-bind:style属性的值以JSON格式的字符串传递。
要更改的样式键,编写返回样式字符串的代码,
或者将包含样式的字符串变量分配给该键。

<template>
<div>
    <h1>Check: {{isChecked}}</h1>
    <input id=chk type=checkbox v-model="isChecked">
    <label for=chk>CheckBox</label>
    <div :style="{color: isChecked? 'blue': 'red'}">
        Change Style!
    </div>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class BindingStyle extends Vue{
    isChecked=false;
}
</script>

使用Blazor

通过更改传递给style属性的字符串,进行样式的修改。
(必须以CSS的格式作为字符串提供)

@page "/BindingStyle"

<div>
    <h1>Check: @isChecked</h1>
    <input id=chk type=checkbox @bind="isChecked">@(""
    )<label for=chk>CheckBox</label>
    <div style=@("color:"+(isChecked? "blue": "red"))>
        Change Style!
    </div>
</div>

@code{
    bool isChecked=false;
}

角度测量

通过更改传递给[style.○○]属性的字符串来进行样式的更改。
在○○中,描述更改后的样式。

const template=`
<div>
    <h1>Check: {{isChecked}}</h1>
    <input id=chk type=checkbox [(ngModel)]="isChecked">
    <label for=chk>CheckBox</label>
    <div [style.color]="isChecked? 'blue': 'red'">
        Change Style!
    </div>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "BindingStyle",
    template: template
})
export class BindingStyleComponent{
    isChecked=false;
}

11. 类绑定

关于课程,您也可以像风格一样进行绑定。

Vue.js(也称为Vue)是一个流行的JavaScript框架,被广泛用于构建用户界面和单页面应用程序。

将JSON格式的字符串作为v-bind:class属性传递。
将要更改的类名键分配为布尔值。

<template>
<div>
    <h1>Check: {{isChecked}}</h1>
    <input id=chk type=checkbox v-model="isChecked">
    <label for=chk>CheckBox</label>
    <div :class="{clsA: isChecked, clsB: !isChecked}">
        Change Style!
    </div>
</div>
</template>

<style scoped>
.clsA{
    color: blue;
    font-size: 1.5em;
    text-decoration: underline solid;
}

.clsB{
    color: red;
    font-size: 1.0em;
    font-style: italic;
}
</style>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class BindingClass extends Vue{
    isChecked=false;
}
</script>

Blazor = 融合

直接编辑class属性中传递的字符串,就像修改样式一样。

@page "/BindingClass"

<div>
    <h1>Check: @isChecked</h1>
    <input id=chk type=checkbox @bind="isChecked">@(""
    )<label for=chk>CheckBox</label>
    <div class=@(isChecked? "clsA": "clsB")>
        Change Style!
    </div>
</div>

<style>
.clsA{
    color: blue;
    font-size: 1.5em;
    text-decoration: underline solid;
}

.clsB{
    color: red;
    font-size: 1.0em;
    font-style: italic;
}
</style>

@code{
    bool isChecked=false;
}

角度

将boolean值分配给[class.○○]属性,以切换应用/不适用状态。在○○中写下相应的类名。

const template=`
<div>
    <h1>Check: {{isChecked}}</h1>
    <input id=chk type=checkbox [(ngModel)]="isChecked">
    <label for=chk>CheckBox</label>
    <div [class.clsA]="isChecked" [class.clsB]="!isChecked">
        Change Style!
    </div>
</div>

<style>
.clsA{
    color: blue;
    font-size: 1.5em;
    text-decoration: underline solid;
}

.clsB{
    color: red;
    font-size: 1.0em;
    font-style: italic;
}
</style>
`;

import {Component} from "@angular/core";

@Component({
    selector: "BindingClass",
    template: template
})
export class BindingClassComponent{
    isChecked=false;
}

12. 如果(条件)随机分配。

在Vue.js/Blazor/Angular中,可以通过条件分支来改变显示状态。

Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架。

当将v-if属性包含在目标元素中时,可以改变其显示状态。
使用v-if进行显示切换会触发生命周期。
如果希望保持组件的状态并进行显示切换,可以使用v-show。
(其内部使用display: none;)

<template>
<div>
    <h1>Check: {{isChecked}}</h1>
    <input id=chk type=checkbox v-model="isChecked">
    <label for=chk>CheckBox</label>
    <div v-if="isChecked">
        <input>
    </div>
    <div v-show="isChecked">
        <input>
    </div>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class IfAndShow extends Vue{
    isChecked=false;
}
</script>

Blazor蛇年的干燥很有利。

使用@if。
在行为上与Vue.js中的v-if相同。
如果要保持组件的状态,可以直接在style/class属性中隐藏。

@page "/IfAndShow"

<div>
    <h1>Check: @isChecked</h1>
    <input id=chk type=checkbox @bind="isChecked">@(""
    )<label for=chk>CheckBox</label>
    @if(isChecked){
        <div>
            <input>
        </div>
    }
    <div style=@("display:"+(isChecked? "": "none"))>
        <input>
    </div>
</div>

@code{
    bool isChecked=false;
}

角度

如果要保持组件的状态并使其隐藏,可以使用[style.display]属性将其设置为“none”。

const template=`
<div>
    <h1>Check: {{isChecked}}</h1>
    <input id=chk type=checkbox [(ngModel)]="isChecked">
    <label for=chk>CheckBox</label>
    <div *ngIf="isChecked">
        <input>
    </div>
    <div [style.display]="isChecked? '': 'none'">
        <input>
    </div>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "IfAndShow",
    template: template
})
export class IfAndShowComponent{
    isChecked=false;
}

13. 遍历(循环)

在Vue.js / Blazor / Angular中,如果具有相同的配置标签,则可以重复显示它们。

关于v-bind:key/@key

为了确保在循环生成的列表中组件的唯一性等方面,建议添加key属性。
(在Blazor中不推荐使用?)
据说这样可以提高性能。
在Vue.js中使用v-bind:key,在Blazor中使用@key。

Vue.js (原名为Vue)是一个用于构建用户界面的渐进式JavaScript框架。

将想要重复的元素包含在v-for属性中。
这可以使用template标签作为无名标签并将其包围起来进行循环,没有问题。
在大多数主流浏览器中,对象的顺序是确定的,但请注意规范上并不保证这一点。
(虽然也可以使用Map,但信息量较少,有些可疑。
根据常规的for循环方式写成v-for=”[key, value] of list”,似乎可以使用。)

<template>
<div>
    <div v-for="(isChecked,key) in dict">
        <input :id="key" type=checkbox v-model="dict[key]" :key="key">
        <label :for="key">{{key}}</label>
    </div>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class ForEachLoop extends Vue{
    dict:{[s:string]:boolean}={
        A:true,
        B:true,
        C:true,
        D:false,
        E:false
    };
}
</script>

蓝光

使用@for/@foreach。

@page "/ForEachLoop"

<div>
    @foreach(var (key,isChecked) in dict){
        <div>
            <input id=@key type=checkbox @bind="dict[key]" @key="key">@(""
            )<label for=@key>@key</label>
        </div>
    }
</div>

@code{
    Dictionary<string,bool> dict=new Dictionary<string,bool>{
        {"A",true},
        {"B",true},
        {"C",true},
        {"D",false},
        {"E",false}
    };
}

Angular (角度)

可以把 *ngFor 属性应用到想要循环的元素中。
与 Vue.js 的模板类似,可以将 ng-container 用作~元素。

const template=`
<div>
    <div *ngFor="let v of dict | keyvalue">
        <input [id]="v.key" type=checkbox [(ngModel)]="dict[v.key]">
        <label [for]="v.key">{{v.key}}</label>
    </div>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "ForEachLoop",
    template: template
})
export class ForEachLoopComponent{
    dict:{[s:string]:boolean}={
        A:true,
        B:true,
        C:true,
        D:false,
        E:false
    };
}

14. 添加组件

在Vue.js/Blazor/Angular中,你可以将自定义的元素(组件)嵌入到HTML标签中。在Blazor中,所有的组件都会被自动加载,但在Vue.js中,你需要使用import语句来指定要加载的组件。在Angular中,你需要将组件的引用写入app.module.ts文件中。

Vue.js(中文名为“视图”)是一种用于构建用户界面的开源JavaScript框架。

<template>
<div>
    <ComponentA />
    <ComponentB />
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";
import ComponentA from "@/components/ComponentA.vue";
import ComponentB from "@/components/ComponentB.vue";

@Component({
    components:{
        ComponentA,
        ComponentB
    }
})
export default class AddComponent extends Vue{}
</script>
<template>
<div>
    <h3>ComponentA</h3>
    <textarea></textarea>
</div>
</template>
<template>
<div>
    <input id=chk type=checkbox>
    <label for=chk>ComponentB</label>
</div>
</template>

布拉索

@page "/AddComponent"

<div>
    <ComponentA />
    <ComponentB />
</div>
<div>
    <h3>ComponentA</h3>
    <textarea></textarea>
</div>
<div>
    <input id=chk type=checkbox>
    <label for=chk>ComponentB</label>
</div>

角度

const template=`
<div>
    <ComponentA></ComponentA>
    <ComponentB></ComponentB>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "AddComponent",
    templateUrl: template
})
export class AddComponentComponent{}
const template=`
<div>
    <h3>ComponentA</h3>
    <textarea></textarea>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "ComponentA",
    template: template
})
export class ComponentAComponent{}
const template=`
<div>
    <input id=chk type=checkbox>
    <label for=chk>ComponentB</label>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "ComponentB",
    templateUrl: template
})
export class ComponentBComponent{}

15. 组件的属性 de

在Vue.js/Blazor/Angular中,可以给子组件添加属性,并在子组件中作为属性使用。
在Vue.js中使用@Prop,在Blazor中使用[Parameter],在Angular中使用@Input来指定属性名。

Vue.js 是一个用于构建用户界面的渐进式JavaScript框架。

<template>
<div>
    <ComponentC msg="View Message" color="#FF00FF" />
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";
import ComponentC from "@/components/ComponentC.vue";

@Component({
    components:{
        ComponentC
    }
})
export default class ComponentAttribute extends Vue{}
</script>
<template>
<div :style="{color: color}">
    Input Attribute={{msg}}
</div>
</template>

<script lang=ts>
import {Component,Prop,Vue} from "vue-property-decorator";

@Component
export default class ComponentC extends Vue{
    @Prop()
    private msg:string;
    @Prop()
    private color:string;
}
</script>

布雷泽

@page "/ComponentAttribute"

<div>
    <ComponentC msg="View Message" color="#FF00FF" />
</div>
<div style=@($"color: {color}")>
    Input Attribute=@msg
</div>

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

角度

const template=`
<div>
    <ComponentC msg="View Message" color="#FF00FF"></ComponentC>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "ComponentAttribute",
    template: template
})
export class ComponentAttributeComponent{}
const template=`
<div [style.color]="color">
    Input Attribute={{msg}}
</div>
`;

import {Component,Input} from "@angular/core";

@Component({
  selector: "ComponentC",
  template: template
})
export class ComponentCComponent{
    @Input()
    private msg: string;
    @Input()
    private color: string;
}

16. 调用组件的方法

在Vue.js/Blazor/Angular中,您可以调用子组件中的成员。

Vue.js 是一种流行的前端框架。

为了在ref属性中绑定变量名称,请指定需要声明的名称,以便在类中使用。

<template>
<div>
    <Toast ref="toast" />
    <button @click="viewToast">Click Me!</button>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";
import Toast from "@/components/Toast.vue";

@Component({
    components:{
        Toast
    }
})
export default class ComponentMethod extends Vue{
    $refs!:{toast: Toast};

    async viewToast(){
        await this.$refs.toast.show("View Torst!");
    }
}
</script>
<template>
<dialog :open="isShow">
    {{msg}}
</dialog>
</template>

<script lang=ts>
import {Component,Vue,Prop} from "vue-property-decorator";

@Component
export default class Toast extends Vue{
    isShow=false;
    msg="";

    public async show(msg:string){
        this.msg=msg;
        this.isShow=true;
        await new Promise(res=>setTimeout(res,1500));
        this.isShow=false;
    }
}
</script>

Blazor 是一种本机在中国的中文的表述。

需要使用@ref属性来指定绑定变量的名称。
为了在类中使用它,需要进行声明。

@page "/ComponentMethod"

<div>
    <Toast @ref="toast" />
    <button @onclick="viewToast">Click Me!</button>
</div>

@code{
    Toast toast;

    async Task viewToast(){
        await toast.show("View Toast!");
    }
}
<dialog open=@isShow>
    @msg
</dialog>

@code{
    bool isShow=false;
    string msg="";

    public async Task show(string msg){
        this.msg=msg;
        isShow=true;
        StateHasChanged();
        await Task.Delay(2500);
        isShow=false;
        StateHasChanged();
    }
}

角度

通过引入组件并在类中使用@ViewChild装饰器分配它来使用。

const template=`
<div>
    <Toast></Toast>
    <button (click)="viewToast()">Click Me!</button>
</div>
`;

import {Component,ViewChild} from "@angular/core";
import {ToastComponent} from "../components/Toast.component";

@Component({
    selector: "ComponentMethod",
    template: template
})
export class ComponentMethodComponent{
    @ViewChild(ToastComponent,{static: false})
    private toast:ToastComponent;

    async viewToast(){
        await this.toast.show("View Torst!");
    }
}
const template=`
<dialog [open]="isShow">
    {{msg}}
</dialog>
`;

import {Component} from "@angular/core";

@Component({
    selector: "Toast",
    template: template
})
export class ToastComponent{
    isShow=false;
    msg="";

    public async show(msg:string){
        this.msg=msg;
        this.isShow=true;
        await new Promise(res=>setTimeout(res,1500));
        this.isShow=false;
    }
}

17. 状态管理容器

类似全局变量,可以从任何组件中引用。
这里只进行变量的简单读写操作。

在Vue.js中,有一个官方的库叫作Vuex。
在Blazor中,虽然没有类似的库存在,但可以通过Blazor的基本功能来处理容器,实现相同的效果。
参考1 参考2
在Angular中,可以将服务作为容器来使用。

只列出操作示例,详细内容略过。

Vue.js (Chinese: Vue.js) is a progressive JavaScript framework for building user interfaces.

<template>
<div>
    <BooksInput />
    <button @click="getBooks">Get Books!</button>
    <h3>BookLists ({{date}})</h3>
    <ul>
        <li v-for="book in books" :key="book">{{book}}</li>
    </ul>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";
import BooksInput from "@/components/BooksInput.vue";
import store from "@/store";

@Component({
    components:{
        BooksInput
    }
})
export default class StateContainer extends Vue{
    books:string[]=[];
    date:Date=null;

    getBooks(){
        this.books=store.state.books;
        this.date=store.state.date;
    }
}
</script>
<template>
<div>
    <div><textarea v-model="bookList" id="bookList"></textarea></div>
    <button @click="setBooks">Set Books!</button>
</div>
</template>

<style scoped>
    #bookList{
        height: 300px;
        width: 300px;
    }
</style>

<script lang=ts>
import {Component,Vue,Prop} from "vue-property-decorator";
import store from "@/store";

@Component
export default class BooksInput extends Vue{
    bookList="";

    public setBooks(){
        store.commit("setBooks",this.bookList.split(/\r|\n|\r\n/).filter(s=>s!=""));
        store.commit("setDate",new Date());
        alert("setBooks!");
    }
}
</script>
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);
export default new Vuex.Store({
    state:{
        books: [] as string[],
        date: null as Date
    },
    mutations:{
        setBooks(state,books:string[]){
            state.books=books;
        },
        setDate(state,date:Date){
            state.date=date;
        }
    }
});

”蓝尤“

需要将容器注册为服务于 Program.cs。

@page "/StateContainer"

<div>
    <BooksInput />
    <button @onclick="getBooks">Get Books!</button>
    <h3>BookLists (@date)</h3>
    <ul>
        @foreach(var book in books){<li @key="book">@book</li>}
    </ul>
</div>

@inject AppState state;
@implements IDisposable;

@code{
    protected override void OnInitialized(){state.OnChange+=StateHasChanged;}
    public void Dispose(){state.OnChange-=StateHasChanged;}

    string[] books={};
    DateTime? date=null;

    void getBooks(){
        books=state.books;
        date=state.date;
    }
}
<div >
    <div><textarea @bind="bookList" id="bookList"></textarea></div>
    <button @onclick="setBooks">Set Books!</button>
</div>

<style>
    #bookList{
        height: 300px;
        width: 300px;
    }
</style>

@inject IJSRuntime js;
@inject AppState state;

@code{
    string bookList="";

    public void setBooks(){
        state.setBooks(Array.FindAll(bookList.Replace("\r\n","\n").Split(new[]{'\n','\r'}),s=>s!=""));
        state.setDate(DateTime.Now);
        js.InvokeVoidAsync("alert","setBooks!");
    }
}
using System;

public class AppState{
    public string[] books{get;private set;}=new string[]{};
    public DateTime? date{get;private set;}=null;

    public void setBooks(string[] books){
        this.books=books;
        NotifyStateChanged();
    }
    public void setDate(DateTime date){
        this.date=date;
        NotifyStateChanged();
    }

    public event Action OnChange;
    private void NotifyStateChanged()=>OnChange?.Invoke();
}

角度

const template=`
<div>
    <BooksInput></BooksInput>
    <button (click)="getBooks()">Get Books!</button>
    <h3>BookLists ({{date}})</h3>
    <ul>
        <li *ngFor="let book of books">{{book}}</li>
    </ul>
</div>
`;

import {Component} from "@angular/core";
import {StoreService} from "../../store.service";

@Component({
    selector: "StateContainer",
    template: template
})
export class StateContainerComponent{
    constructor(
        private store:StoreService
    ){};
    books:string[]=[];
    date:Date=null;

    getBooks(){
        this.books=this.store.books;
        this.date=this.store.date;
    }
}
const template=`
<div>
    <div><textarea [(ngModel)]="bookList" id="bookList"></textarea></div>
    <button (click)="setBooks()">Set Books!</button>
</div>

<style>
#bookList{
    height: 300px;
    width: 300px;
}
</style>
`;

import {Component} from "@angular/core";
import {StoreService} from "../../store.service";

@Component({
    selector: "BooksInput",
    template: template
})
export class BooksInputComponent{
    constructor(
        private store:StoreService
    ){}
    bookList=`たのしいさんすう
たのしいこくご
たのしいどうとく
かぐやひめ
シンデレラ
うらしまたろう
かちかちやま`;

    public setBooks(){
        this.store.books=this.bookList.split(/\r|\n|\r\n/).filter(s=>s!="");
        this.store.date=new Date();
        alert("setBooks!");
    }
}
import {Injectable} from "@angular/core";

@Injectable({
    providedIn: "root"
})
export class StoreService{
    books:string[]=[];
    date:Date=null;
}

18. 读取JSON文件

在Vue.js/Blazor/Angular中,可以读取并显示JSON文件。(由于是客户端端,无法进行写入操作)

在这里将加载以下JSON文件。

[
    "大剣",
    "太刀",
    "片手剣",
    "双剣",
    "ハンマー",
    "狩猟笛",
    "ランス",
    "ガンランス",
    "スラッシュアックス",
    "チャージアックス",
    "操虫棍",
    "ライトボウガン",
    "ヘビィボウガン",
    "弓"
]

Vue.js 是一个流行的JavaScript前端框架。

使用 require 函数将 JSON 文件放置在 /src/assets/ 目录下。

<template>
<div>
    <h3>Read JSON</h3>
    <ul>
        <li v-for="value in list" :key="value">{{value}}</li>
    </ul>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class ReadJSON extends Vue{
    list:string[]=[];

    mounted(){
        this.list=require("@/assets/weapons.json");
    }
}
</script>

蔽芦阔尔

使用 HttpClient.GetJsonAsync 方法,在/wwwroot/ 目录下放置 JSON 文件。

将 JSON 文件放置在 /wwwroot/ 目录下,使用 HttpClient.GetJsonAsync 方法。

@page "/ReadJSON"

<div>
    <h3>Read JSON</h3>
    <ul>
        @foreach(var value in list){<li @key="value">@value</li>}
    </ul>
</div>

@inject HttpClient http;

@code{
    string[] list={};

    protected override async Task OnAfterRenderAsync(bool firstRender){
        if(!firstRender) return;
        list=await http.GetJsonAsync<string[]>("Assets/weapons.json?0");
        StateHasChanged();
    }
}

角度

使用HttpClient模块的get方法。
需要在add.module.ts中添加服务才能使用。
JSON文件的路径必须为/src/assets/下。

const template=`
<div>
    <h3>Read JSON</h3>
    <ul>
        <li *ngFor="let value of list">{{value}}</li>
    </ul>
</div>`;

import {Component,OnInit} from "@angular/core";
import {HttpClient} from "@angular/common/http";

@Component({
    selector: "ReadJSON",
    template: template
})
export class ReadJSONComponent implements OnInit{
    constructor(
        private http:HttpClient
    ){}
    list:string[]=[];

    async ngOnInit(){
        this.list=await new Promise(res=>this.http.get("./assets/weapons.json?0").subscribe(res));
    }
}

19. 文本文件的读取

Vue.js (中文选项:Vue.js )

我们无法仅使用Vue.js来读取纯文本文件。建议使用Vue.js官方的axios来进行读取。可以通过yarn或npm将其添加到项目中。

此外,使用这种方法也可以加载JSON,但需要注意的是,对文件的处理有些不同。

メソッドパス備考axios.get/public/個別の静的ファイルとして扱うrequire/src/assetsビルド時jsファイルとともに結合される
<template>
<div>
    <h3>Read Text</h3>
    <pre>{{text}}</pre>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";
import axios from "axios";

@Component
export default class ReadText extends Vue{
    text="";

    async mounted(){
        this.text=await axios.get("./kimigayo.txt?0").then(v=>v.data);
    }
}
</script>

Blazor (布拉泽)

使用HttpClient.GetStringAsync进行读取,就像使用JSON一样。

@page "/ReadText"

<div>
    <h3>Read Text</h3>
    <pre>@text</pre>
</div>

@inject HttpClient http;

@code{
    string text="";

    protected override async Task OnAfterRenderAsync(bool firstRender){
        if(!firstRender) return;
        text=await http.GetStringAsync("./kimigayo.txt?0");
        StateHasChanged();
    }
}

角度

你可以像使用JSON一样使用HttpClient.get。
要读取文本文件,需要在参数中加入{ responseType: “text” }。

const template=`
<div>
    <h3>Read Text</h3>
    <pre>{{text}}</pre>
</div>
`;

import {Component,OnInit} from "@angular/core";
import {HttpClient} from "@angular/common/http";

@Component({
    selector: "ReadText",
    template: template
})
export class ReadTextComponent implements OnInit {
    constructor(
        private http:HttpClient
    ){}
    text="";

    async ngOnInit(){
        this.text=await new Promise(res=>this.http.get("./assets/kimigayo.txt?0",{responseType:"text"}).subscribe(res));
    }
}

20. HTML输出

在Vue.js/Blazor/Angular中,您可以像element.innerHTML一样将HTML写入到HTML中。

Vue.js是一个流行的JavaScript前端框架。

将值绑定到 v-html 属性上。
不允许使用 script 元素。

<template>
<div>
    <h3>Inner HTML</h3>
    <textarea v-model="txt" style="height:300px;width:300px;"></textarea>
    <div v-html="txt"></div>
</div>
</template>

<script lang=ts>
import {Component,Vue} from "vue-property-decorator";

@Component
export default class InnerHTML extends Vue{
    txt=
`<h1>TEST TITLE</h1>

<span style=color:#009900>
<u>Say</u> <i>Hello!</i>
</span>`;
}
</script>

Blazor = 布雷泽

通过将字符串转换为MarkupString类型并将其输出到任意位置,可以输出HTML。
无法使用script元素。

@page "/InnerHTML"

<div>
    <h3>Inner HTML</h3>
    <textarea @bind-value="txt" @bind-value:event="oninput" style="height:300px;width:300px;"></textarea>
    <div>@((MarkupString)txt)</div>
</div>

@code{
    string txt=
@"<h1>TEST TITLE</h1>

<span style=color:#990099>
<u>Say</u> <i>Hello!</i>
</span>";
}

角度

把值绑定到[innerHTML]属性中。除了script标签外,还不能使用style标签。

const template=`
<div>
    <h3>Inner HTML</h3>
    <textarea [(ngModel)]="txt" style="height:300px;width:300px;"></textarea>
    <div [innerHTML]="txt"></div>
</div>
`;

import {Component} from "@angular/core";

@Component({
    selector: "InnerHTML",
    template: template
})
export class InnerHTMLComponent{
    txt=
`<h1>TEST TITLE</h1>

<font color="#990000">
<u>Say</u> <i>Hello!</i>
</font>`;
}

路由器 qì)

最后,我们将展示本次使用的路由器示例。

Vue.js是一个用于构建用户界面的渐进式JavaScript框架。

每当页面增加时,都需要在/src/router.index.ts中添加。

import Vue from "vue";
import VueRouter from "vue-router";
import Index from "@/views/Index.vue";
import PageA from "@/views/PageA.vue";
import PageB from "@/views/PageB.vue";

Vue.use(VueRouter);

const routes=[
    {path: "/", name: "Index", component: Index},
    {path: "/PageA", name: "PageA", component: PageA},
    {path: "/PageB", name: "PageB", component: PageB}
];

const router=new VueRouter({
    mode: "history",
    base: process.env.BASE_URL,
    routes
});

export default router;
<template>
<main style=display:flex>
    <NavMenu />
    <div class=v-hr></div>
    <router-view/>
</main>
</template>

<style scoped>
.v-hr{
    margin: 0 10px;
    border-right: 5px solid #CCC;
    height: 100vh;
}
</style>

<script lang:ts>
import {Component,Vue} from "vue-property-decorator";
import NavMenu from "@/components/NavMenu.vue";

@Component({
    components:{
        NavMenu
    }
})
export default class App extends Vue{}
</script>
<template>
<nav>
    <ol type="1" start="0">
        <li><router-link to="/">index</router-link></li>
        <li><router-link to="/PageA">PageA</router-link></li>
        <li><router-link to="/PageB">PageB</router-link></li>
    </ol>
</nav>
</template>

<style scoped>
.router-link-exact-active{
    color: #FF0000;
    font-weight: bold;
}
</style>

Blazor

@inherits LayoutComponentBase

<main style=display:flex>
    <NavMenu />
    <div class=v-hr></div>
    @Body
</main>

<style>
.v-hr{
    margin: 0 10px;
    border-right: 5px solid #CCC;
    height: 100vh;
}
</style>
<div>
    <ol type="1" start="0">
        <li><NavLink Match="NavLinkMatch.All" href="./">index</NavLink></li>
        <li><NavLink Match="NavLinkMatch.All" href="./PageA">PageA</NavLink></li>
        <li><NavLink Match="NavLinkMatch.All" href="./PageB">PageB</NavLink></li>
    </ol>
</div>

<style>
nav .active{
    color: #FF0000;
    font-weight: bold;
}
</style>

角度

每增加一页,都需要在/src/app/app-routing.module.ts中进行添加。

import {NgModule} from "@angular/core";
import {Routes, RouterModule} from "@angular/router";

import {IndexComponent} from "./pages/Index/Index.component";
import {PageAComponent} from "./pages/PageA/PageA.component";
import {PageBComponent} from "./pages/PageB/PageB.component";

const routes: Routes=[
    {path: "", component: IndexComponent},
    {path: "PageA", component: PageAComponent},
    {path: "PageB", component: PageBComponent},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
const template=`
<main style=display:flex>
    <NavMenu></NavMenu>
    <div class=v-hr></div>
    <router-outlet></router-outlet>
</main>

<style>
.v-hr{
    margin: 0 10px;
    border-right: 5px solid #CCC;
    height: 98vh;
}
</style>
`;

import {Component} from "@angular/core";

@Component({
    selector: "app-root",
    template: template
})
export class AppComponent{}
const template=`
<nav>
    <ol type="1" start="0">
        <li><a routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}" routerLink="/">index</a></li>
        <li><a routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}" routerLink="/PageA">PageA</a></li>
        <li><a routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}" routerLink="/PageB">PageB</a></li>
    </ol>
</nav>

<style>
.active{
    color: #FF0000;
    font-weight: bold;
}
</style>
`;

import {Component} from "@angular/core";

@Component({
    selector: "NavMenu",
    templateUrl: template
})
export class NavMenuComponent{}

总结

我总结了一下Vue.js和Blazor(+ Angular)的语法比较。虽然在样式规范等方面有些无法改变的部分,但大部分情况下都可以以几乎相同的方式编写。我还觉得在速度方面还有些困难,但我希望未来的Blazor能有所期待。

广告
将在 10 秒后关闭
bannerAds