47

webpack4.x常用配置

 4 years ago
source link: https://www.tuicool.com/articles/R3qIj2M
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

配套代码demo: https://github.com/FinGet/webpack4_demo。博客原文:[https://finget.github.io/2019/07/24/webpack4-0/](https://finget.github.io/2019/07/24/webpack4-0/)

打包多页面在git仓库里,并没有单独写出来。

初始化:

yarn init -y
yarn add webpack webpack-cli -D

webpack-cli的作用就是让我们能在命令行中使用 webpacknpx webpakck 这些指令。

webpack基础配置

// webpack 是node写出来,所以要按node的写法
const path = require('path');

module.exports = {
	mode: 'development', // 模式 默认值:production [development]
	entry: './src/index.js',  // 打包入口文件
	output: {
		filename: 'bundle.[hash:8].js', // 打包后的文件名
		path: path.resolve(__dirname, 'dist') // 这个路径必须是一个绝对路径,所以需要用path来解析一下
	}
}
// src/index.js
console.log('hello webpack4.0');

npx webpack 命令直接打包(npm5.2之后支持的语法)。会打包出一个 main.js 文件

默认支持js模块化:

// commonjs
// src/a.js
module.exports = 'FinGet'
// src/index.js
let name = require('./a.js');
console.log(name); // 'FinGet'
// ES6 module
// src/a.js
export default = 'FinGet'
// src/index.js
import name from './a.js'
console.log(name); // 'FinGet'

手动配置webpack

  • 默认配置文件的名字: webpack.config.js 。为啥叫这个名字呢?
// node_modules/webpack-cli/bin/config/config-yargs.js
...

module.exports = function(yargs) {
  yargs
	.help("help")
	.alias("help", "h")
	.version()
	.alias("version", "v")
	.options({
	  config: {
		type: "string",
		describe: "Path to the config file",
		group: CONFIG_GROUP,
		defaultDescription: "webpack.config.js or webpackfile.js", // 默认名字有两种
		requiresArg: true
	  },
...
  • 可以通过–config指定配置文件:

    npx webpack --config xxxx.js
    
  • 也可以在 package.json 中配置脚本

"script": {
  "build": "webpack --config webpack.config.my.js"
},

运行 npm run build

  • 如果在 package.json 中不设置config文件
"script": {
  "build": "webpack"
},

运行 npm run build -- --config webpack.config.my.js
-- 不能少!

  • 简化的webpack打包出来的文件:
    (function(modules) { // webpackBootstrap
      // The module cache 先定义一个缓存
      var installedModules = {};
      // The require function 配置了一个require方法
      function __webpack_require__(moduleId) {
    
        // Check if module is in cache 检查这个模块是否在缓存中
        if (installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }
        // Create a new module (and put it into the cache) 创建一个新的模块,并存入缓存
        var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };
    
        // Execute the module function
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    
        // Flag the module as loaded
        module.l = true;
    
        // Return the exports of the module
        return module.exports;
      }
      // Load entry module and return exports 加载入口模块
      return __webpack_require__(__webpack_require__.s = "./src/index.js");
    })({
    		// key-value  key -> 模块路径 | value -> 函数
        "./src/a.js": (function(module, exports) {
            eval("module.exports=\"FinGet\";\n\n//# sourceURL=webpack:///./src/a.js?");
        }),
        "./src/index.js": (function(module, exports, __webpack_require__) {
            eval("let name = __webpack_require__(/*! ./a.js */ \"./src/a.js\");\nconsole.log('hello webpack4.0');\nconsole.log(name);\n\n//# sourceURL=webpack:///./src/index.js?");
        })
    
    });
    

webpack-dev-server

yarn add webpack-dev-server -D

它会在内存中生成打包文件。

官方文档: https://webpack.docschina.org/configuration/dev-server/

本地服务

// webpack.config.js
// 开发服务器的配置 官方文档: https://webpack.docschina.org/configuration/dev-server/
devServer: {
	contentBase: './dist', // 告诉服务器从哪个目录中提供内容。只有在你想要提供静态文件时才需要。
	publicPath: './dist', // 将用于确定应该从哪里提供 bundle,并且此选项优先。 此路径下的打包文件可在浏览器中访问。
	port: 3000, // 端口
	progress: true, // 打包过程
	open: true, // 自动打开浏览器
	compress: true, // 一切服务都启用 gzip 压缩
	// host: '' , // 指定使用一个 host
	hot: true, // 启用 webpack 的 模块热替换 功能 依赖于HotModuleReplacementPlugin
}

plugins: [
  // 开启webpack全局热更新
  new webpack.HotModuleReplacementPlugin()
]

做代理

devServer : {
  	proxy: { // 代理
      '/api': {
          target: 'http://localhost:3000',
          pathRewrite: {'^/api' : ''}
      }
    }
}

mock数据

devServer: {
  // 前端mock数据 不存在跨域
  before(app) {
    app.get('/api/goods', (req, res) => {
 	  res.json({
 		code: 0,
 		list: [
 		  {id:1,name:'苹果'},
 		  {id:2,name:'香蕉'}
 	   ]
 	  })
 	})
  }
}

html-webpack-plugin

yarn add html-webpack-plugin -D

plugins: [
  new HtmlWebpackPlugin({
	template: path.resolve(__dirname,'./public/index.html'), // 模版路径
	filename: 'index.html', // 打包后的文件名
	title: 'webpack4.0', // 顾名思义,设置生成的 html 文件的标题
	/**  
		注入选项。有四个选项值 true, body, head, false
		
		true 默认值,script标签位于html文件的 
		body 底部body 同 true
		head script 标签位于 head 标签内
		false 不插入生成的 js 文件,只是单纯的生成一个 html 文件
	*/
	inject: true, 
	// favicon: 'xxx.ico' // 给生成的 html 文件生成一个 favicon
	minify: { // 压缩
		removeAttributeQuotes: true, // 去掉属性的双引号
		collapseWhitespace: true // 代码压缩成一行
	},
	hash: true, // hash选项的作用是 给生成的 js 文件一个独特的 hash 值,该 hash 值是该次 webpack 编译的 hash 值
	cahe: true, // 默认值是 true。表示只有在内容变化时才生成一个新的文件
	showErrors: true, // 如果 webpack 编译出现错误,webpack会将错误信息包裹在一个 pre 标签内,属性的默认值为 true 

	/**
		chunks 选项的作用主要是针对多入口(entry)文件。当你有多个入口文件的时候,对应就会生成多个编译后的 js 文件。那么 chunks 选项就可以决定是否都使用这些生成的 js 文件。
		chunks 默认会在生成的 html 文件中引用所有的 js 文件,当然你也可以指定引入哪些特定的文件。
	**/
	// chunks: ['index','index2'], 
  })
]

css样式

yarn add css-loader style-loader less less-loader -D

在index.js中引入css|less模块

// src/index.js
let name = require('./a.js');
require('./assets/css/index.css');
require('./assets/css/commom.less');
console.log('hello webpack4.0');
console.log(name);
module: { // 模块
  rules: [
	// loader的顺序 默认是从右往左,从上到下
    // css-loader 解析 @import 这种语法的
    // style-loader 将css引入html的head中 style标签
	// {test: /\.css$/, use: ['style-loader','css-loader']}
	{test: /\.(css|less)$/, 
	  use: [
		{
		  loader: 'style-loader',
		  options: {
			insertAt: 'top' // 插入顶部 这样就会被后面的样式覆盖
		  }
		},
		'css-loader',
		'less-loader'
	  ]
	}
  ]
},

mini-css-extract-plugin

yarn add mini-css-extract-plugin -D

上面打包css会把css文件以style标签的形式写入 index.html ,现在我们就来把它们单独打包成文件。

module: { // 模块
	rules: [
		{test: /\.(css|less)$/, 
			use: [
				MiniCssExtractPlugin.loader,
				'css-loader',
				'less-loader'
			]
		}
	]
}, 

plugins: [
  	new MiniCssExtractPlugin({
		filename: 'assets/css/index.css' // 打包到dist/assets/css/index.css
	})
]

css3 自动添加各种浏览器前缀:

yarn add postcss-loader autoprefixer -D

// postcss.config.js
module.exports = {
	plugins: [require('autoprefixer')({'browsers': ['> 1%', 'last 2 versions']})]
}

// webpack.config.js
module: { // 模块
	rules: [
		{test: /\.(css|less)$/, 
			use: [
				MiniCssExtractPlugin.loader,
				'css-loader',
				'postcss-loader',
				'less-loader'
			]
		}
	]
},

推荐用法是在 package.json 中设置:

Replace Autoprefixer browsers option to Browserslist config.  Use browserslist key in package.json or .browserslistrc file.
// postcss.config.js
module.exports = {
	plugins: [require('autoprefixer')]
}
"browserslist": [
   "> 1%",
   "last 2 versions",
   "not ie <= 8"
 ]

压缩css:

:warning:压缩css也要压缩js,不如js不会压缩。

yarn add optimize-css-assets-webpack-plugin terser-webpack-plugin -D

// webpack.config.js
mode:'production', // * development不会压缩
optimization: {
    minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
  },

处理js

yarn add babel-loader @babel/babel-core @babel/preset-env -D

:warning: @babel/babel-core@babel/preset-envbabel-core

babel-preset-env 不一样

// webpack.config.js
module: {
  rules: [
    {
      test: /\.js$/,
      exclude: '/node_modules',
      include: 'src',
      use: {
    	loader: 'babel-loader',
    	options: {
    	    // 配置预设
    		presets: [
    			'@babel/preset-env'
    		]
    	}
      }
    }
  ]
}

或者:

// .babelrc
{
  "presets": ["@babel/preset-env"]
}

处理高级版本js

babel-runtime 是供编译模块复用工具函数,是锦上添花。

babel-polyfil是雪中送炭,是转译没有的api。

详细讲解

// 示例
class Person{
	constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

let person = new Person('FinGet',24);
console.log(person.name);

// 高级api
console.log('FinGet'.includes('Get'));
yarn add @babel/plugin-transform-runtime -D
// @babel/runtime as a production dependency (since it's for the "runtime").
yarn add @babel/runtime

使用:

// .babelrc
"plugins": ["@babel/plugin-transform-runtime"]

@babel/runtime 会在打包的js中,注入一些脚本,所以要安装production依赖

yarn add @babel/polyfill

使用:

require("@babel/polyfill");
// or 
import "@babel/polyfill";
// or
// webpack.config.js
module.exports = {
  entry: ["@babel/polyfill", "./app/js"],
};

这样引入会导致打包出来的js很大

e2Ab6vb.jpg!web

按需引入:

// .babelrd
{
  "presets": [["@babel/preset-env", {
    "useBuiltIns": "usage" 
  }]],
}

关键点是 useBuiltIns 这一配置项,它的值有三种:

  • false : 不对 polyfills 做任何操作
  • entry : 根据 target 中浏览器版本的支持,将 polyfills 拆分引入,仅引入有浏览器不支持的 polyfill
  • usage (新):检测代码中ES6/7/8等的使用情况,仅仅加载代码中用到的 polyfills

zEvA7fJ.jpg!web

ESlint 检测语法

yarn add eslint eslint-loader -D

https://cn.eslint.org/demo/

在根目录下生成一个 .eslint.json 来配置规则。

{
    "parserOptions": {
        "ecmaVersion": 5,
        "sourceType": "script",
        "ecmaFeatures": {}
    },
    "rules": {
        // 允许console
        "no-console": "off",
        // 允许空语句块
        "no-empty": ["error", { "allowEmptyCatch": true }],
        // 强制关键字周围空格的一致性 (keyword-spacing)
        "keyword-spacing": "error",
        // 把 var 语句看作是在块级作用域范围之内
        "block-scoped-var": "error",
        // 要求遵循大括号约定
        "curly": "error",
        // switch 要有default分支
        "default-case": "error",
        // no-eq-null 禁止与null进行比较
        "no-eq-null": "error",
        // 禁止使用多个空格
        "no-multi-spaces": ["error", {"exceptions": { "Property": true }}],
        // 禁止多行字符串
        "no-multi-str": "error",
        // 禁止使用 new 以避免产生副作用
        "no-new": "error",
        // 数组中的空格
        "array-bracket-spacing": ["error", "never"],
        // 禁止或强制在代码块中开括号前和闭括号后有空格
        "block-spacing": "error",
        // 大括号风格要求
        "brace-style": ["error",  "1tbs", { "allowSingleLine": true }],
        // 逗号前后使用一致的空格
        "comma-spacing": ["error", { "before": false, "after": true }],
        // 逗号风格
        "comma-style": ["error", "last"],
        // 计算属性不使用空格
        "computed-property-spacing": ["error", "never"],
        // 函数标识符和其调用之间禁止空格
        "func-call-spacing": ["error", "never"],
        // 箭头函数函数体的位置
        "implicit-arrow-linebreak": ["error", "beside"],
        // tab缩进
        "indent": ["error", "tab", { "SwitchCase": 1 }],
        // 对象key-value空格
        "key-spacing": ["error", { "beforeColon": false }],
        // 行注释位置
        "line-comment-position": ["error", { "position": "above" }],
        // 类成员之间需要空行
        "lines-between-class-members": ["error", "always"],
        // 要求构造函数首字母大写
        "new-cap": "error",
        // 调用无参构造函数时带括号
        "new-parens": "error",
        // 禁止使用 Array 构造函数
        "no-array-constructor": "error",
        // 禁止使用内联注释
        "no-inline-comments": "error",
        // 禁止连续赋值
        "no-multi-assign": "error",
        // 不允许多个空行
        "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1 }],
        // 禁止使用 Object 构造函数
        "no-new-object": "error",
        // 禁用行尾空白
        "no-trailing-spaces": "error",
        // 禁止属性前有空白
        "no-whitespace-before-property": "error",
        // 强制在花括号内使用一致的换行符
        "object-curly-newline": ["error", { "ImportDeclaration": "always", "ExportDeclaration": "always" }],
        // 花括号中使用一致的空格
        "object-curly-spacing": ["error", "never"],
        // 要求在变量声明周围换行
        "one-var-declaration-per-line": ["error", "always"],
        // 禁止块内填充
        "padded-blocks": ["error", "never"],
        // 语句间填充空行
        "padding-line-between-statements": [
            "error",
            { "blankLine": "always", "prev": ["const", "let", "var"], "next": "*"},
            { "blankLine": "any", "prev": ["const", "let", "var"], "next": ["const", "let", "var"]},
            { "blankLine": "always", "prev": "directive", "next": "*" },
            { "blankLine": "any", "prev": "directive", "next": "directive" }
        ],
        // 强制使用一致的反勾号、双引号或单引号
        "quotes": ["error", "single", { "allowTemplateLiterals": true }],
        // 对象字面量属性名称使用引号
        "quote-props": ["error", "as-needed"],
        // 行尾分号
        "semi": ["error", "always"],
        // 分号前后空格
        "semi-spacing": ["error", {"before": false, "after": true}],
        // 分号位置
        "semi-style": ["error", "last"],
        // 语句块之前的空格
        "space-before-blocks": "error",
        // function空格
        "space-before-function-paren": ["error", {
            "anonymous": "always",
            "named": "never",
            "asyncArrow": "always"
        }],
        // 禁止圆括号内的空格
        "space-in-parens": "error",
        // 要求中缀操作符周围有空格
        "space-infix-ops": "error",
        // 要求或禁止在一元操作符之前或之后存在空格
        "space-unary-ops": ["error", {"words": true, "nonwords": false}],
        // 要求或禁止在注释前有空白
        "spaced-comment": ["error", "always"],
        // 强制在 switch 的冒号左右有空格
        "switch-colon-spacing": "error",
        // 要求正则表达式被包裹起来
        "wrap-regex": "error",

        // ES6
        // 要求箭头函数体使用大括号
        "arrow-body-style": "error",
        // 要求箭头函数的箭头之前或之后有空格
        "arrow-spacing": "error",
        // 禁止重复导入
    },
    "env": {}
}
{
  test: /\.js$/,
  loader: ['babel-loader', 'eslint-loader'],
}
// or 
{
  test: /\.js$/,
  use: {
    loader: 'eslint-loader',
    options: {
      enforce: 'pre' // 先执行
    }
  }
}

打包图片

  • js中创建图片引入
import jpg from './assets/snail.jpg'
let img = new Image();
img.src = jpg 

document.body.appendChild(img);
  • css背景图引入
body{
	background-image: url(../logo.svg);
}
  • html img标签引入
<img src="../src/assets/snail.jpg" alt="">

file-loader

yarn add file-loader -D

file-loader 会默认在内部生成生成一张图片,到dist目录下,把生成图片的名字返回回来

{
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
  use: 'file-loader' 
}

html-withimg-loader

html中直接使用img标签src加载图片的话,因为没有被依赖,图片将不会被打包。

yarn add html-withimg-loader

使用:

{
  test: /\.(htm|html)$/i,
  loader: 'html-withimg-loader'
}

url-loader

如果图片较多,会发很多http请求,会降低页面性能。这个问题可以通过url-loader解决。url-loader会将引入的图片编码,生成dataURl。相当于把图片数据翻译成一串字符。再把这串字符打包到文件中,最终只需要引入这个文件就能访问图片了。当然,如果图片较大,编码会消耗性能。因此url-loader提供了一个limit参数,小于limit字节的文件会被转为DataURl,大于limit的还会使用file-loader进行copy。

url-loader和file-loader是什么关系呢?简答地说,url-loader封装了file-loader。url-loader不依赖于file-loader,即使用url-loader时,只需要安装url-loader即可,不需要安装file-loader,因为url-loader内置了file-loader。

yarn add url-loader -D
{
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
  use: {
  	loader: 'url-loader',
  	options: {
  	    name: '[name].[ext]', // 保持名称不变
  		limit: 20*1024, // 小于20k的图片 打包成base64
  		outputPath: 'assets/' // 打包后的存放路径 dist/assets
  	} 
  }
},

output.publicPath

此选项指定在浏览器中所引用的「此输出目录对应的公开 URL」。相对 URL(relative URL) 会被相对于 HTML 页面(或 标签)解析。相对于服务的 URL(Server-relative URL),相对于协议的 URL(protocol-relative URL) 或绝对 URL(absolute URL) 也可是可能用到的,或者有时必须用到,例如:当将资源托管到 CDN 时。

module.exports = {
  //...
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'https://cdn.example.com/assets/'
  }
};

:warning:也可以单独给图片的options下配一个publicPath

source-map

生产模式会压缩代码成一行,就没法调试了。

...
️mode:'developmen', //production 不会生成map文件
entry: './src/index.js',
devtool: "source-map", // 增加映射文件
...

FBvAv22.png!web

webpack 高级配置

watch

...
entry: entry: './src/index.js',
// build 监控,变化自动打包
watch: true,
watchOptions: { // 监控选项
  poll: 1000, // 每秒 watch 1000次
  aggregateTimeout: 500, // 防抖
  ignored: '/node_modules/' //不需要监控的文件
},
...

clean-webpack-plugin

每次打包之前,都删除dist目录下的文件。

yarn add clean-webpack-plugin -D

clean-webpack-plugin: ^3.00

const {CleanWebpackPlugin} = require('clean-webpack-plugin');

plugins: [    
    new CleanWebpackPlugin()  
]
export { CleanWebpackPlugin };//3.0.0导出方式

export default CleanWebpackPlugin;//2.0.2导出方式
所以在2.0.2版本我们可以直接require拿到CleanWebpackPlugin 
const CleanWebpackPlugin = require('clean-webpack-plugin')

module.exports = CleanWebpackPlugin;//1.0.1导出方式

他会默认清空我们output里面设置的所有文件夹

https://github.com/johnagan/clean-webpack-plugin#options-and-defaults-optional

By default, this plugin will remove all files inside webpack's output.path directory, as well as all unused webpack assets after every successful rebuild.

copy-webpack-plugin

拷贝文件

yarn add copy-webpack-plugin -D
const CopyPlugin = require('copy-webpack-plugin');

plugins: [
  new CopyPlugin([
    { from: 'src/assets', to: 'assets' }
  ]),
]

BannerPlugin

版权声明

plugins:[
  new webpack.BannerPlugin('CopyRight by FinGet!')
]
// 会在打包文件头部加上 /*! CopyRight by FinGet! */
// 配置选项
{
  banner: string | function, // 其值为字符串或函数,将作为注释存在
  raw: boolean, // 如果值为 true,将直出,不会被作为注释
  entryOnly: boolean, // 如果值为 true,将只在入口 chunks 文件中添加
  test: string | RegExp | Array,
  include: string | RegExp | Array,
  exclude: string | RegExp | Array,
}

resolve

module.exports = {
  resolve: { // 解析第三方包
    alias: { // 创建 import 或 require 的别名,来确保模块引入变得更简单。
      '@': path.resolve(__dirname, 'src')
    },
    extensions: ['.js','.css','.json'], // 自动解析确定的扩展 就是没有后缀名时,按这个顺序匹配
    modules: [path.resolve('node_modules')] //告诉 webpack 解析模块时应该搜索的目录
  },
}

点这里查看 更多配置项。

DefinePlugin

定义一些全局变量。

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      PRODUCTION: JSON.stringify(true),
      VERSION: JSON.stringify('5fa3b9'),
      BROWSER_SUPPORTS_HTML5: true,
      TWO: '1+1',
      'typeof window': JSON.stringify('object'),
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
    });
  ]
}

注意,因为这个插件直接执行文本替换,给定的值必须包含字符串本身内的实际引号。通常,有两种方式来达到这个效果,使用 ‘“production”‘, 或者使用 JSON.stringify(‘production’)。

webpack-merge

分别配置不同的环境打包文件。

yarn add webpack-merge -D
// build/webpack.base.conf.js
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  entry: path.resolve(__dirname,'../src/index.js'), // 打包入口文件
  output: {
    filename: 'bundle.[hash:8].js', // 打包后的文件名
    path: path.resolve(__dirname, '../dist') // 这个路径必须是一个绝对路径,所以需要用path来解析一下
  },
  module: { // 模块
    rules: [
      {
        test: /\.(css|less)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 1,
            outputPath: 'assets/images' // 打包后的存放路径
          }
        }
      },
      {
        test: /\.(htm|html)$/i,
        loader: 'html-withimg-loader'
      }
    ]
  },

  // 配置插件

  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../public/index.html'), // 模版路径
      filename: 'index.html', // 打包后的文件名
      title: 'webpack4.0', // 顾名思义,设置生成的 html 文件的标题
      inject: true,
      hash: true, // hash选项的作用是 给生成的 js 文件一个独特的 hash 值,该 hash 值是该次 webpack 编译的 hash 值
      cahe: true, // 默认值是 true。表示只有在内容变化时才生成一个新的文件
      showErrors: true, // 如果 webpack 编译出现错误,webpack会将错误信息包裹在一个 pre 标签内,属性的默认值为 true 
    }),
    new MiniCssExtractPlugin({
      filename: 'assets/css/index.css'
    })
  ]
}
// build/webpack.pro.cong.js
const merge = require('webpack-merge');
const base = require('./webpack.base.conf');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

mmodule.exports = merge(base, {
	mode: 'production',
	optimization: {
      minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
    },
    plugins: [
  	  // 文件头部注入
      new webpack.BannerPlugin('CopyRight by FinGet!')
    ]
})
// build/webpack.dev.cong.js
const merge = require('webpack-merge');
const webpack = require('webpack');
const base = require('./webpack.base.conf');

mmodule.exports = merge(base, {
  mode: 'development',
  // 开发服务器的配置 官方文档: https://webpack.docschina.org/configuration/dev-server/
  devServer: {
    contentBase: path.resolve(__dirname, "../dist"), // 告诉服务器从哪个目录中提供内容。只有在你想要提供静态文件时才需要。
    // publicPath: './dist', // 将用于确定应该从哪里提供 bundle,并且此选项优先。 此路径下的打包文件可在浏览器中访问。
    port: 3000, // 端口
    progress: true, // 打包过程
    open: true, // 自动打开浏览器
  },
  devtool: 'source-map',
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
      }
    })
  ],
})
yarn run dev -- --config build/webpack.dev.conf.js


yarn run build -- --config build/webpack.pro.conf.js

webpack 优化

noParse

防止 webpack 解析那些任何与给定正则表达式相匹配的文件。

module.exports = {
  //...
  module: {
    noParse: /jquery|lodash/,
  }
};

happyPack 多线程打包

js、css、img都可以多线程打包,提高打包速度。

yarn add happypack -D
module.exports = {
 module:{
   {
     test: /\.js$/,
     use: 'Happypack/loader?id=js'
   }
 }
 plugins:[
   new Happypack({
     id: 'js', // id与上面对应
     use: [{
       loader: 'babel-loader',
       options: {
         presets:['@babel/preset-env']
       }
     }]
   })
 ]
}

tree-shaking

(webpack 自带)把没用的代码删除掉。

import 在生产环境下 会自动去掉没用的代码。es6 模块会把结果放到 default 上。

// test.js
let sum = (a,b) => {
  return a+b;
}
let minus = (a,b) => {
  return a-b;
}
export default {sum,minus}
import calc from './test.js';
console.log(calc.add(1,2));

let calc = require('./test');
console.log(calc.default.sum(1,2));

这里没用 minus 方法,在 import 方式打包中会 tree-shaking , require 则不会。

scope hosting

let a = 1;
let b = 2;
let c = 3;
let d = a + b + c;
console.log(d);

这个代码很啰嗦,在打包之后,webpack会自动分析,省略代码。

// 打包后
console.log(6);

抽离公共代码

多入口项目,多个入口,引用同一个js/css,则可以抽离公共代码。

module.exports = {
  optimization: {
    splitChunks: { // 分割代码块
      cacheGroups: { // 缓存组
        common: { // 公共模块
          chunks: 'initial',
          minSize: 0,
          minChunks: 2,
        }
      }
    }
  },
}
// 把第三方模块单独打包 比如jquery、lodash
module.exports = {
  optimization: {
    splitChunks: { // 分割代码块
      cacheGroups: { // 缓存组
        common: { // 公共模块
          chunks: 'initial',
          minSize: 0,
          minChunks: 2,
        },
        
        // 单独打包第三方模块
        vendor: {
          priority: 1, // 优先级别
          test: /[\\/]node_modules[\\/]/,
          chunks: 'initial',
          name(module, chunks, cacheGroupKey) {
            const moduleFileName = module.identifier().split('/').reduceRight(item => item);
            return `${moduleFileName}`;
          },
          minSize: 0,
          minChunks: 2
        }
      }
    }
  },
}

optimization参数介绍:

optimization: {
    splitChunks: { 
      chunks: "initial",         // 代码块类型 必须三选一: "initial"(初始化) | "all"(默认就是all) | "async"(动态加载) 
      minSize: 0,                // 最小尺寸,默认0
      minChunks: 1,              // 最小 chunk ,默认1
      maxAsyncRequests: 1,       // 最大异步请求数, 默认1
      maxInitialRequests: 1,     // 最大初始化请求书,默认1
      name: () => {},            // 名称,此选项课接收 function
      cacheGroups: {                // 缓存组会继承splitChunks的配置,但是test、priorty和reuseExistingChunk只能用于配置缓存组。
        priority: "0",              // 缓存组优先级 false | object |
        vendor: {                   // key 为entry中定义的 入口名称
          chunks: "initial",        // 必须三选一: "initial"(初始化) | "all" | "async"(默认就是异步)
          test: /react|lodash/,     // 正则规则验证,如果符合就提取 chunk
          name: "vendor",           // 要缓存的 分隔出来的 chunk 名称
          minSize: 0,
          minChunks: 1,
          enforce: true,
          reuseExistingChunk: true   // 可设置是否重用已用chunk 不再创建新的chunk
        }
      }
    }
  }

点击这里 查看更多配置。

懒加载

在用户触发一个点击操作才加载需要的文件。

// lazy.js
export default '懒加载';
// test.js
function handleClick() {
  import('./lazy.js').then(module => {
    console.log(module.default);
  })
}

最后

创建了一个前端学习交流群,感兴趣的朋友,一起来嗨呀!

Ebi2u2i.png!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK