Vue.js 组件样式指南

时间:2021-1-8 作者:admin

[↑ back to Table of Contents](#table-of-contents)

## [](#keep-expressions-simple)Keep expressions simple

Vue.js's inline expressions are 100% Javascript. This makes them extremely powerful, but potentially also very complex. Therefore you should **keep expressions simple**.

### [](#why-2)Why?

*   Complex inline expressions are hard to read.
*   Inline expressions can't be reused elsewhere. This can lead to code duplication and code rot.
*   IDEs typically don't have support for expression syntax, so your IDE can't autocomplete or validate.

### [](#how-2)How?

If it gets too complex or hard to read **move it to methods or computed properties**!

{{ `${year}-${month}` }}

{{ `${(new Date()).getUTCFullYear()}-${(‘0’ + ((new Date()).getUTCMonth()+1)).slice(-2)}` }}

“`

↑ back to Table of Contents

Keep component options primitive

While Vue.js supports passing complex JavaScript objects via these attributes, you should try to keep the component options as primitive as possible. Try to only use JavaScript primitives (strings, numbers, booleans) and functions. Avoid complex objects.

Why?

  • By using an attribute for each option separately the component has a clear and expressive API.
  • By using only primitives and functions as option values our component APIs are similar to the APIs of native HTML(5) elements. Which makes our custom elements directly familiar.
  • By using an attribute for each option, other developers can easily understand what is passed to the component instance.
  • When passing complex objects it’s not apparent which properties and methods of the objects are actually being used by the custom components. This makes it hard to refactor code and can lead to code rot.

How?

Use a component attribute per option, with a primitive or function as value:

<!-- recommended -->
<range-slider
  :values="[10, 20]"
  min="0"
  max="100"
  step="5"
  :on-slide="updateInputs"
  :on-end="updateResults">
</range-slider>

<!-- avoid -->
<range-slider :config="complexConfigObject"></range-slider>

↑ back to Table of Contents

Harness your component options

In Vue.js your component options are your API. A robust and predictable API makes your components easy to use by other developers.

Component options are passed via custom HTML attributes. The values of these attributes can be Vue.js plain strings (:attr="value" or v-bind:attr="value") or missing entirely. You should harness your component options to allow for these different cases.

Why?

Harnessing your component options ensures your component will always function (defensive programming). Even when other developers later use your components in ways you haven’t thought of yet.

How?

  • Use defaults for option values.
  • Use type option to validate values to an expected type.*[1]**
  • Check if option exists before using it.
<template>
  <input type="range" v-model="value" :max="max" :min="min">
</template>
<script type="text/javascript">
  export default {
    props: {
      max: {
        type: Number, // [1*] This will validate the 'max' prop to be a Number.
        default() { return 10; },
      },
      min: {
        type: Number,
        default() { return 0; },
      },
      value: {
        type: Number,
        default() { return 4; },
      },
    },
  };
</script>

↑ back to Table of Contents

Assign this to component

Within the context of a Vue.js component element, this is bound to the component instance. Therefore when you need to reference it in a different context, ensure this is available as component.

In other words: Do NOT code things like const self = this; anymore. You’re safe using Vue components.

Why?

  • By assigning this to a variable named component the variable tells developers it’s bound to the component instance wherever it’s used.

How?

<script type="text/javascript">
export default {
  methods: {
    hello() {
      return 'hello';
    },
    printHello() {
      console.log(this.hello());
    },
  },
};
</script>

<!-- avoid -->
<script type="text/javascript">
export default {
  methods: {
    hello() {
      return 'hello';
    },
    printHello() {
      const self = this; // unnecessary
      console.log(self.hello());
    },
  },
};
</script>

↑ back to Table of Contents

Component structure

Make it easy to reason and follow a sequence of thoughts. See the How.

Why?

  • Having the component export a clear and grouped object, makes the code easy to read and easier for developers to have a code standard.
  • Alphabetizing the properties, data, computed, watches, and methods makes them easy to find.
  • Again, grouping makes the component easier to read (props, data and computed; watch and methods; lifecycle methods, etc.);
  • Use the name attribute. Using vue devtools and that attribute will make your development/testing easier;
  • Use a CSS naming Methodology, like BEMdetails?;
  • Use the template-script-style .vue file organization. The odds are you’ll spend more time developing/fixing/testing on HTML than JavaScript in most cases.

How?

Component structure:

<template lang="html">
    <div class="Ranger__Wrapper">
        <!-- ... -->
    </div>
</template>

<script type="text/javascript">
  export default {
        // Do not forget this little guy
    name: 'RangeSlider',
    // compose new components
    extends: {},
    // component properties/variables
    props: {
            bar: {}, // Alphabetized
            foo: {},
            fooBar: {},
        },
    // variables
    data() {},
    computed: {},
    // when component uses other components
    components: {},
    // methods
    watch: {},
    methods: {},
    // component Lifecycle hooks
    beforeCreate() {},
    mounted() {},
};
</script>

<style scoped>
  .Ranger__Wrapper { /* ... */ }
</style>

↑ back to Table of Contents

Component event names

Vue.js provides all Vue handler functions and expressions are strictly bound to the ViewModel. Each component events should follow a good naming style that will avoid issues during the development. See the Why below.

Why?

  • Developers are free to use native likes event names and it can cause confusion down the line;
  • The freedom of naming events can lead to a DOM templates incompatibility;

How?

  • Event names should be kebab-cased;
  • A unique event name should be fired for unique actions in your component that will be of interest to the outside world, like: upload-success, upload-error or even dropzone-upload-success, dropzone-upload-error (if you see the need for having a scoped prefix);
  • Events should either end in verbs in the infinitive form (e.g. client-api-load) or nouns (e.g drive-upload-success) (source);

↑ back to Table of Contents

Avoid this.$parent

Vue.js supports nested components which have access to their parent context. Accessing context outside your vue component violates the FIRST rule of component based development. Therefore you should avoid using this.$parent.

Why?

  • A vue component, like any component, must work in isolation. If a component needs to access its parent, this rule is broken.
  • If a component needs access to its parent, it can no longer be reused in a different context.

How?

  • Pass values from the parent to the child component using attribute/properties.
  • Pass methods defined on the parent component to the child component using callbacks in attribute expressions.
  • Emit events from child components and catch it on parent component.

↑ back to Table of Contents

Use this.$refs with caution

Vue.js supports components to have access to other components and basic HTML elements context via ref attribute. That attribute will provide an accessible way through this.$refs to a component or DOM element context. In most cases, the need to access other components context via this.$refs could be avoided. This is why you should be careful when using it to avoid wrong component APIs.

Why?

  • A vue component, like any component, must work in isolation. If a component does not support all the access needed, it was badly designed/implemented.
  • Properties and events should be sufficient to most of your components.

How?

  • Create a good component API.
  • Always focus on the component purpose out of the box.
  • Never write specific code. If you need to write specific code inside a generic component, it means its API isn’t generic enough or maybe you need a new component to manage other cases.
  • Check all the props to see if something is missing. If it is, create an issue or enhance the component yourself.
  • Check all the events. In most cases developers forget that Child-Parent communication (events) is needed, that’s why they only remember the Parent-Child communication (using props).
  • Props down, events up! Upgrade your component when requested with a good API and isolation as goals.
  • Using this.$refs on components should be used when props and events are exhausted and having it makes sense (see the example below).
  • Using this.$refs to access DOM elements (instead of doing jQuery, document.getElement*, document.queryElement) is just fine, when the element can’t be manipulated with data bindings or for a directive.
<!-- good, no need for ref -->
<range :max="max"
  :min="min"
  @current-value="currentValue"
  :step="1"></range>
<!-- good example of when to use this.$refs -->
<modal ref="basicModal">
  <h4>Basic Modal</h4>
  <button class="primary" @click="$refs.basicModal.close()">Close</button>
</modal>
<button @click="$refs.basicModal.open()">Open modal</button>

<!-- Modal component -->
<template>
  <div v-show="active">
    <!-- ... -->
  </div>
</template>

<script>
  export default {
    // ...
    data() {
        return {
            active: false,
        };
    },
    methods: {
      open() {
        this.active = true;
      },
      hide() {
        this.active = false;
      },
    },
    // ...
  };
</script>
<!-- avoid accessing something that could be emitted -->
<template>
  <range :max="max"
    :min="min"
    ref="range"
    :step="1"></range>
</template>

<script>
  export default {
    // ...
    methods: {
      getRangeCurrentValue() {
        return this.$refs.range.currentValue;
      },
    },
    // ...
  };
</script>

↑ back to Table of Contents

Use component name as style scope

Vue.js component elements are custom elements which can very well be used as style scope root. Alternatively the component name can be used as CSS class namespace.

Why?

  • Scoping styles to a component element improves predictability as its prevents styles leaking outside the component element.
  • Using the same name for the module directory, the Vue.js component and the style root makes it easy for developers to understand they belong together.

How?

Use the component name as a namespace prefix based on BEM and OOCSS and use the scoped attribute on your style class. The use of scoped will tell your Vue compiler to add a signature on every class that your <style> have. That signature will force your browser (if it supports) to apply your components CSS on all tags that compose your component, leading to a no leaking css styling.

<style scoped>
    /* recommended */
    .MyExample { }
    .MyExample li { }
    .MyExample__item { }

    /* avoid */
    .My-Example { } /* not scoped to component or module name, not BEM compliant */
</style>

↑ back to Table of Contents

Document your component API

A Vue.js component instance is created by using the component element inside your application. The instance is configured through its custom attributes. For the component to be used by other developers, these custom attributes – the component’s API – should be documented in a README.md file.

Why?

  • Documentation provides developers with a high level overview to a component, without the need to go through all its code. This makes a component more accessible and easier to use.
  • A component’s API is the set of custom attributes through which its configured. Therefore these are especially of interest to other developers which only want to consume (and not develop) the component.
  • Documentation formalises the API and tells developers which functionality to keep backwards compatible when modifying the component’s code.
  • README.md is the de facto standard filename for documentation to be read first. Code repository hosting services (Github, Bitbucket, Gitlab etc) display the contents of the the README’s, directly when browsing through source directories. This applies to our module directories as well.

How?

Add a README.md file to the component’s module directory:

range-slider/
├── range-slider.vue
├── range-slider.less
└── README.md

Within the README file, describe the functionality and the usage of the module. For a vue component its most useful to describe the custom attributes it supports as those are its API:

Range slider

Functionality

The range slider lets the user to set a numeric range by dragging a handle on a slider rail for both the start and end value.

This module uses the noUiSlider for cross browser and touch support.

Usage

<range-slider> supports the following custom component attributes:

attribute type description
min Number number where range starts (lower limit).
max Number Number where range ends (upper limit).
values Number[] optional Array containing start and end value. E.g. values="[10, 20]". Defaults to [opts.min, opts.max].
step Number optional Number to increment / decrement values by. Defaults to 1.
on-slide Function optional Function called with (values, HANDLE) while a user drags the start (HANDLE == 0) or end (HANDLE == 1) handle. E.g. on-slide={ updateInputs }, with component.updateInputs = (values, HANDLE) => { const value = values[HANDLE]; }.
on-end Function optional Function called with (values, HANDLE) when user stops dragging a handle.

For customising the slider appearance see the Styling section in the noUiSlider docs.

↑ back to Table of Contents

Add a component demo

Add a index.html file with demos of the component with different configurations, showing how the component can be used.

Why?

  • A component demo proves the module works in isolation.
  • A component demo gives developers a preview before having to dig into the documentation or code.
  • Demos can illustrate all the possible configurations and variations a component can be used in.

↑ back to Table of Contents

Lint your component files

Linters improve code consistency and help trace syntax errors. .vue files can be linted adding the eslint-plugin-html in your project. If you choose, you can start a project with ESLint enabled by default using vue-cli;

Why?

  • Linting component files ensures all developers use the same code style.
  • Linting component files helps you trace syntax errors before it’s too late.

How?

To allow linters to extract the scripts from your *.vue files, [put script inside a `

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。