webpack

webpack简介

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle。

webpack 使用

  • 初始化并安装
    npm init -y 之后 npm install webpack webpack-cli --save-dev
  • 配置打包文件
    在项目的根目录下添加一个webpack.config.js文件,文件内以导出的方式书写配置文件.默认是由src/index.js ->输出到dist/main.js。
    yarn webpack --mode 模式可以切换webpack的打包模式,可以在配置文件的配置对象中设置 mode:‘development’ 属性来配置
    production模式会优化打包的结果
    development**模式会优化打包的速度
    none模式会运行最原始的webpack配置,不做任何额外的处理
    使用内置的插件需要导入模块
const path = require('path')
module.exports = {
  mode:'development',
  entry: './src/main.js',//配置导入文件的路径
  output: {				//配置输出文件的信息
    filename: 'bundle.js',
    path: path.join(__dirname, 'output')//设置文件的路径,必须为绝对路径
  }
}
  • 执行打包命令
    使用yarn webpack命令
    `

webpack 打包原理

webpack会将打包好的模块以立即执行函数的方式定义。调用时,给参数中传入一个数组,数组列表都是以参数相同的函数,函数对应源代码中的模块。所有的模块会包裹在函数中,从而实现模块的私有作用域.

webpack 资源模块加载

webpack可以使用loader来加载任何类型的资源。需要安装css-loader yarn add css-loader --dev和style-loader (将css-loader转换的结果追加到页面上)yarn add style-loader --dev,安装完成以后需要在webpack.config.js文件中添加配置。在module中添加rules属性。

module: {
    rules: [
      {
        test: /.css$/,   //利用正则表达式来匹配路径。(以.css文件结尾)
        use: [					//在配置use对象时,加载顺序是从后向前执行,必须将css-loader放到最后先执行
          'style-loader',		//使用style-loader将转换好的css结果追加到页面上
          'css-loader'			//使用的css-loader 将css代码转换成js模块才可以进行打包
        ]
      }
    ]
  }

将css资源引入js文件中,满足代码所需要的条件而非应用将js和css相分离。
加载图片资源时也需要引入加载器file-loader 命令yarn add file-loader --dev.webpack会将所有打包的结果放到网站的根目录下。在webpack的配置文件的导出(output属性)里添加publicPath:'实际的url'

module.exports = {
  mode: 'none',
  entry: './src/main.js',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist'),
    publicPath: 'dist/'         //设置导出文件的位置
  },
  module: {						
    rules: [
      {
        test: /.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /.png$/,			//利用正则表达式确定图片文件位置
        use: 'file-loader'		//使用file-loader将图片文件打包
      }
    ]
  }
}

使用url-loader 将文件转化为Data URLs存放到代码中安装yarn add url-loader --dev

小文件使用Data URLs,减少请求次数 ;大文件单独提取存放,提高加载速度

use: {
          loader: 'url-loader',		//
          options: {
            limit: 10 * 1024 // 10 KB	//设置10kb以下的文件通过	url-loader存放超过10KB的文件用文件单独存放
          }
        }

加载器类型

  1. 资源类加载器 css-loader
  2. 文件操作类加载器 file-loader
  3. 代码检查加载器 eslint-loader
    配置bable-loader,
{ 		test: /.js$/,
use: {
          loader: 'babel-loader',		//配置设置为babel-loader
          options: {
            presets: ['@babel/preset-env']//此插件集合包含全部ES全新特性
          }
        }
       }

打包html需要使用html-loader 插件,使用html-loader默认只会处理image标签的src属性,使用其他标签属性需要额外配置

{
        test: /.html$/,
        use: {
          loader: 'html-loader',
          options: {
            attrs: ['img:src', 'a:href']	//处理a标签的herf属性
          }
        }
      }

Webpack模块加载方式

  • 遵循ES Module标准的import声明
  • 遵循CommonJs标准的require函数
  • 遵循AMD标准的define函数和require函数
  • 样式代码中的@import指令和url函数
  • HTML代码中图片标签的src属性
    所有需要引用的资源都会被webpack找出根据配置交给不同的loader处理,将处理的结果整体打包到输出目录,通过这个特点实现项目的模块化

webpack工作原理

一个项目中会存在资源文件和代码集 webpack会根据配置找到一个js文件作为打包入口。沿着入口文件代码根据代码引入的import文件解析依赖的资源模块,最后根据依赖找到所有项目依赖关系的依赖树,webpack会递归遍历依赖树种每个节点所对应的资源文件。最后根据配置文件中的rules属性找到模块对应的加载器加载该模块,将加载出的结果放到bundle.js中。

yarn add marked --dev

插件使用

增强Webpack自动化能力使用Plugin解决其他自动化工作 等

Webpack自动清理输出目录插件

安装clean-webpack-plugin 插件yarn add clean-webpack-plugin --dev。使用时需要导入插件

const { CleanWebpackPlugin } = require('clean-webpack-plugin')//导出插件
module.explorts = {
 plugins: [   //配置插件时在module.explorts中书写一个plugins 属性
    new CleanWebpackPlugin()
  ]
}

html 打包 插件

为使html文件能够参与构建,减少各资源文件位置引用错误,使用html-webpack-plugin插件进行构建。确保引用相对路径正确
安装插件 yarn add html-webpack-plugin --dev
默认html-webpack-plugin 导出为插件类型,无需解构

const HtmlWebpackPlugin = require('html-webpack-plugin')
//还需删除原来资源文件的默认导出路径 (publicpath的属性值)

plugins: [
    new CleanWebpackPlugin(),
    // 用于生成 index.html
    new HtmlWebpackPlugin({
      title: 'Webpack Plugin Sample', 		//设置html文件中titel标签中的内容
      meta: {
        viewport: 'width=device-width'		//设置内mateb标签中content的属性值
      },
      template: './src/index.html'
    }),
    // 用于生成 about.html
    new HtmlWebpackPlugin({
      filename: 'about.html'
    })
  ]

拷贝文件(上线前最后一次打包时使用)

利用copy-webpack-plugin --dev安装。传递数组用于拷贝文件的路径

const CopyWebpackPlugin = require('copy-webpack-plugin')
plugins:[
new CopyWebpackPlugin([
      // 'public/**'//将public目录中的所有文件打包拷贝
      'public'  
    ])
 ]

Webpack 插件原理

plugin通过在生命周期中的钩子中挂载函数实现扩展,必须是一个函数或者是包含apply()方法的 对象。一般声明一个类,在类中定义对象方法。

webpack 增强开发体验

    使用webpack打包时,可以使用yarn webpack --watch命令开启监听模式,代码修改后自动重新打包。使用 browser-sync插件可以自动编译后刷新 browser-sync dist --file "**/*"但是编译读取会重复读取磁盘效率低下。使用webpack Dev Server提供Http Server集成自动编译和自动刷新功能。安装插件yarn add webpack-dev-server --dev后运行命令yarn webpack-dev-server 会自动使用webpack打包应用,并且启用HTTP-serve运行打包结果,监听运行变化。Webpack Dev Server没有将打包结果写入磁盘,而放入内存,内部的HTTP-server从内存读取数据。使用yarn webpack-dev-server --open用于自动唤起浏览器
   Http-Server 默认将构建结果输出的文件构建为开发资源文件,静态资源被访问时需要额外为开发服务器指定查找资源目录serve。

//在配置文件中添加一个devServer属性
devServer: {
	contentBase:'./public'		//制定额外的静态资源路径,可以设置为字符串或者数组来配置一个或多个路径
}

代理API(处理跨域获取资源问题)

在生产环境可以直接访问API,但是在开发环境中访问网页使用的地址是localhost:8080 此时需要CORS跨越请求,但并非所有的浏览器都支持跨域请求,此时就需要将API服务代理到开发服务中。Webpack-Dev-Server 可以通过配置来提供代理服务。
devServer: {
    contentBase: './public',
    proxy: {				//添加代理服务配置,内部的每个属性都是代理服务规则
      '/api': {				// 
        // http://localhost:8080/api/users -> https://api.github.com/api/users
        target: 'https://api.github.com',	// 将github资源代理到开发服务器
        // http://localhost:8080/api/users -> https://api.github.com/users
        pathRewrite: {					//重写代理路径
          '^/api': ''					//将api目录替换成空(会以正则方式替换,所以^表示开头)
        },
        // 不能使用 localhost:8080 作为请求 GitHub 的主机名
        changeOrigin: true				//默认浏览器会以实际请求的主机名
      }
    }
  }

Source Map

  使用webpack打包过程中,会将文件进行转换,线上运行的代码和开发过程中代码不一致,导致难以排除bug。
Source Map文件是映射源代码和转换后的代码的关系,通过Sourse Map能够将转化后的代码逆向得到源代码。在webpack.config.js文件中可以通过devtool属性配置转换的模式。

  • eval 模式:模块代码放到eval函数中执行并且通过sourse URL标识文件路径,只能定位出错文件不可定位行列
  • eval-sourse-map:可定位行列,生产sourse-map
  • cheap-eval-source-map :只定位行,无列信息。定位的源代码是ES6转化后的源代码
  • cheap-module-eval-source-map:加module以后定位代码为编写时的源代码
  • hidden-source-map:生成souce-map开发第三方包时候使用。生成source-map文件,但没有引用文件
  • nosourse-source-map:可以看到错误位置信息,但无源代码

选择使用模式

开发:cheap-module-eval-sourse-map:经过loader转化之后的源代码差异较大,需要使用转化之前的。启动打包慢,重新打包速度的很快
生产:nosurce-source-map 或none:source-map会暴漏源代码

自动刷新问题

自动刷新导致页面状态的丢失。
解决方法:使用热替换(HMR)
webpack-dev-server --hot或在配置文件中配置devServer中hot属性为true,将插件实例化。

const webpack = require('webpack')  		//要使用webpack内置的插件,先将插件导入
module.exports = {
 ...
  devServer: {
    // hot: true		//用hot如果热替换失败就会回退自动刷新功能
     hotOnly: true // 只使用 HMR,不会 fallback 到 live reloading
  },
  ...
  }
  plugin[
    new webpack.HotModuleReplacementPlugin()
]

HMR需要手动处理模块热替换逻辑。样式文件时由css-loader处理,直接替换修改过的内容,而JS文件需要自己手动处理热更新之后的替换规则。

HMR API

HMR为更新修改内容提供了API,使用内置方法accept()
语法:module.hot.accept(更新文件的URL,处理函数)。

//main.js文件
  module.hot.accept('./better.png', () => {
    // 当 better.png 更新后执行
    // 重写设置 src 会触发图片元素重新加载,从而局部更新图片
    img.src = background
  })

由于热更新失败默认采用自动刷新,使得热更新代码有错误,不容易发现,可以采用hotOnly:true

 devServer: {
    //hot: true
     hotOnly: true // 只使用 HMR,不会 fallback 到 live reloading
  },

没有启动HMR,HMR API报错:由于module.hot对象是一由HMR插件提供。没有启动HMR但使用了HMR导致

if (module.hot) {			//首先安段有没有开启,再使用
  let hotEditor = editor
  module.hot.accept('./editor.js', () => {
    // 当 editor.js 更新,自动执行此函数
    // 临时记录编辑器内容
    const value = hotEditor.innerHTML
    // 移除更新前的元素
    document.body.removeChild(hotEditor)
    // 创建新的编辑器
    // 此时 createEditor 已经是更新过后的函数了
    hotEditor = createEditor()
    // 还原编辑器内容
    hotEditor.innerHTML = value
    // 追加到页面
    document.body.appendChild(hotEditor)
  })

在使用webpack打包的过程中,HRM相关配置代码会自动被删除。不会被引入到生产环境中

不同环境打包不同代码

方式1:配置文件根据环境不同导出不同的配置 (中小型项目)
通过导出一个函数来配置对象,函数提供两个参数env(环境名参数)和argv(所有参数)

if (env === 'production') {
    config.mode = 'production'
    config.devtool = false
    config.plugins = [
      ...config.plugins,
      new CleanWebpackPlugin(),
      new CopyWebpackPlugin(['public'])
    ]
  }

  return config
}

方式2:一个环境对应一个配置文件(大型项目)
配置时一般会有多个配置文件,用后面的配置覆盖掉公共属性的配置,并且在公共配置的基础上添加一些配置。使用webpack中的merge合并配置需求。安装webpack-merge使用命令yarn add webpack-merge --dev

const merge = require('webpack-merge')
module.exports = merge(common, {
  mode: 'production',
  plugins: [
    new CleanWebpackPlugin(),
    new CopyWebpackPlugin(['public'])
  ]
})

webpack优化配置

DefinePlugin插件

给代码注入全局成员默认给程序中注入 process.env.NODE_ENV常量,用来为第三方模块提供判断运行环境的参数

const webapck = require('webpack')
plugins: [
	new webpack.DefinPlugin({
	API_BASE_URL:'"https://api.example.com"'
	//如果传入一个值则使用json.stringify将值转化为表示值的代码片段。
	//    new webpack.DefinePlugin({
      // 值要求的是一个代码片段
    //  API_BASE_URL: JSON.stringify('https://api.example.com')
})
]

Tree shaking

移除代码中未引用的代码,在生产模式下自动开启
解释:在生产模式下自动开启usedExports和concatenateModules属性导致自动

 optimization: {
    // 模块只导出被使用的成员
    usedExports: true,//标记未引用的代码
    // 尽可能合并每一个模块到一个函数中
    concatenateModules: true,//删除被标记的代码
    // 压缩输出结果
    // minimize: true
  }

concatenateMoudles属性将所有模块合并到一起。既提升效率有减少代码体积。
使用Tree-shaking前提是用ES Module来组织代码。webpack打包代码之前必须使用ESM。babel-loader转化时如果设置option属性为presets :[@babel/preset-env]时,就会将ESmodule转化成commonjs。此时Treee-shaking就无法生效
sideEffects新特性 通过配置的方式表示代码是否有副作用。从而为Tree-shaking提供更大的 压缩空间。副作用是指模块执行是除了导出成员还有其他任务。设置sideEffects属性值为false其他没有用到的代码就不会被打包。

分包(代码分割)

  • 多入口打包(适用于多页面程序)
    配置entry定义为对象而不是数组,如果定义为数组会把多个文件打包在一起
module.exports = {
  mode: 'none',
  entry: {
    index: './src/index.js',	
    album: './src/album.js'
  },
  output: {
    filename: '[name].bundle.js'	//输出文件名为index.budle.js和album.budle.js
  },
  ...
  }

以上配置会将所有模块引入到每个文件中,需要配置chunks属性
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: ‘Multi Entry’,
template: ‘./src/index.html’,
filename: ‘index.html’,
chunks: [‘index’] //每个打包入口形成独立的chunks
}),
new HtmlWebpackPlugin({
title: ‘Multi Entry’,
template: ‘./src/album.html’,
filename: ‘album.html’,
chunks: [‘album’]
})
]

  • 动态导入
    将公共模块提取到单独的模块中
  output: {
    filename: '[name].bundle.js'
  },
  optimization: {
    splitChunks: {
      // 自动提取所有公共模块到单独 bundle
      chunks: 'all'
    }
  },
...

动态导入以后的名称是序号,通过魔法注释为打包后的文件命名
imput(/* WebpackChunkName:components*/ ./post)`相同命名的文件会打包在一起

提取CSS到单独的文件(CSS按需加载)

miniCssExtractPlugin插件导入后直接放到plugin数组中就可使用

plugins: [
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin()
  ]

提取以后需要压缩输出使用optimize-css-assets-webpack-plugin插件。使用时需要将js压缩和css压缩插件都放在一起才可使用,安装webpack内置插件terser-webpack-plugin压缩js代码

optimization: {
    minimizer: [
      new TerserWebpackPlugin(),
      new OptimizeCssAssetsWebpackPlugin()
    ]
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值