Webpack的基本配置与使用

485 阅读9分钟

这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战

介绍

Webpack就是一个前端资源加载/打包工具,他会根据项目的模块依赖进行静态分析,然后将这些模块按照指定的规则打包生成对应的静态资源。Webpack会将多种静态资源js,css,ts,scss,less,png等文件打包成一个静态文件。减少了页面的请求。

初始化项目

cd 指定项目目录 
# 方法一: 自定义初始化项目配置,cmd会通过不断地询问实现自定义项目配置 
npm init 

# 方法二: 使用默认模板初始化项目 
npm init -y

这样我们的项目目录中就多了一个叫做package.json的项目描述文件,内部包含的常见项目描述信息有:项目名、版本号、脚本命令、项目描述、入口文件、开发者、产品模式三方库依赖、开发模式三方库依赖。

注意:

产品模式三方库依赖 "dependencies" 当前项目开发完毕后打包成产品后依然需要用到的三方库例如:jquery、boostrap、vue、react 、lodash

"dependencies": {     
    "jquery": "^3.5.1"   
}

开发模式三方库依赖 "devDependencies" 当前项目需要打包成完成产品时需要用到工具三方库例如: webpack、sass、babel 等等

安装webpack

# 缩写为 npm i -D webpack 
npm install --save-dev webpack 

# 缩写为 npm i -D webpack-cli 
# webpack 4+ 版本后webpack指令被移动到了 webpack-cli 中 
npm install --save-dev webpack-cli

注意

  1. 在项目中使用npm下载三方库时一定要根据具体需求添加指令参数: --save(缩写-S)--save-dev (缩写-D)

    • --save 指令会将当前npm下载三方库描述添加到package.json中的产品模式三方库依赖中

    • --save-dev 指令会将当前npm下载三方库描述添加到package.json中的开发模式三方库依赖

  2. 所有通过npm下载的三方库都会存放在当前项目node_modules文件夹中,该文件夹不会上传到github仓库中。因为该文件夹存放都是一些现有的三方库所以没必要浪费带宽和时间去上传这些已有的文件。这样会节省时间但是用户在下载前端工程时因为仓库本身没有node_modules文件所以下载下来的项目不能直接运行的(三方库不存在了)。解决方法:在当前项目目录中 使用: npm install 该指令会根据当前项目package.json文件中的额产品模式三方库依赖描述开发模式三方库依赖描述,自动的下载当前项目所需的三方库依赖。

  3. 删除三方库: npm uninstall 三方库名称 --save (无论三方库描述添加到--save中还是 --save-dev中删除都是使用 --save)

# 下载jQuery 
npm install --save jquery 

# 删除 缩写 npm un --save jquery 
npm uninstall --save jquery

Webpack的基本配置

  • 概念:在webpack项目中可以通过webpack.config.js文件(默认名称) 指定如何打包我们的项目webpack.config.js文件被称为webpack的配置文件。该文件可以设置很多配置选项给webpack添加不同的配置规则

  • 语法:

// webpack.config.js 
module.exports = {  // common.js语法 module.exports 公开一个模块 
    // 设置webpack配置选项
}

1. 入口entry

当前项目使用webpack打包时入口起点文件的相对路径。

entry支持多种写法:

  1. 单一入口字符串写法
module.exports = {     entry"./index.js", }
  1. 多文件对象写法
module.exports = {
    entry: {
        app1: "./src/foo.js",
        app2: './src/bar.js'
    } // 在 output filename中可以使用 '[name]'获取到对应文件的key值
}

2. 输出output

webpack打包完毕后输出文件的配置选项,该选项包括以下两点:

  • filename 用于输出文件的文件名。
  • path 输出目录的绝对路径。
// webpack 基于node.js,node.js遵循common.js规范
var path = require('path') // path模块是node.js自带核心模块,专门用来处理文件路径

module.exports = {
    entry: "./index.js",
    output: {
        // __dirname node.js 中的常量,表示当前文件绝对路径
        path: path.resolve(__dirname, 'dist'),
        // path.resolve路径拼接API(将两段字符串用路径拼接符拼接起来) 
        // 为了解决不同操作系统路径拼接符不同的问题 src/index.js   src\index.js
        filename: 'main.js'
    }
}

3. filename

支持模板字符

  • name 多文件打包时,每个文件路径对应key
  • hash 当前文件进行hash编码后的字符串
module.exports = {
    entry: {
        app1: "./src/foo.js",
        app2: './src/bar.js'
    },
    output: {
        path: __dirname + '/dist',
        filename: '[name].[hash].js'
    }
}

转义loader

webpack 可以使用 loader 来预处理文件。这允许你打包除 JavaScript 之外的任何静态资源。

1. css-loader

安装

npm install --save-dev css-loader style-loader

配置webpack module 选项中 指定loader规则

var path = require('path')

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'app.js'
    },
    module: {
        rules: [ // 对不同文件进行不同的规则处理
            { // 创建规则对象
                test: /\.css$/,
                // 文件名满足上面test指定正则规则,将会使用下方的loader对文件进行转义
                use: ['style-loader', 'css-loader']
            }
        ]
    },
    mode: 'production'
}

css-loader 配置选项,css-loader也可进行配置来增强css文件的能力

var path = require('path')

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'app.js'
    },
    module: {
        rules: [{
            test: /\.css$/,

            use: ['style-loader',
                // 此时css-loader变成对象,其内部接收options配置给css添加功能
                {
                    loader: 'css-loader',
                    options: {
                        url: false, // 是否打包编译css文件中url指定的文件,默认true
                        // 开启样式模块化,CSS将启用局部作用域。即使同名选择器不同模块下样式不会互相干扰 
                        modules: true
                    }
                }
            ]
        }]
    }
}

注意:使用 :local(无括号) 可以为此选择器启用局部模式。:global(.className) 可以用来声明一个明确的全局选择器。使用**:global(无括号)** 可以将此选择器切换至全局模式

.box {
    width: 100px;
    height: 100px;
    background-color: red;
    background-image: url(/cat.png);
}

:global(.label) {
    color: red;
    font-weight: 700;
}

:global .box {
    color: blue;
    font-size: 17px;
}

2.less-loader

安装

npm install --save-dev less-loader css-loader style-loader

配置webpack module 选项中 指定loader规则

var path = require('path')

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'app.js'
    },
    module: {
        rules: [{
            test: /\.less$/,
            // less会被编译为css,编译后的文件需要css编译
            use: ['style-loader', 'css-loader', 'less-loader']
        }]
    },
    mode: 'production'
}

3. sass-loader

安装

安装sass-loader后还需要安装node-sass,因为sass基于ruby开发需要node-sass让node.js支持sass环境

npm install --save-dev sass-loader node-sass css-loader style-loader

配置webpack module 选项中 指定loader规则

var path = require('path')

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'app.js'
    },
    module: {
        rules: [{
            test: /\.(sass|scss)$/,
            //有了node-sass支持 sass-loader会把scss文件编译为css,编译后的文件需要css编译
            use: ['style-loader', 'css-loader', 'sass-loader']
        }]
    },
    mode: 'production'
}

4. url-loader 与 file-loader

url-loader 将文件转化为base64编码

file-loader 将文件名进行md5加密打包打包输出的loader 安装

npm i -D file-loader url-loader

配置

module.exports = {
    entry: './src/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'app.js'
    },
    module: {
        rules: [{
                test: /\.(png|jpe?g|gif)$/,
                use: [

                    {
                        loader: 'url-loader',
                        options: {
                            limit: 8000
                            // 8000字节以下的文件全部编译成base64的编码
                            // 大于8000字节不做处理
                        }
                    }
                    // 因为url-loader没有处理8000字节以上的图片所以导致,webpack在打包大图片时直接报错
                    // 原因没有其他loader负责打包这些大图片
                    // 所以在使用url-loader时一定要安装file-loader支持大图片
                    // 'file-loader' file-loader无需在webpack规则中声明
                ]
            },

        ]
    },
    mode: 'production'
}

5. babel-loader

介绍

babel-loader是webpack中专门用来转化js的loader,常见的应用场景为将ES6转换为ES5jsx转化为js

安装

# webpack 4.0+ babel-loader 8.0+ 新版本
npm install --save-dev babel-loader @babel/core

# webpack 3.0+ babel-loader 8.0版本以下 旧版本
npm install --save-dev babel-loader babel-core

如果需要转义ES6向下兼容,需要安装es6向下兼容的配置模块

#  babel-loader 8.0+ 新版本
npm install --save-dev @babel/preset-env

#  babel-loader 8.0以下 旧版本
npm install --save-dev babel-preset-env

配置 ES6转ES5

module.exports = {
    entry: './src/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'app.js'
    },
    mode: 'development',
    module: {
        rules: [{
            test: /.js$/,
            use: [{
                loader: 'babel-loader',
                options: {
                    //@babel/preset-env babel-loader ES6向下兼容的配置模块
                    presets: ['@babel/preset-env']
                }
            }]
        }]
    }
}

配置 JSX 转 ES5

jsx开发中代码使用es6开发的我们可以将jsx转化为ES6之后再转化成ES5

如果需要转义jsx需要jsx babel支持模块

# babel-loader 8.0+ 新版本 
npm install --save-dev @babel/preset-react 

# babel-loader 8.0以下 旧版本 
npm install --save-dev babel-preset-react
module.exports = {
    entry: './src/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'app.js'
    },
    mode: 'development',
    module: {
        rules: [{
            test: /\.js$/,
            // exclude 哪些目录的文件不进行当前rules转义
            // node_modules 存放的都是三方库已经打包过的完整产品,所以不需要重复打包
            exclude: /(node_modules|bower_components)/,
            use: [{
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env', '@babel/preset-react']
                }
            }]
        }]
    }
}

模式mode

webpack以什么模式打包文件,默认"production"产品模式还可以设置为 "development"开发模式产品模式下代码会被压缩文件为.min.js。开发模式下代码不会被压缩。

module.exports = {
    entry: {
        app1: "./src/foo.js",
        app2: './src/bar.js'
    },
    output: {
        path: __dirname + '/dist',
        filename: '[name].[hash].js'
    },
    mode: "development" // 还可以设置为"production"压缩产品模式
}

插件Plugin

1. HtmlWebpackPlugin

自动生成HTML文件并引入webpack 打包文件的插件

安装

npm install --save-dev html-webpack-plugin

使用

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: './src/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'app.js'
    },
    plugins: [
        new HtmlWebpackPlugin()
    ]
}

// new HtmlWebpackPlugin()支持一些配置
new HtmlWebpackPlugin({
    title: 'GEC', // 生成的html title名,与template一起使用会失效
    filename: 'main.html', // 生成的html文件名
    // minify: false, // 生成的HTML是否压缩 true压缩html false不压缩 也可以自定义压缩规则
    minify: {
        removeAttributeQuotes: true
    }, // 相关配置网址 https://github.com/terser/html-minifier-terser#options-quick-referenc
    template: 'public/index.html', // 生成html文件参照模板
    inject: 'head' // webpack打包后的文件插入到新生成的html那个位置
    //true(默认值) 等价于 'body' 生成的文件插入到 body标签的最下方
    // false 不插入
    // 'head' 插入到<head>标签内部
})

2.SplitChunksPlugin

抽离公共模块插件,用来解决解决代码臃肿和重复引入问题在webpack 4.0版本之间使用CommonsChunksPlugin 插件, 4.0后被替换成SplitChunksPlugin。该模块基于webpack所以无需另外下载

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: './src/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'app.js'
    },
    module: {
        rules: [{
            test: /\.js$/,
            // exclude 哪些目录的文件不进行当前rules转义
            // node_modules 存放的都是三方库已经打包过的完整产品,所以不需要重复打包
            exclude: /(node_modules|bower_components)/,
            use: [{
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-react']
                }
            }]
        }]
    },
    optimization: {
        // SplitChunksPlugin 基本配置
        splitChunks: {
            chunks: "initial", // all sync 抽离异步加载模块 initial抽离初始化 自己匹配chunk名称
            minSize: 3000, // 最小大小,即当模块大于minSize.才会将将该模块抽离
            minChunks: 1, // 最小模块数量,当模块数量大于minChunks,才会抽离模块
            // maxAsyncRequests: 5, // 最大异步请求数量
            // maxInitialRequests: 3, // 最小初始化请求数量
            automaticNameDelimiter: '~',
            name: true,
            cacheGroups: { // 缓存组,指定需要抽离的模块存放的位置
                vendors: {
                    test: /[\\/]node_modules[\\/]/
                }
            }
        }
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: 'GEC', // 生成的html title名 
            filename: 'main.html', // 生成的html文件名
            minify: {
                removeAttributeQuotes: true
            }, // 生成的HTML是否压缩
            template: 'public/index.html', // 生成html文件参照模板
            inject: 'body' // webpack打包后的文件插入到新生成的html那个位置
        })
    ],
    mode: 'production'
}

3.模块热替换插件

作用是在开发中.实时预览修改后的页面,无需重新加载整个页面。加载项目时保存整个应用程序的状态。只更新变更的内容(这个插件的效果只能devServer一起使用)

该插件属于webpack自身内部。所以安装了webpack的项目无需下载

const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require("webpack") // 热模块替换插件本身存在webpack内部
module.exports = {
    entry: './src/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'app.js'
    },
    plugins: [
        new HtmlWebpackPlugin(), // 多个插件在plugins配置中使用逗号隔开就好了
        new webpack.HotModuleReplacementPlugin()
    ]
}

热加载模块使用时一定要在dev-server中配置 hot选项为true

开发服务器devServer

安装webpack服务器依赖模块webpack-dev-server

npm install --save-dev webpack-dev-server

配置开发用服务器

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.join(__dirname, "dist"),
        filename: 'app.js'
    },
    plugins: [
        new HtmlWebpackPlugin()
    ],
    devServer: { // 测试用服务器
        contentBase: path.join(__dirname, "dist"), // 项目文件地址
        compress: true, // 是否启用服务器gzip加速优化服务器
        host: '0.0.0.0', // 允许使用本地ip地址访问,局域网内可以访问当前项目
        port: 1234, // 本地测试服务器端口号
        before(app) {
            //测试用服务器中间件,可以给当前服务器添加一些 api接口
            app.get('/api/user', function (req, res) {
                res.json({
                    user: "GEC"
                })
            })

            app.get('/api/data', function (req, res) {
                res.json({
                    message: "这是服务器发给客户端消息"
                })
            })
        },
        proxy: { // 服务器代理,当请求了代理设置的路径时 会自动跳转到指定服务器上,解决跨域问题
            "/search": {
                target: 'http://musicapi.leanapp.cn/',
                changeOrigin: true
                // 当你请求 /search?123123 时 会代理到 'http://musicapi.leanapp.cn/search?123123'
            }

        },
        hot: true //开启热加载
    } // 注意启动服务器的命令不是 npx webpack (将项目打包成产品) 
    // 启动服务器指令 npx webpack-dev-server (这个指令需要在项目中安装webpack-dev-server 库)
}

启动服务器

npx webpack-dev-server 
# 启动服务器指令可以添加一些参数
#例如开启热加载可以不用再config文件中设置
npx webpack-dev-server --hot

# 启动服务器指令可以接受多个参数 开启热加载 允许ip地址访问 服务器启动成功时自动打开浏览器访问当前项目
npx webpack-dev-server --hot --host 0.0.0.0 --open

现在 我们webpack项目 已经学习了2个指令 npx webpack/ npx webpack-dev-server我们发现指令比较长有比较麻烦。我们推荐将这两个脚本命令保存在项目配置中。简化启动指令。方法在项目的 package.json 文件 scripts选项中保存指令

{
    "name": "dev_server_demo",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack",
        "server": "webpack-dev-server"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "html-webpack-plugin": "^4.3.0",
        "webpack": "^4.44.1",
        "webpack-cli": "^3.3.12",
        "webpack-dev-server": "^3.11.0"
    }
}
// 使用 打包 npm run build  等价于 npx webpack
// 启动服务器  npm run server 等价于 npx webpack-dev-server