简介 #
YKit-Config-WMP(YKit Config for WeChat Mini Program)是为微信小程序项目量身定制的构建方案和打包服务。基于开源构建工具 YKit。目标是让开发人员更方便、更快捷的进行小程序项目的开发,同时尽可能减少代码体积,以满足平台对项目 size 的限制。
YKit-Config-WMP 不仅仅是个工具,它也提供了组件机制、插件机制、环境配置、业务隔离、数据 Mock 等功能。
之后,计划支持蚂蚁应用、小米应用、即点即用应用等类小程序应用的构建和转换。
安装 #
ykit-config-wmp 依赖于 ykit,请首先安装 ykit:
sudo npm install ykit -g --registry=http://npmrepo.corp.qunar.com/
安装 ykit-config-wmp:
npm install @qnpm/ykit-config-wmp --save --registry=http://npmrepo.corp.qunar.com/
快速上手 #
下面通过酒店模块 mp_module_hotel 演示整个开发环境是如何运行起来的。
- 首先 clone 要进行开发的模块项目,这里是 mp_module_hotel:
git clone git@gitlab.corp.qunar.com:mini_program/mp_module_hotel.git
- 进入项目目录:
cd mp_module_hotel
// 安装工具 ykit-config-wmp
npm install @qnpm/ykit-config-wmp --save --registry=http://npmrepo.corp.qunar.com/
- 安装必要的依赖的项目模块 home_qunar、common:
// 确保全局已经安装了 ykit
ykit install common
ykit install home_qunar
- 编译项目代码
ykit g
现在整个项目已经编译完成,用微信开发者工具对编译生成的目录 dist
进行预览即可:
打开微信开发者工具 -> 新建项目 -> 选择项目目录为编译生成的目录 dist
PS:也可以使用
ykit g -w
这样src
目录下内容被修改会实时进行编译, 这样就可以愉快的实时开发编译了。
工程化与目录结构 #
工程化的概念 #
一个小程序项目由三个模块组成:
- 公共模块 mp_module_common
- 首页模块 mp_home_xxx
- 业务模块 mp_module_xxx
home_xxx 包括小程序的首页,common 模块主要是一些公共页面(我的、订单)及公共组件。 一般这两个模块是公司小程序项目必须的。
每个模块对应一个 git 工程。每个小程序也对应一个 git 工程 mp_app_xxx
,在其 package.json
的 modules
里配置了组成该小程序所引用的模块。这样同一小程序,不同业务线在独立工程下开发,互不影响。
模块工程目录结构 #
.
├── README.md # ReadMe
├── dist # 编译后的代码目录(微信开发者工具创建项目,请选择此目录)
├── node_modules
├── package.json # 工程配置
├── libs # 其他模块安装目录
├── src # 源码路径
└── ykit.js # Ykit 配置
关于 ykit.js
的配置:
module.exports = {
plugins: ['wmp'],
config: function() {
}
};
业务隔离 #
app.json #
每个业务模块,可以在自己的工程目录下配置仅有关自己业务的 app.json
。
假设现有名为 hotel
,train
的两个业务,分别在其项目下有 app.json
配置:
// hotel app.json
{
"pages": [
"pages/list/list"
]
}
// train app.json
{
"pages": [
"pages/search/search"
]
}
那么在构建项目时,会自动合并所有模块 app.json
的路由配置到小程序项目最终的 app.json
中,并在每个路由前添加其业务名。以上构建结果:
{
"pages": [
"hotel/pages/list/list",
"train/pages/search/search"
]
}
app.js #
每个模块可以定义自己的 app.js
。定义规则是 module.exports
导出一个 object
对象,其指定小程序的生命周期函数,不包含其他函数或数据。对于一些其他函数或数据应该写在项目的 app.js
中。 例:
// mp_module_common 模块 app.js
module.exports = {
onLaunch: () => {
console.log('Common Launch');
},
onShow: () => {
console.log('Common Launch');
}
};
注意:
mp_home_xxx
模块是小程序入口,其app.js
即项目app.js
,会去构建小程序项目最终app.js
。定义规则是object
作为参数传入APP()
函数中。object
可以添加任意其他函数或数据。
global.wxss #
每个模块可以定义自己的 global.wxss
全局样式。在打包构建的时候会自动在项目的 app.wxss
中引入。
使用 Sass #
如果想使用 Sass 语法进行样式定义,可以将样式文件后缀名 .wxss
改成 .scss
或 .sass
,即可使用 Sass。其他不做任何修改,.scss
文件中可以正常引入 .wxss
和其他 .scss
文件。构建打包时,工具会将 .scss
或 .sass
文件编译成 CSS 语法的 .wxss
文件。
基本配置 #
分包加载配置 #
使用小程序分包加载能力。小程序分包加载文档
在项目的 package.json
文件中添加 "subModules": ["packageA", "packageB"]
属性进行分包配置。
模块项目中配置这个只是测试使用。最终发布会由产品确定哪些模块放在分包中,然后公共开发在 mp_app_qunar 项目中配置。分包会在第一次进入分包内页面进行下载,下载完再进行展示。
例如,当前项目有以下路由:
{
"pages": [
"pages/index",
"pages/logs",
"packageA/pages/cat",
"packageA/pages/dog",
"packageB/pages/apple",
"packageB/pages/banana"
]
}
添加分包配置:{ "subModules": ["packageA", "packageB"] }
打包出的分包路由结构如下:
{
"pages":[
"pages/index",
"pages/logs"
],
"subPackages": [
{
"root": "packageA",
"pages": [
"pages/cat",
"pages/dog"
]
}, {
"root": "packageB",
"pages": [
"pages/apple",
"pages/banana"
]
}
]
}
环境配置 #
在项目配置的 app.json
中,可以添加 env
配置。例如,项目 app.json
中有如下 env
配置:
{
"env": {
"prod": {
"server": "https://some.com"
},
"beta": {
"server": "https://beta.some.com"
}
}
}
注意:
mp_home_xxx
模块配置的app.json
即为项目的app.json
同样的,在业务 biz
项目中的 app.json
也可以配置 env
:
{
"env": {
"prod": {
"server": "https://bizsome.com"
},
"beta": {
"server": "https://beta.bizsome.com"
},
"beta1": {
"server": "https://beta1.bizsome.com"
}
}
}
按照以上配置,在默认的环境 prod
下,构建完成后全局变量里会存在:
global.__envType = 'prod';
global.__env = {
"server": "https://some.com",
"biz": {
"envType": "prod",
"server": "https://bizsome.com"
}
};
开发者,可以在 ykit generate
,ykit pack
命令后,加 -e | --env
参数进行环境切换。例如 -e beta
,此时环境如下:
global.__envType = 'beta';
global.__env = {
"server": "https://beta.some.com",
"biz": {
"envType": "beta",
"server": "https://beta.bizsome.com"
}
};
业务和主项目可以使用不同的环境,例如参数为 -e beta,biz:beta1
,非 biz
的其他业务以及主项目都为 beta
环境,biz
业务为 beta1
环境。
别名配置 #
在项目和业务项目配置的 app.json
中,可以添加 alias
配置,key
即为别名,value
引用文件路径,相对于项目根路径:
{
"alias": {
"user": "./user/user.js",
}
}
项目中 require('user')
,即可引用 ./user/user.js
文件
资源配置 #
资源文件需要在项目和业务项目配置的 app.json
中,添加 asserts
配置。项目中除了 .js
、.json
、.wxml
、.wxss
文件,其他资源文件需要配置 asserts
。这样项目构建时才会加载这些资源文件。例如项目根目录下有 images
文件夹,存放图片,则需要配置:
{
"asserts": ["images"],
}
页面忽略配置 #
在项目配置的 app.json
中,可以添加 ignorePages
配置,添加忽略构建的页面路径,小程序中将不存在该页面也访问不到该页面。例:
{
"ignorePages": ["pages/my/my"]
}
路径跟项目 app.json
中配置的路由相同。
高级配置 #
获取配置参数 #
可以通过 Config('xxx')
获取项目 package.json
中的配置参数。例:
// package.json
{
"version": "1.0.0"
}
// 使用 Config 获取
var v = Config('version');
// 构建后代码 =>
var v = ('1.0.0');
Mock 配置 #
开启 mock 模式之前需要配置 mock 接口以及 mock 数据。
- 在项目根目录下创建 mock 文件
./mock/mock.js
- 在
mock.js
里书写 mock 配置信息,例:module.exports = { // 配置接口 host 'wxapp.xxx.com': { // [配置接口路径 hostname]: [mock 数据] '/api/user/info': { name: 'ming.xiao', age: 18 }, '/api/admin/info': { name: 'san.zhang', age: 30 } } }
- 运行
ykit g --mock
构建项目并开启 mock 模式
现在打开小程序开发者工具,选择 dist
作为项目目录。我们发现 https://wxapp.xxx.com/api/user/info
请求变为了 https://127.0.0.1/api/user/info
,请求的是本地开启的 mock server。并且返回我们配置的 mock 数据 { name: 'ming.xiao', age: 18 }
。这就是 mock 最简单的使用方式。
注意:请在小程序开发者工具侧边栏『项目』配置中勾选『开发环境不校验请求域名』,否则 mock 请求不在合法域名下,无法成功发送。
mock 数据模板 #
mock 数据支持 json
对象,对象还支持数据模板规范。详见 Mock.js 语法规范,例:
// 模板
{
'number1|1-100.1-10': 1,
'regexp1': /[a-z][A-Z][0-9]/,
}
// =>
{
"number1": 12.92,
"regexp1": "pJ7",
}
mock 函数 #
mock 数据还支持函数,函数返回对象将作为数据模板。函数接受一个 req
对象作为参数。req
类似 Node http.createServer((req, res)...
中 req
,有 url
,headers
等属性。req
还有一个特殊属性 param
,它是包含 query
和 body
中请求参数的对象。例:
// mock.js
{
'api/info': function(req) {
return req.param;
}
}
// request
// url => https://wxapp.xxx.com/api/info?username=zzz
// body => password=123456
// 返回数据
{
username: 'zzz',
password: 123456
}
注意:忽略 host。看到上面的
mock.js
配置中,我没有配置 host,直接配置的api/info
接口,这是允许的。这将忽略 host,只要接口路径匹配即请求 mock。
推荐配置目录结构 #
由于 mock 数据肯能比较冗长,不推荐数据直接写在接口后面,而是单独提取成文件。这样 mock.js
文件,接口配置一目了然。例:
// mock.js
module.exports = {
'wxapp.xxx.com': {
'/api/user/info': require('./user.js'),
'/api/admin/info': require('./admin.js')
},
'api/info': require('./info.js')
}
然后在 ./mock
里有如下目录结构:
.
├── mock.js // mock 接口
├── user.js // mock 数据
├── admin.js // mock 数据
└── info.js // mock 数据