为什么vue.js不使用moment.js使用datepicker更新dom

时间:2016-12-04 14:13:03

标签: javascript vue.js

我在vue.js中编写一个(非常)简单的datepicker控件,使用moment.js进行格式化和变更日期。

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

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

以下是我的源代码:

App.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: {}
    }
});

的index.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>

2 个答案:

答案 0 :(得分:8)

您必须更改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/

以下是add/subtract库中moment的实施:

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是isn't immutable,这意味着当您在更改属性后调用时刻对象it returns the same object上的加/减功能时。

vue caveats上有一些reactivity,而Object.observe现在已经过时,它无法跟踪javascript对象是否已在内部更改,除非您克隆对象并创建一个你需要的新对象。

还有其他workarounds,包括使用frozen-moment库。

答案 1 :(得分:0)

这是因为moment和vue都依赖于定义对象的getter和setter,这导致在你的情况下一个覆盖另一个。 (因此更改引用的工作原理是因为vm.selectedDate.set被覆盖而vm.set不被覆盖)

从时刻doc开始:

  

Moment.js使用重载的getter和setter。

来自vue的doc

  

Vue将递归地将其属性转换为getter / setter以使其“被动”。该对象必须是明确的:忽略浏览器API对象和原型属性等本机对象。根据经验,数据应该只是数据 - 不建议观察具有自身状态行为的对象。

快速验证:我将console.log(this.selectedDate.get)添加到创建的小提琴saurabh中,stringGet中记录的函数为moment.js,然后我检查了vm.foo的{​​{1}}在我的项目中,它是get proxyGetter的{​​{1}}的包装器,reactiveGetter