Build a vue-cli4 + webpack mobile terminal framework (out of the box)

Time:2021-10-20

brief introduction

This is a mobile terminal framework based on vue-cli4, which includes common project configuration, component packaging and webpack optimization methods, which can be used for rapid development.

Technology stack: vue-cli4 + webpack4 + vant + Axios + less + postcss-px2rem

Source codegithub.com/Michael-lzg…

//Installation dependency
npm install

//Local startup
npm run dev

//Production packaging
npm run build

A year or two ago, vue-cli3 had reached version 3.0 +, but because the old project was used to the scaffold of vue-cli2, I also wrote an article beforeBuild a Vue cli mobile terminal H5 development templateThis paper briefly summarizes the development skills of mobile terminal.

Recently, I upgraded Vue cli4 scaffold and found that it has been upgraded to version 4.0 +. I think it is necessary to use Vue cli4 for development in new projects. In addition, I have a further understanding of webpack recently, so I combined Vue cli4 and webpack to build a mobile end framework for out of the box use. It mainly includes the following technical points:

  • Vue-cli4 scaffold
  • Vant on demand introduction
  • Mobile terminal REM adaptation
  • Axios interception encapsulation
  • Util tool class function encapsulation
  • Vue router configuration
  • Login permission verification
  • Multi environment variable configuration
  • Vue.config.js configuration
  • Toast component encapsulation
  • Dialog component encapsulation
  • Cross domain proxy settings
  • Visual analysis of Web pack packaging
  • CDN resource optimization
  • Gzip Packaging Optimization
  • Add skeleton screen to home page

For more webpack optimization methods, please refer togithub.com/Michael-lzg…

Configure vant

Vant is a lightweight and reliable mobile Vue component library, which is very suitable for mobile development based on Vue technology stack. For a long time in the past, the mobile UI framework I used was vux. Later, because vux does not support vue-cli3, it switched to vant. It has to be said that vant is much better than vux in terms of interactive experience and code logic, and vant has fewer holes.

If all the third-party UI components are introduced, for example, the packaging volume will be too large and the white screen time of the home page will be too long, so it is very necessary to load on demand. Vant also provides a way to load on demand.babel-plugin-importIs a Babel plug-in, which will automatically convert the writing method of import to on-demand import during compilation.

1. Installation dependency

npm i babel-plugin-import -D

2. Configure the. Babelrc or babel.config.js file

//Add configuration in. Babelrc
{
  "plugins": [
    ["import", {
      "libraryName": "vant",
      "libraryDirectory": "es",
      "style": true
    }]
  ]
}

//For users using babel7, you can configure it in babel.config.js
module.exports = {
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
};

3. On demand import

You can directly introduce vant components into the code, and the plug-in will automatically convert the code into the on-demand import form in mode 2

import Vue from 'vue'
import { Button } from 'vant'

Vue.use(Button)

REM adaptation

Mobile terminal adaptation is something that we have to face in the development process. Here, we use px2rem loader in postcss to convert PX in our project into REM in a certain proportion, so that we can write PX to the label on the blue lake.

We set the HTML word and font to 100px, and many people choose to set it to 375px, but I think the converted rem is not accurate enough, and we can’t quickly calculate its original PX value when debugging the code on the console. If you set 1rem = 100px, then the 0.16rem and 0.3rem we see will quickly calculate the original 16px and 30px.

The specific steps are as follows;

1. Installation dependency

npm install px2rem-loader --save-dev

2. Configure vue.config.js as follows

css: {
    //CSS preset configuration item
    loaderOptions: {
      postcss: {
        plugins: [
          require('postcss-px2rem')({
            remUnit: 100
          })
        ]
      }
    }
  },

3. Set HTML and font size in main.js

function initRem() {
  let cale = window.screen.availWidth > 750 ? 2 : window.screen.availWidth / 375
  window.document.documentElement.style.fontSize = `${100 * cale}px`
}

window.addEventListener('resize', function() {
  initRem()
})

Axios request encapsulation

1. Set request interception and response interception

const PRODUCT_URL = 'https://xxxx.com'
const MOCK_URL = 'http://xxxx.com'
let http = axios.create({
  baseURL: process.env.NODE_ENV === 'production' ? PRODUCT_URL : MOCK_URL,
})
//Request interceptor
http.interceptors.request.use(
  (config) => {
    //Set token, content type
    var token = sessionStorage.getItem('token')
    config.headers['token'] = token
    config.headers['Content-Type'] = 'application/json;charset=UTF-8'
    //Request to display loading effect
    if (config.loading === true) {
      vm.$loading.show()
    }
    return config
  },
  (error) => {
    vm.$loading.hide()
    return Promise.reject(error)
  }
)
//Response interceptor
http.interceptors.response.use(
  (res) => {
    vm.$loading.hide()
    //The token is invalid. Log in again
    if (res.data.code === 401) {
      //Login again
    }
    return res
  },
  (error) => {
    vm.$loading.hide()
    return Promise.reject(error)
  }
)

2. Encapsulate get and post request methods

function get(url, data, lodaing) {
  return new Promise((resolve, reject) => {
    http
      .get(url)
      .then(
        (response) => {
          resolve(response)
        },
        (err) => {
          reject(err)
        }
      )
      .catch((error) => {
        reject(error)
      })
  })
}

function post(url, data, loading) {
  return new Promise((resolve, reject) => {
    http
      .post(url, data, { loading: loading })
      .then(
        (response) => {
          resolve(response)
        },
        (err) => {
          reject(err)
        }
      )
      .catch((error) => {
        reject(error)
      })
  })
}

export { get, post }

3. Mount the get and post methods on the Vue instance.

// main.js
import { get, post } from './js/ajax'
Vue.prototype.$http = { get, post }

Tool class function encapsulation

1. Add method to prototype chain of Vue instance

export default {
  install (Vue, options) {
    Vue.prototype.util = {
      method1(val) {
        ...
      },
      method2 (val) {
       ...
      },
  }
}

2. Register in main. JS via Vue. Use()

import utils from './js/utils'
Vue.use(utils)

Vue router configuration

At ordinary times, many people can configure path and component for Vue router to realize route jump. In fact, Vue router can do many things, such as

  • Routing lazy load configuration
  • Change the title of a single page application
  • Login permission verification
  • Page cache configuration

Routing lazy load configuration

3 ways to load routes on demand (lazy loading of routes) in Vue project:

//1. Vue asynchronous component technology:
{
  path: '/home',
  name: 'Home',
  component: resolve => reqire(['../views/Home.vue'], resolve)
}

//2. ES6 proposal import ()
{
  path: '/',
  name: 'home',
  component: () => import('../views/Home.vue')
}

//3. Require. Ensure () provided by webpack
{
  path: '/home',
  name: 'Home',
  component: r => require.ensure([],() =>  r(require('../views/Home.vue')), 'home')
}

This project adopts the second method for subsequent webpack packaging optimization.

Change the title of a single page application

Since a single page application has only one HTML, the title of all pages will not be changed by default, but we can add relevant attributes to the routing configuration, and then change the title of the page through JS in the routing guard

router.beforeEach((to, from, next) => {
  document.title = to.meta.title
})

Login permission verification

In applications, there are usually the following scenarios, such as shopping malls: some pages can be accessed without login, such as home page, commodity details page, etc., which can be seen by users in any case; But there are also some that can only be accessed after logging in, such as personal center, shopping cart, etc. At this point, you need to control the page access.

In addition, for some items that need to record user information and login status, login permission verification is also required to prevent people with ulterior motives from opening the page by directly accessing the URL of the page.

At this point. The routing guard can help us with login verification. The details are as follows:

1. Configure the auth property of the meta object of the route

const routes = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/Home.vue'),
    Meta: {Title: 'home page', keepalive: false, auth: false},
  },
  {
    path: '/mine',
    name: 'mine',
    component: () => import('../views/mine.vue'),
    Meta: {Title: 'mine', keepalive: false, auth: true},
  },
]

2. Judge on the routing home page. Whento.meta.authbytrue(login is required) and there is no login information cache, you need to redirect to the login page

router.beforeEach((to, from, next) => {
  document.title = to.meta.title
  const userInfo = sessionStorage.getItem('userInfo') || null
  if (!userInfo && to.meta.auth) {
    next('/login')
  } else {
    next()
  }
})

Page cache configuration

In the project, there are always some pages that we want to cache after loading once. At this time, keep alive is used. Keep alive is an abstract component provided by Vue to cache components to save performance. As it is an abstract component, it will not be rendered as a DOM element after V page rendering.

1. The keepalive property value of the meta object of the route is configured to distinguish whether the page needs caching

const routes = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/Home.vue'),
    Meta: {Title: 'home page', keepalive: false, auth: false},
  },
  {
    path: '/list',
    name: 'list',
    component: () => import('../views/list.vue'),
    Meta: {Title: 'list page', keepalive: true, auth: false},
  },
]

2. Make cache judgment in app.vue

<div id="app">
  <router-view v-if="!$route.meta.keepAlive"></router-view>
  <keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
  </keep-alive>
</div>

Multi environment variable configuration

First, let’s learn about environment variables. Generally, our project will have three environments, development, test and production. We can create three files to configure environment variables in the root directory of the project.env.development.env.test.env.production

The environment variables file contains only the “key = value” pair of environment variables:

NODE_ENV = 'production'
VUE_ APP_ Env = 'production' // only Vue_ The environment variables at the beginning of app can be used directly in the project code

In addition to custom Vue_ APP_* In addition to variables, there are two available variables:

  • NODE_ Env: one of “development”, “production” or “test”. The specific value depends on the mode in which the application runs.
  • BASE_ URL: consistent with the publicpath option in vue.config.js, that is, the basic path to which your application will be deployed.

Let’s start configuring our environment variables

1. Create a new. Env. In the project root directory*

  • . env.development local development environment configuration
NODE_ENV='development'
VUE_APP_ENV = 'development'
  • Env.staging test environment configuration
NODE_ENV='production'
VUE_APP_ENV = 'staging'
  • Env.production formal environment configuration
NODE_ENV='production'
VUE_APP_ENV = 'production'

In order to configure more variables in different environments, we create a new config / index under the SRC file

//Introduce different configurations of process.env.node according to the environment_ ENV
const config = require('./env.' + process.env.VUE_APP_ENV)
module.exports = config

New under the same level directoryenv.development.jsenv.test.jsenv.production.js, configure the required variables inside.
Take env.development.js as an example

module.exports = {
  baseUrl: ' http://localhost:8089 ', // project address
  baseApi: ' https://www.mock.com/api ', // local API request address
}

2. Configure packaging commands

Scripts in package.json package commands in different environments

  • Start local through NPM run serve
  • Packaging test through NPM run test
  • Formal packaging through NPM run build
"scripts": {
  "dev": "vue-cli-service serve",
  "build": "vue-cli-service build",
  "test": "vue-cli-service build --mode test",
}

Vue.config.js configuration

From vue-cli3, we need to configure our project in vue.config.js for new scaffolds. Mainly including

  • File output location after packaging
  • Close the production environment soucemap
  • Configure REM to convert PX
  • Configure alias alias
  • Remove production environment console
  • Cross domain proxy settings

In addition, there are many configurations for optimized packaging, which will be discussed later.

module.exports = {
  Publicpath: '. /' // default is' / '

  //Where to output the constructed documents, our company requires
  outputDir: 'dist/static',

  //The directory where the generated static resources (JS, CSS, IMG, fonts) are placed.
  assetsDir: 'static',

  //Specify the output path of the generated index.html
  indexPath: 'index.html',

  //Whether to use the Vue build that contains the runtime compiler.
  runtimeCompiler: false,

  transpileDependencies: [],

  //If you don't need the source map of the production environment
  productionSourceMap: false,

  //Configure CSS
  css: {
    //Whether to use the CSS separation plug-in extracttextplugin
    extract: true,
    sourceMap: true,
    //CSS preset configuration item
    loaderOptions: {
      postcss: {
        plugins: [
          require('postcss-px2rem')({
            remUnit: 100,
          }),
        ],
      },
    },
    //Enable CSS modules for all CSS / pre processor files
    modules: false,
  },

  //Is a function that allows finer grained modifications to the internal webpack configuration.
  chainWebpack: (config) => {
    //Configure alias
    config.resolve.alias
      .set('@', resolve('src'))
      .set('assets', resolve('src/assets'))
      .set('components', resolve('src/components'))
      .set('views', resolve('src/views'))

    config.optimization.minimizer('terser').tap((args) => {
      //Remove production environment console
      args[0].terserOptions.compress.drop_console = true
      return args
    })
  },

  //Whether to use thread loader for Babel or typescript. This option is automatically enabled when the CPU of the system has more than one kernel and only works on production builds.
  parallel: require('os').cpus().length > 1,

  devServer: {
    host: '0.0.0.0',
    Port: 8088, // port number
    https: false, // https:{type:Boolean}
    Open: false, // configure auto start browser open: 'Google Chrome' - start Google Chrome by default

    //Configure multiple agents
    proxy: {
      '/api': {
        target: 'https://www.mock.com',
        Ws: true, // WebSockets of proxy
        Changeorigin: true, // allow WebSockets to cross domains
        pathRewrite: {
          '^/api': '',
        },
      },
    },
  },
}

Basic component packaging

In the process of developing projects, many components with similar functions and designs are usually used. Toast and dialog components are basically used in every mobile terminal project. In order to better match the UI design style of our company, instead of directly using the toast and dialog components of vant, we encapsulated similar components for direct call, such as:

This. $toast ({MSG: 'mobile phone number cannot be empty'})

this.$toast({
  MSG: 'success prompt',
  type: 'success',
})

this.$dialog({
  Title: 'delete prompt',
  Text: 'are you sure to delete this tag?',
  showCancelBtn: true,
  Confirmtext: 'confirm',
  confirm(content) {
    Alert ('deletion succeeded ')
  },
})

The renderings are as follows

Build a vue-cli4 + webpack mobile terminal framework (out of the box)

Visual analysis of Web pack

From here, we begin to optimize and package webpack. First, let’s analyze the performance bottleneck of webpack packaging, find out the problem, and then apply the medicine to the case. At this point, the webpack bundle analyzer is used. 1. Installation dependency

npm install webpack-bundle-analyzer -D

2. Configure in vue.config.js

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
configureWebpack: (config) => {
  if (process.env.NODE_ENV === 'production') {
    config.plugins.push(new BundleAnalyzerPlugin())
  }
}

After packaging, we can see such a dependency graph

Build a vue-cli4 + webpack mobile terminal framework (out of the box)

From the above interface, we can get the following information:

  • What are contained in the packaged files and the dependencies between modules
  • The proportion of the size of each file in the total, find out the larger file, think about whether there is a replacement scheme, and whether it contains unnecessary dependencies?
  • Are there duplicate dependencies and how can you optimize them?
  • Compressed size of each file.

CDN resource optimization

The full name of CDN isContent Delivery Network, the content distribution network. CDN is a content distribution network built on the network. Relying on the edge servers deployed everywhere, through the load balancing, content distribution, scheduling and other functional modules of the central platform, CDN enables users to obtain the required content nearby, reduce network congestion, and improve user access response speed and hit rate. The key technologies of CDN mainly include content storage and distribution technology.

As the project becomes larger and larger, more and more third-party NPM packages are relied on, and the files after construction will become larger and larger. In addition, it is a single page application, which will lead to a long white screen when the network speed is slow or the server bandwidth is limited. At this time, we can use the CDN method to optimize the network loading speed.

1. Willvue、vue-router、vuex、axiosThese Vue family resources are all obtained through CDN links, which can be found inindex.htmlInsert the corresponding link in the.

<body>
  <div id="app"></div>
  <script></script>
  <script></script>
  <script></script>
  <script></script>
  <script></script>
</body>

2. Invue.config.jsConfigure external properties

module.exports = {
 ···
    externals: {
      'vue': 'Vue',
      'vuex': 'Vuex',
      'vue-router': 'VueRouter',
      'axios':'axios'
    }
  }

3. Uninstall dependent NPM packages

npm uninstall  vue vue-router vuex axios

At this point, you can start the project to run. We can find that the project has loaded the above four CDN resources on the console.

However, there are many voices saying that Vue family bucket loading CDN resources does not play a big role, and public CDN resources are not as stable as NPM packages. There are different opinions. So I do this optimization for the new branch in the source code. When the project is small, CDN optimization is not considered.

Of course, when introducing other large third-party resources, such as echarts and amap (Gaode map), it is necessary to use CDN resources.

Gzip accelerated optimization

All modern browsers support gzip compression. Enabling gzip compression can greatly reduce the size of transmission resources, so as to shorten the resource download time, reduce the first white screen time and improve the user experience.

Gzip has the best compression effect on files based on text format (such as CSS, JavaScript and HTML). When compressing large files, it can often achieve a compression rate of up to 70-90%. Gzip compresses the compressed resources (such as pictures), and the effect is very poor.

const CompressionPlugin = require('compression-webpack-plugin')
configureWebpack: (config) => {
  if (process.env.NODE_ENV === 'production') {
    config.plugins.push(
      new CompressionPlugin({
        //Gzip compression configuration
        Test: / \. JS $| \. HTML $| \. CSS /, // match the file name
        Threshold: 10240, // compress data exceeding 10KB
        Deleteoriginalassets: false, // delete the original file
      })
    )
  }
}

Add skeleton screen to home page

With the gradual popularity of spa in the front-end community, single page applications inevitably bring pressure to the home page loading. At this time, a good home page user experience is very important. Many apps use a “skeleton screen” to display unloaded content, giving users a new experience.

The so-called skeleton screen is to use some graphics to occupy the space when the page content is not loaded, and then replace it after the content is loaded. In this process, users will feel that the content is gradually loading and about to be presented, reducing the bad experience of “white screen”.

In this paper, Vue skeleton webpack plugin is used to inject skeleton screen into single page application.

1. Skileton1.vue and skileton2.vue are created under the common folder of SRC. The specific structure and style are designed by ourselves. Ten thousand words are omitted here….

2. Create entry-skeleton.js in the same level directory

import Vue from 'vue'
import Skeleton1 from './Skeleton1'
import Skeleton2 from './Skeleton2'

export default new Vue({
  components: {
    Skeleton1,
    Skeleton2
  },
  template: `
    <div>
      <skeleton1 id="skeleton1" />
      <skeleton2 id="skeleton2" />
    </div>
  `
})

Configure the plug-in under vue.config.js

const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin')
configureWebpack: (config) => {
  config.plugins.push(
    new SkeletonWebpackPlugin({
      webpackConfig: {
        entry: {
          app: path.join(__dirname, './src/common/entry-skeleton.js'),
        },
      },
      minimize: true,
      quiet: true,
      router: {
        mode: 'hash',
        routes: [
          { path: '/', skeletonId: 'skeleton1' },
          { path: '/about', skeletonId: 'skeleton2' },
        ],
      },
    })
  )
}

At this point, reload the page and you can see our skeleton screen.Note: be sure to configure style separation extract: true

Recommended articles

Build a Vue cli mobile terminal H5 development template
Encapsulate a toast and dialog component and publish it to NPM
Build a webpack project from scratch
Summarize several methods of Web pack packaging optimization
Advanced application of summarizing Vue knowledge system
Summarize the practical skills of Vue knowledge system
Introduction to summarizing Vue knowledge system
Summarize the common skills of H5 development of mobile terminal (full of dry goods!)

Recommended Today

SQL exercise 20 – Modeling & Reporting

This blog is used to review and sort out the common topic modeling architecture, analysis oriented architecture and integration topic reports in data warehouse. I have uploaded these reports to GitHub. If you are interested, you can have a lookAddress:https://github.com/nino-laiqiu/TiTanI recorded a relatively complete development process in my hexo blog deployed on GitHub. You can […]