微信号:FrontDev

介绍:分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯

从 webpack v1 迁移到 webpack v2

2017-01-26 21:12 前端大全

 (点击上方公众号,可快速关注)


原文:webpack  译文:众成翻译

www.zcfy.cc/article/migrating-from-v1-to-v2-2378.html

如有好文章投稿,请点击 → 这里了解详情


resolve.root, resolve.fallback,resolve.modulesDirectories


上述配置项被一个单独的配置项 resolve.modules 取代。详见 resolving(https://webpack.js.org/configuration/resolve)。


resolve: {

-   root: path.join(__dirname, "src")

+   modules: [

+     path.join(__dirname, "src"),

+     "node_modules"

+   ]

  }


resolve.extensions


此配置项不再需要传一个空字符串。此行为被迁移到 resolve.enforceExtension。


resolve.*


还有更多的变化,由于不常用,不在这里详细列出。


module.loaders 改成了 module.rules


旧的 loader 配置被更强大的 rules 系统取代,后者允许配置 loader 以及其他更多项。为了兼容旧版,module.loaders 语法被保留,旧的属性名依然可以被解析。新的命名约定更易于理解并且是升级配置使用 module.rules 的好理由。


module: {

-   loaders: [

+   rules: [

      {

        test: /\.css$/,

-       loaders: [

+       use: [

          {

            loader: "style-loader"

          },

          {

            loader: "css-loader",

-           query: {

+           options: {

              modules: true

            }

        ]

      },

      {

        test: /\.jsx$/,

        loader: "babel-loader", // Do not use "use" here

        options: {

          // ...

        }

      }

    ]

  }


链式 loaders


与 v1 版本相同,loaders 可以链式调用,上一个 loader 的输出被作为输入传给下一个 loader。使用 rule.use 配置项,use 可以设置为一个 loaders 的列表。在 v1 版本中,loaders 通常被用 ! 连写。这一写法在新版中只在使用旧的 module.loaders 时有效。


module: {

-   loaders: {

+   rules: {

      test: /\.less$/,

-     loader: "style-loader!css-loader!less-loader"

+     use: [

+       "style-loader",

+       "css-loader",

+       "less-loader"

+     ]

    }

  }


取消了在模块名中自动添加 -loader 后缀


现在在指定 loader 的时候不能再省略 -loader 后缀了:


module: {

    rules: [

      {

        use: [

-         "style",

+         "style-loader",

-         "css",

+         "css-loader",

-         "less",

+         "less-loader",

        ]

      }

    ]

  }


你仍然可以启用这一旧行为,方法是通过配置 resolveLoader.moduleExtensions 项,但是我们不推荐这么做。


+ resolveLoader: {

+   moduleExtensions: ["-loader"]

+ }


了解这一改变背后的原因,请参阅 #2986。


json-loader 不再需要手动添加


如果没有为 JSON 文件配置 loader,webpack 将自动尝试通过 加载 json-loader JSON 文件。


module: {

    rules: [

-     {

-       test: /\.json/,

-       loader: "json-loader"

-     }

    ]

  }


我们决定这么做 以消弭 webpack、 node.js 和 browserify 之间的环境差异。


loader 默认的 resolve 配置是相对于 context 的


在 webpack 1 中,loader 默认配置下 resolve 相对于被匹配的文件。而在 webpack 2 中默认配置的 resolve 相对于 context 配置项。


这解决了一些问题,比如使用 npm link 或引用 context 之外的模块时导致重复载入。


你可以不再需要使用一些变通方案了:


module: {

    rules: [

      {

        // ...

-       loader: require.resolve("my-loader")

+       loader: "my-loader"

      }

    ]

  },

  resolveLoader: {

-   root: path.resolve(__dirname, "node_modules")

  }


取消了 module.preLoaders 以及 module.postLoaders


module: {

-   preLoaders: [

+   rules: [

      {

        test: /\.js$/,

+       enforce: "pre",

        loader: "eslint-loader"

      }

    ]

  }


UglifyJsPlugin sourceMap


UglifyJsPlugin 的 sourceMap 配置项现在默认为 false 而不是 true。


这意味着如果你在压缩代码时启用了 source map,或者想要让 uglifyjs 的警告能够对应到正确的代码行,你需要将 UglifyJsPlugin 的 sourceMap 设为 true。


devtool: "source-map",

  plugins: [

    new UglifyJsPlugin({

+     sourceMap: true

    })

  ]


UglifyJsPlugin warnings


UglifyJsPlugin 的 compress.warnings 配置项现在默认为 false 而不是 true。


这意味着如果你想要看到 uglifyjs 的警告信息,你需要将 compress.warnings 设为 true。


devtool: "source-map",

  plugins: [

    new UglifyJsPlugin({

+     compress: {

+       warnings: true

+     }

    })

  ]


UglifyJsPlugin 压缩 loaders


UglifyJsPlugin 不再压缩 loaders。在未来很长一段时间里,需要通过设置 minimize:true 来压缩 loaders。参考 loader 文档里的相关配置项。


loaders 的压缩模式将在 webpack 3 或更高的版本中被取消。


为了兼容旧的 loaders,loaders 可以通过插件来切换到压缩模式:


plugins: [

+   new webpack.LoaderOptionsPlugin({

+     minimize: true

+   })

  ]


OccurrenceOrderPlugin 被默认加载


我们不再需要在配置里指定它:


plugins: [

-   new webpack.optimize.OccurrenceOrderPlugin()

  ]


ExtractTextWebpackPlugin 大变化


ExtractTextPlugin 1.0.0 不能在 webpack v2 下工作。 你需要明确地安装 ExtractTextPlugin v2。


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


这一插件的配置变化主要体现在语法上。


ExtractTextPlugin.extract


module: {

  rules: [

    test: /.css$/,

-    loader: ExtractTextPlugin.extract("style-loader", "css-loader", { publicPath: "/dist" })

+    loader: ExtractTextPlugin.extract({

+      fallbackLoader: "style-loader",

+      loader: "css-loader",

+      publicPath: "/dist"

+    })

  ]

}


new ExtractTextPlugin({options})


plugins: [

-  new ExtractTextPlugin("bundle.css", { allChunks: true, disable: false })

+  new ExtractTextPlugin({

+    filename: "bundle.css",

+    disable: false,

+    allChunks: true

+  })

]


全动态 requires 现在默认会失败


只有一个表达式的依赖(例如 require(expr))将创建一个空的 context 而不是一个完整目录的 context。


如果有上面那样的代码,最好把它重构了,因为在 ES2015 模块下它不可以用。如果你确定不会有 ES2015 模块,你可以使用 ContextReplacementPlugin 来提示编译器进行正确的处理。


在 CLI 和配置中使用自定义参数


如果你之前滥用 CLI 来传自定义参数到配置中,比如:


webpack --custom-stuff


// webpack.config.js

var customStuff = process.argv.indexOf("--custom-stuff") >= 0;

/* ... */

module.exports = config;


你将会发现新版中不再允许这么做。CLI 现在更加严格了。


替代地,现在提供了一个接口来传递参数给配置。我们应该采用这种新方式,在未来许多工具将可能依赖它。


webpack --env.customStuff


module.exports = function(env) {

  var customStuff = env.customStuff;

  /* ... */

  return config;

};


require.ensure 以及 AMD require 的异步


现在这些函数总是异步的,而不是当 chunk 已经加载过的时候同步调用它们的 callback。


注意 require.ensure 现在依赖于原生的 Promise。如果在不支持 Promise 的环境里使用require.ensure,你需要添加 polyfill。


通过 options 配置 loader


你不能再通过 webpack.config.js 的自定义属性来配置 loader。只能通过 options 来配置。下面配置的 ts 属性在 webpack 2 下不再有效:


module.exports = {

  ...

  module: {

    use: [{

      test: /\.tsx?$/,

      loader: 'ts-loader'

    }]

  },

  // does not work with webpack 2

  ts: { transpileOnly: false }

}


什么是 options?


好问题。严格来说,有两种办法,都可以用来配置 webpack 的 loader。典型的 options 被称为query,是一个可以被添加到 loader 名之后的字符串。它比较像一个 query string,但是实际上有更强大的能力:


module.exports = {

  ...

  module: {

    use: [{

      test: /\.tsx?$/,

      loader: 'ts-loader?' + JSON.stringify({ transpileOnly: false })

    }]

  }

}


不过它也可以分开来,写成一个单独的对象,紧跟在 loader 属性后面:


module.exports = {

  ...

  module: {

    use: [{

      test: /\.tsx?$/,

      loader: 'ts-loader'

      options:  { transpileOnly: false }

    }]

  }

}


LoaderOptionsPlugin context


有的 loader 需要从配置中读取一些 context 信息。在未来很长一段时间里,这将需要通过 loader options 传入。详见 loader 文档的相关选项。


为了保持对旧 loaders 的兼容,这些信息可以通过插件传进来:


plugins: [

+   new webpack.LoaderOptionsPlugin({

+     options: {

+       context: __dirname

+     }

+   })

  ]


debug


在 webpack 1 中 debug 配置项切换 loaders 到 debug 模式。在未来很长一段时间里,这将需要通过 loader 配置项传递。详见 loader 文档的相关选项。


loaders 的 debug 模式将在 webpack 3 或后续版本中取消。


为了保持对旧 loaders 的兼容,loader 可以通过插件来切换到 debug 模式。


- debug: true,

  plugins: [

+   new webpack.LoaderOptionsPlugin({

+     debug: true

+   })

  ]


ES2015 的代码分割


在 webpack v1 中,你能使用 require.ensure 作为方法来懒加载 chunks 到你的应用中:


require.ensure([], function(require) {

  var foo = require("./module");

});


ES2015 模块加载规范定义了 import() 方法来运行时动态地加载 ES2015 模块。


webpack 将 import() 作为分割点并将被请求的模块放到一个单独的 chunk 中。


import() 接收模块名作为参数,并返回一个 Promise。


function onClick() {

  import("./module").then(module => {

    return module.default;

  }).catch(err => {

    console.log("Chunk loading failed");

  });

}


好消息是:如果加载 chunk 失败,我们可以进行处理,因为现在它基于 Promise。


警告:require.ensure 允许用可选的第三个参数为 chunk 简单命名,但是 import API 还未提供这个能力。如果你想要保留这个功能,你可以继续使用 require.ensure。


require.ensure([], function(require) {

  var foo = require("./module");

},


(注意废弃的 System.import:Webpack 对 System.import 的使用不符合新提出的标准,所以它在v2.1.0-beta.28 版本中被废弃,转向支持 import())


由于这个建议还在 Stage 3,如果你想要同时使用 import 和 Babel,你需要安装/添加 dynamic-import 语法插件来绕过解析错误。当建议被添加到规范之后,就不再需要这个语法插件了。


动态表达式


可以传递部分表达式给 import()。这与 CommonJS 对表达式的处理方式一致(webpack 为所有可能匹配的文件创建 context)。


import() 为每一个可能的模块创建独立的 chunk。


function route(path, query) {

  return import(`./routes/${path}/route`)

    .then(route => new route.Route(query));

}

// 上面代码为每个可能的路由创建独立的 chunk


混合使用 ES2015、AMD 和 CommonJS


你可以自由混合使用三种模块类型(甚至在同一个文件中)。在这个情况中 webpack 的行为和 babel 以及 node-eps 一致:


// CommonJS consuming ES2015 Module

var book = require("./book");

 

book.currentPage;

book.readPage();

book.default === "This is a book";


// ES2015 Module consuming CommonJS

import fs from "fs"; // module.exports map to default

import { readFileSync } from "fs"; // named exports are read from returned object+

 

typeof fs.readFileSync === "function";

typeof readFileSync === "function";


值得注意的是,你需要让 Babel 不解析这些模块符号,从而让 webpack 可以使用它们。你可以通过设置如下配置到 .babelrc 或 babel-loader 来实现这一点。


.babelrc


{

  "presets": [

    ["es2015", { "modules": false }]

  ]

}


Hints


不需要改变什么,不过也可以改变。


模板字符串


webpack 现在支持表达式中的模板字符串了。这意味着你可以在 webpack 构建中使用它们:


- require("./templates/" + name);

+ require(`./templates/${name}`);


配置中使用 Promise


webpack 现在支持在配置文件中返回 Promise 了。这让你能在配置文件中做异步处理。


webpack.config.js


module.exports = function() {

  return fetchLangs().then(lang => ({

    entry: "...",

    // ...

    plugins: [

      new DefinePlugin({ LANGUAGE: lang })

    ]

  }));

};


高级 loader 匹配


webpack 现在支持对 loader 进行更多方式的匹配。


module: {

  rules: [

    {

      resource: /filename/, // matches "/path/filename.js"

      resourceQuery: /querystring/, // matches "/filename.js?querystring"

      issuer: /filename/, // matches "/path/something.js" if requested from "/path/filename.js"

    }

  ]

}


更多的 CLI 参数项


你可以使用一些新的 CLI 参数项:


--define process.env.NODE_ENV="production" 见 DefinePlugin.


--display-depth 显示每个模块到入口的距离。


--display-used-exports 显示一个模块中被使用的 exports 信息。


--display-max-modules 设置输出时显示的模块数量(默认是 15)。


-p 能够定义 process.env.NODE_ENV 为 "production"。


Loader 变化


仅与 loader 作者有关的改变。


Cacheable


Loaders 现在默认可被缓存。Loaders 如果不想被缓存,需要选择不被缓存。


// Cacheable loader

  module.exports = function(source) {

-   this.cacheable();

    return source;

  }


 // Not cacheable loader

  module.exports = function(source) {

+   this.cacheable(false);

    return source;

  }

 

复合 options


webpack 1 只支持能够 JSON.stringify 的对象作为配置项。webpack 2 现在支持任意 JS 对象作为 loader 配置项。


使用复合 options 只有一个附加条件。你需要在 options 对象上添加一个 ident,让它能够被其他 loader 引用。


options 对象上有了 ident ,内联的 loader 就可以引用这个 options 对象。下面是个例子:


require("some-loader??by-ident!resource")


{

  test: /.../,

  loader: "...",

  options: {

    ident: "by-ident",

    magic: () => return Math.random()

  }

}


这种内联风格在常规的代码里一般用不着,但是在 loader 生成的代码里比较常见。比如,style-loader 生成一个模块,通过 require 加载其余的请求(它们输出 CSS)。


// style-loader generated code (simplified)

var addStyle = require("./add-style");

var css = require("-!css-loader?{"modules":true}!postcss-loader??postcss-ident");

 

addStyle(css);


所以如果你使用复合 options,告诉你的用户你使用的 ident。



觉得本文对你有帮助?请分享给更多人

关注「前端大全」,提升前端技能

 
前端大全 更多文章 如何快速成为JavaScript高手的思考 CSS 垂直水平完全居中手册 CSS 垂直水平完全居中手册 一篇给小白看的 JavaScript 引擎指南 一篇给小白看的 JavaScript 引擎指南
猜您喜欢 逃避虽可耻但有用【1-11集】 Github干货铺:WEB前端知识库 【双十一技术故事】小米:海量数据压力下的推送服务 在Rancher 1.2中实现基于CNI的扁平网络 UCloud混合云获2014年度互联网最佳技术创新奖