Vue - 组件和Vue.extend

在初学 Vue 的时候,都是利用 cdn 的方式在一个页面中导入 vue.js 的库文件.

<script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>

 var app = new Vue({
    el: '#app',
    data: {
      title: '我的第一个vue组件'
    },
    methods: {},
    filters: {},
    directives: {},
    components: {},
  })

接着,肯定就学到了组件开发.

当然,也是在单页面中.

API文档告诉我们

  • Vue.component 用来注册全局组件.
  • 在某个组件内部使用 components:{} 来注册局部组件.
<global-vue-component></global-vue-component>

Vue.component('GlobalVueComponent',{
    template:`<div>{{title}}</div>`,
    data () {
      return {
        title: '我是使用 Vue.component 注册的全局组件'
      }
    }
})
<scope-vue-component></scope-vue-component>

let ScopeVueComponent = {
    template:`<div>{{title}}</div>`,
    data () {
      return {
        title: '我是使用 components 使用的局部组件'
      }
    }
  }
  
   var app = new Vue({
    el: '#app',
    data: {
      title: '我的第一个vue组件'
    },
    components: {
      ScopeVueComponent
    }
  })
image.png

在后来,学到了使用 vue-cli 搭配 .vue 模板的方式开发组件.

<template>
  <div>
    {{ title }}
  </div>
</template>

<script>
// 这是组件的数据和行为内容 vm & model
export default { 
  name:'vue-template-mode-component',
  data () {
    return {
      title: '这是.vue组件的模板开发模式'
    }
  }
}
</script>

好了,三种组件创建的方式说完了,且在各自的环境里都能够正常的运作.


Vue.extend 方法.

Vue 开发中,所有的组件本质上都是由一个继承自 Vue 的构造函数创建的.

比如在注册局部组件时.

const TodoListComp = {
    template:`<div><h1>{{ title }}</h1></div>`,
    data () {
      return {
        title:'用 object 搭配 components 的方式去创建局部组件'
      }
    }
}


const app = new Vue({
    el:'#app',
    components: {
        TodoListComp,        
    }
})


<todo-list-comp></todo-list-comp>

从视觉上,我们看到 TodoListComp 只是一个普通的 Object 对象.

直接赋值给了其他组件的components属性里.

然后,这个组件就成为了一个局部组件,并可以在注册了当前组件的内部去使用了.

那它在内部做了什么,导致这个普通的对象最后可以被当成是一个正常的组件来使用呢?

比如,普通对象上都没有$el 之类的属性.丢给components之后,就啥都有了.

其实,在Vue内部,所有的组件都是通过 Vue.extend() 函数来构建的.
这个函数接收一个满足组件定义的object对象(比如,data,watch,computed,methods等).
最终返回一个构建此组件的构造函数(VueComponent)

使用Vue.extend(options)会根据传入的options创建一个VueComponent的组件构造函数并返回.

const TodoList = Vue.extend({
    template:`<div><h1>{{ title }}</h1></div>`,
    data () {
      return {
        title:'Vue.extend 创建的组件构造函数,使用new的方式创建并挂载'
      }
    }
  })

  console.log(TodoList)
  
image.png

既然使用 Vue.extend 会返回一个组件的构造函数.

那么我们就可以使用 new 这个返回的构造函数

并手动的 mount 并替换某个 dom 节点(就和 new App() 一样)

<!--用TodoListComp构造出来的组件实例对象替换这个节点-->
<div id="todolistcomp"></div>
const comp = new TodoList() // 这不不用传options,是因为在 Vue.extend(options) 的时候已经传递了.
comp.$mount('#todolistcomp') // 既然是创建出来的组件,继承自 Vue.prototype 所以就会有$mount 函数.
image.png

.vue 模板文件开发.

前面,我么已经知道了,所有的vue组件,不管是全局的还是局部的.

都是利用 Vue.extend 方法构建并返回出一个继承自 Vue 的组件构造函数.

这个函数接受一个满足了 Vue 组件属性项的普通的 Object 对象.

在.vue模板文件开发中,也不例外.

我们可以看看,在书写.vue模板文件时,我们到底在写什么?

<template>
  <div>
    <h2>{{title}}</h2>  
    <p v-html="content"></p>
  </div>
</template>
<script>
export default {
  name:'about-vue-template',
  data() {
    return {
      title:"我是.vue模板创建的组件",
      content:"使用.vue模板文件创建的组件本质上是vue-loader将这个文件编译成一个普通的不能在普通的obj对象.[template]xxxxx[/template]就被定义成对象的<b color='red'>.template属性.</b> 其他属性同理<ul><li>data</li><li>watch</li><li>methods</li><li>computed</li></ul>"
    }
  },
  directives:{},
  computed: {},
  methods: {},
  filters: {}
}

我们写的是一个 .vue 文件.

并按照 <template></template> <script></script> (这里不关注<style></style>节点) 的格式编写 .vue 文件.

把它整合起来来看.

  • template 就有点像我们在定普通组件options对象时的 template属性.
  • data 就是options对象的data
  • methods 就是options对象的methods
  • computed 就是options对象的computed
  • filters 就是 options 对象的 filters

等价于

const normalObjectOptions = {
    template: 
    data:
    methods:
    computed:
    filters:
}

好了,继续回到 .vue 模板开发文件中.

在另外一个组件中,使用此组件时,我们会 import xxx from xxx.vue 并搭配 components:{ xxxx }

.vue 会被 webpack 中配置的 vue-loader 处理.这是我们已知的.

结合上述的判断,vue-loader 仅仅只是把 .vue 文件编译成了一个 vue.extend(options) 创建组件所需要的 options 普通对象而已.

// 导入一个.vue文件的模板
import AboutVueComp from './components/about' 
// 查看实际导出的数据
console.log('AboutVueComp',AboutVueComp)
image.png

既然 vue-loader 仅仅,只是把 .vue 模板文件编译成了一个 options 普通对象.

那么我们可以手动的使用 Vue.extends(options) 来获得这个组件对象的构造函数.

const AboutVueCompConstructor = Vue.extend(AboutVueComp)
console.log('AboutVueCompConstructor',AboutVueCompConstructor)
image.png

拿到此组件的构造函数,我们就可以在 组件 mounted 的时候,通过 new 的方式,挂在到 html 上了. (而无需去注册到 components,成为一个局部组件.直接把它当成一个自己熟悉的不能在熟悉的构造函数调用即可.)

// 导入一个.vue文件的模板
import AboutVueComp from './components/about' 
// 查看实际导出的数据
console.log('AboutVueComp',AboutVueComp) // object 
const AboutVueCompConstructor = Vue.extend(AboutVueComp)
console.log('AboutVueCompConstructor',AboutVueCompConstructor)


 mounted() {
    new AboutVueCompConstructor().$mount('#aboutvuecompcontstrutor')
  },  
image.png

使用.js文件的方式使用.vue组件.

既然我们已经知道:

  • .vue模板文件本质上会被vue-loader 编译成了一个普通的不能在普通的 object 对象.
  • 这个对象里包含了 vue 组件需要的一些数据和方法.
  • 然后接着使用 Vue.extend() 接受这个普通的不能在普通的 object 对象.
  • 并返回一个创建此对象的构造函数f VueComponent(options){ this._init(options) }.
  • 最后,我们就可以使用 new 构造函数的方式来创建此组件.

我们完全可以直接使用 .js 文件的方式来创建 vue 组件,进而省略 .vue & vue-loader 这个执行的步骤.

import Vue from 'vue'

export default Vue.extend({
  name: 'js-comp-constructor',
  template: `<div @click="hanlderClick">{{title}}</div>`,
  data () {
    return {
      title: "我是通过.js文件创建出来的组件"
    }
  },
  methods: {
    hanlderClick () { 
      console.log('我是js模式组件提供的click方法')
    }
  },
  mounted () { 
    this.$el.style.width = '300px'
    this.$el.style.height = '200px'
    this.$el.style.backgroundColor = 'red'
    this.$el.style.margin ='0 auto'
    this.$el.style.lineHeight = '200px'
    this.$el.style.color = '#fff'
    this.$el.style.textAlign = 'center'
  }
})

.js文件到处一个vue组件的构造函数.

在另外一个组件里

import JsCompConstructor from './components/js-comp-constructor'

注册这个组件:

components: {
    JsCompConstructor
}


使用这个组件
 <JsCompConstructor></JsCompConstructor>
image.png

通过这样的原理,我们完全可以在HTML页面的任意地方,任意位置,任意的挂在我们自己的组件.并不一定必须使用.vue声明式组件的语法.


最后总结

  • 在 vue 开发中,组件是很常见的.
  • 组件分局部组件全局组件
  • 组件的本质是通过 Vue.extend(options)来构建出一个组件的构造函数.
  • 组件对象 options 写法有两种.
    • .vue 模板格式.
      • 搭配 vue-loader 就编译成一个普通的 options 对象.
    • .js 格式.
      • 提供一个满足组件options的对象.
      • 通过 Vue.extend() 拿到组件构造函数
      • 我们就可以很自由的在任意时间,任意地方去挂载我们的组件了.
image.png

[码云地址]

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 回忆 首先,render函数中手写h=>h(app),new Vue()实例初始化init()和原来一样。$mou...
    LoveBugs_King阅读 2,206评论 1 2
  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 4,989评论 0 29
  • Vue不支持IE8以及以下版本。 想要使用Vue的话可以通过直接下载vue.js,放置到项目中写好路径就可以,或者...
    酥枫阅读 591评论 0 0
  • PS:转载请注明出处作者: TigerChain地址: https://www.jianshu.com/p/8de...
    TigerChain阅读 4,625评论 1 53
  • 第一节 vue:读音: v-u-eview vue和angular区别?vue——简单、易学指令以 v-xxx一片...
    黑色的五叶草阅读 1,091评论 0 1