Vue.js - Study Note 1

Views: 2901
Wrote on April 12, 2020, 9:48 p.m.

Understand design patterns

Reference

  • Model -- Application's data

  • View -- User interface layer

  • Controller -- Logic of your application

MVC: Model-View-Controller

The primary objective of the MVC design pattern is separation of concerns to facilitate testability. The Model View Controller design pattern enables you to isolate the concerns and makes your application's code easier to test and maintain. In a typical MVC design, the request first arrives at the controller which binds the model with the corresponding view. In the MVC design pattern, the view and the controller makes use of strategy design and the view and the model are synchronized using the observer design. Hence, we may say that MVC is a compound pattern. The controller and the view are loosely coupled and one controller can be used by multiple views. The view subscribes to the changes in the model.

MVVM: Model-View-ViewModel

The Model - View - ViewModel (MVVM) is a variation of Martin Fowler's Presentation Model design pattern. The MVVM is a refinement of the popular MVC design and the ViewModel in MVVM is used to facilitation Presentation Separation. In the MVVM the logic is stored in the presenter and the view is completely isolated from the model. While the presenter isn't aware of the view, the view is aware of the presenter -- the presenter in MVVM is used to represent an abstract view of the user interface. A passive view implies that the view doesn't have any knowledge of the model. In the MVVM design pattern, the View is active and contains behaviors, events and data binding information. Note that the view in MVVM is not responsible for managing the state information -- the view is rather synchronized with the viewmodel. The viewmodel in MVVM is responsible for presentation separation and exposes methods and commands to manage the state of a view and manipulate the model.

Must know ES6 features

  • Hoisting
// Example 1 - Does not hoist
var x = 1;                 
console.log(x + " " + y);  // '1 undefined'
var y = 2;                 

// Example 2 - Hoists
var num1 = 3;                   // Declare and initialize num1
num2 = 4;                       // Initialize num2
console.log(num1 + " " + num2); //'3 4'
var num2;                       // Declare num2 for hoisting

// Example 3 - Hoists
a = 'Cran';              // Initialize a
b = 'berry';             // Initialize b
console.log(a + "" + b); // 'Cranberry'
var a, b;                // Declare both a & b for hoisting
  • Template strings
const name = 'Sarsie';
const age = 1;
const message = `My dog ${name} is ${age * 7} years old.'`;
console.log(message);
  • Destructuring
var a, b, rest;
[a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20

[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]

({ a, b } = { a: 10, b: 20 });
console.log(a); // 10
console.log(b); // 20

({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}
  • Classes & Extends
class person {
  constructor(name, age ) {
    this.name = name
    this.age = age
  }
}

class chinese extends person {
  constructor(name, age, language) {
    super() // use super() to pass the properties name and age on the current this 
    this.name = name
    this.language = language
  }
}
  • Functions & Arrow Functions
window.name = "global";

var person = {
    name: "jason",

    shout: function () {
        console.log("my name is ", this.name);
    },
    shout2: () => {
        console.log("my name is ", this.name);
    },
    // Shorter syntax
    shout3() {
        console.log("my name is ", this.name);
    }
};

person.shout();  // "jason"
person.shout2(); // "global"
person.shout3(); // "jason"
  • Singleton Pattern
var Singleton = {

    attribute1: "public attribute 1",

    method1: function() {
      console.log("This is public method1");
      // 不保险 若Singleton.method1作为一个事件监听器,那么this就会指向window
      console.log(this.attribute1);
      // 最保险
      console.log(Singleton.attribute1);
    }

};

// This is public method1
// public attribute 1
// public attribute 1
Singleton.method1();

Directives

v-text => innerText (raw HTML) v-html=>innerHTML (raw HTML) v-for (better provide an unique key attribute with the v-bind:key or :key) v-if / v-else-if / v-else (would not generate in DOM if not matching) v-show (genrate all but display set as none if not matching) v-on or @xxx (listen to DOM events and run some JavaScript when triggered) v-bind or :attributeName (used to bind the class, style, prop or object that contains attribute name-value pairs ) v-model (input, textarea, select) .lazy, .number, .trim (modifiers)

Computed properties are cached, only recompute when data property changes

<div id="app">
    {{name}}
    <hr>
    {{hobby}}
    <hr>
    {{obj}}
    <button @click="my_click">Click to change</button>
</div>
const app = new Vue({
        el: "#app",
        data: {
            name: "jiayu",
            hobby: ["Soccer", "Basketball", "Volleyball"],
            obj: {
                age: 32,
                face: null,
            }
        },
        methods: {
            my_click: function () {
                // Changes name
                this.name = "juying"

                // Changes the length of array 
                // can trigger watch
                this.hobby.push("Play with dog");

                // Changes value in array does not 
                // trigger watch, even deep watch
                // as memory spot is not allocated
                // That's one of Vue's flaws
                this.hobby[0] = "Read books";
                console.log(this.hobby);

                // trigger the watch with $set
                app.$set(this.hobby, 0, "Football");
            }
        },
        watch: {
            name: {
                handler: function (val, preVal) {
                    console.log(val);
                    console.log(preVal)
                }
            },
            hobby: {
                handler: function (val, preVal) {
                    console.log(val);
                    console.log(preVal);
                },
                // deep: true
            }
        }
    })

Access DOM with $refs

<template>
  <div>
    <input ref="inputBox" type="text">
    <Test1 ref="test1Ref"/>
    <Test2 ref="test2Ref"/>
    <h3>Pineapple Apple Pie</h3>
    <p>You have counted {{this.counter}} times</p>
    <input type="text" ref="input">
    <button @click="submit">Add 1 to counter</button>
  </div>
</template>
<script>
export default {
  name: 'Test',
  data(){
    return {
      counter: 0
    }
  },
  methods: {
    submit(){
      this.counter++;
      console.log(this.ref) // undefined
      console.log(this.$refs) // {inputBox: input, test1Ref: VueComponent, test2Ref: VueComponent, input: input}
      console.log(this.$refs.input) // <input type="text">
      console.log(this.$refs.input.value) // Pineapple Apple Pie, this is what you input in the input box
      console.log(this.$refs.input.baseURI) // http://localhost:63342/VueTutorial/14_refs%E5%B1%9E%E6%80%A7.html?_ijt=mjt0fq830f52nq5jsj09hojv6b
    }
  }
}
</script>

Custom Directive

Vue allows you to register customized directives by doing ```

Vue.directive("directiveName", function(el, binding){
    // el # the html element
    // binding # directive content
})

Example:

<div id="app" v-demo:foo.a.b="message">
Vue.directive('demo', {
    bind: function (el, binding, vnode) {
      var s = JSON.stringify
      el.innerHTML =
        'name: ' + s(binding.name) + '<br>' +
        'value: ' + s(binding.value) + '<br>' +
        'expression: ' + s(binding.expression) + '<br>' +
        'argument: ' + s(binding.arg) + '<br>' +
        'modifiers: ' + s(binding.modifiers) + '<br>' +
        'vnode keys: ' + Object.keys(vnode).join(', ')
    }
  })

  new Vue({
    el: '#app',
    data() {
      return {
        message: 'hello!',
      }
    },
  })

Result:

name: "demo"
value: "hello!"
expression: "message"
argument: "foo"
modifiers: {"a":true,"b":true}
vnode keys: tag, data, children, text, elm, ns, context, fnContext, fnOptions, fnScopeId, key, componentOptions, componentInstance, parent, raw, isStatic, isRootInsert, isComment, isCloned, isOnce, asyncFactory, asyncMeta, isAsyncPlaceholder

Components

Component Registration

  • Global Registration
Vue.component('componentName', { /* ... */ })
  • Local Registration
const app = new Vue({
    el: '#app',
    components:{
        'component-a': ComponentA,
        'component-b': ComponentB
    }
})
  • Child Component Registration
  • Register in parent components

Components Communication

  • Parent to children (pass down)
<div id="app">
    <ol>
    <todo-item v-for="item in sites" v-bind:todo="item"></todo-item>
    </ol>
</div>
// Child component
Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

new Vue({
  el: '#app',
  data: {
    sites: [
      { text: 'Runoob' },
      { text: 'Google' },
      { text: 'Taobao' }
    ]
  }
})
  • Child to parent (pass up) Child component to submit event with $emit('eventName, value'), and bind eventName to parent component
// Child
<template>
  <div class="hello">
    <input type="button" name="" id="" @click="chilCall()" value="child_to_parent" /> 
  </div>
</template>
<script>
 export default {
  name: 'hello',
  'methods': {
   chilCall(pars) {
    this.$emit('newNodeEvent', 'something from child component')
   }
  }
 }
</script> 
// Parent
<template>
  <div id="app">
    <hello @newNodeEvent="parentLisen" />
  </div>
</template>
<script>
 import hello from './components/Hello'
 export default {
  name: 'app',
  'components': {
   hello
  },
  methods: {
   parentLisen(evtValue) { 
    //evtValue is the value from the child component
    alert(evtValue)
   }
  }
 }
</script>
  • Between non parent and children components Define a new Vue instance as the bus(publish-subscribe pattern), bus.$emit for the sender and bus.$on for the receiver
<div id="app">
let bus = new Vue();

Vue.component('VTest2',{
    data(){
        return{
            msg:'1',
        }
    },
    methods:{
        clickHander(){
        }
    },
    template:`<div> I am test compunent 2, the receiver, I just received {{ msg }}</div>`,
    created(){
        // that = this;
        bus.$on('testData', (val)=>{
            this.msg = val;
            // console.log(that.msg);
        })
    },
});


Vue.component('VTest1',{
    data(){
        return{
            msg:"Message from VTest1",
        }
    },
    methods:{
        clickHander(){
            bus.$emit('testData', this.msg)
        }
    },
    template:`
    <div>
        I am test compunent 1, the sender
        <br>
        <button @click="clickHander">VTest1 is sending message</button>
    </div>
    `
});


let VHeader = {
    data(){
        return{}
    },
    template: `
    <div>
        I am the Header component
        <VTest1/> 
        <VTest2/>
    </div>`
};

let App = {
    data(){
        return{}
    },
    components:{
        VHeader,
    },
    template: `
    <div class="app">
        <VHeader/>
        I'm a child component
    </div>`
};


new Vue({
    el:'#app',
    data(){
        return{}
    },
    components:{
        App,
    },
    template: 
    `<div id="newApp">
        <App/>
    </div>`
})