vue-admin-stepbystep

A Vue.js project

series

  • [juejin. Cn/post / 684490…]. Vue-admin detailed note, must be hand-held project series (2)
  • [juejin. Cn/post / 684490…]. Vue-admin detailed comments, must do the project series (3) throw to the server to solve the error
  • [item address github.com/whylisa/vue…]

Project function

  • 1 the login
  • 2 the homepage
  • 3 out of
  • Table 4 pages

Project Technical points

  • 1. Use vue
  • 2. Use echarts
  • 3, using jSON-server (series 2 write detailed documentation)
  • 4, use Node to start a simple service, service the interface (series 2 write detailed documentation)
  • 5, the use of axios
  • 6. Rules for using Vue-Router (asynchronous and synchronous loading)
  • 7, the use of localstorage or cookies
  • 8, cooperate element – the UI
  • 9. Modify the pit in the style of the component
  • 10. Packaging optimization
  • 11, DNS optimization
  • 12, configure local proxy, use interface (series 2 write detailed documentation)
  • 13. Use AXIOS with JSON-server to simulate adding, deleting, modifying and checking (series 2 write detailed documents)
  • 14. Use the nProgress plug-in
  • 15. Little-known Element-UI scrollbar
  • 16, grid layout, size screen to match media queries
  • 17. The CSS uses less
  • 18, Code style, personal style, disable jSLint to prevent people from getting mad
  • 19, Compatibility processing (series 2 write detailed documentation)

Project structures,

  • 1 vue init webpack XXUsing the vue – 2.0 cli
Project name: default Project Description: default Author: Default Vue build: Runtime + Compiler Install Vue-router? : Y Use ESLint to Lint your code? : Y Select Standard Setup unit tests: n Setup e2e tests with Nightwatch? : n Should we run `npm install` for you after the project has been created? (recommended) : Yes, use NPMCopy the code
  • 2 Run the CD vue-admin-stepbystep command to enter the project
  • 3 Run the project: NPM run dev

How to add a new feature??

  • 1 incomponentsCreate a new folder in (login) and create components in the file (login.vue)
  • 2 in therouter/index.jsImport components in (login.vue)
  • 3 Configure routing rules

Use Element-UI in your project (other self-gg deepening images)

  • ElementUI document
  • Installation:npm i element-ui -S
// main.js

// Import elementui-js
import ElementUI from 'element-ui'
// Import elementuI-css
import 'element-ui/lib/theme-chalk/index.css'
// Install the plug-in
Vue.use(ElementUI)
Copy the code

What did the project launch do

  • 1 Run in the terminal:npm run dev, is actually running:webpack-dev-server ...
  • 2 Run the webpack-dev-server command to start a server
  • 3 According to the specified entrysrc/main.jsStart analyzing the modules used in the entry
  • 4 when it encountersimportWebpack loads the contents of these modules (if there are duplicate modules, such as Vue, they will actually only be loaded once in the future) and executes the code when it is encountered
  • 5. Create a Vue instance, compile the App component as a template, and render the contents of the template in the App component at the position of # App

The routing configuration

  • 1 Load routes asynchronously
  • 2 Use the progress bar plug-in
  • 3 Log in to intercept sticky sessions
import Vue from 'vue'
import Router from 'vue-router'
// Introduce the nProgress bar
import NProgress from 'nprogress'
// Introduce the nprogress bar style
import 'nprogress/nprogress.css'
// Each component is packaged as a js file during the packaging process. If not, use /* webpackChunkName: "home" */
// When packaging, it generates 0.js,1.js, etc. When used, it is packaged as home.js
// Import the Login component (note, do not add.vue)
// This is the asynchronous loading of the route,! Important, this is necessary to optimize the project

// Introduce the home component
const Home = (a)= > import(/* webpackChunkName: "home" */ '@/components/home')
// Import the login component
const Login = (a)= > import(/* webpackChunkName: "home" */ '@/components/login')
// Introduce the table component
const Table = (a)= > import(/* webpackChunkName: "home" */ '@/components/table/table')
// Introduce the Home Ain component
const HomeMain = (a)= > import('@/components/HomeMain')

// This is synchronous loading
//import Login from '@/components/login/Login'

Vue.use(Router)

const router = new Router({
	mode: 'history'.// Open history mode, remove #,
	 // In vue, an instance is usually used to access a property
     // vm.xxxx vm.$set vm.$refs vm.$router
	routes: [
		{
			path: '/'.redirect: '/homeMain'// Redirect the route
		},
		{
			path: '/login'.name: 'login'.component: Login
		},
		{
			path: '/home'.name: 'home'.component: Home,
			// Children is used to configure child routing. The matched components are displayed in the router-view of the Home component
		    // For the child path:
		    // 1 If it does not start with /, then the hash value is: parent path + / + child path
		    // That is, /home/homemain
		    // 2 If the child route path starts with /, then the future hash will be: /users without the parent path
            // Home ain
            // This is a child route in the page where router-view must be declared as the exit
			children: [
				{
					path: '/homeMain'.name: 'homeMain'.component: HomeMain
				},
				{
					path: '/table'.name: 'table'.component:Table
				}
			]
		}
	]
});
// Configure the navigation guard for the router
// To: Where
// from: where is it from
// next() : next() : permits next('/login') to the login component
// After the login is successful, the token is stored in localStorage
// In the navigation guard, first determine whether the currently visited page is the login page
// If it is a login page, directly permit (next())
// If it is not the login page, obtain the token from localStorage to determine whether there is a login
// If logged in, release directly (next())
// if not logged in, jump to the login page and let the user login (next('/login'))
router.beforeEach((to, from, next) = > {
// Start the progress bar
	NProgress.start()
// Obtain whether there is a token
	let token = localStorage.getItem('myToken')
	// If you already want to login, do not need to intercept
	if (to.path === '/login' || token) {
		next()
	}else {
		next('/login')}}); router.afterEach((a)= > {
// Close the progress bar
	NProgress.done()
})

export default router
Copy the code

The login function

  • 1 installation:npm i -S axios
  • 2 in theLogin.vueComponent to import AXIOS
  • 3 Use Axios to send the request according to the interface document and complete the login
  • 4 Login set the token, you can use the localStorage cookie any choice
<div class="l-right">
   		<div class="l-l">
   			 <! -- @tab-click="handleClick" -->
   			<el-tabs v-model="activeName">
   				<el-tab-pane label="User Login" name="first">
   					<! -- el-form: Custom form components -->
   				    <! -- :model="form" form object, used to collect receipts -->
   				    <! -- label ="80px" -->
   				    <! -- el-form-item: form item -->
   					<el-form ref="form" status-icon :rules="rules" :model="form" label-width="80px">
   						<el-form-item  prop="username">
   							<el-input v-model="form.username" placeholder="Please enter your user name." prefix-icon="iconfont icon-yonghuming"></el-input>
   						</el-form-item>
   						<el-form-item  prop="password">
   							<! -- In the future, when we try to register an event for a component, it will not be registered -->
   							<! [email protected] Click enter on the keyboard to trigger the event -->
   							<! Native: registers events for the root element of the component -->

   							<el-input type="password" v-model="form.password" placeholder="Please enter your password" @keyup.enter.native="login" prefix-icon="iconfont icon-mima"></el-input>
   						</el-form-item>
   						<el-form-item>
   							<! -- Use @ syntax to bind sugar events -->
   							<el-button type="primary" @click="login">The login</el-button>
   							<el-button @click="reset">reset</el-button>
   						</el-form-item>
   					</el-form>
   				</el-tab-pane>
   				<el-tab-pane label="Hot guy login" name="second">Grow very handsome</el-tab-pane>
   			</el-tabs>
   		</div>
   		
   	</div>
   </div>
Copy the code
 export default {
		data () {
			return {
// Define variables that can be retrieved directly from the page using {{}} syntax
				activeName: 'first'.form: {
					username: 'why'.password: "123456"
				},
				rules: {
					// Verify the user name
					username: [
						// The user name is required
// required Whether it is required
// message Indicates the message
// Trigger How to trigger
						{ required: true.message: 'Please enter a user name'.trigger: 'change' },
						{ min: 3.max: 6.message: '3 to 6 characters long'.trigger: 'change'}].// Verify the password
					password: [
						{ required: true.message: 'Please enter your password'.trigger: 'change' },
						{ min: 6.max: 12.message: '6 to 12 characters long'.trigger: 'change'}}},methods: {
			login () {
// Trigger the inspection rule in the page first, do not pass the prompt, pass the request to the background,
$refs ="form"; $refs ="form";
		     this.$refs.form.validate(async (valid) => {
				 if (valid) {
// Use Axios to send the request to the background
// In ES6, the arrow function is not bound to this and can be printed to point to the vue instance
					 this.axios('/api/login').then( res= > {
						 console.log(res.data[0])// To view the data in the interface
						 let lg = res.data[0] // Assign data to variables
						 console.log(lg.username,lg.password)// It is mainly used to view data
						 if(lg.username === this.form.username && lg.password==this.form.password){
							 localStorage.setItem('myToken',lg.username)// Set interception, which can be viewed in the Application console with cookies, etc
							 this.$message.success('Congratulations, you logged in.')// A successful login message
							 this.$router.push('homeMain') // Use the programmatic navigation route to jump
						 }else {
							 this.$message.error('Incorrect account or password')// Error message when the account password is incorrect

						 }
					 })
				 }
			 })
			},
			reset () {
				this.$refs.form.resetFields()// Clear the input box
// The data is written by me, so I can change it by myself}}};Copy the code

Top and side

<el-container>
			<! -- Set height to el-header -->
			<el-header style="height: 70px;">
				<div class="logo">
					<! -- This is where you can put your regular website logo-->
					<! --<img src=".. /assets/main/logo.png" alt="">-->
				</div>
				<div class="header-right">
					<div class="logout"  @click="layout">
					    <! --javascript:; To prevent the default behavior of the A label, -->
						<a href="javascript:;">exit</a>
					</div>
					<div class="people">
						<! -- map the font icon to iconfont -->
						<i class="iconfont icon-lianxirenwode"></i>Zhang SAN</div>
					<div class="call">
						<i class="iconfont icon-lianxiwomen"></i>Contact us</div>
				</div>
			</el-header>
			<el-container>
				<el-aside width="160px" background-color="#26292E">
					<el-scrollbar style="height: 100%;">
						  <! -- el-menu: Navigation menu component -->
				          <! -- default-active: default highlighted menu -->
				          <! -- open close -->
				          <! -- el-submenu -->
				          <! -- el-menu-item-group: Submenu group -->
				          <! -- el-menu-item: each item in the submenu -->
				          <! -- unique-opened: Opens only one submenu -->
				          <! -- router: if router is true, then index will be the connection of the route -->
				          
						<el-menu :unique-opened='true' :router="true" text-color="#ffffff" active-text-color="#cccccc">
							<el-submenu index="1">
								<template slot="title"><i class="iconfont icon-shouye"></i>
									<span @click="gomain">Home page</span>
								</template>
							</el-submenu>
						
							<el-submenu index="2">
								<template slot="title"><i class="iconfont icon-message-channel"></i>table</template>
								<el-menu-item-group>
									<el-menu-item index="/table">table</el-menu-item>
								</el-menu-item-group>
							</el-submenu>
						</el-menu>
					</el-scrollbar>
				</el-aside>
				<el-container>
					<! -- Use element's own scrollbar, not the official document -->
					<el-scrollbar style="height: 100%; width: 100%;">
					<el-main>
						 <keep-alive>
				            <! -- Here is the view component that will be cached -->
				            <! -- $route.meta. KeepAlive: If true, the cache component is displayed with the keep-alive tag -->
			              <router-view v-if="$route.meta.keepAlive">
			              </router-view>
			             </keep-alive>
			          <! -- Here is an uncached view component -->
			          <router-view v-if=! "" $route.meta.keepAlive">
			          </router-view>
					</el-main>
					</el-scrollbar>
				</el-container>
			</el-container>
		</el-container>
Copy the code
export default {
		created() {

		},
		data() {
			return{}},methods: {
			gomain() {
			// Programmatic navigation
				this.$router.push('/homeMain')},// Exit function
			layout() {
// Exit function to remove localStorage myToken
				localStorage.removeItem('myToken')
// Go to the home page
				this.$router.push('login')
// Exit successfully
				this.$message.success('Exit was successful')}}},Copy the code

Home page

  • The code is a bit too much, mainly to echarts, X-axis arrow, bar chart style changes, and element raster layout with media query
xAxis: {
       data: ["March"."April"."May"."June"."July"].axisLine: {
			symbol: ['none'.'arrow'].lineStyle: {
			color: 'rgba(212,212,212,1)'.// The color of the x axis
				width: 1 // Here is the width of the axes, 0 is not displayed}}},yAxis: [{

			type: 'value'.axisLabel: {
	    		show: false // This line of code controls whether the X-axis text is displayed
			},
			splitLine: {
				show: false.// Whether the grid lines are displayed
				// Change the style
			lineStyle: {
				color: '#EDEDED' // Change the grid color}},axisLine: {
				lineStyle: {
				color: '#fff'.// The color of the x axis
				width: 0 // Here is the width of the axes, 0 is not displayed}}}].Copy the code

Use of forms

<div class="table">
		<div class="t-top">
			<! -- using el-input, it defaults to 100% of the parent's width -->
			<el-input v-model="query" placeholder="Please enter content"></el-input>
			<! --el-button binding click event to send data query to background -->
			<! In this case, we need to bind the keyUp event, request the data from the background, and then render a small drop-down box. What we need to do is to send the query field to the background. The background uses the SQL statement fuzzy query, and we render it.
			<el-button type="primary" @click="search">The query</el-button>
		</div>
		<div class="t-bottom">
			
			    <! -- el-table: Table component -->
			    <! -- :data='tableData' -->
			    <! -- el-table -- column -->
			    <! -- prop: data in tableData -->
			    <! -- label: header -->
			    <! -- width: the width of the column -->
			    <! --min-width: set percentage -->
			     <! --:header-cell-style="{background:'red'}"-->
		        <! --align="center" -->
			     
			 <el-table
		      :data="tableData"
		      style="width: 100%"
		      :header-cell-style="{background:'red'}"
		      >
		      <el-table-column
		        prop="date"
		        label="Date"
		        align="center"
		        width="180">
		      </el-table-column>
		      <el-table-column
		        prop="name"
		        label="Name"
		        align="center"
		        width="180">
		      </el-table-column>
		      <! If the percentage is not set, the remaining width is automatically allocated -->
		      <el-table-column
		        prop="address"
		        align="center"
		        label="Address">
		      </el-table-column>
		    </el-table>
		</div>
	</div>
Copy the code
export default {
	  data() {
	    return {
// The bound input query keyword
	      query: ' '.// We need an array to hold the table data
// This is inside the element component,
	        tableData: []
	    }
	  },
	  mounted() {
// Call the render list in Vue's life cycle mounted
	  	this.initTable()
	  },
	  methods:{
	  	initTable() {
	  		this.axios('/api/table').then( res= > {
	  			console.log(res.data)// What data is returned by the interface
	  			this.tableData = res.data // The interface returns an array that can be assigned directly to the table
	  		})
	  	},
	  	search() {
	  		
	  	}
	  }
	}
Copy the code

Change the element-UI style notice!

<! Scoped: You can't change the style in the elelment component -->
<style lang="less" scoped="scoped">
/* If you don't want to use scoped, you can wrap all the styles in the parent class so that they don't affect each other */
Copy the code

Here are some conceptual things, and the rest of series 2 will be refined

Programmatic navigation

  • Is through THE JS code to achieve the hop function of the route
// Note: Router is not route
// Router is used to implement route jump, route is used to obtain route parameters
// The parameter of the push method is: the route address to jump to (path)
this.$router.push('/home')
Copy the code

password

  • Add type=”password” to the input box component to change the password box status
<el-input type="password" v-model="loginForm.password"></el-input>
Copy the code

Login to intercept

  • Note: Users should not be allowed to access any page other than the login page without having logged in

Description of the entire process of login and interception

  • 1 After successful login, store the token to localStorage
  • 2 In the navigation guard, determine whether the currently accessed page is the login page
  • 3 If the login page is displayed, permit the login directly (next()).
  • 4 If the login page is not displayed, obtain the token from localStorage to check whether the user has logged in
  • 5 If logged in, directly release (next())
  • 6 If no login, go to the login page and let the user login (next(‘/login’))

Description of token mechanism

  • In the project, if the login is successful, then the server will return a token to us
  • This token is the token of successful login
  • This token is equivalent to the session ID in cookie+session mechanism

Company personnel and project development process

  • 1 The product manager customizes the project requirements
  • 2. Assign tasks: first assign all tasks to the project team, and then assign them to each developer by the project team
  • 3. Development: get product prototype + requirement document + UI design draft materials, and convert them into HTML pages to complete functions
  • 4 after the completion of the function, their own test whether there is a Bug
  • 5 By the tester to test your function, when the test out of the Bug, will be through zen tao such project management system, to raise the Bug
  • 6. Modify the bugs proposed by your own testers
  • 7. Eventually, the project will go live when there are no bugs
Product Manager requirements output: Product prototype + requirements document prototype design software: Axure, ink knife UI (design) design the prototype given by the product manager into a nice UI draft FE (front-end) front-end product prototype + requirements document + UI design ===> HTML page BE (back-end) back-end to provide the front end data interface tester Product prototype + requirement document + UI design draft to test the functions we wrote. If you find that your functions are inconsistent with the requirements, it will show that there are bugs in addition to the Bug. Then, the testers will mention the Bug Bug system: Zen Way project manager (management technology) technical breakthrough, communicate with other project team members, assign tasks, etcCopy the code

Scoped in vUE single file components

  • Action:styleaddscopedProperty, the style only takes effect on the structure in the current component and does not affect other components

Lang in vue single file component

  • Function: Addlang="less"Then you can use the less syntax
  • But you need to install it yourself:npm i -D less less-loader

The Vetur plug-in is used in VSCode to format a template for a single file component

  • Open Settings, configure:"vetur.format.defaultFormatter.html": "js-beautify-html"

Description of interface invocation

  • Note: All interfaces need to pass a token, and only when the correct token is passed will the server return data to the front end
  • If it’s not passedtoken, the server will return401To tell the interface caller that there is no permission to access the interface

cookie+session VS token

  • Kill status: from session to token

Use the Git

  • Remote branch
# Clone warehouse
git clone[Warehouse address]# pushGit push [repository address] master# Simplify push addressesGit push -u XX master git push -u XX masterThe first time you run the above two commands, you only need to enter the following commands
git push XX

# pullGit pull [repository address] git pull XX masterCopy the code

Route parameter pagination

  • 1 Set the page routing parameters. The parameters are optional
    • If the parameter is optional, the route can match:/XXor/XX/3
  • 2 To use route paging, there are two situations to handle:
  • 3 The first way: To access the page, you need to obtain the data of the corresponding page according to the page number in the current route parameters
  • 4 Second: Click the paging component to get the data. You need to do two things:
    • 4.1 Getting the data from the current page (call the method to get the data)
    • $router.push(); $router.push();
  • 5. Click the paging button to get the second idea:
    • 5.1 Clicking the pagination button triggers the pageChange event of the grouping item
    • $router.push() {this.$router.push();
    • 5.3 After the route changes, $route in watch monitors the route changes
    • 5.4 in$route(to) {}Method, to get the current page number by the argument to, and then call the get data method again to get the data for the current page

Project packaging and optimization

  • Package command:npm run build

According to the need to load

  • 1 changerouter/index.jsThe syntax for importing components in
/ / use:
const Home = (a)= > import('@/components/home/Home')
/ / replace:
// import Home from '@/components/home/Home'

// Give a name to the JS file that is packaged for production
const Home = (a)= > import(/* webpackChunkName: 'home' */ '@/components/home/Home')

// Use the same chunkName to package goods and goods-add components together
const XX = (a)= > import(/* webpackChunkName: 'XX' */'@/components/XX')
const XXX = (a)= > import(/* webpackChunkName: 'XX' */'@/components/XXX')
Copy the code
  • 2 (This step can be omitted) modified/build/webpack.prod.conf.jsThe chunkFilename
{
  // [name] replace [id]
  chunkFilename: utils.assetsPath('js/[name].[chunkhash].js')}Copy the code

Use the CDN

  • Open source project CDN Acceleration service

  • 1. Import the JS file provided by CDN in index.html

  • 2 / build/webpack. Base. Conf., js in front (resolve) add configuration externals

  • Note: After importing the element-UI style file via CDN, there is no need to import the Element-UI CSS file in main.js. So, just comment out the imported element-UI style in main.js

  • Externals configuration:

externals: {
  // key: the name followed by the import package syntax from
  // Value: represents the variable name in the global environment when script is introduced into JS files
  vue: 'Vue'.axios: 'axios'.'vue-router': 'VueRouter'.'element-ui': 'ELEMENT'.moment: 'moment'.echarts: 'echarts',}import ElementUI from 'element-ui'
Copy the code

Commonly used package CDN

  • vue

  • vue-router

  • axios

  • element-ui JS

  • element-ui CSS

  • moment

  • Description:

    • 1 Search for the CDN provided in the official document
    • 2 If not, inhttps://www.bootcdn.cn/Or another CDN provider
<! -- Include the Quill library -->
<script src="https://cdn.bootcss.com/quill/1.3.4/quill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<! -- Quill JS Vue -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-quill-editor.js"></script>
<! -- Include stylesheet -->
<link href="https://cdn.bootcss.com/quill/1.3.4/quill.core.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/quill/1.3.4/quill.snow.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/quill/1.3.4/quill.bubble.min.css" rel="stylesheet">
Copy the code

Remove the console

new webpack.optimize.UglifyJsPlugin({
  compress: {warnings: false.drop_debugger: true.drop_console: true}})Copy the code

Cache and preserve component state

  • keep-alive
  • Solution: Usekeep-alive, the steps are as follows:
{path: '/', name: 'home', Component: home, // Needs to be cached meta: {keepAlive: true}} 2 Modify the route egress and replace it with the following format: Determines whether the route is cached based on whether the keepAlive attribute exists in the meta<keep-alive>
  <! -- Here is the view component that will be cached -->
  <router-view v-if="$route.meta.keepAlive">
  </router-view>
</keep-alive>

<! -- Here is an uncached view component -->
<router-view v-if=! "" $route.meta.keepAlive">
</router-view>
Copy the code

Example Enable the History mode of the route

  • By adding it to a routemode: 'history'You can remove the # from the browser address bar
  • During development, you just need to add this configuration
  • However, after the project is packaged, there is a problem
After removing / / #, address to: http://localhost:1111/home, then, the server need to correctly handle/goods to correct response content, but the/home is not a server interface, but used to realize VueRouter routing function in your browserCopy the code

Back-end processing

  • Procedure 1 Process static resources first
  • 2 Requests for non-static resources are processed uniformly and return index.html
  • When the browser opens index.html and loads the js file of the route, the route will resolve the /login path in the URL without the #

To be continued in the

[Project address github.com/whylisa/vue…]