Preface
During this period, I used ts and vue to build a project. The project started from 0. While building and optimizing, I realized many of my own ideas. One or two components may seem meaningful to me, so looking back at the beginning can also make a record. If you have not used ts, you can passBuild todoList using Vue Cli3 + TypeScript + Vuex + JestThis article starts your ts journey, and the subsequent code is also improved on the structure of todoList
Lazy loading in vue route
Have you really used lazy loading of the route?
In the document and the initialization project of cli, a routing file will be generated by default, roughly as follows:
{ path: '/about', name: 'about', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ './views/') }
Components that are lazy loaded through routing will generate a dist/js/about. file named about after webpack is packaged.
But in react,react-loadable This can cause the route to load another component (usually loading) before lazy loading.
A higher order component for loading components with promises.
In fact, this is the high-order component (HOC) of react. So according to the idea of HOC, can we also implement such a HOC in vue? The answer isYES
Let's take a lookOfficial introduction:
const AsyncComponent = () => ({ // The component to load (should be a Promise) component: import('./'), // A component to use while the async component is loading loading: LoadingComponent, // A component to use if the load fails error: ErrorComponent, // Delay before showing the loading component. Default: 200ms. delay: 200, // The error component will be displayed if a timeout is // provided and exceeded. Default: Infinity. timeout: 3000 })
This new 2.3+ feature makes our beginning possible. We create a higher-order component, use the render function to generate the component and return it.
import LoadingComponent from './'; export default (component: any) => { const asyncComponent = () => ({ component: component(), loading: LoadingComponent, delay: 200, timeout: 3000 }); return { render(h: any) { return h(asyncComponent, {}); } }; };
Use this component in routes
import loadable from './loadable'; const routes = [ { path: '/about', name: 'about', // component: () => import(/* webpackChunkName: "about" */ './views/') component: loadable( () => import(/* webpackChunkName: "about" */ './views/') } ]
It seems to have succeeded, but there are still problems here.
Regarding vue-router, it is inevitable that the hook function of routing will be involved, but in the above usage, the routing hook is invalid, why?
The routing hook is only directly effective on components registered on the route.
Then the About component generated and rendered by loadable is already a child component, so the routing hook cannot be obtained.
Components must ensure robustness in use. Let’s change the solution and return this component directly.
const asyncComponent = importFunc => () => ({ component: importFunc(), loading: LoadingComponent, delay: 200, timeout: 3000 });
Let's replace routes again:
const routes = [ { path: '/about', name: 'about', // component: () => import(/* webpackChunkName: "about" */ './views/') component: asyncComponent( () => import(/* webpackChunkName: "about" */ './views/') } ]
The above usage has solved the problem of routing hooks, but there are still two points worth noting:
- The parameter accepted by asyncComponent is a function. If written directly as import(/* webpackChunkName: "about" */ './views/'), then LoadingComponent cannot take effect.
- AsyncComponent can also add an error component to form a logical closed loop.
The most elegant usage of SVG and Iconfont in vue projects
Try not to use pictures where svg can be used. When I use svg, I used them at the beginning.vue-svg-loader, Please check the specific usage yourself.
However, when writing sidebar, I want to write svg into the configuration file to allow sidebar to form multi-layer automatic rendering.
Obviously, the usage of vue-svg-loader is not appropriate. Let’s first understand the usage of svg, we can read a post about Naifu:SVG Icon Introduction。
SVG symbol ,Symbols let you define an SVG image once, and reuse it in multiple places.
Similar to Sprite's principle, multiple svgs can be synthesized into one, but here id is used to semantic positioning icons.
// Definition<svg class="hidden"> <symbol viewBox="0 0 20 20"> <rect x="0" y="0" width="300" height="300" fill="rgb(255,159,0)" /> </symbol> <symbol viewBox="0 0 20 20"> <rect x="0" y="0" width="300" height="300" fill="rgb(255,159,0)" /> </symbol> </svg> // use<svg> <use xlink:href="#rectangle-1" rel="external nofollow" href="#rectangle" rel="external nofollow" /> </svg>
There is a webpack loader, svg-sprite-loader for Sprite, below is the code
First, modify the configuration according to the official website:
// const svgRule = ('svg'); // Clear all loaders already available. // If you don't do this, the next loader will be attached to the existing loader for that rule. (); (/node_modules/); // Add the loader to be replaced // ('vue-svg-loader').loader('vue-svg-loader'); svgRule .test(/\.svg$/) .pre() .(/\/src\/icons/) .end() .use('svg-sprite-loader') .loader('svg-sprite-loader') .options({ symbolId: 'icon-[name]' }); const imagesRule = ('images'); (resolve('src/icons')); ('images').test(/\.(png|jpe?g|gif|svg)(\?.*)?$/);
Create the ICON folder, and then create the components in the folder.
<template> <svg v-show="isShow" :class="svgClass" aria-hidden="true"> <use :xlink:href="iconName" rel="external nofollow" /> </svg> </template> <script lang="ts"> import { Component, Vue, Prop } from 'vue-property-decorator'; @Component export default class SvgIcon extends Vue { @Prop({ required: true }) private readonly name!: string; @Prop({ default: () => '' }) private readonly className!: string; private get isShow() { return !!; } private get iconName() { return `#icon-${}`; } private get svgClass() { if () { return 'svg-icon ' + ; } else { return 'svg-icon'; } } } </script> <style scoped> .svg-icon { width: 1em; height: 1em; fill: currentColor; overflow: hidden; } </style>
Created in the current directory
import Vue from 'vue'; import SvgIcon from './'; // svg component // Register to global('svg-icon', SvgIcon); const requireAll = (requireContext: any) => ().map(requireContext); const req = ('./svg', false, /\.svg$/); requireAll(req);
Create a new svg folder in the current directory to store the required svg static files.
☁ icons [1.1.0] ⚡ tree -L 2 . ├── ├── svg │ └── └──
use:
<svg-icon name="loading"></svg-icon>
Let’s take a look at the principles and some notable points:
- After processing the svg file passing through import, it will generate a form similar to Sprite diagram, that is, symbol. You can use this svg directly by using .options({ symbolId: 'icon-[name]' }); in the configuration.
- After adding svg-sprite-loader, since cli handles svg by default, it is necessary to exclude the svg of the specified folder.
- When using it, due to the processing of the svgIcon component, you only need to specify name as the file name.
So, what does it have to do with our use of iconfont and svg?
There are many ways to use iconfont, which depends entirely on personal preferences. However, one of the methods used also uses the principle of svg symbol. Generally, iconfont will export these files by default.
☁ iconfont [1.1.0] ⚡ tree -L 2 . ├── ├── ├── ├── ├── ├── └── iconfont.woff2
We focus on the js file. Open the file and you can see that this js file has processed all svg as svg symbol and dynamically inserted into the dom node.
The symbolId generated by iconfont also complies with our svg-icon name naming rules, so we can use this js directly after introducing this project's entry file.
back-to-up
First of all, why do I write this component? The component library used in this project is elementUI, and the UI library comes with el-backtop, but can I say it is not easy to use? Or I am too stupid. After some hard work, I still couldn't use it successfully, so I wrote one myself.
Directly upload the code:
<template> <transition :name="transitionName"> <div v-show="visible" :style="localStyle" class="back-to-ceiling" @click="backToTop"> <slot> <svg viewBox="0 0 17 17" xmlns="http:///2000/svg" aria-hidden="true" style="height: 16px; width: 16px;" > <g> <path d="M12.036 15.59c0 .55-.453.995-.997.995H5.032c-.55 0-.997-.445-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29c.39-.39 1.026-.385 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" fill-rule="evenodd" /> </g> </svg> </slot> </div> </transition> </template> <script lang="ts"> import { Component, Vue, Prop } from 'vue-property-decorator'; @Component export default class BackToTop extends Vue { @Prop({ default: () => 400 }) private readonly visibilityHeight!: number; @Prop({ default: () => 0 }) private readonly backPosition!: number; @Prop({ default: () => ({}) }) private readonly customStyle!: any; @Prop({ default: () => 'fade' }) private readonly transitionName!: string; private visible: boolean = false; private interval: number = 0; private isMoving: boolean = false; private detaultStyle = { width: '40px', height: '40px', 'border-radius': '50%', color: '#409eff', display: 'flex', 'align-items': 'center', 'justify-content': 'center', 'font-size': '20px', cursor: 'pointer', 'z-index': 5 }; private get localStyle() { return { ..., ... }; } private mounted() { ('scroll', ); } private beforeDestroy() { ('scroll', ); if () { clearInterval(); } } private handleScroll() { = > ; } private backToTop() { ({ left: 0, top: 0, behavior: 'smooth' }); } } </script> <style scoped> .back-to-ceiling { background-color: rgb(255, 255, 255); box-shadow: 0 0 6px rgba(0, 0, 0, 0.12); background-color: '#f2f6fc'; position: fixed; right: 50px; bottom: 50px; cursor: pointer; } .back-to-ceiling:hover { background-color: #f2f6fc; } .fade-enter-active, .fade-leave-active { display: block; transition: display 0.1s; } .fade-enter, .fade-leave-to { display: none; } </style>
use:
<back-to-top :custom-style="myBackToTopStyle" :visibility-height="300" :back-position="0"> <i class="el-icon-caret-top"></i> </back-to-top>
custom-style can be defined by yourself, and the returned icon can also be replaced freely.
Note that the animation performance in safari is inconsistent, and it is still inconsistent after using requestAnimationFrame. I hope the students can play freely if they have time.
Summarize
Always write code with a learning attitude, try a variety of ways to write it out the most elegant one you have.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.