wangbinyq / personal-wiki

2 stars 0 forks source link

vue.js #8

Open wangbinyq opened 8 years ago

wangbinyq commented 8 years ago

Hello world

html

<div id="app">
    {{ message }}
</div>

js

var app = new Vue({
    el: '#app',
    data: {
        message: 'Hello world!',
    },
});
wangbinyq commented 8 years ago

Todo

html

<div id="app">
    <input v-model="newtodo" v-on:keyup.enter="addTodo"></input>
    <ul>
        <li v-for="todo in todos">
            <span>{{ todo }}</span><button v-on:click="remove($index)">X</button>
        </li>
    </ul>
</div>  
</div>

js

var vue = new Vue({
    el: '#app',
    data: {
        newtodo: '',
        todos: ['Add som todos'],
    },
    methods: {
        remove: function(index) {
            this.todos.splice(index, 1);
        },
        addTodo: function() {
            var text = this.newtodo.trim();
            if(text) {
                this.todos.push(this.newtodo);
                this.newtodo = '';
            }
        },
    },
});
wangbinyq commented 8 years ago

指令

wangbinyq commented 8 years ago

Vue实例

构造器

  1. 通过 new Vue 创建 ViewMode l实例
  2. 通过 Vue.extend 创建选项预定义并且可复用的组件构造器, (自定义组件/元素)

属性和方法

  1. 每个 Vue 实例都会代理其 data 对象里所有的属性
  2. $ 实例属性和方法

实例生命周期

lifecycle 1

wangbinyq commented 8 years ago

数据绑定

插值

文本

原始HTML

HTML特性

表达式

过滤器, 管道

指令

指令 (Directives) 是特殊的带有前缀 v- 的特性. 指令的值限定为绑定表达式, 因此上面提到的都适用于指令.

参数, 修饰符

v-dir:args.modifiers

缩写

<!-- 完整语法 -->
<a v-bind:href="url"></a>

<!-- 缩写 -->
<a :href="url"></a>

<!-- 完整语法 -->
<button v-bind:disabled="someDynamicCondition">Button</button>

<!-- 缩写 -->
<button :disabled="someDynamicCondition">Button</button>
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>

<!-- 缩写 -->
<a @click="doSomething"></a>
wangbinyq commented 8 years ago

计算属性

在模板中表达式非常便利,但是它们实际上只用于简单的操作。模板是为了描述视图的结构。在模板中放入太多的逻辑会让模板过重且难以维护。这就是为什么 Vue.js 将绑定表达式限制为一个表达式。如果需要多于一个表达式的逻辑,应当使用计算属性。

计算属性 vs. $watch

Vue.js 提供了一个方法 $watch,它用于观察 Vue 实例上的数据变动。当一些数据需要根据其它数据变化时, $watch 很诱人 —— 特别是如果你来自 AngularJS。不过,通常更好的办法是使用计算属性而不是一个命令式的 $watch 回调

wangbinyq commented 5 years ago

How reactive work

Dep

A dep is an observable that can have multiple directives subscribing to it.

Watcher

A watcher parses an expression, collects dependencies, and fires callback when the expression value changes. This is used for both the $watch() api and directives.

Like EventEmitter, addSub -> on, noitfy -> emit.

class Dep {
  ...
  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  ...
}

class Watcher {
  ...
  addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
        dep.addSub(this)  // register this watcher callback on the dep
      }
    }
  }
  ...
}
export default class Watcher {
  constructor (
    vm: Component,
    expOrFn: string | Function,  // dependencies
    cb: Function, // callback
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    ...
    this.getter = parse(expOrFn) // getter traverse dependecy
    this.value = this.get() // run get to collect dependencies
  }

  get () {
    pushTarget(this)  // set Dep.target = this
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  },

  update ();  // run callback
}

Observer

Observer class that is attached to each observed object. Once attached, the observer converts the target object's property keys into getter/setters that collect dependencies and dispatch updates.

  class Observer {
    dep: Dep  // store watchers of this observer
    constructor (value: any) {
      ...
      this.walk(value)
      ...
    }

    walk (obj: Object) {
      const keys = Object.keys(obj)
      for (let i = 0; i < keys.length; i++) {
        defineReactive(obj, keys[i])
      }
    }
  }

  function defineReactive(obj, key, val, customSetter, shallow) {
    const dep = new Dep()  // store property's watchers
    ...
    childOb = !shallow && observe(val) // observe on child property
    Object.defineProperty(obj, key, {
      get: function reactiveGetter () {
        const value = getter ? getter.call(obj) : val // get value from predefined getter or provided value.
        if (Dep.target) {  // `Dep.target` is depend on this property
          dep.depend()  // add `dep` to `Dep.target`'s deps
          if (childOb) {   // if this property value is obj or array, then property is depend on child
            childOb.dep.depend()
            if (Array.isArray(value)) {
              dependArray(value)
            }
          }
        }
        return value
      },
      set: function reactiveSetter (newVal) {
        const value = getter ? getter.call(obj) : val 
        /* eslint-disable no-self-compare */
        if (newVal === value || (newVal !== newVal && value !== value)) {
          return
        }
        /* eslint-enable no-self-compare */
        if (process.env.NODE_ENV !== 'production' && customSetter) {
          customSetter()
        }
        if (setter) {
          setter.call(obj, newVal)
        } else {
          val = newVal // save new value
        }
        childOb = !shallow && observe(newVal)
        dep.notify()  // trigger update on dep's subs
      }
    })
   })
   ...
  }

Example