vue quick start series - building a project with vue cli 3

See other chapters:

vue quick start series

Use Vue cli 3 to build a project (Part 1)

We have learned a mature scaffold( vue-cli ), the author hopes to quickly build the system (or project) through this scaffold. The best way to start building is to learn from excellent projects and follow the gourd.

Here through research vue-admin-template Project, introduce element UI, axios, mock, iconfont, nprogress, permission control, layout, multi environment (. env), cross domain, vue.config.js one by one, and build our own architecture step by step.

Tip: Vue element admin is an excellent background and front-end solution. It shares some components or experiences that you usually use. Vue admin template is a simple version of it.

Note: due to the long length, it is decided to split the text into two parts

Template project - Vue admin template

The Vue admin template is developed based on the Vue cli webpack template and introduces the following dependencies:

  • Element UI is hungry? vue pc UI framework
  • axios is a popular and easy-to-use request library that supports Promise
  • JS cookie is a lightweight JavaScript library to handle cookies
  • normalize.css format CSS
  • nprogress lightweight global progress bar control
  • vuex official status management
  • Vue router official route
  • iconfont Icon Font
  • Permission control
  • lint

Tip: Vue cli webpack template:

  • This template is the main template of Vue cli verison 2. *
  • Vue cli 3 contains all the functionality (and more) provided by this template
  • Vue cli 3 is here, and this template is now considered deprecated

Download the project and start:

> git clone https://github.com/PanJiaChen/vue-admin-template.git vue-admin-template
> cd vue-admin-template
vue-admin-template> npm i
vue-admin-template> npm run dev

> vue-admin-template@4.4.0 dev
> vue-cli-service serve
...

Create project

Our project - myself Vue admin template

Creating projects through Vue cli

// Project defaults are ` [Vue 2] less`, `babel`, `router`, `vuex`, `eslint`
$ vue create  myself-vue-admin-template

The directory structure is as follows:

 myself-vue-admin-template
- mode_modules
- public
    - favicon.ico
    - index.html
- src
    - assets
        - logo.png
    - components
        - HelloWorld.vue
    - router
        - index.js
    - store
        - index.js
    - views
        - Aobut.vue
        - Home.vue
    - App.vue
    - mains.js
- .browerslistrc
- .editorconfig
- .eslintrc.js
- .gitignore
- babel.config.js
- package-lock.json
- package.json
- README.md

Our project Vs template project

The project Vue admin template has more directories and files than myself Vue admin template, and others are the same:

vue-admin-template
+ build
+ mock
+ src/api
+ src/icons
+ src/layout
+ src/styles
+ src/utils
+ src/permission.js
+ src/settings.js
+ .env.development
+ .env.production
+ .env.staging
+ .travis.yml 
+ jest.config.js 
+ jsconfig.json
+ postcss.config.js
+ README-zh.md
+ vue.config.js

The @ vue/cli used is 4.x:

//  myself-vue-admin-template
"devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
// vue-admin-template
"devDependencies": {
    "@vue/cli-plugin-babel": "4.4.4",
    "@vue/cli-plugin-eslint": "4.4.4",
    "@vue/cli-plugin-unit-jest": "4.4.4",
    "@vue/cli-service": "4.4.4",
    "@vue/test-utils": "1.0.0-beta.29",

element-ui

How do template projects use element UI

// package.json
"dependencies": {
    "element-ui": "2.13.2",
}
// main.js
// ps: irrelevant code not shown
import Vue from 'vue'

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// Internationalization - English
import locale from 'element-ui/lib/locale/lang/en' // lang i18n

import App from './App'

// set ElementUI lang to EN
Vue.use(ElementUI, { locale })
// If you want the Chinese version of element UI, declare it as follows
// Vue.use(ElementUI)

new Vue({
  el: '#app',
  render: h => h(App)
})
  • The introduction of Element here is complete, and the other is on-demand
  • The Element component uses Chinese by default, and English is used here
    • The internationalization of element is actually the internationalization of components in element (see the file node_modules / element UI / lib / locale / Lang / EN)

Add element UI

The idea is as follows:

  • Complete introduction of element
  • No translation is required. Chinese is used by default
  • Install the element UI using the plug-in provided by Vue cli

Direct installation via Vue cli

myself-vue-admin-template> vue add vue-cli-plugin-element

📦  Installing vue-cli-plugin-element...

✔  Successfully installed plugin: vue-cli-plugin-element
// to configure
? How do you want to import Element? Fully import
? Do you wish to overwrite Element's SCSS variables? No
? Choose the locale you want to load zh-CN

✔  Successfully invoked generator for plugin: vue-cli-plugin-element

Note: you can also use the Vue cli GUI to install the plug-in Vue cli plugin element

Then you can view the code modified by Vue cli through git status:

myself-vue-admin-template> git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   package-lock.json
        modified:   package.json
        modified:   src/App.vue
        modified:   src/main.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        src/plugins/

no changes added to commit (use "git add" and/or "git commit -a")

The core code is the same as that in the template project, except that the introduction of element is encapsulated in the plugins/element.js file.

Start the service, and the page displays:

...
if Element is successfully added to this project, you'll see an <el-button> below // {1}

...

We will see an element button under the line ({1}), indicating that the element UI was successfully introduced.

axios

How do template projects use axios

// package.json
"dependencies": {
    "axios": "0.18.1",
}

Encapsulate axios:

// src/utils/request.js
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'

// create an axios instance
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})

// request interceptor
service.interceptors.request.use(
  config => {
    ...
  },
  error => {
    ...
  }
)

// response interceptor
service.interceptors.response.use(
  response => {
    const res = response.data

    // if the custom code is not 20000, it is judged as an error.
    if (res.code !== 20000) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        // to re-login
        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
          confirmButtonText: 'Re-Login',
          cancelButtonText: 'Cancel',
          type: 'warning'
        }).then(() => {
          ...
        })
      }
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service
// api/table.js
import request from '@/utils/request'

export function getList(params) {
  return request({
    url: '/vue-admin-template/table/list',
    method: 'get',
    params
  })
}
// views/table/index.vue
<script>
import { getList } from '@/api/table'
...
</script>

Add axios

Vue cli installation plug-in
myself-vue-admin-template> vue add vue-cli-plugin-axios

📦  Installing vue-cli-plugin-axios...
...
✔  Successfully installed plugin: vue-cli-plugin-axios
\myself-vue-admin-template> git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   package-lock.json
        modified:   package.json
        modified:   src/main.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        src/plugins/axios.js

Among them, the Plugin in axios.js indicates that it has been discarded in vscode, so simply move the information about Axios in the template project

Copy axios in template project

Tip: first restore the code of installing axios from Vue cli

myself-vue-admin-template> npm i -D axios@0.18.1

Create a new request.js (from the template project utils/request, comment out the code related to permissions):

// utils/request.js
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
// import store from '@/store'
// import { getToken } from '@/utils/auth'

// create an axios instance
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})

// request interceptor
service.interceptors.request.use(
  config => {
    // do something before request is sent

    // if (store.getters.token) {
    //   // let each request carry token
    //   // ['X-Token'] is a custom headers key
    //   // please modify it according to the actual situation
    //   config.headers['X-Token'] = getToken()
    // }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
  */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  response => {
    const res = response.data

    // if the custom code is not 20000, it is judged as an error.
    if (res.code !== 20000) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        // to re-login
        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
          confirmButtonText: 'Re-Login',
          cancelButtonText: 'Cancel',
          type: 'warning'
        }).then(() => {
          // store.dispatch('user/resetToken').then(() => {
          //   location.reload()
          // })
        })
      }
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

Tip: VUE_APP_BASE_API, please refer to multi environment - > base in this chapter_ url

Create a new table.js and define a request:

// api/table.js
import request from '@/utils/request'

export function getList(params) {
  return request({
    url: '/vue-admin-template/table/list',
    method: 'get',
    params
  })
}

Reference api/table.js in About.vue:

// views/About.vue
...
<script>
import { getList } from '@/api/table'
export default {
  created() {
    this.fetchData()
  },
  methods: {
    fetchData() {
      getList().then(response => {
        console.log('Load data', response);

      })
    }
  }
}
</script>

Tip: the following information may be encountered when saving the code. You can disable eslint loader during production build by configuring lintOnSave

myself-vue-admin-template\src\views\About.vue
   7:1   error  More than 1 blank line not allowed         no-multiple-empty-lines
  12:10  error  Missing space before function parentheses  space-before-function-paren
  16:14  error  Missing space before function parentheses  space-before-function-paren
  18:38  error  Extra semicolon                            semi
  20:7   error  Block must not be padded by blank lines    padded-blocks

✖ 5 problems (5 errors, 0 warnings)
  5 errors and 0 warnings potentially fixable with the `--fix` option.
// vue.config.js
module.exports = {
  lintOnSave: process.env.NODE_ENV !== 'production'
}

Introduce About.vue into App.vue, restart the server, and find that the page (about. Vue) will report 404 error, so next we have to introduce mock.

Tip: please refer to the add mock section of this article.

After adding the mock, start the service. The page will no longer output 404 and other prompts. The console will output the simulated data in the mock. So far, it indicates that both axios and mock have taken effect.

mock

How do template projects use mock

The npm package used here is mockjs. The requests to be intercepted are uniformly placed in the mock directory. Finally, mock is introduced into main.js.

There are about the repair of mock defects and mock-serve.js, which is a little complicated.

Here are some core codes:

// main.js
/**
 * If you don't want to use mock-server
 * you want to use MockJs for mock api
 * you can execute: mockXHR()
 *
 * Currently MockJs will be used in the production environment,
 * please remove it before going online ! ! !
 */
if (process.env.NODE_ENV === 'production') {
  const { mockXHR } = require('../mock')
  mockXHR()
}
// mock/index.js
const Mock = require('mockjs')
const { param2Obj } = require('./utils')

const user = require('./user')
const table = require('./table')

const mocks = [
  ...user,
  ...table
]

// for front mock
// please use it cautiously, it will redefine XMLHttpRequest,
// which will cause many of your third-party libraries to be invalidated(like progress event).
function mockXHR() {
  // mock patch
  // https://github.com/nuysoft/Mock/issues/300
  Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
  ...
}

module.exports = {
  mocks,
  mockXHR
}
// vue.config.js
devServer: {
    before: require('./mock/mock-server.js')
  },
// api/table.js
import request from '@/utils/request'

export function getList(params) {
  return request({
    url: '/vue-admin-template/table/list',
    method: 'get',
    params
  })
}
// views/table/index.vue
import { getList } from '@/api/table'

Add mock

The author uses another method to install it directly through Vue cli plug-in:

myself-vue-admin-template> vue add vue-cli-plugin-mock

📦  Installing vue-cli-plugin-mock...

✔  Successfully installed plugin: vue-cli-plugin-mock

The modified documents are:

myself-vue-admin-template> git status
        modified:   package-lock.json
        modified:   package.json

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        mock/

According to the introduction of Vue click plugin mock in npm, there are two ways to write mock here. The automatically generated code uses writing method 1. In order to be the same as that in the template project, the author changes mock/index.js to writing method 2.

// Writing method I
module.exports = {
  'GET /api/user': {
    // obj
    id: 1,
    username: 'kenny',
    sex: 6,
  },
  ...
};

// Writing method 2
module.exports = [
  {
    path: '/api/user',
    handler: (req, res) => {
      return res.json({ username: 'admin', sex: 5 });
    },
  },
  ...
];

mock has been installed and can be used directly. For specific usage, see the section adding axios above.

Tip: mock can be further optimized, just like a template project:

  • The mock file is divided into modules and integrated through mock/index.js
  • mock works only in the development environment
// vue-admin-template/src/main.js
if (process.env.NODE_ENV === 'production') {
  const { mockXHR } = require('../mock')
  mockXHR()
}
// vue-admin-template/mock/index.js
const Mock = require('mockjs')

const user = require('./user')
const table = require('./table')

const mocks = [
  ...user,
  ...table
]

module.exports = {
  mocks,
  mockXHR
}

iconfont

In the past, icons were pictures. Later, Sprite pictures appeared. For example, many small icons were placed in one picture to reduce requests. In later projects, font libraries, such as font awesome and iconfont, are used instead of local images.

The landing page in the template project uses four icons

// login/index.vue
<svg-icon icon-class="user"/>

<svg-icon icon-class="password" />

<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />

Add iconfont

Download from the official website and have a preliminary experience

go iconfont Select two icons on the official website and put them in the shopping cart, then click to download the code. After decompression, double-click to open index.html to tell us how to use it. We choose the third method (Symbol).

Add iconfont to the project

New SvgIcon.vue:

// src/components/SvgIcon.vue
<template>
  <svg class="svg-icon" aria-hidden="true">
    <use :xlink:href="iconName"></use>
  </svg>
</template>

<script>
export default {
  name: 'icon-svg',
  props: {
    iconClass: {
      type: String,
      required: true
    }
  },
  computed: {
    iconName() {
      return `#icon-${this.iconClass}`
    }
  }
}
</script>

<style>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>

Put iconfont.js into src/utils folder and modify main.js:

...
import './utils/iconfont.js'

//Introducing svg components
import IconSvg from '@/components/SvgIcon'

//Global registration icon svg
Vue.component('icon-svg', IconSvg)

Use icon, for example, in About.vue:

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <icon-svg icon-class="password" />
    <icon-svg icon-class="user" />
  </div>
</template>
reform

iconfont.js is as follows:

!function(e){var t,n,o,c,i,d='<svg><symbol id="icon-password" viewBox="0 0 1024 1024"><path d="M780.8 354.58H66..."  ></path></symbol><symbol id="icon-user" viewBox="0 0 1032 1024"><path d="M494.8704..."  ></path></symbol></svg>',...

If you need to add a third picture, you have to modify the iconfont.js file. Moreover, it is not intuitive which svg you need to use. It depends on the code.

svg files are directly imported into the template project, and there is no iconfont.js file. There are two related packages:

"devDependencies": {
    // Create SVG sprites
    "svg-sprite-loader": "4.1.3",
    // SVG vector graphics file optimization tool based on Nodejs
    "svgo": "1.2.2",
  },

We also change iconfont to this method. First, remove the previous code introduced into iconfont, and then install the plug-in Vue cli plugin SVG sprite through Vue cli:

myself-vue-admin-template> vue add vue-cli-plugin-svg-sprite                   

📦  Installing vue-cli-plugin-svg-sprite...
...
✔  Successfully installed plugin: vue-cli-plugin-svg-sprite

? Add SVGO-loader to optimize your icons before the sprite is created? Yes

🚀  Invoking generator for vue-cli-plugin-svg-sprite...
📦  Installing additional dependencies...

After the plug-in is successfully installed, we view the changed files:

myself-vue-admin-template> git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   package-lock.json
        // We learned that three packages were installed: svgo, svgo loader, Vue cli plugin SVG sprite
        modified:   package.json
        modified:   vue.config.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        // svg encapsulated components
        src/components/SvgIcon.vue

Tip: vue.config.js will add the following content:

module.exports = {
  // slightly
  chainWebpack: config => {
    config.module
      .rule('svg-sprite')
      .use('svgo-loader')
      .loader('svgo-loader')
  }
}

Next, we need to register svg components globally:

// main.js
import SvgIcon from '@/components/SvgIcon'// svg component

// register globally
Vue.component('svg-icon', SvgIcon)

Finally, we have to test whether iconfont works.

First download and save the svg file to the assets/icons directory, and then modify About.vue:

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <svg-icon name="password" />
    <svg-icon name="open" />
  </div>
</template>

Restart the service, and the page successfully displays two svg images. So far, our iconfont has been successfully introduced.

nprogress

In the template project, if you try to switch, there will be a progress bar at the top of the page, which uses nprogress (the slender progress bar of Ajax'y application. Inspired by Google, YouTube and Medium).

Template item:

  "dependencies": {
    "nprogress": "0.2.0",
  }

Add nprogress

Since the Vue cli UI cannot find the corresponding plug-in of nprogress, we can only install it honestly through npm:

$ npm i -D nprogress

Next, use nprogress to add the following code to About.vue and restart the service to see the effect:

// About.vue
<script>
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// Turn off the load spinner by setting it to false.
NProgress.configure({ showSpinner: false }) // NProgress Configuration

export default {
  created () {
    // Simply call start() and done() to control the progress bar.
    NProgress.start();
    // You can try calling NProgress.done() directly or not
    setTimeout(function(){
      NProgress.done()
    }, 10000)
  },
}
</script>

Tip: directly search the keyword NProgress in the template project to find the above code. NProgress.start() and NProgress.done() appear in the permission.js file in the template project and are also in the route.

normalize.css

normalize.css, a modern alternative to CSS Reset

Add normalize.css

$ npm i -D normalize.css

Introduce normalize.css into the entry file

// main.js
import 'normalize.css/normalize.css' // A modern alternative to CSS resets

When the service is restarted, the margin of the body will be reset to 0, indicating that it has taken effect.

js-cookie

JS cookie, a simple and lightweight JavaScript API for handling cookies.

How do template projects use js cookies

Relevant codes are as follows:

// package.json
"dependencies": {
    "js-cookie": "2.2.0",
  },
// src/utils/auth.js
import Cookies from 'js-cookie'

const TokenKey = 'vue_admin_template_token'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

Add JS cookie

$ npm i -D js-cookie

Use the following in About.vue:

// About.vue
<script>
import Cookies from 'js-cookie'

export default {
  created () {
    Cookies.set('sex', 'man')
    alert(Cookies.get('sex'))
  },
}
</script>

Restart the service, and the man page pops up, indicating that the import is successful.

other

npm must use TLS 1.2 or higher

The errors reported by npm i on a certain day are as follows:

npm notice Beginning October 4, 2021, all connections to the npm registry - including for package installation - must use TLS 1.2 or higher. You are currently using plaintext http to connect. Please visit the GitHub blog for more information: https://github.blog/2021-08-23-npm-registry-deprecating-tls-1-0-tls-1-1/  

You can view this article: npm registry is deprecating TLS 1.0 and TLS 1.1

Uncaught (in promise) Error: Redirected when going from

Uncaught (in promise) Error: Redirected when going from "/login" to "/" via a navigation guard.

You can view this article: Uncaught (in promise) Error: Redirected when going from

See other chapters:

vue quick start series

Tags: Vue

Posted on Fri, 12 Nov 2021 03:47:18 -0500 by mapexdrum