The author: Flower flower poplar village head
2021 what’s
2021 is the first and even bigger Vite2, new plug-in architecture, silky development experience, and the perfect combination of Vue3. In the first round of 2021, the village head will start the front-end learning journey with Vite2+Vue3 as the theme.
In this paper, the target
Common task practices in vuE3 + VitE2 projects:
- Infrastructure: configuration, Lint, testing, style organization, service encapsulation, data mock, UI library integration, routing, state management, etc
- Common business writing and code organization
- Packaging releases
Create the Vite2 project
Gossip needless to say, below we table a table of heroes Vite2
Use NPM:
$ npm init @vitejs/app
Copy the code
Specify the project name and template as prompted, or directly
$ npm init @vitejs/app my-vue-app --template vue Copy the code
Vite2 major changes
- Configuration options change:
Vue-specific options
, Create options, CSS options, JSX options, etc., alias behavior changes: no longer requires a/beginning or end - Vue support: via @vitejs/plugin- Vue plugin support
- The React to support
- HMR API changes
- List format changes
- Plug-in API redesign
Vue support
Vue integration is also implemented through plug-ins, just like any other framework:
SFC definitions use setup script by default.
The alias definition
No longer need to add/before and after aliases like vite1, this and Webpack project configuration can be consistent for portability.
import path from 'path'
export default {
alias: {
"@": path.resolve(__dirname, "src"),
"comps": path.resolve(__dirname, "src/components"),}}Copy the code
Try it in app.vue
<script setup>
import HelloWorld from 'comps/HelloWorld.vue'
</script>
Copy the code
Plug-in API redesign
The main change in Vite2 is the plugin architecture, which is more standardized and extensible. The Vite2 plugin API extends from the Rollup plugin architecture, so it is compatible with existing Rollup plugins. The Vite plugin can also be developed and created at the same time.
I will have a separate panel discussion on plug-in writing, welcome to follow me.
Vue3 Jsx support
Vue3 JSX support requires the introduction of plugins: @vitejs/ plugin-vuE-jsx
$ npm i @vitejs/plugin-vue-jsx -D
Copy the code
Register the plugin, vite.config.js
import vueJsx from "@vitejs/plugin-vue-jsx";
export default {
plugins: [vue(), vueJsx()],
}
Copy the code
Usage also has requirements, revamp app.vue
<! -- 1. mark JSX --> <script setup lang=" JSX "> import {defineComponent} from "vue"; import HelloWorld from "comps/HelloWorld.vue"; import logo from "./assets/logo.png" // 2. Define components with defineComponent and export Default defineComponent({render: () => ( <> <img alt="Vue logo" src={logo} /> <HelloWorld msg="Hello Vue 3 + Vite" /> </> ), }); </script>Copy the code
Mock plug-in application
Vite2 has been refactored from vite-plugin-Mock.
Installing a plug-in
npm i mockjs -S
Copy the code
npm i vite-plugin-mock cross-env -D
Copy the code
The configuration, vite. Config. Js
import { viteMockServe } from 'vite-plugin-mock'
export default {
plugins: [ viteMockServe({ supportTs: false}})]Copy the code
Set the environment variable, package.json
{
"scripts": {
"dev": "cross-env NODE_ENV=development vite"."build": "vite build"}},Copy the code
engineering
For basic configuration, styling, Lint, testing, packaging and publishing, see my previous article: Vite Engineering practices
Project Infrastructure
routing
Install the vue – the router 4. X
npm i vue-router@next -S
Copy the code
Route configuration, router/index.js
import { createRouter, createWebHashHistory } from 'vue-router';
const router = createRouter({
history: createWebHashHistory(),
routes: [{path: '/'.component: () = > import('views/home.vue')}});export default router
Copy the code
Introduction, main. Js
import router from "@/router";
createApp(App).use(router).mount("#app");
Copy the code
Don’t forget to create home.vue and modify app.vue
Route usage changes slightly in the village chief’s video tutorial
State management
Install vuex 4. X
npm i vuex@next -S
Copy the code
Store configuration, Store /index.js
import {createStore} from 'vuex';
export default createStore({
state: {
couter: 0}});Copy the code
Introduction, main. Js
import store from "@/store";
createApp(App).use(store).mount("#app");
Copy the code
Usage and before basic same, village chief video tutorial
Style organization
Install the sass
npm i sass -D
Copy the code
The styles directory holds various styles
Index.scss organizes these styles as an exit, while writing some global styles
Finally import in main.js
import "styles/index.scss";
Copy the code
Note that the styles alias is added in viet.config. js
UI library
Use our own element3 from the flower hill team.
Chinese document
The installation
npm i element3 -S
Copy the code
Full introduction, main.js
import element3 from "element3";
import "element3/lib/theme-chalk/index.css";
createApp(App).use(element3)
Copy the code
Import main.js on demand
import "element3/lib/theme-chalk/button.css";
import { ElButton } from "element3"
createApp(App).use(ElButton)
Copy the code
Better to extract as plugins, plugins/element3.js
// complete introduction
import element3 from "element3";
import "element3/lib/theme-chalk/index.css";
// Import on demand
// import { ElButton } from "element3";
// import "element3/lib/theme-chalk/button.css";
export default function (app) {
// complete introduction
app.use(element3)
// Import on demand
// app.use(ElButton);
}
Copy the code
test
<el-button>my button</el-button>
Copy the code
Basic layout
We need a basic layout page for our application, similar to the following, and each page will be parent page:
Layout page, Layout /index.vue
<template> <div class="app-wrapper"> <! - the sidebar - > < div class = "sidebar - container" > < / div > <! Content container --> <div class="main-container"> <! -- Top navigation bar --> <navbar /> <! < AppMain /> </div> </div> </template> <script setup> import AppMain from "./ Components/appmain.vue "; import Navbar from "./components/Navbar.vue"; </script> <style lang="scss" scoped> @import ".. /styles/mixin.scss"; .app-wrapper { @include clearfix; position: relative; height: 100%; width: 100%; } </style>Copy the code
Don’t forget to create appmain. vue and navbar. vue
Route configuration, router/index.js
{
path: "/".component: Layout,
children: [{path: "".component: () = > import('views/home.vue'),
name: "Home".meta: { title: "Home page".icon: "el-icon-s-home"}},],},Copy the code
Dynamic navigation
The side navigation
Dynamically generate side navigation menus from routing tables.
First create a Sidebar component. The configuration in the recursive output routes is a multi-level menu, Layout /Sidebar/index.vue
<template>
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="false"
:active-text-color="variables.menuActiveText"
mode="vertical"
>
<sidebar-item
v-for="route in routes"
:key="route.path"
:item="route"
:base-path="route.path"
/>
</el-menu>
</el-scrollbar>
</template>
<script setup>
import SidebarItem from "./SidebarItem.vue";
import { computed } from "vue";
import { useRoute } from "vue-router";
import { routes } from "@/router";
import variables from "styles/variables.module.scss";
const activeMenu = computed(() => {
const route = useRoute();
const { meta, path } = route;
if (meta.activeMenu) {
return meta.activeMenu;
}
return path;
});
</script>
Copy the code
Note: The sass file export variable resolution requires CSS Module, so the variables file should have a module infix.
Add related styles:
styles/variables.module.scss
styles/sidebar.scss
styles/index.scss
The introduction of
Create sideBarItem. vue component to resolve whether the current route is a navigation link or a parent menu:
Bread crumbs
Breadcrumbs can be dynamically generated by routing the match array.
Bread crumbs components, layouts/components/Breadcrumb. Vue
<template> <el-breadcrumb class="app-breadcrumb" separator="/"> <el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path"> <span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect" >{{ item.meta.title }}</span> <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a> </el-breadcrumb-item> </el-breadcrumb> </template> <script setup> import { compile } from "path-to-regexp"; import { reactive, ref, watch } from "vue"; import { useRoute, useRouter } from "vue-router"; const levelList = ref(null); const router = useRouter(); const route = useRoute(); const getBreadcrumb = () => { let matched = route.matched.filter((item) => item.meta && item.meta.title); const first = matched[0]; if (first.path ! = = = "/") {matched [{path: "/ home", meta: {title: "home page"}}]. Concat (matched); } levelList.value = matched.filter( (item) => item.meta && item.meta.title && item.meta.breadcrumb ! == false ); } const pathCompile = (path) => { var toPath = compile(path); return toPath(route.params); } const handleLink = (item) => { const { redirect, path } = item; if (redirect) { router.push(redirect); return; } router.push(pathCompile(path)); } getBreadcrumb(); watch(route, getBreadcrumb) </script> <style lang="scss" scoped> .app-breadcrumb.el-breadcrumb { display: inline-block; font-size: 14px; line-height: 50px; margin-left: 8px; .no-redirect { color: #97a8be; cursor: text; } } </style>Copy the code
Don’t forget to add dependencies: path-to-regexp
Note: Vue-Router4 no longer uses path-to-regexp to resolve dynamic paths, so this needs to be improved.
Data encapsulation
Unified encapsulation of data request services helps to solve the following problems:
- Unified configuration request
- Requests and responses are processed in a unified manner
Preparations:
-
Install axios:
npm i axios -S Copy the code
-
Add the configuration file:.env.development
VITE_BASE_API=/api Copy the code
Request encapsulation, utils/request.js
import axios from "axios";
import { Message, Msgbox } from "element3";
// Create an axios instance
const service = axios.create({
// Prefix the request address with baseURL
baseURL: import.meta.env.VITE_BASE_API,
// Cookies are carried when sending a cross-domain request
// withCredentials: true,
timeout: 5000});// Request interception
service.interceptors.request.use(
(config) = > {
// Emulated the specified request token
config.headers["X-Token"] = "my token";
return config;
},
(error) = > {
// Unified handling of request errors
console.log(error); // for debug
return Promise.reject(error); });// Response interceptor
service.interceptors.response.use(
/** * The response can be processed by the status code, which can be modified according to the situation
(response) = > {
const res = response.data;
// If the status code is not 20000, an error is considered
if(res.code ! = =20000) {
Message.error({
message: res.message || "Error".duration: 5 * 1000});// 50008: invalid token; 50012: Other clients are logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// Log in again
Msgbox.confirm("You have logged out. Please log in again."."Confirm", {
confirmButtonText: "Log in again".cancelButtonText: "Cancel".type: "warning",
}).then(() = > {
store.dispatch("user/resetToken").then(() = > {
location.reload();
});
});
}
return Promise.reject(new Error(res.message || "Error"));
} else {
returnres; }},(error) = > {
console.log("err" + error); // for debug
Message({
message: error.message,
type: "error".duration: 5 * 1000});return Promise.reject(error); });export default service;
Copy the code
Common Business Processes
Structured data presentation
El-table was used to display structured data, and el-Pagination was used for data pagination.
The file structure is as follows: list.vue displays the list, edit.vue and create.vue edit or create, internally reuse detail.vue processing, model is responsible for data business processing.
Data display in list.vue
<el-table v-loading="loading" :data="list"> <el-table-column label="ID" prop="id"></el-table-column> <el-table-column </el-table-column> <el-table-column label=" age" prop="age"></el-table-column> </el-table>Copy the code
The acquisition logic of list and loading data can be extracted from userModel.js using compsition-API
export function useList() {
// List data
const state = reactive({
loading: true.// Load status
list: [].// List data
});
// Get the list
function getList() {
state.loading = true;
return request({
url: "/getUsers".method: "get",
}).then(({ data, total }) = > {
// Set the list data
state.list = data;
}).finally(() = > {
state.loading = false;
});
}
// Obtain data for the first time
getList();
return { state, getList };
}
Copy the code
Used in the list. The vue
import { useList } from "./model/userModel";
Copy the code
const { state, getList } = useList();
Copy the code
Paging, list.vue
<pagination
:total="total"
v-model:page="listQuery.page"
v-model:limit="listQuery.limit"
@pagination="getList"
></pagination>
Copy the code
The data is also processed in the userModel
const state = reactive({
total: 0./ / the total number of article
listQuery: {// paging query parameters
page: 1.// Current page number
limit: 5.// Number of entries per page}});Copy the code
request({
url: "/getUsers".method: "get".params: state.listQuery, // Add paging parameters to the query
})
Copy the code
Form processing
User data is added and edited using El-Form
This can be handled by a component detail.vue, the only difference being whether information is retrieved and backfilled into the form at initialization.
<el-form ref="form" :model="model" :rules="rules">
<el-form-item prop="name" label="Username">
<el-input v-model="model.name"></el-input>
</el-form-item>
<el-form-item prop="age" label="User age">
<el-input v-model.number="model.age"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="submitForm" type="primary">submit</el-button>
</el-form-item>
</el-form>
Copy the code
Data processing can also be extracted into userModel for processing.
export function useItem(isEdit, id) {
const model = ref(Object.assign({}, defaultData));
// During initialization, isEdit determines whether the details need to be retrieved
onMounted(() = > {
if (isEdit && id) {
// Get details
request({
url: "/getUser".method: "get".params: { id },
}).then(({ data }) = >{ model.value = data; }); }});return { model };
}
Copy the code
Supporting video demonstration
Best practices for Vite2 + Vue3 project “Prepare for 2021”
Form a complete set of source code
This article related source point here
Subsequent updates
There are still some questions not covered due to time, you can pay attention to the flower and Fruit Mountain team, we will continue to update.