Two orioles singing cui Liu, a pile of bugs on the west.
Writing repetitive code at work every day, when a CV boy, busy until eight or nine, low work efficiency, feel that I have no improvement. Learn these Vue tips so you can leave work early and date a goddess. Here are some new Vue tips to help you get things done faster. You guys work overtime, and I’ll go shopping with the goddess.
HookEvent ‘, the original way to listen for the component lifecycle
1. Internal listening for lifecycle functions
Today, the product manager sent me another requirement to develop a chart. I got the requirement and took a look at it. Then I went to the Echarts website to copy the sample code
<template>
<div class="echarts"></div>
</template>
<script>
export default {
mounted() {
this.chart = echarts.init(this.$el)
// Request data, assign data, etc.
// The listener window changes, resize the component
window.addEventListener('resize'.this.$_handleResizeChart)
},
updated() {
// Have done a lot of work
},
created() {
// Have done a lot of work
},
beforeDestroy() {
// When the component is destroyed, the listener event is destroyed
window.removeEventListener('resize'.this.$_handleResizeChart)
},
methods: {
$_handleResizeChart() {
this.chart.resize()
},
// A bunch of other methods}}</script>
Copy the code
After writing the function, I was happy to test it. There was no problem in the test. The product manager said it was great. However, during code review, the tech guy said, this is a problem.
This is not a good way to write it. You should put the resize event and the resize event together. Now the two pieces of code are separated by hundreds of lines of code, which is not readable. Big guy: 'Hook' have you heard? Me: 'Vue3.0' just have ah, zha, we want to upgrade 'Vue'?Copy the code
Then the tech guy ignored me and threw a piece of code at me
export default {
mounted() {
this.chart = echarts.init(this.$el)
// Request data, assign data, etc.
// The listener window changes, resize the component
window.addEventListener('resize'.this.$_handleResizeChart)
// The hook listener destroys the hook function and cancels the listener event
this.$once('hook:beforeDestroy'.() = > {
window.removeEventListener('resize'.this.$_handleResizeChart)
})
},
updated() {},
created() {},
methods: {
$_handleResizeChart() {
// this.chart.resize()}}}Copy the code
After reading the code, I realized that Vue can also listen to life cycle functions in this way.
$on(‘hook:updated’, () => {}) {$on(‘hook:updated’, () => {})
2. Externally listen for lifecycle functions
Today, my colleague asked me in the company group, is there any way to listen to the component’s life cycle function externally?
My colleague uses a third-party component and needs to monitor data changes of the third-party component. However, the component does not provide the change event, so my colleague has no way out. So I decided to monitor the updated hook function of the component externally. After a look, Vue supports listening externally to the component’s lifecycle hook functions.
<template>
<! -- Listen for the component's updated hook function via @hook:updated
<! All lifecycle hooks of a component can be listened for by @hook: hook function name.
<custom-select @hook:updated="$_handleSelectUpdated" />
</template>
<script>
import CustomSelect from '.. /components/custom-select'
export default {
components: {
CustomSelect
},
methods: {
$_handleSelectUpdated() {
console.log('Custom-select component's updated hook function is fired')}}}</script>
Copy the code
Small projectsVuex
? withVue.observable
Write a status management by hand
In the front-end project, there are a lot of data need to convey Shared between the various components, by this time then you need a state management tools, in general, we will use Vuex, but for small projects, like Vuex’s official website said: “if you don’t intend to develop large single-page applications, using Vuex may be onerous redundancy. That’s true — if your application is simple, you’d better not use Vuex.” This allows you to manually build a Vuex using Vue2.6’s new API vue. Observable
1. Createstore
import Vue from 'vue'
// Create a respondable object with vue.Observable
export const store = Vue.observable({
userInfo: {},
roleIds: []})// define mutations, modify attributes
export const mutations = {
setUserInfo(userInfo) {
store.userInfo = userInfo
},
setRoleIds(roleIds) {
store.roleIds = roleIds
}
}
Copy the code
2. Reference in the component
<template>
<div>
{{ userInfo.name }}
</div>
</template>
<script>
import { store, mutations } from '.. /store'
export default {
computed: {
userInfo() {
return store.userInfo
}
},
created() {
mutations.setUserInfo({
name: 'zijun'}}})</script>
Copy the code
Developing global components, you may want to knowVue.extend
Vue.extend is a global Api. It is rarely used in business development. However, when we want to develop some global components such as Loading,Notify,Message, etc., we can use vue. extend.
Students might write this in code when using elemental-UI loading
/ / display loading
const loading = this.$loading()
/ / close the loading
loading.close()
Copy the code
It might not be unusual to write it this way, but if you write it this way, right
const loading = this.$loading()
const loading1 = this.$loading()
setTimeout(() = > {
loading.close()
}, 1000 * 3)
Copy the code
You will notice that I called loading twice, but only one loading occurred, and I only turned loading off, but loading1 was also turned off. How does this work? We now use vue. extend + singleton mode to implement a loading
1. The developmentloading
component
<template> <transition name="custom-loading-fade"> <! --loading mask --> <div v-show="visible" class="custom-loading-mask"> <! <div class="custom-loading-spinner"> < I class="custom-spinner-icon"></ I ><! <p class="custom-loading-text">{{text}}</p> </div> </div> </transition> </template> <script> Export default {props: {// Whether to display loading Visible: {type: Boolean, default: false}, // loading visible text: {type: String, default: '' } } } </script>Copy the code
Once the loading component is developed, if you need to use it directly, you need to use it this way
<template> <div class="component-code"> <! <custom loading :visible="visible" text=" loading "/> </div> </template> <script> export default {data() { return { visible: false } } } </script>Copy the code
But that’s not what we need
- The closure can be displayed by calling the method directly through JS
loading
You can mask the entire page
2. ByVue.extend
Transform a component into a global component
1. The transformationloading
Component, will component ofprops
Instead ofdata
export default {
data() {
return {
text: ' '.visible: false}}}Copy the code
2. ByVue.extend
Transform component
// loading/index.js
import Vue from 'vue'
import LoadingComponent from './loading.vue'
// Wrap the component as a subclass by vue.extend
const LoadingConstructor = Vue.extend(LoadingComponent)
let loading = undefined
LoadingConstructor.prototype.close = function() {
// If Loading has a reference, remove the reference
if (loading) {
loading = undefined
}
// Hide the component first
this.visible = false
// Wait 300 ms for loading to close the animation and then destroy the component
setTimeout(() = > {
// Remove the mounted DOM element
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el)
}
// Call the component's $destroy method for component destruction
this.$destroy()
}, 300)}const Loading = (options = {}) = > {
// If the component is already rendered, return it
if (loading) {
return loading
}
// The element to mount
const parent = document.body
// Component properties
const opts = {
text: ' '. options }// Initializing a component via a constructor is equivalent to new Vue()
const instance = new LoadingConstructor({
el: document.createElement('div'),
data: opts
})
// Attach the loading element to parent
parent.appendChild(instance.$el)
/ / display loading
Vue.nextTick(() = > {
instance.visible = true
})
// Assign the component instance to Loading
loading = instance
return instance
}
export default Loading
Copy the code
3. Use loading on the page
import Loading from './loading/index.js'
export default {
created() {
const loading = Loading({ text: 'Loading... ' })
// Close in 3 seconds
setTimeout(() = > {
loading.close()
}, 3000)}}Copy the code
Loading is now available globally, but if you want to mount it to vue. prototype like element-ui, you’ll need to do it with this.$loading
4. Mount components toVue.prototype
The above
Vue.prototype.$loading = Loading
// Bind the Loading method before export
export default Loading
// Use within the component
this.$loading()
Copy the code
Custom instructions to solve problems from the bottom up
What is a directive? An order is when your girlfriend points at you and says, “Over there washboard, get down on your knees, that’s an order!” . Just kidding. Programmers don’t have girlfriends.
In the last section, we developed a loading component. After the development, other developers put forward two requirements when using it
- Can be
loading
Mounted to an element, can now only be used in full screen - You can mount the specified element using the command
loading
If you need it, we’ll do it
1. The developmentv-loading
instruction
import Vue from 'vue'
import LoadingComponent from './loading'
// Construct a component subclass using vue.extend
const LoadingContructor = Vue.extend(LoadingComponent)
// Define a directive named loading
Vue.directive('loading', {
/** * is called only once, when the directive is first bound to the element. You can do some initialization here@param {*} The element * to which the el directive is bound@param {*} Binding directives pass information, including {name:' directive name ', value: 'directive binding value ',arg:' directive parameter V-bind :text corresponding text'} */
bind(el, binding) {
const instance = new LoadingContructor({
el: document.createElement('div'),
data: {}
})
el.appendChild(instance.$el)
el.instance = instance
Vue.nextTick(() = > {
el.instance.visible = binding.value
})
},
/** * is called when the VNode of the component is updated@param {*} el
* @param {*} binding* /
update(el, binding) {
// Check whether loading is displayed by changing the ratio
if(binding.oldValue ! == binding.value) { el.instance.visible = binding.value } },/** * is called only once, when the instruction is unbound from the element@param {*} el* /
unbind(el) {
const mask = el.instance.$el
if (mask.parentNode) {
mask.parentNode.removeChild(mask)
}
el.instance.$destroy()
el.instance = undefined}})Copy the code
2. Use directives on elements
<template>
<div v-loading="visible"></div>
</template>
<script>
export default {
data() {
return {
visible: false}},created() {
this.visible = true
fetch().then(() = > {
this.visible = false}}})</script>
Copy the code
3. Which scenarios in the project can be customized with instructions
-
Add a loading effect to the component
-
Push-button level permission controls V-Permission
-
Code burial points, which define instructions according to operation types
-
Input The input box automatically gets focus
-
And so on…
The depth of thewatch
withwatch
Trigger a callback immediately, and I can listen in on your every move
In the development of Vue project, we often use Watch to monitor the changes of data, and then do a series of operations after the changes.
1. Basic usage
For example, on a list page, we hope that the search can be triggered automatically when the user enters the search keyword in the search box. At this time, in addition to monitoring the change event of the search box, we can also monitor the change of the search keyword through watch
<template>
<! This example uses element-ui-->
<div>
<div>
<span>search</span>
<input v-model="searchValue" />
</div>
<! -- list, code omitted -->
</div>
</template>
<script>
export default {
data() {
return {
searchValue: ' '}},watch: {
// Reload the data after the value changes
searchValue(newValue, oldValue) {
// Judge the search
if(newValue ! == oldValue) {this.$_loadData()
}
}
},
methods: {
$_loadData() {
// Reload the data}}}</script>
Copy the code
2. Trigger immediately
It is now possible to load data when the value changes, but to load data when the page is initialized, we need to call the $_loadData method again in the Created or Mounted lifecycle hook. Instead of writing this, you can configure the immediate trigger property for Watch to do just that
/ / watch
export default {
watch: {
// Reload the data after the value changes
searchValue: {
// Use handler to listen for property changes. The first call to newValue is an empty string and oldValue is undefined
handler(newValue, oldValue) {
if(newValue ! == oldValue) {this.$_loadData()
}
},
// Configure immediate execution of properties
immediate: true}}}Copy the code
3. Deep Listening (I can see your inner workings)
A form page that needs to be changed to the modified state after the user modifies any item in the form. If we follow the writing method of watch in the above example, then we need to monitor every attribute of the form, which is too troublesome. In this case, we need to use the deep monitor of Watch
export default {
data() {
return {
formData: {
name: ' '.sex: ' '.age: 0.deptId: ' '}}},watch: {
// Reload the data after the value changes
formData: {
// Note that newValue and oldValue are always equal because of object references
handler(newValue, oldValue) {
// Mark the page edit status here
},
// By setting the deep property to true, watch listens for every change in the value of the object
deep: true}}}Copy the code
Listen in, call it off. Check it out$watch
There is a requirement that you have a form and you need to listen for changes to the form as you edit it, save button enabled if it changes, save button disabled otherwise. At this time, for new forms, watch can be directly used to listen to formData (assuming formData), as mentioned in the above example. But for editing forms, the form needs to backfill data, and the value of formData will be modified, which will trigger watch, and it is impossible to accurately determine whether to enable the save button. Now you need to know about $watch
export default {
data() {
return {
formData: {
name: ' '.age: 0}}},created() {
this.$_loadData()
},
methods: {
// Simulate an asynchronous request for data
$_loadData() {
setTimeout(() = > {
/ / value ascribed
this.formData = {
name: 'zijun'.age: 18
}
// After the form data is backfilled, monitor whether the data changes
const unwatch = this.$watch(
'formData'.() = > {
console.log('The data has changed.')}, {deep: true})// The simulated data has changed
setTimeout(() = > {
this.formData.name = 'Joe'
}, 1000)},1000)}}}Copy the code
We can use this.$watch to listen for data changes if needed. $this.$watch returns a function called unwatch. If unwatch() is required, unwatch() is required
Functional components, functions are components, right?
What is a functional component? A functional component is a function that is a component, which feels like a word game. Those of you who have used React will be familiar with functional components. Functional components, we can think of as having no internal state, no lifecycle hook functions, and no this(components that do not need to be instantiated).
In the process of daily writing bug, often develop some pure display business components, such as some details page, list interface, etc., they have a common characteristic is just outside into the data show, don’t need to have internal state, don’t need in the life cycle hook function do processing, you can consider to use functional components.
1. Start with the code for a functional component
export default {
// Specify components as functional by configuring the functional property
functional: true.// External properties received by the component
props: {
avatar: {
type: String}},/** * render function *@param {*} h
* @param {*} Context function components don't have this, props, slots, etc. All hang */ on context
render(h, context) {
const { props } = context
if (props.avatar) {
return <img src={props.avatar}></img>
}
return <img src="default-avatar.png"></img>}}Copy the code
In the example above, we define an avatar component that displays the incoming avatar if it is passed in from the outside, and the default avatar otherwise. In the above code you can see that there is a render function, this is Vue using JSX writing, about JSX, small series will be in the subsequent article will be a detailed tutorial to use.
2. Why use functional components
- The main and key reason is that functional components do not require instantiation, are stateless, and have no life cycle, so render performance is better than normal components
- The functional component structure is simpler and the code structure is clearer
3. Differences between functional components and normal components
- Functional components need to specify functional in the declared component
- Functional components do not need to be instantiated, so no
this
.this
throughrender
The second argument to the function - Functional components have no lifecycle hook functions, cannot use computed properties, watches, and so on
- Functional components cannot expose events through $emit, calling events can only be exposed through
context.listeners.click
To call an event passed in externally - Because functional components are not instantiated, they pass externally
ref
When a component is referenced, it is actually referencedHTMLElement
- Of functional components
props
Can not display the declaration, so there is noprops
All declared properties are automatically and implicitly resolved toprop
All undeclared attributes of a normal component are resolved to$attrs
Inside, and automatically mount to the component root elementinheritAttrs
Attribute disallow)
I don’t want to use itJSX
Can functional components be used?
Prior to Vue2.5, functional components could only be used through JSX; after that, functional components could be generated through template syntax
<! -- Add functional property to template -->
<template functional>
<img :src="props.avatar ? props.avatar : 'default-avatar.png'" />
</template>
<! -- according to section 6 of the previous section, the statement props can be omitted.
Copy the code
conclusion
Don’t blow out your inspiration and your imagination; Don’t be a slave to your model. — Vincent Van Gogh
❤️ Love triple punch
1. If you think this article is not bad, please follow it, like it, and bookmark it so that more people can see it
2. Pay attention to the public number [front-end some play], regularly push fresh dry goods good articles for you.
3. Wear a mask for personal protection at special stage.