1. Optimize the long list performance
In the 2.x version, Vue hijacks the data via object.defineProperty to enable bidirectional data binding. However, in some specific business scenarios, where the component only needs to display the data without any changes, we may not need Vue to hijack the incoming data. When large amounts of data need to be rendered, disabling Vue from hijacking data can significantly reduce component initialization time.
::: tip freezes an Object with the object.freeze method. Once an Object is frozen, it cannot be modified.
export default {
data: (a)= > ({
userList: []}),async created() {
const userList = await this.$service.get("/getuserList");
this.userList = Object.freeze(userList); }};Copy the code
2. Vue component rendering performance analysis
Based on the above example (long list performance optimization), a pure rendered list performance optimization can be achieved by using Object.Freeze, so how do you verify that?
We can detect this with Chrome Devtools. However, in order to obtain accurate performance analysis data, we need to enable the performance mode of Vue application.
Enable Vue performance mode (for development mode)
In the project main.js (before the Vue root instance is initialized), add the following code:
Vue.config.performance = true;
Copy the code
Of course, you can also determine whether to enable performance mode based on the current environment.
constisDev = process.env.NODE_ENV ! = ="production";
Vue.config.performance = isDev;
Copy the code
This activates Vue’s Timing API, which is used internally to mark component performance. As shown in the figure below:
Let’s say we’ve created a demo project and have a hello.vue component to verify long list rendering performance issues. After running the local project, open the browser to the specified route (make sure the hello.vue component is loaded). Open the console and click the “Reload” button, as shown below:
At this point, the page performance is logged. Since the vue.config.performance setting has been added to main.js, you should be able to see the timing section in the analysis. See the figure below.
At this point, you will notice that there are three indicators:
- Init, the time taken to create the component instance
- Render, the time it takes to create the vDOM structure
- Patch, the time it takes to render the vDOM structure into actual DOM elements
Verify performance
In this case, there are only two components under the http://localhost:8080/#/hello route:
App.vue
Hello.vue
Copy the code
App.vue is a view component with only one
Hello.vue just makes a simple long list (100,000 conditional data) display, as follows:
<template>
<div>
<span v-for="(item, idx) in users" :key="idx">
{{item.name}}
</span>
</div>
</template>
<script>
export default {
data () {
return {
users: []
}
},
components: {
},
created () {
let users = Array.from({ length: 100000 }, (item, index) => ({ name: index }))
this.users = users
}
}
</script>
Copy the code
In this case, the time for hello. vue component render&Patch is:
- render -> 924ms
- patch -> 1440ms
Modify hello. vue’s created hook function to look like this:
created () {
let users = Array.from({ length: 100000 }, (item, index) => ({ name: index }))
this.users = Object.freeze(users)
}
Copy the code
Click the “Reload” button again to retest the performance.
In this case, the time for hello. vue component render&Patch is:
- Render -> 390ms (last test result: 924ms, save time: 527ms, performance provides about 57%)
- Patch -> 782ms (last test result: 1440ms, time saved: 658ms, performance provided about: 45.7%)
It was only tested once, but the result shows that the overall performance of the Object. Freeze freeze was significantly improved.
3. Create a Store without Vuex (Vue.Observable)
Server new
- Parameter: {Object} Object
- Usage: make an object responsive. It is used internally by Vue to process objects returned by the data function.
The returned object can be used directly within rendering functions and computed properties, and will trigger an update when it changes. It can also be used as a minimal cross-component state store for simple scenarios:
const state = Vue.observable({ count: 0 })
const Demo = {
render(h) {
return h('button', {
on: { click: (a)= > { state.count++ }}
}, `count is: ${state.count}`)}}Copy the code
We can leverage this API for some simple cases where data state is shared across components.
// miniStore.js
import Vue from "vue";
export const miniStore = Vue.observable({ count: 0 });
export constactions = { setCount(count) { miniStore.count = count; }}export const getters = {
count: (a)= > miniStore.count
}
Copy the code
// Demo.vue
<template>
<div>
<p>count:{{count}}</p>
<button @click="add">+ 1</button>
<button @click="sub">- 1</button>
</div>
</template>
<script>
import { actions, getters } from "./store";
export default {
name: "App".computed: {
count() {
returngetters.count; }},methods: {
add: actions.setCount(this.count+1),
sub: actions.setCount(this.count- 1)}};</script>
Copy the code
4. Attribute & event passing
When writing a Vue component, you often encounter:
- Component layer transfer
props
orlisterers
- Dynamic binding
props
orlisterers
Is there any way to solve these two scenarios?
::: Tip V-bind and V-ON can be implemented to solve the above problems :::
The following is a code example:
<template>
<Child v-bind="$props" v-on="$listeners"> </Child>
</template>
<script>
import Child from "./Child";
export default {
props: {
title: {
required: true.type: String
}
}
components: {
Child
}
};
</script>
Copy the code
5. Listen to function lifecycle functions
Sometimes, you need to do some logical processing after the parent component listens to the mounted child component. For example, when a remote component is being loaded, you want to capture the time it takes for the component to be loaded and mounted from the remote end.
At this point, it is no longer possible to write this.$emit event in each subcomponent as normal. Is there a way to just listen for the lifecycle hook functions of each child in the parent?
::: tip @hook can listen to the child component lifecycle hook function (created, updated, etc.). @hook:mounted=”doSomething” :::
// Parent.vue <template> <Child v-bind="$props" v-on="$listeners" @hook:mounted="doSomething"> </Child> </template> <script> import Child from "./Child"; export default { props: { title: { required: true, type: String } } components: { Child }, methods: { doSomething(){ console.log("child component has mounted!" ); }}}; </script>Copy the code
6. Functional components
::: TIP a functional component that is stateless, cannot be instantiated, does not have any internal lifecycle handling, is lightweight, and therefore has high rendering performance. It is especially suitable for components that depend on external data transfers to change. : : :
It is written as follows:
- In the template tag
functional
- Only props are accepted
- No script tag is required
<! -- App.vue -->
<template>
<div>
<UserList :users="users" :click-handler="clickHandler.bind(this)"></UserList>
</div>
</template>
<script>
import UserList from "./UserList";
export default {
name: "App".data: (a)= > {
users: ['james'.'ian']
}
components: { UserList },
methods: {
clickHandler(name){
console.log(`clicked: ${name}`); }}};</script>
Copy the code
// UserList.vue
<template functional>
<div>
<p v-for="(name, idx) in props.users" @click="props.clickHandler(name)" :key="idx">
{{ name }}
</p>
</div>
</template>
Copy the code
7. Scope slots
In 2.6.0, Vue introduced a new uniform syntax for named slots and scoped slots (the V-slot directive). It replaces slot and Slot-Scope, two features that are currently deprecated but not removed and are still in documentation. The origin of the new syntax can be found in this RFC.
A simple example
How do you use scoped slots? Look at the following example:
<template>
<List :items="items">
<template slot-scope="{ filteredItems }">
<p v-for="item in filteredItems" :key="item">{{ item }}</p>
</template>
</List>
</template>
Copy the code
With a V-slot, you can write the scope of the slot directly to the component label.
<template>
<List v-slot="{ filteredItems }" :items="items">
<p v-for="item in filteredItems" :key="item">{{ item }}</p>
</List>
</template>
Copy the code
::: Tip V-slots can only be used on component or Template tags. They cannot be used on normal native HTML tags. :::
This makes the code more readable, especially in scenarios where it is difficult to specify the source of a template variable.
V-slot Advanced use
The V-slot directive also introduces a way to combine slot&scoped-slots, but requires a “:” to separate them.
<template>
<Promised :promise="usersPromise">
<p slot="pending">Loading...</p>
<ul slot-scope="users">
<li v-for="user in users">{{ user.name }}</li>
</ul>
<p slot="rejected" slot-scope="error">Error: {{ error.message }}</p>
</Promised>
</template>
Copy the code
Rewrite with V-slot:
<template>
<Promised :promise="usersPromise">
<template v-slot:pending>
<p>Loading...</p>
</template>
<template v-slot="users">
<ul>
<li v-for="user in users">{{ user.name }}</li>
</ul>
</template>
<template v-slot:rejected="error">
<p>Error: {{ error.message }}</p>
</template>
</Promised>
</template>
Copy the code
V-slot can also be abbreviated to #, overriding the example above:
<template>
<Promised :promise="usersPromise">
<template #pending>
<p>Loading...</p>
</template>
<template #default="users">
<ul>
<li v-for="user in users">{{ user.name }}</li>
</ul>
</template>
<template #rejected="error">
<p>Error: {{ error.message }}</p>
</template>
</Promised>
</template>
Copy the code
::: tip Note that v-slot is short for #default :::
8. watch
While vue.js gives us a useful computed, there are some scenarios where watch is still needed.
::: tip By default, watch is executed only when the value of the property being listened on has changed. :::
Such as:
export default {
data: (a)= > ({
dog: ""
}),
watch: {
dog(newVal, oldVal) {
console.log(`Dog changed: ${newVal}`); }}};Copy the code
As shown in the code above, the dog function in watch only executes when the dog value has changed.
However, in some cases, you may want to run the listener immediately after creating the component. Sure, you could migrate the logic into methods and call it from both watch and created hook functions, but what’s a simpler way to do it?
You can use the immediate: true option when using watch so that it executes immediately when the component is created.
export default {
data: (a)= > ({
dog: ""
}),
watch: {
dog: {
handler(newVal, oldVal) {
console.log(`Dog changed: ${newVal}`);
},
immediate: true}}};Copy the code
9. Image loading is lazy
V-lazy-image Indicates that the image is loaded lazily.
Install: NPM install v-lazy-image
Use:
// main.js
import Vue from "vue";
import { VLazyImagePlugin } from "v-lazy-image";
Vue.use(VLazyImagePlugin);
Copy the code
<template>
<v-lazy-image src="http://lorempixel.com/400/200/" />
</template>
Copy the code
You can also use progressive image loading, with src-placeholder to load the thumbnails first, and CSS to apply its own filters.
<template>
<v-lazy-image
src="http://demo.com/demo.jpeg"
src-placeholder="http://demo.com/min-demo.jpeg"
/>
</template>
<style scoped>
.v-lazy-image {
filter: blur(10px);
transition: filter 0.7 s;
}
.v-lazy-image-loaded {
filter: blur(0);
}
</style>
Copy the code
10.. Sync modifier
2.3.0 + added
In some cases, we may need to “bi-bind” a prop. Unfortunately, true bidirectional binding can cause maintenance problems because the child can modify the parent and there is no obvious source of change on either parent or child.
This is why we recommend the pattern trigger event of Update :myPropName instead.
For example, in a component that contains the prop property title, we can express the intent to assign a new value to it in the following way:
this.$emit('update:title', newTitle)
Copy the code
The parent component can then listen for that event and update a local data property as needed. Such as:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
Copy the code
For convenience, we provide an abbreviation for this pattern, the.sync modifier:
<text-document v-bind:title.sync="doc.title"></text-document>
Copy the code
::: danger V-bind with the.sync modifier cannot be used with expressions.
For example, v-bind:title.sync= “doc.title + ‘! ‘” is invalid.
Instead, you can only provide the name of the property you want to bind to, similar to v-Model. : : :
We can also use the.sync modifier in conjunction with v-bind when setting multiple prop’s on the same object:
<text-document v-bind.sync="doc"></text-document>
Copy the code
This passes each property in the Doc object, such as title, as a separate prop, and then adds a v-on listener for updates.
Pay attention to
Use v-bind. Sync on a literal object.
For example, v-bind.sync= “{title: doc.title}” does not work.
Because when you’re parsing a complex expression like this, there are a lot of edge cases to consider.
11. provide / inject
2.2.0 new
Type:
- Dojo.provide: Object | () = > Object
- Inject: Array | {[key: string] : string | Symbol | Object}
Note: Provide and Inject mainly provide use cases for higher-level plug-in/component libraries. Not recommended for use directly in application code.
This pair of options needs to be used together to allow an ancestor component to inject a dependency into all of its descendants, regardless of how deep the component hierarchy is, for as long as the upstream and downstream relationships are established. If you’re familiar with React, this is very similar to the context properties of React.
The provide option should be an object or a function that returns an object. This object contains properties that can be injected into its descendants. You can use ES2015 Symbols as a key in this object, but this only works in an environment that natively supports Symbol and reflect.ownKeys.
The inject option should be:
- An array of strings or
- An object whose key is the local binding name and whose value is:
- Search for the key (string or Symbol) used in the available injection, or
- An object whose:
- The from attribute is the key (string or Symbol) to search for in the available injected content
- The default attribute is the value used in the case of degradation
Tip: Provide and Inject bindings are not responsive. This is deliberate. However, if you pass in an object that can be listened to, then the object’s properties are still responsive.
Example:
// The parent component provides 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// Child component to inject 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
Copy the code
Use ES2015 Symbols, function provide and object inject:
const s = Symbol(a)const Provider = {
provide () {
return {
[s]: 'foo'}}}const Child = {
inject: { s },
// ...
}
Copy the code
Injection in 2.5.0+ can be made optional by setting the default:
const Child = {
inject: {
foo: { default: 'foo'}}}Copy the code
If it needs to be injected from a property with a different name, use from to denote its source property:
const Child = {
inject: {
foo: {
from: 'bar'.default: 'foo'}}}Copy the code
Similar to the default value for prop, you need to use a factory method for non-raw values:
const Child = {
inject: {
foo: {
from: 'bar'.default: (a)= > [1.2.3]}}}Copy the code
Debug Vue Template
During Vue development, it is common for JavaScript variables to fail while the Template is being rendered. In this case, you may want to use console.log for debugging. Such as:
<template>
<h1>
{{ log(message) }}
</h1>
</template>
<script>
methods: {
log(message) {
console.log(message); }}</script>
Copy the code
Every time I debug the template rendering, I have to repeat this, which may be very boring, is there a better way?
Add a custom method to the vue. prototype chain.
// main.js
Vue.prototype.$log = window.console.log;
Copy the code
In the end, we can use $log in each component’s template, or if we don’t want to affect the rendering of the template:
<h1>
{{ log(message) || message }}
</h1>
Copy the code
Is this a convenient debugging template?
So, by extension, is there any way to add a breakpoint to debug the template rendering to see the associated variables? We put a debugger in when we use the template.
<h1>
{{ debugger }}
</h1>
Copy the code
You’ll notice that the component doesn’t compile the template at all. Is there a way?
We can try adding a self-executing function to the template, for example:
<h1>{{ (function(){degugger; }) || message }}</h1>
Copy the code
At this point, you should see that the breakpoint is located in the template’s render function.
The _VM, in this case, is the instance object of our component.
While it is interesting to examine compiled templates, for some reason variables are not available in the chrome DevTools function range after we put them in the debugger.
Modify the following writing:
<h1>
{{ (function(){degugger; message}) || message }}
</h1>
Copy the code
At this point, you can do whatever you want.
Scoped for Vue components
The scoped property of the style tag in Vue means that its style applies only to the current module. It privatizes styles and is designed to make them immutable.
Rules/principles of rendering:
- Add a unique data attribute to the HTML DOM node to indicate uniqueness
- To privatize styles, add a data property selector for the current component at the end of the corresponding CSS selector, for example:.demo[data-V-2311c06a]{}
- If a component contains other components, only the outermost label of the other component is appended with the current component’s data-v attribute
For example, the following code looks like this:
<template>
<div class="demo">
<span class="content">
Vue.js scoped
</span>
</div>
</template>
<style lang="less" scoped>.demo{ font-size: 14px; .content{ color: red; }}</style>
Copy the code
Browser rendered code:
<div data-v-fed36922>
Vue.js scoped
</div>
<style type="text/css">
.demo[data-v-039c5b43] {
font-size: 14px;
}
.demo .content[data-v-039c5b43] {
color: red;
}
</style>
Copy the code
::: tip Note that after adding the scoped property, the parent component cannot change the style of the child component :::
14. Deep selector for Vue component style
In the example above, what if you want to change the style of the child component in the parent component?
- 1. Mix the global and local attributes
- 2. Each component adds a unique class in the outermost layer to distinguish the different components
- 3. Use deep selector
Here we focus on using deep to change the style of child components. Change the code in the above example to:
<template>
<div class="demo">
<span class="content">
Vue.js scoped
</span>
</div>
</template>
<style lang="less" scoped>.demo{ font-size: 14px; } .demo /deep/ .content{ color: blue; }</style>
Copy the code
The final compiled output of style is:
<style type="text/css">
.demo[data-v-039c5b43] {
font-size: 14px;
}
.demo[data-v-039c5b43] .content {
color: blue;
}
</style>
Copy the code
The difference between the CSS attribute data-v-xxx and the CSS attribute selector weight is that the CSS attribute is the same as the CSS attribute.
15. Vue component local style Modules
CSS Modules is a popular system for modularizing and combining CSS. Vue-loader provides first-class integration with CSS Modules and can be used as an alternative to emulated scoped CSS.
usage
First, CSS Modules must be turned on by passing Modules: true to the CSS-loader:
// webpack.config.js
{
module: {
rules: [
/ /... Other rules omitted
{
test: /\.css$/.use: [
'vue-style-loader',
{
loader: 'css-loader'.options: {
// Open CSS Modules
modules: true.// Customize the generated class name
localIdentName: '[local]_[hash:base64:8]'}}}}Copy the code
Then add the Module feature to your
<style module>
.red {
color: red;
}
.bold {
font-weight: bold;
}
</style>
Copy the code
This Module feature instructs the Vue Loader to inject CSS Modules local objects into the component as a computing property named $style. You can then use it in a template with a dynamic class binding:
<template>
<p :class="$style.red">
This should be red
</p>
</template>
Copy the code
Since this is a computed property, it also supports the object/array syntax of :class:
<template>
<div>
<p :class="{ [$style.red]: isRed }">
Am I red?
</p>
<p :class="[$style.red, $style.bold]">
Red and bold
</p>
</div>
</template>
Copy the code
You can also access it via JavaScript:
<script>
export default {
created () {
console.log(this.$style.red)
// -> "red_1VyoJ-uZ"
// An identifier generated based on the filename and class name}}</script>
Copy the code
You can check out the CSS Modules specification for more details, such as Global Exceptions and composition.
Can choose method
If you only want to use CSS Modules in certain Vue components, you can use the oneOf rule and check the Module string in the resourceQuery string:
// webpack.config.js -> module.rules
{
test: /\.css$/.oneOf: [
// Here matches'
{
resourceQuery: /module/.use: [
'vue-style-loader',
{
loader: 'css-loader'.options: {
modules: true.localIdentName: '[local]_[hash:base64:5]'}}},// Here matches the normal '
{
use: [
'vue-style-loader'.'css-loader']]}}Copy the code
Use in conjunction with the preprocessor
CSS Modules can be used with other preprocessors:
// webpack.config.js -> module.rules
{
test: /\.scss$/.use: [
'vue-style-loader',
{
loader: 'css-loader'.options: { modules: true}},'sass-loader']}Copy the code
Custom injection name
You can define more than one
<style module="a">
/* Inject identifier a */
</style>
<style module="b">
/* Inject the identifier b */
</style>
Copy the code
16. Always verify Props
Verifying Props is a basic practice in Vue. You probably know how to do some primitive validation, such as String, Number, etc. Of course, you can also customize validation functions, such as:
props: {
name: {
type: String.required: true.validator: function(value) {
return [
'Joe'.'bill'.'Cathy'].indexOf(value) ! = =- 1}}}Copy the code
17. Customize the V-Model
By default, v-Model is the syntactic sugar for the @Input event listener and the :value attribute. However, you can also specify a model attribute in your Vue component to define what events and values are.
export defaut {
model: {
event: 'change'.prop: 'checked'}}Copy the code
18. Dynamic instruction parameters
2.6+ Added Vue 2.6 to dynamically pass directive parameters to components. For example, if you have a button component that in some cases needs to listen for click events and in some cases needs to listen for double-click events, consider writing this:
<template>
<div>
<div>hellojames</div>
<button v-on:[eventType]="eventHandler($event)">xxx</button>
</div>
</template>
<script>
export default {
data () {
return {
someCondition: 1
}
},
computed: {
eventType () {
return this.someCondition ? 'click' : 'dblclick'
}
},
methods: {
eventHandler ($event) {
// todo something
window.console.log($event)
}
}
}
</script>
Copy the code
This way, you can apply the same pattern to dynamic HTML attributes, props, etc
A link to the
- Vue
- VueDose
- Posts About Vue.js
- Vue-loader