为什么 vue.js 不使用 moment.js 使用日期选择器更新 dom



我正在 vue.js 中编写一个(非常)简单的日期选择器控件,使用 moment.js 来格式化和改变日期。

我遇到的问题是,即使单击任一按钮时修改了组件中的日期实例,显示也不会更新。

我尝试将其更改为简单的整数而不是日期实例,并且按预期工作(DOM 已正确更新)

这是我为此准备的源代码:

应用.js

Vue.component("datePicker", {
props: ["value"],
data: function() {
return { selectedDate: moment(this.value) };
},
template: "<div><button v-on:click='decrement'>&lt;</button>{{formattedSelectedDate}}<button v-on:click='increment'>&gt;</button></div>",
methods: {
increment: function () {
this.selectedDate.add(1, "days");
this.$emit('input', this.selectedDate);
},
decrement: function () {
this.selectedDate.subtract(1, "days");
this.$emit('input', this.selectedDate);
}
},
computed: {
formattedSelectedDate: function() {
return this.selectedDate.format("YYYY-MM-DD");
}
}
});
var PointTracker = new Vue({
el: "#PointTracker",
data: {
selectedDate: moment(),
todoItems: {}
}
});

索引.html

<html>
<head>
<meta charset="utf-8">
<title>Point Tracker</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="PointTracker">
<date-picker v-model="selectedDate">
</div>
<script src="node_modules/moment/moment.js"></script>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-resource/dist/vue-resource.js"></script>
<script src="app.js"></script>
</body>
</html>

你必须更改selectedDate的引用,因为它是从moment函数返回的,它们总是具有相同的引用,因此不会为这些函数触发 vue 观察者。

您必须进行以下更改才能更改引用:

methods: {
increment: function () {
this.selectedDate = moment(this.selectedDate).add(1, "days")
},
decrement: function () {
this.selectedDate = moment(this.selectedDate).subtract(1, "days")
}
},

工作小提琴:http://jsfiddle.net/mimani/pLcfyrvy/1/


以下是从矩库中加/减的实现:

function addSubtract (duration, input, value, direction) {
var other = createDuration(input, value);
duration._milliseconds += direction * other._milliseconds;
duration._days         += direction * other._days;
duration._months       += direction * other._months;
return duration._bubble();
}
// supports only 2.0-style add(1, 's') or add(duration)
export function add (input, value) {
return addSubtract(this, input, value, 1);
}
// supports only 2.0-style subtract(1, 's') or subtract(duration)
export function subtract (input, value) {
return addSubtract(this, input, value, -1);
}

它返回相同的对象,因此日期对象的引用相同。


为什么会发生

发生这种情况是因为 moment.js 是一个不是immutable,这意味着当您在 moment 对象上调用加/减函数时,它会返回相同的对象,并在更改属性后返回。

关于 vue 的反应性有一些警告,由于 Object.observe 现在已经过时了,它无法跟踪 javascript 对象是否在内部发生了变化,除非您克隆该对象并创建一个在您的情况下需要的新对象。

还有其他解决方法,包括使用冻结时刻库。

问题是对选定日期的引用永远不会更改。即使您在其上调用方法,它也是相同的对象。
这里的一种方法是以不同的格式存储日期。作为日期对象,作为数字等。然后,当您对其进行更改时,您可以生成该数据的新实例。

下面是一个示例:

var datePicker = Vue.component("datePicker", {
data: function() {
return { selectedDate: moment().toDate() }
},
template: `<div>
<button v-on:click='decrement'>&lt;</button>
{{formattedSelectedDate}}
<button v-on:click='increment'>&gt;</button>
{{selectedDate}}
</div>`,
methods: {
increment: function () {
var currentDate = moment(this.selectedDate);
currentDate.add(1, "days");
this.selectedDate = currentDate.toDate();
},
decrement: function () {
var currentDate = moment(this.selectedDate);
currentDate.subtract(1, "Days");
this.selectedDate = currentDate.toDate();
}
},
computed: {
formattedSelectedDate: function() {
return moment(this.selectedDate).format("YYYY-MM-DD");
}
}
});
var PointTracker = new Vue({
el: "#PointTracker",
data: {
date: 0
},
components: {
datePicker
}
});
<div id="PointTracker">
<date-picker />
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.js"></script>

这是因为 moment 和 vue 都依赖于定义对象的 getter 和 setter,这会导致一个覆盖另一个。(因此更改参考有效,因为vm.selectedDate.set被覆盖而vm.set则不被覆盖)

来自瞬间的文档:

时刻.js使用重载的吸气手和二传手。

来自 vue 的文档:

Vue 会递归地将其属性转换为 getter/setter,使其"反应式"。对象必须是纯文本:浏览器 API 对象和原型属性等本机对象将被忽略。经验法则是,数据应该只是数据 - 不建议观察具有自己状态行为的对象。

快速验证:我console.log(this.selectedDate.get)添加到创建的小提琴 saurabh 中,记录的函数stringGetmoment.js中,然后我在我的项目中检查了vm.fooget,它是proxyGetter,它是reactiveGetter的包装器,两者都在vue.runtime.common.js中。

最新更新