I. Project introduction

This project mainly referred to the layout of Qunar and completed the development of the home page, city selection page and details page.

  • Home page: realize the function of multi-region rotation, as well as the display of multi-region list;
  • City selection page: in this page to achieve the city display, city search, city on the right side of the letter and the left side of the block dynamic linkage effect, when the user in the city list switched to a new city, the corresponding city home page will follow the change;
  • Attractions detail page: Implements a common gallery component, as well as a recursively displayed list component.

1.1 technology stack

Vue 2.5: An incremental framework for building user interfaces

Vuex: State management mode developed specifically for vue.js applications.

Vue Router: is the official route manager of vue. js.

Keep-alive: An abstract component provided by Vue to cache components to save performance

Vue-awesome -swiper: vUE based on swiper and applicable to vUE, supporting server rendering and single-page applications.

Stylus: CSS preprocessor

Axios: an HTTP library based on promises

Better-scroll: a plug-in that focuses on solving the requirements of various scrolling scenarios on mobile terminals (supported by PC).

Webpack: A static module bundler for modern JavaScript applications.

EsLint: A tool to help check syntax errors in Javascript programming and standardize code style

Iconfont: Alibaba icon library

Fastclick: Solve the problem of 300ms click delay on mobile

1.2 Project Presentation

  1. Scenic spot Ticket Home Page

  1. City List page

  1. Site Details Page

1.3 Project Harvest

1. Understand the whole development process of VUE project and get familiar with the development of medium-sized VUE project

  • Vue Router to route multiple pages
  • Share data among Vuex components
  • Swiper plug-in to achieve page rotation effect
  • Axios to fetch Ajax data

2. Mobile page layout skills

Write a stylus for the front end

4. Split common components

5. Standard code writing

1.4 Project Catalog

Attach the project catalog and warehouse address

F:.│.babelrc │.editorConfig │.eslintignore │.eslintrc.js │.gitignore │.postcsrc Json │ package.json │ package.json │ readme.en.md │ Readme. md │ ├─build │ build.js │ check-versions Utils.js │ vue-loader.conf. Js │ webpack.dev.conf. Js │ webpack.prod.conf. Js │ ├─config │ Dev.env.js │ ├─ SRC │ ├─ ├─assets │ ├─ └ styles │ │ border Iconfont. CSS │ │ │ mixins. Styl │ │ │ reset. CSS │ │ │ varibles. Styl │ │ │ │ │ └ ─ iconfont │ │ iconfont. The eot │ │ Iconfont. SVG │ │ iconfont. The vera.ttf │ │ iconfont. Woff │ │ │ ├ ─ common │ │ ├ ─ fade │ │ │ FadeAnimation. Vue │ │ │ │ │ └ ─ gallary │ │ Gallary. Vue │ │ │ ├ ─ pages │ │ │testGit. Js │ │ │ │ │ ├ ─ city │ │ │ │ city. The vue │ │ │ │ │ │ │ └ ─ components │ │ │ Alphabet. Vue │ │ │ Header. The vue │ │ │ List. The vue │ │ │ Search. Vue │ │ │ │ │ ├ ─ the detail │ │ │ │ detail. Vue │ │ │ │ │ │ │ └ ─ components │ │ │ Banner. Vue │ │ │ Header. The vue │ │ │ List. The vue │ │ │ │ │ └ ─ home │ │ │ home. Vue │ │ │ │ │ └ ─ components │ │ Header. The vue │ │ Icons. The vue │ │ how. Vue │ │ Swiper. Vue │ │ fusion. Vue │ │ │ ├ ─ the router │ │ index. The js │ │ │ └ ─ store │ index. The js │ mutations. Js │ state. The js │ └ ─ the static .gitkeepCopy the code

1.5 Initialization of project code

As we do webApp, we need to make corresponding preparations for the mobile terminal.

1. Set meta tags

index.html

    <meta name="viewport" content="Width = device - width, initial - scale = 1.0, the minimum - scale = 1.0, the maximum - scale = 1.0, user - scalable = no">
Copy the code

Effect: The page scale is always 1:1, and the user’s finger scaling is invalid

2. The introduction of the reset. CSS

Purpose: To reset the page style

Reset.css is introduced to ensure that the initial style of the page is the same on each browser because the initial style of the page is different on different mobile devices and different browsers

3. Introduce border. The CSS

Objective: To solve the problem of 1-pixel border on mobile terminal

4. Install FastClick in the project

npm install fastclick --save

Objective: to solve the 300ms delay problem of mobile terminal

1.6 Page componentization

Routing the router – index. Js

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'

Vue.use(Router)

export default new Router({
  routes: [{
    path: '/'.name: 'Home'.component: Home
  }, {
    path: '/city'.name: 'City'.component: City
  }, {
    path: '/detail/:id'.name: 'Detail'.component: Detail
  }],
  scrollBehavior (to, from, savedPosition) {
    return { x: 0.y: 0}}})Copy the code

Page related Directory

Pages ├ ─ city │ │ city. Vue │ │ │ └ ─ components │ Alphabet. Vue │ Header. The vue │ List. The vue │ Search. Vue │ ├ ─ the detail │ │ │ ├ ─ ├ ─ ├ ─ ├ ─ sci-imp. │ ├ ─ sci-imp. │ ├ ─ sci-imp. │ ├ ─ sci-imp Icons. Vue Recommend. Vue Swiper. Vue Weekend. Vue common ├─fade │ FadeAnimationCopy the code

For example, for the attractions ticket page, you can split it into several pieces, put them in the Components directory, and integrate the page by referencing the components in the home.vue container component

Home.vue part of the code

<template>
  <div>
    <home-header></home-header>
    <home-swiper :list="swiperList"></home-swiper>
    <home-icons :list="iconList"></home-icons>
    <home-recommend :list="recommendList"></home-recommend>
    <home-weekend :list="weekendList"></home-weekend>
  </div>
</template>

<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
import { mapState } from 'vuex'
export default {
  name: 'Home'.components: {
    HomeHeader,
    HomeSwiper,
    HomeIcons,
    HomeRecommend,
    HomeWeekend
  },
  data () {
    return {
      lastCity: ' '.swiperList: [].iconList: [].recommendList: [].weekendList: []}}}</script>


Copy the code

Second, the use of project plug-ins

2.1 Ajax to Obtain Home page data

Vue recommends using AXIos for cross-platform data requests

Install axios

npm install axios --save

The best option is to send Ajax requests at home.vue, and once the component gets the Ajax data, it can pass it to each of its children

Put some static files placed in the static directory, to access by http://localhost:8080/static/mock/index.json

Static │ ├ ─ garbage. Json detail.json indexCopy the code

Home.vue part of the code

<template>
  <div>
    <home-header></home-header>
    <home-swiper :list="swiperList"></home-swiper>
    <home-icons :list="iconList"></home-icons>
    <home-recommend :list="recommendList"></home-recommend>
    <home-weekend :list="weekendList"></home-weekend>
  </div>
</template>

<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
import { mapState } from 'vuex'
export default {
  name: 'Home'.components: {
    HomeHeader,
    HomeSwiper,
    HomeIcons,
    HomeRecommend,
    HomeWeekend
  },
  data () {
    return {
      lastCity: ' '.swiperList: [].iconList: [].recommendList: [].weekendList: []}},computed: {
    ...mapState(['city'])},methods: {
    getHomeInfo () {
      axios.get('/api/index.json? city=' + this.city)
        .then(this.getHomeInfoSucc)
    },
    getHomeInfoSucc (res) {
      res = res.data
      if (res.ret && res.data) {
        const data = res.data
        this.swiperList = data.swiperList
        this.iconList = data.iconList
        this.recommendList = data.recommendList
        this.weekendList = data.weekendList
      }
    }
  },
  mounted () {
    this.lastCity = this.city
    this.getHomeInfo()
  }
}
</script>

<style>

</style>

Copy the code

Parent and child components communicate with each other

The parent component passes data to the child component via props, and the child component passes data to the parent component by sending events via emit

Take the List component as an example.

<template>
  <div>
    <div class="title">Recommend sell like hot cakes</div>
    <ul>
      <router-link
        tag="li"
        class="item border-bottom"
        v-for="item of list"
        :key="item.id"
        :to="'/detail/' + item.id"
      >
        <img class="item-img" :src="item.imgUrl" />
        <div class="item-info">
          <p class="item-title">{{item.title}}</p>
          <p class="item-desc">{{item.desc}}</p>
          <button class="item-button">Check the details</button>
        </div>
      </router-link>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'HomeRecommend'.props: {
    list: Array}}</script>

Copy the code

2.2 round figure

Install vue-awes-swiper plug-in

NPM install [email protected] - save

Multicast is used in multiple components

In the home – components – Swiper. Vue, for example

<template>
  <div class="wrapper">
    <swiper :options="swiperOption" v-if="showSwiper">
      <swiper-slide v-for="item of list" :key="item.id">
        <img class="swiper-img" :src="item.imgUrl" />
      </swiper-slide>
      <div class="swiper-pagination"  slot="pagination"></div>
    </swiper>
  </div>
</template>

<script>
export default {
  name: 'HomeSwiper'.props: {
    list: Array
  },
  data () {
    return {
      swiperOption: {
        pagination: '.swiper-pagination'.loop: true}}},computed: {
    showSwiper () {
      return this.list.length
    }
  }
}
</script>

Copy the code

2.3 Better – scroll

The installation

npm install better-scroll --save

use

<div class="wrapper">
  <ul class="content">
    <li>.</li>
    <li>.</li>.</ul>
  <! -- you can put some other DOMs here, it won't affect the scrolling </div>Copy the code
import BScroll from '@better-scroll/core'
let wrapper = document.querySelector('.wrapper')
let scroll = new BScroll(wrapper)
Copy the code

2.4 Using VUEX to Share Data

Install vuex

npm install vuex --save

Click the city on the city list page, and the city can be changed in the upper right corner of the home page.

Specifically described as:

Project is in order to realize the city of data selection List page and front page, and there is no common components, city/components/List. The vue, home/components/Header. The vue, home. The vue components, all need access to data.

Because this project does not require asynchronous operation or additional data processing, only state and mutations are used in the project. The city data is stored in state, and the event type and function changeCity are defined in mutation

store
    index.js
    mutations.js
    state.js
Copy the code

state.js

let defaultCity = 'Shanghai'
try {
  if (localStorage.city) {
    defaultCity = localStorage.city
  }
} catch (e) {}

export default {
  city: defaultCity
}

Copy the code

mutations.js

export default {
  changeCity (state, city) {
    state.city = city
    try {
      localStorage.city = city
    } catch (e) {}
  }
}
Copy the code

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations
})

Copy the code

Vue component, in the calculation property, this.$store.state. XXX, in this project, this.$store.state. Of course, to make the code more concise, use mapState to map this. XXX to this.$store.state.xxx.

In list. vue, commit triggers the methods in mutations to modify the data. Also, to make the code more concise, introduce mapMutations to map this.changecity (city) to this.code.store.mit (‘changeCity’, city).

City/list. vue

import { mapState, mapMutations } from 'vuex'computed: { ... mapState({currentCity: 'city'})},methods: {
    handleCityClick (city) {
      // this.$store.commit('changeCity', city)
      this.changeCity(city)
      this.$router.push('/')},... mapMutations(['changeCity'])}Copy the code

This enables data sharing among these components.

3. Project difficulties

3.1 Interworking between Sibling Components

Implementation function: Click the city list page on the right of the letter, the list options will scroll to the corresponding letter area. [GIF display]

Sibling components can pass values through the bus. However, since the non-parent component is relatively simple, we can have the Alphabet. Vue component pass the value to the parent component, City.vue, and then the city. vue component forwards the value to the list. vue component. Child component to parent component, parent component to another child component. In this way, click on the right side of the Alphabet. Vue to get the corresponding letter.

Alphabet.vue

Add a click event to the element of the loop, such as handleLetterClick, and write the event method in Methods:

<template>
  <ul class="list">
    <li
      class="item"
      v-for="item of letters"
      :key="item"
      :ref="item"
      @click="handleLetterClick"
    >
      {{item}}
    </li>
  </ul>
</template>

<script>
methods: {
    handleLetterClick(e) {
        this.$emit("change", e.target.innerHTML); }}</script>
Copy the code

Next, it forwards the data received by the parent component to the child component list.vue, which passes values to the child component via properties.

First define a letter in the data of the parent component city. vue. Default value is null. In the handleLetterClick method, when receiving a letter from outside, make this.letter = letter.

City.vue

<template>
  <div>
    <city-header></city-header>
    <city-search :cities="cities"></city-search>
    <city-list
      :cities="cities"
      :hot="hotCities"
      :letter="letter"
    ></city-list>
    <city-alphabet
      :cities="cities"
      @change="handleLetterChange"
    ></city-alphabet>
  </div>
</template>

<script>
data() {
    return {
        hotCities: [],
        cities: {},
        letter: ""
    };
},
methods: {
    handleAlpChange(letter) {
        this.letter = letter; }}</script>
Copy the code

In the city-list template of the City. Vue component, pass the letter=”letter” to the sub-component List, and receive the letter in props. And the validation type is String.

List.vue

props: {
    hot: Array.cities: Object.letter: String
  }
Copy the code

This enables the sibling component to pass values.

[Project difficulties]

When list. vue detects that the letter has changed, it needs to display the List items that start with the same letter.

In this case, a listener is needed to listen for letter changes;

Better – scroll to provide such an interface, scroll. ScorllToElement, if the letter is not null, call the enclosing scroll. ScrollToElement () this approach, You can make the scroll area automatically roll to an element, so how to pass the element? In the loop city section, add a ref reference to the loop item to get the current Dom element, equal to key, and then go back to the listener’s letter and define an element equal to the element fetched by ref:

List.vue

watch: {
    letter() {
        if (this.letter) {
            const element = this.$refs[this.letter][0];
            this.scroll.scrollToElement(element); }}},Copy the code

The element is passed to the scrollToElement by a letter. Note that the code ends with a [0] because otherwise, the contents of a ref or are an array. The first element in the array is the real DOM element, and clicking the alphabet on the right jumps to the list of cities under the corresponding letter.

Click the jump function is implemented

Next, slide the alphabet on the right and switch the city list on the left.

<template>
  <ul class="list">
    <li
      class="item"
      v-for="item of letters"
      :key="item"
      :ref="item"
      @touchstart.prevent="handleTouchStart"
      @touchmove="handleTouchMove"
      @touchend="handleTouchEnd"
      @click="handleLetterClick"
    >
      {{item}}
    </li>
  </ul>
</template>

<script>
export default {
  name: 'CityAlphabet'.props: {
    cities: Object
  },
  computed: {
    letters () {
      const letters = []
      for (let i in this.cities) {
        letters.push(i)
      }
      return letters
    }
  },
  data () {
    return {
      touchStatus: false.startY: 0.timer: null
    }
  },
  updated () {
    this.startY = this.$refs['A'] [0].offsetTop
  },
  methods: {
    handleLetterClick (e) {
      this.$emit('change', e.target.innerText)
    },
    handleTouchStart () {
      this.touchStatus = true
    },
    handleTouchMove (e) {
      if (this.touchStatus) {
        if (this.timer) {
          clearTimeout(this.timer)
        }
        this.timer = setTimeout((a)= > {
          const touchY = e.touches[0].clientY - 79
          const index = Math.floor((touchY - this.startY) / 20)
          if (index >= 0 && index < this.letters.length) {
            this.$emit('change'.this.letters[index])
          }
        }, 16)
      }
    },
    handleTouchEnd () {
      this.touchStatus = false}}}</script>

Copy the code

3.2 the search component

Function: when entering the city selection page, when focus to the search box, input the city name or pinyin can display the search results.

<template>
  <div>
    <div class="search">
      <input v-model="keyword" class="search-input" type="text" placeholder="Enter city name or pinyin" />
    </div>
    <div
      class="search-content"
      ref="search"
      v-show="keyword"
    >
      <ul>
        <li
          class="search-item border-bottom"
          v-for="item of list"
          :key="item.id"
          @click="handleCityClick(item.name)"
        >
          {{item.name}}
        </li>
        <li class="search-item border-bottom" v-show="hasNoData">No match was found</li>
      </ul>
    </div>
  </div>
</template>

<script>
import Bscroll from 'better-scroll'
import { mapMutations } from 'vuex'
export default {
  name: 'CitySearch'.props: {
    cities: Object
  },
  data () {
    return {
      keyword: ' '.list: [].timer: null}},computed: {
    hasNoData () {
      return !this.list.length
    }
  },
  watch: {
    keyword () {
      if (this.timer) {
        clearTimeout(this.timer)
      }
      if (!this.keyword) {
        this.list = []
        return
      }
      this.timer = setTimeout((a)= > {
        const result = []
        for (let i in this.cities) {
          this.cities[i].forEach((value) = > {
            if (value.spell.indexOf(this.keyword) > - 1 || value.name.indexOf(this.keyword) > - 1) {
              result.push(value)
            }
          })
        }
        this.list = result
      }, 100)}},methods: {
    handleCityClick (city) {
      this.changeCity(city)
      this.$router.push('/')},... mapMutations(['changeCity'])
  },
  mounted () {
    this.scroll = new Bscroll(this.$refs.search)
  }
}
</script>
Copy the code

[Performance optimization — Anti-shake]

Write a listener watch to monitor the changes of the keyword inside. Considering the performance optimization, use the shaking prevention method to realize this. First define a timer timer in data, and the default value is null. When timer is null, the timer is cleared. Write the timer method below, when the delay is 100ms, the arrow function will be executed.

3.3 Recursive components

Recursive component means calling the component itself from the component itself.

The detail data. Json

{
  "ret": true."data": {
    "sightName": Dalian Shengya Ocean World (AAAA Scenic Spot)."bannerImg": "http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_600x330_bf9c4904.jpg"."gallaryImgs": ["http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_800x800_70debc93.jpg"."http://img1.qunarzz.com/sight/p0/1709/76/7691528bc7d7ad3ca3.img.png_800x800_9ef05ee7.png"]."categoryList": [{
        "title": "Adult ticket"."children": [{
          "title": "Adult Three Hall Joint Ticket"."children": [{
            "title": "Adult 3-restaurant Joint Ticket - Sold by a chain store"
          }]
        },{
          "title": "Five Joint Tickets for Adults"}]}, {"title": Student ticket
      }, {
        "title": "Children's Ticket"
      }, {
        "title": "Preferential ticket"}}}]Copy the code

list.vue

<template>
  <div>
    <div
      class="item"
      v-for="(item, index) of list"
      :key="index"
    >
      <div class="item-title border-bottom">
        <span class="item-title-icon"></span>
        {{item.title}}
      </div>
      <div v-if="item.children" class="item-chilren">
        <detail-list :list="item.children"></detail-list>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'DetailList'.props: {
    list: Array}}</script>

<style lang="stylus" scoped>.item-title-icon position: relative left: .06rem top: .06rem display: inline-block width: .36rem height: .36rem background: url(http://s.qunarzz.com/piao/image/touch/sight/detail.png) 0 -.45rem no-repeat margin-right: .1rem background-size: .4rem 3rem .item-title line-height: .8rem font-size: .32rem padding: 0 .2rem .item-chilren padding: 0 .2rem</style>

Copy the code

In the code above, a judgment is made under the list-children element. When item.children has a value, it calls itself, that is, the detail-list component, which also passes a list as an attribute. Because list.vue has received the list through props, and the outer layer has looped through the list, we need to get the children under the list, so we can just set the list property equal to item.children. Because the data is hierarchical, it can be rendered by adding styles, as shown in the following image:

Iv. Problems encountered in the project and solutions

This part is not all the problems and solutions encountered in the project, because it is also described in the previous part, this part is supplementary to the previous part.

4.1 localStorage

At the beginning, when the city location display was implemented in the upper right corner of the homepage, a store directory was created under the SRC directory, which stored the default data in Vuex, and city was directly set to “Beijing”. However, it was actually problematic to write in this way. Clicking city would change the city, but when the page was refreshed, I went back to Beijing.

Considering that in real projects, if you select a city this time, the next time you open this page, the city selected last time should still be there, how to solve this problem?

At this time you can use HTML5 to provide a new API, called localStorage localStorage, it can achieve localStorage, here is to achieve the function of saving the city.

When the user tries to change the city, I not only change the city in the state, but also save a localStorage, directly write localstorage. city = city. And then let the stare of the city. The default value is localStorage city | | “Beijing”, is ok. In other words, THE default value of city is obtained from localStorage first. If it cannot be obtained, the default value of “Beijing” is used.

store/index.js

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
    state: {
        city: localStorage.city || "Beijing"
    },
    mutations: { changeCity(state, city) { state.city = city; localStorage.city = city; }}})Copy the code

At this time open the page, when the user selects a city, and then refresh the page, you can see that the city selected last time is still there. However, when using localStorage, it is recommended to wrap a try{}catch(e){} around it. In some browsers, if the user disables localStorage or uses instealth mode, using localStorage may cause the browser to directly throw an exception. To avoid this problem, add a try{}catch(e){} to the outer layer.

Define a default defaultCity equal to “Beijing” and write a try{}catch(e){} : Default. city = localstorage. city; defaultCity = localstorage. city; Mutations also include a try{}catch(e) in changeCity:

store/index.js

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);

let defaultCity = "Beijing"
try {
    if(localStorage.city) { defaultCity = localStorage.city; }}catch (e) { }

export default new Vuex.Store({
    state: {
        city: defaultCity
    },
    mutations: {
        changeCity(state, city) {
            state.city = city;
            try {
                localStorage.city = city;
            } catch (e) { }
        }
    }
})
Copy the code

Now we see that the file store/index.js is gradually becoming complicated. In fact, in the real project development, it will be further split, that is, the file will be split into State, Actions, Mutations, Create a file called state.js in store (store public data only), and put the code that sets the default data in it. Export the file to the state object defined in index.js:

let defaultCity = "Beijing"
try {
    if(localStorage.city) { defaultCity = localStorage.city; }}catch (e) { }
export default {
    city: defaultCity
}
Copy the code

Next, just import state in index.js:

import Vue from "vue";
import Vuex from "vuex";
import state from "./state";
Vue.use(Vuex);

export default new Vuex.Store({
    state: state,
    mutations: {
        changeCity(state, city) {
            state.city = city;
            try {
                localStorage.city = city;
            } catch (e) { }
        }
    }
})
Copy the code

Next, create a file in the Store directory called upgrades.js, and cut the mutations object in index.js into the code:

export default {
    changeCity(state, city) {
        state.city = city;
        try {
            localStorage.city = city;
        } catch(e) {}}} finally index.js looks like this:import Vue from "vue";
import Vuex from "vuex";
import state from "./state";
import mutations from "./mutations";
Vue.use(Vuex);

export default new Vuex.Store({
    state: state,
    mutations: mutations
})
Copy the code

In this way, we split vuEX code into State, Actions and Mutations, and its maintainability will be greatly improved in the future.

4.2 keep-alive

Optimize web page performance with keep-alive

When the city list response code is written, the service is started and the page is opened, there is no problem. Some basic business logic has been realized, but open the option of Network in the console and select XHR. When entering the home page for the first time, an index.json file is requested. Json, index. Json again. Go to the list page, city. Json again.

Mounted hook is mounted when the home.vue Home page is opened. / mounted hook is mounted when the hook is mounted. / Mounted Hook is mounted when the Home page is opened.

When we open main.js, we can see that the entry component is App. When we open app. vue, the router-view displays the content corresponding to the current address. We can wrap a keep-alive tag in the outer layer, which is a tag of Vue. What he means is that after the contents of my route are loaded once, I put the contents of the route into memory, and the next time I enter the route, I don’t need to re-render the component and re-execute the hook function, I just need to fetch the previous contents out of memory.

In this case, if you go back to the page, open Network again, go to the list page, select the city and return to the home page, you will no longer load index.json. Similarly, if you enter the list page, you will no longer load city.

Logical problems still remain, so when I was in the “Beijing”, the homepage is the content of the “Beijing”, when the switch is “Shanghai”, the first page should display the contents of the “Shanghai”, so the city, at the time of change home page also need to send an Ajax request, data of different cities in order to get information, we do a adjust to this.

Open the home.vue component and change the axios request address here. Add a parameter after it to make it equal to the current city stored in Vuex, so reference Vuex in the home.vue component as well. Import {mapState} from “vuex” and add a calculated attribute:

computed:{ ... mapState(['city'])}Copy the code

Get the corresponding content of the city, and then put the city in the request parameter when sending Ajax:

axios.get("/api/index.json? city=" + this.city)
.then(this.getHomeInfoSucc);
Copy the code

At this point, we open the page and see that the request parameter already carries the current city:

However, for example, when you switch the city “Guilin” and return to the home page, you do not send the Ajax request again. Although the city above is changed to “Guilin”, the content at the bottom is still “Beijing”. We want the content at the bottom to change with it, what should we do?

When we use keep-alive in app. vue, the content of this block has been cached, and it directly retrieves the data in the cache. How to change the data in the cache? When you use keep-alive, there is an additional lifecycle function activted in the component,

Mounted and Activated can be used to print the contents of the mounted and Activated lifecycle functions.

mounted() {
    console.log("mounted");
    this.getHomeInfo();
},
activated(){
    console.log("activted");
}
Copy the code

Mounted and activated are both executed. When the city is switched, only activated is executed. So we can use the lifecycle function activated to do what we want.

When the page is mounted, an Ajax request must be sent. That is, when the page is displayed again, activated must be executed again. You can determine if the city on the current page is the same as the city shown on the previous page, and if not, make another Ajax request.

First set a data lastCity in data, default value is null, then set it equal to this.city when the page is mounted, and make a save for the lastCity:

mounted() {
    this.lastCity = this.city
    this.getHomeInfo();
}
Copy the code

When the page is reactivated, we write in ActivTED like this:

activated() {
    if(this.lastCity ! =this.city){
        this.lastCity = this.city
        this.getHomeInfo(); }}Copy the code

If the lastCity is not the same as the current city, make a new Ajax request and call the getHomeInfo method. When the last city is different from the city this time, we need to make it equal to the city this time. Back on the page, you can see that Ajax will not request city. Json when the city is the same as the last city, but will only request city.

Back to the code, through activted, a new keep-alive life cycle function, combined with lastCity, a temporary cache variable, the home page code performance optimization adjustment was realized.

V. Follow-up arrangements

  • Page functions are further improved
  • Deploy the project to the server
  • Combine with Node to interconnect with real data

Note, this project comes from the Dell Vue2.5 Development qunar App from zero basic entry to actual combat project

The writing inspiration of this article is partly derived from The React hooks+redux+immutable. Js to build netease Cloud music exquisite webApp