美文网首页
webpack从基础到优化

webpack从基础到优化

作者: 池鱼_故渊 | 来源:发表于2020-05-19 22:35 被阅读0次

    安装

    npm install --save-dev webpack npm install --save-dev webpack@<version> //可以指定安装版本
    如果版本在4以上还需要webpack-cli
    npm install --save-dev webpack-cli
    不推荐全局安装

    "scripts": { "start": "webpack --config webpack.config.js" },
    修改package.json的命令

    配置基本的webpack.config.js

    const path = require('path'); //node自带 的模块
    
    module.exports = {
     entry: './src/index.js',// 入口文件
     output: {
       filename: 'bundle.js',//输出文件名
       path: path.resolve(__dirname, 'dist')//输出文件夹
     }
    };
    

    基本配置的loder

    loader的概念:webpack允许你使用loader来处理除了js之外的任何静态资源,你可以使用nodejs来编写自己的loader
    1.file-loader

    module.exports = {
     module: {
       rules: [
         {
           test: /\.(png|jpg|gif)$/,
           use: [
             {
               loader: 'file-loader',
               options: {
                   name:'[path][name].[ext],'//为文件配置自定义的模板,//[hash]哈希值
                   //[name]资源原本的名字
                   //[ext]资源扩展名
                   //[path]资源路径
                   publicPath: 'assets/',
                   //为你的文件配置自定义的发布目录
                   outputPath: 'images/',
                   //文件打包后的目录
                   //其他不常用配置项需时参考官网loader
               }
             }
           ]
         }
       ]
     }
    }
    

    可以用来处理图片和字体包之类的资源
    相关的配置项:https://www.webpackjs.com/loaders/file-loader/

    2.style-loader css-loader url-loader

    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [ 'style-loader', 'css-loader' ]
            //只能解析正常的css,如果你使用的是sass,还需要装sass-loader等等
          },
          {
            test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
            loader: 'url-loader',
            options: {
              limit: 10000//配置图片的大小小于多少kb可以打包成base64
            }
          }
        ]
      }
    }
    

    生产环境提取css

    const env = process.env.NODE_ENV
    
    const ExtractTextPlugin = require('extract-text-webpack-plugin')
    
    module.exports = {
     module: {
       rules: [
         {
           test: /\.css$/,
           use: env === 'production'
             ? ExtractTextPlugin.extract({
                 fallback: 'style-loader',
                 use: [ 'css-loader' ]
             })
             : [ 'style-loader', 'css-loader' ]
         },
       ]
     },
     plugins: env === 'production'
       ? [
           new ExtractTextPlugin({
             filename: '[name].css'
           })
         ]
       : []
    }
    

    主要借助于extract-text-webpack-plugin的使用
    详细使用见官网https://www.webpackjs.com/loaders/css-loader/
    目前该插件不在支持webpack4.x,新版本应该使用

    mini-css-extract-plugin
    对比上一个插件的优点

    • 异步加载
    • 不重复编译 性能好
    • 只针对css
      const MiniCssExtractPlugin = require('mini-css-extract-plugin');
      
      module.exports = {
          module:{
              rules:[
              {
                  test: /\.css$/,
                  use:[{
                      loader:MiniCssExtractPlugin.loader,
                      options:{
                          
                      },
                      'css-loader'
                  }],
                    //排除在外不需要处理的部分
                    exclude: /node_modules/
              }
              ]
          },
          plugins:[
           new MiniCssExtractPlugin({
              //主文件入口生成的文件名
               filename:'[name].[hash].css',
               // 按需加载文件生成的文件名
               chunkFilename:'[id].[hash].css'
           })
          ]
      }
      
    

    插件使用方法npm地址 https://www.npmjs.com/package/mini-css-extract-plugin

    压缩css 可以使用 optimize-css-assets-webpack-plugin 插件

    const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
    // 普通压缩
     plugins: [ 
       new OptimizeCSSAssetsPlugin ()
     ]
    
    // 使用cssnano配置规则
    // 先 npm i cssnano -D
    plugins: [ 
      new OptimizeCSSAssetsPlugin ({
        // 默认是全部的CSS都压缩,该字段可以指定某些要处理的文件
        assetNameRegExp: /\.(sa|sc|c)ss$/g, 
        // 指定一个优化css的处理器,默认cssnano
        cssProcessor: require('cssnano'),
       
        cssProcessorPluginOptions: {
          preset: [  'default', {
              discardComments: { removeAll: true}, //对注释的处理
              normalizeUnicode: false // 建议false,否则在使用unicode-range的时候会产生乱码
          }]
        },
        canPrint: true  // 是否打印编译过程中的日志
      })
    ]
    

    这个插件不支持热更新 最好在生产环境中使用,开发环境可以用 style-lodader,使用了这个插件会影响webpack内置的对js的压缩失效,需要重新配置

    optimization: {
         minimizer: [
            new UglifyJsPlugin({
             cache: true, // Boolean/String,字符串即是缓存文件存放的路径
             parallel: true, // 启用多线程并行运行提高编译速度
             sourceMap: true
            /*
              uglifyOptions: {
                output: {
                  comments: false  // 删掉所有注释
                },
                compress: {
                    warning: false, // 插件在进行删除一些无用的代码时不提示警告
                    drop_console: true // 过滤console,即使写了console,线上也不显示
                }
              } */
           }),
           new OptimizeCSSAssetsPlugin({})
         ]
       }
    

    css 自动添加前缀 安装 npm install postcss-loader autoprefixer --save-dev
    可以创建postcss的配置文件 postcss.config.js

    module.exports = {
          plugins:[
             require('autoprefixer')
          ]
        }
    
    
    //webpack的css的配置中需要加上postcss-loader
     {
                        test: /\.(sa|sc|c)ss$/,
                        use:[
                            {
                                loader: MiniCssExtractPlugin.loader,
                            },
                            {
                                loader: 'css-loader',
                                options:{
                                    importLoaders: 1
                                }
                            },
                            "postcss-loader",
                            "sass-loader",
                        ],
                        //排除在外不需要处理的部分
                        exclude: /node_modules/
                    },
    

    还可以添加 .browserslistrc 来覆盖浏览器
    这个配置主要是配置 autoprefixer和 @babel/preser-env来使用
    npm 配置地址 https://www.npmjs.com/package/browserslist

    //  常用配置
    "last 1 version",
        "> 1%",
        "IE 10"
    

    常用的插件

    1.HtmlWebpackPlugin
    npm install --save-dev html-webpack-plugin
    帮助我们生成html文件
    基本用法

    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var path = require('path');
    
    var webpackConfig = {
     entry: 'index.js',
     output: {
       path: path.resolve(__dirname, './dist'),
       filename: 'index_bundle.js'
     },
     plugins: [new HtmlWebpackPlugin({
          title:'OutpuManagement'//生成html文件的标题
          template: 'src/index.html'//可以使用固定的模板要是绝对路径才可以
          
    
        })]
    };
    

    如果你有多个 webpack 入口点, 他们都会在生成的HTML文件中的 script 标签内。 如果你有任何CSS assets 在webpack的输出中(例如, 利用ExtractTextPlugin提取CSS), 那么这些将被包含在HTML head中的<link>标签内。

    详细配置参数见 https://github.com/jantimon/html-webpack-plugin#configuration

    2.clean-webpack-plugin
    每次打包前帮我自动删除dist文件夹
    npm install clean-webpack-plugin --save-dev

    const CleanWebpackPlugin = require('clean-webpack-plugin');
    
    module.exports = {
       ...//只展示关键代码
        plugins: [
         new CleanWebpackPlugin(['dist']),
          new HtmlWebpackPlugin({
            title: 'Output Management'
          })
        ],
      };
    

    以上用法是旧版本的用法,新版本的clean-webpack-plugin有所改变

    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    
    module.exports = {
       ...//只展示关键代码
        plugins: [
         new CleanWebpackPlugin(),
          new HtmlWebpackPlugin({
            title: 'Output Management'
          })
        ],
      };
    

    解构或渠道CleanWebpackPlugin

    source map

    使用source map映射打包后的代码在源代码错误地方

    module.exports = {
        entry: {
          app: './src/index.js',
          print: './src/print.js'
        },
       devtool: 'cheap-module-eval-source-map',
       //常用配置
        plugins: [
          new CleanWebpackPlugin(['dist']),
          new HtmlWebpackPlugin({
            title: 'Development'
          })
        ],
        output: {
          filename: '[name].bundle.js',
          path: path.resolve(__dirname, 'dist')
        }
      };
    

    详细配置参数地址 https://www.webpackjs.com/configuration/devtool/

    babel

    简单的说就是把浏览器还不能够识别的高级语法解析成浏览器可以正常识别的语法
    webpack关于babel-loader https://webpack.js.org/loaders/babel-loader/#root
    npm install -D babel-loader @babel/core @babel/preset-env
    基本配置

    module: {
      rules: [
        {
          test: /\.m?js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env']
            }
          }
        }
      ]
    }
    

    也可以在根目录创建按.babelrc文件

    {
      "presets": [
        ["@babel/preset-env",{
            {
            "useBuiltIns": "usage",
            "corejs": 3
          }
        }]
      ],
      plugins:[
      //相关插件的配置
      ]
    }
    
    • 核心库 @babel/core 必须安装 作用:把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理。有些新语法在低版本 js 中是不存在的,如箭头函数,rest 参数,函数默认值等,这种语言层面的不兼容只能通过将代码转为 ast,分析其语法后再转为低版本 js。
    • @babel/preset-env 预设插件,可以根据配置智能转化所需要的配置场景语法,只能转化语法例如(let const),一些内置的方法不能成功转化 例如(map),如果需要进一步转化内置对象和实例方法,就需要用polyfill,“useBuiltins”,这个参数可以帮我们控制preset-env使用什么方式帮我们导入polyfill的核心
      1. entry 入口导入方式 //覆盖面积广 包很大 会参考目标浏览器(browserslist)
      2. useage 会参考目标浏览器(browserslist) 和 代码中所使用到的特性来按需加入 polyfill 此时还需要corejs插件的支持
      3. false 默认不导入
    • babel-loader 处理es6语法,解析成浏览器可识别的语法

    npm i -D @babel/plugin-transform-runtime @babel/runtime @babel/runtime-corejs3

    • @babel/plugin-transform-runtime

    • @babel/runtime

    • @babel/runtime-corejs3(corejs3)

      这种方式会借助 helper function 来实现特性的兼容, 并且利用 @babel/plugin-transform-runtime 插件还能以沙箱垫片的方式防止污染全局, 并抽离公共的 helper function , 以节省代码的冗余
      也就是说 @babel/runtime 是一个核心, 一种实现方式, 而 @babel/plugin-transform-runtime 就是一个管家, 负责更好的重复使用@babel/runtime
      @babel/plugin-transform-runtime 插件也有一个 corejs 参数需要填写

      {
      "presets": [
        ["@babel/preset-env"]
      ],
      "plugins": [
        [ 
          "@babel/plugin-transform-runtime", {
            "corejs": 3
          }
        ]
      ]
      }
      

      entry和@babel/runtime不能一起使用,否则包会很大
      useage和@babel/runtime可以一起使用

    总结:
    1.@babel/preset-env 拥有根据 useBuiltIns 参数的多种polyfill实现,优点是覆盖面比较全(entry), 缺点是会污染全局, 推荐在业务项目中使用
    entry 的覆盖面积全, 但是打包体积自然就大,
    useage 可以按需引入 polyfill, 打包体积就小, 但如果打包忽略node_modules 时如果第三方包未转译则会出现兼容问题
    2.@babel/runtime在 babel 7.4 之后大放异彩, 利用 corejs 3 也实现了各种内置对象的支持, 并且依靠 @babel/plugin-transform-runtime 的能力,沙箱垫片和代码复用, 避免帮助函数重复 inject 过多的问题, 该方式的优点是不会污染全局, 适合在类库开发中使用
    上面 1, 2 两种方式取其一即可, 同时使用没有意义, 还可能造成重复的 polyfill 文件

    静态资源拷贝

    npm 地址 https://www.npmjs.com/package/copy-webpack-plugin
    copy-webpack-plugin 主要实现对已有的文件拷贝,不需要webpack打包的文件
    npm install copy-webpack-plugin --save-dev

    const CopyWebpackPlugin = require('copy-webpack-plugin');
    plugins: [
        new CopyWebpackPlugin([
          {
              from: 'public/*.js',  //从public文件下把所有的js文件  当然,我们需要在模板html中手动引用文件
              to: path.resolve(__dirname, 'dist'), // 拷贝到dist目录下
              flatten: true, //设置为 true,那么它只会拷贝文件,而不会把文件夹路径都拷贝上
              ignore: ['1.js'], // 过滤掉自己不想拷贝过去的文件
          },
          //还可以继续配置其它要拷贝的文件
      ])
      ],
    

    ProvidePlugin

    webpack 内置的模块 https://www.webpackjs.com/plugins/provide-plugin/
    自动加载模块,而可以不用到处用import和require

    new webpack.ProvidePlugin({
      identifier: 'module1',
      // ...
    })
    // 或者
    new webpack.ProvidePlugin({
      identifier: ['module1', 'property1'],
      // ...
    })
    
    const webpack = require('webpack');
    module.exports = {
        //...
        plugins: [
            new webpack.ProvidePlugin({
                React: 'react',
                Component: ['react', 'Component'],
                Vue: ['vue/dist/vue.esm.js', 'default'],
                $: 'jquery',
                _map: ['lodash', 'map']
            })
        ]
    }
    // 每次使用的时候就不需要在引入文件可以直接使用,不推荐大量使用,不利于后面的人维护代码
    

    按需加载

    简单的说就是需要的时候才加载,不需要一开始就加载,有利于首屏优化

    document.getElementById('btn').onclick = function() {
       import('./handle').then(fn => fn.default());
    }
    

    代码分割 splitChunks

    webpack地址 https://webpack.js.org/plugins/split-chunks-plugin/#root

    默认配置

    module.exports = {
      //...
      optimization: {
        splitChunks: {  initial 同步  all 所有的
          chunks: 'async',// 默认支持异步代码分割 import()
          minSize: 30000, // 文件超过30k就会抽离
          minRemainingSize: 0,
          maxSize: 0,
          minChunks: 1, // 最少模块引用了一次
          maxAsyncRequests: 6, // 最多5个请求
          maxInitialRequests: 4, // 最多首屏加载4个请求
          automaticNameDelimiter: '~',
          cacheGroups: { //缓存组
            defaultVendors: { 
              test: /[\\/]node_modules[\\/]/,
              priority: -10 //优先级
            },
            default: {
              minChunks: 2,
              priority: -20,//优先级
              reuseExistingChunk: true
            }
          }
        }
      }
    };
    

    删除无用css样式

        npm i purify-webpack purify-css -D
        const glob = require('glob')
        const PurifyCssPlugin = require('purifycss-webpack')
        plugins: [
          new PurifyCssPlugin ({
              paths: glob.sync(path.join(__dirname, '/*.html'))
          })
        ]
    

    cdn加载文件

    Externals     https://webpack.js.org/configuration/externals/#root
    在html文件引入不想被打包的cdn 例如jquery
    
    <script
      src="https://code.jquery.com/jquery-3.1.0.js"
      integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
      crossorigin="anonymous">
    </script>
    

    webpack配置

    module.exports = {
      //...
      externals: {
        jquery: 'jQuery'
      }
    };
    

    这个时候我们就可以用es6语法或者commonJS或者AMD规范引用

    import $ from 'jquery';
    
    $('.my-element').animate(/* ... */);
    

    只适用于很长时间不会更改的库

    Tree-shaking && Scope-Hoisting

    tree-shaking

    地址 https://www.webpackjs.com/guides/tree-shaking/#%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E9%80%9A%E7%94%A8%E6%A8%A1%E5%9D%97
    webpack在mode为production,并且使用es6语法importexport会自动删除没有使用的代码,
    如果使用了 optimize-css-assets-webpack-plugin这个插件会导致原本webpack内置的对js的压缩失效, 这个时候需要自己手动去配置对js的压缩才会生效UglifyJSPlugin
    tree-shaking的必要条件

    • 使用 ES2015 模块语法(即 import 和 export
    • 在项目 package.json 文件中,添加一个 "sideEffects" 入口。
    • 引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。

    scope-hoisting (作用域提升)

    webpack3以后的内置插件,必须使用es6语法引入的文件才会生效
    ```
    const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');

    module.exports = {
      plugins: [
        // 开启 Scope Hoisting
        new ModuleConcatenationPlugin(),
      ],
    };
    ```
    

    DillPlugin && DillReferencePlugin

    动态加载

    resolve

    include/exclude

    happypack

    相关文章

      网友评论

          本文标题:webpack从基础到优化

          本文链接:https://www.haomeiwen.com/subject/sqxwohtx.html