As the saying goes [to do good work, we must first sharp tools], in our front-end field is the same, want to do a good project, must have a complete project structure as a support, in order to better team cooperation, for the business.
Every time we create a new project, we need to do some basic configuration, we need to copy from other projects when we don’t have a basic template, and we even need to rewrite some of our own utility classes.
The source address
π©π for new projects, and want to try out the available templates for Vue + TypeScript developmentCopy the code
List of Basic features
- Partition of directory structure
- Differentiation of environments (development, test, production)
- Automatic route management and on-demand route loading
- Page loading progress
- API management
- Vuex/Custom state management
- Encapsulation of AXIos (repeated requests cancelled, multiple requests sent with only one loading, token invalid and refreshed)
- General utility functions (anti-shake, choke, etc.)
- Encapsulation of common instructions (animation instructions, lazy loading of pictures, copy instructions, etc.)
- Introduction of Web Workers (to start a thread, share the computational burden of the main thread, and be especially useful for particularly time-consuming tasks)
- WebSocket embedding (two-way communication)
- Multi-page configuration
- Element-ui(table, search, encapsulation of paging components, themes, internationalization, etc.)
- Git commit record optimization
- Mobile and PC adaptation
- Handling permissions (button permissions,
Dynamically add routes based on permissions
) Automated testing
Buried point
[The red part is not finished yet]
Create a project
Select [Vue Cli] (https://cli.vuejs.org/zh/guide/) scaffold to quickly create ` Vue ` create XXXCopy the code
The directory structure
Install BREW on MACCopy the code
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
Install the treeCopy the code
brew install tree
Tree Lists the directory structure
βββ Public β βββ β.js βββ App.config.js Unit Test ββ Package.json Project Information File ββ Public β βββ Favicon. Ico β ββ index.html β ββ other.html β ββstaticβ ββ ββ ββ CSS β ββ ββ ββ ββ CSS β ββ ββ test.work.js βββ SRC β ββ API API Management, separate folders by multiple pages β ββdefault- page β β β β β β but ts export API β β β β β β testModule. API. Ts page under small module API β β β β β other - page β β β β β but ts β β β β β NewsModule. Api. ts β ββ Assets static resource file, compiled by WebPack. Don't need to compile can into the public directory β β β β β styles common style (style, other common style) β β β β β common. SCSS β β β β β pageAnimate. SCSS β β β β components β β β β β business business component β β β β β β xw - list β β β β β β but ts β β β β β β but the ts β β β β β β index. The vue β β β β β common basic components β β β β β β xw - pagination β β β β β β β but the ts β β β β β β β index. The vue β β β β β β xw - search β β β β β β β generateEl. Vue β β β β β β β index. The ts β β β β β β β index. The vue β β β β β β xw - table β β β β β β coustomColumn. Vue β β β β β β GenerateElTable. Ts β β β β β β generateElTableColumn. Ts β β β β β β but the ts β β β β β β index. The vue β β β β β example β example file β ββ ββ Exercises. Vue β ββ Exercises. Vue β ββ Exercises. Vue β ββ exercises Vue β ββ Directive β ββ.directive. Ts β β ββ.directive β ββ Draggable.directive. Ts β β βββ β ββ longpress.directive Permissions. Directive. Ts β β β β i18n internationalization β β β β β but ts β β β β β lang β β β β β en. Ts β β β β β useful. Ts β β β β layout project layout β β β β β base. Layout. Vue β β β β β. Other layout. The vue β β β β the mock mock data β β β β β index. The js β β β β the plugins project plug-in β β β β β config. Ts β β β β β index. Ts β β β β β the lazyLoad. Plugin. Ts β β β β the router routing management, according to the folder page more point β β β β β config. Ts β β β β βdefaultβ β ββ ββ other β ββ ββ ββ ββ ββ ββ ββ ββ D.ts β βββ Flag School - Shims-Vue. D.ts β ββ Flag School - Shims-Vue. According to the folder page more point β β β β β Vuex module of common foundation β β β β β β permissions. Vuex. Ts β β β β β β user. Vuex. Ts β β β β βdefaultβ β β β β β home. Vuex. Ts β β β β β but ts export base vuex module β β β β the theme topics β β β β β fonts β β β β β β element - the ICONS. The vera.ttf β β β β β β Element - the ICONS. Woff β β β β β index. The CSS β β β β types type control file (grammar tips) β β β β β vue. Which s β β β β utils tool function folder β β β β β common. Ts β β ββ common js functions β β ββ eventCenter. Ts progressBar. ProgressBar β β ββ ReadyLocalStorage. Ts read local store data (user information, token, permissions, etc) coexist in the Vuex β β β β β request an Ajax request packaging β β β β β β index. The ts β β β β β β Index. The ts β β β β β β request. Ts β β β β β requestInstance. Ts Ajax example β β β β β useElement. Ts according to the need to use the Element - the UI β β β β β ββ ββ ws. Ts WebSocket Newsletter β ββ404.Vue β β β βdefault- page β β β β β App. Vue β β β β β main. Ts β β β β β the test -moduleSmall module β β β β β home page specific β β β β β β index. The vue β β β β β home2 β β β β β index. The vue β β β β the login. The vue β β β β other - page β β β β App. Vue β βββ ββ press -moduleβ ββ ββ ββ news1 β ββ ββ vue β ββ ββ news2 β ββ vue β ββ news2 β ββ Components β β β β β coustomColumnHeader. Vue β β β β index. The vue β β β tests β β β β unit β β β β example. The spec. The ts β β β tsconfig. Json β β β vue. Config. Js webpack configuration file β β β yarn - error. The log β β β yarn. The lock β β β the env. The development of the local environment configuration β β β the env. Production production environment configuration ββ.env.staging test environment configurationCopy the code
Environment to distinguish the
Different environment variables are implemented through the patterns provided by Webpack
The root directory
Create the following three files respectively
.env.development
NODE_ENV ="development"VUE_APP_REQUEST_URL ='http://localhost:8080'
Copy the code
.env.production
NODE_ENV = "production"
VUE_APP_REQUEST_URL = 'http://prod.com'
Copy the code
.env.staging
NODE_ENV = "production"
VUE_APP_REQUEST_URL = 'http://staging.com'
Copy the code
- Adding a compile command
package.json
"scripts": {
"serve": "vue-cli-service serve --mode development"./ / development
"build:stage": "vue-cli-service build --mode staging"./ / test
"build": "vue-cli-service build" / / production
}
Copy the code
Automatic route management and on-demand route loading
According to the need to load
Import ()
const App = () => import(/* webpackChunkName: app */ './app.vue')
/* webpackChunkName: app */ component block
Asynchronous component approach
Asynchronous components
const App = resolve => require(["./app.vue"], resolve)
Route lazy to load official documents
Automated routing
Recursive module introduction via Webpack’s require.context
require.context(
directory: String.includeSubdirs: Boolean /* Optional, the default is true */.filter: RegExp /* Optional, default is /^\.\/.*$/, all files */.mode: String / * optional, 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy - once, the default value is' sync' * /
)
Copy the code
Parameters to require.context cannot accept variablesCopy the code
[Benefits: Reduce conflicts in multiplayer development and forget to introduce new modules]
API management
API unified management, in the project, the [add, delete, change and check] URL of a page is unified, which will only change the request mode, so the URL is centralized management
Vuex/Custom state management
Vuex
-
The namespace
Resolve naming conflicts between Actions mutions between different modules
-
Dynamic registration module in addition to the basic module, other modules dynamic registration and uninstall
Custom state management (publish subscriber pattern)
A collection of different types of event functions is stored in the collection. Methods such as listening, canceling, dispatching, etc
Closures are used to handle single listeners, which store whether or not they have been executed
once(eventName: string, cb: CbType) {
const { eventStack } = this
const eventValue = eventStack[eventName]
const tempCb = () = > {
let isOutOfDate = false
return (data: object) = > {
if (isOutOfDate) return
cb(data)
isOutOfDate = true
}
}
eventValue ? eventValue.push(tempCb()) : eventStack[eventName] = [tempCb()]
}
Copy the code
Axios encapsulation
[Function List]
-
Request address processing
It mainly deals with the standardization of routing
/** * Processing path *@param The url path *@param IsBaseURL Is the root path */ private transformUrl(url = "", isBaseURL = false) { if(! url)return url; if (isBaseURL) { if (!/ / / $/.test(url)) { return `${url}/ `; } return url; } if (/ ^ / / /.test(url)) { return `${url.substr(1)}`; } return url; } Copy the code
-
Whether loading is required? Only one loading occurs when multiple requests are serialized
Use a variable to record the number of requests. When there are new requests, the number +1; when the number is 0 and loading needs to be started; when the request is completed, -1, and loading is disabled
/** * To enable or disable Loading *@param CustomConfig Indicates a custom configuration item *@param IsOpen Whether to enable */ private handleLoading(customConfig: CustomConfigType, isOpen: boolean) { if(! customConfig.isNeedLoading)return; // Do not start Loading repeatedly if (this.requestCount ! = =0) return; if (isOpen) { console.log("Open Loading"); return } console.log("Loading off"); } Copy the code
/** * initiate a request *@param Config Indicates the configuration item *@param CustomConfig Custom configuration */ private async transfromRquest( config: AxiosRequestConfig, customConfig: CustomConfigType = {} ): Promise<AxiosResponse> { customConfig = { ... this.defaultCustomConfig, ... customConfig };this.transformUrl(config.url); this.handleLoading(customConfig, true); this.addToken(config, customConfig); this.requestCount++ try { const result = await this.axios.request(config); return result; } catch (error) { // ... } finally { this.requestCount-- this.handleLoading(customConfig, false); }}Copy the code
-
Whether token is required
/** * token processing *@param Config Indicates the configuration item *@param CustomConfig Indicates a custom configuration item */
private addToken(config: AxiosRequestConfig, customConfig: CustomConfigType) {
if (customConfig.isNeedToken) {
config.headers = {
token: store.getters['userStore/getToken'] | |' '
};
} else{ config.headers = {}; }}Copy the code
- Request error handling when present
token
When invalid, refreshtoken
Send the failed request again
/** * initiate a request *@param Config Indicates the configuration item *@param CustomConfig Custom configuration */
private async transfromRquest(
config: AxiosRequestConfig,
customConfig: CustomConfigType = {}
): Promise<AxiosResponse> { customConfig = { ... this.defaultCustomConfig, ... customConfig };this.transformUrl(config.url);
this.handleLoading(customConfig, true);
this.addToken(config, customConfig);
this.requestCount++
try {
const result = await this.axios.request(config);
return result;
} catch (error) {
const { code, config } = error
if (code === 401) {
// Resolve token invalidation
// Jump to the login page
// store.commit('userStore/setToken', '')
// store.commit('permissionsStore/setPermissions', {})
// router.replace({ path: '/login', query: {
// redirectUrl: router.currentRoute.fullPath
/ /}})
// Method 2 automatically refreshes the token and resends the failed request
const res = await this.transfromRquest({
method: 'post'.url: '/refresh-token'
})
console.log(res, '/refresh-token')
store.commit('userStore/setToken', res.data.token)
return this.transfromRquest(config)
// In request interception, verify whether the token expires before initiating a request
}
this.handleError(customConfig, error);
return Promise.reject(error);
} finally {
this.requestCount--
this.handleLoading(customConfig, false); }}Copy the code
-
Cancel the request
The CancelToken provided by Axios is combined with queues to achieve this. (The queue stores information about the current request (some custom rules to determine if it is the same request) and the cancel function)
[Disadvantages:]
This kind of cancellation request is actually received by the server, but the browser level has done a layer of processing and cannot wait for the response.
When it is necessary to prevent repeated submission of data, the implementation of this method is not accurate. You can consider anti-shake, execution of variable control function, and click state of variable control button
Introduction of Web Workers
PostMessage cannot send functions
WebSocket embedded
See Packaging a simple WebSocket library
Encapsulation of the element-UI list component
[ι θͺ :]
- Divide components, header, content, bottom
<header class="list-header animate__animated animate__fadeIn">
<slot name="head" />
<xw-search
v-if="searchOption"
:searchOption="searchOption"
:searchParams="searchParams"
@onSearch="getList"
>
<slot name="search" />
</xw-search>
</header>
<main class="list-main">
<slot name="main" />
<xw-table :tableOption="tableOption" />
</main>
<footer class="list-footer">
<slot name="footer" />
<xw-pagination
v-if="paginationOption"
:paginationOption.sync="paginationOption"
@onPagination="getList"
/>
</footer>
Copy the code
- The search results are held by the list component
searchParams: SearchParams = {};
-
Assembly of tabular data
To reduce the need for templates during development, all operations related to the table are encapsulated as configuration items.
import { Component } from 'vue'
export interface TableOption {
// Element-ui table configuration properties
tableAttribute: TableAttribute
// The configuration properties of the column
tableColumn: TableColumn[]
}
export interface TableAttribute {
/ / property
props: {
data: object[]
[index: string]: any
}
/ / event
on: { [key: string]: Function | Function[]}}export interface TableColumn {
/ / property
props: { label? : string prop? : string [index: string]: any },/ / slotsslots? : { [index: string]: {/ / propertyoptions? : object// Custom components
component: Component
}
}
// Multilevel headercolumnChild? : TableColumn[] }Copy the code
Complex example
Edit the current row, display different buttons according to permissions, and load and disable status of buttons
Git commit record optimization
Use Commitizen instead of your Git commit. Commitizen also requires an adapter. Cz-xcom – Changelog is recommended
- The installation
npm install -D commitizen cz-conventional-changelog
- configuration
Package. json configuration:
"scripts": {
"commit": "git-cz"
},
"config": {
"commitizen": {
"path": "node_modules/cz-conventional-changelog"}}Copy the code
- use
npm run commit
-
Custom adapter
-
- The installation
npm i -D cz-customizable @commitlint/config-conventional @commitlint/cli
- The installation
-
- configuration
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"}}Copy the code
At the same time, create the.cz-config.js.commitlintrc.js file in the project directory
The effect is as follows:
Instruction encapsulation
- Custom animation directives with animate. CSS
- Copy and paste instruction
- If you order
- Drag and drop the instructions
- Prohibit expressions and special character commands
- Long according to the instruction
- Permission control instruction
The source address
The above is just a simple overview, please see the source address for details
Post to recommend
- Realize project download, automatic routing and project release scaffolding based on Node
- Encapsulate a simple WebSocket library
- Note: Vue often meet questions summary and analysis
- Proxy is an alternative to Object. DefineProperty in Vue3.0
- Vue 3.0 – First experience 1
- Figure out a prototype, a prototype object, a prototype chain
- Promise Principles = Build a Promise from 0 to 1
[Notes are not easy, if it is helpful to you, please like, thank you]