Vue.js - Study Note 1
Understand design patterns
-
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 andbus.$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>`
})