Comprehensive exploration of new features of Vue3.0 - rapid construction of practical projects based on Composition Api

Quick Start

This project comprehensively uses the new features of Vue3.0, which is suitable for novice learning

  • Based on the Composition API, i.e. function based API, the transformation is carried out. With Vue Cli, the priority is to experience the characteristics of Vue3
  • Using singleton object mode for component communication
  • Use axios library for network request and weui library for UI interface
# Installation dependency
npm install
# Open the localhost:8080 view page in the browser, and hot update in real time
npm run serve
# Project release
npm run build

It is recommended to cooperate with Visual Studio Code and Vue 3 Snippets Code plug-ins eat ψ ( ̄  ̄  ̄) ψ.

Dependencies

The following are the dependencies applied to the project, @ vue / composition API and vue module let vue 2.0 experience the new features of vue 3.0 first. axios is a tool library to help us send network requests to get data. weui is a basic style library consistent with wechat's original vision, which is convenient for us to quickly build project pages.

"@vue/composition-api": "^0.3.4",
"axios": "^0.19.0",
"core-js": "^3.4.3",
"vue": "^2.6.10",
"weui": "^2.1.3"

Directory Structure

├── src
│   ├── App.vue                          # Component entry
│   ├── assets                           # Resource directory
│   ├── stores/index.js                  # State management
│   ├── components                       # Component directory
│   │   ├── Header.vue                   # Head assembly
│   │   ├── Search.vue                   # Search box components
│   │   ├── Panel.vue                    # List components
│   ├── main.js                          # Project entry
├── public                               # template file
├── vue.config.js                        # Scaffold profile
├── screenshot                           # Program screenshots

Composition API

npm install @vue/composition-api --save

After downloading the @ Vue / Composition API plug-in with npm command, Vue.use(VueCompositionApi) needs to be called explicitly after the module is introduced, and the Composition API can be opened by referencing it in main.js according to the document.

// main.js
import Vue from 'vue'
import App from './App.vue'
// 1. Introduce Composition API module
import VueCompositionApi from '@vue/composition-api'

Vue.config.productionTip = false
// 2. Do not miss the explicit call to VueCompositionApi
Vue.use(VueCompositionApi)

new Vue({
  render: h => h(App),
}).$mount('#app')
npm install weui --save

We also use npm to install the weui module, and then introduce the basic style library of weui in main.js, so that we can use wechat basic style to build project pages globally.

// main.js
import Vue from 'vue'
import App from './App.vue'
// Global introduction of basic style library of 'weui'
import 'weui'
import VueCompositionApi from '@vue/composition-api'

Vue.config.productionTip = false
Vue.use(VueCompositionApi)

new Vue({
  render: h => h(App),
}).$mount('#app')

Go back to App.vue, keep the value of components attribute to empty the content of < template > template, delete the < style > template, and wait for the new component to be re introduced.

<template>
  <div id="app">
    Hello World
  </div>
</template>
<script>
export default {
  name: "app",
  components: {}
};
</script>

In the src/components directory, create the first component, named Header.vue, and write the following code, Click to view the source code:

<template>
  <header :style="{
    backgroundColor: color?color:defaultColor
  }">{{title}}</header>
</template>
<script>
import { reactive } from "@vue/composition-api";
export default {
  // The parent component is passed in to change the property value of the header component
  props: {
    // Title
    title: String,
    // colour
    color: String
  },
  setup() {
    const state = reactive({
      defaultColor: "red"
    });
    return {
      ...state
    };
  }
};
</script>
<style scoped>
header {
  height: 50px;
  width: 100%;
  line-height: 50px;
  text-align: center;
  color: white;
}
</style>

setup

Here we use a new property setup, which is the entry of a component. Let's use the new interface exposed by Vue3.0. It runs when the component is instantiated, and after the props property is defined, it is actually equivalent to the two lifecycles of beforeCreate and Created of Vue2.0. Setup returns an object, and all the returned property values In the single file component, it will cooperate with the content of < template > template to complete the binding between Model and View. In the future version, it should also support the return of JSX code fragments.

<template>
  <!-- View -->
  <div>{{name}}</div>
</template>
<script>
import { reactive } from '@vue/composition-api'
export default {
  setup() {
    const state = reactive({ name: 'Eno Yao' });
    // return exposed to template
    return {
      // Model
      ...state
    }
  }
}
</script>

reactive

In the setup function, we adapt to the first new interface of Vue3.0, reactive. It mainly deals with your object and makes it become a responsive object after Proxy processing. It is similar to the data attribute of Vue2.0. It should be noted that the processed object is not equal to the original object, and the processed object belongs to the object of deep cloning.

const state = reactive({ name: 'Eno Yao' })

props

In Vue 2.0, we can use props property value to complete parent-child communication. Here we need to define props property to define the type of accepted value. Then we can use the first parameter of setup to obtain props.

export default {
  props: {
    // Title
    title: String,
    // colour
    color: String
  },
  setup(props) {
    // Here you can use the props property value passed from the parent component
  }
};

We can use the header component in App.vue. With props above, we can make the header component present different states according to the values passed in.

<template>
  <div id="app">
    <!-- Reuse components and pass in props Value to make the component present the corresponding state -->
    <Header title="Eno" color="red" />
    <Header title="Yao" color="blue" />
    <Header title="Wscats" color="yellow" />
  </div>
</template>
<script>
import Header from "./components/Header.vue";
export default {
  name: "app",
  components: {
    Header,
  }
};
</script>

context

The second parameter of the setup function is a context object, which contains some useful properties. These properties need to be accessed through this in Vue2.0. In vue3.0, accessing them becomes the following form:

setup(props, ctx) {
  console.log(ctx) // this cannot be accessed in the setup() function
  console.log(this) // undefined
}

The following useful properties can be accessed:

  • root
  • parent
  • refs
  • attrs
  • listeners
  • isServer
  • ssrContext
  • emit

After completing the Header.vue above, we will write the Search.vue search box component, and continue to create a new Search.vue file under the src/components folder, Click to view the source code.

<template>
  <div :class="['weui-search-bar', {'weui-search-bar_focusing' : isFocus}]" id="searchBar">
    <form class="weui-search-bar__form">
      <div class="weui-search-bar__box">
        <i class="weui-icon-search"></i>
        <input
          v-model="searchValue"
          ref="inputElement"
          type="search"
          class="weui-search-bar__input"
          id="searchInput"
          placeholder="search"
          required
        />
        <a href="javascript:" class="weui-icon-clear" id="searchClear"></a>
      </div>
      <label @click="toggle" class="weui-search-bar__label" id="searchText">
        <i class="weui-icon-search"></i>
        <span>search</span>
      </label>
    </form>
    <a @click="toggle" href="javascript:" class="weui-search-bar__cancel-btn" id="searchCancel">cancel</a>
  </div>
</template>
<script>
import { reactive, toRefs, watch } from "@vue/composition-api";
import store from "../stores";
export default {
  // setup is equivalent to the 2.x version of the beforeCreate life cycle
  setup() {
    // The reactive() function takes a normal object and returns a responsive data object
    const state = reactive({
      searchValue: "",
      // Two states of search box, focusing and unfocusing
      isFocus: false,
      inputElement: null
    });
    // How to switch the state of search box
    const toggle = () => {
      // Let the input box that appears after clicking search focus automatically
      state.inputElement.focus();
      state.isFocus = !state.isFocus;
    };
    // Listen for search box values
    watch(
      () => {
        return state.searchValue;
      },
      () => {
        // Store the input box to the status store center for component communication
        store.setSearchValue(state.searchValue);
        // window.console.log(state.searchValue);
      }
    );
    return {
      // Convert each attribute on the state to responsive data in the form of ref
      ...toRefs(state),
      toggle
    };
  }
};
</script>

toRefs

We can see that we have used a lot of new attributes above. First, we will introduce toRefs. The function can convert the responsive object created by reactive() into a common object. However, each attribute node on this object is the responsive data of ref(). With the v-model instruction, we can complete the bidirectional binding of data, which is very efficient in development.

import { reactive, toRefs } from "@vue/composition-api";
export default {
  setup() {
    const state = reactive({ name: 'Eno Yao' })
  }
  return {
    // If state is returned directly, the data will be nonresponsive, and MV will bind one way
    // ...state,
    // If the state is returned after the toRefs is wrapped, the data will be reactive, and the MVVM will be bound in two directions
    ...toRefs(state),
  };
}

template refs

The input box here has two states, one is the state with input box and the state without input box, so we need a Boolean value isFocus to control the state, encapsulate a toggle method, and let the isFocus value switch the true and false states.

const toggle = () => {
  // Inverse isFocus value
  state.isFocus = !state.isFocus;
};

Then with the v-bind:class instruction, let the name of the weui search bar focusing class decide whether to appear or not according to the value of isFocus, so as to change the state of the search box.

<div :class="['weui-search-bar', {'weui-search-bar_focusing' : isFocus}]" id="searchBar">

v-model instruction is put into the search input box to receive the user's input information, which is convenient for the later to cooperate with the list component to execute the search logic, and ref attribute is also put in to obtain the element node of the < input / > tag. With the native method of state.inputElement.focus(), the cursor will automatically focus on the input box when switching the search box state, so as to enhance the user experience.

<input
  v-model="searchValue"
  ref="inputElement"
/>

watch

The watch() function is used to monitor the changes of some data items, so as to trigger some specific operations. Before use, you still need to import on demand, monitor the changes of searchValue, and then trigger the logic in the callback function, that is, monitor the search value entered by the user, and then touch the logic of the callback function to store the searchValue in the store object we created, which is later in the aspect Data communication with Panel.vue list component:

import { reactive, watch } from "@vue/composition-api";
import store from "../stores";
export default {
  setup() {
    const state = reactive({
      searchValue: "",
    });
    // Listen for search box values
    watch(
      () => {
        return state.searchValue;
      },
      () => {
        // Store the input box to the status store center for component communication
        store.setSearchValue(state.searchValue);
      }
    );
    return {
      ...toRefs(state)
    };
  }
};

state management

Here we maintain a piece of data to realize shared state management, that is to say, we create a new store.js to expose the searchValue value of the shared Panel and Search component of the store object. When the Search.vue component receives the searchValue retrieval value from the input box, it will be placed in the store object of store.js, and then the object will be injected into the Search component All components can share the value in the store object. In order to facilitate debugging, we also encapsulate setSearchValue and getSearchValue to operate the store object, so that we can track the change of state.

// store.js
export default {
    state: {
        searchValue: ""
    },
    // Set search box value
    setSearchValue(value) {
        this.state.searchValue = value
    },
    // Get the value of the search box
    getSearchValue() {
        return this.state.searchValue
    }
}

After completing the above Search.vue, we write the Panel.vue search box component, and continue to create a new Panel.vue file under the src/components folder, Click to view the source code.

<template>
  <div class="weui-panel weui-panel_access">
    <div v-for="(n,index) in newComputed" :key="index" class="weui-panel__bd">
      <a href="javascript:void(0);" class="weui-media-box weui-media-box_appmsg">
        <div class="weui-media-box__hd">
          <img class="weui-media-box__thumb" :src="n.author.avatar_url" alt />
        </div>
        <div class="weui-media-box__bd">
          <h4 class="weui-media-box__title" v-text="n.title"></h4>
          <p class="weui-media-box__desc" v-text="n.author.loginname"></p>
        </div>
      </a>
    </div>
    <div @click="loadMore" class="weui-panel__ft">
      <a href="javascript:void(0);" class="weui-cell weui-cell_access weui-cell_link">
        <div class="weui-cell__bd">View more</div>
        <span class="weui-cell__ft"></span>
      </a>
    </div>
  </div>
</template>
<script>
import { reactive, toRefs, onMounted, computed } from "@vue/composition-api";
import axios from "axios";
import store from "../stores";
export default {
  setup() {
    const state = reactive({
      // The number of pages
      page: 1,
      // Tabular data
      news: [],
      // Filter bad list data by the value of search box
      newComputed: computed(() => {
        // Determine whether the input box has entered filter criteria. If it does not return the original news array
        if (store.state.searchValue) {
          return state.news.filter(item => {
            if (item.title.indexOf(store.state.searchValue) >= 0) {
              return item;
            }
          });
        } else {
          return state.news;
        }
      }),
      searchValue: store.state
    });
    // Send ajax request to get list data
    const loadMore = async () => {
      // Get list data
      let data = await axios.get("https://cnodejs.org/api/v1/topics", {
        params: {
          // Number of topics per page
          limit: 10,
          // The number of pages
          page: state.page
        }
      });
      // Overlay pages
      state.page += 1;
      state.news = [...state.news, ...data.data.data];
    };
    onMounted(() => {
      // Trigger the request when the first screen is loaded
      loadMore();
    });
    return {
      // Keep data responsive
      ...toRefs(state),
      // See more events
      loadMore
    };
  }
};
</script>

lifecycle hooks

The life cycle hook of Vue3.0 is different from the previous version. The new version is registered with the onXxx() function, and the corresponding modules of the life cycle need to be introduced locally:

import { onMounted, onUpdated, onUnmounted } from "@vue/composition-api";
export default {
  setup() {
    const loadMore = () => {};
    onMounted(() => {
      loadMore();
    });
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
    return {
      loadMore
    };
  }
};

Here is a comparison of the life cycle of the new and old versions:

  • <s>beforeCreate</s> -> use setup()
  • <s>created</s> -> use setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

At the same time, the new version also provides two new life cycles to help us debug code:

  • onRenderTracked
  • onRenderTriggered

In the Panel list component, we register the onMounted life cycle, and trigger the request method loadMore to get data from the back end to the data layer. Here we use the axios network request library, so we need to install the module:

npm install axios --save

It encapsulates a request list data method. The interface points to the API provided by the Cnode official website. Since axios returns Promise, it can perfectly write asynchronous logic with async and await, and then trigger it with the onMounted life cycle, and bind the method to the view more button in the view layer to complete the first load of the list and click to view more The lazy loading function of.

// Send ajax request to get list data
const loadMore = async () => {
  // Get list data
  let data = await axios.get("https://cnodejs.org/api/v1/topics", {
    params: {
      // Number of topics per page
      limit: 10,
      // The number of pages
      page: state.page
    }
  });
  // Overlay pages
  state.page += 1;
  // Merge list data
  state.news = [...state.news, ...data.data.data];
};
onMounted(() => {
  // Trigger the request when the first screen is loaded
  loadMore();
});

computed

Next, we will use another attribute computed to calculate the attribute, which is very similar to the way Vue2.0 is used. We also need to import the module as needed:

import { computed } from '@vue/composition-api';

There are two types of calculation attributes: read-only and read-write

// Read only calculated properties
let newsComputed = computed(() => news.value + 1)
// Readable and writable
let newsComputed = computed({
  // Value function
  get: () => news.value + 2,
  // Assignment function
  set: val => {
    news.value = news.value - 3
  }
})

Here we use the readable and writable calculation attributes to process the list data. Remember our last component Search.vue? We can combine the search value entered by the user in the search box with the calculated calculation attribute to filter the useful list data for our users. So we first get the searchValue shared by the Search.vue search box from the shared instance of the store After that, we use the original string method indexOf and array method filter to filter the list data, and then return the new list data news computed, and render the new list data with the v-for instruction on the view layer, so that we can return the original list data news when there is no search box retrieval value, and return the new list data n when there is search box retrieval value ewsComputed.

import store from "../stores";
export default {
  setup() {
    const state = reactive({
      // Original list data
      news: [],
      // Filter the new list data by the value of search box
      newsComputed: computed(() => {
        // Determine whether the input box has entered filter criteria. If it does not return the original news array
        if (store.state.searchValue) {
          return state.news.filter(item => {
            if (item.title.indexOf(store.state.searchValue) >= 0) {
              return item;
            }
          });
        } else {
          return state.news;
        }
      }),
      searchValue: store.state
    });
  }
}

Project source code

If the articles and notes can give you a little help or inspiration, please don't be stingy with your praise and Star, your affirmation is the biggest driving force for me to move forward

Keywords: Javascript Vue axios npm Attribute

Added by EvanAgee on Tue, 10 Dec 2019 11:22:34 +0200