webpack: 多页面+vue单页面 老项目jq升级

1 入手项目

进入一家老的公司,公司规模也不大,但是我们从git上下载后,我们猛然发现,这项目有一种心痛的感觉。

webpack: 多页面+vue单页面 老项目jq升级_第1张图片 webpack: 多页面+vue单页面 老项目jq升级_第2张图片

这都2020年了,居然还有人用这么古老的方案去进行一个项目。那么这时候,我们该怎么办,我们陷入了沉思,继续维护?还是准备跑路?忍住,我们先看看代码…

目录 一个公共的index代码…居然都没有抽离…如下代码大概有个1000多行…

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;}

2 项目分析

不能慌张,我们可是前端工程师…那么我们该怎么办呢?首先我们先分析一下我们可以怎么办,那么我们首先分析一下他使用的技术栈和运用场景。

2.1 运用场景

通过和项目组,产品的沟通。该项目,是运行在微信公众号上的的一个h5页面。那么能够给你的时间,差不多是一周时间,去熟悉了解,整个项目。

2.2 技术栈架构分析

1. js架构使用技术栈
  1.1 jquery
  1.2 jweixin
  1.3 swiper.min
2. css解决方案
   纯手写,手动rem
其实这时候,不难发现,就是累加的js,http未封装状态...
方法 方案 缺点
文件夹隔离 将老代码,丢到我看不到的地方,继续开发新项目。(看不到,那就当做没有问题) 在老项目的代码,硬伤还是硬伤,新代码的架构被迫跟随
微前端 做一个大规模容器,将新老项目做一个中间的桥接,让主架构负责项目的沟通,保证2个服务器正常运行 微前端的技术方案,大部分实例是作为后端管理系统中运行,在手机端中的适配能力未知。
webpack 多文件打包方案,让新老代码,在工具中兼容 人力改代码

3 webpack技术

webpack的优点不言而喻,如果不清楚的,可以去看webpack官网的介绍

本质上,webpack 是一个现代 JavaScript 应用程序的_静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个_依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle

4 目录结构

首先我们肯定是不会想去想改动,老项目代码,那毕竟是老项目…你去改动了…万一东西墙崩溃…最后的事故可不小。那么新页面呢…我们肯定是希望去使用单页面技术,毕竟加载和体验上,都有了毕竟好的体验。

先将原来的文件复制出一个来,我们也不希望在改动的过程中,破坏了代码原本的功能

mkdir jq-webpack
yarn add [email protected] [email protected] -D
touch webpack.config.js

这时候,项目需要将 package.json 添加上指令

"build": "webpack --mode production --config=webpack.config.js",
"server": "webpack-dev-server --hot --config=webpack.config.js"

然后开始配置 webpack.config.js 原则上可以配置多种环境但是我们这边为了简单就配置一种

在项目结构不算过于复杂的情况下,其实我们还是可以理一下思路,就比如项目中,其实初始化css,js 都是可以抽离出来并且,可以形成一套完整的路由体系。那么就可以制作一个项目抽离的目录结构

- router
	- index.js  组合文件
  - resource.js 资源文件
  - router.js 路由文件
- src
	- common 公用的部分
  	- css
    - js
    - images
  - pages 老项目的对应关系
  	- index
        - index.html
        - index.js
        - index.css
    - activity
        - index.html
        - index.js
        - index.css
    - ....
  - utils 工具库
    	index.js
  - public 难以做处理文件
    - images
    - lib
- package.json
- webpack.config.js

4.1 package.json

{
     
  "name": "webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
     
    "build": "webpack  --mode production --config=webpack.config.js", // 打包指令
    "server": "webpack-dev-server --hot --config=webpack.config.js", // 启动指令
    "upload-test": "NODE_ENV=test node ./deploy", // 自动化上传-测试
    "upload-prod": "NODE_ENV=prod node ./deploy" // 自动化上传-正式
  },
  "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.0",
    "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.1.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"
  }
}

5 多页面配置

我们使用的技术,是比较普遍的webpack,多页面技术我们可以看到,每一个老项目的html 中,都会引入关于jq、自己的index.js、然后一股脑的images,又或许有些是放在自己的images里面…放在我们开始做一些隔离,分类组合,开始开多个文件夹,放入html、js、css。样式将会比较清楚,这时候开始做一些外部引入的操作。
首先我们配置一下router的文件,我这边做了一些拆分,当然你如果页面能模块化的,建议拆分的更细。

5.1 路由设置

resource.js 将文件路径中的设置放入资源管理库中

const entry = {
     
  // 首页
  "index-css": "./src/index/index.css",
  "index-js": "./src/index/index.js",
  
  // 活动页
  "activity-css": "./src/pages/activity/index.css",
  "activity-js": "./src/pages/activity/index.js",
}

module.exports = {
     
  entry
};

router.js 在路由页面中去组合

const router = [
  {
     
    name: "首页",
    filename: "index.html",
    chunks: ["index-css", "index-js"], // 如果多个可以引入多个
    template: "./src/pages/index/index.html",
  },
  {
     
    name: "活动",
    filename: "activity.html",
    chunks: ["activity-css", "activity-js"],
    template: "./src/pages/activity/index.html",
  },
];

module.exports = {
     
  router
};

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, //打包后的文件名
      minify: false,
      chunks: item.chunks, //每个html只引入对应的js和css
      inject: true,
      hash: true, //避免缓存js。
      template: item.template
    })
  );
});

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

当然对于html也需要做一些处理处理,这里有两种方法

  • 直接使用cdn引入库、这里又分为免费库,和公司自己的库两种
  • 放入公共的public中,页面中引入

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>活动页面title>
head>
<body>
  		
     <script src="./public/lib/jquery-1.8.0.min.js" charset="utf-8">script>
body>
html>

这里是一段艰苦的历程…

5.2 webpack.config.js 配置

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"); // 清除dist
const CompressionPlugin = require("compression-webpack-plugin");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");

module.exports = {
     
  devServer: {
     
    contentBase: path.resolve("dist"),
    host: "localhost", //服务器的IP地址,这里先使用loaclhost地址
    compress: true, //服务端压缩是否开启
    port: "8888", //配置服务端口号
    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"), //引入cssnano配置压缩选项
      cssProcessorOptions: {
     
        discardComments: {
      removeAll: true }
      },
      canPrint: true //是否将插件信息打印到控制台
    }),
    new ExtractTextWebpackPlugin({
     
      filename: "css/[name].[hash:8].css", // 配置提取出来的css名称
      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: {
     
              //当加载的图片小于limit时,会将图片编译成base64字符串的形式,
              //当图片大于这个limit,会用file-loader进行加载
              limit: 10000,
              //在webpack4.x必须显式的指定fallback备用方法,这里指定为file-loader
              fallback: require.resolve("file-loader"),
              encoding: "base64",
              outputPath: "images/",
              publichPath: "images/",
              name: "[name].[hash:8].[ext]",
              esModule: false //解决方法
            }
          }
        ]
      }
    ]
  }
};

6 图片路径问题

在处理这一段代码的时候,最令人无奈的就是关于,jq中插入过html,你所有的语法是$(’.xx’).html(xx),在这个阶段,你很容易会遇到一个巨大的坑…就是图片无法被webpack去解析,这样打包出来的图片。

有三种解决方案的思路

  • 直接以cdn的形式引入,一个http图片,不存在这个问题
  • 把页面放入到我们已经准备好的public目录下,使用绝对路径去解决
  • 在代码中使用require方法去引入一些图片,然后作为代码的解析

7 单页面配置-Vue

前面做了那么多业务,目的就是从业务上可以往vue页面靠齐…那么肯定不会是以vue-cil 这样方式出现,那么我就要研究一下vue-cil的本质,其实还是一个webpack,那么为什么可以解析vue,less。既然是多页面了,又怎么兼容?

7.1 vue项目建立

熟悉的项目格式又回来了,这里就不多做介绍了

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

7.2 配置单页面

在我们刚刚的路由中,我们设置一下

resource.js 资源文件中增加

"vue-template-js": "./src/pages/vue-template/main.js"

router.js 路由文件中增加

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

7.3 webpack配置

首先我们需要加载less,然后将我们熟悉的px,自动转rem、自动加上兼容前缀

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"
}

7.4 配置rem自动化

postcss.config.js

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

7.5 编写vue-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模板</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">
            正在加载资源
          </div>
          <div class="webpack-home__sub-title">
            初次加载资源可能需要较多时间 请耐心等待
          </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>

7.6 编写vue-template/main.js

实例化一个vue项目,这里只做一个简单的实例化配置,并添加路由

更多可以根据业务情况来配置,比如按需加载什么的,这里只是一个简单的阐述结构

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");

8 自动化发布

在deploy目录下,设置

products.js 服务器配置

/*
 *定义多个服务器账号 及 根据 SERVER_ID 导出当前环境服务器账号
 */
const SERVER_LIST = [
  {
     
    id: 0,
    name: "A-测试环境",
    host: "127.0.0.1", // ip
    url: "http://www.baidu.com",
    port: 22, // 端口
    username: "root", // 登录服务器的账号
    password: "", // 登录服务器的账号
    path: "" // 发布至静态服务器的项目路径
  }
];

module.exports = SERVER_LIST;

index.js 执行函数

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(
  "正在发布到" +
    (process.env.NODE_ENV === "prod" ? "生产" : "测试") +
    "服务器..."
);

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

var conn = new Client();
conn
  .on("ready", function() {
     
    // rm 删除dist文件,\n 是换行 换行执行 重启nginx命令 我这里是用docker重启nginx
    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) {
     
          // 在执行shell命令后,把开始上传部署项目代码放到这里面
          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("发布失败.\n"));
                throw err;
              } else {
     
                console.log(
                  chalk.green(
                    "Success! 成功发布到" +
                      (process.env.NODE_ENV === "prod" ? "生产" : "测试") +
                      "服务器! \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,
  });

9 git地址

https://github.com/MYQ1996/jq-webpack.git

你可能感兴趣的:(webpack,webpack,javascript)