单向绑定

MVVM就是在前端页面上,应用了扩展的MVC模式,我们关心Model的变化,MVVM框架自动把Model的变化映射到DOM结构上,这样,用户看到的页面内容就会随着Model的变化而更新。

例如,我们定义好一个JavaScript对象作为Model,并且把这个Model的两个属性绑定到DOM节点上:

mvvm-bind

经过MVVM框架的自动转换,浏览器就可以直接显示Model的数据了:

mvvm-result

现在问题来了:MVVM框架哪家强?

目前,常用的MVVM框架有:

Angular:Google出品,名气大,但是很难用;

Backbone.js:入门非常困难,因为自身API太多;

Ember:一个大而全的框架,想写个Hello world都很困难。

我们选择MVVM的目标应该是入门容易,安装简单,能直接在页面写JavaScript,需要更复杂的功能时又能扩展支持。

所以,综合考察,最佳选择是尤雨溪大神开发的MVVM框架:Vue.js

目前,Vue.js的最新版本是2.0,我们会使用2.0版本作为示例。

我们首先创建基于koa的Node.js项目。虽然目前我们只需要在HTML静态页面中编写MVVM,但是很快我们就需要和后端API进行交互,因此,要创建基于koa的项目结构如下:

hello-vue/
|
+- .vscode/
|  |
|  +- launch.json <-- VSCode 配置文件
|
+- app.js <-- koa app
|
+- static-files.js <-- 支持静态文件的koa middleware
|
+- package.json <-- 项目描述文件
|
+- node_modules/ <-- npm安装的所有依赖包
|
+- static/ <-- 存放静态资源文件
   |
   +- css/ <-- 存放bootstrap.css等
   |
   +- fonts/ <-- 存放字体文件
   |
   +- js/ <-- 存放各种js文件
   |
   +- index.html <-- 使用MVVM的静态页面

这个Node.js项目的主要目的是作为服务器输出静态网页,因此,package.json仅需要如下依赖包:

"dependencies": {
    "koa": "2.0.0",
    "mime": "1.3.4",
    "mz": "2.4.0"
}

使用npm install安装好依赖包,然后启动app.js,在index.html文件中随便写点内容,确保浏览器可以通过http://localhost:3000/static/index.html访问到该静态文件。

紧接着,我们在index.html中用Vue实现MVVM的一个简单例子。

安装Vue

安装Vue有很多方法,可以用npm或者webpack。但是我们现在的目标是尽快用起来,所以最简单的方法是直接在HTML代码中像引用jQuery一样引用Vue。可以直接使用CDN的地址,例如:

<script src="https://unpkg.com/vue@2.0.1/dist/vue.js"></script>

也可以把vue.js文件下载下来,放到项目的/static/js文件夹中,使用本地路径:

<script src="/static/js/vue.js"></script>

这里需要注意,vue.js是未压缩的用于开发的版本,它会在浏览器console中输出很多有用的信息,帮助我们调试代码。当开发完毕,需要真正发布到服务器时,应该使用压缩过的vue.min.js,它会移除所有调试信息,并且文件体积更小。

编写MVVM

下一步,我们就可以在HTML页面中编写JavaScript代码了。我们的Model是一个JavaScript对象,它包含两个属性:

{
    name: 'Robot',
    age: 15
}

而负责显示的是DOM节点可以用{{ name }}{{ age}}来引用Model的属性:

<div id="vm">
    <p>Hello, {{ name }}!</p>
    <p>You are {{ age }} years old!</p>
</div>

最后一步是用Vue把两者关联起来。要特别注意的是,在<head>内部编写的JavaScript代码,需要用jQuery把MVVM的初始化代码推迟到页面加载完毕后执行,否则,直接在<head>内执行MVVM代码时,DOM节点尚未被浏览器加载,初始化将失败。正确的写法如下:

<html>
<head>

<!-- 引用jQuery -->
<script src="/static/js/jquery.min.js"></script>

<!-- 引用Vue -->
<script src="/static/js/vue.js"></script>

<script>
// 初始化代码:
$(function () {
    var vm = new Vue({
        el: '#vm',
        data: {
            name: 'Robot',
            age: 15
        }
    });
    window.vm = vm;
});
</script>

</head>

<body>

    <div id="vm">
        <p>Hello, {{ name }}!</p>
        <p>You are {{ age }} years old!</p>
    </div>

</body>
<html>

我们创建一个VM的核心代码如下:

var vm = new Vue({
    el: '#vm',
    data: {
        name: 'Robot',
        age: 15
    }
});

其中,el指定了要把Model绑定到哪个DOM根节点上,语法和jQuery类似。这里的'#vm'对应ID为vm的一个<div>节点:

<div id="vm">
    ...
</div>

在该节点以及该节点内部,就是Vue可以操作的View。Vue可以自动把Model的状态映射到View上,但是不能操作View范围之外的其他DOM节点。

然后,data属性指定了Model,我们初始化了Model的两个属性nameage,在View内部的<p>节点上,可以直接用{{ name }}引用Model的某个属性。

一切正常的话,我们在浏览器中访问http://localhost:3000/static/index.html,可以看到页面输出为:

Hello, Robot!
You are 15 years old!

如果打开浏览器console,因为我们用代码window.vm = vm,把VM变量绑定到了window对象上,所以,可以直接修改VM的Model:

window.vm.name = 'Bob'

执行上述代码,可以观察到页面立刻发生了变化,原来的Hello, Robot!自动变成了Hello, Bob!。Vue作为MVVM框架会自动监听Model的任何变化,在Model数据变化时,更新View的显示。这种Model到View的绑定我们称为单向绑定。

经过CSS修饰后的页面如下:

hello-vue

可以在页面直接输入JavaScript代码改变Model,并观察页面变化。

单向绑定

在Vue中,可以直接写{{ name }}绑定某个属性。如果属性关联的是对象,还可以用多个.引用,例如,{{ address.zipcode }}

另一种单向绑定的方法是使用Vue的指令v-text,写法如下:

<p>Hello, <span v-text="name"></span>!</p>

这种写法是把指令写在HTML节点的属性上,它会被Vue解析,该节点的文本内容会被绑定为Model的指定属性,注意不能再写双花括号{{ }}

参考源码

hello-vue

<think>我们正在处理用户关于.NET MAUI中单向数据绑定的问题。根据引用内容,我们知道.NET MAUI支持单向和双向数据绑定。用户明确要求实现单向数据绑定。 回顾引用: 引用[1]提到:".NET MAUI还提供:支持单向和双向数据绑定,开发模式更优雅且易于维护。" 引用[2]详细说明了双向数据绑定的实现,但用户需要的是单向。 在单向数据绑定中,数据流是单向的,通常从数据源(如ViewModel)流向UI,或者反过来(但用户指定的是单向,通常我们理解为源到目标)。在.NET MAUI中,我们可以使用Binding表达式,并设置BindingMode为OneWay。 实现步骤: 1. 准备数据源:通常是一个实现了INotifyPropertyChanged的类(如ViewModel),这样当属性改变时,UI会自动更新。 2. 在XAML中,使用Binding表达式,并可以指定Mode为OneWay(虽然OneWay是默认模式,但显式指定更清晰)。 注意:如果不指定Mode,对于大多数可读属性(如Label的Text)默认是OneWay,而对于可编辑控件(如Entry的Text)默认是TwoWay。因此,如果用户明确要求单向,我们可以显式设置Mode=OneWay。 示例: 假设我们有一个ViewModel,其中有一个字符串属性Title,我们想要在Label上显示这个属性,并且当Title改变时,Label自动更新(但Label的修改不会影响Title)。 ViewModel示例: ```csharp public class MyViewModel : INotifyPropertyChanged { private string _title; public string Title { get => _title; set { if (_title != value) { _title = value; OnPropertyChanged(); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } ``` 在页面中,我们需要设置BindingContext为这个ViewModel的实例(可以在页面构造函数中设置)。 XAML中,使用Binding: ```xml <Label Text="{Binding Title, Mode=OneWay}" /> ``` 这样,当Title属性在ViewModel中改变时,Label的Text会自动更新。但是,如果用户试图修改Label的Text(例如通过代码),它不会改变Title属性。 另外,如果数据源是只读的,并且我们只需要在初始化时设置一次,也可以使用OneTime模式(绑定只发生一次,后续改变不会更新)。 因此,用户可以根据需要选择OneWay或OneTime。 相关问题: 1. 单向数据绑定和双向数据绑定有什么区别? 2. 在.NET MAUI中,如何实现双向数据绑定? 3. 如何设置绑定上下文(BindingContext)? 4. 什么是INotifyPropertyChanged接口,为什么需要它? 5. 除了OneWay,还有哪些绑定模式? 注意:我们引用了站内内容,但用户的问题主要基于引用[1]和引用[2]的背景,因此我们可以在回答末尾添加引用标识。 回答结构: 1. 解释单向数据绑定的概念。 2. 给出实现步骤和示例代码(包括ViewModel和XAML)。 3. 说明绑定模式的选择(OneWay和OneTime)。 4. 生成相关问题。</think>### .NET MAUI 单向数据绑定实现方法 在 .NET MAUI 中实现单向数据绑定(OneWay Binding),数据会从数据源(如 ViewModel)流向 UI 界面,但 UI 的修改不会反向影响数据源。以下是详细实现步骤: #### 1. **创建数据源(ViewModel)** 实现 `INotifyPropertyChanged` 接口,确保属性变更时自动通知 UI: ```csharp public class ProductViewModel : INotifyPropertyChanged { private string _productName; public string ProductName { get => _productName; set { if (_productName != value) { _productName = value; OnPropertyChanged(); // 通知UI更新 } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } ``` #### 2. **在 XAML 中设置绑定** 使用 `{Binding}` 表达式并显式指定 `Mode=OneWay`: ```xml <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiApp.MainPage"> <VerticalStackLayout> <!-- 单向绑定:数据源 → UI --> <Label Text="{Binding ProductName, Mode=OneWay}" FontSize="24" HorizontalOptions="Center"/> </VerticalStackLayout> </ContentPage> ``` #### 3. **设置绑定上下文** 在页面代码中关联 ViewModel: ```csharp public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); BindingContext = new ProductViewModel { ProductName = "初始值" }; } } ``` #### 4. **验证单向特性** - 当 `ProductViewModel.ProductName` 改变时,UI 会自动更新(如通过后台定时任务修改值)。 - 如果 UI 元素是 `Entry` 等可编辑控件,用户输入**不会**改变 `ProductName` 的值(与双向绑定的区别)。 #### 关键注意事项 1. **默认绑定模式**: - 不可编辑控件(如 `Label`)默认是 `OneWay` - 可编辑控件(如 `Entry`)默认是 `TwoWay`,需显式指定 `Mode=OneWay` 关闭反向更新 2. **性能优化**: 对于静态数据,可使用 `OneTime` 模式(仅初始化时绑定一次)[^1]: ```xml <Label Text="{Binding ProductName, Mode=OneTime}" /> ``` 3. **数据流方向**: - `OneWay`:数据源 → UI(单向) - `TwoWay`:数据源 ↔ UI(双向,需配合 `@bind` 使用)[^2] - `OneWayToSource`:UI → 数据源(反向单向) - `OneTime`:仅初始化绑定一次 #### 示例场景 ```csharp // 后台修改数据(触发UI更新) async Task UpdateData() { var vm = (ProductViewModel)BindingContext; await Task.Delay(2000); vm.ProductName = "更新后的产品名"; // UI 自动刷新 } ``` > **单向绑定适用场景**:显示实时数据(如股票价格)、只读信息展示、计算结果输出等[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值