webpack4学习笔记

快速上手

  1. npm init -y

  2. npm install webpack webpack-cli -D

  3. 调整 package.json 文件,确保安装包是私有的(private),并且移除 main 入口。可防止意外发布代码

    {
        +  "private": true,
        -  "main": "index.js",
    }
    
  4. 使用webpack
    ① 配置webpack.config.js 或 (npx webpack --config 自定义文件名)

    var path = require('path');
    
    module.exports = {
       mode: 'production',
       entry: './src/index.js',  
       /**
       entry: {
                main: './src/index.js'
              }
       **/
       output: {
           filename: 'bundle.js',
           path: path.resolve(__dirname, 'dist')
       }
    }
    

② package.json中找到script新增"bundle": "webpack"

"scripts": {
  "bundle": "webpack",
}

npm run bundle

loader 配置

  1. 图片、视频、字体文件打包 url-loader
npm install url-loader -D
module.exports = {
   module: {
       rules: [
         {
           test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
           use: [{
                loader: 'url-loader',
                 options: {
                    name: 'img/[name]_[hash:7].[ext]',
                    limit: 10000 // 小于10000的生成base64
                 }
           }]
         },
         {
            test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
            use: [{
                 loader: 'url-loader',
                 options: {
                    limit: 10000,
                    name: 'media/[name]_[hash:7].[ext]',
                    }
              }]
         },
         {
           test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
           use: [{
              loader: 'url-loader',
              options: {
                  limit: 10000,
                   name: 'fonts/[name].[hash:7].[ext]',
                    }
                }]
            }
        ]
   }
}
  1. css打包并添加前缀 style-loader css-loader postcss-loader autoprefixer
npm i style-loader css-loader postcss-loader autoprefixer -D

添加postcss.config.js

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

package.json中增加browserslist选项(必须添加,不然添加不上兼容性前缀)

{
    "browserslist": [
        "> 1%",
        "last 2 versions",
        "not ie <= 8"
    ]
}

webpack.config.js中增加loader

module.exports = {
   module: {
       rules: [{
           test: /\.css$/,
           use: [
           'style-loader',
           {
               loader: 'css-loader',
               options: {
                   importLoaders: 1 // 引入文件时使用
               }
           },
           'postcss-loader'
           ]
       }]
   }
}
  1. less文件打包 less-loader
// 提前安装style-loader和css-loader
npm install less-loader less -D 
module.exports = {
   module: {
       rules: [{ 
           test: /\.less$/,
           use: ['style-loader','css-loader','less-loader']
       }]
   }
}
  1. scss文件打包 sass-loader
// 提前安装style-loader和css-loader和postcss-loader和autoprefixer
npm install sass-loader node-sass webpack -D 
module.exports = {
   module: {
       rules: [{ 
           test: /\.scss$/,
           use: [
                  'style-loader',
                  {
                      loader: 'css-loader',
                      options: {
                          importLoaders: 2
                      }
                  },
                  'postcss-loader', // 经测试需放在sass-loader后
                  'sass-loader'
              ]
       }]
   }
}

plugins使用

  1. html-webpack-plugin
npm install --save-dev html-webpack-plugin

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
export.modules = {
   plugins: [
     new HtmlWebpackPlugin({
       title: 'Output Management',
       template: 'src/index.html'
     })
   ],
}
  1. clean-webpack-plugin
npm install --save-dev clean-webpack-plugin

webpack.config.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
    plugins: [
     new CleanWebpackPlugin()
    ]
  };

devtool配置

source-map(最慢) 在js文件下多了.map.js文件
inline-source-map 【inline】.map.js文件被打包到了页面引入的js文件中
cheap-inline-source-map【cheap】打包性能提升①不用精确到列②只管业务代码,不管引入的第三方模块代码
cheap-module-inline-source-map【module】不仅需要检查业务代码,而且需要检查引入的第三方模块代码
eval(最快)复杂的代码提示可能不全
最优配置方案

// 开发环境
module.exports = {
   mode: 'development',
   devtool: 'cheap-module-eval-source-map'
}
// 生产环境
module.exports = {
   mode: 'production',
   devtool: 'cheap-module-source-map'
}

开发环境启动

  1. sourceMap
    webpack.config.js
module.exports = {
   mode: 'development',
   devtool: 'cheap-module-eval-source-map'
}
  1. watch Mode
    package.json
"scripts": {
   "watch": "webpack --watch"
}
  1. webpack-dev-server(推荐)
npm install webpack-dev-server -D

webpack.config.js

module.exports = {
   devServer: {
      contentBase: './dist',
      open: true,
      port: 8080 // 默认8080,可更改
   // proxy: {
   //     '/api': {
   //         target: 'http://localhost:3000',
   //         pathRewrite: {'^/api': ''}
   //     }
   // }
   }
};

package.json

"scripts": {
    "start": "webpack-dev-server"
}
  1. webpack-dev-middleware
npm install --save-dev express webpack-dev-middleware

project 增加server.js

  |- package.json
  |- webpack.config.js
+ |- server.js
  |- /dist
  |- /src
    |- index.js
    |- print.js
  |- /node_modules

server.js

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');

const app = express();
const config = require('./webpack.config.js');
const compiler = webpack(config);

// Tell express to use the webpack-dev-middleware and use the webpack.config.js
app.use(webpackDevMiddleware(compiler, {}));

// Serve the files on port 3000.
app.listen(3000, function () {
  console.log('Example app listening on port 3000!\n');
});

package.json

"scripts": {
   "server": "node server.js"
},

热更新(Hot Module Replacement)

eg: D:\学习视频\webpack\源码\02-09_hot_module_replacement
webpack.config.js

const webpack = require('webpack');
module.exports = {
    devServer: {
    +   hot: true,
    +   hotOnly: true // 即使HMR不生效、浏览器也不自动刷新
    },
    plugins: [
    +   new webpack.HotModuleReplacementPlugin()
    ]
}

index.js

if(module.hot) {
    module.hot.accept('./number', () => {
        document.body.removeChild(document.getElementById('number'));
        number();
    })
}

babel配置

安装

npm install  babel-loader @babel/core -D
npm install  @babel/preset-env -D

npm install  @babel/plugin-transform-runtime -D
npm install  @babel/runtime @babel/runtime-corejs3 -S

webpack.config.js

module: {
  rules: [
    { 
       test: /\.js$/, 
       exclude: /node_modules/,
       loader: "babel-loader"
    }
  ]
}

.babelrc

{
  "presets": [
    [
      "@babel/preset-env", // 智能预设,翻译最新的js语法
      {
        "targets": {
          "browsers": [
            "> 1%",
            "last 2 version",
            "not ie<=8"
          ]
        }
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime", //翻译corejs中用到的垫片
      {
        /*
        corejs: boolean或number,默认为false。 
        corejs:false 不转换垫片;
        corejs:2 转换垫片,不支持实例方法
        corejs:3 转换垫片,支持实例方法 
        */
        "corejs": "3", 
        "helpers": true,
        "regenerator": true,
        "useESModules": false
      }
    ]
  ]
}

使用@babel/plugin-transform-runtime而不是useBuiltIns:'usage'的原因是plugin-transform-runtime①避免重复代码 ②防止全局污染

Tree Shaking

去除引入模块中没有引入的代码
只支持ES Module静态模块的引入(import XX from XX)
package.json

{
  "name":"learn-webpack",
+ "sideEffects": [ // 不进行Tree shaking的文件
   "*.css"
  ]
}

webpack.config.js

- mode: 'development',
- optimization: {
-   usedExports: true // 被使用的文件进行打包
- }
+ mode: 'production'

development和production打包文件配置

  • 把webpack.config.js拆成公共、dev、prod三份配置文件
  • 安装merge插件cnpm i webpack-merge -D,dev和prod文件中merge公共文件
  • package.json里配置打包方式
"scripts": {
  "dev": "webpack-dev-server --config ./build/webpack.dev.js",
  "build": "webpack --config ./build/webpack.prod.js"
}

code splitting

三种方式:通过配置entry、同步加载分割、异步加载分割
1. entry
提前安装lodash插件npm i lodash -S
lodash.js

import _ from 'lodash';
window._ = _;

main.js

_.join(['hello','splitting'],',');

webpack entry配置

{
  entry: {
     lodash:'./src/lodash.js',
     main:'./src/main.js'
  }
}

2. 同步加载
webpack optimization配置

optimization: {
    splitChunks: { 
      chunks:'all'
   }
}

main.js

import _ from 'lodash';
_.join(['hello','splitting'],',');

3. 异步加载(无需任何配置,会自动进行代码拆分)

 async function getComponent() {
    const element = document.createElement('div');
    const {default: _} = await import(/* webpackChunkName: "lodash" */ 'lodash');
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    return element;
}

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

splitChunks

webpack.config.js

optimization: {
 splitChunks: {
   chunks: 'all', // initial 同步  async 异步  all 同步、异步
   minSize: 30000, // 超过30kb才拆分打包
   // maxSize: 0, // 一般不配置
   minChunks: 1, // 最少使用一次
   maxAsyncRequests: 5, // 同时加载的模块数最多是5个
   maxInitialRequests: 3, // 首页引入最多拆分3个代码块
   automaticNameDelimiter: '~', // 组合文件之间连接符
   automaticNameMaxLength: 30,
   name: true, // 是否支持命名
   cacheGroups: { // 添加或覆盖splitChunks中的属性
     vendors: {
        test: /[\\/]node_modules[\\/]/,
         priority: -10, // 优先级(不同文件夹间比较)
         name: 'vendors' // filename:只针对initial方式,否则报错
      },
      default: {
        minChunks: 1,
         priority: -20,
         reuseExistingChunk: true, // 已经打包过的文件不再重新打包
         name: 'commons'
      }
   }
 }
}

懒加载文件

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

document.addEventListener('click', function () {
    getComponent().then(element => {
        document.body.appendChild(element);
    });
})

注:函数写成async - await 的方式,打包时直接会把引入的文件打包,没有实现懒加载

css 打包

cnpm install -D mini-css-extract-plugin // 拆分css
cnpm install -D terser-webpack-plugin // 压缩
cnpm install -D optimize-css-assets-webpack-plugin // 压缩

webpack.prod.js

const TerserJSPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
    module: {
        rules: [
           {
             test: /\.css$/,
               use: [
                   MiniCssExtractPlugin.loader, // MiniCssExtractPlugin.loader替换原有的style-loader
                   'css-loader',
                   'postcss-loader'
               ]
            },
         ]
    },
    optimization: {
        minimizer: [
            new TerserJSPlugin({}),
            new OptimizeCSSAssetsPlugin({})
        ],
        splitChunks: {
            cacheGroups: {
                styles: {
                    name: 'styles',
                    test: /\.css$/,
                    chunks: 'all',
                    enforce: true
                }
            }
        }
    },
    plugins: [
        new MiniCssExtractPlugin()
    ]
}

代码优化

1.打包分析 http://www.github.com/webpack/analyse

① 生成打包分析json文件

webpack --profile --json > stats.json

② http://webpack.github.com/analyse上传json可查看分析结果

2.控制台Run Command(Ctrl+shift+p) Show coverage 查看代码使用率

3.代码异步加载
eg: 新建click.js

function click() {
    const element = document.createElement('div');
    element.innerHTML = 'vivian';
    document.body.appendChild(element);
}

export default click;

main.js
**webpackPrefetch:true **
可在任务完成后,带宽空闲时提前加载js

document.addEventListener('click', function () {
   import(/* webpackPrefetch:true */'./click.js')
   .then(({default: func}) => {
        func();
    });
})

浏览器缓存

去除打包时性能提示 webpack配置中增加performance:false

为了防止更新文件后,浏览器缓存。在输出文件夹加contenthash

  output: {
      filename: '[name].[contenthash:7].js',
      chunkFilename: '[name].[contenthash:7].js',
  },
  optimization:{
     runtimeChunk: { // 解决老版本依赖文件生成不同hash值的办法
         name: 'runtime'
     },
     splitChunks: { // 打包出vendors文件
        chunks: 'all',
        cacheGroups: {
            vendors: {
                test: /[\\/]node_modules[\\/]/,
                priority: -10,
                name: 'vendors'
            }
        }
     }
  }

shimming

const webpack = require('webpack');
plugins:[
    new webpack.ProvidePlugin({
        $:'jquery'
    })
]

devServer

devServer: {
    contentBase: './dist',
    open: true,
    port: 8080, // 默认8080,可更改
    hot: true,
    proxy: {
       '/api': {
           target: 'http://localhost:3000',
           secure: false, // true时,接受运行在 HTTPS 上
           pathRewrite: {'^/api': ''},
           changeOrigin: true, // 突破有些接口对origin限制
           headers:{
             host:'www.baidu.com'
           }
       }
    }
}

代理多个路径特定到同一个 target 下

module.exports = {
  //...
  devServer: {
    proxy: [{
      context: ['/auth', '/api'],
      target: 'http://localhost:3000',
    }]
  }
};

webpack打包优化

  1. 更新webpack、node版本
  2. loader范围缩小
{
    test: /\.js$/, 
        exclude:/node_modules/,
        // include:path.resolve(__dirname,'./src'),
        use: [{
            loader: 'babel-loader'
        }, {
            loader: 'imports-loader?this=>window'
        }]
}
  1. 插件的合理使用
    eg:生成环境不使用压缩css插件、使用官方插件等;
  2. 文件引入方式
resolve:{
    extensions:['.js','.jsx'], // 省略后缀
    mainFiles:['index','child'], // 省略文件名(不常用)
    alias:{ // 别名
        child:path.resolve(__dirname,'../src/a/b/c/child')
    }
}
import child from 'child';
  1. 引入第三方文件打包到一个文件中
  • 生成dll文件
    webpack.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({
            name: '[name]',
            path: path.resolve(__dirname, '../dll/[name].mainfest.json')
        })
    ]
}
  • 指向生成的dll文件并挂载到html上
npm i add-asset-html-webpack-plugin -S
var path = require('path');
const fs = require('fs');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');

const plugins = [
    new HtmlWebpackPlugin({
        title: 'Output Management',
        template: 'src/index.html'
    }),
    new CleanWebpackPlugin(),
    new webpack.ProvidePlugin({
        $: 'jquery'
    })
];

const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
files.forEach(file => {
    if (/.*\.dll.js/.test(file)) {
        plugins.push(new AddAssetHtmlWebpackPlugin({ // 挂载到html上
            filepath: path.resolve(__dirname, '../dll', file)
        }))
    }
    if (/.*\.manifest.json/.test(file)) {
        plugins.push(new webpack.DllReferencePlugin({ // 指向生成的dll文件
            manifest: path.resolve(__dirname, '../dll', file)
        }))
    }
})
module.exports = {
    plugin
}
  1. 控制包大小
  2. thread-loader,parallel-webpack,happypack多进程打包
  3. 合理使用sourceMap
  4. 结合stats分析打包结果
  5. 开发环境内存编译
  6. 开发环境无用插件剔除

多页面打包

plugins.push(new HtmlWebpackPlugin({ // 多页面配置多个htmlWebpackPlugin
    template: 'src/index.html',
    filename: `${item}.html`, // 输出文件名
    chunks: ['vendors', item] // 文件引入的js
}))
const makePlugin = (configs) => {
    const plugins = [
        new CleanWebpackPlugin()
    ];

    Object.keys(configs.entry).forEach(item => {
        plugins.push(new HtmlWebpackPlugin({
            template: 'src/index.html',
            filename: `${item}.html`,
            chunks: ['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;
};

自定义loader

  1. 新建make-loader文件夹 ---> npm init -y
  2. npm i webpack webpack-cli -D
  3. webpack.config.js
const path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    resolveLoader: {
        modules: ['node_modules', './loaders']
    },
    module: {
        rules: [{
            test: /\.js/,
            use: [
                {
                    loader: 'replaceLoader'
                },
                {
                    loader: 'replaceLoaderAsync',
                    options: {
                        name: 'vv'
                    }
                }
            ]
        }]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}
  1. 自定义loader
    npm i loader-untils -D
    replaceLoaderAsync.js
const loaderUtils = require('loader-utils');

module.exports = function (source) {
    const options = loaderUtils.getOptions(this);
    const callback = this.async();

    setTimeout(() => {
        const result = source.replace('vivian', options.name);
        callback(null, result);
    }, 1000)
}

replaceLoader.js

module.exports = function (source) {
    return source.replace('vv','vv2');
}
  1. package.json
{
    "build":"webpack"
}

npm run build

自定义plugin

  1. 新建make-plugin文件夹 ---> npm init -y
  2. npm i webpack webpack-cli -D
  3. 自定义plugin
    copyright-webpack-plugin.js
class CopyRightWebpackPlugin {
    apply(compiler) {
        compiler.hooks.compile.tap('CopyrightWebpackPlugin', (compilation) => {
            console.log('complier');
        })

        compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (compilation, cb) => {
            compilation.assets['copyright.text'] = {
                source: function () {
                    return 'copyright by vivian'
                },
                size: function () {
                    return 19;
                }
            }
            cb();
        })
    }
}

module.exports = CopyRightWebpackPlugin
  1. webpack.config.js
const path = require('path');
const CopyRightWebpackPlugin = require('./plugins/copyright-webpack-plugin');

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    plugins: [
        new CopyRightWebpackPlugin()
    ],
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}
  1. package.json
{
    "build":"webpack"
}

npm run build

配置eslint

npm i eslint eslint-loader -D

生成.eslintrc.js初始化文件

npx eslint --init 

webpack.config.js

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          'babel-loader',
          {
            loader: 'eslint-loader',
            options: 'fix',
            force: 'pre' // 最先执行
          }
         ],
      },
    ],
  },
  devServer: {
      overlay: true, // 弹窗显示报错
      contentBase: './dist',
      open: true,
      port: 8089
 },
 // ...
};

你可能感兴趣的:(webpack4学习笔记)