Writing reusable components is a very common requirement in actual development. According to different requirements, there will be many different components, including simple popover components such as Alert, and more complex calendar components

Today let’s write component (Vue component) and see if we can master any knowledge points from it!!

Write a multicast diagram component

The rote map component must be one of the most used components, especially each home page is bound to appear in its figure.

So, we are idle talk less, the implementation of a suitable for mobile terminal on the wheel of the map component

To explode as first

A quick note: To implement component development, look at the following three pieces of logic

The core logic

  1. Initialize the rote map
  2. Automatically play
  3. Toggle left and right wheel broadcast chart

To do a good job, we must sharpen our tools. First, anyway, let’s introduce the components. Okay

The introduction of the component

The use of components is always introduced first, and without exception, see component references below

// app. vue file -> js section <script> // Import Swiper and SwiperItem components import Swiper from'./components/Swiper/Swiper';
import SwiperItem from './components/Swiper/SwiperItem';

export{Swiper, SwiperItem}} </script>Copy the code

The js section has registered the imported components with the current App component, so let’s start using them in the template

// app. vue file -> HTML section <template> <div id="app">
        <Swiper v-model="currentId">
            <SwiperItem :id="currentId"> <div> </div> <SwiperItem :id="currentId"> <div> </div> <SwiperItem :id="currentId"The third picture > < div > < / div > < / SwiperItem > < / Swiper > < / div > < / template >Copy the code

Do you have a lot of question marks? Take your time and listen to the wind

There are three SwiperItem components in the Swiper component, and the contents of the SwiperItem component are exactly the same

So, instead of writing it three times, just render it with an array through V-for

data

Here I simply use node to write an interface, return data, if you do not want to write an interface, I directly posted the data, let you see intuitively

The mock data

Const Albums = [{"id": 1, "title": "Ye Huimei"."public": 2003, "song": 'sunny'."img": "Https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=281827478, & FM = 26 & gp = 0. 1045705647 JPG"},
    { "id": 2."title": "Qilixiang"."public": 2004, "song": 'Qilixiang'."img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584873891083&di=7892d2142e6aba7e203d270e20599235&i mgtype=0&src=http%3A%2F%2Fpic.rmb.bdstatic.com%2Fc1505303db7c257f248adc87b6e22fd5.jpeg"},
    { "id": 3."title": "Chopin in November."."public": 2005, "song": 'his'."img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584873911041&di=51fc38d5805edc63fdd7301dbcef316f&i mgtype=0&src=http%3A%2F%2Fzximg.ingping.com%2Fueditor%2Fjsp%2Fupload%2F201705%2F201705031153520358724.jpg"},
    { "id": 4."title": "Still Fantsea."."public": 2006, "song": 'Listen to your mother'."img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584873937432&di=ebf0092e78d5499f54728eeb43449414&i mgtype=0&src=http%3A%2F%2Fimg1.dongqiudi.com%2Ffastdfs2%2FM00%2F66%2FBA%2FChOqM1rO_0mADp5rAACEhAjn7aY043.jpg"}];Copy the code

/getalbums {code: 0, data: albumes}

In the previous directory structure, you should see the API /shop.js file. Here is the code I wrapped to request the data of the round cast graph

// API /shop.js file import axios from'axios'; / / to intercept the returned response data axios. Interceptors. Response. Use (res = > res. Data);export default {
    async getAlbums() {
        let { data } = await axios.get('/api/getalbums');
        returndata; }}Copy the code

The request data

Now let’s go back to app.vue and start the request operation, then V-for iterates through the array and renders the SwiperItem component

// app. vue file -> js section <script> import Swiper from'./components/Swiper/Swiper';
import SwiperItem from './components/Swiper/SwiperItem'; // add a wrapped request method // @ is webpackaliasAn alias for the SRC directory import shop from'@/api/shop';

export default {
    dataSliders: [], // currentId, currentId: 1}, asyncmounted() {// Request data and update slider array this.sliders = await shop.getalbums (); }, components: { Swiper, SwiperItem } } </script>Copy the code

Render data

Rendering the data is left to our template

// app. vue file -> HTML section <template> <div id="app">
        <Swiper v-model="currentId" v-if="sliders.length">
            <template v-for="item in sliders">
                <SwiperItem :key="item.id" :id="item.id">
                    <img :src="item.img" />
                </SwiperItem>
            </template>
        </Swiper>
    </div>
</template>
Copy the code

Why is the above template written like this?

  1. v-modelYou can also bind on components
    • On a componentv-modelBy default, names are usedvalueProp and namedinputIn the event
  2. v-ifThe control component does not render until it gets the data
  3. On the SwiperItemidThe dynamic property is the key to distinguishing which image should be displayed at the moment

The width of the img element can be defined in the CSS section of app. vue as follows: # App img {width: 100%; height: 220px; }, this limits the width and height looks more appropriate

Now, start developing as much as you can

Swiper components

Swiper has multiple SwiperItem components written inside it, so a slot slot is needed to distribute content and receive values from the V-Model binding data

Let’s take a look at the implementation logic

// swiper. vue file <template> <div class="swiper">
        <div class="view">
            <slot></slot>
        </div>
    </div>
</template>

<script>
exportDefault {props: {// v-model props: {type: Number,
            default: 1
        }
    }
}
</script>

<style scoped>
.swiper {
    position: relative;
    width: 100%;
    height: 220px;
    overflow: hidden;
}
</style>
Copy the code

After writing the basic logic, we’ll write the rest of the logic later. Let’s implement the SwiperItem component

SwiperItem components

The SwiperItem component is used to do just that, since only one image is currently displayed and the rest is hidden

// swiperitem. vue <template> <transition name="items">
        <div class="swiper-item" v-if="isShow">
            <slot></slot>
        </div>
    </transition>
</template>

<script>
export default {
    props: {
        id: {
            type: Number, // Required attribute required:true}},data() {
        return {
            selectedId: 1 
        }
    },
    computed: {
        isShow() {// check the v-if display for propsreturn this.id === this.selectedId;
        }
    }
}
</script>

<style scoped>
.items-enter-active,
.items-leave-active {
    transition: .5s linear;
}
.items-leave-to {
    transform: translateX(-100%);
}
.items-enter {
    transform: translateX(100%);
}
.items-enter-active {
    position: absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
}
</style>
Copy the code

Initialize the rote map

Going back to the Swiper component, we need to write a method to display the rotation map, so simply call it show method,

It iterates through the $children child instance, changes the value of selectedId in the child, and displays the initial carousel graph

// swiper. vue <script>exportdefault { ... Omitted,data() {
        return {
            index: 1
        }
    },
    methods: {
        show() {/ / to the index value of an assignment for the passed / / if there is no transfer value value, is the default for the first child in the component id value. This index = this. Value | | this.$children[0].id; // Iterate over the child components and modify the corresponding selectedId value this on the instance.$children.forEach(vm => { vm.selectedId = this.index; }); }},mounted() {// initialize this.show(); } } </script>Copy the code

Realize the round broadcast graph small point

There are often corresponding small points below the rotation chart, so that users can intuitively see a total of several rotation pictures. In fact, it is very easy to implement, because as long as we know how many data can be used to cycle out v-for corresponding

// swiper. vue file <template> <div class="swiper">
        <div class="view">
            <slot></slot>
        </div>
        <div class="dots">
            <span class="dot"
                  v-for="dot in len"
                  :key="dot" 
                  :class="{active: index === dot}">
            </span>
        </div>
    </div>
</template>

<script>
exportdefault { ... Omitted,data() {
        returnLen: 0}}, methods: {... len: 0}}, methods: {... Omit},mounted() {// initialize this.show(); This.len = this.len = this.len = this.$children.length; } } </script> <style scoped> .swiper { position: relative; width: 100%; height: 220px; overflow: hidden; } /* Swiper. dots {position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); } .swiper .dots .dot { width: 10px; height: 4px; border-radius: 6px; Background-color: rgba(255, 255, 255, 0.7); display: inline-block; margin: 0 2px; } .swiper .dots .active { width: 14px; background-color:#fff;
}
</style>
Copy the code

After some of the above toss and toss, the total should have a bit of the appearance of the bar, let us see the effect

Automatically play

Many of the wheel effects come with auto-play, so we’re not going to ignore them right now. We do this by passing autoplay prop to the Swiper component

// app. vue file -> HTML section <template> <div id="app">
        <Swiper v-model="currentId" v-if="sliders.length" :autoplay="true">... Omit </Swiper> </div> </template>Copy the code

The parent App passes autoplay, so let’s go back to our Swiper component

We will implement a play method, mainly to write a timer, and then change the value of value through the change method to achieve automatic play

// swiper. vue file -> js section <script>exportdefault { props: { ... Omit, autoplay: {type: Boolean,
            default: false}},data() {
        return {
            index: 1,
            len: 0
        }
    },
    methods: {
        show() {... }, // timer playsplay() {
            if (this.autoplay) {
                this.timer = setInterval(() => { this.change(this.index + 1); }, 3000); }}, // Toggle the rotation graph change(index) {// Handle the right boundary if len is exceededif(index === this.len + 1) {// Change the index back to 1. }else if(index === 0) { index = this.len; } / / by$emitTrigger the input event and change value to index value this.$emit('input', index); }, // Clear the timerstop() { clearInterval(this.timer); this.timer = null; }}, watch: {// this is the key // listen for value changes, value assigned to index, with each index change // call the show method again, refresh the viewvalue() { this.show(); }},mounted() {
        this.show();
        this.len = this.$children.length; // Call autoplay this.play(); } } </script>Copy the code

Take a look at the effect

Control switch

By default, images are moved left to -100% for autoplay. PrevIndex (mounted) specifies the prevIndex value in mounted

PrevIndex is compared to the current index. If the prevIndex value is greater than the current index, then the image needs to be moved to the right

Let’s go back to the SwiperItem component and add a dynamic class to it to display the positive and negative transitions

Transition in different directions

// swiperitem. vue <template> <transition name="items">
        <div class="swiper-item" v-if="isShow" :class="{direction}">
            <slot></slot>
        </div>
    </transition>
</template>

<script>
exportdefault { ... omitdata() {
        return{index: 1, // determine the direction:false} } } </script> <style scoped> ... Ellipse /* reverse transition */. Items - leave-to-direction {transform: translateX(100%); } .items-enter.direction { transform: translateX(-100%); } </style>Copy the code

Determine the direction

The above code modifies the transition effects in different directions for us, so without further ado, let’s look at the code

// swiper. vue file -> js section <script>exportdefault { ... Omit, methods: {show() {
            this.index = this.value || this.$children[0].id;
            
            this.$children.forEach(vm => {
                this.$nextTick(() => { vm.selectedId = this.index; }); / / if prevIndex is greater than the index is reversed by the vm. The direction = this. PrevIndex > enclosing the index; // Handle boundary cases for autoplayif (this.timer) {
                    ifPrevIndex === 1 && this.index === this.len) {// Handle the condition vm. Direction = from first to lasttrue;
                    } else ifPrevIndex === this.len && this.index === 1) {// Handle the condition vm. Direction =false; }}}); }, change(index) { this.prevIndex = this.index; . Omit}},mounted() {
        this.show();
        this.len = this.$children.length; this.play(); PrevIndex = this.index; } } </script>Copy the code

Simple combing:

  1. Modify the show method
  • prevIndexIt starts with undefined, because inmountedCall in ordershowWhen it comes to methods,prevIndexIt hasn’t been assigned toindex
  • The equivalent ofprevIndex > indexFor the first time, undefined and 1 will be compared, and then each time you switch images, callchangeMethod of time to give againprevIndexAssign theindexThe value of the
  • $nextTickIn order to invm.directionThe data is modified, waiting for DOM updates before modifying the correspondingselectedIdTo show the corresponding picture
  1. Modify the change method
  • The change method changes very little,this.prevIndex = this.indexThat is, every time you switch, you put the last oneindexAssigned to theprevIndex
  • For example: in the beginningprevIndexIs undefined,index1; whenprevIndexWhen phi is one,indexTwo, and so on, just so you get the idea

Now that we have the logic of the transition effect code, we need to finish the final step. Let’s add a touch event to the rotation diagram so that the user can switch between left and right

Add touch event

Now that the orientation logic is complete, it’s a piece of cake for the user to switch from left to right. The first step is to add touch events to the elements

// swiper. vue file <template> <div class="swiper">
        <div class="view" @touchstart="touchstart" @touchend="touchend"> <slot></slot> </div> ... Omit </div> </template> <script>exportdefault { ... // This. StartX = e.touches[0].pagex; // And stop autoplaying this.stop(); }, touchend(e) {// Calculate the difference between the starting point and the departure point when the finger is liftedletdisX = e.changedTouches[0].pageX - this.startX; // If it is less than 0, it is a forward switch (swipe left) // If it is less than 0, it is a reverse switch (swipe right)if (disX < 0) {
                this.change(this.index + 1);
            } else{ this.change(this.index - 1); } // After the switch, continue to autoplay this.play(); },... Omit}} </script>Copy the code

That’s it. Let’s see what it looks like

The last

Since a timer is used in the component, don’t forget to call stop to clear the timer beforeDestroy

Live and learn

Originally also wanted to write two other reusable components, but afraid of too many words, we look tired, just point it out, I will also put the corresponding address issued, so that everyone convenient reference

There is no end to learning. Thank you for watching