1、小程序中 this 的指代问题
箭头函数可以保证 this 的指代是不变的 :success: res => { }
wx.request({ url: `${config.baseUrl}restful风格的api`, data: { x: '' }, header: { 'content-type': 'application/json' }, success: res => { } })
2、小程序各类型文件的分工
wxss:页面样式
wxml:页面骨架
json:相关页面配置
js:一般是认为是业务逻辑,但在页面的 js 里,真的应该用于写业务逻辑吗?
- 这个问题应该放在具体场景/项目来讲,如果是示例型的,业务逻辑写在 js 里完全没问题。
- 但真正大型低耦合的项目中,不注重良好的分层结构,最后导致项目无法维护。
- js 本身就是动态语言,没有类型的约束下,还是把代码都堆叠在一起,将来一定会是非常混乱的。
所以,页面的 js 并不应该编写业务逻辑。
那么,页面的 js 不写业务逻辑,应该做什么呢?主要用于数据绑定,也即 view (视图层) ,可以视为 “业务逻辑” 与 “页面” 之间的桥梁 / 中间层。
熟悉后端的看到这里就不陌生了,也即常见的 MVC
3、小程序中使用面向对象的方式编码
在工程里新建的 model 目录,作为类似后端中的 service 层,那么名字如何取?
home 不应该是常说的业务对象,那什么是一个业务对象?按当前请求的数据 / 作用的模块进行命名,类似后端的用户 service、其他业务 service 等
4、前端与后端的思维方式?
对于后端来讲,很多时间就是需要去找业务对象。
那么前端同理,如今的前后端的界限其实非常模糊,也即所谓的全占,哦,写错了,是全栈。
因为在以前本身就没有前端和服务端之分,程序员嘛,管你什么类型,一起撸就完事。
只是现在的前端工作非常重,所以一个程序员没有办法也没有那么多精力(毕竟晚上。。。)同时完成两端。
从编程的本质来讲,前端和服务端确实没有太大区别。
由此,业务对象的寻找非常重要。(期末要考的,划线啊)
举个栗子
比如 user 、search、spu、address、sku 等都是一个业务对象
class User{ static getUserInfo(){ } } export { Theme }
为什么是定义成 static ?后面再解释,这么猴急干嘛,
衣服都没脱,哦,不是,是定义一些非 static 方法的时候再来对比。我偏不要 static 行不行?行啊,在其他地方调用这个方法,你再实例化一次 User 呗。加了 static 就可以直接 User.getUserInfo()
5、model 中方法的命名
getUserInfo() 我写的驼峰啊,没问题啊,但是你从大局来看,个人资料里叫 getUserInfo() ,个人资料另一个地方要获取朋友的用户信息,get 什么 info呢?
用业务来命名?显然不对嘛,CMS 里一改,本来是用户信息的,现在成了性感荷官在线发牌,还是 getUserInfo() ?
那按顺序来吧,都定义一个编号,比如第一个叫 getXxInfoA(),第二个叫 getXxInfoB()
还不对?我在其他页面也有用到这个方法呢?再加个前缀呗,getMindXxInfoA()
6、将原 home.js 的业务逻辑搬到 model 的方法里
这样真的就合适了吗?
还想怎样啊,都按照上面写的进行了。。。莫急啊,
都说了还没脱裤子,继续往下看
一个 class 只有一个 request 请求吗?显然不对吧,那每一个 request 都写一个 wx.request ?重复写那么多干嘛,又不是按代码行数给你算钱。。。抽成封装对象
7、封装 HTTP 请求
就算上述的一个 class 的众多 request 都不做抽取,那也是没办法运行的,模型哪里啦的 setData?Page 才有吧,鉴于此
最简单的方式就是,让 class 的 方法接收回调函数
class User{ /*老是忘记写的注释*/ static getMindUserInfoA(callback){ wx.request({ url: `${config.baseUrl}restful风格的路径`, data: { x: '' }, header: { 'content-type': 'application/json' }, success: res => { callback(res.data); } }) } } export { Theme }
在原来写这段代码的地方进行调用
Theme.getMindUserInfoA(data => { this.setData({ userinfo: data }); });
假如这个 class 里要写 100个 request 请求呢?那就要一百遍 wx.request 啊,所以再封装一次
在 utils 下新建一个 js
import { config } from "../config/config"; class Http{ static request({url, data, callback, method='GET'}){ wx.request({ url: `${config.baseUrl}${url}`, data, method, header:{ 'content-type': 'application/json' }, success(res){ callback(res.data) } }) } } export { Http }
改造原有 User 下的 getMindUserInfoA()
import { Http } from "../utils/http"; class User{ static getMindUserInfoA(callback){ //调用方法的时候传入一个对象 Http.request({ url: `restful风格的路径`, data: { x: '' }, method: 'GET', callback: data => { callback(JSON.parse(data)) } }); } } export { User }
原来 home 下的 js 不变(别忘记引入 User )
import {User} from "../../model/User";
User.getMindUserInfoA(data => { this.setData({ userInfo: data }); });
为何上面 User 下的 getMindUserInfoA() 调了回调函数又调一次?因为 home 下的 js 传入了一个 callback
尼玛,回调函数里又有回调函数,跟我说封装好了?别急,再往下看,
真的裤子还没脱
8、小程序中使用 async 和 await (小程序中暂时还是不支持的,你不知道吗?)
第 7 点钟封装的 http 请求还是很麻烦,它在每一层调用的时候,都需要主动接收一个 callback 回调函数作为参数,为何?
举个栗子
比如有 fun1,fun2,fun3 ,依次进行调用,也即 fun1 调用 fun2,fun2 调用 fun3 ,一层层调用的时候是不需要 callbak 的,那为何上面的调用需要呢?
原因是本栗子的三个函数的调用都是同步,但是第 7 点中由于最里面 wx.request 是异步的请求,在各个层之间,只要有一层是异步调用,那么就会导致层与层之间的函数调用都必须是异步的,否则 http.js 的请求还没完,第一层就 setData了,那肯定不行的。
9、处理异步调用的三种方式
1、callback
最基础的一种形式,但却是最恶心的,第 7 点的封装才3层,已经用两个 callback 了
2、promise
这种方式依然不够简洁,虽然比 callbak 要强
3、async await
基于 promise 的,只有一个函数返回 promise 才能使用此种方式
10、如何在小程序中全面支持 async await
有人就会说了,直接加上不就得了呗
import { config } from "../config/config"; class Http{ //由于在函数内部出现了 await,那么在方法名前就要加 async static async request({url, data, callback, method='GET'}){ await wx.request({ url: `${config.baseUrl}${url}`, data, method, header:{ 'content-type': 'application/json' }, success(res){ callback(res.data) } }) } } export { Http }
但是,如果要在一个函数的调用比如 wx.request 中使用 await 的前提,是这个函数的调用必须要返回结果(不是说一定要返回 promise ),事实上一个函数返回的结果不是 promise 也是可以加 await 的,比如一个函数的调用后返回的是 1 ,只不过1这种常量的结果,加 await 是没有意义的。
所以,不是说 await 一定要加在 promise 前面,一个函数调用返回的结果是常量,也可以加 await,只不过是
脱裤子放屁事实上,await 是求值,返回的是1,用 await 加上1,结果还是 1。
wx.request 这个函数并不会返回 promise,至少目前小程序版本不会返回。事实上,这个函数不会返回任何值,所以直接加上 await 是没有意义的。
另外提一点
小程序的云开发中有很多内置函数,类似 wx.request ,调用了云函数但如果不传 success ,那么云函数就会知道,你期望的不是使用回调函数的结果来接收这次 API 的调用,而是要使用 promise 来接收。
但,小程序中所有 wx 开头的 API 中都不能直接返回 promise,都只能够使用回调函数的形式返回结果。(别问我,问腾讯)
11、来完成第 10 点的封装吧
软件里各种各样的疑难杂症,都是可以通过一层层封装来解决。事实上,软件世界,就是在不断的封装
在 utils 中新建一个 js,将内置非 promise API 转为 promise
const promisic = function (func) { return function (params = {}) { return new Promise((resolve, reject) => { const args = Object.assign(params, { success: (res) => { resolve(res); }, fail: (error) => { reject(error); } }); func(args); }); }; }; export { promisic };
这段代码的作用:可以将小程序内置的任意 API 全部都可以转成 promise 形式返回
基本用法
将 wx.request 作为参数传到 promise 中(传入的虽然是函数,但不能加(),加了()就表示已经直接调用这个函数了)
可以简单理解成就是调用了 promise 函数,函数调用则需返回结果,这个返回结果比较特殊,返回的是一个函数(看到这里是不是又觉得很熟悉?对,没错,类似 Java8 新特性,通过行为参数化传递代码,传递 Lambda 表达式)
既然是一个函数,那么就可以在最后加()调用
promisic(wx.request)()
小问题
原来 wx.request 需要传递参数,使用 promisic 包装后怎么传?只需要在最后的 () 传递即可
promisic(wx.request)({url, data, callback, method='GET'})
回到 http.js 中
import { config } from "../config/config";
import { promisic } from "utils";
class Http{
static async request({url, data, callback, method='GET'}){
await promisic(wx.request)({
url: `${config.baseUrl}${url}`,
data,
method,
header:{
'content-type': 'application/json'
}
})
}
}
export {
Http
}
12、promisic 简易原理
实际上 promisic 定义的就是一个 function,它需要接收另一个 function 作为参数,然后在内部进行包装,这也就是为什么传递函数作为参数之后还可以加() 并增加参数。
因为它返回结果本身就还是一个function ,自不过这个 promisic 内部使用 promise 把原来的 API 进行了包装,所以是在 promise 里面帮你调用了原来的 API success 和 fail 。
在这个 promise 也用到了一种设计模式,代理模式。原来是直接调用的 API,现在是通过 promisic 代理调用。
13、将回调函数全部替换为 async 和 await
callback 不需要了,因为 async 与 await 实际上就是同步的方式调用异步的函数。
回到 class 下的 User
(一个函数前面加上了 async 之后,表示这个函数的返回结果最终一定会返回 promise,哪怕返回的是 1,那也是通过peeomise 包装后的 1)
import { Http } from "../utils/http"; class User{ static async getMindUserInfoA(){ //调用方法的时候传入一个对象 return await Http.request({ url: `restful风格的路径`, data: { x: '' }, method: 'GET' }); } } export { User }
home.js 调用处
const resData = await User.getMindUserInfoA();
var data = resData.data.data;
data = JSON.parse(data);
this.setData({
userInfo: data
});
14、<block>
其实只是一个占位符,没有实际意义
15、swiper 与 swiper-item 中 image 的样式
除了要设定 swiper-item 中 image 的样式,还得设置最外面swiper 的高宽
16、使用 npm 引入现成组件,比如 lin-ui
"dependencies": { "lin-ui": "~0.6.0" },
其中 ~ 的作用是,会帮你选择该组件在 npm 库中最高的版本号版本安装,但如果是有 0.7.0 版本,则 npm 只会匹配到 0.6 的最高版本,而不会匹配 0.7.0 版本。(想要了解更多,可以自行搜索 semver )
如果我想更新中间那个版本号呢?比如 发布的版本是 1.2.3,升级的版本是 1.3.2 ,使用 ^ 即可(使用 latest、< > 均可)
"dependencies": { "lin-ui": "^0.6.0" },
17、component 的 js 属性
properties:主要是定义外部属性,因为 component 需要接收参数,哪些参数需要接收则定义在此即可
data:主要用于放置内部的属性
methods:顾名思义,就是定义组件的相关方法
18、component 的 js properties 属性接收数据方式
有两种,如果是数组,设置数组最快的方式就是:grid:Array 等同于:grid: { type: Array,value: [ ] }
19、组件名问题
组件名不对,小程序并不会报错,只有当组件路径不对时才会报错
20、自定义组件的 class 问题
自定义组件是不能直接设置 class ,如果要设置一个组件的样式,不能使用 class,要使用组件的外部样式类。
外部样式类的作用就类似与 slot (插槽),插槽可以自定义组件内容,而外部样式类可以自定义组件内容的样式。
21、view 组件下的所有元素居中
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
22、小程序下的体验优化
小程序不同于 APP,限制于很多机器的性能可能不是那么的强悍,所以在使用过程中经常会出现一定延迟,但是可以通过添加一些动画来减少这样的一个对于用户的反馈延迟,也提高了体验。
这就有点类似APP启动页,从视觉效果上达到假装正在初始化,从而避免APP启动过久,导致启动时一片空白的尴尬。
23、组件设计原则
1、如果要设置自己的组件,最重要的原则就是你必须在这个组件的灵活性、易用性、稳定性之间做出选择,又或者在这几者之间找到一个平衡点。其实灵活性和稳定性是相违背的,一个组件越灵活说明稳定性是相对差的,同时易用性 也是相对较低的。也就是说,这种情况下组件的上手难度其实越高,过于灵活,稳定性因此会相对较差。
24、组件的意义到底是什么?
在 vue 中几乎都是随处可见的组件,究竟为何如今的前端变得如此极端?不管什么场景也好还是业务也好,都以组件来实现?其实小程序的 js 中的 Page ,是可以变成 Component 的 ,为什么?因为 Page 本身就是一个 Component 。
Component({ })
也就是说,实际上来讲,小程序本身也是一个全组件驱动的框架,只不过微信并没有像 Vue 那样极端,之所以这样,是因为小程序刚出来时是没有 Component 的。好的架构 / 系统 都是演进来的,而不是设计出来的。
在小程序中 将 Page 都变成 Component 来处理会更好,好处就在于 Page 非常呆板,他不具有组件的一些行为,但 Component 可以像类一样继承并相互引用,还可以有统一的行为规则。
回到上面的问题,究竟为何如今的前端变得如此极端?因为组件的好处实在是太多了,组件最主要其实有三大基本特性:
- 样式:组件都会提供一些样式,避免重复编写样式
- 骨架:避免重复造轮子,做无用功
- 业务逻辑/行为:其实组件也是对行为的一种封装
25、组件的灵活性与自定义性
组件的意义具有上述三大特性,既然是组件,那么就要考虑到灵活性,组件默认的三大特性可能并不符合任何的开发者当前的业务场景需求,所以基于灵活性考虑,必须提供一些机制和手段,帮助开发者自行改造组件。
- 对于样式,在小程序中,外部样式类是让开发者自定义样式非常重要的手段;
- 对于骨架,slot 插槽,这个是微信小程序对于改造骨架的一种有力辅助;
- 对于业务逻辑,无论哪一种组件都没有对业务场景有一个很好的支撑,这也是受限于与技术无关的机制
其实可以把一些相同的业务场景提取成一个 Behavior ,然后这个 Behavior 就是一种行为。
为什么业务逻辑比较难以自定义?这是因为业务逻辑本身就是组件的一个特性/特征,如果更改了一个业务逻辑,那么可能这个组件就不再是这个组件了。
所以为什么 A 与 B 两个组件不一样?这是因为它们本身就有非常鲜明的业务特征,所以把一些业务特征抽象成了 A 组件,而把另一些特征抽象成了 B 组件,如果把 B 组件的业务逻辑改得都不像 B 的了,那么是否更应该寻找 C 组件来使用呢?
26、组件之间的灵活性、易用性、稳定性有没有相对要好的平衡点?
可以给默认值。不管是样式的默认值,还是插槽的默认值,都应该设置一个能够满足 50% 以上用户需求的默认值。
27、在小程序中,如果没有明确理由,尽量不要设置固定的高和宽
其实对一个元素定高和定宽,是人类一种固有思维。这个时候需要转变下思维,当面对一个元素时,确实是想设置高和宽,但是此时应该想想有没有必要设置,也即如果不设置高和宽会出现什么?
如果不设置高宽就能满足需求,那么优先考虑不设置高宽。有理由就可以设置,比如这个元素太小了,我需要把宽度高度拉大点,便于用户点击,提高用户体验。
固定高宽有两个问题:
- 高和宽并不是那么容易直接从设计图得到,就算是具体量出来也是非常麻烦与不准确的;
- 很多情况下,设置了高宽,后期维护是非常困难的。虽然 css3 中的 calc 帮助开发者快速计算剩余高或宽,但是很多时候还是让元素自适应,而不是设置成固定。
28、优惠券
优惠券其实大多难点在于后端,对于前端来讲,主要的难点就在于核算。
优惠券其实是分类型的:
- 满减
- 折扣
- 使用条件
- 全场券
- 品类券
另一个难点在于需要判断过期时间,简单理解可以认为是在时间段内,超出无效。
但其实还有一种就是优惠券并不是在一个固定的时间范围内的,可能是以用户领取这个券的某个时间点开始算起
甚至还有更复杂的条件,总体来说,越大型的电商,计算规则也就越复杂。
29、关于优惠券的相关概念
领取、使用、过期、类型
活动:优惠券活动,包含了 开始时间、结束时间、上架、下架、分类(比如一个活动只能作用于若干个商品)
30、小程序设置背景颜色
看似 backgroundColor 可以设置页面的背景颜色,但实际上这个属性是设置下拉后底部窗口的背景色,而不是页面的背景色。
两种方式:
1、可以在所需页面的最外层 view 的背景色(这种方式可能局限于页面范围,如果其他页面也要呢?)并且缺点也是有的,就是页面最顶端会无缘无故多出来一块背景色的区域(通常在 wxml 中都包裹着一个 view ,如果该 view 的class 为 container,而小程序有一个全局的样式,在 app.wxss 中也有一个 container ,此时就会相冲突,从而导致这个全局样式的部分效果也会作用于该页面);另外一方面,有些 view 与 view 之间会无缘无故多出一小段距离的背景色,小程序的页面如果页面有图片时,会自带一个间距,这个间距大概在 10 rpx,如何消除呢?将该图片的样式改为 flex 布局。也即 display: flex;
2、第一种方式也不会整个页面都铺满设置的背景色的,只会作用于已有内容的 view 上,但需要实现的是整个页面的背景色。小程序页面本身就有一个 page 标签
31、既然前端要尽请求,那么首页的数据为何后端不能一次性返回呢?而要通过多个接口
有以下几种情况,衡量 API 的依据有什么呢?
HTTP 请求数量;每次 HTTP 会引起多少次数据库查询;接口的灵活性;服务器接口的可维护性
- 每一个数据都发送一个请求
- 整个首页发送一个请求
- 有选择的把部分请求合并成一个
32、在 JS 中使用函数式编程
假如返回的是一个数组,那么可以使用 find 方法过滤需要的数据,该方法接收一个函数,如下
dataArray = dataArray.find(d => d.id === '0');
意思是:查找这个数组中 id 为 0 的元素
33、JS 中双等号 与 三等号
双等号在 JS 中会做类型转换,三等号则绝对等于
34、小程序中动态设置图片的高宽
要保持图片的比例,小程序的 image 组件自带有该设置,也即 mode。但这并不是一种非常好的方式,因为 mode 虽然是可以保持纵横比,但是缩放模式还是不能达到最好的效果,所以还是需要动态计算高和宽。
35、如何在小程序中尽量减少 HTTP 请求,减少冗余代码(比如同一个数组要在页面多个地方用到,但需要多次 find)
- this.data.dataArray = 返回的数组 (每次使用都需要判断是否为空,并且页面本身不应该保存数据)
- 保存到小程序的缓存中(不手动清除将永久保存)
- 保存到全局变量(全局变量并不适合存储返回的请求数据,特殊的除外)
以上都不是特别好的方法,并且在整个小程序里面只能保存一份。看到这里,Java 中的类保存数据就又出现了,了解面向对象的都知道,类的对象本身就具有保存数据的功能。
类的对象本身就具有保存数据的功能,这句话是对的,但不确切,为什么?类确实可以保存数据,但是类不能保存状态。
类实例化之后的对象是既可以保存数据,也可以保存状态。
为什么类可以保存数据,但不能保存状态?
class Clazz{ static A = '1'; static B = '2'; static C = '3'; static D = '4'; }
上述定义了一个类,类中定义了若干个属性,由于这些属性都是 static ,所以是和类相关的,也就是说在使用 Clazz 的时候是不需要实例化该类的,属性的值是可以直接用的。
但是状态的数据可能是不同的,如果状态全部都是相同的,就谈不上状态了。那么怎么保存状态呢?
假如实例化了 Clazz,并且使得 t.age = 1,也即
const t = new Clazz(); t.age = 1; const t2 = new Clazz(); t2.age = 2;
同样的属性,由于对象不同,它保存了两个不同的数据状态。
再来看看数据获取
Clazz.age = 1; Clazz.age = 2;
执行完这两行代码之后,age 肯定等于 2,所以肯定是不能同时保存1 和 2 的。
最好还是不要使用类的 static 保存状态或者数据,否则扩展性是不够的,因为 static 定义之后,全局中这个变量的值只能有一份。
36、JS 操作与对象模型
既然建立了一个业务对象,那么和这个对象有关的操作,都应该放到这个对象模型里面,而不要让这些代码都零散的放到页面的 JS 中
37、符号特征
业务对象很多时候是具有一个符号的特征,有些复杂的业务必须抽象出多个具有符号意义的业务对象才能帮助开发者思考。
38、Spu 和 Sku
Standard Product Unit - 标准化产品单元:
- 商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。
SPU 是商品信息聚合的最小单位。 例如:品牌苹果+型号:5s可以确定一个产品,即SPU 再加上颜色白色,尺码4.0,即表示一个SKU SPU + 颜色 + 尺码,就是一个SKU,SKU是从属于SPU的关系
Stock keeping Unit - 库存量单位:
- 对每一个产品和服务的唯一标示符,该系统的使用SKU的植根于数据管理,使公司能够跟踪系统,如仓库和零售商店或产品的库存情况。他们往往在分配和商人的水平上连载。
一款商品多色,则是有多个SKU,例:一件衣服,有红色、白色、蓝色,则SKU编码也不相同, 如相同则会出现混淆,发错货。 SKU,定义为保存库存控制的最小可用单位,例如纺织品中一个SKU通常表示:规格、颜色、款式。 STOCK KEEP UNIT,这是客户拿到商品放到仓库后给商品编号,归类的一种方法。
39、是不是所有的类方法都要加 static ?什么时候应该加,什么时候不应该加?
static 是和类相关的,类是不具有保存状态的,由此只需要考虑数据是否需要保存状态即可。
也即,是通过类直接调用,还是实例化之后再调用。
是否应该 static 不应该由代码层面推测,而是应该从面向对象的本质去推测。
40、是不是方法都需要加 async 和 await ?
首先讨论下,加 await 是为了什么?是为了在方法中等待拿到此次请求返回的结果,但如果是直接 return 也即没有后续代码(比如赋值)的情况下,可以不加 await。
类方法中有没有必要加 async?async 是强制保证一个方法最后返回一个 promise,但如果一个方法不需要强制保证返回 promise,则不需要加(或者统一封装的 HTTP 请求方法已经强制保证了返回 promise 的情况下)
41、小程序组件中变量的驼峰方式
在组件 JS 中的 properties 定义的驼峰命名方式的变量,调用方传递的变量名需要 snake 形式,比如
在组件 JS 中的 properties 定义的是: sublimeText,
传递时,应该写成:subline-text="{{}}"
42、为什么通常外部样式类需要加 important?
1、小程序的自定义组件中明确说明了,外部样式类和组件内部已经设置的默认样式之间的优先级关系是不确定的,不确定的原因没有明确说明,所以这时候就需要加上 !important
2、HTML 中class 如果设置两个,后面的会覆盖前面的,但小程序中如果 class 的值有两个,却依然是不确定的。
3、并不是所有的外部样式类都需要加 !important 来提高优先级。css 中,每一个 css 样式都是有权重的,凡是能够提高外部样式权重的方案都是可以的,最简单粗暴的是 !important 。
43、小程序 scroll-view 使用 flex 布局
需要在标签属性上开启 enable-flex
<scroll-view enable-flex class="scroll-view"> </scroll-view>
view 是不需要的
44、使用 scroll-view 开启横向滚动后,组件下方出现很长一段空白区
scroll-view 是小程序的原生组件,可以大致认为和 HTML 里的元素行为是一致的,比如 view 通常认为是 div,那么 scroll-view 是一个功能更加丰富的 view 组件,但是 scroll-view 不能和 HTML 的元素做对等。
一般地,如果在没有设置 view 组件的高度时,那么这个容器应该是由内部的元素撑开,也就是说 view 不是固定的。
scroll-view 与 view 是有点不一致的,设置了 flex 布局后,高度大致与没设置样式之前是一致的,也就是说空白区域是竖排留下的空间(仅推测)。
怎么解决?
- 设置 scroll-view 的高度(不推荐)
- 可以在 scroll-view 里再包裹一个 view,并设置 flex 的 row 排列,将 scroll-view 的 enable-flex 去除
45、scroll-view 不要限定宽度
如果固定宽度(就算是100%),下方会出现滚动条,因为scroll-view 中的元素不可能紧挨在一起,总得设置元素之间的距离,那么整体宽度100% 之后会再加上元素之间的内边距,一定是大于 100%的。
既然宽度是 750rpx,知道左边或者右边的内边距,用总体的内边距减去左右两边的内边距,然后再设置 width ,这样也是可以,但这样非常麻烦(频繁计算)。
另外一种就是更改 scroll-view 的盒模型,也即更改 box-sizing 为 border-box,这样就不会再计算左右内边距了。
46、text 文本太长,文本省略的三种速思路
- 可以在 js 中对字符进行截断
- 直接 css 样式处理
- wxs 处理(wxs 是小程序很重要的功能,非常适合逻辑处理非常复杂的时候)
47、wxs 有何不同
wxs 其实也是 JavaScript,那么问题来了,
1、wxs 写的代码和在 JS 中写的代码有何区别?
- 主要区别在于 wxs 的 js 代码只支持 ES5,类似 const、``、export 这些都是不能用的
2、为何会出现两种 JS?
- 之所以会出现两种 JS,是因为小程序的逻辑层(JS 中的代码实际上是运行在逻辑层的环境里)和渲染层/视图层(wxs 的代码是运行在渲染层的环境中)是两个分离的环境。
- 在 HTML 的页面中写 JS 是可以的,但在小程序的 wxml 中直接写 JS 是不允许的,需要使用 wxs 。
48、小程序的运行环境其实有三套
小程序可以认为是以 Web 技术为驱动的新型技术,但不能简单的认为小程序就是 Web 技术
IOS
小程序在 IOS 的逻辑层主要是 JavaScriptCore,而视图层是 WKWebView
Android
在 Android 上 的逻辑层是 V8 引擎,视图层是 XWeb (也是基于Chrome 内核进行渲染的)
PC
在 PC 上逻辑层是 NW.js,视图层是 Chromium 内核渲染的
49、WebStorm 自定义代码片段
file -> settings -> Live Templates -> 点击右侧 + ,选择 template group,输入 group name。
单击选中新建的 group,再点击右侧 + ,选择 live template,填写 Abbreviation(快捷字母)比如 fc ,
description 填说明, Template text 填需要粘贴的代码片段,
在模板片段下方的 No applicable contexts 的提示中点击选择应用的上下文。
最后在右下方的 Options 的 Expand with(默认是 输入快捷字母后按下 Tab 键粘贴)选择需要触发粘贴事件的快捷键
那么在file 中就可以输入 fc,然后按下Tab 键,即可粘贴定义好的代码片段。
50、外部样式类的使用
如果要实现外部样式类,那么需要在组件 JS 的 Component 下定义一个特别的属性:externalClasses: [''],
这个属性是个数组,每一个元素都是代表着定义的外部样式类,类名字自定义即可。
为什么要定义外部样式类属性呢?
- 样式并不是一定是自定义组件固定的,它需要由开发者定义好之后传到自定义组件中引用,就好比定义 properties 中的属性一样,需要接收后才能使用
如何在自定义组件中使用?
- 直接将定义好的外部样式类数组中的元素名写在组件的 class 上即可,比如在自定义组件中有一个 scroll-view组件
<scroll-view class="scroll-view 外部样式类名"> </scroll-view>
51、新监听器Observers
可以监听单个属性,比如
properties: { banner: Object },
observers:{ "banner":function(banner){ } },
属性名和 observers 中保持一致,括号中的 banner 是监听器监听到 properties 中banner 的变化之后传递的参数
可以监听单个属性,比如
properties: { banner: Object, Theme: Object },
observers:{ "banner, Theme":function(banner, Theme){ } },
52、在 view 中使用点击态
样式动画类:hover-class
动画响应时间:hover-stay-time(毫秒)
自定义组件具有封闭性,直接在 app.wxss 中写样式,然后引入,不能直接作用在自定义组件中。
53、编程原则之一
多用return提前结束函数,少用else
54、小程序两个页面传参
1、通过路由的 URL 后附加查询参数
- 但参数非常复杂(不是简单的数字或字符串,而是 JS 对象),怎么传?以前小程序没有状态管理的功能,使用 全局变量的方式也不合适
2、现在小程序可以通过 两个页面之间的事件的方式进行传递,EventChannel
- 虽然小程序支持 EventChannel ,但它的事件模型比较难以理解,用起来不是那么的舒服
55、SPU 与 SKU
56、两个编程的技巧
1、当我们去思考一些复杂问题的时候,通常会觉得思维转换不过来。复杂问题都有一个特性,就是它的思维周期非常长,可能是需要思考一个小时或者几个小时。人的记忆力又是有限的,那在这样的场景下,必须要把这些复杂的逻辑问题找到自己熟悉的事物对应起来,也就是必须要借助一些形象化、符号化的东西来辅助思考。最重要的是,要注意变量的命名,这些变量的命名必须是具有一些形象化、符号化意义的,因为此时变量的命名早已脱离了规范性的范畴。
2、要善于把核心的问题能够抽象出来。当拿到一张图时,设计师不会考虑是如何做出来的、技术难度是什么,所以拆分设计图,也是能力所在。
57、自定义组件的理解
1、可以粗浅的认为:自定义组件要么就是为了封装在一起,然后把代码进行分离。
2、另一个就是自定义组件是为了复用。
3、但自定义组件在面对复杂的问题时,它同样是一种很好的辅助思考的工具。
58、变量命名的思考
带有强烈领域对象的命名并非不可以,处理简单逻辑的时候确实可以带上领域对象作为变量的前缀命名。
但是在处理复杂问题的时候,带有领域对象前缀命名的变量其实是有很多个可描述的角度的。