In the use of VUE, we commonly encounter a variety of scenarios, when the ordinary use of no impact, but perhaps as long as the optimization, can be more efficient and beautiful development. Here are some useful daily development tips.
1. Decentralization of multi-chart resize events
1.1 General Conditions
Sometimes we have scenarios where there are several diagrams in a component and we want the diagrams to be resized when the browser resizes.
So we’ll write in the parent container component:
1mounted() {
2 setTimeout(() => window.onresize = () => {
3 this.$refs.chart1.chartWrapperDom.resize()
4 this.$refs.chart2.chartWrapperDom.resize()
5 // ...
6
7 }, 200)
8destroyed() { window.onresize = null }
Copy the code
In this way, if the chart component is not on the same page as the parent container component, the state of the child component will be managed by the parent component. For easy maintenance, we naturally hope that the events and state of the child component will be maintained by ourselves, so that when adding and deleting components, there is no need to modify the parent component one by one.
1.2 optimization
Throttling is a throttling function that you can throttle by using lodash, and you can also implement it yourself. JS Throttle is throttle and debounce.
Using Echarts as an example, in each diagram component:
1computed: {2 /* chart DOM */ 3 chartWrapperDom() {4 const DOM = document.getelementByid ('consume-analy-chart-wrapper') 5 6 return Dom && echarts.init (dom) 7}, 8 /* Graph resize throttling, here using lodash, You can also use setTimout to throttle */ 9 chartResize() {10 return _.throttle(() => this.chartwrapperdom && this.chartWrapperDom.resize(), 400) 11 } 12}, 13mounted() { 14 window.addEventListener('resize', this.chartResize) 15}, 16destroyed() { 17 window.removeEventListener('resize', this.chartResize) 18}Copy the code
1.3 Optimization again
Here, since multiple chart instances use the same set of initialization logic, the extends can be used to consider reuse, so I thought of the Mixins generation provided by Vue, so I made some optimizations here to make each chart component of the same type more elegant:
Create a new mixin.js file:
1import Echarts from 'echarts' 2import _ from 'lodash' 3 4 5export default { 6 computed: {7 /* chart DOM */ 8 $_chartMixin_chartWrapperDom() {9 const DOM = document.getelementById (this.thisdomId) 10 return DOM &&echarts.init (dom) 11}, 12 13 You can also use setTimout to throttle */ 14 $_chartMixin_ chartResize() {15 return _.throttle(() => this chartWrapperDom.resize(), 400) 16 } 17 }, 18 19 methods: {/ * chart initialization * / 21 $20 _chartMixin_ initChart () {22 enclosing $_chartMixin_ chartWrapperDom. SetOption (23 options {/ * * /}}, 24 25 mounted() { 26 this. $_chartMixin_ initChart() 27 window.addEventListener('resize', this.$_chartMixin_chartResize) 28 }, 29 30 destroyed() { 31 window.removeEventListener('resize', this. $_chartMixin_ chartResize) 32 } 33}Copy the code
Then in each chart component:
1<script type='text/javascript'>
2import ChartMixin from './mixin'
3export default {
4 mixins: [ChartMixin],
5 data() {
6 return {
7 thisDomId: 'consume-analy-chart-wrapper'
8 }
9 }
10}
11</script>
Copy the code
This will mix the resize event logic previously defined in mixin.js into each diagram component and automatically initialize and destroy the event when destroyed.
Of course, it can be further optimized. For example, if there are multiple diagrams in one page, the above implementation has failed to capture. Here we need to reconstruct the code, which can refer to the implementation of Chartinitmixin-Github restrictive law.
2. Register global filters
2.1 General Conditions
Official ways to register filters:
1export default {
2 data () { return {} },
3 filters:{
4 orderBy (){
5 // doSomething
6 },
7 uppercase () {
8 // doSomething
9 }
10 }
11}
Copy the code
But for our projects, most of the filters should be used globally, not written in the component every time it is used. It is better to use global filters.
Official email ⁵ The way to register globally:
1// register 2vue. filter('my-filter', function (value) {3 // return value 4}) 5// getter, 6var myFilter = vue.filter ('my-filter')Copy the code
But it’s not pretty to write separately, so it can be pulled out into separate files.
2.2 optimization
We can pull it out to a separate file and use object.keys to register in the main.js entry
/src/common/filters.js
1let dateServer = value => value.replace(/(\d{4})(\d{2})(\d{2})/g, '$1-$2-$3')
2
3
4export { dateServer }
Copy the code
/src/main.js
1import * as custom from './common/filters/custom'
2Object.keys(custom).forEach(key => Vue.filter(key, custom[key]))
Copy the code
We can then happily use our defined global filters in other.vue files.
1<template> 2 <section class="content"> 3 <p>{{ time | dateServer }}</p> <! -- 2016-01-01 --> 4 </section> 5</template> 6<script> 7 export default { 8 data () { 9 return { 10 time: 20160101 11 } 12 } 13 } 14</script>Copy the code
3. Register global components
3.1 General Situation
Scenarios where components are needed:
1<template>
2 <BaseInput v-model="searchText" @keydown.enter="search" />
3 <BaseButton @click="search">
4 <BaseIcon name="search"/>
5 </BaseButton>
6</template>
7<script>
8 import BaseButton from './baseButton'
9 import BaseIcon from './baseIcon'
10 import BaseInput from './baseInput'
11 export default {
12 components: { BaseButton, BaseIcon, BaseInput }
13 }
14</script>
Copy the code
We wrote a bunch of basic UI components, but every time we needed to use them, we had to import them and then declare components, which was tedious, so we could use a unified registration.
3.2 optimization
We need to use the webpack magic to create our own module context using the require.context() and ⁶ method to implement the automatic dynamic require component. This method takes three parameters: the folder directory to search for, whether its subdirectories should also be searched, and a regular expression to match the files. We added a file called ComponentRegister.js to the Components folder, which uses WebPack to dynamically package all the required base components.
/src/components/ componentRegister .js
1import Vue from 'Vue' 2 3 4/** 5* Uppercase 6* @param STR String 7* @example heheHaha 8* @return {string} heheHaha 9*/ 10function capitalizeFirstLetter(str) { 11 return str.charAt(0).toUpperCase() + str.slice(1) 12} 13 14 15/** 16* To conform to the 'xx/xx. Vue' components format of the component name, 17 * @ param STR fileName 18 * @ example ABC/BCD/def/basicTable vue 19 * @ return {string} BasicTable 20*/ 21function validateFileName(str) { 22 return /^\S+\.vue$/.test(str) && 23 str.replace(/^\S+\/(\w+)\.vue$/, (rs, $1) => capitalizeFirstLetter($1)) 24} 25 26 27const requireComponent = require.context('./', true, /\.vue$/) 28 29 30// Find the.vue file in the component folder, if the file name is index, 31requireComponent.keys().foreach (filePath => {32 const componentConfig = requireComponent(filePath) 33 const fileName = validateFileName(filePath) 34 const componentName = fileName.toLowerCase() === 'index' 35 ? capitalizeFirstLetter(componentConfig.default.name) 36 : fileName 37 Vue.component(componentName, componentConfig.default || componentConfig) 38})Copy the code
Here’s the folder structure:
1components 2│ ├─BasicTable 4│ ├─MultiCondition 6│ ├.vue 3 components 3 │ ├─BasicTable 4│ ├─MultiCondition 6│ indexCopy the code
If the component name is index, the name attribute of the component is used as the registered component name. Therefore, the registered component is multi-condition and basic-table.
Finally, we import ‘Components/ComponentRegister.js’ from main.js, and we can use these base components anytime, anywhere, without having to import them manually.
4. Components of different routes are multiplexed
4.1 Scenario Restoration
In a scenario, vue-Router switches from /post-page/ A to /post-page/b. And then we were surprised to find that the data didn’t update after the page jumped? ! The reason is that vue-Router “intelligently” discovers that this is the same component and then decides to reuse it, so the method you wrote in the Created function never executes at all.
The usual solution is to listen for changes in $route to initialize the data as follows:
1data() { 2 return { 3 loading: false, 4 error: null, 5 post: null 6 } 7}, 8watch: { 9 '$route': {// Use watch to check whether the route is the same. 10 Handler: 'resetData', 11 immediate: true 12} 13}, 14methods: { 15 resetData() { 16 this.loading = false 17 this.error = null 18 this.post = null 19 this.getPost(this.$route.params.id) 20 }, 21 getPost(id){ } 22}Copy the code
4.2 optimization
This can be done by adding a different key to the router-view, so that even if it is a common component, the component will be recreated whenever the URL changes.
1<router-view :key="$route.fullpath"></router-view>
2
Copy the code
You can also add a + +new Date() timestamp to ensure that it is unique.
If a component is added to a created or Mounted hook, the value of Activated hook can be used to fetch new data.
5. Component event attribute penetration
5.1 General Conditions
1// parent component 2<BaseInput :value="value" 3 label=" password "4 placeholder=" password" 5 @input="handleInput" 6 @focus="handleFocus > 7</BaseInput> 8 9 10// Subcomponent 11<template> 12 <label> 13 {{label}} 14 < INPUT :value=" value" 15 :placeholder="placeholder" 16 @focus="$emit('focus', $event)" 17 @input="$emit('input', $event.target.value)"> 18 </label> 19</template>Copy the code
5.2 optimization
The props, props, props, and attrs of vue component instances provide great convenience, especially when passing values between parent and child components.
1. For every prop passed from a parent to a child, we need to explicitly declare it in the props of the child. That gives a lot of props to our child components every time, and a V-bind gives around objects. Vm-props props =”props props =” vM-bind =”props props =”props props =” vm-bind =”props props =”props props =” vm-bind =”props props =”
1<input v-bind="$props"
2 @input="$emit('input', $event.target.value)">
Copy the code
A DOM-native property like a placeholder can be passed directly from parent to child without showing it, using $attrs and ⁹. The method is as follows:
1<input :value="value"
2 v-bind="$attrs"
3 @input="$emit('input', $event.target.value)">
Copy the code
Attrs contains feature bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, it contains all bindings in the parent scope, and contains feature bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope by v−bind=”attrs “. When a component does not declare any prop, this contains all parent bindings, and v-bind=”attrs “contains feature bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, it contains all parent-scoped bindings and can be passed to the internal component by v−bind=”attrs”.
@focus=” emit(‘focus’, emit(‘focus’, emit(‘focus’, event)”;
1<input :value="value" 2 v-bind="$attrs" 3 v-on="listeners"/> 4 5 6computed: { 7 listeners() { 8 return { 9 ... this.$listeners, 10 input: event => 11 this.$emit('input', event.target.value) 12 } 13 } 14}Copy the code
Listeners to the ¹⁰ (native modifiers) are included in the parent scope of the V − ON method. It can be distinguished by V −on=” Listeners ¹⁰ “which contains v-ons in the parent scope (without the.native modifier). It can be distinguished by v-on=” Listeners to the ¹⁰ method in the parent scope (without the. Native modifier). Internal components can be passed in via V − ON =” Listeners “– useful for creating higher-level components.
Note that since our Input is not the root node of the component BaseInput, feature bindings in the parent scope that are not recognized as props by default will be “rolled back” and applied to the root element of the child component as normal HTML features. So we need to set the inheritAttrs: false to [¹²] stack, and these default behaviors will be removed for the optimization to succeed.
6. Routes are lazily loaded based on the development status
6.1 General Conditions
Normally when we load components in a route:
1import Login from '@/views/login.vue' 2 3 4export default new Router({ 5 routes: [{ path: '/login', name: 'Login ', Component: Login}] 6})Copy the code
Add () => import(‘@/views/login.vue’) routes component => import(‘@/views/login.vue’) routes component => import(‘@/views/login.vue’) routes component => import(‘@/views/login.vue’)
As your project pages grow, lazy-loading in your development environment becomes less appropriate, and every change in code that triggers hot updates becomes very slow. Therefore, it is recommended to use route lazy loading only in the build environment.
6.2 optimization
According to the ¹³ (Vue) asynchronous module and the code separation function of Webpack, ¹ ¹⁴ can easily achieve lazy loading of modules, such as:
1const Foo = () => import('./Foo.vue')
Copy the code
To distinguish between a development environment and a production environment, create two new files in the routing folder: _import_production.js
1module.exports = file => () => import('@/views/' + file + '.vue')
Copy the code
_import_development.js (vue-loader version at least v13.0.0)
1module.exports = file => require('@/views/' + file + '.vue').default
Copy the code
In the router/index.js file that sets the route:
1const _import = require('./_import_' + process.env.NODE_ENV) 2 3 4 5export default new Router({ 6 routes: [{ path: '/ login' name: 'log in' component: _import (' login/index ')}] 7})Copy the code
You can get PDF book source code, tutorials, etc., for free use click the link to join the group chat [Web front-end technology group] : jq.qq.com/?_wv=1027&k…)