您的位置:新葡亰496net > 新葡亰官网 > 新葡亰496net:工程化起步,Webpack学习笔记

新葡亰496net:工程化起步,Webpack学习笔记

发布时间:2019-10-30 19:34编辑:新葡亰官网浏览(117)

    webpack 4正式发布,速度最高可提升98%

    2018/02/27 · CSS

    原文出处: Sean T. Larkin   译文出处:众成翻译/怡红公子   

    Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
    Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。

         阅读本文之前,先看下面这个webpack的配置文件,如果每一项你都懂,那本文能带给你的收获也许就比较有限,你可以快速浏览或直接跳过;如果你和十天前的我一样,对很多选项存在着疑惑,那花一段时间慢慢阅读本文,你的疑惑一定一个一个都会消失;如果你以前没怎么接触过Webpack,如果你对webpack感兴趣,本文中有一个贯穿始终的例子,如果你能把这个例子自己动手写一次,写完以后你会发现你已明明白白的走进了Webpack的大门。

    一、webpack的基本概念

    webpack 本质上是一个打包工具,它会根据代码的内容解析模块依赖,帮助我们把多个模块的代码打包。借用 webpack 官网的图片:

    新葡亰496net 1

    如上图,webpack 会把我们项目中使用到的多个代码模块(可以是不同文件类型),打包构建成项目运行仅需要的几个静态文件。webpack 有着十分丰富的配置项,提供了十分强大的扩展能力,可以在打包构建的过程中做很多事情。

     

    代号: _Legato

    今天我们愉快的宣布 webpack 4(Legato)正式发布了!你可以使用 yarn 或者 npm 获得它:

    JavaScript

    $> yarn add webpack --dev

    1
    $> yarn add webpack --dev

    或者

    JavaScript

    $> npm i webpack --save-dev

    1
    $> npm i webpack --save-dev

    Webpack

        

    1.1 入口

    如上图所示,在多个代码模块中会有一个起始的 .js 文件,这个便是 webpack 构建的入口。webpack 会读取这个文件,并从它开始解析依赖,然后进行打包。如图,一开始我们使用 webpack 构建时,默认的入口文件就是 ./src/index.js。

    在我们的项目中,一般就两种,如下:

    • 如果是单页面应用,那么可能入口只有一个;

    • 如果是多个页面的项目,那么经常是一个页面会对应一个构建入口。

    新葡亰496net,入口可以使用 entry 字段来进行配置,webpack 支持配置多个入口来进行构建:

    /**************************** 单页面配置              *****************************/// 这里就是一个入口module.exports = {
      entry: './src/index.js' }// 上述配置等同于module.exports = {
      entry: {
        main: './src/index.js'
      }
    }/**************************** 多页面配置              *****************************/// 或者配置多个入口module.exports = {
      entry: {
        foo: './src/page-foo.js',
        bar: './src/page-bar.js', 
        // ...
      }
    }// 使用数组来对多个文件进行打包module.exports = {
      entry: {
        main: [      './src/foo.js',      './src/bar.js'
        ]
      }
    }
    

    这个例子,可以理解为多个文件作为一个入口,webpack 会解析两个文件的依赖后进行打包。

    原文链接:

    为什么叫 Legato?

    首先我们会开始一个新传统:为我们以后的每个大版本设定代号!因此,我们决定将命名这个特权给予我们最大的 OpenCollective 捐赠者:trivago

    当我们向其发出请求后他们是这么回复我们的:

    [在 trivago] 我们通常以音乐主题来命名我们的项目。例如,我们之前的 JS 框架叫 “Harmony”,我们的新框架叫“Melody”。PHP 的话我们使用基于 Symfony 上层封装,我们叫它“Orchestra”。 Legato 意味着毫无间隙地连续演奏每个节奏。 这点和 Webpack 很像,Webpack 将我们的前端资源(JS,CSS甚至更多)无间隙的打包在一起。因此我们相信 “Legato” 这个代号会适合 Webpack。 ——  Patrick Gotthardt

    我们得知后也非常地激动,因为新版 Webpack 中我们所做的每一个更新目的都在于此,为了当大家在使用 Webpack 的时候敏捷连续毫无顿挫感。非常感谢过去的这些年 trivago 对 webpack 的无私捐赠支持,更感谢其为 webpack 4 命名!

    引申阅读:trivago 帮助保护 webpack 的未来

    安装Webpack

    webpack是建立在node.js环境下的,所以要使用它我们需要先安装node和npm,其相关知识这里将不介绍。

    //一个常见的Webpack配置文件
    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    module.exports = {
      entry: __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "[name]-[hash].js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: ExtractTextPlugin.extract('style', 'css?modules!postcss')
          }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"
        }),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("[name]-[hash].css")
      ]
    }
    

    1.2 loader

    webpack 中提供一种处理多种文件格式的机制,就是使用 loader。我们可以把 loader 理解为是一个转换器,负责把某种文件格式的内容转换成 webpack 可以支持打包的模块。

    举个例子,在没有添加额外插件的情况下,webpack 会默认把所有依赖打包成 js 文件,如果入口文件依赖一个 .hbs 的模板文件以及一个 .css 的样式文件,那么我们需要 handlebars-loader 来处理 .hbs 文件,需要 css-loader 来处理 .css 文件,最终把不同格式的文件都解析成 js 代码,以便打包后在浏览器中运行,简单来说就是如果需要编译一些非html、js、css文件的时候,则需要配置对应文件类型的loader进行编译处理。

    当我们需要使用不同的 loader 来解析处理不同类型的文件时,我们可以在 module.rules 字段下来配置相关的规则,例如使用 Babel 来处理 .js 文件:

    module: {  // ...
      rules: [
        {
          test: /.jsx?/, // 匹配文件路径的正则表达式,通常我们都是匹配文件类型后缀
          include: [        path.resolve(__dirname, 'src') // 指定哪些路径下的文件需要经过 loader 处理
          ],
          use: 'babel-loader', // 指定使用的 loader
        },
      ],
    }
    

    loader 是 webpack 中比较复杂的一块内容,它支撑着 webpack 来处理文件的多样性。

    什么是WebPack,为什么要使用它?

    ️‍ 有什么更新?

    webpack 4 有太多的新东西可以说了,但是我不可能在本文中列举所有的内容,否则这篇文章就要推迟很久才能发布了。因此下面我会和大家分享一些我觉得有趣的更新内容,如果大家想要看所有的更新的话可以查阅 webpack 4 的更新日志

    安装node.js

    在终端输入以下命令即可

    $ sudo apt-get install nodejs
    

    什么是WebPack,为什么要使用它?

    1.3 plugin

    在 webpack 的构建流程中,plugin 用于处理更多其他的一些构建任务。可以这么理解,模块代码转换的工作由 loader 来处理,除此之外的其他任何工作都可以交由 plugin 来完成。通过添加我们需要的 plugin,可以满足更多构建中特殊的需求。例如,要使用压缩 JS 代码的 uglifyjs-webpack-plugin 插件,只需在配置中通过 plugins 字段添加新的 plugin 即可。

    const UglifyPlugin = require('uglifyjs-webpack-plugin')module.exports = {
      plugins: [    new UglifyPlugin()
      ],
    }
    

    除了压缩 JS 代码的 uglifyjs-webpack-plugin,常用的还有定义环境变量的 DefinePlugin,生成 CSS 文件的 ExtractTextWebpackPlugin 等。

    plugin 理论上可以干涉 webpack 整个构建流程,可以在流程的每一个步骤中定制自己的构建需求。

    为什要使用WebPack

    现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。为了简化开发的复杂度,前端社区涌现出了很多好的实践方法

    • 模块化,让我们可以把复杂的程序细化为小的文件;
    • 类似于TypeScript这种在JavaScript基础上拓展的开发语言:使我们能够实现目前版本的JavaScript不能直接使用的特性,并且之后还能能装换为JavaScript文件使浏览器可以识别;
    • Scss,less等CSS预处理器
    • ...

    这些改进确实大大的提高了我们的开发效率,但是利用它们开发的文件往往需要进行额外的处理才能让浏览器识别,而手动处理又是非常繁琐的,这就为WebPack类的工具的出现提供了需求。

    webpack 4 更快(速度提升98%)!

    我们在社区中请求大家对 webpack 4 进行构建性能测试,得出的结果非常有趣。结果很惊人,构建时间降低了 60%-98%!!这里给大家分享一下某个用户的测试结果:

    新葡亰496net 2

    新葡亰496net 3

    来源: Twitter

    当然这只是一部分用户的测试数据,你可以在我的推文的回复中查看他们所有的结果。

    性能测试过程中也发现了一些 loader 的 bug 我们已经及时修复了!!PS: 我们还没有实现多进程,或者缓存功能(计划在v5版实现)。所以理论上我们的性能还有非常大的提升空间!!!!

    构件速度是我们此次发布最主要的目标。你可以把所有的功能都添加到工具中,但是如果不能节省开发时间那你加这些功能又有什么用呢?当然以上的这些都是部分示例,我们非常欢迎大家在推特上使用 #webpack #webpack4 开头提交你们的构建时间报告。

    安装npm

    在终端输入以下命令即可

    $ sudo apt-get install npm
    

    为了保证下载速度,我们直接永久设置为淘宝源为npm的registry地址。在终端里输入

    npm config set registry https://registry.npm.taobao.org
    
    • 配置成功后输入以下命令来查看是否成功
    npm config get registry
    

    如图所示即为成功

    新葡亰496net 4

    设置npm为淘宝源

    为什要使用WebPack

    现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。为了简化开发的复杂度,前端社区涌现出很多好的实践方法

    • 模块化,让我们可以把复杂的程序细化为小的文件
    • 类似于TypeScript这种在JavaScript基础上拓展的开发语言:使我们能够实现目前版本的JavaScript不能直接使用的特性,并且之后还能能装换为JavaScript文件使浏览器可以识别
    • Scss,less等CSS预处理器

    这些改进确实大大的提供了我们的开发效率,但是利用它们开发的文件需要进行额外的处理才能让浏览器识别,手动处理非常复杂,这就为WebPack类的工具的出现提供了需求。

    1.4 输出

    webpack 的输出即指 webpack 最终构建出来的静态文件,可以看看上面 webpack 官方图片右侧的那些文件。当然,构建结果的文件名、路径等都是可以配置的,使用 output 字段:

    module.exports = {  // ...
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
      },
    }// 或者多个入口生成不同文件module.exports = {
      entry: {
        foo: './src/foo.js',
        bar: './src/bar.js',
      },
      output: {
        filename: '[name].js',
        path: __dirname   '/dist',
      },
    }// 路径中使用 hash,每次构建时会有一个不同 hash 值,避免发布新版本时线上使用浏览器缓存module.exports = {  // ...
      output: {
        filename: '[name].js',
        path: __dirname   '/dist/[hash]',
      },
    }
    

    我们一开始直接使用 webpack 构建时,默认创建的输出内容就是 ./dist/main.js。

    什么是Webpack

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

    Mode, 零配置以及默认值

    我们为 webpack 新增了一个 mode 配置项。Mode 有两个值:development 或者是 production,默认值是 production。Mode 是我们为减小生产环境构建体积以及节约开发环境的构建时间提供的一种优化方案。

    如果想要了解更多 mode 配置项的内容,大家可以看看我们之前的一篇文章: webpack 4: mode 和优化

    另外,entryoutput 这些配置项也都有默认值了。这意味着你不需要每次都配置它们了,当然包括 mode。这可能意味着从现在开始,你的配置文件在我们做出如此巨大改变之后会变得非常小!

    Legato 意味着毫无间隙地连续演奏每个节奏。

    另外,我们提供零配置平台来扩展。webpack 最大的一个特色就是可扩展性。最终我们实现的零配置平台会是什么样子呢?当我们实现了 webpack presets 功能后,这意味着你可以基于零配置平台配置你自己,公司,甚至是社区的工作流配置用以继承直接使用。

    安装webpack

    1. 全局安装
      在终端输入
    sudo npm install -g webpack
    
    1. 局部安装
      进入项目目录,并在终端中输入
    npm install --save-dev webpack
    

    什么是Webpack

    WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些预设,打包为合适的格式以供浏览器使用。

    二、一个简单的 webpack 配置

    我们把上述涉及的几部分配置内容合到一起,就可以创建一个简单的 webpack 配置了,webpack 运行时默认读取项目下的 webpack.config.js 文件作为配置。

    所以我们在项目中创建一个 webpack.config.js 文件:

    const path = require('path')const UglifyPlugin = require('uglifyjs-webpack-plugin')module.exports = {
      entry: './src/index.js',
    
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
      },
    
      module: {
        rules: [
          {
            test: /.jsx?/,
            include: [          path.resolve(__dirname, 'src')
            ],
            use: 'babel-loader',
          },
        ],
      },  // 代码模块路径解析的配置
      resolve: {
        modules: [      "node_modules",      path.resolve(__dirname, 'src')
        ],
    
        extensions: [".wasm", ".mjs", ".js", ".json", ".jsx"],
      },
    
      plugins: [    new UglifyPlugin(), 
        // 使用 uglifyjs-webpack-plugin 来压缩 JS 代码
        // 如果你留意了我们一开始直接使用 webpack 构建的结果,你会发现默认已经使用了 JS 代码压缩的插件
        // 这其实也是我们命令中的 --mode production 的效果,后续的小节会介绍 webpack 的 mode 参数
      ],
    }
    

    webpack 的配置其实是一个 Node.js 的脚本,这个脚本对外暴露一个配置对象,webpack 通过这个对象来读取相关的一些配置。因为是 Node.js 脚本,所以可玩性非常高,你可以使用任何的 Node.js 模块,如上述用到的 path 模块,当然第三方的模块也可以。

    创建了 webpack.config.js 后再执行 webpack 命令,webpack 就会使用这个配置文件的配置了。

    有的时候我们开始一个新的前端项目,并不需要从零开始配置 webpack,而可以使用一些工具来帮助快速生成 webpack 配置,或者利用别人已经写好的webpack配置文件即可。

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

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

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

    新葡亰496net 5

    Grunt和Gulp的工作流程

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

    新葡亰496net 6

    Webpack工作方式

    如果实在要把二者进行比较,Webpack的处理速度更快更直接,能打包更多不同类型的文件。

    ✂ 再见 CommonsChunkPlugin

    在新版中我们废弃并移除了 CommonsChunkPlugin,并且使用一些默认值以及更容易被复写的新 API optimize.splitChunks 来代替它。现在你可以在大部分场景中享受自动分块带来的便利了!

    如果想要了解更多该 API 可以查看这篇文章:webpack 4: 代码分块以及分块优化

    使用Webpack

    1. 创建一个工作目录,并进入这个目录创建一个项目,在终端输入以下命令,会自动生成一个package.json文件,这是一个标准的npm说明文件,里面包含了一些信息,包含了项目的依赖模块,自定义脚本任务等。
    npm init
    

    输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。

    1. 在本项目中安装Webpack作为依赖包,在终端输入以下命令
    npm install --save-dev webpack
    
    1. 创建app和public文件夹

      • app文件夹用来存放原始数据和我们将写的JavaScript模块
      • public文件夹用来存放准备给浏览器读取的数据(包括使用webpack生成的打包后的js文件以及一个index.html文件)
    2. 创建几个html和js文件做一个简单的例子

      • public下创建index.html文件,加载通过webpack打包之后的bundle.js文件
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="utf-8">
          <title>Webpack Sample Project</title>
      </head>
      <body>
          <div id='root'>
          </div>
          <script src="bundle.js"></script>
      </body>
      </html>
      
      • app目录下创建Greeter.js,用来返回一个简单的文字信息的html元素的函数
      module.exports = function() {
          var greet = document.createElement('div');
          greet.textContent = "Hi there and greetings!";
          return greet;
          };
      
      • app目录下创建main.js,用来把Greeter模块返回的结点插入页面
      var greeter = require('./Greeter.js');
      document.getElementById('root').appendChild(greeter());
      
    3. 使用webpack进行打包
      基本命令为

    webpack {entry file/入口文件} {destination for bundled file/存放bundle.js的地方}
    

    只需要指定一个入口文件,webpack将自动识别项目所依赖的其它文件,不过需要注意的是如果你的webpack没有进行全局安装,那么当你在终端中使用此命令时,需要额外指定其在node_modules中的地址,继续上面的例子,在终端中属于如下命令

    node_modules/.bin/webpack app/main.js public/bundle.js
    

    执行结果如下,可以看到帮我们打包了两个文件

    新葡亰496net 7

    webpack命令打包

    打开浏览器访问index.html可以看到如下结果

    新葡亰496net 8

    打包后index结果

    • webpack还有许多功能,通过命令行都可以实现,但是命令多了不好记也容易出错,所以webpack也提供了配置文件方式,在项目根目录下创建webpack.config.js文件,在其中编写我们所需要的配置。

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

      注:__dirname是node.js中的一个全局变量,它指向当前执行脚本所在的目录。
      接下来指定打包命令只需要在终端中输入webpack即可。执行的结果和用命令一致。

    • 打包操作还可以更简单,为了解决命令复杂且多的问题,npm还提供了引导任务执行的功能。对其进行配置后可以使用简单的npm start命令来代替这些繁琐的命令。在package.json中对npm的脚本部分进行相关设置即可,设置方法如下。

    {
      "name": "first_webpack_project",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo "Error: no test specified" && exit 1",
        //配置start命令
        "start": "webpack"
      },
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^3.4.1"
      }
    }
    

    注:npm的start是一个特殊的脚本名称,它的特殊性表现在,在命令行中使用npm start就可以执行相关命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run {script name}npm run build,以下是执行npm start后命令行的输出显示

    新葡亰496net 9

    npm start结果

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

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

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

    新葡亰496net 10

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

    新葡亰496net 11

    如果实在要把二者进行比较,Webpack的处理速度更快更直接,能打包更多不同类型的文件。

    三、搭建基本的前端开发环境

    我们日常使用的前端开发环境应该是怎样的?我们可以尝试着把基本前端开发环境的需求列一下:

    • 构建我们发布需要的 HTML、CSS、JS 文件

    • 使用 CSS 预处理器来编写样式

    • 处理和压缩图片

    • 使用 Babel 来支持 ES6/7/8 新特性

    • 本地提供静态服务以方便开发调试

    开始使用Webpack

    初步了解了Webpack工作方式后,我们一步步的开始学习使用Webpack。

    WebAssembly 支持

    Webpack 现在默认支持在任何本地 WebAssembly 模块中的 importexport 语句。这意味着你可以直接在 Rust, C , C 或者其它 WebAssembly 支持语言中使用 import

    Webpack功能

    开始使用Webpack

    初步了解了Webpack工作方式后,我们一步步的开始学习使用Webpack。

    3.1 关联 HTML

    webpack 默认从作为入口的 .js 文件进行构建(更多是基于 SPA 去考虑),但通常一个前端项目都是从一个页面(即 HTML)出发的,最简单的方法是,创建一个 HTML 文件,使用 script 标签直接引用构建好的 JS 文件,如:

    <script src="./dist/bundle.js"></script>
    

    但是,如果我们的文件名或者路径会变化,例如使用 [hash] 来进行命名,那么最好是将 HTML 引用路径和我们的构建结果关联起来,这个时候我们可以使用 html-webpack-plugin。

    html-webpack-plugin 是一个独立的 node package,所以在使用之前我们需要先安装它,把它安装到项目的开发依赖中:

    npm install html-webpack-plugin -D # 或者
    yarn add html-webpack-plugin -D
    

    然后在 webpack 配置中,将 html-webpack-plugin 添加到 plugins 列表中:

    const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {  // ...
      plugins: [    new HtmlWebpackPlugin(),
      ],
    }
    

    这样配置好之后,构建时 html-webpack-plugin 会为我们创建一个 HTML 文件,其中会引用构建出来的 JS 文件。实际项目中,默认创建的 HTML 文件并没有什么用,我们需要自己来写 HTML 文件,可以通过 html-webpack-plugin 的配置,传递一个写好的 HTML 模板:

    module.exports = {  // ...
      plugins: [    new HtmlWebpackPlugin({
          filename: 'index.html', // 配置输出文件名和路径
          template: 'assets/index.html', // 配置文件模板
        }),
      ],
    }
    

    这样,通过 html-webpack-plugin 就可以将我们的页面和构建 JS 关联起来,回归日常,从页面开始开发。如果需要添加多个页面关联,那么实例化多个 html-webpack-plugin, 并将它们都放到 plugins 字段数组中就可以了。

    安装

    Webpack可以使用npm安装,新建一个空的练习文件夹(此处命名为webpack sample progect),在终端中转到该文件夹后执行下述指令就可以完成安装。

    //全局安装
    npm install -g webpack
    //安装到你的项目目录
    npm install --save-dev webpack
    

    模块类型简介以及 .mjs 支持

    之前,JS 一直都是 Webpack 唯一的一等模块类型。当用户不需要使用 CSS/HTML 的时可能会造成一些麻烦。我们基于新的 API 抽象实现了 JS 类型。目前,我们已经支持5种模块类型实现:

    • javascript/auto: _(webpack 3 默认值)_ 所有的 JS 模块规范都可用:CommonJS,AMD,ESM
    • javascript/esm:EcmaScript 模块规范,其它的模块规范都不可用 (.mjs 文件的默认值)
    • javascript/dynamic: 仅支持 CommonJS 和 AMD,EcmaScript 模块规范不可用
    • json: JSON 数据,使用 requireimport 导入 JSON 数据时可用 (.json 文件的默认值)
    • webassembly/experimental: WebAssembly 模块 (.wasm 文件的默认值,目前还是试验阶段)
    • 另外 webpack 支持直接查找 .wasm, .mjs, .js.json 后缀文件

    最让人激动的是,我们甚至可以支持 CSS 和 HTML 模块类型(计划在 webpack 4.x – 5 间版本实现)。 它将允许我们直接将 HTML 作为入口文件!

    生成Source Maps

    开发总是离不开调试,如果可以更加方便的调试当然就能提高开发效率,不过打包后的文件有时候你是不容易找到出错了的地方对应的源代码的位置的,Source Maps就是来帮我们解决这个问题的。

    通过简单的配置后,Webpack在打包时可以为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

    devtool选项 配置结果
    source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度
    cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便
    eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项
    cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点

    在webpack的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:

    devtool选项 配置结果
    source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度
    cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便
    eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项
    cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点

    按照说明选择一种你希望的生成方式,在webpack.config.js中进行配置

    module.exports = {
        //配置生成Source Maps,选择合适的选项
        devtool: "eval-source-map",
        //唯一入口文件
        entry: __dirname   "/app/main.js",
        output: {
            //打包后的文件存放的地方
            path: __dirname   "/public",
            //打包后输出文件的文件名
            filename: "bundle.js"
        }
    }
    

    安装

    Webpack可以使用npm安装,新建一个空的练习文件夹(此处命名为webpack sample progect),在终端中转到该文件夹后执行下述指令就可以完成安装。

    //全局安装
    npm install -g webpack
    //安装到你的项目目录
    npm install --save-dev webpack
    

    3.2 构建 CSS

    我们编写 CSS,并且希望使用 webpack 来进行构建,为此,需要在配置中引入 loader 来解析和处理 CSS 文件:

    module.exports = {
      module: {
        rules: [      // style-loader 和 css-loader 都是单独的 node package,需要安装。
          {
            test: /.css/,
            include: [          path.resolve(__dirname, 'src'),
            ],
            use: [          'style-loader',          'css-loader',
            ],
          },
        ],
      }
    }
    

    我们创建一个 index.css 文件,并在 index.js 中引用它,然后进行构建。

    import "./index.css"
    

    可以发现,构建出来的文件并没有 CSS,先来看一下新增两个 loader 的作用:

    • css-loader 负责解析 CSS 代码,主要是为了处理 CSS 中的依赖,例如 @import 和 url() 等引用外部文件的声明;

    • style-loader 会将 css-loader 解析的结果转变成 JS 代码,运行时动态插入 style 标签来让 CSS 代码生效。

    经由上述两个 loader 的处理后,CSS 代码会转变为 JS,和 index.js 一起打包了。如果需要单独把 CSS 文件分离出来,我们需要使用 extract-text-webpack-plugin 插件。

    extract-text-webpack-plugin 这个插件在笔者写作时并未发布支持 webpack 4.x 的正式版本,所以安装的时候需要指定使用它的 alpha 版本:npm install extract-text-webpack-plugin@next -D 或者 yarn add extract-text-webpack-plugin@next -D。如果你用的是 webpack 3.x 版本,直接用 extract-text-webpack-plugin 现有的版本即可。

    看一个简单的例子:

    const ExtractTextPlugin = require('extract-text-webpack-plugin')module.exports = {  // ...
      module: {
        rules: [
          {
            test: /.css$/,        // 因为这个插件需要干涉模块转换的内容,所以需要使用它对应的 loader
            use: ExtractTextPlugin.extract({ 
              fallback: 'style-loader',
              use: 'css-loader',
            }), 
          },
        ],
      },
      plugins: [    // 引入插件,配置文件名,这里同样可以使用 [hash]
        new ExtractTextPlugin('index.css'),
      ],
    }
    

    正式使用Webpack前的准备

    1. 在上述练习文件夹中创建一个package.json文件,这是一个标准的npm说明文件,里面蕴含了丰富的信息,包括当前项目的依赖模块,自定义的脚本任务等等。在终端中使用npm init命令可以自动创建这个package.json文件

      npm init
      

    输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。

    1. package.json文件已经就绪,我们在本项目中安装Webpack作为依赖包

      // 安装Webpack
      npm install --save-dev webpack
      
    2. 回到之前的空文件夹,并在里面创建两个文件夹,app文件夹和public文件夹,app文件夹用来存放原始数据和我们将写的JavaScript模块,public文件夹用来存放准备给浏览器读取的数据(包括使用webpack生成的打包后的js文件以及一个index.html文件)。在这里还需要创建三个文件,index.html 文件放在public文件夹中,两个js文件(Greeter.js和main.js)放在app文件夹中,此时项目结构如下图所示

      新葡亰496net 12
      项目结构

    index.html文件只有最基础的html代码,它唯一的目的就是加载打包后的js文件(bundle.js)

    <!DOCTYPE html>
    <html lang="en">
      <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元素的函数。

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

    main.js用来把Greeter模块返回的节点插入页面。

    //main.js 
    var greeter = require('./Greeter.js');
    document.getElementById('root').appendChild(greeter());
    

    如果你正在使用 HtmlWebpackPlugin

    在发布之前我们预留了一个月的时间 来升级所有的插件和 loader 以适配 webpack 4 的 API。然而,Jan Nicklas 因为工作原因没办法参加,因此我们不得不发布了一个 html-webpack-plugin 的 fork 版。你可以使用如下命令安装它:

    JavaScript

    $> yarn add html-webpack-plugin@webpack-contrib/html-webpack-plugin

    1
    $> yarn add html-webpack-plugin@webpack-contrib/html-webpack-plugin

    当 Jan 本月底从海外工作回来时,我们会将我们的更新合并回 jantimon/html-webpack-plugin 仓库中!在此之前,如果你有任何问题,可以提交到这里!

    如果你是插件或 loader 的开发者,你可以查看我们的迁移指南:webpack 4: 插件/loader 迁移指南

    使用webpack构建本地服务器

    Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现监测你的代码的修改,并自动刷新修改后的结果,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖。安装命令如下

    npm install --save-dev webpack-dev-server
    

    devserver作为webpack配置选项中的一项,具有以下配置选项

    devserver配置选项 功能描述
    contentBase 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public"目录)
    port 设置默认监听端口,如果省略,默认为”8080“
    inline 设置为true,当源文件改变时会自动刷新页面
    colors 设置为true,使终端输出的文件为彩色的
    historyApiFallback 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html

    webpack.config.js中进行配置devserver:

    module.exports = {
        //配置生成Source Maps,选择合适的选项
        devtool: "eval-source-map",
        //唯一入口文件
        entry: __dirname   "/app/main.js",
        output: {
            //打包后的文件存放的地方
            path: __dirname   "/public",
            //打包后输出文件的文件名
            filename: "bundle.js"
        },
    
        devServer: {
            //本地服务器所加载的页面所在的目录
            contentBase: "./public",
            //终端中输出结果为彩色
            colors: true,
            //不跳转
            historyApiFallback: true,
            //实时刷新
            inline: true
        }
    }
    

    正式使用Webpack前的准备

    1. 在上述练习文件夹中创建一个package.json文件,这其实是一个标准的npm说明文件,里面蕴含了丰富的信息,包括当前项目的依赖模块,自定义的脚本任务。在终端中使用npm init命令可以自动创建这个package.json文件

               

    npm init
    

           
    输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。

        2.   package.json文件已经就绪,我们在本项目中安装Webpack作为依赖包

        

    package.json文件已经就绪,我们在本项目中安装Webpack作为依赖包
    

      3. 回到之前的空文件夹,并在里面创建两个文件夹,app文件夹和public文件夹,app文件夹用来存放原始数据和所写的JavaScript模块,public文件夹用来存放准备给浏览器读取的数据(包括使用webpack生成的打包后的js文件以及一个index.html文件)。在这里还需要创建三个文件,index.html 文件放在public文件夹中,两个js文件(Greeters.js和main.js)放在app文件夹中,此时项目结构如下图所示

       新葡亰496net 13

    index.html文件只有最基础的html代码,它唯一的目的就是加载打包后的js文件(bundle.js)

    <!DOCTYPE html>
    <html lang="en">
      <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元素的函数。

    //main.js 
    var greeter = require('./Greeter.js');
    document.getElementById('root').appendChild(greeter());
    

    main.js用来把Greeter模块返回的节点插入页面。

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

    3.3 CSS 预处理器

    在上述使用 CSS 的基础上,通常我们会使用 Less/Sass 等 CSS 预处理器,webpack 可以通过添加对应的 loader 来支持,以使用 Less 为例,我们可以在官方文档中找到对应的 loader。

    我们需要在上面的 webpack 配置中,添加一个配置来支持解析后缀为 .less 的文件:

    module.exports = {  // ...
      module: {
        rules: [
          {
            test: /.less$/,        // 因为这个插件需要干涉模块转换的内容,所以需要使用它对应的 loader
            use: ExtractTextPlugin.extract({ 
              fallback: 'style-loader',
              use: [            'css-loader', 
                'less-loader',
              ],
            }), 
          },
        ],
      },  // ...}
    

    正式使用Webpack

    webpack可以在终端中使用,其最基础的命令是

    webpack {entry file/入口文件} {destination for bundled file/存放bundle.js的地方}
    

    只需要指定一个入口文件,webpack将自动识别项目所依赖的其它文件,不过需要注意的是如果你的webpack没有进行全局安装,那么当你在终端中使用此命令时,需要额外指定其在node_modules中的地址,继续上面的例子,在终端中属于如下命令

    //webpack非全局安装的情况
    node_modules/.bin/webpack app/main.js public/bundle.js
    

    结果如下

    新葡亰496net 14

    termialResult1

    可以看出webpack同时编译了main.js 和Greeter,js,现在打开index.html,可以看到如下结果

    新葡亰496net 15

    htmlResult1

    有没有很激动,已经成功的使用Webpack打包了一个文件了。不过如果在终端中进行复杂的操作,还是不太方便且容易出错的,接下来看看Webpack的另一种使用方法。

    还有!

    还有更多的特性没有在本文列出,我们强烈建议大家去看一下我们的官方更新日志

    Loaders

    通过使用不同的loader,webpack通过调用外部的脚本或工具可以对各种各样的格式的文件进行处理,比如说分析JSON文件并把它转换为JavaScript文件,或者说把下一代的JS文件(ES6,ES7)转换为现代浏览器可以识别的JS文件。

    Loaders需要单独安装并且需要在webpack.config.js下的modules关键字下进行配置,Loaders的配置选项包括以下几方面:

    |选项|描述|是否必须|
    |test|一个匹配loaders所处理的文件的拓展名的正则表达式|是|
    |loader|loader的名称|是|
    |include/exclude|手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)|否|
    |query|为loaders提供额外的设置选项|否|

    我们把Greeter.js里的问候消息放在一个单独的JSON文件里,并通过合适的配置使Greeter.js可以读取该JSON文件的值,配置方法如下:

    1. 首先安装可以读取json文件的值的loader
    npm install --save-dev json-loader
    
    1. webpack.config.js中进行配置json-loader:
    module.exports = {
        ...
        ...
        ...
    
        //在配置文件里添加JSON loader
        module: {
            loaders: [
                {
                    test: /.json$/,
                    loader: "json-loader"
                }
            ]
        },
    }
    
    1. 创建带有问候信息的JSON文件(在app下创建config.json文件)
    {
      "greetText": "Hi there and greetings from JSON!"
    }
    
    1. 更新app/Greeter.js的内容为:
    //从config.json读取
    var config = require('./config.json');
    
    module.exports = function() {
        var greet = document.createElement('div');
        greet.textContent = config.greetText;
        return greet;
    };
    

    正式使用Webpack

    webpack可以在终端中使用,其最基础的命令是

    webpack {entry file/入口文件} {destination for bundled file/存放bundle.js的地方}
    

    只需要指定一个入口文件,webpack将自动识别项目所依赖的其它文件,不过需要注意的是如果你的webpack没有进行全局安装,那么当你在终端中使用此命令时,需要额外指定其在node_modules中的地址,继续上面的例子,在终端中属于如下命令

    //webpack非全局安装的情况
    node_modules/.bin/webpack app/main.js public/bundle.js
    

    结果如下

    新葡亰496net 16

    可以看出webpack同时编译了main.js 和Greeter,js,现在打开index.html,可以看到如下结果

    新葡亰496net 17

    有没有很激动,已经成功的使用Webpack打包了一个文件了。不过如果在终端中进行复杂的操作,还是不太方便且容易出错的,接下来看看Webpack的另一种使用方法。

    3.4 处理图片文件

    在前端项目的样式中总会使用到图片,虽然我们已经提到 css-loader 会解析样式中用 url() 引用的文件路径,但是图片对应的 jpg/png/gif 等文件格式,webpack 处理不了。是的,我们只要添加一个处理图片的 loader 配置就可以了,现有的 file-loader 就是个不错的选择。

    file-loader 可以用于处理很多类型的文件,它的主要作用是直接输出文件,把构建后的文件路径返回。配置很简单,在 rules中添加一个字段,增加图片类型文件的解析配置:

    module.exports = {  // ...
      module: {
        rules: [
          {
            test: /.(png|jpg|gif)$/,
            use: [
              {
                loader: 'file-loader',
                options: {},
              },
            ],
          },
        ],
      },
    }
    

    通过配置文件来使用Webpack

    Webpack拥有很多其它的比较高级的功能(比如说本文后面会介绍的loaders和plugins),这些功能其实都可以通过命令行模式实现,但是正如已经提到的,这样不太方便且容易出错的,一个更好的办法是定义一个配置文件,这个配置文件其实也是一个简单的JavaScript模块,可以把所有的与构建相关的信息放在里面。

    还是继续上面的例子来说明如何写这个配置文件,在当前练习文件夹的根目录下新建一个名为webpack.config.js的文件,并在其中进行最最简单的配置,如下所示,它包含入口文件路径和存放打包后文件的地方的路径。

    module.exports = {
      entry:  __dirname   "/app/main.js",//已多次提及的唯一入口文件
      output: {
        path: __dirname   "/public",//打包后的文件存放的地方
        filename: "bundle.js"//打包后输出文件的文件名
      }
    }
    

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

    现在如果你需要打包文件只需要在终端里你运行webpack(非全局安装需使用node_modules/.bin/webpack)命令就可以了,这条命令会自动参考webpack.config.js文件中的配置选项打包你的项目,输出结果如下

    新葡亰496net 18

    webpack

    又学会了一种使用Webpack的方法,而且不用管那烦人的命令行参数了,有没有感觉很爽。有没有想过如果可以连webpack(非全局安装需使用node_modules/.bin/webpack)这条命令都可以不用,那种感觉会不会更爽~,继续看下文。

    v4 的文档在哪?

    我们马上要完成迁移指南和v4 的文档了。你可以访问 文档仓库 切换到 next 分支来获取更新情况,当然能搭把手帮个忙是再好不过了!

    Babel

    Babel其实是一个编译JavaScript的平台,它的强大之处表现在可以通过编译帮你达到以下目的:

    • 下一代的JavaScript标准(ES6,ES7),这些标准目前并未被当前的浏览器完全的支持
    • 使用基于JavaScript进行了拓展的语言,比如React的JSX
    1. 安装所有Babel所有的依赖包
    npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
    
    1. webpack.config.js中进行配置babel
    module.exports = {
        ...
        ...
        ...
    
        //在配置文件里添加JSON loader
        module: {
            loaders: [
                {
                    test: /.json$/,
                    loader: "json-loader"
                },
                {
                    test: /.js$/,
                    exclude: /node_modules/,
                    //在webpack的module部分的loaders里进行配置即可
                    loader: 'babel-loader',
                    query: {
                        presets: ['es2015', 'react']
                    }
                }
            ]
        },
    }
    

    现在你的webpack的配置已经允许你使用ES6以及JSX的语法了。

    现在使用React进行测试,先安装 React 和 React-DOM,在终端中输入

    npm install --save react react-dom
    
    1. 更新app/Greeter.js的内容为:
    //Greeter,js
    //导入React
    import React, {Component} from 'react'
    //从config.json读取
    import config from './config.json';
    
    class Greeter extends Component{
      render() {
        return (
          <div>
            {config.greetText}
          </div>
        );
      }
    }
    
    export default Greeter
    
    1. 更新app/main.js内容为:
    import React from 'react';
    import {render} from 'react-dom';
    import Greeter from './Greeter';
    
    render(<Greeter />, document.getElementById('root'));
    

    通过配置文件来使用Webpack

    Webpack拥有很多其它的比较高级的功能(比如说本文后面会介绍的loaders和plugins),这些功能其实都可以通过命令行模式实现,但是正如已经提到的,这样不太方便且容易出错的,一个更好的办法是定义一个配置文件,这个配置文件其实是一个简单的JavaScript模块,可以把所有的与构建相关的信息放在里面。

    还是继续上面的例子来说明如何写这个配置文件,在当前练习文件夹的根目录下新建一个名为webpack.config.js的文件,并在其中进行最最简单的配置,如下所示,它包含入口文件路径和存放打包后文件的地方的路径。

    module.exports = {
      entry:  __dirname   "/app/main.js",//已多次提及的唯一入口文件
      output: {
        path: __dirname   "/public",//打包后的文件存放的地方
        filename: "bundle.js"//打包后输出文件的文件名
      }
    }
    

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

    现在如果你需要打包文件只需要在终端里你运行webpack(非全局安装需使用node_modules/.bin/webpack)命令就可以了,这条命令会自动参考webpack.config.js文件中的配置选项打包你的项目,输出结果如下

    新葡亰496net 19

    又学会了一种使用Webpack的方法,而且不用管那烦人的命令行参数了,有没有感觉很爽。有没有想过如果可以连webpack(非全局安装需使用node_modules/.bin/webpack)这条命令都可以不用,那种感觉会不会更爽~,继续看下文。

    3.5 使用 Babel

    Babel 是一个让我们能够使用 ES 新特性的 JS 编译工具,我们可以在 webpack 中配置 Babel,以便使用 ES6、ES7 标准来编写 JS 代码。

    module.exports = {  // ...
      module: {
        rules: [
          {
            test: /.jsx?/, // 支持 js 和 jsx
            include: [          path.resolve(__dirname, 'src'), // src 目录下的才需要经过 babel-loader 处理
            ],
            loader: 'babel-loader',
          },
        ],
      },
    }
    

    Babel 的相关配置可以在目录下使用 .babelrc 文件来处理,详细参考 Babel 官方文档 .babelrc。

    更快捷的执行打包任务

    执行类似于node_modules/.bin/webpack这样的命令其实是比较烦人且容易出错的,不过值得庆幸的是npm可以引导任务执行,对其进行配置后可以使用简单的npm start命令来代替这些繁琐的命令。在package.json中对npm的脚本部分进行相关设置即可,设置方法如下。

    {
      "name": "webpack-sample-project",
      "version": "1.0.0",
      "description": "Sample webpack project",
      "scripts": {
        "start": "webpack" //配置的地方就是这里啦,相当于把npm的start命令指向webpack命令
      },
      "author": "zhang",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^1.12.9"
      }
    }
    

    注:package.json中的脚本部分已经默认在命令前添加了node_modules/.bin路径,所以无论是全局还是局部安装的Webpack,你都不需要写前面那指明详细的路径了。

    npm的start是一个特殊的脚本名称,它的特殊性表现在,在命令行中使用npm start就可以执行相关命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run {script name}npm run build,以下是执行npm start后命令行的输出显示

    新葡亰496net 20

    npmStartTermialResult

    现在只需要使用npm start就可以打包文件了,有没有觉得webpack也不过如此嘛,不过不要太小瞧Webpack,其强大的功能包含在其一系列可供配置的选项中,我们一项项来看。

    ‍ 各框架 cli 工具支持怎么样了?

    在过去的一个月我们也为每个框架的脚手架工作确保它们能支持 webpack 4。甚至最流行的库例如 lodash-es, RxJS 已经支持 sideEffects 选项,因此使用它们的最新版后你会发现打包体积会大幅度的减小。

    AngularCLI 团队已经计划在近几周即将发布的大版本中直接使用 webpack 4!如果你想要知道最新进展,可以直接联系他们,并询问他们你能帮什么忙而不是直接询问它什么时候发布

    Babel的配置选项

    Babel可以在webpack.config.js中进行配置页可以分块单独配置,当配置较多时,最好单独配置创建一个.babelrc的babel配置文件,webpack会自动调用.babelrc其中的配置选项。

    1. 更新webpack.config.js内容为:
    module.exports = {
        ...
        ...
        ...
    
        //在配置文件里添加JSON loader
        module: {
            loaders: [
                {
                    test: /.json$/,
                    loader: "json-loader"
                },
                {
                    test: /.js$/,
                    exclude: /node_modules/,
                    //在webpack的module部分的loaders里进行配置即可
                    loader: 'babel-loader',
                }
            ]
        },
    }
    
    1. 创建.babelrc文件并添加内容如下:
    {
      "presets": ["react", "es2015"]
    }
    

    更快捷的执行打包任务

    执行类似于node_modules/.bin/webpack这样的命令其实是比较烦人且容易出错的,不过值得庆幸的是npm可以引导任务执行,对其进行配置后可以使用简单的npm start命令来代替这些繁琐的命令。在package.json中对npm的脚本部分进行相关设置即可,设置方法如下。

    {
      "name": "webpack-sample-project",
      "version": "1.0.0",
      "description": "Sample webpack project",
      "scripts": {
        "start": "webpack" //配置的地方就是这里啦,相当于把npm的start命令指向webpack命令
      },
      "author": "zhang",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^1.12.9"
      }
    }
    

    注:package.json中的脚本部分已经默认在命令前添加了node_modules/.bin路径,所以无论是全局还是局部安装的Webpack,你都不需要写前面那指明详细的路径了。

    npm的start是一个特殊的脚本名称,它的特殊性表现在,在命令行中使用npm start就可以执行相关命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run {script name}npm run build,以下是执行npm start后命令行的输出显示

    新葡亰496net 21

    现在只需要使用npm start就可以打包文件了,有没有觉得webpack也不过如此嘛,不过不要太小瞧Webpack,其强大的功能包含在其一系列可供配置的选项中,我们一项项来看。

    3.6 启动静态服务

    至此,我们完成了处理多种文件类型的 webpack 配置。我们可以使用 webpack-dev-server 在本地开启一个简单的静态服务来进行开发。

    在项目下安装 webpack-dev-server,然后添加启动命令到 package.json 中:

    "scripts": {  "build": "webpack --mode production",  "start": "webpack-dev-server --mode development"}
    
    也可以全局安装 webpack-dev-server,但通常建议以项目开发依赖的方式进行安装,然后在 npm package 中添加启动脚本。
    

    尝试着运行 npm run start 或者 yarn start,然后就可以访问  来查看你的页面了。默认是访问 index.html,如果是其他页面要注意访问的 URL 是否正确。

    Webpack的强大功能

    为什么你用如此多的 emoji 表情?

    因为这样写文章很开心呀!大家也可以试试 。

    模块

    Webpack有一个不可不说的优点,它把所有的文件都可以当做模块处理,包括你的JavaScript代码,也包括CSS和fonts以及图片等等等,只有通过合适的loaders,它们都可以被当做模块被处理。

    Webpack的强大功能

    四、webpack 如何解析代码模块路径

    在 webpack 支持的前端代码模块化中,我们可以使用类似 import * as m from './index.js' 来引用代码模块 index.js。

    引用第三方类库则是像这样:import React from 'react'。webpack 构建的时候,会解析依赖后,然后再去加载依赖的模块文件,那么 webpack 如何将上述编写的 ./index.js 或 react 解析成对应的模块文件路径呢?

    在 JavaScript 中尽量使用 ECMAScript 2015 Modules 语法来引用依赖。
    

    webpack 中有一个很关键的模块 enhanced-resolve 就是处理依赖模块路径的解析的,这个模块可以说是 Node.js 那一套模块路径解析的增强版本,有很多可以自定义的解析配置。

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

    开发总是离不开调试,如果可以更加方便的调试当然就能提高开发效率,不过打包后的文件有时候你是不容易找到出错了的地方对应的源代码的位置的,Source Maps就是来帮我们解决这个问题的。
    通过简单的配置后,Webpack在打包时可以为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

    在webpack的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:

    devtool选项 配置结果
    source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度;
    cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便;
    eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项;
    cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点;

    正如上表所述,上述选项由上到下打包速度越来越快,不过同时也具有越来越多的负面作用,较快的构建速度的后果就是对打包后的文件的的执行有一定影响。

    在学习阶段以及在小到中性的项目上,eval-source-map是一个很好的选项,不过记得只在开发阶段使用它,继续上面的例子,进行如下配置

    module.exports = {
      devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      }
    }
    

    cheap-module-eval-source-map方法构建速度更快,但是不利于调试,推荐在大型项目考虑da时间成本是使用。

    接下来?

    我们已经在着手计划下一个版本 webpack 4.x 和 5 的特性了,包括但不限于:

    • ESM 模块导出支持
    • 持久缓存
    • WebAssembly 支持从 experimental 升级为 stable 稳定版。并增加 tree-shaking 和未使用代码去除!
    • Presets —— 基于零配置设计,任何东西都能支持零配置
    • CSS 模块类型——支持 CSS 作为入口文件(再见吧 ExtractTextWebpackPlugin)
    • HTML 模块类型——支持 HTML 作为入口文件
    • URL/文件 模块类型
    • 自定义模块类型
    • 多线程
    • 重新定义我们的组织章程和计划任务
    • Google Summer of Code (之后单独写文说明!!!)

    CSS

    webpack提供两个工具处理样式表,css-loaderstyle-loader,二者处理的任务不同,css-loader使你能够使用类似@importurl(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。

    1. 安装css-loaderstyle-loader模块,在终端输入以下命令
    npm install --save-dev style-loader css-loader
    
    1. 更新webpack.config.js内容,使其能够使用css-loaderstyle-loader
    module.exports = {
        ...
        ...
        ...
    
        //在配置文件里添加JSON loader
        module: {
            loaders: [
                {
                    test: /.json$/,
                    loader: "json-loader"
                },
                {
                    test: /.js$/,
                    exclude: /node_modules/,
                    //在webpack的module部分的loaders里进行配置即可
                    loader: 'babel-loader',
                },
                {
                    test: /.css$/,
                    //添加对样式表的处理
                    loader: 'style-loader!css-loader'
                }
            ]
        },
    }
    
    >注:感叹号的作用在于使同一文件能够使用不同类型的loader
    
    1. app文件夹下创建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;
    }
    
    1. 由于webpack只有单一的入口,其它的模块需要通过 import, require, url等导入相关位置,为了让webpack能找到”main.css“文件,我们把它导入”main.js “中,更新后的main.js文件内容为:
    import React from 'react';
    import {render} from 'react-dom';
    import Greeter from './Greeter';
    
    //使用require导入css文件
    import './main.css';
    
    render(<Greeter/>, document.getElementById('root'));
    

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

    开发总是离不开调试,如果可以更加方便的调试当然就能提高开发效率,不过打包后的文件有时候你是不容易找到出错了的地方对应的源代码的位置的,Source Maps就是来帮我们解决这个问题的。

    通过简单的配置后,Webpack在打包时可以为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

    在webpack的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:

    devtool选项 配置结果
    source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度;
    cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便;
    eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项;
    cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点;

    正如上表所述,上述选项由上到下打包速度越来越快,不过同时也具有越来越多的负面作用,较快的构建速度的后果就是对打包后的文件的的执行有一定影响。

    在学习阶段以及在小到中性的项目上,eval-source-map是一个很好的选项,不过记得只在开发阶段使用它,继续上面的例子,进行如下配置

     

    module.exports = {
      devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      }
    }
    

    cheap-module-eval-source-map方法构建速度更快,但是不利于调试,推荐在大型项目考虑da时间成本是使用。

    五、模块解析规则

    我们简单整理一下基本的模块解析规则,以便更好地理解后续 webpack 的一些配置会产生的影响。

    解析相对路径

    • 1.查找相对当前模块的路径下是否有对应文件或文件夹

    • 2.是文件则直接加载

    • 3.是文件夹则继续查找文件夹下的 package.json 文件

    • 4.有 package.json 文件则按照文件中 main 字段的文件名来查找文件

    • 5.无 package.json 或者无 main 字段则查找 index.js 文件

    解析模块名:查找当前文件目录下,父级目录及以上目录下的 node_modules 文件夹,看是否有对应名称的模块

    解析绝对路径(不建议使用):直接查找对应路径的文件

    在 webpack 配置中,和模块路径解析相关的配置都在 resolve 字段下:

    module.exports = {
      resolve: {    // ...
      }
    }
    

    使用webpack构建本地服务器

    想不想让你的浏览器监测你的代码的修改,并自动刷新修改后的结果,其实Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现你想要的这些功能,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖

    npm install --save-dev webpack-dev-server
    

    devserver作为webpack配置选项中的一项,具有以下配置选项

    devserver配置选项 功能描述
    contentBase 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public"目录)
    port 设置默认监听端口,如果省略,默认为”8080“
    inline 设置为true,当源文件改变时会自动刷新页面
    colors 设置为true,使终端输出的文件为彩色的
    historyApiFallback 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html

    继续把这些命令加到webpack的配置文件中,现在的配置文件如下所示

    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      },
    
      devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        colors: true,//终端中输出结果为彩色
        historyApiFallback: true,//不跳转
        inline: true//实时刷新
      } 
    }
    

    再次感谢

    对于我们的贡献者团队,核心团队,loader 和插件作者,那些第一次提交他们的提交,或者帮助解决故障的人:我们不能不感谢你们。这个产品是为你而生的,你们帮助塑造了它

    2018 我们将注定要抛弃老古董思维,迎接 JS 的美丽复兴!❤

    我之前已经多次强调过,在 JS 复兴 的今天,没有社区的帮忙,webpack 是不会变的如此强大,可持续以及蓬勃的生长。如果没有你们的帮助,webpack 可能现在也还只是另外一款普通的构建工具[Yet Another Build Tool (YABT)]而已。

    via:

    1 赞 收藏 评论

    新葡亰496net 22

    CSS module

    CSS modules 的技术就意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack从一开始就对CSS模块化提供了支持,在CSS loader中进行配置后,你所需要做的一切就是把”modules“传递都所需要的地方,然后就可以直接把CSS的类名传递到组件的代码中,且这样做只对当前组件有效,不必担心在不同的模块中具有相同的类名可能会造成的问题.

    1. 更新webpack.config.js内容,使其能使用CSS module
    module.exports = {
        ...
        ...
        ...
    
        //在配置文件里添加JSON loader
        module: {
            loaders: [
                {
                    test: /.json$/,
                    loader: "json-loader"
                },
                {
                    test: /.js$/,
                    exclude: /node_modules/,
                    //在webpack的module部分的loaders里进行配置即可
                    loader: 'babel-loader',
                },
                {
                    test: /.css$/,
                    //添加对样式表的处理
                    //仅仅添加了?modules-loader
                    loader: 'style-loader!css-loader?modules-loader'
                }
            ]
        },
    }
    
    1. app文件夹下创建Greeter.css并增加内容为:
    .root {
      background-color: #eee;
      padding: 10px;
      border: 3px solid #ccc;
    }
    
    1. 导入.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
    

    使用webpack构建本地服务器

    想不想让你的浏览器监测你都代码的修改,并自动刷新修改后的结果,其实Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现你想要的这些功能,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目的依赖

    npm install --save-dev webpack-dev-server
    

    devserver作为webpack配置选项中的一项,具有以下配置选项

    devserver配置选项 功能描述
    contentBase 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public”目录)
    port 设置默认监听端口,如果省略,默认为”8080“
    inline 设置为true,当源文件改变时会自动刷新页面
    colors 设置为true,使终端输出的文件为彩色的
    historyApiFallback 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html

    继续把这些命令加到webpack的配置文件中,现在的配置文件如下所示

    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      },
    
      devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        colors: true,//终端中输出结果为彩色
        historyApiFallback: true,//不跳转
        inline: true//实时刷新
      } 
    }
    

    六、常用的一些配置

    我们先从一些简单的需求来阐述 webpack 可以支持哪些解析路径规则的自定义配置。

    Loaders

    鼎鼎大名的Loaders登场了!

    Loaders是webpack中最让人激动人心的功能之一了。通过使用不同的loader,webpack通过调用外部的脚本或工具可以对各种各样的格式的文件进行处理,比如说分析JSON文件并把它转换为JavaScript文件,或者说把下一代的JS文件(ES6,ES7)转换为现代浏览器可以识别的JS文件。或者说对React的开发而言,合适的Loaders可以把React的JSX文件转换为JS文件。

    Loaders需要单独安装并且需要在webpack.config.js下的modules关键字下进行配置,Loaders的配置选项包括以下几方面:

    • test:一个匹配loaders所处理的文件的拓展名的正则表达式(必须)
    • loader:loader的名称(必须)
    • include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
    • query:为loaders提供额外的设置选项(可选)

    继续上面的例子,我们把Greeter.js里的问候消息放在一个单独的JSON文件里,并通过合适的配置使Greeter.js可以读取该JSON文件的值,配置方法如下

    //安装可以装换JSON的loader
    npm install --save-dev json-loader
    
    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      },
    
      module: {//在配置文件里添加JSON loader
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          }
        ]
      },
    
      devServer: {
        contentBase: "./public",
        colors: true,
        historyApiFallback: true,
        inline: true
      }
    }
    

    创建带有问候信息的JSON文件(命名为config.json)

    //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;
    };
    

    Loaders很好,不过有的Loaders使用起来比较复杂,比如说Babel。

    CSS预处理器

    Sass 和 Less之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variables, nesting, mixins, inheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句,

    常用的CSS处理器loaders:

    • Less Loader
    • Sass Loader
    • Stylus Loader
    1. 安装postcss-loaderautoprefixer(自动添加前缀的插件)
    npm install --save-dev postcss-loader autoprefixer
    
    1. 更新webpack.config.js内容,使其能使用postcss-loaderautoprefixer,只需要新建一个postcss关键字,并在里面申明依赖的插件
    module.exports = {
        ...
        ...
        ...
    
        //在配置文件里添加JSON loader
        module: {
            loaders: [
                {
                    test: /.json$/,
                    loader: "json-loader"
                },
                {
                    test: /.js$/,
                    exclude: /node_modules/,
                    //在webpack的module部分的loaders里进行配置即可
                    loader: 'babel-loader',
                },
                {
                    test: /.css$/,
                    //添加对样式表的处理
                    //仅仅添加了?modules
                    loader: 'style-loader!css-loader?modules-loader!postcss-loader'
                }
            ]
        },
    }
    
    1. webpack.config.js同级目录下创建postcss.config.js文件,配置内容如下;
    module.exports = {
      plugins: [
        require('autoprefixer')
      ]
    }
    

    Loaders

    鼎鼎大名的Loaders登场了!

    Loaders是webpack中最让人激动人心的功能之一了。通过使用不同的loader,webpack通过调用外部的脚本或工具可以对各种各样的格式的文件进行处理,比如说分析JSON文件并把它转换为JavaScript文件,或者说把下一代的JS文件(ES6,ES7)转换为现代浏览器可以识别的JS文件。或者说对React的开发而言,合适的Loaders可以把React的JSX文件转换为JS文件。

    Loaders需要单独安装并且需要在webpack.config.js下的modules关键字下进行配置,Loaders的配置选项包括以下几方面:

    • test:一个匹配loaders所处理的文件的拓展名的正则表达式(必须)
    • loader:loader的名称(必须)
    • include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
    • query:为loaders提供额外的设置选项(可选)

    继续上面的例子,我们把Greeter.js里的问候消息放在一个单独的JSON文件里,并通过合适的配置使Greeter.js可以读取该JSON文件的值,配置方法如下

    //安装可以装换JSON的loader
    npm install --save-dev json-loader
    
    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      },
    
      module: {//在配置文件里添加JSON loader
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          }
        ]
      },
    
      devServer: {
        contentBase: "./public",
        colors: true,
        historyApiFallback: true,
        inline: true
      }
    }
    

    创建带有问候信息的JSON文件(命名为config.json)

    //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;
    };
    

    Loaders很好,不过有的Loaders使用起来比较复杂,比如说Babel。

    6.1 resolve.alias

    假设我们有个 utils 模块极其常用,经常编写相对路径很麻烦,希望可以直接 import 'utils' 来引用,那么我们可以配置某个模块的别名,如:

    alias: {  // 这里使用 path.resolve 和 __dirname 来获取绝对路径
      utils: path.resolve(__dirname, 'src/utils') 
    }
    

    上述的配置是模糊匹配,意味着只要模块路径中携带了 utils 就可以被替换掉,如:

    // 等同于 import '[项目绝对路径]/src/utils/query.js'import 'utils/query.js' 
    

    如果需要进行精确匹配可以使用:

    alias: {  // 只会匹配 import 'utils'
      utils$: path.resolve(__dirname, 'src/utils') 
    }
    

    Babel

    Babel其实是一个编译JavaScript的平台,它的强大之处表现在可以通过编译帮你达到以下目的:

    • 下一代的JavaScript标准(ES6,ES7),这些标准目前并未被当前的浏览器完全的支持;
    • 使用基于JavaScript进行了拓展的语言,比如React的JSX

    插件

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

    Babel

    Babel其实是一个编译JavaScript的平台,它的强大之处表现在可以通过编译帮你达到以下目的:

    • 下一代的JavaScript标准(ES6,ES7),这些标准目前并未被当前的浏览器完全的支持;
    • 使用基于JavaScript进行了拓展的语言,比如React的JSX

    6.2 resolve.extensions

    extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx'],// 这里的顺序代表匹配后缀的优先级,例如对于 index.js 和 index.jsx,会优先选择 index.js
    

    看到数组中配置的字符串大概就可以猜到,这个配置的作用是和文件后缀名有关的。是的,这个配置可以定义在进行模块路径解析时,webpack 会尝试帮你补全那些后缀名来进行查找,例如有了上述的配置,当你在 src/utils/ 目录下有一个 common.js 文件时,就可以这样来引用:

    import * as common from './src/utils/common'
    

    webpack 会尝试给你依赖的路径添加上 extensions 字段所配置的后缀,然后进行依赖路径查找,所以可以命中 src/utils/common.js 文件。

    但如果你是引用 src/styles 目录下的 common.css 文件时,如 import './src/styles/common',webpack 构建时则会报无法解析模块的错误。

    你可以在引用时添加后缀,import './src/styles/common.css' 来解决,或者在 extensions 添加一个 .css 的配置:

    extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx', '.css'],  
    

    Babel的安装与配置

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

    我们先来一次性安装这些依赖包

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

    在webpack中配置Babel的方法如下

    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel',//在webpack的module部分的loaders里进行配置即可
            query: {
              presets: ['es2015','react']
            }
          }
        ]
      },
    
      devServer: {
        contentBase: "./public",
        colors: true,
        historyApiFallback: true,
        inline: true
      }
    }
    

    现在你的webpack的配置已经允许你使用ES6以及JSX的语法了。继续用上面的例子进行测试,不过这次我们会使用React,记得先安装 React 和 React-DOM

    npm install --save react react-dom
    

    使用ES6的语法,更新Greeter.js并返回一个React组件

    //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
    

    使用ES6的模块定义和渲染Greeter模块

    import React from 'react';
    import {render} from 'react-dom';
    import Greeter from './Greeter';
    
    render(<Greeter />, document.getElementById('root'));
    

    如何使用插件

    要使用某个插件,我们需要通过npm安装它,然后要做的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)

    1. 添加一个显示版权声明的插件,在webpack.config.js中更新内容如下
    var webpack = require('webpack');
    
    module.exports = {
        ...
        ...
        ...
    
        plugins: [
            //在这个数组中new一个就可以了
            new webpack.BannerPlugin("Copyright Flying Unicorns inc.")
      ],
    }
    

    Babel的安装与配置

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

    我们先来一次性安装这些依赖包

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

    在webpack中配置Babel的方法如下

    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel',//在webpack的module部分的loaders里进行配置即可
            query: {
              presets: ['es2015','react']
            }
          }
        ]
      },
    
      devServer: {
        contentBase: "./public",
        colors: true,
        historyApiFallback: true,
        inline: true
      }
    }
    

    现在你的webpack的配置已经运行你使用ES6以及JSX的语法了。继续用上面的例子进行测试,不过要使用React,不要忘了先安装 React 和 React-DOM

    npm install --save react react-dom
    

    使用ES6的语法,更新Greeter.js并返回一个React组件

    //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
    

    使用ES6的模块定义和渲染Greeter模块

    import React from 'react';
    import {render} from 'react-dom';
    import Greeter from './Greeter';
    
    render(<Greeter />, document.getElementById('root'));
    

    6.3 resolve.modules

    前面的内容有提到,对于直接声明依赖名的模块(如 react ),webpack 会类似 Node.js 一样进行路径搜索,搜索 node_modules 目录,这个目录就是使用 resolve.modules 字段进行配置的,默认就是:

    resolve: {
      modules: ['node_modules'],
    },
    

    通常情况下,我们不会调整这个配置,但是如果可以确定项目内所有的第三方依赖模块都是在项目根目录下的 node_modules 中的话,那么可以在 node_modules 之前配置一个确定的绝对路径:

    resolve: {
      modules: [    path.resolve(__dirname, 'node_modules'), // 指定当前目录下的 node_modules 优先查找
        'node_modules', // 如果有一些类库是放在一些奇怪的地方的,你可以添加自定义的路径或者目录
      ],
    }
    

    这样配置在某种程度上可以简化模块的查找,提升构建速度。

    Babel的配置选项

    Babel其实可以完全在webpack.config.js中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。我们现在的babel的配置并不算复杂,不过之后我们会再加一些东西,因此现在我们就提取出相关部分,分两个配置文件进行配置(webpack会自动调用.babelrc里的babel配置选项),如下:

    // webpack.config.js
    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          }
        ]
      },
    
      devServer: {...} // Omitted for brevity
    }
    
    //.babelrc
    {
      "presets": ["react", "es2015"]
    }
    

    到目前为止,我们已经知道了,对于模块,Webpack能提供非常强大的处理功能,那那些是模块呢。

    HtmlWebpackPlugin

    这个插件的作用是依据一个简单的模板,帮你生成最终的Html5文件,这个文件中自动引用了你打包后的JS文件。每次编译都在文件名中插入一个不同的哈希值。

    这个插件自动完成了我们之前手动做的一些事情,在正式使用之前需要对一直以来的项目结构做一些改变:

    1. 移除public文件夹,利用此插件,HTML5文件会自动生成,此外CSS已经通过前面的操作打包到JS中了,public文件夹里。

    2. 在app目录下,创建一个Html文件模板,这个模板包含title等其它你需要的元素,在编译过程中,本插件会依据此模板生成最终的html页面,会自动添加所依赖的 css, js,favicon等文件,在本例中我们命名模板文件名称为index.tmpl.html,模板源代码如下

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Webpack Sample Project</title>
      </head>
      <body>
        <div id='root'>
        </div>
      </body>
    </html>
    
    1. 更新webpack的配置文件,方法同上,新建一个build文件夹用来存放最终的输出文件
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        ...
        ...
        ...
    
        output: {
            //打包后的文件存放的地方
            path: __dirname   "/build",
            //打包后输出文件的文件名
            filename: "bundle.js"
        },
        ...
        ...
        ...
    
        plugins: [
             new HtmlWebpackPlugin({
                template: __dirname   "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
            })
      ],
    }
    

    Babel的配置选项

    Babel其实可以完全在webpack.config.js中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项可以放在一个单独的名为 “.babelrc” 的配置文件中。我们现在的babel的配置并不算复杂,不过之后我们会再加一些东西,因此现在我们就提取出相关部分,分两个配置文件进行配置(不用担心webpack会自动调用.babelrc里的babel配置选项),如下:

    // webpack.config.js
    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/public",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          }
        ]
      },
    
      devServer: {...} // Omitted for brevity
    }
    
    //.babelrc
    {
      "presets": ["react", "es2015"]
    }
    

    到目前为止,我们已经知道了,对于模块,Webpack能提供非常强大的处理功能,那那些是模块呢。

    6.4 resolve.mainFields

    有 package.json 文件则按照文件中 main 字段的文件名来查找文件
    

    我们之前有提到这么一句话,其实确切的情况并不是这样的,webpack 的 resolve.mainFields 配置可以进行调整。当引用的是一个模块或者一个目录时,会使用 package.json 文件的哪一个字段下指定的文件,默认的配置是这样的:

    resolve: {  // 配置 target === "web" 或者 target === "webworker" 时 mainFields 默认值是:
      mainFields: ['browser', 'module', 'main'],  // target 的值为其他时,mainFields 默认值为:
      mainFields: ["module", "main"],
    }
    

    因为通常情况下,模块的 package 都不会声明 browser 或 module 字段,所以便是使用 main 了。

    在 NPM packages 中,会有些 package 提供了两个实现,分别给浏览器和 Node.js 两个不同的运行时使用,这个时候就需要区分不同的实现入口在哪里。如果你有留意一些社区开源模块的 package.json 的话,你也许会发现 browser 或者 module 等字段的声明。

    一切皆模块

    Webpack有一个不可不说的优点,它把所有的文件都可以当做模块处理,包括你的JavaScript代码,也包括CSS和fonts以及图片等等等,只有通过合适的loaders,它们都可以被当做模块被处理。

    优化插件

    webpack提供了一些在发布阶段非常有用的优化插件,它们大多来自于webpack社区,可以通过npm安装,通过以下插件可以完成产品发布阶段所需的功能

    • OccurenceOrderPlugin :为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
    • UglifyJsPlugin:压缩JS代码;
    • ExtractTextPlugin:分离CSS和JS文件
    1. 安装插件,在终端中输入
    npm install --save-dev extract-text-webpack-plugin
    
    1. 在在webpack.config.js中更新配置内容如下
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    module.exports = {
        .
        ...
        ...
    
        plugins: [
             new HtmlWebpackPlugin({
                template: __dirname   "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
                new webpack.optimize.OccurenceOrderPlugin(),
                new webpack.optimize.UglifyJsPlugin(),
                new ExtractTextPlugin("style.css")
            })
      ],
    }
    

    注:

    • 本文参考 http://www.jianshu.com/p/42e11515c10f,作为个人学习笔记
    • 上述环境在ubuntu16.04 lts中测试成功
    • 上述文字皆为个人看法,如有错误或建议请及时联系我

    一切皆模块

    Webpack有一个不可不说的优点,它把所有的文件都可以当做模块处理,包括你的JavaScript代码,也包括CSS和fonts以及图片等等等,只有通过合适的loaders,它们都可以被当做模块被处理。

    6.5 resolve.mainFiles

    当目录下没有 package.json 文件时,我们说会默认使用目录下的 index.js 这个文件,其实这个也是可以配置的,是的,使用 resolve.mainFiles 字段,默认配置是:

    resolve: {
      mainFiles: ['index'], // 你可以添加其他默认使用的文件名},
    

    通常情况下我们也无须修改这个配置,index.js 基本就是约定好了。

    CSS

    webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同,css-loader使你能够使用类似@import 和 url(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。

    继续上面的例子

    //安装
    npm install --save-dev style-loader css-loader
    
    //使用
    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: 'style!css'//添加对样式表的处理
          }
        ]
      },
    
      devServer: {...}
    }
    

    注:感叹号的作用在于使同一文件能够使用不同类型的loader

    接下来,在app文件夹里创建一个名字为"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;
    }
    

    你还记得吗?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'));
    

    通常情况下,css会和js打包到同一个文件中,并不会打包为一个单独的css文件,不过通过合适的配置webpack也可以把css打包为单独的文件的。
    不过这也只是webpack把css当做模块而已,咱们继续看看一个真的CSS模块的实践。

    CSS

    webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同,css-loader使你能够使用类似@import和 url(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。

    继续上面的例子

    //安装
    npm install --save-dev style-loader css-loader
    
    //使用
    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: 'style!css'//添加对样式表的处理
          }
        ]
      },
    
      devServer: {...}
    }
    

    :感叹号的作用在于使同一文件能够使用不同类型的loader

    接下来,在app文件夹里创建一个名字为”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;
    }
    

    你还记得吗?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'));
    

    通常情况下,css会和js打包到同一个文件中,并不会打包为一个单独的css文件,不过通过合适的配置webpack也可以把css打包为单独的文件的。

    不过这也只是webpack把css当做模块而已,咱们继续看看一个真的CSS模块的实践。

    6.6 resolve.resolveLoader

    这个字段 resolve.resolveLoader 用于配置解析 loader 时的 resolve 配置,原本 resolve 的配置项在这个字段下基本都有。我们看下默认的配置:

    resolve: {
      resolveLoader: {
        extensions: ['.js', '.json'],
        mainFields: ['loader', 'main'],
      },
    },
    

    这里提供的配置相对少用,我们一般遵从标准的使用方式,使用默认配置,然后把 loader 安装在项目根路径下的 node_modules 下就可以了。

    CSS module

    在过去的一些年里,JavaScript通过一些新的语言特性,更好的工具以及更好的实践方法(比如说模块化)发展得非常迅速。模块使得开发者把复杂的代码转化为小的,干净的,依赖声明明确的单元,且基于优化工具,依赖管理和加载管理可以自动完成。
    不过前端的另外一部分,CSS发展就相对慢一些,大多的样式表却依旧是巨大且充满了全局类名,这使得维护和修改都非常困难和复杂。

    最近有一个叫做 CSS modules 的技术就意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack从一开始就对CSS模块化提供了支持,在CSS loader中进行配置后,你所需要做的一切就是把”modules“传递都所需要的地方,然后就可以直接把CSS的类名传递到组件的代码中,且这样做只对当前组件有效,不必担心在不同的模块中具有相同的类名可能会造成的问题。具体的代码如下

    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {...},
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: 'style!css?modules'//跟前面相比就在后面加上了?modules
          }
        ]
      },
    
      devServer: {...}
    }
    

    创建一个Greeter.css文件

    .root {
      background-color: #eee;
      padding: 10px;
      border: 3px solid #ccc;
    }
    

    导入.root到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 module

    在过去的一些年里,JavaScript通过一些新的语言特性,更好的工具以及更好的实践方法(比如说模块化)发展得非常迅速。模块使得开发者把复杂的代码转化为小的,干净的,依赖声明明确的单元,且基于优化工具,依赖管理和加载管理可以自动完成。

    不过前端的另外一部分,CSS发展就相对慢一些,大多的样式表却依旧是巨大且充满了全局类名,这使得维护和修改都非常困难和复杂。

    最近有一个叫做 CSS modules 的技术就意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack从一开始就对CSS模块化提供了支持,在CSS loader中进行配置后,你所需要做的一切就是把”modules“传递都所需要的地方,然后就可以直接把CSS的类名传递到组件的代码中,且这样做只对当前组件有效,不必担心在不同的模块中具有相同的类名可能会造成的问题。具体的代码如下

    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {...},
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: 'style!css?modules'//跟前面相比就在后面加上了?modules
          }
        ]
      },
    
      devServer: {...}
    }
    

    创建一个Greeter.css文件

    .root {
      background-color: #eee;
      padding: 10px;
      border: 3px solid #ccc;
    }
    

    导入.root到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 也是一个很大的主题,有兴趣的话可以去官方文档查看更多消息。

    七、配置loader

    CSS预处理器

    Sass 和 Less之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variables, nesting, mixins, inheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句,
    你现在可能都已经熟悉了,在webpack里使用相关loaders进行配置就可以使用了,以下是常用的CSS 处理loaders

    • Less Loader
    • Sass Loader
    • Stylus Loader

    不过其实也存在一个CSS的处理平台-PostCSS,它可以帮助你的CSS实现更多的功能,在其CSS官方文档可了解更多相关知识。

    举例来说如何使用PostCSS,我们使用PostCSS来为CSS代码自动添加适应不同浏览器的CSS前缀。

    首先安装postcss-loader 和 autoprefixer(自动添加前缀的插件)

    npm install --save-dev postcss-loader autoprefixer
    

    接下来,在webpack配置文件中进行设置,只需要新建一个postcss关键字,并在里面申明依赖的插件,如下,现在你写的css会自动根据Can i use里的数据添加不同前缀了。

    //webpack配置文件
    module.exports = {
      devtool: 'eval-source-map',
      entry: __dirname   "/app/main.js",
      output: {...},
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: 'style!css?modules!postcss'
          }
        ]
      },
    
      postcss: [
        require('autoprefixer')//调用autoprefixer插件
      ],
    
      devServer: {...}
    }
    

    到现在,本文已经涉及到处理JS的Babel和处理CSS的PostCSS,它们其实也是两个单独的平台,配合Webpack可以很好的发挥它们的作用。接下来介绍Webpack中另一个非常重要的功能-Plugins

    CSS预处理器

    Sass 和 Less之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variables, nesting, mixins, inheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句,你现在可能都已经熟悉了,在webpack里使用相关loaders进行配置就可以使用了,以下是常用的CSS 处理loaders

    • Less Loader
    • Sass Loader
    • Stylus Loader

    不过其实也存在一个CSS的处理平台-PostCSS,它可以帮助你的CSS实现更多的功能,在其CSS官方文档可了解更多相关知识。

    举例来说如何使用PostCSS,我们使用PostCSS来为CSS代码自动添加适应不同浏览器的CSS前缀。

    首先安装postcss-loader 和 autoprefixer(自动添加前缀的插件)

    npm install --save-dev postcss-loader autoprefixer
    

    接下来,在webpack配置文件中进行设置,只需要新建一个postcss关键字,并在里面申明依赖的插件,如下,现在你写的css会自动根据Can i use里的数据添加不同前缀了。

    //webpack配置文件
    module.exports = {
      devtool: 'eval-source-map',
      entry: __dirname   "/app/main.js",
      output: {...},
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: 'style!css?modules!postcss'
          }
        ]
      },
    
      postcss: [
        require('autoprefixer')//调用autoprefixer插件
      ],
    
      devServer: {...}
    }
    

    到现在,本文已经涉及到处理JS的Babel和处理CSS的PostCSS,它们其实也是两个单独的平台,配合Webpack可以很好的发挥它们的作用。接下来介绍Webpack中另一个非常重要的功能-Plugins

    7.1 loader 匹配规则

    当我们需要配置 loader 时,都是在 module.rules 中添加新的配置项,在该字段中,每一项被视为一条匹配使用 loader 的规则。

    先来看一个基础的例子:

    module.exports = {  // ...
      module: {
        rules: [ 
          {
            test: /.jsx?/, // 条件
            include: [ 
              path.resolve(__dirname, 'src'),
            ], // 条件
            use: 'babel-loader', // 规则应用结果�
          }, // 一个 object 即一条规则
          // ...
        ],
      },
    }
    

    loader 的匹配规则中有两个最关键的因素:一个是匹配条件,一个是匹配规则后的应用。

    匹配条件通常都使用请求资源文件的绝对路径来进行匹配,在官方文档中称为 resource,除此之外还有比较少用到的 issuer,则是声明依赖请求的源文件的绝对路径。举个例子:在 /path/to/app.js 中声明引入 import './src/style.scss',resource 是 /path/to/src/style.scss,issuer 是 /path/to/app.js,规则条件会对这两个值来尝试匹配。

    上述代码中的 test 和 include 都用于匹配 resource 路径,是 resource.test 和 resource.include 的简写,你也可以这么配置:

    module.exports = {  // ...
      rules: [ 
          {
            resource: { // resource 的匹配条件
              test: /.jsx?/, 
              include: [ 
                path.resolve(__dirname, 'src'),
              ],
            },        // 如果要使用 issuer 匹配,便是 issuer: { test: ... }
            use: 'babel-loader',
          },      // ...
        ], 
    }
    
    issuer 规则匹配的场景比较少见,你可以用它来尝试约束某些类型的文件中只能引用某些类型的文件。
    

    当规则的条件匹配时,便会使用对应的 loader 配置,如上述例子中的 babel-loader。

    插件(Plugins)

    插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
    Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

    Webpack有很多内置插件,同时也有很多第三方插件,可以让我们完成更加丰富的功能。

    插件(Plugins)

    插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
    Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

    Webpack有很多内置插件,同时也有很多第三方插件,可以让我们完成更加丰富的功能。

    7.2 规则条件配置

    大多数情况下,配置 loader 的匹配条件时,只要使用 test 字段就好了,很多时候都只需要匹配文件后缀名来决定使用什么 loader,但也不排除在某些特殊场景下,我们需要配置比较复杂的匹配条件。webpack 的规则提供了多种配置形式:

    • { test: ... } 匹配特定条件

    • { include: ... } 匹配特定路径

    • { exclude: ... } 排除特定路径

    • { and: [...] }必须匹配数组中所有条件

    • { or: [...] } 匹配数组中任意一个条件

    • { not: [...] } 排除匹配数组中所有条件

    上述的所谓条件的值可以是:

    • 字符串:必须以提供的字符串开始,所以是字符串的话,这里我们需要提供绝对路径

    • 正则表达式:调用正则的 test 方法来判断匹配

    • 函数:(path) => boolean,返回 true 表示匹配

    • 数组:至少包含一个条件的数组

    • 对象:匹配所有属性值的条件

    通过例子来帮助理解:

    rules: [
      {
        test: /.jsx?/, // 正则
        include: [      path.resolve(__dirname, 'src'), // 字符串,注意是绝对路径
        ], // 数组
        // ...
      },
      {
        test: {
          js: /.js/,
          jsx: /.jsx/,
        }, // 对象,不建议使用
        not: [
          (value) => { /* ... */ return true; }, // 函数,通常需要高度自定义时才会使用
        ],
      },
    ]
    

    上述多个配置形式结合起来就能够基本满足各种各样的构建场景了,通常我们会结合使用 test/and 和 include&exclude 来配置条件,如上述那个简单的例子。

    使用插件的方法

    要使用某个插件,我们需要通过npm安装它,然后要做的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)继续看例子,我们添加了一个实现版权声明的插件。

    //webpack.config.js
    var webpack = require('webpack');
    
    module.exports = {
      devtool: 'eval-source-map',
      entry:  __dirname   "/app/main.js",
      output: {...},
    
      module: {
        loaders: [
          { test: /.json$/, loader: "json" },
          { test: /.js$/, exclude: /node_modules/, loader: 'babel' },
          { test: /.css$/, loader: 'style!css?modules!postcss' }//这里添加PostCSS
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new webpack.BannerPlugin("Copyright Flying Unicorns inc.")//在这个数组中new一个就可以了
      ],
    
      devServer: {...}
    }
    

    通过这个插件,打包后的JS文件显示如下

    新葡亰496net 23

    bundled JavaScript file

    知道Webpack中的插件如何使用了,下面给大家推荐几个常用的插件

    使用插件的方法

    要使用某个插件,我们需要通过npm安装它,然后要做的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)继续看例子,我们添加了一个实现版权声明的插件。

    //webpack.config.js
    var webpack = require('webpack');
    
    module.exports = {
      devtool: 'eval-source-map',
      entry:  __dirname   "/app/main.js",
      output: {...},
    
      module: {
        loaders: [
          { test: /.json$/, loader: "json" },
          { test: /.js$/, exclude: /node_modules/, loader: 'babel' },
          { test: /.css$/, loader: 'style!css?modules!postcss' }//这里添加PostCSS
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new webpack.BannerPlugin("Copyright Flying Unicorns inc.")//在这个数组中new一个就可以了
      ],
    
      devServer: {...}
    }
    

    通过这个插件,打包后的JS文件显示如下

    新葡亰496net 24

    知道Webpack中的插件如何使用了,下面给大家推荐几个常用的插件

    7.3 module type

    webpack 4.x 版本强化了 module type,即模块类型的概念,不同的模块类型类似于配置了不同的 loader,webpack 会有针对性地进行处理,现阶段实现了以下 5 种模块类型。

    • javascript/auto:即 webpack 3 默认的类型,支持现有的各种 JS 代码模块类型 —— CommonJS、AMD、ESM

    • javascript/esm:ECMAScript modules,其他模块系统,例如 CommonJS 或者 AMD 等不支持,是 .mjs 文件的默认类型

    • javascript/dynamic:CommonJS 和 AMD,排除 ESM

    • javascript/json:JSON 格式数据,require 或者 import 都可以引入,是 .json 文件的默认类型

    • webassembly/experimental:WebAssembly modules,当前还处于试验阶段,是 .wasm 文件的默认类型

    如果不希望使用默认的类型的话,在确定好匹配规则条件时,我们可以使用 type 字段来指定模块类型,例如把所有的 JS 代码文件都设置为强制使用 ESM 类型:

    {
      test: /.js/,
      include: [    path.resolve(__dirname, 'src'),
      ],
      type: 'javascript/esm', // 这里指定模块类型},
    

    上述做法是可以帮助你规范整个项目的模块系统,但是如果遗留太多不同类型的模块代码时,建议还是直接使用默认的 javascript/auto。

    webpack 后续的开发计划会增加对更多模块类型的支持,例如极其常见的 CSS 和 HTML 模块类型,这个特性值得我们期待一下。

    HtmlWebpackPlugin

    这个插件的作用是依据一个简单的模板,帮你生成最终的Html5文件,这个文件中自动引用了你打包后的JS文件。每次编译都在文件名中插入一个不同的哈希值。

    安装

    npm install --save-dev html-webpack-plugin
    

    这个插件自动完成了我们之前手动做的一些事情,在正式使用之前需要对一直以来的项目结构做一些改变:

    1. 移除public文件夹,利用此插件,HTML5文件会自动生成,此外CSS已经通过前面的操作打包到JS中了,public文件夹里。
    2. 在app目录下,创建一个Html文件模板,这个模板包含title等其它你需要的元素,在编译过程中,本插件会依据此模板生成最终的html页面,会自动添加所依赖的 css, js,favicon等文件,在本例中我们命名模板文件名称为index.tmpl.html,模板源代码如下
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Webpack Sample Project</title>
      </head>
      <body>
        <div id='root'>
        </div>
      </body>
    </html>
    

    3.更新webpack的配置文件,方法同上,新建一个build文件夹用来存放最终的输出文件

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          { test: /.json$/, loader: "json" },
          { test: /.js$/, exclude: /node_modules/, loader: 'babel' },
          { test: /.css$/, loader: 'style!css?modules!postcss' }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
        })
      ],
    
      devServer: {
        colors: true,
        historyApiFallback: true,
        inline: true
      }
    }
    

    HtmlWebpackPlugin

    这个插件的作用是依据一个简单的模板,帮你生成最终的Html5文件,这个文件中自动引用了你打包后的JS文件。每次编译都在文件名中插入一个不同的哈希值。

    安装

    npm install --save-dev html-webpack-plugin
    

    这个插件自动完成了我们之前手动做的一些事情,在正式使用之前需要对一直以来的项目结构做一些改变:

    1. 移除public文件夹,利用此插件,HTML5文件会自动生成,此外CSS已经通过前面的操作打包到JS中了,public文件夹里。
    2. 在app目录下,创建一个Html文件模板,这个模板包含title等其它你需要的元素,在编译过程中,本插件会依据此模板生成最终的html页面,会自动添加所依赖的 css, js,favicon等文件,在本例中我们命名模板文件名称为index.tmpl.html,模板源代码如下

      Webpack Sample Project

        3.更新webpack的配置文件,方法同上,新建一个build文件夹用来存放最终的输出文件

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          { test: /.json$/, loader: "json" },
          { test: /.js$/, exclude: /node_modules/, loader: 'babel' },
          { test: /.css$/, loader: 'style!css?modules!postcss' }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
        })
      ],
    
      devServer: {
        colors: true,
        historyApiFallback: true,
        inline: true
      }
    }
    

    7.4 使用 loader 配置

    当然,在当前版本的 webpack 中,module.rules 的匹配规则最重要的还是用于配置 loader,我们可以使用 use 字段:

    rules: [
      {
        test: /.less/,
        use: [      'style-loader', // 直接使用字符串表示 loader
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1
            },
          }, // 用对象表示 loader,可以传递 loader 配置等
          {
            loader: 'less-loader',
            options: {
              noIeCompat: true
            }, // 传递 loader 配置
          },
        ],
      },
    ]
    

    我们看下上述的例子,先忽略 loader 的使用情况,单纯看看如何配置。use 字段可以是一个数组,也可以是一个字符串或者表示 loader 的对象。如果只需要一个 loader,也可以这样:use: { loader: 'babel-loader', options: { ... } }。

    我们还可以使用 options 给对应的 loader 传递一些配置项。

    Hot Module Replacement

    Hot Module Replacement(HMR)也是webpack里很有用的一个插件,它允许你在修改组件代码后,自动刷新实时预览修改后的效果。
    在webpack中实现HMR也很简单,只需要做两项配置

    1. 在webpack配置文件中添加HMR插件;
    2. 在Webpack Dev Server中添加“hot”参数;

    不过配置完这些后,JS模块其实还是不能自动热加载的,还需要在你的JS模块中执行一个Webpack提供的API才能实现热加载,虽然这个API不难使用,但是如果是React模块,使用我们已经熟悉的Babel可以更方便的实现功能热加载。

    整理下我们的思路,具体实现方法如下

    • Babel和webpack是独立的工具
    • 二者可以一起工作
    • 二者都可以通过插件拓展功能
    • HMR是一个webpack插件,它让你能浏览器中实时观察模块修改后的效果,但是如果你想让它工作,需要对模块进行额外的配额;
    • Babel有一个叫做react-transform-hrm的插件,可以在不对React模块进行额外的配置的前提下让HMR正常工作;

    更新我们的例子来实际看看如何配置

    //webpack中的配置
    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      devtool: 'eval-source-map',
      entry: __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          { test: /.json$/, loader: "json" },
          { test: /.js$/, exclude: /node_modules/, loader: 'babel' },
          { test: /.css$/, loader: 'style!css?modules!postcss' }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"
        }),
        new webpack.HotModuleReplacementPlugin()//热加载插件
      ],
    
      devServer: {
        colors: true,
        historyApiFallback: true,
        inline: true,
        hot: true
      }
    }
    

    安装react-transform-hmr

    npm install --save-dev babel-plugin-react-transform react-transform-hmr
    

    配置Babel

    {
      "presets": ["react", "es2015"],
      "env": {
        "development": {
        "plugins": [["react-transform", {
           "transforms": [{
             "transform": "react-transform-hmr",
    
             "imports": ["react"],
    
             "locals": ["module"]
           }]
         }]]
        }
      }
    }
    

    现在当你使用React时,可以热加载模块了

    Hot Module Replacement

    Hot Module Replacement(HMR)也是webpack里很有用的一个插件,它允许你在修改组件代码后,自动刷新实时预览修改后的效果。

    在webpack中实现HMR也很简单,只需要做两项配置

    1. 在webpack配置文件中添加HMR插件;
    2. 在Webpack Dev Server中添加“hot”参数;

    不过配置完这些后,JS模块其实还是不能自动热加载的,还需要在你的JS模块中执行一个Webpack提供的API才能实现热加载,虽然这个API不难使用,但是如果是React模块,使用我们已经熟悉的Babel可以更方便的实现功能热加载。

    整理下我们的思路,具体实现方法如下

    • Babel和webpack是独立的工具
    • 二者可以一起工作
    • 二者都可以通过插件拓展功能
    • HMR是一个webpack插件,它让你能浏览器中实时观察模块修改后的效果,但是如果你想让它工作,需要对模块进行额外的配额;
    • Babel有一个叫做react-transform-hrm的插件,可以在不对React模块进行额外的配置的前提下让HMR正常工作;

    更新我们的例子来实际看看如何配置

    //webpack中的配置
    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      devtool: 'eval-source-map',
      entry: __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          { test: /.json$/, loader: "json" },
          { test: /.js$/, exclude: /node_modules/, loader: 'babel' },
          { test: /.css$/, loader: 'style!css?modules!postcss' }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"
        }),
        new webpack.HotModuleReplacementPlugin()//热加载插件
      ],
    
      devServer: {
        colors: true,
        historyApiFallback: true,
        inline: true,
        hot: true
      }
    }
    

    安装react-transform-hmr

    npm install --save-dev babel-plugin-react-transform react-transform-hmr
    

    配置Babel

    {
      "presets": ["react", "es2015"],
      "env": {
        "development": {
        "plugins": [["react-transform", {
           "transforms": [{
             "transform": "react-transform-hmr",
    
             "imports": ["react"],
    
             "locals": ["module"]
           }]
         }]]
        }
      }
    }
    

    现在当你使用React时,可以热加载模块了

    7.5 loader 应用顺序

    一个匹配规则中可以配置使用多个 loader,即一个模块文件可以经过多个 loader 的转换处理,执行顺序是从最后配置的 loader 开始,一步步往前。例如,对于上面的 less 规则配置,一个 style.less 文件会途径 less-loader、css-loader、style-loader 处理,成为一个可以打包的模块。

    loader 的应用顺序在配置多个 loader 一起工作时很重要,通常会使用在 CSS 配置上,除了 style-loader 和 css-loader,你可能还要配置 less-loader 然后再加个 postcss 的 autoprefixer 等。

    上述从后到前的顺序是在同一个 rule 中进行的,那如果多个 rule 匹配了同一个模块文件,loader 的应用顺序又是怎样的呢?看一份这样的配置:

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

    这样无法法保证 eslint-loader 在 babel-loader 应用前执行。webpack 在 rules 中提供了一个 enforce 的字段来配置当前 rule 的 loader 类型,没配置的话是普通类型,我们可以配置 pre 或 post,分别对应前置类型或后置类型的 loader。

    eslint-loader 要检查的是人工编写的代码,如果在 babel-loader 之后使用,那么检查的是 Babel 转换后的代码,所以必须在 babel-loader 处理之前使用。

    还有一种行内 loader,即我们在应用代码中引用依赖时直接声明使用的 loader,如 const json = require('json-loader!./file.json') 这种。不建议在应用开发中使用这种 loader,后续我们还会再提到。

    顾名思义,所有的 loader 按照前置 -> 行内 -> 普通 -> 后置的顺序执行。所以当我们要确保 eslint-loader 在 babel-loader 之前执行时,可以如下添加 enforce 配置:

    rules: [
      {
        enforce: 'pre', // 指定为前置类型
        test: /.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
      },
    ]
    

    当项目文件类型和应用的 loader 不是特别复杂的时候,通常建议把要应用的同一类型 loader 都写在同一个匹配规则中,这样更好维护和控制。

    产品阶段的构建

    目前为止,我们已经使用webpack构建了一个完整的开发环境。但是在产品阶段,可能还需要对打包的文件进行额外的处理,比如说优化,压缩,缓存以及分离CSS和JS。

    对于复杂的项目来说,需要复杂的配置,这时候分解配置文件为多个小的文件可以使得事情井井有条,以上面的例子来说,我们创建一个“webpack.production.config.js”的文件,在里面加上基本的配置,它和原始的webpack.config.js很像,如下

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: 'style!css?modules!postcss'
          }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"
        }),
      ],
    
    }
    
    //package.json
    {
      "name": "webpack-sample-project",
      "version": "1.0.0",
      "description": "Sample webpack project",
      "scripts": {
        "start": "webpack-dev-server --progress",
        "build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"
      },
      "author": "Cássio Zen",
      "license": "ISC",
      "devDependencies": {...},
      "dependencies": {...}
    }
    

    产品阶段的构建

    目前为止,我们已经使用webpack构建了一个完整的开发环境。但是在产品阶段,可能还需要对打包的文件进行额外的处理,比如说优化,压缩,缓存以及分离CSS和JS。

    对于复杂的项目来说,需要复杂的配置,这时候分解配置文件为多个小的文件可以使得事情井井有条,以上面的例子来说,我们创建一个“webpack.production.config.js”的文件,在里面加上基本的配置,它和原始的webpack.config.js很像,如下

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: 'style!css?modules!postcss'
          }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"
        }),
      ],
    
    }
    
    //package.json
    {
      "name": "webpack-sample-project",
      "version": "1.0.0",
      "description": "Sample webpack project",
      "scripts": {
        "start": "webpack-dev-server --progress",
        "build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"
      },
      "author": "Cássio Zen",
      "license": "ISC",
      "devDependencies": {...},
      "dependencies": {...}
    }
    

    7.6 使用 noParse

    在 webpack 中,我们需要使用的 loader 是在 module.rules 下配置的,webpack 配置中的 module 用于控制如何处理项目中不同类型的模块。

    除了 module.rules 字段用于配置 loader 之外,还有一个 module.noParse 字段,可以用于配置哪些模块文件的内容不需要进行解析。对于一些不需要解析依赖(即无依赖) 的第三方大型类库等,可以通过这个字段来配置,以提高整体的构建速度。

    使用 noParse 进行忽略的模块文件中不能使用 import、require、define 等导入机制。
    
    module.exports = {  // ...
      module: {
        noParse: /jquery|lodash/, // 正则表达式
    
        // 或者使用 function
        noParse(content) {      return /jquery|lodash/.test(content)
        },
      }
    }
    

    noParse 从某种程度上说是个优化配置项,日常也可以不去使用。

    优化插件

    webpack提供了一些在发布阶段非常有用的优化插件,它们大多来自于webpack社区,可以通过npm安装,通过以下插件可以完成产品发布阶段所需的功能

    • OccurenceOrderPlugin :为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
    • UglifyJsPlugin:压缩JS代码;
    • ExtractTextPlugin:分离CSS和JS文件

    我们继续用例子来看看如何添加它们,OccurenceOrder 和 UglifyJS plugins 都是内置插件,你需要做的只是安装它们

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

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

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    module.exports = {
      entry: __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: ExtractTextPlugin.extract('style', 'css?modules!postcss')
          }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"
        }),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("style.css")
      ]
    }
    

    优化插件

    webpack提供了一些在发布阶段非常有用的优化插件,它们大多来自于webpack社区,可以通过npm安装,通过以下插件可以完成产品发布阶段所需的功能

    • OccurenceOrderPlugin:为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
    • UglifyJsPlugin:压缩JS代码;
    • ExtractTextPlugin:分离CSS和JS文件

    我们继续用例子来看看如何添加它们,OccurenceOrder 和 UglifyJS plugins 都是内置插件,你需要做的只是安装它们

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

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

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    module.exports = {
      entry: __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "bundle.js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: ExtractTextPlugin.extract('style', 'css?modules!postcss')
          }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"
        }),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("style.css")
      ]
    }
    

    8.使用 plugin

    webpack 中的 plugin 大多都提供额外的能力,它们在 webpack 中的配置都只是把插件实例添加到 plugins 字段的数组中。不过由于需要提供不同的功能,不同的插件本身的配置比较多样化。

    缓存

    缓存无处不在,使用缓存的最好方法是保证你的文件名和文件内容是匹配的(内容改变,名称相应改变)

    webpack可以把一个哈希值添加到打包的文件名中,使用方法如下,添加特殊的字符串混合体([name], [id] and [hash])到输出文件名前

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    module.exports = {
      entry: __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "[name]-[hash].js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: ExtractTextPlugin.extract('style', 'css?modules!postcss')
          }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"
        }),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("[name]-[hash].css")
      ]
    }
    

    现在用户会有合理的缓存了。

    缓存

    缓存无处不在,使用缓存的最好方法是保证你的文件名和文件内容是匹配的(内容改变,名称相应改变)

    webpack可以把一个哈希值添加到打包的文件名中,使用方法如下,添加特殊的字符串混合体([name]新葡亰496net:工程化起步,Webpack学习笔记。, [id] and [hash])到输出文件名前

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    module.exports = {
      entry: __dirname   "/app/main.js",
      output: {
        path: __dirname   "/build",
        filename: "[name]-[hash].js"
      },
    
      module: {
        loaders: [
          {
            test: /.json$/,
            loader: "json"
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /.css$/,
            loader: ExtractTextPlugin.extract('style', 'css?modules!postcss')
          }
        ]
      },
      postcss: [
        require('autoprefixer')
      ],
    
      plugins: [
        new HtmlWebpackPlugin({
          template: __dirname   "/app/index.tmpl.html"
        }),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("[name]-[hash].css")
      ]
    }
    

    现在用户会有合理的缓存了。

    8.1 DefinePlugin

    DefinePlugin 是 webpack 内置的插件,可以使用webpack.DefinePlugin 直接获取。

    这个插件用于创建一些在编译时可以配置的全局常量,这些常量的值我们可以在 webpack 的配置中去指定,例如:

    module.exports = {  // ...
      plugins: [    new webpack.DefinePlugin({      PRODUCTION: JSON.stringify(true), // const PRODUCTION = true
          VERSION: JSON.stringify('5fa3b9'), // const VERSION = '5fa3b9'
          BROWSER_SUPPORTS_HTML5: true, // const BROWSER_SUPPORTS_HTML5 = 'true'
          TWO: '1 1', // const TWO = 1   1,
          CONSTANTS: {        APP_VERSION: JSON.stringify('1.1.2') // const CONSTANTS = { APP_VERSION: '1.1.2' }
          }
        }),
      ],
    }
    

    有了上面的配置,就可以在应用代码文件中,访问配置好的变量了,如:

    console.log("Running App version "   VERSION);if(!BROWSER_SUPPORTS_HTML5) require("html5shiv");
    

    上面配置的注释已经简单说明了这些配置的效果,这里再简述一下整个配置规则。

    • 如果配置的值是字符串,那么整个字符串会被当成代码片段来执行,其结果作为最终变量的值,如上面的 "1 1",最后的结果是 2

    • 如果配置的值不是字符串,也不是一个对象字面量,那么该值会被转为一个字符串,如 true,最后的结果是 'true'

    • 如果配置的是一个对象字面量,那么该对象的所有 key 会以同样的方式去定义

    这样我们就可以理解为什么要使用 JSON.stringify() 了,因为 JSON.stringify(true) 的结果是 'true',JSON.stringify("5fa3b9") 的结果是 "5fa3b9"。

    社区中关于 DefinePlugin 使用得最多的方式是定义环境变量,例如 PRODUCTION = true 或者 DEV = true 等。部分类库在开发环境时依赖这样的环境变量来给予开发者更多的开发调试反馈,例如 react 等。

    建议使用 process.env.NODE_ENV: ... 的方式来定义 process.env.NODE_ENV,而不是使用 process: { env: { NODE_ENV: ... } } 的方式,因为这样会覆盖掉 process 这个对象,可能会对其他代码造成影响。
    

    原文链接:

    最后感谢原文作者

    总结

    这是一篇好长的文章,谢谢你的耐心,能仔细看到了这里,大概半个月前我第一次自己一步步配置项目所需的Webpack后就一直想写一篇笔记做总结,几次动笔都不能让自己满意,总觉得写不清楚。直到看到本文的英文版Webpack for React,真的有多次豁然开朗的感觉,喜欢看原文的点链接就可以看了。其实关于Webpack本文讲述得仍不完全,不过相信以进入Webpack的大门,能够更好的探索其它的关于Webpack的知识了。

    最后欢迎大家在文后发表自己的观点讨论。

    如果有人让你推荐前端技术书,请让他看这个列表 ->《经典前端技术书籍》

     转载地址:

    8.2 copy-webpack-plugin

    这个插件看名字就知道它有什么作用,没错,就是用来复制文件的。

    我们一般会把开发的所有源码和资源文件放在 src/ 目录下,构建的时候产出一个 build/ 目录,通常会直接拿 build 中的所有文件来发布。有些文件没经过 webpack 处理,但是我们希望它们也能出现在 build 目录下,这时就可以使用 CopyWebpackPlugin 来处理了。

    我们来看下如何配置这个插件:

    const CopyWebpackPlugin = require('copy-webpack-plugin')module.exports = {  // ...
      plugins: [    new CopyWebpackPlugin([
          { from: 'src/file.txt', to: 'build/file.txt', }, // 顾名思义,from 配置来源,to 配置目标路径
          { from: 'src/*.ico', to: 'build/*.ico' }, // 配置项可以使用 glob
          // 可以配置很多项复制规则
        ]),
      ],
    }
    

    8.3 extract-text-webpack-plugin

    extract-text-webpack-plugin 之前的章节有简单介绍过,我们用它来把依赖的 CSS 分离出来成为单独的文件。这里再看一下使用 extract-text-webpack-plugin 的配置:

    const ExtractTextPlugin = require('extract-text-webpack-plugin')module.exports = {  // ...
      module: {
        rules: [
          {
            test: /.css$/,        // 因为这个插件需要干涉模块转换的内容,所以需要使用它对应的 loader
            use: ExtractTextPlugin.extract({ 
              fallback: 'style-loader',
              use: 'css-loader',
            }), 
          },
        ],
      },
      plugins: [    // 引入插件,配置文件名,这里同样可以使用 [hash]
        new ExtractTextPlugin('index.css'),
      ],
    }
    

    在上述的配置中,我们使用了 index.css 作为单独分离出来的文件名,但有的时候构建入口不止一个,extract-text-webpack-plugin 会为每一个入口创建单独分离的文件,因此最好这样配置:

    plugins: [  new ExtractTextPlugin('[name].css'),
    ],
    

    这样确保在使用多个构建入口时,生成不同名称的文件。

    这里再次提及 extract-text-webpack-plugin,一个原因是它是一个蛮常用的插件,另一个原因是它的使用方式比较特别,除了在 plugins 字段添加插件实例之外,还需要调整 loader 对应的配置。

    在这里要强调的是,在 webpack 中,loader 和 plugin 的区分是很清楚的,针对文件模块转换要做的使用 loader,而其他干涉构建内容的可以使用 plugin。 ExtractTextWebpackPlugin 既提供了 plugin,也提供了 extract 方法来获取对应需要的 loader。

    8.4 ProvidePlugin

    ProvidePlugin 也是一个 webpack 内置的插件,我们可以直接使用 webpack.ProvidePlugin 来获取。

    该组件用于引用某些模块作为应用运行时的变量,从而不必每次都用 require 或者 import,其用法相对简单:

    new webpack.ProvidePlugin({
      identifier: 'module',  // ...})// 或者new webpack.ProvidePlugin({
      identifier: ['module', 'property'], // 即引用 module 下的 property,类似 import { property } from 'module'
      // ...})
    

    在你的代码中,当 identifier 被当作未赋值的变量时,module 就会被自动加载了,而 identifier 这个变量即 module 对外暴露的内容。

    注意,如果是 ES 的 default export,那么你需要指定模块的 default 属性:identifier: ['module', 'default'],。

    8.5 IgnorePlugin

    IgnorePlugin 和 ProvidePlugin 一样,也是一个 webpack 内置的插件,可以直接使用 webpack.IgnorePlugin 来获取。

    这个插件用于忽略某些特定的模块,让 webpack 不把这些指定的模块打包进去。例如我们使用 moment.js,直接引用后,里边有大量的 i18n 的代码,导致最后打包出来的文件比较大,而实际场景并不需要这些 i18n 的代码,这时我们可以使用 IgnorePlugin 来忽略掉这些代码文件,配置如下:

    module.exports = {  // ...
      plugins: [    new webpack.IgnorePlugin(/^./locale$/, /moment$/)
      ]
    }
    

    IgnorePlugin 配置的参数有两个,第一个是匹配引入模块路径的正则表达式,第二个是匹配模块的对应上下文,即所在目录名。

    九、更好地使用 webpack-dev-server

    在构建代码并部署到生产环境之前,我们需要一个本地环境,用于运行我们开发的代码。这个环境相当于提供了一个简单的服务器,用于访问 webpack 构建好的静态文件,我们日常开发时可以使用它来调试前端代码。

    9.1 webpack-dev-server 的基础使用

    webpack-dev-server 是一个 npm package,安装后在已经有 webpack 配置文件的项目目录下直接启动就可以:

    npm install webpack-dev-server -g
    webpack-dev-server --mode development 
    

    webpack-dev-server 本质上也是调用 webpack,4.x 版本的也要指定 mode,其实 webpack-dev-server 应该直接把 development 作为默认值。

    建议把 webpack-dev-server 作为开发依赖安装,然后使用 npm scripts 来启动,如:

    npm install webpack-dev-server --save-dev
    

    package 中的 scripts 配置:

    {  // ...
      "scripts": {    "start": "webpack-dev-server --mode development"
      }
    }
    
    npm run start
    

    webpack-dev-server 默认使用 8080 端口,如果你使用了 html-webpack-plugin 来构建 HTML 文件,并且有一个 index.html 的构建结果,那么直接访问  就可以看到 index.html 页面了。如果没有 HTML 文件的话,那么 webpack-dev-server 会生成一个展示静态资源列表的页面。

    9.2 webpack-dev-server 的配置

    在 webpack 的配置中,可以通过 devServer 字段来配置 webpack-dev-server,如端口设置、启动 gzip 压缩等,这里简单讲解几个常用的配置。

    public 字段用于指定静态服务的域名,默认是  ,当你使用 Nginx 来做反向代理时,应该就需要使用该配置来指定 Nginx 配置使用的服务域名。

    port 字段用于指定静态服务的端口,如上,默认是 8080,通常情况下都不需要改动。

    publicPath 字段用于指定构建好的静态文件在浏览器中用什么路径去访问,默认是 /,例如,对于一个构建好的文件 bundle.js,完整的访问路径是  publicPath: 'assets/',那么上述 bundle.js 的完整访问路径就是  URL 来作为 publicPath 的值,如 publicPath: ' HMR,那么要设置 publicPath 就必须使用完整的 URL。

    建议将 devServer.publicPath 和 output.publicPath 的值保持一致。
    

    proxy 用于配置 webpack-dev-server 将特定 URL 的请求代理到另外一台服务器上。当你有单独的后端开发服务器用于请求 API 时,这个配置相当有用。例如:

    proxy: {  '/api': {
        target: "http://localhost:3000", // 将 URL 中带有 /api 的请求代理到本地的 3000 端口的服务上
        pathRewrite: { '^/api': '' }, // 把 URL 中 path 部分的 `api` 移除掉
      },
    }
    

    webpack-dev-server 的 proxy 功能是使用 http-proxy-middleware 来实现的。

    contentBase 用于配置提供额外静态文件内容的目录,之前提到的 publicPath 是配置构建好的结果以什么样的路径去访问,而 contentBase 是配置额外的静态文件内容的访问路径,即那些不经过 webpack 构建,但是需要在 webpack-dev-server 中提供访问的静态资源(如部分图片等)。推荐使用绝对路径:

    // 使用当前目录下的 publiccontentBase: path.join(__dirname, "public") 
    
    // 也可以使用数组提供多个路径contentBase: [path.join(__dirname, "public"), path.join(__dirname, "assets")]
    
    publicPath 的优先级高于 contentBase。
    

    before 和 after 配置用于在 webpack-dev-server 定义额外的中间件,如

    before(app){  app.get('/some/path', function(req, res) { // 当访问 /some/path 路径时,返回自定义的 json 数据
        res.json({ custom: 'response' })
      })
    }
    

    before 在 webpack-dev-server 静态资源中间件处理之前,可以用于拦截部分请求返回特定内容,或者实现简单的数据 mock。

    after 在 webpack-dev-server 静态资源中间件处理之后,比较少用到,可以用于打印日志或者做一些额外处理。

    webpack-dev-server 的配置项比较多,这里只列举了一些日常比较有用的。

    9.3 webpack-dev-middleware

    如果你熟悉使用 Node.js 来开发 Web 服务,使用过 Express 或者 Koa,那么对中间件的概念应该会有所了解。

    简而言之,中间件就是在 Express 之类的 Web 框架中实现各种各样功能(如静态文件访问)的这一部分函数。多个中间件可以一起协同构建起一个完整的 Web 服务器。

    webpack-dev-middleware 就是在 Express 中提供 webpack-dev-server 静态服务能力的一个中间件,我们可以很轻松地将其集成到现有的 Express 代码中去,就像添加一个 Express 中间件那么简单。

    首先安装 webpack-dev-middleware 依赖:

    npm install webpack-dev-middleware --save-dev
    

    接着创建一个 Node.js 服务的脚本文件,如 app.js:

    const webpack = require('webpack')const middleware = require('webpack-dev-middleware')const webpackOptions = require('./webpack.config.js') // webpack 配置文件的路径// 本地的开发环境默认就是使用 development modewebpackOptions.mode = 'development'const compiler = webpack(webpackOptions)const express = require('express')const app = express()app.use(middleware(compiler, {  // webpack-dev-middleware 的配置选项}))// 其他 Web 服务中间件// app.use(...)app.listen(3000, () => console.log('Example app listening on port 3000!'))
    

    然后用 Node.js 运行该文件即可:

    node app.js # 使用刚才创建的 app.js 文件
    

    使用 webpack-dev-server 的好处是相对简单,直接安装依赖后执行命令即可,而使用 webpack-dev-middleware 的好处是可以在既有的 Express 代码基础上快速添加 webpack-dev-server 的功能,同时利用 Express 来根据需要添加更多的功能,如 mock 服务、代理 API 请求等。

    其实 webpack-dev-server 也是基于 Express 开发的,前面提及的 webpack-dev-server 中 before 或 after 的配置字段,也可以用于编写特定的中间件来根据需要添加额外的功能。

    9.4 实现一个简单的 mock 服务

    在前端的日常开发工作中,我们本地需要的不仅仅是提供静态内容访问的服务,还需要模拟后端 API 数据来做一些应用测试工作,这个时候我们需要一个 mock 数据的服务,而 webpack-dev-server 的 before 或 proxy 配置,又或者是 webpack-dev-middleware 结合 Express,都可以帮助我们来实现简单的 mock 服务。

    这一部分内容涉及比较多的 Node.js 代码实现,这里不做过于详细的例子解释,只提供一些实现的思路。

    我们最主要的需求是当浏览器请求某一个特定的路径时(如 /some/path ),可以访问我们想要的数据内容。

    我们先基于 Express app 实现一个简单 mock 功能的方法:

    module.export = function mock(app) {  app.get('/some/path', (req, res) => {    res.json({ data: '' })
      })  // ... 其他的请求 mock
      // 如果 mock 代码过多,可以将其拆分成多个代码文件,然后 require 进来}
    

    然后应用到配置中的 before 字段:

    const mock = require('./mock')// ...before(app) {  mock(app) // 调用 mock 函数}
    

    这样的 mock 函数照样可以应用到 Express 中去,提供与 webpack-dev-middleware 同样的功能。

    由于 app.get('', (req, res) => { ... }) 的 callback 可以拿到 req 请求对象,其实可以根据请求参数来改变返回的结果,即通过参数来模拟多种场景的返回数据来协助测试多种场景下的代码应用。

    当你单独实现或者使用一个 mock 服务时,你可以通过 proxy 来配置部分路径代理到对应的 mock 服务上去,从而把 mock 服务集成到当前的开发服务中去,相对来说也很简单。

    当你和后端开发进行联调时,亦可使用 proxy 代理到对应联调使用的机器上,从而可以使用本地前端代码的开发环境来进行联调。当然了,连线上环境的异常都可以这样来尝试定位问题。

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:新葡亰496net:工程化起步,Webpack学习笔记

    关键词:

上一篇:基础面试题

下一篇:没有了