25

简介

鉴于webpack更新太快,总结下基础配置方法,理解有限,仅做抛砖引玉之用。

起步

初始化配置文件 package.json并安装webpack

mkdir webpack-demo && cd webpack-demo
npm init -y    //-y 初始化选项默认为 yes      
npm i webpack webpack-cli -D // -D 即 -save-dev    版本4.x以上需要安装webpack-cli

创建以下目录结构、文件和内容:

webpack-demo
  |- package.json
+ |- /src
+   |- index.js
//index.js
document.write("Hello webpack4!");

创建webpack配置文件

编写开发环境和生产环境彼此独立的webpack配置文件
先添加三个文件

 webpack-demo
  |- package.json
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js
  |- /src
    |- index.js
  |- /node_modules

1.webpack.common.js

用到两个基本的插件

npm i clean-webpack-plugin html-webpack-plugin -D

clean-webpack-plugin:打包时自动清除输出文件夹中未用到的文件;
html-webpack-plugin:打包时会自动生成index.html并替换已有的index.html,bundle.js也会自行添加到 html 中。

  //webpack.common.js
    const path = require('path');
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');  
    const HtmlWebpackPlugin = require('html-webpack-plugin');    
    
    module.exports = {
      entry: './src/index.js',
      plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
          title: 'index'
        })
      ],
      output: {
        filename: 'bundle.js',           
        path: path.resolve(__dirname, 'dist')    //定义输出文件夹dist路径
      }
    };

2.webpack.dev.js

先安装webpack-merge,用以合并通用配置文件与开发环境配置文件

npm i webpack-merge -D

安装开发服务器devServer,作用是修改代码后实时重新加载(刷新浏览器)

npm i webpack-dev-server -D
//webpack.dev.js
  const merge = require('webpack-merge');
  const common = require('./webpack.common.js');
  const webpack = require('webpack');
    
  module.exports = merge(common,{
     devServer: {                //启用开发服务器
       contentBase: './dist',   //告诉服务器从哪提供内容,只有在想要提供静态文件时才需要
       compress: true,          //一切服务都启用gzip 压缩
       host: '0.0.0.0',         //指定使用一个host,可用ip地址访问,没有的话如果别人访问会被禁止。默认localhost。
       port: '9999',            //指定端口号,如省略,默认为”8080“
       hot: true,               //启用模块热替换特性
       inline: true,            //启用内联模式,一段处理实时重载的脚本被插入到bundle中,并且构建消息会出现在浏览器控制台
       historyApiFallback: true,//开发单页应用时有用,依赖于HTML5 history API,设为true时所有跳转将指向index.html
     },
     plugins: [
         new webpack.HotModuleReplacementPlugin(),  //webpack内置的热更新插件
     ],
     mode: 'development'
  });

devServer的更多可选参数-https://www.webpackjs.com/con...

HotModuleReplacementPlugin 模块热替换(Hot Module Replacement)插件,用以在运行时更新发生改变的模块,从而无需进行完全刷新。

3.webpack.prod.js

同样用'webpack-merge'合并通用配置文件与生产环境配置文件

//webpack.prod.js
  const merge = require('webpack-merge');
  const common = require('./webpack.common.js');
    
  module.exports = merge(common,{
    mode: "production"
  });
关于mode

此时你可能会注意到配置文件中有个mode项,webpack4中新加,作用如下:

  • --mode production 生产环境

    不需要像旧版本一样定义node环境变量
    new webpack.DefinePlugin({"process.env.NODE_ENV":JSON.stringify("production") })

    ps:许多 library 将通过与 process.env.NODE_ENV 环境变量关联,以决定 library 中应该引用哪些内容。当使用 process.env.NODE_ENV === 'production' 时,一些 library 可能针对具体用户的环境进行代码优化,从而删除或添加一些重要代码。
    

    自动开启一些插件,如:
    uglifyjs-webpack-plugin js代码压缩(所以无需再单独使用)
    NoEmitOnErrorsPlugin 编译出错时跳过输出,以确保输出资源不包含错误
    ModuleConcatenationPlugin webpack3 添加的作用域提升(Scope Hoisting)

  • --mode development 开发环境

    自行定义node环境变量为development
    new webpack.DefinePlugin({"process.env.NODE_ENV":JSON.stringify("development") })
    使用 eval 构建 module, 提升增量构建速度
    自动开启一些插件,如
    NamedModulesPlugin 使用模块热替换(HMR)时会显示模块的相对路径

具体描述:

Option Description
development Sets process.env.NODE_ENV to value development. Enables NamedChunksPlugin and NamedModulesPlugin.
production Sets process.env.NODE_ENV to value production. Enables FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin and UglifyJsPlugin.
none Opts out of any default optimization options

启动

package.json "scripts" 中添加npm脚本,从而快捷运行开发服务器 | 打包生产环境代码

//package.json
{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "clean-webpack-plugin": "^0.1.19",
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.15.1",
    "webpack-cli": "^3.0.8",
    "webpack-dev-server": "^3.1.4",
    "webpack-merge": "^4.1.3"
  }
}

"start": "webpack-dev-server --open --config webpack.dev.js",

webpack-dev-server    启动开发服务器   
--open    打开浏览器
--config webpack.dev.js    设置运行此脚本时执行的配置文件为webpack.dev.js

"build": "webpack --config webpack.prod.js"

webpack 启动webpack
--config webpack.prod.js   设置运行此脚本时执行的配置文件为webpack.prod.js
执行 npm start

此时应该可以看到 Hello webpack4!

执行 npm run build

项目文件夹中自动生成打包后的文件目录(输出文件夹dist)

 webpack-demo
  |- package.json
  |- webpack.common.js
  |- webpack.dev.js
  |- webpack.prod.js
  |- /src
    |- index.js
  |- /dist 
      | - index.html
      | - app.bundle.js
  |- /node_modules

使用sourcemap

sourcemap 能实现打包后的运行代码与源代码的映射,帮助我们debug到原始开发代码。

///webpack.dev.js
  module.exports = merge(common,{
    devtool: 'cheap-module-eval-source-map',
    ...
  });

大多数时候开发环境用'cheap-module-eval-source-map'是最好的选择,想要完整的功能又不介意构建速度的话就直接用'source-map'。具体的配置项很多,可以是eval,source-map,cheap,module,inline的任意组合。
具体每个参数的作用请查阅官方api:https://webpack.js.org/config...
也可参考这篇文章https://segmentfault.com/a/11... 这里不做详述。

代码分离

把代码分离到不同的 bundle 中,可以按需加载或并行加载这些文件。可用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。

三种常用的代码分离方法:

1.入口起点:使用 entry 配置手动地分离代码。

先在src文件夹添加一个文件another-module.js

//another-module.js
  import _ from 'lodash';
  console.log(
    _.join(['Another', 'module', 'loaded!'], ' ')
  ); 

修改index.js

//index.js
  import _ from 'lodash';
  console.log(
    _.join(['index', 'module', 'loaded!'], ' ')
  );

用到了lodash,安装下依赖

npm i lodash -S

修改webpack.common.js中entry和output配置

//webpack.common.js
  module.exports = {
    entry: {
      index: './src/index.js',
      another: './src/another-module.js'
    },
    output: {
      filename: '[name].bundle.js',            //根据入口文件名来定义输出文件名
      path: path.resolve(__dirname, 'dist')
    }
};
执行 npm run build

将生成如下构建结果:

Hash: 66f57fffc46778f3b145
Version: webpack 4.16.0
Time: 2966ms
            Asset       Size  Chunks             Chunk Names
another.bundle.js   70.4 KiB       0  [emitted]  another
  index.bundle.js   70.4 KiB       1  [emitted]  index
       index.html  251 bytes          [emitted]  
[1] (webpack)/buildin/module.js 497 bytes {0} {1} [built]
[2] (webpack)/buildin/global.js 489 bytes {0} {1} [built]
[3] ./src/another-module.js 86 bytes {0} [built]
[4] ./src/index.js 83 bytes {1} [built]
    + 1 hidden module
Child html-webpack-plugin for "index.html":
     1 asset
    [0] (webpack)/buildin/module.js 497 bytes {0} [built]
    [1] (webpack)/buildin/global.js 489 bytes {0} [built]
        + 2 hidden modules

存在的问题:

  • 如果入口 chunks 之间包含重复的模块,那些重复模块都会被引入到各个 bundle 中。
  • 不够灵活,不能将核心应用程序逻辑动态地拆分代码。

以上两点中,第一点对我们的示例来说无疑是个问题,index.js 和another-module.js中都引入了 lodash,这样就在两个 bundle 中造成重复引用。接着,我们通过使用 SplitChunks 来移除重复的模块。

2.防止重复:使用SplitChunks 去重和分离 chunk。webpack4 之前版本用的是CommonsChunkPlugin

//webpack.common.js
  const path = require('path');
  module.exports = {
    entry:  {
      index: './src/index.js',
      another: './src/another-module.js'
    },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Production'
      })
    ],
    output: {
      filename: '[name].bundle.js',            //根据入口文件名来定义输出文件名
      path: path.resolve(__dirname, 'dist')
    },
+   optimization: {
+     splitChunks: {
+       chunks: 'all'
+     }
+   }
  };
再次执行npm run build查看效果
...

vendors~another~index.bundle.js   69.5 KiB       0  [emitted]  vendors~another~index
              another.bundle.js   1.54 KiB       1  [emitted]  another
                index.bundle.js   1.54 KiB       2  [emitted]  index
                
...

观察打包后文件大小,可以看到index.bundle.jsanother.bundle.js中已经移除了重复的依赖模块。lodash 被分离到单独的vendors~another~index.bundle.jschunk中。

3.动态导入:通过模块的内联函数调用来分离代码。
略~。~

分离css

需要用到插件mini-css-extract-plugin,这个插件会将提取css到单独的文件,根据每个包含css的js文件创建一个css文件,因此,你的样式将不再内嵌到 JS bundle 中。如果你的样式文件大小较大,这会做更快提前加载,因为 CSS bundle 会跟 JS bundle 并行加载。同时还支持按需加载css和SourceMaps.

相较于旧版extract-text-webpack-plugin插件,mini-css-extract-plugin的优势有

  • 异步加载
  • 没有重复的编译
  • 更容易使用
  • Specific to CSS
  • 支持热更新
npm i mini-css-extract-plugin -D
//webpack.common.js
  const MiniCssExtractPlugin = require("mini-css-extract-plugin");
  module.exports = {
    ...
    plugins: [
      new MiniCssExtractPlugin({
        filename: "[name].css",
      })
    ],
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            {
              loader: MiniCssExtractPlugin.loader,
              options: {
                publicPath: '../'             //可以配置输出的css文件路径
              }
            },
            "css-loader"
          ]
        }
      ]
    }
    ...
  }

注意,这个插件不兼容style-loader (用于以<style>标签形式将css-loader内部样式注入到HTML页面)。

如果想在开发环境下使用style-loader,在生产环境分离css文件,可以这么配置:

//webpack.common.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== 'production'

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
    })
  ],
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader:"postcss-loader",    //本文未用到此loader...
            options: {           // 如果没有options这个选项将会报错 No PostCSS Config found
                plugins: (loader) => []
            }
          },
          'sass-loader',
        ],
      }
    ]
  }
}

很明显我们需要先安装处理样式文件的各种loader

npm i style-loader css-loader postcss-loader node-sass sass-loader -D

这里有个问题,node环境变量process.env.NODE_ENV在webpack.config中其实是undefined,之前提及的mode配置会自动定义这个环境变量,但只能在打包后的js中取到,如何在webpack的配置文件中获取这个值呢,需要引入cross-env

npm i cross-env -D

然后在package.json的脚本命令中指定环境变量

"start": "cross-env NODE_ENV=development webpack-dev-server --open --config webpack.dev.js",
"build": "cross-env NODE_ENV=production  webpack --config webpack.prod.js"

可自行添加css文件,在js中import,执行npm run build查看效果

当然也可以不获取process.env.NODE_ENV来区分环境,在dev.js和prod.js分别配置处理样式文件的rule就行了,这也是最开始我们分开写开发环境和生产环境的webpack配置文件的原因。这里提及只是方便从低版本webpack迁移到4.x。

在单个文件中提取所有CSS
配合optimization.splitChunks.cacheGroups使用

//webpack.common.js
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true
        }
      }
    }
  }

会额外生成一个styles.bundle.js

按照入口JS来分离css

//webpack.common.js
...

function recursiveIssuer(m) {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else if (m.name) {
    return m.name;
  } else {
    return false;
  }
}

module.exports = {
  entry:  {
    index: './src/index.js',
    another: './src/another-module.js'
  },

  ...

  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {                                //分离第三方库
          test: /[\\/]node_modules[\\/]/,
          name: 'lodash',
          chunks: 'all'
        },
        indexStyles: {
          name: 'index',                        
          test: (m,c,entry = 'index') => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
          chunks: 'all',
          enforce: true
        },
        otherStyles: {
          name: 'another',
          test: (m,c,entry = 'another') => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
          chunks: 'all',
          enforce: true
        }
      }
    }
  }
};

压缩css

webpack5可能会内置CSS压缩,webpack4需要使用像optimize-css-assets-webpack-plugin这样的插件。有个问题是设置optimization.minimizer后,会覆盖上文提到的mode配置项提供的默认值,因此需要同时使用JS压缩插件UglifyJsPlugin

npm i optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D
//webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.config.js');

const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = merge(common,{
    mode: "production",
    optimization: {
        minimizer: [
          new UglifyJsPlugin({
            cache: true,
            parallel: true,
            sourceMap: true // set to true if you want JS source maps
          }),
          new OptimizeCSSAssetsPlugin({})
        ],
    }
});

缓存

我们都知道浏览器获取资源是比较耗费时间的,所以它会使用一种名为 缓存 的技术。通过命中缓存,以降低网络流量,使网站加载速度更快。如果我们在部署新版本时不更改资源的文件名,浏览器就可能会认为它没有被更新,就会使用它的缓存版本。因此确保 webpack 编译生成的文件能够被客户端缓存,而在文件内容变化后,能够请求到新的文件是很有必要的。

通过使用 output.filename 进行文件名替换,可以确保浏览器获取到修改后的文件。[hash]替换可以用于在文件名中包含一个构建相关的hash,但是更好的方式是使用[chunkhash]替换,在文件名中包含一个 chunk相关的hash。遗憾的是chunkhash和热更新不兼容,所以开发环境和生产环境要分开配置。

//webpack.common.js
  ...
  output: {
      filename: devMode ? '[name].[hash:8].js': '[name].[chunkhash:8].js',       //数字8表示取hash标识符的前八位
      chunkFilename: devMode ? '[name].[hash:8].js': '[name].[chunkhash:8].js',  //异步模块的文件输出名
      path: path.resolve(__dirname, 'dist')
  },
  ...

关于[hash][chunkhash]的区别,简单来说,[hash]是编译(compilation)后的hash值,compilation对象代表某个版本的资源对应的编译进程。项目中任何一个文件改动,webpack就会重新创建compilation对象,然后计算新的compilation的hash值,所有的编译输出文件名都会使用相同的hash指纹,改一个就一起变。而[chunkhash]是根据具体模块文件的内容计算所得的hash值,某个文件的改动只会影响它本身的hash指纹,不会影响其他文件。

上文代码分离一节中已经提到了如何将第三方库(比如lodash或react)提取到单独的vendor chunk文件中,因为它们很少像本地的源代码那样频繁修改。利用客户端的长效缓存机制,可以消除请求,减少向服务器获取资源,同时还能保证客户端代码和服务器端代码版本一致。
除了第三方库,webpack在入口模块中,包含了某些样板(boilerplate),确切来说就是runtimemanifest。即webpack运行时的引导代码,这部分代码我们也将它单独提取出来。

//webpack.common.js
  ...
  optimization: {
    runtimeChunk: 'single',        //分离webpack运行时的引导代码
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
  ...

我们删掉another-module.js,修改index.js如下

///index.js
import _ from 'lodash';

function component() {
    var element = document.createElement('div');

    // Lodash, now imported by this script
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    return element;
}

document.body.appendChild(component());
执行npm run build

产生以下输出:

Hash: 131e8681e4403392cb5d
Version: webpack 4.16.1
Time: 744ms
              Asset       Size  Chunks             Chunk Names
  index.5bc56cae.js  260 bytes       0  [emitted]  index
vendors.5d8f5a63.js   69.5 KiB       1  [emitted]  vendors
runtime.eb6eb2fb.js   1.42 KiB       2  [emitted]  runtime
         index.html  316 bytes          [emitted]  
[1] ./src/index.js 253 bytes {0} [built]
[2] (webpack)/buildin/global.js 489 bytes {1} [built]
[3] (webpack)/buildin/module.js 497 bytes {1} [built]
    + 1 hidden module
Child html-webpack-plugin for "index.html":
     1 asset
    [2] (webpack)/buildin/global.js 489 bytes {0} [built]
    [3] (webpack)/buildin/module.js 497 bytes {0} [built]
        + 2 hidden modules

可以看到编译出的文件名后加上了hash,运行时的引导代码也被单独提取出来了。

接着添加一个print.js

///print.js
export default function print(text) {
   console.log(text);
};

修改index.js

///index.js
  import _ from 'lodash';
+ import Print from './print';

  function component() {
    var element = document.createElement('div');

    // Lodash, now imported by this script
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+   element.onclick = Print.bind(null, 'Hello webpack!');

    return element;
  }

  document.body.appendChild(component());
再执行npm run build

构建结果如下:

Hash: a710a54674ea8d4b3263
Version: webpack 4.16.1
Time: 3328ms
              Asset       Size  Chunks             Chunk Names
  index.15466585.js  327 bytes       0  [emitted]  index
vendors.7bde7828.js   69.5 KiB       1  [emitted]  vendors
runtime.eb6eb2fb.js   1.42 KiB       2  [emitted]  runtime
         index.html  316 bytes          [emitted]  
[1] (webpack)/buildin/global.js 489 bytes {1} [built]
[2] (webpack)/buildin/module.js 497 bytes {1} [built]
[3] ./src/index.js + 1 modules 406 bytes {0} [built]
    | ./src/index.js 337 bytes [built]
    | ./src/print.js 64 bytes [built]
    + 1 hidden module
Child html-webpack-plugin for "index.html":
     1 asset
    [2] (webpack)/buildin/global.js 489 bytes {0} [built]
    [3] (webpack)/buildin/module.js 497 bytes {0} [built]
        + 2 hidden modules

我们期望的是,只有 index bundle 的 hash 发生变化,然而vendors也跟着变了。这是因为每个 module.id 会基于默认的解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变。(官方文档里说runtime的hash也发生了变化,这里并未出现)
可以使用两个插件来解决这个问题。一是NamedModulesPlugin,将使用模块的路径而不是数字标识符。此插件有助于在开发过程中输出结果的可读性,但执行时间会长一些。二是使用webpack内置插件HashedModuleIdsPlugin,推荐用于生产环境构建:

  const webpack = require('webpack');
  ...
  module.exports = {
    ...
    plugins: [
      ...
      new webpack.HashedModuleIdsPlugin(),
      ...
    ],
    ...
  };

接下来,我们可以随意修改index.js的代码或者增删print.js,再进行构建查看hash的变化。

关于css缓存
假如index.js引用了一个index.css文件,它们会共用相同的chunkhash值。这时如果index.js更改了代码,index.css文件就算内容没有任何改变也会重复构建。
我们可以使用MiniCssExtractPlugin里的contenthash,保证css文件所处的模块里只要css文件内容不变,其本身就不会重复构建。

new MiniCssExtractPlugin({
  filename: "[name].[contenthash:8].css",
  chunkFilename: "[name].[contenthash:8].css"
}),

这样基本就完成了webpack的缓存配置。

有个小问题是,当修改index.css文件代码,重新构建后index.js的hash值也一起改变了。。。
尝试了下安装插件WebpackMd5Hash可以解决

npm i webpack-md5-hash -D

but,这个插件可能会引发别的bug,好吧这里先不用,后续补充,有兴趣可自行搜索

Babel

完成了基本的配置文件编写与代码分离,开发中需要用babel将旧的浏览器或环境中的es 2015+代码转换为es5。
需要安装一些依赖。

babel-core           //必备的核心库
babel-loader         //webpack loader配置必备
babel-preset-env     //支持es2015、2016、2017,
babel-preset-stage-0 //默认向后支持 stage-1,stage-2,stage-3,
babel-runtime        
babel-plugin-transform-runtime //转译新的API
npm i babel-runtime -S
npm i babel-core babel-loader babel-preset-env babel-preset-stage-0 babel-plugin-transform-runtime  -D

创建.babelrc文件

///.babelrc
{
  "presets": [
    "env",
    "stage-0"
  ],
  "plugins": [
    ["transform-runtime", {
      "helpers": false, //建议为false
      "polyfill": false, //是否切换新的内置插件(Promise,Set,Map等)为使用非全局污染的 polyfill,根据你的网站兼容性情况来看,开启会增加很多额外的代码
      "regenerator": true //是否切换 generator 函数为不污染全局作用域的 regenerator runtime。
    }],
  ]
}

关于 babel-polyfill 与 babel-plugin-transform-runtime

babel 可以转译新的 JavaScript 语法,但并不会转化BOM里面不兼容的API比如Promise,Set,Symbol,Array.from,async 等。这时就需要 polyfill(软垫片) 来转化这些API

babel-polyfill会仿效一个完整的 ES2015+ 环境,这样你就可以使用新的内置对象比如 Promise 或WeakMap, 静态方法比如 Array.from 或者 Object.assign, 实例方法比如 Array.prototype.includes 和生成器函数(提供 regenerator 插件)。babel-polyfill缺点是它通过改写全局prototype的方式实现,会污染全局对象所以不适合第三方库的开发,且打包后代码冗余量比较大,我们可能不需要用到所有的新API,对于现代浏览器有些也不需要polyfill。

babel-plugin-transform-runtime 依赖babel-runtime,babel编译es6到es5的过程中,babel-plugin-transform-runtime会自动polyfill es5不支持的特性,这些polyfill包就是在babel-runtime这个包里(这就是为啥babel-runtime 需要作为生产依赖引入(使用 --save))。transform-runtime优点是不会污染全局变量,多次使用只会打包一次,并且统一按需引入依赖,无重复、多余引入。缺点是例如"foobar".includes("foo")等实例方法将不起作用。

React

以react开发为例,如果是搭建新的项目,可以直接安装官方脚手架create-react-app或者使用阿里的开源ui框架 Ant Design
这里仅仅提一下如何在webpack中配置react开发环境

npm install react react-dom -S

还需要安装

babel-plugin-transform-decorators-legacy //支持修饰符语法 @connect
babel-preset-react   //解析react语法
react-hot-loader     //react热更新需要在babelrc做配置
///.babelrc
  {
  "presets": [
    "env",
    "react",
    "stage-0"
  ],
  "plugins": [
    ["transform-runtime", {
      "helpers": false, //建议为false
      "polyfill": false, //是否开始polyfill,根据网站兼容性决定是否开启
      "regenerator": true 
    }],
    "react-hot-loader/babel",     //react热更新插件
    "transform-decorators-legacy" //修饰符语法转换插件
  ]
}

如果之前webpack-dev-server配置正确,这时只要把你的根组件标记为热导出,就能启用react热更新

///index.js
  import React from 'react';
  import { hot } from 'react-hot-loader';
     
  const App = () => <div>Hello World!</div>
     
  export default hot(module)(App);

别忘了配置babel-loader

///webpack.common.js
  module: {
      rules: [{
          test: /\.jsx?$/,
          use: 'babel-loader'
      }]
  }

未完待续,容老夫喝口水先...


lxtmx
57 声望3 粉丝