向阳网络科技 發表於 2023-1-13 09:18:00

uniapp 开发微信小程序问题笔记

<p>最近接手了一个小程序开发,从头开始。使用了 uniapp 搭建,以前没有做过小程序开发,着手看文档、查文档。一步一步完成了任务的开发。特此记录开发过程中的问题。</p>
<p>开发建议:</p>
<ul>
<li>使用 HBuilderX 工具进行开发。通过工具创建项目</li>
<li>遇到原来的 vue 写法怎么不生效,别犹豫去看文档,可能就是不支持。</li>
<li>有时间熟读文档。</li>
<li>跨端最大的问题就是兼容性。</li>
</ul>
<h2 id="1-不能采用全局注册的方式注册组件">1. 不能采用全局注册的方式注册组件</h2>
<p>通常在 components 目录会存放项目中共用的组件,然后暴露<code>install</code>,全局引用安装</p>
<pre><code class="language-js">// components/index.js

// 各种组件
import Page from "./page.vue";

//
const Components = ;
export default {
install: (app) =&gt; {
    // 注册
    Components.forEach((component) =&gt; app.component(component.name, component));
},
};

// 然后在 main.js 引用注册
import Vue from "vue";
import Component from "@/components";

// 注册 就可以全局使用了。
Vue.use(Component);
</code></pre>
<p>但是在 uniapp 中不生效,有两种方式实现组件注册:</p>
<h3 id="11-导入到-mainjs-中进行注册">1.1 导入到 main.js 中进行注册</h3>
<p>所有的组件直接导入到 mian.js 中,通过 Vue 对象注册即可在其他页面中引用。</p>
<p>要注意的是,注册的组件名必须是字符串,不能是<code>page.name</code></p>
<pre><code class="language-js">import Vue from "vue";
import Page from "@/components/page.vue";

Vue.component("page", page);
</code></pre>
<h3 id="12-特有的-easycom-模式注册组件">1.2 特有的 easycom 模式注册组件</h3>
<p>只要符合<code>components/组件名称/组件名称.vue</code> 文件存储路径,则不需要手动注册,可直接引用组件。</p>
<p>而且这有助于打包时剔除掉没有使用到的组件。</p>
<p>那现在我们的功能组件存放路径改为<code>components/page/page.vue</code> ,即可全局使用。</p>
<p>也可以在<code>pages.json</code> 配置自定义设置,定义哪些匹配的组件。</p>
<pre><code class="language-json">{
"easycom": {
    "autoscan": true,
    "custom": {
      // uni-ui 规则如下配置
      "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
    }
}
}
</code></pre>
<blockquote>
<p>新加的组件注意清除缓存,重新运行生效。</p>
</blockquote>
<h2 id="2-小程序真机调试包太大无法上传分包处理">2. 小程序真机调试包太大,无法上传,分包处理</h2>
<p>由于开始项目,所有的 UI 设计图都放在了前端,导致编译后整个包大小超过了 4M。开发时并没有提示这个问题,准备真机测试时,提示无法上传。就去找解决方案。</p>
<p>在采用方案之前耗费了好多时间去手动删除一些不用的文件、删除不用的代码。再一看无济于事,看来代码并占不了多少。</p>
<h3 id="21-将所有静态资源都存放到远程服务器上">2.1 将所有静态资源都存放到远程服务器上</h3>
<h3 id="22-采用分包的方式将主包的体积降下来">2.2 采用分包的方式,将主包的体积降下来</h3>
<p>分包的方式,刚开始的时候所有的文件都是放在<code>pages</code>下的,<code>pages.jon</code>中 也都配置在 <code>pages</code>中。</p>
<p>需要将初始加载无关的模块拆出来,同 pages 同级目录下。</p>
<pre><code class="language-js">|———————————————————————————————————— 项目
|—————————————— integral
|——————— rank
|——————— log
|——————— mall
|—————————————— pages
|——————— index
|——————— login
|——————— user
|—————————————— pages.json

</code></pre>
<p>在<code>pages.jong</code> 通过属性<code>subPackages</code> 配置分包编译。</p>
<p>root 为分包的主目录,pages 目录下的页面,<code>path</code>为相对路径。</p>
<pre><code class="language-json">{
"pages": [
    {
      "path": "pages/index/index",
      "style": {
      "navigationBarTitleText": "首页"
      }
    },
    {
      "path": "pages/login/login",
      "style": {
      "navigationBarTitleText": "登录"
      }
    }
    // ...
],
"subPackages": [
    {
      "root": "integral",
      "pages": [
      {
          "path": "rank/rank",
          "style": {
            "navigationBarTitleText": "排行榜"
          }
      }
      // ...
      ]
    }
]
}
</code></pre>
<p>然后重新运行,再次真机调试,没有此前的包过大的提示。顺利打开小程序。</p>
<p>分包之后,检查此前已写好的模块之间的跳转。页面路径已经变化,</p>
<p>可以通过预加载的方式帮助我们在进入某个页面时,由框架自动预下载可能需要的分包,提高后续页面进入的启动速度。</p>
<p>以下配置,帮助我们在进入首页后,加载全部的分包。也可以指定加载分包的某个页面,具体查看官网preloadrule</p>
<pre><code class="language-json">{
"preloadRule": {
    "pages/index/index": {
      "network": "all",
      "packages": ["__APP__"]
    }
}
}
</code></pre>
<h2 id="3-内置组件不能绑定-class-的问题">3. 内置组件不能绑定 class 的问题</h2>
<p>在使用扩展的 UI 组件,比如 uni-ui、uView 等给这些组件绑定 class 时,渲染并不能渲染成功。</p>
<p>只能在给他们包一层<code>view</code> 自定义 calss 。避免全局的样式污染。</p>
<h2 id="4-定位-api-调用需要增加授权配置manifestjson">4. 定位 API 调用,需要增加授权配置<code>manifest.json</code></h2>
<p>增加配置,允许小程序调用位置的权限接口。然后通过<code>requiredPrivateInfos</code>定义你需要哪些方法。</p>
<pre><code class="language-json">{
/* 小程序特有相关 */
"mp-weixin": {
    // ...
    "permission": {
      "scope.userLocation": {
      "desc": "请求获取地理信息"
      }
    },
    "requiredPrivateInfos": [
      "getLocation",
      "chooseAddress",
      "choosePoi",
      "chooseLocation"
    ]
}
}
</code></pre>
<p>如果我们如果想在小程序中功能使用定位功能,则需要申请高德、腾讯或其他地图的 SDK ,通过拿到的经纬度查询详细地址信息。</p>
<h3 id="unigetlocationoptions获取当前地理位置">uni.getLocation(options)获取当前地理位置。</h3>
<p>不能获取到地址中文描述,只能拿到经纬度等其他参数,可以在通过第三方地图服务,获取详细的位置信息。</p>
<blockquote>
<p>address 地址信息仅 APP 端支持。</p>
</blockquote>
<h3 id="unichooselocationoptions-提供给用户选择位置信息">uni.chooseLocation(options) 提供给用户选择位置信息</h3>
<p>可以拿到经纬度、位置名称以及详细的 address 中文描述。</p>
<h2 id="5-主包不能引用分包的组件">5. 主包不能引用分包的组件</h2>
<p>通过分包后,降低了主包的大小。但也出现了一个问题就是主包不能复用分包的组件,很容易理解就是访问主包的页面时,分包还不一定加载。</p>
<h2 id="6-小程序不支持-attrs--listeners">6. 小程序不支持 <code>$attrs \ $listeners</code></h2>
<p>通过对组件进行二次封装、三次封装。底层组件定义的属性、事件如果每层都定义接收,就很麻烦,</p>
<p>在 vue 中,通过属性<code>inheritAttrs: false</code> 不然根元素承载这些属性、时间,然后通过<code>$attrs \ $listeners</code> 绑定到目标元素上。</p>
<pre><code class="language-vue">&lt;template&gt;
&lt;view class="dictionary-picker"&gt;
    &lt;data-picker v-bind="$attrs" v-on="$listeners"&gt;&lt;/data-picker&gt;
&lt;/view&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
inheritAttrs: false,
};
&lt;/script&gt;
</code></pre>
<p>在 uniapp 中,则是不可以的。他没有 <code>$attrs \ $listeners</code> 这两个属性。</p>
<h2 id="7-不支持自定义双向绑定model">7. 不支持自定义双向绑定<code>model</code></h2>
<p>在 vue 中定义一个组件的输入、输出通过绑定值、然后监听抛出事件处理逻辑,</p>
<pre><code class="language-vue">&lt;script&gt;
export default {
name:'data-picker',
model:{
    prop:'value',
    event:'change',
},
methods:{
    handleChange(val){

      this.$emit('change',val)
    }
}
}
</code></pre>
<p>这样在调用组件时可通过<code>v-model</code>绑定。<code>&lt;data-picker v-model='value' /&gt;</code></p>
<p>在 uniapp 中,则是不可以的。 小程序不支持</p>
<h2 id="8-不能绑定给-styleclass-对象">8. 不能绑定给 style、class 对象</h2>
<p>小程序端不支持绑定对象给 class、style</p>
<pre><code class="language-vue">&lt;template&gt;
&lt;view class="dictionary-picker"&gt;
    &lt;view :class="boxStyle"&gt;&lt;/view&gt;
&lt;/view&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
computed: {
    boxStyle() {
      return {
      active: this.activeTab ? true : false,
      };
    },
},
};
&lt;/script&gt;
</code></pre>
<p>这样是不行的,渲染后的元素节点显示 object 。 不通过计算属性,直接在属性上绑定逻辑。</p>
<pre><code class="language-vue">&lt;template&gt;
&lt;view class="dictionary-picker"&gt;
    &lt;view :class=""&gt;&lt;/view&gt;
&lt;/view&gt;
&lt;/template&gt;
</code></pre>
<h2 id="9-适配-iphone-底部刘海">9. 适配 iphone 底部刘海</h2>
<p>在需要适配的页面,元素增加样式。特有的变量<code>safe-area-inset-bottom \ safe-area-inset-bottom</code></p>
<blockquote>
<p>建议这种通用性设置,提供一个基础公共组件<code>page</code>. 通过定义插槽 <code>nav \ header \ main \ footer</code> 插入内容。这是一些公共的样式就不用每个页面去设置。</p>
</blockquote>
<pre><code class="language-less">.footer {
padding-bottom: 0;
// IOS &lt;11.2
padding-bottom: constant(safe-area-inset-bottom);
// iso &gt;11.2
padding-bottom: env(safe-area-inset-bottom);
// 安全距离设置后,一定要设置背景,不然滚动的内容在下方可以看到
background-color: #fff;
}
</code></pre>
<h2 id="10-一些全局的配置色彩字号-注意单位格式">10. 一些全局的配置色彩,字号 注意单位、格式。</h2>
<p>在配置<code>pages.json</code>的一些设置,颜色都是必须是十六进制颜色,不能是 rgb 或 rgba</p>
<ul>
<li>
<p><code>globalStyle.navigationBarBackgroundColor</code> 导航栏背景颜色</p>
</li>
<li>
<p><code>globalStyle.backgroundColor</code> 下拉显示出来的窗口的背景色</p>
</li>
<li>
<p><code>tabBar.color</code> tab 上的文字默认颜色</p>
</li>
<li>
<p><code>tabBar.backgroundColor</code> tab 的背景色</p>
</li>
<li>
<p><code>tabBar.fontSize</code> 文字默认大小</p>
</li>
<li>
<p><code>tabBar.iconWidth</code> 图标默认宽度(高度等比例缩放)</p>
</li>
<li>
<p>...</p>
</li>
</ul>
<h2 id="11-自定义手机顶部的导航栏">11. 自定义手机顶部的导航栏。</h2>
<p>通常手机顶部信号、电量等一些状态占据手机的部分位子。想让这部分区域也融入到我们的程序中。</p>
<p>配置<code>pages.jons</code></p>
<pre><code class="language-json">{
"globalStyle": {
    "navigationStyle": "custom"
}
}
</code></pre>
<p>设置自定义导航样式,这样你的页面会以手机屏幕的最顶端开始。顶部标题、返回都需要自己去定义了。</p>
<h2 id="12-在视图中不能直接使用绑定在vueprototype上的全局变量">12. 在视图中不能直接使用绑定在<code>Vue.prototype</code>上的全局变量</h2>
<p>直接在视图使用<code>Vue.prototype</code>上的变量是访问不到的。向下面这样:</p>
<pre><code class="language-vue">&lt;template&gt;
&lt;view&gt;
    &lt;img :src="$baseUrl + data.imgUrl" /&gt;
&lt;/view&gt;
&lt;/template&gt;
</code></pre>
<p>可以通过计算属性,或者提供一个变量值.</p>
<pre><code class="language-vue">&lt;template&gt;
&lt;view&gt;
    &lt;img :src="avatarUrl" /&gt;
&lt;/view&gt;
&lt;/template&gt;
&lt;script&gt;

export default {
        data() {
    return {
      //
      data:{}
    }
},
computed: {
                avatarUrl() {
                        return this.$baseUrl + '/upload' + this.data.imgUrl;
                }
        },
</code></pre>
<h2 id="13-二次封装-uni-ui-组件更改的组件样式未生效">13. 二次封装 uni-ui 组件,更改的组件样式未生效。</h2>
<p>微信小程序里的组件之间的样式隔离,只需要增加选项配置,</p>
<pre><code class="language-vue">&lt;script&gt;
export default {
        name: 'confirmDialog',
        options: {
                styleIsolation: 'shared' // 解除样式隔离
        },
}
</code></pre>
<p>微信官方文档说明 - 样式隔离</p>
<h2 id="14-使用微信小程序插件-plugin">14. 使用微信小程序插件 plugin</h2>
<p>主要是小程序插件 plugin 的开发文档,在<code>manifest.json</code> 中配置</p>
<pre><code class="language-json">{
"mp-weixin": {
    "appid": "",
    "usingComponents": true,
    "plugins": {
      "myPlugin": {
      "version": "版本号",
      "provider": "wx8**********75"
      }
    }
}
}
</code></pre>
<h2 id="141-在这边引入的插件可全局引用在需要引入的页面中配置pagesjson">14.1 在这边引入的插件可全局引用,在需要引入的页面中配置,<code>pages.json</code></h2>
<pre><code class="language-json">{
"pages": [
    {
      "path": "pages/login/login",
      "style": {
      "navigationBarTitleText": "登录",
      "enablePullDownRefresh": false,
      // 定义微信插件
      "mp-weixin": {
          "usingComponents": {
            "login": "plugin://myPlugin/login"
          }
      }
      }
    }
]
}
</code></pre>
<p>然后在页面 login.vue 中使用组件</p>
<pre><code class="language-vue">&lt;template&gt;
        &lt;view class="login-box"&gt;
                &lt;login
                        :config="loginConfig"
                &gt;&lt;/login&gt;
&lt;/template&gt;
</code></pre>
<h2 id="142-分包引入插件只能在分包中使用">14.2 分包引入插件,只能在分包中使用</h2>
<p>在<code>pages.json</code>配置,同一个插件不能被多个分包引入,直接放入主包中配置。</p>
<pre><code class="language-json">{
"subPackages": [
    {
      "root": "integral",
      "pages": [
      {
          "path": "integralMall/list"
      }
      ],
      "plugins": {
      "pluginName": {
          "version": "版本号",
          "provider": "wx8**********75"
      }
      }
    }
]
}
</code></pre>
<h2 id="15-拦截器uniaddinterceptorapioptions">15. 拦截器<code>uni.addInterceptor(API,Options)</code></h2>
<p>对于 uni 可调用的全局 API,可通过拦截器来批量处理逻辑,常用就是拦截请求,控制权限。</p>
<p>在<code>App.vue</code> 中调用拦截,定义拦截器在未注册时控制某些页面不可访问。</p>
<pre><code class="language-js">import { Not_register_access_page } from "@/utils/config.js";

export default {
onLaunch: function () {
    // 跳转拦截
    uni.addInterceptor("navigateTo", {
      invoke(config) {
      if (
          !$this.isRegister &amp;&amp;
          !Not_register_access_page.includes(config.url)
      ) {
          // 不允许访问,则将前置访问的page地址置空
          config.url = "";
      }
      },
    });
},
};
</code></pre>
<p>拦截器<code>options</code>参数</p>
<ul>
<li><code>invoke</code> 拦截前触发</li>
<li><code>success</code> 成功回调触发</li>
<li><code>fail</code> 失败回调触发</li>
<li><code>complete</code> 完成回调后触发</li>
</ul>
<p><code>uni.removeInterceptor(API)</code>删除拦截器</p>


</div>
<div id="MySignature" role="contentinfo">
    追逐的不应该是梦想,随心所欲,随遇而安!<br><br>
来源:https://www.cnblogs.com/dreamHot/p/17048547.html
頁: [1]
查看完整版本: uniapp 开发微信小程序问题笔记