开发一个 vue component 的套路是什么

开发一个 vue component 的套路是什么_第1张图片
from @unsplash

在早版本的 vue 的文档中,有说 vue 的作用是一个 MVVM 开发模式中的 VM,即 view model。现在没有这回事了,但是在 instance 部分的文档中,依然有说 vue 的设计参考了这种模式。

而现在很多网络服务的框架都采用 MV* 框架(*请自己填补,比如 Rails 的MVC),我们或许可以从这里入手来理解 vue。

如果你已经读过 vue 文档中的 introduction,但是没有理清 vue component 各部分,那么我们可以一起来梳理一下。

先说一个 Vue component 在整个项目中的地位

通常,一个 Vue component 会被挂在一个 components 组成的树上,而这个 component 通常又会引用很多的 child components。

开发一个 vue component 的套路是什么_第2张图片
vue components tree

所以,我们如果写一个 component,最终表现出来的会是 components tree 中的一个 subtree。基于 vue router 这个工具,如果你点击了一个菜单,到达了某个路径,他会自动帮你加载一个 component(subtree)到树上的某个位置。

一个 component 中的 model

一个 vue component (或者说 vue instance,两者只有细微差别)可以看成是 view 和 model 的结合,或者更直接一点:template + model。model 的属性(props)可以在 view 中引用时赋值,model 的数据(data)可以被 view 取用。还有其他一些 view 和 model 的调用关系。我们列个表:

Model View
data template 中可使用的数据
props template 中可使用的数据 / 引用 template 时赋值的属性
methods template 和 model 的其他地方可用的实例方法
computed data 改变或查询时的回调,形成 template 中可使用的数据
watch 针对 data 中的字段做更通用的回调,不像 computed 形成一个 cache 的字段

解释一下:

  1. data 只是 template 中会用到的数据中的一部分,可以通过一些事件来触发这些 data 的改变
  2. props 定义的是引用 template 时,在别的 component 中赋值进来的属性
  3. methods 是 model 的其他部分要用到的方法,或者 template 中发生的事件会触发的方法
  4. computed 是一种回调(或者叫监听),是当 data 中的一些数据改变时,computed 中的字段会自动更新
  5. watch 也是针对 data 中的数据的一些回调(监听),是当 data 中的数据改变时,watch 中的方法自动执行

而当 model 中的某些数据改变(主要是 data 和 computed),view 会自动重新渲染,这是 Vue 帮我们做的事。

以上这些属性,都是写在 component 的 model 中的,比如:

export default {
    name: "MyComponent",
    data() {
        return {
            boy: "LiLei",
            girl: "HanMeimei"
        }
    },
    props: ["verb"],
    methods: {
        reverse() {
            [this.boy, this.girl] = [this.girl, this.body];
        }
    },
    computed: {
        sentence() {
            return ${this.boy} ${this.verb} ${this.girl}!
        }
    },
    watch: {
        boy(val, oldVal) {
            console.log(previous boy ${oldVal}, new boy ${val})
        }
    }
}

而 template 可能是这样的:


这两部分东西 template + model 被写在同一个 vue 文件中,比如叫 MyComponent.vue

如果要引用这个 template,就需要给我们定义的 props 赋值,比如这样:


model 部分的内容也就这么多了,还有一些关于 component 的生命周期回调也可以写在model里,不过这个我们以后再说。

剩下的就都是关于 view,或者说 template 的内容了。

template 的属性

我们上面说的是 template 可以取用 model 的数据和方法,包括 data,computed, methods。

template 中,很多 child components 会去访问这些数据,利用这些数据给自己的属性赋值。而 template 中可用属性有为数不多的几种来源,我们可以大致列这样一张表格:

来源 举例
props 字段定义的
html 中默认
vue 中定义的特殊属性
逻辑属性

前两种都比较好理解,第三种需要看看文档中指出的 6 种特殊属性,我们后面再细说。

最后一种,在 vue 文档中属于 vue 指令 的部分,但我们这些 vue 指令中,有很多可以看作是 component 的逻辑属性,包括 v-if:判断这个 component 是否需要生成,v-for :循环产生 component 等等

另外,给属性赋值时,需要区分赋值的是一个常量,还是一个 javascript 对象,或者是一个需要计算的 javascript 表达式。用文档的话说,就是要区分 static 和 dynamic:

  1. 如果赋值的是一个常量(constant),那么按照上面例子中的语法,就能完成赋值:
  2. 但如果要赋值的是 js 对象,或者是一个需要计算的 js 表达式,那么就需要用 v-bind,比如:,其中 v-bind 的部分可简写为 :verb="randomVerb"(也就是说用一个冒号,代替整个v-bind:

template 中的事件

template 事件监听,声明起来很简单:


简写的话,会是:


也就是说把整个v-on:替换为@

我一开始以为这和 html 中的 onclick 是一样的。但如果真的是一样的,就没法解释这样的语句:


export default {
    methods: {
        changeTo(name) {
            this.boy = name;
        }
    }
})

html 中的 onclick 只能赋值一个 function 对象,而 vue template 中,click事件竟然可以接受一个表达式,而且这个表达式计算的结果也不是一个 function 啊。根据 vue 文档(关于 v-on 的部分),@click="changeTo('Harry')"这部分其实是另外一种形式的简写:v-on:click="changeTo('Harry', $event)"。也就是说:changeTo('Harry')在被 vue 这个框架转译的时候,最后一个参数会是被 vue 整进来的 $event,一个 DOM event对象。当然,你可以不使用这个参数,甚至在声明方法时没有这个参数的位置,但这个 $event 就在那里,是 Vue 这个框架整合进来的。

最后还有一种情况,就是@click="reverse",不加括号,看起来像是一个 function 对象了。但是,实际上vue对他的处理的方式也差不多:变为v-on:click=reverse($event)

以上,我们监听的是一个普通的html元素,所以可以监听的事件就是 dom 中默认的事件。但是我们监听的是一个 component,监听的事件就可以自定义,比如:


而在 my-component 中,只需要在某些方法中广播这个事件,比如:

export default {
    name: "MyComponent",
    methods: {
        example() {
            this.$emit("my-event", someArgs)
        }
    }
}

引用了这个 component 的父级 component 就可以监听这个事件。

template 中的 slot

一个 template 中,当然会有各种子级 component。但除了child 级别的 component,我们还可以定义一些 slot,比如这样:


  

Slot Example

The above is your slot!

它允许我们在调用这个 component 的 template 时,传入一部分自定义的 template,比如这样:


    

This is a paragraph I want to insert into the my component

also, an image

如此,这个 p 标签和 img 标签都会出现在以 my-component 为 root 的 subtree 上,甚至,这些 slot 也可以被 my-component 中的方法操作。

开发一个 vue component 的套路是什么_第3张图片
slot 的位置

另外,slot 还有 named slot 和 scoped slot 的特殊形式,但都是为了在引用 component 的时候传入自定义的元素。

最后,如果一个 component 不是全局的,但又想被别的 component 使用,那就需要做一下引入,比如这样:

import OtherComponent from './OtherComponent.vue'
export default {
    components: {
        OtherComponent
    }
}

如此这般,才能在 vue 的 template 中使用那些非全局的 component。(备注,这里 component 的标签名字,只需要和 components 对象的 key 相同即可,已验证)

model 的模块化

model 利用 mixins 引入别人写的一些 js 对象,让 component 的 model 引入一些公用的属性,避免代码的重复写作。

mixins 表明上看起来只是 model 的一个属性:

import {moduleA} from './mixins.js'
export default {
    mixins: [moduleA]
}

这个属性接受一个 js objects 做成的数组。而其中的每个 object 都包含一些 model 的属性,比如这样:

export const moduleA = {
    computed: {
        sentence() {
            return `${this.boy} ${this.verb} ${this.girl}!`
        }
    }
}

当这个模块(moduleA)被 mix in 到一个 component 的 model 中,那个 model 就自动有了这些属性。

以上,希望对大家帮助。


微信公众号:刘思宁

你可能感兴趣的:(开发一个 vue component 的套路是什么)