美文网首页JavaScriptJavaScript 进阶营让前端飞
WebPack 模块化打包工具(下)

WebPack 模块化打包工具(下)

作者: Nian糕 | 来源:发表于2018-01-05 16:22 被阅读295次
    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 的配置包括以下几方面:

    • test:一个用以匹配 loaders 所处理文件的拓展名的正则表达式(必须)
    • loader:loader的名称(必须)
    • include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选)
    • query:为 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

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

    相关文章

      网友评论

        本文标题:WebPack 模块化打包工具(下)

        本文链接:https://www.haomeiwen.com/subject/oqgrnxtx.html