webpack入门

51 篇文章 2 订阅
35 篇文章 3 订阅

目录

一、什么是webpack

二、五个核心概念

三、Mode模式分 development 和 production

四、性能优化配置

4.1、开发环境性能优化

4.2、生产环境性能优化 


一、什么是webpack

Webpack是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源

二、五个核心概念

  • Entry 入口指示 Webpack以那个文件为入口起点开始打包,分析构建内部依赖图
  • Output 输出指示webpack打包后的资源bundles输出到哪里去,以及如何命名
  • Loader 让webpack能够去处理哪些非js文件
  • Plugins 插件可以用于执行范围更广的任务,插件的范围包括:从打包优化和压缩。一直到重新定义环境中的变量等
  • Mode 模式mode指示webpack使用相应模块的配置

三、Mode模式分 development 和 production

development

webpack.config.js 配置:

//开发环境的配置 :能让代码运行起来
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build'),
    },
    module: {
        rules: [
            //loader配置
            {
                test: /\.less$/,
                use: ['style-loader', 'css-loader', 'less-loader']
            },
            {
                //处理css资源
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                //处理图片资源是es6解析
                test: /\.(jpg|png|gif)$/,
                loader: 'url-loader',
                options: {
                    limit: 8 * 1024,
                    name: '[hash:10].[ext]',
                    //关闭es6模块化 因为两个模块解析不一样所以关闭es6模版
                    esModule: false,
                    outputPath:'images'
                }
            },
            {
                //处理html中img资源是commmjs解析
                test: /\.html$/,
                loader:'html-loader'
            },
            {
                //处理其他资源
                exclude: /\.(html|css|js|less|jpg|png|gif)/,
                //file-loader与url-loader的区别:前者就是将文件打包,后者还带有限制条件 比如limit
                loader: 'file-loader',
                options: {
                    name: '[hash:10].[ext]',
                    outputPath:'media'
                }
            }
        ]
    },
    plugins: [
        //plugins的配置
        new HtmlWebpackPlugin({
            template:'./src/index.html'
        })
    ],
    mode:'development',
    devServer: {
        contentBase: resolve(__dirname, 'build'),
        compress: true,
        port: 3000,
        open:true
    }
}

production

webpack.config.js 配置:

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [...commonCssLoader]
      },
      {
        test: /\.less$/,
        use: [...commonCssLoader, 'less-loader']
      },
      /*
        正常来讲,一个文件只能被一个loader处理。
        当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
          先执行eslint 在执行babel
      */
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'usage',
                corejs: {version: 3},
                targets: {
                  chrome: '60',
                  firefox: '50'
                }
              }
            ]
          ]
        }
      },
      {
        test: /\.(jpg|png|gif)/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          outputPath: 'imgs',
          esModule: false
        }
      },
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(js|css|less|html|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

四、性能优化配置

4.1、开发环境性能优化

优化打包构建速度 HMR

HMR: hot module replacement 热模块替换 / 模块热替换

作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)

          极大提升构建速度

 //开启HMR功能 
 //当修改了webpack配置,新配置想要生效,必须重启webpack服务
 hot:true
  • 样式文件:可以使用HMR功能 :因为style-loader内部实现了~
  • js文件:默认不能使用HMR功能 -->需要修改js代码,添加支持HMR功能的代码 【HMR功能对js的处理,只能处理非入口文件的配置,对入口文件中引入的其他文件,入口文件不能使用】
if (module.hot) {
    //一旦module.hot =true ,说明开启了HMR功能 ---让HMR功能代码生效
    module.hot.accept('./print.js', function () {
        //方法会监听 print.js文件的变化,一旦发生变化,其他模块默认不会重新打包构建
        //会执行后面的回调函数
        print();
    })
}
  • html文件:默认不能使用HMR功能,同时会导致问题--->html文件不能热更新了~(不用做HMR功能)【解决:修改entry入口,将html文件引入】
entry: ['./src/js/index.js','./src/index.html'],

优化代码调试 source-map 

source-map:一种提供源代码到构建代码映射技术(如果构建后代码出错了,通过映射可以追踪源代码错误)

 devtool:'eval-source-map'

devtool :里面的参数配置还有:[inline-|hidden-|eval-] [nosources-] [cheap-[module-]]source-map

* 内联和外部的区别

 1、外部生成了文件,内联没有

 2、内联构建速度更快

  •  source-map:外部

提示错误代码准确信息 和源代码的错误位置

  • inline-source-map :内联

只生成一个内联source-map 【built.js里面】

提示错误代码准确信息 和源代码的错误位置

  • hidden-source-map :外部

提示错误代码错误原因,但是没有错误位置

不能追踪源代码错误,只能提示到构建后代码的错误位置

  • eval-source-map :内联

每一个文件都生成source-map 都在eval中

提示错误代码准确信息 和源代码的错误位置

  • nosources-source-map:外部

提示错误代码准确信息 但是没有任何源代码信息

  • cheap-source-map:外部

错误代码准确信息 和 源代码的错误位置

只能精确到行

  • cheap-module-source-map:外部

错误代码准确信息 和 源代码的错误位置

module会将loader的source map加入

使用场景

* 开发环境:速度快,调试更友好

速度快(eval>inline>cheap>...)

eval-cheap-source-map

eval-source-map

调试更友好

source-map

cheap-module-source-map

cheap-source-map

最终的结果:eval-source-map / eval-cheap-module-source-map

* 生产环境:源代码要不要隐藏?调试要不要更友好

内联会让代码体积变得非常大 所以生产环境下不用内联

隐藏源代码:

nosources-source-map 全部隐藏

hidden-source-map 只隐藏源代码,会提示构建后代码错误信息

调试最好用:

source-map

速度更快用:

cheap-module-source-map

4.2、生产环境性能优化 

 优化打包构建速度

oneOf :一个文件被一个loader处理好了,下面的loader就不用看了

        //以下loader只会匹配一次 让loader处理的性能更好
        //注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ]
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]

 babel缓存

 //开启babel缓存
 //第二次构建时,会读取之前的缓存
 cacheDirectory:true,
output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },

文件资源缓存: 让代码上线运行缓存更好使用

  • hash:每次js和css同时使用会产生一个唯一hash值

问题:因此js和css同时使用一个hash值。如果重新打包,会导致所有缓存失效,(可能却只改动了一个文件)

  • chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样

问题:因此js和css同时使用一个hash值,因为css是在js中引入的,所以同属于一个chunk

  • contenthash:根据文件的内容生成的hash值。不同文件hash值一定不一样

多进程打包

优点:开启多进程打包
缺点:进程开启是有时间的 进程开启大概为600ms,进程通信也有开销
用于使用到一些运用消耗时间比较长的,js
thread-loader 来针对  eslint-loader进行优化
如果构建时间很短,使用多进程打包,会变的慢 

{ 
   loader: 'thread-loader',
   options: {
          workers:2 //进程2个
   }
 },

让某些库不打包

dll

使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
只需要打包一次

webapck.config.js:

plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~
    new webpack.DllReferencePlugin({
      manifest: resolve(__dirname, 'dll/manifest.json')
    }),
    // 将某个文件打包输出去,并在html中自动引入该资源
    new AddAssetHtmlWebpackPlugin({
      filepath: resolve(__dirname, 'dll/jquery.js')
    })
  ],

webpack.dll.js:

/*
  使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
    当你运行 webpack 时,默认查找 webpack.config.js 配置文件
    需求:需要运行 webpack.dll.js 文件
      --> webpack --config webpack.dll.js
*/

const { resolve } = require('path');
const webpack = require('webpack');

module.exports = {
  entry: {
    // 最终打包生成的[name] --> jquery
    // ['jquery'] --> 要打包的库是jquery
    jquery: ['jquery'],
  },
  output: {
    filename: '[name].js',
    path: resolve(__dirname, 'dll'),
    library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字
  },
  plugins: [
    // 打包生成一个 manifest.json --> 提供和jquery映射
    new webpack.DllPlugin({
      name: '[name]_[hash]', // 映射库的暴露的内容名称
      path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
    })
  ],
  mode: 'production'
};

externals

 :拒绝一个包被打包,如果要用这个包的话,就在html里面引进这个包,可以提高这个打包速度
该程序用外联引入就使用externals打包

//先拒绝jquery打包
externals: {
        //忽略库名 ---npm包名  拒接jquery这个包被打包进来
        jquery:'jQuery'
    }
//再在html里面引进jquery就可以使用但不打包 CDN连接
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>

优化代码运行的性能 

tree shaking 

  tree shaking:去除应用程序中没有使用过的无用代码

  前提:1、必须使用es6模块化 2、开启production环境

  作用:减少代码体积:比如说两个js文件但是只使用了一个宁一个就会被树摇掉

  在package.json中配置

  “sideEffects”:false 所有代码都没有副作用(都可以进行tree shaking)

  问题:可能会吧css | @babel/polyfill 文件干掉

  解决方法:"sideEffects":["*.css","*.less"]写在里面的就不会被tree shaking干掉

code split 

代码分割 就可以进行按需加载,也就是并行加载

三种方案:

1⃣️ :多入口optimization


   //可以将node_modules中代码单独打包一个chunk最终输出
   //自动分析多入口chunk中,有没有公共的文件(文件大小是有要求的),如果有,会打包成单独的一个chunk

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

2⃣️: import 导入的文件 

import导入的文件都可以按需加载,都可以拆分打包

import(/* webpackChunkName:'test' */'./test')
  .then(({mul,count}) => {
    //文件加载成功
    // eslint-disable-next-line
    // console.log(result);
     console.log(mul(1,2));
  })
  .catch(() => {
    // eslint-disable-next-line
    console.log('加载文件失败~');
  })

3⃣️ :多入口拆分 

一个入口拆分成一个chunk

懒加载/预加载 

document.getElementById('btn').onclick = function () {
  // console.log(mul(1, 2));
  //懒加载:当文件需要使用时才加载
  //懒加载 当前test.js不会被加载,只有在点击按钮之后才会加载test.js[文件会分割]
  // import(/*webpackChunkName:'test' */'./test').then(({ mul }) => {
  //   console.log(mul(2, 2));
  // })


  //预加载prefetch:会在使用之前,提前加载js文件,
  //正常加载可以认为是并行加载(同一时间加载多个文件)
  //预加载prefetch:等其他资源加载完毕,浏览器空闲了,在偷偷加载资源
  import(/*webpackChunkName:'test',webpackPrefetch:true */'./test').then(({ mul }) => {
    console.log(mul(2, 2));
  })
}

PWA 

渐进式网络开发形式

 new WorkboxWebpackPlugin.GenerateSW({
       /**
        * 1、帮助serviceworker快速启动
        * 2、删除旧的 serviceworker
        * 
        *  文件~
        */
      clientsClaim: true,
      skipWaiting:true
    })

index.js

/**
 * 1、eslint不认识 window、navigater全局变量
 *   解决:需要修改package.json中eslintConfig配置
 *     "env":{
 *       "browser":true //支持浏览器端全局变量
 *     }
 * 2、sw代码必须运行在服务器上
 *   ---nodejs (直接写服务器)
 *   --->下载包 运行指令
 *      npm i serve -g
 *      serve -s build 启动服务器,将bulid目录下所有资源作为静态资源暴露出去
 */

// 注册兼容性问题 
// 处理兼容性问题

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(() => {
        console.log('sw注册成功了');
      })
      .catch(() => {
        console.log('sw注册失败了');
      });
  });
}

以上就是webpack的基础配置啦

更多配置详解请查阅:概念 | webpack 中文文档 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奶茶丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值