兩個項目一個webpack包

前言:文件依賴關係錯綜複雜,靜態資源請求效率低,模塊化支持不友好,瀏覽器對高級JS兼容程度低?那就是時候瞭解webpack了javascript

1、簡單瞭解webpack

(1)概念

webpack 是一個JavaScript 應用程序的靜態模塊打包構建器。在處理應用程序時, webpack 會遞歸地構建一個依賴關係圖,其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle。webpack提供了友好的模塊化支持,以及代碼壓縮混淆、高級js兼容、性能優化。css

(2)核心

1.入口(entry):指定webpack打包編譯從哪一個文件開始下手

入口起點(entry point)指示 webpack 使用哪一個模塊,做爲構建其內部依賴圖的開始。進入入口起點後,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。html

webpack.config.js:vue

module.exports = {
 entry: {
    main: './src' //打包入口,來指定一個入口起點(或多個入口起點,默認值爲 ./src)
  },
 entry: './src', //這個是上面的簡寫方式,是等價的
 entry: {
  home: "./home.js",
  about: "./about.js",
  contact: "./contact.js"
 },//對象法指定多個入口,若是你想要多個依賴一塊兒注入到一個模塊,向 entry 屬性傳入「文件路徑(file path)數組」。
 entry: () => new Promise((resolve) => resolve(['./demo', './demo2'])),//動態入口,當結合 output.library 選項時:若是傳入數組,則只導出最後一項
};

2.出口(output):指定webpack打包編譯後的路徑及文件名

output 屬性告訴 webpack 在哪裏輸出它所建立的 bundles,以及如何命名這些文件。基本上,整個應用程序結構,都會被編譯到你指定的輸出路徑的文件夾中。java

webpack.config.js:node

const path = require('path');

module.exports = {
  output: {
    path: path.resolve(__dirname, 'dist'),//打包文件夾名,默認值爲 ./dist
    filename: '[name].js'//入口文件名
  }
};

3.loader(加載器):webpack識別不了的語言經過加載器來翻譯

loader 用於轉換某些類型的模塊,webpack 自身只理解 JavaScript,loader  能夠將全部類型的文件轉換爲 webpack 可以處理的有效模塊。loader 可以 import 導入任何類型的模塊(如 .css),是 webpack 特有的功能,其餘打包工具備可能不支持。webpack

webpack.config.js:ios

const path = require('path');

const config = {
  module: {
    rules: [//在 webpack 配置中定義 loader 時,要定義在 module.rules 中,裏面包含兩個"必須屬性":test 和 use
      { 
        test: /\.txt$/, //test 定義須要使用相應 loader 進行轉換的某類文件
        use: 'raw-loader' //use 定義使用哪一個 loader 進行轉換
      } 
    ]
  }
};

module.exports = config;

4.插件(plugins):webpack完成不了的功能經過插件來完成

插件接口功能極其強大,能夠用來處理各類各樣的任務。經過require() 使用插件,而後把它添加到 plugins 數組中。多數插件能夠經過選項(option)自定義。es6

webpack.config.js:web

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 經過 npm 安裝
const webpack = require('webpack'); // 用於訪問內置插件

const config = {
  plugins: [
    //在一個配置文件中由於不一樣目的而屢次使用同一個插件,須要經過使用 new 操做符來建立它的一個實例
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

module.exports = config;

5.模式(mode):經過模式肯定是開發環境仍是生產環境以便加載不一樣配置

module.exports = {
  mode: 'production'//經過選擇 development(開發) 或 production(生產),啓用相應模式下的 webpack 內置的優化
};

6.配置

webpack 須要傳入一個配置對象, 根據對象定義的屬性進行解析,所以不多有 webpack 配置看起來很徹底相同。 webpack 的配置文件,是導出一個對象的 JavaScript 文件。能夠經過兩種方式(終端、Node.js)使用 webpack。

 webpack 配置是標準的 Node.js CommonJS 模塊,你能夠:

  • 經過 require(...) 導入其餘文件
  • 經過 require(...) 使用 npm 的工具函數
  • 使用 JavaScript 控制流表達式,例如 ?: 操做符
  • 對經常使用值使用常量或變量
  • 編寫並執行函數來生成部分配置

應避免如下作法

  • 在使用 webpack 命令行接口(CLI)(應該編寫本身的命令行接口(CLI),或使用 --env)時,訪問命令行接口(CLI)參數
  • 導出不肯定的值(調用 webpack 兩次應該產生一樣的輸出文件)
  • 編寫很長的配置,應該將配置拆分爲多個文件

2、動手搭建一個

(1)前提必備

 webpack 配置是標準的 Node.js CommonJS 模塊,在安裝webpack以前,請確保安裝了 Node.js 的最新版本,使用舊版本可能遇到各類問題(可能缺乏 webpack 功能或者缺乏相關 package 包)。

(2)準備工做

1.安裝webpack

對於大多數項目,建議本地安裝。這能夠在引入破壞式變動(breaking change)的依賴時,更容易分別升級項目。不推薦全局安裝 webpack。這會將你項目中的 webpack 鎖定到指定版本,而且在使用不一樣的 webpack 版本的項目中,可能會致使構建失敗。

①本地安裝
npm install --save-dev webpack //安裝最新版本
npm install --save-dev webpack@<version> //安裝特定版本

②全局安裝 
npm install --global webpack

2. 安裝 CLI

若是你使用 webpack 4+ 版本,你還須要安裝 CLI,此工具用於在命令行中運行 webpack。

npm install --save-dev webpack-cli //webpack-cli用於在命令行中運行 webpack

(3)上手操做

在已有的項目中:

npm init -y  //初始化webpack  這裏會自動生成一個package.json
npm i -D webpack webpack-cli //安裝webpack及其腳手架

一個項目包兩個項目demo1和demo2(適用於兩個項目功能需求很相似有公用的地方有不一樣的地方):

{
  "name": "webpackstudy",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "demo1-start": "webpack-dev-server --progress --color",
    "demo2-start": "webpack-dev-server --progress --color",
    "demo1-mock": "webpack-dev-server --progress --color",
    "demo2-mock": "webpack-dev-server --progress --color",
    "demo1-te": "webpack --progress --color",
    "demo2-te": "webpack --progress --color"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.19.0",
    "vue": "^2.6.10",
    "vue-pull-to": "^0.1.8",
    "vue-router": "^3.1.2",
    "vuex": "^3.1.1"
  },
  "devDependencies": {
    "@babel/core": "^7.5.5",
    "@babel/preset-env": "^7.5.5",
    "autoprefixer": "^9.6.1",
    "babel-loader": "^8.0.6",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.2.0",
    "file-loader": "^4.2.0",
    "html-webpack-plugin": "^3.2.0",
    "html-withimg-loader": "^0.1.16",
    "jsonc": "^2.0.0",
    "less": "^3.10.2",
    "less-loader": "^5.0.0",
    "mini-css-extract-plugin": "^0.8.0",
    "mocker-api": "^1.8.1",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "style-loader": "^1.0.0",
    "terser-webpack-plugin": "^1.4.1",
    "uglifyjs-webpack-plugin": "^2.2.0",
    "url-loader": "^2.1.0",
    "vue-loader": "^15.7.1",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.6.10",
    "webpack": "^4.39.2",
    "webpack-cli": "^3.3.7",
    "webpack-dev-server": "^3.8.0"
  }
}
const path = require('path');
const webpack = require('webpack');
// script、link動態添加每次compile後的hash,防止引用緩存的外部文件問題; 自動建立html入口文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 用terser-webpack-plugin替換掉uglifyjs-webpack-plugin解決uglifyjs不支持es6語法問題
const TerserJSPlugin = require('terser-webpack-plugin');
// 此模塊至少須要Node v6.9.0和webpack v4.0.0  混淆代碼
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
// 這個插件將CSS提取到單獨的文件中 支持按需加載CSS和SourceMaps 創建在webpack v4上
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 用於優化、壓縮CSS資源
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// 打包時將以前打包的目錄裏的文件先清除乾淨,再生成新的
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
//mock數據
const apiMocker = require('mocker-api');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
//獲取 npm run 後面的命令
const lifecycle = process.env.npm_lifecycle_event;
//npm run 的文件名
const project = lifecycle.split('-')[0];
//npm run 的環境名
const proMode = lifecycle.split('-')[1] === 'te';
const envMode = lifecycle.split('-')[1];
const webpackConfig = {
    mode: proMode ? 'production' : 'development',
    //打包入口爲每一個項目的main.js
    entry: path.resolve(__dirname, `${project}/main.js`),
    output: {
        //打包後的文件目錄爲各自項目名+Dist
        path: path.resolve(__dirname, `${project}Dist`),
        //打包後源代碼映射
        // devtool: proMode ?'cheap-module-eval-source-map':'hidden-source-map',
        // devtool: "inline-source-map",
        //打包後的出口js目錄
        filename: 'js/[name].[hash].js',
        //分塊打包的js目錄
        chunkFilename: proMode ? 'js/[name].[contenthash].bundle.js' : 'js/[name].bundle.js',
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                exclude: /node_modules/,
                use: {
                    loader: 'vue-loader'
                }
            },
            {
                test: /\.(le|c)ss$/i,
                use: [
                    proMode ? MiniCssExtractPlugin.loader :
                        'vue-style-loader',
                    'css-loader',
                    'postcss-loader',
                    'less-loader',
                ],
            },
            {
                test: /\.html$/i,
                use: [
                    'html-withimg-loader'
                ]
            },
            {
                test: /\.(png|jpg|gif)$/i,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 4096,
                            name: 'img/[name].[contenthash].[ext]'
                        },
                    },
                ],
            },
            {
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            }
        ]
    },
    //webpack-dev-server 配置npm run 時啓動本地服務
    devServer: {
        contentBase: `./${project}Dist`,
        inline: true //實時刷新
    },
    //優化
    optimization: {
        // 分塊
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                },
            }
        }
    },
    plugins: [
        new CleanWebpackPlugin(),
        //定義插件—— 在項目中能夠讀取到
        new webpack.DefinePlugin({
            'baseUrl':proMode ? 'https:www.baidu.com':JSON.stringify('localhost')
        }),
        new HtmlWebpackPlugin({
            title:'webpack練習',
            filename: 'index.html',
            template: `${project}/index.html`,
            // 對 html 文件進行壓縮
            minify: {
                //是否對大小寫敏感,默認false
                caseSensitive: false,

                //是否簡寫boolean格式的屬性如:disabled="disabled" 簡寫爲disabled  默認false
                collapseBooleanAttributes: true,
                //是否去除空格,默認false
                collapseWhitespace: true,

                //是否壓縮html裏的css(使用clean-css進行的壓縮) 默認值false;
                minifyCSS: true,

                //是否壓縮html裏的js(使用uglify-js進行的壓縮)
                minifyJS: true,
                //是否移除註釋 默認false
                removeComments: true,
                //Prevents the escaping of the values of attributes
                preventAttributesEscaping: true,

                //是否移除屬性的引號 默認false
                removeAttributeQuotes: true,

                //從腳本和樣式刪除的註釋 默認false
                removeCommentsFromCDATA: true,

                //是否刪除空屬性,默認false
                removeEmptyAttributes: false,

                //  若開啓此項,生成的html中沒有 body 和 head,html也未閉合
                removeOptionalTags: false,

                //刪除多餘的屬性
                removeRedundantAttributes: true,

                //刪除script的類型屬性,在h5下面script的type默認值:text/javascript 默認值false
                removeScriptTypeAttributes: true,

                //刪除style的類型屬性, type="text/css" 同上
                removeStyleLinkTypeAttributes: true,

                //使用短的文檔類型,默認false
                useShortDoctype: false,
            }
        }),
        new VueLoaderPlugin()
    ],
    //目錄映射
    resolve: {
        alias: {
            '@assets': path.resolve(__dirname, `${project}/assets`),
            '@mixins': path.resolve(__dirname, `${project}/mixins`),
            '@tools': path.resolve(__dirname, `${project}/tools`),
            '@components': path.resolve(__dirname, `${project}/components`),
        }
    }
};
if (proMode) {
    webpackConfig.optimization.minimizer = [
        //混淆語法
        new UglifyJsPlugin({
            chunkFilter: (chunk) => {
                if (chunk.name === 'vendor') {
                    return false;
                }
                return true;
            },
            //去掉控制檯日誌
            uglifyOptions: {
                compress: {
                    drop_console: true
                }
            }
        }),
        new OptimizeCssAssetsPlugin({})
    ];
    // webpackConfig.optimization.minimizer = [new TerserJSPlugin({}),
    //     new OptimizeCssAssetsPlugin({}),];
    webpackConfig.plugins.push(
        new MiniCssExtractPlugin({
            filename: proMode ? '[name].css' : '[name].[hash].css',
            chunkFilename: proMode ? '[id].css' : '[id].[hash].css',
        })
    )
} else {
    // 熱更新模塊
    webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
    webpackConfig.devtool = 'inline-source-map';
    if (envMode == 'mock') {
        //mock環境,啓用mock代理服務
        webpackConfig.devServer.before = (app) => {
            apiMocker(app, path.resolve(`${project}/mock/api.js`));
        };
        //非mock匹配項走測試環境
        webpackConfig.devServer.proxy = process.baseUrl;
    }
}
module.exports = webpackConfig;
相關文章
相關標籤/搜索