阅读 227

Taro 开发微信小程序踩坑汇总

1. 项目背景及疑问

在做微信小程序前,已经上线了一个 h5 项目,h5 中采用的 UI 框架为Vant,Vue 版本为 3.x。为了保持用户体验,就采用了 Vant Weapp,但 Taro 中的 vue 版本选择了 2.x 版本。

  1. 为什么用Taro,不用uni-app

    答:下次就用 uni-app。

  2. 都用 Taro 了,为什么jsx不搞起来?

    答:Taro 主打 react 技术栈,但是 Vant Weapp 是主打 vue 的,我担心在 Taro + Vant Weapp 里面用 jsx,掉进坑里,爬不出来,所以采用了保险点的方式,vue 版本也选择了 2.x 版本,即Taro + vue2.x + vant-weapp

  3. Taro 里使用 Vant-Weapp,即使用使用原生第三方组件库开发,不再具有多端转换的能力,那为什么不直接用微信小程序原生开发?

    答:项目只要求微信小程序可以使用,不做多端,所以可以放心使用,还有就是 h5 项目里的一些逻辑可以复用。

2. 项目前的准备

2.1 安装及初始化项目

2.1.1 Taro 安装 + 初始化项目

注意: node 环境(>=12.0.0)

  1. 安装

yarn global add @tarojs/cli 复制代码

  1. 初始化

taro init taro_demo 复制代码

  1. 安装依赖

# 切换目录 cd taro_demo # 安装依赖 yarn install 复制代码

  1. 修改根目录package.json里的scripts下的dev:weapp开发启动命令

{   "scripts" : { -   "dev:weapp": "taro build --type weapp" +   # watch 同时开启压缩 +   "dev:weapp": "set NODE_ENV=production && taro build --type weapp --watch"   } } 复制代码

注意:关于这里将NODE_ENV直接设置为production,项目的配置就直接使用了生产配置,即config/prod.js配置,本地开发,配置一些开发设置,难免不方便,我们可以安装cross-env再配置一个变量,这样就可以解决了。

yarn add cross-env -D 复制代码

{   "scripts" : { -   "build:weapp": "taro build --type weapp", +   "build:weapp": "cross-env DEV_MODE_ENV=false taro build --type weapp", -   "dev:weapp": "set NODE_ENV=production && taro build --type weapp --watch" +   "dev:weapp": "cross-env NODE_ENV=production DEV_MODE_ENV=true taro build --type weapp --watch",   } } 复制代码

这样我们就可以根据自己配置的DEV_MODE_ENV变量来区分是生产还是开发。但这里有一个问题,process.env.DEV_MODE_ENV这个变量在src下是访问不到的,config目录可以正常访问到。要想在 src 下访问自定义 env 变量,需要在config/*.js里的env里进行设置,如:

// config/dev.js module.exports = {   env: {     NODE_ENV: '"development"',     // 添加SERVICES_BASE_URL变量     SERVICES_BASE_URL: '"http://xxx"',   }, }; // config/prod.js module.exports = {   env: {     NODE_ENV: '"production"',     // 这里添加的 DEV_MODE_ENV 就起作用了,进行生产、开发区分,不用每次手动去改了     SERVICES_BASE_URL:       process.env.DEV_MODE_ENV === "true" ? "http://xxx" : "https://xxx",   }, }; // 代码里访问`process.env.SERVICES_BASE_URL`是可以正常获取到的 复制代码

  1. 启动开发命令,成功后,即可看到根目录有新生成的dist目录

yarn run dev:weapp 复制代码

2.1.2 微信开发者工具
  1. 下载微信开发者工具

  2. 选择刚刚的dist进行打开,不出意外,可以正常预览了。

2.2 项目中主要 package version

后面出现的问题,都是基于这些版本。

packageversion
Taro3.3.7
vant weapp1.9.2
echarts5.1.2

3. Taro 中使用 Vant Weapp

3.1 修改postcss配置,不转换 vant 的样式

// config/index.js const config = {   mini: {     enableSourceMap: false, // 用于控制是否生成 js、css 对应的 sourceMap     // http://taro-docs.jd.com/taro/docs/vant     postcss: {       pxtransform: {         enable: true,         config: {           selectorBlackList: [/van-/],         },       },       url: {         enable: true,         config: {           limit: 1024,         },       },       cssModules: {         enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true         config: {           namingPattern: "module", // 转换模式,取值为 global/module           generateScopedName: "[name]__[local]___[hash:base64:5]",         },       },     },   }, }; 复制代码

3.2 配置小程序原生文件和引用 vant 的组件

3.2.1 根据文档,使用 vant 需要这样做
// config/index.js const config = {   // ...   copy: {     patterns: [       // 前面3个是基础的       {         from: "src/components/vant-weapp/dist/wxs",         to: "dist/components/vant-weapp/dist/wxs",       },       {         from: "src/components/vant-weapp/dist/common/style",         to: "dist/components/vant-weapp/dist/common/style",       },       {         from: "src/components/vant-weapp/dist/common/index.wxss",         to: "dist/components/vant-weapp/dist/common/index.wxss",       },       // 需要用到vant哪个组件就引入,比如引入button       {         from: `src/components/vant-weapp/dist/button/index.wxs`,         to: `dist/components/vant-weapp/dist/button/index.wxs`,       },       {         from: "src/components/vant-weapp/dist/button/index.wxml",         to: "dist/components/vant-weapp/dist/button/index.wxml",       },     ],     options: {},   }, }; 复制代码

// 页面的 index.config.js 中引入或者全局 src/app.config.js中的引入 export default {   navigationBarTitleText: "首页",   usingComponents: {     "van-button": "../../components/vant-weapp/dist/button/index",   }, }; 复制代码

发现没,这不是一堆重复动作吗,需要用到 vant 组件,就必须要到两个地方配置一下,很难受。

3.2.2 升级改造,优化引入步骤
  1. 根目录创建vantComponentConf.js文件

    这样只需每次修改 vantComponentConf 文件,组件引入就生效了,不用重复改动,当然有能力可以直接使用 babel,写一个 loader 解决

注意:

  • 每次添加新的 vant 组件,项目还是得重启,不能像vite那样潇洒,改配置文件也能马上自动重启生效

  • 部分 vant 组件,引入重启后还是提示缺少其它组件,那就再把缺少的组件添加到 vantComponentConf 里去,如 button 组件就依赖 icon 和 loading,单独引入 button 是不行的。

const vantComponetNames = ["button", "icon", "loading"]; module.exports = vantComponetNames; 复制代码

  1. 修改config/index.js文件

+ const path = require("path"); + const fs = require("fs"); + const vantComponetNames = require("../vantComponentConf"); + + const handleGetPatterns = (keyArr) => { +   // 这些为公共组件 +   const defaultArr = [ +     { +       from: "src/components/vant-weapp/dist/wxs", +       to: "dist/components/vant-weapp/dist/wxs", +     }, +     { +       from: "src/components/vant-weapp/dist/common/style", +       to: "dist/components/vant-weapp/dist/common/style", +     }, +     { +       from: "src/components/vant-weapp/dist/common/index.wxss", +       to: "dist/components/vant-weapp/dist/common/index.wxss", +     }, +   ]; + +   const componentArr = keyArr +     .map((componentName) => { +       // 同步检测这个文件是否存在 +       // 因为vant weapp部分组件文件下的不存在 wxs 文件 +       const componentPath = path.resolve( +         __dirname, +         `../src/components/vant-weapp/dist/${componentName}/index.wxs` +       ); + +       const isState = fs.statSync(componentPath, { throwIfNoEntry: false }); + +       return [ +         { +           from: `src/components/vant-weapp/dist/${componentName}/index.${ +             isState ? "wxs" : "wxss" +           }`, +           to: `dist/components/vant-weapp/dist/${componentName}/index.${ +             isState ? "wxs" : "wxss" +           }`, +         }, +         { +           from: +             "src/components/vant-weapp/dist/" + componentName + "/index.wxml", +           to: +             "dist/components/vant-weapp/dist/" + componentName + "/index.wxml", +         }, +       ]; +     }) +     .flat(Infinity); + +   return [...defaultArr, ...componentArr]; + }; const config = {   copy: { +    // http://taro-docs.jd.com/taro/docs/vant +    patterns: handleGetPatterns(vantComponetNames),     options: {},   }, } 复制代码

  1. 修改src/app.config.js文件

+ const vantComponetNames = require("../vantComponentConf"); + + const getVantComponetConf = (arr) => { +   const result = {}; +   arr.forEach((key) => { +     result[`van-${key}`] = `./components/vant-weapp/dist/${key}/index`; +   }); + +   return result; + }; export default {   usingComponents: { +    ...getVantComponetConf(vantComponetNames),   }, } 复制代码

4. 关于使用 Vant 遇到的一些问题

4.1 部分 vant 组件的插槽不能正确显示

如下search组件不能正确显示 slot 内容

<view>   <van-search use-action-slot="true" placeholder="请输入门店名称搜索...">     <view slot="action"> 搜索 </view>   </van-search> </view> 复制代码

clipboard.png

解决:

必须使用 taro 提供的slot-view组件,React 中使用Slot组件

<template>   <view>     <van-search use-action-slot="true" placeholder="请输入门店名称搜索...">       <slot-view name="action">         <text>搜索</text>       </slot-view>     </van-search>   </view> </template> 复制代码

clipboard2.jpg

slot 里的内容正确显示了

4.2 vant-search获取不到输入的内容

<template>   <view>     <van-search       :value="searchVal"       use-action-slot="true"     >       <slot-view name="action">         <text @tap.stop="onSearch">搜索</text>       </slot-view>     </van-search>   </view> </template> <script> export default {   data() {     return {       searchVal: "",     }   },   methods: {     onSearch() {       // 这里并不会打印出实时的输入值       console.log("this.searchVal", this.searchVal);     }   }, } </script> 复制代码

解决:

vant-search并不会自己同步更新数据,需要手动添加change事件更新

<template>   <view>     <van-search       :value="searchVal"       use-action-slot="true" +      @change="onChange"     >       <slot-view name="action">         <text @tap.stop="onSearch">搜索</text>       </slot-view>     </van-search>   </view> </template> <script> export default {   data() {     return {       searchVal: "",     }   },   methods: { +    onChange(e) { +      this.searchVal = e.detail; +    },     onSearch() {       console.log("this.searchVal", this.searchVal);     }   }, } </script> 复制代码

4.3 使用van-field组件时,vue 中的 computed 的值不能触发响应更新

计算属性computed里定义的 submitBtnDisabled,里面依赖的值改变了,但是没触发响应更新。不用怀疑,肯定不是 vue 的问题

<template>   <view class="phoneLoginWrap">     <form @submit="handleSubmit">       <van-cell-group>         <van-field           v-model="user"           :required="true"           name="user"           label="账号:"           placeholder="请输入账号"           title-width="4em"         />         <van-field           v-model="password"           :required="true"           name="password"           type="password"           label="密码:"           placeholder="请输入密码"         />       </van-cell-group>       <van-button type="info" form-type="submit" :block="true" :disabled="submitBtnDisabled">登录</van-button>     </form>   </view> </template> <script> export default {   data() {     return {       user: "",       password: ""     }   },   computed: {       // 只有首次进入页面时触发,后面不被触发       // 导致登录按钮永远是disabeld状态     submitBtnDisabled: function () {       return this.user !== '' && this.password !== '' ? false : true;     }   },   methods: {     handleSubmit(e) {         // vant-button 即使disabled为true,依然可以进入这里         // UI样式生效了,但事件依然可以被触发         // 所以这里要加判断       if (this.submitBtnDisabled) return;       console.log("login!!!", e)     }   } } </script> 复制代码

解决:

其实跟上面的vant-search一个毛病,需要自己手动给vant-field加上change事件,这样在改变输入框的值的时候,按钮的 diabled 属性可以动态更新

<template>   <view>     <form @submit="handleSubmit">       <van-cell-group>         <van-field           v-model="user"           :required="true"           name="user"           label="账号:"           placeholder="请输入账号" +          @change="(...args) => handleFieldChange.call(this, 'user', ...args)"         />         <van-field           v-model="password"           :required="true"           name="password"           type="password"           label="密码:"           placeholder="请输入密码" +          @change="(...args) => handleFieldChange.call(this, 'password', ...args)"         />       </van-cell-group>       <van-button type="info" form-type="submit" :block="true" :disabled="submitBtnDisabled">登录</van-button>     </form>   </view> </template> <script> export default {   name: 'PhoneNumLogin',   data() {     return {       user: "",       password: ""     }   },   computed: {     submitBtnDisabled: function () {       return this.user !== '' && this.password !== '' ? false : true;     }   },   methods: { +    handleFieldChange(type, e) { +      const val = e.detail.trim(); +        // 这里手动更新data的值 +      if (type === 'user') { +        this.user = val; +      } else { +        this.password = val; +      }     },     handleSubmit(e) {       if (this.submitBtnDisabled) return;       console.log("login!!!", e)     }   } } </script> 复制代码

4.4 vant 组件使用的时候控制台大片警告

clipboard3-1.jpg

原因: 微信基础库修改了校验逻辑,所以才出现 slot 没找到或者 properties 类型不对等警告信息

5. 关于使用 ec-canvas echarts 遇到的一些问题

5.1 echarts 体积过大问题

解决:

  • echarts 在线定制下载

  • 一定要在线选择压缩,下载的文件放在 ec-canvas/echarts.js,然后一定要重命名为echarts.js

5.2 ec-canvas 报错 t.addEventListener is not a function

TypeError: t.addEventListener is not a function 复制代码

解决:

在 wx-canvas.js 文件中添加:

// wx-canvas.js export default class WxCanvas {     constructor(ctx, canvasId, isNew, canvasNode) {         // ...     } +    // 新增空函数,修复调用 echarts.init 时报错 +    addEventListener() {} } 复制代码

5.3 ec-canvas 报错 this.Chart.init is not a function

TypeError: this.Chart.init is not a function 复制代码

解决:

Taro 3.0 以上需要用 selectComponent 获取子组件实例

import Taro, { getCurrentInstance } from "@tarojs/taro"; function initChart(el = "#canvasId") {   const currentIns = getCurrentInstance();   currentIns.page.selectComponent(el).init((canvas, width, height) => {     const sysInfo = Taro.getSystemInfoSync();     const chart = echarts.init(canvas, null, {       width: width,       height: height,       devicePixelRatio: sysInfo.devicePixelRatio || sysInfo.pixelRatio, // 像素     });     canvas.setChart(chart);     var option = {       // ...     };     chart.setOption(option);     return chart;   }); } 复制代码

5.4 ec-canvas 接口请求数据后后异步渲染或 socket 获取数据实时动态渲染

<template>     <view style="widh: 100%; height: 300px;">         <ec-canvas id="yearLineChart" canvas-id="month-line-chart" :ec="yearChart" />     </view> </template> <script> import Taro from '@tarojs/taro'; import * as echarts from '@/src/components/echarts-for-weixin-master/ec-canvas/echarts'; function setOption(chart) {   var option = {     // ...   };   chart.setOption(option); } export default {     data() {         return {             yearChart: {                 // 将 lazyLoad 设为 true 后,需要手动初始化图表                 lazyLoad: true               }         }     },     methods: {         // 初始化echarts         initChart(el, data) {           const currentIns = Taro.getCurrentInstance();           currentIns.page.selectComponent(el)             .init((canvas, width, height) => {               const sysInfo = Taro.getSystemInfoSync();               const chart = echarts.init(canvas, null, {                 width: width,                 height: height,                 devicePixelRatio: sysInfo.devicePixelRatio || sysInfo.pixelRatio // 像素               });               setOption(chart, data);               // 注意这里一定要返回 chart 实例,否则会影响事件处理等               return chart;             });         },         // ajax后台获取数据         getData() {             // ....             // 假设data从后台返回             const data = {}             this.initChart('year', '#yearLineChart', data);         }     },     onShow() {       this.getData();     } } </script> 复制代码

5.5 导入 echart 包后提示 it exceeds the max of 500KB

命令行警告,导致每次ctrl + s保存代码,微信开发者工具那边更新缓慢

[BABEL] Note: The code generator has deoptimised the styling of "unknown" as it exceeds the max of "500KB" 复制代码

解决:

  1. config/index.js 添加 exclude 排除项

module.exports = {   // ...   mini: {     // ...     compile: {       exclude: [         path.resolve(           __dirname,           "../src/components/echarts-for-weixin-master/ec-canvas/echarts.js"         ),       ],     },   }, }; 复制代码

  1. 在 babel 配置文件中添加compact: false,默认为 auto

module.exports = {   presets: [     [       "taro",       {         framework: "vue",         ts: false,       },     ],   ],   compact: false, }; 复制代码

5.6 使用 ec-canvas 后,小程序桌面端打开一片显示空白

造成空白的原因,其实是已经报错了

103497502-acfb6580-4e7c-11eb-8639-5ebdc9388cff.png

有说是给ec-canvas标签添加force-use-old-canvas="true"属性为 true 后,既可以。但是我这里 echarts 用的是5.1.2版本,实测没生效。

解决:

windows 终端小程序不支持 canvas2d,修改为在 windows 终端采用旧版 canvas

clipboard4-1.jpg

最新版本有无修复桌面端打开是否成功,我这里没有验证。我直接对着这个 PR 修改源码了,实测有效。

// 修改echarts-for-weixin-master/ec-canvas/ec-canvas.js Component({     methods: { -        init: function (callback) { +        init: async function (callback) {             const version = wx.getSystemInfoSync().SDKVersion;             const canUseNewCanvas = compareVersion(version, "2.9.0") >= 0;             const forceUseOldCanvas = this.data.forceUseOldCanvas;  -          const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas; +          // const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas; +          // 修复桌面端微信打开,echarts一片空白,控制台报错 +          const { platform } = await wx.getSystemInfo(); +          let isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas; +          // 直接通过正则判断,如果是windows桌面端微信打开 +          // 将isUseNewCanvas的值设为false +          if (platform && /windows/i.test(platform)) { +            isUseNewCanvas = false; +           }            this.setData({ isUseNewCanvas });            // ...         }     } }) 复制代码

6. 关于 Taro 的一些问题

6.1 项目体积过大,使用微信开发者工具无法预览,要进行分包处理

注意: 配置在tabBar中的页面不能分包写到subpackages中去

  • 分包前:

// src/app.config.js export default {   pages: ["pages/foo1/index", "pages/foo2/index", "pages/foo3/index"], }; 复制代码

  • 分包后:

// src/app.config.js export default {   pages: ["pages/foo1/index", "pages/foo2/index"],   subpackages: [     {       root: "pages/foo3",       pages: ["index"],     },   ], }; 复制代码

6.2 Taro.eventCenter 全局消息的问题

6.2.1 页面路由跳转 Taro.eventCenter 全局消息首次监听不生效

问题描述

  • 1.路由 A 页面点击确认按钮后 发送全局消息事件 Taro.eventCenter.trigger('getInfo', data)

  • 2.然后跳转到路由 B 页面 B 页面在 componentWillMount 页面被加载的时候 监听 Taro.eventCenter.on('getInfo', e => { }) 监听不到该事件

clipboard5-1.jpg

解决:

  • 手动在父页面手动延迟 trigger(不够优雅)

  • 使用 vuex

6.2.2 Tabbar 之间的通信,Taro.eventCenter 通信首次不触发

原因:跟上面一样,事件已经被触发,但是后面页面还没被初始化,不支持历史消息订阅

解决:

  • 手动在 tab foo1 页面 setTimeout 手动延迟 trigger,然后 tab foo2 就可以监听到了(不优雅)

Taro.switchTab({   url: "/pages/foo1/index",   complete: () => {     // 这样在foo2页面的onShow里就可以监听到了     setTimeout(() => {       Taro.eventCenter.trigger(eventTypes.SENT_CUS_ID, 10086);     }, 800);   }, }); 复制代码

  • 不使用原生tab,自己另造一个,就摆脱了这个局限

  • 使用 vuex(打包增加体积)

  • 既然都 vue 了,eventBus挺好

import Vue from "vue"; import * as types from "./eventTypes"; // 特殊的eventBus // https://segmentfault.com/a/1190000012808179 const bus = new Vue({   data() {     return {       // 定义数据       cus_id: null,     };   },   created() {     // 绑定监听     this.$on(types.CHANGE_CUS_ID, (id) => {       console.log("CHANGE_SHOP_ID==>");       this.cusId = id;     });   }, }); export default bus; export { types }; 复制代码

  • vue 中的其他通信方式...

6.3 sitemap 索引情况提示

控制台警告

[sitemap 索引情况提示] 根据 sitemap 的规则[0],当前页面 [pages/overview/index] 将被索引 复制代码

解决:

修改小程序项目配置文件 project.config.json 的 setting 中配置字段 checkSiteMap 为 false

{   "setting": {     "checkSiteMap": false   } } 复制代码

6.4 生产打包去除 console

  • 安装

注意:

最新版本的 terser 使用会报错

TypeError: Cannot read property 'javascript' of undefined 复制代码

降级安装 4.x 版本

yarn add terser-webpack-plugin@4.2.3 -D 复制代码

  • config/prod.js 配置文件修改

module.exports = {   mini: {     // http://taro-docs.jd.com/taro/docs/config-detail#miniwebpackchain     webpackChain(chain, webpack) {       chain.plugin("terser").use(TerserPlugin, [         {           // https://www.npmjs.com/package/terser-webpack-plugin#terseroptionsco           terserOptions: {             mangle: true,             compress: {               // https://swc.rs/docs/configuration/minification               drop_console: true,               drop_debugger: true,             },             output: {               // https://drylint.com/Webpack/terser-webpack-plugin.html#%E5%AE%89%E8%A3%85               ascii_only: true, // 转义字符串和正则中的 Unicode 字符             },           },         },       ]);     },   }, }; 复制代码

6.5 Taro.onSocketError 返回的错误信息“未完成的操作”,拿不到真实的错误

wx.onSocketError 提示未完成操作是什么意思?

未解决

6.6 微信小程序体验版报错 request fail

安卓上报错信息,换ios可以看到具体报错信息

request:fail -2:net::ERR_FAILED 复制代码

解决:

  • 排查服务器域名配置,严格按照这个规范进行配置

  • 检查证书配置,nginx 配中间证书

6.7 微信小程序新版本更新检测,自动更新

export default {   onReady() {     if (!Taro.canIUse("getUpdateManager")) {       Taro.showModal({         title: "提示",         content: "当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试",         showCancel: false,       });       return;     }     // https://developers.weixin.qq.com/miniprogram/dev/api/base/update/UpdateManager.html     const updateManager = Taro.getUpdateManager();     updateManager.onCheckForUpdate((res) => {       // 请求完新版本信息的回调       if (res.hasUpdate) {         updateManager.onUpdateReady(function () {           Taro.showModal({             title: "更新提示",             content: "新版本已经准备好,是否重启应用?",             success: function (res) {               if (res.confirm) {                 // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启                 updateManager.applyUpdate();               }             },           });         });         updateManager.onUpdateFailed(function () {           // 新版本下载失败           Taro.showModal({             title: "更新提示",             content: "新版本已上线,请删除当前小程序,重新搜索打开",           });         });       }     });   }, }; 复制代码

6.8 Taro 中无法使用 css 变量问题

clipboard6.jpg

解决:

如果是 vue3,就有现成的解决方案,vue3 单文件组件样式特性

// 通过在顶层元素设置 style 来解决 import './style.css'; const style = { '--primary-color': 'red' } return <View style={style} className='container'>container elemet</View> /* styles.css */ .container {   color: var(--primary-color); } 复制代码

6.9 关于 Taro 使用原生 HTML 标签

伪原创工具 SEO网站优化  https://www.237it.com/ 

其实还是建议使用小程序原生标签,不推荐 html 标签,怕掉进坑。

  1. 安装

yarn add @tarojs/plugin-html 复制代码

  1. 配置插件

// config/index.js config = {   // ...   plugins: ['@tarojs/plugin-html'] } 复制代码

  1. 使用

<template>   <div>Hello World!</div> </template>


作者:sRect
链接:https://juejin.cn/post/7035105329956257806


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐