Click to jump to personal blog to view previous articles
preface
This article introduces some tips for daily project development, which can not only improve work efficiency, but also improve application performance. Here are some of the techniques I use on a daily basis.
Minxin makes component reuse flexible
Vue provides a method for inserting component properties into a component. I recommend using minxin as little as possible, but there is one scenario that strongly recommends using Minxin: When a piece of code is repeated in multiple components and the repeated block is large, using it as a minxin is often a great convenience for later maintenance.
For example, we have packaged a list function in the project, such as pull-down refresh, load automatic request data, pull-up load next page data, etc. The code is as follows:
export default {
data() {
return {
page: 1.limit: 10.busy: false.// Request interception to prevent multiple loads
finish: false.// Whether the request is completed is used to display the effect of the page
pageList: [].// Page data
reqParams: {}, // Page request parameters can be changed
defaultParams: {}, // Page request parameters, drop-down refresh will not be reset changes
routeName: ' '.// In special cases, the page needs to reuse someone else's list
autoReq: true.// whether the onload request itself
lodingText: ' '.// The copy shown at the bottom of the request
noDataText: 'No data at present'.// Customize no data copy
lastText: '- I draw the line -'.noData: false.// The page has no data
reqName: ' '}},created() {
this.autoReq && this.initPage(false.true)},onPullDownRefresh() {
this.pullDownRefreshFn()
},
onReachBottom() {
this.reachBottomFn()
},
methods: {
// Reset initialization data
initPage(saveParams = true, refresh = false) {
// Initialize all variables
this.page = 1
this.busy = false
this.finish = false
this.noData = false
this.lodingText = 'Data loading'
if (saveParams) {
const { page, limit } = this.reqParams
page ? this.page = page : ' '
limit ? this.limit = limit : ' '
} else {
this.reqParams = {}
}
this.getCommonList(refresh)
},
// Pull down the refresh function
pullDownRefreshFn() {
this.initData()
this.initPage(false.true)},// load the function
reachBottomFn() {
this.getCommonList()
},
// Reset data, easy to call (generally outside the custom empty some data)
initData() { // Reset variables in data, so that external references to the mixin can be pulled down to refresh the reset variables
},
// List get data interface
async getCommonList(refresh) {
if (!this.reqName) return
if (this.busy) return
this.busy = true
this.finish = false
const httpFn = this.$http || getApp().globalData.$http/ / compatible nvue
try {
constquery = { ... this.defaultParams, ... this.reqParams,page: this.page,
limit: this.limit
}
const { data } = await httpFn(this.reqName, query)
if (this.page === 1) this.pageList = []
/ * * * [using concat Node. JS and push the performance comparison of two or more array] (http://ourjs.com/detail/5cb3fe1c44b4031138b4a1e2) * that both in the Node. JS performance? We did a set of tests, one million tests each. * Push is about 3 times faster than concat methods. Because push is just modifying the original array, it's 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 refers to the */ method that vue overwrites
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) { // There is data
this.busy = false
this.page = Math.ceil(listLen / this.limit) + 1
}
this.resSuccess(data, refresh)
} catch (e) {
// Prevent the interface from locking incorrectly
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 completion to do something (so that the external imported file can reference itself)
finishInit(data) { // What do I do when the request completes
// console.log(' List request completed ');}}}Copy the code
Many of you might look at this scenario and wonder why not encapsulate it as a component? Because many lists have different styles, encapsulation as a component does not scale well. But we can simplify the code with Minxin:
<template>
<view class="c-recommend-goods">
<! -- List style -->
<view class="" v-for="item in pageList" :key="item.id">{{item}}</view>
<! -- Empty state && Load medium prompt -->
<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 to automatically request data
reqParams: {}, // Request parameters
reqName: 'userCompanyList' // Request an address}}}</script>
<style></style>
Copy the code
We can implement a nice list function by defining the request parameters, the address of the request, and the style of the list.
Save the messy template–render function
Sometimes there is a multi-value judgment in the template of a project, and if the business logic is written according to the code below, the code is redundant and messy.
<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>
Copy the code
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>Copy the code
Once and for all component registration
Before using a component, import it and then register it:
import BaseButton from './baseButton'
import BaseIcon from './baseIcon'
import BaseInput from './baseInput'
export default {
components: {
BaseButton,
BaseIcon,
BaseInput
}
}
Copy the code
BaseButton, BaseIcon, and BaseInput can now be used in templates:
<BaseInput
v-model="searchText"
@keydown.enter="search"
/>
<BaseButton @click="search">
<BaseIcon name="search"/>
</BaseButton>
Copy the code
But if there are many components, each time you have to import each component you want to use, and then register the components, it will add a lot of code! How do we optimize?
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 parameters: the folder directory to search, whether its subdirectories should also be searched, and a regular expression to match the files.
We’ll start by adding a file called global.js to the Components folder (which contains high-frequency components) and using require.context to dynamically package all high-frequency components we need. Then import the global.js file in the main.js file.
/ / global. Js file
import Vue from 'vue'
function changeStr (str) {
return str.charAt(0).toUpperCase() + str.slice(1)}const requireComponent = require.context('/'.false./\.vue$/)
// Find components that end in vue in the same directory
const install = () = > {
requireComponent.keys().forEach(fileName= > {
let config = requireComponent(fileName)
console.log(config) //./child1.vue then gets child1 with the re
let componentName = changeStr(
fileName.replace(/ ^ \ \ / /.' ').replace(/\.\w+$/.' ')
)
Vue.component(componentName, config.default || config)
})
}
export default {
install // Expose the install method externally
}
Copy the code
// main.js
import index from './components/global.js'
Vue.use(index)
Copy the code
As a result, we can use these high-frequency components anywhere and anytime on the page without having to manually import them one by one.
Hidden big trick –hook
Sometimes during development we will create a timer that will be destroyed before the component is destroyed. The code is as follows:
mounted() {
// Create a timer
this.timer = setInterval(() = > {
/ /...
}, 500);
},
// Destroy the timer.
beforeDestroy() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null; }}Copy the code
There is an obvious drawback to this: the timer is created and cleaned in different places, so it’s easy to forget to clean!
We can hook the code so that it is easier to maintain:
mounted() {
let timer = setInterval(() = > {
/ /...
}, 500);
this.$once("hook:beforeDestroy".function() {
if (timer) {
clearInterval(timer);
timer = null; }}); }Copy the code
$on(‘hook:updated’, () => {})
In addition to the above application, hook can also listen to the component’s life cycle function externally. 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 when rendering, you can do so with @hook:updated:
<template> <! Listen to the component's updated hook function --> <! All lifecycle hooks of a component can be listened for by @hook: hook function name.<custom-select @hook:updated="doSomething" />
</template>
<script>
import CustomSelect from ".. /components/custom-select";
export default {
components: {
CustomSelect
},
methods: {
doSomething() {
console.log("Custom -select component's updated hook function is triggered"); }}};</script>
Copy the code
Simple and violent router key
When we were developing a project, we might encounter a problem like this: when the page switches to the same route but with different parameter addresses, for example, /detail/1, and goes to /detail/2, the data is not updated after the page jumps? The route configuration is as follows:
{
path: "/detail/:id".name:"detail".component: Detail
}
Copy the code
This is because vue-Router recognizes that the two routes use the same component for reuse and does not recreate the component, and the component’s lifecycle hooks are not triggered, resulting in no data updates after the jump. So how do we solve this problem? We can add a property key to the router-view component as shown in the following example:
<router-view :key="$route.fullpath"></router-view>
Copy the code
This method mainly uses the virtual DOM to compare whether two nodes are the same by key during rendering. If the key is not the same, the router-View component will be judged as a new node, so the component will be destroyed first, and then a new component will be created, so that the life cycle within the component will be triggered again.
High precision permission control – custom directive
We usually add v-if/V-show to an element to determine whether the user has permission or not, but if the criteria are verbose and multiple places need to be determined, this approach is not only inelegant but redundant code. In this case, we can encapsulate an instruction authority, which can realize the button level authority judgment easily and quickly. Let’s start by creating a new array.js file to store permiss-related global functions
// array.js
export function checkArray (key) {
let arr = ['admin'.'editor']
let index = arr.indexOf(key)
if (index > -1) {
return true / / have permission
} else {
return false / / without permission}}Copy the code
Then mount the array file globally
// main.js
import { checkArray } from "./common/array";
Vue.config.productionTip = false;
Vue.directive("permission", {
inserted (el, binding) {
let permission = binding.value; // Obtain the value of v-permission
if (permission) {
let hasPermission = checkArray(permission);
if(! hasPermission) {// Do not have permission to remove Dom elementsel.parentNode && el.parentNode.removeChild(el); }}}});Copy the code
Finally, we can judge the page through the custom command V-permission:
<div class="btns">
<button v-permission="'admin'">Permission button 1</button> / / will be displayed
<button v-permission="'visitor'">Permission button 2</button> / / no show
<button v-permission="'editor'">Permission button 3</button> / / will be displayed
</div>
Copy the code
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 a JavaScript expression enclosed in square brackets as an argument to an instruction:
<a v-bind:[attributeName]="url"> This is a link </a>Copy the code
The attributeName is evaluated dynamically as a JavaScript expression, and the resulting value is used as the final parameter. Similarly, you can bind handlers to a dynamic event name with dynamic parameters:
<a v-on:[eventName]="doSomething"> This is a link </a>Copy the code
Let’s take a look at an example: Suppose you have a button that in some cases wants to listen for click events and in some cases for double-click events. This is where the dynamic command argument 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>
Copy the code
Filters facilitate data processing
Vue. Js allows you to customize filters. It is very simple to use, but maybe some friends have not used it.
1. Understand filters
- Function: To display the data after a specific format.
- Note: The filter does not change the original data, so you need to wrap the presented data.
- Usage scenarios: double curly brace interpolation and v-bind expressions (the latter supported as of 2.1.0+).
2. Define a filter
Local filters can be defined in a component’s options:
filters: { capitalize: function (value) { if (! value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } }Copy the code
You can also define filters globally before creating Vue instances:
Vue.filter('capitalize', function (value) { if (! value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) })Copy the code
3. Use a filter
Use method is simple, that is used in the double brace pipe (pipeline) |
<! -- In double braces --><div>{{ myData| filterName}}</div>
<div>{{ myData| filterName(arg)}}</div>
<!-- 在 v-bind 中 -->
<div v-bind:id="rawId | formatId"></div>
Copy the code
Filters can be connected in series:
{{ message | filterA | filterB }}
Copy the code
In this example, filterA is defined as a filter function that receives a single parameter, and the value of the expression Message is passed into the function as a parameter. It then proceeds to call filterB, which is also defined to receive a single argument, passing the result of filterA to filterB. Let’s look at an example of how to format dates using filters:
<div>
<h2>Displays the formatted date and 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 filter does not point to this
returnmoment(value).format(format); }},data() {
return {
date: new Date()
};
}
Copy the code
Refer to articles and books
- Vue official document
- Vue2.6 check and fill gaps
- Vue.js Best Practices (5 Ways to Become a Vue.js master)
- Actual combat 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