1 start a project

It was an old company, not a big company, but when we downloaded it from Git, we suddenly found that the project had a kind of heartache.

It’s 2020 and someone is using such an old scheme for a project. So at this point, what do we do, we get lost in thought, maintenance? Or are you going to run? Hold on, let’s look at the code…

Directory a common index code…. Not even pulling away… The following code is approximately 1000 + lines….

blockquote,body,dd,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,hr,html,iframe,input,legend,li,ol,p,pre,td,textarea,th,ul{padding:0; margin:0} html{-webkit-overflow-scrolling:touch; -webkit-text-size-adjust:100%; font-family:Arial, Helvetica, sans-serif; } body{-webkit-overflow-scrolling:touch; -webkit-box-sizing:border-box; box-sizing:border-box} a,body,select,select:focus,textarea,textarea:focus{-webkit-tap-highlight-color:transparent; outline:0; -webkit-appearance:none} li{list-style-type:none} table{border-collapse:collapse; border-spacing:0}
fieldset{border:none}
legend{display:none}
a:active,a:hover,button{outline:0} input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box; box-sizing:border-box} b,em,i{font-style:normal; font-weight:400} a{text-decoration:none; -webkit-tap-highlight-color:transparent} @media screen and (min-width:1440px){html{font-size:200%}}
@media screen and (max-width:1440px){html{font-size:200%}}
@media screen and (max-width:1024px){html{font-size:150%}}
@media screen and (max-width:980px){html{font-size:150%}}
@media screen and (max-width:750px){html{font-size:150%}}
@media screen and (max-width:720px){html{font-size:150%}}
@media screen and (max-width:640px){html{font-size:150%}}
@media screen and (max-width:540px){html{font-size:150%}}
@media screen and (max-width:480px){html{font-size:125%}}
@media screen and (max-width:432px){html{font-size:120%}}
@media screen and (max-width:414px){html{font-size:115%}}
@media screen and (max-width:400px){html{font-size:112.5%}}
@media screen and (max-width:393px){html{font-size:104%}}
@media screen and (max-width:375px){html{font-size:104%}}
@media screen and (max-width:360px){html{font-size:100%}}
@media screen and (max-width:320px){html{font-size:87.5%}}
@media screen and (max-width:240px){html{font-size:75%}} body{background-color: #f7f7f7; } .popBox,.popBind,.popBind_text,.popBind_error{position: fixed; width:100%; height:100%; background:rgba(0.0.0.0.5); color:#999999; display: none; top:0px; z-index:10; } .popBoxCont{width:18.1875rem; background-color: #fff; border-radius:0.3125rem; position: absolute; left:50%; top:50%; transform:translate(-50%, -50%); padding-bottom:2.0313rem; } .popBox_top{font-size:1rem; line-height:1.2rem; margin-top:2.75rem; margin-bottom:1.1875rem; } .popBox_top i{font-size:1.3125rem; line-height:1.5rem; vertical-align:bottom; margin-left:0.4375rem; color:#5bba48; font-weight:bold; } .popBoxDetail p{margin-left:2.375rem; margin-right:2.375rem; } .popBoxDetail .popBox_counseName{font-size:1rem; line-height:1.3125rem; color:#5bbb47; background-color: #edfbea; margin-bottom:1.6563rem; position: relative; margin-left:1.9688rem; margin-right:1.9688rem; padding:0.2375rem 0.4688rem; }Copy the code

2 Project Analysis

Don’t panic, we’re front-end engineers… So what do we do? So let’s first look at what we can do, so let’s first look at the technology stack he uses and the scenarios he uses.

2.1 Application Scenarios

Through communication with the project team and the product. This project is an H5 page running on the wechat public account. So that gives you time, about a week, to familiarize yourself with the project.

2.2 Analysis of technology stack architecture

1. Js architecture using technology stack 1.1 jquery 1.2 Jweixin 1.3 swiper.min 2. CSS solution pure handwriting, manual REMCopy the code
In fact, at this time, it is not difficult to find, is the accumulation of JS, HTTP unencapsulated state...Copy the code
methods plan disadvantages
Folder isolation Put the old code where I can’t see it, and work on the new project. (If you can’t see it, consider it no problem) In old project code, hard or hard, the architecture of new code is forced to follow
Micro front-end Make a large-scale container, make a bridge between the old and new projects, let the main architecture take charge of project communication, and ensure the normal operation of the two servers Most of the technical solutions of the micro front end are run in the back-end management system, and the adaptability of the mobile end is unknown.
webpack Multi-file packaging solution, so that old and new code, compatible in the tool Manual code modification

3 webpack technology

The advantages of Webpack are self-evident, if not clear, you can go to the webpack official website

In essence, Webpack is a static module bundler for modern JavaScript applications. When WebPack works with an application, it recursively builds a dependency Graph containing every module the application needs, and then packages all of those modules into one or more bundles.

4 Directory Structure

First of all, we certainly don’t want to change the old project code, after all, it is an old project… You changed it… In case the east and west walls collapse.. The final accident was not a small one. What about the new page.. We definitely want to use single-page technology, after all, loading and experience, after all, have a good experience.

Let’s make a copy of the original file. We don’t want to break the original function of the code in the process of making changes

Mkdir jq-webpack YARN add [email protected] [email protected] -d touch webpack.config.jsCopy the code

At this point, the project needs to add directives to package.json

"build": "webpack --mode production --config=webpack.config.js"."server": "webpack-dev-server --hot --config=webpack.config.js"
Copy the code

We can configure webpack.config.js in principle, we can configure multiple environments, but we will configure one for simplicity

In the case of the project structure is not too complex, in fact, we can still clarify the idea, such as the project, in fact, the initialization of CSS, JS can be removed and can form a complete set of routing system. Then you can make a directory structure for items to pull out

- router-index. js combination file - resource-. js Resource file - router-. js Route file - src-common Common part - CSS-js-images-pages Mapping between old items -  index - index.html - index.js - index.css - activity - index.html - index.js - index.css - .... -utils library index.js -public Difficult to do with file - images-lib-package. json -webpack.config.jsCopy the code

4.1 package. Json

{
  "name": "webpack"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "build": "webpack --mode production --config=webpack.config.js".// Package instruction
    "server": "webpack-dev-server --hot --config=webpack.config.js".// Start command
    "upload-test": "NODE_ENV=test node ./deploy".// Automatic upload - test
    "upload-prod": "NODE_ENV=prod node ./deploy" // Automatic upload - official
  },
  "keywords": []."author": ""."license": "ISC"."devDependencies": {
    "autoprefixer": "^ 9.1.0"."babel-plugin-import": "^ 1.13.3"."chalk": "^ 4.1.0." "."clean-webpack-plugin": "^ 3.0.0"."compression-webpack-plugin": "^ 6.0.0"."copy-webpack-plugin": "^ 4.6.0"."css-loader": "^ 3.3.0"."cssnano": "^ 4.1.10"."expose-loader": "1.0.3"."extract-text-webpack-plugin": "^ 4.0.0 - beta."."file-loader": "^ 6.2.0"."html-webpack-plugin": "4.5.0"."html-withimg-loader": "^ 0.1.16"."less": "^ 3.13.0"."less-loader": "^ 4.1.0." "."mini-css-extract-plugin": "^ 1.3.2." "."optimize-css-assets-webpack-plugin": "^ 5.0.0"."ora": "^ 5.1.0"."post-loader": "^ 2.0.0." "."postcss-loader": "^ 2.1.1"."postcss-pxtorem": "^ 5.0.0"."postcss-safe-parser": "^ 5.0.2"."progress-bar-webpack-plugin": "^ 2.1.0." "."scp2": "^ 0.5.0"."style-loader": "^ 1.0.0"."url-loader": "^ 4.4.1"."vue-loader": "^ 15.9.5"."vue-template-compiler": "^ 2.6.12." "."webpack": "4.19.1"."webpack-cleanup-plugin": "0.5.1"."webpack-cli": "^ 2.1.4." "."webpack-dev-server": "3.11.0"
  },
  "dependencies": {
    "babel-polyfill": "^ 6.26.0"."lib-flexible": "^" 0.3.2."vant": "^ 2.11.2"}}Copy the code

5 Multi-page configuration

The technique we use is the popular Webpack, multi-page technique. We can see that in the HTML of every old project, we would introduce jQ, our own index.js, and then a bunch of images, maybe some of them in our own images… We started doing some isolation, sorting groups, starting to open multiple folders, putting HTML, JS, CSS. The style will be clear, and it’s time to start doing some external introductions. First of all, let’s configure the router file. I have done some splitting here. Of course, if you can modularize the page, I suggest splitting it more finely.

5.1 Route Settings

Resource-.js puts the Settings in the file path into the resource management library

const entry = {
  / / home page
  "index-css": "./src/index/index.css"."index-js": "./src/index/index.js"./ / activity page
  "activity-css": "./src/pages/activity/index.css"."activity-js": "./src/pages/activity/index.js",}module.exports = {
  entry
};
Copy the code

Router.js is ungrouped in the routing page

const router = [
  {
    name: "Home page".filename: "index.html".chunks: ["index-css"."index-js"].// If more than one can introduce more than one
    template: "./src/pages/index/index.html"}, {name: "Activity".filename: "activity.html".chunks: ["activity-css"."activity-js"].template: "./src/pages/activity/index.html",},];module.exports = {
  router
};
Copy the code

index.js

const htmlPlugin = require("html-webpack-plugin");
const resource = require("./resource");
const routerObj = require("./router");

const htmlWebpackPlugins = [];
routerObj.router.forEach(item= > {
  htmlWebpackPlugins.push(
    new htmlPlugin({
      filename: item.filename, // The packaged file name
      minify: false.chunks: item.chunks, // Only the corresponding JS and CSS are imported for each HTML
      inject: true.hash: true.// Avoid caching js.
      template: item.template
    })
  );
});

module.exports = {
  htmlWebpackPlugins,
  entry: resource.entry
};
Copy the code

There are two ways to do this

  • Direct use of CDN into the library, here is divided into free library, and the company’s own library two kinds
  • Put it in the public public, introduced in the page
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Activities page</title>
</head>
<body>
  		
     <script src=". / public/lib/jquery - 1.8.0 comes with. Min. Js. "" charset="utf-8"></script>
</body>
</html>
Copy the code

It’s been a rough ride here…..

5.2 webpack. Config. Js configuration

const path = require("path");
const HtmlRouter = require("./router/index");
const CopyPlugin = require("copy-webpack-plugin");
const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");
const optimizeCss = require("optimize-css-assets-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); / / remove dist
const CompressionPlugin = require("compression-webpack-plugin");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");

module.exports = {
  devServer: {
    contentBase: path.resolve("dist"),
    host: "localhost".// The server IP address, using the loaclhost address
    compress: true.// Whether server compression is enabled
    port: "8888".// Configure the service port number
    stats: "errors-only",
    historyApiFallback: true,
    overlay: true
  },
  entry: HtmlRouter.entry,
  output: {
    path: path.resolve("dist"),
    filename: "js/[name].[hash:8].js"
  },
  plugins: [
    new ProgressBarPlugin(),
    new CleanWebpackPlugin(),
    new optimizeCss({
      cssProcessor: require("cssnano"), // Introduce the CSsnano configuration compression option
      cssProcessorOptions: {
        discardComments: { removeAll: true }
      },
      canPrint: true // Whether to print the plug-in information to the console
    }),
    new ExtractTextWebpackPlugin({
      filename: "css/[name].[hash:8].css".// Set the extracted CSS name
      allChunks: true
    }),
    new CopyPlugin(
      [
        {
          from: path.resolve(__dirname, "./src/public/lib"),
          to: path.resolve(__dirname, "./dist/public/lib")
        },
        {
          from: path.resolve(__dirname, "./src/public/images"),
          to: path.resolve(__dirname, "./dist/public/images")
        }
      ],
      { ignore: [], copyUnmodified: true }
    ),
    new CompressionPlugin()
  ].concat(HtmlRouter.htmlWebpackPlugins),
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "src"),
      "~": path.resolve(__dirname, "src/pages/vue-template")
    }
  },
  module: {
    rules: [
      {
        test: /\.(htm|html)$/i,
        loader: "html-withimg-loader"
      },
      {
        test: /\.css$/,
        use: ExtractTextWebpackPlugin.extract({
          fallback: "style-loader",
          use: [
            {
              loader: "css-loader"
            }
          ],
          publicPath: ".. /"
        })
      },
      {
        test: /\.(png|jpg|gif|jpeg|svg)$/i,
        use: [
          {
            loader: "url-loader",
            options: {
              // When the loaded image is less than limit, the image will be compiled as a base64 string,
              // When the image is larger than this limit, file-loader is used to load the image
              limit: 10000.// The fallback alternative method must be explicitly specified in webpack4.x, which is file-loader
              fallback: require.resolve("file-loader"),
              encoding: "base64",
              outputPath: "images/",
              publichPath: "images/",
              name: "[name].[hash:8].[ext]",
              esModule: false // The solution}}]}};Copy the code

6. Picture path problem

One of the most frustrating aspects of dealing with this code is that jQ inserts HTML, and all your syntax is $(‘.xx’).html(xx). At this stage, you can easily run into a huge pit… Is the image can not be resolved by Webpack, so packaged out of the image.

There are three solutions

  • There is no problem with importing an HTTP image directly as a CDN
  • Put the page in the public directory we have prepared, and use the absolute path to solve the problem
  • Use the require method in the code to introduce some images, which are then parsed as code

7 Single-page configuration -Vue

The purpose of doing so much business in front is to rely on the VUE page from the business… It’s not going to be vue-cil, so I’m going to look at the nature of vue-cil. It’s actually a Webpack, so why can I parse vUE, less? Since it is multi-page, how compatible?

7.1 VUE project establishment

The familiar project format is back, so I won’t cover it here

- src
	- pages
  - vue-template
  	- index.html
    - pages
    	- 404.vue
      - home.vue
    - routers
    	- index.js
    - App.vue
Copy the code

7.2 Configuration Page

In the route we just did, let’s set it up

Js added to the resource file

"vue-template-js": "./src/pages/vue-template/main.js"
Copy the code

Added to router.js routing file

{
  name: "vue-template".filename: "template.html".chunks: ["babel-polyfill"."vue-template-js"].template: "./src/pages/vue-template/index.html",}Copy the code

7.3 webpack configuration

First we need to load less, then we are familiar with the PX, automatically to REM, automatically with the compatible prefix

const VueLoaderPlugin = require("vue-loader/lib/plugin");

plugins: [
  ...
	new VueLoaderPlugin()
]
resolve: {
    alias: {
       "@": path.resolve(__dirname, "src"),
        "~": path.resolve(__dirname, "src/pages/vue-template"),
        vue$: "vue/dist/vue.esm.js"}},module: {
  rules: [{test: /\.css$/,
      use: ExtractTextWebpackPlugin.extract({
        fallback: "style-loader".use: [{loader: "css-loader"}, {loader: "postcss-loader"],},publicPath: ".. /",}, {})test: /\.less$/,
      use: ExtractTextWebpackPlugin.extract({
        use: [{loader: "css-loader"}, {loader: "postcss-loader"}, {loader: "less-loader"],},fallback: "style-loader",}, {})test: /\.vue$/,
      loader: "vue-loader",}]},externals: {
  vue: "Vue"."vue-router": "VueRouter"
}
Copy the code

7.4 Configuring REM Automation

postcss.config.js

module.exports = {
  plugins: {
    autoprefixer: {
      overrideBrowserslist: ["Android > = 4.0"."iOS >= 7"]},"postcss-pxtorem": {
      rootValue: 37.5.propList: ["*"]}}};Copy the code

7.5 writing vue – the template/HTML

<! DOCTYPE html><html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="Width =device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
    />
    <title>Vue template</title>
  </head>

  <style>
    html.body.#app {
      height: 100%;
      margin: 0;
      padding: 0;
    }

    .webpack-home {
      background-color: # 303133;
      height: 100%;
      display: flex;
      flex-direction: column;
    }

    .webpack-home__main {
      user-select: none;
      width: 100%;
      flex-grow: 1;
      display: flex;
      justify-content: center;
      align-items: center;
      flex-direction: column;
    }

    .webpack-home__footer {
      width: 100%;
      flex-grow: 0;
      text-align: center;
      padding: 1em 0;
    }

    .webpack-home__footer > a {
      font-size: 12px;
      color: #ababab;
      text-decoration: none;
    }

    .webpack-home__loading {
      height: 32px;
      width: 32px;
      margin-bottom: 20px;
    }

    .webpack-home__title {
      color: #fff;
      font-size: 14px;
      margin-bottom: 10px;
    }

    .webpack-home__sub-title {
      color: #ababab;
      font-size: 12px;
    }

    .ql-editor {
      min-height: 150px;
    }

    .ql-snow .ql-picker {
      height: 36px ! important;
    }

    @media only screen and (-webkit-min-device-pixel-ratio: 3),
      only screen and (min--moz-device-pixel-ratio: 3),
      only screen and (-o-min-device-pixel-ratio: 3/1),
      only screen and (min-device-pixel-ratio: 3),
      only screen and (min-resolution: 458dpi),
      only screen and (min-resolution: 3dppx) {
      .van-tabbar--fixed {
        padding-bottom: 15px ! important; }}</style>

  <body>
    <div id="app">
      <div class="webpack-home">
        <div class="webpack-home__main">
          <img
            class="webpack-home__loading"
            src="./svg/loading-spin.svg"
            alt="loading"
          />
          <div class="webpack-home__title">Loading resources</div>
          <div class="webpack-home__sub-title">It may take a long time to load resources for the first time</div>
        </div>
        <div class="webpack-home__footer"></div>
      </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </body>
</html>
Copy the code

7.6 writing vue – the template/main. Js

Instantiate a VUE project, doing a simple instantiation configuration and adding routes

More can be configured according to the business situation, such as what to load on demand, here is just a simple elaboration of the structure

import routers from "./routers/index";
import App from "~/App.vue";
import Vant from "vant";
import "vant/lib/index.css";
import "lib-flexible";

Vue.use(VueRouter);
Vue.use(Vant);

const router = new VueRouter({
  routes: routers
});

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

8 Automatic Publishing

In the deploy directory, set

Products.js server configuration

/* * Define multiple server accounts and export the current environment server account */ based on the SERVER_ID
const SERVER_LIST = [
  {
    id: 0.name: "A- Test Environment".host: "127.0.0.1".// ip
    url: "http://www.baidu.com".port: 22./ / port
    username: "root".// Account for logging in to the server
    password: "".// Account for logging in to the server
    path: "" // Publish the project path to the static server}];module.exports = SERVER_LIST;
Copy the code

Index.js executes the function

const scpClient = require("scp2");
const ora = require("ora");
const chalk = require("chalk");
const servers = require("./products");
let server = servers[process.env.NODE_ENV === "prod" ? 1 : 0];
const spinner = ora(
  "Posting to" +
    (process.env.NODE_ENV === "prod" ? "Production" : "Test") +
    "Server..."
);

var Client = require("ssh2").Client;

var conn = new Client();
conn
  .on("ready".function() {
    // rm delete dist file, \n is newline, execute restart nginx command
    let dels = `rm -rf ${server.path}\n mkdir ${server.path}`;
    conn.exec(dels, function(err, stream) {
      if (err) throw err;
      stream
        .on("close".function(code, signal) {
          // After executing shell command, put the start upload deployment project code in this
          spinner.start();
          scpClient.scp(
            "dist/",
            {
              host: server.host,
              port: server.port,
              username: server.username,
              password: server.password,
              path: server.path,
            },
            function(err) {
              spinner.stop();
              if (err) {
                console.log(chalk.red("Publishing failed.\n"));
                throw err;
              } else {
                console.log(
                  chalk.green(
                    "Success! Successfully published to" +
                      (process.env.NODE_ENV === "prod" ? "Production" : "Test") +
                      "Server! \n"));console.log(server.url); }}); conn.end(); }) .on("data".function(data) {
          console.log("STDOUT: " + data);
        })
        .stderr.on("data".function(data) {
          console.log("STDERR: " + data);
        });
    });
  })
  .connect({
    host: server.host,
    port: server.port,
    username: server.username,
    password: server.password,
  });
Copy the code

9 git address

Github.com/MYQ1996/jq-…