webpack: 多頁(yè)面+vue單頁(yè)面 老項(xiàng)目jq升級(jí)

1 入手項(xiàng)目

進(jìn)入一家老的公司,公司規(guī)模也不大,但是我們從git上下載后,我們猛然發(fā)現(xiàn),這項(xiàng)目有一種心痛的感覺(jué)。

image.png
image.png

這都2020年了,居然還有人用這么古老的方案去進(jìn)行一個(gè)項(xiàng)目。那么這時(shí)候,我們?cè)撛趺崔k,我們陷入了沉思,繼續(xù)維護(hù)?還是準(zhǔn)備跑路?忍住,我們先看看代碼...

目錄 一個(gè)公共的index代碼....居然都沒(méi)有抽離...如下代碼大概有個(gè)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 項(xiàng)目分析

不能慌張,我們可是前端工程師...那么我們?cè)撛趺崔k呢?首先我們先分析一下我們可以怎么辦,那么我們首先分析一下他使用的技術(shù)棧和運(yùn)用場(chǎng)景。

2.1 運(yùn)用場(chǎng)景

通過(guò)和項(xiàng)目組,產(chǎn)品的溝通。該項(xiàng)目,是運(yùn)行在微信公眾號(hào)上的的一個(gè)h5頁(yè)面。那么能夠給你的時(shí)間,差不多是一周時(shí)間,去熟悉了解,整個(gè)項(xiàng)目。

2.2 技術(shù)棧架構(gòu)分析

1. js架構(gòu)使用技術(shù)棧
  1.1 jquery
  1.2 jweixin
  1.3 swiper.min
2. css解決方案
   純手寫(xiě),手動(dòng)rem
其實(shí)這時(shí)候,不難發(fā)現(xiàn),就是累加的js,http未封裝狀態(tài)...
方法 方案 缺點(diǎn)
文件夾隔離 將老代碼,丟到我看不到的地方,繼續(xù)開(kāi)發(fā)新項(xiàng)目。(看不到,那就當(dāng)做沒(méi)有問(wèn)題) 在老項(xiàng)目的代碼,硬傷還是硬傷,新代碼的架構(gòu)被迫跟隨
微前端 做一個(gè)大規(guī)模容器,將新老項(xiàng)目做一個(gè)中間的橋接,讓主架構(gòu)負(fù)責(zé)項(xiàng)目的溝通,保證2個(gè)服務(wù)器正常運(yùn)行 微前端的技術(shù)方案,大部分實(shí)例是作為后端管理系統(tǒng)中運(yùn)行,在手機(jī)端中的適配能力未知。
webpack 多文件打包方案,讓新老代碼,在工具中兼容 人力改代碼

3 webpack技術(shù)

webpack的優(yōu)點(diǎn)不言而喻,如果不清楚的,可以去看webpack官網(wǎng)的介紹

本質(zhì)上,webpack 是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)。當(dāng) webpack 處理應(yīng)用程序時(shí),它會(huì)遞歸地構(gòu)建一個(gè)依賴(lài)關(guān)系圖(dependency graph),其中包含應(yīng)用程序需要的每個(gè)模塊,然后將所有這些模塊打包成一個(gè)或多個(gè) bundle。

4 目錄結(jié)構(gòu)

首先我們肯定是不會(huì)想去想改動(dòng),老項(xiàng)目代碼,那畢竟是老項(xiàng)目...你去改動(dòng)了...萬(wàn)一東西墻崩潰..最后的事故可不小。那么新頁(yè)面呢..我們肯定是希望去使用單頁(yè)面技術(shù),畢竟加載和體驗(yàn)上,都有了畢竟好的體驗(yàn)。

先將原來(lái)的文件復(fù)制出一個(gè)來(lái),我們也不希望在改動(dòng)的過(guò)程中,破壞了代碼原本的功能

mkdir jq-webpack
yarn add webpack@4.19.1 webpack-cli@2.1.4 -D
touch webpack.config.js

這時(shí)候,項(xiàng)目需要將 package.json 添加上指令

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

然后開(kāi)始配置 webpack.config.js 原則上可以配置多種環(huán)境但是我們這邊為了簡(jiǎn)單就配置一種

在項(xiàng)目結(jié)構(gòu)不算過(guò)于復(fù)雜的情況下,其實(shí)我們還是可以理一下思路,就比如項(xiàng)目中,其實(shí)初始化css,js 都是可以抽離出來(lái)并且,可以形成一套完整的路由體系。那么就可以制作一個(gè)項(xiàng)目抽離的目錄結(jié)構(gòu)

- router
    - index.js  組合文件
  - resource.js 資源文件
  - router.js 路由文件
- src
    - common 公用的部分
    - css
    - js
    - images
  - pages 老項(xiàng)目的對(duì)應(yīng)關(guān)系
    - index
        - index.html
        - index.js
        - index.css
    - activity
        - index.html
        - index.js
        - index.css
    - ....
  - utils 工具庫(kù)
        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", // 啟動(dòng)指令
    "upload-test": "NODE_ENV=test node ./deploy", // 自動(dòng)化上傳-測(cè)試
    "upload-prod": "NODE_ENV=prod node ./deploy" // 自動(dòng)化上傳-正式
  },
  "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 多頁(yè)面配置

我們使用的技術(shù),是比較普遍的webpack,多頁(yè)面技術(shù)我們可以看到,每一個(gè)老項(xiàng)目的html 中,都會(huì)引入關(guān)于jq、自己的index.js、然后一股腦的images,又或許有些是放在自己的images里面...放在我們開(kāi)始做一些隔離,分類(lèi)組合,開(kāi)始開(kāi)多個(gè)文件夾,放入html、js、css。樣式將會(huì)比較清楚,這時(shí)候開(kāi)始做一些外部引入的操作。
首先我們配置一下router的文件,我這邊做了一些拆分,當(dāng)然你如果頁(yè)面能模塊化的,建議拆分的更細(xì)。

5.1 路由設(shè)置

resource.js 將文件路徑中的設(shè)置放入資源管理庫(kù)中

const entry = {
  // 首頁(yè)
  "index-css": "./src/index/index.css",
  "index-js": "./src/index/index.js",
  
  // 活動(dòng)頁(yè)
  "activity-css": "./src/pages/activity/index.css",
  "activity-js": "./src/pages/activity/index.js",
}

module.exports = {
  entry
};

router.js 在路由頁(yè)面中去組合

const router = [
  {
    name: "首頁(yè)",
    filename: "index.html",
    chunks: ["index-css", "index-js"], // 如果多個(gè)可以引入多個(gè)
    template: "./src/pages/index/index.html",
  },
  {
    name: "活動(dòng)",
    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, //每個(gè)html只引入對(duì)應(yīng)的js和css
      inject: true,
      hash: true, //避免緩存js。
      template: item.template
    })
  );
});

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

當(dāng)然對(duì)于html也需要做一些處理處理,這里有兩種方法

  • 直接使用cdn引入庫(kù)、這里又分為免費(fèi)庫(kù),和公司自己的庫(kù)兩種
  • 放入公共的public中,頁(yè)面中引入
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>活動(dòng)頁(yè)面</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", //服務(wù)器的IP地址,這里先使用loaclhost地址
    compress: true, //服務(wù)端壓縮是否開(kāi)啟
    port: "8888", //配置服務(wù)端口號(hào)
    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配置壓縮選項(xiàng)
      cssProcessorOptions: {
        discardComments: { removeAll: true }
      },
      canPrint: true //是否將插件信息打印到控制臺(tái)
    }),
    new ExtractTextWebpackPlugin({
      filename: "css/[name].[hash:8].css", // 配置提取出來(lái)的css名稱(chēng)
      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: {
              //當(dāng)加載的圖片小于limit時(shí),會(huì)將圖片編譯成base64字符串的形式,
              //當(dāng)圖片大于這個(gè)limit,會(huì)用file-loader進(jìn)行加載
              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 圖片路徑問(wèn)題

在處理這一段代碼的時(shí)候,最令人無(wú)奈的就是關(guān)于,jq中插入過(guò)html,你所有的語(yǔ)法是$('.xx').html(xx),在這個(gè)階段,你很容易會(huì)遇到一個(gè)巨大的坑..就是圖片無(wú)法被webpack去解析,這樣打包出來(lái)的圖片。

有三種解決方案的思路

  • 直接以cdn的形式引入,一個(gè)http圖片,不存在這個(gè)問(wèn)題
  • 把頁(yè)面放入到我們已經(jīng)準(zhǔn)備好的public目錄下,使用絕對(duì)路徑去解決
  • 在代碼中使用require方法去引入一些圖片,然后作為代碼的解析

7 單頁(yè)面配置-Vue

前面做了那么多業(yè)務(wù),目的就是從業(yè)務(wù)上可以往vue頁(yè)面靠齊...那么肯定不會(huì)是以vue-cil 這樣方式出現(xiàn),那么我就要研究一下vue-cil的本質(zhì),其實(shí)還是一個(gè)webpack,那么為什么可以解析vue,less。既然是多頁(yè)面了,又怎么兼容?

7.1 vue項(xiàng)目建立

熟悉的項(xiàng)目格式又回來(lái)了,這里就不多做介紹了

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

7.2 配置單頁(yè)面

在我們剛剛的路由中,我們?cè)O(shè)置一下

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,自動(dòng)轉(zhuǎn)rem、自動(dòng)加上兼容前綴

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自動(dòng)化

postcss.config.js

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

7.5 編寫(xiě)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">
            初次加載資源可能需要較多時(shí)間 請(qǐng)耐心等待
          </div>
        </div>
        <div class="webpack-home__footer"></div>
      </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </body>
</html>

7.6 編寫(xiě)vue-template/main.js

實(shí)例化一個(gè)vue項(xiàng)目,這里只做一個(gè)簡(jiǎn)單的實(shí)例化配置,并添加路由

更多可以根據(jù)業(yè)務(wù)情況來(lái)配置,比如按需加載什么的,這里只是一個(gè)簡(jiǎn)單的闡述結(jié)構(gòu)

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 自動(dòng)化發(fā)布

在deploy目錄下,設(shè)置

products.js 服務(wù)器配置

/*
 *定義多個(gè)服務(wù)器賬號(hào) 及 根據(jù) SERVER_ID 導(dǎo)出當(dāng)前環(huán)境服務(wù)器賬號(hào)
 */
const SERVER_LIST = [
  {
    id: 0,
    name: "A-測(cè)試環(huán)境",
    host: "127.0.0.1", // ip
    url: "http://www.baidu.com",
    port: 22, // 端口
    username: "root", // 登錄服務(wù)器的賬號(hào)
    password: "", // 登錄服務(wù)器的賬號(hào)
    path: "" // 發(fā)布至靜態(tài)服務(wù)器的項(xiàng)目路徑
  }
];

module.exports = SERVER_LIST;

index.js 執(zhí)行函數(shù)

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(
  "正在發(fā)布到" +
    (process.env.NODE_ENV === "prod" ? "生產(chǎn)" : "測(cè)試") +
    "服務(wù)器..."
);

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

var conn = new Client();
conn
  .on("ready", function() {
    // rm 刪除dist文件,\n 是換行 換行執(zhí)行 重啟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) {
          // 在執(zhí)行shell命令后,把開(kāi)始上傳部署項(xiàng)目代碼放到這里面
          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("發(fā)布失敗.\n"));
                throw err;
              } else {
                console.log(
                  chalk.green(
                    "Success! 成功發(fā)布到" +
                      (process.env.NODE_ENV === "prod" ? "生產(chǎn)" : "測(cè)試") +
                      "服務(wù)器! \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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容