webpack從零開始

一.基礎配置

1.init項目

mkdir react-webpack-demo
cd react-webpack-demo
mkdir src
mkdir dist
npm init -y

2.安裝webpack

安裝webpack,並新建webpack.js文件,並初始化文件javascript

yarn add webpack  webpack-cli webpack-dev-server -D
mkdir config
touch config/webpack.common.js
module.exports = {
   
    entry: ['./src/index.js'],//入口
    output: { //出口
        path: paths.appBuild
    },
    module: {}, //配置 loader
    plugins: [], //插件
};

3.安裝react react-dom

yarn add react react-dom

4.配置loader

  1. loaders
    loader 用來解析文件轉譯成瀏覽器能夠識別的文件。如.less、.jsx等這些文件瀏覽器是不能正常轉譯的,loaders的做用就是充當着'翻譯'的做用。
  2. babel 輸入源碼 => 輸出編譯後的代碼,總共分爲三個階段:解析,轉換,生成。
    babel 自己不具備任何轉化功能,它把轉化的功能都分解到一個個 plugin 裏面。所以當咱們不配置任何插件時,通過 babel 的代碼和輸入是相同的。
    插件總共分爲兩種:語法插件(Babel 解析(parse) 特定類型的語法,轉換插件會自動啓用語法插件),轉換插件(例如:箭頭函數 (a) => a 就會轉化爲 function (a) {return a})
  3. 預設(Presets)
    preset 能夠做爲 Babel 插件的組合;
    Preset 是逆序排列的(從後往前)
    @babel/preset-react就是一個官方 Preset
  4. @babel/polyfill
    1.babel 默認只轉換 js 語法,而不轉換新的 API,好比 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局對象,以及一些定義在全局對象上的方法(好比 Object.assign)都不會轉碼。
    2.babel-polyfill 會污染全局變量,給不少類的原型鏈上都做了修改,若是咱們開發的也是一個類庫供 其餘開發者使用,這種狀況就會變得很是不可控。
    3.類庫開發,一般咱們會傾向於使用 babel-plugin-transform-runtime
    4.@babel/preset-env 獲取您指定的任何目標環境,,按需轉碼,引入相應的插件,默認使用browserslistcss

    @babel/core-babel  核心模塊    
    @babel/preset-env  編譯ES6等 
    @babel/preset-react  轉換JSX
    @babel/preset-react  轉換JSX
    @babel/plugin-transform-runtime 避免 polyfill 污染全局變量,減少打包體積
    @babel/polyfill  ES6 內置方法和函數轉化墊片

配置html

{
    test: /\.(js|jsx)$/,
    include: paths.appSrc,
    use: [
        {
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-react'],
                plugins: [
                    // 按需加載lodash
                    'lodash',
                    // babel-plugin-import
                    // true是less, 能夠寫'css' 若是不用less
                    ['import', { libraryName: 'antd', libraryDirectory: 'es', style: 'less' }],
                    [
                        '@babel/plugin-transform-runtime',
                        {
                            absoluteRuntime: false,
                            corejs: false,
                            helpers: false,
                            regenerator: true, // generator不會污染全局的
                            useESModules: false, // 轉換將使用沒法運行的幫助程序
                        },
                    ],
                    // '@babel/plugin-syntax-dynamic-import'
                ],
                cacheDirectory: true,
                cacheCompression: isEnvProduction,
                compact: isEnvProduction,
            },
        },
    ],
}

6. 按需引入Polyfill

Polyfill是一個js庫,主要撫平不一樣瀏覽器之間對js實現的差別。根據瀏覽器不一樣的UA按需加載polyfill,國內瀏覽器支持很差。
<script crossorigin="anonymous" src="https://polyfill.io/v3/polyfi...;></script>java

7. CSS loader

  1. style-loader :以<style></style>形式在html頁面中插入css代碼
  2. css-loader :加載.css文件 ,options{Minimize:是否開啓css代碼壓縮。modules:是否開啓css-modules}
  3. postcss-loader :添加瀏覽器前綴
  4. less-loader less轉css

能夠經過匹配文件是否帶module分別配置是否要開始modulesnode

const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;

module配置react

modules: {
        mode: 'local',
        localIdentName: '[local]--[hash:base64:5]',
        context: resolve(__dirname, 'src'),
        hashPrefix: 'my-less-hash',
    },

8. 使用 source-map,對devtool進行優化

  1. devtool:"cheap-module-eval-source-map",// 開發環境配置
  2. devtool:"cheap-module-source-map", // 線上生成配置

9. 使用 WebpackDevServer

devServer: {
    hot: true,
    contentBase: paths.appBuild,
    host: "0.0.0.0", // 可使用手機訪問
    port: 8080,
    historyApiFallback: true, // 該選項的做用全部的404都鏈接到index.html
    compress: true,
    inline: true,
  }

二 優化

1.文件路徑優化

  1. extension: 指定extension以後, 引入模塊的時候,能夠不用擴展名
  2. alias: 配置別名能夠加快webpack查找模塊的速度
  3. modules:去哪些目錄下尋找第三方模塊
resolve: {
        // 引入模塊的時候,能夠不用擴展名
        extensions: ['.js', '.json', '.jsx'],
        // 別名
        alias: {
            '@assets': resolve(__dirname, '../src/assets'),
            '@components': resolve(__dirname, '../src/components'),
            '@pages': resolve(__dirname, '../src/pages'),
        },
        // 添加一個目錄到模塊搜索目錄
        modules: [resolve(__dirname, '../src'), 'node_modules'],
    },

2.靜態資源託管到CDN時

  1. filename:決定了每一個輸出 bundle 的名稱
  2. path:這些 bundle 將寫入到 output.path 選項指定的目錄下。
  3. publicPath:將上面兩個準備的路徑字符串前面加上外部路徑,好比某個CDN:http://xx.xx.com/public/,此時原資源位置:js/xx.xxxxx.bundle.js --> http://xx.xx.com/public/js/xx...

3.MiniCssExtractPlugin ,抽取 css 文件

css是直接打包進js裏面的. 單獨生成css,css能夠和js並行下載,提升頁面加載效率,
loader和plugins 都要配置webpack

4.緩存

  1. hash(全部文件哈希值相同,只要改變內容跟以前的不一致,全部哈希值都改變,沒有作到緩存意義)
  2. chunkhash(同一個模塊,就算將js和css分離,其哈希值也是相同的,修改一處,js和css哈希值都會變,同hash,沒有作到緩存意義)
  3. contenthash(只要文件內容不同,產生的哈希值就不同)

output和new MiniCssExtractPlugin() 時,配置 filename: 'static/css/[name].[contenthash:8].css',web

5. 代碼分割按需加載、提取公共代碼

optimizationnpm

optimization: {
        minimize: isEnvProduction,
        // 找出模塊的順序,最小的初始包,product 默認開啓
        occurrenceOrder: true,
        // runtimeChunk:會爲每一個僅含有 runtime 的入口起點添加一個額外 chunk;
        // 值 "single" 會建立一個在全部生成 chunk 之間共享的運行時文件。此設置是以下設置的別名:
        runtimeChunk: { name: 'manifest' },
        // usedExports 不導出未使用的代碼
        usedExports: true,
        concatenateModules: true,
        splitChunks: {
            // chunks: 'async', // 必須三選一: "initial" | "all"(推薦) | "async" (默認就是async)
            minSize: 30000, // 最小尺寸,默認值是30kb
            minChunks: 1, // 最小 chunk ,默認1
            maxAsyncRequests: 5, // 最大異步請求數, 默認5
            maxInitialRequests: 3, // 最大初始化請求數,默認3
            automaticNameDelimiter: '-', // 打包分隔符
            name: true, // 打包後的名稱,此選項可接收 function
            //設置緩存組,用來抽取知足不一樣規則的chunk
            cacheGroups: {
                // 這裏開始設置緩存的 chunks
                vendor: {
                    //  key 爲entry中定義的 入口名稱
                    test: /[\\/]node_modules[\\/]/, // 正則規則驗證,若是符合就提取 chunk
                    chunks: 'initial',
                    name: 'vendor', // 要緩存的 分隔出來的 chunk 名稱
                    priority: 10,
                    enforce: true,
                },
                lazy: {
                    test: ({ resource }) => {
                        return /antd/.test(resource);
                    },
                    chunks: 'async',
                    name: 'lazy',
                    priority: 10,
                    enforce: true,
                },
                commons: {
                    chunks: 'all',
                    test: ({ resource }) => {
                        return /[\\/]node_modules[\\/]/.test(resource) && !shouldExcludeFromCommon.test(resource);
                    },
                    name: 'common',
                    minChunks: 2,
                    maxInitialRequests: 5,
                    minSize: 0,
                    priority: 20,
                },
            },
        },
    },

6. 文件壓縮

webpack4只要在生產模式下, 代碼就會自動壓縮json

7. 暴露全局變量

new webpack.ProvidePlugin({
            fetch: 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch',
        }),

8. 指定環境,定義環境變量

DefinePlugin 容許建立一個在編譯時能夠配置的全局常量,這些值會被內聯進那些容許傳一個代碼壓縮參數的代碼中,從而減小冗餘的條件判斷

new webpack.DefinePlugin({
  PRODUCTION: JSON.stringify(true),
  VERSION: JSON.stringify("5fa3b9"),
  BROWSER_SUPPORTS_HTML5: true,
  TWO: "1+1",
  "typeof window": JSON.stringify("object")
})

9. css tree sharking

npm i glob-all purify-css purifycss-webpack --save-dev
 
const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')
plugins:[
    // 清除無用 css
    new PurifyCSS({
      paths: glob.sync([
        // 要作 CSS Tree Shaking 的路徑文件
        path.resolve(__dirname, './src/*.html'), // 請注意,咱們一樣須要對 html 文件進行 tree shaking
        path.resolve(__dirname, './src/*.js')
      ])
    })
]

10. js Tree Shaking

清除到代碼中無用的js代碼,只支持import方式引入,不支持commonjs的方式引入
只要mode是production就會生效,develpoment的tree shaking是不生效的,由於webpack爲了方便你的調試

optimization: {

usedExports:true,

}

10. 注意點

  1. rule配置順序影響構建,無用的rule所有去掉。
  2. loader 加載順序:從右向左:Webpack選擇了compose方式

11. 頁面緩存

緩存一次後 服務器掛了 還能夠用
// // 預緩存
new GenerateSW({

clientsClaim: true,
skipWaiting: true,
importWorkboxFrom: 'local',
include: [/\.js$/, /\.css$/, /\.html$/, /\.jpg/, /\.jpeg/, /\.svg/, /\.webp/, /\.png/],

}),

12. webpack-merge

提取公共配置
module.exports = merge(common, webpackConfig);

13. 使用HappyPack

MacBook Pro 2017上測試了幾把 ,並無提高速度,反而慢了,後續再看。

三 常見問題

1. antd 打包後icons dist文件很大

resolve: {

alias: {
        '@ant-design/icons/lib/dist$': resolve(__dirname, '../src/assets/icons.js')
    },

}
並在icons.js 配置

2. antd 按需引入 配置less是報錯

  1. 按需引入antd配置:在babel-loader plugins中添加 以下配置
  2. 須要先安裝 babel-plugin-import
  3. 導入js和css模塊(LESS/Sass源文件): style: true
  4. 導入js和css模塊(css 內置文件) style:"css"
    ['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }],

若是是less 會報錯:須要在less-loader option 裏面添加javascriptEnabled: true,
若是添加自定義主題: 添加 modifyVars:
{

'primary-color': '#1DA57A',
'link-color': '#1DA57A',
'border-radius-base': '2px',

},

3. antd 須要自行引入moment

在index.html文件引入

4. css module

module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'css-loader',
        options: {
          modules: {
            mode: 'local',
            localIdentName: '[path][name]__[local]--[hash:base64:5]',
            context: path.resolve(__dirname, 'src'),
            hashPrefix: 'my-custom-hash',
          },
        },
      },
    ],
  },

5.UglifyjsWebpackPlugin不支持ES6語法

使用terser-webpack-plugin

相關文章
相關標籤/搜索