JavaScriptJavaScript 进阶营让前端飞

WebPack 模块化打包工具(下)

2018-01-05  本文已影响295人  Nian糕
Unsplash

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

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

1. Loaders

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

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

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

// npm一次性安装多个依赖模块,模块之间用空格隔开
npm i babel-core babel-loader babel-preset-env babel-preset-react -D

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

// 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 的文件

npm i react react-dom -D
{
    "greetText": "Love and Peace from JSON!"
}
//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
// 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 配置选项

// 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/
            }
        ]
    }
}
// .babelrc
{
    "presets": ["env", "react"]
}

2. CSS Modules

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

npm i style-loader css-loader -D
// 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 的文件,并设置如下样式

/* 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

// 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 的类名传递到组件的代码中,这样做有效避免了全局污染

// 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 文件来进行一下测试

/* Greeter.css */
.root {
  background-color: #eee;
  padding: 10px;
  border: 3px solid #ccc;
}

导入 .rootGreeter.js

//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(自动添加前缀的插件)

npm i postcss-loader autoprefixer -D

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

// 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"
                    }
                ]
            }
        ]
    }
}
// postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}

3. Plugins

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

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

npm i html-webpack-plugin -D

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

<!-- 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 文件夹用来存放最终的输出文件

// 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 参数

// 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 正常工作

npm i babel-plugin-react-transform react-transform-hmr -D

配置 Babel

// .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 文件

npm i extract-text-webpack-plugin -D
// 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 上了,有需要的同学可自行下载

End of File

行文过程中出现错误或不妥之处在所难免,希望大家能够给予指正,以免误导更多人,最后,如果你觉得我的文章写的还不错,希望能够点一下喜欢关注,为了我能早日成为简书优秀作者献上一发助攻吧,谢谢!^ ^

上一篇下一篇

猜你喜欢

热点阅读