1, multi-chart resize event to the center
1.1 General situation
Sometimes we encounter such a scenario, there are several components of a chart in the browser when we want to resize the chart also resize, so we will write in the parent container assembly:
mounted() {
setTimeout(() => window.onresize = () => {
this.$refs.chart1.chartWrapperDom.resize()
this.$refs.chart2.chartWrapperDom.resize()
// ...
}, 200)
destroyed() { window.onresize = null }
If you like this chart component with parent container components are not in a page, subassemblies status was put parent component management, in order to facilitate maintenance, we naturally want the event and state subcomponents maintained by yourself, so add remove components when you do not need to modify one by one parent component
1.2 Optimization
This uses lodash throttle throttle function, you can also achieve their own, this article also throttling of reference. In Echarts an example, the graph in each component:
computed: {
/**
* Charts DOM
*/
chartWrapperDom() {
const dom = document.getElementById('consume-analy-chart-wrapper')
return dom && Echarts.init(dom)
},
/**
* Charts resize throttle, used here lodash, you can also use your own setTimout throttling
*/
chartResize() {
return _.throttle(() => this.chartWrapperDom && this.chartWrapperDom.resize(), 400)
}
},
mounted() {
window.addEventListener('resize', this.chartResize)
},
destroyed() {
window.removeEventListener('resize', this.chartResize)
}
1.3 Optimization again
Here because multiple chart instances use the same set of initialization logic, you can use extends to consider reuse, so I think Mixins Vue offers, so here I made a point of optimization, you can make the same type of chart components each more elegant point: a new mixin.js file:
import Echarts from 'echarts'
import _ from 'lodash'
export default {
computed: {
/ * Charts DOM * /
$_chartMixin_chartWrapperDom() {
const dom = document.getElementById(this.thisDomId)
return dom && Echarts.init(dom)
},
/ ** charts resize throttle, used here lodash, you can also use your own setTimout throttling * /
$_chartMixin_chartResize() {
return _.throttle(() => this.$_chartMixin_chartWrapperDom.resize(), 400)
}
},
methods: {
/ * Initialize chart * /
$ _ChartMixin_initChart () {
this.$_chartMixin_chartWrapperDom.setOption({ /* options */ }
},
mounted() {
this.$_chartMixin_initChart()
window.addEventListener('resize', this.$_chartMixin_chartResize)
},
destroyed() {
window.removeEventListener('resize', this.$_chartMixin_chartResize)
}
}
Then in each chart components:
<script type='text/javascript'>
import ChartMixin from './mixin'
export default {
mixins: [ChartMixin],
data() {
return {
thisDomId: 'consume-analy-chart-wrapper'
}
}
}
</script>
This graph may be incorporated in each assembly prior to resize event logic mixin.js defined, and the automatic initialization and self-destruct when the event is destroyed -
2, global filters registration
2.1 General situation
Official registration filter method:
export default {
data () { return {} },
filters:{
orderBy (){
// doSomething
},
uppercase () {
// doSomething
}
}
}
But we do the project, most of the filter is to be used globally, it is not often used to write the components inside, pumped into the global would be better. Official registration of the global fashion:
// Register
Vue.filter('my-filter', function (value) {
// return value after treatment
})
// getter, returns the registered filter
var myFilter = Vue.filter ( 'my-filter')
But then dispersed write unsightly, it can be extracted into separate files.
2.2 Optimization
We could draw off into a separate file and then use Object.keys unified registration at the entrance main.js
/src/common/filters.js
let dateServer = value => value.replace(/(d{4})(d{2})(d{2})/g, '$1-$2-$3')
export { dateServer }
/src/main.js
import * as custom from './common/filters/custom'
Object.keys(custom).forEach(key => Vue.filter(key, custom[key]))
In other then you can happily use them we defined a global filter file .vue
<template>
<section class="content">
<p>{{ time | dateServer }}</p> <!-- 2016-01-01 -->
</section>
</template>
<script>
export default {
data () {
return {
time: 20160101
}
}
}
</script>
3, the global component registration
3.1 General situation
It requires the use of components of the scene:
<template>
<BaseInput v-model="searchText" @keydown.enter="search"/>
<BaseButton @click="search">
<BaseIcon name="search"/>
</ BaseButton>
</template>
<script>
BaseButton import from "./baseButton"
import BaseIcon from './baseIcon'
import BaseInput from './baseInput'
export default {
components: {BaseButton, silicon, BaseInput}
}
</script>
We wrote a bunch of basic UI components, then every time we need to use these components, had to import, and then declare components, cumbersome, can be used here in the form of a unified registration
3.2 Optimization
We need to use what artifact webpack, use require.context () method to create their own modules context, in order to achieve the automatic dynamic require assembly. This method takes three parameters: the file folder you want to search the directory, whether it should also search subdirectories, as well as being a regular expression matching files. We add a file called componentRegister.js folder of files in components, with webpack dynamic in this document will be the basis of all package components that need to come in.
/src/components/componentRegister.js
Import view from 'View'
/**
* Capitalized
* @Param str String
* @example heheHaha
* <a href="http://www.jobbole.com/members/wx1409399284">@return</a> {string} HeheHaha
*/
function capitalizeFirstLetter(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
/**
* To comply 'xx / xx.vue' component assembly is removed name component format
* @param str fileName
* @example abc/bcd/def/basicTable.vue
* <a href="http://www.jobbole.com/members/wx1409399284">@return</a> {string} BasicTable
*/
function validateFileName(str) {
return /^S+.vue$/.test(str) &&
str.replace(/^S+/(w+).vue$/, (rs, $1) => capitalizeFirstLetter($1))
}
const requireComponent = require.context('./', true, /.vue$/)
// Locate the file named .vue Components folder under the component name registration if the file name index, then take the name as a component
requireComponent.keys().forEach(filePath => {
const componentConfig = requireComponent(filePath)
const fileName = validateFileName(filePath)
const componentName = fileName.toLowerCase() === 'index'
? capitalizeFirstLetter(componentConfig.default.name)
: fileName
Vue.component(componentName, componentConfig.default || componentConfig)
})
Here folder structure:
components
│ componentRegister.js
├─BasicTable
│ BasicTable.vue
├─MultiCondition
│ index.vue
Here the judgment made on the component name, if index would then take the name attribute of the processing components as the component name registration, so the final assembly registered as: multi-condition, basic-table at the end we main.js the import 'components /componentRegister.js', then we can use whenever and wherever these basic components, without having to manually introduced ~
4, component reuse different routes
4.1 Restore scene
When a scene vue-router from / post-page / a, jump to / post-page / b. Then our amazing discovery, after the page jump did not even update the data? ! The reason is vue-router "intelligently" found it to be the same component, then it is decided to reuse this component, so you write in the created function in the method never even execution. The usual solution is to monitor changes in $ route to initialize data, as follows:
data() {
return {
loading: false,
error: null,
post: null
}
},
watch: {
'$ route': { // Use watch to monitor whether the same route
Trades, 'reset data'
immediate: true
}
},
methods: {
resetData() {
this.loading = false
this.error = null
this.post = null
this.getPost(this.$route.params.id)
},
getPost(id){ }
}
4.2 Optimization
In order to achieve this effect you can add a different key router-view, so that even the common components, as long as the url changed, they will re-create this component.
<router-view :key="$route.fullpath"></router-view>
You may also be added thereafter + + new Date () stamp for unique
Thanks friends remind @rolitter, if the component is placed <keep-alive> in, you can get the new data on the activated hook method, instead of the original mission, to get data in created mounted hooks.
5, components penetrate event properties
5.1 General situation
// parent component
<BaseInput :value="value"
label = "password"
placeholder = "Please fill in the password"
@input="handleInput"
@focus="handleFocus">
</BaseInput>
// Subassembly
<template>
<label>
{{ label }}
<input :value=" value"
:placeholder="placeholder"
@focus="$emit('focus', $event)"
@input="$emit('input', $event.target.value)">
</label>
</template>
5.2 Optimization
Vue component instance in the $ props, $ attrs provides us with a great convenience, especially when the father and son by value components. 1, each sub-assembly passed a props from the parent component, we have to explicitly declare Props to use in the sub-assembly. As a result, we need to affirm sub-assemblies each time a lot of props, where we know that v-bind can pass an object, you can get all the props parent component of the value of v-bind in vm. $ Props in = " $ props "
<input v-bind="$props"
@input="$emit('input', $event.target.value)">
2, which is similar to placeholer dom native property can be reached using the $ attrs child, no direct statement from the parent. Methods as below:
<input :value="value"
v-bind="$attrs"
@input="$emit('input', $event.target.value)">
$ Attrs contains parent scope not recognized as a prop (and obtain) the binding properties (except for class and style). When a component does not declare any prop, where the binding will contain all the parent scope, and can be v-bind = "$ attrs" incoming internal components.
3, note that subcomponent @focus = "$ emit ( 'focus', $ event)" In fact, nothing to do, just put the event back to the parent component only, and that in fact, similar to the above, absolutely no need to explicitly stated:
<input :value="value"
v-bind="$attrs"
v-on="listeners"/>
computed: {
listeners() {
return {
...this.$listeners,
input: event =>
this.$emit('input', event.target.value)
}
}
}
$ Listeners contains the parent scope (excluding .native decorator) v-on event listener. It can be v-on = "$ listeners" Incoming internal components - very useful when creating a higher level of assembly.
It should be noted that, due to our input is not the root BaseInput this component, and the default characteristics are not recognized as props in the case of parent scope bindings will "back" and as an ordinary HTML attributes used in the subassembly the root element. So we need to set inheritAttrs: false, the default behavior will be removed, the above optimization to be successful.
6, according to the route state of development lazily
6.1 General situation
Generally, we loaded components in route time:
import Login from '@/views/login.vue'
export default new Router({
routes: [{ path: '/login', name: '登陆', component: Login}]
})
When you need lazy loading , when the lazy-loading, requires a component to one of the routes () => import ( '@ / views / login.vue'), very troublesome.
When more and more of your project page, use in the development environment lazy-loading will become less suitable, each time you change the code to trigger hot update will become very slow. It is recommended that only routing lazy loading functionality in the build environment.
6.2 Optimization
The lazy loading code division function Webpack asynchronous component and may easily Vue the assembly, such as:
const Foo = () => import('./Foo.vue')
In distinguishing between development and production environments, you can create two files are in the routing folder:
_import_production.js
module.exports = file => () => import('@/views/' + file + '.vue')
_import_development.js (The wording vue-loader version least v13.0.0 above)
module.exports = file => require('@/views/' + file + '.vue').default
In setting up routes router / index.js file:
const _import = require('./_import_' + process.env.NODE_ENV)
export default new Router({
routes: [{ path: '/login', name: '登陆', component: _import('login/index') }]
})
Such components in a development environment that is non-lazy loading, in a production environment is a lazy loading
7, vue-loader tips
vue-loader is processing * .vue file webpack loader. Which itself provides a rich API, API and some very practical but very few people are familiar with. For example preserveWhitespace and transformToRequire next to introduce
7.1 with a reduced file size preserveWhitespace
Sometimes we do not want to write a template when there are spaces between such elements may be written and elements:
<ul>
<li>1111</li><li>2222</li><li>333</li>
</ul>
Of course there are other ways, such as setting the font of font-size: 0, then the content needs to set the font size alone, the purpose is to remove the space between the elements. In fact, we can achieve this requirement by configuring vue-loader.
{
view: {
preserveWhitespace: false
}
}
Its role is to prevent the generation gap between element content, the use _v compiled Vue template ( "") represents. If the project template content and more, they will still take up some of the file size. Element configuration example after the property, when the compressed volume file is not reduced by nearly 30Kb.
7.2 transformToRequire no longer have to write the picture variables
In writing before the time of Vue often writes code like this: the picture in advance require to pass a variable and then to the assembly.
<template>
<div>
<avatar :default-src="DEFAULT_AVATAR"></avatar>
</div>
</template>
<script>
export default {
created () {
this.DEFAULT_AVATAR = require('./assets/default-avatar.png')
}
}
</script>
In fact, by arranging the transformToRequire, you can be configured directly, after such vue-loader will automatically require to pass attributes corresponding components
{
view: {
transformToRequire: {
avatar: ['default-src']
}
}
}
So we can simplify a lot of code
<template>
<div>
<avatar default-src="./assets/default-avatar.png"></avatar>
</div>
</template>
In the case webpack template vue-cli, the default configuration is:
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
You can draw inferences about the conduct similar configuration
vue-loader and many useful API such as the recent addition of custom blocks, you may be interested to look for documentation.
8, render function
In some scenarios you may need to render to render fully programmable function capability to solve the problems brought about is not easy to solve, in particular, to dynamically generate labels and choose the component type scene.
8.1 Dynamic Label
1. General
According to such props to generate a scene tag
<template>
<div>
<div v-if="level === 1"> <slot></slot> </div>
<p v-else-if="level === 2"> <slot></slot> </p>
<h1 v-else-if="level === 3"> <slot></slot> </h1>
<h2 v-else-if="level === 4"> <slot></slot> </h2>
<strong v-else-if="level === 5"> <slot></slot> </stong>
<textarea v-else-if="level === 6"> <slot></slot> </textarea>
</div>
</template>
Wherein the level of the data is variable, there can be seen a lot of code repetition, if more complex logic, plus some bind more complicated and is determined herein may be utilized to render function is determined to be a tag to be generated.
2. Optimize
Use render method to generate where the corresponding tag according to the parameters above can be avoided.
<template>
<div>
<child :level="level">Hello world!</child>
</div>
</template>
<script type='text/javascript'>
Import view from 'View'
Vue.component('child', {
render(h) {
const tag = ['div', 'p', 'strong', 'h1', 'h2', 'textarea'][this.level]
return h(tag, this.$slots.default)
},
props: {
level: { type: Number, required: true }
}
})
export default {
name: 'hehe',
data() { return { level: 3 } }
}
</script>
Examples can be viewed CodePen (https://codepen.io/SHERlocked93/pen/mLEJPE)
8.2 Dynamic Components
Of course render function, there are many uses, such as to use dynamic components, in addition to use: outside is also possible to use render function
<template>
<div>
<button @click='level = 0'>嘻嘻</button>
<button @click='level = 1'>哈哈</button>
<hr>
<child :level="level"></child>
</div>
</template>
<script type='text/javascript'>
Import view from 'View'
import Xixi from './Xixi'
import Haha from './Haha'
Vue.component('child', {
render(h) {
const tag = ['xixi', 'haha'][this.level]
return h(tag, this.$slots.default)
},
props: { level: { type: Number, required: true } },
components: { Xixi, Haha }
})
export default {
name: 'hehe',
data() { return { level: 0 } }
}
</script>
Examples can be viewed CodePen (https://codepen.io/SHERlocked93/pen/YLWwxM)
Most online posts shades, and even some contradictory, the following articles are summarized in the process of learning, if error, found welcome message pointed out -
reference:
- Vue2 global filter (vue-cli)
- Vue.js Best Practices
- webpack document - require.context
- Use webpack of require.context implement routing "decentralized" management
- vue-element-admin documentation
- Vue.js practical tips
- Optimization of the page to open the speed, you do not look ~