js 痛點
- 文件依賴度高;
- 命名沖突/變量污染;
- 可讀性差蹋嵌;
模塊化
- 把復雜問題分解成相對獨立的模塊,這樣的設計可以降低程序復雜性葫隙,提高代碼的重用栽烂,也有利于團隊協(xié)作開發(fā)與后期的維護和擴展;
模塊化規(guī)范
node:cjs-commonjs
- 有一個全局對象:module恋脚;
- 導出一:通過 module.exports腺办;
- 導出二:通過 exports.a = function() {};
- 引入:require(path),
const a = require('./a.js')
; - 優(yōu)點:
- 擁有獨立作用域糟描;
- 依賴關系處理簡單怀喉;
瀏覽器:amd,異步模塊定義
- 定義模塊:define(id?, depend?, factory)
- 工廠函數會提供三個參數:
- require
- exports
cmd船响,類似于 amd
umd:處理各種模塊化之間的差異
es6:esm-ECMAScript module
使用:
<script type='module'></script>
導出一:export default 模塊磺送,默認導出;
引入一:import 模塊名 from 模塊路徑灿意,默認導入估灿;
要求必須在服務器環(huán)境下,才能運行
導出二:
export 模塊;
缤剧,多導出馅袁;引入二:
import {a as A} from './a.js';
,模塊名必須等同于導出模塊名荒辕,可以通過 as 重命名汗销;esm,IE8抵窒、IE9 不支持弛针,這是引入了 webpack,模塊打包器李皇。
webpack
- webpack:核心代碼削茁;
- webpack-cli:cli 命令行界面;
正常目錄解讀
- src:js 代碼,邏輯代碼茧跋;
- public:html 存放目錄慰丛;
使用
- webpack 只認識相對路徑;
沒有配置時
- 可以在控制臺瘾杭,執(zhí)行
node_modules/.bin/webpack ./src/index.js
诅病;
自定義配置
創(chuàng)建 webpack.config.js;
webpack 會運行到 node 環(huán)境下面粥烁,所有使用
module.exports = {};
贤笆;entry:入口文件;
-
output:輸出目錄讨阻,是一個對象苏潜;,里面路徑接受絕對路徑变勇;
// 一對一 const { resolve } = require("path"); module.exports = { // 入口文件 entry: "./src/index.js", // 輸出目錄 output: { // 接受絕對路徑 path: resolve(__dirname, "build"), filename: "index.js", }, };
// 多對一 const { resolve } = require("path"); module.exports = { // 入口文件 entry: [ './src/index.js', './src/b.js' ], // 輸出目錄 output: { // 接受絕對路徑 path: resolve(__dirname, "build"), filename: "index.js", }, };
// 多對多 entry: { index: "./src/index.js", b: "./src/b.js" }, // 輸出目錄 output: { // 接受絕對路徑 path: resolve(__dirname, "dist"), filename: "[name].js", clean: true, },
-
mode:production恤左、development、none
- 默認 production搀绣;
webpack 會將任意文件數據作為模塊進行處理;
當 webpack 遇到不能解析的模塊時飞袋,webpack 會找到 module 對象下面的 rules,去匹配對應規(guī)則链患;如果有對應規(guī)則匹配時巧鸭,我們就使用對應的 loader 去解析;
-
module
module: { rules: [ { test: /\.txt$/, use: "raw-loader", }, { test: /\.(png|gif|jpe?g)$/, use: { loader: "file-loader", options: { // 圖片打包后文件名 name: "[name]_[contenthash].[ext]", // 打包后存放目錄麻捻,相對于打包文件夾 dist outputPath: './images', // 使用時纲仍,獲取的是絕對路徑 // 打包后存放目錄,相對于 dist 文件 publicPath: "./images", // 使用時贸毕,引用的是相對路徑 ./dist/images/redux_xxx.jpg // url-loader郑叠,多一個 limit,number 類型明棍; // 當圖片小于此值時乡革,url-loader 不會對文件進行打包,而是轉為 base64 格式摊腋,引入到 js 文件中 // limit: 10240, }, }, }, { test: /\.css$/, // loader 執(zhí)行順序:從下至上沸版,從右至左 use: [ "style-loader", { loader: "css-loader", options: { // 啟用 url url: true, // 啟用 @import import: true, // 啟用 sourcemap sourceMap: false, }, }, ], }, ], }
// 自定義 loader module.exports = { // ... // 配置 loader 解析路徑,當自定義 loaders 時兴蒸,需要使用 // 如在根目錄下創(chuàng)建 loaders 來存放自定義 loader resolveLoader: { modules: ["node_modules", resolve(__dirname, "./loaders")], }, module: { rules: [ { test: /\.doc$/, use: "doc-loader", }, ], }, }
const MarkDown = require('markdown-it'); const md = new MarkDown(); module.exports = (source) => { console.log('source: ', md.render(source)); // return a Buffer or String视粮,返回的結果會直接 eval return `export default \`${md.render(source)}\``; }
- webpack5 之后,loader 變更
- asset/resource 發(fā)送一個單獨的文件并導出 URL橙凳。之前通過使用 file-loader 實現蕾殴。 - asset/inline 導出一個資源的 data URI笑撞。之前通過使用 url-loader 實現。 - asset/source 導出資源的源代碼区宇。之前通過使用 raw-loader 實現娃殖。
-
plugin:都是以類或者構造函數的形式出現
- clean-webpack-plugin
// CleanWebpackPlugin // webpack4.0 const { CleanWebpackPlugin } = require("clean-webpack-plugin"); // ... plugins: [ new CleanWebpackPlugin(), ] // webpack5.0 output: { // 接受絕對路徑 path: resolve(__dirname, "dist"), filename: "main.js", clean: true, },
- html-webpack-plugin:自動生成一個 html 文件值戳,與打包后的 js 文件相關聯(lián)
- mini-css-extract-plugin:
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // ... module: { rules: [ { test: /\.css$/, // loader 執(zhí)行順序:從下至上议谷,從右至左 use: [ // "style-loader", // 將 css 樣式添加到 head 當中的 style 里面;如果樣式很多時堕虹,導致html文件過大卧晓,同時也不能對 css 進行優(yōu)化,這時可以用 mini-c's's MiniCssExtractPlugin.loader, "css-loader", // 處理 css 文件 ], }, { test: /\.doc$/, use: "doc-loader", }, ], }, // ... plugins: [ new MiniCssExtractPlugin({ filename: '[name].css' }), ],
- clean-webpack-plugin
-
devServer
devServer: { port: 9000, open: true, // 自動開啟瀏覽器 proxy: { // 請求地址:服務器地址 // '/api' : 'http://localhost:3000' 'api': { target: 'http://localhost:3000', pathRewrite: {'^/api': ''}, secure: false, } }, hot: true, // 默認開啟 }
-
devtool:開發(fā)模式下赴捞,方便調試用
// source-map逼裆,要嚴格遵循下列順序:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map devtool: 'eval-cheap-source-map',
webpack-dev-server
- webpack4.0:在 package.json 中添加執(zhí)行命令
dev: webpack-dev-server
; - webpack5.0:在 package.json 中添加執(zhí)行命令
dev: webpack serve
- 啟動開發(fā)服務器赦政,打包后生成的文件胜宇,會存到內存中,不會寫入硬盤恢着,這樣可以提高開發(fā)效率
代碼分割
1.最基本的代碼分割
// 多對多打包方式桐愉,可能會導致同一模塊打包進多個 chunk
entry: {
index: {
import: "./src/index.js",
dependOn: ["axios", "a"]
},
b: {
import: "./src/b.js",
dependOn: ["axios", "a"]
},
axios: "axios",
a: "./src/a.js"
},
// 當你選擇使用代碼分割時,webpack可能會受[啟發(fā)式]的在多個chunk中復制依賴模塊掰派。如果發(fā)生了這種事情从诲,這時您有可能得到相同模塊的多個實例,他們之間的狀態(tài)將會很難保證同步靡羡。當然系洛,您可以通過設置 `optimization.runtimeChunk` 為 `“single”` 進行解決。這并不能阻止Webpack在入口點之間復制模塊代碼略步,但它可以防止Webpack在運行時創(chuàng)建同一模塊的兩個實例描扯。
optimization: {
// 配置 runtimeChunk 為 single,webpack 會幫助生成一個 runtime chunk趟薄,然后他會把加載的包移到 runtime 中
runtimeChunk: "single"
},
2.SplitChunksPlugin
- CommonsChunkPlugin 曾被用來避免他們之間的重復依賴荆烈,但是不可能再做進一步的優(yōu)化。
- 從 webpack v4 開始竟趾,移除了 CommonsChunkPlugin憔购,取而代之的是 optimization.splitChunks。
entry: {
index: "./src/index.js",
b: "./src/b.js",
},
optimization: {
splitChunks: {
// async 表示只從異步加載模塊中進行拆分
// initial 表示只從入口模塊進行拆分
chunks: 'all'
}
},
- 默認配置
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
動態(tài)導入
// 異步加載 按需加載
// 使用 import() 函數時岔帽,會返回一個 promise 對象
// 內聯(lián)注釋 magic comments玫鸟,需要按照 json 格式編寫
import(
/* webpackChunkName: "my-chunk-name" */
'axios'
).then(res => {
console.log('axios: ', res);
})
預加載 / 預獲取
- 其實是針對于動態(tài)導入資源加載優(yōu)化
- preload:預加載,當我們配置時犀勒,被配置的模塊會在父 chunk 加載時并行加載該模塊屎飘;
- prefetch:預獲取妥曲,當我們配置時,被配置的模塊會在瀏覽器閑置時钦购,也就是未來某個時刻下載模塊
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackPrefetch: true */
/* webpackPreload: true */
'axios'
).then(res => {
console.log('axios: ', res);
})
外部擴展
- 防止將某些 import 的包(package)打包到 bundle 中檐盟,而是在運行時(runtime)再去從外部獲取這些擴展依賴(external dependencies)。
- key:是我們需要進行外部擴展的包名押桃,后續(xù)打包時不會將該包打包葵萎;
- value:是該庫聲明的全局變量;
module.exports = {
//...
externals: {
jquery: 'jQuery',
},
};
tree shaking
- 將上下文中的dead-code移除唱凯,就像搖樹上的枯葉使其掉落一樣羡忘;
optimization: {
usedExports: true,
},
mode: 'production',
webpack.config.js
const { resolve } = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
// 最基本的代碼分割
// 多對多打包方式,可能會導致同一模塊打包進多個 chunk
entry: {
index: "./src/index.js",
b: "./src/b.js",
},
// 當你選擇使用代碼分割時磕昼,webpack可能會受[啟發(fā)式]的在多個chunk中復制依賴模塊卷雕。如果發(fā)生了這種事情,這時您有可能得到相同模塊的多個實例票从,他們之間的狀態(tài)將會很難保證同步漫雕。當然,您可以通過設置 `optimization.runtimeChunk` 為 `“single”` 進行解決峰鄙。這并不能阻止Webpack在入口點之間復制模塊代碼浸间,但它可以防止Webpack在運行時創(chuàng)建同一模塊的兩個實例。
// optimization: {
// // runtimeChunk: "single"
// splitChunks: {
// // async 表示只從異步加載模塊中進行拆分
// // initial 表示只從入口模塊進行拆分
// chunks: 'all'
// }
// },
// 輸出目錄
output: {
// 接受絕對路徑
path: resolve(__dirname, "dist"),
filename: "[name].js",
clean: true,
},
mode: "development",
// 配置 loader 解析路徑先馆,當自定義 loaders 時发框,需要使用
// 在根目錄下創(chuàng)建 loaders 來存放自定義 loader
resolveLoader: {
modules: ["node_modules", resolve(__dirname, "./loaders")],
},
// loader 使用
module: {
rules: [
{
test: /\.txt$/,
use: "raw-loader",
},
{
test: /\.(png|gif|jpe?g)$/,
use: {
loader: "file-loader",
options: {
// 圖片打包后文件名
name: "[name]_[contenthash].[ext]",
// 打包后存放目錄,相對于打包文件夾 dist
outputPath: "./images", // 使用時煤墙,獲取的是絕對路徑
// 打包后引用目錄
publicPath: "./images", // 使用時梅惯,引用的是相對路徑 ./images/redux_xxx.jpg
// url-loader,多一個 limit仿野,number 類型铣减;
// 當圖片小于此值時,url-loader 不會對文件進行打包脚作,而是轉為 base64 格式葫哗,引入到 js 文件中
// limit: 10240,
},
},
},
{
test: /\.css$/,
// loader 執(zhí)行順序:從下至上,從右至左
use: [
// "style-loader", // 將 css 樣式添加到 head 當中的 style 里面球涛;如果樣式很多時劣针,導致html文件過大,同時也不能對 css 進行優(yōu)化亿扁,這時可以用 mini-c's's
MiniCssExtractPlugin.loader,
"css-loader", // 處理 css 文件
],
},
{
test: /\.doc$/,
use: "doc-loader",
},
],
},
// 注冊插件的位置捺典,plugins 的使用
// webpack 會自動調用對應插件的 apply 方法,把插件注冊到 webpack 對應的生命周期當中
// 插件 是 webpack 的 支柱 功能从祝。Webpack 自身也是構建于你在 webpack 配置中用到的 相同的插件系統(tǒng) 之上
// 插件目的在于解決 loader 無法實現的其他事襟己。Webpack 提供很多開箱即用的 插件引谜。
plugins: [
// new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: "app.html", // 會自動創(chuàng)建一個純凈的 app.html 文件
template: "./public/index.html", // 會把已有 html 文件中的信息帶上
title: "app", // <title><%=htmlWebpackPlugin.options.title%></title>
}),
new MiniCssExtractPlugin({
filename: '[name].css'
}),
],
devServer: {
port: 9000,
open: true, // 自動開啟瀏覽器
proxy: {
// 請求地址:服務器地址
// '/api' : 'http://localhost:3000'
'api': {
target: 'http://localhost:3000',
pathRewrite: {'^/api': ''},
secure: false,
}
},
hot: true, // 默認開啟
},
// source-map,要嚴格遵循下列順序:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
devtool: 'eval-cheap-source-map',
};