😭 Коллекция крови и слез! Очень длинный практический обзор разработки апплета uniapp!

внешний интерфейс uni-app
😭 Коллекция крови и слез! Очень длинный практический обзор разработки апплета uniapp!

Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность.

предисловие

После опыта разработки нескольких проектов апплета uniapp я рассмотрю и обобщу весь свой реальный боевой опыт.Следующее содержание предназначено только для сценария разработки апплета WeChat uniapp.В дополнение к uniapp, есть некоторые моменты, на которые следует обратить внимание при разработке апплета. , я надеюсь, что эта статья поможет вам избежать ошибок. Полный текст следующих галантерейных изделий (сухой до жажды)

Если вы ожидаете или нуждаетесь в сообществе апплетов WeChat с открытым исходным кодом, вы можетессылка на сайт нефтяного сообществаСледите за моим прогрессом в разработке, добро пожаловать ⭐star

Знакомство с юниап

Сначала поместите официальное введение, если знакомые уже знают, что студенты uniapp могут быть пропущены.

uni-appэто использованиеVue.jsФреймворк для разработки всех интерфейсных приложений. Разработчики пишут набор кодов, которые можно публиковать в iOS, Android, Интернете (адаптивном) и различных небольших программах (WeChat/Alipay/Baidu/Toutiao/QQ/Kaishou/DingTalk/Taobao ), Быстрые приложения и другие платформы.

С uniapp мы можем использоватьvue.jsграмматика для написанияАпплетыКороче говоря, это разработка обычного vue.jstemplateшаблон изhtmlзаменитьwxmlВы можете разрабатывать небольшие программы, так какие в этом преимущества!

  1. Уменьшите нагрузку, связанную с техническим обучением. Если вы знаете Vue, вы можете сразу приступить к работе. Если вы не знаете Vue, вы можете изучить Vue между прочим, а также сможете пользоваться окружающими экологическими преимуществами Vue.
  2. Грамматике удобнее пересекаться с нативной грамматикой апплета, если у вас разработан апплет, то вы это обязательно почувствуете.this.setData()а также原生小程序组件Проблемы развития и страхи
  3. Помимо упаковки в небольшие программы на различных платформах, uniapp также может быть упакован в приложения, которые подробно не перечислены, например, на следующем рисунке на официальном сайте.

image.png4. Кодовые подсказки инструмента разработки апплета относительно растягиваются. Хотя он сделал большой прогресс по сравнению с предыдущими годами, он до сих пор не достигает уровня, который я хочу.

подготовка к разработке uniapp

установка инструмента IDE

Ссылка на официальный сайт HBuilder

Если рабочий хочет хорошо работать, он должен сначала заточить свои инструменты. Первым шагом в разработке uniapp является установка официального инструмента IDE.HbuilderX, для разработки юни-приложения рекомендую только этот инструмент ide, он действительно очень удобен и может нам помочьБыстро создавайте шаблоны страниц и шаблоны компонентов,Визуальная конфигурация страницы,Отличные подсказки по коду,Возможность завершения кодаи т.п.

HbuilderXв сравнении сvscodeЕсть еще некоторые недочеты, и у некоторых плагинов экология не очень здоровая. Но разработка уни-приложения неHbuilderXне что иное, как (ведь это официальная вещь)

HbuilderXУстановка очень удобная, достаточно скачать и установить прямо на официальном сайте. После завершения загрузки нам нужно установить некоторые плагины, открытьHbuilderX, выберите на верхней панели工具->插件安装,Как показано ниже

image.png

После вскрытия мы видимТекущие установленные плагиныа такженекоторые официальные плагины, из-за возраста не помню, какие не устанавливались в начале, но те, что ниже обведены красным, должны быть установлены, один — это плагин для управления версиями git, а другой — плагин для компиляции sass.

image.png

Мы также можем установить тему нашего редактора и размер символов, отступ кода и т. д. Если вы привыкли к другим редакторам, вы можете настроить их соответствующим образом. Я изначально разрабатывал с vscode, поэтому я переключился наЯланТемы, актуальные эффекты страницы и vscodeone dark proизстиль редактораа такжекодовый цветТочно так же!

image.png

Я думаю, что все вышеперечисленные шаги необходимы, а удобная и красивая среда разработки может значительно повысить ваш интерес к разработке!

Анализ структуры каталогов проекта

Новый проект

HbuilderXПосле завершения установки и настройки можем приступать к разработке.Для начала нам необходимо создать проект.Нажимаем в верхнем левом углу IDE.文件-> 新建 ->项目, то выбираемuniappпроект, выбор шаблонаШаблон по умолчанию

image.png

После завершения создания мы видим, что в дерево файлов слева был добавлен новый файл с именем имени проекта, который является шаблоном проекта, созданным для нас hbuilder.

image.png

Ниже приведено введение в структуру проекта, предоставленную нам uniapp.Есть несколько папок, которые не встроены в шаблон, поэтому нам нужно вручную создать следующее, например, самую внешнююcomponents, используемый для размещения некоторых наших глобальных общих компонентов

    

┌─components            符合vue组件规范的uni-app组件目录
│  └─comp-a.vue         可复用的a组件
├─pages                 业务页面文件存放的目录
│  ├─index
│  │  └─index.vue       index页面
│  └─list
│     └─list.vue        list页面
├─static                存放应用引用的本地静态资源(如图片、视频等)的目录,注意: 静态资源只能存放于此
├─uni_modules           存放[uni_module](/uni_modules)规范的插件。
├─wxcomponents          存放小程序组件的目录,详见
├─main.js               Vue初始化入口文件
├─App.vue               应用配置,用来配置App全局样式以及监听 应用生命周期
├─manifest.json         配置应用名称、appid、logo、版本等打包信息,详见
└─pages.json            配置页面路由、导航条、选项卡等页面类信息,详见
    

Как показано на рисунке ниже, в процессе разработки, в соответствии с привычками разработки проекта vue, я создал страницы в соответствии с бизнес-функциями.功能分类文件夹, чаще всего по домашней страницеtabbarЧтобы различать функциональные модули, в каждой папке хранятся все страницы, задействованные в функции, и каждая функциональная папка имеет отдельныйcomponents文件夹Компоненты, используемые для размещения зависимостей страниц в этой папке только для функций.

image.png

Новая страница

Когда нам нужно создать новую страницу, мы можем передатьhbuilderВстроенные шаблоны страниц для быстрого создания, щелкните правой кнопкой мыши текущий проект в дереве файлов слева, выберите新建页面, введите имя страницы и выберите шаблон для ее создания.Вообще я выбираю шаблон scss.После завершения создания он автоматически поможет вам вpage.jsonЗарегистрируйтесь на этой странице.

image.png

Общий пакет плагинов

Поскольку uniapp использует vue.js в качестве среды разработки, мы должны воспользоваться некоторыми замечательными функциями vue, такими как插件(plugin), вы можете перейти прямо на официальный сайт, чтобы ознакомиться с введением плагина vue.

Официальная ссылка для знакомства с плагином Vue

Внедряя плагины, мы можем значительно повысить эффективность нашей разработки.Конечно, если мы впервые используем uniapp для разработки, может быть неясно, какие функции подходят для упаковки в плагины.Здесь я представлю некоторые из тех, что я упаковал в реальной разработке.Универсальный плагин

Перед инкапсуляцией нам нужно написатьconfigфайл, чтобы мы могли быстро настроить некоторые цвета и пути запросов и т. д.

//congif.js
const config = {
	baseUrl:'https://example.cn',//请求的基本路径
	modalColor:'#5271FF', //弹窗颜色 
}

module.exports = config

всплывающий плагин

В апплете, если у нас нет настраиваемых компонентов всплывающего окна и миметического окна, мы обычно используем официальныйshowModalилиshowToastapi для взаимодействия с пользователем. Эта очень часто используемая операция очень удобна для инкапсуляции и быстрого вызова. Конкретный код выглядит следующим образом

код плагина

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

var message = {
	toast(title, type = 'text') {
		if (title.length > 15) {
			console.error('toast长度超过15个字符,当前长度为' + 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: '提示',
				content,
				showCancel: false,
				confirmColor: config.modalColor,
				success: (result) => {
					res(result)
				}
			})
		})
	}
}
module.exports = message

Пример вызова

this.message.toast('回答已删除')

запросить плагин

Наша часто используемая библиотека запросов в vue:axios, почти каждый разработчик будетaxiosВыполните вторичную инкапсуляцию, чтобы уменьшить объем кода при вызове, и выполните некоторую глобальную настройку. В юни-приложении нам не нужно вводить стороннюю библиотеку запросов, мы напрямую используем официально предоставленнуюuni.request(OBJECT)Этот API вызывает запрос, конечно, нам также нужно сделать вторичную инкапсуляцию. Конкретный код выглядит следующим образом

код плагина

//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('请求失败')
			}
			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请求' + config.baseUrl + path + ' 成功', res.data)
					resolve(res.data)
				},
				fail: (err) => {
					message.toast('请求失败', 'err')
					console.error('request:请求' + config.baseUrl + path + ' 失败', err)
					reject('请求失败')
				}
			})
		})
	},
	put(path, params, contentType = 'form', otherUrl, ) {
		return new Promise((resolve, reject) => {
			var url = (otherUrl || config.baseUrl) + path
			if (!checkUrl(url)) {
				rej('请求失败')
			}
			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请求' + config.baseUrl + path + ' 成功', res.data)
					resolve(res.data)
				},
				fail: (err) => {
					message.toast('请求失败', 'err')
					console.error('request:PUT请求' + config.baseUrl + path + ' 失败', err)
					reject('请求失败')
				}
			})
		})
	},

	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请求' + config.baseUrl + path + ' 成功', res.data)
					resolve(res.data)
				},
				fail: (err) => {
					message.toast('请求失败', 'err')
					console.error('request:GET请求' + config.baseUrl + path + ' 失败', 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:DELETE请求' + config.baseUrl + path + ' 成功', res.data)
					resolve(res.data)
				},
				fail: (err) => {
					message.toast('请求失败', 'err')
					console.error('request:DELETE请求' + config.baseUrl + path + ' 失败', err)
					reject(err)
				}
			})

		})

	},

	async upload(path, fileArray, otherUrl) {

		if (typeof fileArray !== 'object') {
			console.error('request:参数错误,请传入文件数组')
			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:上传失败', 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:请求路径错误' + url)
		return false
	}
	return true
}
module.exports = http

Пример вызова

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('请求失败')
    return false
  }
}

Как мы видим в приведенном выше коде, мы вводим два файла зависимостей, которыеconfigа такжеmessageОб этом будет сказано ниже, вhttpВ объекте есть пять методов, которыеpost,get,update,deleteсоответствующийrestful apiоперации добавления, удаления, поиска и модификации, а такжеuploadМетод используется для загрузки файлов. Нам не нужно рассматривать запрос в апплетеперекрестный доменПроблема, наш путь запроса по умолчанию отconfigполученный из этого файла зависимостей. В это время мы также можем использовать первый弹窗插件Быстрые подсказки сообщения.

плагин для хранения

При использовании vue мы часто используемvuex + vuex-persistedstateЧтобы сделать наши данные доступными и постоянными глобально, у нас есть два метода хранения глобальных данных в апплете.globalDataДобавляя в App.VueglobalDataЭто значение используется для глобализации данных, например, мы можем использовать его для сохранения версии нашего апплета.

export default {
  globalData: {
    version: '1.0.0'
  }
}

ноglobalDataДанные не являются постоянными, они повторно инициализируются, когда мы выходим из апплета и снова заходим, поэтому у нас также есть глобальный метод хранения данных:storage, аналогично localstrge на нашей веб-странице, использование почти такое же. Реализуйте эффект локального хранилища через официально предоставленный API

image.png

В апплете часто менее сложные структуры данных, чем на веб-стороне, хотя апплет uniapp также можно использовать.vuexВ качестве библиотеки управления глобальными данными я все же сделал упрощенную версию для собственного использования. Этот плагин был написан давно, и это просто очень простая реализация.Если вы хотите опустить операцию setData, вы можете использовать прокси для захвата сохраненного значения и сохранения его локально.

код плагина

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:非法key值',key)
			return
		}
		this[key] = value
		uni.setStorageSync('store',this)
		console.log(uni.getStorageSync('store'))
	}
}

module.exports = store

Пример использования

this.store.setData('user',{name:'oil'}) // 赋值
console.log(this.store.user) // 读值

плагин проверки формы

Для проверки формы используется очень легкая библиотека проверки формы с открытым исходным кодом JSValidate.Вы можете напрямую перейти по ссылке, чтобы просмотреть исходный код на складе.Я не буду приводить код ниже, а расскажу о том, как его использовать.JSValidate ссылка на gitee

Пример вызова

// validate.js
const validate = {
	userForm:{
		intro:	'@个人简介|require',
		college: '@学校|require!请选择你的学校',
		nickname:'@昵称|require'
	}
}

module.exports = validate

Напишите метод проверки формы в форме и используйте всплывающее окно для запроса, если условия не выполняются.

//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
}

плагин обработки времени

Что касается плагина обработки времени, то, что я использую в проекте, также является облегченной библиотекой js с открытым исходным кодом.day.js, пожалуйста, обратитесь к официальной документации для конкретного использования.day.js содержит почти все операции, связанные с обработкой времени, такие как сравнение времени, преобразование времени и т. д.ссылка на официальную документацию day.js

day.jsОдной из наиболее часто используемых функций является функция преобразования относительного времени.Преобразование относительного времени заключается в преобразовании строки времени, такой как год, месяц, день, час, минута и секунда, в несколько секунд назад, десять минут назад, несколько месяцев назад. , и так далее. Способ использования кода следующий: мы используем vuecomputedЧтобы обработать строку времени, передайтеЗакрытиеКcomputedПараметр строки времени передается в метод, а затем передается через day.jsformNowМетод возвращает относительное время на китайском языке. date — это плагин, который я представил после инкапсуляции.

образец кода

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

Когда у нас много подключаемых модулей (как показано на рисунке ниже), наши подключаемые модули могут иметь общие конфигурации, такие как приведенные выше.config.js, то мы можем написать входной файлindex.jsИспользуется для представления плагина, который мы написали, а затем вmain.jsУстановите наш плагин в формате .

image.png

образец кода

// 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
// main.js
import Vue from 'vue'
import oilUni from './oil-uni/index.js'
Vue.use(oilUni)
app.$mount()

Мы определяем объект в стартовом файле с именемoil,oilВ дополнение к методу, который мы написали, есть специальный метод, которыйinstall,installЭффект заключается в том, что когда мы используемVue.use()При передаче в конструкторе Vueinstallmethod в качестве первого параметра, а затем мы связываем все методы в цепочке прототипов Vue.

После установки нам остается только использовать в js страницы или компонентаthis. + xx插件Вы можете получить доступ к упакованному плагину.

Упаковка общих компонентов

Роль плагина состоит в том, чтобы сделать наш кодмодульный, чтобы легко повторно использовать некоторые часто используемые операции. а такжесоставнойЭто помогает нам в пользовательском интерфейсеинкапсуляция и повторное использование. Так как сценарии небольших программ, как правило, не очень сложны, очень общих компонентов не так много.Далее рассказывается только о некоторых библиотеках компонентов, которые я обычно использую в бизнесе, и о компонентах, которые я инкапсулирую сам.

Внедрение сторонних библиотек компонентов

colorUI

Первая рекомендация заключается в том, что я использовал его почти во всех проектах апплетов.colorUI, не так многоcolorUIЭто библиотека компонентов, а не библиотека стилей.ссылка на гитхаб colorUI

В colorUI инкапсулировано большое количество встроенных стилей, нам не нужно частоtemplateа такжеstyleПереключение между ними значительно снижает нагрузку на наш разум. можно сравнитьtailwindcssэта библиотека стилей, но вHBuilderX, есть очень здравые подсказки кода в стиле colorUI. На самом деле это не только colorUI. Стиль, который вы написали сами или указанный файл находится вHBuilderX中都会获得很完整的一个代码提示。 Как показано ниже

image.png

ColorUI не предоставляет нам функционально инкапсулированные компоненты, но в своем примере исходного кода большинство сценариев могут напрямую копировать стили и коды и использовать их напрямую, напримерзагрузить изображениеа такжекомпонент формыЖдать.Пример страницы colorUI

Внедрить colorUI тоже очень просто, поместите пакет colorUI в корневую директорию проекта, а затем вApp.vueизstyleСсылка в порядке, colorUI содержит три файла, одинmain.css, который является кодом стиля colorUI,iconэто иконка colorUI,animationЭто анимация colorUI, и функции трех пакетов передаются ввстроенный классНапишите указанный код, чтобы использовать его.

image.png

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

vant-weapp

Vant - это почти лучшая библиотека компонентов для экосистемы Vue на мобильной стороне. Он охватывает практически все компоненты сцены, а Vant имеет небольшую программу, посвященную ему.weappВерсия.официальная документация vant weapp

ноvant weappОн написан на синтаксисе нативного апплета, который несколько отличается от синтаксиса vue в uniapp, поэтому при импорте необходимо проделать некоторые специальные операции, об этом поговорим нижеvant weappКак импортировать.

  1. Сначала вам нужно скачать vant weapp, папку vant в корневом каталоге проектаwxcomponentЕсли у вас его нет в папке, создайте новый.

image.png

  1. существуетApp.vueВставьте файл стиля в
<style>
	@import "/wxcomponents/vant/common/index.wxss";
</style>
  1. Затем мы можем импортировать его на страницу.Импорт не может быть импортирован напрямую на страницу или компонент, но должен быть импортирован в соответствии с тем, как используется апплет.pages.jsonПредставлен в следующем коде на указанной страницеusingComponentsдобавить в конфигурацию组件名:组件路径, а затем вы можете использовать его прямо на странице
{
  "pages": [ 
    {
      "path": "pages/home/home",
      "style": {
        "navigationBarTitleText": "",
        "enablePullDownRefresh": false,
        "usingComponents": {
          "van-progress": "/wxcomponents/vant/progress/index"
        }
      }
    }
  ]
}

обращать вниманиеvant weappСинтаксис в официальной документации предназначен для небольших программ, и его можно преобразовать в vue самостоятельно.

компонент контейнера прокрутки

После введения вышеупомянутых двух библиотек компонентов большинство наших сценариев разработки могут найти в них соответствующие компоненты, в лучшем случае вторичный пакет для адаптации к бизнесу.

Общие бизнес-сценарии

страница авторизации

В апплете вход в систему часто не похож на веб-терминал, который требует ввода пароля учетной записи или кода подтверждения мобильного телефона для входа, а использует функцию быстрой аутентификации каждой платформы.Например, апплет WeChat может получать информацию о пользователе. отправив запрос на официальный API WeChat. .

Получить информацию о пользователе

Поэтому, когда мы разрабатываем страницу, нам часто нужна очень простая кнопка дляПолучить информацию о пользователе, я буду использовать функцию входа в проект, чтобы показать вам, как uniapp реализует апплет WeChat.Войдите, чтобы получить информацию о пользователеМетоды

<template>
	<button class="cu-btn bg-blue shadow-blur round lg" @click="login">
            立即登录
	</button>
</template>
<script>
export default {
    methods: {
	async login() {
            uni.showLoading({
		mask: true,
		title: '登录中'
		})
            const res = await uni.getUserProfile({
		desc: '用于存储用户数据'
		})
	uni.hideLoading()
	if (res[0]) {
		this.message.toast('获取失败', 'text')
		return
	}
        this.getUser(res[1].userInfo)
        },
        async getUser(userInfo) {
            uni.showLoading({
                    mask: true,
                    title: '登录中'
            })
            const loginRes = await uni.login()
            if (loginRes[0]) {
                    uni.hideLoading()
                    this.message.toast('获取失败')
                    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)
    },}
        

Давайте проанализируем приведенный выше код, сначала мы используемuni.getUserProfileapi для получения основной информации о пользователе, такой как аватары, псевдонимы и другие области. Далее мы звонимuni.loginПолучатьcodeстоимость. Значение кода используется для передачи на серверную часть, чтобы получить WeChat для пользователя.уникальный идентификаториз.

Я передаю код на бэкенд, бэкенд разбирает openid и проверяет, есть ли у текущего пользователя информация о пользователе в базе данных, если нет, то естьВойти в первый раз. При первом входе в систему мы переходим на страницу формыПолная личная информацияа такжеПолучить номер мобильного телефона пользователя.

Конкретный код бэкенда здесь подробно описываться не будет, только общий процесс реализации.

Получить номер мобильного телефона пользователя

Необходимым условием для получения номера пользователя является нажатиеopen-typeдляgetPhoneNumberизbuttonтриггер тега, и нужно использоватьgetphonenumberПривязать событие. При нажатии этой кнопки WeChat откроет окно для авторизации получения номеров мобильных телефонов.Если пользователь решит согласиться, это активирует нашуgetphonenumberметод.

getphonenumberВ параметре e.detail метода есть два свойства.зашифрованные данные (зашифрованные данные)а такжеiv (вектор инициализации)Эти два параметра используются для передачи на серверную часть для парсинга номера мобильного телефона Конкретный процесс серверной части более сложен, я расскажу об этом в следующей статье.

Еще один момент очень, очень важен: если вы являетесь приложением, основанным на сообществе, пользователи могут публиковать некоторые комментарии и статьи, тогда сохраняемый аватар пользователя всегда должен быть действительным, если мы используем ссылку на аватар по умолчанию, предоставленную официальным магазином WeChat непосредственно в базу данных, то когдаСмена аватара пользователяПозже,Исходная ссылка истечет!

Итак, что нам нужно сделать, это использоватьuni.downloadFileAPI загружает аватар пользователя как локальный файл, а затем загружает локальный файл на сервер, и аватар пользователя всегда будет действительным!

Конкретный код выглядит следующим образом: бизнес извлечен, остался только основной код.

<button class="cu-btn bg-blue shadow-blur round lg" open-type="getPhoneNumber"
        @getphonenumber="getTel" @click="getCode">确定提交
</button>
async uploadAvatar() {
        // 上传用户头像
         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('登录失败')
                        return
                }
                var uploadRes = await this.http.upload('/applet/file/uploadFile', [downloadRes[1].tempFilePath])
        }
         else {
                return
        }
        this.form.avatarUrl = uploadRes[0]
},
async getTel(e) {
        // 首次登录,获取用户手机号码
        if (!e.detail.encryptedData || !e.detail.iv) {
                this.message.toast('登录失败')
                return
        }
        uni.showLoading({
                mask: true,
                title: '登录中'
        })
        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()
},

страница со списком

Давайте поговорим об очень распространенном бизнес-сценарии, который у нас есть, то естьстраница со списком, страница списка кажется простой, но на самом деле это скрытая тайна. Например下拉刷新,上滑加载,没有更多内容的提示,关键字搜索,标签栏匹配,空白页面,列表项组件повторное использование и т.д. Завершение проектирования с самого начала может иметь большое значение для повышения эффективности разработки и общей надежности кода!

Я буду использовать свои примеры кода демонстрировать фактическое развитие списка функций для достижения всех. Тем не менее, я вытащил из бизнес-кода только основной код!

<!-- 页面代码 -->
<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>

// js代码
export default {
components: {
        AnswerItem,
},
data() {
        return {
                list: {
                        data: [],
                        flag: false, //是否展示刷新
                        limit: 10,
                        total: 0,
                        nomore: false, //是否显示到底
                        empty: false, //是否显示为空,
                        error: false, //是否请求错误,
                }

        };
},
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('请求失败')
                return false
        }
},
async refresh() {
        // 刷新方法
        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('请求失败')
        }
},
}
}
// listHandle.js 列表项处理的方法
const listHandle = {
  add(resData, listData, limit) {
    var ret = {
      nomore: false,
      empty: false,
      data: []
    }
    if (resData) {
      if (resData.length < limit) {
        // 获取数据条数小于页码数,显示已到底
        ret.nomore = true
      }
      ret.data = listData.concat(resData)
    } else {
      ret.data = listData
      ret.nomore = true
      if (!listData.length) {
        // 请求已无返回数据且当前列表无数据,显示为空
        ret.empty = true
      }
    }
    return ret
  },
  update(resData, limit) {
    var ret = {
      nomore: false,
      empty: false,
      data: []
    }
    if (resData) {
      if (resData.length < limit) {
        // 获取数据条数小于页码数,显示已到底
        ret.nomore = true
      }
      ret.data = resData
    } else {
      ret.nomore = true
      // 请求已无返回数据且,显示为空
      ret.empty = true
    }
    return ret
  }
}

module.exports = listHandle

будь осторожен

При скользящей загрузке на мобильный телефон, если следоватьУпорядочить в обратном хронологическом порядке, как традиционный нижний веб-пейджерНомер страницы и отдельный номер страницыДля внутренних запросов. Если пользователь публикует новый контент, пока вы проводите пальцем вверх, ваш список будетдубликат элемента.

Например: вы изначально получили десять фрагментов данных, и когда вы проводите пальцем вверх, чтобы загрузить один,page=2а такжеlimit=10Для бэкэнда это означает использовать все десять частей данных как одну страницу, а я хочу получить содержимое второй страницы. Но в это время пользователь добавляет новый фрагмент данных, выПоследняя строка первой страницы сжата до второй страницыИдите, первая часть данных, которую вы получите в это время, будет точно такой же, как и последняя часть данных, которую вы получили в прошлый раз!

Также очень просто решить эту проблему. Когда мы передаем данные на бэкэнду, мы не передаем значение по количеству страниц. Мы передаем удостоверение личности последнего элемента текущих данных на бэкэнду и дайте бэкэндеДесять элементов с идентификатором меньше этого значения, сколько бы пользователей в это время не вводили новые данные, это не повлияет на ваши результаты. Конечно, предпосылка этой функции заключается в том, что ваш идентификатор таблицы данных увеличивается.Если нет, вы также можете использовать поле времени создания данных для его передачи. Этой функции соответствует следующая строка кода.

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

Вызовы межстраничных методов

При разработке апплета uniapp мы можем столкнуться со сценарием, когда мне нужно вызвать метод предыдущей страницы на текущей странице. Например: я хочу реализовать функцию поиска, когда я нажимаю на детали поиска и ввожу ключевое слово, я хочу вернуться на страницу списка и вызвать метод поиска, как показано ниже.

1.gif

Моя реализация выглядит следующим образом

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

getCurrentPages()— это глобальная функция для получения экземпляра текущего стека страниц, заданного в порядке стека в виде массива. Наша текущая страница является последним элементом массива, тогда предыдущая страницаpages[pages.length - 2]. Конечно, мы должны помнить, чтобы добавить$vmproperties, потому что в uniapp наши данные и методы монтируются на этот инстанс. Через этот пример мы можем получить доступ ко всем данным и методам соответствующей страницы!

Обратите внимание на то, чтобы наступить на яму

Перечисленные ниже проблемы связаны не только с юни-приложением, но и с некоторыми проблемами самого апплета.

cross-component scroll-in-view

в апплетеscroll-viewесть один на этикеткеscroll-into-viewАтрибут, значение этого атрибута может быть передано в идентификаторе, когда мы изменим это значение, мы прокрутим до позиции контейнера указанного идентификатора.

Но если наш вид прокрутки инкапсулирован в компонент, мы находимся в слоте

прокрутка не может потянуть вниз, чтобы обновить

причина

Должно быть так, чтобы выпадающая позиция элемента scroll-view определялась при рендеринге элемента, а высота, которую мы присваиваем scroll-view, меняет выпадающую позицию, что делает невозможным вытягивание на месте при вытягивании вниз.

Решение

Элемент scroll-view визуализируется после определения переменной высоты прокрутки. Конкретный метод выглядит следующим образом:

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

проблема с липким стилем прокрутки

описание проблемы

scroll-viewЭто метка, которая часто используется в небольших программах. В прокручиваемом окне у нас может быть верхняя полоса меток. Если мы не хотим фиксировать ее вверху путем вычисления высоты, мы можем использоватьposition:sticky Добавьте граничное условие, напримерtop:0 Свойство реализует липкий макет.При прокрутке контейнера, если наша верхняя панель вкладок касается верхней части, она не будет прокручиваться снова, а будет зафиксирована вверху.

Но в апплете, если выscroll-viewЭлементы используются непосредственно для дочерних элементовstickyатрибут, элемент, который вы прикрепите, станет недействительным, когда он достигнет нижней части родительского элемента, если вы столкнетесь с ним, это будет очень впечатляюще😭.

Решение

существуетscroll-viewэлемент, добавьте еще один слойviewэлемент, а затем используйтеstickyДочерние элементы атрибута помещаются вview, можно добиться эффекта вставки в определенном положении

iOS исправлена ​​​​ошибка перемещения шрифта в поле ввода

описание проблемы

Форма размещена в контейнере прокрутки, закрепленном посередине страницы, на стороне Android функция тестирования идеальна, а на стороне IOS есть ошибка. Когда поле ввода не нажимается на другие позиции после ввода, чтобы сделать поле ввода не в фокусе, если шрифт внутри окна прокрутки будет прокручиваться вместе со следующим решением

//原本的输入框
<input></input>

Решение

После использования этого метода для изменения не только стиль верстки не изменится, но и будет решена ошибка, что шрифт прокручивается с фиксированным окном прокрутки

// 更改后的输入框
<textarea fixed="true" auto-height="true" ></textarea>

ошибка реального времени

описание проблемы

использовать в апплетеnew Date().toLocaleDateString()Когда API получает время, оно отображается как текущее время в средстве разработки и как время в других регионах на реальной машине.

Причина ОШИБКИ

Метод toLocaleDateString() зависит от базовой операционной системы при форматировании даты. Например, в США месяц указывается перед датой (22.06.2018), а в Индии — перед месяцем (22.06.2018).

решение

использоватьnew Date()Конструктор для получения года, месяца и дня после склейки

Если вы не введете никаких параметров, конструктор Date создаст объект Date на основе текущих системных настроек времени.

Dateа такжеtoLocaleDateString()Разница в том, что один из них предназначен для получения времени, установленного в настоящее время системой, а другой — для форматирования времени базовой операционной системой.

//具体代码如下
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('-')

Автоматически импортировать компоненты по всему миру

Функции

Если вы использовали проекты разработки vue2, вы знаете, что в vue2 вы можете автоматически вводить глобальные компоненты через API веб-пакета, и вам не нужно вручную вводить их один за другим на странице, следующий код

const requireComponent = require.context("@/components", true, /\.vue$/);
// 通过webpack获取conponents目录下所有组件
const global = {
  install(app) {
    const components = requireComponent.keys(); // 获得组件数组
    for (let component of components) {
      let componentName = component.replace(/(.*\/)*([^.]+).*/gi, "$2"); // 获得组件名称
      app.component(componentName, requireComponent(component).default); // 将组件挂载到全局
    }
  },
};
export default global;

Поместите global.js в родственный каталог компонентов, затем вmain.jsвнедрены и используются вVue.useМожно использовать плагин, этот метод может помочь нам автоматически смонтировать компонент в файл global.

будь осторожен

Вышеупомянутый метод нельзя использовать в uniapp, мы можем видеть эту строку в приведенном выше коде.app.component(componentName, requireComponent(component).default);

в юниапapp.componentЭтот метод не может передавать переменные в качестве имен компонентов, напрямую можно передавать только строки, поэтому мы не можем использовать его для автоматического добавления компонентов.

Суммировать

Я пишу эту статью еще через две недели, хотя очень долго, но есть много вещей, которые я не упомянул. Во время пониманиявыходная сила входнаяПравда, но я все же надеюсь получить больше положительных отзывов при написании статей!Лайков много, а обновления очень быстрые.

яФронтендовое масло новичка Ou Yo, добро пожаловать, чтобы обратить внимание, чтобы расти вместе!