webpack 使用总结

1,965 阅读9分钟

前言:

学习webpack的时候,看过它的官网,发现非常难懂,各种内容编排的很乱,并且挺生涩难懂的,和React Native等项目的官网比起来,差太多了,所以如果刚开始接触webpack的话,不建议直接去其官网看。

一、webpack介绍

1.1、什么是webpack

webpack是一个module bundler(模块打包工具),所谓的模块就是在平时的前端开发中,用到一些静态资源,如JavaScript、CSS、图片等文件,webpack就将这些静态资源文件称之为模块。

webpack支持AMD和CommonJS,以及其他的一些模块系统,并且兼容多种JS书写规范,可以处理模块间的以来关系,所以具有更强大的JS模块化的功能,它能对静态资源进行统一的管理以及打包发布,在官网中用这张图片介绍:


webpack官网的介绍.png

它在很多地方都能替代Grunt和Gulp,因为它能够编译打包CSS,做CSS预处理,对JS的方言进行编译,打包图片,代码压缩等等。所以在我接触了gulp之后,就不太想用gulp了。

1.2、为什么使用webpack

在自己的学习使用中、和技术交流群里以及和朋友的讨论、网上其他大神的博客中得到一些为什么要使用webpack的原因,webpack的优点,webpack更适用于哪里。

  • 对 CommonJS 、AMD 、ES6的语法做了兼容;

  • 对js、css、图片等资源文件都支持打包;

  • 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持;

  • 有独立的配置文件webpack.config.js;

  • 可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间;

  • 支持 SourceUrls 和 SourceMaps,易于调试;

  • 具有强大的Plugin接口,大多是内部插件,使用起来比较灵活;

  • webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快;

webpack最常用与spa应用,主要是vue和React,其实它就非常像Browserify,但是将应用打包为多个文件。如果单页面应用有多个页面,那么用户只从下载对应页面的代码. 当他么访问到另一个页面, 他们不需要重新下载通用的代码。

webpack也能用于服务端,但是构建后端代码一般都不会用webpack,坑太多了,所以正常情况下只用于前端,这是一个做了很多年前端的朋友告诉我的经验,所以别人已经走过的坑,还是不要去踩了~~~~ 并且我个人觉得后端其实也没必要这么做,因为后端更多的是处理逻辑,以及为前端提供数据.....

二、基本使用

2.1、webpack基本运行

2.1.1、创建基本项目结构


基本项目结构.png

基本项目结构.png

2.1.2、安装全局依赖:

npm  install  webpack  -g

2.1.3、创建配置文件

在项目的根目录创建三个或多个webpack配置文件,我们创建两个:

  • webpack.dev.config.js → 开发环境中用到的配置文件
  • webpack.pub.config.js → 生产环境中用到的配置文件

    添加webpack配置文件.png

2.1.4、修改配置文件

注意:开发环境和生产环境的配置是不一样的,具体配置上的差异看后面的内容。

首先先配置开发环境,写上最基本的内容:

var path = require('path');
module.exports = {
    entry:path.resolve(__dirname,'src/js/app.js'),
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: 'bundle.js',
    },
}

最基本的配置文件中一般含有文件入口和输出文件的配置信息,后期还可以加上loader和各种插件配置使用。

2.1.5、通过配置文件运行webpack

  • 在根目录运行webpack命令,默认会去查找名称为webpack.config.js的文件执行,如果没有就会报配置信息没有配置的错误。

    webpack

    由于我们是将开发环境和发布环境分开的,并没有直接添加webpack.config.js,所以这种方式在我们这里显然是不行的;

  • 通过运行下面的命令进行配置文件的选择

    webpack --config webpack.dev.config.js

    命令行输出如下:

    zhongfabindeMacBook-Pro:webpack_test zhongfabin$ webpack --config webpack.dev.config.js
    Hash: d8151fcc7a02add3ec93
    Version: webpack 2.2.1
    Time: 49ms
      Asset     Size  Chunks             Chunk Names
    bundle.js  2.57 kB       0  [emitted]  main
     [0] ./src/js/app.js 63 bytes {0} [built]

    在dist中生成了bundle.js:


    生成在dist中的bundle.js.png


    这时候如果想看生成的js是否能用,我们需要将src中的拷贝一份到dist中,并且引入bundle.js(后面可以自动生成index,现在后没学到):


    手动加入index.html.png


    运行效果如下:


    运行效果.png

注意:在实际的开发中,我们基本上不会用以上这种直接提供的命令,因为用这种方式,我们需要手动的敲打很多字母,在实际的开发中通用的方法都是使用配置文件的方式运行(接下来讲解)。

2.2、把运行命令配置到npm的script中

2.2.1、为什么加入到npm的script中

  • npm 是一个非常好用的用来编译的指令,通过 npm 你可以不用去担心项目中使用了什么技术,你只要调用这个指令就可以了,只要你在 package.json 中设置 scripts 的值就可以了。

  • 真正开发的时候你的webpack的命令会敲很长,因为有很多命令需要设置,即便你用了配置文件的方式,但一些--colors --hot这些命令是没法在配置文件中配置的。比如:

    webpack-dev-server --devtool eval --progress --colors --hot --content-base build

    所以我们不可能每次都敲这么长,因而我们就把这一大串配置到npm中

2.2.2、使用步骤

  • 安装webpack,注意:这时候不是全局安装

    npm install --save-dev webpack
  • 配置npm的package.json文件

    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
      "dev":"webpack --config webpack.dev.config.js",
      "pub":"webpack --config webpack.pub.config.js"
    },
  • 在根目录下执行命令

    npm run dev

这样之后就不要敲那么复杂的命令了。

2.3、为发布目录启动服务

我们没修改一次就要需要输入 npm run develop 是一件非常无聊的事情,幸运的是,我们可以把让他自己运行,那就是使用webpack-dev-server。

除了提供模块打包功能,Webpack还提供了一个基于Node.js Express框架的开发服务器,它是一个静态资源Web服务器,对于简单静态页面或者仅依赖于独立服务的前端页面,都可以直接使用这个开发服务器进行开发。在开发过程中,开发服务器会监听每一个文件的变化,进行实时打包,并且可以推送通知前端页面代码发生了变化,从而可以实现页面的自动刷新。

使用步骤:

  • 安装webpack-dev-server

    npm install --save-dev webpack-dev-server
  • 调整npm的package.json中scripts 部分开发命令的配置

    {
      "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1",
          "pub": "webpack --config webpack.pub.config.js",
          "dev": "webpack-dev-server  --config webpack.dev.config.js --devtool eval --progress --colors --hot --content-base src"
      }
    }

    在dev的配置中做了以上改变之后,webpack-dev-server 会在 localhost:8080 建立一个 Web 服务器。
    几个参数的解释:

    • --devtool eval:为你的代码创建源地址。当有任何报错的时候可以让你更加精确地定位到文件和行号
    • --progress:显示合并代码进度
    • --colors -- hot:命令行中显示颜色
    • --content-base 指向设置的输出目录
  • 手动访问 http://localhost:8080
    简单来说,当你运行 npm run develop 的时候,webpack会帮你会启动一个 Web 服务器,然后监听文件修改,然后自动重新合并你的代码。真的非常简洁。

  • 注意点

    • 用webpack-dev-server生成bundle.js文件是在内存中的,并没有实际生成;
    • 如果引用的文件夹中已经有bundle.js就不会自动刷新了,你需要先把bundle.js文件手动删除(后期有插件可以完成);
    • 用webstorm的同学注意了,因为webstorm是自动保存的,所以可能识别的比较慢,你需要手动的ctrl+s一下;
  • 几个报错

    • webpack版本的问题
      如果webpack使用的1.x的版本,那么webpack-dev-server也要使用1.x的版本,否则会报如下错误:Connot find module 'webpack/bin/config-yargs'。


      webpack版本引起的报错.png
    • 端口占用问题
      如果已经有一个工程中使用了webpack-dev-server,并且在运行中,没有关掉的话,那么8080端口就被占用了,此时如果在另一个工程中使用webpack-dev-server就会报错:Error: listen EADDRINUSE 127.0.0.1:8080。


      端口占用错误.png

2.4、浏览器自动刷新

做完以上的所有步骤之后,可以启动一个服务并监听变化了,但是浏览器你还需要手动刷新一下,我们其实是可以通过下面的这个方法让它自动刷新的。

修改配置文件中的entry部分,当前entry的的值为:

entry:path.resolve(__dirname,'src/js/app.js')

将entry的值改为一个数组,除了上面的内容加进去之外,再加其他两句:

var path = require('path');
module.exports = {
    entry: [
        'webpack/hot/dev-server',
        'webpack-dev-server/client?http://localhost:8080',
        path.resolve(__dirname,'src/js/app.js')
    ],
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
}

特别注意:此时用webpack-dev-server生成bundle.js文件是在内存中的,所以在src中的index引入的js文件应该生成的bundle.js,如果还是引入的app.js的话,将会看不到刷新的效果。


修改js文件.png

三、常用的加载器

加载器就是webpack准备的一些预处理工具,比如编译jsx和es6的加载器,处理sass等....

使用加载器的步骤也很简单,首先是安装依赖,然后在配置文件的module中加一个字段module字段,在module写上loaders,在loaders中写上相应的配置。

3.1、编译jsx和ES6到原生js

  • 安装以下的依赖
    npm install --save-dev babel-loader babel-core babel-preset-es2015  babel-preset-react
  • 修改开发配置文件

    module: {
      loaders: [
          {
              test: /\.jsx?$/, // 用正则来匹配文件路径,这段意思是匹配 js 或者 jsx
              loader: 'babel',// 加载模块 "babel""babel-loader" 的缩写
              query: {
                  presets: ['es2015', 'react']
              }
          }
      ]
    }
  • 使用
    直接从新运行npm run dev即可

3.2、加载CSS

Webpack允许像加载任何代码一样加载 CSS。你可以选择你所需要的方式,但是你可以为每个组件把所有你的 CSS 加载到入口主文件中来做任何事情。

加载 CSS 需要 css-loader 和 style-loader,他们做两件不同的事情,css-loader会遍历 CSS 文件,然后找到 url() 表达式然后处理他们,style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中。

  • 首先下载依赖

    npm install --save-dev css-loader   style-loader
  • 修改配置文件,在loaders中加上

    {
      test: /\.css$/,
      loader: 'style!css' // 如果有多个加载器,中间用感叹号隔开,多个加载器从右往左执行
    }

    “!”用来定义loader的串联关系,"-loader"是可以省略不写的,多个loader之间用“!”连接起来

  • CSS的加载策略

    • 在你的主入口文件中,比如 src/app.js 你可以为整个项目加载所有的 CSS
      import  './project-styles.css';
    • 懒加载(推荐使用)
      如果你想发挥应用中多重入口文件的优势,你可以在每个入口点包含各自的 CSS。
      你把你的模块用文件夹分离,每个文件夹有各自的 CSS 和 JavaScript 文件。再次,当应用发布的时候,导入的 CSS 已经加载到每个入口文件中。

      引入CSS.png
  • 使用内联样式取代 CSS 文件
    在 “React Native” 中你不再需要使用任何 CSS 文件,你只需要使用 style 属性,可以把你的 CSS 定义成一个对象,那样就可以根据你的项目重新来考虑你的 CSS 策略。


    内联样式.png

3.3、加载sass

  • 下载依赖

    npm  install  --save-dev  sass-loader
  • 修改配置文件

    {
      test: /\.scss$/,
      loader: 'style!css!sass'
    }
  • 使用和导入和CSS的使用方式一致

3.4、图片处理

直到 HTTP/2 你才能在应用加载的时候避免设置太多 HTTP 请求。根据浏览器不同你必须设置你的并行请求数,如果你在你的 CSS 中加载了太多图片的话,可以自动把这些图片转成 BASE64 字符串然后内联到 CSS 里来降低必要的请求数,这个方法取决与你的图片大小。你需要为你的应用平衡下载的大小和下载的数量,不过 Webpack 可以让这个平衡十分轻松适应。

  • 下载依赖

    npm  install  --save-dev url-loader file-loader
  • 修改配置文件

    {
      test: /\.(png|jpeg|gif|jpg)$/,
      loader: 'url?limit=25000'
    }

    加载器会把需要转换的路径变成 BASE64 字符串,在其他的 Webpack 书中提到的这方面会把你 CSS 中的 “url()” 像其他 require 或者 import 来处理。意味着如果我们可以通过它来处理我们的图片文件。
    url-loader 传入的 limit 参数是告诉它图片如果不大于 25KB 的话要自动在它从属的 css 文件中转成 BASE64 字符串。

  • 大图片处理
    在代码中的情况是以下之一:

    div.img{
      background: url(../image/xxx.jpg)
    }
    //或者
    var img = document.createElement("img");
    img.src = require("../image/xxx.jpg");
    document.body.appendChild(img);

    你可以这样配置:

    {
      test: /\.(png|jpeg|gif|jpg)$/,
      loader: 'file-loader?name=images/[name].[ext]'
    }

    针对上面的两种使用方式,loader可以自动识别并处理。根据loader中的设置,webpack会将小于指点大小的文件转化成 base64 格式的 dataUrl,其他图片会做适当的压缩并存放在指定目录中。

四、发布配置

前面一直都在捣鼓config webpack.dev.config.js,也就是一致都在配置开发环境,现在重要配置发布环境了,也就是配置config webpack.pub.config.js,其实发布的配置和开发的配置差不了太多。

4.1、基础配置

  • 修改npm的package.json文件
    其实相关的配置早就加上去了,就是以下的这句:

    "pub": " webpack --config webpack.pub.config.js  -p",

    指向生产的配置文件,并且加上了webpack的cli的-p,他会自动做一些优化

  • 修改webpack.pub.config.js文件
    其实将webpack.dev.config.js中的内容拷贝过来即可,然后修改entry中的一些东西。

    var path = require('path');
    module.exports = {
      entry: path.resolve(__dirname, 'src/js/app.js'),
      output: {
          path: path.resolve(__dirname, 'dist'),
          filename: 'bundle.js'
      },
      module: {
          loaders: [
              {
                  test: /\.jsx?$/, 
                  loader: 'babel',
                  query: {
                      presets: ['es2015', 'react']
                  }
              },
              {
                  test: /\.css$/, 
                  loader: 'style!css' 
              },
              {
                  test: /\.scss$/,
                  loader: 'style!css!sass'
              },
              {
                  test: /\.(png|jpeg|gif|jpg)$/,
                  loader: 'file-loader?name=images/[name].[ext]'
              },
          ]
      },
    }

    可以看到,其实生产环境的配置和开发的配置没有太大的不同,主要是把不需要的东西给去掉了。

  • 直接运行:npm run pub

4.2、分离应用和第三方

4.2.1、何时应该分离

当你的应用依赖其他库尤其是像 React JS 这种大型库的时候,你需要考虑把这些依赖分离出去,这样就能够让用户在你更新应用之后不需要再次下载第三方文件。当满足下面几个情况的时候你就需要这么做了:

  • 当你的第三方的体积达到整个应用的 20% 或者更高的时候

  • 更新应用的时候只会更新很小的一部分

  • 你没有那么关注初始加载时间,不过关注优化那些回访用户在你更新应用之后的体验

  • 通过手机访问的用户比较多

4.2.2、修改配置文件

  • 在头部引入webpack

    var webpack=require("webpack");
  • 修改entry


    修改entry.png
  • 增加plugins


    增加plugins.png
  • 结果:在dist文件夹中生成两个js文件,一个bundle.js,另一个是vendors.js

五、常用插件介绍

webpack提供了丰富的组件用来满足不同的需求,当然了我们也可以自行实现一个组件来满足自己的需求。

plugins: [
     //your plugins list
 ]

5.1、压缩插件

这个插件是webpack自带的

// 用webpack压缩代码,可以忽略代码中的警告
new webpack.optimize.UglifyJsPlugin({
    compress: {
        warnings: false
    }
}),

5.2、提取CSS插件

在webpack中编写js文件时,可以通过require的方式引入其他的静态资源,可通过loader对文件自动解析并打包文件。通常会将js 文件打包合并,css文件会在页面的header中嵌入style的方式载入页面。但开发过程中我们并不想将样式打在脚本中,最好可以独立生成css文件,以外链的形式加载。这时extract-text-webpack-plugin插件可以帮我们达到想要的效果。需要使用npm的方式加载插件,然后 参见下面的配置,就可以将js中的css文件提取,并以指定的文件名来进行加载。

  • 安装依赖

    npm install --save-dev extract-text-webpack-plugin
  • 使用

    // 可以新建多个抽离样式的文件,这样就可以有多个css文件了。
    new ExtractTextPlugin("app.css"),
    plugins: [
      // 可以新建多个抽离样式的文件,这样就可以有多个css文件了。
      new ExtractTextPlugin("app.css"),
    ]

    修改loaders中的配置:

    {
      test: /\.css$/,
      loader: ExtractTextPlugin.extract('style-loader' , 'css-loader')
    },

注意:我发现这个有一个问题,就是他只能把css抽出来,但是sass的样式不能分离出来。

5.3、新建index.html页面插件

  • 安装依赖

    npm install --save-dev html-webpack-plugin
  • 使用

    // 自动生成index.html页面插件
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    plugins: [
      new HtmlWebpackPlugin({
          template: './src/template.html',
          htmlWebpackPlugin: {
              "files": {
                  "css": ["app.css"],
                  "js": ["vendors.js","bundle.js"]
              }
          },
          minify: {
              removeComments: true,
              collapseWhitespace: true,
              removeAttributeQuotes: true
          }
      }),
    ]

    创建模板文件:template.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <meta name="keyword" content="webpack,gulp,git">
      <title>model</title>
    </head>
    <body>
    <div id="app"></div>
    </body>
    </html>

5.4、自动打开浏览器插件

在开发环境中使用,run之后自动打开浏览器

  • 安装依赖

    npm install --save-dev open-browser-webpack-plugin
  • 使用

    // 自动打开浏览器插件
    var OpenBrowserPlugin = require('open-browser-webpack-plugin');
    plugins: [
      new OpenBrowserPlugin({url: 'http://localhost:8080/'})
    ]

5.5、删除插件

在开发环境中使用,run之后自动打开浏览器

  • 安装依赖

    npm install --save-dev clean-webpack-plugin
  • 使用

    // 删除文件夹
    var CleanPlugin = require('clean-webpack-plugin');
    plugins: [
      // 构建之前先删除dist目录下面的文件夹
      new CleanPlugin(['dist']),
    ]

5.6、去掉react中的警告

plugins: [
    new webpack.DefinePlugin({
        //去掉react中的警告,react会自己判断
        'process.env': {
            NODE_ENV: '"production"'
        }
    }),
]