当前位置:   article > 正文

uni-app开发小程序实用技巧_uniapp onmounted

uniapp onmounted

uni-app开发小程序实用技巧

uni-app 是一个使用 Vue.js (opens new window)开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。

vue-cli创建uni项目

全局安装vue-cli

npm install -g @vue/cli@4
  • 1

使用Vue3/Vite版

npx degit dcloudio/uni-preset-vue#vite my-vue3-project
  • 1

配置eslint

.eslintrc.js

module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: [
    'plugin:vue/vue3-essential',
    '@vue/standard'
  ],
  parserOptions: {
    parser: 'babel-eslint'
  },
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    //关闭组件命名规则
    "vue/multi-word-component-names": "off",
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

配置request

https://www.quanzhan.co/luch-request

安装luch-request

npm i -S luch-request
  • 1

根目录创建vue.config.js

module.exports = {
  transpileDependencies: ['luch-request']
}
  • 1
  • 2
  • 3

使用

import Request from 'luch-request'

// 将实例化当axios用就行
export const axios = new Request()
  • 1
  • 2
  • 3
  • 4

使用iconfont

从iconfont下载压缩包到本地,解压后保留iconfont.css,修改iconfont.css中地址,然后地址前加上https://at.alicdn.com

@font-face {
  font-family: 'iconfont';
  /* Project id 2667731 */
  src: url('https://at.alicdn.com/t/font_2667731_zq18s4xyfkej.woff2?t=1649751472791') format('woff2'),
    url('https://at.alicdn.com/t/font_2667731_zq18s4xykfej.woff?t=1649751472791') format('woff'),
    url('https://at.alicdn.com/t/font_2667731_zq18s4xyfkej.ttf?t=1649751472791') format('truetype');
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

iconfont.css引入到App.vue中,便可以使用iconfont了

@import "@/styles/iconfont.css";
  • 1

二次封装uni再导出

因为使用eslint,直接使用uni的话会报lint错误,我们不妨从一个文件出口导出uni,那样还能二次拦截一次

/src/utils/uni.js

// eslint-disable-next-line
const uniGlobal = uni || window.uni

// 可以在uniGlobal复写属性
uniGlobal.log = function () {
  console.log(...arguments)
}

export default uniGlobal
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在vue文件中使用

import uni from '@/utils/uni'

export default {
  mounted() {
    uni.log('---uni console')
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

每个页面都需要存在的组件

比如封装的showConfirm,每个页面都需要,如果每个页面都要在template中引入一次会很麻烦,可以使用slot来解决这个痛点

/src/components/RootLayout.vue

<template>
  <div>
    <Confirm />
    <slot></slot>
  </div>
</template>
<script>
// AAA
// BBB
import Confirm from '@/components/Confirm'
export default {
  components: { Confirm }
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

就可以将AAABBB等组件放进到每个页面中,就不需要每个页面去引入

在页面使用RootLayout

<template>
  <RootLayout>
    <div>页面1</div>
  </RootLayout>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5

统一的下拉加载

  • onReachBottom尽管在components中能使用,但是还是需要在page的钩子中存在,函数里都可以不写逻辑,但是得在page的钩子中存在

/src/components/MoreLoading.vue

<template>
  <div class="boxLoading" v-if="isShow">
    <span class="text">加载中</span>
  </div>
</template>
<script>
export default {
  props: {
    isShow: {
      type: Boolean,
      default: false
    }
  }
}
</script>
<style lang="less" scoped>
.boxLoading {
  width: 40rpx;
  height: 100rpx;
  margin: auto;
  position: relative;
  white-space: nowrap;

  .text {
    font-size: 22rpx;
    color: #b5b8bc;
    position: absolute;
    top: 60rpx;
    left: 50%;
    transform: translateX(-50%);
  }

  &:before {
    content: "";
    width: 40rpx;
    height: 5px;
    background: #eeeeee;
    position: absolute;
    top: 69rpx;
    left: 0;
    border-radius: 50%;
    animation: shadow 0.5s linear infinite;
  }
  &:after {
    content: "";
    width: 40rpx;
    height: 40rpx;
    background: #03d0a9;
    animation: animate 0.5s linear infinite;
    position: absolute;
    top: 0;
    left: 0;
    border-radius: 3px;
  }
}

@keyframes animate {
  17% {
    border-bottom-right-radius: 3px;
  }
  25% {
    transform: translateY(9px) rotate(22.5deg);
  }
  50% {
    transform: translateY(18px) scale(1, 0.9) rotate(45deg);
    border-bottom-right-radius: 40px;
  }
  75% {
    transform: translateY(9px) rotate(67.5deg);
  }
  100% {
    transform: translateY(0) rotate(90deg);
  }
}

@keyframes shadow {
  0%,
  100% {
    transform: scale(1, 1);
  }
  50% {
    transform: scale(1.2, 1);
  }
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

/src/components/Loadmore.vue

<template>
  <div>
    <MoreLoading :isShow="loading" />
    <div class="tip-text" v-if="currentText">{{currentText}}</div>
  </div>
</template>
<script>
import { onReachBottom } from '@dcloudio/uni-app'
import { onMounted, reactive, toRefs, watch } from 'vue'
import MoreLoading from '@/components/MoreLoading'
import { defaultPageConfig } from '@/utils'

const maxDataTips = '我也是有底线的~'

export default {
  props: {
    isShow: {
      type: Boolean,
      default: true
    },
    isShowEmptyTips: {
      type: Boolean,
      default: false
    },
    suggestLoad: {
      type: Boolean,
      default: false
    },
    getLoadingStatus: {
      type: Function,
      default: (loading = []) => {}
    },
    updateList: {
      type: Function,
      default: (list = []) => {}
    },
    fetchMoreList: {
      type: Function,
      default: () => {}
    }
  },
  components: { MoreLoading },
  setup (props) {
    const pagination = {
      current: 1,
      total: 0,
      totalPage: 1
    }

    const state = reactive({
      loading: false,
      currentText: '',
      list: []
    })

    watch(() => state.list, () => {
      props.updateList && props.updateList(state.list)
    })

    watch(() => props.isShow, () => {
      pagination.current = 1
      state.currentText = ''

      if (props.isShow) {
        state.list = []
        getPageList()
      }
    })

    onMounted(() => {
      if (props.isShow) getPageList()
    })

    const setLoading = (loading) => {
      state.loading = loading
      props.getLoadingStatus && props.getLoadingStatus(loading)
    }

    const getPageList = () => {
      if (props.fetchMoreList) {
        setLoading(true)
        const page = pagination.current
        props.fetchMoreList({ page, per_page: defaultPageConfig.per_page })
          .then((res) => {
            setTimeout(() => {
              const pageInfo = res?.data || {}

              pagination.total = pageInfo.total
              pagination.totalPage = Math.ceil(pageInfo.total / pageInfo.per_page)

              const list = res?.list || []
              if (props.isShowEmptyTips && page === 1 && list?.length === 0) {
                state.currentText = '暂无数据'
              }
              if (props.suggestLoad && page !== 1 && list?.length === 0) {
                state.currentText = maxDataTips
              }
              state.list = [...state.list, ...list]
              setLoading(false)
            }, 500)
          })
          .catch(() => {
            setLoading(false)
          })
      }
    }

    onReachBottom(() => {
      console.log('---onReachBottom')
      if (state.loading) {
        return
      }

      if (!props.suggestLoad && pagination.current >= pagination.totalPage) {
        state.currentText = maxDataTips
      }

      if (state.currentText === maxDataTips) {
        return
      }

      pagination.current = pagination.current + 1
      getPageList()
    })

    return {
      ...toRefs(state)
    }
  }
}
</script>
<style lang="less" scoped>
.tip-text {
  text-align: center;
  color: #999;
  font-size: 24rpx;
  height: 80rpx;
  line-height: 80rpx;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

使用

<template>
  <Loadmore :fetchMoreList="fetchList" :updateList="updateList" />
</template>
  • 1
  • 2
  • 3

富文本实用技巧

重写富文本样式用标签不行的,因为小程序中rich-text会把整个富文本内容加到nodes上,并不会生成标签

  • 一、重写富文本标签样式

1、使用正则给标签加上类名,然后再写这些类名的样式,再引入到App.vue

export const editorHtmlLabel = [
  'h1',
  'h2',
  'h3',
  'h4',
  'strong',
  'p',
  'a',
  'table',
  'thead',
  'tbody',
  'th',
  'tr',
  'td',
  'img'
]
export function replaceClass (str = '') {
  const regStr = `(${editorHtmlLabel.map(item => `<${item}`).join('|')})`
  return str.replace(new RegExp(regStr, 'gi'), function (t) {
    return `${t} class="editor-${t.slice(1)}" `
  })
    .replace(/<a.*?>|<\/a>/g, '') // 小程序打开外链条件严苛,看需求是否需要屏蔽链接
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

/src/styles/editor.less

.editor-content {
  font-family: 'PingFang SC';
  color: #2F3033;
  font-size: 30rpx;
  word-break: break-all;

  .editor-h1 {
    color: #232426;
    font-size: 34rpx;
    font-weight: bold;
    margin-bottom: 30rpx;
  }

  .editor-h2 {
    color: #232426;
    font-size: 32rpx;
    font-weight: bold;
    margin-bottom: 28rpx;
  }

  .editor-h3 {
    color: #232426;
    font-size: 30rpx;
    margin-bottom: 24rpx;
  }

  .editor-h4 {
    color: #232426;
    font-size: 28rpx;
    margin-bottom: 22rpx;
  }

  .editor-strong {
    display: inline-block;
    margin: 10rpx 0 4rpx 0;
  }

  .editor-p {
    margin-bottom: 16rpx;
  }

  .editor-a {
    color: inherit;
  }

  .editor-img-container {
    max-width: 100%;
    overflow-x: auto;
  }

  .editor-img {
    max-width: 100%;
  }

  .editor-table {
    border-collapse: collapse;

    .editor-tr {

      &:first-child {
        .editor-td {
          border-top: 1px solid #666;
        }
      }

      .editor-td {
        border-right: 1px solid #666;
        border-bottom: 1px solid #666;

        &:first-child {
          border-left: 1px solid #666;
        }
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

引入到App.vue中

@import "@/styles/editor.css";
  • 1

使用replaceClass函数生成标签的class

<template>
  <div class="editor-content">
    <div v-html="$utils.replaceClass(detail.content)"></div>
  </div>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 二、富文本多行文本溢出效果

文本和富文本结合的这种,css控制的文本溢出效果会在ios上失效,这种情况我们要把样式加在富文本上

需要再外层再套一层div,并写上style

export function replaceSearchText (str = '', searchText = '', maxLine = 2) {
  const reg = new RegExp(searchText, 'i')
  const result = str.replace(reg, function (t) {
    return `<span style="color: #00BCA8;">${t}</span>`
  })
  let style = ''
  if (maxLine) {
    style = `style="overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: ${maxLine};-webkit-box-orient: vertical;word-break: break-all;"`
  }
  return `<div ${style}>${result}</div>`
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

自定义nav-bar

https://blog.csdn.net/weixin_45278509/article/details/123275848

在这里插入图片描述

mounted () {
  // #ifdef MP-WEIXIN
  // 获取胶囊位置,就能设置nav-bar的属性 paddingTop: `${btnInfo.top}px`   标题的height、lineHeight:btnInfo.height
  // eslint-disable-next-line
  const btnInfo = wx.getMenuButtonBoundingClientRect()
  this.btnInfo = btnInfo
  // #endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

自定义tab-bar

https://www.jianshu.com/p/18d8c7ad7da4

用户授权新方式

自 2022 年 10 月 25 日 24 时后(以下统称 “生效期” ),用户头像昵称获取规则将进行如下调整:

wx.getUserProfile 接口将被收回;wx.getUserInfo 接口获取用户昵称头像将被收回;

新的获取用户头像和昵称的方式采用下方自定义的模式:

https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/userProfile.html

兼容低版本

https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html

小程序的功能不断的增加,但是旧版本的微信客户端并不支持新功能,所以在使用这些新能力的时候需要做兼容。

ios可以横向滑动

加上下面css能解决大部分的问题

page {
  width: 100%;
  overflow-x: hidden;
}
  • 1
  • 2
  • 3
  • 4

但是还有这种可能,就是使用了padding,但是没有用box-sizing,也会出现ios上可以横向拖动

.container {
  padding: 40rpx;
  box-sizing: border-box;
}
  • 1
  • 2
  • 3
  • 4

使用命令直接上传

https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html

https://www.jianshu.com/p/8c595ac792e3

1、微信小程序编译模块安装

npm i miniprogram-ci -D
  • 1

2、小程序微信管理后台设置

小程序后台开发设置里面启用小程序代码上传,下载密匙文件放到项目里,添加ip白名单,此处是公网白名单,公网白名单查询链接

3、在package.json 的 “scripts”: 自定义编译命令

"upload": "npm run build:mp-weixin && node autoUpload.js",
  • 1

4、创建autoUpload.js 放在 package.json同级目录

const ci = require('miniprogram-ci')
const shelljs = require('shelljs')
const path = require('path')
const mainfest = require('./src/manifest.json')

const targetFile = path.resolve(__dirname, './package.json')
const packagejson = require(targetFile)
const currentVersion = packagejson.version
const versionArr = currentVersion.split('.')
const [mainVersion, subVersion, phaseVersion] = versionArr

// 默认版本号
const newVersion = `${mainVersion}.${subVersion}.${Number(phaseVersion) + 1}`

async function upload () {
  const project = new ci.Project({
    appid: mainfest['mp-weixin'].appid, // appid
    type: 'miniProgram',
    projectPath: path.resolve(__dirname, './dist/build/mp-weixin'), // 项目路径
    privateKeyPath: path.resolve(__dirname, `./private.${mainfest['mp-weixin'].appid}.key`), // 小程序后台的上传密匙
    ignores: ['node_modules/**/*']
  })

  ci.upload({
    project,
    version: newVersion,
    desc: mainfest.description,
    setting: {
      es6: true, // es6 转 es5
      es7: true, // 增强编译
      disableUseStrict: true,
      autoPrefixWXSS: true, // 上传时样式自动补全
      minifyJS: true,
      minifyWXML: true,
      minifyWXSS: true
    },
    onProgressUpdate: console.log
  }).then(res => {
    console.log(res)
    console.log(`上传成功,appId==${mainfest['mp-weixin'].appid},版本号==${newVersion}`)
    shelljs.sed('-i', `"version": "${currentVersion}"`, `"version": "${newVersion}"`, path.resolve(__dirname, './package.json')) // 同步更新项目中的版本号
  }).catch(error => {
    console.log(`上传失败,appId==${mainfest['mp-weixin'].appid},版本号==${newVersion}`)
    throw error
  })
};

upload()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

坑点:manifest.json 里面默认有注释,应该删除所有注释,不然会报json转换错误

5、运行 npm upload ,如开发者工具未登陆 则需要登陆,成功后的图片如下:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/231442
推荐阅读
相关标签
  

闽ICP备14008679号