【问题标题】:Switching from Knockout to VueJs : missing concept从淘汰赛切换到 VueJs:缺少概念
【发布时间】:2019-09-09 01:39:47
【问题描述】:

我想将我组织的应用程序从 Knockout 切换到 Vue,但缺少一些概念或者我误解了 vueJs 的理念。所以我无法重现相同的行为。

根据我的阅读,VueJs 真正专注于组件。

每个组件管理都是自己的 DOM。

https://fr.vuejs.org/v2/guide/reactivity.html

https://fr.vuejs.org/v2/guide/list.html

...(我在开始输入键盘之前阅读了本指南页面的大部分内容:p)

这种行为对于需要共享的简单组件来说很好,但就我而言,这有点令人沮丧,因为我需要链接一些模块(请参阅最后关于上下文的问题)

我读过另一篇很棒的文章 (https://jes.al/2017/05/migrating-from-knockoutjs-to-vuejs/) 关于“淘汰到 Vuejs 的过渡”,但它缺乏如此具体的细节。

例如:它解释了“subscribe”(在 KO 中)可以被“watch”(在 Vue 中)替换,但没有解释“何时”我们可以应用这些观察者(请参阅最后的问题)

这是一个非常小的代码提取,可以总结我的大部分特定需求。

HTML

<div id="app">
 <div data-bind="text: title"></div>
 <i data-bind="visible: changeTracker.hasChanges">save your change dude !!</i>
 <div data-bind="with: model">
     <!-- the binging  "with" allow us to move context to model for the current node and prevent typing "model." each time (ex: model.prop1, modeld.pro2,) -->

     <label data-bind="text: name"></label>
     <input type="text" data-bind="value: firstName"/>

     <span data-bind="text: $parent.nameAndFirstName">
     <!-- "$parent" allow us to access vm context itself -->

     <input type="text" data-bind="value: city"/>
 </div> 
 <div>
    <button data-bind="click: changeTracker.undo, enabled: changeTracker.hasChanges"></button>
    <button data-bind="click: saveData, enabled: changeTracker.hasChanges"></button>
 </div>
</div>

Javascript

var changeTrackerVM_Factory = function(){
    var self = {};

    self.initialState = null; // no need to be ko.observable since it's used for internal manipulation only

    // flag that will tell the UI that modification exist
    self.hasChanges = ko.observable(false);

    self.init = function(modelToTrack){
        // some piece of code .... subscribe ... and so on
    }

    self.undo = function(){
         // some piece of code that revrt the object to it's initial state
    };

    return self;
};

var userVM_Factory = function(){
   var self = {};
   // data from-to database
   self.model = {
       id: "", //-- don't need to be observable since it's not rendered and not modifiable
       name: ko.observable(""),
       firstName: ko.observable(""),
       city: ko.observable("")
   };

   self.nameAndFirstName = ko.computed(function(){
     return self.model.name() + "-" + self.model.firstname();
   });

   // build the custom tracker module
   self.changeTracker = changeTrackerVM_Factory(); 

   self.init = function(){
       //some ajaxRequest
       $.ajax(....).then(function(data){
            // map recieved datas
            self.model.name(data.name);
            ......

            // apply custom observation AFTER data mapping
            self.model.city.subscribe(function(newvalue){
                 alert("yeaah !! YOU changed the city value");
            });

            // apply the "custom tracking module" used to show the state in the UI
            self.changeTracker.init(self.model);
       });
   }

   self.save = function(){
        // some piece of code to send to DATABASE
   }

   return self;
};

// initialise the relation between the UI and the VM
var userVM = userVM_Factory();
ko.applybinding(userVM , "#app");

如果我想将此代码转换为 vueJS 并且我有相同的行为,我需要回答:

  • 我怎样才能只跟踪“模型”的几个属性(我相信这是可能的,因为这样一个使用过的库会受到性能优化的影响)

  • 我如何才能在第一次初始化后才启动“监视”(并防止在开始时弹出“警报”)

  • 如您所见,我没有“他的 DOM 块的一个模块”例如:changeTracker.hasChanges 用于 DOM 的顶部和底部

  • 如果我使用组件仅通过它自己的 DOM 来管理“模型”.. 我如何在里面使用 $parent ???

【问题讨论】:

  • 在 Vue 中有一些组件,它们“管理它们的 DOM 部分”(无论是小块还是大块)。组件是可重复使用的 - 在同一页面上多次使用。但是,如果您希望一个组件在您的应用中代表相同的状态(例如,显示相同的值,管理相同的底层实体,无论您将其放置在模板中多少次以及放置在何处),那么您需要使用状态管理。如果您的应用程序不是那么复杂,那么事件总线就可以了,但我建议阅读 Vuex - Vue 的事实上的状态管理器。
  • 好的,我会更多地研究 Vuex ......所以我希望完成与我在 Knockout 中所做的相同的工作,我必须使用额外的库(vue + vuex)......我有兴趣学习新事物,但我必须先评估过渡的成本。
  • 您的简化示例不需要状态管理,但我想您的整个应用程序(更多视图/页面、功能和组件)需要一个状态管理工具。当你需要 Vuex 时就是这种情况。在开发过程中,您不会觉得它是一个单独的工具。
  • 在我的简化示例中,模块“changeTracker”比较对象的初始状态和“仅跟踪的属性”发生变化时,然后确定我们是否应该绘制“保存按钮”。 . 你是说我可以在没有 Vuex 的情况下实现同样的目标 .. 对吗?如果是这样,我将不得不确定如何构建依赖于另一个模块(模型)的模块(changeTracker),它只跟踪很少的属性(不需要跟踪“Id”,因为它不会改变)

标签: vue.js knockout.js


【解决方案1】:

您问的是一个相当繁重的问题(或多个问题,我应该说)。在基本层面上,这些答案应该就足够了 - 以及提供的示例,它涵盖了您的许多问题。如果您有任何问题,请告诉我。

如果您想跟踪某些属性,您可以使用computed 属性或watch 属性。 Computed properties vs Watched properties

如果您想跳过watch 属性中的第一次更改,则必须集成某种逻辑,例如设置bool 标志。 More on that here

有几种不同的方法可以在组件中使用“外部”js 文件——你可以只导入它并使用必要的函数等。或者你可以使用mixinMore on mixins here

子组件emit向父组件传递数据,父组件通过props向子组件传递数据。下面的示例显示了这一点。


编辑:您在询问 mixins 以及如何将“第三方”模块添加到组件中。This CodePen does 产生与原始模块相同的结果我在下面提供了,只是它使用了mixin。这演示了如何通过mixin..(或者我希望它至少可以)在任何组件中“做任何你想做的事”......


示例代码:

[CodePen mirror]

[CodePen using mixin]


/*****************************/
/* User VM Factory Component */
/*****************************/
const userVmFactoryComponent = {
  name: "userVmFactoryComponent",
  template: "#userVmFactoryComponent",
  props: {
    id: [String, Number],
    name: String,
    lastName: String,
    city: String,
  },
  data() {
    return {
      user: {
        id: "",
        name: "",
        lastName: "",
        city: "",
      }
    }
  },
  methods: {
    emitNameChanged(){
      this.$emit('name-changed', this.fullName);
    }
  },
  computed: {
    fullName() { // using fullName vs nameAndFirstName
      return this.user.name + " " + this.user.lastName;
    }
  },
  mounted(){
    this.user.id = this.id;
    this.user.name = this.name;
    this.user.lastName = this.lastName;
    this.user.city = this.city;
  }
}

/****************/
/* Main Vue App */
/****************/
new Vue({
  el: "#app",
  components: {
    userVmFactoryComponent,
  },
  data: {
    users: [],
  },
  methods: {
    handleNameChange(info) {
      alert("You changed the name! (alerted from parent) " + "'" + info + "'")
    }
  },
  mounted() {
    // pretend you're getting this data from an API
    let firstUser = {
      id: 100,
      name: 'John',
      lastName: 'Smith',
      city: 'Paris',
    };
    
    let secondUser = {
      id: 200,
      name: "Jane",
      lastName: "Doe",
      city: "New York",
    }
    
    this.users.push(firstUser);
    this.users.push(secondUser);
  }
})
.user-input {
  margin-bottom: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>


<!-- MAIN VUE APP -->
<div id="app">
  <div>
  <div>
    <div>Without Looping</div>
    <user-vm-factory-component 
      @name-changed="handleNameChange"
      :id="users[0].id" 
      :name="users[0].name" 
      :last-name="users[0].lastName" 
      :city="users[0].city"
    ></user-vm-factory-component>
    <br/>
    <user-vm-factory-component 
      @name-changed="handleNameChange"
      :id="users[1].id" 
      :name="users[1].name" 
      :last-name="users[1].lastName" 
      :city="users[1].city"
    ></user-vm-factory-component>
  </div>
    <br/><hr/><br/>
    <div>
      <div>With Looping</div>
      <div>
        <div v-for="(user, index) in users">
          <user-vm-factory-component
            @name-changed="handleNameChange"
            :id="user.id"
            :name="user.name"
            :last-name="user.lastName"
            :city="user.city"
          ></user-vm-factory-component>
          <br/>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- ===================================== -->
<!-- THIS SIMULATES userVmFactoryComponent -->
<!-- ===================================== -->
<script type="text/x-template" id="userVmFactoryComponent">
  <div>
    Id
    <div class="user-input">
      <input type="text" v-model="user.id" />
    </div>
    Name (alerts when changed)
    <div class="user-input">
      <input type="text" @input="emitNameChanged" v-model="user.name" />
    </div>
    LastName
    <div class="user-input">
      <input type="text" v-model="user.lastName" />
    </div>
    FullName
    <div class="user-input">
      <input type="text" v-model="fullName" />
    </div>
    City
    <div class="user-input">
      <input type="text" v-model="user.city" />
    </div>
  </div>
</script>

【讨论】:

  • 感谢你的好例子,我会把它保存在我的魔法盒子里:)。更严重.. 首先,我已经知道计算和观察,但我想说的是,Vue 似乎 track “数据”对象中的所有东西......而在淘汰赛中我指定 我要跟踪的内容(ko.observable 关键字)...所以我担心性能(当数据对象很大时)...对于第二点,我应该考虑一下一个人,很高兴你帮助了我......
  • 对于 3 点,我想要的意思是我的“changeTracker”模块本身没有 DOM,需要另一个对象来使用。使用这样的模块将允许父对象使用它的属性 hasChanges 来做一些事情(例如:显示一个文本来解释他需要保存的用户)。 ...无论如何,因为我给了我理解 Vuejs 概念的方向,所以我将解决方案作为解决方案。然后,我将尝试用另一种哲学以不同的方式实现相同的目标。谢谢
  • “Vue 似乎可以跟踪 'data' 中的所有内容”是什么意思?是什么让您说或认为他们“跟踪”数据中的所有内容?知道这将帮助我尽我所能回答你的问题。数据属性中不会“跟踪”任何内容 - 唯一会跟踪数据属性的情况是您使用 watch 属性观看它,或者它是 computed 属性的一部分。
  • 对于“changeTracker”模块 - 你可以简单地编写一个 JavaScript 模块(可以做任何你想做的事情),然后将它导入到你的组件中,并使用它的函数/常量/类/等。如果你想让事情井井有条,你可以将“changeTracker”写成mixin——这让你可以访问所有的 Vue 实例属性/属性(无论你想怎么称呼它们),而无需将它们绑定到特定部分DOM(也就是它本身没有 DOM,但你可以在任何你想要的组件中使用方法/数据/计算/监视)。你只需“混合”它。
  • @GuyBaillonRafart - 我已经用一个如何使用mixin 的示例更新了我的答案 - 希望这会有所帮助!
猜你喜欢
  • 2018-02-06
  • 2014-01-03
  • 2013-01-15
  • 2015-07-17
  • 1970-01-01
  • 2014-06-16
  • 1970-01-01
  • 2013-04-10
  • 1970-01-01
相关资源
最近更新 更多