WebPack 模块化打包工具(下)

2024-03-16 16:06:50 浏览数 (2)

本篇博文的内容根据 入门 Webpack,看这篇就够了 该篇文章总结而来,其代码、模块示例、功能拓展部分均有所删减,若是想了解更多关于 WebPack 的详细内容,敬请参考原文

在 WebPack 模块化打包工具(上) 这篇文章当中,我们已经能成功使用 webpack 打包了文件,并配置了devtooldevserver选项,在这篇文章当中,我们将介绍更多关于 webpack 的用法

Loaders

通过使用不同的 Loaders,webpack 有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scsscss,或者把 ES6 或ts文件转换为现代浏览器兼容的js文件,对 React 的开发而言,合适的 Loaders 可以把 React 的中用到的jsx文件转换为js文件

Loaders 需要单独安装并且需要在webpack.config.js 中的modules关键字下进行配置,Loaders 的配置包括以下几方面:

  • test:一个用以匹配 loaders 所处理文件的拓展名的正则表达式(必须)
  • loader:loader的名称(必须)
  • include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选)
  • query:为 loaders 提供额外的设置选项(可选)

我们通过安装和配置 Babel 依赖包来进一步了解 Loaders 吧,我们需要安装拥有核心功能的babel-core包,解析 ES6 的babel-env-preset包和解析 JSX 的babel-preset-react包,键入以下命令一次完成安装

代码语言:javascript复制
// npm一次性安装多个依赖模块,模块之间用空格隔开
npm i babel-core babel-loader babel-preset-env babel-preset-react -D

安装完成之后,我们需要在 webpack 文件中配置 Babel

代码语言:javascript复制
// webpack.config.js
module.exports = {
    devtool: 'eval-source-map',
    entry:  __dirname   "/app/main.js", 
    output: {
        path: __dirname   "/public",
        filename: "bundle.js"
    },
    devServer: {
        contentBase: "./public", //本地服务器所加载的页面所在的目录
        historyApiFallback: true, //不跳转
        inline: true //实时刷新
    },
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            }
        ]
    }
}

现在 webpack 的配置已经允许我们使用 ES6 以及 JSX 的语法了,我们也是使用之前的例子进行测试,不过这次我们会使用到 React,所以还需要安装一下 React 的依赖包,并在app文件夹下新建config.json的文件

代码语言:javascript复制
npm i react react-dom -D
代码语言:javascript复制
{
    "greetText": "Love and Peace from JSON!"
}
代码语言:javascript复制
//Greeter.js
import React, {Component} from 'react'
import config from './config.json';

class Greeter extends Component{
    render() {
        return (
            <div>
                {config.greetText}
            </div>
        );
    }
}

export default Greeter
代码语言:javascript复制
// main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';

render(<Greeter />, document.getElementById('root'));

运行npm satart命令进行编译打包,再运行npm run server命令启动本地服务器

运行结果运行结果

虽然我们当前项目中 Babel 的配置选项很少,完全可以写在webpack.config.js文件当中,当实际项目中,我们是会对 Babel 进行各种各样的配置的,这时候就不适合继续写在webpack.config.js文件中了,所以我们将 Babel 的配置独立到一个.babelrc文件中,webpack 会自动读取.babelrc文件里的 Babel 配置选项

代码语言:javascript复制
// webpack.config.js
module.exports = {
    devtool: 'eval-source-map',
    entry:  __dirname   "/app/main.js", 
    output: {
        path: __dirname   "/public",
        filename: "bundle.js"
    },
    devServer: {
        contentBase: "./public", //本地服务器所加载的页面所在的目录
        historyApiFallback: true, //不跳转
        inline: true //实时刷新
    },
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            }
        ]
    }
}
代码语言:javascript复制
// .babelrc
{
    "presets": ["env", "react"]
}

CSS Modules

JavaScript 模块化处理相信大家已经很熟悉了,而 CSS 同样也能进行模块化处理,webpack 提供的css-loaderstyle-loader可以对样式表进行处理,css-loader使你能够使用类似@importurl(...)的方法实现require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入 webpack 打包后的 JS 文件中

代码语言:javascript复制
npm i style-loader css-loader -D
代码语言:javascript复制
// webpack.config.js
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: "style-loader"
                    },
                    {
                        loader: "css-loader"
                    }
                ]
            }
        ]
    }
};

注意这里对同一个文件引入多个 loader 的方法

随后,我们在app文件夹里创建一个名字为main.css的文件,并设置如下样式

代码语言:javascript复制
/* main.css */
html {
  box-sizing: border-box;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  margin: 0;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

h1, h2, h3, h4, h5, h6, p, ul {
  margin: 0;
  padding: 0;
}

#root {
  color: blue;
}

我们项目中用到的 webpack 只有单一的入口,其它的模块需要通过import, require, url等方式与入口文件建立其关联,为了让 webpack 能找到main.css文件,我们需要把它导入main.js

代码语言:javascript复制
// main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
import './main.css'; //使用require导入css文件

render(<Greeter />, document.getElementById('root'));
运行结果运行结果

Webpack 对 CSS 模块化提供了非常好的支持,只需要在 CSS loader中进行简单配置即可,然后就可以直接把 CSS 的类名传递到组件的代码中,这样做有效避免了全局污染

代码语言:javascript复制
// webpack.config.js
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: "style-loader"
                    },
                    {
                        loader: "css-loader",
                        options: {
                            modules: true, // 指定启用css modules
                            localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
                        }
                    }
                ]
            }
        ]
    }
};

我们在app文件夹下创建一个Greeter.css文件来进行一下测试

代码语言:javascript复制
/* Greeter.css */
.root {
  background-color: #eee;
  padding: 10px;
  border: 3px solid #ccc;
}

导入.rootGreeter.js

代码语言:javascript复制
//Greeter.js
import React, {Component} from 'react'
import config from './config.json';
import styles from './Greeter.css'; //导入

class Greeter extends Component{
    render() {
        return (
            <div className={styles.root}>
                {config.greetText}
            </div>
        );
    }
}

export default Greeter
运行结果运行结果

关于 CSS Modules 的更多用法,可以去官方文档了解更多

我们再介绍一个日常开发里经常用到的 CSS 处理器——PostCSS,首先安装postcss-loaderautoprefixer(自动添加前缀的插件)

代码语言:javascript复制
npm i postcss-loader autoprefixer -D

同样的,也是在 webpack 配置文件中添加postcss-loader,在根目录新建postcss.config.js文件,添加如下代码之后,重新使用npm start打包时,我们写的 CSS 就会自动根据 Can i use 里的数据添加不同前缀了

代码语言:javascript复制
// webpack.config.js
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: "style-loader"
                    },
                    {
                        loader: "css-loader",
                        options: {
                            modules: true, // 指定启用css modules
                            localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
                        }
                    },
                    {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    }
}
代码语言:javascript复制
// postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}

Plugins

Plugins 是用来拓展 Webpack 功能的,它们会在整个构建过程中生效,执行相关的任务,Loaders 和 Plugins 常常被弄混,Loaders 是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,Plugins 并不直接操作单个文件,它直接对整个构建过程其作用

继续运行上面的例子,我们给项目添加几个常用的插件,HtmlWebpackPlugin这个插件的作用是依据一个简单的index.html模板,生成一个自动引用你打包后的 JS 文件的新index.html,这在每次生成的 JS 文件名称不同时非常有用(比如添加了hash值)

代码语言:javascript复制
npm i html-webpack-plugin -D

移除public文件夹,此插件可自动生成index.html文件,在app目录下,创建一个index.tmpl.html文件模板,这个模板包含title等必须元素,在编译过程中,插件会依据此模板生成最终的 HTML 页面,会自动添加所依赖的 CSS, JS, favicon 等文件

代码语言:javascript复制
<!-- index.tmpl.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id='root'></div>
</body>
</html>

更新 webpack 的配置文件,并新建一个build文件夹用来存放最终的输出文件

代码语言:javascript复制
// webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    ...
    module: {
        ...
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),
        new HtmlWebpackPlugin({
            template: __dirname   "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
        })
    ]
}
运行结果运行结果

Hot Module Replacement(HMR)属于 webpack 插件,该插件允许你在修改组件代码后,自动刷新实时预览修改后的效果,我们需要在 webpack 中做两项配置,在 webpack 配置文件中添加 HMR 插件;在 webpack Dev Server中添加hot参数

代码语言:javascript复制
// webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    devtool: 'eval-source-map',
    entry:  __dirname   "/app/main.js", 
    output: {
        path: __dirname   "/public",
        filename: "bundle.js"
    },
    devServer: {
        contentBase: "./public", //本地服务器所加载的页面所在的目录
        historyApiFallback: true, //不跳转
        inline: true, //实时刷新
        hot: true
    },
    module: {
        ...
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),
        new HtmlWebpackPlugin({
            template: __dirname   "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
        }),
        new webpack.HotModuleReplacementPlugin() //热加载插件
    ]
}

Babel 有一个叫做react-transform-hrm的插件,可以在不对 React 模块进行额外的配置的前提下让 HMR 正常工作

代码语言:javascript复制
npm i babel-plugin-react-transform react-transform-hmr -D

配置 Babel

代码语言:javascript复制
// .babelrc
{
    "presets": ["react", "env"],
    "env": {
        "development": {
            "plugins": [["react-transform", {
                "transforms": [{
                    "transform": "react-transform-hmr",
                    "imports": ["react"],
                    "locals": ["module"]
                }]
            }]]
        }
    }
}

我们再来看看其他插件,OccurenceOrderPlugin为组件分配 ID,通过这个插件 webpack 可以分析和优先考虑使用最多的模块,并为它们分配最小的 ID;UglifyJsPlugin压缩 JS 代码;ExtractTextPlugin分离 CSS 和 JS 文件

代码语言:javascript复制
npm i extract-text-webpack-plugin -D
代码语言:javascript复制
// webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    ...
    module: {
        ...
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),
        new HtmlWebpackPlugin({
            template: __dirname   "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
        }),
        new webpack.HotModuleReplacementPlugin(), //热加载插件
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("/app/main.css")
    ]
}
运行结果运行结果

该章节的内容到这里就全部结束了,源码我已经发到了 GitHub WebPack_2 上了,有需要的同学可自行下载

0 人点赞