Uniapp笔记(五)uniapp语法4

本章目标

  • 授权登录【难点、重点】

  • 条件编译【理解】

  • 小程序分包【理解】

一、授权登录

我的模块其实是两个组件,一个是登录组件,一个是用户信息组件,根据用户的登录状态判断是否要显示那个组件

1、登录的基本布局

javascript 复制代码
<template>
      <view class="login-container">
        <!-- 提示登录的图标 -->
        <uni-icons type="contact-filled" size="100" color="#AFAFAF"></uni-icons>
        <!-- 登录按钮 -->
        <button type="primary" class="btn-login">一键登录</button>
      </view>
</template>
<style lang="scss">
    .login-container {
      // 登录盒子的样式
      height: 750rpx;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      position: relative;
      overflow: hidden;
      // 登录按钮的样式
      .btn-login {
        width: 90%;
        border-radius: 100px;
        margin: 15px 0;
        background-color: #EA9518;
      }
    }
</style>

2、获取微信用户的基本信息

  • 为登录绑定单击事件,调用getUserProfile方法,获取用户基本信息
javascript 复制代码
<button type="primary" class="btn-login" @click="getUserProfile">一键登录</button>
  • methods 节点中声明getUserProfile事件处理函数如下
javascript 复制代码
methods: {
    getUserProfile(){
        uni.getUserProfile({
            desc:'用户完善会员信息',
            success(res) {
                console.log('res',res.userInfo);
            }
        })
    }
}

3、将用户的基本信息存储到vuex

  • store/user.js 模块的 state 节点中,声明 userinfo 的信息对象如下:
javascript 复制代码
state:{
    userinfo:JSON.parse(uni.getStorageSync('userinfo')||'{}')
}
  • store/user.js 模块的 mutations 节点中声明updateUserInfo()
javascript 复制代码
mutations:{
    updateUserInfo(state,userinfo){
        state.userinfo=userinfo
        uni.setStorageSync('userinfo',JSON.stringify(userinfo))
    }
}
  • 修改methods节点中的getUserProfile方法,添加调用调用Mutations中的updateUserInfo实现保存数据到vuex中
javascript 复制代码
import {createNamespacedHelpers} from 'vuex'
    const {mapMutations}=createNamespacedHelpers('user')
    export default {
        methods: {
            ...mapMutations(['updateUserInfo']),
            getUserProfile(){
                uni.getUserProfile({
                    desc:'用户完善会员信息',
                    success:(res) =>{
                        //将用户基本信息存储到vuex中
                        this.updateUserInfo(res.userInfo)
                          //调用getCode()方法完成code生成
                          this.getToken(res)
                    }
                })
            }
        }
    }

执行如上代码,用户基本信息已经存储到了本地,如下图所示

4、登录获取token

  • 当用户同意授权后,我们可以调用 uni.login() 方法来获取 code
javascript 复制代码
async getToken(info){
    let [err,res]=await uni.login()
    const query = {
        code: res.code,
        userInfo:this.userinfo,
        appId: 'wx2b9aa7f72b4f34e2',
        appSecret: '29c37378cf09ecf6fc44466bf5612ecd'
    }
    uni.request({
        method:"POST",
        url:'http://47.98.128.191:3001/users/wxLogin',
        data:query,
        success: (res) => {
            console.log(res.data.token);
        }
    })          
}

参数说明:

  • code:必传参数,后端需要通过 code 去获取用户的 openid;

  • userInfo:非必选参数,需要根据实际项目需求,看后端是否需要用户信息;

  • appId 和 appSecret:学习阶段需要传递,实际项目中不需要传递,这两个值会在后端直接设置好;

    appId 和 appSecret 在"微信公众平台"中获取

5、将token存储到vuex中

  • user.js模块中state和mutations中分别定义token以及updateToken方法
javascript 复制代码
export default{
    namespaced:true,
    state:{
        token:uni.getStorageSync('token')||''
    },
    mutations:{
        updateToken(state,token){
            state.token=token
            uni.setStorageSync('token',token)
        }
    }
}
  • 在页面组件中通过mapMutations将updateToken方法进行映射
javascript 复制代码
import {createNamespacedHelpers} from 'vuex'
const {mapMutations,mapState}=createNamespacedHelpers('user')
export default{
    methods: {
		...mapMutations(['updateUserInfo','updateToken']),
        async getToken(info){
			let [err,res]=await uni.login()
			const query = {
				code: res.code,
				userInfo:this.userinfo,
				appId: 'wx2b9aa7f72b4f34e2',
				appSecret: '29c37378cf09ecf6fc44466bf5612ecd'
			}
			uni.request({
				method:"POST",
				url:'http://47.98.128.191:3001/users/wxLogin',
				data:query,
				success: (res) => {
					console.log(res.data.token);
					//保存token到vuex中
					this.updateToken(res.data.token)
				}
			})	
		}
    }
}

6、抽离出自定义组件

javascript 复制代码
<template>
	<view>
		<login v-if="!token"></login>
		<userinfo v-else></userinfo>
	</view>
</template>
<script>
	import {createNamespacedHelpers} from 'vuex'
	const {mapState}=createNamespacedHelpers('user')
	import login from '@/components/login/login.vue'
	import userinfo from '@/components/userinfo/userinfo.vue'
	export default{
		components:{login,userinfo},
		computed:{
			...mapState(['token'])
		}
	}
</script>

二、退出登录

在components/userinfo/userinfo组件中完成用户的退出登录功能

javascript 复制代码
import {createNamespacedHelpers} from 'vuex'
const {mapMutations}=createNamespacedHelpers('user')
export default {
	name:"userinfo",
	methods:{
		...mapMutations(['updateToken','updateUserInfo']),
		async logout(){
			const [err, succ] = await uni.showModal({
				title: '提示',
				content: '确认退出登录吗?'
			}).catch(err => err)
			if (succ && succ.confirm) {
				this.updateToken('')
				this.updateUserInfo({})
			}
		}
	}
]}

三、判断用户是否有效

之前的操作只是判断token有或者无,但是并没有完成token是否有效的判断,下面我们可以完成token是否有效的判断,具体步骤如下

  • 首先在user模块的状态机文件中编写actions的方法getUserInfoAsync来判断用户的token是否有效,如果无效直接跳到登录页面
javascript 复制代码
export default{
	namespaced:true,
	state:{},
	mutations:{},
	//完成的是异步操作
	actions:{
		/*
			vuex中的actions选项中的方法是异步方法
			该方法的参数有两个
			第1个参数是vuex状态机的上下文
			第2个参数是payload是参数
		*/
		getUserInfoAsync(context,payload){
			//向后端接口发送请求
			console.log('token信息',context.state.token);
			uni.request({
				url:'http://47.98.128.191:3001/users/getUserInfo',
				header:{
					'Authorization':context.state.token
				},
				success: (res) => {
					console.log('-----------成功----------');
					console.log('res',res.data.code);
					if(res.data.code==0){
						console.log('999999999999999999');
						uni.showToast({
							title:'token已经过期请重新登录',
							success() {
								//调用mutations中的updateUserInfo和updateToken方法
								context.commit('updateUserInfo',{})
								context.commit('updateToken','')
								//跳转到我的页面中来
								uni.switchTab({
									url:'/pages/mine/index'
								})
							}
						})
						
					}
				},
				fail: (err) => {
					console.log('-----------失败----------');
				}
			})
		}
	}
}
  • 在需要认证的页面组件的onShow方法中调用状态机中的这个方法

比如mine模块中和购物车模块中有需要,则在onShow方法中添加

javascript 复制代码
import {createNamespacedHelpers} from 'vuex'
const {mapActions:mapUserActions}=createNamespacedHelpers('user')
export default {
		methods: {
			...mapUserActions(['getUserInfoAsync']),
		},
		//当页面组件显示在前台的时候调用
		onShow() {
			console.log('-------购物车模块的onShow---------');
			this.getUserInfoAsync()
		}
}

四、条件预编译

注意:在HBuilder中生成条件编译的代码的快捷键是CTRL+ALT+/

1、跨端兼容

uniapp已将常用的组件、JS API 封装到框架中,开发者按照 uni-app 规范开发即可保证多平台兼容,大部分业务均可直接满足。

但每个平台有自己的一些特性,因此会存在一些无法跨平台的情况。

  • 大量写 if else,会造成代码执行性能低下和管理混乱。

  • 编译到不同的工程后二次修改,会让后续升级变的很麻烦。

在 C 语言中,通过 #ifdef、#ifndef 的方式,为 windows、mac 等不同 os 编译不同的代码。 uni-app 参考这个思路,为 uni-app 提供了条件编译手段,在一个工程里优雅的完成了平台个性化实现。

2、条件编译

条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。

写法:以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。

  • #ifdef:if defined 仅在某平台存在

  • #ifndef:if not defined 除了某平台均存在

  • %PLATFORM%:平台名称

条件编译写法 说明
#ifdef APP-PLUS 需条件编译的代码 #endif 仅出现在 App 平台下的代码
#ifndef H5 需条件编译的代码 #endif 除了 H5 平台,其它平台均存在的代码
#ifdef H5 || MP-WEIXIN 需条件编译的代码 #endif 在 H5 平台或微信小程序平台存在的代码(这里只有||,不可能出现&&,因为没有交集)

%PLATFORM% 可取值如下:

平台
APP-PLUS App
APP-PLUS-NVUE App nvue
H5 H5
MP-WEIXIN 微信小程序
MP-ALIPAY 支付宝小程序
MP-BAIDU 百度小程序
MP-TOUTIAO 字节跳动小程序
MP-QQ QQ小程序
MP 微信小程序/支付宝小程序/百度小程序/字节跳动小程序/QQ小程序

支持的文件

  • .vue

  • .js

  • .css

  • pages.json

  • 各预编译语言文件,如:.scss、.less、.stylus、.ts、.pug

3、API条件编译

javascript 复制代码
// #ifdef %PLATFORM%
平台特有的API实现
// #endif

示例,如下代码仅在 App 下出现:

javascript 复制代码
//#ifdef APP_PLUS
plus.push.addEventListener('click',function(msg){
    var payload = null
    var action = '';
    if(msg.payload){
        if(typeof msg.payload === 'string'){
            payload = JSON.parse(msg.payload)
        }
        action = payload.action;
        if(action === 'open'){
            plus.webview.open(payload.url)
        }
    }
})
//#endif

示例,如下代码不会在 H5 平台上出现:

javascript 复制代码
//#ifndef H5
uni.scanCode({
    success:(res)=>{
        console.log(res.result);
    }
});
//#endif

除了支持单个平台的条件编译外,还支持多平台同时编译,使用 || 来分隔平台名称。

示例,如下代码会在 App 和 H5 平台上出现:

javascript 复制代码
//#ifdef APP-PLUS || H5
uni.chooseVideo({
    success:(res)=>{
        console.log(res.result);
    }
});
//#endif

4、组件的条件编译

javascript 复制代码
<!--  #ifdef  %PLATFORM% -->
平台特有的组件
<!--  #endif -->

示例,如下广告组件仅会在微信小程序中出现:

javascript 复制代码
<!-- #ifdef MP-WEIXIN -->
<ad unit-id="test"></ad>
<!-- #endif -->

5、样式的条件编译

javascript 复制代码
/*#ifdef  %PLATFORM% */
平台特有样式
/* #endif */

注意: 样式的条件编译,无论是 css 还是 sass/scss/less/stylus 等预编译语言中,必须使用 /注释/ 的写法。

正确写法

javascript 复制代码
/* #ifdef MP-WEIXIN */
.wx-color{
    color:#fff000;
}
/* #endif */

错误写法

javascript 复制代码
// #ifdef MP-WEIXIN
.wx-color{
    color:#fff000
}
// #endif

6、pages.json 的条件编译

下面的页面,只有运行至 App 时才会编译进去。

javascript 复制代码
// #ifdef APP-PLUS
{
    "path":"pages/api/speech/speech",
    "style":{
        "navigationBarTitleText":"语音识别"
    }
}
//#endif

不同平台下的特有功能,以及小程序平台的分包,都可以通过 pages.json 的条件编译来更好地实现。这样,就不会在其它平台产生多余的资源,进而减小包体积。

json的条件编译,如不同平台的key名称相同,cli项目下开发者自己安装的校验器会报错,需自行关闭这些校验器对json相同key的校验规则。如果使用HBuilderX的校验器,无需在意此问题,HBuilderX的语法校验器为此优化过

7、static 目录的条件编译

在不同平台,引用的静态资源可能也存在差异,通过 static 的的条件编译可以解决此问题,static 目录下新建不同平台的专有目录(目录名称同 %PLATFORM% 值域,但字母均为小写),专有目录下的静态资源只有在特定平台才会编译进去。

如以下目录结构,a.png 只有在微信小程序平台才会编译进去,b.png 在所有平台都会被编译。

javascript 复制代码
┌─static                
│  ├─mp-weixin
│  │  └─a.png     
│  └─b.png
├─main.js        
├─App.vue      
├─manifest.json 
└─pages.json  

五、小程序分包

1、分包概述

1.1、什么是分包

分包是指将一个完整的小程序项目,按照需求划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。

1.2、分包的好处
  • 可以优化小程序首次启动的下载时间

    • 分包前,小程序项目中所有的页面和资源都被打包到了一起,导致真个项目体积过大,影响小程序首次启动的下载时间

    • 分包后,小程序项目由1个主包+多个分包组成

      • 主包:一般只包含项目的启动页面或Tabbar页面,以及所有分包都需要用到的一些公共资源

      • 分包:只包含和当前分包有关的页面和私有资源

  • 在多团队共同开发时可以更好的解耦协作

1.3、分包的加载规则
  • 在小程序启动时,默认会下载主包并启动主包内页面

    • tabBar页面需要放在主包中
  • 当用户进入分包内某个页面时,客户端会把对应分包下载下来,下载完成后再进行展示

    • 非tabBar页面可以按照功能的不同,划分为不同的分包之后,进行按需下载
1.4、分包的体积限制

目前,小程序分包的大小有以下两个限制

  • 整个小程序所有分包大小不超过16M(主包+所有分包)

  • 单个分包/主包大小不能超过2M

2、分包的基本用法

2.1、配置步骤

在pages.json中配置subPackages实现分包,下面对象的常见属性

  • root:分包的根目录

  • name:分包的别名

  • pages:分包目录下,页面的相对存放路径

javascript 复制代码
  "subPackages": [
        {
            "root": "productPkg",
            "name": "product",
            "pages": [
                "pages/product/product-list",
                "pages/product/product-detail"
            ]
        },
        {
            "root": "payPkg",
            "name": "pay",
            "pages": [
                "pages/pay/apy-address",
                "pages/pay/apy-detail"
            ]
        }
    ],
2.2、应用原则
  • 主包无法引用分包内的私有资源

  • 分包之间不能相互引用私有资源

  • 分包可以引用主包内的公共资源

3、独立分包

3.1、什么是独立分包

独立分包本质上也是分包,只不过它比较特殊,可以独立于主包和其他分包而单独运行。

3.2、普通分包和独立分包的区别

最主要的区别:是否依赖于主包才能运行

  • 普通分包必须依赖于主包才能运行

  • 独立分包可以在不下载主包的情况下,独立运行

3.3、独立分包的应用场景
  • 当小程序从普通的分包页面启动时,需要首先下载主包

  • 而独立分包不依赖主包即可运行,可以很大程度上提升分包页面的启动速度

3.4、配置方法
javascript 复制代码
  "subPackages": [
        {
            "root": "productPkg",
            "name": "product",
            "pages": [
                "pages/product/product-list",
                "pages/product/product-detail"
            ]
        },
        {
            "root": "payPkg",
            "name": "pay",
            "pages": [
                "pages/pay/apy-address",
                "pages/pay/apy-detail"
            ],
            "independent":true
        }
    ],
3.5、引用原则

独立分包和普通分包以及主包之间,是相互隔绝的,不能相互引用彼此的资源!例如:

  • 主包无法引用独立分包内的私有资源

  • 独立分包之间,不能相互引用私有资源

  • 独立分包和普通分包之间,不能相互引用私有资源

  • 特别注意:独立分包中不能引用主包内的公共资源

4、分包预下载

4.1、什么是分包预下载

分包预下载指的是:在进入小程序的某个页面时,由框架自动预下载可能需要的分包,从而提升进入后续分包页面时的启动速度。

4.2、配置分包预下载

预下载分包的行为,会在进入指定的页面时触发。在 app.json 中,使用 preloadRule 节点定义分包的预下载 规则,示例代码如下:

javascript 复制代码
 "preloadRule": {
    "pages/contact/contact": {
      "packages": [
        "p1"
      ],
      "network": "wifi"
    }
  },
相关推荐
前端大卫26 分钟前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘42 分钟前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare43 分钟前
浅浅看一下设计模式
前端
Lee川1 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix1 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人1 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人2 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼2 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端