目录

小程序开发最重要最经典面试题

目录

小程序开发最重要、最经典面试题

小程序面试题

1. bindtap 和 catchtap 的区别是什么?

bind 事件绑定不会阻止冒泡事件向上冒泡,catch 事件绑定可以阻止冒泡事件向上冒泡

2. Js 数组转成换字符串,强制转换成整数及转换成浮点数的函数分别是什么?

js 提供了 parseInt()和 parseFloat()两个转换函数。前者把值转换成整数,后者把值转换成浮点数。只有对 String 类型调用这些方法,这两个函数才能正确运行;对其他类型返回的都是 NaN(Not a Number)。

  1. 转换函数:

在 判断字符串是否是数字值前,parseInt()和 parseFloat()都会仔细分析该字符串。parseInt()方法首先查看位置 0 处的 字符,判断它是否是个有效数字;如果不是,该方法将返回 NaN,不再继续执行其他操作。但如果该字符是有效数字,该方法将查看位置 1 处的字符,进行同样的 测试。这一过程将持续到发现非有效数字的字符为止,此时 parseInt()将把该字符之前的字符串转换成数字。

parseInt(“1234blue”); //returns 1234

parseInt(“0xA”); //returns 10

parseInt(“22.5”); //returns 22

parseInt(“blue”); //returns NaN

  1. 强制类型转换

还可使用强制类型转换(type casting)处理转换值的类型。使用强制类型转换可以访问特定的值,即使它是另一种类型的。

ECMAScript 中可用的 3 种强制类型转换如下:

Boolean(value)——把给定的值转换成 Boolean 型;

Number(value)——把给定的值转换成数字(可以是整数或浮点数);

String(value)——把给定的值转换成字符串。

  1. 利用 js 变量弱类型转换

举个小例子,一看,就会明白了。

<script>var str= '012.345 '; var x = str-0; x = x*1;</script>

上例利用了 js 的弱类型的特点,只进行了算术运算,实现了字符串到数字的类型转换,不过这个方法还是不推荐的。

3.  简单描述下微信小程序的相关文件类型。

小程序:pages ——index:index.js(页面逻辑) /index.wxml (页面结构)/index.wxss (页面样式表) / index.json (页面配置)

App.js    小程序逻辑

App.json   小程序公共设置

App.wxss    小程序公共样式表

4.  小程序有哪些参数传值的方法?

1、设置 id 的方法标识跳转后传递的参数值;

2、通过使用 data - xxxx 的方法来标识要传递的值

微信小程序设置 id 的方法标识来传值

在要跳转的 item 处,设置一个 id 并给当前的 id 赋值上对应的 key 值,比如一部电影的 id(后面带着 id 去下一个页面查询,详细信息)如:

后我们在 js 的 bindtap 的响应事件中获取,并传递到下一个界面中;

获取到 id 传的值

通过 e.currentTarget.id;获取设置的 id 值,并通过设置全局对象的方式来传递数值,

获取全局对象  var app=getApp(); //设置全局的请求访问传递的参数  app.requestDetailid=id;

提示:其实我们也可以在,wxml 中查看到我们设置的每一个 item 的 id 值

通过使用 data - xxxx 的方法标识来传值

通过使用 data - xxxx 的方法标识来传值,xxxx 可以自定义取名 比如 data-key 等等都可以。

如何获取 data-xxxx 传递的值?

在 js 的 bindtap 的响应事件中:

通过数据解析一层层找到数据,var id=e.target.dataset.id(根据你的 data-id 的取名)

微信小程序如何跨页面获取值?

依据上面的方式设置要传递的值,页面跳转后,我们就需要在下一个页面拿到传递的数据(这个数据在传递前,就已经被设置成全局变量)

在跳转后的 js 页面,接收传递过来的数据 detail.js

同样通过全局额方式取值出来,(即和 app.js 中取某个变量的值是一样的)

var movieid=getApp().MovieDetailid;

console.log(movieid);

5.  简述下 wx.navigateTo(), wx.redirectTo(), wx.switchTab(), wx.navigateBack(), wx.reLaunch()的区别?

微信小程序 跳转页面

小程序页面有 2 种跳转,可以在 wxml 页面或者 js 中:

1,在 wxml 页面中:

<navigator url="../index/index">跳转到新页面</navigator>

<navigator url="../index/index" open-type="redirect">在当前页打开</navigator>

<navigator url="../index/index" open-type="switchTab">切换到首页Tab</navigator>

2,在 js 页面中:

【注意】此处注意两个关键词 “应用内的页面” 和 “tabBar 页面”。  app.json 文件中 tabBar 中注册过的 tab 页,即为“tabBar 页面”,非 tabBar 中注册占用的页面即为“应用内的页面” 。 如下图:home 页面为“应用内的页面”,index 和 logs 页面则为 “tabBar 页面”。

3,如果上述跳转遇到跳转失败或无效的问题,请访问下面链接:

wx.navigateTo/wx.redirectTo 无效

6.  如果需要用户授权,用户选择拒绝授权,此时应该如何处理?

在微信小程序开发时,当我们调用 API  wx.getUserInfo(OBJECT) 时,需要用户授权。但如果用户拒绝授权,我们如何兼容用户拒绝授权状态,拥有更好的用户体验呢?

先看看这个接口的官方文档:

wx.getUserInfo(OBJECT)

获取用户信息,需要先调用  wx.login  接口。

OBJECT 参数说明:

参数名类型必填说明
withCredentialsBoolean是否带上登录态信息
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

1. tip: wx.getUserInfo  接口需要用户授权,请兼容用户拒绝授权的场景。

我们就是要在用户点击拒绝的时候,弹出提示框,提示用户以提升用户体验。像下面这样的。

用具体代码实现就是,将弹窗写在 wx.getUserInfo  的 fail 回调函数中,像下面这样:

wx.getUserInfo({
  success: function (resuser) {
    console.log(success);
  },

  fail: function () {
    // 调用微信弹窗接口

    wx.showModal({
      title: "警告",

      content:
        "您点击了拒绝授权,将无法正常使用**的功能体验。请10分钟后再次点击授权,或者删除小程序重新进入。",

      success: function (res) {
        if (res.confirm) {
          console.log("用户点击确定");
        }
      },
    });
  },
});

这样用户就获得了提示信息,但此时,用户还是停留在页面的,如果某些展示信息,还是给要给用户展示的,只是在进行某些操作的时候要对授权进行验证的话,那就得继续修改我们的代码,保存用户的登录态,在其他地方做验证使用。

第一种思路:

保存登录态这里是这样的,将用户的登录信息传给后台,后台保存用户信息,同时用 open_id 在后台换取一个 SessionId   用换取的这个 SessionId 存在缓存,做为登录态验证。

wx.getUserInfo({
  success: function (resuser) {
    let userInfo = resuser.userInfo;

    that.healthApi.login(code, userInfo).then((logindata) => {
      // 这里将微信的请求封装成Promiese 风格

      if (logindata.code === 0) {
        var sessionId = logindata.data; // 调用微信wechat.setStorage将换回来的 SessionId 存在本地缓存

        that.wechat.setStorage("sessionId", sessionId).then(() => {
          that.globalData.userInfo = userInfo;

          typeof cb == "function" && cb(that.globalData.userInfo);
        });
      }
    });
  },

  fail: function () {
    wx.showModal({
      title: "警告",

      content:
        "您点击了拒绝授权,将无法正常使用的功能体验。请10分钟后再次点击授权,或者删除小程序重新进入。",

      success: function (res) {
        if (res.confirm) {
          console.log("用户点击确定");
        }
      },
    });
  },
});

这样我们将登录态保存在了 SessionId 中,在每次登录的时候我们只需要再调用一个 检查 SessionId 的接口就行,检查不通过再调微信登录接口。此处不做延伸了。

第二种思路:

在 3.29 微信小程序更新的版本中,加入了这样一条属性

withCredentials 字段基础库版本 1.1.0 开始支持,低版本需做兼容处理

这个字段的意思就是调用  wx.getUserInfo(OBJECT) 是否带上   登录态    的信息。

官方文档是这样解释的:

withCredentials 字段基础库版本 1.1.0 开始支持,低版本需做兼容处理

注:当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。

success 返回参数说明:

参数类型说明
userInfoOBJECT用户信息对象,不包含 openid 等敏感信息
rawDataString不包括敏感信息的原始数据字符串,用于计算签名。
signatureString使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息。
encryptedDataString包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法
ivString加密算法的初始向量,详细见加密数据解密算法

注:需要兼容微信低版本,向后兼容。

那么利用这个接口,我们可以直接拿到 登录状态,在其他需要验证登录的地方进行提示,而在不需要授权的地方还可以让用户浏览小程序。

回到前面的问题,在用户点击拒绝授权后,在某些操作时需要验证用户是否授权过,弹出交互信息,那么就利用上面的 SessionId 或者  withCredentials  登录态进行下面的操作:

applyIn: function applyIn() {

if (wx.getStorageSync('sessionId')) {  // 根据储存的sessionId 进行验证

wx.navigateTo({

url: 'familyDoctorApply/familyDoctorApply?Oid=' + this.data.params.Oid + '&title=' + this.data.params.title + '&serviceCity=' + this.data.array[this.data.index].name + '&productPrice=' + this.data.product.productPrice

});

} else {

wx.showModal({

title: '警告',

content: '您点击了拒绝授权,无法使用此功能。',

success: function (res) {

if (res.confirm) {

console.log('用户点击确定')

}

}

})

}

效果像这样:

这样一个简单完整的登录及授权,登录态保存等前端微信小程序解决方案就完成了,还可以继续扩展到登录有效期,退出登录,用户权限等跟多扩展的地方。

7.  你平时封装可以复用的方法吗?你会把可以复用的方法写在哪个文件里?

其实可以模拟一些框架的,比如 bootsrap,写个 demo 出来,抽出 css 和 js,js 最好抽象成对象(构造函数)或者是带参数的方法,然后你只要声明对像,或者参数指定某个 class 或 id,就可以了

写在 html 文件里有什么优点吗?

独立出来会有什么问题吗?尤其是载入页面的时候,应该会多发很多 http 请求吧,会不会造成加载变慢?

8.  分析下小程序的优劣势?

小程序是在微信生态发展过程中新出现的一种应用形态,小程序的小,从某种程度上已经说明了它的体量不会很大,但是可以实现一些功能相对简单、交互相对简单的服务需求,同时解决了 App 长期以来多平台适配、多应用市场分发、开发成本居高不下等诸多方面的问题。所以小程序【密件】依靠微信平台和自身“阅后即焚”的功能,获得众多年轻人的好评

优势:

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 来修改

9.  设置值到页面暂存区(即 data)里面的方法有几种?分别是什么?有什么区别?

  1. 使用 QueryString 变量

QueryString 是一种非常简单的传值方式,他可以将传送的值显示在浏览器的地址栏中。如果是传递一个或多个安全性要求不高或是结构简单的数值时,可以使用这个方法。但是对于传递数组或对象的话,就不能用这个方法了。下面是一个例子:

a.aspx 的 C#代码

private void Button1_Click(object sender, System.EventArgs e)

{

string s_url;

s_url = "b.aspx?name=" + Label1.Text;

Response.Redirect(s_url);

}

b.aspx中C#代码

private void Page_Load(object sender, EventArgs e)

{

Label2.Text = Request.QueryString["name"];

}
  1. 使用 Application 对象变量

Application 对象的作用范围是整个全局,也就是说对所有用户都有效。其常用的方法用 Lock 和 UnLock。

a.aspx 的 C#代码

private void Button1_Click(object sender, System.EventArgs e)

{

Application["name"] = Label1.Text;

Server.Transfer("b.aspx");

}

b.aspx中C#代码

private void Page_Load(object sender, EventArgs e)

{

string name;

Application.Lock();

name = Application["name"].ToString();

Application.UnLock();

}
  1. 使用 Session 变量

想必这个肯定是大家使用中最常见的用法了,其操作与 Application 类似,作用于用户个人,所以,过量的存储会导致服务器内存资源的耗尽。

a.aspx 的 C#代码

private void Button1_Click(object sender, System.EventArgs e)

{

Session["name"] = Label.Text;

}

b.aspx中C#代码

private void Page_Load(object sender, EventArgs e)

{

string name;

name = Session["name"].ToString();

}
  1. 使用 Cookie 对象变量

这个也是大家常使用的方法,与 Session 一样,其是什对每一个用户而言的,但是有个本质的区别,即 Cookie 是存放在客户端的,而 session 是存放在服务器端的。而且 Cookie 的使用要配合 ASP.NET 内置对象 Request 来使用。

a.aspx 的 C#代码

private void Button1_Click(object sender, System.EventArgs e)

{

HttpCookie cookie_name = new HttpCookie("name");

cookie_name.Value = Label1.Text;

Reponse.AppendCookie(cookie_name);

Server.Transfer("b.aspx");

}

b.aspx 中 C#代码

private void Page_Load(object sender, EventArgs e)

{

string name;

name = Request.Cookie["name"].Value.ToString();

}
  1. 使用 Server.Transfer 方法

这个才可以说是面象对象开发所使用的方法,其使用 Server.Transfer 方法把流程从当前页面引导到另一个页面中,新的页面使用前一个页面的应答流,所以这个方法是完全面象对象的,简洁有效。

a.aspx 的 C#代码

public string Name

{

get{ return Label1.Text;}

}

private void Button1_Click(object sender, System.EventArgs e)

{

Server.Transfer("b.aspx");

}

b.aspx 中 C#代码

private void Page_Load(object sender, EventArgs e)

{

a newWeb; //实例a窗体

newWeb = (source)Context.Handler;

string name;

name = newWeb.Name;

}

微信小程序–data 的赋值与取值

通过小程序官方文档可知:

Page() 函数用来注册一个页面。接受一个 object 参数,其指定页面的初始数据、生命周期函数、事件处理函数等。其中的参数 data 用来设置初始数据,WXML 中的动态数据均来自对应 Page 的 data。

所以如果页面需要显示动态数据必须要把数据更新到 data 中对应的变量中。

·  页面 js 文件中这么写:

Page({
  data: {
    message: "Hello MINA!",
  },
});

· wxml 中这么写:

<view>  {{ message }} </view>

·  如果该数据在操作过程中发生变化,需要将新数据重新绑定到该变量中,写法如下:

function setData() {
  var that = this;

  that.setData({
    message: "新消息",
  });
}

·  如果想在 js 文件中使用 data 中的数据写法如下:

function getData() {
  var that = this;

  console.log(that.data.message);
}

10.  如何检测用户的微信版本是否支持某项功能?

第一期开放的接口,不是不能使用,而是无需检测,全部都是支持的。

只有后面最新开放的一些接口,才需要检测是否支持。

目前开放的所有接口:

onMenuShareTimeline;

onMenuShareAppMessage;

onMenuShareQQ;

onMenuShareWeibo;

onMenuShareQZone;

startRecord;

stopRecord;

onVoiceRecordEnd;

playVoice;

pauseVoice;

stopVoice;

onVoicePlayEnd;

uploadVoice;

downloadVoice;

chooseImage;

previewImage;

uploadImage;

downloadImage;

translateVoice;

getNetworkType;

openLocation;

getLocation;

hideOptionMenu;

showOptionMenu;

hideMenuItems;

showMenuItems;

hideAllNonBaseMenuItem;

showAllNonBaseMenuItem;

closeWindow;

scanQRCode;

chooseWXPay;

openProductSpecificView;

addCard;

chooseCard;

openCard;

11.  如何分包加载?分包加载的优势在哪?

分包加载的介绍

大部分小程序都会由某几个功能组成,通常这几个功能之间是独立的,但会依赖一些公共的逻辑,并且这些功能通常会对应某几个独立的页面。那么小程序代码的打包,大可不必一定要打成一个,可以按照功能的划分,拆分成几个分包,当需要用到某个功能时,才加载这个功能对应的分包。

对于用户来说,小程序加载流程变成了:

1.首次启动时,先下载小程序主包,显示主包内的页面;

2.如果用户进入了某个分包的页面,再下载这个对应分包,下载完毕后,显示分包的页面。

采用分包加载,对开发者而言,能使小程序有更大的代码体积,承载更多的功能与服务;而对用户而言,可以更快地打开小程序,同时在不影响启动速度前提下使用更多功能。

分包的划分

在配置前首先需要开发者规划下各个分包需要容纳的内容,我们建议开发者按照功能划分的的原则,将同一个功能下的页面和逻辑放置于同一个目录下,对于一些跨功能之间公共逻辑,将其放置于主包下,这样可以确保在分包引用这部分功能时,这部分的逻辑一定存在。

在分包划分时,应该注意以下事项:

1.避免分包与分包之间引用上的耦合。因为分包的加载是由用户操作触发的,并不能确保某分包加载时,另外一个分包就一定存在,这个时候可能会导致 JS 逻辑异常的情况,例如报「“xxx.js” is not defined」这样的错误;

2.一些公共用到的自定义组件,需要放在主包内。

分包的配置

当理清了分包的划分后,就可以进行分包的配置了,这一步并不复杂。

假设支持分包的小程序目录结构如下:

开发者通过在 app.json subPackages  字段声明项目分包结构:

分包加载的低版本兼容问题

微信 6.6.0 版本开始支持分包加载,而对于低于这个版本的客户端,我们做了兼容处理,开发者不需要对老版本微信客户端做兼容。对于老版本的客户端,编译后台会将所有的分包打包成一个整包,老版本的客户端依然按照整包的方式进行加载。

所以在老版本的微信客户端下,是依然采取整包加载的方式加载的,建议开发者尽量控制代码包的大小。

目前小程序分包大小的限制:

整个小程序所有分包大小不超过 4M

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

随着时间推移,老版本覆盖率降低,我们会考虑进一步扩大代码包的大小。

12.  在你开发小程序的过程中遇到过什么坑? 你是怎么解决的?

1.我们使用 app.json 文件来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 的时候在 pages 中写注释的时候回报错。

例如:

{
  "pages": [
    //这是首页面

    "pages/welcome/welcome"
  ]
}

此时就会报错

2.在 json 文件中没有写内容的时候也要加一对大括号{ },不然的话也会报错

3. ① 在开发微信小程序的时候,我们使用 app.json 文件来对微信小程序进行全局配置,决定页面文件的路径,窗口表现,设置网络超时时间,设置多 Tab 等。

以下是一个包含了所有配置选项的简单配置,app.json :

{
  //设置页面路径

  "pages": ["pages/index/index", "pages/logs/index"],

  //设置默认页面的窗口表现

  "window": {
    "navigationBarTitleText": "Demo"
  },

  //设置底部 tab 的表现

  "tabBar": {
    "list": [
      {
        "pagePath": "pages/index/index",

        "text": "首页"
      },
      {
        "pagePath": "pages/logs/logs",

        "text": "日志"
      }
    ]
  },

  //设置网络超时时间

  "networkTimeout": {
    "request": 10000,

    "downloadFile": 10000
  },

  //设置是否开启 debug 模式

  "debug": true
}

② 但是在对页面 json 文件进行配置的时候只可以配置设置默认页面的窗口表现(即只能对 window 进行配置),但是在此时可以直接省略 window,如果加 window 则没有效果,也不会报错。

以下是一个包含了 window 配置选项的简单配置,post.json :

注意:这是错误的写法

{
  "window": {
    "navigationBarBackgroundColor": "#ffffff",

    "navigationBarTextStyle": "black",

    "navigationBarTitleText": "微信接口功能演示",

    "backgroundColor": "#eeeeee",

    "backgroundTextStyle": "light"
  }
}

注意:正确的写法

{
  "navigationBarBackgroundColor": "#ffffff",

  "navigationBarTextStyle": "black",

  "navigationBarTitleText": "微信接口功能演示",

  "backgroundColor": "#eeeeee",

  "backgroundTextStyle": "light"
}

4.此前一直没有注意 vertical-align: middle 和 height:40rpx;line-height:40rpx 进行设置垂直剧中的区别,这次主要说一下 vertical-align: middle

代码如下:

<view class="post-author-date">
  <image class="post-author" src="../../images/avatar/1.png"> </image>

  <text class="post-date">Nov 15 2016</text>
</view>

.post-author{ width: 60rpx; height: 60rpx; vertical-align: middle; } .post-date{
margin-top: 5rpx; vertical-align: middle; /*height: 40rpx; line-height: 40rpx;*/
}

总结:

①vertical-align: middle;把此元素放在父元素的中部

② 当一个父元素里面有多个子元素,需要把几个子元素水平对齐,并且每个子元素都垂直剧中的时候,对每一个子元素进行设置 vertical-align: middle

③height: 40rpx; line-height: 40rpx;可以对文本进行垂直居中