小程序学习笔记

 

一、小程序应注意的一些问题

1. 小程序设计尺寸单位的注意事项

(1)px和rpx

  • 在系统中显示的尺寸和小程序中的尺寸是不一样的。
    比如系统中显示like图片的大小为26×23,在iPhone6中不能直接设置成26×23(单位为px)
    而应将尺寸按相应的比例变大或缩小(不同机型比例不一),如这里将其减半,高为13px,宽为11.5px;

注意:

  • (1)px是固定的(即在不同机型中显示的大小都是一样的),rpx是自适应的(会随机型的大小进行相应的变化)。
  • (2)根据不同情况选择使用px,还是rpx。
    一般而言,字体大小一般是不用随机型变化的,可以选择px;而图标大小是根据机型变化的,所以选择rpx。

2. let和var

  • let表示声明一个作用域被限制在块级中的变量、语句或者表达式。
  • var表示其声明的变量只能是全局的或者整个函数块的。
if(true){
var a=1;
let b=2;
}
console.log(a); // 输出1
console.log(b); // 报错:b未被定义

报错具体如下:

b is not defined; [Component] Event Handler Error @ components/like/index#onLike
ReferenceError: b is not defined

原因是:if语句就是一个块,a是一个全局(整个函数块)的变量,而b是一个作用域在块级中的变量,所以a的值能显示出来,而在if块外部读取b的值时会报错。

3. 解决this在回调函数中指代不明的问题

(1)传统方法:在回调函数之外保存this的状态

Page({
  /**
   * 页面的初始数据
   */
  data: {
    test:123
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    console.log(this.data.test) // 输出:123
    let that = this //确定this的指代的传统方法是在回调函数外将this的状态先保存下来,然后在回调函数中使用that变量。
    wx.request({
      url:'http://bl.7yue.pro/v1/classic/latest',
      header:{
        appkey:"zCr1cd2jqewqeq"
      },
      // 回调函数
      success:function(res){
        console.log(that.data.test) // 输出:123
        console.log(this.data.test) // 报错
      }
    })
  },
  ...

报错详情:Cannot read property ‘test’ of undefined;at pages/classic/classic onLoad function;at api request success callback function
原因: this在回调函数之外是有值的,在这种回调函数之内是无值的
在回调函数里面,由于函数的作用域发生了改变,所以回调函数里面的作用域是不明确的,所以不能始终认为this永远指向的是我们这里的Page里面的这个object 。

(2)使用ES6中的箭头函数

Page({
  /**
   * 页面的初始数据
   */
  data: {
    test:123
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    console.log(this.data.test) // 输出:123
    wx.request({
      url:'http://bl.7yue.pro/v1/classic/latest',
      header:{
        appkey:"zCr1cd2jqewqeq"
      },
      // 回调函数
      success:(res)=>{ // 箭头函数
       console.log(this.data.test) // 输出:123
      }
    })
  },
  ...

4. Cannot read property ‘startsWith’ of undefined

具体错误如下:

Cannot read property 'startsWith' of undefined;at pages/classic/classic onLoad function;at api request success callback function
TypeError: Cannot read property 'startsWith' of undefined

原因: 小程序是支持startsWith这个方法的,这里出现这个问题的原因是

 success:(res)=>{
                let code = res.statusCode
                if(code.startsWith('2')){
                    
                }else{

                }
            },

这里的statusCode是一个数字类型,并不是String类型,所以数字类型是不存在这个startsWith的方法的。只要将数字类型转换为String类型就可以解决上述错误

  let code = res.statusCode.toString()
                if(code.startsWith('2')){
                    
                }

5. Promise(获取异步操作的消息)

Promise 对象 - ECMAScript 6入门

 

6. 千万不要在observer中修改自身属性

properties: {
    index:{
      type:String,
      observer:function(newVal,oldVal,changedPath){
        console.log(newVal)
        console.log(oldVal)
        console.log(changedPath)
        let val = newVal<10?'0'+newVal:newVal
        this.setData({
          _index:val   //既然修改自身属性会触发observer,那我们可通过修改其他属性(变量)以避免这种情况的发生
        })
      }
    }
  },

  /**
   * 组件的初始数据
   * (1)如果想对data下面的数据进行初始化的话,不能像properties使用类型(如Number,String)进行初始化
   * 而应使用如下方式进行初始化:year: 0, month:''
   * (2)小程序会合并properties和data中的变量,所以properties和data中不要出现相同命名的变量,
   * 这样会产生变量的值被覆盖
   */
  data: {
    year:0,
    month:'',
    _index:''
  },
  • (1) 千万不要在observer中修改自身属性,这样对导致无限递归调用!!
    observer这个监听函数是在自身属性被修改的时候被触发,如果在observer监听函数中调用setData去修改自身属性,会导致observer被触发,observer被触发之后,其中的setData又会被执行,从而造成无限递归调用的局面。
  • (2) 为什么当index类型设置为Number时,不会产生无限递归调用?
    因为当类型为Number时,即使当index的新值小于10(假设为8),则val为08,因为index的类型为Number,则如果设置index:val,小程序会把08改成8,所以index的值并没有改变,所以不会触发observer。

7. behaviors(组件间代码共享)

(1)基本概念和定义

  • behaviors是用于组件间代码共享的特性,类似于一些编程语言的“mixins”或“traits”。
  • 每个 behavior 可以包含一组属性、数据、生命周期函数和方法,组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。
  • 每个组件可以引用多个 behavior 。 behavior 也可以引用其他 behavior 。
  • behavior 需要使用 Behavior() 构造器定义。

(2)简单示例

// Behavior的编写和Component类似/**
 * 使用Behavior的目的是放置组件共用的代码部分,以供组件调用,从而减少代码的冗余
 * 
 * Behavior是一个构造器,它将构造一个行为,其构造的行为可以定义一个变量来接收它
 */let classicBeh = Behavior({
    properties:{
        img: String,
        content: String,
    },

    data:{

    },

    attached:function(){

    },

    methods:{

    }

})
export {classicBeh}
// components/classic/essay/index.jsimport {classicBeh} from '../classic-beh.js' //先导入classicBeh

Component({
  /* 在组件中定义一个behaviors属性来显式地指定当前组件继承了classicBeh,其属性的值是一个数组的形式,
  因为组件可以继承多个行为*/
  behaviors:[classicBeh],
  /**
   * 组件的属性列表
   */
  properties: {
    //当前组件上面继承了classicBeh,classicBeh包含了一个公共的properties,这边也就包含了这些公共的properties,即img和content这两个属性
  },

  /**
   * 组件的初始数据
   */
  data: {

  },

  /**
   * 组件的方法列表
   */
  methods: {

  }
})

当组件触发 attached 生命周期时,会 依次触发 classicBeh 中的 attached 生命周期函数和 Component 中的 attached 生命周期函数。

(3)字段的覆盖和组合规则

组件和它引用的 behavior 中可以包含同名的字段,对这些字段的处理方法如下:

  • (1) 如果有同名的属性或方法,组件本身的属性或方法会覆盖 behavior 中的属性或方法,如果引用了多个 behavior ,在定义段中靠后 behavior中的属性或方法会覆盖靠前的属性或方法;
  • (2) 如果有同名的数据字段,如果数据是对象类型,会进行对象合并,如果是非对象类型则会进行相互覆盖
  • (3) 生命周期函数不会相互覆盖,而是在对应触发时机被逐个调用。如果同一个 behavior 被一个组件多次引用它定义的生命周期函数只会被执行一次

更多详细内容可以阅读behaviors

8. 条件渲染 wx: if 和 hidden

(1) wx: if

在框架中,使用 wx: if="{{condition}}" 来判断是否需要渲染该代码块,condition为true则渲染,否则不渲染:

<view wx:if="{{condition}}">True</view>

也可以用 wx:elif和 wx:else 来添加一个 else 块:

<view wx:if="{{length > 5}}">1</view><view wx:elif="{{length > 2}}">2</view><view wx:else>3</view>

(2)block wx:if

因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 <block/> 标签将多个组件包装起来并在上边使用 wx:if 控制属性

<block wx:if="{{true}}">
      <view>view1</view>
      <view>view2</view></block>

注意: 
<block/> 并不是一个组件,它仅仅是一个包装元素不会在页面中做任何渲染只接受控制属性

(3)wx:if vs hidden

  • (1) 因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染

因为wx:if在条件值切换时,可能会销毁或重新渲染组件,所以能够销毁的时候能够触发组件生命周期函数detached在组件实例被从页面节点树移除时执行),而hidden是不可以的,因为hidden无论是显示还是隐藏,组件一直都在渲染的,不存在销毁操作。

  • (2) 同时 wx:if也是惰性的,如果在初始渲染条件为 false框架什么也不做,在条件第一次变成真的时候才开始局部渲染
  • (3) 相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏
  • (4) 一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if较好。

简单地说:
(1)wx: if 是条件为true的时候显示hidden是条件为false的时候显示
(2)wx: if 在隐藏的时候不渲染hidden在隐藏的时候仍然渲染,只是不显示而已。
(3)条件频繁切换hidden条件不怎么变化wx: if

(4)自定义组件巧用hidden属性实现隐藏和显示

  • classic.wxml
<!-- classic.wxml --><!-- type表示作品类型,100,200,300分别表示电影,音乐,文章 -->
 <yuan-movie hiddenValue="{{classicData.type!=100}}" img="{{classicData.image}}" content="{{classicData.content}}" /> 
    <yuan-music hiddenValue="{{classicData.type!=200}}" img="{{classicData.image}}" content="{{classicData.content}}" />
    <yuan-essay hiddenValue="{{classicData.type!=300}}" img="{{classicData.image}}" content="{{classicData.content}}"/>
  • classic-beh.js
// classic-beh.js// Behavior的编写和Component类似/**
 * 使用Behavior的目的是放置组件共用的代码部分,以供组件调用,从而减少代码的冗余
 * 
 * Behavior是一个构造器,它将构造一个行为,其构造的行为可以定义一个变量来接收它
 */let classicBeh = Behavior({
    properties:{
        img:String,
        content:String,
        hiddenValue:Boolean,
    },
  ...
})

export {classicBeh}
  • components/classic/movies/index.js
// components/classic/movies/index.jsimport {classicBeh} from '../classic-beh.js'

Component({
   /* 在组件中定义一个behaviors属性来显式地指定当前组件继承了classicBeh,其属性的值是一个数组的形式,
  因为组件可以继承多个行为*/
  behaviors:[classicBeh],
  /**
   * 组件的属性列表
   */
  properties: {
  //当前组件上面继承了classicBeh,classicBeh包含了一个公共的properties,这边也就包含了这些公共的properties
  },
  ...

yuan-music和yuan-essay这两个自定义组件的js代码与上面基本类似。

  • components/classic/movies/index.wxml
<!--components/classic/movies/index.wxml--><view hidden="{{hiddenValue}}" class = "classic-container">
    <image  src="{{img}}" class="classic-img"/>
    <image  src="images/movie@tag.png" class="tag"/>
    <text class="content">{{content}}</text></view>

yuan-music和yuan-essay这两个自定义组件的HTML代码与上面基本类似。

总结:

  • 因为yuan-movie等是自定义组件,在这里直接使用hidden属性,小程序将hidden认为是一个普通属性,因此不支持直接通过hidden实现自定义组件的隐藏和显示
  • 要使自定义组件支持hidden,我们可以组件的内部增加一个属性hiddenValue(名称可以随便取),然后将属性hiddenValue绑定到组件的最外层标签(基础组件)上。
  • 整个流程即是:先在classic.wxml给组件内部自定义的属性hiddenValue赋值,然后将组件内部的hiddenValue属性值绑定到组件最外一层的基础组件(即view)的hidden属性上。
  • 因为基础组件(如view)是支持hidden属性的,所以可以用来根据条件隐藏或显示基础组件。
  • 自定义组件是不能直接支持hidden属性的,因此不能在自定义组件中使用hidden属性来隐藏或显示自定义组件。

9. 组件间通信与事件(难点)

组件间通信与事件

组件间的基本通信方式有以下几种。

  • (1)WXML 数据绑定:用于父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容数据(自基础库版本 2.0.9 开始,还可以在数据中包含函数)。具体在 组件模板和样式 章节中介绍。
  • (2)事件:用于子组件向父组件传递数据,可以传递任意数据。
  • (3) 如果以上两种方式不足以满足需要,父组件还可以通过 this.selectComponent 方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。

10. Component构造器

Component构造器可用于定义组件,调用Component构造器时可以指定组件的属性、数据、方法等

更多详细内容:Component构造器

11. 组件生命周期

(1)组件的主要生命周期

组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。
其中,最重要的生命周期是 created attached detached,包含一个组件实例生命流程的最主要时间点。

  • 组件实例刚刚被创建好时, created 生命周期被触发。此时,组件数据 this.data 就是在 Component 构造器中定义的数据 data 。 此时还不能调用 setData 。 通常情况下,这个生命周期只应该用于给组件 this 添加一些自定义属性字段。
  • 在组件完全初始化完毕、进入页面节点树后,attached 生命周期被触发。此时,this.data 已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。
  • 在组件离开页面节点树后,detached 生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则 detached 会被触发。

参考:组件生命周期

12. javascript良好代码规范参考

JavaScript Style Guide

13. wx: key的作用--提高列表渲染时的效率

使用wx:for时出现警告:warning:Now you can provide attr "wx:key" for a "wx:for" to improve performance.

 <block wx:for="{{books}}" wx:key="id">
                <yuan-book  book="{{item}}" />
 </block>

如果上述代码不加上wx:key,则会出现上述警告。

官方解释:
数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率

换句话说:
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 <input/> 中的输入内容,<switch/> 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。

  • (1)如果我们遍历的列表的子项元素是一个object类型,则我们可以使用object这个对象的属性来作为wx:key的值。(注意:选择的该属性必须是数字或字符串,且值不能重复,即唯一)
<!--books列表的子项为object类型,即为book对象,该对象中满足上述要求的属性为id -->
 <block wx:for="{{books}}" wx:key="id">
                <yuan-book  book="{{item}}" />
 </block>
  • (2)如果我们遍历的列表的子项元素是一个数字或字符串,则wx:key的值填*this即可。
<!-- 假设books这个列表中的子项为数字或字符串-->
 <block wx:for="{{books}}" wx:key="*this">
                <yuan-book  book="{{item}}" />
 </block>

不添加wx:key情况:
如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。

参考:微信小程序:wx:key

14、一个经典错误

(1) Error: Expect FLOW_APPLY_PROPERTY but get another
(2) Error: Expect END descriptor with depth 0 but get another


错误原因:

loading是要在WXML文件中应用,所以要使用setData去更新数据它,小程序才能在WXML及时更新数据。

15. 一个经典思路(page取数据,组件属性传数据并绑定)

在page页面中获得的值(page页面的属性的值),通过组件的属性传递到组件中,然后在组件中进行数据绑定

(1)page页面获取数据

在page页面中通过调用Model中的访问API方法,从服务器中获取数据。

// page页面: book目录下book.js

 onSearching:function(event){
    // 调用keywordModel中的getHistory方法,获取历史搜索词
    const historyWords = keywordModel.getHistory()
    // 调用keywordModel中的getHot方法,获取热门搜索词
    const hotWords = keywordModel.getHot()  // getHot返回的是一个Promise对象
    this.setData({
      searching:true,
      historyWords:historyWords,
    })
    hotWords.then(res=>{
      this.setData({
        hotWords:res.hot
      })
    })
  },

(2)通过组件的属性传递page页面获取的值到组件,并进行绑定

  • 设置组件的属性
// search组件目录下的index.js文件
properties: {
    historyWords:{
      type:Array
    },
    hotWords:{
      type:Array
    }
  },
  • 通过组件的属性将值传递到组件中去,并进行数据绑定
<!-- book目录下的book.wxml --><yuan-search more="{{more}}" bind:cancel="onCancel" wx:if="{{searching}}" history-words="{{historyWords}}" hot-words="{{hotWords}}"/>

注意:
(1) 其实这里的history-words就是search组件中的自定义属性historyWords,但是在这里不能写成驼峰式(即historyWords),得转变为history-words。(以前的说法)
(2) 现在的小程序也支持自定义组件属性在标签中写成驼峰的形式,也就是说这里的history-words可以写成自定义组件中写的自定义属性形式,即historyWords。
(3) 这里的hot-words也类似于history-words。

(3)在组件中进行数据的显示

<!-- search组件中的index.wxml -->
 <view class="tags">
                <block wx:for="{{historyWords}}" wx:key="">
                    <!-- tapping是tag组件的自定义事件 -->
                    <yuan-tag bind:tapping="onConfirm" text="{{item}}" />
                </block>
 </view>

<view class="tags">
                <block wx:for="{{hotWords}}" wx:key="">
                    <!-- tapping是tag组件的自定义事件 -->
                    <yuan-tag bind:tapping="onConfirm" text="{{item}}" />
                </block></view>

二、id选择器和class选择器

1. id选择器

id选择器可以为标有特定id的HTML元素指定特定的样式。

  • HTML元素以id属性来设置id选择器;
  • CSS中id选择器以“#”来定义。
#para1
{
    text-align: center;
    color:  red;
}
<p id="para1">Hello World!</p>

id属性不要以数字开头,数字开头的id在 Mozilla/Firefox 浏览器中不起作用。

2. class选择器

class选择器用于描述一组元素的样式,class选择器有别于id选择器,class可以在多个元素中使用。

  • class选择器在HTML中以class属性表示;
  • 在CSS中,类选择器以一个点号“.”显示;
.center
{
    text-align: center;
}
<h1 class="center">标题居中</h1><p class="center">段落居中</p>

你也可以指定特定的HTML元素使用class。
如:指定所有的p元素使用class="center"让该元素的文本居中:

p.center
{
    text-align: center;
}

类名的第一个字符不能使用数字!它无法在 Mozilla 或 Firefox 中起作用

三、display

  • 块元素(block):是一个元素,占用了全部宽度在前后都是换行符
<h1>...<h6><form><table><dl><ul><ol><p><div>
  • 内联元素(inline):只需要必要的宽度不强制换行
    • (1)和相邻的内联元素在同一行;
    • (2)宽度(width)、高度(height)、内边距的top/bottom(padding-top/padding-bottom)和外边距的top/bottom(margin-top/margin-bottom)都不可改变,就是里面文字或图片的大小;简单地说,就是设置内联之后,这些属性都失效。
p {display:inline;}
<p>display 属性的值为 "inline"的结果</p><p>两个元素显示在同一水平线上。</p>

结果:

内联元素如:

<span><a><label><input><select><textarea><img>
  • div默认是block,span默认是inline。

1. display: none

display属性设置一个元素如何显示,visibility属性指定一个元素应可见还是隐藏

(1) display: none

可以隐藏某个元素,且隐藏的元素不会占用任何空间。也就是说,该元素不但被隐藏了,而且元素原本占用的空间也会从页面布局中消失

h1.hidden {display:none;}
<h1>这是一个可见标题</h1><h1 class="hidden">这是一个隐藏标题</h1><p>注意, 实例中的隐藏标题不占用空间。</p>

结果:

(2) visibility: hidden

可以隐藏某个元素,但隐藏的元素仍需占用和未隐藏之前一样的空间。也就是说,该元素虽被隐藏了,但是仍会影响布局

h1.hidden
{
    visibility: hidden;
}
<h1>这是一个可见标题</h1><h1 class="hidden">这是一个隐藏标题</h1><p>注意, 实例中的隐藏标题仍然占用空间。</p>

结果:

2. display: inline

显示为内联元素,此元素前后没有换行符。

  • (1) 给此类元素设置height和width、内外边距、text-align等属性是无效的。
<div> DIV1 </div> 
<div> DIV2 </div> 

结果:

使用display: inline可以使两个div显示在同一行

div
{
  display: inline
}
<div> DIV1 </div> 
<div> DIV2 </div> 

结果:

3. display: block

显示为块级元素,此元素前后会带有换行符

  • (1) 如果不指定宽高,默认会继承父元素的宽度,并且独占一行,即使宽度有剩余也会独占一行,高度一般以子元素撑开的高度为准;可以自己设置宽高
  • (2) 如果设置display属性,则默认就是block。
.chunk{
   /*设置为块元素(如不设置,默认也为块元素) */
    display: block;
    height: 120px;
    width: 100px;
}

.color1{
    background-color:red;
}
.color2{
    background-color: aquamarine;
}
.color3{
    background-color: blueviolet;
}
<div class="chunk color1"></div><div class="chunk color2"></div><div class="chunk color3"></div>

结果:

4. display: inline-block

显示为内联块元素,表现为同行显示可修改宽高内外边距等属性。换句话说,就是不设置宽度时,内容撑开宽度;不会独占一行,可以设置宽高,代码换行被解析成空格。

对象的内容作为block展现,既具有block的宽度高度特性,又具有inline的同行特性。

.chunk{
/* 行内元素:内联块元素*/display: inline-block;
/* 可设置宽高 */height: 120px;
width: 100px;
}
.color1{
background-color: black;
}
.color2{
background-color: aquamarine;
}
.color3{
background-color: blueviolet;
}
<div class="chunk color1">
</div>
<div class="chunk color2">
</div>
<div class="chunk color3">
</div>

结果:

三个块在同一行中展示

5. display: list-item

此元素会作为列表显示,类似于list列表,可以和list-style-type以及list-style-position属性一起组合使用。

  • 方法一:display: list-item与list-style-type组合使用;
.fake-list {
  display: list-item;
  list-style-position: inside;
}
<div class="fake-list">I will display as a list item</div><div class="fake-list">I will display as a list item</div><div class="fake-list">I will display as a list item</div>

结果:

  • 方法二:使用display: list-item,并设置内边距(内容边界到该层边界的距离)。
div{
  padding:15px
}
span
{
  display: list-item;
}

padding是表示内边距,在这里只有设置合适大小(足够大小)的内边距才可以显示出列表的项目符号(如“小圆黑点”)

<div>
   <span>list item1</span>
   <span>list item2</span>
   <span>list item3</span></div>

结果:

  • 方法三(仅使用HTML列表标签)
<! 无序列表><ul>
        <li>LeBron James</li>
        <li>Dwyane Wade</li>
        <li>Kobe Bryant</li>  
</ul>

<! 有序列表><ol>
    <li>LeBron James</li>
        <li>Dwyane Wade</li>
        <li>Kobe Bryant</li>  
</ol>

结果:

6. display: table

此元素会作为块级表格来显示(类似 <table>,表格前后带有换行符。

7. display: inline-list-item

8. display: flex

参考:Flex 布局教程:语法篇

(1)基本概念

采用flex布局的元素,称为flex容器(flex container),简称“容器”。它的所有元素自动成为容器成员,称为flex项目(flex item),简称“项目”。

容器默认存在两根轴:

  • 水平的主轴(main axis)
  • 垂直的交叉轴(cross axis)

(1)主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end
(2)交叉轴的开始位置叫做cross start,结束位置叫做cross end。项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size

如何确定主轴和交叉轴?
我们可以结合X,Y轴进行理解。 例如:

  • 当flex-direction值为row时,X轴就是主轴,Y轴就是交叉轴;
  • 当flex-direction的值为column时,Y轴就是主轴,X轴就是交叉轴。

(2)容器的属性(flex container)

这6个属性设置在容器上

  • flex-direction: 决定主轴的方向(即项目排列的方向)。
  • flex-wrap: 默认情况下,项目都排在一条线(又称“轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行
  • flex-flow: 是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。
  • justify-content: 定义了项目在主轴上的对齐方式
  • align-items: 定义项目在交叉轴上如何对齐
  • align-content: 定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

(3)项目的属性(flex-item)

这6个属性设置在项目上。

  • order:定义项目的排列顺序。数值越小,排列越靠前,默认为0。
  • flex-grow:定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
  • flex-shrink:定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
  • flex-basis:定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
  • flex:是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选
  • align-self:允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

(4)flex(弹性盒子布局)的一个注意事项

I. 行倒序

.chunk{
    /* display: block; 当container中设置flex布局之后,block布局失效*/
    height: 120px;
    width: 100px;
}
.container{
    display: flex; /*弹性盒子 */
    /* column row column-reverse(列倒序) row-reverse(行倒序) */
    flex-direction: row-reverse; /*列(column):表示竖直排布;行(row):表示水平排布 */
    background-color: bisque;
}
.color1{
    background-color:red;
}
.color2{
    background-color: aquamarine;
}
.color3{
    background-color: blueviolet;
}
<view class="container">
    <view class="chunk color1">
    1
    </view>
    <view class="chunk color2">
    2
    </view>
    <view class="chunk color3">
    3
    </view></view>

II. 列倒序

只要把名为container的选择器flex-direction属性一下即可

flex-direction: column-reverse;

  • 为什么column-reverse(列倒排序)和row-reverse(行倒排序)有点区别:
    行倒排序从最右边开头,列倒排序却不是从最下面开头
  • 原因是:事实上列倒排序是从view container最下面开头的,view的高度是自适应的,
    所以如果不设置固定高度且大于列中所有块的高度总和就不可能看出像行倒排序这种效果

下面在CSS的名为container选择器中简单地设置一下vcontainer的高度即可很明显地看出列倒序从最下面开始对齐的效果

height: 400px;

效果如下:

一个简单的flex布局的CSS例子

.chunk{
/* 行内元素 *//* display: block; 当container中设置flex布局之后,block布局失效*/height: 120px;
width: 160px;
}
.container{
/* flex flexible box *//* flex container flex item *//* view 100% height 自适应 *//* 主轴和交叉轴 */display: flex; /*弹性盒子 *//* column row column-reverse(列倒序) row-reverse(行倒序) */flex-direction:row-reverse; /*列(column):表示竖直排布;行(row):表示水平排布 */flex-wrap: wrap;/* 一行容纳不下的时候进行弹性换行 */justify-content: flex-start; /* 主要用来控制flex item的对齐方向的以及块之间的间距*/align-items: flex-end;/* 设置交叉轴的对齐方式 */background-color: bisque;
height: 320px;
}
.color1{
background-color:red;
font-size: 12px;
}
.color2{
background-color: aquamarine;
font-size: 23px;
}
.color3{
background-color: blueviolet;
font-size: 30px;
}

(5)flex布局在设置组件居中的一个优势

<view class="container">
        <view class="view_1">view_1</view>
        <view class="view_2">view_2</view>
        <view class="view_3">view_3</view></view>
.container 
{
 display:flex;
 flex-direction:column; /*这里主轴为垂直方向*/
 align-items:center; /* 设置项目在交叉轴上的对齐方式(这里的交叉轴是水平方向)*/
}
.view_1
{
  background-color: #ff0f8f;
}
.view_2
{
  background-color: #f77f9f;
}
.view_3
{
  background-color: #f99fff;
}

在flex布局中可以很容易实现组件的居中。
例如:上面的组件是采用的是垂直方向上的flex布局,

  • 如果我们要实现组件在水平方向(即交叉轴)上的居中,只要设置align-items: center即可实现;
  • 如果要实现组件在垂直方向(即主轴)上的居中,只要设置justify-content: center。

当然,如果是水平方向上的flex布局,则可以按照上面的方法进行相应的修改也可实现组件的居中。

  • align-items: center 表示项目在交叉轴方向上居中对齐。
  • justify-content: center 表示项目在主轴方向上居中对齐。

(6)不可忽视width: 100%的作用

.container{
    width: 100%; /* 设置宽度100%,以防止出现一些意想不到的状况*/
    display: flex;
    flex-direction: column;
    align-items: center; /*设置整个页面的元素水平居中(交叉轴为水平方向)*/
}

.header{
    /*为了防止上述因为设置align-items:center导致水平居中的时候头部的日期组件和点赞组件也居中,
    导致出现这种情况的原因是因为包含日期组件和点赞组件的容器的宽度未充满整个宽度*/   
    width: 100%; 
    display: flex;
    flex-direction: row;
    height: 100rpx;
    align-items: center; /*设置容器的项目垂直居中对齐*/
    justify-content: space-between; /* 设置项目在主轴的对齐方式为两端对齐*/
    border-top: 1px solid #f5f5f5; /* 设置上边框的样式*/
    border-bottom: 1px solid #f5f5f5; /*设置下边框的样式*/
}
...
<view class="container">
    <view class="header">
        <yuan-episode class="episode" index="{{classicData.index}}"/>  <!-- index在API接口中表示期刊号 -->
        <yuan-like class="like" bind:like="onLike" like="{{classicData.like_status}}" count="{{classicData.fav_nums}}"/>
    </view>
    <yuan-movie img="{{classicData.image}}" content="{{classicData.content}}" /> 
    <yuan-navi class="navi" title="{{classicData.title}}" first="{{first}}" latest="{{latest}}"/></view>

效果如下图1所示:

删除.header中的width: 100% 则会出现以下情况,如下图2所示:

为什么会出现这种情况呢?
主要是这里的包含日期组件和点赞组件的头部组件的宽度是自适应的,说白了就是头部组件的宽度等于日期组件和点赞组件的宽度之和,而此时头部组件的宽度小于屏幕宽度,所以虽在flex布局中设置了align-items: center(项目在交叉轴上居中,即水平居中),头部组件的确水平居中了,但由于自适应的原因其中的日期组件和点赞组件并没有如我们所期望的两端分布。
解决方法: 在.header中加上width: 100%, 让头部组件的宽度充满屏幕宽度,这时候日期组件和点赞组件就会在头部组件的两端分布了。

9. display: inline-flex

行内元素也可以使用 Flex 布局

10. others

参考:

CSS display 属性详解

四、ES6的几个语法知识

1. 模板字符串

示例1:

  onLoad: function (options) {
   
    let a = 123
    console.log(`${a}456${this.test()}`) //使用模板字符串(使用反引号`,$和{})
    console.log(a+'456'+this.test())  //不采用模型字符串
  },

  test:function(){
    return 789
  },

输出结果为

123456789

示例2:

 getClassicLikeStatus(artID,catagory,sCallback){
        this.request({
            //url:'classic/'+catagory+'/'+artID+'/favor',
            // 采用模板字符串的形式,则是这样表示:(注意是反引号)
            url:`classic/${catagory}/${artID}/favor`,
            success:sCallback
        })
    }

2. 扩展运算符

五、Promise、callback回调函数以及async 、await(重难点)

1. Promise

这篇文章讲得比较详细:Promise 对象

2. callback(回调函数)

所谓"回调函数"(callback),就是那些会被主线程挂起来的代码异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数

3. 事件循环(Event Loop)

详解JavaScript中的Event Loop(事件循环)机制

谈谈 Event Loop(事件循环)机制

并发模型与事件循环

javascript事件循环

javascript:彻底理解同步、异步、事件循环(Event Loop)

六、slot(插槽)

在组件模板中可以提供一个 <slot> 节点,用于承载组件引用时提供的子节点(说白了就是先占一个坑)。

<!-- 组件模板 --><view class="wrapper">
  <view>这里是组件的内部节点</view>
  <slot></slot></view>
<!-- 引用组件的页面模板 --><view>
  <component-tag-name>
    <!-- 这部分内容将被放置在组件 <slot> 的位置上 -->
    <view>这里是插入到组件slot中的内容</view>
  </component-tag-name></view>

1. 组件WXML中使用多个slot

在组件的wxml中可以包含 slot 节点,用于承载组件使用者提供的wxml结构。
默认情况下,一个组件的wxml中只能有一个slot。需要使用多slot时,可以在组件js中声明启用

Component({
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多slot支持
  },
  properties: { /* ... */ },
  methods: { /* ... */ }
})

此时,可以在这个组件的wxml中使用多个slot,以不同的name 来区分

<!-- 组件模板 --><view class="wrapper">
  <slot name="before"></slot>
  <view>这里是组件的内部细节</view>
  <slot name="after"></slot></view>

使用时,用 slot 属性来将节点插入到不同的slot上。

<!-- 引用组件的页面模板 --><view>
      <component-tag-name>
        <!-- 这部分内容将被放置在组件 <slot name="before"> 的位置上 -->
        <view slot="before">这里是插入到组件slot name="before"中的内容</view>
        <!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 -->
        <view slot="after">这里是插入到组件slot name="after"中的内容</view>
      </component-tag-name></view>

七、外部样式类

有时,组件希望接受外部传入的样式类。此时可以在Component 中用 externalClasses 定义段定义若干个外部样式类。

这个特性可以用于实现类似于 view 组件的 hover-class 属性:页面可以提供一个样式类,赋予view的hover-class ,这个样式类本身写在页面中而非 view 组件的实现中

注意:在同一个节点上使用普通样式类外部样式类时,两个类的优先级是未定义的,因此最好避免这种情况。(小程序的外部样式的最大缺点)

<!-- 组件 custom-component.wxml --><!--container为写在组件CSS文件中的普通样式,myclass为写在页面CSS文件中的外部样式 --><custom-component class="container my-class">
 
</custom-component>

普通样式和外部样式同时存在的时候,外部样式并不一定有效果。

使用外部样式的示例

/* 组件 custom-component.js */
Component({
  externalClasses: ['my-class']
})
<!-- 组件 custom-component.wxml --><custom-component class="my-class">
  这段文本的颜色由组件外的 class 决定
</custom-component>
<!--应用自定义组件的页面 external-page.wxml-->
 <view>
    ...
    <custom-component my-class="ex-tag">
        ...        
    </custom-component>
    ...
 </view>
.ex-tag{
   ...
}

这样,组件的使用者可以指定这个样式类对应的 class ,就像使用普通属性一样。

组件的外部样式:从组件外部传进来的样式。

如果同时使用了普通样式和外部样式,且想通过外部样式覆盖相应的普通样式,可以通过添加 !important 关键字实现。

/* 外部样式类:从页面中传入到组件中 */.ex-tag{
    background-color: #fffddd  !important;  /*通过使用 !important来提升外部样式的优先级,这样就可以覆盖普通样式*/
}

八、WXS(WeiXin Script)

WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。
WXS代码可以直接在WXML中使用,而JavaScript代码不能直接在WXML中使用。
WXS 代码可以编写在 wxml 文件中的 <wxs> 标签内,或以 .wxs 为后缀名的文件内。

  • WXS代码写在WXML文件中的<wxs>标签内
<!--wxml--><wxs module="foo">
  var some_msg = "hello world"; 
  module.exports = { msg : some_msg, }
</wxs>

<!--直接引用wxs中的代码--><view>{{foo.msg}}</view>
  • WXS代码写在以 .wxs为后缀名的文件内
// /pages/index/index.js

Page({
  data: {
    msg: "'hello wrold' from js",
  }
})
// /pages/comm.wxs

var foo = "'hello world' from comm.wxs";
var bar = function(d) {
  return d;
}
module.exports = {
  foo: foo,
  bar: bar
};
<!-- /pages/index/index.wxml -->

<wxs src="./../comm.wxs" module="some_comms"></wxs><!-- 也可以直接使用单标签闭合的写法
<wxs src="./../comm.wxs" module="some_comms" />
-->

<!-- 调用 some_comms 模块里面的 bar 函数,且参数为 some_comms 模块里面的 foo --><view>{{some_comms.bar(some_comms.foo)}}</view><!-- 调用 some_comms 模块里面的 bar 函数,且参数为 page/index/index.js 里面的 msg --><view>{{some_comms.bar(msg)}}</view>

上述例子在文件 /page/index/index.wxml 中通过 <wxs> 标签引用了 /page/comm.wxs 模块。

参考:WXS 模块

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值