Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность.
предисловие
После опыта разработки нескольких проектов апплета 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Вы можете разрабатывать небольшие программы, так какие в этом преимущества!
- Уменьшите нагрузку, связанную с техническим обучением. Если вы знаете Vue, вы можете сразу приступить к работе. Если вы не знаете Vue, вы можете изучить Vue между прочим, а также сможете пользоваться окружающими экологическими преимуществами Vue.
- Грамматике удобнее пересекаться с нативной грамматикой апплета, если у вас разработан апплет, то вы это обязательно почувствуете.
this.setData()
а также原生小程序组件
Проблемы развития и страхи - Помимо упаковки в небольшие программы на различных платформах, uniapp также может быть упакован в приложения, которые подробно не перечислены, например, на следующем рисунке на официальном сайте.
4. Кодовые подсказки инструмента разработки апплета относительно растягиваются. Хотя он сделал большой прогресс по сравнению с предыдущими годами, он до сих пор не достигает уровня, который я хочу.
подготовка к разработке uniapp
установка инструмента IDE
Ссылка на официальный сайт HBuilder
Если рабочий хочет хорошо работать, он должен сначала заточить свои инструменты. Первым шагом в разработке uniapp является установка официального инструмента IDE.HbuilderX, для разработки юни-приложения рекомендую только этот инструмент ide, он действительно очень удобен и может нам помочьБыстро создавайте шаблоны страниц и шаблоны компонентов,Визуальная конфигурация страницы,Отличные подсказки по коду,Возможность завершения кодаи т.п.
HbuilderXв сравнении сvscodeЕсть еще некоторые недочеты, и у некоторых плагинов экология не очень здоровая. Но разработка уни-приложения неHbuilderXне что иное, как (ведь это официальная вещь)
HbuilderXУстановка очень удобная, достаточно скачать и установить прямо на официальном сайте. После завершения загрузки нам нужно установить некоторые плагины, открытьHbuilderX, выберите на верхней панели工具
->插件安装
,Как показано ниже
После вскрытия мы видимТекущие установленные плагиныа такженекоторые официальные плагины, из-за возраста не помню, какие не устанавливались в начале, но те, что ниже обведены красным, должны быть установлены, один — это плагин для управления версиями git, а другой — плагин для компиляции sass.
Мы также можем установить тему нашего редактора и размер символов, отступ кода и т. д. Если вы привыкли к другим редакторам, вы можете настроить их соответствующим образом. Я изначально разрабатывал с vscode, поэтому я переключился наЯланТемы, актуальные эффекты страницы и vscodeone dark proизстиль редактораа такжекодовый цветТочно так же!
Я думаю, что все вышеперечисленные шаги необходимы, а удобная и красивая среда разработки может значительно повысить ваш интерес к разработке!
Анализ структуры каталогов проекта
Новый проект
HbuilderXПосле завершения установки и настройки можем приступать к разработке.Для начала нам необходимо создать проект.Нажимаем в верхнем левом углу IDE.文件
-> 新建
->项目
, то выбираемuniappпроект, выбор шаблонаШаблон по умолчанию
После завершения создания мы видим, что в дерево файлов слева был добавлен новый файл с именем имени проекта, который является шаблоном проекта, созданным для нас hbuilder.
Ниже приведено введение в структуру проекта, предоставленную нам 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文件夹
Компоненты, используемые для размещения зависимостей страниц в этой папке только для функций.
Новая страница
Когда нам нужно создать новую страницу, мы можем передатьhbuilderВстроенные шаблоны страниц для быстрого создания, щелкните правой кнопкой мыши текущий проект в дереве файлов слева, выберите新建页面
, введите имя страницы и выберите шаблон для ее создания.Вообще я выбираю шаблон scss.После завершения создания он автоматически поможет вам вpage.json
Зарегистрируйтесь на этой странице.
Общий пакет плагинов
Поскольку uniapp использует vue.js в качестве среды разработки, мы должны воспользоваться некоторыми замечательными функциями vue, такими как插件(plugin)
, вы можете перейти прямо на официальный сайт, чтобы ознакомиться с введением плагина vue.
Официальная ссылка для знакомства с плагином Vue
Внедряя плагины, мы можем значительно повысить эффективность нашей разработки.Конечно, если мы впервые используем uniapp для разработки, может быть неясно, какие функции подходят для упаковки в плагины.Здесь я представлю некоторые из тех, что я упаковал в реальной разработке.Универсальный плагин
Перед инкапсуляцией нам нужно написатьconfig
файл, чтобы мы могли быстро настроить некоторые цвета и пути запросов и т. д.
//congif.js
const config = {
baseUrl:'https://example.cn',//请求的基本路径
modalColor:'#5271FF', //弹窗颜色
}
module.exports = config
всплывающий плагин
В апплете, если у нас нет настраиваемых компонентов всплывающего окна и миметического окна, мы обычно используем официальныйshowModal
илиshowToast
api для взаимодействия с пользователем. Эта очень часто используемая операция очень удобна для инкапсуляции и быстрого вызова. Конкретный код выглядит следующим образом
код плагина
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
В апплете часто менее сложные структуры данных, чем на веб-стороне, хотя апплет 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
Установите наш плагин в формате .
образец кода
// 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()
При передаче в конструкторе Vueinstall
method в качестве первого параметра, а затем мы связываем все методы в цепочке прототипов Vue.
После установки нам остается только использовать в js страницы или компонентаthis. + xx插件
Вы можете получить доступ к упакованному плагину.
Упаковка общих компонентов
Роль плагина состоит в том, чтобы сделать наш кодмодульный, чтобы легко повторно использовать некоторые часто используемые операции. а такжесоставнойЭто помогает нам в пользовательском интерфейсеинкапсуляция и повторное использование. Так как сценарии небольших программ, как правило, не очень сложны, очень общих компонентов не так много.Далее рассказывается только о некоторых библиотеках компонентов, которые я обычно использую в бизнесе, и о компонентах, которые я инкапсулирую сам.
Внедрение сторонних библиотек компонентов
colorUI
Первая рекомендация заключается в том, что я использовал его почти во всех проектах апплетов.colorUI, не так многоcolorUIЭто библиотека компонентов, а не библиотека стилей.ссылка на гитхаб colorUI
В colorUI инкапсулировано большое количество встроенных стилей, нам не нужно частоtemplate
а такжеstyle
Переключение между ними значительно снижает нагрузку на наш разум. можно сравнитьtailwindcssэта библиотека стилей, но вHBuilderX, есть очень здравые подсказки кода в стиле colorUI. На самом деле это не только colorUI. Стиль, который вы написали сами или указанный файл находится вHBuilderX中都会获得很完整的一个代码提示。 Как показано ниже
ColorUI не предоставляет нам функционально инкапсулированные компоненты, но в своем примере исходного кода большинство сценариев могут напрямую копировать стили и коды и использовать их напрямую, напримерзагрузить изображениеа такжекомпонент формыЖдать.Пример страницы colorUI
Внедрить colorUI тоже очень просто, поместите пакет colorUI в корневую директорию проекта, а затем вApp.vue
изstyle
Ссылка в порядке, colorUI содержит три файла, одинmain.css
, который является кодом стиля colorUI,icon
это иконка colorUI,animation
Это анимация colorUI, и функции трех пакетов передаются ввстроенный классНапишите указанный код, чтобы использовать его.
//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Как импортировать.
- Сначала вам нужно скачать vant weapp, папку vant в корневом каталоге проекта
wxcomponent
Если у вас его нет в папке, создайте новый.
- существует
App.vue
Вставьте файл стиля в
<style>
@import "/wxcomponents/vant/common/index.wxss";
</style>
- Затем мы можем импортировать его на страницу.Импорт не может быть импортирован напрямую на страницу или компонент, но должен быть импортирован в соответствии с тем, как используется апплет.
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.getUserProfile
api для получения основной информации о пользователе, такой как аватары, псевдонимы и другие области. Далее мы звонимuni.login
Получатьcodeстоимость. Значение кода используется для передачи на серверную часть, чтобы получить WeChat для пользователя.уникальный идентификаториз.
Я передаю код на бэкенд, бэкенд разбирает openid и проверяет, есть ли у текущего пользователя информация о пользователе в базе данных, если нет, то естьВойти в первый раз. При первом входе в систему мы переходим на страницу формыПолная личная информацияа такжеПолучить номер мобильного телефона пользователя.
Конкретный код бэкенда здесь подробно описываться не будет, только общий процесс реализации.
Получить номер мобильного телефона пользователя
Необходимым условием для получения номера пользователя является нажатиеopen-type
дляgetPhoneNumberизbuttonтриггер тега, и нужно использоватьgetphonenumberПривязать событие. При нажатии этой кнопки WeChat откроет окно для авторизации получения номеров мобильных телефонов.Если пользователь решит согласиться, это активирует нашуgetphonenumberметод.
getphonenumberВ параметре e.detail метода есть два свойства.зашифрованные данные (зашифрованные данные)а такжеiv (вектор инициализации)Эти два параметра используются для передачи на серверную часть для парсинга номера мобильного телефона Конкретный процесс серверной части более сложен, я расскажу об этом в следующей статье.
Еще один момент очень, очень важен: если вы являетесь приложением, основанным на сообществе, пользователи могут публиковать некоторые комментарии и статьи, тогда сохраняемый аватар пользователя всегда должен быть действительным, если мы используем ссылку на аватар по умолчанию, предоставленную официальным магазином WeChat непосредственно в базу данных, то когдаСмена аватара пользователяПозже,Исходная ссылка истечет!
Итак, что нам нужно сделать, это использоватьuni.downloadFile
API загружает аватар пользователя как локальный файл, а затем загружает локальный файл на сервер, и аватар пользователя всегда будет действительным!
Конкретный код выглядит следующим образом: бизнес извлечен, остался только основной код.
<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 мы можем столкнуться со сценарием, когда мне нужно вызвать метод предыдущей страницы на текущей странице. Например: я хочу реализовать функцию поиска, когда я нажимаю на детали поиска и ввожу ключевое слово, я хочу вернуться на страницу списка и вызвать метод поиска, как показано ниже.
Моя реализация выглядит следующим образом
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]
. Конечно, мы должны помнить, чтобы добавить$vm
properties, потому что в 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, добро пожаловать, чтобы обратить внимание, чтобы расти вместе!