Home > Archives > webpack初见

webpack初见

Publish:

不得不说,学习webpack是一个痛苦的过程,我现在也仅仅是入门的水准,不过感觉还是有一些值得总结的地方。

在刚开始学习webpack时最痛苦的一点,我想还是对webpack作用的理解。

人们总说打包、打包,打了包就好了,可是打包究竟好在哪里,为什么要打包?

网上几乎所有的教程都在说把动态依赖加载成静态资源,可以什么是动态依赖,它转化而成的静态资源又是什么呢?

loader、plugin等等这些东西究竟是什么意思?

我想每一个刚开始学习webpack的人都有这样的困惑,那么,我还是以一个简单的例子开始,尝试讲一讲webpack到底有什么用,然后再来讲解配置的过程。(由于还是菜鸟,所以举的例子不一定没有谬误之处,希望读者包涵)

webpack有什么用?

最近跟着祖明老师做efe的前端Task,其中有这样一个任务

image

一个简单的Todo。

要求使用ES6的语法实现,CSS预处理器使用less,可以使用jQuery、Hammer等库,但不能使用React、Vue等MVVM框架。

模块化也是需要的。

作为一个对前端工程化没有经验的新手,我们可能第一想法就会先写出下面这样一个HTML页面:

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <!--this file includes some less code-->
  <link rel="stylesheet/less" type="text/css" href="styles.less" />
</head>
<body>
  <!--some html code-->
  <!--including some imgs maybe-->
  <div class="header">...</div>
  <div class="main">...</div>
  <div class="footer">...</div>

  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
  <script src="https://cdn.staticfile.org/hammer.js/2.0.8/hammer.min.js"></script>
  <script src="https://cdn.staticfile.org/font-awesome/4.7.0/fonts/fontawesome-webfont.svg"></script>
  <script src="less.js" type="text/javascript"></script>
  <script src="all.js"></script>
  <script src="edit.js"></script>
  <script src="onething.js"></script>
  <script src="store.js"></script>
  <script type="text/babel">
    // some js code
  </script>
</body>

</html>

为了转换ES6我们引入了Babel(当然,还需要额外配置.babelrc),为了操作DOM、适配移动端等等的功能,我们还引入了jQuery、Hammer、fontawesome、less等库,此外,作为一个模块化的应用,我们会引入model层store.js,controller层all.js、edit.js等等JS文件。

那么你觉得这样写好吗?

可能你会觉得没什么问题,这样写也可以啊。那么接下来一个问题又来了。

你怎样处理all.js、edit.js、onething.js等模块里对各种库的引用呢?把这些东西再引入一遍吗?

看到这里,即使是新手,可能你也已经知道这样做不太好了。

看来模块化的应用不能这样写,我们需要一个工具帮助我们处理这些需要的库和模块,这个工具就是webpack,这些库和模块就是我们所说的动态依赖,而经过webpack打包而成的bundle.js(后面你会看到这个东西),就是静态资源。

我尽量用通俗的方法来解释这个问题了,不过还是来看看官方的解释吧:

“webpack is a module bundler for modern JavaScript applications. When webpack processes your application, it recursively builds a dependency graph that includes every module your application needs, then packages all of those modules into a small number of bundles - often only one - to be loaded by the browser.”

希望我的解释能对你的疑惑有帮助,下面我们要开始配置webpack了。

安装

先创建一个工程

我的文件结构是这样的:

image

主要是考虑把工程分为几个方面:

index.html作为单页应用的框架

src文件夹下放置我们需要的各个模块,包括:controller、model及样式文件等static资源

main.js作为程序的主入口

确定了项目的结构,现在开始用npm安装webpack吧:

npm init -y
npm install --save webpack
touch webpack.config.js

配置

因为正常情况下,项目的结构并不简单,所以我们一般不考虑使用命令行参数的方式来配置,一是容易出错,二是没有持久性,因此我们创建了一个webpack.config.js

webpack.config.js也是一个模块,由commonJS的语法写成,让我们先来看第一个配置项:入口和出口文件。

入口和出口文件

module.exports = {
    entry: __dirname + '/src/main.js',
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    }
}

入口文件entry就是我们在index.html中需要引入的文件,它是所有依赖的入口,也就是说,所有的依赖都可以通过某一条线连接到入口文件,请看webpack官网上的图片:

image

通过引入这个入口文件,总是可以连接到我们需要的依赖,而一旦webpack发现一个依赖,它就会通过相应的loader自动地将依赖打包进我们最终生成的bundle.js中。

而出口文件output就是我们最终生成的打包完成的依赖:bundle.js,它存放在在dist/。为了简便起见,我将index.html与最终生成的依赖放在了同一个路径下。

现在来看看index.html吧。

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>Task12</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>

<body>
    <div class="header">
        <button class="cancel">Cancel</button>
        <p class="title">Todo</p>
        <button class="add">Add</button>
    </div>
    <div class="main" id="main"></div>
    <div class="footer">
        <div class="one-thing sel">OneThing</div>
        <div class="all">All</div>
    </div>
    <script src="./bundle.js"></script>
</body>

</html>

是不是比上面简洁多了?

loader

简洁虽然是简洁了,可是单就这个config可是不会打包任何依赖的,我们要配置我们需要打包的依赖,于是就轮到loader登场了。

loader是什么呢?我觉得直译过来最为清晰:加载器。

我们需要为不同的依赖配置相应的加载器。

以配置babel转换器为例来说明吧。

首先安装babel-loader和babel规则:

npm install --save-dev babel-core babel-preset-env babel-loader

然后继续写我们的配置文件:

module.exports = {
    devtool: 'eval-source-map',
    entry: __dirname + '/src/main.js',
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    },

    module: {
        rules: [
            {
                test: /.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            }
        ]
    }
}

module就是指我们需要引入的模块(也就是依赖),rules代表对不同依赖应用的规则集,它是一个数组。

而这个数组里的第一项就是我们使用babel-loader的规则了。

这个规则规定:对于项目中引用到的所有JS文件(除了node_modules/文件夹下的),使用babel-loader作为加载器加载它。

当然,.babelrc也是需要配置的,具体请看babel的官网吧。

然后我们就可以在项目里畅快的书写ES6的语法而不用担心不兼容了,因为我们引入的每一个JS文件,都因使用了babel-loader作为加载器而被babel转换成了可以被现代浏览器兼容的ES5语法。

不同的依赖对应不同的loader,这里就不一一讲解了。

需要注意的是,不只是JS的依赖可以被loader处理,各种类型的依赖都可以!请看我的配置:

{
    test: /\.less$/,
    use: [{
        loader: "style-loader"
    }, {
        loader: "css-loader"
    }, {
        loader: "less-loader"
    }]
}

在设置了相应的loader以后,我们可以在JS中使用ES6的模块机制引入 样式文件 依赖了——–它们最终会被打包成静态资源的。来看看代码吧:

import * as onething from './controller/onething.js'

import './static/less/layout.less'

onething.render()

plugin

讲解完loader,另一个重要的角色登场了,它就是plugin。

顾名思义,plugin就是插件。那么插件又是做什么的呢?

简单的来说,插件就是用来帮助我们自定义配置webpack打包流程的规则。

可能会有人疑惑,既然都是配置规则,为什么不直接使用loader,还要多此一举搞出一个plugin呢?

请注意,plugin是对我们整体的构建流程的一个配置,而loader则是更加细粒度的针对我们引入的不同类型的依赖,比如.js、.css、.png等,应用的都是不同的loader。

让我们以一个例子来作为说明吧,继续配置我们的config文件。

var webpack = require('webpack')

module.exports = {
    devtool: 'eval-source-map',
    entry: __dirname + '/src/main.js',
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    },

    module: {
        rules: [
            {
                test: /.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
            {
                test: /\.less$/,
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader"
                }, {
                    loader: "less-loader"
                }]
            }
        ]
    },

    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery',
            Hammer: 'hammerjs'
        })
    ]
}

写好这个配置后记得安装相应的依赖库:

npm i --save jquery hammerjs

在这个配置里,我们使用了一个webpack内置的插件ProvidePlugin,所以需要先把wepack引入。

ProvidePlugin的设置告诉我们,在webpack打包的流程中,遇到$符号,就提供jQuery模块;遇到Hammer变量,就提供hammerjs模块。

如此我们就不用在每个需要用到jQuery或Hammer的模块手动的import它们了。

看出区别了吗?

plugin指出的定语是“只要在打包流程中”。

而loader指出的定语是“对于.XX类型的依赖”

运行

运行就很简单啦,相信每一个了解npm的读者都能够驾驭,我就不多说了。

结语

这篇文章主要是总结一下我在学习webpack时的理解,也为读者提供一个webpack配置的概念,帮助读者对webpack有一个初步的认识,具体的配置肯定不会这样简单。

像开发阶段需要的devtool、devServer配置、css模块化的配置、涉及到MPA的code splitting配置等都没有讲到,也是需要有真正的实践,才能有切身的体会。

因此,如果需要做一个完整的入门webpack配置,我推荐以下的学习资料:

入门webpack,看这篇就够了

webpack 2:Getting Started

另外如果想尝试实现一下本文项目,这里是github地址:

efe-work-task

声明: 本文采用 BY-NC-SA 授权。转载请注明转自: webpack初见 - 无火的余灰