Webpack02

此文项目代码:https://github.com/bei-yang/I-want-to-be-an-architect
码字不易,辛苦点个star,感谢!

引言


此篇文章主要涉及以下内容:

  1. Hot Module Replacement(HMR:热模块替换)
  2. babel/polyfill
  3. babel/plugin-transform-runtime
  4. 配置react打包环境
  5. development vs production模式区分打包
  6. 代码分割code Splitting
  7. 打包分析
  8. webpack官方推荐的编码方式

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

启动hmr

devServer: {
    contentBase: "./dist",
    open: true,
    hot:true,
    //即便HMR不生效,浏览器也不自动刷新,就开启hotOnly
    hotOnly:true
  },

配置文件头部引入webpack

//const path = require("path");
//const HtmlWebpackPlugin = require("html-webpack-plugin");
//const CleanWebpackPlugin = require("clean-webpack-plugin");

const webpack = require("webpack");

在插件配置处添加:

plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: "src/index.html"
    }),
    new webpack.HotModuleReplacementPlugin()
  ],

案例:

//index.js
import "./css/index.css";

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

btn.onclick = function() {
  var div = document.createElement("div");
  console.log("1");
  div.innerHTML = "item";
  document.body.appendChild(div);
};

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

处理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) {
  module.hot.accept("./b", function() {
    document.body.removeChild(document.getElementById("number"));
    number();
  });
}

Babel处理ES6

官方网站:https://babeljs.io/

npm i babel-loader @babel/core @babel/preset-env -D

//babel-loader是webpack 与 babel的通信桥梁,不会做把es6转成es5的工作,这部分工作需要用到@babel/preset-env来做

//@babel/preset-env里包含了es6转es5的转换规则
//index.js
const arr = [new Promise(() => {}), new Promise(() => {})];

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

通过上面的几步 还不够,Promise等一些还有转换过来,这时候需要借助@babel/polyfill,把es的新特性都装进来,来弥补低版本浏览器中缺失的特性

@babel/polyfill

npm install --save @babel/polyfill

Webpack.config.js

{
    test: /\.js$/,
    exclude: /node_modules/,
    loader: "babel-loader",
    options: {
        presets: ["@babel/preset-env"]
    }
}
//index.js 顶部
import "@babel/polyfill";

会发现打包的体积大了很多,这是因为polyfill默认会把所有特性注入进来,假如我想我用到的es6+,才会注入,没用到的不注入,从而减少打包的体积,可不可以呢

当然可以

修改Webpack.config.js

options: {
          presets: [
            [
              "@babel/preset-env",
              {
                targets: {
                  edge: "17",
                  firefox: "60",
                  chrome: "67",
                  safari: "11.1"
                },
                useBuiltIns: "usage"//按需注入
              }
            ]
          ]
        }

当我们开发的是组件库,工具库这些场景的时候,polyfill就不适合了,因为polyfill是注入到全局变量,window下的,会污染全局环境,所以推荐闭包方式:@babel/plugin-transform-runtime

@babel/plugin-transform-runtime

它不会造成全局污染

npm install --save-dev @babel/plugin-transform-runtime

npm install --save @babel/runtime

怎么使用?

先注释掉index.js里的polyfill

//import "@babel/polyfill";

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

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

修改配置文件:注释掉之前的presets,添加plugins

options: {
//   presets: [
//     [
//       "@babel/preset-env",
//       {
//         targets: {
//           edge: "17",
//           firefox: "60",
//           chrome: "67",
//           safari: "11.1"
//         },
//         useBuiltIns: "usage"
//       }
//     ]
//   ]
"plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        "corejs": 2,
        "helpers": true,
        "regenerator": true,
        "useESModules": false
      }
    ]
  ]
}

useBuiltIns选项是babel7的新功能,这个选项告诉babel如何配置@babel/polyfill。

它有三个参数可以使用:

  1. entry:需要在webpack的入口文件里import "@babel/polyfill"一次,babel会根据你的使用情况导入垫片,没有使用的功能不会被导入相应的垫片。
  2. usage:不需要import,全自动检测,但是要安装@babel/polyfill。(试验阶段)
  3. false:如果你import "@babel/polyfill",它不会排除掉没有使用的垫片,程序体积会庞大。(不推荐)

注意:usage的行为类似babel-transform-runtime,不会造成全局污染,因此也不会对类似Array.prototype.include()进行polyfill。

扩展:

babelrc文件:

新建.babelrc文件,把options部分移入到该文件中,就可以了

//.babelrc
{
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        "corejs": 2,
        "helpers": true,
        "regenerator": true,
        "useESModules": false
      }
    ]
  ]
}

//webpack.config.js

{
    test: /\.js$/,
    exclude: /node_modules/,
    loader: "babel-loader"
}

配置React打包环境

安装

npm install react react-dom --save

编写react代码:

//index.js
import "@babel/polyfill";

import React, { Component } from "react";
import ReactDom from "react-dom";

class App extends Component {
  render() {
    return 
hello world
; } } ReactDom.render(, document.getElementById("app"));

安装babel与react转换的插件:

npm install --save-dev @babel/preset-react

在babelrc文件里添加:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage" //按需注入
      }
    ],
    "@babel/preset-react"
  ]
}

面试题:

  1. 为什么使用polyfill?
  2. runtime和polyfill有什么区别?
  3. 什么时候使用polyfill?什么时候使用runtime?

tree shaking

webpack2.x开始支持 tree shaking概念,它能将不使用的代码,不打包。顾名思义,"摇树"(没用的东西摇掉,减少打包体积),只支持ES module的引入方式!!!

//webpack.config.js

optimization: {
    usedExports: true
}
 
//package.json 
"sideEffects":false
// 正常对所有模块进行tree shaking  或者
"sideEffects": [
  '*.css',
  '@babel/polyfill'
]

开发模式设置后,不会帮助我们把没有引用的代码去掉

案例:

//expo.js export const add = (a, b) => {  console.log(a + b); };
 
export const minus = (a, b) => {  console.log(a - b); };
 //index.js import { add } from "./expo"; 

add(1, 2);

development vs Production模式区分打包

npm install webpack-merge -D

案例:

const merge = require("webpack-merge") 
const commonConfig =  require("./webpack.common.js") 
const devConfig = {    ... }
 
module.exports = merge(commonConfig,devConfig)
 
//package.js "scripts":{    
  "dev":"webpack-dev-server --config ./build/webpack.dev.js",      
  "build":"webpack --config ./build/webpack.prod.js" 
}

案例2:

基于环境变量

//外部传入的全局变量 
module.exports = (env)=>{
    if(env && env.production){
        return merge(commonConfig,prodConfig) 
   }else{
        return merge(commonConfig,devConfig)
    } 
}
 
//外部传入变量 
scripts:" --env.production"

代码分割 code Splitting

import _ from "lodash";
 
console.log(_.join(['a','b','c','****']))
 
假如我们引入一个第三方的工具库,体积为1mb,而我们的业务逻辑代码也有1mb,那么打包出来的体积大小会在2mb
 
导致问题:体积大,加载时间长,业务逻辑会变化,第三方工具库不会,所以业务逻辑一变更,第三方工具库也要跟着变。

引入代码分割的概念:

//lodash.js
import _ from "lodash";
window._ = _;
 
//index.js 注释掉lodash引用 //import _ from "lodash";
console.log(_.join(['a','b','c','****']))
//webpack.config.js 
entry: {
    lodash: "./lodash.js",
    index: "./index.js"
  },  
//指定打包后的资源位置
 output: {
    path: path.resolve(__dirname, "./build"),
    filename: "[name].js"
  }

其实code Splitting概念与webpack并没有直接的关系,只不过webpack中提供了一种更加方便的方法供我们实现代码分割

基于https://webpack.js.org/plugins/split-chunks-plugin/

optimization: {
    splitChunks: {
      // chunks:'all', // 对代码进行分割
      chunks: 'async',//对同步,异步,所有的模块有效
      minSize: 30000,//当模块大于30kb
      maxSize: 0,//对模块进行二次分割时使用,不推荐使用
      minChunks: 1,//打包生成的chunk文件最少有几个chunk引用了这个模块
      maxAsyncRequests: 5,//模块请求5次
      maxInitialRequests: 3,//入口文件同步请求3次
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10//优先级 数字越大,优先级越高
        }, 
       default: {
          minChunks: 2, 
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }

使用下面配置即可:

optimization:{
    //帮我们自动做代码分割
    splitChunks:{
        chunks:"all",//默认是支持异步,我们使用all
    }
 }

打包分析

查看打包的速度,大小等信息

https://github.com/webpack/analyse

官方推荐工具

https://webpack.js.org/guides/code-splitting/#bundle-analysis

webpack官方推荐的编码方式

optimization:{
    //帮我们自动做代码分割
    splitChunks:{
        chunks:"async",//默认是支持异步
    }
 }

代码利用率问题

//index.js
 
document.addEventListener("click", () => {
  const element = document.createElement("div");
  element.innerHTML = "welcome to webpack4.x";
  document.body.appendChild(element);
 });
 
通过控制台看看代码利用率
ctrl+shift+p
show coverage

把里面异步代码抽离出来

异步加载,可以实现静默获取内容,当需要时没有白屏的展示

webpack提倡使用异步加载的方式,先异步获取数据——PrefetchPlugin

//index.js 
 
document.addEventListener("click", () => {
   import("./click.js").then(({ default: func }) => {
   //需要用到 npm install --save-dev @babel/plugin-syntax-dynamic-import
    func();
   });
 }); 
 
//click.js
function handleClick() {
   const element = document.createElement("div");
   element.innerHTML = "welcome to webpack4.x";
   document.body.appendChild(element);
 } 
 
export default handleClick; 

你的赞是我前进的动力

求赞,求评论,求分享...

你可能感兴趣的:(Webpack02)