Vue的第一个组件设计

我记得当时我拿起CakePHP,我很喜欢开始使用它是多么容易。这些文档不只结构合理,详尽无遗,并且用户友好。多年之后,这正是我在Vue.js中感觉到的。然而,与Cake相比,Vue文档仍然缺乏一件事:一个真实的项目教程。css

不管框架的文档记录如何,这对每一个人来讲都是不够的。阅读概念并不老是有助于了解大局或了解如何使用它们来实际制做某些东西。若是您像我同样,经过制做一些可用的组件来更好地学习,并在编码时参考文档,帮助记忆文档和熟练使用Vue。html

在本教程中,咱们将构建星级评分系统组件。咱们将学习几种Vue.js中的概念,咱们会在项目中使用到他们,并将讨论为何咱们使用它们。vue

rating.gif

这篇文章深刻介绍了如何以及为什么。它旨在帮助您掌握Vue.js的一些核心概念,并教您如何为将来的项目作出设计决策。若是您想了解整个思考过程,请继续阅读。不然,您能够查看CodeSandbox上的最终代码。webpack

入门

Vue.js,固然你会以本身做为一个简单的脚本运行而以为已经不错了,可是当你想使用单文件组件时状况有点不一样。固然,你不必定必须构建以组件的这种方式。您能够完美地使用定义全局组件Vue.component。问题是,这须要权衡,例如必须使用字符串模板,没有范围的CSS支持,也没有构建步骤(所以,没有预处理器)。然而,咱们但愿更深刻地学习如何构建一个能够在实际项目中使用的实际组件。出于这些缘由,咱们将采用由Webpack提供支持的实际设置。git

为了简化操做并缩短配置时间,咱们将使用vue-cliwebpack-simple Vue.js模板。github

首先,您须要在全局安装vue-cli。启动终端并键入如下内容:web

npm install -g vue-cli
复制代码

您须要继续输入:chrome

vue init webpack-simple path/to/my-project
复制代码

你会在这个过程当中被问到几个问题。选择除“使用sass”之外的全部内容,都是为默认值。而后,vue-cli将初始化项目并建立package.json文件。完成后,您能够导航到项目的目录,安装依赖项并运行项目:vue-cli

cd path/to/my-project
npm install
npm run dev
复制代码

不出意外!Webpack将开始在端口上提供项目8080(若是可用,8080端口没有被其余程序占用)并在浏览器中触发它。若是一切顺利,你应该看到像这样的欢迎页面。npm

vue-js-welcome-page.png

短暂的暂停 - 调试工具

几乎!要正确调试Vue.js组件,您须要正确的工具。继续安装Vue.js devtools浏览器扩展程序(Firefox / Chrome / Safari)。

你的第一个组件

最好的功能之一是Vue.js单文件组件(SFC)。它们容许您在一个文件中定义组件的结构,样式和行为,而没有混合HTML,CSS和JavaScript的常见缺点。

SFC以.vue扩展名结尾,具备如下结构:

<template>
  <!-- Your HTML goes here -->
</template>

<script>
  /* Your JS goes here */
</script>

<style>
  /* Your CSS goes here */
</style>
复制代码

让咱们来建立咱们的第一个组件:建立一个Rating.vue文件/src/components,而后复制/粘贴上面的代码片断。而后,打开/src/main.js并调整现有代码:

import Vue from 'vue'
import Rating from './components/Rating'

new Vue({
  el: '#app',
  template: '<Rating/>',
  components: { Rating }
})
复制代码

最后,为您的Rating.vue添加一些HTML:

<template>
  <ul>
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
  </ul>
</template>
复制代码

如今查看浏览器中的页面,您应该看到列表!Vue.js将您的<Rating>组件附加到#app元素中index.html。若是检查HTML,则应该看不到该#app元素的符号:Vue.js将其替换为Rating组件。

旁注:您是否注意到您甚至不须要从新加载页面?那是由于Webpack的vue-loader带有热重载功能。与实时从新加载或浏览器同步相反,热从新加载不会在每次更改文件时刷新页面。相反,它会监视组件更改并仅刷新它们,保持状态不变。

如今咱们已经花了一些时间来设置,但如今是咱们实际编写有意义的代码的时候了。

模版template

咱们将使用vue-awesome,一个用Font Awesome图标构建的Vue.js的SVG图标组件。这容许咱们只加载咱们须要的图标。继续使用npm(或Yarn)安装它:

npm install vue-awesome
复制代码

而后编辑您的组件,以下所示:

<template>
  <div>
    <ul>
      <li><icon name="star"/></li>
      <li><icon name="star"/></li>
      <li><icon name="star"/></li>
      <li><icon name="star-o"/></li>
      <li><icon name="star-o"/></li>
    </ul>
    <span>3 of 5</span>
  </div>
</template>

<script>
  import 'vue-awesome/icons/star'
  import 'vue-awesome/icons/star-o'

  import Icon from 'vue-awesome/components/Icon'

  export default {
    components: { Icon }
  }
</script>
复制代码

好吧好吧,让咱们放慢脚步并解释全部这些内容😅

Vue.js使用原生ES6模块来处理依赖项和导出组件。<script>块中的前两行表示单独导入图标,所以您最终不会在最终bundle(资源)中找到您不须要的图标。第三个Icon组件是从vue-awesome中导出的,所以您能够在您的组件中使用它。

Icon也是一个Vue.js的单文件组件(SFC),就像咱们正在构建的组件那样。若是你打开文件,你会发现它具备与咱们彻底相同的结构。

export default导出对象文字做为组件的视图模型。咱们在当前的components中注册了Icon组件,所以咱们能够在咱们的本地使用它。

最后,咱们在<template>使用HTML它并传递了一个name属性来定义咱们想要的图标。组件能够经过将它们转换为kebab-case(例如:MyComponent成为`)来用做自定义HTML标记。咱们不须要在组件内嵌套任何东西,所以咱们使用了自闭合标记。

旁注:您是否注意到在HTML周围添加了一个<div>包装?那是由于咱们还在根级别添加了一个计数器<span>,而Vue.js中的组件模板只接受一个根元素。若是你不遵循这个规则,你会收到编译错误。

样式CSS

若是你已经使用过CSS一段时间了,你知道其中一个主要问题是必须处理它的全局性。嵌套一直被认为是解决这个问题的方法。如今咱们知道它能够很快致使特殊性问题,使得样式难以覆盖,亦或没法重用,以及是扩展的噩梦。

BEM这样的方法被发明来经过命名空间类来规避这个问题并保持低特异性。在一段时间内,它是编写干净且可扩展的CSS的理想方式。而后,像Vue.js或React这样的框架和库出现了,并带来了范围样式。

React具备样式化的组件,Vue.js具备组件范围的CSS。它容许您编写特定于组件的CSS,而无需提供什么特定神奇的技巧。您使用“普通”类名编写常规CSS,而且Vue.js经过将数据属性分配给HTML元素并将其附加到已编译样式来肯定处理范围。

让咱们在组件上添加一些简单的类:

<template>
  <div class="rating">
    <ul class="list">
      <li class="star active"><icon name="star"/></li>
      <li class="star active"><icon name="star"/></li>
      <li class="star active"><icon name="star"/></li>
      <li class="star"><icon name="star-o"/></li>
      <li class="star"><icon name="star-o"/></li>
    </ul>
    <span>3 of 5</span>
  </div>
</template>
复制代码

并添加上样式:

<style scoped>
  .rating {
    font-family: 'Avenir', Helvetica, Arial, sans-serif;
    font-size: 14px;
    color: #a7a8a8;
  }
  .list {
    margin: 0 0 5px 0;
    padding: 0;
    list-style-type: none;
  }
  .list:hover .star {
    color: #f3d23e;
  }
  .star {
    display: inline-block;
    cursor: pointer;
    margin: 0 1px;
  }
  .star:hover ~ .star:not(.active) {
    color: inherit;
  }
  .active {
    color: #f3d23e;
  }
</style>
复制代码

你看到了scoped属性了吗?这就是告诉Vue.js,如何肯定样式范围的缘由,所以它们不会泄漏到任何其余地方。若是您正确地在index.html复制/粘贴HTML代码,您会注意到您的样式不生效:那是由于它们的做用域是组件!🎉

那么怎么样预处理呢?

Vue.js能够轻松地从普通的CSS切换到您最喜欢的预处理器模式。您只须要正确的使用Webpack loader,并在<style>块上设置简单的属性。还记得咱们在生成项目的时候,问咱们是否使用sass吗,若是选择"是"使用sass。同时vue-cli已经为咱们安装并配置了sass-loader。如今,咱们须要作的就是在<style>添加lang="scss"标记。

咱们如今可使用Sass进行编写组件级别风格的,经过import引入Sass,就像使用import引入变量,颜色定义等。若是你喜欢的缩进语法(或“sass”符号),只需lang属性切换scsssass

behavior - 行为

如今咱们的组件看起来很好,是时候让它工做了。目前,咱们有一个硬编码模板。让咱们设置一些初始模拟状态并调整模板以便它反映出来:

<script>
  ...
  export default {
    components: { Icon },
    data() {
      return {
        stars: 3,
        maxStars: 5
      }
    }
  }
</script>
复制代码

模版修改:

<template>
  <div class="rating">
    <ul class="list">
      <li v-for="star in maxStars" :class="{ 'active': star <= stars }" class="star">
        <icon :name="star <= stars ? 'star' : 'star-o'"/>
      </li>
    </ul>
    <span>3 of 5</span>
  </div>
</template>
复制代码

咱们在这里作的是使用Vue data来设置组件状态。您定义的每一个属性都会跟着data中的属性进行绑定:若是它发生变化,它将反映在视图中。

咱们正在制做一个可重用的组件,所以data须要是一个工厂函数而不是一个对象字面量。经过这种方式,咱们得到了一个新对象,而不是对将在多个组件之间共享的现有对象的引用。

咱们的data工厂返回两个属性:stars表示当前“激活”星数量,maxStars表示总的星星数量。从这些,咱们调整了咱们的模板,以便它反映实际组件的状态。Vue.js附带了一系列指令,可让您在模板中添加表示逻辑,而无需将其与纯JavaScript混合使用。该v-for指令遍历任何可迭代对象(数组,对象文字,映射等)。它也能够将数字做为重复x次的范围。这就是咱们所作的v-for="star in maxStars",因此咱们<li>对组件中的每一个星都有一个。

您可能已经注意到某些属性以冒号为前缀:这是该v-bind指令的简写,它将属性动态绑定到表达式。咱们原本能够用它的不省略形式写出来:v-bind:class

当星号处于激活状态时,咱们须要

  • 元素上附加active类。在咱们的例子中,这意味着每一个<li>索引都应该小于star才能附加active类。咱们在:class指令中使用了一个表达式,只在当前star小于stars的时候追加active。咱们使用相同的条件,此次使用三元运算符来定义与Icon组件一块儿使用的图标:starstar-o

    计数器应该如何

    既然咱们的star列表与实际数据绑定,那么咱们就应该为计数器作一样的事了。最简单的方法是使用“胡子语法”进行文本插值:

    <span>{{ stars }} of {{ maxStars }}</span>
    复制代码

    很直白,不是吗?如今在咱们的例子中,这能够解决问题,但若是咱们须要更复杂的JavaScript表达式,最好在计算属性中对其进行抽象。

    export default {
      ...
      computed: {
        counter() {
          return `${this.stars} of ${this.maxStars}`
        }
      }
    }
    复制代码

    这里有点矫枉过正。咱们可使用模板内表达式能够保持可读性。然而,当您必须处理更复杂的逻辑时,请记住计算属性​​。

    咱们须要作的另外一件事是提供一种方法来隐藏计数器,若是咱们不想要它。最简单的方法是使用v-if带有布尔值的指令。

    <span v-if="hasCounter">{{ stars }} of {{ maxStars }}</span>
    复制代码

    data中添加hasCounter属性

    export default {
      ...
      data() {
        return {
          stars: 3,
          maxStars: 5,
          hasCounter: true
        }
      }
    }
    复制代码

    交互

    咱们差很少完成了,但咱们仍然须要实现组件中最有趣的部分:反应性。咱们将使用v-on处理事件的Vue.js指令,以及methods一个能够附加全部方法的Vue.js属性。

    <template>
      ...
      <li @click="rate(star)" ...>
      ...
    </template>
    复制代码

    methods中

    export default {
      ...
      methods: {
        rate(star) {
          // do stuff
        }
      }
    }
    复制代码

    咱们在上面添加了一个@click属性<li>,这是一个简写v-on:click。该指令包含咱们在methods组件属性中定义的rate方法的调用。

    “等一下......这看起来很是熟悉HTML的onclick属性。在HTML中使用内联JavaScript不是一种过期的错误作法吗?“

    确实如此,但即便语法看起来很像onclick,比较二者也是一个错误。当您构建Vue.js组件时,您不该将其视为分离的HTML / CSS / JS,而应将其视为使用多种语言的一个组件。当项目在浏览器中编译后,全部HTML和指令都编译为纯JavaScript。若是在浏览器中检查呈现的HTML,则不会看到任何指令的迹象,也不会看到任何onclick属性。Vue.js编译了您的组件并建立了正确的绑定。这也是您能够直接从模板访问组件上下文的缘由:由于指令绑定到视图模型。与具备单独HTML的传统项目相反,模板是组件的组成部分。

    回到咱们的rate方法。咱们须要在click时候修改stars为当前li的索引,因此咱们从@click指令传递索引,咱们能够执行如下操做:

    export default {
      ...
      methods: {
        rate(star) {
          this.stars = star
        }
      }
    }
    复制代码

    在浏览器中查看页面并尝试点击星标:它有效!

    若是您在浏览器devtools中打开Vue面板并选择该组件,您将在单击星标时看到数据发生变化。这代表您的stars属性是被绑定的:当您对其进行修改时,它会将其更改分派给视图。这个概念称为数据绑定,若是你曾经使用过像Backbone.js或Knockout这样的框架,你应该熟悉它。不一样之处在于Vue.js与React同样,仅在一个方向上执行:这称为单向数据绑定。但这个话题又能够用另外一篇文章来讨论了😊

    到这个点上,咱们能够称之为完成,但咱们能够作更多的工做来改善用户体验。

    如今,咱们实际上不能给出零等级,由于点击一个星标将stars设置为其索引,而li的索引永远大于0。更好的作法是从新点击同一颗星并让它切换当前状态而不是保持活跃状态​​。

    export default {
      ...
      methods: {
        rate(star) {
          this.stars = this.stars === star ? star - 1 : star
        }
      }
    }
    复制代码

    如今,若是点击的星的索引等于当前值stars,咱们减小它的值。不然,咱们为其赋值star。

    若是咱们想完全,咱们还应该添加一层保护,以确保stars永远不会分配一个没有意义的值。咱们须要确保stars永远不会少于0,永远不会超过maxStars,而且这是一个合适的数字。

    export default {
      ...
      methods: {
        rate(star) {
          if (typeof star === 'number' && star <= this.maxStars && star >= 0) {
            this.stars = this.stars === star ? star - 1 : star
          }
        }
      }
    }
    复制代码

    passing props 属性传递

    如今,组件的数据在data属性中是硬编码的。若是咱们但愿咱们的组件实际可用,咱们须要可以从其实例传递自定义数据。在Vue.js中,咱们用props能够作到。

    export default {
      props: ['grade', 'maxStars', 'hasCounter'],
      data() {
        return {
          stars: this.grade
        }
      },
      ...
    }
    复制代码

    并在main.js

    new Vue({
      el: '#app',
      template: '<Rating :grade="3" :maxStars="5" :hasCounter="true"/>',
      components: { Rating }
    })
    复制代码

    这里有三件事须要注意:

    首先,咱们使用v-bind简写来从组件实例传递props:这就是Vue.js所谓的动态语法。当您想要传递字符串值时,您不须要它,使用文字语法(不带v-bind的正常属性)将起做用。但在咱们的例子中,因为咱们传递数字和布尔值,因此咱们这样作很重要。

    propsdata特性是在编译时合并了,因此props中的属性会被视图模型中的标签绑定(若是定义了)。但出于一样的缘由,咱们不能使用相同的名称props和data属性,不然data中的属性会被props中的属性覆盖。

    最后,咱们定义了一个grade props,并经过它做为一个data的stars属性。之因此咱们这样作,而不是直接使用grade props,是由于当咱们改变等级时,star会发生修改。在Vue.js中,props是从父母组件传递给孩子组件,而不是相反,因此你不会意外地改变父母的状态。这将违反单向数据流原则并使事情更难调试。这就是为何你应该不尝试修改子组件内的属性。相反,定义一个data使用prop的初始值做为本身的本地属性,也是一个很好的选择。

    最后的修改

    咱们将使用Vue.js中的一个很不错的功能: prop validation

    Vue.js容许您在将数据传递给组件以前对其进行控制。您能够执行如下四项主要操做:检查类型,要求定义prop,设置默认值以及执行自定义验证。

    export default {
      props: {
        grade: {
          type: Number,
          required: true
        },
        maxStars: {
          type: Number,
          default: 5
        },
        hasCounter: {
          type: Boolean,
          default: true
        }
      },
      ...
    }
    复制代码

    咱们使用类型检查来确保将正确类型的数据传递给组件。若是咱们忘记使用动态语法传递非字符串值,这将特别有用。咱们还确保grade是符合组件的给定要求进行传递的。对于其余道具,咱们定义了默认值,所以即便没有传递自定义数据,组件也能正常工做。

    如今咱们能够经过执行如下操做来实例化组件:

    <Rating :grade="3"/>
    复制代码

    就是这样!您建立了第一个Vue.js组件,并探讨了不少概念,包括生成与样板谟VUE-CLI,单文件组件,在组件数据传递,scope style,指令,事件处理,计算属性,Vue methods,单方式数据流,props和prop validation。而这仅仅是Vue.js所提供的浅显的功能!

    这是一个很是密集的教程,因此若是你不理解一切,不要担忧。再次阅读,在各部分之间暂停,探索并摆弄CodeSandbox上的代码。若是您对本教程有任何疑问或评论,请不要犹豫,在微博上@我!

    结尾

    若是您有幸已经读到这里,相信必定对您有所帮助。

    本文为翻译文,并添加一些本身的讨论,不少地方不流畅,请见谅。

    本文的源码rating-vue-component,若是对您有用,但愿您给个star

  • 相关文章
    相关标签/搜索