小程序开发框架:mpvue(一)初篇
小程序开发框架:mpvue(二)项目代码分析
小程序开发框架:mpvue(三)完成一个积极干净的mpvue初创项目
任何语言/框架的学习都离不开项目实战,这篇开始我们将通过实际操作一个项目来学习微信小程序和vue技术的方方面面;
1: 首先我们在小程序首页引入一个组件,并介绍下父组件与子组件是如何进行相互交互的
实现效果如上,在上面的效果中,绿色边框是以组件的形式引入到主页当中该章节主要涉及的知识点有
1. 微信组件image的使用
2. 如何自定义/引入组件
3. 让一个div铺满剩余的页面
4. 组件交互的三种技法
下面我们先逐一过一下知识点
微信小程序提供了一个image组件用于替代html中的img标签,这个标签可以更快的实现图片的裁剪和缩放功能,使用方法
<image class="welcome" mode='aspectFit' :src="welIcon"></image>
微信小程序提供了一个image组件用于替代html中的img标签,这个标签可以更快的实现图片的裁剪和缩放; 上面代码使用了微信的image组件,并设置其mode为aspectFit,表示保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来,当然mode还有很多设置方式,这里就不一一介绍,你只要记住微信的image组件可以通过mode来设置图片的裁剪和缩放即可,具体的请查阅微信image组件使用API
模块化已经成为各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行
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的高度即可
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项目实战一