Introduction to the

This is a vue-CLI4-based mobile framework, which contains the common configuration, component packaging and WebPack optimization methods for the project, and can be used for rapid development.

Technology stack: Vue-CLI4 + WebPack4 + Vant + AXIos + less + PostCSS-px2REM

Source github.com/Michael-lzg…

// Install dependencies
npm install

// Local boot
npm run dev

// Production packaging
npm run build
Copy the code

A year or two ago, Vue-CLI3 was already on its way to version 3.0+, but since older projects were used to the scaffolding of Vue-CLI2, I wrote an article on building a VUe-CLI mobile H5 development template that briefly summarized some mobile development skills.

Recently I updated the vue-CLI scaffolding, which has been upgraded to version 4.0+, and I felt a lot of need to use Vue-CLI4 for new projects. In addition to my recent better understanding of Webpack, I combined Vue-CLI4 and Webpack to build a mobile framework that works right out of the box. It mainly includes the following technical points:

  • Vue – cli4 scaffold
  • Vant is introduced on demand
  • Mobile REM ADAPTS
  • Axios intercepts encapsulation
  • Util utility class function encapsulation
  • Vue – the router configuration
  • Login Permission Verification
  • Configure multiple environment variables
  • Vue. Config. Js configuration
  • Toast Component encapsulation
  • Dialog Component Encapsulation
  • Cross-domain proxy Settings
  • Webpack packaging visual analysis
  • CDN resource optimization
  • Gzip packaging optimization
  • Add a skeleton screen on the home page

For more webPack optimization methods, see github.com/Michael-lzg…

Configuration vant

Vant is a lightweight, reliable library of mobile Vue components that is well suited for mobile development based on the Vue technology stack. For a long time, the mobile UI framework I used was VUX. Since Vux does not support Vue-CLI3, I switched to Vant. I have to say that vant is much better than VUX in terms of interactive experience and code logic, and Vant has fewer pits.

For third-party UI components, if all of them are introduced, for example, the packaging volume will be too large and the loading of the home page will take too long to be white, so it is necessary to load them on demand. Vant also provides an on-demand loading method. Babel-plugin-import is a Babel plug-in that automatically converts the way import is written to import on demand during compilation.

1. Install dependencies

npm i babel-plugin-import -D
Copy the code

2. Configure.babelrc or babel.config.js

// Add configuration to.babelrc
{
  "plugins": [["import", {
      "libraryName": "vant"."libraryDirectory": "es"."style": true}}]]// For users using Babel7, this can be configured in babel.config.js
module.exports = {
  plugins: [['import', {
      libraryName: 'vant'.libraryDirectory: 'es'.style: true
    }, 'vant']]};Copy the code

3. Import on demand

You can introduce Vant components directly into your code, and the plug-in will automatically convert the code into the as-needed import form in Method 2

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

Vue.use(Button)
Copy the code

Rem adaptation

Mobile adaptation is something you have to deal with during development. Here, we use the px2REM-Loader in PostCSS to convert the PX in our project to REM in a certain proportion, so that we can write px against the blue Lake label.

We set the HTML word and font to 100px, and many people choose 375px, but I don’t think the rem conversion is accurate enough, and we can’t quickly calculate its original px value in our head while debugging the code on the console. If we set 1rem=100px, then we see 0.16rem, 0.3rem and we quickly figure out 16px, 30px.

The specific steps are as follows;

1. Install dependencies

npm install px2rem-loader --save-dev
Copy the code

2. Perform the following configuration in vue.config.js

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

Set the 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 = `The ${100 * cale}px`
}

window.addEventListener('resize'.function() {
  initRem()
})
Copy the code

Axios requests 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 the 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)
  }
)
// Respond to interceptors
http.interceptors.response.use(
  (res) = > {
    vm.$loading.hide()
    // The token is invalid. Log in again
    if (res.data.code === 401) {
      // Log in again
    }
    return res
  },
  (error) => {
    vm.$loading.hide()
    return Promise.reject(error)
  }
)
Copy the code

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 }
Copy the code

Mount the get and POST methods to the vue instance.

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

Utility class function encapsulation

Add methods to the prototype chain of the vue instance

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

2. Register with vue.use() in main.js

import utils from './js/utils'
Vue.use(utils)
Copy the code

Vue – the router configuration

In normal times, many people can configure the path and Component on the Vue-Router and realize route hopping. There are many other things that vue-Router can do, such as

  • Lazy route loading was configured. Procedure
  • Change the title of the single-page application
  • Login Permission Verification
  • Page cache Configuration

Lazy route loading was configured. Procedure

In the Vue project, there are three ways to realize route loading on demand (lazy route loading) :

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

// 2, es6 proposal import()
{
  path: '/'.name: 'home'.component: (a)= > import('.. /views/Home.vue')}// 3, require. Ensure ()
{
  path: '/home'.name: 'Home'.component: r= > require.ensure([],() =>  r(require('.. /views/Home.vue')), 'home')}Copy the code

This project uses the second approach, optimized for subsequent WebPack packaging.

Change the title of the single-page application

Since the single-page application has only one HTML, the title of all pages does not change by default, but we can add attributes to the routing configuration and change the title of the page using JS in the routing guard

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

Login Permission Verification

In the application, there are usually the following scenarios, such as mall: some pages do not need to log in to access, such as home page, product details page, are users in any case can see; But there are also need to log in after the access, such as personal center, shopping cart, etc. This is where you need to control page access.

In addition, like some need to record user information and login status of the project, also need to do login permission verification, in order to prevent people with ulterior motives through direct access to the PAGE URL to open the page.

At this time. The route guard can help us with the login verification. The details are as follows:

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

const routes = [
  {
    path: '/'.name: 'home'.component: (a)= > import('.. /views/Home.vue'),
    meta: { title: 'home'.keepAlive: false.auth: false}, {},path: '/mine'.name: 'mine'.component: (a)= > import('.. /views/mine.vue'),
    meta: { title: 'I'.keepAlive: false.auth: true}},]Copy the code

2. Make a judgment on the route home page. When to.meta. Auth is true(requires login) and there is no cache of login information, 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()
  }
})
Copy the code

Page cache Configuration

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

1. Configure the keepAlive attribute value of the Meta object to distinguish whether the page needs to be cached

const routes = [
  {
    path: '/'.name: 'home'.component: (a)= > import('.. /views/Home.vue'),
    meta: { title: 'home'.keepAlive: false.auth: false}, {},path: '/list'.name: 'list'.component: (a)= > import('.. /views/list.vue'),
    meta: { title: 'List page'.keepAlive: true.auth: false}},]Copy the code

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>
Copy the code

Configure multiple environment variables

Env. Development env. Development env. Development env. . Env. Test. The env. Production

An environment variable file contains only key = value pairs of environment variables:

NODE_ENV = 'production'
VUE_APP_ENV = 'production' // Only environment variables starting with VUE_APP can be used directly in project code
Copy the code

In addition to the custom VUEAPP* variable, there are two other variables available:

  • NODE_ENV: one of “development”, “production”, or “test”. The exact value depends on the mode in which the application is running.
  • BASE_URL: matches the publicPath option in vue.config.js, which is the base path to which your application will be deployed.

Let’s start by configuring our environment variables

1. Create.env.* in the project root directory

  • .env.development Local development environment configuration
NODE_ENV='development'
VUE_APP_ENV = 'development'
Copy the code
  • Env.staging test environment configurations
NODE_ENV='production'
VUE_APP_ENV = 'staging'
Copy the code
  • Env. Production Formal environment configuration
NODE_ENV='production'
VUE_APP_ENV = 'production'
Copy the code

To configure more variables for different environments, create a new config/index under the SRC file

// Introduce different configuration process.env.node_env depending on the environment
const config = require('./env.' + process.env.VUE_APP_ENV)
module.exports = config
Copy the code

Create env.development.js, env.test.js, and env.production. Js in the same directory and configure the required variables. In env. Development. Js, for example

module.exports = {
  baseUrl: 'http://localhost:8089'.// The address of the project
  baseApi: 'https://www.mock.com/api'.// The local API requests the address
}
Copy the code

2. Configure packaging commands

Package. json scripts package commands for different environments

  • Start the local server using NPM run serve
  • Package tests with NPM Run test
  • Package formally with NPM Run build
"scripts": {
  "dev": "vue-cli-service serve"."build": "vue-cli-service build"."test": "vue-cli-service build --mode test",}Copy the code

Vue. Config. Js configuration

Starting with Vue-cli3, new scaffolds need to be configured in vue.config.js for our project. Mainly includes

  • Output location of the packaged file
  • Disable the production environment sourcemap
  • Configure REM to convert px
  • Configuring an alias
  • Remove the production console
  • Cross-domain proxy Settings

In addition, there are a number of configurations for optimal packaging, which will be covered later.

module.exports = {
  publicPath: '/'.// Default: '/'

  // Where to output the built file, as required by the company
  outputDir: 'dist/static'.// Put the generated static resources (js, CSS, img, fonts) directory.
  assetsDir: 'static'.// Specify the output path of the generated index.html
  indexPath: 'index.html'.// Whether to use the Vue build that includes the runtime compiler.
  runtimeCompiler: false.transpileDependencies: [].// If you don't need the source map for the production environment
  productionSourceMap: false.CSS / / configuration
  css: {
    // Whether to use the CSS separation plugin ExtractTextPlugin
    extract: true.sourceMap: true.// CSS preset configuration items
    loaderOptions: {
      postcss: {
        plugins: [
          require('postcss-px2rem') ({remUnit: 100}),],},},// Enable CSS modules for all CSS/pre-processor Files.
    modules: false,},// is a function that allows more granular changes to the internal WebPack configuration.
  chainWebpack: (config) = > {
    // Configure the alias
    config.resolve.alias
      .set(The '@', resolve('src'))
      .set('assets', resolve('src/assets'))
      .set('components', resolve('src/components'))
      .set('views', resolve('src/views'))

    config.optimization.minimizer('terser').tap((args) = > {
      // Remove the 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 system CPU has more than one core and only applies to production builds.
  parallel: require('os').cpus().length > 1.devServer: {
    host: '0.0.0.0'.port: 8088./ / the port number
    https: false.// https:{type:Boolean}
    open: false.Open: 'Google Chrome'- starts Google by default

    // Configure multiple proxies
    proxy: {
      '/api': {
        target: 'https://www.mock.com'.ws: true.// WebSockets for proxy
        changeOrigin: true.// Allow webSockets to cross domains
        pathRewrite: {
          '^/api': ' '},},},},}Copy the code

Base component Packaging

During development projects, there are many components with similar functionality and design. Toast and Dialog components are used in almost every mobile project. To better match our company’s UI design style, instead of using Vant’s Toast and Dialog components directly, we have encapsulated similar components that can be called directly, such as:

this.$toast({ msg: 'Mobile phone number cannot be empty' })

this.$toast({
  msg: 'Success Tip'.type: 'success',})this.$dialog({
  title: 'Delete prompt'.text: 'Are you sure to delete this label? '.showCancelBtn: true.confirmText: 'confirm',
  confirm(content) {
    alert('Deletion succeeded')}})Copy the code

The renderings are shown below

Toast passed parameter

Props

name type default description
msg String ‘ ‘ Pop-up message
type String ‘ ‘ Pop-up types: Success,fail, Warning, Loading

Dialog passed parameter

Props

name type default description
title String ‘ ‘ The title
text String ‘ ‘ The text content
type String ‘ ‘ Default is plain text, input
maxlength Number 20 Maximum number of words entered
confirmText String determine The right button
cancelText String cancel The left button

Events

name params description
confirm null Selected callback
cancel ull Callback after cancellation

Webpack visual analysis

From there, we start our WebPack optimization. First, let’s take a look at the WebPack performance bottleneck and find out what the problem is before we can address it. This is where webpack-bundle-Analyzer comes in. 1. Install dependencies

npm install webpack-bundle-analyzer -D
Copy the code

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())
  }
}
Copy the code

Once packaged, we can see such a dependency diagram

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

  • What is contained in the packaged files and the dependencies between modules
  • The size of each file as a percentage of the total, look for larger files, think about alternatives, do you use it and include unnecessary dependencies?
  • Are there duplicate dependencies, and how can this be optimized?
  • The compressed size of each file.

CDN resource optimization

The full name of the CDN is Content Delivery Network. CDN is a content distribution network built on the network. It relies on edge servers deployed in various places and through the load balancing, content distribution, scheduling and other functional modules of the central platform, users can obtain the required content nearby, reduce network congestion, and improve user access response speed and hit ratio. The key technologies of CDN are content storage and distribution.

As the project grows and relies on more third-party NPM packages, the files that are built will become larger. Coupled with the fact that it’s a single-page application, this can lead to long periods of white screen time on slow network speeds or with limited server bandwidth. At this time, we can use the CDN method to optimize the network loading speed.

1. Replace vue, Vue-router, vuex, and Axios with CDN links, and insert the corresponding links in index.html.

<body>
  <div id="app"></div>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>
  <script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script>
  <script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script>
  <script src="https://cdn.bootcss.com/element-ui/2.6.1/index.js"></script>
</body>
Copy the code

2. Configure the externals attribute in vue.config.js

moduleExports = {··· externals: {'vue': 'Vue'.'vuex': 'Vuex'.'vue-router': 'VueRouter'.'axios':'axios'}}Copy the code

3. Uninstall related dependent NPM packages

npm uninstall  vue vue-router vuex axios
Copy the code

It’s time to start the project running. In the console, we can see that the project has loaded the above four CDN resources.

But now there are a lot of voices said, vue family bucket load CDN resources is actually not a big role, and the public CDN resources are not so stable NPM package, this is different. So I did this optimization on 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(AMAP), it is necessary to use CDN resources.

GZip accelerated optimization

All modern browsers support GZIP compression. Enabling GZIP compression can significantly reduce the size of transfer resources, shorten resource download time, reduce the first white screen time, and improve user experience.

Gzip works best for text-based files (such as CSS, JavaScript, and HTML), and can achieve 70-90% compression rates for larger files. It doesn’t work well for resources that have already been compressed (such as images).

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 that exceeds 10KB
        deleteOriginalAssets: false.// Whether to delete the original file}}}))Copy the code

Add a skeleton screen on the home page

With the gradual popularity of SPA in the front end, 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 completely new experience.

The so-called skeleton screen is to use some graphics to occupy the space before the page content is loaded, and then replace it after the content is loaded. During this process, users can sense that content is gradually loading and about to be presented, reducing the negative experience of “white screen”.

This paper uses vue-skeleton-webpack-plugin to inject skeleton screen into single page application.

Skeleton1. Vue, Skeleton2. Vue are created in the common folder of SRC, and the specific structure and style are designed by themselves.

2. Create a new entry-skeleton in the same directory

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

export default new Vue({
  components: {
    Skeleton1,
    Skeleton2
  },
  template: ` 
      
`
}) Copy the code

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'},],},}))}Copy the code

At this point reload the page and see our skeleton screen. Note: Be sure to configure the style extract: true

Recommend the article

Set up a VUe-CLI mobile H5 development template
Encapsulate a TOAST and Dialog component and publish it to NPM
Build a WebPack project from scratch
Summarize a few ways to optimize WebPack packaging
Summarize the advanced application of VUE knowledge system
Summarize the practical skills of vUE knowledge system
Summarize the basic introduction of VUE knowledge system
Summary of mobile terminal H5 development commonly used skills (full of dry goods oh!)

Follow my public account irregularly share front-end knowledge, progress with you!