The purpose of writing this article is to make myself more familiar with the basis and project structure of VUE-CLI scaffolding construction project. Secondly, I hope that my learning process can help students who have doubts. If there are any mistakes, I also hope to get advice

Js library (vue. Js, react.js). The configuration of the project framework is basically the same

That’s why I have a big face

Vue Project Framework (PART 1)

The Framework of the VUE Project (Part 2)

React With Your Bare hands

React Project Framework (Part 2)

The purpose of this time is to follow up on the previous article by adding vue-Router to the framework, which is packaged for services based on third-party toolkits

A, routing,

Routing is the key to flexible switching between VUE components. Vue-router is the official route of vUE.

1. Introduce the vue – the router

Download a.
cnpm i vue-router --save-dev
Copy the code
B. instantiation

Create a router folder under SRC, create index.js under router, and create login.vue under components

import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

const router = new VueRouter({
  mode: "hash".routes: [{path: "/".name: "index".components: require(".. /components/hello.vue")}, {path: "/login".name: "login".components: require(".. /components/login.vue")}]})export default router
Copy the code
C. a mount

Before we finish here, we also need to mount the Router object to the root instance, in index.js, as follows

import router from "./router/index";

new Vue({
  el: "#app",
  router,
  render: h => h(App)
});
Copy the code

Add router-view tags where you need to render components

<template>
  <div>
    <router-view />
  </div>
</template>
Copy the code

Now you can access it in your browser

2. Use navigation guard to realize login control

However, sometimes our project requires that you cannot enter the home page without logging in. This function can be realized by using the front navigation guard of the Router

BeforeEach is the front navigator’s hook function that takes three arguments, to, from, and next

  • To: indicates the route object to be entered
  • From: indicates the outgoing route object
  • Next: Be sure to call this method to resolve the hook

I use H5 localStorage simulation cookie to save user information, here is just a test, if you like cookies, you can use your favorite cookie package, personally like JS-cookie

The idea is that when you log in, after verifying the correct username and password, you set up a string as a token and store it locally. The token is used to avoid logging in next time (this is a security issue).

In the router/index. In js

Route. beforeEach((to, from, next) => {// If this parameter is set during loginif(localStorage.getItem("token") != null){
      if(to.name == 'login'){// If you still access the login page, go to the homepage next({path:'/'})}elseNext ()}}else{// If this value is not set, it is null, indicating that there is no login, leading to the login pageif (to.name == "login") {
      next();
    } else {
      if (to.name == "login") { next(); // The login page must be released, otherwise it will enter an infinite loop}else {
            next({ path: "/login"}); }}}})Copy the code

While this is pretty good, it’s still not as flexible and powerful as vue-Router. In other words, there are a lot of things that can be done here. For example, if the project is a management system, the side column of the home page may be different because of different roles. Although it seems to be a very complicated process, it is only a few steps after careful analysis, and interested students can see here

3. Use named views for special layouts

A. Peer route

Sibling routing means that the two router-view tags are side-by-side and the two tags are named with the name attribute. Therefore, named views are named router-view views

// app.vue
<router-view name="navbar"></router-view>
<router-view name="main"></router-view> import {NavbarComponent, MainComponent} from"@/components"
const router = new VueRouter({
  routes: [
    {
      path: '/', components: {navbar: NavbarComponent, main: MainComponent}}]}) <template> <div> <router-view name= in app.vue"navbar" />
    <router-view name="main" />
  </div>
</template>
Copy the code

And here’s what you see

B. Set routine by

Nested layout is more common in management systems. The layout we often use is nested layout

// This time I define the route {path:"/index",
  name: "index",
  components: require("@/components/hello"),
  children: [
    {
      path: "login",
      name: "login",
      components: require("@/components/login"}]}, // and write hello component <template> <div class="test">index page
    <router-view></router-view>
  </div>
</template>
Copy the code

I went to http://localhost:8080/#/index/login in my browser and got to the following page

Interaction with the server

The official recommendation for interacting with the server is AXIOS, which is ajax-based and simple to use

1. Download

cnpm i axios --save-dev
Copy the code

2. Introduction

There are two ways to use axios: one is to use the AXIos object to call the GET or POST request method, and the other is to use the AXIos API, using the axios() function, which takes a configuration object options

axios.get('/getUser').then(data=>{// request success handler console.log(data)}).catch(err=>{// request failure handler console.log(err)})'/getUser',
    mothod: 'get'}). Then (data=>{// successful handler console.log(data)}). Catch (err=>{// failed handler console.log(err)})Copy the code

3. Packaging

In actual projects, we often need to deal with the result of the sent request or the response of the server. When there are many requests, it is very tedious to deal with one by one, and it is also not practical. We hope that it has been handled globally, and we are only responsible for sending the request and receiving the response of the server.

Fortunately, Axios provides this method.

Create the HTTP folder under SRC and create the request.js file

import axios from "axios"; // Re-instantiate an axios instance that overrides all default attributes const server = axios.create({baseURL:"/api",
  timeout: 5000,
  heads: { 'content-type': 'application/x-www-form-urlencoded'}}); // Or by modifying the instance's defaults properties, the two methods are equivalent to server.defaults.baseurl ="/api";
server.defaults.timeout = 5000;

export default server
Copy the code

Not only that, but we also want to send each request with the token value set by the login. When we receive the server error, we can make corresponding feedback. For example, the status code returned is 404, so that we can navigate to the 404 page

Here you can use the interceptor object provided by Axios as follows

/ / set the interceptor / / request interceptor for server interceptors. Request. Use (config = > {config. Headers. Token =localStorage.get("token");
    return config;
  },
  err => {
    returnPromise.reject(err); }); / / response interceptor server. Interceptors. Response. Use (response = > {return response;
  },
  err => {
  switch (err.response.status) {
      case 404:
        router.push({
          path: "/ 404"
        });
        break;
      case 504:
        router.push({
          path: "/ 504"
        });
        break;
    }
    returnPromise.reject(err); });Copy the code

Remember to add the error corresponding page and route ++

    // router/index.js
    {
      path: "/ 404",
      name: "404",
      components: require(".. /components/404.vue")
    },
    {
      path: "/ 504",
      name: "504",
      components: require(".. /components/504.vue")}Copy the code

Simple encapsulation is done; you just need to introduce it where it is used

4. Cross-domain proxy

Here we have to talk about cross-domain, which is something that can’t be avoided in front – and – back – end separation projects. For projects with separate front and back ends, it is cross-domain because browsers have the same origin policy security restrictions to prevent cross-site request forgery and cross-site scripting attacks

Cross-domain is a violation of the same origin policy, and Webpack-dev-server provides a solution for cross-domain

// in webpack.config.js devServer devServer: {port: 8080, proxy: {"/api": {
            target: "http://127.0.0.1:10000"// The destination address of the agent changeOrigin:true// Enable cross-domain}}}Copy the code

Axios also provides a cross-domain solution, but it is more complicated and requires the setup of the backend students

5. Use

Here is an example to illustrate how to use it. == Of course, I do not have write login function, so remember to comment out the route front guard hook function, otherwise it cannot jump ==

The logic is as follows: when accessing the home page, a request is automatically sent to obtain the user list. The request method is GET

// hello.vue
import axios from '.. /http/request.js'

export default {
  created() {
    axios({
        url: '/getUsers',
        method: 'get'
    })
    .then(data =>{console.log(data)})
    .catch(err =>{console.log(err)})
  }
};
Copy the code

If you already have back-end server support, but did not write the corresponding route, it will report an error timeout in the console, that is, the status code is 504, the page will automatically redirect to 504 page

If, like me, you don’t have a back-end server, the console will report a 404 error, meaning the status code is 404, and the page will automatically redirect to 404

Third, the perfection of details

So far, the project framework function has been finished, but it is not formal in many places. Compared with cli created projects, the production environment and development environment are not separated; Second, a qualified generic framework should have esLint syntax checks; While mainstream browsers are starting to support ES6 test syntax, it’s a good idea to add babel-Loader to convert ES6 syntax into ES5 syntax.

1. babel-loader

Babel-loader mainly converts ES6 syntax to browser-compatible ES5 syntax, but there are many JS files in the project node_moudles, all conversion will slow down the speed and make the project cannot run, so you need to configure the path of the conversion file or block the path that does not need to be converted

Download a.

When converting ES2015 syntax to ECMAScript 5 syntax, Babel needs some helper functions, such as _extend. By default, Babel inlines these helper functions in every JS file, so that when there are many files, the project will be very large.

So Babel provides transform-Runtime to “move” these helper functions into a separate module called Babel-Runtime, which reduces the size of the project file

// @babel/plugin-transform-runtime is a plugin, babel-plugin- is a preset, and the plugin is preset. CNPM I babel-core babel-loader babel-env -env @babel/plugin-transform-runtime --save-devCopy the code

* * * * * * * * * * * * * * * * * * * * *

 TypeError: this.setDynamic is not a function
Copy the code

I solved the problem by downloading @babel/ plugin-transform-Runtime

B. configure
Babel-loader module: {rules: [{rule: [{rule: [{rule: [{rule: [{test: /\.js$/,
            loader: "babel-loader",
            // include: [path.resolve("src")],
            exclude: /node_modules/,
            options: {
                presets: ['env'], // env provides rules for syntax conversion, and here is the default Babel plugins: ['transform-runtime'}}}}Copy the code

Babel-loader does nothing by default, you need to specify the preset syntax for the plugin to work for you in the preset presets option. Babel has several preset scenarios for us, babel-env is the latest one, where babel-preset- is just a prefix, In addition, babel-Loader provides a rich set of plugins that do more than just that, as indicated in the plugins option under Options

Note: remember to download the ++ plugin to use

C. An example of the use of the. Babelrc file

Babel-plugin-syntax-dynamic-import is required, so download this package first and put it into plugins

CNPM I babel-plugin-syntax-dynamic-import --save-dev // under module rules: [{test: /\.js$/,
            loader: "babel-loader",
            include: [path.resolve("src")],
            exclude: [path.resolve("node_modules")],
            options: {
                presets: ['env'], // env provides rules for syntax conversion, and here is the default Babel plugins: ['@babel/transform-runtime'."syntax-dynamic-import"]}}]Copy the code

If you need a lot of plugins and need to configure them separately, you can create a. Babelrc file in the root directory of your project and replace options with json

// In the.babelrc file, you must have two options of array type: presets, plugins {"presets": ["env"]."plugins": ["@babel/transform-runtime"."syntax-dynamic-import"]}Copy the code

See here for more presets options

2. Eslint syntax checks

Eslint can be extremely unfriendly to beginners because of its strict requirements, which make many people want to shut it down, but it has to be said that ESLint is crucial for multi-player teams to ensure consistent code style

Download a.
CNPM I eslint-plugin-vue eslint-friendly-formatter eslint-loader eslint --save-dev // Otherwise Cannot find Module will be reported'eslint-plugin-vue'CNPM I eslint-plugin-vue-g will still be installed locallyCopy the code

B. the use of

In the WebPack configuration file, add new rules

    {
        test: /\.(js|vue)$/,
        loader: "eslint-loader",
        include: [path.resolve(__dirname, 'src')], // Specify the directory to check. Options: {// The configuration parameters here will be passed to esLint's CLIEngine formatter: require('eslint-friendly-formatter'// Specify a format for error reporting}}Copy the code

C. Configure rules

There are three ways to use lint on the esLint website (which is also the order in which rules are found)

  • Embed in code using comments
  • Add the eslintConfig field in package.json, where you specify the configuration
  • Use.eslintrc.* files (any suffix)

I chose to use.eslintrc.js just like everyone else, since it’s a JS file we need to expose an object just like we would a JS module file

Module. exports = {}Copy the code

Here I will only mention a few common or important configuration options. The official website supports The Chinese version, which is much friendlier than the Chinese version of Babel

/ / like this
module.exports = {
    // By default, ESLint will look for configuration files in all parent directories up to the root directory. This can be useful if you want all your projects to follow a specific convention, but can sometimes lead to unexpected results. To restrict ESLint to a specific project, set "root": true under the eslintConfig field in your project root package.json file or.eslintrc.* file. ESLint will stop looking in the parent directory once it finds "root": true in the configuration file.
    "root": true.// extends: Extends property. The value can be "esLint :recommended"," esLint :all" or a plugin or fileExtends:"eslint:recommended"."eslint:all"
        // "plugin:react/recommended"].// env: specifies the environment to run. The options are Browser, node, es6, etc. The values are Boolean, true means enabled, and the default is false
    "env": {
        "browser": true
    },
    // Plugins are plugins that need to be downloaded before being used in a vUE project
    "plugins": [
        "vue"
        // "react"].// rules: specifies syntax rules, which are divided into 0,1 and 2 levels, corresponding to off (off detection), WARN (only warning), and error (directly reporting errors).
    "rules": {
        "eqeqeq": "off"."curly": "error".// if structure must use {}
        "quotes": ["error"."double"]
        / / more rules here at https://www.jianshu.com/p/80267604c775
        // Here is the extends property of Rules
        // If your setup is like this
        // "eqeqeq": ["error", "allow-null"]
        // "eqeqeq": "warn"
        // The last configuration it generates is "eqeqeq": ["warn", "allow-null"]}},Copy the code
4. Set whether to enable ESLint

A good framework will definitely check whether esLint is enabled or not based on the configuration file. I define a configurable variable eslint in the configuration file that enables ESLint syntax-checking when its value is true, but the default value is false

const esLint = false

// Replace the configured eslint-loader. {test: /\.js$/.exclude: /node_modules/.loader: "babel-loader"}, esLint? {test: /\.(js|vue)$/.loader: "eslint-loader".include: [path.resolve(__dirname, "src")].// Specify the directory to check
        options: {
          // The configuration item arguments here will be passed to esLint's CLIEngine
          formatter: require("eslint-friendly-formatter") // Specify a format for error reporting}}, {},...Copy the code

3. Use an absolute path

I have always used relative paths in the project, although there are friendly code hints, but once you change the location of the file, errors will continue until you have changed all paths correctly, the best thing to do is to use absolute paths and alias paths to make writing easier

resolve: {
    alias: {..."@": resolve("src")}}Copy the code

This way we can write paths in import using @ to indicate the root directory to SRC, followed by the rest of the path

import hello from "@/components/hello.vue";
Copy the code

4. Drop the suffix

resolve: {
    extensions:  [".js".".vue".".json"], // You can also add.css,.less,.sass, which are all allowed.Copy the code

Now you don’t have to write a suffix when you import.js or.vue files

import hello from "@/components/hello";
Copy the code

conclusion

It took me two days to finish the framework. To be honest, all the details I didn’t care about before are now transparent. Of course, this is just a small step for me

It is also worth mentioning that @vue/cli3, the third version of vue scaffolding, was released some time ago. I looked at the documentation and found that it only replaced the plugin configuration with vue.config.js. It will automatically generate a webpack.config.js file based on the configuration of vue.config.js, we just need to provide the plug-in and set whether to enable it

If you see this, then your perseverance tells me that you will be more skilled in the future