Vue是一套Web前端框架,看了Vue的文档之后,觉得Vue有一些很有意思的地方,就把它记录下来。

Vue实例的data属性

看下面的例子:

// Our data object
var data = { a: 1 }

// The object is added to a Vue instance
var vm = new Vue({
  data: data
})

// Getting the property on the instance
// returns the one from the original data
vm.a == data.a // => true

// Setting the property on the instance
// also affects the original data
vm.a = 2
data.a // => 2

// ... and vice-versa
data.a = 3
vm.a // => 3

Vue的实例创建之后,会把入参对象中的data下面的所有属性拷到自己的户下,比如上面例子中data.a被拷贝到vm.a。不过更让人惊奇的是,data.a的值变化了之后,vm.a的值竟然也跟着变化。

然后Vue实例本身的属性和方法则是以$开头,例如:

vm.$data
vm.$el
vm.$watch(...)

Vue的Lifecycle钩子

Vue的Lifecycle钩子比React要多很多,每次执行一个操作之前和之后基本都会有一个钩子:

  • beforeCreated和created
  • beforeMount和mounted
  • beforeUpdate和updated
  • beforeDestroy和destroyed

Vue的模板语法

html内容需要使用rawHtml才能放到一个element里面

<p>Using v-html directive: <span v-html="rawHtml"></span></p>

对于v-bind, <div v-bind:id="dynamicId"></div>可以缩写为<div :id="dynamicId"></div>

对于v-on<a v-on:click="doSomething"></a>可以缩写为<a @click="doSomething"></a>

在模板里面只能写一行JavaScript表达式

Okay的

{{ message.split('').reverse().join('') }}

不Okay的

{{ if (ok) { return message } }}

双向数据绑定很简单,可以使用v-model

    <input v-model="question">

v-if用来在模板中做条件控制。因为Vue的指令不能做元素使用,也就是说没有<v-if>这个标签,所以必须把v-if放在<template>标签上,也就是这样<template v-if="ok">。当然,<template>本身最终不会生成HTML代码。

Vue的Component

Vue的Component非常像Web的自定义元素(Custom Elements,在标准Web Components Spec中有说明)

不过对于Vue来说,Component就是一个提供预制选项和模板的Vue实例的类,并且Component的data属性必须是一个函数,这样由Component生成Vue实例的时候,每个实例能有自己的data拷贝:

Vue.component('todo-item', {
  // The todo-item component now accepts a
  // "prop", which is like a custom attribute.
  // This prop is called todo.
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

可以在Vue实例的内部注册子Component

new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

上面的做法叫做Local Registration, 在Component Registration有解释。

Component的Props可以是Object

props的Component的输入参数,不仅是一个数组,也可以是一个object:

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object
}

扩展JS对象生成新的Component

可以通过Vue.extend来把一个普通JS对象转化成一个Component

var Comp = Vue.extend({
  props: ['msg'],
  template: '<div>{{ msg }}</div>'
})

var vm = new Comp({
  propsData: {
    msg: 'hello'
  }
})

这是一个很灵活生成Component的方法,可以随时随地操作,可以用来分解大型的Component。

Component的名字可以是驼峰状和骨肉相连状

HTML本身对标签的名字是大小写不敏感的。所以驼峰状的名字(例如: <MyComponentName>)有可能和HTML标签起冲突。 但是Vue支持另外一种命令方式,那就是骨肉相连状(Kebab),比如<my-component-name>

模板里面的CSS

Vue可以直接在模板里面绑定CSS,并且CSS可以是一个JavaScript对象:

<div v-bind:class="classObject"></div>

data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

Slot:联系子Component的脐带

<navigation-link>是一个Vue Component,它带的模板是下面这样的:

<a
  v-bind:href="url"
  class="nav-link"
>
  <slot></slot>
</a>

可以看到模板中有<slot></slot>这个槽。这个的作用是预留位置,然后在Component实例化的时候,把Component包含的内容放置到这个槽里面。

我们来实例化这个Component:

<navigation-link url="/profile">
  <font-awesome-icon name="user"></font-awesome-icon>
  Your Profile
</navigation-link>

上面的效果等同于:

<a
  v-bind:href="url"
  class="nav-link"
>
  <font-awesome-icon name="user"></font-awesome-icon>
  Your Profile
</a>

你还可以给Slot取名字,参考Named Slots

反刍的Scoped Slots

利用Scoped Slots,我们可以让一个Component把肚子的参数吐出来,供给Component的调用者使用,这个过程有点像反刍。

首先我们有一个叫<todo-list>的Component,里面包含若干条todo数据,并且使用以下模板:

<ul>
  <li
    v-for="todo in todos"
    v-bind:key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

如果我们在调用<todo-list>的时候想使用里面的todo数据,比如todo.text,可以这样操作:

<todo-list v-bind:todos="todos">
  <!-- Define `slotProps` as the name of our slot scope -->
  <template slot-scope="slotProps">
    <!-- Define a custom template for todo items, using -->
    <!-- `slotProps` to customize each todo.            -->
    <span v-if="slotProps.todo.isComplete">✓</span>
    {{ slotProps.todo.text }}
  </template>
</todo-list>

通过指定slot-scope="slotProps"``slotProps属性就像<todo-list>张开的口,可以获取到其内部数据集合,比如slotProps.todo.text

如果不需要访问所有的内部数据,我们可以只取slotProps里面的部分属性:

<todo-list v-bind:todos="todos">
  <template slot-scope="{ todo }">
    <span v-if="todo.isComplete">✓</span>
    {{ todo.text }}
  </template>
</todo-list>

注意,上面的例子中slot-scope="{ todo }"只从之前的slotProps里面选取了todo属性,而不是之前slotProps那样获取所有的属性。

scoped slot可以用来制作Renderless Component