preface

H5 Base Scaffolding: Speed construction project

The previous article covered a generic Webpack build for rapid build projects, and this article will modify the H5 scaffolding in conjunction with the business

Whisper BB, not necessarily suitable for your project, specific project specific treatment, in line with their own business is the best

Add the version number of the resource

For those of you who have read the previous blog, we introduced the concept of a version number when we created the version. When we created the branch, we added the version number. The branch was called feat/0.0.01

I asked you to pay attention to the version number above the URL

Revamp the Webpack path

const branch = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().replace(/\s+/.' ')
const version = branch.split('/') [1] // Get the branch version number

output: {
  publicPath: `. /${version}`,}Copy the code

As mentioned above, we first obtain the branch version number and then modify the resource reference path to complete the processing of the resource version. As shown in the figure below, the H5 link is changed into a regular URL and the reference resource is tagged with the version number

Advantages of version numbers

  1. Can quickly locate the code version, targeted fixes
  2. Each version of the resource is saved on the CDN, and fast rollback only requires a refresh of the HTML, not a rebuild of the publication

Webpack Plugin development

It’s a stupid way to add a version directly, so let’s just write a plugin and play with it

const HtmlWebpackPlugin = require('html-webpack-plugin');
const childProcess = require('child_process')
const branch = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().replace(/\s+/.' ')
const version = branch.split('/') [1]

class HotLoad {
  apply(compiler) { 
    compiler.hooks.beforeRun.tap('UpdateVersion'.(compilation) = > {
      compilation.options.output.publicPath = `. /${version}/ `}}})module.exports = HotLoad;

module.exports = {
  plugins: [
    new HotLoad() 
]}
Copy the code

As mentioned above, we create a Webpack plugin that listens for Webpack hooks to modify the corresponding resource path before the task is executed.

Advanced customization

CDN resource import

In addition, we also introduced the concept of CDN in the previous blog. We can upgrade the above plug-in and introduce common CDN resources during construction to reduce the build and load time.

const scripts = [
  'https://cdn.bootcss.com/react-dom/16.9.0-rc.0/umd/react-dom.production.min.js'.'https://cdn.bootcss.com/react/16.9.0/umd/react.production.min.js'
]


class HotLoad {
  apply(compiler) { 
    compiler.hooks.beforeRun.tap('UpdateVersion'.(compilation) = > {
      compilation.options.output.publicPath = `. /${version}/ `
    })
    
    compiler.hooks.compilation.tap('HotLoadPlugin'.(compilation) = > {
      HtmlWebpackPlugin.getHooks(compilation).alterAssetTags.tapAsync('HotLoadPlugin'.(data, cb) = > {
        scripts.forEach(src= > [
          data.assetTags.scripts.unshift({
            tagName: 'script'.voidTag: false.attributes: { src }
          })
        ])
        cb(null, data)
      })
    })
  }
}
Copy the code

We have used alterAssetTags hooks provided by HtmlWebpackPlugin to add react-related third-party CDN hooks, so that in the production environment, projects under the same domain name can reuse resources.

Loading JS through caching

Static resources that remain unchanged for a long time can be cached locally. When a project is started next time, resources can be directly loaded from the local PC, improving the secondary startup efficiency.

First, we chose indexDB for cache, because indexDB has a larger capacity than Stroage. We need to cache large static resources, so we need a larger capacity indexDB to support it

import scripts from './script.json';
import styles from './css.json';
import xhr from './utils/xhr'
import load from './utils/load'
import storage from './stroage/indexedDb'

const _storage = new storage()
const _load = new load()
const _xhr = new xhr()

class hotLoad {

  constructor(props) {
    this.loadScript(props)
    this.issafariBrowser = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
  }

  async loadScript(props = []) {
    const status = await _storage.init()
    let _scripts = scripts
    const expandScripts = props

    if (status) {
      for (let script of _scripts) {
        const { version, type = 'js', name, url } = script
        if (this.issafariBrowser) {
          await _load.loadJs({ url })
        } else {
          const value = await _storage.getCode({ name, version, type });
          if(! value) {const scriptCode = await _xhr.getCode(url || `${host}/${name}/${version}.js`)
            if (scriptCode) {
              await _load.loadJs({ code: scriptCode })
              await _storage.setCode({ scriptName: `${name}_${version}_${type}`.scriptCode: scriptCode })
            }
          } else {
            await _load.loadJs({ code: value })
          }
        }
      }

      for (let style of styles) {
        const { url, name, version, type = 'css' } = style
        if (this.issafariBrowser) {
          await _load.loadCSS({ url })
        } else {
          const value = await _storage.getCode({ name, version, type })
          if(! value) {const cssCode = await _xhr.getCode(url || `${host}/${name}/${version}.css`)
            _storage.setCode({ scriptName: `${name}_${version}_${type}`.scriptCode: cssCode })
            _load.loadCSS({ code: cssCode })
          } else {
            _load.loadCSS({ code: value })
          }
        }
      }
    } else {
      for (let script of _scripts) {
        const { version, name } = script
        const scriptCode = await _xhr.getCode(script.url || `${host}/${name}/${version}.js`)
        if (scriptCode) {
          await _load.loadJs({ code: scriptCode })
        }
      }
      for (let style of styles) {
        const { url, name, version } = style
        const cssCode = await _xhr.getCode(url || `${host}/${name}/${version}.css`)
        _load.loadCSS({ code: cssCode })
      }
    }

    for (let script of expandScripts) {
      const { url } = script
      await _load.loadJs({ url })
    }

  }
}

window.hotLoad = hotLoad
Copy the code

CreateObjectURL (Blob + url.createObjecturl); createObjectURL (Blob + url.createObjecturl);

class load {
  constructor(){}/ / load js
  loadJs({ url, code, callback }) {
    let oHead = document
      .getElementsByTagName('HEAD')
      .item(0);
    let script = document.createElement('script');
    script.type = "text/javascript";
    return new Promise(resolve= > {
      if (url) {
        script.src = url
      } else {
        let blob = new Blob([code], { type: "application/javascript; charset=utf-8" });
        script.src = URL.createObjectURL(blob);
      }
      oHead.appendChild(script)
      if (script.readyState) {
        script.onreadystatechange = () = > {
          if (script.readyState == "loaded" || script.readyState == "complete") {
            script.onreadystatechange = null;
            callback && callback();
            resolve(true)}}}else {
        script.onload = () = > {
          callback && callback();
          resolve(true)}}})}/ / load the CSS
  loadCSS({ url, code }) {
    let oHead = document
      .getElementsByTagName('HEAD')
      .item(0);
    let cssLink = document.createElement("link");
    cssLink.rel = "stylesheet"
    return new Promise(resolve= > {
      if (url) {
        cssLink.href = url
      } else {
        let blob = new Blob([code], { type: "text/css; charset=utf-8" });
        cssLink.type = "text/css";
        cssLink.rel = "stylesheet";
        cssLink.rev = "stylesheet";
        cssLink.media = "screen";
        cssLink.href = URL.createObjectURL(blob);
      }
      oHead.appendChild(cssLink);
      resolve(true)}}}// Pull static resources through XHR
class xhr {
  constructor() {
    this.xhr;
    if (window.XMLHttpRequest) {
      this.xhr = new XMLHttpRequest();
    } else {
      this.xhr = new ActiveXObject('Microsoft.XMLHTTP'); }}// synchronize request js
  getCode(url) {
    return new Promise(resolve= > {
      this.xhr.open('get', url, true);
      this.xhr.send(null);
      this.xhr.onreadystatechange = () = > {
        if (this.xhr.readyState == 4) {
          if (this.xhr.status >= 200 && this.xhr.status < 300 || this.xhr.status == 304) {
            resolve(this.xhr.responseText)
          }
        }
      }
    })
  }
}
Copy the code

Direct loading in weak network environment

Cache loading in weak network environment

Compared with the figure above, it can be seen that in the case of fluctuating network environment, the speed of the second opening of the webpage supported by cache will be significantly improved. Of course, in terms of performance, it will take some time to determine the version of third-party static resources and read resources from the local, so you can choose according to the business

Comparison of advantages and disadvantages

advantage

  1. Unified takeover project dependencies can be targeted to upgrade common resources
  2. Resources are version-dependent. When cached locally, you can quickly switch versions
  3. The secondary loading speed will increase
  4. Works wonders with Service workers

disadvantage

  1. Unified upgrade process, there may be a reference project mismatch caused by the program crash
  2. In fact, strong caching of all common static CDN resources is also OK, why bother

If any students want to use the plug-in above, I will put it on github at 🐶

Full series of blog entries

The backend module

  1. DevOps – Gitlab Api usage (completed, click to jump)
  2. The conversation – build enterprise foundation platform Basic platform building last | foundation platform structures, medium-length | foundation platform set up next
  3. DevOps – Gitlab CI pipeline construction
  4. Devops-jenkins pipelined construction
  5. Devops-docker use
  6. DevOps – Release task flow design
  7. DevOps – Code review points
  8. Devops-node monitors service quality

The front-end module

  1. DevOps – H5 base scaffolding
  2. Devops-react project development

The end of the

This project is from zero development, the subsequent series of blog will be launched according to the actual development progress (really f * * king tired), after the completion of the project, will open part of the source code for your reference.

Why open part of the source code, because some business needs to fit the actual project targeted development, open out of the public module I write seriously

In order to write a series of blog, actually really masturbate a complete system (not generally tired), feel good students trouble three even (praise, attention, forward).