小程序开发之mpVue
# my-project
> A Mpvue project
## Build Setup
```bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
```
#项目构建
http://mpvue.com/build/
#mpvue-docs 使用手册
http://mpvue.com/mpvue
# Flyio Github:
https://github.com/wendux/fly
For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
#创建一个小程序工程
vue init mpvue/mpvue-quickstart my-project
# 1) package.json
# 项目的主配置文件, 对 mpvue 的描述,项目依赖的各种第三方库、版本信息、执行的脚本、etc。。
# scripts 中配置的是可执行命令
# dev and start 可以将项目以开发模式启动
npm start
npm run dev(注意点:新增文件必须重新运行该命令,编译器不会自动检测新加入的文件)
# build 用于生成发布所用的代码,主要对代码进行压缩优化。
# lint 指令 使用 ESLint 进行代码语法和格式检查,及修复一些可以自动修复的问题
# 检查语法和格式
npm run lint
# 检查代码语法和格式,并修复可自动修复的问题
npm run lint -- --fix
# 2)project.config.json 文件
# 用于管理微信开发者工具的小程序项目的配置文件,记录了小程序的 appid、代码主目录、以及编译选项。导入小程序项目的时候主要是通过该配置文件读取和写入配置信息
# 3)static 目录
# 用于存放各种小程序本地静态资源;图片、文本文件,可通过相对路径或绝对路径进行访问
# 4) build 目录
#用于项目编译打包的 node.js 脚本和 webpack 配置文件
# 5)config 目录
# 用于开发和生产环境下的不同配置
dev.env.js用于开发环境
prod.env.js用于生产环境
# const baseURL = process.env.API_BASE_URL
# wx.request({
# url: `${baseURL}/products`
# })
# 6)src 目录
#components:vue 组件存放的目录
#pages:存放小程序的页面,请遵循每个小程序页面放入一个单独子目录的组织形式
#utils:可选,存放代码中公用工具函数
#main.js 和 App.js:入口文件
# 7) app.json 是小程序的全局配置文件,
{
1. 页面文件路径配置 pages
2. 窗口的全局样式信息 window
3. 底部选项卡式菜单栏的配置
4. 及一些小程序网络超时的配置
5. ....etc
}
# 配置详情参考 小程序官网文档
https://developers.weixin.qq.com/miniprogram/dev/framework/config.html#全局配置
# src/main.js 中,小程序的配置信息
# 8)app.js 包含在小程序各种原生生命周期函数,如 onLaunch、onShow 等
在 mpvue 中,使用 App.vue 组件实现等价功能在 App.vue 组件中可以编写小程序的生命周期方法(通常使用 vue 生命周期方法,同时兼容原生生命周期函数);亦可加入小程序的全局样式(等价于原生中的 app.wxss),一起看下 App.vue 文件
接下来,App.vue 组件被 src/main.js 引入并被设置了一个 mpType 的属性值,其值为 app
疑问:mpType 是什么?为什么要用?怎么用? 后续我们再说明下,骚年等等先
#这里提到了组件,组件特点有哪些?
1.封装性(使用.vue 文件形式){
注意:避免向外暴露过多,只需要必要的交互接口(如:组件属性、事件、方法),外部调用不需要关心组件内部实现细节,只关心功能即可
}
2.复用性{
多次使用,避免编写重复的代码
}
3.扩展性{
1.继承是一种比较有效的扩展机制,不过随着继承的层次变深,代码也会变得难以理解
2.vue组件中,没有采用继承的机制,而是推荐使用“组合”的方式?
如何组合?{
***尽量将想复用性高的组件设计到最小可拆分单位(如按钮、输入框、单选框等)
将其低层组件放入更高层组件中,一层一层组装出满足业务需求的界面
}
除此之外,vue为我们提供了slot插槽,具体如何使用呢?看代码 pages/slot
{
理解为组件中的坑,我们可以选择往坑里扔哪些内容来填充
***slot用于子组件中
{
插槽就是另外一种形式的组件属性,
普通组件属性传入的是比较简单类型的数据,
插槽传入的可以是复杂的界面组件
}
}
}
# 代码更加容易理解和维护、提升了代码的健壮性、会降低 bug 出现的几率(容易定位和调试)
}
# 9)遵循每个页面放入一个子目录的习惯(原生小程序习惯)
该子目录下,新建 2 个文件:
{ 1. 用于实现页面主体功能的 index.vue 组件 2. 用于将这个页面组件生成 Vue 实例并加载的 main.js
}
注:每个 mpvue 页面组件都是这样的结构(查看 pages 目录下的文件了解下)
实现 index.vue 页面组件,写法和 vue 组件一样优点:
{ 1.数据绑定、事件处理、scoped 局部样式、以及使用 HTML 标签来构建界面 2.减少了项目切换到小程序时的学习理解成本 3.使用 Vue 开发的网页应用移植到小程序平台提供了降低迁移成本的可能
}
模板部分我们通常可以用 HTML 标签来写,比如 div、span 等,它们会在编译的时候被自动转换成小程序的原生组件 view、text 之类;而那些小程序特有的组件如 swiper、rich-text 等,可以直接在模板中使用
注意:{
在原生小程序的页面(Page)中包含了很多页面的生命周期方法,如 onLoad、onUnload、onShow、onHide、onPullDownRefresh 等等,mpvue 中推荐使用 Vue 组件生命周期方法,而像 onPullDownRefresh、onReachBottom 这类特殊功能的生命周期则需直接使用原生的
}
# 小程序一般会有多个页面构成,在 src/pages 目录下编写多个页面组件后,mpvue 会自动把它们都添加进配置文件,此时注意一点小程序的机制: 配置文件中 pages 数组里的第一个 page 路径会被当做是首页
#如果我们期望的首页组件并没有被 mpvue 添加到第一个路径的话,就不会被当做首页显示解决方法:{ 1.显示的去指定首页在 src/main.js 的配置里,加入这样一行配置信息:
pages: [
'^pages/index/main'
]
}
注意:十分火急,目前版本这种解决方案存在 bug,老版本的可以,需要等到新版本稳定后待解决。最后建议,在 app.json 中 pages 就指定好首页。
########由于小程序框架本身存在的一些功能限制,导致有些功能不能被翻译过去,也就是说有些标准的 Vue 功能在 mpvue 下是不可以使用或有特殊限制的
##################################################################################################### #需要注意的点有哪些?
#1.在模板中,动态插入 HTML 的 v-html 指令不可用
{ 小程序的界面并不是基于浏览器的 BOM/DOM 的,所以不能动态的在界面模板里直接插入 HTML 片段来显示
# 如果有需求怎么办?使用<rich-text>组件
# 或者 wxParse(https://github.com/icindy/wxParse)实现
}
#2.在模板中,用于数据绑定的双括号语法{{}}中的表达式功能存在诸多限制
{
只能在双括号中使用一些简单的运算符运算(+ - \* % ?: ! == === > < [] .)
# 解决方案,尽量考虑使用计算属性(computed)
}
#3.在模板中,除事件监听外,其余地方都不能调用 methods 下的函数
{
如:v-if 指令中调用函数 getErrorNum()
<div v-if="getErrorNum() > 0 && code == 10001" class="error">{{ errorMsg }}</div>
#解决方案暂时还是计算属性
}
#4.在模板中,不支持直接绑定一个对象到 style 或 class 属性上
在 Vue 中我们可以为 HTML 元素的 class 或 style 绑定一个对象,并按照对象内的属性值来决定是否添加对应的属性名到 HTML 元素的样式名
```
<template>
<div :class="classObject"></div>
</template>
<script>
export default {
data() {
return {
classObject: {
active: true,
'text-danger': false
}
}
}
}
</script>
```
# 运行后 生成<div class="active"></div>
#在 mpvue 下面这个特性不可以使用官方解决方案{
# <div :class="{ active: classObject.active, 'text-danger': classObject['text-danger']}"></div>
}
# 好的解决方案:使用计算属性(直接生成一串样式的字符串,绑定到 class 或 style 上)
```
<template>
<div :class="classStr"></div>
</template>
<script>
export default {
data() {
return {
classObject: {
active: true,
'text-danger': false
}
}
},
computed: {
classStr() {
let arr = []
for (let p in this.classObject) {
if (this.classObject[p]) {
arr.push(p)
}
}
return arr.join(' ')
}
}
}
</script>
```
#5. 在模板中,嵌套使用 v-for 时,必须指定索引 index
之前我们写 v-for 时, <ul v-for="item in List"></ul>
使用 mpvue,必须 <ul v-for="(item,index) in List " :key="index"></ul>才可以
#6.事件处理中的注意点
在 mpvue 中,一般可以使用 Web 的 DOM 事件名来绑定事件,
mpvue 会将 Web 事件名映射成对应的小程序事件名:
// 左侧为 WEB 事件 : 右侧为对应的小程序事件
```
{
click: 'tap',
touchstart: 'touchstart',
touchmove: 'touchmove',
touchcancel: 'touchcancel',
touchend: 'touchend',
tap: 'tap',
longtap: 'longtap',
input: 'input',
change: 'change',
submit: 'submit',
blur: 'blur',
focus: 'focus',
reset: 'reset',
confirm: 'confirm',
columnchange: 'columnchange',
linechange: 'linechange',
error: 'error',
scrolltoupper: 'scrolltoupper',
scrolltolower: 'scrolltolower',
scroll: 'scroll'
}
```
#Web 表单组件<input>和<textarea>的 change 事件会被转为 blur 事件
#Vue 里面绑定事件的时候,可以指定事件修饰符,但是在 mpvue 里,官方给出了一些注意信息: #遇到事件相关的问题,查看一下文档吧
#7.对于表单,请直接使用小程序原生的表单组件
{
实际开发中,推荐直接使用小程序的表单组件标签来写,而不是使用 Web 的表单组件标签来写在 mpvue 中使用了小程序的组件标签,数据绑定功能还是完全可以用的
}
#8.created 不是预期的触发所有页面的 created 在小程序初始化时都会触发,mounted 对应小程序的 onLoad,所以 mpvue mounted 相当于 vue 中的 created
mounted() { // 相对应小程序的 onLoad
// 获取数据
this.getData();
},
vue 中获取数据放到 created 中,mpvue 则放到 mounted 中
#9.不支持的 vue 属性
{
1. keep-alive
2. solt
3. class 和 style 的 classObject 和 styleObject 语法。
4. filters (filters 可以使用 motheds 或者 computed 替换)
}
#10. mpvue-loader 版本 1.1 以上需要新建 main.json 作为配置文件(后续
#11. 图片使用相对路径无法加载,放到 dist/static 目录下使用绝对路径即可\*\*
#12.新建页面需要重新 npm run dev 才能生效
#13. 页面返回后没有销毁
{
1. 小程序中页面返回后该页面就会出栈,下次进入重新触发 onLoad
2. mpvue 返回后页面没有销毁,数据会缓存,所以进入页面我们需要重置数据
}
onLoad() {
// 解决页面返回后,数据没重置的问题
Object.assign(this, this.$options.data());
},
methods: {
},
#8.其他注意事项
{ 1.在 Vue 开发 Web 应用的时候,通常使用 vue-router 来进行页面路由;在 mpvue 小程序开发中,不能用这种方式,使用<a>标签和小程序原生 API wx.navigateTo 做路由功能 2.请求后端数据,我们通常在 Web 开发中使用 axios 等 ajax 库来实现;在小程序开发中也是不能用的,使用小程序的原生 API wx.request
}
#最后,思考
{ 1.怎么存放可全局访问的变量? 2.页面跳转的时候,怎么传递参数到下一个页面比较好? 3.页面返回上一页的时候,怎么传递当前页的数据到上一页? 4.多个页面间需要同步数据,怎么做比较好?
}
直接在组件中通过 import 导入 store 所在的模块文件,然后调用该 store 上的相关方法和属性,比如 commit()、dispatch()等方法来操作 store 中的内容
#底部有彩蛋
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
v1
1
1
1
1
1
1
# 使用 vuex 状态管理模式
第一种用法是将 store 绑定到需要访问 store 内容的 Vue 实例上,然后通过该 Vue 实例下组件的 this.$store 来引用;或通过 mapState 等一系列映射函数将 store 中的 state、getters、mutations、actions 等映射成组件的计算属性或 methods 方法来使用;
第二种用法是直接在组件中通过 import 导入 store 所在的模块文件,然后调用该 store 上的相关方法和属性,比如 commit()、dispatch()等方法来操作 store 中的内容。
#上面的这两种方式在 mpvue 中也都是可用的。但是,由于 mpvue 不像 Vue Web 单页应用那种单 Vue 实例的结构,而是采用了多 Vue 实例的结构(app 和各个页面都会由单独的 Vue 实例来管理),推荐采用上面所说的第二种用法,这种方式会更加灵活和简单一些。
#代码,先在 src 目录下新建一个 stores 目录,接着在 stores 目录下新建一个名为 global-store.js 的文件:
import Vue from 'vue'
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment: (state) => {
state.count += 1
},
decrement: (state) => {
state.count -= 1
}
}
});
在这个代码中,我们新建了一个 Store 实例,管理了一个名为 count 的数字类型的状态,并定义了 2 个 mutations 去操作(增减)这个状态值。
接着,我们要在 2 个页面中访问这个 store。让我们在 src/pages 目录下编写 2 个页面:index 和 test1。
这是 pages/index/index.vue 的代码内容:
<template>
<div class="container">
<div>计数结果:{{count}}</div>
<a href="/pages/test1/main" class="navlink">进入计数器页面</a>
</div>
</template>
<script>
import globalStore from "../../stores/global-store";
export default {
computed: {
count() {
return globalStore.state.count;
}
}
};
</script>
<style scoped>
.navlink {
text-decoration: underline;
}
</style>
这是 pages/test1/index.vue 的代码内容:
<template>
<div class="container btns">
<button class="calbtn" @click="hanleDecrement">-</button>
<span class="calnum">{{count}}</span>
<button class="calbtn" @click="hanleIncrement">+</button>
</div>
</template>
<script>
import globalStore from "../../stores/global-store";
export default {
computed: {
count() {
return globalStore.state.count;
}
},
methods: {
hanleIncrement() {
globalStore.commit("increment");
},
hanleDecrement() {
globalStore.commit("decrement");
}
}
};
</script>
<style scoped>
.btns {
display: flex;
align-items: center;
}
.calnum {
color: red;
font-size: 32px;
}
</style>
这样,我们就有了 2 个使用了我们定义的 global-store 的页面,这些页面都会从 store 中获取 count 状态值并显示;在 test1 页面中,还会调用 increment 和 decrement 两个 mutations 去更新 count 值。
运行小程序,可以看到初始进入 index 页面时是这样的,页面上显示的计数结果是 0:
index 页面然后点击“进入计数器页面”进到 test1 页面,并在这个页面上点击加减按钮操作一下,当中显示的 count 数会发生改变:
test1 页面最后,点击左上角返回按钮返回 index 页面,你将发现这个页面上的计数结果也已经发生了改变,自动同步成前面操作后的结果了:8
#使用 Vuex 做页面间的传值和数据同步很简单 #同样可以在 src/stores 目录下按需创建多个 store 模块,独立管理不同业务范围的数据,并按需导入页面组件使用
#vuex 官网https://vuex.vuejs.org/zh/
# 小程序/mpvue 中使用 flyio 发起网络请求
# Fly.js 库是一个基于 Promise 的、强大的、支持多种 JavaScript 运行时的 http 请求库,支持请求/响应拦截器、自动转化 JSON、请求转发等功能
参考了 https://www.jianshu.com/p/579035fc9c18 && https://blog.csdn.net/sinat_36978723?t=1 两位博主