mpx脚手架中使用的模板为mpx-template,里面做了一些配置化的东西,如果了解源码后,可以自定义模板和脚手架。
git地址(2019年12月19日版本):https://github.com/mpx-ecology/mpx-template
目录结构如下:
.
├── LICENSE
├── README.md
├── __test__ // 测试用脚手架中用户预设答案渲染模板命令
│ ├── testWxCrossNoWeb.js
│ ├── testWxCrossWeb.js
│ ├── testWxNoCross.js
│ └── testWxPlugin.js
├── meta.js // 模板配置入口文件
├── package-lock.json
├── package.json // 安装包
├── realMeta.js // 模板配置包括prompts/cumputed/filters/complete
├── template // 模板
│ ├── README.md // 模板说明
│ ├── build // 模板打包文件
│ │ ├── build.js // 打包配置
│ │ ├── mpx.plugin.conf.js // mpx配置
│ │ ├── webpack.conf.js // 常规webpack配置
│ │ └── webpack.plugin.conf.js // 插件配置
│ ├── config // 运行和打包配置
│ │ ├── dev.env.js // 运行配置
│ │ └── prod.env.js // 生产配置
│ ├── functions // 目前为空
│ ├── package.json // 安装包等配置
│ ├── project.config.json
│ ├── src //
│ │ ├── app.mpx // 入口文件
│ │ ├── app.ts // 使用ts的入口文件
│ │ ├── components // 组件
│ │ │ ├── list.mpx
│ │ │ └── list.ts
│ │ ├── index.html // 生成web端入口文件
│ │ ├── miniprogram // 插件
│ │ │ ├── app.mpx
│ │ │ └── pages
│ │ │ └── index.mpx
│ │ ├── pages // 常规
│ │ │ ├── index.mpx // 首页
│ │ │ └── index.ts // 使用ts
│ │ └── plugin // 插件
│ │ ├── components // 插件的组件
│ │ │ └── list.mpx
│ │ └── plugin.json // 插件配置
│ ├── static // 静态资源配置
│ │ ├── ali
│ │ │ └── mini.project.json
│ │ └── wx
│ │ └── project.config.json
│ └── tsconfig.json // ts配置文件,选用ts时需要
└── testfile // 测试用meta文件
├── wx-cross-noweb-meta.js
├── wx-cross-web-meta.js
├── wx-no-cross-meta.js
└── wx-plugin-meta.js
module.exports = {
prompts: {
mode: {
type: 'list',
required: true,
message: '请选择小程序项目所属平台(目前仅微信下支持跨平台输出)',
choices: ['wx', 'ali', 'swan', 'qq', 'tt'],
default: 'wx'
},
cross: {
when: 'mode === "wx"',
message: '是否需要跨小程序平台',
type: 'confirm',
default: true
},
transWeb: {
when: 'mode === "wx" && cross === true',
message: '是否需要支持输出web',
type: 'confirm',
default: true
},
cloudFunc: {
when: 'mode === "wx" && cross === false',
message: '是否需要使用小程序云开发能力',
type: 'confirm',
default: false
},
tsSupport: {
message: '是否需要使用TS?',
type: 'confirm',
default: false
},
name: {
type: 'string',
required: true,
message: '项目名称'
},
description: {
type: 'string',
required: false,
message: '项目描述',
default: 'A mpx project'
},
author: {
type: 'string',
message: '作者'
},
isPlugin: {
when: 'mode === "wx" && cross === false && cloudFunc === false',
type: 'confirm',
message: '是否是个插件项目?(不清楚请选 No !什么是插件项目请看微信官方文档!)',
default: false
},
appid: {
when: 'mode === "wx"',
required: true,
message: '请输入小程序的Appid',
default: 'touristappid'
},
needEslint: {
type: 'confirm',
message: '是否需要ESlint',
default: true
}
},
computed: {
dirFor () {
switch (this.mode) {
case 'wx':
return 'wx:for'
case 'ali':
return 'a:for'
case 'swan':
return 's-for'
case 'qq':
return 'qq:for'
case 'tt':
return 'tt:for'
}
},
dirKey () {
switch (this.mode) {
case 'wx':
return 'wx:key'
case 'ali':
return 'a:key'
case 'swan':
return 's-key'
case 'qq':
return 'qq:key'
case 'tt':
return 'tt:key'
}
}
},
filters: {
'src/@(miniprogram|plugin)/**/*': 'isPlugin',
'build/webpack.plugin.conf.js': 'isPlugin',
'src/index.html': 'transWeb',
'src/!(miniprogram|plugin)/**/*': 'mode !== "wx" || !isPlugin',
'src/*': 'mode !== "wx" || !isPlugin',
'project.config.json': 'mode === "wx" && !cross',
'static/**/*': 'cross',
'tsconfig.json': 'tsSupport',
'.eslintrc.js': 'needEslint',
'**/*.ts': 'tsSupport',
'functions/*': 'cloudFunc'
},
complete: function (data, { chalk }) {
const green = chalk.green
console.log(green('complete!'))
}
}
mode : 交互字段名称,可在后续条件交互或模板渲染时通过该字段读取到交互结果
type : 交互类型,有 input, confirm, list, rawlist, expand, checkbox, password, editor 八种类型
message : 交互的提示信息
when : 进行该条件交互的先决条件,在该例子中,cross 这个交互动作只在’mode === “wx”'时才会出现
default : 默认值,当用户输入为空时,交互结果即为此值
required : 默认为 false,该值是否为必填项
validate : 输入验证函数
辅助函数只可以控制文件内一部分内容的输出与否,有时候我们需要根据交互结果控制某些文件本身是否输出。 可以达到控制文件输出的效果:
module.exports = {
//…
“filters”: {
‘src/@(miniprogram|plugin)/**/*’: ‘isPlugin’,
‘build/webpack.plugin.conf.js’: ‘isPlugin’,
},
//… }
filters 中键名是要控制输出的文件的路径,可使用字面量,也可使用 简化的 glob 表达式。键名对应的值为命令行交互中得到的数据。
以上这些命令将在脚手架中的generate函数中使用,具体如下,更多详细前往 @mpx/cli 脚手架源码解析
(opts.mock
? metalsmith.use(mock(opts.mock))
: metalsmith.use(askQuestions(opts.prompts))) // 询问问题
.use(computed(opts.computed)) // 处理关键词
.use(filterFiles(opts.filters)) // 过滤文件
.use(renderTemplateFiles(opts.skipInterpolation)) // 渲染模板文件
关于模板中局部代码的渲染,则使用nunjucks插件,具体语法可查看nunjucks文档
如package.json文件:
{
"name": "<$ name $>",
"version": "1.0.0",
"description": "<$ description $>",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"devbuild": "node ./build/build.js",
"watch": "node ./build/build.js -w",
"build": "node ./build/build.js -p",
"prod": "node ./build/build.js -p",
{% if cross %}
"watch:cross": "{% if transWeb %}npx npm-run-all --parallel 'watch --wx --ali --swan --web' httpserver{% else %}npm run watch --wx --ali --swan{% endif %}",
"build:cross": "npm run build --wx --ali --swan{% if transWeb %} --web{% endif %}",
"prod:cross": "npm run prod --wx --ali --swan{% if transWeb %} --web{% endif %}",
{% endif %}
{% if transWeb %}
"watchweb": "npx npm-run-all --parallel 'watch --web' httpserver",
"httpserver": "npx http-server dist/web",
{% endif %}
{% if needEslint %}
"lint": "eslint --ext .js,.mpx src/",
{% endif %}
"help": "node ./build/build.js --help"
},
"author": "<$ author $>",
"license": "ISC",
"dependencies": {
"@mpxjs/api-proxy": "^2.3.0",
{% if transWeb %}
"vue": "^2.6.10",
{% endif %}
"@mpxjs/core": "^2.3.0"
},
"devDependencies": {
"copy-webpack-plugin": "^5.0.3",
"@mpxjs/webpack-plugin": "^2.3.0",
{% if transWeb %}
"http-server": "^0.12.0",
"npm-run-all": "^4.1.5",
"extract-text-webpack-plugin": "^3.0.2",
"html-webpack-plugin": "^3.2.0",
"vue-loader": "^14.2.4",
"vue-router": "^3.1.3",
"vue-template-compiler": "^2.6.10",
"style-loader": "^1.0.1",
{% endif %}
{% if needEslint %}
"eslint-loader": "^2.1.1",
"babel-eslint": "^10.0.1",
"eslint": "^5.10.0",
"eslint-config-babel": "^8.0.2",
"eslint-config-standard": "^12.0.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-plugin-html": "^5.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-local-rules": "^0.1.0",
"eslint-plugin-node": "^8.0.0",
"eslint-plugin-prettier": "^2.6.2",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
{% endif %}
{% if tsSupport %}
"ts-loader": "^6.0.0",
"typescript": "^3.5.0",
{% endif %}
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-runtime": "^6.26.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.24.1",
"chalk": "^2.3.2",
"css-loader": "^0.28.11",
"file-loader": "^1.1.11",
"html-loader": "^0.5.5",
"ora": "^2.0.0",
"path": "^0.12.7",
"rimraf": "^2.6.2",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2",
"url-loader": "^1.0.1",
"webpack": "^4.16.1",
"webpack-cli": "^3.1.0",
"webpack-merge": "^4.1.2",
"webpack-bundle-analyzer": "^3.3.2"
}
}
{% if cross %} … {% endif %} 即为条件判断渲染语句。
另外为了避免变量插值标签与小程序的动态绑定语法冲突,可以自定义一个动态插值标签,如下:
nunjucks.configure({
tags: {
variableStart: '<$',
variableEnd: '$>'
},
autoescape: false,
trimBlocks: true,
lstripBlocks: true
})
页面中其他文件的动态输出类似;
总体来说,整个模板分为几大块,一是自测的脚本和配置文件,二是实际的模板内容;其中模板内容包含了所有的相关文件,可以根据用户的选择配置渲染生成,包括过滤相关的文件,以及模板渲染语法来控制输出的代码内容。如果需要自定义模板的,在上面添加对应的配置即可。