從基礎到實戰 手摸手帶你掌握新版Webpack4.0詳解 教你看文檔

從基礎到實戰 手摸手帶你掌握新版Webpack4.0詳解

項目地址:https://github.com/wudiufo/We...

知識點概覽:css

Loader,HMR ,Create React App, Caching,
Plugin, SourceMap,Vue Cli 3.0 ,Shimming,
WebpackDevServer,TreeShaking, CodeSplitting,
Babel, React , Library, Eslint ,PWA,
Vue, Mode,性能優化,多頁應用,原理,
PreLoading, PreFetching ,環境變量,TypeScript

收穫:html

完全學會Webpack的配置
理解 Webpack的做用及原理
上手項目的打包過程配置
擁有工程化的前端思惟
步入高級前端工程師行列

一:初識Webpack

官網圖鎮樓:前端

圖片描述

1. 1 什麼是WebPack

webpack 是一個現代 JavaScript 應用程序的靜態模塊打包工具:它作的事情是,分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),並生成一個或多個 bundle,將其打包爲合適的格式以供瀏覽器使用。vue

webpack構建:node

構建就是把源代碼轉換成發佈到線上的可執行 JavaScrip、CSS、HTML 代碼,包括以下內容。

1.代碼轉換:TypeScript 編譯成 JavaScript、SCSS或Less 編譯成 CSS 等。react

2.文件優化:壓縮 JavaScript、CSS、HTML 代碼,壓縮合並圖片等。jquery

3.代碼分割:提取多個頁面的公共代碼、提取首屏不須要執行部分的代碼讓其異步加載。webpack

4.模塊合併:在採用模塊化的項目裏會有不少個模塊和文件,須要構建功能把模塊分類合併成一個文件。ios

5.自動刷新:監聽本地源代碼的變化,自動從新構建、刷新瀏覽器,nodemon。git

6.代碼校驗:在代碼被提交到倉庫前須要校驗代碼是否符合規範,以及單元測試是否經過。

7.自動發佈:更新完代碼後,自動構建出線上發佈代碼並傳輸給發佈系統。

構建實際上是工程化、自動化思想在前端開發中的體現,把一系列流程用代碼去實現,讓代碼自動化地執行這一系列複雜的流程。 構建給前端開發注入了更大的活力,解放了咱們的生產力,更加方便了咱們的開發。

1.2 什麼是 webpack 模塊

1.3 搭建Webpack環境

  • 去官網下載node

    // 查看node版本號
    node -v
    // 查看npm版本號
    npm -v

1.4 初始化項目

mkdir webpack-productname
cd webpack-productname 
//初始化webpack配置清單package.json
npm init -y

1.5 安裝webpack

//全局安裝(不推薦),由於若是有兩個項目用了webpack不一樣版本,就會出現版本不統一運行不起來的狀況。只有卸了當前版本安裝對應版本很是麻煩。
npm install webpack webpack-cli -g
//查看版本
webpack -v
//全局卸載
npm uninstall webpack webpack-cli -g
//在項目裏安裝webpack(推薦使用)。能夠在不一樣項目中使用不一樣的webpack版本。
cd webpack-productname
npm install webpack webpack-cli -D
//查看版本
npx webpack -v
//查看對用包的詳細信息
npm info webpack
//安裝指定版本包
npm install webpack@4.16.1 webpack-cli -D

注意:

因爲npm安裝走的是國外的網絡,比較慢容易出現安裝失敗的現象。

能夠用yarn安裝,首先得全局安裝yarn,npm install yarn -g

或使用nrm快速切換npm源,首先得全局安裝nrm, npm install -g nrm

nrm 使用:

nrm ls 查看可選源。

nrm test npm 測試速度。看哪一個快就use哪一個。

nrm use cnpm 使用cnpm 。

webpack-cli:使咱們們能夠在命令行里正確的使用webpack

1.6 webpack的配置文件

webpack 開箱即用,能夠無需使用任何配置文件。然而,webpack 會假定項目的入口起點爲 src/index,而後會在 dist/main.js 輸出結果,而且在生產環境開啓壓縮和優化。一般,你的項目還須要繼續擴展此能力,爲此你能夠在項目根目錄下建立一個 webpack.config.js 文件,webpack 會自動使用它。

在項目根目錄下建立 webpack.config.js 文件,這是webpack默認配置文件

const path = require('path')

module.exports = {
    //默認是production,打包的文件默認被壓縮。開發時能夠設置爲development,不被壓縮
    mode:'production', 
    //打包項目的入口文件
    entry: './index.js',
    //打包項目的輸出文件
    output: {
        //自定義打包輸出文件名
        filename:'bundle.js',
        //輸出文件的絕對路徑
        path: path.resolve(__dirname,'bundle')
    }
}

也能夠本身指定配置文件來完成webpack的打包:

npx webpack --config + 自定義配置文件

詳細請看官方文檔:概念 配置

1.7 webpack打包輸出內容

執行 `npm run build` 後,在控制檯輸出

Hash:1b245e275a547956bf52 //本次打包對應惟一一個hash值
Version:webpack 4.29.6 //本次打包對應webpack版本
Time:162ms Built at:2019-4-11 23:13:43 //本次打包耗時,及打包的時間
Asset Size Chunks Chunk Names //打包後的文件名,大小,id,入口文件名
bundle.js 1.36 KiB 0 [emitted] main 
Entrypoint main=bundle.js
[0]./src/index.js 159 bytes {0}[built]
[1]./src/header.js 187 bytes {e}[built]
[2]./src/sidebar.js 193 bytes {e}[built]
[3]./src/content.js 193 bytes {e} [built]

二:Webpack核心概念

LOADER

2.1 什麼是Loader

webpack可使用 loader 來預處理文件,就是經過使用不一樣的Loader,webpack能夠把不一樣的靜態文件都編譯成js文件,好比css,sass,less,ES6/7,vue,JSX等。

使用Loader打包靜態資源

支持加載圖片文件

須要安裝 file-loader:解決CSS等文件中的引入圖片路徑問題

npm install file-loader -D

webpack.config.js 裏添加 loader 配置

module.exports = {
    //配置模塊,主要用來配置不一樣文件的加載器
  module: {
      //配置模塊規則
    rules: [
      {
        test: /\.(png|jpg|gif)$/, //正則匹配要使用相應loader的文件
        use: [
          {
            loader: 'file-loader', //要用到的loader
              options: {
                  //palceholder佔位符
                  name:'[name].[ext]', //打包後的圖片名字,後綴和打包的以前的圖片同樣
                  outputPath: 'images/' //圖片打包後的地址
              },
          },
        ],
      },
    ],
  },
};

詳細請看官方文檔:file-loader

將小圖片轉換成base64格式

須要安裝 url-loader:當圖片小於limit的時候會把圖片BASE64編碼,大於limit參數的時候仍是使用file-loader 進行拷貝

npm install url-loader -D

webpack.config.js 裏添加 loader 配置

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif|bmp/)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              name:'[name].[ext]',
              outputPath: 'images/',
              limit: 8192 //小於8192k,就能夠轉化成base64格式。大於就會打包成文件格式
            }
          }
        ]
      }
    ]
  }
}

詳細請看官方文檔:url-loader


支持加載樣式CSS文件

須要安裝 css-loader style-loader:

npm install css-loader style-loader -D

webpack.config.js 裏添加 loader 配置

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/, //匹配以css爲後綴的文件
        use: ['style-loader', 'css-loader'],//loader的執行順序是從右向左,從下到上。css-loader:分析幾個css文件之間的關係,最終合併爲一個css。style-loader:在獲得css生成的內容時,把其掛載到html的head裏,成爲內聯樣式。
      },
    ],
  },
};

支持加載樣式SASS文件

須要安裝 sass-loader node-sass:

npm install sass-loader node-sass -D

webpack.config.js 裏添加 loader 配置

module.exports = {
    ...
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                "style-loader", // 將 JS 字符串生成爲 style 節點
                "css-loader", // 將 CSS 轉化成 CommonJS 模塊
                "sass-loader" // 將 Sass 編譯成 CSS,默認使用 Node Sass
            ]
        }]
    }
};

爲 css 樣式屬性加不一樣瀏覽器的前綴

爲了瀏覽器的兼容性,有時候咱們必須加入-webkit,-ms,-o,-moz這些前綴

  • Trident內核:主要表明爲IE瀏覽器, 前綴爲-ms
  • Gecko內核:主要表明爲Firefox, 前綴爲-moz
  • Presto內核:主要表明爲Opera, 前綴爲-o
  • Webkit內核:產要表明爲Chrome和Safari, 前綴爲-webkit
npm i postcss-loader autoprefixer -D

在項目跟目錄下建立 postcss.config.js

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

webpack.config.js

module.exports = {
    ...
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                "style-loader", // 將 JS 字符串生成爲 style 節點
                "css-loader", // 將 CSS 轉化成 CommonJS 模塊
                'postcss-loader',
                "sass-loader", // 將 Sass 編譯成 CSS,默認使用 Node Sass
                
            ]
        }]
    }
};

給loader加一些配置項:

webpack.config.js

module.exports = {
    ...
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                "style-loader", 
                {
                     loader: "css-loader",
                    options:{
                        importLoaders:2 ,//若是sass文件裏還引入了另一個sass文件,另外一個文件還會從postcss-loader向上解析。若是不加,就直接從css-loader開始解析。
                        modules: true //開啓css的模塊打包。css樣式不會和其餘模塊發生耦合和衝突
                    }
                }, 
                'postcss-loader',
                "sass-loader", 
                
            ]
        }]
    }
};

爲字體圖標文件配loader

阿里巴巴矢量圖標庫中,把須要的字體圖標下載到本地,解壓。將iconfont.eot iconfont.svg iconfont.ttf iconfont.woff 文件放入到項目中,在src中新建一個放字體圖標的文件夾font。將iconfont.css文件拷貝到項目中,本身改一下引入字體圖標的路徑。

須要安裝 file-loader:

npm i file-loader -D

webpack.config.js

module.exports = {
    ...
    module: {
        rules: [{
            test: /\.(eot|ttf|svg|woff)$/,
            use:{
                loader:'file-loader'
            }
        },
            ]
        }]
    }
};

詳細請看官方文檔:asset-management


plugin : 能夠在webpack運行到某個時刻的時候,幫你作一些事情

使用plugins讓打包更便捷

HtmlWebpackPlugin :htmlWebpackPlugin 會在打包結束後,自動生成一個html文件,並把打包生成的js自動引入到這個html文件中

安裝:npm i html-webpack-plugin -D

基本用法:在 webpack.config.js 中:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
    plugins: [new HtmlWebpackPlugin({
        template: 'src/index.html' //以index.html爲模板,把打包生成的js自動引入到這個html文件中
    })]
};

CleanWebpackPlugin :自動清除上一次打包的dist文件

安裝:npm i clean-webpack-plugin -D

基本用法:在 webpack.config.js 中:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const path = require('path');

module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
    plugins: [
        new HtmlWebpackPlugin({
        template: 'src/index.html' //在打包以後,以.html爲模板,把打包生成的js自動引入到這個html文件中
    }),
        new CleanWebpackPlugin(['dist']), // 在打包以前,能夠刪除dist文件夾下的全部內容
    
    ]
};

Entry與Output的基礎配置

在打包多入口文件時的配置

基本用法:在 webpack.config.js 中:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const path = require('path');

module.exports = {
  entry: {
       main: './src/index.js',
    sub: './src/index.js'
  },
  output: {
    publicPath: 'http://cdn.com.cn', //將注入到html中的js文件前面加上地址
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
    plugins: [
        new HtmlWebpackPlugin({
        template: 'src/index.html' //在打包以後,以.html爲模板,把打包生成的js自動引入到這個html文件中
    }),
        new CleanWebpackPlugin(['dist']), // 在打包以前,能夠刪除dist文件夾下的全部內容
    
    ]
};

詳細請看官網:Output output-management


SourceMap 的配置

sourcemap:打包編譯後的文件和源文件的映射關係,用於開發者調試用。

  • source-map 把映射文件生成到單獨的文件,最完整但最慢
  • cheap-module-source-map 在一個單獨的文件中產生一個不帶列映射的Map
  • eval-source-map 使用eval打包源文件模塊,在同一個文件中生成完整sourcemap
  • cheap-module-eval-source-map sourcemap和打包後的JS同行顯示,沒有映射列

    development環境推薦使用: devtool: 'cheap-module-eval-source-map',
    production環境推薦使用: devtool: 'cheap-module-source-map',

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    //devtool:'none',//在開發者模式下,默認開啓sourcemap,將其關閉
    //devtool:'source-map'//開啓映射打包會變慢
    //devtool:'inline-source-map'//不單獨生成.map文件,會將生成的映射文件以base64的形式插入到打包後的js文件的底部
    //devtool:'cheap-inline-source-map'//代碼出錯提示不用精確顯示第幾行的第幾個字符出錯,只顯示第幾行出錯,會提升一些性能
    //devtool:'cheap-module-inline-source-map'//不只管本身的業務代碼出錯,也管第三方模塊和loader的一些報錯
    //devtool:'eval'//執行效率最快,性能最好,可是針對比較複雜的代碼的狀況下,提示內容不全面
    //devtool: 'cheap-module-eval-source-map',//在開發環境推薦使用,提示比較全,打包速度比較快
    //devtool: 'cheap-module-source-map',//在生產環境中推薦使用,提示效果會好一些
    
    
    entry: {
        main: './src/index.js'
    },
    module: {
        rules: [{
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }, {
            test: /\.scss$/,
            use: [
                'style-loader', 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }]
    },
    plugins: [new HtmlWebpackPlugin({
        template: 'src/index.html'
    }), new CleanWebpackPlugin(['dist'])],
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    }
}

詳細請看官網:devtool


使用WebpackDevServer 提高開發效率

解決每次在src裏編寫完代碼都須要手動從新運行 npm run dev

1.在 package.json 中配置

{
  "name": "haiyang",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "bundle": "webpack",
    "watch": "webpack --watch",// 加--watch自動監聽代碼的變化
    
  },
  
}

2.在 webpack.config.js 中,加 devServer

安裝 npm i webpack-dev-server –D

  • contentBase :配置開發服務運行時的文件根目錄
  • open :自動打開瀏覽器
  • host:開發服務器監聽的主機地址
  • compress :開發服務器是否啓動gzip等壓縮
  • port:開發服務器監聽的端口
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    entry: {
        main: './src/index.js'
    },
+    devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        proxy: {//配置跨域,訪問的域名會被代理到本地的3000端口
              '/api': 'http://localhost:3000'
        }
    },
    module: {
        rules: []
    },
    plugins: [],
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    }
}

在 package.json 中:

{
  "name": "haiyang",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "bundle": "webpack",
    "watch": "webpack --watch",// 加--watch自動監聽代碼的變化
    "start": "webpack-dev-server",//配置熱更新
    
  },
 
}

詳細請看官網 :dev-server

擴充知識:本身寫一個相似webpackdevserver的工具

瞭解便可,功能不全,自行擴展。

在 package.json 中:

{
  "name": "haiyang",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "bundle": "webpack",
    "watch": "webpack --watch",// 加--watch自動監聽代碼的變化
    "start": "webpack-dev-server",//配置熱更新
+    "server" : "node server.js" //本身寫一個相似webpackdevserver的工具
  },
 
}

安裝 :npm i express webpack-dev-middleware -D

在 項目根目錄下建立 server.js 文件

在 server.js 中

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config.js');
const complier = webpack(config);

const app = express();

app.use(webpackDevMiddleware(complier, {}));

app.listen(3000, () => {
    console.log('server is running');
});

模塊熱替換(hot module replacement)

在 package.json 中:

{
  "name": "haiyang",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack-dev-server" //將文件打包到內存中,有助於開發
  },
}

在 webpack.config.js 中

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    entry: {
        main: './src/index.js'
    },
    devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
+        hot: true,//開啓熱更新
+        hotOnly: true//儘管html功能沒有實現,也不讓瀏覽器刷新
    },
    module: {
        rules: [{
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }, {
            test: /\.scss$/,
            use: [
                'style-loader', 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist']),
+        new webpack.HotModuleReplacementPlugin() //使用模塊熱更新插件
    ],
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    }
}

index.js

//若是模塊啓用了HMR,就能夠用 module.hot.accept(),監聽模塊的更新。
if (module.hot) {
  module.hot.accept('./library.js', function() {
    // 使用更新過的 library 模塊執行某些操做...
  })
}

注意點:

引入css,用框架Vue,React 時,不須要寫 module.hot.accept(),由於在使用css-loader,vue-loader,babel-preset時,就已經配置好了HMR,不須要本身寫

詳細請看官方文檔:hot-module-replacement api/hot-module-replacement concepts/hot-module-replacement


使用 Babel 處理 ES6/7 語法 轉義爲ES5

BABEL官網:https://babeljs.io/setup

安裝依賴包:

npm i babel-loader @babel/core @babel/preset-env -D
//生產依賴,兼容低版本瀏覽器
npm install --save @babel/polyfill

在 webpack.config.js 中

module: {
  rules: [
    {
        test: /\.js$/,
         exclude: /node_modules/,//不須要對第三方模塊進行轉換,耗費性能
         loader: "babel-loader" ,
        options:{
            "presets": [["@babel/preset-env",{
                targets: {//這個項目運行在大於什麼版本的瀏覽器上,已經支持es6的語法的高版本瀏覽器就不須要轉義成es5了
                    edge: "17",
                    firefox: "60",
                    chrome: "67",
                    safari: "11.1",
                  },
                useBuiltIns:'usage' //按需添加polyfill,把用到的代碼都轉成低版本瀏覽器兼容的
            }]]
        }
    }
  ]
}

在 index.js 中:

//在業務代碼運行以前最頂部導入
import "@babel/polyfill";

注意:在開發類庫,第三方模塊或組件庫時不能用 @babel/polyfill 這種方案,由於會把聲明的變量變成全局變量,會污染全局環境。

安裝:

npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
npm install --save @babel/runtime-corejs2

在 webpack.config.js 中

module: {
  rules: [
    {
        test: /\.js$/,
         exclude: /node_modules/,//不須要對第三方模塊進行轉換,耗費性能
         loader: "babel-loader" ,
        options:{
            "plugins": [["@babel/plugin-transform-runtime",{
                "corejs": 2,
                "helpers": true,
                "regenerator": true,
                "useESModules": false
            }]]
        }
    }
  ]
}

因爲babel須要配置的內容很是多,咱們須要在項目根目錄下建立一個 .babelrc 文件。

就不須要在 webpack.config.js 中寫 babel 的配置了。

.babelrc 中:

{
            "plugins": [["@babel/plugin-transform-runtime",{
                "corejs": 2,
                "helpers": true,
                "regenerator": true,
                "useESModules": false
            }]]
        }

配置 React 代碼的打包

業務代碼:

.babelrc 中:

{ 
            "presets": [
                ["@babel/preset-env",{
                targets: {
                    edge: "17",
                    firefox: "60",
                    chrome: "67",
                    safari: "11.1",
                  },
                useBuiltIns:'usage' 
                    }
                ],
                "@babel/preset-react"
            ]
        }
//執行順序:從下往上,從右向左的順序

安裝:

npm i react react-dom --save
npm install --save-dev @babel/preset-react

詳細內容請看官網:babel-loader


三:Webpack進階

Tree Shaking:只支持 ES Module 例如 importexport 的靜態結構特性的引入。當引入一個模塊時,不引入全部的代碼,只引入須要的代碼

在 webpack.config.js 中:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    entry: {
        main: './src/index.js'
    },
    devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true,
        hotOnly: true
    },
    module: {
        rules: []
    },
    plugins: [],
+    optimization: { //在開發環境中加,生產環境不加
        usedExports: true
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    }
}

在 package.json 中:

{
+ "sideEffects": ["*.css"], //對 全部的css文件 不使用Tree shaking。若是填 false,就是都須要用到Tree shaking
}

詳細內容請看官網:tree-shaking


Develoment 和Production模式的區分打包

在項目根目錄下建立兩個文件,webpack.dev.js,webpack.prod.js

webpack.dev.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    entry: {
        main: './src/index.js'
    },
    devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true,
        hotOnly: true
    },
    module: {
        rules: [{ 
            test: /\.js$/, 
            exclude: /node_modules/, 
            loader: 'babel-loader',
        }, {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }, {
            test: /\.scss$/,
            use: [
                'style-loader', 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist']),
        new webpack.HotModuleReplacementPlugin()
    ],
    optimization: {
        usedExports: true
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    }
}

webpack.prod.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
    mode: 'production',
    devtool: 'cheap-module-source-map',
    entry: {
        main: './src/index.js'
    },
    
    module: {
        rules: [{ 
            test: /\.js$/, 
            exclude: /node_modules/, 
            loader: 'babel-loader',
        }, {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }, {
            test: /\.scss$/,
            use: [
                'style-loader', 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                
                
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist']),
        
    ],
    
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    }
}

在 package.json 中:

{
  "scripts": {
    "dev": "webpack-dev-server --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },
}

解決 webpack.dev.js,webpack.prod.js 存在大量重複代碼,在項目根目錄下建立一個 webpack.common.js 文件,把公共代碼提取出來

安裝 :

npm i webpack-merge -D

webpack.common.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {

    entry: {
        main: './src/index.js'
    },
    
    module: {
        rules: [{ 
            test: /\.js$/, 
            exclude: /node_modules/, 
            loader: 'babel-loader',
        }, {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }, {
            test: /\.scss$/,
            use: [
                'style-loader', 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist'],{
            root:path.resolve(__dirname,'../')
        }),
        
    ],
    
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, '../dist')
    }
}

webpack.dev.js

const webpack = require('webpack');
const merge = require('webpack-merge')
const commenConfig = require('./webpack.commin.js')

const devConfig = {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    
    devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true,
        hotOnly: true
    },
    
    plugins: [    
        new webpack.HotModuleReplacementPlugin()
    ],
    optimization: {
        usedExports: true
    },    
}
//將開發配置和公共配置作結合
module.exports = merge(commenConfig, devConfig)

webpack.prod.js

const merge = require('webpack-merge')
const commenConfig = require('./webpack.commin.js')

const prodConfig = {
    mode: 'production',
    devtool: 'cheap-module-source-map',
}
//將線上配置和公共配置作結合
module.exports = merge(commenConfig, prodConfig)

最後在根目錄下建立一個build文件夾,將 webpack.common.js , webpack.dev.js ,webpack.prod.js 放在build文件夾下,統一管理。

在 package.json 中:

{
  "scripts": {
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
  },
}

詳細請看官網文檔:guides/production


Webpack和Code Splitting

安裝: npm i lodash --save npm i babel-plugin-dynamic-import-webpack -D

代碼分割,和webpack無關,爲了提高性能
webpack中實現代碼分割,兩種方式:

第一種方法:同步代碼: 只須要在webpack.common.js中作optimization的配置便可

第二種方法:異步代碼(import): 異步代碼,無需作任何配置,會自動進行代碼分割,放置到新的文件中

第一種方法:在 webpack.common.js 中

module.exports = {
    entry: {
        main: './src/index.js'
    },
    
    module: {
        rules: []
    },
    plugins: [],
+    optimization:{
+       splitChunks:{ //啓動代碼分割,有默認配置項
+            chunks:'all'
+        }  
+    },
    
    output: {}
}

第二種方法在 .babelrc 中:

{
    presets: [
        [
            "@babel/preset-env", {
                targets: {
                    chrome: "67",
                },
                useBuiltIns: 'usage'
            }
        ],
        "@babel/preset-react"
    ],
+    plugins: ["dynamic-import-webpack"]
}

詳細內容請看官網:code-splitting


SplitChunksPlugin 配置參數詳解

安裝:npm install --save-dev @babeL/plugin-syntax-dynamic-import

在業務 index.js 中:

function getComponent() {
     return import(/* webpackChunkName:"lodash" */ 'lodash').then(({ default: _ }) => {
         var element = document.createElement('div');
        element.innerHTML = _.join(['1', '2'], '-');
         return element;
    })
}

 getComponent().then(element => {
     document.body.appendChild(element);
 });

.babelrc 中:

{
    presets: [
        [
            "@babel/preset-env", {
                targets: {
                    chrome: "67",
                },
                useBuiltIns: 'usage'
            }
        ],
        "@babel/preset-react"
    ],
+    plugins: ["@babeL/plugin-syntax-dynamic-import"]
}

在 webpack.common.js 中:

module.exports = {
    entry: {
        main: './src/index.js'
    },
    
    module: {
        rules: []
    },
    plugins: [],
+    optimization:{
+       splitChunks:{ //啓動代碼分割,不寫有默認配置項
+            chunks: 'all',//參數all/initial/async,只對全部/同步/異步進行代碼分割
              minSize: 30000, //大於30kb纔會對代碼分割
              maxSize: 0,
              minChunks: 1,//打包生成的文件,當一個模塊至少用多少次時纔會進行代碼分割
              maxAsyncRequests: 5,//同時加載的模塊數最可能是5個
              maxInitialRequests: 3,//入口文件最多3個模塊會作代碼分割,不然不會
              automaticNameDelimiter: '~',//文件自動生成的鏈接符
              name: true,
            cacheGroups:{//對同步代碼走緩存組
             vendors: {
                  test: /[\\/]node_modules[\\/]/,
                  priority: -10,//誰優先級大就把打包後的文件放到哪一個組
                filename:'vendors.js'
                },
            default: {
              minChunks: 2,
              priority: -20,
              reuseExistingChunk: true,//模塊已經被打包過了,就不用再打包了,複用以前的就能夠
              filename:'common.js' //打包以後的文件名   
            }
        }
+        }  
+    },
    
    output: {}
}

詳細請看官方文檔:split-chunks-plugin


Lazy Loading 懶加載,Chunk是什麼?

用戶當前須要用什麼功能就只加載這個功能對應的代碼,也就是所謂的按需加載 在給單頁應用作按需加載優化時,通常採用如下原則:

  • 對網站功能進行劃分,每一類一個chunk
  • 對於首次打開頁面須要的功能直接加載,儘快展現給用戶
  • 某些依賴大量代碼的功能點能夠按需加載
  • 被分割出去的代碼須要一個按需加載的時機

每個文件就是一個 chunk

詳細請看官方文檔:lazy-loading


打包分析,Preloading,Prefetching

打開網址:webpack分析工具:https://github.com/webpack/analyse

在 package.json 中

{  
  "scripts": {
    "dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js", //把打包過程的描述放在stats.json文件中
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
  },
}

在控制檯運行 npm run dev-build ,在根目錄下生成 stats.json 文件。打開網址 http://webpack.github.io/analyse/ ,把stats.json文件傳上去,會出現分析結果。

詳細請看官方文檔:bundle-analysis 打包分析工具

介紹 webpack-bundle-analyzer 的使用:

經過使用webpack-bundle-analyzer能夠看到項目各模塊的大小,能夠按需優化。

官網圖鎮樓:

安裝:

# NPM
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer

配置:在 webpack.config.js 中:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin(
        {
              analyzerMode: 'server',
              analyzerHost: '127.0.0.1',
              analyzerPort: 8889,
              reportFilename: 'report.html',
              defaultSizes: 'parsed',
              openAnalyzer: true,
              generateStatsFile: false,
              statsFilename: 'stats.json',
              statsOptions: null,
              logLevel: 'info'
            }
    )
  ]
}

輸出:在 package.json 中:

"analyz": "NODE_ENV=production npm_config_report=true npm run build"

在線分析:在 控制檯輸入:

webpack --profile --json > stats.json
  • profile:記錄下構建過程當中的耗時信息;
  • json:以 JSON 的格式輸出構建結果,最後只輸出一個 .json 文件,這個文件中包括全部構建相關的信息。
  • Webpack 官方提供了一個可視化分析工具 Webpack Analyse
  • Modules:展現全部的模塊,每一個模塊對應一個文件。而且還包含全部模塊之間的依賴關係圖、模塊路徑、模塊ID、模塊所屬 Chunk、模塊大小;
  • Chunks:展現全部的代碼塊,一個代碼塊中包含多個模塊。而且還包含代碼塊的ID、名稱、大小、每一個代碼塊包含的模塊數量,以及代碼塊之間的依賴關係圖;
  • Assets:展現全部輸出的文件資源,包括 .js、.css、圖片等。而且還包括文件名稱、大小、該文件來自哪一個代碼塊;
  • Warnings:展現構建過程當中出現的全部警告信息;
  • Errors:展現構建過程當中出現的全部錯誤信息;
  • Hints:展現處理每一個模塊的過程當中的耗時。

打開谷歌控制檯查看代碼使用率,按 ctrl+shift+p ,輸入 coverage 查看。

預取/預加載模塊(prefetch/preload module)

假若有一個HomePage組件,其內部有一個LoginButton.js登錄組件,再點擊後按需加載 LoginModel 組件。

LoginButton.js:

import(/* webpackPrefetch: true */ 'LoginModal');

這會生成 <link rel="prefetch" href="login-modal-chunk.js"> 並追加到頁面頭部,指示着瀏覽器在閒置時間預取 login-modal-chunk.js 文件。就是說,只要首頁加載完成,就會在空閒時間把登陸模塊也加載了。

總結:

/ webpackPrefetch: true /:把主加載流程加載完畢,在空閒時在加載其餘,等再點擊其餘時,只須要從緩存中讀取便可,性能更好。推薦使用,提升代碼利用率。把一些交互後才能用到的代碼寫到異步組件裏,經過懶加載的形式,去把這塊的代碼邏輯加載進來,性能提高,頁面訪問速度更快。

/ webpackPreload: true /: 和主加載流程一塊兒並行加載。

詳細請看官方文檔:prefetchingpreloading-modules


CSS文件的代碼分割

在 webpack.config.js 中

module.exports = {
    entry: {
        main: './src/index.js'
    },
    module: {
       
    },
    plugins: [],
    optimization: {
        splitChunks: {
           chunks: 'all'
    }
    },
    output: {
        filename: '[name].js',//入口文件打包後生成的文件名
+         chunkFilename: '[name].chunk.js',//main.js異步加載的間接的js文件。用來打包import('module')方法中引入的模塊
        path: path.resolve(__dirname, '../dist')
    }
}

由於CSS的下載和JS能夠並行,當一個HTML文件很大的時候,咱們能夠把CSS單獨提取出來加載

  • mini-css-extract-plugin:通常在線上環境使用這個插件,由於在開發環境中不支持HMR。
  • filename 打包入口文件
  • chunkFilename 用來打包import('module')方法中引入的模塊

安裝 :

//抽離css文件
npm install --save-dev mini-css-extract-plugin
//壓縮css文件
npm i optimize-css-assets-webpack-plugin -D

在 webpack.prod.js 中:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');


const prodConfig = {
    mode: 'production',
    devtool: 'cheap-module-source-map',
    module: {
        rules:[{
            test: /\.scss$/,
            use: [
+                MiniCssExtractPlugin.loader, 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
+                MiniCssExtractPlugin.loader,
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
+    optimization: {
        minimizer: [new OptimizeCSSAssetsPlugin({})]
    },
    plugins: [
+        new MiniCssExtractPlugin({
            filename: '[name].css',//直接引用的css文件
            chunkFilename: '[name].chunk.css'//間接引用的css文件
        })
    ]
}

module.exports = merge(commonConfig, prodConfig);

在 webpack.dev.js

const webpack = require('webpack');
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const devConfig = {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true
    },
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                'style-loader', 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
}

module.exports = merge(commonConfig, devConfig);

在 webpack.common.js 中:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
    entry: {
        main: './src/index.js',
    },
    module: {
        rules: [{ 
            test: /\.js$/, 
            exclude: /node_modules/, 
            loader: 'babel-loader',
        }, {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist'], {
            root: path.resolve(__dirname, '../')
        })
    ],
    optimization: {
        usedExports: true,//TreeShaking
        splitChunks: {
      chunks: 'all'
    }
    },
    output: {
        filename: '[name].js',
        chunkFilename: '[name].chunk.js',
        path: path.resolve(__dirname, '../dist')
    }
}

在 package.json 中:

{
    "sideEffects": ["*.css"] //除了css文件,其他的都TreeShaking
}

詳細請看官方文檔:mini-css-extract-plugin


Webpack 與瀏覽器緩存(Caching)

在 webpack.common.js 中:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
    entry: {
        main: './src/index.js',
    },
    module: {
        rules: [{ 
            test: /\.js$/, 
            exclude: /node_modules/, 
            loader: 'babel-loader',
        }, {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist'], {
            root: path.resolve(__dirname, '../')
        })
    ],
    optimization: {
+        runtimeChunk: {//兼容老版本webpack4,把manifest打包到runtime裏,不影響業務代碼和第三方模塊
            name: 'runtime'
        },
        usedExports: true,
        splitChunks: {
      chunks: 'all',
      cacheGroups: {
          vendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10,
              name: 'vendors',
          }
      }
    }
    },
    performance: false,//禁止提示性能上的一些問題
+    output: {
        path: path.resolve(__dirname, '../dist')
    }
}

在 webpack.dev.js 中:

const webpack = require('webpack');
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const devConfig = {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true
    },
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                'style-loader', 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
+    output: {
        filename: '[name].js',
        chunkFilename: '[name].js',
    }
}

module.exports = merge(commonConfig, devConfig);

在 webpack.prod.js 中:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const prodConfig = {
    mode: 'production',
    devtool: 'cheap-module-source-map',
    module: {
        rules:[{
            test: /\.scss$/,
            use: [
                MiniCssExtractPlugin.loader, 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
                MiniCssExtractPlugin.loader,
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
    optimization: {
        minimizer: [new OptimizeCSSAssetsPlugin({})]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[name].chunk.css'
        })
    ],
+    output: {
        filename: '[name].[contenthash].js', //源代碼不變,hash值就不會變,解決瀏覽器緩存問題。打包上線時,用戶只須要更新有變化的代碼,沒有變化的從瀏覽器緩存讀取
        chunkFilename: '[name].[contenthash].js'
    }
}

module.exports = merge(commonConfig, prodConfig);

詳細請看官網文檔:manifest


Shimming (墊片)

在 webpack.common.js 中:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
    entry: {
        main: './src/index.js',
    },
    module: {
        rules: [{ 
            test: /\.js$/, 
            exclude: /node_modules/,
            use: [{
                loader: 'babel-loader'
            }, {
                loader: 'imports-loader?this=>window'
            }]
        }, {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist'], {
            root: path.resolve(__dirname, '../')
        }),
+        new webpack.ProvidePlugin({
            $: 'jquery',//發現模塊中有$字符串,就自動引入iquery,就能夠用jquery
            _join: ['lodash', 'join']//_join表明lodash裏的join方法
        }),
    ],
    optimization: {
        runtimeChunk: {
            name: 'runtime'
        },
        usedExports: true,
        splitChunks: {
      chunks: 'all',
      cacheGroups: {
          vendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10,
              name: 'vendors',
          }
      }
    }
    },
    performance: false,
    output: {
        path: path.resolve(__dirname, '../dist')
    }
}

若是想讓每一個js模塊的this都指向window:

安裝: npm install imports-loader -D

在 webpack.common.js 中:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
    entry: {
        main: './src/index.js',
    },
    module: {
        rules: [{ 
            test: /\.js$/, 
            exclude: /node_modules/,
            use: [{
                loader: 'babel-loader'
            }, {//每一個js模塊的this都指向window
+                loader: 'imports-loader?this=>window'
            }]
        }, {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist'], {
            root: path.resolve(__dirname, '../')
        }),
+        new webpack.ProvidePlugin({
            $: 'jquery',//發現模塊中有$字符串,就自動引入iquery,就能夠用jquery
            _join: ['lodash', 'join']//_join表明lodash裏的join方法
        }),
    ],
    optimization: {
        runtimeChunk: {
            name: 'runtime'
        },
        usedExports: true,
        splitChunks: {
      chunks: 'all',
      cacheGroups: {
          vendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10,
              name: 'vendors',
          }
      }
    }
    },
    performance: false,
    output: {
        path: path.resolve(__dirname, '../dist')
    }
}

詳細請看官方文檔:imports-loader shimming


環境變量的使用

只須要一個common.js文件經過在package.json中傳遞不一樣的參數,區分是開發環境仍是生產環境。

在 package.json 中:

{
  "name": "haiyang",
  "sideEffects": [
    "*.css"
  ],
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev-build": "webpack --config ./build/webpack.common.js",
    "dev": "webpack-dev-server --config ./build/webpack.common.js",
    "build": "webpack --env.production --config ./build/webpack.common.js" //經過--env.production,把環境變量傳進去
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.2.0",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/plugin-transform-runtime": "^7.2.0",
    "@babel/preset-env": "^7.2.0",
    "@babel/preset-react": "^7.0.0",
    "autoprefixer": "^9.3.1",
    "babel-loader": "^8.0.4",
    "clean-webpack-plugin": "^1.0.0",
    "css-loader": "^1.0.1",
    "express": "^4.16.4",
    "file-loader": "^2.0.0",
    "html-webpack-plugin": "^3.2.0",
    "imports-loader": "^0.8.0",
    "mini-css-extract-plugin": "^0.5.0",
    "node-sass": "^4.10.0",
    "optimize-css-assets-webpack-plugin": "^5.0.1",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "url-loader": "^1.1.2",
    "webpack-cli": "^3.1.2",
    "webpack-dev-middleware": "^3.4.0",
    "webpack-dev-server": "^3.1.10",
    "webpack-merge": "^4.1.5"
  },
  "dependencies": {
    "@babel/polyfill": "^7.0.0",
    "@babel/runtime": "^7.2.0",
    "@babel/runtime-corejs2": "^7.2.0",
    "jquery": "^3.3.1",
    "lodash": "^4.17.11",
    "react": "^16.6.3",
    "react-dom": "^16.6.3",
    "webpack": "^4.25.1"
  }
}

在 webpack.common.js 中:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
const merge = require('webpack-merge');
const devConfig = require('./webpack.dev.js');
const prodConfig = require('./webpack.prod.js');
const commonConfig = {
    entry: {
        main: './src/index.js',
    },
    module: {
        rules: [{ 
            test: /\.js$/, 
            exclude: /node_modules/,
            use: [{
                loader: 'babel-loader'
            }, {
                loader: 'imports-loader?this=>window'
            }]
        }, {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin(['dist'], {
            root: path.resolve(__dirname, '../')
        }),
        new webpack.ProvidePlugin({
            $: 'jquery',
            _join: ['lodash', 'join']
        }),
    ],
    optimization: {
        runtimeChunk: {
            name: 'runtime'
        },
        usedExports: true,
        splitChunks: {
      chunks: 'all',
      cacheGroups: {
          vendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10,
              name: 'vendors',
          }
      }
    }
    },
    performance: false,
    output: {
        path: path.resolve(__dirname, '../dist')
    }
}

module.exports = (env) => {
    if(env && env.production) {//線上環境
        return merge(commonConfig, prodConfig);
    }else {//開發環境
        return merge(commonConfig, devConfig);
    }
}

在 webpack.dev.js 中:

const webpack = require('webpack');

const devConfig = {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true
    },
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                'style-loader', 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
    output: {
        filename: '[name].js',
        chunkFilename: '[name].js',
    }
}

module.exports = devConfig;

在webpack.prod.js 中:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

const prodConfig = {
    mode: 'production',
    devtool: 'cheap-module-source-map',
    module: {
        rules:[{
            test: /\.scss$/,
            use: [
                MiniCssExtractPlugin.loader, 
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 2
                    }
                },
                'postcss-loader',
                'sass-loader',
                
            ]
        }, {
            test: /\.css$/,
            use: [
                MiniCssExtractPlugin.loader,
                'css-loader',
                'postcss-loader'
            ]
        }]
    },
    optimization: {
        minimizer: [new OptimizeCSSAssetsPlugin({})]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[name].chunk.css'
        })
    ],
    output: {
        filename: '[name].[contenthash].js',
        chunkFilename: '[name].[contenthash].js'
    }
}

module.exports = prodConfig;

四:Webpack實戰配置案例

Library的打包:庫代碼經過webpack進行打包

倉庫源碼 【41

在 webpack.config.js 中:

const path = require('path');

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    externals: 'lodash',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'library.js',
        library: 'root', //支持經過<scritp src=ilibrary. js'></script> 標籤引入,在全局變量增長一個root變量
        libraryTarget: 'umd' //別人用的時候,經過任何形式引入庫均可以,好比AMD,CMD,ES MODULE,Commonjs
        
        // library: 'root',//打包生成全局變量root
        // libraryTarget: 'this' //把全局變量root掛載到this上,能夠填umd,this,window,global
        
        // externals: {
        //     lodash:{
        //         root:'_', //是用script標籤引入進來的,必須在全局注入一個 _ 變量,下面的library才能正常執行
        //         commonjs:'lodash',//在用commonjs規範引入是,名字必須是lodash
        //     }

        // }

    }
}

在 package.json 中:

"main": "./dist/library.js", //最終要給別人使用的

npm 官網註冊一個帳號,在命令行輸入 :

//添加用戶名和密碼
npm adduser 
//把項目發佈到npm官網上
npm publish
//但別人用你發佈的庫時
npm i + 庫名

詳細請看官方文檔:externals author-libraries


Progressive Web Application:在webpack中配置pwa

漸進式網絡應用程序,PWA 能夠用來作不少事。其中最重要的是,在離線(offline)時應用程序可以繼續運行功能。這是經過使用名爲 Service Workers 的 web 技術來實現的。線上環境時纔用到pwa,開發時不須要

倉庫源碼 【42

安裝:

//模擬服務器
npm i http-server -D
//添加 workbox-webpack-plugin 插件,而後調整 webpack.config.js 文件
npm install workbox-webpack-plugin --save-dev

在 package.json 中:

"scripts": {
 +   "start": "http-server dist",//在dist目錄下運行http-server服務
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
  },

線上環境時纔用到pwa,開發時不須要,只須要改 webpack.prod.js ,

在 webpack.prod.js 中:

const WorkboxPlugin = require('workbox-webpack-plugin');
plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[name].chunk.css'
        }),
+        new WorkboxPlugin.GenerateSW({
            clientsClaim: true,
            skipWaiting: true
        })
    ],

在業務代碼 index.js 中使用pwa

console.log('hello, haiyang');

if ('serviceWorker' in navigator) { //若是瀏覽器支持serviceWorker,就執行如下代碼
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js')
            .then(registration => {//註冊成功
                console.log('service-worker registed');
            }).catch(error => {//沒註冊成功
                console.log('service-worker register error');
            })
    })
}

詳細請看官方文檔:progressive-web-application


TypeScript 的打包配置

TypeScript 是 JavaScript 的超集,爲其增長了類型系統,能夠編譯爲普通 JavaScript 代碼。這篇指南里咱們將會學習是如何將 webpack 和 TypeScript 進行集成。

倉庫源碼 【43

安裝:

npm install --save-dev typescript ts-loader

在 webpack.config.js 中:

const path = require('path');

module.exports = {
    mode: 'production',
    entry: './src/index.tsx',
    module: {
        rules: [{
            test: /\.tsx?$/,
            use: 'ts-loader',
            exclude: /node_modules/
        }]
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
}

在項目根目錄下建立 tsconfig.json 文件:

{
    "compilerOpitons": {
        "outDir": "./dist",
        "module": "es6",//模塊引入的方式
        "target": "es5",//轉換爲es5,在大部分瀏覽器都能運行
        "allowJs": true, //在typescript中容許引入js文件
    }
}

在從 npm 安裝 third party library(第三方庫) 時,必定要記得同時安裝此 library 的類型聲明文件(typing definition)。你能夠從 TypeSearch 中找到並安裝這些第三方庫的類型聲明文件。在使用時,哪有錯能夠有警告提示,方便改錯。

安裝:

//在typescript裏用loadah
npm install --save-dev @types/lodash

詳細請看官方文檔:typescript


使用 WebpackDevServer 實現請求轉發

倉庫源碼 【44

安裝:

//向服務器發送axios請求
npm i axios -D

在 index.js 中:

componentDidMount() {
        axios.get('/react/api/header.json')
            .then((res) => {
                console.log(res);
            })
    }

在 webpack.config.js 中:

devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true,
        hotOnly: true,
+        proxy: {//開發時方便接口轉發,線上不用
            '/react/api': {//訪問 /react/api 時,代理到 target 上
                target: 'https://www.dell-lee.com',
                secure: false,//對https協議的網址的請求的轉發
     //   攔截,請求的是html,不走代理直接返回  /index.html文件          
    //bypass: function(req, res, proxyOptions) {
      //    if (req.headers.accept.indexOf('html') !== -1) {
      //      console.log('Skipping proxy for browser request.');
       //     return '/index.html';
       //   }
     //   },
                pathRewrite: {
                    'header.json': 'demo.json' //最後拿的是demo.json的數據
                },
                changeOrigin: true,//解決網站對接口的限制
                headers: {//變動請求頭
                    host: 'www.dell-lee.com',
                }
            }
        }
    },

詳細請看官方文檔:devserverproxy


WebpackDevServer 解決單頁面應用路由問題

倉庫源碼 【45

安裝:

npm i react-router-dom --save

在 webpack.config.js 中:

devServer: {//配置只在開發時有效,上線時後端也需配置
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true,
        hotOnly: true,
+        historyApiFallback: true,
        //historyApiFallback: {
         // rewrites: [//訪問任何路徑都展現index.html頁面
          //  { from: /\.*/, to: '/index.html' },
          //]
        //},
        proxy: {
            '/react/api': {
                target: 'https://www.dell-lee.com',
                secure: false,
                pathRewrite: {
                    'header.json': 'demo.json'
                },
                changeOrigin: true,
                headers: {
                    host: 'www.dell-lee.com',
                }
            }
        }
    },

詳細請看官方文檔:devserverhistoryapifallback


EsLint 在 Webpack 中的配置

倉庫源碼 【46

安裝:

//安裝eslint工具,規範項目中的代碼
npm i eslint -D
npm i babel-eslint -D
npm i eslint-loader -D
//快速生成eslint配置
npx eslint --init

在 .eslintrc.js 中:

module.exports = {
    "extends": "airbnb",
  "parser": "babel-eslint",
  "rules": {
    "react/prefer-stateless-function": 0,
    "react/jsx-filename-extension": 0
  },
  globals: {
    document: false
  }
};

詳細請看官方文檔:eslint

在 vscode 編輯器裏安裝 eslint 插件,自動檢測語法錯誤。(推薦使用)

在 webpack.config.js 中:

devServer: {
+        overlay: true,//在瀏覽器彈出提示有錯誤
    },


rules: [{ 
            test: /\.js$/, 
            exclude: /node_modules/, 
    +        use: ['babel-loader', 'eslint-loader'] //先檢查代碼寫的是否規範,在轉換成es5
        },
        ...],

在真實項目中,也能夠不在webpack 中配置eslint,在提交git倉庫時,git 鉤子 eslint src 。可是沒有圖形交互式的錯誤提示。

詳細請看官方文檔:eslint-loader


提高 webpack 打包速度的方法

倉庫源碼 【47

1.跟上技術的迭代(Node,Npm,Yarn)

2.在儘量少的模塊上應用 Loader

3.Plugin 儘量精簡併確保可靠

4.resolve 參數合理配置

倉庫源碼 【48

引入資源文件寫後綴,像 圖片文件(.jpg, .png, .svg),邏輯代碼配置在extensions中:extensions: ['.js', '.jsx']

5.使用 DLLPlugin 提升打包速度

詳細請看官方文檔:dll-plugin

倉庫源碼 【49

實現第三方模塊只打包一次

安裝:

npm i add-asset-html-webpack-plugin --save

在 build 文件夾裏建立 webpack.dll.js 文件:把第三方模塊單獨進行打包,生成一個vendors.dll.js 文件,全部的第三方模塊都在這個文件裏。

const path = require('path');
const webpack = require('webpack');

module.exports = {
    mode: 'production',
    entry: {
        vendors: ['lodash'],
        react: ['react', 'react-dom'],
        jquery: ['jquery']
    },
    output: {
        filename: '[name].dll.js',
        path: path.resolve(__dirname, '../dll'),
        library: '[name]'//打包生成的庫名,經過全局變量的形式暴露到全局
    },
    plugins: [
        new webpack.DllPlugin({//對暴露到全局的代碼進行分析,生成vendors.manifest.json 的映射文件,
            name: '[name]',
            path: path.resolve(__dirname, '../dll/[name].manifest.json'),
        })
    ]
}

在 webpack.common.js 中:

const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
files.forEach(file => {
    if(/.*\.dll.js/.test(file)) {
        plugins.push(new AddAssetHtmlWebpackPlugin({//將打包好的dll文件掛載到html中
            filepath: path.resolve(__dirname, '../dll', file)
        }))
    }
    if(/.*\.manifest.json/.test(file)) {
        plugins.push(new webpack.DllReferencePlugin({//分析第三方模塊是否已經在dll文件裏,若是裏面有就不用再node_modules在分析打包了
            manifest: path.resolve(__dirname, '../dll', file)
        }))
    }
})

總結:

若是不使用使用 DLLPlugin 插件,當引入第三方模塊時,每一次打包都要進行分析,是消耗打包的性能的。使用 DLLPlugin 提升打包速度,在第一次打包時,把第三方模塊單獨打包生成一個文件 vendors.dll.js ,以後在打包時就能夠直接從 vendors.dll.js 中引入以前打包好的第三方模塊,速度就會變快。

要想實現,就得作一些配置:

先配置 webpack.dll.js 文件,在配置 webpack.common.js 文件

==============================================================

.dll 爲後綴的文件稱爲動態連接庫,在一個動態連接庫中能夠包含給其餘模塊調用的函數和數據

  • 把基礎模塊獨立出來打包到單獨的動態鏈接庫裏
  • 當須要導入的模塊在動態鏈接庫裏的時候,模塊不能再次被打包,而是去動態鏈接庫裏獲取 dll-plugin

定義Dll

  • DllPlugin插件: 用於打包出一個個動態鏈接庫
  • DllReferencePlugin: 在配置文件中引入DllPlugin插件打包好的動態鏈接庫

在 webpack.dll.js 中:

module.exports = {
    entry: {
        react: ['react'] //react模塊打包到一個動態鏈接庫
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].dll.js', //輸出動態鏈接庫的文件名稱
        library: '_dll_[name]' //全局變量名稱
    },
    plugins: [
        new webpack.DllPlugin({
            name: '_dll_[name]', //和output.library中一致,值就是輸出的manifest.json中的 name值
            path: path.join(__dirname, 'dist', '[name].manifest.json')
        })
    ]
}
webpack --config webpack.dll.config.js --mode production

使用動態連接庫文件

在 webpack.common.js 中:

plugins: [
+        new webpack.DllReferencePlugin({
+            manifest: require(path.join(__dirname, 'dist', 'react.manifest.json')),
+        })
    ],
webpack --config webpack.config.js --mode development

==============================================================

6.控制包文件大小

配置 Tree shaking,把用不到的代碼去除掉。配置 SplitChunksPlugin。

7.thread-loader,parallel-webpack,happypack 多進程打包

HappyPack

HappyPack就能讓Webpack把任務分解給多個子進程去併發的執行,子進程處理完後再把結果發送給主進程。 happypack

安裝:npm i happypack@next -D

配置:

module: {
        rules: [{
            test: /\.js$/,
            //把對.js文件的處理轉交給id爲babel的HappyPack實例
 +          use: 'happypack/loader?id=babel',
            include: path.resolve(__dirname, 'src'),
            exclude: /node_modules/
        }, {
            //把對.css文件的處理轉交給id爲css的HappyPack實例
            test: /\.css$/,
+           use: 'happypack/loader?id=css',
            include: path.resolve(__dirname, 'src')
        }],
        noParse: [/react\.min\.js/]
    },
plugins: [
        //用惟一的標識符id來表明當前的HappyPack是用來處理一類特定文件
        new HappyPack({
            id: 'babel',
            //如何處理.js文件,和rules裏的配置相同
            loaders: [{
                loader: 'babel-loader',
                query: {
                    presets: [
                        "env", "react"
                    ]
                }
            }]
        }),
        new HappyPack({
            id: 'css',
            loaders: ['style-loader', 'css-loader'],
            threads: 4, //表明開啓幾個子進程去處理這一類型的文件
            verbose: true //是否容許輸出日子
        })
    ],
ParallelUglifyPlugin

ParallelUglifyPlugin能夠把對JS文件的串行壓縮變爲開啓多個子進程並行執行

安裝:npm i -D webpack-parallel-uglify-plugin

配置:

new ParallelUglifyPlugin({
            workerCount: 3, //開啓幾個子進程去併發的執行壓縮。默認是當前運行電腦的 CPU 核數減去1
            uglifyJS: {
                output: {
                    beautify: false, //不須要格式化
                    comments: false, //不保留註釋
                },
                compress: {
                    warnings: false, // 在UglifyJs刪除沒有用到的代碼時不輸出警告
                    drop_console: true, // 刪除全部的 `console` 語句,能夠兼容ie瀏覽器
                    collapse_vars: true, // 內嵌定義了可是隻用到一次的變量
                    reduce_vars: true, // 提取出出現屢次可是沒有定義成變量去引用的靜態值
                }
            },
        })

8.合理使用 sourceMap

9.結合 stats 分析打包結果

10. 開發環境內存編譯

11.開發環境無用插件剔除


多頁面打包配置

配置多個 entry 裏的 html 頁面,用HtmlWebpackPlugin 插件,將打包好的j多個js分別插入到對應的html頁面中。

倉庫源碼 【410

在 webpack.common.js 中:

const path = require('path');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
const webpack = require('webpack');


const makePlugins = (configs) => {
    const plugins = [
        new CleanWebpackPlugin(['dist'], {
            root: path.resolve(__dirname, '../')
        })
    ];
    Object.keys(configs.entry).forEach(item => {
        plugins.push(
+            new HtmlWebpackPlugin({
                template: 'src/index.html',
                filename: `${item}.html`,
                chunks: ['runtime', 'vendors', item]
            })
        )
    });
    const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
    files.forEach(file => {
        if(/.*\.dll.js/.test(file)) {
            plugins.push(new AddAssetHtmlWebpackPlugin({
                filepath: path.resolve(__dirname, '../dll', file)
            }))
        }
        if(/.*\.manifest.json/.test(file)) {
            plugins.push(new webpack.DllReferencePlugin({
                manifest: path.resolve(__dirname, '../dll', file)
            }))
        }
    });
    return plugins;
}

const configs = {
+    entry: {
        index: './src/index.js',
        list: './src/list.js',
        detail: './src/detail.js',
    },
    resolve: {
        extensions: ['.js', '.jsx'],
    },
    module: {
        rules: [{ 
            test: /\.jsx?$/, 
            include: path.resolve(__dirname, '../src'),
            use: [{
                loader: 'babel-loader'
            }]
        }, {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            } 
        }, {
            test: /\.(eot|ttf|svg)$/,
            use: {
                loader: 'file-loader'
            } 
        }]
    },
    optimization: {
        runtimeChunk: {
            name: 'runtime'
        },
        usedExports: true,
        splitChunks: {
      chunks: 'all',
      cacheGroups: {
          vendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10,
              name: 'vendors',
          }
      }
    }
    },
    performance: false,
    output: {
        path: path.resolve(__dirname, '../dist')
    }
}

configs.plugins = makePlugins(configs);

module.exports = configs

五:Webpack底層原理及腳手架工具分析

如何編寫一個 Loader

倉庫源碼 【51

mkdir make-loader
cd make-loader
npm init -y
npm i webpack webpack-cli -D
npm i loader-utils -D

在根目錄下建立文件夾loaders,裏面建立本身定義的loader.js文件

在 webpack.config.js 中:

const path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    resolveLoader: { //先在 node_modules 中找用到的loader,若是沒找到,再在loaders裏查找
        modules: ['node_modules', './loaders']
    },
    module: {
        rules: [{
            test: /\.js/,
            use: [//使用本身寫的replaceLoader
                {
                    loader: 'replaceLoader',
                },
                {
                    loader: 'replaceLoaderAsync',
                    options: {
                        name: 'lee'
                    }
                },
            ]
        }]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}

詳細請看官方文檔:loaders#thisquery hiscallback thisasync


如何編寫一個 Plugin

倉庫源碼 【52

詳細請看官方文檔:compiler-hooks


Bundler 源碼編寫(模塊分析)

應對 webpack 原理面試必備:

倉庫源碼 【53

安裝 :

//高亮顯示代碼的工具
npm i cli-highlight -g
//分析源代碼
npm install @babel/parser --save
npm install @babel/core --save
npm install @babel/traverse --save
npm install @babel/preset-env --save

Bundler 源碼編寫(Dependencies Graph)

倉庫源碼 【54


Bundler 源碼編寫(生成代碼)

倉庫源碼 【55

六:Create-React-App 和 Vue-Cli 3.0腳手架工具配置分析

經過CreateReactApp深刻學習Webpack配置

倉庫源碼 【56

詳細請看官方文檔:CreateReactApp

快速開始:

npx create-react-app my-app
cd my-app
npm start

把隱藏的配置項展示出來:不可逆操做

npm run eject

就會多出來兩個文件夾,config,scripts文件夾


Vue-Cli 3.0

倉庫源碼 【57

詳細請看官網:VUE Cli 全局-cli-配置

相關文章
相關標籤/搜索