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
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…