Webpack超詳解

webpack

認識webpack

webpack : 模塊打包機;分析項目結構,找到相對應的模塊,並且找出一些瀏覽器不能直接識別的拓展性的比如less、typeScript等編譯成瀏覽器識別的文件;
es6 let a = 10; IE10不能識別es6;
less
import export
webpack會把es6編譯成es5,把less編譯成css;把ts編譯成js;
打包: 會把js進行編譯和壓縮,甚至把大量的js文件壓縮到一個js文件中,把圖片編譯成base64格式等等,當客戶端訪問這個項目時,不需要再發送那麼多次請求,壓縮了資源的大小;
瀏覽器只識別html,css,js

Webpack詳解

webpack網址

webpack是一個現代 JavaScript 應用程序的 靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序需要的每個模塊,然後將所有這些模塊打包成一個或多個bundle。webpack 本身是基於 node.js 開發的。
從 webpack v4.0.0 版本開始,可以不用引入一個配置文件(零配置文件),然而 webpack 仍然還是高度可配置的。

1. 安裝webpack

目前 _@vue/cli _和 _create-react-app _基本上採用的是 _webpack 4.0 _以上版本,所以以第四代版本爲主;第四代版本需要我們安裝 _webpack _和 webpack-cli(可執行命令);
爲了防止全局安裝 _webpack _的版本衝突,我們真實項目開發的時候基本上以安裝在本地項目中爲主;

$ npm init -y
$ npm install webpack webpack-cli --save-dev
OR
$ yarn add webpack webpack-cli -D

2.webpack的基礎使用

  • 初步體驗(零配置)
/*
 * 默認會打包SRC目錄中的JS文件(入口默認index.js)
 * 打包完成的目錄默認是DIST/MAIN.JS
 *
 * npx:http://www.ruanyifeng.com/blog/2019/02/npx.html
 * 默認執行node_modules/bin/webpack.cmd文件
 * webpack默認支持CommonJS和ES6 Module的模塊規範,依此進行依賴打包
 */
$ npx webpack
  • 自定義基礎配置
    • webpack.config.js OR webpackfile.js
let path = require('path');
module.exports = {
	// 打包模式,開發環境 development  生產環境 production
	mode: 'production',
	// 入口
	entry: './src/index.js',
	// 輸出
	output: {
		// 輸出文件的文件名
		filename: 'bundle.js',
		// 輸出目錄的"絕對路徑"
		path: path.resolve(__dirname, 'dist')
	}
}
  • 自定義配置文件名
    • $ npx webpack --config webpack.config.development.js
    • 可在package.json中配置可執行的腳本命令(區分開發環境)
"scripts": {
    "serve": "webpack --config webpack.config.development.js",
    "build": "webpack --config webpack.config.production.js"
},

3.webpack-dev-server

https://webpack.js.org/configuration/dev-server/

  • 安裝:$ yarn add webpack-dev-server -D
  • 基礎配置
/* webpack.config.js */
// 配置DEV-SERVER
devServer: {
    // 端口
	port: 3000,
    // 顯示編譯進度
    progress: true,
    // 指定訪問資源目錄
    contentBase: './dist',
    // 自動打開瀏覽器
    open: true
}
/* package.json */
"scripts": {
    "serve": "webpack-dev-server",
    "build": "webpack"
}
  • $ npm run serve
  • $ npx webpack-dev-server
  • 代碼更改後,會自動重新編譯,然後自動刷新頁面

4.html-webpack-plugin

https://www.webpackjs.com/plugins/html-webpack-plugin/

  • 安裝:$ yarn add html-webpack-plugin -D
  • 在webpack.config.js中使用
let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    ...,
    // 在 webpack 中使用插件
	plugins: [
		new HtmlWebpackPlugin({
			// 指定自己的模板
			template: './src/index.html',
			// 輸出的文件名
			filename: 'index.html',
			// 給引入的文件設置 HASH 戳(清除緩存的),也可以在 output 中設置 filename: 'bundle.[hash]
  		// .js' 來生成不同的文件
			hash: true,
			// 控制是否以及以何種方式最小化輸出 
    		// https://github.com/kangax/html-minifier
			minify: {
				collapseWhitespace: true,
				removeComments: true,
				removeAttributeQuotes: true,
				removeEmptyAttributes: true
			}
		})
	]
}

5.webpack中的加載器loader:處理樣式的

  • 安裝:$ yarn add css-loader style-loader less less-loader autoprefixer postcss-loader … -D
module.exports = {
    // 配置模塊加載器 LOADER
	module: {
		// 模塊規則:使用加載器(默認從右向左執行,從下向上)
		rules: [{
			test: /\.(css|less)$/, // 基於正則表達式匹配哪些模塊需要處理
			use: [
				"style-loader", // 把 CSS 插入到 HEAD 中
				"css-loader", // 編譯解析 @import/URL() 這種語法
                "postcss-loader", // 設置前綴
                {
					loader: "less-loader",
                    options: {
                        // 加載器額外的配置
                    }
				}
			]
		}]
	}
}

postcss.config.js

module.exports = {
	plugins: [
		require('autoprefixer')
	]
};

package.json

"browserslist": [
    "> 1%",
    "last 2 versions"
]

6.mini-css-extract-plugin 抽離CSS內容

https://www.npmjs.com/package/mini-css-extract-plugin

  • 安裝 $ yarn add mini-css-extract-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D
let MiniCssExtractPlugin = require('mini-css-extract-plugin'),
	OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'),
	UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
    // 設置優化項
	optimization: {
		// 設置壓縮方式
		minimizer: [
			// 壓縮 CSS(但是必須指定 JS 的壓縮方式)
			new OptimizeCssAssetsWebpackPlugin(),
			// 壓縮JS
			new UglifyjsWebpackPlugin({
				cache: true, // 是否使用緩存
				parallel: true, // 是否是併發編譯
				sourceMap: true, // 啓動源碼映射(方便調試)
			})
		]
	},
	plugins: [
		// 使用插件
		new MiniCssExtractPlugin({
			// 設置編譯後的文件名字
			filename: 'main.css'
		})
	],
	module: {
		rules: [{
			test: /\.(css|less)$/,
			use: [
				// "style-loader",
				// 使用插件中的 LOADER 代替 STYLE 方式
				MiniCssExtractPlugin.loader,
				"css-loader",
                "postcss-loader",
				"less-loader"
			]
		}]
	}
}

上述JS壓縮對於 require / import 等還存在問題,需要對於 ES6 中的一些語法進行處理!

7.基於babel實現ES6的轉換和ESLint語法檢測

https://babeljs.io/
https://eslint.org/

  • 安裝 $ yarn add babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-transform-runtime -D
  • 安裝 $ yarn add @babel/runtime @babel/polyfill
  • 安裝 $ yarn add eslint eslint-loader -D
module.exports = {
	...,
	module: {
		rules: [...,{
			test: /\.js$/,
			use: [{
				loader: 'babel-loader',
				options: {
					// 轉換的語法預設(ES6->ES5)
					presets: [
						"@babel/preset-env"
					],
					// 基於插件處理 ES6 / ES7 中 CLASS 的特殊語法
					plugins: [
						["@babel/plugin-proposal-decorators", {
							"legacy": true
						}],
						["@babel/plugin-proposal-class-properties", {
							"loose": true
						}],
						"@babel/plugin-transform-runtime"
					]
				}
			}], // "eslint-loader"
			// 設置編譯時忽略的文件和指定編譯目錄
			include: path.resolve(__dirname, 'src'),
			exclude: /node_modules/
		}]
	}
}

參考 https://eslint.org/demo 生成 .eslintrc.json
補充知識:在 vscode 中開啓 ES7 中類的裝飾器,項目根目錄中設置 jsconfig.json

{
	"compilerOptions": {
		"experimentalDecorators": true
	}
}
@log
class A{
	a=1;
}

8.暴露全局loader

  • $ yarn add expose-loader -D
  • 前置加載器、後置加載器、普通加載器…
// 內聯加載器
import jquery from 'expose-loader?$!jquery';
console.log(window.$);

{
   // 只要引入 JQUERY 就在全局注入$
   test: require.resolve('jquery'),
   use: ['expose-loader?$']
}
let webpack = require('webpack');
module.exports = {
    plugins: [
		// 在每個模塊中都注入$
		new webpack.ProvidePlugin({
			'$': 'jquery'
		})
	],
}

// 頁面中
console.log($);

9.webpack中圖片的處理和分目錄分發

  • 在JS中創建IMG
  • 在CSS中設置背景圖
  • 在HTML中寫死

安裝 $ yarn add file-loader url-loader html-withimg-loader -D

module.exports = {
	...,
	module: {
		// 模塊規則:使用加載器(默認從右向左執行)
		rules: [..., {
			test: /\.(png|jpg|gif)$/i,
			use: [{
				// 把指定大小內的圖片BASE64
				loader: 'url-loader',
				options: {
					limit: 200 * 1024,
    				outputPath:'/images'
				}
			}],
			include: path.resolve(__dirname, 'src'),
			exclude: /node_modules/
		}, {
			test: /\.html$/,
			use: ['html-withimg-loader']
		}]
	}
}

最後實現文件分目錄發佈

module.exports = {
	output: {
		// 配置引用前綴(所有資源前加這個地址)
		publicPath: './'
	},
	plugins: [
		new MiniCssExtractPlugin({
			filename: 'css/main.css'
		})
	],
	module: {
		// 模塊規則:使用加載器(默認從右向左執行)
		rules: [...,{
			test: /\.(png|jpg|gif)$/i,
			use: [{
				options: {
					outputPath: 'images'
				}
			}]
		}]
	}
}

實例

命令

  • npm run server :“server”: “webpack-dev-server --open”
  • npm run build : “build”: “webpack”

結構

image.png

webpack.config.js

// webpack 是基於node運行的;這個文件最終要運行在node環境下;
// 入口和出口文件
// 在真實項目開發中,我們常常使用Es6進行開發,當上線之前,把es6編譯成es5;
let path = require("path");
let WebpackHtmlPlugin = require("html-webpack-plugin");
module.exports = {
    // __dirname: 當前simple文件夾的絕對路徑
    mode: "development",// 開發模式(不壓縮)  production :生產環境(壓縮的)默認是壓縮的;
    devtool: 'eval-source-map',// 用於瀏覽器的調試
    entry: __dirname + "/app/main.js",//webpack打包的入口
    output: {
        path: __dirname + "/public",// 打包之後的文件存放路徑
        filename: "bundle.js"// 打包之後的js的文件名
    },
    // 多入口多出口的配置方式
    // entry:{
    //     main:__dirname+"/app/main.js",
    //     greeter:__dirname+"/app/greeter.js"
    // },
    // output:{
    //     path:__dirname+"/public",
    //     filename:'[name].js'
    // },
    //"/api/getuser"
    // 配置dev-server的選項
    devServer: {
        contentBase: "./public",// 啓動服務的根目錄
        inline: true,// 實時刷新
        port: 8080,// 讓當前項目在8080端口下啓動
        proxy: {// 代理;用於跨域的參數配置
            "/api": {// 如果接口中函數api,那麼這個接口就要跨域;
                // target : 將要跨域請求的服務器地址;
                target: "http://localhost:9000",
                secure: false,// 目標服務器是否是安全協議
                changeOrigin: false,// 是否修改請求的源頭
            }
        }
    },
    /* 
    test:一個用以匹配loaders所處理文件的拓展名的正則表達式(必須)
    loader:loader的名稱(必須)
    include/exclude:手動添加必須處理的文件(文件夾)或屏蔽不需要處理的文件(文件夾)(可選);
    query:爲loaders提供額外的設置選項(可選)
*/
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,// 用正則匹配文件的後綴名,匹配到文件要用loader處理;
                use: ["babel-loader"],// 要使用的loader
                exclude: "/node_modules/"// 這個文件夾下的js文件不需要使用loader處理;
              也可以不加引號用正則匹配/node_modules/
            }, {
                test: /\.css$/,
                // style-loader :會默認生成一個style標籤,將css樣式插入到這個style中;
                // 順序必須是先style-loader,再用css-loader
                // postcss-loader : 可以給瀏覽器對應的樣式自動加前綴
                use: ["style-loader", "css-loader", "postcss-loader"],
                // include: /css/

                //path(路徑) path.resolve() 方法將路徑或路徑片段的序列解析爲絕對路徑。
                //include:[path.resolve(__dirname,"css")]
            },
            // 1.base64有點: 較少了http請求,加快了頁面的加載時間;避免了圖片的跨域,
          不會造成圖片緩存的問題;
            // 2.缺點:數據量比較大,可讀性不好,Ie8以下不可用;
            // 如果圖片的大小超過了url的limit限制,默認就會調用file-loader去處理該圖片,
          此時會生成一個新的圖片而不是轉base64的編碼了;
            {
                test: /(\.png|\.jpg|.gif)$/,
                use: {
                    loader: "url-loader",
                    options: {
                        // 是限制的意思
                        limit: 8192
                    }

                }
            },
            {
                test: /\.less$/,
                // 1.這個順序不能顛倒 ,webpack會從後往前依次進行解析;
                  先用less-loader把less文件解析成css,將css注入到js文件中,
              style-loader創建style標籤,把js中的css放進去;
                use: ["style-loader", "css-loader", "less-loader"]
            }
        ]
    },
    // plugins:這是一個數組
    plugins: [
        new WebpackHtmlPlugin({
            template: __dirname + "/index.html"
        })
    ]

}
// vue-cli

package.json

{
  "name": "simple",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-cli --entry ./app/main.js --output ./public/bundle.js",
    "build": "webpack",
    "server": "webpack-dev-server --open"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "autoprefixer": "^9.6.1",
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "css-loader": "^3.2.0",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.10.3",
    "less-loader": "^5.0.0",
    "postcss-loader": "^3.0.0",
    "style-loader": "^1.0.0",
    "url-loader": "^3.0.0",
    "webpack": "^4.39.3",
    "webpack-cli": "^3.3.8",
    "webpack-dev-server": "^3.8.0"
  }
}

postcss.config.js

後綴自動添加
// postcss.config.js
module.exports = {
    plugins:[
        require("autoprefixer")({
            overrideBrowserslist:[
                "defaults",
                "Android 4.1",
                "iOS 7.1",
                "Chrome>31",
                "ff>31",
                "ie>=8",
                "last 2 versions",
                ">0%"
            ]
        })
    ]
}

.babelrc

    把babel的配置選項放在一個單獨的名爲 ".babelrc" 的配置文件中
		//.babelrc
    {
        "presets": [
            "react",
            "env"
        ]
    }

U7%WPJ0POY2DBQ%E~DFLH2B.jpg

app/main.js

//main.js 
// const greeter = require('./Greeter.js');
// document.querySelector("#root").appendChild(greeter());
// ES6的寫法
// import "../css/index.css";
import "../css/2.less";
import img from "../images/16.jpg";
let a = 555;
let b = 755;
console.log(b);
let ff = a => console.log(a);
ff(a)
console.log(img);
let str=`<img src="${img}"></img>`;
let c=document.querySelector("#root");
c.innerHTML = str;

app/Greeter.js

// Greeter.js
// 這是common.js規範:module.exports require 
// import export
// webpack會把JS編譯成瀏覽器識別的
module.exports = function() {
    var greet = document.createElement('div');
    greet.textContent = "Hi great and greetings!";
    return greet;
  };

鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章