Recently, it is required to use VUE to separate the front and rear ends of wechat public account development. After constantly exploring and stepping on the pit, the following common problems and solutions in vUE project development are summarized. If you’re a Vue, ignore your little brother

  • List to enter the details page pass parameter problem.
  • Local development environment requests server interface cross-domain issues
  • Axios encapsulation and unified management of API interfaces
  • On demand loading of UI libraries
  • How to elegantly override the styles of UI library components only on the current page
  • Timer problem
  • Rem file import problem
  • Vue-awesome -Swiper basically solves all your rotation needs
  • Problem with large.map files generated after packaging
  • FastClick’s 300ms delay solution
  • The order in which options are written in a component
  • Lazy route loading (also called lazy loading)
  • Start the GZIP compression code
  • The details page returns to the practice of the list page caching data and browsing locations, and other pages entering the list page brushing data
  • CSS scoped private scope and depth selectors
  • Hiper turns on speed testing
  • Two ways to obtain vue data + skeleton screen
  • Bidirectional data binding of custom components (parent and child components)
  • Split management of routes
  • Mixins are used to simplify common operations
  • The file, image, or background resource does not exist or has an incorrect path after being packaged
  • Develop the vue plug-in, publish to Github, set up the presentation address, publish the NPM package

= = = = = = = = = = = = = = = = = = = = = = = = = = = this is a delicious line ~ ~ = = = = = = = = = = = = = = = = = = = = = = = = =

List to enter the details page pass parameter problem.

For example, the product list page to the product details page, you need to pass a product ID;


<router-link :to="{path: 'detail', query: {id: 1}}"> Go to the detail page </router-link>Copy the code

The path to the c page is http://localhost:8080/#/detail? Id =1, you can see that a parameter id=1 is passed, and the id will still exist even if the page is refreshed. In this case, the c page can obtain the corresponding detailed data by id. The method of obtaining the ID is this.$route.query.id

The vue parameter transmission modes are query and params+ dynamic route parameter transmission.

Here’s the difference:

1. Query switches routes by path and params switches routes by name

// query Switches routes through path <router-link :to="{path: 'Detail', query: { id: 1 }}"> Go to the Detail page </router-link> // Params switches routes by name <router-link :to="{name: 'Detail', params: { id: 1 }}"> Go to the Detail page </router-link>Copy the code

Query receives parameters via this.$route.query and params receives parameters via this.$route.params.

// query through this.$route.query Receive parameterscreated () {
    const id = this.$route.query.id; } // params through this.$route.params to receive parameterscreated () {
    const id = this.$route.params.id;
}Copy the code

3. Query parameter url display mode: /detail? Id =1&user=123&identity= 1&More parameters

Params + Dynamic route URL mode: /detail/123

4. Params dynamic route transmission parameters, be sure to define parameters in the route, and then in the route jump must add parameters, otherwise the page is blank:

{      
    path: '/detail/:id',      
    name: 'Detail',      
    component: Detail    
},Copy the code

Note that params can pass and receive a parameter if it is not defined in the route, but once the page is refreshed, the parameter does not exist. This doesn’t work for behavior that relies on parameters to do something, because you can’t tell the user not to refresh the page. Such as:

// In the route defined, define only one id parameter {path:'detail/:id',
    name: 'Detail'// Id is a parameter defined in the route, but token is not defined <router-link :to="{name: 'Detail', params: { id: 1, token: '123456' }}"> Go to the Detail page </router-link> // Receive on the Detail pagecreated() {const id = this; // The token does not exist after the page is refreshed.$route.params.id;
    const token = this.$route.params.token;
}Copy the code


Local development environment requests server interface cross-domain issues



Error: there is no access (cross-domain problem). When a local development project requests a server interface, cross-domain issues arise due to the same origin policy on the client.

Here’s a case where no configuration allows local cross-domains:






As you can see, when we click get data, the browser tells us to cross domains. So we can’t access the data.

So let’s demonstrate the data fetch situation with cross-domain allowed:



Note: After the configuration, be sure to shut down the original server and restart the project with NPM run dev. It doesn’t work.





We have set up in 1 to allow local cross-domain, and in 2, notice that when we access the interface, we write/API, where/API refers to the domain name of the interface we want to request. BaseURL = ‘/ API ‘; axios.defaults.baseurl = ‘/ API ‘; axios.defaults.baseurl = ‘/ API ‘; $axios.get(‘app.php? M =App&c=Index&a=index’) At this point, if you look at the XHR request in the network, you will see that the request address localhost:8080/ API is displayed. It’s not a big deal. It’s just an agent:




ProxyTable proxyTable proxyTable proxyTable proxyTable

ProxyTable: {// start with '/ API' to proxy all requests to the target server'/api': {
        target: 'http://jsonplaceholder.typicode.com', // changeOrigin:true, // whether to enable cross-domain pathRewrite: {//'^/api': ' '}}}Copy the code

Note: after the configuration must be shut down the original server, renpm run devStart the project. It doesn’t work.



Axios encapsulation and unified management of API interfaces

The encapsulation of Axios is mainly used to help us intercept requests and responses.

In the interception of requests, we can carry userToken, POST request header, SERIalization of DATA submitted by QS to POST, etc.

In the interception of the response, we can do the uniform processing of the error according to the status code and so on.

The unified management of the AXIOS interface is a necessary process when doing projects. This makes it easier for us to manage our interface so that we don’t have to go back to our business code to modify it when the interface is updated.

Since this is slightly more content, put in another article, here to send a link.


UI library loading on demand:

I won’t go into the reasons why you should use an on-demand loading approach rather than all at once. Vue UI library loading on demand

  • Installation:cnpm i vant -S
  • Install the babel-plugin-import plug-in to load on demand:cnpm i babel-plugin-import -D
  • Add plug-in configuration to.babelrc file:

libraryDirectory { 
    
    "plugins": [// here is the original part of the code // ………… // here is the code we want to configure ["import", 
            { 
                "libraryName": "vant"."libraryDirectory": "es"."style": true}}]]Copy the code
  • Load the plugins you need on demand in main.js:

Import {DatetimePicker, Button, List} from'vant';Copy the code

  • Using components:

// Use the vant component vue.use (DatetimePicker).use(Button).use(List);Copy the code

  • Finally use in the page:

<van-button type="primary"> button < / van - button >Copy the code

Ps: out of vant library, such as antiUi, elementUi, many UI libraries support on-demand loading, you can go to see the documentation, will be mentioned above. The babel-plugin-import plug-in is installed to support on-demand loading. It works in the same way as vant, so you can use it.


How to elegantly override the styles of UI library components only on the current page

<style lang=”less” scoped></style> < span style =” box-sizing: border-box; color: RGB (50, 50, 50); line-height: 20px; white-space: inherit;” So here’s the question. Look at this:



All styles that we normally write are appended with the attribute [data-v-23d425f8] (as shown in 1), but tags inside third-party components are not compiled with the attribute [data-V-23d425f8]. So, if we want to change the style of the component, we have no choice. Instead, write a class for a third party component, then write a style tag in a common CSS file or on the current page without the Socped attribute, and then change the style of the third party component directly in it. This is a good approach, but there are problems with global pollution and naming conflicts. Naming conventions can avoid naming conflicts. But still not elegant enough.

Forced a famous (strong) show () the (disease) (of) before end of the (), how can allow the problem? Okay, here’s the elegant solution:

Solved by depth selector. For example, to change the style of the van-ellipsis class in the component shown above, do this:

.van-tabs /deep/ .van-ellipsis { color: blue};
Copy the code

The compiled result is:



This avoids adding [data-V-23d425F8] to van-ellipsis as well. At this point you can happily modify the styles of third-party components.

Of course, the /deep/ selector here is because I’m using less. If you’re not using less/sass, you can use the >>> symbol.

More on depth selectors later in this article.


Timer problem:

I write a timer on page A and ask it to print a 1 per second, then jump to page B. You can see that the timer is still executing. This is very performance intensive. As shown in the figure below:






Solution 1:

First I define the timer name in the data function:

data() {            
    return{timer: null // timer name}},Copy the code

Then use the timer like this:

This.timer = (() => {// some operation}, 1000)Copy the code

Finally, clear the timer within the beforeDestroy() life cycle:

beforeDestroy() {
    clearInterval(this.timer);        
    this.timer = null;
}Copy the code

There are two disadvantages to plan 1, and to quote the big words:


  • It needs to save this in this component instancetimerIf possible, only lifecycle hooks can access it. It’s not a serious problem, but it can be considered clutter.
  • Our build code is independent of our clean up code, which makes it more difficult to programmatically clean up everything we build.

Solution 2:

This method clears the timer by specifying the position of the $once event listener after the timer is defined. Here’s the full code:

const timer = setInterval(() =>{// some timer operations}, 500); / / by$onceTo listen for timers, the hook can be cleared in beforeDestroy. this.$once('hook:beforeDestroy', () => {            
    clearInterval(timer);                                    
})Copy the code

Solution # 2 thanks to @zzx18023 for the solution in the comments section. Similar to other components that need to be used on the current page, but need to be destroyed off the page (such as some third-party library picker components, etc.), you can use this method to solve the problem of running behind the page after leaving.

Overall, we recommend solution 2 to make the code more readable and clear. If you are not familiar with the use of $once, $on, $off, here is the url tutorial on the official website, programmed event listener there.


Rem file import problem:

Adaptation is an issue that we have to deal with when we’re doing mobile. For example, our solution to deal with adaptation is to write a rem.js, the principle is very simple, that is, according to the size of the page to calculate the HTML font size, basically friends know, here directly attached code, not much introduction.

; (function(c,d){var e=document.documentElement||document.body,a="orientationchange" in window?"orientationchange":"resize",b=function(){var f=e.clientWidth; e.style.fontSize=(f>=750)?"100px":100*(f/750)+"px"}; b(); c.addEventListener(a,b,false)})(window);Copy the code

Here’s the problem of how to introduce it. It’s very simple. In main.js, just import ‘./config/rem’. The import path is based on your file path.


Vue-awesome -Swiper basically solves all your rotation needs

In many of the UI libraries we use (Vant, antiUi, elementUi, etc.), there are rotation components, which are sufficient for normal rotation effects. However, there may be times when our rosters are awesome and the rosters in the UI library may not be enough. Of course, if the technology and time are all right, you can build your own fancy wheel.

The Vue-awesome swiper is a very powerful swiper component that basically meets our swiper needs. Swiper is used by a lot of people, so it’s a great tool to use, and it makes it easy for us to redevelop and customize the rotation we need. The vue-awesome swiper component is essentially based on swiper, or swiper that runs in vue. Here’s how to use it:

  • The installationcnpm install vue-awesome-swiper --save
  • Methods used in components that make little sense globally:
// Import component import'swiper/dist/css/swiper.css' 
import { swiper, swiperSlide } from 'vue-awesome-swiper'// Register components in components: {swiper, swiperSlide} // Template uses round casting // ref is the current round casting // Callback is the callback // For more parameters, see <swiper :options="swiperOption" ref="mySwiper" @someSwiperEvent="callback"> <! -- slides --> <swiper-slide><div class="item">1</div></swiper-slide>            
    <swiper-slide><div class="item">2</div></swiper-slide>            
    <swiper-slide><div class="item">3</div></swiper-slide> <! -- Optional controls --> <div class="swiper-pagination"  slot="pagination"></div>            
    <div class="swiper-button-prev" slot="button-prev"></div>            
    <div class="swiper-button-next" slot="button-next"></div>            
    <div class="swiper-scrollbar"   slot="scrollbar"></div>
</swiper>
Copy the code

// The parameters should be written in datadata() {            
    return{swiper swiperOption: {// scrollbar: {el:'.swiper-scrollbar'}, // add navigation: {nextEl:'.swiper-button-next',                        
                prevEl: '.swiper-button-prev',}, // Other parameters ………… }}},Copy the code

You can add or delete functions required by Swiper based on the documents. Attached documentation: NPM documentation, swiper3.0/4.0 documentation, more usage, please refer to the documentation.


Problem with large.map files generated after packaging

After the project is packaged, the code is compressed and encrypted. If an error occurs when the project is run, the output error message will not tell you exactly where the error occurred. A file with the.map suffix, like unencrypted code, can output exactly which line and which column is wrong. You can set it not to generate such a file. However, we don’t need.map files in the build environment, so we can do this without generating them when packaging:

In the config/index.js file, set productionSourceMap: false to not generate the.map file



FastClick’s 300ms delay solution

When developing mobile projects, there is a 300ms delay for click events. As for why there is this problem, please baidu. FastClick can be used for both vUE and JQ projects.

Install fastClick:

cnpm install fastclick -SCopy the code

Introduce fastClick and initialization in main.js:

import FastClick from 'fastclick'; // Import the plugin
FastClick.attach(document.body); / / use fastclickCopy the code


The order in which options are written in a component

Why should there be a uniform writing order for choices? Simply minimize the cost of choice and perception.

  1. Side effects (triggering effects outside the component)

    • el
  2. Global awareness (requires knowledge beyond components)

    • name
    • parent
  3. Component type (Change the type of the component)

    • functional
  4. Template modifier (changes how templates are compiled)

    • delimiters
    • comments
  5. Template dependencies (resources used within the template)

    • components
    • directives
    • filters
  6. Combine (merge properties into options)

    • extends
    • mixins
  7. Interface (component interface)

    • inheritAttrs
    • model
    • props/propsData
  8. Local state (local responsive properties)

    • data
    • computed
  9. Events (callbacks triggered by reactive events)

    • watch
    • Lifecycle hooks (in the order they are called)
      • beforeCreate
      • created
      • beforeMount
      • mounted
      • beforeUpdate
      • updated
      • activated
      • deactivated
      • beforeDestroy
      • destroyed
  10. Non-responsive properties (instance properties that do not depend on the response system)

    • methods
  11. Rendering (a declarative description of component output)

    • template/render
    • renderError


View the volume of each file after packaging to help you quickly locate large files

Webpack-bundle-analyzer will be installed by default if you are a vue-CLI initialised project. This plugin will help you to see the comparison of the volume structure of the project and all the dependencies used in the project. You can also visually see the proportion of the volume of each module in the overall project. Very bully there is no ~~



NPM run build --report // Run directly, and then open http://127.0.0.1:8888/ in your browser to viewCopy the code

Make sure to turn off NPM run dev before running


Lazy route loading (also called lazy loading)

Lazy route loading can help us to enter the first screen without loading excessive resources, thus reducing the first screen loading speed.

In the routing file,

Non-lazy loading:

import Index from '@/page/index/index';
export default new Router({  
    routes: [    
        { 
            path: '/', 
            name: 'Index',     
            component: Index 
        }
    ]
})Copy the code
Lazy route loading:

export default new Router({
  routes: [    
        { 
            path: '/', 
            name: 'Index', 
            component: resolve => require(['@/view/index/index'], resolve) 
        }
   ]
})Copy the code


Start the GZIP compression code

For a single page application like SPA, the first screen loading is slow because all resources are loaded at once. One of the most effective ways to solve this problem is to enable GIZP on the front and back ends (along with caching, lazy route loading, and so on). Gizp actually helps us reduce the size of files. It can be compressed to about 30%, which means that a 100K file will only have 30K after GIzp.

Vue – Cli initialization project, this configuration is the default, you only need to enable. But you need to install the plugin first:

// Version 2.0 is set differently, v1 as of this writing. V2 Must work with vue-cli3cnpm I [email protected]Copy the code

Config /index.js

Build: {// other code ………… productionGzip:true, / /falseDo not enable gizp,trueOpen // other code}Copy the code

When you package it now, you will generate both the original file and the gzip file that ended with.gz. The specific implementation is if the client support gZIP, then the background after the return of gZIP after the file, if not support to return the normal no gZIP files.

** Note: Here the front end is done when packaging gzip, but the background server configuration is also required. Configuration is relatively simple, the configuration of a few lines of code can be, generally this operation can be called operation little brother little sister to do it, no operation to help the background configuration.


Details page returns to the list page to cache data and browse location, other pages to the list page to refresh the data practice

Consider this scenario: There are three pages, the home/or search page, the category page, and the product details page. We hope that when entering the classification page from the home page, the classification page should refresh the data. When entering the details page from the classification page and returning to the classification page, we do not want to refresh. We hope that the classification page at this time can cache the loaded data and automatically save the user’s last browsing location. Before the search in Baidu is basically keep-alive processing, but there are always some imperfect, so I summed up after the following practice.

To address this scenario we can use the keepAlive property provided by Vue. Here’s another portal that deals with this issue


CSS Coped private scope and depth selectors

Everyone knows that when a

Compile the front:

<style scoped>
.example {
  color: red;
}
</style>Copy the code

The compiled:

<style>
.example[data-v-f3f3eg9] {
  color: red;
}Copy the code

After reading this, you will realize that you are actually adding a property to the style of the component that you are writing, thus implementing the so-called private scope. The downside, however, is that given the way browsers render various CSS selectors, p {color: red} is many times slower when scoped (that is, when used in combination with feature selectors). If you use a class or id instead, such as.example {color: red}, the performance impact is eliminated. So, in your styles, avoid using the tag directly. Instead, give the tag a class name.


If you want one of the selectors in the scoped style to work “deeper”, such as affecting subcomponents, you can use the >>> operator:

<style scoped>
    .parent >>> .child { /* ... */ }
</style>Copy the code

The above code will compile to:

.parent[data-v-f3f3eg9] .child { 
    /* ... */ 
}Copy the code

>>> operator is not supported for less or sass precompilations. Use /deep/ instead of >>> operator. For example:.parent /deep/. * /}


= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

It will continue to be updated:


  • Axios encapsulation and unified management of API interfaces (updated, link above)
  • Hiper turns on speed testing
  • Two ways to obtain vue data + skeleton screen
  • Bidirectional data binding of custom components (parent and child components)
  • Split management of routes
  • Mixins are used to simplify common operations
  • The file, image, or background resource does not exist or has an incorrect path after being packaged
  • Develop the vue plug-in, publish to Github, set up the presentation address, publish the NPM package

— — — — — — — — — — — — a delicious line — — — — — — — — — — — — — — — — — — — — — — — — — a delicious line — — — — — — — — — — — — — — — — — — — — — — — — — a delicious line — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — a delicious segmentation — — — — — — — — — — — — — — — — — — — — a delicious line — — — — — — — — — — — — — — — — — — — — — — — — — a delicious line — — — — — — — — — — — — — — — — — — — — — — — — — a delicious line — — — — — — — — — — — — —

Hiper: a delightful performance analysis tool



In the figure above, the test results of the Hiper tool show DNS query time, TCP connection time, first Byte time to browser, page download time, DOM Ready time to download resources after DOM Ready time, white screen time, DOM Ready time, and total page load time.

Install globally in our editor terminal:

cnpm install hiper -gCopy the code

Use the: terminal to enter the command: hiper test url

When we omit the protocol header, the default is to add 'https://' before the URL

 # Simplest use
 hiper baidu.com

 # How do URLS contain any parameters, please use double quotation marks
 hiper "baidu.com?a=1&b=2"

 Load the specified page 100 times
 hiper -n 100 "baidu.com?a=1&b=2"

 # Disable cache to load the specified page 100 times
 hiper -n 100 "baidu.com?a=1&b=2" --no-cache

 # Ban JavaScript from loading specified page 100 times
 hiper -n 100 "baidu.com?a=1&b=2" --no-javascript
 
 Load the specified page 100 times using GUI format
 hiper -n 100 "baidu.com?a=1&b=2" -H false

 Load the page 100 times using the specified userAgent
 hiper -n 100 "baidu.com?a=1&b=2" -u "Mozilla / 5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"Copy the code

This usage example, I directly copy the documentation, can see the specific documentation, here send a link. When our project is slow to open, this tool can help us quickly figure out which step is affecting the page loading speed.

The usual way to view performance is to view data in Performance and network, record a few key performance indicators, and refresh them several times before looking at these performance indicators. Sometimes we find that the optimized project is slower than before because of the small sample size and the current “network,” “CPU,” and “memory” busyness.

If we have a tool that requests N web pages at once, and then averages the performance metrics, we can know with great accuracy whether the optimization is a “positive optimization” or a “negative optimization.”

Hiper solves this pain point.


Vue two ways to get data practice + simple skeleton screen implementation

There are two ways to obtain data in vUE.

  • Fetch after navigation: Complete the navigation and then fetch the data in the following component lifecycle hooks. Displays an indication such as “loading” during data fetch.

  • Fetch before navigation: Before navigation is complete, data is obtained from the guard that the route enters, and the navigation is performed after the data is obtained successfully.

From a technical point of view, both approaches are fine — it depends on what kind of user experience you want. So let’s put these two approaches to data acquisition into practice, along with a little thought about user experience optimization.

First, there is the first method: fetch after navigation, which is what most of us use (because we probably only know it at first). With this approach, we immediately navigate and render the component, and then retrieve the data in the component’s created hook. This gives us the opportunity to show a loading state during data acquisition and to show different loading states between different views. Getting data is something that everyone does, but here’s something about user experience:

  • Before the data is retrieved, the page component is loaded, but the data is not retrieved and rendered. Therefore, during this process, we cannot load the component that displays the data in the page. Instead, we need to have a loading component or skeleton screen.
  • When the page data fails to be retrieved, which can be interpreted as a request timeout, we will show the disconnected component.
  • In the case of a list page, consider the case of empty data, that is, a null prompt component.

So, our page is going to have these basic three parts, put the code:

<template>
    <div class="list"> <! -- loading or skeleton screen --> <div v-if="loading"> </div> <! <div v-if= <div v-if="error"> </div> <! --> <div v-if="requestFinished" class="content"> <! --> <div v-if=! "" isEmpty"> <! <ul></ul> < div> <! -- a sign to an empty component - > < div v - else > empty < / div > < / div > < / div > < / template >Copy the code

In this fetching case, we come in with a default showing loading or skeleton screen content, and then if the fetching fails (i.e., the request times out or the network is disconnected), load the error component and hide the other components. If the data request is successful, the component of the content is loaded and the other components are hidden. If it’s a list page, you might also have two pieces of content in the content component, the list and the empty hint, so you’ll need to determine whether to load the content or load the empty hint based on the data you get.

Second, the second way: before the navigation is complete

In this way, the data is requested in the beforeRouteEnter hook of the page, and the navigation page is only redirected after the data is successfully obtained.

beforeRouteEnter (to, from, next) {        
    api.article.articleDetail(to.query.id).then(res=> {            
        next(vm => {                
            vm.info = res.data;                
            vm.loadFinish = true})})},Copy the code

We all know that beforeRouteEnter hooks do not use this yet, so we can only assign or call a method in the next() callback function, which takes this as its first argument, after the component is successfully initialized.

2. I think a lot of times our API or Axios methods are mounted to the vue prototype. Since we can’t use this here, we have to introduce the API or our Axios within the page component.

3. Assignment operations can also be written in method methods, but calling this assignment method is still the same as vm.yourFunction().

4. Empty prompt, network disconnection processing and so on are the same as the first method. However, since the data is obtained first and then the loading component is skipped, we do not need to show skeleton screen or loading component in the expected page. Yes, we need a loading prompt at the previous page, such as a progress bar at the top of the page, before the current page enters. In this way, the user experience is more friendly, and not because the request s speed is a little slow, resulting in half a day of no response and the user does not know the result. The global progress bar at the top of the page can be set by running router-beforeeach (to, from, next) {} in main.js. When the page route changes, the progress bar at the top of the page is displayed. After entering a new route, the progress bar is hidden.



As for how to add a progress bar, since it was already written in another article, let’s just send the link here, so we don’t have to repeat the wasted space. The operation is also relatively simple, can be consulted.

In fact, here, then the skeleton screen thing has been solved by the way, the general page skeleton screen is a page skeleton picture, but pay attention to this picture should be as small as possible.


Bidirectional data binding of custom components (parent and child components)

The parent component uses props to pass values to the child component, and the child component uses emit to trigger custom events. But the point here is that the parent and child components communicate using the V-Model. When using another component library, it is often used to control the hidden effect of a component display, such as popover. Here’s how to unravel the mystery of the V-Model. Hold on, old driver, it’s time to step on the gas

The first thing that comes to mind when we mention v-Model is our two-way data binding for the user data of the form, which is very simple and rough to operate. For example:

<input type="text" v-model="msg">

data () {            
    return {                
        msg: ' '}}Copy the code

In fact, v-Model is a syntactic sugar. The above code has the same effect as the following code:

<input type="text" :value="msg" @input="msg = $event.target.value">
data () {
    return {
        msg: ' '}},Copy the code

Value =” MSG “@input=” MSG = $event. Target. Value” What you’re doing here is listening for the form input event and modifying the value of :value. In addition to using v-Model on the input form, it can also be used on the components, which is mentioned on the official website, but the introduction is not very detailed, resulting in a new contact friends will have a sense of confusion. Now that we understand the nature of the v-Model syntax sugar, we can implement two-way data binding between parent and child components like this:

The above principle implementation method, writing method 1:

Parent component usage:

<empty v-model="msg"></empty>Copy the code

Subcomponent:

// Click this button to trigger data synchronization between parent and child components <div class="share-btn" @click="confirm"> </div> </div> </div> </div> </div> </div>type: Boolean,                
        default: false            
    }        
},
methods: {            
    confirm() {// Bidirectional data binding parent :value corresponding value // pass$emitThe parent input event is fired, and the second argument is the value passed to the parent, which is passedfalseThe value // can be interpreted as @input= shown at the top"msg = $event.target.value"This event triggers the parent component's input event and will pass the value 'false'to MSG this.$emit('input'.false)}}Copy the code

For example, you can try to implement a global popover component. You can use v-Model to control the popover display and hide, because you have to do something inside the page to display it, and the code that controls its hiding is written in the component, When a component hides the corresponding value, the parent component’s corresponding value also changes.

Although it is feasible to implement v-Model communication between parent and child components in this way, we have to limit that the popos attribute received must be named value and the emit triggered must be input, which is easy to cause conflicts, especially in the form. So, in order to use v-Model communication to resolve conflicts more gracefully, we can use the Model option in the subcomponent, as shown in method 2:

Parent component:

<empty v-model="msg"></empty>Copy the code

Subcomponent:

<div class="share-btn" @click="confirm"> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> The value of event corresponds to the name of the event to be submitted when you emit emit. You can call it aa or bb, but the name should be meaningful!! model: { prop:'show',            
    event: 'changed'}, props: {// the prop property in the model option is specified, so show receives the value passed by the parent component v-model show: {type: Boolean,                
        default: false            
    }        
},        
methods: {            
    confirm() {// The first parameter, which corresponds to the value of the event of the model option, you can call aa, BBB, CCC, whatever you like.$emit('changed'.false)}}Copy the code

This method of implementing the v-Model binding values for parent and child components is very common in our development, especially when you want to encapsulate common components.

Finally, there is actually a way to implement bidirectional data binding: sync. This property was originally available, but was removed because it was thought to destroy unidirectional data flow, but it turned out to be useful and was added in version 2.3.

For example: Parent component:

<empty :oneprop.sync="msg"></empty>

data () {
    return {
        msg: ' '}}Copy the code

Child components:

<div class="share-btn" @click="changeMsg"> < div style = "box-sizing: border-box; line-height: 21px;type: String,                
        default: 'hello world'
    }        
},        
methods: {            
    changeMsg() {// Two-way data stream this.$emit('update:msg'.'helow world')}}Copy the code

In this way, data from the parent component can be updated in the child component. Since v-Model is only used once, there are scenarios where.sync can be used when multiple values need to be bidirectional bound. Sync is the syntactic sugar to simplify our operations:

<empty
    :msg="message"
    @update:msg="message = $event"
></empty>Copy the code

Mastering the V-Model of components will make it easier to encapsulate some common components.

Here’s another reminder:

  • vm.$emit(event ,[...args])The main purpose of this API is to trigger events on the current instance. Additional parameters are passed to the listener callback. Child components also belong to the current instance. First argument: the name of the event to fire. Subsequent parameters are optional: they are passed as parameters to the event to be triggered.The document

  • Listen for custom events on the current instance. The event can be emitted by $emit and the hook function can be listened to.

Vm.$on(event, callback) : keep listening; The document

Vm.$once(event, callback) : listen once; The document

Vm.$off([event, callback]) : remove listener; The document

Listen for custom events emitted by the $emit function. Listen for the hook function. The scenario of listening for the hook function is not often used, but it is important to know.

  • Vm.$attrs: gets all custom attributes except class and style passed by the parent component.
  • Vm.$Listeners: Retrieve all custom events passed by the parent component

For example: Parent component:

<empty
    :msg="message"
    :title="articleTitle"
    @confirm="func1"
    @cancel="func2"
></empty>Copy the code

You can get the properties and events passed by the parent component in the child component without defining them in the props. A simple demonstration of the child components is as follows:

created() {            
    const msg = this.$attrs.msg; // Get the MSG this passed by the parent component.$listeners.confirm && this.$listeners.confirm(); // Execute if the component passes the event confirm},Copy the code

This will come in handy when we write some more advanced components.


Route Split Management

Here said the route split refers to the route of the file, according to the module split, so convenient route management, more importantly is to facilitate the development of many people. Specific to split, it depends on your project to decide, if the project is small, also one or twenty routes, then it is very unnecessary to split. However, if you are developing a shopping mall project with many features, there may be hundreds or even hundreds of routes, then it is necessary to split the route file. Otherwise, if you look at a long string of routes in the index.js file, it’s bad too.



The router folder contains index.js as the route entry file, and then creates a new folder called Modules to store the route files of each module. For example, there is a routing file for the vote.js voting module and a routing file for the public module. Index. js: Index. js: index.js: index.js

import Vue from 'vue'
import Router from 'vue-router'// PUBLIC page routing file import PUBLIC from'./modules/public'// The routing file of the voting module import VOTE from'./modules/vote'Vue.use(Router) const Router = new Router({mode:'history', routes: [...PUBLIC,...VOTE,]}) // Route changes when router.beforeeach ((to, from, next) => {if(document.title ! == to.meta.title) { document.title = to.meta.title; } next()}) // Exportexport default routerCopy the code

I’m going to introduce vue and router first and then export, and I’m not going to go into this, the basic operation.

Router-beforeeach is written in the router index.js file. Some people might write it in main.js. This is fine, but personally, since it is a routing operation, it is better to manage it in the routing file. By the way, this demonstrates how to automatically change the page title when the page is switched.

Then import the js files that you divide according to the routing module, and then, when instantiating the route, pull the imported files out of the routes array by structure assignment. The end result is the same as the normal way of writing it.

Then take a look at the vote.js we imported:

/** * Router list of the voting module */exportDefault [// voting module home page {path:'/vote/index',        
        name: 'VoteIndex',        
        component: resolve => require(['@/view/vote/index'], resolve),        
        meta: {            
            title: 'vote'}}, // details page {path:'/vote/detail',        
    name: 'VoteDetail',        
    component: resolve => require(['@/view/vote/detail'], resolve),
    meta: {            
        title: 'Voting Details'}}]Copy the code

In this case, the route of the voting module is generated in an array. The entire route split operation is either vUE knowledge or an ES6 import/export and structure syntax. It depends on the project and the environment.

This route uses the lazy route loading method, if not clear, the text is described above.

And in the Meta meta field here, we define a title that stores the title of the current page, document.title.

Mixins are used to simplify common operations

In development, we often encounter money retention with two decimal places, timestamp conversion, and so on. Each time we write it as a public function and filter it in the filters on the page. This method every time, but feel every time need to use, have to write again in the filters, is also more annoying!! But how can it be that we apes are the ultimate in laziness

Guys, cut the shit! On mixins!!

import { u_fixed } from './tool'Const mixins = {filters: {// keep two decimal places mixin_fixed2 (val) {returnU_fixed (val)}, // number to Chinese, 16000 => 16000 mixin_num2Chinese (val) {return val > 9999 ? u_fixed(val/10000) + '万': val; }}}export default mixinsCopy the code

Create a new mixins.js and write all the contents we need to blend in. For example, here we have mixed in the filters, and several commonly used operations are written in the filters. You can expand it yourself.

In this case, import the JS on the page we need, declare it mixed in, and then use it in the normal way.



For example, I can now directly within the page using our filter operation {{1000 | mixin_fixed2}}


The file, image, or background resource does not exist or has an incorrect path after being packaged

The default value is’/’, which is the root path. Therefore, the public path of the packaged resource is static in the root directory. The problem is that if your resources are not stored in the root directory of the server, but in a folder like mobile under the root directory, the packaged path will conflict with the path in your code, and the resources will not be found.

So, to solve this problem, you can change the above path from the root of ‘/’ to the relative path of ‘./ ‘when packaging.



In this case, the packaged image, js and so on is the path'. / static/img/asc. JPG 'Relative paths like that, so no matter where you put it, you can’t go wrong. However, there is always but ~~~~~ everything is ok here, but the path of the background image is still wrong. Because now the relativities are going to bestatic/css/Under folderstatic/img/xx.jpgBut in factstatic/css/It’s not in the folderstatic/img/xx.jpg, i.e.,static/css/static/img/xx.jpgIt doesn’t exist. The current CSS file path relative to. To solve this problem, add a common path to the background image in our CSS'.. /.. / 'That is, let him go back up two levels to the sumindex.htmlThe location of the corresponding file, then the relative path at this pointstatic/img/xx.jpgI can find the right resource. If you want to change the public path of the background image, you can use loader to parse the background image. Open the utils file in the Build folder and go to the exports.cssLoaders function.

Find this location and add a configuration, as shown in the red box above, to change its public path to two levels up and back. So pack again see next, ok!

Last but not least, if your routing mode is history, then it should be packaged on the server. It is important to see the official document for details. Otherwise you will find white screen ah and all kinds of inexplicable problems. Remember!!



Develop the vue plug-in, publish to Github, set up the presentation address, publish the NPM package

For some components we usually use, we can encapsulate them as plug-ins, and then publish them to Github, and finally publish them as NPM packages. In this way, we can directly install plug-ins from NPM into our projects, saving us the process of copying and sharing them with others.

Because this part of the plug-in is more content, I will put it in another article, here is the link.