微信小程序开发

1.微信小程序开发框架

1.1 框架说明

1.1.1 开发工具、调试工具

1.1.2 框架结构

使用vue-cli脚手架创建uni-app框架,vue3 + vite应用框架,uview-plusUI组件库,pinia状态管理器,pnpm依赖包管理。

nodeJs版本要求:^14.18.0 || >=16.0.0

npm:8.5.0 pnpm:6.32.6

vue/cli版本:@5.0.8 vue:^3.2.45 vite:^3.2.4

uview-plus:^3.1.26 pinia:^2.0.30

pnpm install -g @vue/cli vue create -p dcloudio/uni-preset-vue#vite 项目名

1.1.3 框架目录结构

┌─src
│  ├─api                请求地址目录
│  ├─assets             静态资源目录
│  │  └─css                样式
│  │  │  ├─common          公共样式、字体图标样式、uview组件库
│  │  │  └─app.styl        全局样式引入
│  │  └─icon-font          字体图标库
│  └─common
│  │  ├─http               网络请求封装
│  │  ├─bus.js             eventBus封装
│  │  └─common.js          公共方法封装
│  ├─component             公共组件
│  ├─conf                  系统配置文件
│  ├─pages                 主包目录
│  ├─pagesAffairs          办事页面目录
│  ├─pagesCommon           公用页面目录(办理流程、办事指南)
│  ├─pagesIndex            首页页面目录
│  ├─pagesServiced         服务页面目录
│  ├─pagesUser             我的页面目录
│  ├─plugins               插件引入
│  │  └─uview.js           uview-plusUI组件库
│  ├─static                静态图片资源目录
│  ├─stores                pinia状态管理
│  ├─utils                 公用函数、工具目录
│  ├─App.vue               应用配置,用来配置小程序全局样式以及监听、应用生命周期
│  ├─main.js               Vue初始化入口文件
│  ├─manifest.json         配置应用名称、appid、logo、版本等打包信息
│  ├─pages.json            配置页面路由、导航条、选项卡等页面类信息
│─.editorconfig            统一代码编辑器配置
│─.eslintignore            统一代码编辑器配置忽略文件
│─.eslintrc.js             eslint代码约束配置
│─.gitignore               git代码提交忽略文件
│─.prettierrc.json         文件代码格式化配置
│─index.html               静态资源目录
│─jsconfig.json            javascript根目录
│─package.json             常见的配置有配置项目启动、打包命令,声明依赖包等
│─pnpm-lock.yaml
│─postcss.config.js        css适配方案
└─vite.config.js           项目打包、插件引入、编译相关配置

1.2 组件库

UI组件库: uview-plus@3.1.26

// 安装命令
pnpm install uview-plus@3.1.26

引入方式:

// plugins/uview.js 初始化配置
import uviewPlus from "uview-plus"

export const $toast = (message) => {
  uni.$u.toast(message)
}

export default app => {
  app.provide('$toast', $toast)
  app.use(uviewPlus)
}

// main.js 主入口文件进行引用
import uviewPlusFn from '@/plugins/uview'
// 挂载uviewPlus
uviewPlusFn(app)

// App.vue 引入样式文件
<style lang="scss" scope>
// uview-plus
@import 'uview-plus/index.scss';
// 引入uview-plus主题
@import 'uview-plus/theme.scss';
</style>

// 注意:uview-plus开发还不完善,小程序中相关组件样式不生效,存在报错,需在node_modules中手动引入主题样式
// node_modules/-plus/libs/css/components.css中引入
@import "../../theme.scss";

1.3 状态管理器

项目使用:pinia@2.0.30

// 安装命令
pnpm install pinia@3.1.26
// 使用持久化存储-安装命令
pnpm install pinia-plugin-persist

引入方式:

// stores/index.js 初始化配置
import { createPinia } from "pinia"
import piniaPersist from 'pinia-plugin-persist'

const store = createPinia()
// 持久化存储 小程序暂不支持sessionStorage、localStorage所以不能使用
store.use(piniaPersist)

export function setupStore (app) {
  app.use(store)
}

export { store }

// main.js 主入口文件进行引用
import { setupStore } from './stores/index'
// 挂载pinia
setupStore(app))

使用方式:

import { defineStore } from 'pinia'
import { store } from "../index"

export const userCommonStore = defineStore({
  id: 'common',
  state: () => {
    return {
      token: ''
    }
  },
  actions: {
    // token
    setToken (token) {
      this.token = token
    }
  },
  persist: {
    // 开启持久化存储
    enabled: true,
    // 自定义持久化参数
    strategies: [
      {
        // 自定义key,默认就是仓库的key
        key: 'token',
        // 自定义存储方式,默认sessionStorage
        storage: localStorage,
        // 指定要持久化的数据,默认所有 state 都会进行缓存,可以通过 paths 指定要持久化的字段,其他的则不会进行持久化。
        paths: ['token']
      }
    ]
  }
})
// 导出状态hook
export function userCommonStoreHook () {
  return userCommonStore(store)
}

// vue文件使用
import { userCommonStoreHook } from '@/stores/modules/common'
<script>
// 存储
userCommonStoreHook().setToken('value')
// 获取
const token = userCommonStoreHook().token
</script>

1.4 网络请求封装

项目使用:结合new Promise + uni.request(OBJECT)

相关配置方法:uniapp-request

系统配置:

// conf/index.js 相关系统配置文件
export const API = {
  // 开发环境
  development: {
    DEFAULT: 'http://192.168.1.246:19601/iframework' || import.meta.env.BASE_URL,
    JR: 'http://192.168.1.246:19601/iframework' || '/api', // 测试环境
    API: 'http://192.168.18.91:8001' // 第三方api
  },
  // 生产环境
  production: {
    DEFAULT: 'http://192.168.1.246:19601/iframework' || import.meta.env.BASE_URL,
    JR: 'http://192.168.1.246:19601/iframework' || '/api',
    API: 'http://192.168.18.91:8001' // 第三方api
  }
}
const isProd = import.meta.env.MODE === 'production'
export const BASE_CONFIG = isProd ? API.production : API.development

使用方法:

// common/http/http-async.js 目录文件
'use strict'
import { API as configApi } from '@/conf/'
import qs from 'querystring'
import { getStorage, removeStorage } from '@/utils/storage'
import { linkJump } from '@/common/common'
import { userCommonStoreHook } from '@/stores/modules/common'

// 获取接口根路径
const isProd = import.meta.env.MODE === 'production'
const BASE_PATH = isProd ? configApi.production : configApi.development

export const request = (options) => {
  return new Promise((resolve, reject) => {
    let url = options.url

    // 简化类型设置
    const headers = {
      'Content-Type': 'application/json; charset=UTF-8',
      'Cookie': `JSESSIONID=${getStorage('JSESSIONID') || ''}`
    }
    // 提交表单类型
    if (options.isForm) {
      headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
      delete options.isForm
    }
    // 提交上传类型
    if (options.isFile) {
      headers['Content-Type'] = 'multipart/form-data; charset=UTF-8'
      delete options.isFile
    }
    // 校验post数据格式
    const contentTyoe = headers['Content-Type']
    if (
      typeof options.params === 'object' &&
      contentTyoe &&
      String(contentTyoe).indexOf('application/x-www-form-urlencoded') > -1
    ) {
      options.params = qs.stringify(options.params)
    }
    // 添加token
    if (userCommonStoreHook().token) {
      headers['token'] = userCommonStoreHook().token
    }
    // 设置请求路径
    options.url = BASE_PATH[options.apiType || 'JR'] + url
    console.log('url', options, options.params)
    uni.request({
      url: options.url,
      method: options.method,
      data: options.params,
      withCredentials: true,
      header: headers,
      success (response) {
        const status = +response.statusCode
        const data = response.data || {}
        if (status === 200 || status === 304) {
          // 第三方api接口判断
          if (data.statusCode === 200 && options.url.indexOf('api.jsp')) {
            if (data.statusCode === 200) {
              resolve(data)
            } else {
              uni.$u.toast(data.msg || '')
              reject(data)
            }
            return
          }
          if (data.id === 500) {
            uni.$u.toast(data.msg || '')
            // 退出登录
            userCommonStoreHook().clearOut()
          } else if (data.id === '01') {
            resolve(data)
          } else if (data.id === '02' && data.msg === 'token不能为空') {
            uni.$u.toast(data.msg || '')
            // 退出登录
            userCommonStoreHook().clearOut()
          } else {
            uni.$u.toast(data.msg || '')
            reject(data)
          }
        } else if (status === 404) {
          linkJump('/pages/404/index')
          reject(data)
        } else {
          reject(data)
        }
      },
      fail (error) {
        if (err.errMsg === 'request:fail timeout') {
          uni.$u.toast('请求超时,请重试')
        } else if (err.errMsg === 'request:fail ') {
          uni.$u.toast('无网络')
        } else {
          uni.$u.toast('服务器繁忙' || error.errMsg)
        }
        reject(error)
      }
    })
  })
}

请求调用:

'use strict'
 import { request } from '@/common/http/http-async'
 
 export const login = (params = {}) => {
  const param = {
    apiType: 'JR',
    method: 'post',
    json: true,
    url: '/api/internet/login',
    params: params
  }
  return request(param)
}

// 第三方api调用
export const getChannelIdList = (params = {}) => {
  const param = {
    apiType: 'API',
    method: 'get',
    json: true,
    url: '/api.jsp',
    params: params
  }
  return request(param)
}

// vue文件使用
import { getChannelIdList } from '@/api/index'
let params = {}
getChannelIdList(params).then((result) => {}).catch((err) => {})

1.5 .eslintrc.js和.prettierrc配置

// .eslintrc.js
module.exports = {
  // 默认情况下,ESLint会在所有父级组件中寻找配置文件,一直到根目录。ESLint一旦发现配置文件中有 "root": true,它就会停止在父级目录中寻找。
  root: true,
  env: {
    node: true,
    es6: true,
    'vue/setup-compiler-macros': true
  },
  extends: [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    'prettier',
    '@vue/prettier',
    'plugin:prettier/recommended'
  ],
  plugins: ['prettier'],
  // 对Babel解析器的包装使其与 ESLint 兼容。
  parserOptions: {
    ecmaVersion: 2020,
    parse: 'babel-eslint',
    // 代码是 ECMAScript 模块
    sourceType: 'module'
  },
  // 去掉校验
  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
    defineProps: 'readonly',
    defineEmits: 'readonly',
    defineExpose: 'readonly',
    withDefaults: 'readonly'
  },
  rules: {
    'prettier/prettier': 'error',
    'arrow-body-style': 'off',
    'prefer-arrow-callback': 'off',
    // 关闭名称校验
    'vue/multi-word-component-names': 'off'
  }
}

// .prettierrc.json 代码格式化配置
{
  "semi": false,
  "eslintIntegration": true,
  "singleQuote": true,
  "endOfLine": "lf",
  "tabWidth": 2,
  "trailingComma": "none",
  "bracketSpacing": true,
  "arrowParens": "avoid"
}

2. 项目避坑

2.1 包js文件超过2MB无法运行

原因:微信官方对小程序的体积大小有硬性要求,上限为2048KB

处理方法:

  • 分包
  • 优化代码,删除掉不用的代码
  • 图片压缩或者上传服务器
// manifest.json 相关配置
"mp-weixin": {
    // 开启分包优化
    "optimization": {
      "subPackages": true
    }
}

// pages.json 路径配置
// 分包
  "subPackages": [
    {
      "root": "pagesCommon",
      "name": "pagesCommon",
      "pages": [
        {
          "path": "handle-event/index",
          "style": {
            "navigationBarTitleText": "办事",
            "enablePullDownRefresh": true,
            "navigationStyle": "custom"
          }
        }
      ]
    },
    {
      "root": "pagesIndex",
      "name": "pagesIndex",
      "pages": [
        {
          "path": "notice-list/index",
          "style": {
            "navigationBarTitleText": "通知公告",
            "enablePullDownRefresh": true,
            "navigationStyle": "custom"
          }
        },
        {
          "path": "problem-list/index",
          "style": {
            "navigationBarTitleText": "常见问题",
            "enablePullDownRefresh": true,
            "navigationStyle": "custom"
          }
        },
        {
          "path": "notice-detail/index",
          "style": {
            "navigationBarTitleText": "通知公告详情",
            "enablePullDownRefresh": true,
            "navigationStyle": "custom"
          }
        }
      ]
    }
}

2.2 图片生产环境无法预览

原因:微信小程序background-image只能用网络url或者base64图片编码

处理方法:

  • 使用https协议的cdn线上地址访问静态资源(放到服务器访问)
  • 通过base64图片访问
/**
 * 本地图片转换base64
 * @param {*} folder 文件夹名称
 * @param {*} fileName 文件名称
 * @param {*} format 文件后缀
 */
export const getLocalImgToBase64 = (folder, fileName, format = 'png') => {
  let imgUrl = `/static/${folder}/${fileName}.${format}`
  let base64 = uni.getFileSystemManager().readFileSync(imgUrl, 'base64')
  return `data:image/png;base64,${base64}`
}

// 使用
:style="{ backgroundImage: `url(${getLocalImgToBase64('文件夹名称', '文件名', 'png')})`}"

// 小程序图片模糊失真处理(作用于image图片)
image-rendering -moz-crisp-edges
image-rendering -o-crisp-edges
image-rendering -webkit-optimize-contrast // 设置图像缩放算法
image-rendering crisp-edges // 缩放后不会变模糊
-ms-interpolation-mode nearest-neighbor

2.3 无法显示彩色图标,兼容微信小程序

下载字体包

  1. 安装插件
// 安装插件
cnpm install -g iconfont-tools
// 进入到文件夹里面用cmd执行下列命令
iconfont-tools

// main.js
// 引入阿里彩色矢量字体
@import '@/assets/icon-font/iconfont.css'

2.4 字体图标显示空白或者报错

下载字体包

ttf文件转换成base64格式,转换地址:transfonter.org

  1. 将下载后的iconfont.ttf文件引入
@font-face {
  font-family 'iconfont'
  src url('../../icon-font/iconfont.woff') format('woff'), url('../../icon-font/iconfont.woff2') format('woff2'), url('base64编码') format('truetype')

.iconfont {
  font-family 'iconfont' !important
  font-size 45rpx
  font-style normal
  // 抗锯齿
  -webkit-font-smoothing antialiased
  -moz-osx-font-smoothing grayscale
}

.icon-jiantou-zuo:before {
  content: "\e71b";
}

3. 打包

pnpm run build:mp-weixin

登录微信公众平台,设置 > 账号信息 > AppID,获取到ID后打开微信开发者工具,扫码登录需要发布小程序的微信,右侧基本信息,绑定AppId

  1. 上传代码,代码包不能超过2MB

打开微信公众平台,设置项目成员、体验成员

修改打开页面路径

如需发布上线,点击提交审核即可

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值