59

一次webpack体验

 6 years ago
source link: https://juejin.im/post/5a03fdfdf265da432b4a44f7
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
    project
        - css
            - bootstrap.min.css
            - jb.css
        - fonts
            - 一些bootstrap的字体
        - images
            - 一些项目用到的图片
        - js
            - bootstrap.min.js
            - jquery.min.js
            - jb.js
        - index.html
        - favicon.ico复制代码

这个是公司要做的一个官方网站。由于项目比较简单,要求是单页的,没有页面跳转,所以只有一个 .html 文件。项目用了比较常规的 bootstrap + jquery 的开发,这个也没啥好说的。考虑到 CDN 的可控性,所以把所有 bootstrap 的资源都下载到了本地进行引用。项目开始时是用了常规的 js 和 css 引用(css 放前面,js 放后面),在开发完成后,发现有时间多余,就考虑用 webpack 对他进行处理一下,以巩固和学习一下 webpack 所用到的知识

初始化工作

1. 初始化

    npm init

先初始化一个 package.json 文件来管理我们 webpack 所依赖的文件包。一路无脑回车即可。复制代码

2. 安装 webpack

想要用 webpack ,那么你首先肯定要安装 webpack 才可以啊。用以下命令:

    npm install webpack --save复制代码

3. webpack.config.js

在根目录下新建一个 webpack.config.js 文件,用来对 webpack 进行配置。
当有这个文件后,我们系可以在命令行中用以下命令来启动配置好的 webpack 了。

    webpack --config webpack.config.js复制代码

4. 修改 webpack 打包命令

以上虽然也可以启动配置好的 webpack。但是每次要输这么一串命令好像有点太长了(懒啊)。所以在 package.json 中修改这样一项:

    "script": {

      + "start": "webpack --config webpack.config.js",

        "test": "echo \"Error: no test specified\" && exit 1"
    }复制代码

这样的话我们就可以在命令行中少敲几个键盘了。直接用以下命令,就等同于上面的命令了:

    npm start复制代码

好了,到这里,初始化工作就做完了,那么开始我们的 webpack 配置吧

webpack.config.js

1. 哪里来的入口文件?

什么事入口文件

webpack 创建应用程序所有依赖的关系图(dependency graph)。图的起点被称之为入口起点(entry point)。入口起点告诉 webpack 从哪里开始,并根据依赖关系图确定需要打包的内容。可以将应用程序的入口起点认为是根上下文(contextual root) 或 app 第一个启动文件

跟其他的 spa 应用不一样,这样的普通应用,其所有依赖都来自于 index.html 文件。如果说要有入口文件的话,怎么也应该是 index.html 他本身吧。但是 webpack
基本都是用 .js 文件作为入口文件,用 .html 作为入口文件的...(反正我是没有见到过)。至于这里有啥原因的话,大家就参考一下这篇文章吧。(其实我也不懂)

所以说,不管怎么样,我们都需要有个入口文件。

那就不管怎么样,我们经常看到的 webpack 的配置都是这样的

    module.exports = {
        entry: './index.js'
    }复制代码

那么我们不管三七二十一,先在根目录下新建一个 index.js,然后让他作为我们的入口文件。

index.js

要知道,对于我们原来的项目而言,我们根本就是不需要这么一个 index.js 文件的(没有他,我们可以活得更好)。但是我们又不得不创建了这样一个文件。那么问题来了,这么创建出来的文件,里面又该放什么内容呢?我们总不该救这么创建一个空文件就算了吧。

在考虑这个问题的时候,我们可以先去看下 webpack 官方的那个很经典的图(我很懒,就不放图了,大家自己去网上找吧)。webpack 把左边乱七八糟的 .js .css .png .jpg .sass。。。等等文件全部打包成了静态资源。也就是说,webpack 打包的是除 html 外的所有资源,那么我们是不是只要把这些资源都放到入口文件中那么就可以让 webpack 帮我们打包了呢?

但是等等。其他的都没有问题,什么 css 啊,什么 js 啊,都好说,因为用来也不过这么几个,但是图片呢,字体呢?我在项目中用了那么多图片,要全部再在 index.js 里面再写一遍!天呐!这是要命的啊!那么我能不能偷懒下,就只写 css 和 js 呢,其他乱七八糟的我先不管?那就先这么来吧。修改我们的 index.js 文件,添加以下内容。

    require('./css/bootstrap.min.css')
    require('./css/jubang.css')
    require('./js/jquery.min.js')
    require('./js/bootstrap.min.js')
    require('./js/jb.js')复制代码

先这样把他当作我们的入口文件吧

2. 配置出口文件

出口文件就很好配置了,将他打包到根目录下的 dist 目录中。嗯 ~ 这很常见!

    var path = require('path');
    module.exports = {
        entry: './index.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
        }
    }复制代码

3. 配置 loader

这个不知道怎么配置,就先看这篇文章吧。

所以说,到这里我们的 webpack.config.js 就是这样的:

    var path = require('path');

    module.exports = {
        entry: './index.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
        },
        module: {
            rules: [{
                test: /\.css$/,
                use: [
                    'style.loader',
                    'css-loader'
                ]
            }, {
                test: /\.(png|jpg|svg|git)$/,
                use: [
                    'file-loader'
                ]
            }, {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [
                    'file-loader'
                ]
            }]
        }
    }复制代码

这里我们用到了三个 loader,需要先安装下

    npm install css-loader style-loader file-loader --save复制代码

这三个 loader 的作用大家还是自己去网上查找吧

4. 第一次打包

配置到这里,我们可以先来打包一下,有问题再改嘛!

命令行切换到项目目录下,执行以下命令:

    npm start


打包结束后,项目的目录结构

    project
        - css
        - dist
        - fonts
        - images
        - js
        - node-modules
        - favicon.ico
        - index.html
        - index.js
        - package.json
        - webpack.config.js

我们可以看到,项目根目录下面多出来一个 dist 的目录。没错,这个就是我们 webpack 打包后文件生成的目录,至于为什么会是 dist 目录,那是因为你在 webpack.config.js 的 output 中设置的 path。


现在我们来查看下 webpack 打包出了什么东西

        - dist
            - xxxx.jpg
            - xxxx.woff2
            - xxxx.jpg
            - xxxx.svg
            - bundle.js
            - xxxx.ttf
            - xxxx.eot
            - xxxx.woff复制代码

注:xxxx代表一串数字和字母的组合,为了表示方便就这么写了

打包文件中生成了一个 .js 文件,两个 .jpg 文件,四个字体文件(.woff2、.ttf、.eot、.woff),和一个 .svg 文件

我们来看下这是个类型的文件都来自哪里吧复制代码

1. bundle.js

bundle.js 是我们根据我们入口文件,将我们在入口文件中所有的依赖资源都打包进去生成的。这个也是我们最主要要关注的文件。

2. .jpg

两个 .jpg 文件是从哪里来的呢?查看了两张图片之后,其实我们会知道,这两个图片都是在我们自己写的 jb.css 中用来作为 background-image 引入的。我们说了,webpack 会根据入口文作为起点,并根据依赖关系图来进行打包的。换句话说,我们在入口文件中依赖了 jb.css,而 jb.css 依赖了两张 .jpg 图片,所以 webpack 根据依赖分析,将这两张图片也都一起打包进来了。

3. 字体文件 + svg

四个字体文件的来源就需要我们对 bootstrap 有一定了解了。如果熟悉 bootstrap 的同学肯定会知道,在 bootstrap 的依赖里面,他正是依赖了这些字体,也就是说这些字体文件是从 bootstrap.min.css 文件中被打包进来的。其实我在做这个项目的时候,把 bootstrap 的源码弄到本地的时候,会发现里面有一个 fonts 的文件夹,也就是我们项目根目录下的 fonts 文件夹,这里会有四个同样文件结尾的字体文件和一个 .svg 结尾的 svg 文件。而这五个文件不就是我们这里多出来的五个文件嘛

如果还不放心的话,我们可以再做个验证。修改 webpack.config.js 文件

    {
        test: /\.(png|jpg|svg|git)$/,
        use: [
        -    'file-loader'
        +    'file-loader?name=[hash:8].[name].[ext]'
        ]
    }, {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: [
        -    'file-loader'
        +    'file-loader?name=[hash:8].[name].[ext]'
        ]
    }复制代码

我们把图片和字体文件,在 file-loader 处理后让他的名字就变成 ’8位hash值-原文件名-后缀名‘的格式,那么我们就可以比对这几个文件的来源了。
重新打包后(你得先删除原来的 dist 文件夹),我们就可以发现我们的猜测是正确的!

4. 遗留问题

虽然我们的第一次打包成功了,但是还是留下了几个问题没有解决:第一,我的 js、css、字体、图片等资源都被打包进了 dist 目录,但是作为我们的项目最主要的 index.html 文件呢?没有这个文件,我们打包出来的东西还有什么意义呢!。第二,我的 images 文件夹里有那么多图片,你这个 webpack 打包后为什么就只有两张图片了,其他的呢?

那么接下来让我们急需解决。

5. html-webpack-plugin

要解决第一个问题,我们需要用到 html-webpack-plugin 插件。这个插件的具体说明可以查看这里。这个插件的作用是可以将 html 文件打包,并自动添加对打包后的 output 文件的引用。具体如何使用,请先安装:

    npm install html-webpack-plugin --save复制代码

修改配置文件:

    var path = require('path');
    +   var HtmlWebpackPlugin = require('html-webpack-plugin');

在 module 后面加

    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html'
        })
    ]复制代码

我们在配置文件中引入了一个插件,并向 HtmlWebpackPlugin 构造函数传递了一个对象参数,在这个对象参数中,我们指明了一个 template 字段,表面我们要打包的 html 的文件源。然后我们重新打包,再查看我们的目录就可以发现 dist 目录下多出了一个 index.html。由于这个项目本身就是一个简单的不依赖任何环境的项目,所以如果正常的话我们直接打开 index.html 页面就能在浏览器里正常显示了。虽然不知道会怎么样,但是我们还是打开来试试吧。

当我们打开 index.html 在浏览器中显示的时候,我们发现浏览器中好多图片都不见了。细想我们的项目代码,发现除了在 jb.css 中的背景图被正确显示以外,其他的定义在 img 标签中的图片没有一张是显示出来的。

所以虽然这个文件配置还有点问题没解决,那么我们先来解决 html 中的 img 问题吧,也就是我们上面提到的第二个问题。

6. html-withimg-loader

要解决第二个问题(也就是 html 中的 img 问题),我们需要用到 html-whithimg-loader,具体关于这个怎么用可以查看这里

修改配置文件,直接在 rules 中再添加一条配置规则

    {
        test: /\.(html|htm)$/,
        use: [
            'html-withimg-loader'
        ]
    }复制代码

这条配置规则表明,所有要处理的 html 文件首先会经过 html-withimg-loader 这个 loader 处理。就是这么简单能将我们第二个问题解决吗?试试看吧,事件是检验真理的唯一标准。

修改资源目录

重新打包,再查看 dist 目录,这一查看不要紧,发现 dist 目录下多了好多图片文件,密密麻麻,乱七八糟的,这些该不会就是我们 html 中的图片吧。按住自己的强迫症,先找到 index.html (我们最关心的还是他嘛),打开后再浏览器查看效果。发现果然,我们的图片都已经在了,而且样式也差不多对了。但是这么乱七八糟的 dist 目录,怎么会是我们这种强迫症患者所想要的结果呢!我们想要的是图片都放在图片文件夹下,字体都放在字体文件夹下,其他的比较少的就先让他在外面呆着吧。说干就干,我们来调整一下我们的配置文件

        {
            test: /\.(png|jpg|svg|git)$/,
            use: [
            -    'file-loader?name=[hash:8].[name].[ext]'
            +    'file-loader?name=images/[hash:8].[name].[ext]'
            ]
        }, 
        {
            test: /\.(woff|woff2|eot|ttf|otf)$/,
            use: [
            -    'file-loader?name=[hash:8].[name].[ext]'
            +    'file-loader?name=fonts/[hash:8].[name].[ext]'
            ]
        } 复制代码

自动删除 dist

还有一个问题我已经忍了很久了,每次打包前,我们都需要手动先去删除上次打包留下来的 dist 目录,这个就烦了,虽然只是一个 delete 的事情,但是做多了也烦啊!我们想能不能让 webpack 自动帮我们做了这件事,让我们不需要手动去删除。还好 webpack 够智能,总能满足你提出来的各种无理取闹。不过我们先要装一个插件:

    npm install clean-webpack-plugin --save复制代码

然后再配置文件中添加对这插件的引用

    var HtmlWebpackPlugin = require('html-webpack-plugin');
    +   var CleanWebpackPlugin = require('clean-webpack-plugin');复制代码

在 plugins 中添加对这个插件的使用

    new CleanWebpackPlugin(['dist'])复制代码

这样我们就能不用手动删除 dist 文件夹了。

重新打包下试试吧!

打包完后我们在查看 dist 目录就瞬间感觉清爽多了有木有!

    - dist
        - fonts
            - 一些字体文件
        - images
            - 一些图片文件

        - bundle.js
        - index.html复制代码

修改 index.html

将打包后的项目再次在浏览器中查看,并查看控制台会发现,控制台报了一堆错误。其中有几个是对一些 css、js 文件引用的错误。因为我们把需要用的 css、js 都打包进了 bundle.js 中了。而我们原来的项目是通过静态资源引用的方式一个个导入 html 文件中的。所以,当我们 webpack 打包成功后,就不需要对这些资源进行引用了,我们只需要对 bundle.js(我们打包后的文件)进行引用就可以了,所幸的是,打包后的文件 webpack 已经自动帮我们引用了。所以直接在原来项目中的 index.html 中干掉那些 css、js 就可以了。然后重新打包后就没有这些资源找不到的乱七八糟的错误了。

但是,有一个小问题就是关于我们的 .ico 文件。这是我们网站的图标文件。他的引用错误该如何解决呢?我们可以在生成 html 的时候,将这个问题先给解决了。修改配置文件

    new HtmlWebpackPlugin({
        template: './index.html',
    +    favicon: path.resolve(__dirname, './favicon.ico')
    })复制代码

这样的话我们的图标文件也就有了。

7. 关于 jquery

虽然一些乱七八糟的引用错误解决了,但是控制台留下了一个让我们非常头疼的问题:jquery 的引用问题:

    Uncaught Error: Bootstrap's JavaScript requires jQuery复制代码

这里我们需要用到 expose-loader 这个东西,关于他,可以查看这里,废话不多说:

    npm install expose-loader --save复制代码

在 module 的 rules 中再添加一个 loader

    {
        test: require.resolve('./js/jquery.min.js'), // 引入 jquery
        use: [{
            loader: 'expose-loader',
            options: '$'
        }, {
            loader: 'expose-loader',
            options: 'jQuery'
        }]
    }复制代码

当然,网上或许有其他方法关于引入 jquery 的,这里只是说一种。然后再打包我们的页面就没有问题了:各种资源都有了,js 写的效果也出现了。

8. 提取 css

虽然网站看上去没啥问题了,但是细心的同学肯定会发现:当我们打开网站的时候,他会先出现一个没有样式的页面,然后一闪而逝,最后才出现我们预期的样子。这是为什么呢?

原因很好理解,因为我们把 css 和 js 都打包进了同一个 bundle.js 里面了。但是,这个 bundle.js 是在页面最后面才加载进来的。也就是说,我们的样式被放在了页面的底部被加载。这完全不符合我们的预期啊。我们希望的是样式在 head 中加载,而 js 脚本才放在页面底部加载。所以我们就不能把 css 和 js 一起打包进 bundle.js 中了。

extract-text-webpack-plugin

详细资料看这里

    npm install extract-text-webpack-plugin --save复制代码

增加 require

    var ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');复制代码

修改 css rules

    {
        test: /\.css$/,
        use: ExtractTextWebpackPlugin.extract[{
            fallback: 'style-loader',
            use: 'css-loader'
        }]
    }复制代码

增加 plugin

    new ExtractTextWebpackPlugin('style.css')复制代码

打包,然后我们会发现 dist 中多了一个 style.css,然后再 index.html 的 head 中的多了对这个 css 的引用

1. 压缩 js

增加 plugin

    new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        }
    })复制代码

2. 压缩 html

new HtmlWebpackPlugin({
    template: './index.html',
    favicon: path.resolve(__dirname, './favicon.ico'),
    minify: {
        removeAttributeQuotes: true,
        removeComments: true,
        removeEmptyAttribute: true,
        collapseWhitespace: true
    }
}),复制代码

3. 优化图片

    {
        test: /\.(jpg|png|gif|svg)$/,
        - use: 'file-loader?name=images/[hash:8].[name].[ext]'
        + use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
    }复制代码

暂时就先那么多吧,没时间写了,以后再说。第一次发文,求轻虐。

webpack(v3.5.5)中文文档


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK