Vue.js开发者如何入门微信小程序

4,893 阅读11分钟

网上已经有很多前端开发者分享了小程序的入坑心得, 本篇文章不刻意重复造轮子, 面向有过Vue.js开发经验正准备接触微信小程序开发的读者.

从框架入手

早些版本的原生小程序基础库存在组件化开发困难npm模块无法使用等问题, 腾讯团队开源了wepy解决了老版原生小程序开发的短板. 后来支付宝,今日头条等app相继推出自己的小程序, 于是mpvue跨小程序端框架出现; 再到后来出现了多端开发框架uni-app, taro等. 使用框架开发小程序的开发者可以按自己的需求和习惯选择框架:

  • Vue系开发者, 不需要跨端: wepy
  • Vue系需跨端的开发者: uni-app
  • React系需跨端的开发者: taro

如果你是在纠结选择哪个框架, 笔者推荐uni-app, 有几点理由:

  • wepy更贴近Vue开发风格
  • 跨端相对靠谱,这里有一篇同类框架评测. (uni-app官方做的,但比较客观用心)
  • 文档完善, 社区和企鹅群相当活跃
  • 插件市场资源较丰富

主要注意的是, 无论使用哪个框架(特别是多端开发框架), 踩坑次数都不会少; 另外小程序基础库也在不断完善, 目前也支持了组件化开发和npm模块, 喜欢清爽简洁方式开发的不妨用原生小程序进行开发.

从原生小程序入手

所谓原生小程序开发, 就是不使用上述对小程序基础库和语法二次封装和构建的框架进行小程序开发的方式.

为了写清楚小程序的语法和能力及其生态规范, 说实话官方文档已经做得不错了, 但同时学习成本也不低. 阅读此文档时, 推荐开发者先快速通读一两遍除服务端相关模块(服务端,云开发)外的部分,并阅读以下笔者针对vue开发者给一些"捷径", 快速熟悉小程序开发.然后再到编码时边写变查, 逐步熟悉.

项目搭建

新建项目 你需要脱离vue-cli等脚手架工具, 下载微信小程序的官方IDE,然后新建一个小程序项目,其中AppID是在微信公众平台创建小程序应用时获取,如果没有客店点击使用测试号,足够开发阶段使用(正式appid才可以正式发布小程序)。

新建项目

新建成功后就有有了一个可运行的demo,这里介绍一下最简单的小程序需要哪些目录和文件:

pages - 存放页面的目录,每个页面自成一个文件夹,需包含同名的js, wxss,json,wxml文件, 下一小节详细介绍
utils - 非必需目录,存放工具方法,公共方法等
app.js - 入口文件,类似vue.js应用中实例化Vue对象的js(一般命名为app.js或者main.js)
app.wxss - 公共样式表(wxss你可以看做css语法子集),这里所写样式都是全局的,你无需在任何文件引入
project.config.json - IDE的配置目录,无需手动配置
sitemap.json - 微信搜索索引配置,配置你的某些页面是否可以在当前小程序内被搜索,有兴趣了解的同学可参考文档

Hello world!

当然,实际项目中目录会稍微复杂,以笔者某个小程序项目为例: 稍微复杂的项目 有些大家已经熟悉的或者已介绍文件不介绍:

components - 存放小程序自定义组件目录 resource - 存放图片、字体等静态文件的目录,习惯上还常命名static config.js - 常量配置文件,当然你可以放在其他你喜欢存放的目录 miniprogram_npm - npm依赖被微信开发者工具二次构建生成的目录,不需手动创建,后面会有npm依赖安装相关的介绍

UI框架 有了基本目录结构,你可能需要一个UI框架来处理反馈(modal、toast、动画)等交互,而不是重新埋头写,这里笔者推荐几个小程序UI框架,具体的安装(推荐npm安装)移步各自文档:

  • vant-weapp vant的小程序版本,强烈推荐
  • iview-weapp iview的小程序版本,已经较久不更新
  • weui 官方出品的微信风格UI,但组件也相对少

构建

编辑器 在发布和调试时,你必须使用微信开发者工具进行。但在编码时,你仍然可以使用自己喜好的编辑器,并安装小程序的相关辅助插件来协助开发小程序, 微信会实时监听项目文件的修改并编译到模拟器。 vscode示例

在小程序使用npm依赖 npm依赖的安装和引入都和node环境下无差别,但你需在安装好依赖或者有依赖更新后在微信开发者工具进行npm构建: 在右上角的详情中勾选

构建npm,并等待完成 构建完成 此时你会发现多了一个miniprogram_npm目录,此时我们就可以直接像node环境一样直接引入npm依赖.

当然,一些像操作DOM等小程序不支持的对象的依赖是无法运行的,需注意区分

ES6 你可以使用ES6,考虑到兼容性时,无需借助babel来转换为ES5,在右上角的详情中勾选: 勾选

环境变量

在node环境,可以在打包时注入一个全局变量用来区分环境,以便处理在生产环境、开发环境下的不同逻辑:

#生产环境变量设置,如:
set NODE_ENV=production 
#开发环境变量设置,如:
set NODE_ENV=development

但是小程序里,官方一直没有做这方面的支持!所以你需在开发或者发布的时候手动改一下用来充当环境区分的变量:

//示例,config.js
const ENV = 'product'; //环境变量,手动修改
module.exports = {
  ENV,
  DOMAIN: ENV === 'product'? 'a.com': 'dev.a.com',
  TOKEN_KEY: ENV === 'product'? 'token' : 'token_dev',
  //more...
}

编写页面

页面的构造,页面的构造需使用Page方法。详细的属性和生命周期你可以在文档中查看。

为了页面生效,你还必须在根目录下的app.json中的pages字段配上所有页面的路径, 并且pages数组第一个为小程序进入的首个页面:

{
 "pages": [
   "pages/index/index", //进入小程序时加载的第一个页面
   "pages/logs/logs"
 ],
 //more...
}

新建页面

在pages目录右键 - 新建一个页目录

在页面目录右键 - 新建页面文件

页面组成文件

假设新建的页面叫page1,则会生成四个文件:

page1.js - Page方法的调用,处理页面逻辑
page1.json - 当前页面的配置文件,比如设置页面背景色、导航栏标题等
page1.wxml - 页面视图,类似html语法,但你使用的标签需是小程序自有组件或者自定义组件名(下小节介绍);另外,view标签的地位相当于html中的div;并且,指令和事件绑定的语法和vue不同(如:v-if对应wx:if),具体介绍查看这里模板那一块看的不太明白可以先放下,因为你很可能可以使用组件的方式替代它。
page1.wxss - 你几乎可以把它当成一个css文件来看,只是不需要手动引入就会在当前页面生效。具体与css的差异可查看这里

注意: 小程序直接修改Page里的data并不会更新视图,你需通过Page.prototype.setData(文档搜索Page.prototype.setData查看)方法来设置value才可更新视图

组件

在较新的小程序版本已经支持自定义组件,但开发体验笔者认为不如vue的单文件组件。这里做一些简单的对比介绍。

组件化

在微信开发者工具快速新建一个组件

组件文件构成

与页面的构成类似,组件的四个文件作用同页面的一样, 不同的主要有两点:

  • js文件里, 定义组件使用Component构造器.
  • json文件的component字段设为true,如:
{
  "component": true
}

组件的调用

页面或者其他组件目录下的json文件, 将所有引用的组件的标签名(key,自己取名)和文件路径(value)传入usingComponents

{
	"usingComponents": {
		"component-tag-name1": "/components/component-tag-name1/component-tag-name1",
		"component-tag-name1": "/components/component-tag-name1/component-tag-name1"
        //more component...
	}
    //more config...
}

并在wxml文件写入组件标签及其传入的参数,事件等:

<component-tag-name1></component-tag-name1>
<component-tag-name2 option="{{yourOptionData}}" bindSomeEvent="eventCallback" />

下面介绍几个传入Component构造器的几个重要属性.

properties 相当于Vue中的props属性, 约定父组件或者页面传入的参数格式:

Component({
  properties: {
    data1: {
      type: Number, //参数的类型
      value: 0 //默认值
    },
    data2: {
      type: Number,
      optionalTypes: [String, Object], // 可以指定多个属性的类型, 如果type也定义了的话或被计入optionalTypes, 这里的参数可以是 Number 、 String 、 Boolean 三种类型中的一种
      observer: function(newVal, oldVal) {  //参数值变化时的回调函数, 与vue中`watch`的作用类似
      }
    }
  }
})

behaviors

behavior相当于vue中的mixins, 首先你得先构造一个Behavior:

module.exports = Behavior({
  behaviors: [], //behavior里可以引用其他behavior
  properties: { //要共享的参数
    myBehaviorProperty: {
      type: String
    }
  },
  data: { //要共享的data
    myBehaviorData: {}
  },
  attached: function(){}, //要共享的生命周期处理逻辑
  methods: { //要共享的方法
    myBehaviorMethod: function(){}
  }
})

然后在组件中引入:

// my-component.js
var myBehavior = require('my-behavior')
Component({
  behaviors: [myBehavior],
  properties: {
    myProperty: {
      type: String
    }
  },
  //more...
})

observers

作用与vue中的watch属性类似, 可以监听一个或多个数据变化并执行回调, 但语法上不同,详情请移步文档,这里不再赘述

生命周期

在小程序里, 组件的生命周期与页面的生命周期并不相同(这是因为,在vue里,无论页面和组件都是vue实例, 都可以看成组件, 而在小程序是不一样的实例), 并且App注册小程序时也有自己的生命周期.

小程序组件的生命周期与Vue中的生命周期时间节点基本一致, 只是命名上的差异;并且生命周期方法可以作为和data同级的属性, 也可以放在lifetimes属性里,但后者的优先级更高:

Component({
  lifetimes: {
    attached: function() {
      // 在组件实例进入页面节点树时执行
    },
    detached: function() {
      // 在组件实例被从页面节点树移除时执行
    },
  },
  // 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
  attached: function() {
    // 在组件实例进入页面节点树时执行
  },
  detached: function() {
    // 在组件实例被从页面节点树移除时执行
  },
  // ...
})

另外组件还有一组生命周期可使用, 即组件所在页面的生命周期(但并不是页面所有生命周期方法, 也没这个必要), 只有show,hide,resize; 所在页面生命周期回调写在pageLifetimes属性:

Component({
  pageLifetimes: {
    show: function() {
      // 页面被展示
    },
    hide: function() {
      // 页面被隐藏
    },
    resize: function(size) {
      // 页面尺寸变化
    }
  }
})

组件通信和事件

与vue的通信方式一样,事件组件和页面或者组件和组件主要和提倡的通信方式, 并且在小程序里, 也倡导单向数据流规范. 在页面或者组件的wxml绑定事件:

<!-- 当自定义组件触发“myevent”事件时,调用“onMyEvent”方法 -->
<component-tag-name bindmyevent="onMyEvent" />
<!-- 或者可以写成 -->
<component-tag-name bind:myevent="onMyEvent" />

在组件里触发事件:

Component({
  properties: {},
  methods: {
    onTap: function(){
      var myEventDetail = {} // detail对象,会作为参数传递到接收事件的方法里
      var myEventOption = {} // 触发事件的选项, 主要控制事件是否可冒泡或者可有捕获截断,详情可参考文档
      this.triggerEvent('myevent', myEventDetail, myEventOption) //触发事件`myevent`
    }
  }
})

路由

组件介绍完毕, 小程序的路由机制不依赖其他插件, 由小程序自己控制. 并且开放出来的能力只限于作页面跳转并可携带参数, 像路由变化回调等能力并不开放, 跳转携带参数时可在目标页面的onLoad生命周期方法的参数中接收:

//more...
//假设当前页面路由为'/pages/demo/demo?a=1&b=2'
onLoad: function (options) {       //options用于接收上个页面传递过来的参数
    console.log(options.a, options.b); // =>1, 2
})
//more...

路由跳转的几个方法你可以直接看文档,这里不详细介绍,只需注意理解的是页面栈的概念(类似于浏览器环境中的history对象) ,和跳转到tabbar页面需用wx.switchTab方法.

全局变量/全局方法

vue的开发过程中, 定义一个全局方法/属性时, 我们通常做法是挂到Vue对象的原型上:

Vue.prototype.data1 = 'xxx';
Vue.prototype.method1 = ()=>{
};

//在Vue实例里可以方便的访问到:
//more...
mounted(){
  console.log(this.data1);
  this.method1()
}
//more...

当然,一些经常变动和访问的状态我们会交给Vuex处理. 小程序里,全局变量/方法可以在App方法中定义:

App({
  onLaunch (options) {
    // Do something initial when launch.
  },
  globalData: { //全局变量放在globalData
      data1: '1',
      data2: '2'
  },
  //全局方法直接定义在根级别
  method1(){
  }
})

调用:

const app = getApp();

Page({
	data: {
	},
	onLoad() {
		app.method1();
		console.log(app.data1, app.data2);  //=>1, 2
    }
    //more...
})

另外, 你还可以将它们直接挂到global对象中, 但是一般不推荐这么做.

global.data1 = '';

调试

微信开发者工具是用开源的chrome内核二次开发, 在微信开发者工具中调试小程序, 跟在chrome调试web几乎一样; 可惜的是你并不能下载一些比如'vue.js Devtools'这样的扩展来协助调试, 所以很多时候你只能通过断点打印log来调试. 虽然工具提供了app级别的组件结构和数据视图, 但在笔者看来作用甚微(你可以体验一下): 查看页面的data和页面构成

测试

vueVue Test Utils类似, 小程序提供miniprogram-simulate用于调试组件, 配合jest之类的测试框架可以实现组件的测试.

更多细节开发者可以慢慢摸索, 感谢阅读!