webpack工程化实战day4

166 阅读2分钟

Hot Module Replacement(HMR:热模块替换)

  • css模块HMR JS模块HMR
# 启动hmr
devServer: {
	contentBase: './dist',
    open: true,
    hot: true,
    // 即使HMR不生效,浏览器也不自动刷新,就开启hotOnly
    hotOnly: true
}

# 配置文件头部引入webpack
const webpack = require('webpack')

#在插件配置处添加
plugins:[
	new webpack.HotModuleReplacementPlugin()
]

案例一:处理css模块HMR

import "./css/index.css";

var btn = document.createElement("button")
btn.innerHTML = "新增"
document.body.appendChild(btn)

btn.onclick = function(){
	var div = document.createElement('div')
    div.innerHTML = 'item'
    document.body.appendChild(div)
}

// index.css
div:nth-of-type(odd){
	background: yellow;
}

启动HMR后,css抽离会不生效,还有不支持contenthash,chunkhash

案例二:处理js模块HMR

需要使用module.hot.accept来观察模块更新,从而进行更新

// counter.js
function counter(){
	var div = document.createElement('div');
    div.setAttribute('id','counter');
    div.innerHTML = 1;
    div.onclick = function(){
    	div.innerHTML = parseInt(div.innerHTML, 10) + 1;
    }
     document.body.appendChild(div)
}
export default counter

// number.js
function number(){
	var div = document.createElement('div')
    div.setAttribute('id', 'number')
    div.innerHTML = 13000
    document.body.appendChild(div)
}

export default number


index.js

import counter from './counter'
import number from './number'
counter()
number()

if(module.hot){
	// 监听number模块变化
	module.hot.accept('./number.js', function(){
    	document.body.removeChild(document.getElementById("number"))
        number()
    })
}

Babel处理ES6

官方网站:babeljs.io/

Babel是JavaScript编译器,能将ES6代码转换成ES5代码,可以在开发过程中放心使用JS新特性而不用担心兼容性问题,还可以根据插件机制根据需求灵活扩展。

Babel在执行编译过程中,会从项目根目录下的.babelrc JSON文件中读取配置,如没有该文件会从loader的options读取配置

测试代码

# index.js

const arr = [new Promise(()=>{}),new Promise(()=>{})]

arr.map(item => {
	console.log(item)
})

# 安装
npm i babel-loader @babel/core @babel/preset-env -D

babel-loader是webpack与babel的通信桥梁,不会做把ES6转为ES5的工作,转化工作需要用到@babel/preset-env来做

# webpack.config.js
{
	test: /\.js$/,
    exclude: /node_modules/,
    use: {
    	loader: "babel-loader",
        options: {
        	presets: ["@babel/preset-env"]
        }
    }
}

默认的Babel只支持let等一些基础的特性转换,Promise等一些还没有转换过来,这是需要借助@babel/polyfill,把es的新特性都装进来,来弥补低版本浏览器中缺失的特性。

babel/polyfill

以全局变量方式注入进来的,windows.Promise,但是这样会造成全局对象的污染

npm install --save @babel/polyfill

// # index.js顶部
import "@babel/polyfill"

优化

  1. 按需加载,减少冗余

引入@babel/polyfill后,发现打包后的体积打了很多,这是因为polyfill默认会把所有的特性都注入进来,如何实现需要的注入,不需要的无需注入,从而减少打包的体积。

# 修改webpack.config.js
options: {
	presets: [
    	[
        	"@babel/preset-env",
            {
            	targets: {
                	edge: "17",
                    firefox: "60",
                    chrome: "67",
                    safari: "11.1"
                },
                corejs:2, // 新版本需要指定核心版本库
                useBuiltIns: "usage" // 按需注入
            }
        ]
    ]
}

useBuiltIns选项是babel 7的新功能,这个选项告诉babel如何配置@babel/polyfill,有三个参数可以使用:

  • entry:需要在webpack的入口文件中import “@babel/polyfill” 一次,babel会根据使用情况导入垫片,没有使用的功能不会被导入相应的垫片
  • usage:不需要import,全自动检测,但是要安装@babel/polyfill
  • false:如果import "@babel/polyfill",不会排除掉没有使用的垫片,程序体积会变大。(不推荐使用)
  1. 使用babelrc文件
新建.babelrc文件,把上述options中的配置移入到该文件中即可。
// .babelrc

{
	"presets": [
    	[
        	"@babel/preset-env",
            {
            	"targets": {
                	"edge": "17",
                    "firefox": "60",
                    "chrome": "67",
                    "safari": "11.1"
                },
                "corejs":2// 新版本需要指定核心版本库
                "useBuiltIns": "usage" // 按需注入
            }
        ]
    ]
}

# webpack.config.js进行相应调整
{
	test: /\.js$/,
    exclude: /node_modules/,
    loader: "babel-loader"
}

配置React打包环境

# 安装
npm install react react-dom --save

# index.js
import React, { Component } from 'react'
import ReactDom from "react-dom"

class App extends Component {
	render(){
    	return <div>hello world</div>
    }
}

ReactDom.render(<App/>, document.getElementById('app'))

# 安装babel与react转换的插件
npm install --save-dev @babel/preset-react

# 在.babelrc文件里添加相应配置:
"babel/preset-react"

如何实现一个plugin

webpack在编译代码过程中,有生命周期的概念,对应不同的打包阶段

plugin本质上是一个类,那么它是如何注册到webpack的对应阶段的?

webpack打包的流程:

  1. 拿到webpack.config.js中的配置,初始化工作
  2. 实现一个compiler类,注册插件,对应生命周期绑定相应的事件
  3. 执行编译, compiler.run()
  4. compiler(构建阶段)->compilation(在某个阶段,bundle加工的状态)
  5. 递归处理所有的依赖模块,生成chunk
  6. 把chunk输出到output指定的位置

案例

实现功能:实现一个打包清单插件,打包结束后,输出目录多出一个fileList.txt文件,文件内容是:bundle文件的数量以及所有换行输出所有文件名称

代码截图:

使用:

#webpack.config.js
在文件中引入 
const txtWebpackPlugin = require('./txt-webpack-plugin.js')

在plugins选项中添加一项:
new txtWebpackPlugin()

之后可在dist目录中看到相应结果