开发环境到生产环境搭建Webpack

第webpack 简介

webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。
webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。


案例地址: https://github.com/pengjunshan/WebPJS/StudyWebpack

其它Web文章

CSS浮动的使用和解决浮动的五种方法
CSS定位relative、absolute、fixed使用总结
原生开发WebApi知识点总结
开发中常用jQuery知识点总结
C3动画+H5+Flex布局使用总结
ES6常用知识总结
Vue学习知识总结
待续......

本编文章会讲到的知识点

  • webpack初体验
    • 常用命令
    • HelloWebpack
  • webpack开发环境配置
    • 打包html资源
    • 单页面和多页面配置
    • 打包css资源
    • 打包图片资源
    • 打包其它资源
    • devServer自动化
    • 文件归类配置
  • webpack生产环境配置
    • 提取css样式文件
    • css兼容性处理
    • 压缩css
    • js语法检查
    • js兼容性处理
    • js和html压缩
    • 生产环境配置
  • webpack优化环境配置
    • HMR
    • source-map
    • oneOf
    • 缓存
    • tree shaking
    • code split
    • 懒加载
    • PWA
    • 多进程打包
    • externals
    • dll
    • optimization
  • 常用辅助工具
    • resolve
    • watch监控
    • 清除dist文件
    • copy文件到dist
    • BannerPlugin声明
    • 定义全局变量
    • webpack-merge

webpack初体验

常用命令

了解yarn和npm命令,至于yarn和npm的区别我就不多说.

npm yarn 功能
npm i yarn 构建项目
npm init -y yarn init -y 初始化项目
npm i vue -s yarn add vue 局部安装(会打包到代码中)
npm i vue -d yarn add vue -d 局部安装(不会打包到代码中)
npm i vue -g yarn add vue -g 全局安装依赖包
  • npm i xx -S和yarn add xx

会在package.json的dependencies属性下添加xx依赖包,生产环境需要用到的依赖包,比如vue、jquery项目运行时需要用的库。

  • npm i xx -D和yarn add xx -D

会在package.json的devDependencies属性下添加xx依赖包,生产环境不需要用到的依赖包,比如css-loader、less-loader转换css的依赖包。

  • npm i xx -g和yarn add xx -g

安装模块到全局,不会在项目node_modules目录中保存模块包。不会将模块依赖写入devDependencies或dependencies 节点。
比如用npm安装yarn: npm install yarn -g

HelloWebpack

1.新建一个文件夹 初始化项目,会生成一个package.json文件

yarn init -y
或者npm init -y

2.安装webpack webpack-cli依赖包

yarn add webpack webpack-cli -D
或者npm i webpack webpack-cli -D

package.json下会生成devDependencies中会有webpack、webpack-cli

{
  "name": "StudyWebpack",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "webpack": "^4.42.1",
    "webpack-cli": "^3.3.11"
  }
}

3.项目中新建src文件夹,src文件夹下新建index.js
4.项目中新建webpack.config.js文件
5.在webpack.config.js中写配置项

module.exports = {
    mode:'development',//开发环境  development:开发环境  production:生成环境
    entry:'./src/index.js',//入口文件
    output:{//出口配置
        filename:'index.js',//出口文件名
        path:__dirname+'/dist'//出口路径
    }
}

6.在项目下打开命令行窗口,运行npx webpack命令,webpack项目会自动先找到webpack.config配置项,然后根据配置信息进行打包,成功后项目中会多个dist文件,dist就是打包输出的结果。


7.注意:只要修改了webpack.config.js文件就要重新

webpack开发环境配置

打包html资源

1.安装 html-webpack-plugin

yarn add html-webpack-plugin -D
或者npm i html-webpack-plugin -D

2.在webpack.config.js中引入html-webpack-plugin插件并配置使用。详情看代码注释!


/*
  插件的使用步骤:
  plugins: 1. 下载  2. 引入  3. 使用
*/
//引入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    //模式
    mode:'development',
    //入口
    entry:'./src/index.js',
    //出口
    output:{
        filename:'bundle.js',//文件名
        path:__dirname+'/dist'//出口路径
    },
    //配置插件
    plugins:[
        // 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
        // 需求:需要有结构的HTML文件
        new HtmlWebpackPlugin({
            // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
            template:'./src/index.html'
        })
    ]

}

3.运行npx webpack 打包项目,dist文件下会生成index.html文件,并且index.html中自动引入了bundle.js文件


单页面和多页面配置

entry: 入口起点

  • 方式一:单入口 string -->'./src/index.js'
    打包形成一个chunk。 输出一个bundle文件。此时chunk的名称默认是 main。

  • 多入口 array -->['./src/index.js','./src/add.js']
    所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。

  • 方式三:多入口 object{index:'./src/index.js', add:'./src/add.js'}
    有几个入口文件就形成几个chunk,输出几个bundle文件,此时chunk的名称是 key。

module.exports = {
    mode: 'development',
    // entry:'./src/index.js',方式一
    // entry:['./src/index.js','./src/add.js'],方式二
    entry: {//方式三
        index: './src/index.js',
        add: './src/add.js',
        home:'./src/home.js'
    },

    //出口
    output: {
        //文件名 [name]根据入口的名称定义
        filename: 'js/[name].js',
        //输出文件目录(将来所有资源输出的公共目录)
        path: __dirname + '/dist',
        // 所有资源引入公共路径前缀
        publicPath: '/',
        // 非入口chunk的名称
        chunkFilename: 'js/[name]_chunk.js',
        // library: '[name]', // 整个库向外暴露的变量名
        // libraryTarget: 'window' // 变量名添加到哪个上 browser
        // libraryTarget: 'global' // 变量名添加到哪个上 node
        // libraryTarget: 'commonjs'
    },
    plugins: [
        //多出口 一般和多入口对应
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename:'index.html',
            //只引入index.js,不指定的话会引入index、home.js
            chunks:['index']
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename:'home.html',
            chunks:['home']
        }),
    ]
}
打包css资源

1.安装css-loader style-loader less-loader less,我这里只介绍css和less其它样式用同样的配置。

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

2.配置webpack.config.js,在module中配置css-loader和lessloader。详情看代码注释


//引入插件
const HtmlWbpackPlugin = require('html-webpack-plugin')

module.exports = {
    //模式
    mode:'development',//开发环境  development:开发环境  production:生成环境
    //入口
    entry:'./src/index.js',
    //出口
    output:{
        filename:'bundle.js',//出口文件名
        path:__dirname+'/dist'//出口文件路径
    },
    //插件配置
    plugins:[
        new HtmlWbpackPlugin({
            template:'./src/index.html',//配置一个模板html
            name:'index.html'//生成的html名称
        })
    ],
    //loader配置
    module:{
        //rules是个数组,因为不同的配置需要不同的loader处理
        rules:[
            {
                //匹配以.css结尾的文件
                test:/\.css$/,
                // use数组中loader执行顺序:从右到左,从下到上 依次执行
                use:[
                    // 创建style标签,将js中的样式资源插入进行,添加到head中生效
                    'style-loader',
                    // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
                    'css-loader'
                ]
            },
            {
                //匹配以.less结尾的文件
                test:/\.less$/,
                use:[
                    // 创建style标签,将js中的样式资源插入进行,添加到head中生效
                    'style-loader',
                    // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
                    'css-loader',
                    // 将less文件编译成css文件
                    'less-loader'
                ]
            }
        ]
    }
}
打包图片资源

1.安装loader,html-loader url-loader file-loader

yarn add file-loader url-loader html-loader -d

2.配置webpack.config,详情看代码注释。

解决使用html-loader处理html中引入图片失效,解决方法在url-loader中的options对象中中添加esModule:false属性。


const HtmlWebpckPlugin = require('html-webpack-plugin')
module.exports = {
    mode:'development',
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:__dirname+'/dist'
    },
    plugins:[
        new HtmlWebpckPlugin({
            template:'./src/index.html',
            name:'idnex.html'
        })
    ],
    module:{
        rules:[
            {
                test:/\.less$/,
                use:[
                    'style-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                //匹配图片结尾的文件,可手动添加
                test:/\.(png|jpg|jpeg|gif)/,
                // 使用url-loader
                loader:'url-loader',
                options:{
                    // 图片大小小于8kb,就会被base64处理
                    // 优点: 减少请求数量(减轻服务器压力)
                    // 缺点:图片体积会更大(文件请求速度更慢)
                    limit:8*1024,
                     // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
                    // 解析时会出问题:[object Module]
                    // 解决:关闭url-loader的es6模块化,使用commonjs解析
                    esModule:false,
                     // 给图片进行重命名
                    // [hash:10]取图片的hash的前10位
                    // [ext]取文件原来扩展名
                    name: '[hash:10].[ext]'
                }
            },
            {
                //匹配.html结尾的文件
                test:/\.html/,
                // 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
                loader:'html-loader'
            }
        ]
    }
}
打包其它资源

1.使用exclude属性,排除法
2.配置webpack.config,详情看代码注释


const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'bunder.js',
        path: __dirname + '/dist'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            name: 'index.html'
        })
    ],
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            //处理其它资源
            {
                //excude排除法 后期手动添加
                exclude: /\.(css|js|html|less|png|jpg|jpeg|gif)$/,
                //使用file-loader
                loader: 'file-loader',
                options: {
                    name: '[hash:10].[ext]'
                }
            }
        ]
    }
}
devServer自动化

开发服务器 devServer后,只会在内存中编译打包,不会有任何输出;自动编译,自动打开浏览器,修改代码后自动刷新浏览器;

1.安装webpack-dev-server

yarn add webpack-dev-server -D

方法一:配置webpack.config,详细配置请看代码注释


const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    mode:'development',
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:__dirname+'/dist'
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:'./src/index.html',
        })
    ],
     // 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
    // 特点:只会在内存中编译打包,不会有任何输出
    // 启动devServer指令为:npx webpack-dev-server
    devServer: {
        // 项目构建后路径
        contentBase:__dirname+'/dist',
        // 启动gzip压缩
        compress: true,
        // 端口号
        port: 3000,
        // 自动打开浏览器
        open: true
      }
}

方法二:在wepack.json中配置scripts

{
  "name": "StudyWebpack",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "build": "webpack",//打包
    "dev": "webpack-dev-server --open --port 3000 --host"//开发服务器 devServer
  }
文件归类配置

当src下的文件很多时显得很乱,应当给相应的文件归类;

  • outputPath属性为loader处理后的文件增加文件夹归类
  • output输出对象中 filename:'js/bundle.js',增加js文件夹

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    // 模式
    mode:'development',
    // 入口
    entry:'./src/js/index.js',
    // 出口
    output:{
        filename:'js/bundle.js',//输出路径增加js文件夹
        path:__dirname+'/dist'
    },
    //devServer自动化
    devServer:{
        contentBase:__dirname+'/dist',
        compress:true,
        port:3000,
        open:true
    },
    // 插件配置
    plugins:[
        new HtmlWebpackPlugin({
            template:'./src/index.html'
        })
    ],
    // loader配置
    module:{
        rules:[
            {   // 处理css
                test:/\.css$/,
                use:[
                    'style-loader',
                    'css-loader'
                ]
            },
            {   //处理less
                test:/\.less$/,
                use:[
                    'style-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
            {   //处理图片
                test:/\.(png|jpg|jpeg|gif)/,
                loader:'url-loader',
                options:{
                    limit: 8*1024,
                    name:'[hash:10].[ext]',
                    exModule:false,
                    outputPath:'imgs'//图片输出增加imgs文件夹
                }
            },
            {   //处理html中图片
                test:/\.html$/,
                loader:'html-loader'
            },
            {   //处理其它文件
                exclude:/\.(css|html|js|less|png|jpg|jpeg|gif)/,
                loader:'file-loader',
                options:{
                    name:'[hash:10].[ext]',
                    outputPath:'media'//其它文件增加输出文件夹
                }
            }
        ]
    }
}

webpack生产环境配置

提取css样式文件

1.安装mini-css-extract-plugin插件

yarn add mini-css-extract-plugin -D

2.配置webpack.config,详情看代码注释


const HtmlWebpackPlugin = require('html-webpack-plugin')
//1.引入mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
    mode:'development',
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:__dirname+'/dist'
    },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    // 'style-loader',
                    //3. 这个loader取代style-loader。作用:提取js中的css成单独文件
                    MiniCssExtractPlugin.loader,
                    'css-loader'
                ]
            },
            {
                test:/\.less$/,
                use:[
                    // 'style-loader',
                    //3. 这个loader取代style-loader。作用:提取js中的css成单独文件
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader'
                ]
            }
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:'./src/index.html'
        }),
        //2.配置插件
        new MiniCssExtractPlugin({
            //对输出的css文件进行文件夹配置和重命名
            filename:'css/bundle.css'
        })
    ]
}
css兼容性处理

1.安装postcss-loader postcss-preset-env

yarn add postcss-loader postcss-preset-env -D

2.package.json中配置browserslist

 "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  }

3.配置webpack.config,详情看代码注解


const HtmlWebpackPlugin = require('html-webpack-plugin')
//1.引入mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// 设置nodejs环境变量 默认是production环境
// process.env.NODE_ENV = 'development';

module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    // 'style-loader',
                    //3. 这个loader取代style-loader。作用:提取js中的css成单独文件
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    //帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式
                    {
                        // 使用loader的默认配置
                        // 'postcss-loader',
                        // 修改loader的配置
                        loader: 'postcss-loader',
                        options: {
                            ident: 'postcss',
                            plugins: () => [
                                // postcss 的插件
                                require('postcss-preset-env')()
                            ]
                        }
                    }
                ]
            }

        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        //2.配置插件
        new MiniCssExtractPlugin({
            //对输出的css文件进行文件夹配置和重命名
            filename: 'css/bundle.css'
        })
    ]
}
压缩css

1.安装optimize-css-assets-webpack-plugin插件

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

2.配置webpack.config,详情看代码注释


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

//1.引入optimize-css-assets-webpack-plugin
const OptimizeCssAssetsWebapckPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            ident: 'postcss',
                            plugins: () => [
                                require('postcss-preset-env')()
                            ]
                        }
                    }
                ]
            }

        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new MiniCssExtractPlugin({
            filename: 'css/bundle.css'
        }),
        //2.配置插件optimize-css-assets-webpack-plugin
        new OptimizeCssAssetsWebapckPlugin()
    ]
}
js语法检查

1.安装 eslint,eslint-loader,eslint-config-airbnb-base,eslint-plugin-import

yarn add eslint eslint-loader eslint-config-airbnb-base eslint-plugin-import

2.package.json中配置eslintConfig

"eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  }

3.配置webapck.config,详情解释看代码注释


const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    mode:'development',
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:__dirname+'/dist'
    },
    module:{
        rules:[
            {
                // 匹配以.js结尾的文件
                test:/\.js$/,
                // 排除node_modules下的文件,只检查自己写的代码,不检查第三方的代码
                exclude:/node_modules/,
                loader:'eslint-loader',
                options:{
                    //自动修复eslint错误
                    fix:true
                }

            }
        ]
    },
   plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ]
}
js兼容性处理

1.安装babel-loader @babel/core @babel/preset-env core-js

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

2.配置webpack.config,详情解释看代码注释

module: {
        rules: [
            /*
               js兼容性处理:babel-loader @babel/core 
               1. 基本js兼容性处理 --> @babel/preset-env
                   问题:只能转换基本语法,如promise高级语法不能转换
               2. 全部js兼容性处理 --> @babel/polyfill  
                   问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
               3. 需要做兼容性处理的就做:按需加载  --> core-js,就不需要@babel/polyfill包了
           */
            {
                // 匹配以.js结尾的文件
                test: /\.js$/,
                // 排除node_modules下的文件,只检查自己写的代码,不检查第三方的代码
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    // 预设:指示babel做怎么样的兼容性处理
                    presets: [
                      [
                        '@babel/preset-env',
                        {
                          // 按需加载
                          useBuiltIns: 'usage',
                          // 指定core-js版本
                          corejs: {
                            version: 3
                          },
                          // 指定兼容性做到哪个版本浏览器
                          targets: {
                            chrome: '60',
                            firefox: '60',
                            ie: '9',
                            safari: '10',
                            edge: '17'
                          }
                        }
                      ]
                    ]
                  }
            }
        ]
    }
js和html压缩

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    //生产环境下js代码自动压缩
    mode:'production',
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:__dirname+'/dist'
    },
    module:{
        rules:[
           
        ]
    },
   plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            // 压缩html代码
            minify: {
                // 移除空格
                collapseWhitespace: true,
                // 移除注释
                removeComments: true
              }
        })
    ]
}
生产环境配置

css和less有公共的loader代码,可以抽离公共loader代码,以对象扩展的方式引入;正常来讲,一个文件只能被一个loader处理,当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:先执行eslint 在执行babel;


const HtmlWebpackPlugin = require('html-webpack-plugin');
// 抽离css插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 压缩css插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// 复用loader
const commonCssLoader = [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
        // 还需要在package.json中定义browserslist
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [require('postcss-preset-env')()]
        }
    }
];
module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    },
    module: {
        rules: [
            {
                // 处理css
                test: /\.css$/,
                use: [...commonCssLoader]
            },
            {
                //处理less
                test: /\.less$/,
                use: [...commonCssLoader, 'less-loader']
            },
            /*
                正常来讲,一个文件只能被一个loader处理。
                当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
                    先执行eslint 在执行babel
            */
            {
                // 在package.json中eslintConfig --> airbnb
                test: /\.js$/,
                exclude: /node_modules/,
                // 优先执行
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    fix: true
                }
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                  presets: [
                    [
                      '@babel/preset-env',
                      {
                        useBuiltIns: 'usage',
                        corejs: {version: 3},
                        targets: {
                          chrome: '60',
                          firefox: '50'
                        }
                      }
                    ]
                  ]
                }
              },
              {
                test: /\.(jpg|png|gif)/,
                loader: 'url-loader',
                options: {
                  limit: 8 * 1024,
                  name: '[hash:10].[ext]',
                  outputPath: 'imgs',
                  esModule: false
                }
              },
              {
                test: /\.html$/,
                loader: 'html-loader'
              },
              {
                exclude: /\.(js|css|less|html|jpg|png|gif)/,
                loader: 'file-loader',
                options: {
                  outputPath: 'media'
                }
              }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            minify: {
                collapseWhitespace: true,
                removeComments: true
              }
        }),
        new MiniCssExtractPlugin({
            filename: 'css/bundle.css'
        }),
        new OptimizeCssAssetsWebpackPlugin()
    ]
}

webpack优化环境配置

HMR

作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块) 极大提升构建速度;
样式文件:可以使用HMR功能:因为style-loader内部实现了;
js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码; 注意:HMR功能对js的处理,只能处理非入口js文件的其他文件;
html文件: 默认不能使用HMR功能.同时会导致问题:html文件不能热更新了~ (不用做HMR功能);解决:修改entry入口,将html文件引入;

devServer: {
      contentBase: __dirname+'/dist',
      compress: true,
      port: 3000,
      open: true,
      // 模块热替换
      // hot: true
    }
source-map

source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

source-map:外部
错误代码准确信息 和 源代码的错误位置
inline-source-map:内联
只生成一个内联source-map,错误代码准确信息 和 源代码的错误位置
hidden-source-map:外部
错误代码错误原因,但是没有错误位置,不能追踪源代码错误,只能提示到构建后代码的错误位置
eval-source-map:内联
每一个文件都生成对应的source-map,都在eval, 错误代码准确信息 和 源代码的错误位置
nosources-source-map:外部
错误代码准确信息, 但是没有任何源代码信息
cheap-source-map:外部
错误代码准确信息 和 源代码的错误位置 ,只能精确的行
cheap-module-source-map:外部
错误代码准确信息 和 源代码的错误位置 ,module会将loader的source map加入
内联 和 外部的区别:

  • 外部生成了文件,内联没有
  • 内联构建速度更快
    开发环境:速度快,调试更友好
    速度快(eval>inline>cheap>...)
    eval-cheap-souce-map
    eval-source-map
    调试更友好
    souce-map
    cheap-module-souce-map
    cheap-souce-map
    综合推荐:eval-source-map / eval-cheap-module-souce-map
    生产环境:源代码要不要隐藏? 调试要不要更友好
    内联会让代码体积变大,所以在生产环境不用内联
    综合推荐:source-map / cheap-module-souce-map
// 开发模式
 mode: 'development',
devtool:'eval-source-map'
//生产模式
 mode: 'production',
devtool:'source-map'
oneOf

在不使用oneOf之前,一个文件被一个loader匹配到后还会继续向下匹配其它匹配不了的loader,这样性能不好,使用oneOf后当一个文件被一个loader匹配到后就不会向下继续匹配了;但是使用oneOf只会匹配一个loader,所以如果设置了一个文件两个loader的话需要分开写,不能写在同一个oneOf里面。

module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
            // 处理css
            test: /\.css$/,
           // 配置多个loader使用use
            use: [...commonCssLoader]
          },
          {
            //处理less
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          {
            test: /\.js$/,
                exclude: /node_modules/,
                // 配置单个loader使用loader
                loader: 'babel-loader',
                // 排除node_modules下的js
                exclude: /node_modules/,
                // 只匹配src下的js
                include: __dirname + '/src',
                // 优先执行
                enforce: 'pre',
                // 延后执行
                enforce: 'post',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
          }
        ]
      }
    ]
  }
缓存

当已经加载了js或css等其它后,再次使用的时候不需要再次进行请求了,而是使用缓存后的文件;

hash: 每次wepack构建时会生成一个唯一的hash值。
1.问题: 因为js和css同时使用一个hash值。
2.如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)

chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样。
1.问题: js和css的hash值还是一样的。
2.因为css是在js中被引入的,所以同属于一个chunk。

contenthash:根据文件的内容生成hash值。不同文件hash值一定不一样。
1.让代码上线运行缓存更好使用

/*
  缓存:
    babel缓存
      cacheDirectory: true
      --> 让第二次打包构建速度更快
*/
{
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                  '@babel/preset-env',
              ],
               // 开启babel缓存
              // 第二次构建时,会读取之前的缓存
              cacheDirectory:true
            }
          }
tree shaking

tree shaking:去除无用代码
前提:1. 必须使用ES6模块化 2. 开启production环境
作用: 减少代码体积

sideEffects配置
"sideEffects": false :所有代码都都可以进行tree shaking
副作用:可能会把css / @babel/polyfill 文件干掉;
"sideEffects": [".css", ".less"]跳过css、less文件(如果压过没有使用css也会打包进去)

code split

[name]:取入口文件的名称;

optimization:

  1. 可以将node_modules中代码单独打包一个chunk最终输出
  2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
懒加载

当一个index.js中引入了其它js文件,进入index.js时不希望立马加载其它js,用到其它js时再加载;

//当点击按钮时再加载utils.js文件
document.querySelector('#btn').onclick = function(){
  // 懒加载~:当文件需要使用时才加载~
  // 预加载 prefetch:会在使用之前,提前加载js文件  webpackPrefetch: true
  // 正常加载可以认为是并行加载(同一时间加载多个文件)  
  // 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
  // 慎用预加载,兼容性问题,移动端和低版本浏览器不支持
  import(/* webpackChunkName: 'test' */'./utils').then(({getName})=>{
    console.log(getName())
  })
}
PWA

1.安装workbox-webpack-plugin

yarn add workbox-webpack-plugin -D

2.plugins数组中加载插件

plugins: [
...
  new WorkboxWebpackPlugin.GenerateSW({
    /*
      1. 帮助serviceworker快速启动
      2. 删除旧的 serviceworker

      生成一个 serviceworker 配置文件~
    */
    clientsClaim: true,
    skipWaiting: true
  })
  ]
/*
  1. eslint不认识 window、navigator全局变量
    解决:需要修改package.json中eslintConfig配置
      "env": {
        "browser": true // 支持浏览器端全局变量
      }
   2. sw代码必须运行在服务器上
      --> nodejs
      -->
        npm i serve -g
        serve -s dist 启动服务器,将dist目录下所有资源作为静态资源暴露出去
*/
// 注册serviceWorker
// 处理兼容性问题
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(() => {
        console.log('sw注册成功了~');
      })
      .catch(() => {
        console.log('sw注册失败了~');
      });
  });
}
多进程打包

webpack是单线程模型的,也就是说Webpack一个时刻只能处理一个任务,不能同时处理多个任务。使用thread-loader进行多进程打包。

1.安装thread-loader

注意:把这个 thread-loader 放置在其他 loader 之前,options中放一些属性值;进程启动大概为600ms,进程通信也有开销;只有工作消耗时间比较长,才需要多进程打包;

yarn add thread-loader -D

2.配置js loader

  {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
            loader:'thread-loader',
            options:{
              workers:2//设置进程数量
            }
          },
          {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-env',
              ]
            }
          }
        ]
      }
externals

当html中引入了cdn第三方库,js中也Import导入了同个第三方库,我们就可以使用externals来配置打包;

//html中引入jquery的cdn
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>

//js中导入了jquery
import $ from 'jquery'

//webpack.config配置移除jquery打包进去
// 移除某些包被打包进来
    externals:{
        jquery:'jQuery'
    }
dll

使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包,然后再主项目中进行打包时直接引用就可以了,从而加快了打包速度。

1.新建webpack.dll.js文件
2.配置webpack.dll.js

const webpack = require('webpack')
module.exports = {
    //需要打包的第三方库
    entry:{
        jquery:['jquery'],
        // vue:['vue']
    },
    output:{
        filename:'[name].js',
        path:__dirname+'/dll',
        library:'[name]_[hash:10]'// 打包的库里面向外暴露出去的内容叫什么名字
    },
    plugins:[
        // 打包生成一个 manifest.json --> 提供和jquery映射
        new webpack.DllPlugin({
            name:'[name]_[hash:10]',//映射库暴露出的名称
            path:__dirname+'/dll/manifest.json'//文件地址
        })
    ],
    mode: 'production'
}

3.配置好webpack.dll.js后运行webpack --config webpack.dll.js命令行,项目下会生成dll文件夹,dll文件夹下会有第三方库的js和manifest.json文件;
4.安装add-asset-html-webpack-plugin插件

yarn add add-asset-html-webpack-plugin -D

5.配置webpack.config

const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
module.exports = {
    mode:'production',//开发环境  development:开发环境  production:生成环境
    plugins:[
        // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~
        new webpack.DllReferencePlugin({
            manifest:__dirname+'/dll/manifest.json'
        }),
        // 将某个文件打包输出去,并在html中自动引入该资源
        new AddAssetHtmlWebpackPlugin({
            filepath:__dirname+'/dll/jquery.js'
        })
    ],
}
optimization
  • 安装 terser-webpack-plugin插件
const TerserWebpackPlugin = require('terser-webpack-plugin')
 optimization: {
        splitChunks: {
          chunks: 'all'
          // 默认值,可以不写~
          /* minSize: 30 * 1024, // 分割的chunk最小为30kb
          maxSiza: 0, // 最大没有限制
          minChunks: 1, // 要提取的chunk最少被引用1次
          maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
          maxInitialRequests: 3, // 入口js文件最大并行请求数量
          automaticNameDelimiter: '~', // 名称连接符
          name: true, // 可以使用命名规则
          cacheGroups: {
            // 分割chunk的组
            // node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js
            // 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
            vendors: {
              test: /[\\/]node_modules[\\/]/,
              // 优先级
              priority: -10
            },
            default: {
              // 要提取的chunk最少被引用2次
              minChunks: 2,
              // 优先级
              priority: -20,
              // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
              reuseExistingChunk: true
            } 
          }*/
        },
        // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
        // 解决:修改a文件导致b文件的contenthash变化
        runtimeChunk: {
          name: entrypoint => `runtime-${entrypoint.name}`
        },
        minimizer: [
          // 配置生产环境的压缩方案:js和css
          new TerserWebpackPlugin({
            // 开启缓存
            cache: true,
            // 开启多进程打包
            parallel: true,
            // 启动source-map
            sourceMap: true
          })
        ]
      }

常用辅助工具

resolve
 resolve: {
        // 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
        alias: {
            $css: __dirname + '/src/css'
        },
        // 配置省略文件路径的后缀名只会匹配到一个, 一般js和css文件名相同,所以不建议匹配css文
        extensions:['.js', '.json', '.jsx', '.css']
    }

    import {addNum} from './add'//省略的后缀
watch监控
//自带功能 不需要安装
 //监控 实时打包
    watch:true,
    //监控的配置项
    watchOptions:{
        poll:1000,//每秒询问1000次
        aggregateTimeout:500,//防抖 停留500毫秒后再打包
        ignored:/node_modules/  //不需要监控的文件
    }
清除dist文件
  • 每次打包先清除dist目录下的文件
  • 安装clean-webpack-plugin -D
//清除文件
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
 plugins:[
        ...
        new CleanWebpackPlugin()
    ]
copy文件到dist
  • 打包时可以把一些文件夹的内容拷贝到dist文件夹
  • 安装 copy-webpack-plugin -D
//copy文件插件
const CopyWebpackPlugin = require('copy-webpack-plugin')
plugins:[
        ...
        new CopyWebpackPlugin([
            //把doc文件复制到dist文件夹中
            {from:'./doc',to:'./'}
        ])
      
    ]
BannerPlugin声明
  • webpack自带的插件
  • 在打包后的Js文件头加上注释声明
//webpack
const webpack = require('webpack')
 plugins:[
       ...
        new webpack.BannerPlugin('杭州柯林电气股份有限公司版权所有')
    ]
定义全局变量
//使用webpack自带插件DefinePlugin
 new webpack.DefinePlugin({
            DEV:'false',
            FLAGE:JSON.stringify('pjs')//如果想存字符串类型 需要使用JSON.stringify()
        })

if(DEV){
    console.log("正式环境")
}else{
    console.log("测试环境")
}
webpack-merge
  • 合并配置文件
  • 分别创建开发和生产两个文件
  • 安装 webpack-merge -D
//webpack.pro.js
let {smart} = require('webpack-merge')
let base = require('./webpack.config.js')
module.exports = smart(base,{
    mode:'production'
})
//webpack.dev.js
let {smart} = require('webpack-merge')
let base = require('./webpack.config.js')
module.exports = smart(base,{
    mode:'development'
})

推荐阅读更多精彩内容

  • 从V1迁移到V2由于使用的是webpack版本是2.2.1,所以针对原文做了一些修改。针对webpack2的修改部...
    yzc123446阅读 563评论 0 1
  • 1. 新建一个文件夹,命名为 webpack-cli , webpack-cli 就是你的项目名,项目名建议使用小...
    鲁大师666阅读 876评论 1 3
  • 深入浅出Webpack学习笔记 基本概念 常用的构建工具 所有的构建工具所做的工做大致一样,都是把源代码翻译转换成...
    IsaacHHH阅读 220评论 0 0
  • 初始化项目 进入一个文件夹作为项目的根目录 npm init 新建src, dist目录,package.json...
    love_program阅读 840评论 0 4
  • 2018.10.4 天气:晴 今日休息,偃旗息鼓,明日再战。 面包 、饼干 、冰激凌, 还有……大盘鸡、 凉皮 ...
    此处填名字阅读 28评论 0 0