Click to jump to your blog to see previous posts

preface

This article mainly introduces some techniques in the daily project development process, which can not only help improve the work efficiency, but also improve the performance of the application. The following is my summary of some daily work experience.

MINXIN lets components reuse clever activation

Vue provides minxin as a way to insert component attributes into a component. I recommend using minxin as little as possible, but there is one scenario where minxin is highly recommended: When a piece of code appears repeatedly in multiple components, and the repeated code block is very large, as a minxin often can bring great convenience to the later maintenance.

This is a feature that encapsulates a list in the project, with drop-down refresh, load auto-request data, pull up to load next page data, and so on, it looks like this

It doesn’t matter if I don’t understand it. I just gave an example in development

Export default {data() {return {page: 1, limit: 10, busy: false, False, // whether the request is complete, for the page display effect pageList: [], // page data reqParams: {}, // page request parameters, can be changed by defaultParams: {}, // page request parameters, drop-down refresh will not be reset by the change routeName: ", // In special cases, pages need to reuse someone else's list when autoReq: true, // onload whether to request the lodingText itself: ", // Request bottom text noDataText: 'currently noData ', // custom noData text lastText: '- I have a bottom line -', noData: false, // page noData reqName: '', } }, created() { this.autoReq && this.initPage(false, true) }, onPullDownRefresh() { this.pullDownRefreshFn() }, onReachBottom() { this.reachBottomFn() }, methods: InitPage (saveParams = true, initPage(saveParams = true), Refresh = false) {this.page = 1 this.busy = false this.finish = false this.nodata = false this.lodingText = If (saveParams) {const {page, limit} = this.reqParams page? (this.page = page) : '' limit ? (this.limit = limit) : '' } else { this.reqParams = {} } this.getCommonList(refresh) }, // PullDownRefreshFn () {this.initData() this.initPage(false, true)}, // ReachBottomFn () {this.getCommonList()}; // Reset the data () {this.getCommonList()}; // Reset the data () {this.getCommonList()}; } // Async getCommonList(Refresh) {if (! this.reqName) return if (this.busy) return this.busy = true this.finish = false const httpFn = this.$http || GetApp ().globalData.$HTTP // nvue try {const query = {... this.defaultParams, ... this.reqParams, page: this.page, limit: this.limit, } const { data } = await httpFn(this.reqName, query) if (this.page === 1) this.pageList = [] /** * [using the concat Node. JS and push the connection of two or more array performance comparison] (http://ourjs.com/detail/5cb3fe1c44b4031138b4a1e2) * that both in the Node. JS performance? We did a set of tests, one million tests of each. * Push is about 3 times faster than concat. Because push is just a modification of the original array, it is faster. * push returns the length of the array, So there is no to redefine the variables to determine the * [Array. Prototype. Push. Apply (arr1, Arr2) does not trigger the DOM updates automatically] (https://www.imooc.com/wenda/detail/494323) * because this. PageList. Push! == Array.prototype.push, this.pagelist.push */ this.finish = true const resLen = data.list? data.list.length : 0 if (resLen === 0) { this.resSuccess(data, refresh) return } const listLen = this.pageList.push.apply(this.pageList, Data. list) if (listLen < data.count && this.limit <= resLen) {this.busy = false this.page = Math.ceil(listLen) / this.limit) + 1} this.resSuccess(data, refresh)} catch (e) {this.busy = false this.finish = true}, resSuccess(data, refresh) { if (this.finish && this.busy) { if (this.pageList.length > 0) { this.$nextTick(() => { setTimeout(() => { this.lodingText = this.lastText }, 100) }) } else { this.lodingText = this.noDataText this.noData = true } } refresh && uni.stopPullDownRefresh() This. finishInit(data)}, // Request completed to do something (to make it easier for external imported files to reference themselves) finishInit(data) {// Request completed to do something // console.log(' List request completed '); }},}

Many people might wonder why they don’t wrap it as a component, but because many lists have different styles, wrapping it as a component is not very scalable. Now we can use it like this.

<template> <view class="c-recommend-goods"> <! - a list of style - > < the view class = "v -" for = "item in pageList" : the key = "item. Id" > {{item}} < / view > <! >< c-no-data v-if="lodingText" :show-img="noData" :text="lodingText"></c-no-data> </view> </template>  <script> import listMixins from '@/common/mixins/list.js' export default { mixins: [listMixins], data() {return {autoReq: false, // enter the page automatically request data reqParams: {}, // request parameter reqName: 'userCompanyList'}}} </script> <style></style>

We can implement a nice list function by defining request parameters and request addresses, as well as the style of the list.

Save the messy template–render function

  • There is a multi-value judgment in the template
  • Redundant code, code clutter

Take an example of an official document

<template>
  <div>
    <h1 v-if="level === 1">
      <slot></slot>
    </h1>
    <h2 v-else-if="level === 2">
      <slot></slot>
    </h2>
    <h3 v-else-if="level === 3">
      <slot></slot>
    </h3>
    <h4 v-else-if="level === 4">
      <slot></slot>
    </h4>
    <h5 v-else-if="level === 5">
      <slot></slot>
    </h5>
    <h6 v-else-if="level === 6">
      <slot></slot>
    </h6>
  </div>
</template>

<script>
export default {
  data() {
    return {}
  },
  props: {
    level: {
      type: Number,
      required: true,
    },
  },
}
</script>

Now rewrite the above example using the render function:

<script> export default { props: { level: { require: true, type: Number, } }, render(createElement) { return createElement('h' + this.level, this.$slots.default); }}; </script>

Once and for all component registration

Before the component can be used, it needs to be introduced and registered:

import BaseButton from './baseButton'
import BaseIcon from './baseIcon'
import BaseInput from './baseInput'

export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}

BaseButton, BaseIcon, and BaseInput can now be used in templates:

<BaseInput
  v-model="searchText"
  @keydown.enter="search"
/>
<BaseButton @click="search">
  <BaseIcon name="search"/>
</BaseButton>

But if you have more components, and you have to import each component you want to use each time, and then register the component, you can add a lot of code! How do we optimize it?

At this point, we need to use Webpack’s require.context() method to create our own (module) context to implement the automatic dynamic require component. This method takes three arguments: the folder directory to be searched, whether it should also be searched for subdirectories, and a regular expression to match the files.

We’ll start by adding a file called global.js to the Components folder (which is full of high-frequency components) and dynamically packing all the high-frequency components into this file using require.context. Then introduce the global.js file in the main.js file.

// global.js import Vue from 'Vue' function changeStr (STR) {return str.charat (0).toupperCase () + str.slice(1)} const requireComponent = require.context('./', false, /\.vue$/) // Configure const install = () => {RequireComponent.keys (). ForEach (fileName => {let config = Child1 let componentName = changeStr().componentName = changeStr().componentName = changeStr().componentName = changeStr().componentName = changeStr().componentName = changeStr fileName.replace(/^\.\//, '').replace(/\.\w+$/, '') ) Vue.component(componentName, Config. The default | | config)})} export default {install / / exposed outside the install method}
// main.js
import index from './components/global.js'
Vue.use(index)

As a result, you can use these high-frequency components in your page anytime, anywhere, without having to manually introduce them one by one.

Hidden big trick –hook

At some point in the development process we will create a timer that will be destroyed before the component is destroyed. The code is as follows:

Mounted () {/ / this create a timer. The timer = setInterval () = > {/ /... }, 500); }, // destroy the timer. beforeDestroy() { if (this.timer) { clearInterval(this.timer); this.timer = null; }}

There is an obvious drawback to this: Timers are not created and cleaned in one place, so it is easy to forget to clean.

We can integrate the code with the help of hooks so that the code is easier to maintain:

mounted() { let timer = setInterval(() => { // ...... }, 500); this.$once("hook:beforeDestroy", function() { if (timer) { clearInterval(timer); timer = null; }}); }

$on(‘hook:updated’, () => {}); $on(‘hook:updated’, () => {});

In addition to the above usage, hooks can also listen externally to the lifecycle functions of a component. In some cases, we need to know in the parent when a child component is created, mounted, or updated.

For example, if you want to listen for a third-party component CustomSelect’s updated hook while it is rendering, you can do so with @Hook :updated:

<template> <! Listenings on the component's updated life hook function via @Hook :updated --> <! > <custom-select @hook:updated="doSomething" /> </template> <script> import CustomSelect from ".. /components/custom-select"; export default { components: { CustomSelect }, methods: {doSomething() {console.log(" The custom-select component's updated hook function is fired "); }}}; </script>

Router key for simple violence

When we are working on a project, we may encounter this problem: when the page switches to the same route but with a different parameter address, such as /detail/1, and goes to /detail/2, the page does not update the data after the jump? Routing configuration is as follows:

 {
     path: "/detail/:id",
     name:"detail",
     component: Detail
 }

This is because the Vue-Router will recognize that the two routes are using the same component and reuse it. The component will not be recreated, and the component’s lifecycle hooks will not be triggered so that the data is not updated after the jump. So how do we solve this problem? We can add a property key to the Router – View component, for example:

<router-view :key="$route.fullpath"></router-view>

This approach mainly uses virtual DOM to compare two nodes by key during rendering. If the key is different, it determines that the Router – View component is a new node, destroys the component first, and then recreates the new component, so that the life cycle within the component will be triggered again.

High precision permission control — custom directive

It’s common to add v-if/v-show to an element to determine whether the user has permissions, but if the criteria are cumbersome and multiple places need to be evaluated, the code is not only inelegant but also redundant. In view of this situation, we can encapsulate an instruction permission, can simply and quickly achieve the button level permission judgment. Let’s start by creating a new Array.js file to hold the global functions associated with permissions

// array.js export function checkArray(key) { let arr = ['admin', 'editor'] let index = arr.indexOf(key) if (index BB0-1) {return true} else {return false}}

Then mount the Array file into the global

// main.js import { checkArray } from './common/array' Vue.config.productionTip = false Vue.directive('permission', { inserted(el, Binding) {let Permission = binding.value // Gets the value of the v-Permission if (Permission) {let HasPermission = checkArray(permission) if (! HasPermission) {/ / no permissions remove Dom elements el parentNode && el. ParentNode. RemoveChild (el)}}},})

Finally, we can tell from the custom directive v-permission on the page:

<div class=" BTNS "> <button v-permission="'admin'"> permission button 1</button> // displays <button v-permission="'visitor'"> permission button 2</button> </div> > </div> > </div> > </div>

Dynamic instruction parameter

One of the coolest features of Vue 2.6 is the ability to dynamically pass instruction parameters to components. We can use JavaScript expressions enclosed in square brackets as arguments to a directive:

<a v-bind:[attributeName]=" URL "> This is a link </a>

The AttributeName here is evaluated dynamically as a JavaScript expression, and the resulting value is used as the final parameter. Similarly, you can bind a handler to a dynamic event name using dynamic parameters:

<a v-on:[eventName]="doSomething"> This is a link </a>

Let’s look at an example: Suppose you have a button, and in some cases you want to listen for click events, and in some cases you want to listen for double click events. This is where the dynamic instruction parameter comes in handy:

<template>
  <div>
    <aButton @[someEvent]="handleSomeEvent()" />
  </div>
</template>
<script>
export default {
  data () {
    return {
      someEvent: someCondition ? "click" : "dbclick"
    }
  },
  methods: {
    handleSomeEvent () {
      // handle some event
    }
  }
}
</script>

Filters make data processing easier

Vue.js allows you to customize your own filters. It’s quite simple to use, but some of you may not have used it. Here’s how:

1. Understand filters

  • Function: to display the data after a specific format display.
  • Note: Filters do not change the original data and need to wrap the presented data.
  • Usage scenarios: double curly braces interpolation and V-bind expressions (the latter supported starting with 2.1.0+).

2. Define filters

Local filters can be defined in the options of a component:

filters: { capitalize: function (value) { if (! value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } }

Filters can also be defined globally before the Vue instance is created:

Vue.filter('capitalize', function (value) { if (! value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) })

3. Use a filter

Use method is simple, that is used in the double brace pipe (pipeline) |

<! - in a pair of curly braces - > < div > {{myData | filterName}} < / div > < div > {{myData | filterName (arg)}} < / div > <! - in the v - bind - > < div v - bind: id = "rawId | formatId" > < / div >

Filters can be in series:

{{ message | filterA | filterB }}

In this example, Filtera is defined as a filter function that takes a single argument, and the value of the expression message is passed into the function as the argument. It then proceeds to call FilterB, the filter function also defined to take a single argument, passing the result of Filtera to FilterB. Let’s look at an example of how to format a date using a filter:

The < div > < h2 > show the formatted date time < / h2 > < p > {{date}} < / p > < p > {{date | filterDate}} < / p > < p > (date) (month) (year) : {{ date | filterDate("YYYY-MM-DD") }}</p> </div> ...... filters: { filterDate(value, Format = "yyyy-mm-dd HH: MM :ss") {console.log(this)//undefined } }, data() { return { date: new Date() }; }

Refer to articles and books

  • VUE official documentation
  • Vue2.6 leaks fill gaps
  • Vue.js Best Practices (5 Tips to Become a Vue.js Master)
  • Practical skills, Vue can also be written like this
  • Use Vue.Observable () for state management
  • Introduction to VUE project construction and development
  • 12 Vue.js development tips and tricks for 2020