Yifei Kong

Jun 10, 2017

thinking in vue

basic usage

The created vue instance will proxy its data member

<div id="app">
  <ol>
    <li v-for="company in companies">
      <a v-bind:href="company.link">{{ company.text }}</a>  // bind 更新参数
            <button v-on:click="reverseText">逆转消息</button>
    </li>
  </ol>
</div>

let vm = new Vue({
    el: '#app',
        data: {
            companies: [
                    {text: 'Google', link: 'http://google.com'},
                        {text: 'fackbook', link: 'http://facebook.com'},
                        {text: 'apple', link: 'http://apple.com'}
                ]
        },
        methods: {
            reverseText: function() {
            // 注意 this 绑定到了触发这个事件的元素内部
                    this.company.text = this.company.text.split('').reverse.join('');
                }
        }   
});

vm.companies.push({text: 'Amazon', link: 'http://amazon.com'});
// computed 属性可以绑定一个虚拟的属性到几个不同的属性上, 有点类似python的@property
// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...
# 适合用来做自动保存文档等工作
<script>
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!'
  },
  watch: {
    // 如果 question 发生改变,这个函数就会运行
    question: function (newQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.getAnswer()
    }
  },
  methods: {
    // _.debounce 是一个通过 lodash 限制操作频率的函数。
    // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
    // ajax 请求直到用户输入完毕才会发出
    // 学习更多关于 _.debounce function (and its cousin
    // _.throttle),参考:https://lodash.com/docs#debounce
    getAnswer: _.debounce(
      function () {
        if (this.question.indexOf('?') === -1) {
          this.answer = 'Questions usually contain a question mark. ;-)'
          return
        }
        this.answer = 'Thinking...'
        var vm = this
        axios.get('https://yesno.wtf/api')
          .then(function (response) {
            vm.answer = _.capitalize(response.data.answer)
          })
          .catch(function (error) {
            vm.answer = 'Error! Could not reach the API. ' + error
          })
      },
      // 这是我们为用户停止输入等待的毫秒数
      500
    )
  }
})
</script>

flow control

v-if

v-else 元素必须紧跟在 v-if 或者 v-else-if 元素的后面——否则它将不会被识别。v-else-if 也是.

可以使用template来包装多个元素:

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-for

v-for的基本语法如前所述, 另外还可以采用可选参数 key. <li v-for="(item, index) in items">.

v-for还可以遍历对象, <li v-for="value in obj">

v-for还可以直接遍历range, <li v-for="n in 10">

事件

在vue中, 绑定的事件如果需要参数, 可以使用

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

的形式, 其中 $event 指的是原声事件.

修饰符

vue中的事件绑定函数可以使用一些修饰符来指定一些附加的效果. 常用的有 .prevent, .stop, .self, .once 等.

像这样: <a v-on:click.stop="doThis"></a>

对于键盘时间, 还可以使用修饰符来指定键值: <input v-on:keyup.enter="submit">

mustache vs v-bind

mustache can only be used in textContent of an element, v-bind is used for attribute.

<a v-bind:href="url">{{ link_text }}</a>

shortcut

<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>

v-model 做双向绑定

use v-model to double bind data between input and js.

<div id="app-6">
  <p>{{ message }}</p>
  <input v-model="message">
</div>

var app6 = new Vue({
  el: '#app-6',
  data: {
    message: 'Hello Vue!'
  }
})

值得注意的是, v-model 本质上只是一个语法糖. <input v-model="something"> is just a syntax sugar to <input v-bind:value="something" v-on:input="something = $event.target.value">

text area

需要注意的是, textarea 时间上相当于一个input组件, 不能在testarea内部使用 {{value}} 的语法, 而应该使用 v-model

<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>

修饰符

就像事件一样, 也可以指定一些修饰符给 v-model, 常用的有 .trim

vue-component

vue 的component中三个重要的概念: props, events, slots.

其中props向下传递, 用于parent组件向child组件传递值. child组件对于props的访问只能是只读的. 在child组件中使用v-bind:var="var"来访问定义的props. 注意在组件中不能更改props, 如果需要更改他, 请把他赋值给其他变量, 或者使用 computed 属性.

如果把模板直接放到dom中会有一些标签渲染不出来, 建议放到

// 在vue中注册一个组件, 大多数传递给vue实例的参数都可以使用, 除了 data 必须是一个函数

Vue.component('todo-item', {
  // todo-item 组件现在接受一个"prop",类似于一个自定义属性. 这个属性名为 todo。
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>',
  data: function() {},
})

<div id="app-7">
  <ol>
    <!-- 现在我们为每个todo-item提供待办项对象    -->
    <!-- 待办项对象是变量,即其内容可以是动态的 -->
    <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
  </ol>
</div>

Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { text: '蔬菜' },
      { text: '奶酪' },
      { text: '随便其他什么人吃的东西' }
    ]
  }
})

事件

parent组件可以监听子组件的事件, 从而实现通信:

这个例子中, 子组件通过$emit()函数发送increment事件. parent组件通过监听子组件的increment事件, 从而获得子组件的消息.

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})

slots

slots vs props: slots 用于显示一大片的包含html代码的替换块, 而props 用于显示值, 有点类似 v-bind 和 {{}} 的区别.

Props 允许外部环境传递数据给组件 Events 允许从外部环境在组件内触发副作用 Slots 允许外部环境将额外的内容组合在组件中。

vue router

basic: 将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们。

<div id="app">
  <p>
    <router-link to="/user/foo">/user/foo</router-link>
    <router-link to="/user/bar">/user/bar</router-link>
  </p>
  <router-view></router-view>
</div>

<script>
const User = {
  template: `<div>User {{ $route.params.id }}</div>`
}

const router = new VueRouter({
  mode: 'history',  // 这样才能使用 html5 的history api
  routes: [
    { path: '/user/:id', component: User }
  ]
})

const app = new Vue({ router }).$mount('#app')
</script>

vue lifecycle

using in chrome extension

chrome does not allow eval and new Function() in extensions, vue relies on it. you need to use CSP version of vue or relax the restriction by chrome.

See also:

  • https://developer.chrome.com/extensions/contentSecurityPolicy#relaxing-eval
  • https://stackoverflow.com/questions/34615503/vue-js-in-chrome-extension
  • https://vuejs.org/v2/guide/installation.html#CSP-environments