扩展 VueJS 组件 – Vue.js Developers – 中级技巧

2017-08-16 13:46:23来源:oschina作者:CodeMyLife人点击

分享


> 本文转载自:[众成翻译](http://www.zcfy.cc)
> 译者:[Mactaivsh](http://www.zcfy.cc/@Mactaivsh) 博主本人
> 链接:[http://www.zcfy.cc/article/3974](http://www.zcfy.cc/article/3974)
> 原文:[https://medium.com/js-dojo/extending-vuejs-components-42fefefc688b](https://medium.com/js-dojo/extending-vuejs-components-42fefefc688b)
[![Go to the profile of Anthony Gore](/2014th7cj/d/file/p/20170816/klg4sj2udmy.jpg)](https://medium.com/@anthonygore?source=post_header_lockup)
作者:[Anthony Gore](https://medium.com/@anthonygore?source=post_header_lockup)
* * *
# 扩展 Vue 组件
![](/2014th7cj/d/file/p/20170816/fas1hvyyg3i.jpg)
你是否在开发基于 Vue 的 app 时使用过具有相同属性甚至具有相同的 template 结构的组件?
这时,创建一个具有通用属性和 HTML 结构的 “基组件” 并以此扩展并创建出其 “子组件” 是一种比较好的做法。这种结构可以帮助你在你的代码中应用 DRY 原则(Don’t Repeat Yourself)并增强代码的可读性同时减少 bug 的产生。
Vue 提供了实现组件继承的功能,但你也需要为你的子组件添加一些特有的属性。
注: 这篇文章发表于 2017/06/11,详情见[_原文连接_](http://vuejsdevelopers.com/2017/06/11/vue-js-extending-components/?jsdojo_id=medium_evc)
### 示例: 调查问卷
这里有一个使用 Vue.js 制作的调查问卷:
![](/2014th7cj/d/file/p/20170816/qitdhp0c0tb.png)
你可以很明显地注意到每一个问题都有一个与之相关的 input 类型:
1.文本输入
2.下拉选择
3.单选按钮
良好的工程结构应该将这些问题(不同的 input 类型)构建成不同的可复用的组件,根据上面的类型我给他们起了对应的名字:
1.`SurveyInputText`
2.`SurveyInputSelect`
3.`SurveyInputRadio`
每一种问题或者说 input 类型对应一个不同的组件是有道理的,因为它们都需要有自己的专属标记(例如:`vs`) 并且它们都拥有特定的 props 属性, methods 属性等等... 然而,这些组件又有很多相同的地方:
* 他们都有一个与之对应的问题
* 他们都需要表单验证
* 他们都需要错误状态的提示
等等。所以我认为这是扩展组件的一个最好的应用例子。
### 基组件
每一个子组件都会继承一个名为`SurveyInputBase`的单文件根组件,注意一下几点:
* `question` 这个 prop 属性是每个扩展组件都通用的属性,我们也应该添加更多的通用属性,但是针对目前这个简单的例子,我们先只专注这一个属性。
* 我们需要想办法把这个属性复制到任何从基组件扩展的组件上。
*我们需要想办法将不同输入表单的 HTML 结构插入到 template 标签之中。
``` html


```
### 继承组件选项
暂时先忘掉 template 标签吧,我们如何让每个子组件都继承基组件的 props 属性呢?每一个子组件既需要`question`这个 prop 属性,同时也要拥有自己私有的 props 属性:
![](/2014th7cj/d/file/p/20170816/ec0dobvepnw.png)
我们可以通过引入这个基组件并且在子组件的`extends`选项里指向它来达到这个目的:
```


```
打开 Vue Devtools, 我们可以看到通过 `extends` 选项子组件的确获得了来自基组件的 props 属性
![](/2014th7cj/d/file/p/20170816/ixa1ymznvie.png)
### 合并策略
你可能会好奇,为什么子组件能继承 `question` 这个 prop 属性而不是覆盖它。实际上`extends` 选项执行了一种合并策略,它会确保合并正确地执行。比如,如果 props 属性拥有不同的属性名, 很明显他们都会被包含在内,但是如果他们拥有相同的属性名, Vue 只会保留子组件的响应属性。
这个合并策略同样应用于其他选项,比如 methods,计算属性以及生命周期钩子。 更多关于合并策略的内容可查看 [vue 官方文档](https://vuejs.org/v2/guide/mixins.html#Option-Merging) 不过如果你有需要,也可以制定自己的合并策略。
> 注意: 另外一种方式在组件里使用 `mixin` 属性。对于这个例子,我更倾向于使用 `extends`, 尽管如此,两种方式使用的合并策略有轻微的不同,使用`extends`使得组件自身的选项会比要扩展的基组件具有更高的优先级。
### 扩展模板
扩展组件的选项看起来很简单 — 那么模板(template)呢?
之前的合并策略并不适用于`template`选项. 我们要么完全继承自基组件, 要么重新定义`template`选项并覆盖它,那我们如何合并它呢?
我的解决方式是使用 [Pug](https://pugjs.org/) 预处理器. 它带有`include`和`extends`选项,所以它似乎很适合这种设计模式
### 基组件
首先,让我们把基组件的 template 转换成 Pug 的语法:
``` html

```
注意以下几点:
*我们添加 `lang="pug"`到 template 标签是为了让 _vue-loader_ 使用 pug 语法来处理我们的模板 (所以, 不要忘记添加 Pug 模块到你的工程目录,使用 `npm i --save-dev pug` 命令)
* 我们使用 `block input` 来声明一个子组件可以扩展的块
所以这里就是比较麻烦的地方, 如果我们想要子组件去扩展这个模板,我们需要把它放进一个单独的文件里。
``` jade
div.survey-base
h4 {{ question }}
block input
```
然后我们使用 `include` 引入这个文件,然后我们的基组件仍然是一个独立可用的组件。
``` html


```
有点遗憾的是,这么做似乎违背了单文件组件的设定, 但是对于我们的例子,我认为是值得的. 或许你可以自定一个 webpack loader 来避免这个情况。
### 子组件
现在让我们来转换子组件的 template 为 Pug 语法:
```


```
子组件使用了 Pug 的 `extends` 特性,因而包含了基组件的模板并且在`input`块中输出任何自定义模板结构 (类似于_slots_).
这是子组件有效地扩展了基组件并转化为常规的 HTML 后的样子:
``` html

{{ question }}




```
### 总结
运用上述策略我们可以构建出另外拥有私有属性和响应 HTML 结构的两个子组件 `SurveyInputSelect` 和 `SurveyInputRadio`
如果之后我们在项目的主模板里引用它们:
```
question="1. What is your name?"
placeholder="e.g. John Smith"
>
question="2. What is your favorite UI framework?"
:options="['React', 'Vue.js', 'Angular']"
>
question="3. What backend do you use?"
:options="['Node.js', 'Laravel', 'Ruby']"
name="backend"
>

```
最终会被渲染成:
``` html

1. What is your name?





2. What is your favorite UI framework?





3. What backend do you use?


Node.js

Laravel

Ruby


```
> 注意: 我们也可以用 `SurveyInputBase` 组件来示范, 因为它也可以独立运行, 但是在这个例子中是没有必要的。我认为这是要说明的重要的一点。

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台