demand

Development of small program friends at any time will hear a sentence: “hello, give me a XXX environment preview code”, no matter what you are doing, have to hurry back to a: “wait, this will give you play code……”

Then helplessly and painfully you build a XXX environment package, open the wechat developer tool, click the preview, wait for a while, the preview code comes out, you copy and throw to your dads.

Finally one day, when you are concentrating on something indescribable, “Hey, please type me a preview code of XXX environment”, and you roar inside: “I won’t type code for you! Do it yourself!”

So there is this demand, to make something for dads to code, well, there should be only one button, click on it can appear preview two-dimensional code things, it should be like this:

That’s right! Do it!

Planning a

Big things start with your head in the clouds. Now think about what you need to do to make this feature work.

The most important thing to find the interface of wechat developer tools is to see whether wechat developer tools provide us with such an interface for us to operate, after a review of the document we will find that indeed there is!

Developers. Weixin. Qq… . As you will see, the documentation provides us with two types of interfaces, command line calls and HTTP calls. With the interface, everything is easy to do, nothing more than to adjust the interface, get the TWO-DIMENSIONAL code, paste to the page, it is very simple.

Comb the development process

Let’s use a flow chart to illustrate this simple thing:

www.processon.com/vie…

The required technical

If a worker wants to do a good job, he must sharpen his tools first. If we want to develop this thing, we must first sort out the technologies used.

  1. Wechat developer tools
  2. A small program project (here is an mpvue project as an example)
  3. Front end VUE + VUX, there is nothing to do on the front end, this combination is purely because we are already working on the mobile side of things, just use it.
  4. The backend KOA2, of course, can be used for anything, but I chose KOA2 because I can’t use anything else…
  5. Use AXIOS for both front-end and back-end HTTP requests
  6. Shelljs is used for node operations on the command line

I don’t think I have anything else. We’ll see if we need it.

Roll up your sleeves and start at the back end

To save trouble, just put the front and rear ends together. Project Contents:

You can see that the server directory is all the stuff on the back end.

The back end needs to do two things. First, it needs to be able to access static resources built by the front end. Second, it needs to be able to interact with the front end through HTTP interface. See the code:

const path = require('path') const Koa = require('koa') const koaStatic = require('koa-static') const bodyParser = require('koa-bodyparser') const router = require('./router') const app = new Koa() const port = 9871 App.use (bodyParser()) app.use(koaStatic(path.resolve(__dirname, '.. /dist')) // app.use(router.routes()).use(router.allowedmethods ()) // Listen (9871) console.log(' [demo]] start-quick is starting at port ${port}`)Copy the code

For static resources, use koA-static. The emphasis is on how to provide an interface to the front end, which depends on the route.

server/router/index.js

Const Router = require('koa-router') // /controller/wx') const router = new router ({// open prefix = / API /open prefix: '/api' }) router.get('/open', wx.open) .get('/login', wx.login) .get('/preview', wx.preview) .get('/build', wx.build) module.exports = routerCopy the code

It can be clearly seen that the back end provides four interfaces, but the business logic of each interface is encapsulated in wx.js in the controller. If there are other business logic in the future, the corresponding module can be added to the controller.

server/controller/wx.js

Const {open, login, preview, build} = require('.. /utli/wxToolApi') const {successBody, errorBody} = require('.. /utli') class WxController {/** * package the mpvue project according to the environment * @returns {Promise<void>} */ static async build (CTX) {// Const query = ctx.request. Query if (! query || ! Query.env) {ctx.body = errorBody(null, 'build project failed ') return} const [err, data] = await build(query.env) ctx.body = err? ErrorBody (err, 'failed to build project ') : */ static Async Open (CTX) {const [err, data] = await open() ctx.body = err ? ErrorBody (err, 'failed to open wechat developer tool ') : SuccessBody (data, 'open wechat developer tools successfully ')} /** * login to wechat developer tools * @returns {Promise<void>} */ static Async login (CTX) {const [err, data] = await login() ctx.body = err ? ErrorBody (err, 'Failed to log in to qr code ') : */ static Async Preview (CTX) {const [err, const [err, const [err, const [err, const]; data] = await preview() ctx.body = err ? ErrorBody (err, 'error ') : successBody(data,' error ')}} Module.exports = WxControllerCopy the code

In order to make the code clearer, the interface logic for operating wechat developer tools has been extracted into util/ wxToolapi.js, only dealing with how to return to the front end in a uniform format. util/wxToolApi.js

const {promiseWrap, successBody, errorBody} = require('.. /utli') const {INSTALL_PATH, PROJECT_PATH, PORT_PATH, PORT_FILE_NAME, HOST} = require('.. /const') const {readFile} = require('.. /utli/nodeApi') const shell = require('shelljs') const axios = require('axios') module.exports = { /** * * @param env [doc, pre, PRD] * @returns {*} */ build (env) {return promiseWrap(new Promise((resolve, resolve)) Reject) => {shell. CD (PROJECT_PATH) shell. Exec (' NPM run build:${env} ', function (code, stdout, stderr) { resolve(stdout) }) })) }, /** * open () {return promiseWrap(new Promise((resolve, resolve)) Reject) => {// go to the project directory shell. CD (INSTALL_PATH) // Run the command line startup tool shell.exec(' cli -o ${PROJECT_PATH} ', function (code, stdout, stderr) { if (stderr) return reject(stderr) resolve(stdout) }) })) }, // async getPort () {shell.cd(PORT_PATH) // The HTTP service is automatically started after the tool starts. The HTTP service port number is recorded in the user directory. You can check whether the tool is installed or started by checking the user directory, checking whether port files exist in the user directory, and trying to connect to the user directory. const [err, data] = await readFile(PORT_FILE_NAME) return err ? ErrorBody (err, 'failed to read port number file ') : SuccessBody (data, 'port number file read successfully ')}, Return promiseWrap(new Promise(async (resolve, resolve, resolve)); Reject) => {const portData = await module.exports.getPort() if (portdata.code! == 0) { reject(portData) return } const port = portData.data axios.get(`http://${HOST}:${port}/login? format=base64`) .then(res => { resolve(res.data) }) .catch(e => { reject(e) }) })) }, /** * @returns {*} */ preview () {return promiseWrap(new Promise(async (resolve, reject) => { const portData = await module.exports.getPort() if (portData.code ! == 0) { reject(portData) return } const port = portData.data axios.get(`http://${HOST}:${port}/preview? format=base64&projectpath=${encodeURIComponent(PROJECT_PATH)}`) .then(res => { resolve(res.data) }) .catch(e => { reject(e) }) })) } }Copy the code

One thing to note here is why only the open interface needs command line invocation. That’s because HTTP calls must have ports, such as the Open interface

# Open tools http://127.0.0.1: port number /open # Open/refresh project http://127.0.0.1: port number /open? Projectpath = full projectpathCopy the code

If you have not opened the wechat developer tool at all, you will not find the port in the following places:

Port number file location: macOS: ~/Library/Application Support/ wechat Web developer tools /Default/.ide Windows: ~/AppData/Local/ wechat Web developer tools /User Data/Default/.ideCopy the code

So as a fully automated coding tool, how can I even manually open the wechat developer tools!

The front end

The back end is basically so much, finally to the front end, the front end is very simple, not to say:

<template> <div> <group title=" please select environment ">< radio :options="envOption" V-model ="env"></radio> </group> < X-button class=" BTN" <div v-if="loginImg" class="code"> <div v-if="loginImg" class="code"> <divider> Please login to the </divider> <img class="code-img" : SRC ="loginImg" Alt =""> </div> <div v-if="preImg" class="code" Id = "preImg" > < divider > preview qr code < / divider > < img class = "code - img:" SRC = "${base64Prefix} ${preImg} ` `" Alt = "" > < / div > < / div > </template> <script> import {openProject, login, previewProject, buildProject} from 'SERVICES/index' import {showLoading, hideLoading} from 'UTILS' import { Divider, XButton, Radio, Group} from 'vux' export default {data () {return {// data indicates the protocol name of the data, image/ PNG indicates the name of the data type, base64 indicates the encoding method of the data, After the comma is the base64 encoded data of the image/ PNG file. base64Prefix: 'data:image/png; PreImg: ", // Env defaults to doc: 'doc', // All environment options envOption: ['doc', 'pre', 'prd'] } }, components: { Divider, XButton, Radio, Group }, methods: { handleError (msg) { alert(msg) }, async login () { const {data: {code, data, msg}} = await login() if (code ! == 0) { this.handleError(msg) return code } this.loginImg = data return code }, async previewProject () { const {data: {code, data, msg}} = await previewProject() if (code ! == 0) { this.handleError(msg) return code } this.preImg = data return code }, Async handlePreviewProject () {showLoading() // reset the code this.resetimg () // open wechat developer tool const {data: {code}} = await openProject() if (code ! = = 0) {/ / login WeChat developer tools await this. Login hideLoading () () return} / / according to environmental packaging await buildProject (enclosing env) / / preview await this.previewProject() hideLoading() }, resetImg () { this.loginImg = '' this.preImg = '' } } } </script> <style lang='less'> .btn { width: 90%! important; margin: 30px auto 30px auto; } .code { display: flex; align-items: center; flex-direction: column; .code-img { width: 300px; height: 300px; } } </style>Copy the code

One of the pitfalls here is that login returns base64 with data:image/ JPEG; The prefix base64 can be placed directly in the img SRC file, but the preview file that gets the preview code does not return the prefix! So I need to add it myself, which is base64Prefix: ‘data:image/ PNG; base64,’

The last

In fact, the whole coding function has been basically realized here, but if you really want to use there are a lot of things to do.

  1. Deploy to the test machine. You could use your own machine as the machine to deploy the tool, but that’s a bit… If deployed on a test machine, one problem is that the wechat developer tool relies on a graphical interface, while the server is generally a command line, although there is github.com/cytle/wech… Such a project ports wechat developer tools to Linux, but this deployment still seems odd.
  2. Assuming that the above deployment is completed, the process of packaging the applets project needs to be modified to pull the latest code from the corresponding code repository (such as Gitlab) according to the selected environment, and then install dependencies to execute the packaging command.
  3. Now that we have done this step, it is not bad to add the upload small program, wechat developer tool interface is also provided, so that the whole test code to the online steps have.

End~

Classic front-end technology books


1 comment