本帖最后由 小刀葛小伦 于 2019-5-30 10:45 编辑
你是否在开发基于 Vue 的 app 时使用过具有相同属性甚至具有相同的 template 结构的组件? 这时,创建一个具有通用属性和 HTML 结构的 “基组件” 并以此扩展并创建出其 “子组件” 是一种比较好的做法。这种结构可以帮助你在你的代码中应用 DRY 原则(Don’t Repeat Yourself)并增强代码的可读性同时减少 bug 的产生。 Vue 提供了实现组件继承的功能,但你也需要为你的子组件添加一些特有的属性。 基组件每一个子组件都会继承一个名为SurveyInputBase的单文件根组件,注意一下几点: question 这个 prop 属性是每个扩展组件都通用的属性,我们也应该添加更多的通用属性,但是针对目前这个简单的例子,我们先只专注这一个属性。 我们需要想办法把这个属性复制到任何从基组件扩展的组件上。 我们需要想办法将不同输入表单的 HTML 结构插入到 template 标签之中<template>
<template> <div class="survey-base"> <h4>{{ question }}</h4> </div> </template> <script> export default { props: ['question'], } </script> 继承组件选项暂时先忘掉 template 标签吧,我们如何让每个子组件都继承基组件的 props 属性呢?每一个子组件既需要question这个 prop 属性,同时也要拥有自己私有的 props 属性 <template> <input :placeholder="placeholder"> </template> <script> import SurveyInputBase from './SurveyInputBase.vue'; export default { extends: SurveyInputBase, props: ['placeholder'], } </script> 以上代码,通过 extends 选项子组件就获得了来自基组件的 props 属性合并策略你可能会好奇,为什么子组件能继承 question 这个 prop 属性而不是覆盖它。实际上extends选项执行了一种合并策略,它会确保合并正确地执行。比如,如果 props 属性拥有不同的属性名, 很明显他们都会被包含在内,但是如果他们拥有相同的属性名, Vue 只会保留子组件的响应属性。 这个合并策略同样应用于其他选项,比如 methods, 计算属性以及生命周期钩子。 更多关于合并策略的内容可查看 vue 官方文档 不过如果你有需要,也可以制定自己的合并策略 扩展模板扩展组件的选项看起来很简单 — 那么模板(template)呢? 之前的合并策略并不适用于template选项. 我们要么完全继承自基组件, 要么重新定义template选项并覆盖它,那我们如何合并它呢? 我的解决方式是使用 Pug 预处理器. 它带有include和extends选项,所以它似乎很适合这种设计模式 基组件首先,让我们把基组件的 template 转换成 Pug 的语法: <template lang="pug"> div.survey-base h4 block input </template> 注意以下几点: 我们添加 lang="pug"到 template 标签是为了让 vue-loader 使用 pug 语法来处理我们的模板 (所以, 不要忘记添加 Pug 模块到你的工程目录,使用 npm i --save-dev pug 命令) 我们使用 block input 来声明一个子组件可以扩展的块
所以这里就是比较麻烦的地方, 如果我们想要子组件去扩展这个模板,我们需要把它放进一个单独的文件里。 div.survey-bas h4 {{ question } block input 然后我们使用 include 引入这个文件,然后我们的基组件仍然是一个独立可用的组件。 <template lang="pug"> include SurveyInputBase.pug </template> <script> export default { props: [ 'question' ] } </script> 有点遗憾的是,这么做似乎违背了单文件组件的设定, 但是对于我们的例子,我认为是值得的. 或许你可以自定一个 webpack loader 来避免这个情况。 子组件现在让我们来转换子组件的 template 为 Pug 语法: <template lang="pug"> extends SurveyInputBase.pug block input input(type="text" :placeholder="placeholder") </template> <script> import SurveyInputBase from './SurveyInputBase.vue'; export default { extends: SurveyInputBase, props: [ 'placeholder' ] } </script> 子组件使用了 Pug 的 extends 特性,因而包含了基组件的模板并且在input块中输出任何自定义模板结构 (类似于slots). 这是子组件有效地扩展了基组件并转化为常规的 HTML 后的样子: <div class="survey-base"> <h4>{{ question }}</h4> <input type="text" :placeholder="placeholder"> </div> 总结运用上述策略我们可以构建出另外拥有私有属性和响应 HTML 结构的两个子组件 SurveyInputSelect 和 SurveyInputRadio 如果之后我们在项目的主模板里引用它们: <survey-input-text question="1. What is your name?" placeholder="e.g. John Smith" ></survey-input-text> <survey-input-select question="2. What is your favorite UI framework?" :options="['React', 'Vue.js', 'Angular']" ></survey-input-select <survey-input-radio question="3. What backend do you use?" :options="['Node.js', 'Laravel', 'Ruby']" name="backend" > </survey-input-radio> 最终会被渲染成: <div class="survey-base"> <h4>1. What is your name?</h4> <input type="text" placeholder="e.g. John Smith"> </div> <div class="survey-base"> <h4>2. What is your favorite UI framework?</h4> <select> <option>React</option> <option>Vue.js</option> <option>Angular</option> </select> </div> <div class="survey-base"> <h4>3. What backend do you use?</h4> <div><input type="radio" name="backend" value="Node.js">Node.js</div> <div><input type="radio" name="backend" value="Laravel">Laravel</div> <div><input type="radio" name="backend" value="Ruby">Ruby</div> </div>
|