webpack4

webpack4笔记

快速了解几个基本的概念

mode 模式

通过选择 development, productionnone 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production。

  • development:开发模式,会将process.env.NODE_ENV 的值设为 development 。启用NamedChunksPlugin 和 NamedMoudulesPlugin。
  • production:生产模式,会将process.env.NODE_ENV 的值设为 production 。启用FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,OccurrenceOrderPlugin,SideEffectsFlagPlugin 和UglifyJsPlugin
// webpack.config.js
module.exports = {
    mode: 'production',
}

入口文件(entry)

入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

默认值./src/index.js,可以在 webpack 的配置文件中配置入口,配置节点为: entry,当然可以配置一个入口,也可以配置多个。

// webpack.config.js
module.exports = {
+   entry: './src/index.js' //单入口
}
// webpack.config.js
module.exports = {
    entry: {    //多入口
        main: './src/index.js',
        other: './src/other.js'
    } 
}

输出(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件。

const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

loader

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

插件(plugins)

loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

安装webpack

使用webpack需要安装node.js,可直接在node.js官网下载安装

//查看node是否安装成功
$ node -v   //查看node版本 显示版本号表示安装完成
$ npm -v        //查看npm版本 node内置npm   
//创建项目文件
$ mkdir webpack-test && cd webpack-test //新建一个项目文件夹webpack-test
$ npm init -y   //创建一个默认的package.json文件[在项目文件夹下执行]

本地安装

$ npm install webpack webpack-cli -D  //本地安装

全局安装【不推荐】

在全局状态下安装webpack

$ npm install webpack webpack-cli -g  //全局安装【不推荐全局安装

注意:不推荐全局安装 webpack。这会将你项目中的 webpack 锁定到指定版本,并且在使用不同的 webpack 版本的项目中,可能会导致构建失败。

这里是使用npm来安装的,也可以使用cnpm,yarn

简单入门

项目结构

  webpack-test
+ |- package.json
+ |- /dist
+   |- index.html
+ |- /src
+   |- index.js

webpack4支持 0 配置打包

项目根目录执行[npx] webpack会默认将src/index.js打包到dist/main.js

默认配置文件

webpack4默认会在项目根目录找webpack.config.js或者webpackfile.js作为配置文件
也可以通过--config指定配置文件,例如: webpack --config webpack.config.test.js

基础配置

项目根目录新建webpack.config.js文件

//webpack.config.js
const path = require('path')

module.exports = {
    //入口文件
    entry: './src/index.js',
    //开发模式 开发:development, 生产:production
    mode: 'development',
    //出口文件
    output: {
        //打包完的文件名
    filename: "bundle.js",
    //打包后的路径
    path: path.resolve(__dirname,"dist"),
    //公共路径
    publicPath: '/',
    },
    //配置loader
    // module: {},
    //配置插件
    // plugins: []
}
  • entry 入口文件
//多入口
entry: {
    main: './src/index.js',
    other: './src/other.js'
} 
  • mode 模式
    development:开发模式,代码不压缩
    production:生产模式,会压缩代码

  • output 出口文件

//打包多入口文件,也可以通过[name]来生成不同的打包文件,
//[name]是entry中生成 通过[hash]可以生成hash戳
output: {
    filename: "bundle-[name].js",  //bundle-main.js  bundle-other.js
    path: path.resolve(__dirname,"dist"),  //修改path可以打包到对象的path文件夹下
}

通过上面webpack.config.js简单配置执行打包 [npx] webpack会将src/index.js打包到dist/bundle.js

webpack-dev-server

安装yarn add webpack-dev-server -D
webpack-dev-server会通过express(node框架)启动一个http服务

配置devServer

//webpack.config.js
module.exports = {
    //开发服务器配置
    devServer: {
        port: 3000,//修改端口号
        progress: true,//进度条
        contentBase: './dist',//默认访问目录
        overlay: true,//是否在页面展示错误,(创建脚手架一般都会开启)
        hot: true, //热更新
        // open: true,//自动打开浏览器
    },
}

配置script

每次通过[npx] webpack等命令比较麻烦,可以配置script

//package.json
{
  "name": "webpack-demo",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",

  "scripts": {
    "build": "webpack --config webpack.config.js",
    "dev": "webpack-dev-server --config webpack.config.js"
  },

  "devDependencies": {
    "webpack": "^4.41.0",
    "webpack-cli": "^3.3.9",
    "webpack-dev-server": "^3.8.1"
  }
}

这样就可以通过yarn build|devnpm run build|dev 执行相应的script

打包非js文件

打包html文件

安装yarn add html-webpack-plugin -D
html-webpack-plugin可以将htm模板打包

//webpack.config.js
const path = require('path')
const HtmlWabpackPlugin = require('html-webpack-plugin')

module.exports = {
    //入口文件
    entry: './src/index.js',
    //打包环境 开发:development, 生产:production
    mode: 'development',
    //出口文件
    output: {
        filename: "bundle.js",//打包完的文件名
        path: path.resolve(__dirname,"dist"),//打包后的路径
        publicPath: '/',//公共路径
    },
    //配置loader
    // module: {},
    //配置插件
    plugins: [
        new HtmlWabpackPlugin({
            template: './src/index.html',//打包html模板文件 源
            filename: 'index.html',//打包的文件名
            minify: {
                // removeAttrbuteQuotes: true,//删除双引号
                collapseWhitespace: true,//压缩成一行
            },
            // hash: true,//生成hash
        })
    ]
}

处理样式

css-loader,style-loader

当引入css文件,webpack是无法识别的,这就需要使用相应的css插件|loader
安装yarn css-loader style-loader -D
css-loader 使webpack能够解析css @import 'xx.css'
style-loader 将css插入到html文件head标签中

//配置loader
module: {
    rules: [
        //loader解析是从右向左执行
        //用法单一,一般一个loader处理一件事
        {
            test: /\.css$/,
            use: [
                {
                    loader: 'style-loader',//把css插入head标签中 【对象形式,可以添加参数】
                    options: {
                     // insertAt: 'top',//插入head位置 注:添加这个配置(还有wabpack文档中介绍的个别其他属性)报错,不明所以
                   }
                },
                {
                    loader: 'css-loader'
                }
                // 'css-loader',//解析@import 【字符串形式】
            ]
        }
    ]
}

上面通过配置module的rules,通过正则匹配.css文件,陪通过use指定使用相应的loader

loader大致分以下几种

  • pre 前置loader //设置loader可以添加options:{enforce:'pre'}
  • normal 普通loader //默认
  • 内链loader //import from 'expose-loader?!jquery'
  • post 后置loader //设置loader可以添加options:{enforce:'postloader'}

loader解析是从右向左[从后往前]执行,也可以设置options修改执行顺序(options:{enforce:'pre'}优先执行)
webpack需要先解析css,然后再插入到head标中,所以应该先配置style-laoder,再配置css-loader
使用sass,less样式预处理,需要安装相应的loader(例如:sass-loader[看下面配置],less类似),并配置在css-loader后面(需要先解析)

解析sass

配置sass-loader
安装yarn add sass-loader node-sass -D

{
    test: /\.css$/,
    use: [
        {
            loader: 'style-loader',//把css插入head标签中 【对象形式,可以添加参数】
            options: {
             // insertAt: 'top',//插入head位置 注:添加这个配置(还有wabpack文档中介绍的个别其他属性)报错,不明所以
           }
        },
        {
            loader: 'css-loader'
        }
    +   'sass-loader'
    ]
}

上面通过css-loader,style-loader可以将引入的css文件插入到html文件的head标签中,

抽离css到文件

还可以通过mini-css-extract-plugin插件将css抽离到文件中
安装yarn add mini-css-extract-plugin -D

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

plugins: [
    ///***
    new MiniCssExtractPlugin({  //将样式抽离到文件中
        filename: 'main.css'
    })
],
module: {
    rules: [
        //loader解析是从右向左执行
        //用法单一,一般一个loader处理一件事
        {
            test: /\.css$/,
            use: [
                {
                    loader: MiniCssExtractPlugin.loader   
                },
                {
                    loader: 'css-loader'
                }
                // 'css-loader',//解析@import 【字符串形式】
            ]
        }
    ]
}

css添加前缀

安装yarn add postcss-loader autoprefixer -D
css-loader后面添加postcss-loader

module: {
    rules: [
        //loader解析是从右向左执行
        //用法单一,一般一个loader处理一件事
        {
            test: /\.css$/,
            use: [
                {
                    loader: MiniCssExtractPlugin.loader   
                },
                {
                    loader: 'css-loader'
                }
                {
                    loader: 'postcss-loader'
                }
            ]
        }
    ]
}

postcss-loader会在根目录查找postcss.config.js作为配置
在根目录新建postcss.config.js文件

//postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}

打包图片

安装yarn add file-loader url-loader -D

  • js创建图片
    file-loader 默认会在内部生成一张图片,到build目录下,返回图片地址
import logo from './logo.png' //引入图片,返回新的图片地址

let img = new Image();
img.src = logo;
document.body.appendChild(img);
  • css中引入背景图片 可以直接引入
    background: url('./logo.png')

  • img标签引入 使用html-withimg-loader
    安装yarn add html-withimg-loader -D

//webpack.config.js
module: {
    rules: [
        {
            test: /\.html$/,
            use: 'html-withimg-loader'
        }
    ]
}

配置图片相应loader

module: {
    rules: [
        {
            test: /\.(png|jpg|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    limit: 10 * 1024,  //设置当图片小于10KB就转为base64
                    outputPath: '/img/'  //图片打包到img文件下
                }
            }
        },
    ]
}

开发相关辅助

babel转码js

虽然现代的浏览器已经兼容了96%以上的ES6的语法了,但是为了兼容老式的浏览器,我们需要把最新的ES6的语法转成ES5的。这时就可以使用babel-loader

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

配置

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {  //使用babel-loader es6 -> es5
                            presets: [  //也可以在配置在外面[.babelrc文件中配置]
                                [
                                    "@babel/preset-env",
                                    {
                                      "targets": {
                                        "chrome": "58",
                                        "ie": "10"
                                      }
                                    }
                                ]
                            ]
                        }
                    }
                ]
            }
        ]
    }

targets配置的意思就是让babel根据你写入的兼容平台来做代码转换。

使用@babel/polyfill

includes作为数组的实例方法,在某些浏览器其实是不支持的,babel默认的转换对于这种场景并不会做处理,同样不会处理的包括WeakMap, WeakSet, Promise等es6新引入的类,所以我们需要@babel/polyfill为我们这些实例方法等等打上补丁。
安装yarn add @babel/polyfill
很多项目会在入口文件顶部引入@babel/polyfill,或者指定webpack的entry为数组,第一项引入@babel/polyfill,这样配置是可以达到目录,但可能我们只使用了少量polyfill的api,这时全局引入就不太划算了,这时我们可以使用babel的useBuiltIns来配置。如下

//这里配置在.babelrc文件(项目根目录中新建`.babelrc`文件)  
{
  "presets": [
    [
      "@babel/preset-env",
      {
            "useBuiltIns": "usage",
        "targets": {
          "chrome": "58",
          "ie": "10"
        }
      },
    ]
  ]
}

babel帮我们做好了代码分析,在需要用到polyfill的地方引入这个单独的补丁,这样就实现了按需引入

class支持

当打包如下简单代码

class A{
    a = 1 //报错
}

这时可以安装yarn add @babel/plugin-proposal-class-properties -D插件

//.babelrc
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "targets": {
          "chrome": "58",
          "ie": "10"
        }
      },
    ]
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties"
  ]
}

装饰器@Decorator

当处理以下代码时也是会报错

@log  //报错
class A{
    a = 1 
}
function log(tag){
    console.log(tag)
}

安装相关插件yarn add @babel/plugin-proposal-decorators -D
配置

//.babelrc
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "targets": {
          "chrome": "58",
          "ie": "10"
        }
      },
    ]
  ],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose" : true }]
  ]
}

Babel优化

babel-loader可以配置如下几个options:

  • cacheDirectory:默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程(recompilation process)。如果设置了一个空值 (loader: 'babel-loader?cacheDirectory') 或者 true (loader: babel-loader?cacheDirectory=true),loader 将使用默认的缓存目录 node_modules/.cache/babel-loader,如果在任何根目录下都没有找到 node_modules 目录,将会降级回退到操作系统默认的临时文件目录。

  • cacheIdentifier:默认是一个由 babel-core 版本号,babel-loader 版本号,.babelrc 文件内容(存在的情况下),环境变量 BABEL_ENV 的值(没有时降级到 NODE_ENV)组成的字符串。可以设置为一个自定义的值,在 identifier 改变后,强制缓存失效。

  • forceEnv:默认将解析 BABEL_ENV 然后是 NODE_ENV。允许你在 loader 级别上覆盖 BABEL_ENV/NODE_ENV。对有不同 babel 配置的,客户端和服务端同构应用非常有用。

注意:sourceMap 选项是被忽略的。当 webpack 配置了 sourceMap 时(通过 devtool 配置选项),将会自动生成 sourceMap。

babel 在每个文件都插入了辅助代码,使代码体积过大.babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。 默认情况下会被添加到每一个需要它的文件中。你可以引入 babel runtime 作为一个独立模块,来避免重复引入。
安装
yarn add @babel/plugin-transform-runtime -D
yarn add @babel/runtime

@babel/plugin-transform-runtime插件是帮我们把一些babel的辅助方法由直接写入代码专为按需引入模块的方式引用

配置:

webpack.config.js

rules: [
  {
    test: /\.js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
    }
  }
]

修改.babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "targets": {
          "chrome": "58",
          "ie": "10"
        }
      },
    ]
  ],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose" : true }],
    "@babel/plugin-transform-runtime"
  ]
}

此时,webpack打包的时候,会自动优化重复引入公共方法的问题。

资源打包不同文件夹下

  • js
output: {
  filename: "js/[name].js",//打包完的文件名
  path: path.resolve(__dirname,"dist"),//打包后的路径
  // publicPath: '',//公共路径
},
  • css
//css打包到css文件夹下
plugins:[
    new MiniCssExtractPlugin({  //将样式抽离到文件中
        filename: 'css/main.css'   //打包到css文件夹
    }),
]
  • img
module: {
    rules: [
        {
            test: /\.(png|jpg|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    limit: 10 * 1024,  //设置当图片小于10KB就转为base64,否则用file-loader处理
                    outputPath: '/img/'  //打包到img文件夹
                }
            }
        },
    ]
}

module 配置补充

模块(module): 这些选项决定了如何处理项目中的不同类型的模块。

webpack 模块可以支持如下:

  • ES2015 import 语句
  • CommonJS require() 语句
  • AMD define 和 require 语句
  • css/sass/less 文件中的 @import 语句。
  • 样式(url(...))或 HTML 文件(<img src=...>)中的图片链接(image url)

module.noParse

值的类型: RegExp | [RegExp] | function

防止 webpack 解析那些任何与给定正则表达式相匹配的文件。忽略的文件中不应该含有 import, require, define 的调用,或任何其他导入机制。忽略大型的 library 可以提高构建性能。

module.exports = {
  mode: 'devleopment',
  entry: './src/index.js',
  ...
  module: {
    noParse: /jquery|lodash/,
    // 从 webpack 3.0.0 开始,可以使用函数,如下所示
    // noParse: function(content) {
    //   return /jquery|lodash/.test(content);
    // }
  }
  ...
};

module.rules

创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式。这些规则能够对模块(module)应用 loader,或者修改解析器(parser)。

module.exports = {
  ...
  module: {
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
  ...
};

module Rule

  • Rule 条件详解
    • 字符串:匹配输入必须以提供的字符串开始。是的。目录绝对路径或文件绝对路径。
    • 正则表达式:test 输入值。
    • 函数:调用输入的函数,必须返回一个真值(truthy value)以匹配。
    • 条件数组:至少一个匹配条件。
    • 对象:匹配所有属性。每个属性都有一个定义行为。

Rule.test

  • { test: Condition }:匹配特定条件。一般是提供一个正则表达式或正则表达式的数组,但这不是强制的。
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
  ...
};

其他的条件比如:

  • { include: Condition }:匹配特定条件。一般是提供一个字符串或者字符串数组,但这不是强制的。
  • { exclude: Condition }:排除特定条件。一般是提供一个字符串或字符串数组,但这不是强制的。
  • { and: [Condition] }:必须匹配数组中的所有条件
  • { or: [Condition] }:匹配数组中任何一个条件
  • { not: [Condition] }:必须排除这个条件
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.css$/,
        include: [
          path.resolve(__dirname, "app/styles"),
          path.resolve(__dirname, "vendor/styles")
        ],
        use: ['style-loader', 'css-loader']
      }
    ]
  }
  ...
};

Rule.use

应用于模块指定使用一个 loader。

Loaders can be chained by passing multiple loaders, which will be applied from right to left (last to first configured).

加载器可以链式传递,从右向左进行应用到模块上。

use: [
  'style-loader',
  {
    loader: 'css-loader'
  },
  {
    loader: 'less-loader',
    options: {
      noIeCompat: true
    }
  }
];

传递字符串(如:use: [ "style-loader" ])是 loader 属性的简写方式(如:use: [ { loader: "style-loader "} ])。

清理 dist 目录

每次构建,我们的 /dist 文件夹都会保存生成的文件,然后就会非常杂乱。

通常,在每次构建前清理 /dist 文件夹,是比较推荐的做法

clean-webpack-plugin 是一个比较普及的管理插件,让我们安装和配置下。

yarn add clean-webpack-plugin -D

webpack.config.js

  const path = require('path');
  ....
+ const CleanWebpackPlugin = require('clean-webpack-plugin');

  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
    plugins: [
+     new CleanWebpackPlugin()
      ...
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
    ...
  };

现在执行 npm run build,再检查 /dist 文件夹。如果一切顺利,你现在应该不会再看到旧的文件,只有构建后生成的文件!

由于最新版本变化@2.0.1之前的写法已经不能使用:new CleanWebpackPlugin(['/dist'])
官方文档地址:https://www.npmjs.com/package/clean-webpack-plugin
可以直接设置一个对象参考:
new CleanWebpackPlugin({cleanOnceBeforeBuildPatterns: ['**/*', '!static-files*']})

压缩代码

css使用optimize-css-assets-webpack-plugin插件,
js使用uglifyjs-webpack-plugin插件
安装yarn add optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D
配置

const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
    optimization: { //配置优化项
        minimizer: [
            new OptimizeCssAssetsWebpackPlugin(),
            new UglifyjsWebpackPlugin()
        ]
    }
    //也可以配置插件
    //plugins: [
    //  new OptimizeCssAssetsWebpackPlugin(),
    //  new UglifyjsWebpackPlugin()
    //]
}

定义环境变量

使用webpack.DefinePlugin插件可以定义变量
例如:

//webpack.config.js
plugins: [
    new webpack.DefinePlugin({
        DEV: JSON.stringify('dev'),    //定义字符串时,不能直接使用'dev',取结果:dev
    })
]

//index.js
if(DEV){
    ***
}

webpack-merge合并配置文件

可以为不同环境配置不同的配置文件,比如有
webpack.base.js //基础配置文件
webpack.dev.js //开发环境配置
webpack.prod.js //生产环境配置

//webpack.dev.js
let { smart } = reqiure('webpack-merge')
let base = require('./webpack.base.js')

module.exports = smart(base,{
    mode: 'development',
    //其他配置
})
//webpack.prod.js
let { smart } = reqiure('webpack-merge')
let base = require('./webpack.base.js')

module.exports = smart(base,{
    mode: 'production',
    //其他配置
})

全局变量

当我们引入第三方模块时,该模块作用域只在当前模块中。当需要设置全局时,可以添加loader处理
yarn add expose-loader -D

import $ from 'jquery'
console.log(window.$)  // -> underfine   【全局访问不到】
import $ from 'expose-loader?$!jquery'  //通过expose-loader将jquery导出为全局($)
console.log(window.$) // ƒ ( selector, context ) { *** }    【全局变量】

//或者配置webpack.config.js
module: {
    rules: [
        {
            test: require.reslove('jqeury'),
            use: 'expose-loader?$'
        }
    ]
}

另外还可以通过webpack.ProvidePlugin插件,让每个模块都插入某变量|模块

//webpack.config.js
const webpack = require('webpack')

plugins: [
    new webpack.ProvidePlugin({
        $: 'jquery'         //在每个模块都注入$(jquery)
    })
]

webpack跨域

  • 代理
devServer: {
    proxy: {
        '/api':{
            target: 'http:localhost:8081', 
            pathRewrite: {
                '/api': ''  //重写路径
            }
        }
    }
}
//接口:http:localhost:8081/user
//webpack服务http:localhost:8080
//以上配置,当请求'/api/user',直接转化了到http:localhost:8081/user
  • mock数据
devServer: {
    before(app){
        app.get('/user',(req,res) => {
            res.json({code: 200,msg: '成功'})
        })
    }
}
  • 使用webpack-dev-middleware
    安装yarn add webpack-dev-middleware
//server.js
let express = require('express')
let app = express();
let webpack = require('webpack')

//中间件
let middle = require('webpack-dev-middle')
//引入配置文件
let config = require('./webpack.config.js')
//处理配置文件
let compiler = webpack(config)
//使用中间件,当开启服务的时候,将webpack服务也一并开启
app.use(middle(compiler))


app.get('/user',(req,res) => {
    res.json({code: 200,msg: '成功'})
})

app.listen(300)

devtool映射文件

通过配置devtool可以配置映射文件

  • source-map
    单独生成source-map文件,出错显示行和列
    devtool: 'source-map'

  • eval-source-map
    不会生成单独的文件,出错会显示行和列
    devtool: 'eval-source-map'

  • cheap-module-source-map
    不产生列,但是一个单独映射文件(和文件没有关联)。可以保存起来调试使用
    devtool: 'cheap-module-source-map'

  • cheap-module-eval-source-map
    不生成文件,集成到打包后的文件中,不显示列
    devtool: 'cheap-module-eval-source-map'

watch监听(热更新)

当代码修改时,可以时时打包。
devServer也可以实现重新打包,但感觉有点慢

module.exports = {
    devServer: {
        hot: true, //热更新
    }
}

配置watch

module.exports = {
    watch: true,
    watchOptions: {  //监控选项
        poll: 1000,//1分钟询问1000次
        aggregateTimeout: 500,//防抖 输入停止后500ms后打包文件
        ignored: /node_module/, //不监控的文件
    }
}

解析(resolve)

配置模块如何解析。比如: import _ from 'lodash' ,其实是加载解析了lodash.js文件。此配置就是设置加载和解析的方式。

  • resolve.alias

创建 import 或 require 的别名,来确保模块引入变得更简单。例如,一些位于 src/ 文件夹下的常用模块:

// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
+ resolve: {
+   alias: {
+     vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
+     '@': path.resolve(__dirname, 'src/')
+   }
+ }
  ...
}

// index.js
// 在我们的index.js文件中,就可以直接import
import vue from 'vue';
// 等价于
import vue from  'src/lib/vue/dist/vue.esm.js';

  • resolve.extensions的应用

自动解析确定的扩展。

// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  resolve: {
    alias: {
      vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
      '@': path.resolve(__dirname, 'src/')
    },
+   extensions: [".js", ".vue",".json"]   // 默认值: [".js",".json"]
  }
  ...
}

给定对象的键后的末尾添加 $,以表示精准匹配

  • resolve一般配置
//webpack.config.js
module.exports = {
    resolve: { //解析第三方包
        modules: [
            path.resolve('node_modules'),   //在当前node_modules文件查找包
        ],
        mainFields: [ //查找字段 
            'style',
            'main'
        ],
        mainfiles: [  //入口文件名字
            'index.js'
        ],
        alias: { //别名
            bootstrap: 'bootstrap/dist/css/bootstrap.css'
        },
        extensions: [ //添加后缀  当import xx from './xx'
            '.js',   //先找xx.js
            '.css'   //没找到再找xx.css
        ]
    }
}

外部扩展(externals)

externals 配置选项提供了「从输出的 bundle 中排除依赖」的方法。 文档

例如,从 CDN 引入 jQuery,而不是把它打包:

index.html

<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous">
</script>

webpack.config.js

// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  alias: {
    extensions: [".js", ".vue",".json"]   // 默认值: [".js",".json"]
    vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
    '@': path.resolve(__dirname, 'src/')
  },
+ externals: {
+   jquery: 'jQuery'
+ },
  ...
}

这样就剥离了那些不需要改动的依赖模块,换句话,下面展示的代码还可以正常运行:

import $ from 'jquery';

$('.my-element').animate(...);

具有外部依赖(external dependency)的 bundle 可以在各种模块上下文(module context)中使用,例如 CommonJS, AMD, 全局变量和 ES2015 模块。外部 library 可能是以下任何一种形式:

  • root:可以通过一个全局变量访问 library(例如,通过 script 标签)。
  • commonjs:可以将 library 作为一个 CommonJS 模块访问。
  • commonjs2:和上面的类似,但导出的是 module.exports.default.
  • amd:类似于 commonjs,但使用 AMD 模块系统。

不同的配置方式:

externals : {
  react: 'react'
}

// 或者

externals : {
  lodash : {
    commonjs: "lodash",
    amd: "lodash",
    root: "_" // 指向全局变量
  }
}

// 或者

externals : {
  subtract : {
    root: ["math", "subtract"]   // 相当于: window.math.substract
  }
}

构建目标(targets)

webpack 能够为多种环境或 target 构建编译。想要理解什么是 target 的详细信息,请阅读 target 概念页面。

target: 告知 webpack 为目标(target)指定一个环境。

可以支持以下字符串值:

选项 描述
async-node 编译为类 Node.js 环境可用(使用 fs 和 vm 异步加载分块)
electron-main 编译为 Electron 主进程。
electron-renderer 编译为 Electron 渲染进程,使用 JsonpTemplatePlugin, FunctionModulePlugin 来为浏览器环境提供目标,使用 NodeTargetPlugin 和 ExternalsPlugin 为 CommonJS 和 Electron 内置模块提供目标。
node 编译为类 Node.js 环境可用(使用 Node.js require 加载 chunk)
node-webkit 编译为 Webkit 可用,并且使用 jsonp 去加载分块。支持 Node.js 内置模块和 nw.gui 导入(实验性质)
web 编译为类浏览器环境里可用(默认)
webworker 编译成一个 WebWorker

例如,当 target 设置为 "electron",webpack 引入多个 electron 特定的变量.

webpack.config.js

// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  alias: {
    extensions: [".js", ".vue",".json"]   // 默认值: [".js",".json"]
    vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
    '@': path.resolve(__dirname, 'src/')
  },
  externals: {
    jquery: 'jQuery'
  },
+ target: 'node'
  ...
}

相关的loader列表

webpack 可以使用 loader 来预处理文件。这允许你打包除 JavaScript 之外的任何静态资源。你可以使用 Node.js 来很简单地编写自己的 loader。

文件

  • raw-loader 加载文件原始内容(utf-8)
  • val-loader 将代码作为模块执行,并将 exports 转为 JS 代码
  • url-loader 像 file loader 一样工作,但如果文件小于限制,可以返回 data URL
  • file-loader 将文件发送到输出文件夹,并返回(相对)URL

JSON

  • json-loader 加载 JSON 文件(默认包含)
  • json5-loader 加载和转译 JSON 5 文件
  • cson-loader 加载和转译 CSON 文件

转换编译(Transpiling)

  • script-loader 在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不需要解析
  • babel-loader 加载 ES2015+ 代码,然后使用 Babel 转译为 ES5
  • buble-loader 使用 Bublé 加载 ES2015+ 代码,并且将代码转译为 ES5
  • traceur-loader 加载 ES2015+ 代码,然后使用 Traceur 转译为 ES5
  • ts-loaderawesome-typescript-loader 像 JavaScript 一样加载 TypeScript 2.0+
  • coffee-loader 像 JavaScript 一样加载 CoffeeScript

模板(Templating)

  • html-loader 导出 HTML 为字符串,需要引用静态资源
  • pug-loader 加载 Pug 模板并返回一个函数
  • jade-loader 加载 Jade 模板并返回一个函数
  • markdown-loader 将 Markdown 转译为 HTML
  • react-markdown-loader 使用 markdown-parse parser(解析器) 将 Markdown 编译为 React 组件
  • posthtml-loader 使用 PostHTML 加载并转换 HTML 文件
  • handlebars-loader 将 Handlebars 转移为 HTML
  • markup-inline-loader 将内联的 SVG/MathML 文件转换为 HTML。在应用于图标字体,或将 CSS 动画应用于 SVG 时非常有用。

样式

  • style-loader 将模块的导出作为样式添加到 DOM 中
  • css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
  • less-loader 加载和转译 LESS 文件
  • sass-loader 加载和转译 SASS/SCSS 文件
  • postcss-loader 使用 PostCSS 加载和转译 CSS/SSS 文件
  • stylus-loader 加载和转译 Stylus 文件

清理和测试(Linting && Testing)

  • mocha-loader 使用 mocha 测试(浏览器/NodeJS)
  • eslint-loader PreLoader,使用 ESLint 清理代码
  • jshint-loader PreLoader,使用 JSHint 清理代码
  • jscs-loader PreLoader,使用 JSCS 检查代码样式
  • coverjs-loader PreLoader,使用 CoverJS 确定测试覆盖率

框架(Frameworks)

  • vue-loader 加载和转译 Vue 组件
  • polymer-loader 使用选择预处理器(preprocessor)处理,并且 require() 类似一等模块(first-class)的 Web 组件
  • angular2-template-loader 加载和转译 Angular 组件
  • Awesome 更多第三方 loader,查看 awesome-webpack 列表

其他小插件

  • cleanWebpackPlugin
    每次打包会先删除文件
let CleanWebpackPlugin = require('clean-webpack-pulgin')
plugins: [
    new CleanWebpackPlugin('./dist'), //每次打包会先删除dist文件夹,可以传数组['./dist','./assets']
]
  • copyWebpackPlugin
    拷贝插件
let CopyWebpackPlugin = require('copy-webpack-pulgin')
plugins: [
    new CopyWebpackPlugin([
        {from: 'md',to:''},// 将md文件夹的内容拷贝到打包目录下
    ]),
]
  • bannerPlugin //webpack内置插件
    版权申明插件
let webpack = require('webpack')
plugins: [
    new webpack.BannerPlugin('make by 2020 Echo'), //打包出来的每个js文件头部都会插入 /*make by 2020 Echo*/
]

打包分析

webpack-bundle-analyzer插件可以帮助我们分析打包后的图形化的报表。

仅仅在开发环境使用。

安装yarn add webpack-bundle-analyzer -D

+ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  module.exports = {
    plugins: [
+     new BundleAnalyzerPlugin()
    ]
  }

自动生成一个网页报表,如下所示:
[图片上传失败...(image-33e3e3-1586335983662)]

优化

webpack自带优化

  • tree_shaking

使用import语法导入模块,只会打包用了的方法(删除掉没使用的代码)

例如:

//test.js
let fun1 = () => {}
let fun2 = () => {}
module.exports = {fun1,fun2}

//index.js
import test from './test.js'

test.fun1();//这里只用了fun1方法,当使用import导入,生产环境打包时fun2函数(没使用)并不会打包

使用es6(require)导入会把结果放到default

let test = require('./test.js')  
test.default.fun1()  
  • 省略无用代码
let a = 1,b = 2;  
console.log(a+b);
打包后:console.log(3)

动态链接库

使用webpack内置插件dllPulgin

当引入react,react-dom,打包会将其一起打包到一个文件夹,这样文件会比较大。
我们可以将react,react-dom打包到单独文件,或称为动态连接库,并在html文件中引入该文件

//新建webpack.react.js配置文件,用于打包react,react-dom
let path = require('path')
let webpack = require('webpack')

module.exports = {
    entry: {
        react: ['react','react-dom']
    },
    output: {
        filename: '_dll_[name].js', 
        path: path.resolve(__dirname,'dist'), 
        library: '_dll_[name]',   //打包的js前面添加 var _dll_react = (原先打包内容)
        // libraryTarget: 'var', //添加方式 var: var dll = ***; commmonjs: exports(dll) = ***; ...
    },
    mode: 'development',
    devServer: {
        port:3000,
        contentBase: './dist',
    },
    plugins: [
        new webpack.DllPlugin({
            name: '_dll_[name]',
            path: path.resolve(__dirname,'dist','manifest.json')
        })
    ]
}

打包完dist文件下生成_dll_react.jsmanifest.json两文件
然后在html文件中引入_dll_react.js

//项目文件
import React from 'react'
import { render } from 'react-dom'

项目中引入的react,react-dom还是会打包,所以需要添加配置,告知到动态连接库中查找

//项目正式配置文件webpack.config.js
let path = require('path')
let HtmlWebpackPulgin = require('html-webpack-plugin')
let webpack = require('webpack')
module.exports = {
    //配置多入口
    entry: {
        main: './src/react.js'
        // other: './src/other.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname,'dist')
    },
    mode: 'production',
    devServer: {
        port:3000,
        contentBase: './dist'
    },
    plugins: [
        new webpack.DllReferencePlugin({
            manifest: path.resolve(__dirname,'dist','manifest.json')
        }),
        new HtmlWebpackPulgin({
            template: './src/index.html',
            filename: 'index.html',
        })
    ],
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            '@babel/preset-env',
                            '@babel/preset-react'
                        ]
                    }
                },
                include: /src/,
                exclude: /node_modules/
            }
        ]
    }
}

还有前面提到的externals可以设置不打包的模块,CDN引入。

happypack多线程打包

安装yarn add happypack -D

//webpack.config.js
let Happypack = require('happypack')
module.exports = {

    module: {
        rules: [
            {
                test: /\.js$/,
                use: 'Happypack/loader?id=js'
            },
            {
                test: /\.css$/,
                use: 'Happypack/loader?id=css'
            }
        ]
    },
    plugins: [
        new Happypack({
            id: 'js',
            use: [
                {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            '@babel/preset-env',
                            '@babel/preset-react'
                        ]
                    }
                }
            ]
        }),
        new Happypack({
            id: 'css',
            use: [
                MiniCssExtractPlugin.loader, 
                "css-loader", 
                "postcss-loader"
            ]
        })
    ]
}

抽离公共模块

//webpack.config.js
module.exports = {
    optimization: {
        splitChunks: {  //分割代码块
            cacheGroups: {  //缓存组
                common: {   //公共模块
                    chunks: 'initial', //入口处开始
                    minSize: 0, //大于0的公用代码块
                    minChunks: 2,   //公用了2次以上
                }
                vendor: {  //第三方
                    priority: 1, //优先抽离
                    test: /node_modules/,
                    chunks: 'initial', //入口处开始
                    minSize: 0, //大于0的公用代码块
                    minChunks: 2
                }
            }

        }
    }
}

更过optimization配置请看webpack文档

最后

到此,笔记结束。如有错误,欢迎在指出。


如果觉得可以,帮忙点个赞。如有不对还忘指出,谢谢。
传送门git笔记

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容