The original

I have been researching small programs recently, and I stumbled upon the MPVue framework by chance. It happened that I loved Vue so much that I decided to get stuck. Once saw a copy of the old version of Didi small program in Nuggets, feel quite good, but it is based on native small program, so I decided to spend a period of time with MPvue to rewrite. It’s time for dinner.


rendering

For more information, see github.com/QinZhen001/…



The directory structure

┣ ━ API# Store network request related┣━ common ● ┣━ constant // constant ┣━ CSS //weui. CSS ┣━ less // universal less style and variable L ━ lib // third-party library qqmap- wX-jssdk. js chain ━ Components ● The extracted component ┣━ addressList. Vue ┣━ common-footer. Vue ┣━ driver-header. Vue ┣━ Loading-sprinner Star. vue ┣━ pages ● page ┣━ Cars // select car ┣━ cityChoose // select city ┣━ Destination // select destination port ━ evaluation // evaluate port ━ index // home page page ( Login // login to ┣━ orderCancel // order cancellation ┣━ orderClose // order closure ┣━ orderService // orderService ┣━ orderWhy // ask the cause of failure ━ starting // select the starting place of the l ━waitJs ┣━ mutation-types. Js ┣━ mutations. Js L ━ state ┗ ━ static# static resource, store imagesCopy the code


Vuex data

const state = {
  curNavIndex: 0.// Current header navigation index
  phone: ' '.// Login number
  curCity: ' '.// Current city
  startPlace: 'Point of departure'./ / origin
  startFormattedPlace: ' '.// A more humane description of the point of departure
  startPosition: [],        // contains startLatitude and startLongitude
  destination: 'Where are you going?'./ / destination
  endPosition: [],      // Contains endLatitude and endLongitude
  driver: {},       / / driver information contains Cartnumber, cart, id, name, stars
  cost: 0       / / cost
}Copy the code


Functional details


Head navigation slides automatically



In order to make the head navigation automatically slide out when it is clicked, the head navigation automatically slides when swiper is swiped, and the head navigation automatically slides when the car is selected on the CARS page and the car is returned to the Index page. I maintain an index value curNavIndex in VUEX. The scroll left value of the scroll-view varies according to the curNavIndex.

So how do YOU set the exact scroll left value?

Wechat applet cannot conduct Dom manipulation, so it cannot dynamically get the element width. So I maintain an array navOffsetArr based on the width of each item in the header navigation

  Margin = 32+10*2 = 52
  const NAV_SMALL_WIDTH = 52;
  // Margin = 48+10*2 = 68
  const NAV_BIG_WIDTH = 68;


  this.navOffsetArr = [
        0.0,
        NAV_SMALL_WIDTH,
        NAV_SMALL_WIDTH * 2,
        NAV_SMALL_WIDTH * 2 + NAV_BIG_WIDTH,
        NAV_SMALL_WIDTH * 2 + NAV_BIG_WIDTH * 2,
        NAV_SMALL_WIDTH * 3 + NAV_BIG_WIDTH * 2,
        NAV_SMALL_WIDTH * 4 + NAV_BIG_WIDTH * 2
      ]Copy the code

Get index value

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

Watch listens for index values and gets different navScrollLeft values when curNavIndex changes

 watch: {
      curNavIndex(newIndex){
        this.navScrollLeft = this.navOffsetArr[newIndex]
      }
    }Copy the code

Finally, the scrollleft and navScrollLeft are bound to achieve automatic sliding

<scroll-view class="nav" scroll-x="true" scroll-with-animation="true" :scroll-left="navScrollLeft"> ...... . </scroll-view>Copy the code


The home page automatically saves location information

When entering the index home page, the current city, current latitude and longitude, and current address will be automatically stored in state as the starting point information. Here access Tencent map API, or more convenient.

  wx.getLocation({
          type: 'gcj02'.success: (res) = > {
            reverseGeocoder(qqmapsdk, res).then(res= > {
              this.saveStartPlace(res.result.address)
              this.saveFormattedStartPlace(res.result.formatted_addresses.recommend)
              this.saveCurCity(res.result.address_component.city)
            })
            this.saveStartPosition([res.latitude, res.longitude])
          }
        })Copy the code

mapMutations

methods: { ... mapMutations({saveCurNavIndex: 'SET_CUR_NAV_INDEX'.saveStartPlace: 'SET_START_PLACE'.saveFormattedStartPlace: 'SET_FORMATTED_START_PLACE'.saveCurCity: 'SET_CUR_CITY'.saveStartPosition: 'SET_START_POSITION'.saveCost: 'SET_COST'})}Copy the code

ReverseGeocoder () is a function that converts position into address, which is a encapsulation of QQmapSDK.ReverseGeocoder ()

function reverseGeocoder(qqmapsdk, {latitude, longitude}) {
  return new Promise((resolve, reject) = > {
    qqmapsdk.reverseGeocoder({
      location: {
        latitude: latitude,
        longitude: longitude,
      },
      success: (res) = > resolve(res),
      fail: (res) = > reject(res)
    })
  })
}Copy the code

So when we go to the index home page, we can see the data saved to vuex successfully in the Console



Choice of starting point




There are some pits when using the Map component in MPvue, which will be covered later.


Map a map

 <map class="map-didi"
         id="map-didi"
         :latitude="latitude"
         :longitude="longitude"
         :markers="markers"
         @regionchange="regionChange"
         @begin="begin"
         @end="end"
         show-location
    >
    ...
</map>Copy the code

When initializing the map, move the center of the map to startPosition, or if startPosition does not exist, move the center of the map to the coordinates of the current position obtained by wx.getLocation()

 initLocation(){
        if (this.startPosition.length) {
          this.latitude = this.startPosition[0]
          this.longitude = this.startPosition[1]}else {
          wx.getLocation({
            type: "gcj02".success: (res) = > {
              this.longitude = res.longitude
              this.latitude = res.latitude
            }
          })
        }
      }Copy the code

It uses random data to simulate nearby cars, which are then added to this.markers, which are dynamically set according to the curNavIndex, to show different car ICONS when selecting different services

   this.markers = []
        const carNum = getRandomNum(3.8)
        for (let i = 1; i <= carNum; i++) {
          // Define a car object
          let car = {
            id: 0.iconPath: "/static/img/car/cart1.png".latitude: 0.longitude: 0.width: 35.height: 15
          }

          / / random value
          const lon_dis = (Math.ceil(Math.random() * 99)) * 0.00012;
          const lat_dis = (Math.ceil(Math.random() * 99)) * 0.00012;

          car.id = 2 + i
          car.latitude = this.latitude + lat_dis
          car.longitude = this.longitude + lon_dis
          car.iconPath = `/static/img/car/cartThe ${this.curNavIndex + 1}.png`
          this.markers.push(car)
        }Copy the code

The red location icon in the center of the map and the text of pickup time are realized by wrapping the cover image with the cover view

<cover-view class=" center-center "> <cover-view class="text-center"> Fastest {{minutes}} min pick up </cover-view> <cover-image class="inverted-triangle" src="/static/img/triangle-down.png"></cover-image> <cover-image class="img-center" src="/static/img/marker2.png"></cover-image> </cover-view>Copy the code

Inverted triangle is an inverted triangle image, and since cover-View cannot implement complex CSS styles, the inverted triangle effect at the bottom can only be achieved with images.


We do not recommend using controls in map. The official statement also states that controls will soon be obsolete. Please use cover-view


Choose a destination



The address fuzzy retrieval can be achieved by using qqmapsdK.getSuggestion () and setting region as curCity. When the address is selected, qqmapSDK.geocoder () is used to resolve the address to obtain the relevant data of the destination, and then the data is stored in state through mapMutations

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


Fuzzy retrieval

 qqmapsdk.getSuggestion({
          keyword: value,
          region: this.curCity,
          success: (res) = > {
            this.addresses = res.data
          }
        })Copy the code

When an address is clicked, the address is parsed to save data

choosePlace(item){
        //item.address Specifies the specific address
        //item.title Short semantic address
        console.log(item)
        qqmapsdk.geocoder({
          address: item.address,
          success: (res) = > {
            this.saveEndPosition([res.result.location.lat, res.result.location.lng])
            this.saveDestination(item.title)
            this.goBack()
          },
          fail: (err) = > {
            console.log(err)
          }
        })
      }Copy the code

mapMutations

methods: { ... mapMutations({saveDestination: 'SET_DESTINATION'.saveEndPosition: 'SET_END_POSITION'})}Copy the code


Select the city



The style here is implemented according to the current didi small program. As long as the selected city is saved in the state curCity, the search function has not been developed yet. GetCityList data using Tencent map API qqmapsdk.getcitylist (). Initialize an empty object temp_citys, and then create a key based on the capitalization of the city’s initial letter. The value of the key is an array containing all cities starting with the city’s initial letter. Finally, assign temp_citys to this.citylist

 qqmapsdk.getCityList({
        success: (res) = > {
          const result = res.result[1]
          let temp_citys = {} // Use temp_citys to avoid frequent changes to data
          for (let i = 0; i < result.length; i++) {
            let key = result[i].pinyin[0].charAt(0).toLocaleUpperCase()
            if(! temp_citys[key]) { temp_citys[key] = [] } temp_citys[key].push(result[i].fullname) }this.cityList = temp_citys
        }
      })Copy the code

Some other pages will not mention, interested partners can go to see the source code

Some benefits of using MPVue


You can use Vuex

Using VUEX for state management makes it easier to build complex applications. Here’s a debugging tip. Use createLogger() and you can see the state change clearly on the Console

Index. js under store

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import createLogger from 'vuex/dist/logger'

Vue.use(Vuex)

constdebug = process.env.NODE_ENV ! = ='production'

export default new Vuex.Store({
  state,
  mutations,
  strict: debug,
  plugins: debug ? [createLogger()] : []
})Copy the code

When using vuex, import store in the corresponding page’s main.js and assign store to vue.prototype. $store

Such as:

import Vue from 'vue'
import App from './wait.vue'
import store from '.. /.. /store/index'

Vue.prototype.$store = store

const app = new Vue(App)
app.$mount(a)Copy the code


Componentized development

Mpvue componentized development is more convenient, and it is also convenient to transplant components to other projects. The complete Vue development experience improves code reuse.

For example, here’s the search-bar:

<template>
  <div class="search-bar">
    <div class="text-location" @click.stop="chooseCity">{{curCity}}</div>
    <input type="text"
           v-model="search"
           class="input-location"
           placeholder="Where do you get on?"
           placeholder-style="color:#cccccc">
    <div class="cancel-location" @click.stop="cancel"> Cancel </div> </div> </template> <scripttype="text/ecmascript-6">
  import {debounce} from '.. /utils/index'

  export default{
    props: {
      curCity: {
        type: String,
        default: '暂无'}},data() {return {
        search: ' '
      }
    },
    methods: {
      cancel(){
        this.$emit('cancel')},clear(){
        this.search = ' '
      },
      chooseCity(){
        this.$emit('chooseCity')
      }
    },
    watch: {
      search(newVal){
        debounce(() => {
          this.$emit('search', newVal)
        }, 500)()
      }
    }
  }
</script>Copy the code

Here the debounce() function is introduced for throttling processing


You can use Async/await

Native applets already support Promises, but async/await is not yet supported. Using the MPvue framework we can encapsulate some asynchronous functions and avoid callback hell.

For example, network request

export function request(url, method = 'GET', data, header = {}) {
  return new Promise((resolve, reject) = > {
    wx.showLoading({title: 'In the middle of... '})
    wx.request({
      url: baseUrl + url,
      method,
      data,
      header: {'Content-Type': 'json'},
      success: function (res) {
        if (res.statusCode === 200) {
          resolve(res.data)
        } else {
          showToast('Unknown error occurred! ')
          reject(res.data)
        }
      },
      fail: function () {
        showToast('Failed to get data! ')},complete:function () {
        wx.hideLoading()
      }
    })
  })
}
Copy the code
async getInitData(){
    const res = await request('/comments')... }Copy the code


Some pits using MPvue

Young people more impulsive, leng qingqing, said many are tears, the official documents must be better to look, first of all to mention some of the conventional pit.


Nested list rendering

Just note that nested list rendering must specify a different index!



Example:

<! <template> <ul v-for= <template> <ul v-for="(card, index) in list">
        <li v-for="(item, itemIndex) in card">
            {{item.value}}
        </li>
    </ul>
</template>Copy the code


regionchange

The bindregionChange event directly changes bind to @regionChange in the DOM. This event is also very special. Its event type is begin and end. So you can’t tell what the event is in handleProxy, so you have to listen for both the event name and the event type when you listen for this event, right

<map 
    @regionchange="functionName"
    @end="functionName" 
    @begin="functionName">
<map>
Copy the code


Event triggering problem

For a simple example, the slider component has a bindchange attribute, which is an event triggered by a single drag. What if we want to get the corresponding value?

In applets we use: event.detail

But in mpvue you write event.mp.detail


The map flashing

It’s a big pit where the map lights up when you update it dynamically, so you can’t move it

Bug with map component Bindregionchange:

https://github.com/Meituan-Dianping/mpvue/issues/401


Reason: MPVue updates the entire data when updating an attribute, which is inefficient in the case of a large amount of data, and frequent changes of data in the data will also lead to the lag problem


Solution: Use dirty check optimization every time data is updated

Github.com/Meituan-Dia…


But personally feel this direct change source code way or more monster, so found another way

    <map class="map-didi"
         id="map-didi"
         @regionchange="regionChange"
         @begin="begin"
         @end="end" >
    </map>Copy the code
let touchTimeStamp = 0

      regionChange(){ 
      
      },
      begin({timeStamp}){
        touchTimeStamp = timeStamp
      },
      end({timeStamp}){
// Add time judgment
        if (timeStamp - touchTimeStamp > 50) {
          this.mapCtx.getCenterLocation({
            success: (res) = > {
              reverseGeocoder(qqmapsdk, res).then(res= > {
                this.saveStartPlace(res.result.address)
                this.saveFormattedStartPlace(res.result.formatted_addresses.recommend)
              })
              const lon_distance = res.longitude - this.longitude
              const lat_distance = res.latitude - this.latitude
              // Update the current position coordinates
              this.longitude = res.longitude
              this.latitude = res.latitude
              // Determine how far the screen moves, if it exceeds the threshold
              if (Math.abs(lon_distance) >= 0.0022 || Math.abs(lat_distance) >= 0.0022) {
                // Refresh nearby cars
                this.updateCars()
                // Refresh the wait time
                this.minutes = getRandomNum(3.12}}})}}Copy the code

In order to prevent map from constantly triggering begin and end events and causing frequent data updates, a double judgment is made here. When the triggering time of end event minus the triggering time of Start event exceeds a set time, and when the center point moves beyond a threshold, we will update data. This is essentially a throttling process.



Some pit of applets

The conventional pit is not mentioned, here is the bizarre pit.

Cover – the pit of the view

Cover-view A text view overlaid on top of a native component. Native components that can be overlaid include Map, video, Canvas, camera, live-player, and live-pusher. Only nested cover-view and cover-image are supported.

Supports only basic positioning, layout, and text styles. You cannot set unilateral border, background-image, shadow, overflow: visible, etc



So what if we want to implement a unilateral border in a cover-view?



Add a cover-view with a width of 1px to simulate a unilateral border

<cover-view class="footer-bar"> <cover-view class="text" @click.stop="cancel"> Cancel order </cover-view> <cover-view Class ="right-border"></cover-view> <cover-view class="text" @click.stop="endTrip"> endTrip </cover-view> <cover-view Class =" border"></cover-view> </cover-view> </cover-view>Copy the code
.footer-bar {
        padding: 0 12px;
        display: flex;
        align-items: center;
        height: 44px;
        color: rgba(0.0.0.7);
        background: #fff;
        font-size: 0;
        .text {
          flex: 1 1 auto;
          display: inline-block;
          height: 22px;
          line-height: 22px;
          text-align: center;
          font-size: 18px;
        }
        .right-border {
          flex: 0 0 1px;
          height: 22px;
          width: 1px;
          background-color: #d9d9d9; }}Copy the code


The map component has the highest level. How do I create a shadow effect on the Map component?

The implementation is similar, using a cover-image to add an image that can be overlaid on the map component to simulate shadows


See this article for details: juejin.cn/post/684490…


The project address

Github.com/QinZhen001/…

Welcome friends to exchange and study together, if you like the project, please give a little star


conclusion

There is no rush for success in the long way of learning. Technology changes rapidly, and mastery of the unchanging core is king. It feels good to keep polishing your work, and then refine it if you have a chance.


In addition, I am a junior now, and I plan to find an internship after the summer vacation. Is there anyone in Guangzhou willing to accept me?


link

Drops a summer, small program car to https://juejin.cn/post/6844903616961052679

The pit of the request www.cnblogs.com/huangenai/p network request…

The unavailability of mapState and mapGetters for mpvue + VUex micro program development

Blog.csdn.net/wp_boom/art…