VueJs学习随笔--模板语法

开始

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:

<div id="app">
    <p>{{ foo }}</p>
    <button v-on:click="foo = 'baz'">Change it</button>
</div>
<script>
    var obj = {
        foo: 'bar'
    }
    new Vue({
        el: '#app',
        data: obj
    })
</script>

上面代码成功的将 bar 字符串通过点击按钮修改为了 baz,修改 js 代码如下:

<script>
    var obj = {
        foo: 'bar'
    }

    Object.freeze(obj)  //阻止修改现有的属性
    
    new Vue({
        el: '#app',
        data: obj
    })
</script>

添加了 Object.freeze(obj) 后,对 obj 的数据修改不起作用了。

生命周期

周期阶段说明
new Vue()创建vue实例
int event & lifecycle开始初始化
beforeCreate组件刚被初始化, 组件属性计算之前, 如 data 属性等
init injections & reactivity通过依赖注入导入依赖项
created组件实例创建完成,属性已绑定,此时 DOM 还未生成
el 属性检查 vue 配置,即 new Vue() 里面的 el 是否存在,有就继续检查 template 项, 没有则等到手动绑定调用 vm.$mount()
template检查配置中的 template 项,如果没有 template 项填充被绑定区域,则被绑定区域的 el 对象的 outerHTML 作为被填充对象替换掉填充区域。
beforeMount模板编译、挂载之前
create vm.$el and replace “el” with it编译,并替换了被绑定元素
mounted编译、挂载
Before update组件更新之前
updated组件更新之后
destory当 vm.$destroy() 被调用,开始拆卸组件和监听器,生命周期终结

基于上面对生命周期的解释和理解,我们修改上面的 js 代码:

<script>
    var obj = {
        foo: 'bar'
    }

    new Vue({
        el: '#app',
        data: obj,
        created: function(){
            console.log('created');
        },
        beforeCreate: function(){
            console.log('beforeCreate');
        },
        beforeMount: function(){
            console.log('beforeMount');
        },
        mounted: function(){
            console.log('mounted');
        },
        beforeUpdate: function(){
            console.log('beforeUpdate');
        },
        updated: function(){
            console.log('updated');
        },
        beforeDestroy: function(){
            console.log('beforeDestroy');
        },
        destroyed: function(){
            console.log('destroyed');
        }
    })
</script>

执行结果:

beforeCreate
created
beforeMount
mounted

点击 Change it 按钮,会发现新增了两行输出:

beforeUpdate
updated

再次修改,调用 vm.$destroy() :

let vm = new Vue({
    //...
})
vm.$destroy();

你会发现如下两行输出:

beforeDestroy
destroyed

模板引擎

插入值

数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:

<p>{{ foo }}</p>

Vue 也支持 js 表达式绑定:

<p>{{ ok ? 'YES' : 'NO' }}</p>

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令:

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
<script>
    var obj = {
        rawHtml: '<span style="color:red;">This should be red</span>'
    }

    new Vue({
        el: '#app',
        data: obj
    })
</script>

指令

指令 (Directives) 是带有 v- 前缀的特殊特性。指令特性的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。

<p v-if="seen">现在你看到我了</p>

这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除

元素。

一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML 特性:

<a v-bind:href="url">...</a>

在这里 href 是参数,告知 v-bind 指令将该元素的 href 特性与表达式 url 的值绑定。

从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:

var obj = {
    attributeName: 'href'
}

<a v-bind:[attributeName]="url"> ... </a>

这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data 属性 attributeName,其值为 “href”,那么这个绑定将等价于 v-bind:href

修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()

<form v-on:submit.prevent="onSubmit">...</form>

缩写

Vue 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写:

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

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

<!-- 缩写 -->
<a @click="doSomething">...</a>

计算属性

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,对于任何复杂逻辑,你都应当使用计算属性。

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

Original message: “Hello”

Computed reversed message: “olleH”

上面用到的 js 中的方法说明:

  • split() 方法用于把一个字符串分割成字符串数组。
  • reverse() 方法用于颠倒数组中元素的顺序。
  • join() 方法用于把数组中的所有元素放入一个字符串。

Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。

来修改一下上面的 js 代码, 新增一句 vm.message = 'Goodbye'

<script>
    var vm = new Vue({
        el: '#example',
        data: {
            message: 'Hello'
        },
        computed: {
            // 计算属性的 getter
            reversedMessage: function () {
                // `this` 指向 vm 实例
                return this.message.split('').reverse().join('')
            }
        }
    })

    vm.message = 'Goodbye'
</script>

Original message: “Goodbye”

Computed reversed message: “eybdooG”

可以换一种方式(方法的形式)达到上面同样的效果,代码如下:

<div id="example">
    <p>Original message: "{{ message }}"</p>
-   <p>Computed reversed message: "{{ reversedMessage }}"</p>
+   <p>Computed reversed message: "{{ reversedMessage() }}"</p>
</div>
<script>
    var vm = new Vue({
        el: '#example',
        data: {
            message: 'Hello'
        },
-       computed: {
-           reversedMessage: function () {
-               return this.message.split('').reverse().join('')
-           }
-       }
+       methods: {
+           reversedMessage: function () {
+               return this.message.split('').reverse().join('')
+           }
+       }
    })

    vm.message = 'Goodbye'
</script>

但是我们建议使用 computed 的方式,因为计算属性是基于它们的依赖进行缓存的,也就是说在 message 没有改变的时候,多次调用 reversedMessage 都是从缓存中返回结果的。

有一种情况下,我们的 computed 涉及到多个数据变化,例如:

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
    el: '#example',
    data: {
        firstName: 'Foo',
        lastName: 'Bar'
    },
    computed: {
        fullName: function () {
            return this.firstName + ' ' + this.lastName
        }
    }
})

也就是说上面的 firstNamelastName 的变化都会引起 computed 的重新计算,这个时候我们有一种替代方案:

 var vm = new Vue({
    el: '#example',
    data: {
        firstName: 'Foo',
        lastName: 'Bar',
        fullName: 'Foo Bar'
    },
    watch: {
        firstName: function (val) {
            this.fullName = val + ' ' + this.lastName
        },
        lastName: function (val) {
            this.fullName = this.firstName + ' ' + val
        }
    }
})

Vue 提供了一种更通用的方式(watch)来观察和响应 Vue 实例上的数据变动:侦听属性。 上面的 firstNamelastName 的变化都会重新计算 fullName。但是你会发现 computed 的方式似乎更加友好一点,所以我们不能滥用 watch, 要在合适的地方用它。

当然,我们也可以给 computed 添加 setter:

var vm = new Vue({
    el: '#example',
    data: {
        firstName: 'Foo',
        lastName: 'Bar'
    },
    computed: {
        fullName: {
            get: function () {
                return this.firstName + ' ' + this.lastName
            },
            set: function (newValue) {
                var names = newValue.split(' ')
                this.firstName = names[0]
                this.lastName = names[names.length - 1]
            }
        }
    }
})