Webpack 入门

Webpack 入门

如果你不想一步一步的学习,也可以直接从头到尾的阅读一遍,然后直接去将案例下载下来,假设有不懂的可以再回来看一遍.


前言

  • 什么是Webpack

    Webpack 可以看做是模块打包机 : 它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss, TypeScript等), 并将其转换和打包为合适的格式供浏览器使用.

  • WebPack和Grunt以及Gulp相比有什么特性

    其实Webpack和另外两个并没有太多的可比性,Gulp/Grunt是一种能够优化前端的开发流程的工具,而WebPack是一种模块化的解决方案,不过Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具。

    Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。
    image

    Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。
    image

    Webpack的处理速度更快更直接,能打包更多不同类型的文件。

  • 一切皆模块

    Webpack 有一个优点, 它把所有的文件都当作模块处理, JavaScript代码, Css 和 fonts 以及图片等等通过合适的loader都可以被处理.
    模块化开发使得开发者把浮渣的代码转化为小的, 干净的, 依赖声明明确的单元, 配合优化工具, 依赖管理 和加载管理可以自动完成


准备

  • 创建 package.json 文件

    npm init
    
  • 安装 Webpack 作为依赖包

    npm install --save-dev webpack
    
  • 构建项目结构

    创建 app 文件夹, 用来存放原始数据和我们将要写的JavaScript模块

    创建 public 文件夹, 用来存放之后供浏览器读取的文件 (包括使用 webpack 打包生成的js件以及一个 index.html文件)

    再创建三个文件: public/index.html , app/Greeter.js , app/main.js
    image

  • 写入基础代码

    我们在index.html文件中写入最基础的html代码,它在这里目的在于引入打包后的js文件

    <!-- index.html -->
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>Webpack Sample Project</title>
    </head>
    <body>
        <div id='root'></div>
        <script src="bundle.js"></script>
    </body>
    </html>
    

    我们在Greeter.js中定义一个返回包含问候信息的html元素的函数,并依据CommonJS规范导出这个函数为一个模块:

    // Greeter.js
    module.exports = function() {
        var greet = document.createElement('div');
        greet.textContent = "Hi there and greetings!";
        return greet;
    };
    

    main.js文件中我们写入下述代码,用以把Greeter模块返回的节点插入页面。

    //main.js 
    const greeter = require('./Greeter.js');
    document.querySelector("#root").appendChild(greeter());
    

开始使用Webpack

  • 在根目录下新建一个名为 webpack.config.js 的文件.

    module.exports = {
        entry:  __dirname + "/app/main.js", //唯一入口文件路径
        output: {
            path: __dirname + "/public",    //打包后的文件存放路径
            filename: "bundle.js"           //打包后输出文件的文件名
        }
    }
    

    __dirname 是 node.js 的一个全局变量, 它指向当前执行脚本所在的目录

  • 修改 package.json 文件的配置

    虽然它不是必需的,但是这样可以更快捷的执行我们的打包任务,配置后只要使用 npm start 命令就可以直接的执行打包任务了

    {
      "name": "demo",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "start": "webpack"    <= 修改的是这里
      },
      "author": "Lling",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^4.3.0"
      }
    }
    

    这里需要知道的是:

    1. package.json 中的 script 会按照一定顺序寻找命令对应的位置

    2. npm 中的 start 命令是一个特殊的脚本名称,其特殊性表现在, 命令行中使用 npm start 就可以执行其对应的命令, 如果对应的此脚本名称不是 start, 想要在命令行中运行时, 需要这样用 : npm run {scrpit name} .例如: npm run build

    3. 如果npm start 报错,那么你可以试试安装一下 webpack-cli

      npm install webpack-cli -D
      

Webpack 的强大功能

生成Source Maps (使调试更容易)

对小到中型的项目 ,推荐配置 eval-source-map ,需要强调的是只应该在开发阶段使用它

// webpack.config.js
module.exports = {
    devtool: 'eval-source-map',
    entry:  __dirname + "/app/main.js",
    output: {
        path: __dirname + "/public",
        filename: "bundle.js"
    }
}

构建本地服务器

监听代码的修改, 并且自动刷新显示修改后的结果

  • 安装 webpack-dev-server 依赖

    npm install --sava-dev webpack-dev-server
    
  • 添加配置

    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname + "/app/main.js",
      output: {
        path: __dirname + "/public",
        filename: "bundle.js"
      },
    
      devServer: {
        port: 8082,               // 设置默认监听端口, 如果省略 ,默认为 8080
        inline: true,             // 设置为true, 当源文件改变时会自动刷新页面
        contentBase: "./public",  // 默认会为根文件夹提供本地服务器. 这里设置'public'目录为服务器所加载的页面目录
        historyApiFallback: true, // 如果设置为true, 所有的跳转将指向 index.html (开发单页面时非常游泳)
      } 
    }
    
  • 在 package.json 文件上添加配置以开启本地服务器

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "webpack",
        "server": "webpack-dev-server --open"
    }
    

    现在 npm run server 就可以在设置的相应端口上查看结果啦!

Loaders

通过使用不同的loader, webpack有能力调用外部的脚本或工具,实现对不同格式的文件处理, 比如说分析转换 scss 为css, 或者把下一代的js文件(es6,es7)转换为现代浏览器兼容的js文件等

webpack.config.js中的modules关键字下的配置说明

参数 说明 备注
test 一个用以匹配loaders所处理文件的扩展名的正则表达式 必须
loader loader的名称 必须
include / exclude 手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹) 可选

在使用前我们先修改一下我们的文件

// config.json
{
    "greetText": "Hi there and greetings from JSON!"
}
// Greeter.js
var config = require('./config.json');

module.exports = function() {
    var greet = document.createElement('div');
    greet.textContent = config.greetText;
    return greet;
};
Babel

Babel其实就是一个编译JavaScript的平台. 它可以让你使用最新的JavaScript代码(es6, es7),而不用管新标准是否被当前使用的浏览器完全支持; 让你能使用基于JavaScript进行了拓展的语言,比如React的JSX

  • Babel 的安装与配置

    Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。

    先一次性安装这些依赖包

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

    在 webpack 中配置Babel的方法如下:

    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的语法了, 你可以开始尝试使用看看

  • Babel 的配置

    其实Babel完全可以在 webpack.config.js 中进行配置,但是考虑到babel具有非常多的配置选项, 因此为了让配置文件看起来不至于太复杂,我们将其提取出来.分成两个配置文件进行配置 (webpack 会自动调用 .babelrc 里的babel配置选项)

    // webpack.config.js
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            }
        ]
    }
    
    //.babelrc
    {
        "presets": ["react", "env"]
    }
    
CSS处理
  • 样式表处理. webpack 提供两个工具处理样式表, css-loaderstyle-loader .

    css-loader 使你能够使用类似 @importurl(...) 的方法实现 require() 的功能

    style-loader 将所有的计算后的样式加入页面中

    npm install --save-dev style-loader css-loader
    
    // webpack.config.js
    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的类名格式
                        }
                    }
                ]
            }
        ]
    }
    
  • css 预处理器

    使用 PostCss 来为CSS代码自动添加适应不同浏览器的CSS前缀 ( -PostCss是一个CSS的处理平台, 可以帮助你的 css 实现更多的功能, 可以去官方文档了解更多的相关知识)

    npm install --save-dev postcss-loader autoprefixer
    
    //webpack.config.js
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    }
    
    // postcss.config.js
    module.exports = {
        plugins: [
            require('autoprefixer')
        ]
    }
    
  • css 引入

    和js文件的引入相同. 请放心的使用,相同的类名也不会造成不同组件之间的污染

    import './main.css';    //导入我们的css文件
    

    通常情况下, css 会和 js 打包到同一个文件中, 并不会打包为一个单独的 css 文件, 不过通过配置后 webpack 将把 css 打包为单独的一个文件

    关于 css modules 详情可以去官方文档了解更多

插件 (Plugins)

插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。

Loaders 和 Plugins 的区别:

  1. Loaders 是在打包构建过程中用来处理源文件的(jsx, scss, less ... ), 一次处理一个.

  2. Plugins 并不直接操作单个文件, 它直接对整个构建过程起作用.

使用方法:

  1. npm 安装你所要使用的插件

  2. 在 webpack 配置中的 plugins 关键字部分添加该插件的实例

  • Html Webpack Plugin

    作用: 依据一个简单的 index.html 模板, 生成一个自动引用你打包后的 JS 文件的新 index.html . 这在生成的js 文件名称不同时非常有用 (比如添加了 hash 值)

    1. 安装插件

      npm install --save-dev html-webpack-plugin
      
    2. 移除 public 文件夹 ( 该插件会自动生成 index.html 文件 )

    3. 在 app 目录下创建一份 index.html 文件模板, 同时新建 build 文件夹用来存放最终的输出文件

    4. 更新 webpack 配置文件

      const webpack = require('webpack');
      const HtmlWebpackPlugin = require('html-webpack-plugin');
      
      module.exports = {
          entry: __dirname + "/app/main.js",
          output: {
              path: __dirname + "/build",
              filename: "bundle.js"
          },
          devtool: 'eval-source-map',
          devServer: {
              contentBase: "./public",
              historyApiFallback: true,
              inline: true//实时刷新
          },
          module: {
              rules: [
                  {
                      test: /(\.jsx|\.js)$/,
                      use: {
                          loader: "babel-loader"
                      },
                      exclude: /node_modules/
                  },
                  {
                      test: /\.css$/,
                      use: [
                          {
                              loader: "style-loader"
                          }, {
                              loader: "css-loader",
                              options: {
                                  modules: true
                              }
                          }, {
                              loader: "postcss-loader"
                          }
                      ]
                  }
              ]
          },
          plugins: [
              new webpack.BannerPlugin('版权所有,翻版必究'),
              new HtmlWebpackPlugin({
                  template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
              })
          ],
      };
      

产品阶段的构建

在产品阶段,我们可能还需要对打包的文件进行额外的处理, 比如优化,压缩,缓存以及分离css和js.

  • 创建一个 webpack.production.config.js 文件用于生产时使用,并添加基本配置

    // webpack.production.config.js
    const webpack = require('webpack');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
        entry: __dirname + "/app/main.js",
        output: {
            path: __dirname + "/build",
            filename: "bundle.js"
        },
        devtool: 'null', //注意修改了这里,这能大大压缩我们的打包代码
        devServer: {
            contentBase: "./public",
            historyApiFallback: true,
            inline: true,
            hot: true
        },
        module: {
            rules: [{
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            }, {
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: [{
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }],
                })
            }]
        },
        plugins: [
            new webpack.BannerPlugin('版权所有,翻版必究'),
            new HtmlWebpackPlugin({
                template: __dirname + "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
            })
        ],
    };
    

    注意: 如果是window电脑, build 需要配置为 :

    "build": "set NODE_ENV=production && webpack --config ./webpack.production.config.js --progress"
    
  • 优化插件

    ** ExtractTextPlugin 分离CSS和JS文件**

    npm install --save-dev extract-text-webpack-plugin
    

    在配置文件的plugins后引用它们

    // webpack.production.config.js
    ...
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    ...
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"
        }),
        new ExtractTextPlugin("style.css")
    ]
    ...
    
    1. 最新的webpapck版本已经将 UglifyJsPlugin 的内置插件 (该插件用于压缩js代码) 已经移除,现在直接 运行 npm run build, 生成的js代码将直接被压缩
    1. 如果运行 npm run build 报错, 很有可能是因为 ExtractTextPlugin 安装的版本问题, 可以尝试安装ExtractTextPlugin的4.0.0-beta.0版本
    npm install --save-dev extract-text-webpack-plugin@4.0.0-beta.0
    

    缓存

    把一个哈希值添加到打包的文件名中

    ...
    output: {
        path: __dirname + "/build",
        filename: "bundle-[hash].js"
    },
    ...
    

    clean-webpack-plugin 去除 build 文件中的残余文件

    这个插件会清除之前全部打包的js文件

    npm install clean-webpack-plugin --save-dev
    
    ...
    const CleanWebpackPlugin = require("clean-webpack-plugin");
    ...
    plugins: [
        ...
        new CleanWebpackPlugin('build/*.*', {
            root: __dirname,
            verbose: true,
            dry: false
        })
    ]
    ...
    

至此 入门篇结束

posted @ 2023-01-12 11:29  iceCream-Lling  阅读(41)  评论(0编辑  收藏  举报