Preface 🍊
I go to work every day writing repetitive code, I’m not productive, and I feel like I’m not getting any better. How to finish the work at hand faster, improve their own development efficiency, xiaobian sorted out some Vue development skills, you work overtime first, I will go shopping with goddess after work.
HookEvent, originally can listen to the component lifecycle 🍊
Internally listen for life cycle 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 } } 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.
Big man: this writing is not very good, should be monitored`resize`Events and Destruction`resize`Together, the two pieces of code are now separated by hundreds of lines of code, making them less readable
Me: So I switch the two lifecycle hook functions and put them together?
Bosses:`hook`Heard yet? I:` Vue3.0 `Oh, yeah. What? We're upgrading`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', () => {})
Externally listen for life cycle 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
Vuex for small projects? Write a state manager using vue. Observable 🍊
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
Create the store
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
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
To develop global components, you may want to look at vue.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((a)= > {
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
Developing loading components
<template>
<transition name="custom-loading-fade">
<! - loading mask - >
<div v-show="visible" class="custom-loading-mask">
<! -- Loading icon --> <div class="custom-loading-spinner"> <i class="custom-spinner-icon"></i> <! --loading the text shown above --> <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 the display text above 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">
<! -- Other 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 can mask the entire page
The component is converted to a global component by vue.extend
- Change the loading component to props and data
export default {
data() {
return {
text: ' '. visible: false
} } } Copy the code
- Modify components with vue.extend
// 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((a)= > { // 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((a)= > { instance.visible = true }) // Assign the component instance to Loading loading = instance return instance } export default Loading Copy the code
- Loading is used on the page
import Loading from './loading/index.js'
export default {
created() {
const loading = Loading({ text: 'Loading... ' })
// Close in 3 seconds
setTimeout((a)= > { 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
Mount the component to Vue.prototype
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 🍊
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, now can only be used in full screen with 2. You can mount the specified element using the commandloading
If you need it, we’ll do it
Develop the V-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', { / * ** Only called once, when the directive is first bound to an element, where you can do some initialization* @param {*} the element to bind to the el directive{name:' bind ', value: 'bind ',arg:' v-bind:text '}* / bind(el, binding) { const instance = new LoadingContructor({ el: document.createElement('div'), data: {} }) el.appendChild(instance.$el) el.instance = instance Vue.nextTick((a)= > { el.instance.visible = binding.value }) }, / * ** 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 } }, / * ** Only called once, when directives are unbound from elements * @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
Use directives on elements
<template>
<div v-loading="visible"></div>
</template>
<script> export default { data() { return { visible: false } }, created() { this.visible = true fetch().then((a)= > { this.visible = false }) } } </script> Copy the code
Which scenarios in the project can be customized with directives
- Add a component
loading
The effect - Button level permission control
v-permission
- Code burial points, which define instructions according to operation types
input
The input box automatically gets focus- And so on…
Deep watch and watch trigger the callback immediately, I can monitor 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.
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
Immediately triggered
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
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
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.
Let’s 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 hanging on the 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.
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
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 there is no this, which is replaced by the second argument to the Render function
- Functional components have no lifecycle hook functions, cannot use computed properties, watches, and so on
- Functional components cannot expose events via $emit. Calling events can only call external events via context.listeners. Click
- Because functional components are not instantiated, when a component is referenced externally via ref, HTMLElement is actually referenced
- Functions components do not need to be declared, so properties that are not declared in props are automatically resolved to prop, while all undeclared properties in normal components are resolved to $attrs and automatically mounted to the component root element.
I don’t want to use JSX, can I use functional components?
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="avatar ? avatar : 'default-avatar.png'" />
</template>
<! -- according to section 6 of the previous section, the statement props can be omitted. Copy the code
Reprint 🍊
6 Vue Combat Tips you Probably never knew