Flexible Vue components - so simple

Learning objectives of this article

  1. Be able to understand the concept and function of vue components
  2. Be able to master the ability of packaging components
  3. Ability to use communication between components
  4. Able to complete todo case

1. vue components

1.0_ Why use components

I've made a folding panel before

Requirements: now you want multiple parts to be folded and expanded

Scenario 1: copy code

  • Code duplication redundancy
  • Not conducive to maintenance
  1. The case is written in less style, so download it

    yarn add less less-loader@5.0.0 -D

  2. Template label - on this basis, copy several more copies of the template to be reused (explain the bad places and lead to the solution)

    Case: folding panel

    Send Xin Jian to Furong building

Conclusion: the code is very redundant and repetitive. The solution is to adopt our component-based development method. Look down

1.1_vue component_ concept

Components are reusable Vue instances that encapsulate tags, styles, and JS code

Componentization: the idea of encapsulation encapsulates the reusable parts of the page into components, so as to facilitate the development and maintenance of the project

A page can be divided into components. A component is a whole. Each component can have its own independent structure style and behavior (html, css and js)

1.2_vue component_ Basic use

Objective: each component is an independent individual, which is embodied in the code vue file

Pithy formula: which part of the label is reused, which part is encapsulated in the component

(important): a template in a component can only have one root label

(data is an independent function within a scope):

Steps:

  1. Create component components / panel vue

Encapsulation label + style + js - components are independent for reuse

<template>
  <div>
    <div class="title">
      <h4>Send Xin Jian to Furong building</h4>
      <span class="btn" @click="isShow = !isShow">
        {{ isShow ? "Put away" : "open" }}
      </span>
    </div>
    <div class="container" v-show="isShow">
      <p>Cold and rainy rivers enter Wu at night,</p>
      <p>Seeing off guests in pingming, Chu mountain is lonely.</p>
      <p>Luoyang relatives and friends ask each other,</p>
      <p>A piece of ice is in the jade pot.</p>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isShow: false,
    };
  },
};
</script>

<style scoped>
.title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border: 1px solid #ccc;
  padding: 0 1em;
}
.title h4 {
  line-height: 2;
  margin: 0;
}
.container {
  border: 1px solid #ccc;
  padding: 0 1em;
}
.btn {
  /* Change the mouse to the shape of the hand */
  cursor: pointer;
}
</style>
  1. Registration component: it needs to be registered before use

Global - register for use

The global entry is in main JS, register on new Vue

Syntax:

import Vue from 'vue'
import Component object from 'vue File path'

Vue.component("Component name", Component object)

main.js - demo now

// Target: global registration (one definition everywhere)
// 1. Create component - file name vue
// 2. Lead in components
import Pannel from './components/Pannel'
// 3. Global - register components
/*
  Syntax: 
  Vue.component("Component name "(component object)
*/
Vue.component("PannelG", Pannel)

After the PannelG component name is registered globally, it can be used as a label in any Vue file template

Both single and double labels can be in lowercase plus - form. After running, the user-defined label will be parsed as a component and replaced to this position with the label encapsulated in the component

<PannelG></PannelG>
<PannelG/>
<pannel-g></pannel-g>

Local - registered use

Syntax:

import Component object from 'vue File path'

export default {
    components: {
        "Component name": Component object
    }
}

Introduction, registration and use in any vue file

<template>
  <div id="app">
    <h3>Case: folding panel</h3>
    <!-- 4. The component name is used as a label -->
    <!-- <Component name></Component name> -->
    <PannelG></PannelG>
    <PannelL></PannelL>
  </div>
</template>

<script>
// Target: local registration (multi use)
// 1. Create component - file name vue
// 2. Lead in components
import Pannel from './components/Pannel_1'
export default {
  // 3. Local - register components
  /*
    Syntax: 
    components: {
      "Component name ": Component Object
    }
  */
  components: {
    PannelL: Pannel
  }
}
</script>

Component Usage Summary:

  1. (create) encapsulate html+css+vue into a separate In vue file
  2. (import registration) component file = > get component configuration object
  3. (use) the current page is used as a label

1.3_vue component scoped function

Purpose: to solve the problem that multiple component style names are the same and conflict

Requirement: div tag name selector, setting background color

Problem: it is found that both the div in the component and the div outside the component are effective

Solution: to pannel Add the scoped attribute to the style tag in the Vue component

<style scoped>

Adding scoped attribute to style will add a randomly generated attribute starting with data-v to the label of this component

And it must be the element of the current component to have this custom attribute and be affected by this style

Summary: add scoped to style, and the style in the component will only take effect in the current vue component

2. vue component communication

Because the variables and values of each component are independent

Component communication first focuses on the transfer of parent to child, and child to parent

Parent: vue files that use other components

Sub: introduced component (embedded)

For example: app Vue (parent) MyProduct Vue (sub)

2.0_vue component communication_ Parent to child props

Purpose: pass values to components from the outside, learn the grammar first, and look at the usage scenario in the exercise

Requirement: encapsulate a commodity component MyProduct Vue - External incoming specific data to be displayed, as shown in the following figure

Steps:

  1. Create component components / MyProduct Vue - copy the label below

  2. The props defined variable inside the component is used to receive the value passed in from the outside

  3. App. The registration component is introduced into Vue. When used, specific data is passed in to the component for display

components/MyProduct.vue - prepare label

<template>
  <div class="my-product">
    <h3>title: {{ title }}</h3>
    <p>Price: {{ price }}element</p>
    <p>{{ intro }}</p>
  </div>
</template>

<script>
export default {
  props: ['title', 'price', 'intro']
}
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

App. Use and pass in data in Vue

<template>
  <div>
    <!-- 
      target: father(App.vue) -> son(MyProduct.vue) Enter by value transfer respectively
      demand: Each component displays different data information
      step(Pithy formula):
        1. Subcomponents - props - variable (Ready to receive)
        2. Parent component - Pass the value in
     -->
    <Product title="Delicious salivary chicken" price="50" intro="Grand reward for opening, 20% off"></Product>
    <Product title="What a lovely adorable" price="20" intro="The boss is not at home, 10% off the court"></Product>
    <Product title="What an expensive Beijing roast duck" price="290" :intro="str"></Product>
  </div>
</template>

<script>
// 1. Create a component (. vue file)
// 2. Lead in components
import Product from './components/MyProduct'
export default {
  data(){
    return {
      str: "How expensive, Come on, yummy"
    }
  },
  // 3. Register components
  components: {
    // Product: Product // key and value variables have the same name - short form
    Product
  }
}
</script>

<style>

</style>

Summary: components encapsulate reusable labels and styles, while specific data needs to be imported from outside

2.1_vue component communication_ Parent child fit cycle

Purpose: transfer the data cycle to the display in the component respectively

data

list: [
    { id: 1, proname: "Super delicious lollipop", proprice: 18.8, info: 'Grand reward for opening, 20% off' },
    { id: 2, proname: "Super delicious big chicken leg", proprice: 34.2, info: 'Delicious but not greasy, Come and buy it' },
    { id: 3, proname: "Super invincible ice cream", proprice: 14.2, info: 'hot summer , Have an ice cream' },
],

Correct code (not reproducible)`

<template>
  <div>
    <MyProduct v-for="obj in list" :key="obj.id"
    :title="obj.proname"
    :price="obj.proprice"
    :intro="obj.info"
    ></MyProduct>
  </div>
</template>

<script>
// Target: recycle components - pass in data separately
// 1. Create components
// 2. Lead in components
import MyProduct from './components/MyProduct'
export default {
  data() {
    return {
      list: [
        {
          id: 1,
          proname: "Super delicious lollipop",
          proprice: 18.8,
          info: "Grand reward for opening, 20% off",
        },
        {
          id: 2,
          proname: "Super delicious big chicken leg",
          proprice: 34.2,
          info: "Delicious but not greasy, Come and buy it",
        },
        {
          id: 3,
          proname: "Super invincible ice cream",
          proprice: 14.2,
          info: "hot summer , Have an ice cream",
        },
      ],
    };
  },
  // 3. Register components
  components: {
    // MyProduct: MyProduct
    MyProduct
  }
};
</script>

<style>
</style>

Unidirectional data flow

The principle of one-way data flow should be followed in vue

  1. When the data of the parent component changes, the child component will automatically change
  2. The child component cannot directly modify the props passed by the parent component. Props is read-only

What the parent component passes to the child component is an object. When the child component modifies the properties of the object, it will not report an error. The object is a reference type and updates each other

Summary: the value of props cannot be re assigned. The attribute value of object reference relationship changes and affects each other

2.2_vue component communication_ Unidirectional data flow

Target: props variable itself is read-only and cannot be re assigned

Target: the data flow from parent to child is called one-way data flow

Cause: the child component is modified without notifying the parent, resulting in data inconsistency

If the first MyProduct Vue modifies the commodity price to 5.5, but app 18.8 was still recorded in Vue - the data is inconsistent

So: Vue specifies that the variables in props are read-only

Summary: therefore, the props variable itself cannot be re assigned

Question: how can I modify the value received by the subcomponent? In fact, it will affect the father, and then the data response will affect the sons

2.3_vue component communication_ Son to father

Objective: pass the value from the sub component to the outside for use

Requirements: examples in class, bargaining function, sub components Click to realize random bargaining-1 function

Syntax:

  • Custom method: @ parent event name
  • Sub: this$ Emit ("custom event name", value transfer) - execute the function code in the parent methods

components/MyProduct_sub.vue

<template>
  <div class="my-product">
    <h3>title: {{ title }}</h3>
    <p>Price: {{ price }}element</p>
    <p>{{ intro }}</p>
    <button @click="subFn">Treasure knife-Cut 1 yuan</button>
  </div>
</template>

<script>
import eventBus from '../EventBus'
export default {
  props: ['index', 'title', 'price', 'intro'],
  methods: {
    subFn(){
      this.$emit('subprice', this.index, 1) // Son to father
      eventBus.$emit("send", this.index, 1) // Cross component
    }
  }
}
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

App.vue

<template>
  <div>
    <!-- target: Son to father -->
    <!-- 1. Parent component, @Custom event name="father methods function" -->
    <MyProduct v-for="(obj, ind) in list" :key="obj.id"
    :title="obj.proname"
    :price="obj.proprice"
    :intro="obj.info"
    :index="ind"
    @subprice="fn"
    ></MyProduct>
  </div>
</template>

<script>

import MyProduct from './components/MyProduct_sub'
export default {
  data() {
    return {
      list: [
        {
          id: 1,
          proname: "Super delicious lollipop",
          proprice: 18.8,
          info: "Grand reward for opening, 20% off",
        },
        {
          id: 2,
          proname: "Super delicious big chicken leg",
          proprice: 34.2,
          info: "Delicious but not greasy, Come and buy it",
        },
        {
          id: 3,
          proname: "Super invincible ice cream",
          proprice: 14.2,
          info: "hot summer , Have an ice cream",
        },
      ],
    };
  },
  components: {
    MyProduct
  },
  methods: {
    fn(inde, price){
      // Logic code
      this.list[inde].proprice > 1 && (this.list[inde].proprice = (this.list[inde].proprice - price).toFixed(2))
    }
  }
};
</script>

<style>
</style>

Summary: define the parent event and method, and wait for the child component to trigger the event to pass the value to the method

2.4_ Stage summary

Objective: To summarize the parent-child component relationship - Communication Technology pithy

What are the components

  • Is a vue instance that encapsulates tags, styles, and JS code

Component benefits

  • Easy to reuse and expand

What kinds of component communication and how to implement it

  • Parent - > child

  • Father < - son

2.5_vue component communication - EventBus

Objective: commonly used when communicating across components

The relationship between the two components is very complex, and it is very troublesome to communicate through parent-child components. At this time, a general component communication scheme can be used: event bus

Core grammar

EventBus/index.js - define event bus object

import Vue from 'vue'
// Export blank vue objects
export default new Vue()

List.vue registration event - waiting to receive the value to be haggled (direct copy) - prepare brother page

<template>
  <ul class="my-product">
      <li v-for="(item, index) in arr" :key="index">
          <span>{{ item.proname }}</span>
          <span>{{ item.proprice }}</span>
      </li>
  </ul>
</template>

<script>
export default {
  props: ['arr'],
}
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

components/MyProduct_ Sub.vue (take students to write the events that trigger eventBus)

<template>
  <div class="my-product">
    <h3>title: {{ title }}</h3>
    <p>Price: {{ price }}element</p>
    <p>{{ intro }}</p>
    <button @click="subFn">Treasure knife-Cut 1 yuan</button>
  </div>
</template>

<script>
import eventBus from '../EventBus'
export default {
  props: ['index', 'title', 'price', 'intro'],
  methods: {
    subFn(){
      this.$emit('subprice', this.index, 1) // Son to father
      eventBus.$emit("send", this.index, 1) // Cross component
    }
  }
}
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

List.vue correct code (EventBus receiver)

<template>
  <ul class="my-product">
    <li v-for="(item, index) in arr" :key="index">
      <span>{{ item.proname }}</span>
      <span>{{ item.proprice }}</span>
    </li>
  </ul>
</template>

<script>
// Target: value transfer across components
// 1. Introduce a blank vue object (EventBus)
// 2. Receiver - $on listening event
import eventBus from "../EventBus";
export default {
  props: ["arr"],
  // 3. After the component is created, listen to the send event
  created() {
    eventBus.$on("send", (index, price) => {
      this.arr[index].proprice > 1 &&
        (this.arr[index].proprice = (this.arr[index].proprice - price).toFixed(2));
    });
  },
};
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

Summary: an empty Vue object is only responsible for the $on registration event. e m i t triggers the event. Be sure to ensure that the event is triggered by emit. Be sure to ensure that the event is triggered by emit. Be sure to ensure that on executes first

2.6_ Component communication description

Vue's component communication is not only these three methods, but also several methods. If you want to know, you can check it in the article before Xiaobian. Here, Xiaobian only lists the three methods commonly used above for you

3. todo case

Complete effect demonstration

3.0_todo case - creating projects and components

Goal: build a new project and get everything ready

  • Requirement 1: create a new project
  • Requirement 2: component creation – prepare labels and styles (copy from. md notes)

analysis:

① : initialize todo project

② : create 3 components and code inside (copy in preview. md)

③ : prepare the style file of styles (copy from preview)

④: App.vue is introduced for registration. The outermost container class name is todoapp

Prepare in advance: prepare the style file of styles (copy it from the preview materials) in app Vue introduction and use

// 1.0 Style Introduction
import "./styles/base.css"
import "./styles/index.css"

According to the requirements: we define three components for reuse

components/TodoHeader.vue - copy label and class name

<template>
  <header class="header">
    <h1>todos</h1>
    <input id="toggle-all" class="toggle-all" type="checkbox" >
    <label for="toggle-all"></label>
    <input
      class="new-todo"
      placeholder="Enter task name-Enter to confirm"
      autofocus
    />
  </header>
</template>

<script>
export default {
 
}
</script>

components/TodoMain.vue - copy label and class name

<template>
  <ul class="todo-list">
    <!-- completed: Completed class name -->
    <li class="completed" >
      <div class="view">
        <input class="toggle" type="checkbox" />
        <label>Task name</label>
        <button class="destroy"></button>
      </div>
    </li>
  </ul>
  
</template>

<script>
export default {
}
</script>

components/TodoFooter.vue - copy label and class name

<template>
  <footer class="footer">
    <span class="todo-count">surplus<strong>Quantity value</strong></span>
    <ul class="filters">
      <li>
        <a class="selected" href="javascript:;" >whole</a>
      </li>
      <li>
        <a href="javascript:;">hang in the air</a>
      </li>
      <li>
        <a href="javascript:;" >Completed</a>
      </li>
    </ul>
    <button class="clear-completed" >Cleanup completed</button>
  </footer>
</template>

<script>
export default {

}
</script>

App. Introduction and use in Vue

<template>
  <section class="todoapp">
    <!-- Except for the hump, You can also use-Conversion link -->
    <TodoHeader></TodoHeader>
    <TodoMain></TodoMain>
    <TodoFooter></TodoFooter>
  </section>
</template>

<script>
// 1.0 Style Introduction
import "./styles/base.css"
import "./styles/index.css"
    
import TodoHeader from "./components/TodoHeader";
import TodoMain from "./components/TodoMain";
import TodoFooter from "./components/TodoFooter";


export default {
  components: {
    TodoHeader,
    TodoMain,
    TodoFooter,
  },
};
</script>

3.1_todo case - laying to-do tasks

Purpose: to display the to-do tasks to todomain On Vue assembly

  • Requirement 1: display the to-do tasks to todomain On Vue assembly
  • Requirement 2: Associate selected status and set related styles

analysis:

①: App.vue – prepare the array to pass into todomain Within Vue

② : v-for loop display data

③ The: v-model binding check box is selected

④ : set the finish dash style according to the selected status

App.vue

 <TodoMain :arr="showArr"></TodoMain>

export default {
  data() {
    return {
      list: [
        { id: 100, name: "having dinner", isDone: true },
        { id: 201, name: "sleep", isDone: false },
        { id: 103, name: "Beat beans", isDone: true },
      ],
    };
  }
};

TodoMain.vue

<template>
  <ul class="todo-list">
    <!-- 2.2 Circular task-Association selected status-Laying data -->
    <!-- completed: Completed class name -->
    <li :class="{completed: obj.isDone}" v-for="(obj, index) in arr" :key='obj.id'>
      <div class="view">
        <input class="toggle" type="checkbox" v-model="obj.isDone"/>
        <label>{{ obj.name }}</label>
        <!-- 4.0 Register click event -->
        <button @click="delFn(index)" class="destroy"></button>
      </div>
    </li>
  </ul>
</template>

<script>
export default {
  props: ["list"]
};
</script>

<style>
</style>

3.2_todo case - add task

Objective: enter the task name to be completed in the top input box and click enter to complete the new function

  • Demand: enter a task and press enter to add a to-do task

analysis:

①: TodoHeader.vue – input box – keyboard event – enter key

② : pass the child to the parent and put the to-do task – app Vue – add to the array list

③ : if the original array is changed, all the places used will be updated

④ : the input box is empty, prompting the user to enter content

TodoHeader.vue

<template>
  <header class="header">
    <h1>todos</h1>
    <input id="toggle-all" class="toggle-all" type="checkbox" v-model="isAll">
    <label for="toggle-all"></label>
    <!-- 3.0 Keyboard events-Enter key
         3.1 Input box - v-model Get value
     -->
    <input
      class="new-todo"
      placeholder="Enter task name-Enter to confirm"
      autofocus
      @keydown.enter="downFn"
      v-model="task"
    />
  </header>
</template>

<script>
// 3. Objective - new task
export default {
  data(){
    return {
      task: ""
    }
  },
  methods: {
    downFn(){
      if (this.task.trim().length === 0) {
        alert("Task name cannot be empty");
        return;
      }
      // 3.2 (important) - the name of the current task should be added to the list array
      // Son to father Technology
      this.$emit("create", this.task)
      this.task = ""
    }
  }
}
</script>

App.vue

<TodoHeader @create="createFn"></TodoHeader>

methods: {
   createFn(taskName){ // Add task
      // 3.3 push to array
      let id = this.list.length == 0 ? 100 : this.list[this.list.length - 1].id + 1
      this.list.push({
        id: id,
        name: taskName,
        isDone: false
      })
    },
}

3.3_todo case - delete task

Objective: to achieve point x and delete the task function

  • Requirement: click the x after the task to delete the current task

analysis:

① : x tab – click event – incoming id

② : pass the child to the father, and return the id – app In Vue – delete a corresponding object in the array list

③ : if the original array is changed, all the places used will be updated

App.vue - incoming custom event waiting to receive sequence number to be deleted

<TodoMain :arr="showArr" @del="deleteFn"></TodoMain>

methods: {
    deleteFn(theId){ // Delete task
      let index = this.list.findIndex(obj => obj.id === theId)
      this.list.splice(index, 1)
    },
},

TodoMain.vue - pass the id back to delete (delete the data wherever you want)

<!-- 4.0 Register click event -->
<button class="destroy" @click="delFn(obj.id)"></button>

methods: {
     delFn(id){
      // 4.1 son to father
      this.$emit('del', id)
    }
}

3.4_todo case - bottom statistics

Objective: to display the total number of current tasks

  • Demand: count the number of current tasks

analysis:

①: App.vue - array list - passed to todofooter vue

② : display / define calculation attributes directly on the label for display

③ : as long as the original array is changed, all places where this array is used will be updated

TodoFooter.vue - receive list statistics direct display

<template>
  <footer class="footer">
    <span class="todo-count">surplus<strong>{{ count }}</strong></span>
    <ul class="filters">
      <li>
        <a class="selected" href="javascript:;">whole</a>
      </li>
      <li>
        <a href="javascript:;">hang in the air</a>
      </li>
      <li>
        <a href="javascript:;">Completed</a>
      </li>
    </ul>
    <button class="clear-completed">Cleanup completed</button>
  </footer>
</template>

<script>
export default {
  // 5.0 props definition
  props: ['farr'],
  // 5.1 calculation attribute - task quantity
  computed: {
    count(){
      return this.farr.length
    }
  },
}
</script>

<style>

</style>

App.vue - incoming data

<TodoFooter :farr="showArr"></TodoFooter>

3.5_todo case - data switching

Purpose: click the bottom to switch data

  • Requirement 1: click the switch at the bottom – who has the border
  • Requirement 2: switch different data display correspondingly

analysis:

①: TodoFooter.vue – define isSel – the value is all, yes, no, one of them

② selected: multiple classes judge who should have the class name respectively

③ : Click to modify the value of isSel

④ : pass the child to the parent, and pass the type isSel to app vue

⑤ : define the calculation attribute showArr and decide which data to display from the list to todomain Vue and todofooter vue

App.vue

<TodoFooter :farr="showArr" @changeType="typeFn"></TodoFooter>

<script>
    export default{
       data(){
            return {
              // ... Other omissions
              getSel: "all" // Show all by default
            }
        },
        methods: {
            // ... Other omissions
            typeFn(str){ // 'all', 'yes',' no '/ / modify type
              this.getSel = str
            },
        },
        // 6.5 define showArr array - filtered by list matching conditions
          computed: {
            showArr(){
              if (this.getSel === 'yes') { // Show completed
                return this.list.filter(obj => obj.isDone === true)
              } else if (this.getSel === 'no') { // Show incomplete
                return this.list.filter(obj => obj.isDone === false)
              } else {
                return this.list // show all
              }
            }
          },
    }
</script>

TodoFooter.vue

<template>
  <footer class="footer">
    <span class="todo-count">surplus<strong>{{ count }}</strong></span>
    <ul class="filters" @click="fn">
      <li>
        <!-- 6.1 Determine who should have a highlighted style: dynamic class
            6.2 User clicks to switch isSel Values saved in
         -->
        <a :class="{selected: isSel === 'all'}" href="javascript:;" @click="isSel='all'">whole</a>
      </li>
      <li>
        <a :class="{selected: isSel === 'no'}" href="javascript:;" @click="isSel='no'">hang in the air</a>
      </li>
      <li>
        <a :class="{selected: isSel === 'yes'}" href="javascript:;" @click="isSel='yes'">Completed</a>
      </li>
    </ul>
    <!-- 7. target: Cleanup completed -->
    <!-- 7.0 Click event -->
    <button class="clear-completed" >Cleanup completed</button>
  </footer>
</template>

<script>
// 5. Objective: quantity statistics
export default {
  // 5.0 props definition
  props: ['farr'],
  // 5.1 calculation attribute - task quantity
  computed: {
    count(){
      return this.farr.length
    }
  },
  // 6. Goal: who lights up
  // 6.0 variable isSel
  data(){
    return {
      isSel: 'all' // All: 'all', completed 'yes', incomplete' no '
    }
  },
  methods: {
    fn(){ // Toggle filter criteria
      // 6.3 child - > parent type string passed to app vue 
      this.$emit("changeType", this.isSel)
    }
  }
}
</script>

3.6_todo case - emptying completed

Purpose: click the button in the lower right corner to clear the completed tasks

  • Requirements: click the link label in the lower right corner to clear the completed tasks

analysis:

① : empty tag – click event

② Child to parent – app Vue – an emptying method

③ : filter the incomplete overlay list array (regardless of recovery)

App.vue - pass in a custom event first - because you have to receive todofooter Click events in Vue

<TodoFooter :farr="showArr" @changeType="typeFn" @clear="clearFun"></TodoFooter>

<script>
    methods: {
        // ... Omit others
        clearFun(){ // Cleanup completed
          this.list = this.list.filter(obj => obj.isDone == false)
        }
    }
</script>

TodoFooter.vue

<!-- 7. target: Cleanup completed -->
<!-- 7.0 Click event -->
<button class="clear-completed" @click="clearFn">Cleanup completed</button>

<script>
	methods: {
        clearFn(){ // Empty completed tasks
          // 7.1 trigger app clearFun method corresponding to events in Vue
          this.$emit('clear')
        }
    }
</script>

3.7_todo case - data cache

Purpose: after adding / modifying the status / deleting, synchronize the data to the local storage of the browser immediately

  • Requirements: no matter how it changes – ensure that the data is still after refresh

analysis:

①: App.vue - listening list array change - depth

② : overwrite save locally – note that only JSON strings can be saved locally

③ : refresh the page – the list should take value locally by default – empty array without data should be considered

App.vue

<script>
    export default {
        data(){
            return {
                // 8.1 default from local value
                list: JSON.parse(localStorage.getItem('todoList')) || [],
                // 6.4 first transfer receiving type string
                getSel: "all" // Show all by default
            }
        },
        // 8. Target: data cache
        watch: {
            list: {
                deep: true,
                handler(){
                    // 8.0 as long as the list changes - overwrite save to localStorage
                    localStorage.setItem('todoList', JSON.stringify(this.list))
                }
            }
        }
    };
</script>

3.8_todo case - select all function

Objective: click the v sign in the upper left corner to set one key to complete, and then click again to cancel the selection of all

  • Requirement 1: click Select all – the small selection box is affected
  • Requirement 2: select all the small boxes (manually) – select all and select automatically

analysis:

①: TodoHeader.vue – calculate attributes - isAll

②: App.vue – pass in the array list – affect the small selection box in the set of isAll

③ : count the last status of the small selection box in the get of isAll, affect isAll – affect the status of all selection

④ : consider the empty array without data - select all and should not be checked

Tip: it is to traverse all objects and modify the value of their completion status attribute

TodoHeader.vue

<!-- 9. target: Select all status
9.0 v-model Associated select all status
 Page change(Check true, Unchecked false) -> v-model -> isAll variable
-->
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="isAll">

<script>
    export default {
        // ... Other omissions
        // 9.1 defining calculation attributes
        computed: {
            isAll: {
                set(checked){ // Only true / false
                    // 9.3 affect the isDone attribute bound by each small checkbox in the array
                    this.arr.forEach(obj => obj.isDone = checked)
                },
                get(){
                    // 9.4 statistical status of small selection box - > select all box
                    // 9.5 if there is no data, return false directly - do not make all checked
                    return this.arr.length !== 0 && this.arr.every(obj => obj.isDone === true)
                }
            }
        },
    }
</script>

App.vue

<TodoHeader :arr="list" @create="createFn"></TodoHeader>

Today's summary

  • Concept and function of components and how they are created and used

  • Master component communication, including parent to child and child to parent value transmission

  • Familiar with the use and principle of EventBus

Interview questions

1. Please describe the process of packaging vue components

First, components can improve the development efficiency of the whole project. It can abstract the page into several relatively independent modules, which solves the problems of our traditional project development: low efficiency, difficult maintenance, reusability and so on.

  • Analyze requirements: determine the business requirements and separate the reusable structures, styles and functions in the page into a component to realize reuse

  • Specific steps: Vue Component or in the new Vue configuration item components, define the component name. You can accept the parameters and values passed to the component in props. After modifying the data, the sub component wants to pass the data to the parent component. You can use the $emit method.

2. How do Vue components transfer values

Parent to child - > props definition variable - > parent in use component passes value to props variable with attribute

The child triggers the parent's event to the parent - > $emit - > the parent is using the method of @ custom event name = parent (the child brings out the value)

3. Why must Vue component data be a function

Each component is an instance of Vue. For independent scope, variables are not allowed to pollute other people's variables

4. Let's talk about the naming specification of components

There are two ways to name components (in Vue.Component/components). One is to use the chain to name "my component" and the other is to use the big hump to name "MyComponent",

Because we should follow the custom component name in the W3C specification (all lowercase letters and must contain a hyphen), so as to avoid conflicts with current and future HTML elements

Additional exercises_ 1. Do you like little dogs

Objective: to encapsulate the Dog component, which is used to reuse the display of pictures and titles

effect:

Correct answer (don't look at it first)

components/practise/Dog1.vue

<template>
  <div class="my_div">
    <img
      src="https://scpic.chinaz.net/files/pic/pic9/202003/zzpic23514.jpg"
      alt=""
    />
    <p>This is a lonely and poor dog</p>
  </div>
</template>

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

<style>
.my_div {
  width: 200px;
  border: 1px solid black;
  text-align: center;
  float: left;
}

.my_div img {
  width: 100%;
  height: 200px;
}
</style>

On app Used in Vue

<template>
  <div>
    <Dog></Dog>
    <Dog/>
  </div>
</template>

<script>
import Dog from '@/components/practise/Dog1'
export default {
  components: {
    Dog
  }
}
</script>

<style>

</style>

Summary: the repeated parts are encapsulated into components and then registered for use

Additional exercises_ 2. Click the text to change color

Objective: to modify the Dog component and realize the click color change in the component

Tip: text is in the component, so events and methods should be in the component - independent

Illustration:

Correct code (don't look at it first)

components/practise/Dog2.vue

<template>
  <div class="my_div">
    <img
      src="https://scpic.chinaz.net/files/pic/pic9/202003/zzpic23514.jpg"
      alt=""
    />
    <p :style="{backgroundColor: colorStr}" @click="btn">This is a lonely and poor dog</p>
  </div>
</template>

<script>
export default {
  data(){
    return {
      colorStr: ""
    }
  },
  methods: {
    btn(){
      this.colorStr = `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)})`
    }
  }
};
</script>

<style>
.my_div {
  width: 200px;
  border: 1px solid black;
  text-align: center;
  float: left;
}

.my_div img {
  width: 100%;
  height: 200px;
}
</style>

Additional exercises_ 3. Selling dogs

Objective: to recycle data with component display

Data:

[
    {
        dogImgUrl:
        "http://nwzimg.wezhan.cn/contents/sitefiles2029/10146688/images/21129958.jpg",
        dogName: "Pomeranian",
    },
    {
        dogImgUrl:
        "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1224576619,1307855467&fm=26&gp=0.jpg",
        dogName: "poodle",
    },
    {
        dogImgUrl:
        "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2967740259,1518632757&fm=26&gp=0.jpg",
        dogName: "Golden hair",
    },
    {
        dogImgUrl:
        "https://pic1.zhimg.com/80/v2-7ba4342e6fedb9c5f3726eb0888867da_1440w.jpg?source=1940ef5c",
        dogName: "Siberian Husky",
    },
    {
        dogImgUrl:
        "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1563813435580&di=946902d419c3643e33a0c9113fc8d780&imgtype=0&src=http%3A%2F%2Fvpic.video.qq.com%2F3388556%2Fd0522aynh3x_ori_3.jpg",
        dogName: "Alaska",
    },
    {
        dogImgUrl:
        "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1563813454815&di=ecdd2ebf479568453d704dffacdfa12c&imgtype=0&src=http%3A%2F%2Fwww.officedoyen.com%2Fuploads%2Fallimg%2F150408%2F1-15040Q10J5B0.jpg",
        dogName: "Samoye",
    },
]

Illustration:

Correct code (not reproducible)

components/practise/Dog3.vue

<template>
  <div class="my_div">
    <img :src="imgurl" alt="" />
    <p :style="{ backgroundColor: colorStr }" @click="btn">{{ dogname }}</p>
  </div>
</template>

<script>
export default {
  props: ["imgurl", "dogname"],
  data() {
    return {
      colorStr: "",
    };
  },
  methods: {
    btn() {
      this.colorStr = `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(
        Math.random() * 256
      )}, ${Math.floor(Math.random() * 256)})`;

      
    },
  },
};
</script>

<style scoped>
.my_div {
  width: 200px;
  border: 1px solid black;
  text-align: center;
  float: left;
}

.my_div img {
  width: 100%;
  height: 200px;
}
</style>

App.vue is introduced and used to transfer the data cycle to the component display

<template>
  <div>
    <Dog v-for="(obj, index) in arr"
    :key="index"
    :imgurl="obj.dogImgUrl"
    :dogname="obj.dogName"
    ></Dog>
  </div>
</template>

<script>
import Dog from '@/components/practise/Dog3'
export default {
  data() {
    return {
      // 1. Prepare data
      arr: [
        {
          dogImgUrl:
            "http://nwzimg.wezhan.cn/contents/sitefiles2029/10146688/images/21129958.jpg",
          dogName: "Pomeranian",
        },
        {
          dogImgUrl:
            "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1224576619,1307855467&fm=26&gp=0.jpg",
          dogName: "poodle",
        },
        {
          dogImgUrl:
            "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2967740259,1518632757&fm=26&gp=0.jpg",
          dogName: "Golden hair",
        },
        {
          dogImgUrl:
            "https://pic1.zhimg.com/80/v2-7ba4342e6fedb9c5f3726eb0888867da_1440w.jpg?source=1940ef5c",
          dogName: "Siberian Husky",
        },
        {
          dogImgUrl:
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1563813435580&di=946902d419c3643e33a0c9113fc8d780&imgtype=0&src=http%3A%2F%2Fvpic.video.qq.com%2F3388556%2Fd0522aynh3x_ori_3.jpg",
          dogName: "Alaska",
        },
        {
          dogImgUrl:
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1563813454815&di=ecdd2ebf479568453d704dffacdfa12c&imgtype=0&src=http%3A%2F%2Fwww.officedoyen.com%2Fuploads%2Fallimg%2F150408%2F1-15040Q10J5B0.jpg",
          dogName: "Samoye",
        },
      ],
    };
  },
  components: {
    Dog
  }
};
</script>

Additional exercises_ 4. Choose your favorite dog

Objective: the user clicks the dog's name and displays the name once in the list on the right

effect:

Correct code (not reproducible)

components/practise/Dog4.vue

<template>
  <div class="my_div">
    <img :src="imgurl" alt="" />
    <p :style="{ backgroundColor: colorStr }" @click="btn">{{ dogname }}</p>
  </div>
</template>

<script>
export default {
  props: ["imgurl", "dogname"],
  data() {
    return {
      colorStr: "",
    };
  },
  methods: {
    btn() {
      this.colorStr = `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(
        Math.random() * 256
      )}, ${Math.floor(Math.random() * 256)})`;

      // Supplement: trigger parent event
      this.$emit("love", this.dogname);
    },
  },
};
</script>

<style scoped>
.my_div {
  width: 200px;
  border: 1px solid black;
  text-align: center;
  float: left;
}

.my_div img {
  width: 100%;
  height: 200px;
}
</style>

App.vue

<template>
  <div>
    <Dog
      v-for="(obj, index) in arr"
      :key="index"
      :imgurl="obj.dogImgUrl"
      :dogname="obj.dogName"
      @love="fn"
    ></Dog>

    <hr />
    <p>Show favorite dog:</p>
    <ul>
      <li v-for="(item, index) in loveArr" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script>
import Dog from "@/components/practise/Dog4";
export default {
  data() {
    return {
      // 1. Prepare data
      arr: [
        {
          dogImgUrl:
            "http://nwzimg.wezhan.cn/contents/sitefiles2029/10146688/images/21129958.jpg",
          dogName: "Pomeranian",
        },
        {
          dogImgUrl:
            "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1224576619,1307855467&fm=26&gp=0.jpg",
          dogName: "poodle",
        },
        {
          dogImgUrl:
            "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2967740259,1518632757&fm=26&gp=0.jpg",
          dogName: "Golden hair",
        },
        {
          dogImgUrl:
            "https://pic1.zhimg.com/80/v2-7ba4342e6fedb9c5f3726eb0888867da_1440w.jpg?source=1940ef5c",
          dogName: "Siberian Husky",
        },
        {
          dogImgUrl:
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1563813435580&di=946902d419c3643e33a0c9113fc8d780&imgtype=0&src=http%3A%2F%2Fvpic.video.qq.com%2F3388556%2Fd0522aynh3x_ori_3.jpg",
          dogName: "Alaska",
        },
        {
          dogImgUrl:
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1563813454815&di=ecdd2ebf479568453d704dffacdfa12c&imgtype=0&src=http%3A%2F%2Fwww.officedoyen.com%2Fuploads%2Fallimg%2F150408%2F1-15040Q10J5B0.jpg",
          dogName: "Samoye",
        },
      ],
      loveArr: []
    };
  },
  components: {
    Dog,
  },
  methods: {
    fn(dogName) {
      this.loveArr.push(dogName)
    },
  },
};
</script>

<style >
</style>

Additional exercises_ 5. Sold out

Objective: to complete the selling out effect of the figure

Requirements:

  • If it is 0, it will be displayed that it is sold out!!!
  • If the inventory has value, it will not be sold out!!!
  • If the inventory has a value, the cumulative total quantity of goods

Requirement: a line is a component for reuse. Here, it is required to use Table > tr (that is, encapsulate tr components)

Note for component usage: html is parsed normally, and Table > tr or Select > option. Although vue rendering page can be customized, it also needs to follow the label relationship of the browser

  • You cannot use components directly in Table > tr. you need to specify the component name in the is attribute of tr
  • Select > option also cannot encapsulate the options component. You need to specify the component name in the is attribute of option

Effect demonstration:

The array in vue instance data is as follows

goodsArr: [
    {
        count: 0,
        goodsName: "Watermelon"
    }, {
        count: 0,
        goodsName: "Banana"
    }, {
        count: 0,
        goodsName: "Orange"
    }, {
        count: 0,
        goodsName: "Pineapple"
    }, {
        count: 0,
        goodsName: "Strawberry"
    }
]

Correct code (not reproducible)

components/practise/MyTr.vue

<template>
  <tr>
    <td>
      <input type="number" v-model.number="obj['count']"/>
    </td>
    <td>
      <span>{{ obj["goodsName"] }}</span>
    </td>
    <td>
      <span v-show="obj['count'] == 0">Sold out!!!</span>
    </td>
  </tr>
</template>

<script>
export default {
    // Importing objects is risky, but if it is a one-to-one relationship, you can import objects - directly modify the value in the object to affect the external effect
    props: ["obj"]
};
</script>

<style>
</style>

App.vue use

<template>
  <div>
    <table>
      <!-- 2. use tr assembly, Incoming required data -->
      <tr
        is="myTr"
        v-for="(item, index) in goodsArr"
        :key="index"
        :obj="item"
        :index="index"
      ></tr>
    </table>
    <p>All Number:{{ sumNumber }}</p>
  </div>
</template>

<script>
import MyTr from '@/components/practise/MyTr'
export default {
  data() {
    return {
      goodsArr: [
        {
          count: 0,
          goodsName: "Watermelon",
        },
        {
          count: 0,
          goodsName: "Banana",
        },
        {
          count: 0,
          goodsName: "Orange",
        },
        {
          count: 0,
          goodsName: "Pineapple",
        },
        {
          count: 0,
          goodsName: "Strawberry",
        },
      ],
    };
  },
  components: {
    MyTr
  },
  computed: {
    sumNumber(){
      return this.goodsArr.reduce((sum, obj) => sum += obj.count * 1, 0)
    }
  }
};
</script>

<style>
</style>

Additional exercises_ 6. Buy something delicious

Objective: display the commodity list, then package the components to realize the functions of increase and decrease, and count the total price at the end

Requirements: trade name, increase the quantity, reduce this one, and package it into components for use

Effect demonstration:

Data:

[
    {
        "shopName": "Cobic chips",
        "price": 5.5,
        "count": 0
    },
    {
        "shopName": "strawberry jam",
        "price": 3.5,
        "count": 0
    },
    {
        "shopName": "braised pork in brown sauce",
        "price": 55,
        "count": 0
    },
    {
        "shopName": "instant noodles",
        "price": 12,
        "count": 0
    }
]

Correct code (not reproducible)

components/practise/Food.vue

<template>
  <div>
    <span>{{ goodsname }}</span>
    <button @click="add(ind)">+</button>
    <span> {{ count }} </span>
    <button @click="sec(ind)">-</button>
  </div>
</template>

<script>
export default {
    props: ['goodsname', 'ind', 'count'], // Trade name, index, quantity
    methods: {
        add(ind){
            this.$emit('addE', ind)
        },
        sec(ind){
            this.$emit("secE", ind)
        }
    }
};
</script>

App.vue

<template>
  <div>
    <p>The list of goods is as follows:</p>
    <div v-for="(obj, index) in shopData" :key="index">
      {{ obj.shopName }} -- {{ obj.price }}element/share
    </div>
    <p>Please select the purchase quantity:</p>
    <Food
      v-for="(obj, index) in shopData"
      :key="index + ' '"
      :goodsname="obj.shopName"
      :ind="index"
      :count="obj.count"
      @addE="addFn"
      @secE="secFn"
    >
    </Food>
    <p>The total price is: {{ allPrice }}</p>
  </div>
</template>

<script>
import Food from "@/components/practise/Food";
export default {
  data() {
    return {
      // Commodity data
      shopData: [
        {
          shopName: "Cobic chips",
          price: 5.5,
          count: 0,
        },
        {
          shopName: "strawberry jam",
          price: 3.5,
          count: 0,
        },
        {
          shopName: "braised pork in brown sauce",
          price: 55,
          count: 0,
        },
        {
          shopName: "instant noodles",
          price: 12,
          count: 0,
        },
      ],
    };
  },
  components: {
    Food,
  },
  methods: {
    addFn(ind){
      this.shopData[ind].count++
    },
    secFn(ind){
      this.shopData[ind].count > 0 && this.shopData[ind].count--
    }
  },
  computed: {
    allPrice(){
      return this.shopData.reduce((sum, obj) => sum += obj.count * obj.price, 0)
    }
  }
};
</script>

Write at the end

It's not easy to be original. I also hope you guys can support extcolor{blue} {it's not easy to be original. I also hope you guys can support} it's not easy to be original. I also hope you guys can support it

?? Praise, your recognition is the driving force of my creation! extcolor{green} {like it, your recognition is the driving force of my creation!} Praise, your recognition is the driving force of my creation!

Collection, your favor is the direction of my efforts! extcolor{green} {collection, your favor is the direction of my efforts!} Collection, your favor is the direction of my efforts!

Comment, your opinion is the wealth of my progress! extcolor{green} {comment, your opinion is the wealth of my progress!} Comment, your opinion is the wealth of my progress!

Keywords: Javascript Front-end html5 html

Added by smclay on Mon, 07 Mar 2022 03:13:15 +0200