Uniapp笔记(五)uniapp语法4
本章目标
-
授权登录【难点、重点】
-
条件编译【理解】
-
小程序分包【理解】
一、授权登录
我的模块其实是两个组件,一个是登录组件,一个是用户信息组件,根据用户的登录状态判断是否要显示那个组件
1、登录的基本布局
<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方法,获取用户基本信息
<button type="primary" class="btn-login" @click="getUserProfile">一键登录</button>
-
在
methods节点中声明getUserProfile事件处理函数如下
methods: {getUserProfile(){uni.getUserProfile({desc:'用户完善会员信息',success(res) {console.log('res',res.userInfo);}})}
}
3、将用户的基本信息存储到vuex
-
在
store/user.js模块的 state 节点中,声明userinfo的信息对象如下:
state:{userinfo:JSON.parse(uni.getStorageSync('userinfo')||'{}')
}
-
在
store/user.js模块的 mutations 节点中声明updateUserInfo()
mutations:{updateUserInfo(state,userinfo){state.userinfo=userinfouni.setStorageSync('userinfo',JSON.stringify(userinfo))}
}
-
修改methods节点中的
getUserProfile方法,添加调用调用Mutations中的updateUserInfo实现保存数据到vuex中
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
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方法
export default{namespaced:true,state:{token:uni.getStorageSync('token')||''},mutations:{updateToken(state,token){state.token=tokenuni.setStorageSync('token',token)}}
}
-
在页面组件中通过mapMutations将updateToken方法进行映射
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、抽离出自定义组件
<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组件中完成用户的退出登录功能
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是否有效,如果无效直接跳到登录页面
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方法中添加
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条件编译
// #ifdef %PLATFORM%
平台特有的API实现
// #endif
示例,如下代码仅在 App 下出现:
//#ifdef APP_PLUS
plus.push.addEventListener('click',function(msg){var payload = nullvar 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 平台上出现:
//#ifndef H5
uni.scanCode({success:(res)=>{console.log(res.result);}
});
//#endif
除了支持单个平台的条件编译外,还支持多平台同时编译,使用 || 来分隔平台名称。
示例,如下代码会在 App 和 H5 平台上出现:
//#ifdef APP-PLUS || H5
uni.chooseVideo({success:(res)=>{console.log(res.result);}
});
//#endif
4、组件的条件编译
<!-- #ifdef %PLATFORM% -->
平台特有的组件
<!-- #endif -->
示例,如下广告组件仅会在微信小程序中出现:
<!-- #ifdef MP-WEIXIN -->
<ad unit-id="test"></ad>
<!-- #endif -->
5、样式的条件编译
/*#ifdef %PLATFORM% */
平台特有样式
/* #endif */
注意: 样式的条件编译,无论是 css 还是 sass/scss/less/stylus 等预编译语言中,必须使用 /注释/ 的写法。
正确写法
/* #ifdef MP-WEIXIN */
.wx-color{color:#fff000;
}
/* #endif */
错误写法
// #ifdef MP-WEIXIN
.wx-color{color:#fff000
}
// #endif
6、pages.json 的条件编译
下面的页面,只有运行至 App 时才会编译进去。
// #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 在所有平台都会被编译。
┌─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:分包目录下,页面的相对存放路径
"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、配置方法
"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 节点定义分包的预下载 规则,示例代码如下:
"preloadRule": {"pages/contact/contact": {"packages": ["p1"],"network": "wifi"}},相关文章:
Uniapp笔记(五)uniapp语法4
本章目标 授权登录【难点、重点】 条件编译【理解】 小程序分包【理解】 一、授权登录 我的模块其实是两个组件,一个是登录组件,一个是用户信息组件,根据用户的登录状态判断是否要显示那个组件 1、登录的基本布局 <template><…...
编写一个yolov5的模型检测,只要运行后,就不结束,只要有文件放入到文件夹中,就去执行读取
编写一个yolov5的模型检测,只要运行后,就不结束,只要有文件放入到文件夹中,就去执行读取 import os import cv2 import torch from torchvision import transforms from PIL import Image from yolo.model import YOLO…...
vscode调试PHP代码
目录 准备工作ssh的连接以及配置调试 准备工作 1.首先你需要下载一个vscode 2.下载模块 你需要在VScode中去下载我们所需的两个模块PHP Debug以及remote -ssh 3.安装对应版本的xdebug 需要在xdebug的官方去进行分析,选择适合你自己版本的xdebug 去往官方&#x…...
js reverse实现数据的倒序
2023.8.25今天我学习了如何在数组顺序进行倒序排列,如: 原数组为: 我们只需要对数组使用reverse()方法 let demo [{id: 1, name: 一号},{id: 2, name: 二号},{id: 3, name: 三号},]demo.reverse()console.log(demo) 扩展: 当我…...
日常踩坑记录
本篇文章主要介绍一下最近的开发中用到的些小问题。问题不大,但有些小细节,记录一下,有遇到的朋友可以看一下,有更好的解决方法欢迎分享。 浏览器记住密码自动填充表单 这个问题我在火狐浏览器遇到了。我登录系统时选择了浏览器…...
threejs特殊几何体(一:文字几何体对象)
threejs中文字几何体通过newTextGeometry()生成,它被单独作为一个类存在于threejs中const txtGeo new TextGeometry("threejs", { ...opts, font: font }); 我们先看效果: <template><div></div> &…...
链表的实现
本程序List链表用两种方式实现,一种是双向链表,一种是双向循环链表。循环双向链表和双向链表,它们的编码差别很小;但是循环链表在插入效率上胜出很多,同时查询时候更灵活。综合考虑,循环链表是首选。 另外…...
c++ std::mutex与std::condition_variable
1. std::mutex lock()加锁; try_lock()尝试加锁; unlock()解锁; std::mutex m_mutex; m_mutex.lock(); ... m_mutex.unlock(); 2. std::lock_guard 类模板;等同自动锁,直接取代lock()和unlock(); 构造时加锁,析构时解锁; std::mutex m_mutex; {std::lock_guard<std:…...
Aspose.Tasks for .NET V23Crack
Aspose.Tasks for .NET V23Crack 改进了大型项目的内存占用。 添加了API,允许您在应用程序无法访问系统字体文件夹时指定用户的字体文件夹。 Aspose.Tasksfor.NET是处理MicrosoftProject文件的可靠的项目管理API。API支持在不依赖Microsoft Project的情况下读取、写…...
vue过渡及动画
文章目录 前言类名使用自己定义动画样式多个元素过渡使用第三方库 前言 对于vue中的过渡与动画,官网上是这样概述的: Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。包括以下工具: 在 CSS 过渡和动画中自动…...
Linux环境下SVN服务器的搭建与公网访问:使用cpolar端口映射的实现方法
文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…...
【ubuntu】 DNS 设置工具 resolvectl
什么是 resolvectl “resolvectl” 是一个用于管理系统 DNS 解析配置的命令行工具。它是 systemd-resolved 服务的一部分,该服务是在许多基于 Systemd 的 Linux 发行版中用于管理网络配置和 DNS 解析的系统服务。 通过 resolvectl 命令,可以查看当前系…...
Keepalived+Lvs(dr)调度器主备配置小实验
目录 前言 一、实验拓扑图 二、配置LVS(dr)模式 三、配置调配器热备 四、测试 总结 前言 Keepalived和LVS(Linux Virtual Server)是两个常用的开源软件,通常结合使用以提供高可用性和负载均衡的解决方案。 Keepalive…...
第四讲Java基本语法——数组结构(多维数组)
前言 前面几讲,我们讲了Java基本语法,初学者也能够有一定的入门。本讲,我们也是继续来讲解一下Java另一个基础语法——数组,其实在前面讲解数据类型的时候,我们也有提到数组是引用类型,那今天我们就来分析一下什么是数组,怎么用数组呢? 一、数组是什么 数组是…...
【题解】JZOJ6578 / 洛谷P5201[USACO2019Jan]Shortcut G
洛谷 P5201 [USACO19JAN] Shortcut G 题意 在一个带权无向连通图上,每个点有 a i a_i ai 只奶牛,奶牛会走最短路径到 1 1 1,如果有多条路径,选择字典序最小的,定义移动总时间为所有奶牛走到 1 1 1 的时间之和。…...
npm install sentry-cli失败的问题
1. 目前报错 2. 终端运行 npm set ENTRYCLI_CDNURLhttps://cdn.npm.taobao.org/dist/sentry-cli npm set sentrycli_cdnurlhttps://cdn.npm.taobao.org/dist/sentry-cli3. 再安装 npx sentry/wizardlatest -i nextjs即可成功...
Node opensslErrorStack 错误解决方法记录
从Git仓库中下载了一个老项目,使用npm install 安装后没有问题,当我使用npm run dev 的时候遇到了 OpenSSL 相关错误,例如 opensslErrorStack: [error:03000086:digital envelope routines::initialization error] 网上找了一下相关信息&am…...
你知道什么是Grandmillennial风格吗,进来看看吧
如果你既欣赏祖母的印花棉布扶手椅和大胆的图案,又喜欢千禧一代朋友现代家居中的开放空间和时尚家具,那么 "千禧一代 “风格就是为你量身打造的。它借鉴了几十年来的流行趋势,形成了一种独特的、带有现代风格的老式设计。 在典型的 &quo…...
App Inventor 2 开发 ChatGPT 对话App
ChatGPT大家应该不会陌生,它的回答内容非常的专业及深入,具有实际的可指导性。我们通过App Inventor 2开发一个简单的对话App,先看效果: App Inventor 2 ChatGPT教育领域对话演示 代码块如下: 用到的核心组件“ChatBot…...
SQL 大小敏感问题
在SQL中,关键字和函数名 是不区分 大小写的 比如(select、where、order by 、group by update 等关键字),以及函数(ABS、MOD、round、min等) window系统默认是大小写不敏感 (ZEN文件和zen 文件 不能同时存在ÿ…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
