Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

preface

After a number of UNIAPP applets project development, I will review all my experience in pit combat summary, the following content is only for uniAPP development micro channel applets scene, in addition to uniAPP there are some small program development attention points, I hope this article can help you avoid pit. Dry goods (dry to thirst)

Uniapp profile

First, the official introduction. If you are familiar with UNIAPP, you can skip it.

Uni-app is a framework for developing all front-end applications using vue.js. Developers write a set of code that can be published to iOS, Android, Web (responsive), mini programs (wechat/Alipay/Baidu/Toutiao /QQ/ Kuaishou/Dingding/Taobao), Kuaiapp and many other platforms.

With UniApp we can use vue.js syntax to write applets. In short, we can develop applets by replacing the template template from HTML with WXML in normal vue.js development, so what’s the advantage of this?

  1. Reduce the technical learning pressure, if you know vUE you can directly start, if you don’t know VUE incidentally learn VUE, but also can enjoy the surrounding ecological benefits of VUE
  2. Syntax intersection with native applet syntax is more convenient, if you have ever developed a applet you will feelthis.setData() 与 Native applet componentsThe trouble and fear of development
  3. Uniapp can be packaged into small programs of various platforms and also into APP, as shown in the figure below on the official website.

4. The code prompt of the small program development tool is relatively slow, although it has made great progress compared to the last few years, but it still does not reach the level I want.

Uniapp development preparation

Ide Tool Installation

HBuilder website link

To do a good job, he must sharpen his tools. The first step in uniApp development is to install an official IDE called HbuilderX. This is the only IDE I recommend for uni-App development. It is very convenient and helps us to quickly generate page templates and component templates, visual page configuration, excellent code prompts, code completion capabilities, etc.

HbuilderX still has some disadvantages compared to vscode, and some of the plug-in ecology is not very robust. But developing uni-app is HbuilderX (official stuff, after all)

HbuilderX is easy to install, just download it from the official website. Once the download is complete we need to install some plug-ins. Open HbuilderX and select Tools -> Plug-in Installation from the top bar, as shown below

When you open it, you can see the currently installed plug-ins and some official plug-ins. I can’t remember which ones were not installed at the beginning due to the age, but below are the ones that must be installed, one is for git version management and one is for sass compilation.

We can also set our editor theme and character size, code indentation, etc., if you have other editor habits can be adjusted appropriately. I originally developed using vscode, so I switched to the jalen theme, and the actual page looks exactly like vscode one dark pro editor style and code color!

I think the above steps are necessary, comfortable and beautiful development environment can greatly enhance your interest in development!

Project directory structure analysis

New project

Once HbuilderX is installed and configured, we are ready to develop. First we need to create a project, we click on file -> New -> Project in the top left corner of ide, then we select UniApp project and template select default template

After the creation, we can see that a new file named after the project has been added to the file tree on the left, which is the project template that HBuilder has built in for us

The following is an introduction to the project framework given by UniApp. There are some folders that are not built into the template, so we need to manually create the following folders, such as the outermost components, to place some of our global generic components

β”‚ β”œβ”€ β”œβ”€index β”‚ β”œβ”€ β”œβ”€ vue index page β”‚ β”œβ”€ β”œβ”€ vue index page β”‚ β”œβ”€ β”œβ”€ vue index page β”‚ β”œβ”€ β”œβ”€ vue index page β”‚ β”” ─ list. Vue list page β”œ ─ the static storage application reference local static resource directory (such as images, video, etc.), note: can only be stored in the static resources β”œ ─ uni_modules store [uni_module] (/ uni_modules) specification of the plugin. β”‚ β”œβ”€ WxComponents β”‚ β”œβ”€ app.vue β”‚ β”œβ”€ wxComponents β”‚ β”œβ”€main.js β”‚ Vue β”œβ”€manifest.json, β”œβ”€ pages.json, page-routing, navigation, tabs, etcCopy the code

In the process of development, ACCORDING to the development habit of VUE project, I created functional classification folders in Pages according to business functions. The most common one is to distinguish functional modules according to tabbar on the home page. Each folder stores all pages involved in this function. There is also a separate Components folder in each feature folder for the page-dependent components in that feature only folder.

A new page

When we need to create a new page, we can quickly create it through the built-in page template of HBuilder. Right click the current project in the left file tree, choose New page, enter the page name and select template to create the page. Generally, I choose the TEMPLATE of SCSS. When created, the page will be automatically registered with page.json.

Universal plug-in encapsulation

Since UniApp chooses to use vue.js as the development framework, we must make use of some excellent features in VUE, such as plugin. You can go to the official website to see the introduction of vUE plug-in.

Vue plugin official introduction link

By introducing plug-ins, we can greatly improve our development efficiency. Of course, if it is the first time to use UniApp for development, it may not be clear which functions are suitable to be encapsulated as plug-ins. Below, I will introduce some general plug-ins encapsulated in my actual development

We need to write a config file before encapsulation, so that we can quickly customize some colors and request paths, etc.

//congif.js
const config = {
	baseUrl:'https://example.cn'.// The base path of the request
	modalColor:'#5271FF'.// Popover color
}

module.exports = config
Copy the code

Pop-up plug-in

In applets, we use the official showModal or showToast API for some user interaction if we don’t have custom pop-ups and mimicry components. This very frequently used operation is a great way to wrap it up for quick invocation. The specific code is as follows

The plug-in code

const config = require('.. /config.js')

var message = {
	toast(title, type = 'text') {
		if (title.length > 15) {
			console.error('Toast is more than 15 characters in length and the current length is' + title.length)
			return
		}
		var icon = 'none'
		if (type) {
			switch (type) {
				case 'text':
					icon = 'none'
					break
				case 'suc':
					icon = 'success'
					break
				case 'err':
					icon = 'error'
					break
			}
		}
		uni.showToast({
			title,
			icon
		})
	},
	confirm(title, confirmColor) {
		return new Promise((res, rej) = > {
			uni.showModal({
				title,
				cancelColor: '#b6b6b6'.confirmColor: confirmColor || config.modalColor,
				success: (result) = > {
					if (result.cancel) {
						rej(result)
					} else if (result.confirm) {
						res(result)
					}
				}

			})
		})
	},
	async message(content, confrimText) {
		return new Promise((res) = > {
			uni.showModal({
				title: 'tip',
				content,
				showCancel: false.confirmColor: config.modalColor,
				success: (result) = > {
					res(result)
				}
			})
		})
	}
}
module.exports = message

Copy the code

Sample call

this.message.toast('Answer deleted')
Copy the code

Request the plugin

Our common request library in VUE is AXIos, and almost every developer has a secondary wrapper around AXIOS to reduce the amount of code needed to call and do some global configuration. In UNI-app, we don’t need to introduce a third party request library. We directly use the official UNI. request(OBJECT) API to call the request. Of course, we also need to do a secondary encapsulation. The specific code is as follows

The plug-in code

//http.js
const config = require('.. /config.js')
const message = require('./message.js')
var http = {
	post(path, params, contentType = 'form', otherUrl, ) {
		return new Promise((resolve, reject) = > {
			var url = (otherUrl || config.baseUrl) + path
			if(! checkUrl(url)) { rej('Request failed')
			}
			uni.request({
				method: 'POST',
				url,
				header: {
					"Content-Type": contentType === 'json' ? "application/json" :
						"application/x-www-form-urlencoded"
				},
				data: params,
				success: (res) = > {
					console.log('request: POST request' + config.baseUrl + path + 'success', res.data)
					resolve(res.data)
				},
				fail: (err) = > {
					message.toast('Request failed'.'err')
					console.error('request: request' + config.baseUrl + path + 'failure', err)
					reject('Request failed')}})})},put(path, params, contentType = 'form', otherUrl, ) {
		return new Promise((resolve, reject) = > {
			var url = (otherUrl || config.baseUrl) + path
			if(! checkUrl(url)) { rej('Request failed')
			}
			uni.request({
				method: 'PUT',
				url,
				header: {
					"Content-Type": contentType === 'json' ? "application/json" :
						"application/x-www-form-urlencoded"
				},
				data: params,
				success: (res) = > {
					console.log('request: PUT request' + config.baseUrl + path + 'success', res.data)
					resolve(res.data)
				},
				fail: (err) = > {
					message.toast('Request failed'.'err')
					console.error('request: PUT request' + config.baseUrl + path + 'failure', err)
					reject('Request failed')}})})},get(path, params, otherUrl) {
		return new Promise((resolve, reject) = > {
			var url = (otherUrl || config.baseUrl) + path
			if(! checkUrl(url)) {return
			}
			uni.request({
				url,
				data: params,
				success: (res) = > {
					console.log('request: GET request' + config.baseUrl + path + 'success', res.data)
					resolve(res.data)
				},
				fail: (err) = > {
					message.toast('Request failed'.'err')
					console.error('request: GET request' + config.baseUrl + path + 'failure', err)
					reject(err)
				}
			})

		})

	},
	delete(path, params, otherUrl) {
		return new Promise((resolve, reject) = > {
			var url = (otherUrl || config.baseUrl) + path
			if(! checkUrl(url)) {return
			}
			uni.request({
				url,
				data: params,
				method: "DELETE".success: (res) = > {
					console.log('request: the DELETE request' + config.baseUrl + path + 'success', res.data)
					resolve(res.data)
				},
				fail: (err) = > {
					message.toast('Request failed'.'err')
					console.error('request: the DELETE request' + config.baseUrl + path + 'failure', err)
					reject(err)
				}
			})

		})

	},

	async upload(path, fileArray, otherUrl) {

		if (typeoffileArray ! = ='object') {
			console.error('Request: argument error, please pass file array')
			return
		}
		var url = (otherUrl || config.baseUrl) + path
		if(! checkUrl(url)) {return
		}
		var arr = []
		for (let i in fileArray) {
			const res = await uni.uploadFile({
				url: otherUrl || config.baseUrl + path,
				filePath: fileArray[i],
				name: 'file'
			})
			console.log(res)
			if (res[0]) {
				console.error('Request: Upload failed', res[0])
				return
			}
			arr.push(JSON.parse(res[1].data).data)
		}
		return arr
	},

}

function checkUrl(url) {
	var urlReg = /^((ht|f)tps?) :\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])? $/;
	if(! urlReg.test(url)) {console.error('Request: Request path error' + url)
		return false
	}
	return true
}
module.exports = http

Copy the code

Sample call

async getAnswer() {
  const res = await this.http.get('/applet/answerList', {
    qId: this.question.id,
  })
  if (res.code === 200) {
    return res.data
  } else {
    this.message.toast('Request failed')
    return false}}Copy the code

As you can see in the code above, we introduced two dependency files config and message, which we’ll talk about later. In the HTTP object, there are five methods: POST, GET, Update, and delete, which correspond to restful API operations. There is also an upload method for uploading files. We don’t need to worry about cross-domain requests in applets. Our default request path is obtained from the config dependency file. At this time we can also use the first popover plug-in for a quick message prompt.

Memory card

We use vuex + vuex-persistedstate to store globalData and persist it. There are two ways to store globalData. We globalize data by adding the globalData value to app.vue, which we can use to save our applets version, for example.

export default {
  globalData: {
    version: '1.0.0'}}Copy the code

However, globalData data is not persistent and is reinitialized when we exit the applet and re-enter, so we have a global storage method called storage, which is similar to localStroge on the Web side. Through the official API to achieve the effect of local storage

In the small program is often not as complex as the web side of the data structure, although uniApp small program can also use VUex as a global data management library, but I still made a simplified version of their own use. This plugin was written a long time ago and is a very simple implementation. If you want to omit the setData operation, you can use proxy to hijack the stored value and store it locally.

The plug-in code

var store = {
	_init(){
		if(uni.getStorageSync('store') = =' '){
			uni.setStorageSync('store', {})return
		}
		const data = uni.getStorageSync('store')
		for(let i in data){
			if(!this[i]){
				this[i] = data[i]
			}
		}
	},
	setData(key,value){
		if(key == '_init'||key=='setData') {console.error('Store: invalid key value',key)
			return
		}
		this[key] = value
		uni.setStorageSync('store'.this)
		console.log(uni.getStorageSync('store'))}}module.exports = store

Copy the code

The sample used

this.store.setData('user', {name:'oil'}) / / assignment
console.log(this.store.user) / / read values
Copy the code

Form validation plug-in

Form validation is the use of a very lightweight open source form validation library JSValidate, you can directly click the link in the repository to see the source code, the following code is not put, talk about how to use. JSValidate gitee link

Sample call

// validate.js
const validate = {
	userForm: {intro:	'@ bio | the require'.college: '@ school | the require! Please choose your school '.nickname:'@ nickname | the require'}}module.exports = validate

Copy the code

Write a form validation method to the form and use a popover to indicate if the criteria are not met

//form.js validateForm() {const validate = require("./validate") const validator = new this.validator() var result = validator.check(validate.userForm, this.form, false) if (result ! == true) { this.message.message(result) return false } return true }Copy the code

Time processing plug-in

As for the time processing plug-in, I also used an open source lightweight JS library day.js in the project. Please refer to the official documents for specific usage methods. Day.js almost contains all operations related to time processing, such as time comparison and time conversion. Day.js official documentation link

Day. js has a function that is particularly commonly used, which is the function of relative time conversion. Relative time conversion is to convert a time string such as year month day hour minute second to several seconds ago, ten minutes ago, several months ago and so on. Using the following code, we use computed in vue for time string processing, pass a time string parameter to the computed method through a closure, and then return a Chinese relative time through the formNow method of day.js. Date is the plug-in I packaged and introduced.

The sample code

computed: {
  relativeDate() {
    return (date) = > {
      return this.date(date).fromNow()
    }
  }
}
Copy the code

When we have many plug-ins (as shown below), our plug-ins may share some configuration, such as config.js above. Then we can write an entry file called index.js to import our plug-ins and install our plug-ins in main.js.

The sample code

// index.js
const http = require('./lib/http')
const message = require('./lib/message')
const router = require('./lib/router')
const validator = require('./lib/validator')
const date = require('./lib/date')
const store = require('./lib/store')
const to = require('./lib/to')
const oil = {
  message,
  http,
  router,
  validator,
  date,
  store,
  to,
  install(Vue) {
    this.store._init()
    for (let i in this) {
      if (i == 'install') {
        continue
      }
      Vue.prototype[i] = this[i]
    }

    delete this.install
  }
}

export default oil
Copy the code
// main.js
import Vue from 'vue'
import oilUni from './oil-uni/index.js'
Vue.use(oilUni)
app.$mount()
Copy the code

We define an object called oil in our introductory file. In addition to the oil method, there is a special method called install. The purpose of install is to pass the Vue constructor to install as the first argument when we use vue.use (). We then tie all the methods to Vue’s prototype chain.

Once installed, we can access the wrapped plug-in simply by using this. + xx in the js of the page or component.

Common Component encapsulation

The purpose of plug-ins is to modularize our code so that common operations can be easily reused. Componentization helps us encapsulate and reuse a UI. Since the applet scenario is generally not very complex, there are not many very common components. Here are just some of the component libraries I use in my business and the components I encapsulate

Introduction of third-party component libraries

colorUI

The first one I recommend is colorUI, which I use on almost all applets. ColorUI is more of a style library than a component library. ColorUI making links

ColorUI encapsulates a lot of inline styles, so we don’t have to switch between template and style frequently, which greatly reduces our mental burden. An analogy can be made to tailwindCSS, but in HBuilderX, there are very robust colorUi-style code hints. It’s not just colorUI. HBuilderX gives you a complete code hint for your own styles or references. The following figure

ColorUI does not provide functional packaged components for us, but in its sample source code, most of the scenes can be directly copied and modified to use, such as picture uploading and form components. ColorUI sample page

The introduction of colorUI is also very simple, put the package of colorUI in the root directory of the project, and then reference it in the style of app. vue. ColorUI contains three files, one is main. CSS, which contains all the style codes of colorUI. Icon is the icon of colorUI, animation is the animation of colorUI, and the functions of all three packages are used by writing specified code in the inline class.

//App.vue
<style>
	@import "colorui/main.css";
	@import "colorui/icon.css";
	@import "colorui/animation.css";
</style>
Copy the code

vant-weapp

Vant is probably the most useful component library for vue Ecosystem on mobile. It covers almost everything, and vant has a version of app-specific apps that do app-specific things. Official documentation of Vant Syndrome

But vant weapp is written in the native small the grammar of the program, and the vue uniapp grammar some discrepancy, so introduce need to do some special operation, the following said the vant weapp how to introduce.

  1. To do this, you need to download Vant retry P and add the Vant folder to the project root directorywxcomponentFolder, if not, create a new one.

  1. inApp.vueTo introduce a style file,
<style>
	@import "/wxcomponents/vant/common/index.wxss";
</style>
Copy the code
  1. Imports are not imported directly into a page or component, but in the same way applets are usedpages.jsonThe following code is introduced in the specified pageusingComponentsAdded to the configuration itemComponent name: Component pathThen you can use it directly on the page
{
  "pages": [{"path": "pages/home/home"."style": {
        "navigationBarTitleText": ""."enablePullDownRefresh": false."usingComponents": {
          "van-progress": "/wxcomponents/vant/progress/index"}}}]}Copy the code

Note that the syntax in the official documents for Vant retry is mini-programs. You can do it yourself by adding vUE syntax

Rolling container component

After the introduction of the above two component libraries, we can find the corresponding components in most of the development scenarios, at most, a secondary encapsulation to adapt to the business.

Common Service Scenarios

The login page

In small programs, login often uses the fast authentication function of each platform, rather than entering the account password or mobile phone verification code on the Web. For example, wechat small programs can obtain user information by sending requests to the official API of wechat.

Obtaining User information

Therefore, when we design the page, we often only need a very simple button to obtain user information. Below, I will use the login function in a project to show you how UNIApp realizes wechat small program login to obtain user information

<template>
	<button class="cu-btn bg-blue shadow-blur round lg" @click="login">Login immediately</button>
</template>
<script>
export default {
    methods: {
	async login() {
            uni.showLoading({
		mask: true.title: 'Logging in'
		})
            const res = await uni.getUserProfile({
		desc: 'Used to store user data'
		})
	uni.hideLoading()
	if (res[0]) {
		this.message.toast('Fetch failed'.'text')
		return
	}
        this.getUser(res[1].userInfo)
        },
        async getUser(userInfo) {
            uni.showLoading({
                    mask: true.title: 'Logging in'
            })
            const loginRes = await uni.login()
            if (loginRes[0]) {
                    uni.hideLoading()
                    this.message.toast('Fetch failed')
                    return
            }
            const res = await this.http.post('/applet/weChatLogin', {
                    code: loginRes[1].code
            }, 'form')
            uni.hideLoading()
            if(! res.data) {this.router.push('/pages/form/userForm', {
                            userInfo,
                            handle: 'add'
                    })
                    return
            }
            this.store.setData('user', res.data)
    },}
        
Copy the code

Let’s take a look at the code above. First we use the UNI.getUserProfile API to get the user’s basic information, such as avatar, nickname, locale, etc. Next we call uni.login to get the code value. The code value is used to pass to the back end to get the unique identifier OpenID from wechat to the user.

I pass the code code to the back end, which parses openID and checks whether the current user has user information in the database. If not, it is the first login. The first time we log in, we jump to the form page to complete personal information and obtain the user’s mobile phone number.

The specific code of the back end is not expanded in detail here, only the overall implementation process.

Obtain the user’s mobile phone number

The necessary condition to obtain the user number is to click the button label with open-type getPhoneNumber to trigger, and bind an event with getPhoneNumber. When clicked, wechat will pop up a box authorizing access to the phonenumber, which will trigger our getPhonenumber method.

In the getPhonenumber method, there are two properties including the property encryptedData and iv (initialization vector). These two parameters are passed to the back end for phonenumber parsing.

Also very, very important, if you are a local application, the user can express some reviews and articles, so let’s save the user’s head must have been effective, if we use the default avatar provided WeChat official link directly stored in the database, so when a user in head, after the original link will fail!

So what we need to do is use the UNI. DownloadFile API to download the user’s avatar as a local file, and upload the local file to the server, and the user’s avatar will always be valid!

The specific code is as follows, the business has been removed, only the core code.

<button class="cu-btn bg-blue shadow-blur round lg" open-type="getPhoneNumber"
        @getphonenumber="getTel" @click="getCode">Sure to submit</button>
Copy the code
async uploadAvatar() {
        // Upload the user profile picture
         if (this.updateImg) {
                var uploadRes = await this.http.upload('/applet/file/uploadFile'[this.form.avatarUrl])
        }
        else if (this.userInfo) {
                var downloadRes = await uni.downloadFile({
                        url: this.userInfo.avatarUrl
                })
                if (downloadRes[0]) {
                        uni.hideLoading()
                        this.message.toast('Login failed')
                        return
                }
                var uploadRes = await this.http.upload('/applet/file/uploadFile', [downloadRes[1].tempFilePath])
        }
         else {
                return
        }
        this.form.avatarUrl = uploadRes[0]},async getTel(e) {
        // Obtain the user's mobile phone number at the first login
        if(! e.detail.encryptedData || ! e.detail.iv) {this.message.toast('Login failed')
                return
        }
        uni.showLoading({
                mask: true.title: 'Logging in'
        })
        await this.uploadAvatar()
        this.form.tags = this.tag.arr.map(item= > item.name).join(', ')
        const res = await this.http.post('/applet/authorization', {
                code: this.code,
                iv: e.detail.iv,
                encryptedData: e.detail.encryptedData,
                userInfo: this.form
        }, 'json')
        uni.hideLoading()
},
Copy the code

List page

Let’s talk about a very, very common business scenario, which is the list page. The list page looks simple, but it can be very mysterious. Such as pull-down refresh, slide-up loading, prompt for no more content, keyword search, TAB bar matching, blank pages, reuse of list item components, and so on. Having a complete design from the start makes a big difference to development efficiency and overall code robustness!

I will use my actual development code examples to demonstrate the functionality of a list. Again, I removed the business code and left the core code!

<! Page code -->
<scroll-view
  :scroll-y="true"
  style="height: 100vh"
  :refresher-enabled="true"
  :refresher-triggered="list.flag"
  @refresherrefresh="refresh"
  @scrolltolower="getAnswer"
  :enable-back-to-top="true"
  :scroll-into-view="list.scrollId"
  :scroll-with-animation="true"
>
  <view style="position: relative" class="bg-white">
    <view v-for="(item,index) in list.data" :key="index" >
      <answer-item
        :data="item"
      ></answer-item>
    </view>
  </view>
  <view
    class="cu-load bg-gray text-main loading"
    style="height: 40px"
    v-if=! "" list.nomore"
  ></view>
</scroll-view>

Copy the code
/ / js code
export default {
components: {
        AnswerItem,
},
data() {
        return {
                list: {
                        data: [].flag: false.// Whether to display refresh
                        limit: 10.total: 0.nomore: false.// Whether to display to the end
                        empty: false.// Whether the display is null,
                        error: false.// If the request is incorrect,}}; },async onLoad(e) {
        this.getQuestion()
        await this.getAnswer()
        async getAnswer(){},methods: {
    async getAnswer() {
        const listHandle = require(".. /.. /utils/js/listHandle")
        const id = this.list.data.length ? this.list.data[this.list.data.length - 1].id : ' '
        const res = await this.http.get('/applet/answerList', {
                qId: this.question.id,
                limit: this.list.limit,
                userId: this.store.user ? this.store.user.id : ' ',
                id
        })
        if (res.code === 200) {
                const list = listHandle.add(res.data, this.list.data, this.list.limit)
                Object.assign(this.list, list)
                return res.data
        } else {
                this.list.error = true
                this.message.toast('Request failed')
                return false}},async refresh() {
        // Refresh method
        const listHandle = require(".. /.. /utils/js/listHandle")
        this.list.flag = true
        this.list.nomore = false
        this.list.empty = false
        this.list.error = false
        this.list.loadNum = 0
        const res = await this.http.get('/applet/answerList', {
                qId: this.question.id,
                limit: this.list.limit,
                userId: this.store.user ? this.store.user.id : ' '.id: ' '
        })
        this.list.flag = false
        if (res.code === 200) {
                const list = listHandle.update(res.data, this.list.limit)
                Object.assign(this.list, list)
        } else {
                this.list.err = true
                this.message.toast('Request failed')}},}}Copy the code
// listhandle.js list item handling method
const listHandle = {
  add(resData, listData, limit) {
    var ret = {
      nomore: false.empty: false.data: []}if (resData) {
      if (resData.length < limit) {
        // The number of pages is less than the number of pages
        ret.nomore = true
      }
      ret.data = listData.concat(resData)
    } else {
      ret.data = listData
      ret.nomore = true
      if(! listData.length) {// No data has been returned and the current list is empty
        ret.empty = true}}return ret
  },
  update(resData, limit) {
    var ret = {
      nomore: false.empty: false.data: []}if (resData) {
      if (resData.length < limit) {
        // The number of pages is less than the number of pages
        ret.nomore = true
      }
      ret.data = resData
    } else {
      ret.nomore = true
      // The request has returned no data and is displayed empty
      ret.empty = true
    }
    return ret
  }
}

module.exports = listHandle

Copy the code

Pay attention to the point

When loading on the phone, if you are sorting in reverse chronological order, traditional bottom web pager passes a page number and a single page number to the backend query. If users post new content as you scroll up, you’ll have duplicate items in your list.

For example: you take ten data initially, and when you load up to the bottom you pass a page=2 and a limit=10 to the back end, meaning all ten data as one page, I want to take the contents of the second page. But when a user adds a new item, the last item on your first page is pushed to the second page, and the first item you get is exactly the same as the last one you got!

To solve this problem, we simply do not pass values by page number when passing data to the back end. We pass the id of the last entry of the current data to the back end and ask the back end to take ten ids smaller than this value. At this point, no matter how many users insert new data, it will not affect your results. Of course, if your table id is increasing, you can also use the creation time of the data field. Corresponding to this functionality is the following line of code.

const id = this.list.data.length ? this.list.data[this.list.data.length - 1].id : ''

Cross-page method calls

In the development of uniApp applets, we may encounter a situation where I need to call the methods of the previous page on the current page. For example, I want to implement a search function. When I click on search details and enter a keyword, I want to return to the list page and trigger a search method, as shown below

Here’s how I did it

searchFunc(){
  let pages = getCurrentPages()
  let page = pages[pages.length - 2]
  page.$vm.searchText = this.search.text
  page.$vm.refresh()
  this.router.back()
}
Copy the code

GetCurrentPages () is a global function that gets an instance of the current page stack, given as an array in stack order. The current page is the last item in the array, so the previous page is pages[pages.length-2]. Also remember to add the $VM attribute, because in UniApp our data and methods are mounted on this instance. We can use this example to access all the data and methods of the corresponding page!

Step on the pit carefully

The issues listed below are not only about UNI-App, but also about applets themselves

Scoll-view scroll-into-view cross-components

There is a scroll-into-view property on the scroll-view tag of the applet. The value of this property can be passed in as an ID, and when we change this value we will scroll to the container with the specified ID.

But if our scrollView is encapsulated in a component for use, we’re in slot

The scroll view cannot be pulled down to refresh

why

It should be that the drop-down position of the Scrollview element is determined when the element is rendered, but the height we give to the Scrollview changes the drop-down position, resulting in that the falads are not in place when the element is pulled down

The solution

Render the Scrollview element after the height variable of the scrollview is determined. The specific method is as follows

<scroll-view 
	scroll-y="true" 
	:style="'height:'+height" 
	v-if="height"  
	:refresher-enabled='true'
	:refresher-triggered='list.flag' 
	@refresherrefresh='refresh' 
	@scrolltolower='loadMore'>
</scroll-view>
Copy the code

Scroll -view sticky style problem

Problem description

Scroll view is a label that is often used by small programs. Inside the scroll window, we might have a top TAB bar. If we don’t want to fix it to the top by calculating the height, we can use position:sticky with a boundary condition such as the top:0 property to achieve a sticky layout. When the container rolls, if our top TAB hits the top it doesn’t scroll anymore, it stays at the top.

But in a small program if you use the sticky attribute for a child element directly in the Scroll view element, the element you give sticky will expire at the bottom of the parent element, which will be very impressive if you have encountered it 😭.

The solution

In the scroll view element, add another layer of view element, and then put the child element with the sticky attribute into the view, you can achieve the effect of sticking in a certain position

Ios fixed font movement bug in input box

Problem description

I put a form in a scrollable container fixed in the middle of the page. It worked fine in android, but there was a bug on IOS. If the input box is out of focus without clicking any other position after input, the font inside the scroll window will also scroll with the following solution

// The original input box<input></input>
Copy the code

The solution

Using this method, not only does the layout style not change, but the bug of the font scrolling along with the fixed scrolling window is resolved

// The changed input field<textarea fixed="true" auto-height="true" ></textarea>
Copy the code

Real time error BUG

Problem description

When using the new Date().tolocaledateString () API in applets to retrieve the time, it is displayed as the current time in the development tool and as the time of another location in the real machine

Causes of bugs

The toLocaleDateString() method depends on the underlying operating system for formatting dates. For example, in the US, the month appears before the date (06/22/2018), while in India, the date appears before the month (22/06/2018).

The solution

Use the new Date() constructor to get the Date after Date concatenation

If no arguments are entered, the Date constructor creates a Date object based on the current time set by the system.

The difference between Date and toLocaleDateString() is that one fetches the time currently set by the system and the other formats the time by the underlying operating system

// The code is as follows
let date = new Date()
date = date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate()
date = date.split('/')
if (date[1] < 10) {
		date[1] = '0' + date[1]}if (date[2] < 10) {
	date[2] = '0' + date[2]
	}
date = date.join(The '-')
Copy the code

conclusion

This article has been written on and off for two weeks. Although it is quite long, there is still a lot of content THAT I have not covered. Although I understand that output forces input, I still hope to get more positive feedback when I write my article! The likes are big drops and the updates are big drops fast.

I am oil Oyo, the new front end, welcome to pay attention to grow together!