Quick Start

  • Project source: github.com/Wscats/vue-…

This project comprehensively uses the new features of Vue3.0, suitable for beginners to learn 😁

  • Based on theComposition APIFunction-based APICarry on the transformation, cooperateVue Cli, priority experienceVue3features
  • Use the singleton object pattern for component communication
  • useaxiosLibrary makes network requests,weuiLibraries implement UI interfaces
# install dependencies
npm install
# Open localhost:8080 in your browser to view the page and hot update it in real time
npm run serve
# Release project
npm run build
Copy the code

It is recommended that the Visual Studio Code and Vue 3 Snippets Code plug-in consume ψ ( ̄∀ ̄) ψ.

Dependencies

The following are the dependencies used in the project. @vue/ composition-API and vUE module enable us to experience the new features of Vue3.0 first. Axios is a tool library to assist us in sending network requests to get data. Weui is a set of basic style library consistent with wechat’s native vision, which is convenient for us to quickly build project pages.

"@vue/composition-api": "^ 0.3.4"."axios": "^ 0.19.0"."core-js": "^ rule 3.4.3"."vue": "^ 2.6.10"."weui": "^ 2.1.3"
Copy the code

Directory Structure

├─ SRC │ ├─ App.vue # ├─ Assets # ├─ Stores/Index.js # State Management │ ├─ Components # │ │ ├─ app.vue # │ ├─ Search. Vue # │ │ ├─ Panel. Vue # List │ ├─ main.js # Public # Template File ├─ Vue.config.js # Scaffolding configuration file ├─ screenshots from screenshotsCopy the code

Composition API

npm install @vue/composition-api --save
Copy the code

After downloading the @vue/composition-api plug-in using the NPM command, you need to explicitly call vue.use (VueCompositionApi) after importing the module. Following the document reference in main.js turns on the Composition API’s ability.

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

Vue.config.productionTip = false
// 2. Don't forget to explicitly call VueCompositionApi
Vue.use(VueCompositionApi)

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

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

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

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

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

Go back to app.vue, leave the components property value empty the contents of the

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

Create the first component in the SRC/Components directory and call it header. vue and write the following 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 header component's property value
  props: {
    / / title
    title: String./ / color
    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>
Copy the code

setup

Here we use a new property setup, which is a component entry that allows us to use the new interface exposed by Vue3.0. It runs after the component is instantiated and the props property is defined. In Vue2.0’s beforeCreate and Created life cycles, setup returns an object, and all returned property values are merged into Vue2.0’s Render function. It will work with the contents of the

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

reactive

In the setup function, we have adapted the first interface of Vue3.0 to Reactive, which is to process your object by Proxy into a reactive object, similar to the data property 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 deeply cloned object.

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

props

In Vue2.0 we can use the props property to communicate with each other. Here we need to define the props property to define the type of the accepted value. Then we can use the first parameter of setup to get the props use.

export default {
  props: {
    / / title
    title: String./ / color
    color: String
  },
  setup(props) {
    // Here we can use the props property passed by the parent component}};Copy the code

We can use this header component in app.vue. With the props above, we can make the header component behave differently depending on the values passed in.

<template>
  <div id="app">
    <! -- Reuse the component and pass in the props value to render the corresponding state of the component -->
    <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>
Copy the code

context

The second argument to the setup function is a context object that contains some useful properties that can be accessed through this in Vue2.0. In Vue3.0, accessing them takes the following form:

setup(props, ctx) {
  console.log(ctx) This is not accessible in the setup() function
  console.log(this) // undefined
}
Copy the code

The following useful attributes can be accessed:

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

After completing header. vue above, we’ll write the Search. Vue Search box component. Go ahead and create the Search.

<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 corresponds to the 2.x version of the beforeCreate life cycle
  setup() {
    // Reactive () receives a normal object and returns a reactive data object
    const state = reactive({
      searchValue: "".// Search the box for two states, focused and unfocused
      isFocus: false.inputElement: null
    });
    // Switch the state of the search box
    const toggle = (a)= > {
      // Make the input box that appears after clicking on the search automatically focusstate.inputElement.focus(); state.isFocus = ! state.isFocus; };// Listen for the value of the search box
    watch(
      (a)= > {
        returnstate.searchValue; = > {}, ()// Store the input box to the state Store center for component communication
        store.setSearchValue(state.searchValue);
        // window.console.log(state.searchValue);});return {
      // Convert each attribute on state to reactive data in ref form. toRefs(state), toggle }; }};</script>
Copy the code

toRefs

ToRefs is a function that transforms a reactive object created by Reactive () into a normal object, but every attribute node on that object is reactive data of type REF (). With V-model instruction, two-way data binding can be completed, which is very efficient in development.

import { reactive, toRefs } from "@vue/composition-api";
export default {
  setup() {
    const state = reactive({ name: 'Eno Yao'})}return {
    // Return state directly then the data will be non-responsive, MV one-way binding
    / /... state,
    // toRefs wraps and returns state so that the data is responsive, MVVM bidirectional binding. toRefs(state), }; }Copy the code

template refs

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

const toggle = (a)= > {
  // the value of isFocus is reversedstate.isFocus = ! state.isFocus; };Copy the code

Then, with the V-bind :class directive, change the state of the search box by having wei-search-bar_focusing class names appear based on the isFocus value.

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

The search input box contains the V-model instruction to receive input from the user for later retrieval logic with the list component, and the REF attribute to get the element node of the tag. With the native method of state.inputelement. Focus (), the cursor will automatically focus on the input box when switching the state of the search box to enhance user experience.

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

watch

The watch() function is used to monitor changes in certain data items to trigger certain operations. Before using the watch() function, you still need to import on demand, listen for changes in the searchValue, and then trigger the logic in the callback function, that is, listen for the retrieval value entered by the user. We then trigger the callback logic to store the searchValue value in the store object we created, and then communicate with the panel.vue list component after the aspect:

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

state management

Here we maintain a data store to manage shared state. That is, we create a new store.js that exposes a store object that shares the searchValue of the Panel and Search component. When the search. vue component receives the searchValue from the input box, it places it in the Store object of store.js, and then injects that object into the Search component, both components can share the value in the Store object. For debugging purposes we also wrapped setSearchValue and getSearchValue respectively to manipulate the Store object so that we could track state changes.

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

After completing the Search. Vue above, we will write the panel. vue Search box component, continue to create a new panel. vue file under 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">To 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({
      / / number of pages
      page: 1.// List data
      news: [],
      // Filter the bad list data by the value of the search box
      newComputed: computed((a)= > {
        // Check whether the input box entered filter criteria, if not return the original news array
        if (store.state.searchValue) {
          return state.news.filter(item= > {
            if (item.title.indexOf(store.state.searchValue) >= 0) {
              returnitem; }}); }else {
          returnstate.news; }}),searchValue: store.state
    });
    // Send an Ajax request to get the 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./ / number of pages
          page: state.page
        }
      });
      // Add pages
      state.page += 1;
      state.news = [...state.news, ...data.data.data];
    };
    onMounted((a)= > {
      // The request is triggered when the first screen loads
      loadMore();
    });
    return {
      // Keep the data responsive. toRefs(state),// View more eventsloadMore }; }};</script>
Copy the code

lifecycle hooks

The lifecycle hooks of Vue3.0 are different from those of the previous versions. The new version is registered with the onXxx() function and also needs to introduce the corresponding lifecycle module locally:

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

Here is a comparison of the old and new versions’ lifecycles:

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

The new version also provides two new life cycles to help debug code:

  • onRenderTracked
  • onRenderTriggered

In the Panel list component, we register the onMounted lifecycle and trigger the loadMore request method in it to fetch data from the back end to the data layer. Here we use the AXIos network request library, so we need to install this module:

npm install axios --save
Copy the code

Encapsulating a request list data method, the interface points to the API provided by the OFFICIAL website of Cnode, because axios returns a Promise, so with async and await can perfect write asynchronous logic, and then combined with onMounted life cycle trigger, Bind the method to the View layer’s See More button to complete the first load of the list and click to see more lazy loading.

// Send an Ajax request to get the 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./ / number of pages
      page: state.page
    }
  });
  // Add pages
  state.page += 1;
  // Merge list data
  state.news = [...state.news, ...data.data.data];
};
onMounted((a)= > {
  // The request is triggered when the first screen loads
  loadMore();
});
Copy the code

computed

Next, we use another attribute, computed attributes, similar to Vue2.0, and import this module on demand:

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

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

// Read-only computing properties
let newsComputed = computed((a)= > news.value + 1)
// Can read and write
let newsComputed = computed({
  // Evaluates the function
  get: (a)= > news.value + 2.// The assignment function
  set: val= > {
    news.value = news.value - 3}})Copy the code

Here we use readable and writable computed properties to process list data. Remember our last component, search.vue, we can filter list data that is useful to our users by combining the Search values entered by the user in the Search box with computed properties. So we first get the searchValue shared by the search. vue Search box from the shared instance of store, and then use the native string method indexOf and array method filter to filter the list data. Then return the new list data, newsComputed, and render the new list data on the view layer with the V-for instruction. In this way, the original list data, NEWS, can be returned when there is no search box to retrieve values, and the new list data, newsComputed, can be returned when there is a search box to retrieve values.

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

Program source code

If the articles and notes can help you or inspire you, please do not be too generous with your likes and stars, your will certainly be my biggest motivation 😁

  • Github.com/Wscats/vue-…