preface

After contacting small programs for a period of time and doing some projects more or less, I started my journey of Vue again. Influenced by its core ideas, I have higher pursuit of data/state management, componentization, cross-platform, etc. Mpvue is a front-end framework for developing small programs using vue.js, so I started my journey of MPVue. I want to improve the readability of the code while also adding a bit of vue.js development experience.

Technology stack

Front end: wechat applets, MPVue back end: KOA database: mongodb database visualization tool: Robo3T

Mall small program run

A basic mall small procedures, including the front of the home page, classification, shopping cart, my (order) four TAB pages, back-end data definition, classification, and access. I’ll cover some of the main features, compare native applets with vue.js, and what back-end databases can do. If you want to know or have any questions, you can go to the source code to understand oh.

Results to share

I. Front desk page and function display

Home page:

1. Component encapsulation

For example, the home page consists of three parts: rotating recommendations in the head, horizontal sliding recommendations in the middle, and vertical scrolling list of products. These three parts are almost all the necessary functions of mall app. As we all know, such functional components are basically indispensable in all apps. The first thing I learned from VUE was the feature of high reusability of component code. When some components are reused and migrated to other components or pages, Swiper and Scroll, which still support native applet in MPvue component, are compatible with both. For developers familiar with applet and VUE, this function can be very efficient to complete. Finally, the main page file is composed of a component, readable, for beginners, the idea of module encapsulation is the first to have.

<template>
  <div class="container" @click="clickHandle('test click', $event)">
    <div class="swiperList">
      <swiper :text="motto" :swiperList="swiperlist"></swiper>
    </div>
    <div class="navTab">
      <div class="recTab"> < text > - recommended for you - < / text > < / div > < / div > < scroll > < / scroll > < div class ="hot"> <span> -- </span> </div> <hot :v-text="motto"></hot>
    <div class="fixed-img">
      <img :src="fixImg" alt="" class="fix-img">
    </div>
  </div>
</template>
Copy the code

But questions about the component encapsulation and composition, as a result of recent research vue performance optimization and the user experience of the knowledge, consider a serious problems: first, look at the common vue writing: put an app in the HTML component, app and references to other child components in the component, forming a component with app as the root node in the tree:

<body>
    <app></app> 
</body>
Copy the code

This leads to performance problems. To initialize a parent component, you must first initialize its children, which in turn have their own children. To initialize the root tag, start bubbling at the bottom and initialize all the components of the page. So our page will not display until all components have been initialized. This result is obviously not what we want. Every time users click on the page, they have to face blank and response for a while, because after the page is started, they have to respond not only to the component that initializes the page, but also to other components contained in the app, which seriously slows down the page opening speed. The better result is that pages can be rendered sequentially streaming from top to bottom, which may increase the overall time, but reduce the first screen time and make the page open faster from the user’s point of view. Some online methods are similar, each has advantages and disadvantages, so… I’m in the middle of a crazy experiment, waiting for good news.

**2. Bind Class and Style **

When referring to the same child in different parent components, but each needs to receive the bound dynamic style to render different styles, the bound CSS style is a big hole: Mpvue does not support the transmission of style in the form of object. At first, I was frustrated with the style. There are few details about MPvue on the Internet. I tried using strings, and sure enough… Change the code to doubt life, the results you tell me that the start of life is wrong, how can not heartache? .

Solution:

<template>
<div class="swiper-list">
    <d-swiper :swiperList="swiperlist" :styleObject="styleobject"></d-swiper>
</div>
</template>
<script>
    data() {
        return {
            styleobject:'width:100%; height:750rpx; position:absolute; top:0; z-index:3'
        }
    }
</script>
Copy the code

3. The “V-for nesting” trap

When doing VUE project, it is inevitable to use loop, index index value is needed, but V-for index cannot be used repeatedly when nesting, inner loop and outer loop cannot share the same index.

<swiper-item v-for="(items,index) in swiperList" :key="index">
    <div v-for="item in items" class="swiper-info" :key="item.id" @click="choose" >
        <image :src="item.url"  class="swiper-image" :style="styleObject"/>
    </div>
</swiper-item>
Copy the code

The above code will report an error:

<swiper-item v-for="(items,index) in swiperList" :key="index">
    <div v-for="(item,i) in items" class="swiper-info" :key="i" @click="choose" >
        <image :src="item.url"  class="swiper-image" :style="styleObject"/>
    </div>
</swiper-item>
Copy the code

4. This points to the problem and the application of the arrow function

All lifecycle hooks are called with their ‘this’ context pointing to the vue instance invoking it. Use this in all lifecycle hook methods of Vue (created, Mounted, updated, and destroyed). This points to the Vue instance that called it (New Vue). Same thing with MPVue. As we all know, the this in the lifecycle function refers to the Vue instance, so we can access data and perform operations on properties and methods.

Props: {goods:Array}, mounted:function(options){
    let category = [
      {id: 0, name: 'all'},
      {id: 1, name: 'JAVA'},
      {id: 2, name: 'C++'},
      {id: 3, name: 'PHP'},
      {id: 4, name: 'VUE'},
      {id: 5, name: 'CSS'},
      {id: 6, name: 'HTML'},
      {id: 7, name: 'JavaScript'}
    ]
    this.categories = category
    this.getGoodsList(0)
  },
methods: {
    getGoodsList(categoryId){
      console.log(categoryId);
      if(categoryId == 0){
        categoryId = ' '
      }
      wx.request({
        url: 'http://localhost:3030/shop/goods/list',
        data: {
          categoryId: categoryId
        },
        method: 'POST', success: res => { console.log(res); this.goods = res.data.data; }}})},Copy the code

Ordinary function this points to the function of the context, namely calls its context, so here, for the life cycle function with normal function or arrow function actually had no effect, because it is the definition of the environment and running environment are the same, so can also take the vue instance data, properties and methods. In arrow functions, this refers to the outermost block of code that defines it. ()=>{} is equivalent to function(){}.bind(this); So this of course refers to a Vue instance. Wx.request ({}) {goods is not defined}} Originally, the normal function’s this pointed to the context of getGoodsList(), so it never got the value.

5. OnLoad and onShow

OnLoad () could not get updated data when clicking goods on the home page to jump to the details page. First of all, although onLoad: function (options) can accept the value, but this is only loaded once, not what I want, I need to jump to another page from this page (without closing) to receive the corresponding product data. Therefore, the code needs to be placed inside onShow, and the current state will be queried every time the page is loaded, and the sub-objects of the corresponding data will be queried and updated and rendered to the detail page.

onShow: function(options){
    // console.log(this.styleobject)
      // console.log(options)
    wx.getStorage({
      key: 'shopCarInfo',
      success: (res) =>{
        // success
        console.log(`initshopCarInfo:${res.data}`)
        this.shopCarInfo = res.data;
        this.shopNum = res.data.shopNum
      }
    })
    wx.request({
      url: 'http://localhost:3030/shop/goods/detail'// Request the detail table data method:'POST',
      data: {
        id: options.id
      },
      success: res =>{
        // console.log(res);
        const dataInfo = res.data.data.basicInfo;
        this.saveShopCar = dataInfo;
        this.goodsDetail.name = dataInfo.name;
        this.goodsDetail.minPrice = dataInfo.minPrice;
        this.goodsDetail.goodsDescribe = dataInfo.characteristic;

        let goodsLabel = this.goodsLabel
        goodsLabel = res.data.data;
        // console.log(goodsLabel);
        this.selectSizePrice = dataInfo.minPrice;
        this.goodsLabel.pic = dataInfo.pic;
        this.goodsLabel.name = dataInfo.name;
        this.buyNumMax = dataInfo.stores;
        this.buyNumMin= (dataInfo.stores > 0) ? 1:0; }})}Copy the code

OnLoad: Life cycle function – Listens for the initialization of the applet and when the initialization is complete, onLoadh is fired (globally only once). OnShow: lifecycle function – Listens for the applet display. OnShow is triggered when the applet starts, or when it goes from the background to the foreground display.

Background database and data access

1. Set up the HTTP service

In the global configuration file: 1). Introduce and instantiate koA

const Koa = require('koa');
const app = new Koa()
Copy the code

2).app.listen(port number) : Creates and returns an HTTP server, passing the given parameters to Server#listen().

const Koa = require('koa'); Const app = new koa (); app.listen(3000); The app.listen() method here is just a syntactic sugar for the following methods: const HTTP = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);
Copy the code

After the basic configuration is complete, we can use “http://localhost3030+ request address parameter” to get the database value.

2.Koa-router Middleware

Koa-router is a common KOA routing library. If you rely on CTx.request. url to process routes manually, a lot of processing codes will be written. In this case, the middleware of the corresponding route is required to control the route. Switch the interface with route switching, “data” interface.

3. Build an object model

Before we build the library, let’s talk about object modeling. Mongoose is an object model tool for convenient operation of mongodb in node.js asynchronous environment. This NPM package encapsulates the methods for manipulating mongodb. Mongoose has two characteristics: 1. Design non-relational numbers based on the idea of relational database; 2

const mongoose = require('mongoose')

const db = mongoose.createConnection('mongodb://localhost/shop'// Establish a connection to the shop database (shop is my local database name)Copy the code

Local database shop has built five data tables, namely “Address management”, “Commodity Details”, “Order Details”, “Commodity List” and “User List” :

The Schema interface defines the data model: Schema is used to define the structure of the database. Similar to the data definition when creating a table (not only the structure and attributes of the document can be defined, but also the instance methods, static model methods, composite indexes of the document can be defined), each Schema will be mapped to a collection in mongodb, but the Schema does not have the ability to operate the database. The mapping between data tables and objects can also be checked to check whether each group of data meets the conditions defined in the model. At the same time, each object is mapped to a data report and can be saved using this object. This operation is equivalent to the operation of data tables instead of tedious operations like mysql command lines

Take the “List of Goods” data table as an example:

// Models are defined by the Schema interface. var Schema = mongoose.Schema; const listSchema = new Schema({ barCode: String, categoryId: Number, characteristic: String, commission: Number, commissionType: Number, dateAdd: String, dateStart: String, id: Schema.Types.ObjectId, logisticsId: Number, minPrice: Number, minScore: Number, name: String, numberFav: Number, numberGoodReputation: Number, numberOrders: Number, originalPrice: Number, paixu: Number, pic: String, pingtuan: Boolean, pingtuanPrice: Number, propertyIds: String, recommendStatus: Number, recommendStatusStr: String, shopId: Number, status: Number, statusStr: String, stores: Number, userId: Number, videoId: String, views: Number, weight: Number, })Copy the code

Defines the types of data items needed in the data table. The data table will correspond to the incoming data one by one:

4. Koa-router

const Router = require('koa-router'Koa-router const router = new router (); // Create router instance object // register router. Post ('/shop/goods/list', async (CTX, next) => {const Goods = ctx.request. Body const Goods = db.db.model() const Goods = ctx.request.'Goods', db.listschema) // The first 'db' is the custom from require, the second 'DB' is the database connected to mongodb, the model generation refers to the entity data (according to the schema to obtain the data under this field, Body = await new Promise((resolve, reject) => {//ctx.body is ctx.response.body, Wait until you get the data before you send out the bodyif (params.categoryId) {
      Goods.find({categoryId: params.categoryId},(err, docs) => {
        if (err) {
          reject(err)
        }
        resolve({
          code: 0,
          errMsg: 'success',
          data: docs
        })
      })
    } else {
      Goods.find((err, docs) => {
        if (err) {
          reject(err)
        }
        resolve({
          code: 0,
          errMsg: 'success',
          data: docs
        })
      })
    }
  })
})
Copy the code

All database operations are asynchronous operations, so you need to encapsulate promise to do this, so by POST “http://localhost3030/shop/goods/list” can access to the local shop the database. We all know that node.js has request objects and respones objects. Koa encapsulates these two objects in CTX objects. The CTX parameter is a variable passed in by KOA that encapsulates request and Response. It allows us to access request and Response (the front end gets the data via AJAX request HTTP). We can retrieve data from the database via CTX request or. The ctx.body attribute is the content sent to the user. The body is the body of the HTTP response, and the header is the response header ctx.body = ctx.res.body = ctx.response

5. Model layer Settings for data caching

1). Why do data caching? Here have to mention the importance of a data cache, although I’m from the local database access to data, but due to the amount of data is more, moreover said in front of the performance optimization has yet to be completed, or there is a certain request time every time, every time there is no need to open to request it again the back-end, rendering the page, so you need to need to do the local cache frequently used data, This can greatly speed up page rendering. 2). Set the model layer

setGoodsList: function (saveHidden, total,  allSelect, noSelect, list) {
      this.saveHidden = saveHidden,
      this.totalPrice = total,
      this.allSelect = allSelect,
      this.noSelect = noSelect,
      this.goodsList = list
      var shopCarInfo = {};
      var tempNumber = 0;
      var list = [];
      shopCarInfo.shoplist = list;

      for (var i = 0; i < list.length; i++) {
        tempNumber = tempNumber + list[i].number
      }
      shopCarInfo.shopNum = tempNumber;
      wx.setStorage({
        key: "shopCarInfo",
        data: shopCarInfo
      })
    },

Copy the code

Encapsulate the methods that need to store data locally into a method model. When local storage is needed, reference is directly made. Nowadays, architecture ideas commonly used in VUE and React have certain requirements on model layer encapsulation.

bindAllSelect() {
      var list = this.goodsList;
      var currentAllSelect = this.allSelect
      if (currentAllSelect) {
        list.forEach((item) => {
          item.active = false})}else {
        list.forEach((item) => {
          item.active = true}) } this.setGoodsList(this.getSaveHide(), this.totalPrice(), ! currentAllSelect, this.noSelect(), list); },Copy the code

Conclusion:

I have been frustrated for many times in writing this project, because a lot of VUE can be used but mpVue cannot be implemented, resulting in many detours and many pits. But isn’t the growth of the program ape in the process of falling down and getting up again? Composition is not easy, partners can reward points reward points… By the way, attached is my project address: “MPvue-Demo”, but there is still a lot to be improved, let’s go together for a long way!