As one of the three front-end frameworks, vue is a necessary skill for front-end developers.
Even the famous uni app uses Vue JS as the basic framework development.
Uni app is an application that uses Vue JS develops all front-end application frameworks, and developers write a set of code that can be distributed to multiple platforms such as iOS, Android, H5, and various small programs (WeChat / Alipay / Baidu / headband /QQ/ pin / Taobao), fast application and so on.
Here are some tips for using vue.
Elegant update props
Updating prop is a common requirement in business, but it is not allowed to directly modify prop in sub components, because this method does not comply with the principle of one-way data flow, and a warning will be reported in the development mode. Therefore, most people will trigger a custom event through $emit and receive the value of the event in the parent component to update the prop.
child.vue: export defalut { props: { title: String }, methods: { changeTitle(){ this.$emit('change-title', 'hello') } } } parent.vue: <child :title="title" @change-title="changeTitle"></child> export default { data(){ return { title: 'title' } }, methods: { changeTitle(title){ this.title = title } } }
There is no problem with this method, and I often use this method to update prop. But if you just want to update prop, there is no other operation. So the sync modifier can make all this very simple.
parent.vue: <child :title.sync="title"></child> child.vue: export defalut { props: { title: String }, methods: { changeTitle(){ this.$emit('update:title', 'hello') } } }
You only need to add on the binding attribute sync, the update: attribute name can be triggered inside the sub component to update prop. You can see that this method is indeed concise and elegant, which reduces an "unnecessary function" in the code of the parent component.
Reference documents
provide/inject
This pair of options needs to be used together to allow an ancestor component to inject a dependency into all its descendants, no matter how deep the component level is, and it will always take effect when its upstream and downstream relationships are established.
Simply put, a component exposes its own attributes through provide, and its descendant component inject can receive the exposed attributes.
App.vue: export default { provide() { return { app: this } } } child.vue: export default { inject: ['app'], created() { console.log(this.app) // App.vue instance } }
In version 2.5.0 +, you can make it optional by setting the default value:
export default { inject: { app: { default: () => ({}) } }, created() { console.log(this.app) } }
If you want to change the name of the attribute of inject, you can use from to indicate its source:
export default { inject: { myApp: { // The value of from is consistent with the attribute name of provide from: 'app', default: () => ({}) } }, created() { console.log(this.myApp) } }
It should be noted that provide and inject are mainly used when developing high-level plug-in / component libraries. Not recommended for normal application code. But sometimes, maybe it can help us.
Reference documents
Small state manager
The data status in large projects will be complex, and vuex is generally used to manage it. However, in some small projects or projects with simple status, it seems cumbersome to introduce a library in order to manage several states.
In version 2.6.0 +, the new Vue Observable can help us solve this embarrassing problem. It can turn an object into responsive data:
// store.js import Vue from 'vue' export const state = Vue.observable({ count: 0 }) use: <div @click="setCount">{{ count }}</div> import {state} from '../store.js' export default { computed: { count() { return state.count } }, methods: { setCount() { state.count++ } } }
Of course, you can also customize the mutation to reuse the method of changing the state:
import Vue from 'vue' export const state = Vue.observable({ count: 0 }) export const mutations = { SET_COUNT(payload) { if (payload > 0) { state.count = payload } } }
use:
import {state, mutations} from '../store.js' export default { computed: { count() { return state.count } }, methods: { setCount() { mutations.SET_COUNT(100) } } }
Reference documents
Unload watch observation
Data observation is usually defined and configured in watch by using options:
export default { data() { return { count: 1 } }, watch: { count(newVal) { console.log('count New value:'+newVal) } } }
In addition, data observation has another way of functional definition:
export default { data() { return { count: 1 } }, created() { this.$watch('count', function(){ console.log('count New value:'+newVal) }) } }
It has the same function as the former, but this method makes it more flexible to define data observation, and $watch will return a function to cancel observation to stop triggering callback:
let unwatchFn = this.$watch('count', function(){ console.log('count New value:'+newVal) }) this.count = 2 // log: count new value: 2 unwatchFn() this.count = 3 // Nothing happened $watch The third parameter receives a configuration option: this.$watch('count', function(){ console.log('count New value:'+newVal) }, { immediate: true // Execute watch now })
Reference documents
Skillfully using template
I believe that v-if is the most frequently used instruction in development, so you must have encountered such a scenario. Multiple elements need to be switched, and the switching conditions are the same. Generally, one element is wrapped and switched on this element.
<div v-if="status==='ok'"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </div>
If a div like the one above exists only for switching conditions and causes the element level to be nested one more layer, it has no "meaning of existence".
We all know that when declaring a page template, all elements need to be placed in the element. In addition, it can also be used in the template. The element is used as an invisible package element, which is only processed at runtime, and the final rendering result does not include it.
<template> <div> <template v-if="status==='ok'"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template> </div> </template>
Similarly, we can also use the v-for instruction on. This method can also solve the problem of warning when v-for and v-if are used at the same time.
Filter reuse
Filters are used for some common text formatting and are added at the end of the expression, indicated by the "pipe" symbol.
export default { data() { return { text: 'hello' } }, filters: { capitalize: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } } }
Imagine a scenario where this function is used not only in the template, but also in the method. But the filter cannot be referenced directly through this. Do you want to define the same function in methods?
You know, the option configuration will be stored in the instance o p t i o n s in , place with only need want Obtain take t h i s . options, so you only need to get this Options, so you only need to get this options. Filters can get the filters in the instance.
export default { methods: { getDetail() { this.$api.getDetail({ id: this.id }).then(res => { let capitalize = this.$options.filters.capitalize this.title = capitalize(res.data.title) }) } } }
In addition to the instance filter, you can also get the global filter, because this$ options. Filters will look up along the proto, and the global filter exists in the prototype.
Custom instruction get instance
In some cases, when the underlying operation of ordinary DOM elements is required, custom instructions will be used. Like the permission instruction commonly used in the project, it can be accurate to a module node. The general idea is to obtain the permission list. If the current binding permission is not in the list, delete the node element.
Vue.directive('role', { inserted: function (el, binding, vnode) { let role = binding.value if(role){ const applist = sessionStorage.getItem("applist") const hasPermission = role.some(item => applist.includes(item)) // Do you have permission if(!hasPermission){ el.remove() //Delete module node without permission } } } })
The custom instruction hook function receives three parameters in total, including el (real dom of binding instruction), binding (instruction related information), and vnode (virtual dom of node).
Suppose that the business changes now and applist is stored in vuex, but the instruction wants to use the attributes on the instance or $store on the prototype. We can't get it because there is no direct instance access in the hook function. As the current virtual dom, vnode is bound to the instance context. In this case, access vnode Context can easily solve the problem.
Vue.directive('role', { inserted: function (el, binding, vnode) { let role = binding.value if(role){ // vnode.context is the current instance const applist = vnode.context.$store.state.applist const hasPermission = role.some(item => applist.includes(item)) if(!hasPermission){ el.remove() } } } })
Elegant registration plug-in
Plug ins are often used to add global functionality to Vue. Vue router and vuex are commonly used through Vue Use to register. Vue.use will automatically find the install method to call, and the first parameter accepted is the Vue constructor.
Generally, when using component library, in order to reduce the package volume, the way of loading on demand is adopted. If you import components one by one in the entry file, it will make main JS is becoming larger and larger. Based on the idea of modular development, it is best to package it into a configuration file separately. Cooperate with Vue Use, which can be used in the entry file at a glance.
vant.config.js:
import { Toast, Dialog } from 'vant' const components = { Toast, Button } const componentsHandler = { install(Vue){ Object.keys(components).forEach(key => Vue.use(components[key])) } } export default componentsHandler
Copy code
main.js:
import Vue from 'vue' import vantCompoents from '@/config/vant.config' Vue.config.productionTip = false Vue.use(vantCompoents) new Vue({ render: h => h(App) }).$mount('#app')
Reference documents
Automatic introduction module
When developing medium and large-scale projects, a large function will be divided into small functions, which can not only facilitate the reuse of modules, but also make the modules clear and better maintain the later projects.
Like api files, modules are generally divided according to functions. When combining, you can use require Context all module files in the folder are imported at one time, instead of importing module files one by one. Whenever you add a module file, you only need to pay attention to the writing of logic and module exposure Context will help us automatically import.
Note: require Context is not innate, but provided by webpack. At build time, the webpack parses it in the code.
import Request from '../service/request' let importAll = require.context('./modules', false, /\.js$/) class Api extends Request{ constructor(){ super() //importAll.keys() is an array of module paths importAll.keys().map(path =>{ //Compatible processing: default obtain the content exposed by ES6 specification; The latter obtains the content exposed by the commonJS specification let api = importAll(path).default || importAll(path) Object.keys(api).forEach(key => this[key] = api[key]) }) } }
export default new Api()
require.context parameter:
Folder path
Whether to recursively find modules under subfolders
Module matching rules, generally matching file suffixes
This method can be used for any scenario that needs to be imported in batch. It includes some public global components, which can be used by adding new components to the folder without registering. If you haven't used your little partner, you must understand that it is simple and practical and can improve efficiency.
Reference documents
Route lazy loading (dynamic chunkName)
As a means of performance optimization, routing lazy loading can delay the loading of routing components. Usually, we will add "webpackchunkname" for the route that is loaded late. When packaging, the route component will be packaged separately.
let router = new Router({ routes: [ { path:'/login', name:'login', component: import(/* webpackChunkName: "login" */ `@/views/login.vue`) }, { path:'/index', name:'index', component: import(/* webpackChunkName: "index" */ `@/views/index.vue`) }, { path:'/detail', name:'detail', component: import(/* webpackChunkName: "detail" */ `@/views/detail.vue`) } ] })
There is no problem with the above writing method, but a closer look shows that their structures are similar. As an excellent developer, we can use map loop to solve this repetitive work.
const routeOptions = [ { path:'/login', name:'login', }, { path:'/index', name:'index', }, { path:'/detail', name:'detail', }, ] const routes = routeOptions.map(route => { if (!route.component) { route = { ...route, component: () => import(`@/views/${route.name}.vue`) } } return route }) let router = new Router({ routes })
While writing less code, we also sacrifice the "magic Annotation". As we all know, there is no way to write dynamic annotations in code. This question is very embarrassing. Is there no way to have the best of both worlds?
Powerful webpack has come to the rescue. Starting from webpack 2.6.0, placeholders [index] and [request] are supported as incremental numbers or actually parsed file names. We can use "magic notes" like this:
const routes = routeOptions.map(route => { if (!route.component) { route = { ...route, component: () => import(/* webpackChunkName: "[request]" */ `@/views/${route.name}.vue`) } } return route })
Reference document
Some development tips of vue.
The above is the whole content of this article. I hope it will be helpful to your study and I hope you can give me more support.
Original author: WahFung
Original link: https://juejin.im/post/5f179100f265da22e27a9833