教你用webpack搭一個vue腳手架[超詳細講解和註釋!]

null仔發表於2018-01-08

1.適用人群

    1.對webpack知識有一定了解但不熟悉的同學.
    
    2.女同學!!!(233333....)
複製程式碼

2.目的

 在自己對webpack有進一步瞭解的同時,也希望能幫到一些剛接觸webpack的同學.
複製程式碼
腳手架已放上github,不想聽我囉嗦的同學可以直接去download或clone下來看哦.

腳手架裡都有詳細註釋!

複製程式碼

傳送門

覺得有幫助到你的同學給個star哈,也算是對我的一種支援!

3.腳手架結構

├── build                       構建服務和webpack配置
    |—— build.js                webpack打包服務
    |—— webpack.base.conf.js    webpack基本通用配置
    |—— webpack.dev.conf.js     webpack開發環境配置
    |—— webpack.prod.conf.js    webpack生產環境配置
├── config                      構建專案不同環境的配置
├── public                      專案打包檔案存放目錄
├── index.html                  專案入口檔案
├── package.json                專案配置檔案
├── static       	            靜態資源
├── .babelrc                    babel配置檔案
├── .gitignore                  git忽略檔案
├── postcss.config.js           postcss配置檔案
├── src                         專案目錄
    |—— page                    頁面元件目錄
    |—— router                  vue路由配置
    |—— store                   vuex配置
    |—— App.vue                 vue例項入口
    |—— main.js                 專案構建入口


複製程式碼

4.配置npm scripts

4.1 生成package.json檔案,配置npm scripts.

4.1.1 使用 npm init 命令,生成一個package.json檔案!

    npm init
複製程式碼

4.1.2 全域性安裝webpack和webpack-dev-server

   npm install webpack webpack-dev-server -g
複製程式碼

4.1.3 在專案目錄下安裝webpack和webpack-dev-server

  npm install webpack webpack-dev-server -D
複製程式碼

4.1.4 進入package.json配置npm scripts命令

  "scripts": {
    "dev": "webpack-dev-server  --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "build": "node build/build.js"
  }

  通過配置以上命令:
  我們可以通過npm start/npm run dev在本地進行開發,
  scripts.dev命令解讀:
  通過webpack-dev-server命令 啟動build資料夾下webpack.dev.conf.js。
  也可以通過npm run build 打包專案檔案進行線上部署.
  scripts.build命令解讀:
  通過node命令構建build資料夾下的build.js。
  命令的配置可以根據自己腳手架的配置檔案位置和名稱不同修改哦!
複製程式碼

5.構建腳手架目錄

同學們可以通過自己的習慣和喜愛搭建自己的腳手架目錄,下面講解以上面腳手架結構為準!
複製程式碼

6.構建config/config.js

6.1 該檔案主要用來配置構建開發環境和生產環境差異化的引數. 6.2

const _path = require("path");
const ExtractTextPlugin = require("extract-text-webpack-plugin");

//vue-loader基本配置
const baseVueLoaderConf = {
  //引入postcss外掛
  postcss: {
    config: {
      path: _path.resolve("../")
    }
  },
  //轉為require呼叫,讓webpack處理目標資源!
  transformToRequire: {
    video: "src",
    source: "src",
    img: "src",
    image: "xlink:href"
  }
};

//vue-loader 開發環境配置
const devVueLoaderConf = Object.assign({}, baseVueLoaderConf, {
  //loaders
  loaders: {
    css: ["vue-style-loader", "css-loader"],
    less: ["vue-style-loader", "css-loader", "postcss-loader", "less-loader"]
  },
  cssSourceMap: true
});

//vue-loader 生產環境配置
const buildVueLoaderConf = Object.assign({}, baseVueLoaderConf, {
  //loaders
  loaders: ExtractTextPlugin.extract({
    use: ["css-loader", "postcss-loader", "less-loader"],
    fallback: "vue-style-loader"
  }),
  cssSourceMap: false
});

//開發/生產環境 配置引數!
module.exports = {
  dev: {
    publicPath: "/",
    devtoolType: "cheap-module-eval-source-map",
    vueloaderConf: devVueLoaderConf,
    host: "localhost",
    port: "1234",
    proxyTable: {}
  },
  build: {
    publicPath: "/",
    devtoolType: "source-map",
    vueloaderConf: buildVueLoaderConf,
    staticPath: "static"
  }
};

複製程式碼

7.構建build/webpack.base.conf.js

7.1 此檔案主要是webpack開發環境和生成環境的通用配置.

7.2

"use strict";

//引入node path路徑模組
const path = require("path");
//引入webpack生產環境配置引數
const prodConfig = require("../config").build;

//拼接路徑
function resolve(track) {
  return path.join(__dirname, "..", track);
}
//資源路徑
function assetsPath(_path) {
  return path.join(prodConfig.staticPath, _path);
}

//webpack 基本設定

module.exports = {
  //專案入口檔案->webpack從此處開始構建!
  entry: path.resolve(__dirname, "../src/main.js"),
  //配置模組如何被解析
  resolve: {
    //自動解析副檔名(補全檔案字尾)(從左->右)
    // import hello from './hello'  (!hello.js? -> !hello.vue? -> !hello.json)
    extensions: [".js", ".vue", ".json"],
    //配置別名對映
    alias: {
      // import Vue from 'vue/dist/vue.esm.js'可以寫成 import Vue from 'vue'
      // 鍵後加上$,表示精準匹配!
      vue$: "vue/dist/vue.esm.js",
      "@": resolve("src"),
      utils: resolve("src/utils"),
      components: resolve("src/components"),
      public: resolve("public")
    }
  },
  module: {
    //處理模組的規則(可在此處使用不同的loader來處理模組!)
    rules: [
      //使用babel-loader來處理src下面的所有js檔案,具體babel配置在.babelrc,主要是用來轉義es6
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader"
        },
        include: resolve("src")
      },
      //使用url-loader(file-loader的一個再封裝)對引入的圖片進行編碼,此處可將小於8192位元組(8kb)的圖片轉為DataURL(base64),
      //大於limit位元組的會呼叫file-loader進行處理!
      //圖片一般釋出後都是長快取,故此處檔名加入hash做版本區分!
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 8192,
          name: assetsPath("img/[name].[hash:8].[ext]")
        }
      }
    ]
  }
};

複製程式碼

8.構建 build/webpack.dev.conf.js

8.1 該檔案主要用於構建開發環境

8.2

"use strict";
//引入node path路徑模組
const path = require("path");
//引入webpack
const webpack = require("webpack");
//引入webpack開發環境配置引數
const devConfig = require("../config").dev;
//引入webpack基本配置
const baseConf = require("./webpack.base.conf");
//一個webpack配置合併模組,可簡單的理解為與Object.assign()功能類似!
const merge = require("webpack-merge");
//一個建立html入口檔案的webpack外掛!
const HtmlWebpackPlugin = require("html-webpack-plugin");
//一個編譯提示的webpack外掛!
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
//傳送系統通知的一個node模組!
const notifier = require("node-notifier");
//將webpack基本配置與開發環境配置合併!
const devConf = merge(baseConf, {
  //專案出口,webpack-dev-server 生成的包並沒有寫入硬碟,而是放在記憶體中!
  output: {
    //檔名
    filename: "[name].js",
    //html引用資源路徑,在dev-server中,引用的是記憶體中檔案!
    publicPath: devConfig.publicPath
  },
  //生成sourceMaps(方便除錯)
  devtool: devConfig.devtoolType,
  //
  //啟動一個express伺服器,使我們可以在本地進行開發!!!
  devServer: {
    //HMR控制檯log等級
    clientLogLevel: "warning",
    // 熱載入
    hot: true,
    //自動重新整理
    inline: true,
    //自動開啟瀏覽器
    open: true,
    //在開發單頁應用時非常有用,它依賴於HTML5 history API,如果設定為true,所有的跳轉將指向index.html
    historyApiFallback: true,
    //主機名
    host: devConfig.host,
    //埠號
    port: devConfig.port,
    //配置反向代理解決跨域
    proxy: devConfig.proxyTable,
    //為你的程式碼進行壓縮。加快開發流程和優化的作用
    compress: true,
    // 在瀏覽器上全屏顯示編譯的errors或warnings。
    overlay: {
      errors: true,
      warnings: false
    },
    // 終端輸出的只有初始啟動資訊。 webpack 的警告和錯誤是不輸出到終端的
    quiet: true
  },
  module: {
    //處理模組的規則(可在此處使用不同的loader來處理模組!)
    rules: [
      //使用vue-loader處理以vue結尾的檔案!
      {
        test: /\.vue$/,
        loader: "vue-loader",
        options: devConfig.vueloaderConf
      },
      //使用vue-style-loader!css-loader!postcss-loader處理以css結尾的檔案!
      {
        test: /\.css$/,
        use: [
          "vue-style-loader",
          {
            loader: "css-loader",
            options: {
              sourceMap: true
            }
          },
          {
            loader: "postcss-loader",
            options: {
              sourceMap: true
            }
          }
        ]
      },
      //使用vue-style-loader!css-loader!postcss-loader處理以less結尾的檔案!
      {
        test: /\.less$/,
        use: [
          "vue-style-loader",
          {
            loader: "css-loader",
            options: {
              sourceMap: true
            }
          },
          {
            loader: "less-loader",
            options: {
              sourceMap: true
            }
          },
          {
            loader: "postcss-loader",
            options: {
              sourceMap: true
            }
          }
        ]
      }
    ]
  },
  plugins: [
    //開啟HMR(熱替換功能,替換更新部分,不過載頁面!)
    new webpack.HotModuleReplacementPlugin(),

    //顯示模組相對路徑
    new webpack.NamedModulesPlugin(),

    //編譯出錯時,該外掛可跳過輸出,確保輸出資源不會包含錯誤!
    // new webpack.NoEmitOnErrorsPlugin(),

    //配置html入口資訊
    new HtmlWebpackPlugin({
      title: "hello,xc-cli!",
      filename: "index.html",
      template: "index.html",
      //js資源插入位置,true表示插入到body元素底部
      inject: true
    }),

    //編譯提示外掛
    new FriendlyErrorsPlugin({
      //編譯成功提示!
      compilationSuccessInfo: {
        messages: [
          `Your application is running here: http://${devConfig.host}:${devConfig.port}`
        ]
      },
      //編譯出錯!
      onErrors: function(severity, errors) {
        if (severity !== "error") {
          return;
        }
        const error = errors[0];
        const filename = error.file.split("!").pop();
        //編譯出錯時,右下角彈出錯誤提示!
        notifier.notify({
          title: "xc-cli",
          message: severity + ": " + error.name,
          subtitle: filename || "",
          icon: path.join(__dirname, "xc-cli.png")
        });
      }
    })
  ]
});
module.exports = devConf;

複製程式碼

8.3 通過建立以上檔案,並下載相應的依賴和建立專案入口,我們就可以通過npm run dev在本地開發vue專案啦!!!

9.建立 build/webpack.prod.conf.js

9.1 此檔案主要用於構建生產環境的配置. 9.2

"use strict";
//引入node path路徑模組
const path = require("path");
//引入webpack
const webpack = require("webpack");
//一個webpack配置合併模組,可簡單的理解為與Object.assign()功能類似!
const merge = require("webpack-merge");
//引入webpack生產環境配置引數
const prodConfig = require("../config").build;
//引入webpack基本配置
const baseConf = require("./webpack.base.conf");
//一個建立html入口檔案的webpack外掛!
const HtmlWebpackPlugin = require("html-webpack-plugin");
//一個抽離出css的webpack外掛!
const ExtractTextPlugin = require("extract-text-webpack-plugin");
//一個壓縮css的webpack外掛!
const OptimizeCSSPlugin = require("optimize-css-assets-webpack-plugin");
//一個拷貝檔案的webpack外掛!
const CopyWebpackPlugin = require("copy-webpack-plugin");

//資源路徑
function assetsPath(_path) {
  return path.join(prodConfig.staticPath, _path);
}
//將webpack基本配置與生產環境配置合併!
const prodConf = merge(baseConf, {
  //專案出口配置
  output: {
    //Build後所有檔案存放的位置
    path: path.resolve(__dirname, "../public"),
    //html引用資源路徑,可在此配置cdn引用地址!
    publicPath: prodConfig.publicPath,
    //檔名
    filename: assetsPath("js/[name].[chunkhash].js"),
    //用於打包require.ensure(程式碼分割)方法中引入的模組
    chunkFilename: assetsPath("js/[name].[chunkhash].js")
  },
  //生成sourceMaps(方便除錯)
  devtool: prodConfig.devtoolType,
  module: {
    //處理模組的規則(可在此處使用不同的loader來處理模組!)
    rules: [
      //使用vue-loader處理以vue結尾的檔案!
      {
        test: /\.vue$/,
        loader: "vue-loader",
        options: prodConfig.vueloaderConf
      },
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          use: ["css-loader", "postcss-loader"],
          fallback: "vue-style-loader"
        })
      },
      {
        test: /\.less$/,
        use: ExtractTextPlugin.extract({
          use: ["css-loader", "less-loader", "postcss-loader"],
          fallback: "vue-style-loader"
        })
      }
    ]
  },
  plugins: [
    //每個chunk頭部新增hey,xc-cli!
    new webpack.BannerPlugin("hey,xc-cli"),

    //壓縮js
    new webpack.optimize.UglifyJsPlugin({
      parallel: true,
      compress: {
        warnings: false
      }
    }),

    //分離入口引用的css,不內嵌到js bundle中!

    new ExtractTextPlugin({
      filename: assetsPath("css/[name].[contenthash].css"),
      allChunks: false
    }),

    //壓縮css
    new OptimizeCSSPlugin(),

    //根據模組相對路徑生成四位數hash值作為模組id
    new webpack.HashedModuleIdsPlugin(),

    //作用域提升,提升程式碼在瀏覽器執行速度
    new webpack.optimize.ModuleConcatenationPlugin(),

    //抽離公共模組,合成一個chunk,在最開始載入一次,便快取使用,用於提升速度!

    // 1. 第三方庫chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: "vendor",
      minChunks: function(module) {
        //在node_modules的js檔案!
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(path.join(__dirname, "../node_modules")) === 0
        );
      }
    }),

    // 2. 快取chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: "manifest",
      minChunks: Infinity
    }),
    // 3.非同步 公共chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: "app",
      children: true,
      // (選擇所有被選 chunks 的子 chunks)
      async: true,
      // (建立一個非同步 公共chunk)
      minChunks: 3
      // (在提取之前需要至少三個子 chunk 共享這個模組)
    }),

    //將整個檔案複製到構建輸出指定目錄下
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, "../static"),
        to: prodConfig.staticPath,
        ignore: [".*"]
      }
    ]),

    //生成html
    new HtmlWebpackPlugin({
      filename: path.resolve(__dirname, "../public/index.html"),
      template: "index.html",
      favicon: path.resolve(__dirname, "../favicon.ico"),
      //js資源插入位置,true表示插入到body元素底部
      inject: true,
      //壓縮配置
      minify: {
        //刪除Html註釋
        removeComments: true,
        //去除空格
        collapseWhitespace: true,
        //去除屬性引號
        removeAttributeQuotes: true
      },
      //根據依賴引入chunk
      chunksSortMode: "dependency"
    })
  ]
});
module.exports = prodConf;

複製程式碼

#10. 建立 build/build.js 10.1 此檔案是專案打包服務,用來構建一個全量壓縮包 10.2

"use strict";
//node for loading
const ora = require("ora");
// rm-rf for node
const rm = require("rimraf");
//console for node
const chalk = require("chalk");
//path for node
const path = require("path");
//webpack
const webpack = require("webpack");
//webpack production setting
const config = require("./webpack.prod.conf");
//指定刪除的檔案
const rmFile = path.resolve(__dirname, "../public/static");
//build start loading
const spinner = ora("building for production...");
spinner.start();

//構建全量壓縮包!
rm(rmFile, function(err) {
  if (err) throw err;
  webpack(config, function(err, stats) {
    spinner.stop();
    if (err) throw err;
    process.stdout.write(
      stats.toString({
        colors: true,
        modules: false,
        children: false,
        chunks: false,
        chunkModules: false
      }) + "\n\n"
    );

    if (stats.hasErrors()) {
      console.log(chalk.red("  Build failed with errors.\n"));
      process.exit(1);
    }

    console.log(chalk.cyan("  Build complete.\n"));
    console.log(
      chalk.yellow(
        "  Tip: built files are meant to be served over an HTTP server.\n" +
          "  Opening index.html over file:// won't work.\n"
      )
    );
  });
});

複製程式碼

10.3 建立好以上檔案 我們就可以通過npm run build來打包我們的專案檔案並部署上線啦。

11.大功告成!

通過以上步驟,一個spa版的vue腳手架就大功告成啦!

如果對一些細節不懂的可以留言或者上我的github檢視

傳送門

最後還是那句話,如果有幫助到你,請給我star支援哈!

相關文章