从淘汰赛切换到VueJs:概念缺失

时间:2019-04-18 17:05:44

标签: vue.js knockout.js

我想将组织的应用程序从淘汰赛切换到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过渡”,但缺少如此具体的细节。

例如:它的解释是“订阅”(在KO中)可以由“观看”(在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并且我有相同的行为,我需要回答:

  • 我如何仅跟踪“模型”的几个属性(我敢肯定,因为这样使用过的libray会受到性能优化的关注)

  • 仅在首次初始化后如何才能启动“观看”(并防止在开始时出现“警告”弹出窗口)

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

  • 如果我仅使用组件自己的DOM来使用组件来管理“模型”,那么在???

  • 中如何使用$ parent?

1 个答案:

答案 0 :(得分:3)

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

如果要跟踪某些属性,则可以使用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>