微信小程序项目问题及微信小程序面试题
微信小程序项目开发问题微信小程序面试题
微信小程序
1、小程序与h5的区别
简单来说,小程序是一种应用,运行的环境是微信(App);H5是一种技术,依附的外壳是是浏览器。
1.运行环境的不同
H5的运行环境是浏览器,包括webview,而微信小程序的运行环境并非完整的浏览器,因为小程序的开发过程中只用到一部分H5技术。
小程序的运行环境是微信开发团队基于浏览器内核完全重构的一个内置解析器,针对性做了优化,配合自己定义的开发语言标准,提升了小程序 的性能。
2.系统权限
这里的系统权限,可以理解为隐私级别比较高的,如通讯录,或能调用硬件的,比如蓝牙功能等。从这个角度看,H5 本身可以说几乎是没有什么 系统权限的。虽然也有摄像头之类的接口,但是重度依赖浏览器能力,兼容性有限。
而小程序,由于依赖微信客户端本身,所以微信小程序团队将客户端的很多能力开放给了小程序环境,当然,前提是你给微信也授权了相关的能 力,比如允许访问麦克风,允许访问相册等。
所以,如果你的产品重度依赖这些能力,那小程序一定是不二之选,因为 H5 很难做到这些,对于很多小程序提供的能力,H5 是根本没有可能实 现的。
3.开发成本
H5 的开发,涉及开发工具(vscode、Atom等)、前端框架(Angular、react等)、模块管理工具(Webpack 、Browserify 等)、任务管理工 具(Grunt、Gulp等),还有UI库选择、接口调用工具(ajax、Fetch Api等)、浏览器兼容性等等。尽管这些工具可定制化非常高,大部分开发者也 有自己的配置模板,但对于项目中各种外部库的版本迭代、版本升级,这些成本加在一起那就是个不小数目了。
而开发一个微信小程序,由于微信团队提供了开发者工具,并且规范了开发标准,则简单得多。前端常见的HTML、CSS变成了微信自定义的 WXML、WXSS,WXML,官方文档中都有明确的使用介绍,开发者按照说明专注写程序就可以了。需要调用后端接口时,调用发起请求API;需要上传 下载时,调用上传下载API;需要数据缓存时,调用本地存储API;引入地图、使用罗盘、调用支付、调用扫码等等功能都可以直接使用;UI库方面,框 架带有自家weui库加成。并且在使用这些API时,不用考虑浏览器兼容性,不用担心出现BUG,显而易见微信小程序的开发成本相对低很多。
4.运行流畅度的不同
在运行流畅度方面,无论对于用户还是开发者,都可以直观体验出两者的差异。这也是普通大众最容易区分小程序与H5的一点。打开H5,实际上 是打开一个网页,而网页需要在浏览器中渲染。所以加载这一过程,会给人明显的「卡顿」感觉,面对复杂的业务逻辑或者丰富的页面交互时尤 为明显。
而微信小程序,它的代码直接在微信上运行,省去了通过浏览器渲染的步骤,因此,在微信中使用小程序,才会比H5流畅很多。除了首次打开需 要几秒的加载时间外,小程序各个页面的切换、跳转等体验已经媲美原生App,有着同样的柔丝般顺滑的效果。
5.迭代周期
开发成本低,未必迭代周期就短。对于 H5 我们可以随时发布上线,不用受任何牵制。而小程序的特点,就是每次提交版本都要经过微信方面的审 核,且审核时间的长短很随机,着急上线的项目就很无奈了。
至于其他速度,取决于开发人员技能熟练程度,系统复杂度,对基础能力的依赖等,就不好估算了。
6.访问入口
在访问入口这个点上,H5 的核心竞争力就是能在微信之外玩,不依赖微信本身。而小程序的优势,就是有 50+ 微信提供的场景入口,并且聊天界 面顶部的“最近使用”和“我的小程序”这个入口,相对 H5 来说是有绝对优势的。
用户关闭之后,H5 页面如果想继续访问,可能会通过收藏入口,或者转发给“文件传输助手”等聊天界面保存,还可以缩小到图标稍后阅读等等。 本质上还是跟 PC 时代的浏览器收藏夹差不多,需要有个地方把 H5 的链接地址保存下来,方便下次访问。如果没有保存,下次就很难找到了。
至于微信内的搜索,是可以同时搜索 H5 和小程序的,可以根据 H5 的名字和内容、小程序的名字和介绍来搜索。这里 H5 有个天然优势就是,只 要你的链接在各大搜索引擎提交过,那么使用其他的搜索引擎也能搜出这个 H5,比如百度搜索。
7.外部限制
由于小程序依赖微信平台,因此微信平台要对内容安全等事项负责,比如你想搞个有 UGC 的产品,用 H5 可能还可以趁着监管宽松无证裸奔一 阵,或者说做大了再补证。
而小程序,就很可能完全不能过审,根本上不了线。比如试听类,社交类,都有对应的资质,而这个资质还可能很难获得。
类似的,H5 页面可以不用搞 HTTPS,有个网站就能玩,甚至用工具做个小活动也都可以玩。但是小程序,从后端开始就有限制,要求域名备案 +HTTPS,一定程度上也是一点成本。
此外,小程序对文件大小也有限制,虽然现在已经支持分包加载,但是在文件大小方面,H5 本身是没有什么限制的。只是实际开发的时候,要照 顾用户的体验,不能让页面打开太慢。
优势:
1)容易上手,只要之前有HTML+CSS+JS基础知识,写小程序基本上没有大问题;当然 如果了解ES6+CSS3则完全可以编写出即精简又动感的小程序
2)基本上不需要考虑兼容性问题,只要微信可以正常运行的机器,就可以运行小程序;
3)基本组件库已经比较齐全:Toast,Loading框,Picker,定位及地图,Image,Input,Checkbox,Text,TextArea,ScrollView等常用的组件都有,而且使用也挺 简单、方便;
4)发布、审核高效,基本上上午发布审核,下午就审核通过,升级简单,而且支持灰度发布;
5 ) 微信官方提供使用人数、频率等数据统计,小程序js脚本执行错误日志;
6)开发文档比较完善,开发社区比较活跃;
7)最近刚开放的牛x功能,新增webview组件,可以展示网页啦,这个比较爽;
8)支持插件式开发,一些基本功能可以开发成插件,供多个小程序调用;
劣势:
1)后台调试麻烦,因为API接口必须https请求,且公网地址,也就是说后台代码必须发布到远程服务器上;当然我们可以修改host进行dns映射把远程
服务器转到本地,或者开启tomcat远程调试;不管怎么说终归调试比较麻烦。
2)前台测试有诸多坑,最头疼莫过于模拟器与真机显示不一致
3)真机测试,个别功能安卓和苹果表现迥异,我们的小程序里有很多页面有定位功能,模拟器和iphone定位瞬间完成,然而安卓手机就蛋疼了,老显
示“定位中…”要很久才能定位好。后来没办法只能优化,减少定位次数。
4)native组件,展示很不好,比如textarea,不能在滚动页面出现,而且至于顶层,经常其它组件会被它遮挡,点击其它组件时,就进入textarea输入
框;画布组件也是如此;
5)页面跳转深度不能超过5个页面,这个比较麻烦,有些复杂的页面跳转没法实现,不过太复杂的话也有悖小程序简单易用的原则啦;
6)小程序升级问题,官方文档说会自动更新,实际情况往往是要先把原来的小程序删除掉,重新搜索添加,才能加载最新版本;
7)页面渲染稳定性有待提高,已经好几次出现部分用户的页面显示异常,整个页面被放大了好几倍,先删除原来小程序再添加回来,如此重复好几次,
才能显示正常;
8)js引用只能使用绝对路径,很蛋疼;基于安全性及MINA框架实现原理,小程序中对js使用做了很多限制,不能使用:new Function,eval, Generator,不能操作cookie,不能操作DOM;
9)开发工具bug比较多且效率比较低,三天两头升级,解决老问题的同时又出现问题;文件查找、资源定位、代码编辑较eclipse有一定差距。经常出现
把a.js当做b.js来修改
2、app.json配置,app.js作用
app.json:
app.json 用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等
pages:设置页面路径
window:设置默认页面的窗口表现
tabBar:设置底部tab的表现
networkTimeout:设置网络超时时间
debug:设置是否开启debug模式
app.js:
app.js文件是比较特殊的,它是微信小程序的入口文件,掌控整个小程序的生命周期,同时有一些全局的属性、变量也存放在这个文件中。
3、布局弹性盒子单位rpx
小程序设置rpx的目的:为了内容的高宽会随着机型变化(型号、分辨率的不同)会有一个自动的适应
尺寸单位
rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,
屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
设备 rpx换算px (屏幕宽度/750) px换算rpx (750/屏幕宽度)
iPhone5 1rpx = 0.42px 1px = 2.34rpx
iPhone6 1rpx = 0.5px 1px = 2rpx
iPhone6 Plus 1rpx = 0.552px 1px = 1.81rpx
建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况。
4、 生命周期
小程序生命周期:
onLaunch:小程序初始化完成(全局只触发一次)
onShow:小程序启动,或从后台切入前台时触发
onHide:小程序从前台切入后台时调用
onError:小程序发生脚本错误,或者api调用失败时触发,会带上错误信息
onPageNotFound:小程序要打开的页面不存在时触发,会带上页面信息回调该函数
页面的生命周期:
onLoad:页面加载时触发
onShow:页面显示时触发
onReady:页面初次渲染完成触发
onHide:页面隐藏时触发
onUnload:页面卸载时触发
页面执行时的钩子:
onPullDownRefresh:监听用户下拉动作
onReachBottom:页面上拉触底事件
onShareAppMessage:用户点击右上角转发
onPageScroll:页面滚动触发事件
onTabItemTap:当前是Tab页,点击Tab时触发
5、路由跳转几种方式以及它们的区别
1、wx.navigateTo()
保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。
2、wx.switchTab()
跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
3、wx.redirectTo()
关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面。
4、wx.navigateBack()
关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages 获取当前的页面栈,决定需要返回几层。
注意:此方法返回上个页面,页面不会重新加载。
5、wx.reLaunch()
关闭所有页面,打开到应用内的某个页面。
6、导航组件:,跳转方式默认为navigator,可通过open-type属性改变跳转方式。
6、用户授权包括哪些授权
获取当前的地理位置、速度:wx.getLocation(Object object)
获取手机号:需要将 button 组件 open-type
的值设置为 getPhoneNumber
,当用户点击并同意之后,可以通过 bindgetphonenumber
事件回调获取 到动态令牌code
,然后把code
传到开发者后台,并在开发者后台调用微信后台提供的 phonenumber.getPhoneNumber 接口,消费code
来换取用户手机 号。每个code
有效期为5分钟,且只能消费一次。
获取用户信息:wx.getUserInfo(Object object) (旧api)
获取用户信息:wx.getUserProfile(Object object) (新api)
7、登录流程
小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。
首次登录:
1、首先需要调用小程序api接口 wx.login() 获取 临时登录凭证code ,这个code是有过期时间的。
2、将这个code回传到开发者服务器(就是请求开发者服务器的登录接口,通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的会话密钥(session_key)等)。
3、拿到开发者服务器传回来的会话密钥(session_key)之后,前端要保存wx.setStorageSync(‘sessionKey’, ‘value’)
再次登录的时候,就要判断存储的session_key是否过期了:
1、获取缓存中的session_key,wx.getStorageSync(‘sessionKey’)
2、如果缓存中存在session_key,那么调用小程序api接口wx.checkSession()来判断登录态是否过期,回调成功说明当前 session_key 未过期,回 调失败说明 session_key 已过期。登录态过期后前端需要再调用 wx.login()获取新的用户的code,然后再向开发者服务器发起登录请求。
3、一般在项目开发,开发者服务器也会对用户的登录态做过期限制,所以这时在判断完微信服务器中登录态如果没有过期之后还要判断开发者服 务器的登录态是否过期。(请求开发者服务器给定的接口进行请求判断就好)。
4、无论是微信服务器过期了还是开发者服务器登录态过期了,都要像首次登录那样开始三步骤。所以注意封装代码。
8、小程序如何实现分包
具体查看官网:https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/basic.html
配置方法
假设支持分包的小程序目录结构如下:
├── app.js
├── app.json
├── app.wxss
├── packageA
│ └── pages
│ ├── cat
│ └── dog
├── packageB
│ └── pages
│ ├── apple
│ └── banana
├── pages
│ ├── index
│ └── logs
└── utils
开发者通过在 app.json subpackages 字段声明项目分包结构:
写成 subPackages 也支持。
{
"pages":[
"pages/index",
"pages/logs"
],
"subpackages": [
{
"root": "packageA",
"pages": [
"pages/cat",
"pages/dog"
]
}, {
"root": "packageB",
"name": "pack2",
"pages": [
"pages/apple",
"pages/banana"
]
}
]
}
subpackages 中,每个分包的配置有以下几项:
字段 类型 说明
root String 分包根目录
name String 分包别名,分包预下载时可以使用
pages StringArray 分包页面路径,相对与分包根目录
independent Boolean 分包是否是独立分包
打包原则
声明 subpackages 后,将按 subpackages 配置路径进行打包,subpackages 配置路径外的目录将被打包到 app(主包) 中
app(主包)也可以有自己的 pages(即最外层的 pages 字段)
subpackage 的根目录不能是另外一个 subpackage 内的子目录
tabBar 页面必须在 app(主包)内
引用原则
packageA 无法 require packageB JS 文件,但可以 require app、自己 package 内的 JS 文件;使用 分包异步化 时不受此条限制
packageA 无法 import packageB 的 template,但可以 require app、自己 package 内的 template
packageA 无法使用 packageB 的资源,但可以使用 app、自己 package 内的资源
低版本兼容
由微信后台编译来处理旧版本客户端的兼容,后台会编译两份代码包,一份是分包后代码,另外一份是整包的兼容代码。 新客户端用分包,老客户端还是用的整包,完整包会把各个 subpackage 里面的路径放到 pages 中。
9、小程序大小限制
· 整个小程序所有分包大小不超过 8M
· 单个分包/主包大小不能超过 2M
10、自定义tabBar
自定义 tabBar 可以让开发者更加灵活地设置 tabBar 样式,以满足更多个性化的场景。
在自定义 tabBar 模式下
为了保证低版本兼容以及区分哪些页面是 tab 页,tabBar 的相关配置项需完整声明,但这些字段不会作用于自定义 tabBar 的渲染。
此时需要开发者提供一个自定义组件来渲染 tabBar,所有 tabBar 的样式都由该自定义组件渲染。推荐用 fixed 在底部的 cover-view + cover- image 组件渲染样式,以保证 tabBar 层级相对较高。
与 tabBar 样式相关的接口,如
wx.setTabBarItem 等将失效。
每个 tab 页下的自定义 tabBar 组件实例是不同的,可通过自定义组件下的 getTabBar
接口,获取当前页面的自定义 tabBar 组件实例
官网:https://developers.weixin.qq.com/miniprogram/dev/framework/ability/custom-tabbar.html
11、 有哪些参数传值的方法?
一、给HTML元素添加data-*属性来传递我们需要的值,然后通过e.currentTarget.dataset或onload的param参数获取。但data-名称不能有大写字母 和不可以存放对象
二、设置id 的方法标识来传值通过e.currentTarget.id获取设置的id的值,然后通过设置全局对象的方式来传递数值
三、在navigator中添加参数传值
12、你是怎么封装微信小程序的数据请求的?
一、将所有的接口放在统一的js文件中并导出
二、在app.js中创建封装请求数据的方法
三、在子页面中调用封装的方法请求数据
http.js文件
const baseURL = "基准地址"
function http(params) {
wx.showLoading({
title: '拼命加载中...',
})
return new Promise((resolve, reject) => {
wx.request({
...params,
url: baseURL + params.url,
success: res => {
if (res.statusCode === 200) {
wx.hideLoading()
return resolve(res)
} else {
wx.hideLoading()
return reject('请查看参数、方法以及代码')
}
},
fail: err => {
wx.hideLoading()
return reject(err)
},
complete: () => {
wx.hideLoading()
}
})
})
}
export default http
home.js文件
export async function getBanner(data) {
return await http({
url: "banner/list",
method: "GET",
data
})
}
improt{getBanner} from "home.js"
getBanner().then(res=>{
//函数体代码
})
13、小程序如何跳转到外部链接 (比如跳转到baidu.com)
个人类型和海外类型的小程序不支持 web-viewsrc=”www.xxx.xx” 标签 也就是说个人申请的小程序,就别想跳转了!!!!
进入到小程序后台https://developers.weixin.qq.com 设置-开发设置 -业务域名
第一种比较简单,就直接点击手机的返回键,让它自动根据层级返回即可;
第二种,可使用JSSDK 1.3.2提供的接口返回小程序接口,所以我们需要在H5页面引入相应的js文件才能进行操作sdk第三方的js库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VoIPpyCv-1648783646801)(./img/image-20220323140955389.png)]
14、有哪些传值的方法?
navigator组件或wx.navigator;
本地存储;
app.js中的全局对象;
自定义属性data-xxx;
15、在项目中你如何优化小程序的速度?
控制上传包的大小
勾选开发者工具中“上传代码时,压缩代码”选项
及时清理无用的代码和资源文件(包括无用的日志代码)
减少资源包中的图片等资源的数量和大小(理论上除了小icon,其他图片资源从网络下载),图片资源压缩率有限
采用分包加载机制
根据业务场景,将用户访问率高的页面放在主包里,将访问率低的页面放入子包里,按需加载;
将小程序中不经常使用的页面放到多个分包内,主包只保留最常用的核心页面;
使用分包时需要注意代码和资源文件目录的划分。启动时需要访问的页面及其依赖的资源文件应放在主包中。
采用分包预加载技术
在上一条的基础上,当用户点击到子包的目录时,还是有一个代码包下载的过程,这会感觉到明显的卡顿,所以子包也不建议拆的太大,当然我们可以采 用子包预加载技术,并不需要等到用户点击到子包页面后在下载子包,而是可以根据后期数据,做子包预加载,将用户在当先页可能点击的子包页面先加载, 当用户点击后直接跳转;
这种基于配置的子包预加载技术,是可以根据用户网络类型来判断的,当用户处于网络条件好时才预加载,是灵活可控的。
首屏加载的优化建议
利用缓存storage API, 对变动频率比较低的异步数据进行缓存,二次启动时,先利用缓存数据进行初始化渲染,然后后台进行异步数据的更新,这不仅优 化了性能,在无网环境下,用户也能很顺畅的使用到关键服务;
避免白屏,可以在前置页面将一些有用的字段带到当前页,进行首次渲染(列表页的某些数据–> 详情页),没有数据的模块可以进行骨架屏的占位,使 用户不会等待的很焦虑,甚至走了;
及时反馈,及时的对需要用户等待的交互操作进行反馈,避免用户以为小程序卡了,无响应。
避免使用不当setData
不要过于频繁调用setData,应考虑将多次setData合并成一次setData调用;
数据通信的性能与数据量正相关,因而如果有一些数据字段不在界面中展示且数据结构比较复杂或包含长字符串,则不应使用setData来设置这些数据;
与界面渲染无关的数据最好不要设置在data中,可以考虑设置在page对象的其他字段下
用户事件使用不当
视图层将事件反馈给逻辑层时,同样需要一个通信过程,通信的方向是从视图层到逻辑层。因为这个通信过程是异步的,会产生一定的延迟,延迟时间同 样与传输的数据量正相关,数据量小于64KB时在30ms内。降低延迟时间的方法主要有两个。
去掉不必要的事件绑定(WXML中的bind和catch),从而减少通信的数据量和次数;
事件绑定时需要传输target和currentTarget的dataset,因而不要在节点的data前缀属性中放置过大的数据。
视图层渲染原理
首次渲染(优化视图节点),初始渲染发生在页面刚刚创建时。初始渲染时,将初始数据套用在对应的WXML片段上生成节点树。节点树也就是在开发者 工具WXML面板中看到的页面树结构,它包含页面内所有组件节点的名称、属性值和事件回调函数等信息。最后根据节点树包含的各个节点,在界面上依次创 建出各个组件。在这整个流程中,时间开销大体上与节点树中节点的总量成正比例关系。因而减少WXML中节点的数量可以有效降低初始渲染和重渲染的时间 开销,提升渲染性能。
重渲染(减少setData数据量),初始渲染完毕后,视图层可以多次应用setData的数据。每次应用setData数据时,都会执行重渲染来更新界面。初始渲 染中得到的data和当前节点树会保留下来用于重渲染。每次重渲染时,将data和setData数据套用在WXML片段上,得到一个新节点树。然后将新节点树与当 前节点树进行比较,这样可以得到哪些节点的哪些属性需要更新、哪些节点需要添加或移除。最后,将setData数据合并到data中,并用新节点树替换旧节点 树,用于下一次重渲染。在进行当前节点树与新节点树的比较时,会着重比较setData数据影响到的节点属性。因而,去掉不必要设置的数据、减少setData的 数据量也有助于提升这一个步骤的性能。
使用自定义组件
自定义组件的更新只在组件内部进行,不受页面其他不能分内容的影响;比如一些运营活动的定时模块可以单独抽出来,做成一个定时组件,定时组件的 更新并不会影响页面上其他元素的更新;各个组件也将具有各自独立的逻辑空间。每个组件都分别拥有自己的独立的数据、setData调用。
16、程序与原生APP对比,分别说出他们优劣?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uy1x6gLe-1648783646802)(./img/image-20220323141403563.png)]
17、简述微信小程序原理
1. 小程序是个什么?
本质其实就是(混合)的app 介于web app与native 原生app之间,具备丰富的调用手机各种功能的接口,同时又具备灵活性,跨平台
2. 小程序的开发流程:
申请小程序帐号(获得appid)、安装小程序开发者调试工具、配置项目等等
3. 小程序和传统web的区别
web网页开发渲染线程和脚本是互斥的,而小程序对于两者是分开的,分别运行在不同的进程中,是基于双线程的
小程序逻辑层和渲染层是分开的所以没有DOM、BOM,相关API也不能使用(所以好多第三方和dom有关的库也不能使用)
每个页面都是不同的webview渲染,减轻了单个webview的压力
4. 运行环境差异
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-516vVi1i-1648783646803)(./img/image-20220323141503101.png)]
18、小程序的代码组成
与 web 开发(HTML,CSS,JS)类同,小程序由配置代码 JSON 文件、模板代码 WXML 文件、样式代码 WXSS 文件以及逻辑代码 JavaScript 文件组成。
JSON 文件:在小程序代码中扮演静态配置的作用,在小程序运行之前就决定了小程序一些表现(设置 navigationBarTitleText 等),需要注意的是小程 序是无法在运行过程中去动态更新 JSON 配置文件从而发生对应的变化的。
WXML 全称是 WeiXin Markup Language,是小程序框架设计的一套标签语言,结合小程序的基础组件、事件系统,可以构建出页面的结构。
数据绑定:用户界面呈现会因为当前时刻数据不同而有所不同,或者是因为用户的操作发生动态改变,这就要求程序的运行过程中,要有动态的去改变渲 染界面的能力。在 Web 开发中,开发者使用 JavaScript 通过 Dom 接口来完成界面的实时更新。在小程序中,使用 WXML 语言所提供的数据绑定功能,来完 成此项功能。
WXSS(WeiXin Style Sheets)是一套用于小程序的样式语言,用于描述WXML的组件样式,也就是视觉上的效果。
在小程序开发中,开发者不需要像 Web 开发那样去优化样式文件的请求数量,只需要考虑代码的组织即可。样式文件最终会被编译优化。
在WXSS中,引入了rpx(responsive pixel)尺寸单位。引用新尺寸单位的目的是,适配不同宽度的屏幕,开发起来更简单。小程序编译后,rpx会做一 次px换算。换算是以375个物理像素为基准,也就是在一个宽度为375物理像素的屏幕下,1rpx = 1px。
19、渲染原理(重要)
小程序的渲染层和逻辑层分别由两个进程管理,通信会由微信客户端做中转。
1、通信模型
微信小程序的框架包含两部分渲染层(View)和逻辑层(App service)。
View层用来渲染页面结构,APPService层用来处理,数据请求,接口调用,它们在两个线程里运行。
VIew层使用WebView渲染,逻辑层使用JSCore运行。
视图层和逻辑层是通过微信客户端(WeixinJsBridage),来通信的。逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻 辑层进行业务处理。
小程序的运行环境分成渲染层和逻辑层, WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层。渲染层和逻辑层是分离的。
小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore线程运行JS脚本。一个小程序存在多个界面, 所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转,逻辑层发送网络请求也经由 Native转发。
2、数据驱动
数据驱动使得数据状态和视图绑定在一起,wxml文件对应着一个js对象,可以假使这是一个dom树,当数据变化时,直接修改dom树对应的js对 象,wxml对比js对象前后的差别然后进行部分渲染。这个原理听起来很熟悉嘛?是的,和vue、react的虚拟DOM大同小异,都是为了避免重复渲 染“dom”做的优化。
渲染层和数据相关。
逻辑层负责产生、处理数据。
逻辑层通过 Page 实例的 setData 方法传递数据到渲染层。
3、全局数据
因为渲染层和逻辑层的分离,每打开一个页面都会各自又一个 WebView进程进行渲染,在逻辑层的JS脚本运行上下文依旧在一个进程中没有变。 所以App里的globalData是可以全局获取到的
7、数据驱动
数据驱动使得数据状态和视图绑定在一起,wxml文件对应着一个js对象,可以假使这是一个dom树,当数据变化时,直接修改dom树对应的js对 象,wxml对比js对象前后的差别然后进行部分渲染。这个原理听起来很熟悉嘛?是的,和vue、react的虚拟DOM大同小异,都是为了避免重复渲 染“dom”做的优化。
8、全局数据
因为渲染层和逻辑层的分离,每打开一个页面都会各自又一个 WebView进程进行渲染,在逻辑层的JS脚本运行上下文依旧在一个进程中没有变。 所以App里的globalData是可以全局获取到的。
20、怎么解决小程序的异步问题
场景一(常见):
项目需要获取code值,在app实例中也就是app.js中定义一个getCode的方法,success之后,在page中的index.js中的onload生命周期中拿到 code,通过wx.request传递给后台来换取openid。
想的很完美,但是实现时发现,在wx.login还未执行完就执行了page的onLoad;
这种异步情况可以使用promise来解决,示例代码如下:
//app.js
App({
// 获取code,然后传递给index.js中的onload生命周期
getCode (data) {
let _this= this
return new Promise((resolve => {
wx.login({
success: function (res) {
//模拟异步请求 - 3秒后返回数据 - resolve出去
setTimeout(() => {
resolve(res)
}, 3000)
}
})
}))
}
})
//index.js
//获取应用实例
const app = getApp()
Page({
data: {
},
onLoad: function () {
// 通过app实例获取到异步返回的数据 - code
app.getCode().then(res => {
console.log(res.code) // 成功返回;如果不用promise,那code的返回值就是空
})
}
})
21、小程序的双向数据绑定和Vue有何不同
小程序直接this.data的属性是不可以同步到视图的,必须调用:this.setData({})
22、小程序的wxml和html有什么区别?
HTML是用于创建网页的语言。通过使用HTML标记标签创建html文档来创建网页。
23、小程序的wxss和css有什么区别?
WXSS(WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。
WXSS 用来决定 WXML 的组件应该怎么显示。
为了适应广大的前端开发者,WXSS 具有 CSS 大部分特性。同时为了更适合开发微信小程序,WXSS 对 CSS 进行了扩充以及修改。
与 CSS 相比,WXSS 扩展的特性有:
尺寸单位
24、详述小程序的生命周期
很容易将小程序生命周期和页面的生命周期混淆为一起,这两个其实应该是不同却又相互关联的生命周期 小程序生命周期图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xZC0ASlq-1648783646804)(file:///C:/Users/13497/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg)]
小程序启动后,首先完成小程序的初始化(onLaunch)和显示(onShow),然后是页面的加载(onLoad)、显示(onShow)和渲染(onReady)。 如图“小程序的生命周期”。
小程序进入后台时,先触发页面的生命周期函数onHide,再触发小程序的生命周期函数onHide;小程序启动显示或从后台进入前台时,先触发小程序的 生命周期函数Onshow,再触发页面的生命周期函数onShow。
这里解释两个概念:
后台: 当用户点击左上角关闭(或者右上角退出),或者按了home键离开微信,小程序并没有直接销毁,而是进入了后台。
前台: 当再次进入微信或者再次打开小程序,又会从后台进入前台。
只有当小程序进入后台一定时间(目前是5分钟),或者系统资源占用过高,才会被真正的销毁。
https://upload-images.jianshu.io/upload_images/15311104-29383c1f0bc2b0a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/662
Page 实例的生命周期图
初始化:视图线程开始初始化,初始化完成发送通知到逻辑线程,视图线程开始等待数据,(逻辑线程初始化完成后等待视图线程的通知,) 逻辑线程返回页面初始数据,逻辑线程进入等待激活状态。
首次渲染:视图线程拿到初始化渲染数据后,开始首次渲染,渲染完成后(视图层进入持续渲染状态)发送 【初始化完成通知】给逻辑线程, 触发生命周期函数onReady,逻辑线程进入激活态。
持续渲染状态: 用户交互触发事件,逻辑线程处理,通过this.setData更新数据到渲染线程,数据更新,渲染线程局部更新页面。
前台->后台: 用户关闭小程序或home键退出微信,逻辑线程触发生命周期函数onHide进入后台态。
后台->前台:用户再次打开微信或小程序,逻辑线程触发生命周期函数onShow进入激活态。
销毁:页面或小程序被系统回收或销毁时,逻辑线程触发生命周期函数onUnload,结束。
25、详述小程序组件中的生命周期
组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。
其中,最重要的生命周期是 created attached detached ,包含一个组件实例生命流程的最主要时间点。
组件实例刚刚被创建好时, created 生命周期被触发。此时,组件数据 this.data 就是在 Component 构造器中定义的数据 data 。 此时还不能调用 setData 。 通常情况下,这个生命周期只应该用于给组件 this 添加一些自定义属性字段。
在组件完全初始化完毕、进入页面节点树后, attached 生命周期被触发。此时, this.data 已被初始化为组件的当前值。这个生命周期很有用,绝大多 数初始化工作可以在这个时机进行。
在组件离开页面节点树后, detached 生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则 detached 会被触发。
定义生命周期方法
生命周期方法可以直接定义在 Component 构造器的第一级参数中。
自小程序基础库版本 2.2.3 起,组件的的生命周期也可以在 lifetimes 字段内进行声明(这是推荐的方式,其优先级最高)。
示例代码如下:
Component({
lifetimes: {
attached() {
// 在组件实例进入页面节点树时执行
},
detached() {
// 在组件实例被从页面节点树移除时执行
},
},
// 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
attached() {
// 在组件实例进入页面节点树时执行
},
detached() {
// 在组件实例被从页面节点树移除时执行
},
// ...
})
在 behaviors 中也可以编写生命周期方法,同时不会与其他 behaviors 中的同名生命周期相互覆盖。但要注意,如果一个组件多次直接或间接引用同一 个 behavior ,这个 behavior 中的生命周期函数在一个执行时机内只会执行一次。
可用的全部生命周期如下表所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Obs3MUY1-1648783646804)(file:///C:/Users/13497/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg)]
组件所在页面的生命周期
还有一些特殊的生命周期,它们并非与组件有很强的关联,但有时组件需要获知,以便组件内部处理。这样的生命周期称为“组件所在页面的生命周期”,在 pageLifetimes 定义段中定义。其中可用的生命周期包括:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oO9aFjsn-1648783646805)(file:///C:/Users/13497/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg)]
示例代码如下:
Component({
pageLifetimes: {
show() {
// 页面被展示
},
hide() {
// 页面被隐藏
},
resize(size) {
// 页面尺寸变化
}
}
})
26、详述小程序的组件通信
父传子
在子组件的组件标签上通过自定义属性的形式绑定数据或字符串
在子组件中通过properties对象进行属性的接收即可。
子传父
在子组件中的methods对象中定义方法,在方法中通过this.triggerEvent({})方法,完成事件触发
在子组件标签上绑定(例:bind:在this.triggerEvent定义的事件名称=“回调函数” ),在this.triggerEvent定义的事情名称,最后在回调函数中完成逻 辑处理。
兄弟
子传父
父作为中转
父传子
27、小程序中的wxs是什么?如何使用?在项目中哪里使用过?
WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。
WXS是为了结合WXML诞生、使用的(既可以引入也可以直接写在WXML里),因为JavaScript不能在WXML中调用或直接在WXML里面写JavaScript。WXS和JavaScript是两种不相关的语言,因此在WXS中不能使用JavaScript的语法,更不能使用ES6的语法。WXS仅仅是语法形式和JavaScript很像,但是并不等同于JavaScript,WXS是有自己独立的运行环境的,再强调一遍,从根本上来说这两种就是不同的语言,只是在语法上借鉴了JavaScript。具体用法可参考 官方文档
我们在实际开发中经常用到的一个地方就是编写过滤器,但是大家不要错误的认为WXS就是为了在小程序编写过滤器而生的,大家一定要深刻理解WXS是小程序自己的一套脚本语言,主要是为了增强WXML的编程能力的.
28、bind和catch绑定事件有什么区别?
事件分为冒泡事件和非冒泡事件:
冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。
bind事件绑定不会阻止冒泡事件向上冒泡,catch事件绑定可以阻止冒泡事件向上冒泡
29、如何分包加载优势在哪
什么是分包加载?
某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。
在构建小程序分包项目时,构建会输出一个或多个分包。每个使用分包小程序必定含有一个主包。所谓的主包,即放置默认启动页面/TabBar 页面,以及一些所有分包都需用到公共资源/JS 脚本;而分包则是根据开发者的配置进行划分。
在小程序启动时,默认会下载主包并启动主包内页面,当用户用户进入分包内某个页面时,客户端会把对应分包下载下来,下载完成后再进行展示。
目前小程序分包大小有以下限制:
整个小程序所有分包大小不超过 8M
单个分包/主包大小不能超过 2M
分包加载的好处
对小程序进行分包,可以优化小程序首次启动的下载时间,以及在多团队共同开发时可以更好的解耦协作。
分包配置
配置方法
假设支持分包的小程序目录结构如下:
├── app.js
├── app.json
├── app.wxss
├── packageA
│ └── pages
│ ├── cat
│ └── dog
├── packageB
│ └── pages
│ ├── apple
│ └── banana
├── pages
│ ├── index
│ └── logs
└── utils
开发者通过在 app.json subpackages 字段声明项目分包结构:
{
"pages": ["pages/index", "pages/logs"],
"subpackages": [
{
"root": "packageA",
"pages": ["pages/cat", "pages/dog"]
},
{
"root": "packageB",
"name": "pack2",
"pages": ["pages/apple", "pages/banana"]
}
]
}
打包原则
声明 subpackages 后,将按 subpackages 配置路径进行打包,subpackages 配置路径外的目录将被打包到 app(主包) 中
app(主包)也可以有自己的 pages(即最外层的 pages 字段)
subpackage 的根目录不能是另外一个 subpackage 内的子目录
tabBar 页面必须在 app(主包)内
引用原则
packageA 无法 require packageB JS 文件,但可以 require app、自己 package 内的 JS 文件
packageA 无法 import packageB 的 template,但可以 require app、自己 package 内的 template
packageA 无法使用 packageB 的资源,但可以使用 app、自己 package 内的资源
低版本兼容
由微信后台编译来处理旧版本客户端的兼容,后台会编译两份代码包,一份是分包后代码,另外一份是整包的兼容代码。 新客户端用分包,老客户端还是用的整包,完整包会把各个 subpackage 里面的路径放到 pages 中。
30、 请谈谈微信小程序主要目录和文件的作用?
project.config.json 项目配置文件,用得最多的就是配置是否开启https校验;
App.js 设置一些全局的基础数据等;
App.json 底部tab, 标题栏和路由等设置;
App.wxss 公共样式,引入iconfont等;
pages 里面包含一个个具体的页面;
index.json (配置当前页面标题和引入组件等);
index.wxml (页面结构);
index.wxss (页面样式表);
index.js (页面的逻辑,请求和数据处理等);
31、 请谈谈wxml与标准的html的异同?
都是用来描述页面的结构;
都由标签、属性等构成;
标签名字不一样,且小程序标签更少,单一标签更多;
多了一些 wx:if 这样的属性以及 {{ }} 这样的表达式
WXML仅能在微信小程序开发者工具中预览,而HTML可以在浏览器内预览
组件封装不同, WXML对组件进行了重新封装,
小程序运行在JS Core内,没有DOM树和window对象,小程序中无法使用window对象和document对象。
32、webview中的页面怎么跳回到小程序中
从微信小程序webView
的 H5 页面中返回小程序,微信开发文档 提供了 wx.miniProgram
接口返回小程序,
33、跨小程序如何跳转?
首先说一下到另一个小程序需要得东西:
-
跳转目标小程序的APPID
-
使用微信小程序API(wx.navigateToMiniProgram)
使用wx.navigateToMiniProgram实现跳转
首先我们要在 **app.json** 中配置以下代码
1 "navigateToMiniProgramAppIdList": [
2 "目标小程序的appid"
3 ],
对应 js 的代码如下,详情
1 wx.navigateToMiniProgram({
2 appId: 'wxfe0e405895cafdf9',
3 path: '',
4 envVersion: 'release',// 打开正式版
5 success(res) {
6 // 打开成功
7 },
8 fail: function (err) {
9 console.log(err);
10 }
11 })
### 34、上拉加载
首先看一下要实现的效果,这是3g端的上拉加载。小程序要实现同样的效果。
<img src="./img/上拉加载3.gif">
首先功能有
- 点击回到顶部 这个很好实现,有对应的回到顶部函数
- 滑动屏幕记录当前页数 这个也很好实现,主要是监听滚动事件,判断对应滚动条高度,去计算其与子容器的高度即可。
- 上拉加载动画
这里有两个实现的方案。一个是 `page` 自带的下拉触底钩子事件 `onReachBottom` 能做的只是下拉到底部的时候通知你触底了,一个是 `scroll-view` 标签自带事件。现在用两个方法分别实现一下上拉加载。
#### 上拉触底事件 onReachBottom
模板
```vue
<template>
<view class="loading"></view>
<view class="container"
@touchmove="moveFn"
@touchstart="startFn"
@touchend="endFn"
style="transform:translate3d(0,{{childTop}}px,0)">
<repeat for="{{list}}"
key="index"
index="index"
item="item">
<view>{{ item }}<text>{{index}}</text></view>
</repeat>
</view>
</template>复制代码
钩子函数
data = {
getData: '',
top: 0,
lastTop: 0,
canDrag: false,
list: []
}
onReachBottom() {
this.canDrag = true
}
methods = {
moveFn(ev) {
let nowY = ev.changedTouches[0].clientY
nowY = nowY-this.lastTop
if(nowY > 0 )
this.canDrag = false
if( nowY<=0 && this.canDrag ) {
this.top = nowY
}
if( -this.top>= this.maxTop )
this.top = -this.maxTop
},
startFn(ev) {
this.lastTop = ev.changedTouches[0].clientY
},
endFn() {
if(this.top <= -this.maxTop) {
this.text = "去请求数据了"
setTimeout(()=>{
this.text = "请求回来了"
this.canDrag = false
this.list.push(...["数据","数据","数据"])
this.$apply()
this.top = 0;
return
},1000)
}
},
gotoTop() {
wepy.pageScrollTo({
scrollTop: 0
})
}
}
//复制代码
35、小程序中的template是什么?如何使用?和组件有什么不同?
template模块主要是展示,方法需要在使用template的页面中定义,对于通用的数据,最先想到或者理应是template,但是template有个缺点,那就是只是页面效果,不会有对应的js操作。
而component组件,则拥有自己的js文件,整个component组件类似一个page页面。简单来说,只是展示用,建议使用template,组件中涉及到较多的逻辑,建议使用component。
36、小程序自定义弹框滚动与页面滚动冲突问题如何解决?
实现步骤:
1.整个布局用作为根节点包裹所有view,并动态绑定scroll-view的scroll-y属性
2.样式文件中设置Page的overflow-y属性值为hidden
3.样式文件中设置scroll-view的height属性值为100%
4.打开自定义弹窗的点击事件中,更改isScroll的值为false,关闭弹窗的点击事件中,更改 isScroll的值为true
37、点击图片放大到全屏显示如何实现?
wx.previewImage(Object object )在新页面中全屏预览图片。预览的过程中用户可以进行保存图片、发送给朋友等操作
38、小程序写在标签的属性data值,有哪些需要注意的点
自定义属性的命名不要用驼峰或者大写命名了,小程序内部会给你转成小写。
39、wx:if VS hidden区别
wx:if vs hidden
因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个 局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。
同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次 变成真的时候才开始局部渲染。
相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。
一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要 频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好
40、如何实现下拉刷新
- 首先在全局 config 中的 window 配置 enablePullDownRefresh
- 在 Page 中定义 onPullDownRefresh 钩子函数,到达下拉刷新条件后,该钩子函数执行,发起请求方法
- 请求返回后,调用 wx.stopPullDownRefresh 停止下拉刷新
参考 这里:(https://juejin.im/post/5a781c756fb9a063606eb742)
下拉刷新和上拉加载是业务上一个很常见的需求,在微信小程序里,提供了下拉刷新的方法 onPullDownRefresh
。而实现上拉加载相对来说就比较不方便了。
下拉刷新
虽然微信的官方文档有很多坑,但下拉刷新介绍的还是很全面的。在这里稍稍带过。
- 首先在全局
config
中的window
配置enablePullDownRefresh
. - 在
Page
中定义onPullDownRefresh
钩子函数。到达下拉刷新条件后,该钩子函数执行,发起请求方法。 - 请求返回后,调用
wx.stopPullDownRefresh
停止下拉刷新。
config
config = {
pages: [
'pages/index'
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#ccc',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: '#000',
enablePullDownRefresh: true
}
}复制代码
page
onPullDownRefresh() {
wepy.showNavigationBarLoading()
setTimeout(()=>{
this.getData = '数据拿到了'
wepy.stopPullDownRefresh()
wepy.hideNavigationBarLoading()
this.$apply()
},3000)
}复制代码
效果如下:
你会发现下拉的过程有些僵硬。这实际上是没有添加背景色的原因,加上背景色后再试试。
现在感觉好多了吧。下拉刷新有现成的配置和方法,很容易实现
更多推荐
所有评论(0)