小程序开发框架:mpvue(四)组件通信

1,358 阅读8分钟

小程序开发框架:mpvue(一)初篇
小程序开发框架:mpvue(二)项目代码分析
小程序开发框架:mpvue(三)完成一个积极干净的mpvue初创项目

  任何语言/框架的学习都离不开项目实战,这篇开始我们将通过实际操作一个项目来学习微信小程序和vue技术的方方面面;

  1: 首先我们在小程序首页引入一个组件,并介绍下父组件与子组件是如何进行相互交互

在这里插入图片描述
  实现效果如上,在上面的效果中,绿色边框是以组件的形式引入到主页当中


该章节主要涉及的知识点有

  1. 微信组件image的使用
  2. 如何自定义/引入组件
  3. 让一个div铺满剩余的页面
  4. 组件交互的三种技法


下面我们先逐一过一下知识点

  

1. 微信组件image的使用

  微信小程序提供了一个image组件用于替代html中的img标签,这个标签可以更快的实现图片的裁剪和缩放功能,使用方法

<image class="welcome" mode='aspectFit' :src="welIcon"></image>

  微信小程序提供了一个image组件用于替代html中的img标签,这个标签可以更快的实现图片的裁剪和缩放;   上面代码使用了微信的image组件,并设置其mode为aspectFit,表示保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来,当然mode还有很多设置方式,这里就不一一介绍,你只要记住微信的image组件可以通过mode来设置图片的裁剪和缩放即可,具体的请查阅微信image组件使用API

  

2. 如何自定义/引入组件

  模块化已经成为各app的核心,通过模块化不但可以达到代码复用还能优化代码。   首先我们先在components目录下新建一个login.vue文件,你可以认为一个vue后缀的文件即一个组件,一个默认的组件代码如下

<template>
    
</template>

<script>

export default {
  // 这里只是对组件命名,其实这里也可以不写
  name: 'LoginModule',
  data () {
    return {
    }
  },
  methods: {
  }
}
</script>

<style>
</style>

  一个组件默认包含template,script和style三个部分,有了这个基础我们就能写自己的vue组件了   第9行,我们将这个组件命令为LoginModule,其实这行代码也可以不写

然后我们在父组件pages/indexs/index.vue中引入LoginModule组件,主要有三个步骤 1)通过import引入组件

import LoginView from '@/components/login'

2)在components中申明这个组件

  components: {
    LoginView
  },

3)在template中使用这个组件

<template>
  <!-- 因为登录有可能在其他地方进行,顾登录的页面封装成单独的组件 -->
  <div class="container">
    <div class="title">
      <p class="loginPeople" v-if="isShowLoginPeople">{{loginWho}}</p>
      <image class="welcome" mode='aspectFit' :src="welIcon"></image>
    </div>
    <div class="login">
      <!-- 引入子组件 定义一个on的方法监听子组件的状态-->
      <login-view v-bind:inputName="name" v-on:clickLogin="showLoginPeople"></login-view>
    </div>
  </div>
</template>

  这里我们有几个注意的地方 1)import后跟引入组件的名字,components中的名字要和其保持一致 2)在templeta中可以通过LoginView来使用该组件了,因为采用了驼峰命名法,顾在使用的时候可以使用xx-xx的形式,如第10行

  

3. 让一个div铺满剩余的页面

   css中的布局不像android里面可以采用权重布局方式,所以为了做到最大程度的兼容,我一般采用先获取屏幕属性,然后根据UI计算每层的高度;如果一个页面有两个div组成,下一个div需要占满剩下的屏幕,则我们可以简化如下操作

<template>
  <!-- 因为登录有可能在其他地方进行,顾登录的页面封装成单独的组件 -->
  <div class="container">
    <div class="title">
      <p class="loginPeople" v-if="isShowLoginPeople">{{loginWho}}</p>
      <image class="welcome" mode='aspectFit' :src="welIcon"></image>
    </div>
    <div class="login">
      <!-- 引入子组件 定义一个on的方法监听子组件的状态-->
      <login-view v-bind:inputName="name" v-on:clickLogin="showLoginPeople"></login-view>
    </div>
  </div>
</template>

这个页面有两个div,一个title,一个login,我们想让login铺满剩下的布局,则可以采用如下的方式

.login{
    width: 100%;
    position: absolute;
    top: 150px;
    bottom: 0px;
    left: 0px;
    background: #ea5c54;
}

这里的设计思想是通过绝对对位让元素脱离文档流,然后使其定位到底部,即bottom和left均为0, 那么只要top的距离为title这个div的高度即可

  

4. 组件交互的三种技法

1)子组件与父组件通信

子组件通过this.$emit的方式触发父组件监听的事件,当父组件收到事件调用时,触发父组件的方法

  methods: {
    login () {
      let userName = this.inputName
      this.$emit('clickLogin', userName)
    }
  }

如上,子组件定义了一个login方法,当调用login方法的时候,发布一个clickLogin方法出去,有点类似广播;

    <div class="login">
      <!-- 引入子组件 定义一个on的方法监听子组件的状态-->
      <login-view v-bind:inputName="name" v-on:clickLogin="showLoginPeople"></login-view>
    </div>

如上,在父组件引用子组件的时候,注册一个监听,即此处的第3行,当收到子组件发出的clickLogin广播的时候,便会触发自身的showLoginPeople方法,showLoginPeople方法定义如下

  methods: {
    showLoginPeople (user) {
    }
  }

此处的参数user即子组件传过来的值

2)父组件与子组件通信

子组件定义props属性用于接收父组件传过来的值

  props: {
    inputName: String,
    required: true
  },

父组件通过v-bind的方式传递对应的值即可

<login-view v-bind:inputName="name"></login-view>

3)非父子组件通信: 我们通过vuex

vuex我们首先要了解如下几个概念,为了解释起来不那么官方,我会用通俗的语言来描述

3.1)state 即你定义的状态管理里面可以维护的数据 3.2)getters 即你可以通过getters的方式去获取你定义的数据,或者对维护的数据进行操作,然后返回出来 3.3)mutations 采用同步的方式修改维护的数据 3.4)action 采用异步的方式去操作mutation,你可以将action视为对mutations的一个封装,他和mutations最大的区别是,action支持异步操作 3.5)mapstate 从名字就可以看出,他是对state的映射,方便我们获取state,而不用使用$store.state...这种冗长的方式 3.6)mapMutations 他是对mutations的映射,也是用于简化操作的

下面我们将实现的效果是这样的,大家最好自己实现下,要不还是纸上谈兵

1)小程序启动的时候,给子组件LoginModule传递一个值‘扬帆起航’,并显示在用户名编辑框中 2)当用户点击登录按钮,父页面显示正在登录的用户名 3)当用户点击登录按钮的时候,通过vuex对用户名进行存储

效果如下

在这里插入图片描述
关键代码如下 1)主页面代码

<template>
  <!-- 因为登录有可能在其他地方进行,顾登录的页面封装成单独的组件 -->
  <div class="container">
    <div class="title">
      <p class="loginPeople" v-if="isShowLoginPeople">{{loginWho}}</p>
      <image class="welcome" mode='aspectFit' :src="welIcon"></image>
    </div>
    <div class="login">
      <!-- 引入子组件 定义一个on的方法监听子组件的状态-->
      <login-view v-bind:inputName="name" v-on:clickLogin="showLoginPeople"></login-view>
    </div>
  </div>
</template>

<script>
// 通过import的方法导入该组件,这样就可以使用通过LoginView来使用该组件了,因为采用了驼峰命名法,顾在使用的时候可以使用xx-xx的形式
// 在早期的mpvue版本中,不是通过@符号引入组件,而是通过~符号引入组件
import LoginView from '@/components/login'

export default {
  components: {
    LoginView
  },
  data () {
    return {
      // 欢迎icon
      welIcon: '/static/welcome.png',
      // 默认登录的名字
      name: '扬帆起航',
      // 是否显示正在登录的用户名
      isShowLoginPeople: false,
      loginWho: ''
    }
  },
  methods: {
    showLoginPeople (user) {
      this.isShowLoginPeople = true
      this.loginWho = '正在登录的是' + user
      let whoLogin = this.$store.state.userName
      console.log('**whoLogin**' + whoLogin)
    }
  }
}
</script>

<style scoped>
body {
    /* 清空页面的margin和padding */
    margin: 0px;
    padding: 0px;
}
/* 为了实现页面的兼容,即login这个div需要占满剩下的页面,这里的设计思想是
通过决定对位让元素脱离文档流,然后使其定位到底部,即bottom和left均为0,
那么只要top的距离为title这个div的高度即可 */
.title{
  width: 100%;
  height: 150px;
  position: relative;
  background: #ea5c54;
}
.welcome{
  width: 126px;
  height: 100px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
}
.login{
    width: 100%;
    position: absolute;
    top: 150px;
    bottom: 0px;
    left: 0px;
    background: #ea5c54;
}
</style>

2)子组件代码

<template>
    <div class="loginParent">
      <div class="userContain">
        <img class="userIcon" src="/static/name.png">
        <input class="userInput" placeholder="用户名" maxlength="16" type="text" v-bind:value="inputName" v-model="inputName" ref="username">
      </div>
      <div class="login_fields__password">
        <img class="icon-password" alt src="/static/pasword.png">
        <input class="input-password" v-bind:type="inputType" placeholder="密码" maxlength="16">
      </div>
      <div class="login_fields__submit">
        <input type="button" value="登录" @click="login">
      </div>
    </div>
</template>

<script>
import { mapMutations } from 'vuex'

export default {
  // 这里只是对组件命名,其实这里也可以不写
  name: 'LoginModule',
  props: {
    inputName: String,
    required: true
  },
  data () {
    return {
      inputType: 'password'
    }
  },
  methods: {
    // mapMutations为语法糖,一个辅助函数,mapActions/mapMutations只是把action/mutation函数绑定到你的methods里;你调methods里的方法的时候照常传参就可以了。
    ...mapMutations(['setUserName']), // 相当于this.$store.commit('setUserName','具体传递的值'),提交这个方法
    login () {
      let userName = this.inputName
      this.setUserName(userName)
      this.$emit('clickLogin', userName)
    }
  }
}
</script>

<style>
.userContain{
   margin-top: 30px;
   margin-left: 70px;
}
.userIcon {
    display: inline-block;
    width: 26px;
    height: 26px;
}
.userInput {
    display: inline-block;
    width: 140px;
    border-style: solid; 
    border-width: 1px;
    font-size: 12px;
    margin-left: 20px;
}
.login_fields__password{
    margin-top: 30px;
    margin-left: 70px;
}
.icon-password {
    display: inline-block;
    width: 26px;
    height: 26px;
}
.input-password {
    display: inline-block;
    width: 140px;
    border-style: solid; 
    border-width: 1px;
    font-size: 12px;
    margin-left: 20px;
}
.login_fields__submit {
    margin-top: 90px;
}
</style>

3)vuex部分

import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters'
import * as actions from './actions'
import * as mutations from './mutations'
Vue.use(Vuex)

// state类似于该类的属性/状态
// getters,    通过getters可以实现对该类属性/状态的获取和修改
// mutations,  类似于一个提交操作,mutations中一般是同步方法,用于修改该类的属性/状态
// actions,    也是一个提交操作,他和mutation的最大区别就是actions可以包含一个异步操作
// mapGetters  这是一个辅助函数,仅仅是将store中的getters映射到局部计算属性中,用法和mapState类似

const state = {
  // 定义一个该类的属性/状态userName,其默认值为侠骨柔情
  userName: '侠骨柔情'
}

// 引入的各大模块
const store = new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})

export default store

具体的工程可在如下链接下载 mpvue项目实战一