webpack的基础以深入研究

254 阅读10分钟

image.png

为什么我们需要 Webpack

回答这个问题,可以和还没有 Webpack、没有构建工具时对比一下,就能明显地感觉出来了。这里就来列举一下不使用构建工具时的痛点。

web 开发时调用后端接口跨域,需要其他工具代理或者其他方式规避。 改动代码后要手动刷新浏览器,如果做了缓存还需要清缓存刷新。 因为 js 和 css 的兼容性问题,很多新语法学习了却不能使用,无论是开发效率和个人成长都受影响。 打包问题。需要使用额外的平台如 jekins 打包,自己编写打包脚本,对各个环节如压缩图片,打包 js、打包 css 都要一一处理。 ......

定义

webpack是一种前端资源构建工具,一个静态模块打包器

image.png

image.png

五个核心概念+devSever自动化

entry

入口指示webPack以哪个文件为入口起点开始打包,分析构建内部依赖图

output

输出指示webpack打包后的资源bundles输出到哪里去,以及如何命名

Loader

让webpack能够处理那些非js文件如:sass img 等 【webpack本身只能识别js】

Plugins【插件】

从打包优化和压缩,一直到重新定义环境中的变量等

image.png

Mode【模式】

image.png

devSever开发服务器【自动化】

  • 开发服务器devSever:用来自动化(自动打开浏览器,自动刷新浏览器,自动编译)
  • 特点:自会在内存中打包编译,不会有任何输出
  • 下载命令 : npm i webpack-dev-server -D
  • 启动devSever命令:npx webpack-dev-server

image.png

注意:webpac5.0起要加 target:'web'才能自动刷新

image.png

webpack loader区别

  • webpack loader是用来加载文件的,webpack plugin是用来扩展功能的。

  • loader主要是用来加载一个个文件的,比如它可以加载js文件并把js文件转译成低版本浏览器可以支持的js文件;也可以用来加载css文件,可以把css文件变成页面上的style标签;还可以加载图片文件,可以对文件进行优化。

  • plugin是用来加强webpack功能的,比如HTML webpack plugin是用来生成一个html文件的;再比如mini css extract plugin是用来抽取css代码并把它变成一个css文件的。

demo案例

迭代1:打包静态资源js和css

1:全局安装【webpack和webpack-cli】 npm i webpack webpack-cli -g【安装一次即可,可以忽略】

2: 项目文件下创建项目配置项 npm init

3:本地安装 npm i webpack webpack-cli -D 【-D把项目开发依赖下载到本地】

4:下载css-loader 和style-loader npm i css-loader style-loader -D

5:执行打包 webpack

代码目录结构

image.png

image.png

image.png

image.png

迭代2:sass 文件打包

6: 下载包 npm i sass-loader sass -D

7: 添加loader配置

image.png

image.png

image.png

迭代3:打包html

  • loader : 1下载 2:使用【配置loader】
  • plugins : 1 下载 2:引入 3:使用

8:下载打包 html 包 命令: npm i html-webpack-plugin -D

9: 添加配置 plugin

image.png image.png

10:打包运行

image.png

迭代4 :打包图片资源

11:把图片引入css和引入html中

image.png image.png

12:配置plugins

命令:npm i url-loader file-loader -D 和 npm i html-loader -D

image.png

image.png

loader 下面的limit

image.png

迭代4-1: 打包其他资源【如字体文件,暂时忽略】

13 备好字体图标

14 配置plugins用到exclude[正则]

 // 打包其他资源(除了html/js/css 资源以外的资源)
      {
        exclude:/\.(css|html|js)$/,
        loader:'file-loader',
        options:{
          name:'[hash:10].[ext]'
        }
      }
      

迭代5:devSever 用来自动化【自动编译,自动打开浏览器,自动刷新浏览器】看商品devServer开发服务器

迭代1~5的webpack.config.js基本配置 代码如下

/*
 * @Descripttion: 
 * @version: 
 * @Author: xiewutao
 * @Date: 2021-05-10 14:04:54
 * @LastEditors: sueRimn
 * @LastEditTime: 2021-05-12 11:34:24
 * webpack.config.js webpack配置文件
 * 
 * 作用:webpack干哪些活(当你运行webpack指令时,会加载里面的配置)
 * 所有构建工具都是基于nodejs  平台运行的 模块化,默认是才有common.js  如:module.exports
 * 
 *  loader : 1下载  2:使用【配置loader】
 *  plugins : 1 下载  2:引入  3:使用
 * 
 * 
 * //  *执行的命令总结
 * npm init
 * npm i webpack webpack-cli -D
 * npm i style-loader sass -D
 * npm i sass-loader sass -D
 * npm i url-loader -D
 * npm i file-loader -D
 * npm i html-loader -D
 * npm i html-webpack-plugin -D
 * npm i webpack-dev-server -D
 * npx webpack-dev-server 启动
 * webpack  运行
 * 
 * npm uninstall webpack-li
 * npm i webpack-cli@3.3.12 -D
 * 
 */
// resolve 是用来拼接绝对路径的方法
const {resolve} =require('path');
const HtmlWebpackPlugin=require('html-webpack-plugin');
module.exports={
  entry:'./src/index.js',
  output:{
    filename:'built.js',
    // __dirname  node.js变量,代表当前文件的目录绝对路径【根目录】
    path:resolve(__dirname,'build')
  },

  // loader 的配置
  module:{
    // 详细配置
    rules:[
      {
        test:/\.css$/, // 匹配哪些文件
        use:[ // use 使用哪些loader处理【注意:uses 这个数组中,执行的顺序是:是倒叙的,即是从右到左,从下到上  执行css-loaderd 再到style-loader】 // 命令下载两个包 npm i css-loader style-loader -D
          'style-loader', // 创建style标签,将js中的样式资源插入进行,添加到header中生效
          'css-loader'  // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
        ]
      },

      {
        test:/\.scss$/,
        use:[ 
          'style-loader', 
          'css-loader',  
          'sass-loader', // 将scss文件编译css文件 ,需要下载 scss-laoder 和scss两个包   //  npm i sass-loader sass -D
        ]
      },

      // 处理css里面的引入图片资源,,但是处理不了html里图片,所以由下面的test:/\.html$/ 处理
      {
        test:/\.(jpg|png|gif)$/,
        loader:'url-loader',  //  下载包命令:npm i url-loader file-loader -D     这里的use和loader区别:use是数组多个,loader只有一个
        options:{
          // 图片大小小于8kb,就会转base64处理
          // 优点:减少请求的数量【减轻服务器的请求压力】
          //缺点:图片的体积会更大(文件请求速度更慢)
          limit:8*1024,
          // 为了解决html img中src [object Module],所以引入下面的esModule
          // 问题:因为url-loader默认是使用es6模块化解析,而html-loader引入图片是commonjs
          // 出现问题:html img中src [object Module]
          // 解决:关闭url-loader的es6模块化 ,使用comonjs解析
          esModule:false,

          // 给图片重新命名【html中图片】
          // [hash:10] 取hash的前10位
          // .[ext]取文件原来的扩展名
          name:'[hash:10].[ext]',
          outputPath: "images/",   //输出图片放置的位置和src 图片目录一致
          publicPath: './images', //html的img标签src所指向图片的位置,与outputPath一致


        }
      },

      // 处理html里面的引入图片资源
      {
        test:/\.html$/,
        loader:'html-loader'  // 处理html文件的图片,(负责引入img,从而被url-loader进行处理) 命令:  npm i html-loader -D
      },

      // 打包其他资源(除了html/js/css 资源以外的资源)
      // {
      //   exclude:/\.(css|html|js)$/,
      //   loader:'file-loader',
      //   options:{
      //     name:'[hash:10].[ext]'
      //   }
      // }


    ]
  },

  plugins:[
    // 命令: npm i html-webpack-plugin -D
    // 功能:默认创建一个空的html,自动引入打包输出的所有资源(js/css)  所以 在src/index不用引入js文件,打包后 build/index.html   plugins插件自动有引入
    new HtmlWebpackPlugin(
      {
      template:'./src/index.html'//复制 ./src/index.html ,自动引入打包输出的所有资源(js/css)
     }
    )

  ], // 插件

  // 开发服务器devSever:用来自动化(自动打开浏览器,自动刷新浏览器,自动编译)
  // 特点:自会在内存中打包编译,不会有任何输出
  // 下载命令 : npm i webpack-dev-server -D
  // 启动devSever命令:npx webpack-dev-server
  devServer:{
    // 项目构建后路径
    contentBase:resolve(__dirname,'build'),
    // 启动gzip压缩
    compress:true,
    // 端口号
    port:3000,
    // 自动打开浏览器
    open:true
  },

  mode:'development'  //模式:development 开发模式  production生产模式
}

迭代6:提取css文件

15:提取css插件命令:npm i mini-css-extract-plugin -D

16:plugin 的配置

image.png

image.png

迭代7:css兼容的处理 如:flex-box的前缀

17: 插件的命令:npm i postcss-loader postcss-preset-env -D

18 配置:代码如19

image.png

image.png

迭代8 压缩css

命令 npm i optimize-css-assets-webpack-plugin -D

20配置

image.png

迭代 9 :eslint js语法检查和自动修复

21 命令:插件 npm i eslint-loader eslint-config-airbnb-base eslint-plugin-import -D

21 : 使用配置 loader

image.png

22:规则 eslint-disable-next-line 的意思

image.png

迭代10:js兼容性处理【es6处理】

命令:

      js兼容性处理: babel-loader @babel/core @babel/preset-env
      1:基本js兼容性处理----》 命令:npm i @babel/present-env -D
        问题:只能转基本语法如:const let  箭头函数等  ,但是promise不能转换
      2:全部js兼容性处理----》命令 npm i @babel/polyfill -D
        问题:我只要解决部分的兼容性问题,但是全部引如所有的兼容性代码,体积太大了
      3:需要按需加载  -----》 命令  npm i core-js -D

配置

image.png

迭代 11 js压缩和html压缩

js压缩

把开发环境改成生产环境即可

直接把 mode:production

image.png

html压缩

image.png

   new HtmlWebpackPlugin(
      {
      template:'./src/index.html',//复制 ./src/index.html ,自动引入打包输出的所有资源(js/css)
      minify:{ //压缩HTML文件
        collapseWhitespace:true,  //删除空白符与换行符
        removeComments:true,  //移除HTML中的注释
      }
     
     }
    ),

迭代12 @的作用

image.png

image.png

--------webpack-总结生产环境基本配置-------------

/*
 * @Descripttion: 
 * @version: 
 * @Author: xiewutao
 * @Date: 2021-05-12 09:46:28
 * @LastEditors: sueRimn
 * @LastEditTime: 2021-07-02 16:26:58
//  *执行的命令
 * npm init
 * npm i webpack webpack-cli -D
 * npm i style-loader sass -D
 * npm i sass-loader sass -D
 * npm i url-loader -D
 * npm i file-loader -D
 * npm i html-loader -D
 * npm i html-webpack-plugin -D
 * npm i webpack-dev-server -D
 * npx webpack-dev-server 启动
 * webpack  运行
 * 
 * npm uninstall webpack-li
 * npm i webpack-cli@3.3.12 -D
 * 
 * 
 */

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');
process.env.NODE_ENV ='production';  // 修改开发环境

const commonLoaderCss=[
  MiniCssExtractPlugin.loader,//这个loader取代style-loader 。 作用提取jsz中的css成单独文件
  'css-loader',
  {
    loader:'postcss-loader',
    options:{
      postcssOptions:{
        ident:"postcss",
            plugins:[
            require("postcss-preset-env")()
        ]
      }
    }
  },
]

module.exports={
  entry:'./src/js/index.js',
  output:{
    filename:'js/built.js',
    // __dirname  node.js变量,代表当前文件的目录绝对路径【根目录】
    path:resolve(__dirname,'build')
  },
  module:{
    // 配置
    rules:[
      {
        test:/\.css$/,
        use:[
          ...commonLoaderCss
        ]
      },
      {
        test:/\.scss$/,
        use:[
          ...commonLoaderCss,
         'sass-loader',
          
        ] 
      },
      // css图片资源打包
      {
        test:/\.(jpg|png|gif)$/,
        loader:'url-loader',
        options:{
          limit:8*1024,
          name:'[hash:10].[ext]',
          esModule:false,
          outputPath:"../../images"
        }
      },
      {
        test:/\.js$/,
        exclude:/node_modules/,//排除node_moudles
        loader:'babel-loader',
        options:{
          //预设:指示babel做怎么样的兼容性处理
          presets:[[
            '@babel/preset-env',
            {
              //按需加载
              useBuiltIns:'usage',
              // 指定core-js版本
              corejs:{
                version:3
              },
              // 指定兼容性做到 那个版本
              targets:{
                chrome:'60',
                firefox:'60',
                ie:'9',
                safari:'17'
              }
            }
          ]]
        }
      },
      // html中图片资源
      {
        test:/\.html$/,
        loader:'html-loader',
      },
    ]
  },
  plugins:[
    new MiniCssExtractPlugin({
      filename:'css/built.css' // 提取的css文件 目录和文件
    }),
    // 压缩css
    new OptimizeCssAssetsWebpackPlugin({
    }),
    new HtmlWebpackPlugin(
      {
      template:'./src/index.html',//复制 ./src/index.html ,自动引入打包输出的所有资源(js/css)
      minify:{ //压缩HTML文件
        collapseWhitespace:true,  //删除空白符与换行符
        removeComments:true,  //移除HTML中的注释
      }
     }
    ),
  ],
  mode:"production",
   target:'web',//webpack5.0 使用webpack-dev-server时
  devServer:{
    contentBase:resolve(__dirname,'build'),
    compress:true,
    port:3000,
    open:true,
  }
}

----------------第二部分开始 start----------------------

webpack性能优化

开发环境的性能优化

A: HMR(hot module replacement) : 热模块替换 、 模块热替换

作用:一个模块发生变化,只会重新打包这个模块(而不是打包所有模块) 提高构建的速度

 HMR代码示例:

```
  mode:"production",
  target:'web',//webpack5.0 使用webpack-dev-server时
  devServer:{
    contentBase:resolve(__dirname,'build'),
    compress:true,
    port:3000,
    open:true,
    hot:true ,// 开启HMR功能  热模块更新提高构建速度,刚修改必须重启才生效
  }
```  


1:样式文件:可以用HMR功能,因为style-loader内部实现了

2:js 文件: 默认不能使用HMR功能 : 除非优化非入口js文件,

  解决,在到改动的代码上判断  
```
    if(module.hot){  
          module.hot.accept('非入口文件的js路径',function(){  
             重新调用  
          })  
      }  
```

3: html文件:默认不能使用HMR功能,同时会导致问题:html文件不能热更新了

 解决: 修改entry入口,将html文件引入  entry:['./src/js/index.js',''./src/index.html'],  
 拓展:由于html 只有一个没有必要做热更新了  

B: source-map (构建后,代码出错,踪源码错误位置)

作用:一种提供源代码到构建后代码映射技术(也就是构建后,代码出错,通过映射可以追踪源码错误位置)

image.png

代码示例:devtool

  mode:"production",
  target:'web',//webpack5.0 使用webpack-dev-server时
  devServer:{
    contentBase:resolve(__dirname,'build'),
    compress:true,
    port:3000,
    open:true,
    hot:true ,// 开启HMR功能  热模块更新提高构建速度,刚修改必须重启才生效
  },
  devtool:'source-map' // 开发工具

source-map的参数分别是:

[inline -|hidden-|eval-][nosources-][cheap-[module-]]source-map

内联和外部的区别:1外部生成文件,内联没有 2:内联的构建速度快

0: source-map:外部
1:错误代码准确信息和源代码的错误位置
1: inline-source-map:内联
1: 只生成一个内联source-map  
2:错误代码准确信息和源代码的错误位置
2: hidden-source-map:外部
1:错误代码的原因,但是没有错误的位置
2:不能追踪源代码错误,只能提示到构建后的代码错误位置
3:eval-source-map 内联
1:每个文件都生成内联source-map 都在eval
2:错误代码的准确信息和原代码的错误位置

image.png

4:nosources-source-map 外部 【发布时候,建议开启这个或者关闭】
1:错误代码的准确信息,但是没有任何源代码的信息

image.png

5:cheap-source-map:外部
1:错误代码的准确信息和原代码的错误位置 ,准确错误的行位置
6:cheap-module-map :外部
1:错误代码的准确信息和原代码的错误位置
2module会将loader的source-map加入

source-map 总结

开发环境:速度快,调试友好【推荐eval-source-map】
    速度快:(eval>inline>cheap>....)
    eval-cheap-source-map
    eval-source-map
    调试更友好:
    source-map
    cheap-souce-map
    cheap-module-source-map
   
    又友好又快:
    eval-source-map /eval-cheap-moudle-source-map

生产环境:源代码要不要隐藏?调试要不要友好【推荐 hidden-source-map】
  内联让代码体积更大,所以再生产不要用内联
  nosource-source-map  全部隐藏
  hidden-source-map  只隐藏源代码,会提示构建后代码的错误信息

image.png

生产环境的性能优化

A :oneOf优化构建打包速度

注意:oneOf不能有两个loader

image.png

B :缓存:bable缓存和文件缓存

1:babel 缓存(让第二次打包构建速度更快)

image.png

2:文件资源缓存(contenthash,让上线运行缓存更好使用)

hash:每次webpack 构建时回生成一个唯一的hash值【不适合】 问题: 因为js和css同时使用一个hash值,如果重新打包,会把所有的缓存清除【可能我只改一个文件css】

chunkhash【不适合】:根据chunk生成的hash值,如果打包来源同一个chunk,那么hash值就是一样 问题:js和css的hash值还是一样

contenthash【适合】:根据文件的内容生成hash值,不同文件的hash值一定不一样

image.png

C: tree shaking 去除无用的代码

tree shaking 去除无用的代码 前提:1必须使用es6模块化 2 开启production 环境 作用: 减少代码体积

在package.json中配置 “sideEffects”:false 所有代码都没有副作用(都可以进行tree shanking) 问题:可能会把css /@bale/pollyfill(副作用)文件干掉 “sideEffects”:[".css",".less","*.scss"]

D:code split 代码分割

当一个项目慢慢变得复杂的时候会导致这个bundle.js文件越来越大,浏览器加载的速度也会越来越慢,可以使用代码分割来将不同代码单独打包成不同文件。

式一:通过多入口实现代码分割

配置webpack, 将单入口,改为多入口
注意:index.js文件不要引入base.js文件

const path = require('path');
module.exports = {
    mode: 'production',
    entry: {
        index: './src/js/index.js',
        base: './src/js/base.js'
    },
    output: {
        filename: 'js/[name][contenthash:8].js',
        path: path.resolve(__dirname, 'bulid'),
    }
}

方式二:通过optimization将公共代码单独打包 引入js 也单独打包

  1. 该方式可以将node_modules中代码单独打包一个chunk最终输出

  2. 会自动分析多入口chunk中,有没有公共的文件,如果有会将公共文件打包成一个单独的chunk

  3. 该方式可以对单入口文件使用,也可以对多入口文件使用。

  4. 注意: 被动态导入的文件如base.js文件内容被修改后,hash值会更改,但是index.js打包生成的main156g2sa.js文件中会记录base.js的hash值,导致index.js的打包文件的的hash值也更改。

解决方法:可以使用runtimeChunk选项使index.js打包生成的main.js不记录base.js生成的hash值,而是将hash值单独放入到一个文件中

安装jquery

npm i jquery --save

入口文件index.js

import $ from 'jquery'; 
function fn(x, y){
    return x * y; 
}

base.js文件

import $ from 'jquery';
const add = (x, y) => x + y;
add(2, 5);

webpack配置文件

const path = require('path');

module.exports = {
    mode: 'production',
    entry: {
        index: './src/js/index.js',
        base: './src/js/base.js'
    },
    output: {
        filename: 'js/[name][contenthash:8].js',
        path: path.resolve(__dirname, 'bulid'),
    },
    optimization:{
    	//被注释的代码不是必须的,可以根据自己需求开启
        splitChunks:{
            chunks: 'all',
        },
         //将当前模块记录其他模块的hash单独打包为一个文件runtime
        runtimeChunk:{
            name: entrypoint => `runtime-${entrypoint.name}`
        },
    }
}

image.png

进行打包 最终会打包出三个文件,base.js生成的文件,index.js生成的文件,和jquery生成的文件

方式三:import动态导入

通过import动态导入语法,将某个文件单独打包。 可以实现单入口

webpack.config.js文件

const path = require('path');

module.exports = {
    mode: 'production',
    entry: './src/js/index.js',
    output: {
    	//[name]默认是chunks
        filename: 'js/[name][contenthash:8].js',
        path: path.resolve(__dirname, 'bulid'),
    },
    optimization:{
        splitChunks:{
            chunks: 'all'
        },
        //将当前模块记录其他模块的hash单独打包为一个文件runtime
        runtimeChunk:{
			name: entrypoint => `runtime-${entrypoint.name}`
		},
    }
}

入口文件index.js

import $ from 'jquery';

//加载base.js文件,返回promise
//webpackChunkName是将打包后的文件名以base开头,相同的webpackChunkName将被打包到一个文件中
//base.js文件一定要进行导出(export)
import( /* webpackChunkName: 'base' */ './base').then((result) => {
    console.log(result);
}).catch(() => {
    console.log('base.js加载失败!');
});

function fn(x, y) {
    return x * y;
}

image.png

E:懒加载和预加载

1:懒加载

image.png

2:预加载

image.png

F:PWA 渐进式网页【离线可访问】

workbox-->workbox-wepack -plugin

命令: npm i workbox-webpack-plugin -D

image.png

image.png

G:多进程打包

npm i thread-loader -D

image.png

image.png

H:externals 防止第三方包打包js中;如jq 直接用cdn引入

1: 配置修改

image.png

2:在页面使用cdn连引入即可

I: dll技术 动态链接库

对某些库(第三方库:jq,react,vue)进行单独打包 当你

-------------------第二部分end------------------------

安装

1:全局安装 npm install -g webpack

2: 项目文件下创建项目配置项 npm init

3:安装依赖项 npm install --save-dev webpack

创建文件并简单运行

image.png

image.png

image.png

image.png

拓展改造配置项

package.json改造一: 命令:npm start

image.png

package.json改造二: 命令:npm run hd

image.png

loaders

1:安装loaders 命令 npm install -save-dev json-loader

文件的变量解析:

webpack.config.js下的

image.png

image.png

image.png

package.json

package.json改造一: 命令:npm start

image.png

package.json改造二: 命令:npm run hd

image.png

命令后面的--save 和 -D区别 image.png