1 单文件组件
在Vue应用中,单文件组件(Single File Components)是一种特殊的文件格式,以 .vue 为后缀,它允许开发者在一个文件中定义一个Vue组件的全部内容。
每个单文件组件由三个部分组成:template(模板)、script(脚本)和style(样式)。这些部分分别代表Vue组件的结构、逻辑和样式。
具体而言,一个单文件组件的构成如下:
- template部分:这部分使用HTML语法编写,包含了组件的HTML结构。
- script部分:这部分包含JavaScript代码,定义了组件的逻辑和行为。这部分也包含了一个特殊的
<template>
标签,用于包含模板内容。 - style部分:这部分包含CSS样式,用于定义组件的外观和样式。
单文件组件的优点在于:
- 将模板、脚本和样式封装在一个文件中,方便管理和维护。
- 可以在需要时导入到项目中,有助于代码的模块化和可重用性。
- 每个组件都有独立的.vue文件,方便在团队中进行分工协作。
总的来说,单文件组件是Vue框架中一个重要的特性,它帮助开发者更高效地开发和管理复杂的用户界面。
<template>
<div id="app">
<input v-model="name" type="text"/>
<p>Name: {{ name }}</p>
<p>Reversed Name: {{ reversedName }}</p>
</div>
</template>
<script>
export default {
data() {
return {
name: 'John Doe'
};
},
computed: {
reversedName: function () {
console.log('this => ', this)
return this.name.split(' ').reverse().join(' ');
}
},
mounted: function () {
this.$watch('name', function (newVal, oldVal) {
console.log('Name changed from ' + oldVal + ' to ' + newVal);
});
}
};
</script>
2 VueComponent
在Vue中,VueComponent是组件化开发的核心概念。VueComponent是一个构造函数,用于创建Vue组件。通过VueComponent,我们可以定义组件的属性、方法、生命周期钩子、计算属性等等。
在Vue中,组件是可复用的Vue实例,接受相同的选项对象(除了一些根特定的选项)并且可以拥有完全独立的作用域。组件可以扩展HTML元素,封装可重用的代码。在较高层面上,组件是自定义元素,通过Vue提供的全局API(vue-loader/vueify)进行注册,然后在HTML中就可以使用这些自定义元素了。
在VueComponent中,我们可以定义以下选项:
- data:组件的数据对象,用于存储组件的状态。
- props:组件的属性,用于接收来自父组件的数据。
- methods:组件的方法,用于实现组件的功能。
- computed:计算属性,根据组件的数据和属性计算得出的结果。
- watch:监听器,用于监听组件的数据和属性的变化,并在变化时触发回调函数。
- template:组件的模板,定义了组件的HTML结构。
- components:子组件对象,用于在组件中使用其他组件。
- lifecycle hooks:生命周期钩子函数,用于在组件的生命周期的不同阶段执行相应的逻辑。
通过VueComponent,我们可以将复杂的UI拆分成多个独立的、可复用的组件,提高代码的可维护性和可重用性。
在Vue组件的钩子函数中,this 关键字通常指向当前组件的实例对象。这个实例对象包含了组件的数据、方法、计算属性等信息。
例如,在 created 钩子函数中,this 指向当前组件的实例对象:
new Vue({
data: {
message: 'Hello, world!'
},
created: function() {
console.log(this.message); // 输出:Hello, world!
}
});
在上面的例子中,this.message 表示组件实例对象的 message 属性。
但是,在某些情况下,this 的指向可能会发生变化,比如在回调函数或者箭头函数中。在这些情况下,需要格外小心,确保 this 指向正确的对象。可以使用箭头函数或者使用 bind 方法来确保 this 的正确指向。
关于this指向:
- Vue.extend({}) 组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是VueComponent实例对象
- new Vue()配置中:
data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是Vue实例对象
3 VM
在Vue中,VM是ViewModel的缩写,是视图模型的意思,是连接View和Model的桥梁,负责业务逻辑处理,并对数据进行加工后交给View展示。通过Vue类创建的对象叫Vue实例化对象,这个对象就是MVVM模式中的VM层,模型通过它可以将数据绑定到页面上,视图可以通过它将数据映射到模型上。
4 既然vc和vm中的内容一样,为什么不能把vc和vm直接划上等号?
- vc是由VueComponent缔造的,vm是由Vue缔造的。这两个缔造的过程肯定不可能完全一样。
- 比如说在创建组件实例对象、传入配置项的时候,是不可以写el的,但是在创建vm的时候,配置对象中就可以写el。也就是说vm可以写自己为哪个容器服务,但是vc不能指定服务容器,只能跟着vm走。
- 再比如说,vc的数据配置项(data)就必须写成函数。可以说,它们俩身上99%的东西一样,但是有这1%就是不一样。就像是两个双胞胎,长得一模一样,但是不能说他们是同个人。
5 一个重要的内置关系- VueComponent.prototype.__proto__ === Vue.prototype
原型是Javascript中的继承的基础,JavaScript的继承就是基于原型的继承。每一个JS对象都可以获得自己的原型,通过原型可以共享函数对象和实例对象之间的属性和方法。
原型的出现,就是为了解决 构造函数 的缺点:
每一次new一个对象时,都会创建一份render()代码浪费资源。通过原型,我们提供了一个给对象添加函数的方法,不然构造函数只能给对象添加属性,不能合理的添加函数就太 LOW 了。
prototype
每一个函数天生自带一个成员 - prototype,是一个对象空间,即然每一个函数都有,构造函数也是函数,构造函数也有这个对象空间。这个 prototype 对象空间可以由函数名来访问:
function Person() {}
console.log(Person.prototype) // 是一个对象
即然是个对象,那么我们就可以向里面放入一些东西:
function Person() {}
Person.prototype.name = 'prototype'
Person.prototype.sayHi = function () {}
我们发现了一个叫做 prototype 的空间是和函数有关联的,并且可以向里面存储一些东西。
重点: 在函数的 prototype 里面存储的内容,不是给函数使用的,是给函数的每一个实例化对象使用的。
那实例化对象怎么使用?
每一个对象都天生自带一个成员,叫做__proto__
,是一个对象空间,即然每一个对象都有,实例化对象也是对象,那么每一个实例化对象也有这个成员。这个 __proto__
对象空间是给每一个对象使用的:
当你访问一个对象中的成员的时候,如果这个对象自己本身有这个成员,那么就会直接给你结果,如果没有,就会去 __proto__
这个对象空间里面找,里面有的话就给你结果。
那么这个 __proto__
又指向哪里呢?
这个对象是由哪个构造函数 new 出来的,那么这个对象的 __proto__
就指向这个构造函数的 prototype。
function Person() {}
var p1 = new Person()
console.log(p1.__proto__ === Person.prototype) // true
我们发现实例对象的 __proto__
和所属的构造函数的 prototype 是一个对象空间,我们可以通过构造函数名称来向 prototype 中添加成员。Person.prototype.name = 'prototype'
对象在访问的时候自己没有,可以自动去自己的 __proto__
中查找
我们可以把函数放在构造函数的 prototype 中,实例化对象访问的时候,自己没有,就会自动去 __proto__
中找。
function Person() {}
Person.prototype.sayHi = function () {
console.log('hello Person')
}
var p1 = new Person()
p1.sayHi()
1 p1 自己没有 sayHi 方法,就会去自己的
__proto__
中查找 p1.__proto__
就是 Person.prototype
我们又向 Person.prototype 中添加了 sayHi 方法 所以 p1.sayHi 就可以执行了
到这里,当我们实例化多个对象的时候,每个对象里面都没有方法,都是去所属的构造函数的 protottype 中查找,那么每一个对象使用的函数,其实都是同一个函数。
function Person() {}
Person.prototype.sayHi = function () {
console.log('hello')
}
var p1 = new Person()
var p2 = new Person()
console.log(p1.sayHi === p2.sayHi)
p1 是 Person 的一个实例
p2 是 Person 的一个实例
也就是说 p1.__proto__
和 p2.__proto__
指向的都是 Person.prototype
当 p1 去调用 sayHi 方法的时候是去 Person.prototype 中找
当 p2 去调用 sayHi 方法的时候是去 Person.prototype 中找
那么两个实例化对象就是找到的一个方法,也是执行的一个方法。
总结: 当我们写构造函数的时候,属性我们直接写在构造函数体内,方法我们写在原型上。