I was in charge of back-office projects at the company, and I took the opportunity to review and summarize the mistakes I had made
The directory structure
The initial directory is generated by the Vue CLI, but the default directory structure does not meet the requirements of the project. Here is the extended structure
├─ public // Static page ├─ scripts // Related Scripts To Configure ├─ SRC // Home Directory ├─ Assets // Static Resources ├─ API // Interface Information ├─ filters // Filter ├─ lib // ├─ Router // Route Config ├─ Styles // Style ├─ Utils ├─ views // Page ├─ app.vue // page Main entry ├─ main.js // Main entry ├─ tests // Test Cases ├─.EditorConfig // Edit related Configuration ├─ .postCSSLc.js // Postcss Configure ├── Babel.config. js // Preset Record ├─ package.json // Rely ├──.eslintrc.js // ESLint related Configure ├── Readme. md // project README ├ ─ vue.config.js // webpack configurationCopy the code
Rights management
A medium and large background is inevitably indispensable to the control of permissions, RBAC model is needed to understand, more complex model is evolved on this basis, because I have written related articles, here is not specific, you can click to view ramblings about permissions design
Axios and mock
axios
Axios is an HTTP library that can be used in browsers and Node.js, and is often used when using SRR isomorphism. The basic encapsulation is described below, depending on the business.
// utils/config.js
import http from "http";
import https from "https";
import qs from "qs";
const axiosConfig = {
// Note that in the production environment, this is for demonstration only
baseURL: "/mock/".// Data processing after request
transformResponse: [
function(data) {
returndata; }].// Query the object serialization function
paramsSerializer: function(params) {
return qs.stringify(params);
},
// Set timeout
timeout: 30000.// Whether to carry Token across domains
withCredentials: true.responseType: "json"./ / XSRF Settings
xsrfCookieName: "XSRF-TOKEN".xsrfHeaderName: "X-XSRF-TOKEN".// Maximum number of forwards, for Node.js
maxRedirects: 5.// Maximum response data size
maxContentLength: 2000.// Customize the error status code range
validateStatus: function(status) {
return status >= 200 && status < 300;
},
/ / for the node. Js
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true})};export default axiosConfig;
Copy the code
The configuration file is defined above. The reason why we don’t call AXIos globally or modify the configuration is that it will cause pollution. The interceptor part needs to be processed below to cancel repeated requests (if there is a business code, it can also be configured inside, such as the need to send token and other parameters uniformly).
// utils/api.js
import axios from "axios";
import config from "./config";
// Cancel duplicate requests
let pending = [];
const cancelToken = axios.CancelToken;
const removePending = config= > {
for (let p in pending) {
let item = p;
let list = pending[p];
// Execute the function body when the current request exists in the array
if (list.url === config.url + "&request_type=" + config.method) {
// Perform the cancel operation
list.cancel();
// Remove the record from the array
pending.splice(item, 1); }}};const service = axios.create(config);
// Add request interceptor
service.interceptors.request.use(
config= > {
removePending(config);
config.cancelToken = new cancelToken(c= > {
pending.push({
url: config.url + "&request_type=" + config.method,
cancel: c
});
});
return config;
},
error => {
return Promise.reject(error); });// Return status judgment (add response interceptor)
service.interceptors.response.use(
res= > {
removePending(res.config);
return res;
},
error => {
return Promise.reject(error); });export default service;
Copy the code
OK, this is the end of the basic encapsulation. In specific development, you can call Axios by introducing Action in vuex.
mock
Mocks are common in front and back end development, in the form of a convention document interface that allows the front end to develop quickly in order to decouple from unnecessary wait times before the back end.
Create a mock folder in SRC to store mock information. If the business is divided into multiple layers, remember to partition the mock folder. After that, expose the index.js file and import it globally in SRC /main.js. Here is a simple demo example
// src/mock/index.js
import Mock from "mockjs";
// Get the mock.Random object
const Random = Mock.Random;
// Set the asynchronous wait time
Mock.setup({
timeout: "200-2000"
});
const produceNewsData = function() {
let newNewsObject = {
title: Random.ctitle(), // random.ctitle (min, Max) Randomly generates a Chinese title. The default length is between 3 and 7
content: Random.cparagraph(), // random.cparagraph (min, Max) Generates a Chinese paragraph randomly. The number of sentences in the paragraph is 3-7 by default
createdTime: Random.date() // random.date () indicates the format of the generated date string, which defaults to YYYY-MM-DD;
};
return newNewsObject;
};
// Random demo request image
const demoOther = function() {
let newsList = [];
for (let i = 0; i < 20; i++) {
let newNewsObject = {
img: Random.dataImage("300x250")}; newsList.push(newNewsObject); }return newsList;
};
// Request this URL and it will be mock
Mock.mock("/mock/user", produceNewsData);
Mock.mock("/mock/other", demoOther);
Copy the code
// src/main.js
// Omit the other code
import "./mock";
Copy the code
BaseURL is filled in under the wrapper axios config file config.js above, so you can omit the mock prefix and remember to distinguish between development and production environments
Global loading
First, I would like to talk about the common centralized management methods of loading
- Manual management, one per page
loading
Open when a request is about to be sent, close when the request ends, no problem except verbose and modification trouble - Interception is accomplished through some middleware, for example
dav-loading
- Intercept by interceptor, for example
axios
The concrete implementation of the third kind is introduced here, because there is only one kind of background project loading in nature, so it is not necessary to introduce the second kind to affect the flexibility of dividing components.
The implementation idea is very simple. By dividing the API directory, place the interface that needs loading into a file separately, and use AXIos request and response interceptor to match URL to display and hide loading. The following is the specific implementation
// src/utils/app.js
import axios from "axios";
import config from "./config";
import vue from "vue";
import * as loadingAPI from "@/api/loading";
// Use this change to remind loading whether to display
const state = vue.observable({ content: 0 });
function changeState(config, add = true) {
const { url } = config;
const value = Object.values(loadingAPI);
if(! value.includes(url)) {return;
}
if (add) {
state.content += 1;
return;
}
state.content -= 1;
return;
}
const service = axios.create(config);
// Add request interceptor
service.interceptors.request.use(
config= > {
changeState(config);
return config;
},
error => {
return Promise.reject(error); });// Return status judgment (add response interceptor)
service.interceptors.response.use(
res= > {
changeState(res.config, false);
try {
const { data } = res;
Reflect.set(res, "data".JSON.parse(data));
} catch (e) {
//
}
return res;
},
error => {
return Promise.reject(error); });// Omit some code
export default service;
export { state };
Copy the code
Var. Observable generates an object that can be used by components to render functions and calculate properties. Displays and conceals loading by changing the internal content of the object. This is why you need to separate the API directory and then add loading components and state objects to the page
<template>
<div>
<loading v-model="show" />
<router-view class="router"></router-view>
</div>
</template>
<script>
import loading from "./loading";
import { state } from "@/utils/server";
export default {
components: {
loading
},
data: (a)= > ({ state }),
computed: {
show() {
return this.state.content > 0; }}};</script>
Copy the code
The details of the loading component implementation are skipped here. Finally, the page request can be directly consistent with the normal page, for example
serve.get(user).then(user= > {
this.content = user.data.content;
this.title = user.data.title;
});
// Some other operations will not affect loading
Promise.all([serve.get(other)]).then(all= > {
console.log(all);
});
Copy the code
Virtual list
Name has not been, of course, may be a bit before the author writing project in a scene, in the background project (according to the business) image management functions may be often encountered, back-end returned to a certain number of pictures of your address and your rolling load this information, this logic is very simple, but will need to think about this performance problem? Obviously it will, and when enough scrolling is done, the persistence of the previous DOM node will cause the browser to freeze.
Virtual list solution is similar to the scene, the user perception of the area is always limited, only need to change when the need to change the display of data can be, here posted before the article about the list optimization
Routing component custom cache
A very common scenario is in the e-commerce page, when you click on the list to enter the details, when you return, the list information must exist without being cleared, that is
Home -> List -> Details
We obviously want the page to not be cleared. We want to go from the list to the home page and enter the list. We don’t want to keep the list information
keep-alive
Vue has a built-in keep-alive component, which is used to cache component information. In the official guide, it is combined with dynamic components to save unnecessary rendering, and it can also be combined with
<keep-alive>
<router-view></router-view>
</keep-alive>
Copy the code
However, using and this alone can only do caching, but it can’t do what we want to do to specify a page cache. Is there any other way?
It is possible to define two
That’s a little abstract, but let’s do an example
// route
// Omit some code
{
path: "/keepAlive/".component: keepAlive,
children: [{path: "".component: (a)= >
import(/* webpackChunkName: "keep-alive-home" */ "@/view/keep-alive/home"),
name: "keepAlive".meta: {
depth: 1}}, {path: "list".component: (a)= >
import(/* webpackChunkName: "keep-alive-list" */ "@/view/keep-alive/list"),
name: "list".meta: {
name: "list".keepAlive: true.depth: 2}}, {path: "details".component: (a)= >
import(/* webpackChunkName: "keep-alive-details" */ "@/view/keep-alive/details"),
name: "details".meta: {
depth: 3}}},Copy the code
// index.vue
<template>
<! -- Define two exits, one can be cached and one normal route -->
<div>
<keep-alive :include="include">
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if=! "" $route.meta.keepAlive"></router-view>
</div>
</template>
<script>
export default {
data: (a)= > ({ include: []}),watch: {
$route(to, from) {
// Determine the entry condition
const { keepAlive, name, depth } = to.meta;
if (keepAlive && !this.include.includes(name)) {
this.include.push(name);
}
// Determine the rollback condition
if (from.meta.keepAlive && from.meta.depth > depth) {
/ / delete
const index = this.include.indexOf(from.meta.name);
if(index ! = =- 1) {
this.include.splice(index, 1); }}}}};</script>
Copy the code
One caveat here! The cached page must have the name attribute value
First check the name option of the component itself, and if the name option is not available, match its locally registered name (the key value of the parent component’s components option). The anonymous component could not be matched.
To facilitate management, the name attribute of the component is consistent with the meta attribute of the route
The last
The example shown above has been put into my warehouse. If you are interested, you can clone it and debug it yourself.
I am looking for a job. If you have a suitable position, please help me push it inside. You can comment on my resume privately or contact me at [email protected]