preface

First of all, welcome everyone to pay attention to my nuggets, which can also be regarded as a little encouragement to me. After all, writing things can not be realized, and it is my enthusiasm and everyone’s encouragement that can persist.

This article I in the use of MPVue framework responsible for the development of small procedures all the way guess pit and experience, I hope to help you…

Hope to help you, at the same time, we hope to support the source address!

MPVue

  • MPVue is a front-end framework that uses vue. js to develop small programs, so I am not familiar with Vue. Please refer to the official website of Vue

  • If you are not familiar with MPVue, please refer to MPVue official website

  • Code style, the project reference, Vue official style guide, I developed the specification and style is in accordance with the above, the team if there is no specification, strongly recommend the use of Vue official style guide, in this have to make fun of the current team specification chaos a mass slot, a style, ah, said more are tears…

The directory structure

├─ Build ├─ Config ├─ Server ├─ SRC │ ├─ API │ ├─ ─ Mixins │ ├─ Pages │ ├─ Plugin │ ├─ Store │ ├─styles │ ├─ utils └ ─ the staticCopy the code

The meaning of the specific folder development of Vue project, a look will know why, not one by one!

The hole I stepped in

1: Globally registered component pit:

  • In order to userequire.contextUnified Global registry Management, which I used when building projectsrequire.context“Why are the flowers so red?”
Module build failed: Error: unknown: Vue.com ponent () the first parameter must be a static string at PluginPass. CallExpression (G: \ mpvue gworld - miniprogram \ node_modules \ _mpvue - [email protected] @ mpvue - loader \ lib \ mp - compiler \ parse js: 117:15)Copy the code

At that time, I thought, what’s wrong with this? Is it require. Context, one of the things I tried later, in a different form

  • Synchronously reference the import module one by one, in the entry functionsrc/main.jsImport and mount the registry as follows:
import BasePage from '@/components/global/base-page.vue'
import BaseHeader from '@/components/global/base-header.vue'

const components = {
    BasePage,
    BaseHeader
}

Object.keys(components).forEach(key= > {
    Vue.component(key, components[key])
})

Copy the code

Vue.component() must be a static string as the first argument

import BasePage from '@/components/global/base-page.vue'
import BaseHeader from '@/components/global/base-header.vue'


Vue.component('BasePage', BasePage)
Vue.component('BaseHeader', BaseHeader)

Copy the code

Success is a success, just want to say that the MPVue framework is a bit pit, can not unified registration, also can not function form registration, ah…

  • There is no support for using Class and Style bindings on components,…… In theMPVueIn the project, the following is invalid, if you need to use prop definition passed in, examplebase-header.vuecomponent
<template>
    <base-page>
        <template slot="header">
            <base-header class="custom-header"></base-header>
        </template>
    </base-page>
</template>
Copy the code

2: Slot pit:

  • The slotnameThe use of the Vue project we can putslotCan the following
<template>
    <base-page>
        <base-header slot="header"></base-header>
        <template slot="main">
            <scroll-vie  :scroll-y="true"></scroll-vie>
        </template>
        <div slot="footer"></div>
    </base-page>
</template>
Copy the code

Vue projects can be written on normal tags, components, or standard template tags, which are free and fun to use, but MPVue projects can only be written on tags or template tags

<template>
    <base-page>
        <base-header slot="header"></base-header>
    </base-page>
</template>
Copy the code

The above is incorrect, using slot mode

On MPVue, the base-header component is not actually rendered successfully. It is rendered as a label on the console.

<template>
    <base-page>
        <template slot="header">
            <base-header></base-header>
        </template>
        <template slot="main">
             <scroll-view></scroll-view>
        </template>
        <template slot="footer">
        </template>
    </base-page>
</template>
Copy the code
  • Custom Slot (scoped is not supported yet) :
<template>
    <base-page>
        <template slot="header">
            <base-header>
                 <template slot="right">
                     <div v-if="showRight">true</div>
                     <div v-else>false</div>
                 </template>
            </base-header>
        </template>
        <template slot="main">
             <scroll-view></scroll-view>
        </template>
        <template slot="footer">
        </template>
    </base-page>
</template>
<script>
    export default {
        data() {
            return {
                showRight: false
            };
        },
        mounted(){
            this.showRight = true}};</script>
Copy the code

And then you can try it out in your project, just like in base-header and define the right slot, and when showRigth = true, it will also display false characters, which is hard to understand,

Just the slot of two layers of components is complicated, it seems that the document is very clear, if there are other methods, I hope to share and tell me

Because of the limitations of the MPVue framework, the implementation of events or UI display is defined using props. For details, please refer to the source code I shared

We can’t use template string syntax in the template template.

<template>
    <base-page>
        <template slot="header">
            <base-header :title="' Number (${total}) '"></base-header>
        </template>
        <template slot="main">
             <scroll-view></scroll-view>
        </template>
    </base-page>
</template>
<script>
    export default {
        data() {
            return {
                total: 0
            };
        },
        onLoad(options){
            this.total = options.total
        }
    };
</script>
Copy the code

If the wechat development tool is yellow, you can take a look to see if you are using template string syntax on the template template

Use normal string concatenation or computed properties instead

We cannot use the object method syntax in the template template:

<template>
    <base-page>
        <template slot="header">
            <base-header :title="parseInt(money)"></base-header>
        </template>
        <template slot="main">
             <scroll-view>
                <div v-if="[1, 2]. Includes (state)">Object methods suggest using computed properties or a new data property definition, depending on preference</div>
                <div v-if="a && props.a && b">It is recommended to use computed attributes instead of too many conditions</div>
             </scroll-view>
        </template>
    </base-page>
</template>
<script>
    export default {
        data() {
            return {
                total: 0
            };
        },
        onLoad(options){
            this.total = options.total
        }
    };
</script>
Copy the code

The MPVue documentation also states that some complex JavaScript rendering expressions are not supported. Seriously, filters are not supported.

5: open the page again, there is still the last page data, if you want to clean everyone, in the corresponding time to restore the data, such as

<script>
    export default {
        onLoad(options) {
            Object.assign(this.$data, this.$options.data()); }};</script>
Copy the code

$destory() : $destory() : $destory() : $destory() : $destory() : $destory();

<script>
    export default {
        onLoad(options) {
            Object.assign(this.$data, this.$options.data());
            this.$bus.on('team-member-add', payload => {

            });
        },
        onUnload() {
            this.$bus.off('team-member-add'); }};</script>
Copy the code

If this,$destory() is executed actively, the component rendering will not be triggered again if an attempted component introduces a component, as in:

<template>
    <base-page>
        <template slot="header">
            <base-header :title="args.title" :show-right="false"></base-header>
        </template>
        <template slot="main">
            <scroll-view class="scrollbox scroll-view" :scroll-y="true"></scroll-view>
        </template>
        <template slot="export">
            <KeyBoard />
        </template>
    </base-page>
</template>

<script>
    import KeyBoard from '@/pages/components/keyboard';

    export default {
        components: {
            KeyBoard
        },
        data() {
            return {
                args: {}}; }, onLoad(options) {Object.assign(this.$data, this.$options.data());
            this.args = Object.assign(this.args, options);
        },
        onUnload(){
            this.$destroy()
        }
    };
</script>
Copy the code

After destruction, the component KeyBoard will no longer trigger Mounted, so if it is a small project, do not consider destroying the page.

This is because I have a project page data is very complex, open the page more serious jam need

7: Re-page routing data update problem:

  • usevue-bus, redirect on-demand repull interfaces, or alter data
  • onShowHooks (I’ve never used them)
  • Update hooks with drop-down pages on required pagesonPullDownRefresh

Sadly, the [email protected] version had the onRoute hook, which is not as blind as onShow. You might say that onShow uses a flag (or uses storage collaboration) to control the blind update trigger

If using VUE-bus, remember to close the page before onUnload to avoid double registration and triggering problems

Always turn it off

8: custom small program head navigation bar, neat bangs, if your project does not use custom head navigation bar can ignore this part:

I used the module file of Vuex global to manage the iphoneX field and mark it. At first, I used the array of iphoneX to judge and added multiple models

According to the rule, I judge that as long as the positioning of the capsule button top is greater than a certain value, there will be a fringe, see @/store/global/index.js

I am not sure whether it is basically correct. If there is a better way, please let me know

9: the use of the fonts icon, small program cannot use the local reference font (with style cannot use local picture a truth), but can be base64 instead of modifying webpack configuration: / build/webpack. Base. Conf., js

{
  test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/, loader:'url-loader', options: {// Set a size that exceeds your font file sizelimit: 512000,
  name: utils.assetsPath('fonts/[name].[ext]')}}Copy the code

Other than base64 fonts, you can also use HTTPS fonts online

10: Change the default route name to pages/**/main, change the default route name to pages/**/your-hope-route-name, change the webpack configuration:

In the/build/webpack. Base. Conf. Js

Glob module option configuration, ‘pages//main.js’ all changed to ‘pages//*.js’

11: Unified registration import vuEX module:

This trick, which I learned two years ago when I was reading blogs, and which I have since applied to projects, is a really great trick, which also uses the core code of require.context as follows:

const modulesFiles = require.context('@/store'.true, /\.js$/)
const filters = ['index'.'modules']

function modifyKeyName(moduleName) {
    moduleName = moduleName.replace('/index'.' ')
    let words = []
    let word = ' '
    if (moduleName.includes(The '-')) {
        words = moduleName.split(The '-')}else {
        words.push(moduleName)
    }
    words.map(item= > {
        word += item.slice(0.1).toUpperCase() + item.slice(1)})return word
}

const modules = modulesFiles.keys().reduce((modules, modulePath) = > {
    let moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/.'$1')
    if(! filters.includes(moduleName)) {const value = modulesFiles(modulePath)
        moduleName = modifyKeyName(moduleName)
        modules[moduleName] = value.default
    }

    return modules
}, {})


export default modules
Copy the code

See @/store/modules.js for details

I personally prefer to manage a file module of VUEX. If you really use vuex module, please refer to the vuex document.

I don’t use vuex to name my project namespaced: true. I only use vuex’s Module module, so try using vuex’s Module for namespaced in your actual project!

The details of vuex and writing specifications won’t go into detail, but if you know and use Redux, that’s great.

12: About the request encapsulation, in the VUE project, everyone basically uses AXIos as the request interface plug-in, it is really super cool to use, especially Promise and its interceptor, MY encapsulation:



import Axios from 'axios'
import { BASE_HOST_URL } from '@/utils'
import { API_LOGIN } from '@/api'


let baseURL = BASE_HOST_URL


const tipMsg = {
    common: 'Network error'.timeout: 'Request timed out'.fail: 'Service not responding'.400: 'Error request'.401: 'Missing arguments'.404: 'Interface URL error'.403: 'Server rejected request'.405: 'Requested in wrong way'.500: 'Server internal error'.502: 'Wrong gateway'.503: 'Service unavailable'.504: 'Gateway timed out'.domain: 'Domain name not whitelisted'.ssl: 'SSL certificate verification error '.associated: 'Unassociated hostname'
}


const codes = Object.keys(tipMsg)

const defaultCtrl = {
    isMsg: true
}

/*异常 */
function exception({ res, ctrl }) {
    const errMsg = res.errMsg
    let type = 'common'
    if (errMsg.includes('fail')) {
        if (errMsg.includes('domain')) type = 'domain'
        if (errMsg.includes('ssl hand')) type = 'ssl'
        if (errMsg.includes('associated')) type = 'associated'
        if (errMsg == 'request:fail ' || errMsg.includes('unexpected end')) type = 'fail'
        if (errMsg.includes('timeout') || errMsg.includes('network')) type = 'timeout'
    } else {
        if (codes.includes(res.statusCode)) type = res.statusCode
    }
    if (ctrl.isMsg) {
        return Promise.reject(tipMsg[type])
    } else {
        return Promise.reject({
            code: res.statusCode,
            message: tipMsg[type]
        })
    }
}


/ * * / success
function success({ res, ctrl }) {
    return new Promise((resolve, reject) = > {
        const code = res.data.code
        if (code === 0) {
            resolve(res.data.data)
        } else {
            // Capture the body of information
            if (ctrl.isMsg) {
                reject(res.data.message)
            } else {
                reject(res.data)
            }
        }
    })
}


export default {
    install(Vue) {
        Axios.defaults.timeout = 6000
        Axios.defaults.baseURL = baseURL
        Axios.defaults.headers.post['Content-Type'] = 'application/json'

        Axios.defaults.adapter = function (config) {
            if(! config.ctrl) config.ctrl = {}if(! config.data) config.data =JSON.stringify({})
            config.ctrl = Object.assign({}, defaultCtrl, config.ctrl, { path: config.url })

            let header = {
                'Auth-Token': mpvue.getStorageSync('token'),
                'Auth-Role': 'miniprograme'
            }

            if(! header.token)delete header.token
            header = Object.assign(header, config.header)

            return new Promise((resolve, reject) = > {
                let data = config.method === 'get' ? config.params : config.data
                mpvue.request({
                    url: config.url,
                    method: config.method,
                    data: data,
                    header: header,
                    complete: (res) = > {
                        if (res.statusCode === 200) {
                            success({ res, ctrl: { ...config.ctrl } }).then(res= > {
                                resolve(res)
                                / / cache the token
                                if (config.url.includes(API_LOGIN)) {
                                    mpvue.setStorage({ key: 'token'.data: res.token })
                                }
                            }).catch(error= > {
                                reject(error)
                            })
                        } else {
                            exception({ res, ctrl: { ...config.ctrl } }).catch(error= > {
                                reject(error)
                            })
                        }
                    }
                })
            })
        }

        Vue.prototype.$http = Axios
    }
}
Copy the code

The wrapper file is in @/plugin/axios.js.

The key is to rewrite the axios adapter, replace it with the wx.Request applets, and then tweak it to fit your actual project. The wrapper captures a single string of information, or an object with an error code, depending on your custom options

The default encapsulation {isMsg: true} returns only an error message,

Because there are some specific special codes, you can use the pass in the request to determine if:

  • Normal use, only return error message string,
this.$http.post(GETUSERINFO, { id : id }).then((data) = > {

}).catch((error) = > {
     this.$toast(error)
})
Copy the code
  • Normal use, returns only error message objects,
this.$http.post(GETUSERINFO, { id : id }, { ctrl: { isMsg: false } }).then((data) = > {

}).catch((error) = > {
    if(error.code === 119){

    }else{
        this.$toast(error.message)
    }
})
Copy the code

The above error information, based on their own development so long summary of common errors, should also be enough,

As for axISO repackaging, you can modify it according to your actual project

13: The definition of project plug-in, common methods and secondary encapsulation of WX API, I put in the @/plugin/index.js file

  • The parameter is transmitted to the native route

Before the encapsulation:

 wx.navigateTo({
      url: `/pages/user/exit? role=abc&age=18&info=The ${JSON.stringify(this.info)}`
})
Copy the code

After the packaging:

 this.$navigate({
      url: `/pages/user/exit`.query: {
          role: 'abc'.age: 18.info: this.info
      }
})
Copy the code

It’s easier to write when wrapped, and you don’t have to pass json.stringify every time you pass an object

Custom plug-ins that add encapsulation according to your actual project

14. Small program subcontracting:

Please refer to subcontracting,

If the size of your project exceeds 2M, you must subcontract it, and the subcontract can be up to 8M

No more than, can also be subcontracted, such as the project has been subcontracted, it is best to wait for the stability of the subcontract, the later stage of the development of the subcontract do not first, sometimes when debugging needs to frequently switch routes is very inconvenient

Dist /wx/app.json 'file. You can write a small tool to modify the dist/wx/app.json' file directly after packaging.Copy the code

In terms of subcontracting preloading, I also used it in another project that you can try…

15: code specification: this is basically I am responsible for, so most of the code style seems to be more unified, a company specifications are different, how to measure the choice of that specification, really a hundred schools of thought contend ah…

Always configure ESLint, no matter what editor you use and how you configure your ESLint rules, but before you commit, fix as much as you can to avoid a lot of conflicts

conclusion

I feel that I haven’t written a blog for a long time. I feel that it is very difficult to write and I can’t organize the description. Don’t tolerate tolerance ah…

In the future, I will continue to share more knowledge about front-end development. Look forward to it!

complaints

Since this work is really busy, before all overtime, far from 996 so simple, the plan is behind, ah…

Frequent and long hours lead to plans falling through…