Creating our first Vue component

Now it's time to dive deeper into Vue, and create our own custom component — we'll start by creating a component to represent each item in the todo list. Along the way, we'll learn about a few important concepts such as calling components inside other components, passing data to them via props, and saving data state.

注意: If you need to check your code against our version, you can find a finished version of the sample Vue app code in our todo-vue repository . For a running live version, see https://mdn.github.io/todo-vue/dist/ .

Prerequisites: Familiarity with the core HTML , CSS ,和 JavaScript languages, knowledge of the terminal/command line .

Vue components are written as a combination of JavaScript objects that manage the app's data and an HTML-based template syntax that maps to the underlying DOM structure. For installation, and to use some of the more advanced features of Vue (like Single File Components or render functions), you'll need a terminal with 节点 and npm installed.

Objective: To learn how to create a Vue component, render it inside another component, pass data into it using props, and save its state.

Creating a ToDoItem component

Let's create our first component, which will display a single todo item. We'll use this to build our list of todos.

  1. moz-todo-vue/src/components directory, create a new file named ToDoItem.vue . Open the file in your code editor.
  2. Create the component's template section by adding <template></template> to the top of the file.
  3. 创建 <script></script> section below your template section. Inside the <script> tags, add a default exported object export default {} , which is your component object.

Your file should now look like this:

<template> </template>
<script>
  export default {};
</script>

						

We can now begin to add actual content to our ToDoItem . Vue templates are currently only allowed a single root element — one element needs to wrap everything inside the template section (this will change when Vue 3 comes out). We'll use a <div> for that root element.

  1. Add an empty <div> inside your component template now.
  2. Inside that <div> , let's add a checkbox and a corresponding label. Add an id to the checkbox, and a for attribute mapping the checkbox to the label, as shown below.
    <template>
      <div>
        <input type="checkbox" id="todo-item" />
        <label for="todo-item">My Todo Item</label>
      </div>
    </template>
    
    								

Using TodoItem inside our app

This is all fine, but we haven’t added the component to our app yet, so there’s no way to test it and see if everything is working. Let’s add it now.

  1. Open up App.vue 再次。
  2. At the top of your <script> tag, add the following to import your ToDoItem component:
    import ToDoItem from './components/ToDoItem.vue';
    
    								
  3. Inside your component object, add the components property, and inside it add your ToDoItem component to register it.

您的 <script> contents should now look like this:

import ToDoItem from './components/ToDoItem.vue';
export default {
  name: 'app',
  components: {
    ToDoItem
  }
};

						

This is the same way that the HelloWorld component was registered by the Vue CLI earlier.

To actually render the ToDoItem component in the app, you need to go up into your <template> element and call it as a <to-do-item></to-do-item> element. Note that the component file name and its representation in JavaScript is always in PascalCase (e.g. ToDoList ), and the equivalent custom element is always in kebab-case (e.g. <to-do-list> ).

  1. Underneath the <h1> , create an unordered list ( <ul> ) containing a single list item ( <li> ).
  2. Inside the list item add <to-do-item></to-do-item> .

您的 App.vue <template> contents should now look something like this:

<div id="app">
  <h1>To-Do List</h1>
  <ul>
    <li>
      <to-do-item></to-do-item>
    </li>
  </ul>
</div>

						

If you check your rendered app again, you should now see your rendered ToDoItem , consisting of a checkbox and a label.

The current rendering state of the app, which includes a title of To-Do List, and a single checkbox and label

Making components dynamic with props

我们的 ToDoItem component is still not very useful because we can only really include this once on a page (IDs need to be unique), and we have no way to set the label text. Nothing about this is dynamic.

What we need is some component state. This can be achieved by adding props to our component. You can think of props as being similar to inputs in a function. The value of a prop gives components an initial state that affects their display.

Registering props

In Vue, there are two ways to register props:

  • The first way is to just list props out as an array of strings. Each entry in the array corresponds to the name of a prop.
  • The second way is to define props as an object, with each key corresponding to the prop name. Listing props as an object allows you to specify default values, mark props as required, perform basic object typing (specifically around JavaScript primitive types), and perform simple prop validation.

注意: Prop validation only happens in development mode, so you can't strictly rely on it in production. Additionally, prop validation functions are invoked before the component instance is created, so they do not have access to the component state (or other props).

For this component, we’ll use the object registration method.

  1. Go back to your ToDoItem.vue 文件。
  2. 添加 props property inside the export default {} object, which contains an empty object.
  3. Inside this object, add two properties with the keys label and done .
  4. label key's value should be an object with 2 properties (or props , as they are called in the context of being available to the components).
    1. The first is a required property, which will have a value of true . This will tell Vue that we expect every instance of this component to have a label field. Vue will warn us if a ToDoItem component does not have a label field.
    2. The second property we'll add is a type property. Set the value for this property as the JavaScript 字符串 type (note the capital "S"). This tells Vue that we expect the value of this property to be a string.
  5. Now on to the done prop.
    1. First add a default field, with a value of false . This means that when no done prop is passed to a ToDoItem component, the done prop will have a value of false (bear in mind that this is not required — we only need default on non-required props).
    2. Next add a type field with a value of 布尔 . This tells Vue we expect the value prop to be a JavaScript boolean type.

Your component object should now look like this:

<script>
  export default {
    props: {
      label: { required: true, type: String },
      done: { default: false, type: Boolean }
    }
  };
</script>

						

Using registered props

With these props defined inside the component object, we can now use these variable values inside our template. Let's start by adding the label prop to the component template.

<template> , replace the contents of the <label> element with {{label}} .

{{}} is a special template syntax in Vue, which lets us print the result of JavaScript expressions defined in our class, inside our template, including values and methods. It’s important to know that content inside {{}} is displayed as text and not HTML. In this case, we’re printing the value of the label prop.

Your component’s template section should now look like this:

<template>
  <div>
    <input type="checkbox" id="todo-item" />
    <label for="todo-item">{{label}}</label>
  </div>
</template>

						

Go back to your browser and you'll see the todo item rendered as before, but without a label (oh no!). Go to your browser's DevTools and you’ll see a warning along these lines in the console:

[Vue warn]: Missing required prop: "label"
found in
---> <ToDoItem> at src/components/ToDoItem.vue
        <App> at src/App.vue
          <Root>
						

This is because we marked the label as a required prop, but we never gave the component that prop — we've defined where inside the template we want it used, but we haven't passed it into the component when calling it. Let’s fix that.

App.vue file, add a label prop to the <to-do-item></to-do-item> component, just like a regular HTML attribute:

<to-do-item label="My ToDo Item"></to-do-item>

						

Now you'll see the label in your app, and the warning won't be spat out in the console again.

So that's props in a nutshell. Next we'll move on to how Vue persists data state.

Vue's data object

If you change the value of the label prop passed into the <to-do-item></to-do-item> call in your App component, you should see it update. This is great. We have a checkbox, with an updatable label. However, we're currently not doing anything with the "done" prop — we can check the checkboxes in the UI, but nowhere in the app are we recording whether a todo item is actually done.

To achieve this, we want to bind the component's done prop to the checked 属性在 <input> element, so that it can serve as a record of whether the checkbox is checked or not. However, it's important that props serve as one-way data binding — a component should never alter the value of its own props. There are a lot of reasons for this. In part, components editing props can make debugging a challenge. If a value is passed to multiple children, it could be hard to track where the changes to that value were coming from. In addition, changing props can cause components to re-render. So mutating props in a component would trigger the component to rerender, which may in-turn trigger the mutation again.

To work around this, we can manage the done state using Vue’s data property. The data property is where you can manage local state in a component, it lives inside the component object alongside the props property and has the following structure:

data() {
  return {
    key: value
  }
}

						

You'll note that the data property is a function. This is to keep the data values unique for each instance of a component at runtime — the function is invoked separately for each component instance. If you declared data as just an object, all instances of that component would share the same values. This is a side-effect of the way Vue registers components and something you do not want.

You use this to access a component's props and other properties from inside data, as you may expect. We'll see an example of this shortly.

注意: Because of the way that this works in arrow functions (binding to the parent’s context), you wouldn’t be able to access any of the necessary attributes from inside data if you used an arrow function. So don’t use an arrow function for the data 特性。

So let's add a data property to our ToDoItem component. This will return an object containing a single property that we'll call isDone , whose value is this.done .

Update the component object like so:

export default {
  props: {
    label: { required: true, type: String },
    done: { default: false, type: Boolean }
  },
  data() {
    return {
      isDone: this.done
    };
  }
};

						

Vue does a little magic here — it binds all of your props directly to the component instance, so we don’t have to call this.props.done . It also binds other attributes ( data , which you’ve already seen, and others like 方法 , computed , etc.) directly to the instance. This is, in part, to make them available to your template. The down-side to this is that you need to keep the keys unique across these attributes. This is why we called our data 属性 isDone 而不是 done .

So now we need to attach the isDone property to our component. In a similar fashion to how Vue uses {{}} expressions to display JavaScript expressions inside templates, Vue has a special syntax to bind JavaScript expressions to HTML elements and components: v-bind v-bind expression looks like this:

v-bind:attribute="expression"
						

In other words, you prefix whatever attribute/prop you want to bind to with v-bind: . In most cases, you can use a shorthand for the v-bind property, which is to just prefix the attribute/prop with a colon. So :attribute="expression" works the same as v-bind:attribute="expression" .

So in the case of the checkbox in our ToDoItem component, we can use v-bind to map the isDone property to the checked 属性在 <input> element. Both of the following are equivalent:

<input type="checkbox" id="todo-item" v-bind:checked="isDone" />
<input type="checkbox" id="todo-item" :checked="isDone" />

						

You're free to use whichever pattern you would like. It's best to keep it consistent though. Because the shorthand syntax is more commonly used, this tutorial will stick to that pattern.

So let's do this. Update your <input> element now to include :checked="isDone" .

Test out your component by passing :done="true" ToDoItem call in App.vue . Note that you need to use the v-bind syntax, because otherwise true is passed as a string. The displayed checkbox should be checked.

<template>
  <div id="app">
    <h1>My To-Do List</h1>
    <ul>
      <li>
        <to-do-item label="My ToDo Item" :done="true"></to-do-item>
      </li>
    </ul>
  </div>
</template>

						

Try changing true to false and back again, reloading your app in between to see how the state changes.

Giving Todos a unique id

Great! We now have a working checkbox where we can set the state programmatically. However, we can currently only add one ToDoList component to the page because the id is hardcoded. This would result in errors with assistive technology since the id is needed to correctly map labels to their checkboxes. To fix this, we can programmatically set the id in the component data.

We can use the lodash package's uniqueid() method to help keep the index unique. This package exports a function that takes in a string and appends a unique integer to the end of the prefix. This will be sufficient for keeping component id s unique.

Let’s add the package to our project with npm; stop your server and enter the following command into your terminal:

npm install --save lodash.uniqueid

						

注意: If you prefer yarn, you could instead use yarn add lodash.uniqueid .

We can now import this package into our ToDoItem component. Add the following line at the top of ToDoItem.vue ’s <script> 元素:

import uniqueId from 'lodash.uniqueid';

						

Next, add an id field to our data property, so the component object ends up looking like so ( uniqueId() returns the specified prefix — todo- — with a unique string appended to it):

import uniqueId from 'lodash.uniqueid';
export default {
  props: {
    label: { required: true, type: String },
    done: { default: false, type: Boolean }
  },
  data() {
    return {
      isDone: this.done,
      id: uniqueId('todo-')
    };
  }
};

						

Next, bind the id to both our checkbox’s id attribute and the label’s for attribute, updating the existing id and for attributes as shown:

<template>
  <div>
    <input type="checkbox" :id="id" :checked="isDone" />
    <label :for="id">{{label}}</label>
  </div>
</template>

						

摘要

And that will do for this article. At this point we have a nicely-working ToDoItem component that can be passed a label to display, will store its checked state, and will be rendered with a unique id each time it is called. You can check if the unique id s are working by temporarily adding more <to-do-item></to-do-item> calls into App.vue , and then checking their rendered output with your browser's DevTools.

Now we're ready to add multiple ToDoItem components to our App. In our next article we'll look at adding a set of todo item data to our App.vue component, which we'll then loop through and display inside ToDoItem components using the v-for 指令。

In this module

发现此页面有问题吗?

最后修改: , 由 MDN 贡献者

  1. Complete beginners start here!
  2. Web 快速入门
    1. Getting started with the Web overview
    2. 安装基本软件
    3. What will your website look like?
    4. 处理文件
    5. HTML 基础
    6. CSS 基础
    7. JavaScript 基础
    8. 发布您的网站
    9. How the Web works
  3. HTML — Structuring the Web
  4. HTML 介绍
    1. Introduction to HTML overview
    2. Getting started with HTML
    3. What's in the head? Metadata in HTML
    4. HTML text fundamentals
    5. Creating hyperlinks
    6. Advanced text formatting
    7. Document and website structure
    8. Debugging HTML
    9. Assessment: Marking up a letter
    10. Assessment: Structuring a page of content
  5. 多媒体和嵌入
    1. Multimedia and embedding overview
    2. Images in HTML
    3. Video and audio content
    4. From object to iframe — other embedding technologies
    5. Adding vector graphics to the Web
    6. Responsive images
    7. Assessment: Mozilla splash page
  6. HTML 表格
    1. HTML tables overview
    2. HTML table basics
    3. HTML Table advanced features and accessibility
    4. Assessment: Structuring planet data
  7. CSS — Styling the Web
  8. CSS 第一步
    1. CSS first steps overview
    2. What is CSS?
    3. Getting started with CSS
    4. How CSS is structured
    5. How CSS works
    6. Using your new knowledge
  9. CSS 构建块
    1. CSS building blocks overview
    2. Cascade and inheritance
    3. CSS 选择器
    4. The box model
    5. Backgrounds and borders
    6. Handling different text directions
    7. Overflowing content
    8. Values and units
    9. Sizing items in CSS
    10. Images, media, and form elements
    11. Styling tables
    12. Debugging CSS
    13. Organizing your CSS
  10. 样式化文本
    1. Styling text overview
    2. Fundamental text and font styling
    3. Styling lists
    4. Styling links
    5. Web fonts
    6. Assessment: Typesetting a community school homepage
  11. CSS 布局
    1. CSS layout overview
    2. Introduction to CSS layout
    3. Normal Flow
    4. Flexbox
    5. Grids
    6. Floats
    7. 位置
    8. Multiple-column Layout
    9. Responsive design
    10. Beginner's guide to media queries
    11. Legacy Layout Methods
    12. Supporting Older Browsers
    13. Fundamental Layout Comprehension
  12. JavaScript — Dynamic client-side scripting
  13. JavaScript 第一步
    1. JavaScript first steps overview
    2. What is JavaScript?
    3. A first splash into JavaScript
    4. What went wrong? Troubleshooting JavaScript
    5. Storing the information you need — Variables
    6. Basic math in JavaScript — Numbers and operators
    7. Handling text — Strings in JavaScript
    8. Useful string methods
    9. 数组
    10. Assessment: Silly story generator
  14. JavaScript 构建块
    1. JavaScript building blocks overview
    2. Making decisions in your code — Conditionals
    3. Looping code
    4. Functions — Reusable blocks of code
    5. Build your own function
    6. Function return values
    7. 事件介绍
    8. Assessment: Image gallery
  15. 引入 JavaScript 对象
    1. Introducing JavaScript objects overview
    2. Object basics
    3. 对象原型
    4. Object-oriented programming concepts
    5. Classes in JavaScript
    6. Working with JSON data
    7. Object building practice
    8. Assessment: Adding features to our bouncing balls demo
  16. 异步 JavaScript
    1. Asynchronous JavaScript overview
    2. General asynchronous programming concepts
    3. Introducing asynchronous JavaScript
    4. Cooperative asynchronous Java​Script: Timeouts and intervals
    5. Graceful asynchronous programming with Promises
    6. Making asynchronous programming easier with async and await
    7. Choosing the right approach
  17. 客户端侧 Web API
    1. 客户端侧 Web API
    2. Introduction to web APIs
    3. Manipulating documents
    4. Fetching data from the server
    5. Third party APIs
    6. Drawing graphics
    7. Video and audio APIs
    8. Client-side storage
  18. Web forms — Working with user data
  19. Core forms learning pathway
    1. Web forms overview
    2. Your first form
    3. How to structure a web form
    4. Basic native form controls
    5. The HTML5 input types
    6. Other form controls
    7. Styling web forms
    8. Advanced form styling
    9. UI pseudo-classes
    10. Client-side form validation
    11. Sending form data
  20. Advanced forms articles
    1. How to build custom form controls
    2. Sending forms through JavaScript
    3. CSS property compatibility table for form controls
  21. Accessibility — Make the web usable by everyone
  22. Accessibility guides
    1. Accessibility overview
    2. What is accessibility?
    3. HTML: A good basis for accessibility
    4. CSS and JavaScript accessibility best practices
    5. WAI-ARIA basics
    6. Accessible multimedia
    7. Mobile accessibility
  23. Accessibility assessment
    1. Assessment: Accessibility troubleshooting
  24. Tools and testing
  25. Client-side web development tools
    1. Client-side web development tools index
    2. Client-side tooling overview
    3. Command line crash course
    4. Package management basics
    5. Introducing a complete toolchain
    6. Deploying our app
  26. Introduction to client-side frameworks
    1. Client-side frameworks overview
    2. Framework main features
  27. React
    1. Getting started with React
    2. Beginning our React todo list
    3. Componentizing our React app
    4. React interactivity: Events and state
    5. React interactivity: Editing, filtering, conditional rendering
    6. Accessibility in React
    7. React resources
  28. Ember
    1. Getting started with Ember
    2. Ember app structure and componentization
    3. Ember interactivity: Events, classes and state
    4. Ember Interactivity: Footer functionality, conditional rendering
    5. Routing in Ember
    6. Ember resources and troubleshooting
  29. Vue
    1. Getting started with Vue
    2. Creating our first Vue component
    3. Rendering a list of Vue components
    4. Adding a new todo form: Vue events, methods, and models
    5. Styling Vue components with CSS
    6. Using Vue computed properties
    7. Vue conditional rendering: editing existing todos
    8. Focus management with Vue refs
    9. Vue resources
  30. Svelte
    1. Getting started with Svelte
    2. Starting our Svelte Todo list app
    3. Dynamic behavior in Svelte: working with variables and props
    4. Componentizing our Svelte app
    5. Advanced Svelte: Reactivity, lifecycle, accessibility
    6. Working with Svelte stores
    7. TypeScript support in Svelte
    8. Deployment and next steps
  31. Angular
    1. Getting started with Angular
    2. Beginning our Angular todo list app
    3. Styling our Angular app
    4. Creating an item component
    5. Filtering our to-do items
    6. Building Angular applications and further resources
  32. Git and GitHub
    1. Git and GitHub overview
    2. Hello World
    3. Git Handbook
    4. Forking Projects
    5. About pull requests
    6. Mastering Issues
  33. Cross browser testing
    1. Cross browser testing overview
    2. Introduction to cross browser testing
    3. Strategies for carrying out testing
    4. Handling common HTML and CSS problems
    5. Handling common JavaScript problems
    6. Handling common accessibility problems
    7. Implementing feature detection
    8. Introduction to automated testing
    9. Setting up your own test automation environment
  34. Server-side website programming
  35. 第一步
    1. First steps overview
    2. Introduction to the server-side
    3. Client-Server overview
    4. Server-side web frameworks
    5. Website security
  36. Django Web 框架 (Python)
    1. Django web framework (Python) overview
    2. 介绍
    3. 设置开发环境
    4. Tutorial: The Local Library website
    5. Tutorial Part 2: Creating a skeleton website
    6. Tutorial Part 3: Using models
    7. Tutorial Part 4: Django admin site
    8. Tutorial Part 5: Creating our home page
    9. Tutorial Part 6: Generic list and detail views
    10. Tutorial Part 7: Sessions framework
    11. Tutorial Part 8: User authentication and permissions
    12. Tutorial Part 9: Working with forms
    13. Tutorial Part 10: Testing a Django web application
    14. Tutorial Part 11: Deploying Django to production
    15. Web application security
    16. Assessment: DIY mini blog
  37. Express Web Framework (node.js/JavaScript)
    1. Express Web Framework (Node.js/JavaScript) overview
    2. Express/Node introduction
    3. Setting up a Node (Express) development environment
    4. Express tutorial: The Local Library website
    5. Express Tutorial Part 2: Creating a skeleton website
    6. Express Tutorial Part 3: Using a database (with Mongoose)
    7. Express Tutorial Part 4: Routes and controllers
    8. Express Tutorial Part 5: Displaying library data
    9. Express Tutorial Part 6: Working with forms
    10. Express Tutorial Part 7: Deploying to production
  38. Further resources
  39. Common questions
    1. HTML questions
    2. CSS questions
    3. JavaScript questions
    4. Web mechanics
    5. Tools and setup
    6. Design and accessibility