e.target与e.currentTarget
- 如果你点击的是添加事件元素的子元素,就用
e.currentTarget
来获取父元素的值,用e.target
来获取子元素的值,如果没有子元素,就在本元素就不用说了,e.target===e.currentTarget
- e.target 指向的是触发事件监听的对象。
- e.currentTarget 指向的是添加监听事件的对象。
注意事项
-
文字之间是有一些空白间距的 如果要消除间距就需要设置行高来进行消除
-
容器设置 flex 容器本身不会取消自身的块状特性 但 子元素 会消除块状特性
-
组件的封装性 开放性 粒度
哪些是需要封装在内部的内部数据 哪些是需要开放出来的
一个组件可以被成非常简单 也可以被拆分成非常复杂的功能 看自己
组件的复用性, 代码的分离性和维护性... 低耦合
-
小程序 请求 数据的时候
wx.request
如果请求报异常了 也是会走success的,只有网略中断才会执行 fail 报err
封装http请求 基于wx.request进行二次封装
-
在
import
里面 要使用 相对路径 不要使用绝对路径 -
在组件的时候 是可以使用 相对路径的
-
新建一个
config.js
文件export const config = { api_base_url = "https://...", } // 或者可以使用 一定要使用解构 如果想要改变名称 使用 as 就可以了 export {config} || export {config as con}
-
新建一个
util -> https.js
文件来进行封装wx.request
// 不建议使用 request 建议使用es6的 modules 进行导入导出 import { config } from "/config.js"; // 标识一个错误码 统一管理配置 const tips = { // 给一个 默认的错误提示 1:'抱歉,出现了一个错误', 1005: 'id无效', 3000: '不存在' } class HTTP { request(params) { // url data method // params 里面的参数是可以 有的也可以不需要传的 需要做兼容 if(!params.method) { params.method = "GET" } wx.request({ url: config.api_base_url + params.url, method: params.method, data: params.data, header: { 'content-type': 'application/json', 'appkey': 'config.appkey' }, success: (res) => { let code = res.statusCode.toString() if(code.startsWith('2')) { // 因为有的地方 不需要有success回调 所以可以写下面两种方式 if(params.success) params.success(res.data); // 或者可以这么写 params.success && params.success(res.data); } else { // 最简单的是可以这样处理的 wx.showToast({ title: '错误', icon: 'none', duration: 2000 }) } }, fail: (err) => { // 但是如果要是有一个 好的用户反馈的话 // 不仅需要 状态码 而且需要一个 错误码 // 然后就可以有一个 比较好的用户体验 let error_code = res.data.error_code this._show_error(error_code) } }) } // 这个下划线 就表示这个方法 是一个私有方法 然后是 强行私有 _show_error(error_code) { if(!error_code) { error_code = 1; } wx.showToast({ title: tips[err_code], icon: 'none', duration: 2000 }) } } export {HTTP}
-
具体到页面
import {HTTP} from "/http.js" // 因为 http 是一个类 如果想要引用类里面的一个实例方法 就需要 new 一个来进行 // 如果是 function 里面 函数直接 prototype的话 类是可以直接点的 let http = new HTTP(); // 然后调用的时候 就直接 http.request({ url: '', success(res) => { console.log(res) } })
> > > > > > > > > > > >
下面是一种比 封装 https 更好的方法 引用module的概念
-
根目录 -> modules -> 创建单独文件的引用
// 单独文件的封装 import { HTTP } from '../util/http.js' // class 单独文件jsModel extends HTTP{ class ClassicModel extends HTTP{ getLatest(sCallback) { // 之前是因为实例化了之后 去 http.request的 // 现在是因为继承了之后 所以直接 this.request就可以了 不需要实例化 this.request({ url: '', success(res) => { console.log(res) sCallback(res); } }) } } export {ClassicModel}
-
然后在自己的页面中 调用该方法
import {ClassicModel} from '../../models/classic.js'; let classic = new ClassicModel(); onLoad: function (options) { // 使用回调函数的话 在上一个函数return之后是无效的 classic.getgetLatest((res)=> { this.setData({ classic: res }) }); }
给子组件设置值
<!-- classic.wxml --> <!-- 想要给like设置什么值 就需要把like需要设置的值 卸载花括号里面 --> <v-like like="{{classic.like_status}}" count="{{classic.fav_nums}}">
properties: {
// 这里面的值都是外界传递过来的
like: {
type: Boolean,
},
count: {
type: Number
}
},
/**
* 组件的初始数据
*/
data: {
// 这里面的值都是私有的
}
建议: 凡是要被wxml使用的变量 建议都放到data里面去声明一下
组件
-
要是按照封装组件来说,组件只完成自己的业务逻辑,比如
-
心形组件
只完成自身相关的业务逻辑,+1 -1 切换样式等等, 其余的 不通用的业务逻辑都是通过 父组件来调用的也是由父组件来定义的。 - 业务逻辑一定要写在组件的使用方,要确保组件的 粒度
-
-
创建自定义事件
methods: { onLike: function(event) { // 自定义事件 let like = this.properties.like; let count = this.properties.count; count = like ? count-1:count+1 this.setData({ count: count, like: !like }) // 激活 let behavior = this.properties.like ? 'like' : 'cancel'; // this.triggerEvent('like', {自己定义的属性 比如behavior可以进行传递}, {一般不使用 // 只能有三个值 bubbles: 事件是否冒泡 ...等等}) this,triggerEvent('like', {behavior: behavior}, {}); } }
<!--class.wxml--> <v-like bind:like="onLike"></v-like> <!--class.js--> onLike: function(event){ console.log(event); let behavior = event.detail.behavior; }
models -> like.js
新建文件import {HTTP} from ".././"; class LikeModel extends HTTP { like(behavior, artID, category) { // type是js的保留关键字 所以一般不要使用 let url = behavior == 'like'? 'like': 'liek/cancel'; this.request({ url: url, method: 'POST', data: { art_id: artID, type: category } }) } } export {LikeModel}
-
properties
是没有初始化数据是可以通过 定义类型来初始化的data
则是得通过0 "" 等来定义的小程序最后会把这两个最终合并成一个对象
千万不可以两个对象设置重名的对象 否则最后那个会有一个覆盖的现象 p会覆盖d
-
封装组件最难的就是 看业务逻辑是组件内部还是父组件里面
// Component Component({ properties: { index: { type: String, // observer 函数就是当改变了值的时候。微信小程序会主动调用observer函数 // 同时会传递三个参数 observer: function(newVal, oldVal, changePath) { // newVal -> 8 oldVal -> 0 let val = newVal < 10 ? '0' + newVal : newVal; this.setData({ index: val }) // 小程序会判断type.然后如果是数字类型 就会去掉前面的0 // 如果是修改index为string类型 那么会出现无限递归调用的情况 // 8 -> 08 -> 008... 然后因为是string所以一直会监听变化 // 如果是 number. 08 和 8是没有变化的 所以就没事但是会自动去掉0 // 解决方法就是: // 在data里面 绑定一个 _index 的值 然后去调用, 然后index的observer改变了之后 // _index之后 就不会触发监听了 然后类型设置string 页面绑定_index // 或者调用 wxs 来进行设置 } } }, data() { months: [ '一月', '二月', '三月'... ] }, attached: function() { let data = new Date(); let year = data.getFullYear(); let month = date.getFullMonth(); this.setData({ year: year, // 因为获取的月份是比实际月份少一个月 所以可以使用数组的这种方法 month: this.data.months[month] }) } })
-
注意: 不要在函数的observer的值调用自己 然后可能会触发无限调用
-
注意: 组件不允许使用id选择器
-
注意: flex的
align-items: baseline;
这个属性是确保属性是可以底部对齐的。Baseline 对文字是不生效的。所以就使用了 标签来进行设置
-
注意: 触碰区域实际中会比 图片的触碰区域大一些的, 用户体验问题
--要么就是切图的时候 切大一些 --要么就是自己写代码扩大触碰区域
-
在设置flex的时候,很多奇怪的现象都是没有设置宽度造成的
所以 如果你认为一个元素是100% 所以最好写一个显示的100% 进行设置
组件传递方法
-
子组件,绑定事件 bind:tap="onLeft" bind:tap="onRight"
然后在 js 的时候在methods里面绑定 onRight onLeft 绑定
methods: { onLeft: function(event) { this.triggerEvent('left', {}, {}) } }
-
父组件,绑定事件 bind:left="onLeft" bind:right="onRight"
这里只能监听事件的自定义事件,并不能监听 事件的tap事件
然后这里只是图片切换了,但是还是会有事件的触发,所以,有两种
- 组件依然产生一个 向左 的事件,但是在page页面中忽略掉这个事件
- 组件内部如果判定当前期刊是最新的一期的话,然后向左 的事件根本就不触发这个事件。
if(!this.properties.latest) ... // 这样就判断 如果是不是最后一起才会触发这个事件
小程序的继承 复用代码 — behavior(行为)
-
新建一个文件
.js
let classicBeh = Behavior({ // 这样可以进行抽离 组件中公用的方法 然后和component 一样的定义方法 // 因为这个是个构造器 所以可以定义一个变量来接受它 properties:{ img: String // 因为这几个属性都是共用的 所以就直接提取出来了 }, data: { }, methods: { }, // 生命周期函数 也是可以提取出来的 }) export { classicBeh }
-
然后在需要使用的页面中 进行导入import
import {classicBeh} from '..////...' Component({ behaviors: [classicBeh], // 一个组件可以继承多个 behaviors // 多继承 // 相对于属性方法来说的话 // 如果都一样的话,子组件中定义的会覆盖继承的 // 如果调用的多继承 那就是 最后一个继承的 的优先级会高 // 如果是相对于生命周期函数来说的话 // 小程序会都调用一次 })
-
es6的快捷用法
// 在获取数据的时候 直接 ...res。 就可以替代 this.setData({classic: res})了 // 但是没有扩展运算符 还是比较好一些的 更好的 语义化 // ``模版字符串 替换变量 `${a}123` 或者 替换函数 `${this.test()}123`
-
在组件中。复用wxss 代码…
@import "../common.wxss"
万物皆组件,,,很慌... -
在组件中,复用wxml代码… 如果需要
template
模版就可以了
小程序中音乐播放
-
背景音频播放管理
const mMg = wx.getBackgroundAudioManager(); methods: { onPlay: function(event) { if(!this.data.playing) { this.setData({ playing: true }) mMg.src = this.properties.src; }else{ this.setData({ playing: false }) mMg.pause(); } } }
-
切换 期刊 整个 music 组件都恢复到默认状态
父子组件的通信
-
数据绑定 用于父组件向子组件传递数据
-
事件 用于子组件向父组件传递数据,可以传递任意数据
-
如果以上两种方法都不满足 父组件可以通过
this.selectComponent
方法获取子组件实例对象,这样就可以访问到其他组件的任意数据和方法了 -
注意:
hidden
是不会触发detached() 这个函数的;只有
wx:if
执行之后 就会执行一次完成的生命周期 然后hidden
的话就可能不会执行
代码风格
-
对象方法属性简写
-
function 简写 test() {}
-
import 引入的时候分隔开 import {换行 value 换行 }
-
多使用class 而不是使用 function prototype
-
多使用import export 不使用require
-
模版字符串 箭头函数
-
最后一个对象属性 不需要加逗号
-
能用const。就不用let 能用let 就不用var
const 常量或者说你认为这个值不应该改变或者提醒别人这个不应该被改变
定义一个函数 const 也是比较合理的
-
airbnb
前十个 以及 12 13 16.
Promise
-
之前常用的就是
callback
回调函数, 很容易 陷入到了 回调地狱 中 也是剥夺了函数return
的能力 -
promise
就正好可以解决回调地狱
和return
。promise的好处
多个异步等待合并
不需要层层传递callback
…等等;纯粹的回调的话。就是每一层都需要传递 callback 的回调函数的。
-
async await
是最佳的解决 回调函数的 方案,是promise的语法糖
// Promise. 是一个对象 不是一个函数
// 对象是可以保存状态的。而函数就不可以 结果是需要立马返回的,只有闭包函数可以
// 1. 异步代码 写在Promise的函数中
const promise = new Promise((resolve, reject) => {
// pending 进行中 fullfiled 已成功 rejected 已失败
// 通过 resolve reject 可以修改状态
wx.getSystemInfo({
success: res => resolve(res);
// 一旦调用 reslove reject修改了状态之后 整个状态就凝固了...
fail: error => reject(error);
})
})
promise.then(res => console.log(res),
error => console.log(error))
- 封装class的http请求
class HTTP {
// 参数如果写成
// request(params) // 这个参数表示对象是不合理的 语义不明确
// 这种传递参数一个是语义明确 一个是可以看 是否必传 如果没有传递 就会有默认值
// 一个原则 必填参数 必须在默认可选参数之前
requert(url, data={}, method="GET") {
return new Promise((resolve, reject) => {
this._request(url, resolve, reject, data, method)
})
}
_request(url, resolve, reject, data={}, method="GET"){
wx.request({
url: "...",
method: method,
data: data,
header: {
...
},
success: (res) => {
if() {
resolve(res.data);
}
else {
// 这里只是告诉一个状态 可以不需要传递参数
reject()
}
},
fail: error => {
// 也不需要传递参数
reject();
}
})
}
}
// 返回的是promise,所以就保存了状态,然后就可以进行return。
// 如果想要使用 之前的 那个习惯进行传递参数的话 可以进行这样设置参数
request({url, data={}, method="GET"}){} // 可以写成这样的 对象参数的形式
// 然后在调用的时候 就可以这样进行传递了 使用解构语法 可以很好的进行传递参数
return this.request({
url: "..",
data: {
name: "1",
age: 18
},
method: 'POST'
})
// 就不需要这样传递参数了
return this.request('url..',{name:"1",age:18},'POST');
- 多次调用
api
所以要用到promise
// 错误代码
api1.then(res => {
console.log(res);
api2.then(res) => {
console.log(res);
api3.then(res) => {
console.log(res);
}
}
})
// 正确代码 promise 是可以进行.then的
api1.then(res => {
console.log(res);
return api2;
})
.then(res => {
console.log(res);
return api3;
})
.then(res => {
console.log(res);
})
-
思考
promise
是可以进行链式调用的 然后小程序中好多都不是 promise 的api,所以就可以进行闭包的封装。但是…不会. 封装小程序异步操作的api
code_笔记
-
wx:for 一般不会重新定义 item 除非在data中的时候 你已经定义了item变量 会有冲突 才会修改这个值
block
可以被理解成一个() 因为block是不会被页面渲染的如果使用 wx:for 那么就添加一个block 标签进行包裹. 也更加的好理解
-
fixed
页面布局 +z-index
可以实现 页面固定显示 -
wx:key
的写法 是不需要写 {{}} 也不需要写 item.id 是直接写id就可以啦// 针对列表下面的元素 object 类型的 <block wx:key="id" wx:for="{{books}}"> <v-book book="{{item}}" /> </block> // 如果本身就是数字 或者 字符串的话 <block wx:key="*this" wx:for="{{books}}">
-
在组件内部 编写 跳转路径的逻辑,大大降低了组件的通用性,这样也是非常不方便。
如果该组件 仅仅只是服务于项目,那么这个组件就叫做
项目组件
所以这养写也是可以的。如果是要求组件的通用的 建议还是直接
triggerEvent
来传递给外部一个值。 -
组件的复用…之标签的引用
如果不采用 flex 布局,那么采用自动换行的元素的
margin-bottom
是不生效的。 -
组件的高级用法
-
组件的设计思想 插槽
slot
的使用<!--tag组件html--> <view class="container"> <text>{{ text }}</text> <!--slot 插槽 这个就是如果需要 就从外部传进来标签就可以了 空插槽的话是不会被显示的 相当于组件.js里面的properties的属性作用 可以从外部传递标签进来 --> <slot name="after"></slot> </view>
<!--父组件html--> <view class="container"> <block wx:for="comment"> <v-tag text="{{item.contents}}"> <!--如果想要将 v-tag 里面传递进去一个标签 就需要设置一个闭合进行包裹 然后传递标签 然后在 text里面加一个属性 slot--> <text slot="after">{{'+'+item.nums}}</text> </v-tag> </block> </view>
然后在子组件的js文件中启用一下这个 slot
Component({ // options 这个属性是用来启动插槽的 options: { multipleSlots: true }, properties: { text: String, }, })
设置 前两个不一样的样式 该怎么设置
<v-tag tag-class="{{index==0?'e-tag1':'' || index ==1?'e-tag2':''}}"></v-tag>
-
-
<text> aaa\nbbb </text>
text
组件是默认可以进行 解析\n
换行的text
组件内部的文本,最好还是别进行 enter 的操作,他会解析这个换行进行显示的 -
在浏览器或者小程序中,network的里面,
preview
和response
里面,response
是服务器传递过来的原始数据,然后如果文本内容有\n
在服务器编译的时候会 将其编译成\\n
然后最终解析成\n
,解决这个方案,就是匹配正则。// 解决方案1: // 就是对 服务端返回来的数据进行处理 然后将其渲染到页面上去。 // 解决方案2: // 编写一段函数 function 然后使用这个函数来对其进行解析,之后再遇到调用就可以了 // ** wxs **
网友评论